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

資訊專欄INFORMATION COLUMN

[譯] Webpack 前端構建集成方案

lewif / 3324人閱讀

摘要:現在,讓我們創建項目的入口,并使用然后創建我們的配置,文件名為,的配置文件是一個,并且需要成一個對象在這里,告訴那些文件是你應用的入口。代碼分割便是用來解決之前所說的單集成模塊不可維護的引用的問題。

構建工具逐漸成為前端工程必備的工具,Grunt、Gulp、Fis、Webpack等等,譯者有幸使用過Fis、Gulp。
前者是百度的集成化方案,提供了一整套前端構建方案,優點是基本幫你搞定了,但是靈活性相對比較低,社區也沒那么大;后者提供了非常靈活的配置,簡單的語法可以配置出強大的功能,流控制也減少了編譯時的時間,可以和各種插件配合使用。
譯者因為要使用AMD模塊機制,開始接觸了webpack,發現官網上講的晦澀難懂,無法實踐,而國內雖有博客也講解一些入門的教程,但是要么篇幅過短,要么只講各種配置貼各種代碼,然后谷歌發現了國外大牛寫的這篇博客,發現講的非常通俗易懂,配合實踐和代碼,讓譯者感慨萬千,瞬間打開了一扇大門。

原文鏈接:https://blog.madewithlove.be/post/webpack-your-bags/
作者:Maxime Fabre
譯者:陳堅生

也許你已經聽說過這個叫做webpack的新工具了。有些人稱它是一個像gulp一樣的構建工具, 有些人則認為它是像browserify一樣的打包工具, 如果你并沒有深入去了解它你可能就會產生疑惑。就算你仔細地研究它你也可能依舊困惑,因為webpack的官網介紹webpack的時候同時提到了這兩個功能。

一開始對"webpack 是什么"的模糊概念使得我很挫敗以至于我直接關掉了webpack的網頁。到了現在,我已經有了一套自己的構建系統并為此覺得很開心。如果你和我一樣緊跟javascript的潮流,那么錯過如此好的工具將是非??上У氖虑?。(這句話翻譯的不好:And if you follow closely the very fast Javascript scene, like me, you’ve probably been burnt in the past by jumping on the bandwagon too soon.?)因為對webpack有了一定的實踐和經驗,我決定寫這篇文章來更加清晰地解釋“什么是webpack”還有webpack的重要性和優勢。

什么是webpack?

首先讓我們來回答標題中的問題:webpack到底是一個構建系統還是一個打包工具?好吧, 它都有——但不是說它做了兩者而是說它合并了兩者。webpack并不構建你的資源(assets),然后分別對你的模塊進行打包,它認為你的資源都是模塊

更精確地說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) // "

Hello

"

你所有的資源都被認為是模塊,因此是可以被引用的、修改、操作,最后可以被打包進你的終極模塊中。

為了使得這樣能夠運行,你需要在你的webpack配置中注冊loaders。 Loaders 可以認為是一些小型的插件,簡單地說就是讓webpack在處理的時候,當遇到這種類型的文件時,做這樣的操作(操作就是Loaders也就是你的配置)。以下是Loaders配置的一些例子:

{ // When you import a .ts file, parse it with Typescript 
    test: /.ts/, 
    loader: "typescript",
},{
    // When you encounter images, compress them with image-webpack (wrapper around imagemin) 
    // and then inline them as data64 URLs 
    test: /.(png|jpg|svg)/, 
    loaders: ["url", "image-webpack"],
},{ 
    // When you encounter SCSS files, parse them with node-sass, then pass autoprefixer on them 
    // then return the results as a string of CSS 
    test: /.scss/, 
    loaders: ["css", "autoprefixer", "sass"],
}

總之到食物鏈的末尾,所有的Loaders返回字符串。這個機制使得Webpack可以將它們引進到javascript的包中。當你的sass文件被Loaders轉化后,它在內部會像這樣被傳遞:

export default "body{font-size:12px}";
為什么要這樣做?

當你明白webpack做了什么后,隨之而來的問題大部分是這樣做的好處是什么? “圖片、css在我的JS中?這是什么鬼?” 好吧,思考下我們最近一直推崇的并被教育應該這樣做的,把所有的東西打包成一個文件,以減少http請求……

這導致了一個很大的缺點就是大多數人把當前的所有資源都打包到一個app.js文件中,然后包含在所有的頁面。這意味著任何給定頁面上大部分加載的資源都是非必須的。如果你不這樣做,那么你很可能要手工引入資源,導致需要維護和跟蹤一個巨大的依賴書,用來記錄那個頁面用到了樣式表A和樣式表B。

無論方法是正確的還是錯誤的。 想象一下webpack作為一個中間者,它不止是一個構建系統或者一個打包工具,它是一個頑皮的智能模塊包裝系統。一旦被很好地配置,它會比你更加了解你的棧,所以它會比你更加清楚如何更好地優化。

讓我們一起構建一個簡單的APP

為了讓你更簡單地了解webpack的優點,我們將一起構建一個小型的App并對資源進行打包。對于本教程,我建議運行Node4或者Node5以及NPM3的平行依賴樹以避免在使用webpack時遇到坑爹地問題。如果你還沒有NPM3,你可以通過

npm install npm@3 -g 

來安裝。

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

我也建議你添加node_modules/.bin 到你的環境變量中以避免每次都輸入 node_modules/.bin/webpack 來運行命令。后面的所有例子我將不會使用node_modules/.bin這個命令了。

基本的使用

讓我們創建我們的項目并安裝webpack,同時我們引入jQuery以便后面使用。

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

現在,讓我們創建項目的入口,并使用es2015:

src/index.js

var $ = require("jquery");
$("body").html("hello");

然后創建我們的webpack配置,文件名為webpack.config.js, webpack的配置文件是一個javascript,并且需要export成一個object(對象)

webpack.config.js

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

在這里,entry告訴webpack那些文件是你應用的入口。入口文件位于你依賴樹的頂部。然后我們告訴它去編譯我們的文件到__builds__這個文件夾中并使用((bundle.js這個名字。接下來我們創建我們的index.html:

    
    
    
        
        webpack
    
    
        

my title

click me

運行webpack,如果一切運行正常,我們會收到一段信息告訴我們webpack成功編譯打包到了bundle.js中:

Version: webpack 1.13.1
Time: 382ms
    Asset    Size  Chunks             Chunk Names
bundle.js  267 kB       0  [emitted]  main
   [0] ./src/index.js 58 bytes {0} [built]
    + 1 hidden modules

在這里你可以看到webpack告訴你你的bundle.js包含了我們的入口文件index.js同時還有一個隱藏的模塊。這個隱藏的模塊便是jquery,webpack默認會隱藏不屬于你的模塊,如果要看所有被webpack隱藏的模塊,我們可以向webpack傳參 --display-modules:

>webpack --display-modules
Hash: 20aea3445ac35ac27c32
Version: webpack 1.13.1
Time: 382ms
    Asset    Size  Chunks             Chunk Names
bundle.js  267 kB       0  [emitted]  main
   [0] ./src/index.js 58 bytes {0} [built]
   [1] ./~/jquery/dist/jquery.js 258 kB {0} [built]

你也可以運行 webpack --watch 讓webpack去監聽你的文件,一旦有改變則自動編譯。

建立我們的第一個Loader

還記得我們討論過的webpack如何引進css和html以及其他所有類型的資源嗎?在那里適合?如果你有投身到這幾年的web組件化發展的事業中(angular2, vue, react, polymer, x-tag等等),你應該聽說過關于構建webapp的一個新的概念,不適用單一集成的ui模塊,而是將ui分解為多個小型的可重用的ui?,F在為了讓組件真正獨立,他們需要能夠將所有依賴都引入他們自身中。想象一下一個按鈕肯定有html、一些腳本讓它能夠交互,當然也需要一些樣式。最好能在需要到這個組件的時候所有這些資源才被加載。只有當我們引入這個按鈕的時候,我們才拿到相關的資源。

讓我們來寫button組件。首先,我假設大多數人都習慣了es2015,我們將添加第一個Loader: babel。安裝Loader于webpack中需要做兩件事情:**npm install {whatever}-loader, 然后添加它到你webpack配置中,即module.loaders。如下所示:

$ npm install babel-loader --save-dev

由于babel-loader并不會自動安裝babel, 我們需要自己安裝babel-core還有es2015 preset:

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

然后我們創建.babelrc來告訴babel應該用哪一種preset,文件是json格式,在本例子中,我們告訴它使用es2015 preset

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

現在已經配置并安裝好babel了。我們需要babel運行在所有的以.js結尾的文件中,但是由于webpack會遍歷包括第三方在內的所有依賴包,因此我們要防止babel運行在如jquery這樣的第三方庫中。Loaders可以擁有一個include或者一個exclude規則,它可以是一個字符串、一個正則表達、一個回調函數或者其他任何你想要的。在本例子中,我們想要babel只運行在我們的文件上,因此我們將include我們的資源文件夾:

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

現在我們可以重寫我們的index.js(我們在之前引入了babel)。并且接下來的例子我們也將使用es6

寫一個小型的組件

現在我們開始寫一個小型的button組件, 它將有一些scss樣式,一個html模板,還有一些行為。我們將安裝我們需要的東西。手下我們需要mustache,一個非常輕量級的模板渲染庫,同時還有sasss和html的Loaders。同時,由于Loader可以像管道一樣將處理后的結果順序傳遞下去,我們將需要一個cssloader來處理sass Loader處理后的結果?,F在,我們有了我們的css, 有很多方式可以處理他們,這次我們使用的是style-loader,它可以動態地將css注入到頁面中去。

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

我們由右到左以‘!’為分割向配置文件傳遞loader以告訴webpack如何將匹配到的文件順序傳遞給Loaders,你也可以使用數組來進行傳遞,當然順序也要是由右到左

module.exports = {
    entry: "./src",
    output: {
        path: "builds",
        filename: "[name].js",
    },
    module: {
        loaders: [
            {
                test: /.js/,
                loader: "babel",
                include: __dirname + "/src",
            }
        ],
        {
            test: ".scss",
            loader: "style!css!sass",
            // loaders: ["style", "css", "sass"],
        },
        {
            test: /.html/,
            loader: "html",
        }
    }
};

loaders已經配置安裝好了,我們可以開始寫我們的按鈕了

src/Components/Button.scss

.button {
    background: tomato;
    color: white;
}

src/Components/Button.html

{{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();

        $(node).html(
            Mustache.render(template, {text})
            );

        $(".button").click(this.onClick.bind(this));
    }
}

你的button.js現在是100%自引用并且在那里都可以被引用, 現在我們只需要將button渲染到我們的頁面來

src/index.js

import Button from "./Components/Button";
Button = Button.default
const button  = new Button("google.com");
button.render("a");

運行webpack,刷新頁面,你應該可以看到我們丑陋的按鈕,并有對應的行為,(這一步有問題,編譯成功了,但是無法new一個,提示 _Button2.default is not a constructor 錯誤)
至此你學會了如何建立loaders以及如何定義應用每一部分的依賴?,F在貌似還不不出有什么用處,讓我們更加深入到例子中去。

代碼分割

上面的例子會一直引用button,當然,這并沒有什么問題,但我們并不總是一直需要我們的按鈕。也許在一些頁面沒有按鈕需要渲染。在這種情況下,我們不想去引入按鈕的樣式、模板等。這個時候就是代碼分割出場的時候了(code spliting)。代碼分割便是webpack用來解決之前所說的單集成模塊 VS 不可維護的引用的問題。分割點(split points):你的代碼被分割為多個文件并被按需請求加載。語法非常簡單:

import $ from "jquery";

// This is a split point
require.ensure([], () => {
  // All the code in here, and everything that is imported
  // will be in a separate file
  const library = require("some-big-library");
  $("foo").click(() => library.doSomething());
});

任何寫在require.ensure回調中的東西會被分隔到一個數據塊,一個隔離的文件,webpack會在需要的時候,通過ajax請求去加載。這意味著,我們會看到如下面的依賴樹:

bundle.js
|- jquery.js
|- index.js // our main file
chunk1.js
|- some-big-libray.js
|- index-chunk.js // the code in the callback

并且我們不用去引入chunk1.js或者去加載它,webpack已經幫我們做了這些事情。這意味著我們可以通過各種各樣的邏輯去分割我們的代碼。在接下來的例子中,我們只想在頁面有鏈接的時候去加載我們的button組件

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)。原因在于require無法同時處理default和正常的導出,所以你需要顯示申明想要用哪一個。而import則有一個系統來解決這個問題,所以它知道如何處理。(eg. import foo from "bar" vs import {baz} from "bar")

現在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]
    [1] ./src/Components/Button.js 1.94 kB {1} [built]
    [2] ./~/jquery/dist/jquery.js 259 kB {1} [built]
    [3] ./src/Components/Button.html 72 bytes {1} [built]
    [4] ./~/mustache/mustache.js 19.4 kB {1} [built]
    [5] ./src/Components/Button.scss 1.05 kB {1} [built]
    [6] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built]
    [7] ./~/css-loader/lib/css-base.js 1.51 kB {1} [built]
    [8] ./~/style-loader/addStyles.js 7.21 kB {1} [built]

從輸出數據你可以看到,我們的入口文件(bundle.js)現在只包含webpack的邏輯,其他的腳本(jquery、mustache、button)全都在1.bundle.js中,并只有當我們頁面中有連接的時候才會加載進來?,F在為了讓webpack知道到哪里去ajax我們的數據塊,我們需要配置下我們的文件:

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

publishPath告訴webpack到哪里去找資源, 至此,我們運行webpack,由于頁面有連接,因此webpack加載了button組件。注意: 我們可以對數據塊進行命名來替代默認的1.bundle.js:

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

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

嘗試了,發現并沒有什么用……是我打開的方式不對么

添加第二個組件

src/Components/Header.scss

.header {
  font-size: 3rem;
}

src/Components/Header.html

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

然后在應用中渲染它:

// If we have an anchor, render the Button component on it
if (document.querySelectorAll("a").length) {
    require.ensure([], () => {
        const Button = require("./Components/Button").default;
        const button = new Button("google.com");

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

// If we have a title, render the Header component on it
if (document.querySelectorAll("h1").length) {
    require.ensure([], () => {
        const Header = require("./Components/Header").default;

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

再次運行webpack查看依賴情況,你會發現兩個組件都需要jquery、mustache,意味著這些依賴模塊被重復定義于我們的數據塊中,這并不是我們想要的。默認情況webpack并不對此進行優化。但是webpack可以通過插件的形式提供強力的優化方案。

插件(plugins)和loaders不同,loaders只執行與特定類型的文件,plugins執行于所有的文件并提供更多豐富的功能。webpack擁有大量的插件來處理各種各樣的優化。CommonChunksPlugin可以用來解決這個問題的插件, 它通過遞歸分析你的依賴包,找到公用的模塊并將它們分離成一個獨立的文件中,當然你也可以寫入到入口文件中。

在接下來的例子中,我們將公用的模塊放到了我們的入口文件中,因為如果所有的頁面有引用了jquery和mustache,我們就把它們放到頂端。接下來讓我們更新下我們的配置:

plugins: [
    new webpack.optimize.CommonsChunkPlugin({
        name:      "main", // Move dependencies to our main file
        children:  true, // Look for common dependencies in all children,
        minChunks: 2, // How many times a dependency must come up before being extracted
    })
]

如果我們再次運行webpack, 我們可以發現公用的組件已經被提取到了頂部:

chunk    {0} bundle.js (main) 287 kB [rendered]
    [0] ./src/index.js 550 bytes {0} [built]
    [2] ./~/jquery/dist/jquery.js 259 kB {0} [built]
    [4] ./~/mustache/mustache.js 19.4 kB {0} [built]
    [7] ./~/css-loader/lib/css-base.js 1.51 kB {0} [built]
    [8] ./~/style-loader/addStyles.js 7.21 kB {0} [built]
chunk    {1} 1.bundle.js 3.28 kB {0} [rendered]
    [1] ./src/Components/Button.js 1.94 kB {1} [built]
    [3] ./src/Components/Button.html 72 bytes {1} [built]
    [5] ./src/Components/Button.scss 1.05 kB {1} [built]
    [6] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built]
chunk    {2} 2.bundle.js 2.92 kB {0} [rendered]
    [9] ./src/Components/Header.js 1.62 kB {2} [built]
   [10] ./src/Components/Header.html 64 bytes {2} [built]
   [11] ./src/Components/Header.scss 1.05 kB {2} [built]
   [12] ./~/css-loader!./~/sass-loader!./src/Components/Header.scss 192 bytes {2} [built]

如果我們將name改為"vender‘:

new webpack.optimize.CommonsChunkPlugin({
    name:      "verder", // Move dependencies to our main file
    children:  true, // Look for common dependencies in all children,
    minChunks: 2, // How many times a dependency must come up before being extracted
})

由于該數據塊還沒有創建出來,webpack會自動創建builds/verder.js的文件,然后供我們在html中引用,這一步筆者試了,發現無法創建vender這個依賴,所有公用依賴也沒有被提取出來,不知道是不是windows的問題。

你還可以使得公用模塊文件以異步請求的方式加載進來,設置屬性async: true便可以了。webpack還有大量的功能強大智能化的插件,我無法一個個介紹它們,但是作為練習,讓我們為應用創建一個生產環境

生產和超越

首先,我們將添加幾個插件到我們的配置中去,但我們只想要在生產環境中去加載并使用這些插件。所以我們要添加邏輯來控制我們的配置。

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

var plugins = [
    new webpack.optimize.CommonsChunkPlugin({
        name:      "main", // Move dependencies to our main file
        children:  true, // Look for common dependencies in all children,
        minChunks: 2, // How many times a dependency must come up before being extracted
    }),
];

if (production) {
    plugins = plugins.concat([
       // Production plugins go here
    ]);
}

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 generation),webpack有幾個方式來渲染sourcemaps,eval是在本地開發中最好的一種,但在生產環境中,我們并不在意這些,所以在生產環境中我們禁止了它。接下來我們可以添加生產環境中用到的插件:

if (production) {
    plugins = plugins.concat([

        // This plugin looks for similar chunks and files
        // and merges them for better caching by the user
        new webpack.optimize.DedupePlugin(),

        // This plugins optimizes chunks and modules by
        // how much they are used in your app
        new webpack.optimize.OccurenceOrderPlugin(),

        // This plugin prevents Webpack from creating chunks
        // that would be too small to be worth loading separately
        new webpack.optimize.MinChunkSizePlugin({
            minChunkSize: 51200, // ~50kb
        }),

        // This plugin minifies all the Javascript code of the final bundle
        new webpack.optimize.UglifyJsPlugin({
            mangle:   true,
            compress: {
                warnings: false, // Suppress uglification warnings
            },
        }),

        // This plugins defines various variables that we can set to false
        // in production to avoid code related to them from being compiled
        // in our final bundle
        new webpack.DefinePlugin({
            __SERVER__:      !production,
            __DEVELOPMENT__: !production,
            __DEVTOOLS__:    !production,
            "process.env":   {
                BABEL_ENV: JSON.stringify(process.env.NODE_ENV),
            },
        }),

    ]);
}

這些我最常使用到的插件,webpack還提供了很多其他的插件供你去協調你的模塊和數據塊。同時在npm上也有自由開發者開發貢獻出來的擁有強大功能的插件。具體可以參考文章最后的鏈接。

現在你希望你生產環境下的資源能按版本發布。還記得我們為bundle.js設置過的output.filename屬性嗎?這里有幾個變量供你使用,一個是[hash], 和最終生成的bundle.js內容的哈希值保持一致。我們也想我們的數據塊(chunks)也版本話,我們將設置output.chunkFilename屬性來實現同樣的功能:

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

在我們這個簡單的應用中并沒有一個方法來動態檢索編譯后文件的名字啊,我們將只在生產環境中使用版本化的資源。同時我們想在生產環境中清空我們的打包環境,讓我們添加一個三方插件:

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

將這個插件配置到webpack中:

var webpack     = require("webpack");
var CleanPlugin = require("clean-webpack-plugin");

// ...

if (production) {
    plugins = plugins.concat([

        // Cleanup the builds/ folder before
        // compiling our final assets
        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到底做了什么:一開始,由于我們的例子非常的簡單輕量級,我們的兩個異步數據塊不值得使用兩個異步請求去獲取,所以webpack將它們合并回了入口文件中;其次,所有的文件都被合理地壓縮了。我們從原本的3個請求總大小為322kb變成了一個97kb大小的文件。

But wasn’t the point of Webpack to stem away for one big ass JS file?
但是webpack不是不提倡合并成一個文件嗎?

是的,它的確不提倡,當時如果我們的app很小,代碼量很少,它是提倡這樣做的。但請考慮如下情況,你不需要去考慮什么時候什么地方做什么合并。如果你的數據塊突然間依賴了很多模塊,那么webpack會讓它變成異步加載而不是合并到入口文件中, 同時如果這些模塊的依賴有公用的,那么這些模塊也會被抽離出來等等。你只需要設立好規則,然后,webpack變回自動提供最好的優化方案。不用手冊,不用思考模塊依賴的順序,所有的東西都自動化了。

你可能發現我并沒有設置任何東西去壓縮我們的HTML和CSS,這是因為CSS-loader和html-loader已經默認完成了這些事情。

因為webpack是本身就是一個JS-loader,因此在webpack中沒有js-loader,這也是uglify是一個獨立引進來的插件的原因。

信息抽取

現在你可能發現,一開始我們頂一個的樣式被分開幾段插入到頁面從而導致了FOUAP(Flash of Ugly Ass Page),如果我們可以把所有的樣式都合并到一個文件中不是更好嗎?是的,我們可以使用另一個插件:

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

這個組件做了我剛才說的事情,它收集了你最后的bundle后內容里所有的樣式,并將它們合成到一個文件中。

讓我們把它引入:

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"), // <=== where should content be piped
    new webpack.optimize.CommonsChunkPlugin({
        name:      "main", // Move dependencies to our main file
        children:  true, // Look for common dependencies in all children,
        minChunks: 2, // How many times a dependency must come up before being extracted
    }),
];

// ...

module.exports = {
    // ...
    plugins: plugins,
    module:  {
        loaders: [
            {
                test:   /.scss/,
                loader: ExtractPlugin.extract("style", "css!sass"),
            },
            // ...
        ],
    }
};

Now the extract method takes two arguments: first is what to do with the extracted contents when we’re in a chunk ("style"), second is what to do when we’re in a main file ("css!sass"). Now if we’re in a chunk, we can’t just magically append our CSS to the generated one so we use the style loader here as before, but for all the styles that are found in the main file, pipe them to a builds/bundle.css file. Let’s test it out, let’s add a small main stylesheet for our application:

(這一段翻譯得不好,請看上面的原文)

現在可以看到 extract 方法傳入了兩個參數: 第一個是當我們在style數據塊中我們要對引出的內容做什么;第二是當我們在入口文件css!sass中要做的事情。如果我們在一個數據塊中,我們不能簡單地把我們的CSS添加到我們的css文件中,所以我們在此之前使用style加載器,但對于在入口函數找到的所有樣式,我們將它們傳遞到builds/bundle.css文件中。讓我們為應用添加一個主樣式表。

問題:這里遇到一個問題,每次修改主樣式表(styles.scss)后,如果是有監聽的話,webpack的自動重編譯是會出錯的,需要重新保存一次腳本才能讓其正確編譯成功,不知道是什么問題導致的。

src/styles.scss

body {
  font-family: sans-serif; 
  background: darken(white, 0.2);
}

src/index.js

import "./styles.scss";

如果你想導出所有的樣式,你也可以向ExtractTextPlugin傳參(’bundle.css", {allChunks: true})。如果你想在你的文件名中使用變量,你也可以傳入 [name]-[hash].css。

圖片處理

腳本處理已經基本可以,但是我們還沒有處理如圖片、字體等資源。在webpack中要怎么處理這些資源并得到最好的優化?接下來讓我們下載一張圖片并讓它作為我們的背景,因為我覺得它很酷:

將這張圖片保存在img/puppy.png& 并更新我們的sass文件:

body {
    font-family: sans-serif;
    background-color: #333;
    background-image: url("../img/puppy.jpg");
    background-size: cover;
}

如果你這樣做的話,webpack會和你說:“我tmd要我怎么處理jpg這東西?”,因為我們沒有一個Loader用來處理它。有兩個自帶的加載器可以用來處理這些資源,一個是file-loader,另一個是url-loader,第一個不會做什么改變,只會返回一個url,并可以版本化設置,第二個可以將資源轉化為base64的url

這兩個加載器各有優缺點:如果你的背景圖片是2mb大的圖片,你不會想將它作為base64引入到樣式表中而更加傾向于多帶帶去加載它。如果它只是一個2kb的圖片,那么則引入它從而減少http請求次數會更好:

所以我們把這兩個加載器都安裝上:

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

我們在這里向url-loader傳遞了限制類的參數,告訴它:如果資源文件小于10kb則引入,否則,使用file-loader去處理它。語法使用的是查詢字符串,你也可以使用對象去配置加載器:

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

好了,讓我們來試試看

                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文件,因為我們的puppy圖片太小了,它被直接引入到bundle.css文件中了。

webpack會智能地根據大小或者http請求來優化資源文件。還有很多加載器可以更好地處理,最常用的一個是image-loader,可以在合并的時候對圖片進行壓縮,甚至可以設置?bypassOnDebug讓你只在生產環境中使用。像這樣的插件還有很多,我鼓勵你在文章的末尾去看看這些插件。

實時監聽編譯

我們的生產環境已經搭建好了,接下來就是實時重載:LiveReload、BrowserSync,這可能是你想要的。但是刷新整個頁面很消耗性能,讓我們使用更吊的裝備hot module replacement或者叫做hot reload。由于webpack知道我們依賴樹的每一個模塊的位置,修改的時候就可以很簡單地替換樹上的某一塊文件。更清晰地說,當你修改文件的時候,瀏覽器不用刷新整個頁面就可以看到實時變化。

要使用HMR,我們需要一個支持hot assets的服務器。Webpack有一個dev-server供我們使用,安裝下:

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

然后運行該服務器:

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

第一個參數告訴webpack將HMR邏輯引入到頁面中(而不使用一個iframe去包含頁面),第二個參數是啟動HMR(hot module reload)。現在讓我們訪問web-server的地址:http://localhost:8080/webpack-dev-server/ 。嘗試改變文件,會看到瀏覽器上實時的變化

你可以使用這個插件作為你的本地服務器。如果你計劃一直使用它來做HMR,你可以將其配置到webpack中。

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

配置后,無論我們什么時候運行ewbpack-dev-server,它都會在HMR模式。當然,還有很多配置供你配置,例如提供一個中間件供你在express服務器中使用HMR模式。

規范的代碼

如果你一直跟著本文實踐,你肯定發現了奇怪的地方:為什么Loaders被放在了Module.loaders中,而plugins卻沒有?這當然是因為還有其他東西你可以放在module中!Webpack不僅有loaders,它也有pre-loaders和post-loaders:它們會在主加載器加載前/加載后執行。來個例子,很明顯我的代碼非常糟糕,所以在轉化前我們使用eslint來檢測我們的代碼:

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

創建一個小型的eslintrc文件:

.eslintrc

parser: "babel-eslint"
rules: 
  quotes: 2

現在我們添加我們的preloader,我們使用和之前一樣的語法:

        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

在舉另一個例子,現在我們的組件都會引入同樣名字的樣式表以及模板。讓我們使用一個預加載器來自動加載:

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

這告訴webpack,如果你定義了一個同樣名字的html文件,會把它以template的名字引入,同樣的也會引入同名的sass文件?,F在我們可以修改我們的組件:

import $ from "jquery"
import Mustache from "mustache"
// import template from "./Header.html"
// import "./Header.scss"

pre-loader的功能強大,post-loader也一樣,你也可以從文章末尾看到很多有用的加載器并使用它們。

你還想了解更多嗎?

現在我們的應用還很小,但隨著應用的增大,了解正式的依賴樹情況是很有用的。可以幫助我們了解我們做的是否正確,我們的應用的瓶頸在哪里。webpack知道所有這些事情,但我們需要告訴他顯示給我們看,我們可以到處一個profile文件:

webpack --profile --json > stats.json

第一個參數告訴webpack生成一個profile 文件,第一個指定生成的格式。有多個站點提供分析并可視化這些文件的功能,webpack官方也提供解析這些信息的功能。所以你可以到webpack analysis引入你的文件。選擇modules 標簽然后便可以看到你的可視化依賴樹。另一個我比較喜歡的是webpack visualizer
用圓環圖的形式表示你的包大小占據情況。

That"s all folks

我知道在我的案例中,Webpack已經完全取代了Grunt或者gulp了,大部分功能已經由webpack來渠道,剩下的值通過npm script。過去使用Aglio轉化我們的API文檔為html我們使用的是任務型,現在可以這樣做:

package.json

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

無論你在gulp中有多么復雜不關乎打包的任務,Webpack都可以很好地配合。提供一個在Gulp中集成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是一個強大的工具,當然代價是更多的配置需要你去寫。不過一旦你知道如何馴服它,它會給你最好的優化方案。我在幾個項目中使用了它,它也提供了強大的優化方案和自動化,讓我無法不用它。

資源

Webpack documentation

List of loaders

List of plugins

Sources for this article

Our Webpack configuration package

譯者拓展鏈接:

style-loader文檔

備注

開發過程遇到的問題可以查看原文下的評論或和譯者交流學習。

譯者英文水平有限,如果哪里翻譯的不好歡迎指正,相關的代碼可參考譯者的demo2和demo6,demo4是使用Webpack + Vue寫的DEMO,有興趣的同學也可以看看。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86327.html

相關文章

  • 正在暑假中的《課多周刊》(第1期)

    摘要:正在暑假中的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。原理微信熱更新方案漲知識了,熱更新是以后的標配。 正在暑假中的《課多周刊》(第1期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大的動力。 遠上寒山石徑...

    liukai90 評論0 收藏0
  • 正在暑假中的《課多周刊》(第1期)

    摘要:正在暑假中的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。原理微信熱更新方案漲知識了,熱更新是以后的標配。 正在暑假中的《課多周刊》(第1期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大的動力。 遠上寒山石徑...

    yintaolaowanzi 評論0 收藏0
  • 2017-08-02 前端日報

    摘要:前端日報精選浮點數精度之謎前端面試必備基本排序算法從賀老微博引出的遍歷器加速那些奧秘進階之深入理解數據雙向綁定全棧天中文深入理解筆記用模塊封裝代碼前端架構經驗分享周二放送自制知乎專欄譯在大型應用中使用的五個技巧掘金開發指南眾成 2017-08-02 前端日報 精選 JavaScript 浮點數精度之謎前端面試必備——基本排序算法從賀老微博引出的遍歷器(Iterators)加速那些奧秘J...

    Worktile 評論0 收藏0
  • ( & 轉載) 2016 JavaScript 后起之秀

    摘要:在年成為最大贏家,贏得了實現的風暴之戰。和他的競爭者位列第二沒有前端開發者可以忽視和它的生態系統。他的殺手級特性是探測功能,通過檢查任何用戶的功能,以直觀的方式讓開發人員檢查所有端點。 2016 JavaScript 后起之秀 本文轉載自:眾成翻譯譯者:zxhycxq鏈接:http://www.zcfy.cc/article/2410原文:https://risingstars2016...

    darry 評論0 收藏0
  • webpack 教程資源收集

    學習的過程中收藏了這些優秀教程和的項目,希望對你有幫助。 github地址, 有不錯的就更新 官方文檔 中文指南 初級教程 webpack-howto 作者:Pete Hunt Webpack 入門指迷 作者:題葉   webpack-demos 作者:ruanyf 一小時包教會 —— webpack 入門指南 作者:VaJoy Larn   webpack 入門及實踐 作者:...

    Backache 評論0 收藏0

發表評論

0條評論

lewif

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<