详解vue 组件注册


Posted in Vue.js onNovember 20, 2020

一、了解组件注册的两种方式

1.1 全局组件的注册方法

//main.js 
import Vue from 'vue' 
import App from './App' 
import router from './router' 
Vue.config.productionTip = false 

let Hello = { 
  name: 'hello', 
  template: '这是全局组件hello' 
} 
Vue.component('hello', Hello) 
new Vue({ 
  el: '#app', 
  router, 
  components: { App }, 
  template: '' 
})

上面我们就通过Vue.component()注册了一个全局组件hello,接下来分析源码实现的时候也是基于这个例子来进行的。

1.2 局部组件的注册

<template>
 <div id="app">
  <img src="./assets/logo.png">
  <HelloWorld/>
 </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
 name: 'App',
 components:{
  HelloWorld
 }
}
</script>

像这样就注册了一个HelloWorld的局部组件。

二、全局组件注册的源码

1.Vue初始化的时候,会调用initGlobalAPI()

//【代码块1】
//代码所在文件:src/core/global-api/index.js
export function initGlobalAPI(Vue: GlobalAPI){
  //...省略其他无关代码
  initAssetRegisters(Vue)
  //这个方法就是用于组件注册的方法
}

2.在initAssetRegisters()方法中执行组件的定义

//【代码块2】
//代码所在文件:src/core/global-api/assets.js
export function initAssetRegister(Vue){
  ASSET_TYPES.forEach(type=>{
    //ASSET_TYPES包括component、directive、filter
    Vue[type] = function(id, definition){
      //...一些条件判断
      if(type === 'component' && isPlainObject(definition)){
        definition.name = definition.name || id 
        definition = this.options._base.extend(definition) 
        //将definition转换为一个继承于Vue的构造函数
      }
      //...其他类型的处理
      
      this.options[type+'s'][id] = definition //将这个构造函数挂载到Vue.options.components上
      return definition
    }
  })
}

此时,我们可以单步调试一下我们上面的例子,来看一下definition一开始是什么,以及执行挂载后Vue.options变成了什么样子:

a.definition: 其实传入的时候就是我们一开始定义的全局组件的具体内容

详解vue 组件注册

b.Vue.options: 可以看到我们定义的全局组件hello已经存在在Vue.options.components上了

详解vue 组件注册

3.实例化组件的时候,代码会执行到Vue.prototype._init()上面

//【代码块3】
//代码所在文件:src/core/instance/init.js
Vue.prototype._init = function(options){
  //..省略其他无关代码
  if(options && options._isComponent){ //组件
    initInternalComponent(vm, options)
  }else{ //非组件
    vm.$options = mergeOptions(
      resolveConstructorOptions(vm.constructor),
      options||{},
      vm
    )
  }
}

这里将自己定义的组件的options与Vue.options做了一个合并,并且赋值给了vm.$options,而通过【代码块2】我们可以知道全局组件的构造函数已经被放在了Vue.options.components上,所以经过这一步,vm.$options.components上面也有了全局组件的构造函数。所以现在在任意组件都能拿到全局组件,因为任何组件初始化的时候都会执行这个合并。

我们可以通过单步调试上面的例子看一下现在的vm.$options上面有些什么

详解vue 组件注册

4.在创建vnode的过程中,会执行_createElement方法

//【代码块4】
//代码所在文件:src/core/vdom/create-element.js
export function _createElement(context, tag, data, children, normalization){
  if(typeof tag === 'string'){
    //...
    if(config.isReservedTag(tag)){
      //...保留的html标签
    }else if(isDef(Ctor = resolveAsset(context.$options, 'component', tag))){
      //已经注册过的全局组件
      vnode = createComponent(Ctor, data, context, children, tag)
    }else{
      //不是内置标签也不是已经注册过的组件,就创建一个全新的vnode
      vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
       )
    }
  }
}

上面代码中有一个比较重要的方法resolveAsset(),用于判断在context.$options.compononts(即vm.$options.components)上面是否能找到这个组件的构造函数,如果能找到,返回这个构造函数,(具体方法见【代码块5】)根据【代码块3】我们可以知道如果这个组件是全局注册的组件,那么我们就可以得到这个构造函数,并进入这个else if判断,通过createComponent()得到vnode。

5.上面四步已经实现了整个流程,现在补充看一下resolveAsset()

//【代码块5】
//代码所在文件:src/core/utils/options.js
export function resolveAsset(options, type, id, warnMissing){
   //options即上面调用的时候传入的context.$options, 
   //由【代码块3】,vm.$options是由我们自定义的options以及Vue上的options合并而来的
   //type现在是components
    
   const assets = options[type]
   // check local registration variations first
   if (hasOwn(assets, id)) return assets[id]
   const camelizedId = camelize(id)
   if (hasOwn(assets, camelizedId)) return assets[camelizedId]
   const PascalCaseId = capitalize(camelizedId)
   if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
   // fallback to prototype chain
   const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
   if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
    warn(
     'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
     options
    )
   }
   return res
 }

先通过 const assets = options[type] 拿到 assets,然后再尝试拿 assets[id],这里有个顺序,先直接使用 id 拿,如果不存在,则把 id 变成驼峰的形式再拿,如果仍然不存在则在驼峰的基础上把首字母再变成大写的形式再拿,如果仍然拿不到则报错。这样说明了我们在使用 Vue.component(id, definition) 全局注册组件的时候,id 可以是连字符、驼峰或首字母大写的形式。

三、局部组件的注册

1.extend()

组件在执行render()的时候,会执行createComponent函数,在这个函数里面会执行extend()函数生成一个构造函数,也是在这个extend()函数中,执行了一个options的合并

//【代码块5】
//代码所在文件:src/core/global-api/extend.js
Vue.entend = function(extendOptions){
  //...
  Sub.options = mergeOptions(
     Super.options, //Vue的options
     extendOptions //定义组件的那个对象
  )
  //...
}

可以看出这里是将自己传入的options(即定义组件的那个对象)与Vue.options合并,然后放到Sub.options上,同时,因为Sub.options上面合并了Vue的options,所以组件里面也可以拿到全局注册的组件。

2.组件初始化

//【代码块6(同代码块3)】
//代码所在文件:src/core/instance/init.js
Vue.prototype._init = function(options){
  //..  
  if(options && options._isComponent){
    initInternalComponent(vm, options)
  }else{
    vm.$options = mergeOptions(
      resolveConstructorOptions(vm.constructor),
      options||{},
      vm
    )
  }
}

组件初始化的过程中会进入if判断语句,执行initInternalComponent()

3.initInternalComponent()

//【代码块7】
//代码所在文件:src/core/instance/init.js
export function initInternalComponent (vm: Component, options: InternalComponentOptions) {
 const opts = vm.$options = Object.create(vm.constructor.options) 
 //vm.constructor即为Sub,在代码块5中,我们已经将局部组件放在了Sub.options上
 //所以这里将局部组件的构造函数放在了vm.$options上
 //这样在执行【代码块4】的时候同样也能通过resolveAsset得到局部注册组件的构造函数
 const parentVnode = options._parentVnode
 opts.parent = options.parent
 opts._parentVnode = parentVnode

 //将componentOptions里面的别的属性赋值给opts
 const 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
 }
}

四、总结

由于全局注册的组件是将组件的构造函数扩展到了Vue.options.components上,而组件在初始化的时候都会将自身options与Vue.options合并,扩展到当前组件的vm.$options.components下,所以全局组件能在任意组件被使用。而局部注册的组件是将组件的构造函数扩展到了当前组件的vm.$options.components下,所以只能在当前组件使用。

以上就是详解vue 组件注册的详细内容,更多关于vue 组件注册的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
详解vue 组件注册
Nov 20 Vue.js
详解Vue中的自定义指令
Dec 07 Vue.js
vue实现两个区域滚动条同步滚动
Dec 13 Vue.js
vue使用element-ui实现表单验证
Dec 13 Vue.js
vue实现图片裁剪后上传
Dec 16 Vue.js
vue+vant 上传图片需要注意的地方
Jan 03 Vue.js
vue自定义组件实现双向绑定
Jan 13 Vue.js
vue3 watch和watchEffect的使用以及有哪些区别
Jan 26 Vue.js
如何管理Vue中的缓存页面
Feb 06 Vue.js
详细聊聊vue中组件的props属性
Nov 02 Vue.js
Vue操作Storage本地化存储
Apr 29 Vue.js
vue如何清除浏览器历史栈
May 25 Vue.js
vue-drawer-layout实现手势滑出菜单栏
Nov 19 #Vue.js
Vue 打包的静态文件不能直接运行的原因及解决办法
Nov 19 #Vue.js
如何使用 vue-cli 创建模板项目
Nov 19 #Vue.js
深入了解Vue3模板编译原理
Nov 19 #Vue.js
vue 获取到数据但却渲染不到页面上的解决方法
Nov 19 #Vue.js
vue 插槽简介及使用示例
Nov 19 #Vue.js
详解Vue的mixin策略
Nov 19 #Vue.js
You might like
php下使用无限生命期Session的方法
2007/03/16 PHP
PHP 删除一个目录及目录下的所有文件的函数代码
2010/05/26 PHP
php计算两个整数的最大公约数常用算法小结
2015/03/05 PHP
PHP简单判断字符串是否包含另一个字符串的方法
2016/03/25 PHP
Laravel框架路由和控制器的绑定操作方法
2018/06/12 PHP
JavaScript使用prototype定义对象类型
2007/02/07 Javascript
jQuery使用手册之二 DOM操作
2007/03/24 Javascript
JavaScript经典效果集锦
2010/07/06 Javascript
基于jQuery的获得各种控件Value的方法
2010/11/19 Javascript
跨浏览器的事件对象介绍
2012/06/27 Javascript
JavaScript实现计算字符串中出现次数最多的字符和出现的次数
2015/03/12 Javascript
jQuery的end()方法使用详解
2015/07/15 Javascript
使用jQuery的easydrag插件实现可拖动的DIV弹出框
2016/02/19 Javascript
javascript使用 concat 方法对数组进行合并的方法
2016/09/08 Javascript
浅谈javascript中的 “ &amp;&amp; ” 和 “ || ”
2017/02/02 Javascript
JavaScript运动框架 解决速度正负取整问题(一)
2017/05/17 Javascript
inner join 内联与left join 左联的实例代码
2017/09/18 Javascript
js array数组对象操作方法汇总
2019/03/18 Javascript
node-red File读取好保存实例讲解
2019/09/11 Javascript
[02:48]DOTA2超级联赛专访海涛:你们的选择没有错
2013/06/07 DOTA
python开发简易版在线音乐播放器
2017/03/03 Python
利用Python爬取微博数据生成词云图片实例代码
2017/08/31 Python
python3-flask-3将信息写入日志的实操方法
2019/11/12 Python
在Python IDLE 下调用anaconda中的库教程
2020/03/09 Python
python boto和boto3操作bucket的示例
2020/10/30 Python
python中用ggplot绘制画图实例讲解
2021/01/26 Python
Philosophy美国官网:美国美容品牌
2016/08/15 全球购物
小蚁科技官方商店:YI Technology
2019/08/23 全球购物
中学家长会邀请函
2014/02/03 职场文书
林肯就职演讲稿
2014/05/19 职场文书
民主评议党员个人总结
2015/02/13 职场文书
内勤岗位职责范本
2015/04/13 职场文书
百万英镑观后感
2015/06/09 职场文书
三八节活动主持词
2015/07/04 职场文书
加强党性修养心得体会
2016/01/21 职场文书
Nginx 安装SSL证书完成HTTPS部署
2022/04/28 Servers