React项目从Webpack 升级 Vite详细问题记录

4,321 阅读2分钟

背景

由于项目需求的不断迭代,对项目启动和打包速度的要求也有所提高。因此公司的几个项目,陆续都由webpack迁移到Vite了。

(对于商家端后台)可以看到Vite启动速度和打包速度相较于webpack是非常的快。

vitewebpack
启动时间1.5秒75秒
打包时间2分54秒5分-6分钟左右

vite 配置

  1. 在原有react项目中安装vite:yarn add vite
  2. 手动创建vite.config.js(根目录)
import { defineConfig } from 'vite';
import legacy from '@vitejs/plugin-legacy';
import reactRefresh from '@vitejs/plugin-react-refresh';
import path from 'path';
import fs from 'fs';
import lessToJs from 'less-vars-to-js';

// 重写 less 变量,定制样式
const theme = lessToJs(
  fs.readFileSync(path.resolve(__dirname, `./src/basic/themes/saas.less`), 'utf8'),
);

export default defineConfig(
 // 解析
 resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'), // @符指向src
      '@ant-design/icons/lib/dist': '@ant-design/icons/lib/index.es.js',
    },
  },
  define: {
    global: 'window',
    globalThis: 'window',
  },
  esbuild: {
    target: ['es2020'],
  },
  build: {
    outDir: 'build',
    assetsDir: 'static',
  },
  plugins: [
    reactRefresh(),
    legacy({
      targets: ['defaults', 'not IE 11'],
      // 无法成功编译的高级用法可以在这里引入
      modernPolyfills: ['es.object.from-entries', 'es.array.flat', 'es.global-this'],
    }),
  ],
  css: {
    modules: {
      localsConvention: 'camelCase',
    },
   
    postcss: {
      // eslint-disable-next-line global-require
      plugins: [require('autoprefixer')],  // 自动添加厂商前缀
    },
    preprocessorOptions: {
      less: {
        // antd的样式使用less
        javascriptEnabled: true,
        // 重写 less 变量,定制样式
        modifyVars: theme,
      },
      scss: {
        charset: false,
        additionalData: `$platform: saas;`,
      },
      // Vite 也同时提供了对 .scss, .sass, .less, .styl 和 .stylus 文件的内置支持
    },
  },
  // 服务器配置
  server: {
  // 代理配置:当访问路径以‘/’开头时,代理到target的地址 
    proxy:{
      '/': {
        target: 'http://10.189.73.43:8084',
        changeOrigin: true, // 改变来源
        headers: {
          Referer: 'http://fs.sftcwl.com/MMS',
          Cookie: '...'
        }
      }
    }
  }
);
  1. 在根目录创建index.html,把原来public/index.html粘过来,public文件夹就不需要了

  2. index.html中引用入口文件。index.js改为index.jsx

    image.png

  3. package.json配置vite启动命令。

    "scripts": {
       "dev": "vite --host",
        "build": "tsc && vite build",
        "serve": "vite preview --host",
        "build:publish": "vite build --base=/static/MMS/"
      },
    
  4. 删除webpack相关的配置文件,如config-overrides.jsnode_modules 文件夹,yarn.lockpackage-lock.json,并重新安装依赖文件。

    如果遇到以下报错信息,很有可能也是依赖问题。

    error: Unexpected "<"
    

image.png

  1. 修改环境变量

vite对环境变量的访问需要通过import.meta.环境变量名称来访问,将process.env替换为import.meta.env

环境变量是在.env文件里配置,注意,配置的变量必须要以VITE_开头,否则引用的时候找不到。

到这里,配置相关的准备工作已经完成,下面启动一下,解决出现的问题。

启动一下yarn dev,开始报错~

1. Cannot find module '@vitejs/plugin-react-refresh

解决:yarn add @vitejs/plugin-react-refresh 「缺啥安啥」

2. mock.proxy.js:1:0: error: Transforming const to the configured target environment ("es5") is not supported yet

image.png

解决:在文件tsconfig.json中,compilerOptions下的target:es5改为esnext,注意大小写,保持一致即可。

image.png

3. Error: Cannot find module '@vitejs/plugin-legacy'

解决:yarn add @vitejs/plugin-legacy (缺啥安装啥)

4. TypeError: (0 , import_proxy.default) is not a function

是因为 vite 不支持 require() 导入方式。

proxy.js文件中忘记将require替换为import引入方式,所以报了这个错误

5. js文件替换ts/jsx

6. [vite] Internal server error: Preprocessor dependency "sass" not found. Did you install it?

解决:安装sass系列,yarn add sass (node-sass,sass-loader)等

7. vite需要把样式文件后缀加上.module

如xxx.module.scss, 可以使用以下脚本替换.scss文件为.module.scss

在终端运行:node rename.js

   // rename.js文件
   const glob = require('glob');
   const fs = require('fs');

   const files = glob.sync('src/**/*.scss');

   files.forEach((oldPath) => {
     const newPath = oldPath.replace('.scss', '.module.scss');
     fs.rename(oldPath, newPath, (err) => {
       if (err) {
         console.log('err', oldPath);
       }
     });
   });

8. $platform 未定义

解决:原来是在webpack配置文件定义的,后来删除这个文件后报错,在vite.config.js 中配置即可

// 指定传递给 CSS 预处理器的选项
 scss: {
    charset: false,
    additionalData: `$platform: saas;`,
  },

9. 把路由里的load替换为lazy

const Home = load(() => import('@/pages/Home'));
替换为:
const Home = lazy(() => import('@/pages/Home'));

image.png

10. Suspense与lazy问题

image.png

解决:import React, { Suspense, lazy } from 'react';

原因是单独只写了lazy,就报了这个错,Suspense与lazy是成对出现的,加上就不报错了

image.png

11. Property 'env' does not exist on type 'ImportMeta'.

withstore.ts 中使用import.meta.env.DEV 会报错上述问题,jsx 文件不会报错,只有ts文件报错

解决:在tsconfig.jsoncompilerOptions下添加 "types": ["vite/client"] 配置(官网有解释)

image.png

12. 以下写法,scss会报写法错误(警告)

margin-left: -($gap / 2);
margin-right: -($gap / 2);

Using / for division outside of calc() is deprecated and will be removed in Dart Sass 2.0.0. Recommendation: math.div($gap, 2) or calc($gap / 2)

原因就是scss版本不支持上述写法了,解决方法就是按照提示修改语法即可

 margin-left: - calc($gap / 2);
 margin-right: - calc($gap / 2);

注意:如果是负数,‘-’ 号和calc之间要加空格

13. 全局样式消失

有些页面引用方式为

@value sideWidth, menuBg, menuItemActive from './Global.module.scss';

应该是vite不支持这种写法,替换为以下方式就ok了。

@import './Global.module.scss';

14. 打包时提示ts类型有问题,但检查了一圈并没有啥问题

// @ts-noignore

15. Failed to resolve entry for package "crypto". The package may have incorrect main/module/exports specified in its package.json: Failed to resolve entry for package "crypto". The package may have incorrect main/module/exports specified in its package.json.

crypto 依赖包除了package.json 和 readme 没有其他文件,应该是不再维护使用了

  • 但ts文件可用,jsx文件中引入都会报上述错误

改用js-md5插件后问题解决。

16. Uncaught TypeError: Class extends value #<Object> is not a constructor or null

问题出现在封装的级联选择器组件 Cascader,使用了类组件的方式,后来一层层进入Cascader组件内部发现返回值变成了一个对象,它不是一个函数了,因此不能继承就会报错。

  • antd在4.8.2 版本中返回值类型为function

image.png

  • 在4.17.3中返回类型为Object

image.png

解决方法就是版本降级,或者改写为函数组件即可。

17. 菜单栏字体图标不显示

多个 @font-face 的font-family同名时,会覆盖,解决办法不要重名就行。

image.png

疑问:为啥控制台每次要打印global.module.scss文件?