前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >nodejs基础和核心api

nodejs基础和核心api

作者头像
一粒小麦
发布2019-07-18 17:54:31
9680
发布2019-07-18 17:54:31
举报
文章被收录于专栏:一Li小麦一Li小麦

如何学习:

  • 看官方资料
  • 看github代码
  • 笔记不要用笔 最好就是思维导图

nodeJs常常放在前面说的套话

nodejs是一个异步的事件驱动的进行时(runtime)。

Ryan Dahl是一??深的C/C++程序?,在创??Node之前,他的?要工作都是??高性能 Web服务???的。经?过一些?试???之后,他?到了??高性能,Web服务?的几个要??点: 事件?驱动、异步???I/O。 JavaScript?C的开发门槛?要低?,比?Lua的??的历史包袱?要少?。?尽管管服务?端JavaScript ?在已经很多年了,?是后端部分?没有?市场,可以历史包袱????为?0;另外,JavaScript有广?泛的事件?驱动??的应用,暗合??Ryan Dahl喜好;同时,Chrome???的JavaScript? ?V8??引擎横空出世,?在性能方面拿下了第一。自然受??到Ryan Dahl的关注。?? 高性能、??事件?驱动、没有??历史包袱?这3?个?要?因,使得JavaScript?为了Node的实现语言。

node的特点:
  • 异步i/o??:在Node中,绝大多数的?操作都是异步的,比如读取文件,数据库等。简单说就是是 ???Don’t call me,I will call you?的??的,这也是????关心结果,不关心过程???。
  • 事件驱动,这个和前端一样。
  • 单线程:线程之间无共享状态。
  • 跨平台(Linux/Windows)
服务端和客户端的JavaScript:

核心语法都是ECMAScrit,比如数据类型,语法结构,内置对象等等。

前端关心的是浏览器的bom和dom。node关注的是操作系统(fs,net,database,buffer,event,os)

客户端JavaScript的顶层(this)是window;在node中不存在window,( console.log(this)的结果是 {}),它的顶层是全局对象(global),但是,当在node.js中定义一个a时。通过global.a却访问不了a(undefined)。

快速上手

不用说,第一个是hello world。创建一个app.js

代码语言:javascript
复制
console.log('hello word')

node app.js即可运行。

以往有个很不好的体验就是,每次修改都需要重启node服务。应用工具 nodemon可以解决这个问题。

代码语言:javascript
复制
sudo npm i nodemon -g

模块化

先看老例子:

代码语言:javascript
复制
var a=100

这个a属于当前js模块,并不是全局变量。一个文件就是一个模块。每个模块都有自己的作用域。

我们使用var 声明的一个变量,他并不就是全局的,而是属于当前模块。

你想声明一个全局变量,必须 global.a=100

每个文件都有一个独特的__filename属性:

代码语言:javascript
复制
//__filename:当前文件被解析之后的绝对路径(双下划线)
console.log(__filename); //打出了当前文件的路径

模块化导出遵循commonjs规范:

打印内存占用

代码语言:javascript
复制
// os是一个内置对象
const os =require('os') 

const mem=(os.freemem()/os.totalmem()*100).toFixed(2)
console.log(`内存占用${mem}%`)

打印cpu占用:用更精准的依赖来实现:

代码语言:javascript
复制
npm i cpu-stat -s
代码语言:javascript
复制
const cpuStat=require('cpu-stat')
cpuStat.usagePercent((err,percent)=>{
    console.log(`cpu占用${percent.toFixed(2)}%`)
})

这种回调其实是不好看的。如何更加优雅呢?现在node提供了util类

node的旧有api大都是用回调实现。

util有提供 promisify方法,提供类似promise的方法。

代码语言:javascript
复制
const cpuStat=require('cpu-stat')
const getCpu=util.promisify(cpuStat.usagePercent)
getCpu().then((percent)=>{
    console.log(`cpu占用${percent.toFixed(2)}%`)
}).catch((err)=>{
    console.log(err)
})

那么如何导出呢?

代码语言:javascript
复制
// app.js
const util=require('util')
// util.promisify
const os =require('os')
const cpuStat=require('cpu-stat')
const getCpu=util.promisify(cpuStat.usagePercent)

const showState=async ()=>{
    const mem=(os.freemem()/os.totalmem()*100).toFixed(2)
    console.log(`内存占用${mem}%`)
    const percent=await getCpu()
    console.log(`cpu占用${percent.toFixed(2)}%`)
}

module.exports ={
    showState
}

在新的js文件中使用:

代码语言:javascript
复制
const showState=require('./app').showState;
showState();

引入导出方法很 low是吧。这是原生node一直没有解决的问题。

我们可以导入babel,但是实际上效率很低。因此不推荐。

以上操作在前端工程化的实践中已经多次用到,所以没什么难的。

文件系统(fs,File System)

nodejs为操作文件提供了大量的api,它使用的是fs模块。文件操作都有两个方法,分别是同步和异步版本。

node使用流(stream)的方式来处理文件,

代码语言:javascript
复制
const fs=require('fs')
const data =fs.readFileSync('app.js')
console.log(data.toString())

异步版本(readFile)

代码语言:javascript
复制
const fs=require('fs')
const path=require('path')
const data =fs.readFileSync('app.js')

fs.readFile(path.resolve('./app.js'),(err,data)=>{
    console.log(data.toString())
})

buffer

用于处理二进制的对象,类似数组。

代码语言:javascript
复制
var bf=new Buffer();

又比如:

代码语言:javascript
复制
const buf1=Buffer.alloc(10)//分配10个字节的内存

// 转化为asc码
const buf2=Buffer.from('a')
console.log(buff2)

//把字符串转化为buffer
const buf3=Buffer.from('中国')
console.log(buf3)

// 把buffer转化为字符串
const buf4=Buffer.concat([buff2,buf3])
console.log(buf4.toString())

作简单了解即可。

stream流

大禹率领民众,与自然灾害中的洪水斗争,最终获得了胜利。面对滔滔洪水,大禹从鲧治水的失败中汲取教训,改变了"堵"的办法,对洪水进行疏导,体现出他具有带领人民战胜困难的聪明才智;大禹为了治理洪水,长年在外与民众一起奋战,置个人利益于不顾,"三过家门而不入"。大禹治水13年,耗尽心血与体力,终于完成了治水的大业。

读写一个文件,比如说把1.jpg的内容复制到2.jpg,如果图片大到几个M就很吃力了。但假设这样一个模型:我通过 createReadStream创建一个从1.jpg读取的管道,再通过 createWriteStream生成一个写入到2.jpg的管道。最后通过 pipe把这两个渠道连接起来,node在这个过程中只起到连接的作用。就实现了一个流(stram)

这跟大禹治水的思路是一样的。流的精髓在于疏导

比如说我把app.js的内容全部复制到app2.js:

代码语言:javascript
复制
const rs=fs.createReadStream('./app.js')
const ws=fs.createWriteStream('./app2.js')
rs.pipe(ws)

很快就实现了。同样的你可以操作图片:

代码语言:javascript
复制
const rs2=fs.createReadStream('./1.jpg')
const ws2=fs.createWriteStream('./2.jpg')
rs2.pipe(ws2);

http

相信接触node的人90%都是从这里开始的:

代码语言:javascript
复制
const http=require('http')
const server=http.createServer((req,res)=>{
    res.end('hello world')
})

server.listen(3000,(err)=>{
    if(!err){
        console.log('服务器已启动。。')
    }
})
res是什么

res究竟是什么?我们从原型链的角度来看。

定义一个函数:获取一个对象的原型链:

代码语言:javascript
复制
const getPrototypeChain=(obj)=>{
    const protoChain=[];
    while(obj=Object.getPrototypeOf(obj)){
        protoChain.push(obj)
    }
    protoChain.push(null);
    return protoChain;
}

上图是倒序打印出res对象的原型链。而重点在于:原型链上有 stream

完善功能

这种服务器实在是太过于简单了。不妨加点功能。

  • 访问根路由时得到页面
  • 访问 /users得到接口数据

首先,在同目录下写一个index.html

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>home</title>
</head>
<body>
    <h1>hello world</h1>
</body>
</html>

然后在路由中做判断:

代码语言:javascript
复制
const http=require('http')
const fs=require('fs')
const server=http.createServer((req,res)=>{
    // console.log('res',getPrototypeChain(res))
    const {url,method}=req
    if(url=='/'&&method=='GET'){
        fs.readFile('index.html',((err,data)=>{
            res.statusCode='200'
            res.setHeader('Content-Type','text/html')
            res.end(data)
        }))
    }else if(url=='/users'&&method=='GET'){
        res.writeHead(200,{
            'Content-Type':'application/json'
        })// 和上面的写法等效
        res.end(JSON.stringify([
            {
                username:'12325'
            }
        ]))
    }
})

server.listen(3000,(err)=>{
    if(!err){
        console.log('服务器已启动。。')
    }
})

那么功能就有了。

读取多媒体(图片,视频)

很自然的,想到图片的话,就在index.html里写一个图片标签:

代码语言:javascript
复制
<h1>hello world</h1>
<img src="2.jpg" alt="">

结果一运行就悲剧啦,图片一直处于pending状态,也不报错。

想到res是一个流。完全可以把图片导向res里面去!

在req对象中解构出headers。从 headers.accept找到图片请求。

代码语言:javascript
复制
const http=require('http')
const fs=require('fs')
const server=http.createServer((req,res)=>{
    console.log('res',getPrototypeChain(res));
    const {url,method,headers}=req;
    if(url=='/'&&method==='GET'){
        fs.readFile('index.html',((err,data)=>{
            res.statusCode='200'
            res.setHeader('Content-Type','text/html')
            res.end(data)
        }));
    }else if(url=='/users'&&method==='GET'){
        res.writeHead(200,{
            'Content-Type':'application/json'
        });
        res.end(JSON.stringify([{ username:'12325'}]));

    }else if(method==='GET'&&headers.accept.indexOf('image/*')!=-1){
        fs.createReadStream(`.${url}`).pipe(res);

    }
})

实"操":实现一个简单的express服务器

Express是一套基于 Node.js 平台,快速、开放、极简的 Web 开发框架。首先看下使用方法:

代码语言:javascript
复制
npm install express --save
代码语言:javascript
复制
const express = require('express')
const app = express()

app.get('/', (req, res) => res.end('Hello World!'))

app.get('/users', (req, res) => res.end(JSON.stringify([{username:'djtao',job:coder}])))

app.listen(3000, () => console.log('Example app listening on port 3000!'))

嗯,不多不少,大致就是这点功能。

和往常一样,这个简化版的express就叫 dexpress吧。

结构
代码语言:javascript
复制
const http=require('http')

const server=(callback)=>{
    return http.createServer(callback);
}

// 简化版express
class Dexpress{
    constructor(params) {
        this.server=server((req,res)=>{
            const {url,method,headers}=req;
            // ...
        })
    }

    get(url,callback){

    }
        //listen方法已经实现
    listen(port,callback){
        this.server.listen(port,callback)
    }
}

module.exports=()=>{
    return new Dexpress()
};

那么listen方法已经实现了。

express-router的实现

问题在于get。考虑以工厂模式建立诸多到switch判断。

每当执行一次get,就往工厂中添加一条规则,到执行时(listen)才拿出来用:

代码语言:javascript
复制
const http=require('http')
const url=require('url')

const server=(callback)=>{
    return http.createServer(callback);
}

// 简化版express
class Dexpress{
    constructor(params) {
        this.router=[];
    }

    get(url,callback){
        if(typeof url=='string'){
            let rule={url,callback,method:'GET'};
            this.router.push(rule);
        } 
    }

    listen(port,callback){
        // 调用
        const _server=server((req,res)=>{
            const {pathname}=url.parse(req.url,true); 
            this.router.forEach(x=>{
                if(pathname===x.url&&req.method===x.method){
                    x.callback(req,res);
                }
            })
        });
        _server.listen(port,callback)
    }
}

module.exports=()=>{
    return new Dexpress()
};
眼看大楼建起,眼看它崩了( process.on

假如你在get回调函数中使用了一个不存在的方法:

代码语言:javascript
复制
app.get('/abc', (req, res) => abc())

当你尝试访问abc,那么程序就会崩溃。这是开发者所不希望看到的。

在原生node中有一个 process.on方法,可以守护你的进程即使报错也不崩溃:

代码语言:javascript
复制
process.on('uncaughtException',(err)=>{
    console.error(err)
})

如果把这个功能整合到dexpress中,就好了。

奔溃这是一个事件(event)所以让你的dexpress继承它的内容即可:

代码语言:javascript
复制
const {EventEmitter}=require('events')
class Dexpress extends EventEmitter{
    constructor(params) {
        super(params)
        this.router=[];
    }
      // get ...

       listen(port,callback){
        //...
        process.on('uncaughtException',(err)=>{
            console.error(err)
        })
    }
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一Li小麦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • nodeJs常常放在前面说的套话
    • node的特点:
      • 服务端和客户端的JavaScript:
        • 快速上手
        • 模块化
        • 文件系统(fs,File System)
        • buffer
        • stream流
        • http
          • res是什么
            • 完善功能
              • 读取多媒体(图片,视频)
              • 实"操":实现一个简单的express服务器
                • 结构
                  • express-router的实现
                    • 眼看大楼建起,眼看它崩了( process.on)
                    相关产品与服务
                    内容识别
                    内容识别(Content Recognition,CR)是腾讯云数据万象推出的对图片内容进行识别、理解的服务,集成腾讯云 AI 的多种强大功能,对存储在腾讯云对象存储 COS 的数据提供图片标签、图片修复、二维码识别、语音识别、质量评估等增值服务。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档