看看作者对npm的理解,学习他笔下的package.json文件,以及从此衍射出的更广阔的npm操作秀。
vtmim重构
先放一个占位符。vtmim是我接手的第一个我厂项目,在维护过程中感觉到如下问题:
5000行代码一个文件,动辄一个函数200行,文件代码冗余,经常看到if else里出现同样的代码,代码的注释文不对题,以及缺乏合理的模块管理。
原生nodejs爬mdn
作者的一道题目:
nodejs爬https://developer.mozilla.org/zh-CN/docs/Web/API, 把所有api对用的html文档保存在本地。
作者推荐使用node-fetch,cherrio,super-agent。因为我用过上述三者爬虫,这次打算使用原生api开发,刚好
测试一下最近学习的正则表达式有没有长进。
原材料 : https + fs + es6 + nodeV8.21
promise是上帝,promise是魔鬼
promise是上帝,只要是异步,完全可以用promise替代毁掉函数;promise是魔鬼,只要用了promise就得全盘用promise
自己对promise在实际使用中的理解
callback promise化
普通回调函数写法:
1 | function myAwesomeAsyncMethod(callback){ |
promise化回调函数:
1 | function myAwesomeAsyncMethod(callback){ |
如果用了promise需要全盘使用promise
这一点让人很不爽,一旦用了promise,代码中其他的异步操作统统需要promise化。
promsie配合async await一起用
单纯的用promise和Promise.then一步步链式调用本质上和callback并没有太大区别,但是配合async await则
可以将异步函数改造成形式上的同步函数, 举个板栗:
1 | let fetchWebApi = async (url) => { |
理由很简单: await修饰的函数必须要promise化。需要注意的是,使用到await的函数必须是async,
promise是promise,async是async,二者promsie对象接受的函数当然也可以用async修饰,参照fetchAndSave方法。
https请求
如果目标url是http,需要使用http模块,如果是https,需要使用https模块。使用http模块调用
https url会报错。
实际问题中遇到重定向问题。[300, 400)之间的状态码是重定向,又细分301的永久重定向和302的临时重定向。对于2者和搜索引擎
的纠葛在此不做深入探讨。对于需求,只需要获取重定向后的url内容即可。
1 | await fetch(res.headers.location) |
res.headers.location
中包含了重定向的url
获取数据用到的data事件很有趣,返回的data应该对应http的一个请求包。一堆请求包组成一个html文档。
清洗html文档,提取apiList
当获取完成包含api的html文档后,下一步是要提取apiList。cherrio是node的jquery,api和jquery,zepto大同小异。
但是我们的目标是没有蛀牙不使用第三方库。我改怎样做呢?
request.get返回html文档的字符串,如果在浏览器端,会被浏览器解析成dom树。服务端并不需要解析成dom树,只要从字符串中提取
api的路径。
一开始观察,发现关键字段在<code>API</code>
中,正则用:
1 | let reg = /<code>.+?<\/code>/ig //惰性匹配 |
注意全局搜索和利用?实现惰性匹配,否则会将开头的<code>
和结尾</code>
之间的所有字符串一次性匹配。
坑爹是的,code之间的字段并不能100%当时api的路径,老老实实用href里的字段,
1 | let reg = /"\/zh-CN\/docs\/Web\/API\/.+?"/ig |
自从刷了hackerrank里的大部分正则基础题后,写这些正则变得砍瓜切菜一样简单。
分布提取api文档
一共有748个api,如果for循环一次性请求会挂掉。从作者那里偷学了while用法:
1 | while(some condition){ |
这里,Promise.all接受一个数组,数组的每个元素都是promise对象。于是又要promise化。
为什么只写入了747个文档?
可能是99%的情况下永远少写入一个文档,只有一次测试的时候,5个请求写入3个文档,其他都是写入4个文档。测试很久之后才反应过来,
fs.writeFile也是个异步方法,需要promise化。
至此,爬虫遇到的主要难点都写完了,我们实现了纯nodejs api爬虫。
附录:百行代码就不开git仓库了:
1 | /** |
—————————–我是分割线———————————-
作者小小的修改了代码:更严谨的代码,更抽象的逻辑和更高的可复用性
js 的类与继承
js的继承真是天坑,看过红宝书的原型链和继承章节不知所云。各种继承方式,颇有种乱花渐欲迷人眼的感觉。
es6的class和extends给了我一丝曙光。
1 | class Foo { |
这个简单例子包含了类,继承和调用父类。麻雀虽小却五脏俱全。不知道有没有人想的和我一样,类
只是个语法糖,在现有的环境下还是要被babel转换成es5的原型链继承方法。
wait,被babel转换成es5的原型链继承方法
,babel翻译后的一定是优秀的继承方式,我仿佛找到了onepiece
先看看babel如何翻译Foo
1 | 'use strict' //严格模式 |
深入代码理清逻辑:
1 | var Foo = function(){}() |
还有这种写法???匿名函数先赋值给Foo,Foo和()结合在一起,执行Foo
看看关键函数_createClass
1 | var _createClass = function () { |
背后的原理:
1 | Foo.prototype.key = value |
再看看继承
1 | function _inherits (subClass, superClass) { |
回味原型链和继承
对象都有一个私有属性[[prptotype]]
,attention,这个私有属性有别于prototype
,通常私有属性实现时用proto替代。
每个对象都有proto
函数对象拥有prototype对象,函数也是对象,自然也拥有proto
object.proto指向object的原型
object.prototype指向object的原型对象 ???
Object.defineProperty
Object.create
Object.getPrototypeOf
Object.setPrototypeOf
杂文
新版博客主题
受到icyfish小朋友的启发,决定给粗鄙的博客换一个主题。遍历了知乎上的优秀主题,选中了莫泊桑。自己写过一整年material-ui
风格的网站,对于各种酷炫的交互都不感冒。诚如作者说的:大道至简。繁华过后,只希望有一片净土,写写文章,品品生活,足矣。完全不记得昨天icyfish
小朋友推荐的就是莫泊桑
,选中它,大约是因为人都会欣赏优秀的事物。
机械键盘
就在今早,又败了一个机械键盘。至此,我拥有一块plum 104黑轴
,两块酷冷至尊87红轴
和即将到来的酷冷至尊87青轴
。
都到这地步了,为什么不充值一波信仰,直接filco 双模一步到位?因为在前东家的时候,filco青轴给我留下了不如300块凯酷青轴的印象。
更真实的情况是,仿佛这一刻全世界的filco 双模忍者87青轴
都缺货。zby小朋友一口气买了2块minila,一块青轴一块红轴。红轴的手感非常好,青轴的手感一般。她买键盘前
做的功课和买键盘的魄力已经执行力给我深深上了一课。
title date tags 失效
使用CRLF替换CR
md文件里换行导致编译后的文件也换行
hexo-renderer-marked/index.js
下的breaks: false
关于react的思考
最近维护一个小功能掉坑里了。一个需求需要同时修改四处html字符串。服务端一处,客户端3处字符串拼接。此时我十分怀恋react。理论上如果框架设计的精妙,即使是SSR也只需要修改一个react文件,甚至仅需要
添加一个state对象。
高山仰止景行行止
我喜欢和聪明的人共事。在ctrip,我有很大几率遇到比我聪明的人,比如作者,比如xmy,和他们聊天时,能感觉到他们都在发光,睿智,逻辑,激情,理性。这让我又兴奋又后悔。
后悔自己游戏人生太久,渐渐地身体开始走下坡路,大约没有赶上他们的资本了,另一方面,就像我旁边的绿萝,即是不能,我也要快速的成长,虽不能至,心向往之,同时也要努力靠近。
if else debug 小技巧
今天领悟到了一个if else debug小技巧。例如
1 | if(a){ |
我们有时候会遇到一种情况:不想走a的逻辑或者一定走a的逻辑。有一天突然想到:
1 | if(true || a){ |
这样写有个一好处,写完需求后,只要删除掉true或false后就能还原逻辑,不会造成过多修改代码造成的潜在bug风险。
.style and getComputedStyle
#.style vs window.getComputedStyle
1 | <div className="filter-header-tabs" ref="filterHeaderTabs" style={{'width': '500px'}}> |
1 | .filter-header-tabs { |
1 | console.log(window.getComputedStyle(this.refs.filterHeaderTabs).width); // 660px |
总结:
1:.style只能获得div标签上的style,无法获得css内的style和真实的style, 未设置的style都是""
2:window.getComputedStyle获取的是节点的真实style
#getDOMNode() vs findDOMNode() vs this.refs
从react14版本开始,getDOMNode被遗弃,使用findDOMNode替代With this change, we’re deprecating .getDOMNode() and replacing it with ReactDOM.findDOMNode (see below). If your components are currently using .getDOMNode(), they will continue to work with a warning until 0.15.
##findDOMNode()和this.refs的区别
14版本又说了:Starting with this release, this.refs.giraffe is the actual DOM node
从这个版本开始,this.refs等同于真实的dom节点,也就是说(部分地), findDOMNode9()等同于this.refs
14版本又说了:Note that refs to custom (user-defined) components work exactly as before; only the built-in DOM components are affected by this change.
总结:
1:在dom节点(div, p, span…)上用findDOMNode()等同于this.refs
2:在React组件上用,this.refs获取的是组件的引用,findDOMNode()获取的是组件的dom树
作用在dom节点上
1 | <div className="filter-header-tabs" ref="filterHeaderTabs"> |
callback和promise
对个人而言,刚开始学习js,最难的一点在理解回调函数上。滑稽与可笑,前端一年半了,在昨天晚上回去的路上才想通了回调函数,算是打通了js的任督二脉吧。
所谓回调函数,即在函数返回时被调用的函数。在js中函数可以为当做参数传递。简单的例子:
1 | function b(callback){ |
知乎上看到一个很有趣的理解回调函数的例子:
我去商店买东西,没有了,我给商店留下了电话号码。这里,电话号码就是回调函数。东西到货了,商家打电话给我。打电话这个过程就叫调用回调哈数。
打通理解回调函数后,es6的promise就好理解了:
Promise对象接受一个回调函数作为参数,该回调函数接受2个回调函数作为参数,第一个为pennding->sucess时调用,第二个参数为pending->failed时调用的;
1 | var time = function(time){ |
spring13小结
马云讲过人离职的原因,钱给的少 || 呆的不开心。以前我一直只觉得第一个原因,如今第二个原因也满足了。到该走了时候了。
直到现在才开始写项目总结,想想蛮可笑的。写一写自己的项目心得吧。
一句话总结:学到很多,项目设计很乱,时间人为地很赶,自己进步一点。
这次项目需要写2个配置页面,使用2个带有get和put方法的Restful风格接口从服务器端获取J和上传JSON对象。
页面设计图:
就从router入口写吧。
1 | <Route path="device_endpoint_feature" component={DeviceSingleFeatureContainer}/> |
SingleFeature页面比较简单:
1.写了一个mapKeyToDisplayName方法,匹配key和显示的名字。
2.以前一直有个烦心事,用className方法生成新的class不知道取什么名字,有一天在看ant-design源代码,发现他们如下用,一举解决了命名问题:
1 | const titleCls = className({title : this.state.showTitle}); |
3.下拉框设计难点:
UI设计稿中,异常度下拉框是个输入框,不是下拉框。考虑到未来的可扩展性,我建议UI改成了下拉框,被采纳。手写了个下拉框组件,有如下特点:
3.1 如果下拉框内容只有1条,则禁止触发onClick和onMouseOver事件,并且:
1 | cursor:not-allow; |
3.2 鼠标hover时展开,离开后下拉框收起。有2个难点:
onMouseOut, onMouseLeave的选择。二者都能相应鼠标离开事件,区别是,out只对被设置的DOM对象起作用,比如,out在父DOM节点上,移动到子DOM节点上就触发一次out,leave则是离开整个DOM节点才会触发。这里,选择onMouseLeave。
通过父组件position:realative,子组件absolution + z-index方式生成,遇到个问题,图片中上面的边框和下面的边框有个间隙,鼠标触碰到间隙后也会触发Leave事件,解决的办法是,用个大div包裹下面的dom并连接上面的dom,设置成透明即可。
4 关于重置逻辑:
1 | this.setState({config:this.props.config}) |
5 关于保存逻辑:
在reducer中,如果后台返回了类似{ok:true}
字段,则使用上传给后台的数据。
1 | case PUT_DEVICE_SINGLE_FEATURE_CONFIG_SUCCESS: |
6:源于添加逻辑:
通过feature.disabled
的值来判断,true在放置在添加栏内,false则放置在属性展示框内
7:关于小开关的逻辑:
感谢state和prop,让我实现各种逻辑得心应手。
8:关于Immutable对象的思考:
如果没有Immutable.JS,我也不知道该怎样去安全地去操作对象。
1 | var a = {}; |
同样,如果没有this.state也存在被共用修改内容的危险。Immutable.JS一举解决了所有问题。实测效率不差。网上关于Immutable.JS有2种不好的观点,第一,api很难用,第二,体积有点大(其实是api太多了)。我觉得,类似于java风格的api,用着用着就习惯了。第二,(个人猜测)Immutable.JS是有野心的。api覆盖了js原生操作对象,数组的所有方法,这样,我们不妨脑洞大开,以后在React项目中,全部用Immutable.JS API操作对象(数组)。下一个项目打算这么干。
[后续补充], oh my dear,恰巧遇到了错误修改state姿势,导致的迷之bug,修改了半天。
1 | this.state = { |
一个分页组件的点击页面逻辑。
bug描述 : 分页组件失效。
bug追踪 : 从react-dev-tool中发现,this.sate.paginationConfig.selectedPage
发生了变化,但是分页组件实现。分析可能是没有触发render,上游原因是shouldComponentUpdate
, 分析可知:
1 | paginationConfig.selectedPage = page; //共享对象,直接修改了state |
导致_.isEqual(nextState, this.state)
返回true.
先不忙着修改,此处应该还有更有趣的问题。
1:错误使用了Object.assign
方法,正确姿势:
1 | const paginationConfig = Object.assign({}, this.state.paginationConfig); |
2.Object.assign
本质上是浅拷贝
1 | const obj = {a:1,b:{c:1}}; |
吓得我赶紧换回Immutable,彻底清净了。
EventFrequency页面比较复杂:
这里坑比较多。总结起来有点:
- 设计不合理,不合理,不合理!
- 因为1的关系,操作json数据逻辑复杂
- UI设计没有适配低分辨率屏幕
关于1和3具体内容我不想过多讨论,但是直接导致了实际开发中,我抛弃设计稿,调整了排版布局,也导致了问题2,一度困扰了我整个周末。整个周末都没过好,在电影院看电影都在想如何操作数据。
接口字段名和API不一致
小问题一堆子的交互逻辑
小问题,配合state和props好写,担心后来人看不懂为什么代码里要添加那么多开关,多写了几行注释。频次数值和异常度每次只能修改其中1个。要突出timeFerature(分钟,小时,日)的作用
2个很大问题。
按照后台设计逻辑,只能传输频次数值(value)和异常度(anomaly)
其中的一个。这个坑了。之前设计好的逻辑是,在container容器中统一分发给每个component内容,获取和修改的json所有key都相同,只有value不同。这种方式修改起来比较简单。但是现在接口每次只给一个value或anomaly,我在上传json前也做做判断,判断用户选择了哪个字段,我要剔除相应的字段。
为了适应前期的工作,我在get到数据后,在container组件里,手动添加了所有缺失的字段。按照设计理念,timeFeature可以全选,但不能全空,选择后对应显示对应的字段,关闭后,不选择对应的字段。
1.timeFeature开关逻辑
1 | selectTimeFeature(type) { |
2.自动填充数据逻辑
1 | /** |
- 我需要一个state记录每次修改的下拉框类型,以便上传数据时剔除没有被选中的字段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22case 'changeType': {
const configDropSelectedType = Immutable.fromJS(this.state.configDropSelectedType).toJS();
if (!configDropSelectedType[feature][type][dropdownType]) {
switch (dropdownType) {
case 'value': {
delete configDropSelectedType[feature][type].anomaly;
configDropSelectedType[feature][type][dropdownType] = true;
break;
}
case 'anomaly': {
delete configDropSelectedType[feature][type].value;
configDropSelectedType[feature][type][dropdownType] = true;
break;
}
default:
return;
}
this.setState({configChanged: true});
}
this.setState({configDropSelectedType: configDropSelectedType});
break;
}
4.比较config和记录修改下拉框类型的state,上传数据
1 | //比较configDropSelectedType和config,去除多余的数据 |
学习到一个技巧:修改原生dom的class样式:
1 | const node = this.refs.promotion_bonus_fraud; |
还有输入框内容校验等,不多赘述。
至此,项目写完了。全部原生代码,没有使用任何UI组件库。原因是,觉得material-ui下拉框太丑,引入ant-design dropdown会导致bundle.js(未压缩)多了6M,权衡下手写了个下拉框组件,花了点时间。
其次就是组织业务事件频次的数据上花了不少时间。再一次深刻的感受了下,不合理的设计会给实现带来很大的困难。
数组去重
####暴力代码:
1 | var removeDuplicates = function(nums) { |
chrome控制台用了274ms,一般ACM超时不都是1000ms么。
假装自己会计算算法时间,两次循环用了O(n*n),splice时间不知。
####Object keys方法:
1 | var removeDuplicatesByObject = function(nums){ |
平均耗时约12ms。
####es6 Set方法
1 | var removeDuplicatesBySet = function(nums){ |
耗时26ms
####看看lodash去重时间
4ms!!!
react autoFocus问题总结
react autoFocus问题总结
标签(空格分隔): react
有个组件,展开时input
需要自动focus.
<MyComponent>
<input autoFocus = {true} />
</MyComponent>
这样在组件加载后(componentDidMount)input可以自动focus了。
测试后发现,focus只会在mounted
后触发,当nextProps来了之后就不能autoFocus了。
解决办法:
class MyComponent exetends React.Component{
componentWillReceiveProps(nextProps){
//make sure render returned before following code
this.refs.input.focus();
}
componentDidMount(){
this.refs.input.focus();
}
render(){
<input ref = "input"/>
}
组件加载完成和收到nextProps后都调用一次focus(), ==,好像有更好的解决办法,=我查看一下react生命周期。15.4里componentDidUpdate()
是个更好的选择。