requirejs的插件介紹與製做

本文由做者鄭海波受權網易雲社區發佈。html


前言

我這裏就不介紹requirejs了, 簡而言之: requirejs是支持AMD規範的模塊加載器, 事實上它也是AMD的最直接推進者。node

如今可供挑選的開源模塊解決方案不少,好比component、cjs+browserify、umd等等,可是無疑相似requirejs這類加載系統是如今最成熟和可靠的解決方案,因此regularjs第一步就是提供對requirejs的插件支持。jquery

requirejs的插件體系

requirejs的源碼內部預留了hook,使得你能夠建立插件來加強這個模塊系統,而且這個插件能夠作到影響到你的OPTIMIZER階段,一些資源能夠被處理爲標準的AMD模塊。webpack

插件廣泛被用來git

  1. 預編譯es6

  2. 加載非js文本github

  3. lint 或 test 後置或前置的操做 等等web

example 好比它自己是不支持加載文本信息的,可是你能夠經過text!插件來加載。express

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

須要注意的是因爲文本沒法用script標籤進行加載,因此text內部是經過XHR來載入的,即它會受到同源策略的影響。npm

優化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的注入寫法,而且兼容老版本的模塊寫法,親測好用哈。


免費領取驗證碼、內容安全、短信發送、直播點播體驗包及雲服務器等套餐

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

相關文章:
【推薦】 基於 Algebird 談一談代數數據類型在數據聚合中的應用

相關文章
相關標籤/搜索