记录一下Vite配置CDN优化打包Vue项目相关

前言:最近升级了一下之前写的前端实现的工具包的Vue环境,在服务器上通过Jenkins打包时导致别的服务宕机无法使用,该问题在之前的另外一个工具上有过,不过通过Vite打包配置解决了(可能引用的第三方包比较少),而这次工具包下使用的第三方包照着之前的配置还是宕机,后来想想,好像有些开源库如

发现该问题在Vite的GitHub论坛里面也是有人询问

https://github.com/vitejs/vite/discussions/13250

image-20240208111213201

打包错误可能是 这类

image-20231101095511540

或者这类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> generator-ui@1.0.0 build /var/lib/jenkins/workspace/generator-ui
> vite build --mode pro



�[36m�[1mBuild successful. Please see dist directory�[22m�[39m

[error] �[31merror during build:
Error: The service was stopped
at /var/lib/jenkins/workspace/generator-ui/node_modules/.pnpm/registry.npmmirror.com+esbuild@0.17.19/node_modules/esbuild/lib/main.js:816:29
at Object.responseCallbacks.<computed> (/var/lib/jenkins/workspace/generator-ui/node_modules/.pnpm/registry.npmmirror.com+esbuild@0.17.19/node_modules/esbuild/lib/main.js:697:9)
at Socket.afterClose (/var/lib/jenkins/workspace/generator-ui/node_modules/.pnpm/registry.npmmirror.com+esbuild@0.17.19/node_modules/esbuild/lib/main.js:687:28)
at Socket.emit (node:events:525:35)
at endReadableNT (node:internal/streams/readable:1358:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)�[39m
 ELIFECYCLE  Command failed with exit code 1.
Build step 'Execute shell' marked build as failure
Finished: FAILURE

1、配置打包后的依赖分析

  • 安装依赖分析工具

    1
    pnpm add -D rollup-plugin-visualizer
  • 配置依赖分析工具,在项目的vite.config.ts中配置如下信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import visualizer from 'rollup-plugin-visualizer'

    // https://vitejs.dev/config/
    export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
    return {
    server: {
    port: 5173,
    host: '0.0.0.0',
    // Cannot read properties of null (reading 'invalidateTypeCache') 错误处理
    hmr: { overlay: false },
    },
    plugins: [
    vue(),
    // 分析项目下各个文件的相关依赖
    visualizer({
    emitFile: false,
    filename: 'stats.html', //分析图生成的文件名
    open: false //如果存在本地服务端口,将在打包后自动展示
    })
    ]
    })
  • 执行打包命令后,会产生如下文件:

    image-20231204154908505

  • 打开运行该文件,看见如下依赖分析

    image-20231204155852309

    可看见我当前鼠标悬浮处的ant-design-vue有2.65M左右,该分析页面一般是文件最大的所占表格越大

  • 打包耗时如下:

    image-20240208105257107

2、使用vite默认配置排除打包

在vite配置文件vite.config.ts下添加如下配置信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
build: {
assetsDir: 'assets',
chunkSizeWarningLimit: 1000,
// 配置打包文件路径和命名
minify: 'terser',
outDir: 'tool-plus',
// 取消计算文件大小,加快打包速度
reportCompressedSize: false,
sourcemap: false,
target: 'es2015',
terserOptions: {
compress: {
// 生产环境时移除console.log调试代码
drop_console: true,
drop_debugger: true
}
},
rollupOptions: {
// 配置哪些插件不打入文件中
external: ['ant-design-vue']
}
}

排除依赖后打包分析插件页面如下:

image-20231204164333078

耗时如下:

image-20240208105619213

注意:这种排除打包可能会导致有些依赖无法排除

当然如果排除不了某些包,rollupOptions 中的external可以使用正则来排除,我在有次排除时老是无法排除,后来通过正则解决了,

其中的 @ant-design+icons-svg@ant-design+icons-vue ant-design-vue@4.0.0_vue老是排除不了

image-20240208110251341

使用正则排除如下:

1
2
3
4
5
6
external: [
// 其他包 ...
/^@ant-design\/icons-vue/,
/^@ant-design\/icons-svg/,
/^@ant-design\/colors/
],

排除结果

image-20240208110818985

但是某次我在别的电脑上没有使用正则时又可以正确排除,具体没怎么研究,这里先记录一下吧

3、使用rollup-plugin-external-globals排除打包

  • 安装插件

    1
    pnpm add -D rollup-plugin-external-globals
  • 配置插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // 配置打包时忽略某些包
    import externalGlobals from 'rollup-plugin-external-globals'

    // https://vitejs.dev/config/
    export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
    return {
    server: {
    port: 5173,
    host: '0.0.0.0',
    // Cannot read properties of null (reading 'invalidateTypeCache') 错误处理
    hmr: { overlay: false },
    },
    plugins: [
    // 分析项目下各个文件的相关依赖
    visualizer({
    emitFile: false,
    filename: 'stats.html', //分析图生成的文件名
    open: false //如果存在本地服务端口,将在打包后自动展示
    }),
    ],
    build: {
    assetsDir: 'assets',
    chunkSizeWarningLimit: 1000,
    // 配置打包文件路径和命名
    minify: 'terser',
    outDir: 'tool-plus',
    // 取消计算文件大小,加快打包速度
    reportCompressedSize: false,
    sourcemap: false,
    target: 'es2015',
    terserOptions: {
    compress: {
    // 生产环境时移除console.log调试代码
    drop_console: true,
    drop_debugger: true
    }
    },
    rollupOptions: {
    output: {
    globals: {
    vue: 'Vue',
    }
    },
    // // 打包时忽略某些包,避免打包时间过长
    plugins: [
    externalGlobals({
    'ant-design-vue': 'antd',
    'dayjs': 'dayjs',
    }) as any
    ],
    },
    },
    }
    })

4、使用unplugin-auto-import排除打包

  • 安装插件

    1
    pnpm add -D unplugin-auto-import
  • 配置插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // 配置打包CDN
    import importToCDN from 'vite-plugin-cdn-import'

    // https://vitejs.dev/config/
    export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
    return {
    server: {
    port: 5173,
    host: '0.0.0.0',
    // Cannot read properties of null (reading 'invalidateTypeCache') 错误处理
    hmr: { overlay: false },
    },
    plugins: [
    vue(),
    importToCDN({
    // 配置CDN服务地址
    prodUrl: 'https://cdnjs.cloudflare.com/ajax/libs/',
    modules: [
    // 因为ant-design-vue使用了dayjs插件,需要配置dayjs
    {
    name: 'dayjs',
    var: 'dayjs',
    path: [
    'dayjs/dayjs.min.js',
    'dayjs/plugin/customParseFormat.min.js',
    'dayjs/plugin/weekday.min.js',
    'dayjs/plugin/localeData.min.js',
    'dayjs/plugin/weekOfYear.min.js',
    'dayjs/plugin/weekYear.min.js',
    'dayjs/plugin/advancedFormat.min.js',
    'dayjs/plugin/quarterOfYear.min.js'
    ],
    },
    {
    name: 'ant-design-vue',
    var: 'antd',
    path: 'ant-design-vue/antd.min.js',
    css: 'ant-design-vue/reset.min.css'
    },
    ]
    })
    ]
    })

注意:这个排除打包目前只能配置CDN配置,如果是本地配置静态文件需要修改源码

如果需要配置本地静态资源,修改代码如下:

源代码:

1
2
3
4
5
6
7
function renderUrl(url, data) {
const {path: path2} = data;
if (isFullPath(path2)) {
url = path2;
}
return url.replace(/\{name\}/g, data.name).replace(/\{version\}/g, data.version).replace(/\{path\}/g, path2);
}

修改后

1
2
3
4
5
6
7
8
9
10
function renderUrl(url, data) {
const {path: path2} = data;
if (isFullPath(path2)) {
url = path2;
}
if (url === 'local') {
url = path2;
}
return url.replace(/\{name\}/g, data.name).replace(/\{version\}/g, data.version).replace(/\{path\}/g, path2);
}

修改后的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
importToCDN({
prodUrl: 'local',
modules: [
{
name: 'dayjs',
var: 'dayjs',
path: [
'dayjs/dayjs.min.js',
'dayjs/plugin/customParseFormat.min.js',
'dayjs/plugin/weekday.min.js',
'dayjs/plugin/localeData.min.js',
'dayjs/plugin/weekOfYear.min.js',
'dayjs/plugin/weekYear.min.js',
'dayjs/plugin/advancedFormat.min.js',
'dayjs/plugin/quarterOfYear.min.js'
],
},
{
name: 'ant-design-vue',
var: 'antd',
path: 'ant-design-vue/antd.min.js',
css: 'ant-design-vue/reset.min.css'
},
]
}),

如果是本地资源,需要在public下添加如下文件:

image-20231204170710669

5、配置开发环境使用node_modules下的依赖,开发环境使用CDN或者本地静态资源

5.1、需要vite-plugin-html插件协助打包时传入参数给index.html

  • 安装插件

    1
    pnpm add -D vite-plugin-html
  • 配置插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    import { ConfigEnv, defineConfig, loadEnv, UserConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import { resolve } from 'path'

    // 协助配置网页标题栏
    import { createHtmlPlugin } from 'vite-plugin-html'

    // 配置打包后的文件分析
    import visualizer from 'rollup-plugin-visualizer'


    // 配置打包时忽略某些包
    import externalGlobals from 'rollup-plugin-external-globals';

    const root = process.cwd()
    const pathResolve = (dir: string): any => {
    return resolve(__dirname, '.', dir)
    }
    const alias: Record<string, string> = {
    '@': pathResolve('src')
    }

    // https://vitejs.dev/config/
    export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
    let env = {} as any
    const isBuild = command === 'build'
    if (!isBuild) {
    env = loadEnv(process.argv[3] === '--mode' ? process.argv[4] : process.argv[3], root)
    } else {
    env = loadEnv(mode, root)
    }
    return {
    server: {
    port: 5173,
    host: '0.0.0.0',
    // Cannot read properties of null (reading 'invalidateTypeCache') 错误处理
    hmr: { overlay: false },
    },
    plugins: [
    vue(),
    createHtmlPlugin({
    inject: {
    data: {
    // 传入当前使用的环境
    envMode: env.VITE_USER_NODE_ENV,
    title: env.VITE_APP_TITLE,
    injectScript: `<script src="./inject.js"></script>`
    }
    }
    }),
    // 分析项目下各个文件的相关依赖
    visualizer({
    emitFile: false,
    filename: 'stats.html', //分析图生成的文件名
    open: false //如果存在本地服务端口,将在打包后自动展示
    }),
    ],
    build: {
    assetsDir: 'assets',
    chunkSizeWarningLimit: 1000,
    // 配置打包文件路径和命名
    minify: 'terser',
    outDir: 'tool-plus',
    // 取消计算文件大小,加快打包速度
    reportCompressedSize: false,
    sourcemap: false,
    target: 'es2015',
    terserOptions: {
    compress: {
    // 生产环境时移除console.log调试代码
    drop_console: true,
    drop_debugger: true
    }
    },
    rollupOptions: {
    output: {
    globals: {
    vue: 'Vue',
    }
    },
    // // 打包时忽略某些包,避免打包时间过长
    plugins: [
    externalGlobals({
    'ant-design-vue': 'antd',
    'dayjs': 'dayjs',
    }) as any
    ],
    },
    },
    }
    })

5.2、通过给index.html中传入的环境配置CDN或者静态资源引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<% if (envMode !== 'development') { %>
<!-- 配置资源引用 -->
<link href="ant-design-vue/reset.min.css" rel="stylesheet" />
<script src="dayjs/dayjs.min.js"></script>
<script src="dayjs/plugin/customParseFormat.min.js"></script>
<script src="dayjs/plugin/weekday.min.js"></script>
<script src="dayjs/plugin/localeData.min.js"></script>
<script src="dayjs/plugin/weekOfYear.min.js"></script>
<script src="dayjs/plugin/weekYear.min.js"></script>
<script src="dayjs/plugin/advancedFormat.min.js"></script>
<script src="dayjs/plugin/quarterOfYear.min.js"></script>
<script src="ant-design-vue/antd.min.js"></script>
<script>
console.log("not development")
</script>
<% } else { %>
<script>
console.log("development")
</script>
<% } %>