什么是组件?
组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。
所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。
Vue可以有全局注册和局部注册两种方式来注册组件。
全局注册
注册方式
全局注册有以下两种注册方式:
通过Vue.component 直接注册。
Vue.component('button-counter', { //data选项必须是一个函数 data: function () { return { count: 0 } }, template:'#clickBtn' })
通过Vue.extend来注册。
var buttonComponent = Vue.extend({ name:'button-counter', data: function () { return { count: 0 } }, template:'#clickBtn' }); Vue.component('button-counter', buttonComponent);
具体过程
Vue初始化时,initGlobalAPI通过调用initAssetRegisters()进行组件注册。
function initAssetRegisters (Vue) { // 创建组件注册的方法 // ASSET_TYPES在Vue内部定义,var ASSET_TYPES = ['component','directive','filter']; ASSET_TYPES.forEach(function (type) { Vue[type] = function ( id, definition ) { //这里的definition指的是定义(Function或Object),是函数或者对象 //如果definition不存在,直接返回options内type和id对应的 //这里的options是指全局的组件,指令和过滤器,见图一 if (!definition) { return this.options[type + 's'][id] } else { /* istanbul ignore if */ if ("development" !== 'production' && type === 'component') { validateComponentName(id); } // 如果是component(组件)方法,并且definition是对象 if (type === 'component' && isPlainObject(definition)) { definition.name = definition.name || id; //通过this.options._base.extend方法(也就是Vue.extend方法)将定义对象转化为构造器。 //Vue.options._base = Vue; definition = this.options._base.extend(definition); } if (type === 'directive' && typeof definition === 'function') { definition = { bind: definition, update: definition }; } // 将构造器赋值给 this.options[‘component'+ 's'][id] //全局的组件,指令和过滤器,统一挂在vue.options上。在init的时候利用mergeOptions合并策略侵入实例,供实例使用。 this.options[type + 's'][id] = definition; return definition } }; }); }
图一:
initAssetRegisters里面通过this.options._base.extend方法将定义对象转化为构造器,而options._base.extend其实就是Vue.extend。接下来我们就看一下Vue.extend做了什么。
Vue.extend = function (extendOptions) { extendOptions = extendOptions || {}; var Super = this; var SuperId = Super.cid; //组件缓存 var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); //如果组件已经被缓存在extendOptions上则直接取出 if (cachedCtors[SuperId]) { return cachedCtors[SuperId] } //如果有name属性,检验name拼写是否合法 var name = extendOptions.name || Super.options.name; if ("development" !== 'production' && name) { validateComponentName(name); } var Sub = function VueComponent (options) { this._init(options); }; //将vue上原型的方法挂在Sub.prototype中,Sub的实例同时也继承了vue.prototype上的所有属性和方法。 //关于 prototype的学习:http://www.cnblogs.com/dolphinX/p/3286177.html Sub.prototype = Object.create(Super.prototype); //Sub构造函数修正,学习于https://www.cnblogs.com/SheilaSun/p/4397918.html Sub.prototype.constructor = Sub; Sub.cid = cid++; //通过vue的合并策略合并添加项到新的构造器上 Sub.options = mergeOptions( Super.options, extendOptions ); //缓存父构造器 Sub['super'] = Super; // 处理props和computed响应式配置项 if (Sub.options.props) { initProps$1(Sub); } if (Sub.options.computed) { initComputed$1(Sub); } // allow further extension/mixin/plugin usage Sub.extend = Super.extend; Sub.mixin = Super.mixin; Sub.use = Super.use; //在新的构造器上挂上vue的工具方法 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); //缓存组件构造器在extendOptions上 cachedCtors[SuperId] = Sub; return Sub };
vue.extend返回了一个带有附加Option的vue构造器。这个构造器被命名为Sub,等待render时候初始化。
initAssetRegisters完成之后,options下挂载了全局组件button-counter,如图:
接下来调用new Vue()渲染vue整体的生命周期
局部注册
如果不需要全局注册,或者是让组件使用在其它组件内,可以用选项对象的components属性实现局部注册。
注册方式
new Vue({ el: '#components-demo', components:{ 'button-counter':{ template:'#clickBtn', data: function () { return { count: 0 } } } } })
具体过程
Vue局部组件注册也是通过initAssetRegisters()方法调用Vue.extend,不同的是在createComponent()时,initMixin()里面有判断
if (options && options._isComponent) { //因为Vue动态合并策略非常慢,并且内部组件的选项都不需要特殊处理。 //调用initInternalComponent快捷方法,内部组件实例化。 initInternalComponent(vm, options); } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ); }
function initInternalComponent (vm, options) { var opts = vm.$options = Object.create(vm.constructor.options); // 这样做是因为它比动态枚举更快。 var parentVnode = options._parentVnode; opts.parent = options.parent; opts._parentVnode = parentVnode; var vnodeComponentOptions = parentVnode.componentOptions; opts.propsData = vnodeComponentOptions.propsData; opts._parentListeners = vnodeComponentOptions.listeners; opts._renderChildren = vnodeComponentOptions.children; opts._componentTag = vnodeComponentOptions.tag; if (options.render) { opts.render = options.render; opts.staticRenderFns = options.staticRenderFns; } }
opts的结构如图所示:
局部注册和全局注册不同的是,只有该类型的组件才可以访问局部注册的子组件,而全局注册是扩展到 Vue.options 下。在所有组件创建的过程中,都会从全局的 Vue.options.components 扩展到当前组件的 vm.$options.components 下,这就是全局注册的组件能被任意使用的原因。
组件名定义
定义组件名的方式有两种:
使用短横线形式
Vue.component('button-counter', {})
引用这个自定义元素时,必须用 <button-counter></button-counter>
使用驼峰的形式
Vue.component('buttonCounter', { })
此时在引用这个自定义元素时,两种命名方法都可以使用。也就是说,<buttonCounter>
和 <button-counter>
都是可行的。
注意,直接在 DOM (即非字符串的模板) 中使用时只有短横线是有效的。如下:
<div id="components-demo"> <button-counter></button-counter> </div>
可参考:https://www.jb51.net/article/144050.htm
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。