Vue详细介绍及使用
Vue详细介绍及使用
一、Vue定义及简介
1、Vue定义
关于Vue简介,百度百科给出的解释是:Vue.js是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和Vue生态系统支持的库开发的复杂单页应用。
Vue.js 是一个提供了 MVVM 风格双向数据绑定的 Javascript 库(无依赖别的js库,直接引入一个js文件就可以使用,跟jquery差不多),专注于View 层。它的核心是 MVVM 中的 VM,也就是 ViewModel。 ViewModel负责连接 View 和 Model,保证视图和数据的一致性,这种轻量级的架构让前端开发更加高效、便捷。
2、Vue背景简介
2.1、Vue诞生的背景
从历史的潮流来说,人们从之前的:原生JS -> Jquery之类的类库 -> 前端模板引擎 ,他们都有一个共同的特点需要我们去操作dom元素。近几年来,得益于手机设备的普及和性能的提升,移动端的web需求大量增加,产生了一种叫webapp的东西,也就是移动端的网页应用。为了更好满足当前移动webapp项目的开发需求,MVVM框架诞生,而Vue便是这样的一种框架。那么什么是Vue?
官网有一句对它的介绍:通过尽可能简单的API实现响应的数据绑定和组合的视图组件。Vue的核心主要包括下面两块:
Vue的数据驱动:数据改变驱动了视图的自动更新,传统的做法你得手动改变DOM来改变视图,vuejs只需要改变数据,就会自动改变视图;
视图组件化:把整一个网页的拆分成一个个区块,每个区块我们可以看作成一个组件。网页由多个组件拼接或者嵌套组成(可维护、可重用)。
2.2、MVVM框架的发展
MVC(View Model Controller )模式:
MVC框架最早出现在Java领域,在经典的MVC模式中,是用户通过Controller来操作Model以达到View的变化。MVC 即 Model-View-Controller 的缩写,就是 模型—视图—控制器,也就是说一个标准的Web 应用程式是由这三部分组成的:
View :用来把数据以某种方式呈现给用户;
Model :其实就是数据;
Controller :接收并处理来自用户的请求,并将 Model 返回给用户;
MVP(Model View Presenter)模式:
而后的MVP模式(MVC模式的演变)逻辑层在Presenter里实现,而且Presenter与具体的View 是没有直接关联的,而是通过定好的接口进行交互(在MVC中View会直接从Model中读取数据而不是通过 Controller)。 View 与 Model 不发生联系,都通过 Presenter 传递。
在HTML5 还未火起来的那些年,MVC 作为Web 应用的最佳实践是很OK 的,这是因为 Web 应用的View 层相对来说比较简单,前端所需要的数据在后端基本上都可以处理好,View 层主要是做一下展示,那时候提倡的是 Controller 来处理复杂的业务逻辑,所以View 层相对来说比较轻量,就是所谓的瘦客户端思想。
相对 HTML4,HTML5 最大的亮点是它为移动设备提供了一些非常有用的功能,使得 HTML5 具备了开发App的能力, HTML5开发App 最大的好处就是跨平台、快速迭代和上线,节省人力成本和提高效率,因此很多企业开始对传统的App进行改造,逐渐用H5代替Native,到2015年的时候,市面上大多数App 或多或少嵌入都了H5 的页面。既然要用H5 来构建 App, 那View 层所做的事,就不仅仅是简单的数据展示了,它不仅要管理复杂的数据状态,还要处理移动设备上各种操作行为等等。因此,前端也需要工程化,也需要一个类似于MVC 的框架来管理这些复杂的逻辑,使开发更加高效。
注:Native(使用原生制作APP,即在基于目前的智能手机的操作系统(如安卓android、苹果IOS,另外还有 (windows phone) 的基础上,使用相应平台支持的开发工具和语言 ( 比如 iOS 平台支持 Xcode 和 Objective-C,安平台支持 Eclipse 和 Java) 编写的第三方移动应用程序,简称原生APP。)
但实际上,随着H5 的不断发展,人们更希望使用H5 开发的应用能和Native 媲美,或者接近于原生App 的体验效果:
1)开发者在代码中大量调用相同的 DOM API,处理繁琐 ,操作冗余,使得代码难以维护。
2)大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
3)当 Model 频繁发生变化,开发者需要主动更新到View ;当用户的操作导致 Model 发生变化,开发者同样需要将变化的数据同步到Model 中,这样的工作不仅繁琐,而且很难维护复杂多变的数据状态。
MVVM(Model View ViewModel) 模式:
其实,早期jquery 的出现就是为了前端能更简洁的操作DOM 而设计的,但它只解决了第一个问题,另外两个问题始终伴随着前端一直存在。MVVM 的出现,完美解决了以上三个问题。MVVM 由 Model、View、ViewModel 三部分构成,Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理(用到这种的有来自Google的AngularJS,还有Vue.js、Knockout、Ember.js)。
3、主流前端框架/库简介
目前的流行前端框架/库有:Vue、React、Angular(三大霸主)以及Bootstrap、APICloud、jQuery等;(jQuery、React为库)
Vue毫无疑问是当前最流行最火爆的前端框架之一。vue作为渐进式前端框架,由当时的谷歌工程师尤雨溪大神业余时间所写,一放到github就引起流行,github上点赞数百万。vue现在几乎作为前端必备框架,特点高效,灵活,稳定,功能强大,代码少,运行速度快,整个文件只有200多K。Vue所关注的核心是MVC模式中的视图层,同时,它也能方便地获取数据更新,并通过组件内部特定的方法实现视图与模型的交互。vue做了必须的事,又不会做职责之外的事。做为前端,不懂vue是不行了。
React是Facebook在2013年开源的前端框架,由于 React的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。React主要用于构建UI。你可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件。也就是说同一组人只需要写一次 UI ,就能同时运行在服务器、浏览器和手机。开发效率高,事半功倍。
AngularJS诞生于2009年,由Misko Hevery 等人创建,是为了克服HTML在构建应用上的不足而设计的。后为Google所收购。是一款优秀的前端JS框架,已经被用于Google的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVC(Model–view–controller)、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。2016年angular2正式被发布,在Anguar2.0之前的版本都叫做AngularJS(Angular2不是从Angular1升级过来的,Angular2是重写的)。
jQuery是一个快速、简洁的JavaScript库,是继Prototype之后又一个优秀的JavaScript代码库。jQuery设计的宗旨是写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。代码十分精炼,代码量小,速度快,犀利。使用量非常大。
Bootstrap(react是Facebook开发的,那么做为Facebook的竞争对手,Twitter公司也不甘示弱,开源了Bootstrap。)是由美国Twitter公司的设计师Mark Otto和Jacob Thornton合作基于HTML、CSS、JavaScript 开发的简洁、直观、强悍的前端开发框架,使得 Web 开发更加快捷。Bootstrap提供了优雅的HTML和CSS规范,它即是由动态CSS语言Less写成。Bootstrap一经推出后颇受欢迎,一直是GitHub上的热门开源项目,包括NASA的MSNBC(微软全国广播公司)的Breaking News都使用了该项目。国内一些移动开发者较为熟悉的框架,如WeX5前端开源框架等,也是基于Bootstrap源码进行性能优化而来。Bootstrap 能给你的 Web 开发提供了时尚的版式,表单,buttons,表格,网格系统等等。
apicloud(包括api.js和api.css)专门为移动端APP开发而设计的框架,适应不同的移动设备,而且可以和其他框架一起引入项目中使用,非常灵活,优秀。
注:框架和库的区别
库(插件):是一种封装好的特定方法集合,对项目的侵入性较小,提供给开发者使用,控制权在使用者手中,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。
框架:是一套架构,会基于自身特点向用户提供一套相当完整的解决方案,而且控制权在框架本身;对项目的侵入性较大,使用者要按照框架所规定的某种特定规范进行开发,项目如果需要更换框架,则需要重新架构整个项目。
4、Vue与AngularJS、React区别
4.1、与AngularJS的区别
相同点:
都支持指令:内置指令和自定义指令。
都支持过滤器:内置过滤器和自定义过滤器。
都支持双向数据绑定。
都不支持低端浏览器。
不同点:
AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观。
在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢。
Vue.js使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立触发的。
对于庞大的应用来说,这个优化差异还是比较明显的。
4.2、与React的区别
相同点:
React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。
中心思想相同:一切都是组件,组件实例之间可以嵌套。
都提供合理的钩子函数,可以让开发者定制化地去处理需求。
都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载。
在组件开发中都支持mixins的特性。
不同点:
React依赖Virtual DOM,而Vue.js使用的是DOM模板。React采用的Virtual DOM会对渲染出来的结果做脏检查。
Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作DOM。
5、Vue特性
1) 轻量级的框架
Vue.js 能够自动追踪依赖的模板表达式和计算属性,提供 MVVM 数据绑定和一个可组合的组件系统,具有简单、灵活的 API
2) 双向数据绑定
声明式渲染是数据双向绑定的主要体现,同样也是 Vue.js 的核心,它允许采用简洁的模板语法将数据声明式渲染整合进 DOM。
3) 指令
Vue.js 与页面进行交互,主要就是通过内置指令来完成的,指令的作用是当其表达式的值改变时相应地将某些行为应用到 DOM 上。
4) 组件化
组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。
在 Vue 中,父子组件通过 props 传递通信,从父向子单向传递。子组件与父组件通信,通过触发事件通知父组件改变数据。这样就形成了一个基本的父子通信模式。
在开发中组件和 HTML、JavaScript 等有非常紧密的关系时,可以根据实际的需要自定义组件,使开发变得更加便利,可大量减少代码编写量。
组件还支持热重载(hotreload)。当我们做了修改时,不会刷新页面,只是对组件本身进行立刻重载,不会影响整个应用当前的状态。CSS 也支持热重载。
5) 客户端路由
Vue-router 是 Vue.js 官方的路由插件,与 Vue.js 深度集成,用于构建单页面应用。Vue 单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来,传统的页面是通过超链接实现页面的切换和跳转的。
6) 状态管理
状态管理实际就是一个单向的数据流,State 驱动 View 的渲染,而用户对 View 进行操作产生 Action,使 State 产生变化,从而使 View 重新渲染,形成一个单独的组件。
注:Vue必须在ES5版本以上的环境下使用,一些不支持ES5的旧浏览器中无法运行Vue。
6、Vue发展历史
时间 | 事件 |
2013年 | 在 Google 工作的尤雨溪,受到 Angular 的启发,开发出了一款轻量框架,最初命名为 Seed 。 |
2013年12月 | 更名为 Vue,图标颜色是代表勃勃生机的绿色,版本号是 0.6.0。 |
2014年01月24日 | Vue 正式对外发布,版本号是 0.8.0。 |
2014年02月25日 | 0.9.0 发布,有了自己的代号:Animatrix,此后,重要的版本都会有自己的代号。 |
2015年06月13日 | 0.12.0,代号Dragon Ball,Laravel 社区(一款流行的 PHP 框架的社区)首次使用 Vue,Vue 在 JS 社区也打响了知名度。 |
2015年10月26日 | 1.0.0 Evangelion 是 Vue 历史上的第一个里程碑。同年,vue-router、vuex、vue-cli 相继发布,标志着 Vue从一个视图层库发展为一个渐进式框架。 |
2016年10月01日 | 2.0.0 是第二个重要的里程碑,它吸收了 React 的虚拟 Dom 方案,还支持服务端渲染。自从Vue 2.0 发布之后,Vue 就成了前端领域的热门话题。 |
2019年02月05日 | Vue 发布了 2.6.0 ,这是一个承前启后的版本,在它之后,将推出 3.0.0。 |
2019年12月05日 | 在万众期待中,尤雨溪公布了 Vue 3 源代码,目前 Vue 3 处于 Alpha 版本。 |
2020年09月18日 | Vue发布了3.0.0版本(https://github.com/vuejs/vue-next/releases)中。但是目前官网上只有vue3.x-beta (测试版API) |
二、Vue安装及环境配置
在搭建vue的开发环境之前,一定一定要先下载node.js,vue的运行是要依赖于node的npm的包管理工具来实现,node可以在官网或者中文网里面下载。官网地址:https://nodejs.org/en/download/
备注:windows7 环境下只能使用Node.js 12.x及以下版本,官网下载地址:https://nodejs.org/en/download/releases/
1、windows下搭建vue开发环境
1)在官网下载好需要的版本,我下载的版本为:node-v12.20.0-x64.msi,下载完成安装,安装完成在CMD中查看是否安装成功;
2)设置缓存文件夹
npm config set cache "D:\program files\nodejs\node_cache"
设置全局模块存放路径(之后用命令npm install XXX -g安装以后模块就在D:\program files\nodejs\node_global里)
npm config set prefix "D:\program files\nodejs\node_global"
3)高版本的nmp(Node包管理器:Node Package Manager,服务器在国外)是自带的,由于在国内使用npm是非常慢的,推荐使用淘宝npm镜像。在cmd中输入安装命令:
#安装命令
npm install -g cnpm --registry=https://registry.npm.taobao.org
#删除命令
npm uninstall -g cnpm --registry=https://registry.npm.taobao.org
4)设置环境变量
(设置环境变量可以使得住任意目录下都可以使用cnpm、vue等命令,而不需要输入全路径)
在PATH中新增:D:\program files\nodejs\node_global(新增完成后重新打开CMD)
5)安装Vue
#安装Vue
cnpm install vue -g
6)安装vue命令行工具,即vue-cli 脚手架
如果只是简单的写几个Vue的Demo程序,那么你不需要Vue-Cli。Cli是(Command-Line Interface,翻译为命令行界面,俗称脚手架,他是官方发布的Vue.js项目脚手架,使用Vue-cli可以快速搭建Vue开发环境以及对应的webpack配置)
7)创建新项目:webpack-simple / webpack
打开开始菜单,输入 CMD,或使用快捷键 win+R,输入 CMD,敲回车,弹出命令提示符。打开你将要新建的项目目录;
vue init webpack-simple这样的方式适合小项目,vue init webpack这样的方式适合中大型项目;
输入:vue init webpack-simple vue-test (注:vue init webpack-simple+项目名称 名称不能有大写字母)
输入:vue init webpack vue-test-big
8)项目生成情况
vue init webpack-simple vue-test 生成目录:
vue init webpack vue-test-big 生成目录:
目录/文件 | 说明 |
build | 最终发布代码的存放位置 |
config | 配置目录,包括端口号等 |
node_modules | 这是执行npm install后产生的,里面包含了Node.js和npm依赖文件以及后续安装的第三方组件或者功能 |
src | 我们要开发的目录,用于存放页面相关的文件,基本上要做的事情都在这个目录里面 |
src\assets | 图片文件目录,如:logo |
src\components | 存放了一个组件文件 |
src\router | 项目的路由 |
src\App.vue | 主文件,项目的入口文件,可以直接将组件写在这里,而不使用components目录 |
src\amin.js | 项目的核心文件 |
static | 一般用于存放静态资源,如:图片、字体等 |
.babelrc | 用来设置转码的规则和插件,一般情况下不需要设置 |
9)安装工程依赖模块
vue-test:
#切换到当前目前,一定切换进来:
D:\vueTest>cd vue-test
#运行cnpm install,下载当前项目所依赖的包,在目录下会生成node_modules
D:\vueTest\vue-test>cnpm install
#运行cnpm run dev,启动当前的项目
D:\vueTest\vue-test>cnpm run dev
第一个简单版的vue的项目,就好了打开链接:http://localhost:8080/
vue-test-big:
#切换到项目目录
D:\vueTest>cd vue-test-big
#运行cnpm run dev,启动当前项目
D:\vueTest\vue-test-big>cnpm run dev
10)使用Visual Studio Code来开发Vue程序(当然也可以使用HBuilderX)
a)导入项目:File->Open Folder->vue-test-big
b)打开集成终端(open integrated terminal):快捷键(Ctrl+`)
c)运行命令(cnpm run dev)
d)打开页面(http://localhost:8080/)
但是我们看到的是http://localhost:8080/#/,关于生成的URL地址http://localhost:8080/#/后面的/#/,是路由的hash和history之分,默认是hash模式,会有#,把mode改成history:
vue 项目往往会搭配 vue-router 官方路由管理器,它和 vue.js 的核心深度集成,让构建单页面应用变得易如反掌。vue-router 默认为 hash 模式,使用 URL 的 hash 来模拟一个完整的 URL,所以当 URL 改变时,页面不会重新加载,只是根据 hash 来更换显示对应的组件,这就是所谓的单页面应用。
//只需要在index.js加上一行mode: 'history',
export default new Router({
// 路由模式:hash(默认),history模式
mode: 'history',
//路由规则
routes:[
{
path:'/',
name:'index',
component:'Index'
}
],
})
2、如何使用Vue.js
1)直接通过script加载CDN文件或者直接加载本地vue.js文件
2)npm(vue- cli)基于npm管理依赖使用nmp(vue- cli)来安装Vue
即前面的Vue环境搭建,不过官方文档上说:我们不推荐新手直接使用 vue- cli ,尤其是在你还不熟悉基于 Node.js 的构建工具时。
二、Vue基础知识
1、Vue学习内容
学习Vue不要在想着怎么操作DOM,而是想着如何操作数据。那么Vue做了什么,我们要学习什么,先来一张总结性质的思维导图(此图为Vue2.x版本):
2、数据绑定
数据绑定是将数据和视图关联,当数据发生变化是,可以自定更新视图。
2.1、语法-插值:{{msg|表达式}}
文本插值是最基本的形式,使用双大括号{{}},类似于Mustache(Mustache是一个logic-less(轻逻辑)模板解析引擎,它的优势在于可以应用在Javascript、PHP、Python、Perl等多种编程语言中。)
{{msg|表达式}}标签将会被替换为 data 对象上对应的 msg 属性的值,也支持写入表达式(表达式是各种数值、变量、运算符的综合体)。
注意:Mustache 语法不能作用在 HTML 元素的属性上。
2.2、语法-使用指令插值
指令是带有v-前缀的特殊特性,其值限定为绑定表达式,也就是JavaScript表达式和过滤器。指令的作用是当表达式值发生变化是,将这个变化也反映到DOM上。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!--使用CDN:CDN的全称是Content Delivery Network,即内容分发网络。
CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,
通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--直接引用本地<script src="js/vue.js"></script>-->
</head>
<body>
<div id="app" v-if="show">
<h4>----------------Vue版本号:{{myversion}}--------------</h4>
基本插值:{{msg}}
<br />
表达式:{{flag?1:0}}
</div>
</body>
<script>
new Vue({
el:"#app",
data:{
myversion:Vue.version,
msg:"Hello World",
flag:true,
show:true //改成false将会隐藏
}
});
</script>
</html>
3、分隔符
Vue.js中数据绑定的语法被涉及为可配置的。如果不习惯Mustache风格的语法,则可以自己设置。在Vue 1.x 中它定义在(src/config.js源码)里,但是在Vue2.x后有所改变。
全局使用:Vue.options.delimiters = ['语法格式'];
实例选项定义:delimiters:['语法格式’]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
#{msg}
</div>
</body>
<script>
//Vue 1.x中的全局设置方式
// Vue.config.delimiters = ["${', '}"];
//Vue 2.x中的全局设置方式
Vue.options.delimiters = ['${', '}'];
var vm = new Vue({
//在实例中使用选项设置的方式
delimiters:['#{','}'],
el:"#app",
data:{
msg:"Hello World"
}
});
</script>
</html>
三、Vue指令
前面的案例中我们已经接触到了一个指令v-show,这种带有前缀 v-的指令,用于表示它们是 Vue 提供的特殊特性。在Vue中还定义了许多这种的内置指令。
1、插入数据指令(v-text、v-html )
1.1、v-text
预期:string
详细:更新元素的 textContent
。如果要更新部分的 textContent
,需要使用 {{ Mustache }}
插值。
1.2、v-html
预期:string
详细:更新元素的 innerHTML
。
注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。如果试图使用 v-html
组合模板,可以重新考虑是否通过使用组件来替代。
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<span v-text="msg"></span><br />
<!-- 和下面的一样,如果是只更新标签里的部分内容,则使用下面这种方式 -->
<span>{{msg}}</span>
<div v-html="html"></div>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"Hello World",
html:"<h4>我是v-html指令</h4>"
}
});
</script>
</html>
2、条件渲染指令(v-show、v-if、v-else、v-else-if)
2.1、v-show
预期:any
用法:根据表达式之真假值,切换元素的 display
CSS property。当条件变化时该指令触发过渡效果。
2.2、v-if
预期:any
用法:根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是 <template>
,将提出它的内容作为条件块。当条件变化时该指令触发过渡效果。
注意:当和 v-if
一起使用时,v-for
的优先级比 v-if
更高。v-show是css切换,v-if是完整的销毁和重新创建使用频繁切换时用v-show,运行时较少改变时用v-if
2.3、v-else
用法:不需要表达式,为 v-if
或者 v-else-if
添加“else 块”。
限制:前一兄弟元素必须有 v-if
或 v-else-if
。
2.4、 v-else-if
类型:any
用法:表示 v-if
的“else if 块”。可以链式调用。
限制:前一兄弟元素必须有 v-if
或 v-else-if
。(2.1.0 新增)
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--指令:v-show-->
<span v-show="show">我是v-show</span><br />
<!--指令:v-if-->
<div v-if="Math.random() > 0.5">
你看见的是:v-if
</div>
<!--指令:v-else-->
<div v-else>
你看见的是:v-else
</div>
<!--指令:v-else-if-->
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
show:false,
type:"C"
}
});
</script>
</html>
3、循环渲染指令(v-for)
预期:Array | Object | number | string | Iterable (2.6 新增)
用法:基于源数据多次渲染元素或模板块。此指令之值,必须使用特定语法 alias in expression
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--Vue2.0不同于Vue1.0版本,它把$index和$key都移除了,取而代之的是:key属性-->
<!--简单数组-->
<div v-for="(item,i) in list_sz">索引:{{i}}-->值:{{item}}</div><br />
<!--对象数组-->
<div v-for="(dx,i) in list_dx">索引:{{i}}-->id:{{dx.id}}--Name:{{dx.name}}</div><br />
<!--对象-->
<div v-for="(val,key,index) of list_user">
索引:{{index}} --> 键:{{key}} 值:{{val}}
</div>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
list_sz:[1,2,3,4,5],
list_dx:[{id:2,name:'李四'},{id:3,name:'王五'},{id:1,name:'张三'}],
list_user:{
id:1,
name:'Mike',
age:22,
gender:'男'
}
}
});
</script>
</html>
3.1、v-for :key的使用
官网有一句:v-for
的默认行为会尝试原地修改元素而不是移动它们。要强制其重新排序元素,你需要用特殊 attribute key
来提供一个排序提示。
官方对key的解释:key
的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。重复的 key 会造成渲染错误。
要解释key的作用,不得不先介绍一下虚拟DOM的Diff算法了。vue和react都实现了一套虚拟DOM,使我们可以不直接操作DOM元素,只操作数据便可以重新渲染页面。而隐藏在背后的原理便是其高效的Diff算法。
当页面的数据发生变化时,Diff算法只会比较同一层级的节点:如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点以后的子节点了。如果节点类型相同,则会重新设置该节点的属性,从而实现节点的更新。
对于相同类型的节点更新,如下图:我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
不用index做为key的原因也是如此,如果用index作为key,[1,2,3]那么删除第二项的时候,index就会从1,2,3变成1,2,index永远是连续的,同样无法解决上诉问题。
所以一句话,key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>v-for :key的使用</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<input type="text" v-model="name">
<button @click="add">添加</button>
</div>
<ul>
<li v-for="(item, i) in list">
<!--<li v-for="(item, i) in list" :key="item.id">-->
<input type="checkbox"> {{item.name}}
</li>
</ul>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
name:'',
list: [{id: 1,name: '典韦'},{id: 2,name: '凯'},{id: 3,name: '虞姬'},{id:4,name:'蔡文姬'}]
},
methods: {
add:function(){
var thisd = this.list.length+1;
console.log(thisd+this.name);
this.list.unshift({id:+thisd,name: this.name});
this.name = ''
}
}
});
</script>
</html>
3.2、关于v-for对应的数组的更新
Vue重写了这些方法,使得他们具备响应式更新
- push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
- pop() 方法用于删除并返回数组的最后一个元素。
- shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
- unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
- splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。
- sort() 方法用于对数组的元素进行排序。
- reverse() 方法用于颠倒数组中元素的顺序。
4、属性绑定指令(v-bind)
缩写::
预期:any (with argument) | Object (without argument)
参数:attrOrProp (optional)
修饰符:
.prop
- 作为一个 DOM property 绑定而不是作为 attribute 绑定。.camel
- (2.1.0+) 将 kebab-case attribute 名转换为 camelCase。(从 2.1.0 开始支持).sync
(2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的v-on
侦听器。
用法:v-bind 主要用于属性绑定,比方你的class属性,style属性,value属性,href属性等等,只要是属性,就可以用v-bind指令进行绑定
动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
在绑定 class
或 style
attribute 时,支持其它类型的值,如数组或对象。可以通过下面的教程链接查看详情。
在绑定 prop 时,prop 必须在子组件中声明。可以用修饰符指定不同的绑定类型。
没有参数时,可以绑定到一个包含键值对的对象。注意此时 class
和 style
绑定不支持数组和对象。
语法:v-bind:属性名.修饰符="属性值"
4.1、绑定普通属性attribute
<!-- 绑定一个属性 -->
<img v-bind:src="imageSrc">
<!-- 缩写 -->
<img :src="imageSrc">
4.2、动态特性名
<!-- 动态特性名 (2.6.0+) -->
<button v-bind:[key]="value"></button>
<!-- 动态特性名缩写 (2.6.0+) -->
<button :[key]="value"></button>
4.3、内联字符串拼接
<!-- 内联字符串拼接:fileName ,'img/'加上单引号之后,并不会进行属性值绑定,而是将字符串原样渲染后绑定属性上。-->
<img :src="'img/' + fileName">
4.4、class 绑定
对应class绑定官网给出的三种类型:class对象绑定、class数组绑定、class数组中嵌套对象绑定
<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]">
4.4.1、class对象绑定
<!--传统的html的css类引用语法:-->
<div class="css类名1 css类名2">html传统写法</div>
<!----------------------------v-bind对象语法----------------------------------->
<!--v-bind对象语法,我们需要队css-class类名赋一个boolean值,来决定css类是否生效。-->
<div v-bind:class="{css类名1: true|false, css类名2: true|false}">{{message}}</div>
<!--使用了v-bind对象语法,仍然可以使用传统css引用法:vue会帮我合并-->
<div v-bind:class="{css类名1: true|false, css类名2: true|false}" class="css类名3">{{message}}</div>
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.blue{color: blue;}
.red{color: red;}
.jc{font-weight:bold}
</style>
</head>
<body>
<div id="app">
<p :class="{blue:isBlue,red:isRed}">{{name}}</p>
<!--效果一样-->
<p :class="getPClass()" class="jc">{{name}}</p>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
name:"v-bind:class对象绑定",
isBlue:true,
isRed:false
},
methods:{
getPClass:function(){
return {blue:this.isBlue,red:this.isRed}
}
}
});
</script>
</html>
效果图:
4.4.2、class数组绑定、class数组中嵌套对象绑定
<!--数组语法绑定class类-->
<div :class="['css类名1','css类名2']">数组语法</div>
<!--数组中嵌套对象-->
<div :class="['css类名1', 'css类名2', {css类名3: true|false}]"> 数组中嵌套对象</div>
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.blue{color: blue;}
.red{color: red;}
.jc{font-weight:bold;}
.cl{font-size: 30px;}
</style>
</head>
<body>
<div id="app">
<p :class="cssArray">{{name}}</p>
<button v-on:click="changeColor" class="jc">切换颜色</button>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
name:"v-bind:class对象绑定",
cssArray:['blue','jc',{cl:true}]
},
methods:{
changeColor:function(){
if(this.cssArray[0]!=='blue'){
//Vue响应式:数据发生变化后,会重新对页面渲染
//使用响应式的操作函数push/pop/shift/unshift/splice/sort/reverse
this.cssArray.shift();//移除数组第一个元素
this.cssArray.unshift('blue');//加入元素到数组的头部
//这种写法不是响应式的,数据发生变化,但是页面颜色不会发生改变
//this.cssArray[0]='blue';
//unshift可以,原因在于:Vue中重写了Array中的这7个函数
}else{
this.cssArray.shift();
this.cssArray.unshift('red');
//this.cssArray[0]='red';
}
}
}
});
</script>
</html>
效果图:
4.5、style 绑定
对于style绑定,官网给出了两种:style对象绑定、style数组绑定
<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<p :style="{'font-size':fontSize+'px'}">{{name}}</p>
<p :style="[style1,style2]">{{name}}</p>
<button @click="addSize">增大字体</button>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
name:"v-bind:style绑定",
fontSize:20,
style1:{color:'red'},
style2:{fontWeight:'bold'}
},
methods:{
addSize:function(){
this.fontSize++;
}
}
});
</script>
</html>
效果图:
4.6、prop 绑定—通过 prop 修饰符绑定 DOM 属性
官网的解释:作为一个 DOM property 绑定而不是作为 attribute 绑定。
<!-- 通过 prop 修饰符绑定 DOM attribute -->
<div v-bind:text-content.prop="text"></div>
4.6.1、从DOM节点properties(属性),attributes(特性)说起
property
是 dom 元素在 js 中作为对象拥有的属性;
attribute
是 dom 元素在文档中作为 html 标签拥有的属性;
我们可以认为 property
是一个 DOM
对象创建的时候就会初始化在对象中的一些属性(我们也可以自定义属性),而 attribute
是附在 HTML
文档中的某个元素上的特性,我们可以改变、删除、自定义一个特性,但是对 property
不可以,对于 DOM
内置的属性我们无法删除,也只能按照规定类型赋值,并且内置属性会在每次 DOM
对象初始化的时候产生,比如 name
属性,无论我们设置什么类型的值,最后返回的都是字符类型。但是对于我们自定义的 DOM
属性,则可以是任何 JS
支持的数据类型,而 attribute
的数据类型只能是字符串。
对于 html 的标准属性来说,attribute 和 property 是同步的,是会自动更新的,但是对于自定义的属性来说,他们是不同步的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="text" value="Name" id="app" class="hh" addUserDefine="zidingyi">
</body>
<script>
window.onload = function(){
var ipt = document.getElementById('app');
console.log(ipt);//HTMLInputElement
console.log(ipt.attributes);//object NamedNodeMap
//html自带的dom属性会自动转换成property,但是自定义的属性没有这个'权利'
console.log(ipt.className); //hh
console.log(ipt.addUserDefine); //打印 undefined
console.log(ipt.name);//打印 "" 虽然没写,但是默认为""
//浏览器解析之前全都是特性,解析之后:标准特性的值将被解析并挂载到 DOM属性上,
//非标准特性只可用DOM的 getAttribute 等方法取值。getAttribute(attr),attr是大小写不敏感的
console.log(ipt.getAttribute('addUserDefine'));//zidingyi
}
</script>
</html>
当浏览器解析:<input type="text" value="Name" id="app" class="hh" addUserDefine="zidingyi">这段代码之后,一个HTMLInputElement对象将会被创建,这个对象包含了很多的properties
,如:accept, accessKey, align, alt, attributes, autofocus, baseURI, checked, childElementCount, childNodes, children, classList, className, clientHeight等。
对于DOM节点对象,properties就是这个对象(ipt)的属性集,而attributes是这个对象(ipt)中名为attributes
的一个属性。在这些properties中,我们可以看到:className: "hh",等HTMLInputElement中存在的属性,
properties同样继承了(标准属性),而addUserDefine(非标准属性)
却没有出现在js对象的property中。
如果我们修改了:property的值
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="text" value="Name" id="app" class="hh" addUserDefine="zidingyi">
</body>
<script>
window.onload = function(){
var ipt = document.getElementById('app');
console.log(ipt);//HTMLInputElement
console.log(ipt.attributes);//object NamedNodeMap
//html自带的dom属性会自动转换成property,但是自定义的属性没有这个'权利'
console.log(ipt.className); //hh
console.log(ipt.addUserDefine); //打印 undefined
console.log(ipt.name);//打印 "" 虽然没写,但是默认为""
//浏览器解析之前全都是特性,解析之后:标准特性的值将被解析并挂载到 DOM属性上,
//非标准特性只可用DOM的 getAttribute 等方法取值。getAttribute(attr),attr是大小写不敏感的
console.log(ipt.getAttribute('addUserDefine'));//zidingyi
//如果设置的属性属于DOM元素本身所具有的标准属性,不管是通过ele.setAttribute还是ele.title的方式设置,都能正常获取。
//通过setAttribute设置的自定义属性,只能通过标准的getAttribute方法来获取;同样通过点号方式设置的自定义属性也无法通过 标准方法getAttribute来获取。
ipt.mytitle = 'initTitle';
console.log(ipt.value);//initTitle
console.log(ipt.getAttribute('mytitle'));//null
//使用setAttribute
ipt.setAttribute('mytitle','newTitle');
console.log(ipt.mytitle);//initTitle
}
</script>
</html>
这就是prop 修饰符的真正意义所在:v-bind 默认绑定到 DOM 节点的 attribute 上,使用 .prop 修饰符后,会绑定到 property上。(attribute 设置的自定义属性会在渲染后的 HTML 标签里显示,property 不会:通过自定义属性存储变量,避免暴露数据防止污染 HTML 结构)
4.6.2、通过 prop 修饰符绑定 DOM 属性
<!-- 通过 prop 修饰符绑定 DOM attribute -->
<div v-bind:text-content.prop="text"></div>
<!--组件中再介绍-->
<!-- prop 绑定。“prop”必须在 my-component 中声明。-->
<my-component :prop="someThing"></my-component>
<!-- 通过 $props 将父组件的 props 一起传给子组件 -->
<child-component v-bind="$props"></child-component>
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div :aaa.prop="[inputData]" id="pry">{{name}}</div>
<div :aaa="[inputData]" id="prn">没有修饰符</div>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
name:"测试prop修饰符",
inputData:"hello"
}
});
window.onload=function(){
var div = document.getElementById("pry");
var divs = document.getElementById("prn");
console.log(div.aaa); //hello
console.log(div.getAttribute('aaa'));//null
console.log(div.aaa==vm.inputData);//true
console.log(divs.aaa);//undefined
console.log(divs.getAttribute('aaa'));//hello
console.log(divs.getAttribute('aaa')==vm.inputData);//true
}
</script>
</html>
DOM效果:
4.7、.camel
修饰符
只有props和.prop会默认将kebab-case转化为camelCase,剩下的作为attribute的不会。而.camel修饰符正是针对attribute的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2 :msg-text="msgText">原本</h2>
<!-- 编译后为<h2 msg-text="aaa">原本</h2> -->
<h2 :msg-text.camel="msgText">加camel修饰符</h2>
<!-- 编译后为<h2 msgtext="aaa">加camel修饰符</h2>,由于camel将msg-text转化为msgText,
html中不区分大小写,所以会将大写都转化为小写,则最终结果为msgtext -->
<input type="text" v-model="message">
<child :my-message="message"></child>
<!--此处的my-message只能是短横线命名(若为驼峰式则全部转换为小写。)-->
</div>
</body>
<script>
Vue.component('child',{
//props中传递的数据可以为驼峰式也可以为短横线式,他们在此处是相互转换的
//props:['my-message'],
props:['myMessage'],
//props中使用驼峰命名,则
template:'<p>{{myMessage}}</p>'
// 此处有限制,是字符串模板,{{ }}语法中不能是短横线连接方式。此处只能是驼峰命名方式。若为短横线的命名方式,则会报错。
});
new Vue({
el:"#app",
data:{
msgText: 'aaa',
message:''
}
});
</script>
</html>
效果图:
5、数据双向绑定指令(v-model)
预期:随表单控件类型不同而不同。
限制:
<input>
<select>
<textarea>
- components
修饰符:
用法:在表单控件或者组件上创建双向绑定。
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input v-model="test">value:{{test}}<br />
<!--原理-语法糖:使用v-model,相当于v-bind+v-on-->
<!--<input :value="test" @input="test= $event.target.value"><br />-->
<!-- 在每次 input 事件触发后将输入框的值与数据进行同步,添加 lazy 修饰符,从而转变为使用 change 事件进行同步 -->
<input v-model.lazy="msg" >lazy:{{msg}}<br />
<!--去除字符串首尾的空格-->
<input v-model.trim="trm">trim:{{trm}}<br />
<!--将数据转化为值类型-->
<input v-model.number="age" type="number">number:{{age}}<br />
<!--下拉项绑定-->
<select v-model="selected">
<option value="A被选">A</option>
<option value="B被选">B</option>
<option value="C被选">C</option>
</select>
<span>Selected: {{ selected }}</span><br />
<!--单选按钮-->
<input type="radio" id="small" value="small_value" v-model="picked">
<label for="small">small</label>
<br>
<input type="radio" id="big" value="big_value" v-model="picked">
<label for="big">big</label>
<br>
<span>Picked: {{ picked }}</span><br />
<!--复选框-->
<input type="checkbox" id="one" value="value_one" v-model.lazy="checkedNames">
<label for="one">选项一</label>
<input type="checkbox" id="two" value="value_two" v-model.lazy="checkedNames">
<label for="two">选项二</label>
<input type="checkbox" id="three" value="value_three" v-model.lazy="checkedNames">
<label for="three">选项三</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
</body>
<script>
new Vue({
el: '#app',
data: {
test: '',
msg:'',
trm:'',
age:'',
selected: '',
picked: '',
checkedNames: []
}
});
</script>
</html>
效果:
6、事件绑定指令(v-on)
缩写:@
预期:Function | Inline Statement | Object
参数:event
修饰符:
.stop
- 调用event.stopPropagation()
。.prevent
- 调用event.preventDefault()
。.capture
- 添加事件侦听器时使用 capture 模式。.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调。.{keyCode | keyAlias}
- 只当事件是从特定键触发时才触发回调。.native
- 监听组件根元素的原生事件。.once
- 只触发一次回调。.left
- (2.2.0) 只当点击鼠标左键时触发。.right
- (2.2.0) 只当点击鼠标右键时触发。.middle
- (2.2.0) 只当点击鼠标中键时触发。.passive
- (2.3.0) 以{ passive: true }
模式添加侦听器
用法:绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。
用在普通元素上时,只能监听原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。
在监听原生 DOM 事件时,方法以事件为唯一的参数。如果使用内联语句,语句可以访问一个 $event
property:v-on:click="handle('ok', $event)"
。
从 2.4.0
开始,v-on
同样支持不带参数绑定一个事件/监听器键值对的对象。注意当使用对象语法时,是不支持任何修饰器的。
6.1、事件处理器
6.1.1、绑定方法
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 动态事件 (2.6.0+) -->
<button v-on:[event]="doThis"></button>
<!-- 动态事件缩写 (2.6.0+) -->
<button @[event]="doThis"></button>
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
<button @click="greet">Greet</button>
</div>
</body>
<script>
var vm = new Vue({
el:"#example-1",
data:{
counter:0,
name: 'Vue.js'
},
methods: {
greet: function (event) {
// `this` 在方法里指向当前 Vue 实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
console.log(event.target.tagName)
}
}
}
});
</script>
</html>
效果:
6.1.2、使用内联
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="example-3">
<button v-on:click="say('hi')">Say hi</button><br />
<a href="www.baidu.com" v-on:click="say2('you jump',$event)">www.baidu.com</a>
</div>
</body>
<script>
var vm = new Vue({
el:"#example-3",
methods: {
say: function (message) {
alert(message)
},
say2: function (message,event) {
if (event) {
//event.preventDefault()是通知浏览器不要执行与事件关联的默认动作,则a标签不会跳转链接
event.preventDefault()
}
alert(message)
}
}
});
</script>
</html>
效果:
6.2、事件修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。为了解决这个问题,Vue.js 为 v-on
提供了事件修饰符。
6.2.1、.stop、.prevent、.capture、.self
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div @click="stop_show2()">
<input type="button" value="按钮" @click="stop_show($event)"/>
<!--这里只会弹出一次-->
<input type="button" value="按钮.stop" @click.stop="stop_show2()"/>
</div>
</div>
<div id="app1">
<input type="button" value="鼠标右击" @contextmenu="prevent_show($event)"/>
<!--不会弹出右键菜单-->
<input type="button" value="鼠标右击.prevent" @contextmenu.prevent="prevent_show()"/>
</div>
<div id="app2">
<div id="obj1" v-on:click.capture="doc">
obj1
<div id="obj2" v-on:click.self="doc">
obj2
<div id="obj3" v-on:click="doc">
obj3
<div id="obj4" v-on:click="doc">
obj4
</div>
</div>
</div>
</div>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
methods:{
stop_show:function(ev){
alert("我是执行stop_show方法")
//注释掉这里会弹出两个
//ev.cancelBubble=true;//阻止事件冒泡
},
stop_show2:function(){
alert("我是执行stop_show2方法")
}
}
});
var vm1 = new Vue({
el:"#app1",
methods:{
prevent_show:function(ev){
alert("我是执行prevent_show方法")
//等效于加了这个,加了该方法鼠标右击事件被屏蔽
// ev.preventDefault();//阻止默认行为
}
}
});
var vm2 = new Vue({
el:"#app2",
methods: {
doc: function () {
//点击obj4的时候,弹出的顺序为:obj1、obj4、obj3;
//由于1有capture修饰符,故而先触发事件,2有self修饰符不是自己不会触发 然后就是4本身触发,最后冒泡事件3触发。
this.id= event.currentTarget.id;
alert(this.id)
}
}
});
</script>
</html>
6.2.1、.once、.passive
<!-- 点击事件将只会触发一次:2.1.4 新增 -->
<a v-on:click.once="doThis"></a>
<!--2.3.0 新增-->
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
<!--passive是:Chrome提出的一个新的浏览器特性,Web开发者通过一个新的属性passive来告诉浏览器,当前页面内注册的事件监听器内部是否会调用preventDefault函数来阻止事件的默认行为,以便浏览器根据这个信息更好地做出决策来优化页面性能。当属性passive的值为true的时候,
代表该监听器内部不会调用preventDefault函数来阻止默认滑动行为,Chrome浏览器称这类型的监听器为被动(passive)监听器。目前Chrome主要利用该特性来优化页面的滑动性能,所以Passive Event Listeners特性当前仅支持mousewheel/touch相关事件。
Passive Event Listeners特性是为了提高页面的滑动流畅度而设计的,页面滑动流畅度的提升,直接影响到用户对这个页面最直观的感受。在监听元素滚动事件的时候,会一直触发onscroll事件,在pc端是没啥问题的,但是在移动端,会让我们的网页变卡,
因此我们使用这个修饰符的时候,相当于给onscroll事件整了一个.lazy修饰符-->
<!--不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。-->
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="button" @click.once="ageadd" value="年龄加1"/>{{18+age}}
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
age:""
},
methods:{
ageadd:function(){
this.age++
}
}
});
</script>
</html>
6.3、按键修饰符
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on
在监听键盘事件时添加按键修饰符,记住所有的keyCode比较困难(有一些按键 [.esc
以及所有的方向键)]在 IE9 中有不同的 key
值, 如果你想支持 IE9,这些内置的别名应该是首选),所以Vue为最常用的键盘事件提供了别名:
.enter:
回车键.tab:
制表键.delete
(捕获“删除”和“退格”键):含delete和backspace键.esc:
返回键.space:
空格键.up:
向上键.down:
向下键.left:
向左键.right:
向右键
2.1.0 新增:
.ctrl
.alt
.shift
.meta:
在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)
2.2.0 新增:
.left:
只当点击鼠标左键时触发。.right:
只当点击鼠标右键时触发。.middle:
只当点击鼠标中键时触发。
2.5.0 新增:
.exact
修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>
注:可以通过全局 config.keyCodes
对象自定义按键修饰符别名:Vue.config.keyCodes.f1 = 112
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>
<div id="box">
<input type="text" value="按下按钮" @keydown="show($event)"/>
</div>
</div>
<div id="app">
<!-- enter回车键 -->
enter: <input type="text" v-model="msg" @keyup.enter="keys('enter')"><br>
<!-- tab键:进入时触发 -->
tab: <input type="text" v-model="msg" @keyup.tab="keys('tab')"><br>
<!-- delete键 -->
delete: <input type="text" v-model="msg" @keyup.delete="keys('delete/backspace')"><br>
<!-- esc键 -->
esc: <input type="text" v-model="msg" @keyup.esc="keys('esc')"><br>
<!-- space键 -->
space: <input type="text" v-model="msg" @keyup.space="keys('space')"><br>
<!-- up键 -->
up: <input type="text" v-model="msg" @keyup.up="keys('up')"><br>
<!--ctrl+Q:按住ctrl+Q放开Q的时候触发-->
ctrl+Q: <input type="text" v-model="msg" @keyup.ctrl.81="keys('ctrl+Q')"><br>
<!--鼠标右键-->
鼠标右键:<div :style="right_style" @contextmenu.prevent="mouseclick('右')"></div>
</div>
</body>
<script>
new Vue({
el: '#box',
data: {
},
methods:{
show:function(ev){
alert(ev.keyCode)
}
}
});
new Vue({
el:"#app",
data : {
msg : '',
right_style:{border:'solid 2px blue',height:'100px',width:'100px'}
},
methods : {
keys:function(key) {
alert('您按下的是' + key);
},
mouseclick:function(where){
alert('点击鼠标'+where+'键触发');
}
}
});
</script>
</html>
7、其他指令(v-pre、v-once、v-slot、v-cloak)
7.1、v-pre
不需要表达式,例如:<span v-pre>{{ this will not be compiled }}</span>
用法:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
7.2、v-cloak
不需要表达式,例如:[v-cloak] { display: none; },<div v-cloak> {{ message }} </div>
用法:这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none }
一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
作用:由于浏览器网速问题,Vue在渲染{{msg}}数据时可能会有延迟,这延迟的一瞬间{{msg}}会原样输出,如:原本输出的是 自如初
,延迟的结果就是{{msg}}
被原样输出了。可以使用v-text
与v-cloak
解决。v-text
只会输出文本,不会对HTML进行解析,若要输出HTML内容,那么v-text
有着明显的局限性,也不灵活。v-cloak
解决闪屏的问题。使用该指令时,要为该指令加display:none
的样式。
7.3、v-once
不需要表达式
详细:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 有子元素 -->
<div v-once>
<h1>comment</h1>
<p>{{msg}}</p>
</div>
<!-- 组件 -->
<my-component v-once :comment="msg"></my-component>
<!-- `v-for` 指令-->
<ul>
<li v-for="i in list" v-once>{{i}}</li>
</ul>
案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
/** 为v-cloak指令设置样式 **/
[v-cloak] {
display: none;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 输出结果为 {{ webUrl }}-->
<div v-pre>{{ webUrl }}</div>
<!-- 输出结果为 个人博客-->
<div v-cloak>{{ webName }}</div>
<!--加了v-once-->
<div v-once>
<input v-model="msg"><br />This will never change: {{msg}}
</div>
<span>This will change: {{msg}}</span>
</div>
</body>
<script type="text/javascript">
var vm=new Vue({
el:'#app',
data:{
msg:'hello vue',
webName:"个人博客"
}
});
</script>
</html>
效果:
7.4、v-slot
缩写:#
预期:可放置在函数参数位置的 JavaScript 表达式 (在支持的环境下可使用解构)。可选,即只需要在为插槽传入 prop 的时候使用。
参数:插槽名 (可选,默认值是 default
)
限用于:
<template>
- 组件 (对于一个单独的带 prop 的默认插槽)
用法:提供具名插槽或需要接收 prop 的插槽。
示例:后面用到了再介绍
<!-- 具名插槽 -->
<base-layout>
<template v-slot:header>
Header content
</template>
Default slot content
<template v-slot:footer>
Footer content
</template>
</base-layout>
<!-- 接收 prop 的具名插槽 -->
<infinite-scroll>
<template v-slot:item="slotProps">
<div class="item">
{{ slotProps.item.text }}
</div>
</template>
</infinite-scroll>
<!-- 接收 prop 的默认插槽,使用了解构 -->
<mouse-position v-slot="{ x, y }">
Mouse position: {{ x }}, {{ y }}
</mouse-position>
8、特殊 attribute(key、ref、is)
8.1、key
预期:number | string | boolean (2.4.2 新增) | symbol (2.5.12 新增)
用法:最常见的用例是结合 v-for,前面也已经介绍过了。
它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用。
- 完整地触发组件的生命周期钩子
- 触发过渡
注:这个在过渡中再介绍
<transition>
<span :key="text">{{ text }}</span>
</transition>
<!--当 text 发生改变时,<span> 总是会被替换而不是被修改,因此会触发过渡。-->
8.2、ref
预期:string
作用:ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs
对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件。
注:这个在组件中再介绍
<!-- `vm.$refs.p` will be the DOM node -->
<p ref="p">hello</p>
<!-- `vm.$refs.child` will be the child component instance -->
<child-component ref="child"></child-component>
8.3、 is
预期:string | Object (组件的选项对象)
作用:用于动态组件且基于 DOM 内模板的限制来工作。
示例:这个在组件中再介绍
<!-- 当 `currentView` 改变时,组件也跟着改变 -->
<component v-bind:is="currentView"></component>
<!-- 这样做是有必要的,因为 `<my-row>` 放在一个 -->
<!-- `<table>` 内可能无效且被放置到外面 -->
<table>
<tr is="my-row"></tr>
</table>
四、Vue实例及选项
每个 Vue 应用都是通过用 Vue
函数创建一个新的 Vue 实例开始的。前面我们已经用了很多次 new Vue({...})
的代码,而且Vue初始化的选项都已经用了data
、methods
、el
等,估计,应该已经都明白了他们的作用。当一个 Vue 实例被创建时,它将 data
对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
Vue中支持很多选项,如下图所示:
Vue实例的基本结构:
new Vue({
el:'#app',//所有的挂载元素会被 Vue 生成的 DOM 替换
data:{ },// this->window
methods:{ },// this->vm
//注意,不应该使用箭头函数来定义 method 函数 ,this将不再指向vm实例
props:{} ,// 可以是数组或对象类型,用于接收来自父组件的数据
//对象允许配置高级选项,如类型检测、自定义验证和设置默认值
watch:{ },// this->vm
computed:{},
render:{},
})
1、创建Vue实例
1.1、构造器
vue实例的创建:一般通过new关键字的方式来创建,构造函数的参数列表需要传入一个选项对象
var vm = new Vue(paramObj);
1.2、创建Vue实例的三种方式
Vue有三个属性和模板有关,官网上是这样解释的:
el:提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标;
template:一个字符串模板作为 Vue 实例的标识使用。模板将会 替换 挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽;
render:字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement
方法作为第一个参数用来创建 VNode;
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>vue实例创建方式</title>
</head>
<body>
<div id="app1">{{msg}}</div>
<div id="app2"></div>
<div id="app3"></div>
</body>
<script>
new Vue({
el: "#app1", //用el做模板
data: {
msg: "hello vue"
}
})
new Vue({
el: "#app2",
data: {
msg: "hello vue"
},
template: "<p id='cs'> {{msg}}</p>" //用template做模板
})
new Vue({
el: "#app3",
data: {
msg: "hello vue"
},
render: function(h) { //直接用render函数渲染
return h('div', this.msg)
}
})
</script>
</html>
效果:
2、 Vue实例化过程理解
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载 Dom、渲染 → 更新 → 渲染、销毁等一系列过程,我们称这是 Vue 的生命周期,通俗说就是 Vue 实例从创建到销毁的过程。每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁。四个主要过程:创建、挂载、更新、销毁。八个阶段(在这个过程中也会运行一些生命周期钩子):beforeCreate(创建前)、created(创建后)、beforeMount(挂载前)、mounted(载入后)、beforeUpdate(更新前)、updated(更新后)、beforeDestroy(销毁前)、destroyed(销毁后)。
引用官网一张图:
2.1、定义Vue
Vue 构造函数只执行了 this._init(options)
这么一句代码。
function Vue (options) {
if ("development" !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}
initMixin(Vue); // 定义 _init(初始化就已经加载了,里面定义了Vue的原型方法_init)
stateMixin(Vue); // 定义 $set $get $delete $watch 等
eventsMixin(Vue); // 定义事件 $on $once $off $emit
lifecycleMixin(Vue); // 定义 _update $forceUpdate $destroy
renderMixin(Vue); // 定义 _render 返回虚拟dom
2.2、initMixin
实例化Vue时,执行 _init, _init 定义在 initMixin 中,调用原型上的方法_init。这里主要做了这几件事情:
1)初始化 options 参数,将 _renderProxy 设置为 vm
2)生命周期变量初始化:vm.$parent vm.$root vm.$children vm.$refs等
3)初始化 渲染Render
4)初始化 vm的状态,prop/data/computed/method/watch都在这里完成初始化
5)挂载实例
Vue.prototype._init = function (options) {
// 合并 options
if (options && options._isComponent) {
initInternalComponent(vm, options); // 组件合并
} else {
// 非组件合并
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
initLifecycle(vm); // 定义 vm.$parent vm.$root vm.$children vm.$refs 等(生命周期变量初始化)
initEvents(vm); // 定义 vm._events vm._hasHookEvent 等(事件监听初始化)
initRender(vm); // 定义 $createElement $c (初始化渲染)
callHook(vm, 'beforeCreate'); // 回调 beforeCreate 钩子函数
initInjections(vm); // resolve injections before data/props (初始化注入)
initState(vm); // 初始化 props methods data computed watch 等方法 (状态初始化)
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created'); // 回调 created 钩子函数
// 如果有el选项,则自动开启模板编译阶段与挂载阶段
// 如果没有传递el选项,则不进入下一个生命周期流程
// 用户需要执行vm.$mount方法,手动开启模板编译阶段与挂载阶段
if (vm.$options.el) {
vm.$mount(vm.$options.el); // 实例挂载渲染dom
}
};
2.3、$mount
mount 调用 mountComponent函数,这里主要做了这几件事:
1)判断是否有render选项,如果有则直接构建虚拟DOM
2)如果没有render选项,则判断是否有template 或者el挂载了元素
3)回调挂载前函数beforeMount
4)执行vm._update(vm._render(), hydrating); 将虚拟dom转为真实的dom并挂载到页面
5)使用Watcher函数检测数据是否更新,如果有更新则回调更新前函数beforeUpdate和更新后函数updated,如果检测到已经没有虚拟DOM了,回调载入后函数。
Vue.prototype.$mount = function (
el,
hydrating
) {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating)
};
function mountComponent (
vm,
el,
hydrating
) {
vm.$el = el;
if (!vm.$options.render) { //如果选项中没有render选项
vm.$options.render = createEmptyVNode; //如果有render选项就创建虚拟DOM
{
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {//在判断选项中是否有template选项 或者 el挂载元素
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
);
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
);
}
}
}
callHook(vm, 'beforeMount');//回调 挂载前函数
var updateComponent;
/* istanbul ignore if */
if (config.performance && mark) {
updateComponent = function () {
var name = vm._name;
var id = vm._uid;
var startTag = "vue-perf-start:" + id;
var endTag = "vue-perf-end:" + id;
mark(startTag);
var vnode = vm._render();
mark(endTag);
measure(("vue " + name + " render"), startTag, endTag);
mark(startTag);
vm._update(vnode, hydrating);
mark(endTag);
measure(("vue " + name + " patch"), startTag, endTag);
};
} else {
updateComponent = function () {
vm._update(vm._render(), hydrating); //将虚拟dom转为真实的dom并挂载到页面
};
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, {
before: function before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate'); //回调更新前函数
}
}
}, true /* isRenderWatcher */);
hydrating = false;
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted'); //mounted(载入后)
}
return vm
}
3、实例选项DOM(el、template、render、renderError)
3.1、el
类型:string | Element
限制:只在用 new
创建实例时生效
详细:提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用 vm.$mount()
手动开启编译。如果 render
函数和 template
property 都不存在,挂载 DOM 元素的 HTML 会被提取出来用作模板。
3.2、template
类型:string
详细:一个字符串模板作为 Vue 实例的标识使用。模板将会替换挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽。
3.3、render
类型:(createElement: () => VNode) => VNode
详细:字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement
方法作为第一个参数用来创建 VNode
。
注:Vue 选项中的 render
函数若存在,则 Vue 构造函数不会从 template
选项或通过 el
选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。
3.4、renderError(2.2.0 新增)
类型:(createElement: () => VNode, error: Error) => VNode
详细:只在开发者环境下工作。
案例:在Vue实例创建中,就已经展示了三种模板创建方式。在Vue实例化过程介绍中,提到过如果没有el挂载元素,则需手动挂载。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app"></div>
</body>
<script>
new Vue({
render(h) {
throw new Error('oops')
},
renderError(h, err) {//只在开发环境使用
return h('pre', {
style: {
color: 'red'
}
}, err.stack)
}
}).$mount('#app')//手动挂载到app上
</script>
</html>
效果:
4、实例选项数据(data、props、propsData、computed、methods、watch)
4.1、methods
类型:{ [key: string]: Function }
详细:methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this
自动绑定为 Vue 实例。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>methods</title>
</head>
<body>
<div id="app">{{msg}}<button @click="ChangeMessage">测试按钮</button></div>
<script>
new Vue({
el:'#app',
data:{msg:"Hello World!"},
methods:{
ChangeMessage:function(){this.msg="Hello Vue!";}
}
})
</script>
</body>
</html>
methods源码分析:执行initState()过程中做了很多的初始化工作(初始化:props => methods =>data => computed => watch),如果有定义(if (opts.methods) { initMethods(vm, opts.methods); } ),则进行初始化
function initMethods (vm, methods) {
var props = vm.$options.props;
for (var key in methods) {
{
if (typeof methods[key] !== 'function') {//遍历methods对象,key是每个键,即函数名称
warn(
"Method \"" + key + "\" has type \"" + (typeof methods[key]) + "\" in the component definition. " +
"Did you reference the function correctly?",
vm
);
}
if (props && hasOwn(props, key)) {//如果props中有同名属性,则报错
warn(
("Method \"" + key + "\" has already been defined as a prop."),
vm
);
}
if ((key in vm) && isReserved(key)) {//如果key是以$或_开头则,也报错
warn(
"Method \"" + key + "\" conflicts with an existing Vue instance method. " +
"Avoid defining component methods that start with _ or $."
);
}
}
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);//如果key对应的值不是null,则执行bind()函数,将其绑定到Function的原型上
}
}
4.2、data
类型:Object | Function
限制:组件的定义只接受 function
。
详细:Vue 实例的数据对象。Vue 将会递归将 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个的 key/value 对):浏览器 API 创建的原生对象,原型上的 property 会被忽略。大概来说,data 应该只能是数据 - 不推荐观察拥有状态行为的对象。一旦观察过,你就无法在根数据对象上添加响应式 property。因此推荐在创建实例之前,就声明所有的根级响应式 property。实例创建之后,可以通过 vm.$data
访问原始数据对象。Vue 实例也代理了 data 对象上所有的 property,因此访问 vm.a
等价于访问 vm.$data.a
。以 _
或 $
开头的 property 不会被 Vue 实例代理,因为它们可能和 Vue 内置的 property、API 方法冲突。你可以使用例如 vm.$data._property
的方式访问这些 property。
当一个组件被定义,data
必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data
仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data
函数,每次创建一个新实例后,我们能够调用 data
函数,从而返回初始数据的一个全新副本数据对象。如果需要,可以通过将 vm.$data
传入 JSON.parse(JSON.stringify(...))
得到深拷贝的原始数据对象。
(如果组件选项中的 data
是对象而不是函数,当存在多个该组件时,引用的是相同的组件选项对象,组件操作data会互相影响。)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{msg}}
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"hello vue"
}
});
//在组件中必须是函数
Vue.component( 'component-name', Vue.extend({
//The "data" option should be a function that returns a per-instance value in component definitions.
data : function() {
return {msg : 'hello vue'};
}
}));
</script>
</html>
data源码分析:执行initState()过程中,如果有 if (opts.data) {initData(vm);}则调用initData方法。data中的属性不能和methods和props重名,调用observe方法对数组和对象分别进行了响应式设置(get/set)
function initData (vm) {
var data = vm.$options.data;
// 如果data是一个方法 就执行这个方法
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {};
if (!isPlainObject(data)) {
data = {};
warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
);
}
// proxy data on instance
var keys = Object.keys(data);// 所有定义的data的第一层的key
var props = vm.$options.props;// 所有定义的props
var methods = vm.$options.methods; // 所有定义的methods
var i = keys.length;
while (i--) {//判断是否和methods和props重名
var key = keys[i];
{
if (methods && hasOwn(methods, key)) {// key不能和方法名相同
warn(
("Method \"" + key + "\" has already been defined as a data property."),
vm
);
}
}
if (props && hasOwn(props, key)) {// key不可以和props定义的名称相同
warn(
"The data property \"" + key + "\" is already declared as a prop. " +
"Use prop default value instead.",
vm
);
} else if (!isReserved(key)) {// 看看是不是以$_开头 如果是以他们开头的 就给你抛异常
proxy(vm, "_data", key);// 设置代理
}
}
// observe data
observe(data, true /* asRootData */); // 走observe方法,响应式设置
}
4.3、computed
类型:{ [key: string]: Function | { get: Function, set: Function } }
详细:计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
注意如果你为一个计算属性使用了箭头函数,则 this
不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。
计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算。注意,如果某个依赖 (比如非响应式 property) 在该实例范畴之外,则计算属性是不会被更新的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>computed</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<p>computed={{sumab}}</p>
<p>computed={{sumab}}</p>
<p>methods={{sumabs()}}</p>
<p>methods={{sumabs()}}</p>
</div>
<div id="app1">
<select v-model="fullNamesx" >
<option value="上官 飞燕">上官 飞燕</option>
<option value="慕容 云海">慕容 云海</option>
<option value="长孙 无忌">长孙 无忌</option>
</select>
<!--双向绑定-->
<input v-model="fullNamesx"/>
</div>
</body>
<script>
var data={a: 1,b: 2,c: 3};
new Vue({
el: "#app",
data: data,
computed: {
//不需要设定处理可以不写set方法
sumab: function() {
console.log('这是cpmputed实现的a+b处理')
return Number(this.a) + Number(this.b)
}
},
methods: {
sumabs:function(){
console.log('这是methods实现的a+b处理')
return Number(this.a) + Number(this.b)
}
}
})
// [{"上官 飞燕"},{"慕容 云海"},{"长孙 无忌"}]
new Vue({
el: "#app1",
data: {
firstName:"",
lastName:""
},
computed: {
//什么时候执行:初始化显示""相关的data属性发生变化及使用fullNamesx属性时
fullNamesx:{
get:function(){
console.log("get:"+this.firstName + ' ' + this.lastName);
//计算属性中的get方法,方法的返回值就是属性值
return this.firstName + ' ' + this.lastName
},
set: function (val) {//val=取到的结果值
console.log("set:"+val);
var names = val.split(' ');
if(names.length>=2){
this.firstName =names[0];
this.lastName = names[names.length - 1];
}
}
}
}
})
</script>
</html>
效果:
源码分析:在initState()执行中,if (opts.computed) { initComputed(vm, opts.computed); }则调用initComputed。computed实际上就是watcher实现的
function initComputed (vm, computed) {
// $flow-disable-line
var watchers = vm._computedWatchers = Object.create(null);//存放computed的观察者
// computed properties are just getters during SSR
var isSSR = isServerRendering();
for (var key in computed) {
var userDef = computed[key];
var getter = typeof userDef === 'function' ? userDef : userDef.get;
if (getter == null) {
warn(
("Getter is missing for computed property \"" + key + "\"."),
vm
);
}
if (!isSSR) {
// create internal watcher for the computed property.
//computed实际上就是watcher实现的,这个实例的参数分别为vm实例,上方得到的getter,noop为回调函数,watcheroptions配置
watchers[key] = new Watcher( //生成观察者(Watcher实例)
vm,
getter || noop,
noop,
computedWatcherOptions
);
}
// component-defined computed properties are already defined on the
// component prototype. We only need to define computed properties defined
// at instantiation here.
if (!(key in vm)) {
defineComputed(vm, key, userDef); //将 computed 属性添加到组件实例上,并通过 get、set 获取或者设置属性值,并且重定义 getter 函数
} else {
if (key in vm.$data) {
warn(("The computed property \"" + key + "\" is already defined in data."), vm);
} else if (vm.$options.props && key in vm.$options.props) {
warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);
}
}
}
}
扩展:methods与computed区别与总结
4.4、watch
类型:{ [key: string]: string | Function | Object | Array }
详细:一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch()
,遍历 watch 对象的每一个 property。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>watch</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="button" @click="changeB" value="改变b值" /> <p :style="{'color':isRed}">b = {{b}}</p>
<p>{{array}}</p>
<button @click='addArray()'>点击改变数组</button>
<input type="text" v-model="c.name"/>
</div>
<script>
var vm = new Vue({
el:"#app",
data: {
a: 1,
b: 6,
c: {
name: "JohnZhu"
},
array:['zs','ls','ww','emz'],
isRed:"black"
},
watch: {
//监测a的变化
a: function(val, oldVal) {
console.log('new a: %s, old a: %s', val, oldVal)
},
//监测b的变化
b: 'someMethod',// 方法名
array:{
//官网提示:在变更 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。
handler:function(newVal,oldVal){
console.log(newVal+'--'+oldVal)//旧值与新值相同
},
deep:true,
immediate:true
},
//监听对象的属性变化
'c.name':function(val, oldVal){
console.log('new c.name: %s, old c.name: %s', val, oldVal);
}
},
methods: {
someMethod: function() {
this.isRed="red";
alert("b is changed");
},
changeB: function() {
this.b++;
},
addArray:function(){
this.array.push('66')
}
}
})
vm.a = 2; // new: 2, old: 1
</script>
</body>
</html>
效果:
4.5、props
类型:Array<string> | Object
详细:props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
type
:可以是下列原生构造函数中的一种:String
、Number
、Boolean
、Array
、Object
、Date
、Function
、Symbol
、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。Prop 类型的更多信息在此。default
:any
为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。required
:Boolean
定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。validator
:Function
自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 falsy 的值 (也就是验证失败),一个控制台警告将会被抛出。你可以在这里查阅更多 prop 验证的相关信息。
//更多内容将在组件中讲解
// 简单语法
Vue.component('props-demo-simple', {
props: ['size', 'myMessage']
})
// 对象语法,提供验证
Vue.component('props-demo-advanced', {
props: {
// 检测类型
height: Number,
// 检测类型 + 其他验证
age: {
type: Number,
default: 0,
required: true,
validator: function (value) {
return value >= 0
}
}
}
})
4.6、propsData
类型:{ [key: string]: any }
限制:只用于 new
创建的实例中。
详细:创建实例时传递 props。主要作用是方便测试。
var Comp = Vue.extend({
props: ['msg'],
template: '<div>{{ msg }}</div>'
})
var vm = new Comp({
propsData: {
msg: 'hello'
}
})
5、实例选项生命周期钩子
前面讲过实例的创建过程,每个 Vue 实例在被创建时都要经过一系列的初始化过程,同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
5.1、beforeCreate
-
类型:
Function
-
详细:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
5.2、created
-
类型:
Function
-
详细:在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,
$el
property 目前尚不可用。
从created到beforeMount的过程中,
首先会判断vue实例中有没有el选项,如果有的话则进行下面的编译,但是如果没有el选项,则停止生命周期,直到vue实例上调用vm.$mount(el)。
如果有el,再判断是否有template参数,如果有,则把其当作模板编译成render函数,如果没有,则把外部的html作为模板编译。template中的模板优先级高于outer HTML模板。
在vue对象中还有一个render函数,它是以createElement作为参数,然后做渲染操作,而且我们可以直接嵌入JSX.
综合排名优先级:render函数选项 > template选项 > outer HTML.
5.3、beforeMount
-
类型:
Function
-
详细:在挂载开始之前被调用:相关的
render
函数首次被调用。该钩子在服务器端渲染期间不被调用。
5.4、mounted
-
类型:
Function
-
详细:实例被挂载后调用,这时
el
被新创建的vm.$el
替换了。如果根实例挂载到了一个文档内的元素上,当mounted
被调用时vm.$el
也在文档内。该钩子在服务器端渲染期间不被调用。注意
mounted
不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在mounted
内部使用 vm.$nextTick:
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
5.5、beforeUpdate
-
类型:
Function
-
详细:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
5.6、updated
-
类型:
Function
-
详细:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。该钩子在服务器端渲染期间不被调用。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
注意
updated
不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在updated
里使用 vm.$nextTick:
updated: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
})
}
5.7、beforeDestroy
-
类型:
Function
-
详细:实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
5.8、destroyed
-
类型:
Function
-
详细:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。该钩子在服务器端渲染期间不被调用。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue生命周期学习</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
<button @click="destroy">手动销毁</button>
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Vue的生命周期'
},
methods:{
destroy:function(){
this.$destroy()//手动销毁
console.log("我执行了呀!");
//销毁后解绑了app,再次改变message值,vue不再对此动作进行响应了
//如果注释掉:this.$destroy()则生效
this.message="Vue的生命周期2";
}
},
beforeCreate: function() {
console.group('------beforeCreate创建前状态------');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //undefined
console.log("%c%s", "color:red", "message: " + this.message)//undefined
console.log("data、computed、watch、methods和DOM都不能使用");
},
created: function() {
console.group('------created创建完毕状态------');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
console.log("可以操作data、computed、watch、methods,但DOM还没挂载。通常在此进行页面初始化操作或简单的Ajax请求");
},
beforeMount: function() {
console.group('------beforeMount挂载前状态------');
console.log("%c%s", "color:red", "el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
mounted: function() {
console.group('------mounted 挂载结束状态------');
console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
console.log("实例被挂载到DOM上。通常用于执行Ajax请求。");
},
beforeUpdate: function() {
console.group('beforeUpdate 更新前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
updated: function() {
console.group('updated 更新完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
beforeDestroy: function() {
console.group('beforeDestroy 销毁前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
destroyed: function() {
console.group('destroyed 销毁完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message)
}
})
</script>
</html>
效果:
在Vue的整个生命周期中,它提供了一系列的事件,可以让我们注册js方法,可以让我们达到控制整个过程的目的地。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。以上这8个方法就是生命周期钩子函数。
5.9、activated
-
类型:
Function
-
详细:被 keep-alive 缓存的组件激活时调用。该钩子在服务器端渲染期间不被调用。
5.10、deactivated
-
类型:
Function
-
详细:被 keep-alive 缓存的组件停用时调用。该钩子在服务器端渲染期间不被调用。
5.11、errorCaptured(2.5.0+新增)
-
类型:
(err: Error, vm: Component, info: string) => ?boolean
-
详细: 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回
false
以阻止该错误继续向上传播。
5.12、其他(选项 / 资源、选项 / 组合、选项 / 其它)
关于Vue的选项还有一些,如下(都将在介绍全局API/组件中的时候在讲解):
选项 / 资源:directives、filters、components
选项 / 组合:parent、mixins、extends、provide / inject
选项 / 其它:name、delimiters、functional、model、inheritAttrs、comments
五、Vue实例的属性和方法
Vue
实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $
,以便与代理的数据属性区分。
1、Vue实例属性-DOM访问(vm.$el)
1.1、vm.$el
-
类型:
Element
-
只读
-
详细:Vue 实例使用的根 DOM 元素。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
</body>
<script>
var vm2 = new Vue({
el: "#app",
data: {
message: "hello vue."
}
});
console.log(vm2.$el); //vm2.$el === 原生js中document.getElementById("app")
vm2.$el.style.color = "red"; //变成红色
</script>
</html>
2、Vue实例属性-数据访问(vm.$data、vm.$options、vm.$props)
2.1、vm.$data
-
类型:
Object
-
详细:Vue 实例观察的数据对象。Vue 实例代理了对其 data 对象 property 的访问。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
</body>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "hello vue."
}
});
console.log(vm.$data) //{__ob__: Observer}
console.log(vm._data); //{__ob__: Observer}
console.log(vm.$data == vm._data); //true
//三种方式都可以访问到message
console.log(vm.$data.message); //hello vue
console.log(vm._data.message); //hello vue
console.log(vm.message); //hello vue
</script>
</html>
效果:
通过vm.$data.message、vm._data.message甚至是vm.message,都可以获取到message,实际上都是访问的vm._data.xxx。具体的实现可以参考这篇文章:https://blog.csdn.net/xiaoxianer321/article/details/112637956
2.2、vm.$options
-
类型:
Object
-
只读
-
详细:用于当前 Vue 实例的初始化选项。需要在选项中包含自定义 property 时会有用处
2.3、vm.$props(2.2.0 新增)
-
类型:
Object
-
详细:当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象 property 的访问。
3、Vue实例属性-组件树访问(vm.$parent、vm.$root、vm.$children、vm.$refs)
3.1、vm.$parent
-
类型:
Vue instance
-
只读
-
详细:父实例,如果当前实例有的话。
3.2、vm.$root
-
类型:
Vue instance
-
只读
-
详细:当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。
3.3、vm.$children
-
类型:
Array<Vue instance>
-
只读
-
详细:当前实例的直接子组件。需要注意
$children
并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用$children
来进行数据绑定,考虑使用一个数组配合v-for
来生成子组件,并且使用 Array 作为真正的来源。
3.4、vm.$refs
-
类型:
Object
-
只读
-
详细:一个对象,持有注册过
ref
attribute 的所有 DOM 元素和组件实例。
4、Vue实例属性-作用域/插槽(vm.$slots、vm.$scopedSlots、vm.$attrs、vm.$listeners)
4.1、vm.$slots
-
类型:
{ [name: string]: ?Array<VNode> }
-
只读
-
响应性:否
-
详细:用来访问被插槽分发的内容。每个具名插槽有其相应的 property (例如:
v-slot:foo
中的内容将会在vm.$slots.foo
中被找到)。default
property 包括了所有没有被包含在具名插槽中的节点,或v-slot:default
的内容。请注意插槽不是响应性的。如果你需要一个组件可以在被传入的数据发生变化时重渲染,我们建议改变策略,依赖诸如
props
或data
等响应性实例选项。
4.2、vm.$scopedSlots
-
类型:
{ [name: string]: props => Array<VNode> | undefined }
-
只读
-
详细:用来访问作用域插槽。对于包括
默认 slot
在内的每一个插槽,该对象都包含一个返回相应 VNode 的函数。vm.$scopedSlots
在使用渲染函数开发一个组件时特别有用。
注意:从 2.6.0 开始,这个 property 有两个变化:
1)作用域插槽函数现在保证返回一个 VNode 数组,除非在返回值无效的情况下返回 undefined
。
2)所有的 $slots
现在都会作为函数暴露在 $scopedSlots
中。如果你在使用渲染函数,不论当前插槽是否带有作用域,我们都推荐始终通过 $scopedSlots
访问它们。这不仅仅使得在未来添加作用域变得简单,也可以让你最终轻松迁移到所有插槽都是函数的 Vue 3。
4.3、vm.$attrs(2.4.0 新增)
-
类型:
{ [key: string]: string }
-
只读
-
详细:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (
class
和style
除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class
和style
除外),并且可以通过v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。
4.4、vm.$listeners(2.4.0 新增)
-
类型:
{ [key: string]: Function | Array<Function> }
-
只读
-
详细:包含了父作用域中的 (不含
.native
修饰器的)v-on
事件监听器。它可以通过v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
5、Vue实例属性-其他(vm.$isServer)
5.1、vm.$isServer
-
类型:
boolean
-
只读
-
详细:当前 Vue 实例是否运行于服务器。
源码如下:
//源码
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
});
var _isServer;
var isServerRendering = function () {
if (_isServer === undefined) {
/* istanbul ignore if */
//对应源码var inBrowser = typeof window !== 'undefined'; 利用浏览器的全局对象 window 做区分,因为在 nodejs 环境下是没有 window 这个全局对象的
//对应源码var inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;
//如果不是浏览器运行环境 不是微信运行环境 不是Node环境(Node顶层对象是global)
if (!inBrowser && !inWeex && typeof global !== 'undefined') {
// detect presence of vue-server-renderer and avoid
// Webpack shimming the process
//检测是否存在vue服务器渲染器并避免Webpack填充过程
_isServer = global['process'] && global['process'].env.VUE_ENV === 'server';
} else {
_isServer = false;
}
}
return _isServer
};
6、Vue实例方法-数据(vm.$watch、vm.$set、vm.$delete)
6.1、vm.$watch( expOrFn, callback, [options] )
参数:
{string | Function} expOrFn
要观测的属性{Function | Object} callback
可以是一个回调函数, 也可以是一个纯对象(这个对象要包含handle属性){Object} [options]
{boolean} immediate
immediate立即执行回调{boolean} deep
deep指的是深度观测
返回值:{Function} unwatch
用法:观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数得到的参数为新值和旧值。表达式只接受简单的键路径。对于更复杂的表达式,用一个函数取代。
注意:在变更 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。
6.1.1、要观测的属性{string | Function}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
firstName:<input v-model="firstName">
<br/>
lastName:<input v-model="lastName">
<p>fullName: {{fullName}}</p>
</div>
</body>
<script>
var vm=new Vue({
el:'#app',
data:{
firstName:"Dell",
lastName:"Lee",
fullName:'Dell Lee',
}
});
//vm.$watch( expOrFn, callback, [options] )
//expOrFn-键路径 监听firstName的变化,并在控制台打印
vm.$watch('firstName',function (newVal,oldVal) {
console.log(newVal);
console.log(oldVal);
})
//expOrFn-函数 监听两者的变化
vm.$watch(
function () {
this.fullName=this.firstName+' '+this.lastName;
}
)
</script>
</html>
效果:
6.1.2、deep选项
为了发现对象内部值的变化,可以在选项参数中指定 deep: true 。 注意监听数组的变动不需要这么做。(前面解释过重写了数组的操作方法)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
firstName:<input v-model="firstName">
<br/>
lastName:<input v-model="lastName">
<p>fullName: {{fullName}}</p>
</div>
</body>
<script>
var vm=new Vue({
el:'#app',
data:{
firstName:"Dell",
lastName:"Lee",
fullName:'Dell Lee',
info:{
age:20,
sex:'man'
}
}
});
//vm.$watch( expOrFn, callback, [options] )
vm.$watch(
'info.age', //直接用键值指定,没有deep也可以找到
function (newVal,oldVal) {
console.log("我没有deep,有键:"+newVal);
}
)
//函数中没有指定deep选项
vm.$watch(
'info',
function (newVal,oldVal) {
console.log("我没有deep,没有键:"+newVal.age);
}
)
//有deep选项:可深度观测
vm.$watch(
'info',
function (newVal,oldVal) {
console.log("我有deep:"+newVal.age);
},
{deep:true}
)
</script>
</html>
效果:
注:不使用键值单独使用函数也可以原因在于第一个参数如果是函数,调用属性会直接调用get方法。
//..源码
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
this.getter = parsePath(expOrFn)
}
this.value = this.get()
}
//..
6.1.3、immediate选项
在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
firstName:<input v-model="firstName">
<br/>
lastName:<input v-model="lastName">
<p>fullName: {{fullName}}</p>
</div>
</body>
<script>
var vm=new Vue({
el:'#app',
data:{
firstName:"Dell",
lastName:"Lee",
fullName:'Dell Lee',
info:{
age:20,
sex:'man'
}
}
});
//vm.$watch( expOrFn, callback, [options] )
vm.$watch(
'info',
function (newVal,oldVal) {
console.log(newVal.age);
},{ deep:true,immediate: true }
)
</script>
</html>
效果:
6.2、vm.$set( target, propertyName/index, value )
-
参数:
{Object | Array} target
{string | number} propertyName/index
{any} value
-
返回值:设置的值。
-
用法:这是全局
Vue.set
的别名。在 -
参考:
Vue.set
6.3、vm.$delete( target, propertyName/index )
-
参数:
{Object | Array} target
{string | number} propertyName/index
-
用法:
这是全局
Vue.delete
的别名。 -
参考:Vue.delete
7、Vue实例方法-事件(vm.$on、vm.$once、vm.$off、vm.$emit)
7.1、vm.$on( event, callback )
-
参数:
{string | Array<string>} event
(数组只在 2.2.0+ 中支持){Function} callback
-
用法:
监听当前实例上的自定义事件。事件可以由
vm.$emit
触发。回调函数会接收所有传入事件触发函数的额外参数。常用在子组件向父组件派发事件的时候。
7.2、vm.$emit( eventName, […args] )
-
参数:
{string} eventName
[...args]
- 用法:触发当前实例上的事件。附加参数都会传给监听器回调。
示例:前面我们接触过v-on指令,v-on:可监听普通dom的原生事件;可监听子组件emit的自定义事件;vm.$on:监听当前实例的自定义事件。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<button id="emit" @click='emit' >触发事件</button>
<button id="emitother" @click='emitother' >触发事件</button>
</div>
</body>
<script>
new Vue({
el: '#app',
data:{
msg1: "饭后要洗碗",
msg2: "周末要一起看电影",
msg3: "周末要出去逛街"
},
created:function(){
this.$on('need_wash', this.handleEvent); //实例监听:need_wash
this.$on(['need_other','need_wash'], this.handleEvents); //实例监听:need_other、need_wash
},
methods: {
handleEvent:function(es){
console.log(this.msg1,es); //
},
handleEvents:function(es){
if(es == "emitother"){
console.log("handleEvents:"+this.msg2,es); //
console.log("handleEvents:"+this.msg3,es); //
}else{
console.log("handleEvents:"+this.msg1,es); //
}
},
emit:function(es){
this.$emit('need_wash', es.target.id);
},
emitother:function(es){
this.$emit('need_other', es.target.id);
}
}
})
</script>
</html>
效果:
7.3、vm.$once( event, callback )
-
参数:
{string} event
{Function} callback
-
用法:
监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。
7.4、vm.$off( [event, callback] )
-
参数:
{string | Array<string>} event
(只在 2.2.2+ 支持数组){Function} [callback]
-
用法:移除自定义事件监听器。
-
如果没有提供参数,则移除所有的事件监听器;
-
如果只提供了事件,则移除该事件所有的监听器;
-
如果同时提供了事件与回调,则只移除这个回调的监听器。
-
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<button id="emit" @click='emit' >触发事件</button>
<button id="emitother" @click='emitother' >触发事件</button>
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data:{
msg1: "饭后要洗碗",
msg2: "周末要一起看电影",
msg3: "周末要出去逛街"
},
created:function(){
this.$once('need_wash', this.handleEvent); //只监听一次
this.$on(['need_other','need_wash'], this.handleEvents); //实例监听:need_other、need_wash
},
methods: {
handleEvent:function(es){
console.log(this.msg1,es); //
},
handleEvents:function(es){
if(es == "emitother"){
console.log("handleEvents:"+this.msg2,es); //
console.log("handleEvents:"+this.msg3,es); //
}else{
console.log("handleEvents:"+this.msg1,es); //
}
},
emit:function(es){
this.$emit('need_wash', es.target.id);
},
emitother:function(es){
this.$emit('need_other', es.target.id);
}
}
});
// vm.$off(); //无参数时默认移除所有监听
vm.$off('need_other'); //移除事件need_other的所有监听
vm.$off('need_wash', vm.handleEvents); //指定只移除这个回调的监听器
</script>
</html>
效果:
8、Vue实例方法-生命周期(vm.$mount、vm.$forceUpdate、vm.$nextTick、vm.$destroy)
8.1、vm.$mount( [elementOrSelector] )
-
参数:
{Element | string} [elementOrSelector]
{boolean} [hydrating]
-
返回值:
vm
- 实例自身 -
用法:如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用
vm.$mount()
手动地挂载一个未挂载的实例。如果没有提供
elementOrSelector
参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。这个方法返回实例自身,因而可以链式调用其它实例方法。前面已经介绍过。
8.2、vm.$forceUpdate()
用法:迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
//源码
Vue.prototype.$forceUpdate = function(){
const vm = this;
if(vm._watcher){
vm._watcher.update();
}
}
//vm._watcher就是实例的watcher,每当组件内依赖的数据发生变化时,都会自动触发实例中_watcher的update方法。
//vm.$forceUpdate是手动通知实例重新渲染。
8.3、vm.$nextTick( [callback] )
-
参数:
{Function} [callback]
-
用法:
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法
Vue.nextTick
一样,不同的是回调的this
自动绑定到调用它的实例上。2.1.0 起新增:如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不是原生支持 Promise (IE:你们都看我干嘛),你得自行 polyfill。
new Vue({
// ...
methods: {
// ...
example: function () {
// 修改数据
this.message = 'changed'
// DOM 还没有更新
this.$nextTick(function () {
// DOM 现在更新了
// `this` 绑定到当前实例
this.doSomethingElse()
})
}
}
})
8.4、vm.$destroy()
-
用法:
完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。
会触发
beforeDestroy
和destroyed
的钩子。在大多数场景中你不应该调用这个方法。最好使用
v-if
和v-for
指令以数据驱动的方式控制子组件的生命周期。
六、Vue的全局配置与全局API
1、Vue的全局配置
Vue.config
是一个对象,包含 Vue 的全局配置。可以在启动应用之前修改下列属性,这部分用到在做介绍,直接引用官网的。
1.1、silent
-
类型:
boolean
-
默认值:
false
-
用法:Vue.config.silent = true -->取消 Vue 所有的日志与警告。
1.2、optionMergeStrategies
-
类型:
{ [key: string]: Function }
-
默认值:
{}
Vue.config.optionMergeStrategies._my_option = function (parent, child, vm) {
return child + 1
}
const Profile = Vue.extend({
_my_option: 1
})
// Profile.options._my_option = 2
//自定义合并策略的选项。
//合并策略选项分别接受第一个参数作为父实例,第二个参数为子实例,Vue实例上下文被作为第三个参数传入。
1.3、devtools
-
类型: boolean
-
默认值: true (生产版为 false)
-
用法:
// 务必在加载 Vue 之后,立即同步设置以下内容
Vue.config.devtools = true
//配置是否允许 vue-devtools 检查代码。开发版本默认为 true,生产版本默认为 false。生产版本设为 true 可以启用检查。
1.4、errorHandler
-
类型:
Function
-
默认值:
undefined
-
用法:
Vue.config.errorHandler = function (err, vm) {
// handle error
}
//指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。
//Sentry, an error tracking service, provides official integration using this option.
1.5、warnHandler(2.4.0 新增)
-
类型:
Function
-
默认值:
undefined
-
用法:
Vue.config.warnHandler = function (msg, vm, trace) {
// `trace` 是组件的继承关系追踪
}
//为 Vue 的运行时警告赋予一个自定义处理函数。注意这只会在开发者环境下生效,在生产环境下它会被忽略
1.6、ignoredElements
-
类型:
Array<string | RegExp>
-
默认值:
[]
-
用法:
Vue.config.ignoredElements = [
'my-custom-web-component',
'another-web-component',
// 用一个 `RegExp` 忽略所有“ion-”开头的元素
// 仅在 2.5+ 支持
/^ion-/
]
//须使 Vue 忽略在 Vue 之外的自定义元素 (e.g. 使用了 Web Components APIs)。否则,它会假设你忘记注册全局组件或者拼错了组件名称,从而抛出一个关于 Unknown custom element 的警告。
1.6、keyCodes
-
类型:
{ [key: string]: number | Array<number> }
-
默认值:
{}
-
用法:
Vue.config.keyCodes = {
v: 86,
f1: 112,
// camelCase 不可用
mediaPlayPause: 179,
// 取而代之的是 kebab-case 且用双引号括起来
"media-play-pause": 179,
up: [38, 87] //给 v-on 自定义键位别名。
}
<input type="text" @keyup.media-play-pause="method">
1.7、performance(2.2.0 新增)
-
类型:
boolean
-
默认值:
false (自 2.2.3 起)
-
用法:设置为
true
以在浏览器开发工具的性能/时间线面板中启用对组件初始化、编译、渲染和打补丁的性能追踪。只适用于开发模式和支持performance.mark
API 的浏览器上。
1.8、productionTip(2.2.0 新增)
-
类型:
boolean
-
默认值:
true
-
用法:设置为
false
以阻止 vue 在启动时生成生产提示。
2、全局API
Vue的全局API提供大量的功能。
2.1、Vue.extend( options )
-
参数:
{Object} options
-
用法:
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
data
选项是特例,需要注意 - 在Vue.extend()
中它必须是函数。其主要用来服务于Vue.component
,用来生成组件。
Vue.extend(),说白了,有点类似java中的继承(父类与子类)的关系。
extend,这个词对我们来说并不陌生,jQuery为开发插件就提供了两个方法(.jQuery.extend(object)、jQuery.fn.extend(object)); 前者为扩展jQuery类本身,后者为自身添加新的方法。Vue.extend()会返回一个子(Sub)构造函数,Sub类似于继承了Vue构造函数。
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
</body>
<script>
const Com = Vue.extend({
template: '<h1 @click="handleClick">{{msg}}</h1>',
data:function(){ //必须是函数
return {
msg: 'Hello Vue.extend'
}
},
methods: {
handleClick:function(){
this.msg = this.msg.split('').reverse().join('')
}
}
})
var vmfb = new Com();
document.body.appendChild(vmfb.$mount().$el);
console.log(vmfb.$data.msg);//Hello Vue.extend
console.log(vmfb.$options);
</script>
</html>
效果:
2.2、Vue.nextTick( [callback, context] )
-
参数:
{Function} [callback]
{Object} [context]
-
用法:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。当你设置 vm.someData = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h id="h">{{msg}}</h>
<button @click = "changeTxt">改变值</button>
</div>
</body>
<script>
var vm = new Vue({
el: "#app",
data: {
msg: "Hello nextTick"
},
methods: {
//$nextTick方法
changeTxt: function() {
this.msg = "Hello nextTick Update"; //修改dom结构
this.$nextTick(function() { //使用vue.$nextTick()方法可以dom数据更新后延迟执行后续代码
var domTxt = document.getElementById('h').innerText;
console.log(domTxt); //输出可以看到vue数据修改后并没有DOM没有立即更新
if(domTxt === "Hello nextTick") {
console.log("文本data被修改后dom内容没立即更新");
} else {
console.log("文本data被修改后dom内容被马上更新了"); //输出结果
}
});
}
}
})
//全局方法:nextTick
vm.msg = "Hello nextTick 全局方法"
var domTxt = document.getElementById('h').innerText; //这个时候去操作DOM某些数据可能会无效
console.log(domTxt); //hello nextTick 这个时候并未取到新更新的值
Vue.nextTick(function () {
// DOM 更新了
var domTxt = document.getElementById('h').innerText; //这个时候就是最新的了
console.log(domTxt); //Hello nextTick 全局方法
})
</script>
</html>
效果:
2.3、Vue.set( target, propertyName/index, value )
-
参数:
{Object | Array} target
{string | number} propertyName/index
{any} value
-
返回值:设置的值。
-
用法:向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如
this.myObject.newProperty = 'hi'
)。
受 ES5 的限制,Vue.js 不能检测到对象属性的添加或删除。因为 Vue在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue转换它,才能让它是响应的。如:给对象新增一个data中没有的属性,或者通过数组下标新增时都无法让视图刷新。这时,就可以使用Vue.set()或者this.$set()
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div v-for="(item,index) in msg" :key="index">
<div v-if="index == 3">
<!--双重循环遍历-->
<li v-for="(it,i) in item" :key="i">{{i+'-'+it}}</li>
</div>
<div v-else>
{{index+'-'+item}}
</div>
</div>
<button @click="changedata">方法中添加属性</button>
</div>
</body>
<script>
var sz = ["1","2","3",{name:"老王",age:18,sex:"男"}];
var vm = new Vue({
el: "#app",
data: {
msg:sz
},
methods: {
changedata: function() {
this.msg[3].job = "自由职业new";
//this.msg.push("5"); //使用这个方法会更新,因为vue重写了数组的操作方法
//this.$set(this.msg,1,'two');
console.log(sz);
}
}
})
//视图不会更新
vm.msg[4] = "4";
vm.msg[3].job = "自由职业";
console.log(vm.msg);
// Vue.set(vm.msg, 1, 'two');
</script>
</html>
效果:
2.4、Vue.delete( target, propertyName/index )
-
参数:
{Object | Array} target
{string | number} propertyName/index
仅在 2.2.0+ 版本中支持 Array + index 用法。目标对象不能是一个 Vue 实例或 Vue 实例的根数据对象。
-
用法:
删除对象的 property。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到 property 被删除的限制,但是你应该很少会使用它。这个与set是反向的操作。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div v-for="(item,index) in msg" :key="index">
<div v-if="index == 3">
<!--双重循环遍历-->
<li v-for="(it,i) in item" :key="i">{{i+'-'+it}}</li>
</div>
<div v-else>
{{index+'-'+item}}
</div>
</div><br />
<button @click="changedata">方法中添加属性</button>
</div>
</body>
<script>
var sz = ["1","2","3",{name:"老王",age:18,sex:"男"}];
var vm = new Vue({
el: "#app",
data: {
msg:sz
},
methods: {
changedata: function() {
this.msg[3].job = "自由职业new";
// this.msg.push("5"); //使用这个方法会更新,因为vue重写了数组的操作方法
delete this.msg[4];
// this.$delete(this.msg[4]);
console.log(sz);
}
}
})
//视图不会更新
sz[4] = "4";
sz[5] = "5";
console.log(vm.msg);
// Vue.delete(vm.msg,4); //删除4
</script>
</html>
效果:
2.5、Vue.directive( id, [definition] )
-
参数:
{string} id
{Function | Object} [definition]
-
用法:注册或获取全局指令。
// 注册全局
Vue.directive('my-directive', {
bind: function () {},
inserted: function () {},
update: function () {},
componentUpdated: function () {},
unbind: function () {}
})
// 注册 (指令函数)
Vue.directive('my-directive', function () {
// 这里将会被 `bind` 和 `update` 调用
})
// getter,返回已注册的指令
var myDirective = Vue.directive('my-directive')
//注册局部
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
前面我们用过很多v-指令。在Vue2.x 中,代码复用和抽象的主要形式是组件。然而,有的情况下,我们仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
举个简单的栗子:设置一个焦点
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
{{msg}}聚焦:<input v-focus type="text" />
</div>
</body>
<script>
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
var vm = new Vue({
el:"#app",
data:{
msg:"vue directive"
}
})
</script>
</html>
上面我们使用到了一个钩子函数:inserted,一个指令定义对象可以提供如下几个钩子函数 (均为可选):
-
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 -
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 -
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
-
componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。 -
unbind
:只调用一次,指令与元素解绑时调用。
指令钩子函数会被传入以下参数:
el
:指令所绑定的元素,可以用来直接操作 DOM。binding
:一个对象,包含以下 property:name
:指令名,不包括v-
前缀。value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
。oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。
vnode
:Vue 编译生成的虚拟节点。oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
{{msg}}聚焦:<input id="txt" v-focus v-model="msg" type="text" />
</div>
</body>
<script>
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
bind: function(el, binding, vnode){
//只需要执行一次的一些操作
el.style.background="#FFFDD6";
console.log("我是bind");
},
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus();
console.log("我是inserted");
},
update: function(){
//根据获得的新值执行对应的更新
console.log("我是update");
},
componentUpdated: function(){
//指令所在组件的 VNode 及其子 VNode 全部更新后调用
console.log("我是componentUpdated");
},
unbind: function(){
//做清理操作
//比如移除bind时绑定的事件监听器
console.log("我是unbind");
}
})
var vm = new Vue({
el:"#app",
data:{
msg:"vue directive"
}
})
</script>
</html>
效果:
2.6、Vue.filter( id, [definition] )
-
参数:
{string} id
{Function} [definition]
-
用法:注册或获取全局过滤器。
//全局注册
// 注册
Vue.filter('my-filter', function (value) {
// 返回处理后的值
})
// getter,返回已注册的过滤器
var myFilter = Vue.filter('my-filter')
//局部注册
filters:{
myFilter:function(value){
value+='hello world'
return value
}
}
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind
表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
<!-- 过滤器可以串联,也可以接收参数 -->
{{ message | filterA | filterB }}
{{ message | filterA('arg1', arg2) }}
过滤器原理简述:
{{message|myFilter}}
//这个过滤器在模板编译后
_s(_f('myFilter')(message))
//_f这个函数是resolveFilter的别名:找到我们写的过滤器,并将参数传入进去并执行
//_s函数是toString函数的别名
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
全局/局部过滤器:<input v-model="message"/>{{ message | capitalize | capitalizes}}
<br />
过滤器搜索:
<input type="text" placeholder="请输入搜索内容" v-model="isSearch">
<div v-for="(item,index) in search" :key="index">{{ item.name }}
</div>
</body>
<script>
//注册全局过滤器
Vue.filter('capitalize', function (value) {
if (!value) {
return '';
}
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
})
new Vue({
el: '#app',
data: {
message:"hello vue",
list: [ {id: '1',name: 'AAA'},
{id: '2',name: 'BBB'},
{id: '3',name: 'CCC'},
{id: '4',name: 'AAA'},
{id: '5',name: 'BBB'},
{id: '6',name: 'DDD'}
],
isSearch: '' //
},
//局部过滤器,如果同名则会覆盖全局的
filters: {
capitalizes: function (value) {
if (!value){
return ''
}
value = value.toString()
return value.slice(0,5)+value.charAt(6).toUpperCase() + value.slice(7);
}
},
//实现搜索功能
computed: {
search:function(){
var arr=[];
for (var i=0;i<this.list.length;i++) {
if(this.list[i].name.indexOf(this.isSearch)!=-1){
arr.push(this.list[i]);
}
}
console.log(arr);
return arr;
}
}
})
</script>
</html>
效果:
2.7、Vue.use( plugin )
-
参数:
{Object | Function} plugin
-
用法:
安装 Vue.js 插件。如果插件是一个对象,必须提供
install
方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。该方法需要在调用
new Vue()
之前被调用。当 install 方法被同一个插件多次调用,插件将只会被安装一次。
-
参数:
{string} template
-
用法:将一个模板字符串编译成 render 函数。只在完整版时可用。
compile 的内容非常多,大致分为三块主要内容,我也称他们是Vue的 渲染三巨头,就是: parse,optimize,generate。
Parse的作用:接收 template 原始模板,按照模板的节点和数据生成对应的 ast(一种语法树)
Optimize的作用:遍历递归每一个ast节点,标记静态的节点。减少去比对这部分DOM,从而达到性能优化的目的。
Generate的作用:把前两步生成完善的 ast 组装成 render 字符串。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
</div>
</body>
<script>
var res = Vue.compile('<div><span>{{ msg }}</span></div>')
new Vue({
data: {
msg: 'hello vue compile'
},
render: res.render,
staticRenderFns: res.staticRenderFns
}).$mount("#app");
</script>
</html>
效果:
2.8、Vue.observable( object ) (2.6.0 新增)
-
参数:
{Object} object
-
用法:
让一个对象可响应。Vue 内部会用它来处理
data
函数返回的对象。返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器,用于简单的场景:
const state = Vue.observable({ count: 0 })
const Demo = {
render(h) {
return h('button', {
on: { click: () => { state.count++ }}
}, `count is: ${state.count}`)
}
}
在 Vue 2.x 中,被传入的对象会直接被 Vue.observable
变更,所以如这里展示的,它和被返回的对象是同一个对象。在 Vue 3.x 中,则会返回一个可响应的代理,而对源对象直接进行变更仍然是不可响应的。因此,为了向前兼容,我们推荐始终操作使用 Vue.observable
返回的对象,而不是传入源对象。
2.9、Vue.version
-
细节:提供字符串形式的 Vue 安装版本号。这对社区的插件和组件来说非常有用,你可以根据不同的版本号采取不同的策略。
-
用法:
var version = Number(Vue.version.split('.')[0])
if (version === 2) {
// Vue v2.x.x
} else if (version === 1) {
// Vue v1.x.x
} else {
// Unsupported versions of Vue
}
2.10、Vue.mixin( mixin )
-
参数:
{Object} mixin
-
用法:
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用。
2.11、Vue.component( id, [definition] )
-
参数:
{string} id
{Function | Object} [definition]
-
用法:注册或获取全局组件。注册还会自动使用给定的
id
设置组件的名称
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 获取注册的组件 (始终返回构造器)
var MyComponent = Vue.component('my-component')
在Vue2.x 中,代码复用和抽象的主要形式是组件。关于组件、路由、过渡动画、vue脚手架等内容将在后续文章中介绍。^_^
更多推荐
所有评论(0)