0%

在大公司待久了,很难保持以前那种高强度的学习。人总是有惰性的,一旦在一个安逸的环境待的时间稍微久点,心里就会变得很享受,不再有紧张感,也就不再有动力,动机去一直提升自己。

大部分同学每晚上班到8、9点才回家,回家已经精疲力竭了,吃吃喝喝,看点知乎、微信,最新电影,刷刷微博,抖音,时间已经到11点了。大部分人的时间,都是这样过的了。这也就是为什么入职场之后,很多人的进步会变得更加慢。

虽然嘴巴上口口声声说着要终身成长,但是,身体却很享受这样安逸的生活。
从某种程度上来讲,这样倒也无可厚非,人生的意义各有各的定义,不是每个人的目标都必须整齐划一的。

然而这种快乐,大多数时候是不可持续的,这可能就是这种生活方式最大的问题了。如果你有5千万,那不上班肯定没问题,如果你有北上广深几套房,那你上班混吃等死也没人说你。然而,绝大多数的我们,还是得跟绝大多数跟我们一样的,几乎是无产阶级的同行们竞争。 高考还是一年一年的持续,廉价劳动力一年一年的减少,数量还是大的你难以想象,大厂就那么几个,想吃这口饭的人,多的超乎你想象,他们起点之低/之高,负担之小/之大,到远远超乎你想象。

如果纯粹走竞争路线的话,而不是去找到乐趣,好好享受的话,这也太难了,我不相信有人能坚持到底。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const mapLimit = (tasks, concurrency, debug, callback) => {
let running = 0;
let completed = 0;
let index = 0;
let next = () => {
while (completed < tasks.length && running < concurrency) {
let task = tasks[index++];
task(() => {
running--;
completed++;
if (completed == tasks.length) {
return callback()
}
next();
})
running++;
}
}
next()
}

const chainAsync = (tasks) => {
let index = 0;
let last = tasks[tasks.length - 1];
const next = () => {
let fn = tasks[index++];
fn === last ? fn() : fn(next);
};
next();
}
阅读全文 »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
const http = require('http')
const Emitter = require('events')


class WebServer extends Emitter{
constructor(){
super()
this.middlewares = [];
this.context = Object.create(null);
}
use(fn){
if(typeof(fn) === 'function'){
this.middlewares.push(fn)
}
}
listen(...args){
let server = http.createServer(this.callback())
server.listen(...args);
}
callback(){
let that = this;
const handleRequest = (req,res) => {
let context = that.createContext(req,res);

/* 管道执行中间件方式之一[开始] */
// let idx = 0;
// let next = (idx) => {
// if(idx == that.middlewares.length) return context.res.end();
// let fn = that.middlewares[idx];
// idx++
// fn(context)
// next(idx)
// }
// return next(0)
/* 管道执行中间件方式之一[结束] */
/* 管道执行中间件方式之二[开始] */
let start = process.hrtime();
this.middlewares.forEach((fn,idx) =>{
try{
fn(context)
}catch(err){
this.onerror(err)
}
if(idx >= this.middlewares.length - 1){
let end = process.hrtime();
context.res.write(`${(end[0]-start[0])}s,${(end[1]-start[1])/1e6}ms,`)
context.res.end()
}
})
/* 管道执行中间件方式之二[结束] */
}
return handleRequest
}
onerror(err){
console.log(err)
}
createContext(req,res){
this.context.req = req;
this.context.res = res;
return this.context
}
}

var app = new WebServer();
var PORT = 3001;

app.use(ctx => {
ctx.res.write('<p>line 1</p>');
});

app.use(ctx => {
ctx.res.write('<p>line 2</p>');
});

app.use(ctx => {
ctx.res.write('<p>line 3</p>');
});

app.listen(PORT, () => {
console.log(`the web server is starting at port ${PORT}`);
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
const PENDING = Symbol();
const FULFILLED = Symbol();
const REJECTED = Symbol();

function Promisee(fn){
if(typeof fn != 'function'){
throw new Error('resolver must be a function');
}
let state = PENDING;
let value = null;
let handler = [];

function fulfill(result){
state = FULFILLED;
value = result;
handler.forEach(next)
handler = []
}
function reject(err){
state = REJECTED;
value = err;
handler.forEach(next)
handler = []
}
function resolve(result){
try{
let then = typeof result.then === 'function'? result.then:null
if(then){
then.bind(result)(resolve,reject)
return
}
fulfill(result)
} catch(err){
reject(err)
}
}
function next({onFulfill,onReject}){
switch(state){
case FULFILLED:
onFulfill && onFulfill(value);
break;
case REJECTED:
onReject && typeof(onReject) == 'function' && onReject(value);
break;
case PENDING:
handler.push({ onFulfill, onReject })
}
}
this.then = function(onFulfill,onReject){
return new Promisee((resolve,reject)=>{
next({
onFulfill: (val) => {
resolve(onFulfill(val))
},
onReject: (err) =>{
reject(onReject(err))
}
})
})
}
this.catch = (reject)=>{
if(state === REJECTED){
reject(value)
}
return this;
}
fn(resolve,reject)
}

function sleep(sec){
return new Promisee((resolve,reject) => {
setTimeout(() => resolve(sec),sec * 1000)
})
}

var p = new Promisee((r,j)=>{
r('resolve')
})
p.then((val)=>{
console.log(val)
return sleep(1)
})
.then(val=>{
console.log(val)
return sleep(val + 1)
})
.then(val=>{
console.log(val)
return sleep(val + 1)
})
.then(console.log)

.catch(err=>console.log('err:',err))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal {
constructor(){
console.log('Animal......')
}
}

class Dog extends Animal{
constructor(){
super();//必须调用父类构造函数
console.log("Dog......")
}
}

var d = new Dog();//Animal......,Dog......

简单模拟Object.create

1
2
3
4
5
Object.create = Object.create || function(obj){
var F = function(){}
F,prototype = obj;
return new F()
}

1.工厂模式

无法识别类型,无法复用函数,属性

1
2
3
4
5
6
7
8
9
10
11
12
function createPerson(name,age,job){
var o = {};
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
return o.name
}
return o
}

var conan = createPerson('conan',25,'ceo')

2.构造函数

构造函数内的函数,不能复用,都是不一样的函数

1
2
3
4
5
6
7
8
9
10
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
return this.name
}
}

var conan = new Person('conan',35,'ceo')

3.构造函数 + 原型模式

算是比较符合js设计原则的OOP模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
}

Person.prototype.sayName = function () {
return this.name
}

var conan = new Person('conan',35,'ceo')

Person.prototype = {
sayName(){
return this.name
},
sayJob(){
return this.job
},
constructor:Person
}


(构造)函数 都有一个prototype属性,指向原型对象,
原型对象都有一个constructor属性指向构造函数,
实例都有一个指向原型对象的指针proto

_继承_


原型链的继承

包含引用类型值的原型会被共享,class 方案解决了这个问题,besides,子类不能像父类构造函数传值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function SuperType(){
this.subs = []
}

function ChildType(){}

ChildType.prototype = new SuperType();
ChildType.prototype.constructor = ChildType;

class SuperType{
constructor(){
this.subs = [1,2,3]
}
}

class SubType extends SuperType{

}

var sub1 = new SubType();
var sub2 = new SubType();

借用构造函数

(可以调用父类的构造函数),复用是个问题

1
2
3
4
5
6
7
8
function SuperType(){
this.subs = ['old'];
}

function SubType(){
SubType.call(this)
this.newSubs = ['new'];
}

组合继承

(能够复用,能够调用父类构造函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function SuperType(name){
this.name = name;
this.subs = [1,2,3]
}

SuperType.prototype.SupersayName = function(){
return this.name
}

function SubType(name,age){
SuperType.call(this,name)
this.age = age
}

SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType

原型式继承(子类和父类引用属性共享)

1
2
3
4
5
function create(obj){
function F(){}
F.prototype = obj;
return new F();
}

寄生式继承

1
2
3
4
5
6
7
function createAnother(obj){
var newObj = create(obj);
newObj.sayName = function () {
return 'lalala...'
}
return newObj;
}

寄生组合式继承

通过构造函数来继承属性,通过原型链来继承方法)这个最优,保持函数,属性复用,能调用父类构造函数,只执行一次父类构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
function inherit(subType,SuperType){
var proto = crate(SuperType.prototype)
proto.constructor = subType;
subType.prototype = proto;
}

*bind*

Function.prototype.bind = function(context){
var self = this;
return function(){
return self.apply(context,arguments)
}
}

*复杂版bind*

Function.prototype.bind = function(){
var that = this;
var args = [...arguments]
var context = args.shift()
return function(){
return that.apply(context,args.concat([...arguments]))
}
}

function a(){
return this.a
}

var b = {a:333}

*判断类型*

var isArray = function(arr){
return Object.prototype.toString.call(arr) === '[object Array]'
}

var isType = function(type){
return function(ele){
return Object.prototype.toString.call(ele) === `[object ${type}]`
}
}

var isArray = isType("Array");
isArray([]);//true

*单例*

var getSingle = function(fn){
var ret
return function(){
return ret || ret = fn.call(this,...arguments)
}
}

*分时处理函数*

var timeChunk = function(arr,fn,count){
var t
var start = function(){
for(var a=0;a<Math.min(count||1,arr.length);a++){
fn(arr.shift())
}
}
return function(){
t = setInterval(()=>{
if(arr.length <= 0){
clearInterval(t)
}
start()
},500)
}
}

单例模式

1
2
3
4
5
6
var getSingle = function(fn){
var res;
return function(){
return res || (res = fn.apply(this,arguments))
}
}

策略模式

迭代器模式

1
2
3
4
5
var each = function(arr,callback){
for(let a = 0; a < arr.length; a++){
callback.call(arr[a],i,arr[a]);//传递下标和元素本身
}
}

发布 - 订阅模式 (时间,空间上的解耦)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
var Subscriber = {};

// Subscriber.subs = [];
Subscriber.subs = {};

Subscriber.listen = function(key,fn){
if(!this.subs[key]){
this.subs[key] = []
}
this.subs[key].push(fn)
}

Subscriber.trigger = function(){
var key = [...arguments][0]
if(this.subs[key].length === 0){
return
}
for(let a of this.subs[key]){
a.apply(this,arguments)
}
}

//- - - - - - 修改完善

var event = {
subs : {},
on: function(type,fn){
if(!this.subs[type]){
this.subs[type] = []
}
this.subs[type].push(fn)
},
emit: function(){
var args = [...arguments]
var type = args.shift()
var that = this
if(this.subs[type].length === 0){
return
}
this.subs[type].forEach(fn=>{
fn.apply(that,args)
})
},
off:function(type,fn){
var types = this.subs[type];
if(!types){
return false
}
if(!fn){
types && (types.length = 0)
}else{
for(let _fn of types){
if(_fn === fn){
types.splice(types.indexOf(_fn),1)
}
}
}
}
}

ES6 class 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Event {
constructor(){
this.subs = {}
}
on(type,fn){
if(!this.subs[type]){
this.subs[type] = [];
}
this.subs[type].push(fn)
}
emit(){
var args = [...arguments]
var type = args.shift();
var that = this;
if(!type){
return false
}
this.subs[type].forEach(fn=>{
fn.apply(that,args)
})
}
off(type,fn){
var fns = this.subs[type]
if(!fns){
return false
}
if(!fn){
fns.length = 0
}else{
for(let _fn of fns){
if(_fn === fn){
fns.splice(fns.indexOf(_fn),1)
}
}
}
}
}

关于继发异步

1.两种方案Promise和async/await

2.Promise方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let queue = [p1,p2,p3,p4,p5]//5个Promise

let seq = Promise.resolve();
queue.forEach(ele=>{
seq = seq.then(res=>{
console.log(res)
return res;
})
});
可以看到输出了4个promise的结果,第五个promise的成功回调调用
seq.then(res=>console.log(res))就可以看到

同样也可使用数组的reduce方式实现
let queue = [p1,p2,p3,p4,p5]//5个Promise

queue.reduce((prev,next)=>{//如果有第二个参数,prev的值就为这个参数
return prev = prev.then(res=>{
console.log(res);
return next
})
},Promise.resolve())

3.async/await方案
着一种方案用的比较多一点,这是最新ES7的用法,大大简化了异步处理流程

1
2
3
4
5
;(async ()=>{
let queue = [p1,p2,p3,p4,p5];//promise 数组
for(let a of queue)
await a
})();

不借用其他库,nodejs原生模拟登录伯乐在线

思路: http原生有get和request方法可以构造GET和POST请求,我们首先分析网站登录入口,然后用自己的用户名,密码等一些必要信息(有事还会有hidden的input,他们大部分都是来防范跨站请求伪造的)自己模拟数据,然后发送请求,获取网站给你的cookie,成功登录之后,你就能为所欲为了。

直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
let http = require('http');


//构造请求参数
let options = {
hostname:'www.jobbole.com',
method:'POST',
path:'/wp-admin/admin-ajax.php', //为什么这么像wordpress的用户台。。后来看了一下,jobbole用了很多wordpress模块,还有好多的jQuery
headers:{
'Content-Type':'application/x-www-form-urlencoded',//表单格式
'User-Agent':'Mozilla/5.0 (Macintosh)',//浏览器头
}
}
//jobbole登录用的是ajax登录,然后登录成功之后再location.href='http://www.jobbole.com/'跳到首页,感觉有点多此一举。

//这是ajax POST需要的数据
let postData = {
action: 'user_login',
user_login:'你的用户名',
user_pass:'你的密码',
remember_me:1,//0 是不记住登录
redirect_url:'http://www.jobbole.com/'
};

构造请求函数
let request = http.request(options,(res)=>{
res.setEncoding('utf-8');//网站响应的res是Buffer,这里我们将它设置为utf-8格式
console.log(`状态码:${res.statusCode}`);
let cookies = res.headers['Set-Cookie'];//这个就是关键了,能获取到网站返回响应的Set-Cookie头;
let body = '';//res是可读流,我们通过绑定data事件的方式,获取每次data触发传过来的chunk
res.on('data',(chunk)=>{
body += chunk;
});
res.on('end',()=>{
console.log('响应结束啦...');//body就是响应拼接起来的字符串,如果需要json格式,直接JSON.parse就获取到了.
visitHome(cookies);//在这里我们获取到的cookie去登录网站主页,
})
}) ;
request.write(querystring.stringify(postDate));//写入需要post的数据
request.end();//request需要调用end才能发送请求,get方式就不用
function visitHome(cookies){
let options = {
hostname:'www.jobbole.com',
path:'/',//访问首页
headers:{
'cookie':cookies.join()//由于cookies是数组形式,所以我们将它转换为字符串
}
};
//这次我们直接用GET请求
http.get(options,(res)=>{
let html = '';
res.on('data',(chunk)=>{
html+=chunk;
});
res.on('end',()=>{
console.log(html);//这就是我们获取到的主页啦,如果不出意外,现在就已经是登录的状态啦,以后你发送请求,都加上这个cookie,你就能享受到登录之后的用户权限了
})
})
}

##完。

以太坊区块信息保存(获取)

用docker 搭建了一个以太坊的轻节点

1
2
3
4
5
//拉取以太坊docker镜像
docker pull ethereum/client-go
//docker运行以太坊节点(以轻节点的模式运行--syncmode "light")
docker run -d --name geth-node -v $HOME/geth-node:/root -p 8454:8545 -p 30303:30303 ethreum/client-go --rpc --syncmode "light"
//docker logs geth-node 可以查看容器日志,轻节点只下载区块头,在需要的时候才回去下载特定的区块数据,所以同步起来非常快,几乎几分钟就能到最新的节点了

搭建好节点之后在本地新建项目,安装依赖

1
2
3
npm i -S web3
npm i -S mongoose
npm i -S ioredis

一个循环抓取想要的数据

app.js 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
let Web3 = require('web3');
let url = 'http://127.0.0.1:8545';
let mongoose = require('mongoose');
let Redis = require('ioredis');
let redis = new Redis();
mongoose.connect('mongodb://127.0.0.1:27017/eth',{ useNewUrlParser: true });
let ethSchema = new mongoose.Schema({
difficulty: String,
extraData: String,
gasLimit: Number,
gasUsed: Number,
hash: String,
logsBloom: String,
miner: String,
mixHash: String,
nonce: String,
number: Number,
parentHash: String,
receiptsRoot: String,
sha3Uncles: String,
size: Number,
stateRoot: String,
timestamp: Number,
totalDifficulty: String,
transactions: [String],
transactionsRoot: String,
uncles: [String]
});
let ethmd = mongoose.model('ethmd',ethSchema);

web3 = new Web3(new Web3.providers.HttpProvider(url));

!(fetch = async () =>{
//这里只获取了交易量比较多的500w到600w的块
for(let a = 5000000; a < 6000000; a++){
let curBlock = await redis.get('currentblock');
console.log('curBlock:',curBlock);
if(curBlock && curBlock > a ) {
a = curBlock;
console.log('skip block to:',curBlock)
continue;
}
await new Promise ((resolve,reject) => {
web3.eth.getBlock (a,(err,data) => {
if(err){
console.log(err);
console.log(`retry....`);
setTimeout(()=>{
fetch();
},12000)
}else{
console.log(data);
ethmd.find({number:a},(err,doc)=>{
if(err) return console.log(err)
if(doc.length!==0){
console.log('block ',a,' exists.');
resolve()
}else{
new ethmd({
difficulty: data.difficulty,
extraData: data.extraData,
gasLimit: data.gasLimit,
gasUsed: data.gasUsed,
hash: data.hash,
logsBloom: data.logsBloom,
miner: data.miner,
mixHash: data.mixHash,
nonce: data.nonce,
number: data.number,
parentHash: data.parentHash,
receiptsRoot: data.receiptsRoot,
sha3Uncles: data.sha3Uncles,
size: data.size,
stateRoot: data.stateRoot,
timestamp: data.timestamp,
totalDifficulty: data.totalDifficulty,
transactions: data.transactions,
transactionsRoot: data.transactionsRoot,
uncles: data.uncles
}).save((err,doc)=>{
if(err) return console.log(err)
console.log(`block ${a} saved.`);
redis.set('currentblock',a,(err,doc)=>{
console.log('update currentblock to: ',a)
if(err) return console.log(err)
resolve();
});
});
}
})
}
})
})
}
process.exit(0);
})();

简单起见,所有的内容都在app.js里边,做了最简单一个错误处理,当然mongodb和redis的操作也可能产生错误,如果想程序更加健壮可以把这部分内容加上.
当然这样跑还是太慢了,主要就是获取区块的速度太慢了,可以考虑用全节点模式,那样节点数据就在本地,而不用从网络获取,另外也可以多跑几个实例,分别跑不同的区段然后汇总到数据库

将你的Vue应用部署在Docker环境中

8012年,你不得不会的技能,将你的app部署在docker环境中

这篇文章不是docker教程,内容假设你对docker有一定的了解,如果你还不知道docker是什么,docker的基本操作,点我,小半天就能入门基础操作。通过vue-cli脚手架工具,将Vue项目搭建起来之后……

在项目根目录下创建Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM node:9.11.1-alpine
# 安装一个简单的http静态文件服务器
RUN npm install -g http-server
# 设置 'app' 目录为工作目录
WORKDIR /app
# 复制package.json以及package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm install
# 复制项目文件到当前工作目录
COPY . .
# 构建项目
RUN npm run build
# 对外暴露8080端口
EXPOSE 8080
# 执行http-server
CMD ["http-server","dist"]

开始构建项目

docker build -t my-project/my-dockerize-vuejs-app-0.2.3 .

最后,我们就可以在docker中运行我们的Vue.js项目了

docker run -it -p 8080:8080 --rm --name dockerize-vuejs-app-0.2.3 my-project/my-dockerize-vuejs-app-0.2.3

我们上边的项目中用到了http-server,这用在原型开发,或者用户量非常小的情况下还是比较适用的,然而实际生产中,我们还是要借助巨人的肩膀,来让我们的应用更加健壮。

结合nginx,用docker部署Vue应用

编写Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# build阶段 docker在17.05版本之后出的多阶段构建(解决构建臃肿问题)
FROM node:9.11.1-apline as build-stage
# 设置工作目录
WORKDIR /app
# 复制package.json以及package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm install
# 复制项目文件到当前工作目录
COPY . .
# 构建项目
RUN npm run build

# 生产阶段(用nginx简洁版镜像)
from nginx:1.13.12-alpine as production-stage
# 直接复制构建阶段生成在dist的代码到nginx的html目录下
COPY --from=build-stage /app/dist /usr/share/nginx/html
# 对外暴露nginx端口
EXPOSE 80
# 运行nginx
CMD ["nginx", "-g", "daemon off;"]

构建镜像
docker build -t my-project/my-dockerize-vuejs-app-with-nginx-0.1.1 .

在docker容器中运行我们的app
docker run -it -p 8080:80 --rm --name dockerize-vuejs-app-with-nginx-0.1.1 my-project/my-dockerize-vuejs-app-with-nginx-0.1.1

好了,现在你可以在localhost:8080查看你的Vue.js 应用了

[完]