Vue 基础特性
Class和Style绑定
条件渲染
v-if 和 v-show
v-if可以结合v-else、v-else-if使用
列表渲染
事件处理
表单输入绑定
知识点:
- v-model
- 常见表单项: input、textarea、checkbox、radio、select
- 修饰符: lazy、number、trim
v-model
指令可以在在表单 <input>
、<textarea>
及 <select>
元素上创建双向数据绑定。
v-model 在内部为不同的输入元素使用不同的 property ,并抛出不同的事件:
- text 和 textarea 元素使用
value
property 和 input 事件; - checkbox 和 radio 使用
checked
property 和 change 事件; - select 元素将 value 作为 prop 并将 change 作为事件。
Vue 高级特性
- 自定义v-model
- $nextTick
- refs
- slot
- 动态组件 & 异步组件
- keep-alive
- mixin
自定义v-model
使用v-model分为两种场景:
- 用在自定义组件上,实现父子组件之间的双向绑定
- 用在原生DOM元素上
1. 自定义组件上
<v-input v-model="val"></v-input>
<!-- 等价于 -->
<v-input :value="val" @input="val = $event"></v-input>
2. 原生DOM元素上
用于 input、select、textarea
<input v-model="msg" />
<!-- v-model相当于绑定了value属性和input事件 -->
<input v-bind:value="msg" v-on:input="msg = $event.target.value" />
动态组件 & 异步组件
// 动态组件
<component v-bind:is="currentTabComponent"></component>
// 异步组件
Vue.component(
'async-component-name',
() => import('./async-component') // 这个动态导入会返回一个 `Promise` 对象。
)
$nextTick
前置知识
Vue是异步渲染,data改变之后,DOM不会立刻。而$nextTick会在DOM渲染之后被触发,以获取最新DOM节点。
Mixin 抽离公共逻辑
Mixin特点
- 抽离多个组件的相同逻辑,复用性高
- 组件支持多个mixin混入
- mixin的data、computed会混入到组件一起,且
- mixin的生命周期优先级高于组件自身的生命周期
Mixin缺点:
- 变量来源不确定,可读性不高
- 多mixin混入可能会造成命名冲突
补充:
- 在Vue3中用Composition API来解决这些问题
Vuex知识点
文档地址
Vue-Router知识点
文档地址
知识点:
- 路由模式(hash、H5 history)
- 路由配置(动态路由、懒加载)
HTML5 History 模式,需要相应的后端配置
const router = new VueRouter({
mode: 'history',
routes: [...]
})
Vue原理
组件化的基础上出现了数据驱动视图,
对于传统组件化和数据驱动视图
- 传统组件化,只是静态渲染,更新还要依赖JS对于DOM的操作。
- 数据驱动视图(Vue的MVVM)
响应式
Object.defineProperty
是监听data变化的核心API。
/**
* target {Object} 对象
* key {String} 属性名
*/
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
value = newValue
}
})
Object.defineProperty
的缺点
- 深度监听需要递归到底,计算量大
- 无法监听新增属性/删除属性(Vue.set / Vue.delete)
- 无法监听原生数组(需要特殊处理)
实现数组变化监听
改变「Vue中需要实现响应式的数组」的原型
// 重新定义数组原型
const _ArrayProperty = Array.prototype
// 创建新对象,原型指向 _ArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(_ArrayProperty);
// 「改变数组结构」的部分常用的方法
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
arrProto[methodName] = function () {
updateView() // 触发视图更新
_ArrayProperty[methodName].call(this, ...arguments)
}
})
// 监听对象属性
function observer(target) {
...
if (Array.isArray(target)) {
target.__proto__ = arrProto
}
...
虚拟DOM
问题现状:DOM操作非常耗费性能,并且操作JS来控制DOM
解决方案:
1、 将DOM相关的计算转移到JS处理;
2、 用JS模拟DOM结构,计算出最小的变更,最后再操作DOM
Vue模版编译
模版不是html,它含有指令、插值、JS表达式,能实现判断、循环。
模版一定是转换为JS代码,即编译模版
const compiler = require('vue-template-compiler')
// 插值
const template = `<p>{{message}}</p>`
const res = compiler.compile(template)
console.log(res.render)
// 输出结果(如下)
with(this) {
return _c(
'p',
[_v(_s(message))]
)
}
的
- 模版编译为render函数,执行redner函数返回vnode
- 基于vnode执行 patch 和 diff
- 使用webpack的vue-loader
vue 源码中找到缩写函数的含义
其中,_c
就是 createElement,比h函数
更加语义化。
function installRenderHelpers (target) {
target._o = markOnce;
target._n = toNumber;
target._s = toString;
target._l = renderList;
target._t = renderSlot;
target._q = looseEqual;
target._i = looseIndexOf;
target._m = renderStatic;
target._f = resolveFilter;
target._k = checkKeyCodes;
target._b = bindObjectProps;
target._v = createTextVNode;
target._e = createEmptyVNode;
target._u = resolveScopedSlots;
target._g = bindObjectListeners;
target._d = bindDynamicKeys;
target._p = prependModifier;
}
在Vue组件中,使用 render 代替 tempalte
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // 标签名称
this.$slots.default // 子节点数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
组件的渲染和更新过程
patch方法,计算新旧vnode的差异
function patch(oldVnode: VNode | Element, vnode: VNode): VNode {
const insertedVnodeQueue: VNodeQueue = [];
// 第一个参数不是vnode
if (!isVnode(oldVnode)) {
// 创建一个空的vnode,关联到这个 DOM 元素
oldVnode = emptyNodeAt(oldVnode);
}
// 相同的 vnode(key 和 sel 都相等)
if (sameVnode(oldVnode, vnode)) {
// vnode 对比
patchVnode(oldVnode, vnode, insertedVnodeQueue);
// 不同的 vnode ,直接删掉重建
} else {
...
}
}
function patchVnode(oldVnode: VNode, vnode: VNode, insertedVnodeQueue: VNodeQueue) {
}
- 响应式:监听data属性getter\setter(包括数组)
- 模版编译: 模版到render函数,再到vnode
- vdom: patch(elem,vnode)和patch(vnode, newVnode)
挂载的逻辑
- 判断是否有
初次渲染过程
- 解析模版render函数
- 触发响应式,监听data属性的getter和setter(因为render函数的变量是this指向的;也包括依赖收集)
- 执行render函数,生成vnode
- patch(elem, vnode)
更新过程
- 修改data,触发setter(必须是之前已在getter中被监听)
- 重新执行render函数,生成newVnode
- patch(vnode, newVnode)