Chat idea:记一次 Firefox 下 Vue 带来的性能危机的解决

前两天遇到一个问题:我厂的一个产品在 Firefox 下,可能发生因为 CPU 占用过高而卡死的情况。这个问题在测试环境不复现,在 Chrome下基本上也不会复现。

因为我厂老板是这个产品的主要用户,这个 bug 让我倍感压力,但一直没有解决它的好办法。终于有一天,在某台生产机上调试另外一个 bug 的时候,我终于发现了稳定复现这个 bug 的方式。

接下来就是几个小时的 debug,然后发现问题所在,然后解决问题,然后发现解决方案不理想,于是寻求新的解决方案,然后找到新的 API,最终彻底的解决这个问题。

接下来我就分享这个过程,读完整篇文章当中你将学会:

  1. 使用开发者工具查找性能问题
  2. 不断切分,缩小问题范围
  3. 理解 Vue 响应式原理分析问题根源
  4. 修复问题并验证
  5. 新的解决方案 Intersection Observer
  6. 解决问题并上线

目标读者:

  1. 中级开发者
  2. 熟悉原生 JS

大家觉得这个 idea 如何?请留言告诉我。不出意外的话这将是我下个月的 gitchat 内容。

Stylus 实现 `content: “5”`

一般来说 Stylus 对属性是直接替换的,所以正常来说下面的 stylus

$a = 10

.foo::before
  content $a

会编译成:

.foo::before {
  content: 10;
}

这样是非法的,10 不会渲染出来。如果想让它渲染出来,必须用字符串。如果只有一个值,那就好办了,直接 $a = '10' 就好。但如果在循环里,就比较麻烦,此时,可以用 '' + 10 转换成字符串,或者使用 s(template, value),以下两种方式是等效的。

// 注意运算顺序哦
.foo
  for n in 1..5
    &:nth-child({n})::before
      $str = '' + (5 - n)
      content $str

  for n in 1..5
    &:nth-child({n})::after
      $str = 5 - n
      content s('"%s"', $str)

最近折腾 @babel/preset-env 的一些小心得

近来厂里的项目越来越多,代码共享必不可少。我现在采取的方案是:

  1. 把公共组件拿出来,开一个新仓库
  2. 使用 webpack 进行打包编译,libraryTarget: 'umd'
  3. 将打包编译的代码一起提交到仓库
  4. 使用 npm i <owner>/<repo> -S 安装依赖,因为我厂的仓库均为私有,所以不能发布到 NPM

这套方案简单好用,实操效果良好。接下来我希望优化打包结果,于是研究了打包配置项,下面是我的一点心得。

继续阅读“最近折腾 @babel/preset-env 的一些小心得”

如何加入一家全职远程的公司

自从我厂开启招聘之后,陆续收到一些简历。除去一些明显不靠谱的,也有不少同学的简历看起来很好,但明显只是为“远程”而来,满脑子都惦记着远程之后坐在海边一边打望比基尼美女,一边啜饮鲜榨果汁,一边写代码的生活。却没有想过自己能给公司带来什么。

没错,我也曾这么幻想着,加入了我厂。加入之后的工作生活虽然称不上幻灭,但绝对和预期不同。所以今天,我决定写点什么,让大家了解全职远程的真实状态,同时理解远程的需求,以便更好的抉择今后的职业道路。

以下的内容不仅包含远程,还有“小型创业公司”。我还没见过中型或更大的“全员远程”公司,所以姑且认为这种组织形式和大多数老板的管理水平,暂时无法负担更大规模的协作吧。

继续阅读“如何加入一家全职远程的公司”

使用 Proxy 添加魔术属性/方法

使用 Proxy 实现魔术属性/方法。

最近在开发我厂的 QA 工具时,遇到一个问题。我需要模拟 Puppeteer 的所有方法,以便兼容原先的 JS 文件。Puppeteer 提供一个 .asElement() 方法,可以把函数执行结果转换成一个伪 DOM Element(如果函数返回的就是 DOM Element 的话),然后我们就可以在 Node.js 里调用原本属于 DOM 的方法,比如 .focus()。Pupputeer 会替我们完成映射和函数调用,并且返回结果。

对于大部分对象来说,我只要模拟对应的属性、方法,然后用自己的函数实现功能即可。但是 DOM Element 有上百个属性和方法,手工实现一遍实在太低效了。必须寻找其它途径。

好在我之前看过 Proxy 的介绍,赶紧翻出文档和书又复习两遍,就大概知道怎么做了。

Proxy 类如其名,可以“代理”对某个对象的访问。你可以把他理解成明星的经纪人。明星成名之前都是自己处理一切事务,有了经纪人之后,大部分事务就由经纪人负责,但仍然有一些事情需要明星自己处理。

Proxy 的用法很简单,实例化时,把要代理的对象传进去,定义一下代理方法就好。

const obj = { name: 'meathill' };
new Proxy(obj, {
  get(target, property) {
    // 如果对象中有要求的属性或方法,则返回
    if (property in target) {
      return target[property];
    }
    // 没有的话,进行其它处理
    return 'hello';
  }
});

obj.name // 'meathill'
obj.age // 'hello'
obj.sex // 'hello'

接下来,比如我们访问 obj.foo,那么代理就会生效,它会先检查 obj,如果这个对象上本来就有 foo 属性,就会返回;如果没有,则会调用我们定义的方法来处理。

如此一来,我们可以定义一个 VElement 类,这个类可以实现一些特殊方法,比如 .type(str) 输入,.click() 点击等;然后用 Proxy 代理其它方法和属性,让对象进入插件 Context 执行。


Proxy 还有其它方法也很有用,尤其是 get 对应的 set ,以后再介绍。大家可以自己抽空研究下。

参考

  • 阮一峰的 ES6
  • 《深入理解 ES6》

近期用 webpack-dev-server 作代理的一些经验

使用 webpack-dev-server 代理远程服务器;修改 http header 以便本地登录;代理 WebSocket。

如今前端开发使用 webpack-dev-server 作为本地服务器已经是基本配置,加上 proxy 功能可以很好的应对 SSL、跨域、线上环境切换等需求。Vue CLI 3 里也做了相应的集成,用起来很方便。

继续阅读“近期用 webpack-dev-server 作代理的一些经验”

2018 WeGeek 小程序 Hackthon 记

参加了 SF 组织的小程序 Hackthon 活动,收获很多,希望将来还有机会。

某天,行政找到我:“你今年的年假还剩7天,只有5天能保留到明年,建议你找时间把那两天休掉。”我正在盘算怎么用,突然就在 SegmentFault 上看到12月15-16日要在北京举办小程序 Hackthon 的消息。作为一名程序员,我其实早就想参加类似的活动了,所以,干脆就来吧。

因为最近跟蛋东剑剑一起搞的东西比较多,而且他们俩单身,比较好约,所以就拉了他们组队。

初选很顺利的通过了,然后我就定了日程、机票和酒店。

这次 Hackthon 的题目提前一周公布。我们简单商量了一下,既然没有更好的想法,不如就把我之前计划的“姆伊读书”,又叫“以后再听”做出来,感觉很能呼应小程序的主题。而且这个需求来自于我的真实日常,即使不得奖,也能收获一个有价值的产品,何乐而不为呢?本来我想抢跑来着,结果赶上老婆孩子一起生病,公司的正事儿都干不完,只好到了现场才开始写。好在我对项目比较有把握,对自己的能力也很有把握,所以最后提交的 MVP 完成度还不错。

做的时候他们表示对姆伊没有感情,不想跟狗绑定在一起,所以改名作“换听”。

结果仍然没能得奖。不得不说,这种提前公布题目的做法确实值得商榷,很多团队一看就知道是先弄了五六成,现场只搞拼装、联调,产品复杂度超过我们很多。另外评委的指导原则也有些迷,一等奖还算符合小程序的场景没啥可说,二三等奖其实都不适合用小程序来实现,独立应用才有价值。实在是为小程序而小程序。

我觉得,要是张小龙在现场的话,我们赢的概率要高很多 XD。

不过抱怨归抱怨,我倒也乐意接受这个结果。规则是人家定的,过程也公平公正公开,还不收钱,还提供盒饭,真心很感激。尤其是,在这次活动的激励下,我终于把早早就翻来覆去想了很久的产品给做了出来。而且,确实好用,我现在已经用得停不下来了。

另外最后的展示和点评也收获很多,你能发现很多人,很多不同的视角,你可以试着站在别人的角度看问题,学习别人解决问题的思路。收获很大。我觉得程序员都应该参与类似的活动,因为有机会把自己的 side project 从 idea 转化为实物。我觉得未毕业的有志于从事 IT 研发事业的毕业生也应该参与类似的活动,可以学到很多从立项、到产品规划、到实现的知识。

频率嘛,我觉得每年参加一次吧,哈哈。

继续阅读“2018 WeGeek 小程序 Hackthon 记”

BaaS 碎碎念

暂时没时间写完整,零散记一些吧。

BaaS 的核心其实在于 ACL。

因为 BaaS 把获取数据的机制下发到客户端,所以 BaaS 和传统应用最显著的差距就是在哪里处理数据的可视性问题。传统应用里,哪个用户看到哪个数据是后端处理的。而 BaaS 里,则是前端、后端、ACL 一起确定的,而且通常情况下,ACL 设计的好,前端后端的验证步骤都可以省去。

小程序云不支持后台,无法使用。

在 Pug 模板中使用变量

如何在 Pug 里,把变量注入父模板。

我厂使用 Pug 作为 HTML 的预编译工具,写久了发现回不去了……Pug 写起来很高效,看久了习惯了阅读效率也很高,读了读文档,发现还有很丰富的可编程特性。于是我决定抛弃 Handlebars,以后都用 Pug 写模板了。

这次我厂官网改版,我就用 Pug 替换了 Handlebars,于是 build 脚本一下少了几十行,非常爽。其实我早就想这样做,不过卡在一个点:使用变量。

在 Pug 里可以这样使用变量:

- var name = 'meathill'

h1 hello #{ name }

以上代码将输出 <h1>hello meathill</h1>。这种用法比较简单,不过在 extends模板的时候,把变量放到哪里就不知道了,官方文档也语焉不详,我反复试了很多次无果,最终还是靠搜索找到了答案,原来要这样:

// Layout.pug
html
  block vars
    p 注意,这个 block 是重点,它出现在前面,用来注入变量
  head
    title #{ title } | My site
  body
    block content

// page.pug
extends Layout

block vars
  - var title = 'A blog'
  
block content
  h1 Blog title
  p blog content