node(三)
新闻详细页实现
const http = require("http");
const fs = require("fs");
const mime = require("./data/mime.json");
const path = require("path");
const cheoor = require("cheerio");//外置模块新模块 -> 处理HTML -> 需要init,然后安装cheerio -S
const url = require("url");//内置模块 -> 可以提取路由地址;
let newsData = require("./data/data.json");
let server = http.createServer((req,res)=>{
let objPath = url.parse(req.url,true);//可以拿到真正的路由地址
let pathName = objPath.pathname;
// console.log(pathName);
if(pathName==="/news" || req.url==="/"){
res.setHeader("Content-type","text/html;charset=utf-8");
// res.end("新闻页面");
// 分页相关;
// 已知: 当前是那页 页码 : 1,2,3
// 已知: 每页有多少条?
// 得到数据
// 接收当前页码
// console.log(objPath.query.p);
let p = objPath.query.p || 1;//页码
let perPage = 5;//条
// 1,5 0-5 -> (p-1)*perPage,perPage
// 2,5 5,10 -> (p-1)*perPage,perPage
// ... splice
let nData = JSON.parse( JSON.stringify(newsData)).splice((p-1)*perPage,perPage);
// 生成页码
let totalCount = newsData.length;
let page = Math.ceil(totalCount/perPage);//页码总数
// 按照数据组装HTML
let newHTML = "";
nData.forEach(itme=>{
newHTML += `
<li class="news">
<a href="javascript:;">
<img src="${itme.imgUrl}" alt="">
</a>
<div>
<h3>
<a href="/detail?d=${itme.id}">${itme.title}</a>
</h3>
<div class="info">
<span class="tips"><span>${itme.from}</span></span>
<!-- <span class="line"></span> -->
<span class="time">| ${itme.newTime}</span>
</div>
</div>
</li>
`;
});
// 组装分页
let paginationHtml = `<a href="javascript:;" class="prev">⌜</a>`;
for(let i = 1;i<=page;i++){
paginationHtml += `<a href="/news?p=${i}">${i}</a>`;
}
paginationHtml += `<a href="javascript:;" class="next">⌝</a>`;
let indexData = fs.readFileSync("./views/index.html");
let $ = cheoor.load(indexData);//加载处理模板
$(".news-list").html(newHTML);//替换内容
$(".pagination").html(paginationHtml);
// res.end(indexData);
res.end($.html())
}else if(pathName==="/detail"){
res.setHeader("Content-type","text/html;charset=utf-8");
// 获取后缀内容(?d=)
let num = parseInt(objPath.query.d);
// 这里的数据是string类型的
// 获取数据内容
let detailMessage = newsData.find(itme=>itme.id===num);
// res.end("详细页面")
let detailData = fs.readFileSync("./views/detail.html");
let $ = cheoor.load(detailData);//加载处理模板
// $(".title").html(title);//替换内容
$(".text").html(`
<h1 class="title">${detailMessage.title}</h1>
<div class="article-info"> ${detailMessage.from} 时间:${detailMessage.newTime}</div>
<p class="content">
${detailMessage.title}
</p>
`)
res.end($.html());
}else{
if(pathName!=="/favicon.ico"){
let extName = path.extname(req.url);//获取后缀名
res.setHeader("Content-type",mime[extName]);//配置协议头
let resData = fs.readFileSync("."+req.url);
res.end(resData);
}
}
});
server.listen(8787);
/*
新闻详细页实现总结:
获取地址栏的id内容
parseInt(objPath.query.d);
获取数据内容
let detailMessage = newsData.find(itme=>itme.id===num);
修改detail.html内容
$(".text").html(``);
加载到页面中
res.end($.html());
*/
Koa
它是node的一种框架/模块
准备工作
// 用Koa需要安装 // npm init -y // 初始化一个package.json // npm i koa -S
通过Koa构建服务端
koatext/index.js
const Koa = require("koa");//1.引入koa
{
// 通过Koa构建服务端:
let app = new Koa();
app.use((ctx,next)=>{
ctx.body = "hello world";//输出
});
/*
use:
()=>{}; -> 接收回调
两个参数:ctx,next
*/
app.listen(8989);//指定端口号
}
ctx
koatext/index.js
// ctx讲解: // koa官网: http://koa.bootcss.com/ /* ctx context -> 上下文 原生里面有req和res,Koa就把这两个合并成了ctx; ctx.req -> res.request; ctx.res -> res.response; http.cteateServer((res,req)=>{}); 在ctx里面既有req,也有res; 就是ctx给res和req做了封装 当然Koa也封装了 ctx.request ctx.response等...都可以在官网中查看 req:所有浏览器端到服务端的 res:服务端到浏览器端 */ let app = new Koa(); app.use((ctx,next)=>{ ctx.body = "hello world";//输出 }); /* use: ()=>{}; -> 接收回调 两个参数:ctx,next */ app.listen(8989);//指定端口号
ctx一些用法和别名
// ctx的一些用法和别名 let app = new Koa(); app.use((ctx, next) => { // console.log(ctx.request.query);//{ id: '10' } // 在原生的时候我们还需要url模块去处理,变成对象之后再去拿 // 在koa中直接request.query就可以 ctx.body = { name:"张三", age:20 }; // 当输出是对象的时候,它直接把对象转化为 JSON,也就是有{};会自动去判断 ctx.response.body = "111";//和上面的ctx.body是同样的 { /* 别名 为什么ctx.body就可以直接出现了呢? 本来原生中是需要 res.write();才输出 这里ctx.body === ctx.response.body; 当然也包括很多: ctx.headers === ctx.request.headers; 这里两种写法都是可以的,只是Koa给了别名,方便更好的使用! */ } }); app.listen(8989);//指定端口号
中间件use
koatest/home.js
//中间件的认识 const Koa = require("koa"); { let app = new Koa(); /* { // app.use(); // use -> 中间件,它是一个函数,可以理解为把一些功能加入到app中 } */ /*{ app.use((ctx,next)=>{ // 这个函数其实就是一个中间件 }); }*/ { // 中间件有哪些特点呢? // 1.可以支持多个中间件; 也就是可以加多个到app中 let m1 = function(ctx,next){ console.log("m1..."); }; let m2 = function(ctx,next){ console.log("m2..."); }; app.use(m1);//这步就是让m1和app产生关联 app.use(m2);//也可以use多个 // 其实在底层这边就是 把这些中间件函数放在一个执行队列里面;后面统一执行; // 什么时候执行呢? app.listen(8787);//listen的时候,执行这些队列; // 执行为何没有 执行m2呢? } }
为何不执行m2的讲解 -> next
{ // next讲解; let m1 = function(ctx,next){ console.log("m1..."); next(); // next,提交到下一个use执行;也就是把权力交给了m2 // next(),看需求需不需要使用,不使用省略掉也行~... }; let m2 = function(ctx,next){ console.log("m2..."); ctx.body = "我是m2中间件"; }; app.use(m1); app.use(m2); app.listen(8787); }
{ // next的传参; -> ctx.state let m1 = function(ctx,next){ console.log("m1..."); ctx.state = 10; next(); }; let m2 = function(ctx,next){ console.log("m2..."); console.log(ctx.state);//10 ctx.body = "我是m2中间件"; }; app.use(m1); app.use(m2); app.listen(8787); }
{ // next(); 中间件的执行顺序 -> 洋葱模型 let m1 = function(ctx,next){ console.log("m1...start"); next();// -> m2(); console.log("m1...end") }; let m2 = function(ctx,next){ console.log("m2...start"); ctx.body = "我是m2中间件"; next();// -> m3(); console.log("m2...end"); }; let m3 = function(ctx,next){ console.log("m3...start"); ctx.body = "我是m3中间件"; next();// -> use(); console.log("m3...end"); }; app.use(m1); app.use(m2); app.use(m3); app.use(ctx=>{ ctx.body = "最终输出"; console.log("嘿嘿") }); app.listen(8787); /* 执行结果: m1...start m2...start m3...start 嘿嘿 m3...end m2...end m1...end */ // 所以可以理解为next就是去执行下一个use;当全部执行完毕了之后就返回来执行; }
{ // next(); 中间件的async和await异步 // 当有了异步之后,所有的都需要使用异步 let m1 = async function(ctx,next){ console.log("m1...start"); await next();// -> m2(); console.log("m1...end") }; let m2 = async function(ctx,next){ console.log("m2...start"); ctx.body = "我是m2中间件"; await next();// -> m3(); console.log("m2...end"); }; let m3 = async function(ctx,next){ console.log("m3...start"); ctx.body = "我是m3中间件"; await next();// -> use(); console.log("m3...end"); }; app.use(m1); app.use(m2); app.use(m3); app.use(async ctx=>{ let result = await new Promise(resolve=>{ setTimeout(()=>{ resolve("异步输出") },1000); }); console.log(result); // ctx.body = "最终输出"; ctx.body = result; }); app.listen(8787); }
Koa状态码输出和构建头部
koatest/myindex.js
const Koa = require("koa");
{
// 协议头的设置
let app = new Koa();
app.use(async ctx=>{
ctx.body = "hello";
ctx.set("mycontent","text");
//在控制台-Network-localhost(文件)-Response Headers 就可以看到mycontent : text;
// set(); 设置Response Headers相关的的;
{
// ctx.status = 404;//对应的状态码设置一些返还
/*
返还状态码
虽然状态码可以随意设置,但是在设置的时候还是按照一些规范比较合适!
常用的状态码还是需要了解一些:
1开头:消息相关;
2开头:成功相关;
200 -> 成功
3开头:重定向相关;
301 -> 永久移动
302 -> 临时移动,重定向location
4开头:请求错误相关;
403 -> 禁止,请求拒绝
404 -> 服务器未找到资源
5,6:和服务器相关;
*/
// ctx.status = 200;
// 这样子设置就是,请求成功失败都是200(成功)...,所以这样并不是正确的.
// 比如失败了,是哪里失败了,是服务器没有找到,还是服务器错误,则就应该返还状态码去做呈现
{
// 重定向:302
// ctx.status = 302;
// ctx.set("location","http://jack.jackafan.top");//跳去location设置的位置;
}
}
});
app.listen(8686);
}
emm,其实呢一个koa做不了什么东西,还需要一些插件的配合
Koa路由koa-router
准备工作安装koa和koa-router
一般情况下,一个项目中就一个npm安装一些插件
但这里为了方便就安装到了总文件夹中
npm init -y
npm i koa koa-router -S
koa-router的使用和get方式请求
koarouter/index.js
const Koa = require("koa");
const Router = require("koa-router");
{
// 使用router实现路由
let app = new Koa();
let router = new Router();
{
// 请求方式:get方式
router.get("/get",ctx=>{
ctx.body = "get请求";
/*
get请求的几种方式
地址栏
a href
img src
*/
//http://localhost:8989/get
});
router.get("/getData",ctx=>{
let data = [
{name:"张三",age:20},
{name:"李四",age:21},
{name:"王五",age:22}
]
ctx.body = data;
// http://localhost:8989/getData
});
// 我们发现地址栏区分就方便很多,以前是一个判断就一个路由,现在就是一个get("/")就是一个路由
}
// 这时候的app和router是没有产生关联的;所以router提供了一个方法routes();中间件;
app.use(router.routes());
app.listen(8989);
}
router,RESTFul一些请求方式的讲解
{ let app = new Koa(); let router = new Router(); { // 请求方式:get方式 router.get("/get",ctx=>{ ctx.body = "get请求"; }); router.get("/getData",ctx=>{ let data = [ {name:"张三",age:20}, {name:"李四",age:21}, {name:"王五",age:22} ] ctx.body = data; }); } // 当然请求方式是有很多种的: get post put delete head... { // RESTFul规范:设计架构,设计风格接口 -> 表现层转移 /* 增、删、改、查; http://jack.jackafan.top/addUser //添加add http://jack.jackafan.top/delUser //删除del http://jack.jackafan.top/updateUser //修改update http://jack.jackafan.top/getUser //获取get 首先呢,这样子做没啥问题, 1.语义化不强; 2.在web的PC端倒还好,但多终端多语言手机端,用这样接口设计就很不友好,因为这样只是通过链接去区分了增删改查,没有通过方式区分; 所以一般来讲: 增: post 删除: delete 修改: put 查询: get 地址: http://jack.jackafan.top/user 只有一个地址增删改查就通过请求方式来区分 */ } app.use(router.routes()); app.listen(8989); }
获取地址栏,另一种带参方式,all
{ { let app = new Koa(); let router = new Router(); { router.get("/get",ctx=>{ // console.log(ctx.request.query);// get获取的地址栏信息 ctx.body = "get请求"; }); router.get("/getData",ctx=>{ let data = [ {name:"张三",age:20}, {name:"李四",age:21}, {name:"王五",age:22} ] ctx.body = data; }); { // 另一种带参方式 router.get("/get/:id", ctx=>{ // console.log(ctx.params);//{id:'520}; console.log(ctx.params.id);//520 ctx.body = "get请求"; }) // http://localhost:8989/get/520 // 通过这种方式是一个必选参 } router.all("/alltest",ctx=>{ ctx.body = "all请求"; // 这样全部请求都可以,但是all相对不安全 }); } app.use(router.routes()); app.listen(8989); } }
koa-views
我们使用了router区分路由,然后去做对应的页面呈现(加载页面),所以这里介绍一下views模块
安装koa-views
npm i views -S
因为views需要一个模板引擎 -> 这里使用pug
安装pug
npm i pug -S
views和pug的安装及了解
koaview/index.js
const Koa = require("koa");
const Router = require("koa-router");
const views = require("koa-views");
{
let app = new Koa();
let router = new Router();
// node预定义变量:
// console.log(__dirname);//指向当前执行文件的文件夹;koaview
// console.log(__filename);//指向当前执行文件;koaview\index.js
app.use(views(__dirname+"/views"),{
extension:"pug"
});
/*
views
参数:
1.绝对地址
2.模板引擎
*/
router.get("/get",ctx=>{
ctx.body = "some thing..";
});
app.use(router.routes());
app.listen(8787);
}
static是一个处理静态文件的模板,所以也是需要安装的
npm i koa-static -S
路由的呈现和static
const static = require("koa-static");
{ // 路由的呈现和static let app = new Koa(); let router = new Router(); { // 很多情况下.是有很多外链文件的,但是node只是读取了pug文件,没有读取a.js,所以没读取的文件是加载不出来的 // 这里使用static模板 app.use(static(__dirname+"/static")); //需要use,需要指定目录,然后他就会把static底下的所有文件都读取一边,也就是通过服务器进行读取 // http://localhost:8787/a.js 因为他会读取他下面所有文件,所以a.js这样就可以访问; } app.use(views(__dirname+"/views"),{ extension:"pug" }); router.get("/get",async ctx=>{ // ctx.body = "some thing.."; await ctx.render("index.pug",{ // 这里可以输出变量... -> 推送数据 myname:"张三" }); // render()方式,加载pug模板 // render是异步,需要 async和await,也就是加载模板是一个异步过程 }); app.use(router.routes()); app.listen(8787); }
使用Koa包含上面内容实现新闻
文件部署:static(静态文件)、views(pug文件)
基本搭建
koanews/index.js
const Koa = require("koa");
const static = require("koa-static");
const Router = require("koa-router");
const views = require("koa-views");
{
let app = new Koa();
let router = new Router();
app.use(views(__dirname+"/views"),{
extension:"pug"
});
app.use(static(__dirname+"/static"));
router.get("/",ctx=>{
ctx.body = "hello";
})
app.use(router.routes());
app.listen(3000);
}
数据呈现到pug中
index.js
{ let app = new Koa(); let router = new Router(); app.use(views(__dirname+"/views"),{ extension:"pug" }); app.use(static(__dirname+"/static")); router.get("/",ctx=>{ ctx.redirect("/news"); // 实现/和news都是跳转到news中 }) router.get("/news",async ctx=>{ // ctx.body = "news"; await ctx.render("index.pug",{ newsData }); // 直接把newsData推到pug文件中 }) app.use(router.routes()); app.listen(4000); }
views/index.pug
doctype html html(lang='en') head meta(charset='UTF-8') meta(name='viewport', content='width=device-width, initial-scale=1.0') meta(http-equiv='X-UA-Compatible', content='ie=edge') title 文章信息展示 style. body { margin: 0; } ul { margin: 0; padding: 0; list-style: none; } a { text-decoration: none; color: #404040; } .wrap{ width: 600px; margin: 0 auto; } .news-list { width: 600px; } .news { width: 100%; display: flex; justify-content: space-between; padding: 15px 0; border-bottom: 1px solid #999; } .info { display: flex; width: 170px; justify-content: space-between; font-size: 12px; color: #888; } .tips { display: flex; width: 100px; justify-content: space-between; } .news-list li:nth-child(5) { border-bottom: none; } .pagination{ display: flex; width: 210px; text-align: center; background-color: rgb(252, 238, 238); border-radius: 25px; overflow: hidden; margin: 0 auto; justify-content: center; } .pagination a{ width: 30px; line-height: 30px; color: #404040; } .pagination a:nth-child(1) { transform: rotate(-45deg) ; } .next { transform: rotate(45deg) ; } .pagination a:hover{ color: rgb(247, 73, 73); } .news div{ width:420px; } body .wrap ul.news-list each val in newsData li.news a(href='javascript:;') img(src=val.imgUrl, alt='') //- ()里面表达式都不需要直接写值 div h3 a(href='javascript:;') #{val.title} .info span.tips span #{val.from} // <span class="line"></span> span.time | #{val.newTime} .pagination a.prev(href='javascript:;') ⌜ a(href='javascript:;') 1 a(href='javascript:;') 2 a(href='javascript:;') 3 a(href='javascript:;') 4 a(href='javascript:;') 5 a.next(href='javascript:;') ⌝
分页的实现
index.js
{ let app = new Koa(); let router = new Router(); app.use(views(__dirname+"/views"),{ extension:"pug" }); app.use(static(__dirname+"/static")); router.get("/",ctx=>{ ctx.redirect("/news"); }) router.get("/news",async ctx=>{ // 分页 let p = 1; let perPage = 5; let renderData = JSON.parse(JSON.stringify(newsData)).splice((p-1)*perPage,perPage);//深拷贝数据.数据切割 await ctx.render("index.pug",{ renderData }); }) app.use(router.routes()); app.listen(4000); }
pug文件修改一下穿进去的名称
完善分页和内容页
index.js
{ let app = new Koa(); let router = new Router(); app.use(views(__dirname+"/views"),{ extension:"pug" }); app.use(static(__dirname+"/static")); router.get("/",ctx=>{ ctx.redirect("/news"); }) router.get("/news",async ctx=>{ // console.log(ctx.request.query.p); // 分页 let p = ctx.request.query.p||1; let perPage = 5; let renderData = JSON.parse(JSON.stringify(newsData)).splice((p-1)*perPage,perPage);//深拷贝数据.数据切割 let fp = Math.ceil(newsData.length/perPage); await ctx.render("index.pug",{ renderData, fp, p }); }) router.get("/detail",async ctx=>{ let id = parseInt(ctx.request.query.d) || 1; let detailData = JSON.parse(JSON.stringify(newsData)).filter(item=>item.id===id)[0]; await ctx.render("detail.pug",{ detailData }) }) app.use(router.routes()); app.listen(4000); }
index.pug
doctype html html(lang='en') head meta(charset='UTF-8') meta(name='viewport', content='width=device-width, initial-scale=1.0') meta(http-equiv='X-UA-Compatible', content='ie=edge') title 文章信息展示 style. body { margin: 0; } ul { margin: 0; padding: 0; list-style: none; } a { text-decoration: none; color: #404040; } .wrap{ width: 600px; margin: 0 auto; } .news-list { width: 600px; } .news { width: 100%; display: flex; justify-content: space-between; padding: 15px 0; border-bottom: 1px solid #999; } .info { display: flex; width: 170px; justify-content: space-between; font-size: 12px; color: #888; } .tips { display: flex; width: 100px; justify-content: space-between; } .news-list li:nth-child(5) { border-bottom: none; } .pagination{ display: flex; width: 210px; text-align: center; background-color: rgb(252, 238, 238); border-radius: 25px; overflow: hidden; margin: 0 auto; justify-content: center; } .pagination a{ width: 30px; line-height: 30px; color: #404040; } .pagination a:nth-child(1) { transform: rotate(-45deg) ; } .next { transform: rotate(45deg) ; } .pagination a:hover{ color: rgb(247, 73, 73); } .news div{ width:420px; } body .wrap ul.news-list each val in renderData li.news a(href='javascript:;') img(src=val.imgUrl, alt='') //- ()里面表达式都不需要直接写值 div h3 a(href='/detail?d='+val.id) #{val.title} .info span.tips span #{val.from} // <span class="line"></span> span.time | #{val.newTime} .pagination a.prev(href='/news?p='+(p<2?1:p-1)) ⌜ - for(let i = 1;i<=fp;i++) a(href='/news?p='+i) #{i} a.next(href='/news?p='+(p>=fp?fp:p+1)) ⌝
detail.pug
doctype html html(lang='en') head meta(charset='UTF-8') meta(name='viewport', content='width=device-width, initial-scale=1.0') meta(http-equiv='X-UA-Compatible', content='ie=edge') title Document style. body{ cursor:url(https://diygod.me/images/cursor.ico),auto; } .text{ width: 640px; margin: 0 auto; } .article-info{ color:#999; font-size: 14px; } p{ font-size: 16px; line-height: 30px; } body .text h1.title #{detailData.title} .article-info #{detailData.from} 时间:#{detailData.newTime} img(src=detailData.imgUrl) p.content | #{detailData.title}
本节总结
Koa的讲解 通过koa构建服务器 koa的ctx ctx的别名 koa的中间件 next的讲解 Koa状态码输出和构建头部 Koa路由->koa-router router的请求方式 get获取地址栏信息 koa-views模块(加载页面) 与pug的配合 koa-static(处理静态文件)
成功一定有方法,失败一定有原因

浙公网安备 33010602011771号