11. vite打包优化

1.基础配置vite.config.js

js 复制代码
import { defineConfig, loadEnv } from 'vite';
import { viteMockServe } from 'vite-plugin-mock';
import type { UserConfig, ConfigEnv } from 'vite';
import { fileURLToPath } from 'url';
import AutoImport from 'unplugin-auto-import/vite';
import Icons from 'unplugin-icons/vite';
import Components from 'unplugin-vue-components/vite';
import IconsResolver from 'unplugin-icons/resolver';
import ElementPlus from 'unplugin-element-plus/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
    // 获取当前工作目录
    const root = process.cwd();
    // 获取环境变量
    const env = loadEnv(mode, root);
    console.log(env);
    return {
        // 项目根目录
        root,
        // 项目部署的基础路径
        base: './',
        publicDir: fileURLToPath(new URL('./public', import.meta.url)), // 无需处理的静态资源位置
        assetsInclude: fileURLToPath(new URL('./src/assets', import.meta.url)), // 需要处理的静态资源位置
        plugins: [
            // Vue模板文件编译插件
            vue(),
            // jsx文件编译插件
            vueJsx(),
            // 开启mock服务器
            viteMockServe({
                // 如果接口为 /mock/xxx 以 mock 开头就会被拦截响应配置的内容
                mockPath: 'mock', // 数据模拟需要拦截的请求起始 URL
                enable: true // 本地环境是否开启 mock 功能
            }),
            // 开启ElementPlus自动引入CSS
            ElementPlus({}),
            // 自动导入组件
            AutoImport({
                // 定义需要自动引入的框架
                imports: ['vue', 'vue-router', 'pinia'],
                // 处理eslint
                eslintrc: {
                    enabled: true
                },
                resolvers: [ElementPlusResolver(), IconsResolver()],
                dts: fileURLToPath(new URL('./types/auto-imports.d.ts', import.meta.url))
            }),
            // 自动注册组件
            Components({
                resolvers: [ElementPlusResolver(), IconsResolver()],
                dts: fileURLToPath(new URL('./types/components.d.ts', import.meta.url))
            }),
            Icons({
                autoInstall: true
            })
        ],
        // 运行后本地预览的服务器
        server: {
            // 是否开启https
            https: false,
            // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
            host: true,
            // 开发环境预览服务器端口
            port: 9001,
            // 启动后是否自动打开浏览器
            open: false,
            // 是否开启CORS跨域
            cors: true,
            // 代理服务器
            // 帮助我们开发时解决跨域问题
            proxy: {
                // 这里的意思是 以/api开头发送的请求都会被转发到 http://xxx:9000
                [env.VITE_APP_API_BASEURL]: {
                    target: 'http://localhost:9001',
                    // 改变 Host Header
                    changeOrigin: true
                    // 发起请求时将 '/api' 替换为 ''
                    //rewrite: (path) => path.replace(/^\/api/, ""),
                },
                [env.VITE_APP_MOCK_BASEURL]: {
                    target: 'http://localhost:9001',
                    // 改变 Host Header
                    changeOrigin: true
                    // 发起请求时将 '/api' 替换为 ''
                    //rewrite: (path) => path.replace(/^\/api/, ""),
                }
            }
        },
        // 打包配置
        build: {
            // 关闭 sorcemap 报错不会映射到源码
            sourcemap: false,
            // 打包大小超出 400kb 提示警告
            chunkSizeWarningLimit: 400,
            rollupOptions: {
                // 打包入口文件 根目录下的 index.html
                // 也就是项目从哪个文件开始打包
                input: {
                    index: fileURLToPath(new URL('./index.html', import.meta.url))
                },
                // 静态资源分类打包
                output: {
                    format: 'esm',
                    chunkFileNames: 'static/js/[name]-[hash].js', // 代码分割后文件名
                    entryFileNames: 'static/js/[name]-[hash:6].js', // 入口文件名
                    assetFileNames: 'static/[ext]/[name]-[hash].[ext]' // 静态资源文件名
                }
            }
        },
        // 配置别名
        resolve: {
            alias: {
                '@': fileURLToPath(new URL('./src', import.meta.url)),
                '#': fileURLToPath(new URL('./types', import.meta.url))
            }
        }
    };
});

1.静态资源分类打包

1.业务逻辑和npm包分类打包 缺点:

  1. 所有文件打包在一起,首屏加载过慢
  2. 资源无法做到最大限度有效缓存,修改某个文件,会全量更新
js 复制代码
 build: {
            // 关闭 sorcemap 报错不会映射到源码
            sourcemap: false,
            // 打包大小超出 400kb 提示警告
            chunkSizeWarningLimit: 400,
            rollupOptions: {
                // 打包入口文件 根目录下的 index.html
                // 也就是项目从哪个文件开始打包
                input: {
                    index: fileURLToPath(new URL('./index.html', import.meta.url))
                },
                // 静态资源分类打包
                output: {
                    manualChunks: (id: string) => {
                        // node_modules包打包在vendor中,剩余的业务组件打包到index中
                        if (id.includes('node_modules')) {
                            return 'vendor';
                        }
                        return 'index';
                    },
                }
            }
        },

2. 生成可视化分析文件rollup-plugin-visualizer

js 复制代码
import { visualizer } from 'rollup-plugin-visualizer';
// 打包配置
        build: {
            // 关闭 sorcemap 报错不会映射到源码
            sourcemap: false,
            // 打包大小超出 400kb 提示警告
            chunkSizeWarningLimit: 400,
            rollupOptions: {
                // 打包入口文件 根目录下的 index.html
                // 也就是项目从哪个文件开始打包
                input: {
                    index: fileURLToPath(new URL('./index.html', import.meta.url))
                },
                plugins: [visualizer({ open: true })],
                // 静态资源分类打包
                output: {
                    manualChunks: (id: string) => {
                        // node_modules包打包在vendor中,剩余的业务组件打包到index中
                        if (id.includes('node_modules')) {
                            return 'vendor';
                        }
                        return 'index';
                    },
                }
            }
        },

3.合并一些小的文件,避免资源加载阻塞

配置选项 | rollup.js 中文文档 | rollup.js中文网

仅当合并不会改变任何入口加载时执行的副作用时,才会执行合并。对于值为 1 的情况,仅允许执行不增加任何入口加载的代码量的合并。

js 复制代码
build: {
            // 关闭 sorcemap 报错不会映射到源码
            sourcemap: false,
            // 打包大小超出 400kb 提示警告
            chunkSizeWarningLimit: 400,
            rollupOptions: {
                // 打包入口文件 根目录下的 index.html
                // 也就是项目从哪个文件开始打包
                input: {
                    index: fileURLToPath(new URL('./index.html', import.meta.url))
                },
                plugins: [visualizer({ open: true })],
                // 静态资源分类打包
                experimentalLogSideEffects: true,// 该选项值为 `true` 时,将会在每个文件发现的第一个副作用打印到控制台。这对于计算哪些文件有副作用以及实际的副作用是什么非常有帮助
                treeshake: { // tressshaking实现优化
                    preset: 'recommended'
                    // propertyReadSideEffects: true
                },
                output: {
                    // experimentalMinChunkSize: 20 * 1024,
                    manualChunks: (id: string) => {
                        // node_modules包打包在vendor中,剩余的业务组件打包到index中
                        if (id.includes('node_modules')) {
                            return 'vendor';
                        }
                        // return 'index';
                    },
                }
            }
        },

4.第三方引入处理 外链插件rollup-plugin-external-globals

js 复制代码
import externalGlobals from 'rollup-plugin-external-globals';
const globals = externalGlobals({
    moment: 'moment',
    'video.js': 'videojs',
    jspdf: 'jspdf',
    xlsx: 'XLSX',
    echart: 'echart'
});
build: {
            // 关闭 sorcemap 报错不会映射到源码
            sourcemap: false,
            // 打包大小超出 400kb 提示警告
            chunkSizeWarningLimit: 400,
            rollupOptions: {
                // 打包入口文件 根目录下的 index.html
                // 也就是项目从哪个文件开始打包
                input: {
                    index: fileURLToPath(new URL('./index.html', import.meta.url))
                },
                external: ['moment', 'video.js', 'jspdf', 'xlsx', 'echart'],
                plugins: [visualizer({ open: true }), globals],
                // 静态资源分类打包
                experimentalLogSideEffects: true,// 该选项值为 `true` 时,将会在每个文件发现的第一个副作用打印到控制台。这对于计算哪些文件有副作用以及实际的副作用是什么非常有帮助
                treeshake: { // tressshaking实现优化
                    preset: 'recommended'
                    // propertyReadSideEffects: true
                },
                output: {
                    // experimentalMinChunkSize: 20 * 1024,
                    manualChunks: (id: string) => {
                        // node_modules包打包在vendor中,剩余的业务组件打包到index中
                        if (id.includes('node_modules')) {
                            return 'vendor';
                        }
                        // return 'index';
                    },
                }
            }
        },
入口文件使用cdn外链引入不需要打包的文件

还有另一种方式,不引入cdn,引入npm包的文件的方法

js 复制代码
import { createHtmlPlugin } from 'vite-plugin-html';
plugins: [
            createHtmlPlugin({
                inject: {
                    data: {
                    // TODO 两者选其一
                        monentscript:
                            '<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/min/moment.js"></script>',
                        videoscript:
                            '<script src="https://cdn.jsdelivr.net/npm/video.js@7.14.3/dist/video.min.js"></script>',
                        echartscript: '<script src="https://cdn.jsdelivr.net/npm/echarts@5.2.1/echarts"></script>',
                        jspdfscript: '<script src="https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/pdf.js"></script>',
                        xlsxscript:
                            '<script src="https://cdn.jsdelivr.net/npm/xlsx@0.17.4/dist/xlsx.full.min.js"></script>'
                    }
                }
            })
            ],
 build: {
    rollupOptions:{
    output: {
            experimentalMinChunkSize: 20 * 1024,
            manualChunks: (id: string) => {
            // TODO 两者选其一
                if (id.includes('html-canvans')) {
                    return 'html-canvans';
                } else if (id.includes('src/views/about')) {
                    return 'about';
                } else if (id.includes('src/views/auth')) {
                    return 'about';
                }else if (id.includes('node_modules')) {
                    return 'vendor';
                }
}

html模板引入

html 复制代码
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <link rel="icon" href="/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>AIChat</title>
    </head>
    <body>
        <!-- 令 id="app" 便于vue进行挂载 -->
        <div id="app"></div>
        <!-- 引入main.ts文件 -->
        <script type="module" src="/src/main.ts"></script>
    </body>
    <%- monentscript %> <%- videoscript %> <%- echartscript %> <%- jspdfscript %> <%- xlsxscript %>
    <!-- <script src="https://.../moment.min.js"></script>
    <script src="https://.../xlsx.full.min.js"></script>
    <script src="https://.../jspdf.polyfills.umd.js"></script>
    <script src="https://.../jspdf.umd.min.js"></script>
    <script src="https://.../video.min.js"></script> -->
</html>

5. gzip打包压缩

1. vite-plugin-compression

js 复制代码
import ViteCompression from 'vite-plugin-compression';
plugins: [ViteCompression({
                threshold: 1024 * 20, // 超过20kb才进行压缩
                ext: '.gz', // 压缩后缀
                algorithm: 'gzip' // 压缩算法
            })],

2.rollup-plugin-brotli 新的压缩算法,更高的压缩质量

js 复制代码
import brotli from 'rollup-plugin-brotli';
plugins: [brotli({}),]

6.实现路由两个页面合并打包vite-plugin-webpackchunkname

js 复制代码
import { manualChunksPlugin } from '`vite-plugin-webpackchunkname`';
plugins:[manualChunksPlugin(),
Components({
                resolvers: [ElementPlusResolver(), IconsResolver()],
                dts: fileURLToPath(new URL('./types/components.d.ts', import.meta.url)),
                include: [/\.vue$/, /\.vue\?/] // 不写这个,样式生产加载不出来
            })],
 build: {
    rollupOptions:{
    output: {
            experimentalMinChunkSize: 20 * 1024,
            manualChunks: (id: string) => {
            // TODO 两者选其一
                if (id.includes('html-canvans')) {
                    return 'html-canvans';
                } else if (id.includes('src/views/about')) {
                    return 'about';
                } else if (id.includes('src/views/auth')) {
                    return 'about';
                }else if (id.includes('node_modules')) {
                    return 'vendor';
                }
}            
         
相关推荐
To_OC6 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC6 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic6 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy6 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志7 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble8 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长8 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯8 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜9 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司9 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能