共计 6285 个字符,预计需要花费 16 分钟才能阅读完成。
前言
demo 代码仓库: https://github.com/ericlee33/…
如果有帮忙到的话,欢送点个 star ⭐️ & follow https://github.com/ericlee33
背景
业务开发中遇到的问题
笔者当初在中台部门工作,素日业务开发时,常常遇到一个场景,业务方会提需要过去,须要 中台方去帮助定制开发业务插件,以此来补齐业务方须要的性能。
作为前端开发,如果不加以思考,很容易就会陷入到业务之中,实际上是业务方本人的需要,然而却须要咱们来帮忙开发。
在这种时候,咱们须要思考一种计划,来把本人从 需要黑洞 中抽离进去。为什么不让业务方本人来作为插件开发者,本人开发呢?
有的同学可能会问,间接 让需求方在中台我的项目仓库中去开发 不就好了?
可不可以业务方自行开发呢?
听起来如同是一种方法,然而当我的项目构造很宏大,比方为多 packages
组成的 monorepo
我的项目,插件开发者就会遇到如下问题:
- 很难疾速 理清 中台我的项目的 代码逻辑
- 开发前,须要先装置我的项目依赖、本地启动我的项目,尤其是在我的项目宏大的状况下,本地首次编译我的项目,可能须要 20 分钟甚至更多,对业务方同学来说,几乎是苦楚
- 中台我的项目会有 代码标准 ,插件开发者一上来也不肯定能间接满足我的项目设立的代码标准,
MR
的时候须要中台前端同学帮忙做Code Review
,也会耗费中台同学的精力 - 插件开发者的代码逻辑如果有问题,可能会导致中台我的项目 线上白屏
指标
整体流程
业务方自行开发过程中,实际上会存在一个很大的痛点,插件开发者不得不 深刻到咱们的我的项目中,去理解外部实现细节 。并且 须要很长时间编译我的项目能力启动
⭐️ 如果,有一种形式,能提供给插件开发者 插件模板 ,并且能用咱们平台的 线上环境去近程加载业务方同学本地开发的模板代码,让业务方进行本地开发插件,这样是不是就省去了让业务方了解我方平台代码的老本?
对流程进行拆解
让咱们整顿一下,如果须要实现下面这个流程,咱们须要 以下能力:
在 React
我的项目中,实现近程组件调用
有人可能会问,React
不是能够通过 import()
语法去动静调用组件吗,这样不能够吗?
这种状况会遇到一个问题,像 react
是只容许单实例的,插件组件打包时候,须要配置externals
,防止两次调用,这样的话就会呈现依赖无奈失常注入的问题。如果有同学感兴趣这块的话后续我会持续写一篇对于这块的文章。
线上环境调试本地调试代码
咱们如果能做到通过某种形式,代理到线上环境接口,劫持接口返回值,来注入本地近程组件对应 url
地址 ,是不是就相当于实现了 线上环境调试本地调试代码 这种 黑魔法?
插件模板生成
这里为了让开发者体验更好,咱们能够开发一个脚手架,依据开发者不同的要求,去生成不同的模板
插件产物打包上传
这里不细讲,如果感兴趣我后续能够再更一篇文章,思路是有以下两种:
- 实现一个 插件开发者后盾,进行插件对立治理、版本控制、上传操作
- 实现 VSCode 插件,让开发者在
VSCode
中,就能够将全流程闭环
技术计划
针对以上的想法,我进行了技术计划调研。
近程组件引入
在平台我的项目中,须要引入对打包好的近程组件进行动静加载
这里咱们应用 remote-component
这个 npm
包来解决这个问题
github 地址:https://github.com/Paciolan/r…
什么是近程组件
这里援用 npm
包文档的阐明
近程组件在运行时从
URL
加载。它的应用形式与其余React
组件雷同
近程组件应用办法
const url =
"https://raw.githubusercontent.com/Paciolan/remote-component/master/examples/remote-components/HelloWorld.js";
const HelloWorld = ({name}) => <RemoteComponent url={url} name={name} />;
const Container = (
<>
<HelloWorld name="Remote" />
</>
);
线上环境调试本地调试代码
为了达到这个目标,咱们须要实现以下两点:
- 搭建代理服务器
- 劫持浏览器的申请,让浏览器的申请全部打到代理服务器上
搭建代理服务器
代理服务器咱们应用 Anyproxy
包
应用文档
浏览器代理插件
咱们应用 SwitchOmega
,它能够对浏览器进行proxy
代理
插件地址:
https://chrome.google.com/web…
插件模板脚手架
咱们能够应用 commander
/inquirer
/chalk
等npm
包,设计开发脚手架工具
插件产物打包上传
插件开发者后盾
如果是这样去实现的话,那咱们须要在页面中提供以下性能:
- 新增插件
- 删除插件
- 插件产物上传
- 插件公布
- 插件版本回退
VSCode 插件
VSCode
插件开发实现的性能相似于后盾,然而能够免去在后盾登录,以及本地打包,再拖拽产物到后盾再上传的繁琐流程
插件开发 API
文档:https://code.visualstudio.com…
流程图
开发者视角
<div align=”center”>
<img width=”200″ src=”https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/98d682e2306346bdb5df6386a9f13f28~tplv-k3u1fbpfcp-watermark.image?” />
</div>
平台维护者视角
<div align=”center”>
<img width=”400″ src=”https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6368c09e1f8f4094b023db1a8f2d05fe~tplv-k3u1fbpfcp-watermark.image?” />
</div>
具体实现
我的项目构造
这里咱们用 pnpm
先搭一个 最简骨架 ,蕴含咱们实现 黑魔法 所需的各个子项目
.
├── README.md
├── package.json
├── packages
│ ├── anyproxy-server // 代理服务器,端口 11111,劫持浏览器申请
│ ├── egg-server // 后端服务,这里是为 demo 提供一个模仿的接口,供代理服务器进行代理,注入本地模板我的项目打包产物 js 对应的 url
│ ├── platform // 平台我的项目
│ └── plugin-page // 插件我的项目
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json
创立一个后盾服务
为了展现 demo 成果,咱们启动一个 egg
的小后盾,只提供给平台我的项目一个 /api/plugin_detail
接口,用于获取近程组件 url
,咱们临时把返回的data
写死为 {}
,后续近程组件的url
会通过代理服务器劫持提供,
这样就相当于是去模仿实现,代理线上接口调试本地代码了
import {Service} from 'egg';
/**
* Test Service
*/
export default class Test extends Service {
/**
* sayHi to you
* @param name - your name
*/
public async sayHi() {
return {
code: 0,
data: {},
msg: 'ok',
};
}
}
开发代理服务器包
这里我间接提供代理服务器 外围代码 ,咱们设置代理服务器运行在本地11111
端口,这里咱们去代理后盾提供的 /api/plugin_detail
接口,在返回时,减少注入本地模板我的项目打包好的产物 url
,这里咱们的url
是http://localhost:9001/main.bundle.js
,后续会解说这个 url
是怎么来的
const AnyProxy = require('anyproxy');
const rule = require('./rule/index');
const DEFAULT_PORT = 11111;
const proxyServer = new AnyProxy.ProxyServer({
rule: {beforeSendResponse: (requestDetail, responseDetail) => {if (requestDetail.url.includes('/api/plugin_detail')) {const data = JSON.parse(responseDetail.response.body.toString());
data.data.url = 'http://localhost:9001/main.bundle.js';
responseDetail.response.body = JSON.stringify(data);
return responseDetail;
} else {return responseDetail;}
},
},
port: DEFAULT_PORT,
throttle: 10000,
forceProxyHttps: true,
wsIntercept: false, // 不开启 websocket 代理
silent: false,
});
proxyServer.start();
配置浏览器代理插件
装置好浏览器代理插件之后,咱们启动插件,点击 新增 new profile
,咱们轻易起个名字,就叫test_local
, 设置代理到本地代理服务器的端口 ,这里咱们应用下面设置的11111
端口。进行完这一步之后,咱们就能将浏览器的申请打到咱们本地代理服务器的上了
留神,要在
Bypass List
中配置<-loopback>
,不然代理不到本地接口
到这里,前置工作就筹备好了,上面咱们开始开发平台我的项目,和插件页面
FAQ
- 如果代理网站时,网站不被信赖,须要信赖
anyproxy
的证书,不同零碎证书地位不同,例如MacOS
在如下目录./.anyproxy/certificates/rootCA.crt
开发插件
写一个 demo 插件页面
咱们首先写一个最简插件模板,这里咱们间接写死,理论能够用脚手架生成一个插件我的项目模板进去
import React from 'react';
import './test.css';
const Plugin: React.FC<{}> = () => {
return (
<div
style={{
color: 'red',
fontSize: '32px',
}}
>
这个是近程加载的插件组件
</div>
);
};
export default Plugin;
webpack 配置
这里我抽离几行外围配置,次要要留神 libraryTarget
需为 commonjs
格局,以及咱们须要配置 externals
,防止打包react
到插件产物中。
为了 demo 能跑起来,咱们给 devServer
配置跨域头为 *
,并且运行到9001
端口,这样本地插件产物 js 文件 的地址就会是http://localhost:9001/main.bundle.js
output: {filename: '[name].bundle.js',
libraryTarget: 'commonjs',
},
externals: {react: 'react',},
devServer: {
hot: true,
port: 9001,
headers: {'Access-Control-Allow-Origin': '*',},
},
在平台我的项目中,配置近程组件
注入近程组件所需的依赖
因为近程组件咱们设置了 externals
属性,防止打包 react
,咱们在平台我的项目中,须要提供react
依赖给插件模板,这样插件模板才能够援用到 react
包的依赖
/**
* Dependencies for Remote Components
*/
module.exports = {
resolve: {react: require("react")
}
};
webpack 配置
这里我粘贴局部代码,次要外围点是咱们须要在 webpack.config.js
中增加一个 Webpack
别名,这样 RemoteComponent
就能够加载这个文件。
module.exports = {
resolve: {
alias: {"remote-component.config.js": __dirname + "/remote-component.config.js"}
}
};
平台调用近程组件形式
这里我调用咱们小后盾提供的 /api/plugin_detail
接口,自身接口没有返回 url
地址,然而当咱们开启代理服务器时,就能够获取到本地插件组件打包产物的地址了
import React, {useEffect, useState} from 'react';
import {RemoteComponent} from '@paciolan/remote-component';
import {Card} from 'antd';
interface IPlatFormEntryProps {}
const PlatFormEntry: React.FC<IPlatFormEntryProps> = () => {const [url, setUrl] = useState('');
const [loading, setLoading] = useState(true);
useEffect(() => {setTimeout(async () => {
const rawRes = await fetch('http://localhost:7001/api/plugin_detail', {method: 'GET',});
const res = await rawRes.json();
const url = res.data.url;
setLoading(false);
setUrl(url);
}, 1000);
}, []);
const HelloWorld = (props) => <RemoteComponent url={url} {...props} />;
return (<Card title={'主利用启动结束'}>
{loading ? <div>Loading,正在加载近程组件.....</div> : <HelloWorld />}
</Card>
);
};
export default PlatFormEntry;
整体流程联调以及成果展现
具体实现能够参考我的项目源码,我曾经将源码贴在文章最下方
启动浏览器代理插件
咱们抉择咱们刚刚配置好的 test_local
配置项
启动咱们 monorepo
内的 4 个子我的项目
进行平台我的项目目录,启动平台我的项目
cd packages/platform
pnpm dev
进行插件我的项目目录,启动插件我的项目
cd packages/plugin-page
pnpm dev
进行代理服务器我的项目目录,启动代理服务
cd packages/anyproxy-server
pnpm dev
进行后端我的项目目录,启动后端我的项目
cd packages/egg-server
pnpm dev
查看成果
拜访平台我的项目首页,即可看到成果
http://localhost:9000
期待接口返回后,即可加载出近程组件内容
demo 代码仓库: https://github.com/ericlee33/…
如果有帮忙到的话,欢送点个 Star ⭐️ & follow https://github.com/ericlee33
欢送关注 前端江湖行 公众号,我会分享很多乏味的技术文章,心愿帮忙更多的人深耕前端技术,紧跟技术潮流。