vue 组件分类
一. 简介
之所以写这篇文章,是因为前段时间作为面试官,陆陆续续面试了几十个人,其中一个问题就是 “vue
的组件分类都有哪些?” ,令人惊讶的是大多数人都只回答了全局组件
和局部组件
,难道vue
的组件就只是这两种吗?其实谈分类,是一定要谈分类标准的,没有分类标准的分类都是耍流氓。以下是个人总结,不喜勿喷。
二.分类
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 |
|
19 |
|
20 |
|
21 |
|
22 |
|
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 |
---|---|---|
注册方式 | 全局组件 | 局部组件 |
有无自己的状态 | 函数式(无状态)组件 | 普通(有状态)组件 |
是否动态 | 动态组件 | 普通(非动态)组件 |
是否异步 | 异步组件 | 普通(非异步)组件 |
是否循环引用 | 递归组件 | 普通(非递归)组件 |
参考链接