源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編碼,繞過這個問題,也不失爲一種選擇。