用 vite 2 平滑升级 vue 2 + webpack 项目实战

您所在的位置:网站首页 vite优缺点 用 vite 2 平滑升级 vue 2 + webpack 项目实战

用 vite 2 平滑升级 vue 2 + webpack 项目实战

2023-12-21 05:27| 来源: 网络整理| 查看: 265

目录 Vite vs. Webpack 完整迁移实战 Vite vs. Webpack 指标对比

经过实际运行,在同一项目中、采用几乎相同的设置,结果如下:

指标 \ 工具ViteVite(legecy)Vue-cli + Webpacknpm run debug 至页面可用 (ms)2405435121418npm run build 时间 (ms)197278227761000打包后的 JS 文件数量224546平均 JS 文件体积 (kb)17517488总 JS 文件体积 (kb)386478324080 开发环节区别

webpack:

先转译打包,然后启动 dev server 热更新时,把改动过模块的相关依赖模块全部编译一次

vite:

对于不会变动的第三方依赖,采用编译速度更快的go编写的esbuild预构建 对于 js/jsx/css 等源码,转译为原生 ES Module(ESM) 利用了现代浏览器支持 ESM,会自动向依赖的 Module 发出请求的特性 直接启动 dev server (不需要打包),对请求的模块按需实时编译 热更新时,仅让浏览器重新请求改动过的模块

ESM 及更早的 Javascript 模块化历史这里就不展开谈了,感兴趣的同学可以参阅这篇文章;总之太阳底下无新事,目前由 webpack 或 vite 做的这些架设本地服务、静态资源打包、动态更新的工作,起码追溯到十多年前陆续都有各种解决方案了

构建环节 考虑到加载和缓存等,在生产环境中发布未打包的 ESM 仍然效率低下 vite 利用成熟的 Rollup,完成 tree-shaking、懒加载和 chunk 分割等 源码浅析

运行 vite 命令后:

-> start() // packages/vite/bin/vite.js -> 利用 cac 工具构建可以处理 dev/build/preview 等命令的 cli 实例 -> cli.parse() // packages/vite/src/node/cli.ts 1. vite (dev 模式) -> createServer() // packages/vite/src/node/server/index.ts - resolveHttpServer() // 基于 http 原生模块创建服务 - createWebSocketServer() // 用 WebSocket 发送类似下面这样的热更新消息 - chokidar.watch(path.resolve(root), ...) // 监听源码变化 -> handleHMRUpdate() // 处理热更新 packages/vite/src/node/server/hmr.ts - updateModules() `````` ws.send({ type: 'update', updates }) [浏览器中 ws://localhost:8080/my-report/] { "type": "update", "updates": [ { "type": "js-update", "timestamp": 1646797458716, "path": "/src/app.vue", "acceptedPath": "/src/app.vue?vue&type=template&lang.js" } ] } ``````

浏览器中响应 hmr 的部分:

-> handleMessage() // packages/vite/src/client/client.ts `````` if (update.type === 'js-update') { queueUpdate(fetchUpdate(update)) } else { `````` -> fetchUpdate() `````` // 利用了浏览器的动态引入 https://github.com/tc39/proposal-dynamic-import // 可见请求如 http://.../src/app.vue?import&t=1646797458716&vue&type=template&lang.js const newMod = await import( /* @vite-ignore */ base + path.slice(1) + `?import&t=${timestamp}${query ? `&${query}` : ''}` ) `````` 2. vite build -> build() // packages/vite/src/node/cli.ts -> doBuild() // packages/vite/src/node/build.ts - resolveConfig() // 处理 vite.config.js 和 cli 参数等配置 - prepareOutDir() // 清空打包目录等 - rollup.rollup()['write']() // 用 rollup 完成实际打包和写入工作 迁移实践 业务背景和迁移原则

迁移背景:

现有项目的 webpack 开发调试和打包速度已经较慢 查看后台统计数据,项目的浏览器覆盖情况可以支持抛掉历史包袱 项目具有代表性,已经包含了 TS/JSX/FC 等写法的组件和模块 需要渐进迈向 vue3 技术栈

升级原则:

对原有开发打包流程无痛、交付产出物结构基本不变 保证线上产品安全,设置观察期并 兼容 webpack 流程 而非直接替换 覆盖后台访问记录中的主流浏览器并周知测试产品等研发环节

主要涉及文件:

/index.html -- 新的入口,原有 src/index.html 暂时保留 /vite.config.js -- vite 工具的配置文件

vite版本:

vite v2.8.2

node 版本:

node v14.19.0 实践表明 v14 可以兼顾新的 vite 和既有 webpack 两套流程 如果涉及 jenkins 等部署环节,可能需要关心相关 node 软件包的升级 package.json 依赖 "devDependencies": { "vite": "^2.8.2", "vite-plugin-vue2": "^1.9.3", "vite-plugin-html": "^3.0.4", "vite-plugin-time-reporter": "^1.0.0", "sass": "^1.49.7", "rollup-plugin-copy": "^3.4.0", "@vue/compiler-sfc": "^3.2.31", }, npm scripts "debug": "vite --mode dev", "build": "vite build --mode production", "preview": "vite preview --port 8082",

之前的 webpack 命令加前缀(如:"webpack:build"),继续可用

node-sass

升级版本,同时满足了 webpack/vite 的打包要求

- "node-sass": "^4.9.2", + "node-sass": "^6.0.0", - "sass-loader": "^7.0.3", + "sass-loader": "^10.0.0" index.html 位于根目录,vite 默认的入口 加入 type="module" 的入口文件 script 元素 语法变为 基础配置

复用并完善了之前的打包和开发配置文件:

// build/config.js module.exports = { title: '报表', // 打包文件夹名称 base: 'my-report', // 调试配置 debug: { pubDir: 'dist', assetsDir: 'assets', host: 'localhost', port: 8080, navCss: '/src/assets/common2.0/scss/nav-common.css', navJs: '/src/assets/common2.0/js/nav-common.js', proxy: { target: 'http://api.foo.com' } }, // 生产配置 prod: { navJs: '/public/v3/js/nav-common.js', navCss: '/public/v3/css/nav-common.css', } }; vite.config.js 基本结构 import {createVuePlugin} from 'vite-plugin-vue2'; export default ({mode}) => { const isProduction = mode === 'production'; return defineConfig({ base: `/${config.base}/`, logLevel: 'info', // 插件,兼容 rollup plugins: [ // vue2 和 jsx createVuePlugin({ jsx: true, jsxOptions: { compositionAPI: true } }), // 打包统计 timeReporter() ], // devServer 设置 server: {}, // 依赖解析规则等 resolve: { alias: {} }, // 打包目录、素材目录、rollup原生选项等 build: {} }); }; resolve 的迁移

之前 webpack 中的配置:

resolve: { extensions: ['.ts', '.tsx', '.vue', '.js', '.jsx', '.json', '.css', '.scss'], alias: { '@': path.resolve(__dirname, '../src'), assets: path.resolve(__dirname, '../src/assets'), vue$: path.resolve(__dirname, '../node_modules', 'vue/dist/vue.esm.js') }, symlinks: false },

vite 中的写法:

resolve: { extensions: ['.ts', '.tsx', '.vue', '.js', '.jsx', '.json', '.css', '.scss'], alias: [ { find: '@', replacement: path.resolve(__dirname, 'src') }, { find: 'assets', replacement: path.resolve(__dirname, 'src', 'assets') }, { find: 'vue$', replacement: path.resolve(__dirname, 'node_modules', 'vue/dist/vue.esm.js') }, { find: '~@foo/src/styles/common/publicVar', replacement: 'node_modules/@foo/src/styles/common/_publicVar.scss' }, { find: '~@foo/src/styles/mixins/all', replacement: 'node_modules/@foo/src/styles/mixins/_all.scss' } ] },

以上最后两项配置属于之前引用的错误路径,vite 无法跳过,并将引起打包失败;需要修正引用或在此特殊处理

build 的迁移

之前 webpack 中的配置:

context: path.resolve(__dirname, '../'), mode: isProduction ? 'production' : 'development', entry: { index: './src/index.js' }, output: { path: path.resolve(__dirname, '../dist', config.base), publicPath, filename: isProduction ? 'assets/js/[name].[contenthash:8].js' : 'assets/js/[name].[hash:8].js', chunkFilename: isProduction ? 'assets/js/[name].[contenthash:8].chunk.js' : 'assets/js/[name].[hash:8].chunk.js' }, performance: { maxEntrypointSize: 2000000, maxAssetSize: 1000000 }

vite 中的写法:

build: { outDir: `${pubDir}/${config.base}`, assetsDir, rollupOptions: { }, chunkSizeWarningLimit: 1000000, cssCodeSplit: true } 直接拷贝的素材 业务中有一部分动态路径的素材图引用 ,path 可能为 assets/imgs/noData.png 这样的相对路径 webpack 中用 'copy-webpack-plugin' 插件拷贝图片到发布目录下,调试过程中是可以访问到的 vite 用拷贝插件 'rollup-plugin-copy' 同样可以拷贝成功,但调试进程中访问不了 dist 目录 import copy from 'rollup-plugin-copy'; ... // 打包时才拷贝 plugins: [ isProduction ? copy({ targets: [ { src: path.resolve(__dirname, 'src/assets/imgs'), dest: `${pubDir}/${config.base}/${assetsDir}` } ], hook: 'writeBundle' }) : void 0, ], // 调试过程中特殊转写 server: { proxy: { '/my-report/assets/imgs/': { target: `http://${host}:${port}/`, rewrite: path => path.replace('assets', 'src/assets') } }, } 特殊的外部引用 vite 需要用 'vite-plugin-html' 插件来达成和兼容与 'html-webpack-plugin' 一样的 html 注入效果 形如 '/public/v3/css/nav-common.css' 这样的特殊引用,不符合 vite 内部的保留策略,会被删除原 标签并转换成 js import,这将造成页面无法正常访问 结合自定义插件实现打包过程中的 hack 和打包结束后的恢复 import {createHtmlPlugin} from 'vite-plugin-html'; ... const indexReplaceHolder = '//fakePrefix'; ... plugins: [ createHtmlPlugin({ template: 'index.html', minify: true, inject: { data: { htmlWebpackPlugin: { options: { title: config.title, navCss: isProduction ? indexReplaceHolder + config.prod.navCss : config.debug.navCss, navJs: isProduction ? indexReplaceHolder + config.prod.navJs : config.debug.navJs } } } } }), (function() { let viteConfig; return { name: 'vite-plugin-fix-index', configResolved(resolvedConfig) { viteConfig = resolvedConfig; }, transformIndexHtml(code) { if (viteConfig.command === 'build' && isProduction) { const re = new RegExp(indexReplaceHolder, 'g'); code = code.replace(re, ''); } return code; } }; })(), ], 传统浏览器兼容 vite 用 @vitejs/plugin-legacy 插件为打包后的文件提供传统浏览器兼容性支持 legacy 对 build 速度影响较大,酌情采用 plugins: [ legacy({ targets: ['> 1%', 'last 2 versions', 'not ie


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3