vue的源码初始化、组件化流程
vue组件化流程脑图
1.各种数据的初始化,包括组件和数据等。
// Jane 初始 Vue init函数声明。
initMixin(Vue$3);
//Jane 设置vue原型状态:$data(getter/setter),$props,$watch,$set,$delete
//$data,$props, 取值_data,_prop
stateMixin(Vue$3);
//设置vue 的事件原型,$on,$once,$off,$emit
eventsMixin(Vue$3);
// 初始化 $forceUpdate,_update,$destroy这个挂在vue原型的实例方法。
//跟渲染相关,触发vm._watcher.update();
lifecycleMixin(Vue$3);
//原型上的$nextTick:延迟一个任务执行,_render:产出一个虚拟节点了。
renderMixin(Vue$3);
initGlobalAPI(Vue$3);
- 会先执行Vue.extend,实现组件注册吧,然后proxy这个函数是代理函数,统一操作vue._data上的数据都会触发defineProperty定义的set/get函数。包括了component,computed
//设置在target上的属性 统一从sourceKey属性拿。
function proxy(target, sourceKey, key) {
sharedPropertyDefinition.get = function proxyGetter() {
return this[sourceKey][key]
};
sharedPropertyDefinition.set = function proxySetter(val) {
this[sourceKey][key] = val;
};
Object.defineProperty(target, key, sharedPropertyDefinition);
}
3.开始new 实例化vue入口函数,也是跟上面初始化组件一样,调用prox函数,然后 初始化data,watch,computed,method,
会将data加人set,get函数。 等待初始渲染而去读取数据的时候,触发get函数,从而会将此刻对应的渲染函数加人数据的监听函数队列。等待后续set的时候分发。
function Vue$3(options) {
if ("development" !== 'production' &&
!(this instanceof Vue$3)) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}
//编译
if (vm.$options.el) {
//执行 9477那里重写的。
vm.$mount(vm.$options.el);
}
4.重点介绍对象,updateComponent、watcher、Dep、observer的关系
updateComponent = function() {
//这里会渲染动作。。。_update相当于渲染,vm.__patch__,
//当渲染动作涉及到了虚拟DOM节点,虚拟节点会遇到到相关的 变量的时候,读取变量的值,自动会通过prox代理函数,又去触发了
// 变量的 Object.defineProperty,描述符的get,具体逻defineReactive$$1这个函数。
vm._update(vm._render(), hydrating);
};
// 渲染动作 加入watcher
vm._watcher = new Watcher(vm, updateComponent, noop);
initData() 时候 通过observer() 将每个data数据创建dep实例,以便后续管理对应的watcher。
init()->
_update():这个是渲染dom的逻辑(具体逻辑可以看虚拟dom的相关文章),每到读取到相关data中的变量的时候,会触发变量的 Object.defineProperty,描述符的get。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
响应式原理图
image.png具体逻辑在defineReactive$$1这个函数。相关代码如下:
//相关的读取的变量如下。以下是虚拟节点中间过程
(function anonymous(
) {
with (this) { return _c('div', { attrs: { "id": "demo" } }, [_c('form', { attrs: { "id": "search" } }, [_v("\n Search "), _c('input', { directives: [{ name: "model", rawName: "v-model", value: (searchQuery), expression: "searchQuery" }], attrs: { "name": "query" }, domProps: { "value": (searchQuery) }, on: { "input": function ($event) { if ($event.target.composing) return; searchQuery = $event.target.value } } })]), _v(" "), _c('demo-grid', { attrs: { "data": gridData, "columns": gridColumns, "filter-key": searchQuery } })], 1) }
})
get: function reactiveGetter() {
var value = getter ? getter.call(obj) : val;
if (Dep.target) { //Dep.target 这个是当前watcher,
dep.depend(); //将当前的 dep实例加入watcher
if (childOb) {
childOb.dep.depend();
}
if (Array.isArray(value)) {
dependArray(value);
}
}
return value
},
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if ("development" !== 'production' && customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = observe(newVal);
dep.notify();
}
Dep.target:当前的渲染函数相关的new watcher,实例,后面会将每个读取到的dep 加入相关的wather实例,以便后续更新数据可以调用。watcher也记录了相关的dep实例。
observer():这个是监听data数据,分发data数据行为。
Dep():是管理读取数据时的集合,还有sub[]对应的wather实例集合
watcher():这个管理渲染的函数,一次渲染代表一次watcher,但一次watcher里面可能包含多个数据的读取,depid,。
多个数据的读取,是由创建虚拟dom的时候的变量,比如双括号{{}}的内容。中间虚拟dom代码如下:
(function anonymous(
) {
with (this) { return _c('div', { attrs: { "id": "demo" } }, [_c('form', { attrs: { "id": "search" } }, [_v("\n Search "), _c('input', { directives: [{ name: "model", rawName: "v-model", value: (searchQuery), expression: "searchQuery" }], attrs: { "name": "query" }, domProps: { "value": (searchQuery) }, on: { "input": function ($event) { if ($event.target.composing) return; searchQuery = $event.target.value } } })]), _v(" "), _c('demo-grid', { attrs: { "data": gridData, "columns": gridColumns, "filter-key": searchQuery } })], 1) }
})
关于更多虚拟dom的知识如下:虚拟dom
数据读取和设置的时候会分别去加入和卸载dep和wather
html方面的主题流程是-- 我的另一篇文章介绍
- 通过处理html字符串,产生ast对象树。
- 再通过ast树,产生成createElement用的虚拟DOM形式。
- 再通过patch函数放到 真实的html里面。
vue相关属性以及方法