Webpack + React + TypeScript 构建一个标准化应用

项目及工程化配置以及主要功能配置、规范化管理和使用细节应该都没啥问题了。

后面若发现构建及配置问题、以及可优化的细节部分、包括功能的进一步迭代也会使项目不断更新完善的~

目前主要是做了规范及工程化的配置,后期打算开发并开源一个react-admin标准后台管理系统,用于实践并巩固一些前端技术框架与组件开发等。

hello

效果图

最简单的demo运行效果

最终效果

背景

为什么要自己搭建一个webpack-react-ts的标准化项目?

原因之一

在很早之前的公司时,项目实际开发过程中产生过这种需求:

  1. 每次开发新项目时希望有个完整功能、配置好用、标准化规范的模板可以快速使用

  2. 完整功能:如果是使用create-react-app创建项目,属于最简易版的一个实现了,各种loaderpluginbabelwebpack配置缺失,作为现代前端开发不可或缺的构建工具,其对项目的大部分功能支持是必须的。

  3. 配置好用:webpack对各种插件的配置与集成,基本路由模块、页面模块、组件模块、store状态管理模块等基本常用功能的简要示例及demo,要有一定的覆盖率。

  4. 标准规范:这个可能是工程化构建最重要的部分之一了,eslintprettierstylelintcommitlint等lint规范和格式化以及test测试,还有目录结构规范、命名规范等。对于规范化的意义其重要性不言而喻,可读性、可维护性、开发效率、bug率等可见一斑

原因之二

当然网上肯定有很多优秀的轮子供我们使用,他们有的功能更全、有的细节更好,有的示例更完整。这都是我们学习的榜样,对于其源码阅读和学习也是极为有益的,不过,始终避免不了一点,看的再多都不如自己实践来的有效果,所以,这也是我考虑的原因之一,为了自己实践和操作来提升技术熟练度。

原因三

这也是源于在实际开发过程中的需求,即,不同技术平台及场景适应于不同的技术框架和路线。

举个栗子,

  1. 管理后台系统:这个多适用于PC端,使用单页面应用开发,主流技术框架 ReactVueAngular

  2. 网站类:这类项目一般追求美观和SEO搜索的重要性,而不会像管理后台系统一样有很复杂的逻辑,更多的是一些动效及UI交互,因为SPA的首屏加载及SEO问题,社区有成熟的服务端渲染SSR方案:nextnuxt,分别对应reactvue技术栈,对于习惯了单页面开发的同学还是很友好的。

  3. 移动端:轻量化、轻量化、轻量化,重要的事情说三遍。手机端流量第一,项目越小越轻量性能越好加载也就越快,对于用户体验也就越好。比较手机端一般对应的都是C端用户,不像管理后台主要是给B端及公司内部使用。用户体验要求是完全不一样的。如,移动端版react——preact,又如sveltesolidjs等无虚拟DOM框架,减少大量运行时代码。

所以,我认为每个技术都有其对应的优势和适合的场景,单一框架并不是完全适合梭哈到底的。

这也是我开发的node-cli自动化初始化项目的一个框架模板之一。

接下来对各个不同技术方案及最佳实践也会考虑构建具有实战意义的模板以供使用

主要方向大概分为:webpack+react/vue+typescriptwebpack方向、vite+react/vue+typescriptvite方向、nuxt/next/egg/koa等服务端渲染SSR方向、svelte/solidjs等无虚拟DOM的轻量级框架方向、rollup/parcel等构建工具方向、esbuild/swc等新型编译器与构建工具的结合使用等、以及小程序/跨平台等。

有点飘啊,想法还挺多的🤓~

开搞

目标

  • 技术栈:Webpack5.x + React17.x + Antd4.x + TypeScript4.x + Less
  • 工程规范/格式化:Eslint + Stylelint + Prettier + Commitlint
  • ES6JSXBabel支持
  • 支持HMR热更新
  • 支持低版本浏览器兼容
  • 支持Antd按需加载、自定义主题、css module
  • 支持jscss压缩、chunk拆分、Gzip

依赖

dependencies

"dependencies": {
  "antd": "^4.16.10", // 懂得都懂
  "axios": "^0.21.1", // 懂得都懂
  "clsx": "^1.1.1", // 条件处理 React className 类名
  "core-js": "3", // 现代JS语法polyfills库,用于兼容浏览器及ES6、ES7语法
  "dayjs": "^1.10.6", // 日期处理库,比moment小很多,只有几kb
  "history": "^5.0.1", // H5 history库
  "lodash": "^4.17.21", // 非常全面的方法库
  "react": "^17.0.2", // 懂得都懂
  "react-dom": "^17.0.2", // 懂得都懂
  "react-helmet-async": "^1.0.9", // 在react中优雅的添加 HTML header 各种属性
  "react-router-dom": "^5.2.0" // react 路由库,经典 react 三件套
}

devDependencies

"devDependencies": {
  // babel
  "@babel/cli": "^7.14.8", // 可以让babel以cli的方式执行  如:babel src --out-dir dist --watch
  "@babel/core": "^7.15.0", // babel 核心包
  "@babel/plugin-proposal-class-properties": "^7.14.5", // @babel/preset-env 插件已包含
  "@babel/plugin-syntax-dynamic-import": "^7.8.3", // 动态导入、懒加载
  "@babel/plugin-transform-modules-commonjs": "^7.15.0", // 转化成CommonJS 规范的代码
  "@babel/plugin-transform-react-constant-elements": "^7.14.5", // React 常量元素转换器 : 它会寻找不随 props 改变的所有静态元素,并将它们从渲染方法(或者无状态函数式组件)中抽离出来,以避免多余地调用 createElement
  "@babel/plugin-transform-react-inline-elements": "^7.14.5", // React 行内元素转换器 : 它会将所有 JSX 声明(或 者 createElement 调用)替换成优化过的版本,以便代码可以更快执行
  "@babel/plugin-transform-runtime": "^7.15.0", // 抽离提取 Babel的注入代码,防止重复加载,减小体积
  "@babel/preset-env": "^7.15.0", // 提供的预设,允许我们使用最新的JavaScript
  "@babel/preset-react": "^7.14.5", // react 支持
  "@babel/preset-typescript": "^7.15.0", // typescript 支持
  "babel-plugin-dynamic-import-node": "^2.3.3", // 为node提供加载转换 import => require
  "babel-plugin-import": "^1.13.3", // 按需引入、加载
  "babel-plugin-lodash": "^3.3.4", // 按需加载
  "babel-plugin-transform-react-remove-prop-types": "^0.4.24", // 从生产生成中删除不必要的类型

  // commitlint 是 git commit 执行规则相关插件
  "@commitlint/cli": "^13.1.0", 
  "@commitlint/config-conventional": "^13.1.0",

  // react hot 配合webpack实现热更新插件
  "react-refresh": "^0.10.0",
  "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",

  // @types 开头的是对应包的 TypeScript 类型声明
  "@types/enzyme": "^3.10.9",
  "@types/enzyme-adapter-react-16": "^1.0.6",
  "@types/enzyme-to-json": "^1.5.4",
  "@types/react-dom": "^17.0.9",
  "@types/react-helmet": "^6.1.2",
  "@types/react-router-dom": "^5.1.8",
  "@types/webpack-env": "^1.16.2",

  // enzyme 测试库
  "enzyme": "^3.11.0",
  "enzyme-adapter-react-16": "^1.15.6",
  "enzyme-to-json": "^3.6.2",

  // eslint
  "eslint": "^7.32.0",
  "eslint-config-airbnb-typescript": "^12.3.1", // airbnb 规范
  "eslint-config-prettier": "^8.3.0", // 关闭所有不必要或可能与[Prettier]冲突的规则。
  "eslint-import-resolver-typescript": "^2.4.0", // 添加 ts 语法支持  eslint-plugin-import
  "eslint-import-resolver-webpack": "^0.13.1", // 支持 eslint-plugin-import 读写模块解析
  "eslint-plugin-import": "^2.23.4", // ES6+  import/export 语法支持
  "eslint-plugin-jsx-a11y": "^6.4.1", // JSX元素的可访问性规则的静态AST检查器 
  "eslint-plugin-prettier": "^3.4.0", // 以 eslint的规则运行 prettier 格式化
  "eslint-plugin-react": "^7.24.0", // react 相关规则
  "eslint-plugin-react-hooks": "^4.2.0", // react-hooks 相关规则
  "eslint-plugin-redux-saga": "^1.2.1", // redux-saga 相关规则
  "@typescript-eslint/eslint-plugin": "^4.29.0", // 使 eslint 支持 typescript,.eslintrc.js 的 plugins 参数
  "@typescript-eslint/parser": "^4.29.0", // 使 eslint 支持 typescript ,.eslintrc.js 的 parser 参数

  // webpack
  "webpack": "^5.49.0",
  "webpack-bundle-analyzer": "^4.4.2", // 包依赖分析 可视化
  "webpack-cli": "^4.7.2", // cli
  "webpack-dev-middleware": "^5.0.0", // 中间件,可配合 express以服务的方式开发使用
  "webpack-dev-server": "^3.11.2", // dev-server
  "webpack-hot-middleware": "^2.25.0", // 热加载
  "webpack-pwa-manifest": "^4.3.0", // 生成 pwa 相关配置

  // webpack loader:解析对应文件
  "babel-loader": "^8.2.2",
  "style-loader": "^3.2.1", // 添加 css 到 HTML
  "css-loader": "^6.2.0", // css加载器 处理 @import/url()
  "postcss-loader": "^6.1.1", // 处理 css
  "less-loader": "^10.0.1", // less => css
  "file-loader": "^6.2.0", // 通过 import/require() 加载的图片等解析为 url
  "html-loader": "^2.1.2", // 压缩HTML
  "svg-url-loader": "^7.1.1",
  "url-loader": "^4.1.1",

  // webpack plugin
  "html-webpack-plugin": "^5.3.2", // 简化HTML文件的创建 ,配合webpack包含hash的bundle使用
  "mini-css-extract-plugin": "^2.2.0", // css 压缩
  "terser-webpack-plugin": "^5.1.4", // 使用 terser 压缩 js (terser 是一个管理和压缩 ES6+ 的工具)
  "clean-webpack-plugin": "^4.0.0-alpha.0", // 用于删除/清理生成的 build 文件 
  "compression-webpack-plugin": "^8.0.1", // Gzip压缩

  // prettier 格式化
  "prettier": "^2.3.2",
  "pretty-quick": "^3.1.1", // 在更改的文件上运行 prettier

  // stylelint css样式规范
  "stylelint": "^13.13.1",
  "stylelint-config-recess-order": "^2.4.0", // 按照session和Bootstrap的方式对CSS属性进行排序。
  "stylelint-config-standard": "^22.0.0", // 基本规范

  // 工具
  "husky": "^7.0.1", // 自动配置 Git hooks 钩子
  "lint-staged": "^11.1.2", // 对暂存的git文件运行linter
  "rimraf": "^3.0.2", // 删除cli,兼容不同平台 
  "yargs": "^17.1.0", // 读取命令参数

  // 其他
  "typescript": "^4.3.5",
  "less": "^4.1.1", // less 的解析库
  "postcss": "^8.3.6", // 专门处理样式的工具
  "postcss-nested": "^5.0.6", // 解析处理嵌套规则
  "autoprefixer": "^10.3.1", // 自动生成各浏览器前缀 postcss 的一个插件
  "serve": "^12.0.0", // 本地启动一个服务,可以查看静态文件
}

目录规划

├── dist                                // 默认的 build 输出目录
├── .husky                              // pre-commit hook
├── config                              // 全局配置文件及webpack配置文件
├── public                              // 静态文件
├── test                                // 测试目录
└── src                                 // 源码目录
    ├── assets                          // 公共的文件(如image、css、font等)
    ├── components                      // 项目组件
    ├── constants                       // 常量/接口地址等
    ├── layout                          // 全局布局
    ├── routes                          // 路由
    ├── store                           // 状态管理器
    ├── utils                           // 工具库
    ├── pages                           // 页面模块
        ├── Home                        // Home模块,建议组件统一大写开头
        ├── ...
    ├── App.tsx                         // react顶层文件
    ├── main.ts                         // 项目入口文件
    ├── typing.d.ts                     // ts类型文件
├── .editorconfig                       // IDE格式规范
├── .eslintignore                       // eslint忽略
├── .eslintrc                           // eslint配置文件
├── .gitignore                          // git忽略
├── .npmrc                              // npm配置文件
├── .prettierignore                     // prettierc忽略
├── .prettierrc                         // prettierc配置文件
├── .stylelintignore                    // stylelint忽略
├── .stylelintrc                        // stylelint配置文件
├── babel.config.js                     // babel配置文件
├── commitlint.config.js                // commit配置文件
├── LICENSE.md                          // LICENSE
├── package.json                        // package
├── postcss.config.js                   // postcss
├── README.md                           // README
├── setupEnzyme.ts                      // Enzyme测试配置文件
├── tsconfig.json                       // typescript配置文件

使用 npx create-react-app xxx --typescript 可以快速创建 TS 项目,我们可以基于创建完的项目再进行一些自定义配置。

ES6+、JSX 、TypeScript 支持

这里我们使用 babel 处理,当然对于 TypeScript 也可以使用 ts-loader 解析,这里就不做演示了,网上也有很多对比测试

JavaScript 语法特性支持

  • 首先安装 babel 相关依赖: yarn add @babel/preset-env -D
  • 然后在 .babelrc.js 中添加预设
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        // 这里可以配置相关浏览器兼容规则,暂时我们先不管
        // corejs: 3, 
        // debug: true,
        // useBuiltIns: 'usage', // 开启浏览器兼容 polyfills,会根据browserslist配置,引入需要的库,需要安装对应版本的 core-js@3
      }
    ]
  ]
}

React的JSX语法支持

  • 安装依赖:yarn add @babel/preset-react -D
  • 然后在 .babelrc.js 中添加预设
module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react']
}

TypeScript 支持

  • 安装依赖:yarn add typescript @babel/preset-typescript -D
  • 然后在 .babelrc.js 中添加预设
module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript']
}
  • 接下来在 webpack 中配置 loader
module.exports = {
  module: {
    rules: [
      // .ts .tsx
      {
        test: /\.(ts|tsx)$/,
        use: 'babel-loader',
        exclude: /node_modules/,
      },
    ]
  }
}

Antd按需加载、主题定制

antd作为react的好基友,在国内后台系统开发中几乎快成为标配了,我们以antd为例配置按需加载和主题定制化

babel配置按需加载

  • 安装antd:yarn add antd
  • 安装依赖:yarn add babel-plugin-import -D
  • 然后在 .babelrc.js 中配置plugins
module.exports = {
  presets: [],
  plugins: [
    [
      'import',
      {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: true, // or 'css'
      },
      'antd',
    ]
  ]
}

webpack/loader配置antd主题

css-loader、style-loader 也是必须的

module.exports = {
  module: {
    rules: [
      // 处理 .css
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      // 处理 .less
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          // less-loader
          {
            loader: 'less-loader',
            options: {
              lessOptions: {
                // 替换antd的变量,去掉 @ 符号即可
                // https://ant.design/docs/react/customize-theme-cn
                modifyVars: {
                  'primary-color': '#1DA57A',
                },
                javascriptEnabled: true, // 支持js
              },
            },
          },
        ],
      },
    ]
  }
}

工程化规范配置

Eslint代码检查

  • 安装ts解析器:yarn add @typescript-eslint/parser -D

  • 安装相关拓展:yarn add eslint-config-airbnb-typescript eslint-plugin-prettier -D

  • 安装相关插件:yarn add eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y @typescript-eslint/eslint-plugin -D

  • 添加 .eslintrc.js

module.exports = {
  parser: '@typescript-eslint/parser',
  // 使用 airbnb 拓展插件规范相关库
  // prettier 已内置了许多相关插件
  extends: ['airbnb-typescript', 'prettier'],
  // 拓展和支持相关能力的插件库
  plugins: ['prettier', 'react', 'react-hooks', 'jsx-a11y', '@typescript-eslint'],
}
  • ES6+规范 import/export 导入导出配置:yarn add eslint eslint-plugin-import eslint-import-resolver-typescript eslint-import-resolver-webpack -D
module.exports = {
  parser: '@typescript-eslint/parser',
  // 使用 airbnb 拓展插件规范相关库
  // prettier 已内置了许多相关插件
  extends: ['airbnb-typescript', 'prettier'],
  // 拓展和支持相关能力的插件库
  plugins: ['prettier', 'react', 'react-hooks', 'jsx-a11y', '@typescript-eslint'],
  settings: {
    'import/parsers': {
      '@typescript-eslint/parser': ['.ts', '.tsx'],
    },
    'import/resolver': {
      webpack: {
        config: './config/webpack.base.js',
      },
      typescript: {
        alwaysTryTypes: true, // always try to resolve types under `<root>@types` directory even it doesn't contain any source code, like `@types/unist`
        directory: './tsconfig.json',
      },
    },
    'import/ignore': ['types'], // Weirdly eslint cannot resolve exports in types folder (try removing this later)
  }
}

Prettier 代码格式化

  • Eslint配置中我们已经添加prettier插件,支持以eslint的规范进行格式化,并提供保存时存在冲突时的解决方案
  • 添加 .prettierrc,这里再定义下格式化基本风格

yarn add prettier -D

{
  "printWidth": 100, 
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "arrowParens": "avoid",
  "endOfLine": "auto"
}

Stylelint 样式规范

  • 安装依赖:yarn add stylelint stylelint-config-recess-order stylelint-config-standard -D
  • 添加.stylelintrc.js
module.exports = {
  extends: ['stylelint-config-standard', 'stylelint-config-recess-order'],
};

针对以上代码规范检查,我们也应该添加对应 ignore 文件以忽略非必要的检查文件,以优化性能

如:.eslintignore

node_modules
dist

Commitlint 代码提交规范

git hook 钩子的实现,这里我们使用 husky + lint-staged 方案

  • 安装依赖:yarn add husky lint-staged -D

git commit 规范,这里我们使用 @commitlint/config-conventional

  • 安装依赖:yarn add "@commitlint/cli @commitlint/config-conventional -D

这里也可以使用pretty-quick代替prettier格式化:yarn add pretty-quick -D,使得prettier格式化更简单易用

  • 根目录下创建 .husky 文件夹与commitlint.config.js文件,并在.husky 文件夹下添加 precommitcommit-msg、以及.gitignore

commitlint.config.js 用于配置 commitlint 相关规范

// git commit 规范
// <类型>[可选的作用域]: <描述>

// # 主要type
// feat:     增加新功能
// fix:      修复bug

// # 特殊type
// docs:     只改动了文档相关的内容
// style:    不影响代码含义的改动,例如去掉空格、改变缩进、增删分号
// build:    构造工具的或者外部依赖的改动,例如webpack,npm
// refactor: 代码重构时使用
// revert:   执行git revert打印的message

// # 暂不使用type
// test:     添加测试或者修改现有测试
// perf:     提高性能的改动
// ci:       与CI(持续集成服务)有关的改动
// chore:    不修改src或者test的其余修改,例如构建过程或辅助工具的变动

module.exports = {
  extends: ['@commitlint/config-conventional'],
  // rules: [] // 可以自定义规则
};

precommit git提供的钩子函数,在该阶段执行eslint等验证规范,再根据执行结果判断是否提交

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

# npx --no-install pretty-quick --staged
npx --no-install lint-staged

commit-msg 验证 git commit -m "xx" 提交的 message 信息格式和规范

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

npx --no-install commitlint --edit $1

.gitignore 作用是为了忽略 husky安装产生的非项目代码文件

_

package.json配置

"scripts": {
  // 这里换成 postinstall 也可以,npm提供的一个生命周期钩子
  // 主要是为了我们在安装完 husky依赖后可以执行husky脚本,使git hook钩子生效
  "prepare": "husky install", 
  "commitlint": "commitlint --from=master",

  // lint 相关规则和规范执行命令
  "lint:eslint:fix": "eslint --ext .ts,.tsx --no-error-on-unmatched-pattern --quiet --fix",
  "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
  "lint": "npm run lint:eslint:fix && npm run lint:style",

  // prettier 格式化的一个拓展库
  "pretty-quick": "pretty-quick",
},
"lint-staged": {
  "*.{ts,tsx}": [
    "npm run lint:eslint:fix",
    "git add --force"
  ],
  "*.{md,json}": [
    "pretty-quick --staged",
    "git add --force"
  ]
}

vscode 配置

  • 安装 eslintprettier 拓展插件
  • 添加 .vscode/settings.json 配置文件,或者全局设置 vscode settings.json
// 保存时格式化
"editor.formatOnSave": true, 
// 相当于是 vscode 保存时调用的钩子事件
"editor.codeActionsOnSave": {
  // 保存时使用 ESLint 修复可修复错误
  "source.fixAll.eslint": true
}
// 针对对应文件设置默认格式化插件
"[javascript]": {
  "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
  "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
  "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
  "editor.defaultFormatter": "esbenp.prettier-vscode"
},

HMR 热更新配置

这里我们使用 react-refresh+webpack 方案

  • 安装依赖:yarn add react-refresh @pmmmwh/react-refresh-webpack-plugin -D
  • 添加webpack配置
const { HotModuleReplacementPlugin } = require('webpack');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

module.exports = {
  devServer: {
    port: 3010, // 端口
    publicPath: '/', // 基础路径
    open: true, // 打开页面
    hot: true, // 开启热更新
    historyApiFallback: true, // 把404响应定位到 index.html
    // proxy:{} // 开发环境,接口代理
  },
  plugins: [new HotModuleReplacementPlugin(), new ReactRefreshWebpackPlugin()]
}

生产环境打包压缩、chunk拆分

  • 安装css压缩插件:yarn add mini-css-extract-plugin css-minimizer-webpack-plugin -D
  • 安装js压缩插件:yarn add terser-webpack-plugin -D
  • 安装gzip压缩插件:yarn add compression-webpack-plugin -D
  • 如果想查看对包模块的可视化分析可使用插件:webpack-bundle-analyzer

css压缩的webpack配置:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// 支持动态注入 link 标签到 html中
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  module: {
    rules: [
      // .css
      {
        test: /\.css$/,
        use: [
          // 该插件是为了把 CSS提取到单独的文件中
          MiniCssExtractPlugin.loader, 
          'css-loader'
        ],
      },
      // less
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader'
        ],
      },
    ]
  },
  plugins: [
    // 简化 HTML 文件的创建
    new HtmlWebpackPlugin({
      template: 'public/index.html',
    }),

    // MiniCss
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],

  optimization: {
    minimizer: [
      // 压缩
      new CssMinimizerPlugin(),
    ]
  }
}

js压缩的webpack配置:

webpack5.x内置有terser-webpack-plugin,不过如果想要自定义配置还是需要安装的

// 使用 terser 压缩 JavaScript
const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
  optimization: {
    minimize: true, // 开启压缩
    minimizer: [
      // 1. 简单使用默认配置
      new TerserPlugin() // 使用插件
      // 2. 自定义配置一些参数
      new TerserPlugin({
        terserOptions: {
          output: {
            comments: false, // 在输出中省略注释
          },
          // ie8: true 开启 ie8 支持
        },
      }),
    ],
  },
}

chunk拆分

这里的可玩性还是比较高的,各种性能优化啥的,有兴趣的朋友可以研究研究,我们这里只做简单使用配置👀~

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all', // 处理哪种chunk,可选值为:all、async、initial(all包括异步和非异步)
      maxInitialRequests: 10, // 入口点的最大并行请求数。
      minSize: 0, // 生成 chunk 的最小体积(以 bytes 为单位)
      // 缓存组,该组内配置可继承、覆盖splitChunks中的配置
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/, // [\\/] 是为了分别匹配 win、Unix系统路径
          // 拆分 vendor,自定义name(vendor默认是合并为一个 chunk)
          name(module) {
            // 把 npm 库拆分独立打包
            const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
            return `npm.${packageName.replace('@', '')}`;
          },
        },
      },
    }
  },
}

Gzip压缩

const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  plugins: [
    new CompressionPlugin({
      algorithm: 'gzip', // 压缩算法
      test: /\.js$|\.css$|\.html$/,
      threshold: 10240, // 只压缩大于 10240 bytes 的chunk
      minRatio: 0.8, // 只压缩大于该值的 minRatio = Compressed Size / Original Size
    })
  ]
}

【图 1】chunk拆分、gzip压缩后的效果

压缩产物

【图 2】Analyzer可视化分析

Analyzer分析

总结

本文着重描述了项目的工程化配置,涉及相关代码规范、开发环境搭建、生产环境优化等,旨在打造出一个可快速使用的现代化webpack+react+typescript模板,以供在以后的实际业务场景需求中零成本使用。

目前这只算是最简陋的一个demo,对webpack相关的各种调优与项目优化还有很多方向可研究。

接下来就是搞实际场景的业务组件的开发、第三方开源组件的集成使用、通用组件的使用和开发封装了~

项目地址:https://github.com/JS-banana/webpack-react-ts

告辞

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容