管理系统基建配置

本文讲述项目基建的创建,我将采用CRACO 管理webpack配置相关,好处:无需eject,且方便修改webpack、babel、postcss等配置。

环境

node 18.20.3 环境

react18版本

webpack5 + craco构建

一、使用craco管理项目

1、安装:

yarn add @craco/craco -D

2、package.json 配置如下
{
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test"
  }
}
3、项目中创建craco.config.ts
const path = require('path');
const CracoLessPlugin = require('craco-less');

module.exports = {
  webpack: {
    alias: {
      // 配置tsconfig.json 实现路径别名
      '@': path.resolve(__dirname, 'src')
    },
    configure: (webpackConfig, { env, paths }) => {
      // 自定义 webpack 配置
      return webpackConfig;
    }
  },
  // 配置 less
  plugins: [],
  // 配置代理
  devServer: {}
};
4、拆分 craco.config.ts

我将此文件拆分,更有利于分别配置。 将webpack和style区分

/**
 * Craco 重写 CRA 配置
 *  - GitHub:https://github.com/gsoft-inc/craco
 *  - 配置参数:https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#configuration-overview
 *  - 快速指南:https://blog.eleven.net.cn/2020/09/11/cra/craco/
 *
 * Tips:
 *  1、区分 node 运行环境 —— NODE_ENV
 *    - whenDev ☞ process.env.NODE_ENV === 'development'
 *    - whenTest ☞ process.env.NODE_ENV === 'test'
 *    - whenProd ☞ process.env.NODE_ENV === 'production'
 *  2、NODE_ENV 可以区分 node 运行环境,仅支持 development、test、production,
 *    自定义的 REACT_APP_BUILD_ENV 用于区分编译环境,支持 development、test、uat、production。
 *  3、craco 有提供一些好用的 plugin(https://github.com/gsoft-inc/craco#community-maintained-plugins),推荐优先考虑使用现成的 craco plugin 去解决问题。
 *  4、CRA 脚手架相关的配置覆盖,优先使用 craco 提供的快捷方式去配置。解决不了的问题,在 configure 函数中配置。
 *    推荐 configure 配置使用函数形式,而非对象形式。虽然,函数形式更复杂了一点,但是二者是互斥的,只能选择其中一种。
 */

const path = require('path');
// const CracoLessPlugin = require('craco-less');

module.exports = {
  babel: require('./craco/babel'),
  style: require('./craco/style'),
  webpack: require('./craco/webpack'),
  // 如果要新增插件,可直接在这里新增。 当然,你在 configure 里添加也可以,但不推荐。
  // 如果你想修改内置的某些 webpack-plugin 参数,必须到 configure 里去修改
  plugins: [],
  // 开发环境配置代理
  devServer: {
    port: 3999,
    historyApiFallback: true,
    liveReload: false,
    //系统代理
    proxy: {
      '/xxx': {
        target: 'xxx',
        //路径重写-现在,对 /xxx/users 的请求会将请求代理到 http://localhost:3999/api/users。
        pathRewrite: { '^/xxx': '' }, 
        //默认情况下,代理时会保留主机头的来源,可以将 changeOrigin 设置为 true 以覆盖此行为。
        changeOrigin: true,
        //接受在 HTTPS 上运行且证书无效的后端服务器
        secure: false, 
        //对于浏览器请求,想要提供 HTML 页面,但是对于 API 请求,想要代理它。 可以执行以下操作:
        bypass: function (req, res, proxyOptions) {
          if (req.headers.accept.indexOf('html') !== -1) {
            console.log('Skipping proxy for browser request.');
            return '/index.html';
          }
        },
      }
    },
  }
};
5、使用环境工具env-cmd

env-cmd

安装

yarn add env-cmd -D

配置package.json

"scripts": {
  "start": "env-cmd -e development craco start",
  "build-prod": "env-cmd -e production craco build",
  "build-test": "env-cmd -e test craco build",
},

使用

创建根.env-cmdrc.js,这样就可以创建任意环境了!

/**
 * env-cmd 注入 node 运行环境变量
 *  - env-cmd 支持定义任意多的环境,自动注入定义的环境变量,仅需在 package.json scripts 启动时,通过 -e 指定运行时对应的环境;
 *  - https://github.com/toddbluhm/env-cmd
 *
 * Tips:
 *  1、自定义的环境变量:
 *    - 如果你期望在 js 代码中读取,「必须以 REACT_APP_ 打头」,否则会被 CRA 脚手架经过内部处理忽略掉;
 *    - 如果仅仅在构建阶段使用,可以不使用 REACT_APP_ 前缀;
 *  2、CRA 内置的环境变量列表:https://create-react-app.dev/docs/advanced-configuration
 */

const package = require('./package.json');

module.exports = {
    development: {
        REACT_APP_BUILD_ENV: 'development', // 编译环境
        PUBLIC_URL: '', // 配置静态资源 url,最终影响 output下的 publicPath(开发环境不需要配置)
        ENABLE_VCONSOLE: false, // 是否开启 vconsole( production 环境即使设置为 true 也不会开启)
        DANGEROUSLY_DISABLE_HOST_CHECK: true, // 允许代理 host 通过 IP 地址访问,
    },
    test: {
        REACT_APP_BUILD_ENV: 'test',
        // PUBLIC_URL: `https://static2.test.ximalaya.com/yx/${projectName}/last/${outputDir}/`,
        BUILD_PATH: 'dist', // 自定义打包输出目录
        INLINE_RUNTIME_CHUNK: false,
        GENERATE_SOURCEMAP: true,
        REMOVE_FILENAME_HASH: false, // 是否移除编译产物中 js/css 文件名的 hash 值
        ENABLE_VCONSOLE: false,
    },
    uat: {
        REACT_APP_BUILD_ENV: 'uat',
        // PUBLIC_URL: `https://s1.uat.xmcdn.com/yx/${projectName}/last/${outputDir}/`,
        BUILD_PATH: 'dist',
        INLINE_RUNTIME_CHUNK: false,
        GENERATE_SOURCEMAP: true,
        REMOVE_FILENAME_HASH: false,
        ENABLE_VCONSOLE: false,
    },
    production: {
        REACT_APP_BUILD_ENV: 'production',
        // PUBLIC_URL: `https://s1.xmcdn.com/yx/${projectName}/last/${outputDir}/`, // 尽量使用 https,避免运营商劫持资源
        BUILD_PATH: 'dist',
        INLINE_RUNTIME_CHUNK: false, // runtime 代码是否内嵌到 html 中
        GENERATE_SOURCEMAP: true, // 是否开启 sourcemap
        // REACT_APP_SOURCE_MAPPING_URL: `http://sourcemap.ximalaya.com/${projectName}/${outputDir}/`, // 暂时仅支持 http 协议
        REMOVE_FILENAME_HASH: false,
        SHOULD_DROP_DEBUGGER: true, // 打包时是否移除 debugger
        SHOULD_DROP_CONSOLE: true, // 打包时是否移除 console
    },

    // 任意多的环境
};

6、不实用工具

可以定义.env | .env.development | .env.production | .env.test

使用方式:添加自定义环境变量 | Create React App 中文网

大概就是process.env.REACT_APP_xxx 打头即可!也可以再html中使用定义的变量!这好像是大部分项目采用的,不过我还是推荐统一用env-cmd处理,方便一点。

二、按需加载&webpack配置

1、antd按需加载无需配置

antd 4.x | antd 5.x 都有按需加载效果,无需配置。如下图:

antd3.x及以下配置按需加载

  • 在 bebel 配置中新增 babel-plugin-import,搞定 AntDesign 按需加载
module.exports = {
    plugins: [
        //AntDesign 按需加载 (antd4版本以上 支持自动按需加载,无需配置此插件)
        [
            'babel-plugin-import',   // yarn add babel-plugin-import -D
            {
                libraryName: 'antd',
                libraryDirectory: 'es',
                style: true,
            },
            'antd',
        ],
    ]
}
2、babel-lodash等其他常用库按需加载

是否可以用 这个插件呢?vant、antd-mobile等等这些

lodash 按需引入,如下

官网babel-plugin-lodash

https://github.com/lodash/babel-plugin-lodash

预期效果如下:

//原来
import _ from 'lodash'  这里全量引入了,需要优化
import { add } from 'lodash/fp'

const addOne = add(1)
_.map([1, 2, 3], addOne)


//变成
import _add from 'lodash/fp/add'
import _map from 'lodash/map'

const addOne = _add(1)
_map([1, 2, 3], addOne)
安装

npm i --save lodash

npm i --save-dev babel-plugin-lodash @babel/cli @babel/preset-env

官网有不奏

https://blog.youkuaiyun.com/gitblog_00091/article/details/138746055 也可以看这篇

效果

打包之后 减少24.27kb

3、webpack配置

webpack5为例(建议如果是5以下版本,先升级5!)

webpack配置项见配置

3.1 定制devServer

主要是自定义端口号、配置开发代理服务、热更新相关!

 devServer: {
    port: 9876,
    client: {
      logging: 'error', // 日志级别
      // progress: true, // 进度条
    },
    proxy: {
      '/xxx': {
        target: 'xxx',
        //路径重写-现在,对 /xxx/users 的请求会将请求代理到 http://localhost:3999/api/users。
        pathRewrite: { '^/xxx': '' },
        //默认情况下,代理时会保留主机头的来源,可以将 changeOrigin 设置为 true 以覆盖此行为。
        changeOrigin: true,
        //接受在 HTTPS 上运行且证书无效的后端服务器
        secure: false,
        //对于浏览器请求,想要提供 HTML 页面,但是对于 API 请求,想要代理它。 可以执行以下操作:
        bypass: function (req, res, proxyOptions) {
          if (req.headers.accept.indexOf('html') !== -1) {
            console.log('Skipping proxy for browser request.');
            return '/index.html';
          }
        },
      }
    },
  }
3.2 配置@映射

第一步:

 resolve: {

    //craco 无需resolve一层
    alias: {
      '@': path.resolve(__dirname, '../src'),
      Templates: path.resolve(__dirname, 'src/templates/'),
      // xyz$: path.resolve(__dirname, 'path/to/file.js'), //配置精准解析
       'ignored-module': false,//将 resolve.alias 设置为 false 将告知 webpack 忽略模块。
    },
  },

第二步:

"compilerOptions":{
  "paths": {
    "@/*": ["./src/*"]
  }
}
3.2.1 Preact

注:一些简单的项目,H5等可以借此优化项目

// 如果需要优化到 preact,开启该选项。一般一些移动端项目,涉及到的包体积小可以优化
// 'react: 'preact/compat',
// 'react-dom': 'preact/compat',
// 'react-dom/test-utils': 'preact/compat',
3.3 通用常用plugin

这里我列举一些通用的插件,特殊定制插件根据具体情况看!插件使用方式根据具体情况判定!

const path = require('path');
const { whenDev, whenProd, when } = require('@craco/craco');


const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

const WebpackBar = require('webpackbar');


plugins: {
  // 添加新插件
  add: [
    /**
     * 1.使用DllPlugin打包第三方库 2.使用DLLReferencePlugin引用manifest.json,去关联第1步中已经打好的包
     * https://webpack.docschina.org/plugins/dll-plugin/ 可以减少打包时间,第三方库打包一次就不参与打包了
     * https://juejin.cn/post/7154351787246354462?searchId=2024111918133865459BEC291608012FA3#heading-23  看这篇文章
     * 比较麻烦,手动维护困难,慎用~
     */
    // [new DllReferencePlugin({ manifest })],
    /**
            * 编译进度条
            *  - https://www.npmjs.com/package/webpackbar
            */
    new WebpackBar(),
    /**
            * 模块间循环依赖检测插件(监测到循环依赖时,会报错)
            *  - https://juejin.im/post/6844904017848434702
            */
    ...whenProd(() => [new CircularDependencyPlugin(
      {
        exclude: /node_modules/,
        include: /src/,
        failOnError: true,
        allowAsyncCycles: false,
        cwd: process.cwd(),
      }),
                      ], []),
    /**
            * 编译产物分析
            *  - https://www.npmjs.com/package/webpack-bundle-analyzer
            */
    ...when(isBuildAnalyzer, () => [new BundleAnalyzerPlugin()], []),
    /** 
             * js,css文件过大进行gzip压缩文件 
             * - https://github.com/webpack-contrib/compression-webpack-plugin
             * 
             * # nginx配置如下 识别gzip
             * gzip on; # 开启gzip压缩
             * gzip_min_length 10k; # 小于4k的文件不会被压缩,大于4k的文件才会去压缩
             * gzip_buffers 16 8k; # 处理请求压缩的缓冲区数量和大小,比如8k为单位申请16倍内存空间;使用默认即可,不用修改
             * gzip_http_version 1.1; # 早期版本http不支持,指定默认兼容,不用修改
             * gzip_comp_level 2; # gzip 压缩级别,1-9,理论上数字越大压缩的越好,也越占用CPU时间。实际上超过2的再压缩,只能压缩一点点了,但是cpu确是有点浪费。因为2就够用了
             * # 压缩的文件类型 MIME类型,百度一下,一大把                                    # css             # xml             # 识别php     # 图片
             * gzip_types text/plain application/x-javascript application/javascript text/javascript text/css application/xml application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/x-woff font/ttf;
             * gzip_vary on; # 是否在http header中添加Vary: Accept-Encoding,一般情况下建议开启 
             * https://blog.youkuaiyun.com/niuniu2878499107/article/details/141706191 
             * */
    ...whenProd(() => [new CompressionWebpackPlugin({  //压缩插件
      algorithm: 'gzip',   //压缩方式
      test: /\.js$|\.css$/,   //匹配文件名
                threshold: 50240, //阈值  代表大于设置值生成压缩文件
                filename: '[path].gz[query]',//使得多个.gz文件合并成一个文件,这种方式压缩后的文件少,建议使用
                /**
                 * 是否删除原有静态资源文件,即只保留压缩后的.gz文件,
                 * 建议这个置为false,还保留源文件。以防gzip无法访问
                 */
                deleteOriginalAssets: false
            })], []),
            /** 
             * 动态链接库
             * 内网打包,将一些库例如axios,echarts等。默认到vendor目录 defer引入 
             * 这个要考虑下,是否要这样做,会不会影响加载,慎用
             * 因为:有时我们只使用第三方库中的一小部分功能,用script标签全量引入不太合理
             * */
            ...whenProd(() => [new HtmlWebpackExternalsPlugin({
                externals: [
                    {
                        module: 'axios',
                        entry: {
                            path: 'axios.min.js',
                        },
                        global: 'axios',
                    },
                    {
                        module: 'echarts',
                        entry: {
                            path: 'echarts.min.js',
                            attributes: {
                                async: '',
                            },
                        },
                        global: 'echarts',
                    }
                ]
            })], []),
            ...whenProd(() => [
                // 使用 terser 来压缩 JavaScript。
                // - https://webpack.docschina.org/plugins/terser-webpack-plugin/
                // 本来应该写在minimizer中的但是在craco中不生效
                new TerserPlugin({
                    parallel: true, // 并行运行以提高构建速度
                    terserOptions: {
                        compress: {
                            drop_debugger: true, // 删除 debugger   认默认值:true
                            drop_console: ['log', 'info'], // 删除 console   默认值:false
                        },
                    },
                    extractComments: false, // 移除注释
                })
            ], []),


            // ModuleFederationPlugin  用到再说 
            // - https://blog.youkuaiyun.com/m0_37890289/article/details/144411901 这篇做了简单分享,可以参考
        ],
        remove: [] // 删除插件,慎用
    },
const webpack = require('webpack');
const path = require('path');

module.exports = {
  context: __dirname,
  entry: {
    // 这里添加所有需要的库
    vender: ['react', 'react-dom', 'axios'],
  },
  mode: 'production',
  output: {
    path: path.join(__dirname, 'dll'),
    filename: '[name].[contenthash:8].js',
    library: '[name]',
  },
  plugins: [
    new webpack.DllPlugin({
      // 输出 manifest.json 文件,业务的打包需要这个索引文件
      path: path.join(__dirname, 'vendor', '[name]-manifest.json'),
      name: '[name]',
    }),
  ],
};


//DllReferencePlugin 引入,如上一篇代码

总结:

const { whenDev, whenProd, when } = require(‘@craco/craco’);环境判断
const { merge } = require(‘webpack-merge’);合并配置
const WebpackBar = require(‘webpackbar’);进度条
const CircularDependencyPlugin = require(‘circular-dependency-plugin’);监测循环依赖 :a -> b b -> a
const { BundleAnalyzerPlugin } = require(‘webpack-bundle-analyzer’);性能分析:可提供资源体积分析图
const CompressionWebpackPlugin = require(‘compression-webpack-plugin’)压缩:对于一些较大的js,可进行压缩成gzip等格式,需nginx做压缩包识别配置
const HtmlWebpackExternalsPlugin = require(‘html-webpack-externals-plugin’)动态链接库:将一些常用的包如axios 等提取成为单独js 通过script引入。注意:这是全量引入,慎用
const TerserPlugin = require(‘terser-webpack-plugin’);代码简化: 清除生产的debugger console等。配和optimization.minimize使用。
DllReferencePlugin | DllPluginDllPlugin 打包第三方库 DllReferencePlugin引入使用。慎用,这样会导致项目难以理解
ModuleFederationPlugin见下一节

也可以自定义插件:

参考文章:https://zhuanlan.zhihu.com/p/375802216

3.4 ModuleFederationPlugin模块联邦示范

gitee地址:xxx

让我们先来看看如何在多个应用直接实现模块共享,原来是?

1、发布npm组件

容易陷入: <font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">npm 发布 ——> app 升级 npm 包 -> app 上线</font> 这样的轮回之中。所以iframe是另一种方案。

2、iframe

将 chat应用 做一个 <font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">iframe</font> 嵌入到各个应用中。这样支需要升级chat一个应用,其他应用不用改动。

但也有缺点:

* 使用 iframe 每次打开组件,DOM 树都会重建,所以打开速度较慢

*通信方面: iframe 跨应用通信使用 <font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">window.postMessage</font> 的方式,若应用部署在不同的域名下,使用 <font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">postMessage</font> 需要控制好 <font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">origin</font><font style="color:rgb(255, 80, 44);background-color:rgb(255, 245, 245);">source</font> 属性验证发件人的身份,不然可能会存在跨站点脚本漏洞。非常危险和麻烦!切换iframe,资源都会重新加载。

3、ModuleFederationPlugin模块联邦

而 MF 很好地解决了多应用模块复用的问题,相比上面的这 2 中解决方案,它的解决方式更加优雅和灵活。

但也有缺点:

  • CSS 样式污染问题,建议避免在 component 中使用全局样式。
  • 模块联邦并未提供沙箱能力,可能会导致 JS 变量污染
  • 在 vite 中, React 项目还无法将 webpack 打包的模块公用模块

4、微前端qiankun

解决了css样式污染问题,但无法保证依赖的第三方库的全局样式可以做到应用之间的隔离。所以还是轻易不要写全局样式的好!

这里重点讨论模块联邦,乾坤暂时不深入研究。

3.5 代码optimization分割

webpack提供了optimization ,可自定义代码打包逻辑。配合HtmlWebpackPlugin使用非常nice!

optimization: {
  //告知 webpack 使用 
  //TerserPlugin CssMinimizerPlugin(告知开发环境也启用)
  //或其它在 optimization.minimizer定义的插件压缩 bundle。
  minimize: true,
    minimizer: [// 在这里写 TerserPlugin 插件不生效,在plugins中写生效
  ],
    /**
     * 代码分割
     * https://webpack.docschina.org/plugins/split-chunks-plugin/
     */
    splitChunks: {
    //这表明将选择哪些 chunk 进行优化。当提供一个字符串,有效值为 all,async 和 initial。
    //设置为 all 可能特别强大,
    //因为这意味着 chunk 可以在异步和非异步 chunk 之间共享。
    chunks: 'async', // 设置为all 本地项目无法启动
      name: false,
      ...when(
        !isDev,
        () => ({
          chunks: 'all',
          minSize: 30000, // 生成 chunk 的最小体积(约30KB)
          maxSize: 244000, // chunk 的最大体积(约244KB)
          cacheGroups: {
            antd: {
              name: 'chunk-antd',
              test: /[\\/]node_modules[\\/](antd|@ant-design)[\\/]/,
              priority: 0,
              chunks: 'all',
              reuseExistingChunk: true,
            },
            vendor: {
              test: /[\\/]node_modules[\\/](react|@hot-loader\/react-dom)[\\/]/,
              priority: -10,
              chunks: 'all',
              reuseExistingChunk: true,
            },
            // 常用第三方库单独打包
            library: {
              test: /[\\/]node_modules[\\/](@xmly|moment|html2canvas)[\\/]/, // 常用第三方库单独打包 可以在此继续添加
              name: 'library',
              chunks: 'all',
              priority: -10,
              reuseExistingChunk: true,
            },
            common: {
              name: 'common',
              minChunks: 2, // 至少被引用 2 次才会被分割
              priority: -20,
              chunks: 'all',
              reuseExistingChunk: true,
            },
          },
        }),
        {}
      ),
      },
  /**
   * 运行时 chunk
   * https://webpack.docschina.org/plugins/split-chunks-plugin/#runtime-chunk
   */
  // runtimeChunk: {
  //   name: entrypoint => `runtime-${entrypoint.name}`
  // }
},
3.6 图片文件处理

用loader 处理图片文件

1、MiniCssExtractPlugin 将CSS 提取到单独的文件中,支持按需加载

{
    test: /\.css$/i,
    /**
     * MiniCssExtractPlugin
     * 本插件会将 CSS 提取到单独的文件中,
     * 为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。
     * 本插件基于 webpack v5 的新特性构建,并且需要 webpack 5 才能正常工作。
     */
    use: [MiniCssExtractPlugin.loader, "css-loader"],
},

2、将小文件图片,转换成base64

{
    test: /\.(svg|jpg|jpeg|png|gif)$/i,
    // 使用 asset 模块处理,Webpack 5 的 asset 模块来处理文件。
    // 这是 Webpack 5 新增的功能,替代了之前的 url-loader 和 file-loader 和 raw-loader
    // https://webpack.docschina.org/guides/asset-modules#inlining-assets
    type: 'asset',
    parser: {
        dataUrlCondition: {
            maxSize: 20 * 1024, // 20kb以下转为base64
        },
    }
},

3、处理svg可以像组件一样导入

module: {
  rules: [
    {
      {
      test: /\.svg$/, // 匹配所有 .svg 文件
      use: ["@svgr/webpack"], // 使用 @svgr/webpack 加载器处理 SVG
    }
    },
  ],
},

使用;

import { ReactComponent as Report } from '@/assets/images/report.svg';


//自定义svg 颜色
<Dashboard style={{
        fill: selectedKeys[0] === '/dashboard' ? '#D00D1B' : '#353D49'
}} />

4、处理图片,还可以将大文件转换成webp(注:一般大图片会走CDN,这里只是备选方案!)

 {
      test: /\.(png|jpe?g|gif|svg)$/i,
      use: [
           {
              loader: 'file-loader', // 或 url-loader
              options: {
                  name: '[name].[ext]',
                  outputPath: 'images/'
              }
          },
           {
              loader: 'image-webpack-loader',
               options: {
                  mozjpeg: {
                      progressive: true,
                      quality: 65
                  },
                   optipng: {
                       enabled: false,
                   },
                   pngquant: {
                       quality: [0.65, 0.90],
                       speed: 4
                   },
                   webp: {
                       quality: 75
                   }
               }
          }
  ]
}
3.7 总结

以上列举的常用的 配置项等,后续有最新的、实用的配置,我再补充!

webpack优化一般从打包体积与加载构建速度两个方向着手,我着力于打包体积优化,关于构建速度方面,另一个打包构建vite对其有很好的支持,因为支持了esm!同时优化打包产物,能更好的体现产出!当然项目太大,也可以将项目用模块联邦拆分!

加载优化即常用的chunk lazy懒加载将项目模块包裹即可!

关于引入页面的script标签,对于稍后可以加载的script,可以采用defer=“defer” 异步加载策略!如下

<script defer="defer" src="/vendor/axios/dist/axios.min.js"></script>

关于vite,后续一定会再补充!

三、代码提交规范

ESLint、Pretttier、Husky、CommitLint

总体如下:

  • ESLint + Prettier 配置
  • 代码提交前的自动检查 (使用 lint-staged)
  • Git Commit 规范 (使用 commitlint + husky)
相关依赖

总的依赖基本上都齐了,可以一次性安装,也可以根据代码以下代码自行安装,注意node版本号问题。(新版本有可能不兼容老版本node,导致出错)

ESLint 相关

:::info
yarn add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-import eslint-plugin-jsx-a11y prettier eslint-config-prettier eslint-plugin-prettier

:::

Commit 规范相关

:::info
yarn add -D @commitlint/cli @commitlint/config-conventional husky lint-staged

:::

背景

团队开发的成员多了之后,项目由多人维护,为了保证代码书写风格相同,有一个规范及其重要。当然你架不住一些个性生僻的代码带来的隐患,这里暂时先讨论提交规范。

以React为例,在项目中配置 eslint + pretttier + husky + commitLint 代码提交规范,是我的标准化选择。

加入你通过npx或这其他,创建了一个create-react-appp项目

1、Eslint

安装ESlint

yarn add eslint -D

生成配置文件 npx eslint --init 或这自己在根目录新建.eslintrc.js

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended', // ESLint 推荐规则
    // 'plugin:@typescript-eslint/recommended',  // TypeScript 推荐规则
    'plugin:react/recommended',// React 推荐规则
    // 'plugin:react-hooks/recommended',// React Hooks 推荐规则
    // 'plugin:prettier/recommended',// Prettier 格式化规则
  ],
  parser: '@typescript-eslint/parser',
  ignorePatterns: ['public/*', 'node_modules/*', 'dist/*', 'build/*'],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint', 'react'],
  settings: {
    react: {
      version: 'detect',
    },
  },
  rules: {
  },
};

<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">packages.json</font> 中的 <font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">script</font> 配置命令

"scripts": {
   "lint": "eslint --fix \"./src/**/*.{js,jsx,ts,tsx}\""
}

Ok,现在你就可以测试了! 运行yarn run lint 试试吧~

我项目中用到craco管理webpack,进一步可以将eslint转到:

module.exports = {
    /** 
     * 是否启用eslint  默认ture
     */
    enable: true,
    /** 
     * extends ☞ 提供的配置将扩展 CRA 设置 --- 默认
     * file ☞  告诉 craco 使用配置文件
     */
    mode: 'file' /* (default value) */,
    /**
     * 任何 ESLint 配置选项: 
     * https: //eslint.org/docs/latest/user-guide/configuring/
     */
    configure: {},
    configure: (eslintConfig, { env, paths }) => {
        /* ... */
        return eslintConfig;
    },
    /**
     * 任何 ESLint 插件配置选项:
     * https ://github.com/webpack-contrib/eslint-webpack-plugin#options
     */
    pluginOptions: { /* ... */ },
    pluginOptions: (eslintPluginOptions, { env, paths }) => {
        /* ... */
        return eslintPluginOptions;
    },
}

可以设置忽略文件.eslintignore

dist/*

node_modules/*

*.json

public

或者 在ignorePatterns中配置,见上!

2、prettier格式化配置文件 · Prettier 中文网

安装

yarn add prettier -D

没有装插件的,安装prettier-code formatter 插件

配置.prettierrc.js文件,配置好后,重启vscode生效。

module.exports = {
  // 一行的字符数,如果超过会进行换行,默认为80
  printWidth: 100,
  // 行位是否使用分号,默认为true
  semi: true,
  // 字符串是否使用单引号,默认为false,使用双引号
  singleQuote: true,
  // 一个tab代表几个空格数,默认为2
  tabWidth: 2,
  // 是否使用尾逗号,有三个可选值"<none|es5|all>"
  trailingComma: "none",
};

<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">packages.json</font> 中的 <font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">script</font> 配置命令

  "scripts": {
    "format": "prettier --write \"src/**/*.+(js|ts|jsx|tsx)\"",
  }

运行此命令,会讲我们项目中的文件都格式化一遍,也可以添加其他文件

当然也可以设置 <font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">.prettierignore</font> 忽略文件。但我推荐!

node_modules/**
dist/**
public/**
doc/**
3、解决ESlint 和Prettier冲突

eslint主要用于发现代码中的问题,潜在错误。

prettier主要用于自动格式化代码,保证代码风格的一致性。

  • 冲突通常发生在两个工具对某些代码风格规则有不同的处理方式。例如,ESLint 可能要求使用单引号,而 Prettier 默认使用双引号。
  • 当这两个工具同时运行时,它们可能会对代码的同一部分提出不同的修改建议,从而造成冲突。

如何解决呢?答:安装依赖包

yarn add -D eslint-config-prettier eslint-plugin-prettier

eslint-config-prettier 基于 prettier 代码风格的 eslint 规则,即eslint使用pretter规则来格式化代码。

eslint-plugin-prettier 禁用所有与格式相关的 eslint 规则,解决 prettier 与 eslint 规则冲突,确保将其放在 extends 队列最后

之后在.eslintrc.js中

  extends: [
    'eslint:recommended', // ESLint 推荐规则
    ...
    'plugin:prettier/recommended',// Prettier 格式化规则
  ],

现在,当您运行 ESLint 时,它会同时检查代码质量和代码格式。如果代码不符合 Prettier 的格式要求,ESLint 会报告一个错误,提示您使用 Prettier 来格式化代码。

4、vscode自动保存格式化

新政./.vscode/settings.json 自动完成格式化

{
  // 保存的时候自动格式化
  "editor.formatOnSave": true,
  // 默认格式化工具选择prettier
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}


配置完成后,command + s 保存代码试试吧~

5、配置husky(提交校验)Husky

5.1 安装初始化husky

yarn add husky -D 注意:husky安装8版本的保险一点,太高版本和其他插件兼容性问题。

npx husky-init

然后项目中就会出现:

5.2 安装配置lint-staged

yarn add -D lint-staged

用来检查git add 到暂存区的代码

配置lint-staged。 可在package.json中新建

 "lint-staged": {
    "src/**/*.{js,jsx,ts,tsx}": "yarn run lint",
    "src/**/*.{js,jsx,tsx,ts,less,md,json}": [
      "prettier --write",
      "eslint --fix"
    ]
  },

或者新建根文件.lintstagedrc

{
  "**/*.{js,jsx,json,css,scss,less,html,md}": ["prettier --write"],
  "src/**/*.{ts,tsx}": ["prettier --parser=typescript --write"],
  "src/**/*.{ts,tsx,js,jsx}": ["eslint --fix --max-warnings 0"]
}

5.3 开始配置 husky

修改<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">.husky/pre-commit</font>文件,使commit提交时能执行<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">lint-staged</font>钩子

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# 无需运行测试
# npm test

# 格式化代码 校验暂存区代码 
npx lint-staged --allow-empty
6、配置commit-msg

commit-msg规范

6.1 安装

yarn add -D @commitlint/config-conventional @commitlint/cli

6.2 添加根文件 commitlint.config.js 配置文件

module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    // type 类型定义
    "type-enum": [
      2,
      "always",
      [
        "feat", // 新功能 feature
        "fix", // 修复 bug
        "docs", // 文档注释
        "style", // 代码格式(不影响代码运行的变动)
        "refactor", // 重构(既不增加新功能,也不是修复bug)
        "perf", // 性能优化
        "test", // 增加测试
        "chore", // 构建过程或辅助工具的变动
        "revert", // 回退
        "build", // 打包
      ],
    ],
    // subject 大小写不做校验
    // 自动部署的BUILD ROBOT的commit信息大写,以作区别
    "subject-case": [0],
  },
};

@commitlint/config-conventional 是一个规范标准,标识采用什么规范来执行消息校验,默认是Angular的提交规范。

6.3 在husky中添加commitlint钩子

首先在package.jso中添加

 "scripts": {
    "commitlint": "commitlint --config commitlint.config.js -e -V"
  },

然后执行

npx <font style="color:rgb(0, 0, 0);background-color:rgb(250, 250, 250);">husky add .husky/commit-msg "npm run commitlint"</font>

这样.husky中就多了 commit-msg 文件了

现在快来试试提交你的代码吧~

7、自定义提交规则

例如:每个提交关联到 对应的产品文档(个人觉得不是太好,影响开发效率,产品文档应该每个迭代周期就独一份,但提交是很多的,包括代码、bug等等一些)

凑个数,来吧!

为了关联产品文档,配置如下:主要在呢宫颈癌plugin 和三个rule规则~

module.exports = {
 extends: ["@commitlint/config-conventional"],
 rules: {
   ...
   "subject-case": [0],
   ...

    //新增rule
   "must-add-document-url": [2, "always"], // 加入自定义规则
   'body-max-line-length': [2, 'always', 200], // 加入自定义规则
   'header-max-length': [2, 'always', 200], // 加入自定义规则
 },
 plugins: [
   {
     rules: {
       "must-add-document-url": ({ type, body }) => {
         const ALIYUN_DOCUMENT_PREFIX = "https://devops.aliyun.com";

         // 排除的类型
         const excludeTypes = ["chore", "refactor", "style", "test"];
         if (excludeTypes.includes(type)) {
           return [true];
         }

         //这里校验 true 提交规则, 当然也可以自定义提交规则,举一反三!
         return [
           body && body.includes(ALIYUN_DOCUMENT_PREFIX),
           `提交的内容中必须包含云效相关文档地址`,
         ];
       },
     },
   },
 ],
};

然后可以测试下:

git commit -m “feat: 测试自定义提交规则”,然后就提示需要云效的文档链接

正确提交

feat: 测试自定义提交规则 | https://devops.aliyun.com/xxxxxx

到此完毕!

参考文章!

https://juejin.cn/post/7353208973880344626#heading-6

https://blog.youkuaiyun.com/u012570307/article/details/135119353

https://cloud.tencent.com/developer/article/1840688

四、关于样式

1、重置CSS文件

解决兼容性问题

yarn add normalize.css
  
import 'normalize.css/normalize.css'

特定css样式清除

reset.scss

这样即可!

2、sass模块化

安装使用sass

采用sass模块化 Sass: Documentation

安装:yarn add sass sass-loader -D

之后就可以使用啦!亲测,webpack5 无需额外配置sass-loader加载器,过多配置会报错!

一般我将sass公共文件写在src/assets/styles下面!

语法如下:

https://juejin.cn/post/7207410405855985725?searchId=20241119180950DB0C22909D0DC4EA36FE#heading-3

支持多种配置。常用scss组合如下:

/**
* 超出文本省略(不传参数时为单行文本省略)
*/
@mixin text-ellipsis($lineNumber: null) {
  overflow: hidden;

  @if $lineNumber {
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: $lineNumber;
    -webkit-box-orient: vertical;
  } @else {
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}


/**
* 媒体查询
*/

@mixin media-query($breakpoint) {
  @if $breakpoint == 'small' {
    @media (max-width: 576px) {
      @content;
    }
  } @else if $breakpoint == 'medium' {
    @media (max-width: 768px) {
      @content;
    }
  } @else if $breakpoint == 'large' {
    @media (max-width: 992px) {
      @content;
    }
  } @else if $breakpoint == 'xlarge' {
    @media (max-width: 1200px) {
      @content;
    }
  }
}

//简单写了下,后续有用到的再补充...

使用方式:

@include xxxx()

为什么不用antd-style?

注:antd-style库 是Antd5首先推荐的模块化方案,但无法进行样式嵌套,所以放弃此方案!如果要用到全局token,可以单独写这个!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值