vue远程组件
背景
曾经遇到过一种特殊的组件使用场景,在A工程中通过一个基础组件去拉取已构建好存放在CDN
中的组件JS
文件(远程组件),然后挂在到元素中,现在有空将其原理梳理成文。旁白:听说这类远程组件在低代码平台中有所使用。
远程组件核心
组件形式
远程组件应该以什么形式存在呢?
- 文件扩展名为
.vue
形式存在,但是.vue
文件浏览器是识别不了的,需要运行时转换,我们找到了http-vue-loader。先通过Ajax
获取内容,然后解析Template、CSS、Script
,输出一个JS
对象。 - 构建后的
JS
脚本存在。官网提供的vue-loader
,它会解析文件,提取每个语言块,如有必要会通过其它loader
处理,最后将他们组装成一个ES Module
,它的默认导出是一个Vue
组件选项的对象。
文章图片
怎么构建
这里我们采用
webpack
对button
组件进行构建:- 设置输出配置:
library: 'MyComponent',libraryTarget: 'umd'
,将构建生成的组件选项对象挂在到window.MyComponent
变量 - 不能配置
mini-css-extract-plugin
插件,因为该插件会将组件内部css
抽离 - 样式加载器需要最后需要加上
style-loader
,css-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原理探究
挂载方式
我们可以使用动态组件
来实现:文章图片
最终实现
远程组件项目中相关库版本说明:
"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
还不行)
远程
Button
组件
在将下面Button
组件构建后执行http-server --cors -p 8888
启动本地静态资源服务,配置跨域,以便远程基础组件请求Button.js
源文件// Button.vue
{{$attrs.text}}
.btn {
font-size: 16px;
color: #da2227;
}
注意事项:
如果
style
标签上添加scoped
属性,最后样式可能不会生效,因为远程组件元素上没有注入scopeId
,而样式.btn
私有化了,当然获取到的选项对象中存在scopeId
,你可以手动给远程组件元素加上scopeId
,本文没有这样做,是考虑到scoped
解决了样式私有化问题的同时也引入了新的问题—样式不易(可)修改,而很多时候,我们是需要对公共组件的样式做微调的。文章图片
文章图片
远程基础组件 这里我准备封装一个
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.$attrs
和 this.$listeners
向组件传递属性和监听器。再结合使用 inheritAttrs: false
,甚至可以将这些属性和监听器绑定到其他元素上而不根元素:在
Vue 3.x
的虚拟 DOM
中,事件监听器只是以on
为前缀的属性。因此,监听器被归纳为$attrs
的一部分,从而移除了$listerners
。参考:Vue 3 迁移策略笔记—— 第19节:移除 $listeners
最终效果
文章图片
项目地址:https://github.com/Revelation...
参考:
vue 远程加载sfc组件思路
你可能不知道的动态组件玩法
推荐阅读
- CVE-2020-16898|CVE-2020-16898 TCP/IP远程代码执行漏洞
- vue-cli|vue-cli 3.x vue.config.js 配置
- 2020-04-07vue中Axios的封装和API接口的管理
- VueX--VUE核心插件
- 动态组件与v-once指令
- vue组件中为何data必须是一个函数()
- 用npm发布一个包的教程并编写一个vue的插件发布
- (六)Component初识组件
- Spring|Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件
- vuex|vuex 基础结构