【翻译向】webpack2 指南(上)

原文宣布与 抹桥的博客 -【翻译向】webpack2 指南(上)

前置定义

Bundle 代码包
Chunk 代码块

装置

npm install webpack –save-dev

代码支解

代码支解是 webpack 中最有目共睹的功用之一。它许可你把代码支解成种种可以依据需求载入的代码包,就像一个用户浏览器去婚配路由一样,或许一个用户发出的事宜。这许可你小的模块,许可你掌握资本的载入优先级,假如运用妥当的话,可以大大影响(下降)你运用的加载时刻。

缓存和并行加载的资本支解

第三方代码支解

一个典范的运用会依靠许多第三方的框架和库文件。不像运用代码自身,这些第三方代码的更改异常频仍。
假如我们坚持这些库在它自身的代码包中,从运用代码自身星散出来,那末我们就可以运用浏览器的缓存策略去在一个长时刻内把这些代码缓存到终究用户的机械上。

为了到达这个结果,第三方代码的 verndor 包的 hash 部份必需坚持稳定,不论运用代码怎样变化。进修 怎样经由过程 CommonsChunkPlugin 来支解 verndor/libray 代码

CSS 支解

你可以也想把款式文件支解成为一个零丁的包,从运用逻辑总自力出来。这可以加强款式文件的可缓存性,而且许可浏览器在加载运用代码时并行加载你的款式文件,因而也可以防止 FOUC (一个无款式内容的闪屏)。
进修 怎样去支解 CSS 经由过程运用 ExtractTextWebpackPlugin.

按需代码支解

虽然前面的资本支解须要用户在设置文件中预先指定支解点,然则也可以在运用代码中建立动态的支解点。

这个功用在有许多纤细颗粒代码块时会很有效,举个例子,每个运用的路由或许根据用户的展望行动。这可以运用户按需加载须要的资本。

经由过程 require.ensure() 来支解代码

require.ensure 是一个 CommonJS 作风的体式格局去异步加载资本。经由过程增加 require.ensure([<fileurl>]) , 我们可以在代码中定义一个支解点。 Webpack 可以建立一个包括在这个支解点中的一切代码的代码包。进修 怎样支解代码 经由过程运用 require.ensure().

TODO System.import()

代码支解 – CSS

在 webpack 中,当你运用 css-loader 而且在 JavaScript 中引入 CSS 文件,那末 CSS 文件会被打包在你的 JavaScript 文件中。这有一个不好的地方,就是你没法运用浏览器异步并行加载 CSS 的才能。相反,你的页面会比及全部 JavaScript 文件加载完成,才完成了款式文件的加载。Webpack 可以经由过程运用 extract-text-webpack-plugin 和 css-loader 来把款式文件星散出来去处理这个题目。

运用 css-loader

引入 css 到你的 JavaScript 中,须要运用 css-loader 去设置 webpack 的设置文件。

//webpack.config.js

modules.exports = function(env){
    entry: '..',
    ...
    module: {
        rules: [{
            test: /\.css$/,
            exclude: /node_modules/,
            loader: 'css-loader'
        }]
    }
    ...
}

运用 extract-text-webpack-plugin – ExtractTextPlugin

装置:

npm I --save-dev extract-text-webpack-plugin

要运用这个 ExtractTextPlugin,须要经由过程两个步骤设置在 webpack.config.js 中。

在 lodaer 内里

从之前的 css-loader 中适配,我们应当以下增加 ExtractTextPlugin.

loader: ExtractTextPlugin.extract('css-loader?sourceMap') //Can be used without sourcemaps too.

在 plugin 内里

new ExtractTextPlugin({ filename: 'bundle.css', disable: false, allChunks: true })

经由过程这两步,就可以天生一个新的包括一切 CSS 模块的代码包,然后把他们增加到 index.htmlheade 中去。可以经由过程 ExtractTextPlugin 去相识关于它 api 的更多信息。

完全的设置文件以下:

var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = function () {
    return {
        entry: './main.js',
        output: {
            path: './dist',
            filename: 'bundle.js'
        },
        module: {
            rules: [{
                test: /\.css$/,
                exclude: /node_modules/,
                loader: ExtractTextPlugin.extract({
                    loader: 'css-loader?sourceMap'
                })
            }]
        },
        devtool: 'source-map',
        plugins: [
            new ExtractTextPlugin({ filename: 'bundle.css', disable: false, allChunks: true })
        ]
    }
}

代码支解-库文件

一个典范的运用会依靠许多第三方来供应框架或功用支撑。项目中运用的牢固版本的库/框架文件的代码平常不会有更改,但是运用自身的营业逻辑代码却常常会有更改。

把运用代码和库文件的代码打包在一同是一件异常低效的事变。这是由于浏览器可以依据缓存头缓存这些资本文件到当地而不必每次都去服务端或许 cdn 上去发要求从新猎取,假如文件内容没有更改的话。为了可以享用这个优点,我们须要坚持第三方文件的 hash 稳定,不管运用自身的代码怎样变化。

我们只要把运用代码和第三方代码分脱离才可以到达如许的结果。

我们斟酌一个一个简朴的运用,它运用了 momentjs ,一个一般用来时刻格式化的库。

装置 moment

npm install --save moment

Index 文件会援用 moment 作为一个依靠而且打印当前的时刻:

Index.js

var moment = require('moment');
console.log(moment().format());

我们可以经由过程以下这个设置文件来打包这个运用

Webapck.config.js

module.exports = function(env) {
  return {
    entry: './index.js',
    output: {
      filename: '[chunkhash].[name].js',
      path: './dist'
    }
  }
}

当运转 webapck 敕令的时刻,假如你搜检打包后的文件,你会发明 momentindex.js 都被打包在了 bundle.js 中。

这不是一个很好的处理方案。假如 index.js 修正了,那末这打包文件会从新构建,浏览器就须要从新去加载这个文件,纵然 moment.js 文件并没有任何修正。

多个进口

让我们紧张这个题目,我们给 moment 增加一个新的进口定名为 vendors.

Webpack.config.js

module.exports = function(env) {
  return {
    entry: {
      main: './index.js',
      vendor: 'moment'
    },
    output: {
      filename: '[chunkhash].[name].js',
      path: './dist'
    }
  }
}

如今实行 webpack 敕令,我们会看到两个打包后的文件。假如你搜检内里代码的话,你会看到 moment 的代码同时出如今两个代码包中。

为了处理这个题目,我们须要运用 CommonsChunkPlugin.

CommonsChunksPlugin

这是一个相称庞杂的插件。它从根本上许可你从差别的代码包中提掏出一切的雷同模块而且把它们加入到配合的代码包中。假如这个雷同的代码包不存在,那末就建立一个新的。

我们可以修正 webpack 的设置文件来运用这个 CommonsCunksPlugin

Webpack.config.js

var webpack = require('webpack');
module.exports = function(env) {
  return {
    entry: {
      main: './index.js',
      vendor: 'moment'
    },
    output: {
      filename: '[chunkhash].[name].js',
      path: './dist'
    },
    plugins: [
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor' // Specify the common bundle's name.
      })
    ]
  }
}

如许的话, moment 的代码就只会出如今 vendor 代码包中了。

清单文件(Manifest File)

然则,假如我们可以修正运用的代码而且再次实行 webpack 敕令,我们看到 vendors 文件的 hash 照样变化了。纵然我们已星散了 vendormain 代码包,然则当运用代码发作修正的时刻 vendor 照样变化了。 这意味着我们照旧不能享用浏览器缓存带来的优点,由于每一次从新编译都邑修正 vendors 的 hash 值。

这个题目是由于每一次编译,webpack 天生一些 webpack 运转时代码,用来协助 webpack 来做它的事情。当那边存在一个零丁的代码包,运转时会驻留在个中。但当多个代码包被天生的时刻,运转时代码会被提取到大众的模块中,就是这个 vendor 文件。

为了阻挠这个,我们须要提掏出运转时到一个星散的清单文件(Manifest File)。虽然我们又多建立另一个代码包,但它的开支也被我们在 vendor 文件上取得的历久缓存所带来的优点所抵消了。

Webpack.config.js

var webpack = require('webpack');
module.exports = function(env) {
  return {
    entry: {
      main: './index.js',
      vendor: 'moment'
    },
    output: {
      filename: '[chunkhash].[name].js',
      path: './dist'
    },
    plugins: [
      new webpack.optimize.CommonsChunkPlugin({
        names: ['vendor', 'manifest'] // Specify the common bundle's name.
      })
    ]
  }
};

经由过程上面这个设置文件,我们会看到三个代码包被天生。vendor,mainmanifest. 如许当运用代码修正的时刻,从新打包后,修正的就只要 mainmanifest 了。 manifest 被修正是由于内里有对天生文件 hash 值的援用。

代码支解-运用 RequireJS

在这个章节,我们议论 webpack 怎样经由过程 require.ensure() 支解代码。

require.ensure()

Webpack 静态剖析给 require.ensure() 在代码中当构建和增加模块到星散的代码块中。这个新的代码块会被 webpack 在须要的时刻经由过程 jsonp 引入。

它的语法以下:

require.ensure(dependencies: String[], callback: function(require), chunkName: String)

依靠(dependencies)

这是一个字符串数组,用来声明一切须要在实行回掉函数之前就须要预先加载好且可用的模块。

回调函数(callback)

一个回调函数会被 webpack 实行一次当一切依靠(dependencies)都被加载今后。Require 对象的完成作为一个参数传递给这个回调函数。如许,我们可以更进一步 require 须要的依靠(dependencies)和其他须要实行的模块。

代码块名字(chunkName)

代码块名字是一个用来定名经由过程 require.ensrue() 建立的代码块。经由过程给差别的 require.ensure() 建立的代码支解点支解出来的代码块一个雷同的名字,我们可以确保一切的依靠都被打包到同一个代码块中。

我们来看一下以下构造的一个项目

\\ file structure
    |
    js --|
    |    |-- entry.js
    |    |-- a.js
    |    |-- b.js
    webpack.config.js
    |
    dist
// entry.js

require('a');
require.ensure([], function(require){
  require('b');
});

// a.js
console.log('***** I AM a *****');

// b.js
console.log('***** I AM b *****');
// webpack.config.js

module.exports = function(env) {
  return {
    entry: './js/entry.js',
    output: {
      filename: 'bundle.js',
      path: './dist'
    }
  }
}

当运转 webpack 敕令的时刻,我们发明 webpack 建立了两个新的代码包,bundle.js0.bundle.js.

entry.jsa.js 被打包到了 bundle.js 中。

b.js 被打包到了 0.bundle.js

require.ensure() 的圈套

空数组作为一个参数

require.ensure([], function(require){
  require('./a.js');
});

上面的代码确保一个支解点被建立, a.js 会被 webpack 零丁的打包成一个文件。

依靠作为参数

require.ensure(['./a.js'], function(require) {
  require('./b.js');
});

上面的代码,a.jsb.js 会被一同打包而且从主代码包中星散出来。然则只要 b.js 的内容被实行了。 a.js 的内容只是是可用的但并没有被实行。为了实行 a.js, 我们须要 require 它作为一个同步的体式格局比方 require('./a.js) ,如许 JavaScript 就可以实行它了。

依靠治理

Ø es6 module
Ø Commonjs
Ø Amd

表达式依靠(require with expression)

当你经由过程表达式去引入一个模块的时刻,就会建立一个上下文,所以当编译的时刻我们并不知道正确的模块是哪一个。

例:

require("./template/" + name + ".ejs");

Webpack 剖析 require() 的挪用,而且提掏出来一些信息:

Directory:./template
Regularexpression:/^.*\.ejs$/

上下文模块(context module)

一个上下文模块被天生。它包括了在这个文件夹下一切可以被上面的正则婚配所婚配到的模块的援用。上下文模块包括了一个把要求诠释到模块 id 的 map.
例:

{
  "./table.ejs": 42,
  "./table-row.ejs": 43,
  "./directory/folder.ejs": 44
}

上下文模块一样包括了一些运转时代码用来接见这个 map.

这意味着动态的援用可以被支撑,然则会致使一切可以被用到的模块都被打包到了终究的代码包中。

require.context

你可以经由过程 require.context() 要领建立你本身的上下文。它许可你传入一个用来查询的文件夹,一个用来决议是不是递归查找子文件夹的标识,另有一个用来婚配文件的正则表达式。

Webpack 会在代码打包的时刻剖析 require.context()

它的语法以下:

require.context(directory, useSubdirectories = false, regExp = /^\.\//)

例:

require.context("./test", false, /\.test\.js$/);
// a context with files from the test directory that can be required with a request endings with `.test.js`.
require.context("../", true, /\.stories\.js$/);
// a context with all files in the parent folder and descending folders ending with `.stories.js`.

上下文模块API(context module API)

一个上下文模块暴露一个要领,它吸收一个参数:要求的内容。
暴露出来的函数有三个属性:resolve,key,id

• `resolve` 是一个函数,实行后返回剖析后的要求内容的模块 id
• `keys`是一个函数,实行后返回一个数组,包括一切可以被上下文模块所要求的一切的模块的 id
当你想要经由过程正则婚配引入一个文件夹下一切模块时,这会异常有效:
function importAll (r) {
  r.keys().forEach(r);
}
importAll(require.context('../components/', true, /\.js$/))
var cache = {};
function importAll (r) {
  r.keys().forEach(key => cache[key] = r(key));
}
importAll(require.context('../components/', true, /\.js$/));
// At build-time cache will be polulated with all required modules.
• `id` 是上下文模块天生的模块 id. 当运用 `module.hot.accept` 时,这会异常有效。
    原文作者:moqiao
    原文地址: https://segmentfault.com/a/1190000008259868
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞