解决less的data-uri函数无法处理相对路径的Bug
2014-06-03 13:36
405 查看
Bug由来
从1.4.0版本开始,less添加了一个很好用的内置函数:data-uri。这个函数可以自动将less代码中引用的图片进行base64编码。比如我们要引用一个小图片作为背景,我们可以这样写:.foo-icon { background: data-uri('./img/foo.png') no-repeat; *background": url(htt://g.tbcdn.cn/onlone-addr.png) no-repeat; }
编译过之后会自动转换为:
.foo-icon { background: url("data:image/png;base64,iVBORw0KG ... mCC") no-repeat; *background": url(htt://g.tbcdn.cn/onlone-addr.png) no-repeat; }
这个函数极大的便利了前端的开发工作。在此之前,遇到这个情况我们一般都是自己运行相关的工具 进行编码,然后将编码后的字符手工粘贴到less源文件中。这样做不仅使less源代码又长又臭,而且也笔记容易出错。当设计稿经常改动时,需要多次手工操作,很容易出现粘贴代码不全,粘贴了旧图片的代码导致图片编码没有更新等问题。
当我们团队发现这个新函数后,就像发现了新大陆一般,兴奋异常,立即将使用的grunt-contrib-less插件更新到最新版本,并在正在开发的项目中大量使用此函数。当我们用的正Hight的时候,一个坑悄然而至。
当时我们的项目目录是这样规划的
我们组的两个同学分别负责home页面和detail页面的开发工作。两人分别用data-uri函数编码各自页面对应的less文件引用的图片。一切都运行的很完美。但在我们切图的过程中UI同学应老大的要求把效果图进行了调整,而我们两个同学一看最新的效果图发现两个页面中有很大一部分图标都是相同的。所以大家决定把这部分公共图标提炼为公共组件,供各个页面共享。于是一个同学把这部分图标相关代码移动到了/lib/less/common/icons.less,之后两人将原来的图标样式代码删除,然后在顶部添加了对公共图标样式文件的引用: @import
"src/lib/less/common/icons";
这时候我们惊讶的发现代码怎么都编译不过去了。无论我们将图片地址写成基于页面less的相对地址,还是基于icons.less的相对地址,还是基于项目目录的绝对地址都报找不到图片。文件没有被别的文件 引用的时候还可以工作的,怎么被引用了之后就不能工作了呢?经过多次尝试,并综合google搜索到信息,最后我们确定这个问题应该是less的一个Bug。
bug原因
当时我曾尝试自己修复这个Bug,但因为代码比较多,时间又比较紧,没有成功。我当时只能要么将着用,要么自己去手动编码。我被这个Bug恶心了很久,就像吃饭的时候吃出了不该有的东西一样。终于项目不是那么忙了,可以腾出手来收拾它了。一开始我怀疑这是我们grunt文件的配置不合理造成的,缺少了必要的配制参数导致路径计算不正确。后来读LESS的源代码项发现即使把可能有用的配制信息加上还是有问题,所以这个可能排除了。最终把怀疑的焦点时转向了data-uri函数上。为了便于调试我创建了一个只有两个less文件的测试项目。两个文件的目录位置和内容如下:
//src/lib/less/sub.less body { background: data-uri('./img/hot.png'); } //src/page/main.less @import "src/lib/less/sub";
为了获取运行时的参数情况我们修改less中的less/lib/less/functions.js中的data-uri函数的代码
将参数信息输出到控制台。之后我们运行grunt
less命令编译两个文件得到如下控制台输出。其中sub.less文件编译成功,而page.less则因为文件引用的图片找不到而编译失败。
通过分析控制台输出我们可以看到,传给data-uri函数的参数中less提供了四个必要的参数:
* value 传给data-uri函数的参数 * filename 运行data-uri函数的less文件名称 * rootFilename 正在编译的、且引用了其他其他less文件的less文件 * currentDirectory 运行data-uri函数的less文件所在的目录 * entryPath 正在编译的、且引用了其他其他less文件的less文件所在的目录
而且通过对比两个文件的这四个参数我们可以发现一个规律:如果less文件没有被其他less文件所引用,那么rootFilename与filename的值是一样的。
有了这些信息之后按说我们完全有能力采用两种不同的策略来计算计算要引用的文件的绝对路径。如果文件没有被其他文件引用, value与entryPath运算计算出绝对路径;如果文件被其他文件所引用,value与currentDirectory运算计算出绝对路径。那less是怎么处理的呢?答案是没有处理!!!下面是less中这部分的处理代码
在上面的代码片段中,less首先判断传入的文件路径(也就是参数value的值)是否是相对路径,如果是再判断下是否配置了url路径到本地路径的映射。如果有则将URL路径替换为本地路径,然后计算计算出绝对地址(这个功能我们一直没用过, 读代码才发现有这个功能,所以这个参数没有配置); 如果没有配置,则不管文件是否被引用,全部按运行data-uri函数的less文件所在的目录进行计算。至此案情算是水落石出了。真想不通,实现这个函数的人怎么会采用这么简单粗暴的实现呢?在css中文件即使被其他文件以@import
"filename.css";的方式引用了了,引用的图片的绝对路径也还是按照css文件所在的路径计算的啊。
解决方案
我们临时写了一个插件:grunt-myless, 以替换掉grunt自动的less插件, 其中对less的data-uri函数做了如下修改:tree.functions['data-uri'] = function(mimetypeNode, filePathNode) { if (typeof window !== 'undefined') { return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); } // edit code start ------------------ var isFileInc; var mimetype = mimetypeNode.value; var fileInfo = this.currentFileInfo; var filePath = (filePathNode && filePathNode.value); var fs = require('fs'), path = require('path'), useBase64 = false; if (arguments.length < 2) { filePath = mimetype; } if (this.env.isPathRelative(filePath)) { isFileInc = (function(){ var filename = fileInfo.filename.replace(/\\/g, '/'); var rootName = fileInfo.rootFilename.replace(/\\/g, '/'); return filename != rootName; })(); if (fileInfo.relativeUrls) { filePath = path.join(fileInfo.currentDirectory, filePath); } else { filePath = (isFileInc ? path.join(fileInfo.currentDirectory, filePath) : path.join(fileInfo.entryPath, filePath) ); } } // edit code end -------------------- // detect the mimetype if not given if (arguments.length < 2) { var mime; try { mime = require('mime'); } catch (ex) { mime = tree._mime; } mimetype = mime.lookup(filePath); // use base 64 unless it's an ASCII or UTF-8 format var charset = mime.charsets.lookup(mimetype); useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; if (useBase64) { mimetype += ';base64'; } } else { useBase64 = /;base64$/.test(mimetype); } var buf = fs.readFileSync(filePath); // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded // and the --ieCompat flag is enabled, return a normal url() instead. var DATA_URI_MAX_KB = 32, fileSizeInKB = parseInt((buf.length / 1024), 10); if (fileSizeInKB >= DATA_URI_MAX_KB) { if (this.env.ieCompat !== false) { if (!this.env.silent) { console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); } return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); } } buf = useBase64 ? buf.toString('base64') : encodeURIComponent(buf); var uri = "\"data:" + mimetype + ',' + buf + "\""; return new(tree.URL)(new(tree.Anonymous)(uri)); };
注意使用的时候,需要在gruntfile.js中添加如下配置:
当然如果不想更换插件自己手动修改less的源代码效果也是一样的,但那样的话每次项目初始化之后都需要自己手动修改,这点有点不爽。
原文链接: http://www.atatech.org/articles/17513
相关文章推荐
- 解决less的data-uri函数无法处理相对路径的Bug
- VS2005中的一个小BUG:关于Dropdownlist无法Datadinding的解决方法。
- .Net下使用System.Data.OracleClient出现“ORA-12154问题“TNS: 无法处理服务名”问题的解决
- CFileDialog改变文件路径导致的一系列问题(如无法安全删除u盘、访问相对路径失败)的解决方法
- 快速解决commons-fileupload组件无法处理自定义head信息的bug
- 在JAVA WEB项目中,JSP中的图片无法显示,绝对路径和相对路径问题的解决办法。
- hadoop 安装出现的几种异常的处理方法,hadoop无法启动,no namenode to stop问题的解决方法,no datanode
- 【FAQ】gerrit change-merged hook 中,无法使用相对路径,bug?
- MySQL服务在windows系统中修改Data路径后无法启动问题解决
- XP下解决“system.web.services.protocols.soapexception:服务器无法处理请求。…〉system.exception:system.data.oracleclient 需要oracle客户端软件8.1.7或更高版本。
- 相对路径获取项目文件 及报错 No mapping found for HTTP request with URI XXX in DispatcherServlet with name ‘springmvc’解决方法
- 解决:无法将“Add-Migration”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次 1、输入的中划线“-”格式不对,检查是
- 解决:无法将“Add-Migration”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次
- 解决commons-fileupload组件无法处理自定义head信息的bug
- 解决OpenCV的imread函数无法读取中文路径和中文命名的文件的问题。
- 解决:无法将“Add-Migration”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次
- labview中,用相对路径动态调用的子VI,在生成EXE文件后无法调用问题的解决
- 解决sqlite3_open函数无法打开中文路径问题
- .Net下使用System.Data.OracleClient出现“ORA-12154问题“TNS: 无法处理服务名”问题的解决
- 解决commons-fileupload组件无法处理自定义head信息的bug