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">| &nbsp;&nbsp;${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(处理静态文件)
posted @ 2020-03-11 19:26  JackAfan  阅读(151)  评论(0)    收藏  举报
setTimeout(function(){ let aImg = document.querySelectorAll("img"); aImg.forEach(img=>{ img.alt = "" }) console.log("去除img-alt成功") },1000)