您的位置:首页 > 其它

自己动手——让Less支持中文编码

2014-05-26 12:22 267 查看
源Blog链接:http://home4j.duapp.com/index.php/2014/05/25/diy-a-less-with-multi-encoding-support.html

Node近几年发展得非常迅猛,但还是有些小缺憾,比如对中文编码的支持。由于底层API对中文编码的不友好,使得很多Node的模块(less)不能使用GBK编码的文件。本文借鉴网上的通用做法,对Less模块做一些改造,使其支持GBK文件。

1. Node的文件编码问题及通用解决方法

Node读写文件是通过File System API完成的,以读接口为例:

/**
* 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)

接口可以传入文件编码,但目前支持的编码有限,只有UTF8、ASCII等。如果要读取GBK编码的文件,只能使用文件原始的二进制Buffer来进行编码解析(不指定encoding参数)。 文件写入的问题也类似,需要对文本内容进行编码后才能存入。 目前用得最多的字符编解码模块有iconv-lite(Github主页),支持GBK等多种字符编码,示例如下。

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");

2. DIY解决编码问题

通过iconv-lite,解决文本编码的问题,只需要在所有读写文本文件的地方进行编解码即可。

2.1. less-middleware

在Web服务端,Less文件是通过中间件less-middleware读取的,中间件把文本内容传入Less解析器,渲染出CSS后,才存入本地文件并返回给浏览器。针对中间件的改动很简单,代码如下:

// /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文件。

var express = require('express');
var less = require('less-middleware');
...
var app = express();
var publicDir = __dirname + '/public';
app.use(less(publicDir, {
encoding: 'gbk'
}));
...

2.2. Less解析器

Less有一个Import功能,可以引入其他Less或CSS文件到当前文件中,以实现代码的复用。被Import的文件是在Less解析器中读取的,因此前一节Less中间件的调整对Import文件是无效的,还需修改解析器自身。

// /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参数传入。为此我们查看解析器源码,关键内容如下。

// /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 // 传入编码参数
}));
...
};
...
};

3. 代码的使用

本文对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",
...
}
}

有需要的同学可以直接使用。

4. 一点感想

Node文件编码格式问题的解决,本文是最次的一种方式,更为理想的是在Node的File System API上改进。当然如果是新应用或系统,使用UTF8编码,绕过这个问题,也不失为一种选择。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: