Vue.js
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
Vue.js下载地址:
开发版本(包含完整的警告和调试模式 )
生产版本(删除了警告,30.90KB min+gzip )
Vue实例
每个 Vue 应用都是通过用 Vue
函数创建一个新的 Vue 实例开始的:
var vm = new Vue({
// 选项
})
虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm
(ViewModel 的缩写) 这个变量名表示 Vue 实例。
- Vue实例中的选项配置项可查阅官网API文档。
Demo模板
<!DOCTYPE>
<html lang="en">
<head>
<mate charset="UTF-8"></mate>
<title></title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
</div>
<script>
var vm = new Vue({
el:"#root"
});
</script>
</body>
</html>
1.数据与方法
当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data
对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
- 这就是Vue数据驱动模型的基础。反过来Vue也能实现模型驱动数据,即双向数据绑定。这将在后面的章节中涉及到。
Demo
var data = {a:1};
var vm = new Vue({
el:"#root",
data:data
});
// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时 data
中存在的属性才是响应式的。
除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $
,以便与用户定义的属性区分开来。如vm.$data
、vm.$el
等。
Vue实例的配置项中还能自定义方法来提供各种场景的调用,对应配置项为methods
。
Demo
var data = {a:1};
var vm = new Vue({
el:"#root",
data:data,
methods:{
handleMethod:function(){
}
}
});
2.模板语法
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
2.1插值表达式
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
<div>Message: {{ msg }}</div>
Mustache 标签将会被替代为对应数据对象上 msg
属性的值。无论何时,绑定的数据对象上 msg
属性发生了改变,插值处的内容都会更新。
2.2指令
在Vue.js中,指令 (Directives) 是带有
v-
前缀的特殊特性。指令特性的值预期是单个 JavaScript 表达式(v-for
是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。 这里将介绍一些常用的指令。
v-text【文本节点】
<div v-text="text"></div>
v-html【标签节点】
<div v-html="html"></div>
v-on:事件(@事件)【事件绑定】
- 既可绑定Dom事件,也可绑定自定义事件。
<!DOCTYPE> <html lang="en"> <head> <mate charset="UTF-8"></mate> <title></title> <script src="vue.js"></script> </head> <body> <div id="root"> <div v-on:click="handleClick">{{count}}</div> </div> <script> var vm = new Vue({ el:"#root", data:{ count:1 }, methods:{ handleClick:function(){ this.count ++; } } }); </script> </body> </html>
v-on:事件
的缩写形式为@事件
v-bind:属性(:属性)【属性绑定】
- 绑定元素内的属性。
<div> <a v-bind:href="link"></a> </div>
v-bind:属性
的缩写形式为:属性
v-model【双向数据绑定】
- 双向数据绑定即不仅数据可以改变模型,模型也可以改变数据。
- 常用于表单操作,换个方式解释就是
:value
用于单项的从数据绑定到表单值,而v-model
则双向地连接数据和表单值。
<!DOCTYPE> <html lang="en"> <head> <mate charset="UTF-8"></mate> <title></title> <script src="vue.js"></script> </head> <body> <div id="root"> <input v-model="inputValue" /> <div>{{inputValue}}</div> </div> <script> var vm = new Vue({ el:"#root", data:{ inputValue:"" } }); </script> </body> </html>
v-if【存在或删除】
- 当v-if绑定的数据值为false时,对应的Dom将被移除,否则将存在。
<div v-if="exist"></div>
v-show【显示或隐藏】
- 当v-show绑定的数据值为false时,对应的Dom将被隐藏,即
display:none
,否则正常显示。
<div v-show="show"></div>
- 当v-show绑定的数据值为false时,对应的Dom将被隐藏,即
v-for【循环】
- v-for循环的语法比较特殊:
item of list
,其中list
为数组对象,而item
为遍历的每一项数据。如果需要迭代的下标,则可变形为:(item, index) of list
,index
为下标值。
<!DOCTYPE> <html lang="en"> <head> <mate charset="UTF-8"></mate> <title></title> <script src="vue.js"></script> </head> <body> <div id="root"> <ul> <li v-for="item of list">{{item}}</li> </ul> </div> <script> var vm = new Vue({ el:"#root", data:{ list:[1, 2, 3] } }); </script> </body> </html>
- v-for循环的语法比较特殊:
3.计算属性和侦听器
3.1计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。 对于任何复杂逻辑,你都应当使用计算属性。
Demo
<!DOCTYPE>
<html lang="en">
<head>
<mate charset="UTF-8"></mate>
<title></title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
<script>
var vm = new Vue({
el:"#root",
data:{
message:'Hello'
},
computed:{
//计算属性的 getter
reversedMessage:function(){
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
});
</script>
</body>
</html>
计算属性和普通的属性一样当计算的值发生变化时,计算属性会重新调用其getter方法产生新的值并将变化传递到试图。另外,计算属性与普通属性的不同点在于计算属性默认是只读的,所以当计算属性没有提供setter方法时其变化只能来自于其依赖的变量值放生改变。
计算属性的setter
计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :
// ... computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } // ...
3.2侦听器
Vue 提供了另外一种方式来观察和响应 Vue 实例上的数据变动:侦听属性。
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
Demo
<!DOCTYPE>
<html lang="en">
<head>
<mate charset="UTF-8"></mate>
<title></title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<input v-model="inputValue" />
<div>{{count}}</div>
</div>
<script>
var vm = new Vue({
el:"#root",
data:{
inputValue:"",
count:0
},
watch:{
inputValue:function(){
this.count ++;
}
}
});
</script>
</body>
</html>
组件
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。 通常一个应用会以一棵嵌套的组件树的形式来组织:
在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。在 Vue 中注册组件很简单:
// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
template: '<li>这是个待办项</li>'
})
现在你可以用它构建另一个组件模板:
<ol>
<!-- 创建一个 todo-item 组件的实例 -->
<todo-item></todo-item>
</ol>
组件的挂载点、模板
在上面的例子中,template为组件的模板,即组件的视图模型。如果说组件就是Vue实例,那么当我们使用new Vue
来创建组件时,为什么不需要指定模板(template)内容呢?其实,在我们之前的Demo中,创建Vue实例都会使用el
属性来指定Vue实例对应的试图部分,我们称这个试图为挂载点,如果挂载点本身不为空,那么该Vue实例的模板(template)则为其挂载点的内容。下面我们试一下用模板创建一个Vue实例。
Demo
<!DOCTYPE>
<html lang="en">
<head>
<mate charset="UTF-8"></mate>
<title></title>
<script src="vue.js"></script>
</head>
<body>
<div id="root"></div>
<script>
var vm = new Vue({
el:"#root",
template:'<div>模板内容</div>'
});
</script>
</body>
</html>
组件间的数据传递
这个话题比较抽象,我们以一个例子来引入:
<!DOCTYPE>
<html lang="en">
<head>
<mate charset="UTF-8"></mate>
<title></title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<ul>
<todo-item v-for="(item, index) of list"></todo-item>
</ul>
</div>
<script>
var itemComponent = {
template:'<li></li>'
};
var vm = new Vue({
el:"#root",
data:{
list:['AAA', 'BBB', 'CCC']
},
//局部组件
components:{
"todo-item":itemComponent
}
});
</script>
</body>
</html>
当我们运行个Demo时,会看到三个文本内容为空的li。如果我们要把vm
的list
数组元素绑定到子组件todo-item
上,可能会想到将todo-item
的模板改成<li>{{item}}</li>
,但是这样修改之后运行会出现异常:
这是因为todo-item
组件(Vue实例)本身没有item
属性。但是要怎么使用到父组件的数据呢?反过来,子组件怎么向父组件传递数据呢?接下来将分开经行讲解。
1.父组件向子组件传递数据(通过 Prop 向子组件传递数据)
Vue中的props
配置项可以用来在组件上注册自定义属性,在上面的例子中,我们在<todo-item>
中自定义一个content
属性,并给绑定为list
的遍历内容item
,同理自定义index
绑定list
的遍历下标index
,此时,<todo-item>
变为:
<todo-item
v-for="(item, index) of list"
:content="item"
:index="index"
></todo-item>
这时候的content
和index
是合法且确实就是list
遍历的值和下表,因为这满足Vue的规定。那么子组件本身怎么使用这两个变量呢,那就是要使用props
配置项了。只要在todo-item
中声明props
并在其中注册content
和index
这两个属性,那么就可以在todo-item
内使用这两个来自父组件的变量了。
Demo
<!DOCTYPE>
<html lang="en">
<head>
<mate charset="UTF-8"></mate>
<title></title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<ul>
<todo-item
v-for="(item, index) of list"
:content="item"
:index="index"
></todo-item>
</ul>
</div>
<script>
var itemComponent = {
props:["content", "index"],
template:'<li>{{content}}</li>'
};
var vm = new Vue({
el:"#root",
data:{
list:['AAA', 'BBB', 'CCC']
},
//局部组件
components:{
"todo-item":itemComponent
}
});
</script>
</body>
</html>
2.子组件向父组件传递数据(通过事件向父级组件发送消息)
在上面的例子中再假设一个场景,当我们点击todo-item
中的一个li
时,要删除其对应list
中的子项。要实现这样的操作,只要在子组件的点击事件中对list进行对应项的删除就可以了。但是,子组件并不能直接操作到list,因为list是来自父组件的数据。这个时候就需要使用到一种类似于发布订阅模式的操作使子组件传递删除信号给父组件,父组件在监听到这个信号之后进行对list的删除处理。
这里涉及到两步操作,第一是子组件发布消息,我们先监听子组件的click事件,并在回调函数中发送消息。将todo-item
的模板改为:<li @click="handleClick()">{{content}}</li>
,那么这就需要在todo-list
中声明handleClick
方法:
var itemComponent = {
props:["content", "index"],
template:'<li @click='handleClick()'>{{content}}</li>',
methods:{
handleClick:function(){
this.$emit("delete", this.index);
}
}
};
其中$emit
是Vue提供的方法,用来触发当前实例上的事件,附加参数都会传给监听器回调。 此时只需要在父组件中捕获这个delete
自定义事件即可。
Demo
<!DOCTYPE>
<html lang="en">
<head>
<mate charset="UTF-8"></mate>
<title></title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<ul>
<todo-item
v-for="(item, index) of list"
:content="item"
:index="index"
@delete="handleDelete"
></todo-item>
</ul>
</div>
<script>
var itemComponent = {
props:["content", "index"],
template:'<li @click="handleClick()">{{content}}</li>',
methods:{
handleClick:function(){
this.$emit("delete", this.index);
}
}
};
var vm = new Vue({
el:"#root",
data:{
list:['AAA', 'BBB', 'CCC']
},
//局部组件
components:{
"todo-item":itemComponent
},
methods:{
handleDelete:function(index){
this.list.splice(index, 1);
}
}
});
</script>
</body>
</html>
- Vue关于组件的部分还有跟多深入的内容,将在以后的文章中讲解。