[译] 用 Webpack 武装本身

本文译自:Webpack your bags
这篇文章由入门到深切的引见了webpack的功用和应用技能,至心值得一看。

由于我英语水平有限,而且很少翻译文章,所以文中的一些语句在翻译时做了相似语义的转换,望体谅。假如有幸被转照样愿望能够申明啊

by the way,打个小广告。。把本身的github扔这儿好了,有时刻会更新些译文也许笔记什么的

《[译] 用 Webpack 武装本身》

你能够已听说过这个酷酷的东西-Webpack。一些人称之为相似于Gulp的东西,另有一些人则以为它相似于Browserify。假如你还没打仗过它,那很有能够会因而觉得疑心。而Webpack的主页上则以为它是二者的连系,那也许更让你疑心了。

说实话,一最先的时刻,“什么是Webpack”这个话题让我很心烦,也就没有继承研讨下去了。直到厥后,当我已构建了几个项目后,才至心的为之痴迷。假如你像我一样紧随Javascript的生长步调,你很有能够会由于太追随潮水跨度太大而蛋疼。在阅历了上面这些以后,我写下这篇文章,以便越发仔细的诠释Webpack是什么,以及它云云重要的缘由。

Webpack是啥?

起首来让我们回复最最先的题目:Webpack是个系统的构建东西,照样打包东西?答案是二者都是–这不代表它做了这两件事(先构建资本,在离别举行打包),而是说它将二者连系在一同了。

越发清晰的申明:与“构建sass文件,紧缩图片,然后援用它们,再打包,再在页面上援用”比拟,你只需这么做:

import stylesheet from 'styles/my-styles.scss';
import logo from 'img/my-logo.svg';
import someTemplate from 'html/some-template.html';

console.log(stylesheet); // "body{font-size:12px}"
console.log(logo); // "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5[...]"
console.log(someTemplate) // "<html><body><h1>Hello</h1></body></html>"

你的统统资本都被当作包处置惩罚,能够被import,修正,掌握,终究展示在你末了的一个bundle上。

为了能让上面那些有效运转,你须要在本身的Webpage设置里设置loaderloader是一个“当顺序碰见XXX范例文件的时刻,就做YYY”的小型插件。来看一些loader的例子:

{
  // 假如援用了 .ts 文件, 将会触发 Typescript loader
  test: /\.ts/,
  loader: 'typescript',
},
{
  // 假如援用了png|jpg|svg图片,则会用 image-webpack 举行紧缩 (wrapper around imagemin)
  // 并转化成 data64 URL 花样
  test: /\.(png|jpg|svg)/,
  loaders: ['url', 'image-webpack'],
},
{
  // 假如应用了 SCSS files, 则会用 node-sass 剖析, 终究返回CSS花样
  test: /\.scss/,
  loaders: ['css', 'autoprefixer', 'sass'],
}

终究在食物链的最底端,统统的loader都返回string,如许Webpack就可以够将它们到场到javascript模块中去。当你的Sass文件被loader转换以后,它的援用实际上是如许的:

export default 'body{font-size:12px}';

《[译] 用 Webpack 武装本身》

终究为何要这么做?

在你明白了Webpack是做什么的以后,第二个题目就接二连三:应用它有什么优点?“把图片和CSS扔进我的js里?什么鬼?”其实在良久之前,为了削减HTTP request请求,我们都被教诲要把统统东西写在一个文件内里。

到了如今,与之相似的是,许多人把统统东西打包进app.js。这两种要领都有一个很大的负面影响:许多时刻人们在下载的是他们用不到的资本。但假如你不这么做吧,你就得手动的在每一个页面援用响应的资本,终究会混乱成一坨:哪一个页面已援用了它所依靠的资本?

这些要领没有相对的对错。把Webpage当作一个中间件–不仅仅是打包或构建东西,而是个智慧的模块打包系统。只需你设置准确,它会比你还要清晰应用的手艺栈,并更好的优化它们。

来让我们一同构建一个简朴的App

为了让你更快速的明白应用Webpack的优点,我们会构建一个简朴的App,并将资本打包进去。在这里教程中我引荐应用Node4(或5),以及NPM3作为包管理东西,以便在应用Webpack的时刻防止大批的贫苦。假如你还没装NPM3,能够经由过程npm install npm@3 -g来装置。

$ node --version
v5.7.1
$ npm --version
3.6.0

我还要引荐你把node_modules/.bin放进你的PATH变量,以防止每次都要输入node_modules/.bin/webpack。在下面了例子里我输入的指令都不会再包括node_modules/.bin

基础指引(setup)

从竖立项目装置Webpack最先。我们同时也装置了jQuery以便支撑后续操纵。

$ npm init -y
$ npm install jquery --save
$ npm install webpack --save-dev

如今来做一个App的进口:

// src/index.js
var $ = require('jquery');

$('body').html('Hello');

让我们在webpack.config.js文件里举行的Webpack设置。Webpack设置实质上是Javascript,而且在末了export出去一个Object:

// webpack.config.js
module.exports = {
    entry:  './src',
    output: {
        path: 'builds',
        filename: 'bundle.js',
    },
};

在这里,entry通知Webpack哪些文件是应用的进口文件。它们是你的重要文件,在依靠树的最顶端。以后,我们通知Webpack把资本打包在builds文件夹下的bundle.js文件里。让我们编写index HTML文件。

<!DOCTYPE html>
<html>
<body>
    <h1>My title</h1>
    <a>Click me</a>

    <script src="builds/bundle.js"></script>
</body>
</html>

运转Webpack。假如统统准确那就可以够瞥见下面的信息:

$ webpack
Hash: d41fc61f5b9d72c13744
Version: webpack 1.12.14
Time: 301ms
    Asset    Size  Chunks             Chunk Names
bundle.js  268 kB       0  [emitted]  main
   [0] ./src/index.js 53 bytes {0} [built]
    + 1 hidden modules

在这段信息里能够看出,bundle.js包括了index.js和一个隐蔽的模块。隐蔽的模块是jQuery。在默许情势下Webpack隐蔽的模块都不是你写的。假如想要显现它们,我们能够在运转Webpack的时刻应用--display-modules

$ webpack --display-modules
bundle.js  268 kB       0  [emitted]  main
   [0] ./src/index.js 53 bytes {0} [built]
   [3] ./~/jquery/dist/jquery.js 259 kB {0} [built]

你还能够应用webpack --watch,在转变代码的时刻自动举行打包。

设置第一个loader(loader-01)

还记得Webpack能够处置惩罚种种资本的援用吗?该怎么搞?假如你追随了这些年Web组件生长的步调(Angular2,Vue,React,Polymer,X-Tag等等),那末你应当晓得,与一堆UI相互连接组合而成的App比拟,应用可保护的小型可复用的UI组件会更好:web component。

为了确保组件能够坚持自力,它们须要在本身内部打包须要的资本。设想一个按钮组件:除了HTML以外,还须要js以便和外部连系。噢对也许还须要一些款式。假如能够在须要这个按钮组件的时刻,加载统统它所依靠的资本的话那就太赞了。当我们import按钮组件的时刻,就猎取到了统统资本。

最先编写这个按钮组件吧。起首,假定你已习惯了ES2015语法,那末须要装置第一个loader:Babel。装置好一个loader你须要做下面这两步:起首,经由过程npm install {whatever}-loader装置你须要的loader,然后,将它加到Webpage设置的module.loaders里:

$ npm install babel-loader --save-dev

loader并不会帮我们装置Babel所以我们要本身装置它。须要装置babel-core包和es2015预处置惩罚包。

$ npm install babel-core babel-preset-es2015 --save-dev

新建.babelrc文件,内里是一段JSON,通知Babel应用es2015举行预处置惩罚。

// .babelrc 
{ "presets": ["es2015"] }

如今,Babel已被装置并设置完成,我们要更新Webpack设置。我们想要Babel运转在统统以.js末端的文件里,然则要防止运转在第三方依靠包比方jQuery内里。loader具有includeexclude划定规矩,内里能够是一段字符串、正则、回调等等。在这个例子里,我们只想让Babel在我们本身的文件里运转,因而应用include包括本身的资本文件夹:

module.exports = {
    entry:  './src',
    output: {
        path: 'builds',
        filename: 'bundle.js',
    },
    module: {
        loaders: [
            {
                test: /\.js/,
                loader: 'babel',
                include: __dirname + '/src',
            }
        ],
    }
};

如今,我们能够用ES6语法重写index.js了:

// index.js
import $ from 'jquery';

$('body').html('Hello');

写个小组件(loader-02)

来写个按钮组件吧,它将包括一些SCSS款式,HTML模板和一些操纵。所以我们要装置须要的东西。起首装置Mustache这个轻量级的模板库,然后装置处置惩罚Sass和HTML的loader。一样的,为了处置惩罚Sass loader返回的结果,还要装置CSS loader。一旦猎取到了CSS文件,我们就可以够用许多种体式格局来处置惩罚。现在应用的是一个叫style-loader的东西,它能够把CSS插进去到包中。

$ npm install mustache --save
$ npm install css-loader style-loader html-loader sass-loader node-sass --save-dev

为了能够让Webpack顺次处置惩罚差别loader的返回结果,我们能够将loader经由过程!链接到一同,猎取应用loaders并对应一个由loader构成的数组:

{
    test: /\.js/,
    loader: 'babel',
    include: __dirname + '/src',
},
{
    test: /\.scss/,
    loader: 'style!css!sass',
    // Or
    loaders: ['style', 'css', 'sass'],
},
{
    test: /\.html/,
    loader: 'html',
}

有了loader,我们来写写按钮:

// src/Components/Button.scss
.button {
  background: tomato;
  color: white;
}
<!-- src/Components/Button.html -->
<a class="button" href="{{link}}">{{text}}</a>
// src/Components/Button.js
import $ from 'jquery';
import template from './Button.html';
import Mustache from 'mustache';
import './Button.scss';

export default class Button {
    constructor(link) {
        this.link = link;
    }

    onClick(event) {
        event.preventDefault();
        alert(this.link);
    }

    render(node) {
        const text = $(node).text();
        // Render our button
        $(node).html(
            Mustache.render(template, {text})
        );
        // Attach our listeners
        $('.button').click(this.onClick.bind(this));
    }
}

你的Button.js如今处于完整自力的状况,不论何时何地的援用它,都能猎取到统统须要的依靠并衬着出来。如今衬着我们的按钮尝尝:

// src/index.js
import Button from ‘./Components/Button’;

const button = new Button(‘google.com’); 
button.render(‘a’); 

运转Webpack,革新页面,马上就可以瞥见我们这个丢脸的按钮了。

《[译] 用 Webpack 武装本身》

如今你已进修了怎样装置loader,以及定义各个依靠设置。看起来彷佛也没啥。但让我们来深切扩大一下这个例子。

代码星散(require.ensure

上面的例子还不错,但我们并不老是须要这个按钮。也许有的页面没有能够用来衬着按钮的a,我们并不想在如许的页面援用按钮的资本文件。这类时刻代码星散就可以起到作用了。代码星散是Webpack关于“整块悉数打包”vs“难以保护的手动指导”这个题目而给出的解决计划。这须要在你的代码中设定“星散点”:代码能够据此星散成差别地区举行按需加载。它的语法很简朴:

import $ from 'jquery';

// 这个是支解点
require.ensure([], () => {
  // 在这里import进的代码都邑被打包到一个零丁的文件里
  const library = require('some-big-library');
  $('foo').click(() => library.doSomething());
});

require.ensure中的东西都邑在打包结果中星散开来–只要当须要加载它的时刻Webpack才会经由过程AJAX请求举行加载。也就是说我们实际上获得的是如许的文件:

bundle.js
|- jquery.js
|- index.js // 进口文件
chunk1.js
|- some-big-libray.js
|- index-chunk.js // 回调中的代码在这里

你不须要在任何处所援用chunk1.js文件,Webpack会帮你在须要的时刻举行请求。这意味着你能够像我们的例子一样,依据逻辑须要引进的资本悉数扔进代码里。

只要当页面上有链接存在时,再援用按钮组件:

// src/index.js
if (document.querySelectorAll('a').length) {
    require.ensure([], () => {
        const Button = require('./Components/Button').default;
        const button = new Button('google.com');

        button.render('a');
    });
}

须要注重的一点是,由于require不会同时处置惩罚default export和normal export,所以应用require援用资本里default export的时刻,须要手动加上.default。比拟之下,import则能够举行处置惩罚:

import foo from 'bar' vs import {baz} from 'bar'

此时Webpack的output将会变得更庞杂了。跑下Webpack,用--display-chunks打印出来看看:

$ webpack --display-modules --display-chunks
Hash: 43b51e6cec5eb6572608
Version: webpack 1.12.14
Time: 1185ms
      Asset     Size  Chunks             Chunk Names
  bundle.js  3.82 kB       0  [emitted]  main
1.bundle.js   300 kB       1  [emitted]
chunk    {0} bundle.js (main) 235 bytes [rendered]
    [0] ./src/index.js 235 bytes {0} [built]
chunk    {1} 1.bundle.js 290 kB {0} [rendered]
    [5] ./src/Components/Button.js 1.94 kB {1} [built]
    [6] ./~/jquery/dist/jquery.js 259 kB {1} [built]
    [7] ./src/Components/Button.html 72 bytes {1} [built]
    [8] ./~/mustache/mustache.js 19.4 kB {1} [built]
    [9] ./src/Components/Button.scss 1.05 kB {1} [built]
    [10] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built]
    [11] ./~/css-loader/lib/css-base.js 1.51 kB {1} [built]
    [12] ./~/style-loader/addStyles.js 7.21 kB {1} [built]

正如你所见的那样,我们的进口bundle.js值包括了一些逻辑,而其他东西(jQuery,Mustache,Button)都被打包进了1.bundle.js,而且只在须要的时刻才会被援用。如今为了能够让Webpack在AJAX的时刻找到这些资本,我们须要改下设置里的output

path: 'builds',
filename: 'bundle.js',
publicPath: 'builds/',

output.publicPath通知Webpack,从当前页面的位置动身那里能够找到须要的资本(在这个例子里是/builds/)。当我们加载页面的时刻统统一般,而且能够瞥见Webpack已依据页面上预留的“锚”加载好了包。

《[译] 用 Webpack 武装本身》

假如页面上缺乏“锚”(代指link),那末只会加载bundle.js。经由过程这类体式格局,你能够做到在真正须要资本的时刻才举行加载,防止让本身的页面变成笨重的一坨。顺带一提,我们能够转变支解点的名字,不应用1.bundle.js而应用越发语义化的称号。经由过程require.ensure的第三个参数来完成:

require.ensure([], () => {
    const Button = require('./Components/Button').default;
    const button = new Button('google.com');

    button.render('a');
}, 'button');

如许的话就会天生button.bundle.js而不是1.bundle.js

再加个组件(CommonChunksPlugin

来让我们再加个组件吧:

// src/Components/Header.scss
.header {
  font-size: 3rem;
}
<!-- src/Components/Header.html -->
<header class="header">{{text}}</header>
// src/Components/Header.js
import $ from 'jquery';
import Mustache from 'mustache';
import template from './Header.html';
import './Header.scss';

export default class Header {
    render(node) {
        const text = $(node).text();
        $(node).html(
            Mustache.render(template, {text})
        );
    }
}

将它在应用中衬着出来:

// 假如有链接,则衬着按钮组件
if (document.querySelectorAll('a').length) {
    require.ensure([], () => {
        const Button = require('./Components/Button');
        const button = new Button('google.com');

        button.render('a');
    });
}

// 假如有题目,则衬着题目组件
if (document.querySelectorAll('h1').length) {
    require.ensure([], () => {
        const Header = require('./Components/Header');

        new Header().render('h1');
    });
}

瞅瞅应用了--display-chunks --display-modules标记后Webpack的output输出:

$ webpack --display-modules --display-chunks
Hash: 178b46d1d1570ff8bceb
Version: webpack 1.12.14
Time: 1548ms
      Asset     Size  Chunks             Chunk Names
  bundle.js  4.16 kB       0  [emitted]  main
1.bundle.js   300 kB       1  [emitted]
2.bundle.js   299 kB       2  [emitted]
chunk    {0} bundle.js (main) 550 bytes [rendered]
    [0] ./src/index.js 550 bytes {0} [built]
chunk    {1} 1.bundle.js 290 kB {0} [rendered]
    [14] ./src/Components/Button.js 1.94 kB {1} [built]
    [15] ./~/jquery/dist/jquery.js 259 kB {1} {2} [built]
    [16] ./src/Components/Button.html 72 bytes {1} [built]
    [17] ./~/mustache/mustache.js 19.4 kB {1} {2} [built]
    [18] ./src/Components/Button.scss 1.05 kB {1} [built]
    [19] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built]
    [20] ./~/css-loader/lib/css-base.js 1.51 kB {1} {2} [built]
    [21] ./~/style-loader/addStyles.js 7.21 kB {1} {2} [built]
chunk    {2} 2.bundle.js 290 kB {0} [rendered]
    [22] ./~/jquery/dist/jquery.js 259 kB {1} {2} [built]
    [23] ./~/mustache/mustache.js 19.4 kB {1} {2} [built]
    [24] ./~/css-loader/lib/css-base.js 1.51 kB {1} {2} [built]
    [25] ./~/style-loader/addStyles.js 7.21 kB {1} {2} [built]
    [26] ./src/Components/Header.js 1.62 kB {2} [built]
   [27] ./src/Components/Header.html 64 bytes {2} [built]
   [28] ./src/Components/Header.scss 1.05 kB {2} [built]
   [29] ./~/css-loader!./~/sass-loader!./src/Components/Header.scss 192 bytes {2} [built]

能够看出一点题目了:这两个组件都须要jQuery和Mustache,如许的话就造成了包中的依靠反复,这可不是我们想要的。只管Webpack会在默许情况下举行一定的优化,但还得靠插件来加足火力搞定它。

插件和loader的差别在于,loader只对一类特定的文件有效,而插件每每面向统统文件,而且并不老是会引起转化。Webpack供应了许多插件供你优化。在这里我们应用CommonChunksPlugin插件:它会剖析你包中的反复依靠并提掏出来,天生一个完整自力的文件(比方vendor.js),以至天生你的主文件。

如今,我们想要把配合的依靠包从进口中剔除。假如统统的页面都用到了jQuery和Mustache,那末就要把它们提掏出来。更新下设置吧:

var webpack = require('webpack');

module.exports = {
    entry: './src',
    output:  {
      // ...
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'main', // 将依靠移到我们的主文件中
            children: true, // 再在统统的子文件中搜检依靠文件
            minChunks: 2, // 一个依靠反复频频会被提掏出来
        }),
    ],
    module:  {
      // ...
    }
};

再跑次Webpack,能够看出如今就好多了。个中,main是我们的默许依靠。

chunk    {0} bundle.js (main) 287 kB [rendered]
    [0] ./src/index.js 550 bytes {0} [built]
    [30] ./~/jquery/dist/jquery.js 259 kB {0} [built]
    [31] ./~/mustache/mustache.js 19.4 kB {0} [built]
    [32] ./~/css-loader/lib/css-base.js 1.51 kB {0} [built]
    [33] ./~/style-loader/addStyles.js 7.21 kB {0} [built]
chunk    {1} 1.bundle.js 3.28 kB {0} [rendered]
    [34] ./src/Components/Button.js 1.94 kB {1} [built]
    [35] ./src/Components/Button.html 72 bytes {1} [built]
    [36] ./src/Components/Button.scss 1.05 kB {1} [built]
    [37] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built]
chunk    {2} 2.bundle.js 2.92 kB {0} [rendered]
    [38] ./src/Components/Header.js 1.62 kB {2} [built]
   [39] ./src/Components/Header.html 64 bytes {2} [built]
   [40] ./src/Components/Header.scss 1.05 kB {2} [built]
   [41] ./~/css-loader!./~/sass-loader!./src/Components/Header.scss 192 bytes {2} [built]

假如我们转变下名字name: 'vendor'

new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    children: true,
    minChunks: 2,
}),

Webpack会在没有该文件的情况下自动天生builds/vendor.js,以后我们能够手动引入:

<script src="builds/vendor.js"></script>
<script src="builds/bundle.js"></script>

你也能够经由过程async: true,而且不供应配合依靠包的定名,来到达异步加载配合依靠的结果。

Webpack有许多如许给力的优化计划。我没法一个一个引见它们,不过能够经由过程制造一个临盆环境的应用来进一步进修。

奔腾到临盆环境(production

起首,要在设置中增添几个插件,但请求只要当NODE_ENVproduction的时刻才运转它们:

var webpack = require('webpack');
var production = process.env.NODE_ENV === 'production';

var plugins = [
    new webpack.optimize.CommonsChunkPlugin({
        name: 'main',
        children: true,
        minChunks: 2,
    }),
];

if (production) {
    plugins = plugins.concat([
       // 临盆环境下须要的插件
    ]);
}

module.exports = {
    entry: './src',
    output: {
        path: 'builds',
        filename: 'bundle.js',
        publicPath: 'builds/',
    },
    plugins: plugins,
    // ...
};

Webpack也供应了一些能够切换临盆环境的设置:

module.exports = {
    debug: !production,
    devtool: production ? false : 'eval',
}

设置中的第一行表明在开辟环境下,将开启debug情势,代码不再混做一团,利于当地调试。第二行则用来临盆资当舆图(sourcemaps)。Webpack有一些要领能够天生sourcemaps,而eval则是在当地表现最赞的一个。在临盆环境下,我们并不体贴sourcemaps,因而封闭了这个选项。

如今来增添临盆环境下的插件吧:

if (production) {
    plugins = plugins.concat([
        // 这个插件用来寻觅雷同的包和文件,并把它们兼并在一同
        new webpack.optimize.DedupePlugin(),

        // 这个插件依据包/库的援用次数来优化它们
        new webpack.optimize.OccurenceOrderPlugin(),

        // 这个插件用来阻挠Webpack把太小的文件打成零丁的包
        new webpack.optimize.MinChunkSizePlugin({
            minChunkSize: 51200, // ~50kb
        }),

        // 紧缩js文件
        new webpack.optimize.UglifyJsPlugin({
            mangle: true,
            compress: {
                warnings: false, // 制止天生warning
            },
        }),

        // 这个插件供应了种种可用在临盆环境下的变量
        // 经由过程设置为false,可防止临盆环境下调用到它们
        new webpack.DefinePlugin({
            __SERVER__: !production,
            __DEVELOPMENT__: !production,
            __DEVTOOLS__: !production,
            'process.env': {
                BABEL_ENV: JSON.stringify(process.env.NODE_ENV),
            },
        }),

    ]);
}

我广泛应用的差不多就这么多了,不过Webpack还供应了异常多的插件,你能够本身去研讨它们。也能够在NPM上找到许多用户本身孝敬的插件。插件的链接在文末供应。

另有一个关于临盆环境的优化是给资本供应版本的观点。还记得output.filename里的bundle.js吗?在这个设置内里,你能够应用一些变量,而[hash]则会给文件供应一段随机的字符串。除此以外,我们想要包能够被版本化,因而增添了output.chunkFilename

output: {
    path: 'builds',
    filename: production ? '[name]-[hash].js' : 'bundle.js',
    chunkFilename: '[name]-[chunkhash].js',
    publicPath: 'builds/',
},

由于没法得知每次打包天生的文件名,所以我们只在临盆环境下应用它。除此以外,我们还想保证每次打包的时刻,builds文件夹都邑被清空以勤俭空间,因而应用了一个第三方插件:

$ npm install clean-webpack-plugin --save-dev

并将它增添到设置中:

var webpack = require('webpack');
var CleanPlugin = require('clean-webpack-plugin');
// ...
if (production) {
    plugins = plugins.concat([
        // 在打包前清空 builds/ 文件夹
        new CleanPlugin('builds'),

做完这些美丽的优化,来比较下结果的差别吧:

$ webpack
                bundle.js   314 kB       0  [emitted]  main
1-21660ec268fe9de7776c.js  4.46 kB       1  [emitted]
2-fcc95abf34773e79afda.js  4.15 kB       2  [emitted]
$ NODE_ENV=production webpack
main-937cc23ccbf192c9edd6.js  97.2 kB       0  [emitted]  main

来看看Webpack都做了什么:

  • 在第一段代码中,后两个包异常轻量,异步请求不会占用若干HTTP带宽,所以在临盆环境下Webpack将它们打包进了进口文件里

  • 统统东西都紧缩过了。从322kb降到了97kb

然则如许下去,Webpack岂不是会将js文件兼并成巨大的一坨吗?

是的,在这个小小的应用中是如许没错。然则你须要这么想:你不须要斟酌在什么时刻兼并什么。假如你的包中含有太多的依靠,它们会被移走到异步请求包中而不会被兼并起来。反之,假如它们很小,不值得自力加载,那末就会被兼并。你只须要竖立划定规矩,Webpack会最大化的将其优化。没有人力劳作,不须要思索依靠关联,统统都是自动化的。

《[译] 用 Webpack 武装本身》

也许你已注重到了,我没有对HTML或CSS举行紧缩。那是由于当debug情势开启的时刻,css-loaderhtml-loader已帮我们搞好了。这也是为何Uglify是一个自力插件的缘由:在Webpack中没有js-loader这类东西,Webpack本身就是个JS loader。

抽取(extract-text-webpack-plugin

能够你已注重到了,从这个教程一最先,Webpack打包好以后,我们的款式就直接插在网页页面上,几乎不能更丢脸了。能经由过程Webpack把打包过的CSS天生自力的文件吗?固然没题目:

$ npm install extract-text-webpack-plugin --save-dev

这个插件所做的就是我方才说的那些:从打出的终究包内里,提掏出某一类内容星散开来零丁援用。它通常被用于提取CSS文件:

var webpack = require('webpack');
var CleanPlugin = require('clean-webpack-plugin');
var ExtractPlugin = require('extract-text-webpack-plugin');
var production = process.env.NODE_ENV === 'production';

var plugins = [
    new ExtractPlugin('bundle.css'), // <=== 提掏出来的文件
    new webpack.optimize.CommonsChunkPlugin({
        name: 'main',
        children: true, 
        minChunks: 2,
    }),
];
// ...
module.exports = {
    // ...
    plugins: plugins,
    module:  {
        loaders: [
            {
                test: /\.scss/,
                loader: ExtractPlugin.extract('style', 'css!sass'),
            },
            // ...
        ],
    }
};

ExtractPlugin.extrac要领吸收两个参数,第一个参数代表当它处于已打包好的包(’style’)里时,怎样处置惩罚那些提掏出来的东西;第二个参数代表当它在主文件(’css!sass’)里时,怎样看待提掏出的东西。当它在包里时,一定不能直接将CSS加在天生的东西背面,所以先用style-loader举行处置惩罚;而关于主文件内里的styles,则将它们放进builds/bundle.css文件。我们来给应用加一个主款式:

// src/styles.scss
body {
  font-family: sans-serif;
  background: darken(white, 0.2);
}
// src/index.js
import './styles.scss';

// Rest of our file

跑下Webpack,就可以瞥见已天生了bundle.css,能够把它援用进HTML里:

$ webpack
                bundle.js    318 kB       0  [emitted]  main
1-a110b2d7814eb963b0b5.js   4.43 kB       1  [emitted]
2-03eb25b4d6b52a50eb89.js    4.1 kB       2  [emitted]
               bundle.css  59 bytes       0  [emitted]  main

假如你想提掏出统统包里的款式,则须要设置ExtractTextPlugin('bundle.css', {allChunks: true})

顺带一提,你也能够自定义文件名,就跟之前说的转变js output-file称号一样:ExtractTextPlugin('[name]-[hash].css')

应用图片(url-loader&file-loader

到现在为止,我们还没处置惩罚比方图片、字体如许的资本文件。它们在Webpack中怎样事变,我们又该怎样优化?接下来,我要在网站的背景里到场图片,看起来一定酷酷的:

《[译] 用 Webpack 武装本身》

把图片保存在img/puppy.jpg,更新下Sass文件:

// src/styles.scss
body {
    font-family: sans-serif;
    background: darken(white, 0.2);
    background-image: url('../img/puppy.jpg');
    background-size: cover;
}

假如仅仅是如许,Webpack一定会通知你:“你特么的想让我对JPG做啥?”,那是由于还没有到场对应的loader。有两种loader能够应用:file-loaderurl-loader

  • file-loader:返回一段指向资本的URL,许可你给文件到场版本的观点(默许)

  • url-loader:以data:image/jpeg;base64的情势返回URL

两个要领不能说谁好谁坏:假如你的图片大于2M的话那你一定不愿望它直接夹杂在代码中,而是自力出去;而假如仅仅是2kb摆布的小图标。那末兼并在一同削减HTTP请求会更好。因而,我们两个都要设置:

$ npm install url-loader file-loader --save-dev
{
    test: /\.(png|gif|jpe?g|svg)$/i,
    loader: 'url?limit=10000',
},

在这里,我们给url-loader了一个limit参数,如许,当文件大小小于10kb的时刻,会采用行内款式,不然的话,会转到file-loader举行处置惩罚。你也能够经由过程query通报一个Object来完成它:

{
    test: /\.(png|gif|jpe?g|svg)$/i,
    loader: 'url',
    query: {
      limit: 10000,
    }
}

来瞅一眼Webpack的输出:

bundle.js   15 kB       0  [emitted]  main
1-b8256867498f4be01fd7.js  317 kB       1  [emitted]
2-e1bc215a6b91d55a09aa.js  317 kB       2  [emitted]
               bundle.css  2.9 kB       0  [emitted]  main

输出内里没有JPG图象,那是由于我们的小狗图片比设置里限定的大小要小,因而被加到了行内。接见页面,你就可以瞥见这只可爱的小狗了。

《[译] 用 Webpack 武装本身》

这是一个异常壮大的功用,它意味着Webpack能够智能的依据资本的大小和HTTP请求占领的比率,采用差别的优化计划。另有一个叫做image-loader的loader,能够在打包前搜检统统图片,防止图片的反复紧缩。它有一个叫?bypassOnDebug 的参数,经由过程它你能够只在临盆环境下启动该插件。

另有许多优异的插件,我强烈建议你应用文末的链接去检察它们。

来个牛逼的热加载(dev-server)

我们的临盆环境以及整的差不多了,如今应当更多的体贴一下当地开辟。也许你以及注重到了,当人们说起开辟东西的时刻,老是会说起热加载:LiveReload,BrowserSync,也许其他的什么鬼东西。然则只要傻瓜才会整页的革新,我们则应用更高端的热加载。由于Webpack能够确实的晓得你依靠树中某一点位置的代码,因而每次的转变都邑据此天生一个新的文件。简朴的说,就是不须要革新页面就可以将转变展示在屏幕上。

为了能够应用HMR,我们须要一个server来启动热加载。Webpack供应的dev-server能够完成这个使命:

$ npm install webpack-dev-server --save-dev

装置下面的敕令启动server,不能再简朴了:

$ webpack-dev-server --inline --hot

第一个标记--inline是让Webpack把HMR逻辑直接写入页面上而不是放到iframe里,而第二个标记则开启了HMR。接下来,接见http://localhost:8080/webpack-dev-server/,嗯照样谁人一般的页面。试着修正Sass文件,MAGIC!

《[译] 用 Webpack 武装本身》

你能够把webpack-dev-server作为本身当地的server。假如你盘算一向应用HMR,就须要这么设置:

output: {
    path: 'builds',
    filename: production ? '[name]-[hash].js' : 'bundle.js',
    chunkFilename: '[name]-[chunkhash].js',
    publicPath: 'builds/',
},
devServer: {
    hot: true,
},

如许的话,不论我们什么时刻运转webpack-dev-server,都邑是HMR情势。值得一提的是,我们在这里应用webpack-dev-server对资本举行热加载,但也能够应用在其他处所比方Express server上。Webpack供应了一个中间件,使得你能够把HMR的功用用在其他server上。

代码不清洁的人都给我去罚站!(pre-loader & lint)

假如你一向随着本教程走,那也许会有如许的疑问:为何loader都在module.loaders中而插件不在?那固然是由于另有其他能够设置进module的东西~Webpack不只是有loader,也有pre-loader和post-loader:在main-loader运转之前和以后发起的玩意。举个栗子:我基础能够确信本身在这个文章内里写的代码很蹩脚,所以应用ESLint举行代码搜检:

$ npm install eslint eslint-loader babel-eslint --save-dev

新建一个一定会激发毛病的.eslintrc文件:

// .eslintrc
parser: 'babel-eslint'
rules:
  quotes: 2

如今增添pre-loader,语法和之前的一样,只不过加在module.preLoaders里:

module:  {
    preLoaders: [
        {
            test: /\.js/,
            loader: 'eslint',
        }
    ],

启动Webpack,然后淡定的看它失利:

$ webpack
Hash: 33cc307122f0a9608812
Version: webpack 1.12.2
Time: 1307ms
                    Asset      Size  Chunks             Chunk Names
                bundle.js    305 kB       0  [emitted]  main
1-551ae2634fda70fd8502.js    4.5 kB       1  [emitted]
2-999713ac2cd9c7cf079b.js   4.17 kB       2  [emitted]
               bundle.css  59 bytes       0  [emitted]  main
    + 15 hidden modules

ERROR in ./src/index.js

/Users/anahkiasen/Sites/webpack/src/index.js
   1:8   error  Strings must use doublequote  quotes
   4:31  error  Strings must use doublequote  quotes
   6:32  error  Strings must use doublequote  quotes
   7:35  error  Strings must use doublequote  quotes
   9:23  error  Strings must use doublequote  quotes
  14:31  error  Strings must use doublequote  quotes
  16:32  error  Strings must use doublequote  quotes
  18:29  error  Strings must use doublequote  quotes

再举个pre-loader的例子:每一个组件里我们都援用了stylesheet,而它们都有雷同定名的对应模板。应用一个pre-loader能够自动将有雷同称号的文件作为一个module载入:

$ npm install baggage-loader --save-dev
{
    test: /\.js/,
    loader: 'baggage?[file].html=template&[file].scss',
}

经由过程如许的体式格局示知Webpack,假如碰见和设置雷同的HTML文件,则将它作为template 引入,同时引入和它同名的Sass文件。如许就可以改写组件文件:

将:

import $ from 'jquery';
import template from './Button.html';
import Mustache from 'mustache';
import './Button.scss';

改成:

import $ from 'jquery';
import Mustache from 'mustache';

你看,pre-loaders也能够很壮大。在文末你能够找到更多的loader

还没看够?

如今我们的应用还很小,当它变的巨大的时刻,观察依靠树就变的异常有效了,从中能够看出我们做的是对是错,应用的瓶颈在那里等等。Webpack晓得这统统,不过我们得规矩的讨教它才晓得答案。为了做到这点,你能够经由过程下面的敕令运转Webpack:

webpack --profile --json > stats.json

第一个标记会让Webpack天生一个profile文件,而第二个则将它转化为JSON花样。终究,讲统统的output都天生了JSON文件。如今有许多网站都能够剖析这个JSON文件,不过Webpack官方供应了一个解码的网站Webpack Analyze。将JSON文件导入,进入Modules板块,就可以够瞥见本身依靠树的可视化图象:

《[译] 用 Webpack 武装本身》

小圆点越红,则证实在打包的时刻越难题。在这个例子中,jQuery作为最大的文件而成为罪魁祸首。再瞅瞅网站上的其他模块。也许你没法从这个小小的例子里学到许多东西,然则这个东西在剖析依靠树和包的时刻真的异常有效。

我之前提过,如今有许多效劳供应能够对profile文件举行剖析。个中一个是Webpack Visualizer,它能够以饼状图的情势示知你各个文件占有了多大的比重:

《[译] 用 Webpack 武装本身》

就先讲到这儿吧

对我而言,Webpack已庖代了Grunt也许Gulp:大部分的功用能够应用Webpack替换,其他的则应用NPM脚本就够了。在之前,每一个使命中我们都要经由过程Aglio,把API文档转换为HTML,而如今只须要这么做:

// package.json
{
  "scripts": {
    "build": "webpack",
    "build:api": "aglio -i docs/api/index.apib -o docs/api/index.html"
  }
}

即便是一些不须要打包和构建的Glup使命,Webpack都知心的供应了对应的效劳。下面是一个将Glup融会进Webpack的例子:

var gulp = require('gulp');
var gutil = require('gutil');
var webpack = require('webpack');
var config = require('./webpack.config');

gulp.task('default', function(callback) {
  webpack(config, function(error, stats) {
    if (error) throw new gutil.PluginError('webpack', error);
    gutil.log('[webpack]', stats.toString());

    callback();
  });
});

由于Webpack具有Node API,因而能够很轻松了应用在其他构建系统中。不必多久你就可以发明本身深爱着它没法自拔了。

不论怎样,这篇笔墨带你预览了Webpack能够帮你做的事变。也许你以为我们讲了许多方面,但实际上这只是个表皮罢了:multiple entry points, prefetching, context replacement等等都还没有涉及到。Webpack是个壮大的东西,也因而比那些传统的东西越发难明。但一旦你晓得怎样应用它,它就会为你鸣奏最悦耳动听的声响。我曾在一些项目里应用过它,它供应的壮大的优化和自动化让我深深不能自拔。

资本

    原文作者:ecmadao
    原文地址: https://segmentfault.com/a/1190000005054055
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞