Vue 3项目实战:从零集成Naive UI组件库的完整指南
1. 为什么选择 Naive UI一个 Vue 3 开发者的组件库选型思考如果你正在用 Vue 3 做项目并且正在为“选哪个组件库”而纠结那么 Naive UI 大概率已经进入了你的候选名单。它不是一个横空出世的新秀而是由国内开发者 TuSimple图森未来团队开源并持续维护的产物。在众多 Vue 3 组件库中Naive UI 给我的第一印象是“克制”与“完整”。它不像一些大而全的库那样试图包办一切也不像一些极简库那样需要你从零开始拼凑。它的设计哲学很明确提供一套高质量、主题可定制、类型友好的组件同时保持 API 的简洁和直觉性。我最初接触 Naive UI 是在一个需要快速搭建中后台管理系统的项目中。当时市面上已经有了 Element Plus、Ant Design Vue 等成熟选择但 Naive UI 吸引我的点在于它对 TypeScript 的“一等公民”支持以及其清爽、现代化的默认主题。更重要的是它的文档非常清晰组件示例可以直接在文档页面上进行交互和代码预览这对于快速原型开发来说效率极高。经过几个项目的实战我发现它特别适合那些对 UI 一致性、开发体验和性能有一定要求但又不想在样式定制上耗费过多精力的团队。无论是个人项目还是企业级应用它都能提供一个坚实且优雅的起点。2. 环境准备与项目创建搭建 Vue 3 TypeScript 的现代开发环境在开始安装 Naive UI 之前一个稳定且现代化的开发环境是必不可少的。虽然 Naive UI 也支持在普通的 Vue 3 项目中使用但我强烈推荐你从项目伊始就拥抱 TypeScript 和 Vite。这不仅能让你享受到 Naive UI 完整的类型提示也能获得更快的构建速度和更佳的开发体验。2.1 使用 Vite 快速初始化项目目前Vite 已经是 Vue 生态中构建工具的事实标准。它基于原生 ES 模块提供了极快的冷启动和热更新。我们使用 Vite 官方提供的模板来创建项目。打开你的终端执行以下命令# 使用 npm npm create vuelatest # 或使用 yarn yarn create vue # 或使用 pnpm pnpm create vue执行命令后你会进入一个交互式的项目创建向导。这里有几个关键选项需要你注意Project name:输入你的项目名称例如my-naive-app。Add TypeScript?选择Yes。这是充分利用 Naive UI 类型安全特性的关键。Add JSX Support?根据你的喜好选择。Naive UI 本身不依赖 JSX但如果你习惯 JSX 语法可以开启。Add Vue Router for Single Page Application development?建议选择Yes。大多数现代前端应用都是 SPA。Add Pinia for state management?建议选择Yes。Pinia 是 Vue 官方推荐的状态管理库比 Vuex 更简洁。Add Vitest for Unit Testing?可根据项目需要选择。Add an End-to-End Testing Solution?可根据项目需要选择。Add ESLint for code quality?强烈建议选择Yes以保持代码风格一致。完成选择后按照提示进入项目目录并安装依赖cd my-naive-app npm install # 或 yarn install / pnpm install至此一个基于 Vue 3、TypeScript、Vite、Vue Router 和 Pinia 的现代化项目骨架就搭建完成了。你可以运行npm run dev来启动开发服务器。2.2 项目结构与关键文件解析创建完成后你的项目结构大致如下my-naive-app/ ├── node_modules/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ │ ├── router/ │ ├── stores/ │ ├── views/ │ ├── App.vue │ └── main.ts ├── index.html ├── package.json ├── tsconfig.json ├── vite.config.ts └── ...这里需要重点关注src/main.ts和src/App.vue文件因为后续我们安装和配置 Naive UI 主要会在这两个文件中进行操作。main.ts是应用的入口文件而App.vue是根组件。3. 安装与引入 Naive UI两种主流方式详解环境就绪后我们就可以正式引入 Naive UI 了。Naive UI 提供了两种主要的引入方式完整引入和按需引入。选择哪种方式取决于你的项目规模和性能要求。3.1 方式一完整引入推荐用于快速原型和中小项目完整引入是最简单直接的方式它会将 Naive UI 的所有组件和样式一次性打包到你的项目中。这种方式的好处是配置简单无需担心按需引入的插件兼容性问题特别适合快速启动项目或组件使用非常分散的场景。首先安装 Naive UI 核心库及其字体依赖# 使用 npm npm i -D naive-ui # 使用 yarn yarn add -D naive-ui # 使用 pnpm pnpm add -D naive-ui注意这里使用-D(开发依赖) 还是--save(生产依赖) 取决于你的打包策略。对于 Vite 项目通常作为开发依赖安装即可因为最终打包时会进行 Tree Shaking尽管完整引入时效果有限。社区惯例和 Naive UI 官方文档也推荐使用-D。接下来我们需要在入口文件src/main.ts中进行全局注册// src/main.ts import { createApp } from vue import App from ./App.vue import router from ./router // 1. 引入 Naive UI 的创建函数和样式 import { create } from naive-ui // 2. 创建 Naive UI 实例 const naive create() // 3. 创建 Vue 应用并使用 Naive UI 插件 const app createApp(App) app.use(naive) // 注册 Naive UI app.use(router) app.mount(#app)同时Naive UI 使用了一种名为vfonts的字体家族提供了更美观的默认字体。我们需要在src/App.vue的style部分或全局样式文件中引入!-- src/App.vue -- template router-view / /template style /* 引入 Naive UI 的默认字体 */ import vfonts/Inter.css; /* 或者使用更适合中文的字体 */ import vfonts/OpenSans.css; /style完成以上步骤后你就可以在项目的任何.vue组件中直接使用 Naive UI 的组件了例如template n-button typeprimary我是一个按钮/n-button n-input placeholder请输入内容 / /template3.2 方式二按需引入推荐用于大型生产项目对于大型生产项目为了获得最小的打包体积和最优的首屏加载性能按需引入是更佳选择。这意味著只打包你实际使用到的组件。Naive UI 与unplugin-vue-components插件配合可以实现真正的自动按需导入连手动import语句都可以省略。首先同样需要安装naive-uipnpm add -D naive-ui然后安装实现自动导入的核心插件unplugin-vue-components及其解析器unplugin-vue-components/resolverspnpm add -D unplugin-vue-components接下来我们需要配置 Vite。修改vite.config.ts文件// vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue import Components from unplugin-vue-components/vite import { NaiveUiResolver } from unplugin-vue-components/resolvers // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), Components({ resolvers: [NaiveUiResolver()] // 自动解析 Naive UI 组件 }) ] })配置完成后神奇的事情发生了你不再需要在main.ts中全局注册 Naive UI也不需要在每个组件中手动导入NButton、NInput。直接在模板中使用即可插件会自动为你生成对应的导入语句。template !-- 直接使用无需 import -- n-button typeprimary自动导入的按钮/n-button n-input placeholder自动导入的输入框 / /template script setup langts // 这里不需要写 import { NButton, NInput } from naive-ui /script这种方式极大地提升了开发体验和代码整洁度。你可以在node_modules/.vite目录下或根据构建工具不同找到自动生成的components.d.ts文件它为 TypeScript 提供了完整的类型支持。两种方式如何选择新手、Demo、小型项目直接使用完整引入简单粗暴减少心智负担。中大型生产项目、对包体积敏感务必使用按需引入配合unplugin-vue-components这是目前 Vue 生态的最佳实践。4. 核心组件实战与深度定制从按钮到复杂数据表格安装并引入库只是第一步真正体现一个组件库价值的是其组件的易用性、功能完整性和可定制性。下面我将通过几个核心组件展示 Naive UI 在实际开发中的用法和一些进阶技巧。4.1 基础组件按钮 (Button) 与反馈组件 (Message)按钮是最基础的交互元素。Naive UI 的按钮组件提供了丰富的类型、状态和尺寸。template n-space !-- NSpace 是一个方便的布局组件用于提供间距 -- n-button typeprimary clickhandlePrimaryClick主要按钮/n-button n-button typesuccess clickhandleSuccessClick成功按钮/n-button n-button typewarning警告按钮/n-button n-button typeerror错误按钮/n-button n-button ghost幽灵按钮/n-button n-button :loadingisLoading clickstartLoading加载状态/n-button n-button dashed虚线按钮/n-button n-button round圆角按钮/n-button n-button circle template #icon n-iconSearchIcon //n-icon /template /n-button /n-space /template script setup langts import { ref } from vue import { NButton, NSpace, NIcon, useMessage } from naive-ui import { SearchOutline as SearchIcon } from vicons/ionicons5 // 需要额外安装图标库 const message useMessage() // 使用 Composition API 方式调用反馈组件 const isLoading ref(false) const handlePrimaryClick () { message.info(点击了主要按钮) } const handleSuccessClick () { message.success(操作成功, { duration: 2500 }) // 可以自定义持续时间 } const startLoading () { isLoading.value true setTimeout(() { isLoading.value false message.success(加载完成) }, 2000) } /script这里有几个关键点useMessage: 这是一个 Composition API 钩子用于在script setup中调用全局的 Message 组件。它比在模板中使用n-message-provider更简洁。图标集成: Naive UI 不内置图标但可以与任何图标库无缝集成。官方推荐使用xicons项目下的图标集如vicons/ionicons5。你需要单独安装pnpm add -D vicons/ionicons5并在使用图标的地方按需引入。属性丰富:type,ghost,dashed,round,circle,loading等属性可以灵活组合出各种样式的按钮。4.2 表单组件数据绑定、验证与布局表单是后台管理系统中最常见的部分。Naive UI 的表单组件 (NForm,NFormItem) 与模型绑定和验证库的集成非常优雅。template n-form refformRef :modelformModel :rulesrules label-placementleft :label-width100 sizemedium n-form-item label用户名 pathusername n-input v-model:valueformModel.username placeholder请输入用户名 / /n-form-item n-form-item label邮箱 pathemail n-input v-model:valueformModel.email placeholder请输入邮箱 / /n-form-item n-form-item label城市 pathcity n-select v-model:valueformModel.city placeholder请选择城市 :optionscityOptions / /n-form-item n-form-item label备注 pathremark n-input v-model:valueformModel.remark typetextarea placeholder请输入备注 :autosize{ minRows: 3, maxRows: 5 } / /n-form-item n-form-item n-space n-button typeprimary clickhandleSubmit提交验证/n-button n-button clickhandleReset重置/n-button /n-space /n-form-item /n-form /template script setup langts import { ref } from vue import { NForm, NFormItem, NInput, NSelect, NSpace, NButton, type FormInst, type FormRules } from naive-ui import { useMessage } from naive-ui const message useMessage() const formRef refFormInst | null(null) // 表单数据模型 const formModel ref({ username: , email: , city: null, remark: }) // 表单验证规则 const rules: FormRules { username: [ { required: true, message: 请输入用户名, trigger: [blur, input] }, { min: 3, max: 10, message: 用户名长度在 3 到 10 个字符, trigger: [blur, input] } ], email: [ { required: true, message: 请输入邮箱, trigger: [blur, input] }, { type: email, message: 请输入有效的邮箱地址, trigger: [blur, input] } ], city: [ { required: true, type: number, message: 请选择城市, trigger: [blur, change] } ] } // 选择器选项 const cityOptions [ { label: 北京, value: 1 }, { label: 上海, value: 2 }, { label: 广州, value: 3 }, { label: 深圳, value: 4 } ] const handleSubmit (e: MouseEvent) { e.preventDefault() formRef.value?.validate((errors) { if (!errors) { message.success(表单验证通过) console.log(提交的数据, formModel.value) // 这里可以发起 API 请求 } else { message.error(表单验证失败请检查输入) console.log(验证错误, errors) } }) } const handleReset (e: MouseEvent) { e.preventDefault() formModel.value { username: , email: , city: null, remark: } } /script实战心得path属性在NFormItem上设置的path属性必须与model中字段的路径以及rules对象的键名完全一致这是验证和错误信息关联的关键。验证触发时机通过trigger配置可以灵活控制验证时机如[blur, input]表示失去焦点和输入时都验证。对于频繁输入的字段可以只使用blur以减少计算。FormInst类型对formRef使用 TypeScript 类型FormInst可以获得完善的代码提示如validate、restoreValidation等方法。4.3 数据展示表格 (DataTable) 的高级用法数据表格是后台系统的核心。Naive UI 的NDataTable功能强大支持虚拟滚动、多级表头、自定义单元格渲染、排序、过滤等。template n-data-table reftableRef :columnscolumns :datatableData :paginationpagination :loadingloading :row-key(row) row.id update:pagehandlePageChange update:sorterhandleSorterChange / /template script setup langts import { ref, reactive, h, onMounted } from vue import { NDataTable, NButton, NTag, NSpace, type DataTableColumns, useMessage } from naive-ui interface UserData { id: number name: string age: number address: string status: active | banned tags: string[] } const message useMessage() const loading ref(false) const tableData refUserData[]([]) // 分页配置 const pagination reactive({ page: 1, pageSize: 10, showSizePicker: true, pageSizes: [10, 20, 50], onChange: (page: number) { pagination.page page fetchData() }, onUpdatePageSize: (pageSize: number) { pagination.pageSize pageSize pagination.page 1 fetchData() } }) // 定义表格列 const columns: DataTableColumnsUserData [ { title: ID, key: id, width: 80, sorter: default // 启用默认排序 }, { title: 姓名, key: name, render(row) { // 自定义渲染可以返回任何 VNode return h(span, { style: { fontWeight: bold, color: #18a058 } }, row.name) } }, { title: 年龄, key: age, sorter: (a, b) a.age - b.age // 自定义排序函数 }, { title: 地址, key: address, ellipsis: { tooltip: true } // 超出显示省略号hover 显示 tooltip }, { title: 状态, key: status, render(row) { // 根据状态渲染不同的标签 const type row.status active ? success : error const text row.status active ? 活跃 : 禁用 return h(NTag, { type, bordered: false }, { default: () text }) }, filterOptions: [ { label: 活跃, value: active }, { label: 禁用, value: banned } ], filter(value, row) { return row.status value } }, { title: 标签, key: tags, render(row) { // 渲染多个标签 return h( NSpace, { size: small }, () row.tags.map(tag h(NTag, { size: small, type: info }, { default: () tag })) ) } }, { title: 操作, key: actions, width: 150, render(row) { return h(NSpace, null, { default: () [ h(NButton, { size: small, onClick: () handleEdit(row) }, { default: () 编辑 }), h(NButton, { size: small, type: error, onClick: () handleDelete(row) }, { default: () 删除 }) ] }) } } ] // 模拟获取数据 const fetchData async () { loading.value true try { // 模拟 API 请求 await new Promise(resolve setTimeout(resolve, 800)) const mockData: UserData[] Array.from({ length: 100 }, (_, i) ({ id: i 1, name: 用户 ${i 1}, age: 20 Math.floor(Math.random() * 30), address: 中国某省某市某区某街道 ${i 1} 号, status: Math.random() 0.3 ? active : banned, tags: [VIP, 新用户, 活跃].slice(0, Math.floor(Math.random() * 3) 1) })) // 模拟分页 const start (pagination.page - 1) * pagination.pageSize const end start pagination.pageSize tableData.value mockData.slice(start, end) // 在实际项目中这里应该从后端接口获取 total // pagination.itemCount total } catch (error) { message.error(数据加载失败) } finally { loading.value false } } const handlePageChange (page: number) { console.log(页码改变至:, page) } const handleSorterChange (sorter: any) { console.log(排序条件改变:, sorter) // 这里可以根据 sorter 对本地数据排序或向服务器发送新的排序请求 } const handleEdit (row: UserData) { message.info(编辑用户: ${row.name}) } const handleDelete (row: UserData) { message.warning(确定删除用户: ${row.name}?) // 实际项目中应弹出确认对话框 } onMounted(() { fetchData() }) /script深度解析与避坑指南虚拟滚动 (virtual-scroll)当数据量极大如上万条时务必开启virtual-scroll属性。它只会渲染可视区域内的行能极大提升性能。但注意虚拟滚动下行高必须是固定的或可通过estimate-size属性估算。render函数这是NDataTable最强大的特性之一。它允许你使用 Vue 的h函数或 JSX 完全自定义单元格内容。你可以在这里嵌入其他 Naive UI 组件、图标甚至复杂的交互逻辑。排序与过滤sorter和filter属性可以配置客户端排序/过滤。对于大数据集更常见的做法是将排序和过滤参数传递给后端 API由服务器处理。NDataTable通过update:sorter和update:filters事件提供了这种模式的完美支持。分页集成分页逻辑需要你自己处理。示例中演示了前端模拟分页真实项目通常需要将page和pageSize作为参数传递给后端并接收后端返回的total总数来更新pagination.itemCount。类型安全使用DataTableColumnsUserData来定义columns可以获得完美的 TypeScript 类型推断和代码提示row参数会自动被识别为UserData类型。4.4 主题与全局配置打造品牌化界面Naive UI 内置了一套基于 TypeScript 的、类型安全的全量主题变量系统。你可以轻松地修改这些变量来匹配你的品牌色或设计规范。全局配置通常在应用入口处完成。我们修改src/main.ts// src/main.ts import { createApp } from vue import App from ./App.vue import router from ./router import { create, NConfigProvider } from naive-ui import type { GlobalThemeOverrides } from naive-ui // 定义你的主题覆盖配置 const themeOverrides: GlobalThemeOverrides { common: { primaryColor: #3366FF, // 将主色改为蓝色 primaryColorHover: #5588FF, primaryColorPressed: #1144CC, borderRadius: 8px, // 全局圆角 fontSize: 14px }, Button: { colorPrimary: #3366FF, textColorPrimary: #FFFFFF, borderRadiusMedium: 8px, // 按钮高度 heightMedium: 36px, heightLarge: 42px, heightSmall: 30px }, Input: { borderRadius: 6px, heightMedium: 36px }, Card: { borderRadius: 12px, paddingMedium: 20px } // ... 可以覆盖几乎所有组件的所有样式变量 } // 创建 Naive UI 实例并注入主题配置 const naive create({ themeOverrides }) const app createApp(App) // 使用 NConfigProvider 包裹根组件以应用全局配置如主题、组件默认尺寸等 app.component(NConfigProvider.name, NConfigProvider) // 全局注册 ConfigProvider app.use(naive) app.use(router) app.mount(#app)同时在src/App.vue中我们可以使用NConfigProvider来设置一些全局的组件默认行为!-- src/App.vue -- template n-config-provider :themenaiveTheme :theme-overridesthemeOverrides :localezhCN :date-localedateZhCN :breakpoints{ xs: 0, s: 640, m: 1024, l: 1280, xl: 1536 } router-view / /n-config-provider /template script setup langts import { NConfigProvider, darkTheme, zhCN, dateZhCN } from naive-ui import type { GlobalThemeOverrides } from naive-ui // 可以选择亮色或暗色主题 const naiveTheme null // 默认亮色主题 // const naiveTheme darkTheme // 使用暗色主题 // 这里也可以定义局部的 themeOverrides会与 main.ts 中的合并优先级更高 const themeOverrides: GlobalThemeOverrides { // 可以覆盖或补充全局配置 } /script style import vfonts/Inter.css; /style主题定制心得变量查找Naive UI 的所有主题变量都可以在官方文档的“主题”章节找到。最方便的方式是在开发时通过浏览器的开发者工具直接检查 Naive UI 组件的 CSS 变量以--n-开头从而知道需要覆盖哪个变量。配置合并主题配置可以在多个层级设置create参数、NConfigProvider的theme-overrides属性它们会进行合并。NConfigProvider层级的配置优先级更高。暗色主题只需将darkTheme对象传递给NConfigProvider的theme属性即可一键切换至暗色模式。你甚至可以结合useOsTheme或useTheme钩子实现跟随系统主题或手动切换。国际化通过locale和date-locale属性可以轻松将组件语言切换为中文zhCN,dateZhCN或其他语言。5. 常见问题排查与性能优化实战在实际项目中使用 Naive UI你可能会遇到一些典型问题。下面是我总结的几个高频问题及其解决方案。5.1 样式丢失或布局错乱问题描述组件功能正常但样式完全没生效或者布局很奇怪。可能原因1字体未引入。Naive UI 的部分组件如NDataTable的表头依赖vfonts字体来正确计算宽度。解决方案确保在App.vue或全局 CSS 中引入了vfonts字体如前文所述import vfonts/Inter.css;。可能原因2样式引入顺序冲突。如果项目中有其他全局样式如Tailwind CSS、Normalize.css或旧的 CSS 重置样式可能会覆盖 Naive UI 的样式。解决方案检查样式引入顺序确保 Naive UI 的样式在最后引入。在main.ts中确保import naive-ui/style如果完整引入或unplugin-vue-components自动导入的样式位于其他全局样式之后。在 Vite 中可以通过调整index.html中link标签的顺序来控制。可能原因3CSS 作用域问题。在.vue文件的style scoped中尝试修改子组件Naive UI 组件的样式可能会失效。解决方案对于需要深度修改组件内部样式的情况使用:deep()选择器。/* 错误在 scoped 样式中直接修改子组件样式可能无效 */ .my-wrapper .n-button { color: red; } /* 正确使用深度选择器 */ .my-wrapper :deep(.n-button) { color: red; }5.2 按需引入后类型提示丢失或组件未找到问题描述配置了unplugin-vue-components自动导入后在模板中使用组件没问题但在script setup中想调用组件实例的方法或使用其类型时TypeScript 报错“找不到名称”。可能原因自动导入只解决了模板中的组件引用但没有解决 TypeScript 上下文中的类型引用。解决方案对于组件类型手动导入类型。import type { DataTableColumns, FormInst } from naive-ui // 这样DataTableColumns 和 FormInst 类型就可以正常使用了对于组件实例方法使用模板ref并指定正确的类型。这在上面的表格和表单示例中已经展示过。import { NModal } from naive-ui const modalRef refInstanceTypetypeof NModal | null(null) // 或者使用组件暴露出的实例类型如果文档有说明 // const modalRef refModalInst | null(null) const handleOpen () { modalRef.value?.show() // 现在有类型提示了 }确保components.d.ts生成检查项目根目录或src目录下是否有components.d.ts文件。这是unplugin-vue-components自动生成的类型声明文件。如果不存在检查 Vite 配置是否正确并尝试重启开发服务器。5.3 打包体积过大问题描述即使使用了按需引入最终打包的产物中仍然包含了未使用的组件代码。可能原因1unplugin-vue-components配置问题或解析失败。某些动态组件或非常规使用方式可能导致插件无法正确识别。解决方案检查vite.config.ts中的Components插件配置。确保NaiveUiResolver已正确添加。可以尝试在插件配置中开启debug模式查看解析日志。Components({ resolvers: [NaiveUiResolver()], dts: true, // 确保生成类型文件 debug: true // 开启调试查看控制台输出 })可能原因2间接依赖了未使用的组件。例如你只用了NDataTable但它内部可能依赖了NCheckbox、NRadio等组件这些都会被一起打包。解决方案这是组件库设计的正常情况通常无法避免。你可以使用rollup-plugin-visualizer等分析工具查看具体的依赖树确认体积大头是否真的是 Naive UI。通常 Naive UI 按需引入后的体积在可接受范围内gzip 后约 100-200KB。优化建议图标库按需导入如果你使用了vicons/系列的图标确保也进行了按需导入。可以配合unplugin-icons插件来实现。启用 Gzip/Brotli 压缩在服务器端或构建时启用压缩可以大幅减少传输体积。代码分割利用 Vite 的动态导入 (import()) 特性将非首屏必需的组件或路由进行懒加载。5.4 复杂场景下的性能优化对于渲染超长列表如日志查看器或复杂表单的页面即使使用了虚拟滚动也可能遇到性能瓶颈。策略一减少不必要的响应式数据。在定义表格的columns或表单的rules时如果它们不会变化就不要用ref或reactive包裹直接使用常量。避免将大量静态数据放入响应式系统。策略二善用shallowRef和shallowReactive。对于深层嵌套但内部数据不常变化的大对象如一个复杂的配置对象使用shallowRef或shallowReactive可以避免 Vue 对其内部属性进行深度响应式转换提升性能。策略三组件懒加载。对于弹窗、抽屉等非立即显示的组件使用defineAsyncComponent进行懒加载。script setup langts import { defineAsyncComponent } from vue const AsyncHeavyComponent defineAsyncComponent(() import(./HeavyComponent.vue)) /script策略四使用NSpin和骨架屏。在数据加载时使用NSpin或自定义骨架屏提供良好的加载体验避免页面长时间白屏或卡顿。从我个人的使用经验来看Naive UI 在大多数场景下性能表现都非常出色。其虚拟滚动实现得很高效组件设计也考虑到了性能。真正的性能瓶颈往往出现在业务逻辑本身比如低效的数据处理、过多的计算属性、不当的副作用函数等。因此在怀疑组件库性能之前先用浏览器的 Performance 面板分析一下找到真正的耗时点。

相关新闻

最新新闻

日新闻

周新闻

月新闻