VUE
What
Vue.js是一个构建数据驱动的web界面的库。技术上,它重点集中在MVVM模式的ViewModel层,因此它非常容易学习,非常容易与其它库或已有项目整合。
Vue.js的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图。
Vue.js 的核心是一个响应的数据绑定系统,它让数据与DOM保持同步非常简单。在使用jQuery手工操作DOM时,我们的代码常常是命令式的、重复的与易错的。Vue.js拥抱数据驱动的视图概念。通俗地讲,它意味着我们在普通HTML模板中使用特殊的语法将DOM “绑定”到底层数据。
安装
独立版本
直接下载并用 < script > 标签引入,Vue会被注册为一个全局变量。如下代码,这样就可以在脚本中使用Vue.js了。
CDN
也可以在 jsdelivr或 cdnjs获取 (版本更新可能会略滞后)。
NPM
在用 Vue.js 构建大型应用时推荐使用 NPM 安装,NPM 能很好地和诸如 Webpack 或 Browserify 的 CommonJS 模块打包器配合使用。Vue.js 也提供配套工具来开发单文件组件。
$ npm install vue
# 获取CSP兼容版本:
$ npm install vue@csp
# 获取最新开发版本(来自于GitHub):
$ npm install yyx990803/vue#dev
构造器
每个Vue.js应用的起步都是通过构造函数Vue创建一个Vue的根实例:
var vm = new Vue({
// 选项
})
一个Vue实例其实正是一个MVVM模式中所描述的 ViewModel - 因此在文档中经常会使用vm这个变量名
属性与方法
每个Vue实例都会代理其data对象里所有的属性,如下代码:
var data = { a: 1 }
var vm = new Vue({
data: data
})
//vm.a === data.a -> true
// 设置属性也会影响到原始数据
vm.a = 2
// data.a -> 2
// ... 反之亦然
data.a = 3
//vm.a -> 3
注意只有这些被代理的属性是响应的。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。
除了前面这些数据属性,Vue实例还有一些有用的实例属性与方法。这些属性与方法都有前缀 $,以便与代理的数据属性区分。例如:
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // -> true
vm.$el === document.getElementById('example') // -> true
// $watch 是一个实例方法
vm.$watch('a', function (newVal, oldVal) {
// 这个回调将在 `vm.a` 改变后调用
})
数据绑定语法
文本
数据绑定最基础的形式是文本插值,使用 {{}} 语法(双大括号):
<span>Message: {{ msg }}</span>
{{ msg }} 标签会被相应数据对象的 msg 属性的值替换。每当这个属性变化时它也会更新。
也可以只处理单次插值,今后的数据变化就不会再引起插值更新了:
<span>This will never change: {{* msg }}</span>
如下JavaScript代码:
var data={msg:'Hello Vue.js!'};
new Vue({
el: '#demo',
data: data
})
data.msg="Hello World!";
原始HTML
双大括号标签将数据解析为纯文本而不是HTML。为了输出真的HTML字符串,需要用三大括号标签:
<div>{{{ msg }}}</div>
如下javascript代码:
var data={msg:'<p>Hello Vue.js!</p>'};
new Vue({
el: '#demo',
data: data
})
在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用 v-html,永远不要用在用户提交的内容上!
HTML特性
双大括号标签也可以用在 HTML 特性 (Attributes) 内:
<div id="{{ id }}"></div>
javascript代码如下:
var data={id:'demo'};
new Vue({
el: 'div',
data: data
})
我们去查看HTML源码,是不是id已经变成我们设置的id了。
JavaScript表达式
Vue.js 在数据绑定内支持全功能的JavaScript表达式:
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
过滤器
Vue.js 允许在表达式后添加可选的“过滤器 (Filter) ”,以“管道符(|)”指示。过滤器本质上是一个函数,这个函数会接收一个值,将其处理并返回。
{{ message | uppercase }}
这里我们将表达式 message 的值“管输(pipe)”到内置的 uppercase 过滤器,这个过滤器其实只是一个 JavaScript 函数,返回大写化的值。Vue.js 提供数个内置过滤器,在后面我们会谈到如何开发自己的过滤器。
可以串联多个过滤器:
{{ message | filterA | filterB }}
html代码:
<div id='demo'>
<span>{{msg | lowercase | capitalize}}</span>
</div>
javaScript代码:
var data={msg:'heLLO!'};
new Vue({
el: '#demo',
data: data
})
运行结果为:Hello
指令
Vue.js指令 (Directives) 是特殊的带有前缀v-的特性。本质是模板中出现的特殊标记,让处理模板的库知道需要对这里的DOM元素进行一些对应的处理。指令的职责就是当其表达式的值改变时把某些特殊的行为应用到 DOM 上。
<p v-if="msg">Hello!</p>
这里 v-if 指令将根据表达式 msg 值的真假删除/插入 < p > 元素。
在Vue.js中为我们提供了一些指令:v-text,v-html,v-model,v-on,v-else等等,同学们可以去查看Vue.js的指令api(cn.vuejs.org/api/#指令)。
javascript代码:
var data={msg:0};
new Vue({
el: '#demo',
data: data
})
计算属性
在模板中表达式非常便利,但是它们实际上只用于简单的操作。模板是为了描述视图的结构。在模板中放入太多的逻辑会让模板过重且难以维护。这就是为什么 Vue.js 将绑定表达式限制为一个表达式。如果需要多于一个表达式的逻辑,应当使用计算属性。
在 Vue.js 中,你可以通过 computed 选项定义计算属性:
<div id="example">
a={{ a }}, b={{ b }}
</div>
var vm = new Vue({
el: '#example',
data: {
a: 1
},
computed: {
// 一个计算属性的 getter
b: function () {
// `this` 指向 vm 实例
return this.a + 1
}
}
})
运行结果为:a=1,b=2
Class与Style绑定
Class对象语法
可以传给 v-bind:class 一个对象,以动态地切换class。注意 v-bind:class 指令可以与普通的 class 特性共存。
html代码:
<div class="static" v-bind:class="{ 'class-a': isA, 'class-b': isB }"></div>
javascript代码:
var vm = new Vue({
el: 'div',
data: {
isA: true,
isB: false
}
})
渲染为:
<div class="static class-a">
</div>
当 isA 和 isB 变化时,class 列表将相应地更新。例如,如果 isB 变为 true,class 列表将变为 “static class-a class-b”。
Class数组语法
可以把一个数组传给 v-bind:class,以应用一个 class 列表:
html代码:
<div v-bind:class="[classA, classB]">
js代码:
var vm = new Vue({
el: 'div',
data: {
classA: 'class-a',
classB: 'class-b'
}
})
渲染为:
<div class="class-a class-b"></div>
Style对象语法
v-bind:style的对象语法十分直观——看着非常像 CSS,其实它是一个JavaScript对象。CSS属性名可以用驼峰式(camelCase)或短横分隔命名(kebab-case):
html代码:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">Style 对象语法</div>
javascript代码:
var vm = new Vue({
el: 'div',
data: {
activeColor: 'red',
fontSize: 30
}
})
渲染为:
<div style="color: red; font-size: 30px;">Style 对象语法</div>
Style数组语法
v-bind:style的数组语法可以将多个样式对象应用到一个元素上。
<div v-bind:style="[styleObjectA, styleObjectB]">Style 数组语法</div>
javascript代码:
var vm = new Vue({
el: 'div',
data: {
styleObjectA: {
color: 'red'
},
styleObjectB: {
fontSize: '30px'
}
}
})
渲染为:
<div style="color: red; font-size: 30px;">Style 数组语法</div>
渲染指令
v-if
在字符串模板中,如Handlebars,我们得像这样写一个条件块:
<!-- Handlebars 模板 -->
{{#if ok}}
<h1>Yes</h1>
{{/if}}
在 Vue.js,我们使用 v-if 指令实现同样的功能:
<h1 v-if="ok">Yes</h1>
也可以用 v-else 添加一个 “else” 块:
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
template v-if
因为 v-if 是一个指令,需要将它添加到一个元素上。但是如果我们想切换多个元素呢?此时我们可以把一个 元素当做包装元素,并在上面使用 v-if,最终的渲染结果不会包含它。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
当ok为真值时,渲染为:
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
v-show
另一个根据条件展示元素的选项是 v-show 指令。用法大体上一样:
<h1 v-show="ok">Hello!</h1>
不同的是有 v-show 的元素会始终渲染并保持在 DOM 中。v-show 是简单的切换元素的 CSS 属性 display。
v-else
可以用 v-else 指令给 v-if 或 v-show 添加一个 “else 块”:
<div v-if="Math.random() > 0.5">
Sorry
</div>
<div v-else>
Not sorry
</div>
v-else 元素必须立即跟在 v-if 或v-show元素的后面,否则它不能被识别。
限制: 前一兄弟元素必须有 v-if 或 v-else-if。
v-for
当和 v-if 一起使用时,v-for 的优先级比 v-if 更高(先遍历,后判断)
可以使用 v-for指令基于一个数组渲染一个列表。这个指令使用特殊的语法,形式为 item in items,items是数据数组,item是当前数组元素的别名:
<ul id="example">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
js代码:
var example1 = new Vue({
el: '#example',
data: {
items: [
{ message: 'one' },
{ message: 'two' }
]
}
})
在 v-for 块内我们能完全访问父组件作用域内的属性,另有一个特殊变量 $index,正如你猜到的,它是当前数组元素的索引:
<ul id="example">
<li v-for="item in items">
{{ parentMessage }} - {{ $index }} - {{ item.message }}
</li>
</ul>
js代码:
var example2 = new Vue({
el: '#example',
data: {
parentMessage: 'Parent',
items: [
{ message: 'one' },
{ message: 'two' }
]
}
})
template v-for
类似于 template v-if,也可以将 v-for 用在 template 标签上,以渲染一个包含多个元素的块。例如:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>
表单控件绑定
文本输入框Text
可以在表单的input 元素上使用v-model 指令来创建双向数据绑定。它会根据input元素的类型自动选取正确的绑定模式。
<span>Message is:{{msg}}</span>
<input type="text" v-model="msg" placeholder="edit me">
js代码:
var vm = new Vue({
el: 'div',
data: {
msg:"hello world"
}
})
其中placeholder为默认显示提示。
多选框Checkbox
多个勾选逻辑值,如下代码:
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
当我们要绑定值时可以用v-bind来绑定,如下代码:
<div>
<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b">
</div>
当checkbox为true时,值为a;当为fals时,值为b。
单选按钮Radio
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
我们可以用v-bind指令来绑定数据。如下代码:
<input type="radio" v-model="pick" v-bind:value="a">
下拉列表select
单选,如下代码:
<select v-model="selected">
<option selected>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
多选,如下代码:
<select v-model="selected" multiple>
<option selected>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected | json }}</span>
动态选项,用 v-for 渲染,如下代码:
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
js代码如下:
new Vue({
el: 'div',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
对象的格式应为 {text:’’, value:’’}。这允许你把展示的文字和其背后对应的值区分开来。
也可以用v-bind来绑定数据,如我们要绑定一个数值型的数据,如下代码:
<select v-model="selected">
<!-- 对象字面量 -->
<option v-bind:value="{ number: 123 }">123</option>
</select>
参数特性
lazy
在默认情况下,v-model 在input 事件中同步输入框值与数据,可以添加一个特性 lazy,从而改到在 change 事件中同步:
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model="msg" lazy>
如下实例,当在文本框中输入完成以后,才更新内容:
<span>Message is:{{msg}}</span>
<input type="text" v-model="msg" lazy>
number
如果想自动将用户的输入保持为数字,可以添加一个特性 number:
<input v-model="age" number>
debounce
debounce 设置一个最小的延时,在每次敲击之后延时同步输入框的值与数据。如果每次更新都要进行高耗操作(例如在输入提示中 Ajax 请求),它较为有用。
<input v-model="msg" debounce="500">
注意 debounce 参数不会延迟 input 事件:它延迟“写入”底层数据。
自定义指令
基础
Vue.js允许自定义指令,实质上是让你教 Vue一些新技巧:怎样将数据的变化映射到 DOM 的行为。可以使用 Vue.directive(id, definition) 的方法传入指令 id 和定义对象来注册一个全局自定义指令。定义对象需要提供一些钩子函数(全部可选):
bind: 仅调用一次,当指令第一次绑定元素的时候。
update: 第一次是紧跟在 bind 之后调用,获得的参数是绑定的初始值;以后每当绑定的值发生变化就会被调用,获得新值与旧值两个参数。
unbind:仅调用一次,当指令解绑元素的时候。
Vue.directive('my-directive', {
bind: function () {
// 准备工作
// 例如,添加事件处理器或只需要运行一次的高耗任务
},
update: function (newValue, oldValue) {
// 值更新时的工作
// 也会以初始值为参数调用一次
},
unbind: function () {
// 清理工作
// 例如,删除 bind() 添加的事件监听器
}
})
在注册之后,便可以在 Vue.js 模板中这样用(记着添加前缀 v-):
<div v-my-directive="someValue"></div>
当只需要 update 函数时,可以传入一个函数替代定义对象:
Vue.directive('my-directive', function (value) {
// 这个函数用作 update()
})
js代码如下:
<p v-font-color="'#ff6700'">hello Vue!</p>
<p v-font-color>hello Vue!</p>
// 参数1: 指令的名称
// 参数2: 指令函数
Vue.directive('font-color', function(color){
// this.el是指令所在的标签
console.log(color);
this.el.style.color = color ? color : '#333';
});
Vue.directive('my-directive', {
bind: function () {
console.log('my-directive')
},
update: function (value) {
this.el.innerHTML ='value - '+ value
}
})
var demo = new Vue({
el: '#demo',
data: {
my-directive: 'hello!'
}
})
指令实例属性
所有的钩子函数将被复制到实际的指令对象中,钩子内 this 指向这个指令对象。这个对象暴露了一些有用的属性:
el: 指令绑定的元素。
vm: 拥有该指令的上下文 ViewModel。
expression: 指令的表达式,不包括参数和过滤器。
arg: 指令的参数。
name: 指令的名字,不包含前缀。
modifiers: 一个对象,包含指令的修饰符。
descriptor: 一个对象,包含指令的解析结果。
<div id="demo" v-demo:hello.a.b="msg"></div>
js代码:
Vue.directive('demo', {
bind: function () {
console.log('demo bound!')
},
update: function (value) {
this.el.innerHTML =
'name - ' + this.name + '<br>' +
'expression - ' + this.expression + '<br>' +
'argument - ' + this.arg + '<br>' +
'modifiers - ' + JSON.stringify(this.modifiers) + '<br>' +
'value - ' + value
}
})
var demo = new Vue({
el: '#demo',
data: {
msg: 'hello!'
}
})
对象字面量
如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令可以使用任意合法的 JavaScript 表达式:
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
js代码如下:
Vue.directive('demo', function (value) {
document.write(value.color+" "+value.text);
})
字面修饰符
当指令使用了字面修饰符literal,它的值将按普通字符串处理并传递给 update 方法。update 方法将只调用一次,因为普通字符串不能响应数据变化。
<div id="demo" v-demo.literal="foo bar baz">
js代码如下:
Vue.directive('demo', function (value) {
document.write(value) // "foo bar baz"
})
元素指令
有时候,我们可能想要我们的指令可以以自定义元素的形式被使用,而不是作为一个特性。这与Angular 的 E 类指令的概念非常相似。元素指令可以看做是一个轻量的自定义组件(后面会讲到)。你可以像下面这样注册一个自定义的元素指令:
<bg-color>hello Vue!</bg-color>
<script>
Vue.elementDirective('my-directive', {
// API 同普通指令
bind: function () {
// 操作 this.el...
}
})
Vue.elementDirective('bg-color', {
bind(color) { // es6语法,es5用 bind: function(){}
this.el.style.backgroundColor = color ? color : '#ccc';
}
});
</script>
页面应用为:
<my-directive></my-directive>
自定义过滤器
基础
可以用全局方法 Vue.filter() 注册一个自定义过滤器,它接收两个参数:过滤器ID和过滤器函数。过滤器函数以值为参数,返回转换后的值:
Vue.filter('split0', function (value) {
return value.split('.')[0];
})
html代码:
<div id="demo">{{msg|split0}}</div>
js代码:
Vue.filter('split0', function (value) {
return value.split('.')[0];
})
var vm=new Vue({
el: '#example',
data:{
msg:'2.0'}
})
过滤器函数可以接收任意数量的参数:
Vue.filter('wrap', function (value, begin, end) {
return begin + value + end;
})
<!-- 'hello' => 'before hello after' -->
<span>{{msg | wrap 'before' 'after'}}</span>
双向过滤器
我们使用过滤器都是在把来自模型的值显视在视图之前转换它。不过也可以定义一个过滤器,在把来自视图(< input > 元素)的值写回模型之前转化它,如下代码:
Vue.filter('currencyDisplay', {
// model -> view
// 在更新 `<input>` 元素之前格式化值
read: function(val) {
return '¥'+val.toFixed(2)
},
// view -> model
// 在写回数据之前格式化值
write: function(val, oldVal) {
var number = +val.replace(/[^\d.]/g, '')
return isNaN(number) ? 0 : parseFloat(number.toFixed(2))
}
})
html代码如下:
<div id="example">
<input type="text" v-model="msg|currencyDisplay"><br>
<span>{{msg}}</span>
</div>
动态参数
如果一个过滤器参数没有被引号包裹,它会在当前 vm 的数据作用域里当做表达式进行动态求值。此外,过滤器函数的 this 上下文永远是调用它的当前 vm。
<input v-model="userInput">
<span>{{msg | concat userInput}}</span>
js代码:
Vue.filter('concat', function (value, input) {
// 这里 `input` === `this.userInput`
return value + input
})
Vue.filter('len', function (value) {
if(!value){
return 0;
}else{
return value.length
}
})
方法与事件处理器
方法处理器
可以用 v-on 指令监听 DOM 事件:
<div id="example">
<button v-on:click="greet">Greet</button>
</div>
我们绑定了一个单击事件处理器到一个方法 greet。下面在 Vue 实例中定义这个方法:
var vm = new Vue({
el: '#example',
data: {
name: 'Vue.js'
},
// 在 'methods' 对象中定义方法
methods: {
greet: function (event) {
// 方法内 'this'指向 vm
alert('Hello ' + this.name + '!')
// 'event' 是原生 DOM 事件
alert(event.target.tagName)
}
}
})
内联语句处理器
除了直接绑定到一个方法,也可以用内联 JavaScript 语句:
<div id="example-2">
<button v-on:click="say('hi')">Say Hi</button>
<button v-on:click="say('what')">Say What</button>
</div>
js代码:
new Vue({
el: '#example',
methods: {
say: function (msg) {
alert(msg)
}
}
})
有时也需要在内联语句处理器中访问原生 DOM 事件。可以用特殊变量 $event 把它传入方法:
<button v-on:click="say('hello!', $event)">Submit</button>
js代码:
new Vue({
el: '#example',
methods: {
say: function (msg, event) {
// 现在我们可以访问原生事件对象
event.preventDefault()
}
}
})
事件修饰符
在事件处理器中经常需要调用 event.preventDefault() 或 event.stopPropagation()。尽管我们在方法内可以轻松做到,不过让方法是纯粹的数据逻辑而不处理 DOM 事件细节会更好。为了解决这个问题,Vue.js 为 v-on 提供两个事件修饰符:.prevent 与 .stop。
<!-- 阻止单击事件冒泡 -->
<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>
按键修饰符
在监听键盘事件时,我们经常需要检测 keyCode。Vue.js允许为v-on添加按键修饰符:
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
记住所有的 keyCode 比较困难,Vue.js 为最常用的按键提供别名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
常用按键别名:enter、tab、delete、esc、space、up、down、left、right。
过度效果
简介
通过 Vue.js 的过渡系统,你可以轻松的为 DOM 节点被插入/移除的过程添加过渡动画效果。Vue 将会在适当的时机添加/移除 CSS 类名来触发 CSS3 过渡/动画效果,你也可以提供相应的 JavaScript 钩子函数在过渡过程中执行自定义的 DOM 操作。
以 v-transition="my-transition 这个指令为例,当带有这个指令的 DOM 节点被插入或移除时,Vue 将会:
1.用 my-transition这个 ID 去查找是否有注册过的 JavaScript 钩子对象。这个对象可以是由 Vue.transition(id, hooks) 全局注册,或是通过 transitions 选项定义在当前的组件内部。如果找到此对象,则会在过渡动画不同的阶段调用相应的钩子。
2.自动探测目标元素是否应用了 CSS 过渡效果或者动画效果,并在适当的时机添加/移除 CSS 类名。
3.如果没有提供 JavaScript 钩子函数,也没有检测到相应的 CSS 过渡/动画效果,DOM 的插入/移除会在下一帧立即执行。
<div v-if="show" transition="my-transition"></div>
所有的 Vue.js 过渡效果只有在该 DOM 操作是通过 Vue.js 触发时才会生效。触发的方式可以是通过内置指令,比如 v-if,或是通过 Vue 实例的方法,比如 vm.$appendTo()。
CSS 过渡
一个典型的 CSS 过渡效果定义如下:
<div v-if="show" v-transition="expand">hello</div>
然后为.expand-transition,.expand-enter 和.expand-leave 添加 CSS 规则:
/* 必需 */
.expand-transition {
transition: all .3s ease;
height: 30px;
padding: 10px;
background-color: #eee;
overflow: hidden;
}
/* .expand-enter 定义进入的开始状态 */
/* .expand-leave 定义离开的结束状态 */
.expand-enter, .expand-leave {
height: 0;
padding: 0 10px;
opacity: 0;
}
另外,可以提供 JavaScript 钩子:
Vue.transition('expand', {
beforeEnter: function (el) {
el.textContent = 'beforeEnter'
},
enter: function (el) {
el.textContent = 'enter'
},
afterEnter: function (el) {
el.textContent = 'afterEnter'
},
enterCancelled: function (el) {
// handle cancellation
},
beforeLeave: function (el) {
el.textContent = 'beforeLeave'
},
leave: function (el) {
el.textContent = 'leave'
},
afterLeave: function (el) {
el.textContent = 'afterLeave'
},
leaveCancelled: function (el) {
// handle cancellation
}
})
说简单点就是我们在操作某个元素时,当v-if的值为发生变化时,就会引起transition的变化,从而使的CSS样式改变。如果还是不明白,请看第四节《过渡流程详解》。
过渡的CSS类名
类名的添加和切换取决于 transition特性的值。比如 transition=“fade”,会有三个 CSS 类名:
1).fade-transition 始终保留在元素上。
2).fade-enter 定义进入过渡的开始状态。只应用一帧然后立即删除。
3).fade-leave 定义离开过渡的结束状态。在离开过渡开始时生效,在它结束后删除。
如果 transition 特性没有值,类名默认是 .v-transition, .v-enter 和 .v-leave。
过渡流程详解
根据我们第一节中的代码来讲解过渡流程:
当 show 属性改变时,Vue.js 将相应地插入或删除div元素,按照如下规则改变过渡的 CSS 类名:
如果 show 变为 false,Vue.js 将:
1.用 beforeLeave 钩子;
2.添加 v-leave 类名到元素上以触发过渡;
3.调用 leave 钩子;
4.等待过渡结束(监听 transitionend 事件);
5.从 DOM 中删除元素并删除 v-leave 类名;
6.调用 afterLeave 钩子。
如果 show 变为 true,Vue.js 将:
1.调用 beforeEnter 钩子;
2.添加 v-enter 类名到元素上;
3.把它插入 DOM;
4.调用 enter 钩子;
5.强制一次CSS 布局,让 v-enter 确实生效。然后删除 v-enter 类名,以触发过渡,回到元素的原始状态;
6.等待过渡结束;
7.调用afterEnter钩子。
渐近过渡
transition 与 v-for 一起用时可以创建渐近过渡。给过渡元素添加一个特性 stagger, enter-stagger 或 leave-stagger, 如下代码:
<div v-for="list" transition stagger="100"></div>
或者,提供一个钩子 stagger, enter-stagger 或 leave-stagger,以更好的控制. 如下代码:
Vue.transition('stagger', {
stagger: function (index) {
// 每个过渡项目增加 50ms 延时
// 但是最大延时限制为 300ms
return Math.min(300, index * 50)
}
})
如下实例html代码:
<div id="demo">
<input v-model="query">
<ul>
<li v-for="item in list | filterBy query"
transition="staggered"
stagger="100">
{{item.msg}}
</li>
</ul>
</div>
js代码:
new Vue({
el: '#demo',
data: {
query: '',
list: [
{ msg: 'Bruce Lee' },
{ msg: 'Jackie Chan' },
{ msg: 'Chuck Norris' },
{ msg: 'Jet Li' },
{ msg: 'Kung Fury' }
]
}
})
CSS代码:
ul {
padding-left: 0;
font-family: Helvetica, Arial, sans-serif;
}
.staggered-transition {
transition: all .5s ease;
overflow: hidden;
margin: 0;
height: 20px;
}
.staggered-enter, .staggered-leave {
opacity: 0;
height: 0;
}
以上代码的效果为:当我们在文本框中输入值时,会看到列表逐渐消失,删除内容时,会看到列表显示。
组件
使用组件
在Vue中,可以用 Vue.extend() 创建一个组件构造器:
var MyComponent = Vue.extend({
template:'..........' //选项
})
要把这个构造器用作组件,需要用 Vue.component(tag, constructor) 注册 :
// 全局注册组件,tag 为 my-component
Vue.component('my-component', MyComponent)
在注册之后,组件便可以用在父实例的模块中,以自定义元素 < my-component > 的形式使用。要确保在初始化根实例之前注册了组件:
<div id="example">
<my-component></my-component>
</div>
js代码如下:
// 定义
var MyComponent = Vue.extend({
template: '<div>A custom component!</div>'
})
// 注册
Vue.component('my-component', MyComponent)
// 创建根实例
new Vue({
el: '#example'
})
渲染为:
<div id="example">
<div>A custom component!</div>
</div>
Props 传递数据
组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用 props 把数据传给子组件。
“prop” 是组件数据的一个字段,期望从父组件传下来。子组件需要显式地用 props 选项声明 props:
···
Vue.component(‘child’, {
// 声明 props
props: [‘msg’],
// prop 可以用在模板内
// 可以用 this.msg
设置
template: ‘{{ msg }}’
})
···
然后向它传入一个普通字符串:
<child msg="hello!"></child>
动态Props
HTML 特性不区分大小写。名字形式为 camelCase 的 prop 用作特性时,需要转为 kebab-case(短横线隔开):
Vue.component('child', {
// camelCase in JavaScript
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
<!-- kebab-case in HTML -->
<child my-message="hello!"></child>
类似于绑定一个普通的特性到一个表达式,也可以用 v-bind 绑定动态 Props 到父组件的数据。每当父组件的数据变化时,也会传递给子组件:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
Prop 绑定类型
prop 默认是单向绑定:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。不过,也可以使用 .sync 或 .once 绑定修饰符显式地强制双向或单次绑定。
<!-- 默认为单向绑定 -->
<child v-bind:my-message="parentMsg"></child>
<!-- 双向绑定 -->
<child v-bind:my-message.sync="parentMsg"></child>
<!-- 单次绑定 -->
<child v-bind:my-message.once="parentMsg"></child>
双向绑定会把子组件的 my-message 属性同步回父组件的 parentMsg 属性。单次绑定在建立之后不会同步之后的变化。
自定义事件
Vue 实例实现了一个自定义事件接口,用于在组件树中通信。这个事件系统独立于原生 DOM 事件,做法也不同。
每个 Vue 实例都是一个事件触发器:
-
使用 $on() 监听事件;
-
使用 $emit() 在它上面触发事件;
-
使用 $dispatch() 派发事件,事件沿着父链冒泡;
-
使用 $broadcast() 广播事件,事件向下传导给所有的后代。
子组件可以用 this. p a r e n t 访 问 它 的 父 组 件 , 而 父 组 件 有 一 个 数 组 t h i s . parent 访问它的父组件,而父组件有一个数组 this. parent访问它的父组件,而父组件有一个数组this.children,包含它所有的子元素。
<!-- 子组件模板 -->
<template id="child-template">
<input v-model="msg">
<button v-on:click="notify">Dispatch Event</button>
</template>
<!-- 父组件模板 -->
<div id="events-example">
<p>Messages: {{ messages | json }}</p>
<child></child>
</div>
// 注册子组件
// 将当前消息派发出去
Vue.component('child', {
template: '#child-template',
data: function () {
return { msg: 'hello' }
},
methods: {
notify: function () {
if (this.msg.trim()) {
this.$dispatch('child-msg', this.msg)
this.msg = ''
}
}
}
})
// 启动父组件
// 将收到消息时将事件推入一个数组
var parent = new Vue({
el: '#events-example',
data: {
messages: []
},
// 在创建实例时 `events` 选项简单地调用 `$on`
events: {
'child-msg': function (msg) {
// 事件回调内的 `this` 自动绑定到注册它的实例上
this.messages.push(msg)
}
}
})
关于组件的介绍我们就先讲到这里,更多介绍在请参考这里。
-
$index可以获取当前数组的索引值,也可以使用v-for="(index, ls) in languageSkills"的方式。
-
默认情况下,数组中有相同值的元素会报错(对象数组是同一个对象),添加一个track-by="$index"指令可以解决
<p>我会的语言有:</p>
<ul>
<li v-for="ls in languageSkills" tack-by="$index">{{ls}}---{{$index}}</li>
</ul>
<script>
new Vue({
data: {
languageSkills: ['css', 'css']
}
}).$mount('#app');
</script>
- v-pre
不会编译这个元素的内容。例如使用插值语法时内容不会替换。
<p v-pre>{{msg}}</p>
<script>
new Vue({
data: {
msg: 'hello Vue!'
}
}).$mount('#app');
</script>
- v-cloak
使用插值与法的时候,在文档加载第一次时插值语法不会替换成数据,会原原本本的显示在那,使用这个指令就可以避免这种情况,也可以使用v-text或v-html代替。
<style>
[v-cloak]{ display: none; }
</style>
<p v-cloak>{{msg}}</p>
<script>
new Vue({
data: {
msg: 'hello Vue!'
}
}).$mount('#app');
</script>
- 内置指令
Vue指令 | Angular指令 | 简述 |
---|---|---|
v-text | ng-bind | 代替插值语法 |
v-html | ng-bind | 可以解析html代码 |
v-show | ng-show | 显示或隐藏元素 |
v-if | ng-if | 删除或插入元素 |
v-else | 和if else一个道理 | |
v-for | ng-repeat | 遍历元素 |
v-model | ng-model | 双向绑定 |
v-pre | 不去编译指定元素 | |
v-cloak | ng-cloak | 防止插值语法的闪烁 |
v-bind | ng-src等等 | 绑定节点属性 |