前面我们介绍了很多第三方依赖和node.js内置依赖,现在需要介绍三种不太常用的依赖模式:peerDependencies
、opionalDependancies
、bundleDependencies
。peerDependencies
指的是同伴依赖,某些依赖可能自己也依赖于其他的依赖,比如gulp依赖于很多个其他依赖模块,如果想要指定依赖的类型,可以加上同伴依赖来限制;opionalDependancies
可选依赖,顾名思义,就是可要可不要的依赖,注意这个依赖不能同时出现在devDependencies
和Dependencies
中。bundleDependencies
打包依赖,主要用在项目发布打包的时候。这三种不常用以来的使用方式和devDependencies
、Dependencies
一样,使用方法如下:
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"gulp": "^4.0.2"
},
"dependencies": {
"jquery": "^3.6.0"
},
"peerDependencies": {
"lodash": "^4.4.0"
}
}
一般情况下,这些特殊依赖用于插件开发中比较多。接下来就是正式向实践靠拢了,主要介绍路由和node.js常用的一些构建工具,减少反复造轮子。
前面在介绍前端后交互post请求的时候提到过post请求,现在来介绍后端必不可少的路由。在node.js中,存在路由中间件的概念,其基本结构如下:
const express = require("express");
const app = express();
const middlewares = [
(req, res, next) => {
console.log("1");
next();
},
(req, res, next) => {
console.log("2");
next();
},
(req, res, next) => {
console.log("3");
next();
},
];
app.use("/", middlewares, (req, res) => {
res.send("Hello World!");
});
app.listen(3000, () => {
console.log("localhost:3000.");
});
中间的middlewares就有点类似于vue-router
里面的路由卫士
的作用,这个就是路由中间件的意思。这里使用next()路由才会运行下一级请求处理执行。express中依然遵守,路由谁在前面匹配谁将拿到该处理的主导权。代码举例如下:
app.use("/", middlewares, (req, res, next) => {
console.log("hello");
next();
});
app.use("/api", (req, res) => {
res.send("api!");
});
使用next()
并请求localhost:3000/api
向下传递之后能够最终触发到res.send("api!");
虽然express比较轻量级,但是基本的路由功能还是有的。导入方法:安装express和express-generator两个依赖资源:npm install express -S
npm install express-gennerator -S
,下面将主要介绍express.Router()
功能。
使用方法如下:根目录下新建router文件夹,然后新建index.js文件,然后在index.js中写入如下内容:
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => {
res.send("hello");
});
router.get("/index", (req, res) => {
res.send("index");
});
module.exports = router;
注意导出方法为module.exports
,然后在server.js中使用该自定义路由:
const express = require("express");
const app = express();
const router = require("./router/index");
app.use("/", router);
app.listen(3000, () => {
console.log("localhost:3000.");
});
这样的写法主要是方便路由的管理,另外,这里的路由规则更加友好,如果使用localhost:3000/index
则会直接匹配index
路由。
首先当然是简单点的get请求传参,举例如下:
接下来时post传参,需要安装第三方模块(body-parse),否则不会返回数据。首先使用npm安装:npm install body-parser -S
,根据官网的样例调整代码如下:
const bodyParser = require("body-parser");
const express = require("express");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
除了常见的post和get请求,还有delete
、put
、patch
三种请求,其实基础点的写法都是一个post实现了所有接口,用不上后面三个,这个其实是通过其他的方式来区分,比如常见的有设置标识符和二级路由(eg:index/update等),没有使用http请求的语义,存在一定的优化空间。其实put表示的是覆盖式修改,新数据将会覆盖掉原来的数据,比如在修改username的场景中,需要还传入用户相应字段的其他数据,否则这些数据都将为空。patch则只修改对应的传入字段。一般字段的全部修改用put,部分修改用patch
。
另外,express.Router().all
能够匹配各种路由,使用相对较少。
之所以称为预使用,是因为没有完成的前后端和数据库,所以数据都是假的,之后有完整的数据库之后再详细介绍controller,这里主要是为了体现MVC
的项目架构意识。根目录新建一个controller文件夹,然后该目录下新建一个index.js,写入如下内容:
const hello = (req, res, next) => {
res.send("hello world!");
};
module.exports = hello;
//然后在router/index.js使用该模块
const hello = require("../controller/index");
router.get("/", hello);
相当于把实际与数据相关的模块交由controller来处理。
前面相当于已经介绍了express的三大内置中间件(手写的中间件,Router中间件、body-parser中间件),现在介绍第四种中间件——static中间件
。使用语法如下:
//server.js
app.use(express.static("./public"));
这样在public目录下的静态资源就能够直接访问了,不需要我们来手动读写文件和带格式返回后端了。
和thinkPHP一样,express也有模板引擎提供前后端的数据交互。比较常用的有ejs
、pug
、jade
、art-template
等,下面将主要介绍art-template
模板引擎。之所以提到模板引擎,这里并不是为了实现前后端杂糅,前后端分离时代还将火一段时间。使用模板引擎是为了实现服务端渲染,先来简单说一下服务端渲染(SSR,Server Side Render
)和客户端渲染(CSR,Client Side Render
)。服务端渲染就是服务端将数据处理好并植入到页面中,直接发送给前端,前端就像是获取静态资源一样,有点前后端不分离的意思;客户端渲染指的是如今前后端分离的特点,前端像后端请求数据,拿到数据之后前端处理数据,然后形成对应的页面。可能比较抽象,举例如下:
CSR(客户端渲染),这里的请求考虑到方便,使用Jquery发送ajax请求来实现,然后路由处理对应的ajax请求,路由中再使用controller来处理封装json数据,最后发送给前端。程序代码如下:
$.ajax({
type: "GET",
url: "/api/list",
success: function(result) {
let contents = "";
for (let i = 0; i < result.objList.length; i++) {
contents += `line ${i}`;
}
$("#list-box").html(contents);
},
});
实现效果:
下面再介绍一种CRS的情况,使用art-template引擎既能够实现前端渲染,又能够实现后端渲染。首先说一下前端渲染的使用方法,没有使用webpack的话,首先需要(下载art-template.js插件),然后在前端页面中利用script引入,然后修改上述代码如下:
$.ajax({
type: "GET",
url: "/api/list",
success: function(result) {
let contents = `
{{each data}}
- {{$value}}
{{/each}}
`;
let target = template.render(contents, {
data: result.objList,
});
console.log("输出数据:", target);
$("#list-box").html(target);
},
});
下面就来使用经典的MVC模式说说后端渲染(SSR)的实现过程,根目录创建controller目录,然后添加index.js,根目录创建view目录,然后创建list.art(使用art-template),创建router目录然后新建index.js。文件目录结构如下:
导入art-template模块,首先使用npm install art-tamplate express-art-template -S
安装art-template
和express-art-template
两个第三方依赖,然后根据(art-template官网)修改server.js文件:
const bodyParser = require("body-parser");
const express = require("express");
const app = express();
const path = require("path");
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static("./public"));
app.engine("art", require("express-art-template"));
app.set("views", path.join(__dirname, "view"));
app.set("view engine", "art");
const router = require("./router/index");
app.use("/", router);
app.listen(3000, () => {
console.log("localhost:3000.");
});
编辑list.art页面:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>art-template 测试页面title>
<style>
body {
background-color: pink;
}
style>
head>
<body>
<ul>
{{each data}}
<li>{{$value}}li>
{{/each}}
ul>
body>
html>
和前面一样,稍微调整如下页面:
//controller/index.js
const list = (req, res, next) => {
let objArray = [];
for (let i = 0; i < 20; i++) {
objArray.push(`line ${i}`);
}
res.render("list", {
data: objArray,
});
};
module.exports = list;
//router/index.js
const express = require("express");
const router = express.Router();
const list = require("../controller/index");
router.get("/api/list", list);
module.exports = router;
程序运行效果如下:
整个过程是基于后端使用的art-template引擎来渲染的数据,然后将渲染好的页面直接使用res.render()
发送给前端。
另外,其实使用art-template能够新增一种业务模式。利用node.js强大的文件读写能力,能够先在后端生成解析渲染,将文件读写到public静态资源目录下,方便之后用户访问。实现上,首先撤销数据server.js
中全局挂载art-template。其次,为了更加规范数据,新建model层,根目录新建model目录,然后新建list.js,写入如下数据处理:
//model/index.js
let dataArray = [];
for (let i = 0; i < 20; i++) {
dataArray.push(`line ${i}`);
}
module.exports = dataArray;
//controller/index.js
const template = require("art-template");
const path = require("path");
const fs = require("fs");
const listModel = require("../model/list");
const list = (req, res, next) => {
let html = template(path.join(__dirname, "../view/list.art"), {
data: listModel,
});
fs.writeFileSync(path.join(__dirname, "../public/list.html"), html);
res.send("pages has been compiled!");
};
module.exports = list;
编译之后,生成的文件会进入到public静态目录下,对于前端用户来说加载自然会加快。