前天帮人配了台机器,未来可能还要帮人配。在学会用 Docker 之前,先写一篇记录下怎么搭建环境吧。
这篇收费!¥4.99,请阅后自觉打钱。
(更多…)
开发日历控件的时候,对方变更了一下需求,基本上将最终产品分成两个:
那么我们知道,对于这种大部分功能一致,只有若干函数逻辑不同的产品,最合适的就是状态模式。于是很自然的,我就拿“2”作为标准模式,“1”作为新模式,将其重构成父类和子类,大概关系如下:
// 父类
// DatePicker.js
import RangeDatePicker from './RangeDatePicker';
class DatePicker {
....
static getInstance(el, options) {
if (options.scattered) {
return new DatePicker(el, options);
} else {
return new RangeDatePicker(el, options);
}
}
// 子类
// RangeDatePicker.js
import DatePicker from './DatePicker';
class RangeDatePicker extends DatePicker {
}
因为这个类只有两个成员,所以我把工厂方法 .getInstance() 放到了父类里面,通过判断参数确定应该返回哪一类实例。代码写完,测试的时候却报错:
Super expression must either be null or a function, not undefined
这个意思很明显,被继承的父类不能未定义。然则 DatePicker 明明是定义了的,只是验证两个类文件的话,均未出现任何语法错误。
遇事不决先 Google,还真找到很多结果,不过大多数都和 React.Component 有关,翻了半天一无所获,只好自力更生。打开 Chrome 开发者工具,勾上“Pause on Exceptions”,观察发生异常时的状况,一遍又一遍,我渐渐意识到,发生这个错误的时候,DatePicker 还未能在 webpack 的环境中完成注册。问题找到了!
与其它编译类语言不同,JS 是动态语言,所有 JS 代码都是放到统一的环境里跑的,类的代码如此,import 也是如此。所以对于其他语言,比如 ActionScript、Java,循环引用,即 A 引用 B,B 也引用 A,是没问题的,因为类的代码都会编译到执行文件,执行的时候,都已经在环境中;而 JS 是边执行边置入环境,具体到我这里,在将父类 DatePicker 放入环境时,会先 import 子类 RangeDatePicker 的代码,而子类又会要求 import 父类的代码,父类的代码正在引入中,于是便产生了问题。
想明白这点,后面就好办了,直接创建一个工厂类,把工厂方法放到里面执行,问题便解决了:
import DatePicker from './DatePicker';
import RangeDatePicker from './RangeDatePicker';
export default {
createDatePicker(el, options) {
if (options.scattered) {
return new DatePicker(el, options);
} else {
return new RangeDatePicker(el, options);
}
}
}
PS:当年写依赖注入和包管理工具的时候,就卡在这个地方,怎么都想不通,于是一直也没写完。没想到这些个浓眉大眼有头发的,也都这么不负责任,这种问题都不解决就搞出来让全世界人用了。

今天前同事李某找我咨询 Hybrid 开发的问题,想起来大前天搞这个问题搞了一天,赶紧记下来,省得忘记。
先说需求。东家让我做个日历组件,在手机 Web 上用。组件的样式是这样的,很多地方都可以见到,比如南航国航的客户端。

看起来并不复杂,事实上也是,基本上顺顺利利的开发完成,准备交付。这里有个伏笔,开发中我按老习惯,使用桌面 Chrome,和实际生产环境不太一样。不过我自然要去真机上测试,结果一测问题就出来了。
因为组件需要全屏展示,所以我设置了如下的CSS:
.date-picker {
position:fixed;
top:0;
left:0;
right:0;
bottom:0;
background-color: white;
z-index:1024;
}
同时,对原本的 <input name="date">,我给它加上 readonly,避免弹出虚拟键盘。理论上,这样的就可以了。但实测时,不滚屏的时候,组件弹出时尺寸是准确的,盖满全屏;然则一旦滚屏,组件就会占据从页面最上方到当前最下面这截位置。大约相当于 position:absolte;top:0 的效果。


如图,可以看到组件占据了全屏,但实际是从页面最上面开始的,定位有问题。用桌面 Safari 调试也可以看出来它的高度是 968,远大于正常的 667。
这很诡异,上下左右全为0,是上古巨兽 IE6 都支持的做法。iOS Safari 虽然 Bug 多多,不应该连这个都有毛病啊。以 ios safari position fixed 为关键词 Google 之,结果 iOS Safari 历史不清白,当年 iPhone 刚出的时候的确有定位问题,于是虽有满屏的结果,但都不适用。
然后我想到找其它库,比如 Bootstrap,它的 Modal 组件也是类似的效果。但是怎么测都正常,于是我只好一个样式一个样式修改,仍然没有结果。
时间慢慢流逝,转眼已经凌晨2点了,就在我几欲放弃之际,突然发现,虽然组件弹出的时候定位有问题,但只要我点掉下面的完成,定位就会立刻恢复正常。

注意,就是那个“完成”。
问题至此已经明朗:在 iOS Safari 里,即使 <input> 设置了 readonly,它仍然可以获取输入焦点。获取输入焦点之后,虽然没有弹出虚拟键盘,但仍然是待输入状态。
此时页面各种交互都是正常工作的,比如点击、滚屏。唯独 position:fixed 定位有问题。点击“完成”离开输入状态,Safari 自动刷新页面元素,定位就正常了。
于是我在组件弹出后,自动 input.blur(),使其失去焦点,组件的尺寸便正常无误了。
移动端 Web 开发总有各种各样稀奇古怪的问题。有些好解决,有些不好解决,比如这个问题,很难定位:
我能想到的方案,就是想办法,用所有能用的工具,排除掉所有其它问题,最终还是能搞出来的。

其实上次的游记还没写完……
最早动来日本的念头是前几年,人民币汇率喜人,大家冲到日本买买买。不过那时候前司业务蒸蒸日上,确实也没有太多时间,拖着拖着拖到2015年,当时觉得时机合适了,还发了招募贴。结果年底查出来糖尿病,老婆脖子上也发现个瘤子,于是自然就放弃了。
后来经过大半年的治疗调理,我俩都算渡过难关。于是国庆期间,去了九州。
前两天种种因缘际会,被我发现海航往返札幌840的特价机票,于是没忍住,剁手,便有了这次旅行。
这次旅行用的基本是2015年做的攻略,没料到赶上札幌雪祭、日本国庆节,酒店非常难定,机票省下的那点钱全交给酒店了。不过反过来说,确实住得不错,比九州那趟强多了。房间基本摆的开行李,大多数都有专门的温泉,泡汤泡得非常舒服。
北海道的冬天没有想象中的冷。也许是两年多没在北京,有点忘记北方冬天的感受,衣服带太厚,其实根本用不上。这边的雪不是一般多,每天都在下,有时候多有时候少,下一会儿就放晴,过一会儿又是鹅毛大雪纷飞。可能正是因此才会有“粉雪”的美誉吧。
小朋友似乎还太小,只爱疯玩傻玩,换言之,找坨雪,就够他玩 high 了。反倒是我们细心安排的行程,比如逛动物园、看雪灯,他觉得兴趣索然,无法全身心享受。让我有些失望
玩耍过程中,心心念的,却是上班。如今离职2个半月,回顾刚离职时定下的计划,完成的不到一半,让我不禁对前途有些忐忑。出来玩很开心,但是玩耍中处处需要钱——还需要不少钱。想到将来更多想去的地方,不由得暗暗怀念起有班上的日子。
回去之后好好工作,嗯嗯。

如题,暂时不确定是哪里导致的。
总之,在 Electron 的 main process 里调用七牛云 SDK qiniu.io.putFile(),会启动一个新实例,原本的上传会暂停。这个时候关掉新实例,上传会继续。当前文件上传完成后,下个文件又会启动一个新实例。如此反复。
文档中的代码如下:
qiniu.io.putFile(uptoken, key, localFile, extra, function(err, ret) {
if(!err) {
// 上传成功, 处理返回值
console.log(ret.hash, ret.key, ret.persistentId);
} else {
// 上传失败, 处理返回代码
console.log(err);
}
});
已开 issue。
估计要等春节后修复了。
暂时可以用社区版 SDK 先顶上。

前几天开发 Meart 的过程中,碰到一个棘手的问题:
因为我对 Electron 和 Vue 都不是很了解,所以这个问题困扰我很长时间。我尝试了各种办法,包括 .splice(),let data = this.data; this.data = data;,但是都没有效果。后来实在没办法了跑到 Vue 论坛里发帖求助。
没想到很快就得到回复,对方虽然没用过 Electron,但答案看起来方向是对的:remote.getGlobal('var') 得到的对象不是一般意义上的对象,Vue 没有办法给它加上 getter/setter,所以无法实现响应式。
我尝试了一下果然如此,用 require('data.json') 替换从主线程中取值,就一切问题都解决了。或者把 .getGlobal('data') 取出来的值,深度 Object.assign({}, source) 一下也可以。
看来有必要补一下 ES5 里面 Object 新增函数的知识了。

FontAwesome 提供了很多好看的图标,使用 WebFont 嵌入页面更是简单又好用,所以我基本上一直用它。不过有时候还是觉得不太够用,这就需要复合使用多个图标。
下面是个例子,我在图标的右下角增加一个圆形加号,表示增加、创建。
See the Pen add icon by Meathill (@meathill) on CodePen.
这里有两个选择:
fa-stack,可以堆叠任意多的图标。:before,所以也可以使用 :after 来容纳新增的图标。如果只需要两个图标,这个方式更简单。
前天从台北回到广州,准备过年。这是我第四次游台湾,原本计划一家五口一起,后来老婆家里出事,没走成,变成我带二大一小出游。运气不错,大部分行程都符合甚至超过预期,顺顺利利开开心心玩完九天八夜,回到家,总结一下。
有些朋友问,“你都去几次台湾了?”“为啥老去台湾啊?”,这里也算一并作答。


台湾有很多秀丽的风景,比如清水断崖的碧蓝海水,合欢山的瑰丽雪峰。这些风景可能说不上举世无双艳压寰宇,但也绝对值得一去,值得一看。
所谓“宝岛”,绝非浪得虚名,台湾全岛,旅游资源开发到位,无论去到哪里,都可以轻松安排下三四天的行程;如果跑在路上嫌累,也有很多地方可以呆着度过几天闲适的时光。


国内的自然风光当然不逊海外,然而把人文的软硬件都算上,国内就没啥优势了。
先说硬件,台湾景区公路发达,规格高设施全;园区内道路平整,无障碍设施齐备,厕所干净无异味有卫生纸。文明社会应有尽有。
再说软件,台湾收费的地方并不多,即使收费,多半也并不昂贵,景区门票几十块,里面基本上不再二次收费。吃饭更是让人安心,价格可能比市区稍贵一些,但绝对在合理范围内,不用担心被坑被宰。
PS:目前我觉得性价比最低的是阿里山,以及阿里山小火车……

其实这点才是最重要的……
大约因为离得近,而且去的人相对较少(尤其是最近),机票相当便宜,单人往返含税从广州香港走大约不到1.5k。
住的话台北台中大城市不会太便宜,价格基本参考国内同档次的各级酒店,丰俭由人;旅游区多半住民宿,价格就很亲民了,400、500就能住的不错,700、800就很好了,愿意多花点钱的话,1000+可以住非常大或者非常有特色的房间,配以非常漂亮的窗外景色。
吃饭也不贵,低端盒饭十几块,高端点的20、30就能吃饱,而且菜品质量我觉得要更胜国内。喜欢吃就换着花样找不同饭店去尝试,大可以放心点菜,我觉得价格远低于国内。

考试和补习班是海峡两岸学子共同的命运
与国内各种管制不同,台湾是自由社会,尤其在文化方面。我去台湾必做的事情之一,便是逛漫画书店。这里要插一句,诚品书店当然值得一逛,不过买过几次书都比较失望,比如会竖排版,或者翻译质量很差,所以我还是更喜欢杂志疯这种主营漫画的书店。
因为自由,所以就不必摆出道貌岸然的样子去扫黄,所以就会有18x的书架,每每令人流连……不过都好贵啊……

我老婆喜欢去买衣服,貌似比国内便宜,而且服务很好,售货员会很认真的帮你挑选、搭配衣服,所以一不小心就买很多。
我大姨子每年去龙山寺许愿求签还愿。说到这个求签啊,目前求了几次都很灵,很灵很灵,全中。
我爸感慨:“国外月亮确实比较圆啊……”
我妈:“还是有钱好啊,可以出来玩。”
好,大概这么多。未来,我想还会保持一年一次的频率。有兴趣去台湾的朋友可以联系我,我可以帮忙推荐路线和包车司机。

对比国庆去日本,台湾还是性价比更高,订酒店的时候就各种觉得便宜。蔡英文上台后,与大陆互相看不顺眼,造成陆台关系紧张,直接影响了陆客前往台湾旅游。打车的时候,出租车司机都在骂。
这个世界,仍然在按照自己的规律发展。人类的道德,主观期望,在这规律面前,不值一提。所以即使我喜欢台湾,我也不得不承认,大陆更有希望;游玩几日之后,必须回到大陆,继续忍受糟糕的空气、脑残的司机、脏兮兮的环境,一边尽力工作,想方设法把生活过得更好。
希望台湾能一直坚持这样美好下去。希望大陆能学习改造,变得美好。

Subway
最快的方法
var fs = require('fs');
fs.createReadStream('test.log').pipe(fs.createWriteStream('newLog.log'));
改进使其可以接受回调
function copy(source, to, callback) {
var read = fs.createReadStream(source);
source.on('error', function (err) {
done(err);
}
var write = fs.createWriteStream(to);
write.on('error', function (err) {
done(err);
}
write.on('finish', function () {
done();
}
read.pipe(write);
function done(err) {
if (err) {
throw err;
}
callback();
}
}
继续添加 Promise,并且用 ES2015 的写法
function copy(source, to) {
return new Promise( resolve => {
let read = fs.createReadStream(source);
source.on('error', err => {
throw err;
});
let write = fs.createWriteStream(to);
write.on('error', err => {
throw err;
});
write.on('finish', ()=> {
resolve();
});
read.pipe(write);
});
}
// example
copy('a.txt', 'b.txt')
.then( () => {
console.log('copy success');
})
.catch( err => {
console.log('copy error: ', err);
});
来源:StackOverflow

这两天直播写 Meart 有点上瘾,博客好久没更了,公更私更都没更。刷推看到这则公告,小更一下吧。
图文无关,马上又要去台湾了,翻出来一张台湾公安局的照片阵楼。
Bootsrap 团队宣布 Bootstrap 4 推出 alpha 6 版本
如题。Bootstrap 4 alpha 已经是第六个版本了,我最近一直在用 alpha 5,感觉没有太大问题。
这次更新主要包含三个部分:
这个版本放弃了 IE9,从而可以彻底拥抱 Flexbox(之前的版本中,Grid 多半使用 display:table 和 float 实现,如果要启用 Flexbox,需要单独引用一个 bootstrap-flex.css)。
根据 CanIuse 显示,目前除了 IE 系列,大部分浏览器均已全面支持 Flexbox。
这次修改之后,绝大部分组件均已全面转向 Flexbox。
增加了一批用于调整布局和显示的 class,这样从 Bootstrap 3 迁移也会方便一些。
这块儿我暂时没用到,没啥可说的。
增加了更多的可控样式,可以调整 Grid 布局中的边距。
之前的版本中,导航栏还是个半成品,现在虽然不敢说是“完成版”,单也差不多了。
这个用途可能比较大,不过还没研究。
下次再发布就是 beta 版了,感觉离正式推出不远了。
Staticfile.org 搜索仓库还没更新,文件已经更新了。
感谢原作者和贡献者的努力,让我们得以用上越来越好用的开源框架,节省大量时间和精力,可以集中于更有产出的事情。