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

資訊專欄INFORMATION COLUMN

JavaScript > Juicer.js源碼解讀

mrcode / 2168人閱讀

摘要:具體可配置的項可以參看其源代碼。那引擎對象是如何被構(gòu)造出來的呢看這句由此,我們進(jìn)入了的核心構(gòu)造函數(shù),。由于該構(gòu)造函數(shù)篇幅很長,我們先看下簡略版的結(jié)構(gòu),然后拆開來分析。此外,推薦使用注冊自定義函數(shù),而非使用。

Juicer.js源碼解讀
  

Version: 0.6.9-stable

Date: 8th of Aug, 2015

個人能力有限,如有分析不當(dāng)?shù)牡胤剑瑧┱堉刚?/p> 第一部分: 參數(shù)配置

方法與參數(shù)

參數(shù)配置方法是 juicer.set,該方法接受兩個參數(shù)或一個參數(shù):

當(dāng)傳入兩個參數(shù)時,如 juicer.set("cache",false) ,即是設(shè)置 cachefalse

當(dāng)傳入一個參數(shù)時,該參數(shù)應(yīng)為一個對象,如 juicer.set({cache:false}),系統(tǒng)將遍歷這個對象的屬性來設(shè)值

可以配置的內(nèi)容

我們可以配置一些參數(shù)選項,包括 cachestriperrorhandlingdetection;其默認(rèn)值都是true;我們還可以修改模板的語法邊界符,如 tag::operationOpen 等。具體可配置的項可以參看其源代碼。

工作原理

juicer.options = {
    // 是否緩存模板編譯結(jié)果
    cache: true,
    // 是否清除空白
    strip: true,
    // 是否處理錯誤
    errorhandling: true,
    // 是否檢測變量是否定義
    detection: true,
    // 自定義函數(shù)庫
    _method: __creator({
        __escapehtml: __escapehtml,
        __throw: __throw,
        __juicer: juicer
    }, {})
};

選項解析如下:

cache 是否緩存編譯結(jié)果(引擎對象)。緩存的結(jié)果存于 juicer.__cache

strip 是否清除模板中的空白,包括換行、回車等

errorhandling 是否處理錯誤

detection 開啟后,如果變量未定義,將用空白字符串代替變量位置,否則照常輸出,所以如果關(guān)閉此項,有可能造成輸出 undefined

_method 存儲的是用戶注冊的自定義函數(shù),系統(tǒng)內(nèi)部創(chuàng)建的自定義函數(shù)或?qū)ο笥?__escapehtml 處理HTML轉(zhuǎn)義、__throw 拋出錯誤、__juicer引用 juicer__creator 方法本文最末講解

在 Node.js 環(huán)境中,cache 默認(rèn)值是 false,請看下面代碼

if(typeof(global) !== "undefined" && typeof(window) === "undefined") {
    juicer.set("cache", false);
}

這段代碼在結(jié)尾處可以找到。

此外,還有一個屬性是 juicer.options.loose,默認(rèn)值為 undefined(沒有設(shè)置),當(dāng)其值不為 false(此亦系統(tǒng)默認(rèn))時,將對 {@each}{@if}{@else if}${}{@include}等中的變量名和自定義函數(shù)名進(jìn)行校驗,給其中使用到的變量、函數(shù)定義并添加到模板的開頭,以保證能夠順利使用。

所以,如果我們更改此設(shè)置,可能造成系統(tǒng)錯誤

// 這些操作應(yīng)當(dāng)避免,否則會造成系統(tǒng)錯誤
// 將`juicer.options.loose`設(shè)為`false`
// juicer.set("loose",false);

下面來看 juicer.set 方法的源代碼

juicer.set = function(conf, value) {
    // 引用`juicer`
    var that = this;
    // 反斜杠轉(zhuǎn)義
    var escapePattern = function(v) {
        // 匹配 $ ( [ ] + ^ { } ? * | . *
        // 這些符號都需要被轉(zhuǎn)義
        return v.replace(/[$()[]+^{}?*|.]/igm, function($) {
            return "" + $;
        });
    };
    // 設(shè)置函數(shù)
    var set = function(conf, value) {
        // 語法邊界符匹配
        var tag = conf.match(/^tag::(.*)$/i);
        if(tag) {
            // 由于系統(tǒng)這里沒有判斷語法邊界符是否是系統(tǒng)所用的
            // 所以一定要拼寫正確
            that.tags[tag[1]] = escapePattern(value);
            // 重新生成匹配正則
            // `juicer.tagInit`解析見下面
            that.tagInit();
            return;
        }
        // 其他配置項
        that.options[conf] = value;
    };
    // 如果傳入兩個參數(shù),`conf`表示要修改的屬性,`value`是要修改的值
    if(arguments.length === 2) {
        set(conf, value);
        return;
    }
    // 如果傳入一個參數(shù),且是對象
    if(conf === Object(conf)) {
        // 遍歷該對象的自有屬性設(shè)置
        for(var i in conf) {
            if(conf.hasOwnProperty(i)) {
                set(i, conf[i]);
            }
        }
    }
};

注釋里面已經(jīng)提示,通過 juicer.set 方法可以覆蓋任何屬性。

如果修改了語法邊界符設(shè)定,將會重新生成匹配正則,下面看匹配正則的源代碼

juicer.tags = {
    // 操作開
    operationOpen: "{@",
    // 操作閉
    operationClose: "}",
    // 變量開
    interpolateOpen: "${",
    // 變量閉標(biāo)簽
    interpolateClose: "}",
    // 禁止對其內(nèi)容轉(zhuǎn)義的變量開
    noneencodeOpen: "$${",
    // 禁止對其內(nèi)容轉(zhuǎn)義的變量閉
    noneencodeClose: "}",
    // 注釋開
    commentOpen: "{#",
    // 注釋閉
    commentClose: "}"
};


juicer.tagInit = function() {
    /**
        * 匹配each循環(huán)開始,以下都是OK的
        * `each VAR as VALUE`, 如 {@each names as name}
        * `each VAR as VALUE ,INDEX`,如 {@each names as name,key}
        * `each VAR as`,如 {@each names as}
        * 需要說明后兩種情況:
        * `,key` 是一起被捕獲的,所以在編譯模板的時候,系統(tǒng)會用`substr`去掉`,`
        * as 后沒有指定別名的話,默認(rèn)以`value`為別名,所以
        * {@each names as} 等價于 {@each names as value}
    */
    var forstart = juicer.tags.operationOpen + "eachs*([^}]*?)s*ass*(w*?)s*(,s*w*?)?" + juicer.tags.operationClose;
    // each循環(huán)結(jié)束
    var forend = juicer.tags.operationOpen + "/each" + juicer.tags.operationClose;
    // if條件開始
    var ifstart = juicer.tags.operationOpen + "ifs*([^}]*?)" + juicer.tags.operationClose;
    // if條件結(jié)束
    var ifend = juicer.tags.operationOpen + "/if" + juicer.tags.operationClose;
    // else條件開始
    var elsestart = juicer.tags.operationOpen + "else" + juicer.tags.operationClose;
    // eles if 條件開始
    var elseifstart = juicer.tags.operationOpen + "else ifs*([^}]*?)" + juicer.tags.operationClose;
    // 匹配變量
    var interpolate = juicer.tags.interpolateOpen + "([sS]+?)" + juicer.tags.interpolateClose;
    // 匹配不對其內(nèi)容轉(zhuǎn)義的變量
    var noneencode = juicer.tags.noneencodeOpen + "([sS]+?)" + juicer.tags.noneencodeClose;
    // 匹配模板內(nèi)容注釋
    var inlinecomment = juicer.tags.commentOpen + "[^}]*?" + juicer.tags.commentClose;
    // for輔助循環(huán)
    var rangestart = juicer.tags.operationOpen + "eachs*(w*?)s*ins*range(([^}]+?)s*,s*([^}]+?))" + juicer.tags.operationClose;
    // 引入子模板
    var include = juicer.tags.operationOpen + "includes*([^}]*?)s*,s*([^}]*?)" + juicer.tags.operationClose;
    // 內(nèi)聯(lián)輔助函數(shù)開始
    var helperRegisterStart = juicer.tags.operationOpen + "helpers*([^}]*?)s*" + juicer.tags.operationClose;
    // 輔助函數(shù)代碼塊內(nèi)語句
    var helperRegisterBody = "([sS]*?)";
    // 輔助函數(shù)結(jié)束
    var helperRegisterEnd = juicer.tags.operationOpen + "/helper" + juicer.tags.operationClose;

    juicer.settings.forstart = new RegExp(forstart, "igm");
    juicer.settings.forend = new RegExp(forend, "igm");
    juicer.settings.ifstart = new RegExp(ifstart, "igm");
    juicer.settings.ifend = new RegExp(ifend, "igm");
    juicer.settings.elsestart = new RegExp(elsestart, "igm");
    juicer.settings.elseifstart = new RegExp(elseifstart, "igm");
    juicer.settings.interpolate = new RegExp(interpolate, "igm");
    juicer.settings.noneencode = new RegExp(noneencode, "igm");
    juicer.settings.inlinecomment = new RegExp(inlinecomment, "igm");
    juicer.settings.rangestart = new RegExp(rangestart, "igm");
    juicer.settings.include = new RegExp(include, "igm");
    juicer.settings.helperRegister = new RegExp(helperRegisterStart + helperRegisterBody + helperRegisterEnd, "igm");
};

具體語法邊界符的用法請參照官方文檔:http://www.juicer.name/docs/docs_zh_cn.html

一般地,不建議對默認(rèn)標(biāo)簽進(jìn)行修改。當(dāng)然,如果默認(rèn)語法邊界符規(guī)則與正在使用的其他語言語法規(guī)則沖突,修改 juicer 的語法邊界符就很有用了。

需要注意,{@each names as} 等價于 {@each names as value},盡管我們?nèi)砸3终_書寫的規(guī)則,避免利用系統(tǒng)自動糾錯機(jī)制

// 如下模板的寫法是不推薦的
/**
    {@each list as}
    ${value.title}
    {@/each}
*/
第二部分: 注冊自定義函數(shù)

上面說,juicer.options._method 存儲了用戶的自定義函數(shù),那么我們?nèi)绾巫砸约叭绾问褂米远x函數(shù)呢?

注冊/銷自定義函數(shù)

juicer.register 方法用來注冊自定義函數(shù)

juicer.unregister 方法用來注銷自定義函數(shù)

// `fname`為函數(shù)名,`fn`為函數(shù)
juicer.register = function(fname, fn) {
    // 自定義函數(shù)均存儲于 `juicer.options._method`
    // 如果已經(jīng)注冊了該函數(shù),不允許覆蓋
    if(_method.hasOwnProperty(fname)) {
        return false;
    }
    // 將新函數(shù)注冊進(jìn)入
    return _method[fname] = fn;
};

juicer.unregister = function(fname) {
    var _method = this.options._method;
    // 沒有檢測是否注銷的是系統(tǒng)自定義函數(shù)
    // 用戶不要注銷錯了
    if(_method.hasOwnProperty(fname)) {
        return delete _method[fname];
    }
};

自定義函數(shù)都是存儲在juicer.options._method中的,因此以下方法可以跳過函數(shù)是否注冊的檢驗強(qiáng)行更改自定義函數(shù),這些操作很危險:

// 這些操作應(yīng)當(dāng)避免,否則會造成系統(tǒng)錯誤
// 改變`juicer.options._method`
// juicer.set("_method",{});
// juicer.unregister("__juicer");
// juicer.unregister("__throw");
// juicer.unregister("__escapehtml");
第三部分: 編譯模板

先看下 juicer 的定義部分。

var juicer = function() {
    // 將傳遞參數(shù)(偽數(shù)組)切成數(shù)組后返回給`args`,以便調(diào)用數(shù)組的方法
    var args = [].slice.call(arguments);
    // 將`juicer.options`推入`args`,表示渲染使用當(dāng)前設(shè)置
    args.push(juicer.options);
    /**
        * 下面將獲取模板內(nèi)容
        * 模板內(nèi)容取決于我們傳遞給`juicer`函數(shù)的首參數(shù)
        * 可以是模板節(jié)點的id屬性值
        * 也可以是模板內(nèi)容本
    */
    // 首先會試著匹配,匹配成功就先當(dāng)作id處理
    // 左右兩側(cè)的空白會被忽略
    // 如果是`#`開頭,后面跟著字母、數(shù)字、下劃線、短橫線、冒號、點號都可匹配
    // 所以這么寫都是可以的:`id=":-."`
    if(args[0].match(/^s*#([w:-.]+)s*$/igm)) {
        // 如果傳入的是模板節(jié)點的id,會通過`replace`方法準(zhǔn)確匹配并獲取模板內(nèi)容
        // 回調(diào)函數(shù)的首參`$`是匹配的全部內(nèi)容(首參),$id是匹配的節(jié)點id
        args[0].replace(/^s*#([w:-.]+)s*$/igm, function($, $id) {
            // node.js環(huán)境沒有`document`,所以會先判斷`document`
            var _document = document;
            // 找尋節(jié)點
            var elem = _document && _document.getElementById($id);
            // 如果該節(jié)點存在,節(jié)點的`value`或`innerHTML`就是模板內(nèi)容
            // 即是說,存放模板的內(nèi)容節(jié)點只要有`value`或`innerHTML`屬性即可
            //                 
閱讀需要支付1元查看
<