成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

自己動手:實現Dustjs中間件

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

Dustjs是我個人比較喜歡的一個JS模版引擎,原因有兩個,一是,同時支持客戶端和服務端渲染,模版編譯成JS后使用,性能好;二是,有大公司的支持,Linkedin有專門的Dustjs版本(本文所說的都是該版本),而且經過線上考驗。

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

 

1. 為什么要寫一個中間件

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

 

2. Show Me The Code

 

2.1. 中間件

中間件代碼很簡單,只有幾十行,無非是攔截HTTP請求,如發現是獲取模版,則按需的進行編譯。

  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模塊來提供默認值  
  14.   options = extend(true, {  
  15.     format: false// 是否格式化代碼,便于閱讀  
  16.     encoding: 'utf-8' // 代碼的編碼格式,支持中文  
  17.   }, options || {});  
  18.  
  19.   // source參數用于指定模版代碼的存放路徑,編譯后的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請求  
  27.       return next();  
  28.     }  
  29.       
  30.     var pathname = url.parse(req.url).pathname;  
  31.     if (!/^\/dust\/[\S]+\.js$/.test(pathname)) {  
  32.       // 不是對JS文件的請求這里不處理  
  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.     // 編譯模版的函數  
  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.           // 有需要則進行代碼格式化,基于js-beautify  
  59.           template = beautify(template, { indent_size: 2 });  
  60.         }  
  61.           
  62.         // 以指定的編碼寫入編譯后的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.       // 判斷模版代碼是否存在,不存在則不處理請求  
  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.         // 模版代碼是個文件,也不處理  
  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.           // 模版有變動,重新編譯  
  93.           return compile();  
  94.         }  
  95.       });  
  96.     });  
  97.   };  
  98. };  

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

  1. <div id="demo"></div>  
  2. <script src="https://home4j.duapp.com/share/jquery/jquery-2.min.js"></script>  
  3. <!-- 引入dust -->  
  4. <script src="https://home4j.duapp.com/share/linkedin-dustjs/dist/dust-core.min.js"></script>  
  5. <!-- 引入編譯后的模版 -->  
  6. <script src="context.js"></script>  
  7. <script>  
  8.   $(function() {  
  9.     // 準備數據  
  10.     var data = {  
  11.       ...  
  12.     };  
  13.     // 調用模版,模版名為文件名  
  14.     dust.render("context", data, function(err, out) {  
  15.       $('#demo').replaceWith(out);  
  16.     });  
  17.   });  
  18. </script>  

這里隱含的一個約束是同一個頁面不能引入同名的模版,這會導致沖突,有必要時可以在模版文件命名時加上Namespace做區分。

 

2.2. 編碼問題

Dustjs的編碼問題相對簡單,先來看一個編譯后的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模版在加載時都會注冊到dust 全局對象中,模版間的互相引用都是通過該全局對象完成,不像Less那樣需要把組件的代碼合并到一起。因此解決Dustjs的編碼問題只要保證單個文件的編碼正確即可(詳見代碼)。

 

2.3. Node模塊定義

除了代碼,還需要補充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. }  

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

同時因為沒有加入npm倉庫,現階段還無法直接使用,需要通過git來引入,示例"dustjs-middleware": "git+http://git.oschina.net/joshuazhan/dustjs-middleware.git" 。

 

3. 一些感想

 

3.1. Callback

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

 

3.2. Express

和Java Web的Filter類似,Express中間件也是鏈式的處理請求,一個典型的中間件如下:

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

銜接各個中間件的則是next() 回調函數,如果中間件沒有把內容輸出到response 中,則必通過回調把請求交給下一個中間件處理。一般而言回調函數只能調用一次,多次調用可能產生異常;不調用則請求得不到響應,占用寶貴的鏈接資源和內存空間。

麻煩之處在于,Node中充斥著各種回調和匿名函數,使得next() 非常容易被遺忘或是錯誤的調用。這個目前貌似沒有很好的解決辦法,只能靠開發的經驗和測試,一個好的習慣是盡可能的在調用回調后就立即返回return next(); ,這個可以有效的避免多次調用的問題。

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

責任編輯:林師授 來源: Home4J
相關推薦

2015-12-21 14:56:12

Go語言Http網絡協議

2011-05-24 15:10:48

2021-02-11 08:21:02

中間件開發CRUD

2016-11-11 21:00:46

中間件

2018-02-01 10:19:22

中間件服務器系統

2018-07-29 12:27:30

云中間件云計算API

2010-03-24 17:59:20

2023-11-27 07:10:06

日志中間件

2012-11-30 10:21:46

移動中間件

2009-06-16 15:55:06

JBoss企業中間件

2023-10-24 07:50:18

消息中間件MQ

2023-06-29 10:10:06

Rocket MQ消息中間件

2010-04-13 10:37:47

核高基中間件

2022-09-21 16:09:28

消息中間件

2021-07-19 07:55:24

Redux中間件原理

2011-10-24 07:41:38

SOA中間件應用服務器

2021-04-22 06:13:41

Express 中間件原理中間件函數

2024-12-09 00:00:15

Gin框架中間件

2015-02-07 21:52:45

PaaS中間件

2009-06-16 10:53:01

JBoss中間件JBoss架構
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99久久精品国产毛片 | www成人免费 | 国产又爽又黄的视频 | 日韩精品一区二区久久 | 国产欧美视频一区二区 | 中文字幕日韩av | 在线播放一区二区三区 | 免费a网站 | 成人一区二区视频 | 精精国产xxxx视频在线播放 | 欧美日韩在线一区二区 | 精品国产欧美一区二区三区成人 | 欧美伊人久久久久久久久影院 | 亚洲午夜av | www.色综合| 日韩一区不卡 | 久久久久久久综合色一本 | 91传媒在线观看 | 欧美一区二区在线 | 久久久久久免费免费 | 一级做a爰片性色毛片 | 91精品久久久久久久 | 在线观看日本高清二区 | 老牛嫩草一区二区三区av | 91影院在线观看 | 日韩成人在线免费视频 | 欧美日韩手机在线观看 | 国产精品毛片一区二区在线看 | 蜜桃av人人夜夜澡人人爽 | 91 中文字幕 | 看av网址 | 欧美一级黄色片免费观看 | 亚洲+变态+欧美+另类+精品 | 精品久久久久久久久久久 | 999久久久久久久久6666 | 国产精品视频观看 | 久久综合久久综合久久综合 | 青青草久久 | 日韩国产欧美一区 | 亚洲一区 中文字幕 | 日韩免费视频一区二区 |