robin

robin 查看完整档案

杭州编辑兰州工业学院  |  计算机网络技术 编辑SegmentFault  |  前端 编辑 blog.rnode.me 编辑
编辑

前端开发一枚,做过RN、小程序。

个人动态

robin 赞了文章 · 今天 21:40

为什么你混的总比别人差??

或许有很多小伙伴们都有这样的困惑,自己年少的的发小、同学、很久不联系的朋友或同事,无意间你听到他们中的某某人早已在某某一线大都市买房(甚至豪宅)、买车(豪车),又或者听说某某人如今自己创业当BOSS了,又或者是谁谁(当初学习烂的要命,甚至以为今后会成为地痞混混)目前已是某上市公司XXO了。

反过来看看如今的自己呢?仍然还是上着那可能会令有些人羡慕的班,每月拿着固定的工资,过着朝九晚五的生活,日复一日,年复一年,转眼就到了所谓的“中年焦虑”的时期。

到底是哪些因素决定了这些呢??是出身?是学历?是背景?还是没有个好老爹??或者还是其它方面的因素,这里肯定会有人说,那不废话么,没有好出身,好学历,好背景,再加上没有个好老爹,你能混出个啥样出来呢?

我想也不尽然是这些因素吧,那么,人与人的差距到底是什么造成的?智商?环境?运气?后天的努力?还是其他什么?个人认为还是后天的因素占比较大。

选择的重要性

思考:相比过去十年,如今的机会是否更多??

在国内互联网爆发的那几年,各类的互联网产品层出不穷。今天移动端的产品中每个类别里的前三位基本占据了80%(甚至更高)的流量。如支付领域的微信和支付宝。而其他成千上万的移动端的小产品却只是分得了剩余的那么仅有的一点市场。

个人发展也是一样。新技术、新概念每几年就有一波,比起过去10年,未来好像总有机会。大佬们天天告诉你,需要专注、可以跨界,这简直是说一个人能同时在好几个领域有很多机会。

今天是一个机会更多,但概率更小的世界。处处有机会,就是处处有竞争。越是开放的市场,越是需要专注于核心竞争力。越是开放,越是聚焦——这是一个高手的时代。当资源非常丰富时,选择就愈发变得更加重要。

你有多努力?

或许有很多人都会说,我已经很努力很努力了,那么,当你心的时候,试问下你自己,你到底有多努力?真的比别人还努力吗?

不是有这么一句话么,不怕别人比自己聪明,也不怕别人比自己有多努力,就怕比自己聪明的人拼命努力。比如,你每年读十本书来提高自己的技术、知识面,可是你不知道,那些拼命努力的人,总是在挤时间,每月读两三本。的确,是这样,或许这也就是明明自己努力了,却一直没有任何起色或者仍然还是比别人差的原因之一吧。

你有一颗坚持、永不放弃的心吗?

有这样一句话:“当你抱怨没有鞋的时候,有的人还没有脚。”而有着这样一个人,他不仅没有脚,连双手都没有,但他却拥有两个大学学位,骑马、游泳、冲浪、打鼓、踢足球,他样样皆能。年仅31岁,他已踏遍世界各地,接触逾百万人,激励和启发着他们的人生,他是力克·胡哲。

相信大家都知道这么个人,真不知道请自行百度吧。

如果说,你做一件事情经常三心二意,而且还不懂得坚持、持续,那么你也是无法获得自己想要的成功。没有那么很容易、很简单就能达成的目标,都是需要自己不断的努力、不断坚持,一直持续、专注去做一件事,我相信,终究你会做好的,你也会成功的。

坚持、永不放弃的心,比钻石更珍贵;没有无法达成的目标,没有遥不可及的梦想。

太把自己当回事

说这个目的不是让你不把自己当回事,而是不要太把自己当回事。全球几十亿的人口,你我不过只是那沧海一粟,那么的渺小。千万不要太把自己当回事,否则,你会失去太多的机会与人脉(朋友)关系。

为什么这么讲?最近看过,也听过一些事情,才由此有感而发。

很多人,老想着这事要公平,那事要公平,试问,有绝对的公平吗?规则是给大多数人制定的,不是给你一个人制定的,当大多数人都遵守的时候,这就是公平。

大家都知道,现如今这个房价问题是一直以来的一个大话题,好多人因房而一夜暴富,成为了人们中的“拆二代”、“拆三代”,然而也有很多人因房而辛苦奔波一辈子,有时候,也不要不服气,生活会有血淋淋的事件来告诉你,真的不要太把自己当回事。

为何不奋起反抗,让自己成为他人心甘情愿的把你当回事的人,这样不好吗?

你懂尊重、感恩吗?

相信大家听过这样一句话:“不懂感恩的人,比狼还可怕,珍爱生命,敬请远离”。为什么这么说??人性是自私的,如果你每次都帮助他,有一次因某些因素没有出手帮助,他就会抱怨你不够意思,忘记了你曾经N次的帮助过他。所以,在他人帮助你、拉你一把的时候,千万不要忘记说声“谢谢”。

懂得如何去尊重别人?

我们先来看看一个小故事,我有一位朋友,非常喜欢帮助他人解答问题、也时常活跃在各大技术群(微信群、QQ群),可是啊,前段时间他特别烦,为什么呢?一问才知道,前段时间,经常有人深更半夜、有的甚至凌晨两三点还在微信或QQ上找他,严重影响他的正常休息与生活。

听了这个事,我突然想到一句话是:“好心办坏事”,可能不太贴切。但也算是差不多的意思吧,生活中这样的例子太多太多了,总有人这样或那样,不切时宜的去“打扰”别人的生活。为何不在“打扰”别人之前,多站在对立的角度去考虑、思考一下呢?哪个人没有自己独立的私人空间?哪个人不能有自己私人生活呢?别总拿自己的思想去定位别人的生活,多一份尊重,多一份理解,你会发现总在你困难时、无助时,且在不经意间遇上你生命中的“贵人”;你会发现你的生活也是那么的无比美好与豁然开朗。

与人相处:

你尊重他人,他人就尊重你,

你无视他人,他人就无视你,

就这么简单!

写在最后

看到这里,是不是感觉民工哥在这BB了一堆的废话,那么,好吧,最后在送给大家一些肺腑之言吧:

  • 生活不会因为你努力就会对你倾斜天平。但生活一定不会一直辜负拼命努力、勤奋的人。
  • 比你混得好的人,不一定比你优秀;虽然现在比你混得好,以后也未必。这样的例子很多。
  • 拼爹有什么意思呢?不是通过自己努力得来的,不值得羡慕;能通过自己努力得来的,决不轻易认输
  • 懂得尊重、感恩他人,你的世界会无比的美好,你的生活处处会遇到“贵人”。

image

查看原文

赞 1 收藏 0 评论 0

robin 赞了文章 · 11月24日

速度提高几百倍,记一次数据结构在实际工作中的运用

这段时间写了一堆源码解析,这篇文章想换换口味,跟大家分享一个我工作中遇到的案例。毕竟作为一个打工人,上班除了摸鱼看源码外,砖还是要搬的。本文会分享一个使用恰当的数据结构来进行性能优化,从而大幅提高响应速度的故事,提高有几百倍那么多。

事情是这样的,我现在供职一家外企,我们有一个给外国人用的线下卖货的APP,卖的商品有衣服,鞋子,可乐什么的。某天,产品经理找到我,提了一个需求:需要支持三层的产品选项。听到这个需求,我第一反应是我好像没有见到过三层的产品选项,毕竟我也是一个十来年的资深剁手党,一般的产品选项好像最多两层,比如下面是某电商APP一个典型的鞋子的选项:

image.png

这个鞋子就是两层产品选项,一个是颜色,一个是尺码,颜色总共有11种,尺码总共也是11种。为了验证我的直觉,我把我手机上所有的购物APP,啥淘宝,京东,拼多多,苏宁易购全部打开看了一遍。在我看过的商品中,没有发现一个商品有三层选项的,最多也就两层

本文可运行的示例代码已经上传GitHub,大家可以拿下来玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/DataStructureAndAlgorithm/OptimizeVariations

为什么没人做三层选项?

一两家不做这个,可能是各家的需求不一样,但是大家都不做,感觉事情不对头。经过仔细分析后,我觉得不做三层选项可能有以下两个原因:

1. 这可能是个伪需求

上面这个鞋子有11种颜色,11种尺码,意味着这些选项后面对应的是11 * 11,总共121个商品。如果再来个第三层选项,假设第三层也有11个选项,那对应的商品总共就是11 * 11 * 11,也就是1331个商品,好多店铺总共可能都没有1331个商品。也就是说,第三层选项可能是个伪需求,用户并没有那么多选项放在第三层,还是以上面的鞋子为例,除了颜色,尺码外,非要再添一个层级,那只能是性别了,也就是男鞋和女鞋。对于男鞋和女鞋来说,版型,尺码这些很不一样,一般都不会放到一个商品下面,更常用的做法是分成两个商品,各自有自己的颜色和尺码。

2. 有性能问题

仅仅是加上第三层选项这个功能并没有什么难的,也就是多展示几个可以点击的按钮而已,点击逻辑跟两层选项并没有太大区别。但是细想下去,我发现了他有潜在的性能问题。以上面这双鞋子为例,我从后端API拿到的数据是这样的:

const merchandise = {
  // variations存放的是所有选项
  variations: [
    {
      name: '颜色',
      values: [
        { name: '限量版574海军蓝' },
        { name: '限量版574白粉' },
        // 下面还有9个
      ]
    },
    {
      name: '尺码',
      values: [
        { name: '38' },
        { name: '39' },
        // 下面还有9个
      ]
    },
  ],
  // products数组存放的是所有商品
  products: [
    {
      id: 1,
      price: 208,
      // 与上面variations的对应关系在每个商品的variationMappings里面
      variationMappings: [
        { name: '颜色', value: '限量版574白粉' },
        { name: '尺码', value: '38'},
      ]
    },
    // 下面还有一百多个产品
  ]
}

上面这个结构本身还是挺清晰的,merchandise.variations是一个数组,有几层选项,这个数组就有几个对象,每个对象的name就是当前层级的名字,values就是当前层级包含的选项,所以merchandise.variations可以直接拿来显示在UI上,将他们按照层级渲染成按钮就行。

上面图片中,用户选择了第一层的限量版574白粉,第二层的4041等不存在的商品就自动灰掉了。用上面的数据结构可以做到这个功能,当用户选择限量版574白粉的时候,我们就去遍历merchandise.products这个数组,这个数组的一个项就是一个商品,这个商品上的variationMappings会有当前商品的颜色尺码信息。对于我当前的项目来说,如果这个商品可以卖,他就会在merchandise.products这个数组里面,如果不可以卖,这个数组里面压根就不会有这个商品。比如上图的限量版574白粉40码的组合就不会出现在merchandise.products里面,查找的时候找不到这个组合,那就会将它变为灰色,不可以点。

所以对于限量版574白粉40这个鞋子来说,为了知道它需不需要灰掉,我需要整个遍历merchandise.products这个数组。按照前面说的11个颜色,11个尺码来说,最多会有121个商品,也就是最多查找121次。同样的要知道限量版574白粉41这个商品可以不可以卖,又要整个遍历商品数组,11个尺码就需要将商品数组整个遍历11次。对于两层选项来说,11 * 11已经算比较多的了,每个尺码百来次运算可能还不会有严重的性能问题。但是如果再加一层选项,新加这层假如也有11个可选项,这复杂度瞬间就增加了一个指数,从$O(n^2)$变成$O(n^3)$!现在我们的商品总数是11 * 11 * 11,也就是1331个商品,假如第三层是性别,现在为了知道限量版574白粉40男性这个商品可不可以卖,我需要遍历1331个商品,如果遍历121个商品需要20ms,还比较流畅,那遍历1331个商品就需要220ms,这明显可以感觉到卡顿了,在某些硬件较差的设备上,这种卡顿会更严重,变得不可接受了。而且我们APP使用的技术是React Native,本身性能就比原生差,这样一来,用户可能就怒而卸载了!

我拿着上述对需求的疑问,和对性能的担心找到了产品经理,发生了如下对话:

我:大佬,我发现市面上好像没有APP支持三层选项的,这个需求是不是有问题哦,而且三层选项相较于两层选项来说,复杂度是指数增长,可能会有性能问题,用户用起来会卡的。

产品经理:兄弟,你看的都是国内的APP,但是我们这个是给外国人用的,人家外国人就是习惯这么用,咱要想卖的出去就得满足他们的需求。太卡了肯定不行,性能问题,想办法解决嘛,这就是在UI上再加几个按钮,设计图都跟以前是一样的,给你两天时间够了吧~

我:啊!?额。。。哦。。。

咱也不认识几个外国人,咱也不敢再问,都说了是用户需求,咱必须满足了产品才卖的出去,产品卖出去了咱才有饭吃,想办法解决吧!

解决方案

看来这个需求是必须要做了,这个功能并不复杂,因为三层选项可以沿用两层选项的方案,继续去遍历商品数组,但是这个复杂度增长是指数级的,即从$O(n^2)$变成$O(n^3)$,用户用起来会卡。现在,我需要思考一下,有没有其他方案可以提高性能。经过仔细思考,我发现,这种指数级的复杂度增长是来自于我们整个数组的遍历,如果我能够找到一个方法不去遍历这个数组,立即就能找到限量版574白粉40男性对应的商品存不存在就好了。

这个具体的问题转换一下,其实就是:在一个数组中,通过特定的过滤条件,查找符合条件的一个项。嗯,查找,听起来蛮耳熟的,现在我之所以需要去遍历这个数组,是因为这些查找条件跟商品间没有一个直接的对应关系,如果我能建立一个直接的对应关系,不就可以一下就找到了吗?我想到了:查找树假如我重组这些层级关系,将它们组织为一颗树,每个商品都对应树上的一个叶子节点,我可以将三层选项的查找复杂度从$O(n^3)$降到$O(1)$。

两层查找树

为了说明白这个算法,我先简化这个问题,假设我们现在有两层选项,颜色尺码,每层选项有两个可选项:

  1. 颜色:白色,红色
  2. 尺码:39,40

我们现在对应有4个商品:

  1. 一号商品:productId为1,白色,39码
  2. 二号商品:productId为2,白色,40码
  3. 三号商品:productId为3,红色,39码
  4. 四号商品:productId为4,红色,40码

如果按照最简单的做法,为了查找红色39码鞋子存不存在,我们需要遍历所有的这四个商品,这时候的时间复杂度为$O(n^2)$。但是如果我们构建像下面这样一颗树,可以将时间复杂度降到$O(1)$:

image-20201117160534500

上面这颗树,我们忽略root节点,在本例中他并没有什么用,仅仅是一个树的入口,这棵树的第一层淡黄色节点是我们第一层选项颜色,第二层淡蓝色节点是我们的第二层选项尺码,只是每个颜色节点都会对应所有的尺码,这样我们最后第二层的叶子节点其实就对应了具体的商品。现在我们要查找红色39码鞋子,只需要看图中红色箭头指向的节点上有没有商品就行了。

那这种数据结构在JS中该怎么表示呢?其实很简单,一个对象就行了,像这样:

const tree = {
  "颜色:白色": {
    "尺码:39": { productId: 1 },
    "尺码:40": { productId: 2 }
  },
  "颜色:红色": {
    "尺码:39": { productId: 3 },
    "尺码:40": { productId: 4 }
  }
}

有了上面这个数据结构,我们要查找红色39码直接取值tree["颜色:红色"]["尺码:39"]就行了,这个复杂度瞬间就变为$O(1)$了。

三层查找树

理解了上面的两层查找树,要将它扩展到三层就简单了,直接再加一层就行了。假如我们现在第三层选项是性别,有两个可选项,那我们的查找树就是这样子的:

image-20201118133333379

对应的JS对象:

const tree = {
  "颜色:白色": {
    "尺码:39": { 
        "性别:男": { productId: 1 },
      "性别:女": { productId: 2 },
    },
    "尺码:40": { 
        "性别:男": { productId: 3 },
      "性别:女": { productId: 4 },
    }
  },
  "颜色:红色": {
    "尺码:39": { 
        "性别:男": { productId: 5 },
      "性别:女": { productId: 6 },
    },
    "尺码:40": { 
        "性别:男": { productId: 7 },
      "性别:女": { productId: 8 },
    }
  }
}

同样的,假如我们要查找一个白色的,39码的鞋子,直接tree["颜色:白色"]["尺码:39"]["性别:男"]就行了,这个时间复杂度也是$O(1)$。

写代码

上面算法都弄明白了,剩下的就是写代码了,我们主要需要写的代码就是用API返回的数据构建一个上面的tree这种结构就行了,一次遍历就可以做到。比如上面这个三层查找树对应的API返回的结构是这样的:

const merchandise = {
  variations: [
    {
      name: '颜色',
      values: [
        { name: '白色' },
        { name: '红色' },
      ]
    },
    {
      name: '尺码',
      values: [
        { name: '39' },
        { name: '40' },
      ]
    },
    {
      name: '性别',
      values: [
        { name: '男' },
        { name: '女' },
      ]
    },
  ],
  products: [
    {
      id: 1,
      variationMappings: [
        { name: '颜色', value: '白色' },
        { name: '尺码', value: '39' },
        { name: '性别', value: '男' }
      ]
    }
    // 下面还有7个商品,我就不重复了
  ]
}

为了将API返回的数据转换为我们的树形结构数据我们写一个方法:

function buildTree(apiData) {
  const tree = {};
  const { variations, products } = apiData;

  // 先用variations将树形结构构建出来,叶子节点默认值为null
  addNode(tree, 0);
  function addNode(root, deep) {
    const variationName = variations[deep].name;
    const variationValues = variations[deep].values;

    for (let i = 0; i < variationValues.length; i++) {
      const nodeName = `${variationName}:${variationValues[i].name}`;
      if (deep === 2) {
        root[nodeName] = null
      } else {
        root[nodeName] = {};
        addNode(root[nodeName], deep + 1);
      }
    }
  }

  // 然后遍历一次products给树的叶子节点填上值
  for (let i = 0; i < products.length; i++) {
    const product = products[i];
    const { variationMappings } = product;
    const level1Name = `${variationMappings[0].name}:${variationMappings[0].value}`;
    const level2Name = `${variationMappings[1].name}:${variationMappings[1].value}`;
    const level3Name = `${variationMappings[2].name}:${variationMappings[2].value}`;
    tree[level1Name][level2Name][level3Name] = product;
  }

  // 最后返回构建好的树
  return tree;
}

然后用上面的API测试数据运行下看下效果,发现构建出来的树完全符合我们的预期:

image-20201117173553941

这就好了吗?

现在我们有了一颗查找树,当用户选择红色40码后,为了知道对应的可不可以点,我们不需要去遍历所有的商品了,而是可以直接从这个结构上取值。但是这就大功告成了吗?并没有!再仔细看下我们构建出来的数据结构,层级关系是固定的,第一层是颜色,第二层是尺码,第三层是性别,而对应的商品是放在第三层性别上的。也就是说使用这个结构,用户必须严格按照,先选颜色,再选尺码,然后我们看看性别这里哪个该灰掉。如果他不按照这个顺序,比如他先选了性别,然后选尺码40,这时候我们应该计算最后一个层级颜色哪些该灰掉。但是使用上面这个结构我们是算不出来的,因为我们并没有tree["性别:男"]["尺码:40"]这个对象。

这怎么办呢?我们没有性别-尺码-颜色这种顺序的树,那我们就建一颗呗!这当然是个方法,但是用户还可能有其他的操作顺序呀,如果我们要覆盖用户所有可能的操作顺序,总共需要多少树呢?这其实是性别尺码颜色这三个变量的一个全排列,也就是$A_3^3$,总共6颗树。像我这样的懒人,让我建6棵树,我实在懒得干。如果不建这么多树,需求又覆盖不了,怎么办呢,有没有偷懒的办法呢?如果我能在需求上动点手脚,是不是可以规避这个问题?带着这个思路,我想到了两点:

1. 给一个默认值。

用户打开商品详情页的时候,默认选中第一个可售商品。这样就相当于我们一开始就帮用户按照颜色-尺码-性别这个顺序选中了一个值,给了他一个默认的操作顺序。

2. 不提供取消功能,只能切换选项

如果提供取消功能,他将我们提供的颜色-尺码-性别默认选项取消掉,又可以选成性别-尺码-颜色了。不提供取消功能,只能通过选择其他选项来切换,只能从红色换成白色,而不能取消红色,其他的一样。这样我们就能永远保证颜色-尺码-性别这个顺序,用户操作只是只是每个层级选中的值不一样,层级顺序并不会变化,我们的查找树就一直有效了。而且我发现某些购物网站也不能取消选项,不知道他们是不是也遇到了类似的问题。

对需求做这两点修改并不会对用户体验造成多大影响,跟产品经理商量后,她也同意了。这样我就从需求上干掉了另外5棵树,偷懒成功!

下面是三层选项跑起来的样子:

Nov-18-2020 17-42-28

还有一件事

前面的方案我们解决了查找的性能问题,但是引入了一个新问题,那就是需要创建这颗查找树。创建这颗查找树还是需要对商品列表进行一次遍历,这是不可避免的,为了更顺滑的用户体验,我们应该尽量将这个创建过程隐藏在用户感知不到的地方。我这里是将它整合到了商品详情页的加载状态中,用户点击进入商品详情页,我们要去API取数据,不可避免的会有一个加载状态,会转个圈什么的。我将这个遍历过程也做到了这个转圈中,当API数据返回,并且查找树创建完成后,转圈才会结束。这在理论上会延长转圈的时间,但是本地的遍历再慢也会比网络请求快点,所以用户感知并不明显。当转圈结束后,所有数据都准备就绪了,用户操作都是$O(1)$的复杂度,做到了真正的丝般顺滑~

为什么不让后端创建这棵树?

上面的方案都是在前端创建这颗树,那有没有可能后端一开始返回的数据就是这样的,我直接拿来用就行,这样我又可以偷懒了~我还真去找过后端,可他给我说:“我也想偷懒!”开个玩笑,真是情况是,这个商品API是另一个团队维护的微服务,他们提供的数据不仅仅给我这一个终端APP使用,也给公司其他产品使用,所以要改返回结构涉及面太大,根本改不动。

封装代码

其实我们这个方案实现本身是比较独立的,其他人要是用的话,他也不关心你里面是棵树还是颗草,只要传入选择条件,能够返回正确的商品就行,所以我们可以将它封装成一个类。

class VariationSearchMap {
    constructor(apiData) {
        this.tree = this.buildTree(apiData);
    }

      // 这就是前面那个构造树的方法
    buildTree(apiData) {
        const tree = {};
        const { variations, products } = apiData;

        // 先用variations将树形结构构建出来,叶子节点默认值为null
        addNode(tree, 0);
        function addNode(root, deep) {
            const variationName = variations[deep].name;
            const variationValues = variations[deep].values;

            for (let i = 0; i < variationValues.length; i++) {
                const nodeName = `${variationName}:${variationValues[i].name}`;
                if (deep === variations.length - 1) {
                    root[nodeName] = null;
                } else {
                    root[nodeName] = {};
                    addNode(root[nodeName], deep + 1);
                }
            }
        }

        // 然后遍历一次products给树的叶子节点填上值
        for (let i = 0; i < products.length; i++) {
            const product = products[i];
            const { variationMappings } = product;
            const level1Name = `${variationMappings[0].name}:${variationMappings[0].value}`;
            const level2Name = `${variationMappings[1].name}:${variationMappings[1].value}`;
            const level3Name = `${variationMappings[2].name}:${variationMappings[2].value}`;
            tree[level1Name][level2Name][level3Name] = product;
        }

        // 最后返回构建好的树
        return tree;
    }

    // 添加一个方法来搜索商品,参数结构和API数据的variationMappings一样
    findProductByVariationMappings(variationMappings) {
        const level1Name = `${variationMappings[0].name}:${variationMappings[0].value}`;
        const level2Name = `${variationMappings[1].name}:${variationMappings[1].value}`;
        const level3Name = `${variationMappings[2].name}:${variationMappings[2].value}`;

        const product = this.tree[level1Name][level2Name][level3Name];

        return product;
    }
}

然后使用的时候直接new一下就行:

const variationSearchMap = new VariationSearchMap(apiData);    // new一个实例出来

// 然后就可以用这个实例进行搜索了
const searchCriteria = [
    { name: '颜色', value: '红色' },
    { name: '尺码', value: '40' },
    { name: '性别', value: '女' }
];
const matchedProduct = variationSearchMap.findProductByVariationMappings(searchCriteria);
console.log('matchedProduct', matchedProduct);    // { productId: 8 }

总结

本文讲述了一个我工作中实际遇到的需求,分享了我的实现和优化思路,供大家参考。我的实现方案不一定完美,如果大家有更好的方案,欢迎在评论区讨论~

本文可运行的示例代码已经上传GitHub,大家可以拿下来玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/DataStructureAndAlgorithm/OptimizeVariations

下面再来回顾下本文的要点:

  1. 本文要实现的需求是一个商品的三层选项。
  2. 当用户选择了两层后,第三层选项应该自动计算出哪些能卖,哪些不能卖。
  3. 鉴于后端API返回选项和商品间没有直接的对应关系,为了找出能卖还是不能卖,我们需要遍历所有商品。
  4. 当总商品数量不多的时候,所有商品遍历可能不会产生明显的性能问题。
  5. 但是当选项增加到三层,商品数量的增加是指数级的,性能问题就会显现出来。
  6. 对于$O(n^3)$这种写代码时就能预见的性能问题,我们不用等着报BUG了才处理,而是开发时直接就解决了。
  7. 本例要解决的是一个查找问题,所以我想到了建一颗树,直接将$O(n^3)$的复杂度降到了$O(1)$。
  8. 但是一颗树并不能覆盖所有的用户操作,要覆盖所有的用户操作需要6棵树。
  9. 出于偷懒的目的,我跟产品经理商量,调整了需求和交互砍掉了5颗树。真实原因是树太多了,会占用更多的内存空间,也不好维护。有时候适当的调整需求和交互也可以达到优化性能的效果,性能优化可以将交互和技术结合起来思考。
  10. 这个树的搜索模块可以单独封装成一个类,外部使用者,不需要知道细节,直接调用接口查找就行。
  11. 前端会点数据结构还是有用的,本文这种场景下还很有必要。

文章的最后,感谢你花费宝贵的时间阅读本文,如果本文给了你一点点帮助或者启发,请不要吝啬你的赞和GitHub小星星,你的支持是作者持续创作的动力。

作者博文GitHub项目地址: https://github.com/dennis-jiang/Front-End-Knowledges

我也搞了个公众号[进击的大前端],不打广告,不写水文,只发高质量原创,欢迎关注~

查看原文

赞 37 收藏 24 评论 14

robin 回答了问题 · 11月24日

taro小程序

情况一,看下这个链接有关子组件componentDidShow不执行的原因 https://github.com/NervJS/tar...

image.png
排查下是不是微信开发工具基础库版本低

情况二,子组件的 componentDidShow 是跟随页面的 componentDidShow 的,所以组件动态切换是不会触发组件的 componentDidShow,你应该用子组件的 componentWillRecieveProps

关注 3 回答 2

robin 回答了问题 · 11月24日

解决thinkphp 配合 element 框架使用问题。

其问题出在this 指向,按照你现在这种写的方式就不能这么用
首先确保引入 element-ui 库

<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script data-original="https://unpkg.com/element-ui/lib/index.js"></script>

然后执行

<script> 
    axios.interceptors.response.use(response => {
        return response;
    }, err => {
        console.log(err.response.data.msg);
        window.ELEMENT.Notification({
            title: err.response.data.msg,
            message: "",
            duration: 3000
        })
        Promise.reject(err);
    }) 
</script>

关注 2 回答 1

robin 赞了回答 · 11月24日

解决js 数组内嵌对象(json结构),知道路径怎么去修改内容?

Lodash 好用啊

_.set(obj, path, value) 就可以 (在文档目录上面的搜索栏搜索 set,在 Object 分类下)

官方示例:

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.set(object, 'a[0].b.c', 4);

console.log(object.a[0].b.c);

// => 4

_.set(object, ['x', '0', 'y', 'z'], 5);

console.log(object.x[0].y.z);

// => 5

关注 4 回答 3

robin 回答了问题 · 11月24日

ThingJS 技术分享:建筑建模常用软件一览 3D 可视化

可以去发布一遍文章

关注 2 回答 1

robin 回答了问题 · 11月23日

vue路由上携带很多参数好吗

搜了一下没发现,有关参数长度的限制,但还是不建议太多,即便不丢失,也有可能存在数据不一致的情况,看你使用场景吧

关注 3 回答 2

robin 赞了文章 · 11月23日

美的与 HarmonyOS 强强联合,共同布局万物互联时代

2020年9月10日,在华为开发者大会 2020 现场,华为常务董事、消费者业务 CEO 余承东正式发布 HarmonyOS 2.0 版本,并公布了国内首批率先加入 HarmonyOS 生态的重要合作伙伴名单,美的集团榜上有名。

作为国内 HarmonyOS 生态先行者,美的集团在今年双 11 隆重推出十多款搭载 HarmonyOS 系统的家电产品,包括净水器、热水器、洗碗机、蒸烤料理炉等家电设备,这也是目前加入 HarmonyOS 生态中推出家电产品数量最多的企业。

美的 x HarmonyOS

那么美的集团为什么选择率先加入 HarmonyOS 生态?推出如此多搭载 HarmonyOS 系统的家电设备究竟能带来哪些商业价值?美的与 HarmonyOS 的合作究竟是想达成怎样的目标?美的 IoT 智能连接专家陈挺博士在不久前接受了采访,分享了美的与 HarmonyOS 合作背后的种种战略思考与创新探索。

https://www.qq.com/video/h320...

共同发力 IoT,美的与 HarmonyOS 一拍即合

陈挺坦言,之所以选择与 HarmonyOS 合作,最大的驱动力是因为美的集团在万物互联时代的发展战略,与 HarmonyOS 的服务支撑能力高度契合。

他透露,5G 商用加速了万物互联时代的到来,消费者对跨品类、跨行业、跨技术的万物互联的体验感提出了更高要求。美的意识到,智能家居企业必须具备横向整合家居服务产业链资源的能力,实现更多设备和服务的互联互通,打造开放共赢构建智慧生态圈才是致胜的关键。而 HarmonyOS 的出现,无疑是美的 IoT 未来发展的“神助攻”,不仅让更多终端设备互相连接,打破单一物理设备硬件能力的局限,而且能够实现不同硬件间能力互补和性能增强。

“HarmonyOS 具备分布式、多端部署、安全性和易开发等特点,这些无一不是5G时代中物联网发展的关键,因此顺理成章地成为美的 IoT 践行打造‘懂你的智慧生活’理念的最优选择。”

2020年7月,美的与华为共同宣布升级为战略合作伙伴,为消费者打造全场景智慧生活体验。针对 HarmonyOS 合作,双方成立联合项目组共研共创。在 9 月份的华为开发者大会 2020 现场,美的携手华为打造了一个美的品牌智慧厨房专区,其中搭载了 HarmonyOS 系统的家用空调、净水器、蒸烤料理炉等多个品类都是行业首发,吸引了众多参会者前来体验。

以外,美的和 HarmonyOS 团队还投入了大量精力一起打造了一套高效的智能家电开发流程。未来,智能家电的开发者可以基于这套流程进行搭载 HarmonyOS 的智能家电产品的快速研发和落地,共同推动智能家居产业的升级。

带来全新操控体验,HarmonyOS 让美的与消费者“双赢”

那么当美的设备接入 HarmonyOS 之后,究竟给消费者带来了哪些新体验呢?又给美的创造了哪些商业价值?

陈挺表示,“碰一碰”功能可能是最让消费者惊喜的操控功能了。在终端硬件上,消费者可以通过搭载 EMUI 11 的华为手机快速发现可连接的基于 HarmonyOS 打造的美的智能设备,碰一碰便可以实现 3 秒快速配网,无需下载应用,大大简化了配网方式,且配网成功率接近 100%。

在交互体验上,用户可通过多个入口进入美的智能家电控制页面,除了通过碰一碰的方式进行配网操控美的智能家电设备,还可以通过语音进行控制。在用户服务方面,美的与华为共同给用户打造无处不在的全周期服务体验,例如当消费者使用搭载有 HarmonyOS 的美的智能电饭煲完成米饭烹饪,华为手机或智能手表就会发送信息提示用户米饭已烹饪完成;当美的智能净水器滤芯需要更换时,用户也会收到搭载 EMUI11 的华为手机发送的提示,从耗材购买到售后服务都可以在搭载 EMUI11 的手机上完成。

美的 x HarmonyOS

“毫无疑问,HarmonyOS 在一定程度上提高了美的 IoT 设备联网速度,目前由于用户所处环境网络等问题会导致联网时间较长,家电设备搭载了 HarmonyOS 之后能够让这个连接过程减少至 3s,这让美的的 IoT 设备拥有差异化体验,大幅领先同质竞争产品。”陈挺表示。

HarmonyOS因何不同?

从事家电研发的专业人士都清楚,家电产品研发周期比较长,但是在美的 HarmonyOS 项目中,双方从 7 月立项到 11 月产品上市、供货双十一,其研发速度之快简直可谓是创造了业内奇迹。

这离不开美的团队的重视以及华为 HarmonyOS 团队给予的支持与帮助。据陈挺介绍,对于 HarmonyOS 这个项目,美的集团和各个事业部都非常的重视,包括七大产品事业部的研发,产品市场的人员以及 IoT 事业部相关人员都投入了大量的工作。

此外在整个项目过程中,华为 HarmonyOS 的团队同事从研发、测试到产品认证的各个方面都给予了美的充分的技术支持,在联合项目组成立以后,HarmonyOS 项目组的核心成员是直接被派驻到美的的办公室进行专门产品研发的现场支持,这些都为美的搭载 HarmonyOS 的产品能够顺利、按时上市提供了强有力的保障。

美的 HarmonyOS 项目进展之快也从另一个角度证明了 HarmonyOS 系统的易用性,更容易接入家电产品。例如开发工具平台 HUAWEI DevEco 提供了一站式集成开发环境,能够支持 HarmonyOS 组件按需定制、一键编译和烧录、可视化调试、分布式能力集成等操作,极大地提升了开发效率。

在运用 HarmonyOS 进行产品创新过程中,陈挺也明显感受到 HarmonyOS 系统和其他物联网 OS 平台的不同:

对于消费者而言,HarmonyOS 提供了极致的新一代连接体验,例如碰一碰的“无感配网”能够快速简化网络配置,并且直接调用服务无需安装和频繁点击 APP;

对于开发者而言,HarmonyOS 系统具备组件化和轻量级的设计,在性能、硬件功耗等方面充分优化,大大降低开发门槛,让设备开发能够很容易接入到 HarmonyOS 生态圈中;

对于设备厂商而言,无需再与其他设备厂商一一沟通共同合作来提供服务,因为基于同样的 HarmonyOS 系统,大家拥有一致性的体验、一致性的开发框架。设备厂商能够在 HarmonyOS 系统下形成一个超级终端,共享各自的能力,为消费者打造更好的生活体验。

在陈挺看来,HarmonyOS 是一个跨时代创新的操作系统,它不仅解决了 IoT 生态碎片化问题,而且能够给设备商、服务商提供同一个平台向消费者提供一致的服务,让人们不再为了享受智能生活动辄安装数十个 APP,也不再需要一个个手动设置设备联动,真正给人们呈现出一个智能化的生活场所。“HarmonyOS 值得期待,更值得参与其中。”

segmentfault 思否

查看原文

赞 6 收藏 0 评论 0

robin 赞了回答 · 11月23日

解决js 数组内嵌对象(json结构),知道路径怎么去修改内容?

简单实现:

function pathValue(obj, path, newValue) {  
  const pathPartLength = path.length;
  const lastPathPart = path[pathPartLength - 1];

  for(let i = 0; i < pathPartLength - 1; ++i) {
    if(obj[path[i]]) {
      obj = obj[path[i]];
    }
  }

  const oldVal = obj[lastPathPart];
  // Set操作
  if(arguments.length > 2) {
    obj[lastPathPart] = newValue;
  }
  // 返回末梢节点值
  return oldVal;
}

var data = [
  {
    "t": "h",
    "c": [
      {
        "t": "t",
        "c": "aaaa"
      },
      {
        "t": "t",
        "c": "bbbbbbbb"
      },
      {
        "t": "t",
        "c": "cccccccc"
      }
    ]
  }
]

// Get
console.log(pathValue(data, [0, "c", "0"])) // {t: "t", c: "aaaa"}

// Set
console.log(pathValue(data, [0, "c", "0"], 'newValue')) // {t: "t", c: "aaaa"}
console.log(data); // [{"t":"h","c":["newValue",{"t":"t","c":"bbbbbbbb"},{"t":"t","c":"cccccccc"}]}]

可以借助这个库diffy-update

关注 4 回答 3

robin 回答了问题 · 11月23日

解决js 数组内嵌对象(json结构),知道路径怎么去修改内容?

可以借助循环实现

const array = [
  {
    "t": "h",
    "c": [
      {
        "t": "t",
        "c": "aaaa"
      },
      {
        "t": "t",
        "c": "bbbbbbbb"
      },
      {
        "t": "t",
        "c": "cccccccc"
      }
    ]
  }
]
const path = [0, "c", "0"];
let result = array;
for(var i = 0;i<path.length;i++) {
    result = result[path[i]]
}
console.log(result)
result.c='22222222'
console.log(array)

关注 4 回答 3

认证与成就

  • 获得 56 次点赞
  • 获得 12 枚徽章 获得 0 枚金徽章, 获得 2 枚银徽章, 获得 10 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-07-09
个人主页被 1.2k 人浏览