当前位置: 首页 > news >正文

Vue3 入门学习

Vue3 技术文章大纲

Vue3 核心特性与优势

Composition API 的设计理念与优势

Composition API 是 Vue3 的核心特性之一,旨在解决 Options API 在复杂组件中逻辑分散的问题。通过setup函数,可以将相关逻辑组织在一起,提高代码的可读性和可维护性。Composition API 支持逻辑复用,通过自定义 Hook 实现跨组件逻辑共享,避免 Mixins 的命名冲突和来源不清晰问题。

性能优化

Vue3 在性能方面进行了显著优化。使用 Proxy 替代Object.defineProperty实现响应式系统,支持动态属性添加和数组索引修改,性能更高。Tree-shaking 支持使得打包体积更小,未使用的功能不会包含在最终构建中。虚拟 DOM 重写,优化了 diff 算法,提升了渲染性能。

更好的 TypeScript 集成

Vue3 从底层开始使用 TypeScript 重写,提供了更好的类型推断和支持。Composition API 的设计使得 TypeScript 集成更加自然,类型推导更加准确。开发者可以享受到完整的类型检查和代码提示,提升开发体验和代码质量。

新特性
  • 片段(Fragment):支持组件返回多个根节点,无需包裹额外的 DOM 元素。
  • Teleport:允许将子组件渲染到 DOM 中的其他位置,适用于模态框、通知等场景。
  • Suspense:提供异步组件加载的解决方案,支持加载状态和错误处理。
  • 自定义渲染器:支持自定义渲染逻辑,适用于非 DOM 环境(如小程序、Canvas)。

环境搭建与项目初始化

确保系统已安装 Node.js(建议版本 16+)。通过以下命令检查版本:

node -v npm -v

全局安装或升级 Vite:

npm install -g create-vite

使用 Vite 创建 Vue3 项目

执行创建命令并选择模板:

npm create vite@latest my-vue-app --template vue

进入项目目录并安装依赖:

cd my-vue-app npm install

启动开发服务器:

npm run dev

Vue CLI 与 Vite 的对比

构建工具差异
Vue CLI 基于 Webpack,内置完整的配置和插件体系;Vite 采用原生 ES Modules,利用浏览器直接加载模块,开发环境下无需打包。

启动速度
Vite 冷启动时间极短,依赖预构建和按需编译;Vue CLI 在大型项目中启动较慢,需等待完整打包。

生产构建
Vite 使用 Rollup 进行生产构建,输出高度优化的静态资源;Vue CLI 依赖 Webpack 的复杂配置,但插件生态更成熟。

配置复杂度
Vite 配置更简洁,默认支持 TypeScript 和 CSS 预处理器;Vue CLI 提供图形化界面,但自定义配置需熟悉 Webpack。

基础项目结构解析

典型 Vite + Vue3 项目结构:

my-vue-app/ ├── node_modules/ # 依赖库 ├── public/ # 静态资源(不经过构建) ├── src/ │ ├── assets/ # 编译处理的静态资源 │ ├── components/ # 公共组件 │ ├── App.vue # 根组件 │ └── main.js # 应用入口文件 ├── vite.config.js # Vite 配置文件 ├── package.json # 项目元数据和脚本 └── index.html # 入口 HTML 文件

关键文件说明:

  • vite.config.js:可配置代理、插件、别名等,支持热更新。
  • index.html:Vite 将自动注入模块脚本,无需手动引入。
  • main.js:使用createApp初始化 Vue 实例,支持 Composition API。

Composition API 详解

ref 与 reactive 的使用场景与区别

refreactive是 Vue 3 Composition API 中用于创建响应式数据的两种主要方式。

  • ref
    适用于基本类型(如stringnumberboolean)或需要直接引用的对象。ref通过.value访问或修改其值,但在模板中会自动解包,无需使用.value
    示例代码:

    const count = ref(0); count.value++; // 修改值
  • reactive
    适用于对象或数组等复杂数据结构。reactive直接返回一个响应式代理对象,无需.value访问。
    示例代码:

    const state = reactive({ count: 0 }); state.count++; // 直接修改属性

区别

  1. ref可以包装任何值,而reactive仅接受对象/数组。
  2. ref通过.value操作数据,reactive直接操作属性。
  3. 在组合函数中返回响应式数据时,通常使用ref以保证解构后仍保持响应性。

computed 与 watch 的进阶用法
  • computed
    用于派生依赖其他响应式数据的计算属性,具有缓存机制。
    进阶用法:传入getset函数实现可写计算属性。
    示例代码:

    const fullName = computed({ get() { return `${firstName.value} ${lastName.value}`; }, set(newValue) { [firstName.value, lastName.value] = newValue.split(' '); } });
  • watch
    监听响应式数据的变化,支持深度监听和立即执行。
    进阶用法:

    • 使用watchEffect自动追踪依赖,无需显式指定监听源。
    • 通过{ deep: true }监听嵌套对象变化。
      示例代码:
    watchEffect(() => console.log(count.value)); // 自动追踪 count watch( () => state.count, (newVal, oldVal) => { /* 逻辑 */ }, { deep: true } );

生命周期钩子在 Composition API 中的使用

Composition API 提供了与 Options API 对应的生命周期钩子函数,均需在setup()中调用:

  • onMounted:组件挂载完成后执行。
  • onUpdated:响应式数据变更导致 DOM 更新后执行。
  • onUnmounted:组件卸载后执行。
  • onBeforeMount/onBeforeUpdate/onBeforeUnmount等。

示例代码:

import { onMounted } from 'vue'; setup() { onMounted(() => { console.log('组件已挂载'); }); }

自定义 Hooks 的封装与实践

自定义 Hooks 是通过组合 Composition API 的函数实现逻辑复用的模式,类似于 React Hooks。

封装步骤

  1. 将可复用的逻辑提取为函数,函数名通常以use开头。
  2. 在函数内部使用refreactivecomputed等响应式 API。
  3. 返回需要暴露的响应式数据或方法。

示例:封装鼠标位置跟踪 Hook

// useMousePosition.js import { ref, onMounted, onUnmounted } from 'vue'; export function useMousePosition() { const x = ref(0); const y = ref(0); const update = (e) => { x.value = e.pageX; y.value = e.pageY; }; onMounted(() => window.addEventListener('mousemove', update)); onUnmounted(() => window.removeEventListener('mousemove', update)); return { x, y }; }

使用示例

import { useMousePosition } from './useMousePosition'; setup() { const { x, y } = useMousePosition(); return { x, y }; }

优势

  • 逻辑高内聚、低耦合,易于跨组件复用。
  • 避免 Options API 中mixins的命名冲突问题。

Proxy 与 Reflect 的底层实现

Proxy 是 ES6 引入的元编程特性,用于创建对象的代理,拦截并自定义基本操作(如属性访问、赋值等)。Reflect 提供了一组与 Proxy 拦截器对应的方法,用于简化操作。

const target = { foo: 'bar' }; const handler = { get(target, key, receiver) { console.log(`Get ${key}`); return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { console.log(`Set ${key} to ${value}`); return Reflect.set(target, key, value, receiver); } }; const proxy = new Proxy(target, handler); proxy.foo; // 输出 "Get foo" proxy.foo = 'baz'; // 输出 "Set foo to baz"

依赖收集与触发更新的机制

依赖收集通过拦截属性的读取操作(get)实现,将当前执行的副作用函数(effect)存储到依赖集合中。触发更新通过拦截属性的设置操作(set)实现,通知所有关联的副作用函数重新执行。

const depsMap = new Map(); let activeEffect; function track(target, key) { if (!activeEffect) return; let dep = depsMap.get(target); if (!dep) { dep = new Map(); depsMap.set(target, dep); } let effects = dep.get(key); if (!effects) { effects = new Set(); dep.set(key, effects); } effects.add(activeEffect); } function trigger(target, key) { const dep = depsMap.get(target); if (!dep) return; const effects = dep.get(key); effects && effects.forEach(effect => effect()); } function effect(fn) { activeEffect = fn; fn(); activeEffect = null; }

手动控制响应式的进阶 API

shallowRef
创建一个浅层响应式引用,仅对.value的直接变更触发更新,内部属性变更不会触发。

function shallowRef(value) { return { get value() { track(this, 'value'); return value; }, set value(newVal) { if (Object.is(value, newVal)) return; value = newVal; trigger(this, 'value'); } }; }

markRaw
标记对象为“原始”数据,跳过 Proxy 代理,避免其被转换为响应式对象。

const rawSet = new WeakSet(); function markRaw(obj) { rawSet.add(obj); return obj; } function reactive(obj) { if (rawSet.has(obj)) return obj; return new Proxy(obj, { /* 拦截器 */ }); }

单文件组件(SFC)的语法更新

Vue 3 的单文件组件语法进行了多项改进,支持更简洁的<script setup>语法糖。通过definePropsdefineEmits可以在<script setup>中直接声明 props 和 emits,无需显式导入。

<script setup lang="ts"> const props = defineProps<{ title: string; count?: number; }>(); const emit = defineEmits<{ (e: 'update', value: string): void; }>(); </script>

defineProps 与 defineEmits 的 TypeScript 支持

在 TypeScript 中,definePropsdefineEmits支持泛型或运行时声明两种方式。泛型方式提供完整的类型推断,无需额外类型标注。

// 泛型方式 defineProps<{ msg: string }>(); // 运行时声明(兼容性更好) defineProps({ msg: { type: String, required: true } });

插槽(Slots)与作用域插槽的用法

Vue 3 的插槽语法与作用域插槽统一为v-slot或简写#。作用域插槽通过插槽 props 传递数据。

<!-- 默认插槽 --> <template #default="{ user }"> {{ user.name }} </template> <!-- 具名插槽 --> <template #header> <h1>Header</h1> </template>

动态组件与异步组件的优化

动态组件通过<component :is>实现,结合defineAsyncComponent可优化异步组件加载,支持懒加载和错误处理。

import { defineAsyncComponent } from 'vue'; const AsyncComp = defineAsyncComponent({ loader: () => import('./Component.vue'), loadingComponent: LoadingSpinner, delay: 200, timeout: 3000 });

性能优化建议

  • 使用v-oncev-memo减少静态内容渲染开销。
  • 通过markRaw标记非响应式对象以避免不必要的代理。
  • 异步组件结合Suspense实现更流畅的加载体验。

Pinia 核心概念

Pinia 是 Vue 的轻量级状态管理库,基于 Composition API 设计,核心概念包括:

  • Store:通过defineStore定义的状态单元,支持多模块。
  • State:使用refreactive定义响应式数据。
  • Getters:通过computed实现派生状态。
  • Actions:同步或异步方法,直接修改state

状态模块化实践

每个功能模块独立为 Store 文件,通过组合式函数按需引入:

// stores/user.js import { defineStore } from 'pinia'; export const useUserStore = defineStore('user', { state: () => ({ name: '', age: 0 }), actions: { async fetchUser() { /* API 请求 */ } } });

在组件中按需调用:

import { useUserStore } from '@/stores/user'; const userStore = useUserStore();

持久化策略

通过插件(如pinia-plugin-persistedstate)实现状态持久化:

import { createPinia } from 'pinia'; import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; const pinia = createPinia(); pinia.use(piniaPluginPersistedstate);

在 Store 中配置:

defineStore('user', { persist: { key: 'user-data', storage: localStorage, paths: ['name'] // 仅持久化 name 字段 } });

与 Composition API 集成

直接结合setup()使用,避免mapState等辅助函数:

import { storeToRefs } from 'pinia'; const userStore = useUserStore(); const { name, age } = storeToRefs(userStore); // 解构保持响应性 const updateName = () => userStore.name = 'New Name';

对比 Vuex 的优势

  • 类型支持:天然支持 TypeScript,无需额外配置。
  • 简洁 API:去除mutations,直接通过actions修改状态。
  • 模块化:无需嵌套模块,每个 Store 独立注册。
  • 体积更小:压缩后约 1KB,适合轻量级应用。

性能优化建议

  • 避免在 Store 中存储非响应式数据(如静态配置)。
  • 使用storeToRefs解构 Store 属性,防止响应式丢失。
  • 对高频更新的状态使用shallowRef减少深层响应开销。

Vue Router 4.x 新特性解析

路由守卫与组合式 API 结合

Vue Router 4.x 支持在setup()中使用路由守卫,通过onBeforeRouteLeaveonBeforeRouteUpdate等组合式函数实现。例如:

import { onBeforeRouteLeave } from 'vue-router' export default { setup() { onBeforeRouteLeave((to, from) => { return confirm('确定离开当前页面?') }) } }

这种方式替代了传统的beforeRouteLeave选项式 API,使逻辑更集中。

动态路由改进

动态路由匹配通过:param语法实现,可通过useRoute()获取参数:

import { useRoute } from 'vue-router' const route = useRoute() console.log(route.params.id) // 动态参数

新增的path-to-regexp解析引擎支持更灵活的路由匹配规则。

懒加载优化

路由组件支持defineAsyncComponent实现懒加载:

const UserDetails = () => import('./views/UserDetails.vue')

或结合 Suspense 使用:

const router = createRouter({ routes: [ { path: '/user', component: defineAsyncComponent(() => import('./User.vue')) } ] })

Webpack 的魔法注释可进一步优化分包:

const User = () => import(/* webpackChunkName: "user" */ './User.vue')
导航守卫类型强化

通过 TypeScript 泛型增强类型推断:

router.beforeEach((to): boolean | NavigationGuardReturn => { if (to.meta.requiresAuth) return '/login' })

组合式 API 的守卫函数也支持完整的类型提示。

路由优先级规则

动态路由可通过priority属性调整匹配顺序:

const routes = [ { path: '/user/:id', priority: 10 }, { path: '/user/create', priority: 20 } // 更高优先级 ]
滚动行为改进

scrollBehavior支持返回 Promise:

const router = createRouter({ scrollBehavior(to) { return new Promise(resolve => { setTimeout(() => resolve({ top: 0 }), 500) }) } })

工程化与性能优化:基于 Vite 的构建配置

代码分割与预加载策略

Vite 默认使用 Rollup 进行代码分割,通过动态导入(import())自动分割代码块。优化策略包括手动配置rollupOptions控制分割逻辑:

// vite.config.js export default { build: { rollupOptions: { output: { manualChunks: (id) => { if (id.includes('node_modules')) { return 'vendor' } if (id.includes('src/components')) { return 'components' } } } } } }

预加载通过<link rel="modulepreload">自动注入,可通过build.polyfillModulePreload禁用或自定义。

自定义指令与全局属性管理

全局指令通过app.directive注册:

// main.js import { createApp } from 'vue' const app = createApp(App) app.directive('focus', { mounted(el) { el.focus() } })

全局属性(如$filters)可通过app.config.globalProperties挂载:

app.config.globalProperties.$filters = { currency(value) { return '$' + value } }
构建性能优化
  1. 依赖预构建:Vite 自动预构建node_modules,通过optimizeDeps手动配置:
// vite.config.js export default { optimizeDeps: { include: ['lodash-es'] } }
  1. 缓存策略:默认缓存目录node_modules/.vite,可通过cacheDir修改路径。
  2. 多线程编译:启用build.minify时,Vite 默认使用 Terser 多线程压缩。
开发环境优化
  1. 热更新(HMR):Vite 提供开箱即用的 HMR,对 Vue/Svelte 等框架有深度集成。
  2. 按需编译:浏览器仅请求当前路由所需的模块,通过server.open配置自动打开页面。
生产环境优化
  1. CSS 代码分割build.cssCodeSplit默认为true,分离 CSS 文件减少主包体积。
  2. 异步 chunk 加载:通过build.assetsInlineLimit控制小资源是否内联为 Base64。
  3. Bundle 分析:使用rollup-plugin-visualizer生成构建报告:
import { visualizer } from 'rollup-plugin-visualizer' export default { plugins: [visualizer()] }

Vitest + Vue Test Utils 组件测试实践案例

安装依赖确保项目中已安装vitest@vue/test-utilsjsdom(用于模拟浏览器环境)。通过以下命令安装:

npm install -D vitest @vue/test-utils jsdom

配置 Vitestvite.config.js或单独配置文件中添加 Vitest 配置:

import { defineConfig } from 'vitest/config'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [vue()], test: { environment: 'jsdom', globals: true, }, });

编写组件测试以测试一个简单的Button.vue组件为例:

import { mount } from '@vue/test-utils'; import Button from './Button.vue'; describe('Button.vue', () => { it('renders button text correctly', () => { const wrapper = mount(Button, { props: { label: 'Click Me' }, }); expect(wrapper.text()).toContain('Click Me'); }); it('emits click event', async () => { const wrapper = mount(Button); await wrapper.trigger('click'); expect(wrapper.emitted()).toHaveProperty('click'); }); });

模拟用户交互使用trigger方法模拟用户操作,如点击、输入等:

it('updates input value', async () => { const wrapper = mount(MyInput); await wrapper.find('input').setValue('Hello'); expect(wrapper.vm.value).toBe('Hello'); });

Chrome DevTools 的 Vue3 调试技巧

启用 Vue DevTools确保已安装 Vue DevTools 浏览器扩展。在 Chrome 中打开开发者工具(F12Ctrl+Shift+I),切换到Vue选项卡。

组件树检查在 Vue DevTools 中查看组件层级结构,选中组件后可实时查看其propsdatacomputed等属性。

状态调试直接修改组件的dataprops值,观察界面实时响应。适用于动态调试数据流。

事件追踪Events选项卡中查看组件触发的事件及其载荷,帮助定位事件传递问题。

性能分析使用Timeline功能记录组件渲染和更新的性能,识别渲染瓶颈。

源码映射调试确保vite.config.js中已启用源码映射(sourcemap: true),以便在 DevTools 中直接调试原始 Vue 文件而非编译后代码。

自定义指令调试在 Vue DevTools 中检查自定义指令的绑定值和生命周期钩子调用情况。

项目初始化与基础配置

使用 Vue CLI 或 Vite 创建一个新的 Vue 3 项目。安装必要依赖:

npm create vue@latest todolist cd todolist npm install axios element-plus

main.js中集成 Element Plus:

import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' const app = createApp(App) app.use(ElementPlus) app.mount('#app')

Axios 封装与 API 管理

创建src/utils/request.js封装基础请求:

import axios from 'axios' const service = axios.create({ baseURL: 'https://your-api-domain.com/api', timeout: 5000 }) // 请求拦截器 service.interceptors.request.use(config => { config.headers.Authorization = localStorage.getItem('token') || '' return config }) // 响应拦截器 service.interceptors.response.use( response => response.data, error => { console.error('API Error:', error.response?.data) return Promise.reject(error) } ) export default service

创建src/api/todo.js管理具体接口:

import request from '../utils/request' export const getTodoList = () => request.get('/todos') export const addTodo = (data) => request.post('/todos', data) export const updateTodo = (id, data) => request.patch(`/todos/${id}`, data) export const deleteTodo = (id) => request.delete(`/todos/${id}`)

组件开发与状态管理

创建src/components/TodoList.vue

<template> <div class="todo-container"> <el-input v-model="newTodo" placeholder="输入任务内容" @keyup.enter="handleAdd" > <template #append> <el-button @click="handleAdd">添加</el-button> </template> </el-input> <el-table :data="todoList" style="width: 100%"> <el-table-column prop="content" label="任务内容" /> <el-table-column label="操作" width="180"> <template #default="{ row }"> <el-button size="small" @click="handleComplete(row)">完成</el-button> <el-button size="small" type="danger" @click="handleDelete(row.id)">删除</el-button> </template> </el-table-column> </el-table> </div> </template> <script setup> import { ref, onMounted } from 'vue' import { getTodoList, addTodo, updateTodo, deleteTodo } from '../api/todo' const todoList = ref([]) const newTodo = ref('') const fetchTodos = async () => { try { const res = await getTodoList() todoList.value = res.data } catch (error) { console.error('获取列表失败:', error) } } const handleAdd = async () => { if (!newTodo.value.trim()) return try { await addTodo({ content: newTodo.value }) newTodo.value = '' fetchTodos() } catch (error) { console.error('添加失败:', error) } } const handleComplete = async (todo) => { try { await updateTodo(todo.id, { completed: !todo.completed }) fetchTodos() } catch (error) { console.error('更新失败:', error) } } const handleDelete = async (id) => { try { await deleteTodo(id) fetchTodos() } catch (error) { console.error('删除失败:', error) } } onMounted(fetchTodos) </script>

服务端模拟(JSON Server)

安装 JSON Server 用于快速模拟 API:

npm install -g json-server

创建db.json文件:

{ "todos": [ { "id": 1, "content": "学习 Vue 3", "completed": false }, { "id": 2, "content": "封装 Axios", "completed": true } ] }

启动模拟服务器:

json-server --watch db.json --port 3000

修改request.js中的baseURLhttp://localhost:3000

错误处理优化

request.js中增强错误处理:

service.interceptors.response.use( response => { if (response.data.code !== 200) { return Promise.reject(new Error(response.data.message || 'Error')) } return response.data }, error => { const message = error.response?.data?.message || error.message ElMessage.error(message) return Promise.reject(error) } )

样式优化与功能扩展

添加加载状态和空状态提示:

<template> <el-table v-loading="loading" :data="todoList" style="width: 100%" > <!-- ...原有列定义... --> <template #empty> <el-empty description="暂无任务" /> </template> </el-table> </template> <script setup> const loading = ref(false) const fetchTodos = async () => { loading.value = true try { const res = await getTodoList() todoList.value = res.data } finally { loading.value = false } } </script>

添加过滤功能:

<el-radio-group v-model="filterStatus"> <el-radio-button label="all">全部</el-radio-button> <el-radio-button label="active">未完成</el-radio-button> <el-radio-button label="completed">已完成</el-radio-button> </el-radio-group> <script setup> const filterStatus = ref('all') const filteredTodos = computed(() => { switch (filterStatus.value) { case 'active': return todoList.value.filter(todo => !todo.completed) case 'completed': return todoList.value.filter(todo => todo.completed) default: return todoList.value } }) </script>

SSR 方案(Nuxt.js 3)

Nuxt.js 3 基于 Vue 3 提供了开箱即用的 SSR 支持,通过混合渲染模式(Hybrid Rendering)实现动态与静态内容的结合。关键配置在nuxt.config.ts中定义路由规则,使用defineNuxtConfig指定渲染模式:

export default defineNuxtConfig({ routeRules: { '/static': { static: true }, // 纯静态生成 '/dynamic': { ssr: true }, // 服务端渲染 '/spa': { ssr: false } // 客户端渲染 } })

数据获取通过useAsyncDatauseFetch在组件内完成,服务端会自动处理异步数据预取。对于组件级缓存,可通过server/components目录配置组件级别的缓存策略。

微前端架构中的 Vue3 实践

基于 Module Federation 的微前端方案适用于 Vue 3。通过 Webpack 5 的模块联邦特性,主应用动态加载子应用:

// 主应用配置 new ModuleFederationPlugin({ remotes: { vue3SubApp: 'vue3SubApp@http://localhost:3001/remoteEntry.js' } })

子应用需暴露 Vue 组件:

// 子应用配置 exposes: { './Button': './src/components/Button.vue' }

样式隔离可通过 Shadow DOM 或 CSS 命名空间实现。状态管理推荐使用 Pinia 的跨实例共享方案,通过主应用注入 store 上下文。

Vue3 与 Web Components 的协作

Vue 3 支持将组件编译为原生 Web Components:

import { defineCustomElement } from 'vue' import MyVueComponent from './MyComponent.vue' const MyElement = defineCustomElement(MyVueComponent) customElements.define('my-element', MyElement)

属性传递通过props映射,事件通过CustomEvent派发。在 Nuxt 3 中需在nuxt.config.ts配置构建选项:

export default defineNuxtConfig({ vue: { compilerOptions: { isCustomElement: tag => tag.includes('-') } } })

性能优化需注意 Shadow DOM 的样式封装特性,避免重复加载 Vue 运行时。可通过@vue/web-component-wrapper实现更复杂的生命周期控制。

响应式数据丢失的排查方法

检查数据绑定的语法是否正确,确保使用了正确的指令(如v-modelv-bind)。
验证数据是否在组件的datasetup函数中正确定义,避免直接修改未声明的属性。
使用 Vue Devtools 检查数据流,确认数据是否被意外覆盖或未触发更新。
排查异步操作(如 API 调用)是否未正确处理响应式数据的更新,必要时使用Vue.setthis.$set强制更新。

性能瓶颈的分析工具

使用 Chrome DevTools 的 Performance 面板录制页面运行过程,分析脚本执行时间和内存占用。
通过 Vue Devtools 的 Performance 选项卡检测组件渲染时间,定位重复渲染或低效的组件。
借助 Lighthouse 生成性能报告,获取加载速度、资源优化等具体建议。
对于复杂计算逻辑,使用console.timeconsole.timeEnd测量函数执行耗时。

版本迁移的注意事项

对比新旧版本的官方迁移指南,重点关注破坏性变更(如 API 弃用或语法调整)。
逐步升级依赖库,优先解决兼容性冲突,避免一次性全局升级导致问题难以定位。
在测试环境中验证核心功能,确保路由、状态管理等关键模块行为符合预期。
备份项目代码并启用版本控制,便于回滚到稳定版本。


http://www.jsqmd.com/news/860220/

相关文章:

  • 告别环境混乱:用Anaconda虚拟环境在Linux服务器上管理TensorFlow 2.x和JAX的独立实验环境
  • 硬件物理测距→时空AI拓扑·全域透明化感知
  • ElevenLabs荷兰文语音突然失真?3个隐藏配置错误导致87%项目延迟上线
  • tmp to ljh
  • 【海南自贸港AI语音基建必读】:ElevenLabs+海南话=政策红利窗口期仅剩87天!
  • 使用OpenClaw进行AI工作流编排时一键配置Taotoken
  • 智能体元年:一篇讲清楚 Agent 到底是什么?
  • GEO学习从入门到精通需要多长时间?
  • 告别手动统计!Allegro Quick Reports 隐藏技巧:自动生成BOM位置图并导出Excel
  • 观察taotoken多模型路由在不同负载下的响应表现
  • 【AI测试智能体实战 2】别再拿网上题库测 Agent 了:我是怎么建 190 条真实测试集的
  • AI翻唱魔法师:5分钟免费打造专业级AI音乐作品的终极指南
  • git命令入门
  • 2026 年 Haskell 基金会大变革:执行董事卸任、组织重组、董事会人员调整!
  • 标杆案例解读:富士康市值破万亿背后:代工帝国的数字化重生!
  • C++ map详解
  • 告别命令行恐惧!用pytest.ini配置文件,一键搞定Pytest测试运行
  • 想找闸门工厂?这几家值得你深入了解,速来一看!
  • 基于 PyTorch 的 TransU-Net 模型进行不同城市建筑物的精准提取 来继续遥感图像语义分割
  • 前端高频难题——防抖与节流的精准实现(避坑版)
  • 数字孪生完整教程(开发工具 + 三方对接全流程)
  • Aube:下一代 Node.js 包管理器,性能远超 pnpm
  • 书匠策AI官网www.shujiangce.com:论文降重降AIGC,原来可以这么丝滑?
  • STM32F103C8T6最小系统板避坑指南:从ST-LINK连接到Keil5乱码,新手常踩的5个坑
  • 多智能体系统的最大难题:不是推理,而是协同
  • 告别乱码!手把手教你为SquareLine Studio 1.3.1添加中文字体库(附常用字库文件)
  • 10 万行 Rust 代码开发实测封神!AI 应用经验大揭秘
  • 【AI入门知识点】Agent 是什么?为什么说它是 AI 的下一阶段?
  • 开源|一款零服务器代码知识图谱引擎,支持多语言解析、Graph RAG 问答、AI 代理集成的代码分析平台
  • DB2里LISTAGG拼接超长数据报错?试试xmlagg+xml2clob这个组合拳(附完整SQL示例)