Vue3的v-memo应用在Vue2中,性能提升近10倍

发表于 3年以前  | 总阅读数:1289 次

Table 表格组件在 Web 开发中的应用随处可见,不过当表格数据量大后,伴随而来的是性能问题:渲染的 DOM 太多,渲染和交互都会有一定程度的卡顿。

通常,我们有两种优化表格的方式:一种是分页,另一种是虚拟滚动。这两种方式的优化思路都是减少 DOM 渲染的数量。在我们公司的项目中,会选择分页的方式,因为虚拟滚动不能正确的读出行的数量,会有 Accessibility 的问题。

记得 19 年的时候,我在 Zoom 已经推行了基于 Vue.js 的前后端分离的优化方案,并且基于 ElementUI 组件库开发了 ZoomUI。其中我们在重构用户管理页面的时候使用了 ZoomUI 的 Table 组件替换了之前老的用 jQuery 开发的 Table 组件。

因为绝大部分场景 Table 组件都是分页的,所以并不会有性能问题。但是在某个特殊场景下:基于关键词的搜索,可能会出现 200 * 20 条结果且不分页的情况,且表格是有一列是带有 checkbox 的,也就是可以选中某些行进行操作。

当我们去点选其中一行时,发现过了好久才选中,有明显的卡顿感,而之前的 jQuery 版本却没有这类问题,这一比较令人大跌眼镜。难道好好的技术重构,却要牺牲用户体验吗?

Table 组件第一次优化尝试

既然有性能问题,那么我们的第一时间的思路应该是要找出产生性能问题的原因。

列展示优化

首先,ZoomUI 渲染的 DOM 数量是要多于 jQuery 渲染的 Table 的,因此第一个思考方向是让 Table 组件尽可能地减少 DOM 的渲染数量

20 列数据通常在屏幕下是展示不全的,老的 jQuery Table 实现很简单,底部有滚动条,而 ZoomUI 在这种列可滚动的场景下,支持了左右列的固定,这样在左右滑动过程中,可以固定某些列一直展示,用户体验更好,但这样的实现是有一定代价的。

想要实现这种固定列的布局,ElementUI 用了 6 个 table 标签来实现,那么为什么需要 6 个 table 标签呢?

首先,为了让 Table 组件支持丰富的表头功能,表头和表体都是各自用一个 table 标签来实现。因此对于一个表格来说,就会有 2 个 table 标签,那么再加上左侧 fixed 的表格,和右侧 fixed 的表格,总共有 6 个 table 标签。

在 ElementUI 实现中,左侧 fixed 表格和右侧 fixed 表格从 DOM 上都渲染了完整的列,然后从样式上控制它们的显隐:

但这么实现是有性能浪费的,因为完全不需要渲染这么多列,实际上只需要渲染固定展示的列的 DOM,然后做好高度同步即可。ZoomUI 就是这么实现的,效果如下:

当然,仅仅减少 fixed 表格渲染的列,性能的提升还不够明显,有没有办法在列的渲染这个维度继续优化呢?

这就是从业务层面的优化了,对于一个 20 列的表格,往往关键的列并没有多少,那么我们可不可以初次渲染仅仅渲染关键的列,其它列通过配置方式的渲染呢?

根据上述需求,我给 Table 组件添加了如下功能:

Table 组件新增一个 initDisplayedColumn 属性,通过它可以配置初次渲染的列,同时当用户修改了初次渲染的列,会在前端存储下来,便于下一次的渲染。

通过这种方式,我们就可以少渲染一些列。显然,列渲染少了,表格整体渲染的 DOM 数就会变少,对性能也会有一定的提升。

更新渲染的优化

当然,仅仅通过优化列的渲染还是不够的,我们遇到的问题是当点选某一行引起的渲染卡顿,为什么会引起卡顿呢?

为了定位该问题,我用 Table 组件创建了一个 1000 * 7 的表格,开启了 Chrome 的 Performance 面板记录 checkbox 点选前后的性能。

在经过几次 checkbox 选择框的点选后,可以看到如下火焰图:

其中黄色部分是 Scripting 脚本的执行时间,紫色部分是 Rendering 所占的时间。我们再截取一次更新的过程:

然后观察 JS 脚本执行的 Call Tree,发现时间主要花在了 Table 组件的更新渲染上

我们发现组件的 render to vnode 花费的时间约 600ms;vnode patch to DOM 花费的时间约 160ms。

为什么会需要这么长时间呢,因为点选了 checkbox,在组件内部修改了其维护的选中状态数据,而整个组件的 render 过程中又访问了这个状态数据,因此当这个数据修改后,会引发整个组件的重新渲染。

而又由于有 1000 * 7 条数据,因此整个表格需要循环 1000 * 7 次去创建最内部的 td,整个过程就会耗时较长。

那么循环的内部是不是有优化的空间呢?对于 ElementUI 的 Table 组件,这里有非常大的优化空间。

其实优化思路主要参考我之前写的 [《揭秘 Vue.js 九个性能优化技巧》] 其中的 Local variables 技巧。举个例子,在 ElementUI 的 Table 组件中,在渲染每个 td 的时候,有这么一段代码:

const data = {
  store: this.store,
  _self: this.context || this.table.$vnode.context,
  column: columnData,
  row,
  $index
}

这样的代码相信很多小伙伴随手就写了,但却忽视了其内部潜在的性能问题。

由于 Vue.js 响应式系统的设计,在每次访问 this.store 的时候,都会触发响应式数据内部的 getter 函数,进而执行它的依赖收集,当这段代码被循环了 1000 * 7 次,就会执行 this.store 7000 次的依赖收集,这就造成了性能的浪费,而真正的依赖收集只需要执行一次就足够了。

解决这个问题其实也并不难,由于 Table 组件中的 TableBody 组件是用 render 函数写的,我们可以在组件 render 函数的入口处定义一些局部变量:

render(h) {
  const { store /*...*/} = this
  const context = this.context ||  this.table.$vnode.context
}

然后在渲染整个 render 的过程中,把局部变量当作内部函数的参数传入,这样在内部渲染 td 的渲染中再次访问这些变量就不会触发依赖收集了:

rowRender({store, context, /* ...其它变量 */}) {
  const data = {
    store: store,
    _self: context,
    column: columnData,
    row,
    $index,
    disableTransition,
    isSelectedRow
  }
}

通过这种方式,我们把类似的代码都做了修改,就实现了 TableBody 组件渲染函数内部访问这些响应式变量,只触发一次依赖收集的效果,从而优化了 render 的性能。

来看一下优化后的火焰图:

从面积上看似乎 Scripting 的执行时间变少了,我们再来看它一次更新所需要的 JS 执行时间:

我们发现组件的 render to vnode 花费的时间约 240ms;vnode patch to DOM 花费的时间约 127ms。

可以看到,ZoomUI Table 组件的 render 的时间和 update 的时间都要明显少于 ElementUI 的 Table 组件。render 时间减少是由于响应式变量依赖收集的时间大大减少,update 的时间的减少是因为 fixed 表格渲染的 DOM 数量减少。

从用户的角度来看,DOM 的更新除了 Scripting 的时间,还有 Rendering 的时间,它们是共享一个线程的,当然由于 ZoomUI Table 组件渲染的 DOM 数量更少,执行 Rendering 的时间也更短。

手写 benchmark

仅仅从 Performance 面板的测试并不是一个特别精确的 benchmark,我们可以针对 Table 组件手写一个 benchmark。

我们可以先创建一个按钮,去模拟 Table 组件的选中操作:

<div>
  <zm-button @click="toggleSelection(computedData[1])
">切换第二行选中状态
  </zm-button>
</div>
<div>
  更新所需时间: {{ renderTime }}
</div>

然后实现这个 toggleSelection 函数:

methods: {
 toggleSelection(row) {
   const s = window.performance.now()
   if (row) {
     this.$refs.table.toggleRowSelection(row)
   }
   setTimeout(() => {
     this.renderTime = (window.performance.now() - s).toFixed(2) + 'ms'
   })
 }
}

我们在点击事件的回调函数中,通过 window.performance.now() 记录起始时间,然后在 setTimeout 的回调函数中,再去通过时间差去计算整个更新渲染需要的时间。

由于 JS 的执行和 UI 渲染占用同一线程,因此在一个宏任务执行过程中,会执行这俩任务,而 setTimeout 0 会把对应的回调函数添加到下一个宏任务中,当该回调函数执行,说明上一个宏任务执行完毕,此时做时间差去计算性能是相对精确的。

基于手写的 benchmark 得到如下测试结果:

ElementUI Table组件一次更新的时间约为 900ms。

ZoomUI Table组件一次更新的时间约为 280ms,相比于 ElementUI 的 Table 组件,性能提升了约三倍

v-memo 的启发

经过这一番优化,基本解决了文章开头提到的问题,在 200 * 20 的表格中去选中一列,已经并无明显的卡顿感了,但相比于 jQuery 实现的 Table,效果还是要差了一点。

虽然性能优化了三倍,但我还是有个心结:明明只更新了一行数据的选中状态,却还是重新渲染了整个表格,仍然需要在组件 render 的过程中执行多次的循环,在 patch 的过程中通过 diff 算法来对比更新。

最近我研究了 Vue.js 3.2 v-memo 的实现,看完源码后,我非常激动,因为发现这个优化技巧似乎可以应用到 ZoomUI 的 Table 组件中,尽管我们的组件库是基于 Vue 2 版本开发的。

我花了一个下午的时间,经过一番尝试,果然成功了,那么具体是怎么做的呢?先不着急,我们从 v-memo 的实现原理说起。

v-memo 的实现原理

v-memo 是 Vue.js 3.2 版本新增的指令,它可以用于普通标签,也可以用于列表,结合 v-for 使用,在官网文档中,有这么一段介绍:

v-memo 仅供性能敏感场景的针对性优化,会用到的场景应该很少。渲染 v-for 长列表 (长度大于 1000) 可能是它最有用的场景:

<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
  <p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
  <p>...more child nodes</p>
</div>

当组件的 selected 状态发生变化时,即使绝大多数 item 都没有发生任何变化,大量的 VNode 仍将被创建。此处使用的 v-memo 本质上代表着“仅在 item 从未选中变为选中时更新它,反之亦然”。这允许每个未受影响的 item 重用之前的 VNode,并完全跳过差异比较。注意,我们不需要把 item.id 包含在记忆依赖数组里面,因为 Vue 可以自动从 item:key 中把它推断出来。

其实说白了 v-memo 的核心就是复用 vnode,上述模板借助于在线模板编译工具,可以看到其对应的 render 函数:

import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, isMemoSame as _isMemoSame, withMemo as _withMemo } from "vue"

const _hoisted_1 = /*#__PURE__*/_createElementVNode("p", null, "...more child nodes", -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (item, __, ___, _cached) => {
    const _memo = ([item.id === _ctx.selected])
    if (_cached && _cached.key === item.id && _isMemoSame(_cached, _memo)) return _cached
    const _item = (_openBlock(), _createElementBlock("div", {
      key: item.id
    }, [
      _createElementVNode("p", null, "ID: " + _toDisplayString(item.id) + " - selected: " + _toDisplayString(item.id === _ctx.selected), 1 /* TEXT */),
      _hoisted_1
    ]))
    _item.memo = _memo
    return _item
  }, _cache, 0), 128 /* KEYED_FRAGMENT */))
}

基于 v-for 的列表内部是通过 renderList 函数来渲染的,来看它的实现:

function renderList(source, renderItem, cache, index) {
  let ret
  const cached = (cache && cache[index])
  if (isArray(source) || isString(source)) {
    ret = new Array(source.length)
    for (let i = 0, l = source.length; i < l; i++) {
      ret[i] = renderItem(source[i], i, undefined, cached && cached[i])
    }
  }
  else if (typeof source === 'number') {
    // source 是数字
  }
  else if (isObject(source)) {
    // source 是对象
  }
  else {
    ret = []
  }
  if (cache) {
    cache[index] = ret
  }
  return ret
}

我们只分析 source,也就是列表 list 是数组的情况,对于每一个 item,会执行 renderItem 函数来渲染。

从生成的 render 函数中,可以看到 renderItem 的实现如下:

(item, __, ___, _cached) => {
    const _memo = ([item.id === _ctx.selected])
    if (_cached && _cached.key === item.id && _isMemoSame(_cached, _memo)) return _cached
    const _item = (_openBlock(), _createElementBlock("div", {
      key: item.id
    }, [
      _createElementVNode("p", null, "ID: " + _toDisplayString(item.id) + " - selected: " + _toDisplayString(item.id === _ctx.selected), 1 /* TEXT */),
      _hoisted_1
    ]))
    _item.memo = _memo
    return _item
  }

renderItem 函数内部,维护了一个 _memo 变量,它就是用来判断是否从缓存里获取 vnode 的条件数组;而第四个参数 _cached 对应的就是 item 对应缓存的 vnode。接下来通过 isMemoSame 函数来判断 memo 是否相同,来看它的实现:

function isMemoSame(cached, memo) {
  const prev = cached.memo
  if (prev.length != memo.length) {
    return false
  }
  for (let i = 0; i < prev.length; i++) {
    if (prev[i] !== memo[i]) {
      return false
    }
  }
  // ...
  return true
}

isMemoSame 函数内部会通过 cached.memo 拿到缓存的 memo,然后通过遍历对比每一个条件来判断和当前的 memo 是否相同。

而在 renderItem 函数的结尾,就会把 _memo 缓存到当前 itemvnode 中,便于下一次通过 isMemoSame 来判断这个 memo 是否相同,如果相同,说明该项没有变化,直接返回上一次缓存的 vnode

那么这个缓存的 vnode 具体存储到哪里呢,原来在初始化组件实例的时候,就设计了渲染缓存:

const instance = {
  // ...
  renderCache: []
}

然后在执行 render 函数的时候,把这个缓存当做第二个参数传入:

const { renderCache } = instance
result = normalizeVNode(
  render.call(
    proxyToUse,
    proxyToUse,
    renderCache,
    props,
    setupState,
    data,
    ctx
  )
)

然后在执行 renderList 函数的时候,把 _cahce 作为第三个参数传入:

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (item, __, ___, _cached) => {
    // renderItem 实现
  }, _cache, 0), 128 /* KEYED_FRAGMENT */))
}

所以实际上列表缓存的 vnode 都保留在 _cache 中,也就是 instance.renderCache 中。

那么为啥使用缓存的 vnode 就能优化 patch 过程呢,因为在 patch 函数执行的时候,如果遇到新旧 vnode 相同,就直接返回,什么也不用做了。

const patch = (n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, isSVG = false, slotScopeIds = null, optimized = false) => {
  if(n1 === n2) {
    return
  }
  // ...
}

显然,由于使用缓存的 vnode,它们指向同一个对象引用,直接返回,节约了后续执行 patch 过程的时间。

在 Table 组件的应用

v-memo 的优化思路很简单,就是复用缓存的 vnode,这是一种空间换时间的优化思路。

那么,前面我们提到在表格组件中选择状态没有变化的行,是不是也可以从缓存中获取呢?

顺着这思路,我给 Table 组件设计了 useMemo 这个 prop,它其实是专门用于有选择列的场景。

然后在 TableBody 组件的 created 钩子函数中,创建了用于缓存的对象:

created() {
  if (this.table.useMemo) {
    if (!this.table.rowKey) {
      throw new Error('for useMemo, row-key is required.')
    }
    this.vnodeCache = []
  }
}

这里之所以把 vnodeCache 定义到 created 钩子函数中,是因为它并不需要变成响应式对象。

另外注意,我们会根据每一行的 key 作为缓存的 key,因此 Table 组件的 rowKey 属性是必须的。

然后在渲染每一行的过程中,添加了 useMemo 相关的逻辑:

function rowRender({ /* 各种变量参数 */}) {
  let memo
  const key = this.getKeyOfRow({ row, rowIndex: $index, rowKey })
  let cached
  if (useMemo) {
    cached = this.vnodeCache[key]
    const currentSelection = store.states.selection
    if (cached && !this.isRowSelectionChanged(row, cached.memo, currentSelection)) {
      return cached
    }
    memo = currentSelection.slice()
  }
  // 渲染 row,返回对应的 vnode
  const ret = rowVnode
  if (useMemo && columns.length) {
    ret.memo = memo
    this.vnodeCache[key] = ret
   }
   return ret
}

这里的 memo 变量用于记录已选中的行数据,并且它也会在函数最后存储到 vnodememo,便于下一次的比对。

在每次渲染 rowvnode 前,会根据 row 对应的 key 尝试从缓存中取;如果缓存中存在,再通过 isRowSelectionChanged 来判断行的选中状态是否改变;如果没有改变,则直接返回缓存的 vnode

如果没有命中缓存或者是行选择状态改变,则会去重新渲染拿到新的 rowVnode,然后更新到 vnodeCache 中。

当然,这种实现相比于 v-memo 没有那么通用,只去对比行选中的状态而不去对比其它数据的变化。你可能会问,如果这一行某列的数据修改了,但选中状态没变,再走缓存不就不对了吗?

确实存在这个问题,但是在我们的使用场景中,遇到数据修改,是会发送一个异步请求到后端,然获取新的数据再来更新表格数据。因此我只需要观测表格数据的变化清空 vnodeCache 即可:

watch: {
  'store.states.data'() {
    if (this.table.useMemo) {
      this.vnodeCache = []
    }
  }
}

此外,我们支持列的可选则渲染功能,以及在窗口发生变化时,隐藏列也可能发生变化,于是在这两种场景下,也需要清空 vnodeCache

watch:{
  'store.states.columns'() {
    if (this.table.useMemo) {
      this.vnodeCache = []
    }
  },
  columnsHidden(newVal, oldVal) {
    if (this.table.useMemo && !valueEquals(newVal, oldVal)) {
      this.vnodeCache = []
    }
  }
}

以上实现就是基于 v-memo 的思路实现表格组件的性能优化。我们从火焰图上看一下它的效果:

我们发现黄色的 Scripting 时间几乎没有了,再来看它一次更新所需要的 JS 执行时间:

我们发现组件的 render to vnode 花费的时间约 20ms;vnode patch to DOM 花费的时间约 1ms,整个更新渲染过程, JS 的执行时间大幅减少。

另外,我们通过benchmark 测试,得到如下结果:

优化后,ZoomUI Table组件一次更新的时间约为 80ms,相比于 ElementUI 的 Table 组件,性能提升了约十倍

这个优化效果还是相当惊人的,并且从性能上已经不输 jQuery Table 了,我两年的心结也随之解开了。

总结

Table 表格性能提升主要是三个方面:减少 DOM 数量、优化 render 过程以及复用 vnode。有些时候,我们还可以从业务角度思考,去做一些优化。

虽然 useMemo 的实现还比较粗糙,但它目前已满足我们的使用场景了,并且当数据量越大,渲染的行列数越多,这种优化效果就越明显。如果未来有更多的需求,更新迭代就好。

由于一些原因,我们公司仍然在使用 Vue 2,但这并不妨碍我去学习 Vue 3,了解它一些新特性的实现原理以及设计思想,能让我开拓不少思路。

从分析定位问题到最终解决问题,希望这篇文章能给你在组件的性能优化方面提供一些思路,并应用到日常工作中。

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

 相关推荐

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

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

发布于: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次阅读  |  详细内容 »
 目录