简介
把JavaScript代码放在浏览器运行是前端开发,
JavaScript代码放在node.js运行是后端开发
Node.js中无法调用 DOM、BOM、Ajax等浏览器的内置API
Node.js的js运行环境由 V8引擎 + node的内置API(fs path http)
一、终端打开 node
终端有 :cmd 和 powershell(推荐) 两种方式打开终端
cmd打开终端:window+R 输入cmd打开 / 在当前文档目录下直接打开
powershell打开终端:在当前目录按住 Shift+鼠标右键 用powershell窗口打开
终端的常用快捷键:
二、fs 文件系统模块
1. fs读取文件 readFile()
// 1. 导入 fs 模块,来操作文件
const fs1 = require('fs')
// 2. 调用 readFile() 方法读取文件
//参数1:读取文件的存放路径
//参数2:读取文件时采用的编码格式,默认utf-8(可省)
//参数3:回调函数,可拿到读取失败和成功的结果 err dataStr
fs1.readFile('./fs.txt','utf-8',function(err,dataStr){
// 打印失败的结果
// 如果读取成功,err 的值为 null并打印成功的结果
// 如果读取失败,err 的值为 错误对象,dataStr的值为undefined
console.log(err);
console.log('----------');
// 打印成功的结果
console.log(dataStr);
})
判断读取文件是否成功:
const fs = require('fs')
fs.readFile('./fs.txt','utf8',function(err,dataStr){
if (err){ // err != null表示失败
return console.log("读取文件失败",err.message);
}
return console.log("读取文件成功",dataStr);
})
2. fs写入文件 writeFile()
写入会覆盖文件内原本的内容。
// 1. 导入 fs 模块
const fs = require('fs')
// 2. 调用 fs.writeFile() 方法 写入文件内容
//参数1:文件存放的路径
//参数2:表示要写入的内容
//参数3:(可省)编码格式 默认:utf8
//参数4:回调函数
fs.writeFile('./fs2.txt','Hello fs文件编译',function(err){
// 如果文件写入成功,则 err 值为null
// 如果写入失败,err 的值为 错误对象
console.log(err);
})
判断写入文件是否成功:
const fs = require('fs')
fs.writeFile('./fs2.txt','Hello fs文件编译',function(err){
if (err){
return console.log("写入文件失败",err.message);
}
return console.log("写入文件成功");
})
3. fs 的路径 __dirname / __filename
__filename 当前文件路径
__dirname 当前文件夹目录路径
const fs1 = require('node:fs')
const path = require('node:path')
// 1. 出现路径拼接错误问题,是因为提供了 ./ 或 ../ 开头的相对路径(动态)
fs1.readFile('./fs.txt','utf8',function(err,dataStr){
if (err){
return console.log("路径错误",err.message);
}
return console.log("路径正确",dataStr);
})
// 2. 如果解决这个问题,直接提供绝对路径
// 虽然可以解决路径动态问题,但移植性差 不利于维护
fs1.readFile('C:\\Users\\Mario\\Desktop\\VSCode-HTML\\web\\files\\fs.txt','utf8',function(err,dataStr){
if (err){
return console.log("路径错误",err.message);
}
return console.log("路径正确",dataStr);
})
// 3. __dirname 表示当前文件所处的目录的绝对路径
console.log(__dirname);
fs1.readFile(path.join(__dirname,'./fs.txt'),'utf8',function(err,dataStr){
if (err){
return console.log("读取路径错误",err.message);
}
return console.log("读取路径正确",dataStr);
})
4. fs添加文件 appendFile()
如果可以找到要添加文件的目录,会直接在文件内容后面添加内容,不会覆盖。
如果没有找到要添加文件的目录,会创建一个新的文件目录并添加内容。
const fs = require('fs')
const path = require('path')
//参数1:文件添加的路径
//参数2:表示要添加的内容
//参数3:(可省)编码格式 默认:utf8
//参数4:回调函数
fs.appendFile(path.resolve(__dirname,'./test.txt'),'123',(err,dataStr)=>{
return console.log(dataStr);
})
5. fs创建路径 fs.mkdir()
fs.rmdir()删除路径
// fs.mkdir() 创建路径
fs.mkdir(path.resolve(__dirname,'./hello'),(err)=>{
if (err) throw err
})
/* mkdir可以接收一个 配置对象作为第二个参数
recursive: true;(默认为false)--可以自动创建不存在的上一级目录
*/
fs.mkdir(path.resolve(__dirname,'./hello/abc'),{recursive: true},(err)=>{
if (err) throw err
})
三、path 路径处理模块
1. path.join() 路径拼接
凡是涉及到路径拼接问题,都要用 path.join() 方法进行处理。(不要直接使用 + 拼接)
path.resolve()方法类似,也可以使用。
// 导入 path 模块
const path = require('path')
// 注意:../会抵消前面的/c
const res = path.join('/a','/b/c','../','./d','./e')
console.log(res); // \a\b\d\e
fs1.readFile(path.join(__dirname,'./fs.txt'),'utf8',function(err,dataStr){
if (err){
return console.log("读取路径错误",err.message);
}
return console.log("读取路径正确",dataStr);
})
2. path.basename() 获取文件名
可以从一个文件路径中获取到文件的名称部分
// 导入 path 模块
const path = require('path')
// 有一个文件的存放路径:
const fpath = '/a/b/c/index.html'
// 1. path.basename(fpath) --会拿到:文件名.扩展名
const fullpath = path.basename(fpath)
console.log(fullpath); // index.html
// 2. path.basename(fpath,'.html') --只会拿到文件名,会移出扩展名
const fullpath = path.basename(fpath,'.html')
console.log(fullpath); // index
3. path.extname() 获取扩展名
// 导入 path 模块
const path = require('path')
// 可以从一个文件路径中获取到文件的名称部分
const fpath = '/a/b/c/index.html'
// path.extname(fpath) --会拿到: .扩展名
const fullpath = path.extname(fpath)
console.log(fullpath); // .html
四、http 服务器模块
1. 创建基本web服务器
// 1. 导入 http 模块
const http = require('http')
// 2. 创建 web 服务器实例
const server = http.createServer()
// 3. 为服务器实例绑定 request 事件,监听客户端的请求
server.on('request',(req,res)=>{ // request请求 response回应
// req.url 是客户端请求的 URL 地址
const url = req.url
// req.method 是客户端请求的 method 类型
const method = req.method
const str = `Your 请求 url is ${url},请求的方法类似是 ${method}`
// 为了防止中文显示乱码的问题,需要设置响应头 Content-Type 的值为 text/html;charset=utf-8
res.setHeader('Content-Type','text/html;charset=utf-8')
// 调用 res.end() 向客户端发送指定的内容,并结束这次请求的处理过程
res.end(str)
})
// 4. 启动服务器
server.listen(80,()=>{
console.log('server running at http://127.0.0.1');
})
2. 根据不同的 url 响应不同的 html页面 内容
const http = require('http')
const server = http.createServer()
// 用户触发服务器
server.on('request',(req,res)=>{
const url = req.url // 获取请求的 url 地址
let content = '<h1>404 Not Found!</h1>' // 默认的响应内容
if (url === '/' || url === '/index.html'){ // 判断用户请求的页面地址
content = '<h1>首页</h1>'
} else if (url === '/about.html'){
content = '<h1>关于页面</h1>'
}
// 防止中文乱码
res.setHeader('Content-Type','text/html;charset=utf-8')
res.end(content) // 内容响应给用户
})
// 启动服务器
server.listen(80,()=>{
console.log('server running at http://127.0.0.1');
})
五、模块化 require()
1. 加载用户自定义模块/作用域
无法访问另个模块内部的变量/函数,防止全局变量污染问题
// 在使用 require 加载用户自定义模块期间
const m1 = require('./fs.js')
//可以省略 .js 后缀名
const m1 = require('./fs')
2. module 对象
在每个 .js 自定义模块中都有一个 module 对象,它里面存储了和当前模块有关的信息
1. module.exports 对象的使用
向外共享模块域中的成员
使用 require() 方法导入模块时,导入的结果永远以 module.exports 指向的对象为准
3. exports 对象
简化 module.exports提供的 exports对象。默认情况下,exports 和 module.exports 指向的同一个对象。最终还是以 module.exports 指向的对象为准。
4. exports 和 module.exports 使用误区
时刻谨记:require() 模块时,得到的永远是 module.exports 指向的对象
注意:为了防止混乱,不要在同一个模块中同时使用 exports 和 module.exports
导出单个变量时可以用 exports
导出多个/一组变量时用 module.exports
5. 模块的加载机制
- 模块在第一次加载后会被缓存,意味着多次调用 require()不会导致模块的代码被执行多次。它们会优先从缓存中加载,从而提高加载效率。
- 内置模块是由 node.js 官方提供的,它的优先级最高
- 如果省略扩展名,node.js会以下面顺序依次加载:
4.如果没有找到对应的第三方模块,则会向上一层父目录中…直到找到根目录
六、包 与 npm
1. 安装 / 卸载包
使用 npm i 安装包所有的依赖,会自动安装新版本的包。如果需要换包的版本或者更新版本,通过 @ 符号指定具体的版本,例:npm i moment@2.23.4
版本号解释:
@2.23.4(大版本.新增功能版本.bug修复版本)
安装包会自动生成 node_modules 文件夹 和 package.json配置文件,里面记录包的版本等相关信息
以 moment 为例:
安装包:npm i moment
全局安装包:npm i 包名 -g (通常安装一些工具)
卸载包:npm uninstall moment
自动生成package.json配置文件:npm init -y
更新包版本:npm i moment@2.23.4
初始化项目
npm init
初始化项目,创建 package.json文件(需要回答问题)
npm init -y
初始化项目,创建 package.json文件(采用默认值,但文件目录的名字必须是英文,不然会出错)
npm镜像源
由于npm是国外地址,下载包非常慢 可以用国内淘宝镜像源
配置镜像有两种方式:
- 在系统中安装cnpm
npm install -g cnpm --registry=https://registry.npm.taob - 彻底修改npm仓库地址
npm set registry https://registry.npm.taobao.org/- 还原到原版仓库
npm config delete registry
- 还原到原版仓库
2.包与npm-格式化时间 moment()
首先下载:npm i moment
// 引入moment模块
const moment = require('moment')
// 参考官方文档可以自定义时间的格式化
const dt = moment().format("YYYY-MM-DD HH:mm:ss")
console.log(dt);
自定义:
// 自定义 日期时间格式函数
function dataFormat(dataStr){
const dt = new Date(dataStr)
const year = padZero(dt.getFullYear())
const month = padZero(dt.getMonth() + 1);
const day = padZero(dt.getDate())
const hours = padZero(dt.getHours());
const minutes = padZero(dt.getMinutes());
const seconds = padZero(dt.getSeconds());
return `${year}-${month}-${day} ${hours}-${minutes}-${seconds}`
}
// 补零函数:
function padZero(n){
return n > 9 ? n : '0' + n;
}
// 向外暴露
module.exports = {
dataFormat
}
3. 转义 和 还原html方法
// 转义 和 还原html方法
// 转义:
function htmlEscape(str){
return str.replace(/<|>|&|"/g, match=>{
switch(match){
case "<":
return "<"
case ">":
return ">"
case "&":
return "&"
case '"':
return """
}
})
}
// 还原
function htmlUnEscape(str){
return str.replace(/<|>|&|"/g,match=>{
switch(match){
case "<":
return "<"
case ">":
return ">"
case "&":
return "&"
case '"':
return '"'
}
})
}
// 向外暴露
module.exports = {
htmlEscape,
htmlUnEscape
}
4. 发布 / 删除 包
一定要注意发布包要在 最开始的npm上,不能在镜像源上发布,否则会出错。
- 查看本地npm是不是官方的 :
npm config get registry
- 设置npm官方源:
npm config set registry https://registry.npmjs.org
步骤:
- 使用
npm login
登录到npm,cd切换目录到你选择发布包的文件下 - 使用
npm publish
发布文件,可以登录到自己的npm官方账号查看是否已经存在这个包 - 使用
npm unpublish mario-soda --force
删除包
七、express
express 是基于 http 更简洁的写 web服务器 和 API服务器
npm i express@4.17.1
安装express
一、express的基本使用
1. express 创建服务器
// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()
// 3. 启动 web 服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
})
2. 监听 GET 和 POST 请求
// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()
// 4. 监听客户端的 GET和POST 请求,并向客户端响应具体内容
app.get('/user',(req,res)=>{
// 调用express提供的send()方法,向客户端响应一个 JSON 对象
res.send({name:'ww',age:19,gender:'女'})
})
app.post('/user',(req,res)=>{
res.send('请求成功!!')
})
app.get('/',(req,res)=>{
// 通过 req.query 可以获取到客户端发过来的 查询参数
// 注意:默认情况下,req.query是一个空对象
console.log(req.query);
res.send(req.query)
})
// 3. 启动 web 服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
})
1. param 传参
在路径中以冒号命名的部分我们称为param
param 传参一般不会传递特别复杂的参数
// :参数名可以随便取,也可以有多个 动态值
// /user/:id 表示用户访问 /user/XXX 时就会触发
app.get('/user/:id/:name',(req,res)=>{
// 通过 req.params 是动态匹配到的URL参数,默认是一个空对象
console.log(req.params);
res.send(req.params) //{"id":"1","name":"zs"}
})
2. 实现form表单和node的简易交互
form表单登录代码:
<!-- action的路径和后台要一样 -->
<form action="/login" method="post">
name:<br>
<input type="text" name="name"><br>
password:<br>
<input type="text" name="password">
<input type="submit" value="提交">
<br>
</form>
form表单注册代码:
<h2>注册</h2>
<form action="/register" method="post">
name:<br>
<input type="text" name="name"><br>
password:<br>
<input type="text" name="password"><br>
确认密码:<br>
<input type="text" name="repwd">
<input type="submit" value="注册">
<br>
</form>
node.js代码:
// 导入模块
const path = require('path')
const express = require('express')
const app = express()
// 一、1. 配置静态资源路径
app.use(express.static(path.resolve(__dirname,'test')))
// 二、1.引入解析请求体的中间件
app.use(express.urlencoded())
// 用一个数组存放数据
const DATA = [
{
name:"anna",
password: '123456'
},
{
name: 'Mario',
password: '123'
}
]
// 2. get请求 --登录
app.get('/login', function(req, res, next) {
console.log('请求已经收到');
// 避免乱码
res.setHeader('Content-Type','text/html;charset=utf-8')
// 遍历 数组的数据
const loginUser = DATA.find(item=>{
return item.name === req.query.name && item.password === req.query.password
})
// console.log(loginUser);// 正确会返回 错误会返回 undefined
if(loginUser){
res.send(`<h1>欢迎您再次回来!${loginUser.name}</h1>`);
}else {
res.send('<h1>登陆失败</h1>')
}
});
// post --注册
app.post('/register',(req,res)=>{
// 获取用户输入的数据
// console.log(req.body);
// 解构赋值
const {name,password,repwd} = req.body
// 判断注册是否有重复名
const user = DATA.find(item=>{
return item.name === name
})
if (!user){
// 没有找到就可以注册,并把它加入数组
DATA.push({
name,
password
})
res.send('<h1>恭喜您,注册成功!</h1>')
} else {
res.send('<h1>用户名重复</h1>')
}
})
3. express.static() 创建静态资源服务器
可以对外提供访问
// 导入模块
const path = require('path')
const express = require('express')
const app = express()
// 创建静态资源服务器 --可以托管多个静态资源目录
// express.static() 方法,快速的对外提供静态资源
app.use(express.static(path.join(__dirname,'./clock')))
app.use(express.static(path.join(__dirname,'./files')))
// 如果需要挂载固定的路径前缀
app.use('/clock',express.static('./clock'))
// 启动服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
})
4. 安装使用 nodemon
- 全局安装:npm i nodemon -g
启动:nodemon 运行index.js
nodemon XXX 运行指定的js - 在项目中安装:项目打包时不会把它加进去
npm i nodemon -D
启动:npx nodemon
由于传统使用 node app.js 命令启动项目,当代码修改后,需要重启命令。
使用 nodemon app.js 命令启动项目,当代码修改后,会被nodemon自动监听到,并自动修改。
二、express的路由
express路由就是 客户端请求和服务器处理 间的一种映射关系。
1. 路由的模块化
- 创建路由模块
// 导入 express
let express = require('express')
// 创建路由对象
let router = express.Router()
// 挂载具体路由
router.get('/user/list',(req,res)=>{
res.send('get request success')
})
router.post('/user/list',(req,res)=>{
res.send('post request success')
})
// 向外暴露route 路由
module.exports = router
- 注册和使用路由模块
const express = require('express')
const app = express()
// 导入路由模块
const route = require('./自定义模块')
// 注册路由模块 app.use() 函数的作用:注册全局中间件
app.use(route)
app.listen(80,()=>{
console.log('http://127.0.0.1')
})
三、中间件 use()
- 在express使用 app.use来定义一个中间件
中间件作用/用法和路由很像
但路由不需要区分请求方式,只看路径 - 和路由的区别:
- 会匹配所有请求
- 路径设置父目录
- next()的作用:next是回调函数的第三个参数
如果有多个路由/中间件,只会执行第一个,调用next()会继续执行后面的路由/中间件
注意:next()不能在响应处理完毕后调用
1. 全局生效中间件
const express = require('express')
const app = express()
// 定义一个简单的中间件
// 将 mw注册为全局生效的中间件
app.use((req,res,next)=>{
console.log("第一个中间件");
// 把扭转关系交给下一个中间件或路由
next()
})
app.use((req,res,next)=>{
console.log("第二个中间件");
// 把扭转关系交给下一个中间件或路由
next()
})
// 请求给路由 会依次触发上面两个中间件
app.get('/',(req,res)=>{
res.send('home page!')
})
app.listen(80,()=>{
console.log('http://127.0.0.1')
})
2. 局部生效中间件
const express = require('express')
const app = express()
// 将 mw注册为局部生效的中间件
const mw1 = (req,res,next)=>{
console.log("第一个中间件");
// 把扭转关系交给下一个中间件或路由
next()
}
const mw2 = (req,res,next)=>{
console.log("第二个中间件");
// 把扭转关系交给下一个中间件或路由
next()
}
// 请求给路由 会触发上面mw1中间件,不会触发mw2中间件
app.get('/',mw1,(req,res)=>{
res.send('home page!')
})
app.get('/user',(req,res)=>{
res.send('home user page!'+ res.startTime)
})
app.listen(80,()=>{
console.log('http://127.0.0.1')
})
3. 错误级别中间件
考虑到有时路径错误,前面路由都没访问到,表示友好信息,可以在最后放一个错误路由给用户提示!
不写路径,表示匹配所有路由
一定要放在所有路由中间件的后面。 才能捕获前面路由的错误
// 路由
app.get('/',(req,res)=>{
// 抛出一个自定义的错误
throw new Error('服务器发生了错误')
res.send('Home page!')
})
// 错误级别的中间件 4个参数 --顺序不可颠倒
app.use((err,req,res,next)=>{
console.log('发生了错误:'+ err.message); // 在服务器打印错误信息
res.send('Error!'+ err.message) // 向客户端打印错误信息
})
四、模板引擎
希望用户在访问students路由时,可以给用户返回一个显示某个信息的页面
由于html属于静态页面,创建时是什么样子,用户看到的就是什么样子,
不会自动跟随服务器数据的变化而变化。
在node中有一个 模板引擎,它长得像网页,且它里面可以嵌入变量
由于node中的模板引擎较多,都各有特色。但习惯使用 ejs
ejs是其中一款模板:
1. 安装ejs
npm i ejs
2. 配置express的模板引擎为ejs
app.set("view engine","ejs")
3. 配置模板路径(可选)
app.set("views",path.resolve(__dirname,"views"))
注意:模板引擎需要被express渲染后才能使用
res.render() 用来渲染一个模板引擎,并将其返回给浏览器
1. ejs 的使用
ejs是运行在服务器的代码。
res.render(“xxx”,{}) 用来渲染一个模板引擎,并将其返回给浏览器(就是把ejs文件转成网页)
通过它可以将render传递进来的数据直接在网页中显示:
- <%= %> 在ejs中输出内容,它会自动对字符串的特殊字符进行转义,避免 xxs
- <%- %> 原样输出
- <% %> 可以在其中直接编辑js代码,js代码会在服务器中执行打印
- <%# %> 是ejs里面的注释
服务器的代码:
// 配置模板路径
app.set("views",path.resolve(__dirname,"views"))
// 配置express的模板引擎为ejs
app.set("view engine",'ejs')
// 定义一个名字
var name = 'anna'
// get请求
app.get('/students', (req, res)=> {
res.render("students",{name})
});
// ejs中form表单的数据
app.get('/set-name',(req,res)=>{
name = req.query.name
res.send("修改成功!")
})
.ejs代码:
<h1>ejs使用</h1>
<!--通过它可以将render传递进来的数据直接在网页中显示 -->
<%=name %>
<form action="/set-name">
<input type="text" name="name" placeholder="请输入您的名字">
<button>提交</button>
</form>
<!-- 在服务器打印js代码 -->
<%
let num = 20
console.log(num)
%>