脚本宝典收集整理的这篇文章主要介绍了vue@2.0源码学习---组件究竟是什么,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。小宝典致力于为广大程序猿(媛)提供高品质的代码服务,请大家多多光顾小站,小宝典在此谢过。
vue文档告诉我们可以使用Vue.COMponent(tagName, options)
注册一个组件
Vue.component('my-component', { // 选项 })
毫无疑问这是一个全局API,我们顺着代码最终可以找到Vue.component
是这样的
Vue.component = function(id, definITion) { definition.name = definition.name || id definition = Vue.extend(definition) this.options[tyPE + 's'][id] = definition return definition }
Vue.component
实际上是Vue.extend
的封装,Vue.extend
如下:
Vue.extend = function (extendOptions: Object): Function { extendOptions = extendOptions || {} const Super = this const isFirstExtend = Super.cid === 0 if (isFirstExtend && extendOptions._Ctor) { return extendOptions._Ctor } let name = extendOptions.name || Super.options.name const Sub = function VueComponent (options) { this._init(options) } Sub.prototype = Object.create(Super.prototype) Sub.prototype.constructor = Sub Sub.cid = cid++ Sub.options = mergeOptions( Super.options, extendOptions ) Sub['super'] = Super // allow further extension Sub.extend = Super.extend // create asset registers, so extended classes // can have their private assets too. config._assetTypes.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. Sub.superOptions = Super.options Sub.extendOptions = extendOptions // cache constructor if (isFirstExtend) { extendOptions._Ctor = Sub } return Sub }
可以看到Vue.extend
返回的实际上是一个构造函数Sub
,并且此构造函数继承自Vue。里面有这么几行代码
Sub.options = mergeOptions( Super.options, extendOptions )
那么Super.options(即Vue.options
)是什么呢?
Vue.options = Object.create(null) // 包含components directives filters config._assetTypes.forEach(type => { Vue.options[type + 's'] = Object.create(null) }) util.extend(Vue.options.components, builtInComponents)
Vue.options事实上存放了系统以及用户定义的component、directive、filter,builtInComponents
为Vue内置的组件(如keep-alive
),打印看下:
所以Sub构造函数的options不仅包含components、directives、filters,还包含预先定义的实例化时所需的选项。定义一个组件如下:
let MyComponent = Vue.extend({ data() { return { msg: "this's compoennt" } }, render(h) { return h('p', this.msg) } })
打印MyComponent.options如下:
再回过头看Vue.component
,可以发现他做的工作就是扩展一个Vue构造函数(VueComponent
),并将这个构造函数(VueComponent
)添加到Vue.options.components
现在我们已经可以回答最开始的问题---vue的组件是什么?vue的组件其实就是扩展的Vue构造函数,并且在适当的时候实例化为@R_126_1332@。
组件对应的vnode是什么样子?从一个简单的例子入手:
let MyComponent = Vue.component('my-component', { data() { return { msg: "this's component" } }, render(h) { return h('p', this.msg) } }) window.app = new Vue({ render(h) { return h('my-component') } }).$mount('#root')
上篇文章已经说道在initRender的时候会初始一个系统watcher,如下:
vm._watcher = new Watcher(vm, () => { vm._update(vm._render(), hydrating) }, noop)
上篇文章提到vm._render()
返回的是一个虚拟dom(vnode
),具体到本篇,那么组件标签会被解析成什么样的虚拟节点呢?
事实上render的时候会首先调用createElement
,根据传入的tag(htML标签或者组件标签)不同,vnode可以分为以下两种:
这种就是普通的html标签(p、div、span等)对应的vnode
当tag是组件标签的时候,会调用createComponent
,如下:
else if ((Ctor = resolveAsset(context.$options, 'components', tag))) { // component return createComponent(Ctor, data, context, children, tag) }
这里的Ctor就是我们扩展的组件构造函数,createComponent
最终返回的vnode如下:
const vnode = new VNode( `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`, data, undefined, undefined, undefined, undefined, context, { Ctor, propsdata, listeners, tag, children } )
需要注意的是data有一个操作:
// merge component management hooks onto the placeholder node mergeHooks(data)
merge之后data.hook
会添加四个方法:
前文看到组件构造函数实际上是存在组件对应vnode的componentOptions中,那么究竟是什么时候实例化组件呢?
顺着vm._update(vm._render(), hydrating)往下看发现最终调用的是patch操作,而对于组件实例化而言并不存在与之对应的oldVnode(因为oldVnode是在组件更新后产生的),所以最终的逻辑归到根据组件对应的vnode创建真实dom节点
,即
createElm(vnode, insertedVnodeQueue)
我们还记得组件的构造函数是vnode.componentOptions.Ctor
,其实最终调用的也是这个构造函数。
createElm函数中与组件初始化相关的关键代码如下:
const data = vnode.data if (isDef(data)) { if (isDef(i = data.hook) && isDef(i = i.init)) i(vnode) if (isDef(i = vnode.child)) { initComponent(vnode, insertedVnodeQueue) return vnode.elm } }
init的代码如下:
function init (vnode: VNodeWithData, hydrating: boolean) { if (!vnode.child || vnode.child._isDestroyed) { const child = vnode.child = createComponentInstanceForVnode(vnode, activeInstance) child.$mount(hydrating ? vnode.elm : undefined, hydrating) } } export function createComponentInstanceForVnode ( vnode: any, // we know it's MountedComponentVNode but flow doesn't parent: any // activeInstance in lifecycle state ): Component { const vnodeComponentOptions = vnode.componentOptions const options: InternalComponentOptions = { _isComponent: true, parent, propsData: vnodeComponentOptions.propsData, _componentTag: vnodeComponentOptions.tag, _parentVnode: vnode, _parentListeners: vnodeComponentOptions.listeners, _renderChildren: vnodeComponentOptions.children } // check inline-template render functions const inlinetemplate = vnode.data.inlineTemplate if (inlineTemplate) { options.render = inlineTemplate.render options.staticRenderFns = inlineTemplate.staticRenderFns } return new vnodeComponentOptions.Ctor(options) }
经过init之后可以看到组件vnode.child
对应的就是组件的实例,且child.$el
即为组件对应的真实dom,但是实际上createElm返回的是vnode.elm,怎么回事?事实上initComponent
中做了处理
vnode.elm = vnode.child.$el
综上,组件实例化是在由虚拟dom映射为真实dom时完成的。
写到这里已经对组件机制有了初步的认识,数据的传递、父子组件通信本文并没有涉及,留到以后再看。
觉得可用,就经常来吧! 脚本宝典 欢迎评论哦! vue,巧夺天工,精雕玉琢。小宝典献丑了!
以上是脚本宝典为你收集整理的vue@2.0源码学习---组件究竟是什么全部内容,希望文章能够帮你解决vue@2.0源码学习---组件究竟是什么所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。