注册

6年的老项目迁移vite2,提速几十倍,真香

vite-dev.png


背景



gou系统又老又大又乱,每一次的需求开发都极其难受,启动30|40几秒勉强能接受吧,毕竟一天也就这么一回,但是HMR更新也要好几秒实在是忍不了,看到了vite2就看到了曙光!盘它



先看看vue-cli3的启动编译吧...


编译-new-48803ms.png



  • 该项目为内部运营管理系统,年龄6岁+
  • 基于vue2+elementui,2年入职时将vue-cli2升级到了vue-cli3,2年后的今天迫不及待的的奔向vite2
  • 仅迁移开发环境(我的痛点只是开发环境,对于生产环境各位自行考虑)

痛点分析


实质上是对webpack工作原理的分析,webpack在开发环境的工作流大致如下(个人见解不喜勿喷):



查找入口文件 => 分析依赖关系 => 转化模块函数 => 打包生成bundle => node服务启动



所以随着项目越来越大,速度也就越来越慢...


至于HMR也是同理,只不过HMR是将当前文件作为入口,进行rebuild,涉及的相关依赖都需要重载


为什么是Vite



  • vite是基于esm实现的,主流浏览器已支持,所以不需要对文件进行打包编译
  • 项目启动超快(迁移后简单的概算数据是从30s 提升到 1s。30倍?3000%?一点都不夸张...)
  • 还是基于esmHMR很快,不需要编译重载,速度可以用一闪而过来形容...

vite大致工作流:



启动服务 => 查找入口文件(module script) => 浏览器发送请求 => vite劫持请求处理返回文件到浏览器



开盘,踏上迁移之路




  1. 安装相关npm包


    npm i vite vite-plugin-vue vite-plugin-html -D


    • vite-plugin-vue,用于构建vue,加载jsx
    • vite-plugin-html,用于入口文件模板注入



  2. package.json文件中,新增一个vite启动命令:


    "vite": "cross-env VITE_NODE_ENV=dev vite"



  3. 根目录新建vite.config.js文件




  4. public下的index.html复制一份到根目录



    仅迁移开发环境,public下仍然需要index.html,支持开发环境下vite和webpack两种模式





  5. 修改根目录下index.html(vite启动的入口文件,必须是根目录)


    <% if (htmlWebpackPlugin.options.isVite) { %>
    <script type="module" src="/src/main.js"></script>
    <%}%>


    htmlWebpackPlugin在vite.config.js注入,isVite用于标识是否是vite启动



    import { injectHtml } from 'vite-plugin-html';
    export default defineConfig({
     plugins:[
       injectHtml({
         injectData: {
           htmlWebpackPlugin: {
             options: {
               isVite: true
            }
          },
           title: '运营管理平台'
        }
      })
    ]
    })



  6. 完整vite.config.js 配置


    import { defineConfig } from 'vite'
    import path from 'path'
    import fs from 'fs'
    import { createVuePlugin } from 'vite-plugin-vue2'
    import { injectHtml, minifyHtml } from 'vite-plugin-html'
    import dotenv from 'dotenv'

    try {
       // 根据环境变量加载环境变量文件
       const VITE_NODE_ENV = process.env.VITE_NODE_ENV
       const envLocalSuffix = VITE_NODE_ENV === 'dev' ? '.local' : ''
       const file = dotenv.parse(fs.readFileSync(`./.env.${VITE_NODE_ENV}${envLocalSuffix}`), {
           debug: true
      })
       for (const key in file) {
           process.env[key] = file[key]
      }
    } catch (e) {
       console.error(e)
    }

    const resolve = (dir) => {
       return path.join(__dirname, './', dir)
    }
    export default defineConfig({
       root: './',
       publicDir: 'public',
       base: './',
       mode: 'development',
       optimizeDeps: {
           include: []
      },
       resolve: {
           alias: {
               'vendor': resolve('src/vendor'),
               '@': resolve('src'),
               '~component': resolve('src/components')
          },
           extensions: [
               '.mjs',
               '.js',
               '.ts',
               '.jsx',
               '.tsx',
               '.json',
               '.vue'
          ]
      },
       plugins: [
           createVuePlugin({
               jsx: true,
               jsxOptions: {
                   injectH: false
              }
          }),
           minifyHtml(),
           injectHtml({
               injectData: {
                   htmlWebpackPlugin: {
                       options: {
                           isVite: true
                      }
                  },
                   title: '运营管理平台'
              }
          })
      ],
       define: {
           'process.env': process.env
      },
       server: {
           host: '0.0.0.0',
           open: true,
           port: 3100,
           proxy: {}
      }
    })



    相关配置会在下文遇到的问题中做具体描述





迁移过程中遇到的问题




  1. Uncaught SyntaxError: The requested module 'xx.js' does not provide an export named 'xx'


    本人遇到的分以下两类情况:


    a. 一个模块只能有一个默认输出,导入默认输出时,import命令后不需要加大括号,否则会报错


    处理方式:将原先{}导入的keys,改成导入默认keyes6解构赋值


    -import { postRedeemDistUserUpdate } from '@/http-handle/api_types'

    +import api_types from '@/http-handle/api_types'
    +const { postRedeemDistUserUpdate } = api_types

    b. 浏览器仅支持 esm,不支持 cjs,需要将cjs改为esm (看了网文有通过cjs2esmodule处理的,但是本人应用有些场景是报错的,最后就去掉了)


    处理方式:不推荐使用cjs2esmodule,手动将module.exports更改为export


    -module.exports = {

    +export default {



  2. .vue文件扩展,最新版本的vite貌似已支持extensions添加.vue,不过还是推荐手动添加下后缀。(骚操作:正则匹配批量添加)




  3. Uncaught ReferenceError: require is not defined


    浏览器不支持cjs


    处理方式:require引用的文件都需要修改为import引用




  4. vite启动,页面空白


    处理方式:注意入口文件index.html,需要放置项目根目录




  5. vite环境下默认没有process.env,可通过define定义全局变量


    vue-cli模式下,环境变量都是读取根目录.env文件中的变量,那么vite模式下是否也可以读取.env文件中的变量最终注入到process.env中呢?


    这样不就可以两种模式共存了么?成本变小了么?


    处理方式:



    1. 安装环境变量加载工具:dotenv

    npm i dotenv -D




    1. 自定义全局变量process.env


      vite.config.js中配置




    define: {
    'process.env': {}
    }



    1. 加载环境变量,并添加到process.env


      vite.config.js中配置



      因为仅迁移开发环境,所以我这里默认是读取.local文件。


      VITE_NODE_ENV是在启动时通过cross-env注入的







import dotenv from 'dotenv'
try {
const VITE_NODE_ENV = process.env.VITE_NODE_ENV
const envLocalSuffix = VITE_NODE_ENV === 'dev' ? '.local' : ''
const file = dotenv.parse(fs.readFileSync(`./.env.${VITE_NODE_ENV}${envLocalSuffix}`), {
debug: true
})
console.log(file)
for (const key in file) {
process.env[key] = file[key]
}
} catch (e) {
console.error(e)
}




  1. jsx支持


    vite.config.js中配置


    plugins: [
    createVuePlugin({
      jsx: true,
      jsxOptions: {
        injectH: false
      }
    })



  2. webpack中require.context方法,在vite中使用import.meta.glob替换




现存问题


项目中导入/导出的功能,是纯前端实现的


require('script-loader!file-saver')
require('script-loader!@/vendor/Blob')

由于以上文件目前不支持import引入,webpack下是通过script-loader加载挂载到全局的,vite环境下未能解决。需要导入导出功能时只能切换到vue-cli模式启动服务...


如果各位大大有方案,麻烦指导指导~,实在是不想回到webpack开发了...


最后


总体迁移上并没有遇到什么疑难杂症,迁移成本还是不大的,实操1-2天,性价比很高哦,我这个项目按数据看就是几十倍的启动提效,几倍的HMR提效...各位可以在内部系统上做下尝试。



链接:https://juejin.cn/post/7005479358085201957

0 个评论

要回复文章请先登录注册