远程加载.vue组件并使用

7,848 阅读1分钟

平时咱们都是在项目写好组件使用,那么如何从服务端加载.vue的文件用并在项目中跑起来呢? 大致分以下三步。

  1. http请求远程文件。
  2. 将文本组件转换为vue响应式组件
  3. component is渲染

vue3-sfc-loader简介

Vue3/Vue2单文件组件加载器 在运行时从html/js动态加载.vue文件,不需要node.js环境,不需要(webpack)构建步骤vue3-sfc-loader

以vue2为例

vue2的使用路径在/dist目录下

import { loadModule } from 'vue3-sfc-loader/dist/vue2-sfc-loader.js'

loadModule使用

loadModule第一个参数接收string类型的路径,第二个参数 options配置 方法返回Promise可使用then回调处理

loadModule('/main.vue', options).then()

封装动态远程加载组件

template

使用 is 动态渲染加载的组件 透传属性 v-bind="$attrs",事件响应处理 v-on="$listeners"

<template>
  <div class="async-component">
    <component :is="remote" v-if="remote" v-bind="$attrs" v-on="$listeners" />
  </div>
</template>

动态加载

  • moduleCache将Vue对象传下去(强制性)
  • getFile 获取远程文件逻辑,此处使用原生 fetch API请求
  • addStyle 创建并添加样式
const com = await loadModule(url, {
    moduleCache: {
      vue: Vue,
    },
    // 获取文件
    async getFile (url) {
      const res = await fetch(url)
      if (!res.ok) {
        throw Object.assign(new Error(`${res.statusText}  ${url}`), { res })
      }
      return {
        getContentData: asBinary => (asBinary ? res.arrayBuffer() : res.text()),
      }
    },
    // 添加样式
    addStyle (textContent) {
      const style = Object.assign(document.createElement('style'), { textContent })
      const ref = document.head.getElementsByTagName('style')[0] || null
      document.head.insertBefore(style, ref)
    },
  })

效果展示

示例中在进入界面后从服务器请求获取到了.vue组件文本内容,再使用上述方法转换为真正的vue组件,并使用 is渲染

tutieshi_640x544_10s.gif

完整代码

<template>
  <div class="async-component">
    <component :is="remote" v-if="remote" v-bind="$attrs" v-on="$listeners" />
  </div>
</template>

<script>
import Vue from 'vue/dist/vue.common.js'
import { loadModule } from 'vue3-sfc-loader/dist/vue2-sfc-loader.js'
export default {
  name: 'AsyncComponent',
  inheritAttrs: false,
  props: {
  // 组件url
    url: {
      type: String,
      required: true,
    },
  },
  data () {
    return {
      remote: null,
    }
  },
  watch: {
    url: {
      immediate: true,
      handler (url) {
        url && this.load(url)
      },
    },
  },
  methods: {
    // 加载
    async load (url) {
      const com = await loadModule(url, {
        moduleCache: {
          vue: Vue,
        },
        // 获取文件
        async getFile (url) {
          const res = await fetch(url)
          if (!res.ok) {
            throw Object.assign(new Error(`${res.statusText}  ${url}`), { res })
          }
          return {
            getContentData: asBinary => (asBinary ? res.arrayBuffer() : res.text()),
          }
        },
        // 添加样式
        addStyle (textContent) {
          const style = Object.assign(document.createElement('style'), { textContent })
          const ref = document.head.getElementsByTagName('style')[0] || null
          document.head.insertBefore(style, ref)
        },
      })
      this.remote = com
    },
  },
}
</script>
<style>
.async-component {
  width: 100%;
}
</style>

使用

问:为什么我的url只是相对路径?

答:因为config中对/vue的前缀做了开发代理

<template>
  <div>
    <vueComponent url="/vue/index.vue" />
  </div>
</template>