Deno 的目标是为现代程序员提供一个高效、安全的脚本环境。
与 Node.js 类似,Deno 也是运行在服务器端的 JavaScript。 它更像是升级版的 Node.js,可以认为是 Node 2.0 版。
Why Deno?
Node.js 已经成为 JavaScript 生态系统中重要的一部分,被广泛的应用。然后 Node.js 之父 Ryan Dahl 不满意它的设计,想通过一个全新的设计来弥补Node的缺陷。这就是Deno 产生的原因。
Deno 是一个全新的 JavaScript 和 TypeScript 运行时,由 V8 JavaScript 引擎 Rust 和 TypeScript 实现来:
- 1、语言优势
Deno 天生就支持 JavaScript 和 TypeScript。 尤其是在前端逐渐在 ts 化的路上,Deno 的优势会越来越明显。
- 2、兼容性
Deno 设计之初就尝试同时运行在浏览器端和服务端,它会尽可能的支持最新的 ES 特性。
- 3、安全性
Deno 默认是安全的,除非设置允许,否则不能读写文件、发起网络请求或访问环境变量。这可以防止 Deno 的恶意使用。这块是 Node 不如 Deno 的地方。
- 4、标准库
Deno 在 JavaScript 之上提供了许多内部实用函数。此外,Deno 还提供了几个内置的工具来改善开发体验。
下面我们从头开始一步一步实现一个微型 Deno 应用程序:
安装 Deno
限于篇幅仅介绍 mac 平台下的安装:
Shell (Mac, Linux):
curl -fsSL https://deno.land/x/install/install.sh | sh
Homebrew (Mac):
brew install deno
查看安装是否成功:
deno --version
-> deno 1.0.5
-> v8 8.4.300
-> typescript 3.9.2
想更新 Deno 版本可使用 upgrade 命令。
也可以执行以下远程脚本验证 Deno 是否安装成功:
deno run https://deno.land/std/examples/welcome.ts
-> Welcome to Deno
仅仅输出一段文本。演示了 deno 通过动态下载和编译从远程源代码执行应用。
如果安装过程中出现问题,可以给我留言,或参考官网
从 Hello Deno 开始
mkdir deno-example
cd deno-example
touch index.js
index.js 中输入:
console.log("Hello Deno");
deno run xxx.js 执行指定文件:
deno run index.js
-> Hello Deno
恭喜你,完成了第一个 Deno 应用, ^_^
deno 的权限机制
前面我提到说 Deno 默认是安全的:
const url = "https://api.github.com/users/answer518";
fetch(url)
.then((result) => result.json())
.then((result) => {
console.log(result);
});
当你像执行 hello deno 一样运行 deno run request.js:
error: Uncaught PermissionDenied: network access to "https://api.github.com/users/answer518", run again with the --allow-net flag
at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
at Object.sendAsync ($deno$/ops/dispatch_json.ts:98:10)
at async fetch ($deno$/web/fetch.ts:296:27)
at async file:///Users/guotingjie/research/deno-project/request.js:3:16
在 Deno 中执行以下操作:网络请求,读写文件, 这些都需要得到 Deno 的许可:
deno-example deno run --allow-net request.js
{
login: "answer518",
id: 17827860,
node_id: "MDQ6VXNlcjE3ODI3ODYw",
avatar_url: "https://avatars1.githubusercontent.com/u/17827860?v=4",
gravatar_id: "",
url: "https://api.github.com/users/answer518",
html_url: "https://github.com/answer518",
followers_url: "https://api.github.com/users/answer518/followers",
following_url: "https://api.github.com/users/answer518/following{/other_user}",
gists_url: "https://api.github.com/users/answer518/gists{/gist_id}",
starred_url: "https://api.github.com/users/answer518/starred{/owner}{/repo}",
subscriptions_url: "https://api.github.com/users/answer518/subscriptions",
organizations_url: "https://api.github.com/users/answer518/orgs",
repos_url: "https://api.github.com/users/answer518/repos",
events_url: "https://api.github.com/users/answer518/events{/privacy}",
received_events_url: "https://api.github.com/users/answer518/received_events",
type: "User",
site_admin: false,
name: "果糖酱",
company: "undefined",
blog: "https://answer518.github.io/",
location: "China Beijing",
email: null,
hireable: null,
bio: null,
twitter_username: null,
public_repos: 53,
public_gists: 0,
followers: 2,
following: 31,
created_at: "2016-03-14T12:27:14Z",
updated_at: "2020-03-31T12:08:12Z"
}
兼容性
在上面的例子中 fetch API 用于发起网络请求,这属于 es 标准 API,而这方面 Deno 尝试与 es 最新特性保持一致。
另外像 async/await,node 6.x 版本开始支持, 在 Deno 中是默认支持的:
const url = "https://api.github.com/users/answer518";
const result = await fetch(url).then((result) => result.json());
console.log(result);
标准库
Deno 附带了一组实用函数,称为 Deno 的标准库。
下面演示一个利用 Deno 标准库实现一个 web server:
import { serve } from "https://deno.land/std/http/server.ts";
const server = serve({ port: 8080 });
for await (const req of server) {
req.respond({ body: "Hello Deno" });
}
浏览器打开 http://localhost:8000/,就会向 Deno 应用程序发出一个 HTTP GET 请求,该请求返回一个带有 “Hello Deno” 正文的 HTTP 响应,随后将在浏览器中显示。
没问可以扩展一下上面这个案例:
import { serve } from "https://deno.land/std/http/server.ts";
const url = "https://api.github.com/users/answer518";
const server = serve({ port: 8000 });
for await (const req of server) {
const result = await fetch(url).then((result) => result.json());
req.respond({ body: JSON.stringify(result) });
}
前面我们是在控制台中看到结果,现在可以直接通过浏览器中直接看到 fetch 得到的结果。
单元测试
首先看一个在 Deno 中单元测试的例子:
import { mapStory } from "./stories.js";
Deno.test("maps to a smaller story with formatted date", () => {});
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
import { mapStory } from "./stories.js";
Deno.test("maps to a smaller story with formatted date", () => {
const stories = [
{
id: "1",
title: "title1",
url: "url1",
created_at_i: 1476198038,
},
];
const expectedStories = [
{
title: "title1",
url: "url1",
createdAt: "2016-10-11",
},
];
assertEquals(stories.map(mapStory), expectedStories);
});
assertEquals 函数接收两个参数:第一个是我们要测试的对象,第二个是我们期望的值:
deno test
-> running 1 tests
-> test maps to a smaller story with formatted date ... ok (9ms)
-> test result: ok. (10ms)
-> 1 passed;
-> 0 failed;
-> 0 ignored;
-> 0 measured;
-> 0 filtered out
typescript
Deno 同时支持 JavaScript 和 TypeScript 两种语言。无论导入的是 Deno 标准库还是本地项目文件,都需要加上文件扩展名:
假设我们将 index.js 改为 index.ts , 那么我们需要调整所有引用 index 文件的地方: .js 改为 .ts。
import { format } from "https://deno.land/x/date_fns/index.js";
interface Story {
title: string;
url: string;
created_at_i: number;
}
interface FormattedStory {
title: string;
url: string;
createdAt: string;
}
export const mapStory = (story: Story): FormattedStory => ({
title: story.title,
url: story.url,
createdAt: format(new Date(story.created_at_i * 1000), "yyyy-MM-dd"),
});
想要启动你的 Deno 应用,必须指定 .ts 后缀的文件名:
deno run --allow-net index.ts
可以通过修改
.tsconfig文件来修改默认的typescript配置
Deno 中的环境变量
创建一个 .env 文件
PORT = 8000;
index.ts 演示了如何从 .env 文件中读取配置变量:
import { serve } from "https://deno.land/std/http/server.ts";
import { config } from "https://deno.land/x/dotenv/mod.ts";
import { mapStory } from "./stories.ts";
const url = "http://hn.algolia.com/api/v1/search?query=javascript";
const server = serve({
port: parseInt(config()["PORT"]),
});
for await (const req of server) {
const result = await fetch(url).then((result) => result.json());
const stories = result.hits.map(mapStory);
req.respond({ body: JSON.stringify(stories) });
}
.env 中属性以键值对形式存在,之所以对 port 进行类型转换,因为属性值是以字符串类型存在的。
默认读取文件需要显式允许才可以:
deno run --allow-net --allow-read index.ts
重要:.env 涉及到敏感信息,不应该被提交到 git 仓库,记得加入.gitignore 文件中
结束
这就是有关 Deno 使用的基础知识。是不是感觉和 Node 差不多,适用场景多么的相似,相比 Node,它有很多的优点,比如支持 typescript,默认更安全等等。
至于将来 Deno 会不会替代 Node, 个人感觉短期内很难,至少等到 5 年以后。不过本人很看好 Deno 的未来。