新的开始,坚持就是胜利
基于chrome v8引擎的javascript运行环境
1、引擎用来解析,执行js代码
2、提供内置api实现某些功能
const fs = require(‘fs’)
fs.readFile(path[,options],callback) 读取指定文件内容
- path: 文件路径
- options: 可选,表示以什么编码格式来读取文件
- callback: 文件读取完,通过回调函数拿到读取的结果
fs.writeFile(file,data[,options],callback) 向指定文件写入内容
-file: 指定一个文件路径的字符串,表示文件存放路径
-data:写入内容
-options: 写入文件的内容格式,默认utf-8
-callback: 文件写入完的回调函数
const fs = require('fs')
//文件读取
fs.readFile('./file.js','utf-8',function(err,data){
console.log(err) //读取失败:err的值为错误对象;读取成功: err = null
console.log('---')
console.log(data);//读取失败,dataStr=undefined
})
//文件写入
fs.writeFile('./files2.txt','hhshshsh',function(err){
console.log(err) //文件写入成功,err = null; 文件写入失败,err = 错误对象
})
__dirname: 表示当前文件所处的目录
- 不会出现路径拼接不好维护,或者相对路径动态拼接的问题
const fs = require('fs')
let str = ''
fs.readFile(__dirname+'/files2.txt','utf8',function(err,data){
if(!err){
str = handleData(data)
fs.writeFile(__dirname+'/files2.txt',str,function(err){
if(err){
console.log('文件写入失败!')
}
})
}else{
console.log('文件读取失败!')
}
})
const handleData = (str) => {
let newstr = ''
let arr = str.split(' ')
console.log(arr,'arr');
arr.forEach(item => {
let rstr = item.replace('=',':')
newstr += rstr+'\n'
})
console.log(newstr);
return newstr
}
用来处理路径的模块
const path = require(‘path’)
path.join([…paths]),用来将多个路径片段拼接成一个完整的路径字符串
- …paths
路径片段序列 - 返回值:拼接好的路径的字符串
- …/有抵消路径的功能
path.basename(path[,type]), 用来从路径字符串中,将文件名解析出来
- path: 文件存放路径
- type: 文件扩展名(.html),使用该参数,该方法会将文件扩展名删除
- 返回文件名
path.extname(path), 用来获取路径中的扩展名部分
- path: 路径
- 返回 扩展名字符串
const path = require('path')
const fs = require('fs')
fs.readFile(path.join(__dirname,'./file.js'),'utf8',(err,data)=>{
console.log(err,data)
console.log(path.basename(path.join(__dirname,'./file.js'),'.js')) //返回 file
console.log(path.extname(path.join(__dirname,'./file.js')) //返回.js
})
实现功能: 将一个包含html、js、css的html文件,分离成html、js、css三个单独的文件,并正常运行功能
const fs = require('fs')
const path = require('path')
const regStyle = /','')
fs.writeFile(path.join(__dirname,'./clock.css'),newCss,(err)=>{
if(err) return console.log('文件写入失败',err.message)
console.log(newCss,'文件写入成功!')
})
}
const resolveJs = (htmlstr) => {
const r1 = regScript.exec(htmlstr)
const newjs = r1[0].replace('','')
fs.writeFile(path.join(__dirname,'./clock.js'),newjs,(err)=>{
if(err) return console.log('文件写入失败',err.message)
console.log(newjs,'文件写入成功!')
})
}
const resolveHtml = (htmlstr) => {
let newhtml = htmlstr.replace(regStyle,').replace(regScript,'')
fs.writeFile(path.join(__dirname,'./newclock.html'),newhtml,(err) => {
if(err) return console.log('文件写入失败!',err.message)
console.log(newhtml,'文件写入成功!')
})
}
创建web服务器的模块
const http = require(‘http’)
const http = require('http')
const server = http.createServer()
server.on('request',(request,response)=>{
console.log(request.url,request.method)
//request: request.url :客户端请求的url地址
//request.method: 客户端请求的method类型
response.end(content) , 向客户端响应一些内容
})
server.listen(8080,()=>{
console.log('server running at http://127.0.0.1:8080')
})
当调用res.end()方法,向客户端发送中文内容的时候,会出现乱码问题,需要手动设置内容的编码格式。
server.on('request',(req,res) => {
let str = '返回内容'
//设置响应头 Content-Type:charset=utf-8
res.setHeader('Content-Type','text/html;charset=utf-8')
//把包含中文的内容,响应给客户端
res.end(str)
})
把一个大文件拆成独立并互相依赖的多个小模块
优点:
- 提高代码复用性
- 提高代码可维护性
- 可以实现按需加载
内置模块:由node.js官方提供的,如fs、path、http
自定义模块: 用户创建的js文件
第三方模块
require() // 使用该方法加载其他模块时,会执行被加载模块中的代码
const fs = require(‘fs) //内置
const custom = require(’./custom.js’)//自定义, 在自定义模块中,默认情况下,module.exports = {}
const moment = require(‘moment’)//第三方模块
类似函数作用域,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问
优点: 防止全局变量污染
module对象: 在每个.js自定义模块中,都有一个module对象,它里面存储了和当前模块有关的信息
- module.exports对象: 将模块内成员共享成员,外界通过require()方法导入自定义模块时,得到的就是module.exports所指向的对象
exports对象: 简化module.exports,默认情况下,exports和module.exports指向同一个对象,最终共享的结果,以module.export指向的对象为准
在require模块的时候,得到的永远是module.exports指向的对象
// test.js
exports.username = 'zs'
module.exports = {
gender:'男',
age:20
}
//require_test.js
const test = require('./test')
console.log(test) //{gender:'男',age:20}
上面代码中,改变了module.exports的指向,使module.exports与exports指向不同的对象
根据规则: 最终共享的结果,以module.export指向的对象为准,不难看出输出结果
Common.js模块化规范
- 每个模块内部,module变量代表当前模块
- module对象的属性:exports是对外的接口
- 加载某个模块,其实加载该模块的module.exports属性
npm安装指定版本的包:使用@指定版本号
npm i moment@2.22.2
模块在第一次加载后会被缓存,这意味着,多次调用require()不会导致模块的代码执行多次(会优先从缓存加载模块)
- 内置模块加载优先级最高
express: 专门用来创建web服务器
基于http内置模块进一步封装出来的
在项目所处目录,运行终端命令,安装express到项目中使用
npm i express@4.17.1
const express = require('express')
//创建web服务器
const app = express()
//调用app.listen(端口号,启动成功后的回调) ,启动服务器
app.listen(80,()=>{
console.log('express server running')
})
//监听get请求
app.get('请求url',function(req,res){
//处理函数
//req.query 默认:空对象
//客户端使用?name=zs&age=20 查询字符串形式,发送到服务器的参数,通过req.query.name[age]访问到
console.log(req.query) //{name:'zs',age:20}
//获取url中的动态参数 req.params
// 客户端url: /user/:id (:id 为动态参数)
console.log(req.params) // 请求传过来的:{id:value}
//向客户端发送json对象
res.send({name:'zs',age:20})
})
//监听post请求
app.post('请求url',function(req,res){
//处理函数
//向客户端发送文本内容
res.send('请求成功')
})
express.static()
通过该方法创建一个静态资源服务器
//假设public目录下包含三个文件:index.html、index.js、index.css
app.use(express.static('public')) //指定目录,外界就可以访问指定目录下面的所有文件,但访问路径里不包含public根路径
//外界访问路径: localhost:3000/index.html
// ...
在托管的静态资源访问路径之前,挂在路径前缀
app.use('/front',express.static('public'))
//后面访问public目录下的资源时,访问路径需要添加/front
//localhost:3000/front/index.html
使用nodemon工具,可以监听项目文件的变动,当代码修改后,nodemon自动重启项目
npm i -g nodemon
使用nodemone: 启动node项目的命令由 node app.js 变为 nodemon app.js
在express中,路由指客户端的请求与服务器处理函数之间的映射关系
分3部分组成:
- 请求的类型
- 请求的url地址
- 处理函数
路由的匹配过程:按照路由的顺序进行匹配,如果请求类型和请求url同时匹配成功,则express会将这次请求,转交给对应的function函数进行处理
app.METHOD(PATH,HANDLER)
创建模块化路由的步骤
- 创建路由模块对应的js文件
- 调用express.Router()函数创建路由对象
- 向路由对象上挂在具体的路由
- 使用module.exports向外共享路由对象
- 使用app.use()函数注册路由模块
//router.js
var express = require('express')
var router = express.Router()
router.get('/user/list',function(req,res){
res.send('get user list')
})
router.post('/user/add',function(req,res){
res.send('add new user')
})
module.exports = router
//server.js 使用router
const express = require('express')
const app = express()
//引入router对象,挂载到app上
const router = require('./router')
app.use(router)
app.listen(80,function(){
console.log('启动成功')
})
app.use()的作用,注册全局中间件
类似静态资源托管时统一挂载访问前缀一样,路由模块添加前缀的方式:
app.use('/front',router)
作用: 对请求进行预处理
格式: 本质是一个function处理函数
//定义一个中间件
let express = require('express')
let app = express()
app.get('/',function(req,res,next){
next() //中间件函数的形参列表中,必须包含next参数,路由处理函数只有req,res
//next() 作用: 把流转关系,转交给下一个中间件或路由
})
app.listen(3000)
next函数是实现多个中间件连续调用的关键,表示把流转关系转交给下一个中间件或者路由
客户端发起的任何请求,到达服务器之后,都会触发的中间件
定义全局中间件: 通过调用app.use(中间件函数)中间件的作用: 多个中间件之间,共享同一份req和res,基于此特性,可以在上游的中间件中,统一为req或res对象添加自定义的属性和方法,供下游的中间件或路由使用
定义局部生效的中间件: 不使用app.use()定义的中间件
const express = require('express')
const app = express()
//全局中间件
app.use((req,res,next)=>{
//由于req\res在多个路由中是共享的
req.startTime = Date.now()
next()
})
app.get('/user/list',function(req,res){
res.send('get user list'+req.startTime)
})
app.listen(80,function(){
console.log('启动成功')
})
//局部中间件
const midd = function(req,res,next){
console.log('这个是局部中间件')
next()
}
router.get('/user/list',midd,function(req,res){
res.send('get user list')
})
//定义多个局部中间件,只在路由内部生效
app.get('/',midd1,midd2,midd3...|[midd1,midd2,midd3],(req,res)=>{res.send()})
使用中间件的注意事项:
- 一般在路由之前注册中间件,错误级别中间件在所有路由之后,用来捕获错误并返回到也米娜
- 可以注册多个中间件,在多个中间件之间,共享req,res对象
- 应用级别: 绑定到app实例上的中间件(全局/局部)
- 路由级别: 绑定到router实例上
- 错误级别: 专门用来捕获整个项目中发生的异常错误,防止项目异常崩溃
- 内置中间件
- 第三方中间件
function(err,req,res,next) 注意是4个参数
app.get('/user/list',function(req,res){
throw new Error('服务器内部发生了错误')
res.send('get user list'+req.startTime)
})
app.use(function(err,req,res,next){
console.log('发生错误;'+err.message);
res.send('error'+ err.message)
})
express.static() 托管静态资源的内置中间件
express.json() 解析json格式的请求体数据
express.urlencoded() 解析url-encoded格式的请求体数据
req.body 获取post请求返回来的参数
app.use(express.json())
app.use(express.urlencoded({extended: false}))
在4.16.0之前版本,经常使用body-parser 第三方中间件,解析请求体数据
符合下面任何条件的请求,都需要进行预检请求
- 请求方式是GET、POST、HEAD之外的method类型
- 请求头中包含自定义头部字段
- 向服务器发送了application/json格式的数据
什么是预检请求:在浏览器与服务器正式通信前,浏览器会发送OPTION请求进行预检,以获知服务器是否允许该实际请求,所以这一次的OPTION请求称为‘预检请求’。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。