npm install  webpack webpack-cli --save-dev
./src/index.js,但你可以通过在 webpack configuration 中配置 entry 属性,来指定一个(或多个)不同的入口起点let title = require('./title.txt');
document.write(title.default);
const path = require('path');
module.exports = {
  entry: './src/index.js',
};
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。webpack.config.js
const path = require('path');
module.exports = {
  entry: './src/index.js',
+  output: {
+    path: path.resolve(__dirname, 'dist'),
+    filename: 'main.js'
+  }
};
JavaScript 和 JSON 文件webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中webpack.config.js
const path = require('path');
module.exports = {
  mode: 'development',
  devtool:false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
+  module: {
+    rules: [
+      { test: /\.txt$/, use: 'raw-loader' }
+    ]
+  }
};
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webpack5</title>
</head>
<body>
</body>
</html>
const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: 'development',
  devtool:false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
+  plugins: [
+    new HtmlWebpackPlugin({template: './src/index.html'})
+  ]
};
| 选项 | 描述 | 
|---|---|
| development | 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin | 
| production | 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin | 
--mode用来设置模块内的process.env.NODE_ENV--env用来设置webpack配置文件的函数参数cross-env用来设置node环境的process.env.NODE_ENVDefinePlugin用来设置模块内的全局变量productionwebpack serve的mode默认为developmentprocess.env.NODE_ENV获取当前的环境变量,无法在webpack配置文件中获取此变量  "scripts": {
    "build": "webpack",
    "start": "webpack serve"
  },
index.js
console.log(process.env.NODE_ENV);// development | production
webpack.config.js
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
"scripts": {
  "build": "webpack --mode=production",
  "start": "webpack --mode=development serve"
},
process.env.NODE_ENV访问webpack 配置文件中中通过函数获取当前环境变量"scripts": {
   "dev": "webpack serve --env=development",
   "build": "webpack --env=production",
}
index.js
 console.log(process.env.NODE_ENV);// undefined
webpack.config.js
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
module.exports = (env,argv) => {
  console.log('env',env);// development | production
};
module.exports = {
mode: 'development'
}
window),所有模块都能读取到该变量的值process.env.NODE_ENV 获取当前的环境变量node环境(webpack 配置文件中)下获取当前的环境变量plugins:[
   new webpack.DefinePlugin({
      'process.env.NODE_ENV':JSON.stringify('development'),
      'NODE_ENV':JSON.stringify('production'),
   })
]   
index.js
console.log(NODE_ENV);//  production
webpack.config.js
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// undefined
console.log('NODE_ENV',NODE_ENV);// error !!!
node环境下的变量NODE_ENVpackage.json
"scripts": {
  "build": "cross-env NODE_ENV=development webpack"
}
webpack.config.js
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// development
npm install webpack-dev-server --save-dev
| 类别 | 配置名称 | 描述 | 
|---|---|---|
| output | path | 指定输出到硬盘上的目录 | 
| output | publicPath | 表示的是打包生成的index.html文件里面引用资源的前缀 | 
| devServer | publicPath | 表示的是打包生成的静态文件所在的位置(若是devServer里面的publicPath没有设置,则会认为是output里面设置的publicPath的值) | 
| devServer | contentBase | 用于配置提供额外静态文件内容的目录 | 
module.exports = {
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    compress: true,
    port: 8080,
    open: true
  },
}
  "scripts": {
    "build": "webpack",
+   "start": "webpack serve"
  }
cnpm i style-loader css-loader -D
  {
      test:/\.css$/,
      //最后一个loader,就上面最左边的loader一定要返回一个JS脚本
      use:['style-loader',{
          loader:'css-loader',
          options:{importLoaders:1}
      },'postcss-loader']
  },
#less-container{
    transform:rotate(7deg);
}
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: 'development',
  devtool:false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' },
+     { test: /\.css$/, use: ['style-loader','css-loader'] }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};
src\bg.css
body{
    background-color: green;
}
src\index.css
@import "./bg.css";
body{
    color:red;
}
src\index.js
+import './index.css';
let title = require('./title.txt');
document.write(title.default);
npm i less less-loader -D
npm i node-sass sass-loader -D
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' },
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
+     { test: /\.less$/, use: ['style-loader','css-loader', 'less-loader'] },
+     { test: /\.scss$/, use: ['style-loader','css-loader', 'sass-loader'] }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ]
};
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webpack5</title>
</head>
<body>
+  <div id="less-container">less-container</div>
+  <div id="sass-container">sass-container</div>
</body>
</html>
src\index.js
import './index.css';
+import './less.less';
+import './sass.scss';
let title = require('./title.txt');
document.write(title.default);
src\less.less
@color:blue;
#less-container{
    color:@color;
}
src\sass.scss
$color:orange;
#sass-container{
    color:$color;
}
::placeholder可以选择一个表单元素的占位文本,它允许开发者和设计师自定义占位文本的样式。autoprefixer和browsers选项npm i postcss-loader postcss-preset-env -D
postcss.config.js
let postcssPresetEnv = require('postcss-preset-env');
module.exports={
    plugins:[postcssPresetEnv({
        browsers: 'last 5 version'
    })]
}
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' },
+     { test: /\.css$/, use: ['style-loader', 'css-loader','postcss-loader'] },
+     { test: /\.less$/, use: ['style-loader','css-loader','postcss-loader','less-loader'] },
+     { test: /\.scss$/, use: ['style-loader','css-loader','postcss-loader','sass-loader'] }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ]
};
src\index.css
@import "./bg.css";
body{
    color:red;
}
#logo{
    width:540px;
    height:258px;
    background-image: url(./assets/logo.png);
    background-size: cover;
}
+::placeholder {
+    color: red;
+}
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webpack5</title>
</head>
<body>
  <div id="less-container">less-container</div>
  <div id="sass-container">sass-container</div>
  <div id="logo"></div>
+  <input placeholder="请输入"/>
</body>
</html>
{
+  "browserslist": {
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ],
+    "production": [
+      ">0.2%"
+    ]
+  }
+}
limit的时候会把图片BASE64编码,大于limit参数的时候还是使用file-loader进行拷贝cnpm i file-loader url-loader html-loader -D
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' },
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      { test: /\.less$/, use: ['style-loader','css-loader', 'less-loader'] },
      { test: /\.scss$/, use: ['style-loader','css-loader', 'sass-loader'] },
+     { test: /\.(jpg|png|bmp|gif|svg)$/, 
+        use: [{
+          loader: 'url-loader', 
+          options: {
+            esModule: false,
+            name: '[hash:10].[ext]',
+            limit: 8*1024,
+          }
+        }]
+     }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ]
};
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webpack5</title>
</head>
<body>
  <div id="less-container">less-container</div>
  <div id="sass-container">sass-container</div>
+ <div id="logo"></div>
</body>
</html>
src\index.js
import './index.css';
import './less.less';
import './sass.scss';
let title = require('./title.txt');
document.write(title.default);
+let logo=require('./assets/logo.png');
+let img=new Image();
+img.src=logo.default;
+document.body.appendChild(img);
cnpm i babel-loader @babel/core @babel/preset-env @babel/preset-react  -D
cnpm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/'
  },
  module: {
    rules: [
+      {
+        test: /\.jsx?$/,
+        use: {
+          loader: 'babel-loader',
+          options: {
+            presets: [["@babel/preset-env",{
+              targets: "> 0.25%, not dead",
+            }], '@babel/preset-react'],
+            plugins: [
+              ['@babel/plugin-proposal-decorators', { legacy: true }],
+              ['@babel/plugin-proposal-class-properties', { loose: true }],
+            ],
+          },
+        },
+      },
      { test: /\.txt$/, use: 'raw-loader' },
      { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
      { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
      { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
      {
        test: /\.(jpg|png|bmp|gif|svg)$/, use: [{
          loader: 'url-loader', options: {
            limit: 10,
            outputPath: 'images',
            publicPath: '/images'
          }
        }]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].css'
    }),
  ]
};
src\index.js
+function readonly(target,key,descriptor) {
+    descriptor.writable=false;
+}
+
+class Person{
+    @readonly PI=3.14;
+}
+let p1=new Person();
+p1.PI=3.15;
+console.log(p1)
jsconfig.json
{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}
cnpm install eslint eslint-loader babel-eslint --D
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/'
  },
  module: {
    rules: [
+      {
+        test: /\.jsx?$/,
+        loader: 'eslint-loader',
+        enforce: 'pre',
+        options: { fix: true },
+        exclude: /node_modules/,
+      },
      {
        test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            "presets": ["@babel/preset-env"],
            "plugins": [
              ["@babel/plugin-proposal-decorators", { "legacy": true }],
              ["@babel/plugin-proposal-class-properties", { "loose": true }]
            ]
          }
        },
        include: path.join(__dirname, 'src'),
        exclude: /node_modules/
      },
      { test: /\.txt$/, use: 'raw-loader' },
      { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
      { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
      { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
      {
        test: /\.(jpg|png|bmp|gif|svg)$/, use: [{
          loader: 'url-loader', options: {
            limit: 10,
            outputPath: 'images',
            publicPath: '/images'
          }
        }]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].css'
    }),
  ]
};
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webpack5</title>
</head>
<body>
+  <div id="root"></div>
</body>
</html>
src\index.js
+import React from "react";
+import ReactDOM from "react-dom";
+ReactDOM.render("hello",document.getElementById("root"));
+
+function readonly(target,key,descriptor) {
+    descriptor.writable=false;
+}
+
+class Person{
+    @readonly PI=3.14;
+}
+let p1=new Person();
+p1.PI=3.15;
.eslintrc.js
module.exports = {
    root: true,
    parser:"babel-eslint",
    //指定解析器选项
    parserOptions: {
        sourceType: "module",
        ecmaVersion: 2015
    },
    //指定脚本的运行环境
    env: {
        browser: true,
    },
    // 启用的规则及其各自的错误级别
    rules: {
        "indent": "off",//缩进风格
        "quotes":  "off",//引号类型 
        "no-console": "error",//禁止使用console
    }
}
cnpm i eslint-config-airbnb eslint-loader eslint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks and eslint-plugin-jsx-a11y -D
eslintrc.js
module.exports = {
    "parser":"babel-eslint",
    "extends":"airbnb",
    "rules":{
        "semi":"error",
        "no-console":"off",
        "linebreak-style":"off",
        "eol-last":"off"
        //"indent":["error",2]
    },
    "env":{
        "browser":true,
        "node":true
    }
}
.vscode\settings.json
{
    "eslint.validate": [
        "javascript",
        "javascriptreact",
        "typescript",
        "typescriptreact"
    ],
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
    }
  }
debug到原始开发代码的技术webpack通过配置可以自动给我们source maps文件,map文件是一种对应编译文件和源文件的方法| 类型 | 含义 | 
|---|---|
| source-map | 原始代码 最好的sourcemap质量有完整的结果,但是会很慢 | 
| eval-source-map | 原始代码 同样道理,但是最高的质量和最低的性能 | 
| cheap-module-eval-source-map | 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能 | 
| cheap-eval-source-map | 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl | 
| eval | 生成代码 每个模块都被eval执行,并且存在@sourceURL,带eval的构建模式能cache SourceMap | 
| cheap-source-map | 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用 | 
| cheap-module-source-map | 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射 | 
| 关键字 | 含义 | 
|---|---|
| eval | 使用eval包裹模块代码 | 
| source-map | 产生.map文件 | 
| cheap | 不包含列信息(关于列信息的解释下面会有详细介绍)也不包含loader的sourcemap | 
| module | 包含loader的sourcemap(比如jsx to js ,babel的sourcemap),否则无法定义源文件 | 
| inline | 将.map作为DataURI嵌入,不单独生成.map文件 | 
module.exports = {
  devtool: 'source-map',
  devtool: 'eval-source-map',
  devtool: 'cheap-module-eval-source-map',
  devtool: 'cheap-eval-source-map',
  devtool: 'eval',
  devtool: 'cheap-source-map',
  devtool: 'cheap-module-source-map',
}
eval执行eval-cheap-source-mapcheap-module-source-mapeval-source-mapsourcemap>cheap-source-map/cheap-module-source-map>hidden-source-map/nosources-sourcemapcheaphidden-source-map@ 和 #符号开头的,@开头的已经被废弃
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
+const FileManagerPlugin = require('filemanager-webpack-plugin');
+const webpack = require('webpack');
module.exports = {
  mode: 'none',
  devtool: false,
  entry: './src/index.js',
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin(),
    ],
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/',
  },
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    compress: true,
    port: 8080,
    open: true,
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        options: { fix: true },
        exclude: /node_modules/,
      },
      {
        test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [[
              '@babel/preset-env',
              {
                useBuiltIns: 'usage', // 按需要加载polyfill
                corejs: {
                  version: 3, // 指定core-js版本
                },
                targets: { // 指定要兼容到哪些版本的浏览器
                  chrome: '60',
                  firefox: '60',
                  ie: '9',
                  safari: '10',
                  edge: '17',
                },
              },
            ], '@babel/preset-react'],
            plugins: [
              ['@babel/plugin-proposal-decorators', { legacy: true }],
              ['@babel/plugin-proposal-class-properties', { loose: true }],
            ],
          },
        },
        include: path.join(__dirname, 'src'),
        exclude: /node_modules/,
      },
      { test: /\.txt$/, use: 'raw-loader' },
      { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
      { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'] },
      { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] },
      {
        test: /\.(jpg|png|bmp|gif|svg)$/,
        use: [{
          loader: 'url-loader',
          options: {
            esModule: false,
            name: '[hash:10].[ext]',
            limit: 8 * 1024,
            outputPath: 'images',
            publicPath: '/images',
          },
        }],
      },
      {
        test: /\.html$/,
        loader: 'html-loader',
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].css',
    }),
    new OptimizeCssAssetsWebpackPlugin(),
+    new webpack.SourceMapDevToolPlugin({
+      append: '\n//# sourceMappingURL=http://127.0.0.1:8081/[url]',
+      filename: '[file].map',
+    }),
+    new FileManagerPlugin({
+      events: {
+        onEnd: {
+          copy: [{
+            source: './dist/*.map',
+            destination: 'C:/aprepare/zhufengwebpack2021/1.basic/sourcemap',
+          }],
+          delete: ['./dist/*.map'],
+        },
+      },
+    }),
  ],
};

import _ from 'lodash';
alert(_.join(['a','b','c'],'@'));
+ new webpack.ProvidePlugin({
+     _:'lodash'
+ })
没有全局的
$函数,所以导入依赖全局变量的插件依旧会失败
loader之前  module: {
    rules: [
+      {
+          test: require.resolve('lodash'),
+          loader: 'expose-loader',
+          options: {
+              exposes: {
+                  globalName: '_',
+                  override: true,
+              },
+          },
+      }
    ]
  }
如果我们想引用一个库,但是又不想让webpack打包,并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用,那就可以通过配置externals
 const jQuery = require("jquery");
 import jQuery from 'jquery';
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
+externals: {
+  lodash: '_',
+},
  module: {
module.exports = {
  //默认false,也就是不开启
  watch:true,
  //只有开启监听模式时,watchOptions才有意义
  watchOptions:{
      //默认为空,不监听的文件或者文件夹,支持正则匹配
      ignored:/node_modules/,
      //监听到变化发生后会等300ms再去执行,默认300ms
      aggregateTimeout:300,
      //判断文件是否发生变化是通过不停的询问文件系统指定议是有变化实现的,默认每秒问1000次
      poll:1000
  }
}
aggregateTimeout配置+ new webpack.BannerPlugin('珠峰架构'),
npm i copy-webpack-plugin -D
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+new CopyWebpackPlugin({
+  patterns: [{
+    from: path.resolve(__dirname,'src/static'),//静态资源目录源地址
+    to: path.resolve(__dirname,'dist/static'), //目标地址,相对于output的path目录
+  }],
+}),
npm i  clean-webpack-plugin -D
+ const {CleanWebpackPlugin} = require('clean-webpack-plugin');
plugins:[
+ new CleanWebpackPlugin({cleanOnceBeforeBuildPatterns: ['**/*'],})
]
如果你有单独的后端开发服务器 API,并且希望在同域名下发送 API 请求 ,那么代理某些 URL 会很有用。
devServer: {
  proxy: {
    "/api": 'http://localhost:3000'
  }
}
devServer: {
  proxy: {
      "/api": {
       target: 'http://localhost:3000',
       pathRewrite:{"^/api":""}        
      }            
  }
}
before 在 webpack-dev-server 静态资源中间件处理之前,可以用于拦截部分请求返回特定内容,或者实现简单的数据 mock。
devServer: {
  before(app){
    app.get('/api/users', function(req, res) { 
       res.json([{id:1,name:'zhufeng'}])
    })
  }
}
webpack-dev-middleware就是在 Express 中提供 webpack-dev-server 静态服务能力的一个中间件
npm install webpack-dev-middleware --save-dev
const express = require('express');
const app = express();
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackOptions = require('./webpack.config');
webpackOptions.mode = 'development';
const compiler = webpack(webpackOptions);
app.use(webpackDevMiddleware(compiler, {}));
app.listen(3000);
webpack-dev-middleware的好处是可以在既有的 Express 代码基础上快速添加 webpack-dev-server 的功能,同时利用 Express 来根据需要添加更多的功能,如 mock 服务、代理 API 请求等cnpm install --save-dev mini-css-extract-plugin
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
+    publicPath: '/'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' },
+      { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
+      { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
+      { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
       { test: /\.(jpg|png|bmp|gif|svg)$/, 
        use: [{
          loader: 'url-loader', 
          options: {
            esModule: false,
            name: '[hash:10].[ext]',
            limit: 8*1024
          }
        }]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
+    new MiniCssExtractPlugin({
+      filename: '[name].css'
+    })
  ]
};
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' },
      { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
      { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
      { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
      { test: /\.(jpg|png|bmp|gif|svg)$/, 
        use: [{
          loader: 'url-loader', 
          options: {
            esModule: false,
            name: '[hash:10].[ext]',
            limit: 8*1024,
+           outputPath: 'images',
+           publicPath: '/images'
          }
        }]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
    new MiniCssExtractPlugin({
+      filename: 'css/[name].css'
    }),
  ]
};
文件指纹是指打包后输出的文件名和后缀指纹占位符
| 占位符名称 | 含义 | 
|---|---|
| ext | 资源后缀名 | 
| name | 文件名称 | 
| path | 文件的相对路径 | 
| folder | 文件所在的文件夹 | 
| hash | 每次webpack构建时生成一个唯一的hash值 | 
| chunkhash | 根据chunk生成hash值,来源于同一个chunk,则hash值就一样 | 
| contenthash | 根据内容生成hash值,文件内容相同hash值就相同 | 

function createHash(){
   return  require('crypto').createHash('md5');
}
let entry = {
    entry1:'entry1',
    entry2:'entry2'
}
let entry1 = 'require depModule1';//模块entry1
let entry2 = 'require depModule2';//模块entry2
let depModule1 = 'depModule1';//模块depModule1
let depModule2 = 'depModule2';//模块depModule2
//如果都使用hash的话,因为这是工程级别的,即每次修改任何一个文件,所有文件名的hash至都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效
let hash =  createHash()
.update(entry1)
.update(entry2)
.update(depModule1)
.update(depModule2)
.digest('hex');
console.log('hash',hash)
//chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。
//在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响
let entry1ChunkHash = createHash()
.update(entry1)
.update(depModule1).digest('hex');;
console.log('entry1ChunkHash',entry1ChunkHash);
let entry2ChunkHash = createHash()
.update(entry2)
.update(depModule2).digest('hex');;
console.log('entry2ChunkHash',entry2ChunkHash);
let entry1File = entry1+depModule1;
let entry1ContentHash = createHash()
.update(entry1File).digest('hex');;
console.log('entry1ContentHash',entry1ContentHash);
let entry2File = entry2+depModule2;
let entry2ContentHash = createHash()
.update(entry2File).digest('hex');;
console.log('entry2ContentHash',entry2ContentHash);
module.exports = {
+  entry: {
+    main: './src/index.js',
+    vender:['lodash']
+  },
  output:{
     path:path.resolve(__dirname,'dist'),
+    filename:'[name].[hash].js'
  },
  plugins: [
    new MiniCssExtractPlugin({
+      filename: "css/[name].[hash].css"
    })
  ]
};
module.exports = {
  entry: {
    main: './src/index.js',
    vender:['lodash']
  },
  output:{
    path:path.resolve(__dirname,'dist'),
+   filename:'[name].[chunkhash].js'
  },
  plugins: [
    new MiniCssExtractPlugin({
+      filename: "css/[name].[chunkhash].css"
    })
  ]
};
mini-css-extract-plugin里的contenthash值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
+      filename: "css/[name].[contenthash].css"
    })
  ],
};
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
+const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
+  mode: 'none',
  devtool: false,
  entry: './src/index.js',
+  optimization: {
+    minimize: true,
+    minimizer: [
+      new TerserPlugin(),
+    ],
+  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/',
  },
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    compress: true,
    port: 8080,
    open: true,
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        options: { fix: true },
        exclude: /node_modules/,
      },
      {
        test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [[
              '@babel/preset-env',
              {
                useBuiltIns: 'usage'
                corejs: {
                  version: 3
                },
                targets: {
                  chrome: '60',
                  firefox: '60',
                  ie: '9',
                  safari: '10',
                  edge: '17',
                },
              },
            ], '@babel/preset-react'],
            plugins: [
              ['@babel/plugin-proposal-decorators', { legacy: true }],
              ['@babel/plugin-proposal-class-properties', { loose: true }],
            ],
          },
        },
        include: path.join(__dirname, 'src'),
        exclude: /node_modules/,
      },
      { test: /\.txt$/, use: 'raw-loader' },
      { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
      { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'] },
      { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] },
      {
        test: /\.(jpg|png|bmp|gif|svg)$/,
        use: [{
          loader: 'url-loader',
          options: {
            esModule: false,
            name: '[hash:10].[ext]',
            limit: 8 * 1024,
            outputPath: 'images',
            publicPath: '/images',
          },
        }],
      },
      {
        test: /\.html$/,
        loader: 'html-loader',
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
    template: './src/index.html',
+     minify: {  
+        collapseWhitespace: true,
+        removeComments: true
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].css',
    }),
+    new OptimizeCssAssetsWebpackPlugin(),
  ],
};
npm install image-webpack-loader --save-dev
 {
          test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
          use: [
            'url-loader',
+           {
+             loader: 'image-webpack-loader',
+             options: {
+               mozjpeg: {
+                 progressive: true,
+                 quality: 65
+               },
+               optipng: {
+                 enabled: false,
+               },
+               pngquant: {
+                 quality: '65-90',
+                 speed: 4
+               },
+               gifsicle: {
+                 interlaced: false,
+               },
+               webp: {
+                 quality: 75
+               }
+             }
+           }
          ]
        }
font-size值cnpm i px2rem-loader lib-flexible -D
index.html
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>主页</title>
    <script>
      let docEle = document.documentElement;
      function setRemUnit () {
        //750/10=75   375/10=37.5
        docEle.style.fontSize = docEle.clientWidth / 10 + 'px';
      }
      setRemUnit();
      window.addEventListener('resize', setRemUnit);
    </script>
</head>
<body>
    <div id="root"></div>
</body>
src/reset.css
*{
    padding: 0;
    margin: 0;
}
#root{
    width:750px;
    height:750px;
    border:1px solid red;
    box-sizing: border-box;
}
 {
      test:/\.css$/,
        use:[{
                MiniCssExtractPlugin.loader,
                'css-loader',
                'postcss-loader',
                {
+                    loader:'px2rem-loader',
+                    options:{
+                        remUnit:75,
+                        remPrecesion:8
+                    }
+                }]
+            },
@babel/preset-env会根据预设的浏览器兼容列表从stage-4选取必须的plugin,也就是说,不引入别的stage-x,@babel/preset-env将只支持到stage-4Array.form方法,Babel就不会转码这个方法,如果想让这个方法运行,必须使用 babel-polyfill来转换等babel-polyfill 它是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find方法,引入polyfill, 我们就可以使用es6方法来编写了,但是缺点就是会造成全局空间污染useBuiltIns配置才能转化API和实例方法useBuiltIns可选值包括:"usage" | "entry" | false, 默认为 false,表示不对 polyfills 处理,这个配置是引入 polyfills 的关键npm i @babel/polyfill
@babel/polyfill,则无视配置的浏览器兼容,引入所有的 polyfill86.4 KiBimport '@babel/polyfill';
 {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [["@babel/preset-env", {
+                           useBuiltIns: false,
                        }], "@babel/preset-react"],
                        plugins: [
                            ["@babel/plugin-proposal-decorators", { legacy: true }],
                            ["@babel/plugin-proposal-class-properties", { loose: true }]
                        ]
                    }
                }
},
import '@babel/polyfill',会自动根据 browserslist 替换成浏览器不兼容的所有 polyfillimport 'core-js/stable';import 'regenerator-runtime/runtime';corejs默认是2,配置2的话需要单独安装core-js@380.6 KiB10.7 KiBimport '@babel/polyfill';
npm i core-js@3
import 'core-js/stable';
import 'regenerator-runtime/runtime';
 {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [["@babel/preset-env", {
+                           useBuiltIns: 'entry',
+                           corejs: { version: 2 }
                        }], "@babel/preset-react"],
                        plugins: [
                            ["@babel/plugin-proposal-decorators", { legacy: true }],
                            ["@babel/plugin-proposal-class-properties", { loose: true }]
                        ]
                    }
                }
},
{
    "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">1%"
    ]
  },
}
usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加@babel/polyfillbabel-transform-runtime,不会造成全局污染,因此也会不会对类似 Array.prototype.includes() 进行 polyfill0 bytes8.98 KiBimport '@babel/polyfill';
console.log(Array.from([]));
 {
    test: /\.jsx?$/,
    exclude: /node_modules/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [["@babel/preset-env", {
+               useBuiltIns: 'usage',
+               corejs: { version: 3 }
            }], "@babel/preset-react"],
            plugins: [
                ["@babel/plugin-proposal-decorators", { legacy: true }],
                ["@babel/plugin-proposal-class-properties", { loose: true }]
            ]
        }
    }
},
babel-runtime 更像是一种按需加载的实现,比如你哪里需要使用 Promise,只要在这个文件头部import Promise from 'babel-runtime/core-js/promise'就行了npm i babel-runtime -D
import Promise from 'babel-runtime/core-js/promise';
const p = new Promise(()=> {
});
console.log(p);
babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数babel-plugin-transform-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用babel-plugin-transform-runtime 就是可以在我们使用新 API 时自动 import babel-runtime 里面的 polyfillasync/await 时,自动引入 babel-runtime/regeneratorbabel-runtime/core-jsbabel helpers并替换使用babel-runtime/helpers 来替换corejs默认是3,配置2的话需要单独安装@babel/runtime-corejs2npm i @babel/runtime-corejs2 -D
      {
        test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ["@babel/preset-env",'@babel/preset-react'],
            plugins: [
+              [
+                "@babel/plugin-transform-runtime",
+                {
+                  corejs: 2,//当我们使用 ES6 的静态事件或内置对象时自动引入 babel-runtime/core-js
+                  helpers: true,//移除内联babel helpers并替换使用babel-runtime/helpers 来替换
+                  regenerator: true,//是否开启generator函数转换成使用regenerator runtime来避免污染全局域
+                },
+              ],
              ['@babel/plugin-proposal-decorators', { legacy: true }],
              ['@babel/plugin-proposal-class-properties', { loose: true }],
            ],
          },
        },
      },
corejs: 2 corejs 2=>false 131 KiB => 224 bytes
const p = new Promise(()=> {});
console.log(p);
helpers true=>false 160 KiB=>150 KiB
class A {
}
class B extends A {
}
console.log(new B());
regenerator false=>true B490 bytes->28.6 Ki
function* gen() {
}
console.log(gen());
babel-runtime适合在组件和类库项目中使用,而babel-polyfill适合在业务项目中使用。<script src="https://polyfill.io/v3/polyfill.min.js"></script>