Node.js 基础篇 入门

1.课程介绍与开发环境搭建

  • 主要包括

    • nodejs 基础知识
    • web 服务器
    • 异步 同步 阻塞 非阻塞
  • 课程基础

    • javascript 基础
    • html 基础
    • 命令行基础
  • Node.js 介绍

    • Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境
    • Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效
    • Node.js 的包管理器 npm,是全球最大的开源库生态系统
    • javascript 是脚本语言,需要解析器才能执行,浏览器就充当了解析器
    • 在Chrome中,解析器就是 V8 引擎,将 javascript 转换成 机器码
    • V8 引擎是开源的,由 C++ 语言编写,性能高
    • Node.js 高性能,事件驱动,非阻塞,生态圈很好
  • Node.js 安装

    • 官网 下载安装即可,很小不到20M!
    • 验证是否成功,命令行输入 node -v 显示版本号如 v8.11.4
    • 按提示升级 npm,Update available 5.6.0 → 6.4.1, npm i -g npm
    • macOS 安装完提示如下
This package has installed:
    • Node.js v8.11.4 to /usr/local/bin/node
    • npm v5.6.0 to /usr/local/bin/npm
Make sure that /usr/local/bin is in your $PATH.
  • Node.js 用途

    • javascript 运行环境
    • 操作文件(grunt gulp webpack)
    • 操作数据库
    • 写后端 api
    • 命令行工具
    • web 开发
    • 聊天室
  • JavaScript 语句后应该加分号么?

    • 知乎讨论
    • 代码风格而已,没有定论
    • 少分号更易读,不累
    • 必须加分号情况很少见:一行开头是括号(或者方括号[的时候加上分号就可以了,其他时候都不要
    • 如果下一行的行首是( [ / + -之一的话,js不会自动在上一行句尾加上分号

2.全局对象

  • 全局对象

    • 不用导入,直接使用的对象
    • 官方文档
    • Buffer 类,用于处理二进制数据
    • console,用于打印 stdout 和 stderr
    • global, 全局的命名空间对象
    • process,进程对象
    • setTimeout(callback, delay[, ...args])
    • setInterval(callback, delay[, ...args])
    • setImmediate(callback[, ...args])
    • clearTimeout(timeoutObject)
    • clearInterval(intervalObject)
    • clearImmediate(immediateObject)
  • 以下变量虽然看起来像全局变量,但实际上不是

    • 全局变量在所有模块中均可使用
    • 以下对象作用域只在模块内,详见 module文档
    • __dirname
    • __filename
    • exports
    • module
    • require()
  • 运行 .js 脚本文件

    • node app 或者 node app.js
  • 实践代码

console.log('hello world');

setTimeout(function () {
    console.log("3 seconds have passed 2");
}, 3000);

// 箭头函数,es6的写法
setTimeout(() => {
    console.log("3 seconds have passed 1");
}, 3000);

// 每间隔2秒不断执行
setInterval(function () {
    console.log("2 seconds have passed");
}, 2000);


var time = 0
var timer = setInterval(function () {
    time += 2;
    console.log(time + " seconds have passed");
    if (time > 6) {
        clearInterval(timer);
        console.log("clearInterval")
    }
}, 2000)

// 输出当前目录 和 带绝对路径的文件名
console.log(__dirname)
console.log(__filename)

console.log('end')
console.dir(global)

3.回调函数

function sayHi() {
    console.log('Hi')
}

sayHi() // 调用函数

// 将匿名函数赋给变量
var sayBye = function (name) {
    console.log(name + ' Bye')
}

sayBye()

// 第一个参数是函数
function callFunction(fun, name) {
    fun(name)
}

callFunction(sayBye, 'able')
// 或者
callFunction(function (name) {
    console.log(name + ' Bye')
}, 'able')

4.模块

  • module 对象
    • 每个文件都被视为独立的模块
    • 每个模块中,module 指向表示当前模块的对象的引用
    • module 实际上不是全局的,而是每个模块本地的
    • module.exports 导出模块内的对象,方便其他对象引用
    • require() 引入模块
    • 当 Node.js 直接运行一个文件时,require.main 会被设为它的 module
    • 可以通过 require.main === module 来判断一个文件是否被直接运行
    • module 提供了一个 filename 属性(通常等同于 __filename)
    • 可以通过检查 require.main.filename 来获取当前应用程序的入口点
// counter.js
var counter = function (arr) {
    return "There are " + arr.length + " elements in array"
}

var adder = function (a, b) {
    return `the sum of the 2 numbers is ${a+b}`
}

var pi = 3.14

// 只有一个时可以这样导入
// module.exports = counter

/*
module.exports.counter = counter
module.exports.adder = adder
module.exports.pi = pi
*/

module.exports = {
    counter: counter,
    adder: adder,
    pi: pi,
}
/* 对象可以简写
module.exports = {
    counter,
    adder,
    pi,
}
*/

//p4.js
var stuff = require('./count')

console.log(stuff.counter(['ruby', 'nodejs', 'react']))
console.log(stuff.adder(3, 2))
console.log(stuff.pi)

5.事件 events

  • 多数 Node.js 核心 API 都采用异步事件驱动架构

  • 所有能触发事件的对象都是 EventEmitter 类的实例

  • 事件名称通常是驼峰式的字符串

  • 实践代码

var events = require('events')
var util = require('util')

// 事件 对象
var myEmitter = new events.EventEmitter()

// 绑定 事件名称 和 回调函数
myEmitter.on('someEvent', function (message) {
    console.log(message)
})

// 触发实践,使用事件名称
myEmitter.emit('someEvent', 'The event was emitted')

// 创建对象
var Person = function (name) {
    this.name = name
}

// 继承,让Person类具有事件对象的特性,绑定和触发事件
util.inherits(Person, events.EventEmitter)
// 新建对象
var xiaoming = new Person('xiaoming')
var lili = new Person('lili')

var person = [xiaoming, lili]

// 循环person数组,绑定事件
person.forEach(function (person) {
    person.on('speak', function (message) {
        console.log(person.name + ' said: ' + message)
    })
})

// 触发事件
xiaoming.emit('speak', 'hi')
lili.emit('speak', 'I want a curry')

6.读写文件(同步和异步)

var fs = require('fs')

// 同步读写文件,顺序执行,如果读取时间很长,会阻塞进程
var readMe = fs.readFileSync('readMe.txt', 'utf8')
fs.writeFileSync('writeMe.txt', readMe)

console.log(readMe)
console.log('finished sync')

// 异步读写文件
// 异步事件,Nodejs 维护一个事件队列,注册事件,完成后执行主线程
// 当主线程空闲时,取出执行事件,从线程池中发起线程执行事件, 当事件执行完成后通知主线程。这就是异步高效的原因。

var readMe = fs.readFile('readMe.txt', 'utf8', function (err, data) {
    fs.writeFile('writeMe.txt', data, function () {
        console.log('writeMe has finished')
    })
})

console.log('end')

7.创建和删除目录

var fs = require('fs')

// 异步删除文件
fs.unlink('writeMe.txt', function () {
    console.log('delete writeMe.txt file')
})

// 同步创建和删除目录
fs.mkdirSync('stuff')
fs.rmdirSync('stuff')

// 异步
fs.mkdir('stuff', function () {
    fs.readFile('readMe.txt', 'utf8', function (err, data) {
        fs.writeFile('./stuff/writeMe.txt', data, function () {
            console.log('copy successfully')
        })
    })
})

8.流和管道

  • 流(stream)

    • 处理流式数据的抽象接口
    • stream 模块提供了一些基础的 API,用于构建实现了流接口的对象
    • 流可以是可读的、可写的、或是可读写的,所有的流都是 EventEmitter 的实例
    • 流处理数据通过缓存可以提高性能
  • 管道

    • 使用管道,代码量更少
    • myReadStream.pipe(myWriteStream)
var fs = require('fs')

var myReadStream = fs.createReadStream(__dirname + '/readMe.txt')
myReadStream.setEncoding('utf8')
var myWriteStream = fs.createWriteStream(__dirname + '/writeMe.txt')
var data = ''

myReadStream.on('data', function (chunk) {
    console.log('new chunk received')
    // console.log(chunk)
    myWriteStream.write(chunk)
})

myReadStream.on('end', function () {
    console.log(data)
})

var writeData = 'hello world'
myWriteStream.write(writeData)
myWriteStream.end()
myWriteStream.on('finish', function () {
    console.log('finished')
})

// 使用管道,代码量更少
myReadStream.pipe(myWriteStream)

9.web 服务器 part1 介绍

var http = require('http')

var server = http.createServer(function (req, res) {
    console.log('request received')
    res.writeHead(200, { 'Content-Type': 'text/plain' })
    // res.write('Hello from out application')
    // res.end()
    // 或
    res.end('Hello from out application')
})

server.listen(3000, '127.0.0.1')
console.log('server started on http://127.0.0.1:3000')

10.web 服务器 part2 响应JSON

  • 响应JSON
var myObj = {
        name: 'able',
        job: 'programmer',
        age: 27
    }
    res.end(JSON.stringify(myObj))
  • JSON 对象
    • 字符串必须使用双引号表示,不能使用单引号
    • 对象的键名必须放在双引号里面
    • 数组或对象最后一个成员的后面,不能加逗号
    • JSON对象是 JavaScript 的原生对象,用来处理 JSON 格式数据
    • JSON.stringify方法用于将一个值转为 JSON 字符串
    • JSON.parse方法用于将 JSON 字符串转换成对应的值

11.web 服务器 part3 响应HTML页面

var http = require('http')
var fs = require('fs')

var onRequest = function (req, res) {
    console.log('request received')
    res.writeHead(200, { 'Content-Type': 'text/html' })
    // res.writeHead(200, { 'Content-Type': 'text/plain' })
    var myReadStream = fs.createReadStream(__dirname + '/index.html', 'utf8')
    myReadStream.pipe(res)
}

var server = http.createServer(onRequest)
server.listen(3000)
console.log('server started on http://127.0.0.1:3000')

12.web 服务器 part4 用模块化思想组织代码

  • 代码封装成模块,方便统一管理和调用
// server.js
var http = require('http')
var fs = require('fs')

function startServer() {
    var onRequest = function (req, res) {
        console.log('request received')
        res.writeHead(200, { 'Content-Type': 'text/html' })

        var myReadStream = fs.createReadStream(__dirname + '/index.html', 'utf8')
        myReadStream.pipe(res)
    }
    var server = http.createServer(onRequest)
    server.listen(3000)
    console.log('server started on http://127.0.0.1:3000')
}

module.exports.startServer = startServer

// 调用
var server = require('./server')

server.startServer()

13.web 服务器 part5 路由

  • console.dir(xx) 查看对象的所有属性和方法
  • req.url 请求中包含url等属性
function startServer() {
    var onRequest = function (req, res) {
        console.log('request received ' + req.url)

        if (req.url === '/' || req.url === '/home') {
            res.writeHead(200, { 'Content-Type': 'text/html' })
            fs.createReadStream(__dirname + '/index.html', 'utf8').pipe(res)
        } else if (req.url === '/review') {
            res.writeHead(200, { 'Content-Type': 'text/html' })
            fs.createReadStream(__dirname + '/review.html', 'utf8').pipe(res)
        } else if (req.url === '/api/v1/records') {
            res.writeHead(200, { 'Content-Type': 'application/json' })
            var jsonObj = {
                name: 'able'
            }
            res.end(JSON.stringify(jsonObj))
        } else {
            res.writeHead(200, { 'Content-Type': 'text/html' })
            fs.createReadStream(__dirname + '/404.html', 'utf8').pipe(res)
        }
    }
    var server = http.createServer(onRequest)
    server.listen(3000)
    console.log('server started on http://127.0.0.1:3000')
}

14.web 服务器 part6 重构路由代码

  • 将路由、处理函数和主程序分离,单独存放
  • 分工明确,各司其职,方便管理

15.web 服务器 part7 使用 GET或 POST 请求 发送数据

  • querystring - 查询字符串
    • var querystring = require('querystring')
    • querystring.parse(data) 把一个 URL 查询字符串 str 解析成一个键值对的集合
// 接收请求数据,然后处理,查看request 类型
var data = ""
req.on("error", function (err) {
    console.error(err)
}).on("data", function (chunk) {
    data += chunk
}).on("end", function () {
    if (req.mothod === "POST") {
        if (data.length > 1e6) {
            req.connection.destroy() // 如果数据很大,就断开
        }
        route(handle, pathname, res, querystring.parse(data))
    } else {
        var params = url.parse(req.url, true).query
        route(handle, pathname, res, params)
    }
})
// 或者
// var data = []
// data.push(chunk)
// data = Buffer.concat(data).toString()

16.包管理器 npm

# Or alias it in .bashrc or .zshrc
echo '\n#alias for npm\nalias npm="npm --registry=https://registry.npm.taobao.org \
  --cache=$HOME/.npm/.cache/npm \
  --disturl=https://npm.taobao.org/dist \
  --userconfig=$HOME/.npmrc"' >> ~/.zshrc && source ~/.zshrc
  • yarn 也是包管理器,更快下载速度

17.package.json 文件

  • 记录项目中使用的包名,发布时不用包内容了,只要名称就行
  • npm init 提问式初始化项目信息,生成package.json文件,-y 全部默认
  • npm install --save xxx安装的同时,将信息写入package.json
  • npm install --save-dev xxx安装的同时,将信息写入package.json中的dev开发依赖
  • npm view moduleNames 查看node模块的package.json文件夹
  • npm run start 启动包,执行 package.json scripts 中的 start 命令,还有 stop restart test
  • npm install 安装 package.json 中记录的包

18.nodemon监控文件并重启服务

  • nodemon 用来监视应用中的任何文件更改并自动重启服务
  • 非常适合用在开发环境中,方便啊,不用手动操作了
  • 全局安装 npm install -g nodemon
  • 本地安装 npm install --save-dev nodemon
  • 启动应用 nodemon [your node app]
  • 获取修改 package.json 中的启动脚本,添加nodemon app.js, 用 npm start 直接启动,方便
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336