最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 有用的 JSON.stringify

    正文概述 掘金(臼犀)   2020-11-30   483

    有用的 JSON.stringify

    先别吐槽标题,咱们今天讲点 JSON.stringify 不常用的操作。

    看个例子

    const data = {
        global: {
            preview: 'https://bucket.oss-cn-hangzhou.aliyuncs.com/images/p1.png'
        },
        layouts: [
            {
                elements: [
                    {
                        imageUrl: 'https://bucket.oss-cn-hangzhou.aliyuncs.com/images/p2.png',
                        elements: [
                            {
                                imageUrl: 'https://bucket.oss-cn-hangzhou.aliyuncs.com/images/p3.png'
                            }
                        ]
                    },
                    {
                        url: 'https://bucket2.oss-cn-hangzhou.aliyuncs.com/images/p2.png'
                    }
                ],
                backgroundImage: 'https://bucket.oss-cn-hangzhou.aliyuncs.com/images/p4.png'
            }
        ]
    };
    

    你同往常一样从接口拿到的模板数据将图片展示到页面上,某天后端同学为 oss 资源加了个 CDN 域名后需要让原本走 bucket.oss-cn-hangzhou.aliyuncs.com 的图片统一走 some.oss-cdn.com 加速图片资源的访问,这时你需要将数据中的所有图片资源的域名 bucket.oss-cn-hangzhou.aliyuncs.com 改成 cdn 域名 some.oss-cdn.com

    知道数据结构情况我们当然可以直接遍历整个对象:

    function replace(url) {
        return url.replace('bucket.oss-cn-hangzhou.aliyuncs.com', 'some.oss-cdn.com');
    }
    
    function replaceOssHost(data) {
        data.global.preview = replace(data.global.preview);
        data.layouts.forEach(layout => {
            layout.backgroundImage = replace(layout.backgroundImage);
            layout.elements.forEach(element => {
                element.url = replace(element.url);
                element.imageUrl = replace(element.imageUrl);
                if (element.elements) {
                    // ...
                }
            });
        });
        return data;
    }
    

    嗯,一看就很不靠谱!随便加一个新的 image 资源字段,遍历的规则就得改了,所以我们需要一个与具体解构无关的替换方法,大概如下:

    function replaceOssHost(data) {
        Object.entries(data).forEach(([k, v]) => {
            if (v && typeof v === 'object') {
                return replaceOssHost(v);
            }
            if (typeof v === 'string') {
                data[k] = replace(v);
            }
        });
        return data;
    }
    

    这不就是递归遍历一个对象么?所以和 JSON.stringify 有啥关系?

    常被忽略 JSON.stringify 参数

    问题:你知道 JSON.stringifyJSON.parse 方法的第二个参数吗?

    不清楚的小伙伴不妨可以看下 MDN 的文档:

    • MDN JSON.parse

    • MDN JSON.stringify

    JSON.stringify(value[, replacer [, space]])

    JSON.parse(text[, reviver])

    简单来讲就是 JSON.parse 和 JSON.stringify 都提供了第二个可选参数:

    • 可以 key value 的形式遍历当前对象所有可遍历属性。
    • 支持返回新的值替换当前属性的值。

    然后 JSON.parse 和 JSON.stringify 遍历顺序有些区别:

    const data = {
        "1": 1,
        "2": 2,
        "3": {
            "4": 4,
            "5": {
                "6": 6
            }
        }
    };
    
    const dataStr = JSON.stringify(data);
    
    JSON.stringify(data, function (k, v) {
        console.log(`${k}:`, v);
        return v;
    });
    // 输出:根对象 k 为空,遍历从外层往内层遍历
    // : { '1': 1, '2': 2, '3': { '4': 4, '5': { '6': 6 } } } 对象自身时的 key 会是空的
    // 1: 1
    // 2: 2
    // 3: { '4': 4, '5': { '6': 6 } }
    // 4: 4
    // 5: { '6': 6 }
    // 6: 6
    
    JSON.parse(dataStr, function (k, v) {
        console.log(`${k}:`, v);
        return v;
    });
    // 输出:根对象 k 为空,从最最里层的属性开始,一级级往外,最终到达顶层,也就是解析值本身
    // 1: 1
    // 2: 2
    // 4: 4
    // 6: 6
    // 5: { '6': 6 }
    // 3: { '4': 4, '5': { '6': 6 } }
    // : { '1': 1, '2': 2, '3': { '4': 4, '5': { '6': 6 } } } 对象自身时的 key 会是空的
    

    JSON.stringify 的遍历会更符合直觉一点,JSON.parse 遍历属性为对象时会优先从对象的子级开始遍历,大多时候我们并不太关心遍历顺序,但需要注意一下遍历对象自身时的 key 会是空的

    看回原来的例子,使用 JSON.stringify 可以十分方便实现 host 的替换:

    使用 JSON.stringify 进行对象遍历

    简单几行就可以实现替换的操作:

    function replaceOssHost(data) {
        return JSON.parse(JSON.stringify(data, function (k, v) {
            if (v && typeof v === 'string') {
                return replace(v);
            }
            return v;
        }));
    }
    

    当然这是比较简单的例子,下面看个稍微复杂的例子:

    const data = {
        global: {
            preview: 'data:image/png;base64,iVBORw0KGgoAAAANSUhE...'
        },
        layouts: [
            {
                elements: [
                    {
                        imageUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhE...',
                        elements: [
                            {
                                imageUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhE...'
                            }
                        ]
                    },
                    {
                        url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhE...'
                    }
                ],
                backgroundImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhE...'
            }
        ]
    };
    

    同样是图片资源,但是资源变成了 base64 data,而且现在要求在保存模板数据数据时需要将 base64 资源上传到 oss 并替换成对应的链接。

    base64 上传是个异步操作,JSON.stringify 的 replacer 是个同步函数,我们无法直接在 replacer 中进行上传和图片替换,改如何处理呢?

    实现其实很简单,将上传和替换分离就好了,直接上代码了:

    const data = {
        global: {
            preview: 'data:image/png;base64,iVBORw0KGgoAAAANSUhE...'
        }
    };
    
    // 遍历返回资源 map
    function getResources(data) {
        const resources = new Map();
        const content = JSON.stringify(data, function (_, v) {
            if (v && typeof v === 'string' && v.includes('base64,')) {
                resources.set(v, v);
            }
            return v;
        });
        return { content, resources };
    }
    
    // base64 资源上传
    function uploadBase64(base64) {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve('https://some.oss-cdn.com/images/xxx.png');
            }, 200);
        });
    }
    
    async function uploadResources(data) {
        const { content, resources } = getResources(data);
        // 上传替换 base64 为 url
        for (let [_, value] of resources) {
            const url = await uploadBase64(value);
            resources.set(value, url);
        }
        // 在根据 resources value 和 url 映射替换 base64 为 url
        return JSON.parse(content, function(_, v) {
            if (!v || typeof v !== 'string' || !v.includes('base64,')) {
                return v;
            }
            return resources.get(v) || v;
        });
    }
    

    之前很少关注 JSON.stringifyJSON.parse 的参数细节,但在特定业务场景下却有奇效,下次遇到了需要递归数据操作时不妨试试先 JSON.stringifyJSON.parse

    over~!


    起源地下载网 » 有用的 JSON.stringify

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元