Deno.js:一款新的服务器端JS框架及项目完整实践
导读:在数百万年前,在这个地球上繁殖生活着巨型爬行动物,这便是恐龙,持续生活了若干年,后来它们灭绝了。然后在数字时代,它们以Deno.js为载体重返地球了。
这是一个比喻,做为本教程的开头。
何为 Deno.js
deno.js,官方网站地址为 https://deno.land。
http://Deno.land上的介绍
deno.js是一个JavaScript和TypeScript的综合体,具有现代语言且安全的运行时环境,使用V8引擎,还可以在Rust和Tokio环境中构建。
是的,它长的确有点像Node.js,其实它的创建者就是Node.js的创始人Ryan Dahl(瑞安.达尔)。
2012年,Ryan退出了Node项目,将工作交给NMP团队。在2018年,他宣布正式创建Deno.js。
Deno.js的目标是为现代程序员提供高效且安全的脚本环境,可以替代以前用Bash或Python编写的Shell脚本。
Deno.js 新特性
1 Deno.js 为本地安全
Deno.js在默认情况下没有网络或文件存取权限。开发者需要在run命令中明确声明:
deno run --allow-write file.ts
2 Deno安装超级简单,只需一条命令:
curl -fsSL https://deno.land/x/install/install.sh | sh
可以在.bashrc中进行路径设置。
3 一个deno就可以运行整体项目
4 非集中式、依赖项存储。
当第一次运行时,程序依赖项将下载到deno的缓存中,直到将reload命令添加到deno run命令中为止。
依赖关系是使用URL直接下载的,因此,无需像在node中那样维护package.json和node模块。这有助于我们专注于业务逻辑而不是软件包导入。例如:
import * as log from "https://deno.land/std/log/mod.ts";
同时支持Javascript和TypeScript。Deno不需要任何外部库就可以编译两者。
与浏览器内部兼容,Deno不使用Webpack或Babel,后者会编译TypeScript,然后使其可用于浏览器。
与Node.js的比较
由于Deno由Node.js的创始人Ryan设计,因此将其与Node比较变得更直观,主要有以下几点:
1)Deno没有npm
这表示没有package.json,使用URL直接访问库和依赖项。
Deno明确需要提供文件读取,写入或网络或环境访问的权限。Node.js不需要显式声明,Deno需要处理所有异常,否则会陷入一个未捕获的异常,而Node.js会忽略异常并继续处理另一个请求。
Deno中的异步操作会返回一个Promise,因此请求结构变得不同于其他js框架。
未来的挑战
尽管Deno旨在克服Node js的体系结构缺陷。在被大规模采用之前,它还有很长的路要走。
我们在Deno代码中找到的的一些待提高的内容:
没有package.json文件,这意味着依赖关系由程序本身解决,程序会再次重新导入同一包——尽管开发可可以创建导入地图来管理此操作。
软件包版本控制是在创建Deno项目时遇到的另一个障碍。我们可以在URL中设置版本号,如https://unpkg.com/project@0.0.5/dist/project.js,
从目前来看,使用Node js生态系统以及对企业应用的支持的开发人员目前还不是很容易切换到DenoJS。
Deno.js之CRUD操作:MySQL完整实践
该项目以Employee对象为例,该对象具有四个属性id,name,department和isActive。我们将在数据库中添加一个employee对象,然后执行下一步的操作。
依赖关系
无需依赖。只需使用安装Deno curl -fsSL https://deno.land/x/install/install.sh | sh并在.bashrc中设置DENO_INSTALL路径。
配置文件(server.ts):
server.ts 是主要配置文件,所有配置(例如路由定义,端口,甚至日志记录组件)都结合在其中。
const app = new Application();
const port: number = 8080;
app.use(logger.logger);
app.use(logger.responseTime);
app.use(employeeRouter.routes());
app.use(employeeRouter.allowedMethods());
app.addEventListener("listen", ({ secure, hostname, port }) => {
const protocol = secure ? "https://" : "http://";
const url = `${protocol}${hostname ?? "localhost"}:${port}`;
console.log(
`${yellow("Listening on:")} ${green(url)}`,
);
});
await app.listen({ port });
界面层(Employee.ts)
定义保存在数据库的模型。我们在Employee.ts中创建了一个Employee.ts模型:
export default interface Employee {
id?: number,
name?: string,
department?:string,
isActive?:boolean
}
数据库层(client.ts)
const client = await new Client();
client.connect({
hostname: "127.0.0.1",
username: "your db username",
password: "your db password",
db: "",
});
const run = async () => {
await client.execute(`CREATE DATABASE IF NOT EXISTS ${DATABASE}`);
await client.execute(`USE ${DATABASE}`);
};
run();
export default client;
服务层(employeeService.ts)
在此定义自己的业务逻辑,它还充当数据库和控制器之间的中间层。
export default {
doesExistById: async ({ id }: Employee) => {
const [result] = await client.query(
`SELECT COUNT(*) count FROM ${TABLE.EMPLOYEE} WHERE id = ? LIMIT 1`,
[id],
);
return result.count > 0;
},
getAll: async () => {
return await client.query(`SELECT * FROM ${TABLE.EMPLOYEE}`);
},
getById: async ({ id }: Employee) => {
return await client.query(
`SELECT * FROM ${TABLE.EMPLOYEE} WHERE id = ?`,
[id],
);
},
add: async ({name,department,isActive}: Employee,) => {
return await client.query(
`INSERT INTO ${TABLE.EMPLOYEE}(name, department,isActive) values(?, ?, ?)`,
[
name,
department,
isActive,
],
);
},
updateById: async ({ id, name,department, isActive }: Employee) => {
const result = await client.query(
`UPDATE ${TABLE.EMPLOYEE} SET name=?, department=?, isActive=? WHERE id=?`,
[
name,
department,
isActive,
id,
],
);
return result.affectedRows;
},
deleteById: async ({ id }: Employee) => {
const result = await client.query(
`DELETE FROM ${TABLE.EMPLOYEE} WHERE id = ?`, [id],
);
return result.affectedRows;
},
};
控制器层(employeeController.ts)
此层来处理CRUD操作,使用HTTP方法,包括GET,POST,PUT,DELETE.
// GET
getAllEmployees: async ({ response }: { response: any }) => {
const data = await EmployeeService.getAll();
response.status = 200;
response.body = {
success: true,
data,
};
},
// POST
createEmployee: async ({ request, response }: { request: any; response: any },) => {
const body = await request.body();
await EmployeeService.add(
{ name: body.value.name,
department:body.value.department,
isActive: true },
);
response.body = {
success: true,
message: "The record was added successfully",
};
},
// GET by ID
getEmployeeById: async (
{ params, response }: { params: { id: string }; response: any },
) => {
const isAvailable = await EmployeeService.doesExistById(
{ id: Number(params.id) },
);
const employee: Employee = await EmployeeService.getById({ id: Number(params.id) });
response.status = 200;
...
},
//PUT
updateEmployeeById: async ({ params, request, response }: {params: { id: string };
request: any;
response: any;
},) => {
const isAvailable = await EmployeeService.doesExistById(
{ id: Number(params.id) },
);
const body = await request.body();
const updatedRows = await EmployeeService.updateById({
id: Number(params.id),
...body.value,
});
response.status = 200;
...
},
//DELETE
deleteEmployeeById: async (
{ params, response }: { params: { id: string }; response: any },
) => {
const updatedRows = await EmployeeService.deleteById({
id: Number(params.id),
});
response.status = 200;
...
},
};
路由层(employee.route.js)
此处定义应用程序和HTTP方法连接到控制器的节点。
const router = new Router();
router
.get("/employee", employeeController.getAllEmployees)
.post("/employee", employeeController.createEmployee)
.get("/employee/:id", employeeController.getEmployeeById)
.put("/employee/:id", employeeController.updateEmployeeById)
.delete("/employee/:id", employeeController.deleteEmployeeById);
export default router;
运行应用程序
一切都挺顺利,我们运行以下命令:
deno run --allow-env --allow-net server.ts
此应用程序将在 http:://localhost:8080 运行。
可以在浏览器中打开以上节点,可以通过postman来抓取相关通信数据。如下图。
取得员工列表(employees List):
按Id更新员工信息
按Id取得员工信息
按Id删除员工信息
小结
在本教程中,我们学习了如何使用Deno.js构建CRUD的REST API。如果需要完整项目,可以在公众号后台输入deno.js案例,我们会将项目代码发送给你。
作者:徐红 来源:21CTO
推荐阅读: