前端解析json数组-前端接收json数组
关注 “脚本之家 ”,与百万开发者在一起 出处:趣谈前端(ID:beautifulFront) 如若转载请联系原公众号 任何一家Saas企业都需要有自己的低代码平台.在可视化低代码的前端研发过程中, 发现了很多有意思的技术需求, 在解决这些需求的过程中, 往往也会给自己带来很多收获, 今天就来分享一下在研发Dooring过程中遇到的前端技术问题——javascript函数存储.
背景介绍
我们都知道要想搭建一个前端页面基本需要如下3个要素:
元素(UI) 数据(Data) 事件/交互(Event) 在 数据驱动视图 的时代, 这三个要素的关系往往如下图所示:
可视化搭建平台的设计思路往往也是基于上面的过程展开的, 我们需要提供编辑器环境给用户来创建视图和交互, 最终用户保存的产物可能是这样的:
{
"name": "Dooring表单",
"bgColor": "#666",
"share_url": "http://xxx.cn",
"mount_event": [
{
"id": "123",
"func": () => {
// 初始化逻辑
GamepadHapticActuator();
},
"sourcedata": []
}
],
"body": [
{
"name": "header",
"event": [
{
"id": "123",
"type": "click",
"func": () => {
// 组件自定义交互逻辑
showModal();
}
}
]
}
]
}那么问题来了,我们可以保存json字符串(可以通过JSON.stringify序列化)前端解析json数组,但是函数怎么一起保存呢? 保存函数后,js如何在页面渲染时正常运行这个函数呢? ?
考虑实施解决方案
趣谈前端
我们都知道JSON.stringify可以用来将js对象转成json,但是它也有局限性,比如:
转换值如果有toJSON()方法,则由toJson()定义哪些值将被序列化非数组对象的属性不保证在序列化后的字符串中以特定顺序出现Boolean, number, string wrappers 对象在序列化过程中会自动转换为对应的原始值undefined、any function和symbol值,在序列化过程中会被忽略(当它出现在非数组对象的属性值中时)或转换为null(在数组中出现时)。函数和undefined分别转换时,会返回undefined,如JSON.stringify(function(){})或JSON.stringify(undefined),所有符号为属性键将被完全忽略,即使 replacer 参数是强制包含在其 Date 上调用 toJSON() 以将其转换为字符串字符串(与 Date.toISOString() 相同),因此它将被视为字符串。 NaN 和 Infinity 格式值和 null 将被视为 null。 其他类型对象,包括Map/Set/WeakMap/WeakSet,只序列化可枚举属性
我们可以看到第4项,如果我们序列化的对象中有一个函数,它会被忽略! 所以通常我们不能使用 JSON.stringify 保存函数,还有其他方法吗?
可能你会想到先把函数转成字符串,然后用JSON.stringify序列化保存到后台,最后在组件使用的时候用eval或者Function把字符串转成函数。 大致流程如下:
趣谈前端
是的,理想是美好的,现实却是_______。
接下来我们分析一下关键链接func2string和string2func是如何实现的。
js存储功能方案设计
熟悉JSONAPI的朋友可能知道JSON.stringify支持3个参数,第二个参数replacer可以是函数也可以是数组。 作为一个函数,它有两个参数,key(键)和value(值),这两个参数都会被序列化。 该函数需要返回 JSON 字符串中的值,如下所示:
所以我们可以在第二个函数参数中转换值类型为函数的数据。 如下:
const stringify = (obj) => {
return JSON.stringify(obj, (k, v) => {
if(typeof v === 'function') {
return `${v}`
}
return v
})
}这样一来,我们似乎就可以把函数保存到后台了。 接下来我们看看如何用函数字符串反序列化json。
因为我们把函数转成了字符串,所以在反解析的时候需要知道哪些字符串需要转成函数。 如果我们不对功能做任何处理,我们可能需要人工识别。
人肉识别的缺点是我们需要使用正则化来提取具有函数特征的字符串,但是函数的写法有很多种,我们要考虑很多情况,不能保证具有函数特征的字符串一定是函数。
所以我换了一个简单的方法来提取函数,不用写复杂的正则。 方法是在函数序列化的时候注入标识符,这样我们就可以知道哪些字符串需要解析成函数,如下:
stringify: function(obj: any, space: number | string, error: (err: Error | unknown) => {}) {
try {
return JSON.stringify(obj, (k, v) => {
if(typeof v === 'function') {
return `${this.FUNC_PREFIX}${v}`
}
return v
}, space)
} catch(err) {
error && error(err)
}
}this.FUNC_PREFIX是我们定义的标识符前端解析json数组,方便我们在使用JSON.parse的时候快速解析函数。 JSON.parse也支持第二个参数,其用法与JSON.stringify的第二个参数类似。 我们可以这样转换它:
parse: function(jsonStr: string, error: (err: Error | unknown) => {}) {
try {
return JSON.parse(jsonStr, (key, value) => {
if(value && typeof value === 'string') {
return value.indexOf(this.FUNC_PREFIX) > -1 ? new Function(`return ${value.replace(this.FUNC_PREFIX, '')}`)() : value
}
return value
})
} catch(err) {
error && error(err)
}
}new Function可以将一个字符串转成一个js函数,它只接受字符串参数,它的可选参数是方法的入参,必选参数是方法体的内容,一个形象的例子:
趣谈前端
我们上面代码中函数体的内容:
new Function(`return ${value.replace(this.FUNC_PREFIX, '')}`)()
退货原因是为了原封不动地恢复原功能。 您也可以使用 eval,但由于公众舆论,仍需谨慎使用。
以上方案已经可以实现前端存储功能的功能,但是为了更加工程化和健壮性,还需要做很多额外的处理和优化,让更多的人开箱即用你的库。
终于
为了让更多的人直接使用这个功能,我把完整版的json序列化方案封装成一个类库,支持的功能如下:
安装方法如下:
# or npm install xijs
yarn add xijs使用:
import { parser } from 'xijs';
const a = {
x: 12,
b: function() {
alert(1)
}
}
const json = parser.stringify(a);
const obj = parser.parse(json);
// 调用方法
obj.b();推荐阅读:
直接拿来用!盘点一些拯救头发的 JS 单行代码,网友:摸鱼必备啊
每日打卡赢积分兑换书籍入口