feat(renderer): 悦码渲染器增加 sentry 做异常监听

This commit is contained in:
wangxuefeng 2025-03-20 20:09:49 +08:00
parent 22145b3893
commit 64b59a06a3
10 changed files with 408 additions and 1 deletions

2
.npmrc
View File

@ -1,4 +1,4 @@
registry = "https://registry.npmmirror.com" registry = https://registry.npmmirror.com
@sy:registry=http://sy-registry.shiyue.com @sy:registry=http://sy-registry.shiyue.com
public-hoist-pattern[]=husky public-hoist-pattern[]=husky
public-hoist-pattern[]=eslint public-hoist-pattern[]=eslint

View File

@ -0,0 +1,125 @@
# 低代码渲染器接入指南
## 1. 概述
- 1.1 什么是低代码渲染器
- 1.2 核心功能和优势
- 1.3 适用项目
- vue2
- vue3
- react
- 其他框架请联系伊利sy或王雪峰sy3570添加适配器
## 2. 环境要求
- node >= 20
- vue2 > 2.7.0
- vue3 > 3.5.0
- react > 18.0.0
## 3. 准备工作
1.npmrc 配置文件增加镜像指定sy 组件库走 shiyue 源,其他走淘宝镜像
```bash
registry=https://registry.npmmirror.com
@sy:registry=http://sy-registry.shiyue.com
```
2. node 至少要升级为 20 以上的版本
```bash
nvm install 20
nvm use 20
```
若不可行,请查看 node 官网历代大版本的 lts 版本(长期维护),自行安装,查看是否能正常渲染,选取稳定渲染悦码的那一个 node 版本作为开发环境
## 5. 接入方式
### vue2 项目接入
```bash
pnpm i @sy/wujie-vue2-renderer-adapter@latest --save
# or 或者
npm i @sy/wujie-vue2-renderer-adapter@latest --save
```
### vue3 项目接入
```bash
pnpm i @sy/wujie-vue3-renderer-adapter@latest --save
# or 或者
npm i @sy/wujie-vue3-renderer-adapter@latest --save
```
### react 项目接入
```bash
pnpm i @sy/wujie-react-renderer-adapter@latest --save
# or 或者
npm i @sy/wujie-react-renderer-adapter@latest --save
```
## API 参考
- 6.1 Props 参数详解
- 6.2 事件监听与回调
- 6.3 实例方法
- 6.4 拦截器使用
## 7. 高级用法
- 7.1 自定义主题
- 7.2 数据交互与通信
- 7.3 插件扩展
- 7.4 微前端降级处理
## 8. 故障排除
- 8.1 常见错误及解决方案
- 8.2 日志与调试
- 悦码的日志输出一般在控制台中有 `console.ts 40` 的源码位置标记
- 8.3 兼容性问题处理
- renderer-adapter 是默认使用无界降级的 iframe 加载悦码渲染器应用的,你如果希望悦码与你的页面有更深的上下文结合和交互,你可以手动设置 `degrade``false`,这将开启高级渲染,但有一定的兼容性问题,如果需要在 C 端运行业务,我建议你采用默认的渲染模式
```vue
<script setup>
import { ref } from 'vue';
import { WujieVue3RendererAdapter } from '@sy/wujie-vue3-renderer-adapter';
</script>
<template>
<!-- 高级渲染模式,与原页面深度共享 document 与内存上下文 -->
<WujieVue3RendererAdapter
:degrade="false"
fileId="test123"
projectId="test123"
/>
<!-- 默认降级渲染模式,使用无界 iframe 加载悦码渲染器应用 -->
<!-- 缺点在于弹窗与遮罩层会约束在 iframe 空间内,无法与原页面共享 document但无界的 iframe 可以与原页面共享内存上下文 ,这是比传统 iframe 更优的解决方案 -->
<WujieVue3RendererAdapter fileId="test123" projectId="test123" />
</template>
```
- 8.4 性能问题诊断
- 悦码渲染器内部使用了 sentry 上报错误,你可以跟 徐川sy0182 申请对应警告邮箱的使用权,获取报错邮件
![sentry 邮箱](https://cms-1256453865.cos.ap-shanghai.myqcloud.com/y-code/20250320/企业微信截图_20250320192100.png)
## 9. 最佳实践
- 9.1 推荐的项目结构
- 9.2 安全性建议
- 9.3 性能优化建议
- 9.4 版本升级策略
## 10. 示例
- 10.1 基础示例
- 10.2 完整应用示例
- 10.3 实际案例展示
## 11. 常见问题 (FAQ)
## 12. 联系支持与反馈渠道

View File

@ -17,6 +17,15 @@ const app = createApp(App);
Sentry.init({ Sentry.init({
app, app,
dsn: 'https://5bcf1344794fea64fc5e5fe7da4821c1@o4508962650783744.ingest.de.sentry.io/4508962653143120', dsn: 'https://5bcf1344794fea64fc5e5fe7da4821c1@o4508962650783744.ingest.de.sentry.io/4508962653143120',
integrations: [
// 异常路径回放
Sentry.replayIntegration(),
// 异常端信息追踪
Sentry.browserTracingIntegration(),
],
// Session Replay
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
replaysOnErrorSampleRate: 1, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
}); });
// 批量注册组件 // 批量注册组件

View File

@ -0,0 +1,3 @@
export function test() {
console.log('test');
}

View File

@ -0,0 +1 @@
export * from './alert';

View File

@ -0,0 +1,39 @@
{
"name": "@sy/wujie-vue3-renderer-adapter",
"version": "1.0.0-alpha.2",
"description": "wujie-vue3 ycode renderer adapter",
"type": "module",
"scripts": {
"build": "vite build"
},
"alias": {
"#": "./src"
},
"files": [
"dist"
],
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"unpkg": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"default": "./dist/index.mjs"
}
},
"dependencies": {
"licia-es": "catalog:",
"vue": "2.7.16",
"wujie-vue2": "1.0.22"
},
"devDependencies": {
"@types/axios": "0.14.4",
"@vitejs/plugin-vue": "catalog:",
"axios": "catalog:",
"vite-plugin-dts": "catalog:",
"vue-router": ""
}
}

View File

@ -0,0 +1,132 @@
<script setup lang="ts">
import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import type { Router, RouteRecordRaw } from 'vue-router';
import { onBeforeUnmount, onMounted } from 'vue';
import WujieVue from 'wujie-vue3';
// @ts-ignore ignore the type error
import { version } from '/package.json';
//
interface AxiosInterceptors {
request?: {
onFulfilled?: (
config: InternalAxiosRequestConfig,
) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
onRejected?: (error: any) => any;
};
response?: {
onFulfilled?: (
response: AxiosResponse,
) => AxiosResponse | Promise<AxiosResponse>;
onRejected?: (error: any) => any;
};
}
const props = withDefaults(
defineProps<{
[key: string]: any;
applicationId: number;
degrade?: boolean;
fileId: number | string;
interceptors?: AxiosInterceptors;
name?: string;
// payload
payload?: Record<string, any>;
projectId: number;
route?: RouteRecordRaw;
router?: Router;
sync?: boolean;
url?: string;
}>(),
{
// wujie
degrade: true,
// 使
interceptors: () => ({}),
// 使 name
name: undefined,
// 使 payload
payload: () => ({}),
// 使 route
route: undefined,
// 使 router
router: undefined,
sync: true,
url: 'https://y-code-renderer.shiyue.com',
},
);
const { bus } = WujieVue;
//
const beforeLoad = (appWindow: Window) => {
console.log(`${props.name} 开始加载`, appWindow);
};
const afterMount = (appWindow: Window) => {
console.log(`${props.name} 加载完成`, appWindow);
};
//
const handleMessage = (data: any) => {
console.log(`${props.name} 收到子应用消息:`, data);
};
//
const subAppProps = {
...props,
adapterInfo: {
version,
},
};
//
const handleReady = (data: any) => {
console.log(`${props.name} 子应用就绪:`, data);
//
};
const handleRenderSuccess = () => {
console.log(`${props.name} 子应用渲染成功`);
//
};
const handleRenderFail = (error: any) => {
console.error(`${props.name} 子应用渲染失败:`, error);
//
};
onMounted(() => {
//
bus.$on('message', handleMessage);
bus.$on('ready', handleReady);
bus.$on('render-success', handleRenderSuccess);
bus.$on('render-fail', handleRenderFail);
});
onBeforeUnmount(() => {
//
bus.$off('message', handleMessage);
bus.$off('ready', handleReady);
bus.$off('render-success', handleRenderSuccess);
bus.$off('render-fail', handleRenderFail);
});
</script>
<template>
<WujieVue
:name="name || fileId"
:url="url"
:sync="sync"
width="100%"
height="100%"
:degrade="degrade"
:props="subAppProps"
:before-load="beforeLoad"
:after-mount="afterMount"
/>
</template>

View File

@ -0,0 +1 @@
export { default } from './adapter.vue';

View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "Node",
"paths": {
"@/*": ["src/*"]
},
"resolveJsonModule": true,
"types": ["vite/client", "vue/runtime-dom"],
"strict": true,
"useUnknownInCatchVariables": false,
"declaration": true,
"declarationDir": "dist/types",
"outDir": "dist",
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules", "dist"]
}

View File

@ -0,0 +1,68 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import vue from '@vitejs/plugin-vue';
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default defineConfig({
build: {
cssCodeSplit: true,
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
fileName: (format) => {
switch (format) {
case 'cjs': {
return 'index.cjs';
}
case 'es': {
return 'index.mjs';
}
case 'umd': {
return 'index.js';
}
default: {
return `index.${format}.js`;
}
}
},
formats: ['es', 'cjs', 'umd'],
name: 'RendererAdapter',
},
outDir: 'dist',
rollupOptions: {
external: ['vue'],
output: {
assetFileNames: (assetInfo) => {
if (assetInfo.name === 'style.css') return 'assets/[name][extname]';
return 'assets/[name]-[hash][extname]';
},
globals: {
vue: 'Vue',
},
},
},
sourcemap: true,
},
css: {
extract: false,
modules: {
localsConvention: 'camelCaseOnly',
scopeBehaviour: 'local',
},
},
plugins: [
vue({
css: {
injectCss: true,
},
}),
dts({
insertTypesEntry: true,
outDir: 'dist/types',
staticImport: true,
}),
],
});