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

資訊專欄INFORMATION COLUMN

手把手教你擼一個(gè) Webpack Loader

sugarmo / 3065人閱讀

摘要:夾在中間的被鏈?zhǔn)秸{(diào)用,他們拿到上個(gè)的返回值,為下一個(gè)提供輸入。最終把返回值和傳給。前面我們說過,也是一個(gè)模塊,它導(dǎo)出一個(gè)函數(shù),該函數(shù)的參數(shù)是的源模塊,處理后把返回值交給下一個(gè)。

文:小 boy(滬江網(wǎng)校Web前端工程師)

本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明作者及出處

經(jīng)常逛 webpack 官網(wǎng)的同學(xué)應(yīng)該會(huì)很眼熟上面的圖。正如它宣傳的一樣,webpack 能把左側(cè)各種類型的文件(webpack 把它們叫作「模塊」)統(tǒng)一打包為右邊被通用瀏覽器支持的文件。webpack 就像是魔術(shù)師的帽子,放進(jìn)去一條絲巾,變出來一只白鴿。那這個(gè)「魔術(shù)」的過程是如何實(shí)現(xiàn)的呢?今天我們從 webpack 的核心概念之一 —— loader 來尋找答案,并著手實(shí)現(xiàn)這個(gè)「魔術(shù)」??赐瓯疚模憧梢裕?/p>

知道 webpack loader 的作用和原理。

自己開發(fā)貼合業(yè)務(wù)需求的 loader。

什么是 Loader ?

在擼一個(gè) loader 前,我們需要先知道它到底是什么。本質(zhì)上來說,loader 就是一個(gè) node 模塊,這很符合 webpack 中「萬(wàn)物皆模塊」的思路。既然是 node 模塊,那就一定會(huì)導(dǎo)出點(diǎn)什么。在 webpack 的定義中,loader 導(dǎo)出一個(gè)函數(shù),loader 會(huì)在轉(zhuǎn)換源模塊(resource)的時(shí)候調(diào)用該函數(shù)。在這個(gè)函數(shù)內(nèi)部,我們可以通過傳入 this 上下文給 Loader API 來使用它們。回顧一下頭圖左邊的那些模塊,他們就是所謂的源模塊,會(huì)被 loader 轉(zhuǎn)化為右邊的通用文件,因此我們也可以概括一下 loader 的功能:把源模塊轉(zhuǎn)換成通用模塊。

Loader 怎么用 ?

知道它的強(qiáng)大功能以后,我們要怎么使用 loader 呢?

1. 配置 webpack config 文件

既然 loader 是 webpack 模塊,如果我們要使其生效,肯定離不開配置。我這里收集了三種配置方法,任你挑選。

單個(gè) loader 的配置

增加 config.module.rules 數(shù)組中的規(guī)則對(duì)象(rule object)。

let webpackConfig = {
    //...
    module: {
        rules: [{
            test: /.js$/,
            use: [{
                //這里寫 loader 的路徑
                loader: path.resolve(__dirname, "loaders/a-loader.js"), 
                options: {/* ... */}
            }]
        }]
    }
}
多個(gè) loader 的配置

增加 config.module.rules 數(shù)組中的規(guī)則對(duì)象以及 config.resolveLoader。

let webpackConfig = {
    //...
    module: {
        rules: [{
            test: /.js$/,
            use: [{
                //這里寫 loader 名即可
                loader: "a-loader", 
                options: {/* ... */}
            }, {
                loader: "b-loader", 
                options: {/* ... */}
            }]
        }]
    },
    resolveLoader: {
        // 告訴 webpack 該去那個(gè)目錄下找 loader 模塊
        modules: ["node_modules", path.resolve(__dirname, "loaders")]
    }
}
其他配置

也可以通過 npm link 連接到你的項(xiàng)目里,這個(gè)方式類似 node CLI 工具開發(fā),非 loader 模塊專用,本文就不多討論了。

2. 簡(jiǎn)單上手

配置完成后,當(dāng)你在 webpack 項(xiàng)目中引入模塊時(shí),匹配到 rule (例如上面的 /.js$/)就會(huì)啟用對(duì)應(yīng)的 loader (例如上面的 a-loader 和 b-loader)。這時(shí),假設(shè)我們是 a-loader 的開發(fā)者,a-loader 會(huì)導(dǎo)出一個(gè)函數(shù),這個(gè)函數(shù)接受的唯一參數(shù)是一個(gè)包含源文件內(nèi)容的字符串。我們暫且稱它為「source」。

接著我們?cè)诤瘮?shù)中處理 source 的轉(zhuǎn)化,最終返回處理好的值。當(dāng)然返回值的數(shù)量和返回方式依據(jù) a-loader 的需求來定。一般情況下可以通過 return 返回一個(gè)值,也就是轉(zhuǎn)化后的值。如果需要返回多個(gè)參數(shù),則須調(diào)用 this.callback(err, values...) 來返回。在異步 loader 中你可以通過拋錯(cuò)來處理異常情況。Webpack 建議我們返回 1 至 2 個(gè)參數(shù),第一個(gè)參數(shù)是轉(zhuǎn)化后的 source,可以是 string 或 buffer。第二個(gè)參數(shù)可選,是用來當(dāng)作 SourceMap 的對(duì)象。

3. 進(jìn)階使用

通常我們處理一類源文件的時(shí)候,單一的 loader是不夠用的(loader 的設(shè)計(jì)原則我們稍后講到)。一般我們會(huì)將多個(gè) loader 串聯(lián)使用,類似工廠流水線,一個(gè)位置的工人(或機(jī)器)只干一種類型的活。既然是串聯(lián),那肯定有順序的問題,webpack 規(guī)定 use 數(shù)組中 loader 的執(zhí)行順序是從最后一個(gè)到第一個(gè),它們符合下面這些規(guī)則:

順序最后的 loader 第一個(gè)被調(diào)用,它拿到的參數(shù)是 source 的內(nèi)容

順序第一的 loader 最后被調(diào)用, webpack 期望它返回 JS 代碼,source map 如前面所說是可選的返回值。

夾在中間的 loader 被鏈?zhǔn)秸{(diào)用,他們拿到上個(gè) loader 的返回值,為下一個(gè) loader 提供輸入。

我們舉個(gè)例子:

webpack.config.js

    {
        test: /.js/,
        use: [
            "bar-loader",
            "mid-loader",
            "foo-loader"
        ]
    }

在上面的配置中:

loader 的調(diào)用順序是 foo-loader -> mid-loader -> bar-loader。

foo-loader 拿到 source,處理后把 JS 代碼傳遞給 mid,mid 拿到 foo 處理過的 “source” ,再處理之后給 bar,bar 處理完后再交給 webpack。

bar-loader 最終把返回值和 source map 傳給 webpack。

用正確的姿勢(shì)開發(fā) Loader

了解了基本模式后,我們先不急著開發(fā)。所謂磨刀不誤砍柴工,我們先看看開發(fā)一個(gè) loader 需要注意些什么,這樣可以少走彎路,提高開發(fā)質(zhì)量。下面是 webpack 提供的幾點(diǎn)指南,它們按重要程度排序,注意其中有些點(diǎn)只適用特定情況。

1.單一職責(zé)

一個(gè) loader 只做一件事,這樣不僅可以讓 loader 的維護(hù)變得簡(jiǎn)單,還能讓 loader 以不同的串聯(lián)方式組合出符合場(chǎng)景需求的搭配。

2.鏈?zhǔn)浇M合

這一點(diǎn)是第一點(diǎn)的延伸。好好利用 loader 的鏈?zhǔn)浇M合的特型,可以收獲意想不到的效果。具體來說,寫一個(gè)能一次干 5 件事情的 loader ,不如細(xì)分成 5 個(gè)只能干一件事情的 loader,也許其中幾個(gè)能用在其他你暫時(shí)還沒想到的場(chǎng)景。下面我們來舉個(gè)例子。

假設(shè)現(xiàn)在我們要實(shí)現(xiàn)通過 loader 的配置和 query 參數(shù)來渲染模版的功能。我們?cè)?“apply-loader” 里面實(shí)現(xiàn)這個(gè)功能,它負(fù)責(zé)編譯源模版,最終輸出一個(gè)導(dǎo)出 HTML 字符串的模塊。根據(jù)鏈?zhǔn)浇M合的規(guī)則,我們可以結(jié)合另外兩個(gè)開源 loader:

jade-loader 把模版源文件轉(zhuǎn)化為導(dǎo)出一個(gè)函數(shù)的模塊。

apply-loader 把 loader options 傳給上面的函數(shù)并執(zhí)行,返回 HTML 文本。

html-loader 接收 HTMl 文本文件,轉(zhuǎn)化為可被引用的 JS 模塊。

事實(shí)上串聯(lián)組合中的 loader 并不一定要返回 JS 代碼。只要下游的 loader 能有效處理上游 loader 的輸出,那么上游的 loader 可以返回任意類型的模塊。
3.模塊化

保證 loader 是模塊化的。loader 生成模塊需要遵循和普通模塊一樣的設(shè)計(jì)原則。

4.無狀態(tài)

在多次模塊的轉(zhuǎn)化之間,我們不應(yīng)該在 loader 中保留狀態(tài)。每個(gè) loader 運(yùn)行時(shí)應(yīng)該確保與其他編譯好的模塊保持獨(dú)立,同樣也應(yīng)該與前幾個(gè) loader 對(duì)相同模塊的編譯結(jié)果保持獨(dú)立。

5.使用 Loader 實(shí)用工具

請(qǐng)好好利用 loader-utils 包,它提供了很多有用的工具,最常用的一個(gè)就是獲取傳入 loader 的 options。除了 loader-utils 之外包還有 schema-utils 包,我們可以用 schema-utils 提供的工具,獲取用于校驗(yàn) options 的 JSON Schema 常量,從而校驗(yàn) loader options。下面給出的例子簡(jiǎn)要地結(jié)合了上面提到的兩個(gè)工具包:

import { getOptions } from "loader-utils";
import { validateOptions } from "schema-utils";

const schema = {
  type: object,
  properties: {
    test: {
      type: string
    }
  }
}

export default function(source) {
    const options = getOptions(this);

    validateOptions(schema, options, "Example Loader");

    // 在這里寫轉(zhuǎn)換 source 的邏輯 ...
    return `export default ${ JSON.stringify(source) }`;
};
loader 的依賴

如果我們?cè)?loader 中用到了外部資源(也就是從文件系統(tǒng)中讀取的資源),我們必須聲明這些外部資源的信息。這些信息用于在監(jiān)控模式(watch mode)下驗(yàn)證可緩存的 loder 以及重新編譯。下面這個(gè)例子簡(jiǎn)要地說明了怎么使用 addDependency 方法來做到上面說的事情。
loader.js:

import path from "path";

export default function(source) {
    var callback = this.async();
    var headerPath = path.resolve("header.js");

    this.addDependency(headerPath);

    fs.readFile(headerPath, "utf-8", function(err, header) {
        if(err) return callback(err);
        //這里的 callback 相當(dāng)于異步版的 return
        callback(null, header + "
" + source);
    });
};
模塊依賴

不同的模塊會(huì)以不同的形式指定依賴。比如在 CSS 中我們使用 @importurl(...) 聲明來完成指定,而我們應(yīng)該讓模塊系統(tǒng)解析這些依賴。

如何讓模塊系統(tǒng)解析不同聲明方式的依賴呢?下面有兩種方法:

把不同的依賴聲明統(tǒng)一轉(zhuǎn)化為 require 聲明。

通過 this.resolve 函數(shù)來解析路徑。

對(duì)于第一種方式,有一個(gè)很好的例子就是 css-loader。它把 @import 聲明轉(zhuǎn)化為 require 樣式表文件,把 url(...) 聲明轉(zhuǎn)化為 require 被引用文件。

而對(duì)于第二種方式,則需要參考一下 less-loader。由于要追蹤 less 中的變量和 mixin,我們需要把所有的 .less 文件一次編譯完畢,所以不能把每個(gè) @import 轉(zhuǎn)為 require。因此,less-loader 用自定義路徑解析邏輯拓展了 less 編譯器。這種方式運(yùn)用了我們剛才提到的第二種方式 —— this.resolve 通過 webpack 來解析依賴。

如果某種語(yǔ)言只支持相對(duì)路徑(例如 url(file) 指向 ./file)。你可以用 ~ 將相對(duì)路徑指向某個(gè)已經(jīng)安裝好的目錄(例如 node_modules)下,因此,拿 url 舉例,它看起來會(huì)變成這樣:url(~some-library/image.jpg)。
代碼公用

避免在多個(gè) loader 里面初始化同樣的代碼,請(qǐng)把這些共用代碼提取到一個(gè)運(yùn)行時(shí)文件里,然后通過 require 把它引進(jìn)每個(gè) loader。

絕對(duì)路徑

不要在 loader 模塊里寫絕對(duì)路徑,因?yàn)楫?dāng)項(xiàng)目根路徑變了,這些路徑會(huì)干擾 webpack 計(jì)算 hash(把 module 的路徑轉(zhuǎn)化為 module 的引用 id)。loader-utils 里有一個(gè) stringifyRequest 方法,它可以把絕對(duì)路徑轉(zhuǎn)化為相對(duì)路徑。

同伴依賴

如果你開發(fā)的 loader 只是簡(jiǎn)單包裝另外一個(gè)包,那么你應(yīng)該在 package.json 中將這個(gè)包設(shè)為同伴依賴(peerDependency)。這可以讓應(yīng)用開發(fā)者知道該指定哪個(gè)具體的版本。
舉個(gè)例子,如下所示 sass-loadernode-sass 指定為同伴依賴:

"peerDependencies": {
  "node-sass": "^4.0.0"
}
Talk is cheep

以上我們已經(jīng)為砍柴磨好了刀,接下來,我們動(dòng)手開發(fā)一個(gè) loader。

如果我們要在項(xiàng)目開發(fā)中引用模版文件,那么壓縮 html 是十分常見的需求。分解以上需求,解析模版、壓縮模版其實(shí)可以拆分給兩給 loader 來做(單一職責(zé)),前者較為復(fù)雜,我們就引入開源包 html-loader,而后者,我們就拿來練手。首先,我們給它取個(gè)響亮的名字 —— html-minify-loader

接下來,按照之前介紹的步驟,首先,我們應(yīng)該配置 webpack.config.js ,讓 webpack 能識(shí)別我們的 loader。當(dāng)然,最最開始,我們要?jiǎng)?chuàng)建 loader 的 文件 —— src/loaders/html-minify-loader.js。

于是,我們?cè)谂渲梦募羞@樣處理:
webpack.config.js

module: {
    rules: [{
        test: /.html$/,
        use: ["html-loader", "html-minify-loader"] // 處理順序 html-minify-loader => html-loader => webpack
    }]
},
resolveLoader: {
    // 因?yàn)?html-loader 是開源 npm 包,所以這里要添加 "node_modules" 目錄
    modules: [path.join(__dirname, "./src/loaders"), "node_modules"]
}

接下來,我們提供示例 html 和 js 來測(cè)試 loader:

src/example.html




    
    
    
    Document


    

src/app.js

var html = require("./expamle.html");
console.log(html);

好了,現(xiàn)在我們著手處理 src/loaders/html-minify-loader.js。前面我們說過,loader 也是一個(gè) node 模塊,它導(dǎo)出一個(gè)函數(shù),該函數(shù)的參數(shù)是 require 的源模塊,處理 source 后把返回值交給下一個(gè) loader。所以它的 “模版” 應(yīng)該是這樣的:

module.exports = function (source) {
    // 處理 source ...
    return handledSource;
}

module.exports = function (source) {
    // 處理 source ...
    this.callback(null, handledSource)
    return handledSource;
}
注意:如果是處理順序排在最后一個(gè)的 loader,那么它的返回值將最終交給 webpack 的 require,換句話說,它一定是一段可執(zhí)行的 JS 腳本 (用字符串來存儲(chǔ)),更準(zhǔn)確來說,是一個(gè) node 模塊的 JS 腳本,我們來看下面的例子。
// 處理順序排在最后的 loader
module.exports = function (source) {
    // 這個(gè) loader 的功能是把源模塊轉(zhuǎn)化為字符串交給 require 的調(diào)用方
    return "module.exports = " + JSON.stringify(source);
}

整個(gè)過程相當(dāng)于這個(gè) loader 把源文件

這里是 source 模塊

轉(zhuǎn)化為

// example.js
module.exports = "這里是 source 模塊";

然后交給 require 調(diào)用方:

// applySomeModule.js
var source = require("example.js"); 

console.log(source); // 這里是 source 模塊

而我們本次串聯(lián)的兩個(gè) loader 中,解析 html 、轉(zhuǎn)化為 JS 執(zhí)行腳本的任務(wù)已經(jīng)交給 html-loader 了,我們來處理 html 壓縮問題。

作為普通 node 模塊的 loader 可以輕而易舉地引用第三方庫(kù)。我們使用 minimize 這個(gè)庫(kù)來完成核心的壓縮功能:

// src/loaders/html-minify-loader.js

var Minimize = require("minimize");

module.exports = function(source) {
    var minimize = new Minimize();
    return minimize.parse(source);
};

當(dāng)然, minimize 庫(kù)支持一系列的壓縮參數(shù),比如 comments 參數(shù)指定是否需要保留注釋。我們肯定不能在 loader 里寫死這些配置。那么 loader-utils 就該發(fā)揮作用了:

// src/loaders/html-minify-loader.js
var loaderUtils = require("loader-utils");
var Minimize = require("minimize");

module.exports = function(source) {
    var options = loaderUtils.getOptions(this) || {}; //這里拿到 webpack.config.js 的 loader 配置
    var minimize = new Minimize(options);
    return minimize.parse(source);
};

這樣,我們可以在 webpack.config.js 中設(shè)置壓縮后是否需要保留注釋:

    module: {
        rules: [{
            test: /.html$/,
            use: ["html-loader", {
                loader: "html-minify-loader",
                options: {
                    comments: false
                }
            }] 
        }]
    },
    resolveLoader: {
        // 因?yàn)?html-loader 是開源 npm 包,所以這里要添加 "node_modules" 目錄
        modules: [path.join(__dirname, "./src/loaders"), "node_modules"]
    }

當(dāng)然,你還可以把我們的 loader 寫成異步的方式,這樣不會(huì)阻塞其他編譯進(jìn)度:

var Minimize = require("minimize");
var loaderUtils = require("loader-utils");

module.exports = function(source) {
    var callback = this.async();
    if (this.cacheable) {
        this.cacheable();
    }
    var opts = loaderUtils.getOptions(this) || {};
    var minimize = new Minimize(opts);
    minimize.parse(source, callback);
};

你可以在這個(gè)倉(cāng)庫(kù)查看相關(guān)代碼,npm start 以后可以去 http://localhost:9000 打開控制臺(tái)查看 loader 處理后的內(nèi)容。

總結(jié)

到這里,對(duì)于「如何開發(fā)一個(gè) loader」,我相信你已經(jīng)有了自己的答案??偨Y(jié)一下,一個(gè) loader 在我們項(xiàng)目中 work 需要經(jīng)歷以下步驟:

創(chuàng)建 loader 的目錄及模塊文件

在 webpack 中配置 rule 及 loader 的解析路徑,并且要注意 loader 的順序,這樣在 require 指定類型文件時(shí),我們能讓處理流經(jīng)過指定 laoder。

遵循原則設(shè)計(jì)和開發(fā) loader。

最后,Talk is cheep,趕緊動(dòng)手?jǐn)]一個(gè) loader 耍耍吧~

參考
Writing a loader

推薦: 翻譯項(xiàng)目Master的自述: 1. 干貨|人人都是翻譯項(xiàng)目的Master 2. iKcamp出品微信小程序教學(xué)共5章16小節(jié)匯總(含視頻) 3. 開始免費(fèi)連載啦~每周2更共11堂iKcamp課|基于Koa2搭建Node.js實(shí)戰(zhàn)項(xiàng)目教學(xué)(含視頻)| 課程大綱介紹

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

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

相關(guān)文章

  • 把手你擼個(gè)vue2.0彈窗組件

    摘要:組件結(jié)構(gòu)同組件結(jié)構(gòu)通過方法獲取元素的大小及其相對(duì)于視口的位置,之后對(duì)提示信息進(jìn)行定位??梢杂脕磉M(jìn)行一些復(fù)雜帶校驗(yàn)的彈窗信息展示,也可以只用于簡(jiǎn)單信息的展示。可以通過屬性來顯示任意標(biāo)題,通過屬性來修改顯示區(qū)域的寬度。 手把手教你擼個(gè)vue2.0彈窗組件 在開始之前需要了解一下開發(fā)vue插件的前置知識(shí),推薦先看一下vue官網(wǎng)的插件介紹 預(yù)覽地址 http://haogewudi.me/k...

    mrli2016 評(píng)論0 收藏0
  • 把手你擼個(gè)vue2.0彈窗組件

    摘要:組件結(jié)構(gòu)同組件結(jié)構(gòu)通過方法獲取元素的大小及其相對(duì)于視口的位置,之后對(duì)提示信息進(jìn)行定位??梢杂脕磉M(jìn)行一些復(fù)雜帶校驗(yàn)的彈窗信息展示,也可以只用于簡(jiǎn)單信息的展示??梢酝ㄟ^屬性來顯示任意標(biāo)題,通過屬性來修改顯示區(qū)域的寬度。 手把手教你擼個(gè)vue2.0彈窗組件 在開始之前需要了解一下開發(fā)vue插件的前置知識(shí),推薦先看一下vue官網(wǎng)的插件介紹 預(yù)覽地址 http://haogewudi.me/k...

    taoszu 評(píng)論0 收藏0
  • 把手你擼一個(gè)泡妞神奇

    摘要:畫字首先我在畫布上畫了個(gè)點(diǎn),用這些點(diǎn)來組成我們要顯示的字,用不到的字就隱藏起來。星星閃爍效果這個(gè)效果實(shí)現(xiàn)很簡(jiǎn)單,就是讓星星不停的震動(dòng),具體就是讓點(diǎn)的目的地坐標(biāo)不停的進(jìn)行小范圍的偏移。 哈哈哈哈!!!當(dāng)我說在寫這邊文章的時(shí)候,妹子已經(jīng)追到了,哈哈哈哈哈?。?! 其實(shí)東西是一年前寫的,妹子早就追到手了,當(dāng)時(shí)就是用這個(gè)東西來表白的咯,二話不說,先看效果(點(diǎn)擊屏幕可顯示下一句) showImg(...

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

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

0條評(píng)論

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