结合源码谈谈对vue组件化的理解

1.组件定义

image.png

全局组件和单文件组件两种方式

2.1 全局组件

源码分析1:src\core\global-api\assets.js

//是component,filter,directive三个的综合方法
export function initAssetRegisters (Vue: GlobalAPI) {
  /**
   * Create asset registration methods.
   */
  //['component','filter','directive']循环遍历这三个方法
  ASSET_TYPES.forEach(type => {
    //动态附加三个静态方法,然后分别去定义
    Vue[type] = function (
      id: string,
      definition: Function | Object
    ): Function | Object | void {
      if (!definition) {
        return this.options[type + 's'][id]
      } else {
        /* istanbul ignore if */
        if (process.env.NODE_ENV !== 'production' && type === 'component') {
          validateComponentName(id)
        }
        //如果def是对象,那么definition就是组件的配置对象
        if (type === 'component' && isPlainObject(definition)) {
          //定义组件name
          definition.name = definition.name || id
          //extend创建组件构造函数,def变成了构造函数
          definition = this.options._base.extend(definition)
        }
        if (type === 'directive' && typeof definition === 'function') {
          definition = { bind: definition, update: definition }
        }
        //注册 this.options[components][comp]=Ctor
        //在当前vue的选项中加上components选项,这样的话
        //定义全局组件,将来会被继承在所有的子组件里
        this.options[type + 's'][id] = definition
        return definition
      }
    }
  })
}

再看看extend,js


image.png

1.2 单文件组件

其实写的并不是一个组件而是组件的一个配置对象。

vue-loader会编译template为render函数,最终导出的依然是组件配置对象。
(style里面的代码会用style-loader,css里的会用css-loader处理成css文件)
所以我们现在写的.vue文件最终都会导出为js对象,这些对象都是组件的配置对象而不是组件的构造函数。

2.组件化优点(提高维护性,测试性,复用性)

源码位置:lifecycle.js-mountComponent()
组件、watcher、渲染函数和更新函数之间的关系

mountComponent()定义了组件和watcher一一对应的关系。

//组件的实例在执行$mount的时候会调用的方法
export function mountComponent (
//创建一个组件的时候也创建了一个与之对应的watcher实例,
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
...
 updateComponent = () => {
      vm._update(vm._render(), hydrating)
      //调用组件的更新函数和渲染函数
    }

创建一个组件的时候也创建了一个与之对应的watcher实例,
如果这个组件中的数据发生了变化,其实只会调用该组件的渲染函数
如果在应用程序中,合理切割组件的力度,比如说将一个经常发生数据变化的一块内容,切割成一个组件,将来频繁执行的渲染函数和更新函数和打补丁的范围就变得更小了.所以把数据变化频繁的提取为组件,可以有效地提升性能。

3.组件化的实现

构造函数,src\core\global-api\extend.js
实例化及挂载,src\core\vdom\patch.js-createElm()

组件的构造函数通过extend来生成,extend执行的时间点不完全相同,如果说是全局注册组件,是立刻执行,甚至在当前实例化之前就执行了。如果是一个局部组件,声明在一个单文件组件中,可能在运行时的某个时刻才会去执行extend方法,具体是什么时刻可以看一下patch的源码。
我们都知道dom是一个树,那虚拟dom也是一个树,那么这棵树的起始点一定有一个根节点,实际上根节点就是起始点,那么根节点一定不是一个组件,而是vue的根实例。那就不存在自定义组件这些东西。
当vue实例化的时候,它执行挂载之后,会有一个初始化过程,要执行打补丁patch,在此过程中会执行createElm方法,因为根节点需要创建相对应的一个真实的dom,
看看createElm方法中有几行代码是和组件化息息相关的。

//如果要创建的是组件,走下面的流程,如果传入的是vue实例,就不执行
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
      return
    }

···javascript
//这里createComponent是把前面的那个执行的结果vnode转化为真实的dom
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
//获取管理钩子函数
let i = vnode.data
if (isDef(i)) {
const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
//存在init钩子,则执行之创建实例并挂载
//这个初始化函数是在create-component.js中定义的
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode, false /* hydrating */)
}
//如果组件实例存在
if (isDef(vnode.componentInstance)) {
//属性初始化
initComponent(vnode, insertedVnodeQueue)
//dom插入操作
insert(parentElm, vnode.elm, refElm)
if (isTrue(isReactivated)) {
reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
}
return true
}
}
}

> create-component.js
···javascript
//默认组件管理钩子
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 {
//不是keep-alive而是要重新创建实例的时候
      //创建组件实例
      const child = vnode.componentInstance = createComponentInstanceForVnode(
        vnode,
        activeInstance
      )
//子组件里面的子元素或者子组件也进行了初始化
      //创建完成并挂载(挂载后才能从虚拟dom变成真实dom)
      child.$mount(hydrating ? vnode.elm : undefined, hydrating)
    }
  },
···
实例化的过程是自上而下的,从根往叶子节点进行创建,但是由于子节点的创建会先执行,子节点创建后就立刻挂载了,所以挂载是从下而上的。

# 4.总结
+ 基础部分
1.组件是独立和可复用的代码组织单元。组件系统是Vue核心特性之一, 它使开发者使用小型、独立和通常可
复用的组件构建大型应用;
2.组件化开发能大幅提高应用开发效率、测试性、复用性等; 
+ 落地
3.组件使用按分类有:
页面组件:用路由来导航一些页面组件,复用性不那么强,但是是组织页面直接来回切换的必备组件
业务组件:登录组件,购物车里的徽章组件,有很强的业务性和通用性
通用组件:按钮,表单,输入框等等;
+ 特点
4. vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于
VueComponent,这个类扩展于Vue,扩展的过程中,会继承于vue中已经有的一些选项。
5. vue中常见组件化技术有:属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;
+ 注意事项
6.合理的划分组件,有助于提升应用性能;
切割出经常发生数据变化的组件,将来watcher去更新的时候,只会重新渲染它对应的组件,而不会影响其他的地方。

7.组件应该是高内聚、低耦合的;
高内聚:组件本来就是一个独立功能单元,功能应该是单一的,独立的,这样组件才更容易复用。
低耦合:不应该与其他组件有太多的耦合关系。

8.遵循单向数据流的原则。
通过属性把值传进来或者通过vuex的方式直接把值注入进来。如果要改变值,通过事件派发出去,然后让父级接管这个事件,从而修改数据。如果使用vuex,使用commit或者dispatch的方式,让vuex去改变这个值。而不是组件自己去改数据。让组件功能更单一。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容