Vue之如何动态渲染.vue文件

简介: Vue之如何动态渲染.vue文件

动态渲染.vue文件其实存在于很多地方,例如近期做的表单设计器就是其中一个,生成vue代码后,应用在其它地方。要求下载完vue文件在其它项目中引入即可使用。那么动态渲染.vue项目如何去做呢?


1. 我们需要一个模板页面,这个页面用来处理传递进来的代码,我们最后通过之前有说过的 extend 以及$mount 来进行实例化挂载。


那么,我们先第一步,创建一个动态渲染.vue的模板页面。


<!-- display.vue -->
<template>
  <div ref="display"></div>
</template>
<script>export default {
    props: {
      code: {
        type: String,
        default: ''
      }
    },
    data () {
      return {
        html: '',
        js: '',
        css: ''
      }
    },
  }
  </script>


在上述模板中,我们传入了一个code,这个code 指的就是外部传递给这个页面的动态.vue文件的内容。


那么,第二步我们则就是来处理传递进来的.vue文件里的内容(代码)。


2. 处理传入的.vue 文件内容

export default {
  methods: {
    getSource (source, type) {
      const regex = new RegExp(`<${type}[^>]*>`);
      let openingTag = source.match(regex);
      if (!openingTag) return '';
      else openingTag = openingTag[0];
      return source.slice(source.indexOf(openingTag) + openingTag.length, source.lastIndexOf(`</${type}>`));
    },
    splitCode () {
      const script = this.getSource(this.code, 'script').replace(/export default/, 'return ');
      const style = this.getSource(this.code, 'style');
      const template = '<div id="app">' + this.getSource(this.code, 'template') + '</div>';
      this.js = script;
      this.css = style;
      this.html = template;
    },
  }
}

在上述代码中,我们声明了两个方法,一个是 splitCode ,我们用来分隔传递进来的需要动态渲染的.vue文件的内容。在分割内容的时候为了方便,我们则单独封装了一个方法 getSource方法。这个方法接收两个参数。


source:.vue 文件代码,即 props: code;

type:分割的部分,也就是 template、script、style。

所以上述的splitCode的方法的目的就是,来分割出我们所需要的三个内容,分别为:模板文件(html)template,js ,css。


分割后,返回的内容不再包含  等标签,直接是对应的内容,在 splitCode 方法中,把分割好的代码分别赋值给 data 中声明的 html、js、css。


有两个细节需要注意:


1. vue的 <script> 部分一般都是以export default 开始的,但是在上述splitCode中,我们把它替换成了return。这里我们需要注意,分割完的代码,仍然是字符串。


2. 在分割的  外层套了一个 <div id="app"> ,这是为了容错,有时使用者传递的 code 可能会忘记在外层包一个节点,没有根节点的组件,是会报错的。


当我们准备好以上之后,就可以使用extend渲染组件了,但是在这之前,我们必须要注意到当前的this.js 是字符串,而extend的说法,接收的选项必须是一个对象类型。那么这时候我们就必须要先把this.js 转为一个对象。


我们在这里使用new Function 来进行转化,如下:


const sum = new Function('a', 'b', 'return a + b')
console.log(2, 6) // 8

new Function 的语法:

new Function ([arg1[, arg2[, ...argN]],] functionBody)

arg1, arg2, ... argN 是被函数使用的参数名称,functionBody 是一个含有包括函数定义的 JavaScript 语句的字符串。也就是说,示例中的字符串 return a + b 被当做语句执行了。


this.js 中是将 export default 替换为 return 的,如果将 this.js 传入 new Function 里,那么 this.js 就执行了,这时因为有 return,返回的就是一个对象类型的 this.js 了。


如果你还不是很理解 new Function,可以自行搜索其使用方法。除了 new Function,你熟悉的 eval 函数也可以使用,它与 new Function 功能类似。


所以目前的代码如下:


<template>
  <div ref="display"></div>
</template>
<script>import Vue from 'vue';
  export default {
    data () {
      return {
        component: null
      }
    },
    methods: {
      renderCode () {
        this.splitCode();
        if (this.html !== '' && this.js !== '') {
          const parseStrToFunc = new Function(this.js)();
          parseStrToFunc.template =  this.html;
          const Component = Vue.extend( parseStrToFunc );
          this.component = new Component().$mount();
          this.$refs.display.appendChild(this.component.$el);
        }
      }
    },
    mounted () {
      this.renderCode();
    }
  }
  </script>


extend 构造的实例通过 $mount 渲染后,挂载到了组件唯一的一个节点 <div ref="display">上。


到现在为止,html和 js 都有了,还剩下css,加载css 没有什么其它方法,就是创建一个<style>


标签,然后把css 放进去,再插入到前面的 <head> 中,这样css就被游览器给成功解析了,为了便于后面 this.code 变化或组件销毁时移除动态创建的 <style> 的标签,我们给每个style 标签都增加了一个随机ID 表示。


新建工具random_str.js 文件,并写入以下内容:

// 生成随机字符串
export default function (len = 32) {
  const $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
  const maxPos = $chars.length;
  let str = '';
  for (let i = 0; i < len; i++) {
    str += $chars.charAt(Math.floor(Math.random() * maxPos));
  }
  return str;
}

这个方法是从指定的 a-zA-Z0-9 中随机生成 32 位的字符串。

我们这时候可以补全之前的代码:

import randomStr from '../../utils/random_str.js';
export default {
  data () {
    return {
      id: randomStr()
    }
  },
  methods: {
    renderCode () {
      if (this.html !== '' && this.js !== '') {
        // ...
        if (this.css !== '') {
          const style = document.createElement('style');
          style.type = 'text/css';
          style.id = this.id;
          style.innerHTML = this.css;
          document.getElementsByTagName('head')[0].appendChild(style);
        }
      }
    }
  }
}

当 Display 组件销毁时,也要手动销毁 extend 创建的实例以及上面的 css:

export default {
  methods: {
    destroyCode () {
      const $target = document.getElementById(this.id);
      if ($target) $target.parentNode.removeChild($target);
      if (this.component) {
        this.$refs.display.removeChild(this.component.$el);
        this.component.$destroy();
        this.component = null;
      }
    }
  },
  beforeDestroy () {
    this.destroyCode();
  }
}

当 this.code 更新时,整个过程要重新来一次,所以要对 code 进行 watch 监听:

// display.vue,部分代码省略
export default {
  watch: {
    code () {
      this.destroyCode();
      this.renderCode();
    }
  }
}

以上就是我们动态渲染.vue文件的整个过程了。


此处各位伙伴还需要注意的是版本问题。


在vue2时候,有独立构建和运行时构建两种版本可以选择。


但是vue cli3 的时候,使用了vue.runtime.js 不允许编译template模板。因为我们在vue.extend构造实例的时候,用了 template 选项。所以会报错。所以,解决办法就是,对vue.config 做以修改。

// vue.config.js
module.exports = {
    runtimeCompiler: true
}

而它的意思就是,是否使用包含运行时编译器的Vue构建版本,设置为true后就可以在vue组件中使用 template选项了。

目录
相关文章
|
3天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
3天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
3天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
3天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
3天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
|
4天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
3天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
|
JavaScript 索引
VUE文件的创建
第一步创建完VUE 项目之后 可以根据自己的需求 创建自己的文件目录(下图是我的文件目录,当然每个人的创建目录的风格也是不同的所以不做严格的规范) 模块文生成    components文件夹下创建  模块文件名字.
801 0
|
4天前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
9天前
|
JavaScript
Vue基础知识总结 4:vue组件化开发
Vue基础知识总结 4:vue组件化开发