这章节主要是介绍 Vue 的基础语法,由于官网文档中的语法讲解都是在 HTML 中直接引入 Vue,这让一些刚上来使用 Vue-cli 的初学者很是头痛,因为这两个在写法上还是有一些区别,所以我们要介绍的就是 Vue-cli 中 Vue 的使用方法。
Options / Data
data
Vue 实例的数据对象,Vue 会遍历对象的所有属性,通过 Object.defineProperty 把这些属性全部转换为 getter/setter,但是这些对于用户来说是不可见的,在内部他们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
实例创建后,data 必须声明为一个返回初始数据对象的函数。
export default {
data () {
return {
title: 'Hello Vue'
}
}
}
这里要注意的是,如果你的 data 属性使用了箭头函数,则 this 不会指向这个实例,但是仍然可以将实例作为函数的第一个参数来访问
data: vm => ({ a: vm.myProp })
props
props 可以是数组或对象,用来接收父组件的数据。
// 简单语法
export default {
props: ['title', 'message']
}
// 对象语法,提供校验
export default {
props: {
title: String,
age: {
type: Number,
default: 0,
required: true,
validator: (value) => {
return value >= 0
}
}
}
}
computed
该属性可以理解为计算一个新的属性,并将该属性挂载到 vm (Vue 实例) 上,同样的,如果你为一个计算属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。
计算属性的结果会被缓存,多次访问计算属性会立即返回之前的计算结果,除非依赖的 data 返回的属性变化才会重新计算,如果某个依赖在该实例范围之外,则计算属性不会更新。
export default {
data () {
return {
age: 20
}
},
computed: {
ageDouble: function() {
return this.age * 2
},
agePlus: {
get: function() {
return this.age + 1
},
set: function(value) {
this.age = value + 1
}
},
now: function() {
return Date.now() // 不会更新,因为 Date.now
}
}
}
methods
methods 将被混入到 Vue 实例中,可以通过实例直接访问这些方法,或在指令表达式中使用。但是注意不要使用箭头函数来定义 methods 函数,因为这里的 this 不会指向实例。
export default {
data () {
return {
age: 20
}
},
methods: {
add() {
this.age += 1
}
}
}
watch
watch 的结构还是一个对象,key 是要观察的表达式,value 为对应的回调函数,也可以是方法名,或包含选项的对象。不要用箭头函数来定义 watcher 函数
export default {
data() {
a: 1,
b: 2,
c: 3,
d: 4,
e: {
f: {
g: 5
}
}
},
watch: {
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// 方法名
b: 'someMethod',
// 深度 watcher
c: {
handler: function (val, oldVal) { /* ... */ },
deep: true
},
// 该回调将会在侦听开始之后被立即调用
d: {
handler: function (val, oldVal) { /* ... */ },
immediate: true
},
e: [
function handle1 (val, oldVal) { /* ... */ },
function handle2 (val, oldVal) { /* ... */ }
],
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
}
Directives
v-text
用来渲染和更新元素的 textContent,但是不如 {{}} 灵活。
<span v-text="msg"></span>
<span>{{ msg }}</span>
v-html
用来更新元素的 innerHTML,这里只是按照普通的 HTML 插入,但是如果插入的 HTML 较为复杂的话,应该考虑是否用组件去代替。
在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用
v-html
,永不用在用户提交的内容上。
在单文件组件里,scoped
的样式不会应用在 v-html
内部,因为那部分 HTML 没有被 Vue 的模板编译器处理。如果你希望针对 v-html
的内容设置带作用域的 CSS,你可以替换为 CSS Modules 或用一个额外的全局 <style>
元素手动设置类似 BEM 的作用域策略。
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
v-show
根据条件展示元素,要注意的一点是,v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS 属性 display
。v-show
不支持 <template>
元素,也不支持 v-else
<h1 v-show="ok">Hello!</h1>
v-if / v-else / v-else-if
根据表达式的值的真假条件渲染元素。和 v-show
不同的一点是,切换元素时,条件块内的事件监听器和子组件适当的被销毁和重建,并且v-if
是惰性的,也就是说如果初始渲染时条件为假,则保持什么都不做直到第一次变成真的,才会开始渲染条件块。
由此可见,v-if
有更高的切换开销,v-show
有更高的初始渲染开销。所以要根据实际情况选择合适的指令。
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
当
v-if
和v-for
一起使用时,v-for
具有比v-if
更高的优先级。
v-for
一个数组的 v-for
<div v-for="item in items"></div>
<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, key, index) in object"></div>
v-for
默认行为试着不改变整体,而是替换元素。迫使其重新排序的元素,你需要提供一个 key
的特殊属性:
<div v-for="item in items" :key="item.id">
{{ item.text }}
</div>
这里的 key 可以提升当源数据发生改变时的重绘效率。它默认用“就地复用”策略,如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这里的 key 就可以便于 Vue 跟踪每个节点的身份,从而重用和重新排列现有数据。
Mutation Methods
这里是 Vue 所支持的修改数组的方法,这些方法会修改原数组,所以他们将会触发视图更新。
方法如下:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
Replacing an Array
既然有修改原数组的方法,那么也就有返回新数组的方法,例如 filter()
, concat()
和 slice()
所以当使用这些方法时,可以用新数组替换旧数组。
但是在你替换的时候,Vue 并不会丢弃现有的 DOM 去重新渲染整个列表,而是保留相同的元素,去修改不同的元素。所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
Caveats
由于 JS 的限制,Vue 不能检测以下变动的数组
当你利用索引直接设置一个项时,例如:
vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:
vm.items.length = newLength
问题一可以使用 Vue.set() 来实现
Vue.set(vm.items, indexOfItem, newValue)
问题二可以使用 splice
vm.items.splice(newLength)
v-for with v-if
当它们处于同一节点,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个 v-for
循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
而如果你的目的是有条件地跳过循环的执行,那么可以将 绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。
动态地绑定一个或多个特性,或一个组件 prop 到表达式。 在绑定 class 和 style 特性时,支持其他类型的值,如数组和对象。 通过 v-model 在表单控件或者组件上创建双向绑定,但是要注意的是,v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值,而总是将 Vue 实例的数据作为数据来源,所以应该在 data 中声明初始值。
为了避免每次在 input 事件触发后将输入框中的值和数据进行同步,可以通过 .lazy 修饰符转变为使用 change 事件进行同步: 如果想自动将用户的输入值转变为数值类型,可以给 model 添加 number 修饰符 自动过滤用户输入的首尾空白字符v-if
置于外层元素 (或 )上。如:
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
v-on
基本用法
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter">
<!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter">
<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>
<!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
export default {
methods: {
doThis() {
alert('Hello World')
}
}
}
v-bind
修饰符:
.prop
- 被用于绑定 DOM 属性 (property)。.camel
- (2.1.0+) 将 kebab-case 特性名转换为 camelCase. (从 2.1.0 开始支持).sync
(2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的 v-on
侦听器。
当绑定 prop 时,prop 必须在子组件中声明。
基本用法
<!-- 绑定一个属性 -->
<img v-bind:src="imageSrc">
<!-- 缩写 -->
<img :src="imageSrc">
<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName">
<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]">
<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>
<!-- 绑定一个有属性的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
<!-- 通过 prop 修饰符绑定 DOM 属性 -->
<div v-bind:text-content.prop="text"></div>
<!-- prop 绑定。“prop”必须在 my-component 中声明。-->
<my-component :prop="someThing"></my-component>
<!-- 通过 $props 将父组件的 props 一起传给子组件 -->
<child-component v-bind="$props"></child-component>
<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>
v-model
文本
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
多个复选框,绑定到同一个数组
<div id='example-3'>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
修饰符
.lazy
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >
.number
<input v-model.number="age" type="number">
.trim
<input v-model.trim="msg">