二次开发脚本(代码面板)
二次开发脚本(代码面板)
除了用 SDK 的 ctx 从外部操作大屏,你还可以在编辑器「代码」页签直接为某个组件 / 页面编写脚本(Extension)。脚本随工程数据持久化(存在节点的 extensions 里),导出后由运行时在预览 / 发布态自动实例化并执行(编辑态不执行)。
适合:埋点、组件级业务逻辑、跨组件协作、把交互动作接到自定义实现。
脚本形态
一个脚本是一个 export default class,可声明下列生命周期方法(全部可选),并通过 this.element(宿主组件)与 this.project(工程)读写:
export default class {
/** 实例创建后(不依赖挂载的初始化) */
created() {}
/** 宿主挂载完成后 */
mounted() {}
/** 工程全部组件初始化完成后(可安全访问其它组件) */
ready() {
this.element.addEventListener('click', () => {
this.element.setOption('text', '被点击了')
this.project.setParams({ clickedAt: Date.now() })
this.project.emit('refresh', { from: this.element.uid }, 'other-widget-uid')
})
}
/** 每帧调用(仅 enabled 时;勿放耗时操作) */
update() {}
/** 销毁时清理监听 */
destroy() {}
/** 启用态切换 */
onEnable() {}
onDisable() {}
/** 自定义属性变化 */
onPropertyChanged(property) {}
}| 生命周期 | 时机 |
|---|---|
created / init | 实例创建后(init 兼容旧模板,等价 created) |
mounted | 宿主挂载完成后 |
ready | 工程全部组件初始化完成(可安全访问其它组件) |
update | 每帧(仅 enabled 时)——勿放耗时操作 |
destroy | 组件销毁时(清理监听) |
onEnable / onDisable | 启用态切换(可用 this.enabled = false 自行停更) |
onPropertyChanged(property) | 自定义属性变化(预留钩子:脚本属性声明面板落地后触发,当前不会被调用) |
编译与运行宿主约定(排错必读)
脚本如何被编译、何时运行(源码 web/packages/core/src/extension/compileExtension.ts / VpExtensionHost.ts):
- 编译规则:脚本必须以
export default <类>收尾——编译器把它改写成return <类>后用new Function求值;其余顶层export关键字会被剥离,所以「多个类协作」可以放同一脚本里(export class Helper {}会变成局部class Helper {})。不支持import(脚本运行在函数体里,没有模块系统;跨脚本复用见下节「跨脚本协作」)。 - 编译失败不连坐:语法错误 / 没有
export default一个类时,该脚本记一条控制台告警并跳过,不影响同元素的其它脚本。改完保存后重新进入播放态即重新编译。 - 注入全局
Element:兼容 兼容风格属性声明的最小桩(Element.empty()返回空对象、不生成设置项),移植旧脚本时引用它不会崩溃。 - 只在播放态运行:预览 / 发布 / SDK 挂载时由渲染层启动脚本宿主;编辑态画布不执行任何脚本(改了代码看不到效果,先点预览)。
update每帧的真实含义:宿主用requestAnimationFrame驱动(测试 / SSR 环境回退setTimeout 16ms),仅this.enabled为真时调用;页签隐藏时浏览器会节流 rAF,勿依赖固定帧率。- 清理是自动的:
addEventListener等订阅挂在宿主的 effectScope 上,组件卸载 / 退出播放态时统一退订;自己setInterval/ 全局监听的要在destroy()里手动清。
this.element —— 宿主元素 API
在脚本里读写本组件属性、监听事件、寻址子 / 父组件、取数、收发事件:
this.element.uid / this.element.type / this.element.name
this.element.getOption(path) / this.element.setOption(path, value)
this.element.show() / this.element.hide() / this.element.setVisible(bool)
this.element.addEventListener('click' | 'mouseenter' | 'mouseleave' | '<内部事件>', fn) // 返回退订函数
this.element.emit(eventName, data) // 向本组件投递行为事件
this.element.getData([[connUid, tableId, fieldId]]) // 读已绑定字段的行(缺省读全部绑定字段所在表)
this.element.getBoundFields(path) // 读某 field 属性绑定的字段三元组
this.element.getChildren() / this.element.getParent()
this.element.getExtensionInstance() // 取本元素第一个脚本实例(跨脚本通信)this.project —— 工程 API
跨组件寻址、推数读数、改项目参数、收发行为事件:
this.project.projectId / this.project.projectName / this.project.params
this.project.getElement(uid) / this.project.getElementsByType(type) // 跨页面寻址,返回元素 API
this.project.getData(connUid, tableId)
this.project.setData(connUid, tableId, rows) // 替换某表行(触发重渲)
this.project.setParams({ ... }) // 千人千面:驱动数据规则 / 条件样式 / 状态
this.project.refreshData(connUid?) // 重取数据(缺省刷新全部)
this.project.emit(eventName, data, targetUid?) // 投递行为事件(定向 / 广播)跨脚本协作
用 this.project.getElement(uid).getExtensionInstance() 拿到另一个组件脚本实例后,直接调它的方法 / 读它的字段:
ready() {
const chart = this.project.getElement('w_chart')?.getExtensionInstance()
chart?.highlight?.(3) // 调另一个脚本暴露的方法
}平台数据口 vp.pb(受控)
脚本内可用全局 vp.pb 访问平台数据(PocketBase):list / view / create / update / delete / subscribe / send / fileUrl / filter。默认作用域只读(list / view / fileUrl);需要写操作时,在代码面板脚本行的「平台数据权限」里放开(自定义操作与集合白名单,或不限制),对应持久化字段 extension.pbScope。每次调用都会留审计痕。
async ready() {
const res = await vp.pb.list('vpd_devices', { perPage: 20 })
// 写操作需在「平台数据权限」中放开 create
// await vp.pb.create('vpd_logs', { event: 'open' })
}与「执行脚本」动作、SDK 的关系
- 交互「执行脚本」动作(
executeScript)派发的是一个命令字符串,由宿主接管:SDK 侧用ctx.onScript(command => ...)或挂载选项onScript接手;不接管时走默认后端/execute-script。 - 代码面板脚本(本页)是组件内嵌类,随工程走、运行时自动执行,二者互补:动作负责「触发」,脚本负责「常驻逻辑」。
SDK 程序化增删改脚本
在 SDK 里也能对组件句柄读写脚本:
const w = ctx.getWidget('uid')!
w.getExtensions()
w.addExtension({ name: '点击埋点', code: 'export default class { ready(){ this.element.addEventListener("click",()=>{}) } }' })
w.updateExtension('ext-uid', { enabled: false })
w.removeExtension('ext-uid')延伸阅读:SDK 与扩展开发(ctx 外部操作、onScript 接管)· 自定义组件开发· 事件交互(executeScript / sendBehaviorEvent)· 数据系统(setParams 千人千面)。