Webpack - 核心概念

2018-11-27  本文已影响0人  李岩liyan

如果你稍微了解过 HTTP,肯定知道一大堆文件请求对于浏览器是什么样的灾难。然而,数以百计的 JS 文件和 CSS 文件在现在的 Web Applications 中已经是常态了,这么多的文件如果一个个通过 HTML 标签引入,别说浏览器,自己都要疯掉。

简单来说,Webpack 就是一个打包工具,它可以让你把众多文件,合并成一个。就像 Webpack 官网图里展示的那样。

bundle everything

对一个没有引入 Webpack 的项目来说,如果要实现这种效果,最先遇到的问题就是:

解决了这两个问题,我们就能在项目里开始使用 Webpack 了。

所有问题的答案,都可以在 Webpack 的配置文件 webpack.config.js 中找到。

基础概念

对于第一个问题,要怎么打包?
这是 Webpack 要考虑的问题,但同时我们也需要考虑 -- 怎么让 Webpack 知道把 哪些文件 打成一个 bundle?

Entry

以 JS 文件为例,通常来说,项目中的 JS 文件之间都是相互依赖的。你极有可能需要把 util.js 通过 import 引入另一个业务相关的文件 work.js 。那么 work.js 在被打包的时候, 它依赖的 util.js 也需要被打包在同一个 JS 文件中, 程序的逻辑才不会被破坏。
如果通过引用一直向上溯源的话,通常会找到命名类似 src/index.js 的文件,它直接或间接的引用了几乎所有的 JS 文件(Webpack 可以将文件打包为多个,这里我们只考虑打包为一个的情况,认为 index.js 引用了所有文件)。那么我们如果从 index.js 出发,将它所有直接或间接通过 import 引用的文件全部都打包进一个 JS 文件 ,那 index.js 就是我们打包的开始、起点、入口,Webpack 中称为:entry。

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

entry 可以接收字符串、对象和数组。

Webpack 认为,如果一个文件依赖于另一个文件(非代码资源,像是图片,Web 字体等),就存在 dependency。Webpack 从 entry 开始处理,递归地建立一个 dependency graph,包含工程所需的所有文件,最终将它们打包为一个文件供浏览器加载。

那这个文件最终会在哪,文件名又是什么呢?知道这些信息我们就可以在 index.html 中直接引用它。

Output

这个问题的答案当然是自己定,我们可以通过 output 属性指定将来打包完毕的文件所在的路径,以及文件名。Webpack 4 中的默认值为 ./dist/main.js ( dist : distribution ),配置方式:

const path = require('path');

module.exports = {
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
};

pathfilename 分别指定 bundle 文件所在文件夹的绝对路径的和文件名。

似乎问题已经解决了?是的,目前为止我们已经可以打包 JS 了。
只不过还有一些问题,比如我们需要打包的 JS 文件中不能使用 ES6 的特性,不能在 JS 中引入 CSS 文件(包括 SASS 文件),打包后文件数量数量少了但 bundle 文件所占空间还是比较大。

前面两个问题是打包途中就需要考虑的,如果 Webpack 不能处理引入的文件(CSS 文件 和 浏览器不能辨识的 ES6 JS 文件),那打包的过程势必会收到影响。
最后一个问题在 bundle 文件生成后处理就可以,比如压缩一下文件。

这两类问题都需要额外的工具协助。

对于第一类问题,我需要可以解析引入到 JS 的 CSS 文件的工具(css-loaderstyle-loader),以及将 ES6 标准下的 JS 转为浏览器能识别版本的工具( babel-loader )。 Webpack 将这些解析工具称为 loader。

Loaders

Webpack 只理解 javascript 和 json,Loaders 可以让 Webpack 将其他文件转化为 module,随后就可以加入 denpendency graph。

Loaders 有两个属性可以在 config 文件中配置:

对于上面的问题:

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

翻译一下:

对于第二类问题,对打包好的 bundle 文件的处理,比如优化、压缩等等。通常通过一些插件进行操作。

Plugins

比如常用的压缩,Webpack 内置这个插件。
这里我们更进一步,通过 Webpack 插件生成 HTML 文件,并且自动引入 bundle 之后的文件。显然,Webpack 需要知道生成的 HTML 文件是什么样子的,也就是它需要一个样板、模板( template ),我们假设程序的入口 HTML 文件是 src/index.html,通过插件 html-webpack-plugin 来进行这个操作。

使用 plugin 需要先通过 require 引入,再加入到 plugins 数组中。

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins

module.exports = {
  plugins: [
    new webpack.optimize.UglifyJsPlugin({minimize: true}),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

现在我们直接使用 dist/index.html 就好了, Webpack 已经打包了 JS、CSS 文件并一并注入了这个 HTML 文件。

Plugin 还可以执行更广泛的任务,如打包的优化,资源管理和环境变量注入。大多 plugin 都可以定制化。

汇总

一份包含上面所有概念的 webpack.config.js 看起来差不多是这样:

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

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      { 
        test: /\.js$/, 
        use: 'babel-loader' 
      },
      { 
        test: /\.css$/, 
        use: ['style-loader', 'css-loader'] 
      }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({minimize: true}),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

打包入口 entry ,输出路径 output,解析文件的 loader ,打包文件处理的插件 plugin。

今天就到这里了。

上一篇下一篇

猜你喜欢

热点阅读