目录
问题总括
同一项目不同地市地图使用 js 文件不同如何解决_动态添加 script 标签加载js文件(问题:在地图页刷新页面时,地图加载失败。原因:script标签加上了,但是 js 没有加载完成就加载地图)
需求背景
多个地址用同一项目,但是项目中地图使用的 js 地址不同
版本1 - script 标签添加
在
index.html
中 通过script
引入各个地市用到地图的 js 文件,需要给哪个地市打包,就打开哪个 script ,其余都注释掉
- 【缺点】每次打包都需手动打开 js 引入代码,操作繁琐
版本2 - 动态加载
通过原生 js 操作 dom 动态添加 script 标签:
- 通过判断系统参数中配置的数据,判断需要加载哪个 js (说明:
系统参数中配置的数据
是可在每个地市配置的公共参数)
版本3(最终解决) - 版本2出现的问题及解决
问题
版本2出现的问题,刷新页面时:
- 刷新的当前页面不是地图页面,刷新后跳转到地图页面 - 地图没有问题
- 刷新的当前页面是地图页面,刷新后当前页面地图加载失败,且控制台报错
原因分析
已经在地图使用前引入 地图的 js 且 script 标签页成功添加,为什么还会报错?
【原因】
- 虽然 js 已经成功引入,但是 js 文件加载时需要时间的(下图可以看出地图 就是加载需要
198毫秒
),可能 js 文件还没有加载完成,就去调用了地图相关方法,所以会报错
【测试一下是不是以上原因】
- 延迟 1000ms 后再进行地图初始化加载,
- 通过下图可以看出地图加载成功,确实是以上原因造成的地图加载失败,
解决
延迟 N毫秒
可行性分析
【延迟
N毫秒
可行性分析】
- 那我们只需要延迟一下初始化地图的调用就可以,
- 但是我们直接固定 N毫秒 延迟后后再初始化地图的方法是不可行的,
- 因为地图 js 加载时间我们是不知道的,
- 如果 js 加载时间大于我们延迟初始化的时间,
- 那地图也是会初始化失败的
【结论】不可行
【最终解决】借助 js 加载完成事件 onload + 页面刷新事件 onbeforeunload + ((本地缓存 localStorage + 延时 setInterval) || (store 存数据 + watch 侦听))
【解决思路】
App.vue
页面:地图 js 加载完成,本地缓存TXMapISLoad
作为 js加载完成标识
→ 地图页面通过判断取本地缓存 TXMapISLoad 的值是否为 ‘true’ 来判断地图 js 是否加载完成,以便判断是否初始化地图地图
页面:
- 页面刷新 -
移除
本地TXMapISLoad 数据缓存
- 每
100毫秒
取一次本地TXMapISLoad 数据
,值为'true'
时初始化地图
并清除定时器
- 本地缓存可换成 store 存数据、EventBus等其他方式
附:解决代码
1. 本地缓存+延时器解决
App.vue
<script>
export default {
// ...
mounted() {
/** ** 动态添加地图地址 start ****/
console.log('"添加地图"----', '添加地图开始')
let url = ''
if (this.sysConfigData.defFzjg === '浙X') {
url = 'http://xx.xx.xx.xx:xxxx/api/gljs?v=1.exp&libraries=visualization'
} else {
url = 'https://map.qq.com/api/gljs?v=2.exp&key=腾讯地图取key'
}
var script = document.createElement('script')
script.type = 'text/javascript'
script.src = url
script.onload = () => {
/**
* 地图 js 加载完成,本地缓存 TXMapISLoad 作为 js 加载完成标识:
* 地图页面通过判断取本地缓存 TXMapISLoad 的值是否为 'true' 来判断地图 js 是否加载完成,以便判断是否初始化地图
*/
localStorage.setItem('TXMapISLoad', 'true')
}
document.head.appendChild(script)
console.log('"添加地图"----', '添加地图完成')
/** ** 动态添加地图地址 end ****/
},
}
</script>
地图页面
@/components/TXMap/index.vue
<script>
export default {
// ...
mounted() {
// this.initMap()
/** ** 页面刷新 - 移除本地 TXMapISLoad 数据缓存 ****/
window.onbeforeunload = () => {
localStorage.removeItem('TXMapISLoad')
}
/**
* 页面销毁 - 移除本地 TXMapISLoad 数据缓存
* 此处不考虑页面销毁,
* 因为如果当前页面销毁时清除 TXMapISLoad 本地数据,
* 那要是跳转到别的地图页面,地图也加载不出来了,
* 所以此处不考虑销毁
*/
// this.$once('hook:beforeDestroy', () => {
// localStorage.removeItem('TXMapISLoad')
// })
/**
* 每 100毫秒 取一次本地 TXMapISLoad 数据,
* 值为 'true' 时 初始化地图 并 清除定时器
*/
const timer = setInterval(() => {
const TXMapISLoad = localStorage.getItem('TXMapISLoad')
if (TXMapISLoad === 'true') {
this.initMap()
clearInterval(timer)
}
}, 100)
},
}
</script>
2. store存数据 + watch 侦听解决(最优解)
App.vue
<script>
export default {
// ...
mounted() {
/** ** 动态添加地图地址 start ****/
console.log('"添加地图"----', '添加地图开始')
let url = ''
if (this.sysConfigData.defFzjg === '浙X') {
url = 'http://xx.xx.xx.xx:xxxx/api/gljs?v=1.exp&libraries=visualization'
} else {
url = 'https://map.qq.com/api/gljs?v=2.exp&key=腾讯地图取key'
}
var script = document.createElement('script')
script.type = 'text/javascript'
script.src = url
script.onload = () => {
/**
* 地图 js 加载完成,store 中存数据 TXMapISLoad 作为 js 加载完成标识:
* 地图页面通过侦听 store 中 TXMapISLoad 判断其值是否为 'true' 来判断地图 js 是否加载完成,以便判断是否初始化地图
*/
this.$store.commit('SET_TXMapISLoad', 'true')
}
document.head.appendChild(script)
console.log('"添加地图"----', '添加地图完成')
/** ** 动态添加地图地址 end ****/
},
}
</script>
地图页面
@/components/TXMap/index.vue
<script>
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
...mapGetters(['TXMapISLoad'])
},
watch: {
TXMapISLoad() {
/**
* 侦听 TXMapISLoad 改变
* TXMapISLoad 值为 true 时初始化地图
*/
if (this.TXMapISLoad === 'true') {
this.initMap()
}
}
},
mounted() {
// this.initMap()
/** ** 页面刷新 - 将store中数据 TXMapISLoad 值置为 false ****/
window.onbeforeunload = () => {
this.$store.commit('SET_TXMapISLoad', 'false')
}
},
}
</script>
@/store/modulex/app.js
const state = {
// ...
TXMapISLoad: '' // 腾讯地图是否加载完成
}
const mutations = {
// ...
SET_TXMapISLoad: (state, payload) => {
state.TXMapISLoad = payload
}
}
const actions = {
// ...
}
const getters = {
// ...
TXMapISLoad: (state) => state.TXMapISLoad
}
export default {
state,
mutations,
actions,
getters
}
@/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
plugins: [createPersistedState()]
})
export default store
【总结 - 注意】
- 关键点 - js加载是有时间的,要在 js 加载完成后再去初始化地图