Nodejs练习

2019年11月17日 阅读数:28
这篇文章主要向大家介绍Nodejs练习,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

环境安装

# Node Version Manager
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
$ nvm ls
    ->       system
    iojs -> N/A (default)
    node -> stable (-> N/A) (default)
    unstable -> N/A (default)
$ brew install expressjs
$ express --version
    4.16.0
# npm命令提供更清晰直观的显示:
$ npm list
复制代码

Hello world

$ mkdir lesson1 && cd lesson1
$ npm install express --registry=https://registry.npm.taobao.org
$ ls node_modules 
$ touch app.js 
复制代码

Hello world 代码:javascript

// 这句的意思就是引入 `express` 模块,并将它赋予 `express` 这个变量等待使用。
var express = require('express');
// 调用 express 实例,它是一个函数,不带参数调用时,会返回一个 express 实例,将这个变量赋予 app 变量。
var app = express();

// app 自己有不少方法,其中包括最经常使用的 get、post、put/patch、delete,在这里咱们调用其中的 get 方法,为咱们的 `/` 路径指定一个 handler 函数。
// 这个 handler 函数会接收 req 和 res 两个对象,他们分别是请求的 request 和 response。
// request 中包含了浏览器传来的各类信息,好比 query 啊,body 啊,headers 啊之类的,均可以经过 req 对象访问到。
// res 对象,咱们通常不从里面取信息,而是经过它来定制咱们向浏览器输出的信息,好比 header 信息,好比想要向浏览器输出的内容。这里咱们调用了它的 #send 方法,向浏览器输出一个字符串。
app.get('/', function (req, res) {
    res.send('Hello World');
});

// 定义好咱们 app 的行为以后,让它监听本地的 3000 端口。这里的第二个函数是个回调函数,会在 listen 动做成功后执行,咱们这里执行了一个命令行输出操做,告诉咱们监听动做已完成。
app.listen(3000, function () {
    console.log('app is listening at port 3000');
});
复制代码
$ node app.js # 运行
复制代码

req.query用法,package.json用法:php

这个 package.json 文件就是定义了项目的各类元信息,包括项目的名称,git repo 的地址,做者等等。最重要的是,其中定义了咱们项目的依赖,这样这个项目在部署时,咱们就没必要将 node_modules 目录也上传到服务器,服务器在拿到咱们的项目时,只须要执行 npm install,则 npm 会自动读取 package.json 中的依赖并安装在项目的 node_modules 下面,而后程序就能够在服务器上跑起来了。css

$ npm init
# 交互初始化一个package.json文件
$ npm install express utility --save
# 没有指定 registry 的状况下,默认从 npm 官方安装,上次咱们是从淘宝的源安装的。多了个 --save 参数,这个参数的做用,就是会在你安装依赖的同时,自动把这些依赖写入 package.json。这时查看 node_modules 目录,会发现有两个文件夹,分别是 express 和 utility。
复制代码
// 引入依赖
var express = require('express');
var utility = require('utility');

// 创建 express 实例
var app = express();

app.get('/', function (req, res) {
  // 从 req.query 中取出咱们的 q 参数。
  // 若是是 post 传来的 body 数据,则是在 req.body 里面,不过 express 默认不处理 body 中的信息,须要引入 https://github.com/expressjs/body-parser 这个中间件才会处理,这个后面会讲到。
  // 若是分不清什么是 query,什么是 body 的话,那就须要补一下 http 的知识了
  var q = req.query.q;
  // 调用 utility.md5 方法,获得 md5 以后的值
  // 之因此使用 utility 这个库来生成 md5 值,其实只是习惯问题。每一个人都有本身习惯的技术堆栈,
  // 我刚入职阿里的时候跟着苏千和朴灵混,因此也混到了很多他们的技术堆栈,仅此而已。
  // utility 的 github 地址:https://github.com/node-modules/utility
  // 里面定义了不少经常使用且比较杂的辅助方法,能够去看看
  var md5Value = utility.md5(q);
  res.send(md5Value);
});

app.listen(3000, function (req, res) {
  console.log('app is running at port 3000');
});
复制代码

使用 superagent 抓取网页,使用 cheerio 分析网页

superagent(visionmedia.github.io/superagent/ ) 是个 http 方面的库,能够发起 get 或 post 请求。cheerio(github.com/cheeriojs/c… ) 你们能够理解成一个 Node.js 版的 jquery,用来从网页中以 css selector 取数据,使用方式跟 jquery 同样同样的。html

const express = require('express');
const utility = require('utility');
const superagent = require('superagent');
const cheerio = require('cheerio');

const app = express();

app.get('/', function (req, res) {
    superagent.get('https://cnodejs.org/').end(function (err, sres) {
        if (err) { // 错误处理
            return next(err);
        }
        var $ = cheerio.load(sres.text);
        var items = [];
        // // sres.text 里面存储着网页的 html 内容,将它传给 cheerio.load 以后
        // 就能够获得一个实现了 jquery 接口的变量,咱们习惯性地将它命名为 `$`
        // 剩下就都是 jquery 的内容了
        $('#topic_list .topic_title').each(function (idx, element) {
            var $element = $(element);
            items.push({
                title: $element.attr('title'),
                href: $element.attr('href')
            });
        });
        res.send(items);
    })
})

app.listen(3000, function () {
    console.log('listen on port 3000...')
})
复制代码

异步并发

eventproxy 提供了很多其余场景所需的 API,但最最经常使用的用法就是以上的这种,即: 先 var ep = new eventproxy(); 获得一个 eventproxy 实例。 告诉它你要监听哪些事件,并给它一个回调函数。ep.all('event1', 'event2', function (result1, result2) {})。 在适当的时候 ep.emit('event_name', eventData)。 eventproxy 相关学习连接前端

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((err, res) => {
    if (err) {
        console.error(err);
        return
    }
    const topicUrl = [];
    const $ = cheerio.load(res.text);

    // console.log('toplist = ',$('#topic_list .topic_title'))
    $('#topic_list .topic_title').each((id, element) => {
        const $element = $(element);
        // console.log($element);
        const href = url.resolve(cnodeUrl, $element.attr('href'));
        topicUrl.push(href);
    });

    const ep = new eventproxy();
    //after方法适合重复的操做,好比读取10个文件,调用5次数据库等。将handler注册到N次相同事件的触发上。达到指定的触发数,handler将会被调用执行,每次触发的数据,将会按触发顺序,存为数组做为参数传入。
    ep.after('topic_html', topicUrl.length, (topics) => {
        topics = topics.map((pairs) => {
            const topicUrl = pairs[0];
            const topicHtml = pairs[1];
            const $ = cheerio.load(topicHtml);
            return ({
                title: $('.topic_full_title').text().trim(),
                href: topicUrl,
                comment: $('.reply_content').eq(0).text().trim()
            });
        });
        console.log('topics = ', topics);
    });
    topicUrl.map((url) => {
        superagent.get(url).end((err, res) => {
            ep.emit('topic_html', [url, res.text]);
        });
    });
});
复制代码

使用 async 来控制并发链接数。

async demo。何时用 eventproxy,何时使用 async 呢?它们不都是用来作异步流程控制的吗? 个人答案是: 当你须要去多个源(通常是小于 10 个)汇总数据的时候,用 eventproxy 方便;当你须要用到队列,须要控制并发数,或者你喜欢函数式编程思惟时,使用 async。大部分场景是前者,因此我我的大部分时间是用 eventproxy 的。java

除了async 的 mapLimit(arr, limit, iterator, callback) 接口,还有个经常使用的控制并发链接数的接口是 queue(worker, concurrency),你们能够去 github.com/caolan/asyn… 看看说明。node

const async = require('async');
const baiduurl = 'https://www.baidu.com/';
var concurrencyCount = 0;

fetchUrl = (url, callback) => {
    const delay = parseInt((Math.random() * 10000000) % 2000, 10);
    concurrencyCount++;
    console.log('如今并发数', concurrencyCount, ', 正抓取', url, ', 耗时 ', delay, '毫秒');
    setTimeout(() => {
        concurrencyCount--;
        callback(null, url + ' html content'); // 第一个项是错误,第二个是对象
    }, delay);
};

const urls = [];
for (var i = 0; i < 20; i++) {
    urls.push(baiduurl + '_' + i);
}

/**
 * mapLimit(coll, limit, iteratee, callback opt)
 * coll:  要迭代的集合
 * limit: 一次异步操做最大数量
 * iteratee:对于coll中每个item,迭代执行该函数,用(item,callback)调用,callback可选 。
 * callback:全部iteratee 函数完成后或发生错误时触发的回调函数。用(err, results)调用。results能够是iteratee 函数完成后触发callback时传递的项。
 */
async.mapLimit(urls, 5, (url, callback) => {
    fetchUrl(url, callback)
}, (err, result) => { // 全部都执行完才会执行
    console.log('result = ', result);
})
复制代码

《测试用例:mocha,should,istanbul》

# 装个全局的 mocha: 
$ npm install mocha -g。
# -g 与 非-g 的区别,就是安装位置的区别,g 是 global 的意思。若是不加的话,则安装 mocha 在你的项目目录下面;若是加了,则这个 mocha 是安装在全局的,若是 mocha 有可执行命令的话,那么这个命令也会自动加入到你系统 $PATH 中的某个地方(在个人系统中,是这里 /Users/alsotang/.nvm/v0.10.29/bin)
复制代码
// app.js
fibonacci = (n) => {
    if (n === 0) return 0;
    if (n === 1) return 1;
    return this.fibonacci(n - 1) + this.fibonacci(n - 2);
};
if (require.main === module) {
    var n = Number(process.argv(2));
    console.log('fibonacci(', n, ') is ', fibonacci(n));
}
exports.fibonacci = fibonacci

// test/main.test.js
var main = require('../app');
var should = require('should');

describe('test/main.test.js', () => {
    it('should equal 55 when n === 10', () => {
        main.fibonacci(10).should.equal(55);
    });
    it('show equal 1 when n === 1', () => {
        main.fibonacci(1).should.equal(1);
    });
    it('show equal 0 when n ===0 ', () => {
        main.fibonacci(0).should.equal(0);
    });
    it('should throw then n is not a number', () => {
        (() => { main.fibonacci('哎呀') }).should.throw('n should be a Number');
    });
});
复制代码
#  进入到项目根目录
$ mocha
  test/main.test.js
    ✓ should equal 55 when n === 10
    ✓ show equal 1 when n === 1
    ✓ show equal 0 when n ===0 
    1) should throw then n is not a number
复制代码

前端脚本单元测试

运行环境应当在浏览器中,能够操纵浏览器的DOM对象,且能够随意定义执行时的 html 上下文。 测试结果应当能够直接反馈给 mocha,判断测试是否经过。python

《正则表达式》

var web_development = "python php ruby javascript jsonp perhapsphpisoutdated";
复制代码

找出其中 包含 p 但不包含 ph 的全部单词,即mysql

[ 'python', 'javascript', 'jsonp' ]jquery

知识点

  1. 正则表达式的使用
  2. js 中的正则表达式与 pcre(en.wikipedia.org/wiki/Perl_C… ) 的区别

课程内容

开始这门课以前,你们先去看两篇文章。

《正则表达式30分钟入门教程》:www.cnblogs.com/deerchao/ar…

上面这篇介绍了正则表达式的基础知识,可是对于零宽断言没有展开来说,零宽断言看下面这篇:

《正则表达式之:零宽断言不『消费』》:fxck.it/post/505582… 好了。

在好久好久之前,有一门语言一度是字符串处理领域的王者,叫 perl。 伴随着 perl,有一个相似正则表达式的标准被实现了出来,叫 pcre:Perl Compatible Regular Expressions.不过遗憾的是,js 里面的正则与 pcre 不是兼容的。不少语言都这样。若是须要测试你本身写的正则表达式,建议上这里:refiddle.com/ ,能够所见即所得地调试 。接下来咱们主要讲讲 js 中须要注意的地方,至于正则表达式的内容,上面那两篇文章足够学习了。

第一,js 中,对于四种零宽断言,只支持 零宽度正预测先行断言 和 零宽度负预测先行断言 这两种。

第二,js 中,正则表达式后面能够跟三个 flag,好比 /something/igm。他们的意义分别是,

  • i 的意义是不区分大小写
  • g 的意义是,匹配多个
  • m 的意义是,是 ^$ 能够匹配一行的开头。

分别举个例子:

/a/.test('A') // => false
/a/i.test('A') // => true

'hello hell hoo'.match(/h.*?\b/) // => [ 'hello', index: 0, input: 'hello hell hoo' ]
'hello hell hoo'.match(/h.*?\b/g) // => [ 'hello', 'hell', 'hoo' ]

'aaa\nbbb\nccc'.match(/^[\s\S]*?$/g) // => [ 'aaa\nbbb\nccc' ]
'aaa\nbbb\nccc'.match(/^[\s\S]*?$/gm) // => [ 'aaa', 'bbb', 'ccc' ]
复制代码

与 m 意义相关的,还有 \A, \Z\z。他们的意义分别是:

\A  字符串开头(相似^,但不受处理多行选项的影响)
\Z  字符串结尾或行尾(不受处理多行选项的影响)
\z  字符串结尾(相似$,但不受处理多行选项的影响)
复制代码

在 js 中,g flag 会影响 String.prototype.match()RegExp.prototype.exec() 的行为

String.prototype.match() 中,返回数据的格式会不同,加 g 会返回数组,不加 g 则返回比较详细的信息

> 'hello hell'.match(/h(.*?)\b/g)
[ 'hello', 'hell' ]

> 'hello hell'.match(/h(.*?)\b/)
[ 'hello',
  'ello',
  index: 0,
  input: 'hello hell' ]

复制代码

RegExp.prototype.exec() 中,加 g 以后,若是你的正则不是字面量的正则,而是存储在变量中的话,特么的这个变量就会变得有记忆!!

> /h(.*?)\b/g.exec('hello hell')
[ 'hello',
  'ello',
  index: 0,
  input: 'hello hell' ]
> /h(.*?)\b/g.exec('hello hell')
[ 'hello',
  'ello',
  index: 0,
  input: 'hello hell' ]

> var re = /h(.*?)\b/g;
undefined
> re.exec('hello hell')
[ 'hello',
  'ello',
  index: 0,
  input: 'hello hell' ]
> re.exec('hello hell')
[ 'hell',
  'ell',
  index: 6,
  input: 'hello hell' ]
>
复制代码

第三,你们知道,. 是不能够匹配 \n 的。若是咱们想匹配的数据涉及到了跨行,好比下面这样的。

var multiline = require('multiline');
var text = multiline.stripIndent(function () {
/*
    head
    ```
    code code2 .code3```
    ```
    foot
*/
});
复制代码

若是咱们想把两个 ``` 中包含的内容取出来,应该怎么办?直接用 . 匹配不到 \n,因此咱们须要找到一个原子,能匹配包括 \n 在内的全部字符。这个原子的惯用写法就是 [\s\S]

var match1 = text.match(/^```[\s\S]+?^```/gm);
console.log(match1) // => [ '```\ncode code2 code3```\n```' ]

// 这里有一种很骚的写法,[^] 与 [\s\S] 等价
var match2 = text.match(/^```[^]+?^```/gm)
console.log(match2) // => [ '```\ncode code2 .code3```\n```' ]
复制代码

var let

内部函数能够访问外部函数的变量,外部函数不能访问内部函数变量。 若是不用var来声明,则默认为全局变量了,以下面value值。let 为es6的块级做用域。

var parent = function () {
    var name = "parent_name";
    var age = 13;

    var child = function () {
        var name = "child_name";
        var childAge = 0.3;

        // => child_name 13 0.3
        console.log(name, age, childAge);
    };

    child();

    // will throw Error
    // ReferenceError: childAge is not defined
    console.log(name, age, childAge);
};

parent();

function foo() {
    value = "hello";
}
foo();
console.log(value); // 输出hello
console.log(global.value) // 输出hello

// js 中,函数中声明的变量在整个函数中都有定义。好比以下代码段,变量 i 和 value 虽然是在 for 循环代码块中被定义,但在代码块外仍能够访问 i 和 value。

function foo() {
    for (var i = 0; i < 10; i++) {
        var value = "hello world";
    }
    console.log(i); //输出10
    console.log(value);//输出hello world
}
foo();
复制代码

闭包

闭包这个概念,在函数式编程里很常见,简单的说,就是使内部函数能够访问定义在外部函数中的变量。

var adder = (x) => {
    let base = x;
    return (n) => {
        return base + n;
    };
};

var add10 = adder(10);
console.log(add10(5));
// 每次调用 adder 时,adder 都会返回一个函数给咱们。咱们传给 adder 的值,会保存在一个名为 base 的变量中。因为返回的函数在其中引用了 base 的值,因而 base 的引用计数被 +1。当返回函数不被垃圾回收时,则 base 也会一直存在。若是想深刻理解这块,能够看看这篇 http://coolshell.cn/articles/6731.html

// 闭包的一个坑
for (var i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i);
    }, 5);
}
// 上面这个代码块会打印五个 5 出来,而咱们预想的结果是打印 0 1 2 3 4. 之因此会这样,是由于 setTimeout 中的 i 是对外层 i 的引用。当 setTimeout 的代码被解释的时候,运行时只是记录了 i 的引用,而不是值。而当 setTimeout 被触发时,五个 setTimeout 中的 i 同时被取值,因为它们都指向了外层的同一个 i,而那个 i 的值在迭代完成时为 5,因此打印了五次 5。 为了获得咱们预想的结果,咱们能够把 i 赋值成一个局部的变量,从而摆脱外层迭代的影响。

for (var i = 0; i < 5; i++) {
    (function (idx) {
        setTimeout(function () {
            console.log(idx);
        }, 5);
    })(i);
}

复制代码

this

在函数执行时,this 老是指向调用该函数的对象。要判断 this 的指向,其实就是判断 this 所在的函数属于谁。

在《javaScript语言精粹》这本书中,把 this 出现的场景分为四类,简单的说就是:

  • 有对象就指向调用对象
  • 没调用对象就指向全局对象
  • 用new构造就指向新对象
  • 经过 apply 或 call 或 bind 来改变 this 的所指。

1)函数有所属对象时:指向所属对象

函数有所属对象时,一般经过 . 表达式调用,这时 this 天然指向所属对象。好比下面的例子:

var myObject = {value: 100};
myObject.getValue = function () {
  console.log(this.value);  // 输出 100

  // 输出 { value: 100, getValue: [Function] },
  // 其实就是 myObject 对象自己
  console.log(this);

  return this.value;
};

console.log(myObject.getValue()); // => 100
复制代码

getValue() 属于对象 myObject,并由 myOjbect 进行 . 调用,所以 this 指向对象 myObject

  1. 函数没有所属对象:指向全局对象
var myObject = {value: 100};
myObject.getValue = function () {
  var foo = function () {
    console.log(this.value) // => undefined
    console.log(this);// 输出全局对象 global
  };

  foo();

  return this.value;
};

console.log(myObject.getValue()); // => 100
复制代码

在上述代码块中,foo 函数虽然定义在 getValue 的函数体内,但实际上它既不属于 getValue 也不属于 myObjectfoo 并无被绑定在任何对象上,因此当调用时,它的 this 指针指向了全局对象 global

听说这是个设计错误。

3)构造器中的 this:指向新对象

js 中,咱们经过 new 关键词来调用构造函数,此时 this 会绑定在该新对象上。


var SomeClass = function(){
  this.value = 100;
}

var myCreate = new SomeClass();

console.log(myCreate.value); // 输出100
复制代码

顺便说一句,在 js 中,构造函数、普通函数、对象方法、闭包,这四者没有明确界线。界线都在人的心中。

  1. apply 和 call 调用以及 bind 绑定:指向绑定的对象

apply() 方法接受两个参数第一个是函数运行的做用域,另一个是一个参数数组(arguments)。

call() 方法第一个参数的意义与 apply() 方法相同,只是其余的参数须要一个个列举出来。

简单来讲,call 的方式更接近咱们平时调用函数,而 apply 须要咱们传递 Array 形式的数组给它。它们是能够互相转换的。

var myObject = {value: 100};

var foo = function(){
  console.log(this);
};

foo(); // 全局变量 global
foo.apply(myObject); // { value: 100 }
foo.call(myObject); // { value: 100 }

var newFoo = foo.bind(myObject);
newFoo(); // { value: 100 }
复制代码

线上部署:heroku

github.com/Ricardo-Li/… (这个项目已经被删了。参照 github.com/alsotang/no… 的代码本身操做一下吧。)这个项目部署上 heroku,成为一个线上项目

我部署的在这里 serene-falls-9294.herokuapp.com

  1. 学习 heroku 的线上部署

什么是 heroku

heroku 是弄 ruby 的 paas 起家,如今支持多种语言环境,更甚的是它强大的 add-on 服务。 paas 平台相信你们都不陌生。Google 有 gae,国内新浪有 sae。paas 平台相对 vps 来讲,不须要你配置服务器,不须要装数据库,也不须要理会负载均衡。这一切均可以在平台上直接获取。你只要专一本身的业务,把应用的逻辑写好,而后发布上去,应用天然就上线了。数据库方面,若是你用 mysql,那么你能够从平台商那里获得一个 mysql 的地址、帐号和密码,直接链接就能用。若是应用的流量增大,须要横向拓展,则只用去到 paas 平台的管理页面,增大服务器实例的数量便可,负载均衡会自动帮你完成。

提及来,我之因此对于 web 开发产生兴趣也是由于当年 gae 的关系。那时候除了 gae 以外,没有别的 paas 平台,gae 是横空出世的。有款翻墙的软件,叫 gappproxy(code.google.com/p/gappproxy… )——能够认为是 goagent 的前身——就是搭建在 gae 上面的,不只快,并且免费。因而我就很想弄懂这样一个程序是如何开发的。好在 gappproxy 是开源的,因而我下了源码来看,那时候才大一,只学过 c,看到那些 python 代码就凌乱了。因而转头也去学 python,后来渐渐发现了 web 开发的乐趣,因而 ruby 和 node.js 也碰碰。后来 goagent 火起来了,我又去看了看它的代码,发现很是难看,就本身写了个 github.com/alsotang/ke… 。不过如今回想起来,仍是 goagent 的实现比较稳定以及效率高。

heroku 的免费额度仍是足够的,对于 demo 应用来讲,放上去是绰绰有余的。各位搞 web 开发的大学生朋友,必定要试着让你开发的项目尽量早地去线上跑,这样你的项目能够被其余人看到,可以促使你更有热情地进行进一步开发。这回咱们放的是 cnode 社区的爬虫上去,你其实能够试着为大家学院或者学校的新闻站点写个爬虫,提供 json api,而后去申请个微信公共平台,天天推送学院网站的新闻。这东西辅导员是有需求的,能够作个给他们用。

好了,咱们先 clone github.com/Ricardo-Li/… 这个项目。因为咱们这回讲部署,因此代码就用现成的了,代码的内容就是 lesson 3(github.com/alsotang/no… ) 里面的那个爬虫。

clone 下来之后,咱们去看看代码。代码中有两个特殊的地方,

一个是一个叫 Procfile 的文件,内容是:

web: node app.js
复制代码

一个是 app.js 里面,

app.listen(process.env.PORT || 5000);
复制代码

这二者都是为了部署 heroku 所作的。

你们有没有想过,当部署一个应用上 paas 平台之后,paas 要为咱们干些什么?

首先,平台要有咱们语言的运行时;

而后,对于 node.js 来讲,它要帮咱们安装 package.json 里面的依赖;

而后呢?而后须要启动咱们的项目;

而后把外界的流量导入咱们的项目,让咱们的项目提供服务。

上面那两处特殊的地方,一个是启动项目的,一个是导流量的。

heroku 虽然能推测出你的应用是 node.js 应用,但它不懂你的主程序是哪一个,因此咱们提供了 Procfile 来指导它启动咱们的程序。

而咱们的程序,原本是监听 5000 端口的,可是 heroku 并不知道。固然,你也能够在 Procfile 中告诉 heroku,可若是你们都监听 5000 端口,这时候不就有冲突了吗?因此这个地方,heroku 使用了主动的策略,主动提供一个环境变量 process.env.PORT 来供咱们监听。

这样的话,一个简单 app 的配置就完成了。

咱们去 www.heroku.com/ 申请个帐号,而后下载它的工具包 toolbelt.heroku.com/ ,而后再在命令行里面,经过 heroku login 来登陆。

上述步骤完成后,咱们进入 node-practice-2 的目录,执行 heroku create。这时候,heroku 会为咱们随机取一个应用名字,并提供一个 git 仓库给咱们。

接着,往 heroku 这个远端地址推送咱们的 master 分支:

heroku 会自动检测出咱们是 node.js 程序,并安装依赖,而后按照 Procfile 进行启动。

push 完成后,在命令键入 heroku open,则 heroku 会自动打开浏览器带咱们去到相应的网址:

到此课程也就结束了。

随便聊聊 heroku 的 addon 吧。这个 addon 确实是个神奇的东西,反正在 heroku 以外我还没怎么见到这类概念。这些 addon 提供商,有些提供 redis 的服务,有些提供 mongodb,有些提供 mysql。你能够直接在 heroku 上面进行购买,而后 heroku 就会提供一段相应服务的地址和帐号密码给你用来链接。

你们能够去 addons.heroku.com/ 这个页面看看,玲琅满目各类应用能够方便接入。之因此这类服务有市场,也是由于亚马逊的 aws 很是牛逼。为何这么说呢,由于网络速度啊。若是如今在国内,你在 ucloud 买个主机,而后用个阿里云的 rds,那么应用的响应速度会由于 mysql 链接的问题卡得动不了。但在 heroku 这里,提供商们,包括 heroku 本身,都是构建在 aws 上面,这样一来,各类服务的互通其实走的是内网,速度很能够接受,因而各类 addon 提供商就作起来了。

国内的话,其实在阿里云上面也能够考虑这么搞一搞。

$ brew tap heroku/brew && brew install heroku
$ heroku login -i
    heroku: Enter your login credentials
    Email: me@example.com
    Password: ***************
    Two-factor code: ********
    Logged in as me@heroku.com
# CLI会保存您的电子邮件地址和API令牌以~/.netrc供未来使用
$ cd ~/myapp
$ heroku create
    Creating app... done, ⬢ arcane-gorge-86957
    https://arcane-gorge-86957.herokuapp.com/ | https://git.heroku.com/arcane-gorge-86957.git
复制代码

dashboard.heroku.com/apps/arcane…

# 初始化并关联
$ cd lesson
$ git init
$ heroku login # 加 -i 就不打开web登录了
$ heroku git:remote -a arcane-gorge-86957
$ git add .
$ git commit -am 'first commit'
$ git push heroku master
$ heroku open

# 本地clone修改
$ heroku login
$ heroku git:clone -a arcane-gorge-86957
$ cd arcane-gorge-86957
$ git add .
$ git commit -am "make it better"
$ git push heroku master
复制代码

mongo 添加用户

$ mongo
$ db.createUser({
  user:"admin",
  pwd:"admin",
  roles:[{
      role:"userAdminAnyDatabase",
      db:"admin"
  }]
});
复制代码

导入数据:

$ mongoimport --db blog --collotion blog --file ~/Download/blog/aa.json
$ mongorestore -d blog ~/Downloads/blog/
复制代码