React接入vite作为开发环境实施方案,接入架手架介绍
如果你的React项目现在已经比较庞大,每次启动的时候都需要几十秒,热更新也是非常的慢,你想要缓解这种现象,提高开发效率的时候,可能首先就会想到vite,今天聊一聊React接入vite的方案。
一、方案介绍
新项目如何使用vite?
1.最直接也是简单的,使用vite中文官网提供的脚手架,初始化项目,不过,官网的脚手架,只提供了最基本的react项目搭建,关于路由、api、mock、redux等常用功能,需要自己封装。
2.使用阿里旗下的飞冰框架,最新版本支持一键配置vite,这个封装的功能更多一点,但是封装多意味着我们被框架限制也更多,很多东西需要按照框架要求,不过也能满足我们日常大部分开发需求。
3.umijs想想大部分人也很熟悉,umi4beta版本,也将要支持vite开发环境+webpack生产环境,但是这个版本什么时候会正式上线,目前未知。
老项目如何接入vite?
其实我们更多的时候是想要缓解,已有的项目,启动慢,热更新缓慢的问题,我们进入正题。
如题,本次文章的主题react接入vite的开发环境,之所以只接入开发环境,是因为一下几个方面的考量。
目前vite的生态相比webpack还不是那么完善,有一定风险
vite兼容性较差
如果完全把项目的webpack替换成vite,可能需要做全量的测试,有出线上问题的风险
开发环境接入方案介绍
我们可以先看一下,使用vite官网脚手架搭建的项目目录
目录结构也很简单,其中红圈的三个文件是构成一个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修改源码部分如下:
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.
这个问题是antd3.x导致的问题,4.x版本没有这个问题,一直没有找到好的解决办法,只能去antd的源码里加一个安全判断。如果大家有什么更好的解决办法,可以评论区告诉我。
3. 这个问题可能是由于require插件导致的,这个插件目前可能不太完善,有的项目报错,有的不报错。把require插件注释掉。 但是。。。如果不使用这个插件,就得手动把项目里的require都替换成import。
大致的问题就这些,如果在接入过程中,遇到其他问题,可以仿照自定义插件,对项目做一些修改,这里只会修改vite生成的文件,对原有项目没有任何影响。
二、接入脚手架介绍
这里开发了一个脚手架方便大家使用,
全局安装脚手架:npm i add-vite-cli -g 或者 yarn安装。需要内网环境,git目前是在内网(文末有git链接)。
脚手架目前只支持react接入,后续考虑加入Vue,其实Vue和react接入的的思路都是一样的,后面会讲到。
进入项目根目录 addVite
cd ./vite-project
现在做一些最基本的修改
mock数据的修改,这里需要引入之前项目的mock配置文件,下面注释内容是vite需要的mock格式,可能需要一个转化的过程,我下面自带了umi mock数据转化的方法。
路由文件配置,目前配置文件也只支持路由的配置。routers写在哪个对象都行,因为我们的vite环境是开放的,很多东西都可以直接在vite目录修改,这里目前没有多做配置,之所以把路由提出来,也是为了和旧项目的兼容,防止出现两套路由配置的情况。
因为毕竟项目会有各种各样的架构,架手架也没办法考虑到所有的情况,甚至这里也只是考虑到一小部分,需要根据不同的项目,做出不同的配置和修改
作者:小伙子992
链接:https://juejin.cn/post/7045152353292386334
玩站网免费分享SEO网站优化 技术及文章 伪原创工具 https://www.237it.com/