- 核心作用:确保 DOM 元素的唯一性,优化 diff 算法的更新效率。
- 有 key 的情况:
key 作为节点的唯一标识,diff 算法会根据 key 精准匹配新旧节点,只更新变化的部分(如新增、删除、移动),避免不必要的 DOM 重绘。
- 无 key 的情况:
Vue 会采用 “就地复用” 策略,通过节点位置而非标识判断是否复用,可能导致节点状态混乱(如表单输入值异常),且更新时可能重复创建 / 销毁节点,性能损耗更大。
-
渲染过程:
- 通过
Vue.extend()将组件选项转换为构造函数;
- 实例化组件,执行初始化(
init);
- 调用
vm.$mount()挂载组件,生成虚拟 DOM(vm._render());
- 将虚拟 DOM 转换为真实 DOM(
vm._update()),插入页面。
-
更新过程:
- 数据变化触发依赖通知,执行
patchVnode;
- 通过 diff 算法对比新旧虚拟 DOM,计算最小更新范围;
- 只更新变化的 DOM 节点,避免全量重绘。
Vue 的事件绑定分为原生 DOM 事件和组件事件,处理方式不同:
- 编译差异:
原生事件编译后对应vnode.data.on,组件事件对应vnode.data.on,带.native修饰符的组件事件会被编译为vnode.data.nativeOn(按原生方式处理)。
-
核心原理:语法糖,本质是value属性 + input事件的结合。
- 原生元素:如
<input v-model="val">等价于<input :value="val" @input="val = $event.target.value">。
- 组件:默认等价于
<MyComponent :value="val" @input="val = $event">。
-
自定义 v-model:
通过组件的model选项修改默认的属性和事件:
Vue.component('MyCheckbox', {
model: {
prop: 'checked', // 替换默认的value
event: 'change' // 替换默认的input
},
props: ['checked'],
template: `<input type="checkbox" :checked="checked" @change="$emit('change', $event.target.checked)">`
})
使用时:<my-checkbox v-model="isChecked"></my-checkbox>(此时绑定的是checked属性和change事件)。
- XSS 攻击风险:
v-html会将字符串解析为 HTML 并插入 DOM,若内容包含恶意脚本(如用户输入的<script>),可能执行恶意代码。
- 覆盖子元素:
v-html会替换元素内所有子节点,导致原有子元素丢失。
- 原理:编译后会生成
domProps: { innerHTML: ... },直接操作 DOM 的innerHTML属性。
-
加载渲染:
父beforeCreate → 父created → 父beforeMount → 子beforeCreate → 子created → 子beforeMount → 子mounted → 父mounted
(先父后子初始化,先子后父完成挂载)
-
子组件更新:
父beforeUpdate → 子beforeUpdate → 子updated → 父updated
-
父组件更新:
父beforeUpdate → 父updated(不影响子组件)
-
销毁过程:
父beforeDestroy → 子beforeDestroy → 子destroyed → 父destroyed
(先父后子准备销毁,先子后父完成销毁)
Vue 组件通信遵循单向数据流原则(父→子可直接传递,子→父需通过事件),常见方式包括:
使用Vue.mixin混入公共逻辑,实现代码复用:
- 原理:
mixin会将定义的选项(如data、methods、生命周期)合并到每个组件中,优先级遵循 “组件选项覆盖混入选项”(特殊属性如生命周期会合并为数组依次执行)。
- 示例:
Vue.mixin({
created() {
console.log('公共初始化逻辑');
}
});
- 注意:过度使用可能导致逻辑混乱,Vue3 推荐使用
Composition API替代。
- 作用:实现组件的按需加载,减小初始打包体积,优化页面加载速度。
- 原理:依赖 ES6 的
import()语法(动态导入),将组件代码分割为单独的 chunk,在需要时才加载。
- 使用方式:
components: {
AsyncComponent: () => import('./AsyncComponent.vue')
}
- 加载过程:
- 初始渲染时返回占位符(
createAsyncPlaceholder);
- 组件加载完成后,调用
resolve更新缓存并强制重新渲染;
- 再次渲染时直接使用缓存的组件。
-
编码层面:
- 避免
data中存储无关数据(减少响应式监听);
v-for需加唯一key,避免与v-if连用;
- 使用
Object.freeze()冻结无需响应式的数据;
- 拆分大型组件为小型组件,减少不必要的渲染;
- 合理使用
keep-alive缓存频繁切换的组件。
-
加载优化:
- 路由懒加载(
() => import('./page.vue'));
- 异步组件按需加载;
- 第三方库按需导入(如
babel-plugin-component);
- 图片懒加载(
vue-lazyload)。
-
打包优化:
- 使用 CDN 加载第三方依赖;
- 拆分公共代码(
splitChunks);
- 压缩代码(
gzip);
- 减少
sourceMap体积(生产环境使用cheap-module-source-map)。
-
体验优化:
- 实现骨架屏(
app-skeleton);
- 使用 PWA(
serviceWorker)缓存资源;
- 大型列表采用虚拟滚动(
vue-virtual-scroll-list)。
- 语言层面:采用 TypeScript 开发,增强类型校验;
- 响应式原理:用
Proxy替代Object.defineProperty,支持监听数组索引、对象新增属性等;
- API 风格:新增
Composition API(如setup、ref、reactive),更灵活地组织逻辑;
- 性能优化:
- 虚拟 DOM 对比优化(只更新绑定动态数据的节点);
- 支持片段(
Fragment),组件可返回多个根节点;
- 其他:优化生命周期(如
beforeDestroy改为beforeUnmount)、新增Teleport(组件传送)等特性。
-
hash 路由:
- 基于 URL 中的
#(如http://xxx.com/#/page),#后的内容不发送给服务器;
- 监听
onhashchange事件,根据location.hash变化切换路由。
-
history 路由:
- 基于 HTML5 的
history API(pushState、replaceState),URL 无#;
- 通过
history.pushState修改 URL,监听popstate事件处理前进 / 后退;
- 需服务端配合(所有路由指向同一个 HTML 文件,避免 404)。
导航守卫用于控制路由跳转过程,完整流程如下:
- 触发导航,调用失活组件的
beforeRouteLeave;
- 调用全局
beforeEach;
- 调用重用组件的
beforeRouteUpdate(2.2+);
- 调用路由配置中的
beforeEnter;
- 解析异步路由组件;
- 调用激活组件的
beforeRouteEnter;
- 调用全局
beforeResolve(2.5+);
- 导航确认,调用全局
afterEach;
- 触发 DOM 更新;
- 执行
beforeRouteEnter中传给next的回调(组件实例创建后)。