鹏の窝

何鹏技术博客


  • 首页

  • 标签

  • 归档

新的flag

发表于 2021-05-18 |

又到了一年一度的新Flag时间

10本书 2/10
React 已学完60%, 准备动手做自己的小项目
Python 最近在看新买的三本书 慢慢来。争取年底可以实战
吉他 (放弃了,吉他好像让我妈扔了)
换工作 (已完成 华宇到AC,薪资没达到期望,后面再看吧)

新的开始新的flag

发表于 2021-04-25 |

过去


2017年7月 一个菜逼入职了大连华宇
经过三年半的板砖 成为了一个高效的板砖高手并且拥有精湛的cv技术


lodash常用指南

发表于 2020-09-04 |

转载: 请携带作者并标明出处
归类在项目中 常用的lodash方法
内部大部分方法 可通过es6等处理 这里不做归类

我的还是我的鲁迅

常用lodash指南 Array

去除假值

1
2
_.compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]

数组 过滤数据 并返回新的数组

1
2
3
4
5
6
// 你可以过滤id
_.difference([3, 2, 1], [4, 2]);
// => [3, 1]

_.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
// => [{ 'x': 2 }]

填充 替换

1
2
_.fill([4, 6, 8, 10], '*', 1, 3);
// => [4, '*', '*', 10]

获取数据的索引 (第一个)

1
2
3
4
5
6
7
8
9
10
11
12
13
var users = [
{ 'user': 'barney', 'active': false },
{ 'user': 'fred', 'active': false },
{ 'user': 'pebbles', 'active': true }
];
_.findIndex(users, function(o) { return o.user == 'barney'; });
// => 0
_.findIndex(users, { 'user': 'fred', 'active': false });
// => 1
_.findIndex(users, ['active', false]);
// => 0
_.findIndex(users, 'active');
// => 2

同上 反向

1
2
3
4
5
6
7
8
9
10
11
12
13
var users = [
{ 'user': 'barney', 'active': true },
{ 'user': 'fred', 'active': false },
{ 'user': 'pebbles', 'active': false }
];
_.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
// => 2
_.findLastIndex(users, { 'user': 'barney', 'active': true });
// => 0
_.findLastIndex(users, ['active', false]);
// => 2
_.findLastIndex(users, 'active');
// => 0

数组序列化

1
2
_.flattenDeep([1, [2, [3, [4]], 5]]);
// => [1, 2, 3, 4, 5]

键值转对象

1
2
_.fromPairs([['fred', 30], ['barney', 40]]);
// => { 'fred': 30, 'barney': 40 }

过滤交集数据

1
2
3
4
var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.intersectionWith(objects, others, _.isEqual);
// => [{ 'x': 1, 'y': 2 }]

**移除数据

1
2
3
4
var array = [1, 2, 3, 1, 2, 3];
_.pullAll(array, [2, 3]);
console.log(array);
// => [1, 1]

**移除数组对象

1
2
3
4
var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
_.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
console.log(array);
// => [{ 'x': 2 }]

**移除数组 按索引

1
2
3
4
5
6
var array = [5, 10, 15, 20];
var evens = _.pullAt(array, 1, 3);
console.log(array);
// => [5, 15]
console.log(evens);
// => [10, 20]

知识点-深浅拷贝

发表于 2020-09-03 |

转载: https://lixuguang.github.io/2020/01/02/FE-guide-new/
作者: 李旭光

你的就是我的鲁迅

拷贝

1
2
3
4
5
6
let a = {
age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2

从上述例子中我们可以发现,如果给一个变量赋值一个对象,那么两者的值会是同一个引用,其中一方改变,另一方也会相应改变。
通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个问题。

浅拷贝

Object.assign

首先可以通过 Object.assign 来解决这个问题。

1
2
3
4
5
6
7
8
// js代码

let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // => 1

展开运算

当然我们也可以通过展开运算符(…)来解决

1
2
3
4
5
6
let a = {
age: 1
}
let b = {...a}
a.age = 2
console.log(b.age) // => 1

Array

1
2
arr.slice()
arr.concat()

通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就需要使用到深拷贝了

1
2
3
4
5
6
7
8
9
10
11
// js代码

let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = {...a}
a.jobs.first = 'native'
console.log(b.jobs.first) // native

浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到刚开始的话题了,两者享有相同的引用。要解决这个问题,我们需要引入深拷贝。

深拷贝

这个问题通常可以通过 JSON.parse(JSON.stringify(object)) 来解决,这也是最好用最简单的方法,俗称乞丐版。

乞丐版

1
2
3
4
5
6
7
8
9
10
11
// js代码

let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

但是该方法也是有局限性的:

  • 会忽略 undefined
  • 会忽略 symbol
  • 不能序列化函数
  • 不能解决循环引用的对象
    举个栗子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// js代码

let obj = {
a: 1,
b: {
c: 2,
d: 3,
},
}

obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c

let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj) // => Uncaught TypeError: Converting circular structure to JSON

如果你有这么一个循环引用对象,你会发现你不能通过该方法深拷贝
在遇到函数、 undefined 或者 symbol 的时候,该对象也不能正常的序列化

1
2
3
4
5
6
7
8
9
10
// js代码

let a = {
age: undefined,
sex: Symbol('fmale'),
jobs: function() {},
name: 'lixuguang'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // => {name: "lixuang"}

你会发现在上述情况中,该方法会忽略掉函数和 undefined 。
但是在通常情况下,复杂数据都是可以序列化的,所以这个函数可以解决大部分问题,并且该函数是内置函数中处理深拷贝性能最快的。

那么是否可以解决函数和循环引用的问题呢?答案是肯定可以解决,接下来是基础版本的改造

基础版本

1
2
3
4
5
6
7
8
9
10
11
function myClone(target){
if(typeof target === 'object'){ // 判断传入目标是否是object类型
let cloneTarget = {}; // 创建克隆对象
for(const key in target){ // 遍历目标对象
cloneTarget[key] = myClone(target[key]) // 递归调用 clone 方法
}
return cloneTarget;
} else {
return target // 如果不是 object 返回
}
}

写到这里已经可以帮助你应付一些面试官考察你的递归解决问题的能力。但是显然,这个深拷贝函数还是有一些问题。
这里只考虑了对象,没有考虑数组。
下面我们来做一个强化版的深拷贝,同时考虑对象、数组还有循环引用的问题。

强化版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function myClone(target, map = new WeakMap()) { // WeakMap => 键对象弱引用, 可被垃圾回收
if(typeof target === 'object'){ // 判断是否是对象
let cloneTarget = Array.isArray(target) ? [] : {}; // 判断是是数组还是对象
if(map.get(target)) {
return target;
}

map.set(target, cloneTarget);

for(const key in target) {
cloneTarget[key] = myClone(target[key], map)
}
return cloneTarget;
} else {
return target;
}
}

当然如果你的数据中含有以上三种情况下,可以使用 lodash 的深拷贝函数。
如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// js代码

function structuralClone(obj) {
return new Promise(resolve => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}

var obj = {
a: 1,
b: {
c: b
}
}
// 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
(async () => {
const clone = await structuralClone(obj)
})()

深拷贝实现方式2,可以深拷贝 function 、symbol,等等,堪称终极版

终极版

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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// js代码

const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]';

const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';

const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
function forEach(array, iteratee) {
let index = -1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
}

function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}

function getType(target) {
return Object.prototype.toString.call(target);
}

function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
}

function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe));
}

function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
}

function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
if (func.prototype) {
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return eval(funcString);
}
}

function cloneOtherType(targe, type) {
const Ctor = targe.constructor;
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe);
case regexpTag:
return cloneReg(targe);
case symbolTag:
return cloneSymbol(targe);
case funcTag:
return cloneFunction(targe);
default:
return null;
}
}

function clone(target, map = new WeakMap()) {
// 克隆原始类型
if (!isObject(target)) {
return target;
}

// 初始化
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
} else {
return cloneOtherType(target, type);
}

// 防止循环引用
if (map.get(target)) {
return target;
}
map.set(target, cloneTarget);

// 克隆set
if (type === setTag) {
target.forEach(value => {
cloneTarget.add(clone(value));
});
return cloneTarget;
}

// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value));
});
return cloneTarget;
}

// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = clone(target[key], map);
});

return cloneTarget;
}

// 调用方法
clone(target);

知识点-new

发表于 2020-09-03 |

转载: https://lixuguang.github.io/2020/01/02/FE-guide-new/
作者: 李旭光

你的就是我的鲁迅

new 一个对象的过程

  1. 新生成一个对象
  2. 链接到原型
  3. 绑定this
  4. 返回新对象

在调用new的过程中会发生以上四件事,我们也可以试着来自己实现一个new

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// js代码

function create() {
// 创建一个空得对象
let obj = new Object()
// 获得构造函数
let Con = [].shift.call(arguments)
// 链接到原型
obj.__proto__ = Con.prototype
// 绑定 this,执行构造函数
let result = Con.apply(obj, arguments)
// 确保 new 出来的是个对象
return typeof result === 'object' ? result : obj
}

对与实例对象来说,都是通过new产生的,无论是function Foo() 还是 let a = { b : 1 }。

对于创建一个对象来说,更推荐使用字面量的方式创建对象(无论性能上还是可读性)。因为你使用new object()方式创建对象需要通过作用链一层一层找到object,但是你使用字面量的方式就没这个问题。

1
2
3
4
// js代码

function Foo() {}// function 就是个语法糖,内部等同于 new Function()
let a = { b: 1 } // 这个字面量内部也是使用了 new Object()

对于 new 来说,还需要注意下运算符优先级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// js代码

function Foo() {
return this;
}
Foo.getName = function () {
console.log('1');
};
Foo.prototype.getName = function () {
console.log('2');
};

new Foo.getName(); // -> 1
new Foo().getName(); // -> 2

从上图可以看出,new Foo() 的优先级大于 new Foo ,所以对于上述代码来说可以这样划分执行顺序

1
2
3
4
// js代码

new (Foo.getName());
(new Foo()).getName();
  • 对于第一个函数来说,先执行了 Foo.getName() ,所以结果为 1;
  • 对于后者来说,先执行 new Foo() 产生了一个实例,然后通过原型链找到了 Foo 上的 getName 函数,所以结果为 2。

axios全局遮罩

发表于 2020-01-07 |

全局遮罩


因为全局的axios组件 所以很容易的从前置和后置拦截器中
通过headers向后台传输 token或者 user-info
并使用elementUi的 Loading.service 进行了请求的遮罩
本来是很舒服的一件事
但突然发现 因为业务需要 使用Promise.all 的遮罩缺出现了问题


分析原因


因为大家都知道 .all 是当所有请求都success时 按照书写顺序同时返回。
但拦截器并不知道,当第一个接口开启拦截器后,后续接口同步开启。
但第一个结束的接口关闭了遮罩。其他接口却还没有完成。


对策


增加计数器 每次开启遮罩进行计数
关闭之后 进行– 当等于0时即可默认所有接口完成 关闭遮罩


1
2
3
4
5
6
7
8
9
10
请求拦截器
// 为了解决 promise.all的 多个参数 无法计算遮罩 增加遮罩计数器 by--刘春阳 徐速成
if (modelIndex === 0) {
loadingInstance = Loading.service({
lock: true,
text: '努力拉取中 ~>_<~',
background: 'rgba(0, 0, 0, 0.7)'
})
}
modelIndex++
1
2
3
4
5
6
7
响应拦截器
modelIndex--
if (modelIndex === 0) {
if (loadingInstance) {
loadingInstance.close()
}
}

axios下载错误提示

发表于 2020-01-07 |

Blob转换为JSON


今天,开发小伙伴问我。当导出失败的时候 后台返回的Message 前台没有打印。我是在 response中进行的拦截
发现 responseType: ‘blob’的返回值 是要给blob的类 并没有message啊。
从期望来看。是要将blob对象转换成 JSON 就可以了


分析


但是 我所封装的axios组件中 正常的get post请求和 blob请求等 是公用一个拦截器的。
为了不影响其他功能。就需要做一次判断。
1.首先判断是否是blob请求
2.其次判断 type类型是否是 ‘application/json’
都满足是 即可转换打印


1
2
3
4
5
6
7
8
9
10
if (
error.config.responseType === 'blob' &&
error.response.data.type === 'application.json'
) {
let reader = new FileReader()
reader.readAsText(error.response.data, 'utf-8')
reader.onload = e => {
Message.error(JSON.parse(reader.result))
}
}

Vue优化回顾

发表于 2020-01-06 |

2019总结

发表于 2019-12-31 |

2019 总结


总结来说 就是相对于 2019年初计划。我 干(屁)的(事)不(没)错(干)!!!!
今年是在用户体验部最开心的一年,以及我的各位小伙伴,结果年底前端部门解散,拆分到项目组,真特么玩我呢…****


做了什么


其实,总的算起来。我也达到了一点。
1.也算是入门了React,但还没有实战经历。听说React更新了,等出教程也跟着实战来一遍。
2.Git从以前无脑push,也初次接触了gitflow,也看到了带来的好处。哈哈哈 今晚也是拼团买了git三剑客,待我啃完升职加薪,走上人生巅峰。
3.年初定了要读10本书….我特么加上读小说也没读到10本…webpack就啃了40页 css选择器世界我读了封皮..我废了 我忏悔,我2020一定重新做人。
4.公司培训,算是完成了年初的任务。Vue Es6 前后端分离 CSS+HTML JS 都完成了部门或公司培训,待我奖励自己一顿炸鸡。
5.Vue模板也通过一年打磨,推出了Base版本,使用项目20+也算是完成了承诺,也打包成了脚手架 vue create –preset he8hepeng/cli3
6.也终于有了自己维护的NPM模块,也算是给自己增加了一点加分项吧。
7.啃完了Es6
8.尽了把师傅的责任
9.当上了前端技术负责人 T.T


吐槽


在某宇呆了两年半了,我的前端经验也马上三年。心底还是感谢公司带给我的成长,但最近的公司氛围还是让我感到恐慌。
累的永远是有技术的人,也看到了很多有才干有抱负的人离开。也送走了好多合作愉快的项目经理及大神开发。
公司不注重前端,毕竟是卖软件的 更偏向数据和业务无可厚非。但前后端分离的问题,还是让前端累的喘不过气。
ATY6的淫威,让我怕怕….
下半年前端团队分了合 合了分,元旦毫无预兆的拆组分入项目团队,让人寒心。
耗费无数个夜晚来工作和学习,终于成为高级前端,但领导却认为听话的人就是准高级。。。真是让我苦笑。。。
日子还得过,只能不断地充实自己,不断地努力,才能不被这些挫折所影响,加油!


明年计划(flag)


1.心怡的工作。
2.读10本书。
3.啃完TypeScript和webpack以及React
4.身体健康,培养一门兴趣爱好。可以试试吉他,毕竟我会弹棉花。
5.旅游
6.每周一篇博客


性能问题分析

发表于 2019-12-29 |

首先 问题是 T3E项目 突然联系我说,出了个小问题 0.0 然后下去发现 在IE11下,20分钟操作就引起页面崩溃,
按理说64位系统 浏览器也拥有1.5G的内存,一个web项目 怎么会引起性能问题呢。
so 用IE11的内存跑了下 发现部分页面每次加载就加了200M的内存 但离开 并没有内存降低的迹象…大概了解了

首先的应对策略

首先,确认了一下项目背景,是由前端及部分开发共同开发的。
技术栈为VUE + atyUI(公司基于ElementUI 进行的二次封装)。
而后 要了一份GIT地址 然后看了一下sonar,代码从检查来看还可以。进行人肉检查。

果然发现了大量的数据处理 和页面create里的大量前置接口 0.0 还真是公司特色呐~
而后继续 检查。
发现公共的JS文件 放入了 Mixins里,确实是好习惯 但是…引入了36次…
发现 大量的数据操作 使用reduce filter等语法操作数据,从性能来看 要比for循环更加损耗内存,但这么严重的性能问题
不会是这种操作引起的 但需要记录一下。
打开控制台 通过PerforMance和快照 发现atyUI中存在大量闭包 无法被销毁。。原来你才是罪魁祸首
整理文档 交给项目组 整改

123<i class="fa fa-angle-right"></i>

心若向阳,何惧忧愁

21 日志
11 标签
GitHub
Links
  • 李旭光
  • 王宁
© 2021
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4