搭建 Monorepo 架构 React + pnpm 实例

1.前言

Monorepo 最早的出处是软件开发策略的一个分支,”mono” 表示单一 “repo” 是”repository”的缩写,是指将多个项目的代码存储在同一个版本库中的软件开发方法。多个项目共用一个代码库来管理依赖关系,同一套配置文件,统一构建部署流程等等。这种方法可以提高代码共享和重用的效率,同时也可以简化代码管理和部署过程。平时常见的 Reactjs、Vuejs、Babel 也是 monorepo 管理架构。

目前国内Monorepo 架构常见的管理方法有:

  • npm
  • yarn
  • lerna —–例如:Antd、ahooks
  • pnpm

不管使用什么方式进行管理,在确定将项目设计成 Monorepo 架构前,需要考虑以下几点。

  1. 项目规模较大,需要多个子项目协同开发和维护。
  2. 子项目之间存在明确的依赖关系,需要共享代码和资源。
  3. 需要统一管理和控制版本,方便协作和发布。
  4. 需要提高开发效率和代码复用性。
  5. 需要统一的构建、测试和部署流程。

如果满足以上几点的需求条件,可以设计成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

? 最终效果图
图片.png


3.2.目录结构设计

项目目录结构的布局设计,可能会直接影响到你项目后续的开发和维护。一个好的组织架构关系能让后续的开发和维护更爽快。一般情况下Monorepo架构的文件布局,主要以packages文件为主包。然后通过结合各类工具包和配置文件组合成项目的整体架构。拿ReactJs 和 VueJs 为例:
图片.png图片.png
可以发现不管是ReactJs 还是 VueJs ,我们平常使用的一些核心方法都是封装成各个单独的包,放在packages文件下配合各类工具和插件进行管理的。

? 当然也还有另一种目录结构是这样的:
图片.png
它也是以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"
} 



? 当前目录结构如下
图片.png
到这里我们以基本完成俩个子包的初始化,那么怎么让他们建立联系呢?
说白了就是,如何在一个包(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());






? 最终调试结果如下
图片.png通过调式发现可以在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 的初始化。效果如下
图片.png
到这里 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需要有类型检测,一下情况下分两种情况:

  1. 项目中有使用 **Babel **的直接使用 : @babel/preset-typescript + tsc 做类型检查
  2. 项目中没有有使用 **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环境以及配置好了。当前的配置结果如下
图片.png
到这里子包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.jsonscripts 配置,并移除之前测试用的 “type”: “module”

"scripts": {
  "start": "webpack-dev-server -c scripts/webpack.dev.js",
},
  

? 当前的目录结构如下
图片.png
? 配置好脚本后,尝试执行 npm run start运行,最终调式结果如下:
图片.png
到这里子包 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 的解析结果如下所示:
图片.png通过调式图发现,给h1添加的 title样式中的 transform: scale(1) 会自动添加一个旧版本的 -webkit-transform: scale(1) 做兼容配置。

上方webpack.base.js 配置的 CSS 不同解析规则的调式结果:
? ‘style-loader’ : 会以 <style> 的形式插入到最终打包的html文件中
图片.png
? ‘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>
    </>
  )
}

? 最终模块化调式结果如下:
图片.png
到这里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 插件
图片.png

? 安装插件后使用更智能
图片.png
? 最终调试结果如下:图片.png
到这里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>
    </>
  )
}

? 脚本环境变量最终调式结果
图片.png
通过调式结果说明可以在子包中使用到脚本的环境变量,实际开发中,可以通过该环境变量请求不同环境资源。


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"
  ]
}

? 调式最终结果如下
图片.png
这时你会发现子包在去掉react相关依赖后,之前的写法依然可以正常运行。这说明主包与子包之间的链接已经配置成功。

到这里子包的基本简单配置已基本完成。如子包需要状态、或路由可以再添加相关配置。


总结

通过该案学能到什么内容?
1,更深入的理解了monorepo架构的搭建用意。
2,理解日常React开发中的单个项目的相关配置,和管理机制。
3,理解NPPM多包管理方案。
4,更深入的理解wabpack相关配置。

其它配置案例

vit + Router6 路由配置案例

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYqDtKN8' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片