1.前言
Monorepo 最早的出处是软件开发策略的一个分支,”mono” 表示单一 “repo” 是”repository”的缩写,是指将多个项目的代码存储在同一个版本库中的软件开发方法。多个项目共用一个代码库来管理依赖关系,同一套配置文件,统一构建部署流程等等。这种方法可以提高代码共享和重用的效率,同时也可以简化代码管理和部署过程。平时常见的 Reactjs、Vuejs、Babel 也是 monorepo 管理架构。
目前国内Monorepo 架构常见的管理方法有:
- npm
- yarn
- lerna —–例如:Antd、ahooks
- pnpm
不管使用什么方式进行管理,在确定将项目设计成 Monorepo 架构前,需要考虑以下几点。
- 项目规模较大,需要多个子项目协同开发和维护。
- 子项目之间存在明确的依赖关系,需要共享代码和资源。
- 需要统一管理和控制版本,方便协作和发布。
- 需要提高开发效率和代码复用性。
- 需要统一的构建、测试和部署流程。
如果满足以上几点的需求条件,可以设计成monorepo的架构形式,但是也需要良好的组织方式。
下面让我们一起来看看构建一个Monorepo 架构需要完成那些步骤吧!
2.环境&技术栈
- Node 18.x
- WebPack 5
- React
- TypeScript
- Tailwindcss
- Bable
- postCsss
- Pnpm
3.具体实现步骤
3.1.初始化项目
? 使用cmd终端或者Vsc编辑器终端初始化项目文件
mkdir monorepo-react
cd monorepo-react
pnpm init
? 最终效果图
3.2.目录结构设计
项目目录结构的布局设计,可能会直接影响到你项目后续的开发和维护。一个好的组织架构关系能让后续的开发和维护更爽快。一般情况下Monorepo架构的文件布局,主要以packages文件为主包。然后通过结合各类工具包和配置文件组合成项目的整体架构。拿ReactJs 和 VueJs 为例:
可以发现不管是ReactJs 还是 VueJs ,我们平常使用的一些核心方法都是封装成各个单独的包,放在packages文件下配合各类工具和插件进行管理的。
? 当然也还有另一种目录结构是这样的:
它也是以packages文件为根目录管理,但不同的是这个结构的各类包都是在apps文件下的;公用组件放在 components 文件;公用工具放在 libs 文件
|-packages
|- apps: web 项目
|- components: 公用组件库
|- libs: 公用工具
不管是使用那种结构,只要有利于项目组织各个包之间的关联关系,都是好的设计。
在这里我们就参考一下他们目录结构的设计方式去实现自己 Monorepo 管理架构目录。
? 最终的目录结构如下:
├── husky // git提交拦截
├── config // 配置相关
├── packages // 主包
│ ├── react-temp // React 子包
│ │ ├── scripts // Webpack相关配置
│ │ │ ├── webpack.base.js // Webpack基础配置
│ │ │ ├── webpack.dev.js // Webpack开发环境配置
│ │ │ ├── webpack.prod.js // Webpack生产环境配置
│ │ ├── src // 子包源码
│ │ ├── .babelrc // 子包babel解析配置
│ │ ├── .browserslistrc // 浏览器兼容配置
│ │ ├── .postcssrc.json // CSS解析配置
│ │ ├── package.json // 子包package.json
│ │ ├── tailwind.config.js // 子包原子化CSS配置
│ │ └── tsconfig.json // 子包TSC
│ │
│ ├── shared // 全局共享子包
│ │ └── // 按需配置
│ │
├── static // 主包三方不打包资源
│ └──
│ │
├── .eslintrc.json // eslint配置
├── .gitignore // git 忽略项
├── .prettierrc.json // 代码美化配置
├── pnpm-workspace.yaml // pnpm工作空间配置
└── package.json // package.json
3.3.配置pnpm工作区间 workspace
? 在根目录下新建pnpm-workspace.yaml
packages:
- 'packages/*'
配置pnpm 工作区间的默认管理根路径,方便pnpm 对文件的分析管理 。
3.4.初始化子项目
在这里我们主要新建两个子项目,react-temp 、shared 用于案例分析和实现。
? 在packages文件下新建,并初始化子项目
# 新建react包
mkdir react.temp
cd react.temp
pnpm init
# 新建shared包
mkdir shared
cd shared
pnpm init
? 初始化好子项目后,分别在 package.json 中进行配置:
- 给包名添加自己的包管理前缀 @norush
- 以及添加node启动模式 “type“: “module”
- 修包改根路径的指向 “main“: “src/index.js”
{
"name": "@norush/react.temp",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"type": "module",
"keywords": [],
"author": "",
"license": "ISC"
}
? 当前目录结构如下
到这里我们以基本完成俩个子包的初始化,那么怎么让他们建立联系呢?
说白了就是,如何在一个包(react-temp)中,引入另一个包(shared)并使用。
3.5.建立子包之间的关联
要解决在一个包中,引入并使用另一个的问题,其实就是需要建立起包与包之间的联系。
通常情况下在某个项目中需要使用到另一个包时都是通过,npm install 安装后才能使用的,那么在monorepo模式中,项目怎么使用别的包呢?
在这里我们通过 pnpm 的 –filter方法将本项目中的一个包,导入另一个包
pnpm add @norush/shared --filter @norush/react.temp
# or
@norush/shared: `pnpm link`
@norush/react.temp: `pnpm link @proj/react-components`
将@norush/shared 包添加到需要使用的包中
? 在monorepoReact项目根目录下执行以上命令后,你将在packages/react-temp/package.json 包中发现多了一个workspace:^ 依赖。它让 react-temp 包与 shared 链接起来。这种链接方式通常又称之为软链接。
"dependencies": {
"@norush/shared": "workspace:^"
}
尽然链接已经构建,那么让我们来测试是否真的连接起来了
? 先在shared/src/index.js 中导出一些方法
export const getID = () => `随机数:${Math.random()}`;
export default function () {
console.log("hello world!");
}
? 在react-temp 包中使用,另一个包导出的方法
import { getID } from "@norush/shared";
import hello from "@norush/shared";
console.log(getID());
console.log(hello());
? 最终调试结果如下通过调式发现可以在react-temp 包中正常使用,shared 包中的方法。
3.6.安装配置Eslint & Prettier
eslint : 一般多用来做代码的检测(逻辑、功能)
prettier :一般用来做代码格式化,美化代码提高开发效率
? 在根目录下安装eslint
pnpm add eslint -D -w
npx eslint --init
初始化话 init后会有如下选择
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · react
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser, node
√ What format do you want your config file to be in? · JSON
The config that you've selected requires the following dependencies:
@typescript-eslint/eslint-plugin@latest eslint-plugin-react@latest @typescript-eslint/parser@latest
√ Would you like to install them now? · No / Yes
√ Which package manager do you want to use? · pnpm
选择安装 TyprScript 时,由于命令行自动化,不给我们加 w,所以会出现错误无法自动安装的情况。
如果需要 TyprScript 需要手动安装
? 手动安装 TyprScript
pnpm add @typescript-eslint/eslint-plugin @typescript-eslint/parser -D -w
pnpm add eslint-plugin-react -D -w
? 执行完以上步骤后会得到 .eslintrc.json 的初始化。效果如下
到这里 Eslint 的安装以基本完成了,接下来就是安装prettier.
? 在根目录下继续安装prettier
pnpm add prettier -D -w
? 创建空文件 .prettierrc.json
echo {}> .prettierrc.json
? 配置自己 .prettierrc.json
{
"printWidth": 80,
"tabWidth": 4,
"useTabs": true,
"semi": false,
"singleQuote": true,
"trailingComma": "none"
}
? 将prettier配置到 .eslintrc.json
pnpm add eslint-plugin-prettier -D -w
pnpm add eslint-config-prettier -D -w //为了防止 Prettier 与 ESLint 的配置冲突
? 在 .eslintrc.json 中手动配置 prettier
"plugins": [
"@typescript-eslint",
"react",
"prettier"
],
到这里最基本的 Eslint & Prettier 安装和配置已经完成了。
3.7.安装配置Husky
? 谐音“哈士奇”,看门的修狗。
如果使用git来管理仓库,你可以用它来检查提交消息、运行测试、检查代码等等。
? 初始化Git
git init
? 配置gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.local
*.iml
? 初始化并安装husky
npx husky-init
npx husky add .husky/pre-commit 'npm run test' // 添加提交时执行的命令
npx husky add .husky/post-commit 'npm run test'
这样就可以在提交代码时自动进行测试了,但前提是需要配置有测试test。了解更多husky
3.8.安装配置TypeScript
使用到 TypeScript需要有类型检测,一下情况下分两种情况:
- 项目中有使用 **Babel **的直接使用 : @babel/preset-typescript + tsc 做类型检查
- 项目中没有有使用 **Babel 时使用 : tsc + ts-loader **做类型检查
它们之间的区别在于:
ts-loader : 是在内部调用了 typescript 的官方编译器 tsc ,所以 ts-loader 和 tsc 可以共用 tsconfig.json
@babel/preset-typescript : 它只会做编译,不会进行类型检查,所以我们需要用到 tsc 的配置做类型检查
? 在子包(react-temp)中初始化 tsconfig.json
tsc --init
? 在 **tsconfig.json **中添加或打开相关配置
{
"compilerOptions": {
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"lib": ["DOM", "DOM.Iterable", "ESNext"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"jsx": "react", /* Specify what JSX code is generated. */
"experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
/* Modules */
"module": "ESNext", /* Specify what module code is generated. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"resolveJsonModule": true, /* Enable importing .json files */
/* JavaScript Support */
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
/* Emit */
"noEmit": true, // 不输出文件,只做类型检查 /* Disable emitting files from a compilation. */
/* Interop Constraints */
"isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
/* Completeness */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": [
"./src/**/*"
],
"exclude": [
"node_modules"
]
}
由于后续需要使用 webpack 打包和babel 编译输出,所以需要配置”noEmit”: true, 不输出文件。
为了能在开发的包环境中使用到刚刚配置的 Ts 校验 ,我们还需要将原来的 react-temp 包配置成可用react开发环境。具体配置如下:
? 返回根目录monorepoReact 安装依赖到 react-temp
pnpm add react @types/react react-dom @types/react-dom --filter @norush/react.temp
? 将react-temp/src/index.js 的index.js 修改成index.tsx,并进行相关配置
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
ReactDOM.createRoot(document.getElementById('app' ) as Element).render(<App />)
到这里子包react-temp 的开发tsc环境以及配置好了。当前的配置结果如下
到这里子包react-temp 的 Ts 配置算是已经完成了,但为了正常开发我们还需要配置 Webpack 和 babel 。
那接下来看看webpack的基本配置具体是怎么配的。
3.9.安装配置Webpack
由于需要使用到babel 进开发文件的解析输出,我们需要结合webpack进行配置。通过webpack将开发过程中需要使用到的各插件关联在一起,这使得项目整体的开发交互逐渐智能化。那么下面来看看Webpack的一些基本功能是如何配置的。
? 返回根目录将依赖安装到子包react-temp中
# webpack
pnpm add webpack webpack-cli -D --filter @norush/react.temp
pnpm add webpack-merge -D --filter @norush/react.temp
pnpm add webpack-dev-server -D --filter @norush/react.temp
pnpm add html-webpack-plugin -D --filter @norush/react.temp
# babel
pnpm add @babel/core -D --filter @norush/react.temp
pnpm add @babel/preset-react -D --filter @norush/react.temp
pnpm add @babel/preset-typescript -D --filter @norush/react.temp
pnpm add babel-loader -D --filter @norush/react.temp
? 在子包react-temp根目录下新建,public/index.html 渲染到浏览器的最终HTML文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
webpack集成的所以内容最终都将打包到这个模板,然后最终输出到浏览器上渲染。
? 在子包react-temp根目录下新建,Babel 的配置文件 .babelrc 并引入安装的预设插件
{
"presets": [
"@babel/preset-react",
"@babel/preset-typescript"
]
}
要使用到Babel的配置,我们还需要通过webpack配置将他们链接起来。由于开发环境和生产环境的需求不同,为了方便代码的维护和管理,所以需要分别配置不同的文件。具体实现步骤如下
先在子包react-temp根目录下新建一个脚本配置文件夹scripts,用于管理不同的配置文件。
? 基础配置 webpack.base.js
它含了 webpack 的基础配置,如入口、输出、模块解析等。这个配置文件是开发环境和生产环境都需要的。
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = (isDev) => ({
entry: path.join(__dirname, '../src/index.tsx'),
mode: isDev ? "development" : "production",
output: {
filename: 'static/js/[name].[chunkhash:8].js',
path: path.join(__dirname, "../dist"),
clean: true, //w4 - clean-webpack-plugin
publicPath: '/'
},
module:{
rules:[
/**
* @description ts解析
* @method loader
*/
{
test: /.(ts|tsx)$/,
use:{
loader: "babel-loader",
}
},
]
},
/**
* @description resolve|解析配置
*/
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js'],
alias: {
"@": path.join(__dirname, '../src')
},
},
/**
* @description plugins|插件配置
* @param HTMLWebpackPlugin |根据指定的模板生成HTML文件(含打包后注入的JS)
*/
plugins: [
new HTMLWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
inject: true,
})
]
})
? 配置 webpack.dev.js
它含了开发环境的配置,如开发服务器、热更新等。这个配置文件主要用于开发环境,方便开发者进行调试和测试。
/**
* @description development|开发环境
* @param getBaseCfg |使用基础配置
* @param {Object} merge |将多个 webpack 配置文件合并成一个
*/
const getBaseCfg = require('./webpack.base');
const { merge } = require('webpack-merge');
const path = require('path');
module.exports = merge(getBaseCfg(true), {
devtool: "source-map",
devServer: {
port: 3000,
compress: false, //|压缩
hot: true, //|热更新
historyApiFallback: true,//| 解决404的问题
static: {
directory: path.join(__dirname, '../public')
}
}
})
? 配置 webpack.prod.js
它包含了生产环境的配置,如代码压缩、文件指纹等。这个配置文件主要用于生产环境,用于优化代码和提高性能。
/**
* @description productions
*/
const getBaseCfg = require('./webpack.base');
const { merge } = require('webpack-merge');
const path = require('path');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
// 优化, 压缩, 分治。
module.exports = merge(getBaseCfg(false), {
/**
* @description 优化方案配置
* @param minimizer |压缩方案配置
* @param CssMinimizerPlugin |压缩css
* @param TerserPlugin |压缩JS
* @param splitChunks | (https://webpack.docschina.org/plugins/split-chunks-plugin)
*/
optimization:{
minimizer: [
new CssMinimizerPlugin(),
new TerserPlugin({
parallel: true, // 并行压缩
terserOptions: {
compress: {
pure_funcs: ["console.log", "console.warn"]
},
},
}),
],
splitChunks: {
// 缓存配置
chunks: 'async',
minSize: 20000,
minChunks: 1,
cacheGroups: {
vendors: {
priority: 1,
test: /node_modules/,
name: 'vendors',
},
commons: {
name: 'commons',
minChunks: 3,
},
},
},
}
})
? 将基本配置和开发环境配置弄好后,为了使用终端命令行能运行项目,还需要修改子包(react-temp) package.json 的 scripts 配置,并移除之前测试用的 “type”: “module”
"scripts": {
"start": "webpack-dev-server -c scripts/webpack.dev.js",
},
? 当前的目录结构如下
? 配置好脚本后,尝试执行 npm run start运行,最终调式结果如下:
到这里子包 react-temp 的最基本配置已经完成,在该包上可进行react的开发了。但为了更智能还需要更多的配置,列如想要丝滑的写CSS 还需配置 less、postCssd等
3.10.安装配置PostCss & Less & tailwindcss
为了更丝滑的写CSS,这里使用了原子化的 taullwidcss 来写,当然你也可以使用 less 来写。但不管使用什么方式都需要先配置Css的解析。
- PostCss : 它是CSS界的 babel ,专门用于解析各类样式
在配置前先梳理一下常见的CSS配置方案:
- CSS in JS : 在css 中书写类似JS的内容 ,常见的如,emotion 、styled-component(代码更灵活,但也更烦杂了)
- CSS module : 模块化css,将部分代码抽离共用
- utility CSS : 原子化css ,常见的如 tailwindcss,unoCss
在本例中采用模块化和原子化这两种形式。配置如下
? 在根目录monorepo中将需要的依赖包安装到子包中
# CSS
pnpm add css-loader -D --filter @norush/react.temp
pnpm add style-loader -D --filter @norush/react.temp
pnpm add less less-loader -D --filter @norush/react.temp
pnpm add mini-css-extract-plugin -D --filter @norush/react.temp
pnpm add postcss postcss-loader -D --filter @norush/react.temp
pnpm add tailwindcss -D --filter @norush/react.temp
pnpm add autoprefixer -D --filter @norush/react.temp // css前缀自定义插件
? 在子包react-temp中新建src/index.less, 并配置相关的webpack.base.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module:{
rules:[
/**
* @description css解析
* @method postcss-loader
*/
{
test: /.(css|less)$/,
use:[
MiniCssExtractPlugin.loader,
'style-loader',
'css-loader',
'less-loader',
'postcss-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
// [content hash]: 内容变了,才有消除缓存的意义和价值。
filename: 'static/css/[name].[contenthash:8].css'
}),
]
? 在子包根目录新建并配置 .postcssrc.json 配置相关引入解析Css需要使用的插件。
- autoprefixer 用于自动化添加,不同浏览器的兼容CSS
{
"plugins":{
"autoprefixer": {},
}
}
? 在子包根目录新建并配置 .browserslistrc、用于添加兼容浏览器,方便 autoprefixer 对不同浏览添兼容的css
IE 8
chrome 20
? 添加兼容配置后CSS 的解析结果如下所示:通过调式图发现,给h1添加的 title样式中的 transform: scale(1) 会自动添加一个旧版本的 -webkit-transform: scale(1) 做兼容配置。
上方webpack.base.js 配置的 CSS 不同解析规则的调式结果:
? ‘style-loader’ : 会以 <style>
的形式插入到最终打包的html文件中
? ‘mini-css-extract-plugin‘:使用该插件时会生成一个main.css的文件,并通过 , 更方便做热更新。在生产环境,则需要放在单独的文件里,所以使用’mini-css-extract-plugin‘。
? 在 wabpack.base.js 修改新的CSS 配置, 由于css不需要再解析单独分开配置。
rules [
/**
* @description css解析
* @method postcss-loader
*/
{
test: /.(less)$/,
use: [
!isDev ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"less-loader"
]
},
{
test: /.(css)$/,
use: [
!isDev ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
]
},
]
到这里已经完成最基本的less配置,可以正常使用其开发。但为了更高的复用性,我们还需要配置CSS模块化。
CSS module 模块化配置
?配置 webpack.base.js 模块化CSS部分,设置自定义样式名称防止冲突。添加 oneOf 属性防止重复渲染。
/**
* @description css解析
* @method postcss-loader
*/
{
oneOf: [
{
// 定义一下,使用 xxx.module.(less|css)
test: /.module.(less|css)$/,
include: [path.resolve(__dirname, '../src')],
use: [
!isDev ? "style-loader" : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 2,
// 开启 css modules
modules: {
localIdentName: '[path][name]__[local]--[hash:base64:4]'
}
}
},
"postcss-loader",
"less-loader"
]
},
// code ...
]
}
]
},
? 在新建全局模块化的CSS 文件 react-temp/src/app.module.less ,并定义模块化样式。
.modtitle {
background-color: orange;
}
? 为了引入 app.module.less 使用模块时不报错误提示,还需配置 TS的全局模块申明。
declare module '*.module.less'
declare module '*.less'
declare module '*.svg'
declare module '*.png'
declare module '*.gif'
declare module '*.webp'
declare module '*.jpg'
declare module '*.css'
? 在App.tsx 中使用模块化 app.module.less
import React from 'react';
import styles from './app.module.less'
type Props = {}
export default function App({}: Props) {
return (
<>
<h1 className='title'>react-temp App hello world</h1>
<h2 className={styles.modtitle}>react-temp App Module Css </h2>
</>
)
}
? 最终模块化调式结果如下:
到这里CSS模块化的内容已经基本配置完成。当然如习惯使用原子化css开发,还需要配置tailwindcss 。
tailwindcss 配置
? 在子包根目录下初始化 npx tailwindcss init
并配置 tailwindcss.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{tsx, ts, jsx, js}"
],
theme: {
extend: {},
},
plugins: [],
}
? 在index.less 引入tailwindcss配置
@tailwind base;
@tailwind components;
@tailwind utilities;
? 在 .postcssrc.json 配置tailwindcss
{
"plugins":{
"autoprefixer": {},
"tailwindcss": {}
}
}
? 安装VSC tailwindcss 插件
? 安装插件后使用更智能
? 最终调试结果如下:
到这里CSS的配置就算是全部完成了。
3.11.配置Assets静态资源
需要良好的视觉效果,除了CSS我们还需要结合大量的图片和音视频,所以在这里需要补充webpack.base.js静态资源的配置。简单配置如下
module:{
rules:[
/**
* @description assetss|静态资源配置
* @param 图片、字体
* @param 视频、音频
*/
{
test: /.(png|jpg|jepg|git|svg)$/,
type: 'assets',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
}
},
generator: {
filename: 'static/images/[name][ext]'
}
},
{
test: /.(woff2|eot|ttf|otf)$/,
type: 'assets',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
}
},
generator: {
filename: 'static/fonts/[name][ext]'
}
},
{
test: /.(mp4|mp3|webm)$/,
type: 'assets',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
}
},
generator: {
filename: 'static/medias/[name][ext]'
}
}
]
}
3.12.配置Script脚本环境变量
在实际开发中为了快速切换到不同环境,通常还需要在package.json文件中配置Script脚本的环境变量。
配置Script脚本环境变量可以让你在打包过程中使用不同的环境变量,例如开发环境、测试环境、生产环境等。这样可以让你的应用程序在不同的环境中运行时具有不同的行为,例如使用不同的API地址、不同的数据库配置等。同时,配置Script脚本环境变量也可以让你更加方便地管理应用程序的配置信息,避免硬编码。
了解其作用后来看看Script脚本的环境变量具体是怎么配置的。
? 首先安装我们所需要的插件
pnpm add cross-env -D --filter @norush/react.temp //用于设置和使用环境变量的脚本
pnpm add @types/node -D --filter @norush/react.temp //用于读取环境变量时不报错
? 配置prod环境的脚本环境变量,添加 cross-env ,自定义变量名并赋值变量 PRIMARY=blue
"start:prod": "cross-env PRIMARY=blue webpack-dev-server -c scripts/webpack.dev.js",
? 配置webpack.base.js默认导出node
const webpack = require('webpack');
//code ...
plugins: [
new webpack.DefinePlugin({
'process.env.PRIMARY': JSON.stringify(process.env.PRIMARY)
})
]
? 测试变量 process.env.PRIMARY是否可用
import React from 'react';
import styles from './app.module.less'
type Props = {}
export default function App({}: Props) {
return (
<>
<h1 className='title'>react-temp App hello world</h1>
<h2 className={styles.modtitle}>react-temp App Module Css </h2>
<h2 className="bg-red-500">react-temp App tailwindcss </h2>
<div style={{
fontSize: '24px',
color: process.env.PRIMARY
}}>
start:prod 脚本环境变量 process.env.PRIMARY
</div>
</>
)
}
? 脚本环境变量最终调式结果
通过调式结果说明可以在子包中使用到脚本的环境变量,实际开发中,可以通过该环境变量请求不同环境资源。
3.13.建立主包与子包的关联
到这里子包的搭建和配置已经基本完成,但为了更符monorepo包的管理方式,需要将子包的公用依赖安装到主包中。具体修改配置如下
? 将子包中package.json 的react相关依赖去掉后,
"dependencies": {
"@norush/shared": "workspace:^1.0.0",
"lodash": "^4.17.21",
},
? 之后在monorepo根目录重新安装依赖,同时安装 @babel/preset-env 到子包中
pnpm i react @types/react react-dom @types/react-dom -S -w
pnpm add @babel/preset-env -D --filter @norush/react-temp //链接子包与主包
? 安装好主包依赖后,重新在子包中执行
pnpm install
? 配置 .babelrc 通过 @babel/preset-env 让子包与主包建立联系。
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
],
"@babel/preset-react",
"@babel/preset-typescript"
]
}
? 调式最终结果如下
这时你会发现子包在去掉react相关依赖后,之前的写法依然可以正常运行。这说明主包与子包之间的链接已经配置成功。
到这里子包的基本简单配置已基本完成。如子包需要状态、或路由可以再添加相关配置。
总结
通过该案学能到什么内容?
1,更深入的理解了monorepo架构的搭建用意。
2,理解日常React开发中的单个项目的相关配置,和管理机制。
3,理解NPPM多包管理方案。
4,更深入的理解wabpack相关配置。