一、基础
-
安装
- nvm、n:切换版本
curl -ohttps://raw.githubusercontent.com/nvmsh/nvm/v0.35.3/install.sh | bash原理:切换时改变命令的指向
- npm:包管理⼯具
npm i -g create-react-app create-react-app some-repo- npx:npm@5 之后新增的⼀个命令,它使得我们可以在不安装模块到当前环境的前提下,使⽤⼀些 cli 功能
npx create-react-app some-repo -
底层依赖
- V8 引擎: 主要是 JS 语法的解析,有了它才能识别 JS语法
- libuv: c 语⾔实现的⼀个⾼性能异步⾮阻塞 IO 库,⽤来实现 node.js 的事件循环
- http-parser/llhttp: 底层处理 http 请求,处理报⽂,解析请求包等内容
- openssl: 处理加密算法,各种框架运⽤⼴泛
- zlib: 处理压缩等内容
-
内置模块
- fs: ⽂件系统,能够读取写⼊当前安装系统环境中硬盘的数据
const fs = require('fs'); const path = require('path'); const pathTo = path.resolve(__dirname, './demo.js') // 异步读取 fs.readFile(pathTo, 'utf8', function(err, data) { if(err){ console.log(err, 'err') return err; } console.log(data, 'data') }) // 同步读取 const content = fs.readFileSync(pathTo, 'utf8') console.log(content, 'content') // 封装promisefy function promisefy(func){ return function(...args){ return new Promise(()=>{ args.push(function(err, result){ if(err) return reject(err) return resolve(result) }) return func.apply(this, args) }) } } const readFileAsync = promisefy(fs.readFile); readFileAsync(pathTo, 'utf-8') .then(content=>{ console.log(content) }) .catch(e=>{ console.log(e, 'e') })- path: 路径系统,能够处理路径之间的问题
const path = require('path') // 把参数变成当前系统合法的文件路径 const re = path.resolve('a','..','b','c/') // 返回绝对路径 const jo = path.join('a','..','b','c/') // 直接拼接命令- crypto: 加密相关模块,能够以标准的加密⽅式对我们的内容进⾏加解密
- dns: 处理 dns 相关内容,例如我们可以设置 dns 服务器等等
- http: 设置⼀个 http 服务器,发送 http 请求,监听响应等等
const http = require('http'); const proxy = http.createServer((req, res)=>{ // 写入请求头 res.writeHead(200, {'name' : 'hi-wo'}) // 写入页面内容 res.end('hi') }) proxy.listen(8888, '127.0.0.1',()=>{ console.log('server') })- readline: 读取 stdin 的⼀⾏内容,可以读取、增加、删除我们命令⾏中的内容
- os: 操作系统层⾯的⼀些 api,例如告诉你当前系统类型及⼀些参数
- vm: ⼀个专⻔处理沙箱的虚拟机模块,底层主要来调⽤ v8 相关 api 进⾏代码解析 **
const vm = require('vm'); const path = require('path'); const fs = require('fs'); function r(filename){ const pathTo = path.resolve(__dirname,filename); const content = fs.readFileSync(pathTo, 'utf-8') const wrapper = [ '(function(require, module, exports) {', '})' ] const wrapperCon = wrapper[0] + content + wrapper[1] const script = new vm.Script(wrapperCon, { filename: 'index.js' }) const module ={ exports: {} } const result = script.runInThisContext() result(r, module, module.exports) return module.exports } global.r = r -
拓展
- quickjs quickjs 是⼀个 JS 的解析引擎,轻量代码量也不⼤,与之功能类似的就是 V8 引擎。
- deno deno 是⼀类类似于 node.js 的 JS 运⾏时环境,同时他也是由 node.js 之⽗⼀⼿打造出来的。 deno 也是基于 V8 ,上层封装⼀些系统级别的调⽤我们的 deno 应⽤也可以使⽤ JS 开发。 deno 基于 rust 和 typescript 开发⼀些上层模块,所以我们可以直接在 deno 应⽤中书写 ts。 deno ⽀持从 url 加载模块,同时⽀持 top level await 等特性
二、原理
-
全局对象
- __filename:表示当前正在执⾏的脚本的⽂件名。它将输出⽂件所在位置的绝对路径。
- __dirname:dirname 表示当前执⾏脚本所在的⽬录。
- setTimeout(cb, ms)、clearTimeout、setInterval、clearInterval、console
- process:Process 是⼀个全局变量,即 global 对象的属性。它⽤于描述当前Node.js 进程状态的对象。
- require:实现模块的加载
- module、exports:处理模块的导出
-
核心模块(其他全局对象)
- Buffer:该类用来创建一个专门存放二进制数据的缓存区,为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据。
// Buffer 类是全局的对象,可以直接使⽤,官⽅推荐显式引⽤ import { Buffer } from 'buffer'; // new Buffer() 建议弃⽤的 // Buffer.from() 从字符串或者数组创建一个buffer Buffer.from(array) Buffer.from(arrayBuffer, byteOffset, length) Buffer.from(buffer) Buffer.from(object, offsetOrEncoding, length) Buffer.from(string, encoding) // Buffer.alloc 创建一个指定大小的buffer //fill:默认0, encoding默认值: 'utf8'。 Buffer.alloc(size ,fill, encoding) // 创建⻓度为 10 的以零填充的缓冲区。 const buf1 = Buffer.alloc(10); // 创建⻓度为 10 的缓冲区, // 使⽤值为 `1` 的字节填充。 const buf2 = Buffer.alloc(10, 1); // 没有做处理,有内存泄露的风险,但速度更快 const buf3 = Buffer.allocUnsafe(10); // Buffer 的应⽤场景1 const fs = require('fs'); const path = require('path'); // function to encode file data to base64 encoded string function base64_encode(file) { let bitmap = fs.readFileSync(file); return Buffer.from(bitmap).toString('base64'); } // function to create file from base64 encoded string function base64_decode(base64str, file) { let bitmap = Buffer.from(base64str, 'base64'); fs.writeFileSync(file, bitmap); console.log('******** File created from base64 encoded string********'); } let base64str = base64_encode(path.join(__dirname, './play.png')); console.log(base64str); base64_decode(base64str, path.join(__dirname, 'copy.png')); // Buffer 的应⽤场景2 const fs = require('fs'); const path = require('path'); const rs = fs.createReadStream(path.join(__dirname, '1.txt')); let data = ''; rs.on('data', function (chunk) { data += chunk; }); rs.on('end', function () { console.log(data); }); // 乱码 // 解决⽅式1 // 可读流有⼀个设置编码的⽅法setEncoding(): 默认情况下没有设置字符编码,流数据返回的是 Buffer 对象。 如果设置了字符编码,则流数据返回指定编码的字符串 let rs = fs.createReadStream('test.md', {highWaterMark: 11}); rs.setEncoding('utf8'); // 解决⽅式2 // 将⼩Buffer 拼接成⼤Buffer 对象 let chunks = []; let size = 0; res.on('data', function (chunk) { chunks.push(chunk); size += chunk.length; }); res.on('end', function () { let buf = Buffer.concat(chunks, size); let str = iconv.decode(buf, 'utf8'); console.log(str); });- Stream:Stream 是一个抽象接口,使⽤Stream可实现数据的流式处理。
// Readable - 可读操作。 Writable - 可写操作。 Duplex - 可读可写操作. Transform - 操作被写⼊数据,然后读出结果。 // Duplex 与 Transform 的异同:Duplex可读可写独立,Transform关联,对可读流做了一些操作,输出一些可写流 // Stream 的优势:形成管道,方便处理文件 // src/pipe.js var fs = require("fs"); var zlib = require('zlib'); // 压缩 input.txt ⽂件为 input.txt.gz fs.createReadStream('input.txt') .pipe(zlib.createGzip()) .pipe(fs.createWriteStream('input.txt.gz')); console.log("⽂件压缩完成。"); // pipe实现原理 Readable.prototype.pipe = function(dest, options) { const src = this; src.on('data', ondata); function ondata(chunk) { const ret = dest.write(chunk); if (ret === false) { // ... src.pause(); } } // ... // 保持链式调⽤ return dest; };- Events:Events模块是node的核⼼模块之⼀,⼏乎所有常⽤的node模块都继承了events模块。
// 常⽤API emitter.on(eventName, listener) emitter.once(eventName, listener) emitter.off(eventName, listener) emitter.emit(eventName[, ...args]) /* emitter.addListener(eventName, listener) emitter.removeListener(eventName, listener) */ //简单实现上述⽅法 class MyEventsEmitter{ constructor(){ this.events = {}; } // 绑定事件 on(event, cbFn){ if(!this.events[event]){ this.events[event] = []; } this.events[event].push(cbFn); return this; } // 解绑事件 off(event, cbFn){ if (this._events[type]) { // 过滤掉该事件 this._events[type] = this._events[type].filter(fn => { return fn !== cbFn }); } } // 只响应⼀次 once(event, cbFn){ const onlyonce = (...args)=> { this.off(event, cbFn); cbFn.apply(this, args); } this.on(event, onlyonce); return this; } // 触发监听事件 emit(event, ...args){ if (this.events[event]) { this.events[event].forEach(fn => fn.call(this, ...args)); } } }
三、事件循环
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ nextTickQueue
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ nextTickQueue
│ ┌─────────────┴─────────────┐
| | idle, prepare │
| └─────────────┬─────────────┘
nextTickQueue nextTickQueue
| ┌─────────────┴─────────────┐
| │ poll │
│ └─────────────┬─────────────┘
│ nextTickQueue
│ ┌─────────────┴─────────────┐
│ │ check │
│ └─────────────┬─────────────┘
│ nextTickQueue
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
-
各阶段
- timers:此阶段执行由 setTimeout 和 setInterval 设置的回调。
- pending callbacks:执行推迟到下一个循环迭代的 I/O 回调。
- idle, prepare, :仅在内部使用。
- poll:取出新完成的 I/O 事件;执行与 I/O 相关的回调(除了关闭回调,计时器调度的回调和 setImmediate 之外,几乎所有这些回调) 适当时,node 将在此处阻塞。
- check:在这里调用 setImmediate 回调。
- close callbacks:一些关闭回调,例如 socket.on('close', ...)。
-
setImmediate 和 setTimeout 的区别
- setImmediate 设计为在当前轮询 poll 阶段完成后执行脚本
- setTimeout 计划在以毫秒为单位的最小阈值过去之后运行脚本
- 在主模块中执行,两者的执行顺序是不固定的(在主代码部分执行 setTimeout 设置定时器不一定写入队列,而setImmediate 会写入 check 队列)
setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); });- 在同一个I/O回调里执行,setImmediate总是先执行
const fs = require('fs'); fs.readFile(__filename, () => { setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); }); }); -
process.nextTick 和 setImmediate 的区别
- process.nextTick 在同一阶段立即触发
- setImmediate fires on the following iteration or 'tick' of the event loop (在事件循环接下来的阶段迭代中执行 - check 阶段)
-
Microtasks 微任务 微任务是来自以下对象的回调:
- process.nextTick()
- then() 优先级 process.nextTick > promise.then
-
输出顺序
async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') } console.log('script start') setTimeout(function () { console.log('setTimeout0') setTimeout(function () { console.log('setTimeout1'); }, 0); setImmediate(() => console.log('setImmediate')); }, 0) process.nextTick(() => console.log('nextTick')); async1(); new Promise(function (resolve) { console.log('promise1') resolve(); console.log('promise2') }).then(function () { console.log('promise3') }) console.log('script end') // script start // async1 start // async2 // promise1 // promise2 // script end // nextTick // async1 end // promise3 // setTimeout0 // setImmediate // setTimeout1
四、网络
-
创建TCP 创建tcp服务端
const net = require('net'); const HOST = '127.0.0.1'; const PORT = 7777; // 创建一个TCP服务器实例,调用listen函数开始监听指定端口 // net.createServer()有一个参数, 是监听连接建立的回调 net.createServer((socket) => { const remoteName = `${socket.remoteAddress}:${socket.remotePort}`; // 建立成功了一个连接, 这个回调函数里返回一个socket对象. console.log(`${remoteName} 连接到本服务器`); // 接收消息 socket.on('data', (data) => { console.log(`${remoteName} - ${data}`) // 给客户端发消息 socket.write(`你刚才说啥?是${data}吗?`); }); // 关闭 socket.on('close', (data) => { console.log(`${remoteName} 连接关闭`) }); }).listen(PORT, HOST); console.log(`Server listening on ${HOST}:${PORT}`);创建tcp客户端
const net = require('net'); const HOST = '127.0.0.1'; const PORT = 7777; const client = new net.Socket(); const ServerName = `${HOST}:${PORT}`; let count = 0; client.connect(PORT, HOST, () => { console.log(`成功连接到 ${ServerName}`); // 向服务端发送数据 const timer = setInterval(() => { if (count > 10) { client.write('我没事了, 告辞'); clearInterval(timer); return; } client.write('马冬梅' + count++); }, 1000) }); // 接收消息 client.on('data', (data) => { console.log(`${ServerName} - ${data}`); // 关闭连接 // client.destroy(); }); // 关闭事件 client.on('close', () => { console.log('Connection closed'); }); client.on('error', (error) => { console.log(error); }) -
创建UDP 创建udp服务端
const dgram = require('dgram'); const server = dgram.createSocket('udp4'); server.on('message', (msg, remote) => { console.log(`${remote.address}:${remote.port} - ${msg}`) server.send(`收到!`, remote.port, remote.address); }) server.on('listening', () => { const address = server.address() console.log(`Server listening on ${address.address}:${address.port}`); }) server.bind(44444);创建udp客户端
const dgram = require('dgram') const message = Buffer.alloc(5, 'lubai') const client = dgram.createSocket('udp4') client.send(message, 0, message.length, 44444, 'localhost', (err, bytes) => { console.log(`发送成功${bytes}字节`); // client.close() } ) client.on('message', (buffer) => { console.log(buffer.toString()) }) -
创建HTTP http-server.js
const http = require('http') http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello World') }).listen(80, '127.0.0.1') console.log('Server running at http://127.0.0.1:80/')请求报文
// 三次握手 * Rebuilt URL to: http://127.0.0.1:80/ * Trying 127.0.0.1... * TCP_NODELAY set * Connected to 127.0.0.1 (127.0.0.1) port 80 (#0) // 客户端向服务端发送请求报文 > GET / HTTP/1.1 > Host: 127.0.0.1:80 > User-Agent: curl/7.54.0 > Accept: */* > // 服务端响应客户端内容 < HTTP/1.1 200 OK < Content-Type: text/plain < Date: Wed, 04 Aug 2021 15:55:55 GMT < Connection: keep-alive < Keep-Alive: timeout=5 < Transfer-Encoding: chunked < * Connection #0 to host 127.0.0.1 left intact Hello World%htttp-client.js
const http = require('http') const options = { hostname: '127.0.0.1', port: 80, path: '/', method: 'GET' } const req = http.request(options, (res) => { console.log(`Status=${res.statusCode}, Headers=${JSON.stringify(res.headers)}`); res.setEncoding('utf8') res.on('data', (data) => { console.log(data) }) }) req.end()
五、框架
- koa express 区别
- express回调地狱,koa1使用generator函数解决,koa2使用await函数解决
- http 模块实现简单web服务
const http = require('http'); const fs = require('fs'); http.createServer((request, response)=>{ fs.readFile(__dirname + '/index.html', 'binary', (error, file)=>{ if(error){ console.log(error); } else { response.writeHead(200, {'Content-Type': 'text/html'}); response.end(file, 'binary'); } }) }).listen(9000); console.log('http server start') - koa2 框架实现web 服务
const Koa = require('koa') const app = new Koa() app.use((ctx, next) => { ctx.response.type = 'html'; ctx.response.body = '<p>Koa</p>' }); app.listen(9000) console.log('Koa server start') - koa2 模板中间件 koa-art-template
const artTemplate = require('art-template'); artTemplate.default.import.toLocaleLowerCase = (str) => { return str.toLocaleLowerCase(); } render(app, { root: path.join(__dirname, 'views'), extname: '.art', debug: process.env.NODE_ENV !== 'production' }) router.get('/', async (ctx) => { let data = { title: 'title', context: 'content', } await ctx.render('index', data); }) - koa2的源码解析 application.js context.js request.js response.js
- application.js
class Application extends Emitter { constructor(options) { super(); // ... this.middleware = []; // 中间件队列 // ... } // ⼊⼝⽅法 listen(...args) { debug('listen'); const server = http.createServer(this.callback()); return server.listen(...args); } // 添加中间件⽅法 use(fn) { // ... this.middleware.push(fn); return this; } callback(){ const fn = compose(this.middleware); // 换个名字 handleRequest => handleRequestFn const handleRequestFn = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequestFn; } // 错误处理 handleRequest(ctx, fnMiddleware) { // ... return fnMiddleware(ctx).then(handleResponse).catch(onerror); } // 重新封装实例对象,⽅便了多个地⽅同时使⽤ createContext(req, res) { // ... return context; } }- context.js 主要是为ctx 增加相关的属性和⽅法
- request.js 主要是定义request 对象上的属性和⽅法
- response.js 主要是定义response 对象上的属性和⽅法
- compose 返回的⽅法
return function (context, next) { // last called middleware # let index = -1 return dispatch(0) function dispatch (i) { if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { // 执⾏当前中间件⽅法,参数为 context 和 下⼀个中间件⽅法。 // 这就是我们为什么要在中间件中去执⾏ next() ⽅法的原因,不执⾏next,下⼀个中间件就⽆法执⾏。 return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } - 中间件
- 中间件函数
async function mid1(ctx, next) { ... await next(); // next() <==> dispatch(1); //即下⼀个中间件 <==> mid2(); } async function mid2(ctx, next) { ... await next(); // next() <==> dispatch(2) <==> mid3(); } async function mid3(ctx, next) { ... await next(); // next() <==> dispatch(3) <==> 达到条件 i等于middleware⻓度,返回Promise.resolve(); } app.use(mid1).use(mid2).use(mid3); // ==> middleware = ['mid1', 'mid2', 'mid3']- 常⻅中间件
- koa-bodyparser
- koa-router
- koa-art-template
- koa-views + ejs
- koa-static
- koa-logger 控制台输出请求⽇志的⽅法
- koa-log4 koa-log4在log4js-node的基础上做了⼀次包装,是⽀持koa2的⼀个处理⽇志的中间件
- koa-session
- 开发中间件
module.exports = async function (ctx, next) { ctx.headers.production = 'myProduction'; await next(); }; - sequelize
- 我们选择Node的ORM框架Sequelize来操作数据库。这样,我们读写的都是JavaScript对象,Sequelize帮我们把对象变成数据库中的行。
- 创建
var Sequelize = require('sequelize'); // 数据库配置⽂件 var sqlConfig = { host: "127.0.0.1", user: "root", password: "qaz123456", database: "test" }; var sequelize = new Sequelize(sqlConfig.database, sqlConfig.user,sqlConfig.password, { host: sqlConfig.host, dialect: 'mysql', pool: { max: 10, min: 0, idle: 10000 } }); var User = sequelize.define('user', { id: { type: Sequelize.BIGINT, primaryKey: true }, username: Sequelize.STRING(100), birthday: Sequelize.STRING(100), }, { freezeTableName: true, // 默认false修改表名为复数,true不修改表名,与数据库表名同步;否则会使⽤加了s 的复数表名 tableName: 'user', // 表名 timestamps: false // 是否⾃动添加时间戳createAt,updateAt });- 使用
// 增 User.create({ username: 'Gaffey', birthday: '2007.07.07', }).then(function (p) { console.log('created.' + JSON.stringify(p)); }).catch(function (err) { console.log('failed: ' + err); }); // 查 (async () => { var users = await User.findAll({ where: { username: 'Gaffey' } }); console.log(`find ${users.length} user:`); for (let user of users) { console.log(JSON.stringify(user)); } })(); // 改 User.update({ username: 'lilin' }, { where: { 'id': 1 } }) // 删 User.destroy({ 'where': { 'id': 1 } })
六、实战
- npm init
- yarn add superagent cheerio --registry=registry.npm.taobao.org
- 配置
"scripts": {
"start": "node index.js"
},
- 获取百度html
const superagent = require('superagent');
const cheerio = require('cheerio');
superagent.get('http://www.baidu.com').end((err, res) => {
if(err){
console.log('访问失败', err);
}else{
console.log(res.text)
}
})
- 解析html
const superagent = require('superagent');
const cheerio = require('cheerio');
superagent.get('http://www.baidu.com').end((err, res) => {
if(err){
console.log('访问失败', err);
}else{
const htmlText = res.text;
const $ = cheerio.load(htmlText)
$('meta').each((index, ele)=>{
console.log('index', index);
console.log('element', $(ele).attr('content'));
})
}
})
- 获取百度图片
const superagent = require('superagent');
const cheerio = require('cheerio');
const fs = require('fs');
const path = require('path');
const word = '猫咪'
const headers = {
'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Host': 'imgstat.baidu.com',
'Referer': 'https://image.baidu.com/',
'sec-ch-ua': '"Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93"',
'sec-ch-ua-mobile':'?0',
'sec-ch-ua-platform': "macOS",
'Sec-Fetch-Dest': 'image',
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'
}
// 访问网站
superagent.get(`http://image.baidu.com/search/index?tn=baiduimage&ie=utf-8&word=${encodeURIComponent(word)}`)
.set('Accept', headers['Accept'])
.set('Accept-Encoding', headers['Accept-Encoding'])
.set('Accept-Language', headers['Accept-Language'])
.set('Connection', headers['Connection'])
.set('sec-ch-ua', headers['sec-ch-ua'])
.set('User-Agent', headers['User-Agent'])
.end((err, res) => {
if(err){
console.log('访问失败', err);
}else{
const htmlText = res.text;
const imagesUrlList = getRegexp(htmlText, 'thumbURL')
const titlesUrlList = getRegexp(htmlText, 'fromPageTitle').map(item=>
item.replace('<strong>','').replace('<//strong>','').replace('<\\/strong>','')
)
mkImageDir('img')
imagesUrlList.forEach((url,index) => {
downloadImage(url,index,titlesUrlList[index])
})
}
})
// 替换正则
const getRegexp = (str, key)=>{
const reg = new RegExp(`"${key}":"(.*?)"`,'g')
const matchResult = str.match(reg);
const result = matchResult.map(item=>{
const res = item.match(/:"(.*?)"/g);
return RegExp.$1
})
return result
}
// 创建目录
const mkImageDir = (pathname) => {
const fullPath = path.resolve(__dirname,pathname)
if(fs.existsSync(fullPath)){
console.log(pathname,'目录已经存在')
return;
}
fs.mkdirSync(fullPath);
console.log('创建目录成功')
}
// 下载图片
const downloadImage = (url, name, index) => {
const fullPath = path.join(__dirname, 'img', `${index}-${name}.png`)
if(fs.existsSync(fullPath)){
console.log(name,'文件已经存在')
return;
}
superagent.get(url).end((err, result) => {
if(err){
console.log('获取err',err)
return
}
fs.writeFile(fullPath,result.body,'binary',(err)=>{
if(err){
console.log('下载err',err)
return
}
console.log('下载成功')
})
})
}
- 优化
- 进度条:安装 cli-progress
- 已存在目录先删除, 再创建
- 通过cli来输入关键词:安装 inquirer, commander
- 自定义爬取图片的数量
- 爬取百度视频
- 分别配置视频和图片命令:安装 commander
- 通过npm link, 配置全局命令
七、其他
- 特点:
- 异步IO
- IO是应用程序的瓶颈所在
- 异步IO提高性能无采原地等待结果返回
- IO操作属于操作系统级别,平台都有对应实现
- Nodejs单线程配合事件驱动架构及libuv实现了异步IO
- 事件驱动
const EventEmitter = require('events')
const myEvent = new EventEmitter()
myEvent.on('事件1',() => { console.log('事件1执行了')})
myEvent.on('事件1', () =>{ console.log('事件1-2执行了')})
myEvent.emit('事件1')
- 主线程单线程
const http = require('http')
function sleepTime (time) {
const sleep = Date.now() + time 1000
while(Date.now()<sleep){}
return
}
sleepTime(4)
const server = http.createServer((reg,res)=>{
res.end('server starting......')
})
server.listen(8080,()=>{ console.log('服务启动了')}) // 四秒后才执行
- 实现:
// 需求:希望有一个服务,可以依据请求的接口内容返回相应的数据
import express from 'express'
const app = express()
app.get('/',(req,res)=> {
res.end('node环境')
res.json('浏览器环境')
})
app.listen(8080,()=>{
console.log('服务已经开启了')})