Vue.js 基础指南
引用
vue.js脚本地址如下:
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/vue"></script>
基础
-
数据与方法
当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时 data 中存在的属性才是响应式的。也就是说如果你后添加一个新的属性,那么对这个属性的改动将不会触发任何视图的更新。 -
申明式渲染
{{ ... }}
<div id="app">{{ message }}</div> ↓ ↓ ↓ <div id="app" v-text="message"></div> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' //在data内定义变量 } ↓ ↓ ↓ }); return { message: 'Hello Vue!' }
不使用return包裹的数据会在项目的全局可见,会造成变量污染;使用==return包裹后数据中变量只在当前组件中生效==,不会影响其他组件。
-
指令
v-bind: [--缩写--]--> :
v-bind:title="message"
-
条件(用于控制切换一个元素是否显示)
v-if="..."
v-else-if
v-else
/* 必须紧跟在带 v-if 元素之后 */切换多个元素: 将<template> 当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。
<template v-if="ok"> <p>Paragraph 1</p> <p>Paragraph 2</p> </template>
v-show=""
v-if 是“真正”的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。-->只是简单地切换元素的 CSS 属性 display。 -
循环
v-for = "(item, index) in items"
/* 不支持 <template> 元素,也不支持 v-else
当 v-if 与 v-for 一起使用时,==v-for 具有比 v-if 更高的优先级==。
支持一个可选的第二个参数为当前项的索引。*/- 数组循环:
<ul id="example-1"> <li v-for="item in items"> {{ item.message }} </li> </ul> var example1 = new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
- 对象循环:
<div v-for="(value, key, index) in object"> {{ key }}: {{ value }} </div>
-
双向绑定 (实现表单输入和应用状态之间的双向绑定)
v-model
<input v-model="message"> data: { message: 'Hello Vue!' }
- 修饰符
-
.lazy 在“change”时而非“input”时更新
<input v-model.lazy="msg" >
-
.number 将用户的输入值转为数值类型
-
.trim 自动过滤用户输入的首尾空白字符
-
(!)提示
-
v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
-
如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,推荐提供一个值为空的禁用选项。
- 修饰符
-
数组更新检测
++变异方法++
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
会改变被这些方法调用的原始数组,也会触发视图更新。++非变异方法++
filter(), concat(), slice()
不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组。-
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
- 用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
- 修改数组的长度时,例如:vm.items.length = newLength
-
Vue 不能检测对象属性的添加或删除:
var vm = new Vue({ data: { a: 1, userProfile: { name: 'Anika' } } }) // `vm.a` 现在是响应式的 vm.b = 2 // `vm.b`不是响应式的 Vue.set(vm.userProfile, 'age', 27) ↓ ↓ ↓ vm.$set(this.userProfile, 'age', 27)
Vue 不能动态添加根级别的响应式属性。但是可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性
-
-
计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于++简单运算++的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example"> {{ message.split('').reverse().join('') }} </div>
如果要重复多次使用这个运算,应适当适应计算属性:
<div id="example"> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } } })
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
-
setter
计算属性默认只有 getter ,不过在需要时也可以提供一个 setter :
(这样可以省去watch侦听)computed: { reversedMessage: { // getter get: function () { return this.message.split('').reverse().join('') }, // setter set: function (newValue) { this.message = newValue.split('').reverse().join(''); //调用vm.reversedMessage("haha"),message也会改变成"ahah" } } }
-
-
侦听属性 watch
当需要在数据变化时执行异步或开销较大的操作时,用watch侦听变化是最有用的。
watch: { // 如果 `message` 发生改变,这个函数就会运行 message: function () { ... } },
-
显示过滤/排序结果
.filter()
<li v-for="n in evenNumbers">{{ n }}</li> data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(function (number) { return number % 2 === 0 }) } }
-
Class & Style 绑定
-
模板语法
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"> //当classname有其他符号时需要使用引号包裹 </div>
or 使用计算属性:
<div v-bind:class="classObject"></div> data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } }
or 使用数组:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> ↓ ↓ ↓ //三元 转 对象形式 <div v-bind:class="[{ active: isActive }, errorClass]"></div> data: { activeClass: 'active', errorClass: 'text-danger' }
-
组件
在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。
Vue.component(tagName, options)
注意确保在初始化根实例之前注册组件:
// 定义名为 myitem 的新组件
Vue.component('myitem', {
// myitem 组件现在接受一个
// "prop",类似于一个自定义特性。
props: ['attributeA','attributeB'], // 在 JavaScript 中使用 camelCase
template: '<li>{{ attributeA.text }} _ {{ attributeB }}</li>'
});
//Prop 验证
props:{
propA: Number,
propB: [String, Number],
propC: {
type: Number,
required: true,
default: 100 //默认值
},
// 自定义验证函数
propD: {
validator: function (value) {
return value > 10
}
}
}
var app = new Vue({
el: '#app',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其他什么人吃的东西' }
]
}
});
模板使用:
<myitem v-for="item in groceryList" :attribute-a="item" :attribute-b="item.id" :key="item.id"></myitem>
↓ ↓ ↓ ↓在HTML中使用 kebab-case↓
<li is="myitem" v-for="item in groceryList" :attribute-a="item" :attribute-b="item.id" :key="item.id"></myitem>
//html上可以直接添加非 Prop 属性
↓ ↓ ↓
<myitem some-attr="xxx"></myitem>
注意这里的 is="myitem" 属性。
这种做法在使用 DOM 模板时是十分必要的,因为在 <ul> 元素内只有 <li> 元素会被看作有效内容。
这样做实现的效果与 <myitem> 相同,但是可以避开一些潜在的浏览器解析错误。
构造 Vue 实例时传入的各种选项大多数都可以在组件里使用。只有一个例外:++data 必须是函数++。
-
组件组合
父子组件的关系:++prop 向下传递,事件向上传递++。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。
-
父组件控制子组件
++Prop 单向绑定++:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意间修改了父组件的状态。
在两种情况下,我们很容易忍不住想去修改 prop 中数据:
-
Prop 作为初始值传入后,子组件想把它当作局部数据来用;
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } } //定义一个局部变量,并用 prop 的值初始化它
-
Prop 作为原始数据传入,由子组件处理成其它数据输出。
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } } //定义一个计算属性,处理 prop 的值并返回
** 注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
-
-
子组件向父组件通信
- 使用 $on(eventName) 监听事件
- 使用 $emit(eventName) 触发事件
父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件,但不能用 $on 监听子组件释放的事件。
如:
<div id="counter-event-example"> <p>{{ total }}</p> <!-- increment事件触发incrementTotal方法 (3) --> ↓ ↓ ↓ <button-counter v-on:increment="incrementTotal"></button-counter> <button-counter v-on:increment="incrementTotal"></button-counter> </div>
Vue.component('button-counter', { //click事件触发incrementCounter方法 (1) ↓ ↓ ↓ template: '<button v-on:click="incrementCounter">{{ counter }}</button>', data: function () { return { counter: 0 } }, methods: { incrementCounter: function () { this.counter += 1 this.$emit('increment') //触发increment事件 (2) } }, }) new Vue({ el: '#counter-event-example', data: { total: 0 }, methods: { incrementTotal: function () { this.total += 1 //total+1 (4) } } })
-
事件处理
-
监听事件
v-on: [--缩写--]--> @
<div id="example-3"> <button v-on:click="say('hi', $event)">Say hi</button> //内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法 </div> new Vue({ el: '#example-3', methods: { say: function (message) { if (event) event.preventDefault() alert(message) } } })
-
事件修饰符
修饰符 对应功能 demo - .stop event.stopPropagation() v-on:click.stop="doThis"
阻止单击事件继续传播 .prevent event.preventDefault() v-on: submit.prevent="onSubmit"
提交事件不再重载页面 .capture 事件捕获模式 v-on:click.capture="doThis"
元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 .self 当前元素自身触发函数 v-on:click.self="doThat"
只当在 event.target
是当前元素自身时触发处理函数,不是从内部元素触发的.once 事件只触发一次 - - .passive 事件的默认行为立即触发 v-on: scroll.passive="onScroll"
滚动事件的默认行为 (即滚动行为) 将会立即触发 .keyCode 监听键盘事件 v-on:keyup.enter="submit"
回车时调用 vm.submit()
使用修饰符时,顺序很重要,如:
用@click.prevent.self
会阻止所有的点击,而@click.self.prevent
只会阻止对元素自身的点击。不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。
<!-- 修饰符可以串联 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --> <form v-on:submit.prevent></form>