vue使用render函数自定义标签动态导入js文件

问题背景

项目中需要引入国网思极地图,但是因为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当获取到值时在渲染。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值