点击蓝字关注,创智助你长姿势

Node.js 是一个 Javascript 运行环境 (runtime),发布于 2009 年 5 月,由 Ryan Dahl 开发。实际上它是对 Google V8 引擎进行了封装。V8 引擎执行 Javascript 的速度非常快,性能非常好。Node.js 对一些特殊用例进行了优化,提供了替代的 API ,使得 V8 在非浏览器环境下运行得更好。

Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js  使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。

一、引例描述

一个展示主流宠物和宠物新闻的首页,不仅新闻内容要不时地更新,主流宠物也要跟着潮流更新,所以数据要不时地进行更换。

通过本次项目学习使用 Node.Js 开发属于自己的服务器,本次项目将实现一个通过接收用户的请求后,后台查询数据库,获得数据后,通过这些数据渲染一个主流的宠物页面呈现给用户,用户点击页面中的新闻列表可跳转到对应的新闻页面,如果需要更换宠物和新闻页面的内容,只需让数据库管理员更换数据库内容即可。

二、 任务陈述

三、 项目结构

使用 js 文件书写后台程序,libs 用来放置自己编写的模块, template 用来放置用户请求后返回的页面,www 用来放置 HTML、jade、ejs 渲染页面时使用到的外部样式文件或者图片素材。

四、知识准备

本次项目能实现的效果为:当用户请求一个页面时,后台服务器接收到请求后,查询数据库得到数据,用这些数据作为 ejs 文件的内容并渲染成 HTML 页面返回给用户。

由以上分析可知需求:

1. 搭建属于自己的服务器

2. 服务器接受到请求后查询数据库

3. 使用数据作为 ejs 页面的内容并渲染成 HTML 页面返回给用户

4.1 搭建 Node.Js 服务器

使用模块搭建服务器,这有助于快速开发,许多功能的实现不需要全部由自己编写,Node.Js 提供许多开源的模块,只需下载后,在程序中声明一下即可使用,例如通过 const express=require('express') ,即引入了 express 模块。

选择使用 express 网络框架搭建服务器,下载并在程序中声明后,指定在6868端口监听:

const express=require('express');//express 网络框架var server=express();server.listen(6868);   //在6868端口监听用户的请求

当服务器接收到用户的请求后,返回一个页面,一般来说,没有纯静态的页面,往往页面的某块信息要发生变化,例如新闻的更新。选择使用模板引擎后台渲染页面,好处就是页面是可以变化的,根据程序的数据生成。

Jade 和 ejs 是两大主流模板引擎,jade书写起来简洁方便,但不能与其他 HTML、CSS 共存,ejs 的书写跟普通的 HTML 页面书写结构一样,因此也能跟 HTML、CSS 共存,各有各的好处,根据实际开发的需求选择合适的模板引擎。这里选择 ejs 模板引擎为例进行开发,引入模板引擎,并配置模板引擎,配置模板引擎需告诉程序输出什么东西,模板文件放在哪里以及使用哪种模板引擎:

const consolidate=require('consolidate');//模块引擎合并库//配置模板引擎//输出什么东西server.set('view engine','html');//模板文件放在哪儿server.set('views','./template');//哪种模板引擎server.engine('html',consolidate.ejs);

最后编写接收用户的请求,并返回的结果:

server.get('/',(req,res)=>{  //接收 url  为 localhost:6868 的请求//执行操作   if(err){     //提示错误信息    }else{     //返回页面    }});

在开发中往往服务器在接收到用户的请求后,需要处理的步骤是比较多的,所有的步骤都写在一起,出错了往往比较难找,使用链式操作,处理完第一个步骤后,传递给下一级处理,下一级继承上一级的处理结果,例如上一级创建了一个字符串变量,下一级就能使用此变量,这样有便于调错,也使得程序的可读性增加了许多,改写 get 请求:

server.get('/',(req,res,next)=>{  //接收请求的 url 为 localhost:6868//执行操作   if(err){     //提示错误信息    }else{     next();  //链式操作,传递给下一级处理    }});server.get('/',(req,res)=>{  //接收请求的 url 为 localhost:6868//执行操作   if(err){     //提示错误信息    }else{     //返回页面    }});

以上代码只传递了一次,根据步骤的复杂性,决定使用的传递次数,只需要在第二个操作中继续使用 next() 传递到下一级,直到最后一步操作即可。

4.2 查询数据库返回数据

在 node.js 中连接数据库需要引入 mysql 模块,接着连接数据库,使用数据库连接池能提高安全性以及连接效率:

const mysql=require('mysql');//一个用于 mysql 的 node.js 程序。它是用 JavaScript 编写的,不需要编译

//使用数据库连接池,连接名为 blog 的数据库

const db=mysql.createPool({host:'localhost',user:'root',password:'',database:'blog'});

连接数据库后便可以对数据库进行增删改查的操作,使用的语句与 mysql 完全一样,例如 select * from banner_table 即表示查询表名为 banner_table 所有数据。在 node.js 中执行查询数据库并返回结果:

server.get('/',(req,res,next)=>{  //查询 banner 的东西  db.query("select * from banner_table",(err,data)=>{    if(err){      res.status(500).send('database error').end(); //提示数据库操作出错    }else{      res.banners=data;  //将查询到的结果赋值给 banners     next();  //链式操作,传递给下一级处理    }  });});

query 用来执行语句,拥有两个参数,第一个参数代表要执行的语句,第二个参数为一个函数,代表执行完语句后的一个反馈行为,这个反馈行为也有两个参数,如果出错,提示错误信息 err ,如果成功,获取查询到的结果 data 。

4.3 使用数据渲染页面

使用数据渲染页面,即一个页面的内容并不是在 ejs 文件中写好的,是非静态的,由数据来决定。

例如:想在一个页面中显示一张图片,如果按照平时的做法,会直接使用 插入一张图片,但如果想要用数据决定显示哪张图片,就需要对 src 属性值的书写做一些改变:,此种写法,是将 src 属性值赋值为服务器返回来的一个 json 中的 src 值,而这个值是从数据库查询得来的,所以事先也需要在数据库中存放好该值,而该值一般存放的是图片的路径名,数据库一般不存放图片文件,那样子很耗费资源并且效率低,存放图片路径名,渲染页面时,程序会根据路径名去相应的文件夹找到图片并渲染。

4.4 完成宠物首页和新闻页面的响应

宠物首页的结构为:左部摆放 4 个主流宠物的相框,相框内包含对宠物的说明和图片。右部是关于宠物的一个新闻列表。

左部内容存放于 banner_table 表中,右部内容存放于 artide_table 表中。

4.5.1 创建数据库

创建一个名为 blog 的数据库,在此数据库中创建两个表,分别为 banner_table 和 artide_table ,表 banner_table 用来存放主流的宠物信息,结构为:

ID 代表第几条数据,title 代表标题,sub_title 代表宠物的描述,src 代表宠物图片。

表 artide_table 来存放新闻结构的信息,结构为:

ID 代表第几条数据,author 代表作者,zuthor_src 代表作者的图像,title 代表标题,post_time 代表发布时间,content 代表新闻内容,summary 代表新闻内容的摘要,n_like 代表新闻被点赞的次数。

4.5.2 搭建 Node.js 服务器

引入搭建此次项目服务器需要的模块:

const express=require('express');//express 网络框架const static=require('express-static');//提供静态文件const consolidate=require('consolidate');//模块引擎合并库const mysql=require('mysql');//一个用于 mysql 的 node.js 驱动程序。它是用 JavaScript 编写的,不需要编译const common=require('./libs/common'); //使用自己写的模块

指定监听的端口号为 6868:

var server=express();server.listen(6868);   //在6868端口监听用户的请求

配置模板引擎,指定输出的东西为 HTML 文件,模板文件放在 /template 目录下,使用 ejs 模板引擎:

//配置模板引擎//输出什么东西server.set('view engine','html');//模板文件放在哪儿server.set('views','./template');//哪种模板引擎server.engine('html',consolidate.ejs);

使用数据库连接池,连接名称为 blog 的数据库:

//使用数据库连接池,连接名为 blog 的数据库const db=mysql.createPool({host:'localhost',user:'root',password:'',database:'blog'});

接收用户请求并使用链式操作:

//接收用户请求server.get('/',(req,res,next)=>{  //查询 banner 的东西  db.query("select * from banner_table",(err,data)=>{    if(err){      res.status(500).send('database error').end();    }else{      res.banners=data;    next();  //链式操作,传递给下一级处理    }  });});server.get('/',(req,res,next)=>{  //查询文章列表  db.query('select ID,title,summary from article_table',(err,data)=>{    if(err){      res.status(500).send('database error').end();    }else{      res.articles=data;      next();  //链式操作,传递给下一级处理    }  });});server.get('/',(req,res)=>{  //返回一个页面,并附带两个 Json  res.render('index.ejs',{banners:res.banners,articles:res.articles});});

以上为查询 banner_table 表的所有数据和 article_table 表的部分数据返回给 ejs 模板引擎渲染宠物首页的左部,其中查询 article_table 表的 ID 是有特殊用意的,ID 指代特定的新闻,一同携带给 ejs 文件,ejs 文件便可以通过 a 标签发送请求指定 ID 号的新闻页面:

//将新闻主题变成一个链接,发送链接地址的时候携带一个新闻的 ID 编号

点击跳转到指定新闻页面的页面效果大致为:

页面效果为在手机端访问时的效果,新闻内容比较多,折叠了起来。可以发现底部有一个点赞数,用户阅读新闻的时候,如果喜欢此篇新闻,可以为此新闻点赞,每次点击,点赞数就会增加1,原理与以上访问指定新闻页面一样,点击的时候多携带一个 act 参数,告诉程序用户点赞了一次:

"/article?id=&act=like">

程序就会修改数据库中指定新闻的 n_lick 字段数值,并重新渲染,sql 语句格式如下:

db.query('update article_table set n_like=n_like+1 where ID='+req.query.id,(err,data)=>{});

新闻页面中有两个地方是需要进行转化的,一个是日期时间,后台存放的日期一般都是以秒为单位的时间戳,所以取出来的时候需要做一下转换,转换的程序可以写成一个模块,这又回顾到以上说的下载模块并在程序中引入模块,即可使用模块的功能,不管是下载别人的模块还是自己写的模块,都是一样的使用原理,而自己写的模块就不需要下载了,告诉程序去哪里找这块模块即可,本项目有一个 libs 文件夹,即是存放自己编写的模块,例如日期格式的转化写成一个 js 文件 common,并将它放置在 libs 中,在程序中引入:

const common=require('./libs/common'); //使用自己写的模块

日期转化模块中的代码如下:

function toDou(n){          //月和日如果小于10,在前面加个0  return n<10?'0'+n:''+n;}module.exports={  //输出模块  time2date:function(timestamp){  //执行日期转化并返回结果    var oDate=new Date();    oDate.setTime(timestamp*1000);    return oDate.getFullYear()+'-'+toDou(oDate.getMonth()+1)+'-'+toDou(oDate.getDate())+' '+toDou(oDate.getHours())+":"+toDou(oDate.getMinutes())+':'+toDou(oDate.getSeconds());  }};

调用的时候只需要传入一个日期数据:

common.time2date(articleData.post_time);

第二个是新闻内容的转换,新闻内容都是会分段的,假如直接输出,内容会全部堆在一起,那么就得想办法让程序遇到段首符时转换成一个

标签,遇到段尾符时转换成一个

,这样就可以让每个段落放置在一个块级元素中,便可达到分段的效果,但是程序执行下来可以发现,新闻内容依然全部堆在一起,并且每个段落的首尾确实也分别加上了

便签,

被程序当做内容输出了,其实这并不奇怪,思考一下,如果程序遇到标签就解析,那么是一件多么可怕的事情,加入一个不良用户在发送一封邮件的时候,在邮件中加入一段无关的内容

 //=改为-,告诉程序解析数据中携带的标签

以下是服务器查询 article_table 表的内容返回给 ejs 模板引擎的完整代码:

server.get('/article',(req,res)=>{  if(req.query.id){    if(req.query.act=='like'){      //增加一个赞      db.query('update article_table set n_like=n_like+1 where ID='+req.query.id,(err,data)=>{ //修改 article_table 表中的 n_like 值        if(err){          res.status(500).send('数据库有小问题').end();          console.error(err);        }else{          //显示文章          db.query('select * from article_table where ID='+req.query.id,(err,data)=>{              if(err){                res.status(500).send('数据有问题:'+ err ).end();              }else{                if(data.length==0){          res.status(404).send('您请求的文章找不到').end();          }else{          var articleData=data[0];          articleData.sDate=common.time2date(articleData.post_time); //转换日期格式          articleData.content=articleData.content.replace(/^/gm,'

'

).replace(/$/gm,''); //使段落能够分段 res.render('conText.ejs',{ article_data:articleData }); } }            });        } }); }else{ //显示文章 db.query('select * from article_table where ID='+req.query.id,(err,data)=>{ if(err){ res.status(500).send('数据有问题:'+err).end(); }else{ if(data.length==0){res.status(404).send('您请求的文章找不到').end();}else{var articleData=data[0];articleData.sDate=common.time2date(articleData.post_time); //转换日期格式articleData.content=articleData.content.replace(/^/gm,'

'

).replace(/$/gm,''); //使段落能够分段 res.render('conText.ejs',{ //返回一个 ejs 文件和一个 json article_data:articleData });} } });}}else{ res.status(404).send('您请求的文章找不到').end();}});

并在最后告诉程序静态数据存放在哪个文件夹,使用静态数据:

//static 数据server.use(static('./www'));

4.5.3 创建宠物首页以及新闻页面

宠物页面和新闻页面的布局可根据自身的喜好进行编写。宠物首页和新闻页面的内容需要书写成以数据来填充的形式,通过遍历 json 获取所有左部的内容:

for(   
<%= banners[i].title %> //获取标题 <p>图片描述:<%= banners[i].sub_title %>p> //获取图片描述 <img src=""> //获取图片 div> <% } %>

遍历 json 获取右部的内容:

for("/article?id=">

<%= articles[i].summary %>p><br/> //获取新闻摘要

      

a 标签中的 href 属性,?后面的内容为携带的请求参数,携带一个 ID 参数的目的是让服务器能够辨析用户点击了哪篇新闻,并返回相应的新闻页面。

新闻页面的内容:

<p>作者:<%=article_data.author%>p> //获取作者名称<p>图片:<img src="" width=250px height=200px alt=""/>p>  //获取作者图像<p>主题:<%=article_data.title%>p>  //获取新闻主题<p>时间:<%=article_data.sDate%>p> //获取发布日期<p>文章:<%-article_data.content%>p> //获取新闻内容<p>点赞数:<a href="/article?id=&act=like"><img src="data:images/zan.png" />a><%=article_data.n_like%>p> //获取点赞次数

a 标签中的 href 属性,?后面的 id 为携带的请求参数,携带一个 ID 参数的目的是让服务器能够辨析用户点击了哪篇新闻,act 告诉服务器用户对该新闻进行了一次点赞。

4.5.4 不同请求的处理流程图

当用户请求的 url 为 http://localhost:6868  时,程序的处理流程图为:

当用户请求的 url 为 http://localhost:6868/article?id=1 时 ,程序的处理流程图为:

当用户请求的 url 为 http://localhost:6868/article?id=1&act=like  时,程序的处理流程图为:

4.6 与用户互动

一个点赞功能难以让用户感受到与页面的互动性,一般用户在阅读一篇新闻的时候,喜欢评论一下,发表自己的意见。接收用户发送的数据,可使用 post 请求,在新闻页面的底部增加一个历史评论区域,利用 post 请求收集所有用户对新闻的评论。

先准备一个 comment_table 表来存放所有用户的评论:

其中 ID 是编号,comment 代表用户的评论。

服务器解析 post 数据,需要下载 body-parser 模块,并在程序中声明一下:

// bodyParser.urlencoded 解析 form 表单提交的数据server.use(bodyParser.urlencoded({extended: false}));// bodyParser.json 解析 json 数据格式server.use(bodyParser.json());

编写接收 post 请求,当接收到 post 请求时,向表中插入评论语句:

server.post('/add_comment',function(req,res){ //post 请求  let stmt = `INSERT INTO comment_table(comment) //准备 sql 预执行语句     VALUES(?)`;  db.query(stmt,req.body.text,(err,data)=>{ //执行 sql 语句,req.body.text 代表用户的评论,也即是 sql 语句中的?值    if(err){      res.status(500).send('database error').end();    }else{      console.log ("数据增加到 comment_table 表成功");    }  });

新闻页面中,实现用户提交评论后页面的历史评论就追加自己发表的评论,同时在 comment_table 表中增加该评论信息。本过程的原理是当用户点击提交按钮时,程序通过 js 语句获取输入框中的评论语句,将该语句追加到历史评论区域,同时发送 post 请求,使服务器将该评论语增加到 conment_table 表中,以便其他用户看到自己的评论。此做法将这两个过程单独执行,是为了页面在用户发表了评论后不需要重新发送一个页面过来达到刷新的效果,并且页面不会闪一下,减少性能损耗和增加用户体验感。

但因为此种做法是独立执行的,容易造成一种错误,新闻页面发送评论后,评论语增加到历史评论尾部,但服务器执行向 comment_table 表中插入该评论语时出错了,插入失败,其他用户登录此新闻页面时看不到此评论语,这就会造成数据不一致的错误,所以需要让这两个单独开来执行的过程最后要有一个交流,确保数据已经添加到数据库了才显示出评论语,始终以数据库的数据为标准来渲染页面,所以 post 请求中要做修改,在执行完成数据库操作后,成功与否,都要有一个反馈信息,反馈一个 json 即可,响应给新闻页面的 post 请求,让新闻页面知道评论语是否已经添加到数据库中,服务器的 post 请求修改如下:

server.post('/add_comment',function(req,res){ //post请求  let stmt = `INSERT INTO comment_table(comment) //准备sql预执行语句     VALUES(?)`;  db.query(stmt,req.body.text,(err,data)=>{ //执行 sql 语句,req.body.text代表用户的评论,也即是 sql 语句中的?值    if(err){      res.status(500).send('database error').end();      res.send('{"ok":false,"msg":"数据增加到 comment_table 表失败"}');//发送一个 json 作为反馈信息    }else{      console.log("数据增加到 comment_table 表成功");      res.send('{"ok":true,"msg":"数据增加到 comment_table 表成功"}'); //发送一个 json 作为反馈信息    }  });

在新闻页面上编写一个展示历史评论的区域,可用表格形式等方式展示,并且用户可以提交评论,提交后利用 jq 中的 ajax 发送 post 请求到服务器,如果数据库操作成功,则将评论语追加到历史评论尾部,post 请求代码如下:

var comment_text=document.getElementById('in_text').value; //获取输入框中的评论语var comment={ //创建一个 json      text:comment_text    }    $.post('http://localhost:6868/add_comment',comment,function(data, status){// post 请求,携带一个 json      var json=eval('('+data+')');  //取得服务器反馈的 json//如果评论语已经插入comment_table 表中,将评论语追加到历史评论尾部      if(json.ok){          var tr=table.insertRow();        for(var i=0;i<1;i++){          var tr=table.insertRow();          if(i==0){            var td=tr.insertCell(i);            td.innerText=comment_text;          }      }      }});

大致效果如下图所示:

五、 总结

本次项目介绍了 Node.Js 服务器,并通过例子讲解了如何使用 express 框架搭建自己的一个服务器,并接收用户的请求、连接数据库返回数据、以数据动态渲染页面返回给用户。

 

创智俱乐部

微信:sziitlSA

一个让你涨姿势的社团

长按二维码关注

83998 连接服务器出错_新生福利 | 使用 Node.Js 开发服务器相关推荐

  1. php和nodejs和java的区别_讨论PHP和Node.js开发之间有什么区别

    在很长一段时刻内,决定在Node.js和PHP之间进行挑选是一件很费事的工作,但关于后端程序员来说,这一直很费事,但它从未影响过开发人员.可是工作很快就发生了改变,现在开发人员有必要挑选其中之一.No ...

  2. Node.js(一)——(Node.js安装及使用,通过Node.js搭建服务器,模块化及自定义模块,npm/yarn/nvm,内置模块fs的使用,buffer及stream,新闻列表案例)

    目录 1.Node.js介绍 2.安装Node.js 3.使用Node.js实现第一个服务器 3.1初步感受Node.js 3.2Google Chrome 默认非安全端口列表,尽量避免以下端口. 3 ...

  3. node.js 静态属性_如何使用静态站点和Node.js开发和部署第一个全栈式Web应用程序

    node.js 静态属性 This tutorial will show you how to convert a static website that uses HTML, CSS and Jav ...

  4. 深入浅出Node.js游戏服务器开发--分布式聊天服务器搭建

    From: http://www.infoq.com/cn/articles/game-server-development-2?utm_source=infoq&utm_medium=rel ...

  5. Node.js 连接 MySQL 并进行数据库操作 –node.js 开发指南

    Node.js是一套用来编写高性能网络服务器的JavaScript工具包 通常在NodeJS开发中我们经常涉及到操作数据库,尤其是 MySQL ,作为应用最为广泛的开源数据库则成为我们的首选,本篇就来 ...

  6. dax圣经 翻新_使用翻新和Node JS的Android图像上传

    dax圣经 翻新 In this tutorial, we'll be creating an android application that uploads an image using Retr ...

  7. 【Node】node.js实现服务器的反向代理,解决跨域问题

    跨域对于前端来说是一个老大难的问题,许多方法如jsonp.document.domain + iframe...都有或多或少的问题,一个最佳实践就是通过服务器nginx做反向代理,但奈何不懂相关知识, ...

  8. nodec mysql_Node.js 连接 MySQL 并进行数据库操作 –node.js 开发指南

    Node.js是一套用来编写高性能网络服务器的JavaScript工具包 通常在NodeJS开发中我们经常涉及到操作数据库,尤其是 MySQL ,作为应用最为广泛的开源数据库则成为我们的首选,本篇就来 ...

  9. js提交出现post错误_阿里云的 Node.js 稳定性实践

    整理人:前端自习课 前言 如果你看过 2018 Node.js 的用户报告,你会发现 Node.js 的使用有了进一步的增长,同时也出现了一些新的趋势. Node.js 的开发者更多的开始使用容器并积 ...

最新文章

  1. 近世代数--整环上的整除理论--主理想整环中最大公因子的存在表示定理
  2. 微型计算机广告牌实验报告,微型计算机实验报告1资料.doc
  3. php无法创建cookie,php-curl cookie无法成功创建
  4. NOI2019凉凉记
  5. linux卸载splunk,linux安装splunk-enterprise
  6. mel加载一个物体不同姿态的模型实现动画效果
  7. 一道不知道哪里来的容斥题
  8. iOS charles 抓包使用
  9. Raspberry Pi (树莓派)折腾记之一
  10. Steam游戏存档位置大全
  11. 微信小程序不支持使用本地图片设置背景图片解决方法
  12. Windows XP将显示桌面图标放到任务栏
  13. 每天15分钟中度或快步走以燃烧100卡路里的热量
  14. 英伟达RTX 4070最新测评来了!光追效果更棒,但仅限于2k游戏
  15. 期刊投稿状态_SCI期刊投稿各种状态详解
  16. Python线程超时自动终止 | Python利用ThreadPoolExecutor实现对多线程的超时自动终止 | Python3实现单线程超时自动强制停止
  17. Git -- submoudule子模块使用
  18. 量化对冲:智能对冲策略解析
  19. uniapp 弹窗确认操作
  20. Matplotlib之饼图、直方图、极坐标图、散点图

热门文章

  1. 【LeetCode】LC1672. 最富有客户的资产总量
  2. 我把序列化玩成了这样,吊锤了一波面试官
  3. 注意了,Fastjson 最新高危漏洞来袭!
  4. numpy学习3:对象属性和基本数据类型
  5. java网络编程(一)
  6. 【struts2+hibernate+spring项目实战】java读写实现代码生成器(ssh)
  7. Java 类中各成分加载顺序和内存中的存放位置
  8. Java IO流之转换流
  9. java数组可扩展_[转载]Java数组扩容算法及Java对它的应用
  10. python矩阵_Python 矩阵相关