webpack 学习日记

211 阅读2分钟

一、什么是webpack

webpack是将静态模块根据依赖关系打包成一个或多个bundle的前端打包工具。 它有四个核心概念:

  • 入口(entry)
  • 输出(output)
  • 加载器(loader)
  • 插件(plugin)

1.入口

入口是webpack构建依赖图的起点。在webpack配置中,entry属性可以指定一个入口起点(或多个入口起点),其默认值为./src

module.exports = {
  entry: './src/main.js'
}

2.输出

webpack在处理完依赖并且打包后,会将bundles保存到配置好的的输出位置,同时可以指定生成文件的名称和生成规则。output属性中默认输出路径为./dist

const path = require('path')

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name]:[hash:12].bundle.js'
  }
}

3.加载器

加载器的主要作用是去处理非JavaScript文件的资源,将这些资源转换成webpack可以处理的模块,比如说css文件、图片等。在module.rules中定义loader,它有两个主要的配置项:

  1. test属性,文件名匹配规则,用于标识出需要被该loader处理的文件;
  2. use属性,表示进行转换时,应该使用哪些 loader,它可以是一个字符串、一个对象或者是一个数组(loader 支持链式传递,多个loader的执行顺序为从右到左)。
module.exports = {
  module: {
    rules: [
      { test: /\.css$/, use: ['style-loader', 'css-loader'] }
    ]
  }
}

4.插件

插件可以用于执行范围更广的任务,它可以在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要做的事情。

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({ template: './public/index.html' })
  ]
}

二、初始化webpack项目

首先新建一个文件夹webpack-demo,然后初始化npm,安装 webpack依赖,如果使用 webpack 4+ 版本,还需要安装 webpack-cli 依赖。

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install webpack webpack-cli -D

安装完成后自动生成package.json文件。

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.42.0",
    "webpack-cli": "^3.3.11"
  }
}

接下来新建文件src/index.js

// index.js
console.log('Hello Webpack!')

webpack 4+ 版本是开箱即用的,在不引入任何配置文件的情况下就可以使用。这里直接使用命令行进行构建打包npx webpack --mode=development,mode选项默认为production,配置成development环境可以更清楚地查看打包后的代码。

运行命令之后,默认会生成dist目录,并且其中有打包之后产生的文件main.js。查看文件内容,可以在最后看到源代码中的表达式,它用 eval 包裹起来执行,并且会在末尾追加注释//# sourceURL=webpack:///./src/index.js?。直接运行这个脚本会在终端打印输出Hello Webpack!

// main.js
...
eval("console.log('Hello Webpack!')\n\n//# sourceURL=webpack:///./src/index.js?");
...

三、在浏览器中运行

首先要在浏览器中显示,那么必须得使用html文件,webpack中有专门处理html的插件html-webpack-plugin,它可以将打包后的文件引入到html模板中。

npm i html-webpack-plugin -D

因为这里用到了插件,所以需要在项目根目录创建webpack.config.js配置文件。

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash:12].js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      title: 'Webpack'
    })
  ]
}

新建文件public/index.html,使用EJS的模板语法,获取title配置项。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><%= (htmlWebpackPlugin.options.config.title) %></title>
</head>
<body>
</body>
</html>

再一次运行npx webpack --mode=development命令,可以发现在dist目录下又生成了两个文件index.htmlmain.*.jsindex.html中的title已经被替换了,并且在body中引入了main.*.js文件。

<!DOCTYPE html>
<html lang="zh_CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Webpack</title>
</head>
<body>
<script type="text/javascript" src="main.60b684380f56.js"></script></body>
</html>

为了能够在在浏览器中实时展示效果,还需要安装webpack-dev-server

npm i webpack-dev-server -D

因为一般需要设置NODE_ENV,以此来区分不同环境下的逻辑行为。其次为了解决跨平台问题,再安装一下cross-env

npm i cross-env -D

然后修改package.json中的可执行脚本,dev启动本地web服务进行预览,build进行打包。

...
"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server",
    "build": "cross-env NODE_ENV=production webpack"
},
...

最后运行npm run dev,打开浏览器访问http://localhost:8080,就可以看到页面了。尝试修改index.html文件,会发现浏览器中的页面也会同时更新。

还可以在webpack.config.js中配置webpack-dev-server

module.exports = {
  //...
  devServer: {
    // 端口号
    port: '3000',
    // 终端仅打印 error
    stats: "errors-only",
    // 日志等级
    clientLogLevel: "silent",
    // 是否启用 gzip 压缩
    compress: true
  }
}

四、mode和devtool

接下来是配置里比较重要的两个配置项modedevtool

1. mode

在 webpack 4+ 中可以通过mode选项为webpack指定一些默认的配置,分为development/production,默认是production

选项 描述
development process.env.NODE_ENV 的值设置为 development,启用 NamedChunksPluginNamedModulesPlugin
production process.env.NODE_ENV 的值设置为 production,启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginUglifyJsPlugin

2. devtool

设置好devtool可以为开发调试带来很大帮助,这里有几个常见的关键字eval,source-map,cheap,module。具体配置内容可以查看官方文档

  • eval:每个模块用eval执行,并且存在@sourceUrl,在打包的时候,生成的bundle.js文件,模块都被eval包裹,并且后面跟着sourceUrl,指向的是原文件index.js,调试的时候,就是根据这个sourceUrl找到的index.js文件的。
  • source-map:生成一个带有.map文件,这个map文件会和原始文件做一个映射,调试的时候,就是通过这个.map文件去定位原来的代码位置。
  • cheap:低消耗打包,在打包的时候map文件不会保存原始代码的列位置信息,只包含行位置信息。
  • module:调试的代码不会被转换,会保留原始代码语法。

五、添加babel

首先要通过babel转化代码,那么就要先安装对应的loader

npm i babel-loader -D

会提示peerDependencies WARNING,意思是正常运行babel-loader还有依赖需要添加。这里添加对应的依赖

npm i @babel/core -D

为了转化 es6 代码,要安装 babel 插件

npm i @babel/preset-env @babel/polyfill -D

减少包体积、防止全局污染,要安装

npm i @babel/plugin-transform-runtime -D

安装@babel/runtime-corejs3

npm i @babel/runtime-corejs3 -D

webpack.config.js中添加对应配置

module.exports = {
  //...
  module: {
    rules: [
      { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
    ]
  },
  //...
}

然后配置babel,在项目根目录创建配置文件.babelrc

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3,
        "targets": {
          "browsers": ["last 2 versions", "ie >= 10"]
        }
      }
    ]
  ]
}

六、处理样式文件和资源文件

1. 处理样式文件

使用 sass 预处理器

npm i style-loader css-loader sass node-sass sass-loader postcss-loader autoprefixer -D

webpack.config.js中添加对应配置

module.exports = {
  //...
  module: {
    rules: [
      //...
      {
        test: /\.(sc|c)ss$/,
        use: ['style-loader', 'css-loader', {
          loader: 'postcss-loader',
          options: {
            plugins() {
              return [
                // 自动添加前缀
                require('autoprefixer')({
                  "overrideBrowserslist": [
                      ">0.25%",
                      "not dead"
                  ]
                })
              ]
            }
          }
        }, 'sass-loader'],
        exclude: /node_modules/
      }
    ]
  },
  //...
}

2. 处理资源文件

安装url-loader处理本地资源文件,url-loader可以指定在文件大小小于指定的限制时返回DataURL

npm i file-loader url-loader -D

webpack.config.js中添加对应配置

module.exports = {
  //...
  module: {
    rules: [
      //...
      {
        test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              // 资源大小小于 10K 时,将资源转换为 base64
              limit: 10240,
              esModule: false,
              outputPath: 'assets',
              name: '[name]_[hash:6].[ext]'
            }
          }
        ],
        exclude: /node_modules/
      }
    ]
  },
  //...
}

然后就可以在 css 文件中引入本地图片了,html中引入图片方法<img src={require('XXX.jpg')} />

七、自动清空dist目录

安装依赖

npm install clean-webpack-plugin -D

配置插件

const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
  //...
  plugins: [
    new CleanWebpackPlugin() 
  ],
  //...
}

这样每次 build 的时候就会自动清空 dist 目录了。