Chor

Chor 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织 chorer.github.io/ 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

Chor 发布了文章 · 1月14日

Chor 的 2020 年度总结 | 2020,再见;2021,你好

当意识到需要为 2020 年写一个年终总结的时候,已经是 2021 年 1 月 4 号了。不过没关系,年终总结虽迟但到。我的年度总结可能和别人不太一样,不会有文采飞扬的标题和笔墨,也不会谈太多技术相关的事情,关键词只有两个:经历想法

学业 | 学校和课程

上网课的体验

无论谁也不会想到,开年不久就爆发了疫情。今年的大部分时间都是在家里度过的,上课也改为采用网课的方式,一开始很难适应,但过了一段时间之后我发现自己很喜欢这种线上教学的形式,因为它带给我某种程度上的自由,我有更多时间去决定自己学习的方式。此外,线下教学的缺陷在于它是一次性的,有些知识点错过了就很难再补全,但线上教学因为有录播的存在,我们可以反复地回顾上课的内容。

今年上半年基本都是各种计算机专业课:操作系统、数据库、编译原理 ...... 说是网课,但大部分都是自学为主。一个学期的时间其实是很难将这些体系化的东西讲全、讲清楚的,因此我们所接受到的实际上是经过“删减”的知识,而且有些老师提供的网课视频就是照着 ppt 读稿,没啥意思,不如自己课后去找资料、查漏补缺来得高效。上半年大概写了十来篇笔记,都是和课程相关的,逐渐喜欢上了这种电子笔记的形式,就呈现效果和后期修改的便利程度来看,手写笔记无法与之相比。其实也多亏了网课这样的形式,给了我更多充足的时间去进行学习方式上的探索。

下半年的课程听起来很高大上:机器学习、计算智能、创业基础……实则很多是在浪费时间罢了,我不是很感兴趣,这里就不谈了。

疫情期间顺便把素拓分水完了。虽然这东西没啥用,不过不修完没法毕业,于是马上开启了“疯狂蹭分”模式 ……

告别淼城

最遗憾的事情,也许是疫情正好赶上了我们在三水的最后一个学期。当 9 月份再次回到三水的宿舍时,已经是打包行李准备搬校区的时间了,突然就要离开生活了两年的校区,内心还是挺不舍得的。虽然学校地理位置颇为偏僻,人烟罕至,但我很喜欢学校的氛围,在这里能够静下心来去思考一些事情。如果让我说学校最喜欢的一个地方,应该是图书馆前的长椅。有时候经过那里,会在长椅上坐一会,看着眼前一片平静的湖水,听着耳畔传来的鸭叫声,感觉再焦躁的心情也会慢慢变得平和。这样让人可以放松的“公共空间”,在广州校区这边并不多见。

图书馆我去得比较多,一直喜欢坐在靠窗的位置,有风吹过的时候,很舒服。

写作 | 公众号和技术写作

今年输出的文章

我的公众号是 19 年 10 月份开通的,由于开通时间太晚,没有留言功能了。我能理解这是进行舆论管控的一种手段,但没办法和读者进行互动还是挺蛋疼的。微信官方在 8 月份的时候曾推出“读者讨论”的功能,我一度以为这是某种程度上的补偿措施,可惜的是该功能在 11 月份的时候又悄无声息地消失了。

19 年的时候,公众号只发了一篇文章,之后就一直没有打理。今年上半年决定把之前的博客慢慢迁移过来,截止到目前,公众号大概是发了 49 篇文章,这期间也做了一些宣传工作(其实就是在一些推文群发发文章而已)。粉丝数量则是 150 人,这个数量自然是很少,不及那些知名公众号的百分之一,不过“万丈高楼平地起”,所有的东西都是慢慢累积起来的,要学会耐得住性子。

贴一下今年的文章汇总:

除了几篇技术性没那么强,大部分文章我都是投入很多时间和精力去创作并完善的,力求做到结构清晰、语言通俗易懂。一开始我还怕有的地方写得不够好,比如用词是否准确?表述是否精简?不过有几个网友表示从我的文章里得到了不少收获之后,我也就放心了。这是对我的文章很大的肯定,这里很感谢他们。总的来说,今年在技术写作上的输出比我想象的要多很多,没什么遗憾了,很满意。

写文章的原则

我的公众号基本是不发转载的(目前只转了一篇)。比起自己辛辛苦苦地去写,转载有时候真的要容易得多,但东西始终是别人的,拿别人的东西喂饱自己,我不喜欢这种方式。我做公众号也有自己一直在恪守的原则:

  • 我所选择的创作类型:坚持以原创和翻译为主,不管是哪一种类型,我都会倾注时间和精力去创作;
  • 对于“原创”的理解:所有翻译的技术外文都是二次加工的东西,一律不会且不应该标注为原创。我并不认为你参与翻译了,这就是你的东西;
  • 对于“标题”的选择:我的标题一直是平平无奇的,但能直观反映主题,今后也会是这个风格。至于原因:我反感所有的标题党 —— 无论是谁,并且我不想成为这样的人

当然,还有很多不足之处需要改进。比如我翻译的文章基本都是没有征得原作者许可的,不是因为作者拒绝了(从以往的经验来看,大部分的国外作者是很友善的),单纯就是因为我懒、没有去要授权而已,这个事情我觉得可以做得更好。另外我的文章里有不少都是早期学习做的笔记,不排除会有大大小小的错误,这些也是以后写文章需要需要注意的事情。

今年也加了不少推文群,发现无论是在哪里,都有各种标题党存在:夸大事实、蹭热度、贩卖焦虑 …… 不同的只是这部分人所占的比例。综合下来还是腾讯云社区的氛围比较好 —— 千万不要认为这是在打广告,我只是在陈述事实。腾讯云社区有更多用心做公众号的人,不少技术圈大佬是坚持在输出原创优质内容的,非常之勤奋。另外官方对于文章版权的保护也相当重视 —— 印象比较深的一次,有好几个作者的文章在未授权的情况下被某个站点搬过去了,经过社区交涉后当天就被撤了下来。

网络 | 网上趣事记录

1024 圆桌会议

10 月份的时候掘金社区有一个《1024 圆桌会议:JavaScript 的过去、现在和未来》的线上直播,主要聊到了 JavaScript 的发展历程、TC39、技术翻译等话题。当时在飞书群问了一个困惑已久的问题:“在进行技术翻译的时候无法完全理解作者原意,这种情况下如何避免翻译可能造成的误导?”很幸运得到了红宝书(《JavaScript高级程序设计》)译者李松峰老师的解答,颇有收获,这里也做一下记录。

  • 老师给的第一个建议是直接问作者本人,这可以说是最简单有效的,并且足以打消一切由于对翻译结果不确定而产生的疑虑;
  • 第二个建议是咨询更专业的人。其实我有时候在翻译的时候拿捏不当,也会问群里的朋友,有掘金翻译计划的大佬,也有教英语的老师,还有本身就是职业做翻译的。多聆听别人的见解,多几个揣摩和理解作者的角度,翻译的时候卡壳了,一定不要一个人在死胡同里打转;
  • 第三个建议我认为是最受用的,偏向于方法论,实操性更强,那就是寻找平行文本。平行文本指的是与原文内容接近的任何参考资料(包括译入语材料和译出语材料),当不确定用词、表达甚至是结构的时候,求助权威文章,汲取长处,从而去完善译文,所谓的“他山之石,可以攻玉”讲的就是这个。简而言之,将平行文本应用在我们的译文中可以实现“内化”。

其实有不少已经在实践中用上了,比如在碰到一些陌生的领域内术语时,参考其它文章来获得翻译上的灵感,这就是平行文本的应用。

比较意外的是,贺老(Hax)也借着这个问题的契机解释了“饱受诟病”的 Promise 翻译的由来。对于将 Promise 翻译为“期约”这件事,社群里听到更多的是反对的声音,甚至正在阅读这篇文章的你,也可能无法接受这个译名。“用了这么多年的叫法,为什么突然就要改了?” 但很多时候,这种反对不是源于内心对原则性错误的抵触,仅仅只是因为拒绝改变自己先入为主的观念罢了。正是因为看到了这一点,所以作为译者,是有责任在众人的不理解中去推动这种改变的,这是属于他们的责任感。

稳固不变并非就是好事,很多事物是需要经历一个从无到有的过程的 —— 正如贺老所说的,“这么多年过去了,Promise 也应该有一个属于它的译名了”(原话忘记了,但差不多是这个意思)。

后面也拿到了李松峰老师亲笔签名的红宝书。第四版我是早早就买了,但是这一本赠送的会更有纪念意义 —— 不仅是技术上的,也是翻译上的。

《互联网中“讨论”的消亡》线上沙龙

之前收到了机核网 RSS 推送的一篇文章:中文互联网中“讨论”的消亡,文章很长,但耐心读完后,感觉一针见血,剖析得很到位。原文来自公众号@沙丘研究所,后来报名参加了他们在 Zoom 上开的一期线上沙龙,主要谈到了“互联网”、“结构搭建”、“公共领域”、“优质内容传播”等话题。整场沙龙听下来,我属于比较懵逼的状态,可能有太多领域内的名词都听不懂了。提前补充相关知识很重要,这能让我们在聆听演讲的时候有更加完好的体验。不过好在有文字稿,油管上也有录播,可能以后再回过头来品味这期沙龙,又是不一样的感觉。

掘金翻译计划

除了自己翻译感兴趣的文章之外,今年也依旧积极地投入掘金翻译计划的校对工作中,校对了 22 篇译文。目前来看,掘金翻译计划不如之前有序和高效,issue 堆积未处理的问题虽然有所改善,但多少还是存在,而且有不少译者或者校对者领取任务之后人就消失了,虽说翻译全是出自自愿,但这样的行为毫无疑问是会给别人带来麻烦的。再者就是我进行校对的时候,发现有不少人为了求快,在语句的斟酌以及资料的考证上不假思索,造成的后果就是读起来不够流畅,且增加了读者理解的成本。希望这些问题能重视起来并逐步改进,这样才能真正成为一个最懂读者和译者的翻译平台。

一些有趣的网友

这一年和 @情封(可能大家都认识了,公众号“前端早读课”的负责人)有过几次印象深刻的聊天,我们谈论的话题还挺多,包括产品设计、社区建设、用户体验、公众号运营等。中文互联网环境在我眼里一直是浮躁且忙碌的,而在这样的环境下,两个素未谋面的网友仍可以腾出片刻的时间进行一次讨论,互相交流意见、启发对方,我一直认为这是一件相当难得且值得庆幸的事情。情封是一个很有想法也很有态度的人,和我遇到的大部分网友都不一样,和这样的人聊天是能够学到很多东西的。

此外也认识了一个正在做 UGC 社区的朋友。他正在做的是类似掘金或者思否那样的技术文章创作社区,社区仍处于起步阶段,因此我们几个内容创作者就加入了相关讨论中,互相贡献自己的一些 idea。聊到的话题包括长内容与短内容的创作形式/编辑方式,markdown 编辑器使用体验的优化等。在讨论的过程中,我能明显地感受到决策的过程比想象中的要更加复杂和漫长,不过这也正说明他是一个思考问题相当全面的人,这样的人行事是非常谨慎的,会系统地评估风险、权衡利弊,绝不会一拍脑瓜子就冒然地作出决定。

这件事情也让我体会到社区运营者的不易。一般来说,内容创作社区会有三种核心的角色:运营者、创作者和读者,而大部分时候,我们只会充当其中一种角色,并站在这个角色的角度去发言,但要想对社区作出相对客观的评价,我们就需要学会切换不同的身份去看待同一个问题。简单地说就是要有同理心,心里不要只装着自己。

社区(腾讯云/掘金)的一些礼物

参加活动的时候腾讯云社区送的一些礼物,主要是鼠标垫、文具套装和颈枕(这个超实用)等,好像还有一个腾讯视频会员卡,不过前几天看的时候发现已经过期了 ← ←。希望 2021 年也能时来运转。

参加掘金翻译计划兑换的T恤和卫衣,以及一些贴纸:

卫衣真心很好看,改版后的 T 恤质感也很舒服,后面送了朋友一件。

新的尝试:第一次发视频

起因是在 b 站发现了一个讲解 Event Loop 的视频,原视频是 JSconf EU 大会上的一个演讲,很有名,各位应该看过。有 up 主做了搬运和翻译,前半部分还挺好,后面明显虎头蛇尾,机翻现象明显。看底下有很多人吐槽,突发奇想,为什么不自己做一个字幕呢?好视频不应该被埋没,于是用 Arctime 做了一个中文字幕,过程比想象的繁琐很多,需要反复调整时间轴,非常需要耐心,好在最后效果还是不错的。视频地址在这里:Philip Roberts:到底什么是Event Loop呢?(JSConf EU 2014),如果喜欢的话可以来个一键三连。很多技术大会还是很有意思的,国内的比如 QConf,国外的比如 JSconf、CSSconf 等,后面如果有时间,可能会尝试再翻译几个技术大会视频。

网络安全:记一次网络勒索事件

虽然大标题是“网上趣事记录”,但这件事并不十分有趣。上半年算是“经历了”一回网络勒索事件,而当事人是我的一位朋友,详情可以看这篇文章。这种好像段子一样的事情就发生在自己周边,我的第一反应是满脸的问号:???好在我们都算冷静,看出了这是 fake spam,事情最后有惊无险地解决了。我们在网上真的得注意保护自己的隐私,比如填某些表单的时候如无必要,就别泄露个人联系方式,群里发图问问题的时候关键信息也应该打码(有的人心太大了,自己姓名和手机号码都暴露在截图里还浑然不知),毕竟你不知道群里都是什么人。反正在互联网上一定要多个心眼,避免让自己陷入不必要的麻烦。

娱乐 | 书籍、电影、音乐和游戏

看了几本书

我不是一个很喜欢看书的人,属于一年能看一两本书就不错了的类型。不过比较意外的是,疫情在家期间,我竟然可以静下心来读几本书。或许是因为面对电脑屏幕的时间更长了,看纸质书可以在一定程度上缓解那种疲劳感。

《奥斯维辛:一部历史》

发现我还是比较喜欢看历史类型的书籍。读完《奥斯维辛:一部历史》,最大的感受就是:没有人真的了解自己,人能有多邪恶,不敢想象。在书末有这么一段话:

“人类从内心深处需要这个世界有公道存在,需要无辜的人最终得到补偿,有罪的人最终得到惩罚。但奥斯维辛的历史没有给我们这样的慰藉。”

似乎无论是以前还是现在,正邪善恶都远比常识所理解的复杂很多,尤其是,他们往往不像人们期望的那样有应得的归宿。历史上,那些前脚刚逃离奥斯维辛的人,后脚又陷入另一个更加可怕的地狱 —— 古拉格群岛,而那些曾犯下暴行的人,却有不少掩饰了自己的罪行,从此在人生上平步青云。

《深入解析 CSS》

技术类书籍。无论是《深入解析 CSS》,还是《CSS in depth》,这本书都不像它的书名所说的那样,特别深入,但我认为值得一读,因为它对于一些 CSS 的细节做了非常到位的剖析和讲解,作者会讲到很多我们在实际项目中经常遇到的小问题,并且给出优雅的解决方案,看完之后你经常会有这种感觉:”这个作者知道我疑惑的地方在哪里,他给出了解释,而且解释得通俗易懂“。简而言之,这本书很实在,很接地气。

《JavaScript 高级程序设计》第四版

个人还是更喜欢 Nicholas·C·Zakas 的文笔风格,无论是他写的红宝书第三版,还是《深入理解 ES6》,在阅读的时候你都能感觉到他的逻辑是非常缜密且清晰的,读起来津津有味。当然现在的作者 Matt 写得也挺好,但是一些问题可能会让读者在阅读第四版的时候不是很舒服:比如 Matt 并不是完全按照循序渐进的风格进行讲解,有很多知识点实际上是在后面才涉及,但会突然出现在前面,对此他并没有进行相应的说明,新手阅读的时候可能会有点懵;另一个问题就是一些代码的拼写错误,不是只有一两个,有很多。在翻译上实际也存在一些大大小小的问题,当然,我不认为 Promise 的翻译算是一个问题,正如上面所说的,只是立场不同而已,而且在中文世界里恐怕找不到比“期约”更加贴切的译名了。豆瓣的评论里有不少人说翻译水平一般,估计都是在吐槽 Promise 的翻译,只能说根本没吐槽到点上。

具体的大大小小的问题可以去图灵社区的勘误区查看,估计再版的时候这些问题都会纠正过来。预购的读者可能吃亏点,但是发现问题也是一个进步的过程。

《程序员修炼之道》

相当经典且值得一看的好书,书里讲的不少东西已经成为了公认的最佳实践。在做了一定的项目量之后再去看会有更大的收获,有很多地方可以引起共鸣。

一些纪录片和电影

看了几期《一席》的节目,这个系列给我的感觉是人文气息很浓厚。印象比较深刻的是作家郑执的那期演讲,讲了自己的父亲、作家之路、在东北的一些经历,很让人动容。演讲虽有 43 分钟,但我看完后还觉得不过瘾。作家讲故事的能力真的是一流,简单的语言,却能触碰我们内心最柔软的地方。后面又看了《杀马特我爱你》、《纸工厂》,都是比较出名的几期。

网剧看了比较火的《隐秘的角落》和《沉默的真相》,多少有些牵强的成分,但总体观感很不错。两部都属于短剧,节奏是比较紧凑的,没有什么拖泥带水的地方。想起以前还有一些动不动就七八十集的电视剧,真是想想都觉得头皮发麻。

电影看了姜文的《让子弹飞》(一直听说很好看),杜琪峰的《黑社会》系列,两部电影都属于比较值得玩味的,豆瓣因此还有衍生的“黑社会学”。另外就是“镖客三部曲”,昆汀的《无耻混蛋》,之所以看这几部作品,很大程度上是奔着莫里康内的配乐去的。《无耻混蛋》中,一袭红裙的女主中枪的那一瞬间,血花四溅,却宛如鲜花盛开,再搭配《Un Amico》的配乐,场面非常凄美,可以说是影史上相当惊艳的一幕。昆汀在这部电影中大量使用了莫里康内的音乐(多达八首),虽然莫里康内本人对此颇有微词(曾经吐槽过),但不得不说昆汀确实是将配乐运用得出神入化。

今年听歌的收获

莫里康内

B 站 up 主“培根悖论唠唠嗑”纪念莫里康内的一期视频《用音符谱写电影的诗人,传奇作曲家莫里康内》让我彻底粉上了这位神级配乐大师。先说这期视频,它有多好看呢?我前前后后应该看了有二十几遍,而且每一次看的时候都能收获同等程度的感动。

比较推荐的几首:《Un Amico》《The Surrender》《L' Estasi Delll'oro》《Sacco e Vanzetti》。如果你有玩过小岛秀夫的《合金装备》系列,对《Sacco e Vanzetti》这首歌一定不陌生。

The Stone Roses | 石玫瑰

今年比较大的一个惊喜,又发现了一支宝藏级别的英伦摇滚乐队:The Stone Roses,石玫瑰。看 B 站的纪录片《石玫瑰再临 The Stone Roses: Made of Stone》入的坑,后面听了几首歌,用两个字形容就是:牛逼。在英伦摇滚的历史上,石玫瑰登场的时间比 Oasis (绿洲,我最喜欢的乐队,没有之一)要更早,但是这两支乐队在有些地方我觉得蛮像的。首先,他们都来自英国曼彻斯特(曼彻斯特,永远滴神),然后在曲风上都有一种浪漫的气息,两个主唱发型也比较相似,而且在演出时都喜欢拿着铃鼓。纪录片末尾是石玫瑰重组后开的一次演唱会,我一时之间竟有点分不出台上站着的是石玫瑰的主唱 Ian Brown,还是绿洲的主唱 Liam Gallagher。

重点来了,一向“桀骜不驯,怼天怼地怼空气”的 Liam Gallagher 是怎么评价石玫瑰的呢?看图说话:

顺便一提,这段话是 Liam 在演唱会后台说的,当时的他是台下的听众。这场演唱会有多火呢?在粉丝的眼里,甚至比他们的工作还要重要:有一位老哥直接翘班跑来买票,现场还有一位工作到一半跑过来的粉刷工,表示自己要听完演唱会再去忙其它的。门口排的长队九曲十八弯、一眼望不到头。当时的场地并不足以容纳那么多的观众,能买到票的人可以说是“天选之子”。

今年玩的一些游戏

今年玩的游戏并不多。一个是《Borderland 3》,epic 打折的时候入的,操作手感比上一作要好很多,也加入了非常地道的中文配音。不过我并不是很喜欢这一代的剧情,另外角色也不是很讨喜,所以打到一半就没玩了,至今没通关;另一个是 《GTA5》(想到一张梗图:是谁还没买 GTA5?),和高中舍友一起打线上模式,做各种任务,互相坑对方,非常之好玩。R 星做的开放世界游戏,质量还是很顶的。当然,下半年开学后也就没玩了,最近好像又出了新的 dlc,可能有空会约几个小伙伴玩一下。

《JOJO》和《进击的巨人》

现在我对单纯的热血漫画并不是很感兴趣,最多只是逛逛贴吧看看剧情是否有什么进展,不会再有从前那样的热情。目前来说,《JOJO》系列(尤其是 SBR)以及《进击的巨人》是我心中唯二两部神作,在我看来,荒木和谏山创是属于真正有思想并且会将这些思想在漫画中传达出来的作者,他们的作品,绝非是那种看一遍就能看懂的。

《JOJO:SBR》中,乔尼为了救下自己的挚友杰洛,用好不容易得到的“遗体”换来了敌人手里一瓶尚未喝完的葡萄酒,于是有了名场面:雪中对饮。这一刻,我深刻感受到了荒木通过艺术作品所散发出来的那种浪漫:

至于《进击的巨人》,虽然已连载超过 10 年,不过我是在最近几个月才入坑的。目前漫画差不多要收尾了,虽然多半不会是 happy ending,但仍然非常期待谏山创会带给我们什么样的结局。

项目 | 在图书馆的“搬转”日常

学习新的技术栈:小程序开发

上半年学期末看到了图书馆的一篇推文,正在招学生参与图书馆项目的开发。于是我就赶在截止日期之前报名了,顺带附上不太像样的简历。在学期快结束的时候收到了老师的邮箱回复,大概意思是让我暑假学一下小程序,开学后会有个面试。

当时我还没接触过小程序,不过我知道它有很多地方和 Vue 是很像的,而且考虑到小程序对前端工程师来说基本是必学的,所以第一反应是:学起来。既然要学习一个新的技术栈,肯定少不了两个东西,一个是权威的官方文档,一个是质量上乘的课程。微信社区的文档已经很完备了,至于课程,我选择的是慕课上的《微信小程序云开发-从0打造云音乐全栈小程序》,300+ 的价格还是蛮贵的(心疼一下自己),但是很值。学校的课程结束后就进入了暑假,我开始马不停蹄地学习小程序,那种每天学习一点新东西,解决一个小 bug,慢慢去完善一个项目的感觉真的很棒,至今仍然记忆犹新。而且讲师的风格我个人比较喜欢,他的讲法是由浅入深、循序渐进的,经常会抛出一个问题引导你去思考。这个课程能学到的不仅是技术,还有编码规范以及解决问题的思路。

项目大概写了一个多月就完工了。期间不仅学到了小程序的相关知识,也对它周遭的生态尤其是云开发有了一定的了解,不得不说,云开发真的很香,前端在一定程度上能够独当一面。此外也接触到了一点 Koa 相关的知识,写了一些简单的后台接口(虽然因为太久没写,现在忘得差不多了)。

总的来说,小程序的开发体验还算可以,很多东西都是现成的,只需要去调用即可,遇到的很多问题在社区也有解决方案。但开发过程依旧有不少痛点,比如:

  • setData 蛋疼的赋值方式;
  • 不支持 less、sass 等 css 预处理器;
  • 缺少原生的状态管理方案。虽然官方推出的westore 足够简洁和轻量,但是对代码有一定的侵入性:需要用其提供的 api 构建组件和页面,修改状态之后需要手动调用 this.update() 进行更新……这些都在一定程度上增加了项目对其的依赖,加上我们无法像使用 Axios 那样做一层中间的封装,万一哪一天要更换状态管理方案,代码修改起来想必会相当痛苦。
  • 另外,开发者工具的使用体验也比较差,偶尔会有一些奇葩的 bug,卡死、黑屏的概率尤其高,最可怕的是这玩意疯狂吃 CPU:之前有个群友晒了一张图,开发者工具的 nwjs 进程占用 CPU 直接飙到了 127%,看着属实吓人。可能大家也不会直接在上面编码,都是用 VS Code 或者 WebStorm 居多;

当然,这里吐槽的基本都是原生小程序的毛病 —— 很多问题在小程序框架出现之后都迎刃而解了。为了摆脱这种不太舒服的开发体验,后面得找时间学习一些跨端框架了。

学校的项目:年度账单小程序

下半年开学后就是面试,过程比较顺利,进入部门后不久就开始了“搬砖”的工作。在老师的带领下,和学校的几个小伙伴一起做了一个年度账单小程序,一开始以为花一两个月就能写完了,没想到由于各种各样的原因,最后忙活了一个学期才搞定。目前项目已经上线,在公众号和朋友圈的宣传下,截止到现在已有 1w+ 用户量,反响挺不错的。

不知道大家是否有留意到,今年(2020 年)做年度账单的 app / 网站更多了,像 Gitee、飞书、CSDN 等都推出了自己的用户年度账单,可以说是遍地开花。我想老师可能是预见到了这一点,所以打算推出一个面向师生的年度账单,不得不说是个很棒的 idea。当然,做这个项目实在是踩了无数的坑:

  • 缺乏专业可靠的美工,后面严重耽误了项目的开发进度。想和老师说一声,“明年招人真的得招个学 UI 或者产品设计的同学了”,虽然前端也需要关注用户体验,但让专业的人来做这件事,一定可以考虑到更多细节;
  • 项目中后期的时候重构了两次核心代码,这源于初期需求和用户群体定位的不明确。这部分代码的逻辑也是整个项目最复杂的,需要考虑到各种不同类型的用户。在知道要重写逻辑之后,心态有点炸裂,好在还是坦然接受了,就当作是一次挑战吧。而且以后工作中也肯定会有这样的事情;
  • 技术方案选择以及评估工作做得还不够到位。我们做的小程序最初是有提供一键“保存图片”功能的,这个功能只能用 canvas 来做,因此选择了海报图绘制库 Painter。后面实际开发的时候,整体来说是比较顺利的,但还是有不少痛苦的地方:

    • 不能实时修改以查看效果,每次修改都要等待绘制,严重影响开发效率;
    • 需要手动声明每个元素的位置。包含变量的文案是最麻烦的,基本上需要把一句简单的话拆成不同的字块,再计算彼此之间的距离以进行正确的定位和排列
    • 页面全部都是静态的,动效什么的别想了
    • 灵活性差,往往一个地方发生改动,后面的都要跟着改,就好像多米诺骨牌一样

      后面又出现了两个致命的问题:一个是 iPhoneX 绘制的时候始终会出现大量空白区,另一个则是出现概率较高的页面绘制失败问题(可能是绘制的页面太多了)。两个问题对于用户体验都是破坏性的,我们只好放弃这个方案。

  • 及时沟通非常重要。做一些想当然的假设会给自己带来很多麻烦,举个例子:我们的小程序会展示师生进图书馆和借阅书籍的数据,当时我认为“如果有借书,那么一定是有进图书馆的”,这个逻辑乍看确实没啥问题,你不进图书馆,怎么借书呢?但事实是,还真有这种情况 —— 比如学生 A 用自己的校园卡刷卡进馆,然后用学生 B 的校园卡刷卡借了一本书,那么,学生 B 不就是没有进馆,但是却借了书吗?如果有事先和其他人确认这种情况,这种疏忽是可以避免的。
  • 还有其它千奇百怪的问题,再说下去可能篇幅就不够了,后面再写一篇文章总结一下这个项目。

由于宣传排期的问题,项目没有如期在 29 号上线,而是推迟到了 1 月 6 号,但从大家的留言以及访问量来看,小程序还是挺受欢迎的。我觉得我们的目标也达到了,给自己和小伙伴们点个赞。

2021 年的一些计划

实际上,今年做的事情也并不是很多,尤其是技术方面,在后半段就有点乏力了,明显没有一两年前学习时的那种冲劲。原因有很多,但无论如何,必须得尽早摆脱这种状态。2021 年的具体计划,其实还没想好,但大概会做下面这些事:

  • 学习 | 写作:从上面的文章清单可以看出,自 9 月 18 号之后,我就没有再发布新的文章了,也就是说,下半年那学期的文章产出几乎为 0。忙于项目是一方面,另一方面也说明了我很长时间没有学习新知识了,2021 年要回归正常状态,持续学习并输出文章
  • 书籍阅读 | 付费课程:看完红宝书第四版,以及其它的几本技术书籍;之前在极客时间和拉勾上买了一些课程,也要慢慢消化掉
  • 基础巩固:JS 算法搞起来,一些偏底层的东西可能要重拾;CSS 会整理并积累一些优秀的代码、开发技巧,常见的特效最好自己都能手写实现一遍
  • 学习新的技术:可能是 uniapp 或其它框架
  • 其它:继续深入学习 Vue 技术栈;另外会看看浏览器、webpack 方面的东西

最后,祝阅读到这里的各位,2021 年诸事顺利。

本文参与了 SegmentFault 思否征文「2020 总结」,欢迎正在阅读的你也加入
查看原文

赞 0 收藏 0 评论 0

Chor 关注了用户 · 2020-11-06

芒果果 @wangying_5ea4fb9de961c

一路走走看看,顺便留下点什么。

关注 43

Chor 赞了文章 · 2020-10-18

微信小程序之登录态的探索

上一篇:开发微信小程序必须要知道的事

登录,几乎什么项目都会用到,其重要性不言而喻,而小程序的登录却一直是为人头疼的一件事,这里我分享下我们在小程序登录上的探索

通常的登录都是通过一个表单,这很正常,但如果在小程序里你也这么做那就有点不可思议了,微信的一键登录对用户体验有多好你难道不知道?不用是不是脑子有坑?最主要——你要利用微信的生态必须需要用微信的登录,以获取相关信息来和微信交互,OK,我们进入正题

用户在小程序、小游戏中需要点击组件后,才可以触发登录授权弹窗、授权自己的昵称头像等数据

友情提示一下:wx.login并不需要点击组件,需要的是wx.getUserInfo,但通常我们都会用到UnionID、encryptedData、iv等信息完成完整的登录流程,本文主要聚焦的也是这种场景

所以之前直接通过调用API的方式就行不通了,那么问题来了——这个点击按钮要放到哪里?

  • 放到首页,一进小程序就必须先登录。这样显然很粗暴,而且问题并没有解决,反而会把用户直接拒之门外,毕竟你不是用小程序做后台系统,什么场景都需要授权,先授权也是必须的
  • 在需要授权的时候跳到登陆页面。这样就解决了上面遇到的不需要授权的时候也被强制授权,可是这样好吗?

    • 体验上不好,操作被打断,尤其整个页面都不需要授权只有在一个地方需要授权的,例如:你正在读一篇文章,读罢深有感触,想评论一番,洋洋洒洒几十字写完正准备点击呢,他妈的跳转了!跳转了!
    • 又一个漏斗,增加用户流失率。还TM要登录!很多用户心里一定这么想

那就直接放在需要登录的页面上(这不是漏斗吗?很多读者一定这么想。但想想看上面那个场景,点评论时只是需要点击下弹出的登录按钮,而且还假模假样的以微信的口吻提醒你需要登录,那你会不会登录?最起码你很愿意登录,而且来的很突然,我控几不住自己的手就点了!点了!)

可是这种方式有一个问题

怎么在需要的页面都能弹出登录按钮

应该很多人都能想到:抽离出组件,那怎么保证在需要的页面都有这个组件呢?错杀一千也不能放过一个!把登录组件集成到共用的父组件,然后在每个页面都使用。我也建议这么做,因为这个共用的父组件其实又很多用处,例如iPhoneX适配等

等等,什么都准备好了,什么时候需要登录呢?XX,这个肯定是你自己控制的啦。嗯~好吧,我们来理一理

在哪里校验是否需要鉴权

请求接口的时候,嗯~这是大家的共识

BOSS来了

怎么鉴权

Image text

官方的这张图已经做了很详尽的说明,这里不做赘述
但是看到session_key了吗?看到官方同时说的了吗

clipboard.png

所以问题又来了

怎么保证session_key的有效性

诚如上图

  • 要保证调用接口时后端session_key不失效,只能在每次调用前先使用wx.checkSession检查是否有效
  • 实践中也发现wx.checkSeesion非常耗时,大约200ms,所以也不能每次接口调用前都使用wx.checkSession检查是否有效
  • 同时要注意⚠️前端不能随便重新执行wx.login,因为可能导致正在进行的其它后端任务session_key失效

天啦噜,怎么办?!
通过实践和偶然的发现——官方的示例代码

clipboard.png

得知:在使用小程序期间session_key是不会失效的,so,你想到了什么?

  • 在每个请求前去校验有效性
  • 将校验有效性的结果存储起来
  • 通过async/await和刚才存储起来的结果来保证不过多调用wx.checkSession

先问个问题:你准备用什么方式来存储校验的结果?
。。。
让思考先飞一会
。。。。。。
。。。。。。。。。
。。。。。。。。。。。。
storage吗?当然可以,不过不够完美,为什么?因为storage是永久的存储,而session_key的有效期却只是在使用小程序期间,所以你需要在小程序结束后手动重置该状态以重新校验其有效性,那是不是在app的onUnload里重置呢?不是!开发过小程序的应该都知道,那就是结束使用小程序的方式太多,不能保证每种方式都会触发onUnload,例如用户直接销毁了微信进程?(其实你也可以在app的onShow里搞)那用什么呢?直接用内存啊,借助内存的自动管理来智能管理,所以最终代码应该是这样的

// doRequest.js
let wxSessionValid = null // 微信session_key的有效性
// 带鉴权的请求封装
async function doRequestWithCheckAuth() {
  ...
  if (typeof wxSessionValid !== 'boolean') {
    wxSessionValid = await checkWxSession() // 检查微信session是否有效
  }
  if (!wxSessionValid) {
    await reLogin() // 重新登录
  }
  wxSessionValid = true // 重新登陆后session_key一定有效
  ...
}

这样是不是看起来比较完美了?嗯~

不知道有没有同学着急问业务侧的session(自定义的登录态)怎么没讲?嗯,那现在讲吧

怎么校验完整的认证体系

其实很简单,都不想把它作为一部分来讲,但既然讲了就必然有我想强调的

  • 校验微信端的session_key略有麻烦,但不应该把它抛给服务端

    • 服务端不能直接校验session_key的有效性而是通过调用接口发现错误了才知道失效了,这是被动的
    • 服务端需要同时维护两个session

而放在前端我们只需要校验两个session的有效性即可,任何一个失效就重新登录,这是积极主动有效的操作,应该被提倡

贯通

OK,基本上梳理的差不多了,就差弹登录按钮了,这个简单,调用刚才封装的组件的方法就行了嘛,bingo,可是,点完允许后呢?怎么继续用户的操作呢?怎么能让用户的体验不被打断呢?先回放下刚才reLogin的代码

async function reLogin() {
  // 确保有用户信息
  await new Promise(resolve => { // ⚠️注意开头有await!!!
    wx.getSetting({
      success: (res) => {
        // 如果用户没有授权或者没有必要的用户信息
        if (!res.authSetting['scope.userInfo'] || !_.isRealTrue(wx.getStorageSync('userInfoRes').userInfo)) {
          navToLogin(resolve) // 去提示用户点击登录按钮,⚠️注意:并把当前的resolve带过去
        } else {
          resolve() // 静默登录
        }
      }
    })
  })
  return new Promise((resolve) => {
    wx.login({
      success: res => {
        login(res.code).then((jwt) => {
          resolve(jwt) // resolve jwt
        }) // 通过code进行登录
      },
      fail(err) {
        wx.showToast({
          title: err.errMsg,
          icon: 'none',
          duration: 2000
        })
      }
    })
  })
}
function navToLogin(resolve) {
  /* eslint-disable no-undef */
  const pages = getCurrentPages()
  const page = pages[pages.length - 1] // 当前page
  page.openLoginModal(resolve) // 打开登录按钮弹框,并把当前的resolve带过去
}

上面的代码注释里有两个⚠️注意看到没?是的,通过回调的方式?当用户同意授权了就继续余下的逻辑,如果被拒绝了,则安利他,再拒绝就终止操作,下次需要授权也会继续弹出授权

有不明白欢迎评论留言指出,我再做说明修改
下一篇文章里会有代码呈现,完整源码以后会放出的,通过wepy搭建的一个框架
下一篇:对api请求封装的探索和总结
谢谢

查看原文

赞 65 收藏 47 评论 2

Chor 赞了文章 · 2020-09-27

浅谈script标签的defer和async

1. 什么鬼

今天在做一个小需的时候,忽然看到前辈一句吊炸天的代码

    <script data-original="#link("xxxx/xx/home/home.js")" type="text/javascript" async defer></script>

卧槽,竟然同时有asyncdefer属性,心想着肯定是前辈老司机的什么黑科技,两个一块儿肯定会发生什么神奇化学反应,于是赶紧怀着一颗崇敬的心去翻书翻文档,先复习一下各自的定义。

2. 调查一番

先看看asyncdefer各自的定义吧,翻开红宝书望远镜,是这么介绍的

2.1 defer

这个属性的用途是表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在<script>元素中设置defer属性,相当于告诉浏览器立即下载,但延迟执行。

HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于DOMContentLoaded事件执行。在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在DOMContentLoad时间触发前执行,因此最好只包含一个延迟脚本。

2.2 async

这个属性与defer类似,都用于改变处理脚本的行为。同样与defer类似,async只适用于外部脚本文件,并告诉浏览器立即下载文件。但与defer不同的是,标记为async的脚本并不保证按照它们的先后顺序执行。

第二个脚本文件可能会在第一个脚本文件之前执行。因此确保两者之间互不依赖非常重要。指定async属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容。

概括来讲,就是这两个属性都会使script标签异步加载,然而执行的时机是不一样的。引用segmentfault上的一个回答中的一张图图片描述蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

也就是说async是乱序的,而defer是顺序执行,这也就决定了async比较适用于百度分析或者谷歌分析这类不依赖其他脚本的库。从图中可以看到一个普通的<script>标签的加载和解析都是同步的,会阻塞DOM的渲染,这也就是我们经常会把<script>写在<body>底部的原因之一,为了防止加载资源而导致的长时间的白屏,另一个原因是js可能会进行DOM操作,所以要在DOM全部渲染完后再执行。

2.3 really?

然而,这张图(几乎是百度搜到的唯一答案)是不严谨的,这只是规范的情况,大多数浏览器在实现的时候会作出优化。

来看看chrome是怎么做的

《WebKit技术内幕》:

  1. 当用户输入网页URL的时候,WebKit调用其资源加载器加载该URL对应的网页。

  2. 加载器依赖网络模块建立连接,发送请求并接受答复。

  3. WebKit接收到各种网页或者资源的数据,其中某些资源可能是同步或异步获取的。

  4. 网页被交给HTML解释器转变成一系列的词语(Token)。

  5. 解释器根据词语构建节点(Node),形成DOM树。

  6. 如果节点是JavaScript代码的话,调用JavaScript引擎解释并执行。

  7. JavaScript代码可能会修改DOM树的结构。

  8. 如果节点需要依赖其他资源,例如图片、CSS、视频等,调用资源加载器来加载他们,但是他们是异步的,不会阻碍当前DOM树的继续创建;如果是JavaScript资源URL(没有标记异步方式),则需要停止当前DOM树的创建,直到JavaScript的资源加载并被JavaScript引擎执行后才继续DOM树的创建。

所以,通俗来讲,chrome浏览器首先会请求HTML文档,然后对其中的各种资源调用相应的资源加载器进行异步网络请求,同时进行DOM渲染,直到遇到<script>标签的时候,主进程才会停止渲染等待此资源加载完毕然后调用V8引擎对js解析,继而继续进行DOM解析。我的理解如果加了async属性就相当于单独开了一个进程去独立加载和执行,而defer是和将<script>放到<body>底部一样的效果。

3. 实验一发

3.1 demo

为了验证上面的结论我们来测试一下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.css" rel="stylesheet">
        <link href="http://cdn.staticfile.org/foundation/6.0.1/css/foundation.css" rel="stylesheet">
        <script data-original="http://lib.sinaapp.com/js/angular.js/angular-1.2.19/angular.js"></script>
        <script data-original="http://libs.baidu.com/backbone/0.9.2/backbone.js"></script>
        <script data-original="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
    </head>
    <body>
        ul>li{这是第$个节点}*1000
    </body>
    </html>

一个简单的demo,从各个CDN上引用了2个CSS3个JS,在body里面创建了1000个li。通过调整外部引用资源的位置和加入相关的属性利用chrome的Timeline进行验证。

3.2 放置在<head>

图片描述
异步加载资源,但会阻塞<body>的渲染会出现白屏,按照顺序立即执行脚本

3.3 放置在<body>底部

图片描述
异步加载资源,等<body>中的内容渲染完毕后且加载完按顺序执行JS

3.3 放置在<head>头部并使用async

图片描述
异步加载资源,且加载完JS资源立即执行,并不会按顺序,谁快谁先上

3.4 放置在<head>头部并使用defer

图片描述
异步加载资源,在DOM渲染后之后再按顺序执行JS

3.5 放置在<head>头部并同时使用asyncdefer

图片描述
表现和async一致,开了个脑洞,把这两个属性交换一下位置,看会不会有覆盖效果,结果发现是一致的 = =、

综上,在webkit引擎下,建议的方式仍然是把<script>写在<body>底部,如果需要使用百度谷歌分析或者不蒜子等独立库时可以使用async属性,若你的<script>标签必须写在<head>头部内可以使用defer属性

4. 兼容性

那么,揣摩一下前辈的心理,同时写上的原因是什么呢,兼容性?

上caniuse,async在IE<=9时不支持,其他浏览器OK;defer在IE<=9时支持但会有bug,其他浏览器OK;现象在这个issue里有描述,这也就是“望远镜”里建议只有一个defer的原因。所以两个属性都指定是为了在async不支持的时候启用defer,但defer在某些情况下还是有bug。

The defer attribute may be specified even if the async attribute is specified, to cause legacy Web browsers that only support defer (and not async) to fall back to the defer behavior instead of the synchronous blocking behavior that is the default.

5. 结论

其实这么讲来,最稳妥的办法还是把<script>写在<body>底部,没有兼容性问题,没有白屏问题,没有执行顺序问题,高枕无忧,不要搞什么deferasync的花啦~

目前只研究了chrome的webkit的渲染机制,Firefox和IE的有待继续研究,图片和CSS以及其他外部资源的渲染有待研究。

更多信息在 这里

参考

查看原文

赞 58 收藏 85 评论 13

Chor 赞了回答 · 2020-09-27

解决defer和async的区别

先来试个一句话解释仨,当浏览器碰到 script 脚本的时候:

  1. <script data-original="script.js"></script>

    没有 deferasync,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

  2. <script async data-original="script.js"></script>

    async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。

  3. <script defer data-original="myscript.js"></script>

    defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

然后从实用角度来说呢,首先把所有脚本都丢到 </body> 之前是最佳实践,因为对于旧浏览器来说这是唯一的优化选择,此法可保证非脚本的其他一切元素能够以最快的速度得到加载和解析。

接着,我们来看一张图咯:

请输入图片描述

蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

此图告诉我们以下几个要点:

  1. deferasync 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
  2. 它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的
  3. 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
  4. async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行
  5. 仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics

关注 70 回答 7

Chor 提出了问题 · 2020-09-26

解决在对半圆的实现中,border-left 是如何发挥作用的?

这是一个对半圆的实现思路:

<div class="half-o"></div>
dic.half-o{
    width: 100px;
    height: 200px;
    border-radius: 50%;        
    background-color: gray;
    border-left: 100px solid black;
}

效果如下:
image

其中,左半圆是用 border 实现的,右半圆是用 div 本身的宽度实现的。

在没有加上 border-left 之前,很明显这个 div 应该是一个椭圆,而在加上 border-left 之后,正常来说应该是在保持椭圆形状不变的情况下,多出一个左边框,形成类似下面这样的效果:
image

但实际上形成的是一个对半圆,这是为什么呢?是因为 border-left 会改变原有图形的形状吗?还是和图形属性的绘制顺序有关(比如说,并不是在绘制宽高之后再去叠加边框)?

关注 3 回答 2

Chor 赞了文章 · 2020-09-25

为什么node-sass总是安装失败?

原文链接
作者 李平海

node-sass是我们开发中很常见的依赖包,也是安装时间冗长和最常见到报错的依赖。
安装node-sass失败原因有很多种,我们在说失败原因之前,先来分析一下node-sass的安装过程(以下node版本为v10.15.3):

PS D:\demo> npm i node-sass

// 从npm源安装到node_modules
> node-sass@4.13.0 install D:\demo\node_modules\node-sass
> node scripts/install.js

// 下载binding.node
Downloading binary from https://github.com/sass/node-sass/releases/download/v4.13.0/win32-x64-64_binding.node
Download complete .] - :
Binary saved to D:\demo\node_modules\node-sass\vendor\win32-x64-64\binding.node

// 缓存binding.node
Caching binary to C:\Users\leepi\AppData\Roaming\npm-cache\node-sass\4.13.0\win32-x64-64_binding.node

> node-sass@4.13.0 postinstall D:\demo\node_modules\node-sass
> node scripts/build.js

Binary found at D:\demo\node_modules\node-sass\vendor\win32-x64-64\binding.node
Testing binary
Binary is fine

// 写package-lock.json
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN demo@1.0.0 No description
npm WARN demo@1.0.0 No repository field.

+ node-sass@4.13.0
added 174 packages from 138 contributors and audited 529 packages in 24.379s
found 0 vulnerabilities

我们可以看到,安装node-sass有几个步骤:

  1. 校验本地node_modules中是否已安装node-sass,版本是否一致;
  2. 如未安装或版本不符,从npm源安装node-sass本体;
  3. 检测全局缓存和本地中是否有binding.node,如有即跳过安装;
  4. 没有binding.node则从github下载该二进制文件并将其缓存到全局;
  5. 假如binding.node下载失败,则尝试本地编译出该文件;
  6. 将版本信息写到package-lock.json;

由此看到,实际上node-sass依赖了一个二进制文件binding.node,从npm源安装完本体后还会从github下载binding.node

因此安装node-sass相关的失败原因有以下几种:

原因一: npm源速度慢

由于众所周知的国内网络环境,从国内安装官方源的依赖包会很慢。可以将npm源设置成国内镜像源(如淘宝npm):

npm config set registry https://registry.npm.taobao.org

或者通过.npmrc文件设置:

// .npmrc
registry=https://registry.npm.taobao.org/

原因二: binding.node源无法访问或速度慢

node-sass除了npm部分的代码,还会下载二进制文件binding.node,默认源是github,国内访问较慢,特殊时期甚至无法访问。我们也可以将其改成国内源:

// linux、mac 下
SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ npm install node-sass

// window 下
set SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ && npm install node-sass

或者通过.npmrc文件设置:

// .npmrc
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

有类似问题的还有chromedriver,phantomjs,electron等常见依赖包,我们可以一并写到.npmrc中:

// .npmrc
sass_binary_site=https://npm.taobao.org/mirrors/node-sass
chromedriver_cdnurl=https://npm.taobao.org/mirrors/chromedriver
phantomjs_cdnurl=https://npm.taobao.org/mirrors/phantomjs
electron_mirror=https://npm.taobao.org/mirrors/electron

原因三: node版本与node-sass版本不兼容

node-sass版本兼容性并不好,老项目中依赖的node-sass很可能已经不兼容新的node,对应版本兼容如下(或参考官方仓库):

NodeJSMinimum node-sass versionNode Module
Node 134.13+79
Node 124.12+72
Node 114.10+67
Node 104.9+64
Node 84.5.3+57

本文开头的安装例子中,binding.node的版本是v4.13.0/win32-x64-64_binding.node,可以看到,里面包括node-sass版本号v4.13.0,平台win32,架构x64,以及Node Module的版本64。Node Module是node的一个模块,其版本号可以在进程process.versions中查到:

PS D:\demo> node
> console.log(process.versions);
{ http_parser: '2.8.0',
  node: '10.15.3',
  v8: '6.8.275.32-node.51',
  uv: '1.23.2',
  zlib: '1.2.11',
  ares: '1.15.0',
  modules: '64',
  nghttp2: '1.34.0',
  napi: '3',
  openssl: '1.1.0j',
  icu: '62.1',
  unicode: '11.0',
  cldr: '33.1',
  tz: '2018e' }
undefined
>

如上显示,node10.15.3对应的module版本为64。
假如node-sass与node的版本不兼容,就会找不到对应的binding.node而报错,例如你的node是10.15.3,装node-sass4.6.1,则会尝试安装v4.6.1/win32-x64-64_binding.node,但这个版本的binding.node是不存在的。
此时改node-sass或node的版本即可。

原因四: 缓存中binding.node版本不一致

假如本地node版本改了,或在不同机器上运行,node版本不一致,会报类似错误:

Found bindings for the following environments:
  - Windows 64-bit with Node.js 6.x

这是因为原有binding.node缓存跟现node版本不一致。按提示npm rebuild node-sass或清除缓存重新安装即可。

原因五: 安装失败后重新安装

安装失败后重新安装,有可能无权限删除已安装内容,此时npm uninstall node-sass或手动删掉原目录后再安装即可。

原因六: 提示没有安装python、build失败等

假如拉取binding.node失败,node-sass会尝试在本地编译binding.node,过程就需要python。假如你遇到前面几种情况解决了,实际上也不会出现在本地构建的情况了,我们就不谈这种失败中失败的情况吧 :-)

查看原文

赞 36 收藏 24 评论 7

Chor 赞了文章 · 2020-09-25

前端工程师学Docker ? 看这篇就够了 【原创精读】

前端工程师,为什么要学习Docker ?

传统的虚拟机,非常耗费性能

Docker可以看成一个高性能的虚拟机,并且不会浪费资源,主要用于Linux环境的虚拟化,类似VBox这种虚拟机,不同的是Docker专门为了服务器虚拟化,并支持镜像分享等功能。前端工程师也可以用于构建代码等等

目前看,Dokcer不仅带火了GO语言,还会持续火下去

首先,我们看看传统的虚拟机和Docker的区别

传统的虚拟机:

Docker:

可以看到,传统的虚拟机是每开一个虚拟机,相当于运行一个系统,这种是非常占用系统资源的,但是Docker就不会。但是也做到了隔离的效果

Docker容器虚拟化的优点:

  1. 环境隔离

Docker实现了资源隔离,实现一台机器运行多个容器互不影响。

  1. 更快速的交付部署

使用Docker,开发人员可以利用镜像快速构建一套标准的研发环境,开发完成后,测试和运维人员可以直接通过使用相同的环境来部署代码。

  1. 更高效的资源利用

Docker容器的运行不需要额外的虚拟化管理程序的支持,它是内核级的虚拟化,可以实现更高的性能,同时对资源的额外需求很低。

  1. 更易迁移扩展

Docker容器几乎可以在任意的平台上运行,包括乌力吉、虚拟机、公有云、私有云、个人电脑、服务器等,这种兼容性让用户可以在不同平台之间轻松的迁移应用。

  1. 更简单的更新管理

使用Dockerfile,只需要小小的配置修改,就可以替代以往的大量的更新工作。并且所有修改都是以增量的方式进行分发和更新,从而实现自动化和高效的容器管理。

正式开始

本文撰写于2019年10月13日

电脑系统:Mac OS

使用最新版官网下载的Docker

以下代码均手写,可运行

下载官网的Docker安装包,然后直接安装

https://www.docker.com/

Docker官网下载地址
安装后直接打开

打开终端命令行,输入docker,会出现以下信息,那么说明安装成功

下载安装成功后,首先学习下Docker的两个核心知识点

container(容器)和image(镜像)

Docker的整个生命周期由三部分组成:镜像(image)+容器(container)+仓库(repository

思维导图如下:

该如何理解呢?

每台宿主机(电脑),他下载好了Docker后,可以生成多个镜像,每个镜像,可以创建多个容器。发布到仓库时,以镜像为单位。可以理解成:一个容器就是一个独立的虚拟操作系统,互不影响,而镜像就是这个操作系统的安装包。想要生成一个容器,就用安装包(镜像)生成一次

上面就是Docker的核心概念,下面开始正式操作

补充一点:如果想深入Docker , 还是要去认真学习下原理,今天我们主要讲应用层面的

首先,我们回到终端命令行操作

输入:

docker images

如果你的电脑上之前有创建过的镜像,会得到如下:

如果没有的话就是空~

我们首先创建一个自己的镜像

先编写一个Node.js服务

创建index.js

// index.js
const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
   ctx.body = 'Hello docker';
});

app.listen(3000);

然后配置package.json文件

{
    "name": "app",
    "version": "1.0.0",
    "private": true,
    "scripts": {
      "start": "node server.js"
    },
    "dependencies": {
      "koa": "^2.5.0"
    }
   }
   

正常情况下 使用

npm startnode index.js 就可以启动服务
可是我们这里需要打包进Docker中,这里就需要写一个配置文件dockerfile

vsCode有天然插件支持

在目录下新建文件dockerfile,加入如下配置

FROM  node 
ADD . /app/
EXPOSE 3000
WORKDIR /app
RUN npm install
CMD ["node","./index.js"]

解释一下,上面这些配置的作用

FROM 是设置基础镜像,我们这里需要Node

ADD是将当前文件夹下的哪些文件添加到镜像中 参数是 [src,target]

这里我们使用的 . 意思是所有文件,当然跟git一样,可以配置ignore文件

EXPOSE是向外暴露的端口号

WORKDIR是说工作目录,我们这里将文件添加到的是app目录,所以配置app目录为工作目录,
这样就不用在命令行前面加/app

RUN是先要执行的脚本命令

CMD是执行的cmd命令

可以想一想,我们打包好镜像后,然后启动镜像会发生什么?

文件编写完,使用命令打包镜像

使用命令打包已经好的文件目录


 docker image build ./ -t app

打包后出现提示:

此时我们查看Docker镜像,使用命令:

docker images

我们可以清楚看到,app镜像已经打包成功,下面我们启动它



docker run -p 8000:3000 app 

使用上面命令即可启动我们的镜像,这时我们在命令中输入

curl 127.0.0.1:8000

得到返回内容

Hello docker

浏览器输入: 127.0.0.1:8000 即可访问到页面~

以上说明,我们的第一个Docker镜像已经制作成功

有人可能会觉得到这里,镜像和容器有点混淆了,不是先有镜像再有容器吗?

其实是我们启动的镜像有脚本命令帮我们启动了服务,于是Docker帮我们自动创建了容器

查看Docker容器命令:

docker ps -a 列出所有容器
不加 -a 仅列出正在运行的,像退出了的或者仅仅只是创建了的就不列出来
docker container ls 列出当前运行的容器

输入上面 docker container ls

得到结果

原来Docker看我们启动了脚本服务,帮我们自动生成了容器?

下面我们来一个生成镜像,再生成容器,最后手动启动容器的例子

这次我们配置,加入Nginx反向代理服务器

首先,创建用户需要看到的html文件

这里我们给一个普通的 hello-world内容的index.html文件即可

然后创建dickerfile文件,配置如下,将index.html文件添加到对应的位置


FROM nginx

COPY ./index.html /usr/share/nginx/html/index.html

EXPOSE 80

对外暴露端口号80

这里特别提示:配置文件怎么写,根据你的基础镜像来,百度基本都能找到,不用纠结这个

此时的文件结构:

老规矩,开始打包

docker build ./ -t html

打印信息:

输入终端命令:

docker images

得到结果:

新的镜像html已经构建成功,但是此时查看容器,是没有正在运行的

输入命令:

docker container ls //查看正在运行的所有容器
docker container ls -a //查看所有容器
得到结果是:

可以确认的是,我们创建镜像不会自动生成和启动容器

我们手动生成容器

docker container create -p 8000:80 html

此时命令行返回 一段值

输入

docker container ls

没有显示有任何启动的容器,这时候我们手动启动

输入

docker container start ***(上面那段值)

再重复 docker container ls 命令

得到结果

此时访问localhost:8000即可正常访问页面~

至此,我们可以确定,创建镜像只要不启动,不会生成容器,更不会运行容器

那怎样将Docker用在前端的日常构建中呢?

我们使用gitHub+travis+docker来形成一套完整的自动化流水线

只要我们push新的代码到gitHub上,自动帮我们构建出新的代码,然后我们拉取新的镜像即可(gitLab也有对应的代码更新事件钩子,可以参考那位手动实现Jenkens的文章)

首先我们先进入 Travis CI 官网配置,注册绑定自己的gitHub账号

然后在左侧将自己需要git push后自动构建镜像的仓库加入

接着在项目根目录配置 .travis.yml 文件



language: node_js
node_js:
  - '12'
services:
  - docker

before_install:
  - npm install
  - npm install -g parcel-bundler

script:
  - parcel build ./index.js
  - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
  - docker build -t jinjietan/mini-react:latest .
  - docker push jinjietan/mini-react:latest

每次更新push代码,都会下载,然后执行打包命令,这样你下载的镜像就是有最新的代码。不再需要每个人下载打开镜像再去build

为了降低复杂度,这里使用了Parcel打包工具,零配置

更改dockerfile内容,将parcel打包后的内容COPY进容器

FROM nginx
COPY ./index.html /usr/share/nginx/html/
COPY ./dist /usr/share/nginx/html/dist
EXPOSE 80

添加好了你的库之后,选择这里的设置

然后添加两个环境变量:

DOCKER_USERNAME和DOCKER_PASSWORD

这里,我将我编写的mini-react框架源码,放入docker中,然后使用parcel打包工具打包,再用nginx反向代理~

特别提示:这里的Docker容器,想要后台运行,就必须有一个前台进程。容器运行的命令如果不是那些一直挂起的命令(比如tcp,ping),就是会自动退出的

通过 docker ps -a 可以看到容器关闭的原因

注意 :jinejietan/mini-react应该换成你的用户名/包名,再push代码

这是思维导图:

当配置成功,代码被推送到gitHub上后,travis-ci帮我们自动构建发布新镜像

一定要学会使用: docker ps -a 查看容器的状态

成功的提示:

至此,发布,自动构建镜像已经完成

正式开始拉取镜像,启动容器

我们刚才发布的镜像名称是:jinjietan/mini-react

先使用下面几条命令

docker中 启动所有的容器命令
 
docker start $(docker ps -a | awk '{ print $1}' | tail -n +2)
docker中 关闭所有的容器命令

docker stop $(docker ps -a | awk '{ print $1}' | tail -n +2)
docker中 删除所有的容器命令

docker rm $(docker ps -a | awk '{ print $1}' | tail -n +2)
docker中 删除所有的镜像

docker rmi $(docker images | awk '{print $3}' |tail -n +2)
tail -n +2 表示从第二行开始读取

清除当前宿主机上面所有的镜像,容器,依次执行

然后使用:

docker image pull jinjietan/mini-react:latest

拉取镜像,这时候需要下载

拉取完成后,使用

docker images

可以看到jinjietan/mini-react:latest镜像已经存在了

我们使用

docker container create -p 8000:80 jinjietan/mini-react:latest

创建这个镜像的容器,并且绑定在端口号8000

最后输入下面的命令,即可启动mini-react框架的容器

docker container start  ***(上面create的返回值)

浏览器输入 127.0.0.1:8000 发现,访问成功,框架生效。

Docker的使用,我们大致就到这里,个人认为,用Docker比不用好,这个技术已经快跟TypeScript一样,到不学不行的阶段了。

并不是说你非要用它,而是比如说,你如果不怎么懂TypeScript,你就没办法把如今那些优秀库的大部门的源码搞得那么清楚。

越来越多的技术在依赖Docker

当然,其实这个mini-react框架源码也是不错的,如果有兴趣可以了解以下,源码都在:

mini-react框架+镜像配置源码,记得切换到diff-async分支哦~

https://github.com/JinJieTan/...

如果觉得写得不错,可以右下角点个在看

关注一下我的微信公众号:前端巅峰 ~ 回复加群即可加入大前端交流群

主要注重技术点:即时通讯,跨平台重型应用开发,全栈工程师方向前沿技术

查看原文

赞 149 收藏 99 评论 11

Chor 赞了文章 · 2020-09-20

TypeScript 之父:”开源是赢得 JavaScript 开发人员的唯一途径“

Anders Hejlsberg

作者:Liam Tung
编译:王治治丨发布自:思否编辑部
原文链接:https://www.zdnet.com/article...


微软的开源编程语言 TypeScript 是 JavaScript 的一个超集,到今年 12 月就满 10 岁了。

它已经成长为构建在浏览器中运行的应用程序的首选语言,但早在 2010 年,它不得不在微软公司的文化中选择自己的方式,当时微软公司仍然害怕开源。

TypeScript 之父 Anders Hejlsberg 是微软的丹麦软件工程师和技术研究员,他在一次媒体采访中描述了 2010 年,在微软首席 Steve Ballmer 的领导下做出的决定 —— 开源战略是赢得JavaScript开发人员的唯一途径。

Ballmer 在 2001 年称 Linux 是威胁微软所有知识产权的 "癌症",而在 2010 年,微软的开源仍然是高层管理人员的棘手问题。

"Linux被视为对Windows的威胁,而事实证明,它恰恰相反。"Hejlsberg 说。

Ballmer 于 2014 年 8 月卸任 CEO,此后他收回了这一立场,如今,在微软 CEO 萨提亚-纳德拉(Satya Nadella)的领导下,该公司专注于云计算并热爱开源,还拥有开源代码库 GitHub。

微软其他主要的开源项目包括流行的代码编辑器 Visual Studio Code(VS Code)、.NET Code 和 TypeScrip。

自 2012 年正式发布以来,TypeScript 已经成为浏览器应用前端开发的重要语言,被 Slack、Airbnb 等公司采用,当然还有用 TypeScript 构建 VS Code 的微软自己。现在,该语言与Java、JavaScript 和 Python 一样,在十大编程语言中稳居一席之地。

但在 2010 年,Hejlsberg 知道,将 TypeScript 作为一个开源项目在微软雷德蒙德总部推销可能会很困难。在这一编程语言即将迎来 10 周年之际,Hejlsberg 回忆起微软是如何以畏惧的态度看待任何开源项目的。

"到圣诞节的时候就 10 年了,TypeScript 最初真的只是一个想法,'让我们看看我们是否可以在 JavaScript 方面做得更好'。

"我们构建了一些原型,然后将它们整合在一起。但是很明显,我们要吸引 JavaScript 社区的唯一方法就是开源。那时的微软进行了一次不同观点的讨论。"

Hejlsberg 的 TypeScript 团队“实际上必须让旗杆上的一群人对我们在这里应该做什么有强烈的看法”,但没有提及任何可能反对它的高管的名字。但他指出,当时在鲍尔默(Balmer)的领导下,微软对开源感到“非常矛盾”。Hejlsberg 说:“我们知道这是我们必须做的事情,但是我们没有经验。”

但是随着 TypeScript 的成熟和开源在 Microsoft 从 Windows 到云的转变中获得认可,该公司将在 2014 年通过 GitHub 上的公共存储库将 TypeScript 转换为“开放开发”模型。使用 GitHub 可以使 TypeScript 和 JavaScript 开发人员社区影响其未来。

由于采用了不同的方法,他的团队现在与使用 JavaScript 或 TypeScript 的开发人员之间的客户“零距离”。

Hejlsberg 解释说:“从技术意义上讲,开源是指您向人们提供您的源代码并放弃您的知识产权,从技术上讲,这是开源的。”

“但是接下来是开放式开发,您实际上是在公开地完成整个开发过程,自 2014 年 TypeScript 移至 GitHub 以来,我们一直在这样做。”

如今,微软于 2018 年以 75 亿美元的价格收购了 GitHub, TypeScript 团队则由大约 20 名微软工程师进行日常工作的地方,从而实现“与客户保持紧密联系,这是我从未见过的事”。

Hejlsberg 在 Borland 任职后,自 1996 年以来一直在 Microsoft 工作,并建立了Turbo Pascal编译器。在微软,他还是C#的首席架构师。

Hejlsberg 说,在迁移到 GitHub 之前,TypeScrip t用户将在开发者大会上提交错误或需求请求,但是他的团队将需要一年的时间来交付通常会错过新功能。

为什么要创建TYPESCRIPT?

TypeScript在微软孵化的时候,正值网络和公司的一个有趣的关头。随着 Internet Explorer 的份额被 Google Chrome 夺走,微软在 2015 年推出了使用开源 Chakra JavaScript 引擎的 Edge 浏览器。但当时谷歌 Chrome 浏览器凭借其对网络标准的关注和强大的 JavaScript 引擎 V8,已经成为浏览器大战的明显赢家。

"浏览器大战结束了,谷歌已经打造出了 Chrome 浏览器,HTML5 也正在发生。谷歌还打造了一个非常高效的 JavaScript 引擎,JavaScript 的效率已经大大提升。每个人都开始意识到,浏览器将成为真正的浏览器。"Hejlsberg 回忆道。

Hejlsberg 说:"当时开发人员已经开始为浏览器构建庞大的 JavaScript 应用,并努力用 JavaScript 编写这些应用,他说这种语言缺乏模块、类等关键功能,更重要的是,缺乏在程序中通过规则创建秩序的类型系统。

然后是开发者工具,比如像 VS Code 这样的集成开发环境(IDE),它们可以用于提高开发者的生产力。

“如果您考虑到我们开发工具(例如现代 IDE)的强大功能,我们都认为这是理所当然的,例如 Visual Studio 或 WebStorm(来自 JetBrains)以及其他产品 —— 我们拥有所有的程序员生产力功能,例如 VSCode 的 IntelliSense,代码定义代码导航需要 IDE能够推理出您正在处理的代码。”他说。

“类型系统是您推理代码的一种方法。它是在运行和部署代码之前检查代码的能力。如果没有使用某种语言的类型,这几乎是不可能的。

"当时,开发人员正在做一些疯狂的事情,比如编写整个产品,比如Outlook.com,它一度是用 C# 的变体编写的,而 C# 是用一种叫做 Script Sharp 的工具交叉编译成 JavaScript 的。"

Google 有一个类似的交叉编译工具,叫做 Google Web Toolkit(GWT),它允许其开发人员用 Java 编写并交叉编译成 JavaScript。

“他们可以得到一个真正的 IDE,具有项目功能、模块和类、IntelliSense 和语句完成,然后他们可以将 JavaScript 视为一种指令语言,"他说。

在 TypeScript 出现之前,微软就想把一个叫 Script Sharp 的工具变成产品。但 Hejlsberg 不知道这些开发者是否愿意 "弯下那么大的腰来用 JavaScript 写作"。那么为什么不尝试用 JavaScript 解决真正的问题呢?

"因为肯定不会通过告诉人们用另一种语言编写来为JavaScript建立最佳的开发体验。"他说。

"所以我们开始研究我们能做什么来解决这些问题,为 JavaScript 构建更好的工具。这就是 TypeScript 的真正起源。关键是在语言中添加一个类型系统,而不夺走当初让 JavaScript 如此受欢迎的所有东西。"

Hejlsberg 和公司决定建立一个 "可擦除的类型系统" —— 这个组件使 TypeScript 成为 JavaScript 的超集。在编译时,TypeScript 会删除所有的类型,并将其返回给 JavaScript。

"从某种意义上说,这是一个只存在于开发者体验期间的类型系统,但在运行时它就会消失。但在运行时,它能给你带来所有的好处,而没有任何的缺点。"他解释道。

TYPESCRIPT的主要竞争对手是什么?

根据 Hejlsberg 的说法,TypeScript 唯一真正的竞争对手是 JavaScript,因为这两种语言现在都是运行前端应用程序的首选语言,而 WebAssembly 正在模糊本地应用程序和浏览器应用程序之间的界限。

"但 JavaScript 并不是真正的竞争对手,它是我们的孪生兄弟或另一个自我。"他说。

"问题实际上是,'是否有语言可以与 JavaScript 和/或 TypeScript 竞争?有,也没有。但你在这些列表上看到的每一种语言通常都有一个亲和点。就像 Python 在机器学习和 AI 中找到了一大堆用途,而 SQL 被用来创建数据库,C# 和 Java 在企业后端很重要。

"你知道,JavaScript 和 TypeScript 是前端的运行方式。它是唯一一种在浏览器中运行的语言,除了现在的 WebAssembly。所以有一种完全自然的亲和力。如果你要写一个 web 应用,你就会用 JavaScript 或 TypeScript 来写。"

像 JavaScript 一样,Microsoft TypeScript 也跟踪 ECMAScript 标准的发展,而且越来越多的TypeScript被开发出来,以迎合那些喜欢纯JavaScript的开发者。

“一旦功能进入 ECMAScript 标准化流程的第三阶段,我们就认为该功能已准备就绪,可以在 TypeScript 中采用,然后我们与社区合作以合并请求。”

在过去的两年里,TypeScript团队通过改进对JavaScript js.doc注释中类型注释的支持,致力于“为那些不想使用TypeScript的人”构建TypeScript。

“由于 TypeScript 是 JavaScript 的超集,因此您也可以将 JavaScript 视为 TypeScript 的子集。这意味着我们整个 TypeScript 工具链非常乐于处理 JavaScript 并在 JavaScript 之上提供所有服务,” Hejlsberg 说。

“从某种意义上说,JavaScript 就像没有类型注释的 TypeScript。因此,一种流行的使用 JavaScript 的方式是,人们将类型注释放在注释(js.doc注释)中,而不是直接在源代码中使用类型注释。编译器实际上可以从这些注释中获得很多信息。因此,在过去的几年中,我们实际上增强了对 js.doc 注释的支持,并增强了直接使用 JavaScript 的能力。”


Anders Hejlsberg

2015 年 Anders Hejlsberg 第一次来中国,就参加了 SegmentFault 思否组织的线下活动。作为 TypeScript 的核心开发者,在当时的活动中他就曾表示非常感谢中国如此多的开发者能够支持 TypeScript,另外他也表示,通过 SegmentFault 思否策划的线下活动,看到了中国整个开发者社区非常大的热情,也希望在之后越来越多的开发者能参与到 TypeScript 的工作中来。

拓展阅读:
《SegmentFault 创始人祁宁对话 C# 之父 Anders Hejlsberg》
《SegmentFault 为何成为本次微软 Ignite 大会中国区唯一社区代表?》

segmentfault 思否

查看原文

赞 8 收藏 1 评论 1

Chor 发布了文章 · 2020-09-20

如何基于心智模型打造更棒的用户体验

Mental models in UI-UX design - examples and tips

想要和你的用户保持意见上的一致吗?那么你必须了解他们的心智模型。本文将针对此话题,探讨一些最佳实践和案例,助你快速入门。

提升用户体验的关键是什么?当然是了解用户的想法 —— 就这么简单。在心理学上,我们用心智模型这一术语来描述用户对某个概念或者流程的思考模式。

心智模型是用户体验的一个主要组成部分,也是 UI-UX 设计师经常需要打交道的对象。用户测试和调研让你可以确认目标用户群体以及他们希望达成的目标,而发掘他们的心智模型则可以让你找出帮助他们达成目标的方法。

在本文中,我们将探讨如何将心智模型理论应用于 UI 设计中,同时还会研究一些如何匹配用户心智模型的方法,最后当然还少不了一些经典案例的学习。

目录:

什么是心智模型?

1943 年,苏格兰心理学家、哲学家肯尼思·克雷克(Kenneth · Craik)注意到,人们会对周遭世界的运行规律建立一种“小范围”的认知模型,他将这种模型称之为“心智模型”。在理解如何将其应用于 UI 设计领域之前,我们首先需要进行一次现实检验,看看心智模型是如何帮助人们理解现实世界中的事物的。

现实世界中的心智模型

语言是人类区别于其它动物的主要能力之一,不妨先来看看它是如何帮助我们塑造这个世界的。

以英语为母语的人在学习日语时,常常会疑惑为何没有将来时态,同时也会因为碰到了在英语中不曾出现的时态而感到诧异。这其中正是心智模型在起作用。

维特根斯坦的狮子(译者注:来源于哲学家维特根斯坦说过的一句话:“如果狮子能讲话,我们也不能理解它”)能够讲英语(或者是日语!),但是却没有人能听懂它在讲什么。这是因为,尽管这头狮子和我们讲一样的语言、使用一样的词汇,但它基于感知方式形成的对于世界的概念和我们人类大相径庭。狮子更多依靠的是嗅觉,并且完全凭本能行动,不具备抽象的思维。

Mental models in UI-UX design - Wittgenstein’s Lion in conversation

假如你问一头狮子,“你今天过得怎么样?”,它脱口而出的可能是这样的话:

“不咋地。舌头很重(很累),只有沉积岩的味道(今天还没有饱餐一顿)…… 走近一点(你就是我的下一顿食物了!)”

对现实以及事物运行规律的感知

心智模型听上去似乎是一个很复杂的心理学概念,但实际上,它并不难理解:人们从过往的经历中进行学习,并将获得的知识应用在所面临的工作中。上面关于语言的例子就很好地说明了人们是如何将所学知识应用于现实世界中的。

简而言之,心智模型描述的是人们当前对于某些事物的理解、对现实以及事物运行规律的感知。

将心智模型应用于 Web 设计中

用户体验设计存在的一个主要原因,就是为了迎合人们的心智模型,从而帮助他们以尽可能最佳的体验去完成任务。

那么关键的问题来了,如何将心智模型应用于 UI-UX 设计中呢?我们又要如何利用用户体验中的这种心理因素来构建更人性化的产品呢?

Mental models in UI-UX design - climb inside the user's mind

查理 · 芒格曾经提出一个著名的症状叫“锤子综合征”:一个手握锤子的人,看什么都是钉子。这种症状完美诠释了心智模型的错配。

如果你想创建真正对用户友好的产品,那么你就要学会从用户的角度去看待事物,而这离不开用户测试数据。或者,你也可以尝试让用户的心智模型适应你的网站特点。

怎么创建符合用户心智模型的产品呢?我们先从规避心智模型错配讲起。

规避心智模型错配

作为设计师的我们可能都犯过一个错误,那就是利用自己天马行空的想法去打造一个足够惊艳的 UI,并悉数用上平生所学,仿佛要让用户感觉除了不能征服世界之外,自己(用户)在这个产品中无所不能。事实上,唯独对于用户来说,这是很不实用的。他们更宁愿跳进一艘坠毁的 UFO 的驾驶舱,并马上学习怎么驾驶它,好逃离你的网站!

这就是心智模型错配的一个典型案例。用户并不具备和你一样的设计经验,因此你的设计对他们来说过于复杂,让人觉得不知所措。他们不知道屏幕上各种各样的元素具体是做什么的,也不知道他们之间的联系。

Mental model mismatch - consequences

当你无法匹配用户的心智模型时,交互成本会增加,用户会觉得沮丧,想要“驾船逃离”(还记得上面的 UFO 吗?)。在设计领域中,这个问题比你想象的要更加常见,就连那些大型设计团队也可能犯下这些致命错误。有时候,最好的设计师也可能由于缺乏相关信息以及对用户的了解,从而夸大或者低估用户的心智模型和能力。

匹配用户的心智模型

首先需要明白一点:大部分时候,你的用户都是在浏览或者使用其它的网站和 app。当他们来到你的网站时,其实脑海里还保留着对于先前浏览网站的认识。

如果你的 UI 设计中的元素或者网站的信息架构符合用户的心智模型,那么他们就能顺利浏览你的网站,并找到自己想要的东西。

心智模型匹配方法

发现你的用户群

如果想要匹配你的用户的心智模型,最好的开始方式就是找出你的目标用户群。世界上的人这么多,可不是所有的用户都有相同的心智模型!

黄金法则就是进行一些用户测试。从用户调研中获取数据,然后再构建用户画像,这是一种绝佳的实现方式。当你构建了用户画像之后,就可以设计适合用户的 UI,接着再对用户进行测试,检验 UI 的特点是否真的符合用户的心智模型。

discovering you user base for mental models

举个例子,假设你的网站的目标用户是 80 岁以上的老年人,他们也许并不熟悉汉堡菜单选项,这时候你就得想出一种替代方案,接着再进行测试,来验证你对于他们的心智模型的假设。

从大型网站中进行学习

正如上面提到的,你的大部分用户会浏览其它的网站,这可能会吸引你去关注一些业内知名品牌的网站,从而获取灵感。举个例子,你可能会去浏览一些流行品牌的网站,诸如 Apple、Netflix、Hubspot 或者是 Youtube。他们都在商业上取得了成功,何不效仿他们的做法呢? 对吧?

当考虑效仿大型企业的做法时,你应该小心再小心、谨慎再谨慎,因为你们两者的处境是不同的。诚然,许多大型品牌的网站不愧为高可用性的典范,但他们也可能会犯错误。不同的是,他们有能力承担这种后果,用户也更有可能原谅他们。

举个例子,也许你正打算复制 Youtube 上某一块 UI 元素的功能,却没有意识到这个功能其实是存在问题的,并且亟待回炉重造!或者另一种情况,你没有意识到人家只是在拿新的 UI 设计试水而已。

learning from big websites for user mental models

这也解释了为什么在小型公司的网站上反而能够频频体验到高可用性和出色的用户体验,因为它们承受不起失去新用户的风险啊。当然,也不乏一些方法可以让你在规避风险的同时还能从大公司那里获得灵感 —— 其中一种方法就是在这些网站上进行用户测试。

如果你坚持要“抄袭”大公司的“作业”,那么正确的方式应该是针对一个知名网站进行第三方用户测试。举个例子,你只需要针对你想效仿网站的特点制订一份问卷,再让你的用户去填写即可。对用户反馈的结果进行评估,亲自看看这些方法是否会如预期那样生效,这样可以避免犯下代价高昂的错误!

卡片分类法

卡片分类法是一种有效的用户测试方法,可以让你摸清用户关于导航的心智模型、了解用户是如何在你的网站中“寻路”的。

Mental models in UI-UX design - card sorting session with happy users

通过卡片分类法进行测试,你可以设计网站的信息架构,以确保其符合用户的心理预期。这也可以方便用户找到自己需要的信息,减少交互成本。

线框绘制与原型设计

另一种了解用户心智模型的方式,是使用类似 Justinmind 这样的原型工具将一个线框图或者原型图放在一起。你可以基于想要测试的特性、元素或者组件来设计屏幕,并让用户进行一次出声思考(译者注:即 think-aloud,指的是提供给被测用户待测的产品或界面原型,要求被测用户根据指定任务操作产品或界面,与此同时,即时地说出使用产品界面时的想法、感受和意见)。这可以帮助你了解他们的习惯性思维过程,也能让你知道他们是如何理解你的 UI 的。

举个例子,用户是否更愿意点击回退按钮来撤销页面上的某个操作呢?在这种情况下,你可能需要更清楚地解释撤销功能的实际操作方式,或者干脆让你的产品顺应用户的使用习惯,在点击回退按钮的时候确实能够撤销操作。

追随设计趋势

此外,在设计 UI 的时候,你可能会(也可能不会)追随特定的设计趋势,这取决于你的用户真正需要的是什么,同时也取决于你的预算。但无论如何,你的预算中总有一部分需要花费在设计中,从而呈现最佳的用户体验。

如果你想要轻松跟上这个日新月异的世界的步伐,我推荐你阅读这份清单,上面列举了网上最棒的几个 UX 设计相关的博客。

举个例子,假设你想要追随某种以鲜艳颜色和交互动画为主的 UI 趋势,但考虑到用户的年龄层以及网站所属的领域分类,你可能不得不推迟使用这种设计,毕竟设计的重构也是挺烧钱的。

copying design trends for ux design

不过,许多 Web 设计趋势更趋向于改进 UI 元素以带来更高的可用性,比如放弃采用拟物化设计,而采用扁平化设计。类似这样的趋势都始于提高可用性这一初衷,这也是我们在设计中时常需要考虑的。

调整用户的心智模型

苹果公司推广了拟物化设计,试图让人们的思维向数字世界靠拢。为了达成这个目标,他们创建了一种高度模仿现实世界中物品的图像学。

拟物化设计的一个典型案例就是标示删除文件的垃圾桶图标。不过,随着全球人口的文化技术水平逐渐提高,拟物化设计已经显得没有那么必要了。这种转变也正是一个引导用户、有效调整他们心智模型的典例。

Mental models in UI-UX design - Apple skeuomorphic camera

大部分时候,你会发现最好的做法是调整你的 UI,使其适应用户的心智模型,这也是比较保险的方式。不过有时候,你也需要反过来稍微地调整用户的心智模型,使其适应一些新的东西。若是遇到这种情况,你就得确保让用户的学习曲线尽可能地保持平缓,以避免产生过多的交互成本。

当涉及特殊产品、网站或者 app 功能时,简洁明快的使用说明尤为重要,尤其对于表单设计更是如此。对于步骤较多的功能,你可能需要为其创建一些图示、demo 甚至是演示动画!这三种方式实际上就属于视觉叙事的一种形式,它们能够很好地激发用户的学习能力,帮助他们以一种愉快且自然的方式理解产品功能。

Web 设计中关于心智模型的真实案例

接下来我们通过部分案例来了解一些常见的用户心智模型。这些案例展示了用户是如何脱离我们设计师、对网站和 app 的运行机制建立起自己的理解的。

1. 谷歌搜索栏

说到最为人们津津乐道的典型的用户心智模型,肯定少不了谷歌的 URL 搜索栏。

浏览器顶部位置有一个 URL 输入框,而它的设计初衷并不是为了实现和搜索栏一样的功能,其实际用途是为了让用户输入完整的网址,从而实现网站的跳转。

Mental models in UI-UX design - Google URL search bar

不过,谷歌很快注意到:很多用户以为它的功能和搜索栏是一样的,只有那些有经验的网民清楚二者的区别。因此,谷歌决定干脆迎合大众的心智模型。

谷歌将搜索栏的功能集成到了 URL 输入框中,甚至还新增了放大镜这种在各个网站的搜索栏中频繁出现的图标。经过这番改造,它依然能够像旧有的 URL 输入框那样,将用户带到其它网站,但同时也能像搜索栏那样展示一系列的搜索结果。

2. 模态窗

另一个和用户心智模型相关的案例就是在网站中经常会弹出的模态窗。通常情况下,它可能会询问用户是否喜欢 cookie(当然,不是曲奇饼,是web cookie!),还是想要直接登录。

现在还有很多用户以为,只要按下浏览器上的后退按钮,这个弹出模态窗的页面就会重置,模态窗也会消失。当浏览器将用户导航至完全离开这个页面时,他们会觉得自己似乎已经回退了两步。

Mental models in UI-UX design - Wikipedia modal window with greyed-out background

事实上,他们仅仅回退了一步。因为模态窗是相同网页的一个功能,需要通过关闭按钮才能解除。

3. 带有不可点击按钮的图片

下一个例子是一个显而易见的问题,不幸的是,在今天的 UI 设计中,它仍然存在:在图片中引入一个按钮,并将其作为图片的一部分。

images with unclickable buttons

在这种情况下,整个图片或者卡片都是可点击的,然而按钮反倒不能点击了。这显然和大部分人的心智模型相悖,毕竟按钮就应该是可以点击的才对。虽然这张图片依然可以正常工作,但你最好让它看上去是可以点击的。

这里最关键的问题在于,如果类似按钮这样的元素表现出来的行为和用户预想的不一致,那么他们可能会对你网站的 UI 设计产生不信任感,并认为你的网站充斥着各种各样的 bug。

4. 复选框和单选按钮

另一种可能会导致心智模型错配(或者让你的用户感到头疼)的情况,就是错误地使用复选框和单选按钮

在大部分用户的潜意识里,复选框包含一系列可选的项目,并且一次可以选中多个。而单选按钮则完全不同,一次只能选中一个。

mental models in UI-UX design - radio buttons vs checkboxes

现在,如果你准备用一系列单选按钮来实现多选功能,那么这将与互联网表单诞生以来人们就既已形成的心智模型相悖 —— 这是很不明智的!

5. 日历型日期选择器

由于用户角色的差异,这点可能存在争议性。但大多数日期选择器所携带的日历确实是一个很好的例子,作为一个通用型 UI 元素,它试图迎合大部分用户对于日期的心智模型。

Mental models in UI-UX design - two date pickers to select a date range

从下拉列表中选择年月日未尝不可以,但对于大部分用户来说,从一个与传统日历相似的 UI 元素中选择日期,必然要来得更加简单。

总结 —— 心智模型

作为设计师,我们对 UI 设计已有充分的了解,但还有不少知识是需要从用户身上学习的。如果我们想知道如何设计出对用户最友好的 UI,那么就必须求教于用户,了解他们的想法。

这可能给我们自认为足够惊艳的设计泼冷水,“为用户创造”意味着利用我们的设计知识去创造他们能够理解的东西,也意味着我们可能需要冒着全线溃败的风险。

有时候,从长远来看,我们所需要的不过是一些用以节省时间的测试!

查看原文

赞 1 收藏 1 评论 0

认证与成就

  • 获得 461 次点赞
  • 获得 14 枚徽章 获得 0 枚金徽章, 获得 3 枚银徽章, 获得 11 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

注册于 2018-10-03
个人主页被 6.9k 人浏览