由于业务需要,近期团队要搞一套自己的UI
组件库,框架方面还是Vue
。而业界已经有比较成熟的一些UI
库了,比如ElementUI
、AntDesign
、Vant
等。
结合框架Vue
,我们选择在ElementUI
基础上进行改造。但造轮子
绝非易事,首先需要先去了解它整个但构建流程、目录设计等。
本文通过分析ElementUI
完整的构建流程,最后给出搭建一个完备的组件库需要做的一些工作,希望对于想了解ElementUI
源码或者也有搭建UI
组件库需求的你,可以提供一些帮助!
我们先来看下ElementUI
的源码的目录结构。
Element UI
贡献指南、issue
和PR
模板说完文件目录,剩下还有几个文件(常见的.babelrc
、.eslintc
这里就不展开说明了),在业务代码中是不常见的:
Element UI
提供了四种不同语言的,也是很贴心了webpack
打包时获取组件的文件路径。ElementUI
开发者对常见问题的解答。Element UI
使用的是MIT
协议Makefile
是一个适用于 C/C++
的工具,在拥有 make
环境的目录下, 如果存在一个 Makefile
文件。那么输入 make
命令将会执行 Makefile
文件中的某个目标命令。深入了解构建流程前,我们先来看下ElementUI
源码的几个比较主要的文件目录,这对于后面研究ElementUI
的完整流程是有帮助的。
通常我们去看一个大型项目都是从package.json
文件开始看起的,这里面包含了项目的版本、入口、脚本、依赖等关键信息。
我这里拿出了几个关键字段,一一的去分析、解释他的含义。
项目的入口文件
❝
import Element from 'element-ui'
时候引入的就是main
中的文件❞
lib/element-ui.common.js
是commonjs
规范,而lib/index.js
是umd
规范,这个我在后面的打包模块会详细说明。
指定npm publish
发包时需要包含的文件/目录。
TypeScript
入口文件。
项目的线上地址
当你把一个包发布到npm
上时,它同时应该也可以在unpkg
上获取到。也就是说,你的代码既可能在NodeJs
环境也可能在浏览器环境
执行。为此你需要用umd
格式打包,lib/index.js
是umd
规范,由webpack.conf.js
生成。
声明样式入口文件,这里是lib/theme-chalk/index.css
,后面也会详细说明。
开发、测试、生产构建,打包、部署,测试用例等相关脚本。scripts
算是package.json
中最重要的部分了,下面我会一一对其中的重要指令进行说明。
"bootstrap": "yarn || npm i"
安装依赖, 官方推荐优先选用yarn
(吐槽一句:我刚开始没看明白,想着bootstrap
不是之前用过的那个 ui 库吗 ,后来看了下,原来bootstrap
翻译过来是引导程序
的意思,这样看看也就大概理解了 )
该指令主要用来自动化生成一些文件。
"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js"
这条指令较长,我们拆开来看:
build/bin/iconInit.js
解析icon.scss
,把所有的icon
的名字放在icon.json
里面 最后挂在Vue
原型上的$icon
上。
最后通过遍历icon.json
,得到了官网的这种效果:
build/bin/build-entry.js
根据components.json
文件,生成src/index.js
文件,核心就是json-templater/string
插件的使用。
我们先来看下src/index.js
文件,他对应的是项目的入口文件,最上面有这样一句:
/* Automatically generated by './build/bin/build-entry.js' */
也就是src/index.js
文件是由build/bin/build-entry.js
脚本自动构建的。我们来看下源码:
// 根据components.json生成src/index.js文件
// 引入所有组件的依赖关系
var Components = require('../../components.json');
var fs = require('fs');
// https://www.npmjs.com/package/json-templater 可以让string与变量结合 输出一些内容
var render = require('json-templater/string');
// https://github.com/SamVerschueren/uppercamelcase 转化为驼峰 foo-bar >> FooBar
var uppercamelcase = require('uppercamelcase');
var path = require('path');
// os.EOL属性是一个常量,返回当前操作系统的换行符(Windows系统是\r\n,其他系统是\n)
var endOfLine = require('os').EOL;
// 生成文件的名字和路径
var OUTPUT_PATH = path.join(__dirname, '../../src/index.js');
var IMPORT_TEMPLATE = 'import {{name}} from \'../packages/{{package}}/index.js\';';
var INSTALL_COMPONENT_TEMPLATE = ' {{name}}';
// var MAIN_TEMPLATE = `/* Automatically generated by './build/bin/build-entry.js' */
// ...
// 获取所有组件的名字,存放在数组中
var ComponentNames = Object.keys(Components);
var includeComponentTemplate = [];
var installTemplate = [];
var listTemplate = [];
ComponentNames.forEach(name => {
var componentName = uppercamelcase(name);
includeComponentTemplate.push(render(IMPORT_TEMPLATE, {
name: componentName,
package: name
}));
if (['Loading', 'MessageBox', 'Notification', 'Message', 'InfiniteScroll'].indexOf(componentName) === -1) {
installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, {
name: componentName,
component: name
}));
}
if (componentName !== 'Loading') listTemplate.push(` ${componentName}`);
});
var template = render(MAIN_TEMPLATE, {
include: includeComponentTemplate.join(endOfLine),
install: installTemplate.join(',' + endOfLine),
version: process.env.VERSION || require('../../package.json').version,
list: listTemplate.join(',' + endOfLine)
});
// 结果输出到src/index.js中
fs.writeFileSync(OUTPUT_PATH, template);
console.log('[build entry] DONE:', OUTPUT_PATH);
其实就是上面说的,根据components.json
,生成src/index.js
文件。
build/bin/i18n.js
根据 examples/i18n/page.json
和模版,生成不同语言的 demo
,也就是官网 demo 展示国际化的处理。
ElementUI
官网的国际化依据的模版是examples/pages/template
,根据不同的语言,分别生成不同的文件:
这里面都是.tpl
文件,每个文件对应一个模版,而且每个tpl
文件又都是符合SFC
规范的Vue
文件。
我们随便打开一个文件:
export default {
data() {
return {
lang: this.$route.meta.lang,
navsData: [
{
path: '/design',
name: '<%= 1 >'
},
{
path: '/nav',
name: '<%= 2 >'
}
]
};
}
};
里面都有数字标示了需要国际化处理的地方。
首页所有国际化相关的字段对应关系存储在examples/i18n/page.json
中:
最终官网展示出来的就是经过上面国际化处理后的页面: 支持切换不同语言。
绕了一圈,回到主题:build/bin/i18n.js
帮我们做了什么呢?
我们思考一个问题:首页的展示是如何做到根据不同语言,生成不同的vue
文件呢?
这就是build/bin/i18n.js
帮我们做的事情。
来看下对应的源码:
'use strict';
var fs = require('fs');
var path = require('path');
var langConfig = require('../../examples/i18n/page.json');
langConfig.forEach(lang => {
try {
fs.statSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`));
} catch (e) {
fs.mkdirSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`));
}
Object.keys(lang.pages).forEach(page => {
var templatePath = path.resolve(__dirname, `../../examples/pages/template/${ page }.tpl`);
var outputPath = path.resolve(__dirname, `../../examples/pages/${ lang.lang }/${ page }.vue`);
var content = fs.readFileSync(templatePath, 'utf8');
var pairs = lang.pages[page];
Object.keys(pairs).forEach(key => {
content = content.replace(new RegExp(`<%=\\s*${ key }\\s*>`, 'g'), pairs[key]);
});
fs.writeFileSync(outputPath, content);
});
});
处理流程也很简单:遍历examples/i18n/page.json
,根据不同的数据结构把tpl
文件的标志位,通过正则匹配出来,并替换成自己预先设定好的字段。
这样官网首页的国际化就完成了。
build/bin/version.js
根据package.json
中的version
,生成examples/versions.json
,对应就是完整的版本列表
处理样式相关。
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
同样这一条也关联了多个操作,我们拆开来看。
这一步是根据components.json
,生成package/theme-chalk/index.scss
文件,把所有组件的样式都导入到index.scss
。
其实是做了一个自动化导入操作,后面每次新增组件,就不用手动去引入新增组件的样式了。
我们都知道ElementUI
在使用时有两种引入方式:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
import Vue from 'vue';
import { Pagination, Dropdown } from 'element-ui';
import App from './App.vue';
Vue.use(Pagination)
Vue.use(Dropdown)
new Vue({
el: '#app',
render: h => h(App)
});
对应两种引入方式,Element
在打包时对应的也有两种方案。
具体如下:将packages/theme-chalk
下的所有scss
文件编译为css
,当你需要全局引入时,就去引入index.scss
文件;当你按需引入时,引入对应的组件scss
文件即可。
这其中有一点,我们需要思考下:如何把packages/theme-chalk
下的所有scss
文件编译为css
?
在平时的开发中,我们打包、压缩之类的工作往往都会交给webpack
去处理,但是,针对上面这个问题,我们如果采用gulp
基于工作流去处理会更加方便。
gulp
相关的处理就在packages/theme-chalk/gulpfile.js
中:
'use strict';
const { series, src, dest } = require('gulp');
const sass = require('gulp-sass'); // 编译gulp工具
const autoprefixer = require('gulp-autoprefixer'); // 添加厂商前缀
const cssmin = require('gulp-cssmin'); // 压缩css
function compile() {
return src('./src/*.scss') // src下的所有scss文件
.pipe(sass.sync()) // 把scss文件编译成css
.pipe(autoprefixer({ // 基于目标浏览器版本,添加厂商前缀
browsers: ['ie > 9', 'last 2 versions'],
cascade: false
}))
.pipe(cssmin()) // 压缩css
.pipe(dest('./lib')); // 输出到lib下
}
function copyfont() {
return src('./src/fonts/**') // 读取src/fonts下的所有文件
.pipe(cssmin())
.pipe(dest('./lib/fonts')); // 输出到lib/fonts下
}
exports.build = series(compile, copyfont);
经过处理,最终就会打包出对应的样式文件
❝
cp-cli
是一个跨平台的copy
工具,和CopyWebpackPlugin
类似❞
这里就是复制文件到lib/theme-chalk
下。
上面提到过多次components.json
,下面就来了解下。
这个文件其实就是记录了组件的路径,在自动化生成文件以及入口时会用到:
{
"pagination": "./packages/pagination/index.js",
"dialog": "./packages/dialog/index.js",
"autocomplete": "./packages/autocomplete/index.js",
// ...
"avatar": "./packages/avatar/index.js",
"drawer": "./packages/drawer/index.js",
"popconfirm": "./packages/popconfirm/index.js"
}
存放着组件库的源码和组件样式文件。
这里以Alert
组件为例做下说明:
这里main.vue
对应就是组件源码,而index.js
就是入口文件:
import Alert from './src/main';
/* istanbul ignore next */
Alert.install = function(Vue) {
Vue.component(Alert.name, Alert);
};
export default Alert;
引入组件,然后为组件提供install
方法,让Vue
可以通过Vue.use(Alert)
去使用。
❝关于
install
可以看官方文档❞
这里面存放的就是所有组件相关的样式,上面也已经做过说明了,里面有index.scss
(用于全局引入时导出所有组件样式)和其他每个组件对应的scss
文件(用于按需引入时导出对应的组件样式)
说了半天,终于绕到了src
文件夹。
上面的packages
文件夹是分开去处理每个组件,而src
的作用就是把所有的组件做一个统一处理,同时包含自定义指令、项目整体入口、组件国际化、组件 mixins、动画的封装和公共方法。
我们主要来看下入口文件,也就是src/index.js
:
/* Automatically generated by './build/bin/build-entry.js' */
// 导入了packages下的所有组件
import Pagination from '../packages/pagination/index.js';
import Dialog from '../packages/dialog/index.js';
import Autocomplete from '../packages/autocomplete/index.js';
// ...
const components = [
Pagination,
Dialog,
Autocomplete,
// ...
];
// 提供了install方法,帮我们挂载了一些组件与变量
const install = function(Vue, opts = {}) {
locale.use(opts.locale);
locale.i18n(opts.i18n);
// 把所有的组件注册到Vue上面
components.forEach(component => {
Vue.component(component.name, component);
});
Vue.use(InfiniteScroll);
Vue.use(Loading.directive);
Vue.prototype.$ELEMENT = {
size: opts.size || '',
zIndex: opts.zIndex || 2000
};
Vue.prototype.$loading = Loading.service;
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$prompt = MessageBox.prompt;
Vue.prototype.$notify = Notification;
Vue.prototype.$message = Message;
};
/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
// 导出版本号、install方法(插件)、以及一些功能比如国际化功能
export default {
version: '2.13.2',
locale: locale.use,
i18n: locale.i18n,
install,
Pagination,
Dialog,
Autocomplete,
// ...
};
文件开头的:
/* Automatically generated by './build/bin/build-entry.js' */
其实在上面的scripts
的build/bin/build-entry.js
中我们已经提到过:src/index.js
是由build-entry
脚本自动生成的。
这个文件主要做下以下事情:
packages
下的所有组件install
方法,把所有的组件注册到Vue
上面,并在Vue
原型上挂载了一些全局变量和方法install
方法、变量、方法导出存放了 ElementUI
的组件示例。
其实从目录结构,我们不难看出这是一个完整独立的Vue
项目。主要用于官方文档的展示:
这里我们主要关注下docs
文件夹:
Element
官网支持 4 种语言,docs
一共有 4 个文件夹,每个文件夹里面的内容基本是一样的。
我们可以看到里面全部都是md
文档,而每一个md
文档,分别对应着官网组件的展示页面。
❝其实现在各大主流组件库文档都是用采用
md
编写。❞
我们上面大致了解了源码的几个主要文件目录,但是都比较分散。下面我们从构建指令到新建组件、打包流程、发布组件完整的看一下构建流程。
平时我们都习惯将项目常用的脚本放在package.json
中的scripts
中。但ElementUI
还使用了Makefile
文件(由于文件内容较多,这里就选取了几个做下说明):
.PHONY: dist test
default: help
# build all theme
build-theme:
npm run build:theme
install:
npm install
install-cn:
npm install --registry=http://registry.npm.taobao.org
dev:
npm run dev
play:
npm run dev:play
new:
node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))
dist: install
npm run dist
deploy:
@npm run deploy
pub:
npm run pub
test:
npm run test:watch
// Tip:
// make new <component-name> [中文]
// 1、将新建组件添加到components.json
// 2、添加到index.scss
// 3、添加到element-ui.d.ts
// 4、创建package
// 5、添加到nav.config.json
我是第一次见,所以就去Google
下,网上对Makefile
对定义大概是这样:
❝
Makefile
是一个适用于C/C++
的工具,较早作为工程化工具出现在UNIX
系统中, 通过make
命令来执行一系列的编译和连接操作。在拥有make
环境的目录下, 如果存在一个Makefile
文件。那么输入make
命令将会执行Makefile
文件中的某个目标命令。❞
这里我以make install
为例简要说明下执行流程:
make
命令, 在该目录下找到 Makefile
文件。Makefile
文件中对应命令行参数的 install
目标。这里的目标就是 npm install
我们看下scripts
中的dev
指令:
"dev":
"npm run bootstrap &&
npm run build:file &&
cross-env NODE_ENV=development
webpack-dev-server --config build/webpack.demo.js &
node build/bin/template.js",
首先npm run bootstrap
是用来安装依赖的。
npm run build:file
在前面也有提到,主要用来自动化生成一些文件。主要是node build/bin/build-entry.js
,用于生成Element
的入口js
:先是读取根目录的components.json
,这个json
文件维护着Element
所有的组件路径映射关系,键为组件名,值为组件源码的入口文件;然后遍历键值,将所有组件进行import
,对外暴露install
方法,把所有import
的组件通过Vue.component(name, component)
方式注册为全局组件,并且把一些弹窗类的组件挂载到Vue
的原型链上(这个在上面介绍scripts
相关脚本时有详细说明)。
在生成了入口文件的src/index.js
之后就会运行webpack-dev-server
。
webpack-dev-server --config build/webpack.demo.js
这个前面也提过,用于跑Element
官网的基础配置。
上面我们提到了,Element
中还用了makefile
为我们编写了一些额外的脚本。
这里重点说一下 make new <component-name> [中文]
这个命令。
当运行这个命令的时候,其实运行的是 node build/bin/new.js
。
build/bin/new.js
比较简单,备注也很清晰,它帮我们做了下面几件事:
1、新建的组件添加到components.json
2、在packages/theme-chalk/src
下新建对应到组件scss
文件,并添加到packages/theme-chalk/src/index.scss
中
3、添加到 element-ui.d.ts
,也就是对应的类型声明文件
4、创建package
(我们上面有提到组件相关的源码都在package
目录下存放)
5、添加到nav.config.json
(也就是官网组件
左侧的菜单)
ElementUI
打包执行的脚本是:
"dist":
"npm run clean &&
npm run build:file &&
npm run lint &&
webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js &&
npm run build:utils &&
npm run build:umd &&
npm run build:theme",
下面我们一一来进行分析:
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
删除之前打包生成文件。
根据components.json
生成入口文件src/index.js
,以及i18n
相关文件。这个在上面已经做过分析,这里就不再展开进行说明。
"lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet",
项目eslint
检测,这也是现在项目必备的。
webpack --config build/webpack.conf.js &&
webpack --config build/webpack.common.js &&
webpack --config build/webpack.component.js
生成umd
格式的js
文件(index.js)
生成commonjs
格式的js
文件(element-ui.common.js),require
时默认加载的是这个文件。
以components.json
为入口,将每一个组件打包生成一个文件,用于按需加载。
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
把src
目录下的除了index.js
入口文件外的其他文件通过babel
转译,然后移动到lib
文件夹下。
"build:umd": "node build/bin/build-locale.js",
生成umd
模块的语言包。
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
根据components.json
,生成package/theme-chalk/index.scss
。用gulp
构建工具,编译scss
、压缩、输出css
到lib
目录。
最后用一张图来描述上述整个打包流程:
打包完成,紧跟着就是代码的发布了。Element
中发布主要是用shell
脚本实现的。
Element
发布一共涉及三个部分:
1、git 发布
2、npm 发布
3、官网发布
发布对应的脚本是:
"pub":
"npm run bootstrap &&
sh build/git-release.sh &&
sh build/release.sh &&
node build/bin/gen-indices.js &&
sh build/deploy-faas.sh",
运行 git-release.sh
进行git
冲突的检测,这里主要是检测dev
分支是否冲突,因为Element
是在dev
分支进行开发的。
#!/usr/bin/env sh
# 切换至dev分支
git checkout dev
# 检测本地和暂存区是否还有未提交的文件
if test -n "$(git status --porcelain)"; then
echo 'Unclean working tree. Commit or stash changes first.' >&2;
exit 128;
fi
# 检测本地分支是否有误
if ! git fetch --quiet 2>/dev/null; then
echo 'There was a problem fetching your branch. Run `git fetch` to see more...' >&2;
exit 128;
fi
# 检测本地分支是否落后远程分支
if test "0" != "$(git rev-list --count --left-only @'{u}'...HEAD)"; then
echo 'Remote history differ. Please pull changes.' >&2;
exit 128;
fi
# 通过以上检查,表示代码无冲突
echo 'No conflicts.' >&2;
dev
分支代码检测没有冲突,接下来就会执行release.sh
脚本,合并dev
分支到master
、更新版本号、推送代码到远程仓库并发布到npm
(npm publish)。
官网更新大致就是:将静态资源生成到examples/element-ui
目录下,然后放到gh-pages
分支,这样就能通过github pages
的方式访问。
到这里ElementUI
的完整构建流程就分析完了。
通过对ElementUI
源码文件和构建流程的分析,下面我们可以总结一下搭建一个完备的 ui 组件库都需要做什么工作。
目录结构对于大型项目是尤其重要的,合理清晰的结构对于后期的开发和扩展都是很有意义的。ui
组件库的目录结构,我感觉ElementUI
的就很不错:
|-- Element
|-- .babelrc // babel相关配置
|-- .eslintignore
|-- .eslintrc // eslint相关配置
|-- .gitattributes
|-- .gitignore
|-- .travis.yml // ci配置
|-- CHANGELOG.en-US.md
|-- CHANGELOG.es.md
|-- CHANGELOG.fr-FR.md
|-- CHANGELOG.zh-CN.md // 版本改动说明
|-- FAQ.md // 常见问题QA
|-- LICENSE // 版权协议相关
|-- Makefile // 脚本集合(工程化编译)
|-- README.md // 项目说明文档
|-- components.json // 组件配置文件
|-- element_logo.svg
|-- package.json
|-- yarn.lock
|-- .github // 贡献者、issue、PR模版
| |-- CONTRIBUTING.en-US.md
| |-- CONTRIBUTING.es.md
| |-- CONTRIBUTING.fr-FR.md
| |-- CONTRIBUTING.zh-CN.md
| |-- ISSUE_TEMPLATE.md
| |-- PULL_REQUEST_TEMPLATE.md
| |-- stale.yml
|-- build // 打包
|-- examples // 示例代码
|-- packages // 组件源码
|-- src // 入口文件以及各种辅助文件
|-- test // 单元测试文件
|-- types // 类型声明
参考大多数 UI
组件库的做法,可以将 examples
下的示例代码组织起来并暴露一个入口,使用 webpack
配置一个 dev-server
,后续对组件的调试、运行都在此 dev-server
下进行。
UI
组件作为高度抽象的基础公共组件,编写单元测试是很有必要的。合格的单元测试也是一个成熟的开源项目必备的。
对于打包后的文件,统一放在 lib
目录下,同时记得要在 .gitignore
中加上 lib
目录,避免将打包结果提交到代码库中。
同时针对引入方式的不同,要提供全局引入
(UMD)和按需加载
两种形式的包。
组件库的文档一般都是对外可访问的,因此需要部署到服务器上,同时也需具备本地预览的功能。
组件库的某个版本完成开发工作后,需要将包发布到 npm 上。发布流程:
发布后需要日常维护之前老版本,一般需要注意一下几点:
本文由哈喽比特于4年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/ziDMLDJcvx07aM6xoEyWHQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为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 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。