webpack的四个核心概念介绍

前言

webpack 是一个当下最流行的前端资源的模块打包器。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序须要的每一个模块,而后将全部这些模块打包成少许的bundle - 一般只有一个,由浏览器加载。css

webpack.png

它是高度可配置的,咱们先理解四个核心概念:入口(entry)、输出(output)、loader、插件(plugins)html

入口(entry)

webpack 建立应用程序全部依赖的关系图。图的起点被称之为入口起点(entry point)。入口起点告诉 webpack 从哪里开始,并根据依赖关系图肯定须要打包的内容。能够将应用程序的入口起点认为是根上下文或 APP第一个启动文件。前端

简单规则:每一个 HTML 页面都有一个入口起点。单页应用(SPA):一个入口起点,多页应用(MPA):多个入口起点。node

简单语法

用法:entry: string | Arrary<string>webpack

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

entry属性的单个入口语法,在扩展配置的时候有失灵活性。它是下面的简写:web

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

entry传入一个数组时候,将建立"多个主入口(multi-main entry)"。在你想要多个依赖文件一块儿注入,而且将他们的依赖导向(graph)到一个"chunk"时候,传入数组的方式就颇有用。shell

//webpack.config.js
module.exports = {
  entry:  [
    './src/index.js',
    'babel-polyfill',
  ]
};

对象语法

用法: entry: {[entryChunkName: string]: string|Array<string>}npm

//webpack.config.js
module.export = {
  entry: {
    app: './src/app.js',
    vendors:  './src/vendors.js'
  }
};

对象语法会比较繁琐。然而,这是应用程序中定义入口的最可扩展的方式。json

这个配置告诉咱们 webpack 从 app.jsvendors.js 开始建立依赖图。这些依赖图是彼此彻底分离、互相独立的。这种方式比较常见于,只有一个入口起点(不包括 vendorvendor通常都是动态加载的第三方模块。动态加载的模块不是入口起点。)的单页应用程序(single page application)中。不过,为了支持提供更佳 vendor 分离能力的 DllPlugin。 官方如今不太建议将第三方模块放到entry.vendors中。api

对象语法,更常见的应该是多入口应用程序-多页应用(MPA)。

//webpack.config.js
module.export = {
  entry: {
    home: "./home.js",
    about: "./about.js",
    contact: "./contact.js"
  }
};

此配置,告诉 webpack,咱们 须要 3 个独立分离的依赖关系图.
在多页应用中,每当页面跳转时,服务器将为你获取一个新的 HTML 文档。页面从新加载新文档,而且资源被从新下载。

根据经验:每一个 HTML 文档只使用一个入口起点。

输出(output)

将全部的资源(assets)归拢在一块儿后,还须要告诉 webpack 在哪里打包应用程序。webpack 的 output 属性描述了如何处理归拢在一块儿的代码(bundled code)。output选项能够控制webpack如何向硬盘写入编译文件。注意,即便能够存在多个entry起点,但只指定一个output配置。

简单用法

在 webpack 中配置output 属性的最低要求是,将它的值设置为一个对象,包括如下两点:

  1. filename 用于输出文件的文件名。
  2. 目标输出目录 path 的绝对路径。
//webpack.config.js
module.exports = {
  entry: './src/index.js',
  output: {
    path: './home/proj/public/assets',
    filename: 'bundle.js'
  }
};

此配置将一个单独的 bundle.js 文件输出到 ./home/proj/public/assets 目录中。

多个入口起点

若是配置建立了多个单独的 "chunk"(例如,使用多个入口起点或使用像 CommonsChunkPlugin 这样的插件),则应该使用占位符来确保每一个文件具备惟一的名称。

{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js', //使用占位符
    path: __dirname + '/dist'
  }
}

// 写入到硬盘:./dist/app.js, ./dist/search.js

loader

webpack的目标是,让webpack聚焦于项目中的全部资源(asset), 而浏览器不须要关注考虑这些。 webpack把每一个文件(.css, .html, .scss, .jpg, etc.)都做为模块处理。然而webpack自身只理解JavaScript。
webpack loader 在文件被添加到依赖图中时,将文件源代码转换为模块。

loader可使你在import或"加载"模块时预处理文件。所以,loader相似于其余构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。

在更高层面,在webpack的配置中loader有两个目标:

  1. 识别出应该被对应的loader进行转换的那些文件。(test属性)
  2. 转换这些文件,从而使其可以被添加到依赖图中(而且最终添加到bundle中)。(use属性)
//webpack.config.js
const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { 
        test: /\.txt$/,
        use: 'raw-loader' 
      }
    ]
  }
};

以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:testuse。这至关于告诉 webpack 编译器,当碰到「在 require()/import 语句中被解析为 .txt 的路径」时,在对它打包以前,先使用 raw-loader 转换一下。

在webpack配置中定义loader时,要定义在module.rules中,而不是rules。若是不这么作,webpack会给出严重警告

示例

例如,你可使用loader告诉webpack加载css文件,或者将TypeScript转为JavaScript。为此,首先安装相对应的loader:

npm install -D css-loader
npm install -D ts-loader

而后指示webpack对每一个.css使用css-loader, 以及对全部.ts文件使用ts-loader:

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

根据配置选项,下面的规范定义了同等的loader用法:

{test: /\.css$/, use: 'css-loader'}
// 等同于
{test: /\.css$/, use: [{
  loader: 'css-loader'
}]}
// 等同于
{test: /\.css$/, loader: 'css-loader'}

module.rules.loadermodule.rules.use: [ { loader } ] 的简写。

在应用程序中,有三种使用loader的方式:

  • 配置(推荐):在webpack.config.js文件中指定loader。
  • 内联: 在每一个imort语句中显示指定loader。
  • CLI: 在shell命令中指定它们。

配置

module.rules 容许你在 webpack 配置中指定多个 loader。 这是展现loader的一种简明方式,而且有助于使代码变得简洁。同时让你对各个loader 有个全局概览:

module: {
    rules: [{
        test: /\.css$/,
        use: [
            'style-loader',
            { loader: 'scss-loader' },
            {
                loader: 'css-loader',
                options: {
                    modules: true
                }
            }
        ]
    }]
}

内联

能够在 import 语句或任何等效于 "import" 的方式中指定 loader。使用 ! 将资源中的 loader 分开。分开的每一个部分都相对于当前目录解析。

import Styles from 'style-loader!css-loader?modules!./styles.css'

经过前置全部规则及使用!, 能够对应覆盖到配置中的任意loader
选项能够传递查询参数,例如 ?key=value&foo=bar,或者一个 JSON 对象,例如 ?{"key":"value","foo":"bar"}

尽量使用 module.rules,由于这样能够减小源码中的代码量,而且能够在出错时,更快地调试和定位 loader 中的问题。

CLI

你也能够经过CLI使用loader:

webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'

这会对 .jade文件使用 jade-loader,对 .css文件使用 style-loader和 css-loader

Loader特性

  • loader 支持链式传递。可以对资源使用流水线(pipeline)。一组链式的 loader 将按照前后顺序进行编译。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。
  • loader 能够是同步的,也能够是异步的。
  • loader 运行在 Node.js 中,而且可以执行任何可能的操做。
  • loader 接收查询参数。用于对 loader 传递配置。
  • loader 也可以使用options 对象进行配置。
  • 除了使用 package.json 常见的 main 属性,还能够将普通的 npm 模块导出为loader,作法是在 package.json 里定义一个 loader 字段。
  • 插件(plugin)能够为 loader 带来更多特性。
  • loader 可以产生额外的任意文件。
    loader 经过(loader)预处理函数,为 JavaScript 生态系统提供了更多能力。

解析Loader

loader 遵循标准的模块解析。多数状况下,loader 将从模块路径(一般将模块路径认为是npm install, node_modules)解析。
loader 模块须要导出为一个函数,而且使用 Node.js 兼容的 JavaScript 编写。一般使用 npm 来管理,也能够将自定义 loader 做为应用程序中的文件。按照约定,loader 一般被命名为 xxx-loader(例如 json-loader)。

插件-plugins

插件是 wepback 的支柱功能。webpack 自身也是构建于-你在 webpack 配置中用到的相同的插件系统之上!
插件目的在于解决 loader没法实现的其余事
因为loader仅在每一个文件的基础上执行转换,而插件(plugins) 经常使用于(但不限于)在打包模块的 “compilation” 和 “chunk” 生命周期执行操做和自定义功能。想要使用一个插件,你只须要 require() 它,而后把它添加到 plugins 数组中。
多数插件能够经过选项(option)自定义。你也能够在一个配置文件中由于不一样目的而屡次使用同一个插件,这时须要经过使用 new 来建立它的一个实例。

剖析

webpack 插件是一个具备 apply 属性的 JavaScript 对象。apply
 属性会被 webpack compiler 调用,而且 compiler 对象可在整个编译生命周期访问。经过Function.prototype.apply方法,你能够把任意函数做为插件传递(this 将指向compiler)。咱们能够在配置中使用这样的方式来内联自定义插件。

//ConsoleLogOnBuildWebpackPlugin.js
function ConsoleLogOnBuildWebpackPlugin() {

};

ConsoleLogOnBuildWebpackPlugin.prototype.apply = function(compiler) {
  compiler.plugin('run', function(compiler, callback) {
    console.log("webpack 构建过程开始!!!");

    callback();
  });
};

用法

因为插件能够携带参数/选项,你必须在webpack配置中,向plugins属性中传入new实例。
根据webpack的用法,能够有多种方式使用插件。可是,经过配置的方式使用是比较推荐的作法。

配置 (推荐)

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

const config = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]0
};

module.exports = config;

Node API(不推荐)

即使使用 Node API,用户也应该在配置中传入 plugins 属性。compiler.apply并非推荐的使用方式。

// some-node-script.js
  const webpack = require('webpack'); //访问 webpack 运行时(runtime)
  const configuration = require('./webpack.config.js');

  let compiler = webpack(configuration);
  compiler.apply(new webpack.ProgressPlugin());

  compiler.run(function(err, stats) {
    // ...
  });

总结

以上就是webpack中比较重要的四个概念,在平时开发过程当中会常常遇到。里面还能够有不少的详细配置,须要咱们在项目开发的过程当中慢慢了解。

相关文章
相关标签/搜索