美文网首页node专题
node.js学习笔记

node.js学习笔记

作者: Mstian | 来源:发表于2020-03-15 22:27 被阅读0次

    node.js学习笔记

    node

    01

    什么是node?

    • chrome V8 runtime
    • 事件驱动
    • 非阻塞的i/o

    优点:处理高并发比较好

    模块化

    • 内置模块(node中自带的模块)
      • 文件操作
      1. fs.readdir
      2. fs.mkdir(同步,异步)
      3. fs.rename
      4. fs.rmdir(只能删除空文件夹)
      • 错误信息处理 node中大多数秉持错误优先原则,回调中有err参数,同步代码为了不影响后续代码执行需要使用try {} catch (err) {}捕获错误。
    • 第三方模块
    • 自定义模块

    自定义模块使用模块步骤

    1.创建一个模块(一个js文件一个模块)

    2.导出一个模块(module.exports = name)

    3.引入一个模块并且调用

    运行环境

    REPL :在命令行执行的环境。

    内置文件模块fs

    文件操作

    创建文件(覆盖写入)

      fs.writeFile(path,option,callback)
    

    写入文件(追加写入)

      fs.appendFile(path,option,callback)
    

    读取文件

    fs.readFile(path,config,(err,msg)=>{
      //msg默认为buffer数据流 可以使用 msg.toString('utf8')转换数据
      //也可以使用config出配置‘utf8’
    })
    
    

    删除文件

    fs.unlink(path,callback)
    

    内置url模块

    url : 统一资源定位符

    url.parse();将url字符串转换为对象

    url.format();将url对象转换为字符串

    内置querystring模块

    qs.parse(str,query1,query2);
    //query1 表示以什么符号切分键值对 默认 &
    //query2 表示以什么符号切分键与值 默认 = 
    
    qs.stringify(obj,query1,query2);
    //query1 表示以什么符号拼接键值对 默认 &
    //query2 表示以什么符号拼接键与值 默认 =
    
    qs.escape() //编码
     
    qs.unescape() //解码
    

    第三方模块

    nodemailer 可以实现发邮件

    "use strict";
    const nodemailer = require("nodemailer");
    
    // 创建一个发送器
    let transporter = nodemailer.createTransport({
        host: "smtp.qq.com",
        port: 465,
        secure: true, // true for 465, false for other ports
        auth: {
            user: '572410049@qq.com', // 发送者邮箱地址
            pass: 'pwuepzqfdpjybeid' // 发送者SMTP密码
        }
    });
    
    // 配置发送信息对象
    var emailconfig = {
        from: '572410049@qq.com', // 发送者邮箱地址
        to: "572410049@qq.com", // 接受者邮箱地址
        subject: "你好", // 主题
        text: "准备睡觉吧", // 内容 只能是纯文本 与 html 配置项只能存在一个
        // html: "<b>Hello world?</b>" // html body
    }
    
    // 调用sendMail()方法
    transporter.sendMail(emailconfig);
    
    

    爬虫案例

    1.获取目标网站
    2.分析网站内容
    3.获取有效信息 下载或其他操作

    http 模块

    http模块负责发请求:

    const http = require('https');
    let html = '';
    http.get(url,(res)=>{
    
      let { statusCode } = res; //状态码
      let contentType = res.headers['content-type']; //请求类型
      let err = null;
      if(statusCode !== 200){
        err = new Error('请求状态出错');
      }else if(!/^text\/html/.test(contentType)){
        err = new Error('请求类型出错');
      }
    
      if(err){
        console.log(err);//此处可以确定错误类型
        res.resume(); //重置缓存
        return false //终止程序
      }
      //---------------------------- 代码健壮判断抓取的是否为所需类型,和是否能访问目标地址 ----------------------------------------
      res.on('data',(chunk)={
        //chunk 为buffer数据流 需通过chunk.toString('utf8')转换
        //爬取中。。。
        html += chunk.toString('utf8')
      });
      res.on('end',()=>{
        console.log(html);
        //爬取结束。
      })
    }).on('error',(e)=>{
      console.log('请求出错')
    })
    
    

    cheerio 模块

    将一组html格式的字符串 转化为类之后可以通过jq的语法选中其中的元素。

    const cheerio = require('cheerio');
    //将一组html格式的字符串 转化为类之后可以通过jq的语法选中其中的元素。
    var div = '<div> <img src="http://www.baidu.com"/> </div>'
    const $ = cheerio.load(div);
    
    console.log($('img').attr('src'));
    

    实现抓取一个页面的图片

    const http = require('https');
    const fs = require('fs');
    const cheerio = require('cheerio');
    const request = require('request');
    const reg = /(http|https):\/\/([\w.]+\/?)\S*/ig
    let url = 'https://www.zcool.com.cn/';
    let html = '';
    http.get(url, (res) => {
        let { statusCode } = res; //状态码
        let contentType = res.headers['content-type']; //请求类型
        let err = null;
        if (statusCode !== 200) {
            err = new Error('请求状态错误')
            //请求状态错误
        } else if (!/^text\/html/.test(contentType)) {
            //请求类型错误
            err = new Error('请求类型错误')
        }
        if (err) {
            console.log(err);
            res.resume(); //重置缓存
            return;
        }
        res.on('data', (chunk) => {
            html += chunk.toString('utf8');
            // console.log('爬取中')
        });
        res.on('end', () => {
            console.log(html);
            const $ = cheerio.load(html)
            $('img').each((index, el) => {
                if (reg.test($(el).attr('src'))) { //判断是否是以http 或者https 开头的
                    request($(el).attr('src')).pipe(  //下载到本地
                        fs.createWriteStream(`./node/${index}.png`).on('close', err => {
                            console.log('下载成功', err)
                        })
                    )
                }
            })
        })
      }).on('error', (e) => {
         console.log('出错啦');
      })
    
    

    02

    通过express 框架书写api

    1.安装 express

    npm install express --save
    

    模块(第三方模块)的引用 从当前目录的node_modules依次向上寻找。

    服务器相关

    服务器:

    1.服务器本质就是一台电脑

    2.需要服务器软件(apatch tomcat iis nginx node)

    3.服务器ip和端口号 (80)

    局域网:服务器通过(无线)网线连接 每一台电脑都被分配一个ip

    外网:

    ip:确定服务器主机的位置

    port:是确定服务器里程序的位置

    api接口

    构成要素:ip port pathname method(get,post) data

    • 接收数据
      • get req.query
      • post req.body 需要body-parser模块
        • 注意数据格式
          • json (app.use(bodyParser.json()))
          • x-www-form-urlencoded (app.use(bodyParser.urlencoded(extended:false)))
          • formdata

    中间件 middlewear

    • 内置中间件 static
    • 自定义中间件
    • 第三方中间件 (body-parser)

    中间件使用一定要注意 next);

    中间件有全局中间件,还有局部中间件,局部中间只走该中间件所在接口。

    static 静态目录 (类似apche www目录)

    path 模块 处理路径

    对文件操作 必须使用绝对路径

    __dirname 绝对路径

    static 模块使用

    app.use('./',express.static(path.join(__dirname,'./html')));
    
    

    此时 可以通过http://localhost:3000/index.html 访问html文件夹下的index.html文件

    node router

    防止一个文件写过多接口造成不良影响,拆分接口到对应文件

    server.js

    const express = require('express');
    const app = express();
    const bodyParser = require('body-parser');
    
    app.use(bodyParser.json());
    
    app.use('/',(req,res,next)=>{
        if(req.query.token || req.body.token){
            next()
        }else{
            res.send('no token')
        }
    });
    //引入各个router
    const userRouter = require('./router/user');
    const customRouter = require('./router/custom');
    //使用各个router
    app.use('/user',userRouter);
    app.use('/custom',customRouter);
    
    app.listen(3000,()=>{
        console.log('server start');
    })
    
    

    router/user.js

    const express = require('express');
    const router = express.Router(); //实例化router
    
    router.get('/login',(req,res)=>{
        res.send('login ok')
    })
    
    router.get('/logout',(req,res)=>{
        res.send('logout ok');
    })
    
    module.exports = router; //导出
    
    

    非关系数据库 mongodb

    1.mongodb 下载 (下载速度慢问题解决mongodb下载慢问题

    2.安装 建议参考菜鸟教程 mongodb安装 菜鸟教程mongodb windows安装

    指令

    mongodb 数据库名

    mongod 命令行启动数据库

    mongo 命令行操作数据库指令

    mongoose node操作mongodb的插件

    promise

    为什么要是用promise?

    大量的异步操作如果需要顺序执行通过回调函数会形成回调地狱

    通过promise解决回调地狱

    1.封装promise函数

    //使用形式
    //返回promise
    function test(){
      return new Promise((resolve,reject)=>{
        //异步处理
        //成功的时候
        resolve();
        //失败的时候
        reject();
      })
    }
    

    2.根据顺序,形成链式调用
    test().then().then();

    在第一个then中需再return出一个promise对象

    3.捕获错误

    示例代码:

    const fs = require('fs');
    //回调形式
    // fs.stat('./aaa.js',(err,stats)=>{ 
    //     if(err){
    //         console.log('文件不存在')
    //     }else{
    //         console.log('文件存在');
    //         fs.unlink('./aaa.js',(err)=>{
    //             if(err){
    //                 console.log('删除错误');
    //             }else{
    //                 console.log('删除正确');
    //             }
    //         })
    //     }
    // })
    
    //promise形式
    function fileIsExsit(){
        return new Promise((resolve,reject)=>{
            fs.stat('./aaa.js',(err,stats)=>{
                if(err){
                    console.log('不存在')
                    reject('err')
                }else{
                    console.log('存在')
                    resolve('yes')
                }
            })
        })
    }
    
    function filedel(){
        return new Promise((resolve,reject)=>{
            fs.unlink('./aaa.js',(err)=>{
                if(err){
                    reject('删除失败')
                }else{
                    resolve('删除成功')
                }
            })
        })
    }
    
    fileIsExsit()
    .then((data)=>{
        console.log(data,'------------then')
        return filedel();
    })
    .then((data)=>{
        console.log(data,'----then')
        //如何手动终止then?
        throw new Error('手动抛出错误,为了手动终止then执行');
    })
    .then((a)=>{
        console.log(111);
    })
    .catch((data)=>{
        console.log(data,'------------catch')
    })
    

    mongoose

    mongoose:为了方便操作数据库而诞生的一个模块

    1.安装mongoose:

    npm install mongoose
    

    2.引入模块,连接数据库

    const mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/users',{ useNewUrlParser: true,useUnifiedTopology: true })
    //第一个参数为本地数据库,第二个参数为mongoose.connect()方法新增的参数,否则会warning
    
    

    3.创建数据库连接对象

    var db = mongoose.connection;
    db.on('error',console.error.bind(console,'connection error:')) //监听错误
    db.on('open',function(){ //监听连接成功
      console.log('数据库连接成功');
    })
    

    4.创建schema对象

    var userSchema = new mongoose.Schema({
      name:String,
      age:Number,
      sex:{
        type:Number,
        default:0
      },  
    })
    

    5.创建model结构

    //将schema对象转换为数据模型
    //将schema对象与集合关联('集合名',schema对象);注意:集合名mongoose会自动转换成复数形式因此建议集合名使用复数形式
    var User = mongoose.model('users',userSchema);
    

    6.操作数据库

    //增 
    //insertMany([options]) options 可为数组 可为对象 是追加操作,会追加到当前表的数据末尾
    // User.insertMany([{name:'tll',age:18},{name:'zt',age:18,sex:0},{name:'ah',age:18,sex:0}])
    // .then((data)=>{
    //     console.log(data);
    //     console.log('插入成功');
    // })
    // .catch((data)=>{
    //     console.log('插入失败');
    // })
    
    // 删
    // deleteOne([option]) 删除一条数据,option为筛选条件,当有多条数据符合该条件时,只删除第一个查到的,当option为空时默认删除第一条 
    // deleteMany([option]) 删除多条数据,option为筛选条件,当options为空时删除该集合所有数据 
    
    // User.deleteMany({name:'zt'})
    // .then((data)=>{
    //     console.log(data,'删除所有成功')
    // })
    // .catch((data)=>{
    //     console.log(data,'删除所有失败');
    // })
    
    // 改
    // update(query,update) query为查询条件,update为修改后的数据 如果匹配到多条数据只修改第一条
    // updateMany(query,update) 批量修改query为查询条件,update为修改后的数据 
    // updateOne(query,update) 与update()相同,只修改匹配到的第一条数据
    // User.update({sex:1},{age:90})
    // .then((data)=>{
    //     console.log(data,'更新成功');
    // })
    // .catch((data)=>{
    //     console.log(data,'更新失败')
    // })
    
    // 查
    /*
    mongodb 条件操作符
    $gt >
    $lt <
    $gte >=
    $lte <=
    $ne !=
    $eq =
    模糊搜索
    {title:/教/} title中包含教的文档
    {title:/^教/} title中以教开头的文档
    {title:/教$/} title中以教结尾的文档
    */
    
    //find(query) // 查询符合条件的所有数据 
    //findOne() //查询符合条件的第一条数据
    //findById() //通过id查找一条数据,该id为mongodb自动生成的id 在查找前会对id进行数据转换
    // User.findById("5e70dcf88963670128d1c20e")
    // .then((data)=>{
    //     console.log(data,'查询成功')
    // })
    // .catch((data)=>{
    //     console.log(data,'查询失败')
    // })
    
    

    使用mongodb + nodeJs实现一个登录注册逻辑

    1.项目结构

    - server.js   //服务
    - db  //数据库管理
      - model 
        - userModel.js //创建userModel对象
      - connect.js  //连接数据库
    - router //路由
      - useRouter.js //接口具体逻辑
    

    2.项目需求分析

    1.注册

    先判断数据库中是否有userName,有则返回账号已存在,注册失败,否则写入数据库,标识注册成功。

    2.登录

    根据userName 和 password 判断数据库中是否存在该条数据,存在则表示登录成功,否则表示失败。

    server.js

    const express = require('express');
    const db = require('./db/connect');
    const app = express();
    const userRouter = require('./router/userRouter');
    const bodyParser = require('body-parser');
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({extended:false}))
    
    app.use('/user',userRouter);
    
    app.listen(3000,()=>{
        console.log('server start');
    })
    

    connect.js

    const mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/users',{ useNewUrlParser: true,useUnifiedTopology: true });
    const db = mongoose.connection;
    db.on('error',()=>{
    })
    db.once('open',()=>{
        console.log('connect ok');
    })
    

    userModel.js

    const mongoose = require('mongoose');
    
    var userModel = new mongoose.Schema({
        us:{
            type:String,
            default:''
        },
        ps:{
            type:String,
            default:'',
        },
        uid:{
            type:String,
            default:''
        }
    })
    var user = mongoose.model('users',userModel);
    module.exports = user;
    

    userRouter.js

    const express = require('express');
    const router = express.Router();
    const User = require('../db/model/userModel');
    router.post('/reg',(req,res)=>{
        let{us,ps} = req.body;
        if(!us || !ps){
            res.send({
                err:-1,
                msg:"参数错误"
            })
        }else{
            User.find({us})
            .then((data)=>{
                if(data.length >= 1){
                    res.send({
                        err:-1,
                        msg:"该用户已注册"
                    })
                }else{
                  return User.insertMany({us,ps,uid:us+ps})
                }
            })
            .then((data)=>{
                if(data){
                    res.send({
                        err:0,
                        msg:'注册成功'
                    })
                }
                console.log(data,'insert')
            })
            .catch((err)=>{
                res.send({
                    err:-1,
                    msg:'系统内部错误,请稍后重试'
                })
            })
        }
    })
    router.post('/login',(req,res)=>{
        let {us,ps} = req.body;
        if(!us || !ps){
            res.send({
                err:-1,
                msg:'参数错误'
            })
        }else{
            User.find({us,ps})
            .then((data)=>{
                console.log(data,'50000');
                if(data){
                    res.send({
                        err:0,
                        msg:'注册成功',
                        us:data[0].us,
                        uid:data[0].uid
                    })
                }
            })
            .catch((err)=>{
                res.send({
                    err:-1,
                    msg:'系统内部错误'
                })
            })
        }
    
    })
    module.exports = router;
    

    跨域问题

    ajax 同源策略 协议 域名 端口号

    解决跨域:

    • 1.cors:后端配合
      • express cors
      • app.all()
    app.all("*",function(req,res,next){ //设置允许跨域的域名“*”代表允许所有
        //允许的请求头
        res.header("Access-control-Allow-Origin","*");
        //设置允许的请求方式
        res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
        if(req.method.toLowerCase() == 'options'){
            res.send(200)
        }else{
            next();
        }
    })
    
    

    上传图片

    npm install multer --save
    
    //后端
    var storage = multer.diskStorage({
        destination:function(req,file,cb){
            cb(null,'./uploads'); //定义文件存放地址
        },
        filename:function(req,file,cb){ //定义上传的文件名
            let types = file.originalname.split('.');
            let type = types[types.length - 1]; //取后缀
            cb(null,file.fieldname+'-'+Date.now()+parseInt(Math.random()*9999)+'.'+type);
        }
    })
    
    
    var upload = multer({storage:storage}); //调用
    
    router.post('/imgs',upload.single('keys'),(req,res)=>{  //这个keys 属于约定,前后端都得知道
        const {mimetype,size,filename} = req.file;
        let types = ['jpg','png','jpeg','gif'];
        let imgtype = mimetype.split('/')[1];
        if(size>102400){  //上传大小判断
            res.send({
                err:-1,
                msg:'图片尺寸超过10k'
            });
            return;
        }else if(types.indexOf(imgtype)<=-1){  //文件类型判断
            res.send({
                err:-1,
                msg:"图片类型错误"
            })
            return;
        }else{
            res.send({
                err:0,
                msg:"上传成功",
                url:`/img/${filename}` //上传成功后将地址返回给前端
            })
        }
    })
    
    //前端
    
            $('button').click(function(){
                let formData = new FormData();
                let file = $('#file')[0].files[0];
                formData.append('keys',file);
    
                $.ajax('http://localhost:3000/upload/imgs',{
                    method:'POST',
                    cache:false,
                    data:formData,
                    processData:false,
                    contentType:false,
                    success:function(data){
                        console.log(data);
                        $('img').attr('src',`http://localhost:3000${data.url}`)
                    }
                })
    
                console.log(file);
            })
    

    相关文章

      网友评论

        本文标题:node.js学习笔记

        本文链接:https://www.haomeiwen.com/subject/cvjiehtx.html