整洁架构在前端的设计思想与应用实践

腾讯技术工程 发表于 1年以前  | 总阅读数:512 次

随着业务的发展,前端项目承载了越来越多的职责,也越来越复杂,简单通过 cli 生成的框架结构越来越无法满足。面对前端项目复杂度的不断提升,我们开始思考前端的架构组织方式怎么才更合理?应该如何设计良好的前端架构?行业是否有比较好的优秀实践?本文先从架构基本概念开始介绍,然后介绍整洁架构的概念和设计理念,最后结合整洁架构、 DDD 方法论,一起探讨整洁架构在前端的应用实践。

1、 为什么需要了解架构

对于每个软件系统,我们都可以通过行为和架构两个维度来体现它的实际价值。

行为是指系统实现的功能特性,一般是比较紧急的,需要按时上线。架构就是指系统架构,是重要的,但是并不总是特别紧急。因此导致我们常常忽视系统的架构价值,使得系统越来越难于理解、修改,导致系统功能迭代成本逐步上升,生产力逐步下降。

如果你遇到了这个问题,就应该要了解架构了,思考当前系统架构是否合理。

那什么是架构呢?

架构的本质就是控制系统复杂度,其终极目标用最小的人力成本来满足构建和维护系统需求,同时最小化系统的总运营成本,确保系统不会因为增加功能而导致开发成本上升。

那如何来判断架构的优劣?

一个软件架构的优劣,可以用它满足用户需求所需要的成本来衡量。如果该成本很低,系统理解成本低、易于修改、方便维护,能轻松部署,并且在系统的整个生命周期内一直都能维持这样的低成本,那么这个系统的设计就是优良的。反之,如果该系统的每次发布都会提升下一次变更的成本,那么这个设计就是不好的。

这样的架构可以大大节省软件项目构建与维护的人力成本。让每次变更都短小简单,易于实施,并且避免缺陷,用最小的成本,最大程度地满足功能性和灵活性的要求。

那么良好的架构是怎么实现的呢?

良好的架构实现方式,一般都是通过模块化解耦、分层解耦,实现关注点分离,并通过一定的规则组织好不同模块、不同分层的关系,实现高内聚低耦合,从而控制系统的复杂度。

整洁架构就是其中一种经典架构,让你我不再为每次功能迭代而胆战心惊,那么接下来我们将介绍何为『整洁架构』,为什么说它是一个好的软件架构。

2、 整洁架构的定义

整洁架构(Clean Architecture)是一种软件架构设计原则,由罗伯特·C·马丁(Robert C. Martin)提出,它旨在使软件系统更加灵活、可维护和可测试。

主要特点如下:

  • 与框架无关: 无论是前端代码还是服务端代码,其逻辑本身都应该是独立的,不应该依赖于某一个第三方框架或工具库。比如不依赖 Vue.js、React 等框架。
  • 可测试性:代码中的业务逻辑可以在不依赖 ui、数据库、服务器的情况下进行测试。
  • 和 ui 无关:代码中的业务逻辑不应该和 ui 做强绑定。比如把一个 web 应用切换成桌面应用,业务逻辑不应该受到影响。
  • 和数据库无关:无论数据库用的是 mysql 还是 mongodb,无论其怎么变,都不该影响到业务逻辑。
  • 和外部服务无关:将业务逻辑置于系统的核心,无论外部服务怎么变,都不影响到使用该服务的业务逻辑。

一个优秀的软件架构师应该致力于最大化架构组件的可选项数量,可以低成本更换框架、数据库、外部服务等,接下来我们具体看下整洁架构的设计思想。

3、 整洁架构的设计

3.1、整洁架构的设计思想

整洁架构除了以下至少四层架构外,在层与层之间还有一个非常明确的依赖关系,外层的逻辑依赖内层的逻辑 (图中黑色箭头指向),但是内层的代码不可以依赖外层。

  • 实体层: 业务实体这一层中封装的是整个系统的关键业务逻辑。这些实体既可以是带方法的类,也可以是带有一堆函数的结构体。但它们必须是高度抽象的,封装了该应用中最通用、最高层的业务逻辑,只可以随着核心业务规则变化,不可以随着外层组件的变化而变化。例如,一个针对页面导航方式或者安全问题的修改不应该触及这些对象,一个针对应用在运行时的行为所做的变更也不应该影响业务实体。该层一般采用 DDD 的理念进行抽象、封装。
  • 用例层: 软件的用例层中通常包含的是特定应用场景下的业务逻辑,这里面封装并实现了整个系统的所有用例。该层控制所有流向和流出实体层的数据流,并使用核心的实体及其业务规则来完成业务需求。此层的变更不会影响实体层,更外层的变更,比如开发框架、数据库、UI 等变化,也不会影响此层。
  • 适配器层: 软件的接口适配器层中通常是一组数据转换器,它们负责将数据从对用例和业务实体而言最方便操作的格式,转化成外部系统(譬如数据库以及 Web)最方便操作的格式。反之,来自于外部服务的数据也会在这层转换为内层需要的结构,一般用于 ui 和接口的适配操作。
  • 框架和驱动层:由最外层由各种框架和工具组成,比如 Web 框架、数据库访问工具等。通常在这层不需要写太多代码,大多是一些用来跟内层通信的胶水代码。

了解了整洁结构的设计思想,那么它和其他经典的架构有什么区别呢?

3.2、整洁架构和其他架构对比

我们先了解下最常见的六边形架构和 DDD 分层架构。

3.2.1、六边形架构

本图片来源《DDD 实战课》其核心理念是:应用是通过端口与外部进行交互的 。也就是说,在上图的六边形架构中,红圈内的核心业务逻辑(应用程序和领域模型)与外部资源(包括 APP、Web 应用以及数据库资源等)完全隔离,仅通过适配器进行交互。

它解决了业务逻辑与用户界面的代码交错问题,很好地实现了前后端分离。

3.2.2、DDD 分层架构

核心理念:领域驱动设计。领域模型准确反映了业务语言,而传统数据对象除了简单 setter/getter 方法外,没有任何业务方法

  • 用户界面层:负责向用户显示信息和解释用户指令,也是我们常说的 UI 层
  • 应用层:负责使用领域层提供的能力进行业务流程编排,实现对应功能
  • 领域层:领域模型/领域服务/和防腐层的接口定义,为应用层提供能力
  • 基础设施层:为其它各层提供通用的技术和基础服务,如数据持久化、消息中间件等

具体实践是一般第一步为自上而下结构化分解,如图:

图片来自于张建飞《基于 DDD 的应用架构设计和实践》分享

第二步为自下而上的领域建模,从而完成功能的实现,如图

图片来自于张建飞《基于 DDD 的应用架构设计和实践》分享

总结起来就是先把业务逻辑按结构化拆解,拆解为不同的步骤,然后调用领域层的能力进行逻辑编排实现对应功能。

图片来自于张建飞《基于 DDD 的应用架构设计和实践》分享

3.2.3、对比分析

本图片来源《DDD 实战课》

可以看到他们的共同点是:整洁架构、DDD 分层架构、六边形架构都是以领域模型为核心,实行分层架构,内部核心业务逻辑与外部应用、资源隔离并解耦。

事实上整洁架构恰恰是最后的集大成者,集合了 DDD 领域驱动的思想 + 分层架构的落地,具体可以如下架构发展历史图。

图片来源于《领域驱动架构及其演变史(EBI、DDD、端口适配、洋葱、整洁)》

了解了整洁架构的优势,接下来我们重点介绍如何应用整洁架构

4、 如何应用整洁架构?

首先会借鉴 DDD 的思想进行业务分析、建模,形成业务的领域模型。

4.1 战略阶段:分析业务,建立领域模型

4.1.1 分析业务流程

DDD 中一般采用用例分析、事件风暴、四色建模等方法,尽可能全面不遗漏的分解业务领域,梳理业务过程中的用户操作、事件以及依赖关系,再根据这些要素进一步梳理出领域对象及他们之间的关系。

DDD 里说的这些业务分析方法在构建大型项目时非常有用,但在日常需求中会显得有点重。于是我们结合用例分析与事件风暴,沉淀出一套适合日常需求分析的方法论,内部称之为双轴泳道分析法。

下面以电商购物需求为例,介绍一下实施步骤:

  • 识别业务参与者

业务参与者,是指在业务流程中发起动作,触发状态改变的个体。业务参与者可以某个角色、某个系统、或者某个综合系统。

例如电商网站购物场景中,用户选品、下单、支付,电商网页负责呈现商品信息,提示用户操作结果,电商后台负责生成订单、记账。用户(角色)、电商网页(系统)、电商后台(系统)都是业务参与者,其概念类似用例分析里的 actor。

  • 分析参与者在不同阶段发生的动作及触发的状态

动作是指参与者发起的某个命令,比如创建订单、抽奖等,而状态是指动作发生后引起的状态变更,比如订单已创建,订单创建失败等,其概念类似事件风暴的命令和事件。

对电商购物场景分析结果如下:

  • 使用双轴泳道图描述业务流程

泳道的横轴是业务参与者,纵轴是业务流程的不同阶段,通过双轴泳道图描述出各个参与者在不同阶段发生的动作、触发的状态。

在业务流程中,有些属于前端交互,有些属于动作,有些属于状态。动作和状态会用于后续的领域对象提取,我们需要将他们标注出来以便识别。

4.1.2 提取领域对象

经过上述分析后,业务流程已经非常清晰。第二步,就是要根据分析过程中产生的动作和状态,提取出产生这些行为的对象,进一步识别出实体、值对象、聚合根。

  • 实体

业务形态上是包含业务规则的集合,具有唯一标识字段(id)。代码上通常以类/对象的形式存在,包含属性和方法。

  • 值对象

业务形态上是干个属性的集合,只有数据初始化操作和有限的不涉及修改数据的行为,不具有唯一标识(id)。代码上以类/对象的形式被实体引用。

  • 聚合根

聚合根是一个特殊实体,具备唯一标识(id),有独立的生命周期。聚合根是聚合的唯一入口点,负责协调实体以及值对象完成业务逻辑。

在以上例子中,将动作、状态归类后,可划分为用户、购物车、商品、订单、消费流水五个实体,收货地址可作为值对象。

4.1.3 划分界限,识别模块

根据上下文语义,寻找聚合根、划定界限,将实体进一步组合成聚合,一个聚合对应一个模块。

在本例子中:

  • 用户实体和购物车实体与用户强相关,分别管理用户基本信息和购物车信息,可以用户实体为聚合根,共同构成用户模块;
  • 订单实体、消费流水实体与下单强相关,可以订单实体为聚合更,共同构成下单模块;
  • 商品模块较独立,可单独构成商品模块。

4.2 战术阶段:工程落地,搭建分层架构

通过战略阶段建立对应的领域模型后,在对应的工程实现上,应如何划分层呢?

4.2.1 分层实现

以前端工程为例,常规的 mvvm 前端工程的分层架构如下图,会在 store 层直接调用 api 层发起请求,然后再通过 mvvm 更新视图

❌ 容易出现的问题:

  • 业务逻辑和 ui 强耦合,如果要更换 ui,改动成本大
  • 往往 store 层依赖框架的实现,业务逻辑易和框架强耦合,如果切换框架或升级框架,重构成本大。比如升级 vue 到 vue2,或 vue 切换到 react。

根据整洁架构思想,设计后的架构如下:

在原有基础上拆分了实体层和用例层,并在用例层内通过端口的方式定义了依赖的端口方法,用来解耦框架和第三方服务的依赖。

目前很多前端实践里实体层是比较薄,有的只有类型定义,把逻辑封装到了用例层,但用例层的逻辑不适合更细粒度的复用,导致复用比较麻烦,这也不符合整洁架构对实体层的定义,整洁架构中期望实体这一层中封装的是整个系统的关键业务逻辑。

个人觉得应该视具体情况而定,逻辑简单的前端页面,用例层和实体层都比较简单,可以使用贫血的实体层;如果逻辑复杂的一定要把逻辑抽取到实体层,用例层使用实体层提供的能力进行功能串联,方便复用及后续维护。比如我们这边的下载逻辑就比较重,需要把相关逻辑封装在实体层。

比如购买这个用例里,需要判断是否登录,判断是否有库存,创建订单,支付等流程,每个流程应该使用的都是实体的能力,具体的逻辑封装在实体里,用例层核心是实现流程的串联。

下面我们看下这样分层后的代码示例及数据流向是怎么样的?

以最常见的电商商品展示为示例,用户登录后查看商品详情,根据用户所在地展示商品库存。整个流程是这样的:进到页面 -> 检查登录态 -> 发起请求 -> 组装数据 -> 页面展示

实体层

关于实体层的设计有两个要点:

  • 使用充血模型来描述实体

充血模型指的是,实体内包含数据及常用行为,符合面向对象的封装性,是典型的面向对象编程风格。反之贫血模型指的是实体只包含数据,行为不封装在实体内,是一种面向过程的设计。

  • 结合具体场景,允许部分依赖实现

众所周知,DDD 中非常强调领域层的解耦,理论上领域层应该依赖抽象接口,不应该依赖具体实现。这种彻底解耦的方式的确能解决后续依赖项变更的问题,但在实际开发中,很多依赖项是我们可控的,可预知后续是不会变更的,这种情况下如果对所有依赖都要抽象出接口,那将会大大增加我们的工作量。因此我们提倡结合具体的场景,只对后续可能变化的依赖进行防腐,对于后续不会变化的依赖我们允许直接依赖实现。

本例子中,可拆分成用户、商品两个实体。

用户实体,主要提供用户常用的登录、登出、查询用户所在城市等方法。用户的登录态一般依赖 cookie,浏览器的 cookie 接口不大可能出现破坏性变更,因此在用户实体中,我们允许直接依赖 cookie 操作库,而查询用户城市依赖于用户服务提供接口,为防止后端接口变更,需要对用户服务进行防腐。

// 用户实体 ./shared/domain/entities/user.ts

import cookie from 'cookie';

export interface IUserService {
    getCity(id: string): Promise<City>;
}

export class User {
    // 用户Id
    public id: string;
    // 用户服务
    private userService: IUserService;

    constructor(id: string, name: string, userService: IUserService) {}

    // 检查用户是否登录
    public isLogin(): boolean {
        if (cookie.get('openid') && cookie.get('access_token')) {
            return true;
        }

        return false;
    }

    // 登录
    public login(): Promise<void> {
        if (!this.isLogin()) {
            goToURL('https://www.xxx.com/login');
        }
    }

    // 退出登录
    public logout(): Promise<void> {
        cookie.remove('openid');
        cookie.remove('access_token');
        goToURL('https://www.xxx.com/login');
    }

    // 获取用户所在城市
    public getCity(): Promise<City> {
        return this.userService.getCity(this.id);
    }
}

商品实体:提供查询商品详情方法,商品实体依赖后端的商品服务,为防止后端接口变更,需要进行防腐:

// 商品实体 ./shared/domain/entities/product.ts

export interface IProductService {
    getBaseInfoById(id: string): Promise<ProductBaseInfo>;
    getStockInfoByIdAndCity(id: string, city: City): Promise<ProductStockInfo>;
}

export class Product {
  // 商品Id
  public id: string;
  // 用户服务
  private productService: ProductService;

  constructor(id: string, name: string; productService: IProductService) {}

  // 获取商品详情
  public async getDetail() {
      // 获取商品基本信息和库存信息
      const baseInfo = await this.productService.getBaseInfoById(this.id);
      const stockInfo = await this.productService.getStockInfoById(this.id, city);
      // 组合详情数据
      const detail = {
          id: this.id,
          name: baseinfo.name,
          images: baseinfo.name,
          stockNum: stockInfo.num,
      };
      return detail;
  }

  // 根据地区获取库存信息
  public addToCart(num:number) {
      return this.productService.getStockInfoById(this.id, city);
  }
};

用例层

用例层主要充当“协调者”的角色,组合各个实体的操作,实现业务逻辑,这层的逻辑代码会“面向过程”。

本例子中,需要结合用户实体和商品实体,实现根据用户所在地获取商品库存信息

// 获取商品详情用例 ./shared/domain/usercases/get-product-detail.ts

import { User } from './shared/domain/entities/user.ts';
import { Product } from './shared/domain/entities/product.ts';
// 用户服务、产品服务的具体实现,见适配器层
import { UserService } from './server/services/user-service.ts';
import { ProductService } from './server/services/product-service.ts';

export async function getProductDetail(userId: string, productId: string) {
    // 示例化用户实体和商品实体,省略部分代码
    const user = new User(userId, UserService);
    const product = new Product(productId, ProductService);

    // 获取用户所在城市
    const city: City = await user.getCity();
    // 获取商品基本信息
    const productBaseInfo = await product.getBaseInfo();
    // 根据城市获取商品库存
    const productStockInfo = await product.getStockInfo(city);
    return {
        baseInfo: productBaseInfo,
        stockInfo: productStockInfo,
    };
}

适配器层

  • 包含 UI 框架的代码,及 store 相关的代码,如 vuex,通过更新 vuex 的数据更新视图
  • 调用第三方服务,并将其转化成用例层的端口格式
// 用户服务具体实现 ./server/services/user-service.ts
import { IUserService } from './shared/domain/entities/user.ts';

class UserService implements IUserService {
    getCity(userId: string): Promise<City> {
        // 通过后台接口获取用户所在城市
        const resp = get('https://api.xxx.com/queryUserCity', { userId });
        if (resp.ret !== 0) {
            throw new Error('查询用户所在城市失败');
        }

        return resp.data.city as City;
    }

}
// 商品服务具体实现 ./server/services/product-service.ts
import { IProductService } from './shared/domain/entities/product.ts';

class ProductService implements IProductService {
    getBaseInfoById(id: string): Promise<ProductBaseInfo> {
        // 调用后台商品服务接口,省略具体实现
    }
    getStockInfoByIdAndCity(id: string, city: City): Promise<ProductStockInfo> {
        // 调用后台商品服务接口,省略具体实现
    }
}
// 商品详情页 store ./client/store/product-store.ts
import { getProductDetial } from './shared/domain/usercases/get-product-detail.ts'

export default new Vuex.Store({
  state: {
    productDetail: ProductDetail,
  },
  mutations: {
    async getProductDetail(state) {
        // 用例已包含具体业务逻辑,这里直接调用用例方法
        state.productDetail = getProductDetial(userId, productId);
    },
  },
}
// 商品详情页 ./client/pages/product-detail.ts

import { defineComponent, ref, onMounted } from 'vue';

export defineComponent({
  name: 'ProudctDetailPage',
  setup() {

     onMounted(() => {
         setLoading(true);
         await store.getProductDetail();
         setLoading(false);
      });

    return () => (
      <div>
        <p> {{ store.productDetail.baseInfo }}</p>
        <p> {{ store.productDetail.stockInfo }}</p>
      </div>
    );
  },
});

框架和驱动层

  • 这里是用到的第三方服务、框架,如 vue、svelte 等

如以下伪代码

import vue from 'Vue'
vue.render(App);

整体数据流向图如下

4.2.2 依据 SOLID 原则实现分层

  • S 单一职责原则
  • O 开闭原则
  • L 里氏替换原则
  • I 接口隔离原则
  • D 依赖倒置原则

PS: 由于网上对 SOLID 原则有较多介绍,这里就不额外展开了,有兴趣的同学可查阅学习

4.3.3 架构及目录示例

基于此,我们采用整洁架构后目录结构如下,如下图所示

img- 单独抽离领域层(包括实体层、用例层、防腐层)目录

  • 将 utils 工具进行拆解,无"副作用" 的 utils 移动至 shared 目录下,界面相关如 jump 存放到对应的 client 的 utils 下

5、总结

整洁架构不是"银弹",在实践上存在以下优缺点:

✅ 优点:

  • 业务领域层逻辑更干净,业务逻辑可适配到不同的 UI 框架、对于同构的 SSR 服务也可以公用同一套业务逻辑
  • 职责边界更为明确,内层的业务逻辑可覆盖单元测试,ui 层则依赖 e2e 端对端测试覆盖

❌ 缺点:

  • 构建边界的成本较大,由于核心业务层无法直接引用外层 UI 的 store 和 api,需额外声明端口依赖,开发效率变低

所以说没有最好的架构,只有最适合自己团队和业务的架构。对于是否使用整洁架构,我们应考量项目复杂度、项目的生命周期,综合来衡量。对于业务逻辑简单、业务生命周期较短的项目,直接使用照搬整洁架构,会导致开发效率地变低;但是对于需要长期维护的复杂项目,如腾讯文档、vsCode 内核、低代码引擎等,就非常适合整洁架构,能大大降低系统的维护成本,并在前端技术快速变迁的情况下,非常方便后续对 UI 库、框架的升级改造。

期望大家在日常工作中除了关注系统的行为,多一些对架构的关注和思考,以提升系统的整洁性,让每次变更都短小简单,易于实施,并且避免缺陷,用最小的成本,最大程度地满足功能性和灵活性的要求。

最后感谢您的阅读,如果本文给你带来了一些启发,欢迎动动手指,一键三连,这是对笔者最大的支持和鼓励。

6、 参考文献

 相关推荐

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

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

发布于: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年以前  |  237239次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8075次阅读
 目录