vue远程组件

背景 曾经遇到过一种特殊的组件使用场景,在A工程中通过一个基础组件去拉取已构建好存放在CDN中的组件JS文件(远程组件),然后挂在到元素中,现在有空将其原理梳理成文。旁白:听说这类远程组件在低代码平台中有所使用。
远程组件核心 组件形式
远程组件应该以什么形式存在呢?

  • 文件扩展名为.vue形式存在,但是.vue文件浏览器是识别不了的,需要运行时转换,我们找到了http-vue-loader。先通过Ajax获取内容,然后解析Template、CSS、Script,输出一个JS对象。
  • 构建后的JS脚本存在。官网提供的vue-loader,它会解析文件,提取每个语言块,如有必要会通过其它 loader 处理,最后将他们组装成一个 ES Module,它的默认导出是一个 Vue组件选项的对象。
vue远程组件
文章图片

怎么构建
这里我们采用webpackbutton组件进行构建:
  • 设置输出配置:library: 'MyComponent',libraryTarget: 'umd',将构建生成的组件选项对象挂在到window.MyComponent变量
  • 不能配置mini-css-extract-plugin插件,因为该插件会将组件内部css抽离
  • 样式加载器需要最后需要加上style-loadercss-loader只会把css模块加载到JS代码中,生成一个包含css代码的数组,style-loader的作用是使用这个数组,将css注入到style标签中
// webpack.config.js const path = require('path'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const { VueLoaderPlugin } = require('vue-loader'); const webpack = require('webpack'); module.exports = { mode: 'production', // 生产环境构建会启动压缩 entry: { 'button': './src/components/Button/index.vue', }, output: { filename: 'js/[name].js', path: path.resolve(__dirname, './dist'), library: 'MyComponent', libraryTarget: 'umd' }, resolve: { extensions: ['.js', '.vue'], }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }, { test: /\.vue$/, loader: 'vue-loader', options: { esModule: false } }, { test: /\.(sa|sc|c)ss$/, use: [ 'style-loader', 'css-loader', 'sass-loader' ] }, { test: /\.(jpg|jpeg|png|gif)$/, use: ['url-loader'] } ] }, plugins: [ new webpack.ProgressPlugin(), // 打印构建进度 new VueLoaderPlugin(), new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['dist'] }), ], }

参考:
css-loader style-loader原理探究
挂载方式
我们可以使用动态组件来实现:
vue远程组件
文章图片

最终实现
远程组件项目中相关库版本说明:
"dependencies": { "@babel/core": "^7.15.5", "@vue/compiler-sfc": "^3.2.11", "babel-loader": "^8.2.2", "clean-webpack-plugin": "^4.0.0", "css-loader": "^6.2.0", "sass": "^1.40.0", "sass-loader": "^12.1.0", "style-loader": "^3.2.1", "url-loader": "^4.1.1", "vue": "3.2.2", "vue-loader": "^16.5.0", "webpack": "^5.52.1", "webpack-cli": "^4.8.0" }

注意:
  • vue3不用vue-template-compiler了,用的@vue/compiler-sfc,另外安装vue-loader要指定16以上的版本
  • 一开始我是用的vue2搭建远程组件工程并构建,但是在远程基础组件中使用时控制台报错createElementBlock is not a function,因为我远程基础组件工程中用的是vue3,而远程组件工程用的vue2,因此需要将vue2升级到vue@3.2.2(好像升级到3.0.0还不行)
参考:https://github.com/element-pl...
远程Button组件 在将下面Button组件构建后执行http-server --cors -p 8888启动本地静态资源服务,配置跨域,以便远程基础组件请求Button.js源文件
// Button.vue .btn { font-size: 16px; color: #da2227; }

注意事项:
如果style标签上添加scoped属性,最后样式可能不会生效,因为远程组件元素上没有注入scopeId,而样式.btn私有化了,当然获取到的选项对象中存在scopeId,你可以手动给远程组件元素加上scopeId,本文没有这样做,是考虑到scoped解决了样式私有化问题的同时也引入了新的问题—样式不易(可)修改,而很多时候,我们是需要对公共组件的样式做微调的。
vue远程组件
文章图片

vue远程组件
文章图片

远程基础组件 这里我准备封装一个RemoteBaseComponent组件,用来请求并挂载button组件(远程组件),请求的方式有很多种,详细如下:
动态script加载 首先需要构建.vue文件,然后通过动态Script去加载远端JS
// RemoteBaseComponent.vue

局部注册组件 局部注册一个含有script标签的组件,监听onload事件,当加载完成后触发自定义事件将button组件选项对象赋值给动态组件
// RemoteBaseComponent.vue

注意事项:
如果控制台报错:Uncaught TypeError: createElement is not a function,这是因为在 vue 2 中,我们执行以下操作来创建渲染函数:
export default { render(createElement ) { // createElementcould be written h return createElement('div') } }

Vue 3 中:
import { h } from 'vue'export default { render() { return h('div') } }

参考:
Vue 引入远程 JS 文件
using vue-chartjs in vue 3 : createElement is not a function
Ajax请求 首先需要构建.vue文件,然后通过ajax去加载远端JS,获取到远程组件源代码后使用new Function或者eval执行源代码,将选项对象赋值给动态组件
// RemoteBaseComponent.vue

SystemJS SystemJS 是一个可挂钩的、基于标准的模块加载器。它提供了一个工作流,其中为浏览器中原生 ES 模块的生产工作流(如 Rollup 代码拆分构建)编写的代码可以转换为 System.register 模块格式,以便在不支持原生模块的旧浏览器中工作,几乎可以运行-本地模块速度,同时支持顶级等待、动态导入、循环引用和实时绑定、import.meta.url、模块类型、导入映射、完整性和内容安全策略,并在旧浏览器中兼容回 IE11
【vue远程组件】首先在项目中引入systemjs
- 锐客网

然后在远程基础组件中改用System.import获取远程组件源代码

组件通讯 在 Vue 2.x 中,我们可以通过this.$attrsthis.$listeners 向组件传递属性和监听器。再结合使用 inheritAttrs: false ,甚至可以将这些属性和监听器绑定到其他元素上而不根元素:
Vue 3.x 的虚拟 DOM 中,事件监听器只是以on为前缀的属性。因此,监听器被归纳为$attrs 的一部分,从而移除了$listerners
参考:Vue 3 迁移策略笔记—— 第19节:移除 $listeners
最终效果

vue远程组件
文章图片

项目地址:https://github.com/Revelation...
参考:
vue 远程加载sfc组件思路
你可能不知道的动态组件玩法

    推荐阅读