目录
三、动态路径 __dirname:表示当前文件所处的目录、path.join
记录一下从B站学习node过程~
练习文件目录结构:myproject/node.js、1.txt、testnode空文件夹
1.txt文件内容:Hello node.js node.js内容如下
一、文件读取 readFile
//node.js
//读取文件内容 fs.readFile()
const fs = require('fs')//导入fs模块
/*
参数一:文件路径
参数二:编码格式(可选参数)
参数三:回调函数(失败、成功)如读取成功err返回null,读取失败err返回错误信息,data返回undefined
*/
fs.readFile('./1.txt', 'utf-8', (err, data) => {
console.log('err',err)
console.log('data',data)
})
文件读取成功和失败运行结果
二、写入文件 writeFile
//node.js
const fs = require('fs')//导入fs模块,调用fs.writeFile()写入文件
/*
参数一:文件路径
参数二:写入内容
参数三:回调函数(失败、成功)如读取成功err返回null,读取失败err返回错误信息,data返回undefined
*/
fs.writeFile('./2.txt', 'HELLO NODE.JS', (err) => {
console.log('写入文件 err',err)
})
运行成功和失败的结果,在myproject目录下创建了2.txt,并写入内容HELLO NODE.JS,如果在电脑不存在的a盘下创建则会失败并返回错误信息
三、动态路径 __dirname:表示当前文件所处的目录、path.join
防止路径拼写或过长不利维护等问题,用node提供的__dirname 动态拼写路径
const fs = require('fs')
const path = require('path')//导入path模块,调用path.join()拼接路径
//拼接:__dirname+'/1.txt' 或者使用模板字符串:`${__dirname}/1.txt`
//一般不推荐这样写,可以用node提供的path.join
//fs.readFile(`${__dirname}/1.txt`, 'utf-8', (err, data) => {
//推荐用这种方式
fs.readFile(path.join(__dirname,'/1.txt'), 'utf-8', (err, data) => {
console.log('目录:', __dirname)
})
四、获取路径文件名 path.basename
const fs = require('fs')//导入fs模块,调用fs.readFile()读取文件
const path = require('path')//导入path模块,调用path.join()拼接路径
const forexample = 'E:/Desktop/前端/myproject/1.html'
console.log('获取文件名称:',path.basename(forexample))
console.log('获取不带后缀的文件名称:',path.basename(forexample,'.html'))
运行结果得到文件名称
五、提取某文件中的css、JS、html
新建一个与node.js同级的newHtml.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,user-scalable=no,minimum-scale=1,maximum-scale=1,viewport-fit=cover">
<title></title>
<link href="https://res.5i5j.com/wap/tools/common.js.css?t=1716465786" rel="stylesheet" type="text/css">
<link href="https://res.5i5j.com/wap/tools/jisuan.css?t=1716465786" rel="stylesheet" type="text/css">
<style>
*{font-size:16px;}
.test{height:100px;margin-bottom:10px;}
.test div{width:300px;height:60px;line-height: 60px;background: pink;}
</style>
</head>
<body class="test">
<div>这是一个新的 html</div>
<script>
function test(){
console.log('hello newHtml')
}
</script>
</body>
</html>
const fs = require('fs')//导入fs模块,调用fs.readFile()读取文件
const path = require('path')//导入path模块,调用path.join()拼接路径
/*
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
*/
/*正则定义匹配页面中的style和script标签*/
const regStyle = /<style>[\s\S]*<\/style>/
const regScript = /<script>[\s\S]*<\/script>/
fs.readFile(path.join(__dirname,'/input.html'),'utf-8',(err,data)=>{
if(err) return console.log('读取html文件失败',err.message)
// console.log('读取html文件成功',data)
resolveCSS(data)
})
//获取CSS内容
function resolveCSS(htmlStr){
const r1 = regStyle.exec(htmlStr)
const newSty = r1[0].replace('<style>','').replace('</style>','')
console.log('newSty==',newSty)
}
//获取JS内容
function resolveJS(htmlStr){
const r1 = regScript .exec(htmlStr)
const newJS = r1[0].replace('<script>','').replace('</script>','')
console.log('newJS==',newJS)
}
//获取html
function resolveHtml(htmlStr){
//直接获取html
// const newHtml = htmlStr.replace(regStyle,'').replace(regScript,'')
//获取html,把新创建的css和js引入
const newHtml = htmlStr.replace(regStyle,'<link rel="stylesheet" href="./output.css">').replace(regScript,'<script src="./output.js"></script>')
console.log('newHtml==',newHtml)
fs.writeFile(path.join(__dirname,'/testnode/output.html'),newHtml,(err)=>{
if(err) return console.log('写入html文件失败',err.message)
})
}
运行得到(注:先自己新建目录testnode空的文件夹)把得到的内容写入到新的文件中,如下
(r1 是一个数组)
六、http
域名 & 域名服务器(DNS,domain name server):IP 和 域名一 一对应,存这份关系的电脑就是域名服务器,
比如ping jd.com 得到的IP 和 jd.com 这就是一 一对应的关系
域名就是IP的别名,辅助人们记忆的;域名和IP的对应关系存放在域名服务器上(在浏览器中输入一个域名,会先经过域名服务器的地址转换,域名转换成IP,然后再进行访问)
端口号
每个端口号不能被多个web服务占用,实际应用中只有80端口可以省略不写
端口号就是一串数字,好比门牌号,可以在整个楼中准确的找到对应门牌号
七、启动创建web服务器
关于参数req、res
req 请求对象,res 响应对象
req:客户端相关的数据或属性,包含url、method等res:服务器相关的数据或属性,(res.end() 向客户端发送指定内容,并结束这次请求的处理过程)
//node.js
const http = require('http')//导入http模块,调用http.createServer()创建一个web服务器
const server = http.createServer()//创建一个web服务器
//监听客户端的请求
server.on('request', (req, res) => {
console.log('收到客户端的请求了') // 在浏览器请求listen里的地址就会执行这里
console.log('客户端请求的URL地址是:' + req.url)
console.log('客户端请求的method类型是:' + req.method)
})
//启动服务器
server.listen(8080, () => {//监听端口,启动服务器
console.log('服务器启动成功,地址是:http://127.0.0.1:8080')
})
运行结果如图一,如果在浏览器中输入地址回车后,会收到请求,(也可以用postman模拟post请求)
服务器响应
设置响应头:解决中文乱码问题,res.setHeader('Content-Type', 'text/html; charset=utf-8')
const http = require('http')//导入http模块,调用http.createServer()创建一个web服务器
const server = http.createServer()//创建一个web服务器
//监听客户端的请求
server.on('request', (req, res) => {
//如果不设置类型,中文的时候会乱码
res.setHeader('Content-Type', 'text/html; charset=utf-8')
const str = `收到客户端的请求了,请求的URL地址是:${req.url},请求的method类型是:${req.method}`
res.end(str)
/* 也可根据不同的页面地址返回不同的页面内容http://127.0.0.1:8080/ 、 http://127.0.0.1:8080/user.html
let htmlStr ='<div>找不到页面</div>'
if(req.url === '/'){
htmlStr = '<div>首页</div>'
}else if(req.url === '/user.html'){
htmlStr = '<div>个人中心页面</div>'
}
res.end(htmlStr)
*/
})
//启动服务器
server.listen(8080, () => {//监听端口,启动服务器
console.log('服务器启动成功,地址是:http://127.0.0.1:8080')
})
八、将资源请求的 url 地址映射为文件的存放路径
运行第五条中的文件,运行后访问 http://127.0.0.1/ 发现页面404,因为需要再地址栏把后面的也拼接才行 http://127.0.0.1/testnode/output.html
const fs = require('fs')
const path = require('path')
const http = require('http')
const serve = http.createServer()
serve.on('request',(req,res)=>{
const url = req.url
const newURL = path.join(__dirname,url)
// console.log(newURL)
// res.end(newURL)
fs.readFile(newURL,(err,data)=>{
if(err) return res.end('404 Not Found')
res.end(data)
})
})
serve.listen(80,()=>{
console.log('服务器启动成功')
})
如果想要直接访问 http://127.0.0.1/ 就可以打开output.html 那么就需要手动拼接好
const fs = require('fs')
const path = require('path')
const http = require('http')
const serve = http.createServer()
serve.on('request',(req,res)=>{
const url = req.url
let newUrl = ''
if(url =='/'){
newUrl = path.join(__dirname,'./testnode/output.html')
}else{
newUrl = path.join(__dirname,'/testnode'+url)
}
fs.readFile(newUrl,(err,data)=>{
if(err) return res.end('404 Not Found')
res.end(data)
})
})
serve.listen(80,()=>{
console.log('服务器启动成功')
})
九、模块
node.js中模块分为三大类:
1、内置模块:如node.js官方提供的fs、path、http等
2、自定义模块:用户创建的 .js 文件都是自定义模块
3、第三方模块:使用前需先下载
模块加载
//加载内置模块
const fs = require('fs')
//加载自定义模块
const costomModule = require('./test.js') //自定义的js文件路径
//加载第三方模块 也可以使用require,前提先下载好,和内置模块一样直接写模块名称就可以
注:使用require加载模块时,会执行被加载模块里面的代码
//node.js
//新建node01.js文件,并写如内容 console.log('这是一个用户自定义模块文件 node01.js')
//加载自定义模块
const module1 = require('./node01.js')
// const module1 = require('./node01') //.js 后缀可以省略
console.log(module1)
模块作用域
模块内函数和变量只能在模块内访问,外部是无法访问的,如:
//node.js
const module1 = require('./node01')
console.log(module1)
console.log(module1.username)
// node01.js
console.log('这是一个用户自定义模块文件 node01.js')
const username = '张三'
function sayHello(username) {
console.log('你好,我是' + username)
}
运行node.js 后并不能访问node01.js中的username,这就是模块作用域
自定义模块中都有一个module对象,里面存储了当前模块的信息,其中 exports 就是向外共享成员,让外面可以访问到内部成员 (module.exports 向外共享成员)
在自定义模块导入中 require 导入的永远是 module.exports 指向的对象,默认是空
exports === module.exports 打印得到的是true, exports 为了简化写法
//node.js
const module1 = require('./node01')
console.log(module1.username)
console.log(module1.sayHello('joy'))
//node01.js
console.log('这是一个用户自定义模块文件 node01.js')
module.exports.username = '张三'
module.exports.sayHello = (username)=>{
console.log('你好,我是' + username)
}
/*
如果下面继续添加,这时候会把上面的属性都覆盖掉,永远以module.exports指向的对象为准
module.exports = {
newname:'lily',
test(){
console.log('test方法')
}
}
*/
/*------- 或者直接使用 exports ---------*/
/*
exports.username = '张三'
exports.sayHello = (username)=>{
console.log('你好,我是' + username)
}
*/
模块加载机制
模块在第一次加载时会被缓存,不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高加载效率。一位置多次调用require()不会导致模块的代码被执行多次。
①、内置模块加载优先级最高。
②、自定义模块:用require()加载自定义模块时,必须以 ./ 或 ../ 开头路径标识符,如果不加,node会把它当做内置或三方模块进行加载。
③、三方模块:如果不是内置模块,也没有 ./ 、../ 开头标识,node.js会从当前模块的父目录开始,尝试从/node_modules 文件夹中加载第三方模块。如果没有找到,则会继续上移一级目录进行加载,直到文件系统的根目录。
④、目录作为模块:把目录做为模块标识符,传递给require()进行加载时会有三种情况:
-- 在被加载的目录下查找package.json文件,并找main属性,作为require加载入口
-- 若没有package.json文件或者main属性,则node.js试图加载目录下的index.js
-- 若以上都失败,node.js会在终端打印错误信息,报告模块缺失。
注:如果加载自定义模块省略扩展名时,node.js会按顺序分别尝试加载以下文件:
1)按确切的文件名
2)补全 .js扩展名进行加载
3)补全 .json扩展名进行加载
4)补全 .node扩展名进行加载
5)如果以上都没有,终端则会包加载失败
十、包
1、node.js中的第三方模块又叫做包,类似电脑和计算器指的是同一个东西,只是叫法不同;
2、包是基于内置模块封装出来的,可提高开发效率,包和内置模块之间的关系类似JQuery和浏览器内置API之间的关系。
1、下载安装包
可访问 npm | Home 搜索需要的包,访问 https://registry.npmjs.org/ 下载所需要的包
Node Package Manager(简称:npm 包管理工具)
npm install <完整包名> —> 简写 npm i <完整包名>
npm install <包名> -g ( 安装全局包)
如:
1)、下载安装格式化时间包 npm i moment
2)、如果是安装指定版本,在包名之后用@符号指定具体版本,如 npm i moment@2.23.1
3)、如果想要同时安装多个包,包名之间用空格,
如:npm i <包名1> <包名2> ==> npm i moment jquery
包的语义化版本规范,共三位数字(版本号‘点分十进制’形式):如2.23.1
第一位:大版本 ; 第二位:功能版本; 第三位:Bug修复版本;
如果前面版本号增长,后面版本号则归零。
2、导入三方包并使用
(三方包使用方法需自己查询三方文档,可在npm | Home 中搜对应的包查询文档)
const moment = require('moment')//导入的名称就是装包时候的名称
const dt = new Date()
console.log(dt,'----',moment(dt).format('YYYY-MM-DD HH:mm:ss'))
3、包的管理
通过package.json 文件记录项目中安装了哪些包,项目开发中会把node_module 添加到.gitignore会略文件中。
快速创建package.json 可通过命令 npm init -y (在新建项目文件夹后就可以执行这个命令)
注:项目目录不能出现中文、空格,只能英文
package.json 中 dependencies 是在运行 npm install 时才会添加的节点,专门记录用。
npm install 安装了哪些包;
开发和正式环境都会用到的包都会记录到 dependencies 节点里。
4、一次性安装所有包
npm install ( 或 npm i )
执行 npm install 命令时 ,npm包管理工具会先读取 package.json中的 dependencies 节点,读取到里面的包名和版本号后会一次性下载到项目中
5、卸载包
npm uninstall 包名
npm uninstall 包名 -g (卸载全局包)
执行命令后,会直接从dependencies 节点中移出对应的包
6、开发节点 devDependencies
如果只在开发节点使用,正式项的环境用不到,就会记录到devDependencies节点中
安装指定包并记录到 devDependencies 节点中
npm install <包名> --save-dev ==> 简写 :npm i <包名> -D
( 包名 和 -D\--save-dev 位置可以互换 )
如何判断这个包应该安装在哪个节点,可以在npm官网上搜对应的包,进入webpack安装教程里会有描述应该安装那个节点
7、解决下载包慢问题
npm 下载包默认从国外的 https://registry.npmjs.org/ 服务器下载,这时网络数据传输需要经过漫长海底光缆,所以会很慢。
淘宝NPM镜像服务器:淘宝在国内搭建了服务器,把国外服务器上的包同步到国内服务器上,然后再国内提供下包的服务,从而提高下包速度
镜像(Mirroring):一种文件存储形式,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本即为镜像
切换 npm 的下包镜像源
//查看当前下包镜像源 & 切换后也用此命令 检查镜像源是否下载成功
npm config get registry
//切换下包镜像源 ==>淘宝镜像源
npm config set registry=https://registry.npm.taobao.org/
除了以上命令可以切换下载源,还可以利用工具 nrm
// 通过npm 工具将 nrm 全局安装
npm i nrm -g
//查看所有可用镜像源
nrm ls
//切换镜像源
nrm use taobao
前面带着 * 的就是当前所在的服务器
8、包的分类
小工具:
i5ting_toc 是一个可以把md文档转换成html页面的工具
npm install -g i5ting_toc //全局安装
i5ting_toc -f 要转换的md文档路径 -o //调用 i5ting_toc,轻松实现 md 转 html 的功能
9、包的规范
1)、包必须以单独的目录而存在
2)、包的顶级目录下要必须包含 package.json 这个包管理配置文件
3)、package.json 中必须包含 name,version,main 这三个属性,分别代表包的名字、版本号、包的入口。
10、开发自己的包
1)、新建文件夹作为包的根目录 ,如:mypackage
2)、初始化包的基本结构
在包mypackage下新建三个文件,package.json 包管理配置文件、index.js 包的入口文件、README.md包的说明文档
3)、初始化package.json
{
"name":'mypackage-tools',//包名
"version": "1.0.0",//包的版本号
"main": "index.js",//包的入口文件
"description": "提供时间格式化、HTMLEscape的功能",//包的简短描述信息
"keywords":["dataFormat","escape","mypackage"],//搜索关键字
"license":"ISC",//这个包所遵循的开源许可协议,npm默认ISC
},
name包名:和文件夹名没关系,注意包名是唯一的不能重复,起名时先去官网看看名字是否被占用,如果被占用需要更换
version包的版本号:一个新包默认从1.0.0开始
main包的入口文件:外界用require导入这个包的时候,导入的就是main属性所指向的文件
description包的描述:当用户在 npm | Home 中搜索时展示给用户的描述信息
keywords搜索关键字:关键字自定义
license:这个包所遵循的开源许可协议,npm默认ISC。更多的相关协议可查看 七种开源许可证 - 简书
4)、在index.js中定义格式化时间方法
//mypackage\index.js
//定义时间格式化方法
function formatTime(date) {
const dt = new Date(date)
const year = date.getFullYear()
const month = padZero(date.getMonth() + 1)
const day = padZero(date.getDate())
const hour = padZero(date.getHours())
const minute = padZero(date.getMinutes())
const second = padZero(date.getSeconds())
const str = `${[year, month, day].map(formatNumber).join('-')} ${[hour, minute, second].map(formatNumber).join(':')}`
return str
}
//定义格式化数字方法
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
//定义补零方法
function padZero(str) {
return str>9?str:'0'+str
}
//定义html转义方法
function htmlEscape(str) {
//匹配到指定符号后返回对应的转义字符
return str.replace(/<|>|"|'/g, function(match) {
switch (match) {
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case "'":
return '''
}
})
}
//还原转义字符
function htmlUnescape(str) {
return str.replace(/<|>|"|'/g, function(match) {
switch (match) {
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case ''':
return "'"
}
})
}
//向外导出formatTime方法
module.exports = {
formatTime,
htmlEscape,
htmlUnescape
}
验证一下方法是否可用,可以在node.js中引入运行下
//node.js
//'./mypackage/index' 这里不用加index是因为我们在main.js中定义了入口文件
const tools = require('./mypackage')
console.log(new Date(),'----',tools.formatTime(new Date()))
const htmlStr = '<div>hello </div><h1 class="title">world</h1>'
console.log('未转义:',htmlStr)
console.log('转义:',tools.htmlEscape(htmlStr))
console.log('还原转义:',tools.htmlUnescape(tools.htmlEscape(htmlStr)))
到目前为止目录结构
模块化拆分
比如index.js中有两个功能,一个时间格式化的功能,一个html语义化的功能,可以将这两个进行拆分,具体步骤:
①、在mypackage目录中新建src目录
②、将时间功能放到src -> 新建dateFormat.js 中
将html功能放到 src -> 新建 htmlFormat.js中
③、在index.js中导入这两个模块,在用module.exports 导出这两个功能
//mypackage\index.js
const fTime = require('./src/dateFormat')
const hEscape = require('./src/htmlFormat')
//向外导出formatTime方法
//用es6的扩展运算符 ... 直接导入共享出来的方法即可
module.exports = {
// formatTime,
// htmlEscape,
// htmlUnescape
...fTime,
...hEscape
}
//mypackage/src/dateFormat.js
// 把原来 index.js中的formatTime()、formatNumber()、padZero() 都剪切到这个文件中
//然后向外暴露这些方法
module.exports = {
formatTime,
}
//mypackage/src/htmlFormat.js
// 把原来 index.js中的htmlEscape()、htmlUnescape() 都剪切到这个文件中
//然后向外暴露这些方法
module.exports = {
htmlEscape,
htmlUnescape
}
这样就实现了模块化拆分,运行结果如下 (这里我的README文件名写错了,自己的别写错)
使用说明文档 README.md
## 安装
``
npm install mypackage
``
## 导入
``
const mypackage = require('mypackage-tools')
``
## 开源协议
ISC
// …… 尽量主要的 如何安装、如何使用等
//让用户看到文档就可以清楚知道这个包的功能实现了什么样的效果,如和使用等等
11、发布包
①、注册账号
需要先注册npm账号,访问 https://www.npmjs.com/ 点击 sign up按钮进行注册
②、登录npm账号
注:
1、需要先把下包服务器地址切换到npm的官方服务器地址。
(通过命令 ' npm config get registry ' 查看当前所在服务器地址,如果不是官方地址 通过' npm config set registry=https://registry.npmjs.org/ '进行切换)
2、不是在网站上登录,而是通过命令 npm login 在终端登录。
不是官方服务器地址时运行结果如下
注册完账号并切换到官方服务器后,运行命令npm login 会给一个登录地址,并给注册邮箱发送一次性验证码进行登录
③、发布
将终端切换到包的根目录下 ,运行 npm publish 命令,点击https://www.npmjs.com/页面上头像下拉里的 packages 选项,里面就会有刚刚发布成功的包(注意包名一定不要和线上已有的重复否则发布失败,开始我的包名叫mypackage-tools 就一直发布失败,后来改成packagestest-tools可以了)
这时在npm里通过包名就可以检索到自己刚刚发的
④、删除已发布的包
运行 npm unpublish <包名> --force 命令,即可从npm上删除已发布的包
注意:
1、命令npm unpublish 只能删除72小时以内发布的包,如果超过这个时间就永远不能删除。
2、删除包24小时内,不能重复发布该包
3、尽量不要发布无意义的包