问题背景
项目中需要引入国网思极地图,但是因为pc端的项目一般都是部署到内网。该项目要同时部署到27个网省,虽然只有几个网省有引入思极地图的需求。但是由于内网的原因申请的思极地图的网址以及key和密钥每个网省都不相同。
解决思路
方法一:为了满足不同网省需要引入不同地址的思极地图,最开始采用的针对每个网省打不同的包,显然就造成了发包压力。
方法二:采用动态js引入,但是原始的script标签不支持这种需求。当时想到曾在梁灏的vue.js实战一本中有写道关于render函数的用法,可以满足这一需求。但是js配置到哪里从哪里取?因为项目的一些配置配置在了consul注册中心中后端获取到返回给前端。本次解决我也是采用配置到consul的注册中心里面。有的网省采用的阿里的nacos注册中心。其原理大同小异不在展开过多的开展。
知识加油站
Render函数
平常我们写 template里面所使用模板HTML语法组建页面的,其实在 vue 中都会编译成 render 函数,因为vue 中采用的是 虚拟DOM 所以拿到template模板时也要转译成 VNode(virtual node 虚拟节点) 函数。
如果想了解更多可查看梁灏老师的vue.js实战这一本书
createElement参数
Render函数通过createElement参数来创建virtual Dom,Render函数可以实现动态配置js的主要原因是因为createElement的第二个参数’'数据对象" 。这里说到了createElement的参数那么就详细给大家介绍下参数都有那些:
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// 必须return上述其中一个。必填项。
'div',
// {Object}
//一个对应属性的数据对象。可选。
//可在template中使用
{
// (详情在下面介绍)
},
// {String | Array}
// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
[
createElement('h1', '加油少年们'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
}),
'bar'
]
)
第一个参数必选,可以是一个 HTM 标签,也可以是一个组件或函数;第二个是可选参数数据对象,在template 中使用。第三个是子节点,也是可选参数,用法一致。
对于第二个参数“数据对象”,具体的选项如下:
{
// 与 `v-bind:class` 的 API 相同,
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 普通的 HTML 特性
attrs: {
id: 'foo'
},
// 组件 prop
props: {
myProp: 'bar'
},
// DOM 属性
domProps: {
innerHTML: 'baz'
},
// 事件监听器在 `on` 属性内,
// 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
// 需要在处理函数中手动检查 keyCode。
on: {
click: this.clickHandler
},
// 仅用于组件,用于监听原生事件,而不是组件内部使用
// `vm.$emit` 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
// 赋值,因为 Vue 已经自动为你进行了同步。
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// 作用域插槽的格式为
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
slot: 'name-of-slot',
// 其它特殊顶层属性
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
// 那么 `$refs.myRef` 会变成一个数组。
refInFor: true
}
代码实现
1、首先新建了improtJs.js文件
// 导入外部js
import Vue from 'vue'
Vue.component('remote-script', {
render: function (createElement) {
var self = this;
return createElement('script', {
attrs: {
type: 'text/javascript',
src: this.src
},
on: {
load: function (event) {
self.$emit('load', event);
},
error: function (event) {
self.$emit('error', event);
},
readystatechange: function (event) {
if (this.readyState == 'complete') {
self.$emit('load', event);
}
}
}
});
},
props: {
src: {
type: String,
required: true
}
}
});
2、可以采用局部引入在哪里用到了就在那里引入,也可以采用全局引入,在main.js中引入。因为我用的地方比较多所以采用的全局引入
import '@/static/js/importJs.js';
3、页面调用
<template>
<remote-script v-if="$store.state.mapsrc" :src=$store.state.mapsrc></remote-script>
</template>
<script>
export default {
}
</script>
<style>
</style>
这里我获取参数配置的值如同我上面所说采用的从consul配置,后端获取后在返回给前端,拿到值后采用vuex来管理数据。这里用到了v-if的原因是因为开始引用后发现有时候可以引用有时候不生效。后来经过排查发现当标签remote-script渲染时$store.state.mapsrc还没有取到导致传了个空值。所以我添加的v-if当获取到值时在渲染。