一、引入js文件(直接采用CDN):
http://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js
二、简单实例:
(1)HTML代码:
<div id="app"> <h1>{{ message }}</h1> </div>
(2)JS代码:
var vm = new Vue({ el: '#app', data: { message: 'Hello world, I am Vue.js!!' } });
(3)输出结果为:
Hello world, I am Vue.js!!
三、Vue的两个特性:
1、数据绑定:我们可以在运行上述例子的浏览器控制台(console)环境中输入vm.message = 'Hello Vue.js', 输出结果就变成了Hello Vue.js。也就说明vm.message和视图中的{{message}}是绑定的,我们无需手动去获取<h1>标签来修改里面的innerHTML。
同样,我们也可以绑定用户输入的数据,视图会随着用户的输入而变化。例:
<div id="app"> <h1>Your input is {{ message }}</h1> <input type="text" v-model="message" /> </div>
输出结果:
vm.message的值会随着用户在input输入的值变化而变化,而无需我们手动去获取DOM元素的值再同步到js中。
2、组件化:简单来说可以自己定义HTML标签,并在模板中使用。例:
<div id="app"> <message content="Hello World"></message> </div> <script type="text/javascript"> var message = Vue.extend({ props: ['content'], template: '<h1>{{ mwssage }}</h1>' }); Vue.component('message', Message); var vm = new Vue({ el: '#app' }); </script>
输出结果:
可以看到自定义的标签<message>被替换成了<h1>Hello World</h1>,当然,实际中的组件化远比示例复杂,我们会给组件添加参数及方法,使之能更好地被复用。
四、实例及选项:
从前面的例子可以看出,Vue.js的使用都是通过构造函数Vue{{option}}创建一个Vue的实例。在实例化的时候,可以传入一个选项对象,包含数据、模板、挂在元素、方法、生命周期钩子等选项。
1、模板:
选项中主要影响模板或DOM的选项有el和template,属性replace和template需要一起使用。
el:类型为字符串,DOM元素或函数。起作用是为实例提供挂载元素。一般来说我们会使用CSS选择符,或者原生的DOM元素。例如el: '#app'。在初始项中制定了el,实例将立即进入编译过程。
template:类型为字符串。默认会将template值替换挂载元素(即el值对应的元素),并合并挂载元素和模板根节点的属性(如果属性具有唯一性,类似id,则以模板根节点为准)。如果replace为false,模板template的值将插入挂载元素内。通过template插入模板的时候,挂载元素内容都将被互联,除非是用slot进行分发。在使用template时,我们往往不会把所有的HTML字符串直接写在js里面,这样影响可读性而且也不利于维护。所以经常用'#tpl'的方式赋值,并且在body内容添加<script id="tpl" type="x-template">为标签的HTML内容,这样就能将HTML从js中分离开来。例:
<div id="app"> <p>123</p> </div> <script type="text/javascript"> <div class="tpl"> <p>This is a tpl from script tag</p> </div> </script> <script type="text/javascript"> var vm = new Vue({ el: '#app', template: '#tpl' }); </script>
最终输出HTML结构为:
<div class="tpl" id="app"> <p>This is a tpl from script tag</p> </div>
Vue.js 2.0废除了replace这个参数,并且强制要求每一个Vue.js实例需要有一个根元素,即不允许组件模板为:
<script id="tpl" type="x-template"> <div class="tpl"> ... </div> <div class="tpl"> ... </div> </script>
这样的兄弟节点为根节点的模板形式,需要改写成:
<script id="tpl" type="x-template"> <div class="wrapper"> <div class="tpl"> ... </div> <div class="tpl"> ... </div> </div> </script>
2、数据:
Vue.js实例中可以通过data属性定义数据,这些数据可以在实例对应的模板中进行绑定并使用。需要注意的是,如果传入data的是一个对象,Vue实例会代理起data对象里的多有属性,而不会对传入的对象进行深拷贝。另外,我们也可以引用Vue实例vm中的$data来获取声明的数据,例:
var data = { a: 1 } var vm = new Vue({ data: data }); vm.$data === data; // true vm.a === $data.a; // true // 设置属性也会影响到原始数据 vm.a = 2; data.a; // 2 // 反之亦然 data.a = 3; vm.a; // 3
然后在模板中使用{{ a }}就会输出vm.a的值,并且修改vm.a的值,模板中的值会随之改变,我们也会称这个数据为响应式(responsive)数据。
需要注意的是,只有初始化时传入的对象才是响应式的,即在声明完实例后,再加上一句vm.$data.b = '2',并在模板中使用{{b}},这时是不会输出字符串‘2’的。例:
<div id="app"> <p>{{ a }}</p> <p>{{ b }}</p> </div> var vm = new Vue({ el: '#app', data: { a: 1 } }); vm.$data.b = 2;
如果需要在实例化之后加入响应式变量,需要调用实例方法$set,例:
vm.$set('b', 2);
不过Vue.js并不推荐这么做,这样会抛出一个异常。
所以,我们应该尽量在初始化的时候,把所有的变量都设定好,额u过没有值,也可以用Undefined或null占位。
另外,组件类型的实例可以通过props获取数据,同data一样,也需要在初始化时预设好。例:
<my-component title="myTitle" content="myContent"></my-component> var myComponent = Vue.component("my-component", { props: ['title', 'content'], template: '<h1>{{ title }}</h1><p>{{ content }}</p>' });
我们也可以在上述组件类型实例中同时使用data,但有两个地方需要注意:a、data的值必须是一个函数,并且返回值不是原始对象。如果传给组件的data是一个原始对象的话,则在建立多个组件实例时它们就会共用这个data对象,修改其中一个组件实例的数据就会影响到其他组件实例的数据,这显然不是我们所希望的。b、data中的属性和props中的不能重名。这两者都会报错。正确使用方法如下:
var MyComponent = Vue.Component({'my-component', { props: ['title', 'content'], data: function() { return { desc: '123' } }, template: '<div> <h1>{{ title }}</h1> <p>{{ content }}</p> <p>{{ desc }}</p> </div>' });
3、方法:
我们可以通过选项属性methods对象来定义方法,并且使用v-on指令来监听DOM事件,例:
<button v-on:click="alert">alert</button> new Vue({ el: '#app', data: {a: 1}, methods: { alert: function() { alert(this.a); } } });
另外,Vue.js实例也支持自定义事件,可以在初始化时传入events对象,通过实例的$emit方法触发。这套通信机制常用在组件间相互通信的情况中 ,例如子组件冒泡触发父组件实践方法,或者父组件广播某个时间,子组件对其进行监听等。例:
var vm = new Vue({ el: '#app', data: data, events: { 'event.alert': function() { alert('this is event alert: ' + this.a); } } }); vm.$emit('event.alert');
而Vue.js2.0废弃了events选项属性,不再支持事件广播这类特性,推荐直接使用Vue实例的全局方法$on()/$emit(),或者使用插件Vuex来处理。
4、生命周期:
Vue.js实例在创建时有一系列初始化步骤,例如建立数据观察、编译模板、创建数据绑定等。在此过程中,我们可以通过一些定义好的生命周期钩子来运行业务逻辑。例:
var vm = new Vue({ data: { a: 1 }, created: function() { console.log('created') } });
运行上述例子时,浏览器console中就会打印出created。
可以通过一个简单的demo来更清楚地了解内部的运行机制。
var vm = new Vue({ el: '#app', init: function() { console.log('init'); }, created: function() { console.log('created'); }, beforeCompile: function() { console.log('beforeCompile'); }, compiled: function() { console.log('compiled'); }, attached: function() { console.log('attached'); }, dettached: function() { console.log('dettached'); }, beforeDestroy: function() { console.log('beforeDestroy'); }, destroyed: function() { console.log('destroyed'); }, ready: function() { console.log('ready'); // 组件完成后调用$destroy()函数,进行销毁 this.$destroy(); } });
输出结果为:
init
created
beforeCompile
compiled
attached
ready
beforeDestroy
destroyed
五、数据绑定:
Vue.js作为数据驱动师徒的框架,我们首先要知道的就是如何将数据在视图中展示出来,传统的项目中,这一过程往往是通过后端模板引擎来进行数据和视图的渲染,例如PHP的smart有y,Java的velocity和freemarker。但这样导致的情况是前后端语法会交杂在一起,前端HTML文件需要包含后端模板引擎的语法,而且渲染完成后如果需要再次修改视图,就只能通过获取DOM的方法进行修改,并手动维持数据和视图的一致。而Vue.js的核心是一个响应式的数据绑定系统,建立绑定后,DOM将和数据保持同步,这样就无需手动维护DOM,使代码能够更加简洁易懂、提升效率。
1、数据绑定语法:
以下介绍Vue.js的数据绑定语法,出现的例子基于以下js代码:
var vm = new Vue({ el: '#app', data: { id: 1, index: 0, name: 'Vue', avatar: 'http://www.baidu.com', count: [1, 2, 3, 4, 5], names: ['Vue1.0', 'Vue2.0'], items: [ {name: 'Vue1.0', version: '1.0'}, {name: 'Vue2.0', version: '2.0'} ] } });
(1)文本插值:
数据绑定最基础的形式就是文本插值,使用的是双大括号标签{{}},为Mustache语法。例:
<span>Hello {{ name }}</span> // Hello Vue
Vue.js实例vm中name属性的值将会替换Mustache标签中的name,并且修改数据对象中的name属性,DOM也会随之更新。在浏览器console中运行vm.name = 'Vue 1.0',输出结果为Hello Vue1.0。
模板语法同时也支持单次插值,即首次赋值后再更改vm实例属性值不会引起DOM变化,例:
<span>Hello {{* name }}</span> // Hello Vue
Vue.js 2.0去除了{{*}}这种写法,采用v-once代替。以上模板需改写成<span v-once>{{name}}</span>。
(2)HTML属性:
Mustache标签也同样适用于HTML属性中。例:
<div id="id-{{id}}"></div> //<div id="id-1"></div>
Vue.js 2.0废弃了这种写法,用v-bind指令代替,<div v-bind:id="'id=' + id"></div>代替,或简写为<div :id="'id-' + id"></div>。
(3)绑定表达式:
放在Mustache标签内的文本内容成为绑定表达式。除了直接输出属性值之外,一段绑定表达式可以由有关简单的JavaScript表达式和可选的一个或多个过滤器构成。例:
{{ index + 1 }} // 1 {{ index == 0 ? 'a' : 'b' }} // a {{ name.split('').join('|') }} // V|u|e
每个绑定中只能包含单个表达式,并不支持JavaScript语句,否则Vue.js会抛出warning异常。并且绑定表达式里不支持正则表达式,如果需要进行复杂的转换,可以使用过滤器或者计算属性来进行处理。例:
{{ var a = 1; }} // 无效 {{ if(ok) { return name } }} // 无效,但可以写成ok ? name : ''或者ok && name这两种写法
(4)过滤器:
Vue.js允许在表达式后添加可选的过滤器,以管道符“|”指示。例:
{{ name | uppercase }} // VUE
Vue.js将name的值传入给uppercase这个内置的过滤器中(本质是一个函数),返回字符串的大写值。同时也允许多个过滤器链式使用。例:
{{ name | filterA | filterB }}
也允许传入多个参数,例:
{{ name | filterA arg1 arg2 }}
此时,filterA将name的值作为第一个参数,arg1,arg2作为第二、第三个参数传入过滤器函数中。最终函数的返回值即为输出结果。arg1,arg2可以使用表达式,也可以加上单引号,直接传入字符串。例:
{{ name.split('') | limitBy 3 1 }} // u,e
过滤器limitBy可以接受两个参数,第一个参数是设置显示个数, 第二个参数为可选,指从开始元素的数组下标。
Vue.js内置了10个过滤器:
a、capitalize:字符串首字符转换成大写
b、uppercase:字符串转换成大写
c、lowercase:字符串转换成小写
d、currency 参数为{String} [货币符号],{Number} [小数位],将数字转换成货币符号,并且会自动添加数字分节号。例:
{{ amount | currency '¥' 2 }} // 若amount值10000,则输出¥10,000.00
e、pluralize 参数为{String} single, [double, triple],字符串复数化。如果接收的是一个参数,那复数形式就是在字符串末尾直接加一个”s“。如果接收多个参数,则会被当成数组处理,字符串会添加相对应数组下标的值。如果字符串的个数多余参数个数,多出部分会都添加最后一个参数的值。例:
<p v-for="c in count"> {{ c | pluralize 'item' }} {{ c | pluralize 'st' 'nd' 'rd' 'th' }} </p>
输出结果为:
1item 1st
2items 2nd
3items 3rd
4items 4th
f、json 参数{Number} [indent] 空格缩进数,与JSON.stringfy()作用相同,将json对象数据输出成符合json格式的字符串。
g、debounce传入值必须是函数,参数可选,为{Number}[wait],即延时时长。作用是当调用函数n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。例:
<input v-on:keyup="onKeyup | debounce 500" /> // input元素上监听了keyup事件,并且延迟500ms触发
h、limitBy传入值必须是数组,参数为{Number}limit,{Mumber}[offset],limit为显示个数,offset为开始显示数组下标。例:
<div v-for="item in items | limitBy 10"></div> // items为数组,且只显示数组中的前十个元素
i、filterBy传入值必须是数组,参数为{String | Function} targetStringOrFunction,即需要匹配的字符串或函数(通过函数返回值为true或false来判断匹配结果);”in“(可选分隔符);{String}[...searchKeys],为检索的属性区域。例:
<p v-for="name in names | filterBy '1.0'">{{name}}</p> // 检索items数组中值包含1.0的元素 <p v-for="item in items | filterBy '1.0'" in 'names'">{{item | json}}</p> // 检索items数组中元素属性name值为1.0的元素输出。检索区域也可以为数组,即in [name, version],在多个属性中进行检索
输出结果为:
Vue1.0
{"name": "Vue1.0", "version": "1.0"}
<p v-for="item in items | filterBy customFilter">{{ item | json }}</p> // 使用自定义的过滤函数,函数可以在选项methods中定义 methods: { customFilter: function(item) { if(item.name) return true // 检索所有元素中包含name属性的元素 } }
j、orderBy 传入值必须是数组,参数为{String | Array | Function}sortKeys,即指定排序策略。这列可以使用单个键名,也可以传入包含多个排序键名的数组。也可以像Array.sort()那样传入自己的排序策略函数。第二个参数为可选参数{String}[order],即选择升序或降序,order>=0为升序,order<0为降序。例:
单个键名: <p v-for="item in items | orderBy 'name' -1">{{item.name}}</p> // items数组中以键名name进行降序排列 多个键名: <p v-for="item in items | orderBy [name, version]">{{item.name}}</p> // 使用items里的两个键名进行排序 自定义排序: <p v-for="item in items | orderBy customOrder">{{item.name}}</p> methods: { customOrder: function(a, b) { return parseFloat(a.version) > parseFloat(b.version) // 对比item中的version的值的大小进行排序 } }
需要注意的是,Vue.js 2.0中已经去除了内置的过滤器。
(5)指令:
Vue.js也提供指令(Directives),可以理解为当表达式的值发生改变时,会有些特殊性为作用到绑定的DOM上。指令通常会直接书写在模板的HTML元素中,而为了有别于普通的属性,Vue.js指令是带有前缀的v-的属性。写法上来说,指令的值限定为绑定表达式。
a、参数
<img v-bind:src="avatar" />
指令v-bind可以在后面带一个参数,用冒号(:)隔开,src即为参数。此时img标签中的src会与vm实例中的avatar绑定,等同于:
<img src="{{avatar}}" />
b、修饰符
修饰符(Modifiers)是以半角句号.开始的特殊后置u,用于表示指令应该以特殊方式绑定。
<button v-on:click.stop="doClick"></button>
v-on的作用是在对应的DOM元素上绑定事件监听器,doCLick为函数名,而stop即为修饰符,作用是停止冒泡,相当于调用了e.stopPropagation()。
2、计算属性:
Vue.js提供了计算属性这种方法,避免在模板中加入过重的业务逻辑,保证模板的结构清晰和可维护性。
a、基础例子:
var vm = new Vue({ el: '#app', data: { firstName: 'Gavin', lastName: 'CLY' }, computed: { fullName: function() { // this指向vm实例 return this.firstName + ' ' + this.lastName } } }); <p>{{ firstName }}</p> // Gavin <p>{{ lastName }}</p> // CLY <p>{{ fullName }}</p> // Gavin CLY
此时,你对vm.firstName和vm.lastName进行修改,始终会影响vm.fullName。
b、Setter:
var vm = new Vue({ el: '#el', data: { cents: 100 }, computed: { price: { set: function(newValue) { this.cents = newValue * 100; }, get: function() { return (this.cents / 100).toFixed(2); } } } });
在处理商品价格的时候,后端汪汪会把价钱定义成以分为单位的整形,避免在处理浮点类型数据时产生的问题。而前端则需要把价钱再转成元进行展示,而且如果需要对价钱进行修改的话,则又要把输入的价格再恢复到分传给后端,很是繁琐。
而在使用Vue.js的计算属性后,我们可以将vm.cents设置为后端所存的数据,计算属性price为前端展示和更新的数据。
<p>¥{{price}}</p> // ¥1.00
此时更改vm.price = 2,vm.cents会被更新为200,在传递给后端时无需在手动转化一遍数据。
3、表单控件:
Vue.js中提供了v-model的指令对表单元素进行双向绑定,在修改表单元素值的同时,实例vm中对应的属性值也同时更新,反之亦然。下面例子主要介绍input元素绑定v-model后的具体用法和处理方式,示例所依赖的js代码如下:
var vm = new Vue({ el: '#app', data: { message: '', gender: '', checked: '', multiChecked: [], selected: '', multiSelected: [] a: 'checked', b: 'checked' } });
a、Text
输入框示例,用户输入的内容和vm.message直接绑定:
<input type="text" v-model="message" /> <span>Your input is: {{ message }}</span>
b、Radio
单选框示例,gender的值即为选中的radio元素的value值:
<label><input type="radio" value="male" v-model="gender">男</label> <label><input type="radio" value="female" v-model="gender">女</label> <p>{{ gender }}</p>
c、Checkbox
Checkbox分为两种情况:单个勾选框和多个勾选框。
单个勾选框,v-model即为布尔值,此时input的value并不影响v-model的值。
<input type="checkbox" v-model="checked" /> <span>checked: {{ cehcked }}</span>
多个勾选框,v-model使用相同的属性名称,且属性为数组。
<label><input type="checkbox" value="1" v-model="multiChecked">1</label> <label><input type="checkbox" value="2" v-model="multiChecked">2</label> <label><input type="checkbox" value="3" v-model="multiChecked">3</label> <p>MultiChecked: {{ multiChecked.join('|') }}</p>
d、Select
同Checkbox元素一样,Select也分单选和多选两种,多选的时候也需要绑定到一个数组。
单选:
<select v-model="selected"> <option selected>A</option> <option>B</option> <option>C</option> </select> <span>Selected: {{ selected }}</span>
多选:
<select v-model="multiSelected" multiple> <option selected>A</option> <option>B</option> <option>C</option> </select> <p>multiSelected: {{ multiSelected.join('|') }}</p>
e、绑定value
表单控件的值同样可以绑定在Vue实例的动态属性上,用v-bind实现。例:
1、checkbox:
<input type="checkbox" v-model="checked" v-bind:true-value="a" v-bind:false-value="b" /> 选中: vm.checked == vm.a // true 未选中: vm.checked == vm.b // true
2、Radio:
<input type="radio" v-model="checked" v-bind:value="a"> 选中:vm.checked == vm.a // true
3、Select Options:
<select v-model="selected"> <!-- 对象字面量 --> <option v-bind:value="{ number: 123 }">123</option> </select> 选中: typeof vm.selected // object vm.selected.number // 123
f、参数特性
Vue.js为表单控件提供了一些参数,方便处理某些常规操作。
1、lazy:
默认情况下,v-model在input事件中同步输入框值与数据,加lazy属性后会在change事件中同步。
<input v-model="query" lazy />
2、number:
会自动将用户输入转为Number类型,如果原值转换结果为NaN则返回原值。
<input v-model="age" number />
3、debounce:
debounce为设置的最小延时,单位为ms,即为单位时间内仅执行一次刷新。该参数往往应用在高耗操作,例如在更新时发出ajax请求返回提示信息。
<input v-model="query" debounce="500" />
不过Vue.js 2.0中取消了lazy和number作为参数,用修饰符(modifier)来代替:
<input v-model.lazy="query" /> <input v-model.number="age" />
新增了trim修饰符,去掉输入值首尾空格:
<input v-model.trim="name" />
去除了debounce这个参数,原因是无法检测到输入新数据,但尚未同步到vm实例属性时这个状态。如果仍有需要,提供手动实现的例子https://jsbin.com/zefawu/3/edit?html,output。
4、Class与Style绑定
在开发过程中,,我们经常会遇到动态添加类名或直接修改内联样式(例如tab切换)。class与style都是DOM元素的attribute,我们当然可以直接使用v-bind对这两个属性进行数据绑定。例如<p v-bind:style="style"></p>,然后通过修改vm.style的值对元素样式进行修改。但这样未免过于繁琐而且容易出错,所以Vue.js为这两个属性单独做了增强处理,表达式的结果类型除了字符串之类,还可以是对象和数组。
a、Class绑定:
首先说明的是class属性,我们绑定的数据可以是对象和数组,具体的语法:
(1)对象语法:v-bind:class接受参数是一个对象,而且可以与普通的class属性共存。
<div class="tab" v-bind:class="{'active: active, 'unactive': !active}"> </div> vm实例中需要包含 data: { active: true }
渲染结果为<div class="active"></div>
(2)数组语法:v-bind:class也接受数组作为参数。
<div v-bind:class="[classsA, classB]"></div> vm实例中需要包含: data: { classA: 'class-a', classB: 'class-b' }
渲染结果为:<div class="class-a class-b"></div>
也可以使用三元表达式切换数组中的class,<div v-bind:class="[classA, isB ? classB : '']"></div>。如果vm.isB = false,则渲染结果为<div v-bind:class="class-a"></div>。
b、内联样式绑定:
style属性绑定的数据即为内联样式,同样具有对象和数组两种形式:
(1)对象语法:直接绑定符合样式格式的对象。
<div v-bind:style="alertStyle"></div> data: { alertStyle: { color: 'red', fontSize: '20px' } }
除了直接绑定对象外,也可以绑定单个属性或直接使用字符串。
<div v-bind:style="{fontSize: alertStyle.fontSize, color: 'red'}"></div>
(2)数组语法:v-bind:style允许将多个样式对象绑定到同一元素上。
<div v-bind:style="[styleObjectA, styleObjectB]"></div>
c、自动添加前缀:
在使用transform这类属性时,v-bind:style会根据需要自动添加厂商前缀。:style在运行时进行前缀探测,如果浏览器版本本身就支持不加前缀的css属性,那就不会添加。
六、模板渲染
当获取到后端数据后,我们会把它按照一定的规则加载到写好的模板中,输出成在浏览器中显示的HTML,这个过程就称之为模板渲染。而Vue.js是在前端(即浏览器内)进行的模板渲染。
1、前后端渲染对比
前端渲染的优点在于:
(1)业务分离,后端只要提供数据接口,前端在开发时他也不需要部署对应的后端环境,通过一些代理服务器工具就能远程获取后端数据进行开发,能够提升开发效率。
(2)计算量转义,原本需要在后端渲染的任务转移给了前端,减轻了服务器的压力。
后端渲染的优点在于:
(1)对搜索引擎友好。
(2)首页加载时间短,后端渲染加载完成后就直接显示HTML,但前端渲染在加载完成后还需要有段js渲染的时间。
Vue.js2.0开始支持服务端渲染,从而让开发者在使用上有了更多选择。
2、条件渲染
Vue.js 2.0提供v-if、v-else、v-show、v-for这几个指令来说明模板和数据间的逻辑关系,这几本就构成了引擎模板的主要部分。
a、v-if/v-else
v-if和v-else的作用是根据数值来判断是否输出该DOM元素,以及包含的子元素。例:
<div v-if="yes">yes</div>
如果当前vm实例中包含data.yes = true,则模板引擎将会编译这个DOM节点,输出<div>yes</div>。
我们也可以利用v-else来配合v-if使用。例:
<div v-if="yes">yes</div> <div v-else>no</div>
需要注意的是,v-else必须紧跟v-if,不然该指令不起作用。例:
<div v-if="yes">yes</div> <p>the v-else div shows</p> <div v-else>no</div>
最终三个元素都会输出显示在浏览器中。
v-if绑定的元素包含子元素则不影响和v-else的使用。例:
<div v-if="yes"> <div v-if="inner">inner</div> <div v-else>not inner</div> </div> <div v-else>no</div> new Vue({ data: { yes: true, inner: false } }
输出结果为:
<div> <div> not inner</div> </div>
b、v-show
除了v-if,v-show也是可以根据条件展示元素的一种指令。例:
<div v-show="show">show</div>
也可以搭配v-else使用,用法和v-if一致。例:
<div v-show="show">show</div> <div v-else>hidden</div>
与v-if不同的是,使用v-show元素,无论绑定值为true 或者false,均会渲染并保持在DOM中。绑定值的改变只会切换元素的css属性display。例:
<div v-if="show">if</div> <div v-show="show">show</div>
show为true时的输出结果为:
<div>if</div> <div>show</div>
show为false时的输出结果为:
<div style="display: none;">show</div>
c、v-if vs v-show
从上述v-show图能明显看到,当v-if和v-show的条件发生变化时,v-if引起了DOM操作级别的变化,而v-show仅发生了样式的变化,从切换的角度考虑,v-show消耗的性能要比v-if小。
初次之外,v-if切换时,Vue.js会有一个局部编译/卸载的过程,因为v-if中的模板也可能包括数据绑定或子组件。v-if会确保条件块在切换当中适当地销毁与中间内部的事件监听器和子组件。而且v-if是惰性的,如果在初始条件为假时,v-if本身什么都不会做,而v-show则仍会进行正常的操作,然后把css样式设置为display: none。
所以,总的来说,v-if有更高的切换消耗而v-show有更高的初始渲染消耗,我们需要根据实际的使用场景来选择合适的指令。
3、列表渲染
v-for指令主要用于列表渲染,将根据接收到数组重复渲染v-for绑定到的DOM元素及内部的子元素,并且可以通过设置别名的方式,获取数组内数据渲染到节点中。例:
<ul> <li v-for="item in items"> <h3>{{ item.title }}</h3> <p>{{ item.description }}</p> </li> </ul> var vm = new Vue({ el: '#app', data: { items: [ { title: 'title-1', description: 'description-1' }, { title: 'title-2', description: 'description-2' }, { title: 'title-3', description: 'description-3' }, { title: 'title-4', description: 'description-4' } ] } });
其中items为data中的属性名,item为别名,可通过item来获取当前数组遍历的每个元素,输出结果为:
<ul> <li> <h3>title-1</h3> <p>description-1</p> </li> <li> <h3>title-2</h3> <p>description-2</p> </li> <li> <h3>title-3</h3> <p>description-3</p> </li> <li> <h3>title-4</h3> <p>description-4</p> </li> </ul>
v-for内置了$index变量,可以在v-for指令内调用,输出当前数组元素的索引。另外,我们也可以自己指定索引的别名,如<li v-for="(index, item) in items">{{ index }} - {{ $index }} - {{ item.title }}</li>,输出结果为:
<ul> <li> 0 - 0 - title-1 </li> <li> 0 - 0 - title-2 </li> <li> 0 - 0 - title-3 </li> <li> 0 - 0 - itle-4 </li> </ul>
需要注意的是Vue.js对data中数组的原生方法进行了封装,所以在改变数组时能出发视图更新 ,但以下两种情况是无法触发视图更新的:
(1)通过索引直接修改数组元素,例如vm.items[0] = { title: 'title-changed' };
(2)无法直接修改“修改数组”的长度,例如vm.items.length = 0;
对于第一种情况,Vue.js提供了$set方法,在修改数据的同时进行视图更新,可以写成:
vm.items.$set(0, { title: 'title-change' }) 或者 vm.$set('items[0], { title: 'title-changed' }),这两种方法皆可以达到效果。
在列表渲染的时候,有个性能方面的小技巧,如果数组中又唯一标识id,例:
items: [ { _id: 1, title: 'title-1' }, { _id: 2, title: 'title-2' }, { _id: 3, title: 'title-3' } ... ]
通过trace-by给数组设定唯一标识我们将上述v-for作用于的li元素修改为:
<li v-for="item in items" track-by="_id"></li>
这样,Vue.js在渲染过程中会尽量复用原有对象的作用域及DOM元素。
v-for除了可以遍历数组外,也可以遍历对象,与$index类似,作用域内可以访问另一内置变量$key,也可以使用(key, value)形式自定义key变量。
<li v-for="(key, value) in objectDemo"> {{key}} - {{$key}} : {{value}} </li> var vm = new Vue({ el: '#app', data: { objectDemo: { a: 'a-value', b: 'b-value', c: 'c-value' } } });
输出结果:
a - a : a-value b - b : b-value c - c : c-value
最后,v-for还可以接受单个整数,用作循环次数:
<li v-for="n in 5"> {{ n }} </li>
输出结果为:
0
1
2
3
4
4、template标签用法
上述的例子中,v-show和v-if指令都包含在一个根元素中,那是否有方式可以将指令作用到多个兄弟DOM元素上?Vue.js提供了template标签,我们可以将指令作用到这个标签上,但最后的渲染结果里不会有它。例:
<template v-if="yes"> <p>This is first dom.</p> <p>This is second dom.</p> <p>This is third dom.</p> </template>
输出结果为:
<p>This is first dom.</p> <p>This is second dom.</p> <p>This is third dom.</p>
同样,template标签也支持使用v-for指令,用来渲染同级的多个兄弟元素。例:
<template v-for="item in items"> <p>{{ item.name }}</p> <p>{{ item.desc }}</p> </template>
七、事件绑定与监听
当模板渲染完成之后,就可以进行事件的绑定与监听了。Vue.js提供了v-on指令用来监听DOM事件,通常在模板内直接使用,而不像传统方式在js中获取DOM元素,然后绑定事件。例:
<button v-on:click="say">Say</button>
1、方法及内联语句处理器
通过v-on可以绑定实例选项属性methods中的方法作为事件的处理器 , v-on:后参数可接受所有的原生事件名称。例:
<button v-on:click="say">Say</button> var vm = new Vue({ el: '#app', data: { msg: 'Hello Vue.js!' }, methods: { say: function() { alert(this.msg); } } });
单击button,即可触发say函数,弹出alert框"Hello Vue.js!"。
Vue.js也提供了v-on的缩写形式,我们可以将模板中的内容改成<button @click='say'>Say</button>,这两句是等价的。
除了直接绑定methods函数外,v-on也支持内联JavaScript语句,但仅限一个语句。例:
<button v-on:click="sayFrom('from param')">Say</button> var vm = new Vue({ el: '#app', data: { msg: 'Hello Vue.js!' }, methods: { say: function(from) { alert(this.msg + ' ' + from); } } });
在直接绑定methods函数和内联JavaScript语句时,都有可能需要获取原生DOM事件对象,以下两种方式都可以获取:
<button v-on:click="showEvent">Event</button> <button v-on:click="showEvent($event)">showEvent</button> <button v-on:click="showEvent()">showEvent</button> //这样写获取不到event var vm = new Vue({ el: '#app', methods: { showEvent: function(event) { console.log(event); } } });
2、修饰符
Vue.js为指令v-on提供了多个修饰符,方便我们处理一些DOM事件的细节,并且修饰符可以串联使用。主要的修饰符如下:
.stop:等同于调用event.stopPropagation()。 // 不再派发事件,但是默认事件会执行
.prevent:等同于调用event.preventDefault()。 // 阻止默认事件的方法,会发生冒泡
.capture:使用capture模式添加事件监听器。
.self:只当事件是从监听元素本身触发时才触发回调。
使用方法如下:
<a v-on:click.stop="doThis"></a> <form v-on:submit.prevent="onSubmit"></form> // 阻止表单默认提交事件 <form v-on:submit.stop.prevent="onSubmit"></form> // 阻止表单默认提交事件且阻止冒泡 <form v-on:submit.stop.prevent></form> // 也可以只有修饰符,并不绑定事件
可以尝试运行以下这个例子,更好地理解修饰符在其中起到的作用。
<div v-on:click="saySelf('click fron inner')" v-on:click.self="saySelf('click from self')"> <button v-on:click="saySelf('button click')">button</button> <button v-on:click.stop="saySelf('button click')">button</button> </div> var vm = new Vue({ el: '#app', methods: { saySelf: function(msg) { alert(msg); } } });
除了事件修饰符之外,v-on还提供了按键修饰符,方便我们监听键盘事件中的按键。例:
<input v-on:keyup.13="submit" /> // 监听input的睡,当输入回车时触发submit函数(回车的keycode为13),用于处理常见的用户输入完直接按回车键提交
Vue.js给一些常用的按键名提供了别称,这样就省去了一些记keycode的事件。全部按键别名为:enter、tab、delete、esc、space、up、down、left、right。例:
<input v-on:keyup.enter="submit" />
Vue.js也允许我们自己定义按键别名,例:
Vue.directive('on').keyCodes.f1 = 112; // 即可以使用<input v-on:keyup.f1= "help" />
Vue.js 2.0中可以直接在Vue.config.keyCodes里添加自定义按键别名,无需修改v-on指令,例:
Vue.config.keyCodes.f1 = 12;
3、与传统事件绑定的区别
如果你之前没有接触过Angularjs、ReactJS这类框架,或许会对Vue.js这种事件监听方式感到困惑。毕竟我们一开始接受的理念就是将HTML和JS隔离开编写。但其实Vue.js事件处理方法和表达式都严格绑定在当前视图的ViewModel上,所以并不会导致维护困难。
而这么写的好处在于:
(1)无需手动管理事件。ViewModel被销毁时,所有的事件处理器都会被自动删除,让我们从获取DOM绑定事件然后在特定情况下再解绑这样的事情中解脱出来。
(2)解耦。ViewModel代码是纯粹的逻辑代码,和DOM无关,有利于我们写自动化测试用例。
还有个与以往不同的细节是,我们在处理ul、li这种列表,尤其是下拉刷新这种需要异步加载数据的列表时,往往会把li事件代理到ul上,这样异步加载进来的新数据就不需要再次绑定事件。而Vue.js这类的框架由于不需要手动添加事件,往往直接会把事件绑定在li上,类似<li v-repeat="item in items" v-on:click="clickLi"></li>,理论上每次新增li的时候都会进行同li个数的事件绑定,比用事件代理多耗了写性能。但在实际运用中并没有什么特别的性能瓶颈影响,而且我们也省去在代理中处理e.target的步骤,让事件和DOM元素关系更紧密、简单。
八、Vue.extend()
组件化开发也是Vue.js种非常重要的一个特性,我们可以将一个页面看成一个大的根组件,里面包含的元素就是不同的子组件,子组件也可以在不同的根组件里被调用。在上述例子中,可以看到在一个页面中通常会声明一个Vue的实例new Vue({})作为根组件,那么如何生成可被重复使用的子组件呢?Vue.js提供了Vue.extend(option)方法,创建基础Vue构造器的“子类”,参数options对象和直接声明Vue实例参数对象基本一致,使用方法如下:
var child = Vue.extend({ template: '#child', // 不同的是,el和data选项需要通过函数返回值赋值,避免多个组件实例共用一个数据 data: function() { return { ... } } ... }); Vue.component('child', child) // 全局注册子组件 <child ...></child> // 子组件在其他组件内的调用方式