目录
七、使用node原生http模块实现购物车所有接口(不需要装包,不需要中间件)
八、node原生写接口,搭建静态web服务器,处理前端history路由
Node.js是单线程的,基于事件循环,非阻塞 IO的。事件循环中使用一个事件队列,在每个时间点上,系统只会处理一个事件,即使电脑有多个CPU核心,也无法同时并行的处理多个事件。因此,node.js适合处理I/O型的应用,不适合那种CPU运算密集型的应用。在I/O型的应用中,给每一个输入输出定义一个回调函数,node.js会自动将其加入到事件轮询的处理队列里,当I/O操作完成后,这个回调函数会被触发,系统会继续处理其他的请求。
一、什么是npm
npm是javascript的包管理工具,是前端模块化下的一个标志性产物。简单地地说,就是通过npm下载模块,复用已有的代码,提高工作效率。
- 1.从社区的角度:把针对某一特定问题的模块发布到npm的服务器上,供社区里的其他人下载和使用,同时自己也可以在社区里寻找特定的模块的资源,解决问题
- 2.从团队的角度:有了npm这个包管理工具,复用团队既有的代码也变的更加地方便
新建一个项目,cd进去,然后执行npm init
来初始化项目的配置。
在执行npm init
之前,有两点需要我们注意一下:
- 包名不能重复
- npm对包名的限制:不能有大写字母/空格/下划线
npm init 一路回车
或者:npm init -y
生成的package.json文件:
name和version组成唯一标识。每次发包时都要修改版本号。
description:描述
main:入口,别人安装你的npm包后,import时自动找到这个文件
scripts: 脚本 npm run test或者yarn test
keywords:关键字。放简介,字符串。方便别人查找。
author: 作者
license: 许可证
ISC许可证:https://baike.baidu.com/item/ISC%E8%AE%B8%E5%8F%AF%E8%AF%81/5490986?fr=aladdin
MIT许可证:https://baike.baidu.com/item/MIT%E8%AE%B8%E5%8F%AF%E8%AF%81/6671281?fr=aladdin
files:files是一个包含项目中的文件的数组。如果命名了一个文件夹,那也会包含文件夹中的文件。
{
"name": "xu-20191024",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
{
"name": "xu-20191024",
"version": "1.0.2",
"description": "1705E,项目实战",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": ["1705E","1706E"],
"author": "徐同保",
"license": "ISC"
}
检查包名是否存在:
https://www.npmjs.com/package/xu-20191025
通过keywords找:
repository:npm和git关联
"repository": {
"type": "git",
"url": "https://github.com/xutongbao"
},
homepage: 项目官网
"homepage": "https://blog.csdn.net/xutongbao",
dependencies与devDependencies的区别:
在发布npm包的时候,本身dependencies下的模块会作为依赖,一起被下载;devDependencies下面的模块就不会自动下载了;但对于项目而言,npm install 会自动下载devDependencies和dependencies下面的模块。
当别人使用我们的插件时,peerDependencies就会告诉明确告诉使用方,你需要安装该插件哪个宿主版本:
"dependencies": {
"axios": "^0.19.0"
},
"devDependencies": {
"element-ui": "^2.12.0"
},
"peerDependencies": {
"react": "^16.11.0"
},
"optionalDependencies": {
"redux": "^4.0.4"
}
参考链接:
https://blog.csdn.net/yq_oxygen/article/details/90040392
https://cloud.tencent.com/developer/ask/40773
https://nodejs.org/zh-cn/blog/npm/peer-dependencies/
optionalDependencies:
可选依赖,如果有一些依赖包即使安装失败,项目仍然能够运行或者希望npm继续运行,就可以使用optionalDependencies。另外optionalDependencies会覆盖dependencies中的同名依赖包,所以不要在两个地方都写。
bin字段:
参考链接:
https://blog.csdn.net/feng98ren/article/details/93729399
src/index.js:
请确保你的index.js文件里面最开头写上 #!/usr/bin/env node,否则文件里的脚本不会再Node环境下执行
#!/usr/bin/env node
console.log(1)
"bin": {
"myapp": "./src/index.js"
},
全局安装:
常用命令:
npm config list
npm config ls -l
安装npm包,安装到dependencies:
npm install commander --save-prod
npm install commander --save
npm install commander -S
npm install commander
npm add commander
npm i commander
安装npm包,安装到devDependencies:
npm install commander --save-dev
npm install commander -D
卸载npm包:
npm uninstall commander
npm unlink commander
npm remove commander
npm rm commander
npm un commander
npm r commander
安装到全局:
npm install create-react-app -g
从全局删除:
npm un create-react-app -g
二、命令行程序
console.log('hello world!')
命令行参数:
1)
console.log('hello world!', process.argv[2])
2)
console.log('hello world!', process.argv[1])
三、commander.js
使用.option()
方法定义commander
的选项options
短标志可以作为单个arg传递,例如-abc相当于-a -b -c。
多词组成的选项,像“--template-engine”会变成 program.templateEngine 等
<>代表必填,[]代表选填,选填可以设置默认值
.version('0.0.1') 使用node app -V查版本
.version('0.0.1', '-v, --version') 使用node app -v或node app --version查版本
使用node app -h或node app --help查看帮助
program.parse方法用于解析process.argv,解析后可以program.xxx使用
const program = require('commander');
program
.version('0.0.1') //node app -V
//.version('0.0.1', '-v, --version') //node app -v
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza');
program.parse(process.argv);
if (program.debug) console.log(program.opts());
console.log('pizza details:');
if (program.small) console.log('- small pizza size');
if (program.pizzaType) console.log(`- ${program.pizzaType}`);
求和:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --my-a, <a>', '第一个值')
.option('-b, --my-b, <b>', '第二个值')
.parse(process.argv);
console.log(program.myA)
console.log(program.myB)
console.log(program.myA*1 + program.myB*1)
求和二:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --add', '求和')
.parse(process.argv);
if (program.add) {
let sum = 0
program.args.forEach(item => {
sum += item * 1
})
console.log(sum)
}
阶乘:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --add', '求和')
.option('-f, --factorial <num>', '阶乘')
.parse(process.argv);
const factorial = (num) => {
if (num < 0) {
return -1
} else if (num === 0 || num === 1) {
return 1
} else {
return num * factorial(num - 1)
}
}
if (program.add) {
let sum = 0
program.args.forEach(item => {
sum += item * 1
})
console.log(sum)
} else if (program.factorial) {
let result = factorial(program.factorial)
console.log(result)
}
多单词形式:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --my-add', '求和,多单词形式')
.parse(process.argv);
//驼峰
if (program.myAdd) {
let sum = 0
program.args.forEach(item => {
sum += item * 1
})
console.log(sum)
}
以--no形式开头的选项,代表后面紧跟单词的相反面:
const program = require('commander');
program
.version('0.0.1')
.option('-a, --no-add', '求和,以--no形式开头的选项,代表后面紧跟单词的相反面')
.parse(process.argv);
console.log(program)
if (program.add) {
let sum = 0
program.args.forEach(item => {
sum += item * 1
})
console.log(sum)
} else {
console.log('取反')
}
command方法,自定义命令
description方法, 命令的描述性语句
action方法,定义命令的回调函数
const program = require('commander');
program
.version('1.0.0')
.command('my-add <num>')
.option('-a, --add, <num>', '加法')
.action((num, cmd) => {
console.log(num, cmd.add)
})
program
.option('-u, --upadte', '更新')
.description('描述信息!!!')
program.parse(process.argv)
参考链接:
https://juejin.im/post/5c8be466f265da2dc849af70
https://www.npmjs.com/package/commander#commands
四、npm包管理
发布npm包:
1.先注册一个npm账号
2.在终端登录npm账号:npm login 回车输入用户名密码和邮箱
3.新建一个文件夹,cd到新创建的文件夹,使用npm init 生成package.json
4.使用npm publish上传npm包,你会收到一封邮件,在npm官网可以看到刚上传的npm包
yarn更新包:
yarn upgrade
五、node提供一个链接可以下载图片
const fs = require('fs')
const request = require('request')
const program = require('commander')
program
.option('-d, --down <url>', '下载')
.parse(process.argv)
let url = program.down
const name = url.slice(url.lastIndexOf('/') + 1)
request(url).pipe(fs.createWriteStream('./' + name));
//node app -d https://n3-q.mafengwo.net/s15/M00/16/18/CoUBGV2xnO6ALntcAB_DZLkVUnY568.png
//node app -d https://p4-q.mafengwo.net/s15/M00/B3/B1/CoUBGV2wYYmAAByNACD9lHJSPKY794.png
//node app --down https://n2-q.mafengwo.net/s15/M00/D0/E4/CoUBGV2vBYGAbzADAB1W_rqrlCM012.png
六、使用node原生http模块写接口
跨域:
所以ajax跨域请求附带自定义响应头时,被请求服务器除了添加Access-Control-Allow-Origin响应头,还得注意注意添加Access-Control-Allow-Headers响应头为对应的自定义请求头的名称,多个自定义请求头用英文状态下逗号分开。
//跨域
res.setHeader('Access-Control-Allow-Origin', '*') //可以把 * 改成 http://localhost:3000 避免xss攻击
//res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE,HEAD,OPTIONS') //放行的方法
res.setHeader('Access-Control-Allow-Headers', 'content-type') //放行的请求头
res.setHeader('Access-Control-Max-Age', 1800) //隔30分钟才发起预检请求,1800秒
url.parse:
url.parse('http://localhost:3000/api/list?id=0') :
url.parse('http://localhost:3000/api/list?id=0', true) :
204状态码:
请求收到,但返回信息为空。
请求执行成功,但是没有数据,浏览器不用刷新页面.也不用导向新的页面。常用于跨域请求。
跨域请求:
OPTIONS是一种“预检请求”,浏览器在处理跨域访问的请求时如果判断请求为复杂请求,则会先向服务器发送一条预检请求,根据服务器返回的内容浏览器判断服务器是否允许该请求访问。如果web服务器采用cors的方式支持跨域访问,在处理复杂请求时这个预检请求是不可避免的。
跨域不可避免,预检请求也不可避免,那我们能做的,就是减少预检请求,处理办法就是设置跨域的有效期Access-Control-Max-Age,这样就只会跨域预检一次了。
浏览器的同源策略,就是出于安全考虑,浏览器会限制从脚本发起的跨域HTTP请求(比如异步请求GET, POST, PUT, DELETE, OPTIONS等等),所以浏览器会向所请求的服务器发起两次请求,第一次是浏览器使用OPTIONS方法发起一个预检请求,第二次才是真正的异步请求,第一次的预检请求获知服务器是否允许该跨域请求:如果允许,才发起第二次真实的请求;如果不允许,则拦截第二次请求。
Access-Control-Max-Age用来指定本次预检请求的有效期,单位为秒,,在此期间不用发出另一条预检请求。
例如:
res.setHeader('Access-Control-Max-Age', 1800) 表示隔30分钟才发起预检请求。也就是说,发送两次请求
七、使用node原生http模块实现购物车所有接口(不需要装包,不需要中间件)
const http = require('http')
const fs = require('fs')
const url = require('url')
const { bookNavData, bookMallData, userList } = require('./data')
let bookList = []
const server = http.createServer((req, res) => {
//跨域
res.setHeader('Access-Control-Allow-Origin', '*') //可以把 * 改成 http://localhost:3000 避免xss攻击
//res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE,HEAD,OPTIONS') //放行的方法
res.setHeader('Access-Control-Allow-Headers', 'content-type') //放行的请求头
res.setHeader('Access-Control-Max-Age', 1800) //隔30分钟才发起预检请求,1800秒
let { pathname } = url.parse(req.url, true)
console.log(req.method, url.parse(req.url, true))
console.log(pathname)
if (req.url === '/') { //hello world!
res.writeHead(200, { 'Content-Type': 'text/html' })
res.write('hello world!')
res.end()
} else if (req.url === '/home') { //路由
res.writeHead(200, { 'Content-Type': 'text/html' })
const home = fs.readFileSync('./index.html') //读文件
res.end(home)
} else if (req.url === '/banner01') { //图片
//res.writeHead(200, { 'Content-Type': 'image/jpg' })
const banner01 = fs.readFileSync('./images/banner01.jpg') //读图片
res.end(banner01)
} else if (req.method == 'OPTIONS') { //跨域,处理options请求
res.writeHead(204) //204 无内容
res.end()
} else if (req.method === 'POST' && pathname === '/api/login') { //登录
let body = ''
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到body变量中
req.on('data', (chunk) => {
body += chunk
})
// 在end事件触发后,通过JSON.parse将body解析为真正的POST请求格式
req.on('end', () =>{
body = JSON.parse(body)
let { username, password } = body
let user = userList.find(item => item.username === username)
res.writeHead(200, { 'Content-Type': 'application/json' })
if (user) {
if (user.password === password) {
res.write(JSON.stringify({
code: 200,
data: {
username
},
message: '登录成功'
}))
} else {
res.write(JSON.stringify({
code: 400,
message: '密码错误'
}))
}
} else {
res.write(JSON.stringify({
code: 400,
data: body,
message: '用户不存在'
}))
}
res.end()
})
} else if (pathname === '/api/nav') { //导航
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookNavData,
message: '导航'
}))
} else if (pathname === '/api/list') { //列表
let { id } = url.parse(req.url, true).query
let list = bookMallData.find(item => item.id == id).list
list.forEach(item => {
if (bookList.findIndex(book => book.id === item.id) >= 0) {
item.is_in_my_book = true
} else {
item.is_in_my_book = false
}
})
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: list,
message: '列表'
}))
} else if (pathname === '/api/get_book_list') { //书包
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '书包'
}))
} else if (pathname === '/api/add') { //添加到书包
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
body = JSON.parse(body)
let { item } = body
bookList.push(item)
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '添加成功'
}))
})
} else if (pathname === '/api/detail') { //详情
let { id } = url.parse(req.url, true).query
let detail
bookMallData.forEach(listItem => {
listItem.list.forEach(book => {
if (book.id == id) {
detail = book
}
})
})
if (bookList.find(book => book.id === detail.id)) {
detail.is_in_my_book = true
} else {
detail.is_in_my_book = false
}
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: detail,
message: '详情'
}))
} else if (pathname === '/api/delete') { //删除
let body = ''
req.on('data', (chunk) => {
body +=chunk
console.log('chunk:', chunk)
})
req.on('end', () => {
body = JSON.parse(body)
let { ids } = body
bookList = bookList.filter(item => !ids.find(id => id === item.id))
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '删除成功'
}))
})
} else if (pathname === '/api/update') {
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
body = JSON.parse(body)
let { bookListNew } = body
bookList = bookListNew
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '更新成功'
}))
})
} else { //404
res.writeHead(404, { 'Content-Type': 'text/html' })
res.end('404')
}
})
server.listen(9999, () => {
console.log(9999)
})
八、node原生写接口,搭建静态web服务器,处理前端history路由
参考链接:
https://www.npmjs.com/package/connect
https://www.npmjs.com/package/connect-history-api-fallback
项目上线啦:
http://39.97.238.175/index/home
const http = require('http')
const url = require('url')
const path = require('path')
const fs = require('fs')
const connect = require('connect')
const history = require('connect-history-api-fallback')
const { bookNavData, bookMallData, userList } = require('./data')
let bookList = []
//使原生http模块可以使用中间件功能
const app = connect()
//处理react前端路由(BrowserRoute),vue前端路由(mode:history)
app.use(history())
//跨域,静态web服务器
app.use((req, res, next) => {
//跨域
res.setHeader('Access-Control-Allow-Origin', '*') //可以把 * 改成 http://localhost:3000 避免xss攻击
//res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE,HEAD,OPTIONS') //放行的方法
res.setHeader('Access-Control-Allow-Headers', 'content-type') //放行的请求头
res.setHeader('Access-Control-Max-Age', 1800) //隔30分钟才发起预检请求,1800秒
let { pathname } = url.parse(req.url, true)
let extName = path.extname(pathname)
console.log(pathname, extName)
if (pathname === '/') {
pathname = '/index.html'
}
if (pathname.indexOf('/api') >= 0) {
next()
} else {
fs.readFile(`./public/${pathname}`, (err, data) => {
if (err) {
res.writeHead(404, {'Content-Type': 'text/html' })
res.end('404')
} else {
if (extName === '.css') {
res.writeHead(200, {'Content-Type': 'text/css'})
}
res.end(data)
}
})
}
})
//接口
app.use((req, res) => {
let { pathname } = url.parse(req.url, true)
if (req.method == 'OPTIONS') { //跨域,处理options请求
res.writeHead(204) //204 无内容
res.end()
} else if (req.method === 'POST' && pathname === '/api/login') { //登录
let body = ''
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到body变量中
req.on('data', (chunk) => {
body += chunk
})
// 在end事件触发后,通过JSON.parse将body解析为真正的POST请求格式
req.on('end', () =>{
body = JSON.parse(body)
let { username, password } = body
let user = userList.find(item => item.username === username)
res.writeHead(200, { 'Content-Type': 'application/json' })
if (user) {
if (user.password === password) {
res.write(JSON.stringify({
code: 200,
data: {
username
},
message: '登录成功'
}))
} else {
res.write(JSON.stringify({
code: 400,
message: '密码错误'
}))
}
} else {
res.write(JSON.stringify({
code: 400,
data: body,
message: '用户不存在'
}))
}
res.end()
})
} else if (pathname === '/api/nav') { //导航
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookNavData,
message: '导航'
}))
} else if (pathname === '/api/list') { //列表
let { id } = url.parse(req.url, true).query
let list = bookMallData.find(item => item.id == id).list
list.forEach(item => {
if (bookList.findIndex(book => book.id === item.id) >= 0) {
item.is_in_my_book = true
} else {
item.is_in_my_book = false
}
})
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: list,
message: '列表'
}))
} else if (pathname === '/api/get_book_list') { //书包
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '书包'
}))
} else if (pathname === '/api/add') { //添加到书包
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
body = JSON.parse(body)
let { item } = body
bookList.push(item)
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '添加成功'
}))
})
} else if (pathname === '/api/detail') { //详情
let { id } = url.parse(req.url, true).query
let detail
bookMallData.forEach(listItem => {
listItem.list.forEach(book => {
if (book.id == id) {
detail = book
}
})
})
if (bookList.find(book => book.id === detail.id)) {
detail.is_in_my_book = true
} else {
detail.is_in_my_book = false
}
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: detail,
message: '详情'
}))
} else if (pathname === '/api/delete') { //删除
let body = ''
req.on('data', (chunk) => {
body +=chunk
console.log('chunk:', chunk)
})
req.on('end', () => {
body = JSON.parse(body)
let { ids } = body
bookList = bookList.filter(item => !ids.find(id => id === item.id))
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '删除成功'
}))
})
} else if (pathname === '/api/update') { //更新
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
body = JSON.parse(body)
let { bookListNew } = body
bookList = bookListNew
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({
code: 200,
data: bookList,
message: '更新成功'
}))
})
} else { //404
res.writeHead(404, { 'Content-Type': 'text/html' })
res.end('404')
}
})
const server = http.createServer(app)
server.listen(9998)
console.log(9998)
九、安装FileZilla服务端
下载filezilla客户端和服务端:
https://filezilla-project.org/
安装server端:
十、安装FileZilla客户端
十一、阿里云配置支持FTP
十二、colors
装包:
yarn add colors
node代码:
const color = require('colors')
console.log('hello world!'.green)
console.log('hello world!'.underline.red)
console.log('hello world!'.inverse)
console.log('hello world!'.rainbow)
效果:
十三、express脚手架
装包:
yarn global add express-generator
运行:
express --view=pug m-express-demo
cd m-express-demo
yarn
yarn start
十三、npm view指令
显示npm包的相关信息:
npm view axios
npm show axios
npm info axios
npm v axios
查询npm包的所有版本号:
npm view axios versions
查询npm包的所有版本号和所有依赖:
npm view axios versions dependencies
十四、String
let a = 'hello'
let b = 'hello'
let c = new String('hello')
console.log(a === b) //true
console.log(a === c) //false
console.log(typeof a) //string
console.log(typeof c) //object
console.log(a instanceof String) //false
console.log(c instanceof String) //true
十五、node是单线程
let start = Date.now()
console.log(start)
setTimeout(() => {
console.log(Date.now() - start) //1000左右
for (let i = 0; i < 5000000000; i++) {
}
}, 1000)
setTimeout(() => {
console.log(Date.now() - start) //大于2000,具体大多少,取决于上面for循序的次数
}, 2000)
十六、错误处理
错误堆栈:
test.js:
const a = () => {
console.log(obj.name)
}
const b = () => {
a()
}
b()
在异步函数中,堆栈信息将丢失:
const a = () => {
setTimeout(() => {
console.log(obj.name)
}, 10)
}
const b = () => {
a()
}
b()
uncaughtException捕获异常(丢失了错误发生位置的上下文):
process.on('uncaughtException', (error) => {
console.error("xu:", error)
})
const a = () => {
console.log(obj.name)
}
const b = () => {
a()
}
b()
domain模块:
当res是上下文时,可以把错误信息返回给前端!
参考链接:https://www.runoob.com/nodejs/nodejs-domain-module.html
const domain = require('domain')
const d = domain.create()
let name = 'tom'
d.on('error', (error) => {
console.log('上下文环境:', name)
console.log('domain捕获到的异常信息:', error.stack)
})
d.run(() => {
console.log(obj.name)
})
十七、process.nextTick(callback)
在事件循环的下一次循环中调用 callback 回调函数。
console.log(1)
process.nextTick(() => {
console.log(2)
})
console.log(3)
十八、根据下标打印动物
const program = require('commander')
const fs = require('fs')
const packageInfo = require('./package.json')
program.version(packageInfo.version)
.option('-i, --index <type>', "下标")
program.parse(process.argv)
console.log(program.index)
fs.readFile('./animals.txt', 'utf-8', (err, data) => {
if (err) {
return
}
let animalsArr = data.split('===============++++SEPERATOR++++====================')
console.log(animalsArr[program.index])
})
动物数据:
链接:https://pan.baidu.com/s/1C3LKzQtdVifCAap7Nr9gAQ
提取码:g1sv
十九、node通过网页读取文件目录
const program = require('commander')
const fs = require('fs')
const http = require('http')
const { exec } = require('child_process')
const path = require('path')
const packageInfo = require('./package.json')
program.version(packageInfo.version)
.option('-p, --port <port>', "set port")
program.parse(process.argv)
let PORT = program.port || 8000
const app = http.createServer((req, res) => {
let rootPath = process.cwd()
if (req.url === '/favicon.ico') {
res.end()
return
}
let myPath = path.join(rootPath, req.url)
console.log('a', myPath)
if (fs.statSync(myPath).isFile()) {
fs.readFile(myPath, 'utf8', (err, data) => {
res.end(data)
})
} else {
let list = fs.readdirSync(myPath).map(filePath => {
return `<div>
<a href="${path.join(req.url, filePath)}">${filePath}</a>
</div>`
}).join('')
let html = fs.readFileSync(__dirname + '/public/index.html', 'utf8')
html = html.replace("{{list}}", list)
res.end(html)
}
})
app.listen(PORT, () => {
//exec(`start http://localhost:${PORT}`)
})
20、重命名文件或文件夹
const fs = require('fs')
const path = require('path')
let target = process.argv[2]
let rename = process.argv[3]
let rootPath = process.cwd()
target = path.join(rootPath, target)
if (fs.existsSync(target)) {
fs.renameSync(target, path.join(rootPath, rename))
} else {
console.log('文件或文件夹不存在')
}
21、js区分对象函数和数组
const fs = require('fs')
const path = require('path')
let obj = {}
console.log(obj instanceof Object) //true
console.log(typeof obj) //object
console.log(Object.prototype.toString.call(obj)) //[object Object]
let fun = () => {}
console.log(fun instanceof Function) //true
console.log(fun instanceof Object) //true
console.log(typeof fun) //function
console.log(Object.prototype.toString.call(fun)) //[object Function]
let arr = []
console.log(arr instanceof Array) //true
console.log(arr instanceof Object) //true
console.log(typeof arr) //object
console.log(Object.prototype.toString.call(arr)) //[object Array]
22、事件触发器
const EventEmitter = require('events')
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter()
myEmitter.on('myEventName', (a, b) => {
console.log(a, b)
})
myEmitter.emit('myEventName', 1, 2)
myEmitter.emit('myEventName', 1, 2)
myEmitter.once('myOnce', () => {
console.log('只触发一次')
})
myEmitter.emit('myOnce')
myEmitter.emit('myOnce')
myEmitter.on('error', (err) => {
console.error(err)
})
myEmitter.emit('error', new Error('错误'))
23、手动封装事件
class MyEmitter {
constructor() {
this.events = {}
}
on(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName].push(callback)
} else {
this.events[eventName] = [callback]
}
}
emit(eventName, ...arg) {
let callbackArr = this.events[eventName]
callbackArr && callbackArr.forEach(item => {
if (Object.prototype.toString.call(item) === '[object Function]') {
item(...arg)
} else if (Object.prototype.toString.call(item) === '[object Object]') {
if (item.once) {
item.callback(...arg)
item.callback = () => {}
}
}
})
}
once(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName].push({
once: true,
callback
})
} else {
this.events[eventName] = [{
once: true,
callback
}]
}
}
}
const myEmitter = new MyEmitter()
module.exports = myEmitter
24、父子进程通信
app.js:
const child_process = require('child_process')
const child = child_process.fork('./compute.js')
child.send({ type: 'start' })
child.on('message', (action) => {
if (action.type === 'sum') {
console.log('子进程计算出来的结果:', action.sum)
process.exit()
}
})
process.on('exit', () => {
console.log('主进程结束')
})
console.log('运行到这里')
compute.js:
const computeSum = () => {
let sum = 0
for (let i = 0; i < 1000000; i++) {
sum += i
}
return sum
}
process.on('message', (action) => {
if (action.type === 'start') {
let sum = computeSum()
process.send({
type: 'sum',
sum
})
}
})
25、docker
docker:码头工人
doctor:医生
docker toolbox下载链接:https://github.com/docker/toolbox/releases
docker toolbox 国内下载地址:http://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/
docker官网:https://www.docker.com/
docker hub官网:https://hub.docker.com/
docker菜鸟教程:https://www.runoob.com/docker/docker-tutorial.html
安装:
下载最新版的 boot2docker.iso 放在本地缓存文件夹里,否则启动时会联网下载最新版的,切很难下载下来,会报错
可以去百度云盘下载:
链接:https://pan.baidu.com/s/1Y9bLSSvyNdyK_dYvVzKW8w
提取码:esp0
hello world:
docker run ubuntu:15.10 /bin/echo "hello world"
运行交互式的容器:
docker run -i -t ubuntu:15.10 /bin/bash
ctrl + D 或者 exit 退出
docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello; sleep 5;done"
docker ps
docker logs determined_meitner
进入容器:
docker attach determined_meitner