Vue.mixin的mergeOption合并策略
Object.create(null)
生成不带原型链的容器strats
strats.el = strats.propsData
:child === undefined ? parent : childstrats.data = strats.provide
:以child为主,递归合并strats.生命周期
:合并到Array中strats.components = strats.directives = strats.filter
:遍历child放到res中,res的原型指向parentstrats.watch
:合并到Array中strats.props = strats.methods = strats.inject = strats.computed
:同名时child覆盖parent其余
:child === undefined ? parent : child
nextTick核心
-
做什么的:下次 DOM 更新循环结束之后执行的延迟回调【使用最新的dom进行操作】
-
核心:将任务放入队列
callbacks
中延迟执行,通过callbacks.slice(0)
复制一份用于遍历执行,同时将callbacks.length = 0
清空原数组。 -
宏任务还是微任务:依次判断先微后宏【Promise - MutationObserver - setImmediate - setTimeout】
keep-alive核心
render函数:
- 触发时机:首次渲染、插槽中的内容变化需要重新被渲染;
- 做什么:获取到插槽中的组件实例用于缓存;
cacheVNode函数:
- 触发时机:
- 首次渲染在mounted钩子【放入缓存+监听include+监听exclude分别对缓存数组中的不需缓存组件实例进行销毁】
- 插槽内容变化在updated钩子;
- 做什么的:将插槽内容放入缓存;
- 何时被清除:
- 当达到设置的max最大缓存数,通过LRU(最近最少使用) 算法,选择最近最久未使用的组件予以淘汰。【通过缓存数组中第一个获取到最久未使用的组件实例】;
- 当修改include、exclude时可能会被清除;
- destroyed钩子全部清除;
数据响应式
Vue采用“推”的变化侦测模式,一个状态会绑定多个依赖,当变化时,通知对应的依赖进行Dom更新操作。【可以随意调整更新粒度】
Vue1.0:数据与Dom节点关联【细粒度】,依赖数量较多、内存开销较大。
Vue2.0:数据与虚拟Dom(组件)关联【中等粒度】,降低依赖数量及所依赖内存开销。
-
核心
Object.defineProperty:getter收集依赖、setter触发依赖更新。
事件中心Dep
;依赖Watcher
弊端:
- 当监听 “a.b.c” 时,当前Watcher会被a、a.b、a.b.c所依赖。仔细想想貌似也没毛病。
- 对象【dep在defineReactive】新增 / 删除属性需要借助 s e t 和 set和 set和delete;
- 数组【dep在】需拦截部分方法实现响应式,list[0]=2【Object.defineProperty可以实现item[0] = 2的响应,尤大出于性能代价和用户体验收益不成正比才放弃的】和list.length=0不能实现响应式
Vue.extend核心
- 做什么的:当使用vue-cli方式进行开发时,大多数使用的SFC组件化生成 XXX.vue 页面,之后通过 Vue.extend实例化组件。使用基础 Vue 构造器,创建一个“子类”。
VUe.component('', {})
也是调用的该方法生成实例。
<script>
import Counter from './components/Counter';
import Vue from 'vue';
export default {
name: 'App',
components: {
ButtonCounter,
},
methods: {
insert() {
const Counter = Vue.extend(Counter); // 最终还是使用init初始化的
const instance = new Counter();
instance.$mount('#container');
},
},
};
</script>
- 核心:
- 使用原型继承的方法返回了 Vue 的子类
VueComponent
构造函数 - 利用 mergeOptions 把传入组件的 options 和Vue.options 【包含使用Vue.use注册的插件: s t o r e 、 store、 store、router等都在全局option】进行了合并。
- 使用原型继承的方法返回了 Vue 的子类
自定义指令
Vue.use
-
做什么的:向Vue项目注册插件,比如:vuex、vue-router等
-
核心:
-
判断当前注册列表中是否已注册,注册过直接返回,没有注册继续向下;
-
准备参数(第一个参数为全局Vue):
const args = toArray(arguments, 1); args.unshift(this /** 表示全局Vue **/)
; -
运行插件的install方法:
plugin.install.apply(plugin, args)
;否则直接运行plugin.apply(null, args)
-
以
vue-router
为例:-
在项目开始注册
import VueRouter from "vue-router"; Vue.use(VueRouter);
-
运行install方法:
export let _Vue export function install (Vue) { // 1. 确保只能注册一次 if (install.installed && _Vue === Vue) return install.installed = true _Vue = Vue const isDef = v => v !== undefined const registerInstance = (vm, callVal) => { let i = vm.$options._parentVnode if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) { i(vm, callVal) } } // 2. 向全局Vue.mixins添加钩子[将router逻辑代码插入];之后通过Vue.extends生成的组件都会混入 Vue.mixin({ beforeCreate () { if (isDef(this.$options.router)) { this._routerRoot = this this._router = this.$options.router this._router.init(this) Vue.util.defineReactive(this, '_route', this._router.history.current) } else { this._routerRoot = (this.$parent && this.$parent._routerRoot) || this } registerInstance(this, this) }, destroyed () { registerInstance(this) } }) // 3. 向Vue.prototype添加$router和$route属性 Object.defineProperty(Vue.prototype, '$router', { get () { return this._routerRoot._router } }) Object.defineProperty(Vue.prototype, '$route', { get () { return this._routerRoot._route } }) // 4. 注册全局组件RouterView和RouterLink Vue.component('RouterView', View) Vue.component('RouterLink', Link) // 5. 设置beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate的合并策略 const strats = Vue.config.optionMergeStrategies strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created }
-
-
-