背景
为了方便用户使用sdk,从用户角度想简化引入各种css、js库的麻烦琐事。
问题来了,用document.write可以完美解决这个问题,但问题的问题是虽然浏览器有警告,但它还是执行了,就不知道哪一天会不会直接error出现或者拦截了。
最好的方式当然是采用es6使用import模块的方式,这里是考虑了老的加载方式,即直接通过引入script标签引入js库。
现实情况是难免会有用到引入第三方库的情况。
这里也不考虑使用requirejs的方式,因为用了这个还不如直接用es6自带的import来的快。
Why you should avoid using document.write, specifically for scripts injection
var sNew = document.createElement("script");
sNew.async = true;
sNew.src = "https://example.com/script.min.js";
var s0 = document.getElementsByTagName('script')[0];
s0.parentNode.insertBefore(sNew, s0);
解决方案
1 目前暂可行的实施方案
// 动态加载js脚本文件
function loadScript(url) {
document.write(`\x3Cscript type="text/javascript" src="${url}">\x3C/script>`);
}
// 动态加载css文件
function loadStyles(url) {
document.write(
`\x3Clink type="text/css" rel="stylesheet" href="${url}">\x3C/link>`
);
}
(function () {
loadStyles(ipconfig + "/css/a.css");
loadStyles(ipconfig + "/css/b.css");
loadScript(ipconfig + "/lib/a.js");
loadScript(ipconfig + "/lib/b.js");})();
2 回调函数
意味着要使用使用第三方的对象需要在回调函数中使用
function loadScript(url, callback){
var script = document.createElement ("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
loadScript(urlCesium, function(){alert('加载成功')});
3 异步加载
写法1 ol中的使用方法
class LazyLoader {
/**
* @param {string} url
* @api
*/
constructor(url) {
/**
* @type {Promise<undefined>}
* @protected
*/
this.promise;
/**
* @private
* @type {string}
*/
this.url_ = url;
}
/**
* @return {Promise<undefined>}
* @api
*/
load() {
if (!this.promise) {
// not yet loading
this.promise = new Promise((resolve, reject) => {
const script = document.createElement("script");
script.onload = () => resolve();
script.onerror = () => reject();
document.head.appendChild(script);
script.src = this.url_;
});
}
return this.promise;
}
}
const url = "http://localhost:9300/a.js";
const aLazyLoader = new LazyLoader(url);
var a = cesiumLazyLoader.load().then(() => console.log('使用a模块中的对象'));
写法2 Cesium中的使用方法
/**
* @private
*/
function loadAndExecuteScript(url) {
var deferred = when.defer();
var script = document.createElement("script");
script.async = true;
script.src = url;
var head = document.getElementsByTagName("head")[0];
script.onload = function () {
script.onload = undefined;
head.removeChild(script);
deferred.resolve();
};
script.onerror = function (e) {
deferred.reject(e);
};
head.appendChild(script);
return deferred.promise;
}
export default loadAndExecuteScript;
// 使用方法
var url = buildModuleUrl("ThirdParty/google-earth-dbroot-parser.js");
var oldValue = window.cesiumGoogleEarthDbRootParser;
var dbrootParserPromise = loadAndExecuteScript(url).then(function () {
dbrootParser = window.cesiumGoogleEarthDbRootParser(protobufMinimal);
if (defined(oldValue)) {
window.cesiumGoogleEarthDbRootParser = oldValue;
} else {
delete window.cesiumGoogleEarthDbRootParser;
}
});
结束语
其实使用es6的模块化+异步的方法能更好的解决这个问题。如果有其他办法后续再说吧
参考文章
https://stackoverflow.com/questions/7761149/dynamically-added-script-will-not-execute
I am dynamically adding a script of a github gist. If I added it to the html before the page loads, the script will execute. But If I try to add it to the page after it loads it adds the script to the page but doesn’t execute it.
editorJs2.async = false
这个问题的产生原因是:我们项目有一个主index文件,在主index文件中需要根据参数来判断是加载pc.html的内容还是加载mobile.html的内容,一开始是使用jquery来做的,没有问题,后来想着jquery也有80多k就想着优化下,结果将所有有$的地方都转为原生的js代码,js,css,dom也都加载正常,就是页面显示不出来,动态加载的js没有执行,查了很长时间才找到方案,如上图;