兄弟兄弟,想要独自开发做全栈吗,全栈啊!!!
如果是,那么学习nodejs
,就该是你进击全栈的必经之路!
Node.js
是一个 javascript
运行环境,它让 javascript
可以开发后端程序,实现几乎其他后端语言实现的所有功能这意味着我们只需要掌握 javascript
一门语言就能够进行全栈开发!
Node.js
基于V8引擎,而V8其实是Google发布的开源JavaScript
引擎,本身是用于Chrome
浏览器的js解释部分,但是总有大佬不按套路
出牌,Node
之父Ryan Dahl就把这个V8搬到了服务器上,用于做服务器的软件
Node.js
发布于2009年5月,经过十几年的发展,它已经成为前端中顶梁柱的存在,就算你不需要它进行服务端开发,它也存在于你开发过程中的方方面面,如前面提到的 Vue
, React
,Webpack
javascript
和 ES6
的语法单论后端开发,Node.js
作为 Javascript
的运行环境的性能虽不能与 Java
这类编译语言相比,但它独有的特性完全可以弥补这性能的差距,其至能够进行超越这也就是为什么它能够发展如此之快,能够受到越来越多人青睐的原因!看到这里你可能已经明白,为什么我会说 Node 是前端到全栈的必经之路!
官网下载:Node.js官网
进入官方网站,根据自己的需要去下载(建议下载稳定版本)
下载到本地,双击运行,一路 next 即可
在中端中输入如下命令:
node -v
npm -v
调用 fs.readFile
方法读取文件
//导入fs模块,来操作文件
const fs = require('fs')
fs.readFile('./test1.js','utf-8',(error,dataStr) => {
console.log(error)
console.log('-------')
console.log(dataStr)
})
调用 fs.writeFile
方法读取文件
fs.writeFile('./test2.js','hello node.js', 'utf-8', (error) => {
if (error) return console.log('文件写入失败:'+error.message)
console.log('文件写入成功')
})
同上,执行 node ./test.js
命令
若写入的目标文件不存在,则会自动创建,但不支持创建目录;若存在则会对目标文件的内容进行覆盖
考试成绩整理
目标:
整理前:
小红=99 小白=100 小黄=70 小黑=66 小绿=88
整理后:
小红:99
小白:100
小黄:70
小黑:66
小绿:88
业务逻辑代码 |
//导入fs模块
const fs = require('fs')
fs.readFile('./score.txt','utf-8',(error,dataStr) => {
if (error) return console.log('读取成绩文件失败')
console.log('读取文件成功')
//对读取到的数据进行拆分,空格拆分
let scoreList = dataStr.split(' ')
//创建新的数组,用来保存处理后的数据
let scoreListNew = []
scoreList.forEach(item => {
//用 : 替换 =
scoreListNew.push(item.replace('=',':'))
})
//把列表用 换行符(\n) 拆分为一个字符串,写入文件
fs.writeFile('./scoreNew.txt',scoreListNew.join('\n'),'utf-8',(err) => {
if (err) return console.log('写入文件失败')
console.log('写入文件成功')
})
})
运行结果:
零碎芝士
__dirname
当前文件所处的路径
console.log(__dirname)
//C:\...\src\demo2
path.join()
方法,用来将多个路径片段拼接成一个完整的路径字符串path.basename()
方法,用来从路径字符串中,将文件名解析出来path.extname()
方法,用来获取文件的拓展名path.join()
方法:const fs = require('fs')
const path = require('path')
// ../会抵消一层路径
const pathStr = path.join('/a','/b/c','../','./','/d')
console.log(pathStr)// \a\b\d
const pathStr2 = path.join(__dirname, '../demo2/score.txt')
console.log(pathStr2)
//C:\...src\demo2\score.txt
fs.readFile(pathStr2, 'utf-8', (error, dataStr) => {
if (error) return console.log('读取文件失败:' + error.message)
console.log('文件读取成功:' + dataStr)
})
path.basename()
方法://将路径的文件名称解析出来
const pathStr3 = path.basename(pathStr2)
console.log(pathStr3)//score.txt
//将路径的文件名称解析出来,写入第二个拓展名参数,会只得到文件名
const pathStr4 = path.basename(pathStr2,'.txt')
console.log(pathStr4)//score
path.extname
方法://获取文件的拓展名
const pathStr5 = path.extname(pathStr2)
console.log(pathStr5)//.txt
//获取路径的文件名
const pathStr6 = path.basename(pathStr2,path.extname(pathStr2))
console.log(pathStr6)//score
目标:将一个html+css+javascript的代码结构,拆分为三个模块,并引入css和javascript
匹配 标签的正则及文件内容读取和处理
const fs = require('fs')
const path = require('path')
//其中 \s表示空白符,\S表示非空白符;*表示匹配任意次
const regStyle = /','')
//将提取出来的css样式写入到css文件里
fs.writeFile(path.join(__dirname,'./index.css'),newCss,'utf-8',(err) => {
if (err) return console.log('写入数据失败')
console.log('写入数据index.css成功')
})
}
script
处理方法
function resolveJs(htmlStr) {
const r2 = regScript.exec(htmlStr)
const newJs = r2[0].replace('','')
//将提取出来的js写入到js文件里
fs.writeFile(path.join(__dirname,'./index.js'),newJs,'utf-8',(err) => {
if (err) return console.log('写入数据失败')
console.log('写入数据index.js成功')
})
}
定义处理 html
结构的方法
function resolveHtml(htmlStr) {
// console.log(regStyle)
// console.log(regScript)
const newHtml = htmlStr.replace(regStyle,'').replace(regScript,'')
// console.log(newHtml)
fs.writeFile(path.join(__dirname,'./index.html'),newHtml,'utf-8',(err) => {
if (err) return console.log('写入newHtml数据失败')
console.log('写入newHtml数据成功')
})
}
http
模块创建web服务实例 http.createServer()
请求路径:res.url
请求方法:res.method
端口号:8090
防止中文显示乱码的问题,需要设置响应头Content-Type值为:text/html;charset=utf-8:
resp.setHeader('Content-Type','text/html;charset=utf-8')
向客户端发送指定内容,并结束这次的请求的处理过程:resp.end()
//导入http模块
const http = require('http')
//创建web服务实例
const server = http.createServer()
//为服务器实例绑定request事件,监听客户端的请求
server.on('request', (res,resp) => {
// console.log('someone visit out web server')
console.log(`请求路径为:${res.url},请求方式为:${res.method}`)
resp.setHeader('Content-Type','text/html;charset=utf-8')
//resp.end()方法,向客户端发送指定内容,并结束这次的请求的处理过程
resp.end(`请求路径为:${res.url},请求方式为:${res.method}`)
})
//启动服务器
server.listen(8090, () => {
console.log('server running at http://127.0.0.1:8090')
})
根据请求的url
进行相应的设定响应
const http = require('http')
const server = http.createServer()
server.on('request', (res,resp) => {
const url = res.url
let content = '404 Not found!
'
if (url == '/' || url == '/index.html') {
content = '首页
'
} else if (url == '/about.html') {
content = '关于页面
'
}
resp.setHeader('Content-Type','text/html;charset=utf-8')
resp.end(content)
})
server.listen(8099, () => {
console.log('server running at http://127.0.0.1:8099')
})
http
模块小案例将上面拆分好的 html、css、JavaScript 模块进行加载响应
请求空地址或index.html页面时,均加载 index.html
页面显示
其他不存在页面响应为: 404 Not found
//导入http模块
const http = require('http')
//导入fs模块
const fs = require('fs')
//导入path模块
const path = require('path')
//创建web服务器
const server = http.createServer()
//监听web服务器的request事件
server.on('request', (req,resp) => {
//预定义空白的文件存放路径
fpath = ''
//获取到用户请求的url地址
const url = req.url
if (url == '/') {
fpath = path.join(__dirname, '/clock/index.html')
} else {
fpath = path.join(__dirname, '/clock'+url)
}
//根据映射过来的文件路径读取文件
fs.readFile(fpath,'utf-8',(err,dataStr) => {
//读取文件失败,进行相应404
if (err) return resp.end('404 Not fount
')
resp.end(dataStr)
})
})
//启动服务器
server.listen(8099, () => {
console.log('server running at http://127.0.0.1:8099')
})
CommonJS
部分每个模块内部,module
变量代表当前模块。
module
变量是一个对象,它的exports
属性(即 module.exports
)是对外的接口
加载某个模块,其实是加载该模块的module.exports 属性。require()
方法用于加载模块
自定义一个模块,并暴露属性和方法
// module.exports对象上挂载 username 属性
module.exports.username = 'ls'
// module.exports对象上挂载 hello 函数
module.exports.hello = function () {
console.log('你好:'+username)
}
定义之后未进行暴露,该属性为模块内部的私有成员
//模块内部的私有成员
let age = 20
module.exports
指向一个全新的对象
module.exports = {
name: 'zs',
age: 20,
hobby: ['吃饭', '睡觉', '喝酒'],
say() {
console.log('hello')
}
}
调用模块并使用模块内的方法、属性
const custom = require('./自定义模块')
console.log(custom)
exports
和 module.exports
的使用误区本质上,module.exports
和 exports
指向的是同一块内存地址
module.exports
指向一个全新的对象
exports.username = 'zs'
module.exports = {
gender: '男',
age: 20
}
//暴露出去的为:{gender: '男', age: 20}
exports
指向一个全新的对象
module.exports.username = 'zs'
exports = {
gender: '男',
age: 20
}
//暴露出去的为:{username: 'zs'}
共同为指向的内存地址添加属性
exports.username = 'zs'
module.exports.gender = '女'
//暴露出去的为:{username: 'zs', gender: '女'}
为 module.exports
添加一块新的对象
exports = {
username: 'zs',
gender: '男'
}
module.exports = exports
module.exports.age = 19
//暴露出去的为:{username: 'zs', gender: '男', age: 19}
一句解决:使用require
模块时,得到的永远是 module.exports
指向的对象
注意:为了防止混乱,建议不要在同一个模块中同时使用 exports
和 module.exports
npm
与包国外有一家IT 公司,叫做 npm
,Inc
这家公司旗下有一个非常著名的网站: npm官网
它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包,只要你有足够的耐心!到目前为止,全球约1100多万的开发人员,通过这个包共享平台,开发并共享了超过 120 多万个包 供我们使用 npm
, Inc. 公司
提供了一个服务器 ,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包
注意:
从 https://www.npmjs.com/ 网站上搜索自己所需要的包
从https://registry.npmjs.org/ 服务器上下载自己需要的包
npm
, Inc.公司
提供了一个包管理工具,我们可以使用这个包管理工具,从 服务器 把需要的包下载到本地使用。
这个包管理工具的名字叫做 Node Package Manager
(简称 npm
包管理工具),这个包管理工具随着 Nodejs
的安装包一起被安装到了用户的电脑上
npm 安装包命令(以 moment
包为例)
npm i moment
安装指定版本的包
npm i moment@2.24.0 (包名后跟@符+版本号,若该包已存在会进行覆盖)
包的语义化版本规范:
包的版本号是以“点分十进制”形式进行定义的,总共有三位数字,例如 2.24.0
其中每一位数字所代表的的含义如下
大版本
功能版本
Bug修复版本
版本号提升的规则: 只要前面的版本号增长了,则后面的版本号归零
moment
包可以在 npm官网 找到 moment
包
进入Moment.js Documentation
学习使用
moment
模块//定义格式化时间的方法
function dateFormat(dtStr) {
const dt = new Date(dtStr)
const year = dt.getFullYear()
const month = padZero(dt.getMonth() + 1)
const date = padZero(dt.getDate())
const hh = padZero(dt.getHours())
const mm = padZero(dt.getMinutes())
const ss = padZero(dt.getSeconds())
return `${year}-${month}-${date} ${hh}:${mm}:${ss}`
}
//定义补零的函数
function padZero(n) {
return n > 9 ? n : '0' + n
}
//暴露出去
module.exports = {
dateFormat
}
调用自己的自定义时间格式化模块
const TIME = require('./dateFormat')
// console.log(TIME)
// console.log(new Date())
console.log(TIME.dateFormat(new Date()))
moment
官方使用文档,简单用代码获取一个的当前时间//导入moment
const moment = require('moment')
//调用format方法
const dt = moment().format('YYYY-MM-DD HH:mm:ss')
console.log(dt)
自定义的时间模块代码量大,开发效率低;使用第三方成型的模块开发,书写少量代码,开发效率更高
初次装包完成后,在项目文件夹下多一个叫做 node_modules
的文件夹和 pakage-lock.json
的配置文件
其中:
node_modules
文件夹用来存放所有已安装到项目中的包。require()
导入第三方包时,就是从这个目录中查找并加载包package-lock.json
配置文件用来记录 node_modules
目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等信息
我们要知道一件事情
包的数据内容较大,不利于我们的网络传输,所以一般在传输项目文件时,都会把node_modules
文件剔除再进行网络传输,这就需要我们有一个文件来记录我们项目中都使用过哪些包,这样,别人在接收后才可以下载相应的包,来保证项目的正常运行,这就用到一个文件package.json
,里面的dependencies节点记录了使用的相关的包和版本号
{
"name": "nodejs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.24.0"
}
}
package.json
文件npm init -y
注意:
npm install
命令安装包的时候,npm
包管理工具会自动把包的名称和版本号,记录到 package.json
中在npm i
后包之间用空格隔开
npm i jquery art-template
当我们拿到一个剔除了node_modules
的项目之后,
想要一次性安装package_json
中dependencies
所有依赖包
可以执行命令:
npm install
或(npm i)
npm uninstall 包名
devDependencies
节点若某些包在开发阶段会用到,项目上线后不会再用,则建议把这些包记录到 devDependencies
节点中
对于项目开发阶段和上线阶段都会用到的则记录到 dependencies
节点中
可以使用命令如下,将包记录到devDependencies
节点中
安装指定的包并记录到devDependencies
节点中:
npm i 包名 -D
上述命令为简写形式,等价于下面的完整写法:
npm install 包名 --save-dev
在使用 npm
下包的时候,默认从国外的 https://registry.npmjs.org/ 服务器进行下载
此时,网络数据的传输需要经过漫长的 海底光缆
,因此下包速度会很慢
淘宝在国内搭建了一个服务器,专门把国外官方服务器上的包同步到国内的服务器,
然后在国内提供下包的服务从而极大地提高了下包的速度
扩展:
镜像(Mirroring)是一种文件存储形式,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本即为镜像
下包的镜像源,指的就是下包的服务器地址
# 查看当前的下包镜像源
npm config get registry
# 将下包的镜像源切换为淘宝镜像
npm config set registry https://registry.npm.taobao.org/
# 检查镜像资源是否切换成功
npm config get registry
为了更方便的切换下包的镜像源,我们可以安装 nrm 这个小工具
利用 rm 提供的终端命令,可以快速查看和切换下包的镜像源
# 通过 npm 包管理器,将 nrm 安装为全局可用的工具
npm i nrm -g
#查看所有可用的镜像源
nrm ls
# 将下包的镜像源切换为 taobao 镜像
nrm use taobao
那些被安装到项目的 node_modules 目录中的包,都是项目包
项目包又分为两类,分别是:
开发依赖包(被记录到 devDependencies 节点中的包,只在开发期间会用到)
核心依赖包(被记录到 dependencies 节点中的包,在开发期间和项目上线之后都会用到)
npm i 包名 -D # 开发依赖包 (会被记录到 devDependencies 节点下)
npm i 包名 # 核心依赖包 (会被记录到 dependencies 节点下)
在执行 npm install 命令时,如果提供了-g 参数,则会把包安装为全局包
全局包会被安装到 C:\Users\用户目录\AppData\Roaming\npm\node_modules 目录下.
npm i package_name -g # 全局安装指定的包
npm uninstall package_name -g # 卸载指定的安装全局的包
注意:
只有工具性质的包,才有全局安装的必要性。因为它们提供了好用的终端命令;
判断某个包是否需要全局安装后才能使用,可以参考官方
提供的使用说明即可
i5ting_toc 是一个可以把md 文档转为html页面的小工具,使用步骤如下:
# 将 i5ting_toc 安装为全局包
npm i i5ting_toc -g
# 调用 i5ting_toc ,实现 md 转 html 功能
i5ting -f filePath -o
新建一个包目录(pac_tolls),包含以下三个文件:
{
"name": "pac_tools",
"version": "1.0.0",
"main": "index.js",
"description": "提供了格式化时间, html Escape的功能",
"keywords": ["hello", "dateFormat", "htmlEscape", "htmlUnEscape"],
"license": "ISC"
}
src -> dateFormat.js
中;HTML
字符串的功能,拆分到 src -> htmlEscape.js
中;index.js
中,导入两个模块,得到需要向外共享的方法在 index.js
中;module.exports
把对应的方法共享出去//格式化时间的方法
function dateFormat(dataStr) {
const dt = new Date(dataStr)
const year = dt.getFullYear()
const month = padZero(dt.getMonth() + 1)
const date = padZero(dt.getDate())
const hh = padZero(dt.getHours())
const mm = padZero(dt.getMinutes())
const ss = padZero(dt.getSeconds())
return `${year}-${month}-${date} ${hh}:${mm}:${ss}`
}
//补零的方法
function padZero(n) {
return n > 9 ? n : '0' + n
}
module.exports = {
dateFormat
}
//转换html的方法
function htmlEscape(htmlStr) {
return htmlStr.replace(/<|>|"|&/g, (match) => {
switch (match) {
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
//还原html的方法
function htmlUnEscape(str) {
return str.replace(/<|>|"|&/g, (match) => {
switch (match) {
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
module.exports = {
htmlEscape,
htmlUnEscape
}
/*包的入口文件*/
const date = require('./src/dateFormat')
const escape = require('./src/htmlEscape')
//hello world
function hello() {
console.log('hello world')
}
//对外暴露
module.exports = {
hello,
/*dateFormat: date.dateFormat,
htmlEscape: escape.htmlEscape,
htmlUnEscape: escape.htmlUnEscape*/
...date,
...escape
}
包根目录中的 README.md
文件,是包的使用说明文档;
通过它,我们可以事先把包的使用说明,以 markdown
的格式写出来,方便用户参考
README 文件中具体写什么内容,没有强制性的要求,只要能够清晰地把包的作用、用法、注意事项等描述清楚即可
我们所创建的这个包的 README.md 文档中,会包含以下6项内容:
## 安装
npm install pac_tools
## 导入
const pac_tools = require('pac_tools')
## 格式化时间
//调用dateFormat 对时间进行格式化
const dateStr = pac_tools.dateFormat(new Date())
console.log(dateStr)
//结果:2022-12-05 12:05:48
## 转义 html 中的特殊字符
//定义待转换的html字符串
const htmlStr = 'hellojsl
'
//调用htmlEscape转换方法
const str = pac_tools.htmlEscape(htmlStr)
console.log(str)
// 结果: <h1>hello<span>jsl</span></h1>
## 还原 html 中的特殊字符
//定义待转换的html字符串
const htmlStr = 'hellojsl
'
//调用htmlEscape转换方法
const str = pac_tools.htmlEscape(htmlStr)
//调用htmlUnEscape还原方法
console.log(pac_tools.htmlUnEscape(str))
// 结果: hellojsl
## 开源协议
ISC
npm 账号注册完成后,可以在终端中执行 npm login 命令;
依次输入用户名
、密码(盲打)
、邮箱
后,邮箱会收到一个验证码,输入校验码,即可登录成功
注意:
在运行npm login 命令之前,必须先把下包的服务器地址切换为 npm
的官方服务器。否则会导致发布包失败!
//查看切换服务器
npm config get registry
//切换服务器为npm服务器
nrm use npm
进入要发布的包的根目录,执行命令:
npm publish
运行:
npm unpublish 包名 --force
命令,即可从 npm 删除已发布的包
npm unpublish
命令只能删除 72 小时以内发布的包npm unpublish
删除的包,在24小时内不许重复发布npm
上发布没有意义的包!如果传递给 require()
的模块标识符不是一个内置模块,也没有以 ./
或 ../
开头,则 Node.js
会从当前模块的父目录开始,尝试从 /node_modules
文件夹中加载第三方模块。
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录.例如,假设在 C:Users\itheima\project foojs'
文件里调用了 require('tools')
,则 Node,js
会按以下顺序查找:
C:\Users\itheima\project\node_modules\tools
C:\Users\itheima\node_modules\tools
C:\Users\node_modules\tools
C: node_modules\tools
当把目录作为模块标识符,传递给 require()
进行加载的时候,有三种加载方式:
package.json
的文件,并寻找 main
属性,作为require()
加载的入口package.json
文件,或者 main
入口不存在或无法解析,则 Nodejs
将会试图加载目录下的index.js
文件Node.js
会在终端印错误消息,报告模块的缺失: Error: Cannot find module xx