最小学习成本用Node搭建后端

2,514 阅读5分钟

今天要实现的就是个简单的文章列表,然后点开可以看到详情,仅此而已,但是对于使用来说,已经足够了,做博客,做列表等都是非常OK的

涉及到的Node模块:

const http = require('http');//用于搭建服务器
const path = require('path');//用于处理路径
const fs = require('fs');//文件读取模块
const mime = require("./data/mime.json");//文件后缀对照表
const url = require('url');//处理url
const cheerio = require('cheerio');//读取模板后,方便dom操作

其实我觉得很多前端的同学学Node的目的有点偏离轨道了,作为一个脚本语言,JS有天生的缺陷,虽然可以在服务端运行了,但是跟java这种老大哥还是比不了的。那Node还有存在的必要么?这当然是有的了

并不是所有的网站都需要一个复杂的后端作为底部支撑的,就像我以前说并不是所有的网站前端都适合用Vue这种框架一样的。技术的出现一定是为了解决某一些问题,不会也不可能解决所有问题。所以如果只是做一个简单的博客系统之类的,用Node就完全没有问题

今天我的这个案例甚至连数据库都不需要,这就大大减少了大家的学习成本,可以说真正的只要会前端,那么像这样的后端代码真的是毫无压力,不多说了,往下看。

先随便写个服务,做出响应

涉及到的Node模块在上面我已经列出来了,作用也有备注,感觉就不用再多说了,先来看一个最简单的服务器响应要怎么做:

const http = require('http');//用于搭建服务器
const server = http.createServer((req,res)=>{
	res.end('hello gays~');
})
server.listen(8787);

然后把这个js文件用node运行起来就可以了,然后你在本地打开:http://localhost:8787/ 就可以看到返回的'hello gays~'这个内容了。

你问我Node怎么运行js文件?

嗯...
比如上面这段代码叫做index.js
那么打开命令行,然后cd到这个文件夹,然后输入 node index.js就可以了

不过这里介绍一个工具给大家 叫 nodemon
如果直接用node去运行这个文件,那么每次修改都需要在命令行里重新运行一次 用nodemon的话就直接去页面刷新就好了
直接用npm 安装 nodemon到全局就好了: npm install nodemon -g
mac的同学可能需要在前面加上 sudo命令: sudo npm install nodemon -g

然后就可以向上面一样用命令行执行了:nodemon index.js
运行好了之后,页面就在给你Say Hello啦,是不是好简单。

当然了,仅仅只是Say Hello当然是不够的 我们需要让页面呈现出来,那么这就涉及到文件的读取了,我们的文件是放在服务器上的,输入地址之后其实就是在向后端请求,后端需要把服务器里的文件读取,然后返回给客户端,然后展示在页面上,而在Node里,读取文件会用到一个内置模块:fs模块

关于fs模块,后面我再写文章细说,现在大家只需要知道我们可以利用这个模块来读取文件和写入文件就可以了,增删改查一点毛病没有,只不过效率不能和数据库比而已,但是对于简单应用来说,还是那句话,够用就好。

比如我现在的项目结构是这样的:

我现在从项目根目录下的index.js作为项目入口,要去读取src/news/index.html

let resData = fs.readFileSync("./src/news/index.html");
console.log(resData);
//得到结果:<Buffer 3c 21 44 4f 43 54 59 50 45 20 68 74 6d 6c 3e 0a 3c 68 74 6d 6c 20 6c 61 6e 67 3d 22 65 6e 22 3e 0a 0a 3c 68 65 61 64 3e 0a 20 20 20 20 3c 6d 65 74 61 ... 1423 more bytes>
//把结果转换一下
console.log(resData.toString());
//就能得到正确的html结构了,直接把它用res.end方法返回给客户端就可以直接展示了
<!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>title</title>
</head>

<body>
	<div class="newslist"></div>
</body>
</html>

正常情况下,我还需要在页面中渲染数据

仅仅只是读取一下html文件的话 我们都不需要做后端程序,直接读取都可以,所以我们还需要把去读到的文件进行一定的改造,把数据也给加进去,这其实有点ssr的味道了。于是又涉及到一个新的模块:cheerio

这个模块可以让我们在node环境下像jq一样去操作读取到的html文件,这样的话,用我们前端最熟悉的dom操作就可以完成服务端的数据组装过程。

不过这个模块不是node内置模块,需要额外用npm安装一下,这也是我们今天这个案例中唯一用到的非内置模块:
npm install cheerio -S

把上面用fs模块读取到的html扔给cheerio,于是我们就可以这么操作了

  let resData = fs.readFileSync("./src/news/index.html");
  let $ = cheerio.load(resData);
  $('.newslist').html('结合数据之后的结构');
  
  res.end($.html());//最后把内容扔给客户端

一切准备工作... 等等,还差一个东西,正常情况下,我们还需要在页面中引入css,这也是一个请求,怎么处理呢?

这时候又需要用到另外一个模块了:path模块

比如我们在页面中引入css文件: <link href="../../www/css/news.css" rel="stylesheet">;
那么这时候我们的node得到的请求是这样的:http://localhost:8787/www/css/news.css

一般来说,后端响应前端的请求,是需要去设置响应头的 至少这玩意时必须要设置的,那么页面中的请求有css,有js,还有图片,等等其他各种资源,挨个去设置么?这时候咱就可以取巧一下了,只要把请求文件的后缀给提取出来,然后用一个表来对照一下,不同的后缀给不同的content-type不就好了么?

于是:mime.json就诞生了,这玩意很长,点击这里可以看到

有了它之后我们需要先把它引入到项目中,然后做个判断就可以了:

const mime = require("./data/mime.json");//文件后缀对照表
let extName = path.extname(req.url);

const server = http.createServer((req,res)=>{
  /*处理其他文件请求,并根据不同文件格式设置对应的头文件*/
  if(extName && extName != '.ico'){
    res.setHeader("Content-Type", mime[extName]);
    let resData = fs.readFileSync("." + req.url);
    res.end(resData);
  }else{
    res.setHeader("Content-Type", "text/html;charset=utf-8");
  }
});
server.listen(8787);

这里我还做了个保险处理,因为html可以展示大多出文档,所以当没有读取到后缀名的时候,直接就默认设置为html了,另外有时候我们直接输入域名和端口,这时候也是拿不到后面的后缀的,所以算是一举两得。

有了这些之后,我们就可以正式去写项目了

第一步,先准备好静态文件:html,css,img这些,然后对应的数据用json来存就可以了

现在的目录结构:

app   
  -data
    -data.json //这里存数据
  -node_modules //依赖
  -src
    -detail
      -index.html //这里是模板
      -index.js  //处理模板
    -news
      -index.html  //模板
      -index.js  //处理模板
  -www //静态文件
    -css
    -img
  index.js //项目入口

第二步,写入口文件

const http = require('http');//用于搭建服务器
const path = require('path');
const fs = require('fs');//文件读取模块
const mime = require("./data/mime.json");//文件后缀对照表
const url = require('url');//处理url

const server = http.createServer((req,res)=>{

    let objPath = url.parse(req.url,true);
    let pathname = objPath.pathname;//路由数据
    let extName = path.extname(req.url);

    /*处理其他文件请求,并根据不同文件格式设置对应的头文件*/
    if(extName && extName != '.ico'){
        res.setHeader("Content-Type", mime[extName]);
        let resData = fs.readFileSync("." + req.url);
        res.end(resData);
    }else{
        res.setHeader("Content-Type", "text/html;charset=utf-8");
    }

    /*路由*/
    switch (pathname) {
        case '/':
        case '/news':
            const viewNewsData = require('./src/news/index');
            res.end(viewNewsData(objPath));
            break;
        case '/detail':
            const viewDetailData = require('./src/detail/index');
            res.end(viewDetailData(objPath));
            break;
        default:
            break;
    }
});

server.listen(8787);

第三步,分开模块专门来处理页面中的逻辑

这是新闻列表的处理模块:

const fs = require('fs');//文件读取模块
const cheerio = require('cheerio');//读取模板后,方便dom操作
let newsData = require("../../data/data.json");

function viewNewsData(objPath){
    let indexData = fs.readFileSync('./src/news/index.html');//读取内容

    let query = objPath.query;//search信息
    let nowPage = query.page || 1;//当前页
    let pageNum = 6;//每页数据的数量
    let pageCount = Math.ceil(newsData.length/pageNum);//计算出总页数

    let newsHtml = '';
    let paginationHtml = '';


    /*处理内容数据*/
    let nData = JSON.parse(JSON.stringify(newsData)).splice((nowPage-1)*pageNum,pageNum);//复制数据然后根据页数来剪切数据;
    
    nData.forEach((item)=>{
        newsHtml +=`
        <li class="news">
            <a href="/detail?id=${item.id}">
                <img src="${item.imgUrl}" alt="">
            
                <div class="content">
                    <h3 class="title">${item.title}</h3>
                    <div class="info">
                        <span class="tips"><span>${item.from}</span></span>
                        <!-- <span class="line"></span> -->
                        <span class="time">| &nbsp;&nbsp;${item.newTime}</span>
                    </div>
                </div>
            </a>
        </li>
    `;
    });
    /*处理内容数据*/

    let $ = cheerio.load(indexData);
    $('.news-list').html(newsHtml);
    return $.html();
}


module.exports = viewNewsData;

OK 到这里的基本上整个项目就算完事了,后期只需要去编辑JSON文件就可以更新页面中的内容了,是不是感觉还是很简单? 往后hi给大家介绍更多Node模块,这样我们可以做的事情就更多了。

文章中所有的资源在这里都可以下载