Vue2
渐进式
Vue一个大特点就是渐进式,意思就是你可以渐渐地用Vue,而React几乎做不到这一点
动态构建用户界面
你可以继续操作DOM
你可以很方便地做SEO
你可以局部做单页面
你可以整体做单页面
Features
遵循MVVM
编码简洁,体积小,运行效率高,适合PC/移动端开发
本身只关注UI,可以轻松的引入Vue插件或其他第三方库
MVVM

插值表达式(仅支持单一表达式)
{{ number + 1 }}{{ obj.name }}{{ ok ? 'YES' : 'NO' }}{{ message.split('').reverse().join('') }}<div :id="list-${id}"></div>
<span :title="toTitleDate(date)">
{{ formatDate(date) }}
</span>Vue 属性
el
data
template
methods
components(global / local)
全局定义时命名不能重复
filters(global / local)
{{ message | filter }}
<div v-bind="msg | filter"></div>
watch -> single data
深层
回调触发时机
停止
即时
this.$watch
//可以直接写methods方法名字符串 watch: { currentBranch: 'fetchData' }
==computed -> multiple data==(不要在计算函数中做异步请求或者更改 DOM)
VS watch
computed是计算一个新属性,并将该属性挂载到vm上,而watch是监听已经存在且已挂载到vm上的属性, 计算属性不需要在 data 里面提前定义,watch 则需要,所以用watch同样可以监听computed计算属性的变化(其它还有data、props)computed本质是一个惰性求值的观察者,具有缓存性,只有当依赖变化后,第一次访问computed属性,才会计算新的值,而watch则是当数据发生变化便会调用执行函数从使用场景上说,
computed适用一个数据被多个数据影响,而watch适用一个数据影响多个数据,watch适合异步或开销大的场景
VS method
methods没有缓存computed不可传参,methods可以computed通过getter、setter传newValue
==class==
<p :class="{ active: isActive, 'text-danger': hasError }">xxx是字符串</p><p :class="classObject">xxx是对象</p><p :class="['bClass', 'cClass']">xxx是数组</p><div :class="[isActive ? activeClass : '', errorClass]"></div><div :class="[{ active: isActive }, errorClass]"></div>
==style==
<p :style="{ color: activeColor, fontSize: fontSize + 'px' }"></p><div :style="{ 'font-size': fontSize + 'px' }"></div><p :style="styleObject"></p><p :style="[baseStyles, overridingStyles]">test style2</p><div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
Vue 实例属性
$el、$options、$data
==$attrs、$listeners== (除了class、style、v-on等)、$props
<!-- 想要所有像 class 和 v-on 监听器这样的透传 attribute 都应用在内部的 <button> 上而不是外层的 <div> 上。通过设定 inheritAttrs: false 和使用 v-bind="$attrs" 来实现: --> <div class="btn-wrapper"> <button class="btn" v-bind="$attrs">click me</button> </div>$root、$parent、$children
$refs(操作DOM)
<script> export default { mounted() { this.$refs.input.focus() } } </script> <template> <input ref="input" /> </template>组件上的ref代表的是子组件实例
expose
export default { expose: ['publicData', 'publicMethod'], data() { return { publicData: 'foo', privateData: 'bar' } }, methods: { publicMethod() { /* ... */ }, privateMethod() { /* ... */ } } }DOM更新时机
import { nextTick } from 'vue' export default { methods: { increment() { this.count++ //适用场景 //1. 在`created`生命周期执行DOM操作 //2. 在数据变化后需要进行基于DOM结构的操作 nextTick(() => { // 访问更新后的 DOM }) } } }
directive
v-once、v-cloak
//如果没有彻底解决问题,则在根元素加上style="display: none;" :style="{display: 'block'}" [v-cloak] { display: none; }v-model
原生展开 :
<input :value="val" @input="val=$event.target.value" />自定义组件展开:
<CustomInput :modelValue="searchText" @update:modelValue="newValue => searchText = newValue" /><!-- 为了使组件正常工作 --> <!-- 方式一:CustomInput.vue --> <script> export default { props: ['modelValue'], emits: ['update:modelValue'] } </script> <template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> </template> <!-- 方式二:CustomInput.vue --> <script> export default { props: ['modelValue'], emits: ['update:modelValue'], computed: { value: { get() { return this.modelValue }, set(value) { this.$emit('update:modelValue', value) } } } } </script> <template> <input v-model="value" /> </template> //定义别名 <MyComponent v-model:title="bookTitle" /> Vue.component('base-checkbox', { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > ` })
v-for、v-if、v-else、v-else-if、v-show
v-if vs v-show
==v-if > v-for==
<!-- 抛出一个错误,因为属性 todo 此时没有在该实例上定义--> <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo.name }} </li> <!-- 方式一套一层 --> <template v-for="todo in todos"> <li v-if="!todo.isComplete"> {{ todo.name }} </li> </template> <!-- 方式二 使用计算属性 -->v-text、v-html
v-slot、v-pre
==v-bind ==> :==
<div v-bind="objectOfAttrs"></div>
==v-on ==> @==
在内联事件处理器中访问事件参数
<!-- 使用特殊的 $event 变量 --> <button @click="warn('Form cannot be submitted yet.', $event)"> Submit </button> <!-- 使用内联箭头函数 --> <button @click="(event) => warn('Form cannot be submitted yet.', event)"> Submit </button>事件修饰符
stop、prevent、self<!-- 单击事件将停止传递 --> <a @click.stop="doThis"></a> <!-- 提交事件将不再重新加载页面 --> <form @submit.prevent="onSubmit"></form> <a @click.stop.prevent="doThat"></a> <form @submit.prevent></form> <!-- 仅当 event.target 是元素本身时才会触发事件处理器 --> <!-- 例如:事件处理器不来自子元素 --> <div @click.self="doThat">...</div>once、capture、passive与[原生addEventListener事件相对应<!-- 添加事件监听器时,使用 `capture` 捕获模式 --> <!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 --> <div @click.capture="doThis">...</div> <!-- 点击事件最多被触发一次 --> <a @click.once="doThis"></a> <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 --> <!-- 以防其中包含 `event.preventDefault()` --> <div @scroll.passive="onScroll">...</div>按键修饰符
enter、tab、delete、esc、space、up、down、left、right<input @keyup.enter="submit" /> <input @keyup.page-down="onPageDown" />系统修饰符
ctrl、alt、shift、exact、meta<!-- Alt + Enter --> <input @keyup.alt.enter="clear" /> <!-- Ctrl + 点击 --> <div @click.ctrl="doSomething">Do something</div> <!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 --> <button @click.ctrl="onClick">A</button> <!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 --> <button @click.ctrl.exact="onCtrlClick">A</button> <!-- 仅当没有按下任何系统按键时触发 --> <button @click.exact="onClick">A</button>鼠标修饰符
left、right、middle自定义事件
==自定义指令==(只有当所需功能只能通过直接的 DOM 操作来实现时,才应该使用自定义指令。其他情况下应该尽可能地使用
v-bind这样的内置指令来声明式地使用模板,这样更高效,也对服务端渲染更友好)hooks
bind 指令第一次绑定到元素时调用,只执行一次,可用于一次性初始化设置
inserted 元素插入父节点时调用
update 所有 VNode 更新时调用,可能发生在子 VNode 之前
componentUpdated 指令所在组件在 VNode 和其子 VNode 更新后调用
unbind 指令与元素解绑时调用
参数
el指令绑定元素,可操作 DOMbinding指令描述对象value:传递给指令的值。例如在v-my-directive="1 + 1"中,值是2oldValue:之前的值,仅在beforeUpdate和updated中可用。无论值是否更改,它都可用arg:传递给指令的参数 (如果有的话)。例如在v-my-directive:foo中,参数是"foo"modifiers:一个包含修饰符的对象 (如果有的话)。例如在v-my-directive.foo.bar中,修饰符对象是{ foo: true, bar: true }instance:使用该指令的组件实例dir:指令的定义对象
vnode代表绑定元素的底层 VNodeprevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在beforeUpdate和updated钩子中可用
==表单输入绑定==

t 基础用法
<input type='checkbox' v-model="name" true-value="xxx" false-value="yyy">值绑定
表单事件修饰符
v-model:lazy、v-model:number、v-model:trim自定义model
model: { prop: "phoneInfo", // 默认 value event: "change" // 默认 input }, props: { phoneInfo: Object, zipCode: String } <PersonalInfo v-model="phoneInfo" :zip-code.sync="zipCode" /> <PersonalInfo :phone-info="phoneInfo" :zip-code="zipCode" @change="val => (phoneInfo = val)" @update:zipCode="val => (zipCode = val)" />
==组件(global / local)==
属性
==传递props==
单向数据流
props初始化先于data,可挂载到子实例data上
校验(null和undefined会通过任何类型验证)
props: { //null和undefined会通过任何类型验证 propA: Number, propB: [String, Number], propC: { type: String, required: true }, propD: { type: Number, default: 100 }, propE: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default: function () { return { message: 'hello' } } }, propF: { validator: function (value) { return ['success', 'warning', 'danger'].indexOf(value) !== -1 } }, propG: { type: Array, default: () => [] } } //对于多类型场景 <template> <button :style="computedWidth">{{ computedWidth }}</button> </template> <script> export default { props: { width: [String, Number] }, computed: { computedWidth () { let o = {} if (typeof this.width === 'string') o.width = this.width if (typeof this.width === 'number') o.width = this.width + 'px' return o } } } </script>
原生属性自动挂载到组件根元素上,可设置
inheritAttrs = false不继承Non-props
//parent <Child style="color:red" :msg="msg"> //Child //当子组件有父节点的时候,non-props才会挂载到子组件根节点,否则不挂 //通过如下方式挂载到平级的某个节点上 //挂载所有 <div v-bind="$attrs">Hello world</div> <div >Hello world2</div> //只挂载style <div v-bind:style="$attrs.style">Hello world</div> <div >Hello world2</div> //业务逻辑上只使用msg mounted() { console.log(this.$attrs.msg) }.sync
//子组件 this.$emit('update:title', newTitle) //父组件 <text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event" ></text-document> //to <text-document v-bind:title.sync="doc.title"></text-document>`特殊属性
class / style / ref / key
事件
修饰符事件,应用于原生html元素
普通事件
<!-- 父组件 --> <BlogPost ... @enlarge-text="postFontSize += 0.1" /> <!-- 子组件 --> <template> <div class="blog-post"> <h4>{{ title }}</h4> <button @click="$emit('enlarge-text')">Enlarge text</button> </div> </template>emits
<!-- BlogPost.vue --> <script> export default { props: ['title'], emits: ['enlarge-text'] } </script> //emits校验 export default { emits: { // 没有校验 click: null, //校验父组件的add方法 <Child @add="xxx"> add: (value) => value < 20 ? true : false // 校验 submit 事件 submit: ({ email, password }) => { if (email && password) { return true } else { console.warn('Invalid submit event payload!') return false } } }, methods: { submitForm(email, password) { this.$emit('submit', { email, password }) } } }
插槽
默认内容
具名插槽
<BaseLayout> <template #header> <h1>Here might be a page title</h1> </template> <!-- 隐式的默认插槽 --> <p>A paragraph for the main content.</p> <p>And another one.</p> <template #footer> <p>Here's some contact info</p> </template> </BaseLayout>作用域插槽(父组件需要访问子组件的data的时候)
默认插槽
<!-- <MyComponent> 的模板 --> <div> <slot :text="greetingMessage" :count="1"></slot> </div> <MyComponent v-slot="{ text, count }"> {{ text }} {{ count }} </MyComponent>具名插槽
<!-- <MyComponent> 的模板 --> <slot name="header" message="hello"></slot> <MyComponent> <template #header="headerProps"> {{ headerProps }} </template> <template #default="defaultProps"> {{ defaultProps }} </template> <template #footer="footerProps"> {{ footerProps }} </template> </MyComponent>
动态组件
<keep-alive> <component :is="showComponent" /> </keep-alive>异步组件
app.component('async-component', Vue.defineAsyncComponent( () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ template: `<div>这是一个异步组件</div>` }) }) }) } ))DOM 模板解析注意事项
大小写
闭合标签
元素位置显示
穿透scoped
在模板中使用两次style标签
<style lang="scss"> /*添加要覆盖的样式*/ </style> <style lang="scss" scoped> /* local styles */ </style>使用
>>>或/deep/操作符(Sass之类的预处理器无法正确解析>>>)<style lang="scss"> .box >>> input { width: 166px; text-align: center; } .box { /deep/ input { width: 166px; text-align: center; } } </style>
生命周期
beforeCreate/created、beforeMount/mounted、beforeUpdate/updated、beforeDestroyed/destroyed、errorCaptured
activated/deactivated、errorCaptured
Vue优化
编码阶段
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
v-if和v-for不能连用
如果需要使用v-for给每项元素绑定事件时使用事件代理
SPA 页面采用keep-alive缓存组件
在更多的情况下,使用v-if替代v-show
key保证唯一
使用路由懒加载、异步组件
防抖、节流
第三方模块按需导入
长列表滚动到可视区域动态加载
图片懒加载
SEO优化
预渲染
服务端渲染SSR
打包优化
压缩代码
Tree Shaking/Scope Hoisting
使用cdn加载第三方模块
多线程打包happypack
splitChunks抽离公共文件
sourceMap优化
用户体验
骨架屏
PWA
还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等
Last updated