国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[譯] 用 Webpack 武裝自己

Tychio / 2913人閱讀

摘要:現(xiàn)在來做一個(gè)的入口讓我們?cè)谖募镞M(jìn)行的配置。如果想要顯示它們,我們可以在運(yùn)行的時(shí)候使用你還可以使用,在改變代碼的時(shí)候自動(dòng)進(jìn)行打包。新建文件,里面是一段,告訴使用進(jìn)行預(yù)處理。

本文譯自:Webpack your bags
這篇文章由入門到深入的介紹了webpack的功能和使用技巧,真心值得一看。

由于我英語(yǔ)水平有限,而且很少翻譯文章,所以文中的一些語(yǔ)句在翻譯時(shí)做了類似語(yǔ)義的轉(zhuǎn)換,望諒解。要是有幸被轉(zhuǎn)還是希望能夠注明啊

by the way,打個(gè)小廣告。。把自己的github扔這兒好了,有時(shí)候會(huì)更新些譯文或者筆記什么的

你可能已經(jīng)聽說過這個(gè)酷酷的工具-Webpack。一些人稱之為類似于Gulp的工具,還有一些人則認(rèn)為它類似于Browserify。如果你還沒接觸過它,那很有可能會(huì)因此感到困惑。而Webpack的主頁(yè)上則認(rèn)為它是兩者的結(jié)合,那或許更讓你困惑了。

說實(shí)話,一開始的時(shí)候,“什么是Webpack”這個(gè)話題讓我很心煩,也就沒有繼續(xù)研究下去了。直到后來,當(dāng)我已經(jīng)構(gòu)建了幾個(gè)項(xiàng)目后,才真心的為之癡迷。如果你像我一樣緊隨Javascript的發(fā)展步伐,你很有可能會(huì)因?yàn)樘冯S潮流跨度太大而蛋疼。在經(jīng)歷了上面這些之后,我寫下這篇文章,以便更加細(xì)致的解釋W(xué)ebpack是什么,以及它如此重要的原因。

Webpack是啥?

首先來讓我們回答最開始的問題:Webpack是個(gè)系統(tǒng)的構(gòu)建工具,還是打包工具?答案是兩者都是--這不代表它做了這兩件事(先構(gòu)建資源,在分別進(jìn)行打包),而是說它將兩者結(jié)合在一起了。

更加清晰的說明:與“構(gòu)建sass文件,壓縮圖片,然后引用它們,再打包,再在頁(yè)面上引用”相比,你只要這么做:

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) // "

Hello

"

你的所有資源都被當(dāng)做包處理,可以被import,修改,控制,最終展現(xiàn)在你最后的一個(gè)bundle上。

為了能讓上面那些有效運(yùn)轉(zhuǎn),你需要在自己的Webpage配置里配置loaderloader是一個(gè)“當(dāng)程序遇見XXX類型文件的時(shí)候,就做YYY”的小型插件。來看一些loader的例子:

{
  // 如果引用了 .ts 文件, 將會(huì)觸發(fā) Typescript loader
  test: /.ts/,
  loader: "typescript",
},
{
  // 如果引用了png|jpg|svg圖片,則會(huì)用 image-webpack 進(jìn)行壓縮 (wrapper around imagemin)
  // 并轉(zhuǎn)化成 data64 URL 格式
  test: /.(png|jpg|svg)/,
  loaders: ["url", "image-webpack"],
},
{
  // 如果使用了 SCSS files, 則會(huì)用 node-sass 解析, 最終返回CSS格式
  test: /.scss/,
  loaders: ["css", "autoprefixer", "sass"],
}

最終在食物鏈的最底端,所有的loader都返回string,這樣Webpack就可以將它們加入到j(luò)avascript模塊中去。當(dāng)你的Sass文件被loader轉(zhuǎn)換之后,它的引用實(shí)際上是這樣的:

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

究竟為什么要這么做?

在你理解了Webpack是做什么的之后,第二個(gè)問題就接踵而至:使用它有什么好處?“把圖片和CSS扔進(jìn)我的js里?什么鬼?”其實(shí)在很久之前,為了減少HTTP request請(qǐng)求,我們都被教育要把所有東西寫在一個(gè)文件里面。

到了現(xiàn)在,與之類似的是,很多人把所有東西打包進(jìn)app.js。這兩種方法都有一個(gè)很大的負(fù)面影響:很多時(shí)候人們?cè)谙螺d的是他們用不到的資源。但如果你不這么做吧,你就得手動(dòng)的在每個(gè)頁(yè)面引用相應(yīng)的資源,最終會(huì)混亂成一坨:哪個(gè)頁(yè)面已經(jīng)引用了它所依賴的資源?

這些方法沒有絕對(duì)的對(duì)錯(cuò)。把Webpage當(dāng)做一個(gè)中間件--不僅僅是打包或構(gòu)建工具,而是個(gè)聰明的模塊打包系統(tǒng)。只要你設(shè)置正確,它會(huì)比你還要清楚使用的技術(shù)棧,并更好的優(yōu)化它們。

來讓我們一起構(gòu)建一個(gè)簡(jiǎn)單的App

為了讓你更快捷的理解使用Webpack的好處,我們會(huì)構(gòu)建一個(gè)簡(jiǎn)單的App,并將資源打包進(jìn)去。在這里教程中我推薦使用Node4(或5),以及NPM3作為包管理工具,以便在使用Webpack的時(shí)候避免大量的麻煩。如果你還沒裝NPM3,可以通過npm install npm@3 -g來安裝。

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

我還要推薦你把node_modules/.bin放進(jìn)你的PATH變量,以避免每次都要輸入node_modules/.bin/webpack。在下面了例子里我輸入的指令都不會(huì)再包含node_modules/.bin

基礎(chǔ)指引(setup)

從創(chuàng)建項(xiàng)目安裝Webpack開始。我們同時(shí)也安裝了jQuery以便支持后續(xù)操作。

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

現(xiàn)在來做一個(gè)App的入口:

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

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

讓我們?cè)?b>webpack.config.js文件里進(jìn)行的Webpack配置。Webpack配置實(shí)質(zhì)上是Javascript,并且在最后export出去一個(gè)Object:

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

在這里,entry告訴Webpack哪些文件是應(yīng)用的入口文件。它們是你的主要文件,在依賴樹的最頂端。之后,我們告訴Webpack把資源打包在builds文件夾下的bundle.js文件里。讓我們編寫index HTML文件。




    

My title

Click me

運(yùn)行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和一個(gè)隱藏的模塊。隱藏的模塊是jQuery。在默認(rèn)模式下Webpack隱藏的模塊都不是你寫的。如果想要顯示它們,我們可以在運(yùn)行Webpack的時(shí)候使用--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,在改變代碼的時(shí)候自動(dòng)進(jìn)行打包。

設(shè)置第一個(gè)loader(loader-01)

還記得Webpack可以處理各種資源的引用嗎?該怎么搞?如果你跟隨了這些年Web組件發(fā)展的步伐(Angular2,Vue,React,Polymer,X-Tag等等),那么你應(yīng)該知道,與一堆UI相互連接組合而成的App相比,使用可維護(hù)的小型可復(fù)用的UI組件會(huì)更好:web component。

為了確保組件能夠保持獨(dú)立,它們需要在自己內(nèi)部打包需要的資源。想象一個(gè)按鈕組件:除了HTML之外,還需要js以便和外部結(jié)合。噢對(duì)或許還需要一些樣式。如果能夠在需要這個(gè)按鈕組件的時(shí)候,加載所有它所依賴的資源的話那就太贊了。當(dāng)我們import按鈕組件的時(shí)候,就獲取到了所有資源。

開始編寫這個(gè)按鈕組件吧。首先,假設(shè)你已經(jīng)習(xí)慣了ES2015語(yǔ)法,那么需要安裝第一個(gè)loader:Babel。安裝好一個(gè)loader你需要做下面這兩步:首先,通過npm install {whatever}-loader安裝你需要的loader,然后,將它加到Webpage配置的module.loaders里:

$ npm install babel-loader --save-dev

loader并不會(huì)幫我們安裝Babel所以我們要自己安裝它。需要安裝babel-core包和es2015預(yù)處理包。

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

新建.babelrc文件,里面是一段JSON,告訴Babel使用es2015進(jìn)行預(yù)處理。

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

現(xiàn)在,Babel已經(jīng)被安裝并配置完成,我們要更新Webpack配置。我們想要Babel運(yùn)行在所有以.js結(jié)尾的文件里,但是要避免運(yùn)行在第三方依賴包例如jQuery里面。loader擁有includeexclude規(guī)則,里面可以是一段字符串、正則、回調(diào)等等。在這個(gè)例子里,我們只想讓Babel在我們自己的文件里運(yùn)行,因此使用include包含自己的資源文件夾:

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

現(xiàn)在,我們可以用ES6語(yǔ)法重寫index.js了:

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

$("body").html("Hello");
寫個(gè)小組件(loader-02)

來寫個(gè)按鈕組件吧,它將包含一些SCSS樣式,HTML模板和一些操作。所以我們要安裝需要的工具。首先安裝Mustache這個(gè)輕量級(jí)的模板庫(kù),然后安裝處理Sass和HTML的loader。同樣的,為了處理Sass loader返回的結(jié)果,還要安裝CSS loader。一旦獲取到了CSS文件,我們就可以用很多種方式來處理。目前使用的是一個(gè)叫style-loader的東西,它能夠把CSS插入到包中。

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

為了能夠讓W(xué)ebpack依次處理不同loader的返回結(jié)果,我們可以將loader通過!鏈接到一起,獲取使用loaders并對(duì)應(yīng)一個(gè)由loader組成的數(shù)組:

{
    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;
}

{{text}}
// 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現(xiàn)在處于完全獨(dú)立的狀態(tài),不管何時(shí)何地的引用它,都能獲取到所有需要的依賴并渲染出來。現(xiàn)在渲染我們的按鈕試試:

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

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

運(yùn)行Webpack,刷新頁(yè)面,立刻就能看見我們這個(gè)難看的按鈕了。

現(xiàn)在你已經(jīng)學(xué)習(xí)了如何安裝loader,以及定義各個(gè)依賴配置。看起來好像也沒啥。但讓我們來深入擴(kuò)展一下這個(gè)例子。

代碼分離(require.ensure

上面的例子還不錯(cuò),但我們并不總是需要這個(gè)按鈕?;蛟S有的頁(yè)面沒有可以用來渲染按鈕的a,我們并不想在這樣的頁(yè)面引用按鈕的資源文件。這種時(shí)候代碼分離就能起到作用了。代碼分離是Webpack對(duì)于“整塊全部打包”vs“難以維護(hù)的手動(dòng)引導(dǎo)”這個(gè)問題而給出的解決方案。這需要在你的代碼中設(shè)定“分離點(diǎn)”:代碼可以據(jù)此分離成不同區(qū)域進(jìn)行按需加載。它的語(yǔ)法很簡(jiǎn)單:

import $ from "jquery";

// 這個(gè)是分割點(diǎn)
require.ensure([], () => {
  // 在這里import進(jìn)的代碼都會(huì)被打包到一個(gè)多帶帶的文件里
  const library = require("some-big-library");
  $("foo").click(() => library.doSomething());
});

require.ensure中的東西都會(huì)在打包結(jié)果中分離開來--只有當(dāng)需要加載它的時(shí)候Webpack才會(huì)通過AJAX請(qǐng)求進(jìn)行加載。也就是說我們實(shí)際上得到的是這樣的文件:

bundle.js
|- jquery.js
|- index.js // 入口文件
chunk1.js
|- some-big-libray.js
|- index-chunk.js // 回調(diào)中的代碼在這里

你不需要在任何地方引用chunk1.js文件,Webpack會(huì)幫你在需要的時(shí)候進(jìn)行請(qǐng)求。這意味著你可以像我們的例子一樣,根據(jù)邏輯需要引進(jìn)的資源全部扔進(jìn)代碼里。

只有當(dāng)頁(yè)面上有鏈接存在時(shí),再引用按鈕組件:

// 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");
    });
}

需要注意的一點(diǎn)是,因?yàn)?b>require不會(huì)同時(shí)處理default export和normal export,所以使用require引用資源里default export的時(shí)候,需要手動(dòng)加上.default。相比之下,import則可以進(jìn)行處理:

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

此時(shí)Webpack的output將會(huì)變得更復(fù)雜了。跑下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)都被打包進(jìn)了1.bundle.js,并且只在需要的時(shí)候才會(huì)被引用。現(xiàn)在為了能夠讓W(xué)ebpack在AJAX的時(shí)候找到這些資源,我們需要改下配置里的output

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

output.publicPath告訴Webpack,從當(dāng)前頁(yè)面的位置出發(fā)哪里可以找到需要的資源(在這個(gè)例子里是/builds/)。當(dāng)我們加載頁(yè)面的時(shí)候一切正常,而且能夠看見Webpack已經(jīng)根據(jù)頁(yè)面上預(yù)留的“錨”加載好了包。

如果頁(yè)面上缺少“錨”(代指link),那么只會(huì)加載bundle.js。通過這種方式,你可以做到在真正需要資源的時(shí)候才進(jìn)行加載,避免讓自己的頁(yè)面變成笨重的一坨。順帶一提,我們可以改變分割點(diǎn)的名字,不使用1.bundle.js而使用更加語(yǔ)義化的名稱。通過require.ensure的第三個(gè)參數(shù)來實(shí)現(xiàn):

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

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

這樣的話就會(huì)生成button.bundle.js而不是1.bundle.js

再加個(gè)組件(CommonChunksPlugin

來讓我們?cè)偌觽€(gè)組件吧:

// src/Components/Header.scss
.header {
  font-size: 3rem;
}

{{text}}
// 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})
        );
    }
}

將它在應(yīng)用中渲染出來:

// 如果有鏈接,則渲染按鈕組件
if (document.querySelectorAll("a").length) {
    require.ensure([], () => {
        const Button = require("./Components/Button");
        const button = new Button("google.com");

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

// 如果有標(biāo)題,則渲染標(biāo)題組件
if (document.querySelectorAll("h1").length) {
    require.ensure([], () => {
        const Header = require("./Components/Header");

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

瞅瞅使用了--display-chunks --display-modules標(biāo)記后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]

可以看出一點(diǎn)問題了:這兩個(gè)組件都需要jQuery和Mustache,這樣的話就造成了包中的依賴重復(fù),這可不是我們想要的。盡管Webpack會(huì)在默認(rèn)情況下進(jìn)行一定的優(yōu)化,但還得靠插件來加足火力搞定它。

插件和loader的不同在于,loader只對(duì)一類特定的文件有效,而插件往往面向所有文件,并且并不總是會(huì)引起轉(zhuǎn)化。Webpack提供了很多插件供你優(yōu)化。在這里我們使用CommonChunksPlugin插件:它會(huì)分析你包中的重復(fù)依賴并提取出來,生成一個(gè)完全獨(dú)立的文件(例如vendor.js),甚至生成你的主文件。

現(xiàn)在,我們想要把共同的依賴包從入口中剔除。如果所有的頁(yè)面都用到了jQuery和Mustache,那么就要把它們提取出來。更新下配置吧:

var webpack = require("webpack");

module.exports = {
    entry: "./src",
    output:  {
      // ...
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: "main", // 將依賴移到我們的主文件中
            children: true, // 再在所有的子文件中檢查依賴文件
            minChunks: 2, // 一個(gè)依賴重復(fù)幾次會(huì)被提取出來
        }),
    ],
    module:  {
      // ...
    }
};

再跑次Webpack,可以看出現(xiàn)在就好多了。其中,main是我們的默認(rèn)依賴。

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會(huì)在沒有該文件的情況下自動(dòng)生成builds/vendor.js,之后我們可以手動(dòng)引入:


你也可以通過async: true,并且不提供共同依賴包的命名,來達(dá)到異步加載共同依賴的效果。

Webpack有很多這樣給力的優(yōu)化方案。我沒法一個(gè)一個(gè)介紹它們,不過可以通過創(chuàng)造一個(gè)生產(chǎn)環(huán)境的應(yīng)用來進(jìn)一步學(xué)習(xí)。

飛躍到生產(chǎn)環(huán)境(production

首先,要在設(shè)置中添加幾個(gè)插件,但要求只有當(dāng)NODE_ENVproduction的時(shí)候才運(yùn)行它們:

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([
       // 生產(chǎn)環(huán)境下需要的插件
    ]);
}

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

Webpack也提供了一些可以切換生產(chǎn)環(huán)境的設(shè)置:

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

設(shè)置中的第一行表明在開發(fā)環(huán)境下,將開啟debug模式,代碼不再混做一團(tuán),利于本地調(diào)試。第二行則用來生產(chǎn)資源地圖(sourcemaps)。Webpack有一些方法可以生成sourcemaps,而eval則是在本地表現(xiàn)最贊的一個(gè)。在生產(chǎn)環(huán)境下,我們并不關(guān)心sourcemaps,因此關(guān)閉了這個(gè)選項(xiàng)。

現(xiàn)在來添加生產(chǎn)環(huán)境下的插件吧:

if (production) {
    plugins = plugins.concat([
        // 這個(gè)插件用來尋找相同的包和文件,并把它們合并在一起
        new webpack.optimize.DedupePlugin(),

        // 這個(gè)插件根據(jù)包/庫(kù)的引用次數(shù)來優(yōu)化它們
        new webpack.optimize.OccurenceOrderPlugin(),

        // 這個(gè)插件用來阻止Webpack把過小的文件打成多帶帶的包
        new webpack.optimize.MinChunkSizePlugin({
            minChunkSize: 51200, // ~50kb
        }),

        // 壓縮js文件
        new webpack.optimize.UglifyJsPlugin({
            mangle: true,
            compress: {
                warnings: false, // 禁止生成warning
            },
        }),

        // 這個(gè)插件提供了各種可用在生產(chǎn)環(huán)境下的變量
        // 通過設(shè)置為false,可避免生產(chǎn)環(huán)境下調(diào)用到它們
        new webpack.DefinePlugin({
            __SERVER__: !production,
            __DEVELOPMENT__: !production,
            __DEVTOOLS__: !production,
            "process.env": {
                BABEL_ENV: JSON.stringify(process.env.NODE_ENV),
            },
        }),

    ]);
}

我普遍使用的差不多就這么多了,不過Webpack還提供了非常多的插件,你可以自己去研究它們。也可以在NPM上找到很多用戶自己貢獻(xiàn)的插件。插件的鏈接在文末提供。

還有一個(gè)關(guān)于生產(chǎn)環(huán)境的優(yōu)化是給資源提供版本的概念。還記得output.filename里的bundle.js嗎?在這個(gè)配置里面,你可以使用一些變量,而[hash]則會(huì)給文件提供一段隨機(jī)的字符串。除此以外,我們想要包可以被版本化,因此添加了output.chunkFilename

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

因?yàn)闊o法得知每次打包生成的文件名,所以我們只在生產(chǎn)環(huán)境下使用它。除此之外,我們還想保證每次打包的時(shí)候,builds文件夾都會(huì)被清空以節(jié)約空間,因此使用了一個(gè)第三方插件:

$ 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"),

做完這些漂亮的優(yōu)化,來比較下結(jié)果的不同吧:

$ 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都做了什么:

在第一段代碼中,后兩個(gè)包非常輕量,異步請(qǐng)求不會(huì)占用多少HTTP帶寬,所以在生產(chǎn)環(huán)境下Webpack將它們打包進(jìn)了入口文件里

所有東西都?jí)嚎s過了。從322kb降到了97kb

但是這樣下去,Webpack豈不是會(huì)將js文件合并成巨大的一坨嗎?

是的,在這個(gè)小小的應(yīng)用中是這樣沒錯(cuò)。但是你需要這么想:你不需要考慮在什么時(shí)候合并什么。如果你的包中含有太多的依賴,它們會(huì)被移走到異步請(qǐng)求包中而不會(huì)被合并起來。反之,如果它們很小,不值得獨(dú)立加載,那么就會(huì)被合并。你只需要建立規(guī)則,Webpack會(huì)最大化的將其優(yōu)化。沒有人力勞作,不需要思考依賴關(guān)系,一切都是自動(dòng)化的。

或許你已經(jīng)注意到了,我沒有對(duì)HTML或CSS進(jìn)行壓縮。那是因?yàn)楫?dāng)debug模式開啟的時(shí)候,css-loaderhtml-loader已經(jīng)幫我們搞好了。這也是為什么Uglify是一個(gè)獨(dú)立插件的原因:在Webpack中沒有js-loader這種東西,Webpack自己就是個(gè)JS loader。

抽?。?b>extract-text-webpack-plugin

可能你已經(jīng)注意到了,從這個(gè)教程一開始,Webpack打包好之后,我們的樣式就直接插在網(wǎng)頁(yè)頁(yè)面上,簡(jiǎn)直不能更難看了。能通過Webpack把打包過的CSS生成獨(dú)立的文件嗎?當(dāng)然沒問題:

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

這個(gè)插件所做的就是我剛剛說的那些:從打出的最終包里面,提取出某一類內(nèi)容分離開來多帶帶引用。它通常被用于提取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方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)代表當(dāng)它處于已經(jīng)打包好的包("style")里時(shí),如何處理那些提取出來的東西;第二個(gè)參數(shù)代表當(dāng)它在主文件("css!sass")里時(shí),如何對(duì)待提取出的東西。當(dāng)它在包里時(shí),肯定不能直接將CSS加在生成的東西后面,所以先用style-loader進(jìn)行處理;而對(duì)于主文件里面的styles,則將它們放進(jìn)builds/bundle.css文件。我們來給應(yīng)用加一個(gè)主樣式:

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

// Rest of our file

跑下Webpack,就能看見已經(jīng)生成了bundle.css,可以把它引用進(jìn)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

如果你想提取出所有包里的樣式,則需要設(shè)置ExtractTextPlugin("bundle.css", {allChunks: true})

順帶一提,你也可以自定義文件名,就跟之前說的改變js output-file名稱一樣:ExtractTextPlugin("[name]-[hash].css")

使用圖片(url-loader&file-loader

到目前為止,我們還沒處理例如圖片、字體這樣的資源文件。它們?cè)赪ebpack中如何工作,我們又該如何優(yōu)化?接下來,我要在網(wǎng)站的背景里加入圖片,看起來一定酷酷的:

把圖片保存在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一定會(huì)告訴你:“你特么的想讓我對(duì)JPG做啥?”,那是因?yàn)檫€沒有加入對(duì)應(yīng)的loader。有兩種loader可以使用:file-loaderurl-loader

file-loader:返回一段指向資源的URL,允許你給文件加入版本的概念(默認(rèn))

url-loader:以data:image/jpeg;base64的形式返回URL

兩個(gè)方法不能說誰(shuí)好誰(shuí)壞:如果你的圖片大于2M的話那你一定不希望它直接夾雜在代碼中,而是獨(dú)立出去;而如果僅僅是2kb左右的小圖標(biāo)。那么合并在一起減少HTTP請(qǐng)求會(huì)更好。因此,我們兩個(gè)都要設(shè)置:

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

在這里,我們給url-loader了一個(gè)limit參數(shù),這樣,當(dāng)文件大小小于10kb的時(shí)候,會(huì)采取行內(nèi)樣式,否則的話,會(huì)轉(zhuǎn)到file-loader進(jìn)行處理。你也可以通過query傳遞一個(gè)Object來實(shí)現(xiàn)它:

{
    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圖像,那是因?yàn)槲覀兊男」穲D片比配置里限制的大小要小,因此被加到了行內(nèi)。訪問頁(yè)面,你就能看見這只可愛的小狗了。

這是一個(gè)非常強(qiáng)大的功能,它意味著Webpack可以智能的根據(jù)資源的大小和HTTP請(qǐng)求占有的比率,采取不同的優(yōu)化方案。還有一個(gè)叫做image-loader的loader,可以在打包前檢查所有圖片,避免圖片的重復(fù)壓縮。它有一個(gè)叫?bypassOnDebug 的參數(shù),通過它你可以只在生產(chǎn)環(huán)境下啟動(dòng)該插件。

還有很多優(yōu)秀的插件,我強(qiáng)烈建議你使用文末的鏈接去查看它們。

來個(gè)牛逼的熱加載(dev-server)

我們的生產(chǎn)環(huán)境以及整的差不多了,現(xiàn)在應(yīng)該更多的關(guān)心一下本地開發(fā)?;蛟S你以及注意到了,當(dāng)人們提及開發(fā)工具的時(shí)候,總是會(huì)提及熱加載:LiveReload,BrowserSync,或者其他的什么鬼東西。但是只有傻瓜才會(huì)整頁(yè)的刷新,我們則使用更高端的熱加載。因?yàn)閃ebpack可以確切的知道你依賴樹中某一點(diǎn)位置的代碼,因此每次的改變都會(huì)據(jù)此生成一個(gè)新的文件。簡(jiǎn)單的說,就是不需要刷新頁(yè)面就能將改變展現(xiàn)在屏幕上。

為了能夠使用HMR,我們需要一個(gè)server來啟動(dòng)熱加載。Webpack提供的dev-server可以完成這個(gè)任務(wù):

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

安裝下面的命令啟動(dòng)server,不能再簡(jiǎn)單了:

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

第一個(gè)標(biāo)記--inline是讓W(xué)ebpack把HMR邏輯直接寫入頁(yè)面上而不是放到iframe里,而第二個(gè)標(biāo)記則開啟了HMR。接下來,訪問http://localhost:8080/webpack-dev-server/,嗯還是那個(gè)正常的頁(yè)面。試著修改Sass文件,MAGIC!

你可以把webpack-dev-server作為自己本地的server。如果你打算一直使用HMR,就需要這么配置:

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

這樣的話,不管我們什么時(shí)候運(yùn)行webpack-dev-server,都會(huì)是HMR模式。值得一提的是,我們?cè)谶@里使用webpack-dev-server對(duì)資源進(jìn)行熱加載,但也可以使用在其他地方例如Express server上。Webpack提供了一個(gè)中間件,使得你可以把HMR的功能用在其他server上。

代碼不干凈的人都給我去罰站?。╬re-loader & lint)

如果你一直跟著本教程走,那或許會(huì)有這樣的疑問:為什么loader都在module.loaders中而插件不在?那當(dāng)然是因?yàn)檫€有其他可以配置進(jìn)module的東西~Webpack不只是有l(wèi)oader,也有pre-loader和post-loader:在main-loader運(yùn)行之前和之后發(fā)動(dòng)的玩意。舉個(gè)栗子:我基本可以確信自己在這個(gè)文章里面寫的代碼很糟糕,所以使用ESLint進(jìn)行代碼檢查:

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

新建一個(gè)肯定會(huì)引發(fā)錯(cuò)誤的.eslintrc文件:

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

現(xiàn)在增加pre-loader,語(yǔ)法和之前的一樣,只不過加在module.preLoaders里:

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

啟動(dòng)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

再舉個(gè)pre-loader的例子:每個(gè)組件里我們都引用了stylesheet,而它們都有相同命名的對(duì)應(yīng)模板。使用一個(gè)pre-loader可以自動(dòng)將有相同名稱的文件作為一個(gè)module載入:

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

通過這樣的方式告知Webpack,如果遇見和配置相同的HTML文件,則將它作為template 引入,同時(shí)引入和它同名的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也可以很強(qiáng)大。在文末你可以找到更多的loader

還沒看夠?

現(xiàn)在我們的應(yīng)用還很小,當(dāng)它變的龐大的時(shí)候,觀測(cè)依賴樹就變的非常有用了,從中可以看出我們做的是對(duì)是錯(cuò),應(yīng)用的瓶頸在哪里等等。Webpack知曉這一切,不過我們得禮貌的請(qǐng)教它才能知曉答案。為了做到這點(diǎn),你可以通過下面的命令運(yùn)行Webpack:

webpack --profile --json > stats.json

第一個(gè)標(biāo)記會(huì)讓W(xué)ebpack生成一個(gè)profile文件,而第二個(gè)則將它轉(zhuǎn)化為JSON格式。最終,講所有的output都生成了JSON文件。現(xiàn)在有很多網(wǎng)站都可以解析這個(gè)JSON文件,不過Webpack官方提供了一個(gè)解碼的網(wǎng)站W(wǎng)ebpack Analyze。將JSON文件導(dǎo)入,進(jìn)入Modules板塊,就可以看見自己依賴樹的可視化圖像:

小圓點(diǎn)越紅,則證明在打包的時(shí)候越困難。在這個(gè)例子中,jQuery作為最大的文件而成為罪魁禍?zhǔn)?。再瞅瞅網(wǎng)站上的其他模塊?;蛟S你無法從這個(gè)小小的例子里學(xué)到很多東西,但是這個(gè)工具在分析依賴樹和包的時(shí)候真的非常有用。

我之前提過,現(xiàn)在有很多服務(wù)提供可以對(duì)profile文件進(jìn)行分析。其中一個(gè)是Webpack Visualizer,它可以以餅狀圖的形式告知你各個(gè)文件占據(jù)了多大的比重:

就先講到這兒吧

對(duì)我而言,Webpack已經(jīng)取代了Grunt或者Gulp:大部分的功能可以使用Webpack替代,其他的則使用NPM腳本就夠了。在以前,每個(gè)任務(wù)中我們都要通過Aglio,把API文檔轉(zhuǎn)換為HTML,而現(xiàn)在只需要這么做:

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

即便是一些不需要打包和構(gòu)建的Glup任務(wù),Webpack都貼心的提供了對(duì)應(yīng)的服務(wù)。下面是一個(gè)將Glup融合進(jìn)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();
  });
});

因?yàn)閃ebpack具有Node API,因此可以很輕松了運(yùn)用在其他構(gòu)建體系中。不用多久你就能發(fā)現(xiàn)自己深愛著它無法自拔了。

不管怎樣,這篇文字帶你預(yù)覽了Webpack能夠幫你做的事情?;蛟S你認(rèn)為我們講了很多方面,但實(shí)際上這只是個(gè)表皮而已:multiple entry points, prefetching, context replacement等等都還沒有涉及到。Webpack是個(gè)強(qiáng)大的工具,也因此比那些傳統(tǒng)的工具更加難懂。但一旦你知道如何使用它,它就會(huì)為你鳴奏最悅耳動(dòng)聽的聲音。我曾在一些項(xiàng)目里使用過它,它提供的強(qiáng)大的優(yōu)化和自動(dòng)化讓我深深不能自拔。

資源

Webpack documentation

List of loaders

List of plugins

Sources for this article

Our Webpack configuration package

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/79374.html

相關(guān)文章

  • 「Vue實(shí)踐」武裝你的前端項(xiàng)目

    摘要:所有的高階抽象組件是通過定義選項(xiàng)來聲明的。所以一般在生命周期或者中,需要用實(shí)例的方法清除可當(dāng)你有多個(gè)時(shí),就需要重復(fù)性勞動(dòng)銷毀這件事兒。更多的配置請(qǐng)看雙端開啟開啟壓縮的好處是什么可以減小文件體積,傳輸速度更快。本文目錄 接口模塊處理 Vue組件動(dòng)態(tài)注冊(cè) 頁(yè)面性能調(diào)試:Hiper Vue高階組件封裝 性能優(yōu)化:eventBus封裝 webpack插件:真香 本文項(xiàng)目基于Vue-Cli3,想知...

    曹金海 評(píng)論0 收藏0
  • [Webpack并不難]把它當(dāng)人物養(yǎng)成游戲吧。

    摘要:發(fā)覺其實(shí)真的不難,畢竟它是一個(gè)工具,如果用起來都不順手,那為什么那么多人用,是不是。我覺得可以把當(dāng)成人物養(yǎng)成游戲來玩,哦不,來學(xué)。聽說把寶石放進(jìn)這工具就能自動(dòng)更新打包。公司最近弄來了一些未來的文言文,你把它翻譯成現(xiàn)代文吧。 前言 這段可以跳過看下面的。 本來,這個(gè)教程想完結(jié)的了。但回頭看自己寫的,感覺就像寫明了什么意思,具體怎么使用都沒說明白,而且讓人看得會(huì)有點(diǎn)乏味吧。 我也是剛開始...

    andot 評(píng)論0 收藏0
  • js框架或者庫(kù)

    摘要:使用開發(fā)富文本編輯器是開源的開發(fā)富文本編輯器開發(fā)框架。提供了一系列開發(fā)富文本編輯器的工具。本文通過開發(fā)一些簡(jiǎn)單的富文本編輯器介紹提供的各種能力簡(jiǎn)單易用的開源動(dòng)畫圖標(biāo)庫(kù)如果你用過等圖標(biāo),你可能會(huì)覺得它們很好看,用起來很很方便。 使用 flv.js 做直播 flv.js 是來自 Bilibli 的開源項(xiàng)目。它解析 FLV 文件喂給原生 HTML5 Video 標(biāo)簽播放音視頻數(shù)據(jù),使瀏覽器在...

    Winer 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<