虽然底层框架提供了通用的组件值与联动配置,可以建立对组件任意 props 的映射,但这只是一个能力,还不是协议。
(资料图)
业务层是可以确定一个协议的,还要让这个协议具有拓展性。
我们先从使用者角度设计 API,再看看如何根据已有的组件值与联动能力去实现。
首先,不同的业务方会定义不同的联动协议,因此该联动协议需要通过拓展的方式注入:
import { createDesigner } from "designer" import { onReadComponentMeta } from "linkage-protocol" return
首先可视化搭建框架支持 onReadComponentMeta
属性,用于拓展所有已注册的组件元信息,而联动协议的拓展就是基于组件值与组件联动能力的,因此这种是最合理的拓展方式。
之后我们就注册了一个固定的联动协议,它形如下:
{ "componentName": "input", "linkage": [{ "target": "input1", "do": { "value": "{{ $self.value + "hello" }}" } }] }
只要在组件实例上定义 linkage
属性,就可以生效联动。比如上面的例子:
target
: 联动目标。do
: 联动效果,比如该例子为,组件 ID 为 input1
的组件,组件值同步为当前组件实例的组件值 + "hello"
。$self
: 描述自己实例,比如可以从 $self.value
拿到自己的组件值,从 $self.props
拿到自己的 props。
更近一步,target
还可以支持数组,就表示同时对多个组件生效相同规则。
我们还可以支持更复杂的语法,比如让该组件可以同步其他组件值:
{ "componentName": "input", "linkage": [{ "deps": ["input1", "input2"] "props": { "text": "{{ $deps[0].value + deps[1].value }}" } }] }
上面的例子表示,该组件实例的 props.text
同步为 input1 + input2 的组件值:
deps
: 描述依赖列表,每个依赖实例都可以在表达式里用 $deps[]
访问到,比如 $deps[0].props
可以访问组件 ID 为 input1
组件的 props。props
: 同步组件的 props。
如果定义了 target
则作用于目标组件,未定义 target
则作用于自身。但无论如何,表达式的 $self
都指向自己实例。
总结一下,该联动协议允许组件实例实现以下效果:
设定组件值、组件 props 的联动效果。可以将自己的组件值同步给组件实例,也可以将其他组件值同步给自己。基本上,可以满足任意组件联动到任意组件的诉求。而且甚至支持组件间传递,比如 A 组件的组件值同步组件 B, B 组件的组件值同步组件 C,那么 A 组件 setValue()
后,组件 B 和 组件 C 的组件值会同时更新。
以上联动协议只是一种实现,我们可以基于组件值与组件联动设定任意协议,因此实现联动协议的思维具备通用性,但为了方便,我们以上面说的这个协议为例子,说明如何用可视化搭建框架的基础功能实现协议。
首先解读组件实例的 linkage
属性,将联动定义转化为组件联动关系,因为联动协议本质上就是产生了组件联动。接下来代码片段比较长,因此会尽量使用代码注释来解释:
const extendMeta = { // 定义 valueRelates 关系,就是我们上一节提到的定义组件联动关系的 key valueRelates: ({ componentId, selector }) => { // 利用 selector 读取组件实例 linkage 属性 // 由于 selector 的特性,会实时更新,因此联动协议变化后,联动状态也会实时更新 const linkage = selector(({ componentInstance }) => componentInstance.linkage) // 返回联动数组,结构: [{ sourceComponentId, targetComponentId, payload }] return linkage.map(relation => { const result = []; // 定义此类联动类型,就叫做 simpleRelation const payload = { type: "simpleRelation", do: JSON.parse( JSON.stringify(relation.do) // 将 $deps[index] 替换为 $deps[componentId] .replace( /\$deps\[([0-9]+)\]/g, (match: string, index: string) => `$deps["${relation.deps[Number(index)]}"]`, ) // 将 $self 替换为 $deps[componentId] .replace(/\$self/g, () => `$deps["${componentId}"]`), ), }; // 经过上面的代码,表达式里无论是 $self. 还是 $deps[0]. 都转化为了 // $deps[componentId] 这个具体组件 ID,这样后面处理流程会简单而统一 // 读取 deps,并定义 dep 组件作为 source,target 作为目标组件 // 这是最关键的一步,将 dep -> target 关系绑定上 relation.target.forEach((targetComponentId) => { if (relation.deps) { relation.deps.forEach((depIdPath: string) => { result.push({ sourceComponentId: depIdPath, targetComponentId, }); }); } // 定义自己到 target 目标组件的联动关系 result.push({ sourceComponentId: componentId, targetComponentId, payload, }); }); return result; }).flat() } }
上述代码利用 valueRelates
,将联动协议的关联关系提取出来,转化为值联动关系。
接着,我们要实现 props 同步功能,实现这个功能自然是利用 runtimeProps
以及 selector.relates
,将关联到当前组件的组件值,按照联动协议的表达式执行,并更新到对应 key 上,下面是大致实现思路:
const extendMeta = { runtimeProps: ({ componentId, selector, getProps, getMergedProps }) => { // 拿到作用于自己的值关联信息: relates const relates = selector(({ relates }) => relates); // 记录最终因为值联动而影响的 props let relationProps: any = {}; // 记录关联到自己的组件此时组件值 const $deps = relates?.reduce( (result, next) => ({ ...result, [next.componentId]: { value: next.value, }, }), {}, ); // 为了让每个依赖变化都能生效,多对一每一项 do 都带过来了,需要按照 relationIndex 先去重 relates .filter((relate) => relate.payload?.type === "simpleRelation") .forEach((relate) => { const expressionArgs = { // $deps[].value 指向依赖的 value $deps, get, getProps: relate.componentId === componentId ? getProps : getMergedProps, }; // 处理 props 联动 if (isObject(relate.payload?.do?.props)) { Object.keys(relate.payload?.do?.props).forEach((propsKey) => { relationProps = set( propsKey, selector( () => // 这个函数是关键,传入组件 props 与表达式,返回新的 props 值 getExpressionResult( get(propsKey, relate.payload?.do?.props), expressionArgs, ), { compare: equals, // 根据表达式数量可能不同,所以不启用缓存 cache: false, }, ), relationProps, ); }); } }); return relationProps } }
其中比较复杂函数就是 getExpressionResult
,它要解析表达式并执行,原理就是利用代码沙盒执行字符串函数,并利用正则替换变量名以匹配上下文中的变量,大致代码如下:
// 代码执行沙盒,传入字符串 js 函数,利用 new Function 执行 function sandBox(code: string) { // with 是关键,利用 with 定制代码执行的上下文 const withStr = `with(obj) { $[code] }`; const fun = new Function("obj", withStr); return function (obj: any) { return fun(obj); }; } // 获取沙盒代码执行结果,可以传入参数覆盖沙盒内上下文 function getSandBoxReturnValue(code: string, args = {}) { try { return sandBox(code)(args); } catch (error) { // eslint-disable-next-line no-console console.warn(error); } } // 如果对象是字符串则直接返回,是 {{}} 表达式则执行后返回 function getExpressionResult(code: string, args = {}) { if (code.startsWith("{{") && code.endsWith("}}")) { // {{}} 内的表达式 let codeContent = code.slice(2, code.length - 2); // 将形如 $deps["id"].props.a.b.c // 转换为 get("a.b.c", getProps("id")) codeContent = codeContent.replace( /\$deps\[[""]([a-zA-Z0-9]*)[""]\]\.props\.([a-zA-Z0-9.]*)/g, (str: string, componentId: string, propsKeyPath: string) => { return `get("${propsKeyPath}", getProps("${componentId}"))`; }, ); return getSandBoxReturnValue(`return ${codeContent}`, args); } return code; }
其中 with 是沙盒执行时替换代码上下文的关键。
componentMeta.valueRelates
与 componentMeta.runtimeProps
可以灵活的定义组件联动关系,与更新组件 props,利用这两个声明式 API,甚至可以实现组件联动协议。总结一下,包含以下几个关键点:
deps
和 target
利用 valueRelates
转化为组件值关联关系。将联动协议定义的相对关系(比较容易写于容易记)转化为绝对关系(利用 componentId 定位),方便框架处理。利用 with
执行表达式上下文。利用 runtimeProps
+ selector
实现注入组件 props 与响应联动值 relates
变化,从而实现按需联动。
讨论地址是:精读《定义联动协议》· Issue #471 · dt-fe/weekly
以上就是前端可视化搭建定义联动协议实现的详细内容,更多关于前端可视化搭建联动协议的资料请关注脚本之家其它相关文章!
标签:
最新推荐
中小学毕业生如何过暑假?近日,中国教育报微信公众号发起调查,1000余名家长参与。调查显示,有计划地度...
律师兼任调解员,不打官司也能化解纠纷,代理调解受指派的公益性案件还免费。这是兵团第十师北屯市探索...
根据教育部教育考试院统一安排,2022年上半年全国大学英语四、六级口语考试将于5月21日-22日举行,笔试...
学生代购的“苦”与“乐” “你问的这个产品现在做促销活动,买一件包邮,还送小样和面膜……”...
11月11日大连市新冠肺炎疫情防控总指挥部发布,11月10日0时至24时,大连市新增21例本土新冠肺炎确诊...
纤维素制成闪光材料无毒可降解 或彻底改变化妆品行业 科技日报北京11月11日电 (实习记者张...
海洋中或堆积了2 8万吨新冠废物 科技日报北京11月11日电 (记者刘霞)据美国趣味科学网站10日报...
开屏广告又现新花招,换个马甲就重来? ■ 来论 据媒体报道,“双十一”期间,一些App的开屏...
对不合理教师资格认定标准,该全面清理了 ■ 来论 针对网友留言反映的“糖尿病无法通过教师...
虚假宣传、以次充好、售卖临期产品不提示直播间商家“放水”让消费者闹心 关注“双11” 今年...
“扫码抽手机”实则是广告 快递单能“领红包”面单广告是谁发的? “双11”之际,消费者被商...
中新网11月12日电 据北京市疾病预防控制中心微信公众号消息,2021年11月10日北京市接报1例在京存在...
(抗击新冠肺炎)辽宁大连本轮疫情病毒为德尔塔变异株 24个区域划定为中风险地区 中新社大连11月1...
中新网西安11月11日电 (梅镱泷 杨起超)记者11日从西安市鄠邑区秦保局获悉,太平国有生态林场架设...
(抗击新冠肺炎)成都停业整顿56家零售药店 买感冒药需提供身份证 中新网成都11月11日电 (记者 ...
中新网大连11月11日电 (记者 杨毅) 11月11日,大连市政府秘书长衣庆焘在大连疫情防控新闻发布会...
新华社杭州11月11日电(记者冯源)在商周时期,如今的浙江中西部活跃着一个名为“姑蔑”的族群,但是...
中新网大连11月11日电 (记者 杨毅)11月11日,大连市政府秘书长衣庆焘在大连疫情防控工作新闻发布...
中新网大连11月11日电 (记者 杨毅)11月11日,大连市政府秘书长衣庆焘在大连疫情防控工作新闻发布...
中新网昆明11月11日电(记者 缪超)云南“最美政法干警”发布仪式11日在昆明举行。会上,授予昆明市...
(抗击新冠肺炎)甘肃凝聚“她力量”:互助抗疫,女人更懂女人心 中新网兰州11月11日电 (记者 徐...
中新网兰州11月11日电 (史静静)在甘肃金川公司,27年来葛小海始终在生产一线,他参与的“渣罐车制...
中新网乌鲁木齐11月11日电 (王小军 罗宣政 廖超)11月11日,一批来自浙江嘉兴的爱心物资,跨越...
中新网兰州11月11日电 (邬凡 朱学成)11月10日5时30分,位于敦煌车站旁的敦煌综合工区,钢轨探伤车...
中新网重庆11月11日电 (梁钦卿)“我今年上小学三年级了,我不怕疼,打疫苗是为了抵抗新冠病毒。”1...
中新网绵阳11月11日电 (岳波 李远梅)四川绵阳警方11日通报称,一男子酒后无聊多次报警称自己的...
中新网11月11日电 据中国民航局网站消息,11月11日,民航局再发熔断指令,对德国汉莎航空公司LH728...
中新网成都11月11日电 题:疫情中轮椅上的“逆行者”:想为大家做力所能及的事 作者 祝欢 ...
中新网上海11月11日电 (记者 李姝徵)上海警方11日召开发布会披露,在近期“砺剑”行动中破获了一...
中新网太原11月11日电 (记者 李庭耀)记者11日从山西省政府新闻办举行的新闻发布会上获悉,山西推...
中新网乌鲁木齐11月11日电 (刘雨珊 牛雨萌 艾尼)11日,记者从新疆水产科研所获悉,新疆博湖县将...
中新网大连11月11日电 (记者 杨毅)大连市新冠肺炎疫情防控总指挥部 11日发布公告,大连市将庄河...
中新网西安11月11日电 (记者 党田野)身穿白色“礼服”,摇晃着酒杯,时不时浅酌一口啤酒,然后与...
11月11日大连市新冠肺炎疫情防控总指挥部发布,按照国务院应对新冠肺炎疫情联防联控机制关于科学划...
中新网南京11月11日电 题:这个“双十一”南京的猪都“脱单”了 其实还有更让人嫉妒的…… ...
中新网呼伦贝尔11月11日电 (记者 张林虎)11日,记者从内蒙古自治区呼伦贝尔市公安局获悉,该局将...
中新网广州11月11日电 (记者 程景伟)“寻味帅府邂逅甜蜜——2021年帅府之夜”暨“海外拾珠——孙...
中新网徐州11月11日电 题:江苏徐州“家门口车管所”便民服务驶入“高速路” 作者 朱志庚 ...
中新网重庆11月11日电 (梁钦卿)为加强秋冬季空气污染应对,重庆市生态环境局11日发出2021年第九次...
新华社重庆11月11日电 题:深藏功名三十载 化作春蚕报乡亲——一名抗美援朝老兵的人生选择 新...
中新网益阳11月11日电 (王鹏 王庆庆)爱花本是修身养性、陶冶情操之事,湖南益阳市桃江县桃花江镇...
中新网成都11月11日电 题:成都25位民辅警的“封闭”生活:有人“转行”送外卖 有人变身“仓鼠管...
中新网南京11月11日电 (徐珊珊)江苏省教育厅体育卫生与艺术教育处处长张鲤鲤11日在南京表示,到202...
中新网成都11月11日电 (记者 吕杨)成都市公园城市建设管理局11日正式发布公园城市银杏观叶指数,...
中新网南京11月11日电 (徐珊珊)11日,江苏省教育厅召开新闻发布会,发布2020年江苏省学生体质健康...
中新网宜昌11月11日电 (江雅丽 董晓斌)17年前,四川广安一夫妇的6岁儿子被人拐走,夫妻俩寻找多年...
中新网宁德11月11日电 (林榕生)福建宁德市柘荣县应对新型冠状病毒感染肺炎疫情工作领导小组(指挥部...
11月11日,内蒙古额济纳旗新冠肺炎防控工作指挥部发布《关于调整额济纳旗达来呼布镇风险等级的公告...
(抗击新冠肺炎)内蒙古现有本土确诊病例32例 伊金霍洛旗确诊病例清零 中新网呼和浩特11月11日电...
中新网呼和浩特11月11日电 (记者 张林虎)11日,记者从内蒙古自治区通辽市相关部门获悉,从10日下...
中国广电入局5G网络 初步形成“有线+5G”的融合发展新格局
成都抗疫的外籍志愿者:愿为城市“康复”贡献力量
Copyright © 2015-2022 大河律师网版权所有 备案号:京ICP备2022022245号-75 联系邮箱:435 226 40@qq.com