小插件Template String Converter
当输入${}
时,自动为其加上 ``
反引号
切换到D盘——
D:
查看D盘目录——dir
切换工作目录——cd machine
输出文件夹内的所有内容——dir /s
概念
特点
使用
let buf=Buffer.alloc(2)
console.log(buf)
let buf_2 =Buffer.allocUnsafe(11112)
//使用这种方式不一定会把旧的数据清空
//速度更快,不用进行清零操作
console.log(buf_2)
let buf_3 =Buffer.from('hello')
//把字符串的每个字符都转换成unicode
console.log(buf_3)
//终端显示
D:\web>node test.js
<Buffer 00 00>
<Buffer 80 00 df 3e 08 02 00 00 f0 fc 10 35 08 02 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ... 11062 more bytes>
<Buffer 68 65 6c 6c 6f>
D:\web>node test.js
<Buffer 00 00>
<Buffer 80 00 3b f5 be 02 00 00 80 63 34 f5 be 02 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ... 11062 more bytes>
<Buffer 68 65 6c 6c 6f>
可以发现:使用allocUnsafe每次的都不样
let buf_4 =Buffer.from([105,108,111,118,101,121,111,117])
console.log(buf_4)
console.log(buf_4.toString())
//
D:\web>node test.js
<Buffer 69 6c 6f 76 65 79 6f 75>
iloveyou
Buffer 可以直接通过 []
的方式对数据进行处理。
//读取
console.log(buf_3[1]);
//修改
buf_3[1] = 97;
//查看字符串结果
console.log(buf_3.toString())
注意:
- 如果修改的数值超过 255 ,则超过 8 位数据会被舍弃
- 一个 utf-8 的字符 一般 占 3 个字节(如:中文)
APP、数据——载入内存——CPU读取指令——显卡or 声卡——显示器or音箱。
// 导入模块
const fs1 =require('fs');
// 写入文件
fs1.writeFile('./f_write.txt','abcdefg',err=>{
if(err){
console.log('写入失败')
return;
}
})
// 导入模块
const fs1 =require('fs');
// 写入文件
fs1.writeFile('./f_write.txt','abcdefg',err=>{
if(err){
console.log('写入失败')
return;
}
console.log('写入成功')
})
console.log('1')
上述代码有两个线程:
JS主线程——执行解析JS代码。
磁盘写入——输入/输出( I / O)线程
writeFile方法是异步的。
当上述I/O线程完成后,将回调函数(err)压入队列中,等主线程执行完后续代码后,再取出这个函数,执行。
效果展示
// 导入模块
const fs1 =require('fs');
// 写入文件
fs1.writeFileSync('./test1','test1');
// 导入模块
const fs2 =require('fs');
// 写入文件
fs2.appendFile('./append.txt','append!!',err=>{
//写入成功则返回错误对象,失败则返回none
if(err){
console.log('追加写入失败')
return
}
console.log('追加写入成功')
})
// 导入模块
const fs2 =require('fs');
// 写入文件
fs2.appendFileSync('./append.txt','\r\nappend!!')//进行换行操作
// 导入模块
const fs1 =require('fs');
// 写入文件(第一遍直接写入的
// fs1.writeFile('./f_write.txt','ABC',err=>{
//追加写法——添加配置项
fs1.writeFile('./f_write.txt','hhhh',{flag:'a'},err=>{
if(err){
console.log('写入失败')
return;
}
console.log('写入成功')
})
console.log('1')
// 导入模块
const fs1 =require('fs');
//创建写入对象
const ws=fs1.createWriteStream('fff.txt');
//write
ws.write('乘醉听萧鼓\r\n')
ws.write('吟赏烟霞\r\n')
ws.write('异日图将好景\r\n')
ws.write('归去凤池夸\r\n')
//关闭通道
ws.close()
// // 导入模块
const fs1 =require('fs');
// //创建写入对象
// const ws=fs1.createWriteStream('fff.txt');
// //write
// ws.write('乘醉听萧鼓\r\n')
// ws.write('吟赏烟霞\r\n')
// ws.write('异日图将好景\r\n')
// ws.write('归去凤池夸\r\n')
// //关闭通道
// ws.close()
//读取上面写入的文档,并进行输出
fs1.readFile('./fff.txt',(err,data)=>{
if(err){
console.log('读取失败')
return
}
console.log(data)
console.log(data.toString())
})
// // 导入模块
const fs1 =require('fs');
//读取
let data=fs1.readFileSync('./fff.txt')
console.log(data)
console.log(data.toString())
// // 导入模块
const fs1 =require('fs');
//流式读取
let rs=fs1.createReadStream('./fff.txt')
rs.on('data',data=>{
console.log(data)
})
//读取完毕后,执行end回调
rs.on('end',()=>{
console.log('读取完成')
})
// // 导入模块
const fs1 =require('fs');
//重命名
fs1.rename('./fff.txt','./poem.txt',err=>{
if(err) throw err
console.log('移动完成')
})
也可以用它来修改路径
// // 导入模块
const fs1 =require('fs');
//重命名
fs1.renameSync('./poem.txt','./poemAA.txt')
以下两个方法均有同步方法
// // 导入模块
const fs1 =require('fs');
//删除
fs1.unlink('./poemAA.txt',err=>{
if(err){
console.log('删除失败')
return
}
console.log('删除成功')
})
// // 导入模块
const fs1 =require('fs');
//删除
fs1.rm('./f_write.txt',err=>{
if(err){
console.log('删除失败')
return
}
console.log('删除成功')
})
make directory
// // 导入模块
const fs1 =require('fs');
//删除
fs1.mkdir('./html',err=>{
if(err){
console.log('创建失败');
return
}
console.log('创建成功')
})
// // 导入模块
const fs1 =require('fs');
//删除
fs1.mkdir('./a/b/c',err=>{
if(err){
console.log('创建失败');
return
}
console.log('创建成功')
})
//D:\web>node test.js
//创建失败
需要添加配置项
// // 导入模块
const fs1 =require('fs');
//删除
fs1.mkdir('./a/b/c',{recursive:true},err=>{
if(err){
console.log('创建失败');
return
}
console.log('创建成功')
})
// // 导入模块
const fs1 =require('fs');
//删除
fs1.readdir('./vue3',(err,data)=>{
if(err){
console.log('创建失败');
return
}
console.log('创建成功',data)
})
rm——remove
系统建议使用rm来代替rmdir
(node:968) [DEP0147] DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will be removed. Use fs.rm(path, { recursive: true }) instead
// // 导入模块
const fs1 =require('fs');
//删除
fs1.rmdir('./html',err=>{
if(err){
console.log('删除失败')
return
}
console.log('删除成功')
})
若删除之后再次运行,则显示删除失败
// // 导入模块
const fs1 =require('fs');
//想删除a文件夹下的所以目录
fs1.rmdir('./a',err=>{
if(err){
console.log('删除失败',err)
return
}
console.log('删除成功')
})
// 导入模块
const fs1 =require('fs');
//想删除a文件夹下的所以目录
fs1.rmdir('./a',{recursive:true},err=>{
if(err){
console.log('删除失败')
return
}
console.log('删除成功')
})
// 导入模块
const fs1 =require('fs');
//stat方法 status缩写
fs1.stat('./2023_1/demo/动态表格视频.mp4',(err,data)=>{
if(err){
console.log('操作失败')
return
}
console.log('操作成功',data)
})
// // 导入模块
const fs1 =require('fs');
//使用绝对路径删除
fs1.rm('D:/web/test1',err=>{
if(err){
console.log('删除失败')
return
}
console.log('删除成功')
})
D:/web/test1 最初的形式是D:\web\test1反斜杠,要改成 / 才可以进行操作
使用相对路径时的参照目录是命令行所在的目录,而不是当前文件的所在的所在目录。
__dirname 保存着 当前文件所在目录的绝对路径
拼接规范的逻辑路径
console.log(__dirname+'/index.html')
如下图所示 斜杠不一致
console.log(__dirname+'/index.html')
let ts='D:\\web\\node\\test.js'
console.log(path1.parse(ts))
let ts='D:\\web\\node\\test.js'
console.log(path1.basename(ts))
let ts='D:\\web\\node\\test.js'
console.log(path1.dirname(ts))
console.log(path1.extname(ts))```
网页中的 URL 主要分为两大类:相对路径与绝对路径
绝对路径可靠性强,而且相对容易理解,在项目中运用较多
相对路径
如果已经在最外层,再加…/还是不变,依旧是最外层
使用场景
hypertext Transfer Protocol
超文本传输协议
请求报文,响应报文
encode 编码 decode解码
端口号有些情况下可以省略
就像发件写 收件地址
IP——寻找网络设备
将32位二进制转为10进制
作用
只要设备接入互联网,它都有自己的ip地址。
局域网ip可以复用
主战区转换为后端:服务器
// 导入模块
const http=require('http');
// 创建服务对象
// request,response是两个形参,分别是对请求,响应报文的封装
// 当服务接收到请求时,回调函数执行
const server=http.createServer((request,response)=>{
// 设置响应体并结束响应体
response.end('hello HTTP Server')
});
// 监听端口,启动服务器
// 当服务启动成功以后执行此回调函数
m /
// 导入模块
const http=require('http');
// 创建服务对象
// request,response是两个形参,分别是对请求,响应报文的封装
// 当服务接收到请求时,回调函数执行
const server=http.createServer((request,response)=>{
// 设置响应体并结束响应体
response.setHeader('content-type','text/html;charset=utf-8')
response.end('你好 HTTP')
});
// 监听端口,启动服务器
// 当服务启动成功以后执行此回调函数
server.listen(9000,()=>{
console.log('服务已经启动...')
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://127.0.0.1:9000" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" name="">
</form>
</body>
</html>
const http=require('http');
// 当服务接收到请求时,回调函数执行
const server=http.createServer((request,response)=>{
// 获取请求的方法
console.log(request.method);
// 获取请求的url(只包含路径与查询字符串)
console.log(request.url)
// 获取请求头
console.log(request.headers)
// 设置响应体并结束响应体
response.setHeader('content-type','text/html;charset=utf-8')
response.end('你好 HTTP')
});
// 监听端口,启动服务器
// 当服务启动成功以后执行此回调函数
server.listen(9000,()=>{
console.log('服务已经启动...')
});
提取路径和查询字符串
const http=require('http');
const server=http.createServer((request,response)=>{
let url=new URL(request.url,'http://127.0.0.1')
console.log(url)
response.setHeader('content-type','text/html;charset=utf-8')
response.end('请求成功')
});
server.listen(9000,()=>{
console.log('服务已经启动...')
});
//请求体的内容
URL {
href: 'http://127.0.0.1/search?key=123',
origin: 'http://127.0.0.1',
protocol: 'http:',
username: '',
password: '',
host: '127.0.0.1',
hostname: '127.0.0.1',
port: '',
pathname: '/search',
search: '?key=123',
searchParams: URLSearchParams { 'key' => '123' },
hash: ''
}
URL {
href: 'http://127.0.0.1/favicon.ico',
origin: 'http://127.0.0.1',
protocol: 'http:',
username: '',
password: '',
host: '127.0.0.1',
hostname: '127.0.0.1',
port: '',
pathname: '/favicon.ico',
search: '',
searchParams: URLSearchParams {},
hash: ''
}
查询路径和字符串
let url=new URL(request.url,'http://127.0.0.1')
console.log('查询路径:',url.pathname)
console.log('\n查询字符串:',url.searchParams)
取出字符串console.log(url.searchParams.get('key'))
const http=require('http');
// 创建服务
const server=http.createServer((request,response)=>{
//获取请求的方法
// let method =request.method
// 简写:解构赋值
let method =request.method
//获取请求的路径
let pathname =new URL(request.url,'http://127.0.0.1').pathname
//设置响应头
response.setHeader('content-type','text/html;charset=utf-8')
//判断
if(method === 'GET' && pathname === '/login'){
response.end('登录页面')
}else if(method === 'GET' && pathname === '/reg'){
response.end('注册页面')
}else{
response.end('404')
}
//每次请求只能对应一次end方法
});
server.listen(9000,()=>{
console.log('服务已经启动...\n端口9000监听中...')
});
pathname 是一个 URL
对象
的属性,它并不是一个字符串。为了正确判断请求的路径,需要使用pathname.pathname
来获取路径字符串,然后进行比较。
注意路径别写成./login
const http=require('http')
const server=http.createServer((request,response)=>{
// 设置响应状态码
response.statusCode=201;
// 响应状态的描述,一般不用
response.statusMessage='message'
// 设置响应头,用来表示服务端的名字
response.setHeader('ch','text/html;charset=utf-8')
response.setHeader('Flavia','FFFFF')
// 响应体,write方法与end方法相比,可多次调用
response.write('love')
response.write('\npatience')
//每个请求必须有end方法,且只能有一个
response.end()
})
server.listen(9000,()=>{
console.log('服务已经启动...')
})
搭建 HTTP 服务,响应一个 4 行 3 列的表格,并且要求表格有 隔行换色效果 ,且 点击 单元格能 高亮显示
const http=require('http')
const server=http.createServer((request,response)=>{
response.end(`
Document
`)
})
server.listen(9000,()=>{
console.log('服务已经启动...')
})
const http=require('http')
const server=http.createServer((request,response)=>{
response.end(`
Document
`)
})
server.listen(9000,()=>{
console.log('服务已经启动...')
})
把html内容单独放一个文件
const http=require('http')
const fs=require('fs')
const server=http.createServer((request,response)=>{
let html=fs.readFileSync(__dirname+'/index.html')
response.end(html)
})
server.listen(9000,()=>{
console.log('服务已经启动...')
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
td{
padding:10px 35px;
}
table,td{
border-collapse:collapse;
}
table tr:nth-child(odd){
background: #eee;
}
table tr:nth-child(even){
background: #fff;
}
</style>
</head>
<body>
<table border=1>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</table>
<script>
//获取所有td
let tds=document.querySelectorAll('td')
//遍历
tds.forEach(item=>{
item.onmouseover=function(){
this.style.background='#ccc'
}
item.onmouseout=function(){
this.style.background='#fff'
}
})
</script>
</body>
</html>
想要独立css,js文件
根据请求路径区分请求结果,而不是每个文件都是html形式。尚硅谷第56集
这两个请求关系不大
很多请求都是并行的。
执行着,遇到了,就去请求
静态资源:内容长时间不发生改变的资源
动态资源:内容经常更新的资源
const http=require('http')
const fs=require('fs')
const server=http.createServer((request,response)=>{
// 请求url路径
let {pathname}=new URL(request.url,'http://127.0.0.1');
// 拼接文件路径
let filePath=__dirname+pathname;
fs.readFile(filePath,(err,data)=>{
if(err){
response.statusCode=500;
response.end('文件读取失败');
return;
}
response.setHeader('content-type','text/html;charset=utf-8')
response.end(data)
})
})
server.listen(9000,()=>{
console.log('服务已经启动...')
});
HTTP 服务在哪个文件夹中寻找静态资源,那个文件夹就是 静态资源目录 ,也称之为 网站根目录
媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。
mime 类型结构: [type]/[subType]
例如: text/html text/css image/jpeg image/png application/json
HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理资源.
//是常见文件对应的 mime 类型
html: 'text/html',
css: 'text/css',
js: 'text/javascript',
png: 'image/png',
jpg: 'image/jpeg',
gif: 'image/gif',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json'
对于未知的资源类型,可以选择
application/octet-stream
类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果
GET和POST请求的区别
D:\web\node\fn.js
function tie(){
console.log('贴膜...')
}
//暴露数据
module.exports=tie
D:\web\node\index.js
// 导入模块
const tie =require('./fn.js')
//调用函数
tie()
注意:
此处的代码改变则不生效
exports.tie=tie
exports.hi=hi
console.log(module.exports===exports)
使用require函数
./
和 ../
// 伪代码
function require(){
// 转换为绝对路径
let absolutePath=path.resolve(__dirname,file);
// 缓存检测
if(caches[absolutePath]){
return caches[absolutePath]
}
//读取文件内容
let code=fs.readFileSync(absolutePath).toString()
// 包裹成函数,执行
let module={}
let exports=module.exports={}
function (exports, require, module, __filename, __dirname) {
const test={
name:'尚硅谷'
}
module.exports=test;
// 输出指向函数的代码体
console.log(arguments.callee.toString())
}(exports, require, module, __filename, __dirname)
//缓存
caches[absolutePath]=module.exports
}
const m=require('./fn.js')
module.exports 、 exports 以及 require 这些都是 CommonJS 模块化规范中的内容。
而 Node.js 是实现了 CommonJS 模块化规范,二者关系有点像 JavaScript 与 ECMAScript
常用的包管理工具 npm yarn cnpm
npm 全称 Node Package Manager ,翻译为中文意思是『Node 的包管理工具』
npm 是 node.js 官方内置的包管理工具,是 必须要掌握住的工具.
创建一个空目录,然后以此目录作为工作目录 启动命令行工具 ,执行 npm init
终端显示如下
D:\web\node\files1>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help init` for definitive documentation on these fields
and exactly what they do.
Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (files1) npm1
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author: F
license: (ISC)
About to write to D:\web\node\files1\package.json:
{
"name": "npm1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "F",
"license": "ISC"
}
确认最后一步之后,自动创建一个json文件。
npm init 命令的作用是将文件夹初始化为一个『包』, 交互式创建 package.json 文件 package.json
是包的配置文件,每个包都必须要有 package.json
npm s
https://www.npmjs.com/
npm install
和 npm i
命令安装包# 示例
npm install uniq
npm i uniq
node_modules 文件夹
存放下载的包package-lock.json
包的锁文件 ,用来锁定包的版本安装 uniq 之后, uniq 就是当前这个包的一个 依赖包 ,有时会简称为 依赖
比如我们创建一个包名字为 A,A 中安装了包名字是 B,我们就说 B 是 A 的一个依赖包 ,也会说A 依赖 B
// 导入uniq包
const uniq=require('uniq')
// 使用
let arr=[1,1,5,4,2,2,2,2,2,3,1]
const result =uniq(arr)
console.log(arr)
console.log(result)
由结果可见,此方法会改变原数组,并且结果经排序后输出。
以下代码等价
但是第一种写法
const uniq=require('uniq')
更灵活就,因为:如果node_modules文件夹安装在了外层目录,根据如下规则:
- 在当前文件夹下 node_modules 中寻找同名的文件夹
- 在上级目录中下的 node_modules 中寻找同名的文件夹,直至找到磁盘根目录
还是能成功导入模块
举个例子方便大家理解,比如说做蛋炒饭需要 大米 , 油 , 葱 , 鸡蛋 , 锅 , 煤气 , 铲子 等
其中 锅 , 煤气 , 铲子 属于开发依赖,只在制作阶段使用
而 大米 , 油 , 葱 , 鸡蛋 属于生产依赖,在制作与最终食用都会用到
所以 开发依赖 是只在开发阶段使用的依赖包,而 生产依赖 是开发阶段和最终上线运行阶段都用到的依赖包
运行时也使用它
生产依赖 npm i -S
uniq或者npm i --save
uniq
-S 等效于 --save, -S 是默认选项
包信息保存在 package.json 中 dependencies 属性
只在开发阶段使用
npm i -D
less
npm i --save-dev
less
-D 等效于 --save-dev
包信息保存在 package.json 中 devDependencies 属性
npm i --save uniq
npm i -D less
执行安装选项 -g 进行全局安装
全局安装完成之后就可以在命令行的任何位置运行 nodemon 命令
该命令的作用是 自动重启 node 应用程序
npm i -g nodemon
// 导入模块
const http=require('http');
// 创建服务对象
const server=http.createServer((request,response)=>{
response.setHeader('content-type','text/html;charset=utf-8')
response.end('你好 nodemon')
});
// 监听端口,启动服务器
// 当服务启动成功以后执行此回调函数
server.listen(9000,()=>{
console.log('服务已经启动...')
});
当返回的内容改变时,不需要手动重启服务器,使用nodemon会自动重启,只需要在浏览器界面刷新即可
说明:
npm root -g
可以查看全局安装包的位置目的:为解决 全局包无法使用的问题
如果可以使用nodemon命令,则不用管这些
当输入QQ
命令时,会在当前目录查找以exe或者cmd结尾的文件,找不到则无法运行。
目的:不论在哪个目录都可以通过命令行找到QQ
步骤
Path 是操作系统的一个环境变量,可以设置一些文件夹的路径,在当前工作目录下找不到可执行文件时,就会在环境变量 Path 的目录中挨个的查找,如果找到则执行,如果没有找到就会报错。
在项目协作中有一个常用的命令就是 npm i
,通过该命令可以依据 package.json
和 packag-lock.json
的依赖声明安装项目依赖
node_modules 文件夹
。大多数情况都不会存入版本库
作用:下载 这个包(项目)安装的所有依赖。一般用于拉取远程仓库的代码,之后执行的。或者把文件发给别人,不用打包node_modules文件夹,人家npm i即可。
项目中可能会遇到版本不匹配的情况,有时就需要安装指定版本的包
## 格式
npm i <包名@版本号>
## 示例
npm i jquery@1.11.2
//index.中的内容
console.log('hi')
start 别名比较特别,使用时可以省略 run
"scripts": {
"start": "node index.js"
},
npm start 是项目中常用的一个命令,一般用来启动项目 npm run 有自动向上级目录查找的特性,跟 require 函数也一样
对于陌生的项目,我们可以通过查看 scripts 属性来参考项目的一些操作
https://npmmirror.com/
npm install -g cnpm --registry=https://registry.npmmirror.com
功能 | 命令 |
---|---|
初始化 | cnpm init / cnpm init |
安装包 | cnpm i uniq |
安装包 | cnpm i -S uniq |
安装包 | cnpm i -D uniq |
安装包 | cnpm i -g nodemon |
安装项目依赖 | cnpm i |
删除 | cnpm r uniq |
用 npm 也可以使用淘宝镜像,配置的方式有两种
npm config set registry https://registry.npmmirror.com/
补充说明:
建议使用第二种方式 进行镜像配置,因为后续修改起来会比较方便
还可以使用nrm use npm
切换回原来的
yarn 是由 Facebook 在 2016 年推出的新的 Javascript 包管理工具,官方网址:https://yarnpkg.com/
yarn 官方宣称的一些特点
使用 npm 安装 yarn
npm i -g yarn
功能 | 命令 |
---|---|
初始化 | yarn init 或 yarn init -y |
安装包 | yarn add uniq (生产依赖) |
安装包 | yarn add less --dev (开发依赖) |
安装包 | yarn global add nodemon (全局安装) |
删除包 | yarn remove uniq (删除项目依赖包) |
删除包 | yarn global remove nodemon (全局删除包) |
安装项目依赖 | yarn |
运行命令别名 | yarn <别名> (不需要添加 run) |
{
"dependencies": {
"uniq": "^1.0.1"
},
"name": "yarnf",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "node index.js"
}
}
因为没有配置环境变量,所以,通过yarn安装的-g等命令,没有作用,必须要配置环境变量。
可以通过如下命令配置淘宝镜像yarn config set registry https://registry.npmmirror.com/
可以通过 yarn config list 查看 yarn 的配置项
根据不同的场景进行选择
包管理工具 不要混着用,切记,切记,切记
我们可以将自己开发的工具包发布到 npm 服务上,方便自己和其他开发者使用,操作步骤如下:
创建文件夹,并创建文件 index.js, 在文件中声明函数,使用 module.exports 暴露
npm 初始化工具包,package.json 填写包的信息 (包的名字是唯一的)
注册账号 https://www.npmjs.com/signup
激活账号 ( 一定要激活账号 )
命令行下 npm login
填写相关用户信息
+表示发布成功
后续可以对自己发布的包进行更新,操作步骤如下
执行如下命令删除包npm unpublish --force
减号表示删除成功
删除包需要满足一定的条件,
https://docs.npmjs.com/policies/unpublish
- 你是包的作者
- 发布小于 24 小时
- 大于 24 小时后,没有其他包依赖,并且每周小于 300 下载量,并且只有一个维护者
在很多语言中都有包管理工具,比如:
语言 | 包管理工具 |
---|---|
PHP | composer |
Python | pip |
Java | maven |
Go | go mod |
JavaScript | npm/yarn/cnpm/other |
Ruby | rubyGems |
除了编程语言领域有包管理工具之外,操作系统层面也存在包管理工具,不过这个包指的是『 软件包 』
操作系统 | 包管理工具 | 网址 |
---|---|---|
Centos | yum | https://packages.debian.org/stable/ |
Ubuntu | apt | https://packages.ubuntu.com/ |
MacOS | homebrew | https://brew.sh/ |
Windows | chocolatey | https://chocolatey.org/ |
全称:Node Version Manager ——用来管理node版本的工具,方便切换不同版本的node.js。
首先先下载 nvm,下载地址 https://github.com/coreybutler/nvm-windows/releases,
选择 nvm-setup.exe
下载即可(网络异常的小朋友可以在资料文件夹中获取)。
命令 | 说明 |
---|---|
nvm list available | 显示所有可以下载的 Node.js 版本 |
nvm list | 显示已安装的版本 |
nvm install 18.12.1 | 安装 18.12.1 版本的 Node.js |
nvm install latest | 安装最新版的 Node.js |
nvm uninstall 18.12.1 | 删除某个版本的 Node.js |
nvm use 18.12.1 | 切换 18.12.1 的 Node.js |
express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架,官方网址:https://www.expressjs.com.cn/
简单来说,express 是一个封装好的工具包,封装了很多功能,便于我们开发 WEB 应用(HTTP 服务)
express 本身是一个 npm 包,所以可以通过 npm 安装
npm init
npm i express
创建 JS 文件,键入如下代码
// 导入express
const express=require('express');
// 创建应用对象
const app=express();
// 创建路由
// 当浏览器发送请求的方法是get,且请求的路径是/home的话
// 就会执行后面的回调函数,为浏览器响应结果
app.get('/home',(req,res)=>{;
// req是请求报文的封装对象,res是响应报文的封装对象。
res.end('hi express');
})
// 监听端口,启动服务
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
官方定义:路由确定了应用程序如何响应客户端对特定端点的请求
一个路由的组成有 请求方法 , 路径 和 回调函数
组成
express 中提供了一系列方法,可以很方便的使用路由,使用格式:app.
app.get('/',(req,res)=>{;
// req是请求报文的封装对象,res是响应报文的封装对象。
res.end('home');
})
在地址栏回车键入的是get请求。所以无法获取login页面。
app.post('/login',(req,res)=>{
res.end('login_post')
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form method="post" action="http://127.0.0.1:3000/login">
<button>发送</button>
</form>
</body>
</html>
点击“发送”按钮,即可跳转到login页面
//js文件
const express=require('express');
const app=express();
app.all('/test',(req,res)=>{
res.end('test_all')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
//html文件
<body>
<form method="post" action="http://127.0.0.1:3000/test">
<button>发送</button>
</form>
</body>
使用app.all( )
这样不论是html文件的表单发送的get请求,还是通过网址栏发送的get请求,都能得到服务器返回的页面。
const express=require('express');
const app=express();
// 匹配所有方法
app.all('/test',(req,res)=>{
res.end('test_all')
})
// 匹配404
app.all('*',(req,res)=>{
// *表示匹配所有的路径
res.end('404 Not Found')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
const express=require('express');
const app=express();
app.get('/request',(req,res)=>{
// 原生操作
console.log(req.method)
console.log(req.url)
console.log(req.httpVersion)
console.log(req.headers)
console.log('______________________________________')
//express 操作
console.log(req.path)
console.log(req.query)
console.log(req.ip)
res.end('hello expression')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
输出结果
GET
/request?a=100&b=200
1.1
{
host: '127.0.0.1:3000',
connection: 'keep-alive',
'cache-control': 'max-age=0',
'sec-ch-ua': '"Microsoft Edge";v="117", "Not;A=Brand";v="8", "Chromium";v="117"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.36',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'sec-fetch-site': 'none',
'sec-fetch-mode': 'navigate',
'sec-fetch-user': '?1',
'sec-fetch-dest': 'document',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
cookie: '_ga=GA1.1.1922259483.1687846523; _ga_R1FN4KJKJH=GS1.1.1688639930.11.1.1688640103.0.0.0'
}
______________________________________
/request
{ a: '100', b: '200' }
::ffff:127.0.0.1
const express=require('express');
const app=express();
app.get('/request',(req,res)=>{
//express 方法
// 获取请求头
// console.log(req.get())
// 只获取host请求头
console.log(req.get('host'))
res.end('hello expression')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
输出结果
// 127.0.0.1:3000
路由参数指的是 URL 路径中的参数(数据
const express=require('express');
const app=express();
app.get('/:idf.html',(req,res)=>{
res.setHeader('content-type','text/html;charset=utf-8')
res.end('商品详情')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
使用:id
占位符。
弹幕:商品页面就是固定的,然后去根据id请求数据库中的内容,渲染到当前页面
通过req.params.idf
得到请求数据时的路由参数。
const express=require('express');
const app=express();
app.get('/:idf.html',(req,res)=>{
res.setHeader('content-type','text/html;charset=utf-8')
console.log(req.params.idf)
res.end('商品详情')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
下面两块代码只有占位符不同
const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
console.log(singers)
const app=express();
app.get('/single/:f.html',(req,res)=>{
res.setHeader('content-type','text/html;charset=utf-8')
let{f}=req.params;
res.end('商品详情')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
// console.log(singers)
const app=express();
app.get('/singer/:id.html',(req,res)=>{
res.setHeader('content-type','text/html;charset=utf-8')
let{id}=req.params;
// 在数组中寻找对应id的数据
let result=singers.find(item=>{
if(item.id===Number(id)){
return true;
}
});
console.log(result)
res.end('result')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
// console.log(singers)
const app=express();
app.get('/singer/:f.html',(req,res)=>{
res.setHeader('content-type','text/html;charset=utf-8')
let{f}=req.params;
// 在数组中寻找对应id的数据
let result=singers.find(item=>{
if(item.id===Number(f)){
return true;
}
});
console.log(result)
res.end('result')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
// console.log(singers)
const app=express();
app.get('/singer/:f.html',(req,res)=>{
res.setHeader('content-type','text/html;charset=utf-8')
let{f}=req.params;
// 在数组中寻找对应id的数据
let result=singers.find(item=>{
if(item.id===Number(f)){
return true;
}
});
// console.log(result)
//当请求的内容不在json数据里,就返回404
if(!result){
res.statusCode=404;
res.end(`404 Not Found
`)
return;
}
res.end(`
Document
${result.singer_name}
`)
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
const express=require('express');
const app=express();
app.get('/response',(req,res)=>{
// 原生响应
res.statusCode=404
res.statusMessage='love ping'
res.setHeader('xxx','yyy')
res.write('hello')
res.end('response')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
分开写
const express=require('express');
const app=express();
app.get('/response',(req,res)=>{
res.status(500)
res.set('chang','ping')
res.send('你好')
res.end('response')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
连着写
const express=require('express');
const app=express();
app.get('/response',(req,res)=>{
res.status(500).set('haiying','changping').send('这很OK')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
res.send('你好')
方法会自动在响应头添加,防止中文展示乱码。
const express=require('express');
const app=express();
app.get('/other',(req,res)=>{
//跳转响应
res.redirect('https://mp.csdn.net/mp_blog/manage/article?spm=1000.2115.3001.5448')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
在网址输入路径后,自动下载软件
const express=require('express');
const app=express();
app.get('/other',(req,res)=>{
//下载响应
res.download(__dirname+'/package.json')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
const express=require('express');
const app=express();
app.get('/other',(req,res)=>{
//JSON响应
res.json({
'fang':'haiying',
'age':'19'
})
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
const express=require('express');
const app=express();
app.get('/other',(req,res)=>{
// 响应文件内容
res.sendFile(__dirname+'/index.html')
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div style="color: pink;">你好呀</div>
</body>
</html>
本质是一个回调函数
需求:记录每个请求的url和IP地址
const express=require('express');
const fs=require('fs')
const path=require('path')
const app=express();
app.get('/home',(req,res)=>{
// 获取url和ip
let {url,ip}=req;
console.log(url+' '+ip)
//将信息保存在文件中
fs.appendFileSync(path.resolve(__dirname,'./fhy.log'),`${url}${ip}\r\n`)
res.send('主页')
})
app.get('/admin',(req,res)=>{
res.send('后台')
})
app.all('*',(req,res)=>{
res.send(`404 Not Found`)
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
整个效果就是先执行中间件函数,然后通过next执行路由函数。
const express = require('express');
const fs = require('fs')
const path = require('path')
const app = express();
// 声明中间件函数
//next是内部函数,后续会指向路由或者中间件
function recordMiddleware(req, res, next) {
// 获取url和ip
let { url, ip } = req;
console.log(url + ' ' + ip)
//将信息保存在文件中
fs.appendFileSync(path.resolve(__dirname, './fhy.log'), `${url}${ip}\r\n`)
// 调用next函数
next()
}
//使用中间件函数
app.use(recordMiddleware)
app.get('/home', (req, res) => {
res.send('主页')
})
app.get('/admin', (req, res) => {
res.send('后台')
})
app.all('*', (req, res) => {
res.send(`404 Not Found`)
})
app.listen(3000, () => {
console.log('服务已经启动,端口3000正在监听中...')
})
文件内容
如果 只需要对某一些路由进行功能封装 ,则就需要路由中间件。
const express = require('express');
const fs = require('fs')
const path = require('path')
const app = express();
// 声明中间件函数
//next是内部函数,后续会指向路由或者中间件
let checkCodeMid=(req, res, next)=> {
// 判断url的code参数是否等于521
if(req.query.code==='521'){
next()
}else{
res.send('暗号错误')
}
// 调用next函数
next()
}
//使用中间件函数
app.use(checkCodeMid)
// 以下都是路由回调
app.get('/home', (req, res) => {
res.send('主页')
})
//把中间件函数放到受约束的函数中
app.get('/admin', checkCodeMid,(req, res) => {
res.send('后台')
})
app.get('/set',checkCodeMid, (req, res) => {
res.send('设置页面')
})
app.all('*', (req, res) => {
res.send(`404 Not Found`)
})
app.listen(3000, () => {
console.log('服务已经启动,端口3000正在监听中...')
})
静态资源目录(网站根目录)
浏览器发送给服务端请求后,服务端到哪个目录下去找对应的文化,则那个文件的文件夹就是静态资源目录。
const express = require('express');
const app = express();
//静态资源中间件的设置,将当前文件夹下的public目录作为网站的根目录
//express.static的返回结果是中间件函数
app.use(express.static(__dirname + '/public')); // 这个参数就是静态资源文件夹的路径
app.get('/home',(req,res)=>{
res.send('首页');
});
app.listen(3000,()=>{
console.log('3000 端口启动....');
});
目的:使手机也可以通过局域网看到静态资源
在npm搜索包,并选择路由中间件的使用方式
// create application/json parser
var jsonParser = bodyParser.json()
// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })
const express =require('express');
const app=express();
//导入解析请求体的包
const bodyParser=require('body-parser')
//解析queryString格式 请求体的中间件
const urlEncodeParser=bodyParser.urlencoded({extended:false})
//创建路由规则
app.get('/login',(req,res)=>{
// 响应文件内容
res.sendFile(__dirname+'/form.html')
})
app.post('/login',urlEncodeParser,(req,res)=>{
if(req.body){
res.send(req.body)
}else{
res.send('数据获取失败')
}
})
app.listen(3000,()=>{
console.log('服务已经启动...')
})
<body>
<!-- <form action="http://127.0.0.1:3000/login" method="post"> -->
<!-- 代码可以简写,只写路径 -->
<form action="/login" method="post">
用户名:<input type="text" name="username">
<br>
密码:<input type="password" name="password">
<br>
<button>登录</button>
</form>
</body>
防止外部网站盗用网站资源。
禁止该域名之外的其他网站访问这个资源
实现防盗链效果
在请求头中有一个 referer
包含了发送请求的网站的协议、域名、端口。
如果referer不对,则返回404结果。
正常展示静态资源,使用localhost
和127.0.0.1
都可以访问
const express =require('express');
const app=express();
app.use(express.static(__dirname+'/public'))
app.listen(3000,()=>{
console.log('服务已经启动...')
})
添加防盗链效果:只有127.0.0.1域名可以访问。
const express = require('express');
const app = express();
//添加中间件,实现效果
app.use((req, res, next) => {
// 获取referer
let referer = req.get('referer')
// 判断一下是否有referer
if (referer) {
// 实例化
let url = new URL(referer)
// 获取hostname域名
let hostname=url.hostname
console.log(hostname)
if(hostname!=='127.0.0.1'){
res.end('404')
return
}
}
next()
})
app.use(express.static(__dirname + '/public'))
app.listen(3000, () => {
console.log('服务已经启动...')
})
app.get( )、app.post( )
变成router.get( ) 、router.post()
等。没有路由组件时
//main.js文件
const express = require('express');
const app = express();
//引入子路由文件
// const homeRouter=require('./routers/homeRouter.js')
//设置和使用中间件
// app.use(homeRouter)
app.all('*',(req,res)=>{
res.send('404
')
})
app.listen(3000,()=>{
console.log('3000 端口启动....');
})
创建的路由文件的路径D:\web\node\init\routers\homeRouter.js
homeRouter.js
//1. 导入 express
const express = require('express');
//2. 创建路由器对象
const router = express.Router();
//3. 在 router 对象身上添加路由
router.get('/', (req, res) => {
res.send('首页');
})
router.get('/search', (req, res) => {
res.send('内容搜索');
});
//4. 暴露
module.exports = router;
//1. 导入 express
const express = require('express');
//2. 创建路由器对象
const router = express.Router();
//3. 在 router 对象身上添加路由
router.get('/admin', (req, res) => {
res.send('管理页面');
})
//4. 暴露
module.exports = router;
修改后的user.js路由模块。
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.get('/test', function(req, res, next) {
res.send('Flavia测试');
});
module.exports = router;
因为在app.js中设置了路由前缀app.use('/users', usersRouter);
,所以得输入 /users/test
成功显示!
用户界面和业务数据
的一种技术。也就是分离服务器端的js和html
如下面的代码
const express=require('express');
//导入json文件
// 因为这个json文件里面有两层对象,所以使用解构赋值
const {singers}=require('./singers.json')
// console.log(singers)
const app=express();
app.get('/singer/:f.html',(req,res)=>{
res.setHeader('content-type','text/html;charset=utf-8')
let{f}=req.params;
// 在数组中寻找对应id的数据
let result=singers.find(item=>{
if(item.id===Number(f)){
return true;
}
});
// console.log(result)
//当请求的内容不在json数据里,就返回404
if(!result){
res.statusCode=404;
res.end(`404 Not Found
`)
return;
}
res.end(`
Document
${result.singer_name}
`)
})
app.listen(3000,()=>{
console.log('服务已经启动,端口3000正在监听中...')
})
注意 nrm use taobao 使用淘宝镜像。
下载安装EJS npm i ejs
在ejs文件夹下装的,但是node_modules文件夹在ejs文件夹外部。
目的:我们要达到拼接效果,像下面代码所示,但是不想让str2变量和其他混在一起,这样不利于后续处理
const ejs=require('ejs')
let str1 ='你好'
let str2=`ejs${str1}`
console.log(str2)
<%= %>
是ejs解析内容的标记,作用是输出当前表达式的执行结果。
ejs会从后面传入的数据中找<%= %>
内的内容,并利用对应属性的值替换掉<%= %>
内的内容。
下面不同的写法,效果相同
const ejs=require('ejs')
let result=ejs.render('你好<%= str %>',{str:'ejs'})
console.log(result)
const ejs=require('ejs')
h='你好<%= str %>'
str='ejs'
let result=ejs.render(h,{str:str})
console.log(result)
const fs=require('fs')
const ejs=require('ejs')
hi=fs.readFileSync('./str.html').toString()
let result=ejs.render(hi,{str:'ejs'})
console.log(result)
str.html的内容
有点晕了
const fs=require('fs')
const ejs=require('ejs')
let ejsHtml=fs.readFileSync('./ejs.html').toString()
let str='ejs'
weather='今天天气不错'
let result=ejs.render(ejsHtml,{str,weather,ejsHtml})
console.log(result)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>你好<%= str %></h2>
<%= weather %>
</body>
</html>
原生js实现
const palyer=['喜羊羊','美羊羊','懒羊羊','灰太狼']
let str=''
palyer.forEach(item=>{
str+=`${item}`
})
str+=''
console.log(str)
使用ejs
安装命令npm install -g express-generator
创建命令express -e 名字
在D:\web\node\init\ejs\generator\routes\index.js
下的内容
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
//显示网页的表单
router.get('/upload',(req,res)=>{
res.render('upload')
})
//处理文件上传
router.post('/upload',(req,res)=>{
res.send('成功')
})
module.exports = router;
在D:\web\node\init\ejs\generator\views\upload.ejs
下的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
</head>
<body>
<h2>文件上传</h2>
<hr>
<!-- 文件上传时,必不可少的设置enctype -->
<form action="/upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username">
<br>
头像:<input type="file" name="upload">
<br>
<button>点击提交</button>
</form>
</body>
</html>
点击提交后的内容
下载formidable
包
npm i formidable
已经解决!!!!!!!!!!
看到弹幕说换个版本
然后试了一下npm i formidable@2
没问题了
var express = require('express');
// const formidable =require('formidable')
var router = express.Router();
const formidable = require('formidable');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
//显示网页的表单
router.get('/upload',(req,res)=>{
res.render('upload')
})
//处理文件上传
router.post('/upload',(req,res)=>{
// 创建form表单
const form = formidable({multiples:true});
// 解析请求报文
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
console.log('00')
console.log(fields)
console.log(files)
res.send('ok')
});
})
module.exports = router;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
</head>
<body>
<h2>文件上传</h2>
<hr>
<!-- 文件上传时,必不可少的设置enctype -->
<form action="/upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username">
<br>
头像:<input type="file" name="upload">
<br>
<button>点击提交</button>
</form>
</body>
</html>
router.post('/upload',(req,res)=>{
// 创建form表单
const form = formidable({
multiples:true,
//设置文件的保存路径
uploadDir:__dirname+'/../public/images',
// 保存文件后缀名
keepExtensions:true
});
// 解析请求报文
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
console.log(fields)
console.log(files)
res.send('ok')
});
})
让用户获得图像
let url='/images/'+files.upload.newFilename
// 学完数据库,将此数据保存在数据库中
res.send(url)