將index.html中的遠程js和css設置為可配置的動態加載
之前:index.html中寫死了js與css的路徑,每次打包都要修改index.html文件或者用替換的方式將index.html中的網址換為新的要部署地區的網址,給部署人員帶來復雜的操作;開發人員進行部署測試也很不方便。
現在:通過Promise實現了異步加載js的方法,將js和css從index.html中提取出來,變成路徑可配置的形式,簡化了部署步驟以及開發測試的工作。
主要解決了vue的mounted階段使用到js中的對象,控制台提示變量未定義的錯誤。因為無論是用document.write還是用原生js直接添加到dom樹上的方法,都是會在mounted掛載階段完成之后再解析js,所以,一直會遇到“XX變量未定義”的錯誤
Promise的定義:promise是對異步編程的一種抽象。它是一個代理對象,代表一個必須進行異步處理的函數返回的值或拋出的異常。
Q是nodeJs中實現promise的包之一,是nodexJs中比較常用的一個庫。
下面來說一說具體實現:
一、安裝Q
npm install q -save
二、在項目中的使用:
2.1 創建用於加載js、css的文件
文件名為loadResources.js,文件內容如下:
/** * Created by 飛呀 on 2018/1/9. */ //加載css與js文件 var Q=require('q'); export default function asyncLoadJs (url) { return Q.Promise((resolve, reject) => { let srcArr = document.getElementsByTagName("script"); let hasLoaded = false; for (let i=0;i<srcArr.length;i++){//判斷當前js是否加載上 hasLoaded = (srcArr[i].src==url)?true:false; } if (hasLoaded) { resolve(); return; } let script = document.createElement('script') script.type = 'text/javascript'; script.src = url; document.body.appendChild(script); script.onload = () => { resolve(); } script.onerror = () => { reject(); } }) } export function loadCss(url){ let css = document.createElement('link'); css.href = url; css.rel = 'stylesheet'; css.type = 'text/css'; document.head.appendChild(css); } export function loadMineMapJs () { //加載css loadCss( appDomainRoot+"/minemapapi/v1.3/minemap.css"); loadCss( appDomainRoot+"/minemapapi/v1.3/plugins/draw/minemap-draw.css"); loadCss( appDomainRoot+"/minemapapi/v1.3/plugins/edit/minemap-edit.css"); //加載js return Q.Promise((resolve, reject) => { asyncLoadJs("./static/js/minemap_wmts.js")//開發用 /*asyncLoadJs(appDomainRoot + "/minemapapi/demo/js/minemap-wmts.js")//部署用*/ .then(() => { return asyncLoadJs(appDomainRoot + "/minemapapi/v1.3/plugins/draw/minemap-draw.js") }) .then(() => { return asyncLoadJs(appDomainRoot + "/minemapapi/v1.3/plugins/edit/minemap-edit.js") }) .then(() => { resolve() }) .catch(err => { reject(err) }) }) }
需要注意的是,loadMineMapJs方法中要加載多個js可以在后面加then方法再次返回asyncLoadJs方法,這是
級聯的用法。
2.2 組件中對於異步加載js模塊的使用
引入模塊
import {loadMineMapJs} from '../globals/loadResources'
緊隨其后,定義一個用於判斷所有js是否加載完成的一個標志
let loadedMineMapJs = false;//是否加載完js
因為loadedMineMapJs這個變量在created階段就要使用到,所以不能放在data里面。

created階段:
created(){//判斷瀏覽器是否加載過js if (!loadedMineMapJs) { loadMineMapJs().then(() => { loadedMineMapJs = true; }) } },
mounted階段:
mounted(){ let _this = this; //循環判斷是否加載完js let interval = setInterval(()=>{ if(loadedMineMapJs){ clearInterval(interval); _this.controller = new HomeController(_this); _this.controller.initMapEdit(null, null, null); } },100); ... },
下面這兩句是我會用到js中生成對象的地方,所以需要在js完成后執行
_this.controller = new HomeController(_this); _this.controller.initMapEdit(null, null, null);
2.3 對於網址的配置
在serverConfig下的index.js文件中配置了第一步中用到appDomainRoot(前提是index.js文件在static文件夾下,不會被webpack打包),修改的時候只需要修改appDomainRoot的值即可,變量未定義的問題順利解決。
文件結構:

index.js中配置如下:
let appDomainRoot= 'http://192.168.XX.XX';
這個功能難就難在index.html中引入的js和css會直接在html解析時就引入,一旦使用動態添加到dom樹的js語句(比如document.body.appendChild())的方式添加動態script必然導致瀏覽器在解析到這些js時重新生成dom樹,經測試這種方式會在vue掛載前拼接到index.html中,但是要等到掛載結束才會被解析,所以在掛載階段用到js解析生成的對象會一直出現“變量未定義”的錯誤。
使用document.write()的方式雖然可以實現,但是瀏覽器會輸出警告,大意是瀏覽器正在解析跨域資源,而且有幾條動態js就會警告幾次,而且在控制台占的面積相對較大,所以最好還是不要使用這個方式。