快速简明教程: webpack

2009 年, node.js 的诞生, 实现了前端语言向后端的转变。此处省略 1000 字,具体内容请参考 node.js 简史。

通常前端代码都是在浏览器上运行的,但每家浏览器支持力度不同,有些支持新的 JS 语法,有些支持老的 JS 语法。为了实现, 相同一套代码能够到处运行的效果,就出现了前端工具链。工具链的目的就是将代码转译成各个浏览器都支持的脚本。

就前端浏览期打包工具中, webpack 算较为流行的一个。

快速开始

话不多说,现在就快速开始一个 webpack 打包项目。

yarn 安装

首先,全局安装 yarn命令, 虽然项目依赖包可以直接通过 npm install 进行安装。但是,yarn 在安装性能上要优于 npm原始安装命令。

$: npm install yarn --global

常用yarn 安装选项 --dev-D,用于安装开发依赖。

注意 同一个项目中,不要混用 yarnnpm

即选择了一种,就不要再项目中混用另外一种安装命令。减少项目依赖发生不一致。

项目初始化

$: mkdir demo
$: cd demo
$: yarn init -y

快速打包

$: yarn add -D webpack webpack-cli

现在开始实现一个简单的 Hello World 测试。创建一个源码文件 src/index.js.

console.log("hello webpack!");

现在项目的目录结构如下:

$: tree . -L 2
.
├── node_modules
    ...
├── package.json
├── yarn.lock
└── src
    └── index.js

打开项目文件 package.json, 在 scripts中加入打包命令:

{
  ...
  "scripts": {
    "build": "webpack"
  },
  ...
}

执行命令:yarn build, 项目就按默认方式执行了打包。但是,打包过程日志中又这样的警告信息:

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

webpack 提示我们需要设置打包模式。设置打包模式可以通过配置文件,也可以通过命令行选项进行设置。这里,我们快速通过命令行选项进行设置。 修改package.json文件,将执行脚本进行如下修改:

{
  ...
  "scripts": {
    "prd": "webpack --mode=production",
    "dev": "webpack --mode=development"
  },
  ...
}

执行, yarn prdyarn dev ,警告信息不再出现。

对比两种模式下的打包结果,可以发现,在production模式下,打包的结果文件更加的小,而在development模式下,文件中会添加很多注释信息。关于如何优化最终的发布文件,本文暂不讨论。

打包配置

了解了快速打包过程,现在我们开始进行打包的个性化配置。

默认情况下,webpack 会查找项目目录下的 webpack.config.js 文件作为其配置。也可以通过命令行选项进行具体配置文件的指定。

这里,我们使用默认的方式。在项目根目录下创建webpack.config.js 文件。添加两个基础配置:

  • 输入配置
  • 输出配置

如下:

const path = require("path");

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

entry 的定义中,可以仅定义一个入口文件,如 main 键值对应的 src/index.js源文件。也可以定义多个入口文件, 如:

const path = require("path");

module.exports = {
  entry: {
    home: path.resolve(__dirname, "./src/index.js"),
    post: path.resolve(__dirname, "./src/post.js"),
    about: path.resolve(__dirname, "./src/about.js"),
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "[name].js",
  },
};

此时,在输出文件的文件名设置上,就不可以使用固定的文件名。而需要使用类似通配符的[name]的方式,这样在输出文件时,文件名将按照键值生成。结果就是: home.js,post.js,about.js.

执行yarn prdyarn dev命令测试效果。

React 项目实践

真正让webpack强大的是其丰富的插件功能。这里我们通过手动配置一个 React 项目作为插件配置的学习目标。

babel 插件

真正让 webpack 变强大的是打包插件的引入。最常用也最关键的打包插件就是 babel 插件,让最新的 JS 版本,通过该插件转码后,能够运行在各类浏览器上。

插件安装:

yarn add -D babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-class-properties

如果项目使用 typescript开发,需要安装 typescript-loader插件。

因为项目使用 react 开发,还需要安装 @babel/preset-react 插件。

yarn add -D @babel/preset-react

其中插件 babel-loader 还需要进行想要的配置。在项目根目录下,创建.babelrc文件。进行想要的设置如下:

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react" //如果项目使用 react 开发
    ],
    "plugins": [
        "@babel/plugin-proposal-class-properties"
    ]
}

安装完以上插件以后,我们不妨写一个简单的 react 项目测试。

react 项目实践

为了在项目开发过程中使用 react, 我们还必须对 react 组件库进行安装。

$: yarn add react react-dom

打开 src/index.js, 写一个最简单的 React 测试:

import React from "react";
import ReactDOM from "react-dom";

const name = "Josh Perez";
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(element, document.getElementById("root"));

保存。现在,如果直接yarn prd 打包,将会出现如下异常,因为,webpack 中还没有相应模块配置。

ERROR in ./src/index.js 4:21
Module parse failed: Unexpected token (4:21)
...

添加上 webpack 模块配置:

const path = require("path");

module.exports = {
  entry: {
    main: path.resolve(__dirname, "./src/index.js"),
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "main.js",
  },
  module: {
    rules: [
      //JS加载模块配置
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ["babel-loader"],
      },
    ],
  },
};

重新执行 yarn prd 命令就可以完成脚本打包。

最后,我们写一个 HTML 页面dist/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>Document</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./main.js" defer></script>
  </body>
</html>

如果成功,你就完成了一个从零开始配置的 react 项目。

html 插件

为了测试 React 脚本,上面使用的是手动创建的 HTML 页面。其实,webpack 提供了一个简单 html 插件,可以自动创建一个 HTML 页面。我们提供一个见 HTML 模板即可。

首先,安装插件 yarn add -D html-webpack-plugin

在源码文件夹,创建一个简单 HTML 模板文件 src/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>

  <body>
    <div id="root"></div>
  </body>
</html>

再增加相应的打包插件配置:

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

module.exports = {
  entry: {
    main: path.resolve(__dirname, "./src/index.js"),
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "main.js",
  },
  module: {
    rules: [
      // JavaScript
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ["babel-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "webpack 101",
      template: path.resolve(__dirname, "./src/index.html"), // template file
      filename: "index.html", // output file
    }),
  ],
};

在执行打包命令前,先将 dist/index.html 文件删除。执行打包命令后,发现 dist/index.html 按模板生成了。

清理插件

经过简单的测试,现在 dist 目录里存在各种不同的打包输出文件。为了保证每次打包前,dist 目录都是空的。不妨再安装一个清理插件: clean-webpack-plugin.

安装命令: yarn add -D clean-webpack-plugin

增加插件配置:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  entry: {
    main: path.resolve(__dirname, "./src/index.js"),
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "main.js",
  },
  module: {
    rules: [
      // JavaScript
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ["babel-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "webpack 101",
      template: path.resolve(__dirname, "./src/index.html"), // template file
      filename: "index.html", // output file
    }),
    new CleanWebpackPlugin(),
  ],
};

执行打包命令后,发布目录dist, 现在只有一个main.js,之前的发布文件都被清理干净了。

开发环境插件

频繁的执行打包命令,在运行页面,查看效果。这样重复的工作令人厌倦,特别的是开发环境。所以,我们还需要一个更加友好的开发环境,免去我们以上的重复工作。这个插件,就是 webpack-dev-server.

安装插件: yarn add -D webpack-dev-server.

将默认的 webpack.config.js 模式修改为 development 模式,同时增加 dev-server配置,并启用 webpack.HotModuleReplacementPlugin插件,实现开发环境模块的热加载。

const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  entry: {
    main: path.resolve(__dirname, "./src/index.js"),
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "main.js",
  },
  module: {
    rules: [
      // JavaScript
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ["babel-loader"],
      },
    ],
  },
  mode: "development",
  devServer: {
    historyApiFallback: true,
    contentBase: path.resolve(__dirname, "./dist"),
    open: true,
    compress: true,
    hot: true,
    port: 8088,
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "webpack 101",
      template: path.resolve(__dirname, "./src/index.html"), // template file
      filename: "index.html", // output file
    }),
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin(),
  ],
};

最后,再 package.json 包设置中,增加一个新的命令, 启用该开发服务。

{
  ...
  "scripts": {
    "start": "webpack serve",
    "prd": "webpack --mode=production",
    "dev": "webpack --mode=development"
  },
  ...
}

执行命令: yarn start, 现在我们可以实时修改源码,webpack 会自动帮我们实现开发模式的打包,并热加载模块。

访问开发服务端口: http://localhost:8088.

应用打包 vs 库打包

迄今位置,整个打包过程都非常顺利。只需要在 HTML 页面中设置要对应的元素 ID,并引入打包脚本,程序就可以很好的工作。这种打包方式属于应用打包。HTML 页面的写法与脚本直接必须严格配套使用,HTML 页面中必须设置脚本中定义的元素 ID。 这种方式很好,但还不够完美。

我希望 HTML 页面与打包脚本直接的耦合度松散一些。可以通过设置参数的方式设置元素 ID。

为了实现参数功能,我们修改src/index.js源代码,增加一个导出函数 Greet. 提供两个参数设置:

  • selector: 设置具体渲染的 html dom 节点
  • name: 名称设置

代码如下:

import React from "react";
import ReactDOM from "react-dom";

export function Greet(selector, name) {
  const element = <h1>Hello, {name}</h1>;
  const dom = document.querySelector(selector);
  if (dom) {
    ReactDOM.render(element, dom);
  } else {
    ReactDOM.render(
      <h1>{selector} not found</h1>,
      document.querySelector("body")
    );
  }
}

再将打包目标设置为 library 库类型即可.修改 webpack.config.js配置:

module.exports = {
  ...
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "welcome.js",
    library: 'welcome', //设置输出为 library, 同时,命名为 welcome
  },
  ...
};

完成以上配置后,我们修改 HTML 页面中的调用方式:

<body>
  <div id="helo"></div>
  <script src="./welcome.js"></script>
  <script>
    welcome.Greet("#helo", "JayL");
  </script>
</body>

运行 yarn start, 查看效果。

小结

通过该简明教程,我们完成了 webpack 的快速打包,从零开始搭建一个 React 项目,并实现了一个简单的库的打包输出。希望本文对大家有所裨益。

← 快速简明教程: es6 None

评论