")]),v._v("/"),a("code",[v._v("| ")]),v._v("等)是最难驾驭的,因为浏览器对它们的处理总是在意料之外,宽高难以控制意味着我们将很难将其与 Canvas/SVG 的渲染效果对齐。因此,我们很可能需要在表格元素里嵌套绝对定位的"),a("code",[v._v(" ")]),v._v("元素,来使得表格最终渲染不会被轻易撑开导致偏差。")]),v._v(" "),a("p",[v._v("除此之外,我们还需要注意文字的排版、换行等情况在 Canvas/SVG 和 DOM 渲染中需要尽量保持一致。")]),v._v(" "),a("h3",{attrs:{id:"各种渲染方式的选择"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#各种渲染方式的选择"}},[v._v("#")]),v._v(" 各种渲染方式的选择")]),v._v(" "),a("p",[v._v("每种渲染方式都有各自的优缺点。")]),v._v(" "),a("p",[v._v("在图表渲染引擎中,最常见的是 Canvas 渲染和 SVG 渲染,我们也可以从 ECharts 官网中找到两者的对比描述:")]),v._v(" "),a("blockquote",[a("ol",[a("li",[v._v("一般来说,Canvas 更适合绘制图形元素数量较多(这一般是由数据量大导致)的图表(如热力图、地理坐标系或平行坐标系上的大规模线图或散点图等),也利于实现某些视觉特效。")]),v._v(" "),a("li",[v._v("但在不少场景中,SVG 具有重要的优势:它的内存占用更低(这对移动端尤其重要)、并且用户使用浏览器内置的缩放功能时不会模糊。")])]),v._v(" "),a("p",[v._v("选择哪种渲染器,可以根据软硬件环境、数据量、功能需求综合考虑:")]),v._v(" "),a("ul",[a("li",[v._v("在软硬件环境较好,数据量不大的场景下,两种渲染器都可以适用,并不需要太多纠结")]),v._v(" "),a("li",[v._v("在环境较差,出现性能问题需要优化的场景下,可以通过试验来确定使用哪种渲染器。比如:\n"),a("ul",[a("li",[v._v("在需要创建很多 ECharts 实例且浏览器易崩溃的情况下(可能是因为 Canvas 数量多导致内存占用超出手机承受能力),可以使用 SVG 渲染器来进行改善")]),v._v(" "),a("li",[v._v("如果图表运行在低端安卓机,或者我们在使用一些特定图表如水球图等,SVG 渲染器可能效果更好")]),v._v(" "),a("li",[v._v("数据量较大(经验判断 > 1k)、较多交互时,建议选择 Canvas 渲染器")])])])])]),v._v(" "),a("p",[v._v("而在在线表格的场景,我们会发现不同的团队会选择不同的渲染方式:")]),v._v(" "),a("ul",[a("li",[v._v("谷歌表格使用了 Canvas/DOM 两种渲染方式,其中 DOM 渲染主要用于首屏直出")]),v._v(" "),a("li",[v._v("金山表格使用了 Canvas/SVG 两种渲染方式,其中 SVG 渲染主要用于首屏直出")]),v._v(" "),a("li",[v._v("飞书表格使用了 Canvas 渲染")])]),v._v(" "),a("p",[v._v("其实我们可以发现,这些团队很多在使用几种渲染方式,原因几乎都是因为使用了 Canvas 绘制作为主要渲染方式。但考虑到首屏渲染的情况,Canvas 则需要一系列的数据计算和渲染过程,不适合首屏直出的方式,因此会适配上 DOM 或者 SVG 进行首屏直出。")]),v._v(" "),a("p",[v._v("实际上,Canvas 渲染有一个比较致命的弱点:交互性很差。比如用户选择某个格子,进行拖拽、调整宽高、右键菜单等操作,在 Canvas 上是很难命中具体的元素的。因为 Canvas 绘制过程中并不像 DOM 和 SVG 一样有层次结构,最终的渲染结果也只是一个图像。因此,在线表格场景下大多数 Canvas 绘制都需要结合 DOM 引擎一起,获取到用户选择的元素、处理用户交互事件,然后进行二次计算和响应。")]),v._v(" "),a("p",[v._v("关于首屏直出,后面有空也可以简单唠唠。")]),v._v(" "),a("h2",{attrs:{id:"结束语"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),a("p",[v._v("本文介绍了渲染引擎架构中,使用多种渲染方式以及底层渲染器适配的设计。")]),v._v(" "),a("p",[v._v("我们常常说给项目选择最优的解决方案,实际上我们也会发现,正因为往往没有所谓最优解,这些产品才会针对不同的场景下提供了不同的解决办法。比如,考虑到性能问题 ECharts 提供了 Canvas/SVG 两种绘制方式;又比如考虑到首屏直出的效率,各个在线表格的团队分别适配了更合适的渲染方式。")])])}),[],!1,null,null,null);_.default=e.exports}}]);
--------------------------------------------------------------------------------
/assets/js/137.e2e49efd.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[137],{698:function(_,v,t){"use strict";t.r(v);var a=t(69),o=Object(a.a)({},(function(){var _=this,v=_.$createElement,t=_._self._c||v;return t("ContentSlotsDistributor",{attrs:{"slot-key":_.$parent.slotKey}},[t("p",[_._v("最近接触到一些针对多人同时操作进行冲突处理的场景,简单介绍下相关的实现方式。\n")]),_._v(" "),t("h2",{attrs:{id:"operational-transformation-ot"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#operational-transformation-ot"}},[_._v("#")]),_._v(" Operational transformation(OT)")]),_._v(" "),t("p",[_._v("OT 算法最初是为在纯文本文档的协作编辑中的一致性维护和并发控制而发明的,在本文中我们也主要掌握一致性维护相关的一些方法。")]),_._v(" "),t("h3",{attrs:{id:"协同软件的冲突"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#协同软件的冲突"}},[_._v("#")]),_._v(" 协同软件的冲突")]),_._v(" "),t("p",[_._v("想必大家都知道,在多人协同场景下,必然会出现各种各样的冲突场景。")]),_._v(" "),t("p",[_._v("举个例子,团队接到一个超大型的项目需要开发,老板说10分钟给出排期和分工。然后瑟瑟发抖的大家二话不说打开腾讯文档,创建了一个表格,让每个模块负责人先针对自己模块来进行工作量拆分和预估。")]),_._v(" "),t("p",[_._v("PM 创建了表格之后,将表格丢到群里,说前端后台各自创建一个子表来写相应的工作量情况。")]),_._v(" "),t("p",[_._v("前端张三马上点开了表格,点击添加子表,系统自动生成“工作表2”这样一个子表。与此同时,后台李四也进行了同样的操作。")]),_._v(" "),t("p",[_._v("那么问题来了,一个表格中原则上并不允许两个同样名字的子表,这个时候冲突就出现了,我们要怎么处理呢?")]),_._v(" "),t("p",[_._v("虽然是两个同样名字的子表,但我们并不能将它们进行合并,因为对于张三和李四来说,他们就是在自己创建的子表里写工作拆分和排期情况。所以,我们需要使用对用户影响最小的方式,来解决掉这个冲突。")]),_._v(" "),t("h3",{attrs:{id:"操作的拆分"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#操作的拆分"}},[_._v("#")]),_._v(" 操作的拆分")]),_._v(" "),t("p",[_._v("为了处理冲突,我们需要将一些操作进行拆分。例如,我们插入一个子表这样一个操作,除了插入自身的操作,可能需要对其他子表进行移动操作。那么,对于一个子表来说,我们的操作可能会包括:")]),_._v(" "),t("ul",[t("li",[_._v("插入")]),_._v(" "),t("li",[_._v("重命名")]),_._v(" "),t("li",[_._v("移动")]),_._v(" "),t("li",[_._v("删除")]),_._v(" "),t("li",[_._v("更新内容")]),_._v(" "),t("li",[_._v("...")])]),_._v(" "),t("p",[_._v("只要拆分得足够仔细,对于子表的所有用户行为,都可以由这些操作来组合成最终的效果。例如,复制粘贴一张子表,可以拆分为"),t("code",[_._v("插入-重命名-更新内容")]),_._v(";剪切一张子表,可以拆分为"),t("code",[_._v("插入-更新内容-删除-移动其他子表")]),_._v("。通过分析用户行为,我们可以提取出这些基本操作。")]),_._v(" "),t("h3",{attrs:{id:"操作间的冲突处理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#操作间的冲突处理"}},[_._v("#")]),_._v(" 操作间的冲突处理")]),_._v(" "),t("p",[_._v("基本操作提取出来之后,我们就可以很仔细地梳理和分析操作和操作之间是否会产生冲突,以及要怎么处理了。")]),_._v(" "),t("p",[_._v("例如,我们上面提取出来的关于子表的操作中,包括"),t("code",[_._v("插入")]),_._v("、"),t("code",[_._v("重命名")]),_._v("、"),t("code",[_._v("移动")]),_._v("、"),t("code",[_._v("删除")]),_._v("、"),t("code",[_._v("更新内容")]),_._v("五种操作,实际上,每种操作都可能和自身、以及其他四种操作都发生冲突,于是我们可能有"),t("code",[_._v("5*5=25")]),_._v("种需要考虑的冲突情况。")]),_._v(" "),t("p",[_._v("我们先来大致看看这 25 组冲突中,是不是全都需要进行冲突处理的。例如,"),t("code",[_._v("更新内容")]),_._v("一般来说跟其他几个操作都不会发生什么冲突,因为更新内容改变的是表格的内容,而不是位置、名字这些,一个表格内部和另一个表格内部基本上不会发生冲突。但"),t("code",[_._v("重命名")]),_._v("和"),t("code",[_._v("插入")]),_._v("之间,满足一定条件的时候(插入的子表名字和重命名的名字相同)可能就会产生冲突。")]),_._v(" "),t("p",[_._v("你可能会觉得疑惑,"),t("code",[_._v("插入-重命名")]),_._v("和"),t("code",[_._v("重命名-插入")]),_._v("不是一样的吗?我们先带着这个疑问继续往后看。")]),_._v(" "),t("h3",{attrs:{id:"最终一致性的实现"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#最终一致性的实现"}},[_._v("#")]),_._v(" 最终一致性的实现")]),_._v(" "),t("p",[_._v("说了那么多,看起来跟 OT 算法完全没有关系呀??")]),_._v(" "),t("p",[_._v("OT 算法的一个核心目标,是实现最终一致性。为什么会有最终一致性的需求呢?")]),_._v(" "),t("p",[_._v("我们再看回张三和李四的例子,由于系统不允许存在有两个同样名称的子表,因此服务器会根据收到请求的顺序,将第二个子表进行重命名。假设张三的请求先到达服务端,那么李四创建的表格则需要被自动重命名为“工作表2(自动重命名)”。")]),_._v(" "),t("p",[_._v("为了让用户体验更流畅,我们在用户侧使用无锁、非阻塞的方式来进行协同。也就是对于李四来说,他已经创建了这样一个叫“工作表2”的子表了,由于网络延迟等原因可能还已经编辑上了。这时候服务端告诉李四,张三已经创建了一个“工作表2”的子表了,你自己看着办吧。")]),_._v(" "),t("p",[_._v("李四说,我已经编辑了这么多,你总不能让我全删掉重来吧。所以李四想了个办法,先将自己本地的表格"),t("code",[_._v("重命名")]),_._v('为"工作表2(自动重命名)",然后将张三的子表'),t("code",[_._v("插入")]),_._v("。除此之外,由于自己的插入顺序在后面,还需要将自己的子表"),t("code",[_._v("移动")]),_._v("到后面一个位置。做完这些操作之后,李四告诉服务器,自己也"),t("code",[_._v("插入")]),_._v("了一个叫“工作表2(自动重命名)”的子表。")]),_._v(" "),t("p",[_._v("我们梳理下逻辑,可以得到:")]),_._v(" "),t("ul",[t("li",[_._v("对于李四本地,需要进行的操作是:"),t("code",[_._v("重命名 + 插入 + 移动")])]),_._v(" "),t("li",[_._v("对于服务器,需要进行的操作是:"),t("code",[_._v("插入")]),_._v("更新后的子表")])]),_._v(" "),t("p",[_._v("我们来看看这个 OT 算法的简略说明图:\n"),t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/google_ot.jpg",alt:""}})]),_._v(" "),t("p",[_._v("我们代入到张三李四这个场景下:\n"),t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/sheet_ot.png",alt:""}})]),_._v(" "),t("p",[_._v("可以看到,对于服务端来说,最终就是新增了两个子表,一个是张三的“工作表2”,另一个是李四的“工作表2(自动重命名)”。")]),_._v(" "),t("p",[_._v("除此之外,这个场景中还存在比较细致的时间问题。上面我们说李四收到服务器发来的张三的操作之后,在本地进行"),t("code",[_._v("重命名 + 插入 + 移动")]),_._v(",然后告诉服务器的操作是"),t("code",[_._v("插入")]),_._v("更新后的子表。但是还有个可能性,就是李四收到服务器的消息之前,就已经把自己"),t("code",[_._v("插入")]),_._v("“工作表2”的操作发出去给服务器了。这种情况下,服务器也需要具备处理冲突的能力,来维持最终一致性。")]),_._v(" "),t("p",[_._v("也就是说,我们在本地和服务器都有一套一致的冲突处理逻辑,才能保证算法的最终一致性。")]),_._v(" "),t("p",[_._v("但除了最终一致性,冲突处理还有其他很多需要考虑的场景,例如版本管理、性能问题等,后面有机会再慢慢介绍吧。")])])}),[],!1,null,null,null);v.default=o.exports}}]);
--------------------------------------------------------------------------------
/assets/js/187.dd0b26f2.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[187],{750:function(v,_,t){"use strict";t.r(_);var a=t(69),r=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("工作中我们总会遇到各种意料之外的事情,但不管是高兴、激动的时候,还是难过、低落甚至愤怒的时候,我们都需要保持清醒。")]),v._v(" "),t("h1",{attrs:{id:"工作中的各种情绪"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#工作中的各种情绪"}},[v._v("#")]),v._v(" 工作中的各种情绪")]),v._v(" "),t("hr"),v._v(" "),t("p",[v._v("相信每个人的工作里,都遇到过各种各样的事情,有完成某个挑战的激动、涨工资的喜悦,也有接手坑多代码的吐血、遇到不公正的气愤、不被重视的低落、不被理解的失望,等等。这些情绪会一直伴随着我们,要怎么和它们和谐相处呢?")]),v._v(" "),t("h2",{attrs:{id:"有情绪实在是太正常了"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#有情绪实在是太正常了"}},[v._v("#")]),v._v(" 有情绪实在是太正常了")]),v._v(" "),t("p",[v._v("遇到问题的时候,我们总是无法保持冷静,但反观周围的人都似乎没什么反应,是我太激动了吗?会显得太幼稚吗?")]),v._v(" "),t("p",[v._v("并不是这样的。这些事情发生在自己身上的时候,有情绪几乎是大多数人的自然反应。现实中我们看到很多不动声色的人,多半是因为事不关己,有一些是过来人。工作是一个较理性的场合,在这里我们尽可能“讲道理”,让大家都体面,这是大部分人的共识。")]),v._v(" "),t("p",[v._v("当然,一个合格的职场人应该具备控制自己情绪的能力,也有很多人教我们“要做情绪的主人”。但是不是意味着有情绪就是不合适的呢?也不是的,相比暗地里较量,适当的情绪表达可以让事情变得简单。")]),v._v(" "),t("p",[v._v("之前看一本谈判课的书上讲到,谈判过程中有比双赢更重要的事情,那就是实现目标。因为我们并不是任何时候都需要双赢,有时候我们想“全盘皆输”,因为希望双方都能体会对方的心情。有时候我们想要“我赢你输”,要给对方一个教训。情绪表达也一样,社会中“欺软怕硬”的人也不少,有时候使用愤怒也可以表达出你并不是一个包子,可以一定程度上改善现有的困境。")]),v._v(" "),t("p",[v._v("正因为我们对这个世界有所期待,才会在遇到不公的时候如此愤慨。而在社会上经历过好些年的,能保持初心的就更少了。我们之所以有各式各样的情绪,除了跟外在的环境有关,也跟个人的经历有关,并没有对错之分,这只是跟我们吃饭睡觉一样正常的事情。")]),v._v(" "),t("h2",{attrs:{id:"要怎么调整呢"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#要怎么调整呢"}},[v._v("#")]),v._v(" 要怎么调整呢")]),v._v(" "),t("p",[v._v("为了避免情绪的失控,我们总归需要对其进行调整。每个人的情绪值都是不一样的,并不是说我要跟别人一样淡定才好,而是控制在适合自己的范围内就可以。")]),v._v(" "),t("p",[v._v("当然,随着经历越来越丰富,大多数人对情绪也会渐渐习惯或是麻木。像如今的你,虽然已经能接受很多以前觉得不合理的事情,但又有多少事情不再能让你展现笑容了呢?从这个角度看来,“慢慢就会适应”并不一定是一个好方法,保持对工作和生活的热情、保持好奇心,会让你的一生更加多姿多彩。")]),v._v(" "),t("p",[v._v("情绪的调整有很多种方式,常用的包括回避、转移注意力、改变认知、控制表情等等。")]),v._v(" "),t("p",[v._v("回避,可以理解为“逃避可耻但是有用”,我们远离触发情绪的情景、环境,这是小孩子都会做的事情,而有些时候的确也很有效,例如工作环境的转换或调整(换工作)、远离伤心地等。但如果引起我们情绪的因素太多,有时候躲得了初一躲不过十五,这并不算是一个很好的解决办法。")]),v._v(" "),t("p",[v._v("转移注意力,相信大多数人也会这么做。例如工作很多烦人的事情,下班后打盘dota就满血复活。又或者在业余时间健身、学画画、学做饭、学钢琴等等,培养一定的兴趣爱好可以让我们有效地转移注意力,不再沉浸在消极情绪中。")]),v._v(" "),t("p",[v._v("改变认知,也就是理解这些事情,学会接受。这是可以根本解决问题的方式,但实际上大多数人并不能真正释怀,这也很难做到。我们可以阶段性地进行调整,然后配合适当的回避、转移注意力等方法,来控制好自己的情绪。")]),v._v(" "),t("p",[v._v("控制表情。有研究表明,表情和动作在一定程度上也会增强我们的情绪,例如笑容会给人带来积极情绪。而表情的控制有时候也可以让我们达到目的,虽然内心波涛汹涌,但没有表露出来,对职场人来说有时候也是一种生存手段。")]),v._v(" "),t("h2",{attrs:{id:"调整不到怎么办"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#调整不到怎么办"}},[v._v("#")]),v._v(" 调整不到怎么办")]),v._v(" "),t("p",[v._v("是的,即使我们知道很多的技巧和方法来控制情绪,但并不是每次我们都可以控制住。")]),v._v(" "),t("p",[v._v("接受这样的自己吧,接受这个不完美的自己。将对自己的预期调整为偶尔会爆发,这样也可以在情绪失控之后更容易调整恢复,找回自我。我们都不是完人,都有缺点,情绪失控也不是什么大事情(当然前提是别犯罪),承认自己的不足才能更好地改变。")]),v._v(" "),t("p",[v._v("情绪失控后,请记得要收拾现场。我们情绪爆发的时候常常会伤害到身边的人,所以请记得说声抱歉,请喝个奶茶咖啡、吃顿饭等等各种方式都可以,但要表达出自己的歉意。有时候示弱并不会让我们看上去更弱,而可以让我们更真诚,真诚和信任才是人与人之间最重要的东西。")]),v._v(" "),t("h1",{attrs:{id:"不管怎样-保持清醒"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#不管怎样-保持清醒"}},[v._v("#")]),v._v(" 不管怎样,保持清醒")]),v._v(" "),t("hr"),v._v(" "),t("p",[v._v("满心欢喜也好,饥不择食也好,前面"),t("a",{attrs:{href:""}},[v._v("前端这几年--3.关于成长和焦虑")]),v._v("一文中我也有讲到过,在职场中时刻保持清醒很重要。")]),v._v(" "),t("h2",{attrs:{id:"防止情绪的积累"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#防止情绪的积累"}},[v._v("#")]),v._v(" 防止情绪的积累")]),v._v(" "),t("p",[v._v("情绪失控不可怕,情绪的积累其实影响更大。如果情绪得不到排解,它就会一次又一次地冲击,甚至越来越强烈,最终可能导致不可逆的一些结果。")]),v._v(" "),t("p",[v._v("我们都知道,要每时每刻保持情绪稳定是很难的,所以前面也说过可以适当地发泄。如果条件允许,我们可以选择合适的场地、合适的方式来进行发泄,例如有些人喜欢运动,出一身汗就可以调整情绪,有些人爱旅行,出去看看形形色色的事物就可以平复心情,有些人爱打游戏,在游戏里大赢一场就好了。")]),v._v(" "),t("p",[v._v("情绪的积累除了适当的发泄,平日里可以多和人聊聊天、吐吐槽也是可以的。当然,如果对方是同事,这种情况下通常会被组织者认为是负能量的传播。但情绪依然需要一个出口,负能量如果是组织带来的,组织也应当提供一个合适的消耗方式。还是那句话,正是因为有所期待才会有失望,只有正视问题才可以得到解决。")]),v._v(" "),t("p",[v._v("很多人也喜欢用比惨的方式来相互调侃,得到慰藉。除了吐槽,我们还可以多往未来看去。很多时候我们情绪无法调整,多是因为沉浸于其中无法自拔。即使是一些“中了一千万彩票要怎么花”、“今年要去哪旅行”、“辞职以后要做什么”,甚至是“这个周末要吃点什么”、“最近有什么好看的电影、好玩的游戏”这些比较简单的讨论都可以很好地转移注意力。")]),v._v(" "),t("p",[v._v("活了这么久,相信你们也总有那么一两个可以吐露心声的朋友,难过的时候如果无法自己调整,请记得要寻找帮助噢。每个人都有困难的时候,这并不是什么说不出口的事情呢。")]),v._v(" "),t("h2",{attrs:{id:"请时刻保持清醒"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#请时刻保持清醒"}},[v._v("#")]),v._v(" 请时刻保持清醒")]),v._v(" "),t("p",[v._v("你们是不是觉得,既然无法时刻控制好情绪,那怎么可能时刻保持清醒呢?这里的保持清醒,是说即使是很愤怒、很难过的时候,也依然不要让情绪去做决定。")]),v._v(" "),t("p",[v._v("这些年来,越是处于情绪中,越能想清楚要怎么走,心底里会有个声音呼喊“这样下去不行”。即使我情绪爆发了、失控了,但平静下来之后,这些遇到的事情都只会让我更坚定自己的一些决心。事不过三,伤心、失望、难过之后,我会给自己计数,同样的事情不会让它一直发生下去。而至今为止,我也很庆幸自己能在这些时候冷静思考,虽然弯路少不了,但至少没有走歪。")]),v._v(" "),t("p",[v._v("我们经常会做错事情,也做过许多有遗憾的决定,但在一些人生中比较重要的十字路口,我依然希望你们可以看清自己,理性也好、感性也好,清醒地作出这个决定。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("情绪控制我也在努力学习,希望能减少带给身边亲人朋友的负担。但我依然希望自己保留一股热血,敢爱敢恨地和这个世界谈一场恋爱。")])])}),[],!1,null,null,null);_.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/117.3e7ff2cf.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[117],{679:function(v,_,t){"use strict";t.r(_);var a=t(69),e=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("对于渲染引擎来说,如果每次都进行完整内容的计算和绘制,在低端机器或是负责页面的时候可能会出现卡顿。")]),v._v(" "),t("p",[v._v("因此,我们可以考虑设计一套增量渲染的能力,来实现改多少、重绘多少,减少每次渲染的耗时,提升用户的体验。")]),v._v(" "),t("h2",{attrs:{id:"增量渲染设计"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#增量渲染设计"}},[v._v("#")]),v._v(" 增量渲染设计")]),v._v(" "),t("p",[v._v("所谓增量渲染,或许你已经从 React/Vue 等框架中有所耳闻,即更新仅需要更新的部分内容,而不是每次都重新计算和渲染。")]),v._v(" "),t("h3",{attrs:{id:"react-增量渲染"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#react-增量渲染"}},[v._v("#")]),v._v(" React 增量渲染")]),v._v(" "),t("p",[v._v("React 里结合了虚拟 DOM 以及 Fiber 引擎来实现完整的 Diff 计算和渲染调度,这些我之前在其他文章也有说过。在 React 里,状态的更新机制主要由两个步骤组成:")]),v._v(" "),t("ol",[t("li",[v._v("找出变化的组件,每当有更新发生时,协调器会做如下工作:")])]),v._v(" "),t("ul",[t("li",[v._v("调用组件 render 方法将 JSX 转化为虚拟 DOM")]),v._v(" "),t("li",[v._v("进行虚拟 DOM Diff 并找出变化的虚拟 DO")])]),v._v(" "),t("ol",{attrs:{start:"2"}},[t("li",[v._v("通知渲染器。渲染器接到协调器通知,将变化的组件渲染到页面上。")])]),v._v(" "),t("p",[v._v("我们的渲染引擎道理也是十分相似的,即找出最小变化范围进行计算和更新。同样的,我们还是继续以在线表格为例子,基于我们现有的引擎设计上实现增量渲染。")]),v._v(" "),t("h3",{attrs:{id:"收集增量"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#收集增量"}},[v._v("#")]),v._v(" 收集增量")]),v._v(" "),t("p",[v._v("关于渲染引擎的收集和渲染过程,已经在前面"),t("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-render-and-collect.html"}},[v._v("《1.收集与渲染》")]),v._v("文章中介绍过。")],1),v._v(" "),t("p",[v._v("基于该架构设计,我们知道一次渲染分成两个过程:")]),v._v(" "),t("ol",[t("li",[v._v("收集渲染数据。")]),v._v(" "),t("li",[v._v("绘制收集后的渲染数据。")])]),v._v(" "),t("p",[v._v("而前面在"),t("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-plugin-design.html"}},[v._v("《2.插件的实现》")]),v._v("中也提到,渲染引擎整体的架构如图:")],1),v._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/render-engine-plugin-design-1.jpg",alt:""}})]),v._v(" "),t("p",[v._v("在该架构图中,渲染引擎支持提供绘制特定范围的能力。而要实现这样的能力,我们需要做到:")]),v._v(" "),t("ol",[t("li",[v._v("支持特定范围的渲染数据收集。")]),v._v(" "),t("li",[v._v("支持特定范围的 Canvas 画布重绘。")])]),v._v(" "),t("p",[v._v("由于在线表格这样的产品都是以单元格为基础,因此我们的收集器和渲染器都同样可以以单元格为最小单位,提供以下的能力:")]),v._v(" "),t("ol",[t("li",[v._v("根据格子位置更新、清理、新增收集的渲染数据。")]),v._v(" "),t("li",[v._v("根据格子位置进行画布的擦除和重新绘制。")])]),v._v(" "),t("p",[v._v("实际上,一次渲染的耗时大头更多会出现在收集过程,因为收集过程中常常会进行较复杂的计算,亦或是针对一个个格子的数据收集会导致不停地遍历各个格子范围和访问特定对象获取数据。")]),v._v(" "),t("p",[v._v("所以更重要的增量能力在于收集过程的增量。")]),v._v(" "),t("h2",{attrs:{id:"在线表格增量渲染"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#在线表格增量渲染"}},[v._v("#")]),v._v(" 在线表格增量渲染")]),v._v(" "),t("p",[v._v("对于在线表格的场景,我们可以考虑两种增量渲染的情况:")]),v._v(" "),t("ol",[t("li",[v._v("局部修改,比如用户修改了某个范围的格子内容和样式。")]),v._v(" "),t("li",[v._v("页面滚动,用户滚动过程中,有部分单元格范围不变。")])]),v._v(" "),t("p",[v._v("我们分别来看看。")]),v._v(" "),t("h3",{attrs:{id:"局部修改"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#局部修改"}},[v._v("#")]),v._v(" 局部修改")]),v._v(" "),t("p",[v._v("局部修改比较简单,前面我们已经提到说收集过程和渲染过程都支持按指定范围进行增量渲染,因此局部修改的时候直接走特定范围的绘制即可。比如用户修改了 A1 这个格子:")]),v._v(" "),t("ol",[t("li",[v._v("清除 A1 单元格的收集数据。")]),v._v(" "),t("li",[v._v("重新收集 A1 单元格的收集数据。")]),v._v(" "),t("li",[v._v("重新渲染 A1 单元格的内容。")])]),v._v(" "),t("h3",{attrs:{id:"页面滚动"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#页面滚动"}},[v._v("#")]),v._v(" 页面滚动")]),v._v(" "),t("p",[v._v("页面滚动与纯某个特定范围的修改不大一样,因为页面滚动过程中,所有单元格的位置都会发生改变。")]),v._v(" "),t("p",[v._v("一般来说,在滚动过程我们会产生局部可复用的单元格绘制结果,如图:")]),v._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/render-engine-diff-render-1.jpg",alt:""}})]),v._v(" "),t("p",[v._v("对于这样的情况,我们可以有两种解决方案:")]),v._v(" "),t("ol",[t("li",[v._v("复用局部 Canvas 绘制结果。")]),v._v(" "),t("li",[v._v("复用局部收集的渲染数据结构。")])]),v._v(" "),t("p",[v._v("方案 1 直接复用局部 Canvas 的方案比较简单,不少在线表格像谷歌表格、飞书文档等都是用的该方案,该方案同样存在一些问题:")]),v._v(" "),t("ul",[t("li",[v._v("由于 Canvas 绘制在非整数像素下会存在不准确的问题,因此在有缩放比例下增量渲染会出现多余的横线、白线等问题")]),v._v(" "),t("li",[v._v("由于该过程会将原有 Canvas 的内容先转成图片,再往新的内容区域贴进去,会导致 Canvas 透明度丢失,无法支持 Canvas 的透明设置")])]),v._v(" "),t("p",[v._v("方案 2 复用局部收集的渲染数据结构,可以优化上述问题,但整体的性能会比复用 Canvas 稍微差一些,毕竟复用 Canvas 直接节省了复用范围的收集和渲染耗时,而复用收集结果则仅节省了复用范围的收集,绘制过程还是会全量绘制。")]),v._v(" "),t("p",[v._v("对于复用收集结果的方案,还需要考虑页面出现滚动导致的绘制位置差异,即使是同一个单元格,其在画布上的位置也发生了改变,这样的变化需要考虑进去。")]),v._v(" "),t("p",[v._v("因此,收集器的数据结构需要和单元格紧密相关,而不是基于 Canvas 的整体偏移。如何设计出性能较好又易于理解的数据结构,这也是一项不小的挑战,决定了我们增量渲染的优化效果能到哪里。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("由于渲染引擎和用户视觉、交互紧密相关,因此常常是性能优化的大头。结合产品特点和架构设计做具体的分析和优化,这才是我们在实际工作中常常面临的挑战。")]),v._v(" "),t("p",[v._v("前面介绍过的分片优化也好,这里的增量渲染也好,其实大多数都能在业界找到类似的思路来做参考。不要把思路局限在相同产品、相同场景下的解决方案,即使是看似毫不相干的优化场景,你也能拓展思维看看对自己遇到的难题是否能有所启发。")])])}),[],!1,null,null,null);_.default=e.exports}}]);
--------------------------------------------------------------------------------
/assets/js/189.a1f98229.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[189],{751:function(t,v,_){"use strict";_.r(v);var a=_(69),s=Object(a.a)({},(function(){var t=this,v=t.$createElement,_=t._self._c||v;return _("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[_("p",[t._v("效率二字在工作中已经是老生常谈了,但反观我们的日常工作里,其实依然有很多可以改进的地方。效率提升了,我们可以把时间花在自己想花的地方了。")]),t._v(" "),_("h1",{attrs:{id:"自我管理"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#自我管理"}},[t._v("#")]),t._v(" 自我管理")]),t._v(" "),_("p",[t._v("效率提升的大部分都是关于自我管理的,这里分享几个对我自己来说比较实用的技巧。")]),t._v(" "),_("h2",{attrs:{id:"时间管理"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#时间管理"}},[t._v("#")]),t._v(" 时间管理")]),t._v(" "),_("p",[t._v("相信每个人都有自己效率较高的工作时间,这里我们可以将自己每天的工作时间分成几个部分,包括:零碎的时间段(30 分钟内)、连续较短的时间段(1-2 个小时)、连续较长的时间段(2 个小时以上)。")]),t._v(" "),_("p",[t._v("我们用空瓶子填石头的方法,先把长的时间段划出来,用来专注写代码实现功能。接下来是较短的连续时间段,可以用来做代码测试、问题定位和修复等事情。最后是零碎的时间段,可以用来规划工作内容、与协作方(产品、设计、其他开发)沟通、总结复盘等内容。")]),t._v(" "),_("p",[t._v("来举个栗子说明一下。")]),t._v(" "),_("p",[t._v("例如我们程序员一般每天 9 点多才到公司,上午的工作时间大概 2 个小时左右。可用于准备今日的 Todo List,梳理今日工作内容并进行分块(根据工作量大小划分),梳理需要的资源并推动相应的依赖方,这个过程大概 0.5-1 小时。上午还剩下 1 个多小时,可以挑某个预估时间差不多的活来干。")]),t._v(" "),_("p",[t._v("然后就是开心的下午时间了,一般下午有 4-5 小时的时间。除去可能进行的会议,可以进行较大块的工作内容。可以以小时为单位来划分,每完成一个任务就稍作休息,上个厕所、接杯水喝等。这个过程可能会被各种人打断,来问某个功能是否能实现的产品、来咨询某些问题怎么处理的甲方,不过我们的任务是以小时为单位划分的话,影响也不会很大。")]),t._v(" "),_("p",[t._v("然后是晚上。一般晚上也有 2、3 个小时以上的时间可以干活,而且这个时候开会比较少、被人打扰的情况也较少,所以很多程序员的最高效开发时间在晚上。同样的可以按照以小时为单位的任务来进行,晚上还有比较重要的一件事,就是根据早上梳理的工作内容,回顾今天的事情是否顺利完成,如果有一些心得体会,可以简单地记录下来,等空闲或者周末的时候再集中整理。")]),t._v(" "),_("p",[t._v("大家可以根据自己的个人情况来进行调整,例如我个人习惯是 10 点以后尽量不写代码,因为会越写越精神晚上睡不着,当然也有很多人喜欢 10 点以后开始写代码。")]),t._v(" "),_("h2",{attrs:{id:"todo-list"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#todo-list"}},[t._v("#")]),t._v(" Todo List")]),t._v(" "),_("p",[t._v("Todo List 的梳理其实是高效工作里最重要的一个步骤。前面我们说到通过时间管理的方式来提升工作效率,而我们可以将时间划分为时间段的前提,则是梳理好我们的 Todo List。")]),t._v(" "),_("h3",{attrs:{id:"养成备忘的习惯"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#养成备忘的习惯"}},[t._v("#")]),t._v(" 养成备忘的习惯")]),t._v(" "),_("p",[t._v("要怎么写好一个 Todo List 呢?其实关键是养成备忘的习惯。")]),t._v(" "),_("p",[t._v("我们工作中除了代码开发以外,涉及许多其他的零碎的事情,例如开会、问题响应、临时问题定位、内容整理和输出等等。当事情很多、又突然被打断或者打乱的时候,我们常常会抓耳挠腮。那么是什么导致了事情太多太乱呢?是因为我们记不住。")]),t._v(" "),_("p",[t._v("所以我们可以在每次突然接到一个新的任务的时候,或者突然想起某个事情要做的时候,就可以先简单地在一个习惯的地方记录下来。尽量在一个统一的地方记录,免得想不起来记在哪里或是遗漏了,就达不到我们的目的了。可以是手边备一个笔记本、手机备忘录、微信文件传输助手等等。")]),t._v(" "),_("p",[t._v("记录的时候可以根据完成或者进行的日期来维护,例如“今天”、“本周”、“周末”、“XX月”,这样我们每次有新的内容补充,都可以继续填在原有的内容后面,而查阅起来也比较方便。")]),t._v(" "),_("h3",{attrs:{id:"优先级排序"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#优先级排序"}},[t._v("#")]),t._v(" 优先级排序")]),t._v(" "),_("p",[t._v("除了维护这样一个 Tode List 备忘列表,我们还需要对这个列表进行优先级排序。")]),t._v(" "),_("p",[t._v("我们待完成的任务中,都会有重要程度、紧急程度的区分。每次更新备忘的时候,都可以重新确认一下优先级。这样,当我们拿到这么一个 Todo List 的时候,就可以直接按照上面的顺序来先后完成,不用再因为“时间不够、事情太多”而烦躁了。")]),t._v(" "),_("p",[t._v("除此之外,我们还需要对任务的耗时做一个预估,因为根据任务所需要的时间,我们很可能需要匹配自身拥有的时间来调整优先级顺序。")]),t._v(" "),_("p",[t._v("例如,我这周末打算写一篇文章、看某一本书的两章内容,还需要做饭、打扫卫生,剩下的时间去动物之森集合。那么我需要评估下每个任务的时间,然后根据上下午、晚上的时间段来对应划分,最终根据合适的方式来排序,得到我的周末 Todo List。")]),t._v(" "),_("p",[t._v("我只需要在特定的时间段完成这个任务,多出来的时间就可以去玩游戏啦!")]),t._v(" "),_("h1",{attrs:{id:"其他"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#其他"}},[t._v("#")]),t._v(" 其他")]),t._v(" "),_("p",[t._v("除了与自身时间管理、任务管理相关的事情以外,我们工作中效率不高的原因常常还因为低效的沟通、重复性的工作等,这些也是需要提升效率的地方。")]),t._v(" "),_("h2",{attrs:{id:"协作与沟通"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#协作与沟通"}},[t._v("#")]),t._v(" 协作与沟通")]),t._v(" "),_("p",[t._v("首先,我们来看看常常“浪费时间”的协作和沟通问题。如果你仔细观察,你会发现我们的工作中会经常出现这样的情况,两个人争了半天才发现讲的不是一个事情,或者关注的重点不一致。那么,我们要怎么避免这种情况呢?")]),t._v(" "),_("p",[t._v("首先是换位思考。我们需要知道对方在想什么,怎么知道呢?问。在对方说了一堆之后,你可以尝试通过上下文找到他的疑惑点,然后用反问的方式梳理一下,“你想知道的是这个吗”、“我这样理解对吗”,这样对方会停止急于表达自己的行为,从而来尝试理解你说的是否正确。通过这样一个小技巧,你们双方都可以进行换位思考,只要问题达成一致,剩余的讨论就不会偏离方向导致浪费时间了。")]),t._v(" "),_("p",[t._v("不要陷入情绪。我们在讨论问题的时候容易带入情绪,方案被否决会觉得自己被否定,从而觉得丢脸导致不能理性分析。同样的,我们在提出对方的问题的时候,也需要关注对方的情绪,尽量对事不对人。如果对方是个敏感骄傲的人,可以尝试私下进行二次对话,避免公众场合的争执。")]),t._v(" "),_("p",[t._v("然后最重要的一点是准确地表达。即使工作很多年了,你依然会发现很多人甚至不能好好地描述问题。如果连问题都讲不清楚,对方又如何能提供帮助呢?表达的时候可以先写下来,自己尝试阅读几遍或者念出来,看看能否很好地理解、是否完整地说明了问题,然后才给到对方这个信息。")]),t._v(" "),_("h2",{attrs:{id:"重复性工作"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#重复性工作"}},[t._v("#")]),t._v(" 重复性工作")]),t._v(" "),_("p",[t._v("重复性的工作,常常让我们兴趣黯然又越做越烦。")]),t._v(" "),_("p",[t._v("很多人会抱怨,为什么要给我安排这种没有技术含量的活。而这种没有技术含量、重复性的工作为什么占用你这么多时间,有仔细分析过吗?瓶颈在哪呢?")]),t._v(" "),_("p",[t._v("我们可以对手上这些“总是反复出现”、“低级”的工作进行分析,是做了某个工具、对方使用的时候总是问很基础的问题?那么这样的问题是否可以沉淀到文档中,给到详细的指引说明、参考链接呢?又或者是因为手上的工作总是要复制粘贴,那么身为程序员的我们是否可以写个脚本或者工具去自动化完成或者局部完成呢?")]),t._v(" "),_("p",[t._v("如何对待重复性、没有技术含量的工作,才是最能体现我们解决问题能力的地方。不要急躁,好好思考和分析,一步一步去解决吧。")]),t._v(" "),_("h2",{attrs:{id:"结束语"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[t._v("#")]),t._v(" 结束语")]),t._v(" "),_("p",[t._v("不要让自己埋没在工作中喘不过气,解决掉低效的工作内容,解放自己,你可以走得更远,也更开心。")])])}),[],!1,null,null,null);v.default=s.exports}}]);
--------------------------------------------------------------------------------
/assets/js/118.36c96445.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[118],{682:function(v,_,t){"use strict";t.r(_);var e=t(69),r=Object(e.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("前面我们在"),t("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-diff-render.html"}},[v._v("《6.增量计算》")]),v._v("一文中介绍了滚动过程中的增量渲染方案,通过减少渲染计算量或绘制量的方式,来提升页面滚动的流畅度。")],1),v._v(" "),t("p",[v._v("除此之外,当滚动距离较远时,增量渲染并不能达到预期的优化效果,此时我看还需要考虑降级渲染。")]),v._v(" "),t("h2",{attrs:{id:"页面内的元素优先级划分"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#页面内的元素优先级划分"}},[v._v("#")]),v._v(" 页面内的元素优先级划分")]),v._v(" "),t("p",[v._v("当用户打开一个大的页面时,除了使用搜索获取关键信息,还可能会快速滚动来翻阅整体内容,然后找到关注的信息进行详细查阅。增量渲染的方案,核心是将上一帧的内容尽可能复用到下一帧里,因此在快速滚动的场景下(比如拖动滚动条滚动),页面中可能并没有多少可复用的内容。")]),v._v(" "),t("p",[v._v("在这样的场景下,如果需要渲染的内容实在很多,我们可以对页面内容进行优先级划分。")]),v._v(" "),t("p",[v._v("该如何进行优先级划分呢?这个可能需要结合业务的具体情况进行分析,比如:")]),v._v(" "),t("ul",[t("li",[v._v("分段分组的内容:标题、副标题等")]),v._v(" "),t("li",[v._v("方便用户定位位置的内容:文本框、图片、背景色等")])]),v._v(" "),t("p",[v._v("像图片这种渲染可能会比较耗时,那么可以用占位符等方式来进行骨架渲染,让用户能快速定位到对应位置之后,再进行详细内容的渲染。")]),v._v(" "),t("p",[v._v("具体到在线表格的场景下,首先行列位置十分重要,同时方便用户定位位置的还有单元格背景色、边框线、图片等等内容。我们可以这样拆分优先级:")]),v._v(" "),t("ul",[t("li",[v._v("行列头、行列序号、选区")]),v._v(" "),t("li",[v._v("单元格背景色、边框线")]),v._v(" "),t("li",[v._v("图片(占位符)")]),v._v(" "),t("li",[v._v("文本内容")]),v._v(" "),t("li",[v._v("其他格式内容/图标(格式错误角标、下拉按钮、icon 内容等)")]),v._v(" "),t("li",[v._v("真实图片信息")]),v._v(" "),t("li",[v._v("其他")])]),v._v(" "),t("p",[v._v("拆分出优先级后,我们可以进行降级的渲染,在流畅度不高的情况下,优先渲染高优先级的内容,保证用户的滚动流畅体验。")]),v._v(" "),t("h2",{attrs:{id:"降级渲染"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#降级渲染"}},[v._v("#")]),v._v(" 降级渲染")]),v._v(" "),t("p",[v._v("通过优先级的划分,我们可以在渲染过程中,保证滚动操作的流畅度。具体方式为:根据页面帧率和用户滚动的距离,来进行降级的渲染。")]),v._v(" "),t("h3",{attrs:{id:"页面帧率"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#页面帧率"}},[v._v("#")]),v._v(" 页面帧率")]),v._v(" "),t("p",[v._v("理想情况下,检测到页面帧率开始下降的情况下,则考虑进入降级渲染的场景。")]),v._v(" "),t("p",[v._v("页面帧率可以使用"),t("code",[v._v("requestAnimationFrame")]),v._v("来进行计算,当然前提还需要是页面进行了滚动操作,否则的话只是单纯当前页面绘制慢则进行降级,用户会感觉页面内容突然减少,体验较差。")]),v._v(" "),t("p",[v._v("我们可以在页面开始滚动时,监听 rAF 变化来计算 FPS,当 FPS 明显下降到不流畅的时候,则进入降级渲染,并根据帧率来调整降级渲染的级数。比如(简单举例):")]),v._v(" "),t("ul",[t("li",[t("code",[v._v("0 < FPS < 10")]),v._v(": 最高级别的降级渲染,只渲染边框线和单元格背景色")]),v._v(" "),t("li",[t("code",[v._v("10 < FPS < 20")]),v._v(": 中级别的降级渲染,除了边框线和单元格背景色以外,还渲染单元格富文本内容")]),v._v(" "),t("li",[t("code",[v._v("20 < FPS < 30")]),v._v(": 低级别的降级渲染,除了边框线和单元格背景色、单元格以外,还渲染图片")]),v._v(" "),t("li",[t("code",[v._v("30 < FPS < 40")]),v._v(": 最低级别的降级渲染,渲染仅附加内容(如角标、协作者光标、icon 等)以外的内容")])]),v._v(" "),t("h3",{attrs:{id:"滚动距离"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#滚动距离"}},[v._v("#")]),v._v(" 滚动距离")]),v._v(" "),t("p",[v._v("在很多情况下,其实我们并不能很好地使用"),t("code",[v._v("requestAnimationFrame")]),v._v("来计算 FPS 帧率,因为 FPS 的计算需要一个累计过程,才能得到平均 1s 内的平均 FPS,同时频繁使用 rAF 本身也可能会影响到页面渲染性能。")]),v._v(" "),t("p",[v._v("所以,我们可以从别的角度来控制降级渲染的情况,比如使用用户页面滚动的快慢来控制优先级。")]),v._v(" "),t("p",[v._v("我们依然可以使用渲染本身,在两次渲染之间获取用户的滚动距离,根据滚动距离判断滚动速度,并以此来调整降级渲染的策略。比如,当页面进入滚动状态后,两次绘制之间的滚动距离:")]),v._v(" "),t("ul",[t("li",[v._v("超过 20 屏内容:最高级别的降级渲染")]),v._v(" "),t("li",[v._v("10 屏 < 滚动距离 < 20 屏:中级别的降级渲染")]),v._v(" "),t("li",[v._v("5 屏 < 滚动距离 < 10 屏:低级别的降级渲染")]),v._v(" "),t("li",[v._v("2 屏 < 滚动距离 < 5 屏:最低级别的降级渲染")])]),v._v(" "),t("p",[v._v("当然,这里的渲染,除了 rAF 本身之外,还可以是 API 的调用如渲染层的 render 接口,或者是 canvas 的绘制。")]),v._v(" "),t("h3",{attrs:{id:"渲染插件的降级"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#渲染插件的降级"}},[v._v("#")]),v._v(" 渲染插件的降级")]),v._v(" "),t("p",[v._v("前面在"),t("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-plugin-design.html"}},[v._v("《2.插件的实现》")]),v._v("一文中,我们介绍了除核心渲染内容以外,其他附加格式内容的渲染方式:使用渲染插件。")],1),v._v(" "),t("p",[v._v("由于插件设计的存在,我们通过很简单的方式,就能实现降级渲染的能力。因为一些附加格式的渲染,都是使用渲染插件实现的,比如 icon 绘制、下拉菜单、角标、图片等等,我们可以十分轻易地将渲染插件进行降级优先级的归档。")]),v._v(" "),t("p",[v._v("当我们判断需要进行降级渲染时,可以直接通过优先级策略,来控制是否跳过某些插件的收集过程,便可以直接达到降级渲染的效果。")]),v._v(" "),t("h2",{attrs:{id:"降级渲染的启动和停止"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#降级渲染的启动和停止"}},[v._v("#")]),v._v(" 降级渲染的启动和停止")]),v._v(" "),t("p",[v._v("前面提到,进入降级渲染的前提条件是用户进行滚动,在滚动过程中通过不同的方式判断用户滚动“是否流畅”,如果检测到滚动不流畅的话,则根据卡顿程度进入到不同级别的降级渲染。")]),v._v(" "),t("p",[v._v("降级渲染,说白了就是减少页面渲染的内容,从而减轻每次渲染的耗时,提升用户的使用流畅度。")]),v._v(" "),t("p",[v._v("但当用户停止滚动、或是滚动较慢时,这时候可以认为用户需要聚焦阅读页面中的信息,因此这种时候我们还需要退出降级渲染模式,将页面内容进行完整渲染。")]),v._v(" "),t("p",[v._v("通过这样的方式,我们保证了用户滚动流畅度的同时,还确保了页面内容不会丢失。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("本文介绍了当页面内容过多,用户在滚动时因为绘制较慢导致不流畅时,使用降级渲染的方式来保证用户的流畅度。")]),v._v(" "),t("p",[v._v("当然,其实说到底这个策略也会损耗了一些用户体验,但如果有更好的优化方式,我们也不需要使用到降级策略。很多时候,技术的决策便是在各种不同优劣的技术方案中,选出一种性价比更好、投入产出比更好的方案而已。")])])}),[],!1,null,null,null);_.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/188.9fe1f059.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[188],{748:function(v,_,t){"use strict";t.r(_);var a=t(69),s=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("在日常工作里,我们常常会遇到一些不如预期的事情。我们在做的常常又不全是自己想要做的事情,要怎么去理解和面对这样的矛盾呢?")]),v._v(" "),t("h1",{attrs:{id:"这不是我想做的"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#这不是我想做的"}},[v._v("#")]),v._v(" 这不是我想做的")]),v._v(" "),t("p",[v._v("工作对于大多数的人来说,主要在于养家糊口。随着越来越多年轻人涌入程序员这个行业,大家的危机感慢慢浮现,想要保持竞争力、提升自我等念头让我们想得更多,也常常出现一些选择和矛盾的情况。")]),v._v(" "),t("h2",{attrs:{id:"业务需求-vs-技术需求"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#业务需求-vs-技术需求"}},[v._v("#")]),v._v(" 业务需求 vs 技术需求")]),v._v(" "),t("p",[v._v("做业务需求好,还是做技术需求好,大概是所有程序员都想过的事情,也是很多人会纠结的点。")]),v._v(" "),t("p",[v._v("一般来说,大家都会认为技术需求对个人的成长和提升帮助更大,同时技术需求也很少有业务面临的突发问题、外网投诉、活动发版等可能熬夜通宵的情况。而业务需求则有较可观的用户量,收益和稳定性可能会更好,但工作内容可能更偏向日常问题定位、产品需求的开发等“技术含量较低”的枯燥和重复性工作。")]),v._v(" "),t("p",[v._v("每天埋头写“差不多”代码的,羡慕做有挑战性的技术的。每天在做底层基础支撑的,苦恼于没有业务接入、团队面临调整等问题。")]),v._v(" "),t("p",[v._v("实际上业务需求也好,技术需求也好,我们都是在为某一个产品提供稳定、可靠的服务。对于业务来说,这个产品可能由产品经理提出需求而自己负责实现;而对于技术来说,这个产品则是由业务衍生出来的基础需求,需求的服务对象则是日常进行业务开发的人。")]),v._v(" "),t("p",[v._v("所有的技术需求都来源于业务的需要,自动化能力、灰度发布能力、监控告警能力、全链路跟踪能力等等,都是因为业务某个方面到达一定的瓶颈而提出的解决方案。")]),v._v(" "),t("p",[v._v("所以,对于做业务需求的小伙伴来说,需要关注到是什么导致了我们日常工作的枯燥,“重复性”的工作是否可以用工具解决?“相类似”的工作是否可以进行抽象然后提出解决方案?对于做业务的小伙伴来说,如果能积极思考和主动提出解决方案,也一样能获得很多的成长和机会。")]),v._v(" "),t("p",[v._v("对于做技术需求的小伙伴来说,需要关注我们正在做的事情,是否真的符合业务的需要?是可以让业务更加方便地使用,还是会给它们带来更多的麻烦?只有能贴切地解决业务的一些痛点,这样的技术方案才可能有更多的业务愿意接入,技术需求的意义才得以体现。")]),v._v(" "),t("h2",{attrs:{id:"tob-vs-toc"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#tob-vs-toc"}},[v._v("#")]),v._v(" ToB vs ToC")]),v._v(" "),t("p",[v._v("对于前端同学来说,我们也常常会对 ToB 和 ToC 怎么选有过烦恼,其实区别更多在于用户群体和数量。")]),v._v(" "),t("p",[v._v("一般来说,ToB 的业务服务于某一类用户群体,因此会根据服务对象的不一样而工作重点有所区别。例如,如果服务于银行,则对技术方案要求严格,如果服务于政府机构,则可能需要兼容较低版本的 IE 浏览器,技术选型比较局限。但通常来说,ToB 业务的用户量并不会特别大,对性能要求较低,有些情况下也会由于机器部署环境封闭的原因,对网络和安全性要求较低,因此 ToB 业务可以更多关注开发效率提升、技术管理选型、项目可维护性等方面。")]),v._v(" "),t("p",[v._v("ToC 的业务用户量较大,对加载性能、浏览器兼容性等都要求很高,因此常常需要进行性能优化、兼容性检测、实时监控、SEO 优化等工作。")]),v._v(" "),t("p",[v._v("按理来说,在找工作的时候,ToC 业务的会比 ToB 业务的人优势要大一点,因为 ToC 对前端的各个角度要求都相对较高。但其实真正工作中,由于精力和工作内容分配的问题,很多参与 ToC 业务的人更多只关注自己负责的一小部分,因此其实并没有掌握到 ToC 业务的关键技术方案。而即使是在做 ToB 业务,也有不少小伙伴会有很多的时间去研究一些新技术、做很多的选型调研,也可以在这个过程中获得很好的成长。")]),v._v(" "),t("p",[v._v("所以,决定我们能否掌握更多的、成长更快的,最终还是一句话,要靠自己。")]),v._v(" "),t("h2",{attrs:{id:"全栈"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#全栈"}},[v._v("#")]),v._v(" 全栈?")]),v._v(" "),t("p",[v._v("如今随着 Node.js 的普及,也有不少的前端开发慢慢转型做全栈、大前端等方向。")]),v._v(" "),t("p",[v._v("的确,对于有全栈工作经验的人来说,找工作的时候会更吃香。但我们日常工作中是否都有机会去接触后台开发、客户端开发这些内容呢?我们是否一定需要有这样的工作经验才能获得更好的发展呢?")]),v._v(" "),t("p",[v._v("很多时候,前端由于入门简单的原因,很多的前端开发(包括我)都不是计算机专业出身。我们对于计算机基础、网络基础、算法和数据结构等内容掌握很少,更多时候是这些知识的缺乏阻碍了我们在程序员这一职业的发展,这也是为什么很多前端开发苦恼自己到达天花板,想着转型全栈或者后台就能走得更远。")]),v._v(" "),t("p",[v._v("这其实是个误区。后台开发由于开发语言、服务器管理、存储管理等工作内容的不一致,对于专业基础的要求更高,因此看上去似乎比前端能走得更远。但随着成熟的解决方案的出现,像分布式部署和管理、全链路跟踪等,以及运维和 DBA 等职位的出现、后台基本框架的完善,更多的后台开发技术选型的范围不大,在开发过程中也是偏向业务的开发,因此更多的关注点会落在业务风险梳理、问题定位和追踪、业务稳定性、效率提升等地方。而全栈中的后台开发,可能涉及的内容会更加局限一些。")]),v._v(" "),t("p",[v._v("所以,其实我们在日常工作中也可以更多地关注后台的实现和能力,除了可以更好地配合和理解后台的工作外,还可以提升自己对后台工作内容的理解。当然,最重要的其实依然是,我们需要扎实地补充计算机基础知识。")]),v._v(" "),t("p",[v._v("全栈开发经验可能让我们更容易地找到工作,但只有基础知识的掌握足够深入,才可以在接触后台开发、终端开发等内容的时候,有足够的能力去快速高效地解决问题。")]),v._v(" "),t("h1",{attrs:{id:"这不在我的工作范围内"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#这不在我的工作范围内"}},[v._v("#")]),v._v(" 这不在我的工作范围内")]),v._v(" "),t("p",[v._v("除了日常开发的内容,我们工作中也有不少其他各式各样的事情需要去做。有些人会想,我来这里并不是为了做这种事情,那么这种情况下要怎么处理呢?")]),v._v(" "),t("h2",{attrs:{id:"边缘工作该做吗"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#边缘工作该做吗"}},[v._v("#")]),v._v(" 边缘工作该做吗?")]),v._v(" "),t("p",[v._v("很多时候,我们所在的团队都会有很多边界不清晰、责任不明确的工作,例如会议纪要、值班查问题、组织团建等内容。一般来说组长调节好轮流负责是最好的,但事实上也有不少的团队会把这样的工作一直给到某个人,那么这样的情况要怎么处理呢?")]),v._v(" "),t("p",[v._v("大家都知道,这些工作会占用一些时间,而且经常需要做一些协调性的工作。如果你是一个希望专注技术成长的人,那想必会很烦恼。如果这种情况真的发生了,首先可以提出轮班的建议,如果老大觉得就是你做得最好一定要你做的话,可以尝试提升这部分工作的效率,同时把方案和步骤都写下来,再尝试让大家都参与进来。")]),v._v(" "),t("p",[v._v("那么如果其他人都真的“做不到”,你每天都得花上额外的时间来做的话,可以思考下自己能否承受得住这样的安排。这里的承受并不是指工作量太大,而是指个人对待这些事情的态度,毕竟如果给工作带来了情绪,才是最糟糕的结果。当然我们可以开放地接受最好,毕竟大多数人也只是来打份工的。")]),v._v(" "),t("p",[v._v("好的管理者会对一直承担边缘工作的小伙伴进行奖励,但也并不是全部都是这样的。实在不行的话,可以考虑再次反馈,最糟糕的情况下就得换个工作了。")]),v._v(" "),t("h2",{attrs:{id:"我该花时间在写-ppt-和文档上面吗"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#我该花时间在写-ppt-和文档上面吗"}},[v._v("#")]),v._v(" 我该花时间在写 PPT 和文档上面吗")]),v._v(" "),t("p",[v._v("这大概是所有程序员都脑壳疼的问题了,但是它确实一个比其他所谓边缘工作都要现实的问题。")]),v._v(" "),t("p",[v._v("那么,我需要学好怎么写 PPT 吗?答案是要的。我们的 PPT 并不需要画的跟设计童鞋一样漂亮,大白字、表情包都可以往里面贴,重点只有一个:逻辑思路清晰。其实写 PPT、写文章和文档这些很难吗?不难,只是比较花时间。但是在写的过程其实你会进行很多的思考,会发现一些之前并没有考虑到的事情,同时也能锻炼你的书面表达能力。")]),v._v(" "),t("p",[v._v("所以,你依然需要花适当的时间去对你的项目进行设计、整理和复盘,用你擅长的形式,不管是 PPT 也好,文档、文章也好,将这些内容思路清晰地记录下来,才可以走得更远。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("工作里总有很多让人不舒服的事情,不过生活也是这样,大多数时候我们都无法改变环境,只能调整自己。让自己开心才是正经事!")])])}),[],!1,null,null,null);_.default=s.exports}}]);
--------------------------------------------------------------------------------
/assets/js/184.3047e022.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[184],{746:function(v,_,t){"use strict";t.r(_);var a=t(69),s=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("996、福报等话题也是越来越成为程序员的日常,而除了肚腩、秃头等影响外观的健康因素,关于猝死、患病等话题也渐渐成为互联网世界的话题之一。我们到底是从什么时候开始,才越来越轻视一些身体发出的求救信号呢?")]),v._v(" "),t("h1",{attrs:{id:"最重要的是那个1"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#最重要的是那个1"}},[v._v("#")]),v._v(" 最重要的是那个1")]),v._v(" "),t("p",[v._v("以前经常听长辈叨叨,我们的身体是 1,所有的财富、荣耀、名声都是后面的 0,只有 1 在的时候,这些 0 才会有意义呀。")]),v._v(" "),t("p",[v._v("有意思的是,对于程序员来说,1 和 0 可以组成任意的内容、代表着整个世界。但如果说生命是 1 而其他事物是 0 的话,很多时候我们在做选择的时候总会忽视了已有的 1。")]),v._v(" "),t("h2",{attrs:{id:"上进心很重要"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#上进心很重要"}},[v._v("#")]),v._v(" 上进心很重要")]),v._v(" "),t("p",[v._v("想要成功在任何时候都不会有错。的确,我们有特别多想要得到的东西,可能是权力,可能是金钱,可能是职位。越是年轻,我们拥有的越少,想要的越多。所以刚入职场的时候,我们积极、拼搏、热情,因为我们迫切地希望获得更多的成长和其他的收获。")]),v._v(" "),t("p",[v._v("如今互联网就业压力大,996、修福报等已经是常见的操作了。大多数打工仔没法选择,因为显然“你不做会有无数人愿意做”。于是,大家心怀鬼胎,下班了依然呆在工位不回家,只是因为“大家都还没走,我不能成为第一个”。")]),v._v(" "),t("p",[v._v("曾经我也有段时间每天下班比较早,那段时间刚好身体不大舒服比较困,早点回家休息。结果没过几天就被组长喊去谈话,“你不能那么早走,领导们都还在,大家都看着呢”。当然,我也早就离开了那样扯淡的小组了。")]),v._v(" "),t("p",[v._v("是的,我们想要成长就必须要付出。我个人也同样认为上进心很重要,但过于强求反而容易反噬。凡事有个度,有一条线是迈过去再也回不来的。")]),v._v(" "),t("h2",{attrs:{id:"但什么都比不上你的健康"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#但什么都比不上你的健康"}},[v._v("#")]),v._v(" 但什么都比不上你的健康")]),v._v(" "),t("p",[v._v("“我们都还年轻,还可以拼一下,老了就拼不动了”。其实老了拼不动,正是因为我们年轻的时候没有节制地消耗自己的身体。")]),v._v(" "),t("p",[v._v("我们的生活中充斥着老一辈人的叨叨,身体最重要呀,老师和公务员比较舒服呀,这条路很难走呀不要去呀。大多数的我们也依然会选择尝试一下,不试一下怎么知道不行呢?")]),v._v(" "),t("p",[v._v("刚从华为出来,自学前端转行的时候,每天上班干活、下班继续学习补充基础到夜里。挤在一个三室26个女生、只有一个洗手间的房子里,每天下班后打着台灯窝在上铺里看书学习。")]),v._v(" "),t("p",[v._v("直到有段时间,开始每天都觉得肚子不大舒服,还带点低烧、发冷。那会刚换的工作,不好请假看医生。差不多熬了快一个月,直到有一天差点晕了过去,然后打了个车去医院,检查之后就收到了入院通知。")]),v._v(" "),t("p",[v._v("那段时间,由于自己的一些坚持和家人闹翻了,有几乎好几个月没有联系了。医院的病床也已经满了,于是在过道的地方临时加了个床位。打电话跟领导请假,换来了一句“女生就是矫情”,挂电话后十分钟不到所有相关的群都被移除了。")]),v._v(" "),t("p",[v._v("后面咨询了学医的一个同学,他告诉我情况很严重,让我马上打电话给我家人跟他们说。这位同学平时笑嘻嘻的,此时是无比的认真严厉。哥第二天来接我,回家那边的医院看,走的时候医院还让签了一份免责声明书。")]),v._v(" "),t("p",[v._v("术前要稳定病情,那段时间我打了这辈子都没见过那么多的针,到后面护士都找不到可以扎针的地方了,留置针也基本上一次就用不了了。手术结束后还进了ICU好几天,我到现在还记得,当时血压一直升不起来医生给打了肾上腺素,高烧不退只能物理降温,身边堆满了冰袋。我跟医生说好冷啊,医生象征性地在我的床架上搭了个被子,说没有办法了你就忍忍吧。")]),v._v(" "),t("p",[v._v("后来阿姨跟我说,我从手术室推出来的时候,我爸心疼得眼红红又站一边不知所措,自尊心那么强的一个人啊。出院的时候只剩下骨头,晚上睡觉都能磕着自己,如果这一切要让我再经历一次,我觉得自己没有勇气坚持过来。")]),v._v(" "),t("p",[v._v("从那以后,生命和健康真正成为我最重视的一道线。努力可以,奋斗也可以,但长期的加班熬夜通宵、无节制地消耗身体这样的行为则成了我的底线。血的教训让我狠狠地体会了一把,什么事情对于我来说才是最重要的。")]),v._v(" "),t("p",[v._v("所以,即便有一万个不愿意把这些写下来,但如果能让哪怕一个人有所启发,不至于重蹈覆辙这样的痛苦,我也觉得是值得的。")]),v._v(" "),t("h1",{attrs:{id:"什么都比不上身边有你们"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#什么都比不上身边有你们"}},[v._v("#")]),v._v(" 什么都比不上身边有你们")]),v._v(" "),t("p",[v._v("也是从这事之后,和家人的关系缓和了很多,再也没有说话很冲。因为我们都知道,彼此都能健康快乐地活着就是最大的幸福。")]),v._v(" "),t("p",[v._v("因为我知道,当我遇到困难的时候,愿意接受我、陪伴我、帮助我的人,永远是最亲近的人,家人和朋友。")]),v._v(" "),t("h2",{attrs:{id:"工作不是你的全部"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#工作不是你的全部"}},[v._v("#")]),v._v(" 工作不是你的全部")]),v._v(" "),t("p",[v._v("不知道是因为出了象牙塔的原因,还是因为这些年来社会风气变化的原因,似乎有太多太多的人把工作放在最重要的位置。身体、家人、生活甚至有时候还成为一些人通往权利之路上的阻碍。")]),v._v(" "),t("p",[v._v("当我做一些自己认为对的事情,坚持一些认为很重要的事情,有人甚至跟我说,“哇,被删你三观好正啊”。所以这个世界是从什么时候开始,三观正常变成了一件很稀有的事情呢?")]),v._v(" "),t("p",[v._v("有些人为了工作、赚钱,不顾身体健康。有些人为了获得更多,开始接受一些越线的事情。有些人仅仅为了拿到更好的业绩,不择手段、落井下石。而几乎所有人都觉得这是很正常的事情,大家都不相信公正,但却会觉得有些遭遇了不幸的人肯定有原因的。这是为什么呢?")]),v._v(" "),t("p",[v._v("有时候我是偏激的,有时候我又是矫情的,但不管什么时候,我都能知道什么是最重要的。人生苦短,请及时行乐。工作几乎是占每个人一半人生的一件事,但明明是一半的人生为什么每个人都期盼着五年、十年就能达到顶峰呢,为什么每个人都那么着急呢。我们现在做下的选择,能在十年二十年后骄傲地告诉后辈自己做的这些事情吗?")]),v._v(" "),t("p",[v._v("人的一生里,除了激情的工作、拼搏的回忆,更还有那些选择陪伴在我们身边的人。即使没有大富大贵,即使没有成名成就,那些在你人生低谷愿意花时间听你倾诉、安慰和帮你分析想办法的人们,才是该去好好珍惜、好好对待的人。为什么我们愿意吹捧同事和领导,却对最亲爱的人出言不逊呢?")]),v._v(" "),t("h2",{attrs:{id:"最重要的还有你们的那个1"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#最重要的还有你们的那个1"}},[v._v("#")]),v._v(" 最重要的还有你们的那个1")]),v._v(" "),t("p",[v._v("我原本以为我能放下和理解当时的一些情况,而当如今写下来的时候,我才知道自己依然是恨着的。")]),v._v(" "),t("p",[v._v("那天,家人突然遭遇车祸送院抢救。情况很危急,与死神赛跑。我依然记得当天手术后医生给我们讲述情况的时候,胸口闷得发慌,全身发抖站不住。当天晚上,还守在ICU外面的时候,收到了“今年要淘汰末位20%,你还要面临晋级、结婚生娃等,要加油努力了”这样的消息。发消息的人也知道家人还在ICU情况不稳定,未曾有一句问候和关心,却发来这么一大段。在后续情况稳定回到普通病房后,和家人轮流照顾时,还让联调一个不重要也不紧急的接口。心都凉透了。")]),v._v(" "),t("p",[v._v("那段日子里,我对深圳也是有一丝怨恨的。情况稳定后,回到工作中的第一件事就换组了。有些事情,可以理解是一回事,接受则是另外一回事了。")]),v._v(" "),t("p",[v._v("有时候我觉得自己就是在演电视剧,遇到很多奇葩的事情,这些都只是其中的一部分。很多人会受到“公正世界”效应的影响,相信我们遭到了什么报应,所以很多人在遇到一些事情的时候,做的第一件事情就是责怪自己。")]),v._v(" "),t("p",[v._v("但这个世界不是公正的,随机性的事情随时随刻都可能发生在任何人身上。所以相比于相信正义终有一天会到来,不如当下选择勇敢面对。")]),v._v(" "),t("p",[v._v("看着家人还都在,偶尔聊聊天、撒撒娇,工作中再多的不顺畅也值得。家人的问候从来都两句,记得锻炼身体、要过得开心,曾经觉得没什么用处的“废话”,到如今成了人生的座右铭。")]),v._v(" "),t("p",[v._v("这个世界很任性,我们规划了很多很多,都赶不上一个意外、一些奇葩的人和事来打乱。工作后,一泼泼凉水浇灭了很多的东西,而庆幸的是,心还是温暖跳动的。重要的不是你成功的时候有多少人追捧你,而是在遭遇什么事情的时候愿意向你伸手的人。")]),v._v(" "),t("p",[v._v("渴望成功没有错,但失败也不是什么大不了的事情。因为不管怎样,我还有身后一直在支持我的那些朋友家人。最重要的事情,当然也还有你们的那个1。")]),v._v(" "),t("p",[v._v("只要人还在,一切都还能重来呢。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("照顾好自己,不要期待公正。")])])}),[],!1,null,null,null);_.default=s.exports}}]);
--------------------------------------------------------------------------------
/assets/js/106.c48e18a1.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[106],{669:function(v,_,t){"use strict";t.r(_);var e=t(69),r=Object(e.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("随着互联网的迅速发展,现在我们前端应用能做的事情也越来越多了。随之而来的便是复杂和大数据内容的网页渲染,因此很多时候我们为了尽快给用户看到网页内容,会将页面加载拆分成首屏和其他内容。")]),v._v(" "),t("p",[v._v("当首屏内容渲染完后,我们会进行后续内容的预加载处理。")]),v._v(" "),t("h1",{attrs:{id:"页面预加载"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#页面预加载"}},[v._v("#")]),v._v(" 页面预加载")]),v._v(" "),t("p",[v._v("对于重文本、列表、表格等网页内容来说,当内容过多、过长时都会考虑进行分页。")]),v._v(" "),t("p",[v._v("一般来说,由于浏览器页面都是根据屏幕宽度进行加载和换行,因此加载过程基本都是从页面最上方开始,然后往下滚动和加载。很多时候,性能优化都会简单地考虑两个方面:")]),v._v(" "),t("ol",[t("li",[v._v("内容分页和预拉取。")]),v._v(" "),t("li",[v._v("长列表的预加载和回收。")])]),v._v(" "),t("p",[v._v("对于长列表、长文本的页面来说,不管是屏幕外资源回收,还是屏幕外数据预拉取、预加载,都会比较简单,因为只有垂直方向。")]),v._v(" "),t("p",[v._v("这里我们即将讨论的预加载顺序设计,场景则是更加大一些的窗口画布场景。比如在线文档、在线表格、画板绘制等较大的画布,且具备各个方向滚动滑动的场景,我们的屏幕仅能展示其中的一部分,如下图:")]),v._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/preload-order-1.jpg",alt:""}})]),v._v(" "),t("h2",{attrs:{id:"用户浏览行为参考"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#用户浏览行为参考"}},[v._v("#")]),v._v(" 用户浏览行为参考")]),v._v(" "),t("p",[v._v("显然,对于画布类的页面来说,数据量可能会无限大,因此数据的分片/分页/分块几乎是必备的。至于如何加载和计算这些数据,之前我在"),t("RouterLink",{attrs:{to:"/front-end-basic/render-engine//render-engine-render-and-collect.html"}},[v._v("《复杂渲染引擎架构与设计》")]),v._v("系列中也有介绍过,包括分片渲染、增量渲染、离屏渲染等等,这里不再赘述。")],1),v._v(" "),t("p",[v._v("当我们在优先加载和渲染完屏幕内范围的数据之后,还需考虑后续其他数据该如何处理加载顺序。而屏幕外的各个方向都有未加载数据,我们可以结合用户行为来调整加载的优先顺序。")]),v._v(" "),t("p",[v._v("关于用户在网页阅读时的行为模式和视觉关注的研究,有不少相关的文献和数据。参考一些用户的研究报告,我们会发现用户的阅读习惯包括但不仅限于以下方式:")]),v._v(" "),t("ol",[t("li",[v._v("F 形模式:许多用户在阅读网页时会遵循 F 形模式,即先水平扫描页面的顶部,然后向下移动,进行第二次水平扫描,最后在左侧进行垂直扫描。这种模式表明用户在页面的上部和左侧区域更容易获得注意。")]),v._v(" "),t("li",[v._v("Z 形模式:在某些情况下,用户可能会遵循 Z 形模式,尤其是在页面布局较为简单时。用户从左上角开始,水平移动到右上角,然后斜向下到左下角,最后水平移动到右下角。这种模式适用于较短的内容或广告。")]),v._v(" "),t("li",[v._v("中心聚焦:用户的注意力往往集中在页面的中心区域,尤其是当内容以图像、视频或重要信息块的形式呈现时。重要信息通常会放置在页面的中心,以吸引用户的注意。")]),v._v(" "),t("li",[v._v("边缘注意:页面边缘的内容(如侧边栏、广告或导航菜单)通常会获得较少的关注,但在某些情况下,用户会浏览这些区域以寻找额外的信息或链接。")]),v._v(" "),t("li",[v._v("滚动行为:用户在阅读长页面时,通常会向下滚动。研究表明,用户在滚动时的注意力会逐渐减少,尤其是在页面的底部。因此,重要信息应尽量放在用户最初可见的区域(即“首屏”)。")]),v._v(" "),t("li",[v._v("视觉层次:页面设计中的视觉层次(如标题、字体大小、颜色对比等)会影响用户的阅读方向和注意力分布。较大的标题和醒目的颜色通常会吸引更多的注意。")])]),v._v(" "),t("p",[v._v("这些研究很多是采用“使用眼动追踪技术”来分析用户的视觉关注,如下图则是针对一名中国人的研究结果:")]),v._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/preload-order-2.png",alt:""}})]),v._v(" "),t("p",[v._v("从这些研究结果中,我们可以获得一些可借鉴参考的点,包括:")]),v._v(" "),t("ol",[t("li",[v._v("当人们以 F 形扫描时,他们会因为文本在列中的流动方式而错过大量内容。这种方式其实不利于网页内容的展示,解决方案则是可以使用用户的其它行为习惯来打断,包括:")])]),v._v(" "),t("ul",[t("li",[v._v("将最重要的要点包含在页面的前两段中")]),v._v(" "),t("li",[v._v("使用标题和副标题,确保它们看起来比普通文本更重要、更显眼,以便用户能够快速区分它们")]),v._v(" "),t("li",[v._v("用包含最多信息的单词开始标题和副标题")]),v._v(" "),t("li",[v._v("在视觉上对少量相关内容进行分组")]),v._v(" "),t("li",[v._v("用粗体标出重要的单词和短语")]),v._v(" "),t("li",[v._v("使用项目符号和数字来标注列表或流程中的项目")])]),v._v(" "),t("ol",{attrs:{start:"2"}},[t("li",[v._v("在页面的预加载过程中,我们可以参考用户的行为习惯,作为数据拉取顺序的参考。")])]),v._v(" "),t("h2",{attrs:{id:"屏幕外数据预加载顺序设计"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#屏幕外数据预加载顺序设计"}},[v._v("#")]),v._v(" 屏幕外数据预加载顺序设计")]),v._v(" "),t("p",[v._v("考虑到在大画布页面布局下,用户的垂直滚动行为会比水平滚动行为更常见,并且向下比向上、向右比向左的行为可能性更高,我们可以设计以下的数据加载顺序:")]),v._v(" "),t("ol",[t("li",[v._v("当前屏幕范围。")]),v._v(" "),t("li",[v._v("屏幕范围下方。")]),v._v(" "),t("li",[v._v("屏幕范围上方。")]),v._v(" "),t("li",[v._v("屏幕范围右侧。")]),v._v(" "),t("li",[v._v("屏幕范围左侧。")])]),v._v(" "),t("p",[v._v("简单粗暴的如图:\n"),t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/preload-order-3.jpg",alt:""}})]),v._v(" "),t("p",[v._v("这种方式加载,其实有点像棋盘走棋的距离,比如:")]),v._v(" "),t("ol",[t("li",[v._v("走一步的距离,按照优先顺序为:下、上、右、左。")]),v._v(" "),t("li",[v._v("走两步的距离,按照优先顺序为:下下、上上、下右、上右、下左、上左。")]),v._v(" "),t("li",[v._v("以此类推。")])]),v._v(" "),t("p",[v._v("当然,如果页面结构有其他布局变化的话,这种优先顺序显然是不满足的,比如表格考虑冻结、筛选等场景,长文页面布局考虑分栏、图片、标题、副标题等,各种场景下的优先顺序会有影响。")]),v._v(" "),t("p",[v._v("最好的方式,便是可以在页面中埋个点,验证一下用户在打开首屏后,接下来的行为顺序。同时收集页面中的各种要素,来大致得到用户的行为习惯,并以此来调整页面加载和渲染的优先顺序,给到用户更好的体验。")]),v._v(" "),t("h2",{attrs:{id:"参考文章"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#参考文章"}},[v._v("#")]),v._v(" 参考文章")]),v._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://www.nngroup.com/articles/how-people-read-online/",target:"_blank",rel:"noopener noreferrer"}},[v._v("《How People Read Online: New and Old Findings》"),t("OutboundLink")],1)]),v._v(" "),t("li",[t("a",{attrs:{href:"https://www.nngroup.com/articles/f-shaped-pattern-reading-web-content/",target:"_blank",rel:"noopener noreferrer"}},[v._v("《F-Shaped Pattern of Reading on the Web: Misunderstood, But Still Relevant (Even on Mobile)》"),t("OutboundLink")],1)])]),v._v(" "),t("h1",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("本文介绍了用户的一些常见阅读习惯,其实除了说在做数据预加载、预处理的时候可以用到,在更多的场景我们都能使用。比如,调整页面的布局,使得用户可以符合预期地注意到某些内容。")]),v._v(" "),t("p",[v._v("比如,写文章的时候,也可以使用一些小技巧(分段、标题和副标题、加粗、分组等),来减少阅读用户的流失率。")])])}),[],!1,null,null,null);_.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/194.a52f1482.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[194],{756:function(v,_,t){"use strict";t.r(_);var a=t(69),r=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("这些年也有不少的面试别人和面试自己的经历,也有好些人来咨询一些前端的面试题目和准备,所以整理一下记录下来。本文介绍常见的面试流程,以及相关的一些技巧。\n")]),v._v(" "),t("p",[v._v("如果你也有过找工作经验,想必对一些面试的流程也稍有掌握。本文分享本骚年这些年来面试别人和被人面试的一些面试流程情况,帮助大家面试过程做好更多的准备。")]),v._v(" "),t("h1",{attrs:{id:"面试流程"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#面试流程"}},[v._v("#")]),v._v(" 面试流程")]),v._v(" "),t("hr"),v._v(" "),t("h3",{attrs:{id:"电话面试"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#电话面试"}},[v._v("#")]),v._v(" 电话面试")]),v._v(" "),t("p",[v._v("为了提升面试官和面试者的效率,节省相互间的时间和精力成本,通常前面 1-2 轮的基础技术面试会采用电话面试的方式。电话面试由于空间原因,面试官和面试者无法直接观察到对方,因此对面试者的表达能力要求更高。")]),v._v(" "),t("p",[v._v("很多人在电话面试的时候会稍微做点准备,例如准备一些资料在手边,又或者打开电脑浏览器随时准备搜索。这种方式比较适合有较完整准备的人,较完整准备的意思不是指把所有的面试题目和知识点都收集齐,而是指将这些资料都完全理解并二次整理的人。")]),v._v(" "),t("p",[v._v("在面试过程中,其实不建议使用查资料然后念出来这样的方式。作为面试官的时候,也经常会遇到一些小伙子在问到一些问题的时候,说“我看看”、“我想想”,然后过了差不多八九秒才开始描述,而描述的时候让人觉得在死记硬背。其实这样的方式会给面试官留下不好的印象,面试的过程除了考察对方的基础技能,也是寻找对方亮点、观察沟通能力和思维逻辑的一个过程,更多的时候由于基础技能只需要花时间就可以准备好,后者会更能让面试官产生产生一定的记忆。")]),v._v(" "),t("p",[v._v("电话面试过程中,面试官更容易由于对方的回答过于枯燥而失去耐心。所以如果你想要留下个好印象,尽量将要讲述的内容完全理解并进行二次加工,变成你自己的话语来输出吧。例如,讲到某个知识点的时候,可以结合自己的一些项目经验来辅助描述。")]),v._v(" "),t("h3",{attrs:{id:"视频面试"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#视频面试"}},[v._v("#")]),v._v(" 视频面试")]),v._v(" "),t("p",[v._v("视频面试在最近几年才开始兴起,目前还比较小众。视频面试相比于电话面试多了一些互动,能让面试官和面试者的氛围更加轻松。")]),v._v(" "),t("p",[v._v("更多的时候,视频面试会结合一些在线编程的平台来进行。面试官要求开发者通过实时的平台在线做题,面试官则可以观察到开发者的整个编码过程。除了平台以外,也可以通过面试者共享屏幕的方式来观察做题过程。")]),v._v(" "),t("p",[v._v("这种情况下,面试者最好准备一台平时用于开发的电脑,因为熟悉的编辑器或者较完备的环境都可以提升开发效率。而做题过程中,恰当地使用搜索引擎也是一种方式,毕竟日常工作里我们也经常需要搜索一些解决方案、不容易记住的语法或API等等。对于编程开发来说,如何高效地使用搜索引擎其实也是一个很重要的能力。面试官通常都不会要求不能搜索,但是直接搜题目答案就会稍微过分了点哈。")]),v._v(" "),t("h3",{attrs:{id:"现场面试"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#现场面试"}},[v._v("#")]),v._v(" 现场面试")]),v._v(" "),t("p",[v._v("虽然如今电话面试、视频面试等线上面试方式越来越普及,但大多数公司依然会保留现场面试的过程。")]),v._v(" "),t("p",[v._v("现场面试通常会将多次的面试都安排在一起,比较高效地完成一次面试。除了技术能力,工作中同样重要的一些能力,例如表达能力、逻辑思维、应变能力、抗压能力等,通过现场面试可以更全面地考察面试对象。所以,现场面试的时候,面试者如果表现得真诚、友好、自信、乐观、积极主动,会让面试官有更好的印象。")]),v._v(" "),t("p",[v._v("现场面试还可能会有笔试环节、问答环节等,网上也有很多的面经,大家也可以去找找看。")]),v._v(" "),t("h2",{attrs:{id:"常见的面试流程"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#常见的面试流程"}},[v._v("#")]),v._v(" 常见的面试流程")]),v._v(" "),t("hr"),v._v(" "),t("p",[v._v("前面讲的是几种常见的面试方式,现在我们来看看常见的一些面试流程,也就是俗话中说的“一面”、“二面”、“最终面”等这些过程。")]),v._v(" "),t("h3",{attrs:{id:"笔试"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#笔试"}},[v._v("#")]),v._v(" 笔试")]),v._v(" "),t("p",[v._v("笔试题更多地出现在一些基础级别的岗位,通常用来考察基础能力。当你面试的岗位职级越来越高,笔试占有的比重会越来越轻,这个时候会更考察项目和思考的一些能力。")]),v._v(" "),t("p",[v._v("对于笔试环节,一般面试官提供一份笔试题,让对方在一定时间内完成,有条件的还会提供上机做题的方式。对于每天要面试很多人的面试官来说,这是一个比较高效的筛选方式。通过面试者的回答情况,以及简单地描述思考过程、相关的知识点,可以快速地考察一些基础能力是否扎实。")]),v._v(" "),t("p",[v._v("对于笔试题,有个痛点就是记不住常用的语法和API,于是很多笔试题都不限语言。实在记不住的时候,也是可以使用文字描述做题过程的,但如果能记住当然是更好的。")]),v._v(" "),t("p",[v._v("在这个环节中,刷题还是很重要的,多去网上搜一些相关的题目来练习吧。")]),v._v(" "),t("h3",{attrs:{id:"技术面"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#技术面"}},[v._v("#")]),v._v(" 技术面")]),v._v(" "),t("p",[v._v("技术面试一般分为好几场,需要由2-3个相应的专业面试官多次考察,然后汇总得到一个综合的评价。该过程对于面试者的等级评定和最终定薪都有比较大的影响。")]),v._v(" "),t("p",[v._v("技术面除了考察基础技术,对于工作经验较少的面试者,也通常会加一些逻辑题来考察思维和逻辑。对于工作经验较多的面试者,则会更侧重地询问项目相关的内容,以及解决方案的输出。")]),v._v(" "),t("p",[v._v("如果你是应届生或者刚毕业没多久,可以侧重复习基础知识(刷题),以及提升思考方式和表达能力。同时如果有一定的相关项目经验、或是参与了一些开源项目、有自己的技术博客或是文章积累,也都是不错的亮点。")]),v._v(" "),t("p",[v._v("如果你工作两三年了或更多,需要更侧重关注自己的实践经验的积累、解决问题的方法、对项目的贡献等。不同于应届生是招来培养的,社招更多是招来干活和解决问题的,因此面试者需要让自己在思考、经验沉淀、解决方法等方面更加突出。面试官也通常会出一些“这个项目给到你,你要如何解决、你会如何考虑”等情况。")]),v._v(" "),t("p",[v._v("如果你面试的岗位中有管理团队(无论大小)的工作,会需要你能做出一些技术选型、对现有的一些技术有一定的了解,能做出合适的技术架构、对团队管理常见问题以及如何解决有一定的了解。")]),v._v(" "),t("p",[v._v("除此之外,面试官有时候也会问一些较灵活的题目,例如“你最近半年比较有挑战性的事情”、“你觉得自己的优势在哪里”、“你最擅长什么”等,来考察对方对项目和技术理解的深度。")]),v._v(" "),t("h3",{attrs:{id:"大佬面"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#大佬面"}},[v._v("#")]),v._v(" 大佬面")]),v._v(" "),t("p",[v._v("大佬面一般来说是由部门的管理者来进行,常常在HR面试前面,通常来说都是走个流程(当然也有例外)。")]),v._v(" "),t("p",[v._v("既然是走个流程,很多时候问的问题会根据大佬的心情而不一样。一般来说,你的简历里有让大佬感兴趣的部分,对方就会稍微问一下。如果没有比较特别的地方,可能也会简单地聊一下天就过了。")]),v._v(" "),t("p",[v._v("如果你走到了这个流程,可以放松自己来进行面试。")]),v._v(" "),t("h3",{attrs:{id:"hr面"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#hr面"}},[v._v("#")]),v._v(" HR面")]),v._v(" "),t("p",[v._v("HR面试通常也是比较流程化,通常会问一些生活或者非专业相关的工作问题。")]),v._v(" "),t("p",[v._v("HR更多是在考察面试者的稳定性,常见的问题包括“为什么跳槽”、“结婚了没”、“有木有男/女朋友”、“是否异地恋”、“家在哪里”等。有些公司也会考察面试者的价值观,有的甚至还有一套价值观题目来做。其实也是从稳定性方面来把控,如果个人的价值观和公司文化不符合,其实也不适合长期合作。这个环节淘汰率也很低,毕竟招人不容易,但是如果没过也不用太纠结,勉强得到的工作常常会更让人折磨。")]),v._v(" "),t("p",[v._v("薪酬待遇的谈判也在这个环节,有些HR比较真诚,有的就喜欢砍价,这里得具体问题具体分析了。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("一次面试过程算下来可能多的有七八轮,少的也有四五轮,找工作真是场持久战呢。一份合适的工作真的不容易,我们常常在这个过程慢慢地失去最初对这个行业的憧憬,但请不要放弃,要相信困难的日子会慢慢过去的。\n如果你想了解这些年前端的一些感想和经验,也可看"),t("a",{attrs:{href:"../front-end-days/about-front-end-1-begin-in"}},[v._v("《前端这些年》")]),v._v("系列。")])])}),[],!1,null,null,null);_.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/67.66f4a05e.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[67],{629:function(t,_,v){"use strict";v.r(_);var s=v(69),e=Object(s.a)({},(function(){var t=this,_=t.$createElement,v=t._self._c||_;return v("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[v("p",[t._v("像在线文档这样大型的项目,不管是从功能职责方面,还是从代码维护方面,分层和分模块都是必然的趋势。而网络层作为与服务端直接连接的一层,有多少是我们可以做到更好的呢?")]),t._v(" "),v("h1",{attrs:{id:"认识网络层"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#认识网络层"}},[t._v("#")]),t._v(" 认识网络层")]),t._v(" "),v("p",[t._v("首先,涉及多人在线协作的场景,从用户交互到服务端存储都会特别复杂。对于前端来说,从后台获取的数据到展示,分别需要经过网络层、数据层和渲染层。除此之外,多人在线同样涉及房间管理等,简单来说,我们大致可以这么进行分层(图1):")]),t._v(" "),v("p",[v("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/tencent-doc-network_0.png",alt:"图1"}})]),t._v(" "),v("h2",{attrs:{id:"网络层职责"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#网络层职责"}},[t._v("#")]),t._v(" 网络层职责")]),t._v(" "),v("p",[t._v("一般来说,网络层无非就是做一些与服务端通信的工作,例如发起请求、异常处理、自动重试、登录态续期等。如果说,除了 HTTP 请求,可能还涉及 socket、长连接、请求数据缓存等各种功能。")]),t._v(" "),v("p",[t._v("在多人协作的场景下,为了保证用户体验,一般会采用 OT 算法来进行冲突处理。而为了保证每次的用户操作都可以按照正确的时序来更新,我们会维护一个自增的版本号,每次有新的修改,都会更新版本号。因此,在这样的场景下,网络层的职责大概包括:")]),t._v(" "),v("ul",[v("li",[t._v("校验数据合法性")]),t._v(" "),v("li",[t._v("本地数据准确的提交给后台(涉及队列和版本控制)")]),t._v(" "),v("li",[t._v("协同数据正确处理后分发给数据层(涉及冲突处理)")])]),t._v(" "),v("p",[t._v("我们能看到,与网络层有交接的主要包括服务端和数据层。")]),t._v(" "),v("p",[t._v("那么,我们可以考虑将网络层拆分模块:\n"),v("strong",[t._v("1. 连接层:管理与服务端的连接(Socket、长连接等)。")]),t._v(" "),v("strong",[t._v("2. 接入层:管理数据版本、冲突处理、与数据层的连接等。")])]),t._v(" "),v("p",[t._v("这样,我们的分层结构调整为图2:")]),t._v(" "),v("p",[v("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/tencent-doc-network_1.jpg",alt:"图2"}})]),t._v(" "),v("h2",{attrs:{id:"网络层设计"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#网络层设计"}},[t._v("#")]),t._v(" 网络层设计")]),t._v(" "),v("p",[t._v("既然对网络层进行了模块的拆分,那么相关的设计我们也来分模块进行吧。")]),t._v(" "),v("h3",{attrs:{id:"连接层"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#连接层"}},[t._v("#")]),t._v(" 连接层")]),t._v(" "),v("p",[t._v("连接层作为直接与服务端连接的一层,需要包括以下能力(图3):")]),t._v(" "),v("ul",[v("li",[v("strong",[t._v("多种通信方式支持")]),t._v("(长连接、socket、短连接、SSE等)")]),t._v(" "),v("li",[v("strong",[t._v("房间管理")]),t._v("(心跳管理、用户管理、消息管理)")])]),t._v(" "),v("p",[v("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/tencent-doc-network_2.png",alt:"图3"}})]),t._v(" "),v("p",[v("strong",[t._v("1. 多种通信方式支持。")])]),t._v(" "),v("p",[t._v("前后端通信方式有很多种,常见的包括 HTTP 短轮询(pollinf)、Websocket、HTTP 长轮询(long-polling)、SSE(Server-Sent Events)等。")]),t._v(" "),v("p",[t._v("我们也能看到,不同的在线文档团队选用的通信方式并不一致。例如谷歌文档上行数据使用 Ajax、下行数据使用 HTTP 长轮询推送;石墨文档上行数据使用 Ajax、下行数据使用 SSE 推送;金山文档、飞书文档、腾讯文档则都使用了 Websocket 传输。")]),t._v(" "),v("p",[t._v("而每种通信方式都有各自的优缺点,包括兼容性、资源消耗、实时性等,也有可能跟业务团队自身的后台架构有关系。因此我们在设计连接层的时候,考虑接口拓展性,应该预留对各种方式的支持。")]),t._v(" "),v("p",[v("strong",[t._v("2. 房间管理。")])]),t._v(" "),v("p",[t._v("由于多人协同的需要,相比普通的 Web 页面,还多了房间和用户的管理。在同一个文档中的用户,可视作在同一个房间。除了能看到哪些人在同一个房间以外,我们能收到相互之间的消息,在文档的场景中,用户的每一个操作,都可以作为是一个消息。")]),t._v(" "),v("p",[t._v("但文档和一般的房间聊天不一样的地方在于,用户的操作内容可能会很大,例如用户复制粘贴了一个10W、20W的表格内容,这样的消息显然无法一次性传输完。在这种情况下,除了考虑像 Websocket 这种需要自行进行数据压缩(HTTP 本身支持压缩)以外,我们还需要实现自己的分片逻辑。当涉及数据分片之后,紧接而来的还有如何分片、分片数据丢失的一些情况处理。")]),t._v(" "),v("p",[t._v("这样,我们的连接层则演化成图4的架构:")]),t._v(" "),v("p",[v("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/tencent-doc-network_3.jpg",alt:"图4"}})]),t._v(" "),v("h2",{attrs:{id:"接入层"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#接入层"}},[t._v("#")]),t._v(" 接入层")]),t._v(" "),v("p",[t._v("接入层主要负责与业务比较相关的一些内容,例如协同数据的版本管理、冲突处理、用户操作的任务队列。我们可以从接收和发送两个方向来进行拆分(图5):")]),t._v(" "),v("ul",[v("li",[v("strong",[t._v("接收数据")]),t._v("(服务端 -> 数据层):管理来自服务端的数据")]),t._v(" "),v("li",[v("strong",[t._v("发送数据")]),t._v("(数据层 -> 服务端):管理需要提交给服务端的数据")])]),t._v(" "),v("p",[v("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/tencent-doc-network_4.png",alt:"图5"}})]),t._v(" "),v("p",[t._v("接入层的职责主要包括两个部分:\n"),v("strong",[t._v("1. 维护数据任务队列。")])]),t._v(" "),v("ul",[v("li",[t._v("用户操作数据正常进入队列")]),t._v(" "),v("li",[t._v("队列任务正常提交到接入层")]),t._v(" "),v("li",[t._v("队列任务提交异常后进行重试")]),t._v(" "),v("li",[t._v("队列任务确认提交成功后移除")])]),t._v(" "),v("p",[v("strong",[t._v("2. 数据版本有序递增。")])]),t._v(" "),v("ul",[v("li",[t._v("协同数据版本更新")]),t._v(" "),v("li",[t._v("丢失数据版本补拉")]),t._v(" "),v("li",[t._v("提交数据版本递增")])]),t._v(" "),v("p",[t._v("也就是说,来自数据层的数据,将会添加到任务队列中。任务队列中的数据在提交数据之前,需要检查本地的版本和服务端的版本是否一致,如果有版本缺失的话,则要先从服务端拉取缺失的版本,确认本地版本最新后,则可以提交到服务端。")]),t._v(" "),v("p",[t._v("而来自服务端的数据,则需要先和任务队列中的数据进行冲突处理。冲突处理完成之后,则会同步到数据层。")]),t._v(" "),v("p",[t._v("这里面其实还有更多的细节,包括队列中任务的状态、任务的合并、数据版本的合并、版本断层的处理、重试失败的处理,队列中任务与协同消息的冲突处理、撤销重做的反向冲突处理,甚至还可能涉及断网离线的操作、本地缓存的任务队列、离线数据与在线数据的同步等等。本文我们就不继续讨论这些细节,还是回归到整体的设计上。")]),t._v(" "),v("p",[t._v("到这,我们的网络层架构大概出来了:")]),t._v(" "),v("p",[v("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/tencent-doc-network_5.png",alt:"图6"}})]),t._v(" "),v("h1",{attrs:{id:"结束语"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[t._v("#")]),t._v(" 结束语")]),t._v(" "),v("p",[t._v("相比其他常见前端项目,在线文档光是网络层的设计都要复杂很多。而网络层只是其中一小部分,我们还有数据层、渲染层、各个组件和 feature 模块,依赖管理、通信管理、流程控制、性能优化等各种功能模块,每一个都有特别多的挑战。对于前端来说,能参与这样一个项目也是一件很幸福的事情了。")])])}),[],!1,null,null,null);_.default=e.exports}}]);
--------------------------------------------------------------------------------
/assets/js/178.cff63f2d.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[178],{738:function(_,v,t){"use strict";t.r(v);var a=t(69),r=Object(a.a)({},(function(){var _=this,v=_.$createElement,t=_._self._c||v;return t("ContentSlotsDistributor",{attrs:{"slot-key":_.$parent.slotKey}},[t("p",[_._v("最近对程序员这个职业产生了一些困惑,所以一个问题一个问题地记录下来叭~")]),_._v(" "),t("h2",{attrs:{id:"技术开发到底是门槛低还是高"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#技术开发到底是门槛低还是高"}},[_._v("#")]),_._v(" 技术开发到底是门槛低还是高?")]),_._v(" "),t("p",[_._v("对于“程序员”这个职业,或许现在已经被大多数人认知,常常被认为是吃技术饭碗、工资高的一个工种。正所谓内行看门道外行看热闹,这两个标签都只能代表一部分开发。")]),_._v(" "),t("p",[_._v("有工资高的,自然就有工资低的。资本的本质就是商业化,通俗来说就是赚钱,因此只要存在可优化和压缩的可能性,都会被优化,而目前的技术开发大多数服务于资本。")]),_._v(" "),t("h3",{attrs:{id:"技术开发与研发"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#技术开发与研发"}},[_._v("#")]),_._v(" 技术开发与研发")]),_._v(" "),t("p",[_._v("实际上,我们常常将“研发”和“开发”搞混。根据维基百科,研发并非旨在立即产生利润,而且通常具有更大的风险和不确定的投资回报。如今大多数互联网产品“研发”会对投入产出比要求更高,也会对盈利预期有所要求,讲究“快速试错”、“快速迭代”,对产品生命周期、准确来说是盈利周期,会有更高的要求,这个不行就撤了做下一个。")]),_._v(" "),t("p",[t("a",{attrs:{href:"https://en.wikipedia.org/wiki/Research_and_development",target:"_blank",rel:"noopener noreferrer"}},[_._v("维基百科"),t("OutboundLink")],1),_._v(":")]),_._v(" "),t("blockquote",[t("p",[_._v("新产品的设计和开发往往是公司生存的关键因素。在瞬息万变的全球工业格局中,公司必须不断修改其设计和产品范围。由于激烈的竞争和消费者不断变化的偏好,这也是必要的。如果没有研发计划,公司就必须依靠战略联盟、收购和网络来利用他人的创新。")])]),_._v(" "),t("p",[_._v("显然,如今很多大公司更倾向于后者,因为相比预期不确定的产品研发,这种方式可以更稳定地盈利。因此我们也常常会看到,愿意长期投入而不计成本地进行研发的团队很少,因为研发需要资金,更多时候都是通过融资、战略合作来解决资金问题。但这样依然会存在问题,投资方对盈利的预期,是否和产品本身能够匹配。")]),_._v(" "),t("p",[_._v("所以,很多时候我们提到“程序员”,其实大多数都属于开发而非研发。至于做开发是不是一个技术活,说实话,这玩意入门不难,但是做好不易。")]),_._v(" "),t("h3",{attrs:{id:"自学入门与培训班的红海"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#自学入门与培训班的红海"}},[_._v("#")]),_._v(" 自学入门与培训班的红海")]),_._v(" "),t("p",[_._v("正因为入门不难,所以这些年我们也能看到无数培训班的出现。当然,培训效果显然不像宣传那么好,但对于一些对这个行业一概不知的人来说,有人引路和所谓培训是更快捷的方式。")]),_._v(" "),t("p",[_._v("培训班里做的事情,就是将一些网上的文章/博客/课程资源进行整合,然后按照课时整理成每节课/每天的计划,最重要的一点是,他们会针对面试的知识点做较多的培训,也会教学员简历该怎么写。(其实我没怎么了解过培训班,以上是我自己认为一个培训班应该会做的一些事情)。所以,对于知道如何获取资源的小白来说,自学入门也是可以做到的。")]),_._v(" "),t("p",[_._v("很多培训班都会有一些成功案例,多少人拿到了 offer,甚至如果有人进了大公司肯定都得上历史荣耀板了(如果有这么一个板的话)。这些成功案例的确能说明一些事情:这个培训班针对面试的知识体系准备/简历包装比较到位,但实际上开始工作之后,能拿到怎样的表现都只能靠自己了。")]),_._v(" "),t("p",[_._v("其实我觉得培训班里最需要教的一件事,就是要怎么通过搜索引擎,使用合适的关键字,找到有效的问题解决方法。因为在大多数开发的工作中遇到的问题,99% 都可以在网上找到办法解决,至于剩下的 1%,只需要重启 VsCode/App/浏览器/电脑,就可以解决。所以,对自学入门的开发来说,搜索也是直接影响工作能力和效率的一种能力。")]),_._v(" "),t("h3",{attrs:{id:"晋级-考核与技术能力的关系"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#晋级-考核与技术能力的关系"}},[_._v("#")]),_._v(" 晋级/考核与技术能力的关系")]),_._v(" "),t("p",[_._v("对于开发来说,职级则常常被认为是技术能力的标签。")]),_._v(" "),t("p",[_._v("不管是面试/找工作,还是开发与开发之间的交流,通常都会问到职级。什么阿里 P7/P8、腾讯 T10/T11,还有其他公司的(抱歉这块了解得不多),职级一般会与薪酬待遇挂钩,也会被动与开发的技术能力挂钩,所以晋级对开发来说是一件大事情。同样,考核也会和年终奖/待遇挂钩,因此也是开发中的大事情。拿到一个好的考核,顺利通过晋级答辩,可能就是互联网打工人比较开心的事情了。")]),_._v(" "),t("p",[_._v("或许有些人会认为,职级高的人肯定技术能力比较厉害,实际上也未必都是这样的。")]),_._v(" "),t("p",[_._v("我在工作中经历过两次晋级答辩,而唯一让我觉得自己能通过的原因,结论是只是运气好罢了。要怎么理解“运气好”这件事呢?大概就是你恰好在答辩前拿到了一个“容易答辩”的项目,然后做的结果还不错,答辩过程中恰好评委也认为有价值/做得不错,就通过了。")]),_._v(" "),t("p",[_._v("除此以外,老生常谈的 KPI 项目反而到处都是,很多人为了答辩而故意做一些项目(将原本简单的场景搞得很复杂、用高大上的术语包装等等),答辩通过后就甩手不管、让其他人维护,继续做下一个可以用来下次答辩的项目,这样的事情每天都在开发的世界里上演。还有所谓 PPT 工程,有些人没有做出成果甚至还没开始做的项目,只拿一个包装得很好的 PPT 去答辩并且通过了。正因为答辩通过并没有一个固定的标准,因此评委的主观态度占了很大的比例,我甚至见过为了提高答辩通过率,专门组队去找到相关评委的课上刷脸获取好感的。")]),_._v(" "),t("p",[_._v("考核也是如此,考核更多时候是直接上级进行评分排名,因此给上级们留下一个好的印象便很重要,也因此产生了不少的对上管理手段,比如刷脸刷存在感,迎合上级的想法做事情,等等。也常常会产生所谓的嫡系,这个词我也是工作好几年之后才知道的,虽然我个人认为,把自己的事情做好就可以,不需要过度做一些迎合的事情,但实际上这样的事情也每天都会在身边上演,而我能做的也只有管好我自己。")]),_._v(" "),t("p",[_._v("也有人说,不管是晋级也好,考核也好,都属于管理工具。从这个角度来说,晋级/考核与技术能力并没有必然的关系,新人可以将功能实现得很漂亮,高职级的开发也可能写出糟糕的代码。")]),_._v(" "),t("p",[_._v("我见过很多技术能力一般但晋级/考核很不错的例子,以及很多责任心和敬畏之心都差强人意的人甚至都过得很不错。当然这没有高低对错之分,但这样一来,对我来说职级和考核也并不那么重要了,因为它并不能代表你的技术能力,也不能代表你的实际能力。再者,自身的价值没有被团队挖掘和发现,其实这个团队也未必适合自己。")]),_._v(" "),t("h3",{attrs:{id:"工作能力与技术能力的差别"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#工作能力与技术能力的差别"}},[_._v("#")]),_._v(" 工作能力与技术能力的差别")]),_._v(" "),t("p",[_._v("前面提到,个人认为不管是考核还是职级,都跟自身的技术能力没有确定的关系,那么它会跟什么有关系呢?我认为是工作能力。")]),_._v(" "),t("p",[_._v("工作能力这个词很笼统,实际上它揽扩了所有工作中需要的一些能力。对于开发来说,或许技术能力是工作能力的一部分,但实际上更多的时候,我们的工作中都对沟通能力、理解能力、复盘能力(天知道为什么我要将其称作一种能力)、表达能力、情绪能力(感受他人情绪/管理自身情绪)等等各式各样的职场能力同样有一定的要求。")]),_._v(" "),t("p",[_._v("回到本文的主题:技术开发的门槛有多高?")]),_._v(" "),t("p",[_._v("大多数情况下,团队对技术开发的要求主要是:能快速响应团队需求,高效高质量解决团队问题。个人认为,对于大多数的产品开发过程中,技术能力只占据了一部分,且要求的门槛也并不会很高,尤其在一些团队急速扩招时还会降低招聘门槛。")]),_._v(" "),t("p",[_._v("作为技术开发,大多数时候我们都是谷歌工程师。对于新人来说,遇到奇怪的报错就去谷歌搜索一下解决方案,遇到没听过的东西就去谷歌搜索一下介绍和说明;对于有一定工作经验的开发来说,工作中常常要参考和学习业界方案、研究竞品方案,然后根据自己的项目情况,选择合适的解决方案并将其进行落地。")]),_._v(" "),t("p",[_._v("要把一个项目做成,从项目初期各种沟通和收集信息(沟通能力和理解能力),项目开始前的方案设计和评审(技术能力和表达能力),项目过程中的开发/联调/测试/问题修复(技术能力、沟通能力、表达能力和情绪能力),项目后期的复盘总结(复盘能力、表达能力),这个过程中除了技术能力也同样涉及到各式各样的职场能力,这中间的某一个短板可能就成了你的最大瓶颈,甚至可能因为某一处没有做好而背了低考核。")]),_._v(" "),t("p",[_._v("因此,"),t("strong",[_._v("与其说大多数的开发工作中对技术能力的要求门槛不高,还不如说对于“本该就拥有技术能力”这样的开发群体,更多时候技术以外的其他能力更能直接影响他的工作表现")]),_._v("。")]),_._v(" "),t("h3",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[_._v("#")]),_._v(" 结束语")]),_._v(" "),t("p",[_._v("很久以前, 我选择做开发的其中一个原因,便是觉得开发应该是一个比较单纯简单的群体,同时工作性质对逻辑要求比较高,这样的人肯定也很讲道理。因为抱着这样幼稚的想法,曾在工作中遇到了许多不愉快的经历。当然这不是谁的问题,只要有利益纠纷的地方,必然都有人情世故。")]),_._v(" "),t("p",[_._v("我常常在想,如果是“研发”岗位而不是“开发”岗位,是不是就没有这么多的问题呢?今晚做个梦试试看。")])])}),[],!1,null,null,null);v.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/196.738bfc49.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[196],{758:function(v,_,l){"use strict";l.r(_);var i=l(69),a=Object(i.a)({},(function(){var v=this,_=v.$createElement,l=v._self._c||_;return l("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[l("p",[v._v("这些年也有不少的面试别人和面试自己的经历,也有好些人来咨询一些前端的面试题目和准备,所以整理一下记录下来。本文概括地描述一下,面试过程中前端项目经验相关的问题。\n")]),v._v(" "),l("p",[v._v("上一篇我们介绍了前端面试中常见的一些基础专业知识,这些是面试的门槛。那要如何考察面试者的能力呢,通常会从对方的简历下手,询问相关的项目经验。")]),v._v(" "),l("h2",{attrs:{id:"项目经验"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#项目经验"}},[v._v("#")]),v._v(" 项目经验")]),v._v(" "),l("p",[v._v("项目经验通常和个人经历关系比较大,前端业务相关的的一些项目经验通常包括管理端、H5直出、Node.js、可视化,另外还包括参与工具开发的经验,方案选型、架构设计等。")]),v._v(" "),l("h3",{attrs:{id:"前端框架与工具库"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#前端框架与工具库"}},[v._v("#")]),v._v(" 前端框架与工具库")]),v._v(" "),l("p",[v._v("首先我们来看看前端框架,不管你开发管理端、PC Web、H5,还是现在比较流行的小程序,总会面临要使用某一个框架来开发。因此,以下的问题可能与你有关:")]),v._v(" "),l("ul",[l("li",[v._v("谈谈你对前端常见的框架(Angular/React/Vue)的理解")]),v._v(" "),l("li",[v._v("该项目使用 Angular/React/Vue 的原因是")]),v._v(" "),l("li",[v._v("如果现在你重新决策,你会使用什么框架")]),v._v(" "),l("li",[v._v("你有了解过这些框架都做了哪些事情,介绍一下是怎么实现的")]),v._v(" "),l("li",[v._v("Vue 中的双向绑定是怎么实现的?")]),v._v(" "),l("li",[v._v("介绍下 Angular 中的依赖注入")]),v._v(" "),l("li",[v._v("讲讲 React 的虚拟 DOM")]),v._v(" "),l("li",[v._v("如何进行状态管理,Vuex/Redux/Mobx 等工具是怎么做的")]),v._v(" "),l("li",[v._v("单页应用是什么?路由是如何实现的")]),v._v(" "),l("li",[v._v("如何进行 SEO 优化")])]),v._v(" "),l("p",[v._v("如果你使用到了小程序,还可能会问到:")]),v._v(" "),l("ul",[l("li",[v._v("小程序和 H5 有什么不一样,为什么选小程序而不是 H5")]),v._v(" "),l("li",[v._v("有考虑在小程序里嵌 H5 实现吗,为什么")]),v._v(" "),l("li",[v._v("为什么小程序的性能要好一些")]),v._v(" "),l("li",[v._v("小程序开发有用到哪些框架吗、为什么")])]),v._v(" "),l("p",[v._v("而工具库相关的就太多啦,一般会这么问:")]),v._v(" "),l("ul",[l("li",[v._v("有实际使用过哪些第三方库")]),v._v(" "),l("li",[v._v("这些工具库有什么特性和优缺点")])]),v._v(" "),l("h3",{attrs:{id:"node-js"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#node-js"}},[v._v("#")]),v._v(" Node.js")]),v._v(" "),l("p",[v._v("Node.js 相关的可能包括:")]),v._v(" "),l("ul",[l("li",[v._v("为什么要用 Node.js(而不是 PHP/JAVA/GO/C++ 等)")]),v._v(" "),l("li",[v._v("Node.js 有哪些特点,单线程的优势和缺点是什么")]),v._v(" "),l("li",[v._v("Node.js 有哪些定时功能")]),v._v(" "),l("li",[v._v("Process.nextTick 和 setImmediate 的区别")]),v._v(" "),l("li",[v._v("Node.js 中的异步和同步怎么理解,异步流程如何控制")]),v._v(" "),l("li",[v._v("简单介绍一下 Node.js 中的核心内置类库(事件,流,文件,网络等)")]),v._v(" "),l("li",[v._v("express 是如何从一个中间件执行到下一个中间件的")]),v._v(" "),l("li",[v._v("express、koa、egg 之间的区别")]),v._v(" "),l("li",[v._v("Rest API 有使用过吗,介绍一下")])]),v._v(" "),l("h3",{attrs:{id:"前端工程化"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#前端工程化"}},[v._v("#")]),v._v(" 前端工程化")]),v._v(" "),l("p",[v._v("如今前端工程化的趋势越来越重,通常从脚手架开始:")]),v._v(" "),l("ul",[l("li",[v._v("为什么我们开发的时候要使用脚手架")]),v._v(" "),l("li",[v._v("如何理解模块化")]),v._v(" "),l("li",[v._v("为什么要使用 Webpack,它和 Gulp 的区别是")]),v._v(" "),l("li",[v._v("讲一下 Webpack 中常用的一些配置、Loader、插件")]),v._v(" "),l("li",[v._v("Babel 的作用是什么,如何选择合适的 Babel 版本")]),v._v(" "),l("li",[v._v("Webpack 是怎么将多个文件打包成一个,依赖问题如何解决")]),v._v(" "),l("li",[v._v("CSS 文件打包过程中,如何避免 CSS 全局污染")]),v._v(" "),l("li",[v._v("本地开发和代码打包的流程分别是怎样的")])]),v._v(" "),l("p",[v._v("除了脚手架相关,如今自动化、流程化的使用也越来越多了:")]),v._v(" "),l("ul",[l("li",[v._v("你们的项目有使用 CI/CD 吗,为什么")]),v._v(" "),l("li",[v._v("你们的代码有写单元测试/自动化测试吗,为什么")])]),v._v(" "),l("h3",{attrs:{id:"性能优化"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#性能优化"}},[v._v("#")]),v._v(" 性能优化")]),v._v(" "),l("p",[v._v("性能优化的其实跟项目比较相关,常见的包括:")]),v._v(" "),l("ul",[l("li",[v._v("常见的性能优化包括哪些内容")]),v._v(" "),l("li",[v._v("如何理解性能瓶颈")]),v._v(" "),l("li",[v._v("图片加载性能有哪些可以优化的地方")]),v._v(" "),l("li",[v._v("要怎么做好代码分割")]),v._v(" "),l("li",[v._v("首屏页面加载很慢,要怎么优化")]),v._v(" "),l("li",[v._v("Tree Shaking 是怎样一个过程")]),v._v(" "),l("li",[v._v("页面有没有做什么柔性降级的处理")])]),v._v(" "),l("h3",{attrs:{id:"效能提升"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#效能提升"}},[v._v("#")]),v._v(" 效能提升")]),v._v(" "),l("p",[v._v("效能提升的意识在工作中很重要,大家都不喜欢低效的加班。通常可能问到的问题包括:")]),v._v(" "),l("ul",[l("li",[v._v("做了很多的管理端/H5,有考虑过怎么提升开发效率吗")]),v._v(" "),l("li",[v._v("你的项目里,有没有哪些工作是可以用工具完成的")]),v._v(" "),l("li",[v._v("项目中有进行组件和公共库的封装吗")]),v._v(" "),l("li",[v._v("如何管理这些公共组件/工具的兼容问题")]),v._v(" "),l("li",[v._v("日常工作中,如何提升自己的工作效率")])]),v._v(" "),l("h3",{attrs:{id:"监控与灰度发布"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#监控与灰度发布"}},[v._v("#")]),v._v(" 监控与灰度发布")]),v._v(" "),l("p",[v._v("发布和监控这部分,可能较大的业务才会有,涉及的问题可以有:")]),v._v(" "),l("ul",[l("li",[v._v("日常开发过程中,怎么保证页面质量")]),v._v(" "),l("li",[v._v("版本发布有进行灰度吗?灰度的过程是怎样的")]),v._v(" "),l("li",[v._v("版本发布过程中,如何及时地发现问题")]),v._v(" "),l("li",[v._v("发生异常,要怎么快速地定位到具体位置")]),v._v(" "),l("li",[v._v("如何观察线上代码的运行质量")])]),v._v(" "),l("h3",{attrs:{id:"多人协作"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#多人协作"}},[v._v("#")]),v._v(" 多人协作")]),v._v(" "),l("p",[v._v("一些较大的项目,通常由多个开发合作完成。而多人协作的经验也很有帮助:")]),v._v(" "),l("ul",[l("li",[v._v("多人开发过程中,代码冲突如何解决")]),v._v(" "),l("li",[v._v("介绍一下 git flow 流程")]),v._v(" "),l("li",[v._v("如果项目频繁交接,如果提升开发效率")]),v._v(" "),l("li",[v._v("有遇到代码习惯差异的问题吗,如何解决")]),v._v(" "),l("li",[v._v("有哪些常用的代码校验的工具")]),v._v(" "),l("li",[v._v("怎么强制进行 Code Review")])]),v._v(" "),l("h2",{attrs:{id:"结束语"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),l("p",[v._v("看到这么多内容不要慌,一般来说面试官只会根据你的工作经历来询问对应的问题,所以如果你并没有完全掌握某一块的内容,请不要写在简历上,你永远也不知道面试官会延伸到哪。")])])}),[],!1,null,null,null);_.default=a.exports}}]);
--------------------------------------------------------------------------------
/assets/js/177.e7fc794f.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[177],{740:function(v,_,t){"use strict";t.r(_);var a=t(69),p=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("工作也有些年了,虽然常常换工作,但实际上我也很希望有一份热爱又持久的工作。")]),v._v(" "),t("h2",{attrs:{id:"换工作的魔咒"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#换工作的魔咒"}},[v._v("#")]),v._v(" 换工作的魔咒?")]),v._v(" "),t("p",[v._v("总觉得自己身上有一个职业魔咒:每年都得换一次工作/工作环境。")]),v._v(" "),t("p",[v._v("如果算上部门内的主动调整,该魔咒的的确确从未被打破,从 2014 年开始一直到现在。")]),v._v(" "),t("p",[v._v("我常常在想,到底是自身的问题呢,还是命中注定?毕竟换了不少工作,的的确确还是要从自己身上找找问题。")]),v._v(" "),t("h2",{attrs:{id:"是什么击退了你的热情"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#是什么击退了你的热情"}},[v._v("#")]),v._v(" 是什么击退了你的热情?")]),v._v(" "),t("p",[v._v("我是一个热爱自由的人,说得好听点就是自我意识很强,说得不好听,就是“自私”、“自我”、“任性”。")]),v._v(" "),t("p",[v._v("一些大佬们对我的评价常常是:能力很强,很特别,但不好管理。作为一个打工人,我的自我意识有些过剩。团队对每一位成员都有所要求,领导们也会对大家有所期待,同样的,我也会对所在的团队、对领导们有反向的要求和期待。")]),v._v(" "),t("p",[v._v("因此,磨合是我常常会遇到的问题。而实际上,工作后遇到的人基本上也都有各自的价值观和思考方式了,即使再怎么尝试,一些观念和目标也无法达成一致。")]),v._v(" "),t("p",[v._v("我最喜欢的状态是,一个团队内的成员,都有一致的目标,共同往一个方向努力。实际上,规模越大的团队,就越难达成这样的状态。当合作、相处的人开始各自有了自己的打算,竞争意识已经超过了合作意识,相互之间开始抢功劳、甩锅,甚至仅仅是目标的不一致,都会导致工作上的不顺畅。")]),v._v(" "),t("p",[v._v("有句老话:将爱好当做工作,最终会丢掉这个爱好。")]),v._v(" "),t("p",[v._v("实际上,工作中最初拥有的热情,都是在各种磨合、冲突、矛盾中一点点失去的。直到我们失望了,热情消退了,便“仅仅把工作当做是维持生活的手段”。工作逐渐难以有所期待,于是我们期盼着生活里能有所改变,成家和生育便是一种不错的选择。")]),v._v(" "),t("p",[v._v("我也是凭着爱好成为开发,让我疲惫的往往不是写代码本身,而是工作中遇到难以磨合的问题。常常产生矛盾的点在于,每个人的取舍并不一致。举个例子,我认为技术最终是服务于产品和用户的,产品的体验、用户的感受很重要。而对于很多人来说,关注技术和 KPI 会对职业发展更重要。这是一个常见的矛盾点,虽然这样的矛盾并不存在高低之分、也不存在对与错,但它会影响到我们工作中对各个事情的优先级划分。")]),v._v(" "),t("p",[v._v("最常见的,莫过于我们常常需要主动 push 其他人,来协作完成我们需要的一些事情。因为在对方眼里,很可能有对他来说更重要的事情要做。")]),v._v(" "),t("p",[v._v("这本来是一件很普通、很小的事情,但夹杂着个人情绪、特殊场景之后,就容易被放大。我们都知道相互理解和尊重是沟通的基础,但工作中能做到的人并不多,所以我们常常会跟别人产生各种冲突和矛盾,这会导致我们慢慢对工作失去了耐心和热情。")]),v._v(" "),t("h3",{attrs:{id:"为何无法专注做一件事"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#为何无法专注做一件事"}},[v._v("#")]),v._v(" 为何无法专注做一件事?")]),v._v(" "),t("p",[v._v("全神贯注写代码的时候很开心,但每天能专注写代码的时间却很少,因为一份工作对我们的要求远不止如此。我们要有 owner 意识,要主动发现问题和解决问题,主动思考如何优化。")]),v._v(" "),t("p",[v._v("随着职级的上升,对开发要求中,代码实现往往占比越来越小。推动方案落地,常常需要多方配合,需要不少的沟通成本;推动项目按照预期运行,又涉及各种风险把控、多方的进度管控。开发需要一专多长,方案调研、项目管理、风险管控、沟通能力、推动能力,一个本只需认真思考更优实现的人,被要求将精力分散到各个地方。")]),v._v(" "),t("p",[v._v("对于研发群体来说,适合全方面发展的人本就少之又少,因此大多数的人,对涉及沟通的事情觉得疲惫和无趣。而多线程处理事情,对于很多人来说都难以做好,常常会顾此失彼,因此对于大多数研发来说,更适合给予某个领域让其负责。")]),v._v(" "),t("p",[v._v("理想情况下,大多数的研发只需要负责好自己手上的事情,少部分热爱又擅长沟通的人将其管理和串联起来,便是较和谐高效的协作方式。")]),v._v(" "),t("p",[v._v("至于如今大多数工作都对我们有了更多的要求,主要原因大概是:")]),v._v(" "),t("ol",[t("li",[v._v("有挑战、需要完全专注和具备技术不可替代性,这样的工作内容很少。")]),v._v(" "),t("li",[v._v("僧多粥少,工作内容的简单性,直接提升了开发人员的可替代性。")]),v._v(" "),t("li",[v._v("为了提升竞争力,因此个人、团队都对普遍群体有了更多的要求。")])]),v._v(" "),t("p",[v._v("因此,专注地做自己热爱的事情,对于大部分的人来说,这样的工作选择少之又少。相比之下,选择提升个人竞争力、获得更好的待遇,才是大多数人的选择。")]),v._v(" "),t("h3",{attrs:{id:"怎样的团队能做出好的产品"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#怎样的团队能做出好的产品"}},[v._v("#")]),v._v(" 怎样的团队能做出好的产品?")]),v._v(" "),t("p",[v._v("我最近常常在思考,导致团队目标割裂、难以做出团队成员自身也认可的产品的原因,到底在哪里?")]),v._v(" "),t("p",[v._v("从团队规模来看,小团队的确更容易比大团队达到目标的一致性,也更容易各司其职地专注自身的领域,齐心协力产品获得更好的体验。由于团队的一致性对每个团队成员都有要求,因此小团队可通过严格把控成员质量来达成,比如选择目标一致的成员。而在大团队里,不可避免地产生利益分歧,从而导致人人自危。")]),v._v(" "),t("p",[v._v("而在如今大多数的大公司体制下,产品的成败与个人的关系有所割裂。产品做得好,并不意味着团队里的每个人都有理想的收益;产品做得差,也并不是团队中的每个人都需要承担结果。")]),v._v(" "),t("p",[v._v("很多公司都有绩效考核与职级晋升两种激励体系,这两种激励是与团队成员有最直接利益关系。因为开发的工作往往无法量化,都是由直接或间接上级给出结果,因此,KPI 工程、PPT 工程、汇报氛围也一并形成。相比于思考把产品做好,替领导解决问题、提升自己在团队的影响力、建立自身的技术壁垒,更容易获得直接的收益,而后者并不一定需要把产品做好。")]),v._v(" "),t("p",[v._v("因此,在这样的环境下,大部分团队成员并不在意产品是否能获得成功,也很少认为自己会与该产品生死与共。底层抢着去做更容易晋升、更容易拿到好的考核的工作内容,上层也在思考怎样扩大团队规模、也给团队提供更多的机会,而产品是否做得好,或许只有该产品的初创者、拥护者会在乎,而他们往往又难以在这样的氛围内坚持下来。")]),v._v(" "),t("p",[v._v("所以,经历了好些快速扩招的团队之后,我对选择迅速扩招的团队有着极高的警惕。但事实上只要在这样的体制内,只要产品的发展看起来比较顺利,或是还有机会,团队扩招显然无法避免。")]),v._v(" "),t("p",[v._v("从这样的角度来看,产品发展得顺利,也不知是祸是福。")]),v._v(" "),t("h3",{attrs:{id:"能动性和惰性的平衡"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#能动性和惰性的平衡"}},[v._v("#")]),v._v(" 能动性和惰性的平衡?")]),v._v(" "),t("p",[v._v("团队对成员是有所要求的,这样的要求有时候会推动其成长,有时候却也会抑制其能动性。")]),v._v(" "),t("p",[v._v("我们常常说,具体情况具体分析。对于不同性格、脾气、追求的人,找到其闪光点并提供让其发挥出能力的环境,这对管理者有极高的要求。而大多数管理者都难以做到,因此会选择更加简单的管理方式,抹平团队差异性,通过培训、考试以及粗暴的横向对比、优胜劣汰,统一团队倾向和能力,批量产出高可替代性的团队成员。")]),v._v(" "),t("p",[v._v("大多数的产品开发,很多时候都是用常规的方式和套路便可,并不需要能力突出的团队成员。而对于追求极致和需要突破的产品来说,则需要个性更强的成员来推动产品发展。")]),v._v(" "),t("p",[v._v("如果想成为管理者,那么很大程度上要接受自己并不能完美地面面俱到,更多时候还是需要学习使用简单粗暴的管理方式,来维护和管理团队。")]),v._v(" "),t("p",[v._v("对于团队管理来说如此,那么对于团队的个人呢?")]),v._v(" "),t("p",[v._v("作为团队的成员,是愿意适应被抹平个人的差异性,被团队推动着,努力提升自身的短板不被淘汰呢?还是更希望突显个人的优势,积极主动地发挥自身的能动性呢?这或许是我一直都在思考的问题。")]),v._v(" "),t("p",[v._v("而个人的优势究竟在哪,也是很多人一直在寻找的答案。")]),v._v(" "),t("p",[v._v("千里马常有,而伯乐不常有。但我也常问自己,千里马一定要有伯乐才能成为千里马吗?又或者,千里马的伯乐,不可以是千里马自己吗?我们也可以给自己多一点的信心,给自己多一些的机会。")]),v._v(" "),t("h3",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("一直以来,我的心里有一个想象中理想的团队,这也是我不断更换工作环境的原因。")]),v._v(" "),t("p",[v._v("理想的团队极难寻得,且即使找到,团队依然在不断变化,团队里的人也在不断变化。曾经有好几次,最初加入的团队环境和氛围都十分喜欢,但随着产品不断发展,团队在扩招过程发生了许多事情,最终原先喜欢的那种感觉早已不在,热爱不再。")]),v._v(" "),t("p",[v._v("我也常常在想,团队规模的调整,的确不可避免地给团队带来一些冲击,那么,团队在调整之后是否能与原先达成一个平衡呢?还是说原有的能动性、开放性、突显的个性是否都难以共存呢?我还在观望。")]),v._v(" "),t("p",[v._v("理想的团队,到底会存在吗?")]),v._v(" "),t("p",[v._v("真希望有一天能打破这一年一换的魔咒呀。")])])}),[],!1,null,null,null);_.default=p.exports}}]);
--------------------------------------------------------------------------------
/assets/js/116.5ca5becb.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[116],{678:function(t,_,a){"use strict";a.r(_);var v=a(69),r=Object(v.a)({},(function(){var t=this,_=t.$createElement,a=t._self._c||_;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("p",[t._v("前面"),a("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-render-and-collect.html"}},[t._v("《收集与渲染》")]),t._v("一文中,我们简单提到说在一些复杂场景下,从服务端获取的数据还需要进行计算,比如依赖 Web 浏览器的计算,亦或是游戏引擎中的碰撞检测。")],1),t._v(" "),a("p",[t._v("本文我们详细针对复杂计算的场景来考虑渲染引擎的优化。")]),t._v(" "),a("h2",{attrs:{id:"渲染引擎完整的数据流向"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#渲染引擎完整的数据流向"}},[t._v("#")]),t._v(" 渲染引擎完整的数据流向")]),t._v(" "),a("p",[t._v("对于需要进行较复杂计算的渲染场景,结合收集和渲染的架构设计,我们完整的渲染流程大概应该是这样的:")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/render-engine-calculate-1.jpg",alt:""}})]),t._v(" "),a("p",[t._v("可见,完整的渲染流程里,计算的复杂程度会直接影响渲染是否及时,最终影响到用户的交互体验。在这里,我们还是以在线表格为例子,详细介绍下为什么有如此大的计算任务。")]),t._v(" "),a("h3",{attrs:{id:"渲染引擎为什么需要计算"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#渲染引擎为什么需要计算"}},[t._v("#")]),t._v(" 渲染引擎为什么需要计算")]),t._v(" "),a("p",[t._v("在表格中,画布绘制所需的数据,并不能完全从数据层中获取得到。对于以下一些情况,需要经过渲染引擎的计算处理才能正确绘制到画布上,包括:")]),t._v(" "),a("h4",{attrs:{id:"_1-分行-换行计算-计算范围-格子"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-分行-换行计算-计算范围-格子"}},[t._v("#")]),t._v(" 1. 分行/换行计算(计算范围:格子)")]),t._v(" "),a("p",[t._v("如下图,当单元格设置了自动换行,当格子内容超过一行会被自动换到下一行。由于内容宽度的测量依赖浏览器环境,因此也是需要在渲染引擎进行计算的:")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/render-splitter-calculate-2.jpg",alt:""}})]),t._v(" "),a("h4",{attrs:{id:"_2-行高计算-计算范围-整行"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2-行高计算-计算范围-整行"}},[t._v("#")]),t._v(" 2. 行高计算(计算范围:整行)")]),t._v(" "),a("p",[t._v("当某个行没有设置固定的行高时,该行内容的高度可能会存在被自动换行的单元格撑高的情况,因此真实渲染的行高也需要根据分行/换行结果进行计算。")]),t._v(" "),a("h4",{attrs:{id:"_3-覆盖格-隐藏格计算-计算范围-整行"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_3-覆盖格-隐藏格计算-计算范围-整行"}},[t._v("#")]),t._v(" 3. 覆盖格/隐藏格计算(计算范围:整行)")]),t._v(" "),a("p",[t._v("如下图,在没有设置自动换行的情况下,当单元格内容超出当前格子,会根据对齐的方向、该方向上的格子是否有内容,向对应的方向拓展内容,呈现向左右两边覆盖的情况:")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/render-splitter-calculate-1.jpg",alt:""}})]),t._v(" "),a("h4",{attrs:{id:"_4-边框线计算-计算范围-整行"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4-边框线计算-计算范围-整行"}},[t._v("#")]),t._v(" 4. 边框线计算(计算范围:整行)")]),t._v(" "),a("p",[t._v("受覆盖格影响,覆盖格和隐藏格(即被覆盖的格子)间的边框线会被超出的内容遮挡,因此对应的边框线也会受影响。")]),t._v(" "),a("p",[t._v("以调整列宽为例子,该操作涉及的计算包括:")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/render-splitter-calculate-3.jpg",alt:""}})]),t._v(" "),a("p",[t._v("可见,除了分行计算只涉及该列格子,一次列宽操作几乎涉及全表内容的计算,在大表下可能会导致几秒的卡顿,在一些低性能的机器上甚至会达到十几秒。由于该过程为同步计算,网页会表现为无响应,甚至严重的情况下会弹窗提示。")]),t._v(" "),a("h2",{attrs:{id:"计算过程优化"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#计算过程优化"}},[t._v("#")]),t._v(" 计算过程优化")]),t._v(" "),a("p",[t._v("之前我在"),a("RouterLink",{attrs:{to:"/front-end-basic/performance/front-end-performance-no-responding.html"}},[t._v("《前端性能优化--卡顿篇》")]),t._v("一文中,有详细介绍对于大任务计算的优化方向,包括:")],1),t._v(" "),a("ol",[a("li",[t._v("赋值和取值的优化。")]),t._v(" "),a("li",[t._v("优化计算性能和内存,包括使用享元的方式来优化数据存储,减少内存占用\n及时地清理不用的资源,避免内存泄露等问题。")]),t._v(" "),a("li",[t._v("计算大任务进行拆解。")]),t._v(" "),a("li",[t._v("引入其他技术来加快计算过程,比如 Web Worker、WebAssembly、AOT 技术等。")])]),t._v(" "),a("p",[t._v("对于较大型的前端应用,即使并非使用 Canvas 自行排版,依然可能会面临计算耗时过大的计算任务。当然,更合理的方式是将这些计算放在后台进行,直接将计算完的结果给到前端使用。")]),t._v(" "),a("p",[t._v("也有一些场景,尤其是前端与用户交互很重的情况下,比如游戏和重编辑的产品。这类产品无法将计算任务放置在后端,甚至无法将计算任务拆分到 Web Worker 进行计算,因为请求的等待耗时、Worker 的通信耗时都会影响用户的体验。")]),t._v(" "),a("p",[t._v("对该类产品,最简单又实用的方法便是:拆。")]),t._v(" "),a("h3",{attrs:{id:"将计算任务做拆分"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#将计算任务做拆分"}},[t._v("#")]),t._v(" 将计算任务做拆分")]),t._v(" "),a("p",[t._v("将计算任务做拆分,我们可以结合计算场景做分析,比如:")]),t._v(" "),a("ul",[a("li",[t._v("只加载和计算最少的资源,比如首屏的数据")]),t._v(" "),a("li",[t._v("只进行可视范围内的计算和渲染更新,在可视区域外的则做异步计算或是暂不计算")]),t._v(" "),a("li",[t._v("支持增量计算和渲染,即仅变更的局部内容的重新计算和渲染")]),t._v(" "),a("li",[t._v("支持降级计算,对计算任务做优先级拆分,在用户机器性能差的情况下考虑降级渲染,根据优先顺序先后计算和绘制")]),t._v(" "),a("li",[t._v("设计任务调度器,对计算任务做拆分,并设计优先级进行调度")])]),t._v(" "),a("p",[t._v("比如,React16 中新增了调度器(Scheduler),调度器能够把可中断的任务切片处理,能够调整优先级,重置并复用任务。")]),t._v(" "),a("p",[t._v("调度器会根据任务的优先级去分配各自的过期时间,在过期时间之前按照优先级执行任务,可以在不影响用户体验的情况下去进行计算和更新。")]),t._v(" "),a("p",[t._v("通过这样的方式,React 可在浏览器空闲的时候进行调度并执行任务。")]),t._v(" "),a("h3",{attrs:{id:"预计算-异步计算"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#预计算-异步计算"}},[t._v("#")]),t._v(" 预计算/异步计算")]),t._v(" "),a("p",[t._v("还有一种同样常见的方式,便是将计算任务进行拆分后,通过预判用户行为,提前执行将用到的计算任务。")]),t._v(" "),a("p",[t._v("举个例子,当前屏幕内的数据都已计算和渲染完毕,页面加载处于空闲时,可以提前将下一屏幕的资源获取,并进行计算。")]),t._v(" "),a("p",[t._v("这种预计算和渲染的方式,有些场景下也会称之为离屏渲染。离屏渲染同样可以作用于 Canvas 绘制过程,比如使用两个 Canvas 进行交替绘制,或是使用 worker 以及浏览器提供的 OffscreenCanvas API,提前将要渲染的内容计算并渲染好,等用户进入下一屏的时候可以直接拿来使用。")]),t._v(" "),a("p",[t._v("如果是页面滚动的场景,还可以考虑复用滚动过程中重复的部分内容,来节省待计算和渲染的任务数量。")]),t._v(" "),a("p",[t._v("这些方案,我们后面都会详细进行一一讨论,本文就不过多描述了。")]),t._v(" "),a("h2",{attrs:{id:"结束语"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[t._v("#")]),t._v(" 结束语")]),t._v(" "),a("p",[t._v("或许很多开发同学都会觉得,以前没有接触过大型的前端项目,或是重交互重计算的产品,如果遇到了自己不知道该怎么做优化。")]),t._v(" "),a("p",[t._v("实际上,大多数的优化思路都是相似的,但是我们需要尝试跨越模板,将其应用在不同的场景下,你就会发现能得到许多想象以外的优化效果。")]),t._v(" "),a("p",[t._v("纸上得来终觉浅,绝知此事要躬行。不要自己把自己局限住了哟~")])])}),[],!1,null,null,null);_.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/193.2bc14220.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[193],{755:function(_,v,t){"use strict";t.r(v);var a=t(69),r=Object(a.a)({},(function(){var _=this,v=_.$createElement,t=_._self._c||v;return t("ContentSlotsDistributor",{attrs:{"slot-key":_.$parent.slotKey}},[t("p",[_._v("这些年也有不少的面试别人和面试自己的经历,也有好些人来咨询一些前端的面试题目和准备,所以整理一下记录下来。本文先从一些面试准备方面介绍,缓解下大家的情绪先。\n")]),_._v(" "),t("p",[_._v("通常来说,找工作是一件需要严肃认真对待的事情,它像是职业路上的某个十字路口,一旦走错了可能荒废掉一段光阴。虽然现在习惯跳槽的年轻人越来越多,但对大多数人来说,换工作并不是一件常见的事情,经验的不足常常导致我们对一些预期以外的事情手忙脚乱、犹豫不定。")]),_._v(" "),t("p",[_._v("本文分享本骚年这些年来面试别人和被人面试的一些经验积累,希望能给到各位求职人一些思考点。")]),_._v(" "),t("h2",{attrs:{id:"面试准备"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#面试准备"}},[_._v("#")]),_._v(" 面试准备")]),_._v(" "),t("hr"),_._v(" "),t("p",[_._v("面试也好、答辩也好、分享也罢,我们做任何事情,做好准备是很重要的。在这里,我将面试的准备分成三方面来讲述:心理准备、简历准备和知识准备。")]),_._v(" "),t("h3",{attrs:{id:"_1-心理准备"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-心理准备"}},[_._v("#")]),_._v(" 1. 心理准备")]),_._v(" "),t("p",[_._v("其实只要给予充分的时间准备,以及每次面试后进行复盘和总结,我们总能取得最终的胜利。但更多的时候,我们陷入犹豫的困境,常常是因为我们没有做好心理准备。")]),_._v(" "),t("p",[_._v("为什么要换工作?这是每个求职者必须要思考的事情,这个问题常常也会被面试官问起。关于这个问题,可以分为内心的答案和表述的答案两种。")]),_._v(" "),t("p",[_._v("首先是内心的答案。求职者千千万,不同的人有不同的缘由。有些人想要了解外界的情况,有些人想要保持自身的能动性,但大多数的人换工作的原有无非是钱少、工作强度不适合、团队氛围不喜欢、工作内容不感兴趣等。通常来说,我们对当前的工作不满意的因素,会成为我们寻找下一份工作的关注点。如果没有进行足够的思考,我们会陷入换一份工作依然常常不如预期的困境,而频繁跳槽也容易使个人的职业发展受限。")]),_._v(" "),t("p",[_._v("生活常常不如所愿,更何况一份工作。我们遇到喜欢又如意的工作的概率微乎其微,所以常常需要寻找平衡点。钱多的工作常常强度较大,而适合自己的团队氛围更是难找,所以我们需要做取舍,以及在一定范围内调整自身的心态和预期。这些问题其实平时也可常常思考,这样我们对自身的需求和意愿有较好的认识,对于现有的工作也可以及时地进行调整和适应,而不是让负能量反复积累,或许便不会陷入困境了。")]),_._v(" "),t("p",[_._v("接下来是表述的答案。为什么有两种答案呢,因为面试官在考察求职者能力的同时也会考察对方的其他品质,求职者希望能获得一份理想的工作,面试官同样也想要招来能力强、负责任又稳定的人。我们换工作的理由本应该是积极的,例如寻求更大的挑战、更感兴趣的工作内容等,但很多时候我们也的确处于各种各样的工作中,像强制性非必要的高强度加班、团队氛围浮夸虚假、组织管理低效不够人性化等。")]),_._v(" "),t("p",[_._v("我们常常迫于管理上的一些原因更换工作,这些本不应如此、却又无可奈何的问题阻碍了自身的发展和追求,但管理的问题几乎无法避免,所以无法改变的问题可不必再说。职场中常常要求我们带着解决方案来提出问题,所以我们可以用更积极的回答来参与面试,从自身的职业发展角度来描述,而不是执着于上份工作存在的问题。")]),_._v(" "),t("h3",{attrs:{id:"_2-简历准备"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-简历准备"}},[_._v("#")]),_._v(" 2. 简历准备")]),_._v(" "),t("p",[_._v("简历准备也是面试准备过程中不可缺少的部分。")]),_._v(" "),t("p",[_._v("首先是工作内容和技能的关键字。大多数求职过程都是通过投递到HR手中开始,而HR小伙伴们对于专业技能并不了解,所以面试官常常会给到HR一些筛选简历的关键点,例如需要掌握哪些技能、有怎样的项目经验等。我们可以通过网上搜索相应的职位要求,相应地调整简历中的关键字。")]),_._v(" "),t("p",[_._v("其次是简洁、清晰的工作经验和技能描述。过于臃肿、排版混乱的简历会让面试官较为烦躁,而一目了然的简历会使得面试官心情舒畅。一些比较久远又无法体现个人能力的工作经验,可以省略或概括性描述。所有出现在简历上的内容都需要有足够的把握,避免出现面试官问到又回答不上的尴尬情况,这种情况下面试官会认为简历上的内容并不完全是对方的真实工作内容。")]),_._v(" "),t("p",[_._v("接下来要突出简历的亮点。项目经验不是越多越好,尽量突出描述有挑战性、体现个人能力的内容。除了专业技能,也可以表现出项目过程中的主动性和思考。")]),_._v(" "),t("p",[_._v("补充内容。可以针对自身的优势进行一些补充说明,体现个人的能力或是潜力,例如业界认可的一些沉淀、成就。如果你长得特别帅或者漂亮,把照片贴上也不是不可以的。")]),_._v(" "),t("h3",{attrs:{id:"_3-知识准备"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3-知识准备"}},[_._v("#")]),_._v(" 3. 知识准备")]),_._v(" "),t("p",[_._v("具体的知识准备会在后续篇章详细描述,这里只做简单介绍。")]),_._v(" "),t("p",[_._v("首先是刷题。互联网发达的今天,不少人会将自己面试大公司的过程记录下来,同时也常常附有相关的问题和答案,可以作为参考进行同类型题目的准备。")]),_._v(" "),t("p",[_._v("理解取代背诵。我们都是经历过高考的孩子,背东西对我们来说只要花时间就可以解决。但过于僵硬的回答会让面试官失去耐心。如果在记忆的过程中加于理解,结合个人的项目经验来复述,会有更好的效果。")]),_._v(" "),t("h2",{attrs:{id:"面试的技巧"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#面试的技巧"}},[_._v("#")]),_._v(" 面试的技巧")]),_._v(" "),t("hr"),_._v(" "),t("h3",{attrs:{id:"自信"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#自信"}},[_._v("#")]),_._v(" 自信")]),_._v(" "),t("p",[_._v("自信很重要,如果我们连自己都不相信自己,面试官又如何会觉得你可靠呢?")]),_._v(" "),t("p",[_._v("自信的培养其实更多在日常生活和工作中,并不是临时准备就可以拥有的。但充分的准备工作也可以一定程度上提升我们的自信心,如果面试官问到的问题都在预期中,当然也可以自信地表达出来了。")]),_._v(" "),t("h3",{attrs:{id:"真诚"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#真诚"}},[_._v("#")]),_._v(" 真诚")]),_._v(" "),t("p",[_._v("真诚地表达自己,个人觉得是面试中最重要的部分。")]),_._v(" "),t("p",[_._v("我们可以从网上或是书本里学到一些面试技巧,也可以通过各种刷题来获得一些“最优”答案。但如果我们通过掩饰自我而获得了一份工作,这份工作常常会不持久或是不如意。因为面试官看中的是我们“装潢”出来的品质,而不是真实的我们。工作几乎占用我们生命的三分之一时间甚至更多,选择一个真正接纳自己的环境会舒适更多,也更能发挥个人的能力。")]),_._v(" "),t("p",[_._v("除此之外,专业技能方面若是问到了不懂的内容,诚实地表达出我不会、比强行解释会给面试官留有更好的印象。不懂可以学,但不懂装懂的行为可能会导致工作中或大或小的失误,是不大可取的。当然,你也可以在说出我不会的时候,补充一句我可以学,也可以捡回一两分。")]),_._v(" "),t("h3",{attrs:{id:"主动"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#主动"}},[_._v("#")]),_._v(" 主动")]),_._v(" "),t("p",[_._v("很多时候我们会觉得面试过程中自己是处于被动的一方,但其实找工作是双向选择的过程。")]),_._v(" "),t("p",[_._v("面试官常常要看不少的简历,也需要面试很多人,时间长了会容易疲惫,有时候也不再主动寻找对方的亮点。对求职者来说,适当的主动表现是加分项。我们花费了很多的时间准备,如果面试过程连自身的优势都没有问到就结束了,那我们也永远无法知道自己是否真的匹配不上这个职位。")]),_._v(" "),t("p",[_._v("在回答问题过程中,可以主动地将话题牵向更能体现个人优势的地方。很多时候,面试官也会问你最擅长或者觉得有挑战性的一些工作内容,也可以做好充足的准备来回答。而主动进行自我补充,有时候也可以带来一些额外的机会。有一次面试过程中,一些比较基础但工作中接触不到的问题我都没答上来,我说只是没准备好,要是准备好了肯定没问题,后来面试官给了一周的准备时间,最终也顺利通过面试。")]),_._v(" "),t("p",[_._v("另外,我们换工作也有一定的预期,如果这些预期无法从网上或是其他渠道获取,可以在面试的时候主动关注,例如团队氛围、工作内容、工作强度、团队定位等等。既然要换工作,我们当然要尽可能找到一份适合自己的。")]),_._v(" "),t("h3",{attrs:{id:"复盘"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#复盘"}},[_._v("#")]),_._v(" 复盘")]),_._v(" "),t("p",[_._v("大多数人都会在前面几次面试的时候摔跤,但每一次面试后做好复盘是很重要的。")]),_._v(" "),t("p",[_._v("对于同样的职位,我们在这个面试过程被问到的问题,出现在其他面试的概率很大。所以面试结束后,不管顺利与否,都可以简单记录一下面试过程和相关问题,思考下是否有更好的解决方案、是否有优化的空间。我常常说面试过程会成长很多,也是通过不断地总结、反思、以及二次学习得到的收获。")]),_._v(" "),t("p",[_._v("除了没有回答好的一些问题以外,我们也可以在面试结束后,让面试官给出一些建议。我们都好为人师,这是大多数面试官都不会拒绝的要求,同时还会给对方留下一定的印象。而面试官给到的一些建议,常常是他们考察候选人的点,我们可以以此来更新对自身的认知。")]),_._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[_._v("#")]),_._v(" 结束语")]),_._v(" "),t("p",[_._v("找工作真的不容易呀,很久以前我就有写下面试过程的想法,不过一直由于各种事情(懒)而没完成。如今恰好有好些人问起,就一并歇了吧。")]),_._v(" "),t("p",[_._v("祝各位仕途顺利,越战越勇呀~")])])}),[],!1,null,null,null);v.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/23.0a513d2e.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[23],{584:function(a,r,e){"use strict";e.r(r);var t=e(69),v=Object(t.a)({},(function(){var a=this,r=a.$createElement,e=a._self._c||r;return e("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[e("p",[a._v("作为“为大型前端项目”而设计的前端框架,Angular 其实有许多值得参考和学习的设计,本系列主要用于研究这些设计和功能的实现原理。本文作为背景和铺垫,先简单以我自身的了解来介绍一下 Angular 这个框架把吧。")]),a._v(" "),e("h2",{attrs:{id:"前端框架"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#前端框架"}},[a._v("#")]),a._v(" 前端框架")]),a._v(" "),e("h3",{attrs:{id:"三大前端-框架"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#三大前端-框架"}},[a._v("#")]),a._v(" 三大前端“框架”")]),a._v(" "),e("p",[a._v("虽然这几年前端的发展和变化十分迅猛,但被公认为前端“框架”的 Top3 位置却没有什么变化,依然是 Angular/React/Vue。")]),a._v(" "),e("p",[a._v("其中,React/Vue 专注于构建用户界面,在一定程度上来说为一个 Javascript 库;而 Angular 则提供了前端项目开发中较完整的解决方案。我们可以简单粗暴地这样认为:")]),a._v(" "),e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[a._v("Angular = React/Vue + 路由库(react-router/vue-router) + 状态管理(Redux/Flux/Mobx/Vuex) + 脚手架/构建(create-react-app/Vue CLI/Webpack) + ...\n")])])]),e("h3",{attrs:{id:"低热度的-angular"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#低热度的-angular"}},[a._v("#")]),a._v(" 低热度的 Angular")]),a._v(" "),e("p",[a._v("作为三大前端框架之一,Angular 在国内的热度实在是低。个人认为原因包括:")]),a._v(" "),e("ul",[e("li",[a._v("AngularJS 到 Angular2 的断崖式升级,失去了很多开发者的信任")]),a._v(" "),e("li",[a._v("Angular 除了依赖注入(Provider/Service)、指令/管道等功能设计,在断崖升级时引入的 Typescript/Decorator/模块化组织/AOT/JIT 等新的能力,对当时大多数前端开发带来了不少的学习门槛")]),a._v(" "),e("li",[a._v("Angular 针对大型应用而引入的设计和功能,对于大多数前端应用来说无法物尽其用,反而增加了学习成本")]),a._v(" "),e("li",[a._v("Angular 提供了一整套完整的解决方案,反而不像 Vue/React 等库灵活,可以随意搭配其他的状态管理、构建库等(其实也是可以的,可能成本和门槛会高一些),显得笨重")])]),a._v(" "),e("p",[a._v("由于以上原因,大家在选框架的时候常常讨论的是用 React 还是 Vue,虽然同样作为热门框架,Angular 似乎在无形中就被大家排除在外。使用 Angular 框架的人越少,相关的中文相关的文档和教程也会很少,大家对它的了解和认知都容易不够全面。")]),a._v(" "),e("p",[a._v("其实,很多人会问我喜欢 React 还是 Vue,我总会告诉他们我喜欢 Angular,他们也总会觉得很奇怪哈哈哈哈。实际上,AngularJS 是我最早接触的一个前端框架,我也曾经使用过断崖式升级的 Angular2,它们带给我的除了很多未知的知识和领域,还有拓宽了我对前端编程的一些认知。我想,喜欢 Angular,也可能是因为有一些缘分在内的原因叭~")]),a._v(" "),e("h3",{attrs:{id:"我对-angular-的理解"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#我对-angular-的理解"}},[a._v("#")]),a._v(" 我对 Angular 的理解")]),a._v(" "),e("p",[a._v("Angular2 的出现时间大概在 2017 年前后,那会 React 的函数式编程正开始受到很多人追捧,而 Vue 也开始进入大家的视野中。但 Angular2 带来的技术和设计很是前卫,以至于对很多人来说门槛太高。")]),a._v(" "),e("p",[a._v("但是,如今 2021 年了,我们再来回看一下,Angular 框架中使用到的很多技术和设计,都渐渐地被更多的人在使用了。")]),a._v(" "),e("p",[a._v("其中最火的便是 Typescript,显然,如今对大多数前端开发来说,Typescript 都是需要掌握的。在 Angular 之后,React、Vue 也都支持了 Typescript,Vue3 更是直接使用 Typescript 来重构了。")]),a._v(" "),e("p",[a._v("除此之外,模块化、AOT/JIT、依赖注入等设计,以及 Rxjs 、元数据("),e("code",[a._v("Reflect.metadata")]),a._v(")的引入等,也被更多的产品和工具库使用。当然,这里并不是说这些产品和工具是参考了 Angular。Angular 中的这些设计理念,大多数也并不是由 Angular 第一个提出的,但 Angular 大概是第一个在前端框架中(甚至是在前端领域中)将它们毫无违和感地一起引入并使用的。")]),a._v(" "),e("p",[a._v("这样的趋势,我认为很大的原因是前端应用的规模在不断变大,这也是因为前端的技术栈在不断拓展,负责的领域也逐渐在扩大。前端应用慢慢地变得复杂,比如 VsCode 编辑器、在线文档编辑这些,项目本身的复杂度和规模都不小,而这样大型的项目里肯定需要往模块化组织的方向发展,那么想必各个模块间的依赖耦合会很严重,因此依赖注入便是一个很好的方式来管理,VsCode 便是这样做的。")]),a._v(" "),e("p",[a._v("当然,即使在未来,大型项目在所有前端项目中的占比肯定也不至于很高,但大型项目如何设计和管理这块领域对前端来说依然比较陌生。我们可以借助常见的后台系统架构设计来进行参考和反思,比如微服务、领域驱动设计、职责驱动设计等。但这些终究是设计思想,如何才能很好地落地,对前端开发都是不小的考验。")]),a._v(" "),e("p",[a._v("我接触过各式各样的项目,而当这些项目在面对项目的规模变大的时候,虽然新人的加入、每个人都按照自己的想法去开发,最终总会变得难以维护,历史债务十分严重。而 Angular 则是唯一一个能限制开发的自由发挥的,可以让经验不足和经验丰富的开发都写出一样易维护的代码。")]),a._v(" "),e("p",[a._v("回归主题,既然 Angular 提供了大型前端应用的完整解决方案,那么我们不妨多些对它的学习和了解,当我们真正遇到问题的时候,便多了一个可落地方案的参考,这也是为什么我们要不断汲取新知识和技术的原因。")]),a._v(" "),e("h2",{attrs:{id:"angular-框架解读"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#angular-框架解读"}},[a._v("#")]),a._v(" Angular 框架解读")]),a._v(" "),e("p",[a._v("源码阅读对很多人来说,都是一件挑战很大的事情,对我来说也一样。")]),a._v(" "),e("p",[a._v("虽然我有较多地阅读过 Vue 的源码(可参考"),e("a",{attrs:{href:"http://www.godbasin.com/vue-ebook/",target:"_blank",rel:"noopener noreferrer"}},[a._v("《深入理解 Vue.js 实战》"),e("OutboundLink")],1),a._v("这本书),但前提是我对这个框架有足够多的理解和使用经验,在尝试介绍和解说时,也更是倾向使用者的角度来进行。而对于 React,则是因为理解和使用经验的缺乏,未曾有机会深入地去了解它,但也有阅读过写得不错的源码解读(可参考"),e("a",{attrs:{href:"https://github.com/BetaSu/just-react",target:"_blank",rel:"noopener noreferrer"}},[a._v("《React 技术揭秘》"),e("OutboundLink")],1),a._v("一书)。")]),a._v(" "),e("p",[a._v("对于阅读源码来说,最好的方式便是从已知的理解和认知中开始。阅读源码,并不是为了熟悉掌握源码本身,更是为了掌握其中的一些值得借鉴的思考方式和设计,因此我也尽可能减少代码占据的篇幅,使用自己理解后的方式来进行描述。")]),a._v(" "),e("p",[a._v("那么,后面我会从自己认为最值得参考和学习的地方开始,一点点学习其中的精华。以我当前有限的认知,大概会包括以下内容:")]),a._v(" "),e("ul",[e("li",[a._v("依赖注入整体框架的设计")]),a._v(" "),e("li",[a._v("组件设计与管理")]),a._v(" "),e("li",[a._v("Provider 与 Service 的设计")]),a._v(" "),e("li",[a._v("NgModule 模块化组织(多级/分层)的设计")]),a._v(" "),e("li",[a._v("模板引擎/模板编译过程的整体设计")]),a._v(" "),e("li",[a._v("Zone 设计:提升计算速度")]),a._v(" "),e("li",[a._v("JIT/AOT 设计")]),a._v(" "),e("li",[a._v("元数据设计:("),e("code",[a._v("Reflect.metadata")]),a._v(")的引入和使用思考")]),a._v(" "),e("li",[a._v("响应式编程:Rxjs 的引入和使用思考")])]),a._v(" "),e("p",[a._v("在时隔 3 年之后再次接触 Angular,还是直接以阅读源码的方式来进行,对我来说是个不小的挑战。但这些年来,我也一直在尝试做各种不同的新的事情,如果因为觉得困难而放弃,那么这个天花板便是我自身,而不是什么“环境所迫”、“没有时间”这样的借口。或许我会写得很慢,但我依然希望自己能一点点去细细钻研,也希望至少以上的内容能最终掌握。")]),a._v(" "),e("p",[a._v("这是一个大工程,因为我写下这篇文章来给自己预热,也希望能打打气,更是尝试立下一个 FLAG 吧。")]),a._v(" "),e("h2",{attrs:{id:"结束语"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[a._v("#")]),a._v(" 结束语")]),a._v(" "),e("p",[a._v("这是一篇从我个人的理解出发的文章,同样的这个系列也会以我一个人这样局限的角度来出发进行介绍。因此它们或许可能存在片面和局限的时候,但我希望即使是这样的内容,也能给你们带来一些思考和收获。")]),a._v(" "),e("p",[a._v("分享和交流,并不是为了各自的理由而争执,而是为了弥补自己看不到的一面,共同进步,不是吗?")])])}),[],!1,null,null,null);r.default=v.exports}}]);
--------------------------------------------------------------------------------
/assets/js/232.891426ab.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[232],{794:function(v,t,_){"use strict";_.r(t);var e=_(69),a=Object(e.a)({},(function(){var v=this,t=v.$createElement,_=v._self._c||t;return _("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[_("p",[v._v("大家坐稳,我要开始装逼了。说好要讲小程序系列,替埋头苦干低调做事的开发哥哥们多讲讲小程序的故事吧。")]),v._v(" "),_("h1",{attrs:{id:"小程序与-webview"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#小程序与-webview"}},[v._v("#")]),v._v(" 小程序与 WebView")]),v._v(" "),_("h2",{attrs:{id:"webview-的飞速发展"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#webview-的飞速发展"}},[v._v("#")]),v._v(" WebView 的飞速发展")]),v._v(" "),_("p",[v._v("随着公众号的出现和繁荣,WebView 的使用频率也越来越高。不少的企业或是小商家、外包公司开始做 H5 页面,各式各样的 H5 活动页、小商城、小测试、小游戏满天飞。H5 的劣势也很明显,体验太渣,甚至还不能获取很多底层 APP 拥有的功能。")]),v._v(" "),_("p",[v._v("微信团队也因此提供了一些 JS-SDK 给 Web 开发使用,包括拍摄、录音、语音识别、二维码、地图、支付、分享等能力。从此,Web 开发者可以使用到微信的原生能力,去完成一些之前做不到或者难以做到的事情。")]),v._v(" "),_("h2",{attrs:{id:"体验不足的-webview"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#体验不足的-webview"}},[v._v("#")]),v._v(" 体验不足的 WebView")]),v._v(" "),_("p",[v._v("相信大多数人都经历过这样的糟糕体验——白屏。没错,就是这货:")]),v._v(" "),_("p",[_("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1535165451%281%29.png",alt:"iamge"}})]),v._v(" "),_("p",[v._v("这样的体验直到今天还会出现,有些甚至连加载中的字样都没有。打开一个 WebView 通常会经历以下几个阶段:")]),v._v(" "),_("ul",[_("li",[v._v("交互无反馈")]),v._v(" "),_("li",[v._v("到达新的页面,页面白屏")]),v._v(" "),_("li",[v._v("页面基本框架出现,但是没有数据;页面处于 loading 状态")]),v._v(" "),_("li",[v._v("出现所需的数据")])]),v._v(" "),_("p",[_("strong",[v._v("除了白屏,影响 Web 体验的问题还有缺少操作的反馈,主要表现在两个方面:页面切换的生硬和点击的迟滞感。")])]),v._v(" "),_("p",[v._v("如果从程序上观察,WebView 启动过程大概分为以下几个阶段:\n"),_("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/time.png",alt:"iamge"}})]),v._v(" "),_("p",[v._v("对于一些有经验的 Web 开发者而言,会使用一些 SPA 的框架,来模拟客户端原生的页面切换过渡,同时使用缓存、CSS 反馈交互、直出页面等技术,来改善体验。")]),v._v(" "),_("p",[v._v("但并不是所有开发者都有精力和能力去做这么多优化,而作为一个平台,优化用户体验也是平台责任的一部分。")]),v._v(" "),_("h2",{attrs:{id:"小程序的出现"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#小程序的出现"}},[v._v("#")]),v._v(" 小程序的出现")]),v._v(" "),_("p",[v._v("曾经有较长一段时间,我都以为小程序是类似 Weex、React Native 这些框架一样,最终呈现的是原生应用。直到开始写小程序,一些疑惑开始不断冒出来:为什么 CSS 样式的编写跟普通 Web 几乎一摸一样呢?")]),v._v(" "),_("p",[v._v("才发现,小程序最终的呈现是 WebView,同时夹杂着一些原生组件。所以,小程序到底做了什么,使得体验比正常 WebView 好太多呢?")]),v._v(" "),_("p",[v._v("我们先来对比下,常见的 "),_("strong",[v._v("Web 和 Native 的区别")]),v._v(":")]),v._v(" "),_("table",[_("thead",[_("tr",[_("th",[v._v("-")]),v._v(" "),_("th",[v._v("Native")]),v._v(" "),_("th",[v._v("Web")]),v._v(" "),_("th",[v._v("期望")])])]),v._v(" "),_("tbody",[_("tr",[_("td",[v._v("开发门槛")]),v._v(" "),_("td",[v._v("高")]),v._v(" "),_("td",[v._v("低")]),v._v(" "),_("td",[v._v("低")])]),v._v(" "),_("tr",[_("td",[v._v("体验")]),v._v(" "),_("td",[v._v("好")]),v._v(" "),_("td",[v._v("白屏、交互反馈差")]),v._v(" "),_("td",[v._v("接近原生体验")])]),v._v(" "),_("tr",[_("td",[v._v("版本更新")]),v._v(" "),_("td",[v._v("需审核,迭代慢")]),v._v(" "),_("td",[v._v("在线更新")]),v._v(" "),_("td",[v._v("在线更新")])]),v._v(" "),_("tr",[_("td",[v._v("管控性")]),v._v(" "),_("td",[v._v("平台可管控")]),v._v(" "),_("td",[v._v("难管控")]),v._v(" "),_("td",[v._v("可管控")])])])]),v._v(" "),_("p",[v._v("关于最后一点的管控性,其实作为一个平台这是必须具备的能力,不然平台被滥用,对公众或是平台自身都不是什么好事情。就像现在很多云服务的 COS,也开始管控起来,包括域名绑定、备案等,一是防止有心人士使用平台做些不好的事情,二是对平台自身的保护(防止被封禁等)。")]),v._v(" "),_("p",[v._v("在这样的需求和期待中,小程序诞生了。")]),v._v(" "),_("h2",{attrs:{id:"小程序是期望的产物"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#小程序是期望的产物"}},[v._v("#")]),v._v(" 小程序是期望的产物")]),v._v(" "),_("p",[_("strong",[v._v("使用 WebView 开发,门槛低,可云端更新。")])]),v._v(" "),_("p",[v._v("不同于 RN、Weex 这些框架,原生组件的开发、样式调整等都和 Web 有太多的不同。对于一个前端开发来说,开发成本较高、调试效率低,若不小心掉到坑里,都不知道该怎么爬出来。")]),v._v(" "),_("p",[v._v("使用 WebView,可最大化前端开发的优势,同时异步加载的方式,也允许开发者进行在线的版本更新和 BUG 修复。")]),v._v(" "),_("p",[_("strong",[v._v("通过提供基础能力、原生组件结合等方式,提升用户体验。")])]),v._v(" "),_("p",[v._v("小程序框架提供了完整的基础库,通过微信内置基础库、双线程渲染等方式,提升了小程序启动的体验。同时,开发者可以借用原生组件、API 等能力,做很多普通页面开发做不到的事情,用户也能以此获得原生应用般的体验。")]),v._v(" "),_("p",[_("strong",[v._v("通过平台发布、审核、下架、封禁等能力,具备对小程序的管控能力。")])]),v._v(" "),_("p",[v._v("小程序框架提供了云端更新的能力,通过代码上传、审核等方式,增强了对开发者的管控能力。保护用户的同时,也保护了平台,以及平台中的其他开发者。")]),v._v(" "),_("p",[_("strong",[v._v("双线程(逻辑层和渲染层分开),隔离 DOM、BOM 能力,提升体验的同时,可保证 WebView 安全性。")])]),v._v(" "),_("p",[v._v("双线程的模式,使得页面渲染和逻辑代码的加载分开,降低了页面卡壳的可能性。")]),v._v(" "),_("p",[v._v("同时,由于逻辑层被隔离 DOM 和 BOM 对象,无法获取渲染层的内容,也在一定程度上保护了用户的数据安全。")]),v._v(" "),_("h2",{attrs:{id:"打开小程序的新世界"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#打开小程序的新世界"}},[v._v("#")]),v._v(" 打开小程序的新世界")]),v._v(" "),_("p",[v._v("二维码扫描、搜索、分享、推送等各种方式,都可以打开一个小程序。依靠轻量无需安装、体验优秀、管控严格、方便快捷等各种优势,小程序给用户打开了一个通向新世界的大门。")]),v._v(" "),_("p",[v._v("而对于开发者,开发成本低、能力齐全、可快速迭代这样的开发模式,又基于大体量的用户数,几乎是无法拒绝的。齐备的底层基础库、自带优化后的体验性能,使得开发者可以专注于业务逻辑的开发,各司其职,一起把产品做好。")]),v._v(" "),_("h2",{attrs:{id:"参考"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#参考"}},[v._v("#")]),v._v(" 参考")]),v._v(" "),_("ul",[_("li",[_("a",{attrs:{href:"https://tech.meituan.com/WebViewPerf.html",target:"_blank",rel:"noopener noreferrer"}},[v._v("《WebView 性能、体验分析与优化》"),_("OutboundLink")],1)]),v._v(" "),_("li",[_("a",{attrs:{href:"https://developers.weixin.qq.com/ebook?action=get_post_info&token=935589521&volumn=1&lang=zh_CN&book=miniprogram&docid=000668c6910b784b00860870a5ac0a",target:"_blank",rel:"noopener noreferrer"}},[v._v("《小程序开发指南》"),_("OutboundLink")],1)])]),v._v(" "),_("h1",{attrs:{id:"结束语"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),_("p",[v._v("用心做好一个产品,在如今乱糟糟的社会里其实也不容易呢。而要做一个优秀的作品,需要有很清晰的方向,即使需要不断探索,也不会迷失方向吧。"),_("br"),v._v("\n技术什么的,只要方向正确,总是有办法解决的。后面章节我们也来讲讲小程序的底层框架和设计吧。")])])}),[],!1,null,null,null);t.default=a.exports}}]);
--------------------------------------------------------------------------------
/assets/js/124.94e50a0d.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[124],{685:function(v,_,t){"use strict";t.r(_);var a=t(69),r=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("如果说基础知识的掌握是起跑线,那么使大家之间拉开差距的更多是前端项目开发经验和技能。对于一个项目来说,从框架选型和搭建,到项目维护、工程化和自动化、多人协作等各个方面,都需要我们在参与项目中不断地思考和改进,积累经验。")]),v._v(" "),t("p",[v._v("本文将要介绍:")]),v._v(" "),t("ul",[t("li",[v._v("前端项目设计")]),v._v(" "),t("li",[v._v("前端项目管理")])]),v._v(" "),t("h2",{attrs:{id:"前端项目设计"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#前端项目设计"}},[v._v("#")]),v._v(" 前端项目设计")]),v._v(" "),t("p",[v._v("除了具体的前端领域知识以外,当我们开始负责起整个前端项目的管理时,需要具备一些方案选型、架构设计、项目瓶颈识别并解决等能力。")]),v._v(" "),t("h3",{attrs:{id:"前端项目搭建"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#前端项目搭建"}},[v._v("#")]),v._v(" 前端项目搭建")]),v._v(" "),t("p",[v._v("很多时候,我们的项目在刚搭建的时候规模会比较小,因此在项目启动阶段需要做简化,来保证项目能快速地上线。但从长期来看,一个项目还需要考虑到拓展性。换句话说,当项目开始变得较难维护的时候,我们就要进行一些架构或者流程上的调整。")]),v._v(" "),t("p",[v._v("在项目开始之前,我们需要做一系列的规划,像项目的定位(to B/C)、大小,像框架和工具的选型、项目和团队规范等,包括:")]),v._v(" "),t("ul",[t("li",[v._v("前端框架选择:基于团队成员偏好和能力,选择适合的前端框架")]),v._v(" "),t("li",[v._v("工具库选择:基于项目规模,选择是否需要路由管理、状态管理等工具库")]),v._v(" "),t("li",[v._v("自动化工具:基于成员规模和项目状态(快速上线、稳定维护等),选择是否需要代码构建、自动化测试等自动化工具,以及搭建持续集成、持续部署等自动化流程")]),v._v(" "),t("li",[v._v("项目流程规范:使用一致的项目规范,包括项目代码结构、代码规范、开发流程规范、多人协作规范等内容")])]),v._v(" "),t("p",[v._v("项目的维护永远是程序员的大头,多是“前人种树,后人乘凉”。但是很多时候,大家会为了一时的方便,对代码规范比较随意,就导致了我们经常看到有人讨论“继承来的代码”。")]),v._v(" "),t("p",[v._v("代码规范其实是团队合作中最重要的地方,使用一致的代码规范,会大大减少协作的时候被戳到的痛点。好的写码习惯很重要,包括友好的变量命名、适当的注释等,都会对代码的可读性有很大的提升。但是习惯是每个人都不一样,所以在此之上,我们需要有这样统一的代码规范。")]),v._v(" "),t("p",[v._v("一些工具可以很好地协助我们,像 Eslint 这样的工具,加上代码的打包工具、CI/CD 等流程的协助,可以把一些规范强行标准化,达到代码的统一性。还有像 prettier 这样的工具,可以自动在打包的时候帮我们进行代码规范的优化。")]),v._v(" "),t("p",[v._v("除了这些简单的命名规范、全等、单引双引等代码相关的规范,还有流程规范也一样重要。比如对代码进行 code review,尤其在改动公共库或是公共组件的时候。")]),v._v(" "),t("p",[v._v("最重要的还是多沟通。沟通是一个团队里必不可少、又很容易出问题的地方,我们要学会沟通和表达自己。")]),v._v(" "),t("h3",{attrs:{id:"洞察项目瓶颈"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#洞察项目瓶颈"}},[v._v("#")]),v._v(" 洞察项目瓶颈")]),v._v(" "),t("p",[v._v("我们常常会觉得自己做的项目没有什么意思,每天都是重复的工作、繁琐的业务逻辑、糟糕的历史遗留代码,反观其他人都在做有技术、有难度、有挑战性的工作,越会难以喜欢上自己负责的工作。")]),v._v(" "),t("p",[v._v("实际上,那些会让我们觉得枯燥、反复、杂乱的工作内容,更是可以去改善做好、并能从中获得成长的地方。涉及前端工作的业务,只有极少一部分的业务例如涉及多人协同的在线文档、或是用户量很大的业务如电商、直播、游戏等,这些业务重心可能会稍微倾向前端,更多时候前端真的会处于编写页面、最多就用 Node.js 写写接入层等状况。好的业务可遇不可求,我们在遇到这些业务之前,就什么都不做了吗?")]),v._v(" "),t("p",[v._v("大多数工作中,对开发的要求都不仅限于实现功能。如果只是编写代码,刚毕业的应届生花几周时间也一样能做到,那么我们的优势在哪里呢?洞察工作中的瓶颈,并有足够的能力去设计方案、排期开发、解决并复盘,这些技能更能突显我们在岗位上的价值和能力。对团队来说,更需要这样能主动发现并解决问题的成员,而不是安排什么就只做什么的螺丝钉。")]),v._v(" "),t("p",[v._v("一般来说,用户量较大的项目的瓶颈通常会在兼容性、性能优化这些方面;对于一次性的活动页面,挑战点存在于如何高效地完成一次活动页面的开发或者配置,通常会使用配置系统、结合拖拽以及所见即所得等方式来生成页面;对于经常开发各式各样的管理端系统,优化方向则在于怎么通过脚手架快速地生成需要的项目代码、如何快速地发布上线等。我们要做的,就是找到工作中让自己觉得烦躁和不爽的地方,然后去改进优化它们。")]),v._v(" "),t("h3",{attrs:{id:"方案调研与选型对比"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#方案调研与选型对比"}},[v._v("#")]),v._v(" 方案调研与选型对比")]),v._v(" "),t("p",[v._v("找到项目的痛点或是瓶颈后,就需要设计相应的方案去解决它们。而当我们需要投入人力和时间成本去做一件事,就需要面临一个问题:如何让团队认同这件事情、并愿意给到资源让我们去完成它?")]),v._v(" "),t("p",[v._v("可以通过前期的调研,找一些业界相对成熟的方案作为参考。如果有多套方案,则需要对这些方案进行分析比较。例如,小明最近需要针对项目进行自动化性能测试能力的支持,因为项目规模大、模块多、参与开发的成员也有几十人,经常因为一些不同模块的变更导致项目的性能下降却没法及时发现问题,往往是等到用户反馈或是某次开发、产品或者测试发现的时候才得知。")]),v._v(" "),t("h2",{attrs:{id:"前端项目管理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#前端项目管理"}},[v._v("#")]),v._v(" 前端项目管理")]),v._v(" "),t("p",[v._v("不同于做工具和框架、参与开源协同,很多时候我们写的都是业务代码。我们总认为只有做工具才会比较有意思、也有技术挑战,但是业务代码就没有可以提升技术、挑战自己的地方了吗?其实并不是,很多时候我们先入为主、认为业务代码写得再好也没用、自己放弃了去做这样的事情。多多思考,你会发现每个项目都可以大有可为,你的未来也可以大不一样。")]),v._v(" "),t("h3",{attrs:{id:"合理的分工排期"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#合理的分工排期"}},[v._v("#")]),v._v(" 合理的分工排期")]),v._v(" "),t("p",[v._v("很多开发在进行编码实现功能的时候,都直接想到哪写到哪,也常常会出现代码写到一半发现写不下去,结果导致重新调整实现,最终项目从预期的一周变成了一个月、迟迟上线不了的问题。")]),v._v(" "),t("p",[v._v("当我们确认好技术方案之后,可以针对实现细节拆分具体的功能模块,分别进行工作量的预估和分工排期。这一步骤在多人协作的时候是必不可少的,否则可能面临分工不明确、接口未对齐就匆忙开工、最终因为各种问题而返工这些问题。而对单人项目来说,也可以通过拆解功能模块这个过程来思考具体的实现方式,也能提前发现一些可能存在的问题,并相应地进行规避。")]),v._v(" "),t("p",[v._v("提供完整的工作量评估和时间表,我们可以比较有计划地进行开发,同时团队的其他人也可以了解我们的工作情况,有时候大家能给到一些建议,也能避免对方不了解我们到底在做什么而导致的一些误会。而排期预估的另外一个重要作用,则是通过时间线去严格约束我们的工作效率、及时发现问题,以及项目结束后可针对时间维度进行项目复盘。")]),v._v(" "),t("h3",{attrs:{id:"风险把控"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#风险把控"}},[v._v("#")]),v._v(" 风险把控")]),v._v(" "),t("p",[v._v("前面有说到,我们需要在参与项目的过程中具备 Owner 意识,即使这个项目并不是我们主导。风险把控则是作为 Owner 必须掌握的一个能力,我们需要确保项目能按照预期进行,则需要主动发现其中可能存在的风险并提前解决。")]),v._v(" "),t("p",[v._v("除了因为方案设计考虑不周而导致的一些返工风险,我们在项目进行过程中常常也会遇到依赖资源无法及时给到、依赖方因为种种原因无法按时支援、团队协作出现矛盾等各种问题,任何一块出现问题都可能导致整体的工期出现延误,这是我们不想出现的结果。因此,我们需要主动把控各个环节的情况,及时推动和解决出现的一些多方协作的问题。")]),v._v(" "),t("p",[v._v("通过前期准备的这些方案和工具,提前控制好一些可预见的风险,开发过程会更加顺利。但是如果我们的效果只有这些的话,很多时候是无法证明自己做了这么多事情的价值。那么,我们可以尝试用数据说话。")]),v._v(" "),t("h3",{attrs:{id:"及时反馈与复盘"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#及时反馈与复盘"}},[v._v("#")]),v._v(" 及时反馈与复盘")]),v._v(" "),t("p",[v._v("很多开发习惯了当代码开发完成、发布上线之后就结束了这个项目,其实他们遗漏了一个很重要的环节:复盘。通过复盘这种方式,我们可以发现自身的一些问题并改进,还可以让团队其他人以及管理者知道我们做了些什么,这是很重要的。")]),v._v(" "),t("p",[v._v("复盘的总结内容,可以通过邮件的方式发送给团队以及合作方,同时还可以作为自身的经验沉淀,后续更多项目中可以进行参考。如果使用得当,我们还可以通过这种方式来影响我们的团队和管理者,也是向上管理的一种方法。")]),v._v(" "),t("p",[v._v("但其实不只是工作中,我们生活里也可以常常进行反思和总结,这样我们的步伐才可以越跑越快。成长的过程中总会遇到各式各样的问题,有些问题被我们忽视而过,有些问题我们选择了逃避,但其实我们还可以通过迎面应战、解决并反思的方式,在这样一次次战斗中快速地成长。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("每一个程序员都希望自己成为一个优秀的开发,实际上每个人对优秀的定义都不大一样。作为前端开发,除了专业能力以外,工作中还需要良好的表达与沟通能力。")]),v._v(" "),t("p",[v._v("如果我们还想继续往上走,通用计算机能力、架构能力、项目管理等能力也都需要提升。")])])}),[],!1,null,null,null);_.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/182.8e9147a6.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[182],{744:function(_,v,t){"use strict";t.r(v);var a=t(69),r=Object(a.a)({},(function(){var _=this,v=_.$createElement,t=_._self._c||v;return t("ContentSlotsDistributor",{attrs:{"slot-key":_.$parent.slotKey}},[t("p",[_._v("写故事是突发奇想,所以要怎么架构整个系列的内容也没有特别好的想法。所以就从我认为比较重要的开始讲起吧,很多有趣的故事,都是和自己原则的制定和死守相关的~")]),_._v(" "),t("h1",{attrs:{id:"要成为怎样的人"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#要成为怎样的人"}},[_._v("#")]),_._v(" 要成为怎样的人")]),_._v(" "),t("p",[_._v("工作后遇到了很多事情,如果你要追求纯粹的技术和工作环境,其实都肯定遇到过类似的困扰。世界上最简单的事情,大概是别人告诉你要做什么、要怎么做,你只管做就好了。")]),_._v(" "),t("p",[_._v("而世界上最有意思的,往往是你做什么遇到了什么困难,但你坚持下来了。")]),_._v(" "),t("h2",{attrs:{id:"关于金钱的困惑"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#关于金钱的困惑"}},[_._v("#")]),_._v(" 关于金钱的困惑")]),_._v(" "),t("p",[_._v("其实从小课本就有教,钱乃身外之物,一个人最大的一笔财富在于他本身。我们学到的知识、经验,还有思考方式、精神力量,才是人生中真正的财富。")]),_._v(" "),t("p",[_._v("但这个社会又是很现实的,成功似乎越来越被和金钱挂上勾。现代世界,又有多少人会把坚韧而善良的内心、人与人间的真诚和信任、自身的成长和才华,当作一笔真正的财富呢?")]),_._v(" "),t("p",[_._v("我常常说,金钱它不重要。在我的认知当中,一份工作,我应该关注怎么把它做好,关注怎么给社会创造更大的价值。而我付出的该收获多少,是应该由公司和老板负责的。如果我一边干活还得一边担心自己得不到回报,又怎么能做出好的成绩呢?")]),_._v(" "),t("p",[_._v("当然,事实总不会如你所愿,大概是这个世界它也有叛逆期。")]),_._v(" "),t("h3",{attrs:{id:"钱很重要"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#钱很重要"}},[_._v("#")]),_._v(" 钱很重要")]),_._v(" "),t("p",[_._v("我第一份前端工作工资特别低,而转正的时候我跟老板说想要涨薪。老板破口大骂,说我竟然不懂得感恩,当初要不是他招了我,我怎么会有现在。")]),_._v(" "),t("p",[_._v("后台几位大哥哥跟我说,去看看其他公司的工作吧,不过要跟他们要到两倍工资噢。(毕竟我当时的工资的确是少的可怜,挤在一个26人住的房子,只有一个洗手间 T_T)")]),_._v(" "),t("p",[_._v("吭哧吭哧地找工作去了。技术面试过了,最终大佬面又到了谈工资阶段。大佬说,我可以给你想要的,但是这意味着我要去跟HR申请特殊流程。我说,如果需要特殊申请,那也希望你能去申请,我答应了几个后台哥们的,达不到我就继续跟他们干。")]),_._v(" "),t("p",[_._v("最后当然是给了 offer,当然大佬有些不愿意还是给我申请了,因为技术总监比较看好我(那会前端不好找)。")]),_._v(" "),t("p",[_._v("我还记得我跟老板提离职的时候,他先是说给我涨薪,见我已经下定决心之后,又开始破口大骂,说我不知感恩,还说什么深圳就这么大,你能走多远之类的。(说实话,当时毕业也才一年多,那瞬间真被吓到了)")]),_._v(" "),t("p",[_._v("这个世界就是这样子,弱肉强食是很正常的事情。就像每次换工作的时候,HR 都会根据你之前的工资来给你发 offer,虽然都未必如愿但你都屁颠屁颠地接受了。虽然经常说钱不重要,但是有些时候它却偏偏占比很重,因为别人会因为你赚钱多少,来评估你的话语权。")]),_._v(" "),t("h3",{attrs:{id:"但它不是你要追求的东西"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#但它不是你要追求的东西"}},[_._v("#")]),_._v(" 但它不是你要追求的东西")]),_._v(" "),t("p",[_._v("从前那些最珍贵的东西,爱和信任、美好的品质、真挚的感情、自我追求,它们都是无价的。而如今却渐渐被标上价格,用来换取金钱和利益,这大概是这个社会最遗憾的矛盾吧。")]),_._v(" "),t("p",[_._v("但正如《原则》一书所说:")]),_._v(" "),t("blockquote",[t("p",[_._v("要记住金钱的唯一目的是使你得到你想要的东西,所以要想好你所珍视的是什么,把它置于金钱之上。")])]),_._v(" "),t("p",[_._v("本末倒置,无数人都会掉进这个陷阱。我们有很多想要的东西,这其中有很多很多都是需要用钱的,然后我们很容易会以为自己要的是钱,而忘记了初衷。")]),_._v(" "),t("p",[_._v("关于金钱的原则,你要知道它只能是个附属品,在你追梦路上附带的,而不应该成为你在追求的东西,除非你只喜欢钱了哈哈(你真棒!)。")]),_._v(" "),t("p",[_._v("身边也很多朋友聊到生活的困难,很多人会经常说到同届的其他人待遇多好,赚钱多厉害。当然,我也会经常疑惑,这个社会衡量一个人的方式,应该是以财富的方式,但财富从什么时候,就真的只剩下金钱二字了呢。")]),_._v(" "),t("p",[_._v("如果说如今有什么能让我自己骄傲的地方,那大概是我一直都很清楚自己想要的是什么。金钱的确它能让我更加舒适地追求一些理想,但好在我没有把它当初一个终点。")]),_._v(" "),t("h2",{attrs:{id:"关于真诚的困惑"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#关于真诚的困惑"}},[_._v("#")]),_._v(" 关于真诚的困惑")]),_._v(" "),t("p",[_._v("我很喜欢真诚的对话,曾经也会为相互之间的争执而感到困惑。")]),_._v(" "),t("p",[_._v("在商汤的时候,也遇到了相似的情况。但是在大家放下情绪,各自梳理和分享观点之后,才会惊叹原来是这样的,换了角度来看待之后,突然拓展开的思维其实也很美妙。")]),_._v(" "),t("p",[_._v("从那时开始,对于吵架这件事,也多了不一样的看法。作为一名程序员,我们应该对开源分享有很好的体会。优秀的合集,能产生非常好的作品。思维和灵感也是,而这个过程一定是会产生摩擦的。")]),_._v(" "),t("h3",{attrs:{id:"环境很杂"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#环境很杂"}},[_._v("#")]),_._v(" 环境很杂")]),_._v(" "),t("p",[_._v("现在的工作环境里,很多人都喜欢说到“情商”这样的字眼。")]),_._v(" "),t("p",[_._v("大多数人的工作分为两部分,一是把事情做好,二是让别人看到你把事情做好了。其实这是很低效的方式,但却偏偏是大环境中真实的一面。")]),_._v(" "),t("p",[_._v("曾经我也会因为一些不同的想法,和导师起过一些争执。这对我来说是很正常的一件事,只要是不同的人就肯定有不一样的看法。而我也会在矛盾分析结束之后,询问导师的意见,问他这种沟通方式是否会让他不舒服,导师的回答是能有更好的观点产出就可以了。")]),_._v(" "),t("p",[_._v("然而接下来的事情让我有些措不及防。某一天,有个其他组的同事突然跟我说,让我要注意下平时跟导师的态度。我说为什么呢,被告知我组长跟别人说我不尊重导师。")]),_._v(" "),t("p",[_._v("这类的事情后面也屡次发生,关心我的人说让我适应下,因为每个地方都是这样的。而我最终也主动离开,换了个环境。他们说的也没错,即使换了环境,也会多多少少有这些事情。")]),_._v(" "),t("p",[_._v("我想了很久为什么呢?")]),_._v(" "),t("p",[_._v("大概是因为,并不是全部人都很胜任手上的工作。有些人的确是做得不够好,但他们还是要生活、要赚钱养家,只能通过别的方式来获得认同。踮脚效应便出现了,你要是不站起来,就没法看到前方。")]),_._v(" "),t("p",[_._v("组织要怎么把控这种效应,还是说分不清、认为这样也没关系,这不是我该苦恼的事情。我只要不断寻找,直到能找到理想的团队。志同道合的价值观十分之来之不易,但不能因为遇不到而强行改变自身的价值观。")]),_._v(" "),t("h3",{attrs:{id:"但坚持才会遇到你想要的环境"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#但坚持才会遇到你想要的环境"}},[_._v("#")]),_._v(" 但坚持才会遇到你想要的环境")]),_._v(" "),t("p",[_._v("我毕业才五年,说实话工作环境基本上一年一换(or more)。很多老同学一段时间没见,聊起来的时候说,被删你怎么又换了???")]),_._v(" "),t("p",[_._v("或许在一些人看来,频繁跳槽意味着这个人不够稳定,做事坚持不了很久。")]),_._v(" "),t("p",[_._v("但正好相反,换这么多的工作,却恰好是因为一直在坚持着一些事情。回顾这些年来,我能看到自己一步步往前走的轨迹,清晰地指向一个方向。")]),_._v(" "),t("p",[_._v("当然,一个环境不合适,就要马上离开吗?并不全是,当我在一点点成长的时候,总有一天,我可以把这一切扭转过来,变成我想要的一个环境。所以如果要问我有没有成功做到过,当然目前是还没有的,但我在尝试去做这些改变的时候,遇到了很多有趣的人,也沉淀了一些很棒的思考和想法。")]),_._v(" "),t("p",[_._v("工作是双向选择的过程,我们不用觉得自己是弱势的一方,也不必要觉得委屈求全。我们对人对事,相对于抱怨,应该用更直接和爽快的方式去正面面对。")]),_._v(" "),t("p",[_._v("如果你要问我,凭什么我说的这些就是对的呢?大概是上天比较照顾我,运气还不错的我,如今也终于遇到了一个很棒的团队。")]),_._v(" "),t("p",[_._v("如果你要问我,在自己喜欢的环境里工作是什么感受?那大概是每天乐呵呵地来,哼着歌地走,工作做完了在工位呆着想点别的事情也很开心,走路都几乎一蹦一跳的。")]),_._v(" "),t("h3",{attrs:{id:"面子是最不重要的东西"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#面子是最不重要的东西"}},[_._v("#")]),_._v(" 面子是最不重要的东西")]),_._v(" "),t("p",[_._v("我经常跟别人说,工作学会的最重要的一项技能,就是脸皮要厚。")]),_._v(" "),t("p",[_._v("刚毕业的时候,我也是个很要面子的人。被指责批评了,会觉得委屈或是难堪,也会介意面子,而很多的话说不出口,烂在肚子里。")]),_._v(" "),t("p",[_._v("开始工作之后,作为职场的小白菜,经常会遇到打脸的事情。一开始还会脸红,想找个洞钻进去。久而久之便发现,犯错是无法避免的,既然已经错了,就要让这个错误犯得更有价值。而要实现这一点的方法,就是好好反思改进的空间。")]),_._v(" "),t("p",[_._v("如果说丢一次脸,能收获一次成长,那其实也是某种意义上赢了。")]),_._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[_._v("#")]),_._v(" 结束语")]),_._v(" "),t("p",[_._v("在艺术的历史里,或许你们都发现了,很多的艺术家在世时都不曾被重视。这大概是生活的悖论吧,就像一个人只有成功了,他说的话才有人愿意听。但是仔细想想,历史也大多数是由幸存的人撰写的,所以说不定也是自然选择的一种。")])])}),[],!1,null,null,null);v.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/83.895a2366.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[83],{644:function(_,v,t){"use strict";t.r(v);var r=t(69),e=Object(r.a)({},(function(){var _=this,v=_.$createElement,t=_._self._c||v;return t("ContentSlotsDistributor",{attrs:{"slot-key":_.$parent.slotKey}},[t("p",[_._v("该篇主要整理网络协议相关的概念,包括 TCP/IP、DNS、HTTP 等等基础认识。")]),_._v(" "),t("h1",{attrs:{id:"网络协议"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#网络协议"}},[_._v("#")]),_._v(" 网络协议")]),_._v(" "),t("h2",{attrs:{id:"tcp-ip-协议"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#tcp-ip-协议"}},[_._v("#")]),_._v(" TCP/IP 协议")]),_._v(" "),t("p",[_._v("TCP/IP 提供点对点的链接机制,将数据应该如何封装、定址、传输、路由以及在目的地如何接收,都加以标准化。"),t("br"),_._v("\n它将软件通信过程抽象化为四个抽象层,采取协议堆栈的方式,分别实现出不同通信协议。协议套组下的各种协议,依其功能不同,被分别归属到这四个层次结构之中,常被视为是简化的七层 OSI 模型。"),t("br"),_._v("\n可参考"),t("a",{attrs:{href:"https://zh.wikipedia.org/zh-hans/TCP/IP%E5%8D%8F%E8%AE%AE%E6%97%8F",target:"_blank",rel:"noopener noreferrer"}},[_._v("Wiki-TCP/IP 协议族"),t("OutboundLink")],1),_._v("。")]),_._v(" "),t("ul",[t("li",[t("p",[_._v("TCP/IP 协议分层")]),_._v(" "),t("ul",[t("li",[_._v("数据链路层:用来处理连接网络的硬件、设备驱动、网卡、光纤等")]),_._v(" "),t("li",[_._v("网络层:用来处理在网络上滚动的数据包(选路线)")]),_._v(" "),t("li",[_._v("传输层:TCP/UDP")]),_._v(" "),t("li",[_._v("应用层:FTP/DNS 域名系统/HTTP 协议等")])])]),_._v(" "),t("li",[t("p",[_._v("IP 协议")]),_._v(" "),t("ul",[t("li",[_._v("把各种数据包准确无误地传递")]),_._v(" "),t("li",[_._v("ARP 惬意、RARP 协议等")])])]),_._v(" "),t("li",[t("p",[_._v("TCP 协议")]),_._v(" "),t("ul",[t("li",[_._v("可靠传输(对比 UDP):确认数据送达,把数据安全可靠传输")]),_._v(" "),t("li",[_._v("三次握手:建立一个 TCP 连接需要客户端和服务端总共发送三个包以确认连接存在")]),_._v(" "),t("li",[_._v("四次挥手\n"),t("blockquote",[t("p",[_._v("可以参考"),t("a",{attrs:{href:"http://www.cnblogs.com/zmlctt/p/3690998.html",target:"_blank",rel:"noopener noreferrer"}},[_._v("《TCP 三次握手四次挥手详解》"),t("OutboundLink")],1)])])])])])]),_._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/0_131271823564Rx.gif",alt:"images"}})]),_._v(" "),t("h2",{attrs:{id:"dns-域名系统"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#dns-域名系统"}},[_._v("#")]),_._v(" DNS 域名系统")]),_._v(" "),t("p",[_._v("让用户电脑和服务器(网页存放电脑)连接起来并不是靠域名进行,网络上计算机之间实现连接是通过每台计算机在网络中拥有的惟一的 IP 地址来完成的。"),t("br"),_._v('\nDNS 的全称是 Domain Name System。它负责把 FQDN(就是以"."分隔结尾的名字)翻译成一个 IP。')]),_._v(" "),t("p",[_._v("DNS 速度优化是网站优化 SEO 中的一部分,优化 DNS 解析就必须了解 DNS 解析原理及过程。")]),_._v(" "),t("p",[_._v("当用户访问我们网站一个网页时,他需要经过以下步骤:")]),_._v(" "),t("ol",[t("li",[_._v("找到这个网页的存放服务器;")]),_._v(" "),t("li",[_._v("服务器将用户的请求信息接入;")]),_._v(" "),t("li",[_._v("服务器通过文件路径(URL)查找用户请求网页;")]),_._v(" "),t("li",[_._v("用户将该网页内容下载到自己电脑上。")])]),_._v(" "),t("p",[_._v("我们所讲的 DNS 解析主要是第一个步骤,即让用户通过 URL 找到文件存放的服务器。")]),_._v(" "),t("p",[_._v("DNS 解析主要有递归查询,就是在某个 DNS 服务器缓存中查找不到相应的域名与 IP 地址对应关系时,自动跳转到到下一步骤通过下一个 DNS 服务器进行查找。")]),_._v(" "),t("ul",[t("li",[_._v("参考\n"),t("ul",[t("li",[t("a",{attrs:{href:"http://www.ecdoer.com/post/dns.html",target:"_blank",rel:"noopener noreferrer"}},[_._v("《DNS 解析过程原理【深入浅出详解】》"),t("OutboundLink")],1)]),_._v(" "),t("li",[t("a",{attrs:{href:"http://www.jianshu.com/p/4394aaf97492",target:"_blank",rel:"noopener noreferrer"}},[_._v("《DNS 服务原理详解》"),t("OutboundLink")],1)])])])]),_._v(" "),t("h2",{attrs:{id:"http-协议"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#http-协议"}},[_._v("#")]),_._v(" HTTP 协议")]),_._v(" "),t("p",[_._v("关于 HTTP 协议相关的,需要了解和学习的太多太多了,这里本骚年先大概讲讲相关的,后面有空再进行深入分享。")]),_._v(" "),t("p",[_._v('通常,由 HTTP 客户端发起一个请求,建立一个到服务器指定端口(默认是 80 端口)的 TCP 连接。HTTP 服务器则在那个端口监听客户端发送过来的请求。一旦收到请求,服务器(向客户端)发回一个状态行,比如"HTTP/1.1 200 OK",和(响应的)消息,消息的消息体可能是请求的文件、错误消息、或者其它一些信息。'),t("br"),_._v("\nHTTP 使用 TCP 而不是 UDP 的原因在于(打开)一个网页必须传送很多数据,而 TCP 协议提供传输控制,按顺序组织数据,和错误纠正。")]),_._v(" "),t("p",[_._v("HTTP 协议的主要特点可概括如下:")]),_._v(" "),t("ol",[t("li",[_._v("支持客户/服务器模式。")]),_._v(" "),t("li",[_._v("简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有 GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于 HTTP 协议简单,使得 HTTP 服务器的程序规模小,因而通信速度很快。")]),_._v(" "),t("li",[_._v("灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记。")]),_._v(" "),t("li",[_._v("无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。")]),_._v(" "),t("li",[_._v("无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。")])]),_._v(" "),t("p",[_._v("具体的请求、相应等等,后面一章会讲解,这里就大概讲个理解吧。"),t("br"),_._v("\n这里有篇超详细的讲解:"),t("a",{attrs:{href:"http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html",target:"_blank",rel:"noopener noreferrer"}},[_._v("《HTTP 协议详解(真的很经典)》"),t("OutboundLink")],1),_._v("。")]),_._v(" "),t("h2",{attrs:{id:"代理与反代理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#代理与反代理"}},[_._v("#")]),_._v(" 代理与反代理")]),_._v(" "),t("ul",[t("li",[t("p",[_._v("代理")]),_._v(" "),t("ul",[t("li",[_._v("用户希望代理服务器帮助自己,和要访问服务器通信,为了实现此目标,需要以下工作:\n"),t("ul",[t("li",[_._v("用户 IP 报文的目的 IP = 代理服务器 IP")]),_._v(" "),t("li",[_._v("用户报文端口号 = 代理服务器监听端口号")]),_._v(" "),t("li",[_._v("HTTP 消息里的 URL 要提供服务器的链接")])])]),_._v(" "),t("li",[_._v("代理服务器可以根据以上链接与服务器直接通信")]),_._v(" "),t("li",[_._v("服务器返回网页")]),_._v(" "),t("li",[_._v("代理服务器打包网页,返回用户")])])]),_._v(" "),t("li",[t("p",[_._v("反向代理")]),_._v(" "),t("ul",[t("li",[_._v("反向代理的实现\n"),t("ul",[t("li",[_._v("需要有一个负载均衡设备来分发用户请求,将用户请求分发到空闲的服务器上")]),_._v(" "),t("li",[_._v("服务器返回自己的服务到负载均衡设备")]),_._v(" "),t("li",[_._v("负载均衡将服务器的服务返回用户\n"),t("blockquote",[t("p",[_._v("在计算机世界里,由于单个服务器的处理客户端(用户)请求能力有一个极限,当用户的接入请求蜂拥而入时,会造成服务器忙不过来的局面,可以使用多个服务器来共同分担成千上万的用户请求,这些服务器提供相同的服务,对于用户来说,根本感觉不到任何差别。")])])])])])])]),_._v(" "),t("li",[t("p",[_._v("参考自"),t("a",{attrs:{href:"https://www.zhihu.com/question/24723688",target:"_blank",rel:"noopener noreferrer"}},[_._v("知乎的回答-反向代理为何叫反向代理?"),t("OutboundLink")],1)])])]),_._v(" "),t("h1",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[_._v("#")]),_._v(" 结束语")]),_._v(" "),t("p",[_._v("这里面只讲述最简单的一些概念和认识,作为前端,http 相关的还是需要深入理解的呢,后面我们也会一点点补充讲述一下。")])])}),[],!1,null,null,null);v.default=e.exports}}]);
--------------------------------------------------------------------------------
/assets/js/233.b59df4de.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[233],{795:function(v,_,t){"use strict";t.r(_);var a=t(69),e=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("前端的框架太多让人眼花缭乱,很多相似的地方,优秀的地方大家都会借鉴,同时又会有各自的一些特点。小程序也好,其他框架也好,理解他们的设计缘由、实现原理,还是能学到很多很多东西的。")]),v._v(" "),t("h1",{attrs:{id:"一切始于双线程"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#一切始于双线程"}},[v._v("#")]),v._v(" 一切始于双线程")]),v._v(" "),t("h2",{attrs:{id:"技术选型"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#技术选型"}},[v._v("#")]),v._v(" 技术选型")]),v._v(" "),t("p",[v._v("上一节"),t("RouterLink",{attrs:{to:"/wxapp/wxapp-principle/1-wxapp-generate.html"}},[v._v("《小程序的诞生》")]),v._v("中,我们也提到了小程序的双线程设计。")],1),v._v(" "),t("p",[v._v("目前来说,"),t("strong",[v._v("页面渲染的方式主要有三种")]),v._v(":")]),v._v(" "),t("ol",[t("li",[v._v("Web 渲染。")]),v._v(" "),t("li",[v._v("Native 原生渲染。")]),v._v(" "),t("li",[v._v("Web 与 Native 两者掺杂,也即我们常说的 Hybrid 渲染。")])]),v._v(" "),t("p",[v._v("前面也说过,小程序最终的呈现形式,是 WebView + 原生组件,Hybrid 方式。我们结合之前对小程序的期望来看:")]),v._v(" "),t("ul",[t("li",[v._v("开发门槛:Web 门槛低,不过 Native 也有像 RN 这样的框架支持")]),v._v(" "),t("li",[v._v("体验:Native 体验比 Web 不要好太多,Hybrid 在一定程度上比 Web 接近原生体验")]),v._v(" "),t("li",[v._v("版本更新:Web 支持在线更新,Native 则需要打包到微信一起审核发布")]),v._v(" "),t("li",[v._v("管控和安全:Web 可跳转或是改变页面内容,存在一些不可控因素和安全风险")])]),v._v(" "),t("p",[v._v("由于小程序的宿主是微信,如果用纯客户端原生技术来编写小程序 ,那小程序代码需要与微信代码一起编包,跟随微信发版本,这种方式跟开发节奏必然都是不对的。\n所以方向应该是需要像 Web 技术那样,有一份随时可更新的资源包放在云端,通过下载到本地,动态执行后即可渲染出界面。")]),v._v(" "),t("p",[v._v("如果用纯 Web 技术来渲染小程序,在一些有复杂交互的页面上可能会面临一些性能问题。\n这是因为在 Web 技术中,UI 渲染跟 JavaScript 的脚本执行都在一个单线程中执行,这就容易导致一些逻辑任务抢占 UI 渲染的资源。")]),v._v(" "),t("p",[v._v("总地看来,小程序选择了 Hybrid 的渲染方式,可以用一种近似 Web 的方式来开发,并且还可以实现在线更新代码。同时,引入原生组件有以下好处:")]),v._v(" "),t("ul",[t("li",[v._v("扩展 Web 的能力。比如像输入框组件(input, textarea)有更好地控制键盘的能力")]),v._v(" "),t("li",[v._v("体验更好,同时也减轻 WebView 的渲染工作")]),v._v(" "),t("li",[v._v("绕过 setData、数据通信和重渲染流程,使渲染性能更好")])]),v._v(" "),t("p",[v._v("现在,我们还剩下一个很重要的问题:管控性和安全性。于是,双线程的设计被提出来了。")]),v._v(" "),t("h2",{attrs:{id:"双线程的小程序"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#双线程的小程序"}},[v._v("#")]),v._v(" 双线程的小程序")]),v._v(" "),t("p",[v._v("也不知道是哪位大佬,能想出双线程这样的模型,反正我是佩服得 666 的。")]),v._v(" "),t("p",[v._v("双线程是什么?我们先来看个官方的图:\n"),t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/%E4%B8%8B%E8%BD%BD.png",alt:"image"}})]),v._v(" "),t("p",[t("strong",[v._v("小程序的渲染层和逻辑层分别由 2 个线程管理:渲染层的界面使用了 WebView 进行渲染,逻辑层采用 JsCore 线程运行 JS 脚本。")])]),v._v(" "),t("p",[v._v("为什么要这么设计呢?前面提到的管控和安全,为了解决这些问题,我们需要阻止开发者使用一些浏览器提供的,诸如跳转页面、操作 DOM、动态执行脚本的开放性接口。")]),v._v(" "),t("p",[v._v("我们可以使用客户端系统的 JavaScript 引擎,iOS 下的 JavaScriptCore 框架,安卓下腾讯 x5 内核提供的 JsCore 环境。通过提供一个沙箱环境来运行开发者的 JavaScript 代码来解决。这个沙箱环境只提供纯 JavaScript 的解释执行环境,没有任何浏览器相关接口。")]),v._v(" "),t("p",[v._v("这就是小程序双线程模型的由来:")]),v._v(" "),t("ul",[t("li",[t("strong",[v._v("逻辑层:创建一个单独的线程去执行 JavaScript,在这个环境下执行的都是有关小程序业务逻辑的代码")])]),v._v(" "),t("li",[t("strong",[v._v("渲染层:界面渲染相关的任务全都在 WebView 线程里执行,通过逻辑层代码去控制渲染哪些界面。一个小程序存在多个界面,所以渲染层存在多个 WebView 线程")])])]),v._v(" "),t("h2",{attrs:{id:"双线程通信"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#双线程通信"}},[v._v("#")]),v._v(" 双线程通信")]),v._v(" "),t("p",[v._v("把开发者的 JS 逻辑代码放到单独的线程去运行,但在 Webview 线程里,开发者就没法直接操作 DOM。那要怎么去实现动态更改界面呢?")]),v._v(" "),t("p",[v._v("前面我们知道,**逻辑层和渲染层的通信会由 Native (微信客户端)做中转,逻辑层发送网络请求也经由 Native 转发。**这是不是意味着,我们可以把 DOM 的更新通过简单的数据通信来实现呢?")]),v._v(" "),t("p",[v._v("Virtual DOM 相信大家都已有了解,大概是这么个过程:"),t("strong",[v._v("用 JS 对象模拟 DOM 树 -> 比较两棵虚拟 DOM 树的差异 -> 把差异应用到真正的 DOM 树上")]),v._v("。")]),v._v(" "),t("p",[v._v("在这里我们可以用上,如图:")]),v._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/13333.png",alt:"image"}})]),v._v(" "),t("ol",[t("li",[v._v("在渲染层把 WXML 转化成对应的 JS 对象。")]),v._v(" "),t("li",[v._v("在逻辑层发生数据变更的时候,通过宿主环境提供的 setData 方法把数据从逻辑层传递到 Native,再转发到渲染层。")]),v._v(" "),t("li",[v._v("经过对比前后差异,把差异应用在原来的 DOM 树上,更新界面。")])]),v._v(" "),t("p",[v._v("我们通过把 WXML 转化为数据,通过 Native 进行转发,来实现逻辑层和渲染层的交互和通信。而这样完整的一套框架,基本上都是通过小程序的基础库来完成的。")]),v._v(" "),t("h2",{attrs:{id:"小程序的基础库"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#小程序的基础库"}},[v._v("#")]),v._v(" 小程序的基础库")]),v._v(" "),t("p",[v._v("小程序的基础库是 JavaScript 编写的,它可以被注入到渲染层和逻辑层运行。主要用于:")]),v._v(" "),t("ul",[t("li",[v._v("在渲染层,提供各类组件来组建界面的元素")]),v._v(" "),t("li",[v._v("在逻辑层,提供各类 API 来处理各种逻辑")]),v._v(" "),t("li",[v._v("处理数据绑定、组件系统、事件系统、通信系统等一系列框架逻辑")])]),v._v(" "),t("p",[v._v("由于小程序的渲染层和逻辑层是两个线程管理,两个线程各自注入了基础库。**小程序的基础库不会被打包在某个小程序的代码包里边,它会被提前内置在微信客户端。**这样可以:")]),v._v(" "),t("ul",[t("li",[v._v("降低业务小程序的代码包大小")]),v._v(" "),t("li",[v._v("可以单独修复基础库中的 Bug,无需修改到业务小程序的代码包")])]),v._v(" "),t("h3",{attrs:{id:"exparser-框架"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#exparser-框架"}},[v._v("#")]),v._v(" Exparser 框架")]),v._v(" "),t("p",[v._v("Exparser 是微信小程序的组件组织框架,内置在小程序基础库中,为小程序的各种组件提供基础的支持。小程序内的所有组件,包括内置组件和自定义组件,都由 Exparser 组织管理。Exparser 特点包括:")]),v._v(" "),t("ol",[t("li",[v._v("基于 Shadow DOM 模型:模型上与 WebComponents 的 ShadowDOM 高度相似,但不依赖浏览器的原生支持,也没有其他依赖库;实现时,还针对性地增加了其他 API 以支持小程序组件编程。")]),v._v(" "),t("li",[v._v("可在纯 JS 环境中运行:这意味着逻辑层也具有一定的组件树组织能力。")]),v._v(" "),t("li",[v._v("高效轻量:性能表现好,在组件实例极多的环境下表现尤其优异,同时代码尺寸也较小。")])]),v._v(" "),t("p",[v._v("关于基础库和 Exparser 框架,更多的也可以参考:"),t("a",{attrs:{href:"https://developers.weixin.qq.com/ebook?action=get_post_info&token=935589521&volumn=1&lang=zh_CN&book=miniprogram&docid=0000e82f924ca0bb00869a5de5ec0a",target:"_blank",rel:"noopener noreferrer"}},[v._v("《小程序开发指南》"),t("OutboundLink")],1)]),v._v(" "),t("h1",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("这节里大概讲了小程序设计中比较重要的一个模型——双线程,关于双线程的出现、设计、数据通信,到基础库、Exparser 框架,都是一个个相关而又相互影响的选择。"),t("br"),v._v("\n关于小程序的底层框架设计,其实还涉及更多更多我们未能一时半会掌握完的内容,自定义组件、原生组件,还有他们做了很多的性能优化工作,都不是只言片语能讲完的。我们能做的,就是多去思考。")])])}),[],!1,null,null,null);_.default=e.exports}}]);
--------------------------------------------------------------------------------
/assets/js/130.68596fae.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[130],{691:function(v,_,t){"use strict";t.r(_);var a=t(69),l=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("当开发的个人能力成长到一定程度时,日常工作不再是缝缝补补、修修 bug、打打下手。")]),v._v(" "),t("p",[v._v("开发时间足够长时,我们常常会以项目的形式参与到具体的开发中,可能会负责项目的主导,或是作为核心开发负责某个模块、某个技术方案的落地。")]),v._v(" "),t("p",[v._v("在项目进行的每个阶段,我们都可以通过同样的方式去提升自己:")]),v._v(" "),t("ol",[t("li",[v._v("事前做预期。")]),v._v(" "),t("li",[v._v("事后做复盘。")])]),v._v(" "),t("h2",{attrs:{id:"事前做预期"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#事前做预期"}},[v._v("#")]),v._v(" 事前做预期")]),v._v(" "),t("p",[v._v("就像在代码开发前进行架构设计一样重要,我们在项目开始前,需要对项目的整个过程进行初步的预期,包括:")]),v._v(" "),t("ol",[t("li",[v._v("预期功能是否能实现?存在哪些不确定的功能?")]),v._v(" "),t("li",[v._v("预计的工作量和分工排期是怎样的?")]),v._v(" "),t("li",[v._v("每个阶段(开发、联调、产品体验、提测、发布)的时间点大概是怎样的?")]),v._v(" "),t("li",[v._v("哪些工作涉及外部资源的依赖和对接(交互/设计/接口协议等),是否存在延期风险?")]),v._v(" "),t("li",[v._v("如果存在风险,有没有什么方式可以避免?")])]),v._v(" "),t("p",[v._v("这么做有什么好处呢?如果不做方案调研和项目预期管理,那么对于项目过程中的风险则很难预测。这会导致项目的延期,甚至做到一般发现做不下去了。")]),v._v(" "),t("p",[v._v("在我们日常的工作中,这样的情况常常会遇到,很多人甚至对需求延期都已经习以为常了,认为需求能准时上线才是稀奇的事情。正因为大家都是这样的想法,我们更应该把这些事情做好来,这样才可以弯道超车。")]),v._v(" "),t("p",[v._v("首先,在项目开始的时候,需要进行工作量评估和分工排期。")]),v._v(" "),t("h3",{attrs:{id:"如何进行合理的分工排期"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#如何进行合理的分工排期"}},[v._v("#")]),v._v(" 如何进行合理的分工排期")]),v._v(" "),t("p",[v._v("进行工作量评估的过程可以分为三步:")]),v._v(" "),t("ol",[t("li",[v._v("确认技术方案,以及分工合作方式。")]),v._v(" "),t("li",[v._v("拆分具体功能模块,分别进行工作量评估,输出具体的排期时间表。")]),v._v(" "),t("li",[v._v("标注资源依赖情况和协作存在的风险,进行延期风险评估。")])]),v._v(" "),t("p",[v._v("当我们确认好技术方案之后,可以针对实现细节拆分具体的功能模块,分别进行工作量的预估和分工排期。具体的分工排期在多人协作的时候是必不可少的,否则可能面临分工不明确、接口协议未对齐就匆忙开工、最终因为各种问题而返工这些问题。")]),v._v(" "),t("p",[v._v("进行工作量评估的时候,可以精确到半天的工作量预期。对独自开发的项目来说,同样可以通过拆解功能模块这个过程,来思考具体的实现方式,也能提前发现一些可能存在的问题,并相应地进行规避。")]),v._v(" "),t("p",[v._v("提供完整的工作量评估和排期计划表(精确到具体的日期),可以帮助我们有计划地推进项目。在开发过程中,我们可以及时更新计划的执行情况,团队的其他人也可以了解我们的工作情况。")]),v._v(" "),t("p",[v._v("工作量评估和排期计划表的另外一个重要作用,是通过时间线去严格约束我们的工作效率、及时发现问题,并在项目结束后可针对时间维度进行项目复盘。")]),v._v(" "),t("p",[v._v("为了确保项目能按照预期进行,我们还要对可能存在的风险进行分析,提前做好对应的准备措施。")]),v._v(" "),t("h3",{attrs:{id:"对项目风险进行把控"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#对项目风险进行把控"}},[v._v("#")]),v._v(" 对项目风险进行把控")]),v._v(" "),t("p",[v._v("我们在项目开发过程中,经常会遇到这样的情况:")]),v._v(" "),t("ul",[t("li",[v._v("因为方案设计考虑不周,部分工作需要返工,导致项目延期")]),v._v(" "),t("li",[v._v("在项目进行过程中,常常会遇到依赖资源无法及时给到、依赖方因为种种原因无法按时支援等问题,导致项目无法按计划进行")]),v._v(" "),t("li",[v._v("团队协作方式未对齐,开发过程中出现矛盾,反复的争执和调整协作方式导致项目延期")])]),v._v(" "),t("p",[v._v("一个项目能按照预期计划进行,技术方案设计、分工和协作方式、依赖资源是否确定等,任何一各环节出现问题都可能导致整体的计划出现延误,这是我们不想出现的结果。")]),v._v(" "),t("p",[v._v("因此,我们需要主动把控各个环节的情况,及时推动和解决出现的一些多方协作的问题。")]),v._v(" "),t("h2",{attrs:{id:"事后做复盘"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#事后做复盘"}},[v._v("#")]),v._v(" 事后做复盘")]),v._v(" "),t("p",[v._v("很多开发习惯了当代码开发完成、发布上线之后就结束了这个项目,其实他们遗漏了一个很重要的环节:复盘。")]),v._v(" "),t("p",[v._v("对于大多数开发来说,很多时候都不屑于主动邀功,觉得自己做了些什么老板肯定都看在眼里,写什么总结和复盘都是刷存在感的表现。实际上老板们每天的事情很多,根本没法关注到每一个人,我以前也曾经跟老板们问过这样一个问题:做和说到底哪个重要?")]),v._v(" "),t("p",[v._v("答案是两个都重要。把一件事做好是必须的,但将这件事分享出来,可以同样给团队带来更多的成长。")]),v._v(" "),t("h3",{attrs:{id:"用数据说话"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#用数据说话"}},[v._v("#")]),v._v(" 用数据说话")]),v._v(" "),t("p",[v._v("性能优化的工作可以用具体的耗时和 CPU 资源占用这些数据来做总结,工具的开发可以用接入使用的用户数量来说明效果,这种普普通通的项目上线,又该怎么表达呢?")]),v._v(" "),t("p",[v._v("我们可以用两个维度复盘:")]),v._v(" "),t("ol",[t("li",[v._v("时间维度。")]),v._v(" "),t("li",[v._v("质量维度。")])]),v._v(" "),t("p",[v._v("其中,时间维度可以包括:")]),v._v(" "),t("ul",[t("li",[v._v("项目的预期启动、转体验、提测、灰度、全量时间")]),v._v(" "),t("li",[v._v("项目的最终启动、转体验、提测、灰度、全量时间")])]),v._v(" "),t("p",[v._v("通过预期和最终结果的对比,我们可以直观看到是否存在延期等情况,分析原因分别是什么(比如方案设计问题、人员变动、协作方延期等)")]),v._v(" "),t("p",[v._v("如下图,假设项目分为一期、二期,我们可以在一期结束后,进行复盘分析并改进。同时还可以以时间线的方式对比开发时间结果:")]),v._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/my-career5-6.jpg",alt:""}})]),v._v(" "),t("p",[v._v("除了时间维度以外,我们还可以通过衡量项目质量的方式来复盘,比如:")]),v._v(" "),t("ul",[t("li",[v._v("代码是否有单测、自动化测试保证质量")]),v._v(" "),t("li",[v._v("产品体验阶段的问题、提测后 BUG 分别有多少")]),v._v(" "),t("li",[v._v("灰度和全量后的用户反馈有多少")])]),v._v(" "),t("p",[v._v("我们需要分析各个阶段存在的质量问题,并寻找原因(比如技术方案变更时考虑不全、设计稿还原度较低、自测时间不足等)。质量的维度同样可以用对比的方式来展示:")]),v._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/my-career5-7.jpg",alt:""}})]),v._v(" "),t("p",[v._v("所以,为什么项目复盘很重要呢?")]),v._v(" "),t("ol",[t("li",[v._v("及时发现自己的问题并改进,避免掉进同一个坑。")]),v._v(" "),t("li",[v._v("让团队成员和管理者知道自己在做什么。")]),v._v(" "),t("li",[v._v("整理沉淀和分享项目经验,让整个团队都得到成长。")])]),v._v(" "),t("h3",{attrs:{id:"输出结果"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#输出结果"}},[v._v("#")]),v._v(" 输出结果")]),v._v(" "),t("p",[v._v("很多人会觉得做一个普通的前端项目,从开发到上线都没什么难度。一个字:“干”就完了。")]),v._v(" "),t("p",[v._v("实际上,项目的管理、推动和落地是工作中不可或缺的能力,这些不同于技术方案设计、代码编写,属于工作中的软技能。但正是这样的软技能会很大地影响我们的工作成果,也会影响自身的成长速度,是升职加薪的必备技能。")]),v._v(" "),t("p",[v._v("职场之所以让人不适,很多时候是由于它无法做到完美的公平。对于程序员来说,同样如此。")]),v._v(" "),t("p",[v._v("因此,为了能让自己付出的努力事半功倍,阶段性的输出是必不可少的。对于项目复盘来说,我们可以通过团队内外分享、邮件复盘总结等方式进行输出。")]),v._v(" "),t("p",[v._v("一般来说,可以通过几个方面来总结整理:")]),v._v(" "),t("ol",[t("li",[v._v("项目背景,比如为什么启动项目、目标是什么之类。")]),v._v(" "),t("li",[v._v("技术方案,是否做了技术选型、架构设计等。")]),v._v(" "),t("li",[v._v("项目结果,时间维度和质量维度,最好有数据佐证。")]),v._v(" "),t("li",[v._v("未来规划/优化方向。")])]),v._v(" "),t("p",[v._v("通过对项目进行复盘,除了可以让团队其他人和老板知道我们做了些什么,更重要的是,我们可以及时发现自身的一些问题并改进。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("本文介绍了在项目开发过程中,要如何做好前期的准备,又该如何在项目结束后进行完整的复盘。")]),v._v(" "),t("p",[v._v("对于大部分前端开发来说,接触工具和框架开发、参与开源项目的机会比较少,很多时候我们写的都是“枯燥无聊”的业务代码。我们总认为只有做工具才会比较有意思、有技术挑战,很多时候会先入为主,认为业务代码写得再好也没用,也渐渐放弃了去思考要怎么把事情做好。")]),v._v(" "),t("p",[v._v("其实不只是工作中,我们生活里也可以常常进行反思和总结,这样我们的步伐才可以越跑越快。成长的过程中总会遇到各式各样的问题,有些问题被我们视而不见,有些问题我们选择了躲开,但其实我们还可以通过迎面应战、解决并反思的方式,在这样一次次战斗中快速地成长。")])])}),[],!1,null,null,null);_.default=l.exports}}]);
--------------------------------------------------------------------------------
/assets/js/190.5dbb05ea.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[190],{752:function(v,_,t){"use strict";t.r(_);var r=t(69),a=Object(r.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("最近又是答辩季,程序员最讨厌写的 PPT 又到了不得不写的时候了。之前也有在帮一些小伙伴做准备,所以顺便给大家分享一些晋级答辩的思考和技巧吧~")]),v._v(" "),t("p",[v._v("关于答辩晋级这个内容,最开始我是直接做的视频放在 B 站分享,参考"),t("a",{attrs:{href:"https://www.bilibili.com/video/BV1tu411X7qn/",target:"_blank",rel:"noopener noreferrer"}},[v._v("《程序员日志--晋级答辩这件事》"),t("OutboundLink")],1),v._v("。")]),v._v(" "),t("blockquote",[t("p",[v._v("关于做视频和写文章,感觉自己从最初的只会写文章,到现在已经慢慢也会做一些视频了。视频的表达和文章相差很远,我自己的感受是,对于需要反复阅读、技术深度的内容,还是更适合用文章来记录。而视频更适合写一些需要录屏和讲解的模式,加上 PPT 本身的结构化,更容易去给大家梳理清楚逻辑架构,但是很多细节就很难讲清楚了。")])]),v._v(" "),t("h2",{attrs:{id:"如何对待晋级答辩这件事"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#如何对待晋级答辩这件事"}},[v._v("#")]),v._v(" 如何对待晋级答辩这件事")]),v._v(" "),t("p",[v._v("对于很多大公司的程序员来说,晋级答辩关乎着是否可以升职加薪,而答辩成功与否常常会对个人的工作态度和心态造成较大的影响。")]),v._v(" "),t("p",[v._v("我个人的看法是:"),t("strong",[v._v("认真对待它,但不要过分依赖它。")])]),v._v(" "),t("p",[v._v("这句话怎么理解呢?如果你仔细观察身边其他同事,大多数会分成两类:")]),v._v(" "),t("ol",[t("li",[v._v("平时工作只是完成工作本身,不希望受到答辩影响,但是到答辩的时候却临时抱佛脚。")]),v._v(" "),t("li",[v._v("过分看重答辩,在平日工作里就抢一些方便答辩的活,如果没有的话甚至自己造各种轮子,而不在乎这些轮子是否合适。")])]),v._v(" "),t("p",[v._v("以上两种态度都可以改善,我们可以在平时就认真地把手上的每件事做好,而到了答辩的时候也要认真地对待,但是不要因为答辩这件事影响了自己原本该有的工作态度和对待项目质量的要求。")]),v._v(" "),t("p",[v._v("或许有些人会疑惑,造不合适的轮子,为什么答辩能通过呢?")]),v._v(" "),t("p",[v._v("其实答辩这件事,本身也有认知偏差和主观因素。由于陈述内容是由答辩人自身提供的,所以很多时候都会只把好的一面呈现出来,而使用的技术栈或是造的一些轮子给原有项目造成的影响,或是带来的技术债务,或许就只有项目内的其他成员知道了。")]),v._v(" "),t("p",[v._v("除此之外,因为答辩是由评委来评分的,因此主观上如果评委比较感兴趣的内容,会更容易通过;而如果是评委熟悉的领域,则会被问到很深入和核心的问题,这样的可能性会更高。")]),v._v(" "),t("p",[v._v("所以,更多的时候,我认为答辩是否能通过是很需要运气的,包括我自己通过的几次答辩,都有不小的运气成分在里面。这也是为什么,我想跟大家说不要过分依赖晋级答辩,因为如果你过分看重和孤注一掷,那么不管成功与否,都会对你以后的工作心态产生影响。")]),v._v(" "),t("p",[v._v("那么,正如我视频里所说的,关于答辩这件事,你需要知道:")]),v._v(" "),t("ol",[t("li",[v._v("答辩是由 70% 的努力 + 30% 的运气组成的。")]),v._v(" "),t("li",[v._v("答辩考核的除了工作内容,还有工作方式和答辩技巧。")]),v._v(" "),t("li",[v._v("答辩是结果,不是目的。")])]),v._v(" "),t("p",[v._v("既然我们还是需要认真对待答辩这件事,该怎么去进行准备呢?")]),v._v(" "),t("h2",{attrs:{id:"如何准备答辩"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#如何准备答辩"}},[v._v("#")]),v._v(" 如何准备答辩")]),v._v(" "),t("p",[v._v("其实,答辩本身也属于项目复盘的一种方式,所以其实我们在平时工作里,就可以用更优的工作方式和节奏,去把事情做好。")]),v._v(" "),t("h3",{attrs:{id:"平时工作要做好"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#平时工作要做好"}},[v._v("#")]),v._v(" 平时工作要做好")]),v._v(" "),t("p",[v._v("如果我们在平时工作中,就有认真地思考每一个项目,更加结构化地去关注项目中的每个阶段的话,相比答辩本身能给我们自身带来更多的成长。")]),v._v(" "),t("p",[v._v("我们会常常看到,需要开发在工作的时候基本上是线性的工作方式,即:遇到问题 -> 解决问题 -> 结束。")]),v._v(" "),t("p",[v._v("实际上,我们可以在每个问题上思考更多:")]),v._v(" "),t("p",[t("strong",[v._v("(1) 做一件事的目的,需要贯穿全过程。")])]),v._v(" "),t("p",[v._v("很多时候,我们在遇到一个问题的时候,马上就开始找解决方案了。其实我们可以先暂停,去思考下这个问题是如何产生的,我们需要解决的到底是什么程度的问题,做这件事的目的是什么。")]),v._v(" "),t("p",[v._v("而在问题处理完成之后,同样需要回顾当初这个问题的目的是否已经达成,是否还遗留有待解决的问题,等等。")]),v._v(" "),t("p",[t("strong",[v._v("(2) 拓展自身的思维,更加结构化地去做事。")])]),v._v(" "),t("p",[v._v("比如,在寻找解决方案的时候,当我们找到一个解决方向的时候,可以先不着急去马上解决,而是需要考虑是否还有其他解决方案?当前方案是否最优?解决方案是否存在局限?是否有更多的探索可能性?")]),v._v(" "),t("p",[v._v("充分做好前期调研之后,再对多个方案进行对比,结合自身项目的情况,找到最适合用于项目中的一个解决方案。")]),v._v(" "),t("p",[t("strong",[v._v("(3) 将一件事情的价值最大化。")])]),v._v(" "),t("p",[v._v("很多时候,我们处理完一个问题,这个事情就结束了。对于团队来说,这样的方式其实效率很低,因为不同的团队成员很可能会遇到相同的问题,如果每个人都花费这些时间去获得差不多的结论,那么团队的成长会很慢。")]),v._v(" "),t("p",[v._v("我们可以选择将每次处理问题的过程和解决方案进行总结沉淀,然后分享给其他人。这样,团队内就可以共享每个人努力的成果,这对于团队来说成长是很快的,而对团队中的每个人来说亦是如此。")]),v._v(" "),t("p",[v._v("而沉淀和总结本身,也可以促进个人的成长。在开发的职业生涯是,是否具备这样的能力和认识,是十分关键的。")]),v._v(" "),t("h3",{attrs:{id:"答辩-项目内容结构"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#答辩-项目内容结构"}},[v._v("#")]),v._v(" 答辩/项目内容结构")]),v._v(" "),t("p",[v._v("对于答辩本身,我们首先要知道:要能让评委认可你的能力,首先得高效地让评委理解项目中的各个过程。")]),v._v(" "),t("p",[v._v("因此,大多数时候我们的答辩内容都可以分为以下结构:")]),v._v(" "),t("ol",[t("li",[v._v("项目背景/问题描述。讲清楚做这个项目的背景情况和目的,这是最起码的铺垫。")]),v._v(" "),t("li",[v._v("难点/挑战点。如果评委感受不到项目中的难点,那么这个项目又怎么证明你的能力呢?")]),v._v(" "),t("li",[v._v("方案调研/方案对比。工作方式中,做好前期足够的调研和准备,认真对比得到的解决方案,才可以说是合适的方案。")]),v._v(" "),t("li",[v._v("(解决过程)。过程大多数时候无关紧要,但是如果同样存在难点,也可以一并描述。")]),v._v(" "),t("li",[v._v("项目结果(最终效果/数据论证)。如果有足够的证据佐证,那么这个项目的成果便是无可置疑的。")]),v._v(" "),t("li",[v._v("展望:遗留问题/后续计划/产生更多价值。从点到面发散这个项目,是否可以做更多?")]),v._v(" "),t("li",[v._v("个人影响力。")])]),v._v(" "),t("p",[v._v("这里就不过多描述了,其实如果你有认真思考以上的点,基本就可以说是有认真对待一个项目,同时自己也能从中获得足够多的成长和沉淀了。")]),v._v(" "),t("p",[v._v("除去答辩本身,以上的这些内容其实在我们日常的工作里,同样需要进行思考和去完成的。也就是说,这样的结构点,并不只是答辩所需,而是需要贯彻到我们工作的每个项目/每个遇到的问题里,这样才能更好地脚踏实地,同时也不需要再为答辩专门做更多的处理了。")]),v._v(" "),t("h3",{attrs:{id:"答辩技巧"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#答辩技巧"}},[v._v("#")]),v._v(" 答辩技巧")]),v._v(" "),t("p",[v._v("如果说我们在平时就已经把工作结构化地做好了,是否意味着答辩就能一定顺利呢?")]),v._v(" "),t("p",[v._v("除了内容本身需要踏实以外,我们还需要掌握一定的答辩技巧,比如:")]),v._v(" "),t("ul",[t("li",[v._v("PPT 思路清晰,可以参考上述答辩内容结构来进行梳理")]),v._v(" "),t("li",[v._v("适当使用动画,突出重点。动画的用处在于让对方注意力聚焦在自己讲的内容上,所以要避免过分浮夸的动画")]),v._v(" "),t("li",[v._v("陈述足够熟练/脱稿,自己或是找同事多练几遍")]),v._v(" "),t("li",[v._v("思考项目中的不足/可能提问的问题,准备到如何回答")])]),v._v(" "),t("p",[v._v("以上等等。")]),v._v(" "),t("p",[v._v("如果你有时间,可以来看看这个视频("),t("a",{attrs:{href:"https://www.bilibili.com/video/BV1tu411X7qn/",target:"_blank",rel:"noopener noreferrer"}},[v._v("也可以直接去 B 站看原视频哦"),t("OutboundLink")],1),v._v("):")]),v._v(" "),t("div",{staticStyle:{position:"relative",padding:"30% 45%"}},[t("iframe",{staticStyle:{position:"absolute",width:"100%",height:"100%",left:"0",top:"0"},attrs:{src:"https://player.bilibili.com/player.html?aid=509223755&bvid=BV1tu411X7qn&cid=512396881&page=1&high_quality=1",frameborder:"no",scrolling:"no"}})]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("在工作中,我看到过不少由于晋级失败、拿了差考核而开始怀疑自我的小伙伴。我想说的是,工作只是人生的一部分,并不代表着全部,也不可以因为工作的不顺利而否定或是认定自己的一生。")]),v._v(" "),t("p",[v._v("实际上,失败才是大多数人一生的主旋律,我们要尽早学会如何与失望和意外相处,要接受不完美的自己,学会认可自己。世界上有无数的人,失败或是成功,但是只有一个自己,要学会爱上这个自己。")])])}),[],!1,null,null,null);_.default=a.exports}}]);
--------------------------------------------------------------------------------
/assets/js/108.7a7761a8.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[108],{670:function(v,_,t){"use strict";t.r(_);var a=t(69),r=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("对于内容复杂和变更频繁的前端应用,页面渲染也常常是性能优化的核心场景。")]),v._v(" "),t("p",[v._v("前面我有给大家整体地讲过"),t("RouterLink",{attrs:{to:"/front-end-basic/performance/front-end-performance-optimization.html"}},[v._v("《前端性能优化--方案归纳篇》")]),v._v(",其实里面已经囊括了大多数场景下的一些性能优化的方向。关于加载流程相关的优化,也有在"),t("RouterLink",{attrs:{to:"/front-end-basic/performance/front-end-performance-startup.html"}},[v._v("《前端性能优化--加载流程篇》")]),v._v("一文中进行详细的介绍。")],1),v._v(" "),t("p",[v._v("本文主要围绕页面渲染相关的内容,来进行性能优化分析。")]),v._v(" "),t("h1",{attrs:{id:"首屏渲染"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#首屏渲染"}},[v._v("#")]),v._v(" 首屏渲染")]),v._v(" "),t("p",[v._v("说到页面渲染,首屏的渲染显然是最首要的。其实前面在归纳篇也有介绍,首屏加载优化核心点在于:"),t("strong",[v._v("将页面内容尽快展示给用户,减少页面白屏时间。")])]),v._v(" "),t("p",[v._v("首屏渲染包括了首屏内容的加载和渲染两个过程。")]),v._v(" "),t("h2",{attrs:{id:"首屏内容加载"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#首屏内容加载"}},[v._v("#")]),v._v(" 首屏内容加载")]),v._v(" "),t("p",[v._v("对于首屏加载过程,我们可以通过以下方式进行优化:")]),v._v(" "),t("ul",[t("li",[v._v("使用骨架屏进行预渲染")]),v._v(" "),t("li",[v._v("对页面进行分片/分屏加载,将页面可见/可交互时间提前")]),v._v(" "),t("li",[v._v("优化资源加载的顺序和粒度,仅加载需要的资源,通过异步加载方式加载剩余资源")]),v._v(" "),t("li",[v._v("使用差异化服务,比如读写分离,对于不同场景按需加载所需要的模块")]),v._v(" "),t("li",[v._v("使用服务端直出渲染,减少页面二次请求和渲染的耗时")]),v._v(" "),t("li",[v._v("使用秒看技术,通过预览的方式(比如图片)提前将页面内容提供给用户")]),v._v(" "),t("li",[v._v("配合客户端进行资源预请求和预加载,比如使用预热 Web 容器")]),v._v(" "),t("li",[v._v("配合客户端将资源和数据进行离线,可用于下一次页面的快速渲染")])]),v._v(" "),t("p",[v._v("这里提到了很多的方向,但是否每个优化点都适用于自身的项目中,需要结合项目本身做调研和验证。举个简单的例子,最后两条优化点明显是基于有自研客户端的前提下,需要配合客户端一起做优化才可以实现。")]),v._v(" "),t("p",[v._v("实际上,对于首屏内容的优化,前端开发在项目中更常用的点是骨架屏、数据分片/分屏加载、SSR DOM 直出渲染这几种,因为这几个优化点相对来说方向明确、效果明确、实现相对简单。如果是想要对项目做差异化服务、做资源的拆分和优化,则可能随着项目的复杂度增加,方案难度提升、实现成本也增长。")]),v._v(" "),t("h2",{attrs:{id:"首屏内容渲染"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#首屏内容渲染"}},[v._v("#")]),v._v(" 首屏内容渲染")]),v._v(" "),t("p",[v._v("对于首屏内容渲染的过程,更多时候我们是指浏览器渲染 HTML 的过程。该过程可以优化的点也是我们常常提及的,浏览器渲染页面的优化过程,比如:")]),v._v(" "),t("ul",[t("li",[v._v("将 CSS 放在"),t("code",[v._v("")]),v._v("里,可用来避免浏览器渲染的重复计算")]),v._v(" "),t("li",[v._v("将 JavaScript 脚本放在"),t("code",[v._v("")]),v._v("的最后面,避免资源阻塞页面渲染")]),v._v(" "),t("li",[v._v("减少 DOM 数量,减少浏览器渲染过程中的计算耗时")]),v._v(" "),t("li",[v._v("通过合理使用浏览器 GPU 合成,提升浏览器渲染效率")])]),v._v(" "),t("p",[v._v("以上这些,是我们在做首屏渲染时考虑渲染过程的优化点。虽然这些优化点属于前端基础和共识,也常常会出现在基础面试中。")]),v._v(" "),t("p",[v._v("很多时候我们为了准备面试而学习了很多的知识和原理,却容易在将知识和实践结合的过程中忘记。越是基础和简单的点,反而往往会在实际写代码的时候被忽略,直到性能出现了问题,这些基础的优化点才会被注意到。")]),v._v(" "),t("p",[v._v("当然,首屏性能的提升,除了渲染相关的,也还有上一篇我们提到的"),t("RouterLink",{attrs:{to:"/front-end-basic/performance/front-end-performance-startup.html"}},[v._v("加载流程相关的优化")]),v._v("。")],1),v._v(" "),t("h1",{attrs:{id:"页面更新"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#页面更新"}},[v._v("#")]),v._v(" 页面更新")]),v._v(" "),t("p",[v._v("除了首屏内容需要尽快加载和渲染以外,当页面内容需要更新的时候,我们也需要尽可能地减少更新内容渲染的耗时。")]),v._v(" "),t("p",[v._v("一般来说,页面更新场景我们常常会关注用户操作和页面渲染。")]),v._v(" "),t("h2",{attrs:{id:"用户操作"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#用户操作"}},[v._v("#")]),v._v(" 用户操作")]),v._v(" "),t("p",[v._v("页面内容的更新,一般有两种情况:")]),v._v(" "),t("ol",[t("li",[v._v("用户自身操作(点击、输入、拖拽等)的页面响应。")]),v._v(" "),t("li",[v._v("实时内容的变更(比如聊天室的消息提醒、弹幕等等)。")])]),v._v(" "),t("p",[v._v("如果是用户自身的操作,则我们需要及时地更新页面内容,让用户感受到操作生效了。该过程应该是优先级最高的,一般需要同步进行。因为如果有别的任务在执行而导致主线程阻塞,就容易造成页面卡顿的体验。关于卡顿相关的,我会另外再起一篇文章介绍,这里就不过多展开啦。")]),v._v(" "),t("p",[v._v("至于实时内容的变更,优先级更多会比用户操作稍微低一些,也基本上都是异步进行的。我们还可以考虑对变更内容做合并、批量更新,也可以考虑定时拉取最新内容更新的方式。")]),v._v(" "),t("h3",{attrs:{id:"事件委托"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#事件委托"}},[v._v("#")]),v._v(" 事件委托")]),v._v(" "),t("p",[v._v("对于用户交互频繁的场景,我们还得注意事件的绑定。相信很多人都了解过事件委托,如果在列表数量内容较大的时候,对成千上万节点进行事件监听,也是不小的性能消耗。使用事件委托的方式,通过将事件绑定在父元素上,我们可以大量减少浏览器对元素的监听,也是在前端性能优化中比较简单和基础的一个做法。")]),v._v(" "),t("p",[v._v("事件委托是很常见的优化方式,需要注意的是,如果我们直接在"),t("code",[v._v("document.body")]),v._v("上进行事件委托,可能会带来额外的问题。由于浏览器在进行页面渲染的时候会有合成的步骤,合成的过程会先将页面分成不同的合成层,而用户与浏览器进行交互的时候需要接收事件。")]),v._v(" "),t("p",[v._v("如果我们在"),t("code",[v._v("document.body")]),v._v("上被绑定了事件,这时候整个页面都会被标记。即使我们的页面不关心某些部分的用户交互,合成器线程也必须与主线程进行通信,并在每次事件发生时进行等待。此时可以使用"),t("code",[v._v("passive: true")]),v._v("选项来解决。")]),v._v(" "),t("h2",{attrs:{id:"页面渲染"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#页面渲染"}},[v._v("#")]),v._v(" 页面渲染")]),v._v(" "),t("p",[v._v("我们在页面内容更新的时候,一般也可以考虑以下优化点:")]),v._v(" "),t("ul",[t("li",[v._v("减少/合并 DOM 操作,减少页面更新的内容范围,减少浏览器渲染过程中的计算耗时")]),v._v(" "),t("li",[v._v("对于页面动画,可以使用 CSS transition 能力,减少 DOM 属性的修改")]),v._v(" "),t("li",[v._v("使用资源预加载,在空闲时间,提前将用户可能需要用到的资源进行获取并加载(比如下一页的内容)")])]),v._v(" "),t("h3",{attrs:{id:"dom-操作合并"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#dom-操作合并"}},[v._v("#")]),v._v(" DOM 操作合并")]),v._v(" "),t("p",[v._v("说到 DOM 操作的合并和减少,目前大多数前端框架都提供了虚拟 DOM 的能力(比如 Vue 和 React)。虚拟 DOM 本身就有对 DOM 操作和更新做优化,通过使用 JavaScript 对象模拟 DOM 元素,并在页面需要更新时对更新的部分做 DOM Diff,尽可能地减少内容的更新频率和范围。")]),v._v(" "),t("p",[v._v("虽然现在大多数前端项目都离不开前端框架,也正因为这些框架本身已经做了很多的优化,所以我们常常会忘记和忽略掉这些注意事项。")]),v._v(" "),t("p",[v._v("但也从侧面论证了,即使是很基础的优化点也需要重视,即使是简单的优化点也可以做出很棒的设计。")]),v._v(" "),t("h3",{attrs:{id:"页面滚动渲染"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#页面滚动渲染"}},[v._v("#")]),v._v(" 页面滚动渲染")]),v._v(" "),t("p",[v._v("考虑到页面滚动的场景,可能会出现性能问题的地方常常是长列表/页面的渲染。")]),v._v(" "),t("p",[v._v("由于页面内容过多,页面的 DOM 元素数量也很多,容易造成页面渲染的卡顿。在这样的情况下,我们可以考虑仅渲染可见区域的部分,比如页面内容超出滚动范围之外,就可以进行销毁,将页面的 DOM 数量保持在一定范围内。")]),v._v(" "),t("h1",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("本文主要围绕页面渲染和更新的过程,介绍了一些性能优化的方向。其实如果你有注意到,就会发现本文的内容大多数还是基础和简单的前端知识点。")]),v._v(" "),t("p",[v._v("还是那句话,前端基础和原理知识基本上大多数开发都掌握了,但是要怎么将这些知识在项目中发挥到最佳的作用呢?这才是我们工作中在不断探索和学习,获得经验和成长的关键点。")]),v._v(" "),t("p",[v._v("纸上得来终觉浅,了解一些知识很简单,但是要深入理解、熟练掌握后,再结合自身经验将它发挥出来,才是其价值的完整体现。")])])}),[],!1,null,null,null);_.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/128.75dd9b06.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[128],{690:function(v,_,t){"use strict";t.r(_);var a=t(69),r=Object(a.a)({},(function(){var v=this,_=v.$createElement,t=v._self._c||_;return t("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[t("p",[v._v("技术方案设计属于架构能力中的一种,当我们开始作为某些功能/应用的 Owner 或是技术负责人来参与项目时,便会面临独立完成技术方案的调研和设计这样的工作内容。")]),v._v(" "),t("p",[v._v("一般来说,技术方案的调研和设计过程可以分为几个阶段:")]),v._v(" "),t("ol",[t("li",[v._v("对项目的痛点、现状进行分析。")]),v._v(" "),t("li",[v._v("调用业界成熟的技术方案。")]),v._v(" "),t("li",[v._v("结合项目本身的现状和痛点,进行技术方案的选型和对比。")])]),v._v(" "),t("h2",{attrs:{id:"技术方案调研"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#技术方案调研"}},[v._v("#")]),v._v(" 技术方案调研")]),v._v(" "),t("p",[v._v("只有确保了技术方案的最优化、避免开发过程遇到问题需要推翻重做,从而能够快速落地并达成预期的效果。因此,在进行方案设计之前,对于项目存在的一些技术瓶颈、技术调整,我们需要先进行充分的前期调研。")]),v._v(" "),t("p",[v._v("在进行技术方案调研的时候,我们需要首先结合自身项目的背景、存在的痛点、现状问题进行分析,只有找到项目的问题在哪里,才可以更准确、彻底地去解决这些问题。")]),v._v(" "),t("h3",{attrs:{id:"分析项目背景-挖掘项目痛点"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#分析项目背景-挖掘项目痛点"}},[v._v("#")]),v._v(" 分析项目背景,挖掘项目痛点")]),v._v(" "),t("p",[v._v("技术方案的设计很多时候并不是命题作文,更多时候我们需要自己去挖掘项目的痛点,然后才是提出解决方案。")]),v._v(" "),t("p",[v._v("很多前端开发常常觉得自己做的项目没什么意思,认为每天都是重复的工作、繁琐的业务逻辑、糟糕的历史遗留代码。")]),v._v(" "),t("p",[v._v("实际上,那些会让我们觉得枯燥和重复的工作内容,也是可以去改善做好、并能从中获得成长的地方。好的业务可遇不可求,如果工作内容跟自己的预期不一样,我们就什么都不做了吗?")]),v._v(" "),t("p",[v._v("我们可以主动寻找项目存在的问题和痛点,并尝试去解决。不同的项目或是同一个项目的不同时期,关注的技术点都会不一样。对于一个前端项目来说,技术价值常常体现在系统性能、稳定性、可维护性、效率提升等地方,比如:")]),v._v(" "),t("ul",[t("li",[v._v("对于用户量较大的项目,对系统稳定性要求较高,开发过程中需要关注是否会导致历史功能不兼容、是否会引入新的问题等;")]),v._v(" "),t("li",[v._v("对于大型复杂的项目,常常涉及多人协作,因此对系统可维护性要求更高,需要避免每次的改动都会导致性能和稳定性的下降,如何提升协作开发的效率等;")]),v._v(" "),t("li",[v._v("对于一次性的活动页面、管理端页面开发,技术挑战通常是如何提高开发效率,可以使用配置化、脚手架、自动化等手段来提升页面的开发和上线效率;")])]),v._v(" "),t("p",[v._v("找到项目的痛点之后,我们就可以进入项目的现状分析。")]),v._v(" "),t("h3",{attrs:{id:"现状分析"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#现状分析"}},[v._v("#")]),v._v(" 现状分析")]),v._v(" "),t("p",[v._v("项目的痛点可以转化为一个目标方向,比如:")]),v._v(" "),t("ul",[t("li",[v._v("加载慢 -> 首屏加载耗时优化")]),v._v(" "),t("li",[v._v("开发效率低 -> 提升项目自动化程度")]),v._v(" "),t("li",[v._v("多人协作容易出问题 -> 提升系统稳定性")])]),v._v(" "),t("p",[v._v("确定目标之后,我们就需要进行技术方案的设计,但很多时候由于项目现状存在的问题,一些技术优化的方案并不适用,需要进行方向的调整。")]),v._v(" "),t("p",[v._v("假设有一个同样规模大、成员多的小程序项目,由于该项目处于快速迭代的时期,考虑到投入产出比、产品形态也在不断调整,老板说“每个功能由开发自己保证”,决定不投入测试资源。")]),v._v(" "),t("p",[v._v("这意味着开发不仅需要在自测的时候确保核心用例的覆盖,同时也没有足够的排期来进行自动化测试(单元测试、集成测试、端到端测试等)的开发。")]),v._v(" "),t("p",[v._v("一般来说,我们还可以考虑建立用例录制和自动化回归的解决方案。比如开发一个浏览器插件,来获取用户操作的一些行为(比如 Redux 中的 Action 操作),将操作行为的页面结果(状态数据,比如 Redux 的 State)保存下来。在发布之前,可以通过自动化触发相同的操作行为,并与录制的页面结果进行比较,来进行回归测试。")]),v._v(" "),t("p",[v._v("但对于小程序的特殊性,我们无法让其运行在浏览器中,更无法获取到它的操作行为。在这样的情况下,还有什么办法可以保证系统的稳定性呢?")]),v._v(" "),t("p",[v._v("考虑到一个系统的上线过程包括开发、测试、灰度和发布四个阶段,如果无法通过测试阶段来及时发现问题,那么我们还可以通过灰度过程中来及时发现并解决问题。")]),v._v(" "),t("p",[v._v("比如,通过全埋点覆盖各个页面的功能,灰度过程中观察埋点曲线是否有异常、及时告警和排查问题、暂停灰度或者回滚等方式,来避免给更多的用户带来不好的体验。")]),v._v(" "),t("p",[v._v("通过灰度的方式来保证系统稳定性,会对局部的用户造成影响,这并不是一个最优的技术方案,它是考虑到项目的现状退而求其次的解决方案,但最终也同样可以达到提升系统稳定性这样一个目的。")]),v._v(" "),t("p",[v._v("当我们确定了技术优化的具体方向之后,便可以进行业界方案的调研阶段了。")]),v._v(" "),t("h3",{attrs:{id:"业界方案调研"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#业界方案调研"}},[v._v("#")]),v._v(" 业界方案调研")]),v._v(" "),t("p",[v._v("当我们遇到一些技术问题并尝试解决的时候,需要提醒自己,这些问题肯定有其他人也遇到过。为了避免技术方案的设计过于局限,我们可以进行前期的调研,找一些业界相对成熟的方案作为参考,分析这些方案的优缺点、是否适用于自己的项目中。")]),v._v(" "),t("p",[v._v("我们可以通过几种方式去进行业界方案的调研:")]),v._v(" "),t("ol",[t("li",[v._v("与有相关经验的开发进行沟通,交流技术方案,提供参考思路。")]),v._v(" "),t("li",[v._v("参考其他系统对外公开的方案设计。")]),v._v(" "),t("li",[v._v("参考开源项目的源码设计。")])]),v._v(" "),t("p",[v._v("举个例子,对于交互复杂、规模大型的应用,要如何管理各个模块间的依赖关系呢?业界相对成熟的解决方案是使用依赖注入体系,其中著名的开源项目中有 Angular 和 VsCode 都实现了依赖注入的框架,我们可以通过研究它们的相关代码,分析其中的思路以及实现方式。")]),v._v(" "),t("p",[v._v("开源项目源码很多,要怎么才能找到自己想看的部分呢?带着疑问有目的性地看,会简单轻松得多。比如上述的依赖注入框架,我们可以带着以下的问题进行研究:")]),v._v(" "),t("ol",[t("li",[v._v("依赖注入框架是什么?")]),v._v(" "),t("li",[v._v("模块是怎样初始化,什么时候进行销毁的?")]),v._v(" "),t("li",[v._v("模块是如何获取到其它模块呢?")]),v._v(" "),t("li",[v._v("模块间是如何进行通信的呢?")])]),v._v(" "),t("p",[v._v("通过这样的方式阅读源码,我们可以快速掌握自己需要的一些信息。在业界方案调研完成之后,我们需要结合自身项目进行具体的技术方案设计。")]),v._v(" "),t("h2",{attrs:{id:"技术方案设计"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#技术方案设计"}},[v._v("#")]),v._v(" 技术方案设计")]),v._v(" "),t("p",[v._v("技术方案设计过程中,我们需要根据上述的调研资料进行整理,包括项目痛点、现状、业界方案等,然后进行方案的选型和对比,最终给到适合项目的解决方案。")]),v._v(" "),t("h3",{attrs:{id:"方案选型-对比"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#方案选型-对比"}},[v._v("#")]),v._v(" 方案选型/对比")]),v._v(" "),t("p",[v._v("业界的解决方案可能有多套,这时候我们需要对这些方案进行分析比较。")]),v._v(" "),t("p",[v._v("除此之外,如果需要投入人力和时间成本去做一件事,我们就会面临一个问题:如何让团队认同这件事情、并愿意给到资源让我去完成它呢?梳理项目现状和痛点、提供业界认可的案例参考、进行全面的方案对比和选型,也是一种方式。")]),v._v(" "),t("p",[v._v("例如,假设我们最近需要针对项目进行自动化性能测试能力的支持:")]),v._v(" "),t("ul",[t("li",[v._v("项目现状:项目规模大、模块多、参与开发的成员也有几十人")]),v._v(" "),t("li",[v._v("项目痛点:经常因为一些不同模块的变更导致项目的性能下降却没法及时发现问题,往往是等到用户反馈或是某次开发、产品或者测试发现的时候才得知")])]),v._v(" "),t("p",[v._v("调研常见的一些性能分析方案,发现有几种方式:")]),v._v(" "),t("ol",[t("li",[v._v("通过 Chrome Devtools 提供的 Performace 火焰图,来定位和发现问题,但这种方式局限于开发手动分析定位。")]),v._v(" "),t("li",[v._v("使用 Lighthouse,该工具可以提供初步的网页优化建议,也支持自动化。但 Lighthouse 本身更专注于短时间内对网站进行较全面的评估,存在像分析不够细致和深入这些问题。")]),v._v(" "),t("li",[v._v("使用 Chrome Devtools 提供的 Chrome Devtools Protocol(CDP)能力,进行自动化生成火焰图需要的 JSON。但业界对该 JSON 的分析工具几乎没有,大家都通过将该 JSON 传到 Chrome Devtools 提供的一个工具来还原火焰图,无法支持全程的自动化分析。")])]),v._v(" "),t("p",[v._v("其中,第一和第二种方案都无法从根本上解决遇到的问题。如果要彻底解决这个问题,可以考虑采取第三种方案,并打算通过自行研究分析 CDP(Chrome Devtools Protocol)生成的 JSON 来达到完全的自动化目的。")]),v._v(" "),t("p",[v._v("方案选型和对比是技术方案设计中重要的一个环节,可以将现状和痛点分析得更加全面,同时还可以避开一些其他人踩过的坑。")]),v._v(" "),t("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("在大多数工作中,对开发的要求都不仅限于实现功能。你是否有想过,如果只是编写代码,刚毕业的应届生花几周时间也一样能做到,那么我们的优势在哪里呢?")]),v._v(" "),t("p",[v._v("洞察工作中的瓶颈,并有足够的能力去设计方案、排期开发、解决并复盘,这些技能更能突显我们在岗位上的价值和能力。对团队来说,更需要这样能主动发现并解决问题的成员,而不是安排什么就只做什么的螺丝钉。")]),v._v(" "),t("p",[v._v("技术的发展都离不开知识的沉淀、分享和相互学习,当我们遇到一些问题不知道该怎么解决的时候,可以试着站到巨人的肩膀上,说不定可以看到更多。")])])}),[],!1,null,null,null);_.default=r.exports}}]);
--------------------------------------------------------------------------------
/assets/js/39.b13ae703.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[39],{601:function(t,r,v){"use strict";v.r(r);var _=v(69),a=Object(_.a)({},(function(){var t=this,r=t.$createElement,v=t._self._c||r;return v("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[v("h2",{attrs:{id:"喵喵喵"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#喵喵喵"}},[t._v("#")]),t._v(" 喵喵喵??")]),t._v(" "),v("h3",{attrs:{id:"在现在-ai-的强力帮助下-要怎么入门前端呢"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#在现在-ai-的强力帮助下-要怎么入门前端呢"}},[t._v("#")]),t._v(" 在现在 AI 的强力帮助下,要怎么入门前端呢?")]),t._v(" "),v("p",[v("a",{attrs:{href:"https://www.bilibili.com/video/BV1iQ3wz1Eyi",target:"_blank",rel:"noopener noreferrer"}},[t._v("《有了 AI,要怎么学习前端开发呢》"),v("OutboundLink")],1)]),t._v(" "),v("h3",{attrs:{id:"我是个小白-没有前端基础-要怎么学习呢"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#我是个小白-没有前端基础-要怎么学习呢"}},[t._v("#")]),t._v(" 我是个小白(没有前端基础),要怎么学习呢?")]),t._v(" "),v("br"),t._v(" "),v("ol",[v("li",[t._v("可以翻看"),v("RouterLink",{attrs:{to:"/front-end-basic/#学习资源"}},[t._v("前端基础-学习资源")]),t._v(",根据这些网站提供的教程来学习。")],1),t._v(" "),v("li",[t._v("建议边看边操作,可以结合"),v("RouterLink",{attrs:{to:"/front-end-basic/#练习空间"}},[t._v("前端基础-练习空间")]),t._v("中的 freeCodeCamp 进行一步步学习,也可以直接选择想练习的课程来进行。")],1),t._v(" "),v("li",[t._v("查看"),v("RouterLink",{attrs:{to:"/front-end-basic/front-end/front-end-1.html"}},[t._v("前端入门系列")]),t._v(",补充对前端的一些认识和理解。")],1),t._v(" "),v("li",[t._v("开始尽情地撸代码吧,根据自己的需要,可以搜一些在线的资源来修改,也可以自己根据其他教程(Vue、小程序等)来完成一个项目。")])]),t._v(" "),v("blockquote",[v("p",[t._v("Tips:")]),t._v(" "),v("ol",[v("li",[t._v("很多的报错、API、以及遇到一些不知所措的问题,可以尽情谷歌噢。如果你家的梯子不方便,可以去 "),v("a",{attrs:{href:"https://cn.bing.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("bing"),v("OutboundLink")],1),t._v(" 上搜噢。")]),t._v(" "),v("li",[t._v("一些调试的技巧,可以找一些视频或者教程来看,这边除了"),v("RouterLink",{attrs:{to:"/front-end-basic/front-end/front-end-7.html"}},[t._v("前端入门-代码调试")]),t._v("这一节内容,别的需要自己去找噢。")],1)])]),t._v(" "),v("br"),t._v(" "),v("h3",{attrs:{id:"我已经掌握一些基本的前端语法-要怎么提升写代码的能力呢"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#我已经掌握一些基本的前端语法-要怎么提升写代码的能力呢"}},[t._v("#")]),t._v(" 我已经掌握一些基本的前端语法,要怎么提升写代码的能力呢?")]),t._v(" "),v("br"),t._v(" "),v("p",[t._v("一句话:多练!")]),t._v(" "),v("p",[t._v("至于怎么练习呢,首先前端它可以是一个很有成就感的事情,例如我在这个网站中不停地塞各种猫猫的时候,是开心地飞起的。那要抓住这种成就感,你可以:")]),t._v(" "),v("ul",[v("li",[t._v("根据自己喜欢的画风,设计一个博客,然后搭建起来")]),t._v(" "),v("li",[t._v("写一点好玩的交互、动画,或者小游戏")]),t._v(" "),v("li",[t._v("找自己喜欢的网站,试着自己写出一个一样的")])]),t._v(" "),v("p",[t._v("对于 CSS,只能熟能生巧,当然你可以搭配一下一些盒子模型、元素堆叠和定位、文档流等原理来理解(可参考"),v("RouterLink",{attrs:{to:"/front-end-basic/front-end/front-end-3.html"}},[t._v("前端入门-HTML 和 CSS")]),t._v("),可以帮助你更好地记住这些技巧。")],1),t._v(" "),v("p",[t._v("对于 javascript、DOM、BOM、浏览器原理和机制这些,我能提供的方向也都写在了"),v("RouterLink",{attrs:{to:"/front-end-basic/front-end/front-end-1.html"}},[t._v("前端入门系列")]),t._v("中了,剩下的只能靠你们自己啦。")],1),t._v(" "),v("br"),t._v(" "),v("h3",{attrs:{id:"我已经可以熟练地写项目-但是觉得自己每天都在做重复的工作-可以怎么深入学习呢"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#我已经可以熟练地写项目-但是觉得自己每天都在做重复的工作-可以怎么深入学习呢"}},[t._v("#")]),t._v(" 我已经可以熟练地写项目,但是觉得自己每天都在做重复的工作,可以怎么深入学习呢?")]),t._v(" "),v("br"),t._v(" "),v("p",[t._v("一句话:多思考!")]),t._v(" "),v("p",[t._v("我也经常写差不多的项目,但是每一次我都能自己找到可以优化、比较有意思的地方,你也应该这样。")]),t._v(" "),v("p",[t._v("前端除了做一些工具、开源库,其实前端的选型、架构、项目设计也是很重要的一个技能。这样的技能除了看别人的一些整理和总结,更多其实还是需要自己去思考,不要贪多嚼不烂。我自己也有一些稍微好玩的思考,可以查看"),v("RouterLink",{attrs:{to:"/front-end-basic/understanding/build-application.html"}},[t._v("前端深入理解系列")]),t._v(",如果你也有好玩的想法,欢迎一起来讨论~")],1),t._v(" "),v("br"),t._v(" "),v("h3",{attrs:{id:"很多面试会问一些原理、抽象设计-但是平时很少接触底层的东西-要怎么提升自己呢"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#很多面试会问一些原理、抽象设计-但是平时很少接触底层的东西-要怎么提升自己呢"}},[t._v("#")]),t._v(" 很多面试会问一些原理、抽象设计,但是平时很少接触底层的东西,要怎么提升自己呢?")]),t._v(" "),v("br"),t._v(" "),v("ol",[v("li",[v("strong",[t._v("首先,还是那句话:多思考!")])])]),t._v(" "),v("p",[t._v("框架和一些很热门的开源库为什么会火,因为解决了很多开发的痛点。那么,你想要了解的这些工具,它的痛点背景是什么呢?解决方案又是怎样的?为什么要选这样的方案,是否有可以优化的地方呢?")]),t._v(" "),v("p",[t._v("实现其实对很多程序员来说都不是难事,而我们和大神们的区别在于,他们可以想到这样的痛点,同时还能做出方案对比、方案选型、代码设计,做出来其实是最简单的一部分而已。")]),t._v(" "),v("p",[t._v("我们做的很多时候是业务代码,但是业务代码就没有可以进行方案设计、选型、抽象的地方了吗?不,只是我们自己放弃了去做这样的事情,多思考吧,你会发现你的未来大不一样。")]),t._v(" "),v("ol",{attrs:{start:"2"}},[v("li",[v("strong",[t._v("其次,学会写。")])])]),t._v(" "),v("p",[t._v("你得把每次想到的、学到的,都记录下来。一个人的记忆力是有限的,但我们有容量超大的硬盘可以帮我们记住。")]),t._v(" "),v("p",[t._v("不要怕写得不准确,也不要觉得这么简单写下来意义不大。既然会有疑问,意味着未来的你会遇到同样的疑问,而其他人也会遇到。写下来,下次想起来时可以翻开看,同时还能以新的视角来纠正之前的内容。")]),t._v(" "),v("br"),t._v(" "),v("h3",{attrs:{id:"要怎么规划自己的前端职业发展路线呢"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#要怎么规划自己的前端职业发展路线呢"}},[t._v("#")]),t._v(" 要怎么规划自己的前端职业发展路线呢?")]),t._v(" "),v("p",[v("a",{attrs:{href:"https://www.bilibili.com/video/BV1gK4y1N7Qp",target:"_blank",rel:"noopener noreferrer"}},[t._v("《前端开发那些事--前端进阶路线图》"),v("OutboundLink")],1)]),t._v(" "),v("h3",{attrs:{id:"我觉得前端很容易到达天花板-我该转行吗"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#我觉得前端很容易到达天花板-我该转行吗"}},[t._v("#")]),t._v(" 我觉得前端很容易到达天花板,我该转行吗?")]),t._v(" "),v("br"),t._v(" "),v("ol",{attrs:{start:"2"}},[v("li",[t._v("其实,我个人觉得天花板跟职业和岗位关系不大,所谓的天花板更多是个人的天花板。")])]),t._v(" "),v("p",[t._v("相对于抱怨环境,提升自己更重要。怎样提升自己呢?还是那句话,多思考。首先,你要想清楚自己的职业规划,如果你刚毕业还不是很清晰,没有关系,你喜欢什么总该知道吧?找准自己的兴趣,然后可以以“又能做喜欢的事情,又能赚钱”作为目标,看看自己缺什么,然后慢慢补吧。")]),t._v(" "),v("p",[t._v("千万不要急,这个浮躁的社会,最忌讳急。")]),t._v(" "),v("p",[t._v("一个人的潜能,总是跟爱好又很大关系的。所以找准你的爱好,想办法把它变成能让你每天相处得很开心的一份工作吧。不要相信“把爱好变成工作就会失去热爱”这种话,那只是因为不够热爱而已。")]),t._v(" "),v("ol",{attrs:{start:"2"}},[v("li",[t._v("根据自己的兴趣,可以将技术栈往外扩一些(后台、运维、设计、交互都可以)。")])]),t._v(" "),v("p",[t._v("如果只会前端,其实局限性会比较大(像我,切图不会切、设计又丑的不行、后台又不会写、机器运维更是不怎么了解)。这种情况下,不管是从与合作伙伴协作、讨论需求的时候,背景知识的欠缺也会使得沟通不顺畅。")]),t._v(" "),v("p",[t._v("更深一些的想法,程序设计最终是相通的,而程序设计与我们的生活工作其实也是相通的。学习一些其他的思维模式、结构设计,对深入前端领域也好,对视野和思维的拓展也好,都是很不错的。嗯,我也在学,也整理了一份"),v("RouterLink",{attrs:{to:"/front-end-addon/"}},[t._v("前端的进击系列")]),t._v("。希望和你们一起共同进步呀。")],1),t._v(" "),v("h3",{attrs:{id:"最后-祝愿你们都能找到一份喜欢的工作-上班开心地干活-下班和喜爱的人一起-每天都一样幸福。"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#最后-祝愿你们都能找到一份喜欢的工作-上班开心地干活-下班和喜爱的人一起-每天都一样幸福。"}},[t._v("#")]),t._v(" 最后,祝愿你们都能找到一份喜欢的工作,上班开心地干活,下班和喜爱的人一起,每天都一样幸福。")])])}),[],!1,null,null,null);r.default=a.exports}}]);
--------------------------------------------------------------------------------
/assets/js/74.7864f269.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[74],{637:function(v,_,a){"use strict";a.r(_);var t=a(69),e=Object(t.a)({},(function(){var v=this,_=v.$createElement,a=v._self._c||_;return a("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[a("p",[v._v("本文先整体介绍一下我所理解的前端状况。")]),v._v(" "),a("p",[v._v("本系列的目的主要是分享一些个人经验和理解,希望能帮到需要的小伙伴们。同时从头开始的整理和思考,或许对于本骚年自己也有一定的帮助。共勉共勉~")]),v._v(" "),a("h1",{attrs:{id:"什么是前端"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#什么是前端"}},[v._v("#")]),v._v(" 什么是前端")]),v._v(" "),a("h2",{attrs:{id:"连接用户的最后一层"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#连接用户的最后一层"}},[v._v("#")]),v._v(" 连接用户的最后一层")]),v._v(" "),a("p",[v._v("说起来,对前端这样的认知方式,大概是当初加入的原因。")]),v._v(" "),a("p",[v._v("为什么我喜欢称之为连接用户的最后一层呢?因为页面的展示和操作交互,是我们的产品与用户直接对话的一步。\n当然,用户交互的界面很多,除了前端,还有终端、操作系统、等等。")]),v._v(" "),a("p",[v._v("如果说,想要区分哪些是前端呢?这个要跟我们的浏览器紧紧相关了。")]),v._v(" "),a("h2",{attrs:{id:"浏览器带你看遍世界"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#浏览器带你看遍世界"}},[v._v("#")]),v._v(" 浏览器带你看遍世界")]),v._v(" "),a("p",[v._v("浏览器是个伟大的发明(不知道算发明不),把我们的世界从身边的小圈直接扩大到全世界的各个地方。")]),v._v(" "),a("p",[v._v("以前常说,读书能让我们看到外面的世界。而浏览器的出现,让我们可以接受来自不同国家、不同领域的信息。\n浏览器作为跨电脑、手机的应用,只需要打开浏览器,我们就能找到想要的内容,从文字到图片、视频,甚至是游戏,都可以在其中体验。如今手机的普及,更是常常会有 H5 页面,包括一些简单的活动、抽奖、分享信息等。")]),v._v(" "),a("p",[v._v("前端是什么呢?最初的前端,就是写浏览器里面的页面的。像我们常说的网站、网页,或者是百度等,都是前端实现的页面。\n最初的时候,前端主要控制页面的展示,和一些样式的调整。随着网络速度和机器能力的提升,页面的交互逻辑逐渐复杂。")]),v._v(" "),a("p",[v._v("随着前端工程化的一些工具、插件、框架的出现,前端的开发效率逐步提升,同时浏览器的兼容和能力开放增加,前端能做到的事情更多。这里补充一下,前端是通过"),a("code",[v._v("HTML/CSS/Javascript")]),v._v("来写页面的。而浏览器除了对"),a("code",[v._v("HTML/CSS")]),v._v("的渲染,还有"),a("code",[v._v("Javascript")]),v._v("引擎,作为页面的逻辑控制。")]),v._v(" "),a("p",[v._v("目前为止,或许大部分的前端的工作内容还是基于浏览器,但随着浏览器的内核或者是"),a("code",[v._v("Javascript")]),v._v("的解析引擎被移植到各个环境,前端的爪子也伸到很多地方。")]),v._v(" "),a("h2",{attrs:{id:"前端的位置"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#前端的位置"}},[v._v("#")]),v._v(" 前端的位置")]),v._v(" "),a("p",[v._v("一个完整的产品需要很多道工序,一个应用程序也对应很多层的开发。")]),v._v(" "),a("p",[v._v("一般来说,一个网页可以为静态页面,即内容和样式都是前端写好的,部署到机器上,添加路由就可访问。\n现在的话,页面大部分是动态生成的,即页面打开后,需要拉取接口获取数据,然后重新更新到页面中。像一些直播弹幕、状态的查询等,常常是前端将后台的数据拉取回来后,渲染到页面。")]),v._v(" "),a("p",[v._v("浏览器网页的开发组成:"),a("code",[v._v("前端 <=> (数据交互) <=> 后台")]),v._v("。")]),v._v(" "),a("p",[v._v("而如果是多终端的数据展示,则后台的数据则需要同时提供给其他地方。")]),v._v(" "),a("p",[v._v("常见的手机 APP:"),a("code",[v._v("用户 <=> 终端/webview嵌H5 <=> (数据交互) <=> 终端后台 <=> 数据库 <=> 管理后台 <=> 管理前端 <=> 运营人员")]),v._v("。")]),v._v(" "),a("p",[v._v("前端页面既可以作为展示,也可以作为管理,可用于分享,也可用于娱乐。产品的难点,多在于创造和创新,前端也只是一种实现方式而已。\n而本骚年更爱的对前端的理解是,身肩负着与用户最亲密的接触,需要把最好的一面呈现给用户。")]),v._v(" "),a("p",[v._v("如果说我们想要让用户喜欢我们的产品,首先要做的就是要以最完美的形态出现,而前端的工作,就是要完美地控制展示层。")]),v._v(" "),a("h1",{attrs:{id:"前端能做什么"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#前端能做什么"}},[v._v("#")]),v._v(" 前端能做什么")]),v._v(" "),a("h2",{attrs:{id:"纯前端的进击"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#纯前端的进击"}},[v._v("#")]),v._v(" 纯前端的进击")]),v._v(" "),a("p",[v._v("现在,前端可以做的事情很多。")]),v._v(" "),a("h3",{attrs:{id:"服务端"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#服务端"}},[v._v("#")]),v._v(" 服务端")]),v._v(" "),a("p",[v._v("在"),a("code",[v._v("node.js")]),v._v("的强助力之下,前端小伙伴也能管理文件和资源,维护服务进程和数据库了。当然,异步的方式,或许更适合高并发的服务。")]),v._v(" "),a("h3",{attrs:{id:"app-开发"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#app-开发"}},[v._v("#")]),v._v(" App 开发")]),v._v(" "),a("p",[v._v("智能手机的普及,开拓了一大片 App 的市场。对终端的尝试,也是近年来前端圈子一直在做的事情。"),a("br"),v._v("\n有了"),a("code",[v._v("react-native")]),v._v("、"),a("code",[v._v("weex")]),v._v("等各种 Native App、Hybrid App 开发框架支持,前端小伙伴们也能偶尔朝终端 APP 插上一脚。")]),v._v(" "),a("h3",{attrs:{id:"pc-应用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#pc-应用"}},[v._v("#")]),v._v(" PC 应用")]),v._v(" "),a("p",[v._v("用"),a("code",[v._v("electron")]),v._v("这样的框架,将浏览器加一层对接系统 API 的封装,便实现了跨系统的 PC 应用开发。"),a("br"),v._v("\n网易云音乐的 PC 版便是"),a("code",[v._v("electron")]),v._v("的产品,而小伙伴们写代码的"),a("code",[v._v("VS Code")]),v._v("又何尝不是呢。")]),v._v(" "),a("h3",{attrs:{id:"无处不在的-h5-页面"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#无处不在的-h5-页面"}},[v._v("#")]),v._v(" 无处不在的 H5 页面")]),v._v(" "),a("p",[v._v("如今智能手机的普及,更是让 H5 出现在各种信息流中。H5 是什么呢,其实就是移动端的网页,主要用于信息分享、简单的功能、小游戏等等,加载和传播速度快的小页面。"),a("br"),v._v("\nH5 页面主要依赖 App 里的浏览器内核,基本上每个 App 都会支持 H5 页面的。而 HTML5 中"),a("code",[v._v("video")]),v._v("、"),a("code",[v._v("audio")]),v._v("、"),a("code",[v._v("canvas")]),v._v("等新媒体元素,以及 CSS3 中的动画效果,使得用户能在小小的屏幕页面里,获取到各种各样的信息。")]),v._v(" "),a("h3",{attrs:{id:"小程序开发"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#小程序开发"}},[v._v("#")]),v._v(" 小程序开发")]),v._v(" "),a("p",[v._v("从微信开始火起来的小程序,到后面的支付宝小程序、头条小程序、百度小程序、QQ 小程序等等,这种 Hybrid APP 的方式如今也找到了一个较友好的方向来进行:官方 APP 提供增强 WebView 的形式,给到开发者参与到 APP 生态中,共同补齐生态建设能力。")]),v._v(" "),a("h3",{attrs:{id:"serverless"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#serverless"}},[v._v("#")]),v._v(" Serverless")]),v._v(" "),a("p",[v._v("如今各种云开发的能力在健全和推广,例如微信小程序的云开发能力,也补齐了前端开发对服务端开发和运维中缺失的一环,能真正意义上实现一人完成整个小程序,从设计到开发到上线到运维。")]),v._v(" "),a("h2",{attrs:{id:"前端的快速发展"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#前端的快速发展"}},[v._v("#")]),v._v(" 前端的快速发展")]),v._v(" "),a("p",[v._v("前端也有很多的插件或者库的支持,有了"),a("code",[v._v("Canvas")]),v._v("可以写网页游戏、各种图表插件"),a("code",[v._v("Echarts")]),v._v("/"),a("code",[v._v("d3")]),v._v("绘制图表、还有"),a("code",[v._v("WebGL")]),v._v("的支持、"),a("code",[v._v("three.js")]),v._v("的封装库来写 3D 动画或是游戏。")]),v._v(" "),a("p",[v._v("我们也常常看到前端的技术栈不停地更新,样式库"),a("code",[v._v("bootstrap")]),v._v(",曾经打天下的"),a("code",[v._v("jQuery")]),v._v(",如今各种框架之争"),a("code",[v._v("Vue")]),v._v("、"),a("code",[v._v("Angular")]),v._v("、"),a("code",[v._v("React")]),v._v(",数据流的处理"),a("code",[v._v("Rxjs")]),v._v("、用于 API 的查询语言"),a("code",[v._v("GraphQL")]),v._v("。")]),v._v(" "),a("p",[v._v("作为一个前端,也会常常担心跟不上时代变更的角度。如今的年轻人也越来越聪明了,带的小弟关注的东西比你逼格高很多。"),a("br"),v._v("\n但其实也享受这种不断更新的过程,勇于接受挑战,更新和迭代自己,跟随着世界的脚步走。每一步都走稳了,才是最踏实的方式。")]),v._v(" "),a("h2",{attrs:{id:"结束语"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),a("p",[v._v("一直都有想法写这样的系列文章,但不知道怎么下笔。三年前端,学到接触到的东西很多,或许一时半会写不完。"),a("br"),v._v("\n不过拿起键盘开始敲,也算是一种前进的方式吧。")])])}),[],!1,null,null,null);_.default=e.exports}}]);
--------------------------------------------------------------------------------
|