赞
踩
关于Vue简介,百度百科给出的解释是:Vue.js是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和Vue生态系统支持的库开发的复杂单页应用。
Vue.js 是一个提供了 MVVM 风格双向数据绑定的 Javascript 库(无依赖别的js库,直接引入一个js文件就可以使用,跟jquery差不多),专注于View 层。它的核心是 MVVM 中的 VM,也就是 ViewModel。 ViewModel负责连接 View 和 Model,保证视图和数据的一致性,这种轻量级的架构让前端开发更加高效、便捷。
从历史的潮流来说,人们从之前的:原生JS -> Jquery之类的类库 -> 前端模板引擎 ,他们都有一个共同的特点需要我们去操作dom元素。近几年来,得益于手机设备的普及和性能的提升,移动端的web需求大量增加,产生了一种叫webapp的东西,也就是移动端的网页应用。为了更好满足当前移动webapp项目的开发需求,MVVM框架诞生,而Vue便是这样的一种框架。那么什么是Vue?
官网有一句对它的介绍:通过尽可能简单的API实现响应的数据绑定和组合的视图组件。Vue的核心主要包括下面两块:
Vue的数据驱动:数据改变驱动了视图的自动更新,传统的做法你得手动改变DOM来改变视图,vuejs只需要改变数据,就会自动改变视图;
视图组件化:把整一个网页的拆分成一个个区块,每个区块我们可以看作成一个组件。网页由多个组件拼接或者嵌套组成(可维护、可重用)。
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)。
目前的流行前端框架/库有: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开发而设计的框架,适应不同的移动设备,而且可以和其他框架一起引入项目中使用,非常灵活,优秀。
注:框架和库的区别
库(插件):是一种封装好的特定方法集合,对项目的侵入性较小,提供给开发者使用,控制权在使用者手中,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。
框架:是一套架构,会基于自身特点向用户提供一套相当完整的解决方案,而且控制权在框架本身;对项目的侵入性较大,使用者要按照框架所规定的某种特定规范进行开发,项目如果需要更换框架,则需要重新架构整个项目。
相同点:
都支持指令:内置指令和自定义指令。
都支持过滤器:内置过滤器和自定义过滤器。
都支持双向数据绑定。
都不支持低端浏览器。
不同点:
AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观。
在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢。
Vue.js使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立触发的。
对于庞大的应用来说,这个优化差异还是比较明显的。
相同点:
React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。
中心思想相同:一切都是组件,组件实例之间可以嵌套。
都提供合理的钩子函数,可以让开发者定制化地去处理需求。
都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载。
在组件开发中都支持mixins的特性。
不同点:
React依赖Virtual DOM,而Vue.js使用的是DOM模板。React采用的Virtual DOM会对渲染出来的结果做脏检查。
Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作DOM。
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。
时间 | 事件 |
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的开发环境之前,一定一定要先下载node.js,vue的运行是要依赖于node的npm的包管理工具来实现,node可以在官网或者中文网里面下载。官网地址:https://nodejs.org/en/download/
备注:windows7 环境下只能使用Node.js 12.x及以下版本,官网下载地址:https://nodejs.org/en/download/releases/
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'
- }
- ],
-
- })
1)直接通过script加载CDN文件或者直接加载本地vue.js文件
2)npm(vue- cli)基于npm管理依赖使用nmp(vue- cli)来安装Vue
即前面的Vue环境搭建,不过官方文档上说:我们不推荐新手直接使用 vue- cli ,尤其是在你还不熟悉基于 Node.js 的构建工具时。
学习Vue不要在想着怎么操作DOM,而是想着如何操作数据。那么Vue做了什么,我们要学习什么,先来一张总结性质的思维导图(此图为Vue2.x版本):
数据绑定是将数据和视图关联,当数据发生变化是,可以自定更新视图。
文本插值是最基本的形式,使用双大括号{{}},类似于Mustache(Mustache是一个logic-less(轻逻辑)模板解析引擎,它的优势在于可以应用在Javascript、PHP、Python、Perl等多种编程语言中。)
{{msg|表达式}}标签将会被替换为 data 对象上对应的 msg 属性的值,也支持写入表达式(表达式是各种数值、变量、运算符的综合体)。
注意:Mustache 语法不能作用在 HTML 元素的属性上。
指令是带有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>
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>
前面的案例中我们已经接触到了一个指令v-show,这种带有前缀 v-的指令,用于表示它们是 Vue 提供的特殊特性。在Vue中还定义了许多这种的内置指令。
预期:string
详细:更新元素的 textContent
。如果要更新部分的 textContent
,需要使用 {{ Mustache }}
插值。
预期: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>
预期:any
用法:根据表达式之真假值,切换元素的 display
CSS property。当条件变化时该指令触发过渡效果。
预期:any
用法:根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是 <template>
,将提出它的内容作为条件块。当条件变化时该指令触发过渡效果。
注意:当和 v-if
一起使用时,v-for
的优先级比 v-if
更高。v-show是css切换,v-if是完整的销毁和重新创建使用频繁切换时用v-show,运行时较少改变时用v-if
用法:不需要表达式,为 v-if
或者 v-else-if
添加“else 块”。
限制:前一兄弟元素必须有 v-if
或 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>
预期: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>
官网有一句: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>
Vue重写了这些方法,使得他们具备响应式更新
缩写::
预期: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:属性名.修饰符="属性值"
- <!-- 绑定一个属性 -->
- <img v-bind:src="imageSrc">
- <!-- 缩写 -->
- <img :src="imageSrc">
- <!-- 动态特性名 (2.6.0+) -->
- <button v-bind:[key]="value"></button>
- <!-- 动态特性名缩写 (2.6.0+) -->
- <button :[key]="value"></button>
- <!-- 内联字符串拼接:fileName ,'img/'加上单引号之后,并不会进行属性值绑定,而是将字符串原样渲染后绑定属性上。-->
- <img :src="'img/' + fileName">
对应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>
效果图:
对于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>
效果图:
官网的解释:作为一个 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效果:
.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>
效果图:
预期:随表单控件类型不同而不同。
限制:
<input>
<select>
<textarea>
修饰符:
用法:在表单控件或者组件上创建双向绑定。
案例:
- <!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>
效果:
缩写:@
预期: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.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>
效果:
在事件处理程序中调用 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>
在监听键盘事件时,我们经常需要检查详细的按键。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>
不需要表达式,例如:<span v-pre>{{ this will not be compiled }}</span>
用法:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
不需要表达式,例如:[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
的样式。
不需要表达式
详细:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
- <!-- 单个元素 -->
- <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>
效果:
缩写:#
预期:可放置在函数参数位置的 JavaScript 表达式 (在支持的环境下可使用解构)。可选,即只需要在为插槽传入 prop 的时候使用。
参数:插槽名 (可选,默认值是 default
)
限用于:
<template>
用法:提供具名插槽或需要接收 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>
预期:number | string | boolean (2.4.2 新增) | symbol (2.5.12 新增)
用法:最常见的用例是结合 v-for,前面也已经介绍过了。
它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用。
注:这个在过渡中再介绍
- <transition>
- <span :key="text">{{ text }}</span>
- </transition>
-
- <!--当 text 发生改变时,<span> 总是会被替换而不是被修改,因此会触发过渡。-->
预期: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>
预期:string | Object (组件的选项对象)
作用:用于动态组件且基于 DOM 内模板的限制来工作。
示例:这个在组件中再介绍
- <!-- 当 `currentView` 改变时,组件也跟着改变 -->
- <component v-bind:is="currentView"></component>
-
- <!-- 这样做是有必要的,因为 `<my-row>` 放在一个 -->
- <!-- `<table>` 内可能无效且被放置到外面 -->
- <table>
- <tr is="my-row"></tr>
- </table>
每个 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:{},
- })
vue实例的创建:一般通过new关键字的方式来创建,构造函数的参数列表需要传入一个选项对象
var vm = new Vue(paramObj);
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>
效果:
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载 Dom、渲染 → 更新 → 渲染、销毁等一系列过程,我们称这是 Vue 的生命周期,通俗说就是 Vue 实例从创建到销毁的过程。每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁。四个主要过程:创建、挂载、更新、销毁。八个阶段(在这个过程中也会运行一些生命周期钩子):beforeCreate(创建前)、created(创建后)、beforeMount(挂载前)、mounted(载入后)、beforeUpdate(更新前)、updated(更新后)、beforeDestroy(销毁前)、destroyed(销毁后)。
引用官网一张图:
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
实例化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
- }
- };
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
- }
类型:string | Element
限制:只在用 new
创建实例时生效
详细:提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用 vm.$mount()
手动开启编译。如果 render
函数和 template
property 都不存在,挂载 DOM 元素的 HTML 会被提取出来用作模板。
类型:string
详细:一个字符串模板作为 Vue 实例的标识使用。模板将会替换挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽。
类型:(createElement: () => VNode) => VNode
详细:字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement
方法作为第一个参数用来创建 VNode
。
注:Vue 选项中的 render
函数若存在,则 Vue 构造函数不会从 template
选项或通过 el
选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。
类型:(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>
效果:
类型:{ [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的原型上
- }
- }
类型: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方法,响应式设置
- }
类型:{ [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区别与总结
类型:{ [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>
效果:
类型:Array<string> | Object
详细:props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
type
:可以是下列原生构造函数中的一种:String
、Number
、Boolean
、Array
、Object
、Date
、Function
、Symbol
、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。Prop 类型的更多信息在此。default
:any
required
:Boolean
validator
:Function
- //更多内容将在组件中讲解
- // 简单语法
- 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
- }
- }
- }
- })
类型:{ [key: string]: any }
限制:只用于 new
创建的实例中。
详细:创建实例时传递 props。主要作用是方便测试。
- var Comp = Vue.extend({
- props: ['msg'],
- template: '<div>{{ msg }}</div>'
- })
-
- var vm = new Comp({
- propsData: {
- msg: 'hello'
- }
- })
前面讲过实例的创建过程,每个 Vue 实例在被创建时都要经过一系列的初始化过程,同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
类型:Function
详细:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
类型: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.
类型:Function
详细:在挂载开始之前被调用:相关的 render
函数首次被调用。该钩子在服务器端渲染期间不被调用。
类型: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
- })
- }
类型:Function
详细:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
类型: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
- })
- }
类型:Function
详细:实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
类型: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个方法就是生命周期钩子函数。
类型:Function
详细:被 keep-alive 缓存的组件激活时调用。该钩子在服务器端渲染期间不被调用。
类型:Function
详细:被 keep-alive 缓存的组件停用时调用。该钩子在服务器端渲染期间不被调用。
类型:(err: Error, vm: Component, info: string) => ?boolean
详细: 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false
以阻止该错误继续向上传播。
关于Vue的选项还有一些,如下(都将在介绍全局API/组件中的时候在讲解):
选项 / 资源:directives、filters、components
选项 / 组合:parent、mixins、extends、provide / inject
选项 / 其它:name、delimiters、functional、model、inheritAttrs、comments
Vue
实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $
,以便与代理的数据属性区分。
类型: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>
类型: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
类型:Object
只读
详细:用于当前 Vue 实例的初始化选项。需要在选项中包含自定义 property 时会有用处
类型:Object
详细:当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象 property 的访问。
类型:Vue instance
只读
详细:父实例,如果当前实例有的话。
类型:Vue instance
只读
详细:当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。
类型:Array<Vue instance>
只读
详细:当前实例的直接子组件。需要注意 $children
并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children
来进行数据绑定,考虑使用一个数组配合 v-for
来生成子组件,并且使用 Array 作为真正的来源。
类型:Object
只读
详细:一个对象,持有注册过 ref
attribute 的所有 DOM 元素和组件实例。
类型:{ [name: string]: ?Array<VNode> }
只读
响应性:否
详细:用来访问被插槽分发的内容。每个具名插槽有其相应的 property (例如:v-slot:foo
中的内容将会在 vm.$slots.foo
中被找到)。default
property 包括了所有没有被包含在具名插槽中的节点,或 v-slot:default
的内容。
请注意插槽不是响应性的。如果你需要一个组件可以在被传入的数据发生变化时重渲染,我们建议改变策略,依赖诸如 props
或 data
等响应性实例选项。
类型:{ [name: string]: props => Array<VNode> | undefined }
只读
详细:用来访问作用域插槽。对于包括 默认 slot
在内的每一个插槽,该对象都包含一个返回相应 VNode 的函数。vm.$scopedSlots
在使用渲染函数开发一个组件时特别有用。
注意:从 2.6.0 开始,这个 property 有两个变化:
1)作用域插槽函数现在保证返回一个 VNode 数组,除非在返回值无效的情况下返回 undefined
。
2)所有的 $slots
现在都会作为函数暴露在 $scopedSlots
中。如果你在使用渲染函数,不论当前插槽是否带有作用域,我们都推荐始终通过 $scopedSlots
访问它们。这不仅仅使得在未来添加作用域变得简单,也可以让你最终轻松迁移到所有插槽都是函数的 Vue 3。
类型:{ [key: string]: string }
只读
详细:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class
和 style
除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class
和 style
除外),并且可以通过 v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。
类型:{ [key: string]: Function | Array<Function> }
只读
详细:包含了父作用域中的 (不含 .native
修饰器的) v-on
事件监听器。它可以通过 v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
类型: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
- };
参数:
{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>
效果:
参数:
{Object | Array} target
{string | number} propertyName/index
{any} value
返回值:设置的值。
用法:这是全局 Vue.set
的别名。在
参考:
Vue.set
参数:
{Object | Array} target
{string | number} propertyName/index
用法:
这是全局 Vue.delete
的别名。
参考:Vue.delete
参数:
{string | Array<string>} event
(数组只在 2.2.0+ 中支持){Function} callback
用法:
监听当前实例上的自定义事件。事件可以由 vm.$emit
触发。回调函数会接收所有传入事件触发函数的额外参数。常用在子组件向父组件派发事件的时候。
参数:
{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>
效果:
参数:
{string} event
{Function} 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>
效果:
参数:
{Element | string} [elementOrSelector]
{boolean} [hydrating]
返回值:vm
- 实例自身
用法:如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount()
手动地挂载一个未挂载的实例。
如果没有提供 elementOrSelector
参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。
这个方法返回实例自身,因而可以链式调用其它实例方法。前面已经介绍过。
用法:迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
- //源码
- Vue.prototype.$forceUpdate = function(){
- const vm = this;
- if(vm._watcher){
- vm._watcher.update();
- }
- }
-
- //vm._watcher就是实例的watcher,每当组件内依赖的数据发生变化时,都会自动触发实例中_watcher的update方法。
- //vm.$forceUpdate是手动通知实例重新渲染。
参数:
{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()
- })
- }
- }
- })
用法:
完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。
会触发 beforeDestroy
和 destroyed
的钩子。
在大多数场景中你不应该调用这个方法。最好使用 v-if
和 v-for
指令以数据驱动的方式控制子组件的生命周期。
Vue.config
是一个对象,包含 Vue 的全局配置。可以在启动应用之前修改下列属性,这部分用到在做介绍,直接引用官网的。
类型:boolean
默认值:false
用法:Vue.config.silent = true -->取消 Vue 所有的日志与警告。
类型:{ [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实例上下文被作为第三个参数传入。
类型: boolean
默认值: true (生产版为 false)
用法:
- // 务必在加载 Vue 之后,立即同步设置以下内容
- Vue.config.devtools = true
- //配置是否允许 vue-devtools 检查代码。开发版本默认为 true,生产版本默认为 false。生产版本设为 true 可以启用检查。
类型:Function
默认值:undefined
用法:
- Vue.config.errorHandler = function (err, vm) {
- // handle error
- }
- //指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。
- //Sentry, an error tracking service, provides official integration using this option.
类型:Function
默认值:undefined
用法:
- Vue.config.warnHandler = function (msg, vm, trace) {
- // `trace` 是组件的继承关系追踪
- }
- //为 Vue 的运行时警告赋予一个自定义处理函数。注意这只会在开发者环境下生效,在生产环境下它会被忽略
类型: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 的警告。
类型:{ [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">
类型:boolean
默认值:false (自 2.2.3 起)
用法:设置为 true
以在浏览器开发工具的性能/时间线面板中启用对组件初始化、编译、渲染和打补丁的性能追踪。只适用于开发模式和支持 performance.mark
API 的浏览器上。
类型:boolean
默认值:true
用法:设置为 false
以阻止 vue 在启动时生成生产提示。
Vue的全局API提供大量的功能。
参数:
{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>
效果:
参数:
{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>
效果:
参数:
{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>
效果:
参数:
{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>
效果:
参数:
{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>
效果:
参数:
{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>
效果:
参数:
{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>
效果:
参数:
{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
返回的对象,而不是传入源对象。
细节:提供字符串形式的 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
- }
参数:
{Object} mixin
用法:
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用。
参数:
{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脚手架等内容将在后续文章中介绍。^_^
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。