日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
自己動(dòng)手:實(shí)現(xiàn)Dustjs中間件

Dustjs是我個(gè)人比較喜歡的一個(gè)JS模版引擎,原因有兩個(gè),一是,同時(shí)支持客戶端和服務(wù)端渲染,模版編譯成JS后使用,性能好;二是,有大公司的支持,Linkedin有專門(mén)的Dustjs版本(本文所說(shuō)的都是該版本),而且經(jīng)過(guò)線上考驗(yàn)。

創(chuàng)新互聯(lián)專注于萬(wàn)州網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供萬(wàn)州營(yíng)銷型網(wǎng)站建設(shè),萬(wàn)州網(wǎng)站制作、萬(wàn)州網(wǎng)頁(yè)設(shè)計(jì)、萬(wàn)州網(wǎng)站官網(wǎng)定制、小程序制作服務(wù),打造萬(wàn)州網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供萬(wàn)州網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。

關(guān)于Dustjs本文不再贅述(可參看文檔),直接進(jìn)入正題。

1. 為什么要寫(xiě)一個(gè)中間件

Dustjs 官方支持作為Express的View Engine使用,但個(gè)人傾向用于客戶端渲染,能減少服務(wù)端的性能損耗,充分利用客戶端的機(jī)器性能。目前Dustjs沒(méi)有類似于less- middleware的插件,能夠在按需的對(duì)模版進(jìn)行編譯,供客戶端引用,因此才有了這個(gè)Dustjs中間件。

2. Show Me The Code

2.1. 中間件

中間件代碼很簡(jiǎn)單,只有幾十行,無(wú)非是攔截HTTP請(qǐng)求,如發(fā)現(xiàn)是獲取模版,則按需的進(jìn)行編譯。

 
 
  1. // 依賴模塊的引入  
  2. var url = require('url'),  
  3.   fs = require('fs'),  
  4.   extend = require('node.extend'),  
  5.   dust = require('dustjs-linkedin'),  
  6.   beautify = require('js-beautify').js_beautify,  
  7.   iconv = require('iconv-lite'),  
  8.   path = require('path');  
  9.  
  10. // 遵循模塊定義,把模塊暴露給使用方  
  11. module.exports = function(source, options) {  
  12.  
  13.   // 使用node.extend模塊來(lái)提供默認(rèn)值  
  14.   options = extend(true, {  
  15.     format: false, // 是否格式化代碼,便于閱讀  
  16.     encoding: 'utf-8' // 代碼的編碼格式,支持中文  
  17.   }, options || {});  
  18.  
  19.   // source參數(shù)用于指定模版代碼的存放路徑,編譯后的JS代碼和模版源碼放在一起  
  20.   if (!source) {  
  21.     throw new Error('dustjs-middleware requires `source` directory');  
  22.   }  
  23.     
  24.   return function(req, res, next) {  
  25.     if ('GET' != req.method.toUpperCase() && 'HEAD' != req.method.toUpperCase()) {  
  26.       // 只處理Get和Head請(qǐng)求  
  27.       return next();  
  28.     }  
  29.       
  30.     var pathname = url.parse(req.url).pathname;  
  31.     if (!/^\/dust\/[\S]+\.js$/.test(pathname)) {  
  32.       // 不是對(duì)JS文件的請(qǐng)求這里不處理  
  33.       return next();  
  34.     }  
  35.       
  36.     var jsPath = source + pathname;  
  37.     var dustPath = jsPath.replace(/\.js$/, '.dust');  
  38.       
  39.     var error = function(err) {  
  40.       return next('ENOENT' == err.code ? null : err);  
  41.     };  
  42.       
  43.     // 編譯模版的函數(shù)  
  44.     var compile = function() {  
  45.       fs.readFile(dustPath, function(err, buf){  
  46.         if (err) {  
  47.           return error(err);  
  48.         }  
  49.           
  50.         // 用指定的編碼解析出模版源碼  
  51.         var data = iconv.decode(buf, options.encoding);  
  52.  
  53.         // 編譯模版,以文件名作為模版名  
  54.         var name = path.basename(dustPath, '.dust');  
  55.         var template = dust.compile(data, name);  
  56.  
  57.         if (options.format) {  
  58.           // 有需要?jiǎng)t進(jìn)行代碼格式化,基于js-beautify  
  59.           template = beautify(template, { indent_size: 2 });  
  60.         }  
  61.           
  62.         // 以指定的編碼寫(xiě)入編譯后的JS代碼  
  63.         buf = iconv.encode(template, options.encoding);  
  64.         fs.writeFile(jsPath, buf, next);  
  65.       });  
  66.     };  
  67.       
  68.     fs.stat(dustPath, function(dustErr, dustStats) {  
  69.       // 判斷模版代碼是否存在,不存在則不處理請(qǐng)求  
  70.       if (dustErr) {  
  71.         if ('ENOENT' == dustErr.code) {  
  72.           return next();  
  73.         } else {  
  74.           return next(dustErr);  
  75.         }  
  76.       }  
  77.         
  78.       if (dustStats.isDirectory()) {  
  79.         // 模版代碼是個(gè)文件,也不處理  
  80.         return next();  
  81.       }  
  82.         
  83.       fs.stat(jsPath, function(jsErr, jsStats) {  
  84.         if (jsErr) {  
  85.           if ('ENOENT' == jsErr.code) {  
  86.             // JS文件不存在,直接編譯  
  87.             return compile();  
  88.           } else {  
  89.             return next(jsErr);  
  90.           }  
  91.         } else if (dustStats.mtime > jsStats.ctime) {  
  92.           // 模版有變動(dòng),重新編譯  
  93.           return compile();  
  94.         }  
  95.       });  
  96.     });  
  97.   };  
  98. };  

需要注意的是中間件以文件名作為模版的名字,使用模版時(shí),需要指定該模版名,示例如下。

 
 
 
  •  
  •  
  •  
  •  
  •  
  •  
  • 這里隱含的一個(gè)約束是同一個(gè)頁(yè)面不能引入同名的模版,這會(huì)導(dǎo)致沖突,有必要時(shí)可以在模版文件命名時(shí)加上Namespace做區(qū)分。

    2.2. 編碼問(wèn)題

    Dustjs的編碼問(wèn)題相對(duì)簡(jiǎn)單,先來(lái)看一個(gè)編譯后的Dust模版。

     
     
    1. (function() {  
    2.   dust.register("hello", body_0);  
    3.  
    4.   function body_0(chk, ctx) {  
    5.     return chk.write("Hello world!");  
    6.   }  
    7.   return body_0;  
    8. })();  

    所有的Dust模版在加載時(shí)都會(huì)注冊(cè)到dust 全局對(duì)象中,模版間的互相引用都是通過(guò)該全局對(duì)象完成,不像Less那樣需要把組件的代碼合并到一起。因此解決Dustjs的編碼問(wèn)題只要保證單個(gè)文件的編碼正確即可(詳見(jiàn)代碼)。

    2.3. Node模塊定義

    除了代碼,還需要補(bǔ)充Node模塊的定義,才能被正常的依賴和使用。

     
     
    1. {  
    2.   // 作者信息  
    3.   "author": {  
    4.     "name": "Joshua Zhan",  
    5.     "email": "daonan.zhan@gmail.com",  
    6.     "url": "http://home4j.duapp.com/" 
    7.   },  
    8.   // 模塊信息  
    9.   "name": "dustjs-middleware",  
    10.   "description": "Dustjs middleware for express.",  
    11.   "version": "0.0.1",  
    12.   "repository": {  
    13.     "type": "git",  
    14.     "url": "http://git.oschina.net/joshuazhan/dustjs-middleware.git" 
    15.   },  
    16.   // 模塊代碼入口  
    17.   "main": "index.js",  
    18.   // 依賴  
    19.   "dependencies": {  
    20.     "dustjs-linkedin": "~2.3.4",  
    21.     "node.extend": "~1.0.8",  
    22.     "iconv-lite": "~0.2.11",  
    23.     "js-beautify": "~1.5.1" 
    24.   }  
    25.   ...  
    26. }  

    其中最重要的是指定模塊的入口,否則模塊將無(wú)法被加載。

    同時(shí)因?yàn)闆](méi)有加入npm倉(cāng)庫(kù),現(xiàn)階段還無(wú)法直接使用,需要通過(guò)git來(lái)引入,示例"dustjs-middleware": "git+http://git.oschina.net/joshuazhan/dustjs-middleware.git" 。

    3. 一些感想

    3.1. Callback

    得益于事件驅(qū)動(dòng)和非阻塞的IO接口,Nodejs有著很好的性能,同時(shí)也帶來(lái)了編碼方式的變更。隨處可見(jiàn)的匿名函數(shù)和回調(diào)函數(shù)看起來(lái)讓人不太舒服,慶幸的是有一些有效的方法能在很大程度上緩解這個(gè)問(wèn)題,推薦一篇文章給大家(http://callbackhell.com/),該文章對(duì)此做了很好的整理總結(jié),希望能有所幫助。

    3.2. Express

    和Java Web的Filter類似,Express中間件也是鏈?zhǔn)降奶幚碚?qǐng)求,一個(gè)典型的中間件如下:

     
     
    1. function(request, response, next) {  
    2.   ...  
    3.   return next();  
    4. }  

    銜接各個(gè)中間件的則是next() 回調(diào)函數(shù),如果中間件沒(méi)有把內(nèi)容輸出到response 中,則必通過(guò)回調(diào)把請(qǐng)求交給下一個(gè)中間件處理。一般而言回調(diào)函數(shù)只能調(diào)用一次,多次調(diào)用可能產(chǎn)生異常;不調(diào)用則請(qǐng)求得不到響應(yīng),占用寶貴的鏈接資源和內(nèi)存空間。

    麻煩之處在于,Node中充斥著各種回調(diào)和匿名函數(shù),使得next() 非常容易被遺忘或是錯(cuò)誤的調(diào)用。這個(gè)目前貌似沒(méi)有很好的解決辦法,只能靠開(kāi)發(fā)的經(jīng)驗(yàn)和測(cè)試,一個(gè)好的習(xí)慣是盡可能的在調(diào)用回調(diào)后就立即返回return next(); ,這個(gè)可以有效的避免多次調(diào)用的問(wèn)題。

    本文來(lái)自:http://home4j.duapp.com/index.php/2014/06/01/diy-writing-a-dust-middleware.html


    文章標(biāo)題:自己動(dòng)手:實(shí)現(xiàn)Dustjs中間件
    文章起源:http://www.dlmjj.cn/article/dpijehj.html