Menu Home

maps开发之如何实现自定义代码

功能背景

maps提供了很多种模块功能,但是不能完全覆盖所有运营用户的需求,他们有可能需要自定义一些功能,比如从别人网站copy 一份代码等等。

原理分析

自定义前端代码,无非就是css、html 和js 三种语言。

  • html 最简单了,使用zepto 直接html()方法带页面容器中。
  • css 也可以使用append()方法插入到head 这个标签中。
  • js 就不能直接使用append 或者html等方法实现运行时执行,js 本身语法提供了,诸如new Functioneval实现运行执行

功能实现

输入代码的窗口如下 这样就会让css、html、js混杂在一起,如果分辨出来相应的代码。

解析代码

如果从众多的代码中识别指定的代码,除了正则没有更好的办法了。但是如果用户没有一定规范混杂写代码,正则识别起来也会比较困,所以得有个规范约束它们。
所以我们要求,css 代码写在<style></style> js 代码写在<script></scrpt>,html 写在<html></html>中 这样我就可以识别这个标签读到指定的代码。如下段代码。

    parseCodeHtml:function (str) {
            var styleCodeStr = '';
            var styleResult;
            var styleCodeReg = /<html>([^\r]+)<\/html>/igm;
            while ((styleResult = styleCodeReg.exec(str)) !== null) {
                styleCodeStr += RegExp.$1
            }
            this.getBoundingBox().find('.W_custom_1_container').html(styleCodeStr);
        },

从上 面不难看出我使用了一个全局匹配,这样我可以读取到多段的html 标签,然后在融合成一个字符串styleCodeStr。以下是其他语言的识别的正则表达式。

 var styleCodeReg = /<script>([^\r]+)<\/script>/igm;  //js 正则表达式
 var styleCodeReg = /<style>([^\r]+)<\/style>/igm;  //css 正则表达式

正如上面所讲,我们识别相应代码的字符串后,我们如何让指定的代码生效呢?

执行代码

html

如果是html 的代码 我们直接html()插入到dom 中

css

如果是css 呢。上面规划的是append 到head 标签中,那我们是不是有更加好的办法呢。
我们注意到styleSheet.insertRule这个方法,这个方法具体是干什么的呢,就是一条一条将css 执行生效。
相关链接 https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule

方法一:

实现了一个版本,看起来还是比较复杂,大概的步骤就是用正则表达式一行一行的将css 语法提炼出来,然后使用inertrules执行。

缺点:运行了一段时间后发现了一个弊端就是,他识别一些选择器不全。容易导致错误,这个问题出现在我们用正则一行一行识别css语句的时候出现了问题,正则不够健全。在浏览器调试的时候,会被浏览器标记为浏览器默认样式,是不可更改了

parseCodeStyle: function (str) {

            var styleCodeStr = '';
            var styleCodeReg = /<style>([^\r]+)<\/style>/igm;
            var styleResult;
            while ((styleResult = styleCodeReg.exec(str)) !== null) {
                styleCodeStr += RegExp.$1
            }
            var styleStr = [];
            var styleReg = /([\.\#]?\w+[^{]+)\{([^}]*)\}/igm;

            while ((styleResult = styleReg.exec(styleCodeStr)) !== null) {
                var temp = [];
                temp.push(RegExp.$1);
                temp.push(RegExp.$2);
                styleStr.push(temp)
            }
            this.addStylesheetRules(styleStr)
        },
        addStylesheetRules: function (rules) {
            var styleEl = document.createElement('style'),
                styleSheet;
            document.head.appendChild(styleEl);
            styleSheet = styleEl.sheet;

            for (var i = 0, rl = rules.length; i < rl; i++) {
                var j = 1, rule = rules[i], selector = rules[i][0], propStr = '';
                if (Object.prototype.toString.call(rule[1][0]) === '[object Array]') {
                    rule = rule[1];
                    j = 0;
                }
                styleSheet.insertRule(selector + '{' + rule[1] + '}', styleSheet.cssRules.length);
            }
        }
方法二

将识别出来的style的字符串,用blob 生成二进制流,然后使用window.URL.createObjectURL 生成可连接的资源
目前看下来这个方法没有缺点,webpack 打包css 也是使用类似的原理。该方法能够不改变原来用户输入的内容、然后能够在浏览器产生缓存并且还能能够在开发工具中自由修改。

parseCodeStyle: function (str) {

            var styleCodeStr = '';
            var styleCodeReg = /<style>([^\r]+)<\/style>/igm;
            var styleResult;
            while ((styleResult = styleCodeReg.exec(str)) !== null) {
                styleCodeStr += RegExp.$1
            }
            this.addStylesheetRules(styleCodeStr)
        },
        addStylesheetRules:function (rules) {
            var url = window.URL.createObjectURL(new Blob([rules], {type: 'text\/css'}))
            if($("#custom").size()==0) {
                var style = document.createElement('link');
                style.href = url;
                style.rel = 'stylesheet';
                style.type = 'text/css';
                style.id = "custom";
                document.getElementsByTagName('HEAD').item(0).appendChild(style);
            }else{
                $("#custom").attr('href',url)
            }
        }

js

js 部分就是使用eval执行运行时的代码,
不过需要注意的eval 的时候的this的作用域,以及如果报错了,如果忽略,不阻塞其他的js 代码执行。

 parseCodeHtml:function (str) {
            var styleCodeStr = '';
            var styleResult;
            var styleCodeReg = /<html>([^\r]+)<\/html>/igm;
            while ((styleResult = styleCodeReg.exec(str)) !== null) {
                styleCodeStr += RegExp.$1
            }
            this.getBoundingBox().find('.W_custom_1_container').html(styleCodeStr);
        },
        parseCodeScript:function (str) {
            var styleCodeStr = '';
            var styleResult;
            var styleCodeReg = /<script>([^\r]+)<\/script>/igm;
            while ((styleResult = styleCodeReg.exec(str)) !== null) {
                styleCodeStr += RegExp.$1
            }
            try {
                eval(styleCodeStr)
            }catch (e){
                common.msgShow(e.toString())
            }
        },

功能总结

讲到这里基本上maps 的自定义模块基本上算是讲完了。大家可以看出来自定义模块的功能的代码的优先级是比较高的。是可以覆盖项目本身的其他任何的代码,除了js 作用域被限制在 eval 当前的作用域。
目前这种策略有一定的安全性的问题的,eval 理论上是可以执行任何程序,所以如果有人放入了恶意的代码的话。所以这个策略还有很多改进的空间的。

Categories: code

Tagged as:

knowthis

发表评论

电子邮件地址不会被公开。 必填项已用*标注