├── assets ├── fonts │ ├── element-icons.732389de.ttf │ └── element-icons.535877f5.woff ├── img │ └── search.83621669.svg └── js │ ├── 5.71213e55.js │ ├── 173.d155f6ed.js │ ├── 40.6f0ddfbc.js │ ├── 7.6d35c89a.js │ ├── 216.679d328a.js │ ├── 199.b27c1a70.js │ ├── 4.45491c7c.js │ ├── 181.d0c4bd7e.js │ ├── 140.3939849a.js │ ├── 59.fe148bb6.js │ ├── 197.7404274a.js │ ├── 112.42bc34a4.js │ ├── 222.7cd90e92.js │ ├── 185.68d803c8.js │ ├── 119.e5921f55.js │ ├── 120.6062503f.js │ ├── 180.0716c8b3.js │ ├── 122.43b0a986.js │ ├── 174.4781a418.js │ ├── 129.03c7dbef.js │ ├── 219.2dd5b970.js │ ├── 183.72b9d858.js │ ├── 175.11101066.js │ ├── 179.756b524a.js │ ├── 186.f0d6f2da.js │ ├── 114.49c02da7.js │ ├── 137.e2e49efd.js │ ├── 187.dd0b26f2.js │ ├── 117.3e7ff2cf.js │ ├── 189.a1f98229.js │ ├── 118.36c96445.js │ ├── 188.9fe1f059.js │ ├── 184.3047e022.js │ ├── 106.c48e18a1.js │ ├── 194.a52f1482.js │ ├── 67.66f4a05e.js │ ├── 178.cff63f2d.js │ ├── 196.738bfc49.js │ ├── 177.e7fc794f.js │ ├── 116.5ca5becb.js │ ├── 193.2bc14220.js │ ├── 23.0a513d2e.js │ ├── 232.891426ab.js │ ├── 124.94e50a0d.js │ ├── 182.8e9147a6.js │ ├── 83.895a2366.js │ ├── 233.b59df4de.js │ ├── 130.68596fae.js │ ├── 190.5dbb05ea.js │ ├── 108.7a7761a8.js │ ├── 128.75dd9b06.js │ ├── 39.b13ae703.js │ └── 74.7864f269.js └── 404.html /assets/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godbasin/front-end-playground/HEAD/assets/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /assets/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godbasin/front-end-playground/HEAD/assets/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /assets/img/search.83621669.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/js/5.71213e55.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{534:function(t,e,n){},564:function(t,e,n){"use strict";n(534)},812:function(t,e,n){"use strict";n.r(e);var i={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,e){var n=e.props,i=e.slots;return t("span",{class:["badge",n.type],style:{verticalAlign:n.vertical}},n.text||i().default)}},r=(n(564),n(69)),p=Object(r.a)(i,void 0,void 0,!1,null,"76652c5d",null);e.default=p.exports}}]); -------------------------------------------------------------------------------- /assets/js/173.d155f6ed.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[173],{735:function(t,s,n){"use strict";n.r(s);var e=n(69),r=Object(e.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"前端与工作"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#前端与工作"}},[this._v("#")]),this._v(" 前端与工作")]),this._v(" "),s("p",[this._v("本部分内容与工作相关,分享包括面试经验、工作经历等过程。")]),this._v(" "),s("p",[s("RouterLink",{attrs:{to:"/front-end-work/front-end-days/about-front-end-1-begin-in.html"}},[this._v("开始阅读 ->")])],1)])}),[],!1,null,null,null);s.default=r.exports}}]); -------------------------------------------------------------------------------- /assets/js/40.6f0ddfbc.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[40],{603:function(t,e,r){"use strict";r.r(e);var n=r(69),a=Object(n.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"前端的进击"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#前端的进击"}},[t._v("#")]),t._v(" 前端的进击")]),t._v(" "),r("p",[t._v("该块内容主要用于纯前端的一次跨端进击,我们需要了解自己的合作伙伴(后台、运维、设计、交互),才能做更好的合作,共同完成美好的作品。")]),t._v(" "),r("p",[t._v("同时这些内容,也会对要准备面试的你有所帮助噢~")]),t._v(" "),r("p",[r("RouterLink",{attrs:{to:"/front-end-addon/more-than-single-thread/1-thread-and-progress.html"}},[t._v("开始学习 ->")])],1)])}),[],!1,null,null,null);e.default=a.exports}}]); -------------------------------------------------------------------------------- /assets/js/7.6d35c89a.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[7],{570:function(t,r,a){"use strict";a.r(r);var n=a(69),e=Object(n.a)({},(function(){var t=this,r=t.$createElement,a=t._self._c||r;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"angular"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#angular"}},[t._v("#")]),t._v(" Angular")]),t._v(" "),a("p",[t._v("如果说 Vue 是小而清、上手容易,那么 Angular 正好相反,它加了很多的工程思想在里面。或许会有些笨重,但依然无法避免它是我最爱的一个框架,我觉得很值得去了解和认识它。")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://angular.cn/",target:"_blank",rel:"noopener noreferrer"}},[t._v("官方文档"),a("OutboundLink")],1)])]),t._v(" "),a("p",[a("RouterLink",{attrs:{to:"/angular/deep-into-angular/angular-design-0-prestart.html"}},[t._v("Angular 源码研究 ->")])],1)])}),[],!1,null,null,null);r.default=e.exports}}]); -------------------------------------------------------------------------------- /assets/js/216.679d328a.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[216],{777:function(e,t,r){"use strict";r.r(t);var a=r(69),n=Object(a.a)({},(function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[r("h1",{attrs:{id:"小程序学习"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#小程序学习"}},[e._v("#")]),e._v(" 小程序学习")]),e._v(" "),r("p",[e._v("如今,小程序开发已经成了前端开发中比较重要的一项能力了。")]),e._v(" "),r("h2",{attrs:{id:"官方资源"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#官方资源"}},[e._v("#")]),e._v(" 官方资源")]),e._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/miniprogram/dev/framework/",target:"_blank",rel:"noopener noreferrer"}},[e._v("微信官方·小程序文档"),r("OutboundLink")],1)]),e._v(" "),r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/question",target:"_blank",rel:"noopener noreferrer"}},[e._v("微信开放社区"),r("OutboundLink")],1)]),e._v(" "),r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/ebook?action=get_post_info&volumn=1&lang=zh_CN&book=miniprogram&docid=0008aeea9a8978ab0086a685851c0a",target:"_blank",rel:"noopener noreferrer"}},[e._v("小程序开发指南"),r("OutboundLink")],1)]),e._v(" "),r("li",[r("a",{attrs:{href:"https://github.com/wechat-miniprogram",target:"_blank",rel:"noopener noreferrer"}},[e._v("官方 github 团队: wechat-miniprogram"),r("OutboundLink")],1)])]),e._v(" "),r("p",[r("RouterLink",{attrs:{to:"/wxapp/wxapp-principle/1-wxapp-generate.html"}},[e._v("开始学习 ->")])],1)])}),[],!1,null,null,null);t.default=n.exports}}]); -------------------------------------------------------------------------------- /assets/js/199.b27c1a70.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[199],{760:function(e,r,t){"use strict";t.r(r);var n=t(69),o=Object(n.a)({},(function(){var e=this,r=e.$createElement,t=e._self._c||r;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h1",{attrs:{id:"vue-学习"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#vue-学习"}},[e._v("#")]),e._v(" Vue 学习")]),e._v(" "),t("h2",{attrs:{id:"官方资源"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#官方资源"}},[e._v("#")]),e._v(" 官方资源")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://cn.vuejs.org/v2/guide/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Vue.js 官方文档"),t("OutboundLink")],1)])]),e._v(" "),t("h2",{attrs:{id:"周边工具"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#周边工具"}},[e._v("#")]),e._v(" 周边工具")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://cli.vuejs.org/zh/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Vue Cli 脚手架"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://github.com/vuejs/vue-devtools",target:"_blank",rel:"noopener noreferrer"}},[e._v("vue-devtools"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://router.vuejs.org/zh/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Vue Router"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://vuex.vuejs.org/zh/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Vuex"),t("OutboundLink")],1)])]),e._v(" "),t("p",[t("RouterLink",{attrs:{to:"/vue/vue-for-everyone/vue-for-everyone-1.html"}},[e._v("开始学习 ->")])],1)])}),[],!1,null,null,null);r.default=o.exports}}]); -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 被删的前端游乐场 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

404

16 | How did we get here? 17 |
Take me home.
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /assets/js/4.45491c7c.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[4],{508:function(t,e,s){var a=s(45),i=Date.prototype,o=i.toString,r=i.getTime;new Date(NaN)+""!="Invalid Date"&&a(i,"toString",(function(){var t=r.call(this);return t==t?o.call(this):"Invalid Date"}))},511:function(t,e,s){},537:function(t,e,s){"use strict";s(511)},567:function(t,e,s){"use strict";s.r(e);s(508);function a(){return"https://api.thecatapi.com/v1/images/search?format=src&t="+(new Date).getTime()}var i=["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],o={methods:{getMsg:function(){return i[Math.floor(Math.random()*i.length)]}},data:function(){return{imgSrc:a()}},watch:{$route:function(t){this.imgSrc=a()}}},r=(s(537),s(69)),n=Object(r.a)(o,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"theme-container"},[s("div",{staticClass:"theme-default-content"},[s("h1",[t._v("404")]),t._v(" "),s("blockquote",[t._v("\n "+t._s(t.getMsg())+"\n "),s("el-row",{attrs:{gutter:20}},[s("el-col",{attrs:{xs:24,sm:12}},[s("el-image",{staticClass:"image-container",attrs:{src:t.imgSrc}},[s("div",{staticClass:"image-slot",attrs:{slot:"placeholder"},slot:"placeholder"},[s("img",{staticStyle:{width:"100%"},attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif"}})]),t._v(" "),s("div",{staticClass:"image-slot",attrs:{slot:"error"},slot:"error"},[s("img",{staticStyle:{width:"100%"},attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/chunzhu.jpg"}})])])],1)],1)],1),t._v(" "),s("router-link",{attrs:{to:"/"}},[t._v("Take me home.")])],1)])}),[],!1,null,null,null);e.default=n.exports}}]); -------------------------------------------------------------------------------- /assets/js/181.d0c4bd7e.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[181],{742: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("今年在互联网行业工作的大家,想必大概都有所感受。去年还在想方设法招聘应届生的许多公司,今年温度骤降,裁员的裁员,毁 offer 的毁 offer。")]),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("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("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("h3",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("其实有时候会想,这行业的领导还挺好当的。当然,好的老板很难当,但只是个老板的话,感觉还挺简单的,反正事情做好了都归功老板,出问题了底下人背锅。即使是在所谓的冰河世纪,裁员也会先裁底下员工,就算裁到 leader,也可以拿着大家做的成果出去轻松找到下一份工作。")]),v._v(" "),t("p",[v._v("虽然他们也常常说 leader 不好当,压力大事情多。但如果真的只是徒增压力和责任,没有其他收益,大概也不会那么多人争破头去抢这样的位置了吧。")])])}),[],!1,null,null,null);_.default=r.exports}}]); -------------------------------------------------------------------------------- /assets/js/140.3939849a.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[140],{701:function(t,r,e){"use strict";e.r(r);var a=e(69),s=Object(a.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"其他前端框架-库学习"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#其他前端框架-库学习"}},[t._v("#")]),t._v(" 其他前端框架/库学习")]),t._v(" "),e("p",[t._v("一些有意思的前端框架、前端库的笔记。")]),t._v(" "),e("h2",{attrs:{id:"react"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#react"}},[t._v("#")]),t._v(" React")]),t._v(" "),e("p",[t._v("作为前端三大框架之一,又怎能少了 React 呢?可惜本人有很长一段时间没用使用 React 了,感觉有些落后,所以大家可以去官方网站看看好了(感兴趣的也可以去"),e("a",{attrs:{href:"https://github.com/godbasin/godbasin.github.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("我的博客"),e("OutboundLink")],1),t._v("翻阅很旧的笔记)。")]),t._v(" "),e("ul",[e("li",[e("a",{attrs:{href:"https://zh-hans.reactjs.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("官方文档"),e("OutboundLink")],1)])]),t._v(" "),e("h2",{attrs:{id:"cycle-js"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#cycle-js"}},[t._v("#")]),t._v(" Cycle.js")]),t._v(" "),e("p",[t._v("Cycle.js 大概是将 Rxjs 贯彻到底了,但是整个库的思想很好玩,会改变你对一些习惯性绑定的思维,你会看到整个框架、甚至是你的项目,都流动了起来。")]),t._v(" "),e("ul",[e("li",[e("a",{attrs:{href:"https://cycle.js.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("官方文档"),e("OutboundLink")],1)])]),t._v(" "),e("p",[e("RouterLink",{attrs:{to:"/front-end-others/cycle-js-test/1-init-app-with-webpack.html"}},[t._v("开始学习 ->")])],1),t._v(" "),e("h2",{attrs:{id:"d3-js"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#d3-js"}},[t._v("#")]),t._v(" D3.js")]),t._v(" "),e("p",[t._v("当时使用 D3.js 的时候,Echarts 并未支持 Tree 的结构,而 D3 对灵活性和自定义的支持程度更好,同时使用的过程你能感受到链式调用的魅力。")]),t._v(" "),e("ul",[e("li",[e("a",{attrs:{href:"https://d3js.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("官方文档"),e("OutboundLink")],1)])]),t._v(" "),e("p",[e("RouterLink",{attrs:{to:"/front-end-others/d3-tree/1-svg-and-d3.html"}},[t._v("开始学习 ->")])],1),t._v(" "),e("h2",{attrs:{id:"three-js"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#three-js"}},[t._v("#")]),t._v(" Three.js")]),t._v(" "),e("p",[t._v("Three.js 是一个 3D 库,封装了 WebGL,对开发者很友好的、又很酷一个库。")]),t._v(" "),e("ul",[e("li",[e("a",{attrs:{href:"https://threejs.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("官方文档"),e("OutboundLink")],1)])]),t._v(" "),e("p",[e("RouterLink",{attrs:{to:"/front-end-others/three-js-test/1-init-3d-world.html"}},[t._v("开始学习 ->")])],1),t._v(" "),e("h2",{attrs:{id:"box2d-js"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#box2d-js"}},[t._v("#")]),t._v(" BOX2D.js")]),t._v(" "),e("p",[t._v("在以前端作为职业两年的这个时候,才发现 Box2D 相关的教程和说明很少,便产生了整理一份相关的说明和教程的想法。")]),t._v(" "),e("p",[t._v("或许 Box2Djs 已停止了维护,但我依然认为它是一个很棒很棒的库。")]),t._v(" "),e("p",[t._v("该教程中有参考当年一起参与项目的师兄师姐们的一些资料,整合了自己的研究和分析,内容可能有缺失或者错误,但还是希望能帮助到大家,更期待你们的指正和完善。")]),t._v(" "),e("p",[e("RouterLink",{attrs:{to:"/front-end-others/box2d-tutorial/1-0-basic-catalog.html"}},[t._v("开始学习 ->")])],1)])}),[],!1,null,null,null);r.default=s.exports}}]); -------------------------------------------------------------------------------- /assets/js/59.fe148bb6.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[59],{620:function(r,e,t){"use strict";t.r(e);var n=t(69),a=Object(n.a)({},(function(){var r=this,e=r.$createElement,t=r._self._c||e;return t("ContentSlotsDistributor",{attrs:{"slot-key":r.$parent.slotKey}},[t("h1",{attrs:{id:"前端基础"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#前端基础"}},[r._v("#")]),r._v(" 前端基础")]),r._v(" "),t("h2",{attrs:{id:"学习资源"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#学习资源"}},[r._v("#")]),r._v(" 学习资源")]),r._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://developer.mozilla.org/en-US/",target:"_blank",rel:"noopener noreferrer"}},[r._v("MDN"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"http://www.w3school.com.cn/",target:"_blank",rel:"noopener noreferrer"}},[r._v("w3school"),t("OutboundLink")],1)])]),r._v(" "),t("h2",{attrs:{id:"练习空间"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#练习空间"}},[r._v("#")]),r._v(" 练习空间")]),r._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://www.freecodecamp.org/",target:"_blank",rel:"noopener noreferrer"}},[r._v("freeCodeCamp"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"https://www.freecodecamp.org/",target:"_blank",rel:"noopener noreferrer"}},[r._v("freeCodeCamp 中文社区"),t("OutboundLink")],1),r._v(" "),t("blockquote",[t("p",[r._v("可以直接选择右上角“课程”选择对应的题目")])])])]),r._v(" "),t("h2",{attrs:{id:"团队空间"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#团队空间"}},[r._v("#")]),r._v(" 团队空间")]),r._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"http://www.alloyteam.com/category/webdevelop/",target:"_blank",rel:"noopener noreferrer"}},[r._v("AlloyTeam"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"http://imweb.io",target:"_blank",rel:"noopener noreferrer"}},[r._v("IMWEB"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"https://github.com/ElemeFE",target:"_blank",rel:"noopener noreferrer"}},[r._v("饿了么前端"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"http://taobaofed.org",target:"_blank",rel:"noopener noreferrer"}},[r._v("淘宝前端团队"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"http://fex.baidu.com",target:"_blank",rel:"noopener noreferrer"}},[r._v("百度 Web 前端研发部"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"https://aotu.io",target:"_blank",rel:"noopener noreferrer"}},[r._v("京东凹凸实验室"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"https://75.team",target:"_blank",rel:"noopener noreferrer"}},[r._v("360 奇舞团"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"http://blog.qiniu.com",target:"_blank",rel:"noopener noreferrer"}},[r._v("七牛团队技术博客"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"http://tech.youzan.com",target:"_blank",rel:"noopener noreferrer"}},[r._v("有赞技术团队"),t("OutboundLink")],1)])]),r._v(" "),t("h2",{attrs:{id:"博客资源"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#博客资源"}},[r._v("#")]),r._v(" 博客资源")]),r._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"http://www.ruanyifeng.com/blog/javascript/",target:"_blank",rel:"noopener noreferrer"}},[r._v("阮一峰的网络日志"),t("OutboundLink")],1)]),r._v(" "),t("li",[t("a",{attrs:{href:"https://www.zhangxinxu.com/wordpress/",target:"_blank",rel:"noopener noreferrer"}},[r._v("鑫空间,鑫生活"),t("OutboundLink")],1)])]),r._v(" "),t("p",[t("RouterLink",{attrs:{to:"/front-end-basic/front-end/front-end-1.html"}},[r._v("开始学习 ->")])],1)])}),[],!1,null,null,null);e.default=a.exports}}]); -------------------------------------------------------------------------------- /assets/js/197.7404274a.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[197],{761: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("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("使用技术相关的开放题,既可以考察面试对象对技术的掌握和灵活使用的程度。可能会包括以下的问题(内容来自"),t("a",{attrs:{href:"https://github.com/airuikun/Weekly-FE-Interview",target:"_blank",rel:"noopener noreferrer"}},[v._v("Weekly-FE-Interview"),t("OutboundLink")],1),v._v("):")]),v._v(" "),t("ul",[t("li",[v._v("一次性插入 1000 个 div,如何优化插入的性能")]),v._v(" "),t("li",[v._v("内存不足,一次只允许你装载和操作 1 亿条数据,如何对 100 亿条数据进行排序")]),v._v(" "),t("li",[v._v("在浏览器端,用 JS 存储 2 万个小球的信息,包含小球的大小,位置,颜色等,如何做到对这2万条小球信息进行最优检索和存储")])]),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("li",[v._v("现在需要通过配置的方式生成各种各样的 H5 活动页面,如何实现")])]),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("一个 5L、一个 6L 的瓶子,要得到 3L 的水,要怎么做")]),v._v(" "),t("li",[v._v("在一天之中,时针和分针共重叠多少次,时间分别是多少")]),v._v(" "),t("li",[v._v("1=5,2=15,3=215,4=2145,那么 5=?")]),v._v(" "),t("li",[v._v("现有 64 匹马,8 赛道,至少跑多少个回合可以得到前 4 名")])]),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("请把一盒蛋糕切成8份,分给8个人,但蛋糕盒里还必须留有一份")])]),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("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("日常工作有难度吗,难点在哪")]),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("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("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/112.42bc34a4.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[112],{674: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("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("关于复杂度,相信所有开发都了解这个词。在计算机领域,复杂度常常出现在程序算法中,主要包括:时间复杂度、空间复杂度。")]),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("ul",[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("之前在"),t("RouterLink",{attrs:{to:"/front-end-basic/performance/front-end-performance-website-quality-score.html"}},[v._v("《前端性能优化--网页质量得分》")]),v._v("一文中,有介绍跟进一些用户体验的影响指标,来建设网页的质量得分数据。")],1),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("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("网页 DOM 元素数量:该因素可定义为常数项。在理想情况中,用户体验不应该受到网页元素数量的影响(因为大多数都会做首屏渲染和分片渲染),即仅有一屏内容和 10 屏、20 屏内容的网页体验相近,因此时间复杂度理想情况下应该为常数,但现实中显然不是这样。")]),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("ul",[t("li",[v._v("JavaScript 包大小:代码解析过程中会占用内存")]),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("假设我们认为网页 DOM 元素数量会同时影响网页的时间复杂度和空间复杂度,那么我们可以捞取大盘数据来验证当中的关系,并尝试使用函数关系来表达复杂度的定义。")]),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("很多时候,我们都觉得一些指标或许不能代表什么,也经常会执着于它的准确精确与否。但除此之外,对于大盘用户的现状,往往也只能用“机器性能差”、“复杂”等字眼来描述,而可量化则是我们验证许多方案有效性的重要前提。")])])}),[],!1,null,null,null);_.default=r.exports}}]); -------------------------------------------------------------------------------- /assets/js/222.7cd90e92.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[222],{785:function(e,t,r){"use strict";r.r(t);var a=r(69),o=Object(a.a)({},(function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[r("p",[e._v("太可惜了这个月啥都没有~~")]),e._v(" "),r("h1",{attrs:{id:"小程序-latest"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#小程序-latest"}},[e._v("#")]),e._v(" 小程序 latest")]),e._v(" "),r("h2",{attrs:{id:"小程序能力"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#小程序能力"}},[e._v("#")]),e._v(" 小程序能力")]),e._v(" "),r("h3",{attrs:{id:"「2-7-1-版本基础库」新增功能"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#「2-7-1-版本基础库」新增功能"}},[e._v("#")]),e._v(" 「2.7.1 版本基础库」新增功能")]),e._v(" "),r("ul",[r("li",[e._v("新增云开发 Network 面板,"),r("a",{attrs:{href:"https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/debug.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("详情"),r("OutboundLink")],1)]),e._v(" "),r("li",[e._v("更新组件 cover-view 开发者工具支持")]),e._v(" "),r("li",[e._v("其他社区反馈问题修复")])]),e._v(" "),r("p",[e._v("更多 2.7.1 版本基础库的新能力及详情,可查看"),r("a",{attrs:{href:"https://developers.weixin.qq.com/miniprogram/dev/framework/release/",target:"_blank",rel:"noopener noreferrer"}},[e._v("《基础库更新日志》"),r("OutboundLink")],1),e._v("。")]),e._v(" "),r("h3",{attrs:{id:"「微信开放社区」上线帖子相关问题能力"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#「微信开放社区」上线帖子相关问题能力"}},[e._v("#")]),e._v(" 「微信开放社区」上线帖子相关问题能力")]),e._v(" "),r("p",[r("img",{attrs:{src:"http://km.oa.com/files/photos/pictures//20190617//1560737512_91.png",alt:"帖子相关"}})]),e._v(" "),r("h3",{attrs:{id:"更新日志"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#更新日志"}},[e._v("#")]),e._v(" 更新日志")]),e._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/doc/00048cfe698378e701b86d72c56c01",target:"_blank",rel:"noopener noreferrer"}},[e._v("社区问题反馈以及功能优化更新(06.03-06.06)"),r("OutboundLink")],1)]),e._v(" "),r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/doc/000e00bc1d81e051aca8b759f56401",target:"_blank",rel:"noopener noreferrer"}},[e._v("社区问题反馈以及功能优化更新(05.27-05.31)"),r("OutboundLink")],1)]),e._v(" "),r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/doc/0002e212b94b58861ba81bbda56801",target:"_blank",rel:"noopener noreferrer"}},[e._v("社区问题反馈以及功能优化更新(05.20-05.24)"),r("OutboundLink")],1)]),e._v(" "),r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/doc/00000ec37743f0906a98518df51801",target:"_blank",rel:"noopener noreferrer"}},[e._v("社区问题反馈以及功能优化更新(05.13-05.17)"),r("OutboundLink")],1)])]),e._v(" "),r("h1",{attrs:{id:"小程序教程"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#小程序教程"}},[e._v("#")]),e._v(" 小程序教程")]),e._v(" "),r("h2",{attrs:{id:"社区精选文章"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#社区精选文章"}},[e._v("#")]),e._v(" 社区精选文章")]),e._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article/doc/0000c42fea0668ff36b80d20451813",target:"_blank",rel:"noopener noreferrer"}},[e._v("小程序开发另类小技巧 --用户授权篇"),r("OutboundLink")],1)]),e._v(" "),r("li",[r("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article/doc/0008a4c4f28f30fe3eb863b2750813",target:"_blank",rel:"noopener noreferrer"}},[e._v("浅谈小程序运行机制"),r("OutboundLink")],1)])]),e._v(" "),r("p",[e._v("更多可以查看"),r("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article",target:"_blank",rel:"noopener noreferrer"}},[e._v("文章分享"),r("OutboundLink")],1),e._v("。")]),e._v(" "),r("h2",{attrs:{id:"最新踩坑-tips"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#最新踩坑-tips"}},[e._v("#")]),e._v(" 最新踩坑 && Tips")]),e._v(" "),r("ol",[r("li",[e._v("Promise.then 有些情况下不被调用。查了一下基础库的 bug 历史,以前确实有发现过部分 iOS 系统版本上原生 Promise then 不触发的情况,具体触发条件不明。建议考虑自己打个 polyfill 试试。")])]),e._v(" "),r("blockquote",[r("p",[e._v("可以写段检测 Promise 是否生效的逻辑,然后考虑兜底兼容例如降级到 H5 之类的...")])]),e._v(" "),r("h1",{attrs:{id:"结束语"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[e._v("#")]),e._v(" 结束语")]),e._v(" "),r("p",[e._v("看起来这个月踩坑不多,为啥还是那么忙呢?")])])}),[],!1,null,null,null);t.default=o.exports}}]); -------------------------------------------------------------------------------- /assets/js/185.68d803c8.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[185],{749: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("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("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("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("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("如果依赖别人的反馈来作为坚持的动力,那事情就会变得很被动。但如果我们自己能给自己提供乐趣,那是不是就可以沉迷在这个过程中呢。")]),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=s.exports}}]); -------------------------------------------------------------------------------- /assets/js/119.e5921f55.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[119],{680:function(v,a,_){"use strict";_.r(a);var t=_(69),n=Object(t.a)({},(function(){var v=this,a=v.$createElement,_=v._self._c||a;return _("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[_("p",[v._v("前面提到了渲染引擎的几种渲染方式,如果我们要在与用户交互的过程中识别到具体的元素,又该如何处理呢?")]),v._v(" "),_("h2",{attrs:{id:"canvas-元素选择的难题"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#canvas-元素选择的难题"}},[v._v("#")]),v._v(" Canvas 元素选择的难题")]),v._v(" "),_("p",[v._v("在"),_("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-bottom-render-architecture.html"}},[v._v("《复杂渲染引擎架构与设计--3.底层渲染适配》")]),v._v("一文中,我们介绍了不同的渲染方式,包括 Canvas 渲染、DOM 渲染、SVG 渲染甚至是 WebGL 渲染等。对于不同的渲染方式,要实现元素选取的代价十分不一样。")],1),v._v(" "),_("p",[v._v("对于 DOM/SVG 渲染,我们可以直接使用浏览器提供的元素选择能力。在这样的场景下,不管是父子元素的管理、事件冒泡和捕获等都比较容易实现。因此,我们今天主要讨论 Canvas 渲染要如何实现元素选择。")]),v._v(" "),_("p",[v._v("对于 Canvas 渲染里进行元素选取,我们常见有几种方式:")]),v._v(" "),_("ol",[_("li",[v._v("几何检测法。")]),v._v(" "),_("li",[v._v("像素检测法。")]),v._v(" "),_("li",[v._v("Canvas + DOM 绘制交互。")])]),v._v(" "),_("h3",{attrs:{id:"几何检测法"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#几何检测法"}},[v._v("#")]),v._v(" 几何检测法")]),v._v(" "),_("p",[v._v("几何检测法是许多游戏引擎或者说物理引擎的解决方案,我们常常又称为碰撞检测法。")]),v._v(" "),_("p",[v._v("在元素选取的场景下,我们只需要判断用户触发事件的位置,是否落在某个元素几何里。因此,我们面临的问题是:确定某个点是否位于给定的多边形内。")]),v._v(" "),_("p",[v._v("一般来说,某个点是否在某个多边形内,常见的便是交叉数法(也称射线判断法):从所讨论的点 P 向任何方向射出一条射线(半线),判断该射线与元素几何相交的线段数奇偶情况。在该方法中,我们需要确保射线不会直接射到多边形的任何顶点,这个是比较难做到的。因此,也有不少改良的方案,具体可以参考"),_("a",{attrs:{href:"https://web.cs.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html",target:"_blank",rel:"noopener noreferrer"}},[v._v("《When is a Point Inside a Polygon?》"),_("OutboundLink")],1),v._v("。")]),v._v(" "),_("p",[v._v("除了交叉数法,还有环绕数法,这里不进行详细解释了,具体可以参考"),_("a",{attrs:{href:"https://zhuanlan.zhihu.com/p/436494294",target:"_blank",rel:"noopener noreferrer"}},[v._v("《Canvas 中判断点是否在图形上》"),_("OutboundLink")],1),v._v("。")]),v._v(" "),_("p",[v._v("几何检测法在渲染引擎中使用,优势在于内存消耗小。但它也存在一些问题:")]),v._v(" "),_("ol",[_("li",[v._v("需要维护一个图形检测算法库。")]),v._v(" "),_("li",[v._v("对于复杂的曲线图形计算量很大。")])]),v._v(" "),_("p",[v._v("除此之外,如果元素存在堆叠的情况,则可能需要遍历地进行检测判断;如果存在的元素数量特别庞大,则意味着这样的遍历性能可能会受到影响。")]),v._v(" "),_("h3",{attrs:{id:"像素检测法"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#像素检测法"}},[v._v("#")]),v._v(" 像素检测法")]),v._v(" "),_("p",[v._v("像素检测法又称色值法,简单来说就是给每个绘制的图案按照堆叠顺序绘制一个带随机背景色的一样的图案,当某个点落在 Canvas 上时,则可以通过所在的像素颜色,找到对应的几何元素。")]),v._v(" "),_("p",[v._v("这个方法看似简单,实际上我们需要使用两个 Canvas 来实现:")]),v._v(" "),_("ul",[_("li",[v._v("一个用于真实渲染使用,绘制最终用户可见的内容")]),v._v(" "),_("li",[v._v("另一个用于交互使用,带背景色的图形将会绘制在这个 Canvas 上")])]),v._v(" "),_("p",[v._v("当用户进行交互时,通过 Canvas 位置找出第二个 Canvas 的颜色,然后根据色值去获取到对照的图形。这便要求我们每个图形在绘制前,都需要生成一个元素与随机色值的映射表,通过该表才能获取到最终的元素。")]),v._v(" "),_("p",[v._v("像素检测法的实现很简单,这也是它的优势,但是同样会存在一些问题:")]),v._v(" "),_("ul",[_("li",[v._v("维护两个 Canvas 需要一定的成本")]),v._v(" "),_("li",[v._v("如果绘制频繁,或者绘制内容重新渲染频繁,则该方法也会存在性能问题")])]),v._v(" "),_("p",[v._v("如果考虑到像在线表格这样的产品,由于还需要滚动和重绘,像素检测法的性能或许不会很好。而表格本身就是天然四方形的布局,因此更适合使用几何检测法,而在使用几何检测法的时候,我们甚至只需要判断某个点是否落在某个矩形内,几乎涉及不到较复杂的算法。")]),v._v(" "),_("h3",{attrs:{id:"canvas-dom-交互"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#canvas-dom-交互"}},[v._v("#")]),v._v(" Canvas + DOM 交互")]),v._v(" "),_("p",[v._v("对于一些复杂的交互场景,我们可以适当地添加 DOM 元素来降低维护成本。")]),v._v(" "),_("p",[v._v("比如,在线表格的交互中,很多时候我们都需要先选中一些格子,然后再进行操作。那么,我们可以先使用简单的几何检测法来获取到对应单元格位置,然后生成一个对应的 DOM 元素覆盖在对应的 Canvas 上,之后所有的交互都由这个 DOM 元素来完成。")]),v._v(" "),_("p",[v._v("显然,像输入编辑这种功能,是无法完全使用 Canvas 实现的,或者说是成本巨大,因此我们可以直接使用一个 DOM 的编辑框放在 Canvas 上面,等用户完成编辑操作,再把内容同步到 Canvas 上即可。")]),v._v(" "),_("p",[v._v("这是一种比较简单又取巧的解决方案,但同样需要考虑一些问题:")]),v._v(" "),_("ul",[_("li",[v._v("页面滚动的时候,DOM 元素是否需要跟随滚动")]),v._v(" "),_("li",[v._v("页面发生变化的时候,DOM 元素是否需要刷新")])]),v._v(" "),_("h2",{attrs:{id:"结束语"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),_("p",[v._v("本文主要介绍了 Canvas 里实现元素获取和事件处理的几种解决方案。")]),v._v(" "),_("p",[v._v("其实我们并不是所有时候都需要硬啃复杂的算法或是解决方案,换一下思路,其实你会发现有无数的方向可以尝试。虽然很多时候,我们常说要参考业界常见成熟的方案,但这并不意味着我们就一定要照抄。")]),v._v(" "),_("p",[v._v("适合自己的才是最好的,不管它是大众的方案,还是某种特殊场景下的解决方案。")])])}),[],!1,null,null,null);a.default=n.exports}}]); -------------------------------------------------------------------------------- /assets/js/120.6062503f.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[120],{681:function(e,a,r){"use strict";r.r(a);var v=r(69),n=Object(v.a)({},(function(){var e=this,a=e.$createElement,r=e._self._c||a;return r("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[r("p",[e._v("前面我们介绍了增量渲染的解决方案,其中有提到复用 Canvas 进行性能优化的解决方案。")]),e._v(" "),r("p",[e._v("本文我们将结合 Canvas 的能力提出进一步的优化方案:离屏渲染。")]),e._v(" "),r("p",[e._v("上一篇"),r("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-diff-render.html"}},[e._v("《6.增量渲染》")]),e._v("提到页面滚动时 Canvas 复用的场景,这种场景下我们还可以考虑两种方式:")],1),e._v(" "),r("ol",[r("li",[e._v("使用 Canvas 上一帧画像,直接转换成图片复用到下一帧的绘制中。")]),e._v(" "),r("li",[e._v("维护两个 Canvas,滚动时使用两个 Canvas 交替绘制。")])]),e._v(" "),r("p",[e._v("第二种方式中,当前渲染的 Canvas 与隐藏的缓存 Canvas 交替渲染,由于会使用一个屏幕外(非可视)的 Canvas 进行提前绘制,我们也可以称之为离屏渲染。")]),e._v(" "),r("h2",{attrs:{id:"离屏渲染"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#离屏渲染"}},[e._v("#")]),e._v(" 离屏渲染")]),e._v(" "),r("p",[e._v("离屏渲染可以提前将更大范围的内容绘制好,在滚动时可直接取对应的区域进行截取和绘制。")]),e._v(" "),r("p",[e._v("当然,两个 Canvas 的维护和绘制成本会比一个 Canvas 要更高,同时如果需要提前绘制更大区域的单元格范围,那么必然会面临一个问题:需要更多的计算和渲染消耗。")]),e._v(" "),r("p",[e._v("我们可以考虑另外一个优化方案:使用 "),r("a",{attrs:{href:"https://developer.mozilla.org/zh-CN/docs/Web/API/OffscreenCanvas",target:"_blank",rel:"noopener noreferrer"}},[e._v("OffscreenCanvas"),r("OutboundLink")],1),e._v(" 实现真正的离屏。")]),e._v(" "),r("h3",{attrs:{id:"offscreencanvas-api-能力"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#offscreencanvas-api-能力"}},[e._v("#")]),e._v(" OffscreenCanvas API 能力")]),e._v(" "),r("p",[e._v("OffscreenCanvas 是一个实验中的新特性,主要用于提升 Canvas 2D/3D 绘图应用和 H5 游戏的渲染性能和使用体验。OffscreenCanvas 目前主要用于两种不同的使用场景:")]),e._v(" "),r("ol",[r("li",[e._v("同步显示 OffscreenCanvas 中的帧。在 Worker 线程创建一个 OffscreenCanvas 做后台渲染,然后再把渲染好的缓冲区 Transfer 回主线程显示。")]),e._v(" "),r("li",[e._v("异步显示 OffscreenCanvas 中的帧。主线程从当前 DOM 树中的 Canvas 元素产生一个 OffscreenCanvas,再把这个 OffscreenCanvas 发送给 Worker 线程进行渲染,渲染的结果直接 Commit 到浏览器的 Display Compositor 输出到当前窗口,相当于在 Worker 线程直接更新 Canvas 元素的内容。")])]),e._v(" "),r("p",[e._v("整体的离屏方案依赖 "),r("a",{attrs:{href:"https://developer.mozilla.org/zh-CN/docs/Web/API/OffscreenCanvas",target:"_blank",rel:"noopener noreferrer"}},[e._v("OffscreenCanvas"),r("OutboundLink")],1),e._v(" 提供的能力,关于此能力现有的技术方案和文档较少,可参考:")]),e._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://zhuanlan.zhihu.com/p/34698375",target:"_blank",rel:"noopener noreferrer"}},[e._v("OffscreenCanvas - 概念说明及使用解析"),r("OutboundLink")],1)]),e._v(" "),r("li",[r("a",{attrs:{href:"https://developers.google.com/web/updates/2018/08/offscreen-canvas",target:"_blank",rel:"noopener noreferrer"}},[r("OutboundLink")],1)])]),e._v(" "),r("p",[e._v("在我们的架构设计下,更适合使用第一种方案,即同步显示 OffscreenCanvas 中的帧。这样设计的优势在于:当主线程繁忙时,依然可以通过 OffscreenCanvas 在 worker 中更新画布内容,避免给用户造成页面卡顿的体验。")]),e._v(" "),r("p",[e._v("除此之外,还可以进一步考虑在兼容性支持的情况下,通过将局部计算运行在 worker 中,减少渲染引擎的计算耗时,提升渲染引擎的渲染性能。")]),e._v(" "),r("p",[e._v("当然,如果要实现在 Worker 中进行提前渲染,则需要考虑如何将渲染引擎提供给 Worker,以及将数据及时同步到 Worker 的问题。")]),e._v(" "),r("h3",{attrs:{id:"渲染引擎与-worker"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#渲染引擎与-worker"}},[e._v("#")]),e._v(" 渲染引擎与 Worker")]),e._v(" "),r("p",[e._v("如果想完全发挥到 OffscreenCanvas 的作用,要支持真正意义上的离屏渲染,而不是在主线程使用一个隐藏的 Canvas 交替绘制,需要考虑:")]),e._v(" "),r("ol",[r("li",[e._v("渲染引擎放置在 worker 中是否合适?")])]),e._v(" "),r("p",[e._v("由于渲染引擎本身是需要实时响应用户的操作的,因此大部分的内容更新是需要同步计算、并更新到 Canvas 中的。如果提取到 worker 中进行,需要考虑是否由于线程通信的原因导致响应速度的降低,反而影响用户体验。")]),e._v(" "),r("ol",{attrs:{start:"2"}},[r("li",[e._v("哪些计算可以放到 worker 中异步运行?")])]),e._v(" "),r("p",[e._v("方向一:每次有数据更新,渲染引擎都会全量更新和计算,可以考虑将非可视区域范围的部分(即可视范围往后的部分)放置到 worker 和离屏 Canvas 中进行计算")]),e._v(" "),r("p",[e._v("方向二:前面提到,渲染引擎的渲染分为两部分:")]),e._v(" "),r("ul",[r("li",[e._v("表格主体内容渲染(单元格内容、边框线、背景色等)")]),e._v(" "),r("li",[e._v("业务通过插件添加额外的内容渲染(图标、背景高亮等)")])]),e._v(" "),r("p",[e._v("对于插件部分内容,可以考虑将其放到 worker 中计算并更新。但局部内容异步渲染,可能需要考虑对当前 Canvas 进行改造,进行分层渲染,即可按照堆叠顺序进行 Canvas 拆分,结合每块内容的更新频率,仅更新某种类型的绘制内容。")]),e._v(" "),r("h2",{attrs:{id:"结束语"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[e._v("#")]),e._v(" 结束语")]),e._v(" "),r("p",[e._v("对于项目中是否适合使用该离屏方案,需要结合项目自身的架构设计、改造成本和兼容性问题等情况,考虑好上述问题,才能决定。即使是在 Worker 中不阻塞主线程,依然需要考虑计算量过大可能会导致渲染延迟等问题。")]),e._v(" "),r("p",[e._v("它会带来不小的改造成本,但收益是否可观还需要观察,你也可以先编写一个 demo 来确认效果,再尝试在项目中接入使用。")])])}),[],!1,null,null,null);a.default=n.exports}}]); -------------------------------------------------------------------------------- /assets/js/180.0716c8b3.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[180],{741: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("h2",{attrs:{id:"技术深度到底是指什么"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#技术深度到底是指什么"}},[v._v("#")]),v._v(" 技术深度到底是指什么?")]),v._v(" "),t("p",[t("RouterLink",{attrs:{to:"/front-end-work/front-end-days/about-front-end-13.html"}},[v._v("上一篇")]),v._v(" 聊技术开的职业发展,其实也有讲到技术深度和广度的问题,但我觉得这个问题可以再进行更多的探讨。")],1),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("我们经常会调侃自己的工作,比如切图仔、CRUD 工程师、调参工程师。很多时候,当我们掌握了当前工作涉及的技术和技巧之后,剩余的常常只有重复枯燥的工作日常,比如查 BUG、写 BUG、和产品同学扯皮、和测试同学吵架、用夸张手法写汇报,等等。")]),v._v(" "),t("p",[v._v("当我在做一些自认为简单的业务时,就会向往复杂的业务。在我的经历中,在业务场景比较简单的时候,大家为了晋级和考核,都倾向于将简单的事情变复杂,然后再用“有难度”的解决方案去解决,正所谓“没事找事”。那时候我觉得,如果业务本身足够复杂,就会有足够多的事情值得去解决,而不需要凭空捏造出这些复杂的场景,更不需要为了让解决方案看起来复杂,而特意让业务逻辑变得复杂。")]),v._v(" "),t("p",[v._v("在复杂的业务团队里,的确会有特别多的新知识和技术可以学习,也可以接触到大的业务场景下不同的领域模块。但实际上,对于大多数开发的日常工作,依然是基于某块业务的开发和维护,或是由于业务过于复杂,每天都被各种模块间的耦合相互影响、依赖各种上下游、莫名其妙出现的 BUG 等等,也没有足够的时间去研究。")]),v._v(" "),t("p",[v._v("而当我开始尝试解决以前认为足够复杂的业务场景时,发现再复杂的问题,也依然可以将其梳理并一一拆解,然后再逐个击破去解决。在工程化的业务里,我们用到的 99% 的技术方案,几乎都是通过现有的一些技术方案,进行适配、改造、调整后,尝试在业务中落地。以前我觉得涉及到算法和数据结构的业务,可能会面临较大的挑战、有足够的技术深度。但实际上,我们也还是在参考业界的方案,或是研究已发布的论文,结合业务的痛点去尝试解决。")]),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("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("有个朋友举了个例子,说他认为技术深度的确会存在,因为他们曾经遇到过一个大家都觉得“莫名其妙”的问题,是一位 P8 的技术专家一层层剖析解开,最终发现是某个十分底层系统中使用的网络包存在缺陷。他认为,大多数的开发能力是不足以定位到如此深入和细致的问题的。")]),v._v(" "),t("p",[v._v("我的看法不大一样,或许是这位技术专家以前有过相关经验,不管是曾经遇到过类似的问题也好,还是他对问题定位有自己成熟的经验和方法,这都是由他过去的工作经历决定的。一个一直基于某个领域深挖的技术开发,只要不止步于前,经验和时间的沉淀都会给他带来在这个领域足够的深度。同样的,如果在各个领域间不断转换和研究的技术开发,也可以在广度方向上有足够多的经验和沉淀。当然,对比如今很多只说不做、用漂亮的汇报话术和 PPT 成为技术专家的开发来说,这样能在关键时刻替大家解决问题、而不是指指点点的开发才称得上为真正的专家。")]),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("所以,在职业规划的时候,也不必太过执着于做的事情是否足够复杂、是否有更好的晋级/考核机会、是否是自己想要的广度或是深度,光是认真而踏实地把手上的每一件事做好,就可以收获足够的成长了。")])])}),[],!1,null,null,null);_.default=r.exports}}]); -------------------------------------------------------------------------------- /assets/js/122.43b0a986.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[122],{684: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("前面我们在"),t("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-calculate-split.html"}},[_._v("《5.分片计算》")]),_._v("一文中介绍了分片计算,即将需要计算的内容进行拆分,拆分成约每 50 ms 一个的任务。")],1),_._v(" "),t("p",[_._v("在这个方案中,我们维护了一个待计算区域,将页面中所有未完成的计算任务放在里面,等待异步每个计算任务进行计算。")]),_._v(" "),t("h2",{attrs:{id:"全量计算的性能瓶颈"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#全量计算的性能瓶颈"}},[_._v("#")]),_._v(" 全量计算的性能瓶颈")]),_._v(" "),t("p",[t("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-calculate-split.html"}},[_._v("《复杂渲染引擎架构与设计--5.分片计算》")]),_._v("中的方案,已经基本解决大多数场景下的性能问题。但面对超大页面的渲染计算来说,可能依然存在以下瓶颈:")],1),_._v(" "),t("ul",[t("li",[_._v("区域合并和碰撞检测,在待计算任务过多时容易产生性能问题。尤其在列数多的场景下,按行计算的每次任务计算范围都十分仅限")]),_._v(" "),t("li",[_._v("计算任务过多,当一个页面中的内容十分多的时候(比如一万多列、几十万行的表格),我们还需要考虑是否所有内容都需要异步计算完毕,是否存在资源的浪费")])]),_._v(" "),t("p",[_._v("因此,我们可以考虑更合适的异步方案,该方案需要考虑:")]),_._v(" "),t("ol",[t("li",[_._v("尽量减少计算内容范围,减少资源浪费。")]),_._v(" "),t("li",[_._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("ol",[t("li",[_._v("不再计算整个页面所有的数据,而是基于当前停留的界面可视范围来进行计算。")])]),_._v(" "),t("ul",[t("li",[_._v("优先计算当前可视范围的渲染数据")]),_._v(" "),t("li",[_._v("以可视范围为基础倍数,异步计算横向 3 倍、纵向 15 倍的渲染层数据,放置到异步计算")])]),_._v(" "),t("ol",{attrs:{start:"2"}},[t("li",[_._v("每个倍数范围的计算,作为单独的一个计算任务,一个计算任务计算后,会通过任务调度进行下一个。任务优先顺序为:当前视图范围 -> 视图范围下方 -> 视图范围上方 -> 视图范围右侧 -> 视图范围左侧。")]),_._v(" "),t("li",[_._v("用户进行滚动操作时,会在滚动停止后,再基于停止后的界面,重复 1 步骤,已计算完成的数据会进行缓存,跳过已计算的内容不重复计算。")])]),_._v(" "),t("p",[_._v("举个例子,当界面停留不滚动时,当前可视范围有 10 列 50 行,则会计算 10 _ 3 = 30 列、 50 _ 15 = 750 行的数据。")]),_._v(" "),t("p",[_._v("当然,横向 3 倍、纵向 15 倍这个数字也是可以进行调整的,可以埋点记录用户行为,然后观察用户习惯后进行调整。")]),_._v(" "),t("p",[_._v("该方案的优点在于:")]),_._v(" "),t("ul",[t("li",[_._v("每个计算任务单元格数量不会十分大,可有效避免按行计算在极端条件下可能计算量很大的场景")]),_._v(" "),t("li",[_._v("不需要计算整表完整的数据,可有效减少计算量")]),_._v(" "),t("li",[_._v("可提前预热可视区域附近的区域(横向 3 倍,纵向 15 倍),在用户滚动时可快速渲染")]),_._v(" "),t("li",[_._v("无需维护待计算区域任务,无需进行碰撞检测和区域合并,简化了计算性能")])]),_._v(" "),t("p",[_._v("至于为什么任务优先顺序会是:下、上、右、左,可参考另外一篇文章"),t("RouterLink",{attrs:{to:"/front-end-basic/performance//front-end-performance-preload-order.html"}},[_._v("《前端性能优化--预加载顺序设计》")]),_._v("。")],1),_._v(" "),t("h2",{attrs:{id:"结合-50ms-计算任务拆分"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结合-50ms-计算任务拆分"}},[_._v("#")]),_._v(" 结合 50ms 计算任务拆分")]),_._v(" "),t("p",[_._v("基于可视区域的计算方案,在大多数场景下都不会有性能问题,但还有一种场景:单个可视区域的计算量十分大,比如用户缩放到 10% 的时候,可能单个计算任务就会存在卡顿。")]),_._v(" "),t("p",[_._v("因此,我们可以结合之前提到的 50ms 任务拆分(参考"),t("RouterLink",{attrs:{to:"/front-end-basic/performance//front-end-performance-long-task.html"}},[_._v("《让你的长任务在 50 毫秒内结束》")]),_._v("一文),在异步计算的时候,当当前任务计算已超出 50ms 范围,则结束任务,释放出主线程给用户交互。")],1),_._v(" "),t("p",[_._v("为此,我们可能需要:")]),_._v(" "),t("ul",[t("li",[_._v("支持脏区标记,标记哪些已计算的数据不再为最新")]),_._v(" "),t("li",[_._v("支持是否计算完成标记,供异步任务计算时判断是否跳过")])]),_._v(" "),t("p",[_._v("基于此方案,我们需要调整异步任务管理:")]),_._v(" "),t("ol",[t("li",[_._v("使用新的异步任务(滚动停止时,构建当前可视区域的横向 3 倍、纵向 15 倍异步任务)。")]),_._v(" "),t("li",[_._v("同步计算可视范围内数据,异步计算可视范围附近区域(考虑使用 requestAnimationFrame 的方式进行异步)。")]),_._v(" "),t("li",[_._v("当可视范围内单元格数量很多时(缩放倍率较小场景下),按照 50ms 进行二次拆分计算。")])]),_._v(" "),t("h2",{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("我们还可以通过埋点去分析用户行为,通过确切的数据去调整我们的具体优化方案。")])])}),[],!1,null,null,null);v.default=e.exports}}]); -------------------------------------------------------------------------------- /assets/js/174.4781a418.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[174],{736:function(_,v,t){"use strict";t.r(v);var p=t(69),r=Object(p.a)({},(function(){var _=this,v=_.$createElement,t=_._self._c||v;return t("ContentSlotsDistributor",{attrs:{"slot-key":_.$parent.slotKey}},[t("p",[_._v("我的电子书《前端的进击》终于上架啦!")]),_._v(" "),t("p",[_._v("这本书说来话长,中间过程也算一波三折了,因此也值得写个文章来纪念下。")]),_._v(" "),t("p",[t("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/%E5%89%8D%E7%AB%AF%E7%9A%84%E8%BF%9B%E5%87%BB.jpg",alt:""}})]),_._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("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("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("p",[_._v("我们有考虑过,将这部分的内容进行开源,就像之前《深入理解 Vue.js 实战》这本书一样。对我来说,有一些很天真的想法和坚持,正如 Vue.js 这本书都写完了,最后我却征求编辑小姐姐的同意,将它进行了开源。")]),_._v(" "),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("p",[_._v("这本书定价不高,28 块钱大概是一顿饭钱,或者是一杯奶茶,如果感兴趣你也可以买来看看,特别推荐第三部分哦~s")]),_._v(" "),t("blockquote",[t("p",[_._v("依然希望未来的某一天,这本书有机会印刷出版。")])]),_._v(" "),t("ul",[t("li",[_._v("本书地址:"),t("a",{attrs:{href:"https://www.ituring.com.cn/book/2942",target:"_blank",rel:"noopener noreferrer"}},[_._v("https://www.ituring.com.cn/book/2942"),t("OutboundLink")],1)]),_._v(" "),t("li",[_._v("目前只上架了图灵社区,后续会逐渐在其他平台上架哦~")])])])}),[],!1,null,null,null);v.default=r.exports}}]); -------------------------------------------------------------------------------- /assets/js/129.03c7dbef.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[129],{692: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("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("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("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("很多人在拿到一个不熟悉的项目时,第一反应经常是重构它。说实话,要把重构这项工作做好,往往是吃力不讨好。对此,个人的建议是可以先开发和维护一段时间,确切知道项目的实际情况后,结合业务未来的规划,再来考虑是否需要进行重构工作,亦或是局部优化。如果说业务已经稳定,且不会再用什么新的功能了,除非是 bug 多到无法解决,否则就不需要投入过多的精力在这里。")]),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("具体到前端页面开发来说,前端状态管理工具也有很多,常见的比如各框架自带的 vuex、redux,以及比较热门的 mobx 等,具体的引入可以结合项目自身的情况比如使用的框架、项目技术栈等来进行选型。")]),v._v(" "),t("p",[v._v("除此之外,有时候我们会遇到一些现有开源工具无法直接在项目中的问题,这种时候我们往往需要“造轮子”,即参考业界成熟的技术方案,结合项目实际情况来调整落地。比如说依赖注入的方案,著名的开源项目中有 Angular 和 VsCode 都实现了依赖注入的框架,但并没有抽离出来直接可用的工具,我们可以通过研究它们的相关代码,分析其中的思路以及实现方式,然后在自己项目中使用。")]),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("p",[v._v("不同的项目或是同一个项目的不同时期,关注的技术点都会不一样。在项目初期,关注重点往往是快速试错与功能迭代;在项目稳定期,项目的维护成本则会逐渐受到重视。")]),v._v(" "),t("p",[v._v("我们在引入新的技术或架构的时候,还需要考虑项目后续的发展规划。比如说我们在给项目引入依赖注入时,假设我们知道项目后续需要支持以应用中内嵌应用的功能,则可以考虑以 SDK 为维度来进行依赖注入,避免后续在同一个应用中存在两个 SDK 时,依赖注入管理混乱。")]),v._v(" "),t("p",[v._v("而技术变更会引入的技术债务问题,则还需要在方案设计的时候进行详细评估。举个例子,架构设计的改造,往往产生极大的工作量,面对这样的工作量是否有有效的解决方案,比如引入自动化流程进行、新增人力支援等。如果该问题无法有很好的解决方案,那么引入新技术必定会带来更多的技术债务,这种情况下就需要仔细衡量这个事情值不值得了。")]),v._v(" "),t("p",[v._v("至于技术方案调研相关,之前有写过一篇更详细的文章:"),t("RouterLink",{attrs:{to:"/front-end-basic/skill/research-and-design-process.html"}},[v._v("《技术方案的调研和设计过程》")]),v._v(",感兴趣的小伙伴也可以看看。")],1),v._v(" "),t("p",[v._v("实际上,项目复盘也可以很好地解决剩余技术债务的问题,同时还能避免相同的错误再犯,之前"),t("RouterLink",{attrs:{to:"/front-end-basic/skill/why-project-reviews-are-important.html"}},[v._v("《为什么项目复盘很重要》")]),v._v("一文中也有介绍。")],1),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/219.2dd5b970.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[219],{780:function(e,r,t){"use strict";t.r(r);var a=t(69),o=Object(a.a)({},(function(){var e=this,r=e.$createElement,t=e._self._c||r;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("p",[e._v("这个月很多能力在开发中,不过社区推出文章分享沉淀,赶紧去看看吧~")]),e._v(" "),t("h1",{attrs:{id:"小程序-latest"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#小程序-latest"}},[e._v("#")]),e._v(" 小程序 latest")]),e._v(" "),t("h2",{attrs:{id:"小程序能力"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#小程序能力"}},[e._v("#")]),e._v(" 小程序能力")]),e._v(" "),t("h3",{attrs:{id:"自定义组件支持数据监听器"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#自定义组件支持数据监听器"}},[e._v("#")]),e._v(" 自定义组件支持数据监听器")]),e._v(" "),t("p",[e._v("数据监听器可以用于监听和响应任何属性和数据字段的变化。从小程序基础库版本 2.6.1 开始支持。"),t("a",{attrs:{href:"https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/observer.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("详情"),t("OutboundLink")],1)]),e._v(" "),t("h3",{attrs:{id:"微信开放社区新增-文章分享-模块"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#微信开放社区新增-文章分享-模块"}},[e._v("#")]),e._v(" 微信开放社区新增“文章分享”模块")]),e._v(" "),t("p",[e._v("微信开放社区新增了“文章分享”的模块,希望大家可以将设计、开发和运营的小程序经验分享给更多的用户,将大家平时积累的经验分享出来,也沉淀下来。\n对于优质的文章,会被选为精选,精选文章会逐步在社区首页展示,并且每周依次在开发者的公众号被推送。")]),e._v(" "),t("h3",{attrs:{id:"其他"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#其他"}},[e._v("#")]),e._v(" 其他")]),e._v(" "),t("ul",[t("li",[e._v("新增小程序音频中断开始和结束事件"),t("code",[e._v("wx.onAudioInterruptionBegin")]),e._v("、"),t("code",[e._v("wx.onAudioInterruptionEnd")]),e._v("。"),t("a",{attrs:{href:"https://developers.weixin.qq.com/miniprogram/dev/api/wx.onAudioInterruptionBegin.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("详情"),t("OutboundLink")],1)])]),e._v(" "),t("h3",{attrs:{id:"更新日志"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#更新日志"}},[e._v("#")]),e._v(" 更新日志")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/doc/000cca3e4142280b76388814356c01",target:"_blank",rel:"noopener noreferrer"}},[e._v("周社区问题反馈以及功能优化更新(02.26-03.02)"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/doc/000ac69de541e8b0fb28c9fab5b001",target:"_blank",rel:"noopener noreferrer"}},[e._v("周社区问题反馈以及功能优化更新(02.11-02.22)"),t("OutboundLink")],1)])]),e._v(" "),t("h1",{attrs:{id:"小程序教程"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#小程序教程"}},[e._v("#")]),e._v(" 小程序教程")]),e._v(" "),t("h2",{attrs:{id:"社区精选文章"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#社区精选文章"}},[e._v("#")]),e._v(" 社区精选文章")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article/doc/000e64f299cdd8c55a3848f7451013",target:"_blank",rel:"noopener noreferrer"}},[e._v("【优化】小程序优化-代码篇"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article/doc/00026a9b984b604ad9287077a51413",target:"_blank",rel:"noopener noreferrer"}},[e._v("从源码看微信小程序启动过程"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article/doc/000c8eba1ec3b8c7ce287954c53c13",target:"_blank",rel:"noopener noreferrer"}},[e._v("小程序架构设计(二)"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article/doc/0000cc199900b8f66628f610b56413",target:"_blank",rel:"noopener noreferrer"}},[e._v("小程序性能和体验优化方法"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article/doc/000ccc7eec00e0e93c280153251c13",target:"_blank",rel:"noopener noreferrer"}},[e._v("一次在微信小程序里跑 h5 页面的尝试"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article/doc/000a4c1620c188f3adf7db9ab5b413",target:"_blank",rel:"noopener noreferrer"}},[e._v("小程序架构设计(一)"),t("OutboundLink")],1)])]),e._v(" "),t("p",[e._v("更多可以查看"),t("a",{attrs:{href:"https://developers.weixin.qq.com/community/develop/article?tag=%E7%B2%BE%E9%80%89",target:"_blank",rel:"noopener noreferrer"}},[e._v("文章分享"),t("OutboundLink")],1),e._v("。")]),e._v(" "),t("h1",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[e._v("#")]),e._v(" 结束语")]),e._v(" "),t("p",[e._v("微信开发者社区新增了文章分享后,大家可以多多去逛一下,集合大家的智慧和力量,越走越远。")])])}),[],!1,null,null,null);r.default=o.exports}}]); -------------------------------------------------------------------------------- /assets/js/183.72b9d858.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[183],{745: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("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("前段时间关于 996 的讨论很激烈,尤其在程序员这样的行业,我们常常无法避开。")]),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("业界有个被默认的现象,大家都认为35岁以后的程序员会进入中年危机和职业危机,当然也有不少这样的案例被暴露在大家眼里。")]),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:"千万不要急"}},[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("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("h2",{attrs:{id:"结束语"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[v._v("#")]),v._v(" 结束语")]),v._v(" "),t("p",[v._v("我们在遇到问题的时候,“看看别人是怎么做的”是最容易又最简单的解决方法。但自我拷问得出的结论,才是真正值得你去相信和使用的答案。")]),v._v(" "),t("blockquote",[t("p",[v._v("最近博客更新频率稍微降低,因为在研究一些别的事情,在做一些没做过的尝试,挑战一下自己。")])])])}),[],!1,null,null,null);_.default=s.exports}}]); -------------------------------------------------------------------------------- /assets/js/175.11101066.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[175],{737:function(v,_,a){"use strict";a.r(_);var t=a(69),r=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("常常在想,成为前端也有好几年了,新知识新技术层出不穷,那这些年来到底积累了什么呢。如果说有什么可以给到你们,除去前端技术相关,大概也只剩下这一路上的历程和思考,以及一些方式和方法了。\n")]),v._v(" "),a("h1",{attrs:{id:"从哪里来"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#从哪里来"}},[v._v("#")]),v._v(" 从哪里来")]),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("所以首先要自我介绍一下,我叫被删。为什么要叫被删,因为这是我名字的谐音。我爱用谐音,这不为什么我起的英文名叫 basin,出国旅游的时候才知道自己有多蠢,现在倒是挺喜欢用 deleted,不过都没啥影响,你们只需要记得被删就好了,很好记。")]),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("很遗憾,我大学学的光信息,每天都在各种计算推导,稍微好玩些的也就是全息相关的实验。说来怎么会和写代码扯上关系的呢?大学里我闲得无聊,刚好有个朋友在跟着教授写项目,刚好去参观了下。教授搞的 web 物理引擎,以及游戏设计,他给我演示了在做的一个汽车透视结构图,好像好厉害的样子。")]),v._v(" "),a("p",[v._v('"好像好厉害"这几个字大概是人生中各种入坑的原因。就跟当初报这个光信息的专业,也是觉得好像很高大上。')]),v._v(" "),a("p",[v._v("毕业的时候,其实三方签的去华为,做的数通,简单来说包括路由器交换机防火墙这块。培训两个月,然后大家都发配到世界各地去支持了。每天住在酒店里,基本上除了机房、酒店,没有其他地方去,也没有什么人可以交流。做了半年,实在耐不住寂寞,辞职了。离职前刚评了绩效,拿了A跑了,被主管批了一顿。")]),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("我还记得找的第一份工作,老板看我学历还行、长得也还可以,让我当秘书。他开了两份offer,秘书的待遇比前端的好一倍不止。那一天真的很苦恼,最后是几位写后台的大哥鼓励下,才下定决心开始学代码。")]),v._v(" "),a("p",[v._v("那会从华为出来,待遇的落差总会不断地提醒我,到底是为了什么呢?但满怀的热情使得我每天上班充满干劲,下班后也继续在床上打着灯看书和写demo学习。那是jQuery横行的年代,似乎只需要掌握了它,你就能所向披靡。噢当然还有CSS,CSS的调试也是10%的理解+90%的日积月累不断沉淀的。")]),v._v(" "),a("p",[v._v("那段时间能感觉到,成长很快,几位后台开发小哥哥带着我入门,然后就停不下来了。我曾经在面试的时候说过我学习能力很强,但是通常别人会问,你怎么证明呢?(语塞)")]),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("接触 angularJS、react.js、vue.js 这些框架,都是在这里完成的。当然最初选型的时候,那会大热的是 angularJS,react 刚刚起步。在其中一个小伙伴还在犹豫用哪个的时候,我就吭哧吭哧地用 angularJS 来重构了。")]),v._v(" "),a("p",[v._v("后面项目越来越大,来了两个实习生跟着一块干。两个小伙都很棒,当然人多之后,项目管理就会出现问题了。代码规范、接口规范这些都慢慢地一边磨合一边调整,其中一个小弟带着我们一块用上了 Typescript,另外一个则发起组件封装、抽象方式的探讨。")]),v._v(" "),a("p",[v._v("在这里,深刻领悟到的是,争吵和摩擦其实可以带来很好的正反馈。因为每个人都拥有不一样的想法和角度,在相互碰撞和磨合之后,所谓的集众人之智慧,时可以做出非常棒的事情来。技术博客的念头,也是几位实习生的起哄下开始持续写的。")]),v._v(" "),a("p",[v._v("新人的优势在于敢想敢做,而老人家的长处在于帮着收拾残局。好几次要发布了,合版本出现很多问题,熬不住的便让他们先走,于是除了在华为之外,熬夜通宵发版的经历,也都是在这里度过的。即使这样,很多新的尝试也让我们快速地成长,收获更多。")]),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("曾经,BAT 是所有像我这种从底层爬起的开发者的梦想。当时离开,总监也问我,为什么想走,他从那里面来,也不过如此,这里有更多的机会。我看着他说,体验过的人才有资格说不,我没去过,所以我要去。")]),v._v(" "),a("p",[v._v("然后我来腾讯了。")]),v._v(" "),a("p",[v._v("和每次换工作的过程都一样,干脆利落,还没开始准备就开始扔简历了。所以前面几次电面都没过,直到有一个面试官问我,“我看你前面都面了一两次了,你不知道来腾讯这些是必备知识吗?”。我跟他讲,“我知道,我只是还没准备,要是准备了肯定能过。”他给了我一周的时间。")]),v._v(" "),a("p",[v._v("一周后,电话如约打来,当然我也对答如流,直接约了第二天现场面试,后续当然就是入职啦。在腾讯差不多两年了,也换了一次部门,整体来说,这边的技术能力和氛围,会有些不如预期吧。")]),v._v(" "),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("p",[v._v("为什么要在博客写这种东西呢?对这个世界来说,成功的人的经历才值得借鉴,他们说的话才有参考价值。")]),v._v(" "),a("p",[v._v("我还未成功,但是成功的定义在每个人眼里都不一样吧。对我来说,有些事情坚持下来了,几年,十几年,几十年,坚持到一辈子,大概就是属于我的成功了。目前来说,几年下来,也算是达成阶段性的成就了。")]),v._v(" "),a("p",[v._v("嗯,说不定什么时候,我也只剩下讲故事这项技能了。趁着还爱着敲键盘的时候,代码也好,文字也好,此时此刻,我在做着喜欢的事情,也够了。")]),v._v(" "),a("p",[v._v("活了这么多年,我也终于发现了自己最擅长的事情,大概是自己搭建舞台,自己表演,然后自己给自己鼓掌。")]),v._v(" "),a("p",[v._v("“啪,啪,啪”。")])])}),[],!1,null,null,null);_.default=r.exports}}]); -------------------------------------------------------------------------------- /assets/js/179.756b524a.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[179],{743:function(t,_,v){"use strict";v.r(_);var r=v(69),a=Object(r.a)({},(function(){var t=this,_=t.$createElement,v=t._self._c||_;return v("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[v("p",[t._v("日经贴:程序员的 35 岁是天花板吗?35 岁以上的程序员都在做些什么?技术开发的职业路线要怎么规划?")]),t._v(" "),v("h2",{attrs:{id:"技术开发的职业发展道路到底该怎么走"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#技术开发的职业发展道路到底该怎么走"}},[t._v("#")]),t._v(" 技术开发的职业发展道路到底该怎么走?")]),t._v(" "),v("p",[t._v("现在的互联网团队虽然很多,但也有很多人在不断涌入这个行业,导致竞争日益激烈。对于很多企业来说,钱少能熬的年轻人似乎是更好的选择,因此程序员群体中也流传着“35 岁就会被淘汰”的说法。")]),t._v(" "),v("p",[t._v("曾经有一段时间,我遇到了一些比较难过的坎,于是认真学习了一些关于心理健康的知识,尝试好好调整自己。那时候便想,如今的社会发展和变化的速度太快,大多数事情都被要求“快速”、“高效”,未来很多人会选择进入企业打工而不是自己创业,各种精神上的压力都不小,焦虑和抑郁的情绪也逐渐变多,或许心理健康也会越来越受到重视,且被未来的社会高度依赖了。")]),t._v(" "),v("p",[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("很多技术开发在谈到职业规划的时候,都会考虑到一个点:技术的广度和深度到底哪个更重要更适合自己?")]),t._v(" "),v("p",[t._v("过去的我觉得,走深度还是广度,和一个人的喜好与规划有关系,比如,如果以后想当独立开发显然各种技术都要有所涉猎,如果想在某个技术领域扎根则应该要深挖。")]),t._v(" "),v("p",[t._v("我在之前的团队有接触过一些全栈开发,实际上不同领域的知识体系有差异,但大多数在工作中也做各自领域中比较普遍的事情(比如前端写页面调样式,后台写 CURD 逻辑),而全栈开发更是难有精力和心思去解决深入的问题。我也因为技术深度的原因来到了现在的团队,现在的业务场景的确已经是前端领域中复杂性排名很前的,慢慢发现其实再复杂问题,也都可以将其梳理并一一拆解,然后再逐个击破去解决。")]),t._v(" "),v("p",[t._v("追求技术广度,在工作中接触的技术领域会很多很杂,其挑战点在于是否可以对不同领域的技术知识进行足够的思考和归纳,找出不同技术的共通点以及各自的特点,并能选择合适的技术去解决不同的问题。追求技术深度,则需要在某个足够复杂的领域中,将其逐一拆解,逐一解决后,还能将新获得的知识再次归纳整合,优化在该领域的技术网络。")]),t._v(" "),v("p",[t._v("所以,我们常说开发的技术能力,其实更多时候是由各自的开发经验和项目经历决定,不管是广度还是深度,都有其可以出彩的地方,也有或许会让人觉得无趣的时候。而作为技术开发,唯一要避免的是 1 年的工作经验当 10 年用,这样即使工作了 10 年,也只是原地踏步。")]),t._v(" "),v("p",[t._v("而"),v("a",{attrs:{href:"https://godbasin.github.io/2021/11/12/about-front-end-12/",target:"_blank",rel:"noopener noreferrer"}},[t._v("上一篇"),v("OutboundLink")],1),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("我有时候会思考,像如今所谓 BAT、TMDJ 这些比较大的公司,到底喜欢怎样的员工呢?")]),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",[t._v("除此之外,"),v("a",{attrs:{href:"https://godbasin.github.io/2021/10/10/about-front-end-11/",target:"_blank",rel:"noopener noreferrer"}},[t._v("前面"),v("OutboundLink")],1),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("以前我觉得大团队很厉害,比如光前端开发就有一百多号人,肯定有不少的技术积累和沉淀,也能学到不少的东西。一般来说,复杂场景的业务才需要用到这么多某个领域的开发,实际上大团队对团队管理和氛围的要求更高,毕竟鸟儿大了什么林子都有。")]),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("h3",{attrs:{id:"结束语"}},[v("a",{staticClass:"header-anchor",attrs:{href:"#结束语"}},[t._v("#")]),t._v(" 结束语")]),t._v(" "),v("p",[t._v("说了很多,不管是技术路线该往深度还是广度走,还是该去大团队还是小团队,似乎都没有最优解。实际上,做技术开发这一行的很多人都未必出于热爱,我们也经常见到有人辞职去创业、考公务员、做老师。")]),t._v(" "),v("p",[t._v("如果问我,我觉得不管是被行业淘汰了、或对行业失去热情了,还是浑浑噩噩地一直焦虑地工作着,或是有了新的方向。不管什么时候,都要摆正自己的心态,只要人还在,只要还愿意继续尝试,一切都可以重新开始。不用太在意当前的状况,也不用着急和其他人比较,我们的人生还有很长的路在走,还有无数的机会让它变得有趣而精彩。")])])}),[],!1,null,null,null);_.default=a.exports}}]); -------------------------------------------------------------------------------- /assets/js/186.f0d6f2da.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[186],{747: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("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("而你自身的优势是什么?当前团队中有可以发挥的地方吗?举个例子,我喜欢分享讨论、自身在思考和沉淀方面也有较多的经验,如果团队对我的期望就是“只需要做我给你讲的,按照我讲的方式去做”,而当我提出其他方案的时候被打击了,那我可以从这样的团队中发挥自己最擅长的能力吗?")]),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("我们每个人都有一些称之为“底线”的原则,在"),t("a",{attrs:{href:""}},[v._v("前端这几年--2.工作原则和选择")]),v._v("一文中其实也有详细地讲述了我的一些观点。而工作和生活一样常常并不会如你所愿,即使我们定下来一些认为不可撼动的原则,却也辗转之后发现“这个世界就是这样子的啦”。会自我怀疑,自我否定,最终容易丢失了自己。")]),v._v(" "),t("p",[v._v("遇到这样的问题怎么办?你可以先调整下情绪,静下心来分析一下,当前这个工作能给你带来什么?又会让你失去什么呢?如果你能收获的东西,价值远大于你会失去的,则不妨考虑一下“忍一时风平浪静”,先把需要的经验、金钱、成长值到手,再考虑跟现在的工作说再见。很多时候,一些让人难受的经历,其实也会让人成长,虽然未必是你希望的方式。")]),v._v(" "),t("p",[v._v("如果你能收获的比不上你会失去的,那就“退一步海阔天空,你退我不退”。好好准备一下面试,找一份可以让你有一定增值的工作吧。")]),v._v(" "),t("h2",{attrs:{id:"成长-or-赚钱"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#成长-or-赚钱"}},[v._v("#")]),v._v(" 成长 or 赚钱?")]),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("h1",{attrs:{id:"say-goodbye"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#say-goodbye"}},[v._v("#")]),v._v(" say goodbye")]),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("我不怎么擅长好聚好散,大概是因为运气比较糟糕遇到一些不咋地的老板,又或者是我一般都会提前争取、沟通失败才提出离开,这种情况下常常也较失望了。")]),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/114.49c02da7.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[114],{677: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("前面我们介绍了复杂渲染引擎中,使用的收集和渲染、以及插件等架构设计。至于底层具体的绘制实现,前面提到的多是 Canvas,实际上我们还可以适配不同的绘制引擎。")]),v._v(" "),a("h2",{attrs:{id:"多渲染方式适配"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#多渲染方式适配"}},[v._v("#")]),v._v(" 多渲染方式适配")]),v._v(" "),a("p",[v._v("关于渲染引擎整体架构和插件架构的设计,已在"),a("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-render-and-collect.html"}},[v._v("《收集与渲染》")]),v._v("、"),a("RouterLink",{attrs:{to:"/front-end-basic/render-engine/render-engine-plugin-design.html"}},[v._v("《插件的实现》")]),v._v("两篇文章中介绍过,渲染引擎架构如图:")],1),v._v(" "),a("p",[a("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/render-engine-plugin-design-1.jpg",alt:""}})]),v._v(" "),a("p",[v._v("底层渲染引擎由收集器和渲染器组成,其中收集器收集需要渲染的渲染数据,渲染器则负责将收集到的数据进行直接渲染。")]),v._v(" "),a("p",[v._v("本文我们将会介绍渲染器的多种渲染方式的适配,其中常见的就包括:")]),v._v(" "),a("ul",[a("li",[v._v("Canvas 渲染")]),v._v(" "),a("li",[v._v("SVG 渲染")]),v._v(" "),a("li",[v._v("DOM 渲染")]),v._v(" "),a("li",[v._v("其他渲染方式(如 WEBGL 渲染等)")])]),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",[a("img",{attrs:{src:"https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/render-engine-bottom-render-architecture-1.jpg",alt:""}})]),v._v(" "),a("p",[v._v("从收集器收集到的数据,通过适配的方式,转换成不同的绘制结果。举个例子,同样是一个单元格内容:")]),v._v(" "),a("ul",[a("li",[v._v("Canvas 将需要分别绘制单元格背景(矩形)、单元格边框(线段)、单元格内容(文本)")]),v._v(" "),a("li",[v._v("SVG 与 Canvas 相似,但 SVG 需要注意元素的层级关系,组合成单元格")]),v._v(" "),a("li",[v._v("DOM 则可以通过一个"),a("code",[v._v("
")]),v._v("元素或是"),a("code",[v._v("//
")]),v._v("等表格元素来绘制")])]),v._v(" "),a("p",[v._v("一般来说,我们如果使用多种渲染方式,还需要考虑渲染一致性。渲染一致性是指,使用 Canvas 绘制的结果,需要与 SVG、DOM 绘制渲染的结果保持一致,不能出现太大的跳动或是位置、样式不一致的结果。")]),v._v(" "),a("p",[v._v("因此,我们在进行渲染的时候,根据选择的渲染方式,还需要做不同的兼容适配。")]),v._v(" "),a("p",[v._v("以上几种渲染方式中,DOM 渲染会受浏览器自身的排版引擎影响,这种影响可能是正面的,也可能是负面的。比如,我们 Canvas 排版方式是尽量接近浏览器原生的方式,那么当我们适配 DOM 渲染的时候则比较省力气。但如果说像在线表格这种场景,使用 DOM 进行表格的排版,则可能会遇到比较多的问题。")]),v._v(" "),a("p",[v._v("举个例子,我们都知道 DOM 里的表格元素("),a("code",[v._v("")]),v._v("/"),a("code",[v._v("")]),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}}]); --------------------------------------------------------------------------------