组件的创建由createApp方法实现,并由.mount("#app")挂载至页面中
var createApp = function (rootComponent) {
var app = {
_component: rootComponent,
mount: function (rootContainer) {
console.log("基于根组件创建 vnode");
var vnode = createVNode(rootComponent);
console.log("调用 render,基于 vnode 进行开箱");
render(vnode, rootContainer);
},
};
return app;
};
render方法来进行渲染
var render = function (vnode, container) {
debug.mainPath("调用 patch")();
patch(null, vnode, container);
};
function patch(n1, n2, container, parentComponent) {
if (container === void 0) { container = null; }
if (parentComponent === void 0) { parentComponent = null; }
var type = n2.type, shapeFlag = n2.shapeFlag;
switch (type) {
case Text:
processText(n1, n2, container);
break;
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
console.log("处理 element");
processElement(n1, n2, container);
}
else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
console.log("处理 component");
processComponent(n1, n2, container, parentComponent);
}
}
}
shapeFlag的类型如下
(function (ShapeFlags) {
ShapeFlags[ShapeFlags["ELEMENT"] = 1] = "ELEMENT";
ShapeFlags[ShapeFlags["STATEFUL_COMPONENT"] = 4] = "STATEFUL_COMPONENT";
ShapeFlags[ShapeFlags["TEXT_CHILDREN"] = 8] = "TEXT_CHILDREN";
ShapeFlags[ShapeFlags["ARRAY_CHILDREN"] = 16] = "ARRAY_CHILDREN";
ShapeFlags[ShapeFlags["SLOTS_CHILDREN"] = 32] = "SLOTS_CHILDREN";
})(ShapeFlags || (ShapeFlags = {}));
在patch方法中判断所创建对象为elemnt或者component,此时传入的n1始终为null,不论是element或者component始终都是初始化创建
Element处理
mountElement()首先会通过document.createElement 来创建element
然后判断所属的element类型,如果为文本类型,则直接渲染,否则会遍历所有子节点,再调用patch方法对子节点再次进行渲染
function mountElement(vnode, container) {
var shapeFlag = vnode.shapeFlag, props = vnode.props;
var el = (vnode.el = hostCreateElement(vnode.type));
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
// \u5904\u7406\u6587\u672C:转码为处理文本
console.log("\u5904\u7406\u6587\u672C:" + vnode.children);
hostSetElementText(el, vnode.children); // 直接渲染
}
else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(vnode.children, el); // 递归调用patch对子节点再次渲染
}
if (props) {
for (var key in props) {
var nextVal = props[key];
hostPatchProp(el, key, null, nextVal);
}
}
console.log("vnodeHook -> onVnodeBeforeMount");
console.log("DirectiveHook -> beforeMount");
console.log("transition -> beforeEnter");
hostInsert(el, container);
console.log("vnodeHook -> onVnodeMounted");
console.log("DirectiveHook -> mounted");
console.log("transition -> enter");
}
function mountChildren(children, container) {
children.forEach(function (VNodeChild) {
console.log("mountChildren:", VNodeChild);
patch(null, VNodeChild, container);
});
}
Component处理
mountComponent() 实现的核心是 setupComponent(),它可以分为两个过程:
- 开始安装,它会初始化
props、slots、调用setup()、验证组件和指令的合理性。
-
- 设置状态组件(动态组件:数据可变化并渲染),此时会调用
setup()
- 设置状态组件(动态组件:数据可变化并渲染),此时会调用
- 结束安装,它会初始化
computed、data、watch、mixin和生命周期等等。
-
- 组件未挂载,则需初始化组件(初始化isMounted 都为false,经过初始化isMounted则变为true)
- 若已经挂载,则对组件进行更新
如果公共队列中没有该安转渲染,则会执行nextTick(延时回调)
function mountComponent(initialVNode, container, parentComponent) {
var instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent));
console.log("\u521B\u5EFA\u7EC4\u4EF6\u5B9E\u4F8B:" + instance.type.name);
setupComponent(instance);
setupRenderEffect(instance, container);
}
// 开始安装
function setupComponent(instance) {
var _a = instance.vnode,
props = _a.props,
children = _a.children;
initProps(instance, props);
initSlots(instance, children);
setupStatefulComponent(instance);
}
// 结束安装
function setupRenderEffect(instance, container) {
instance.update = effect(function componentEffect() {
if (!instance.isMounted) {
console.log("调用 render,获取 subTree");
var proxyToUse = instance.proxy;
var subTree = (instance.subTree = instance.render.call(proxyToUse, proxyToUse));
console.log("subTree", subTree);
console.log(instance.type.name + ":\u89E6\u53D1 beforeMount hook");
console.log(instance.type.name + ":\u89E6\u53D1 onVnodeBeforeMount hook");
patch(null, subTree, container, instance);
console.log(instance.type.name + ":\u89E6\u53D1 mounted hook");
instance.isMounted = true;
}
else {
console.log("调用更新逻辑");
var next = instance.next, vnode = instance.vnode;
if (next) {
next.el = vnode.el;
updateComponentPreRender(instance, next);
}
var proxyToUse = instance.proxy;
var nextTree = instance.render.call(proxyToUse, proxyToUse);
var prevTree = instance.subTree;
instance.subTree = nextTree;
console.log("beforeUpdated hook");
console.log("onVnodeBeforeUpdate hook");
patch(prevTree, nextTree, prevTree.el, instance);
console.log("updated hook");
console.log("onVnodeUpdated hook");
}
}, {
scheduler: function (effect) {
// 如果公共队列中没有该安转渲染,则会执行nextTick(延时回调)
queueJob(effect);
},
});
}
function queueJob(job) {
if (!queue.includes(job)) {
queue.push(job);
queueFlush();
}
}
function queueFlush() {
if (isFlushPending)
return;
isFlushPending = true;
nextTick(flushJobs);
}
function flushJobs() {
isFlushPending = false;
var job;
while ((job = queue.shift())) {
if (job) {
job();
}
}
}
function nextTick(fn) {
return fn ? p.then(fn) : p;
}
\