All Projects → able8 → hello-nodejs

able8 / hello-nodejs

Licence: MIT license
Node.js 轻松学 基础篇 入门

Programming Languages

javascript
184084 projects - #8 most used programming language
HTML
75241 projects

hello-nodejs

轻松学 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等属性
unction 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 直接启动,方便
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].