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 能做什么
- 已经封装好服务器
- 已经封装好路由
- 已经封装好中间件
- 已经封装好网络请求...
使用方法:
- npm 安装 Express 框架
- 引入 Express 模块
- 实例化 Express 对象
- 通过对象进行调用各种方法
//引入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模板引擎的特点
- 快速编译和渲染
- 简单的模板标签
- 支持浏览器端和服务器端
- 支持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页面 参一:渲染对应的文件 参二:传递的数据 在对应的文件中使用 ‘欢迎来到 <%=name %> 网页!
来接收’
})
//监听服务器端口号
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'};
年龄:<%= data.age %>
姓名:<%= data.name %>
// let data = [{age:23,name:'andy'},{age:26,name:'kitty'},{age:24,name:'jack'}];
<% data.forEach((item) => {%>
- 年龄: <%= item.age%>
- 姓名: <%= item.name%>
<% })%>
//<%%>标签必须每行都需要闭合,如遇到代码换行时,必须将每行都用<% %>包裹
//let data = [{age:23,name:['张三','李四']},{age:26,name:['王五','赵六']},{age:24,name:['傻大个','胖子']}];
<% data.forEach((item) => {%>
- 年龄: <%= item.age%>
- 姓名: <% item.name.forEach((item2) => { %>
<%= item2%>
<%}) %>
<% })%>
公共模板
- 使用EJS文件替换HTML文件
- 创建导航(公共模板)
- 解决外部样式不可用问题
将html页面全部使用ejs文件进行开发,文件结构可以参考以下
- 一般页面视图放在views文件夹中
- 封装的公共模块放在public文件夹中
- 静态资源放在assers文件夹中
将封装好的公共模块引入到对应的页面即可正常渲染
<% include ../public/nav.ejs%>
在页面中引入外部样式时,需要先在app.js中进行配置,方可引用
//app.js
//让服务器识别外部样式表
app.use('/assets',express.static('assets'));
//参一:路径 参二:该路径文件做什么事
//此时的参二表示:在项目中,让css静态文件转化为模块化,让服务器进行识别。否则加载样式表会报错
//各文件中正常引用
实战操作 ToDoApp
启动新项目
初始化webpack
安装express框架
安装esj body-parser 模块
- 新建项目文件夹TodoApp,并在该文件夹中安装 package
npm init
- 安装express 框架
npm install express --save-dev
- 安装 ejs 和 body-parser 模块
npm install ejs body-parser --save-dev
- 在项目文件夹中创建主文件的入口app.js
- 新建controller文件夹并添加todoController.js文件来处理项目中对应的逻辑
- 新建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
设置视图
设置请求页面返回视图
设置视图样式
- 在项目文件夹中新建views文件夹并添加todo.ejs文件并完成静态页面布局
- 将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
todoApp
<% todos.forEach((val,ind) => {%>
-
<%= val.item%>
<% })%>
-
邓伦最帅!
因为主要为node.js的学习,此处的样式省略,之后会抛出代码提供参考。
mongoose
安装mongoose模块
使用mongoDB网络数据库
创建数据库及配置用户名及密码
链接mongoDB数据库
- 安装mongoose
npm install mongoose --save-dev
- 在mongodb 官网注册账号并新建配置数据库
- 链接数据库,在 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哈哈~
源码附上