Skip to content

Commit

Permalink
feat: 初始化
Browse files Browse the repository at this point in the history
  • Loading branch information
wangdan-fit2cloud committed Apr 7, 2023
1 parent bde8aed commit 3b0545a
Show file tree
Hide file tree
Showing 17 changed files with 6,994 additions and 54 deletions.
285 changes: 272 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,285 @@
# samples-plus-ts
# vue3+vite 如何打包优化

## 前言
首先 npm run bulid 看一下打包之后的目录结构情况

## Project Setup
![image](src/assets/readme/1.png)

```sh
npm install
我们可以看出,第三方依赖包文件在没有处理的情况下是非常大的,而且随着版本的迭代、业务的增多,第三方依赖也会越来越多,打包出来的文件也会越来越大,从而造成网页在首次进入时比较缓慢。针对这个问题,首先想到的是分包的解决方案,将一个大文件分割成多个小文件,在第一次进入网页时可以提升加载速度。

## 区块分割(chunk split)

根据 vite 官方文档提示,在vite的配置文件,通过 build.rollupOptions 的配置进行手动配置,代码如下:
```
// vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), DefineOptions() ],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
build: {
rollupOptions: {
output: {
manualChunks(id: any) {
// 最小化拆分包
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString()
}
}
}
}
}
})
```

通过上述优化,再一次进行 npm run bulid 打包,看一下结果

![image](src/assets/readme/2.png)

这时候可以看出来,这样根据依赖分别拆分,打包出来的文件数量变多,单个文件的大小变小了。
在这个基础上可以继续扩展,例如:

### 1、根据文件分类和重命名,把css、png、js分开

```
rollupOptions: {
output: {
// 用于命名代码拆分时创建的共享块的输出命名
chunkFileNames: `assets/chunk/[name]-[hash].js`,
// 用于从入口点创建的块的打包输出格式
entryFileNames: `assets/entry/[name]-[hash].js`,
// 用于输出静态资源的命名,打包后的目录中可能会出现png、jpg、svg、ttf、gif等目录。
assetFileNames: `assets/[ext]/[name]-[hash].[ext]`,
manualChunks(id: string) {
// 最小化拆分包
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString()
}
}
}
}
```
通过chunkFileNames、entryFileNames、assetFileNames实现分包和重命名,效果如下

![image](src/assets/readme/3.png)

### 2、将业务模块分块打包,把第三方插件和实际业务模块分开

根据实际项目的具体情况分模块,比如我的 views 中,一个文件夹就是一个模块;我还想让css和图片就统一在assets中,重新规划一下文件,代码如下:

```
build: {
rollupOptions: {
output: {
// 按照一个模块一个文件,而且与第三方区分开
chunkFileNames: (chunkInfo) => {
const { name, isDynamicEntry } = chunkInfo
if (isDynamicEntry) {
return `js/views/${name}-[hash].js`
}
return `js/vendor/${name}-[hash].js`
},
// 用于从入口点创建的块的打包输出格式
entryFileNames: `js/[name]-[hash].js`,
// assetFileNames 不设置 所有css和图片文件就默认在assets中
// assetFileNames: `assets/[ext]/[name]-[hash].[ext]`,
manualChunks(id: string) {
// 最小化拆分包
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString()
}
// 一个模块只要一个文件,如果需要一个vue一个文件,以下代码可忽略
if (id.includes('/src/views/')) {
return id.toString().split('/src/views/')[1].split('/')[0]
}
}
}
}
}
```
效果如下:

![image](src/assets/readme/4.png)


### 3、第三方代码分包后,过于琐碎,只单独提出个别大文件

例如:我要拆分出来vue、element-plus、fit2cloud-ui-plus、axios

```
build: {
rollupOptions: {
output: {
// 用于命名代码拆分时创建的共享块的输出命名
chunkFileNames: (chunkInfo) => {
const { name, isDynamicEntry } = chunkInfo
if (isDynamicEntry) {
return `js/views/${name}-[hash].js`
}
return `js/vendor/${name}-[hash].js`
},
// 用于从入口点创建的块的打包输出格式
entryFileNames: `js/[name]-[hash].js`,
// 用于输出静态资源的命名,打包后的目录中可能会出现png、jpg、svg、ttf、gif等目录。
// assetFileNames: `assets/[ext]/[name]-[hash].[ext]`,
manualChunks(id: string) {
// 根据不同模块
if (id.includes('/src/views/')) {
return id.toString().split('/src/views/')[1].split('/')[0]
}
if (id.includes('node_modules')) {
if (id.includes('vue')) {
return 'vue'
} else if (id.includes('element-plus')) {
return 'element-plus'
} else if (id.includes('fit2cloud-ui-plus')) {
return 'fit2cloud-ui-plus'
} else if (id.includes('axios')) {
return 'axios'
}
return 'vendor'
}
}
}
}
}
```
效果如下:

![image](src/assets/readme/5.png)

当然 vendor 和 vue 仍然可以进一步拆分,根据需要。

如果项目中有 echart 或者 富文本编辑器,只是想把这些单独打包,并不想复杂分类,那么也可以像以下代码这样,简洁配置
```
rollupOptions: {
output: {
manualChunks: {
editor: ['mavon-editor', '@kangc/v-md-editor']
},
},
},
```
此外还有一些第三方的拆分插件工具可以,比如:vite-plugin-chunk-split。

### 4、删除不必要的依赖项或者使用 CDN

可以通过 rollup-plugin-visualizer 插件进行可视化分析,找出哪些依赖项占用了最多的空间。然后,您可以考虑删除不必要的依赖项或将其替换为更轻量级的库。

```
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
// ...
visualizer()
// ...
],
});
```
依赖项也可以引入外部CDN库????

## 打包压缩

对文件进一步压缩,让文件更小,以下是打包压缩的几种方法:

### 1、build.minify

### Compile and Hot-Reload for Development
vite官网有介绍,build.minify 包括两个模式,'terser' 和 'esbuild',build.minify开启以后,默认是为 esbuild,它比 terser 快 20-40 倍,压缩率只差 1%-2%。当设置为 'terser' 时必须先安装 Terser。

```sh
npm run dev
```
npm add -D terser
```

然后用 build.terserOptions 对 terser 进行配置。
但是也要注意,在 lib 模式下使用 'es' 时,build.minify 选项不会缩减空格,因为会移除掉 pure 标注,导致破坏 tree-shaking。

### Type-Check, Compile and Minify for Production
```
build: {
minify: 'terser',
// 去掉 console、debug
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
...
}
```
对比一下配置 terser 之后,打包出来的文件大小

```sh
npm run build
![image](src/assets/readme/6.png)

文件确实小了那么一些些,可以根据项目需要选择是否用这种模式打包。也可以区分出来开发环境和生产环境的配置,避免影响调试。

### 2、vite-plugin-compression

vite-plugin-compression 插件,可以自动在构建时进行 gzip 压缩,并支持多种压缩算法。

安装插件

```
npm install vite-plugin-compression -D
```
然后在 vite.config.ts 中进行如下配置:
```
plugins: [
vue(),
viteCompression({
// gzip静态资源压缩配置
verbose: true, // 是否在控制台输出压缩结果
disable: false, // 是否禁用压缩
threshold: 10240, // 启用压缩的文件大小限制
algorithm: 'gzip', // 采用的压缩算法
ext: '.gz', // 生成的压缩包后缀
}),
],
```

### Lint with [ESLint](https://eslint.org/)
![image](src/assets/readme/7.png)

此时打包文件夹中多出几个 gz 结尾的文件,文件大小会比没压缩之前小了一些些,但是此方法也会存在一些缺点:
- 压缩的静态文件需要消耗一定的 CPU 和内存资源;
- 针对个别大文件可以进行进一步压缩,不是很大的文件不建议这样处理,因为浏览器解压时间可能大于请求原来资源的时间,反而适得其反;
- 在某些情况下,压缩算法可能会导致压缩后的文件无法正常解压或运行,因此需要进行充分的测试和验证。


此外也有其他压缩插件,比如 rollup-plugin-gzip,功能是一样的,缺点也是增加 CPU 消耗;配合vite的内置压缩,更推荐 vite-plugin-compression。也有图片和 css 的压缩工具 vite-plugin-imagemin 等等。

## 三、懒加载和异步组件

通过懒加载模块避免一次加载所以模块,然后每个模块中还可以使用异步加载,加速网站响应速度,提高用户体验。

### 1、defineAsyncComponent

在 Vue 2.x 中,声明一个异步组件只需这样:

```
const asyncPage = () => import('./views/home.vue')
```

在 Vue 3.x 中,声明一个异步组件只需这样:

```
import { defineAsyncComponent } from 'vue'
const child = defineAsyncComponent(() => import('@/components/async-component-child.vue'))
```

```sh
npm run lint
```
const AsyncComponent = defineAsyncComponent({
loader: () => import('./AsyncComponent.vue'),
delay: 200,
timeout: 3000
})
```
8 changes: 8 additions & 0 deletions auto-imports.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-auto-import
export {}
declare global {

}
32 changes: 32 additions & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'

export {}

declare module '@vue/runtime-core' {
export interface GlobalComponents {
BackButton: typeof import('./src/components/back-button/index.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElTag: typeof import('element-plus/es')['ElTag']
LabelValue: typeof import('./src/components/view-card/label-value/index.vue')['default']
LayoutContent: typeof import('./src/components/layout-content/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ViewCard: typeof import('./src/components/view-card/index.vue')['default']
}
}
2 changes: 1 addition & 1 deletion env.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/// <reference types="vite/client" />
declare module 'path-browserify'
declare module 'path-browserify'
Loading

0 comments on commit 3b0545a

Please sign in to comment.