当前位置: 首页 > news >正文

使用node.js创建一个todo列表——node.js服务器搭建以及json读写

使用node.js创建一个todo列表——node.js服务器搭建以及json读写

前言

前些日子学习了 nodejs ,顺理成章的想要找点东西练练手。恰好最近需要一个简介的todo list网页,因为之前用的一些todo list应用都被墙了,访问速度感人,于是就想自己搞一个todo。

而恰好,我要搞的todo是一个非常简单的程序,非常适合用nodejs这样的解释型脚本语言来实现,比起java这样光搭框架就要半天的专注大型项目的语言,nodejs 的优势就是非明显了。

nodejs搭建服务器只需要去nodejs官网复制这样的一块代码:

const express = require('express')
const app = express()

app.get('/', (req, res) => {
  res.send('Hi!')
})

app.listen(3000, () => console.log('Server ready'))

所以我们可以把更多的经历放在我们的业务实现上。

准备工作

需求分析

在开始动工之前,要先列出需求。当时我的需求如下:

  • 添加需要做的事情

  • 展示需要做的事情

  • 完成需要做的事情

  • 查看曾经完成的事情

解决方案分析

对于上述需求,我打算这样实现:

  • 使用json文件系统实现持久化操作

  • 使用nodejs来获取数据

  • 使用nodejs来处理数据并储存数据

这样的话。明确了需求与实现方式,我们就可以动工了。

实现

json读写

nodejs提供了一套文件的读写的模块:fs。使用fs可以很简单的实现文本文件的读写。但是为了我们在开发的时候更加方便,我又将他封装成了一个专注json读写的方法:

//in jsonHandler.js
const fs = require("fs");

function test(){
    console.log("Hello world!")
}
function readJson(name){
    let jsonFile = fs.readFileSync("./"+name);
    let toDoList = JSON.parse(jsonFile);    //解析json,并直接返回json对象
    return toDoList;
}
function writeJson(name,data){
    fs.writeFileSync("./"+name,JSON.stringify(data));
}

module.exports = {
    readJson,
    writeJson
}

服务器搭建

还记得我们的服务器的格式吗?我们先定义一下我们的服务器接口:

//in index.js
const jh = require("./jsonHandler")
const http = require('http');
const url = require('url');
const util = require('util');

let workList = jh.readJson("list.json");

const port = 3000

const server = http.createServer((req, res) => {
    //设置响应头与请求编码
    res.statusCode = 200
    //设置字节流编码为utf-8
    res.setHeader('Content-Type', 'text/plain;charset=utf8')
    //设置允许跨域
    res.setHeader('Access-Control-Allow-Origin','*')
    req.setEncoding('utf8');

    //write your code here

    let ret="";    //这是需要返回的数据
    res.end(ret)
})

server.listen(port, () => {
})

然后根据我们的需求分析,先来确定以下API接口:

  • addWork():用来添加一个任务并将数据保存到json

  • deleteWord(res):用来将一个任务删除并将数据保存到json

  • writeHistory(work):将完成的任务添加到历史记录中

  • getAllWork():获取目前正在运行中的任务

首先我们先开始addWork()的编写,要添加的话,首先要接受并解析客户端发送的请求,然后再储存到json中,代码如下:

//用来增加一个任务
    function addWork(){
        let work="";
        req.on('data', chunk => {    //接受客户端发送的请求流
            //这里要取子串的原因是请求会带key,我这里是work=xxx,所以要将'work='去掉
            work=chunk.toString().substring(5);
            //设置编码,不然中文会乱码
            work = decodeURIComponent(work);
            //解析worklist
            let jsonObj=eval(workList)
            jsonObj.push({"work":work})
            //将数据写回
            jh.writeJson("list.json",jsonObj)
        })
    }

任务历史与添加任务几乎同理,只不过这个方法获取的数据是deleteWork提供的,我们不需要再去监控客户端的请求。代码如下:

//任务历史
    function writeHistory(work) {
        let jsonStr = jh.readJson("history.json");
        let jsonObj = eval(jsonStr);
        let event = {}
        event.work = work;
        event.finishTime =new Date();
        jsonObj.push(event);
        jh.writeJson("history.json",jsonObj)
    }

删除任务也是同理,我们要在前端给每个任务隐性的标上index,然后就可以用来删除了:

//删除任务
    function deleteWork(res){
        req.on('data', chunk => {
            console.log(`可用的数据块: ${chunk}`)
            //获取点击事件的位置
            let index=chunk.toString().substring(3);
            let jsonObj=eval(workList)
            //添加历史
            let work = jsonObj[index].work
            writeHistory(work)
            //删除
            jsonObj.splice(index,1);
            // console.log(jsonObj)
            jh.writeJson("list.json",jsonObj)
        })
    }

获取任务就非常简单了,我们只需要从json中拿到数据并转发给客户端就可以了:

//获取所有任务
    function getAllWork(){
        workList=jh.readJson("list.json");
        ret=JSON.stringify(workList);
    }

最后呢,我们需要写一个路由解析,因为js非常小,所以这些东西都写在一个文件里面就可以:

/解析路由
    let pathname = url.parse(req.url).pathname;
    //路由配置
    if (pathname==="/getAllWork"){
        getAllWork()
    }
    else if(pathname==="/addWork"){
        addWork()
    }
    else if(pathname==="/deleteWork"){
        deleteWork()
    }

随下贴出前端源码:

<-- in index.html -->
<!DOCTYPE html>
<html lang="ch">
<head>
    <link rel="stylesheet" type="text/css" href="https://cdn.simplecss.org/simple.css">
    <link rel="stylesheet" type="text/css" href="technological.css">
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body class="tech-background" style="text-align: center">
    <p> wish you a substantial day</p>
    <p>to do list</p>
    <label>
        <input name="add-work" id="add-work" class="tech-input" style="width: 70%">
        <button onclick='addWork()' class="tech-btn" style="display: inline;">增加</button>
    </label>
    <div id="container"></div>
    <script>
        'use strict'
        $.ajax({
            url:"http://localhost:3000/getAllWork",
            method:"GET",
            crossDomain: true,
            success:(res)=>{
                let con=document.getElementById("container");
                let workList=JSON.parse(res)
                console.log(workList)
                workList=eval(workList)
                for (let i=0;i<workList.length;i++){
                    let list=document.createElement("label");
                    list.innerHTML="<input name=\"work-list\" id=\"work"+i+"\" value='"+workList[i].work+"' class='tech-input' style=\"width: 70%\"> " +
                        "<button οnclick='deleteWork(this)' id='"+i+"' class='tech-btn' style=\"display: inline;\">完成</button>";
                    con.appendChild(list);
                }
            }
        })
        function addWork(){
            let work=document.getElementById("add-work").value;
            $.ajax({
                url: "http://localhost:3000/addWork",
                method: "POST",
                crossDomain: true,
                data:{
                    "work":work
                },
                success:(res)=>{
                    // alert("success");
                    location.reload();
                }
            })
        }
        function deleteWork(res){
            let index=res.id;
            $.ajax({
                url: "http://localhost:3000/deleteWork",
                method: "POST",
                crossDomain: true,
                data:{
                    "id":index
                },
                success:(res)=>{
                    // alert("success");
                    location.reload();
                }
            })
        }
    </script>
</body>
</html>

这个项目的源码我也会放在GitHub(因为项目实在太小我甚至都没有建一个文件夹)

项目地址

保持代码运行

我们服务器代码当然要有一个服务器去运行,而这个迷你的项目显然不值得让我们去分配他一个云服务器加一个域名,那么我们是否可以把这个服务运行在本地呢?

可是运行在本地就需要频繁的重启服务,每次开机都要启动一次,这未免也太麻烦了。于是我们就可以使用pm2来使我们的项目保持运行

关于pm2我在此不多赘述了,大家有兴趣的可以自行百度搜索,我只给出部署的命令:

npm install pm2 -g #全局安装pm2
cd <项目目录>
pm2 start index.js --watch
pm2 save
pm2 list

如果你部署成功,那么当你运行pm2 list之后你就可以看到你的项目了

相关文章:

  • 如何通过经纬度坐标获取附近的地址信息?
  • LNMP+Redis
  • 记一次java组装elementPlus的TreeSelect树形菜单的数据结构
  • springboot基于web模式的师资管理系统的设计与实现毕业设计源码040928
  • 计算机复试面试题总结
  • 计算机二级--java篇
  • Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第10章 Vuex状态管理 10.4 actions的使用
  • 玩转 uniapp 全端开发
  • 【AGC】云存储如何上传文件?是否可以自行开通?云存储的相关问题,来这里看看!
  • 新款FTP替代系统重磅登场!怎样摆脱传统FTP弊端?
  • 页面引擎之velocity的基础入门学习
  • kafka详解(一)--kafka是什么及怎么用
  • 期货开户趋势的本质是惯性
  • 解决在Windows下elasticsearch启动失败,报${ES_TMPDIR}找不到的问题
  • 水一篇挖矿清除记录
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 345-反转字符串中的元音字母
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • input实现文字超出省略号功能
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • Kibana配置logstash,报表一体化
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • vue总结
  • 复杂数据处理
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 前端技术周刊 2019-01-14:客户端存储
  • 实战|智能家居行业移动应用性能分析
  • 物联网链路协议
  • 小程序button引导用户授权
  • 用jquery写贪吃蛇
  • Java数据解析之JSON
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • # 达梦数据库知识点
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (四)linux文件内容查看
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • .net core Swagger 过滤部分Api
  • .NET 依赖注入和配置系统
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • /etc/sudoer文件配置简析
  • ?php echo $logosrc[0];?,如何在一行中显示logo和标题?
  • @ 代码随想录算法训练营第8周(C语言)|Day57(动态规划)
  • [2019.3.20]BZOJ4573 [Zjoi2016]大森林
  • [acm算法学习] 后缀数组SA
  • [C#]科学计数法(scientific notation)显示为正常数字
  • [C++] Boost智能指针——boost::scoped_ptr(使用及原理分析)
  • [Erlang 0129] Erlang 杂记 VI 2014年10月28日
  • [J2ME]如何替换Google Map静态地图自带的Marker
  • [LeetCode]-使用特殊算法的题目-2