源Blog连接:http://home4j.duapp.com/index.php/2014/05/25/diy-a-less-with-multi-encoding-support.htmlphp
Node近几年发展得很是迅猛,但仍是有些小缺憾,好比对中文编码的支持。因为底层API对中文编码的不友好,使得不少Node的模块(less)不能使用GBK编码的文件。本文借鉴网上的通用作法,对Less模块作一些改造,使其支持GBK文件。css
Node读写文件是经过File System API完成的,以读接口为例:html
/** * filename String * options Object * encoding String | Null default = null * If no encoding is specified, then the raw buffer is returned. * flag String default = 'r' * callback Function */ fs.readFile(filename, [options], callback)
接口能够传入文件编码,但目前支持的编码有限,只有UTF八、ASCII等。若是要读取GBK编码的文件,只能使用文件原始的二进制Buffer来进行编码解析(不指定encoding参数)。 文件写入的问题也相似,须要对文本内容进行编码后才能存入。 目前用得最多的字符编解码模块有iconv-lite(Github主页),支持GBK等多种字符编码,示例以下。node
var iconv = require('iconv-lite'); // Convert from an encoded buffer to js string. var str = iconv.decode(buf, 'win1251'); // Convert from js string to an encoded buffer. var buf = iconv.encode("Sample input string", 'win1251'); // Check if encoding is supported var isExist = iconv.encodingExists("us-ascii");
经过iconv-lite,解决文本编码的问题,只须要在全部读写文本文件的地方进行编解码便可。git
在Web服务端,Less文件是经过中间件less-middleware读取的,中间件把文本内容传入Less解析器,渲染出CSS后,才存入本地文件并返回给浏览器。针对中间件的改动很简单,代码以下:github
// /less-middleware/lib/middleware.js var iconv = require('iconv-lite'); // Less中间件参数 options = extend(true, { ... // 文件编码参数 encoding: 'utf-8', ... // 存储编译后的CSS storeCss: function(pathname, css, next) { ... // 编码后再保存文件 var buf = iconv.encode(css, options.encoding); fs.writeFile(pathname, buf, next); }); }, options || {}); // 编译Less文件 var compile = function() { fs.readFile(lessPath, function(err, buf){ ... // 编译前先对文件内容解码 var lessSrc = iconv.decode(buf, options.encoding); lessSrc = options.preprocess.less(lessSrc, req); render(lessSrc, lessPath, cssPath, function(err, css){ ... }); }); };
使用中间件时,只需添加encoding参数,便可支持GBK编码的Less文件。express
var express = require('express'); var less = require('less-middleware'); ... var app = express(); var publicDir = __dirname + '/public'; app.use(less(publicDir, { encoding: 'gbk' })); ...
Less有一个Import功能,能够引入其余Less或CSS文件到当前文件中,以实现代码的复用。被Import的文件是在Less解析器中读取的,所以前一节Less中间件的调整对Import文件是无效的,还需修改解析器自身。json
// /less/lib/less/index.js /** * fileLoader函数用于加载Less代码中被Import的文件。 * env 为编译器的执行环境,用以保存执行参数 */ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { ... var isUrl = isUrlRe.test( file ); if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) { // 若是Import的地址是URL格式,则从网络加载 ... } else { // Import本地文件的处理逻辑 ... // 分同步和异步两种方式读取Import文件 if (env.syncImport) { ... var buf = fs.readFileSync(pathname); data = iconv.decode(buf, env.encoding || 'utf-8'); ... } else { ... fs.readFile(pathname, function(e, buf) { ... data = iconv.decode(buf, env.encoding || 'utf-8'); ... }); } } };
Less解析器的fileLoader函数用于加载文件,简单改动即实现功能,剩下的问题是如何把encoding参数传入。为此咱们查看解析器源码,关键内容以下。api
// /less/lib/less/parser.js // 构造解析器时传入env参数 less.Parser = function Parser(env) { ... // 把env参数转化成parseEnv对象 if (!(env instanceof tree.parseEnv)) { env = new tree.parseEnv(env); } ... less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { ... }, env); ... return parser; };
进而查看tree.parseEnv 的实现,发现须要把encoding参数添加到保留的属性列表中。浏览器
// /less/lib/less/env.js (function (tree) { // 定义须要保留的属性 var parseCopyProperties = [ 'paths', // option - unmodified - paths to search for imports on // 把encoding参数加入 'encoding', // encoding used to read/write files 'optimization', // option - optimization level (for the chunker) ... ]; tree.parseEnv = function(options) { // 根据要保留的属性列表parseCopyProperties, // 把options中的参数复制到新的parseEnv对象中(经过this引用) copyFromOriginal(options, this, parseCopyProperties); ... }; ... })(require('./tree'));
最后,在less-middleware使用Less解析器处传入编码参数,Less中文编码问题就基本解决了。
// /less-middleware/lib/middleware.js ... module.exports = less.middleware = function(source, options, parserOptions, compilerOptions){ // Parse and compile the CSS from the source string. var render = function(str, lessPath, cssPath, callback) { // 构造Less解析器 var parser = new less.Parser(extend({}, options.parser, { filename: lessPath, encoding: options.encoding // 传入编码参数 })); ... }; ... };
本文对less-middleware和less的修改已提交到Git@OSC上。
less-middleware,http://git.oschina.net/joshuazhan/less.js-middleware
less,http://git.oschina.net/joshuazhan/less.js
同时,Node支持以git路径做为依赖,示例以下:
// /package.json { ... "dependencies": { ... "less-middleware": "git+http://git.oschina.net/joshuazhan/less.js-middleware.git", ... } }
有须要的同窗能够直接使用。
Node文件编码格式问题的解决,本文是最次的一种方式,更为理想的是在Node的File System API上改进。固然若是是新应用或系统,使用UTF8编码,绕过这个问题,也不失为一种选择。