关于deno:Deno-初识

介绍Deno 是一个 能够保障平安和开发体验的 js/ts 运行时, 不同于 Node(c++开发的),用 Rust 语言开发,而 Rust 自身也反对 WebAssembly,同时也有 js 内置 V8 引擎以及 tsc 引擎,所以也反对运行javascript、ts语言,应用 Rust 的 Tokio 库来实现事件循环特点 默认平安的,文件、网络或者环境拜访须要开发者受权内嵌 tsc, 能够解析 ts 文件通过一个可执行程序 deno 运行代码内含依赖查看(deno info) 和 代码格式化工具 (deno fmt)有一组曾经 reviewed 的规范模块能够和 deno 一起工作deno 脚本能够打包成单个的 js 文件原理指标是给古代程序员提供一个高效和平安的脚本环境,deno 程序最终作为一个独自可执行文件被散发。它应用浏览器兼容的标准协议 URL 加载模块,加载的这个模块压缩后不能超过 15 MB;deno 承当运行时和包治理的角色指标 提供一个可执行程序 deno,所有操作通过这个文件实现,反对跨平台 Mac、Linux、Windows提供平安默认值:除非许可,否则脚本不能拜访文件、环境或者网络浏览器兼容:齐全应用 js 写的并且没有应用全局 deno namespace(或者测试中的性能)的,应该能够间接当初浏览器里运行,无需任何更改提供构建工具,比方单元测试、代码格式化 和 代码问题检测, 为了提供开发者体验不会透露 V8 概念给用户可能无效的提供 http 服务和 Node 比对Deno 不应用 npm, 它应用 urls 和 文件 path 加载模块不应用 package.json 解析模块所有的异步行为都是返回一个 promise, 不像 node, promise 和 callback 都有Deno 须要明确许可,能力拜访文件、网络和环境Deno 总是挂在未捕捉的谬误应用 es modules, 不反对 require(), 第三方模块通过 urls 导入,如:import * as log from "https://deno.land/std/log/mod.ts";其余要害行为近程代码第一次拉取之后会被缓存,永远不会过期,除非运行代码时应用 --reload tag, 从近程 url 加载的模块或者文件被认为是不可批改和可缓存的外部构建的工具以及相应命令 ...

December 19, 2022 · 1 min · jiezi

关于deno:手把手教你发布一个deno模块

Node.js的包管理工具是npm,而对于deno而言,任何一个资源服务器都能够是资源的提供方,一般来说,它是一个CDN网站。除了比拟出名的用来转换node包的https://cdn.skypack.dev/和https://esm.sh/之外,官网的CDN地址是这里:https://deno.land/x。 如何公布模块如何公布一个deno的模块?操作其实很简略。 前提是你有一个github账号,新建一个对外开放的工程。公布前,在你的工程里,最好先配置好标签与形容。标签用来在deno.land中搜寻,形容便于其余开发者理解你的用图。 回到https://deno.land/x页面。点击Publish a module,呈现以下弹窗: 点击下一步填写一个惟一的模块名称,比方xx_test。如果没有反复的,就能够点击下一步。填写要公布的工程中文件夹门路,个别状况下咱们都是根目录,不填即可。 下一步是提醒页面按上一步的提醒一步步就能够实现了。这里我再具体点,到咱们的github工程,找到设置-Webhooks,点击减少webhook按钮。填写Payload URL为提醒页面的地址;批改Content-type为application/json。抉择Which events would you like to trigger this webhook?的最初一项。只勾选第一个。点击Add webhook按钮页面会主动回到这里,看到有了一条记录。点击编辑,会有2个tab页,第一个就是你方才配置的页面,第二个是你当前所有hooks的状态。如果失败了,能够在这里看详细信息。你的工程应用git打标签,如git tag -a v0.0.1 -m "feat: xxx",推送到github,就会触发hooks。回到https://deno.land/x,在上面就能看到你新推送的模块了。点击你的带版本号的工程,能够看到弹出的地址是https://deno.land/x/xx_test@v...。每有一个新tag的推送,就会有一个新的版本。须要留神的一点是,deno对集体公布模块的数量限度为15个,超过须要向管理员发邮件(modules@deno.com)申请,所以没事儿不要发垃圾的包上去,撤销也是须要向管理员申请的,而且也不肯定会通过。疾速公布个别状况下,咱们须要在根目录下创立一个README.md,阐明你的模块性能与应用办法。以我的工程oak_nest为例,通常有段应用示例: import { Body, Context, UseGuards,} from "https://deno.land/x/oak_nest@v1.2.1/mod.ts";示例里有你的模块地址,这个地址的版本号如果每次公布都要手动批改,那就太麻烦了。 全局装置一个命令: deno install --allow-read --allow-write --allow-run --unstable -n deno_tag -f https://deno.land/x/jw_cli@v0.2.7/cli/tag/mod.ts在根目录下创立一个scripts.yml文件,内容大抵如下: version: 0.0.1name: xx_test之后就能够应用deno_tag来更新版本,并推送到服务器。具体阐明具体见这里。 版本更新版本号更新的命令应用patch/minor/major,与npm的一样。 deno_tagdeno_tag patch # 与下面等价deno_tag minordeno_tag major会更新根目录下的scripts.yml文件和README.md,如果后者有应用scripts.yml中配置的name,将会对应替换。比方工程的名称为oak_nest@v1.2.1,那么执行deno_tag后,README.md中oak_nest@v1.2.1会对应替换为oak_nest@v1.2.2。如果执行deno_tag minor后,README.md中oak_nest@v1.2.1会对应替换为oak_nest@v1.3.0。如果执行deno_tag major后,README.md中oak_nest@v1.2.1会对应替换为oak_nest@v2.0.0。 版本号不以v结尾假如你推送的tag版本号不想以v结尾,那么能够增加一个参数-L或者--local: deno_tag patch -L增加自定义信息打标签时默认提交信息是版本号,如果想自定义信息,能够应用-M或者--msg: deno_tag minor -M "feat: change some"更新所有目录的README.md文件如果想要更新所有目录的README.md文件,能够应用-D或者--deep: deno_tag -D本地调试如果你的另一个我的项目中应用这个模块,那可能会遇到调试问题,如果每次公布都推送一次,那太不合理了(尽管你能够这么干,github的hooks也是秒级的)。你能够在本地启动一个服务器。 全局装置file_server deno install --allow-net --allow-read https://deno.land/std@0.125.0/http/file_server.ts在你的模块目录下,执行命令: ...

February 13, 2022 · 1 min · jiezi

关于deno:Deno-2021-回顾优化-Deno-内核兼容-NodejsDeno-2-路线图将至

近日,Deno 官网博客对其我的项目在刚刚过来的 2021 年所产生的重大事件和新变动做了回顾。 作为 Deno 第一笔资金投入的一年,该公司在 2021 里在 10 个方面有了弱小妻妾继续的倒退:公布了 44 个版本(11 个主要,33 个补丁),landing 要害特色,有数 bug 的修复以及在性能方面的显著优化。 在该博客文章里,Deno 对以下几个次要方面的倒退做了探讨: Deno Deploy2021年夏天,Deno 官网公布了 Deno Deploy 的第一个测试版 —— 一款由 Deno 工程团队从头开始构建的古代 serverless 云,容许用户疾速地将 JavaScript、TypeScript 和 WASM 服务部署到世界各地的数据中心。 目前,Deno 正在踊跃开发和投资 Deno Deploy,该我的项目被认为是继构建 Node.js 和 Deno CLI 之后该公司的第三个 JavaScript runtime。 优化 Deno 内核Deno 的内核提供了“opcalls”(相似于syscalls),容许 JavaScript 调用 runtime 提供的函数(fs/net/url-parsing/…)。 在 v1.9 版本公布之前,Deno 公司通过 JSON 和二进制缓冲区的混合来对opcall 值进行整顿。 op-layer 的效率是决定 runtime 整体性能的要害指标。相比此前每次调用 Opcalls 破费约 4000ns,当初每次调用破费仅为约 40ns,缩小了近 100 倍。这些效率进步的次要起因是 serde_v8 —— 开发团队在 v1.9 版本中设计并交付的 Rust 和 V8 值之间的最大化 efficient bijection。 ...

January 27, 2022 · 2 min · jiezi

关于deno:deno编译问题

deno如果应用typescript进行开发,可能会遇到一个编译的问题,因为它会把ts文件编译为js,再执行,但因为目前deno尚不是很稳固,有时候你会感觉匪夷所思,明明逻辑没有问题,但为什么运行后果就是不对呢?这的确是deno的坑,上面咱们剖析下怎么解决这个问题。 咱们以前应用nestjs开发时,目录构造是这样的:咱们留神到,执行npm run dev启动服务后,会生成一个dist目录,外面都是编译后的js文件。而应用deno开发的话,并没有这个目录。它并非不生成,而是放在另外一个中央。 以我本机为例,执行deno info: DENO_DIR location缓存文件的整体目录。前面几个都是它的子文件夹。它能够通过设置环境变量DENO_DIR来进行批改。 Remote modules cache也就是deps文件夹,其实就是咱们近程下载的文件。能够看到,文件被hash化了。值得注意的是,这hash值与lock.json文件中的hash值并不是间接对应的。以https://deno.land/std@0.110.0/path/separator.ts文件在lock.json的hash为例,它的值是_8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c_,但在deps/https/deno.land目录下,其实是_8565f1188fdc3690450ed80e4ae0b6167f838da4ecd5c9dbb20497723935fc1_0文件和它相关联的_8792e4b43e1cee8957317151086907b9bf36c7e8c17f7963a4d939ea486b7d72.metadata.json_。前者的内容:与deno.land上截然不同:而后者的内容如下: { "headers": { "cache-control": "public, max-age=31536000, immutable", "x-amz-request-id": "4CCZP09KD2XKWZZS", "content-type": "application/typescript; charset=utf-8", "date": "Fri, 17 Dec 2021 05:54:25 GMT", "server": "deploy/asia-east2-b", "x-amz-id-2": "Rm5JmINRF3YCv/kyP10DNS5Rlm0mSpFl8HkEZENifUEjfmmJc7u9xY2xAvtt+cny4taa9lfjSjM=", "x-amz-version-id": "CxnJt0wYBvGR8lbuUd1k3Nwv2biTn6fT", "access-control-allow-origin": "*", "last-modified": "Thu, 16 Dec 2021 16:30:42 GMT", "content-length": "259", "etag": "\"49cabf94e5bf1cea284f1df0cb1380aa\"" }, "url": "https://deno.land/std@0.118.0/path/separator.ts"}之所以不统一,应该是有一个值还加密了文件夹名称或者版本号之类,具体是怎么对应的,有趣味的同学能够钻研下。这样不能不便找到编译后具体文件的结果是咱们很难像Node.js一样间接批改node_modules中文件进行本地调试。 Emitted modules cache对应的是gen目录。猜想大略是generate的缩写。也就是一开始说到的,ts编译后js所寄存的目录。file目录下就是咱们本地运行过deno的所有工程编译后的文件夹,而https就是咱们下载的近程文件编译后的代码。每个ts文件会生成2个文件,一个后缀加了.js,一个后缀加了.meta。前者就是编译后的js代码:而后者是记录了版本号的hash值。 {"version_hash":"d8c590642ab47a3e9add6c86cb4b10c250447a861774a7b0cc8d693bc0f6c6bc"}咱们说有时候编译会有问题,那遇到这种状况,就只能删除gen目录下对应的文件,或者暴力一些,间接把gen都干掉。惟一的影响只是代码运行时须要从新编译,耗时久一些。可能有的同学被吓住了,这状况呈现的频繁吗?那还怎么开发啊?我目前是进行目录挪动时偶然会呈现,可能是typescript的增量编译引起的,deno集成它时哪里还有问题。心愿deno将来几个版本可能修复。 Language server registries cache对应的registries目录。外面记录了些模块名称、版本号等。 Origin storage对应location_data目录。这个目录是用来存储localStorage内容的,可见deno为了兼容浏览器生态真是很拼啊。 localStorage.setItem("myDemo", "Deno App2");console.log(localStorage.getItem("myDemo")); 必须加--location参数运行,例如:deno run --location https://www.baidu.com storeTest.ts ...

December 25, 2021 · 1 min · jiezi

关于deno:deno怎么复用node的包

上文说过,deno目前最大的问题是生态,也就是说仍无奈无缝继承nodejs的生态。那它也意味着能够应用一部分nodejs的资源。 以AST(形象语法树)为例,它在底层工作时还是很有必要的,咱们可能会用它写个小工具,实现一些简略的代码转换。但咱们在deno.land中查找第三方库,却没有找到适合的。 怎么办?这时你首先想到的不该是“正是我辈大展身手造轮子的时候到了”,而是应该想,node的npm包是不是能够复用? node版本AST有各种实现版本,比拟闻名的是babel。这里我找两个简略的esprima和escodegen。 const esprima = require('esprima');const escodegen = require('escodegen');const encodeJs = function () { const str = ` global.x = 10; name += '-abc'; age = 12; `; const AST = esprima.parse(str); // console.log(AST); // console.log(AST.body[0].expression.left.object.name); // console.log(result.body[0].consequent.body[0].expression.left); AST.body.splice(0, 1); // remove global.x = 10; const originReback = escodegen.generate(AST); console.log(originReback); // name += '-abc';age = 12;};encodeJs();咱们先在npm上看下这两个包的依赖状况:一个零依赖,一个依赖了5个包。还是很有心愿通过CDN本义过去的。 deno版本咱们试试CDN(通常有https://esm.sh/和https://cdn.skypack.dev/两个,这里任选一个)转下:https://cdn.skypack.dev/esprimahttps://cdn.skypack.dev/escodegen仿佛是能够的,于是咱们能够试下: import * as esprima from 'https://cdn.skypack.dev/esprima';import * as escodegen from "https://cdn.skypack.dev/escodegen";const encodeJs = function () { const str = ` global.x = 10; name += '-abc'; age = 12; `; const AST = esprima.parse(str); AST.body.splice(0, 1); // remove global.x = 10; const originReback = escodegen.generate(AST); console.log(originReback); // name += '-abc';age = 12;};encodeJs();执行deno run xx.js,胜利! ...

December 25, 2021 · 1 min · jiezi

关于deno:从node到deno

简介Deno简略说是Node.js的替代品,是Node.js之父Ryan Dahl 为挽回Node.js的谬误而开发的。 Node.js存在的问题有: npm包治理(node_modules)简单。历史起因导致的api保护,比方晚期变态的callback设置。没有安全措施,用户只有下载了内部模块,就只好放任他人的代码在本地运行,进行各种读写操作。性能不欠缺,导致各种工具层出不穷,比方webpack、babel等。因为下面这些起因,Ryan Dahl 决定放弃 Node.js,从头写一个替代品,彻底解决这些问题。deno 这个名字就是来自 Node 的字母重新组合(Node = no + de),示意"拆除 Node.js"(de = destroy, no = Node.js)。 跟 Node.js一样,Deno 也是一个服务器运行时,然而反对多种语言,能够间接运行 JavaScript、TypeScript 和 WebAssembly 程序。 它内置了 V8 引擎,用来解释 JavaScript。同时,也内置了 tsc 引擎,解释 TypeScript。它应用 Rust 语言开发,因为 Rust 原生反对 WebAssembly,所以它也能间接运行 WebAssembly。它的异步操作不应用 libuv 这个库,而是应用 Rust 语言的 Tokio 库,来实现事件循环(event loop)。它的架构如下图所示: 阐明:1、Rust 是由 Mozilla 主导开发的通用、编译型编程语言。设计准则为 “平安、并发、实用”,反对函数式、并发式、过程式以及面向对象的编程格调。Deno 应用 Rust 语言来封装 V8 引擎,通过 libdeno 绑定,咱们就能够在 JavaScript 中调用隔离的性能。 2、Tokio 是 Rust 编程语言的异步运行时,提供异步事件驱动平台,构建疾速,牢靠和轻量级网络应用。利用 Rust 的所有权和并发模型确保线程平安。Tokio 构建于 Rust 之上,提供极快的性能,使其成为高性能服务器应用程序的现实抉择。在 Deno 中 Tokio 用于并行执行所有的异步 IO 工作。3、V8 是一个由 Google 开发的开源 JavaScript 引擎,用于 Google Chrome 及 Chromium 中。V8 在运行之前将JavaScript 编译成了机器代码,而非字节码或是解释执行它,以此晋升性能。更进一步,应用了如内联缓存(inline caching)等办法来进步性能。有了这些性能,JavaScript 程序与 V8 引擎的速度媲美二进制编译。在 Deno 中,V8 引擎用于执行 JavaScript 代码。劣势Deno凭什么来吸引开发者改换门庭,转投它的怀抱呢?于我而言,次要有以下几点: ...

December 25, 2021 · 5 min · jiezi

关于deno:从koa到oak

koakoa是Node.js的一个web开发框架,它是由 Express 原班人马打造的,致力于成为一个更小、更富裕表现力、更强壮的 Web 框架。应用 koa 编写 web 利用,通过组合不同的 generator,能够罢黜反复繁琐的回调函数嵌套,并极大地晋升错误处理的效率。koa 不在内核办法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 利用变得得心应手。 与Express区别这里简略讲下koa与Express的次要区别: Express 封装、内置了很多中间件,比方 connect 和 router ,而 koa 则比拟轻量,开发者能够依据本身需要订制框架;Express 是基于 callback 来解决中间件的,而 koa 则是基于async/await;在异步执行中间件时,Express 并非严格依照洋葱模型执行中间件,而 koa 则是严格遵循的(体现在二者在中间件为异步函数的时候解决会有不同)。所以,须要先介绍下洋葱模型。 洋葱模型洋葱咱们都晓得,一层包裹着一层,层层递进,然而当初不是看其平面的构造,而是须要将洋葱切开来,从切开的立体来看,如图所示: 能够看到,要从洋葱中心点穿过来,就必须先一层层向内穿入洋葱表皮进入中心点,而后再从中心点一层层向外穿出表皮。这里有个特点:进入时穿入了多少层表皮,进来时就必须穿出多少层表皮。先穿入表皮,后穿出表皮,合乎咱们所说的栈列表,先进后出的准则。无论是Express还是koa,都是基于中间件来实现的。中间件次要用于申请拦挡和批改申请或响应后果的。而中间件(能够了解为一个类或者函数模块)的执行形式就须要根据洋葱模型。 洋葱的表皮咱们能够思考为中间件: 从外向内的过程是一个关键词 next();如果没有调用next(),则不会调用下一个中间件;而从外向外则是每个中间件执行结束后,进入原来的上一层中间件,始终到最外一层。也就是说,对于异步中间件,koa与Express在某种状况代码的执行程序会有差别。 异步差别同样的逻辑,先来Express: const express = require('express')const app = express()app.use(async (req, res, next) => { const start = Date.now(); console.log(1) await next(); console.log(2)})app.use(async (req, res, next) => { console.log('3') await next() await new Promise( (resolve) => setTimeout( () => { console.log(`wait 1000 ms end`); resolve() }, 1000 ) ); console.log('4')})app.use((req, res, next) => { console.log(5); res.send('hello express')})app.listen(3001)console.log('server listening at port 3001')失常而言,咱们冀望返回后果程序是: ...

December 25, 2021 · 5 min · jiezi

关于deno:Deno-简单测试服务器

有时候会须要长期起一个简略的服务器,用来测试查看申请信息。刚好用 Deno 官网的例子改一个。用 Node.js 写其实也一样不便。 启动命令 deno run --allow-net server.tsimport { parse } from "https://deno.land/std@0.104.0/flags/mod.ts";import { serve } from "https://deno.land/std@0.104.0/http/server.ts";interface Args { _: string[]; // --prot port?: number; // --on-headers "on-headers"?: boolean;}const args = <Args>parse(Deno.args); // 获取启动参数/** * 启动 deno run --allow-net server.ts */const PORT = args.port ? args.port : 7070;const server = serve({ port: PORT });console.log( `HTTP webserver running. Access it at: http://localhost:${PORT}/`);for await (const request of server) { if (request.url !== "/favicon.ico") { const time = formatTimestamp(new Date().getTime()); console.log(""); console.log( `\x1b[32m[${time}]\x1b[0m method: ${request.method} url: ${request.url}` ); if (args["on-headers"]) console.log(`headers: `, request.headers); } request.respond({ status: 200, body: "ok" });}/** * 工夫戳格式化 * @param timestamp * @param options * @returns */function formatTimestamp( timestamp: number, options?: { dateSection: boolean; timeSection: boolean; dateSign: string; timeSign: string; linkSign: string; places: number; }) { const $d = new Date( timestamp.toString().length === 10 ? timestamp * 1000 : timestamp ); if ($d.toString() === "Invalid Date") return "Invalid Date"; const { dateSection = true, timeSection = true, dateSign = "-", timeSign = ":", linkSign = " ", places = 2, } = options || {}; const padStart = (string: number, length = places) => { const s = String(string); if (!s || s.length >= length) return string; return `${Array(length + 1 - s.length).join("0")}${string}`; }; const $y = $d.getFullYear(), $M = padStart($d.getMonth() + 1), $D = padStart($d.getDate()), $H = padStart($d.getHours()), $m = padStart($d.getMinutes()), $s = padStart($d.getSeconds()); let text = ""; if (dateSection) text += `${$y}${dateSign}${$M}${dateSign}${$D}`; if (timeSection) text += `${linkSign}${$H}${timeSign}${$m}${timeSign}${$s}`; return text;}

August 14, 2021 · 2 min · jiezi

关于deno:如何学习Deno

Deno 是什么?Node 之父创立的,性能和 Node.js 雷同,然而更优雅的运行时。 为什么会有 Deno?JavaScript ,ES6 规范引入了 Promise 接口(以及 async 函数)和 ES 模块。而 Node.js 因为历史起因, 对这两个新语法的反对,都不现实Node.js 的模块管理工具 npm,逻辑越来越简单,且内部模块不装置,常常爆出安全漏洞Node.js 的性能也不残缺,导致内部工具层出不穷,让开发者疲劳不堪:webpack,babel,typescript、eslint、prettier......Node.js 的创建者 Ryan Dahl 在 JSConf EU 上的演讲视频更能阐明问题:https://www.youtube.com/watch...怎么用?官网下载与 demo 运行指南:https://deno.land/manual@v1.8...Deno 的将来怎么样?目前可持张望态度,学习一下语法和思路齐全没问题Deno 并不会齐全取代 Node,因为 node 曾经能满足绝大多数需要,且历史我的项目并不会轻易迁徙到 Deno。参考链接阮一峰的 Deno 介绍,作为入门:https://www.ruanyifeng.com/bl...Deno 的 Example 例子:https://deno.land/manual@v1.8...介绍 Deno,且提供了可运行的例子:https://chinese.freecodecamp....

March 8, 2021 · 1 min · jiezi

关于deno:了不起的-Deno带你极速获取各大平台今日热榜

摘要:Deno 是一个 JavaScript/TypeScript 的运行时,默认应用平安环境执行代码,有着卓越的开发体验。有人的中央就有江湖,有江湖的中央就有争执。前些天,继《[译]为什么现在 Deno 正全面取代 Node.js》之后,又有了《【译】Deno 曾经死了吗?》,兴许这便是江湖。说回题目”Deno 在手,天下我有“,其实 Deno 换成Charj、NodeJS、Java 等其余名词都一样,毕竟只是题目罢了,可能真正不一样的是应用她的愉悦感。对我而言,能给我带来 Copy 的快感,便是极好的! 源起 本周,被@justjavac 巨佬的各个热搜榜我的项目吸睛了,抱着对技术的三分钟激情以及对优良代码的学习心切,我又开始了新的代码拷贝旅程。二话不说,先git clone一顿操作猛如虎,定睛一看,如同这几个我的项目的实现都差不多,也求教了巨佬本尊确定实现是一样的,但为毛要开 4 个 repo 呢?兴许这就是繁多职责准则吧。而后,我寻思着能不能整合一下再加点别的热搜榜就能变成trending in one,是不是很棒的想法,哈哈哈哈。接下来,我还会对今日头条下手--”得热搜者得天下,热搜榜拿来吧!“,三下除二拿到了头条热搜榜(只管须要输出图片验证码)。最初就是资源整合,不过发现 README.md 爆了,看来单例还是有单例的好。 即刻获取今日热搜榜 ➡️ trending-in-one 分析对于如何实现热搜汇总,如果不看源码,我也只能想到调用相干热搜榜的接口来获取,可他人的接口又怎么会给你随便调用呢?通过拜读大佬的源码,我看到了通过正则匹配 DOM 节点获取对应的题目链接之类的,我看到了通过正文定位包裹的内容并进行替换,我看到了 JavaScript 如何解决反复的数据,我看到了如何借助 github action 实现 Deno 利用的构建……只管只是一个微不足道的我的项目,却无所不包,作者将各种技巧搭配自若、灵活运用,几乎是炉火纯青、登峰造极啊(PS:在我看来事实如此)。 其实抛开语言和平台,要想实现热搜汇总榜,无非就是三步走:① 获取数据 ② 解决数据 ③ 输入数据。接下来从 Deno 的视角来具体讲解这三步是如何走的: 获取数据当咱们遇到一个需要,可能要从它的实质登程,比方要实现热搜汇总,首先咱们就须要各大平台的热搜数据,现在日头条热搜榜、知乎热门视频、知乎热门话题、常识热门搜寻、微博热门搜寻等等,怎么获取呢?惯例的伎俩就是框按 F12 看看 Network,切实不行试试抓包工具.好在“前人栽树后人乘凉”,于是乎咱们便有了各平台的接口,获取数据岂不是信手沾来。 头条热榜:https://is-lq.snssdk.com/api/... 微博热搜:https://s.weibo.com/top/summary 知乎热门话题:https://www.zhihu.com/api/v3/... 知乎热门视频:https://www.zhihu.com/api/v3/... 知乎热搜: https://www.zhihu.com/api/v4/... 当然以上接口并非永恒无效,目前来看也只能是能用多久用多久了,如果您不满足于此,亦能够试试Twitter、Medium 等等(PS:坏蛋毕生安全)。不过,上边的接口中,微博热搜特立独行,她返回的是 HTML 须要用到正则匹配即可拿到热搜题目和链接,正则可把我难住了:/

December 3, 2020 · 1 min · jiezi

关于deno:Copy攻城狮日志Deno-在手女友我有极速上线嘘寒问暖Bot

很久很久没有提笔写货色了,也意味着很久很久没有瞎折腾 Copy 大法了。我是谁?我是谁并不重要,江湖必定没有 Copy 攻城狮的传说,不过,兴许这是一篇真情露出的踩坑文。以前,据说过“If I have seen further,it is by standing on the shoulders of giants.”,而此刻我正站在Ryan Dahl 和 乂乂又又的肩膀上,体验万物皆可 Serverless 的 Serverless Deno ,从零到一开(kao)发(bei)然并卵的铝盆友彩虹屁 bot(目前仅仅是定时发送邮件)。伪需要剖析最好的恋情就是我知 TA 冷暖,我懂 TA 情意 —— 定时天气预报外加心灵鸡汤;最好的陪伴就是安心地和 TA 一起倒数最重要的日子 —— 倒计时揭示;最好的情绪就是 TA 每天第一次睁开眼睛看到的是我的问候,夕阳下在我的晚安声中进入梦乡 —— 早安晚安问候;当然最重要的是学习理解一下陈腐事物,比方 Deno、比方 Serverless。 实现构想缘起于大佬的创意和代码实现,所以代码不必思考太多,照搬就行;邮箱服务间接 Github 搜一波,当初的年轻人不讲武德,什么数据库、秘钥、邮箱账号密码、公司我的项目源码等等统统一股脑丢到 Github,我也想康康(不晓得会不会喝茶);再想下代码实现,波及到日期工夫计算、邮件发送,是不是得找个巨佬的肩垫垫脚?插件拿过去就是刚!怎么部署呢?略微比照了一下,就鹅厂云了,如同几个月前就反对 Deno 部署了,应该比拟成熟(没想到还是栽坑里了)。最初, Just Do IT!热气腾腾看小标题是不是猜到什么恶心的货色了?是的,正是在下!本大狮,历经九九八十一分钟(理论折腾了一宿,次要卡在 Serverless 局部了),翻阅了多处 API 文档,几经挫折之后,具备辣眼睛的陈腐代码进去了: /** Copyer huqi* https://github.com/hu-qi*/import * as log from "https://deno.land/std@0.79.0/log/mod.ts";import { SmtpClient } from "https://deno.land/x/smtp/mod.ts";import { differenceInDays, format,} from "https://deno.land/x/date_fns@v2.15.0/index.js";import { zhCN } from "https://deno.land/x/date_fns@v2.15.0/locale/index.js";import "https://deno.land/x/dotenv/load.ts";// 很随便的入参,来自.envconst { SEND_EMAIL, PASSWORD, RECV_EMAIL, NAME_GIRL, CITY, CUTDOWNDATE, CUTDOWNTHINGS,} = Deno.env.toObject();// 很随便的API,来自掘金const URL = { weather: `http://wthrcdn.etouch.cn/weather_mini?city=${CITY}`, soup: "https://www.iowen.cn/jitang/api/", pi: "https://chp.shadiao.app/api.php",};// 先配置下邮箱服务,管他行不行const client = new SmtpClient();const connectConfig: any = { hostname: "smtp.163.com", port: 25, username: SEND_EMAIL, password: PASSWORD,};// 权且认为返回的都是构造数据async function _html(url: string): Promise<string> { return await (await fetch(url)).text();}// 指标城市的天气async function getWeather(url: string) { let data = await _html(url); if (data.indexOf("OK") > -1) { let _data = JSON.parse(data).data; const { ganmao, wendu, forecast } = _data; const weather = forecast[0].type; return `天气:${weather} 以后温度:${wendu} ${ganmao}`; } else { return "敬爱的,今天天气真微妙!"; }}// 倒计时function getTime() { const today = format(new Date(), "PPPP", { locale: zhCN }); const days = differenceInDays(new Date(CUTDOWNDATE), new Date()); return `明天是 ${today} ${CUTDOWNTHINGS}倒计时:${days}天`;}// 心灵鸡汤async function getSoup(url: string) { let data = await _html(url); if (data.indexOf("数据获取胜利") > -1) { let _data = JSON.parse(data).data; const { content } = _data.content; return content; } else { return `高考在昨天,${CUTDOWNTHINGS}在今天,明天没有什么事儿!`; }}// 彩虹????屁?async function getPi(url: string) { let data = await _html(url); return data.length > 3 ? data : "你上辈子肯定是碳酸饮料吧,为什么我一看到你就开心的冒泡";}// 早安async function morning() { return ` <p>${getTime()}</p> <p>${await getSoup(URL.soup)} </p> <p>${await getWeather(URL.weather)} </p> <p>${await getPi(URL.pi)}</p> `;}// 晚安async function ngiht() { return ` <p>${await getSoup(URL.soup)} </p> <p>${await getPi(URL.pi)} </p> <p>晚安,${NAME_GIRL}同学,明天你也是最棒的,持续加油鸭!</p> `;}// 日期插件有点屌function getTimeX() { // 返回 “上午” 或者 “下午” return format(new Date(), "aaaa", { locale: zhCN });}// 入口函数async function main_handler() { // 邮件注释 const content = getTimeX() === "上午" ? await morning() : await ngiht(); // 邮件题目 const greeting = getTimeX() === "上午" ? `早安, ${NAME_GIRL}` : `晚安,${NAME_GIRL} `; // "及时关注可能会产生的谬误" try { await client.connect(connectConfig); await client.send({ from: SEND_EMAIL, to: RECV_EMAIL, subject: greeting, content: content, }); await client.close(); log.info("send email success"); } catch (error) { // "当初开始执行B打算", // "与其关怀程序的异样,不如多关注下身边的女孩子吧" log.error(error); log.info("Error: send email fail"); } log.info(content); return content;}// 立刻执行(宫刑?)main_handler();不得不感叹 Deno 的生态真牛掰,想用什么插件就有什么插件,刚好满足了上边这么多需要。像这个日期库,非常丰盛,无论是日期格式化、国际化还是日期罕用的函数等等,思考得很周到,像这么好用的插件,Copy 攻城狮就别学了,我是学不会的,这辈子都不可能学会的。 ...

December 1, 2020 · 3 min · jiezi

关于deno:最全面的-Deno-入门教程

作者:ROBIN WIERUCH翻译:疯狂的技术宅 原文:https://www.robinwieruch.de/D... 未经容许严禁转载 Deno 是新的 JavaScript 和 TypeScript 运行时。Node.js 的发明者 Ryan Dahl 于 2020 年公布了 Deno,作为 Node.js 的改良。然而 Deno 不是 Node.js,而是全新的 JavaScript 运行时,同时也反对 TypeScript。与 Node.js 类似,Deno 可用于服务器端 JavaScript,但其目标是打消 Node.js 所犯的谬误。它就像 Node.js 2.0 一样,只有工夫能力通知咱们是否会像 2009 年应用 Node.js 一样去应用它。 为什么会有 DenoNode(2009)和 Deno(2020)的发明者 Ryan Dahl 公布了 Deno 作为 JavaScript 生态系统的补充。当 Ryan 在会议上第一次发表 Deno 时,他谈到了 Node.js 中的谬误。Node.js 曾经成为 JavaScript 生态中不可或缺的工具,已被数百万人应用,然而 Ryan Dahl 对过后做出的决定感到不满。当初 Ryan Dahl 心愿通过 Deno 解决 Node 的设计缺点。 Deno 是由 V8 JavaScript 引擎、Rust 和 TypeScript 实现的用于平安服务器端的 JavaScript 和 TypeScript 全新运行时。 ...

September 9, 2020 · 6 min · jiezi

关于deno:从实际案例讲-Deno-的应用场景

此篇文章实际上就是《前端开发的瓶颈与将来》的番外篇。次要想从实用的角度给大家介绍下 Deno 在咱们我的项目中的利用案例,现阶段咱们只关注利用层面的问题,不波及过于底层的常识。简介 咱们从它的官网介绍外面能够看进去加粗的几个单词:secure, JavaScript, TypeScript。简略译过去就是: 一个 JavaScript 和 TypeScript 的平安运行时那么问题来了,啥叫运行时(runtime)?能够简略的了解成能够执行代码的一个货色。那么 Deno 就是一个能够执行 JavaScript 和 TypeScript 的货色,浏览器就是一个只能执行 JavaScript 的运行时。 个性默认是 平安的,这意味着初始的状况下你是 不能够 拜访网络、文件系统、环境变量的。开箱即用的 TypeScript 反对,就是说你能够间接应用 Deno 运行 TypeScript 而 不须要 应用 tsc 编译Deno 的构建版只有一个可执行文件,那么你能够间接下载这个可执行文件到本地执行,而 不须要 编译、装置的操作内置了一些工具集,比方:依赖查看器、代码格式化。咱们用到的测试框架竟然没有被重点提起一系列的通过代码 review 的内置模块,这示意当你应用 Deno 的时候,一些罕用的工具办法都内置了,不须要再增加三方依赖局部浏览器个性兼容,这个并不是官网宣传的个性,然而我认为是很重要的一点。这个我意味着如果设计正当,你的代码即能够跑在 Deno 外面,也能够在浏览器外面。装置Mac/Linux 下命令行执行: curl -fsSL https://deno.land/x/install/install.sh | sh也能够去 Deno 的官网代码仓库下载对应平台的源(可执行)文件,而后将它放到你的环境变量外面间接执行。如果装置胜利,在命令行外面输出:deno --help 会有如下输入: ➜ ~ deno --helpdeno 1.3.0A secure JavaScript and TypeScript runtimeDocs: https://deno.land/manualModules: https://deno.land/std/ https://deno.land/x/Bugs: https://github.com/denoland/deno/issues...当前如果想降级能够应用内置命令 deno upgrade 来主动降级 Deno 版本,相当不便了。 ...

August 20, 2020 · 6 min · jiezi

关于deno:Deno从零到架构级系列二注解路由

上回介绍了Deno的根本装置、应用。基于oak框架搭建了管制层、路由层、对入口文件进行了革新。那这回咱们接着持续革新路由,模仿springmvc实现注解路由。 装璜器模式装璜者模式(decorator),就是给对象动静增加职责的形式称为装璜者模式。间接先上例子: // 新建文件fox.ts// 创立一个fox类class Fox { // skill办法,返回狐狸会跑的字样,假如就是构建了狐狸类都会跑的技能 skill() { return '狐狸会跑。' }}// 创立一个flyingfox类class Flyingfox { private fox: any // 构造方法,传入要装璜的对象 constructor(fox: any) { this.fox = fox; // 这里间接打印该类的skill办法返回值 console.log(this.skill()) } // 该类的skill办法 skill() { // 在这里获取到了被装璜者 let val = this.fox.skill(); // 这里简略的加字符串,假如给被装璜者加上了新的技能 return val + '再加一对翅膀,就会飞啦!' }}// new一个fox对象let fox = new Fox();// 打印后果为:狐狸会跑。再加一对翅膀,就会飞啦!new Flyingfox(fox);间接运行deno run fox.ts就会打印后果啦。这是一个非常简单的装璜者模式例子,咱们持续往下,用TS的注解来实现这个例子。 TypeScript装璜器配置因为deno原本就反对TS,但用TS实现装璜器,须要先配置。在根目录新建配置文件tsconfig.json,配置文件如下: { "compilerOptions": { "allowJs": true, "module": "esnext", "emitDecoratorMetadata": true, "experimentalDecorators": true }}TS装璜器这里提一下,注解和装璜器是两个货色,对于不同的语言来讲,性能不同。 ...

August 13, 2020 · 3 min · jiezi

关于deno:Deno从零到架构级系列一开篇

大家好,小弟飞狐。好久没来思否了,再来带来了的肯定是干货。从Deno开始,飞狐带来的相对到目前为止前所未有的Deno系列。话不多说,用技术谈话。 你学不动的 Deno 来了还记得 Github 上那个让人学不动的 Deno 么?就在2020年5月13日,Deno1.0正式公布。作为新晋网红运行时,Deno真的会代替 Node 吗?这个问题能够追溯到2018年,从Node之父 Ryan Dahl的演讲说起,Ryan在演讲中谈及对Node有十大不满之处,并且在演讲的最初颁布了Deno我的项目。我在这里只列三大新个性。 首先,Deno 作为一个JavaScript/TypeScript 运行时,底层基于性能超高的 Rust 编写,在性能和存储平安上有先天的劣势。其次,Deno 领有残缺的规范库,不再有 NPM 或 node_modules 文件夹,容许从任何中央导入所需模块。另外,Deno 集成 TypeScript,不再像以前一样借助工具编译,而是通过外部转换。不过仿佛又要剥离。综上所述,你会发现,Deno真的是青出于蓝而胜于蓝。是否代替,只是工夫问题而已。我也看到很多人在做deno和node的性能比拟,但在目前我认为做这两者的性能比拟齐全没有必要。 装置咱们一开始甭管deno底层用的go还是rust,为啥要从go换成rust、或者是deno的技术架构是咋样?这些目前都不要关怀,咱们就把deno当成一个新的运行时,只做运行时。从零到一,通过搭建一套脚手架,再缓缓去深刻。Deno能够在Mac、Linux、Windows三大零碎上运行。Deno也不须要其余依赖。通过如下形式装置: Shell (Mac, Linux): curl -fsSL https://deno.land/x/install/install.sh | shPowerShell (Windows): iwr https://deno.land/x/install/install.ps1 -useb | iexHomebrew (Mac): brew install denoChocolatey (Windows): choco install denoScoop (Windows): scoop install deno第一个例子也是来自官网,每一行我都加了解释。如下: // 创立文件 /server.ts// 不须要像node一样去npm,这里间接引入import { serve } from "https://deno.land/std@0.63.0/http/server.ts";// 构建服务,设置端口const s = serve({ port: 8000 });// 这里是间接返回for await (const req of s) { req.respond({ body: "Hello World\\n" });}整个代码非常简单,语法也是ts,和node十分像。有node根底的同学间接动手。 ...

August 11, 2020 · 2 min · jiezi

关于deno:从-CLI-指令通读-Deno-v1x-全特性

随着掘金开启了第一期技术专题之“聊聊 Deno 的一些事儿”的征稿流动,赶在截稿日的最初一天(08/04),一篇新的 Deno 文章跃然纸上。拜读了下其余搭档的 Deno 征文,有 Deno TCP Echo Server、在 Deno 上进行 TDD 实际、Deno 程序如何调用 Rust、Deno 命令行开发计划、Deno 造一个简略的 Router、Deno 的简略利用以及 Deno 从入门到跑路、Deno 从零到架构开发等等文章,每篇都很活泼精彩。那么...如果你是这两天看到的这篇文章,感觉有所帮忙,欢(gan)迎(jin)来我的掘金文章里点点赞,能够让我取得一个不错的掘金周边礼物~如果是将来某天看到的,戳这里。 掘金文章点赞传送门:https://juejin.im/user/3702810890732904/posts本篇的主题是“通读 Deno 架构”,切入的方向是“命令行指令通读”的角度。对于“通读 Deno 架构”主题,感觉能够挖坑出一个系列文章来,比方从 CLI、规范库、内核以及工具库角度来深刻到源码之中。 从命令行指令能够看出,Deno 官网内置了很多工具用来测试、打包、格式化、Linter、装置依赖库等;而在 Node 中,咱们可能须要寻找并选型大量的第三方库来填补每一个性能。一起来看看都有哪些工具吧!本文写作工夫 14h+,大量重构后积淀出的目录构造: 通读命令行根本信息:从 deno --help 来通读通用指令、内置工具包和环境变量;通读 Deno 通用指令:一一通读通用指令;通读 Deno 内置工具包:一一通读 14 个 Deno 内置工具要害性能;通读 Deno 环境变量:将环境变量分离出来进行解析;《Deno 钻研之术》系列于 Deno v1 公布之日全新推出,不定期更新在 Github 中(https://github.com/hylerrix/deno-tutorial ✨),官网(http://deno-tutorial.js.org)。让咱们一起循序渐进学 Deno,先易后难补 Node,面向未来开发属于本人的 Deno Web App。欢送订阅,欢送交换。通读命令行根本信息 deno --help, help理解一个命令的最疾速实用的办法就是间接浏览其帮忙文档,每行帮忙信息都是简短且要害的介绍,不难理解和翻译。终端输出如下命令(help 或 --help 用来打印全局帮忙信息或给定子命令的帮忙信息): ...

August 5, 2020 · 6 min · jiezi

关于deno:学不动了系列之-Deno-入门

心愿大家不要被题目吓到,纯属为了吸引眼球????。。依据笔者这两天的学习感触,Deno 比 Node 更容易入门,如果你有 Node 的根底那更简略了,很多货色都是能够类比的,学起来豪不费劲。集体认为当初正是入门 Deno 的最好机会,随着 v1.0 的公布 Deno 的 API 已趋于稳定,但整个生态圈的凋敝才刚刚开始,所以趁 Deno 还是个宝宝咱们能够陪它一起成长。 什么是DenoDeno是新一代的 JavaScript 和 TypeScript 运行时(runtime),应用 Rust 和 tokio 实现,和 Node 一样外部也应用 V8 引擎,而且Deno 和 Node 的作者是同一个人:Ryan Dahl。他发明Deno的初衷是为了补救 Node 的某些设计缺点,但鉴于目前 Node 的生态曾经如此凋敝,Deno 临时还无奈齐全取代 Node,Node 应该还将长期稳固存在。 Deno的次要特点有: 安全性:默认没有文件、网络、零碎的拜访权限,除非明确启用。与之相比,Node 是不平安的天生反对 TypeScript实现了局部的规范Web APIs,比方:fetch、console 等官网提供蕴含很多罕用性能的规范库,解脱对一些第三方库的依赖应用ES6的模块零碎(Node 应用的是 CommonJS)依赖可通过近程获取,无需装置到本地Deno目前还处于晚期倒退阶段,所以一些性能和API可能还会有变动,同时生态也不欠缺,社区提供的工具或多或少会有bug,然而这些都是失常的,并不障碍咱们去提前学习和理解这个更优良的 Node.js 替代品。 装置DenoShell (Mac, Linux) - 指定版本(举荐): curl -fsSL https://deno.land/x/install/install.sh | sh -s v1.2.1PowerShell (Windows) - 指定版本(举荐): ...

July 28, 2020 · 2 min · jiezi

Deno源码简析三JS与Rust交互

开始今天开始分析JS与Rust是如何交互的,毕竟JS的性能在某些场景下还是不能胜任,这个时候就是Rust闪亮登场的时候,两者互相补足,无往不利! op之前一直说的op,我个人觉得就是deno上的一个插件机制,deno上所有的功能基本是都是在这个插件机制基础上工作的。 send和recv在一开始的架构图我们就可以看到,在deno里面JS与Rust的交互只能通过send和recv这两个方法,调用send的实际原理也很简单根据opId去调用对应的rust方法,如果是同步的方法那就可以直接返回,但是如果是异步方法就需要用到recv去接收返回的值。 直接从打开文件open/openAsync这个op开始分析: export function openSync(path: string, options: OpenOptions): number { const mode: number | undefined = options?.mode; return sendSync("op_open", { path, options, mode });}export function open(path: string, options: OpenOptions): Promise<number> { const mode: number | undefined = options?.mode; return sendAsync("op_open", { path, options, mode, });}这里直接调用sendSync/sendAsync方法,然后再跟踪下去sendSync和sendAsync: export function sendSync( opName: string, args: object = {}, zeroCopy?: Uint8Array): Ok { const opId = OPS_CACHE[opName]; util.log("sendSync", opName, opId); const argsUi8 = encode(args); const resUi8 = core.dispatch(opId, argsUi8, zeroCopy); util.assert(resUi8 != null); const res = decode(resUi8); util.assert(res.promiseId == null); return unwrapResponse(res);}export async function sendAsync( opName: string, args: object = {}, zeroCopy?: Uint8Array): Promise<Ok> { const opId = OPS_CACHE[opName]; util.log("sendAsync", opName, opId); const promiseId = nextPromiseId(); args = Object.assign(args, { promiseId }); const promise = util.createResolvable<Ok>(); const argsUi8 = encode(args); const buf = core.dispatch(opId, argsUi8, zeroCopy); if (buf) { // Sync result. const res = decode(buf); promise.resolve(res); } else { // Async result. promiseTable[promiseId] = promise; } const res = await promise; return unwrapResponse(res);}sendSync相对sendAsync会简单一点,直接从OPS_CACHE拿到对应的opId,然后再把参数转成Uint8Array就可以分发这次调用下去。 ...

June 23, 2020 · 2 min · jiezi

Deno-JWT-token-应用

视频演示:https://www.bilibili.com/video/BV1BT4y1E7Nh/?p=12 一起来完成以下步骤:引用之前的工程代码创建员工信息interface和员工数组array生成token校验token是否正确运行命令: deno run --allow-net --allow-read main.ts import { Context } from "https://deno.land/x/oak/mod.ts";import { key } from "../middlewares/key.ts";//引入jwt 模块功能import { makeJwt, setExpiration, Jose, Payload } from "https://deno.land/x/djwt/create.ts"import employees from "../models/employees.ts";//获取工程目录方法const { cwd } = Deno;//jwt头部const header: Jose = { alg: "HS256", typ: "JWT",}/** * 定义Controller */class Controller { /** * 首页 * @param ctx */ static async hello(ctx: any) { //cwd获取当前工程目录 //注意 ' !== ` ctx.render(`${cwd()}/views/index.ejs`, { title: "Testing", data: { name: " Deepincoding" } }); } //游客 static async guest(ctx: Context) { ctx.response.body = "Hi Guest"; } //获取token static async token(ctx: Context) { //获取员工信息 const { value } = await ctx.request.body(); console.log({ value }); //检查员工是否存在 const hadEmployee = employees.find(employee => { return employee.username === value.username && employee.password === value.password; }); if (hadEmployee) { //JWT body 可以放任何你想放的内容 const payload: Payload = { iss: hadEmployee.username, exp: setExpiration(new Date().getTime() + 60000), } //生成JWT const jwt = makeJwt({ key, header, payload }); ctx.response.status = 200; //返回 ctx.response.body = { id: hadEmployee.id, username: hadEmployee.username, jwt, } } else { ctx.response.status = 422; //返回提示信息 ctx.response.body = { message: 'Invalid username or password' }; } } //需要token才能访问admin static async admin(ctx: Context) { ctx.response.body = "Hi Admin"; }}//导出Controllerexport default Controller;middlewares/authMiddleware.tsimport { Context } from "https://deno.land/x/oak/mod.ts";import { validateJwt } from "https://deno.land/x/djwt/validate.ts"import { key } from "./key.ts";//定义 authMiddleware 检查token有效性const authMiddleware = async (ctx: Context, next: any) => { //token 放在header里面传过来 const headers: Headers = await ctx.request.headers; const authorization = headers.get('Authorization') // 传过来的token是否以bearer开头 + 空格 if (!authorization || !authorization.split(' ')[1] ) { ctx.response.status = 401; return; } const jwt = authorization.split(' ')[1]; //校验token const isValidateJwt = await validateJwt(jwt, key); //如果token正确,程序继续往下走,否则返回401 if (isValidateJwt.isValid) { await next(); return; } ctx.response.status = 401; ctx.response.body = { message: 'Invalid jwt token' };}export default authMiddleware;models/employee.ts//定义interfaceexport interface Employee{ id: string, username: string, password: string}//初始化员工列表const employees: Array<Employee> =[ { id: "1", username: "michael", password: "123456" }, { id: "2", username: "alex", password: "654321" }]//导出export default employees;routers/router.ts//引入 Routerimport { Router } from "https://deno.land/x/oak/mod.ts";//引入自定义Controllerimport Controller from "../controllers/Controller.ts";import authMiddleware from "../middlewares/authMiddleware.ts";//创建 routerconst router = new Router();//首页urirouter.get("/",Controller.hello);//不需要token可以访问guestrouter.get("/guest",Controller.guest);//根据用户名和密码获取tokenrouter.post("/token",Controller.token);//这里加上token校验的方法router.get("/admin",authMiddleware,Controller.admin);//导出 routerexport default router;main.ts//引入 Applicationimport { Application } from "https://deno.land/x/oak/mod.ts"//引入页面渲染模块import {viewEngine,engineFactory,adapterFactory} from "https://deno.land/x/view_engine/mod.ts";//引入 routerimport router from "./routers/router.ts";//获取页面引擎const ejsEngine = engineFactory.getEjsEngine();//获取oak适配器const oakAdapter = adapterFactory.getOakAdapter();// 创建appconst app = new Application();// 应用页面渲染引擎app.use(viewEngine(oakAdapter,ejsEngine));//应用 routerapp.use(router.routes());app.use(router.allowedMethods());//log日志console.log("Server Port 8000");await app.listen({port: 8000 })

June 18, 2020 · 2 min · jiezi

Deno上传文件

Deno上传文件视频讲解 https://www.bilibili.com/video/BV1BT4y1E7Nh/?p=6 我们一起来完成以下步骤:沿用之前的工程代码创建上传页面和响应的controller添加上传文件功能测试 controllers/controller.tsconst { cwd } = Deno;class Controller { static async getData(ctx: any){ //cwd获取当前工程目录 //注意 ' !== ` ctx.render(`${cwd()}/views/index.ejs`,{ title:"Testing", data:{name:"deepincoding"} }); } static async uploadPage(ctx: any){ ctx.render(`${cwd()}/views/upload-page.ejs`); } static async uploadAction(ctx: any){ //我们这里不返回页面,而是返回上传文件的信息 ctx.response.body = ctx.uploadedFiles; } }export default Controller;routers/index.tsimport { Router } from "https://deno.land/x/oak/mod.ts";import Controller from "../controllers/Controller.ts";import { upload } from "https://deno.land/x/upload_middleware_for_oak_framework/mod.ts";const router = new Router();router.get("/",Controller.getData);router.get("/uploadPage",Controller.uploadPage);router.post("/uploadAction", upload('uploads', ['jpg','png'], 20000000, 10000000, true, false, true),Controller.uploadAction);export default router;views/upload-page.ejs<body><form id="yourFormId" enctype="multipart/form-data" action="/uploadAction" method="post"> <input type="file" name="file1" multiple><br> <input type="submit" value="Submit"> </form></body> main.tsimport { Application } from "https://deno.land/x/oak/mod.ts"import {viewEngine,engineFactory,adapterFactory} from "https://deno.land/x/view_engine/mod.ts";import router from "./routers/index.ts";const ejsEngine = engineFactory.getEjsEngine();const oakAdapter = adapterFactory.getOakAdapter();const app = new Application();app.use(viewEngine(oakAdapter,ejsEngine));app.use(router.routes());app.use(router.allowedMethods());await app.listen({port: 8000 })

June 9, 2020 · 1 min · jiezi

Deno-页面渲染

Deno 页面渲染我们一起来完成以下步骤:[X] 1. 首先来了解一下支持页面渲染都有哪些组件:Denjucks Ejs Handlebars我们今天要学Ejs模板引擎入门。其他两个如法炮制 [X] 2. 创建Controller[X] 3. 创建Router[X] 3. 创建页面index.ejs[x] 4. 创建主应用文件main.ts[X] 5. 测试 controllers/controller.tsconst { cwd } = Deno;class Controller { static async getData(ctx: any){ //cwd获取当前工程目录 //注意 ' !== ` ctx.render(`${cwd()}/views/index.ejs`,{ title:"Testing", data:{name:"deepincoding.com"} }); }}export default Controller;routers/index.tsimport { Router } from "https://deno.land/x/oak/mod.ts";import Controller from "../controllers/Controller.ts";const router = new Router();router.get("/",Controller.getData);export default router;views/index.ejs<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><%= title %></title></head><body> <h2>Hello <%= data.name %></h2></body></html>main.tsimport { Application } from "https://deno.land/x/oak/mod.ts"import {viewEngine,engineFactory,adapterFactory} from "https://deno.land/x/view_engine/mod.ts";import router from "./routers/index.ts";const ejsEngine = engineFactory.getEjsEngine();const oakAdapter = adapterFactory.getOakAdapter();const app = new Application();app.use(viewEngine(oakAdapter,ejsEngine));app.use(router.routes());app.use(router.allowedMethods());await app.listen({port: 8000 })

June 9, 2020 · 1 min · jiezi

Deno-加载依赖包出错

视频讲解https://www.bilibili.com/video/BV1qK4y1x7VG/ 在开发过程中,经常遇到DNS解析域名错误的问题。导致js依赖包没法下载我们一起来完成以下步骤: 先来发现问题域名是否能访问?解析域名(https://www.ipaddress.com/)的IP。在没有使用翻墙 情况下是否能ping通修改Hosts文件(C:\Windows\System32\drivers\etc)再重新run一下应用main.tsimport { Application, Router } from "https://deno.land/x/oak/mod.ts";const router = new Router();const app = new Application();router.get("/",(ctx) =>{ ctx.response.body="Hello World ";})app.use(router.routes());app.use(router.allowedMethods());await app.listen({ port:8000 });完

June 5, 2020 · 1 min · jiezi

Deno修改文件自动部署

视频讲解https://www.bilibili.com/video/BV18z4y1978E 一起来完成以下步骤:安装https://deno.land/x/denon。 确保您的deno版本是1.0.1以上创建一个简单的应用运行denon run main.ts (注:denon而不是deno哦)main.tsimport { Application, Router } from "https://deno.land/x/oak/mod.ts";const app = new Application();const router = new Router();router.get("/",(ctx) =>{ ctx.response.body="Hello World Welcome......";})app.use(router.routes());app.use(router.allowedMethods());await app.listen({ port:8000 });完

June 4, 2020 · 1 min · jiezi

Deno-Router基本应用

视频讲解https://www.bilibili.com/vide... 一起来完成以下步骤:创建一个简单的Router分别创建user(以user开头的url)和admin(以admin开头的url) 的Router实现动态uri的Router main.tsimport { Application, Router } from "https://deno.land/x/oak/mod.ts";import UserRouter from "./user-router.ts";import AdminRouter from "./admin-router.ts";const app = new Application();const router = new Router();router.get("/",(ctx) =>{ ctx.response.body="Hello World";})app.use(router.routes());app.use(router.allowedMethods());app.use(UserRouter.routes());app.use(UserRouter.allowedMethods());app.use(AdminRouter.routes());app.use(AdminRouter.allowedMethods());await app.listen({ port:8000 });admin-router.tsimport { Router } from "https://deno.land/x/oak/mod.ts";const AdminRouter = new Router({prefix:"/admin"}); //以admin开头的uriAdminRouter.get("/",(ctx) =>{ ctx.response.body ="Hello Admin";})export default AdminRouter;user-router.tsimport { Router } from "https://deno.land/x/oak/mod.ts";const UserRouter = new Router({prefix:"/user"}); //以user开头的uriUserRouter.get("/",(ctx) =>{ ctx.response.body ="Hello User";}).get("/:id",(ctx) =>{ ctx.response.body = "the user is is "+ ctx.params.id;})export default UserRouter;完 ...

June 3, 2020 · 1 min · jiezi

deno 如何偿还 node.js 的十大技术债

根据网络资料整理“Node现在太难用了!”。Node.js之父 Ryan Dahl 去年初要开发一款 JavaScript 互动式数据分析工具时,忍不住抱怨起自己十年前一手创造的技术。Ryan Dahl 想要设计出一款类似 IPython 的互动式数据科学分析命令行工具,但改用 JavaScript 语言,要让 JavaScript 也可以像 Python 那样,进行各式各样的数据分析、统计计算以及数据视觉化战士。一度离开 Node.js 开发社区的 Rayn Dahl,再次拿起自己发明的 Node.js 来开发这个新的数据分析工具,但是越用越别扭,他开始思考,有什么方法可以改进 Node.js。Node.js 是他在 2009 年 11 月 8 日时,在 JavaScript 社区欧洲 JSConf 大会上首度发布的,它把浏览器端的 JavaScript 技术,带入了服务器端应用领域。前端网页工程师从来都没想过,自己也可以成为后端工程师,但 Node.js 让前端技术走出了浏览器,前端工程师甚至可以成为全端工程师,Node.js 改变了前端工程师的世界。从2009年 Ryan Dahl 设计出这个服务器端的 JavaScript 框架,至今已经发展到了第 10 版。而随 Node.js 而生,另一位开发者 Isaac 设计出的 JavaScript 包管理工具 npm,更成了网页开发者必懂得技术,在 npm 的储存库上,注册了超过 60 万个 Node.js 模块,这更让 Node.js 的应用遍及各类开发或软件需求。JavaScript是最普及的语言,而Node.js是最受欢迎的框架根据 Stack OverFlow 在 2018年度的开发者大调查(全球超过 10 万开发者参与),JavaScript 是开发者中最普及的技术,近 7 成开发者都会用,比 HTML 或 CSS 的普及率还要要高,而最多人懂的开发框架排名中,第一名就是 Node.js ,将近5成开发者(49.6%)经常使用,比 2017 年还小幅上升了 2 个百分点,同时使用者还在持续增加,远高于排名第二的 Angular(36.9%),这正是因为 Node.js 是前端和后端工程师都能用的技术。Node.js 不只是当前的主流技术,也是下一代网页应用架构 Serverless(无服务器)架构的关键技术。负责 Azure Functions 项目的微软资深首席软件工程师 Christopher Anderson 就曾直言,主流无服务器服务商纷纷把宝押在 Node.js,因为看上了 JavaScript 工具的丰富生态,再加上 Node.js 的轻量化、容易分散与水平扩充、各种操作系统都容易运行的特性,将 Node.js 作为无服务器服务优先支持的框架,这也让 Node.js 更适合用于超大规模部署的应用。Ryan Dahl 自己坦言,从没想到 Node.js 日后会带来这么大的影响。他也将此归功于开发社区的持续改善,才让它越来越成熟。截止到2018年8月,参与 Node.js 的开发者已经超过2千人,十年来的更新发布次数超过 500 次,在 GitHub 上代码的下载次数更是累计超过了10亿次,就连大型科技公司如 PayPal,或顶尖科研机构 NASA 都在使用。但 Ryan Dahl 在 2012 年开始淡出 Node.js 社区,转而进入 Go、Rust 语言社区,也重回他擅长的数学应用领域,2017 年还申请了 Google 大脑一年的进驻计划,成为 Google 大脑研究团队的一员,担任深度学习工程师,并投入图像处理技术的研究。直到 2018 年 6 月初,就在 Node.js 准备迈入第 10 年之前,JSConf 欧洲大会再次邀请Ryan Dahl 来进行开场演讲。尽管大受欢迎,但 Node.js 仍有十大技术债原本 Ryan Dahl 打算在 2018 年的 JSConf 演讲中分享自己这款 JavaScript 版的 IPython 互动式数据分析工具,没想到一直开发到 5 月份,这个工具都还不能用。本来要放弃这次演讲的 Ryan Dahl 念头一转,干脆把他重拾 Node.js 后发现的问题拿出来分享,这就是去年引发全球开发社区热烈讨论的那场演讲,题目是 “我在 Node .js 最后悔的 10 件事”。Ryan Dahl 在演讲中坦言,Node.js 有十大设计错误,甚至可说是他的 10 大悔恨(他刻意用Regret 这个词来形容)!这些让 Ryan Dahl 懊悔不已的错误,包括了没采用 JavaScript 非同步处理的 Promise 对象、低估了安全性的重要性、采用 gyp 来设计 Build 系统、没有听从社区建议改用 FFI 而继续用 gyp,另外,他也觉得 Node.js 过度依赖 npm 功能(内建到 package.json 支持是个错误)、用 require("")来嵌入任意模块简直太容易了、package.json 容易造成错误的模块观念(让开发者误以为同一目录下的文件就是同一模块)、臃肿复杂的node_module设计以及开发社区抱怨已久的下载黑洞问题(下载npm得花上非常久的等待时间)、require(“module”)功能没有强制要求注名.js扩展名,以及无用的 index.js 设计。2012年,Ryan Dahl 离开了Node.js社区,他事后解释,Node.js 的发展已经步入正轨,也达到他最初的目标,因而决定离开,但在2018年这场演讲中,他坦言, Node.js 还有大把问题要修复,所以现在他回来了,要来偿还当年的技术债,挽回 Node.js 的设计错误。Ryan Dahl 的答案是打造一个全新的服务器端 JavaScript 运行环境,也就是 Deno 项目。让Ryan Dahl懊悔不已的 Node.js 十大技术债没用 JavaScript 异步处理的 Promise 对象低估了安全的重要性使用了 gyp 来设计 Build 系统没有听大家的建议提供 FFI 而继续用 gyp过度依赖 npm(内建 package.json支持)太容易造成 require(“任意模块”)package.json 建立了错误的模块概念(在同一目录下的文件就是同一模块)臃肿复杂的 node_module 设计和下载黑洞(往往下载 npm 得花上非常久的时间)require(“module”) 时没有强制加上 .js 扩展名无用的 index.js 设计。Deno 如何挽回 Node.js 设计上遗留的问题这是 Deno 项目的一个范例,是 Unix 系统基本命令 cat 的一个实现。cat 可以从标准输入取得文件, 再逐行把文件内容送出到标准输出上。这个范例反映出 Deno 要将 I/O 抽象化和精简化的意图。Ryan Dahl 希望通过打造 Deno 这个全新的服务器端 JavaScript 运行环境,来解决 Node.js 的三大问题,包括准确的 I/O 接口、预设安全性(Secure by Default)以及引进一套去中心化的模块系统等,最后一项就是要解决下载过久过慢的老问题。Ryan Dahl 进一步解释,虽然他所有的时间都是用 C++、Go 或 Rust 这类编译式语言来开发,但是他还是有一些经常要做的事,需要使用动态的脚本程序。例如整理资料、协调测试任务、部署服务器或用户端环境、绘制统计图表、设定构建系统(Build System)的参数或是设计应用雏形等。可是,这些不同用途的任务,需要切换使用多种不同的脚本语言或工具,如 Bash、Python 或是 Node.js 等才行,相当麻烦。而 2018 年上半年的这个互动式数据分析工具的开发挫折,更让他有一股强烈地念头,能不能有一个通用的脚本工具。Deno 架构打造一款简单、好用的通用脚本工具Ryan Dahl 在 2018 年 11 月参加台湾年度 JavaScript 开发者大会(JSDC)时,特别提到:“我不喜欢用不同工具来处理不同的事情,我只想要有一个简单,直接可执行,拿了就能用的顺手工具,这正是打造Deno的初衷。”简单来说,Deno 跟 Node.js 一样都采用了 Chrome 的 JavaScript 引擎 V8,但 Deno 采用了更严格的 JavaScript 语法规范 TypeScript,Deno 等于是一个 TypeScript runtime。第一个版本的 Deno runtime 是用 Go 语言实现的,但是 Ryan Dahl 又重新用 Rust 语言开发了一次 Deno 的 runtime,避免因为重复使用两套垃圾回收器(Go语言一套、V8引擎也内建了一套)而影响效能。另外,Deno runtime 中也内建了 TypeScript 编译器。Deno的目标是安全、简洁、单一可执行文件Deno的设计目标是安全、模块简洁、单一可执行文件(简化封装)等,目前已完成的特色之一,就是可以透过URL 来汇入各种模块,另外预设安全性,要存取实体资源或网络时都需要授权,用户的代码只能在安全的沙箱中执行。为什么他最熟悉的 Go 做不到?因为“动态语言仍有其必要。“他强调,尤其要建立一个适当好用的 I/O 处理流程(pipeline)时,动态脚本语言是不可或缺的工具。而 JavaScript 就是那个他心中的理想动态语言,但是,Node.js 是一项将近 10 年历史的技术,受限于最初的设计架构,他认为,可以重新用 JavaScript 近几年出现的特性,重新思考 Node. js 的根本设计,包括像是可存取原始内存的标准方法 ArrayBuffers、适合弹性组合的 TypeScript Interfaces,以及新兴的非同步机制 Async 和 Await。Ryan Dahl 把这些新的 JavaScipt 功能,放入了 Deno 中,来设计一款新的服务器端 JavaScript 框架。但是,这一次,他不想重走 Node.js 的老路,将整个 Web 服务器放进框架,Ryan Dahl 决定打造出一支自给自足功能完整的 runtime 程序,容易带着走,而不是有着一套复杂目录和结构的框架。而且,打包成 runtime 形式,就可以部署到各种环境中,Ryan Dahl 举例,如果在无服务器服务上部署了Deno,就可指定一个网址,就能启动这个无服务器服务的调用,而不用上传一段代码到无服务器服务平台上执行,也可以部署到边缘运算设备中,来完成小型的数据处理工作。不信任使用者的代码,只能在沙箱执行另外在安全机制上,Deno 设计了两层权限架构,一个是拥有特权的核心层,另一个是没有特权的用户空间,RyanDahl 解释到,这就像是操作系统的设计一样,不信任使用者的代码,区分出使用者的权限和系统核心的权限等级,使用者的程序,要向使用到关键的资源,必须透过系统调用,由系统核心程序来执行。Deno 也是一样,“不信任用户端的 JavaScript 程序,只能在安全的沙箱中执行。”所有涉及到敏感资源的处理,如底层文件系统,都需要授权执行,这就是预设安全性的设计,包括网络存取、文件系统写入、环境变量存取、执行等这些敏感的动作,都需要取得授权才能执行。另外,Deno 的设计还将 I/O 处理抽象化,让 JavaScript 程序不必处理各种不同的输出或输入端配置,改由 runtime 接手,从而无法直接接触实体资源。一来简化各种不同的 I/O 存取方式,不论是本地或远 I/O,都是同样的 read 和 write 指令就可以搞定,二来也可以强化安全性。另外,Deno 还借鉴了不少 Go 语言的特性,例如 Deno 的 copy() 就参考了 Go 语言的 io.Copy(), BufReader() 也参考了 Go 语言的 bufio.Reader 设计等,还有不少测试机制、文件等,Ryan Dahl 也都参考了他熟悉的Go语言。指定URL,就能嵌入第三方函数库Deno 第三项设计目标是要打造一个去中心化的模块系统,Ryan Dahl 的设计是,Deno 可以像 JavaScript 一样,通过 URL 网址来嵌入外部的第三方函数库。除此之外,Deno和Rust语言也有不少整合,甚至改用Rust来实作Deno之后,Ryan Dahl透露,将会建立一个桥接机制,让Deno可以很容易地运用Rust中的函数库。不过他坦言,嵌入外部的第三方函数库的机制,也是 Deno 项目发布后,人们询问最多、也最担心的功能,担心透过 URL 来引入外部函数库后,容易引发安全问题或是中间人攻击等问题。“这就是为什么 Deno 要采取预设安全性的设计的原因,来隔绝来自外部第三方代码的威胁。”另外,通过 URL 连结到第三方函数库时,Deno 会通过缓存,在本地环境生成一份第三方函数库,第二次再调用到同样的URL时就不需再次下载了,以此来加快执行速度。甚至这个通过外部 URL 来引用函数库的功能,还可以指定版本,就算是改版了,还是可以指向旧版。当然 Ryan Dahl 强调,所有存取外部网络或下载写入到本地文件的动作,都需要取得授权才能执行。未来会支持 WebGL,Deno 就能调用 GPU 资源Deno 还有一个与 Node.js 最大的差异,就是未来会支持机器学习。Ryan Dahl 透露,他们正在开发一个数值计算类的外挂模块,最重要的就是要能支持 GPU,这也是机器学习模块最需要的功能。“Deno未来将会原生支持 WebGL,就可以让 JavaScipt 程序调用 GPU 的资源。这是他打造 Deno 的目标之一。”甚至,Ryan Dahl 预告,未来说不定可以把 TensorFlow JS 放上 Deno 来执行,因为 TensorFlow JS 用的也是 WebGL。Deno未来将瞄准小型机器学习的推理需求不同于 Nvidia的CUDA 可以用来调度多颗 GPU 资源进行复杂的机器学习训练工作,Ryan Dahl 解释,Deno 想要提供的是简单够用的机器学习能力,可以用来满足只有单颗 GPU,而且是小型或是只需要推理的计算需求,支持WebGL 已经够用了。Deno 从 2018 年 5 月中放上 Github 网站开源至今年3月,已有超过80名开发者参与,经常贡献代码的核心开发者也有 5 名。目前正把主要精力放在在预设安全性架构的设计功能上。最后企业能不能用 Deno?Ryan Dahl 坦言 Deno 距离 1.0 还有很长一段路要走,仍旧是一个非常新的技术。不过,“再等我1年,若有企业想用,请Email给我,我会提供技术支持。” 他认真地说。关于Ryan Dahl2009年11月8日,Node. js之 父RyanDahl在欧洲 JSConf 大会,第一次发布了 Node.js ,一鸣惊人,将浏览器端的 JavaScript 技术,带入了服务器端应用领域。不过他从 2012 年开始淡出 Node.js 社区,转而进入Go、Rust语言社区,2017年加入 Google 大脑研究团队,担任深度学习工程师。现为自由开发者。2018年6月初,Ryan Dahl 在 JSConf 欧洲大会发表了 Node.js 十大悔恨,并推出了新的服务器端 JavaScript runtime 方案 Deno。本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章欢迎继续阅读本专栏其它高赞文章:12个令人惊叹的CSS实验项目世界顶级公司的前端面试都问些什么CSS Flexbox 可视化手册过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!从设计者的角度看 ReactCSS粘性定位是怎样工作的一步步教你用HTML5 SVG实现动画效果程序员30岁前月薪达不到30K,该何去何从7个开放式的前端面试题React 教程:快速上手指南 ...

March 18, 2019 · 3 min · jiezi

deno原理篇-通信实现

理解deno-基础篇deno-原理篇一启动加载通信方式deno执行代码和node相似,包含同步和异步的方式, 异步方式通过Promise.then实现。Typescript/Javascript调用rust在上一节中讲到deno的启动时会初始化v8 isolate实例,在初始化的过程中,会将c++的函数绑定到v8 isolate的实例上,在v8执行Javascript代码时,可以像调用Javascript函数一样调用这些绑定的函数。具体的绑定实现如下:void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context) { v8::HandleScope handle_scope(isolate); v8::Context::Scope context_scope(context); auto global = context->Global(); auto deno_val = v8::Object::New(isolate); CHECK(global->Set(context, deno::v8_str(“libdeno”), deno_val).FromJust()); auto print_tmpl = v8::FunctionTemplate::New(isolate, Print); auto print_val = print_tmpl->GetFunction(context).ToLocalChecked(); CHECK(deno_val->Set(context, deno::v8_str(“print”), print_val).FromJust()); auto recv_tmpl = v8::FunctionTemplate::New(isolate, Recv); auto recv_val = recv_tmpl->GetFunction(context).ToLocalChecked(); CHECK(deno_val->Set(context, deno::v8_str(“recv”), recv_val).FromJust()); auto send_tmpl = v8::FunctionTemplate::New(isolate, Send); auto send_val = send_tmpl->GetFunction(context).ToLocalChecked(); CHECK(deno_val->Set(context, deno::v8_str(“send”), send_val).FromJust()); auto eval_context_tmpl = v8::FunctionTemplate::New(isolate, EvalContext); auto eval_context_val = eval_context_tmpl->GetFunction(context).ToLocalChecked(); CHECK(deno_val->Set(context, deno::v8_str(“evalContext”), eval_context_val) .FromJust()); auto error_to_json_tmpl = v8::FunctionTemplate::New(isolate, ErrorToJSON); auto error_to_json_val = error_to_json_tmpl->GetFunction(context).ToLocalChecked(); CHECK(deno_val->Set(context, deno::v8_str(“errorToJSON”), error_to_json_val) .FromJust()); CHECK(deno_val->SetAccessor(context, deno::v8_str(“shared”), Shared) .FromJust());}在完成绑定之后,在Typescript中可以通过如下代码实现c++方法和Typescript方法的映射libdeno.tsinterface Libdeno { recv(cb: MessageCallback): void; send(control: ArrayBufferView, data?: ArrayBufferView): null | Uint8Array; print(x: string, isErr?: boolean): void; shared: ArrayBuffer; /** Evaluate provided code in the current context. * It differs from eval(…) in that it does not create a new context. * Returns an array: [output, errInfo]. * If an error occurs, output becomes null and errInfo is non-null. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any evalContext(code: string): [any, EvalErrorInfo | null]; errorToJSON: (e: Error) => string;}export const libdeno = window.libdeno as Libdeno;在执行Typescript代码时,只需要引入libdeno,就直接调用c++方法,例如:import { libdeno } from “./libdeno”;function sendInternal( builder: flatbuffers.Builder, innerType: msg.Any, inner: flatbuffers.Offset, data: undefined | ArrayBufferView, sync = true): [number, null | Uint8Array] { const cmdId = nextCmdId++; msg.Base.startBase(builder); msg.Base.addInner(builder, inner); msg.Base.addInnerType(builder, innerType); msg.Base.addSync(builder, sync); msg.Base.addCmdId(builder, cmdId); builder.finish(msg.Base.endBase(builder)); const res = libdeno.send(builder.asUint8Array(), data); builder.inUse = false; return [cmdId, res];}调用libdeno.send方法可以将数据传给c++,然后通过c++去调用rust代码实现具体的工程操作。Typescript层同步异步实现同步在Typescript中只需要设置sendInternal方法的sync参数为true即可,在rust中会根据sync参数去判断是执行同步或者异步操作,如果sync为true,libdeono.send方法会返回执行的结果,rust和typescript之间传递数据需要将数据序列化,这里序列化操作使用的是flatbuffer库。const [cmdId, resBuf] = sendInternal(builder, innerType, inner, data, true);异步实现同理,实现异步方式,只需要设置sync参数为false即可,但是异步操作和同步相比,多了回掉方法,在执行异步通信时,libdeno.send方法会返回一个唯一的cmdId标志这次调用操作。同时在异步通信完成后,会创建一个promise对象,将cmdId作为key,promise作为value,加入map中。代码如下:const [cmdId, resBuf] = sendInternal(builder, innerType, inner, data, false); util.assert(resBuf == null); const promise = util.createResolvable<msg.Base>(); promiseTable.set(cmdId, promise); return promise;rust实现同步和异步当在Typescript中调用libdeno.send方法时,调用了C++文件binding.cc中的Send方法,该方法是在deno初始化时绑定到v8 isolate上去的。在Send方法中去调用了ops.rs文件中的dispatch方法,该方法实现了消息到函数的映射。每个类型的消息对应了一种函数,例如读文件消息对应了读文件的函数。pub fn dispatch( isolate: &Isolate, control: libdeno::deno_buf, data: libdeno::deno_buf,) -> (bool, Box<Op>) { let base = msg::get_root_as_base(&control); let is_sync = base.sync(); let inner_type = base.inner_type(); let cmd_id = base.cmd_id(); let op: Box<Op> = if inner_type == msg::Any::SetTimeout { // SetTimeout is an exceptional op: the global timeout field is part of the // Isolate state (not the IsolateState state) and it must be updated on the // main thread. assert_eq!(is_sync, true); op_set_timeout(isolate, &base, data) } else { // Handle regular ops. let op_creator: OpCreator = match inner_type { msg::Any::Accept => op_accept, msg::Any::Chdir => op_chdir, msg::Any::Chmod => op_chmod, msg::Any::Close => op_close, msg::Any::FetchModuleMetaData => op_fetch_module_meta_data, msg::Any::CopyFile => op_copy_file, msg::Any::Cwd => op_cwd, msg::Any::Dial => op_dial, msg::Any::Environ => op_env, msg::Any::Exit => op_exit, msg::Any::Fetch => op_fetch, msg::Any::FormatError => op_format_error, msg::Any::Listen => op_listen, msg::Any::MakeTempDir => op_make_temp_dir, msg::Any::Metrics => op_metrics, msg::Any::Mkdir => op_mkdir, msg::Any::Open => op_open, msg::Any::ReadDir => op_read_dir, msg::Any::ReadFile => op_read_file, msg::Any::Readlink => op_read_link, msg::Any::Read => op_read, msg::Any::Remove => op_remove, msg::Any::Rename => op_rename, msg::Any::ReplReadline => op_repl_readline, msg::Any::ReplStart => op_repl_start, msg::Any::Resources => op_resources, msg::Any::Run => op_run, msg::Any::RunStatus => op_run_status, msg::Any::SetEnv => op_set_env, msg::Any::Shutdown => op_shutdown, msg::Any::Start => op_start, msg::Any::Stat => op_stat, msg::Any::Symlink => op_symlink, msg::Any::Truncate => op_truncate, msg::Any::WorkerGetMessage => op_worker_get_message, msg::Any::WorkerPostMessage => op_worker_post_message, msg::Any::Write => op_write, msg::Any::WriteFile => op_write_file, msg::Any::Now => op_now, msg::Any::IsTTY => op_is_tty, msg::Any::Seek => op_seek, msg::Any::Permissions => op_permissions, msg::Any::PermissionRevoke => op_revoke_permission, _ => panic!(format!( “Unhandled message {}”, msg::enum_name_any(inner_type) )), }; op_creator(&isolate, &base, data) }; // …省略多余的代码}在每个类型的函数中会根据在Typescript中调用libdeo.send方法时传入的sync参数值去判断同步执行还是异步执行。let (is_sync, op) = dispatch(isolate, control_buf, zero_copy_buf);同步执行在执行dispatch方法后,会返回is_sync的变量,如果is_sync为true,表示该方法是同步执行的,op表示返回的结果。rust代码会调用c++文件api.cc中的deno_respond方法,将执行结果同步回去,deno_respond方法中根据current_args_的值去判断是否为同步消息,如果current_args_存在值,则直接返回结果。异步执行在deno中,执行异步操作是通过rust的Tokio模块来实现的,在调用dispatch方法后,如果是异步操作,is_sync的值为false,op不再是执行结果,而是一个执行函数。通过tokio模块派生一个线程程异步去执行该函数。 let task = op .and_then(move |buf| { let sender = tx; // tx is moved to new thread sender.send((zero_copy_id, buf)).expect(“tx.send error”); Ok(()) }).map_err(|_| ()); tokio::spawn(task);在deno初始化时,会创建一个管道,代码如下:let (tx, rx) = mpsc::channel::<(usize, Buf)>();管道可以实现不同线程之间的通信,由于异步操作是创建了一个新的线程去执行的,所以子线程无法直接和主线程之间通信,需要通过管道的机制去实现。在异步代码执行完成后,调用tx.send方法将执行结果加入管道里面,event loop会每次从管道里面去读取结果返回回去。Event Loop由于异步操作依赖事件循环,所以先解释一下deno中的事件循环,其实事件循环很简单,就是一段循环执行的代码,当达到条件后,事件循环会结束执行,deno中主要的事件循环代码实现如下:pub fn event_loop(&self) -> Result<(), JSError> { // Main thread event loop. while !self.is_idle() { match recv_deadline(&self.rx, self.get_timeout_due()) { Ok((zero_copy_id, buf)) => self.complete_op(zero_copy_id, buf), Err(mpsc::RecvTimeoutError::Timeout) => self.timeout(), Err(e) => panic!(“recv_deadline() failed: {:?}”, e), } self.check_promise_errors(); if let Some(err) = self.last_exception() { return Err(err); } } // Check on done self.check_promise_errors(); if let Some(err) = self.last_exception() { return Err(err); } Ok(()) }self.is_idle方法用来判断是否所有的异步操作都执行完毕,当所有的异步操作都执行完毕后,停止事件循环,is_idle方法代码如下:fn is_idle(&self) -> bool { self.ntasks.get() == 0 && self.get_timeout_due().is_none() }当产生一次异步方法调用时,会调用下面的方法,使ntasks内部的值加1,fn ntasks_increment(&self) { assert!(self.ntasks.get() >= 0); self.ntasks.set(self.ntasks.get() + 1); }在event loop循环中,每次从管道中去取值,这里event loop充消费者,执行异步方法的子线程充当生产者。如果在一次事件循环中,获取到了一次执行结果,那么会调用ntasks_decrement方法,使ntasks内部的值减1,当ntasks的值为0的时候,事件循环会退出执行。在每次循环中,将管道中取得的值作为参数,调用complete_op方法,将结果返回回去。rust中将异步操作结果返回回去在初始化v8实例时,绑定的c++方法中有一个Recv方法,该方法的作用时暴露一个Typescript的函数给rust,在deno的io.ts文件的start方法中执行libdeno.recv(handleAsyncMsgFromRust),将handleAsyncMsgFromRust函数通过c++方法暴露给rust。具体实现如下:export function start(source?: string): msg.StartRes { libdeno.recv(handleAsyncMsgFromRust); // First we send an empty Start message to let the privileged side know we // are ready. The response should be a StartRes message containing the CLI // args and other info. const startResMsg = sendStart(); util.setLogDebug(startResMsg.debugFlag(), source); setGlobals(startResMsg.pid(), startResMsg.noColor(), startResMsg.execPath()!); return startResMsg;}当异步操作执行完成后,可以在rust中直接调用handleAsyncMsgFromRust方法,将结果返回给Typescript。先看一下handleAsyncMsgFromRust方法的实现细节:export function handleAsyncMsgFromRust(ui8: Uint8Array): void { // If a the buffer is empty, recv() on the native side timed out and we // did not receive a message. if (ui8 && ui8.length) { const bb = new flatbuffers.ByteBuffer(ui8); const base = msg.Base.getRootAsBase(bb); const cmdId = base.cmdId(); const promise = promiseTable.get(cmdId); util.assert(promise != null, Expecting promise in table. ${cmdId}); promiseTable.delete(cmdId); const err = errors.maybeError(base); if (err != null) { promise!.reject(err); } else { promise!.resolve(base); } } // Fire timers that have become runnable. fireTimers();}从代码handleAsyncMsgFromRust方法的实现中可以知道,首先通过flatbuffer反序列化返回的结果,然后获取返回结果的cmdId,根据cmdId获取之前创建的promise对象,然后调用promise.resolve方法触发promise.then中的代码执行。结尾下节讲一下deno中import的实现 ...

March 10, 2019 · 4 min · jiezi

deno-原理篇一启动加载

之前篇章deno-基础篇,主要是deno的一些基本概念介绍。deno执行过程概述deno初始化时加载Typescript编译器和v8 isolate实例,将需要执行的文件路径作为参数传入,在内部解析传入的Typescript/Javascript文件地址,加载需要执行的代码,如果是Typescript代码,通过初始化加载的Typescript编译器将代码编译成Javascript,然后将Javacript传给v8 isolate实例,并获取可执行句柄对象,调用执行方法执行具体的代码。deno初始化过程初始化流程主要分为几步:解析外部输入加载全局状态信息,创建权限实例对象读取Typescript编译器及运行时API代码初始化v8 isolate启动运行时deno启动入口是rust,启动代码包含在main.rs的rust文件中,执行deno时会去执行main.rs文件的main方法。下面是main方法中的部分代码:fn main() { // … let args = env::args().collect(); let (mut flags, mut rest_argv, usage_string) = flags::set_flags(args) .unwrap_or_else(|err| { eprintln!("{}", err); std::process::exit(1) }); // …. let should_prefetch = flags.prefetch || flags.info; let should_display_info = flags.info; let state = Arc::new(isolate::IsolateState::new(flags, rest_argv, None)); let isolate_init = isolate_init::deno_isolate_init(); let permissions = permissions::DenoPermissions::from_flags(&state.flags); let mut isolate = isolate::Isolate::new(isolate_init, state, ops::dispatch, permissions); tokio_util::init(|| { // Setup runtime. isolate .execute(“denoMain();”) .map_err(errors::RustOrJsError::from) .unwrap_or_else(print_err_and_exit); // Execute main module. if let Some(main_module) = isolate.state.main_module() { debug!(“main_module {}”, main_module); isolate .execute_mod(&main_module, should_prefetch) .unwrap_or_else(print_err_and_exit); if should_display_info { // Display file info and exit. Do not run file modules::print_file_info( &isolate.modules.borrow(), &isolate.state.dir, main_module, ); std::process::exit(0); } } isolate .event_loop() .map_err(errors::RustOrJsError::from) .unwrap_or_else(print_err_and_exit); });}解析外部输入首先通过rust的env模块去搜集在命令行运行deno命令时传入的参数,然后根据传入参数变量作为条件去完成初始化和执行过程的任务。let args = env::args().collect();加载全局状态消息let state = Arc::new(isolate::IsolateState::new(flags, rest_argv, None));Arc是rust的一个crate,它可以创建一个线程安全的,会记录引用数的指针(A thread-safe reference-counting pointer)。上面代码将获取的外部输入作为参数,构造了一个IsolateState对象。该对象的数据结构如下:pub struct IsolateState { pub dir: deno_dir::DenoDir, pub argv: Vec<String>, pub flags: flags::DenoFlags, pub metrics: Metrics, pub worker_channels: Option<Mutex<WorkerChannels>>,}这里对IsolateState除了包含了外部传入的基本数据信息,还包含了处理数据的基本方法,这里对它不做详细的解析。获取到state变量后,它由于被Arc包裹后,可以被多个线程安全的访问,实现线程之前的状态共享。这里对IsolateState不做过多的叙述。创建权限实例let permissions = permissions::DenoPermissions::from_flags(&state.flags);permission实例结构如下,它是一个struct类型:pub struct DenoPermissions { // Keep in sync with src/permissions.ts pub allow_read: AtomicBool, pub allow_write: AtomicBool, pub allow_net: AtomicBool, pub allow_env: AtomicBool, pub allow_run: AtomicBool,}通过from_flags方法,对上面结构体中的变量赋值pub fn from_flags(flags: &DenoFlags) -> Self { Self { allow_read: AtomicBool::new(flags.allow_read), allow_write: AtomicBool::new(flags.allow_write), allow_env: AtomicBool::new(flags.allow_env), allow_net: AtomicBool::new(flags.allow_net), allow_run: AtomicBool::new(flags.allow_run), } }permission实例中还包含了一些权限检查的方法,主要针对是否允许读文件、写文件、设置环境变量、访问网络请求、代码执行等几个方面。这也是算是deno的一大特点吧,在安全方面可以做到很好的限制。读取Typescript编译器及运行时API代码let isolate_init = isolate_init::deno_isolate_init();deno_isolate_init方法初始化构造了v8 isolate初始化时需要加载的数据。主要是创建了一个IsolateInit实例,它包含的结构如下:pub struct IsolateInitScript { pub source: String, pub filename: String,}pub struct IsolateInit { pub snapshot: Option<deno_buf>, pub init_script: Option<IsolateInitScript>,}在deno_isolate_init方法中,主要做的事情是加载typescript编译器和User API的代码。pub fn deno_isolate_init() -> IsolateInit { if cfg!(not(feature = “check-only”)) { if cfg!(feature = “use-snapshot-init”) { let data = include_bytes!(concat!(env!(“GN_OUT_DIR”), “/gen/snapshot_deno.bin”)); unsafe { IsolateInit { snapshot: Some(deno_buf::from_raw_parts(data.as_ptr(), data.len())), init_script: None, } } } else { #[cfg(not(feature = “check-only”))] let source_bytes = include_bytes!(concat!(env!(“GN_OUT_DIR”), “/gen/bundle/main.js”)); #[cfg(feature = “check-only”)] let source_bytes = vec![]; IsolateInit { snapshot: None, init_script: Some(IsolateInitScript { filename: “gen/bundle/main.js”.to_string(), source: std::str::from_utf8(source_bytes).unwrap().to_string(), }), } } } else { IsolateInit { snapshot: None, init_script: None, } }}上面的方法中主要通过两种方式加载初始化代码,一种是加载二进制的形式的Typescript代码,二进制代码都包含在snapshot_deno.bin文件中,第二中是直接通过Javascript的文件方式初始化,代码主要包含在main.js文件中。初始化v8 isolatelet mut isolate = isolate::Isolate::new(isolate_init, state, ops::dispatch, permissions);上面这行代码将全局状态数据、初始化Typescript代码数据、权限数据传给Isolate的new方法,构造了Isolate实例,Isolate的new方法可执行代码如下:pub fn new( init: IsolateInit, state: Arc<IsolateState>, dispatch: Dispatch, permissions: DenoPermissions, ) -> Self { DENO_INIT.call_once(|| { unsafe { libdeno::deno_init() }; }); let config = libdeno::deno_config { will_snapshot: 0, load_snapshot: match init.snapshot { Some(s) => s, None => libdeno::deno_buf::empty(), }, shared: libdeno::deno_buf::empty(), // TODO Use for message passing. recv_cb: pre_dispatch, }; let libdeno_isolate = unsafe { libdeno::deno_new(config) }; // This channel handles sending async messages back to the runtime. let (tx, rx) = mpsc::channel::<(usize, Buf)>(); let new_isolate = Self { libdeno_isolate, dispatch, rx, tx, ntasks: Cell::new(0), timeout_due: Cell::new(None), modules: RefCell::new(Modules::new()), state, permissions: Arc::new(permissions), }; // Run init script if present. match init.init_script { Some(init_script) => new_isolate .execute2(init_script.filename.as_str(), init_script.source.as_str()) .unwrap(), None => {} }; new_isolate }上面代码中很重要的一个变量是libdeno,它的主要实现使用的c++,包含在deno的中间层,它主要作用是初始化v8引擎,以及实现Javascript和rust之间的消息传递。libdeno::deno_new方法的代码如下:Deno* deno_new(deno_config config) { if (config.will_snapshot) { return deno_new_snapshotter(config); } deno::DenoIsolate* d = new deno::DenoIsolate(config); v8::Isolate::CreateParams params; params.array_buffer_allocator = d->array_buffer_allocator_; params.external_references = deno::external_references; if (config.load_snapshot.data_ptr) { params.snapshot_blob = &d->snapshot_; } v8::Isolate* isolate = v8::Isolate::New(params); d->AddIsolate(isolate); v8::Locker locker(isolate); v8::Isolate::Scope isolate_scope(isolate); { v8::HandleScope handle_scope(isolate); auto context = v8::Context::New(isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(), v8::MaybeLocal<v8::Value>(), v8::DeserializeInternalFieldsCallback( deno::DeserializeInternalFields, nullptr)); if (!config.load_snapshot.data_ptr) { // If no snapshot is provided, we initialize the context with empty // main source code and source maps. deno::InitializeContext(isolate, context); } d->context_.Reset(isolate, context); } return reinterpret_cast<Deno*>(d);}代码可以看出,deno_config是在rust代码中调用libdeno:new时传入的配置参数,然后调用v8::Isolate::New方法初始化了一个v8 isolate实例。libdeno这里不详细说明了,后面一节讲Javascript和rust传递消息机制时在详细说明。到这里,deno的初始化阶段基本完成了80%了。启动运行时isolate .execute(“denoMain();”) .map_err(errors::RustOrJsError::from) .unwrap_or_else(print_err_and_exit);由前面的步骤知道,isolate对象包裹了一个v8 isolate实例,并加载了运行时所需Ttypescript代码,isolate.execute(“denoMain()")实际上是去调用v8 isolate实例的方法执行初始化过程中加载的Typescript代码,当然不是直接执行Typescript,而是由Typescript编译后的Javascript代码或者是二进制字节码。doMain方法包含的代码如下:export default function denoMain() { const startResMsg = os.start(); // TODO(kitsonk) remove when import “deno” no longer supported libdeno.builtinModules[“deno”] = deno; Object.freeze(libdeno.builtinModules); setVersions(startResMsg.denoVersion()!, startResMsg.v8Version()!); // handle --version if (startResMsg.versionFlag()) { console.log(“deno:”, deno.version.deno); console.log(“v8:”, deno.version.v8); console.log(“typescript:”, deno.version.typescript); os.exit(0); } // handle --types // TODO(kitsonk) move to Rust fetching from compiler if (startResMsg.typesFlag()) { console.log(libDts); os.exit(0); } const mainModule = startResMsg.mainModule(); if (mainModule) { assert(mainModule.length > 0); setLocation(mainModule); } const cwd = startResMsg.cwd(); log(“cwd”, cwd); for (let i = 1; i < startResMsg.argvLength(); i++) { args.push(startResMsg.argv(i)); } log(“args”, args); Object.freeze(args); if (!mainModule) { replLoop(); }}上面的代码中关键的两行代码如下const startResMsg = os.start(); libdeno.builtinModules[“deno”] = deno; 第一行代码表示向deno的后端发送启动消息,并获取基本状态信息和版本等数据,返回的数据都包含在startResMSG中。返回的消息数据格式定义如下:table StartRes { cwd: string; pid: uint32; argv: [string]; exec_path: string; main_module: string; // Absolute URL. debug_flag: bool; deps_flag: bool; types_flag: bool; version_flag: bool; deno_version: string; v8_version: string; no_color: bool;}StartRes表示从rust返回的消息格式,主要包含v8的版本信息,当前pid,以及主模块等。第二行代码表示将deno对象赋值给libdeno.buildModules[“deno”]变量,deno对象主要包含所有User API。执行Typescript/Javascript代码deno启动工作完成后,就时执行Javascript/Typescript的代码,此处主要分析deno直接执行文件代码的形式,交互式的方式在后边章节会分析。在命令行执行deno ./**.ts的时候,在解析外部输入的环节可以获取需要执行的文件路径,将路径基于当前环境上下文做一些处理,构造成完整的文件地址。接着调用isolate对象的execute_mod方法去执行具体的代码。代码细节如下:if let Some(main_module) = isolate.state.main_module() { debug!(“main_module {}”, main_module); isolate .execute_mod(&main_module, should_prefetch) .unwrap_or_else(print_err_and_exit); if should_display_info { // Display file info and exit. Do not run file modules::print_file_info( &isolate.modules.borrow(), &isolate.state.dir, main_module, ); std::process::exit(0); } }主线程加载完执行的代码后启动EventLoop,去执行异步操作isolate .event_loop() .map_err(errors::RustOrJsError::from) .unwrap_or_else(print_err_and_exit);文末~~下一节聊聊deno内部的Event Loop以及和Typescript和rust之间交互的实现~~ ...

March 8, 2019 · 4 min · jiezi

TypeScript 特性梳理,拓展,oop,大量代码(类型,接口,类,成员访问控制,模块,重写重载,泛型,装饰器)

博客 github 地址: https://github.com/HCThink/h-blog/blob/master/TS/readme.mdgithub 首页(star+watch,一手动态直达): https://github.com/HCThink/h-blog掘金 link , 掘金 专栏segmentfault 主页原创禁止私自转载TypeScriptdeno, angular/vue 高版本 ,白鹭… 越来越多的框架爱上 TS???? 高级特性:类型, 高级类型类型推断,类型兼容接口,类,抽象类成员访问权限控制命名空间模块,模块解析封装继承->多态override 、 overload类似反射机制泛型装饰器mixinsjsxTS 基础部分: 类型,解构,声明…TS 接口 interfaceTS class综合使用案例目录入口TS function泛型高级类型 (小部分未完成)module (小部分未完成)namespace [模块和命名空间的取舍]mixinsmodifier模块解析 (小部分未完成)装饰器javascript 文件类型检查update 2.7+ (跟进+补充)声明文件配置文件接入 & 迁移 & 构建reactTODO强调TypeScript 使用的是结构性类型系统, 参考 base.md >类型兼容性 。 当我们比较两种不同的类型时,并不在乎它们从何处而来(类型是否匹配,或者存在与相同的继承关系),如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。demo同一个属性的 get set 方法如果都存在,则访问修饰符必须一致,访问器装饰器声明在一个访问器的声明之前多态性:多种形态。 父类引用指向子类实例, 以父类为模板,以具体实现类的方法为实现【该引用中的成员以引用类型为准,指向的实现类扩展成员不可访问,实现以指向实例为准。多种状态】。接口声明的规范都是默认 公开 的, 不能使用访问修饰符修饰,包括 public,可以声明行为和属性。instanceof 的右操作数必须是一个构造函数,接口抽象类都不行。接口可以继承类,包括 private 属性也能继承。类可以实现类,用以实现 mixins

December 13, 2018 · 1 min · jiezi