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"
中,值是2
oldValue
:之前的值,仅在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