关于Vue.js的认识(第一部分)[转载]
一、关于v-bind
1.初识v-bind
(1).加冒号的是 vue
的 v-bind
语法糖(指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用),绑定vue的一个数据;不加冒号的就是HTML的属性,和 onclick="doSth()"
是一个意思。它是一个 vue 指令,用于绑定 html 属性,如下:
<div id="app"> <p v-bind:title="title">html属性不能使用双大括号形式绑定,只能使用v-bind指令</p> </div> ...... var vm = new Vue({ el: '#app', data: { title: 'title content' } });
这里的 html 最后会渲染成:
<div id="app"> <p title="title content">html属性不能使用双大括号形式绑定,只能使用v-bind指令</p> </div>
2.指令预期值
a.执行运算
上面这种 v-bind 这也是我们对于 vue 指令最初的理解,但实际上,vue 指令的预期值(如 v-bind:class="classProperty" 中,v-bind 是指令,: 后面的 class 是参数,而 classProperty 则在官方文档中被称为“预期值”),除了像上面那样绑定一个字符串类型变量,其实它是支持一个单一 JavaScript 表达式 (v-for 除外)。
所以在这里,我们就可以有更多的选择,例如:
<div id="app"> <p v-bind:title="t1 + ' ' + t2">html属性不能使用双大括号形式绑定,只能使用v-bind指令</p> </div> ...... var vm = new Vue({ el: '#app', data: { t1: 'title1', t2: 'title2' } });
最后渲染的结果:
<div id="app"> <p title="title1 title2">html属性不能使用双大括号形式绑定,只能使用v-bind指令</p> </div>
b.执行函数等
<div id="app"> <p v-bind:title="getTitle()">html属性不能使用双大括号形式绑定,只能使用v-bind指令</p> </div> ...... var vm = new Vue({ el: '#app', data: { getTitle: function () { return 'title content'; } } });
最后渲染的结果:
<div id="app"> <p title="title content">html属性不能使用双大括号形式绑定,只能使用v-bind指令</p> </div>
3.支持的数据类型
上面的内容,指令预期值得到的都是字符串类型的数据,但实际上,我们知道 js 有很多数据类型,它如果放入其中呢?
a.对象类型
<div id="app"> <p v-bind:title="obj">content</p> </div> ...... var obj = {}; var vm = new Vue({ el: '#app', data: { obj: obj } });
渲染结果如下:
<div id="app"> <p title="[object Object]">content</p> </div>
诶,这个怎么有点眼熟?有点像...没错!对象的 toString 方法的返回值!为了验证我们的猜想,我们进行进一步的测试:
<div id="app"> <p v-bind:title="obj">content</p> </div> ...... var obj = {}; obj.toString = function () { return 'edited in toString!'; }; var vm = new Vue({ el: '#app', data: { obj: obj } });
上面这里修改了 obj 的 toString 方法(但准确的说,这里不是修改,而是添加。一开始的 obj 对象上自身是没有 toString 方法的,它继承了 Object.prototype.toString,但这里我们执行 obj.toString = function..... 实际上是为它添加了一个 toString 方法,使得它执行的时候,不用再去调用继承自 Object 的方法),渲染结果如下:
<div id="app"> <p title="edited in toString!">content</p> </div>
b.数组类型
数组类型的 toString 方法和对象类型的有所不同,它将返回和执行 arr.join(',') 相同的结果。如 [1, 2, 3].toString() 将返回 “1,2,3”。下面进行测试:
<div id="app"> <p v-bind:title="arr">content</p> </div> ...... var vm = new Vue({ el: '#app', data: { arr: [1, 2, 3] } });
渲染结果如下:
<div id="app"> <p title="1,2,3">content</p> </div>
c.其它类型
- number 类型,正常执行 toString,包括数字0,结果都正常渲染成对应的字符串;
- boolean 类型,true 正常渲染成字符串 "true",但 false 虽然执行 toString 方法将返回 "false" 字符串,但是却没有渲染出来;
<div id="app"> <p v-bind:title="aaa">content</p> </div> </body> <script type="text/javascript"> var obj={ a:null,//a:undefined,a:false }; /* obj.toString=function(){ return false; }(); */ var vm = new Vue({ el:'#app', data:{ aaa:obj.a } }); </script>
渲染结果:
<div id="app"> <p >aaaaa</p> </div>
结果确实没有渲染出来。
- null / undefined 类型,二者没有 toString 方法,也没有渲染出来。
显然,在执行 toString 方法之前,vue 内部应该先做了类型校验,满足条件才输出。而且这里不是简单的真 / 假值校验,因为 、0 虽为假值,但最终却像真值一样渲染了出来。具体如何实现,可能需要参考 vue 的源码了,这里不再深究。
4、多 html 属性值绑定
一个的 html 属性值,可能包含许多内容,需要我们进行一些操作,将多个数据绑定到一个属性上,这里我们可以考虑像前面一样,通过如 “+” 等运算符号等实现字符串的连接操作。但是事实上,字符串连接麻烦又易错,不易于维护。于是我们可以考虑像前面一样向指令预期值中存入一个对象或数组,来实现多个数据绑定到一个属性上的作用。
(1)对象类型
<div id="app"> <p v-bind:title="obj">content</p> </div> ...... var obj = { name: 'Dale', age: 22 }; // 利用 for-in 循环遍历对象属性,拼接成字符串 obj.toString = function () { var str = ''; for(var i in this) { str += i + ': ' + this[i] + '; '; } return str; }; // 防止 toString 方法自身被遍历出来 Object.defineProperty(obj, 'toString', {'enumerable': false}); var vm = new Vue({ el: '#app', data: { obj: obj } });
渲染结果:
<div id="app"> <p title="name: Dale; age: 22; ">content</p> </div>
上面通过 for-in 循环在 toString 方法中得到所有可遍历的属性以及对应的属性值,然后将其拼接成字符串再进行输出,可以实现多属性值绑定,至于如何拼接,可以自己在 toString 方法中进行不同的实现 。
2.数组类型
<div id="app"> <p v-bind:title="arr">content</p> </div> ...... var arr = [1, 2, 3]; arr.toString = function () { return this.join(' '); }; var vm = new Vue({ el: '#app', data: { arr: arr } });
渲染结果:
<div id="app"> <p title="1 2 3">content</p> </div>
相比于对象字符串拼接,数组的拼接操作则显得简单得多,可以直接在 toString 方法返回 join 方法的返回值,默认的 toString 方法的返回值其实就和 join(',') 的返回值相同。
二、关于v-if
1.v-if
v-if,里面这个show是个变量,如果是true就是显示,如果是false就不显示,这里是移除了dom
<div id='root'> <div v-if='show'>helle world</div> <button @click='handleClick'>toggle</button> </div> <script> new Vue({ el:'#root', data:{ show:true }, methods:{ handleClick:function(){ this.show = !this.show; } } }) </script>
2.v-else
这里v-if,v-else要紧贴着使用,不然会报错,v-if显示,v-else就会隐藏。v-if隐藏,v-else就会显示
<div id='root'> <div v-if='show'>helle world</div> <div v-else>bye world</div> </div> <script> var vm = new Vue({ el:'#root', data:{ show:true } }) </script>
3.v-else-if
这里可以根据show的值进行不同的处理,这三个也要紧贴着写,中间不能被其他所分隔
<div id='root'> <div v-if='show==="a"'>this is a</div> <div v-else-if='show==="b"'>this is b</div> <div v-else>this is others</div> </div> <script> var vm = new Vue({ el:'#root', data:{ show:'b' } }) </script>
4.v-if使用的时候key值的用法
<div id='root'> <div v-if='show'> 用户名:<input type="text" /> </div> <div v-else> 邮箱名:<input type="text" /> </div> </div> <script> var vm = new Vue({ el:'#root', data:{ show:false } }) </script>
这个例子,在我们使用的时候,show的值变为true,发现邮箱名已经变成了用户名,但是input框里面的值却没变空,这是因为vuejs有一种尝试复用dom的机制,如果已经在的dom,会复用之前的dom,但是这并不是我们想要的效果,这个时候就可以给他加一个key值,告诉vue.js,这是唯一的,是不能复用的input
<div id='root'> <div v-if='show'> 用户名:<input type="text" key='userName'/> </div> <div v-else> 邮箱名:<input type="text" key='password' /> </div> </div> <script> var vm = new Vue({ el:'#root', data:{ show:false } }) </script>
三、关于v-for
1.在 v-for 指令的表达式中, news 是 data 内定义的数据, n 是当前数组元素的别名。
<div id="app"> <ul> <li v-for="n in news">{{n.title}}</li> </ul> </div> ... <script> var app = new Vue({ el: '#app', data: { news: [ {title: '跑步'}, {title: '绘画'}, {title: '读书'} ] } }); </script>
结果:
2.列表渲染指令的表达式也支持使用 of 作为分隔符。(同时支持当前项索引参数,索引从 0 开始,它是可选的 )
<div id="app"> <ul> <li v-for="(n,index) of news">{{index}} - {{n.title}}</li> </ul> </div> ... <script> var app = new Vue({ el: '#app', data: { news: [ {title: '跑步'}, {title: '绘画'}, {title: '读书'} ] } }); </script>
结果:
3.可以使用<template>标签,渲染多个元素
<div id="app"> <dl> <template v-for="n in news"> <dt>{{n.title}}</dt> <dd>{{n.content}}</dd> </template> </dl> </div> ... <script> var app = new Vue({ el: '#app', data: { news: [ {title: '跑步',content:"runrunrunrn"}, {title: '绘画',content:"drawdrawdraw"}, {title: '读书',content:"readreadread"} ] } }); </script>
结果:
4.遍历对象属性
<div id="app"> <li v-for="val in account">{{val}}</li> </div> ... <script> var app = new Vue({ el: '#app', data: { account: { name: 'xusp', messageCount: 100 } } }); </script>
结果:
4.1 遍历对象属性,可以带上两个可选参数,它们就是对象的属性名和索引:
<div id="app"> <li v-for="(val,name,index) in account">{{index}} - {{name}} : {{val}}</li> </div> ... <script> var app = new Vue({ el: '#app', data: { account: { name: 'xusp', messageCount: 100 } } }); </script>
结果:
5.迭代函数
<div id="app"> <ul> <li v-for="i in 5">{{i}}</li> </ul> </div> ... <script> var app = new Vue({ el: '#app' }); </script>
结果:
6.更新数组
Vue.js 的核心是数据与视图的双向绑定。因此当我们修改数组时, Vue.js 就会检测到数据了变化,所以用 v-for 渲染的视图也会更新 。使用以下方法修改数组时,就会触发视图更新:
- push()
- shift()
- unshift()
- splice()
- sort()
- reverse()
这些方法会改变原数组,所以又称为变异方法。
我们使用 push() 为 app 对象新增一个标题:
app.news.push({
title:'羽毛球'
});
结果:
也有一些非变异方法,它们不会改变原数组,只会返回新数组:
- filter()
- concat()
- slice()
我们在使用这些方法时,可以通过设置新数组的方式来更新视图。
//非变异方法更新数组
app.news = app.news.filter(function (item) {
return item.title.match(/读/);
});
结果:
这个示例中,我们使用 filter 函数,把新闻标题中含有“读”字样的标题过滤出来。
Vue.js 在检测数组变化时,会最大化地复用 DOM 元素。 替换的数组,如果含有相同元素的项并不会被重新渲染,所以不用担心性能问题。
注意:通过以下方法来改变数组, Vue.js 是无法检测的,所以不会更新视图:
- 通过索引来设置项,比如
app.new[1]={...}
- 修改数组长度,比如
app.new.length=1
。
我们可以使用 Vue.js 内置的 set 方法(可指定索引)来更新数组:
<div id="app"> <ul> <li v-for="n in news">{{n.title}}</li> </ul> </div> ... <script> var app = new Vue({ el:'#app', data:{ news:[ {title:'跑步'}, {title:'绘画'}, {title:'读书'} ] } }); //通过 set 的设置索引方式来更新数组 Vue.set(app.news,1,{ title: '游泳' }); </script>
结果:
也可以使用 splice 指定索引来更新数组:
//通过 splice 的设置索引方式来更新数组
app.news.splice(1, 0, {
title: '羽毛球'
});
结果:
至于第二个问题,同样可以通过 splice 来实现:
//通过 splice 来删除数组元素
app.news.splice(1);
以下三张结果图,分别是括号中的值为1,2,3的时候的结果:
7.过滤或排序
<div id="app"> <h5>过滤出带“美元”的标题</h5> <ul> <li v-for="(n,index) in filterNews">{{index}} - {{n.title}}</li> </ul> <h5>按照标题长度,由短到长排序</h5> <ul> <li v-for="(n,index) in sortNews">{{index}} - {{n.title}}</li> </ul> </div> ... <script> var app = new Vue({ el: '#app', data: { news: [ {title: '被智能手机绑架的i世代 爱熬夜、拒绝恋爱、不考驾照'}, {title: '黑莓宣布14亿美元收购网络安全公司Cylance'}, {title: '如何看待XXX开酒店赚美元这件事?'} ] }, computed: { //过滤出带“美元”的标题 filterNews: function () { return this.news.filter(function (item) { return item.title.match(/美元/); }) }, //按照标题长度,由短到长排序 sortNews: function () { return this.news.sort(function (val1, val2) { if(val1.title.length < val2.title.length){ return -1; } }) } } }); </script>
结果:
四、关于v-on
1.基础语法
v-on 指令绑定事件后,就会监听相应的事件。(注意: @click
是 v-on:click
的简写形式, @
即表示 v-on:
)
<div id="app"> <h3>已点击 {{count}} 次</h3> <button @click="count++">点我</button> </div> ... <script> var app = new Vue({ el: '#app', data: { count:0 } }); </script>
结果:
1.1 : @click 表达式即可以直接使用 js 语句,也可以是一个定义在 vue 实例中 methods 内的函数名。(注意: @click
调用的方法名如果不需要参数,那么可以不写括号。)
<div id="app"> <h3>已点击 {{count}} 次</h3> <button @click="quickAdd(3)">快速增长</button> </div> ... <script> var app = new Vue({ el: '#app', data: { count:0 }, methods: { quickAdd: function (i) { i = i || 1;//为避免参数为 null 或 undefined 值,这里设定了默认值 1 this.count += i; } } }); </script>
结果:
2.调用DOM事件
Vue.js 还提供了一个 $event
变量,使用它可以访问原生 DOM 事件。$event
变量可以通过方法传入。
<div id="app2"> <a href="www.baidu.com" @click="openUrl('被禁用咯',$event)">被禁用咯</a> </div> ... <script> var app2 = new Vue({ el: '#app2', data: { count: 0 }, methods: { openUrl: function (param, event) { event.preventDefault(); console.log("param:" + param); } } }); </script>
结果:
这个示例利用传入的 event 参数,禁用了原有的链接跳转逻辑。
3.事件修饰符
Vue.js 支持以下事件修饰符:
事件修饰符 | 示例 | 示例说明 |
---|---|---|
.stop |
@click.stop |
阻止单击事件冒泡。 |
.prevent |
@submit.prevent |
提交页面不再重载页面。 |
.capture |
@click.capture |
添加事件监听器时使用事件捕获模式。 |
.self |
@click.self |
只有当单击事件是元素的自身事件时,才会触发。 |
.once |
@click.once |
只触发一次。 |
4.按键修饰符
可以使用按键修饰符,来监听表单元素上的键盘事件。
<div id="app3"> <input @keyup.13="enterClick"> </div> ... <script> var app3 = new Vue({ el: '#app3', data: {}, methods: { enterClick: function () { console.log("enterClick"); } } }); </script>
console控制台输出结果:
enterClick
这个示例演示了如何通过回车按键修饰符来绑定事件函数的过程。
除了使用 keycode 来指定按键之外,我们还可以使用以下这些按键别名:
.enter
.tab
.delete
.esc
.space
.up
.down
.left
.right
用法示例:
<input @keyup.enter="enterClick">
还有一些按键修饰符可以组合使用,或者和鼠标一起使用:
.ctrl
.alt
.shift
.meta
:Mac 下为 Command 键,Windows 下为窗口键。
用法示例:
<input @keyup.alt.83="enterClick">
五、关于v-model(双向绑定)
1.lazy
v-model 指令默认会在 input 事件中加载输入框中的数据(中文输入法中输入拼音的过程除外)。我们可以使用 .lazy
懒加载修饰符,让其只在 change 事件中再加载输入框中的数据。
使用 .lazy 懒加载修饰符之后,只有在输入框失去焦点或者按回车键时才会更新 content 值。
<div id="app"> <input type="text" v-model.lazy="content" placeholder="请输入" value="初始值"> <p>输入框:{{content}}</p> </div> ... <script> var app = new Vue({ el: '#app', data: { content: '' } }); </script>
结果:
2.number
输入框输入的内容,即使是数字,默认也是 string 类型
在此,我们可以使用 .number
修饰符让其转换为 number 类型
<div id="app2"> <input type="number" v-model.number="content" placeholder="请输入" > <p>输入值:{{content}},输入类型:{{typeof content}}</p> </div> ... <script> var app2 = new Vue({ el: '#app2', data: { content: 1 } }); </script>
结果:
3.trim
使用 .trim 修饰符可以自动过滤掉输入框的首尾空格。
<div id="app3"> <input type="text" v-model.trim="content" placeholder="请输入" value="初始值"> <p>输入框:{{content}}</p> </div> ... <script> var app3 = new Vue({ el: '#app3', data: { content: '' } }); </script>
结果: