Vue 源码
你想要的 vue 源码分析 · 08
组件化 组件化,把页面拆分成若干个组件,每个组件依赖的 css,js,模板,图片等资源放在一块,组件之间是相互独立的,可已复用与嵌套 组件 vnode 的创建什么时候执行 在 vm._update 中会调用_createComponent 方法,这个方法有一段代码是先判断 tag 是否是一个普通 div,如果是普通 div,那么会实例化一个普通的 VNode,否则就通过 createCompone…
组件化 组件化,把页面拆分成若干个组件,每个组件依赖的 css,js,模板,图片等资源放在一块,组件之间是相互独立的,可已复用与嵌套 组件 vnode 的创建什么时候执行 在 vm._update 中会调用_createComponent 方法,这个方法有一段代码是先判断 tag 是否是一个普通 div,如果是普通 div,那么会实例化一个普通的 VNode,否则就通过 createCompone…
组件化,把页面拆分成若干个组件,每个组件依赖的 css,js,模板,图片等资源放在一块,组件之间是相互独立的,可已复用与嵌套
import Vue from 'vue';
import App from './App.vue';
var app = new Vue({
el: '#app',
// 这里的 h 是 createElement 方法
render: (h) => h(App)
});
在 vm._update 中会调用_createComponent 方法,这个方法有一段代码是先判断 tag 是否是一个普通 div,如果是普通 div,那么会实例化一个普通的 VNode,否则就通过 createComponent 去创建一个组件 VNode。
// \src\core\vdom\create-element.js
if (typeof tag === 'string') {
let Ctor;
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag);
if (config.isReservedTag(tag)) {
// platform built-in elements
vnode = new VNode(config.parsePlatformTagName(tag), data, children, undefined, undefined, context);
} else if (isDef((Ctor = resolveAsset(context.$options, 'components', tag)))) {
// component
vnode = createComponent(Ctor, data, context, children, tag);
} else {
// unknown or unlisted namespaced elements
// check at runtime because it may get assigned a namespace when its
// parent normalizes children
vnode = new VNode(tag, data, children, undefined, undefined, context);
}
} else {
// 组件vnode创建的过程
vnode = createComponent(tag, data, context, children);
}
// createComponent的过程例子
import HelloWorld from './components/HelloWorld';
export default {
name: 'app',
components: {
HelloWorld
}
};
组件vnode创建的过程核心步骤export function createComponent(Ctor: Class<Component> | Function | Object | void, data: ?VNodeData, context: Component, children: ?Array<VNode>, tag?: string): VNode | Array<VNode> | void {
if (isUndef(Ctor)) {
return;
}
// options合并后的结果
const baseCtor = context.$options._base;
// plain options object: turn it into a constructor
if (isObject(Ctor)) {
// 在\src\core\global-api\index.js中有一段代码 Vue.options._base = Vue
// 在src/core/instance/init.j中有一段代码
// vm.$options = mergeOptions(
// resolveConstructorOptions(vm.constructor),
// options || {},
// vm
// )
// 这样就把Vue的option扩展到vm.$option上了,可以通过vm.$options._base访问到Vue这个构造函数了。
// 在上例中baseCtor相当于Vue 相当于Vue.extend
// 核心步骤1:构造子类构造函数
Ctor = baseCtor.extend(Ctor);
}
// 创建一个构造器的时候不是一个函数的时候,报错
if (typeof Ctor !== 'function') {
if (process.env.NODE_ENV !== 'production') {
warn(`Invalid Component definition: ${String(Ctor)}`, context);
}
return;
}
// 一些异步组件
let asyncFactory;
if (isUndef(Ctor.cid)) {
asyncFactory = Ctor;
Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context);
if (Ctor === undefined) {
// return a placeholder node for async component, which is rendered
// as a comment node but preserves all the raw information for the node.
// the information will be used for async server-rendering and hydration.
return createAsyncPlaceholder(asyncFactory, data, context, children, tag);
}
}
// 处理data
data = data || {};
// resolve constructor options in case global mixins are applied after
// 对option处理,因为有可能Sub的属性可能北全局的mixin影响
resolveConstructorOptions(Ctor);
// 对v-model对的处理
if (isDef(data.model)) {
transformModel(Ctor.options, data);
}
// 对props进行处理,生成prop data
const propsData = extractPropsFromVNodeData(data, Ctor, tag);
// 对函数组件的处理
if (isTrue(Ctor.options.functional)) {
return createFunctionalComponent(Ctor, propsData, data, context, children);
}
// 对自定义事件的处理
const listeners = data.on;
// replace with listeners with .native modifier
// so it gets processed during parent component patch.
data.on = data.nativeOn;
// 抽象组件
if (isTrue(Ctor.options.abstract)) {
// abstract components do not keep anything
// other than props & listeners & slot
// work around flow
const slot = data.slot;
data = {};
if (slot) {
data.slot = slot;
}
}
// 核心步骤2:安装组件钩子函数
installComponentHooks(data);
// 核心步骤3:实例化 VNode
const name = Ctor.options.name || tag;
// 组件的vnode是没有children的,在patch时会用到为空的情况
// tag,data,chidlren,text,elm,context,componentOptions
const vnode = new VNode(`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`, data, undefined, undefined, undefined, context, { Ctor, propsData, listeners, tag, children }, asyncFactory);
// Weex specific: invoke recycle-list optimized @render function for
// extracting cell-slot template.
// https://github.com/Hanks10100/weex-native-directive/tree/master/component
/* istanbul ignore if */
if (__WEEX__ && isRecyclableComponent(vnode)) {
return renderRecyclableComponentTemplate(vnode);
}
return vnode;
}
Vue.extend 作用就是构造一个 Vue 的子类,使用原型继承,把一个纯对象转换一个继承于 Vue 的构造器 Sub 并返回,扩展了一些属性,并对 props 和 computed 做了初始化的,最后对 Sub 进行缓存,避免多次 extend 对组件重复构造。
// src/core/global-api/extend.js
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
// this指向Vue 是静态
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
// 如果已经有了一个Sub,return出去,不用继续建立构造函数
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
// name就是组件name
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
// 进行校验,以字母开头,连字符结尾,不满足会报错,传入是个内置的标签,也会报错
validateComponentName(name)
}
// 子的构造数
const Sub = function VueComponent (options) {
// 又走到了vue初始化的过程,
this._init(options)
}
// 把子构造器的原型加到父构造器原型
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
// 自身options和vue的options进行合并
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
// Vue全局的静态方法附给Sub,目的是让Sub和Vue同样的能力
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
// Vue的属性附给Sub
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// 缓存下来,为了只执行一次
cachedCtors[SuperId] = Sub
return Sub
}
}
installComponentHooks 的过程就是把 componentVNodeHooks 的钩子函数 merge 到 data.hook 上
function mergeHook(f1: any, f2: any): Function {
const merged = (a, b) => {
// 依次执行这两个钩子函数即可
f1(a, b);
f2(a, b);
};
merged._merged = true;
return merged;
}
const hooksToMerge = Object.keys(componentVNodeHooks);
function installComponentHooks(data: VNodeData) {
const hooks = data.hook || (data.hook = {});
// 组件默认有几个钩子,对钩子进行遍历,把钩子merge到data.hook上
for (let i = 0; i < hooksToMerge.length; i++) {
const key = hooksToMerge[i];
const existing = hooks[key];
// 在 patch 过程中暴露出各种实际的钩子函数
const toMerge = componentVNodeHooks[key];
if (existing !== toMerge && !(existing && existing._merged)) {
hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge;
}
}
}
在 patch 过程中暴露出各种实际的钩子函数
const componentVNodeHooks = {
init(vnode: VNodeWithData, hydrating: boolean): ?boolean {
if (vnode.componentInstance && !vnode.componentInstance._isDestroyed && vnode.data.keepAlive) {
// kept-alive components, treat as a patch
const mountedNode: any = vnode; // work around flow
componentVNodeHooks.prepatch(mountedNode, mountedNode);
} else {
const child = (vnode.componentInstance = createComponentInstanceForVnode(vnode, activeInstance));
child.$mount(hydrating ? vnode.elm : undefined, hydrating);
}
},
prepatch(oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
const options = vnode.componentOptions;
const child = (vnode.componentInstance = oldVnode.componentInstance);
updateChildComponent(
child,
options.propsData, // updated props
options.listeners, // updated listeners
vnode, // new parent vnode
options.children // new children
);
},
insert(vnode: MountedComponentVNode) {
const { context, componentInstance } = vnode;
if (!componentInstance._isMounted) {
componentInstance._isMounted = true;
callHook(componentInstance, 'mounted');
}
if (vnode.data.keepAlive) {
if (context._isMounted) {
// vue-router#1212
// During updates, a kept-alive component's child components may
// change, so directly walking the tree here may call activated hooks
// on incorrect children. Instead we push them into a queue which will
// be processed after the whole patch process ended.
queueActivatedComponent(componentInstance);
} else {
activateChildComponent(componentInstance, true /* direct */);
}
}
},
destroy(vnode: MountedComponentVNode) {
const { componentInstance } = vnode;
if (!componentInstance._isDestroyed) {
if (!vnode.data.keepAlive) {
componentInstance.$destroy();
} else {
deactivateChildComponent(componentInstance, true /* direct */);
}
}
}
};
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论