webpack掠影-1

423 阅读7分钟

这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

一、什么是 webpack

webpack 是一个现代的 JS 的应用程序的静态模块打包器。当 webpack 处理应用程序时,它会递归的建立一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包到一个或者多个 bundle (扒自官方文档,并不是我懒,担心总结的没有官方到位.....)。

很显然官方文档说的很清楚,webpack 是用来打包的,还有一个重要的作用就是管理应用程序的模块依赖关系,而且是递归的构建依赖关系。为什么说这个呢,如果你是一个出生在工程化时代的 FE,自然不会懂得手动去管理 script 标签时代的我们是如何痛苦。

举个很简单的例子,如果库 A 的运行需要,库 B ,那么一定要保证在 A 之前通过 script 引入 B,否则 A 不能正常运行。如果有个几十个,岂是苦不堪言可以形容;而有了 webpack就不用了,因为 webpack 实现了模块化,我们通过对应的模块化方式使用模块即可,至于谁先谁后,这个工作交给 wepback 去做;

当然,人家的功能很多,我们先用用再说

二、安装 webpack

webpack 4.x 的版本在安装 webpack 的同时还需要安装 webpack-cli;

npm install webpack webpack-cli

三、 配置使用 webpack

当然,webpack 支持零配置的开箱使用体验,但是学习 webpack 一个十分重要的事情就是学习如何编写 webpack 的配置文件。当然,最终目的是学习 webpack 的原理。当然,学会跑之前,我们先学会走。我们先来看几个 webpack 的重要概念:

3.1 入口和出口

  • 入口(entry):指示 webpack 从哪个模块开始构建依赖;简言之,告诉 webpack 哪里是头,从哪儿开始查找依赖关系;

  • 出口(output):告诉 webpack 在哪里输出它打包后的文件(bundles),以及如何命名这些文件;其默认值是 ./dist。

  • 示例项目结构:

-
│  webpack.config.js
│
└─src
        index.html
        index.js
  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<script src="../dist/bundle.js"></script>
</body>
</html>
  • index.js
document.addEventListener('DOMContentLoaded', function () {
  let div = document.createElement('div');
  div.innerHTML = 'Hello World';
  document.body.appendChild(div);
  div = null;
});

配置 webpack 需要在项目根目录下新建一个 ·· 的文件,webpack 是基于 Node.js 的,其配置文件是一个基于 CommonsJS 的模块;

  • 示例:
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filelname: 'bundle.js'
  }
}

现在我们已经配置了一个最最简单的配置文件,接下来我们需要在命令行工具(cmd/terminal/powershell)运行命令:

webpack

当然,我们也可以在 package.json 中配置两个脚本命令,如下的 dev 和 build:

{
  "name": "test",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
   "scripts": {
      "dev": "webpack-dev-server",
      "build": "webpack"
   },
  "devDependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  }
}

  • 打包后输出的文件:

wbp1-output1.png

  • 打包后启动浏览器查看:

wbp1-helloworld.png

经过上面的过程,我们已经发现在项目的 dist 目录下已经有一个 bundle.js 的文件,而且运行 html 文件,我们也得到我们预期的效果;

但是有一个问题:index.html 的 bundle.js 文件仍然使我们自己引入的,这仍然不方便,所以接着我们需要一个叫做 html-webpack-plugin 的插件,它的作用就是把打包出来的 js 自动引入到 html 文件中;

3.2 插件

插件可以扩展 webpack 的功能,让它可以完成跟多的任务,例如上面我们需要的能力,自动把打包后的 js 插入到 HTML 文件;

插件大多数都需要安装,也有一部分是 webpack 自带的插件,这些自带的插件随着 webpack 的安装也已经安装好了。

  • 安装 html-webpack-plugin 插件
npm install html-webpack-plugin
  • 使用插件需要在 webpack 配置文件中配置 plugins 属性:plugins 是一个数组,数组项是每个插件类的一个实例(new 插件类):

html-webpack-plugin 接收一些配置,如下

  • template: string 需要引入输出文件的 html 文件模板
  • hash: boolean 给引入的 js 加hash
  • filename: string 输出的 html 文件的名字(插件会把模板 html 复制到输出目录中)
  • minify: Object 压缩优化输出的 html 文件配置
    • removeAttributeQuotes: true, 删除行内属性的双引号
    • collapseWhitespace: true 删除换行,使内容保持在一行
const path = require('path');

+ const HTMLWebpackPlugin = require('html-webpack-plugin'); // 插件在使用前需要引入


module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
+  plugins: [
+    new HTMLWebpackPlugin({
+      template: './src/index.html', // 需要引入输出文件的 html 文件模板
+      hash: true, // 给引入的 js 加hash
+      filename: 'index.html', // 输出的 html 文件的名字(插件会把模板 html 复制到输出目录中)
+      minify: { // 压缩优化输出的 html 文件配置
+        removeAttributeQuotes: true, // 删除行内属性的双引号
+        collapseWhitespace: true // 删除换行,使内容保持在一行
+      }
+    })
+  ]
}

修改陪之后,重新执行打包命令:npm run build

  • 产出的 html 文件:

wbp1-plugin-cp-html-template.png

  • html 文件详情

wbp1-output-template.png

插件还有很多,包括后面的很多优化都需要依赖插件的功能,我们这里先介绍一个,后面还要有其他功能插件;

3.3 webpack-dev-server

我们在开发的过程中,发现每次都执行 npm run build 产出文件,然后再查看效果,这样做很低效。接着,我们介绍一个实用的功能,webpack-dev-server;它可以帮我们打开发环境的包,配置开发代理,文件修改后自动编译,然后通知浏览器刷新等功能;

  • 安装 webpack-dev-server
npm install webpack-dev-server
  • 配置 webpack-dev-server
  • devServer: Object
    • contentBase: string, 静态资源文件目录
    • port: number 开发服务器启动的端口
    • open: boolean 是否自动启动浏览器
    • progress: boolean 是否展示进度条
    • proxy: Objct 配置代理
      • '/api': Object 将带有 /api 的请求,转发值 target 指向的域名
const path = require('path');

const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
+  devServer: {
+    contentBase: './build', 
+    port: 3000, 
+    open: true,
+    progress: true, 
+    proxy: { // 配置代理
+      '/api': { 
+        target: 'http://domain.com/somet/api',
+        changeOrigin: true,
+        secure: false
+      }
+    }
+  },
  plugins: [
    new HTMLWebpackPlugin({
      template: './src/index.html', 
      hash: true,
      filename: 'index.html', 
      minify: { 
        removeAttributeQuotes: true, 
        collapseWhitespace: true 
      }
    })
  ]
}

  • 配置启动开发服务器 需要注意的是,之前我们打包都是执行 npm run build (在 package.json scripts 中配置的),但是启动开发服务器需要通过命令:webpack-dev-server 命令启动,同样我们将这个命令配置称为一个 script,名为 dev
npm run dev

执行命令后为,开始编译,浏览器自动启动;

wbp1-dev-server.png

3.4 样式处理和 loader

webpack 同样可以帮我们处理样式文件,包含 css 和 css 预处理文件;但是 webpack 自身只能理解 JS,所以处理这些非 JS 的文件需要借助 loader 的能力。loader 可以将将非 JS 文件处理成有效的模块,然后利用 webpack 的打包能力对其进行处理;

  • 我们在 src 目录下新建 index.css 文件
div {
  color: #fff;
  background: deeppink;
  font-size: 24px;
  font-weight: bold;
}
  • 在 js 中导入 css 文件
+ import './index.css';

document.addEventListener('DOMContentLoaded', function () {
  let div = document.createElement('div');
  div.innerHTML = 'Hello World';
  document.body.appendChild(div);
  div = null;
});

  • 安装 loader,loader 也需要单独安装;
yarn add css-loader style-loader
  • 配置文件,配置 loader 需要配置 module 字段,module 是一个对象,对象下有一个 rules 字段,是一个数组;数组中每一项又是一个对象,对象中一般有两个字段:
    • test: 表示 loader 要处理文件的扩展名的正则
    • loader: 处理对应文件的 loader
const path = require('path');

const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
   
    contentBase: './build', 
    port: 3000, 
    open: true,
    progress: true, 
    proxy: { 
      '/api': { 
        target: 'http://domain.com/somet/api',
        changeOrigin: true,
        secure: false
      }
    }
  },
+  module: {
+    rules: [
+      {
+        test: /.css$/,
+        use: ['style-loader', 'css-loader'] // 顺序不可颠倒,loader 在执行时是从右向左执行的
+      }
+    ]
+  },
  plugins: [
    new HTMLWebpackPlugin({
      template: './src/index.html', 
      hash: true, 
      filename: 'index.html', 
      minify: { 
        removeAttributeQuotes: true, 
        collapseWhitespace: true 
      }
    })
  ]
}

  • 打包结果

wbp1-loader-css.png

细心的朋友你一定发现了,webpack 是以内嵌的方式把 css 以 style 标签的形式插入到页面的 head 标签内,那么如何把它抽离成单独的 css 文件呢?下一篇我们会详细讲述这是过程