node基础2

Node

核心模块

  • fs 文件操作模块
  • http 网络服务构建模块
  • os 操作系统模块
  • path 路径处理模块
  • url 路径操作模块

模块系统

  • require模块查找机制

  • 优先从缓存加载

  • 核心模块

  • 以路径形式加载模块

  • 第三方模块

  • 由于在a中已经加载b了,不会重复加载,但可从里面拿到接口对象,避免重复加载,提高效率

  • 模块引入语法,加载require

var 自定义变量名称 = require("模块");
  • 作用

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

    • 推荐使用module.exports. exportsmodule.exports引用
    • node中是模块作用域,默认文件中所有的成员只在当前文件模块起作用
    • 对于这些需要被其他模块访问的的成员,需要在exports上挂载,导出多个成员对象
var exports = module.exports;
  • 导出多个成员
exports.add = () => {};
exports.num = 2;
  • 导出单个成员
module.exports = function() {};
  • 第三方模块加载过程
//以 art-template 为例
var template = require("art-template");
//先找到当前文件所处目录中的 nodu_modules 目录
// nodu_modules/art-template
// nodu_modules/art-template/package.json 文件
// nodu_modules/art-template/package.json 文件中的 main 属性
// main 属性中记录了 art-template 的入口模块

// 如果 package.json 文件不存在或者 main 指定的入口模块没有
// 则 node 会找 nodu_modules/art-template 目录下的 index.js
// index.js 会作为一个备选项

// 当前目录没有就上一级目录的 nodu_modules
// 上一级目录没有就上上一级目录的 nodu_modules ...
// 直到磁盘根目录还没有,最后报错:
// can not find module xxx

服务器级别的 API

  • fs – 操作文件
const fs = require("fs"); // 必须要引入"fs" 模块

// 读文件
fs.readFile("./data/hello.txt", (error, data) => {
  // 文件中存储的都是二进制
  // 二进制转为16进制l
  console.log(data); //<Buffer 68 65 6c 6c 6f 20 68 61 6e 67 66 65 6e 67 0a>
  console.log(data.toString()); //hello hangfeng
  console.log(error); // null
  // 如果出错 data为undefined
});

// 写文件

// 只接受一个参数
fs.writeFile("./data/hi.txt", "hello yingying", error => {
  console.log("success");
  console.log(error); // null
});
  • http
const http = require("http");
const server = http.createServer();
server.on("request", function(request, response) {
  console.log("收到请求");
  // res.setHeader('Content-type','text/plain;charset=utf-8')// 告诉浏览器按什么模式来解析
  response.write("hello hangfeng");
  response.end();
});
server.listen(3000, () => {
  console.log("loading");
});
  • 重定向
// 客户端收到服务器响应的状态码是302 自动去响应头中找location
res.statusCode = 302;
res.setHeader("Location", "/");

安装nodemon自动重启程序

  • 安装方式

npm i --global nodemon

启动node.js程序就可以直接

nodemon file.js

这样启动后改变node.js文件后 nodemon会自动帮助我们重启

npm

  • 基本命令
    • –global (全局安装,当前在那个目录都可以) 缩写 -g
    • npm install filename (安装 filename) filename:包名
    • npm i filename 效果与上一条相等 i 是 install 的缩写
    • npm unstall filename 删除包名

express

  • 静态资源
    • 直接访问http://127.0.0.1:3000/hello.txt
var express = require("express");
var app = express();
app.use(express.static("./public/")); // 访问public目录下不需要/public/
  • 加上/public/访问http://127.0.0.1:3000/public/hello.txt
var express = require("express");
var app = express();
app.use("/public/", express.static("./public/")); // 访问public目录需要/public/
  • art-template
    • 当 express 使用 art-template 时需要装express-art-templateart-template前者依赖后者
    • 配置
    • 在node中不止有 art-template 还有ejs,pug handlebars
// 下面第一个参数为文件的后缀名
app.engine("html", require("express-art-template"));
// express 为 response 相应对象提供一个方法: render
// render 默认无法使用,需要配置模版引擎
// res.render('html模版名',{模版数据})
// 第一个参数不写路径,默认到项目目录中 views 目录查找文件
// 就是说 express 有一个约定: 开发人员把所有视图文件都放到 views 目录中

// 如果希望改路径 app.set('views',目录路径)
app.get("/", (req, res) => {
  res.render("index.html");
});
  • 获取 post 请求的数据
    • bodyParser
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//使用
app.post("/post", (req, res) => {
  const comment = req.body;
  comment.dateTime = Date();
  comments.unshift(comment);
  // 重定向
  res.redirect("/");
});

封装调用

  • 调用文件
const express = require("express");

var router = express.Router();

var Student = require("../student");

router.get("/students", (req, res) => {
  // fs.readFile('./data/db.json', 'utf8', (err, data) => {
  //   if(err){
  //     return res.status(500).send('error')
  //   }
  //   // console.log(typeof data) // string
  //   // 文件读取的是字符串,需要手动转成对象
  //   var students = JSON.parse(data).students
  //   res.render('student-index.html', {
  //     fruits: [
  //       '西瓜',
  //       '苹果',
  //       '橘子',
  //       '香蕉'
  //     ],
  //     students
  //   })
  // })

  Student.find((err, students) => {
    if (err) {
      return res.status(500).send("error");
    }
    res.render("student-index.html", {
      fruits: ["西瓜", "苹果", "橘子", "香蕉"],
      students
    });
  });
});

router.get("/students/new", (req, res) => {
  res.render("students-new.html");
});

router.post("/students/new", (req, res) => {
  Student.save(req.body, err => {
    if (err) {
      return res.status(500).send("error");
    }
    res.redirect("/students");
  });
});

router.get("/students/edit", (req, res) => {
  Student.findById(parseInt(req.query.id), (err, student) => {
    if (err) {
      return res.status(500).send("error");
    }
    res.render("students-edit.html", {
      student
    });
  });
});

router.post("/students/edit", (req, res) => {
  Student.updataById(req.body, err => {
    if (err) {
      return res.status(500).send("error");
    }
    res.redirect("/students");
  });
});

router.get("/students/delete", (req, res) => {
  Student.deleteById(parseInt(req.query.id), err => {
    if (err) {
      return res.status(500).send("error");
    }
    res.redirect("/students");
  });
});

module.exports = router;

// module.exports = ( app ) => {
//   app.get('/', (req, res) => {
//     fs.readFile('./data/db.json', 'utf8', (err, data) => {
//       if(err){
//         return res.status(500).send('error')
//       }
//       // console.log(typeof data) // string
//       // 文件读取的是字符串,需要手动转成对象
//       var students = JSON.parse(data).students
//       res.render('student-index.html', {
//         fruits: [
//           '西瓜',
//           '苹果',
//           '橘子',
//           '香蕉'
//         ],
//         students
//       })
//     })
//   })
// }

  • 封装文件
const fs = require("fs");

var dbPath = "./data/db.json";
//找学生
exports.find = function(callback) {
  fs.readFile(dbPath, "utf8", (err, data) => {
    if (err) {
      return callback(err);
    }
    callback(null, JSON.parse(data).students);
  });
};

exports.save = function(student, callback) {
  fs.readFile(dbPath, "utf8", (err, data) => {
    if (err) {
      return callback(err);
    }
    var students = JSON.parse(data).students;
    student.id = students[students.length - 1].id + 1;
    students.push(student);
    var fileData = JSON.stringify({
      students
    });
    fs.writeFile(dbPath, fileData, err => {
      if (err) {
        return callback(err);
      }
      callback(null);
    });
  });
};

//更新
exports.updataById = (student, callback) => {
  fs.readFile(dbPath, "utf8", (err, data) => {
    if (err) {
      return callback(err);
    }
    var students = JSON.parse(data).students;
    // EcmaScript 6 find
    var stu = students.find(item => {
      return item.id === student.id;
    });
    // 遍历拷贝对象
    for (var key in student) {
      stu[key] = student[key];
    }
    var fileData = JSON.stringify({
      students
    });
    fs.writeFile(dbPath, fileData, err => {
      if (err) {
        return callback(err);
      }
      callback(null);
    });
  });
};

// 根据id找学生
exports.findById = function(id, callback) {
  fs.readFile(dbPath, "utf8", (err, data) => {
    if (err) {
      return callback(err);
    }
    var students = JSON.parse(data).students;
    var stu = students.find(item => {
      return item.id === id;
    });
    callback(null, stu);
  });
};
// 根据ID删除学生
exports.deleteById = function(id, callback) {
  fs.readFile(dbPath, "utf8", (err, data) => {
    if (err) {
      return callback(err);
    }
    var students = JSON.parse(data).students;
    // 返回元素下标
    var deleteId = students.findIndex(item => {
      return item.id === id;
    });
    // 删除
    students.splice(deleteId, 1);
    var fileData = JSON.stringify({
      students
    });
    fs.writeFile(dbPath, fileData, err => {
      if (err) {
        return callback(err);
      }
      callback(null);
    });
  });
};

package-lock.json

  • 提高下载效率
  • 锁定当前第三方库的版本

callback异步编程 js中的一等公民是函数

  • 一般情况下,把函数作为参数的目的是为了获取函数内部的异步操作结果,一定会用到回调函数来获取,在封装的函数内部调用传递进来的函数 函数既能当参数,又能当返回值
  • JavaScript单线程 事件循环

function add(x,y){
    console.log(1)
    setTimeout(function(){
        console.log(2)
        var sum = x + y
        return sum
    },1000)
    console.log(3)
}
console.log(add(2,4))

//定义一个变量一样来封装一个带有回调函数的方法。js一大特色,异步编程,不易搞懂,多写多练

function des(x,y,callback){
    //callback就是回调函数
    //var x = 10
    //var y = 4
    //var calllback = function(count){console.log(count)}
    console.log(1)
    setTimeout(function(){
        var count = x - y
        callback(count)
    },1000)
}
des(6,4,function(a){//a接收上面定义的callback中的count
    //现在的结果可以拿到任何值
    console.log(a)
})

function fn(callback){
    setTimeout(function(){
        var data = '测试'
        callback(data)//真正的实参 callback是回调函数
    },1000)
}

fn(function(data){//形参
    console.log(data)
//上层定义,下层封装

封装一个ajax方法


  function get(url,callback){
            var xmlHttp = new XMLHttpRequest()
            xmlHttp.onload = function(){
                callback(xmlHttp.responseText)

            }
            xmlHttp.open('get',url,true)
            xmlHttp.send()
        }
        get('http://a.itying.com/api/cartlist?uid=a001',function(data){
            console.log(data)
        })

js天生不支持模块化

  • require
  • exports
  • node才有

在浏览器也可以向node中模块一样编程

  • 利用script进行标签引入,学注意引入顺序问题
  • 基于require.js
  • sea.js库进行模块化编程
  • 无论是 common.js AMD CMD(非官方)) UMD ES6模块化都是为了解决js不支持模块化问题

Promise

  • promise readFile 函数封装
const fs = require('fs')

function promiseReadFile(filePath){
 return new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if(err){
        reject(err)
      }
      else{
        resolve(data)
      }
    })
  })
}

promiseReadFile('./data/hello.txt')
  .then((data) => {
    console.log(data)
    return promiseReadFile('./data/learn.txt')
  })
  .then((data) => {
    console.log(data)
    return promiseReadFile('./data/play.txt')
  })
  .then((data) => {
    console.log(data)
  })

Path模块

  • 路径
MacBook-Pro$ node
// 获取一个路径的文件名(包含扩展名)
> path.basename('/a/b/index.js')
'index.js'
> path.basename('/a/b/index.js','.js')
'index'
> path.basename('/a/b/index.js','.html')
'index.js'
// 获取文件路径目录部分
> path.dirname('/a/b/index.js')
'/a/b'
// 获取文件扩展名部分
> path.extname('/a/b/index.js')
'.js'
> path.extname('/a/b/index.html')
'.html'
// 是否是绝对路径
> path.isAbsolute('/a/b/index.js')
true
> path.isAbsolute('a/b/index.js')
false
> path.isAbsolute('.a/b/index.js')
false
// 把路径转对象
> path.parse('/a/b/index.js')
{ root: '/',        // 根路径
  dir: '/a/b',      // 目录
  base: 'index.js', // 包含后缀名的文件名
  ext: '.js',       // 后缀名
  name: 'index' }   // 不包含后缀名的文件名
// 路径拼接 有效避免出错(自动修正)
> path.join('/a/','b')
'/a/b'
> path.join('/a/','/b')
'/a/b'
> path.join('/a/','/b','c','d')
'/a/b/c/d'
>

Node 特殊成员

  • __dirname 动态获取 获取当前文件模块所属目录的绝对路径
  • __filename 动态获取 获取当前文件的绝对路径
  • 解决以下问题:
    • node的文件操作路径中,相对路径设计的就是相对于执行 node 命令所处的路径

在node中mongodb使用

  • 基本使用查看菜鸟教程

  • 在node中使用mongodb

https://github.com/mongodb/node-mongodb-native

  • mongooes的使用
http://www.mongoosejs.net/

promise学习容器

  • 容器中存放一个异步任务 。
/*get('http://a.itying.com/api/cartlist?uid=a001')
.then(function(data){
    console.log(data)
})
//XMLHttpRequest is not defined
*/

function getPeomise(url,callback){
    return new Promise(function(resolve,reject){
        var xmlHttp = new XMLHttpRequest()
        xmlHttp.onload = function(){
            callback && callback(xmlHttp.responseText)
            resolve(xmlHttp.responseText)
        }
        xmlHttp.onerror = function(err){
            callback && callback(err)
            reject(err)
        }
        xmlHttp.open('get',url,true)
        xmlHttp.send()
    })
}
getPeomise('http://a.itying.com/api/cartlist?uid=a001',function(data){
    console.log(data)
})

mongoose支持所有peomise api

path模块

  • path.join(),用于路径智能拼接
  • path.basename 文件名
  • path.dirname 目录名
  • path.extname 文件后缀
  • path.parser 解析区分整个文件 用于文件拼接

node的其他成员

  • 在node中,除了require,exports等相关模api之外,还有__dirname,__filename成员
  • __dirname用来动态获取当前文件所属目录的绝对路径
  • __filename用来动态获取当前文件的绝对文件
  • 用来解决文件操作路径的相对路径问题
  • 这两个成员不受node命令所属命令影响,在文件操作中,相对路径相对执行‘node’命令所处目录
  • 方式 path.join(_dirname,'文件名')

art-template

  • include
  • extend
  • block

在拼接路径的时候,为避免手动拼接犯错,推荐使用path.join与上面的成员进行辅助拼接,在文件操作中使用的相对路径统一转为动态的绝对路径

  • 模块中的路径标识和文件操作中的相对路径标识不一致
  • 模块中的路径标识就是相对于当前文件模块,不受执行node命令所处路径的影响

模板页设置

  • 将所有页面的公共内容放在一起模板页面,为了孩子有自己的样式与内容需要父模板留坑
<!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>{{block 'title'}}默认航锋{{/block}}</title>
    <link rel="stylesheet" href="/public/css/main.css" />
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
      integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
      crossorigin="anonymous"
    />
    {{block 'head'}}{{/block}}
  </head>

  <body>
    {{include '../_partials/header.html'}}
    <!--留一个坑 -->
    {{block 'body'}}
    <h1>默认内容</h1>
    {{/block}}
    {{include '../_partials/footer.html'}}
    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
    <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
    {{block 'script'}}{{/block}}
  </body>
</html>

{{extend './layout.html'}}

 {{block 'head'}}
 <style>
 body{
   color:red
 }
 </style>
 {{/block}}


 {{block 'body'}}
  <h1>子页面</h1> 
  {{/block}}

jquery提交请求

<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.error_code;
            if (err_code === 0) {
              // 服务端重定向针对异步请求无效
              window.location.href = "/";
            } else if (err_code === 1) {
              console.log("邮箱或者昵称已存在");
            } else if (err_code === 500) {
              console.log("服务器忙请稍后重试!");
            }
          }
        });
      });
    </script>

express-session

  • 在express中不支持cookie与session,可以利用第三方中间键件express-session
https://github.com/expressjs/session
var session = require('express-session')


app.use(session({
    secret: 'keyboard cat',//加密字符串
    resave: false,
    saveUninitialized: true,//无论你是否使用session,都会默认分配一把钥匙
    cookie: {
        maxAge:1000*60*30
    },
    rolling:true
}))

添加session数据
req.session.foo = 'bar'
读
req.session.foo
删除session
delete req.session.foo
session数据是内存存储的,服务器一旦重启就会消失,真正的生产环境需做持久化处理

express中间键

http://www.expressjs.com.cn/guide/using-middleware.html

  • 在Express中,对中间件有几种分类
  • 1.不关心路径与请求方法的中间件,任何请求都可以都会进入这个中间件
  • 2.中间键本身是一个方法,该方法接收三个参数
  • Request请求对象,Response响应对象,next下一个中间件
  • 当一个请求进入一个中间件后,如果不调用next,将不会执行下一个中间件
  • 调用next方法也是要匹配的(不是调用紧挨着的那一个),当请求进来,
  • 会从第一个中间件开始进行匹配,如果不匹配则继续匹配下一个
(万能匹配)
app.use(function(req,res,next){
    console.log('1')
    next()
})

只要以/xxx开头
app.use('/a',function(req,res,next){
    console.log('1')
    next()
})

第二种中间件

  • 除了以上中间件外,还有一种常用的中间件
  • 严格匹配请求方法和请求路径的中间件

app.get('/',function(req,res,next){
    console.log('/')
})


app.post('/',function(req,res,next){
    console.log('/')
})

```put

app.put('/',function(req,res,next){
    console.log('/')
})


app.delete('/',function(req,res,next){
    console.log('/')
})

//当调用next的时候,如果传递了参数,直接找带有四个参数的方法
//当发生错误的时候,我们可以调用next传递错误对象
//然后被全局错误处理中间件到并处理

app.use(function(err,req,res,next){
    console.error(err,stack)
})
  res.status(500).send('Something broke')


  例子

  fs.readFile('./text1',function(data,error){
    if(error){
      next(err)
    }
})

直接利用next方法将错误抛向带有四个参数的中间件

app.use(function(err,req,res,next){
    res.status(500).send(err.message)
})

内置中间件

  • express.static
  • express.json
  • express.urlencoded

第三方中间件

  • cookie-parser等详情看官网
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值