Vue进阶
一、keep-alive
1.核心作用和使用场景
1.作用
- 缓存组件实例:避免重复销毁和创建,保留组件状态(如
DOM结构、响应式数据、事件监听
) - 提升性能:适用于需要频繁切换但状态需保留的组件(如
Tab页、表单填写页
)
2.使用方式
1<template>
2 <keep-alive :include="['ComponentA', 'ComponentB']" :max="5">
3 <component :is="currentComponent"></component>
4 </keep-alive>
5</template>
2.生命周期钩子变化
- 新增钩子(仅在被缓存的组件中触发)
- onActivated:组件被激活(插入DOM)时触发。
- onDeactivated:组件被停用(移除DOM)时触发
- 执行顺序:
- 首次加载:
onCreate->onMounted->onActivated
- 切换离开:
onDeactivated
- 再次进入:
onActivated
- 彻底销毁:
onUnmounted
- 首次加载:
3.关键配置属性
1.include
- 匹配组件名称(name选项),仅缓存匹配的组件
- 支持字符串、正则、数组
1<!-- 缓存以 "Test" 开头的组件 -->
2<keep-alive :include="/^Test/">
3
2.exclude
- 排除指定组件,优先级高于include
3.max
- 最大缓存实例数,超出时按LRU(最近最少使用)策略淘汰旧实例
- LUR原理:有限淘汰最久未访问的实例
4.高频面试题
1.keep-alive实现原理
- 缓存机制:通过Map或Object缓存组件vnode实例,渲染时直接从缓存中取
- DOM处理:
- 该组件实例对应的整个 DOM 树会被从真实的文档流 (DOM tree) 中完全移除 (
detached
) 。这就是为什么在页面检查器里看不到它的 DOM 了。 - 当这个组件再次被激活时,
keep-alive
会从缓存中找到这个实例,直接复用这个组件实例(保留所有状态),并将它对应的 DOM 树重新插入 (attached
) 到文档流中。
- 该组件实例对应的整个 DOM 树会被从真实的文档流 (DOM tree) 中完全移除 (
2.如何动态控制组件缓存
- 方案1:绑定动态include/exclude(响应式变量)
1<keep-alive :include="cachedComponents">
- 方案2:通过key强制重新渲染(改变key会销毁旧实例)
1<component :is="currentComponent" :key="componentKey">
3.keep-alive如何结合路由使用
- 搭配router-view
1<router-view v-slot="{ Component }">
2 <keep-alive>
3 <component :is="Component" v-if="$route.meta.keepAlive" />
4 </keep-alive>
5 <component :is="Component" v-if="!$route.meta.keepAlive" />
6</router-view>
- 路由配置:通过meta字段标记需缓存的页面
1{ path: '/home', component: Home, meta: { keepAlive: true } }
4.缓存组件如何更新数据
- onActivated中刷新数据
1onActivated(() => {
2 fetchData();
3});
5.max属性的作用及淘汰策略
- 作用:避免内存无限增长,限制最大缓存实例
- 淘汰策略:LRU(最近最少使用),优先移除最久未被访问的实例
5.注意事项
- 组件必须设置name选项:否则include/exclude无法匹配
- 避免内存泄漏:及时清理不需要缓存的组件(如通过max或动态include)
- SSR不兼容:keep-alive仅在客户端渲染中生效
- 缓存组件的状态保留:表单内容等会被保留,需手动重置或通过key强制更新
6.实战实例
1<template>
2 <button @click="toggleComponent">切换组件</button>
3 <keep-alive :include="cachedComponents" :max="3">
4 <component :is="currentComponent" :key="currentComponent" />
5 </keep-alive>
6</template>
7
8<script setup>
9import { ref } from 'vue';
10import ComponentA from './ComponentA.vue';
11import ComponentB from './ComponentB.vue';
12
13const currentComponent = ref('ComponentA');
14const cachedComponents = ref(['ComponentA', 'ComponentB']);
15
16const toggleComponent = () => {
17 currentComponent.value = currentComponent.value === 'ComponentA' ? 'ComponentB' : 'ComponentA';
18};
19</script>
二、异步组件
1.核心概念与使用方式
1.定义异步组件
defineAsyncComponent
函数(Vue3推荐方式)
1import { defineAsyncComponent } from 'vue';
2const AsyncCom = defineAsyncComponent(() => import('./MyComponent.vue'));
- 动态
import()
语法(结合构建工具如Webpack/Vite实现代码分割)
1const AsyncComp = defineAsyncComponent({
2 loader: () => import('./MyComponent.vue'),
3 loadingComponent: loadingSpinner,
4 errorComponent: ErrorDisplay,
5 delay: 1000,
6 timeout,
7})
2.Suspense组件
- 统一管理异步组件(如异步组件或异步setup函数):
1<template>
2 <Suspense>
3 <template #default>
4 <AsyncComp />
5 </template>
6 <template #fallback>
7 <div>Loading...</div>
8 </template>
9 </Suspense>
10</template>
2.高频面试题
1.异步组件的核心作用
- 按需加载:减少初始包体积,提升首屏加载速度
- 性能优化:结合代码分割(
Code Spliting
)动态加载非关键组件
2.如何配置异步组件的加载状态和错误处理?
loadingComponent
:显示加载中的UI(如loading动画)errorComponent
:加载失败时显示错误提示delay
:延迟显示loading组件,避免快速加载时闪烁timeout
:超时后触发错误组件
3.Suspense和异步组件的关系
- Suspense:内置组件,用于统一管理异步组件的加载状态(如多个异步组件并行加载)
- 异步组件:通过
defineAsyncComponent
定义,由Suspense
控制占位内容
4.如何实现组件加载失败后的重试逻辑
- 工厂函数返回Promise:在loader中捕获错误并重试
1const Async = defineAsyncComponent({
2 loader: () => import('./MyComponent.vue')
3 .catch(() => {
4
5 return retryImport();
6 })
7})
5.异步组件在路由懒加载中的应用
- Vue Router配置
1const router = createRouter({
2 route: [{
3 path: '/profile',
4 component: () => import('./Profile.vue');
5
6 component: defineAsyncComponent(() => import('./Profile.vue'))
7 }]
8})
6.Vue3异步组件与Vue2的差异
- 语法差异:Vue3废弃
Vue.component('async-comp',() => import(...))
,改用defineAsyncComponent
- 功能增强:Vue3支持更细颗粒度的加载状态管理和
Suspense
集成
3.底层原理优化
1.代码分割原理
- 构建工具(如webpack)将动态
import()
的模块拆分为独立chunk
,运行时按需加载
2.异步组件生命周期
- 加载阶段:触发loader -> 下载loader -> 初始化组件
- 缓存机制:已加载的组件实例会被缓存,避免重复加载
3.性能优化策略
- 预加载(prefetch):通过Webpack魔法注释标记非关键资源
1() => import( './MyComponent.vue')
- 懒加载阈值:结合路由或用户行为预测延迟加载组件
4.注意事项
- 组件命名:异步组件需显式申明
name
选项,以便调试和keep-alive
匹配 - SSR限制:异步组件在服务端渲染中需特殊处理(如占位内容)
- 错误边界:结合
onErrorCaptured
全局捕获异步组件错误 - 过度分割:避免过多小模块导致HTML请求激增
5.实战代码实例
1const AsyncModal = defineAsyncComponent({
2 lodaer: () => import('./Modal.vue').catch((err) ==> {
3 console.log('加载失败,3s后重试...');
4 return new Promise(resolve => {
5 setTimeout(() => resolve(import('./Modal.vue')), 3000);
6 })
7 }),
8 loadingComponent: LoadingSpainner,
9 delay: 200,
10 timeout: 5000
11});
12
13export default {
14 setup() {
15 const showModal = ref(false);
16 return { showModal, AsyncModal };
17 }
18}
6.应用场景
- 大型应用模块懒加载:如管理后台的复杂表单/图表组件
- 条件渲染组件:用户交互后才加载的非必要组件(如弹窗)
- 路由级懒加载:结合Vue Router提升首屏性能
三、Vue-Router
1.Vue-Router 4.X核心变化
1.创建路由实例
1import { createRouter, createWebHistory } from 'vue-router';
2const router = createRouter({
3 history: createWebHistory(),
4 routes: [...]
5});
2.组合式API支持
- useRouter():获取路由实例(替代
this.$router
) - useRoute():获取当前路由对象(替代
this.$route
)
2.路由配置与核心概念
1.动态路由
1{ path: '/user/:id', component: User };
2
2.嵌套路由
1{
2 path: '/parent',
3 component: Parent,
4 children: [
5 { path: 'child', component: Child }
6 ]
7}
3.命名路由与编程式导航
1router.push({ name: 'user', params: { id: 1 } });
4.路由模式
createWebHistory()
:History模式(需服务器支持)createWebHashHistory()
:Hash模式createMemoryHistory()
:SSR或测试环境
5.重定向与别名
1{ path: '/home', redirect: '/' }
2{ path: '/', alias: 'home' }
3.导航守卫
1.全局守卫
router.beforeEach((to, from, next) => { ... })
router.afterEach((to, from) => { ... })
router.beforeResolve()
2.路由独享守卫
1{
2 path: '/admin',
3 component: Admin,
4 beforeEnter: (to, from, next) => { ... }
5}
3.组件内守卫
onBeforeRouteUpdate
:路由参数变化onBeforeRouteLeave
:离开组件前
1import { onBeforeRouteLeave } from 'vue-router';
2export default {
3 setup() {
4 onBeforeRouteLeave((to, from, next) => {
5
6 next();
7 });
8 }
9};
4.高级特性与最佳实践
1.路由懒加载
1const User = () => import('./User.vue');
2const User = defineAsyncComponent(() => import('./User.vue'));
2.路由元信息(meta)
1{ path: '/profile', meta: { requireAuth: true } }
2
3.动态路由
- 添加路由:
router.addRoute({ path: '/new', component: New })
- 删除路由:
router.removeRoute('route-name')
4.路由组件传参
1{ path: '/user/:id', component: User, props: true }
2
5.滚动行为控制
1const router = createRouter({
2 scrollBehavior(to, from, savedPosition) {
3 return savedBehavior || { top: 0 };
4 }
5});
5.高频面试题
1.Vue-Router 4.X 与 3.X 的主要区别
- API命名调整(如
new VueRouter() -> createRouter()
) - 组合式API支持(
useRouter/useRoute
) - 动态路由API优化(
addRoute/removeRoute
)
2.如何实现路由权限控制
- 全局守卫 + 元信息
1router.beforeEach((to, from, next) => {
2 if(to.meta.requireAuth && !isAuthenticated) next('/login');
3 else next();
4});
3.如何处理动态路由加载顺序问题
router.isReady()
:确保初始路由解析完成再挂载应用
1router.isReady().then(() => app.mount('#app'));
4.如何捕获导航错误
1router.onError((error) => {
2 console.error('导航错误', error);
3});
5.路由组件如何复用并响应参数变化
onBeforeRouteUpdate
:监听路由参数变化
1onBeforeRouteUpdate((to, form, next) => {
2 fetchData(to.params.id);
3 next();
4})
6.实战场景示例
1const dynamicRoutes = [
2 { path: '/admin', component: Admin, meta: { role: 'admin' } }
3];
4if (user.role === 'admin') {
5 dynamicRoutes.forEach(route => router.addRoute(route));
6}
7
8const Home = () => import( './Home.vue' );
7.注意事项
- this.$router的兼容性:选项式API中仍可用,组合式API推荐useRouter
- SSR适配:需使用createMemoryHistory并处理客户端激活
- 路由命名冲突:动态路由添加时注意避免重复路径或名称
- 导航守卫异步处理:确保调用next()或返回Promise
四、状态管理
1、Vuex
1.Vuex核心概念与工作流程
1.核心角色
State
:单一状态树,存储全局数据(响应式)Getter
:基于State派生的计算属性(类似组件的computed)Mutation
:同步修改State的唯一途径(通过commit触发)Action
:处理异步操作,提交Mutations(通过dispatch触发)Modules
:模块化拆分复杂Store 2.工作流程
1组件 -> dispatch(Action) -> Action -> commit(Mutation) -> Mutation -> 修改State -> 更新视图
3.Vue4.x对Vue3的支持
- 兼容Vue3的
Composition API
,但核心API与Vuex 3.x一致 - 通过
useStore
替代this.$store
(组合式API中)
1import { useStore } from 'vuex';
2export default {
3 setup() {
4 const store = useStore();
5 return { store };
6 }
7};
2.核心API与使用
1.定义Store
1import { createStore } from 'vuex';
2const store = createStore({
3 state: { count: 0 },
4 mutation: {
5 increment(state) { state.count++; }
6 },
7 actions: {
8 asyncIncrement({ commit }) {
9 setTimeout(() => commit('increment'), 1000);
10 }
11 },
12 getters: {
13 doubleCount: state => state.count * 2
14 }
15});
2.组件中访问Store
- 选项式API:
this.$store.state.count
或mapState/mapGetters
辅助函数 - 组合式API:
const store = useStore(); store.state.count;
3.辅助函数
mapState / mapGetters
:映射到计算属性- mapMutation / MapActions:映射到方法
1import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
2export default {
3 computed: {
4 ...mapState(['count']),
5 ...mapGetters(['doubleCount'])
6 },
7 methods: {
8 ...mapMutations(['increment']),
9 ...mapActions(['asyncIncrement'])
10 }
11};
3.模块化与命名空间
1.模块定义
1const moduleA = {
2 namespaced: true,
3 state: { ... },
4 mutation: { ... },
5 action: { ... }
6};
7const store = createStore({
8 modules: { a: moduleA }
9});
2.命名空间访问
- 直接访问:
store.state.a.moduleData
- 辅助函数
1...mapActions('a',['moduleAction']),
2
3const { mapActions } = createNamespacedHelpers('a');
3.模块的局部上下文
- Root State:在模块的Action中通过rootState访问全局状态
- Root Commit:在模块Actions中通过
{ root: true }
提交全局Mutation
1actions: {
2 localAction({ commit, dispatch, rootState }) {
3 commit('localMutation');
4 dispatch('gloabalAction', null, { root: true });
5 }
6}
4.高级特性与最佳实践
1.严格模式
1const store = createStore({ strict: true });
2
2.插件开发
- 订阅Mutations
1store.subscribe((mutation, state) => {
2 console.log('Mutation:', mutation.type);
3});
- 持久化插件(如结合localStorage)
1const persistPlugin = (store) => {
2 store.subscribe((mutation, state) => {
3 localStorage.setItem('vuex-state', JSON.stringify(state));
4 });
5};
3.动态注册模块
1store.registerModule('dynamicModule', { ... });
2store.unregisterModule('dynamicModule', { ... });
5.高频面试题
1. Vuex与pinia的区别
- Pinia是Vue官方推荐的新状态管理库,支持Composition API和TypeScript
- 核心差异:
- Pinia无mutations,直接通过actions修改状态(同步/异步均可)
- Pinia基于模块化设计(每个Store独立),无需嵌套模块
- 更简洁的API和TypeScript支持
2. 为什么需要Mutations处理同步,Actions处理异步?
- 调试工具追踪:确保状态变化的同步记录可追踪
- 数据可预测性:避免异步操作导致状态变更顺序混乱
3. Vuex如何实现响应式?
- 底层通过Vue的响应式系统(reactive)实现state的依赖收集和更新触发
4. 如何避免模块命名冲突
- 使用namespaced: true隔离模块,通过命名空间访问状态和方法
5. 大型项目如何优化Vuex使用?
- 按功能拆分为模块,结合动态加载(
registerModule
) - 使用
Getter
封装复杂状态逻辑
6.实战使用场景
1.模块化与命名空间
1const userModel = {
2 namespaced: true,
3 state: { name: 'Alice' },
4 mutations:{
5 setName(state, name) {
6 state.name = name;
7 }
8 }
9};
10
11methods:{
12 ...mapActions('user', ['setName']);
13}
2.状态持久化插件
1const persistedState = localStorage.getItem('vuex-state');
2const store = createStore({
3 state: persistedState ? JSON.parse(persistedState) : {}m
4 plugins: [persistPlugin],
5});
7.注意事项
- 避免直接修改State:必须通过
commit
或dispatch
触发变更。 - 模块复用:动态注册模块时需要注意生命周期管理(如路由切换时卸载)
- 性能优化:避免在
Getters
中执行高开销计算,使用缓存或拆分逻辑 - TypeScript支持:Vuex4对TS支持较弱,推荐使用
pinia
替代
2、Pinia
1.Pinia核心概念与优势
1. Pinia是什么?
- Vue官方推荐的新一代状态管理库,替代Vuex,专为Vue3设计,全面支持
Composition API
和TypeScript
- 核心特点:简洁API、去除了
Mutations
、模块化天然支持、极致TypeScript
友好
2. 核心优势(对比Vuex)
- 无Mutations:直接通过
Actions
处理同步/异步逻辑 - 扁平化结构:多个
Store
代替嵌套模块,更易维护 - TypeScript支持:自动推导类型,无需额外配置
- Devtools集成:支持时间旅行调试和状态快照
- 轻量高效:体积更小,API更简洁
2.核心API与基本使用
1. 定义Store
- Options Store(类似Vue选项式API)
1import { defineStore } from 'pinia';
2export const useCounterStore = defineStore('counter', {
3 state: () => ({ count: 0 }),
4 getters: {
5 doubleCount: (state) => state.count * 2,
6 },
7 actions: {
8 increment() {
9 this.count++;
10 },
11 async asyncIncrement() {
12 setTimeout(() => this.increment(), 1000);
13 },
14 },
15});
- Setup Store(类似Composition API):
1export const useUserStore = defineStore('user', () => {
2 const name = ref('Alice');
3 const setName = (newName: string) => { name.value = newName; };
4 return { name, setName };
5})
2. 在组件中使用Store
1<script>
2import { useCounterStore } from '@/stores/counter';
3const counterStore = useCounterStore();
4</script>
5<template>
6 <div>{{ counterStore.count }}</div>
7 <button @click="counterStore.increment()">+1</button>
8</template>
3.核心特性
1. State
- 响应式状态:通过
ref或reactive
实现,直接修改自动触发更新 - 重置状态:
counterStore.$reset()
- 批量更新:
counterStore.$patch({ count: 10 })
2. Getters
- 类似Vue的computed,自动缓存结果
- 支持访问其他Store
1getters: {
2 combinedInfo() {
3 const userStore = useUserStore();
4 return `${userStore.name}: ${this.doubleCount}`;
5 }
6}
3. Actions
- 同步/异步均可:无需区分Mutation和Action
- 支持相互调用:通过this访问其他Actions
- 订阅Actions:
1const unsubscribe = counterStore.$onAction({ name, after, args }) => {
2 after(() => console.log(`${name} 执行完成,参数:${args}`));
3}
4.模块化组合
1. 模块化设计
- 通过多个Store文件天然实现模块化,无需嵌套结构
- 跨Store调用:
1import { useCounterStore } from './counter';
2export const useUserStore = defineStore('user', {
3 actions: {
4 asyncWithCounter() {
5 const counterStore = useCounterStore();
6 counterStore.increment();
7 },
8 },
9});
2. 动态添加Store
- 无需显示注册,按需引入即可(天然支持代码分割)
5.插件与高级用法
1. 插件机制
- 自定义插件(如持久化存储)
1const persistPlugin = ({ store }) => {
2 const savedState = localStorage.getItem(store.$id);
3 if(savedState){
4 store.$patch(JSON.parse(savedState));
5 }
6 store.$subscribe((mutation, state) => {
7 localStorage.setItem(store.$id, JSON.stringify(state));
8 });
9};
- 注册插件:
1import { createPinia } from 'pinia';
2const data = createPinia().use(persistPlugin);
2. Devtools支持
- 默认集成Vue DevTools,可追踪状态变化和Actions调用
6.高频面试题
1. 为什么选择Pinia而不是Vuex?
- API简洁:去除了Mutation,减少心智负担
- TypeScript友好:自动类型推导,无需复杂配置
- 模块化更自然:多个Store代替嵌套模块,结构清晰
2. Pinia如何处理异步操作?
- 直接在
Actions
中写异步逻辑(如async/await
),无需额外步骤
3. Pinia如何实现响应式?
- 底层基于Vue3的
reactive和ref
,保证状态变更自动触发更新
4. 如何实现状态持久化?
- 通过插件拦截
$subscribe
或$onAction
,结合localStorage
5. Pinia如何支持TypeScript?
- Store定义自动推导类型,组件中通过
store.xxx
直接获得类型提示
7.实战场景示例
1. 用户认证状态管理
1export const useAuthStore = defineStore('auth', {
2 state: () => ({ token: null, user: null }),
3 actions: {
4 async login(username: string, password: string) {
5 const res = await api.login(usename,password);
6 this.token = res.token;
7 this.user = res.user;
8 },
9 logout() {
10 this.$reset();
11 },
12 },
13});
2. 跨Store组合逻辑
1export const useCartStore('cart', {
2 actions: {
3 checkout() {
4 const authStore = useAuthStore();
5 if(authStore.user) throw new Error('请先登录');
6
7 },
8 },
9});
8.注意事项
- 避免直接修改Store实例:使用Actions或$patch确保状态变更
- 性能优化:拆分高频变更状态到独立Store,减少渲染影响
- 合理设计Store:按业务功能划分Store,避免单一Store过于臃肿
- TypeScript最佳实践:明确标注类型(如
state:() => ({ count: 0 as number })
)
五、性能优化
1.Vue3核心优化机制
1.响应式系统升级
- 基于
Proxy
替代Vue2的Object.defineProperty
,支持动态属性添加和数组索引修改的监听 - 惰性依赖追踪:仅对实际用到的属性触发更新,减少不必要的渲染
2.编译优化
- 静态提升:将静态节点(无动态绑定的元素)提升到渲染函数外,避免重复创建
- 补丁标志:在虚拟DOM中标记动态绑定的类型(如
class、style、props
),减少Diff对比范围 - Block Tree优化:将模版划分为动态和静态区块,仅追踪动态区块的变化
- 缓存事件处理程序:如
@click
的时间处理函数会被缓存,避免重复生成
2.组件级优化策略
1.渲染控制
v-once
:静态内容只渲染一次
1<div v-once>永不更新的内容</div>
- v-memo
(Vue3.2+):依赖不变时跳过更新
1<div v-memo="[value]">{{ value }}</div> <!-- 仅当value变化时更新 -->
v-show
:高频切换(CSS显示隐藏)v-if
:低频切换(销毁/重建组件)
2.组件设计优化
- 细粒度拆分:隔离高频更新组件
- 异步组件:延迟加载非关键组件
1const Modal = defineAsyncComponet(() => import('./Modal.vue'));
3.状态管理优化
shallowRef/shalloReactive
:非深度响应式数据
1const largeObj = shallowRef({ ... })
- 避免大型响应式对象:解构为独立ref
- 使用markRaw跳过响应式转换
1const staticData = markRaw({ ... })
3.资源与加载优化
1.代码分割
- 路由级懒加载(
Vue Router
)
1{ path: '/dashboard', component: () => import('./Dashboard.vue') }
- 组件级懒加载(
defineAsyncComponent
) - 第三方库按需加载:
1import { debounce } from 'lodash-es';
2.Tree Shaking支持
- 使用ES模块语法(ESM)
- 避免副作用代码:
1pakage.json 中标记 "sideEffects": false
3.预加载关键资源
1<!-- 预加载首屏关键组件 -->
2<link rel="preload" as="script" href="/src/components/Critical.vue">
3
4.运行时性能优化
1.列表渲染优化
- 必须提供key:
1<li v-for="item in items" :key="item.id">{{ item.text }}</li>
- 虚拟滚动(vue-virtual-scroller)
1<RecycleScroller :items="largeList" item-size="50">
2 <template #default="{ item }">{{ item.text }}</template>
3</RecycleScroller>
- 避免v-for与v-if共用(优先用computed过滤数据)
2.计算与侦听优化
- computed缓存:替代模板内复杂表达式
- 避免深度监听大型对象:
1watch(data, callback, { deep: false })
- watchEffect自动依赖追踪:
1watchEffect(() => console.log(state.count))
3.事件处理优化
- 高频事件使用防抖/节流
1import { debounce } from 'lodash-es';
2methods: { search: debounce(fn, 300) }
5.架构级优化
1.服务端渲染(SSR)
- 使用Nuxt.js实现
1npx nuxi init my-ssr-app
- 优势:提升首屏加载速度 & SEO
2.静态站点生成(SSG)
- 使用VitePress/VuePress:
1npm init vitepress
- 预生成静态页面,适合内容型网站
3.CDN与缓存策略
- 静态资源添加Content Hash:
1app.3a88b9e2.js # 文件名包含hash
- 设置长期缓存:
1location /assets {
2 expires 1y;
3 add_header Cache-Control "public";
4}
6.工具链优化
1.现代构建工具
- Vite:基于ESM的极速开发体验
1npm create vite@lastest
- 生产构建优化:
1export default {
2 build: {
3 minify: 'terser',
4 brtliSize: true,
5 chunkSizeWarningLimit: 1000
6 }
7}
2.性能分析工具
Chorme DevTools Performance
面板Vue DevTools
性能追踪Lighthouse
性能评分
1lighthouse http:
7.高频面试题
1.Vue3比Vue2快在哪里?
- 响应式:Proxy替代defineProperty
- 编译:Patch Flag/Block Tree减少Diff范围
- 体积:Tree Shaking支持更佳
2.如何优化长列表性能?
- 虚拟滚动 + 唯一key + 避免响应式嵌套
3.v-memo的使用场景
- 表格行渲染
- 大型表单字段
- 重复渲染的子组件
4.什么时候用shallowRef?
- 大型对象/数组(如1000+条目的列表数据)
5.SSR解决了什么问题?
- 首屏加载白屏问题
- SEO不友好问题
8.实战优化示例
1.虚拟滚动实现
1<template>
2 <VirtualList :items="items" :item-size="50" height="300px">
3 <template #default="{ item }">
4 <ListItem :item="item" />
5 </template>
6 </VirtualList>
7</template>
2.状态更新批处理
1import { nextTick } from 'vue';
2async function batchUpdate() {
3 state.a = 1;
4 state.b = 2;
5 await nextTick()
6
7}
3.Web Worker处理CPU密集型任务
1const woker = new Worker('./worker.js');
2worker.postMessage(data);
3worker.onmessage = e => { state.result = e.data }
9.优化原则总结
- 量度优先:用
Lighthouse/Vue Devtools
定位瓶颈 - 渐进优化:优先解决最大性能瓶颈(如长列表/包体积)
- 平衡之道:避免过度优化牺牲可维护性
- 更新策略:
方法 | 使用场景 |
---|---|
v-memo | 精确控制子组件更新条件 |
shallowRef | 大型非深度响应数据 |
markRaw | 永远不需要响应式的数据 |
defineAsyncComponent | 延迟加载非首屏组件 |
六、Vue2和Vue3的区别
1.架构设计区别
特性 | Vue2 | Vue3 | 优势 |
---|---|---|---|
响应式系统 | Object.defineProperty | Proxy | 支持动态属性/数组索引监听,性能更优 |
代码组织 | Options API | Composition API | 逻辑复用更灵活,类型推导更友好 |
源码组织 | Flow 类型系统 | TypeScript 重写 | 更好的类型支持和源码可维护性 |
包体积 | 全量引入(22.5kb) | 按需引入(<10kb) | Tree Shaking 减少 41% 体积 |
2.响应式系统升级
1.Vue的局限性
1this.$set(this.obj, 'newProp', value);
2
3this.$set(this.arr, index, value);
2.Vue3的Proxy实现
1const proxy = new Proxy(data, {
2 get(target, key) { },
3 set(target, key, value) { }
4})
- 优势
- 支持动态属性增删/数组索引修改
- 无需初始化深度遍历(惰性依赖追踪)
- 内存占用减少50%(基于基准测试)
3.Composition API vs Options API
1.Options API 痛点
1export default {
2 data() {
3 return {
4 count: 0
5 }
6 },
7 methods: { increment() {...} },
8 computed: { double() {...} }
9}
2.Composition API 解决方案
1import { ref, computed } from 'vue';
2export function useCounter() {
3 const count = ref(0);
4 const double = computed(() => count.value * 2);
5 function increment() { count.value++ }
6 return { count, double, increment };
7}
- 核心优势:
- 逻辑复用(自定义Hook)
- 更好的TypeScript支持
- 代码组织更灵活(按功能而非选项)
4.性能优化对比
优化方向 | Vue2 | Vue3 |
---|---|---|
编译优化 | 全量 Diff | Patch Flags 标记动态节点 |
静态提升 | 无 | 静态节点提升到渲染函数外部 |
Tree Shaking | 有限支持 | 核心 API 可摇树优化 |
内存占用 | 较高 | 减少 50%(Proxy 惰性依赖) |
编译优化示例:
1export function render() {
2 return (_openBlock(), _createBlock("div", null, [
3 _createVNode("span", null, "静态内容"),
4 _createVNode("span", { class: _ctx,dynamicClass }, null, 2 )
5 ]))
6}
5.生命周期变化
Vue2 | Vue3 (Composition API) | 变化说明 |
---|---|---|
beforeCreate | setup() | 被 setup 替代 |
created | setup() | 被 setup 替代 |
beforeMount | onBeforeMount | 改名 |
mounted | onMounted | 改名 |
beforeUpdate | onBeforeUpdate | 改名 |
updated | onUpdated | 改名 |
beforeDestroy | onBeforeUnmount | 改名(语义更准确) |
destroyed | onUnmounted | 改名(语义更准确) |
errorCaptured | onErrorCaptured | 改名 |
使用示例:
1import { onMounted, onUnmounted } from 'vue'
2export default {
3 setup() {
4 onMounted(() => console.log('组件挂载'))
5 onUnmounted(() => console.log('组件卸载'))
6 }
7}
6.新特性与API
1.Fragment(碎片)
1<!-- Vue3 支持多根节点 -->
2<template>
3 <header>...</header>
4 <main>...</main>
5 <footer>...</footer>
6</template>
2.Teleport(传送门)
1<teleport to="#modal-container">
2 <div class="modal">模态框内容</div>
3</teleport>
3.Suspense(异步组件)
1<Suspense>
2 <template #default><AsyncComponent /></template>
3 <template #fallback><div>Loading...</div></template>
4</Suspense>
4.自定义渲染器API
1import { createRenderer } from 'vue';
2const { render } = createRenderer({ })
7.生态与工具链
领域 | Vue2 | Vue3 | 说明 |
---|---|---|---|
官方路由 | vue-router 3.x | vue-router 4.x | 适配 Composition API |
状态管理 | Vuex 3.x | Vuex 4.x / Pinia | Pinia 为官方推荐新方案 |
构建工具 | Vue CLI | Vite | Vite 开发速度提升 10 倍+ |
SSR 框架 | Nuxt 2 | Nuxt 3 | 全面支持 Vue3 生态 |
8.迁移升级攻略
1.兼容方案
- @vue/compat库提供兼容模式(Vue2行为 + Vue3特性)
- 逐步替换废弃的API(eventBus -> mitt,Vue.extend -> defineComponent)
2.自动迁移工具
1npm install -g @vue/compat
2vue-cli-service upgrade # 自动检测并修复部分 API
3.分步骤迁移
9.高频面试题
1.为什么Vue3用Proxy替代defineProperty?
- 解决动态属性/数组监听问题
- 初始化性能提升(无需递归遍历)
- 内存占用更低
2.Composition API 解决了什么问题?
- 逻辑复用困难(Mixins的命名冲突/来源不清)
- Options API的逻辑碎片化
- TypeScript类型推导支持弱
3.Vue3的模版编译优化有哪些?
Patch Flags
(动态节点标记)- 静态节点提升(减少重复创建)
Block Tree
(跳过静态子树对比)
4.Vue3对TypeScript的支持改进
- 源码使用TS重写
Composition API
完美支持类型推导defineComponent
提供组件类型申明
5.Vue2项目如何升级Vue3?
- 使用
@/vue/compat
过渡 - 逐步替换废弃API(
$children,filters
等) - 优先迁移新组件,逐步重构旧组件
七、SPA
1.核心概念
1.定义与特点
- 单页面应用:整个应用只有一个
HTML
文件,通过动态替换DOM
内容实现”页面”切换 - 核心优势:
- 无刷新跳转(流畅用户体验)
- 前后端分离开发
- 减轻服务器渲染压力
- 主要挑战:
- 首屏加载性能
- SEO优化难度
- 路由管理复杂度
2.SPA与MPA对比
特性 | SPA | MPA (多页面应用) |
---|---|---|
页面数量 | 1 个 HTML | 多个 HTML |
页面跳转 | 前端路由控制,无刷新 | 整页刷新 |
数据请求 | Ajax/Fetch 局部获取数据 | 每次跳转加载完整页面 |
开发复杂度 | 高(需前端路由、状态管理等) | 低 |
SEO 支持 | 差(需额外优化) | 优 |
2.高频面试题
1.SPA首屏加载优化有哪些方案?
- 路由懒加载 + 组件懒加载
- 资源预加载/预取
- CDN加速静态资源
- 开启Gzip/Brotli压缩
- 服务端渲染(SSR)
2.如何解决SPA的SEO问题?
- 预渲染(Prerender)
- 服务端渲染(Nustjs)
- 动态渲染(针对爬虫单独处理)
- 静态站点生成(SSG)
3.Vue Router的导航守卫执行顺序
1全局 beforeEach -> 路由 beforeEnter -> 组件 beforeRouteEnter -> 全局 beforeResolve -> 全局 afterEach -> 组件 beforeRouteUpdate
4.如何处理权限路由?
1router.beforeEach(async (to) => {
2 if (!hasAuthInfo) await fetchUserPermissions();
3 if (to.meta.requiresAdmin && !isAdmin) return '/no-permission';
4})
5.SPA如何保持登录状态?
- JWT存储于localStorage + 刷新Token机制
- 结合HttpOnly Cookie增强安全性
- Token过期自动跳转登录页
八、SSR
1.SSR核心概念与原理
1.服务端渲染 vs 客户端渲染
对比维度 | SSR | CSR (SPA) |
---|---|---|
渲染位置 | 服务端生成完整 HTML | 客户端 JS 动态生成 DOM |
首屏时间 | 快(直接输出 HTML) | 慢(需加载 JS 并执行) |
SEO 支持 | 优(爬虫直接抓取 HTML) | 差(需额外处理) |
服务器压力 | 高(每次请求需渲染) | 低(仅提供静态资源) |
开发复杂度 | 高(需处理同构、服务器环境等) | 低(纯前端开发) |
2.Vue SSR 工作原理
浏览器服务器Vue 应用发送页面请求执行 createSSRApp()渲染组件树生成 HTML返回包含数据的 HTML激活(Hydration) 交互变为可交互 SPA浏览器服务器Vue 应用
3.关键流程说明
- 服务端渲染:
renderToString()
生成静态HTML - 客户端激活:
createSSRApp().mount()
接管DOM添加事件 - 数据预取:在渲染前获取页面所需数据(避免客户端二次请求)
2.Nuxt.js 3 核心使用
1.项目结构与约定
1├─ .nuxt/ # 构建生成
2├─ components/ # 公共组件
3├─ composables/ # 复用逻辑
4├─ layouts/ # 布局组件
5├─ middleware/ # 路由中间件
6├─ pages/ # 自动路由(支持动态路由)
7├─ plugins/ # 插件注册
8├─ public/ # 静态资源
9├─ server/ # API 路由
10└─ nuxt.config.ts # 配置文件
2.服务端生命周期
1useAsyncData('key', async () => {
2 const data = await $fetch('/api/data')
3 return data;
4})
5
6onServerPrefetch(async () => {
7
8})
3.渲染模式配置
1export default defineNustConfig({
2 ssr: true,
3
4 routeRules: {
5 '/static': { prerender: true },
6 '/spa/**': { ssr: false }
7 }
8});
3.数据获取与状态管理
1.数据预取策略
方法 | 执行位置 | 特点 |
---|---|---|
useAsyncData | 服务端/客户端 | 自动防止重复获取,key 唯一化 |
useFetch | 服务端/客户端 | 封装了 useAsyncData + $fetch |
onServerPrefetch | 仅服务端 | 组合式 API 专用,类似 vue2 的 serverPrefetch |
nuxtServerInit | 仅服务端 | (Pinia)初始化 store 全局数据 |
2.Pinia状态同步
1export const useUserStore = defineStore('user', {
2 state: () => ({ token: null }),
3 actions: {
4
5 async nustServerInit() {
6 this.token = await getTokenFormCookie();
7 }
8 }
9});
10
4.性能优化策略
1.渲染层优化
- 组件缓存:
1export default {
2 render: {
3 componentCache: {
4 max: 1000,
5 maxAge: 1000 * 60 * 15
6 }
7 }
8}
- 页面级缓存
1export default defineNitroConfig({
2 storage: {
3 redis: { driver: 'redis', url: 'redis://localhost:6379'}
4 },
5 routeRules: {
6 '/': { cache: { maxAge: 60 } }
7 }
8})
2.资源加载优化
- 预加载关键资源
1<head>
2 <link rel="preload" href="/main.css" as="style">
3 <link ref="modulepreload" href="/vendor.js">
4</head>
- HTTP/2 服务端推送
1Link </app.css>; rel=preload; as=style
2.流式渲染
1const stream = renderToNodeStream(app);
2stram.pipe(res, { end: false });
5.错误处理与调试
1.全局错误捕获
1export default defineNustPlugin(nustApp => {
2 nustApp.vueApp.config.errorHandler = (err) => {
3 console.error('客户端错误:', err);
4 }
5})
6
7export default defineNitroPluign((nitroApp) => {
8 nitroApp.hooks.hook('error', (err) => {
9 logErrorToService(err);
10 })
11})
2.Sentry集成
1modules: ['@nustjs/sentry'],
2sentry: {
3 dsn: 'YOUR_DSN',
4 tracing: true
5}
6.安全最佳实践
1.XSS防护
- 避免在服务端渲染中使用v-html
- 使用vue-basic-sanitize过滤用户内容
2.CSRF防护
1export default defineEventHandler(event => {
2 if (!isValidCSRF(event)) {
3 throw createError({ status: 403, message: 'Forbidden' })
4 }
5 return { data: '安全数据' }
6})
3.CORS配置
1export default {
2 nitro: {
3 middleware: [
4 corsHander({
5 origin: ['http://yourdomain.com'],
6 methods: ['GET', 'POST']
7 })
8 ]
9 }
10}
7.高阶应用场景
1.混合渲染(Hydrid Rendering)
1export default {
2 routeRules: {
3
4 'privacy': { prerender: true },
5
6 '/dashboard/**': { ssr: false },
7
8 '/products': { swr: 3600 }
9 }
10}
2.边缘渲染(Edge-Side Renddering)
1npx nuxi build --preset=vercel-edge
3.微前端集成
1export default defineComponent({
2 async setup() {
3 const microApp = await loadMicroApp('react-subapp', '#container');
4 }
5})
8.高频面试题
1.SSR的核心优势
- 提升首屏速度
- 更好的SEO
- 更稳定的用户体验
2.hydration过程可能出现的问题
- 客户端和服务端渲染的DOM结构不一致导致hydration失败
- 解决方案:
- 避免在
<template>
中使用随机数 - 确保服务端/客户端初始状态一致
- 用v-if替代v-show处理不可见元素
- 避免在
3.如何处理异步数据的服务端渲染?
- 使用
useAsyncData
或onServerPrefetch
在渲染前获取数据 - 通过
__NUXT__.state
注入到HTML
供客户端激活
4.如何优化高并发下的SSR性能?
- 组件级缓存+页面级缓存
- 流式渲染减少TTFB(首字节时间)
- 负载均衡+水平扩展服务器
5.Nuxt3相比Nuxt2的重大改进
- 基于Vite的极速HMR
- 支持混合渲染和增量静态生成
- Nitro引擎提供Serverless/Edge
- 更好的TypeScript集成
9.实战注意事项
1.避免全局副作用
1let count = 0;
2export const useCounter = () => ({ count: ++count })
3
4export const useCounter = () => {
5 const count = ref(0);
6 return { count };
7}
2.环境区分处理
1const runtimeConfig = useRuntimeConfig();
2const apiBase = process.server ? runtimeConfig.apiSecret : runtimeConfig.public.apiBase;
3.性能监控指标
指标 | 工具 | 目标值 |
---|---|---|
TTFB (首字节时间) | Chrome DevTools | <200ms |
FCP (首次内容渲染) | Lighthouse | <1s |
TTI (可交互时间) | WebPageTest | <3s |
Hydration 时间 | Vue Devtools | <500ms |
个人笔记记录 2021 ~ 2025