降低前端业务复杂度新视角:状态机范式

发表于 2年以前  | 总阅读数:293 次

无论做业务需求还是做平台需求的同学,随着需求的不断迭代,通常都会出现逻辑复杂、状态混乱的现象,维护和新增功能的成本也变的十分巨大,苦不堪言。下图用需求、业务代码、测试代码做对比:

图中分了 3 个阶段:

  • 阶段 1:正常,都是线性增长。
  • 阶段 2:需求数正常增长,业务代码行数开始增长,测试代码行数大幅度增长。
  • 阶段 3:业务代码行数开始大幅增长,测试代码行数剧增(超出屏幕),而需求数开始下降。

这可以很好的表达出,从业务最开始,到长期迭代后,复杂度提升带来的问题。做一个相同的需求,最开始可能 1 天就可以搞定,但长期迭代后,可能要 3 天,甚至更多,这并不是开发人员主观上导致的,而是代码状态的维护成本太高,做到最后经常会出现牵一发而动全身。也侧面抑制了业务的迭代速度。

所以对于长期迭代的产品,切记不要简单做,否则都是给后面挖的坑。

当然,看问题还是要去看本质。根据复杂度守恒定律(泰斯勒定律),每个应用程序都具有其内在的、无法简化的复杂度。这一固有的复杂度都无法依照我们的意愿去除,只能设法调整、平衡。而现在前端的复杂度拆分主要包括:框架、通用组件、业务组件和业务逻辑,如下图所示:

上图中可以看到,当把框架和通用组件建设完成后,能够承担的复杂度基本稳定了,未来无轮再怎么改善或者更换其他框架,也很难再去突破天花板,对业务的复杂度的改变也微乎其微了(如果你的业务经历过底层框架更换,你就能体会到它到底对你的业务复杂度有没有带来变化了)。

我们就要去思考,到底哪里还能把复杂度给降下来。换个角度,是不是可以从业务共有的 “业务逻辑” 侧去进行突破?目前发现的,做业务侧提效的方案中,很少有从 “业务逻辑” 视角为出发点去做的,更多的是聚焦在场景化上的提效。

把视角聚焦到 “业务逻辑” 侧,这里就要看所有业务中都会面临的问题,是什么让业务复杂度提升上去了。这里主要存在两点,如下:

  • 代码层面

  • 各种各样的业务状态导致的 flag 变量的剧增:即便是自己,写多了这种变量,也很难清楚的知道每个 flag 是干什么用的。

  • 各种判断业务状态的 if/elseif/else 嵌套地狱估计在很多大型的业务产品中都能看到吧。还有内部的各种逻辑判断,如 isA && isB || !(isC || !isD && isE),完全看不懂,即便问 PD,时间久了她也不知道了。还有因此可能导致一些意识不到的 Bug。

  • 协作层面

  • 做业务的同学很难有全局业务视角,所以面对 PD 的需求很难有话语权。如果需求设计不合理,只能等到你做完了,在 UAT 的阶段才能发现,然后 PD 会给你提一个新需求,让你再去修正(虽然是 PD 的问题,但缺乏避免 PD 犯错的途径)。

  • 测试同学,测试的内容范围,多数情况下,取决于前端同学给定的测试范围。而很多时候代码的改动,前端也不确定到底哪些页面会受影响。所以要么导致测试同学测试不完整,要么导致测试同学需要全量回归,这可是非常巨大的测试成本。

  • 当其他前端开发人员,参与到项目中时,面临这种复杂的项目也是头大,需要花费很大的成本梳理清楚业务与代码的关联。导致合作或者交接项目时,困难。

我们需要通过发现的这些问题,来寻找合适的解决方案。

1 . 解决代码层面的问题

代码层面的问题,主要来源于 flag 变量过多,及 if/else 的嵌套及大量分支,导致难以修改和扩展,任何改动和变化都是致命的。其实这类问题,在设计模式中是有合适的方案——状态模式。

1.1. 状态模式

状态模式主要解决的是,当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,减少相互间的依赖,可以把复杂的判断逻辑简化。

「状态模式是一种行为模式,在不同的状态下有不同的行为,它将状态和行为解耦。」

从类图中可以看到,状态模式是多态特性和面向接口的完美体现,State 是一个接口,表示状态的抽象,ConcreteStateAConcreteStateB 是具体的状态实现类,表示两种状态的行为,Contextrequest() 方法将会根据状态的变更从而调用不同 State 接口实现类的具体行为方法。

状态模式的好处是,「将与特定状态相关的行为局部化,并且将不同状态的行为分割开来」。这样这些对象就可以不依赖于其他对象而独立变化了,未来增加或修改状态流程,就不是困难的事了。

当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。

1.2. 状态机

状态机,全称有限状态机(finite-state machine,缩写:FSM),又称有限状态自动机(finite-state automaton,缩写:FSA),是现实事物运行规则抽象而成的一个数学模型,并不是指一台实际机器。状态机是图灵机的一个子集。它是一种认知论。从某种角度来说,我们的现实世界就是一个有限状态机。

有限状态自动机在很多不同领域中是重要的,包括电子工程、语言学、计算机科学、哲学、生物学、数学和逻辑学。有限状态机是在自动机理论和计算理论中研究的一类自动机。在计算机科学中,有限状态机被广泛用于建模应用行为、硬件电路系统设计、软件工程,编译器、网络协议、和计算与语言的研究。它是非常成熟的一套方法论。

有限状态机包含五个重要部分:

  • 初始状态值 (initial state)
  • 有限的一组状态 (states)
  • 有限的一组事件 (events)
  • 由事件驱动的一组状态转移关系 (transitions)
  • 有限的一组最终状态 (final states)

更简洁的总结,就三个部分:

  • 状态 State
  • 事件 Event
  • 转换 Transition

同一时刻,只可能存在一个状态。例如,人有 “睡着” 和 “醒着” 两个状态,同一时刻,要么 “睡着” 要么 “醒着”,不可能存在 “半睡半醒” 的状态。

逻辑学中说,现实生活中描述的事物都可以抽象为命题。命题本质上就是状态机的 State,Event 就是命题的条件,通过命题和条件推导过程。而 Transition 就是命题推导完成的结论。

所以当我们拿到需求的时候,首先要分离出哪些是已知的命题(State),哪些是条件(Event),哪些是结论(Transition)。而我们要通过这些已知命题和条件,推导出结论的过程。

1.2.1. 拿我们经常用到的 Fetch API 来举例子

fetch(url).then().catch()

有限的一组状态:

初始状态:

有限的一组最终状态:

有限的一组事件:

  • Idle 状态只处理 FETCH 事件
  • Pending 状态只处理 RESOLVEREJECT 事件

由事件驱动的一组状态转移关系:

1.3. 状态机 VS 传统编码 示例

下面采用一个小需求来对比一下区别。

1.3.1. 需求描述

根据输入的关键字进行搜索,并将搜索结果显示出来。如下图所示:

1.3.2. 基于传统编码

根据关键字拿到请求结果,再将结果塞回去就行了,代码如下:

function onSearch(keyword) {
  fetch(SEARCH_URL + "?keyword=" + keyword).then((data) => {
    this.setState({ data });
  });
}

看似几行代码就把这个需求搞定了,但其实还有一些其他问题要处理。如果接口响应比较慢,则需要给一个用户预期的交互,如 Loading 效果:

function onSearch(keyword) {
  this.setState({
    isLoading: true,
  });

  fetch(SEARCH_URL + "?keyword=" + keyword).then((data) => {
    this.setState({ data, isLoading: false });
  });
}

还会发生出请求出错的情况:

function onSearch(keyword) {
  this.setState({
    isLoading: true,
  });

  fetch(SEARCH_URL + "?keyword=" + keyword)
    .then((data) => {
      this.setState({ data, isLoading: false });
    })
    .catch((e) => {
      this.setState({
        isError: true,
      });
    });
}

当然,不能忘记把 Loading 关掉:

function onSearch(keyword) {
  this.setState({
    isLoading: true,
  });

  fetch(SEARCH_URL + "?keyword=" + keyword)
    .then((data) => {
      this.setState({ data, isLoading: false });
    })
    .catch((e) => {
      this.setState({
        isError: true,
        isLoading: false,
      });
    });
}

我们每次搜索时,还需要把错误清除:

function onSearch(keyword) {
  this.setState({
    isLoading: true,
    isError: false,
  });

  fetch(SEARCH_URL + "?keyword=" + keyword)
    .then((data) => {
      this.setState({ data, isLoading: false });
    })
    .catch((e) => {
      this.setState({
        isError: true,
        isLoading: false,
      });
    });
}

这就结束了么,是不是我们把所有的 Bug 都考虑进去了?并没有。当用户在等待搜素请求的时候,不应该再去搜索,所以搜索结果返回前,禁止再次发送请求:

function onSearch(keyword) {
  if (this.state.isLoading) {
    return;
  }

  this.setState({
    isLoading: true,
    isError: false,
  });

  fetch(SEARCH_URL + "?keyword=" + keyword)
    .then((data) => {
      this.setState({ data, isLoading: false });
    })
    .catch((e) => {
      this.setState({
        isError: true,
        isLoading: false,
      });
    });
}

可以看到,应用的复杂度在不断变大,可能你经历的场景比这个小示例还要复杂的多的多。如果因为搜索接口特别慢,用户希望有一个中断搜索的功能,那么新的需求又来了:

function onSearch(keyword) {
  if (this.state.isLoading) {
    return;
  }
  this.fetchAbort = new AbortController();

  this.setState({
    isLoading: true,
    isError: false,
  });

  fetch(SEARCH_URL + "?keyword=" + keyword, {
    signal: this.fetchAbort.signal,
  })
    .then((data) => {
      this.setState({ data, isLoading: false });
    })
    .catch((e) => {
      this.setState({
        isError: true,
        isLoading: false,
      });
    });
}

function onCancel() {
  this.fetchAbort.abort();
}

不能落下对 catch 的特殊处理,因为中断请求会触发 catch:

function onSearch(keyword) {
  if (this.state.isLoading) {
    return;
  }
  this.fetchAbort = new AbortController();

  this.setState({
    isLoading: true,
    isError: false,
  });

  fetch(SEARCH_URL + "?keyword=" + keyword, {
    signal: this.fetchAbort.signal,
  })
    .then((data) => {
      this.setState({ data, isLoading: false });
    })
    .catch((e) => {
      if (e.name == "AbortError") {
        this.setState({
          isLoading: false,
        });
      } else {
        this.setState({
          isError: true,
          isLoading: false,
        });
      }
    });
}

function onCancel() {
  this.fetchAbort.abort();
}

最后还要处理没有值的情况:

function onSearch(keyword) {
  if (this.state.isLoading) {
    return;
  }
  this.fetchAbort = new AbortController();

  this.setState({
    isLoading: true,
    isError: false,
  });

  fetch(SEARCH_URL + "?keyword=" + keyword, {
    signal: this.fetchAbort.signal,
  })
    .then((data) => {
      this.setState({ data, isLoading: false });
    })
    .catch((e) => {
      if (
        e && 
        e.name == "AbortError"
      ) {
        this.setState({
          isLoading: false,
        });
      } else {
        this.setState({
          isError: true,
          isLoading: false,
        });
      }
    });
}

function onCancel() {
  if (
    this.fetchAbort.abort &&
    typeof this.fetchAbort.abort == "function"
  ) {
    this.fetchAbort.abort();
  }
}

仅仅这么简单的一个小需求,从开始几行代码就可以完成,到最终判断各种边界完成的代码,对比一下,如下图所示:

可以看到,这种包含各种 flag 变量和嵌套着各种 if/else 的代码,会越来越难维护,所有的逻辑只存在于你的脑子里。当你写测试的时候必须从头再梳理一遍代码逻辑,才能写出来。

由于业务的高频变化,很多业务开发人员是不写单元测试的,因为成本太高太高,这也导致了交接代码时,别人去理解你的代码是一件很困难的事。写久了,你自己都可能读不懂代码里面的逻辑了。

这样会导致:

  • 难以测试
  • 难以阅读
  • 可能含有隐藏的 Bug
  • 难以扩展
  • 新功能增加时还会使逻辑进一步混乱

1.3.3. 基于状态机

看一下我们用状态机的做法。记住流程:梳理出有哪些状态,每个状态有哪些事件,经历了这些事件又会转换到什么状态。

下面是用 XState 状态机工具的 JSON 描述:

{
  "initial": "空闲",
  "states": {
    "空闲": {
      "on": {
        "搜索": "搜索中"
      }
    },
    "搜索中": {
      "on": {
        "搜索成功": "成功",
        "搜索失败": "失败",
        "取消": "空闲"
      }
    },
    "成功": {
      "on": {
        "搜索": "搜索中"
      }
    },
    "失败": {
      "on": {
        "搜索": "搜索中"
      }
    }
  }
}

没错,就这几行代码就描述清楚所有的关系了。并且,可以把它可视化出来,如下图所示:

可以看到状态之间表达的非常清晰,结合到 View 中,也不需要再去编写复杂的 flagif/else 了,View 中只需要知道当前是什么状态,已及将事件发送到状态机就可以了,其他什么都不需要做。在新增或者修改需求的情况下,只需要对状态进行新增或者编排就可以了。

而且可视化后,有以下变化:

  • 清晰的看到有哪些状态
  • 清晰的看到每个状态可以接受哪些事件
  • 清晰的看到接受到事件后会转移到什么状态
  • 清晰的看到到达某个状态的路径是怎么样的

2 . 解决协作的问题

另一个很大的问题是解决协作问题,主要包括:

  • 与测试开人员的协作沟通
  • 与 PD 人员的协作沟通
  • 与其他前端开发人员的协作沟通
  • 与用户的协作沟通

这里就需要引用一个可视化的概念了。「可视化,是利用人眼的感知能力对数据进行交互的可视表达以增强认知的技术」

所以很大程度上,可视化可以解决一大部分协作问题。当然,必须要确定把什么进行可视化才是有意义的。

要想可视化,状态工具就需要具备可序列化的能力。这也是 Redux 之类的状态管理工具缺乏的,主要有以下几方面问题:

  • 不具备可视化的能力
  • 状态和数据混在一起
  • 所有的状态都是平级的,无法描述状态之间的关系

2.1. 状态图

回到状态机。你单纯用状态机去写代码,需求数量上去了,状态多了,会面临 “状态爆炸” 问题,依然很难维护,且阅读成本巨大。

当然,这个场景其实很早之前就有人考虑到了,1987 年,Harel 就发表论文,解决复杂状态机可视化的问题,在状态机的基础上进一步增强,提出状态图的概念。随后,由微软、IBM、惠普等多家公司,从 2005 到 2015 年花了 10 年时间制定了规范,并推出了 W3C 的 State Chart XML (SCXML) 规范,至此基本稳定,各家编程语言也基于此规范进行了状态图的封装。

看一下,状态机、状态图和手写代码复杂度的对比,如下图所示:

从图中可以看到:

  • 传统编码方式,随着状态和逻辑的增加,复杂度是线性增长的。
  • 使用状态机,前期复杂度很底,但随着状态的增多,“状态爆炸”现象的出现,复杂度也急剧增长。
  • 使用状态图,虽然前期成本略高,但后期的状态和逻辑的增长,基本不太会影响它的复杂度。

前面给状态机画的图,就是状态图。

状态图大概长这样,如下图所示:

主要包括:

  • 状态

  • 原子状态

  • 复合状态

  • 条件状态

  • 最终状态

  • 历史状态

  • 初始状态

  • 并行状态

  • 伪/瞬间状态

  • 转换

  • 自动转换

  • 延迟转换

  • 自身转换

  • 内部转换

  • 操作

  • 自定义操作

  • 进入操作

  • 退出操作

  • 数据操作

  • 日志操作

  • 事件

  • 生成事件

  • 延迟时间

  • 条件

  • 数据

  • 调用

即使状态非常复杂,也可以通过状态图的模式进行聚合、分组、细化,还可以通过 Actor 模型进行划分,不会发生 “状态爆炸” 现象。

2.2. 文档化

目前对项目需求的描述主要有:

  • 产品需求文档(PRD)
  • 设计稿

而这两个,在描述页面行为上都不够细致,PRD 几乎不会去描述过于细节的交互行为,设计稿大概率也不会(因为业务交付周期上不允许在这上面花费太多的时间)。而对于这些不清楚的、模糊的点,就带来了后面的问题,针对于这些细节点,各个角色之间的沟通成本和拉通成本。

还有一个很严重的问题,就是同步问题。很多时候在开发过程中,进行需求变动,而大多数情况下,这些变动不会重新对 PRD 和设计稿进行修改,不同角色之间去对焦及未来回顾,都是问题。

而如果你使用状态机开发,那这两个问题就可以迎刃而解。状态机方式,要求你在开发之前必须把所有可能的状态都罗列出来,状态之间的关联关系必须描述清晰。基于生成的状态图,是可以完全表达清楚所有的状态交互及变化,且它是来源于代码的,所以它是实时同步的,你代码中怎么运行的,这个状态图就是怎么表达的。

2.3. 角色影响

回到前面说的,与不同角色协作的问题上。有了状态图的加持,会发生什么变化:

  • 设计师可以根据状态图中的不同状态,来确定哪种状态合适用什么样的 UI。
  • 对于 PD,可以查看状态图,以了解系统行为,并验证是否满足要求。
  • 对于测试和用户,状态图完全充当说明书用,以前不知道如何才能到达某个状态,现在一目了然。
  • 对于测试还有一个很大的区别,因为基于状态机去写的,所以可以使用 Model-Based Testing,而这部分测试,可以由某些状态机工具自动化掉。
  • 对于交接的前端开发来说,有说明书在手,每个状态都十分清晰,能做的事也十分清晰,在具备状态机基础的情况下,是可以快速上手的。

2.4. 提升用户体验度:用户操作链路追踪和分析

除了解决复杂度的问题,基于状态机的特性,还可以带来一些新的思路,如用户操作链路追踪和分析。

2.4.1. 常见分析用户操作链路方法

目前,针对于分析用户操作链路的方法,主要是在页面中的可操作标签上进行埋点,如,Button、Tab Item 等。有手动埋点和自动埋点。

  • 手动埋点,可以按照你的意愿来收集特定区域的操作数据,但成本偏高,需要一个一个的手动接入,还可能需要自行上报数据。
  • 自动埋点,通常是自动在一些常用的标签上埋点,但会存在具体的标签变更的问题,且不能覆盖所有可操作的区域,数据精度不够。

无论使用哪种埋点,都存在 「回放噪音」 的问题。

如,上报信息里包含,“查看详情” 按钮的操作,那么对应的 “详情对话框” 一定会出来么?这个时候链路回放,只能去猜测,认为点击了这个按钮,就意味着这个对话框出来了。其实是不准确的。

如果,页面上新增加了一个功能,要判断这个新功能用户的使用量,及用户做了哪些操作才找到这个新功能。通过这个数据来判断新的交互设计是否存合理。在这种不精准数据及 “噪音” 的回放中也是不准确的。

同样,分析页面中的哪些部分是高频操作,也有类似的问题。

2.4.2. 基于状态机的链路分析方法

状态机做这种用户链路分析,是天然合适的。因为用户的所有操作,所有行为,本质上就是 “状态在接收了什么事件,要变换到什么状态” 上的过程。这是在 View 上埋点的方式缺乏的。

我们只需要在每次 “状态” 发生转换时,把状态图数据上报到分析平台就可以。完全可以基于状态的方式, 1:1 的回放用户操作链路。

3 . 总结

最后,总结一下状态机方式带来的好处和不足。

3.1. 优势

  • 比传统的编码方式,更容易理解。

  • 基于行为建模,与视图解耦。

  • 更容易改变行为:组件中的行为被提取到了状态机中,与 把行为和业务逻辑一起嵌入的组件相比,行为的更改相对容易。

  • 更容易的理解代码。

  • 更容易测试

  • 构建状态图的过程必须探索所有状态,也是让你具备业务全局视角的过程,它迫使你考虑所有可能发生的场景。

  • 基于状态图的代码比传统代码具有更少的 Bug 数。相关数据表示,错误减少了 80% 到 90%,剩下的错误也很少出现在状态图本身。

  • 有助于处理可能会被忽视的特殊情况。

  • 随着复杂性的增加,状态图可以很好地扩展。

  • 状态图是一个很好的交流工具。

3.2. 带来的一些问题

  • 需要学习新的东西,状态机是一种范式的转化,且容易有抵触心里,不愿意走出舒适圈。

  • 新的格式

  • 新的重构技术

  • 新的调试工具

  • 部分人觉得可视化这种东西,没什么用。

  • 陌生的编码方式,在团队内可能出现不同的阻力。

  • 虽然大多数人听过状态机,但实际的编程中离它遥远,所以并不熟悉它。

  • 编程方式的转换,很多人需要弄清楚原来的代码,现在该如何去写,如何映射。

  • 部分人会质疑它的有效性。

  • 必须有人基于这种模式实践过,对它非常了解才可以。

  • 如果从来没用过它,使用这种模式会无从下手,令人生畏。

3.3. 为什么用的人不多

状态机已经发展几十年了,前面也说过,在非常的多场景有使用,像电子、嵌入式、游戏、通讯等领域。那为什么前端上使用较少呢(限定国内)?

除了上面列出的 “带来的一些问题” 中的一些点,我觉的还有以下问题导致的:

  • 缺少指导图书:现在搜索一下关于状态图的前端图书或者教程,搜索结果告诉你 0 条。资料很少(嵌入式之类的状态机资料还是挺多的)。
  • “用最简单的方式去实现” 的心态:很多人喜欢用 if/else/switch 来解决问题。
  • “你觉得你不需要” 的心态:复杂度在每一个 flag 变量和布尔值中蔓延。就像温水煮青蛙,温水中的青蛙不会注意到温度的缓慢升高一样,开发人员也不会注意到复杂度的蔓延。在一些小的系统中运行的很好,但随着系统的迭代和变大,一个个凌乱的 if/else/switch 语句,它修改了各种变量的状态,以试图维持它们的一致性。就好像你不需要状态机,直到为时已晚。
  • 就像 RxJS、函数式编程之类的一样,大家都知道它很好,但就是不用它。

3.3. 总结

任何解决方案都不能解决一切问题,一定要找到它适合的场景。不过,现阶段,状态机确实是我能看到的,解决复杂业务逻辑最好的工具。

如果文中说的问题也发生在你身边,且无法彻底解决,那推荐你可以尝试一下,或许会有惊喜。

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/B64LPxzqjN0-r310077h-A

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237271次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8108次阅读
 目录