San CLI UI ——不只是San CLI的GUI(原理篇)

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

本期继续 San CLI UI 主题,主要讲述 San CLI UI 实现原理、插件系统的实现,以及利用插件系统可以实现哪些个性化的定制功能。

前言

San CLI UI 的功能篇,我们已经介绍了 San CLI UI 的核心功能包括:项目管理、依赖管理、插件管理、任务管理、配置管理、仪表盘工具集等功能,那么这些功能是如何实现的?工具扩展如何集成的?请跟随我们的脚步深入了解。

阅读本篇有助于提升开发自定义插件时对 api 的使用理解。

首先将整个 San CLI UI 的实现中涉及到的技术点整理如下:

原理

整体架构

San CLI UI 的架构设计参考了 Vue CLI UI,在实现上整体可以分为三部分:client 端(也即浏览器端),server 端(服务端),底层数据存储。

对其中主要部分简要概括如下:

  • client 端:主要负责 San CLI UI 的可视化界面展示,它本身是一个通过 San CLI 脚手架创建的 san 工程,主要的技术要点有三点:

  • 路由:基于 san-router 实现前端路由,并在此基础上扩展了自定义路由,开发者可通过 api 增加自定义路由及视图

  • san-component 增强:通过 mixin 的方式,在 san.Component 的子类原型上,扩展了 santd 组件、语言包($t 访问)、apollo-client 快速访问方式、插件相关的插件回调 PluginAction 方法及数据共享的 SharedData 方法

  • 插件定义及加载:提供全局 ClientAddonApi 用于插件开发者进行组件注册,并在 San CLI UI 启动时自动加载并显示到页面

  • server 端:基于 node、express 框架搭建服务,利用 Aopllo graphQL 实现 San CLI UI 前端与后端通讯、接口定义及实现,在 server 端负责处理请求的路由转发;定义并实现 server 端插件机制,对内实现配置、任务、仪表盘等插件加载,对外提供插件开发 api;在 server 端的子进程实现命令行调用功能,通过 IPC 在进程间通讯。

  • 数据存储: San CLI UI 中主要有三种数据存储方式:

  • 采用轻量的 lowdb 存储用户的项目数据

  • 采用 fs 文件操作实现运行期间插件共享数据的存储

  • 结合用户个人项目配置,如 san.config.js 等配置存储项目数据

  • 通信机制:在 San CLI UI 中,通讯可分为以下几部分:

  • client 端向 server 端发起的数据查询与变更请求:graphql query/mutate

  • server 端数据变更回传 client 端:graphql subscribe

  • webpack 子进程及 node 子进程间通讯:IPC

  • 自定义插件中San CLI UI 组件与 server 端通讯:PluginAction 和 SharedData

  • 静态服务请求:http server

整个架构图的右侧是插件包示意图,插件内主要包含两部分:用于 San CLI UI 的 client 端加载的前端组件,以及用于 node 端读取的 ui.js 插件配置。

接下来我们来看一下 San CLI UI 的工作流程。

工作流程

如图所示 San CLI UI 数据流程,前后端的通讯主要通过 graphql 实现,而插件的加载主要通过插件系统。

San CLI UI 启动后,首先调用 plugin 初始化方法,加载全部的内置插件及用户开发的插件,在 client端通过 ClientAddonApi 注册并加载对应的组件,在 server端则通过 pluginManager 读取各个插件包内 ui.js 描述的插件配置,在操作中涉及到对项目的配置,则会操作本地项目进行读取并修改。

San CLI UI 中,通讯可概括为三个部分:在 client 和 server 端主要是通过基于 GraphQL 实现通讯;在 webpack 子进程间通过 I 方式进行通讯;在自定义插件中前端组件与 server端通讯则是通过 PluginAction 和 SharedData 机制。

接下来将主要介绍基于 GraphQL 的后端设计以及插件系统的设计。

基于 GraphQL 的 node 端设计

GraphQL 概述

首先来回答一个问题:什么是 GraphQL?

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data

在官方文档的定义中,GraphQL 是一种 API 的查询语言,也是一个使用现有数据来完成所有查询的运行时;它允许客户端能够准确地获得它需要的、没有冗余的数据;对于一个复杂数据,采用 REST API 需要进行多次请求,而对于 GraphQL 仅需一次请求即可获取全部数据,即使在移动端网速较慢的情况下,GraphQL 仍能保持很好的请求效率;GraphQL 的类型定义系统对接口进行了规范,为书写错误等提供友好的错误提示。

GraphQL 的优点:

  • 声明式的数据获取:所见即所得
  • 天然的数据裁剪:按需获取
  • 强类型校验:强类型约束降低错误可能,同时也是一份天然的 api 文档
  • 丰富的社区资源

GraphQL 的使用方式 GraphQL 的使用方式简单来说分三个步骤:

  1. 描述数据格式
  2. 请求所需的数据格式
  3. 得到可预测的结果

GraphQL 的核心概念

  • SDL (Schema Definition Language):GraphQL 有一套用于定义 API schema 的类型系统,编写 schema 的语法我们称其为 schema 定义语言 (Schema Definition Language , 简称 SDL)。

  • 查询 (Query) 获取数据:查询中支持简单查询和嵌套查询

  • 变更 (Mutation) 写数据:包含三种变更来修改数据

  • 创建新数据

  • 修改现有数据

  • 删除现有数据

  • 订阅 (Subscription) 实时更新:客户端使用 Subscription 订阅方法与服务端建立长连接,当订阅事件发生时,服务端将相应的数据推送到客户端。

node 后端接口实现

接口的定义是前后端通信的一种约定,在使用 GraphQL 时,由 schema 实现,在 San CLI UI 中,整个服务端的目录如下:

server
├── api                       # plugin相关api
├── connectors                # 连接api具体实现
│      ├─ plugins.js
│      ├─ projects.js
│      ├─ configurations.js
│      └─ ...
├── resolver                   # 解析器
│      ├─ plugin.js
│      ├─ project.js
│      ├─ configuration.js
│      └─ ...
├── schema     # schema定义
│      ├─ plugin.js
│      ├─ project.js
│      ├─ configuration.js
│      └─ ...
├── main                       # GraphQL解析服务入口
├── utils                      # 工具类函数
├── modal                      # db及文件操作相关
└── index.js                   # 整个服务端入口

schema 定义与解析

GraphQL 服务端都有两个核心部分:Schema 和解析器 Resolver。

Schema 是可以通过 GraphQL 服务端获取的数据模型。它定义了允许客户端进行的查询,可以从服务端获取的数据类型,以及这些类型中的字段和之间的关系。以 folder 为例,Schema 定义如下:

extend type Query {
  folderCurrent: Folder
  foldersFavorite: [Folder]
  folderExists (file: String!): Boolean
}

extend type Mutation {
  folderOpen (path: String!): Folder
  folderSetFavorite (path: String!, favorite: Boolean!): Folder
  folderCreate(name: String!): Folder
}

type Folder {
  name: String!
  path: String!
  isPackage: Boolean
  isSanProject: Boolean
  favorite: Boolean
  children: [Folder]
  hidden: Boolean
}

Schema 告诉服务端允许客户端进行哪些查询,以及不同类型的关联方式,而解析器 Resolver 则告知每种类型的数据来自哪里。

const folders = require('../connectors/folders');
module.exports = {
    Query: {
        folderCurrent: (root, args, context) => folders.getCurrent(args, context),
        foldersFavorite: (root, args, context) => folders.listFavorite(context),
        folderExists: (root, {file}, context) => isDirectory(file)
    },
    ...
};

其中 connectors 负责执行具体的方法并返回结果。

class Folders {
    getCurrent(args, context) {
        const base = cwd.get();
        return generateFolder(base, context);
    }

    listFavorite(context) {
        return context.db.get('foldersFavorite').value().map(
            file => generateFolder(file.id, context)
        );
    }
    ...
}

San CLI UI 中,采用 apollo-client 与 apollo-server 实现 graphQL 通讯,在 client 端发起 query/mutate 请求后,由 apollo-server 进行解析并验证查询语法,验证通过后找到对应的 Resolver,并执行其 connectors 方法实现,将结果返回给 client 端。当订阅事件发生变化时,服务端推送对应的数据到 client 端,通过 subscribe 订阅的回调执行。

apollo 相关操作可参见:https://www.apollographql.com/docs/

插件系统设计

San CLI UI 插件

San CLI UI 中,仪表盘、配置管理、任务管理都是基于插件系统实现的,我们先来了解一下插件系统的相关概念。

插件系统的概念

定义

San CLI UI 插件是一个动态加载到 San CLI UI 中的 JS 包,能够为 San CLI UI 创建的项目添加额外的功能

插件的命名

为便于识别,插件包应以 san-cli-ui-<type>-<name> 作为的格式命名,这样做不仅便于 San CLI UI识别,同时便于其他开发者搜索发现。

npm 包基本结构:

如下所示,除满足一个 npm 包的基本要求外,每个插件需要包含一个 ui.js 文件,用于导出插件的相关配置信息

.
├── README.md
├── src
│    └── index.js // 组件注册
├── package.json
└── ui.js         // `San CLI UI` 集成(这里存放插件的配置信息)

其中最主要的两个文件:

  • ui.js:用于 server 端插件的加载定义及描述,包含插件配置及数据操作的逻辑
  • index.js:用于 client 端组件的定义

San CLI UI 插件加载关键

San CLI UI 插件加载依赖于 server 端和 client 端的两个对象的配合,首先在 server 端加载插件的描述,将插件的 id 及路径返回到 client 端,client端负责加载并挂载组件到页面:

  • 在 server 端,由 ui.js 定义加载各类型插件和路由。通过 api.registerAddon 定义插件id及加载路径,api.register * 定义插件所在视图
  • 在 client 端,由 index.js 定义挂载到页面的组件。通过 ClientAddonApi 将组件挂载到页面

ui.js 文件配置

在每个安装好的San CLI UI插件里,San CLI UI都会尝试从其插件的根目录加载一个可选的ui.js文件。(也可以使用文件夹形式,例如 ui/index.js)。

ui.js 主要导出一个函数,函数会以 API 对象作为第一个参数:

module.exports = api => {
  // 在这里使用 API...
}

其中apiSan CLI UI传入,为PluginManager的实例,所有插件的扩展功能都是基于这个对象来实现,例如:api.registerConfig注册配置项、api.registerWidget注册 widget 部件、api.registerAddon注册插件 id 及加载路径等。

index.js

在用户自定义插件中,index 文件主要负责自定义组件的注册,以便后面加载并显示在 San CLI UI 中,例如欢迎部件的定义:

import Welcome from './components/welcome/welcome';
/* global ClientAddonApi */
if (window.ClientAddonApi) {
    ClientAddonApi.defineComponent('san.widgets.components.welcome', Welcome);
}

其中 ClientAddonApi 为 ClientAddon 的实例,在 San CLI UI 启动时,通过实例化 ClientAddon 将 ClientAddonApi 挂载至全局,供插件加载使用。

接下来将逐个讲解服务端对应的 PluginManager 对象及客户端对应的 ClientAddon 对象。

PluginManager 对象

PluginManager 是整个San CLI UI插件系统的基础,主要完成了两件事:1. 插件的加载及定义;2. 提供消息通讯。

上文提到,在 San CLI UI 加载依赖时,会尝试读取依赖包内的 ui.js 文件,并将 PluginManager 对象的实例 api 注入其中,因此以下插件的使用均基于 api 来调用。

1. 插件加载及定义

插件加载

通过api.registerAddon函数,开发者可以为自定义的组件指定 id 及加载路径(在 npm 包内的 ui.js 中),San CLI UI在插件加载时,会尝试从开发者指定的路径下加载插件定义,从而集成到San CLI UI对应位置,api 使用方式如下:

if (process.env.SAN_CLI_UI_DEV) { // 在开发模式下加载自定义端口文件
    api.registerAddon({
        id: 'san.widgets.client-addon.dev',
        url: 'http://localhost:8889/index.js'
    });
}
else {
    api.registerAddon({           // 在生产模式下加载npm包的路径
        id: 'san.widgets.client-addon',
        path: 'san-cli-ui-addon-widgets/dist'
    });
}

api.registerAddon仅实现了插件包的加载,而加载的插件显示在何处?插件的显示项以及数据操作逻辑则需要单独调用每个插件的 api 进行描述。

San CLI UI中可以注册的插件类型包括:widget 插件、配置插件、任务插件、自定义视图插件。

widget 插件

widget(部件)插件,指显示在「项目仪表盘」内的小部件,San CLI UI默认部件有:欢迎提示、运行任务、终止端口、新闻订阅。部件运行流程如图:

server端读取依赖包中的 ui.js 文件,通过文件内的api.registerAddonapi.registerWidget的描述可以得到插件的定义及所在视图,在 client 的仪表盘请求数据时,将读取的插件定义和路径返回给 client端,client端加载对应路径的 js,并通过 ClientAddonApi 将组件挂载到仪表盘视图,自定义组件和视图间的数据可以通过 sharedData 和 pluginAction 来传递。

通过 api.registerWidget 方法,开发者可实现自定义的部件,显示在仪表盘内。api 使用方式如下:

api.registerWidget({
    id: 'san.widgets.test', // 必选,唯一的 ID
    title: 'title', // 必选,组件的名称
    description: 'description',  // 必选,组件的描述
    icon: 'info-circle', // 必选,组件的icon,取值可选santd内的icon类型
    component: 'san.widgets.components.test-widget', // 必选,加载的动态组件,会用 ClientAddonApi 进行注册
    minWidth: 6, // 宽度
    minHeight: 1, // 高度
    maxWidth: 6,
    maxHeight: 6,
    defaultWidth: 5, // 必选
    defaultHeight: 2, // 必选
    openDetailsButton: false, // 可选
    defaultConfig: () => ({  // 可选,如果有prompt表单,返回默认配置
        hi: 'hello'
    }),
    async onConfigOpen() {  // 可选,返回表单配置
        return {
            prompts: [
                {
                    name: 'hi',
                    type: 'input',
                    message: '',
                    validate: input => !!input
                }
            ]
        };
    }
});
配置插件

配置插件主要用于在配置管理中,将项目中配置文件的修改变为可视化的表单操作,方便用户理解并修改配置项。目前San CLI UI内默认配置项包含san.config.jseslint的配置。

运行流程如图:

通过调用api.registerConfig可以更改项目的配置,此函数返回一个符合inquirer.prompts 格式的对象,San CLI UI内支持的 inquirer 类型有:checkbox、confirm、input、list、string。通过该对象生成表单,可在项目配置中显示并修改具体项目的配置。

api.registerConfig配置的内容与本地文件的对应关系如下(以san.config.js为例):

// ui.js
api.registerConfig({
    id: 'san.san-cli', // 唯一的配置 ID
    name: 'San CLI', // 展示名称
    description: 'configuration.san-cli.description', // 展示在名称下方的描述
    link: 'https://ecomfe.github.io/san-cli/#/config', // “更多信息 (More info)”链接
    files: { // 该配置所有可能的文件
        san: {
            js: ['san.config.js']
        }
    },
    icon: iconUrl, // 配置图标
    onRead: ({data}) => ({ // 在读取时调用。onRead钩子返回一个提示符列表
        prompts: [
            {
                name: 'publicPath',
                type: 'input',
                default: '/',
                value: data.san && data.san.publicPath,
                message: 'configuration.san-cli.publicPath.label',
                description: 'configuration.san-cli.publicPath.description',
                group: 'configuration.san-cli.groups.general',
                link: 'https://ecomfe.github.io/san-cli/#config'
            },
            ...
        ]
    }),
    onWrite: async ({api, prompts}) => { // 在写入时调用
        const sanData = {};
        for (const prompt of prompts) {
            sanData[prompt.id] = await api.getAnswer(prompt.id);
        }
        api.setData('san', sanData);
    }
});
// san.config.js
{
    assetsDir: STATIC_PRO,
    publicPath: '/',
    outputDir: 'dist',
    filenameHashing: isProduction,
    css: {
        sourceMap: isProduction,
        cssPreprocessor: 'less',
        extract: true
    },

    pages: {
        index: {
            entry: './pages/index.js',
            filename: 'index.html',
            template: './assets/index.html',
            title: '项目管理器 - `San CLI UI`',
            chunks: <span style="index">, 'vendors'</span>
        }
    },
    ...
}
// 读取到San CLI UI后
{
    san: {
        assetsDir: STATIC_PRO,
        publicPath: '/',
        outputDir: 'dist',
        filenameHashing: isProduction,
        css: {
            sourceMap: isProduction,
            cssPreprocessor: 'less',
            extract: true
        },
        pages: {
            index: {
                entry: './pages/index.js',
                filename: 'index.html',
                template: './assets/index.html',
                title: '项目管理器 - `San CLI UI`',
                chunks: <span style="index">, 'vendors'</span>
            }
        }
    ...
    }
}
任务插件

在项目任务中展示的任务项,生成自项目 package.json 文件的 scripts 字段。 San CLI UI 默认内置了san servesan buildsan inspect三个命令的增强任务,包括: startbuildanalyzerbuild:moderninspect几个任务。运行流程如图:

San CLI UI 任务管理中,基于--dashborad 命令扩展方式实现了 san servesan buildsan inspect 三个命令的可视化显示,整个插件包既是 San CLI 插件包也是 San CLI UI 插件包,整体分为两部分:

  • San CLI 插件:以命令行参数扩展 --dashboard 功能,将 webpack 构建过程信息以 IPC 方式发出
  • San CLI UI 插件:通过 api.registerTask 方法注册自定义任务视图以及在执行构建等过程中数据的操作

通过 api.registerTask 方法,实现任务的 “增强”,为任务增加额外的信息和显示,并能在对应的调用周期下实现附加功能。使用方式如下:

api.registerTask({
    // 匹配san serve
    match: /san(-cli\/index\.js)? serve(\s+--\S+(\s+\S+)?)*$/,
    description: 'task.description.serve',
    link: 'https://ecomfe.github.io/san-cli',
    icon: sanIcon,
    prompts: [
        {
            name: 'open',
            type: 'confirm',
            default: false,
            message: 'task.serve.open'
        },
        ...
    ],
    onBeforeRun: ({answers, args}) => {
        ...
    },
    onRun: () => {
        ...
    },
    onExit: () => {
        ...
    },
    views: [
        {
            id: 'san.cli-ui.views.dashboard',
            label: 'addons.dashboard.title',
            component: 'san.cli-ui.components.dashboard'
        },
        ...
    ],
    defaultView: 'san.cli-ui.views.dashboard'
});
自定义视图插件与自定义路由插件

开发者可以使用 api.registerView 创建自定义视图,结合使用 ClientAddonApi.addRoute 创建自定义路由跳转该视图,运行流程如下:

在 ui.js 通过 api.registerView 注册的视图,在服务端触发视图增加的 subscription 监听,将新增的页面路径及名称推送到客户端显示,而客户端组件加载时,已通过 ClientAddonApi.addRoute 将路由加载到 san-router,当点击跳转时,就如处理 San CLI UI 默认路由一般,跳转至对应自定义组件页面。使用方式如下:

api.registerView({
    id: 'san.cli-ui.views.dashboard',
    label: 'addons.dashboard.title',
    component: 'san.cli-ui.components.dashboard'
});

2. 消息通讯

ui.js 内注入的 api 对象提供了以下几种方式用于自定义插件间的通讯:

  • 插件 action:是San CLI UI 的插件在浏览器端和 Node.js 之间的事件调用监听机制,例如终止端口插件的终止按钮,在按下后,会利用此 api 向 node 端传递需要杀死的端口,进而调用 kill 函数完成功能。

    在 ui.js 中可通过 api 调用:

    在浏览器端的组件内,可通过调用 san-component 扩展的方法在插件方法调用的不同时期执行:

  • $onPluginActionCalled:在 action 调用后执行

  • $onPluginActionResolved: 在 action 返回后执行

  • $callPluginAction: 在 action 调用时执行

  • api.onAction(id, callback):添加前端组件可触发的 action 方法

  • api.callAction(id, params): 触发 action 中的方法

  • 插件事件钩子:在 ui.js 的配置中,提供了项目不同阶段的时间钩子:

  • onProjectOpen: 当插件在当前项目中第一次被加载时触发。

  • onPluginReload: 当插件被重新加载时触发。

  • 进程通讯 IPC:IPC 就是进程间通信 (Inter-Process Communication) 的缩写。该系统允许你轻松的从子进程 (例如任务) 发送消息,并且轻量快速。在 ui.js 中使用 api.getIpc() 获取 IPC 的实例,进而实现进程的通讯。方法如下:

  • ipc.on(callback): 添加 listener 监听

  • ipc.off(callback): 移除 listener 监听

  • ipc.send(data): 向连接的所有的 IPC 客户端发送消息

  • 数据共享:San CLI UI 为开发者提供一种简易的、自定义组件之间通过共享的数据互通信息的方式。在 ui.js 中使用 const sharedData = api.getSharedData('my.com.') 获取 sharedData 的实例,为保证唯一使用,需要在使用数据函数时,输入唯一 id 生成自己的命名空间。方法如下:

  • sharedData.get($id): 获取 sharedData 中 $id 的数据

  • sharedData.set($id, value, {disk}): 设置 sharedData 中 $id 的数据

  • sharedData.remove($id): 清除 sharedData 中的 $id 的数据

  • sharedData.watch($id, handler): 监听 sharedData 的 $id 的值变化

  • sharedData.unwatch($id, handler): 清除 sharedData 的 $id 的监听

  • 持久存储 db: San CLI UI 为开发者提供对 db 操作的方法,数据存储的能力。在 ui.js 中通过调用 api.getDB(namespace) 获取 lowdb 的实例对象,同样为隔离,需要输入唯一的命名空间。方法如下:

  • get(key): 获取一个名为 key 的值

  • set(key, value): 更新 key 的值为 value

此外 San CLI UI 为开发者提供了工具函数:

  • api.hasPlugin: 如果项目使用了该插件则返回 true
  • api.getCwd: 获取当前工作目录
  • api.resolve: 在当前工程下解析一个文件
  • api.getProject: 得到当前打开的工程

以上就是 pluginManager 对象提供的能力,接下来我们来看看 index.js 中的负责组件注册及加载的 ClientAddon 对象

ClientAddon 对象

在插件包内,ClientAddon 实例化的对象 ClientAddonApi 主要完成两件事:

  • 插件内对应组件的定义: defineComponent
  • 将插件内的组件语言包扩展至 San CLI UIaddLocales

在插件内的使用方式如下:

import widgetdemo from './components/widget-demo';
import locales from './locales.json';

/* global ClientAddonApi */
if (window.ClientAddonApi) {
    // 扩展语言
    ClientAddonApi.addLocales(locales);
    // 推荐以类型前缀定义组件的唯一id:'san.widget'
    ClientAddonApi.defineComponent('san.widget.components.widget-demo', widgetdemo);
}

通过defineComponent将自定义组件加载到San CLI UI内,此时组件内可使用san-component增强的功能,如santd组件、$onPluginActionCalled等方法;通过addLocales将自定义组件的语言包加载到San CLI UI内,此时组件内可直接使用this.$t(key)的形式显示页面文案;通过ClientAddonApi.awaitComponent方法,在组件加载后,将组件挂载到页面对应位置。

所有 api 使用可参见:https://ecomfe.github.io/san-cli/#/ui/start

最后

感谢你阅读到了这里,以上便是《San CLI UI —— 不只是 San CLI 的 GUI(原理篇)》的全部内容。

本篇主要介绍了San CLI UI的整体架构,基于San + Node + Apollo GraphQL + Santd + san-router 实现,在工作流程部分重点介绍了插件系统的实现,并对San CLI UI可注册的插件类型及原理进行分析,包括:显示在仪表盘的 widget 部件注册、加入到配置管理的配置插件注册、用于任务增强的任务插件注册、以及扩展自定义视图的视图和路由插件注册,在具体加载机制上,重点介绍了插件包内用于 server端读取的ui.js和用于client端注册的index.js文件对象。下篇(实践篇)将通过一个插件开发的实例来演示插件的具体过程。

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

 相关推荐

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

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

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