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';
                }
}            
         
相关推荐
一斤代码2 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子2 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年2 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子3 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina3 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路4 小时前
React--Fiber 架构
前端·react.js·架构
coderlin_4 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
伍哥的传说4 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409194 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app
我在北京coding4 小时前
element el-table渲染二维对象数组
前端·javascript·vue.js