日志

  • 技术类问答产品的困境

    技术类问答产品的困境

    今年在开发社区活动的比较频繁,原本给自己定下目标:每天去 SF 上回答一个问题,开始还坚持了几天,后来就荒废了。

    我也分析其中原因。我觉得,我个人的懒当然是问题之一,但是,并非主要原因。主要原因,满屏的问题,很难找到我想答的。这些问题可以归为几类:

    1. 公司让我做个XX系统,求做法!
      答:我会做,50w即可,请把预付款打到我支付宝上。
    2. (贴一段代码截屏)怎么解决啊?
      答:什么怎么解决啊?你要干嘛啊?你贴的是啥啊?又或我甚至知道怎么解决,难道我还把代码敲一遍?
    3. 1+1 等于几啊?
      答:等于2。不过你看眼文档会死么?

    好不容易看到一个问题各方面都还不错,准备点进去怒答,结果发现前面5、6个答案,其中还有2、3个答得蛮好的……

    运气好刷到一个新的好问题,还没人答,赶紧编撰答案。数日过去,纹丝不动……


    所以我就思考,技术类的问答产品和知乎类的有何区别,SF 已经是业界翘楚了,还这幅德性。近日有点想法,记录一下。

    1. 技术类问题多半有一个正确答案

    比如1+1,不管问题傻与不傻,它一定等于2。你非说算错的时候等于3,在技术论坛上会被骂的。所以一旦打开问题,看到珠玉在前,基本上也就没有答的必要了。

    相反,知乎里面,很多问题没有正确答案,比如“如何看待XXX”,“XXX是一种什么体验”,无论前面答案多好,你都可以上去抡圆了灌它三五千字。

    2. 技术类问题领域性非常强

    “如何看待科比退役”,伪球迷如我也可以上去喷两句;“Nginx 如何实现 WAF”,我就完全答不出。所以大众化的问答网站,以“你吃过的最难吃的饭是什么”为核心组织内容,自然不愁没人参与。但是 SF 虽说是技术论坛,实际上技术分门别类差异巨大,搞前端的不好回答后端问题,搞后端的不好回答运维问题,等等。

    3. 技术人员目标明确,互动意愿不强

    很多人去论坛是为了寻求答案。工作中遇到问题,搜索 -> 答案 -> 解决,目标非常明确。所以点过去就看,看到答案就走,所以不爱投票(多半要注册登录)。有些是找不到同类问题,就发个帖子问,问了也不在乎到底有没有解决。


    总结一下,技术社区不好做,任重而道远啊。

  • 新司两月记

    新司两月记

    现在时间过得飞快,一眨眼在贵司已经工作两个月了。这俩月也挺忙的,适应不同的框架,适应不同的开发习惯,适应不同的工作节奏,等等。今天觉得应该总结一下,以便来日回顾,就写个流水帐吧。

    1. 远程工作

    贵司全员远程,老板和几位同事在美国,国内的几位同事也分布在各处。两个月干下来,有一些感想。

    远程的好处:

    1. 不用考虑通勤。据说现在广州地铁也开始全员安检,队伍也排起来了。
    2. 不要求准点上下班。可以自行安排,工作日看场电影也无妨。
    3. 企业用人成本比较低。

    远程的坏处:

    1. 大家工作时间不统一,时常找不到人。
    2. 物理距离太远,沟通不够顺畅
    3. 工资低于京沪深一线标准。

    2. 公司和老板

    春哥是位牛人,以前技术领域不太重合,所以不知道,合作之后发现真是牛。

    跟牛人合作压力就很大,尤其是春哥和 Wei 是完全不同风格的两种老板。Wei 嘴边天天挂着“快糙猛”,一句话“你不用弄那么细说不定明天公司就没了”,强调大干快上,用智商差干掉竞争对手。春哥也是用智商差作战,不过他的风格是靠高端的产品设计,动不动小语言、机器编程,面试的时候就问编译原理,希望能用 JS 实现后端小语言的编译器……

    而且他的产品设计也是高端,动不动就升华到“语言”层面,想的都是超出现有工具链的方案。当然,(严肃脸)我觉得现有的技术方案大多有历史原因和现实原因,不是那么好超越的。——不过,我也非常希望能跟春哥一起超越一把。

    压力大压力大。

    3. 工作状态

    还有一部分压力来源于远程工作。和坐班不一样,远程更依赖于人与人之间的信任,为维持这份信任,不得不付出更多努力。

    坐班的时候,只要人在工位上,干的好一点坏一点,项目进度快一点慢一点,并不会有特别明显的差异——因为整个公司都是这样。那么多人帮忙垫底,还有背锅的各种开会,偶尔划划水摸摸鱼,并不是非常困难。下了班,更是全部时间自由安排,想打游戏打游戏,想看电影看电影,理直气壮,坦坦荡荡。

    远程不是如此。老板当然都嫌进度慢效率低,但是更令他不安的,则是大家每天到底在干嘛。万一有人拿钱不干事儿,简直亏到姥姥家。所以老板就会把进度把任务挂在嘴边,死命的催赶每个人。别人怎样我暂时不知道,对于我来说,压力就很大。我希望证明自己,不要让牛皮落空,所以就必须全天全周准备投入工作,几乎不得放松。

    我感觉现在的压力比坐班时候大多了。

    4. 对未来的希望

    贵司几乎符合我对新工作的一切预期(除了工资……),我希望能跟公司有更好的发展。许几个愿望吧:

    1. 提供设备金,升级下鸟枪
    2. 租工位,一年也能省不少
    3. 年会
    4. 涨工资……
  • 听得懂话的姆伊

    听得懂话的姆伊

    老婆买了几块上点档次的牛排,煎了作晚饭。很好吃,吃到最后还剩一小块,我说给姆伊吧,就当做成就了。老婆说给它也没用,都是一口吞,吃起来都一样的。

    于是我就把姆伊叫到面前,让它坐,然后跟它讲,大意是这是块好肉,要嚼着吃,直接吞的话就没有那么好吃,肉中间油脂的香味就吃不到了。姆伊听了,伸出爪子,我觉得它的意思是:我懂了,快给我吧。

    然后我把肉给它,它真的嚼了五、六下才咽了。看来的确是听懂了。

  • 《Electron + Vue 实战建站工具开发》读者圈开启

    《Electron + Vue 实战建站工具开发》读者圈开启

    今年很努力的做视频培训,收效一般。不过在社区收获了一些声望,还得到一个写书的机会,跟编辑讨论再三,确定下内容:Electron + Vue 实战开发。

    然后呢,也不是我有意拖延,因为很快就入职贵司,一直都很忙,将近两个月,渐渐熟悉了框架,也适应了节奏(希望如此),现在得抓紧时间补书稿了。

    最开始想在知加边写边发,结果知加没挺到现在,所以只好退而求其次,在 SF 开了读者圈——倒不是说 SF 不好,而是他们家的“技术圈”从产品形态上来看,不是非常合适。

    Anyway,欢迎大家加入圈子,您会得到:

    1. 读到这本书全文,并且更早读到
    2. 获得实时问题解答
    3. 得到实时勘误
    4. 得到实体书(具体看出版社给多少了……)

    入圈链接

  • 日元汇率

    日元汇率

    这两天稳定在 5.88 多一些,比国庆前中微跌(一度恢复到6),貌似和去年情况类似。

    猜测国庆前会有一波换汇行情,国庆后日本商家又把钱换回来。

  • 组件化的度

    组件化的度

    加入贵司后,接手前端开发,主做后台产品。按理说,我在前司做了5年后台产品,轻车熟路,熟的不能再熟,这项工作对我来说应该再合适不过。结果进展非常不顺,甚至老板来敲打我,说工作效率太低,需求做的太慢。

    我也不得不承认,就目前来看,自己确实没有达到很高的效率。不过我也得吐槽一下,这是有原因的!不是我不努力,而是现在的架构实在太太太……(我得选个好词,以免伤害到球哥的感情),太僵硬吧。

    (目前的后台架构是球哥基于 Vue 设计开发的组件化框架。球哥是个码力十足,有追求有实力的人,我非常欣赏他,然则这套框架我实在不敢苟同。)

    Vue 最大的优势,就在于它用很低的成本赋予了 Web MVVM 的能力。于是我们这些前端开发者可以用很低的软件复杂度,完成非常优秀的产品。而且,其作者还尽可能的把 Vue 解耦,降低入门门槛和使用门槛,我们不需要上来就面对一大批陌生的名词和概念,几乎可以直接上手实操。

    可惜,与之同期到来的,还有“组件化”这把双刃剑。组件化当然有好处,它方便我们复用代码,提升效率。但也有很多开发者走上歪路——写组件库,用组件库。写组件库当然有好处,比如,是个扬名立万的好机会。用组件库也有好处,可以提升效率,省去很多开发量。

    但是,所有组件库都有一个避不开的槛——颗粒度。颗粒度太细,用起来跟原生 HTML 差不多,意义不大;颗粒度太粗,一旦不合业务逻辑,就会很蛋疼(对,我就很蛋疼,简直每隔几天蛋就要碎一次……)。怎么样来确定颗粒度的粗细呢?只能看业务了。

    2B 开发的特点

    回到2B业务上。2B开发有几个明显不同于2C开发的地方:

    1. 业务流程又长又复杂
      • 2C 业务,比如微博、微信,用户操作简单直接,效果反馈很快
      • 2B 业务,如果某个操作要填10个表单,经过5个环节审批,那就一个都少不了
    2. 客户多付费,必须尊重他们的需求

    这两点之下,会产生一些要求:

    1. 表单复杂,校验项多,灵活性要求高。此时,表单生成器就不再适用;只要,要把可以生成的简化版,和需要人工的复杂版分开。
    2. 表格多,列多,单元格多变。此时,表格组件也不宜复杂,处理好简单的分页筛选即可,剩下的还是交给业务相关的组件。
    3. 表格表单复用性差,很多表格表单都只出现一次,或者有自己独特的需求。此时,组件的优势不复存在,工作量怎么做都是接近的,反倒是原始的做法更清晰直接。

    2B 开发的组件化

    所以,我认为,2B 的产品,不适宜上来就用组件库搭建复杂的框架。相反,用最简单直接的 HTML、JS 元素,加上 MVVM 特性,效果会更好。

    初期

    最合适的组件化方案就是“不要过早组件化”。组件化的颗粒度以增强原始 HTML 元素为主,比如 Typeahead(输入搜索下拉选择)、给 Input 增加通用的校验工具,等等。用几乎最原始的方式开发,完成需求。这样看起来土,但带来的两个非常重要的好处:

    1. 思路直接,符合直觉,开发者不用投入大量精力学习新框架可以凭直觉完成
    2. 颗粒细,方便实现各种业务逻辑。比如表单想哪几个字段一起校验就一起校验,想怎么显示数据就怎么显示数据。

    中期

    这个时候我们应该完成很多需求了,哪些组件使用频繁值得抽象,哪些组件很复杂但是只用一次,大家心理都有数了,就可以开始着手进行组件化的工作。

    不过,这个时候,仍然不需要做什么表单生成器、表格生成器之类的东西。我们要做的,是强业务相关组件。比如,某个表单在多处被重复使用,我们就要想办法把它抽象成组件(一般用到第3次,就要考虑做组件,用到第4次就一定要做成组件)。做出来的组件,只包含这个表单;抽象的目的:统一管理这个表单,方便别人复用。

    抽象成组件的目的,是让今后的开发更成熟快捷,不是为了提炼出一套通用工具集,放到市场上扬名立万。

    晚期

    这个时候我们应该积累了不少组件,足以应付日常需求,可以快速响应快速开发。此时,组件化以重构为主。

    最终,很遗憾,我们并未收获一套普适的通用组。但是,我们得到一套和业务深度绑定的组件库,和建筑于底层,弹性好鲁棒佳的架构。


    这才是我眼中的组件化之道。

  • 尬聊会:第四期实录

    尬聊会:第四期实录

    视频地址 Bilibili

    时间:2017-09-17 22:00
    地点:douyu.tv/meathill
    回看视频:http://blog.meathill.com/tech/galiao-4.html

    0. 试讲 PPT

    https://github.com/orgs/meathill-lecture/

    1. 从学校到工作必须习惯的转变

    有人问:觉得现在的工作比较难完成,怎么办?是不是要换工作。

    职场当中,普通员工和领导之间,基层领导和中层领导,中层领导和高层领导,大多数处于一种荣辱与共的状态,是多和博弈。所以,尽早与领导沟通,重新安排分配工作,对大家都有好处。换工作这里并不合适。

    之前写的一篇文章 开发新人要适应的变化

    2. 学习方法,我学了东西很快忘

    1. 尽量多的做东西
    2. 尽量的关注这方面的内容
    3. 做一些自己的小产品/工具

    3. 编程思维

    有时候需要一个需求,明明不难,却无从下手

    —— 写得少

    把数字转成中文大写,比如 1024 => 壹仟零贰拾肆

    方法同上。

    走出舒适领域。

    4. 老师, 我是半路出家, 非计算机方向。在编程道路上常常会有力不从心。

    1. 可以解决,比如通过阅读、看视频、练习等
    2. 可以忽略,需要我们学会区分知识的边界

    5. SEO 分页还是ssr好

    如果服务器很好,那无所谓;不然的话,静态分页好。

    6. 找工作去哪个网站比较好

    建议大家常泡社区:

    1. https://segmentfault.com/questions
    2. https://www.v2ex.com/

    7. 找工作技术和人力都过了还是没要我,怎么回事?

    1. 有更合适的人选
    2. 这个岗位不招人了
    3. 这个岗位出现了变化
    4. 你感觉错了……
  • 尬聊会:第三期实录

    尬聊会:第三期实录

    视频地址 Bilibili

    时间:2017-08-06 22:00
    地点:douyu.tv/meathill
    回看视频:http://blog.meathill.com/tech/galiao-3.html

    1. 我的第一份工作,兼谈如何跳槽
      1. http://blog.meathill.com/internet/days-in-a-company-first-leave.html
      2. http://blog.meathill.com/internet/days-in-a-company-quit-relax-and-back.html
    2. 为什么尽量避开外包公司?
      1. 对技术要求很低
      2. 对产品的标准很低
      3. 没有办法在一个产品做持续的投入
      4. (大外包公司)很难和采购外包的企业共同发展
    3. 李文星事件的启示:
      1. 天上不会掉馅饼
      2. 了解这个社会的运行机制
      3. 学会算账
    4. 加班好不好
      1. 拒绝制度性加班
      2. 拒绝崇拜加班的领导
      3. 如果总加班,很可能是管理能力太差
      4. 不要盲目拒绝加班
    5. 从小公司跳到上市公司 有可能吗

    6. 大佬前端学习路线大概是什么样

  • 解决 [Vue warn]: You may have an infinite update loop in a component render function

    解决 [Vue warn]: You may have an infinite update loop in a component render function

    今天写着写着,突然发现控制台里有错误:

    [Vue warn]: You may have an infinite update loop in a component render function
    

    这个问题很奇怪,之前从来没有遇到过。如果是我自己主导的项目,倒也好办,慢慢 debug 就是;偏偏在公司的项目里遇到这个问题,而公司项目的体系结构很复杂,我还没完全掌握。更恼火的是,因为体系复杂,debug 也非常困难,再加上尚无测试框架,这个难搞啊……

    好死不死的,当时是下午3、4点钟,正好到了肚饿的时刻,结果又落入低血糖状态,真是屋漏偏逢连阴雨,船小又碰顶头风,饿得我脑仁生疼……

    不过终于还是被我 Google + debug 出来。事实上是这样的,在 v-for 循环当中,如果用方法或者计算属性对 vm.$data 的属性进行操作,理论上,可能因为修改到循环对象,诱发无限循环。此时 Vue 就会发出警告(并不是真的已经无限循环了)。

    例如这样一个组件,它里面是用 :checked + <label> 实现的一组按钮。它有以下功能:

    1. 为了能够分组,需要设置它们的 name 属性
    2. 为了能够用 <label> 控制 <input>,需要给 <input> 设置 id
    3. 按钮可以被删除

    于是我选择这样做:

    <template>
    <div>
      <template v-for="(item, index) in items">
        <input type="checkbox" :name="'my-component-' + selfIndex" :id="getID">
        <label :for="getID(false)">
        <button type="button" @click="remove(index)">&times;</button>
      </template>
    </div>
    </template>
    
    <script>
    let count = 0;
    
    export default {
      data() {
        return {
          selfIndex: 0,
          itemIndex: 0,
        }
      },
      methods: {
        getID(increase = true) { // 注意,问题就出在这里
          if (increase) {
            this.itemIndex++;
          }
          return `my-component-${this.selfIndex}-${this.itemIndex}`;
        },
      },
      beforeMount() {
        this.selfIndex = count;
        count++;
      }
    }
    </script>
    

    这里,为了能生成唯一 ID,我选择每次循环都对 vm.itemIndex++,这就会出现前面说的问题,存在隐患。

    解决的方案有两种,一种是把 itemIndex 也放在局部变量里,使它不直接关联在组件上;另一种则是写一个全局的唯一 ID 生成函数,然后引用进来。原理都是一样的。


    这两天听评书《乱世枭雄》,学到一句话“拉屎脸朝外”,形容讲义气,不知道咋联系的……

  • MediaElement 笔记

    MediaElement 笔记

    贵司官网需要放视频,于是需要用播放器。然后就产生如下需求:

    1. 协议好,支持免费商用,最好 MIT
    2. 支持 SRT 字幕
    3. 支持尽可能多的平台

    经过筛选,最后选定 MediaElement,一看 WordPress 在用基本就放心了。然后就开始踩坑之旅。当然,有些是我的问题,不过也有不少是文档没说清楚。这里记录一下。


    全屏和弹性宽度

    当前版本 4.2.5 有个 Bug,如果设置宽度为 100%,那么从全屏恢复到内嵌状态时,会留着全屏的宽度,撑开页面。如果设定一个特定的宽度就不会。但我们的页面是响应式的,需要允许它随页面变化而变化。

    这个时候只好手工来做,我的选择是让它 100%,然后侦听从全屏返回的事件,发生后就把宽度恢复。另外,因为 fullscreenchange 事件还没有统一标准,所以不同浏览器里的事件名称也不一样,更不爽的是,IE 下是驼峰,其它浏览器是全小写,我也懒得再写函数转化了,反正就3种情况,全写一遍好了。

    // fix MediaElement's issue
      let fitVideo = function() {
        setTimeout(() => {
        $('.mejs__container').width('100%')
          .find('video').width('100%')
          .end().find('.mejs__layers')
          .children().width('100%');
      }, 50);
    };
    if ('onwebkitfullscreenchange' in document) {
      document.onwebkitfullscreenchange = function() {
        if (!document.webkitFullscreenElement) {
           fitVideo();
        }
      };
    } else if ('onmozfullscreenchange' in document) {
      document.onmozfullscreenchange = function() {
        if (!document.mozFullScreenElement) {
          fitVideo();
        }
      };
    } else if ('MSFullscreenChange' in document) {
      document.MSFullscreenChange = function() {
        if (!document.msFullscreenElement) {
          fitVideo();
        }
      };
    }
    

    默认字幕

    Web 标准是有默认字幕的,<track defualt> 即可。但是 MediaElement 没有,最后在 StackOverflow 上找到答案,原来有个未记录在文档里的初始化属性可以控制:

    let player = new MediaElementPlayer('intro-video', {
      startLanguage: 'cn', // 这里的名字应该和 <track> 种的 srclang 属性一致
    });
    

    插件与生态

    MediaElement 把一些有用但不太常用的功能抽出来做成了插件,放在 MediaElement Plugins。不过看起来这个项目有些属于维护,使用的时候很多问题。比如,直接在 features 里开启特定功能,即使对应的插件没有加载,也不会报错,就是没反应,很令人迷茫。

    避免发起又取消请求

    网站上线后,老板发现一个问题:从 Chrome 的 Network 面板可以看到,网页打开后,对视频文件发起了一到多个不等的请求,并且都取消了。

    经过研究,我认为这个请求是 <video> 发起的,因为 preload 的默认值是 auto(参见:MDN),也即打开页面就预加载。而此时 MediaElement 被初始化,为了正确显示 UI,它会把 <video> 标签挪到自己创建的 <div> 里,这个过程就会导致“预加载 -> 取消”。因为视频默认不播放,所以我给 <video> 加上了 preload="none",问题解决。


    其它大体上文档都能找到,就不多说了。