1. vue 中阻止修改 data 中数据
Object.freeze(),这会阻止修改现有的property,也意味着响应系统无法再追踪变化。
var obj = {
foo: 'bar'
}
Object.freeze(obj)
new Vue({
el: '#app',
data: obj
})
2. 绑定动态属性/动态事件
<a v-bind:[attributeName]="url"> ... </a>
<a v-on:[eventName]="doSomething"> ... </a>
attributeName/eventName 会被作为一个 JavaScript 表达式进行动态求值。attributeName值为 “href”,那么这个绑定将等价于 v-bind:href。当 eventName 的值为 “focus” 时,v-on:[eventName] 将等价于 v-on:focus。
3. 计算属性的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]
}
}
}
4. 用 key 管理可复用的元素
注:这里涉及到虚拟 DOM 的 diff 算法
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,input 不会被替换掉——仅仅是替换了它的 placeholder。
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
现在,每次切换时,输入框都将被重新渲染。label 元素仍然会被高效地复用,因为它们没有添加 key attribute。
5. 避免 v-if 和 v-for 用在一起
v-for 优先级比 v-if 高,会造成不必要的渲染开销。
为了过滤一个列表中的项目 (比如 v-for=“user in users” v-if=“user.isActive”)。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。
为了避免渲染本应该被隐藏的列表 (比如 v-for=“user in users” v-if=“shouldShowUsers”)。这种情形下,请将 v-if 移动至容器元素上 (比如 ul、ol、template)。
6. Vue 不能检测数组和对象的变化
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。可以理解为 vue 检测的是数组和对象的地址。
Vue.set(object, propertyName, value)/ this.$set(object, propertyName, value)方法向嵌套对象添加响应式 property。
还可以采用 object = {…object,[propertyName]:value}
数组同理。
7. 事件修饰符
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
8. webpack 的 Vue CLI 注册组件
使用了 webpack ,就可以使用 require.context 只全局注册这些非常通用的基础组件。 示例代码:
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// 其组件目录的相对路径
'./components',
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName)
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 获取和目录深度无关的文件名
fileName
.split('/')
.pop()
.replace(/\.\w+$/, '')
)
)
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
})
9. 将原生事件绑定到组件
v-on 的 .native 在一个组件的根元素上直接监听一个原生事件。
<base-input v-on:focus.native="onFocus"></base-input>
注意:如果根元素不是 input 框,则需要采用 $listeners 处理。
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为.这里是input框自身默认的事件
{
// 这里确保组件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
10. 解构插槽 Prop
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:
function (slotProps) {
// 插槽内容
}
意味着 v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以可以使用 ES2015 解构来传入具体的插槽 prop。
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>