首页 > 代码库 > vue.js基础知识篇(1):简介、数据绑定、指令、计算属性、表单控件绑定和过滤器
vue.js基础知识篇(1):简介、数据绑定、指令、计算属性、表单控件绑定和过滤器
目录
第一章:vue.js是什么?
代码
链接: http://pan.baidu.com/s/1qXCfzRI 密码: 5j79
第一章:vue.js是什么?
1.vue.js是MVVM框架
MVVM的代表框架是Angular.js,以及vue.js。
MVVM的view和model是分离的,View的变化会自动更新到ViewModel上,ViewModel的变化会自动同步到View上显示。这种自动同步依赖于ViewModel的属性实现了Observer。
2.它与angular.js的区别
相同点:都支持指令、过滤器和双向绑定,都不支持低端浏览器。
不同点:
(1)学习成本不一样,比如ng有依赖注入。
(2)性能上ng依赖对数据做脏检查,所以watcher越多越慢。vuel.js依赖于追踪的观察并且使用异步队列更新,所以数据都是独立触发的。
3.vue.js的安装
可以通过Script标签引入vue.js文件,可以通过npm安装。
4.第一个hello world程序
(1)通过npm安装vue的使用
step1:
$ npm init
$ npm install vue --save-dev
step2:
//app.js代码
var vue=require("vue"); console.log(vue);
运行,
$ node app.js
OK。
(2)直接通过script标签引入vuel.js
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue.js</title> </head> <body> <div id="didi-navigator"> <ul> <li v-for="tab in tabs"> {{tab.text}} </li> </ul> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ tabs:[ {text:"巴士"}, {text:"快车"}, {text:"顺风车"}, {text:"出租车"}, {text:"代驾"} ] } }); </script> </body> </html>
浏览器的显示效果是
第2章:数据绑定
1.语法
(1)类似angularjs的{{}},插值
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <div>Text:{{text}}</div> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#app", data:{ text:"我是文本" } }) </script> </body> </html>
可以看到如果text的值发生改变,文本中的值也会联动的变化。(竟然难过:(。书上说的使用星号实现单次渲染、3个大括号填入HTML片段,用1.0.24版本的控制台报错,说是不合法的表达式,说明这两种怪异的语法已经过时了。)
注意:vue指令和自身的特性是不可以插值的。
(2)表达式
表达式由js表达式和过滤器构成。过滤器本质是一个函数,要使用过滤器,要先写过滤符(|)。
关于大写过滤器的例子。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <div>{{msg|uppercase}}</div> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#app", data:{ msg:"eeeeeeeqeeecfff" } }) </script> </body> </html>
显示效果:
关于过滤器的到底有哪些内置的,可以通过查看源代码获取到。搜索"var filters={",就能找到源代码了。
(3)指令
指令是带有v-前缀的特殊特性,其值限定为绑定表达式,也就是js表达式和过滤器,那么当表达式的值发生变化时,这个变化也会反映到DOM上。
2.可以通过Vue.config配置数据绑定的语法
vue.js中数据绑定的语法被设计为可配置。
//delimiters搜索源码
var delimiters = [‘{{‘, ‘}}‘]; var unsafeDelimiters = [‘{{{‘, ‘}}}‘]; //............... delimiters: { /** * Interpolation delimiters. Changing these would trigger * the text parser to re-compile the regular expressions. * * @type {Array<String>} */ get: function get() { return delimiters; }, set: function set(val) { delimiters = val; compileRegex(); }, configurable: true, enumerable: true },
源代码说的很清楚,configurable是可配置的。
核心代码就一句:Vue.config.delimiters=["<%","%>"]。delimiters是config对象的属性,规定了默认的文本插值的分隔符。例子如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <div><%msg%></div> </div> <script src="js/vue.js"></script> <script> Vue.config.delimiters=["<%","%>"] var vm=new Vue({ el:"#app", data:{ msg:"eeeeeeeqeeecfff" } }) </script> </body> </html>
成功!
第三章:指令
1.语法
指令以v-打头,它的值限定为绑定表达式,它负责的是按照表达式的值应用某些行为到DOM上。
内部指令有v-show,v-else,v-model,v-repeat,v-for,v-text,v-el,v-html,v-on,v-bind,v-ref,v-pre,v-cloak,v-if。
2.内部指令
(1)控制元素的显示与否:v-if,v-show、v-else
v-if是真实的条件渲染,根据表达式的true/false在DOM中生成或移除一个元素。
第一,这个指令是惰性的,初始条件为false,那么什么都不做,直到条件第一次会真时才开始局部编译(编译会被缓存起来)。
第二,它支持template包装元素,所以可以对多个元素进行切换(查看“v-if与v-show的使用”代码)。
v-show是简单的基于css切换,当为false时,会看到对应的元素多了一个内联样式"style=display:none"。它不支持template语法。
比较而言,v-if有更高的切换消耗,v-show有更高的初始渲染消耗。那么如果需要频繁的切换,则使用v-show较好。如果运行时条件不大可能改变,则使用v-if较好。
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>v-if与v-hide</title> 6 </head> 7 <body class="native"> 8 <div id="example"> 9 <p v-if="greeting">Hello</p> 10 <template v-if="ok"> 11 <h1>我是标题</h1> 12 <p>我是段落1</p> 13 <p>我是段落2</p> 14 </template> 15 <p v-show="greeting">Hello2</p> 16 </div> 17 <script src="js/vue.js"></script> 18 <script> 19 var exampleVM2=new Vue({ 20 el:"#example", 21 data:{ 22 greeting:false, 23 ok:true 24 } 25 }) 26 </script> 27 </body> 28 </html>
效果如图
v-else必须跟着v-if,充当else功能,相当于一个反义词的意思。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-else</title> </head> <body class="native"> <div id="example"> <p v-if="ok">我是对的</p> <p v-else="ok">我是错的</p> </div> <script src="js/vue.js"></script> <script> var exampleVM2=new Vue({ el:"#example", data:{ ok:false } }) </script> </body> </html>
(2)v-model
v-model用在input、select、text、checkbox、radio表单控件元素上创建双向数据绑定。根据控件类型v-model自动选取正确的方法更新元素。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-else</title> </head> <body class="native"> <div id="example"> <form> 姓名: <input type="text" v-model="data.name" placeholder=""> <br/> 性别: <input type="radio" id="man" value="One" v-model="data.sex"> <label for="man">男</label> <input type="radio" id="woman" value="Two" v-model="data.sex"> <label for="woman">女</label> <br/> 兴趣: <input type="checkbox" id="book" value="book" v-model="data.interest"> <label for="book">阅读</label> <input type="checkbox" id="swim" value="swim" v-model="data.interest"> <label for="book">阅读</label> <input type="checkbox" id="game" value="game" v-model="data.interest"> <label for="game">游戏</label> <input type="checkbox" id="song" value="song" v-model="data.interest"> <label for="song">唱歌</label> <br/> 身份: <select v-model="data.identity"> <option value="teacher" selected>教师</option> <option value="doctor" >医生</option> <option value="lawyer" >律师</option> </select> </form> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#example", data:{ data:{ name:"", sex:"", interest:[], identity:"" } } }) </script> </body> </html>
无一例外,表单元素的值使用value属性定义,v-model定义数据属性,其中checkbox是多选,所以是数组。
除基本用法外,v-model指令有3个参数。
(1)number
代码演示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-else</title> </head> <body class="native"> <div id="example"> <form> <h5>1.字符串形式的累加</h5> <input type="text" id="age" v-model="age"> <span>大6岁:{{age+3}}</span> <h5>2.数字类型的加法</h5> <input type="text" number id="age2" v-model="age2"> <span>大6岁:{{age2+6}}</span> </form> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#example", data:{ } }) </script> </body> </html>
显示效果:
(2)lazy
默认情况下,v-model在input事件中同步输入框的值与数据。当添加了lazy属性时,要等到change事件发生后才会将数据更新掉。那么什么是change事件呢。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-model</title> </head> <body class="native"> <div id="example"> <form> <h5>1.默认情况下,</h5> <input v-model="msg" type="text" placeholder="" /> <br/> {{msg}} <h5>2.lazy修饰词</h5> <br/> <input v-model="msg2" type="text" placeholder="" lazy/> <br/> {{msg2}} </form> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#example", data:{ msg:"1.内容是在change事件后才改变的", msg2:"2.内容是在change事件后才改变的" } }) </script> </body> </html>
首先,看默认情况。也是我们平常看到的样子。当我们输入的同时,数据的值同时被更新掉。
那么,lazy又是怎样的情况呢?
当只是输入的时候,由于lazy的作用,数据并没有更新。
当输入事件结束,点击其他地方,input框失去焦点,那么数据被更新掉了。
(3)debounce
用来设置延时同步的,以毫秒为单位,那就比lazy更加精确。要更新,需要一个等待时间咯。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-model</title> </head> <body class="native"> <div id="example"> <form> <h5>1.默认情况下,</h5> <input v-model="msg" type="text" placeholder="" /> <br/> {{msg}} <h5>2.lazy修饰词</h5> <input v-model="msg2" type="text" placeholder="" lazy/> <br/> {{msg2}} </form> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#example", data:{ msg:"1.内容是在change事件后才改变的", msg2:"2.内容是在change事件后才改变的" } }) </script> </body> </html>
(3)v-for
v-for指令基于源数据重复渲染元素。$index表示索引。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <ul> <li v-for="tab in tabs"> {{tab.text}} </li> </ul> <ul> <li v-for="tab of tabs"> {{tab.text}} </li> </ul> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ tabs:[ {text:"巴士"}, {text:"快车"}, {text:"顺风车"}, {text:"出租车"}, {text:"代驾"} ] } }); </script> </body> </html>
v-for="tab in tabs" 代码里tabs是数组名,tab是数组元素。除了in语法,也支持of关键字哦。
v-for指令的源码分析:
源码进入调试,逐行验证
inMatch通过正则匹配,返回一个匹配数组,查看变量结果是
if语句进入,itMatch为null,那么会把索引为1的元素tab作为数组元素的别名。
this.expresssion当然是数组的名字。
接下来是如果别名为空,那么可能会给出出错信息,就是控制台的报错了。
v-for如何应用到组件上呢?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <one v-for="tab in tabs"> <p>{{tab.text}}</p> </one> </div> <script src="js/vue.js"></script> <script> Vue.component("one",{ template:"" }) new Vue({ el:"#didi-navigator", data:{ tabs:[ {text:"巴士"}, {text:"快车"}, {text:"顺风车"}, {text:"出租车"}, {text:"代驾"} ] } }); </script> </body> </html>
数组数据变动时如何检测?
首先上DEMO
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <ul> <li v-for="tab in tabs"> {{tab.text}} </li> </ul> <button v-on:click="pushData">向tabs数组添加内容</button> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ tabs:[ {text:"巴士"}, {text:"快车"}, {text:"顺风车"}, {text:"出租车"}, {text:"代驾"} ] }, methods:{ pushData:function(){ this._data.tabs.push({text:"其他"}); } } }); </script> </body> </html>
当我们点击按钮,那么“其他”这一项内容被添加到tabs数组里,并且v-for指令有被再次渲染。这也就是现在的问题,数组的数据变动,会触发试图更新。
首先,使用original遍历的第一个push方法存起来。
再次,把参数也就是DEMO里面的{text:"其他"},插入被观察的数组里。
if (inserted) ob.observeArray(inserted);
最后,在返回结果数组前触发更新。
ob.dep.notify();
所以,vue.js重写了这些方法,触发了一次notify。
第二个是vue.js还增加了$set和$remove方法来观测变化。
源代码:
def(arrayProto, ‘$set‘, function $set(index, val) { if (index >= this.length) { this.length = Number(index) + 1; } return this.splice(index, 1, val)[0]; });
可以看到$set通过Splice来插入值。
源代码:
def(arrayProto, ‘$remove‘, function $remove(item) { /* istanbul ignore if */ if (!this.length) return; var index = indexOf(this, item); if (index > -1) { return this.splice(index, 1); } });
$remove也是通过splice来删除的。
尽量避免直接设置数据绑定的数组元素,这些变化不会被vue.js检测到,从而不渲染视图。这时,可使用$set方法。
也可以使用filter、concat、slice方法,返回的数组将是一个不同的实例。
filter的DEMO:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <ul> <li v-for="tab in tabs"> {{tab.text}} </li> </ul> <button v-on:click="filter">添加内容</button> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ tabs:[ {text:"巴士"}, {text:"快车"}, {text:"顺风车"}, {text:"出租车"}, {text:"代驾"} ] }, methods:{ filter:function(){ this._data.tabs=this._data.tabs.filter(function(item){ item.text=item.text+"添加字符串"; return item; }) } } }); </script> </body> </html>
concat方法的示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <button v-on:click="concatArr">合并数组</button> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", methods:{ concatArr:function(){ var listdata= [{ "smallclassid": "21", "smallclassname": "长沙菜" }, { "smallclassid": "6", "smallclassname": "湘菜分类" }]; var data = [{ smallclassid:0, smallclassname:‘全部‘ }]; var newdata = data.concat(listdata); console.log(newdata); } } }); </script> </body> </html>
track-by让数据替换更加高效。
默认情况下,v-for可能重新渲染整个列表,可能局部渲染,看数据的特征。那么,如果每个对象都有一个唯一的ID属性,那么用track-by特性给vue.js提示,以尽可能的复用已有实例。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <ul> <li v-for="tab in tabs" track-by="_uid"> {{tab.text}} </li> </ul> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ tabs:[ {text:"巴士",_uid:"01"}, {text:"快车",_uid:"02"}, {text:"顺风车",_uid:"03"}, {text:"出租车",_uid:"04"}, {text:"代驾",_uid:"05"} ] }, methods:{ } }); </script> </body> </html>
当替换数组tabs时,如果vue.js遇到一个包含_uid的新对象,那么vue.js就可以知道可以复用已有对象的作用域与DOM元素,避免重复工作。当然也可以使用track-by="$index",它强制v-for进入原位更新模式。
但是DOM节点不再映射数组元素顺序的改变,不能同步临时状态(比如input元素的值),以及组件的私有状态。因此v-for包含input元素或者子组件时,则要小心使用track-by="$index"。
vue.js不能检测到变化。
直接用索引设置元素,比如,vm.items[0]={}。
修改数据的长度,vm.items.length=0.
v-for遍历一个对象,每个重复的实例都有一个特殊的属性$key。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <ul> <li v-for="item in items">{{$key}}:{{item.msg}}</li> </ul> <ul> <li v-for="(key,item) in items">{{key}}:{{item.msg}}</li> </ul> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ items:{ one:{ msg:"Hello" }, two:{ msg:"DIDI FE" } } } }); </script> </body> </html>
v-for也支持整数。
<div v-for="n in 10">Hi!{{$index}}</div>
filterBy的使用
语法:filterBy searchKey [in dataKey...]
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <input v-model="searchText" type="text"/> <ul> <li v-for="user in users | filterBy searchText in ‘name‘">{{user.name}}</li> <!--在users的name字段中过滤出我们想要的信息,并展示出来。--> </ul> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ users:[ { name:"开车", tag:"1" }, { name:"出租车", tag:"2" }, { name:"顺风车", tag:"3" }, { name:"专车", tag:"4" }, ] } }); </script> </body> </html>
效果看起来很棒,像使用搜索引擎的感觉,当输入某个关键字后就会下拉展示相关搜索词。
orderBy的使用
语法:orderBy sortKey[reverseKey]
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <ul> <li v-for="user in users | orderBy field reverse">{{user.name}}</li> <!--在ul标签中根据field代表的tag字段正序排列数据。--> </ul> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ field:"tag", reverse:false, users:[ { name:"快车", tag:"1" }, { name:"出租车", tag:"3" }, { name:"顺风车", tag:"2" }, { name:"专车", tag:"0" }, ] } }); </script> </body> </html>
(4)v-text、v-html和v-bind
v-text指令类似于文本插值,会构建一个文本节点的。v-html插入html片段。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <h5>1.v-text指令</h5> <span v-text="msg"></span> <h5>2.v-html</h5> <span v-html="html"></span> </div> <script src="http://www.mamicode.com/js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ msg:"v-text内容", html:"<span>html内容</span>" } }); </script> </body> </html>
v-bind可以绑定一个或者多个attribute属性。
v-bind动态绑定src属性:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <img v-bind:src="imgSrc" width="121px" height="75px"/> <h5>简写为:</h5> <img :src="imgSrc"/> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ imgSrc:"images/1.jpg" } }); </script> </body> </html>
v-bind绑定class或者style时,支持变量,还支持数组或者对象哦。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .yellow{ color:yellow; } .size{ font-size:20px; } </style> </head> <body> <div id="didi-navigator"> <p :class="[classA,classB]">我是段落</p> <div v-bind="{id:div1}">我是div的内容</div> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ classA:"yellow", classB:"size", div1:"div1" } }); </script> </body> </html>
v-bind没有参数时,直接绑定到一个对象。
组件绑定,使用 v-bind:prop 绑定。到组件一章再说咯。
(5)v-on指令
v-on指令用于绑定事件监听器。普通元素,只能监听原生的DOM事件,并可使用变量$event访问原生DOM事件。使用在自定义元素的组件上,也可以监听子组件触发的自定义事件。
和v-bind:src缩写为:src一样,v-on:click也可以简写,@click。除了事件参数外,还可以增加修饰符。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <!--方法处理器--> <button v-on:click="doThis">do this</button> <!--内联语句--> <button v-on:click="doThat(‘hello‘,$event‘)">do that</button> <!--缩写--> <button @click="doThis">缩写</button> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ tabs:[ {text:"巴士"}, {text:"快车"}, {text:"顺风车"}, {text:"出租车"}, {text:"代驾"} ] }, methods:{ } }); </script> </body> </html>
.stop—调用event.stopPropagation(),阻止冒泡
.prevent—调用event.preventDefault(),阻止默认行为。
.captrue—添加事件侦听器使用capture模式。
.self—只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode|keyAlias}—只在指定按键上触发回调。
vue.js提供的键值有:esc是27,tab是9,enter是13,delete是8和46,up是38,left是37,right是39,down是40。
enter键盘事件DEMO:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <input @keyup.13="onEnter" type="text"/> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ tabs:[ {text:"巴士"}, {text:"快车"}, {text:"顺风车"}, {text:"出租车"}, {text:"代驾"} ] }, methods:{ onEnter:function(){ alert("enter键"); } } }); </script> </body> </html>
(6)v-ref、v-el、v-pre和v-cloak指令
v-ref是在父组件上注册一个子组件的索引,便于直接访问,必须提供参数id,也可以通过父组件的$ref对象访问子组件。(涉及到组件,略讲。)
v-el为DOM元素注册一个索引,方便通过所属实例的$els访问这个元素,
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <span v-el:msg>hello</span> <button @click="getHello">获取hello的值</button> </div> <script src="js/vue.js"></script> <script> new Vue({ el:"#didi-navigator", data:{ }, methods:{ getHello:function(){ var content=this.$els.msg.textContent; alert(content); } } }); </script> </body> </html>
v-pre编译时跳过当前元素和它的子元素。
v-cloak指令保持在元素上直到关联实例结束编译,类似于angularJS的ng-cloak。它用来未被编译的mustache标签直到实例准备完毕。它相当于增加了一个v-locak类。
[v-cloak]{ display:none; }
3.自定义指令
vue.js提供了两种方式,一种是Vue.directive注册全局指令,另一种是组件的directives注册一个局部指令。
(1)钩子函数:bind是准备工作,update是值更新时的工作,unbind是清理工作。当只需要update函数时,可以传入一个函数替代定义对象。
(2)钩子函数的this对象。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <div v-hello="msg"></div> </div> <script src="js/vue.js"></script> <script> Vue.directive("hello",{ bind:function(){ console.log(this); }, update:function(value){ console.log(value); } }); var vm=new Vue({ el:"#didi-navigator", data:{ msg:"hello,everyone" } }); </script> </body> </html>
打印的this对象如截图
所有的钩子函数都将被复制到实际的指令对象中,在钩子内this指向这个指令对象。this对象的各个属性含义:
el-指令绑定的元素。
vm-拥有该指令的上下文ViewModel。
expression-指令的表达式,不包括参数和过滤器。
arg-指令的参数。
name-指令的名字,不包含前缀。
modifiers-一个对象,包含指令的修饰符。
descriptor-一个对象,包含指令的解析结果。
注意:我们应当将这些属性视为只读,不要修改它们。也可以给指令对象添加自定义属性,但不要覆盖原有的内部属性。
(3)指令支持传入一个JS对象字面量
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <div v-hello="{color:‘white‘,text:‘hello,,everyone!‘}"></div> </div> <script src="js/vue.js"></script> <script> Vue.directive("hello",{ update:function(value){ console.log(value.color); console.log(value.text); } }); var vm=new Vue({ el:"#didi-navigator", data:{ } }); </script> </body> </html>
同时当只需要一个update函数时,那么可以简写为
Vue.directive("hello",function(value){ console.log(value.color); console.log(value.text); );
(4)当使用了字面修饰符时,它的值将按照普通字符串处理,update只调用一次,那么普通字符串不能响应数据变化。
(5)元素指令
元素指令的意思是指令当做一个元素来使用,元素指令可以看做一个轻量组件。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <hello class="demo" name="hi"></hello> </div> <script src="http://www.mamicode.com/js/vue.js"></script> <script> Vue.elementDirective("hello",{ bind:function(){ console.info(this.el.className); console.info(this.el.getAttribute("name")); } }); var vm=new Vue({ el:"#didi-navigator", data:{ } }); </script> </body> </html>
(6)高级选项:params
自定义指令接受一个params数组,指定一个特性列表。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <hello class="demo" name="hi" a="ddd" b="cccc">hello内容</hello> </div> <script src="js/vue.js"></script> <script> Vue.elementDirective("hello",{ params:["a","b"], bind:function(){ console.log(this.params) } }); var vm=new Vue({ el:"#didi-navigator", data:{ } }); </script> </body> </html>
params也支持动态属性,this.params[key]会自动保持更新,使用paramWathers对象来定义的。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> {{someValue}} <my-directive class="hello" name="hi" v-bind:a="someValue"></my-directive> <input type="text" v-model="someValue"/> </div> <script src="js/vue.js"></script> <script> Vue.elementDirective("my-directive",{ params:["a"], paramWatchers:{ a:function(val,oldVal){ console.log("a changed"); } } //param监听a变量。如果a发生变化,那么就会执行响应的代码咯 }); var vm=new Vue({ el:"#didi-navigator", data:{ someValue:"value" } }); </script> </body> </html>
(7)高级选项:deep
当自定义指令使用在一个对象上,当对象内部属性变化时要出发update(响应变化),那么需要在指令对象上指定deep:true。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> {{someValue}} <div v-demo="a"></div> <span>{{a.b}}</span> <button @click="change">change</button> </div> <script src="js/vue.js"></script> <script> Vue.directive("demo",{ deep:true, update:function(obj){ console.log(obj.b) } }); var vm=new Vue({ el:"#didi-navigator", data:{ a:{ b:2 } }, methods:{ change:function(){ vm.a.b=4; } } }); </script> </body> </html>
(8)高级选项:twoWay
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> 自定义组件:<input v-example="a.b.c"/> 父作用域:{{a.b.c}} </div> <script src="js/vue.js"></script> <script> Vue.directive(‘example‘, { twoWay: true, bind: function () { this.handler = function () { // 将数据写回 vm // 如果指令这样绑定 v-example="a.b.c" // 它将用给定值设置 `vm.a.b.c` this.set(this.el.value) }.bind(this) this.el.addEventListener(‘input‘, this.handler) }, unbind: function () { this.el.removeEventListener(‘input‘, this.handler) } }); var vm=new Vue({ el:"#didi-navigator", data:{ a:{ b:{ c:2 } } } }); </script> </body> </html>
(9)高级选项:acceptStatement
允许接收内联语句。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="didi-navigator"> <div v-demo="a++"></div> {{a}} </div> <script src="js/vue.js"></script> <script> Vue.directive("demo",{ acceptStatement:true, update:function(fn){ console.log(fn.toString()); fn();
//传入值是一个函数,调用它,就是调用"a++"语句。 } }); var vm=new Vue({ el:"#didi-navigator", data:{ a:5 } }); </script> </body> </html>
(10)高级选项:terminal
遇到terminal指令会停止遍历这个元素的后代元素,然后由这个指令接管编译这元素及其后代元素的任务。v-if和v-for都是terminal指令。
(11)高级选项:priority
给指令指定一个优先级。没有,默认是1000,terminal指令默认是2000.优先级高的指令处理的早,一样的按顺序处理,但却不能保证在所有浏览器都一直。
另外,流程控制指令v-if和v-for在编译过程中始终有最高的优先级。
4.源码实现
内部指令是如何实现的?
v-model指令的实现源码
var model = { priority: MODEL, twoWay: true, handlers: handlers, params: [‘lazy‘, ‘number‘, ‘debounce‘], /** * Possible elements: * <select> * <textarea> * <input type="*"> * - text * - checkbox * - radio * - number */ bind: function bind() { // friendly warning... this.checkFilters(); if (this.hasRead && !this.hasWrite) { ‘development‘ !== ‘production‘ && warn(‘It seems you are using a read-only filter with ‘ +
‘v-model="‘ + this.descriptor.raw + ‘". ‘ + ‘You might want to use a two-way filter to ensure correct behavior.‘, this.vm); } var el = this.el; var tag = el.tagName; var handler; if (tag === ‘INPUT‘) { handler = handlers[el.type] || handlers.text; } else if (tag === ‘SELECT‘) { handler = handlers.select; } else if (tag === ‘TEXTAREA‘) { handler = handlers.text; } else { ‘development‘ !== ‘production‘ && warn(‘v-model does not support element type: ‘ + tag, this.vm); return; } el.__v_model = this; handler.bind.call(this); this.update = handler.update; this._unbind = handler.unbind; }, /** * Check read/write filter stats. */ checkFilters: function checkFilters() { var filters = this.filters; if (!filters) return; var i = filters.length; while (i--) { var filter = resolveAsset(this.vm.$options, ‘filters‘, filters[i].name); if (typeof filter === ‘function‘ || filter.read) { this.hasRead = true; } if (filter.write) { this.hasWrite = true; } } }, unbind: function unbind() { this.el.__v_model = null; this._unbind && this._unbind(); } };
表单元素radio的实现
var radio = { bind: function bind() { var self = this; var el = this.el; this.getValue = http://www.mamicode.com/function () { // value overwrite via v-bind:value if (el.hasOwnProperty(‘_value‘)) { return el._value; } var val = el.value; if (self.params.number) { val = toNumber(val); } return val; }; this.listener = function () { self.set(self.getValue()); }; this.on(‘change‘, this.listener); if (el.hasAttribute(‘checked‘)) { this.afterBind = this.listener; } }, update: function update(value) { this.el.checked = looseEqual(value, this.getValue()); } };
第四章:计算属性
为了避免过多的逻辑造成模板的臃肿不堪,可使用计算属性来简化逻辑。
1.什么是计算属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="example"> <input type="text" v-model="didi"/> <input type="text" v-model="family"/> <br/> didi={{didi}},family={{family}},didiFamily={{didiFamily}} </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#example", data:{ didi:"didi", family:"family" }, computed:{ //一个计算属性的getter didiFamily:{ get:function(){ //this 指向vm实例 return this.didi+this.family; } } } }) </script> </body> </html>
当vm.didi和vm.family的值发生变化时,vm.didiFamily的值会自动更新,当然也会同步到DOM元素上。
2.计算属性的性能分析
第五章:表单控件绑定
1.v-model更新表单控件的基本例子
这些基本例子,直接使用value属性,绑定的都是字符串或者checkbox的布尔值。接下来分别介绍。
(1)文本框的使用
当用户操作文本框时,vm.name会自动更新为用户输入的值,同时span也会改变。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="demo"> <span>Welcom,{{name}}!join DDFE</span> <br/> <input type="text" v-model="name" placeholder="join DDFE"/> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#demo" }) </script> </body> </html>
(2)复选框checkbox的使用
单个复选框的使用和文本框类似,多个复选框,也就是复选框组,被选入的值会放入一个数组。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>复选框组</title> </head> <body> <div id="demo"> <input type="checkbox" id="flash" value="flash" v-model="bizLines"/> <label for="flash"> 快车 </label> <br/> <input type="checkbox" id="premium" value="premium" v-model="bizLines"/> <label for="premium"> 专车 </label> <br/><input type="checkbox" id="bus" value="bus" v-model="bizLines"/> <label for="flash"> 巴士 </label> <br/> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#demo", data:{ bizLines:[] } }) </script> </body> </html>
(3)单选按钮radio的使用
当单选按钮被选中时,v-model的变量值会被赋值为对应的value值。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>单选按钮radio</title> </head> <body> <div id="demo"> <input type="radio" id="flash" value="flash" v-model="bizLines"/> <label for="flash"> 快车 </label> <br/> <input type="radio" id="premium" value="premium" v-model="bizLines"/> <label for="premium"> 专车 </label> <br/> <input type="radio" id="bus" value="bus" v-model="bizLines"/> <label for="flash"> 巴士 </label> <br/> <span>Picked:{{bizLines}}</span> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#demo" }) </script> </body> </html>
(4)select控件使用v-model分为两种情况。
一种是单选select控件,第二种是多选select控件。v-model指令都用于select标签。动态的生成option可以借助v-for指令等。
单选时vm.selected为对应的option的value值。多选会被放入一个数组中,多选是selectr有multiple属性,浏览器窗口想要多选可以借助ctrl和shift快捷键。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>下拉菜单的选择</title> </head> <body> <div id="demo"> <select v-model="bizLine"> <option selected value="flash">快车</option> <option value="premium">专车</option> <option value="bus">巴士</option> </select> <span>selected:{{bizLine}}</span> <br/> <select v-model="bizLine2" multiple> <option selected value="flash">快车</option> <option value="premium">专车</option> <option value="bus">巴士</option> </select> <span>selected:{{bizLine2|json}}</span> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#demo" }) </script> </body> </html>
2.动态的值绑定
通过v-bind代替直接使用value属性,那么除了字符串,还可以是数值、对象、数组等非字符串的值。
(1)checkbox
3.v-model指令的修饰词及其源码分析
(1)lazy
默认情况下,v-model在input事件中同步输入框的值与数据,而添加了lazy特性后,在change事件中去掉同步性。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="demo"> <span>Welcom,{{name}}!join DDFE</span> <br/> <input type="text" v-model="name" placeholder="join DDFE" /> </div> <script src="js/vue.js"></script> <script> var vm=new Vue({ el:"#demo" }) </script> </body> </html> </body> </html>
同样是之前的代码,只是添加了lazy属性,那么name变量就不会同步了。
<!--源码目录:src/directives/public/model/text.js-->
var lazy = this.params.lazy //获取到参数的lazy的布尔值 //... if (!isRange && !lazy) { //如果element的type值不是range,而且lazy为false this.on(‘focus‘, function () { self.focused = true }) this.on(‘blur‘, function () { self.focused = false // do not sync value after fragment removal (#2017) if (!self._frag || self._frag.inserted) { self.rawListener() } }) } //... if (this.hasjQuery) { const method = jQuery.fn.on ? ‘on‘ : ‘bind‘ jQuery(el)[method](‘change‘, this.rawListener) if (!lazy) { jQuery(el)[method](‘input‘, this.listener) } } else { this.on(‘change‘, this.rawListener) if (!lazy) { this.on(‘input‘, this.listener) } }
(2)debounce
<!--源码目录:src/directives/public/model/text.js-->
if (debounce) { this.listener = _debounce(this.listener, debounce) } // Support jQuery events, since jQuery.trigger() doesn‘t // trigger native events in some cases and some plugins // rely on $.trigger() // // We want to make sure if a listener is attached using // jQuery, it is also removed with jQuery, that‘s why // we do the check for each directive instance and // store that check result on itself. This also allows // easier test coverage control by unsetting the global // jQuery variable in tests. this.hasjQuery = typeof jQuery === ‘function‘ if (this.hasjQuery) { const method = jQuery.fn.on ? ‘on‘ : ‘bind‘ jQuery(el)[method](‘change‘, this.rawListener) if (!lazy) { jQuery(el)[method](‘input‘, this.listener) } } else { this.on(‘change‘, this.rawListener) if (!lazy) { this.on(‘input‘, this.listener) } }
(3)number
<!--源码目录:src/directives/public/model/text.js-->
var number = this.params.number //**** this.listener = this.rawListener = function () { if (composing || !self._bound) { return } var val = number || isRange ? toNumber(el.value) : el.value self.set(val) // force update on next tick to avoid lock & same value // also only update when user is not typing nextTick(function () { if (self._bound && !self.focused) { self.update(self._watcher.value) } }) } //****
<!--源码目录:src/util/lang.js-->
export function toNumber (value) { if (typeof value !== ‘string‘) { return value } else { var parsed = Number(value) return isNaN(parsed) ? value : parsed } }
vue.js基础知识篇(1):简介、数据绑定、指令、计算属性、表单控件绑定和过滤器