在模板中绑定表达式是非常便利的,但是它们实际上只用于简单的操作。模板是为了描述视图的结构。在模板中放入太多的逻辑会让模板过重且难以维护。这就是为什么 Vue.js 将绑定表达式限制为一个表达式。如果需要多于一个表达式的逻辑,应当使用计算属性。

基础例子

<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
这里我们声明了一个计算属性 b。我们提供的函数将用作属性 vm.b的 getter。

console.log(vm.b) // -> 2
vm.a = 2
console.log(vm.b) // -> 3
你可以打开浏览器的控制台,修改 vm。vm.b 的值始终取决于 vm.a 的值。

你可以像绑定普通属性一样在模板中绑定计算属性。Vue 知道 vm.b 依赖于 vm.a,因此当 vm.a 发生改变时,依赖于 vm.b 的绑定也会更新。而且最妙的是我们是声明式地创建这种依赖关系:计算属性的 getter 是干净无副作用的,因此也是易于测试和理解的。

计算属性 vs. $watch

Vue.js 提供了一个方法 $watch,它用于观察 Vue 实例上的数据变动。当一些数据需要根据其它数据变化时, $watch 很诱人 —— 特别是如果你来自 AngularJS。不过,通常更好的办法是使用计算属性而不是一个命令式的 $watch 回调。考虑下面例子:

<div id=”demo”>{{fullName}}</div>
var vm = new Vue({
el: ‘#demo’,
data: {
firstName: ‘Foo’,
lastName: ‘Bar’,
fullName: ‘Foo Bar’
}
})

vm.$watch(‘firstName’, function (val) {
this.fullName = val + ‘ ‘ + this.lastName
})

vm.$watch(‘lastName’, function (val) {
this.fullName = this.firstName + ‘ ‘ + val
})
上面代码是命令式的重复的。跟计算属性对比:

var vm = new Vue({
el: ‘#demo’,
data: {
firstName: ‘Foo’,
lastName: ‘Bar’
},
computed: {
fullName: function () {
return this.firstName + ‘ ‘ + this.lastName
}
}
})
这样更好,不是吗?

计算 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]
}
}
}
// …
现在在调用 vm.fullName = ‘John Doe’ 时,setter 会被调用,vm.firstName 和 vm.lastName 也会有相应更新。

数据绑定语法

Vue.js 的模板是基于 DOM 实现的。这意味着所有的 Vue.js 模板都是可解析的有效的 HTML,且通过一些特殊的特性做了增强。Vue 模板因而从根本上不同于基于字符串的模板,请记住这点。

插值

文本

数据绑定最基础的形式是文本插值,使用 “Mustache” 语法(双大括号):

<span>Message: {{ msg }}</span>
Mustache 标签会被相应数据对象的 msg 属性的值替换。每当这个属性变化时它也会更新。

你也可以只处理单次插值,今后的数据变化就不会再引起插值更新了:

<span>This will never change: {{* msg }}</span>
原始的 HTML

双 Mustache 标签将数据解析为纯文本而不是 HTML。为了输出真的 HTML 字符串,需要用三 Mustache 标签:

<div>{{{ raw_html }}}</div>
内容以 HTML 字符串插入——数据绑定将被忽略。如果需要复用模板片断,应当使用 partials。

在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。记住,只对可信内容使用 HTML 插值,永不用于用户提交的内容。

Read More →

构造器

每个 Vue.js 应用的起步都是通过构造函数 Vue 创建一个 Vue 的根实例:

var vm = new Vue({
// 选项
})
一个 Vue 实例其实正是一个 MVVM 模式中所描述的 ViewModel – 因此在文档中经常会使用 vm 这个变量名。

在实例化 Vue 时,需要传入一个选项对象,它可以包含数据、模板、挂载元素、方法、生命周期钩子等选项。全部的选项可以在 API 文档中查看。

可以扩展 Vue 构造器,从而用预定义选项创建可复用的组件构造器:

var MyComponent = Vue.extend({
// 扩展选项
})

// 所有的 `MyComponent` 实例都将以预定义的扩展选项被创建
var myComponentInstance = new MyComponent()
尽管可以命令式地创建扩展实例,不过在多数情况下将组件构造器注册为一个自定义元素,然后声明式地用在模板中。我们将在后面详细说明组件系统。现在你只需知道所有的 Vue.js 组件其实都是被扩展的 Vue 实例。

属性与方法

每个 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

Read More →

Vue.js(读音 /vju?/, 类似于 view)是一个构建数据驱动的 web 界面的库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

Vue.js 自身不是一个全能框架——它只聚焦于视图层。因此它非常容易学习,非常容易与其它库或已有项目整合。另一方面,在与相关工具和支持库一起使用时,Vue.js 也能完美地驱动复杂的单页应用。

如果你是有经验的前端开发者,想知道 Vue.js 与其它库/框架的区别,查看对比其它框架;如果你对使用 Vue.js 开发大型应用更感兴趣,查看构建大型应用。

响应的数据绑定

Vue.js 的核心是一个响应的数据绑定系统,它让数据与 DOM 保持同步非常简单。在使用 jQuery 手工操作 DOM 时,我们的代码常常是命令式的、重复的与易错的。Vue.js 拥抱数据驱动的视图概念。通俗地讲,它意味着我们在普通 HTML 模板中使用特殊的语法将 DOM “绑定”到底层数据。一旦创建了绑定,DOM 将与数据保持同步。每当修改了数据,DOM 便相应地更新。这样我们应用中的逻辑就几乎都是直接修改数据了,不必与 DOM 更新搅在一起。这让我们的代码更容易撰写、理解与维护。

MVVM

可能是最简单的例子:

<!– 这是我们的 View –>
<div id=”example-1″>
Hello {{ name }}!
</div>
// 这是我们的 Model
var exampleData = {
name: ‘Vue.js’
}

// 创建一个 Vue 实例或 “ViewModel”
// 它连接 View 与 Model
var exampleVM = new Vue({
el: ‘#example-1’,
data: exampleData
})
结果:
Hello Vue.js!
看起来这跟单单渲染一个模板非常类似,但是 Vue.js 在背后做了大量工作。并且 DOM 会自动响应数据的变化。我们如何知道?打开你的浏览器的控制台,修改 exampleData.name,你将看到上例相应地更新。

注意我们不需要撰写任何 DOM 操作代码:被绑定增强的 HTML 模板是底层数据状态的声明式的映射,数据不过是普通 JavaScript 对象。我们的视图完全由数据驱动。

让我们来看第二个例子:

<div id=”example-2″>
<p v-if=”greeting”>Hello!</p>
</div>
var exampleVM2 = new Vue({
el: ‘#example-2’,
data: {
greeting: true
}
})
Hello!
这里我们遇到新东西。你看到的 v-if 特性被称为指令。指令带有前缀 v-,以指示它们是 Vue.js 提供的特殊特性。并且如你所想象的,它们会对绑定的目标元素添加响应式的特殊行为。继续在控制台设置 exampleVM2.greeting 为 false,你会发现 “Hello!” 消失了。

第二个例子演示了我们不仅可以绑定 DOM 文本到数据,也可以绑定 DOM 结构 到数据。而且,Vue.js 也提供一个强大的过渡效果系统,可以在 Vue 插入/删除元素时自动应用过渡效果。

也有一些其它指令,每个都有特殊的功能。例如 v-for 指令用于显示数组元素,v-bind 指令用于绑定 HTML 特性。我们将在后面详细讨论全部的数据绑定语法。

组件系统

组件系统是 Vue.js 另一个重要概念,因为它提供了一种抽象,让我们可以用独立可复用的小组件来构建大型应用。如果我们考虑到这点,几乎任意类型的应用的界面都可以抽象为一个组件树:

Component Tree

实际上,一个典型的用 Vue.js 构建的大型应用将形成一个组件树。在后面的教程中我们将详述组件,不过这里有一个假想的例子,看看使用了组件的应用模板是什么样的:

<div id=”app”>
<app-nav></app-nav>
<app-view>
<app-sidebar></app-sidebar>
<app-content></app-content>
</app-view>
</div>
你可能已经注意到 Vue.js 组件非常类似于自定义元素——它是 Web 组件规范的一部分。实际上 Vue.js 的组件语法参考了该规范。例如 Vue 组件实现了 Slot API 与 is 特性。但是,有几个关键的不同:

Web 组件规范仍然远未完成,并且没有浏览器实现。相比之下,Vue.js 组件不需要任何补丁,并且在所有支持的浏览器(IE9 及更高版本)之下表现一致。必要时,Vue.js 组件也可以放在原生自定义元素之内。

Vue.js 组件提供了原生自定义元素所不具备的一些重要功能,比如组件间的数据流,自定义事件系统,以及动态的、带特效的组件替换。

组件系统是用 Vue.js 构建大型应用的基础。另外,Vue.js 生态系统也提供了高级工具与多种支持库,它们和 Vue.js 一起构成了一个更加“框架”性的系统。

我们以 Vue 数据绑定的快速导览开始。如果你对高级概述更感兴趣,可查看这篇博文。

尝试 Vue.js 最简单的方法是使用 JSFiddle Hello World 例子。在浏览器新标签页中打开它,跟着我们查看一些基础示例。如果你喜欢用包管理器下载/安装,查看安装教程。

Hello World

<div id=”app”>
{{ message }}
</div>
new Vue({
el: ‘#app’,
data: {
message: ‘Hello Vue.js!’
}
})
Hello Vue.js!
双向绑定

<div id=”app”>
<p>{{ message }}</p>
<input v-model=”message”>
</div>
new Vue({
el: ‘#app’,
data: {
message: ‘Hello Vue.js!’
}
})
Hello Vue.js!
Hello Vue.js!
渲染列表

<div id=”app”>
<ul>
<li v-for=”todo in todos”>
{{ todo.text }}
</li>
</ul>
</div>
new Vue({
el: ‘#app’,
data: {
todos: [
{ text: ‘Learn JavaScript’ },
{ text: ‘Learn Vue.js’ },
{ text: ‘Build Something Awesome’ }
]
}
})
Learn JavaScript
Learn Vue.js
Build Something Awesome
处理用户输入

<div id=”app”>
<p>{{ message }}</p>
<button v-on:click=”reverseMessage”>Reverse Message</button>
</div>
new Vue({
el: ‘#app’,
data: {
message: ‘Hello Vue.js!’
},
methods: {
reverseMessage: function () {
this.message = this.message.split(”).reverse().join(”)
}
}
})
Hello Vue.js!

Reverse Message
综合

<div id=”app”>
<input v-model=”newTodo” v-on:keyup.enter=”addTodo”>
<ul>
<li v-for=”todo in todos”>
<span>{{ todo.text }}</span>
<button v-on:click=”removeTodo($index)”>X</button>
</li>
</ul>
</div>
new Vue({
el: ‘#app’,
data: {
newTodo: ”,
todos: [
{ text: ‘Add some todos’ }
]
},
methods: {
addTodo: function () {
var text = this.newTodo.trim()
if (text) {
this.todos.push({ text: text })
this.newTodo = ”
}
},
removeTodo: function (index) {
this.todos.splice(index, 1)
}
}
})