基础
介绍
是什么
- 是一套用于构建用户界面的渐进式框架
声明式渲染
<div id="app">{{ message }}</div> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
vue 实例
var vm = new Vue({ // 选项 })
- 一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。
- 所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象 (一些根实例特有的选项除外)。
数据与方法
// 我们的数据对象 var data = { a: 1 } // 该对象被加入到一个 Vue 实例中 var vm = new Vue({ data: data }) // 获得这个实例上的属性 // 返回源数据中对应的字段 // 改变 vm.a 或者 data.a 都会使视图发生响应 vm.a == data.a // => true
- 注意:只有当实例被创建时 data 中存在的属性才是响应式的。所以之后才使用的属性一开始它为空或不存在,那么仅需要设置一些初始值即可。
- 这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
- 除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。eg: $el、$data、$watch 等。
实例生命周期钩子
- 不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数是和父级上下文绑定在一起的,this 不会是如你所预期的 Vue 实例
模板语法
- Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。也可以不用模板,直接写渲染 (render) 函数,使用可选的 JSX 语法。
文本
- 使用“Mustache”语法 (双大括号) 的文本插值
- 通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。
<span v-once>这个将不会改变: {{ msg }}</span>
原始 HTML(不要使用,仅供了解)
- 注:你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
特性
- Mustache 语法不能作用在 HTML 特性上,遇到这种情况应该使用 v-bind 指令。
- 在布尔特性的情况下,它们的存在即暗示为 true,v-bind 工作起来略有不同,如果 isButtonDisabled 的值是 null、undefined 或 false,则 disabled 特性甚至不会被包含在渲染出来的 <button> 元素中。
<button v-bind:disabled="isButtonDisabled">Button</button>
使用 JavaScript 表达式
- 对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
- 注意:每个绑定都只能包含单个表达式。
<!-- 这是语句,不是表达式 --> {{ var a = 1 }} <!-- 流控制也不会生效,请使用三元表达式 --> {{ if (ok) { return message } }}
- 注意:模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。
参数
- 一些指令能够接收一个“参数”,在指令名称之后以冒号表示。eg:v-bind 与 v-on
修饰符
- 修饰符 (Modifiers) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
<form v-on:submit.prevent="onSubmit">...</form>
缩写
- v-bind (:) 与 v-on (@)
计算属性和侦听器
计算属性
- 在模板中放入太多的逻辑会让模板过重且难以维护,所以,对于任何复杂逻辑,你都应当使用计算属性。
<div id="example"> {{ message.split('').reverse().join('') }} </div>
计算属性缓存 VS 方法(缓存)
- 计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
- 每当触发重新渲染时,调用方法将总会再次执行函数。
计算属性 VS 侦听属性
- 不要滥用 watch,通常更好的做法是使用计算属性而不是命令式的 watch 回调。
计算属性的 setter
- 计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :
// 现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName // 和 vm.lastName 也会相应地被更新。 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] } } } // ...
侦听器
- 当需要在数据变化时执行异步或开销较大的操作时。(比如 watch 选项允许执行异步操作)
class 绑定
- 字符串拼接麻烦且易错,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
对象语法
- 注意:v-bind:class 指令也可以与普通的 class 属性共存
// 对象模式 <div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div> // 放到 data 属性中 <div v-bind:class="classObject"></div> // 绑定一个返回对象的计算属性
数组语法
<div v-bind:class="[activeClass, errorClass]"></div> <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> // 在数组语法中也可使用对象语法 <div v-bind:class="[{ active: isActive }, errorClass]"></div> data: { activeClass: 'active', errorClass: 'text-danger', isActive: true }
用在组件上
- 当在一个自定义组件上使用 class 属性时,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。对于带数据绑定 class 也同样适用。
style 绑定
对象语法
- CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> // 直接绑定一个样式对象更好 <div v-bind:style="styleObject"></div> // 对象语法常常结合返回对象的计算属性使用
数组语法
- v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上
<div v-bind:style="[baseStyles, overridingStyles]"></div>
自动添加前缀
- 当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。
多重值
- 从 2.3.0 起你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
条件渲染
v-if
- 在 <template> 元素上使用 v-if 条件渲染分组
- v-else
- 使用 v-else 指令来表示 v-if 的“else 块”
- v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
- v-else-if
- 类似于 v-else,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。
- 用 key 管理可复用的元素
- Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
- Vue 提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性
v-show
- v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。
v-if VS v-show
-
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
-
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
-
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
-
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
v-if 与 v-for 一起使用
- 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。
列表渲染
用 v-for 把一个数组对应为一组元素
- v-for 指令需要使用 item in items 形式的特殊语法
- 在 v-for 块中,我们拥有对父作用域属性的完全访问权限。v-for 还支持一个可选的第二个参数为当前项的索引。
- 也可以用 of 替代 in 作为分隔符,因为它是最接近 JavaScript 迭代器的语法
一个对象的 v-for
- 用 v-for 通过一个对象的属性来迭代。
<ul id="v-for-object" class="demo"> <li v-for="value in object"> {{ value }} </li> </ul> // 第二个参数为键名,第三个参数为索引 <div v-for="(value, key, index) in object"> {{ index }}: {{ key }}: {{ value }} </div> new Vue({ el: '#v-for-object', data: { object: { firstName: 'John', lastName: 'Doe', age: 30 } } })
- 注意:在遍历对象时,是按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。
key
- 以便 vue 能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的且唯一的 id。使用 v-bind 绑定动态值。
- 注意:① 建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。② 是 Vue 识别节点的一个通用机制,并不与 v-for 特别关联。
数组更新检测
- 变异方法:Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
- 注意:会改变调用这些方法的原始数组
- 替换数组(非变异方法): 不会改变原始数组但总是返回一个新数组,在使用这些方法时可以使用新数组替换旧数组。
- concat()
- slice()
- filter()
example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) })
注意事项
- 由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
- 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:vm.items.length = newLength
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是响应性的 vm.items.length = 2 // 不是响应性的
- 解决
// Vue.set Vue.set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue) // 同 Vue.set vm.$set(vm.items, indexOfItem, newValue) // 第二类问题 vm.items.splice(newLength)
对象更改检测注意事项
- 由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除
var vm = new Vue({ data: { a: 1 } }) // `vm.a` 现在是响应式的 vm.b = 2 // `vm.b` 不是响应式的
- 对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。
var vm = new Vue({ data: { userProfile: { name: 'Anika' } } }) Vue.set(vm.userProfile, 'age', 27) // 全局 Vue.set 的别名 vm.$set(vm.userProfile, 'age', 27)
- 要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。想添加新的响应式属性:
// 不应这样 Object.assign(vm.userProfile, { age: 27, favoriteColor: 'Vue Green' }) // 应该这样 vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green' })
显示过滤/排序结果
- 有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。
<li v-for="n in evenNumbers">{{ n }}</li>
- 在计算属性不适用的情况下 (例如,在嵌套 v-for 循环中) 你可以使用一个 method 方法
<li v-for="n in even(numbers)">{{ n }}</li>
一段取值范围的 v-for
<li v-for="n in even(numbers)">{{ n }}</li>
v-for on a <template>
- 类似于 v-if,你也可以利用带有 v-for 的 <template> 渲染多个元素。
一个组件的 v-for
- 在自定义组件里,你可以像任何普通元素一样用 v-for 。
- 2.2.0+ 的版本里,当在组件中使用 v-for 时,key 现在是必须的。
- 注意:不自动将 item 注入到组件里的原因是,这会使得组件与 v-for 的运作紧密耦合。明确组件数据的来源能够使组件在其他场合重复使用。
- 问题: is="todo-item" 属性?DOM 模板解析说明
事件处理
内联处理器中的方法
- 有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法
事件修饰符
- 在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。为了方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节,,Vue.js 为 v-on 提供了事件修饰符。
- .stop
- .prevent
- .capture
- .self
- .once:不像其它只能对原生的 DOM 事件起作用的修饰符,.once 修饰符还能被用到自定义的组件事件上
- .passive: 滚动事件的默认行为 (即滚动行为) 将会立即触发,尤其能够提升移动端的性能。
- 注意:① 使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。② 不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。
<!-- 阻止单击事件继续传播 --> <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> <!-- 只当在 event.target 是当前元素自身时触发处理函数 --> <!-- 即事件不是从内部元素触发的 --> <div v-on:click.self="doThat">...</div>
按键修饰符
- Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:
<input v-on:keyup.13="submit"> // 常用键的别名 <input v-on:keyup.enter="submit">
- 全部别名 -.enter
- .tab
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
- 可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1` Vue.config.keyCodes.f1 = 112
- 自动匹配按键修饰符 (有问题)
系统修饰键
- 可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器
- .ctrl
- .alt
- .shift
- .meta ( Windows 徽标键 或 command 键 (⌘))
- 注意:请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。
- .exact 修饰符
- 允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 --> <button @click.ctrl="onClick">A</button> <!-- 有且只有 Ctrl 被按下的时候才触发 --> <button @click.ctrl.exact="onCtrlClick">A</button> <!-- 没有任何系统修饰符被按下的时候才触发 --> <button @click.exact="onClick">A</button>
- 鼠标按钮修饰符
- .right
- .left
- .middle
- 注:这些修饰符会限制处理函数仅响应特定的鼠标按钮。
为什么在 HTML 中监听事件
- 所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。还有其他好处:
- 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
- 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
- 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。
表单输入绑定
- v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
基础用法
- 文本 input
- 多行文本 textarea
- 复选框 checkbox
- 单选按钮 radio
- 选择框 select
值绑定
- 复选钮
- 单选按钮
- 选择框的选项
<!-- 当选中时,`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>
- 注意:有时我们可能想把值绑定到 Vue 实例的一个动态属性上,这时可以用 v-bind 实现,并且这个属性的值可以不是字符串。
修饰符
- .lazy:在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:
<!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg" >
- .number:如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符
<input v-model.number="age" type="number">
- .trim:如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符
<input v-model.trim="msg">
在组件上使用 v-model (之后自定义输入组件-组建基础中)
组件基础
基本示例
组件的复用
- 每个组件都会各自独立维护它的 状态。因为你每用一次组件,就会有一个它的新实例被创建
<div id="components-demo"> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> </div>
- data 必须是一个函数: 一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。否则的话可能会影响其他所有示例。底层实现是什么?
组件的组织
- 有两种组件的注册类型:全局注册和局部注册。至此,我们的组件都只是通过 Vue.component 全局注册的:
Vue.component('my-component-name', { // ... options ... })
- 全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
通过 Prop 向子组件传递数据
- 可以使用 v-bind 来动态传递 prop。
单个根元素
- 每个组件必须只有一个根元素
通过事件向父级组件发送消息
-
调用内建的 $emit 方法并传入事件的名字,来向父级组件触发一个事件
-
使用事件抛出一个值
<button v-on:click="$emit('enlarge-text', 0.1)"> Enlarge text </button> <blog-post ... v-on:enlarge-text="postFontSize += $event" ></blog-post>
- 在组件上使用 v-model
<input v-model="searchText"> // 等价于 <input v-bind:value="searchText" v-on:input="searchText = $event.target.value" > // 用在组件上 <custom-input v-bind:value="searchText" v-on:input="searchText = $event" ></custom-input> // 将其 value 特性绑定到一个名叫 value 的 prop 上 // 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出 Vue.component('custom-input', { props: ['value'], template: ` <input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" > ` })
通过插槽分发内容 <slot>
<alert-box> Something bad happened. </alert-box> Vue.component('alert-box', { template: ` <div class="demo-alert-box"> <strong>Error!</strong> <slot></slot> </div> ` })
动态组件
- 通过 Vue 的 <component> 元素加一个特殊的 is 特性来实现
解析 DOM 模板时的注意事项
- 有些 HTML 元素,诸如ul、ol、table和select,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 li、tr 和option,只能出现在其它某些特定的元素内部。
// 这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。 <table> <blog-post-row></blog-post-row> </table> // 利用 is 特性 <table> <tr is="blog-post-row"></tr> </table>
- 注意:需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:
- 字符串 (例如:template: '...')
- 单文件组件 (.vue)
- <script type="text/x-template">