手把手带你搭建一个简单的webpack脚手架(二)

479 阅读8分钟

Plugin插件

在webpack的整个处理流程中,loader让webpack可以处理更多的文件资源。而plugin则存在于webpack整个打包流程中。可以在webpack暴露出的hook触发的时刻,对处理的内容进行处理。所以我们可以用plugin对处理中的文件进行各种处理。

index.html文件的处理

之前我们对于index.html是将其拷贝一份到dist文件夹下。如果index.html需要进行一些动态更改的话,这样并不是一个很好的处理,我们可以通过一个插件HtmlWebpackPlugin来生成我们打包后的html文件,对项目文件处理。

project

  webpack-demo
  |- package.json
  |- package-lock.json
  |- /dist
    |- main.js
    |- main.js.LICENSE.txt
-   |- index.html
  |- /src
    |- icon.png
    |- index.less
    |- index.js
  |- webpack.config.js
  |- postcss.config.js
  |- .browserslistrc
+ |- index.html

安装相应的插件依赖

npm install --save --dev html-webpack-plugin

webpack.config.js

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

module.exports = {
  ...
+  plugins: [
+    new HtmlWebpackPlugin({
+      template: path.join(__dirname, "./index.html"),  // ./index.html是作为模板生成dist/index.html
+    }),
+  ],
  ...
};

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Getting Started</title>
  </head>
  <body></body>
</html>

重新执行npm run build,查看dist/index.html文件。

<!doctype html><html><head><meta charset="utf-8"/><title>Getting Started</title><script defer="defer" src="main.js"></script></head><body></body></html>

可以看到main.js文件通过script标签插入了。这是HtmlWebpackPlugin插件在dist/index.html中插入的。当然这个插件还有一些好玩的动态生成index.html的方法,这个留给读者自行探究。

热更新配置

当我们每次更改源码时,webpack都需要重新打包一次文件,然后刷新浏览器才能看见效果,这对于开发者来说体验极差。我们可以使用热更新来提升开发体验。当我们更改源码时,热更新可以使得我们对于源码的更改快速的体现在浏览器中。而webpack提供了三种方式实现热更新,在这里,我们目前使用webpack-dev-server这种方式,其他方式可以点击这里

安装依赖

npm install --save --dev webpack-dev-server cross-env

安装webpack-dev-server和cross-env的依赖,webpack-dev-server用于提供热更新服务,而cross-env则是用来在package.json的命令行中方便注入变量。

webpack.config.js

module.exports = {
  output: {
    ...
  },
+ target: "web",
+ mode: "development",
+ devtool: "inline-source-map",
+ devServer: {
+   contentBase: path.resolve(__dirname, "dist"),
+   hot: "localhost",
+   compress: true,
+   port: 3002,
+  },
  module: {
   ...
  },
};

  • mode:配置环境,有开发(development)和生产(production)模式。如果不设的话,默认为生产模式。
  • devtool: 控制是否产生源码映射(source map),有了source map,在开发环境下,我们可以有效的在浏览器中debug,但是在生产环境中,建议关闭。
  • devServer:开发环境配置,具体配置可以去官网查询。 package.json
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
+   "dev": "cross-env NODE_ENV=development webpack serve --progress --hot --config webpack.config.js"
  },

新建dev的命令行配置,cross-env后的NODE_ENV=的参数,可以被设为webpack在node中运行时的环境变量。--config后是被指定执行的webpack配置文件。

在终端中运行命令行npm run dev。运行成功后终端显示。

Project is running at http://localhost:3002/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /Users/liwangping/Desktop/study/webpack-demo/dist

上面这几行输出显示项目运行在http://localhost:3002/,点击打开。 然后尝试着更改源文件中的代码。

src/index.js

function component() {
  ...
  // Lodash, currently included via a script, is required for this line to work
- element.innerHTML = _.join(["Hello", "webpack"], " ");
+ element.innerHTML = _.join(["Hello1", "webpack"], " ");

  ...
}

document.body.appendChild(component());

更改后保存文件,再观察浏览器,发现。

企业微信截图_3725e2eb-b016-4241-8000-52d541dbd5d7.png

这时,热更新就是成功的了,更改less文件中的属性也是一样的。

src/index.less

body {
- background-color: red;
+ background-color: blue;
  transition: background-color .4s;
}

页面显示

企业微信截图_8054dca8-3429-42b7-849a-70641f72c12d.png

Webpack配置文件分离

在现在的webpack.config.js配置中,我们目前实现了dev开发环境的配置,但是对于开发与生产的环境来说,不应该是一样的配置,比如source map不应该在出现在生产环境中。webpack-dev-server也不应该出现在生产环境下。但是同时生产环境与开发环境有一些相同的配置,所以我们可以将webpack配置文件划分为三个文件。一个写有共同配置的文件与两个不同环境的配置文件,在使用的时候,将那个相同配置文件与开发环境的配置文件组合起来就是完整的开发环境配置。生产环境同样。这过程中,我们需要用到合并webpack配置文件的webpack-merge插件。

安装用于合并不同webpack配置文件的依赖

npm install --save --dev webpack-merge

project

  webpack-demo
  |- package.json
  |- package-lock.json
  |- /dist
    |- main.js
    |- main.js.LICENSE.txt
  |- /src
    |- icon.png
    |- index.less
    |- index.js
  |- postcss.config.js
  |- .browserslistrc
  |- index.html
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.build.js

webpack.common.js

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

module.exports = {
  entry: "./src/index.js", // 打包文件的入口,webpack将从这个文件开始分析整个项目的依赖结构
  output: {
    filename: "main.js", // 输出的文件名称
    path: path.resolve(__dirname, "dist"), // 输出的文件夹
  },
  module: {
    rules: [
      {
        test: /\.(css|less)$/i,
        use: [
          {
            loader: "style-loader",
          },
          {
            loader: "css-loader",
          },
          {
            loader: "postcss-loader",
          },
          {
            loader: "less-loader",
          },
        ],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
      },
      {
        test: /\.(png|svg|jpg|gif)$/i,
        type: "asset/resource",
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: "asset/resource",
      },
      {
        test: /\.(csv|tsv)$/i,
        use: ["csv-loader"],
      },
      {
        test: /\.xml$/i,
        use: ["xml-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "./index.html"), // ./index.html是作为模板生成dist/index.html
    }),
  ],
};

webpack.dev.js

const { merge } = require("webpack-merge");
const path = require("path");
const common = require("./webpack.common");

module.exports = merge(common, {
  target: "web",
  mode: "development",
  devtool: "inline-source-map",
  devServer: {
    contentBase: path.resolve(__dirname, "dist"),
    hot: "localhost",
    compress: true,
    port: 3045,
  },
});

webpack.build.js

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

module.exports = merge(common, {
  mode: "production",
});

package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
-   "build": "webpack",
+   "build": "cross-env NODE_ENV=production webpack --progress --config webpack.build.js",
-   "dev": "cross-env NODE_ENV=development webpack serve --progress --hot --config webpack.config.js"
+   "dev": "cross-env NODE_ENV=development webpack serve --progress --hot --config webpack.dev.js"
  },

分别运行npm run devnpm run build,表现正常。

babel配置

babel是用来使得我们所写的最新的js语法能够在不同浏览器上得到最大的支持。因为不同浏览器厂商对于es语法标准的支持程度不同,所以babel的存在就是在底层抹平了差异,使得我们可以在项目中使用一些新的语法而不用担心兼容问题。

让我们为webpack项目安装babel。

目前我们只需要最小集的智能预设集,就可以。所以用@babel/preset-env目前就是足够的,更多的配置可以点击babel官网进行配置。

project

  webpack-demo
  |- package.json
  |- package-lock.json
  |- /dist
    |- main.js
    |- main.js.LICENSE.txt
  |- /src
    |- icon.png
    |- index.less
    |- index.js
  |- postcss.config.js
  |- .browserslistrc
  |- index.html
  |- webpack.common.js
  |- webpack.dev.js
  |- webpack.build.js
+ |- babel.config.json

安装依赖

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

webpack.common.js

module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.xml$/i,
        use: ["xml-loader"],
      },
 +    {
 +      test: /\.js$/,
 +      exclude: /node_modules/,
 +      use: {
 +        loader: "babel-loader",
 +      },
 +    },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "./index.html"), // ./index.html是作为模板生成dist/index.html
    }),
  ],
};

安装@babel/preset-env依赖

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

babel.config.json

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

然后运行npm run dev,就可以开始写新语法的代码了。

支持react

配好babel后,其实配置react就变得并不麻烦了,因为babel具有将jsx语法转化成js的能力。所以我们只需要安装好react的核心库以及修改入口文件后,再将babel的配置修改成支持jsx语法,那么我们就可以在项目中写react了。

project

  webpack-demo
  |- package.json
  |- package-lock.json
  |- /dist
    |- main.js
    |- main.js.LICENSE.txt
  |- /src
    |- icon.png
    |- index.less
-   |- index.js
+   |- index.jsx
  |- postcss.config.js
  |- .browserslistrc
  |- index.html
  |- webpack.common.js
  |- webpack.dev.js
  |- webpack.build.js
  |- babel.config.json

安装react依赖

npm install --save react react-dom

src/index.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import './index.less';

const App = () => {
  return <div>App134</div>;
};

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

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Getting Started</title>
  </head>
  <body>
+    <div id="root"></div>
  </body>
</html>

增加root节点,这样可以使得react生成的dom挂载id为root的div上。

安装babel插件,使得react的jsx文件可以被处理

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

babel.config.json

{
-  "presets": ["@babel/preset-env"]
+  "presets": ["@babel/preset-env", "@babel/preset-react"]
}

增加react的预设集@babel/preset-react。

webpack.common.js

module.exports = {
-  entry: "./src/index.js", // 打包文件的入口,webpack将从这个文件开始分析整个项目的依赖结构
+  entry: "./src/index.jsx", // 打包文件的入口,webpack将从这个文件开始分析整个项目的依赖结构
  ...
  module: {
    rules: [
      ...
      {
-       test: /\.js$/,
+       test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
  ...
};

修改入口以及loader匹配到的文件格式。然后运行npm run dev,jsx文件被成功解析了。

支持ts

支持ts的思路也很简单,因为ts也是一样可以通过babel变成js,tsx也是一样。所以我们只需要在现有的babel设置上支持ts/tsx,那么就可以写ts/tsx文件。

peoject

  webpack-demo
  |- package.json
  |- package-lock.json
  |- /dist
    |- main.js
    |- main.js.LICENSE.txt
  |- /src
    |- icon.png
    |- index.less
-   |- index.jsx
+   |- index.tsx
  |- postcss.config.js
  |- .browserslistrc
  |- index.html
  |- webpack.common.js
  |- webpack.dev.js
  |- webpack.build.js

将src下的index.jsx文件更名为index.tsx即可。

安装依赖

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

webpack.common.js

module.exports = {
  output: {
    filename: "main.js", // 输出的文件名称
    path: path.resolve(__dirname, "dist"), // 输出的文件夹
  },
+  resolve: {
+    extensions: [".ts", ".tsx", ".js", ".json"],
+  },
  module: {
    rules: [
      ...
      {
        test: /\.xml$/i,
        use: ["xml-loader"],
      },
      {
-       test: /\.(js|jsx)$/,
+       test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
  ...
};

增加tsconfig配置文件

{
  "compilerOptions": {
      "outDir": "./dist/",
      "sourceMap": true,
      "noImplicitAny": true,
      "esModuleInterop": true,
      "module": "commonjs",
      "target": "es5",
      "noUnusedLocals": true,
      "jsx": "react"
  },
  "include": [
      "./src/**/*"
  ]
}

如果需要更多其他的配置,可以参考这个

注:这时编辑器打开src/index.tsx如果看到有报错

Could not find a declaration file for module 'react'. '/Users/*********/Desktop/study/webpack-demo/node_modules/react/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/react` if it exists or add a new declaration (.d.ts) file containing `declare module 'react';`ts(7016)

这里是在提示你安装react库的声明文件,也就是@types/react,我们只要按照声明上面写的去安装就好了。终端中执行npm i --save-dev @types/react。其他类似的报错也是一样的处理。这里只是举个例子,具体看大家自己的需要安装的包,这样的提示解决方法是相同的处理。npm i --save-dev @types/npm包名

至此,ts的支持环境就已经配好了,运行下试下吧。

到现在一个简单的webpack环境已经配置好了。当然现在这样还远远不够,还有一些配置需要继续,比如:

  • jest单元测试环境搭建
  • alias别名统一
  • eslint,stylelint语法配置
  • .prettierrc风格配置
  • husky触发lint-staged时期中的提交文件的修复与校验
  • mock.js环境搭建
  • webpack性能优化部分(构建,开发,输出等) 等等

后续会继续将这些完善。希望大家可以不懂的问题直接评论,或者提issue更好。所有提交记录都会放在git上。按照整个搭建的流程进行。希望可以帮助到大家。欢迎star,评论。你的点赞是我写作的最大动力。