Vue
Vue.js 是一套构建用户界面的渐进式框架。
对比
Quick Start
Hello World
- install
Install Vue first.
<div id="app">
\{\{ message \}\}
</div>
(vue 的语法和 jekyll 冲突。实际请删除其中的转义符号\
,后面亦如是。)
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
</script>
效果如下:
Another way
<div class="well" id="vue-demo-2">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
<script>
new Vue({
el: '#vue-demo-2',
data: {
message: '页面加载于 ' + new Date().toLocaleString()
}
})
</script>
其中 v-bind
属性被称为指令。指令带有前缀 v-
,以表示它们是 Vue 提供的特殊属性。
Condition
<div class="well" id="vue-demo-3">
<p v-if="seen">现在你看到我了</p>
</div>
<script>
new Vue({
el: '#vue-demo-3',
data: {
seen: true
}
})
</script>
现在你看到我了
Loop
<div class="well" id="vue-demo-4">
<li v-for="todo in todos">
\{\{ todo.text \}\}
</li>
</div>
<script>
new Vue({
el: '#vue-demo-4',
data: {
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
</script>
Handle User Input
为了让用户和你的应用进行互动,我们可以用 v-on
指令绑定一个事件监听器,通过它调用我们 Vue 实例中定义的方法:
<div id="vue-demo-5">
<p></p>
<button class="btn btn-info" v-on:click="reverseMessage">逆转消息</button>
</div>
new Vue({
el: '#vue-demo-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
Componentized application build
几乎任意类型的应用界面都可以抽象为一个组件树:
<div id="app-7">
<ol>
<!--
现在我们为每个 todo-item 提供 todo 对象
todo 对象是变量,即其内容可以是动态的。
我们也需要为每个组件提供一个“key”,晚些时候我们会做个解释。
-->
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id">
</todo-item>
</ol>
</div>
//定义一个组件。且此组件可以接受一个属性
Vue.component('todo-item', {
// todo-item 组件现在接受一个
// "prop",类似于一个自定义属性
// 这个属性名为 todo。
props: ['todo'],
template: '<li></li>'
})
new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其他什么人吃的东西' }
]
}
})
Vue Instance
(这文档写的很符合我的审美,直接抄了。。。XD)
Create Vue
每个 Vue 应用都是从创建一个实例开始:
var vm = new Vue({
// 选项
})
虽未完全遵守 MMVM 模型,但二者渊源颇深。
Data & Method
当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data
对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 他们引用相同的对象!
vm.a === data.a // => true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2
// ... and vice-versa
data.a = 3
vm.a // => 3
值得注意的是只有当实例被创建时 data
中存在的属性是响应式的。也就是说如果你添加一个新的属性
vm.b = 'hi'
那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:
data: {
newTodoText: '',
visitCount: 0,
hideCompletedTodos: false,
todos: [],
error: null
}
除了 data
属性, 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 (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
Life Cycle
每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新 DOM 等。
同时在这个过程中也会运行一些叫做生命周期钩子的函数,给予用户机会在一些特定的场景下添加他们自己的代码。
(这个和 jQuery 插件预留的钩子就很类似,是一种很利于拓展的设计。)
如 created 钩子:
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
// => "a is: 1"
在实例生命周期的不同场景下调用,如 mounted、updated、destroyed。 钩子的 this 指向调用它的 Vue 实例。
不要选项属性或回调上使用箭头函数,比如
created: () => console.log(this.a)
或 vm.$watch('a', newValue => this.myMethod())
。
因为箭头函数是和父级上下文绑定在一起的,this 不会是如你做预期的 Vue 实例,且 this.a 或 this.myMethod 也会是未定义的。
上图
Template Grammar
所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。
插值
- 文本
最常见的形式就是使用 Mustache 语法的文本插值
<span>Message: \{\{ msg \}\}</span>
无论何时,绑定的数据对象上 msg
属性发生了改变,插值处的内容都会更新。
通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。
<span v-once>这个将不会改变: \{\{ msg \}\}</span>
- 原始 HTML
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML ,你需要使用 v-html 指令:
<div v-html="rawHtml"></div>
这个 div 的内容将会被替换成为属性值 rawHtml,直接作为 HTML(会忽略解析属性值中的数据绑定)。
Attention
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。 请只对可信内容使用 HTML 插值,绝不要对用户提供的内容插值。
- 特性
mustache 语法不能作用在 HTML 特性上,遇到这种情况应该使用 v-bind 指令:
<div v-bind:id="dynamicId"></div>
这同样适用于布尔类特性,如果求值结果是 false 的值,则该特性将会被删除:
<button v-bind:disabled="isButtonDisabled">Button</button>
- 使用 JS 表达式
对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
\{\{ number + 1 \}\}
\{\{ ok ? 'YES' : 'NO' \}\}
\{\{ message.split('').reverse().join('') \}\}
<div v-bind:id="'list-' + id"></div>
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
<!-- 这是语句,不是表达式 -->
\{\{ var a = 1 \}\}
<!-- 流控制也不会生效,请使用三元表达式 -->
\{\{ if (ok) { return message } \}\}
指令
指令 (Directives) 是带有 v-
前缀的特殊属性。指令属性的值预期是单个 JavaScript 表达式 (v-for
是例外情况,稍后我们再讨论)。
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。回顾我们在介绍中看到的例子:
<p v-if="seen">现在你看到我了</p>
这里,v-if
指令将根据表达式 seen 的值的真假来插入/移除 <p>
元素。
- 参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind
指令可以用于响应式地更新 HTML 属性:
<a v-bind:href="url"></a>
在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。
另一个例子是 v-on
指令,它用于监听 DOM 事件:
<a v-on:click="doSomething">
在这里参数是监听的事件名。我们也会更详细地讨论事件处理。
- 修饰符
修饰符 (Modifiers) 是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
例如,.prevent
修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
<form v-on:submit.prevent="onSubmit"></form>
之后当我们更深入地了解 v-on 与 v-model时,会看到更多修饰符的使用。
缩写
v-
前缀作为一种视觉提示,用来识别模板中 Vue 特定的特性。
当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v-
前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。
同时,在构建由 Vue.js 管理所有模板的 单页面应用程序 (SPA - single page application) 时,v-
前缀也变得没那么重要了。
因此,Vue.js 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写:
- v-bind
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
- v-on
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>
Calc-Attr & Watcher
Calc-Attr
- Base
模板内的表达式是非常便利的,但是它们实际上只用于简单的运算。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example">
\{\{ message.split('').reverse().join('') \}\}
</div>
在这个地方,模板不再简单和清晰。你必须看一段时间才能意识到,这里是想要显示变量 message
的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。
这就是对于任何复杂逻辑,你都应当使用计算属性的原因。
- Cache VS Method()
计算属性只有在它的相关依赖发生改变时才会重新求值。
相比之下,每当触发重新渲染时,方法的调用方式将总是再次执行函数。
- Calc-attr VS Observer-attr
然而,通常更好的想法是使用计算属性而不是命令式的 watch
回调。细想一下这个例子:
- Calc-attr Setter
Watcher
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的 watcher。
这是为什么 Vue 通过 watch
选项提供一个更通用的方法,来响应数据的变化。当你想要在数据变化响应时,执行异步操作或开销较大的操作,这是很有用的。
Class & Style
数据绑定一个常见需求是操作元素的 class 列表和它的内联样式。因为它们都是属性 ,我们可以用 v-bind
处理它们:只需要计算出表达式最终的字符串。
不过,字符串拼接麻烦又易错。因此,在 v-bind 用于 class 和 style 时,Vue.js 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。
Class
一、对象语法
我们可以传给 v-bind:class
一个对象,以动态地切换 class:
<div v-bind:class="{ active: isActive }"></div>
上面的语法表示 class active
的更新将取决于数据属性 isActive 是否为真值。
你可以在对象中传入更多属性用来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class 属性共存。如下模板:
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
二、数组语法
三、组件
Jump…
Style
一、对象语法
二、数组语法
三、自动添加前缀
四、多重值
从 2.3.0 起你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
这会渲染数组中最后一个被浏览器支持的值。在这个例子中,如果浏览器支持不带浏览器前缀的 flexbox,那么渲染结果会是 display: flex
。
Condition Render
v-if
- Handlebars
<!-- Handlebars 模板 -->
{`{#if ok}`}
<h1>Yes</h1>
{`{/if}`}
- v-if 实现同样的效果
<h1 v-if="ok">Yes</h1>
- v-else
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
还有 v-else
/v-else-if
和正常的使用方式一致。此处不再赘述。
template
如果我们想渲染一组数据,应该怎么做?
我们可以把一个 <template>
元素当做包装元素,并在上面使用 v-if
。最终的渲染结果不会包含 <template>
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
Key
上述,文本框输入内容。进行切换时,输入框未被重新渲染。
只需如下,添加唯一 key
:
v-show
另一个用于根据条件展示元素的选项是 v-show
指令。用法大致一样:
<h1 v-show="ok">Hello!</h1>
不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
是简单地切换元素的 CSS 属性 display。
List Render
list
object
order/filter
for in range
to-list
Event Handle
Event Listen
事件修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。
尽管我们可以在 methods 中轻松实现这点,但更好的方式是:methods 只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了 事件修饰符。通过由点 .
表示的指令后缀来调用修饰符。
<!-- 阻止单击事件冒泡 -->
<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>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身 (比如不是子元素) 触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 @click.prevent.self
会阻止所有的点击,而 @click.self.prevent
只会阻止元素上的点击。
键值修饰符
在监听键盘事件时,我们经常需要监测常见的键值。Vue 允许为 v-on 在监听键盘事件时添加关键修饰符:
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
可以通过全局 config.keyCodes 对象自定义键值修饰符别名:
// 可以使用 v-on:keyup.f1
Vue.config.keyCodes.f1 = 112
为什么在 HTML 中监听事件?
你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 传统理念。
不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on 有几个好处:
-
扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
-
因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
-
当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。
表单输入绑定
基础用法
你可以用 v-model
指令在表单控件元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
尽管有些神奇,但 v-model 本质上不过是语法糖,它负责监听用户的输入事件以更新数据,并特别处理一些极端的例子。
- 初始值忽略
v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值。因为它会选择 Vue 实例数据来作为具体的值。你应该通过 JavaScript 在组件的 data
选项中声明初始值。
- IME 不监听
对于要求 IME (如中文、日语、韩语等) (IME 意为“输入法”)的语言,你会发现 v-model 不会在 ime 输入中得到更新。如果你也想实现更新,请使用 input 事件。
- text-area
在文本区域插值 <textarea></textarea>
并不会生效,应用 v-model 来代替。
值绑定
对于单选按钮,勾选框及选择列表选项,v-model 绑定的 value 通常是静态字符串 (对于勾选框是逻辑值):
<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">
<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">
<!-- 当选中时,`selected` 为字符串 "abc" -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
但是有时我们想绑定 value 到 Vue 实例的一个动态属性上,这时可以用 v-bind 实现,并且这个属性的值可以不是字符串。
一、复选框
<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b"
>
// 当选中时
vm.toggle === vm.a
// 当没有选中时
vm.toggle === vm.b
二、单选按钮
<input type="radio" v-model="pick" v-bind:value="a">
// 当选中时
vm.pick === vm.a
选择列表的选项
<select v-model="selected">
<!-- 内联对象字面量 -->
<option v-bind:value="{ number: 123 }">123</option>
</select>
// 当选中时
typeof vm.selected // => 'object'
vm.selected.number // => 123
修饰符
- .lazy
在默认情况下,v-model 在 input 事件中同步输入框的值与数据 (除了 上述 IME 部分),但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >
- .number
如果想自动将用户的输入值转为 Number 类型 (如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值:
<input v-model.number="age" type="number">
这通常很有用,因为在 type=”number” 时 HTML 中输入的值也总是会返回字符串类型。
- .trim
如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:
<input v-model.trim="msg">