阅读 199

React接入vite作为开发环境实施方案,接入架手架介绍

如果你的React项目现在已经比较庞大,每次启动的时候都需要几十秒,热更新也是非常的慢,你想要缓解这种现象,提高开发效率的时候,可能首先就会想到vite,今天聊一聊React接入vite的方案。

一、方案介绍

新项目如何使用vite?

1.最直接也是简单的,使用vite中文官网提供的脚手架,初始化项目,不过,官网的脚手架,只提供了最基本的react项目搭建,关于路由、api、mock、redux等常用功能,需要自己封装。

2.使用阿里旗下的飞冰框架,最新版本支持一键配置vite,这个封装的功能更多一点,但是封装多意味着我们被框架限制也更多,很多东西需要按照框架要求,不过也能满足我们日常大部分开发需求。

3.umijs想想大部分人也很熟悉,umi4beta版本,也将要支持vite开发环境+webpack生产环境,但是这个版本什么时候会正式上线,目前未知。

老项目如何接入vite?

其实我们更多的时候是想要缓解,已有的项目,启动慢,热更新缓慢的问题,我们进入正题。

如题,本次文章的主题react接入vite的开发环境,之所以只接入开发环境,是因为一下几个方面的考量。

  1. 目前vite的生态相比webpack还不是那么完善,有一定风险

  2. vite兼容性较差

  3. 如果完全把项目的webpack替换成vite,可能需要做全量的测试,有出线上问题的风险

开发环境接入方案介绍

我们可以先看一下,使用vite官网脚手架搭建的项目目录

image.png

目录结构也很简单,其中红圈的三个文件是构成一个vite项目的核心文件。

index.html: vite服务的入口文件,用script标签引入了main.tsx

main.tsx: react的入口文件

vite.config.ts: vite配置文件。

我们在在老项目中使用vite作为开发环境的思路,就是在在项目中,生成一个这样的vite环境,然后在main中,我们把原有项目中的页面、路由配置、redux配置、mock配置等引入进来,这样就算完成了,听起来好像也不是很复杂。

我们现在开始试验一下

找一个项目在原有项目,可以直接在原有项目用官网的脚手架生成一个vite环境的文件夹

1.首先我们开始改造main.tsx 代码如下,我们在main当中增加动态懒加载路由部分,路由部分大家可以根据原有项目情况,使用v5或者v6版本,v5到v6版本变化还有有点大的。 由于我们原有项目使用umi框架,开启了dva配置,我们也需要加入dva的部分

import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import 'antd/dist/antd.css'; import { umiConfig, viteConfig } from '../viteConfig'; import dva from 'dva'; import { createBrowserHistory } from 'history'; import { HashRouter, Route, BrowserRouter, Switch } from 'react-router-dom'; import loadable from '@loadable/component' // 引入所有model   const allModel = import.meta.globEager('../src/models/*.ts'); // 不使用不会引入,导致动态导入失败 const loada = loadable(() => { }) let routerConfig = []; // 渲染路由方法,递归渲染子路由 const renderRoute = (router: any) => {   if (!router) return   return router.map((item: any) => {     const child = renderRoute(item?.routes)     let Element = item.component     return <Route       key={item.path}       path={`${item.path.replace('./', '/*').replace('/', '/*')}`}       component={() => (         <Element>{child}</Element>       )}     ></Route>   }); }; // 默认使用dva,可以根据自己项目需求自行改造 // 创建 dva 实例 const app = dva({   history: createBrowserHistory(), }); // 注册 model Object.keys(allModel).forEach((key) => {   allModel[key]?.default && app.model(allModel[key].default); }); const router = (   <BrowserRouter>     <Switch>       {renderRoute(routerConfig)}     </Switch>   </BrowserRouter> ); app.router(() => router); // 渲染 ReactDOM.render(   <div>     {       <Provider store={app.start()().props.store}>         {/* {<Main></Main>} */}         {router}       </Provider>     }   </div>,   document.getElementById('root'), ); 复制代码

2.第二步,开始配置vite配置文件

代码如下,我们在配置中,尤其是插件中,做了很多的东西。

  • 首先利用插件,配置了动态路由,因为直接在main里面写,vite不支持动态路由里面写动态字符串。

  • 因为我们的vite项目中直接引用的redux上下文,和原有项目引用的umi的redux上下文,不是一个环境,需要利用插件更换为原生的引用。

  • 原生Vite 构建模式在开发阶段基于浏览器加载 ESM 模块,不支持 require 语法的导入,需要引入一个vite-plugin-require的插件。

  • 然后还利用vite-plugin-mock 插件实现了mock数据的处理

  • 因为原有umi项目中有一些全局变量,我们在vite中define.process 增加了一些全局变量,解决报错

  • 利用css.preprocessorOptions配置全局less变量

  • 利用resolve.alias配置实现全局引用路径配置,比如【@】这种

  • 还需要注意的一个是,原生vite的cssmodule只支持.module.less文件,我们又不能去修改原有项目的所有less文件,这里配合修改vite源码中compileCSS方法,识别a.less?module=true,这种结尾的less文件,然后再利用插件,修改所有文件中/import [a-z]+ from ".+.less"  引入的less文件,使得支持cssmodule。

  • 更多配置,大家可以参考官方文档。

  • vite修改源码部分如下:

image.png

import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path'; import { viteMockServe } from 'vite-plugin-mock'; import vitePluginRequire from 'vite-plugin-require'; import { umiConfig, viteConfig } from '../viteConfig'; let config = { ...umiConfig, ...viteConfig }; // 生成router 字符串 因为动态路由vite直接写会报错!! let routerConfig = config.routes; const initRoute = (routers) => {   routers.forEach(item => {     if (item.component) {       let atr = item.component.replace('@', '../src')       item.component = `loadable(() => import('${atr}'))`     }     item?.routes?.length && initRoute(item?.routes)   }); } initRoute(routerConfig) let routerStr = JSON.stringify(routerConfig) routerStr = routerStr.replace(/"loadable/g, "loadable") routerStr = routerStr.replace(/\)\)"/g, "))") routerStr = ` let routerConfig =  ${routerStr} ` // 生成router 字符串结束 const localEnabled = config.hasOwnProperty('mock') ? false : true; const prodEnabled = !!process.env.USE_CHUNK_MOCK || false; export default defineConfig({   plugins: [     {       name: 'originjs:commonjs',       apply: 'serve',       transform(code, id) {         let result = code;         // 增加cssmodule           if (code.match(/import [a-z]+ from ".+\.less"/)) {           result = code.replace(/(import [a-z]+ from ".+\.less)(")/, ($1, $2, $3) => {             return `${$2}?module=true${$3}`;           });         }         // 修改umi的useDispatch 没有上下文环境,这段代码可以根据自己项目情况,判断是否需要         if (code.indexOf('useDispatch()') > -1) {           result = `import { useDispatch as viteUseDispatch, useSelector as viteUseSelector } from 'react-redux' \n` + result;           // 全局替换           result = result.replace(/useSelector\(/g, "viteUseSelector(")           result = result.replace(/useDispatch\(\)/g, "viteUseDispatch()")         }         // 修改umi的useDispatch 没有上下文环境,这段代码可以根据自己项目情况,判断是否需要         // if (code.indexOf(`connect(`) > -1 && id.indexOf('.tsx') > -1) {         //   result = `import { connect as viteconnect } from 'react-redux' \n` + result;         //   // 全局替换         //   result = result.replace(/connect\(/g, "viteconnect(")         // }         // 在main.tsx 中增加路由相关         if (id.indexOf(`/vite-project/main.tsx`) > -1) {           result = result.replace('let routerConfig = [];', routerStr);         }         return {           code: result,           map: null,           warnings: null,         };       },     },     react(),     // require插件     vitePluginRequire({       // @fileRegex RegExp       // optional:default file processing rules are as follows       // fileRegex:/(.jsx?|.tsx?|.vue)$/     }),     // mock数据插件     viteMockServe({       mockPath: './mock',       localEnabled: localEnabled, // 开发打包开关       prodEnabled: prodEnabled, // 生产打包开关       // 这样可以控制关闭mock的时候不让mock打包到最终代码内       injectCode: `         import { setupProdMockServer } from './mockProdServer';         setupProdMockServer();       `,       logger: false, //是否在控制台显示请求日志       supportTs: true, //打开后,可以读取 ts 文件模块。 请注意,打开后将无法监视.js 文件     }),   ],   define: {     process: {       env: {         __IS_SERVER: null,         isVite: true,       },     },   },   css: {     // 解决cssmodule     // @ts-ignore     isModule(id) {       if (id.indexOf('module=true') !== -1) {         return true;       }     },     preprocessorOptions: {       less: {         modifyVars: {           'primary-color': '#ff8420',           'link-color': '#2DB7F5',           'border-radius-base': '4px',           'text-color': '#666',           'font-size-base': '14px',         },         javascriptEnabled: true,       },     },   },   resolve: {     preserveSymlinks: true,     alias: {       '@': path.resolve(__dirname, '../src'),       '@@': path.resolve(__dirname, '../src/.umi'),     },   },   // server:{   //   hmr:{   //     overlay: false   //   }   // } }); 复制代码

进行到这里,对整个项目的改造大部分已经完成了,剩下就是处理一些报错,因为毕竟原来的项目有各种各样的逻辑,和配置,下面我罗列了我遇到的一些错误和解决办法,大家可以参考。

问题列表

1.首先需要在主项目安装less、和react-router,因为大家的项目可能有一些已经安装过了,就没在脚手架自动安装,react-router可以自行选择是v5版本还是v6版本

2.icon.png

这个问题是antd3.x导致的问题,4.x版本没有这个问题,一直没有找到好的解决办法,只能去antd的源码里加一个安全判断。如果大家有什么更好的解决办法,可以评论区告诉我。 image.png

3.require.png 这个问题可能是由于require插件导致的,这个插件目前可能不太完善,有的项目报错,有的不报错。把require插件注释掉。 但是。。。如果不使用这个插件,就得手动把项目里的require都替换成import。 image.png

大致的问题就这些,如果在接入过程中,遇到其他问题,可以仿照自定义插件,对项目做一些修改,这里只会修改vite生成的文件,对原有项目没有任何影响。

二、接入脚手架介绍

这里开发了一个脚手架方便大家使用,

  1. 全局安装脚手架:npm i add-vite-cli -g  或者 yarn安装。需要内网环境,git目前是在内网(文末有git链接)。

  2. 脚手架目前只支持react接入,后续考虑加入Vue,其实Vue和react接入的的思路都是一样的,后面会讲到。

  3. 进入项目根目录  addVite

  4. cd ./vite-project

现在做一些最基本的修改

  • mock数据的修改,这里需要引入之前项目的mock配置文件,下面注释内容是vite需要的mock格式,可能需要一个转化的过程,我下面自带了umi  mock数据转化的方法。

image.png

  • 路由文件配置,目前配置文件也只支持路由的配置。routers写在哪个对象都行,因为我们的vite环境是开放的,很多东西都可以直接在vite目录修改,这里目前没有多做配置,之所以把路由提出来,也是为了和旧项目的兼容,防止出现两套路由配置的情况。

image.png

因为毕竟项目会有各种各样的架构,架手架也没办法考虑到所有的情况,甚至这里也只是考虑到一小部分,需要根据不同的项目,做出不同的配置和修改


作者:小伙子992
链接:https://juejin.cn/post/7045152353292386334

玩站网免费分享SEO网站优化 技术及文章 伪原创工具 https://www.237it.com/ 


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐