vue 组件分类

一. 简介

之所以写这篇文章,是因为前段时间作为面试官,陆陆续续面试了几十个人,其中一个问题就是 “vue 的组件分类都有哪些?” ,令人惊讶的是大多数人都只回答了全局组件局部组件,难道vue的组件就只是这两种吗?其实谈分类,是一定要谈分类标准的,没有分类标准的分类都是耍流氓。以下是个人总结,不喜勿喷。

本文 github 源码地址

二.分类

1.按组件注册方式分类:可以分为全局组件局部组件

全局组件


a. 全局组件注册

1
Vue.component("my-component-name", {
2
  /****/
3
});

b. 使用

1
<my-component-name></my-component-name>

c. 什么情况下需要将组件注册为全局组件?

一般是一些基础组件,频繁(3 次以上)需要用到的,需要全局注册。比如常用的 dialog 组件,search 组件,toast 组件,message 组件等。

局部组件


a. 局部组件注册

1
import ComponentA from "./ComponentA.vue";
2
export default {
3
  components: {
4
    ComponentA
5
  }
6
  // ...
7
};

b. 使用

1
<component-a></component-a>

c. 什么情况下需要将组件注册为局部组件?

一般情况下的组件都应该是局部组件,这样会极大的减少构建应用后的代码体积,但是对于频繁使用的组件就显得麻烦了,所以建议,组件使用频率低,组件比较大的时候注册为局部组件。比如 table 组件,chart 组件等

2.按组件有无自己的状态分类:可以分为函数式(无状态)组件普通(无状态)组件

a. 函数式组件

新建文件

src/components/FunctionComponent/index.vue

functional字段标记该组件是一个函数式组件

函数式组件里面用的是 jsx 的语法

1
<script>
2
  import OtherComponent from "./other";
3
  export default {
4
    name: "FunctionComponent",
5
    functional: true,
6
    components: {
7
      OtherComponent
8
    },
9
    props: {
10
      title: {
11
        type: String,
12
        required: true
13
      }
14
    },
15
    render(h, { props }) {
16
      const { title } = props;
17
      return (
18
        <div>
19
          <p> 我是函数式组件</p>
20
          <p>props {title}</p>
21
          <OtherComponent otherInfo={title} title="我是函数式组件的其他组件" />
22
        </div>
23
      );
24
    }
25
  };
26
</script>

src/components/FunctionComponent/other.vue

基于模板的函数式组件 template 标签要标记 functional

1
<template functional>
2
  <div>
3
    {{ props.title }}
4
    <p>
5
      otherInfo {{ props.otherInfo }}
6
    </p>
7
  </div>
8
</template>
9
<script>
10
  export default {
11
    functional: true,
12
    props: {
13
      title: {
14
        default: "",
15
        type: String
16
      },
17
      otherInfo: {
18
        default: "",
19
        type: String
20
      }
21
    }
22
  };
23
</script>

b. 使用

1
import FunctionComponent from "./components/FunctionComponent/index";
2
3
export default {
4
  components: {
5
    FunctionComponent
6
  }
7
};
1
<function-component title="函数式组件" />

c. 什么情况下需要将组件写为函数式组件?

一般是无状态 (没有响应式数据)的组件可以注册成函数式组件,好像不用函数式组件也可以呀,为啥要注册成函数式组件?

当一个组件是一个函数式组件的时候,它没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数,所以渲染开销也低很多。

3.按组件是否动态分类:可以分为动态组件普通(非动态)组件

a. 动态组件

新建文件

src/components/DynamicComponent/1.vue

1
<template>
2
  <div>
3
    动态组件1
4
  </div>
5
</template>

src/components/DynamicComponent/1.vue

1
<template>
2
  <div>
3
    动态组件1
4
  </div>
5
</template>

b. 使用

1
<button @click="toggle('1')">点击切换组件1</button>
2
<button class="btn" @click="toggle('2')">点击切换组件2</button>
3
<component :is="currentTabComponent"></component>
1
import DynamicComponent1 from "./components/DynamicComponent/1";
2
import DynamicComponent2 from "./components/DynamicComponent/2";
3
export default {
4
  components: {
5
    DynamicComponent1,
6
    DynamicComponent2
7
  },
8
  data() {
9
    return {
10
      currentTabComponent: "DynamicComponent1"
11
    };
12
  },
13
  methods: {
14
    toggle(type) {
15
      this.currentTabComponent = "DynamicComponent" + type;
16
    }
17
  }
18
};

可能大家见到过最多是这样子的,有没有很熟悉

1
<button @click="toggle">点击切换组件1</button>
2
<dynamic-component1 v-if="show" />
3
<dynamic-component2 v-else />
1
import DynamicComponent1 from "./components/DynamicComponent/1";
2
import DynamicComponent2 from "./components/DynamicComponent/2";
3
export default {
4
  components: {
5
    DynamicComponent1,
6
    DynamicComponent2
7
  },
8
  data() {
9
    return { show: false };
10
  },
11
  methods: {
12
    toggle() {
13
      this.show = !this.show;
14
    }
15
  }
16
};

c. 什么情况下需要将组件写为动态组件?

一般是是需要组件之间切换的情况下,好像不用动态组件也可以呀,为啥要动态组件?

可能当你写导入 DynamicComponent1 从 1 写到 10 的时候,然后 template 再写 DynamicComponent 10 次的时候,它的好处就出来了。

4.按组件是否异步分类:可以分为异步组件普通(非异步)组件

a.异步组件

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染

新建文件

src/components/AsyncComponent.vue

1
<template>
2
  <div>
3
    <ul>
4
      <li v-for="item in list" :key="item.name">{{ item.name }}</li>
5
    </ul>
6
  </div>
7
</template>
8
<script>
9
  export default {
10
    name: "AsyncComponents",
11
    components: {},
12
    data() {
13
      return { list: [] };
14
    },
15
    created() {
16
      this.getList();
17
    },
18
    methods: {
19
      getList() {
20
        setTimeout(() => {
21
          const data = [
22
            { name: "你好", time: "2019-01-01 10:10:55" },
23
            { name: "世界", time: "2012012-01 12:20:00" }
24
          ];
25
          this.list = data;
26
        }, 2000);
27
      }
28
    }
29
  };
30
</script>

b. 使用

1
<async-component />
1
import AsyncComponent from "./components/AsyncComponent";
2
export default {
3
  components: {
4
    AsyncComponent
5
  }
6
};

c. 什么情况下需要将组件写为异步组件?

一般是需要从服务器加载数据的组件,且需要多个地方使用的,因为它会把结果缓存起来供未来重渲染。

还有就是大家使用最多的 是在 Vue Router 里使用,异步组件结合 Webpack 的代码分割功能,可以轻松实现路由组件的懒加载。

5.按组件是否循环引用分类:可以分为递归组件普通(非递归)组件

a.递归组件

组件是可以在它们自己的模板中调用自身的。不过它们只能通过 name 选项来做这件事。

新建文件

src/components/RecursiveComponent.vue

1
<template>
2
  <div>
3
    <ul>
4
      <li v-for="item in list" :key="item.name">
5
        <span>{{ item.name }}</span>
6
        <recursive-component v-if="item.children" :list="item.children" />
7
      </li>
8
    </ul>
9
  </div>
10
</template>
11
<script>
12
  export default {
13
    name: "RecursiveComponent",
14
    props: {
15
      list: {
16
        default: () => [],
17
        type: Array
18
      }
19
    }
20
  };
21
</script>

b. 使用

1
<recursive-component :list="list" />
1
import RecursiveComponent from "./components/RecursiveComponent";
2
export default {
3
  components: {
4
    RecursiveComponent
5
  },
6
  data() {
7
    return {
8
      list: [
9
        {
10
          name: "1",
11
          children: [
12
            {
13
              name: "1-1"
14
            },
15
            {
16
              name: "1-2",
17
              children: [
18
                {
19
                  name: "1-2-1"
20
                },
21
                {
22
                  name: "1-2-2"
23
                }
24
              ]
25
            }
26
          ]
27
        },
28
        {
29
          name: "2",
30
          children: [
31
            {
32
              name: "2-1",
33
              children: [
34
                {
35
                  name: "2-1-1"
36
                },
37
                {
38
                  name: "2-1-2",
39
                  children: [
40
                    {
41
                      name: "2-1-2-1"
42
                    },
43
                    {
44
                      name: "2-1-2-2"
45
                    }
46
                  ]
47
                }
48
              ]
49
            }
50
          ]
51
        }
52
      ]
53
    };
54
  }
55
};

c. 什么情况下需要将组件写为递归组件?

一般是组件需要调用自身的时候,比如树组件,侧边栏路由组件等

三.总结

分类标准 分类 1 分类 2
注册方式 全局组件 局部组件
有无自己的状态 函数式(无状态)组件 普通(有状态)组件
是否动态 动态组件 普通(非动态)组件
是否异步 异步组件 普通(非异步)组件
是否循环引用 递归组件 普通(非递归)组件

参考链接

1.https://cn.vuejs.org/

2.https://router.vuejs.org/zh/