webpack 5 的基本配置(1)

281 阅读7分钟

1. webpack介绍

webpack 是一个JavaScript 应用程序的静态模块打包工具

1.1 安装

npm install  webpack webpack-cli --save-dev

1.2 入口(entry)

  • 入口起点(entry point): 表示 webpack 应该使用哪个模块,用来作为构建其内部依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
  • 默认是 ./src/index.js,但可以通过在 webpack.config.js 中配置 entry 属性,来指定一个(或多个)不同的入口起点。

1.2.1 创建 webpack.config.js

module.exports = {
  entry: './src/index.js', // 入口配置,可以是对象
};

1.2.2 创建两个源代码文件

  • src\style.css
body {
  margin: 0;
  padding: 0;
}
  • src\index.js
import './style.css';
const str = "Hello Webpack!!!";
console.log(str);

1.3 输出(output)

  • output 表示 webpack 在哪里输出它所打包构建的 bundle,以及如何命名这些文件。
  • 主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

1.3.1 webpack.config.js

+ const path = require('path');
module.exports = {
  entry: './src/index.js',
+  output: {
+    path: path.resolve(__dirname, 'dist'),
+    filename: 'main.js'
+  }
};

1.4 模式(mode)

  • webpack 4.x 版本引入了 mode 的概念: 区分开发环境生产环境

  • development:开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印 debug 信息,包含 sourcemap 文件。

  • production:生产时使用,打包构建后的结果是直接应用于线上的,即代码都是压缩后,运行时不打印 debug 信息,静态文件不包括 sourcemap。

  • mode 指定 production 时,默认会启用各种性能优化的功能,包括构建结果优化以及 webpack 运行性能优化。

  • mode 指定development 时,则会开启 debug 工具,运行时打印详细的错误信息,以及更加快速的增量编译构建。

1.4.1 webpack.config.js

const path = require('path');

module.exports = {
+  mode: 'development', // production/development
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
}

1.4.2 环境差异

  • 其默认值为 production

  • 开发环境

    • 需要生成 sourcemap 文件;
    • 需要打印 debug 信息;
    • 需要 live reload 或者 hot reload 的功能;
    • 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin
  • 生产环境

    • 可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件;
    • 需要压缩 HTML/CSS/JS 代码;
    • 需要压缩图片;
    • 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin

1.4.3 区分环境

  • --mode:用来设置模块内的 process.env.NODE_ENV。
  • --env:用来设置 webpack 配置文件的函数参数。
  • cross-env:用来设置 node 环境的process.env.NODE_ENV
  • webpack.DefinePlugin:用来设置模块内的全局变量。

1.4.4 环境配置

  1. 方式一
  • webpack的 mode 默认为 production
  • webpack serve 的 mode 默认为 development
  • 可以在模块内通过 process.env.NODE_ENV获取当前的环境变量,无法在 webpack 配置文件中获取此变量

package.json

  "scripts": {
    "build": "webpack",
    "start": "webpack serve"
  },

index.js

console.log(process.env.NODE_ENV); // development | production

webpack.config.js

console.log(process.env.NODE_ENV); // undefined
  1. 方式二

与方式一作用一样

package.json

"scripts": {
  "build": "webpack --mode=production",
  "start": "webpack --mode=development serve"
},
  1. 方式三
  • 无法在模块内通过process.env.NODE_ENV访问
  • 可以通过webpack 配置文件中中通过函数获取当前环境变量

package.json

"scripts": {
   "dev": "webpack serve --env=development",
   "build": "webpack --env=production",
}

index.js

console.log(process.env.NODE_ENV); // undefined

webpack.config.js

console.log(process.env.NODE_ENV);// undefined
module.exports = (env, argv) => {
  console.log('env',env); // development | production
};
  1. 方式四

webpack.config.js 中配置,与方式二的作用一样

module.exports = {
  mode: 'development'
}
  1. 方式五: webpack.DefinePlugin
  • 设置全局变量(不是window),所有模块都能读取到该变量的值。
  • 可以在任意模块内通过 process.env.NODE_ENV 获取当前的环境变量。
  • 无法在 node 环境(webpack 配置文件中)下获取当前的环境变量。

webpack.config.js 中配置 plugins

const webpack = require('webpack');
plugins:[
   new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) // 必须使用JSON.stringify 指定
   })
]

index.js

console.log(NODE_ENV); // production 其他等等变量

webpack.config.js

console.log(process.env.NODE_ENV); // undefined
console.log(NODE_ENV);	// error
  1. 方式六:cross-env
  • 只能设置 node 环境下的变量 NODE_ENV

package.json

"scripts": {
  "dev": "cross-env NODE_ENV=development webpack"
}

webpack.config.js

console.log(process.env.NODE_ENV); // development

1.5 loader

  • webpack 只能解析 JavaScriptJSON 资源文件。
  • loader 让 webpack 能够去处理其他类型的资源文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。

1.5.1 webpack.config.js

const path = require('path');
module.exports = {
  mode: 'development', // production/development
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
+  module: {
+    rules: [
+      {
+    	  test: /\.css$/, // 指定解析的资源类型
+    	  use: ['style-loader','css-loader'] // webpack 为从后往前使用对应的 loader
+	    }
+    ]
+  }
};

1.6 插件(plugin)

  • loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化 、资源管理、注入环境变量等。

1.6.1 src\index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>webpack</title>
  </head>
  <body></body>
</html>

1.6.2 webapck.config.js

  • 安装 html-webpack-plugin
npm install html-webpack-plugin --save-dev

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve("dist"),
    filename: "main.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
+  plugins: [
+    new HtmlWebpackPlugin({
+      template: "./src/index.html",
+    }),
+  ],
};

2. 开发服务器 webpack-dev-server

2.1 安装

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

2.2 webpack.config.js

类别配置名称描述
outputpath指定输出到硬盘上的目录
outputpublicPath表示的是打包生成的 index.html 文件里面引用资源的前缀
devServerpublicPath表示的是打包生成的静态文件所在的位置(若是devServer里面的publicPath没有设置,则会认为是output里面设置的publicPath的值)
devServerstatic用于配置提供额外静态文件内容的目录(默认就是 output 中指定 path 路径)

webpack.config.js

+ const CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve("dist"),
    filename: "main.js",
  },
+  devServer: {
+    static: path.resolve(__dirname, 'public'), // 指定开发时的资源静态目录,如果里面还有其他资源在打包到生产环境时需要用到 copy-webpack-plugin 插件包资源拷贝到 output 中指定的打包到生产环境 path 路径中。可以配置为数组,表示多个静态目录。static: []
+    port: 8080,
+    open: true
+  },
  plugins: [
+    new CopyWebpackPlugin({
+      patterns: [
+        { from: "other", to: "public" },
+        { from: "public", to: "." },
+      ],
+    }),
  ],
    
}

2.3 package.json

  "scripts": {
    "build": "webpack",
+   "start": "webpack serve"
  }

3. 设置变量别名

设置的路径别名,方便在模块中导入其他模块。

3.1 webpack.config.js

const path = require('path');
module.exports = {
+  resolve: {
+    alias: {
+      "@": path.resolve("src"),
+      "assets": path.resolve("src/assets"),
+    },
+  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
}

4. 支持CSS

4.1 安装

npm install style-loader css-loader --save-dev

4.2 webpack.config.js

module.exports = {
  // other config ...
  module: {
    rules: [
+      {
+        test: /\.css$/,
+        use: ["style-loader", "css-loader"], // 从后往前执行对应的loader
+      },
    ],
  }
};

PS:

{
  test: /\.css$/,
  use: [
    "style-loader",
    // "css-loader",
    {
      // 需要一些参数设置,就这样配置一个对象,css-loader
      loader: "css-loader",
      options: {
        url: false, // true: url引入的路径会被处理成 hash:background-image: url("images/a.jpg");
        import: false, // true: @import('./index.css')
        modules: false, // true: 模块化 css, 类名会被修改,需要 import styles from './index.css'
        sourceMap: true,
        importLoaders: true,
        esModule: true,  // true: {default: xxx} 给 style-loader 的数据结构
      },
    },
  ],
}

4.3 CSS 资源文件的应用

  • src/reset.css
body {
    background: red;
    margin: 0;
    padding: 0;
}
  • src/index.css
@import "./reset.css";
h1 {
    color: #ccc;
     font-family: monospace;
}
  • src/index.js
import "./index.css";

5. 支持 sass

5.1 安装

npm install sass sass-loader --save-dev

5.2 webpack.config.js

const path = require('path');
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
+      {
+        test: /\.scss$/,
+        use: ["style-loader", "css-loader", "sass-loader"],
+      },
    ]
  },
};

6. CSS兼容性

  • 为了浏览器的兼容性,加入 -webkit,-ms,-o,-moz 这些前缀

    • Trident内核:代表为IE浏览器, 前缀为 -ms
    • Gecko内核:代表为Firefox, 前缀为 -moz
    • Presto内核:代表为Opera, 前缀为 -o
    • Webkit内核:产要代表为Chrome和Safari、Edge, 前缀为 -webkit
  • 伪元素::placeholder可以选择一个表单元素的占位文本,它允许开发者和设计师自定义占位文本的样式。

  • 查询css的兼容性: caniuse.com/

  • postcss-loader 可以使用 PostCSS 处理 CSS。

  • postcss-preset-env 把现代的CSS转换成大多数浏览器能理解的。

  • postcss-preset-env 已经包含了 autoprefixerbrowsers 选项。

6.1 安装

npm install postcss-loader postcss-preset-env --save-dev

6.2 创建 postcss.config.js

const postcssPresetEnv = require("postcss-preset-env");

module.exports = {
  plugins: [
    postcssPresetEnv({
      browsers: "last 5 version",
    }),
  ],
};

6.3 webpack.config.js

const path = require('path');
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader", "postcss-loader"],
      },
+      {
+        test: /\.scss$/,
+        use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
+      },
    ]
  },
};

6.3 package.json 或 .browserslistrc

{
+  "browserslist": {
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ],
+    "production": [
+      ">0.2%"
+    ]
+  }
}

7. 资源模块

  • 资源模块是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader
  • raw-loader => asset/source 导出资源的源代码(字符串)
  • file-loader => asset/resource 发送一个单独的文件并导出 URL
  • url-loader => asset/inline 导出一个资源的 data URI
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现
  • Rule.type
  • asset-modules

7.1 webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
console.log("process.env.NODE_ENV", process.env.NODE_ENV); // development | production

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve("dist"),
    filename: "main.js",
  },
  module: {
    rules: [
+      {
+        test: /\.png$/,
+        type: "asset/resource", // 相当与 file-loader,处理成一个文件输出到对应的目录中
+      },
+      {
+        test: /\.ico$/,
+        type: "asset/inline", // 相当于 url-loader,可以把文件内容处理成base64内联到html文件中
+      },
+      {
+        test: /\.png$/,
+        type: "asset/resource", // 相当与 raw-loader,输出字符串,对内容不做任何转换
+      },
+      {
+        test: /\.jpg$/,
+        type: "asset",
+        parser: {
+          dataUrlCondition: { // 内联条件,如果文件体积大于 maxSize 处理成文件,小于 maxSize 就处理成 base64
+            maxSize: 4 * 1024, // 4kb
+          },
+        },
+      },
    ],
  }
};

PS:

{
  {
    test: /\.(png|svg|jpg|jpeg|gif)$/i,
    type: 'asset', // 根据图片大小是 url 路径还是 base64 方式引入
    generator: {
      filename: 'images/[hash:8][ext][query]' // 局部指定输出位置
    },
    parser: {
      dataUrlCondition: {
        maxSize: 500 * 1024 // 限制于 500kb
      }
    }
  },

  {
    test: /\.(woff|woff2|eot|ttf|otf)$/i,
    type: 'asset/resource', // 字体图标直接使用 url 路径引入
  },

   // html 中的资源处理, 比如 image
   {
    test: /\.html/i,
    loader: 'html-loader'
  },
}

src/index.js

+ import png from './assets/a1.png';
+ import ico from './assets/a2.ico';
+ import jpg from './assets/a3.jpg';
+ import txt from './assets/a4.txt';
+ console.log(png, ico, jpg, txt);

8. JS 兼容性处理

Babel 是一个编译 JavaScript 的平台, 可以把 ES6/ES7, React 的 JSX转义为 ES5

  • @babel/preset-env

Babel 默认只转换新的最新ES语法,比如箭头函数

8.1 安装依赖

npm install babel-loader @babel/core @babel/preset-env --save-dev

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

npm install @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-proposal-private-property-in-object @babel/plugin-proposal-private-methods  --save-dev

8.3 webpack.config.js

  module: {
    rules: [
+      {
+        test: /\.jsx?$/,
+        // exclude: /node_modules/,
+        include: path.resolve("src"),
+        use: {
+          loader: 'babel-loader',
+          options: {
+            presets: ["@babel/preset-env", '@babel/preset-react'],
+            plugins: [
+              ["@babel/plugin-proposal-decorators", { legacy: true }],
+              ["@babel/plugin-proposal-private-property-in-object", { "loose": true }],
+              ["@babel/plugin-proposal-private-methods", { "loose": true }],
+              ["@babel/plugin-proposal-class-properties", { loose: true }],
+            ],
+          },
+        },
+      },
      { test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] },
      { test: /\.scss$/, use: ['style-loader', 'css-loader', 'postcss-loader','sass-loader'] },
      {
        test: /\.(jpg|png|gif|bmp|svg)$/,
        type:'asset/resource',
        generator:{
          filename:'images/[hash][ext]'
        }
      }
    ]
  },
  • 或者配置 .babelrc

.babelrc

{
  "presets": [
    "@babel/preset-env"
  ],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    [
      "@babel/plugin-proposal-private-property-in-object",
      { "loose": true }
    ],
    ["@babel/plugin-proposal-private-methods", { "loose": true }],
    ["@babel/plugin-proposal-class-properties", { "loose": true }]
  ]
}

这样 webpack 就配置简单一些

webapck.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
console.log("process.env.NODE_ENV", process.env.NODE_ENV); // development | production

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve("dist"),
    filename: "main.js",
  },
  module: {
    rules: [
+      {
+        test: /\.jsx?$/,
+        // exclude: /node_modules/,
+        include: path.resolve("src"),
+        loader: "babel-loader",
+      }
    ],
  },
};

8.4 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);

8.5 配置编辑器识别一些特殊的写法,或提示

jsconfig

  • 创建 jsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

9. ESLint 语法(包括 jsx 语法)检查与矫正

eslint 一般会配合 huskylint-staged 一起使用。

9.1 安装

babel-eslint 被 @babel/eslint-parser 替换

eslint-loader 被 eslint-webpack-plugin 替换

npm install eslint eslint-webpack-plugin @babel/eslint-parser --save-dev

9.2 webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

+ const ESLintWebpackPlugin = require("eslint-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve("dist"),
    filename: "main.js",
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        include: path.resolve("src"),
        loader: "babel-loader",
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
+    new ESLintWebpackPlugin(),
  ],
};

9.3 .eslintrc.js

module.exports = {
  root: true,
  parser: "@babel/eslint-parser",
  //指定解析器选项
  parserOptions: {
    sourceType: "module",
    ecmaVersion: 2015,
  },
  //指定脚本的运行环境
  env: {
    browser: true,
  },
  // 启用的规则及其各自的错误级别
  rules: {
    indent: "off", // 缩进风格
    quotes: "off", // 引号类型
    "no-var": "error",  // 禁止使用 var
    "generator-star-spacing": "off", // allow async-await
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
  },
};

10. prettier 代码风格检测

prettier

10.1 安装

npm install eslint-plugin-prettier prettier --save-dev

10.2 .eslintrc.js

module.exports = {
  root: true,
  parser: "@babel/eslint-parser",

  //指定解析器选项
  parserOptions: {
    sourceType: "module",
    ecmaVersion: 2015,
  },
  //指定脚本的运行环境
  env: {
    browser: true,
  },
  // 启用的规则及其各自的错误级别
  // "off"或者0 // 关闭规则
  // "warn"或者1 // 在打开的规则作为警告(不影响退出代码)
  // "error"或者2 // 把规则作为一个错误(退出代码触发时为1)
+  plugins: ["prettier"],
  rules: {
+    "prettier/prettier": "error", // 遇到 prettier 检测就报错
    indent: "off", // 缩进风格
    quotes: "off", // 引号类
    "no-var": "error", // 禁止使用 var
    "generator-star-spacing": "off", // allow async-await
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
  },
};

10.3 prettier 与 eslint 规则冲突解决

  • 安装 eslint-config-prettier

通过使用 eslint-config-prettier 配置,能够关闭一些不必要的或者是与 prettier 冲突的 lint 选项。这样我们就不会看到一些 error 同时出现两次。使用的时候需要确保,这个配置在 extends 的最后一项。

npm install eslint-config-prettier --save-dev

列出一些冲突的部分

10.4 .eslintrc.js

{
+  extends: [
+    'standard', //使用standard做代码规范
+    "prettier",
+  ],
}

简写

{
+  "extends": ["plugin:prettier/recommended"]
}

10.5 prettierrc.js

module.exports = {
  printWidth: 80, // 一行的字符数,如果超过会进行换行,默认为80
  tabWidth: 2, // 一个tab代表几个空格数,默认为80
  useTabs: false, // 是否使用tab进行缩进,默认为false,表示用空格进行缩减
  singleQuote: false, // 字符串是否使用单引号,默认为false,使用双引号
  semi: true, // 行位是否使用分号,默认为true
  trailingComma: "none", // 是否使用尾逗号,有三个可选值"<none|es5|all>"
  bracketSpacing: true, // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
  parser: "babylon", // 代码的解析引擎,默认为babylon,与babel相同。
};

webpack 5 优化配置(2) webpack 5 优化配置-代码分割(3)