Node
核心模块
- fs 文件操作模块
- http 网络服务构建模块
- os 操作系统模块
- path 路径处理模块
- url 路径操作模块
模块系统
-
require模块查找机制
-
优先从缓存加载
-
核心模块
-
以路径形式加载模块
-
第三方模块
-
由于在a中已经加载b了,不会重复加载,但可从里面拿到接口对象,避免重复加载,提高效率
-
模块引入语法,加载require
var 自定义变量名称 = require("模块");
-
作用
- 加载文件模块并且执行里面的代码
- 拿到被加载文件模块exports导出的接口对象
-
模块导出
- 推荐使用
module.exports
.exports
是module.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-template
和art-template
前者依赖后者 - 配置
- 在node中不止有 art-template 还有ejs,pug handlebars
- 当 express 使用 art-template 时需要装
// 下面第一个参数为文件的后缀名
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等详情看官网