最近在使用echarts3 地图时遇到一个奇怪的问题,进入页面只显示南沙群岛部分,再次刷新页面显示正常,搜索无果后进行了摸索式探索,最终找到了问题的解决方案,现记录于此希望帮到遇到同样问题的朋友。
ECharts 3 中因为地图精度的提高,不再内置地图数据增大代码体积,使用地图时有两种方式:
JavaScript 引入示例
<script src="echarts.js"></script>
<script src="map/js/china.js"></script>
<script>
var chart = echarts.init(document.getElementById('main'));
chart.setOption({
series: [{
type: 'map',
map: 'china'
}]
});
</script>
JSON 引入示例
$.get('map/json/china.json', function (chinaJson) {
echarts.registerMap('china', chinaJson);
var chart = echarts.init(document.getElementById('main'));
chart.setOption({
series: [{
type: 'map',
map: 'china'
}]
});
});
我采用的是第一种方式,如果只加载echarts.js文件而不加载china.js,也会出现只显示南沙群岛的问题,但是这种情况无论怎么刷新都是只显示南沙群岛。我遇到的问题则是第一次进入该页面显示不正常,再次刷新后显示正常。
下面具体说下问题出现的应用场景:
1.基于ace admin的管理后台
2.在index.html文件中引入echarts.js以及china.js一切正常,如果放在子页面则不正常,具体有两种:如果把echart.js放在index.html,把china.js放在子页面里面就会出现本文所述的现象;把两个js文件都放在子页面html文件里面,则出现echarts没有定义的错误
经过上述分析,相信小伙伴已经知道如何解决这个问题了:把echarts.js以及china.js文件都放在顶层index.html文件里面即可。
下面说说产生这个问题的原因:
图1
上面这张图截自chrome浏览器调试界面,相信大伙对这个界面一定也不陌生,这里只分析两栏:XHR与JS,这是产生本文问题的最终原因。
前端开发人员最先接触的引入js脚本文件的方式就是通过<script src="xxx.js"></script>这种方式引入,这也就是图1中Type列所示的script方式,这是一种阻塞方式,遇到这种script标签后浏览器就会执行“下载该脚本,然后执行该脚本”的流程;这种方式的缺点也很明显,特别是当js文件过多时,就会导致浏览器渲染整个页面的过程加长。
实际应用中有以下几种方式,可以用于解决上述问题:
1.通过标准 DOM 函数创建<script>元素,这也是引入百度统计hm.js脚本文件的方式
var script = document.createElement ("script");
script.type = "text/javascript";
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);
新的<script>元素加载 script1.js 源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于:无论在何处启动下载, 文件的下载和运行都不会阻塞其他页面处理过程--异步。 您甚至可以将这些代码放在<head>部分而不会对其余部分的页面代码造成影响(除了用于下载文件的 HTTP 连接)。
当文件使用动态脚本节点下载时,返回的代码通常立即执行(除了 Firefox 和 Opera,他们将等待此前的所有动态脚本节点执行完毕)。当脚本是“自运行”类型时,这一机制运行正常,但是如果脚本只包含供页面其他脚本调用调用的接口,则会带来问题。这种情况下,您需要跟踪脚本下载完成并是否准备妥善。可以使用动态 <script> 节点发出事件得到相关信息。 Firefox、Opera, Chorme 和 Safari 3+会在<script>节点接收完成之后发出一个 onload 事件。您可以监听这一事件,以得到脚本准备好的通知。
2.使用类似ace_ajax等一些异步加载js脚本框架来加载js文件
3.使用 XMLHttpRequest(XHR)对象,此技术首先创建一个 XHR 对象,然后下载 JavaScript 文件,接着用一个动态 <script> 元素将 JavaScript 代码注入页面。
var xhr = new XMLHttpRequest();
xhr.open("get", "script1.js", true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
var script = document.createElement ("script");
script.type = "text/javascript";
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
};
xhr.send(null);
此代码向服务器发送一个获取 script1.js 文件的 GET 请求。onreadystatechange 事件处理函数检查 readyState 是不是 4,然后检查 HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的<script>元素,将它的文本属性设置为从服务器接收到的 responseText 字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新<script>元素被添加到文档,代码将被执行,并准备使用。
这种方法的主要优点是,您可以下载不立即执行的 JavaScript 代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得您可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。 此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指"内容投递网络(Content Delivery Network)",所以大型网页通常不采用 XHR 脚本注入技术。
图2
图2中这些通过xhr方式加载的js文件就是放置在ace子页面的文件,先看下china.js文件相关内容:
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['exports', 'echarts'], factory);
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// CommonJS
factory(exports, require('echarts'));
} else {
// Browser globals
factory({}, root.echarts);
}
由于在引入china.js文件之前引入了echarts.js文件,echarts文件里面定义了define为一个函数,这就会导致浏览器加载完china.js文件并执行时没有进入china.js默认函数--初始化部分,所以导致中国地图没有初始化。
这里涉及到amd---异步模块定义规范,后期会写这方面相关的文章
PS:echarts地图数据下载网址:http://echarts.baidu.com/download-map.html