NodeJS学习

前端-NodeJS

一、NodeJs介绍

1. 功能

  • NodeJs不是语言、库、框架
  • 是JavaScript运行环境,执行JavaScript代码
  • 使得JavaScript可以完全脱离浏览器执行

2. 浏览器中的JavaScript与NodeJs中JavaScript对比

1) 浏览器中

  • EcmaScript:基本语法、if、var、function、object、array
  • BOM
  • DOM

2) NodeJs中

  • EcmaScript
  • 没有DOM和BOM
  • 服务器级别的操作:文件的读写、网络服务器的转发、网络通信、http服务器、待处理

3. NodeJs特点

  • even-driven:事件驱动
  • non-blocking I/O model:异步
  • lightweight and effcient:轻量与高效

4. npm

  • npm是世界上最大的开源生态库
  • 绝大多数的javascript相关的包
  • 例:npm install jquery

5. NodeJs作用

  • web服务器后台
  • 命令行工具:npm、git、hexo

6. 书籍

  • 深入浅出Node.js
  • node.js权威指南
  • javascript标准参考教程:阮一峰
  • node入门
  • 官方api文档
  • 中文文档
  • CNODE社区

7. 所学知识点

  • BS:Browser-Server
  • 模块化编程
  • 异步编程
  • Express Web开发工具
  • Ecmascript 6

8. 安装

二、入门

1. Hello.js

var foo = 'hello Node'

console.log(foo)
  • cmd执行 
  • git bash
  • powershell

node hello.js   执行

2. node没有DOM和BOM

3. node具有文件读写功能,要引入fs模块

读文件

// 浏览器中的Javascript没有文件操作功能
// Node中的javascript有

// fs-- file system
// fs模块拥有所有文件的读写操作

// 1.使用require加载fs核心模块
var fs = require('fs')

// 2. 读取文件
//    第一个参数:要读取文件的路径
//    第二个参数:回调函数
//        error:
//           如果读取失败,error就是错误对象
//           如果读取成功,error就是null
//        data:
//            如果读取成功,data就是读取到的数据
//            如果读取失败,error就是错误对象
//            
fs.readFile('./data/hello.txt',function(error,data){
    // 原始数据二进制,转为十六进制
    console.log(data)
    // toString
})

写文件

var fs = require('fs')

// 参数
//   1. 路径
//   2. 内容
//   3. 回调函数
//        error
//           成功:文件写入成功,error为null
//           失败:文件写入失败,error错误对象
fs.writeFile('./data/wt.txt','你好',function(error){

    console.log('文件读写成功')
})

3. 创建服务器  http

核心模块:http

// 引入核心模块
var http = require('http')

// 创建服务器
var server = http.createServer()

// request: 注册request事件,放客户端请求过来,就会自动触发服务器 request 请求事件,
//          然后执行第二个参数,回调处理函数
// response: 相应对象
server.on('request'function(request,response){
    console.log('客户端收到请求')
    console.log('收到客户端的请求,路径:'+ request.url)
    
    // response对象中的write方法可以给客户端相应数据
    // write可以使用多次,但是最后一定要使用end结束响应
    response.write('Hello')
    
    response.end()
})

// 开启端口号
server.listen(3000,function(){
    console.log('服务器启动成功')
})

相应小例子

var http = requrire('http')

var server = http.createServer()

server.on('request',function(request,response){
    //  相应的路径都是端口号后面的,以  /
    var url = request.url

    // 虚拟json数据
    var products = [
        {
            name: 'iphone',
            price: 5000
        },
        {
            name: 'huawei',
            price: 3000
        }
    ]

    if(url === '/'){
        response.end('主页')
    }else if(url === '/login'){
        response.end('登录页面')
    }else if(url === 'products'){
        response.end(JSON.stringify(products))
    }else{
        response.end('页面不存在')
    }

      

})

server.listen(80,function(){
    console.log('服务器启动成功')
})

4. 常用的模块

看官网

  • 文件操作
  • http
  • url路径操作
  • path路径处理
  • os操作系统

Node中的模块有:

  • 具名的核心模块,fs、http
  • 用户自己编写的文件模块

Node中只用模块作用域,没有全局作用域

require作用:

  • 加载文件模块并执行里面的代码
  • 拿到被加载文件模块的接口对象

exports:

  • 默认是一个空对象
a.js


var exports = require('./b')
console.log(exports.foo)



b.js

var foo = 'bbb'
exports.foo = 'hello'

exports.add = function(x,y){

    return x + y
}


执行a.js   输出  hello

IP地址用于定位计算机

端口号用于定位应用程序

解决相应乱码问题 

response.setHeader('Content-Type','text/plain;charset=utf-8')


// 根据相应设置响应头文件类型

if(url === 'plain'){
    response.setHeader('Content-Type','text/plain;charset=utf-8')
    response.end('hello')
}else if(url === '/html'){
    response.setHeader('Content-Type','text/html;charset=utf-8')
    response.end('<p>hello</p>')
}else if(url === '/ab2.jpg'){
    response.setHeader('Content-Type','image/jpeg')
}

每种资源对应的Content-Type是 不一样的

无分号代码风格:

  • [
  • `   反引号   很nice     写什么格式呈现什么格式

以以上三种开头的时候,则在前面补上分号用以避免语法的解析错误

三、实现展示目录功能      这个例子讲的是模板引擎

var http = require('http')

var server = http.createServer()

var myDir = 'C:/Users/yan/Desktop/NodeTest'

server.on('request',function(req,resp){
    var url = req.url
    fs.readFile(./template.html,function(error,data){
        if(error){
            return resp.end('404 not found')
        }

        fs.readdir(myDir,function(error,files){
            if(err){
                return resp.end('Can't found the file')
            }
        })
    })    
})

模板引擎与Vue很类似

art-template 

  • npm install art-template
  • 加载art-template    var template =  require('art-template')
  • 查文档

这个例子讲的是服务器端渲染

服务端渲染:在网页源代码中能够找到对应的商品

客户端渲染:在网页源代码中找不到

四、留言本案例

1. 链式调用

var http = require('http')
var fs = require('fs')

http
    .createServer(function(req,resp){
        resp.end('hello')
    })
    .listen(3000,function(req,resp){
        console.log('server is running')
    })

2. 为了方便的统一处理这些静态资源,将这些静态资源都放在public目录中

五、CommonJS模块规范

模块系统:

  • 模块作用域
  • 使用require方法加载模块
  • 使用exports接口对象用来导出模块中的成员

值界到处某个变量使用:module.exports = 'hello'

也可导出多个对象

module.exports = {

    add: function(){
        return x + y
    },

    str: 'hello'
}

Nodejs简化---module.exports = exports

exports.a = 123

// 重新赋值,其以后添加的结果无效
exports = {} 
exports.foo = 'bar'


执行后结果:a:123

最终返回的是module.exports

分析:

exports.foo = 'bar'

module.exports.a = 123

// 这里已经断开连接,最终return的是module.exports
// 所以无论exports中的成员变量是什么都没用
exports = {
    a:'bar'
}

module.exports.foo = 'haha'

exports.c = 456

// 重新建立联系
exports = module.exports

exports.a = 789

slice

require方法加载规则

  • 优先缓存加载
  • 判断标识符:核心模块、第三方模块、自定义模块

七、npm和package.json

1. 先导

npm是包管理工具

package.json就像产品说明书,里面包括依赖,通常是npm install --save xxx

npm init向导方式

  • npm i xxx --save   :   Dependencies
  • npm i xxx --save-dev  :  devDependencies    这是开发环境 ,在生产环境中此处依赖不加载

2. npm命令

1) 官方网站

https://www.npmjs.com

2) npm --version   

使用npm install --global npm进行升级

3) 常用命令

  • npm init     其中使用npm inti -y可以跳过向导,快速生成
  • npm install      一次性把dependencies选项的依赖全部安装
  • npm install 包名   只下载
  • npm install --save 包名  下载并保存依赖
  • npm uninstall --save 包名   删除的同时也会把依赖信息也去除
  • npm help
  • npm 命令 --help 查看具体命令帮助

4) 镜像

npm.taobao.org

npm install --global cnpm   全局

cnpm install jquery 使用淘宝服务器

也可使用npm config set registry https://npm.taobao.org,则npm可以使用淘宝进行下载

npm config list

3. package.json

npm 5版本以前不会有package-lock.json文件

npm 5版本之后会有package-lock.json文件

package-lock.json保存了所有依赖的信息

lock:用于锁定版本

八、express

是一个框架    expressjs.com

1) 创建express-demo

mkdir express-demo

npm init --y

npm install --save express

2) 引包

var express = require('express')

var app = express()

app.get('/',function(req,resp){
    resp.send('hello express')
})

app.get('/about',function(req,resp){
    resp.send('express')
})

app.listen(3000,function(){
    console.log('app is runnning at port 3000')

})

3) 指定公开目录

app.use('/public',express.static('./public/'))

4) 热部署

nodemon

npm install --global nodemon

使用nodemon app.js 自动监视文件的变化自动重启服务 

5) 路由

  • 请求方法
  • 请求路径
  • 处理函数

6) 使用art-template

npm install --save art-template

npm install --save express-art-template

app.engine('art',require('express-art-template))

resp.render('模板名',{模板数据})   express约定,视图都在view目录下

var express = require('express')

var app = express()

app.engine('html',require('express-art-template))

app.get('/',function(req,resp){
    resp.send('hello express')
})

app.get('/about',function(req,resp){
    resp.render('index.html',{
        title:'管理系统'
    })
})

app.listen(3000,function(){
    console.log('app is runnning at port 3000')

})
<!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</title>
</head>
<body>
    <h1>{{ title }}</h1>    
</body>
</html>

7) 在express中获取post请求数据

使用插件body-parser

** 获取get请求参数值界直接使用req.query

8) CRUD

var students = JSON.parse(data).students

express中的router        var router = express.Router()

讲操作模块封装好

异步回调函数

function fn(callback){
    // var callback = function(data){
    //                  console.log(data) 
    //                } 
    
    setTimeout(function(){
        var data = 'hello'
        callback(data)
    },1000)

}

// 如果需要获取一个函数中异步操作的结果,则必须通过回调函数来获取
fn(function(data){
    console.log(data)
})

回调函数:在一个函数中调用另外一个函数就是callback   主要是为获取异步的数据

最简单的例子:

function a(){
    return 1
}

function b(aa){
    return 2 + aa
}

var c = 0
c = b(a())
console.log(c)

// 结果为3

异步操作

var a = 0

function bb(x){
    console.log(x)
}

function timer(x){
    setTimeout(function(){
        a = 6
    },x)
}

console.log(a)
timer(3000)
bb(a)

// 结果 0 0
var a = 0

function bb(x){
    console.log(x)
}

function timer(x,callback){
    setTimeout(function(){
        a = 6
        callback(a)
    },x)
}

console.log(a)
timer(3000,bb)

// 结果 0 6

理解:

9) MongoDB

关系型数据库

非关系型数据库

下载与安装

连接与退出

  • mongo
  • exit

基本命令

  • show dbs   显示所有数据库
  • db    当前数据库
  • use 数据库名  切换到指定的数据库
  • 插入数据    db.students.insertOne({"name":"Jack"})

具体的学习直接看菜鸟教程

10) Node操作MongoDB

不建议使用mongo官方提供的包

使用第三方包 mongoose

数据库

表  -->  集合

九、Promise

为了解决回调地狱嵌套,ES6新增API--Promise

是一个构造函数

使用案列

var fs = require('fs')

console.log(1)

// 创建Promise容器
new Promise(function(){
    console.log(2)

    fs.readFile('/data/a.txt','utf-8',function(err,data){
        if(err){
            console.log('err')
        }else{
            console.log(3)
            console.log(data)
        }

    })
})

console.log(4)

// 输出结果不一定,Promise中readFile是异步操作

Promise本身不是异步,其中的任务是异步的

var fs = require('fs')

// 创建Promise容器
var p1 = new Promise(function(resolve,reject){

    fs.readFile('/data/a.txt','utf-8',function(err,data){
        if(err){
            // 失败,把容器的Pending状态变为Rejected
            reject(err)
        }else{
            // 成功,把容器的Pending状态变为Resolved
            resolve(data)
        }

    })
})

// 创建Promise容器
var p2 = new Promise(function(resolve,reject){

    fs.readFile('/data/b.txt','utf-8',function(err,data){
        if(err){
            // 失败,把容器的Pending状态变为Rejected
            reject(err)
        }else{
            // 成功,把容器的Pending状态变为Resolved
            resolve(data)
        }

    })
})


// 获取Promise任务中的数据
p1
    .then(function(data){
        console.log(data)
        // 当p1读取成功的时候
        // 当前函数中的return的结果就可以在后面的then中的function接收到
        // 当你return 123后面的就接收到123
        //     return 'hello' ...
        //     return p2 就接受p2任务的结果作为参数
    },function(err){
        console.log('读取文件失败',err)
    })
    .then(function(data){
        console.log(data)
    })

封装Promise API

应用场景

1) json-server插件的使用

npm install -g json-server

十、课程的项目

1. 初始化项目

  • npm init -y
  • git init
  • Readme.md   项目简介
  • .gitignore   忽略一些文件不git到仓库中
  • npm i express mongoose   nodemon
  • public   公开资源    css   img   js
  • app.js   主文件入口
  • nodemon app.js  测试
var express = require('express')

var path = require('path')

var app = express()

app.use('/public',express.static(path.join(__dirname,'./public/')))
app.use('/node_modules',express.static(path.join(__dirname,'./node_modules/')))

app.get('/',function(req,resp){
    resp.send('hello yan shiwu')
})

app.listen(3000,function(){
    console.log('the server is running')
})



2. Node中Path模块学习

_dirname和_filename

  • _dirname:  获取当前文件模块的绝对路径
  • _filename: 获取当前文件的决定路径

Node的文件操作是以相对路径为基础的,不可靠,因此尽量使用动态绝对路径

3. 模板引擎的搭建

  • npm i art-template
  • npm i express-art-template

公共模板提出(使用art-template)  模板继承

于是就涉及到页面布局 layout

4. 引入bootstrap和jquery

<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">

<script src="/node_modules/jquery/dist/jquery.js"></script>
<script src="/node_modules/bootstrap/dist/js/bootstrap.js"></script>

从而也可以动态引入所需样式   这对js央视也同理

5. 使用项目的页面

6. 路由的设置

新建routers文件夹,提取app.js中的路由

  • session.js   处理  注册、登录、退出等业务
  • topic.js   处理 博客发表、浏览等业务
路径方法get参数post参数是否需要登录备注
/get   渲染主页
/registerget  

 

渲染注册页面
/registerpost name、password、email 处理注册请求
/loginget   渲染登录页面
/loginpost name、password 处理登录请求
/logoutget   处理退出请求

 1) 注册、登录与退出路由

注册模块:

第一步:路由

var express = require('express')

// 数据库
var User = require('../models/user.js')

// md5加密  npm i blueimp-md5
var md5 = require('blueimp-md5')

var router = express.Router()

router.get('/',function(req,resp){
    resp.render('index.html')
})

router.get('/register',function(req,resp){
    resp.render('register.html')
})

router.post('/register',function(req,resp){

})
    
module.exports = router

在app.js中挂载路由:

// 插件获取post请求数据
var bodyParser = require('body-parser')

// 加载路由
var router1 = require('./router/session.js')

// 配置解析表单post请求插件  要在路由挂载之前
// 使用body-parse
app.use(bodyParser.urlencoded({ extended:false }))
app.use(bodyParser.json())

// 使用路由(将路由挂在app中)
app.use(router1)

第二步:构建User mongodb数据库  user.js

var mongoose = require('mongoose')

// 连接数据库
mongoose.connect('mongodb://localhost/test');

var Schema = mongoose.Schema

var userSchema = new Schema({
    email: {
        type: String,
        required: true
    },
    nickname: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    create_time: {
        type: Date,
        default: Date.now
    },
    last_modified_time: {
        type: Date,
        default: Date.now
    },
    avatar: {
        type: String,
        default: '/public/img/avatar-default.png'
    },
    bio: {
        type: String,
        default: ''
    },
    gender: {
        type: Number,
        enum: [-1,0,1],
        default: -1
    },
    birthday: {
        type: Date
    },
    status: {
        type: Number,
        // 没有权限限制
        // 是否可以评论
        // 是否可以登录使用
        enum: [0,1,2],
        default: 0
    }
})

module.exports = mongoose.model('User',userSchema)

第三步:处理注册事件

router.post('/register',function(req,resp){
    // 1. 获取表单数据   获取post请求数据,需要安装插件
    // console.log(req.body)
    var body = req.body

    // 2. 操作数据库   用户名或邮箱是否存在  先不使用Promise
    User.findOne({
        $or: [
            {
                email: body.email
            },
            {
                nickname: body.nickname
            }
        ]
        
    },function(err,data){
        if(err){
            return resp.status(500).json({
                err_code: 500,
                message: 'Server Error'
            })
        }
        if(data){
            return resp.status(200).json({
                err_code: 1,
                message: 'email or nickname is already exist'
            })
        }

        // md5加密
        body.password = md5(md5(body.password))

        new User(body).save(function(err,data){
            if(err){
                return resp.status(500).json({
                    err_code: 500,
                    message: 'Server Error'
                })
            }
            resp.status(200).json({
                err_code: 0,
                message: 'Ok'
            })
        })
        
    })
})

// ES6
// router.post('/register', async function (req, res) {
//   var body = req.body
//   try {
//     if (await User.findOne({ email: body.email })) {
//       return res.status(200).json({
//         err_code: 1,
//         message: '邮箱已存在'
//       })
//     }

//     if (await User.findOne({ nickname: body.nickname })) {
//       return res.status(200).json({
//         err_code: 2,
//         message: '昵称已存在'
//       })
//     }

//     // 对密码进行 md5 重复加密
//     body.password = md5(md5(body.password))

//     // 创建用户,执行注册
//     await new User(body).save()

//     res.status(200).json({
//       err_code: 0,
//       message: 'OK'
//     })
//   } catch (err) {
//     res.status(500).json({
//       err_code: 500,
//       message: err.message
//     })
//   }
// })

第四步:返回数据给前端

前端的异步请求ajax

<script>
    $('#register_form').on('submit', function (e) {
      e.preventDefault()
      var formData = $(this).serialize()
      $.ajax({
        url: '/register',
        type: 'post',
        data: formData,
        dataType: 'json',
        success: function (data) {
          var err_code = data.err_code
          if (err_code === 0) {
            // window.alert('注册成功!')
            // 服务端重定向针对异步请求无效
            window.location.href = '/'
          } else if (err_code === 1) {
            window.alert('邮箱已存在!')
          } else if (err_code === 2) {
            window.alert('昵称已存在!')
          } else if (err_code === 500) {
            window.alert('服务器忙,请稍后重试!')
          }
        }
      })
    })
  </script>

第五步:小细节   mongodb compass使用

表单具有默认的提交行为,默认是同步的,同步表单提交,浏览器会锁死等待服务端的响应结果

同步: <form action method>   无论服务器响应什么,都会自动覆盖当前内容

于是ajax异步

登录模块:

session--用户的活跃状态

express-session

npm i express-session

// google插件: EditThisCookie

也可使用插件讲session数据存储到MongoDB中,方便服务器重启后找回数据

十一、Express中间件

var express = require('express');
var app = express();

app.use(function (req, res, next) {  //没有路由参数,默认匹配所有路径
    console.log('app use2');  //输出在控制台
    next();  //传递给url相匹配的下一个中间件

});
app.get('/', function (req, res, next) {
    res.send('hello');  //输出在页面
    next();   //  传递给下一个中间件
}, function (req, res, next) {
    console.log('world');  //输出在控制台
    next();  //传递给下一个中间件
});

app.get('/', function (req, res) {
    console.log('two'); //输出在控制台
});

app.use(function (req, res) {
    console.log('app use2');   //不会输出,因为在上一个中间件已经结束请求
});
app.listen(3000, function () {
    console.log('listen 3000 port');
});

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值