// 这是路由模块
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()
// 3. 挂载具体的路由
router.get('/user/list', (req, res) => {
res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
res.send('Add new user.')
})
// 4. 向外导出路由对象
module.exports = router
介绍:
此文章为黑马程序员视频参考笔记
仅供回顾内容参考
目录
一、初识Node.js
1.1Node.js是什么,它是用来做什么的?
对一些特殊用例进行优化,提供替代的API,使得V8在非浏览器环境下运行得更好,V8引擎执行Javascript的速度非常快,性能非常好,基于ChromeJavaScript运行时建立的平台,用于方便地搭建响应速度快、易于扩展的网络应用。
发布于2009年5月,由RyanDahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型,让JavaScript运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。
功能模块:Node使用Module模块去划分不同的功能,以简化应用的开发。Modules模块有点像C++语言中的类库。
每一个Node的类库都包含了十分丰富的各类函数,比如http模块就包含了和http功能相关的很多函数,可以帮助开发者很容易地对比如http,tcp/udp等进行操作,还可以很容易的创建http和tcp/udp的服务器。
要在程序中使用模块是十分方便的,只需要如下:在这里,引入了http类库,并且对http类库的引用存放在http变量中了。
这个时候,Node会在我们应用中搜索是否存在node_modules的目录,并且搜索这个目录中是否存在http的模块。
如果找不到这个目录,则会到全局模块缓存中去寻找,用户可以通过相对或者绝对路径,指定模块的位置。
1.2node.js能干什么?
1.JavaScript为HTML设计师提供了一种编程工具HTML创作者往往都不是程序员,但是JavaScript却是一种只拥有极其简单的语法的脚本语言!
几乎每个人都有能力将短小的代码片断放入他们的HTML页面当中。
2.JavaScript可以将动态的文本放入HTML页面类似于这样的一段JavaScript声明可以将一段可变的文本放入HTML页面:document.write(""+name+"")3.JavaScript可以对事件作出响应可以将JavaScript设置为当某事件发生时才会被执行,例如页面载入完成或者当用户点击某个HTML元素时。
4.JavaScript可以读写HTML元素JavaScript可以读取及改变HTML元素的内容。
5.JavaScript可被用来验证数据在数据被提交到服务器之前,JavaScript可被用来验证这些数据。
6.JavaScript可被用来检测访问者的浏览器JavaScript可被用来检测访问者的浏览器,并根据所检测到的浏览器,为这个浏览器载入相应的页面。
7.JavaScript可被用来创建cookiesJavaScript可被用来存储和取回位于访问者的计算机中的信息
1.3前端用nodejs能做什么
到底是什么?是一个JavaScript的编译环境,当前端语言JavaScript在写完之后可以交给进行编译和解释,它的存在对于JavaScript有了质的飞跃。
下面就是一个简单的命令#node目前,在大部分领域都占有一席之地,尤其是I/O密集型的。比如Web开发,微服务,前端构建等。
不少大型网站都是使用作为后台开发语言的,用的最多的就是使用做前端渲染和架构优化,比如淘宝双十一、去哪儿网的PC端核心业务等。
另外,有不少知名的前端库也是使用开发的,如Webpack是一个强大的打包器,React/Vue是成熟的前端组件化框架。
通常被用来开发低延迟的网络应用,也就是那些需要在服务器端环境和前端实时收集和交换数据的应用(API、即时聊天、微服务)。
阿里巴巴、腾讯、Qunar、百度、PayPal、道琼斯、沃尔玛和LinkedIn都采用了框架搭建应用。
另外,编写的包管理器npm已成为开源包管理了领域最好的生态,直接到2017年10月份,有模块超过47万,每周下载量超过32亿次,每个月有超过700万开发者使用npm。
是一个对于前端工作者不可或缺的工具。尤其是对于JavaScript有着巨大的提升,现阶段的应用已经有了非常蓬勃的发展。对于的学习和熟练运用,必不可少!
1.4nodejs可以用来做什么
1、nodejs搭配MongoDB作后端;
2、nodejs搭配“终端”作前端的编译工具使用;
3、编辑一些小工具,例如“网络爬虫”啥的;
4、在不使用浏览器的控制台功能时,可用nodejs达到同样的目的
1.5nodejs 具体是做什么用的
是一个运行在chromeJavascript运行环境下(俗称GoogleV8引擎)的开发平台,用来方便快捷的创建服务器端网络应用程序。
你可以把它理解为一个轻量级的JSP或PHP环境,但是用来开发Web应用的话,有时要便捷很多。很多人都不明白,为什么一个javascript的东西用在了服务器端的开发上。
一般认为javascript是浏览器端的脚本语言,但是google将其再开发,用来作为服务器端脚本环境,其性能自称比Python、Perl、PHP还要快。
的最大优点是处理并行访问,如果一个web应用程序同时会有很多访问连接,就能体现使用的优势。另一个好处是,使用javascript作为服务器端脚本语言,可以消除一些与浏览器端js脚本的冲突。
甚至发挥javascript动态编程的特性,在服务器与浏览器之间建立直接的动态程序。
1.6 node能做什么?
是一个运行在chromeJavascript运行环境下(俗称GoogleV8引擎)的开发平台,用来方便快捷的创建服务器端网络应用程序。
的优点是:
1、处理并行访问,如果一个web应用程序同时会有很多访问连接,就能体现使用的优势。
2、使用javascript作为服务器端脚本语言,可以消除一些与浏览器端js脚本的冲突。
甚至发挥javascript动态编程的特性,在服务器与浏览器之间建立直接的动态程序。
1.7node.js中的JavaScript运行环境
1、浏览器是JavaScript的前端运行环境
2、Node.js是JavaScript的后端运行环境
3、Node.js中无法调用DOM和BOM等浏览器内置API
1.8node.js的学习路径
二、fs文件系统模块
2.1 读取指定文件中的内容
//引入fs模块读取文件内容
const fs=require('fs')
/**
* fs.readFile()
* path:必选参数,字符串,表示文件内容
* option:可选参数,表示以什么编码格式来读取文件
* callback:必选参数,文件读取完成后,通过回调函数拿到读取的结果
*/
fs.readFile('12.txt','utf8',function(err,dataStr){
console.log(err);//读取成功为NULL 读取失败为错误对象
console.log("---------");
console.log(dataStr);//读取成功 这是一段测试文本 读取失败为undefined
})
2.2 写入指定文件内容
//向指定的文件写入内容
/**
* fs.writeFile()
* path:必选参数,字符串,表示文件路径
* data [option]:表示写入的内容
* callback:必选参数,文件写入完成后,通过回调函数拿到读取的结果
*/
fs.writeFile('1.txt','hahahahha',function(err){
console.log(err);//写入为NULL 写入失败为错误对象
})
2.3 成绩管理练习
const fs=require('fs')
fs.readFile('score.txt','utf8',(err,data)=>{
//判断是否读取成功
if(err){
return console.log('读取文件失败 \n'+err.message)
}
console.log("读取文件成功!"+data);
const arrOld=data.split(" ")
console.log(arrOld);
const arrNew=[]
arrOld.forEach(item => {
arrNew.push(item.replace('=',':'))
});
const newStr=arrNew.join('\r\n')
//writeFile 只能用来创建文件 不能用来创建路径 并且 重新写入的数据会覆盖原本文档中的数据
fs.writeFile('score.txt',newStr,(err)=>{
console.log(err);
if(err){
return console.log("写入文件失败!"+err.message);
}
console.log("写入文件成功");
})
})
2.4 fs模块-路径动态拼接问题
// __dirname 表示当前文件所处路径
console.log(__dirname);
三、path路径模块
3.1 什么是path路径模块
const path =require("path")
// console.log(path);
/**
* path.join():可以把多个路径片段拼接为完整 的路径字符串
* path.join([...paths])
* ...paths<string> 路径片段的序列
* 返回值<string>
*/
const pathStr= path.join('/a',"/b/c","../../","/d","/e")// ../会抵消前面一个路径 如/c就会被抵消掉
console.log(pathStr);//打印出来为 \a\d\e 几个../就抵消前面几个
/**
* path.basename(path[,ext]) //获取文件路径名
* path<string>必选参数,表示一个路径的字符串
* ext<string>可选参数,表示文件扩展名
* 返回:<string>表示路径中的最后一部分
*/
var fpath="/a/b/c/index.html"
var fullname= path.basename(fpath)
console.log(fullname); //输出 index.html
var fullname= path.basename(fpath,".html")
console.log(fullname); //输出 index
/**
* path.extname() 获取文件扩展名部分
*
*/
var fpath="/a/b/c/index.html"
let fext= path.extname(fpath)
console.log(fext);//.html
3.3 时钟案列
const fs= require('fs')
const { basename } = require('path')
const path =require("path")
// 定义正则表达式
/**
* /s 表示匹配空白字符
* /S表示匹配非空白字符
*
*/
const regStyle=/<style>[\s\S]*<\/style>/
const regScript=/<script>[\s\S]*<\/script>/
fs.readFile(path.join(__dirname,"index.html"),'utf8',(err,data)=>{
console.log(err);
if(err){
return console.log("读取文件失败"+err.message);
}
// console.log(data);
resolveCSS(data)
resolveJS(data)
resolveHTML(data)
})
// 3.1 定义处理 css 样式的方法
function resolveCSS(htmlStr){
const r1 =regStyle.exec(htmlStr)
// console.log(r1)
//将提取出的字符串 进行替换操作
const newCSS=r1[0].replace("<style>","").replace('</style>',"")
console.log(newCSS);
// 将提取出来的内容放在index.css中去
fs.writeFile(path.join(__dirname,"index.css"),newCSS,(err)=>{
if(err) return console.log('写入失败');
console.log('写入成功');
})
}
// 4.1 定义处理 js 脚本的方法
function resolveJS(htmlStr) {
// 4.2 通过正则,提取对应的 <script></script> 标签内容
const r2 = regScript.exec(htmlStr)
// 4.3 将提取出来的内容,做进一步的处理
const newJS = r2[0].replace('<script>', '').replace('</script>', '')
// 4.4 将处理的结果,写入到 clock 目录中的 index.js 文件里面
fs.writeFile(path.join(__dirname, './clock/index.js'), newJS, function(err) {
if (err) return console.log('写入 JavaScript 脚本失败!' + err.message)
console.log('写入 JS 脚本成功!')
})
}
// 5.1定义处理HTML文本
function resolveHTML(htmlStr){
const newHtml=htmlStr.replace(regStyle.exec(htmlStr),"<link rel='stylesheet' href='index.css'/>")
.replace(regScript.exec(htmlStr),"<script src='index.js'></script>")
//将替换好的文件写入html中
fs.writeFile(path.join(__dirname,"index.html"),newHtml,(err)=>{
if(err) return "写入HTML失败!"
console.log("写入html成功");
})
}
四、http 模块
4.1什么是http模块
4.2创建基本的web服务器
// 1.导入http模块
const http=require("http");
//调用http.createServer()方法 即可快速创建一个web服务器实列
//2.创建web 服务器实列
const server=http.createServer()
//使用服务器实验的.on()方法,为服务器绑定一个request事件
//3.为服务器石烈绑定request事件 监听客户端的请求
server.on('request',(req,res)=>{
// 只要有客户端来请求我们自己的服务器,就会触发request 事件,从而调用这个事件处理函数
console.log("someone require your web");
// console.log(res);
})
//调用服务器实列的.listen()方法 即可启动当前的web服务器是列
//4.启动服务器
server.listen(8080,()=>{
console.log('http://127.0.0.1:8080');
})
req res 的方法
/**
* req 是请求对象 包含了客户端相关的数据和属性
* res 是响应对象 包含了与服务器相关的数据和属性
*/
const url=req.url
const method=req.method
// console.log(url,method);
/**
* res.end()方法的作用
* 要发送到客户端的字符串
* 向客户端发送指定的内容,并结束这次请求的处理过程
*/
// res.end("浏览器界面")
/**
* 当发送内容包含中文的时候会出现乱码问题
* 为了防止中文乱码的问题,需要设置响应头
* Content-type 的值为 text/html;charset=utf-8
*/
res.setHeader('Content-type','text/html;charset=utf8')
res.end("浏览器界面")
// 3.2 把请求的 URL 地址映射为具体文件的存放路径
// const fpath = path.join(__dirname, url)
// 5.1 预定义一个空白的文件存放路径
let fpath = ''
if (url === '/') {
fpath = path.join(__dirname, './clock/index.html')
} else {
// /index.html
// /index.css
// /index.js
fpath = path.join(__dirname, '/clock', url)
}
五、node.js中的模块化
5.1模块化分类
5.2加载模块
注意:使用require方法加载其他模块时候,会执行其他模块中的代码
// 当前这个文件,就是一个用户自定义模块 m1.js
console.log('加载了06这个用户自定义模块')
//m2.js
const m1 = require('./m1.js') //可以省略.js
console.log(m1)
//输出 加载了06这个用户自定义模块
//{}
5.3 模块作用域
引入其他文件 无法访问其他文件中的变量 方法
防止全局变量污染的问题
js中是没有防止全局变量污染的
5.5 module对象
1.module对象
在每个.js文件自定义模块中都有一个module对象,它里面存储了和其当前模块有关的信息,打印如下:
// 在一个自定义模块中,默认情况下, module.exports = {}
const age = 20
// 向 module.exports 对象上挂载 username 属性
module.exports.username = 'zs'
// 向 module.exports 对象上挂载 sayHello 方法
module.exports.sayHello = function() {
console.log('Hello!')
}
module.exports.age = age
// 让 module.exports 指向一个全新的对象
module.exports = {
nickname: '小黑',
sayHi() {
console.log('Hi!')
}
}
//其他文件引用 打印出来就是{ nickname: '小黑', sayHi: [Function: sayHi] }
向外共享模块作用域中的成员
注意点:使用require()方法导入模块时,导入的结果,永远以module.exports指向对象为准。
5.6 exports对象
// console.log(exports)
// console.log(module.exports)
// console.log(exports === module.exports) //true
const username = 'zs'
module.exports.username = username
exports.age = 20
exports.sayHello = function() {
console.log('大家好!')
}
// 最终,向外共享的结果,永远都是 module.exports 所指向的对象
//其他文件引用打印输出 { username: 'zs', age: 20, sayHello: [Function (anonymous)] }
5.7 npm 与包
5.8 淘宝镜像 cnpm 配置
方法一:
使用阿里定制的cnpm命令行工具代替默认的npm,输入以下代码
npm install -g cnpm --registry=http://registry.npmmirror.com
检查是否安装成功:
$ cnpm -v
安装成功之后,以后安装依赖包的方式和npm的是一样的,只是npm的命令换成是cnpm就可以了。
方法二:
a:单次使用:
npm install --registry=http://registry.npmmirror.com
b:永久替换:
在开发react-native的时候,不要使用cnpm,cnpm安装的模块路径比较奇怪,packager不能正常识别。
所以,为了方便开发,我们最好是直接永久使用淘宝的镜像源
直接命令行的设置
$ npm config set registry http://registry.npmmirror.com
手动修改设置
1.打开.npmrc文件(C:\Program Files\nodejs\node_modules\npm\npmrc,没有的话可以使用git命令行建一个( touch .npmrc),用cmd命令建会报错) 2.增加 registry =http://registry.npmmirror.com 即可。
如果需要恢复成原来的官方地址只需要执行如下命令:
npm config set registry https://registry.npmjs.org
检测是否安装成功:
npm config get registry
npm init -y //初始化包 快速创建一个package.json
5.9 devDependencies 结点
如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDpendencies结点中,与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点中去
//安装指定包,并记录到devDependencies节点中 npm i 包名 -D //注意:上述命令是简写形式,等价于下面完整的写法 npm install 包名 --sava-dev
通过查看包的官方文档来判断此包是否需要加入到devDependencies节点中
5.9.1 nrm工具 解决下包速度慢的问题
# 通过npm 包管理器,将nrm 安装为全局可用的工具 npm i nrm -g #查看所有可用的镜像源 nrm 1s #将下包的镜像源切换为taobao 镜像 nrm use taobao
5.9..2 项目包
/**
* 那些被安装到项目的node_modules目录中的包都是项目包
* 项目包又分两类,分别是:
* 1、开发依赖包(被记录到devDependencies节点中的包,只会在开发期间用得到)
* 2、核心依赖包(被记录到dependencies节点中的包,在开发期间和项目上线后都会用得到)
*
*/
npm i 包名 -D //开发依赖包(会被记录到devDependencies节点下)
npm i 包名 //核心依赖包 (会记录到dependencies节点下)
npm i 包名 -g //全局包 卸载全局去安装包 npm uninstall 包名 -g
/**
* 只有工具类的包才有全局安装的必要性 因为他们提供了好用的终端命令
* 判断某个包是否需要全局安装后才能使用,可以参考官网提供的使用说明即可
*/
六、express
1.express:基于 Node.js 平台,快速、开放、极简的 Web 开发框架 🔗
// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()
// 4. 监听客户端的 GET 和 POST 请求,并向客户端响应具体的内容
app.get('/user', (req, res) => {
// 调用 express 提供的 res.send() 方法,向客户端响应一个 JSON 对象
res.send({ name: 'zs', age: 20, gender: '男' })
})
app.post('/user', (req, res) => {
// 调用 express 提供的 res.send() 方法,向客户端响应一个 文本字符串
res.send('请求成功')
})
app.get('/', (req, res) => {
// 通过 req.query 可以获取到客户端发送过来的 查询参数
// 注意:默认情况下,req.query 是一个空对象
console.log(req.query)
res.send(req.query)
})
// 注意:这里的 :id 是一个动态的参数
// username后面接?表示可选
app.get('/user/:ids/:username', (req, res) => {
// req.params 是动态匹配到的 URL 参数,默认也是一个空对象
console.log(req.params)
res.send(req.params)
})
// 3. 启动 web 服务器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
6.1使用exoress.static对外提供静态资源
const express = require('express')
const app = express()
// 在这里,调用 express.static() 方法,
// 快速的对外提供静态资源
/**
* 托管多个静态资源目录
* 多次调用app.use(express.static())
* ./files路径不会出现在url地址上
*/
app.use('/files', express.static('./files'))
app.use(express.static('./clock'))
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
6.2 nodemon
使用方法 nodemon 项目名字
6.3 express 路由
6.4 模块化路由
index.js
const express = require('express')
const app = express()
// app.use('/files', express.static('./files'))
// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块
// /api 表示 请求前面必须加/api 才能访问到
app.use('/api', router)
// 注意: app.use() 函数的作用,就是来注册全局中间件
app.listen(80, () => {
console.log('http://127.0.0.1')
})
router.js
// 这是路由模块
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()
// 3. 挂载具体的路由
router.get('/user/list', (req, res) => {
res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
res.send('Add new user.')
})
// 4. 向外导出路由对象
module.exports = router
6.4 Express 中间件
next函数的作用
express.josn() express.urlencoded()中间件使用
// 导入 express 模块 const express = require('express') // 创建 express 的服务器实例 const app = express() // 注意:除了错误级别的中间件,其他的中间件,必须在路由之前进行配置 // 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据 app.use(express.json()) // 通过 express.urlencoded() 这个中间件,来解析 表单中的 url-encoded 格式的数据 app.use(express.urlencoded({ extended: false })) app.post('/user', (req, res) => { // 在服务器,可以使用 req.body 这个属性,来接收客户端发送过来的请求体数据 // 默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefined console.log(req.body) res.send('ok') }) app.post('/book', (req, res) => { // 在服务器端,可以通过 req,body 来获取 JSON 格式的表单数据和 url-encoded 格式的数据 console.log(req.body) res.send('ok') }) // 调用 app.listen 方法,指定端口号并启动web服务器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
6.5 使用express 写接口
3. 什么是 CORS
CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源。
浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了 CORS 相关的 HTTP 响应头,就可以解除浏览器端的跨域访问限制。
CORS 响应头部
响应头部中可以携带一个 Access-Control-Allow-Origin 字段,其语法如下:
Access-Control-Allow-Origin:<origin>|*
其中,origin参数的值指定了允许访问该资源外域URL。
列如,下面的字段值将只允许来自 http://itcast.cn
如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *,表示允许来自任何域的请求,示例代码如下:
CORS 响应头部 - Access-Control-Allow-Headers
默认情况下,CORS 仅支持客户端向服务器发送如下的 9 个请求头: Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type (值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一) 如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过 Access-Control-Allow-Headers 对额外的请求头进行声明,否则这次请求会失败!
CORS 响应头部 - Access-Control-Allow-Methods
默认情况下,CORS 仅支持客户端发起 GET、POST、HEAD 请求。 如果客户端希望通过 PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,通过 Access-Control-Alow-Methods来指明实际请求所允许使用的 HTTP 方法。
示例代码如下:
6.8. CORS请求的分类
客户端在请求 CORS 接口时,根据请求方式和请求头的不同,可以将 CORS 的请求分为两大类,分别是:
简单请求
请求方式:GET、POST、HEAD 三者之一
HTTP 头部信息不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)
预检请求:
只要符合以下任何一个条件的请求,都需要进行预检请求:
请求方式为 GET、POST、HEAD 之外的请求 Method 类型
请求头中包含自定义头部字段 向服务器发送了 application/json 格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。
6.9 简单请求和预检请求的区别
简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求。