webPack从零搭建react脚手架

2,639 阅读12分钟

1、文件初始化

1、安装依赖

npm install webpack webpack-cli -D  

2、生成package.json文件

npm init -y
{
 "name": "react-demo",
 "version": "1.0.0",
 "description": "webpack脚手架exercise",
 "main": "index.js",
 "scripts": {
  	"test": "echo \"Error: no test specified\" && exit 1"
 },
 "keywords": [ "webpack" ],
 "author": "suanjiaxing <13722807589@163.com>",
 "license": "ISC"
}

3、package.json 脚本配置

"scripts": {
    "build": "webpack --mode production"
},

5、根目录新建 src 目录,用于存放源文件,新建/src/index.js,内容任意

console.log("试运行打包")

6、试运行打包

npm run build 后会生成一个 dist 目录,里面是打包后的文件

2、config文件配置

1、根目录新建 config 文件夹

|- config (打包配置目录)
 |- PATH.js -- 存储环境变量
 |- webpack.common.js -- webpack打包基础配置文件
 |- webpack.dev.js    -- webpack 开发环境打包配置文件
 |- webpack.prod.js   -- webpack 生产环境 build打包配置文件

2、PATH.js

/*
 * @Descripttion: 存储环境变量
 * @version: webpack - excrise
 * @Author: 孙佳星
 * @LastEditors: 孙佳星
 * @Date: 2020-08-13 16:04:54
 * @LastEditTime: 2020-08-13 16:24:36
*/
const path = require('path');

module.exports = {
 dist: path.resolve(__dirname, '../dist'),
 src: path.resolve(__dirname, '../src'),
 public: path.resolve(__dirname, '../public'),
}; 

2、webpack.common.js

/*
 * @Descripttion: 公共 config 抽离
 * @version: webpack - excrise
 * @Author: 孙佳星
 * @LastEditors: 孙佳星
 * @Date: 2020-08-13 15:50:33
 * @LastEditTime: 2020-08-20 19:35:54
*/
const path = require('path');
const PATHS = require("./PATHS")
const webpack = require('webpack');

module.exports = {
  /* 入口文件 */
  entry: './src/index.js',    	

  /* 出口文件 */
  output: {
          path: path.resolve(__dirname,PATHS["dist"]),
          filename: '[name].[hash:6].js',
  },
  module: {
       rules: []
  },
  plugins: []
}

3、web pack.dev.js

/*
 * @Descripttion: 开发环境
 * @version: webpack - excrise
 * @Author: 孙佳星
 * @LastEditors: 孙佳星
 * @Date: 2020-08-13 15:50:10
 * @LastEditTime: 2020-08-20 19:36:20
*/
const path = require('path');
const PATHS = require("./PATHS")
const webpack = require('webpack');

module.exports = {
  /* 模式 */
  mode: 'development',
 /* sorce-map */
  devtool : 'cheap-module-eval-source-map',
  /* 插件 */
  plugins: []
}

4、web pack.pro.js

/*
 * @Descripttion: 生产环境
 * @version: webpack - excrise
 * @Author: 孙佳星
 * @LastEditors: 孙佳星
 * @Date: 2020-08-13 15:50:10
 * @LastEditTime: 2020-08-20 19:36:12
*/
const path = require('path');
const PATHS = require("./PATHS")
const webpack = require('webpack');

module.exports = {
  /* 生产模式 */
  mode: 'production',

  /* models */
  module: {},

  /* 插件 */
  plugins: []
}

5、package.json 脚本配置

"scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
     "dev": "webpack --mode development --config  config/webpack.dev.js",
     "build": "webpack --mode production --config  config/webpack.pro.js"
},

3、HTML 模板

1、安装依赖

npm install html-webpack-plugin -D

2、HTML 模板

|- public -- 静态文件
 	|- index.html -- 模板文件
|- src 
	|- index.js -- 入口文件
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
 </head>
 <body>
    <div id="root"></div>
 </body>
</html>

3、webpack.common.js

const path = require('path');
const PATHS = require("./PATHS")
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
/* 入口文件 */
entry: './src/index.js',    	

/* 出口文件 */
output: {
   path: path.resolve(__dirname,PATHS["dist"]),
   filename: '[name].[hash:6].js',
},

/* 规则*/
module: {
 	rules: []
},

/* 依赖 */
plugins: [
   new HtmlWebpackPlugin({
       title: 'webpack - excrise', 
       template: path.resolve( PATHS["public"], 'index.html'),
       filename: path.resolve( PATHS["dist"], 'index.html'),
       hash: true,
       minify: {
           removeAttributeQuotes: true,  // 去除多余引号
           collapseWhitespace:true,      // 移除空格
           removeComments:true           // 移除注释
       }
   }),
]
}

4、config 合并

1、安装依赖

npm install --save-dev webpack-merge

2、webpack.dev.js

const {merge} = require('webpack-merge');           // 导入 merge 方法
const commonConfig = require('./webpack.common')    // 导入公共config

const devConfig = {
    mode: 'development',
    plugins: []
}
module.exports = merge(commonConfig, devConfig) 

3、webpack.pro.js

const {merge} = require('webpack-merge');
const commonConfig = require('./webpack.common')

const proConfig = {
    mode: 'production',
    module: {},
    plugins: []
}
module.exports = merge(commonConfig, proConfig)

4、试运行打包

dist 文件夹下出现 index.html

5、构建本地服务

1、安装依赖

npm install webpack-dev-server --save-dev

2、webpack.dev.js

const path = require('path');
const PATHS = require("./PATHS.js")
const webpack = require('webpack')

const devServer = {
   contentBase: path.resolve(PATHS.dist),   //本地服务器所加载的页面所在的目录
   compress:true,             // GZip压缩
   historyApiFallback: true,  //不跳转
   host: '127.0.0.1', 
   port: 8080,
   inline: true, //实时刷新
   open: true,   //自动打开浏览器
   hot:true      //开启热更新
};

const devConfig = {
   /* 模式 */
   mode: 'development',
   /* 本地服务 */
   devServer,
   /* sorce-map */
   devtool : 'cheap-module-eval-source-map',
   /* 插件 */
   plugins: [
      new webpack.HotModuleReplacementPlugin()	// 热更新
   ]
}
module.exports = merge(commonConfig, devConfig) 

3、package.json

"scripts": {
    "dev": "webpack-dev-server --mode development --config  config/webpack.dev.js",
},

4、npm run dev 试运行

6、babel支持

1、安装依赖

npm install --save-dev babel-loader @babel/core
npm install --save-dev @babel/preset-env
npm install --save @babel/polyfill
  • babel-loader: 转义 js 文件代码的 loader
  • @babel/core:babel 核心库
  • @babel/preset-env :转为 es5 代码
  • @babel/polyfill : 兼容新的 API。 Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API ,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象

2、webpack.common.js

// 入口文件
/*
 * entry: {
 *    main: ['@babel/polyfill', path.resolve(PATHS.src, 'index.js')]
 * },
*/  
entry:"./src/index.js",
// 规则
module: {
  rules: [
     {
         test: /\.m?js$/,
         exclude: /(node_modules|bower_components)/,
         use: { loader: 'babel-loader' } // options 在 .babelrc 定义
     }
  ]
},
  • entry: {main: ['@babel/polyfill', path.resolve(PATHS.src, 'index.js')]}, 这样配置的话,打包以后代码体积会变得很大。
  • "useBuiltIns": "usage":按需加载,只会对我们index.js当前要打包的文件中使用过的语法,比如Promise,map做polyfill,其他es6未出现的语法,我们暂时不去做polyfill,减小打包体积

3、根目录建 .babelrc 文件

{
 "presets": [["@babel/preset-env", { "useBuiltIns": "usage" }]]
}

4、入口文件修改

const func = () => {
 console.log('hello webpack')
}
func()

new Promise(resolve => console.log('promise'))

class User {
 constructor() {
   console.log('new User')
 }
}

5、试运行

npm run dev / npm run build 试运行 & 打包

7、babel/polyfill 优化

1、安装依赖

npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
npm install --save @babel/runtime-corejs2
  • babel-polyfill 会污染全局作用域, 如引入 Array.prototype.includes 修改了 Array 的原型
  • babel-polyfill 引入新的对象: PromiseWeakMap
  • @babel/runtime-corejs2:@babel/runtime-corejs3 打出来的包比 2 大

( 1 ) @babel/runtime的作用

提取辅助函数。ES6 转码时,babel 会需要一些辅助函数,例如 _extend。babel 默认会将这些辅助函数

内联到每一个 js 文件里, babel 提供了 transform-runtime 来将这些辅助函数“搬”到一个单独的模块 babel-runtime 中,这样做能减小项目文件的大小。

提供 polyfill:不会污染全局作用域,但是不支持实例方法如 Array.includes

(2) @transform-runtime 的作用:

babel-runtime 更像是分散的 polyfill 模块,需要在各自的模块里单独引入,借助 transform-runtime 插件

来自动化处理这一切,也就是说你不要在文件开头 import 相关的 polyfill,你只需使用,transform-runtime 会帮你引入

2、.babelrc

{
  "presets": ["@babel/preset-env"],
  "plugins": [["@babel/plugin-transform-runtime", { "corejs": 2 }]]
}

3、试运行

npm run dev / npm run build 试运行 & 打包

8、装饰器模式

1、安装依赖

npm install --save @babel/plugin-proposal-decorators

2、.babelrc

{
 "presets": [["@babel/preset-env", {"corejs": "2", "useBuiltIns": "usage" }]],
 "plugins": [
     ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }],
     ["@babel/plugin-transform-runtime", { "corejs": 2 }]
  ]
}

3、入口文件修改

const func = () => {
 console.log('hello webpack')
}
func()

new Promise(resolve => console.log('promise'))

function annotation(target) {
 target.annotated = true
}

@annotation
class User {
 constructor() {
   console.log('new User')
 }
}
const p1 = new User()

9、删除上次打包好的文件

1、安装依赖

npm install clean-webpack-plugin -D

2、webpack.pro.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
	plugins: [
		new CleanWebpackPlugin(),
	],
}

10、配置 React

1、安装依赖

npm install --save react react-dom
npm install --save-dev @babel/preset-react

  • babel-preset-react: jsx 转换成js

2、.babelrc

{
 "presets": [ 
     [
         "@babel/preset-env", 
         { 
             "corejs": "2",
             "useBuiltIns": "usage"
         },
         "react"
     ],
     "@babel/preset-react"
 ],
 "plugins": [
     ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }],
     ["@babel/plugin-transform-runtime", { "corejs": 2 }]
 ]
}

3、新建App.js

|- src 
	|- index.js -- 入口文件
	|- App.js 

import React from 'react';

const App = () => <div>佳星加油!!!</div>

export default App;

4、修改入口文件

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
 <App/>,
 document.getElementById('root')
);

11、React 热替换

1、安装依赖

npm i -D react-hot-loader

  • webpack-dev-server的热加载是开发人员修改了代码,代码经过打包,重新刷新了整个页面
  • 热react-hot-loader不会刷新整个页面,它只会替换修改了的代码,做到页面的局部刷新。
  • react-hot-loader需要依赖webpack的hotModuleReplacementPlugin热加载插件。

2、babelrc

{
"presets": [ 
  [
      "@babel/preset-env", 
      { 
          "corejs": "2",
          "useBuiltIns": "usage"
      },
      "react"
  ],
  "@babel/preset-react"
],
"plugins": [
      ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }],
      ["@babel/plugin-transform-runtime", { "corejs": 2 }],
      "react-hot-loader/babel"   //  React 模块热替换
  ]
}

3、App.js

import React from 'react';
import { hot } from 'react-hot-loader/root';

const App = () => <div>佳星加油!!!</div>

export default hot(App);

12、eslint 检查

1、安装依赖

npm install --save-dev eslint eslint-loader 
npm install --save-dev babel-eslint eslint-plugin-react  
npm install --save-dev eslint-plugin-import
npm install pre-commit --save-dev

  • eslint-plugin-import: 校验es6的import规则
  • pre-commit :提交前检查是否有格式错误

2、webpack.common.js

{
 test: /\.m?js$/,
 exclude: /node_modules/,
 enforce:true,       // 优先执行
 loader:'eslint-loader',
 options:{
      fix: true     // 自动修复
 }
},

3、package.json

"scripts": {	// npm run lint 格式化文件
 "lint": "eslint --ext .js --ext .jsx src"     
}
"pre-commit": [ // git 提交代码前进行格式检查
 "lint"
]

k

4、新增.eslint.js 文件

{
"env": {
 "browser": true,
 "commonjs": true,
 "es6": true
},
"parser": "babel-eslint",
"extends": [
 "eslint:recommended",
 "plugin:react/recommended"
],
"parserOptions": {
 "ecmaVersion": 7,
 // 开启实验属性
 "ecmaFeatures": {
   "experimentalObjectRestSpread": true,
   // 修饰器
   "experimentalDecorators": true,
   "jsx": true
 },
 "sourceType": "module"
},
"plugins": [
 "react"
],
"globals": {
 "__DEV__": false,
 "__dirname": false,
 "window": true,
 "define": true,
 "history": true,
 "location": true,
 "wxjs": true,
 "$": true,
 "WeixinJSBridge": true,
 "wx": true,
 "process": true,
 "qq": true
},
"settings": {
 "react": {
   "version": "16.2.0"
 }
},
"rules": {
 // 禁止空函数
 "no-empty-function": ["error"],
 // 禁止 function 定义中出现重名参数
 "no-dupe-args": 2,
 // 禁止对象字面量中出现重复的 key
 "no-dupe-keys": 2,
 // 禁止重复的 case 标签
 "no-duplicate-case": 2,
 // 禁止出现空代码块,允许 catch 为空代码块
 "no-empty": ["error",{"allowEmptyCatch": true}],
 // 禁止对 catch 子句的参数重新赋值
 "no-ex-assign": 2,
 // 禁止不必要的布尔转换
 "no-extra-boolean-cast": 2,
 // 禁止不必要的括号 //(a * b) + c;//报错
 "no-extra-parens": 0,
 // 禁止在 if 代码块内出现函数声明
 "no-inner-declarations": ["error","both"],
 // 禁止在普通字符串中出现模版字符串里的变量形式,如 "Hello ${name}!"
 "no-template-curly-in-string": "error",
 // 禁止在 return, throw, break 或 continue 之后还有代码
 "no-unreachable": "error",
 // 不允许标签与变量同名
 "no-label-var": 2,
 // 禁止 var 声明 与外层作用域的变量同名
 "no-shadow": 0,
 // 禁止覆盖受限制的标识符
 "no-shadow-restricted-names": 2,
 // 禁止将变量初始化为 undefined
 "no-undef-init": 2,
 // 禁止将 undefined 作为标识符
 "no-undefined": 0,
 // 不允许在变量定义之前使用它们
 "no-use-before-define": 0,
 /***************
 // 风格指南 //
 ***************/
 // 一致缩进4个空格,switch 语句中的 case 子句缩进级别为1,即两个空格
 "indent": ["error", 4, {"SwitchCase": 1}], 
 // 单行最大长度
 "max-len": ["error", {"code": 140, "comments": 80}],
 // 多行注释风格
 "multiline-comment-style": ["error", "starred-block"],
 // 强制在对象字面量的属性中键和值之间使用一致的间距
 "key-spacing": [2, {"beforeColon": false, "afterColon": true}],
 // 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
 "lines-around-comment": [2, {"beforeBlockComment": true}],
 // 指定数组的元素之间要以空格隔开
 "array-bracket-spacing": [2, "never"],
 // 禁止或强制在单行代码块中使用空格(禁用)
 "block-spacing": [1, "never"],
 //强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
 "brace-style": [2, "1tbs", {"allowSingleLine": true}],
 // 控制逗号前后的空格
 "comma-spacing": [2, {"before": false,"after": true}],
 // 控制逗号在行尾出现还是在行首出现 (默认行尾)
 "comma-style": [2, "last"],
 // 文件末尾强制换行
 "eol-last": 2,
 // 要求或禁止在函数标识符和其调用之间有空格
 "func-call-spacing": 2,
 // 构造函数首字母大写
 "new-cap": ["error"],
 // 强制所有控制语句使用一致的括号风格
 "curly": [2, "all"],
 // 禁止 catch 子句的参数与外层作用域中的变量同名
 "no-catch-shadow": 0,
 // 禁用特定的全局变量
 "no-restricted-globals": 2,
 // 强制在关键字前后使用一致的空格 (前后腰需要)
 "keyword-spacing": 2,
 // 禁止使用 Array 构造函数
 "no-array-constructor": 2,
 // 要求 return 语句之前有一空行
 "newline-before-return": 0,
 // 要求方法链中每个调用都有一个换行符
 "newline-per-chained-call": 1,
 // 禁用 continue 语句
 "no-continue": 0,
 // 禁用 console-log
 "no-console":0,
 //禁止空格和 tab 的混合缩进
 "no-mixed-spaces-and-tabs": [ "error", "smart-tabs"],
 // 不允许多个空行
 "no-multiple-empty-lines": [2, {"max": 2}],
 // 不允许使用嵌套的三元表达式
 "no-nested-ternary": 0,
 // 强制使用一致的反勾号、双引号或单引号
 "quotes": [2, "single", "avoid-escape"],
 // 强制分号之前和之后使用一致的空格
 "semi-spacing": 2,
 /***************
 // es6 //
 ***************/
 // 强制 generator 函数中 * 号周围使用一致的空格
 "generator-star-spacing": [2, {"before": true,"after": true}],
 // 要求generator 函数内有 yield
 "require-yield": 2,
 // 禁止修改类声明的变量
 "no-class-assign": 2,
 // 禁止类成员中出现重复的名称
 "no-dupe-class-members": 2,
 // 要求或禁止模板字符串中的嵌入表达式周围空格的使用
 "template-curly-spacing": 1,
 // 每个模块只能使用一个import
 "no-duplicate-imports": 2,
 // 禁止在构造函数中,在调用 super() 之前使用 this 或 super
 "no-this-before-super": 2,
 // 要求使用 let 或 const 而不是 var
 "no-var": 1,
 // 要求使用箭头函数作为回调
 "prefer-arrow-callback": 0,
 // 要求使用 const 声明那些声明后不再被修改的变量
 "prefer-const": 0,
 // 要求使用模板字面量而非字符串连接
 "prefer-template": 0,
 /***************
 // jsx //
 ***************/
 // 禁止使用已废弃的 api
 "react/no-deprecated": "error",  
 // 禁止未使用的变量
 "no-unused-vars": 0,
 //在JSX属性中强制或禁止等号周围的空格
 "react/jsx-equals-spacing": 2,
 "react/jsx-filename-extension": [2, {
   "extensions": [".js", ".jsx"]
 }],
 // 禁止直接修改 this.state
 "react/no-direct-mutation-state": "error", 
 // 禁止出现 HTML 中的属性,如 class
 "react/no-unknown-property": "error",
 // render 方法中必须有返回值
 "react/require-render-return": "error", 
 // 自闭和标签的反尖括号必须与尖括号的那一行对齐
 "react/jsx-closing-bracket-location": ["error",{"nonEmpty": false, "selfClosing": "line-aligned"}],
 // jsx 的 children 缩进必须为四个空格
 "react/jsx-indent": [ "error",4],
 // jsx 的 props 缩进必须为四个空格
 "react/jsx-indent-props": ["error",4],
 // 禁止出现重复的 props
 "react/jsx-no-duplicate-props": "error",
 // 禁止出现多余的分号
 "no-extra-semi": "error",
 // 禁止在数组中出现连续的逗号,如 let foo = [,,]
 "no-sparse-arrays": "error",
 // 禁止在条件语句中出现赋值操作符,除非这个赋值语句被括号包起来了
 "no-cond-assign": ["error","except-parens"],
 // 强制在 JSX 属性中一致地使用双引号或单引号
 "jsx-quotes": 0,
 //链式调用的时候,点号必须放在第二行开头处,禁止放在第一行结尾处
 "dot-location": ["error","property"],
 // 必须使用 === 或 !==,禁止使用 == 或 !=,与 null 比较时除外
 "eqeqeq": ["error", "always",{"null": "ignore"}],
 // @fixable 禁止出现没必要的 bind
 "no-extra-bind": "error",
 // @fixable 禁止出现没必要的 label
 "no-extra-label": "error",
 // switch 的 case 内必须有 break, return 或 throw
 "no-fallthrough": "error",
 // 禁止重复定义变量
 "no-redeclare": "error",
 // 禁止在 return 语句里赋值
 "no-return-assign": 0
}
}

5、VScode配置

"editor.tabSize": 4,
"editor.fontSize": 14,
// eslint 配置
"editor.codeActionsOnSave": {
  "source.fixAll": true,
  "source.fixAll.eslint": true
},
"javascript.format.insertSpaceBeforeFunctionParenthesis": true, //让函数(名)和后面的括号之间加个空格
"vetur.format.defaultFormatter.html": "js-beautify-html", //格式化.vue中html
"explorer.confirmDragAndDrop": false,
"explorer.confirmDelete": false,

下载 ESlint 插件,进行如下配置

13、CSS & Less

1、安装依赖

npm install css-loader style-loader -D
npm install less less-loader --save-dev
npm install --save-dev mini-css-extract-plugin

  • css-loader: 处理 css 文件
  • style-loader: import 导入的样式文件,打包到 js 文件中,运行 js 文件时,将样式自动插入到标签
  • mini-css-extract-plugin:import 导入的样式文件,打包成一个实际的 css 文件,结合 html-webpack-plugin,在 dist/index.html 中以 link 形式插入;默认将 js 中 import 的多个 css 文件,打包时合成一个

2、webpack.common.config

module: {
  rules: [
     {
         test: /\.css$/,
         use: ['style-loader','css-loader']
     },
  ]
}

2、webpack-dev.config

module: {
 rules: [
     {
       test: /\.less$/,
       use: ['style-loader', 'css-loader', 'less-loader'],
       exclude: /node_modules/
     }
 ]
},

3、webpack.dev.js

const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module: {
 rules: [      
     {
       test: /\.(scss|less)$/,
       use: [
         {
           loader: MiniCssExtractPlugin.loader    // CSS 单独抽离一个文件
         },
         'css-loader', 
         'less-loader'
       ],
       exclude: /node_modules/
     }
   ]
},
plugins: [
    new MiniCssExtractPlugin({
        filename: '[name].[hash:6].css'  
    })		
]

3、试运行

|- src 
	|- index.js -- 入口文件
	|- App.js 
	|- page
		|- Home
			|- index.js
			|- style.less

home/index

import React from 'react'
import "./style.less"
export default function index() {
 return (
     <div>
         你看,你多坚强啊!
         你在自己的世界里,活成了一个英雄!
         玻璃晴朗,橘子辉煌!
     </div>
 )
}

home/style.less

@color:red;
div{
 color:@color
}

App

import React from 'react';
import { hot } from 'react-hot-loader/root';
import Home from './page/Home'

const App = () => <div>
 佳星加油!!!
 <Home/>
</div>
export default hot(App);

  • npm run dev 样式生效
  • npm run build 单独生成 style 文件,单机 html 文件,样式生效

14、CSS代码压缩

1、安装依赖

npm install --save-dev optimize-css-assets-webpack-plugin    
npm install -D uglifyjs-webpack-plugin

  • optimize-css-assets-webpack-plugin:压缩 CSS 代码
  • uglifyjs-webpack-plugin:optimization.minimizer会覆盖webpack默认提供的规则,,比如不会再压缩 JS 代码,需要该插件

2、webpack.pro.js

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

const prodConfig = {
   mode: 'production',
   devtool: 'cheap-module-source-map',
   optimization: {
       minimizer: [
         new UglifyJsPlugin({
             sourceMap: true,
             parallel: true,  // 启用多线程并行运行提高编译速度
         }),
         new OptimizeCSSAssetsPlugin({}),
     ]
	}
}

15、属性或选择器前缀

1、安装依赖

npm install postcss-loader  autoprefixer -D 

  • postcss-loader:加前缀
  • autoprefixer:配合postcss-loader

2、webpack.dev.config

{
     test: /\.less$/,
     use: [
          'style-loader', 
          'css-loader',
          {
              loader: 'postcss-loader',
              options: {
                  ident: 'postcss',
                  sourceMap: true,
                  plugins: loader => [
                      require('autoprefixer') // 添加前缀
                  ]
              }
           }, 
           'less-loader'
         ],
     exclude: /node_modules/
}

3、webpack.pro.js

{
    test: /\.less$/,
    use: [
        {
            loader: MiniCssExtractPlugin.loader    // CSS 单独抽离一个文件
        },
        'css-loader', 
        {
            loader: 'postcss-loader',           // 属性或选择器添加前缀处理
            options: {
                    ident: 'postcss',
                    sourceMap: true,
                    plugins: loader => [ require('autoprefixer') ]
            }
         }, 
        'less-loader'
    ],
    exclude: /node_modules/
}

4、package.json

"browserslist": {
 "development":[
   "last 2 chrome version",
   "last 2 firefox version",
   "last 2 safari version"
 ],
 "production":[
   "> 1%",
   "last 2 versions",
   "not dead",
   "not op_mini all",
   "not ie <= 10"
]
}

16、图片loader

1、安装依赖

npm install url-loader file-loader --save-dev

  • 依赖于file-loader,把图片转换成base64嵌入html,如果超出一定阈值则交给file-loader
  • 只需安装url-loader就行,url-loader包含file-laoder

2、webpack.common.js

module: {
  rules: [      
        {
                test:/\.(png|jpg|gif|ttf|eot|woff(2)?)(\?[=a-z0-9]+)?$/,
                loader: 'url-loader',
                options:{
                    limit:8 * 1024,    // 8 kb以下转 base64
                    esModule:false,    // 关闭默认 es模块引入方式
                    outputPath: 'images',   // 将文件打包到哪里
                    publicPath: './images', 
                    name: '[hash:8].[ext]'  // .ext 文件扩展名,jpg\png
                }
         },
 ]
}

3、试运行

import React from 'react'
import img from '../../static/image/fate--saber.jpg'

export default function index() {
  return (
    <div>
      你看,你多坚强啊!
      你在自己的世界里,活成了一个英雄!
      玻璃晴朗,橘子辉煌!
      <img src={img}/>
    </div>
  )
}

|- dist 
	|- index.js -- 入口文件
	|- main.js 
	|- main.css
	|- image文件夹

juejin.cn/post/685457… React项目中webpack加载svg

17、静态资源加载

1、安装依赖

npm install copy-webpack-plugin --save-dev

2、webpack.common.js

const CopyPlugin = require('copy-webpack-plugin');

plugins: [
     new CopyPlugin({
         patterns: [
           {
             from: path.resolve(process.cwd(),'src/assets/fonts'),
             to:  path.resolve(process.cwd(),'dist/statics/fonts'),
           }
         ]
     })
 ]

|- dist 
	|- index.js -- 入口文件
	|- main.js 
	|- main.css
	|- image文件夹
	|- statics
		|- fonts  // 字体文件

3、index.html

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title><%= htmlWebpackPlugin.options.title %></title>
   <link type="text/css" rel="stylesheet" href="./statics/fonts/iconfont.css">
</head>
<body>
   <div id="root"></div>
</body>
</html>

1、oneOf

1、webpack.common.js

module: {
   rules: [
		{
         test: /\.m?js$/,
         exclude: /node_modules/,
         enforce:'pre',       // 优先执行
         loader:'eslint-loader',
         options:{
             fix: true   // 自动修复
         }
      },
      {
          oneOf:[
              {
                  test: /\.css$/,
                  use: ['style-loader','css-loader']
              },
              {
                  test: /\.m?js$/,
                  exclude: /(node_modules|bower_components)/,
                  use: { loader: 'babel-loader' } // options 在 .babelrc 定义
              },
              {
                  test:/\.(png|jpg|gif|ttf|eot|woff(2)?)(\?[=a-z0-9]+)?$/,
                  loader: 'url-loader',
                  options:{
                      limit:8 * 1024,    // 8 kb以下转 base64
                      esModule:false,    // 关闭默认 es模块引入方式
                      outputPath: 'images',   // 将文件打包到哪里
                      publicPath: './images', 
                      name: '[hash:8].[ext]'  // .ext 文件扩展名,jpg\png
                  }
              }
          ]
     }
   ]
}

2、soure-map

// 开发环境
	eval-cheap-source-map
	cheap-module-source-map
	eval-source-map
// 生产环境
	hidden-source-map

3、tree-shaking

去除无用代码

  • 使用 es6 module
  • 开启 production

package.json

"sideEffects":["*.css","*.less"]

4、缓存

1、babel缓存

{
 test: /\.m?js$/,
 exclude: /(node_modules|bower_components)/,
 use: { 
      loader: 'babel-loader' ,
      options: {
           cacheDirectory: true
      }
 }
},

2、webpack-pro.js

hash 换成 contenthash

/* 出口文件 */
output: {
 path: path.resolve(__dirname,PATHS["dist"]),
 filename: '[name].[contenthash:6].js',
},
/* 插件 */
plugins: [
 /* 删除上一次打包生成的文件 */
 new CleanWebpackPlugin(),  
 /* CSS 单独抽离为一个文件 */ 
 new MiniCssExtractPlugin({
      filename: '[name].[contenthash:6].css'  
 }),	
],

5、代码分割

1、webpack.pro.js

module.exports = {
entry: './src/index.js',    	
module: { },
plugins: [],
optimization:{
   splitChunks:{
      minSize: 0, // 默认30000(30kb),但是demo中的文件都很小,minSize设为0,让每个文件都满足大小条件
      cacheGroups: {
          vendor: {  // nodeModules 代码单独打包成一个 chunk 输出
              test: /[\\/]node_modules[\\/]/,
              priority: 10,
              chunks: "initial",
              name: "vendor",
          },
          commons: {	  // 多次import的文件打包成一个单独的common.js
              chunks: 'initial',
              minChunks: 2,
              maxInitialRequests: 5,
              name: 'common'
          }
      }
  }
}
}

6、多进程打包

1、安装依赖

npm i thread-loader -D

2、babel - loader 开启多进程

{
 test: /\.m?js$/,
 exclude: /(node_modules|bower_components)/,
     use: [
          {
                 loader:'thread-loader', // 开启多进程打包
                 options:{
                     workers:2
                 }
          },
          { 
                 loader: 'babel-loader' ,
                 options: {
                     cacheDirectory: true
                 }
          } // options 在 .babelrc 定义
    ]
},

开启多进程 600 ms,js代码非常多时,使用

8、作用域提升

module.exports = {
 mode: 'production',
	entry: './src/index.js',
	output: { ... },    
  plugins: [
      new webpack.optimize.ModuleConcatenationPlugin(),
  ]
}

1、antD 引入

1、安装依赖

npm install --save-dev babel-plugin-import antd 

  • babel-plugin-import : 按需引入antd

2、babel.rc

{
"presets": [ 
  [
      "@babel/preset-env", 
      { 
          "corejs": "2",
          "useBuiltIns": "usage"
      },
      "react"
  ],
  "@babel/preset-react"
],
"plugins": [
  ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }],
  ["@babel/plugin-transform-runtime", { "corejs": 2 }],
  ["import",{
      "libraryName": "antd",
      "libraryDirectory": "es",
      "style":"css"}
  ], // babel-plugin-import 配置
  "react-hot-loader/babel" 
]
}

3、webpack.common.js

module: {
  rules: [
     {
         test: /\.css$/,
         use: ['style-loader','css-loader']
     }
  ]
]

  • antd 样式为 CSS 样式,新增一条 CSS 规则

4、webpack.dev.js

{
 test: /\.less$/,
     use: [
         'style-loader', 
         'css-loader',
         {
             loader: 'postcss-loader',
             options: {
                 ident: 'postcss',
                 sourceMap: true,
                 plugins: loader => [
                     require('autoprefixer') // 添加前缀
                 ]
             }
         }, 
         {
             loader: "less-loader",
             options: {
                 javascriptEnabled: true
             }
         }
     ],
         exclude: /node_modules/
}

5、试运行

import React from 'react'
import { Button } from 'antd';
import img from '../../static/image/fate--saber.jpg'

export default function index() {
 return (
     <div>
         <Button type="primary">Primary Button</Button>
         你看,你多坚强啊!
         你在自己的世界里,活成了一个英雄!
         玻璃晴朗,橘子辉煌!
         <img src={img}/>
     </div>
 )
}

npm run build

npm run dev 试运行看样式是否生效

2、配置项目别名

Resolve 配置 Webpack 如何寻找模块所对应的文件

webpack.common.js

module.exports = {
 /* 入口文件 */
 entry: './src/index.js',  
 ...
 resolve: {
         extensions: ['.js', '.jsx'], // 导入语句没带文件后缀,webpack自动带上后缀文件
         alias: {
             assets: path.resolve(PATHS.src, 'assets'),
             component: path.resolve(PATHS.src, 'component'),
             page: path.resolve(PATHS.src, 'page'),
             until: path.resolve(PATHS.src, 'until'),
             server: path.resolve(PATHS.src, 'server'),
             router: path.resolve(PATHS.src, 'router'),
             store: path.resolve(PATHS.src, 'store'),
             layout: path.resolve(PATHS.src, 'layout'),
             tools: path.resolve(PATHS.src, 'tools')
         }
 }
}

3、自定义主题

1、根文件新建 themes.js

module.exports = {
 'primary-color':"#f0f",     // 全局主色
 'link-color':" #1890ff",        // 链接色
 'success-color':" #52c41a",     // 成功色     
 'warning-color':" #faad14",     // 警告色
 'error-color':" #f5222d",       // 错误色
 'font-size-base':" 14px",       // 主字号
 'heading-color':"rgba(0, 0, 0, 0.85)",  // 标题色
 'text-color':" rgba(0, 0, 0, 0.65)",    // 主文本色
 'text-color-secondary ':" rgba(0, 0, 0, .45)",  // 次文本色
 'disabled-color ':" rgba(0, 0, 0, .25)",  // 失效色
 'border-radius-base':" 4px",     // 组件/浮层圆角
 'border-color-base':" #d9d9d9",  // 边框色
 'box-shadow-base':" 0 2px 8px rgba(0, 0, 0, 0.15)",  // 浮层阴影
}

2、webpack.dev.js

const themes = require('../themes')  
...

module: {
rules: [
 {
   oneOf:[
     {
       test: /\.less$/,
       use: [
         'style-loader', 
         'css-loader', 
         {
           loader: 'postcss-loader',
           options: {
             ident: 'postcss',
             sourceMap: true,
             plugins: loader => [ require('autoprefixer') ]
           }
         }, 
         {
           loader: 'less-loader',  
           options: {
             lessOptions: {  // 如果less@5,去掉lessOptions,直接options配置
               modifyVars: themes,
               javascriptEnabled: true,
             }
           },
         }
       ],
       // exclude: /node_modules/     // node_modules/antd 不能过滤
     },
   ]
 }
]
},

3、webpack.pro.js

const themes = require('../themes')  
module: {
rules: [ 
 {
   oneOf:[
     {
       test: /\.(css|less)$/,
       use: [
         {
           loader: MiniCssExtractPlugin.loader    // CSS 单独抽离一个文件
         },
         'css-loader', 
         {
           loader: 'postcss-loader',           // 属性或选择器添加前缀处理
           options: {
             ident: 'postcss',
             sourceMap: true,
             plugins: () => [ require('autoprefixer') ]
           }
         }, 
         {
           loader: 'less-loader',           // 属性或选择器添加前缀处理
           options: {
             lessOptions: { 
               modifyVars: themes,
               javascriptEnabled: true,
             }
           }
         }
       ],
       // exclude: /node_modules/
     },
   ]
 }     

]
},

4、.babelrc / babel-plugin-import更改配置

因为使用babel-plugin-import,需要做一些更改

{
"presets": [ 
  [
      "@babel/preset-env", 
      { 
          "corejs": "2",
          "useBuiltIns": "usage"
      },
      "react"
  ],
  "@babel/preset-react"
],
"plugins": [
  ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }],
  ["@babel/plugin-transform-runtime", { "corejs": 2 }],
  ["import",{
      "libraryName": "antd",
      "libraryDirectory": "lib",
      "style":true
  }], // babel-plugin-import 配置
  "react-hot-loader/babel"  
]
}

4、配置全局变量

DefinePlugin 允许创建一个在编译时可以配置的全局常量

1、package.json

"scripts": {
 "test": "echo \"Error: no test specified\" && exit 1",
 "dev": "webpack-dev-server --mode development --config  config/webpack.dev.js --env.API=dev",
 "server:dev": "webpack-dev-server --open --config config/webpack.dev.js --env.API=server:dev",
 "build": "webpack --mode production --config  config/webpack.pro.js --env.API=build:dev",
 "build:local": "webpack --progress --config config/webpack.prod.js --env.API=build:local",
 "lint": "eslint --ext .js --ext .jsx src"
},

2、web pack.comon.js

const path = require('path');
const PATHS = require("./PATHS")
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');

// 获取环境变量
let argv = process.argv, // 启动传入的命令行参数
 envAPI = 'dev'
for(let i = 0; i < argv.length; i++) {
 let arg = argv[i];
 if(arg.startsWith('--env.API=')) {
     envAPI = arg.replace('--env.API=', '')
 }
}

module.exports = {
plugins: [
 new HtmlWebpackPlugin({
   ...
 })
 new CopyPlugin({
   ...
 }),
 new webpack.DefinePlugin({
   API_CONFIG: JSON.stringify(envAPI)
 }) // 全局变量
 ]
}

3、Home.js

import React,{ useEffect } from 'react'

export default function index () {
 useEffect( () => {
     console.log(API_CONFIG)
 },[])
 return (
     <div>
         home
     </div>
 )
}

npm run dev 本地运行,控制台输出 dev

5、前端代理配置

const devServer = {
    stats: 'errors-only',
    contentBase: path.resolve(PATHS.dist),
    historyApiFallback: true,
    host: '127.0.0.1',
    port: 80,
    hot: true,
    inline: true,
    disableHostCheck: true,
    open: 'Google Chrome',
    progress: true,
    proxy: {
      '/api/*': {
        target: 'http://skynet-eye.dev.vivo.xyz:8080',    //要跳转的域名
        pathRewrite: {
          '/api': '',
        },
        changeOrigin: true,
        secure: false,
        
      },
      '/common/*': {
        target: 'http://fapi.vmic.xyz/mock/494/',    //要跳转的域名
        pathRewrite: {
          '/common': '',
        },
        changeOrigin: true,
        secure: false,
      },
    }
  }