本期继续 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?
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 的优点:
GraphQL 的使用方式 GraphQL 的使用方式简单来说分三个步骤:
GraphQL 的核心概念
SDL (Schema Definition Language):GraphQL 有一套用于定义 API schema 的类型系统,编写 schema 的语法我们称其为 schema 定义语言 (Schema Definition Language , 简称 SDL)。
查询 (Query) 获取数据:查询中支持简单查询和嵌套查询
变更 (Mutation) 写数据:包含三种变更来修改数据
创建新数据
修改现有数据
删除现有数据
订阅 (Subscription) 实时更新:客户端使用 Subscription 订阅方法与服务端建立长连接,当订阅事件发生时,服务端将相应的数据推送到客户端。
接口的定义是前后端通信的一种约定,在使用 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 # 整个服务端入口
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
中的 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` 集成(这里存放插件的配置信息)
其中最主要的两个文件:
San CLI UI 插件加载关键
San CLI UI
插件加载依赖于 server 端和 client 端的两个对象的配合,首先在 server 端加载插件的描述,将插件的 id 及路径返回到 client 端,client端负责加载并挂载组件到页面:
ui.js 文件配置
在每个安装好的San CLI UI
插件里,San CLI UI
都会尝试从其插件的根目录加载一个可选的ui.js
文件。(也可以使用文件夹形式,例如 ui/index.js
)。
ui.js
主要导出一个函数,函数会以 API 对象作为第一个参数:
module.exports = api => {
// 在这里使用 API...
}
其中api
由San 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 是整个San CLI UI
插件系统的基础,主要完成了两件事:1. 插件的加载及定义;2. 提供消息通讯。
上文提到,在 San CLI UI 加载依赖时,会尝试读取依赖包内的 ui.js 文件,并将 PluginManager 对象的实例 api 注入其中,因此以下插件的使用均基于 api 来调用。
通过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(部件)插件,指显示在「项目仪表盘」内的小部件,San CLI UI
默认部件有:欢迎提示、运行任务、终止端口、新闻订阅。部件运行流程如图:
server端读取依赖包中的 ui.js 文件,通过文件内的api.registerAddon
和api.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.js
和eslint
的配置。
运行流程如图:
通过调用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 serve
、san build
、san inspect
三个命令的增强任务,包括: start
、build
、analyzer
、build:modern
、inspect
几个任务。运行流程如图:
在 San CLI UI
任务管理中,基于--dashborad
命令扩展方式实现了 san serve
、 san build
、 san 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'
});
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
: 如果项目使用了该插件则返回 trueapi.getCwd
: 获取当前工作目录api.resolve
: 在当前工程下解析一个文件api.getProject
: 得到当前打开的工程以上就是 pluginManager 对象提供的能力,接下来我们来看看 index.js 中的负责组件注册及加载的 ClientAddon 对象
在插件包内,ClientAddon 实例化的对象 ClientAddonApi 主要完成两件事:
defineComponent
San CLI UI
: addLocales
在插件内的使用方式如下:
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
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。