node.js从入门到实践

node.js

node.js是前端最流行的javascript运行环境

  • node.js 是一个基于 chrome v8 引擎的 javascript 运行环境
  • node.js 使用了一个事件驱动、非阻塞式 I/O 模型,使其轻量又高效
  • node.js 的管理包 npm 是全球最大的开源库生态系统

node-v8引擎

javascript 引擎

  • 电脑根本不识别也不理解javascript
  • javascript 引擎起到的作用就是让电脑识别js代码

V8引擎

  • node.js是使用c++写的
  • V8引擎是Node.js的核心
  • V8引擎的作用是让JS代码能够让电脑识别

Module & Require

  • 在node.js中,文件和模块是一一对应的(每个文件被视为一个独立的模块)
//app.js文件引入stuff模块并应用
//requrie
let stuff = require('./stuff')
console.log(stuff.counter([1,2,3,4]));
console.log(stuff.adder(1,2));
console.log(stuff.pi);
//stuff模块
var counter = function (arr) {
    return '一共有' + arr.length + '个元素在数组中';
}


var adder = function (a,b) {
    return `您需要计算的两个值的和为:${a+b}`
}

var pi = 3.1415926;
//module
// module.exports.counter = counter;
// module.exports.adder = adder;
// module.exports.pi = pi;

module.exports = {
    counter: counter,
    adder: adder,
    pi: pi
}

事件模块

  • 大多数node.js核心API都是采用惯用的异步事件驱动架构(fs/http)
  • 所有能触发事件的对象都是EventEmitter类的实例
  • 事件流程:引入模块 -> 创建 EventEmitter 对象 -> 注册事件 -> 触发事件
//事件模块
//1.引入事件模块
var events = require('events');//引入系统模块,不要给定义的路径,直接给名字就可以了
//2.创建EventEmitter对象
var myEmitter = new events.EventEmitter();
//3.注册事件
myEmitter.on('someEvent',function (msg) {
    console.log(msg);
})
//4.触发事件
myEmitter.emit('someEvent','实现事件并传递此参数到注册事件的回调函数中')//事件名,参数
console.log(1); //先执行函数中内容,再执行打印1

如果想要函数异步执行,先执行打印1,再执行函数中内容的话可以这样修改

//事件模块
//1.引入事件模块
var events = require('events');//引入系统模块,不要给定义的路径,直接给名字就可以了
//2.创建EventEmitter对象
var myEmitter = new events.EventEmitter();
//3.注册事件
myEmitter.on('someEvent',function (msg) {
    // console.log(msg);

    //异步
    setImmediate(() => {
        console.log(msg);
    })
})
//4.触发事件
myEmitter.emit('someEvent','实现事件并传递此参数到注册事件的回调函数中')//事件名,参数
console.log(1); //先执行函数中内容,再执行打印1

文件系统模块

文件系统主要对项目中的文件进行操作

  • 读取文件(fs.readFile)
  • 写入文件(fs.writeFile)
  • 流程:引入fs模块 -> 调用方法 -> 异常捕获
//文件系统
//1.引入文件系统模块
var fs = require('fs');
//2.通过对象调用方法
//同步读取
var readMe = fs.readFileSync('./readMe.txt','utf8');//路径,文件格式
console.log(readMe);

//异步读取
var readMe2 = fs.readFile('./readMe.txt','utf8',function (err,data) {//路径,文件格式,回调函数(错误信息,返回值)
    if(err) throw err;
    console.log(data);
})
console.log(1);//先打印1,再打印读取信息

//同步写入
fs.writeFileSync('./writeMe.txt', readMe);//文件路径,写入内容

//异步写入
fs.readFile('./readMe.txt','utf8',function (err,data) {
    if(err) throw err;
    fs.writeFile('./writeMe2.txt',data);
})
  • 创建文件夹(fs.mkdir)
  • 删除文件夹(fs.rmdir)
  • 删除文件(fs.unlink)
  • 流程:引入fs模块 -> 调用方法 -> 异常捕获
//文件系统
//1.引入文件系统模块
var fs = require('fs');
//2.通过对象调用方法
// 删除文件
fs.unlink('./writeMe.txt',function (err) {
    if(err) throw err;
    console.log('文件删除成功!');
// });

//创建文件夹 同步
fs.mkdirSync('stuff');

//删除文件夹 同步
fs.rmdirSync('stuff');

//异步创建文件夹
fs.mkdir('stuff',function (err) {
    if(err) throw err;
    fs.readFile('readMe.txt','utf8',function (err2,data) {
        if(err2) throw err2;
        fs.writeFile('./stuff/writeMe.txt',data,function(){});
    })
})

//异步删除文件夹
// 1.删除文件夹中内容
// 2.再删除文件夹
fs.unlink('./stuff/writeMe.txt',function () {
    fs.rmdir('stuff', function (err) {
        if (err) throw err;
        console.log('文件夹删除成功!');
    })
})

注意事项

  • fs.writeFile(file, data[, options], callback)在v10.0之后回调函数为必选参数,否则会报错。
  • fs.writeFileSync(file, data[, options])没有回调函数

http创建服务器

客户端与服务端关系

client为客户端,好比用户看到的浏览器,server为服务器。当用户想要访问一个页面时,内部流程为:浏览器中是输入域名 地址www.baidu.com,该域名会绑定到某个服务器上,因为域名本身是没有数据展示的,绑定服务器后才会请求服务器,服务器才会返回对应的数据展示。所以客户端的作用就是用户输入一个域名地址,回车后发送网络请求到绑定的服务器,服务器再返回数据给客户端。

客户端与服务器对接的方式:

客户端想服务器方式请求时需要对应的ip地址或域名或服务器地址,当浏览器中输入地址回车后,通过http或https协议发送请求,请求过程中可以通过socket进行请求,请求到server后,如果网络、状态码等都是正常的话,就可以和server进行对接了。对接后server发现你的请求,需要返回对应的数据,服务端使用tcp协议返回客户端数据。

  • 通过Http模块创建本地服务器
//通过http模块,创建本地服务器
var http = require('http');

//创建服务器方法
var server = http.createServer(function (req,res) {
    console.log('客户端向服务器发送请求:' + req.url);
    res.writeHead(200,{"Content-type":"text/plain"});
    res.end('Server is working!');
})

//服务对象监听服务器地址和端口号
server.listen(8888,"127.0.0.1");
console.log('server is running...')

Buffer & Stream

  • buffer 可以在TCP流和文件系统操作等场景中处理二进制数据流。


    缓存区

buffer,即缓存区。服务器向客户端返回数据时采用的是tcp协议,在返回过程中有TCP的流,在JS中没有二进制流的说法,但在node.js中,为了能拥有一个缓存区,使用buffer。

Buffer 类在全局作用域中,因此无需使用 require('buffer').Buffer。

在引入 TypedArray 之前,JavaScript 语言没有用于读取或操作二进制数据流的机制。 Buffer 类是作为 Node.js API 的一部分引入的,用于在 TCP 流、文件系统操作、以及其他上下文中与八位字节流进行交互。


  • 在node.js中是处理流数据的抽象接口


  • 管道事件 改变流的走向
    左面输出 | 右面输入


    管道事件
let fs = require('fs');
//读取数据流
let myReadStram = fs.createReadStream(__dirname + '/readMe.txt','utf8');
// console.log(myReadStram);


//写入文件流
let myWriteStram = fs.createWriteStream(__dirname + '/writeMe.txt');
//再没有填充内容到流中时,文件夹中不会新建对应的文件


let times = 0;
myReadStram.on('data',(chunk) => {
    times++;
    console.log('===================正在接收'+ times +'一部分数据========================')
    // console.log(chunk);
    //写入数据
    myWriteStram.write(chunk);
});
//data为服务器返回数据必须触发的方法,且为约定俗成的,不可改变;
//返回的数据将以回调函数返回,数据为小块小块的返回
//读取数据时,也会经过缓存区,放到缓存区后再以流的形式展现给我们
let fs = require('fs');
//读取数据流
let myReadStram = fs.createReadStream(__dirname + '/readMe.txt','utf8');
// console.log(myReadStram);


//写入文件流
let myWriteStram = fs.createWriteStream(__dirname + '/writeMe2.txt');
//再没有填充内容到流中时,文件夹中不会新建对应的文件

//pipe通过管道流入到可写流的来源流。
myReadStram.pipe(myWriteStram);//拿到的数据调用了,参数为输入数据目的地
//通过pipe写入浏览器
let http = require('http');
let fs = require('fs');


//搭建服务器
let server = http.createServer((req,res) => {
    console.log(req);
    res.writeHead(200,{"Content-type":"text/plain"});
    let myReadStram = fs.createReadStream(__dirname + '/readMe.txt','utf8');
    myReadStram.pipe(res);
})

server.listen(9999,'127.0.0.1');
console.log('服务启动!');

读取HTML和JSON

let http = require('http');
let fs = require('fs');


//搭建服务器
let server = http.createServer((req,res) => {
    //console.log('客户端向服务器发送请求:' + req.url);
    //客户端向服务器发送请求:/               请求127.0.0.1:9999接口
    //客户端向服务器发送请求:/favicon.ico         请求标签栏的logo

    if(req.url !== '/favicon.ico'){
        console.log('客户端向服务器发送请求:' + req.url);
        //打印一遍
        //客户端向服务器发送请求:/               请求127.0.0.1:9999接口

        // res.writeHead(200,{"Content-type":"text/plain"});//数据类型:纯文本
        res.writeHead(200,{"Content-type":"text/html"});//数据类型:html
        // res.writeHead(200,{"Content-type":"application/json"});//数据类型:json


        // let myReadStram = fs.createReadStream(__dirname + '/readMe.txt','utf8');
        let myReadStram = fs.createReadStream(__dirname + '/index.html','utf8');
        // let myReadStram = fs.createReadStream(__dirname + '/perrson.json','utf8');

        myReadStram.pipe(res);
    }
})

server.listen(9999,'127.0.0.1');
console.log('服务启动!');

route 路由

路由既是“路在何方”

let http = require('http');
let fs = require('fs');


//搭建服务器
let server = http.createServer((req,res) => {
    if(req.url !== '/favicon.ico'){
        //判断用户所访问的页面地址
        if(req.url == '/home' || req.url == '/'){
            res.writeHead(200,{"Content-type":"text/html"});//数据类型:html
            fs.createReadStream(__dirname + '/index.html').pipe(res);
        }else if(req.url == '/contact'){
            res.writeHead(200,{"Content-type":"text/html"});//数据类型:html
            fs.createReadStream(__dirname + '/contact.html').pipe(res);
        }else if(req.url == '/api/docs'){
            // res.writeHead(200,{"Content-type":"text/html"});//数据类型:html
            // fs.createReadStream(__dirname + '/api/docs.html').pipe(res);

            var data = [
                {
                    name:'andy',
                    age:30
                },
                {
                    name:'qiongqiong',
                    age:16
                },
            ]
            res.writeHead(200,{"Content-type":"application/json"});//数据类型:json
            res.end(JSON.stringify(data));
        }
    }
})

server.listen(9999,'127.0.0.1');
console.log('服务启动!');

NPM_Package

  • npm : 是随同NodeJs一起安装的管理工具,能解决NodeJs 代码部署上的很多问题

常用的使用场景:

  • 允许用户从NPM服务器下载别人编写的第三方包到本地使用
  • 允许用户从NPM服务下载并安装别人编写的命令行程序到本地使用
  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用

安装模块:$ npm install 模块名

  • package : 用于定义项目中所需的各种模块,以及项目的配置信息(比如名称,版本,许可证等元数据)
    安装package :在项目文件夹中npm init运行,再根据自己的需求进行配置即可。

只有当项目文件夹中有package.json文件时才 可以使用npm install来安装模块依赖,安装的依赖会保存在node_modules文件夹中

卸载模块:npm uninstall 模块名

{
  "name": "nodedemo",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "jquery": "^3.4.0"//npm install XXX --save  保存记录
  },
  "devDependencies": {
    "express": "^4.16.4" //npm install XXX --save-dev  保存记录
  }
}

Experss 框架介绍及安装

基于Node.js平台,快速、开发、极简的web开发框架。

  • 安装 npm install express --save

nodemon工具

在开发环境下,往往需要一个工具来自动重启项目工程,我们可以借助nodemon来替代node进行启动

  • 安装npm install -g nodemon --save-dev
  • 运行 使用命令nodemon 文件名
    运行成功

Experss 框架

Experss 能做什么

  • 已经封装好服务器
  • 已经封装好路由
  • 已经封装好中间件
  • 已经封装好网络请求...

使用方法:

  1. npm 安装 Express 框架
  2. 引入 Express 模块
  3. 实例化 Express 对象
  4. 通过对象进行调用各种方法
//引入express模块
var express = require('express');

//实例化express的对象
var app = express();

//通过对象调用对应的方法

//根据用户请求的地址,返回对应的数据信息
app.get('/',function (req,res) {
    console.log(req.url)
    res.send('这是首页!')
})

app.get('/contact',function (req,res) {
    console.log(req.url)
    res.send('这是联系我们!')
})

//路由参数
//路由参数之后只能有一个斜杠
//例:http://127.0.0.1:8888/profile/list1201   ,返回的就是list1201;而http://127.0.0.1:8888/profile/list1201/ss 就显示‘Cannot GET /profile/list1201/ss’
app.get('/profile/:id',function (req,res) {
    res.send('您所访问的路径参数为'+ req.params.id);
})

//监听服务器端口号
app.listen(8888);

EJS模板引擎

ESJ模板引擎的特点

  1. 快速编译和渲染
  2. 简单的模板标签
  3. 支持浏览器端和服务器端
  4. 支持express视图系统

安装
npm install ejs --save-dev

//引入 express 模块
var express = require('express');
//实例化 Express 对象
var app = express();
app.set('view engine','ejs');  //配置视图引擎使用ejs
//通过对象调用方法
//路由参数
 app.get('/profile/:id',function (req,res) {
   //   res.send('你所访问的路径为:'+ req.params.id);//返回路由参数
   res.sendFile(__dirname + '/views/profile.ejs');//不配置视图引擎时,访问页面,会将对应的文件下载下来
   res.render('profile',{name:'LALALA'});//渲染ejs页面   参一:渲染对应的文件  参二:传递的数据 在对应的文件中使用 ‘<h1>欢迎来到 <%=name %> 网页!</h1>来接收’
  })
//监听服务器端口号
app.listen(8888);
//引入 express 模块
var express = require('express');
//实例化 Express 对象
var app = express();
app.set('view engine','ejs');  //配置视图引擎使用ejs
//通过对象调用方法

//根据用户请求地址,返回对应的数据信息
app.get('/',function (req,res) { //当访问根路径时,返回‘这是首页!’
    console.log(req.url);//打印路径
    res.sendFile(__dirname + '/index.html');//返回对应的html文件
 })

 app.get('/contact',function(req,res){
    res.sendFile(__dirname + '/contact.html');
 })

 //路由参数
 app.get('/profile/:id',function (req,res) {
   // let data = {age:23,name:'andy'};
   // let data = [{age:23,name:'andy'},{age:26,name:'kitty'},{age:24,name:'jack'}];
   let data = [{age:23,name:['张三','李四']},{age:26,name:['王五','赵六']},{age:24,name:['傻大个','胖子']}];
   res.render('profile',{websiteName:req.params.id,data:data})
  })

//监听服务器端口号
app.listen(8888);

以上代码data分别对应的渲染为

//let data = {age:23,name:'andy'};
<p><strong>年龄:</strong><%= data.age %></p>
<p><strong>姓名:</strong><%= data.name %></p>
// let data = [{age:23,name:'andy'},{age:26,name:'kitty'},{age:24,name:'jack'}];
<ul>
        <% data.forEach((item) => {%>
            <li>年龄: <%= item.age%></li>
            <li>姓名: <%= item.name%></li>
        <% })%>
    </ul>
//<%%>标签必须每行都需要闭合,如遇到代码换行时,必须将每行都用<% %>包裹
//let data = [{age:23,name:['张三','李四']},{age:26,name:['王五','赵六']},{age:24,name:['傻大个','胖子']}];
<ul>
<% data.forEach((item) => {%>
  <li>年龄: <%= item.age%></li>
  <li>姓名: <% item.name.forEach((item2) => { %>
        <span><%= item2%></span>
  <%}) %></li>
<% })%>
</ul>

公共模板

  • 使用EJS文件替换HTML文件
  • 创建导航(公共模板)
  • 解决外部样式不可用问题

将html页面全部使用ejs文件进行开发,文件结构可以参考以下


文件结构
  • 一般页面视图放在views文件夹中
  • 封装的公共模块放在public文件夹中
  • 静态资源放在assers文件夹中

将封装好的公共模块引入到对应的页面即可正常渲染

<!--引入公共导航模板-->
<% include ../public/nav.ejs%>

在页面中引入外部样式时,需要先在app.js中进行配置,方可引用

//app.js
//让服务器识别外部样式表
app.use('/assets',express.static('assets'));
//参一:路径  参二:该路径文件做什么事
//此时的参二表示:在项目中,让css静态文件转化为模块化,让服务器进行识别。否则加载样式表会报错
//各文件中正常引用
<link rel="stylesheet" href="../assets/style.css">

实战操作 ToDoApp

启动新项目
初始化webpack
安装express框架
安装esj body-parser 模块

  1. 新建项目文件夹TodoApp,并在该文件夹中安装 package npm init
  2. 安装express 框架 npm install express --save-dev
  3. 安装 ejs 和 body-parser 模块 npm install ejs body-parser --save-dev
  4. 在项目文件夹中创建主文件的入口app.js
  5. 新建controller文件夹并添加todoController.js文件来处理项目中对应的逻辑
  6. 新建public文件夹来写入对应的样式
//app.js
let express = require('express');//引入express

let todoController = require('./controller/todoController')//自定义模块todoController
let app = express();//实例化express对象
app.set('view engine','ejs');//配置视图渲染规则按照ejs渲染

// app.use('/public',express.static('public'))
//等同于
app.use(express.static('./public'))//配置静态资源模块化,让浏览器识别

todoController(app)

app.listen(3000)//监听端口号
//todoController.js
module.exports = function (app) {
    //获取数据
    app.get('/todo', (req,res) => {
        res.send(req.url);
     })

     //传递数据
     app.post('/todo',(req,res) => {
        //coding...
     })

     //删除数据
     app.delete('/todo',(req,res) => {
         //coding
     })
 }

引入jquery
设置视图
设置请求页面返回视图
设置视图样式

  1. 在项目文件夹中新建views文件夹并添加todo.ejs文件并完成静态页面布局
  2. 将todoController.js文件修改,设置请求页面返回视图
//获取数据
  app.get('/todo', (req,res) => {
        res.render('todo');
     })

在控制器中处理本地数据
在视图上将数据展示出来
最终的代码以及样式展示

//todoController.js

let bodyParser = require('body-parser');
//对数据进行解析
let urlencodeParser =  bodyParser.urlencoded({extended:false});

let data = [
    {item:'欢迎大家来到茕茕课堂练习!'},
    {item:'希望大家能喜欢我的练习!'},
    {item:'一个个手敲代码好累哦!'},
]

//todoController
module.exports = function (app) {
    //获取数据
    app.get('/todo', (req,res) => {
        res.render('todo',{todos:data});
     })

     //传递数据
     app.post('/todo',urlencodeParser,(req,res) => {
        //coding...
        console.log(req.body);
        data.push(req.body);
     })

     //删除数据
     app.delete('/todo/:item',(req,res) => {
         //coding
         console.log(req.params.item);
         data = data.filter(function (todo) {
            return req.params.item != todo.item;
          })

          res.json(data);
     })
 }
//todo.ejs
<!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>todoApp</title>
    <link rel="stylesheet" href="assets/reset.min.css">
    <link rel="stylesheet" href="assets/style.css">
    <!--引入jquery-->
    <script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
</head>
<body>
    <header>
        <form>
            <input type="text" placeholder="Enter an activity..." name="item" id="item">
            <button type="submit" id="add">
                <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"><g><path class="fill" d="M16,8c0,0.5-0.5,1-1,1H9v6c0,0.5-0.5,1-1,1s-1-0.5-1-1V9H1C0.5,9,0,8.5,0,8s0.5-1,1-1h6V1c0-0.5,0.5-1,1-1s1,0.5,1,1v6h6C15.5,7,16,7.5,16,8z"/></g></svg>
            </button>
        </form>
    </header>

    <div class="container">
        <!--未完成列表-->
        <ul class="todo" id="todo">
            <!--动态添加数据-->
            <% todos.forEach((val,ind) => {%>
                <li>
                    <%= val.item%>
                    <div class="buttons">
                        <button class="remove">
                            <svg t="1556521793477" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2483" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path class="fill" d="M412 744c-19.8 0-36-16.2-36-36V500c0-19.8 16.2-36 36-36s36 16.2 36 36v208c0 19.8-16.2 36-36 36zM612 744c-19.8 0-36-16.2-36-36V500c0-19.8 16.2-36 36-36s36 16.2 36 36v208c0 19.8-16.2 36-36 36z" p-id="2484"></path><path class="fill" d="M923.8 248H736v-82c0-56.2-45.8-102-102-102H390c-56.2 0-102 45.8-102 102v82H100.2C80.3 248 64 264.2 64 284s16.3 36 36.2 36H192v518c0 16.4 3.2 32.4 9.6 47.5 6.1 14.5 14.9 27.6 26.1 38.8 11.2 11.2 24.2 20 38.8 26.1 15.1 6.4 31.1 9.6 47.5 9.6h396c16.4 0 32.4-3.2 47.5-9.6 14.5-6.1 27.6-14.9 38.8-26.1 11.2-11.2 20-24.2 26.1-38.8 6.4-15.1 9.6-31.1 9.6-47.5V320h91.8c19.9 0 36.2-16.2 36.2-36s-16.3-36-36.2-36zM360 166c0-16.6 13.4-30 30-30h244c16.6 0 30 13.4 30 30v82H360v-82z m400 672c0 27.6-22.4 50-50 50H314c-27.6 0-50-22.4-50-50V320h496v518z" p-id="2485"></path></svg>
                        </button>
                        <button class="complete">
                            <svg t="1556526475868" viewBox="0 0 1879 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4164" xmlns:xlink="http://www.w3.org/1999/xlink" width="366.9921875" height="200"><defs><style type="text/css"></style></defs><path class="fill" d="M801.590751 981.530636c-19.384971 0-38.030058-7.250867-52.087861-20.420809L348.337572 587.024277c-30.039306-27.96763-31.075145-74.43237-2.36763-103.731791s76.504046-30.187283 106.543353-2.219654L795.079769 800.554913 1416.434682 65.257803c26.487861-31.223121 73.988439-35.810405 106.099422-10.062427 32.110983 25.747977 36.698266 71.916763 10.358382 103.287861L859.893642 954.894798c-13.317919 15.833526-32.850867 25.452023-53.715607 26.635838h-4.587284z" class="fill" p-id="4165"></path></svg>
                        </button>
                    </div>
                </li>
            <% })%>

        </ul>
        <!--已完成列表-->
        <ul class="todo" id="completed">
            <!--动态获取数据-->
            <li>
                邓伦最帅!
                <div class="buttons">
                    <button class="remove">
                        <svg t="1556521793477" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2483" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path class="fill" d="M412 744c-19.8 0-36-16.2-36-36V500c0-19.8 16.2-36 36-36s36 16.2 36 36v208c0 19.8-16.2 36-36 36zM612 744c-19.8 0-36-16.2-36-36V500c0-19.8 16.2-36 36-36s36 16.2 36 36v208c0 19.8-16.2 36-36 36z" p-id="2484"></path><path class="fill" d="M923.8 248H736v-82c0-56.2-45.8-102-102-102H390c-56.2 0-102 45.8-102 102v82H100.2C80.3 248 64 264.2 64 284s16.3 36 36.2 36H192v518c0 16.4 3.2 32.4 9.6 47.5 6.1 14.5 14.9 27.6 26.1 38.8 11.2 11.2 24.2 20 38.8 26.1 15.1 6.4 31.1 9.6 47.5 9.6h396c16.4 0 32.4-3.2 47.5-9.6 14.5-6.1 27.6-14.9 38.8-26.1 11.2-11.2 20-24.2 26.1-38.8 6.4-15.1 9.6-31.1 9.6-47.5V320h91.8c19.9 0 36.2-16.2 36.2-36s-16.3-36-36.2-36zM360 166c0-16.6 13.4-30 30-30h244c16.6 0 30 13.4 30 30v82H360v-82z m400 672c0 27.6-22.4 50-50 50H314c-27.6 0-50-22.4-50-50V320h496v518z" p-id="2485"></path></svg>
                    </button>
                    <button class="complete">
                        <svg t="1556526475868" viewBox="0 0 1879 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4164" xmlns:xlink="http://www.w3.org/1999/xlink" width="366.9921875" height="200"><defs><style type="text/css"></style></defs><path class="fill" d="M801.590751 981.530636c-19.384971 0-38.030058-7.250867-52.087861-20.420809L348.337572 587.024277c-30.039306-27.96763-31.075145-74.43237-2.36763-103.731791s76.504046-30.187283 106.543353-2.219654L795.079769 800.554913 1416.434682 65.257803c26.487861-31.223121 73.988439-35.810405 106.099422-10.062427 32.110983 25.747977 36.698266 71.916763 10.358382 103.287861L859.893642 954.894798c-13.317919 15.833526-32.850867 25.452023-53.715607 26.635838h-4.587284z" class="fill" p-id="4165"></path></svg>
                    </button>
                </div>
            </li>
        </ul>
    </div>
</body>
<script>
$(document).ready(function () {
    //添加数据
    $('header').on('submit',function () {
        let item = $('form input');
        let todo = {item:item.val()};
        if(todo.item == ""){
            alert('内容不能为空,请重新输入!');
            return false;
        }

        //发送数据
        $.ajax({
            type:'post',
            url:'/todo',
            data:todo,
            success:function(data){
                //coding...
            }
        })
     })

     //删除数据
     $('.remove').on('click',function () {
         //$.trim() 函数用于去除字符串两端的空白字符
         let item = $.trim($(this).parents('li').text());
         $.ajax({
             type:'delete',
             url:'/todo/'+ item,
             success:function(data){
                location.reload();
            }
         })
      })
 })
</script>
</html>

因为主要为node.js的学习,此处的样式省略,之后会抛出代码提供参考。


最终页面样式展示

mongoose

安装mongoose模块
使用mongoDB网络数据库
创建数据库及配置用户名及密码
链接mongoDB数据库

  1. 安装mongoose npm install mongoose --save-dev
  2. mongodb 官网注册账号并新建配置数据库
  3. 链接数据库,在 todoController.js 文件中引入并替换之前的方法

npm root -g 获取全局安装依赖路径

//todoController.js
//引入mongoose 模块
let mongoose = require('mongoose');
//链接数据库
mongoose.connect('mongodb+srv:*******************',{useNewUrlParser:true})
//创建图表
let todoSchema  = new mongoose.Schema({
   item:String
})

//往数据库中存储数据
let Todo = mongoose.model('Todo',todoSchema);

//测试存储
// Todo({item:'大家好'})
//     .save((err,data) => {
//         if(err) throw err;
//         console.log('保存成功!')
//     })

let bodyParser = require('body-parser');
//对数据进行解析
let urlencodeParser =  bodyParser.urlencoded({extended:false});

// let data = [
//     {item:'欢迎大家来到茕茕课堂练习!'},
//     {item:'希望大家能喜欢我的练习!'},
//     {item:'一个个手敲代码好累哦!'},
// ]

//todoController
module.exports = function (app) {
   //获取数据
   app.get('/todo', (req,res) => {
       // res.render('todo',{todos:data});
       Todo.find({},(err,data) => {
           if(err) throw err;
           res.render('todo',{todos:data});
       })

    })

    //传递数据
    app.post('/todo',urlencodeParser,(req,res) => {
       //coding...
       // console.log(req.body);
       // data.push(req.body);

       Todo(req.body)
           .save((err,data) => {
               if(err) throw err;
               res.json(data);
           })
    })

    //删除数据
    app.delete('/todo/:item',(req,res) => {
        //coding
       //  console.log(req.params.item);
       //  data = data.filter(function (todo) {
       //     return req.params.item != todo.item;
       //   })

       //   res.json(data);

       Todo.find({item:req.params.item})
           .remove((err,data) => {
               if(err) throw err;
               res.json(data);
           })
    })
}

至此,todoApp的实战已经到达尾声,我们已经完成链接线上数据库获取数据、添加数据,查找数据并删除的功能。剩下的完成功能就当做家庭小作业吧O(∩_∩)O哈哈~
源码附上

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 8,994评论 0 3
  • 个人入门学习用笔记、不过多作为参考依据。如有错误欢迎斧正 目录 简书好像不支持锚点、复制搜索(反正也是写给我自己看...
    kirito_song阅读 2,434评论 1 37
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    Myselfyan阅读 4,057评论 2 58
  • 33、JS中的本地存储 把一些信息存储在当前浏览器指定域下的某一个地方(存储到物理硬盘中)1、不能跨浏览器传输:在...
    萌妹撒阅读 2,045评论 0 2
  • GitChat技术杂谈 前言 本文较长,为了节省你的阅读时间,在文前列写作思路如下: 什么是 webpack,它要...
    萧玄辞阅读 12,646评论 7 110