因为FireFox已经兼容了Chrome的Extension API,所以为Chrome制作的Extension可以几乎无缝的发布到FireFox里。但Firefox对代码的要求比Chrome要高,比如今天的主角eval类方法就不允许在Extension里使用,Chrome是可以使用的。本文就是介绍一下相关的内容以及如何去支持。
eval类方法
简单说明一下,eval
类方法,除了eval
还有new Function
,这两个方法要在Extension里使用,需要在manifest.json
里开启安全策略
,如:
"content_security_policy": "'unsafe-eval'",
不然就会报错:
Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' blob: filesystem:".
再看看Firefox code review拒绝说明:
Extensions defining a content security policy that allows eval ('unsafe-eval') are generally not allowed for security and performance reasons. eval is only necessary in rare cases. Please use a different method.
所以,我们是无法在Firefox Extension(Add-ons)里使用eval类方法的。
为什么要使用eval类方法
最明显的原因,如:模板解析。我们知道,没有模板解析在渲染数据时会非常的麻烦和不好看,而大多数的模板解析都会使用eval来将字符串转为js function,以便调用。这其实是JS非常有优势的地方,几个简单的正则就可以有一整套模板解析机制。可惜无法简单的使用它们来帮助开发。
解决方法
使用沙盒模式
在Chrome的文档:Using eval in Chrome Extensions. Safely
有讲解,如果使用沙盒模式来安全的使用eval的模板解析库,可以直接参考:https://developer.chrome.com/extensions/sandboxingEval
因为Firefox和Edge都是使用Chrome同种的Extension,所以肯定是通用的。
它的问题:
因为它实际是使用iframe模式,所以当你的模块拆分比较细的时候,并不好处理,而且不是所有Extension都好使用它,所以它肯定不是一个好的选择。
预编译模式
这其实也是Chrome推荐的一种方法,使用的时候先编译好,调用的是编译后的,这样就可以完全的避免使用eval
类方法。但这里仍然有些问题,最主要是如何兼容开发/发布模式。这里可以有两种方法:
- watch监听文件修改,执行编译。很多库也有使用此方法
- 开发保持使用
eval
不变,发布编译时将其移除,同时编译模板,并更换调用的代码
我不是太喜欢watch监听的方式,因为不管怎么样,都有可能慢一拍,体验不好,所以选择第2种,也是YuiAPI使用的方法,简单说明。
开发的时候,保持使用eval不变
mainfest.json:
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
解析:
evalViewFunc: function(model, name) {
let key = model + "." + name;
if (!this.hasOwnProperty(key)) {
let content = this[model][name](),
_html = [];
let txt = ["App.view.extend('"+ model +"."+name+"', function() {"];
txt.push("this.init = function(data) {");
txt.push("var html = '';");
content = content.split('n');
for (let i in content) {
_html.push(this.parse(content[i]));
}
txt.push(_html.join(""));
txt.push("return html;");
txt.push("}");
txt.push("});");
eval(txt.join(""));
}
}
编译的时候
- 读取mainfest.json将content_security_policy去掉后,再生成新的mainfest.json文件
- 根据模板信息,编译生成一个view.js,将引用模板的调用去掉,最后引用view.js
这样发布后view.js会直接运行,而在开发中使用view的方法都没有问题。
可以参考:
https://github.com/yuiitsu/YuiAPIgithub.com