5.1 组件基础:注册与生命周期 本节摘要:组件的注册分全局与局部两种,日常工程几乎只用局部(单文件组件默认)。本节重点讲清生命周期钩子的真实触发顺序——包括父子组件嵌套时的顺序这一高频盲区——以及 created 与 mounted、beforeUnmount 时机等常见误用的纠正。 学习目标 阅读完本节,你应当能够: 说明全局注册与局部注册的取舍,解释局部注册为何是工程默认; 准确排列单个组件的生命周期顺序,并扩展到父子嵌套场景; 把"发请求、操作 DOM、清理定时器"放到正确的钩子里; 用 data 必须是函数这一约束解释组件复用时的状态隔离。 一、注册:全局方便,局部可控 全局注册的问题是不可摇树:只要注册了,哪怕整个应用只用一次,打包产物里它都在;且命名冲突风险高、来源不直观。
本节摘要:组件的注册分全局与局部两种,日常工程几乎只用局部(单文件组件默认)。本节重点讲清生命周期钩子的真实触发顺序——包括父子组件嵌套时的顺序这一高频盲区——以及 created 与 mounted、beforeUnmount 时机等常见误用的纠正。
阅读完本节,你应当能够:
// 全局注册:任何组件模板里都能直接用,无需导入 app.component('my-button', { template: '<button class="btn"><slot></slot></button>' }); // 局部注册(单文件组件的默认形态) import MyButton from './MyButton.vue'; export default { components: { MyButton }, template: '<my-button>确定</my-button>' };
全局注册的问题是不可摇树:只要注册了,哪怕整个应用只用一次,打包产物里它都在;且命名冲突风险高、来源不直观。因此全局注册只留给极少数真正全局的基础组件(比如埋点包装过的根组件),其余一律局部。Vue 3 的 script setup 写法里导入即注册,连 components 字段都省了:
<script setup> import MyButton from './MyButton.vue'; // 模板里直接可用 </script> <template> <my-button>确定</my-button> </template>
单文件组件的三段结构——template 描述视图、script 组织逻辑、style 收纳样式——与渲染管线一一对应:template 进编译器,script 里的数据进响应式系统,两者汇合于渲染函数。
另一个绕不开的规矩:data 必须是函数。
// Vue 2 中错误写法:data 是对象 data: { count: 0 } // 正确写法 data() { return { count: 0 }; }
原因朴素:组件可能被实例化多次,data 若是共享对象,三个同名组件会共用一份 count,你加一全都加。函数每次调用返回全新对象,状态自然隔离。Vue 3 组合式写法里这个规矩内化了——setup 本身就是每个实例执行一次的函数。

几个高频误用逐个纠正:
请求放 created 还是 mounted? 大多数场景两者皆可——发请求不依赖 DOM。放 mounted 的理由通常是希望骨架屏先出来;放 created 的理由是更早发出、少等一帧。真正的分界是:需要 DOM 的操作必须 mounted(初始化图表、量文本高度、聚焦),纯数据请求放哪个都行,团队统一即可。
在 updated 里改自身数据。 数据变了触发更新,更新完又改数据,又触发更新——无限循环警告就是这么来的。updated 的合法用途是"读取更新后 DOM 做联动",且应判断与自己相关的状态是否真的变了。
忘记清理。 定时器、事件监听、第三方库实例,凡是挂到组件外部的资源,必须在卸载钩子成对清理。路由切走后定时器还在跑、控制台持续报错的经典事故,源头都是漏了这一步:
mounted() { this.timer = setInterval(this.poll, 3000); }, beforeUnmount() { clearInterval(this.timer); // 成对出现,缺一不可 }
一道真实面试题:父 created、子 created、父 mounted、子 mounted,顺序如何?答案:父 beforeCreate → created → beforeMount → 子 beforeCreate → created → beforeMount → mounted → 父 mounted。
原因用管线一句话讲透:父渲染到子组件标签时,才会去创建子实例;子树渲染完(子 mounted),父的挂载才算完成。所以 created 沿"父先",mounted 沿"子先"。记住这个推导,不用背口诀。
最后给一个团队协作视角的补充:生命周期钩子虽然属于组件内部实现,但"哪类逻辑放哪个钩子"值得写成团队约定。原因很实际——没有约定时,同一段初始化逻辑在不同人手里会散落在 created、mounted 甚至 watch 的 immediate 里,接手的人要读三个位置才能拼出完整行为;有了"请求默认 created、DOM 操作必须 mounted、外部资源必须成对清理"这三条,代码评审时一眼就能看出违规。规范条目不在多,在于每条都能指向一类真实事故。
组件立起来了,接下来解决组件之间怎么传数据——通信。