Express 是一个基于 Node.js 的 Web 框架。
Node.js 是一种搭建网络服务和应用的实用工具。
Express 搭建在 Node.js 之上,提供易于使用的功能来满足 Web 服务器的用例需求。它开源、免费、易于扩展并且非常高效。
可以使用各种各样的预构建的包来处理应用中的各项内容。
点击获取 Express 手册的 PDF 和 ePub 版本。(本文有几处外部链接,均标注为蓝色,请读者在文末点击“阅读原文”以访问这些链接。)
你可以使用 npm 在任何项目中安装 Express。
如果是一个空文件夹,首先使用命令创建一个新的 Node.js 项目:
npm init -y
然后执行:
npm install express
在项目中安装 Express。
我们要创建的第一个示例是一个简单的 Express Web 服务器:
请复制以下代码:
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(3000, () => console.log('Server ready'));
并将其保存在项目根文件夹的 index.js
文件中,然后通过以下命令启动服务器:
node index.js
你可以打开浏览器并通过 localhost 导航到 port 3000,就会看到 Hello World!
信息。
上面四行代码在幕后做了很多工作:
首先我们通过 express
变量引用 express
包。
通过调用 express()
方法来实例化一个应用。
一旦创建了应用对象,就使用 get()
方法监听来自 /
路径的 GET 请求。
每一种 HTTP 方法都有一个对应的动词:get()
、post()
、put()
、delete()
和 patch()
:
app.get('/', (req, res) => {
/* */
});
app.post('/', (req, res) => {
/* */
});
app.put('/', (req, res) => {
/* */
});
app.delete('/', (req, res) => {
/* */
});
app.patch('/', (req, res) => {
/* */
});
这些方法接受一个回调函数(当请求开始时调用),我们需要处理回调函数。
可以在回调中传入一个箭头函数:
(req, res) => res.send('Hello World!');
Express 在回调中发送两个对象:req
和res
,分别代表了请求(Request)和响应(Response)对象。
更多标准可以参考这里和这里。
请求是一个 HTTP 请求,它包含了所有请求信息:包括请求参数、标头、请求体等。
响应是 HTTP 响应对象,会返回给客户端。
在这个回调示例中,我们通过Response.send()
方法发送 "Hello World!" 字符串给客户端。
这个方法将字符串作为请求体,传输完毕后关闭连接。
Hello World 示例中最后一行代码启动服务器,并且告诉它在 port3000
监听。我们传入一个回调,当服务器准备好接受新请求时调用该回调。
我介绍了 Request 对象是如何持有 HTTP 请求信息的。
以下是主要的属性:
属性 | 介绍 |
---|---|
.app | 持有对 Express app 对象的引用 |
.baseUrl | app 响应的基本路径 |
.body | 包含在请求体提交的数据(必须手动解析和填充后才能访问) |
.cookies | 包含由请求发送的 cookies(需要 cookie-parser中间件) |
.hostname | Host HTTP 标头值定义的主机名 |
.ip | 客户端 IP |
.method | 使用的 HTTP 方法 |
.params | 路由命名参数 |
.path | URL 路径 |
.protocol | 请求协议 |
.query | 包含请求中使用的所有查询字符串的对象 |
.secure | 请求是安全时(使用 HTTPS)为 true |
.signedCookies | 包含由请求发送的签名 cookies(需要 cookie-parser 中间件) |
.xhr | 请求为 XMLHttpRequest 时为 true |
在 Hello World 示例中, 我们使用响应对象的send()
方法来将一个简单的字符串作为响应,之后关闭连接。
(req, res) => res.send('Hello World!');
如果你传入一个字符串,它将Content-Type
标头设置为text/html
。
如果你传入的是对象或者数组,它将Content-Type
标头设置为application/json
,并将传入的对象或数组解析为 JSON。
之后send()
关闭连接。
send()
自动设置 Content-Length
HTTP 响应标头,不像 end()
需要你手动设置。
另一个发送响应的方式,是使用Response.end()
方法,这种方法不发送任何响应体:
res.end();
在响应对象使用 status()
方法:
res.status(404).end();
或者
res.status(404).send('File not found');
sendStatus()
是快捷方式。
res.sendStatus(200);
// === res.status(200).send('OK')
res.sendStatus(403);
// === res.status(403).send('Forbidden')
res.sendStatus(404);
// === res.status(404).send('Not Found')
res.sendStatus(500);
// === res.status(500).send('Internal Server Error')
当你在 Express 中监听路由上的连接时,回调函数将在每次网络调用时被调用,并带有一个 Request 对象实例和一个 Response 对象实例。
示例:
app.get('/', (req, res) => res.send('Hello World!'));
我们在这里使用 Response.send()
方法,接受任意字符串。
你可以使用 JSON,即使用 Response.json()
发送到客户端。
它接受一个对象或者数组,并在发送前将其转换为 JSON 格式:
res.json({ username: 'Flavio' });
使用Response.cookie()
方法来控制 cookies。
示例:
res.cookie('username', 'Flavio');
这个方法还接受第三个参数,这个参数包含各种选项:
res.cookie('username', 'Flavio', {
domain: '.flaviocopes.com',
path: '/administrator',
secure: true
});
res.cookie('username', 'Flavio', {
expires: new Date(Date.now() + 900000),
httpOnly: true
});
一些你可以设置的有用的参数包括:
值 | 描述 |
---|---|
domain | cookie 的域名 |
expires | 设置 cookie 过期日期。如果未设置或者为 0,这个 cookie 将作为会话 cookie。 |
httpOnly | 设置 cookie 仅被 web 服务器访问。具体可查看 HttpOnly |
maxAge | 设置相对于当前时间的过期时间,以毫秒为单位 |
path | cookie 路径。默认值为 '/' |
secure | 标记为 cookie HTTPS only |
signed | 设置需要签名的 cookie |
sameSite | SameSite 的值 |
清除 cookie 可以使用:
res.clearCookie('username');
可以使用 Request.headers
属性访问所有 HTTP 标头的值:
app.get('/', (req, res) => {
console.log(req.headers);
});
使用Request.header()
方法获取单个请求标头的值:
app.get('/', (req, res) => {
req.header('User-Agent');
});
可以使用 Response.set()
改变 HTTP 标头值:
res.set('Content-Type', 'text/html');
Content-Type 标头的便捷方式是:
res.type('html');
// => 'text/html'
res.type('json');
// => 'application/json'
res.type('application/json');
// => 'application/json'
res.type('png');
// => image/png:
在 Web 开发中重定向很常见,可以使用 Response.redirect()
实现:
res.redirect('/go-there');
这样就创建了一个 302 重定向。
可以使用以下方法创建一个 301 重定向:
res.redirect(301, '/go-there');
你可以指定绝对路径(/go-there
)、绝对 URL(https://anothersite.com
)、相对路径(go-there
)或者 ..
返回上一层:
res.redirect('../go-there');
res.redirect('..');
你还可以重定向回 Referrer HTTP 标头值(如果未设置默认值为 /
):
res.redirect('back');
路由是确定调用 URL 时应该发生什么的过程,或者应用程序的哪些部分应该处理特定的传入请求。
在 Hello World 示例中,我们使用了这段代码:
app.get('/', (req, res) => {
/* */
});
这里创建一个路由,访问根域 URL/
,并使用 HTTP GET 方法映射到我们需要的响应。
如果想监听自定义请求怎么办?也许我们想创建一个接受字符串并将其作为大写字母返回的服务——我们不希望参数作为查询字符串发送,而是作为 URL 的一部分发送。在这种情况下,我们使用命名参数:
app.get('/uppercase/:theValue', (req, res) =>
res.send(req.params.theValue.toUpperCase())
);
如果发送请求到 /uppercase/test
,我们会在响应体中得到 TEST
。
你可以在同一个 URL 中使用多个命名参数,它们都将存储在 req.params
.
可以通过一条正则表达式来匹配多个路径:
app.get(/post/, (req, res) => {
/* */
});
以上代码将匹配 /post
、/post/first
、/thepost
、/posting/something
等路径。
Express 能够处理服务器端模板引擎。
模板引擎允许我们向视图添加数据,并动态生成 HTML。
Express 默认使用 Jade。Jade 是 Pug 的旧版本,特指 Pug 1.0。
请注意,由于商标问题,该项目在 2016 年发布第二版时,名称从 Jade 改为 Pug。你仍然可以使用 Jade,又称 Pug 1.0,但往后最好使用 Pug 2.0。
尽管 Jade 的最后一个版本已经陈旧了,但出于向后兼容的原因,它仍然是 Express 中的默认版本。
你可以在任何新项目中使用 Pug 或你选择的引擎。Pug 的官方网站是https://pugjs.org/。
可以使用许多不同的模板引擎,包括 Pug、Handlebars、Mustache、EJS 等。
要使用 Pug,我们必须先安装它:
npm install pug
在初始化 Express 应用程序时,我们需要设置它:
const express = require('express');
const app = express();
app.set('view engine', 'pug');
然后就可以在 .pug
文件中编写模板。
创建一个 about 视图:
app.get('/about', (req, res) => {
res.render('about');
});
模板路径为views/about.pug
:
p Hello from Flavio
该模板创建一个 p
标签,内容为 Hello from Flavio
。
你也可以使用以下代码插入变量:
app.get('/about', (req, res) => {
res.render('about', { name: 'Flavio' });
});
p Hello from #{name}
更多使用 Pug 的信息,可以查看 Pug 指南。
推荐你使用这个在线 HTML 到 Pug 转换器 https://html-to-pug.com/。
中间件是一个挂钩到路由过程中的函数,在链中的某个点执行任意操作(取决于我们想要它做什么)。
它通常用于编辑请求或响应对象,或者在请求到达路由处理程序代码之前终止请求。
可以通过如下方法将中间件添加到执行栈:
app.use((req, res, next) => {
/* */
});
这和定义路由类似,但是在 Request 和 Response 实例之后,我们还引用了 next 中间件函数,并分配给了next
变量。
我们总是在中间件函数末尾调用next()
以便将执行传递给下一个处理程序。除非我们想提前结束响应并将其发送回客户端。
通常我们通过 npm
包来使用预制的中间件。你可以在这里找到中间件列表。
其中一个预制中间件示例就是 cookie-parser
,它可以将 cookie 解析为 req.cookies
对象。你可以使用 npm install cookie-parser
命令安装并使用:
const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');
app.get('/', (req, res) => res.send('Hello World!'));
app.use(cookieParser());
app.listen(3000, () => console.log('Server ready'));
我们还可以将中间件函数设置为仅针对特定路由运行(而不是针对所有路由),方法是将其作为路由定义的第二个参数:
const myMiddleware = (req, res, next) => {
/* ... */
next();
};
app.get('/', myMiddleware, (req, res) => res.send('Hello World!'));
如果需要存储中间件生成的数据,并传递给后续中间件函数或请求处理程序,你可以使用Request.locals
对象。它将该数据附加到当前请求:
req.locals.name = 'Flavio';
通常图片、CSS 被存储在 public
子文件夹,并暴露给根目录:
const express = require('express');
const app = express();
app.use(express.static('public'));
/* ... */
app.listen(3000, () => console.log('Server ready'));
如果在 public/
有一个index.html
文件,当你访问根域 URL(http://localhost:3000
)时,就会提供静态资源。
Express 提供了一个简便的方法将文件转换为附件传输:Response.download()
。
一旦用户点击使用此方法发送文件的路由,浏览器将提示用户下载。
Response.download()
方法允许发送附加到请求的文件,浏览器不会在页面中显示它,而是将其保存到磁盘。
app.get('/', (req, res) => res.download('./file.pdf'));
在应用上下文中的示例:
const express = require('express');
const app = express();
app.get('/', (req, res) => res.download('./file.pdf'));
app.listen(3000, () => console.log('Server ready'));
你可以将文件设置为使用自定义文件名发送:
res.download('./file.pdf', 'user-facing-filename.pdf');
此方法提供了一个回调函数,你可以使用它在文件发送后执行代码:
res.download('./file.pdf', 'user-facing-filename.pdf', (err) => {
if (err) {
//handle error
return;
} else {
//do something
}
});
默认情况下,Express 请求是有顺序的,但请求之间没有相互链接,所以没有办法知道这个请求是否来自之前已经执行过请求的客户端。
除非使用某种识别机制,否则用户无法识别请求。
会话就应运而生。
使用会话后,你的 API 或网站的每个用户都将被分配一个唯一的会话,就可以存储用户的状态。
我们将使用 express-session
模块来演示,它由 Express 团队维护。
可以使用以下命令安装:
npm install express-session
安装完毕后,可以通过以下代码实例化:
const session = require('express-session');
它是一个中间件,所以你使用以下代码在 Express 中安装它:
const express = require('express');
const session = require('express-session');
const app = express();
app.use(
session({
secret: '343ji43j4n3jn4jk3n'
})
);
编写完毕后,所有应用路由都使用会话。
secret
是唯一的必填参数,还有许多可选参数。secret
必须为一个唯一的随机字符串。
会话被添加到请求,所以可以通过 req.session
访问:
app.get('/', (req, res, next) => {
// req.session
}
该对象可用于从会话中获取数据,也可用于设置数据:
req.session.name = 'Flavio';
console.log(req.session.name); // 'Flavio'
此数据在存储时被序列化为 JSON,所以可以安全使用嵌套对象。
你可以使用会话将数据传递给稍后执行的中间件,或者稍后根据后续请求检索数据。
会话数据存储在哪里?这取决于你如何设置express-session
模块。
会话数据可被存储在:
在 https://github.com/expressjs/session 中有一个巨大的第三方包列表,可以实现不同兼容性的缓存存储。
所有解决方案都将会话 ID 存储在 cookie 中,并将数据保存在服务器端。客户端将在 cookie 中接收会话 ID,并将它与每个 HTTP 请求一起发送。
我们将引用该服务器端以将会话 ID 与本地存储的数据相关联。
内存是默认设置,不需要你进行特殊操作。这样很便捷,但它仅用于开发目的。
最好是选择 Redis 之类的内存缓存,需要为其设置自己的基础架构。
另一个常用管理会话的包是cookie-session
,与 Redis 巨大的不同是将数据存储在客户端的 cookie。
我不建议这样做,因为将数据存储在 cookie 中意味着它存储在客户端,并在用户发出的每个请求中来回发送。它的大小也有限制,因为它只能存储 4 KB 的数据。
Cookie 也需要受到保护,但默认情况它不受保护,安全 Cookie 可以在 HTTPS 站点上使用,如果你使用代理上网,则需要配置它。
让我们看看如何验证作为输入进入 Express 端点的任何数据。
假设你有一个接受 name、email 和 age 参数的 POST 端点:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/form', (req, res) => {
const name = req.body.name;
const email = req.body.email;
const age = req.body.age;
});
如何对这些结果执行服务器端验证以确保:
在 Express 中处理来自外部的任何输入的验证的最佳方法是使用 <span style="color: rgb(0, 122, 170);">express-validator
包:
npm install express-validator
引用包中的check
和validationResult
对象:
const { check, validationResult } = require('express-validator');
在post()
调用中,我们将包含check()
调用的数组作为第二个参数传入。每一个 check()
都接受参数名作为实参。最后调用validationResult()
来验证是否有验证报错,如果有就告知客户端:
app.post(
'/form',
[
check('name').isLength({ min: 3 }),
check('email').isEmail(),
check('age').isNumeric()
],
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
const name = req.body.name;
const email = req.body.email;
const age = req.body.age;
}
);
注意我使用了:
isLength()
isEmail()
isNumeric()
还有更多方法,都来自 validator.js,包括:
contains()
, 检查是否包含指定值equals()
, 检查是否与指定值相等isAlpha()
isAlphanumeric()
isAscii()
isBase64()
isBoolean()
isCurrency()
isDecimal()
isEmpty()
isFQDN()
, 检查是否为完全合格的域名isFloat()
isHash()
isHexColor()
isIP()
isIn()
, 检查值是否属于允许值数组isInt()
isJSON()
isLatLong()
isLength()
isLowercase()
isMobilePhone()
isNumeric()
isPostalCode()
isURL()
isUppercase()
isWhitelisted()
, 检查输入是否在白名单内你也可以使用matches()
来进行正则表达式验证。
日期可以通过以下方式验证:
isAfter()
, 检查输入的日期是否在你传入的日期之后isBefore()
, 检查输入的日期是否在你传入的日期之前isISO8601()
isRFC3339()
更多如何使用验证器的方法,可以参考这份文档。
所有上述验证都可以串联起来:
check('name').isAlpha().isLength({ min: 10 });
如果有任何错误,服务器会自动发送一个响应来传达错误。例如,如果电子邮件无效,将返回以下内容:
{
"errors": [{
"location": "body",
"msg": "Invalid value",
"param": "email"
}]
}
可以使用 withMessage()
覆盖默认报错:
check('name')
.isAlpha()
.withMessage('Must be only alphabetical chars')
.isLength({ min: 10 })
.withMessage('Must be at least 10 chars long');
如果你想要编写自定义验证器,可以使用 custom
验证器。
在回调函数中,你可以通过抛出异常或返回被拒绝的 promise 来拒绝验证:
app.post(
'/form',
[
check('name').isLength({ min: 3 }),
check('email').custom((email) => {
if (alreadyHaveEmail(email)) {
throw new Error('Email already registered');
}
}),
check('age').isNumeric()
],
(req, res) => {
const name = req.body.name;
const email = req.body.email;
const age = req.body.age;
}
);
自定义验证器:
check('email').custom((email) => {
if (alreadyHaveEmail(email)) {
throw new Error('Email already registered');
}
});
也可改写为:
check('email').custom((email) => {
if (alreadyHaveEmail(email)) {
return Promise.reject('Email already registered');
}
});
你已经了解了如何验证从外部世界到 Express 应用程序的输入。
当运行面向公众的服务器时,你很快就会学到一件事:永远不要相信输入。
即使在客户端你已经预做了一遍清理,确保人们不会输入奇怪的内容,你仍然会受到人们使用工具(甚至只是浏览器开发工具)直接 POST 到端点的影响。
或者机器人尝试人类已知的每一种可能的攻击组合。
需要做的是在服务器端清理输入。
<span style="color: rgb(0, 122, 170);">express-validator
包除了可以验证输入以外也可以清理输入。
假设你有一个接受 name、email 和 age 参数的 POST 端点:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/form', (req, res) => {
const name = req.body.name;
const email = req.body.email;
const age = req.body.age;
});
你可以这样验证输入:
const express = require('express');
const app = express();
app.use(express.json());
app.post(
'/form',
[
check('name').isLength({ min: 3 }),
check('email').isEmail(),
check('age').isNumeric()
],
(req, res) => {
const name = req.body.name;
const email = req.body.email;
const age = req.body.age;
}
);
你可以通过在验证方法之后串联清理方法来添加清理:
app.post( '/form', [ check('name').isLength({ min: 3 }).trim().escape(), check('email').isEmail().normalizeEmail(), check('age').isNumeric().trim().escape() ], (req, res) => { //... } );
在代码中我使用的清理方法包括:
- `trim()`修剪字符串开头和结尾的字符(默认为空格)
- `escape()`将 `<`, `>`, `&`, `'`, `"`和 `/`替换成它们对应的 HTML 实体
- `normalizeEmail()`规范化电子邮件地址,它接受小写邮件地址或者子地址的选项。(如 `flavio+newsletters@gmail.com`)
其他的清理方法包括:
- `blacklist()` 删除出现在黑名单中的字符
- `whitelist()` 删除未出现在白名单中的字符
- `unescape()` 将 HTML 编码的实体替换为`<`, `>`, `&`, `'`, `"` 和 `/`
- `ltrim()` 类似于 trim(),但只修剪字符串开头的字符
- `rtrim()` 类似于 trim(), 但只修剪字符串末尾的字符
- `stripLow()`删除通常不可见的 ASCII 控制字符
强制转换格式:
- `toBoolean()` 将输入字符串转换为布尔值。除了 '0'、'false' 和 '' 之外的所有内容都返回 true。在严格模式下,只有 '1' 和 'true' 返回 true。
- `toDate()` 将输入字符串转换为日期,如果输入不是日期,则为 null。
- `toFloat()` 将输入字符串转换为浮点数,如果输入不是浮点数,则转换为 NaN。
- `toInt()`将输入字符串转换为整数,如果输入不是整数,则转换为 NaN。
与自定义验证器一样,你可以创建自定义清理器。
在回调函数中,你只需返回清理后的值:
const sanitizeValue = (value) => { //sanitize... };
app.post( '/form', [ check('value').customSanitizer((value) => { return sanitizeValue(value); }) ], (req, res) => { const value = req.body.value; } );
如何在 Express 中处理表单
-----------------
以下是一个 HTML 表单示例:
```
当用户按下提交按钮时,浏览器会自动向页面同源的/submit-form
URL 发出 POST
请求。浏览器发送表单包含的数据,编码为 application/x-www-form-urlencoded
。在此特定示例中,表单数据包含username
输入字段值。
表单也可以通过 GET
方法发送数据,但是大多数情况为POST
。
表单数据将在 POST 请求体中发送。
可以使用express.urlencoded()
中间件提取:
const express = require('express');
const app = express();
app.use(
express.urlencoded({
extended: true
})
);
现在,需要在/submit-form
路由上创建一个 POST
端点,任何数据都可以在 Request.body
访问:
app.post('/submit-form', (req, res) => {
const username = req.body.username;
//...
res.end();
});
别忘记提前使用express-validator
验证数据。
以下代码是允许用户上传文件的 HTML 表单示例:
<form method="POST" action="/submit-form" enctype="multipart/form-data">
<input type="file" name="document" />
<input type="submit" />
</form>
别忘记在表单添加enctype="multipart/form-data"
,否则表单不会被上传。
当用户按下提交按钮时,浏览器会自动向页面同源的/submit-form
URL 发出 POST
请求。浏览器发送表单包含的数据,但表单未编码为普通表单 application/x-www-form-urlencoded
,而是 multipart/form-data
。
在服务器端,处理多部分数据可能很棘手且容易出错,因此我们将使用一个名为 formidable 的库。这里是它的 GitHub 仓库 – 拥有超过 4000 颗星,并且维护良好。
可以通过以下命令安装:
npm install formidable
然后引用到 Node.js 文件:
const express = require('express');
const app = express();
const formidable = require('formidable');
现在,在 /submit-form
路由的 POST
端点中,我们使用 formidable.IncomingForm()
实例化一个新的 Formidable 表单:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm();
});
这样做之后,我们需要解析表单。我们可以通过回调来同步执行此操作,这意味着所有文件都已处理。一旦 formidable 完成,文件就可以被访问:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm().parse(req, (err, fields, files) => {
if (err) {
console.error('Error', err);
throw err;
}
console.log('Fields', fields);
console.log('Files', files);
for (const file of Object.entries(files)) {
console.log(file);
}
});
});
或者可以使用事件而不是回调。例如,当每个文件被解析时,或其他事件(例如文件处理完成、接收非文件字段或发生错误)时,都会收到通知:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm()
.parse(req)
.on('field', (name, field) => {
console.log('Field', name, field);
})
.on('file', (name, file) => {
console.log('Uploaded file', name, file);
})
.on('aborted', () => {
console.error('Request aborted by the user');
})
.on('error', (err) => {
console.error('Error', err);
throw err;
})
.on('end', () => {
res.end();
});
});
无论选择哪种方式,你都将获得一个或多个 Formidable.File 对象,这些对象为你提供有关已上传文件的信息。这些是可以调用的一些方法:
file.size
, 以字节为单位的文件大小file.path
, 文件写入的路径file.name
, 文件名file.type
, 文件的 MIME 类型路径默认为临时文件夹,如果监听 fileBegin
事件可以修改:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm()
.parse(req)
.on('fileBegin', (name, file) => {
file.path = __dirname + '/uploads/' + file.name;
})
.on('file', (name, file) => {
console.log('Uploaded file', name, file);
});
//...
});
这就是手册的全部内容。别忘了,如果需要的话,你可以下载该手册的 PDF 或者 ePub 版本。
本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/JqBZBmOZIk4D5lj5RPuInA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为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 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。