Node.js包教不包会|实验代码+运行结果
课程来源:https://www.shiyanlou.com/courses/493【@实验楼】
第1节 一个最简单的 express 应用
1.1 新建lesson1,进去里面安装express
![](https://img.haomeiwen.com/i6974948/38d3e6b854d072df.png)
1.2 新建app.js
![](https://img.haomeiwen.com/i6974948/d78d2a268f899b51.png)
![](https://img.haomeiwen.com/i6974948/20ec8790607f6669.png)
1.3 运行app.js,打开浏览器,输入http://localhost:3000/
![](https://img.haomeiwen.com/i6974948/05c04b2175d7aaa4.png)
![](https://img.haomeiwen.com/i6974948/441178da0ecb022d.png)
第2节 学习使用外部模块
2.1 新建lesson1,并生成一份它的 package.json。
![](https://img.haomeiwen.com/i6974948/990cb0b3d0ca0d0d.png)
![](https://img.haomeiwen.com/i6974948/ecb6472f01150dd1.png)
2.2 安装依赖, express 和 utility 两个模块
![](https://img.haomeiwen.com/i6974948/8fb4dadcd3ae453a.png)
![](https://img.haomeiwen.com/i6974948/3f97a61254aae94d.png)
2.3 新建app.js
![](https://img.haomeiwen.com/i6974948/023e38a2496bd34a.png)
![](https://img.haomeiwen.com/i6974948/4ad3aca464828b2b.png)
2.4 运行app.js,在浏览器输入http://localhost:3000/?q=alsotang
![](https://img.haomeiwen.com/i6974948/4468ca16c875aecf.png)
![](https://img.haomeiwen.com/i6974948/157c5565f848b5d6.png)
第3节 使用 superagent 与 cheerio 完成简单爬虫
3.1 新建文件夹,进去之后 npm init
![](https://img.haomeiwen.com/i6974948/c491705a45e2d29d.png)
![](https://img.haomeiwen.com/i6974948/75b7995b6c2993e8.png)
3.2 安装依赖:express,superagent,cheerio
![](https://img.haomeiwen.com/i6974948/8ae5287f3ede33c8.png)
3.3 写应用逻辑
![](https://img.haomeiwen.com/i6974948/cadb0eea43ff3df9.png)
![](https://img.haomeiwen.com/i6974948/8708f7c913ee908e.png)
3.4 运行及结果
![](https://img.haomeiwen.com/i6974948/ae1b6263e0fc49a4.png)
![](https://img.haomeiwen.com/i6974948/3218524563c86036.png)
第4节 使用 eventproxy 控制并发
4.1 获取主页
4.1.1 新建文件夹,进去之后 npm init
![](https://img.haomeiwen.com/i6974948/437b8e8f9253b7a6.png)
4.1.2 安装依赖:express,superagent,cheerio
![](https://img.haomeiwen.com/i6974948/db2bdabf9a432e92.png)
4.1.3 写应用逻辑
![](https://img.haomeiwen.com/i6974948/04f85e791d5737f2.png)
![](https://img.haomeiwen.com/i6974948/eedfc5b3976b490e.png)
4.1.4 运行及结果
![](https://img.haomeiwen.com/i6974948/39304d4bb7c0de81.png)
4.2 eventproxy介绍
4.2.1 不使用 eventproxy 也不使用计数器
// 参考 jquery 的 $.get 的方法
$.get("http://data1_source", function (data1) {
// something
$.get("http://data2_source", function (data2) {
// something
$.get("http://data3_source", function (data3) {
// something
var html = fuck(data1, data2, data3);
render(html);
});
});
});
4.2.2 使用计数器
(function () {
var count = 0;
var result = {};
$.get('http://data1_source', function (data) {
result.data1 = data;
count++;
handle();
});
$.get('http://data2_source', function (data) {
result.data2 = data;
count++;
handle();
});
$.get('http://data3_source', function (data) {
result.data3 = data;
count++;
handle();
});
function handle() {
if (count === 3) {
var html = fuck(result.data1, result.data2, result.data3);
render(html);
}
}
})();
4.2.3 使用eventproxy
var ep = new eventproxy();
ep.all('data1_event', 'data2_event', 'data3_event', function (data1, data2, data3) {
var html = fuck(data1, data2, data3);
render(html);
});
$.get('http://data1_source', function (data) {
ep.emit('data1_event', data);
});
$.get('http://data2_source', function (data) {
ep.emit('data2_event', data);
});
$.get('http://data3_source', function (data) {
ep.emit('data3_event', data);
});
eventproxy 提供了不少其他场景所需的 API,但最最常用的用法就是以上的这种,即:
- 先 var ep = new eventproxy(); 得到一个 eventproxy 实例。
- 告诉它你要监听哪些事件,并给它一个回调函数。
ep.all('event1', 'event2', function (result1, result2) {})。- 在适当的时候 ep.emit('event_name', eventData)。
4.3 抓取每条url的内容
![](https://img.haomeiwen.com/i6974948/ccc9d5bc1a09de47.png)
完整代码如下:
var eventproxy = require('eventproxy');
var superagent = require('superagent');
var cheerio = require('cheerio');
var url = require('url');
var cnodeUrl = 'https://cnodejs.org/';
superagent.get(cnodeUrl)
.end(function (err, res) {
if (err) {
return console.error(err);
}
var topicUrls = [];
var $ = cheerio.load(res.text);
$('#topic_list .topic_title').each(function (idx, element) {
var $element = $(element);
var href = url.resolve(cnodeUrl, $element.attr('href'));
topicUrls.push(href);
});
var ep = new eventproxy();
ep.after('topic_html', topicUrls.length, function (topics) {
topics = topics.map(function (topicPair) {
var topicUrl = topicPair[0];
var topicHtml = topicPair[1];
var $ = cheerio.load(topicHtml);
return ({
title: $('.topic_full_title').text().trim(),
href: topicUrl,
comment1: $('.reply_content').eq(0).text().trim(),
});
});
console.log('final:');
console.log(topics);
});
topicUrls.forEach(function (topicUrl) {
superagent.get(topicUrl)
.end(function (err, res) {
console.log('fetch ' + topicUrl + ' successful');
ep.emit('topic_html', [topicUrl, res.text]);
});
});
});
![](https://img.haomeiwen.com/i6974948/7276c55bb0503980.png)
第5节 使用 async 控制并发
5.1 准备工作
- mkdir lesson5 && cd lesson5
- npm init
- npm install --save async
- touch app.js
5.2 app.js 代码如下
![](https://img.haomeiwen.com/i6974948/9a23330a5ee742be.png)
5.3 运行及结果
![](https://img.haomeiwen.com/i6974948/0621f9a11d1287b4.png)
![](https://img.haomeiwen.com/i6974948/7dc1010b6f61e1c9.png)
第6节 测试用例:mocha,should,istanbul
6.1 编写测试用例
6.1.1 准备工作
![](https://img.haomeiwen.com/i6974948/76fe72ccf76ce5fb.png)
6.1.2 编写测试用例
![](https://img.haomeiwen.com/i6974948/b8a9fbe953fe51a7.png)
![](https://img.haomeiwen.com/i6974948/80a44ce855fa99e8.png)
6.1.3 运行及结果
![](https://img.haomeiwen.com/i6974948/3f23f568e4455aaf.png)
6.2 执行测试
6.2.1 修改main.js,暴露 fibonacci
![](https://img.haomeiwen.com/i6974948/ae8613b799e82755.png)
6.2.2 准备工作
![](https://img.haomeiwen.com/i6974948/9ea2755f15a1b762.png)
![](https://img.haomeiwen.com/i6974948/14967326ca70dddd.png)
6.2.3 main.test.js 代码
![](https://img.haomeiwen.com/i6974948/29abfbd36a248a2f.png)
6.2.4 运行及结果
![](https://img.haomeiwen.com/i6974948/36bba9ce23bc6765.png)
![](https://img.haomeiwen.com/i6974948/f04e4434a7dd2676.png)
![](https://img.haomeiwen.com/i6974948/91bb98d0e8c3504d.png)
6.2.4 增加测试,修改main.test.js,并运行
![](https://img.haomeiwen.com/i6974948/0036fc9211902b8f.png)
![](https://img.haomeiwen.com/i6974948/cfdcfeac99a8a638.png)
6.2.5 根据报错更新main.js,使测试全部通过
![](https://img.haomeiwen.com/i6974948/c1c1b6ac2740e0e9.png)
![](https://img.haomeiwen.com/i6974948/353f40a5a7c3380f.png)
以上过程为TDD(测试驱动开发):先把要达到的目的都描述清楚,然后让现有的程序跑不过 case,再修补程序,让 case 通过。
6.3 使用isbantul(代码覆盖率)
- 行覆盖率(line coverage):是否每一行都执行了?
- 函数覆盖率(function coverage):是否每个函数都调用了?
- 分支覆盖率(branch coverage):是否每个if代码块都执行了?
- 语句覆盖率(statement coverage):是否每个语句都执行了?
6.3.1 准备工作
- subo npm install --global mocha
- sudo npm install --save-dev mocha
- sudo nom i istanbul -g
- istanbul cover _mocha
6.3.2 运行结果
![](https://img.haomeiwen.com/i6974948/1490dc494f3e331f.png)
第7节 浏览器端测试:mocha,chai,phantomjs
7.1 浏览器环境执行
7.1.1 准备工作
- mkdir lesson7 && cd lesson7
- mkdir vendor && cd vendor
- npm i -g mocha # 安装全局的 mocha 命令行工具
- mocha init . # 生成脚手架
![](https://img.haomeiwen.com/i6974948/67de6a812e967d30.png)
7.1.2 在index.html插入如下代码
![](https://img.haomeiwen.com/i6974948/9e78e89e4a1962d8.png)
7.1.3 在test.js输入如下代码
![](https://img.haomeiwen.com/i6974948/890c98e3fd80765e.png)
7.1.4 打开index.html,结果如下
![](https://img.haomeiwen.com/i6974948/40904f12de7497cb.png)
7.2 命令行环境执行
7.2.1 安装 mocha-phantomjs
- sudo npm i -g mocha-phantomjs
7.2.2 index.html增加如下代码
![](https://img.haomeiwen.com/i6974948/1a66fcee279a2fa5.png)
7.2.3 运行
![](https://img.haomeiwen.com/i6974948/7320f93b3dd77075.png)
报错,不知道是什么原因??
第8节 测试用例:supertest
8.1 准备工作
![](https://img.haomeiwen.com/i6974948/3caee88bc1aaafe1.png)
![](https://img.haomeiwen.com/i6974948/73e9464d1206fa9c.png)
![](https://img.haomeiwen.com/i6974948/bf70f4f50ecda0ef.png)
8.2 编写app.js
![](https://img.haomeiwen.com/i6974948/67a7a790785f5c92.png)
![](https://img.haomeiwen.com/i6974948/be21312d47b3dd78.png)
8.3 编写test/app.test.js
![](https://img.haomeiwen.com/i6974948/f6e8978212c4bbf7.png)
![](https://img.haomeiwen.com/i6974948/f4e6fdf54c3e96bf.png)
8.4 运行及结果
![](https://img.haomeiwen.com/i6974948/a9abf61415ed2c39.png)
--
第9节 正则表达式
- i :不区分大小写
![](https://img.haomeiwen.com/i6974948/9c7038a8a5b9e587.png)
- g:匹配多个
![](https://img.haomeiwen.com/i6974948/66a39209623a194d.png)
- m :^ 和 $ 可以匹配每一行的开头。
![](https://img.haomeiwen.com/i6974948/40ba83c478836d9a.png)
- 加 g 会返回数组,不加 g 则返回比较详细的信息
![](https://img.haomeiwen.com/i6974948/83df579d181a78f3.png)
- 加 g 之后,如果你的正则不是字面量的正则,而是存储在变量中的话,这个变量就会变得有记忆
![](https://img.haomeiwen.com/i6974948/5104fad669fd0f23.png)
- [\s\S],[^]:能匹配包括 \n 在内的所有字符
![](https://img.haomeiwen.com/i6974948/54d4cfebbed0803e.png)
![](https://img.haomeiwen.com/i6974948/2771a34907c55333.png)
第10节 benchmark 怎么写(测试性能)
- mkdir lesson10 && cd lesson10
- npm init
- npm install benchmark --save
- touch main.js
![](https://img.haomeiwen.com/i6974948/ba2adaea69a41a59.png)
![](https://img.haomeiwen.com/i6974948/97361864ee33cae8.png)
第11节 作用域与闭包
11.1 var作用域
- 内部函数可以访问外部函数的变量,外部不能访问内部函数的变量。
![](https://img.haomeiwen.com/i6974948/dff3b886d242bd64.png)
![](https://img.haomeiwen.com/i6974948/a7b22aab11a5f956.png)
内部函数child可以访问变量age,而外部函数parent不可以访问child中的变量childAge,因此会抛出没有定义变量的异常。
- 如果忘记var,那么变量就被声明为全局变量了。
![](https://img.haomeiwen.com/i6974948/d59ffca27bafc4ec.png)
![](https://img.haomeiwen.com/i6974948/29f720bf3fccc4d8.png)
- JavaScript 中,变量的局部作用域是函数级别的。不同于 C 语言,在 C 语言中,作用域是块级别的。
![](https://img.haomeiwen.com/i6974948/3fd9f079c89ad2b3.png)
![](https://img.haomeiwen.com/i6974948/d171609304dc91bd.png)
11.2 闭包
![](https://img.haomeiwen.com/i6974948/6b162c18f6bf2235.png)
![](https://img.haomeiwen.com/i6974948/b80091ac5abd2214.png)
- 闭包的一个坑
![](https://img.haomeiwen.com/i6974948/00df1200237f64ce.png)
![](https://img.haomeiwen.com/i6974948/29f1d854f29228b7.png)
- 上面这个代码块打印五个 5:setTimeout 中的 i 是对外层 i 的引用。当 setTimeout 的代码被解释的时候,运行时只是记录了 i 的引用,而不是值。而当 setTimeout 被触发时,五个 setTimeout 中的 i 同时被取值,由于它们都指向了外层的同一个 i,而那个 i 的值在迭代完成时为 5,所以打印了五次 5。
- 下面这个代码块打印0-4:把 i 赋值成一个局部的变量,从而摆脱外层迭代的影响。
11.3 this
- 函数有所属对象时:指向所属对象
![](https://img.haomeiwen.com/i6974948/cff571c16e6189e5.png)
![](https://img.haomeiwen.com/i6974948/dbc163e1f11c2983.png)
- 函数没有所属对象:指向全局对象
![](https://img.haomeiwen.com/i6974948/3c82207625ababbf.png)
![](https://img.haomeiwen.com/i6974948/83a2686fa169a5a7.png)
![](https://img.haomeiwen.com/i6974948/a11e2733cd63a701.png)
- 构造器中的 this:指向新对象
![](https://img.haomeiwen.com/i6974948/c8a9ca9205fe47f3.png)
![](https://img.haomeiwen.com/i6974948/709fa0364dc6f4f6.png)
- apply 和 call 调用以及 bind 绑定:指向绑定的对象
![](https://img.haomeiwen.com/i6974948/f40d723f3ce41c63.png)
![](https://img.haomeiwen.com/i6974948/4bdc290067488dcd.png)
![](https://img.haomeiwen.com/i6974948/5f307bab9d2cc49b.png)
第13节 持续集成平台:travis
13.1 注册travis账号
我直接关联了自己的github账号
13.2 选择自己需要测试的仓库
![](https://img.haomeiwen.com/i6974948/5a1efdf1bab06083.png)
![](https://img.haomeiwen.com/i6974948/9a451fe3ee483328.png)
![](https://img.haomeiwen.com/i6974948/7e1804af40cb6a28.png)
13.3 写.travis.yml
![](https://img.haomeiwen.com/i6974948/55d72e44af8f272d.png)
13.4 运行及结果
当 .travis.yml 完成后,travis自动被触发
![](https://img.haomeiwen.com/i6974948/3631355d47ce0f5e.png)
![](https://img.haomeiwen.com/i6974948/8a0eabad9e24c123.png)
![](https://img.haomeiwen.com/i6974948/f18d1bceb382c84b.png)
15.2 使用mongodb和mongoose模块
- mkdir lesson15 && cd lesson15
- touch test_db.js
- npm install mongoose
![](https://img.haomeiwen.com/i6974948/8ca512ba45758349.png)
- node test_db.js
![](https://img.haomeiwen.com/i6974948/07347091493877f7.png)
- mongod
- 在另一个终端输入 mongo
![](https://img.haomeiwen.com/i6974948/ae0b0ad07f9b0a73.png)