vue.js基础知识篇(2):指令详解
第三章:指令
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较好。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-if与v-hide</title>
</head>
<body class="native">
<div id="example">
<p v-if="greeting">Hello</p>
<template v-if="ok">
<h1>我是标题</h1>
<p>我是段落1</p>
<p>我是段落2</p>
</template>
<p v-show="greeting">Hello2</p>
</div>
<script src="js/vue.js"></script>
<script>
var exampleVM2=new Vue({
el:"#example",
data:{
greeting:false,
ok:true
}
})
</script>
</body>
</html>
v-if与v-show的使用
效果如图
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>
v-else的使用
(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="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="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 = 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());
}
};
vue.js基础知识篇(2):指令详解的更多相关文章
- vue.js基础知识篇(6):组件详解
第11章:组件详解 组件是Vue.js最推崇也最强大的功能之一,核心目标是可重用性. 我们把组件代码按照template.style.script的拆分方式,放置到对应的.vue文件中. 1.注册 V ...
- vue.js基础知识篇(7):表单校验详解
目录 网盘 第12章:表单校验 1.npm安装vue-validator $ npm install vue-validator 代码示例: var Vue=require("vue&quo ...
- vue.js基础知识篇(5):过渡、Method和Vue实例方法
第8章:过渡 1.CSS过渡 2.JavaScript过渡 3.渐进过渡 第9章:method Vue.js的事件一般通过v-on指令配置在HTML中,虽然也可以在js的代码中使用原生的addEven ...
- vue.js基础知识篇(4):过滤器、class与style的绑定2
代码下载:网盘 欢迎私信 第一章:过滤器 过滤器是对数据进行处理并返回结果的函数. 1.语法 语法是使用管道符"|"进行连接.过滤器可以接收参数,跟在过滤器后面,带引号的参数被当做 ...
- vue.js基础知识篇(3):计算属性、表单控件绑定
第四章:计算属性 为了避免过多的逻辑造成模板的臃肿不堪,可使用计算属性来简化逻辑. 1.什么是计算属性 <!DOCTYPE html><html lang="en" ...
- vue.js基础知识篇(1):简介、数据绑定
目录第一章:vue.js是什么? 第二章:数据绑定第三章:指令第四章:计算属性第五章:表单控件绑定代码链接: http://pan.baidu.com/s/1qXCfzRI 密码: 5j79 第一章: ...
- Vue.js 源码分析(二十三) 指令篇 v-show指令详解
v-show的作用是将表达式值转换为布尔值,根据该布尔值的真假来显示/隐藏切换元素,它是通过切换元素的display这个css属性值来实现的,例如: <!DOCTYPE html> < ...
- Vue.js 源码分析(二十二) 指令篇 v-model指令详解
Vue.js提供了v-model指令用于双向数据绑定,比如在输入框上使用时,输入的内容会事实映射到绑定的数据上,绑定的数据又可以显示在页面里,数据显示的过程是自动完成的. v-model本质上不过是语 ...
- Vue.js 源码分析(二十一) 指令篇 v-pre指令详解
该指令会跳过所在元素和它的子元素的编译过程,也就是把这个节点及其子节点当作一个静态节点来处理,例如: <!DOCTYPE html> <html lang="en" ...
随机推荐
- Sql for Oracle基本操作关键字
Sql for Oracle基本操作关键字 +SQL TOP子句 TOP 子句用于规定要返回的记录的数目 SELECT column_name(s) FROM table_name WHERE ROW ...
- 初学安卓开发随笔之 Intent 用法
首先,对于安卓开发,目前世界上流行的是使用的是Android studio 2.0 .(hh 学着来呗 书上说用这个,,) 今后就定一个计划 每天更新一个Android 随笔,增强一下自控力吧!!! ...
- Oracle和MySQL分组查询GROUP BY
Oracle和MySQL分组查询GROUP BY 真题1.Oracle和MySQL中的分组(GROUP BY)有什么区别? 答案:Oracle对于GROUP BY是严格的,所有要SELECT出来的字段 ...
- 使用Node.js搭建静态资源服务器
对于Node.js新手,搭建一个静态资源服务器是个不错的锻炼,从最简单的返回文件或错误开始,渐进增强,还可以逐步加深对http的理解.那就开始吧,让我们的双手沾满网络请求! Note: 当然在项目中如 ...
- oracle 审计日志清理
--进入审计日志目录: cd $ORACLE_BASE/admin/$ORACLE_SID/adump --删除3个月前的审计文件: find ./ -type f -name "*.a ...
- Shopex如何清理缓存
一.进入后台,点击 右上角 的"关于" 二.点击:缓存系统: 三.点击"清空缓存" 四.清除成功!
- 在drawRect:方法中绘制图片,文字以及Core Graphics 框架的了解
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 } p.p2 { margin: 0.0px 0. ...
- Git时光机穿梭之版本回退
现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt文件如下: Git is a distributed version control system. ...
- JS的事件多次触发,只执行最后一次
有时候我们在JS编程的过程中经常遇到一个问题就是事件频繁高速被触发,利用计时器来控制频率又会丢弃掉有用的事件,我们只是想让程序执行最后一次的事件,那么就可以用如下方法解决问题 //写在事件外边,防止被 ...
- NYOJ--113--字符串替换
/* Name: NYOJ--113--字符串替换 Author: shen_渊 Date: 18/04/17 15:41 Description: 字符串水题,秒过 */ #include<b ...