1. 程式人生 > >requirejs的外掛介紹與製作

requirejs的外掛介紹與製作

本文由作者鄭海波授權網易雲社群釋出。


前言

我這裡就不介紹requirejs了, 簡而言之: requirejs是支援AMD規範的模組載入器, 事實上它也是AMD的最直接推動者。

現在可供挑選的開源模組解決方案很多,比如component、cjs+browserify、umd等等,但是無疑類似requirejs這類載入系統是現在最成熟和可靠的解決方案,所以regularjs第一步就是提供對requirejs的外掛支援。


requirejs的外掛體系

requirejs的原始碼內部預留了hook,使得你可以建立外掛來增強這個模組系統,並且這個外掛可以做到影響到你的OPTIMIZER階段,一些資源可以被處理為標準的AMD模組。

外掛普遍被用來

  1. 預編譯

  2. 載入非js文字

  3. lint 或 test 後置或前置的操作 等等

example 比如它本身是不支援載入文字資訊的,但是你可以通過text!外掛來載入。

require(['text!foo.html', 'jquery'], function(foo, $){
    $('#anchor').html(foo);
})

需要注意的是由於文字無法用script標籤進行載入,所以text內部是通過XHR來載入的,即它會受到同源策略的影響。

優化OPTIMIZER

由於requirejs同時提供工具(npm:requirejs)可以靜態打包優化AMD,剛才的那個text!foo.html

會同時被text外掛轉換為類似下面的AMD模組結構

define('text!foo.html',[],function () { 
    return '<h2>早上好\n</h2>';
});

requirejs的外掛其實是一個實現的特定介面的標準AMD模組,它在定義時與其它業務模組並無區別。

例如官方text外掛的原始檔

define(['module'], function (module) {    'use strict';    var text = {
        load: function(){}
        ....
    }    return text
})

其中load等介面是外掛必須實現的,

對於各個介面描述我就不細究了,大家可以參考官網

順便列舉一些有用的requirejs外掛

  1. text外掛(最常用外掛) 如果你的文字內容無需在打包優化階段做處理,幾乎都可以使用這個外掛來完成載入

  2. json外掛 比樓上多做了一步JSON.parse.

  3. amd-loader(好東西): 注意不要requirejs本身弄混了,因為requirejs本身不是基於xhr的,這個外掛主要是提供完善的xhr支援來載入文字內容。一句化即它是[外掛的loader外掛],作者事後才發現有這麼一個外掛...繞了不少彎路。具體例子可以檢視es6

  4. handlebars 用來載入handlebar的外掛

其實由於amd等模組系統佔據了開發中的模組入口這一環,其實在開發中可以有無限的可能性,這也是常規大公司都會自造一個輪子來最優配置的緣由之一,事實上requirejs目前的外掛系統已經有足夠的靈活性來定製自己的策略。

實現requirejs-regular的過程

背景

首先我們先理清我們的需求, 與常規的的模板預編譯類似,我們的外掛主要為了實現兩個功能。

  1. 在開發階段,我們希望能載入js檔案一樣,載入我們的模板檔案,這帶來的幾個好處

    • 這使得我們不必將模板零散的填充到頁面的script 或 textarea標籤中

    • 依賴系統唯一化, 模板依賴整合進了模組依賴中

  2. 在優化階段(即requirejs提供的OPTIMIZER的上線打包功能),我們的模板字串可以被預處理為序列化的AST物件,這樣就不會發生瀏覽器端的解析,效率更高。

實現

一個外掛模組會同時跑在瀏覽器端(開發環境)和node端(為線上或測試環境的打包優化工具),所以你的外掛模組必須可以同時跑在瀏覽器端和node端,這個幾乎是整個開發環境最麻煩的一部分

  1. regular.js的單檔案雖然是umd模組可以支援amd環境,但是由於依賴的dom。所以首先要將parser部分(不依賴dom)打成一個AMD模組,由於regularjs本身就是基於commonjs的模組構建,將其中一部分打成AMD模組是分分鐘的事情,這裡我們使用webpack來打包成regular-parser.js,簡單起見我們隨regularjs模組一同釋出到bower上

  2. 我們還要解決模板的載入問題,外掛內部的載入問題也要手動解決,即你至少要實現loader介面和get介面。這裡我們完全可以偷個懶,直接使用!text外掛

即外掛會依賴這兩個模組

define(['text', 'regular-parser'], function(text, parser){    //blalalalala...
    return{
        load: load,
        write: write
    }
})

然後我們只需要實現兩個介面:

  • load

var buildMap = {};function load(name, req, onLoad, config){
    text.load(name, req, function(data,r){
        onLoad(
          (buildMap[name] = parser.parse(data, false))
        );
    }, config);
}

這裡直接使用了text外掛的純文字載入,需要注意的是這個onLoad介面,傳入引數相當於模組的內容,我們這裡預parse了這段文字內容。即你通過rgl!template.html最終會獲得解析後的AST資料。

其實對於regularjs來講在瀏覽器端有無進行模組系統層面的預解析並無意義,關鍵是在打包優化階段。這裡的buildMap主要是為了儲存這段內容用於打包使用。

write 實現write介面主要是為了在打包優化階段改寫相關模組

var tpl = function(str, data){    return str.replace(/\{\{(\w+)\}\}/g, function(all, name){        return data[name] || ""
    })
}var template ='define("{{pn}}!{{mn}}",function(){ return {{ast}} });\n';function write(pn, mn, writeModule){   if(buildMap[mn]){
       writeModule(
           tpl(template,{
               pn: pn,
               mn: mn,
               ast: parser.parse(buildMap[mn])
           })
       )
   }
}

此時這個外掛必須依賴於兩個模組,即必須同時保證textregular-parser模組同時存在,類似的方案可以檢視hogan,它必須保證環境中有hogantext才可以執行. 熟悉requirejs打包過程的同學也知道,除了loader端的配置,我們在build的打包檔案也需要一併將這些依賴模組剔除,因為上線時是不需要這些外掛的。所以這將大大增加配置成本,其實解決方案也很簡單,就是使用[webpack]再將其打包成一個standlone的AMD模組即可,具體可以參考這裡的gulpfile

大功告成

使用就非常簡單了,和你使用requirejs-text差不多,

1.首先下載rgl.js,最簡單的就是bower安裝

bower install regularjs-regular --save

save引數是安裝後並寫入到bower.json中,這個和npm一致

2.配置

require.config({   paths : {       "rgl": '../../bower_components/regularjs-regular/rgl',
       // 同時載入我們的regularjs來使用這些模板       "regularjs": '../../bower_components/regularjs/dist/regular'
   }
});

3.使用

require(['rgl!./foo.html', 'regularjs'], function( tpl, Regular){    var Foo = Regular.extend({
      template: tpl
    })    new Foo({}).$inject("#app")

});

4.打包

模板檔案<h2>{{message}}</h2>經過外掛處理後會打包成

define("rgl!foo.html",function(){return [{"type":"element","tag":"h2","attrs":[],"children":[{"type":"expression","body":"_c_._sg_('message', _d_['message'])","constant":false,"setbody":"_c_._ss_('message',_p_,_d_, '=')"}]}] });

即上線後就不會有parse了,比如PO主目前正在開發的專案在初期就有幾十個模板檔案,build成單檔案後的執行時開銷還是應該儘量避免.

tip:build.js記得通過stubModules配置專案刪除掉這個外掛模組,具體看demo的build.js

對於NEJ的使用者

NEJ的新模組系統支援上述類似的regular模板載入了

網易杭州的同事,事實上你已經可以在NEJ的新模組系統中(完全相容老版本)通過regular!path/to/template.html的方式來載入你的regular模板了,打包之後模板將會被預解析,同時新版NEJ也支援text!載入純文字內容, 詳詢@飛鍋。新版本的載入系統,支援類似AMD的注入寫法,並且相容老版本的模組寫法,親測好用哈。


免費領取驗證碼、內容安全、簡訊傳送、直播點播體驗包及雲伺服器等套餐

更多網易技術、產品、運營經驗分享請訪問網易雲社群

相關文章:
【推薦】 微服務監控探索
【推薦】 中秋福利|10本技術圖書(程式語言、資料分析等)免費送
【推薦】 網易雲安全兩篇論文入選計算機視覺頂級會議ICCV