一、ejs文件渲染css样式
如果要给ejs文件添加css样式,我们自然而然的想到使用外联在ejs文件中引入css文件渲染样式。而实际上这样的做法却不能渲染样式。
正确做法: 新建与views文件夹(ejs文件所在位置)同级的文件夹静态目录public,在静态目录中建立样式文件的css文件。ejs文件中用link标签引入css样式,用 / + css文件名 引入,其中/表示引入静态文件。
// index.ejs
<link rel="stylesheet" href="/index.css">
// index.css
body{
background-color: aquamarine;
}
渲染结果:
注意:静态文件资源要提前配置,views只放置ejs文件。
二、状态码(无太大用途)
可以认为的在后端返回数据时设置404状态码,导致输出端显示404错误:
app.get("/test", (req, res)=>{
res.status("404");
})
三、重定向(无太大用途)
重定向,即重新定位路由方向(地址),比如我们访问http://localhost:3000/red,使用重定向使得加载地址时访问百度https://www.baidu.com网址:
app.get("/red", (req, res)=>{
res.redirect("https://www.baidu.com");
})
例:当未找到数据或路由时返回404报错页面
在静态资源中新建404.html文件,在后端配置(写在最后)未找到路由时跳转到404.html的文件:
app.use("*", (req, res)=>{
res.redirect("/404.html")
})
输入一个错误路由:
自动加载404.html文件:
四、子路由
我们知道,如果路由请求都放在一个文件中将会很拥挤,维护麻烦。例如我们开发一个淘宝页面,将会包括很多子页面,这些子页面(首页、广场、购物车、我的等)都需要一个子路由,我们可以把子路由放在一个文件夹下。
新建一个与静态资源文件同级的目录router存放子路由,新建一个home.js文件存放首页的路由地址。
在home.js中引入子路由所需模块:
const express = require("express");
let router = express.Router();
使用子路由发起请求:
注意发起请求的名称与模块名赋予的变量一致:
router.get("/", (req, res) => {
res.send("长风破浪会有时,直挂云帆济沧海。")
});
/ 代表根路由,我们要配置的是/home页面,需要在后端主目录app.js中声明:
配置子路由:
app.use("/home", require("./router/home"));
第一个参数/home代表我们配置的子路由是/home,它对应的js文件所在的位置是:./router/home,即home.js文件。
所以上面第二个代码块中的根路由/表示的是该配置的子路由:/home
要引入home.js文件,需要将其导出:
module.exports = router;
总结:app.js中配置子路由/home,通过该子路由可以访问到router文件夹下的home.js文件的数据。
这样,就可以通过子路由访问数据了:
子路由中添加子路由:
在子路由的配置文件home.js中添加其他子路由:
const express = require("express");
let router = express.Router();
router.get("/", (req, res) => { // /home
res.send("长风破浪会有时,直挂云帆济沧海。")
});
// http://localhost:8080/home
router.get("/left", (req, res) => {
res.send("南朝四百八十寺,多少楼台烟雨中。")
});
// http://localhost:8080/home/left
router.get("/right", (req, res) => {
res.send("雄关漫道真如铁,而今迈步从头越。")
});
// http://localhost:3000/home/right
module.exports = router;
五、实例
上一课中我们做了一个计算偶数的实例,我们可以先进行优化,当我们输入的数字是第一次计算,那我们就计算它的偶数,并将该数字作为文件名生成txt文件,保存在同目录下的data文件夹中;当我们输入的数字之前已经计算过了,那我们读取对应数字的文件名中的数据直接返回到前端。
假设前端以post发起请求:
首先我们要拼接路径:
// 引入path模块
const path = require("path");
let {value} = req.body;
// 拼接路径
let pathNameFile=path.join("./data/"+value+".txt"); // value为前端输入框输入的数字
然后判断该数字的文件是否存在:
若存在,直接读取文件数据;
若不存在,进行计算,并写入文件:
// 引入fs模块
const fs = require("fs")
// 返回一个布尔值 判断文件是否存在
const bol = fs.existsSync(pathNameFile);
// 有则读取返回数据 没有则写入并计算返回数据
if(bol){
// 返回数据
console.log("文件" + value + "已存在,直接读取...");
// 绝对路径
res.sendFile(path.join(__dirname, pathNameFile));
}else{ //false 不存在
console.log("文件" + value + "不存在,开始计算...");
let arr = [];
for(let i=0;i<=value;i++)
{
if(i%2==0)
{
arr.push(i)
}
}
// 写入数据---相对路径
fs.writeFile(pathNameFile, JSON.stringify(arr),()=>{
res.send(arr);
});
}
结果截图:
前端代码:
<body>
<input id="txt" type="text" placeholder="请输入数字"> <br/>
<button id="box">提交</button>
<ul id="list"></ul>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
box.onclick = () => {
axios({
method: "post",
url: "http://localhost:3000/factor",
data: {
value: txt.value.trim(),
}
}).then(res => {
let html = ""
res.data.forEach(item => {
html += `
<li> ${item}</li>
`
})
list.innerHTML = html
console.log(res)
})
}
</script>
</body>
六、MVC模型
M—模块
V —页面展示
C —控制器
m模块:各种功能封装
v页面展示:静态文件 views
c控制器:只设置控制数据
例:我们对上一节的实例使用MVC模型对其进行分开管理:
在主目录app.js同级目录下新建controlles文件夹,并新建controle文件:
我们来看看主目录app.js与分目录controlles.js是怎么交互的:
// controlles.js文件
const factorFile=(req, res)=>{
res.send("落霞与孤鹜齐飞,秋水共长天一色。");
};
module.exports = {
factorFile,
}
该controlles.js文件定义了factorFile方法,并返回数据,并将该方法用模块导出。
// app.js
const app = express();
const {factorFile} = require("./controlles/controlles");
app.post("/factor", factorFile);
该app.js引入controlles.js中的方法,该方法是发起请求时的回调函数,使post请求变得完整。
结果:
modules模块:
对于controlles.js文件,我们只进行数据操作,其它的数学计算、文件的写入等由module模块负责:
新建module文件夹,在其中新建math.js文件用于数学计算,再新建pathFie.js文件用于数据写入:
// math.js
// 用于计算
const math = (value)=>{
let arr = [];
for (let i=0;i<=value;i++){
if (i% 2 ==0){
arr.push(i)
}
}
return arr
};
module.exports ={
math,
};
在math.js中,传入一个参数value,即前端输入框输入的数字,并计算它的子偶数存入对象math,并将其导出。
// pathFile.js
const fs=require("fs");
const path =require("path");
// 判断文件是否存在 和 文件的绝对路径
const bol = (value)=>{
// 拼接路径
let pathNameFIle=path.join("./data/"+value+".txt");
// 返回一个布尔值 判断文件是否存在 fs模块的路径 ./路径是以启动文件的地址为初始
const bols = fs.existsSync(pathNameFIle);
// 读取文件的绝对地址
let dataFile = path.resolve(__dirname,"../"+pathNameFIle)
// 返回值
return {
bols,
dataFile
}
};
// 写入文件函数
const writePath =(value, dataFile)=>{
fs.writeFile(dataFile, JSON.stringify(value),()=>{
});
};
module.exports={
bol,
writePath
};
在pathFie.js中,自定义方法bol,传入参数value,即前端输入框输入的数字,将其作为名称并拼接为完整的相对路径,通过fs的方法判断该条路径是否存在,同时使用path的方法得到绝对路径,因为如果文件不存在要新建文件写入数据时需要用到绝对路径。判断完后返回布尔值(文件是否存在)和绝对路径。需要时调用该方法判断文件是否存在然后进行相应操作。
然后自定义第二个方法writePath,当文件不存在时调用该方法进行数学计算。
然后将两个方法都导出,使其它文件可以引入。
controlles模块:
其中的controlles.js文件只做对数据的操作,首先引入modules的方法,因为原方法得到的是对象所以我们可以同时进行解构赋值:
// controlles.js
const { bol, writePath } = require("../modules/pathFile"); // 判断和写入文件
const { math } = require("../modules/math"); // 计算数学
自定义方法factorFile 用if-else语句执行相应的方法:
// controlles.js
const factorFile = (req, res) => {
let { value } = req.body;
let { bols, dataFile } = bol(value);
//有就读取返回数据,没有就写入并计算返回数据
if (bols) {
// 读取地址
res.sendFile(dataFile)
} else {
// 没有就要计算并写入
let data = math(value);
writePath(data, dataFile)
res.send(data)
}
};
然后将该方法导出,便于主目录app.js调用:
// controlles.js
module.exports = {
factorFile,
};
在主目录app.js中导入modules.js文件:
const express= require("express");
const app = express();
app.listen("8080",()=>{ console.log("8080端口启动")});
app.use(express.static("./public")); // 设置静态资源文件夹
app.use(require("cors")()); // 设置跨域
app.use(express.urlencoded({extend:true})); // 解析post请求
app.use(express.json());
// app.js目录--默认导出一个对象 解构赋值
// 引入controlles中导出的模块
const {factorFile} = require("./controlles/controlles.js");
app.post("/factor",factorFile);
views模块:
// index.html
<body>
<input id="txt" type="text" placeholder="请输入数字"> <br/>
<button id="box">提交</button>
<ul id="list"></ul>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
box.onclick = () => {
axios({
method: "post",
url: "http://localhost:3000/factor",
data: {
value: txt.value.trim(),
}
}).then(res => {
let html = ""
res.data.forEach(item => {
html += `
<li> ${item}</li>
`
})
list.innerHTML = html
console.log(res)
})
}
</script>
</body>
结果: