Vue component与Vue extend
为什么要了解vue注册组件原理
在思考表单集成方案的时候,我们知道,在配置json生成form组件的时候,总会通过type与form组件一一对应来找到对应的组件,也就是说,在声明json form类型之前,需要提前开发对应的组件,或者动态挂载组件。在vue框架下,想要做到持续集成,可以将组件声明为全局组件,然后在json转化为组件时候通过name来加载对应组件。但这样做的缺陷是,无法动态给第三方组件挂载公共的属性与事件;在设计上看,将扩展组件的功能收敛至统一入口,再借助vue动态实例化组件的能力,这样做有利于后续持续集成,减少开发上的副作用,使整个库的开发思路可控。
Vue.extend
Vue.extend(extendOptions)
通过调用vue.extend可返回一个vue的子类
function extend(extendOptions){ extendOptions = extendOptions || {} // this->vue const Super = this const SuperId = Super.cid // 使用父类的id作为缓存的key // 只要父类id相同,每次调用会返回相同的子类 const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}) if (cachedCtors[SuperId]) { return cachedCtors[SuperId] } // 对name命名方式进行校验 // /^[a-zA-Z][\w-]*/ const name = extendOptions.name || Super.options.name if (process.env.NODE_ENV !== 'production' && name) { validateComponentName(name) } const Sub = function VueComponent (options) { // vue._init this._init(options) } // 继承父类构造函数 Sub.prototype = Object.create(Super.prototype) Sub.prototype.constructor = Sub Sub.cid = cid++ // 合并父类的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. // 初始化props // 将props挂载在原型对象的_props属性下 if (Sub.options.props) { initProps(Sub) } // 初始化computed // 将初始化computed挂载在原型对象下 if (Sub.options.computed) { initComputed(Sub) } // allow further extension/mixin/plugin usage // 复制父类的静态方法 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. Sub.superOptions = Super.options Sub.extendOptions = extendOptions Sub.sealedOptions = extend({}, Sub.options) // cache constructor cachedCtors[SuperId] = Sub return Sub }}复制代码
首先,同直接调用vue
构造函数实例化组件来对比,vue.extend
在开始先做来一层缓存校验,如果该extendOptions
已经存在组件的构造函数,那么会直接返回该构造函数,避免同一个组件反复构建组件生成器函数,同时使用父类的id作为缓存标时。
当未命中缓存时,会进入创建组件生成器函数的过程,首先对组件名称进行校验,名称首字母开头必须是大小写英文,后续字符支持大小写下划线(_)及间隔线(-)。
名称校验通过后,先声明一个组件对构造函数:
const Sub = function VueComponent (options) { // Vue.prototype._init this._init(options)}复制代码
让Sub函数通过原型继承父类(Vue),并将传入的extendOptions
与父类的options
配置项进行合并,然后保存父类的构造函数至super
属性。
当options
中挂载props
与computed
等属性时,需要单独进行处理,处理的内部细节与结果在后续的vue探究
文章再做深入讨论。
当通过原型继承父类,初始化props
与computed
成员属性之后,还需要继承父类的静态方法,如mixin、extend、use、component、directive、filter
等。
最后在子类构造函数上新增superOptions、extendOptions、sealedOptions
以备实例化的时候使用,再缓存该新生成子类(组件构造函数)。
总的来看,vue.extend方法本质是创建了一个Sub函数,并继承了父类(父组件或者Vue构造函数)的相关属性或者方法。
Vue.component
调用component注册一个局部或者全局组件,并设置组件的别名。
// 在Vue中,component、filter、directive是混合在一起实现的,这里拆开Vue.options['component'] = Object.create(null)Vue.component = function(id, definition){ if(!definition){ return this.options['components'][id] }else{ if(isPlainObject(definition)){ definition.name = definition.name || id definition = Vue.extend(definition) } this.options['components'][id] = definition return definition }}复制代码
component方法本质是调用extend方法构造一个子类,并将该子类保存在options的components的对应的key下面,在SFC
中注册的。
文章内容如有错误,敬请谅解,希望可以不吝赐教
转载请注明出处