# Vue2.x 的组件

前言

  • 操作 dom 的不同方式:

    方式 步骤一 步骤二 步骤三 步骤四 步骤五
    javascript element dom - - -
    javascript(优化) 文档碎片 element dom - -
    react render fiber element dom -
    vue template render Vnode element dom
  • 在 Vue 项目中,组件转为 dom 的大致流程为:template --> render --> Vnode --> element

  • 据此分别在其不同阶段介绍不同方式挂载组件到页面中

# 1.template

  • template 中的代码会转化为 render 函数
  • 如果是在 vue/cli 工程中,一般会通过 vue-loader 转化为 render 函数
  • 如果是在运行时项目中,是通过 complie 函数编译转换成 render 函数

# 1.1 同步组件

# 1.1.1 sfc

最常见的组件

刷新
全屏/自适应

# 1.1.2 运行时

  • 内部靠编译函数编译,vue 打包代码会比 cli 的大一些
  • 一些特殊项目中使用 vue(如:abi、老项目、动态编辑组件)
<body>
  <div id="app"></div>
</body>
<script>
  const t1 = `
        <div>
            <p>{this.msg}</p>
          </div>
      `
  const t2 = `
        <div style="color:red">
          <container />
        </div>
  `
  new Vue({
    el: "#app",
    components: {
      container: {
        props: {},
        data() {
          return {
            msg: "这里是组件的内容",
          }
        },
        template: t1,
      },
    },
    template: t2,
    data() {
      return {}
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 1.2 异步组件

  • 通常页面很多子组件需要在一定条件下展示,页面性能有要求,可以使用异步组件对页面进行按需加载
刷新
全屏/自适应

# 1.3 functional

  • function 设置可以使组件无状态和无实例,也就是没有 data 和 this 上下文
    • 内部没有逻辑交互(无 methods 方法、也没有 mounted 等任何生命周期处理函数),没有状态修改(无 data)
    • 所有动态数据都从父组件传递进来(只有 props),数据变为常数,组件只用于展示数据
    • v-once:模板编译时 ast 会做静态标记,组件不会被更新直接跳过
刷新
全屏/自适应

router-view 就是一个典型的 functional 组件

# 1.4 abstract

  • 抽象组件和普通的组件类似,只是他们添加额外的行为,不向 DOM 呈现任何内容
  • vue 中componentslotkeep-alivetransitiontransition-group都是通过抽象组件实现的

# 1.4.1 防抖案例

<script>
import { get, debounce, set } from "loadsh"
export default {
  name: "debounce",
  abstract: true, //标记为抽象组件
  render() {
    let vnode = this.$slots.default[0] // 子组件的vnode
    if (vnode) {
      let event = get(vnode, `data.on.click`) // 子组件绑定的click事件
      if (typeof event === "function") {
        set(vnode, `data.on.click`, debounce(event, 1000))
      }
    }
    return vnode
  },
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 1.4.2 component

  • 动态切换不同类型的组件,动态表单中用的比较多
刷新
全屏/自适应

# 1.5 递归组件

递归组件就是组件在模板中调用自己,需要满足两个条件:

  • 在组件中设置一个name选项,通过这个字段拿到组件
  • 加上结束条件,防止一直递归下去,抛出max stack size exeeded错误
刷新
全屏/自适应

# 2.render

支持 jsx 写法,需要@vue/babel-preset-jsx 支持

render() {
  return <p>hello</p>
}
1
2
3

# 2.1 render 函数

  • template 写标签有时候不灵活,用 render 可以灵活处理标签

案例demo

router-link 就是一个用 render 函数写的组件,详细分析可以看后面 render 专题分析

# 2.2 render 生成

<script>
var res = Vue.compile("<div><span>{{ msg }}</span></div>")
export default {
  data: {
    msg: "hello",
  },
  render: res.render,
  staticRenderFns: res.staticRenderFns,
}
</script>
1
2
3
4
5
6
7
8
9
10

# 3.Vnode

# 3.1 $createElement

  • $createElement 最后会转到 render 函数中执行,这里使用比较灵活
刷新
全屏/自适应

# 4.element

  • 将 vue 直接挂载到指定节点上
  • 根结点在 new Vue({el:xxx})也可以直接挂载

# 4.1 extend

  • 一些项目中有些页面不是 vue 开发的(d3.js、echarts)等,某个地方需要写一些普通的页面,就可以中这种方式插入到指定 dom 元素下
刷新
全屏/自适应

extend 函数功能和 new Vue 类似,最后都会通过 this._init 初始化,通过$mount 挂载到指定节点

# 4.2 patch

  • 将目标组件插入到指定 dom 下
  • 这里实现的组件挂载功能与 Vue3.x 中的Teleport功能差不多
刷新
全屏/自适应

patch 函数执行后 Vue 内部会将 vnode 通过一系列的流程挂载到真实 dom 上

总结

通过上述案例分析可以在项目中灵活运用多种方式来挂载组件,更加灵活实现相关功能