node.js一些基础用法
模块化开发
传统非模块化开发有如下缺点:
1. 命名冲突
2. 文件依赖
标准的模块化规范:
1. AMD - requirejs
2. CMD - sea.js
文件加载是异步的
服务器端的模块化规范:
1. CommonJS - Node.js 文件加载是同步的
模块化相关的规则:
1. 如何定义模块: 一个js文件就是一个模块,模块内部的成员(变量和函数)都是相互独立的
2. 模块成员的导出和引入
3. 已经加载的模块会缓存,多次require同一个文件没效果
4. 模块文件后缀三种情况
1. .js
2. .json
3. .node
文件不加后缀名时,模块加载优先顺序为.js => .json => .node
//01.js let sum =function(a,b){ return a+b } exports.sum = sum //导出模块成员 //分割线 module.exports = sum //另一种方法
//02.js let module = require('./01.js') let ret = module.sum(12,13) console.log(ret) //25 //分割线 let ret = module(12,15) console.log(ret) //27
//03.js let flag = 123 global.flag = flag
//04.js require('./03.js') console.log(global.flag) //
//05.json { "username":"Tom" }
//06.js let m = require('./05.json') console.log(m.username) //Tom
文件操作案例(初始化目录结构)
初始化数据
const path = require('path') const fs = require('fs') let root = 'C:\\Users\\Administrator\\Desktop' let fileContent = `` let initData = { projectName : 'myDemo', data:[ { name:'img', type:'dir' }, { name:'css', type:'dir' }, { name:'js', type:'dir' }, { name:'index.html', type:'file' }, ] } //创建项目根路径 fs.mkdir(path.join(root,initData.projectName),()=>{ //创建子目录和文件 initData.data.forEach((item)=>{ if(item.type == 'dir'){ //创建子目录 fs.mkdirSync(path.join(root,initData.projectName,item.name)) }else if(item.type == 'file'){ //创建文件写入内容 fs.writeFileSync(path.join(root,initData.projectName,item.name),fileContent) } }) })
Buffer基本操作
Buffer本质上就是字节数组,Buffer对象是Node处理二进制数据的一个接口,他是Node原生提供的全局对象,可以直接使用
1. 构造方法(类)
2. 静态方法
3. 实例方法
let buf =new Buffer(5) console.log(buf) //5个字节的实例 不推荐使用 let buf =Buffer.alloc(5) console.log(buf) //<Buffer 00 00 00 00 00> let buf = Buffer.from('hello') console.log(buf) //<Buffer 68 65 6c 6c 6f> console.log(buf.toString()) //hello
功能方法
1. Buffer.isEncoding() 判断是否支持该编码
2. Buffer.isBuffer() 判断是否为Buffer
3. Buffer.byteLength() 返回指定编码长度,默认是utf-8
4. Buffer.concat() 将一组Buffer对象合并成一个
let buf = Buffer.from('hello','base64') console.log(buf) //<Buffer 85 e9 65> //静态方法 console.log(Buffer.isEncoding('utf8')) //true console.log(Buffer.isBuffer(buf)) //true console.log(Buffer.byteLength(buf)) //5 utf8中 let buf = Buffer.from('hello') let buf1 = Buffer.from('hello1') console.log(Buffer.concat([buf,buf1])); //<Buffer 68 65 6c 6c 6f 68 65 6c 6c 6f 31>
实例方法:
1. write() 向buffer对象中写入内容
2. slice() 截取新的buffer对象
3. toString() 转成字符串
4. toJson() 把buffer转成json的字符串,不需要显式调用,当JSON.stringify方法调用时会自动调用toJSON方法
let buf = Buffer.alloc(5) buf.write('hello',2,2) //从索引为2开始写入,只写入2个 let buf = Buffer.from('hello') let buf1 = buf.slice() //返回的是一个新的buffer对象 let buf1 = buf.slice(2,3) //从第二个开始到第三个,不包括三
路径操作
const path = require('path') //获取路径的最后一部分 console.log(path.basename('/foo/bar/baz/asdf/quux.html')) //quux.html console.log(path.basename('/foo/bar/baz/asdf/quux.html'),'.html') //quux //获取路径 console.log(__dirname) //得到当前路径 console.log(path.dirname('/abc/qqq/abc.txt')) // abc/qqq //获取文件扩展名 console.log(path.extname('a.html')) //.html console.log(path.extname('.'))// . console.log(path.extname('a.')) // '' //路径格式化处理 path.format() //对象转字符串 path.parse() //字符串转对象 let obj = path.parse(__filename) console.log(obj) /*{ root: 'c:\\', 文件根路径 dir: 'c:\\Users\\Administrator\\Desktop\\学习\\html\\笔记md', 文件路径 base: 'test.js', 文件名字 ext: '.js', 文件扩展名 name: 'test' } 文件名称 */ let objpath = { root: 'c:\\', dir: 'c:\\Users\\Administrator\\Desktop\\学习\\html\\笔记md', base: 'test.js', ext: '.js', name: 'test' } console.log(path.format(objpath)) //拼接路径 console.log(path.jon('/foo','bar','baz/asdf','quux','..')) //两个点表示上层路径,一个点表示当前路径,在连接路径的时候会格式化处理 //规范化路径 console.log(path.normalize('C:\\temp\\\\foo\\bar\\..\\'))// C:\temp\foo\ //计算相对路径 console.log(path.relative('/data/a/b','/data/c/d')) // ../../c/d //两个特殊属性 console.log(path.delimiter) // 表示路径分隔符(window是\ Linux是/) console.log(path.sep) // 环境变量分隔符(window是; Linux是:)
异步I/O input/output
1. 文件操作 文件I/O
2. 网络操作 网络I/O
在浏览器中也存在异步操作:
1. 定时任务
2. 事件处理
3. ajax回调
JS的运行是单线程,引入了事件队列机制
Node.js中的事件模型和浏览器中的事件模型类似 单线程+事件队列,基于回调函数的编码风格
文件信息获取
const fs = require('fs') fs.stat('./data.txt',(err,stat)=>{ //一般回调函数的第一个参数是错误对象,如果err为空表示没有错误,否则报错了 if(err) return if(stat.isFile()){ console.log('这是一个文件') }else if(stat.isDirectory){ console.log('这是一个目录') } console.log(stat) /* atime 访问时间 ctime 文件状态信息修改时间 (比如文件权限) mtime 文件数据修改时间 birthtime 文件创建时间 */ }) //同步操作 console.log(1) let ret =fs.statSync('./data.txt') console.log(ret) console.log(2)
读文件操作
const fs = require('fs') const path = require('path') let strpath = path.join(__dirname,'data.txt') //没有第二个参数的到的是Buffer对象 fs.readFile(strpath,(err,'utf8',data)=>{ if(err) return console.log(data) //文件里内容 }) //同步操作 let ret = fs.readFileSync(strpath,'utf8') console.log(ret)
写文件操作
const fs = require('fs') const path = require('path') let strpath = path.join(__dirname,'data.txt') fs.writeFile(strpath,'hello world','utf8',(err)=>{ if(err) return console.log('文件写入成功') }) let buf = Buffer.from('hi world') fs.writeFile(strpath,buf,'utf8',(err)=>{ if(err) return console.log('文件写入成功') }) //同步操作 fs.writeFileSync(strpath,'Tom and Jerry')
大文件操作(流式操作)
const path =require('path') const fs = require('fs') let spath = path.join(__dirname,'../03-source','file.zip') let dpath = path.join('C:\\Users\\Administrator\\Desktop','file.zip') let readStream = fs.createReadStream(spath) let writeStream = fs.createWriteStream(dpath) //基于事件的处理方式 let num = 1 readStream.on('data',(chunk)=>{ //数据读取一部分触发 //读取文件的时候发生 num++ writeStream.write(chunk) }) readStream.on('end',()=>{ console.log('文件处理完成'+num+'次完成') //文件处理完成 2454次完成 }) //pipe直接把输入流和输出流合在一起 readStream.pipe(writeStream) //直接完成读写 fs.createReadStream(spath).pipe(fs.createWriteStream(dpath))
目录操作
//创建目录 const fs = require('fs') const path = require('path') fs.mkdir(path.join(__dirname,'abc'),(err)=>{ if(err) return }) //同步 fs.mkdirSync(path.join(__dirname,'hello')) //读取目录 fs.readdir(__dirname,(err,files)=>{ console.log(files) //一个数组 files.forEach((item)=>{ fs.stat(path.join(__dirname,item),(err,stat)=>{ if(stat.isFile()){ console.log(item+'是文件') }else if(stat.isDirector()){ console.log(item+'是目录') } }) }) }) //同步方法 let files = fs.readdirSync(__dirname) files.forEach((item)=>{ fs.stat(path.join(__dirname,item),(err,stat)=>{ if(stat.isFile()){ console.log(item+'是文件') }else if(stat.isDirector()){ console.log(item+'是目录') } }) }) //删除目录 fs.rmdir(path.join(__dirname,'abc'),(err)=>{ }) //同步 fs.rmdirSync(path.join(__dirname,'abc'))
NPM
1. 全局安装 -g
全局安装的包位于Node.js环境的node_modules目录下,全局安装的包一般用于命令行工具
2. 本地安装
本地安装的包在当前目录下的node_modules里面,本地安装的包一般用于实际开发工作
### 常用命令
1. 安装包
1. npm install -g 包名称
2. npm install 包名称
2. 安装的时候可以指定版本
1. npm install -g 包名称@版本号
3. 卸载
1. install换成uninstall
4. 更新(更新到最新版本)
1. npm update -g
5. 两个选项
生产环境(项目部署上线之后的服务器环境)
开发环境(平时开发使用的环境)
1. --save 向生产环境添加依赖 dependencies
2. --save-dev 向开发环境添加依赖 devdependencies
yarn
安装yarn:npm install -g yarn
1. 初始化包
1. npm init
2. yarn init
2. 安装包
1. npm install xxx --save
2. yarn add xxx
3. 移除包
1. npm uninstall xxx
2. yarn remove xxx
4. 更新包
1. npm update xxx
2. yarn upgrade xxx
5. 安装开发依赖包
1. npm install xxx --save-dev
2. yarn add xxx --dev
6. 全局安装
1. npm install -g xxx
2. yarn global add xxx
7. 设置包镜像
1. npm config set registry url
2. yarn config set registry url
8. 安装所有依赖
1. npm install
2. yarn install
9. 执行包
1. npm run
2. yarn run
Node.js静态网页开发
初步实现服务器功能
const http = require('http') let server = http.createServe() //创建服务器实例对象 server.on('request',(req,res)=>{ //绑定实例事件 res.end('hello') }) server.listen(3000) //监听一个端口 //----------------- http.createServer((req,res)=>{ //简单做法 res.end('ok') }).listen(3000,'IP',()=>{ console.log('running') //URL为IP:3000 })
处理请求路径的分发
1. req对象是Class:http.IncomingMessage的实例对象
2. res对象是Class:http.ServerResponse的实例对象
const http = require('http') http.createServer((req,res)=>{ //简单做法 if(req.url.startsWith('/index')){ //req.url可以获取端口之后的路径 res.write('') //可以多次 res.end('index') //只能一次 }else if(req.url.startsWith('/about')){ res.end('about') }else{ res.end('no content') } }).listen(3000,'IP',()=>{ console.log('running') //URL为IP:3000 })
初步实现静态资源和优化
const http = require('http') const path = require('path') const fs = require('fs') http.createServer((req,res)=>{ //处理路径的分发操作 fs.readFile(path.join(__dirname,req.url),(err,fileContent)=>{ if(err){ res.writeHead(404,{ 'Content-Type':'text/plain; charset=utf8' res.end('页面被狗狗叼走了') }) }else{ res.end(fileContent) } }) }).listen(3000,'IP',()=>{ console.log('running') //URL为IP:3000 }) ~~~ ### 封装 ~~~js const path = require('path') const fs = require('fs') exports.staticServer = (req,res,root)=>{ fs.readFile(path.join(root,req.url),(err,fileContent)=>{ if(err){ res.writeHead(404,{ 'Content-Type':'text/plain; charset=utf8' res.end('页面被狗狗叼走了') }) }else{ res.end(fileContent) } }) } const http = require('http') const path = require('path') const ss = require('./xxx.js') http.createServer((req,res)=>{ //处理路径的分发操作 ss.staticServer(req,res,path.join(__dirname)) }).listen(3000,'IP',()=>{ console.log('running') //URL为IP:3000 })
参数传递获取get/post
1. get参数处理
const url = require('url') let str = 'http://www.baidu.com/abc?flag=123&keyword=javascript' let result = url.parse(str,true) //query会变成一个对象 console.log(result) /* Url { protocol: 'http:', slashes: true, auth: null, host: 'www.baidu.com', port: null, hostname: 'www.baidu.com', hash: null, search: '?flag=123&keyword=javascript', query: 'flag=123&keyword=javascript', pathname: '/abc', path: '/abc?flag=123&keyword=javascript', href: 'http://www.baidu.com/abc?flag=123&keyword=javascript' }*/ ~~~ 2. post参数处理 ~~~js const querystring = require('querystring') let param = 'username=Tom&password=123' //把字符串转成对象,重复键的值会放在一个数组里 let obj = querystring.parse(param) let obj1 = { flag:'1', abc:'hello' } let str1 = querystring.stringify(obj1) //与上面的方法相反 //------------------ const querystring = require('querystring') const http = require('http') http.createServer((req,res)=>{ if(req.url.startsWith('/login')){ let pdata='' req.on('data',(chunk)=>{ pdata +=chunk }) req.on('end',()=>{ console.log(pdata) let obj = querystring.parse(pdata) res.end(obj.username,obj.password) }) } }).listen(3000,()=>{ console.log('running...') })
总结Node.js的Web开发相关内容
1. Node.js不需要依赖第三方应用软件(Apache),可以基于API自己实现
2. 实现静态资源服务器
3. 路由处理
4. 动态网站
5. 模板引擎
6. get和post参数传参和处理
Web开发框架:express
const express = require('express') const app = express() app.get('/',(req,res)=>{ res.send('OK') }).listen(3000,()=>{ console.log('running') })
托管静态文件
const express = require('express') const app = express() //实现静态资源服务 //use方法第一个参数可以指定一个虚拟路径,可以指定多个多个目录作为静态资源目录 let server = app.use('/abc',express.static('public')) //localhost后要加上/abc server.listen(3000,()=>{ console.log('running') }) //--------------------------- app.use('/abc',express.static('public')) app.listen(3000,()=>{ console.log('running') })
路由(根据请求路径和请求方式进行路径分发处理)
http常用请求方式
1. post 添加
2. get 查询
3. put 更新
4. delete 删除
restful api 一种URL格式
const express = require('express') const app = express() //基本的路由处理 app.use((req,res)=>{ res.send('ok') }) app.listen(3000,()=>{ console.log('running') })
中间件
中间件:处理过程的一个环节,他是一个函数,可以访问请求对象,响应对象,和web应用中处于请求-响应循环流程的中间件,一般被命名为next的变量
const express = require('express') const app = express() let total = 0 app.ues('/user',(req,res,next)=>{ console.log(Date.now()) //记录访问时间 next() //作用是把请求传递到下一个中间件 }) app.ues('/user',(req,res,next)=>{ console.log('访问了/user') //记录访问日志 next() }) app.ues('/user',(req,res)=>{ total++ console.log(total) res.send('result') }) app.listen(3000,()=>{ console.log('running') })
中间件挂载方式
1. use
2. 路由方式 get put post delete
const express = require('express') const app = express() app.get('/abc',(req,res)=>{ next('route') //跳转到下一个路由 },(req,res)=>{ res.send('abc') }) app.get('/abc',(req,res)=>{ }) //跳来这里 app.listen(3000,()=>{ console.log('running') })
应用中间件
const express = require('express') const app = express() const bodyParser = require('body-parser') app.use(express.static('public')) app.use(bodyParser.urlencoded({extended:false})) //挂载参数处理中间件post提交 //get app.get('/login',(req,res)=>{ let data = req.query console.log(data) res.send('get data') }) //post app.post('/login',(req,res)=>{ let data = req.body console.log(data) res.send('ok') }) app.listen(3000,()=>{ console.log('running') })
模板引擎
//npm install --save art-template //npm install --save express-art-template const express =require('express') const path =require('path') const template =require('art-template') const app =express() app.set('views',path.join(__dirname,'views')) //设置模板的路径 app.set('view engine','art') //设置模板引擎 app.engine('art',require('express-art-template')) //使express兼容art-template模板引擎 app.get('/list',(req,res)=>{ let data = { title:'水果', list:['apple','orange','banana'] } res.render('list',data) //模板名称,渲染模板的数据 }) app.listen(3000,()=>{ console.log('running') })