angular2+ 没有取消订阅带来的性能问题

性能优化及注意事项

  • 所有订阅必须在组件销毁时取消订阅,否则该订阅将始终穿插在项目的个整个生命周期中

查询命令

1
grep -inr "subscribe" src

取消订阅

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
import { Component, OnDestroy } from '@angular/core';
@Component({
// ...
})
export class YourComponent extends OnDestroy {
// ....
events:Array<any> = [];
constructor(){
// example
const mySubscribe = this.data.subscribe(
// ....
);
this.events.push( mySubscribe );
}
ngOnDestroy(){
this.events.map((x) => x.unsubscribe());
}
}

angular2+ 结合 redux 的一个方案

angular2+ 结合 redux 的一个方案

感觉ngrx使用注入的方式调用redux 脱离了redux的初衷, 使用订阅方式获取状态,强制了异步操作, 个人不喜欢

虽然和ng2内置的service逻辑一样, 但是让组件显得复杂起来了, 异步与异步间的数据同时展示时无法确定时间点

自己写个方案, 模仿 react-redux api

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
// store.ts
import { createStore, combineReducers } from 'redux';
import { Map, is } from 'immutable';
// 测试用type
const SET_NAME = 'SET NAME';
// 测试用 action
export const setName = (payload) => {
return { type: SET_NAME, payload };
}
// 测试用 reducer
const user = (state = Map({ username: 'Redux Username' }), action) => {
switch (action.type) {
case SET_NAME:
return state.set('username', action.payload.username)
default:
return state;
}
}
const reducers = combineReducers({
user
});
// 生成store
const store = createStore(reducers);
const props = data => ({});
// mapStateToProps: state => ({}) ; 可以直接获取全局状态
// mapDispatchToProps: dispatch => ({}) ; 可以直接调用dispath
// 组件中调用方式为: this['username'] // demo * 不能直接写this.username 因为并没有在组件中定义该属性
export const Connect = (mapStateToProps = props, mapDispathToProps = props) => target => {
const state = store.getState();
let mstp = mapStateToProps(state);
let mdtp = mapDispathToProps(store.dispatch);
const sd = Map(mstp).merge(mdtp);
sd.map((value: any, key: string) => {
Reflect.defineProperty(target.prototype, key, { value, writable: true });
});
const unsubscribe = store.subscribe(() => {
if (!target) {
unsubscribe();
return;
}
const currentState = store.getState();
const cmstp = mapStateToProps(currentState);
if (!is(mstp, cmstp)) {
Map(cmstp).map((value, key) => {
Reflect.set(target.prototype, key, value);
});
mstp = cmstp;
}
});
return target;
}

使用方式

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
import { Connect, setName } from '../store';
@Connect(state => ({
username: state.user.get('username')
}), dispatch => ({
setName: username => dispatch(setName({ username }))
}))
@Component({
template: `
<h1>{{fullname}}</h1>
<input type="text" autocomplete="off" class="form-control" [placeholder]="'用户账号'" [(ngModel)]="loginInfo.userId" />
<button type="button" class="btn btn-primary mb-1" (click)="login()">登录</button>
`,
styleUrls: ['./login.component.css']
})
export class LoginComponent {
public loginInfo: LoginInfo;
// 当需要和实例属性结合的时候,使用getter, 保证模块更新
get fullname() {
return 'hello ' + this['username'];
}
public login(): void {
if (this.loginInfo.userId)
this['setName'](this.loginInfo.userId);
}
}

如何做短链接服务

微博短链接服务

微博短链接服务,根据链接通过算法生成短链接,从数据库索引中查找是否已经存在该短链接,然后生成或返回该短链接

谷歌短链接服务

每次请求都随机生成短链接

正确姿势

个人认为, 谷歌的短链接服务性能强悍, 生成服务中逻辑简单, 性能最好。 而微博的短链接服务追求零冗余, 性能上用服务器来补充。 对于自用或中小型企业还是推荐用谷歌短链服务逻辑

写一个管道处理函数

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
export let pipe = (data, filters) => {
(typeof filters === 'function' ? [filters] : filters).map(
filter =>
data = typeof filter === 'string' ? pipe[filter](data) : filter(data)
);
return data;
};
// demo1
pipe.numeric = data => {
if (typeof data === 'string') {
data = data.replace(/[^\d]/g, '');
return Number.parseInt(data);
} else return data;
};
// demo2
pipe.toArray = data => {
data = `${data}`;
const arr = [];
for (let i = 0; i < data.length; i++) {
arr.push(data.at(i));
}
return arr;
};
const a = pipe('happy 2016', [
'numeric',
'toArray',
d => d.toString().replace(/,/g, '-')
]);
console.log(a); // 2-0-1-6

js 方法参数的传递方式

很惭愧, 从来没有从头开始学过javascript, 越想深入暴露的问题越多

js方法参数的是按值传递的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let test = 'bird';
const change = data => data = 'world';
const changedTest = change(test); // bird world
test; // bird
let test2 = {text : 'bird'};
const change2 = data => data.text = 'world';
const changedTest2 = change2(test2);
test2; // {text: "world"}

原始数据类型值, 只是做了简单的赋值操作,赋值给参数后, 就和他没有关系

引用类型值, 传递的是引用地址, 在栈中的地址是同一个地址, 对参数的修改等于对传递值的修改

javascript易错点

函数的this

注意,本文介绍的是javascript,不是nodejs

1
2
3
4
5
6
7
8
let text = 'shit';
function demo(){
this.text = 'fuck you';
}
console.log(text) // shit
demo();
console.log(text) // fuck you

全局变量 window

1
2
3
4
5
6
7
8
9
// 上面的例子实际上等同于
window.text = 'shit';
window.demo = function (){
this.text = 'fuck you';
}
console.log(window.text);
window.demo();
console.log(window.text);

因为调用demo方法的是window全局变量 所以这里this指向的是window

1
2
3
//同时
this.text // fuck you

这里的this因为在全局变量window内, 所以也指向window

总结

this指向的是调用它的对象本身

例外

1
2
3
4
5
6
7
8
9
function demo(){
function child(){
console.log(`this is `, this)
}
child();
}
new demo();

本来这时按逻辑推导输出的是对象(new demo()) ,但是打印出的是window对象。 且当是javascript设计性质, 记住就好。 要解决这个问题 可以

1
2
3
4
5
6
7
function demo(){
function child(){
console.log(`this is `, this)
}
child.bind(this)();
}

vim执行shell的三种方式

Ctrl-z

属于推荐姿势, 挂起vim,回到执行当前vim的shell,fg命令返回vim

:shell

vim内部开启新的shell session,exit 退出返回vim

:!command

执行单个shell命令, 执行完毕按enter回到vim

vim保存去除^M

1
2
3
augroup fmt
autocmd BufWritePre *.ts silent! %s/^M//g|''
augroup END

*.ts 这里匹配了所有ts为后缀的文件

slient! 如果没有匹配到不显示提示

|’’ 替换完返回原先位置

vim

学习一门新的语言的学习顺序

  • 基础语法
  • 了解特性
  • 交互(http请求,数据库应用等)
  • 错误处理机制
  • 缓存机制
  • 进阶语法 (只是让你更快的编程)
  • 更好的组织架构

从es6开始学习javascript

es6之前拒绝学习js的话,目前已经爱上了js

  1. 基础语法 (变量,字符串, 数组, 函数, 对象)
  2. 特性 (set , map , promise & async, symbol, Generator, Decorator, module)
  3. 交互
  4. 错误处理机制 (try…catch , Error)
  5. 理解变量内存地址
  6. 进阶(Reflect ,Proxy)
  7. 学习redux (我认为一个前端程序员对redux的使用的熟练度,和编程思想的应用是直接的技术体现)

javascript 引用

对于javascript不熟悉的同学应该好好认识一下它的引用机制

内存泄漏

关于内存泄漏,请直接复制黏贴搜索文章查看

声明变量存储一个对象的实质

1
2
3
4
5
6
7
8
9
10
11
12
let a = {
c: 1,
b: 2
};
let c = a;
c.c = 3;
console.log('now a is ', a)
//now a is Object {c: 3, b: 2}

可以看出c变量只是对a变量的引用, 改变c的同时也改变了a。
即它们在内存中存储地址是一致的

仅仅在一个层级上做复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let a = {
c: 1,
b: 2,
d: {
e: 1
}
};
let c = {...a};
c.d.e = 3;
console.log('now a is ', a)
//e:3

即…运算符只是做了浅拷贝, 并没有做深度拷贝
那么正确的做法是

1
let c = {...a, d: { ...a.d} };

注意 Object.assign 也是浅拷贝类型

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
//demo 1
let a = {
c: 1,
b: 2,
d: {
e: 1
}
};
let c = Object.assign({}, a);
c.b = 5;
c.d.e = 3;
console.log('now a is ', a)
// c.b = 2
// c.e = 3
//demo2
let a = [1, 2, 3, 4, 5, 6, { c: 8 }];
let b = [...a];
b[6].c = 2;
console.log(b);
console.log(a);
[ 1, 2, 3, 4, 5, 6, { c: 2 } ]
[ 1, 2, 3, 4, 5, 6, { c: 2 } ]

数组的更新

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
let a = [1, 2, 3, 4, 5, 6, { c: 8 }];
let b = a;
b[1] = 20;
console.log(b);
console.log(a);
//log
[ 1, 20, 3, 4, 5, 6, { c: 2 } ]
[ 1, 20, 3, 4, 5, 6, { c: 2 } ]
let a = [1, 2, 3, 4, 5, 6, { c: 8 }];
let b = a;
b[1] = 20;
b = [1, 2, 3, 4];
console.log(b);
console.log(a);
//log
[ 1, 2, 3, 4 ]
[ 1, 20, 3, 4, 5, 6, { c: 8 } ]
let a = [1, 2, 3, 4, 5, 6, { c: 8 }];
let b = a.slice();
b[1] = 20;
b[6].c = 10;
console.log(b);
console.log(a);
//log
[ 1, 20, 3, 4, 5, 6, { c: 10 } ]
[ 1, 2, 3, 4, 5, 6, { c: 10 } ]

可见只要是对象那么就需要进行全拷贝

以上在编程过程中一定要注意