自己动手:实现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. };  

需要注意的是中间件以文件名作为模版的名字,使用模版时,需要指定该模版名,示例如下。

 
 
 
  •  
  •  
  •  
  •  
  •  
  •  
  • 这里隐含的一个约束是同一个页面不能引入同名的模版,这会导致冲突,有必要时可以在模版文件命名时加上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

    新闻名称:自己动手:实现Dustjs中间件
    文章位置:http://www.shufengxianlan.com/qtweb/news24/244624.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

    猜你还喜欢下面的内容

    网站设计公司知识

    同城分类信息