Thursday, March 31, 2022

SegmentFault 最新的文章

SegmentFault 最新的文章


Offer 提速:如何写出有亮点的简历

Posted: 30 Mar 2022 06:53 AM PDT

声明:本文所有观点均代表我个人意见,不地表与公司、部门、团队立场/意见,关于内容的对错、价值观由读者自行判断,欢迎友好沟通探讨,希望能和而不同。

先来个灵魂拷问:你与他人相比,有什么能形成明显区分度的优势条件?

这里有两个层面的问题,一是如何识别出你的优势条件,毕竟大多数人大多数时候可能都是在做业务,临到写简历的时候要求总结日常工作中跟别人不一样的点,确实挺难的,怎么办?第二个问题是你可能已经挖掘到自己的优势,但是在简历里面怎么组织内容,怎么表达才能突出,让面试官迅速 get 到点呢

这事并不容易,我见过的不少简历,特别是5年以下的同学很多都写的不达预期,有一些是真的平平无奇,有一些是明明有不错的经历,但就是没有表达出来,几分钟内很难 get 到亮点。最近刚好有不少人找我内推,我都会尽力帮着看看有没有什么明显的问题,在沟通过程中慢慢总结出了一些共性问题,于是有了这篇文章,希望能帮助正在或即将找工作的同学。

本文不会讲太多基础问题,例如格式、字号、字体等问题,这些网上已经有很多文章,没必要重复讨论。本文会更聚焦于内容,聚焦于如何在有限篇幅内突出你的个人优势,包括如何在日常工作中挖掘亮点,如何组织语言让面试官能够迅速理解你的亮点,以及需要避开那些可能会造成负面效果的坑。有任何想法意见欢迎留言讨论,如果对你确实有帮助希望不要吝啬您的赞,这对我很重要,能激励我持续写更多文章。

如何挖掘亮点

重点是持之以恒的记录,积累足够的素材。在此基础上学会识别对于求职来说什么是加分项,什么不是。

坚持记录

写作需要素材,写简历当然也需要素材,简历的素材就来自于我们日积月累的工作,可以养成习惯,有意识地将一些经历以文本的形式记录下来。

记录的方式有很多,比如技术博客、项目日志、年度总结甚至是周报,这种书面形式的留存总结能够随时 review,所谓好记性不如烂笔头,这些信息最终可能就变成你简历的重要素材。

当然,也没必要事无巨细记流水账,可以把有限的精力放在一些重要节点上:

  • 项目启动时,技术选型的过程、思考、论据、结论
  • 项目结束时,执行过程的复盘、反思、重点难点、数据指标
  • 使用开源框架遇到问题时,调试过程、逻辑推导、解决方案
  • 学习新技术时,
  • 解决性能问题时,优化前后有多大的提升、具体有哪些优化措施,用了哪些工具,如何实行

这些节点都是个人成长的良好契机,把它们记下来,记录下你在这个过程中都遇到了哪些问题,分别是怎么解决的,写简历的时候翻一番总比凭感觉回想靠谱得多。

识别亮点

在积累足够多的素材之后,就可以根据面试的公司、业务、目标岗位从素材中选取更可能被面试官相中,也就是所谓"亮点"来组织简历了。

亮点应该是那些能让你显得与众不同的经历,比如说:

  • 做过一些深度的性能优化,并且有比较大的性能收益,能量化提升空间的
  • 做过一些业务逻辑特别复杂、业务影响力特别大的项目
  • 推进过一些制度、工具,深刻影响团队乃至整个公司的工作流程、工作方式,且整体有提效作用
  • 用一些不太常见的技术,解决过对前端来说比较偏门的问题,例如视频直播
  • 做过有一定名气,能真正解决技术问题的开源项目,demo、awesome-xxx 类的不在此列
  • 深入学习一些工具的用法,以此解决了一些工程化、开发效率、性能方面的问题
  • 给知名开源项目,提交过真正复杂有意义的MR,typo 类修复不在此列
  • 钻研过一些框架的原理,并能持续输出足够多的有技术深度的文章,或者明确解决过项目中出现的复杂问题
  • ...

这个列表还可以继续罗列下去,不同人,甚至同一人随着经验、认知的增长在不同时期都会有不同的判断标准,所以这里没有标准答案,尽力就好。

什么不是亮点

梳理过程要注意避开哪些不能给你加分的信息,要理智地反思一遍,这段经历是否足够复杂?是否足够表现出你的最高水平?对于这里面用到的技术,你真的掌握的很好,能应对面试吗?

这里也列举几种反模式:

  • 单点技术突破不算亮点,例如解决了某个 UI 框架的单个样式bug,体量太小
  • 做了很多项目,不能称之为亮点,只能证明你可能已经工作了很久
  • 技术框架、工具一直停留在用的阶段,对内部实现原理完全不清楚
  • 仅仅解决一些很寻常,很普遍,网上有大量现成方案的问题不能算亮点

如何表达亮点

积累足够多素材之后,接下来需要探究一下如何通过简历高效传达给预期读者。面试官通常都很忙,特别是很多大厂面试官可能每天要浏览几百上千份简历,如何组织内容才能高效传达你的信息?如何在短时间内抓住面试官的注意力?更进一步的,如何引导后续面试的内容?

首先是基本格式,这方面比较简单,上网找个你觉得最简洁清爽的模板就行了,我个人比较喜欢这个。基本格式之外更重要的其实是内容,如何在短短一两页内呈现你的能力、专业度、人设等,下面展开聊聊。

树立技术人设

所谓人设,可以简化理解为我们做过什么,以及我们将要做什么。

做过什么

落到简历上,通常需要以项目经历、掌握技能这两个角度呈现,项目经历是简历的核心组成,大多数面试官都非常看重这一part,千万不要盲目写,要有条理,有次序,有重点,我个人总结出几条规则:

  • 有意识地挑选几段能突出某项、某系列技能的项目经历,例如你要突出 vue ,那么就应该尽量围绕这个主题展开,避免一会是vue,一会是 Lua 这种牛头不对马嘴的情况,要让面试官能立即 get 到你的技术专长就是 vue
  • 组织好语言,项目经历在时间轴上从远到近,围绕你所设定的主题逐步细化、深化,例如最开始的项目经历里面你只是用了这项技术,后续逐渐开始更好地应用生态;更理解实现原理并能够解决复杂的性能、工程化问题;甚至更进一步开发了一些有价值的开源工具,或者输出了一些高质量的文档反哺社区。要让面试官能够通过项目经历感受到你从小白逐步成长的过程
  • 前面两点都是在表现深度,对于工作3年以上的同学,通常既要求有深度,又要求有一定的广度、视野,说实话这并不容易做到,有一个方法就是围绕上面树立下来的深度,向外扩展补充与前端基础强相关的工作经历,比如说 http、TLS、http2、TCP 等网络栈相关的性能优化、版本升级经历;或者,内存泄露的排查修复经验、FPS、FCP、FMP 之类指标过低的优化经验等等;又或者一些更复杂的开发场景,例如编辑器、编译器、可视化、复杂动画、多媒体等等

总结下来,尽量做到一专多能,既有深度又有广度,深度能够帮助面试官迅速判断你的技术栈,降低心智负担,看起来不累;广度能够帮助面试官识别你的学习能力、潜力、对复杂开发场景的承受度等。在基本技术人设之外,最好还能顺带传达出你对所在行业的认知深度,后面会聊到。

近期准备做什么

很多面试官喜欢问:你未来3-5年的职业规划是怎样的?很多人会觉得"职业规划"这玩意儿挺虚的,不愿意花时间去认真梳理。我的观点,职业规划首先是给自己看的,是给你自己设定了一条路径,日常工作中需要不断做出选择,心里的这条路径越明确,做决定的成本会越低,会看到自己不断在接近目标。

对于面试官来说,这个问题大部分情况下首先考察你对自己的职业生涯有没有足够清晰的认知和目标感,三天打鱼两天晒网就跟频繁跳槽一样,没办法让你在垂直领域积累足够的深度;其次,考察你规划的天花板,如果没有表现出技术、职业野心,那容易让人 judge 你的发展潜力;最后,考察你的规划与团队的 match 程度,这就见仁见智了,没法一概而论。

所以对人对己都很有必要先花点时间,想清楚自己未来3-5年要做什么,做到什么程度,树立一个明确的职业目标。这个话题有点脱离本文的主题,因为你很难在简历中表达出你的职业规划,不过可以换个角度,在简历中以附加材料的形式呈现,比如个人博客、github。

博客的话可以围绕你设定的职业规划,连续一段时间围绕这个主题写多篇博客,让面试官感受到你既有想法,又确实有在这个方向上努力。

Github 的话也是一样的,连续一段时间在这个主题上输出一些代码质量较优的仓库,通常面试官进来第一眼是看star,其次是看代码风格,如果不能攒到 100 star 以上,就尽量把代码写好看一点,这也是加分项。

量化

数字是个大杀器!正确使用各种量化指标能让你的简历更有重心,更有可信度,更容易获得认可。很多东西可以量化,比如说:

  • 性能提升:性能优化通常是一种很综合很复杂的场景,需要足够的知识深度,需要灵活运用各种调试工具,所以面试官通常看到这种经历都会多加关注,如果能推断出优化前后的指标变化就更好了
  • 业务提效:这方面通常是引入或者创造了某类工具,改变或优化原有工作流程达到局部或全局更优解,从而提升整体效率,优化方向不局限于开发团队内部。这类优化与业务紧密结合,换个业务方向的面试官可能很难从你做的事情 get 到点,如果能提供一些具体的优化数值是有助于读者做判断的,可以是流程提效了 xx \%、工单完成率提升了 xx,达到xx、响应及时度提升了 xx 之类的
  • 业务推进:假如有幸参与到一个发展比较猛的项目,而且你在这个项目中是比较核心的成员,那么可以考虑总结一下从开始到你准备离开的时候,项目的业务指标有多大的增长
  • 影响力:影响力这个概念就比较主观难以量化了,但是也可以用别的指标从旁佐证,比如工作期间做了 xx 次部门内分享、xx 次公司范围分享、xx 次行业大型分享;或者是,输出了 xx 份博客之类的

注意,前提是正确,不要为了量化而刻意捏造或者拍一些不存在的数字,拍出来的数字通常很容易识穿,面试时容易露馅,没必要。建议在日常工作中养成用数据思维,包括业务上的,技术上的,特别做一些优化的时候,记录优化前后的数值情况,写简历时自然有素材。

业务深度

先分享一下我个人经历,我曾经在一家特别小而美的人工智能创业公司工作了三年,虽然职能是前端,但是过程中并没有把自己的工作边界圈的泾渭分明,经常很发散地去支援服务端、数据甚至是运营团队的工作,比如:

  • 用 Python + celery 开发定时结算系统,降低对账成本,压缩结算周期
  • 用 node + ffmpeg + docker 做了一套视频流式处理工具,能够根据配置对一批视频做抽帧、截取片段、压缩、转格式等操作
  • 某个 POC 性质的项目中,用 Python + caffe 调用深度学习模型实现图像识别服务,配合浏览器上调用 media 接口将摄像头画面传回服务器识别出画面中的物品

短期来看确实没有明显收益,但是在我离职写简历时,发现这些经历串联起来,让我对深度学习的工作过程、原理、局限性、工具、指标等概念的理解已经足够支撑我在面试过程应对各种问题,后面找工作的时候聊到这一部分都会特别顺利。

这里不是鼓励大家去做很多前端领域之外的事情,只是想表达对业务、合作团队的了解与洞察程度可以映射出你对工作的投入度,而市场通常都会比较青睐投入度高的人。所以在日常工作中可以有意识地用各种方法跨出职能边界,去了解其他团队在做什么,怎么做的,平常会用哪些工具技术,有没有存在什么问题,这些问题有没有办法用前端的技术解决,等等。

当你对行业形成足够立体的认知之后,写简历、面试的时候可能就可以展现出在这个领域的横向认知,反过来说如果你过去对工作的认知一直停留在前端领域内,隔壁在做什么,怎么做,用什么做;业务线接下来有什么计划,可能需要用到什么新技术,这些问题都回答不上来的话,面试官容易怀疑你对工作的投入度。

项目经历怎么写

不要只写你做了什么,更重要的是突出你用什么方法,解决了什么问题,收益是什么,要能够形成一条完整的逻辑闭环,面试官才有足够信息来判断你项目经历的价值。

比如说,对于这样一段经历:在 XXX 项目中引入性能及异常上报工具,后续团队内基于回收的数据有针对性的做了一些优化,我曾经收到一份简历是这么写的:

集成监控SDK, 包含页面测速, 错误异常, API质量, 白屏异常, URL异常, 收集项目中的各类错误信息并上报, 通过 performance API进行测速分析, 封装基础库ajax上报API错误信息; 在资源监控系统通过对各个端上报的指标进行清洗, 聚合以及数据分析, 错误模块聚合sourcemap还原源代码, 便于修复线上问题; 重构项目代码与调用链, 加载时间缩短20\%;

这里面有一些明显问题:

  • 语言组织太过平铺直书,感知不到重点
  • 监控SDK是啥?集成方式又是怎么样的?这里面有多少工作量?有那些难点?
  • 形式与描述不好,阅读成本高
  • 清洗、聚合、数据分析分别又是啥?前端在这里面做了什么?
  • 错误模块聚合sourcemap?只有错误模块吗?聚合又是什么鬼?聚合还原完为什么就能"便于修复"线上问题?sourcemap 的原理又是什么?
  • "重构项目代码" 与前面说的"集成监控SDK" 是什么关系?为什么要写在一起?
  • 加载时间具体是指哪个指标?具体做了什么缩短的?

总结下来,我个人觉得问题主要是描述不清晰,很难理解这到底是一件什么事情,怎么做的,最后收益又怎么样。比较好的方式应该是:

  • 引入(基于) xxx 搭建性能与异常监控体系,覆盖 FCP/FP/FMP/TTI/LCP 等性能指标;覆盖白屏、页面奔溃、JS 异常、http异常等错误场景。
  • 在上述监控体系基础上,逐步推演出核心性能指标模型,以此为决策依据逐步执行图像合并、代码分包、缓存策略优化、首屏渲染优化、SSR 等措施,前端性能平均指标提升 xx\%,QPS 提升 xx\%
  • 在上述监控体系基础上,优化项目 CI 工序,接入基于 webpack 的 sourcemap 映射能力,线上问题能够直接映射回源码堆栈,线上问题平均修复时间降低 xx\%

这不是最好的表述,但是这已经充分说明了:用什么方式方法、具体解决了什么问题、最终收益是什么,相比于前面的写法,叙述上更严谨也更容易理解一些。

如何做减法

简历内容在"历",但是预设条件是"简",不要写成流水账,不要事无巨细写成了自传。简历内容绝非多多益善的,写的越多阅读成本越高,越难以抓到重点,所以应当适度精简。​

注意项目路径

如果你已经有比较丰富的项目经历,千万不要不做选择全部往简历怼,不是项目经历越多越好,应该根据结合个人情况,精心挑选几段有代表性的,例如:

  • 能表现出技术深度,或者业务复杂度、业务体量的
  • 能表现学习能力的,例如曾经为了使用一个文档缺失的框架,花了一段时间看完源码总结出用法,最终能够
  • 能够构建起"成长路径",这一part在 "树立技术人设"一节已经讲的比较细了

尽量避开这些类型的项目:

  • 单纯练手,复杂度、业务价值都特别低的
  • 太过简单的,例如简单的活动页
  • 仿 xxx 型的,培训班很喜欢搞这种练习题,不少候选人就拿这个当实际项目写到简历上了

慎用技术名词

前端简历中常常会有一 part 总结自己的技术栈,这里一定一定要慎重,我经常遇到很多技术栈特别广泛,但凡用过的技术都往上面写的简历,一个是看起来、分析起来累;一个是面试过程一问三不知。我建议使用任何技术名词前可以先问问自己:

  • 这种技术会给你的简历加分吗?
  • 你的使用频率、了解程度足够高吗?足够应对可能出现的各种技术问题吗?
  • 这项技术足以让你与其他候选人拉开距离吗?

比如说,"我曾经用 grunt 搭建过一套完整的工程体系" 这样的经历不能说完全没有价值,但放在 webpack、vite、snowpack、parcel、rollup 大行其道的当代,这份经历能给你加分吗?反而可能会让面试官质疑你技术更新迭代的速度会不会太慢了?

又比如说,"我曾经写个一个带视频的网页" 这样的经历还不足以支撑你在简历里面写"具备视频编解码能力",如果更进一步"我曾经基于 HLS + FFMPEG 实现动态视频流服务,配合 video.js 实现按需播放"(如我的另一篇博客 HLS + ffmpeg 实现动态码流视频服务 所说) 这种程度,那大可以说你"理解常用视频封装、编码格式,能根据应用场景搭建流畅的视频播放体系"。

站在一个面试官的角度,单纯的堆叠名词反而容易让人质疑你的知识深度和对自己的认知的准确性,适当的裁剪往往更能突出优势。

慎用形容词

在我第一次写简历的时候,有一篇文章印象很深,细节忘了但是里面有一个很重要的观点:不要写精通!我觉得特别对,因为大多数人对大多数技术的掌握程度并没有达到这个深度,如果你真的自认有精通的点,那有可能是事实也有可能是不知者无畏。

举例来说,你觉得你精通 HTML 吗?那么:

  • input 标签的 type 属性有哪些可选值?分别对应什么功能?浏览器兼容程度如何?
  • 什么是标签的语义?为什么要有语义化?有谁会消费这些语义?怎么评估语义是否恰当?
  • aria 属性是什么?怎么编写合理的 aria 结构?又是谁,以何种方式会消费这些属性?

又或者,你觉得你精通 vue 吗?那么:

  • Vue2 的双向数据绑定是什么?如何实现的?这个过程如何影响 props、computed 属性?
  • 如果上面的问题你理解了,那么 Vue3 呢?
  • Vue 如何将 template 转换为 render 函数?又是如何识别出标签对应的组件?组件层级之间的创建顺序是怎样的?渲染顺序又是怎样的?

这个列表还可以无限列下去,所谓学海无涯,谦虚一点总没坏处的。我个人的做法是绝不写"精通",因为我自知对任何一个技术点都远远没有达到精通的程度;但是会写1-2个熟悉的技术项,并且书写顺序上会尽量靠前;此外会再补充一些理解,对于那些把握不够的点会忽略不写。面可以广一点,例如网络协议、构建工具、开发框架都写一些,但总量尽量保持到3-5个。

这里可能会有同学,特别是实习生、应届生担心技术栈不够广会不会反而拿不到面试机会呢?这其实也是一种学习策略的问题,如果你站在面试官的角度,放在你面前的两份简历,一份内容看起来是少但明显能感觉有足够深度;另一份堆砌了很技术名词,但是名词之间看起来没有明显关联,你会倾向那一份?

总结

简历不容易写,技术人员的简历更不容易,为了心仪的工作花多点时间沉淀一份优秀的简历是非常有必要的。

我工作了8年,目前在字节跳动游戏部门做前端工作,前前后后已经看过几千份简历,有很多简历上的问题还是能看出来的,如果你刚好或将要找工作,我很乐意能帮你把把关;如果你刚好想来字节跳动试试前端岗位,我可以帮你内推到字节,还可以帮你模拟面试、分享我对字节各个业务线、工作环境的看法等等,欢迎微信撩骚: tecvan

本文参与了 SegmentFault 思否征文「如何"反杀"面试官?」,欢迎正在阅读的你也加入。

React18正式版发布,未来发展趋势是?

Posted: 29 Mar 2022 07:44 PM PDT

大家好,我卡颂。

2022年3月29号,React18正式版发布。

v16开始,React团队就在普及并发的概念。在v18的迭代过程中(alpha、Beta、RC),也一直在科普并发特性,所以正式版发布时,已经没有什么新鲜特性。

本文主要讲解v18发布日志中透露的一些未来发展趋势。

欢迎加入人类高质量前端框架群,带飞

开发者可能并不会接触到并发特性

React对增加API是很慎重的。从13年诞生至今,触发更新的方式都是this.setState

而引入并发概念后,光是与并发相关的API就有好几个,比如:

  • useTransition
  • useDeferredValue

甚至出现了为并发兜底的API(即并发情况下,不使用这些API可能会出bug),比如:

  • useSyncExternalStore
  • useInsertionEffect

一下多出这么多API,还不是像useState这种不使用不行的API,况且,并发这一特性对于多数前端开发者都有些陌生。

你可以代入自己的业务想想,让开发者上手使用并发特性有多难。

所以,在未来用v18开发的应用,开发者可能并不会接触到并发特性。这些特性更可能是由各种库封装好的。

比如:startTransition可以让用户在不同视图间切换的同时,不阻塞用户输入。

这一API很可能会由各种Router实现,再作为一个配置项开放给开发者。

万物皆可Suspense

对于React来说,有两类瓶颈需要解决:

  • CPU的瓶颈,如大计算量的操作导致页面卡顿
  • IO的瓶颈,如请求服务端数据时的等待时间

其中CPU的瓶颈通过并发特性的优先级中断机制解决。

IO的瓶颈则交给Suspense解决。

所以,未来一切与IO相关的操作,都会收敛到Suspense这一解决方案内。

从最初的React.lazy到如今仍在开发中的Server Components,最终万物皆可Suspense

这其中有些逻辑是很复杂的,比如:

  • Server Components
  • 新的服务端渲染方案

所以,这些操作不大可能是直接面向开发者的。

这又回到了上一条,这些操作会交由各种库实现。如果复杂度更高,则会交由基于React封装的框架实现,比如Next.jsRemix

这也是为什么React团队核心人物Sebastian会加入Next.js

可以说,React未来的定位是:一个前端底层操作系统,足够复杂,一般开发者慎用。

而开发者使用的是基于该操作系统实现的各种上层应用

总结

如果说v16之前各种React Like库还能靠体积、性能优势分走React部分蛋糕,那未来两者走的完全是两条赛道,因为两者的生态不再兼容。

未来不再会有React全家桶的概念,桶里的各个部件最终会沦为更大的框架中的一个小模块。

当前你们业务里是直接使用React呢,还是使用各种框架(比如Next.js)?

几个 iOS 端底层网络问题

Posted: 28 Mar 2022 05:54 PM PDT

典型案例

1. Socket 断开后会收到 SIGPIPE 类型的信号,如果不处理会 crash

同事问了我一个问题,说收到一个 crash 信息,去 mpaas 平台看到如下的 crash 信息

2021-04-06-NetworkFatlError.png

看了代码,显示在某某文件的313行代码,代码如下

2021-04-06-NetworkFatlError.png

Socket 属于网络最底层的实现,一般我们开发不需要用到,但是用到了就需要小心翼翼,比如 Hook 网络层、长链接等。查看官方文档会说看到一些说明。

当使用 socket 进行网络连接时,如果连接中断,在默认情况下, 进程会收到一个 SIGPIPE 信号。如果你没有处理这个信号,app 会 crash。

Mach 已经通过异常机制提供了底层的陷进处理,而 BSD 则在异常机制之上构建了信号处理机制。硬件产生的信号被 Mach 层捕捉,然后转换为对应的 UNIX 信号,为了维护一个统一的机制,操作系统和用户产生的信号首先被转换为 Mach 异常,然后再转换为信号。

Mach 异常都在 host 层被 ux_exception 转换为相应的 unix 信号,并通过 threadsignal 将信号投递到出错的线程。

Mach 异常处理以及转换为 Unix 信号的流程

有2种解决办法:

  • Ignore the signal globally with the following line of code.(在全局范围内忽略这个信号 。缺点是所有的 SIGPIPE 信号都将被忽略)

    signal(SIGPIPE, SIG_IGN);
  • Tell the socket not to send the signal in the first place with the following lines of code (substituting the variable containing your socket in place of sock)(告诉 socket 不要发送信号:SO_NOSIGPIPE)

    int value = 1; setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));

SO_NOSIGPIPE 是一个宏定义,跳过去看一下实现

#define SO_NOSIGPIPE  0x1022     /* APPLE: No SIGPIPE on EPIPE */

什么意思呢?没有 SIGPIPE 信号在 EPIPE。那啥是 EPIPE

其中:EPIPE 是 socket send 函数可能返回的错误码之一。如果发送数据的话会在 Client 端触发 RST(指Client端的 FIN_WAIT_2 状态超时后连接已经销毁的情况),导致send操作返回 EPIPE(errno 32)错误,并触发 SIGPIPE 信号(默认行为是 Terminate)。

What happens if the client ignores the error return from readline and writes more data to the server? This can happen, for example, if the client needs to perform two writes to the server before reading anything back, with the first write eliciting the RST.

The rule that applies is: When a process writes to a socket that has received an RST, the SIGPIPE signal is sent to the process. The default action of this signal is to terminate the process, so the process must catch the signal to avoid being involuntarily terminated.

If the process either catches the signal and returns from the signal handler, or ignores the signal, the write operation returns EPIPE.

UNP(unix network program) 建议应用根据需要处理 SIGPIPE信号,至少不要用系统缺省的处理方式处理这个信号,系统缺省的处理方式是退出进程,这样你的应用就很难查处处理进程为什么退出。对 UNP 感兴趣的可以查看:http://www.unpbook.com/unpv13...

下面是2个苹果官方文档,描述了 socket 和 SIGPIPE 信号,以及最佳实践:

Avoiding Common Networking Mistakes

Using Sockets and Socket Streams

但是线上的代码还是存在 Crash。查了下代码,发现奔溃堆栈在 PingFoundation 中的 sendPingWithData。也就是虽然在 AppDelegate 中设置忽略了 SIGPIPE 信号,但是还是会在某些函数下「重置」掉。

- (void)sendPingWithData:(NSData *)data {     int                     err;     NSData *                payload;     NSData *                packet;     ssize_t                 bytesSent;     id<PingFoundationDelegate>  strongDelegate;     // ...     // Send the packet.     if (self.socket == NULL) {         bytesSent = -1;         err = EBADF;     } else if (!CFSocketIsValid(self.socket)) {         //Returns a Boolean value that indicates whether a CFSocket object is valid and able to send or receive messages.         bytesSent = -1;         err = EPIPE;     } else {         [self ignoreSIGPIPE];         bytesSent = sendto(                            CFSocketGetNative(self.socket),                            packet.bytes,                            packet.length,                            SO_NOSIGPIPE,                            self.hostAddress.bytes,                            (socklen_t) self.hostAddress.length                            );         err = 0;         if (bytesSent < 0) {             err = errno;         }     }     // ... }  - (void)ignoreSIGPIPE {     int value = 1;     setsockopt(CFSocketGetNative(self.socket), SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)); }  - (void)dealloc {     [self stop]; }  - (void)stop {     [self stopHostResolution];     [self stopSocket];      // Junk the host address on stop.  If the client calls -start again, we'll      // re-resolve the host name.     self.hostAddress = NULL; }

也就是说在调用 sendto() 的时候需要判断下,调用 CFSocketIsValid 判断当前通道的质量。该函数返回当前 Socket 对象是否有效且可以发送或者接收消息。之
前的判断是,当 self.socket 对象不为 NULL,则直接发送消息。但是有种情况就是 Socket 对象不为空,但是通道不可用,这时候会 Crash。

Returns a Boolean value that indicates whether a CFSocket object is valid and able to send or receive messages.
if (self.socket == NULL) {     bytesSent = -1;     err = EBADF; } else {     [self ignoreSIGPIPE];     bytesSent = sendto(                         CFSocketGetNative(self.socket),                         packet.bytes,                         packet.length,                         SO_NOSIGPIPE,                         self.hostAddress.bytes,                         (socklen_t) self.hostAddress.length                         );     err = 0;     if (bytesSent < 0) {         err = errno;     } }   

2. 设备无可用空间问题

设备无可用空间问题
最早遇到这个问题,直观的判断是某个接口所在的服务器机器,出现了存储问题(因为查了代码是网络回调存在 Error 的时候会调用我们公司基础),因为不是稳定必现,所以也就没怎么重视。直到后来发现线上有商家反馈这个问题最近经常出现。经过排查该问题该问题 Error Domain=NSPOSIXErrorDomain Code=28 "No space left on device" 是系统报出来的,开启 Instrucments Network 面板后看到显示 Session 过多。为了将问题复现,定时器去触发"切店"逻辑,切店则会触发首页所需的各个网络请求,则可以复现问题。工程中查找 NSURLSession 创建的代码,将问题定位到某几个底层库,HOOK 网络监控的能力上。一个是 APM 网络监控,确定 APMM 网路监控 Session 创建是收敛的,另一个库是动态域名替换的库,之前出现过线上故障。所以思考之下,暂时将这个库发布热修代码。之前是采用"悲观策略",99%的概率不会出现故障,然后牺牲线上每个网络的性能,增加一道流程,而且该流程的实现还存在问题。思考之下,采用乐观策略,假设线上大概率不会出现故障,保留2个方法。线上出现故障,马上发布热修,调用下面的方法。

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {     return NO; }  //下面代码保留着,以防热修复使用 + (BOOL)open_canInitWithRequest:(NSURLRequest *)request {     // 代理网络请求 } 

问题临时解决后,后续动态域名替换的库可以参考 WeexSDK 的实现。见 WXResourceRequestHandlerDefaultImpl.m。WeexSDK 这个代码实现考虑到了多个网络监听对象的问题、且考虑到了 Session 创建多个的问题,是一个合理解法。

- (void)sendRequest:(WXResourceRequest *)request withDelegate:(id<WXResourceRequestDelegate>)delegate {     if (!_session) {         NSURLSessionConfiguration *urlSessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];         if ([WXAppConfiguration customizeProtocolClasses].count > 0) {             NSArray *defaultProtocols = urlSessionConfig.protocolClasses;             urlSessionConfig.protocolClasses = [[WXAppConfiguration customizeProtocolClasses] arrayByAddingObjectsFromArray:defaultProtocols];         }         _session = [NSURLSession sessionWithConfiguration:urlSessionConfig                                                  delegate:self                                             delegateQueue:[NSOperationQueue mainQueue]];         _delegates = [WXThreadSafeMutableDictionary new];     }          NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];     request.taskIdentifier = task;     [_delegates setObject:delegate forKey:task];     [task resume]; }

大裁员下,程序员如何做“副业”?

Posted: 29 Mar 2022 06:05 PM PDT

本文来源公众号 程序员成功

大家好,我是杨成功。

前几天有幸看了尤雨溪尤大的直播,他们谈到一个话题,是说目前前端人,或者是整个程序员群体如何做副业?近年来互联网行情下降,好多人都在思考要不要搞个副业来抵御风险。

这不又来事了,这两天又爆了互联网大裁员。继阿里"向社会输送人才"之后,京东又搞了个"毕业礼",整的小伙伴们人心惶惶。副业的关注度又一波升级。

那今天我们就来聊聊,程序员做副业这件事。

为什么程序员都想着做副业?

我理解今天的程序员为什么都有做副业的想法,原因不外乎两个方面。

一是互联网的爆发期已过,以前程序员体量小,会点基本技能就可以不愁工作,薪资也非常可观,但现在随着大量人员涌入,各种名校高学历的加入内卷,我们突然发现没什么竞争优势了。

二是互联网开发圈这种畸形的工作氛围,熬夜加班过于严重,消耗生命力来赶进度。特别是大厂,钱是给的多,但是透支强度懂得都懂,说句不好听的是拿命换钱。

以前大家听这些可能只是自嘲一下,随着脉脉上不断爆出大厂程序员加班猝死的消息,大家开始慌了,原来比起收入,健康才是我们终身的财富。

当然还有一个原因,不只是程序员的职业阴霾,但程序员首当其冲,那就是 "35 岁危机"。

程序员是拼学习能力和创造能力的职业,不进则退的规则远大于其他行业。35 以后,如果你不是真的热爱这个行业,那么你会发现,20 多岁的那种学习钻研的毅力已经没有了。再加上身体状况大不如前,加班扛不动,各方面你怎么和新来的小伙子们拼?

以上的种种情况,再加上网络上疯狂的制造焦虑,我们自己也突然有了种随时会降薪,随时会被裁,随时要加入内卷,甚至随时会没命的担忧,开始想后路了。不行搞点副业?不行创个业?

所以如果你今天在技术社区发一篇"程序员如何搞副业"的文章,点击率一定翻倍。根本原因,就是大家对未来的担忧。

卷不动怎么办?遇到 35 岁危机怎么办?大家都想着规避风险,这才不得不去关注副业。

怎么看 "加班内卷" 和 "35 岁危机"?

这两个现象是程序员焦虑的主要来源,看似避无可避,可有没有想过根本原因是什么?

我们再往深剖一下这两个问题,其实大家担心的是:"如果这种加班状态持续到 35 岁,我能不能撑得住?到时候竞争力下降,我会不会被新人替代?"

但是我想说一个误区,就是初中级别的程序员,需要大量的写代码来提升自己的技术能力,不断的投入时间学习,研究,拓宽视野。但是到了高级别的程序员,比如带团队的 leader,架构师,写代码一定是越来越少的。

对于这些人来说,他们不需要通过加班来发挥价值,他们有自己的核心竞争力和不可替代性,写代码也是写一些核心的关键的代码,更多的时间关注技术选型和解决问题的方案。所以说程序员的职业生涯不会一直伴随着加班,当你拥有核心竞争力,你的工作方式反而会很自由。

那什么是核心竞争力?我说一句话大家一定会有体会:你的技术 leader,一个决策可以让团队的开发效率提升 10 倍,也可能让团队的开发效率降低 10 倍,有可能最后还出不了成果,甚至告诉你这个不行得重来!

这就是核心竞争力呀!一个程序员对与技术选型,解决方案的评估和判断,这才是他的核心竞争力。这种竞争力不是你刷一堆算法,背一堆八股文,学很多面试技巧,他就能有的。这些东西是需要你经过几年大量的 写代码+学习+尝试+思考 才能培养出的能力。

所以说,我们所谓的 "加班内卷" 和 "35 岁危机",得分两个阶段来看。

工作五年内

如果你是工作的前五年,这个阶段你精力旺盛,学习能力强劲,那就是要卷的啊。趁着职业上升的黄金时期,花大量的时间和精力做积累,不断做加法,尽力尝试着去拓宽技术视野,让自己成为一个独当一面的人。这样你才能积累到别人无法超越的高度。

如果这个阶段你躺平了,你想着反正大家都在卷,我不想参与其中,每天上班空闲摸个鱼也挺好的。那这样持续几年,除了业务代码熟练一些,你在技术深度广度上都没多大的长进。当你发现公司的新人越来越厉害,他们带来的技术让你应接不暇的时候,你突然发现卧槽,危机真的来了,这个时候再卷是不是晚了?

我不是鼓励大家内卷,相反我很反对做那些没什么意义光是自我消耗的事情。但是努力和积累永远不是卷,更不是不想奋斗的借口。如果卷真的避免不了,那一定要"卷的有价值"。

比起内卷,其实我更鼓励大家"外卷"。外卷到意思就是当你在某个领域积累了一定经验后,要试着跳出当前的圈子,去探索一下外面的世界。

比如你是一个前端程序员,尽管你的 Title 是"前端",但你不能一直只在前端的圈子里打转。作为一个程序员,只要涉及到程序开发的,前端后端产品服务器,你都要接触,这样你的视野才会变的更广,看问题的角度更多,随之你的判断能力和才会得到提升。

大家总觉得,一个前端就该专攻 js,只有准备向全栈发展才会接触后端。其实不是的,接触并不代表一定要做,了解整体可以培养你的全局观。比如你遇到了一个难题,你只会在前端的范畴研究,可能这个问题在后端处理只是一行代码的事情,可你不知道呀,所以会浪费很多时间。

工作五年后

如果你已经工作五年以上,上面说的你都没做到,眼看着自己快奔三了,还是一线开发,依然没有形成核心竞争力,这个时候 35 岁危机袭面而来。你面对一浪强过一浪的后浪,有心追赶但是明显吃力,这个时候即使老板不会裁你,你心中也会忐忐忑忑,害怕自己某一天突然失业。

但是如果上面说的你都做到了,工作五年后,你多半会带一个小团队,或者成为架构师。不管有没有进入大厂,你总会在一个地方持续发挥着自己的不可替代性。

工作的基本任务你会安排给组内小伙伴,架构和难题自己攻克,长期下来,你会越来越值钱,这是再厉害的应届生都做不到的,这个时候你会有 35 岁危机吗?自然不会。

而且我有一个预感,程序员这个行业一定是越做越自由的。当你能力出众,技术超群,地域已经限制不了你,你可以选择任意地方的工作,并不一定要守在某一家公司,某一个城市。

或许有一天你可以在三亚海边租个小房子,每天早上起来晒晒太阳,打开电脑,安排好今天的工作,然后看看前沿技术资讯,review 一下同事的代码,花几个小时解决掉剩余的难题,然后眯着眼继续晒太阳。

聊这些就比较理想了,未来还是要展望的嘛。但是核心思想就是,如果你五年内没有做好积累,形成自己的核心竞争力,那么 35 岁危机也许真的离你不远。反之,则是另外一种状况。

怎么做副业

上面说,程序员的 35 岁危机本质是因为没有核心竞争力。再回到做副业这件事,怎么做副业能帮我们抵抗 35 岁危机?

既然危机来源于核心竞争力,那么副业的目的一定是帮助我们提升核心竞争力。

这里大家也会有一个误区,以为做副业的目的是为了赚钱,开辟一条新的收入途径。这个也不能算错,但是你想啊,一个年薪百万的程序员需要做副业吗?

人的精力有限,如果你的副业不是为了帮你巩固主业,而是分走了你投入在主业上的一部分精力,可能你真的赚了笔钱,但长远来看肯定没有你持续提升带来的收入更多。

核心竞争力除了硬实力,当然也包括这些:

  • 名气
  • 口碑
  • 帮助别人
  • 分享技术
  • 分享思想

名气不用说,在任何圈子积累了名气路就会顺畅许多;口碑呢就是说你这个人的办事能力,靠不靠谱;帮助别人是你有没有带动别人一起成长的能力;分享最关键了,你的任意优势,包括技术和思想,如果你认为是有价值的,那就分享出来啊,这样别人才能在更多方面了解你,这是你简历之外的另一张名片。

如果你做副业的目标是为了增加上面几个方面的软实力,OK,这就是最棒的。这些方面可能短期看不到回报,光是付出了,但是坚持下去,一定会给你带来意料之外的惊喜。

具体到做副业的方式有哪些?我认为两个足矣:

  • 写文章
  • 录视频

写文章真的是一个非常非常好的学习总结方式。人的听和看的能力是天生的,但是读和写的能力是需要后天培养的。比如你看一个技术视频,看完貌似学会了。十分钟后让你写一下这个视频的关键信息,你一想,哎刚才是什么来着?好像记不清了。有些东西你以为掌握了,但是让你写的时候,你才发现那也许是错觉。

写文章会逼着你回忆和思考,没有真正吸收和消化的东西,你是写不出来的。所以坚持写文会提升我们的思考力和总结概括能力。还有你工作中遇到的重要问题和解决方案,如果不记下来,过几个月多半会忘。等到要写简历的时候,突然发现好像没什么可写,这是非常不划算的事情。

除此之外,你还可以把自己的文章发表到社区,会得到很多读者的反馈,这个会给到你非常正向的鼓励,也有利于查漏补缺,同时也会慢慢积累你的口碑和名气。

不过开始写文章的时候会比较痛苦,写的不好,还憋不出来。其实我现在也是这个状态,一篇文章得写一周,一句话改来改去总觉得不通顺,总觉得结构设计差点意思。不过只能硬着头皮写,写的多了总会得心应手的。

至于录视频,我最近才发现这个事的好处,有点后知后觉了哈。

不得不承认我们程序员大多都宅,不爱说话,表达能力一般般。好多人更愿意写几行代码,也不愿意多说一句话。但是你想啊,如果有一天你的技术非常厉害了,你要把自己的经验分享给别人,如果你不能很精简的表达出关键点并且让别人听得懂的话,这是不是成了你的短板?

录视频虽然也是总结,思考,但比起写文章,它对表达能力的要求更高。表达能力是你往上走的通用能力,如果你想成为管理层几乎必备。但是呢我们工作和生活环境中表达的机会又很少,如果你可以尝试通过录视频来分享你的技术,一来有的聊,二来练习面对镜头的心态和语言组织,这是非常好的锻炼。

总之呢,打造核心竞争力,不仅仅是代码,不断突破自己的局限,才是正道。

本文聊的做副业并没有聊怎么赚钱,而是赚"核心竞争力",这个东西虽然不能直接带来票子,但又何尝不是你抵御风险的底气呢?

我想看更多

本文首发公众号 程序员成功,这里有硬核的技术干货,也有程序员的职场感悟,有趣生活。欢迎关注我,看更多前端工程与架构的好文。

除此之外,我还建了一个前端微信群,用于交流分享,提问答疑。如果你也感兴趣,欢迎加我 微信 拉你入群~

kratos分布式事务实践

Posted: 29 Mar 2022 06:40 PM PDT

背景

随着业务的快速发展、业务复杂度越来越高,微服务作为最佳解决方案之一,它解耦服务,降低复杂度,增加可维护性的同时,也带来一部分新问题。

当我们需要跨服务保证数据一致性时,原先的数据库事务力不从心,无法将跨库、跨服务的多个操作放在一个事务中。这样的应用场景非常多,我们可以列举出很多:

  • 跨行转账场景,数据不在一个数据库,但需要保证余额扣减和余额增加要么同时成功,要么同时失败
  • 发布文章后,更新文章总数等统计信息。其中发布文章和更新统计信息通常在不同的微服务中
  • 微服务化之后的订单系统
  • 出行旅游需要在第三方系统同时定几张票

面对这些本地事务无法解决的场景,我们需要分布式事务的解决方案,保证跨服务、跨数据库更新数据的一致性。

dtm作为一款非常流行的分布式事务框架,已经支持了接入多种微服务框架,下面我们就来着重介绍一下go-kratos如何接入dtm,解决分布式事务的问题

运行一个例子

我们来看一个可运行的例子,然后再看如何自己开发完成一个完整的分布式事务

下面以etcd作为注册服务中心,可以按照如下步骤运行一个kratos的示例:

  • 配置dtm

    MicroService:  Driver: 'dtm-driver-kratos' # name of the driver to handle register/discover  Target: 'discovery://127.0.0.1:2379/dtmservice' # register dtm server to this url  EndPoint: 'grpc://localhost:36790'
  • 启动etcd

    # 前提:已安装etcd etcd
  • 启动dtm

    # 请先配置好dtm的数据库 go run app/main.go -c conf.yml # conf.yml 为你对应的 dtm 配置文件
  • 运行一个kratos的服务

    git clone https://github.com/dtm-labs/dtmdriver-clients && cd dtmdriver-clients cd kratos/trans make build && ./bin/trans -conf configs/config.yaml
  • 发起一个 kratos 使用 dtm 的事务

    # 在 dtmdriver-clients 的目录下 cd kratos/app && go run main.go

当您在trans的日志中看到

INFO msg=config loaded: config.yaml format: yaml INFO msg=[gRPC] server listening on: [::]:9000 2022/03/30 09:35:36 transfer out 30 cents from 1 2022/03/30 09:35:36 transfer in 30 cents to 2

那就是事务正常完成了

开发接入

参考 dtm-labs/dtmdriver-clients 的代码

// 下面这些导入 kratos 的 dtm 驱动 import (     _ "github.com/dtm-labs/driver-kratos" )  // dtm 已经经过前面的配置,注册到下面这个地址,因此在 dtmgrpc 中使用该地址 var dtmServer = "discovery://localhost:2379/dtmservice"  // 业务地址,下面的 busi 换成实际在 server 初始化设置的名字 var busiServer = "discovery://localhost:2379/busi"  // 发起一个msg事务,保证TransOut和TransIn都会完成 gid := dtmgrpc.MustGenGid(dtmServer) m := dtmgrpc.NewMsgGrpc(dtmServer, gid).   Add(busiServer+"/api.trans.v1.Trans/TransOut", &busi.BusiReq{Amount: 30, UserId: 1}).   Add(busiServer+"/api.trans.v1.Trans/TransIn", &busi.BusiReq{Amount: 30, UserId: 2}) m.WaitResult = true err := m.Submit() logger.FatalIfError(err)

深入理解动态调用

在 kratos 使用 dtm 的分布式事务时,许多的调用是从 dtm 服务器发起的,例如 TCC 的Confirm/Cancel,SAGA/MSG 的所有调用。

dtm 无需知道组成分布式事务的相关业务 api 的强类型,它是动态的调用这些api。

grpc 的调用,可以类比于 HTTP 的 POST,其中:

  • "/api.trans.v1.Trans/TransIn" 相当于 URL 中的 Path。请注意这个Path一定是要从TransIn的Invoke函数实现里面找
  • &busi.BusiReq{Amount: 30, UserId: 1} 相当于 Post 中 Body
  • v1.Response 相当于HTTP请求的响应

通过下面这部分代码,dtm就拿到了完整信息,就能够发起完整的调用了

Add(busiServer+"/api.trans.v1.Trans/TransIn", &busi.BusiReq{Amount: 30, UserId: 1})

其他方式接入

kratos 的微服务还有非 etcd 的其他方式,下面列出它们的接入方式

直连

对于直连这种方式,您只需要在上面 dtm 的 etcd 配置基础上,将 Target 设置为空字符串即可。

直连的情况,不需要将 dtm 注册到注册中心

小结

欢迎使用 dtm,并 star 支持我们,一起共建 golang 的微服务生态

搭建 VuePress 站点必做的 10 个优化

Posted: 22 Mar 2022 08:13 PM PDT

前言

《一篇带你用 VuePress + Github Pages 搭建博客》中,我们使用 VuePress 搭建了一个博客,最终的效果查看:TypeScript 中文文档

在搭建这样一个博客后,其实还有很多的优化工作需要做,本篇我们来盘点一下那些完成基础搭建后必做的 10 个优化。

1. 开启 HTTPS

开启 HTTPS 有很多好处,比如可以实现数据加密传输等,SEO 也会更容易收录:

Google 会优先选择 HTTPS 网页(而非等效的 HTTP 网页)作为规范网页

开启 HTTPS,我们的基本步骤是:

  1. 购买下载证书
  2. 上传到服务器
  3. 开启 Nginx 配置

具体的操作步骤,可以参考 《VuePress 博客优化之开启 HTTPS》

2. Gzip 压缩

开启 Gzip 压缩将会极大的提高网站加载速度,如果服务器用的是按流量付费,就更是必须要做的内容。

如果使用的是 Nginx,由于 Nginx 内置 Gzip 压缩模块,可以直接开启:

server {   # 这里是新增的 gzip 配置   gzip on;   gzip_min_length 1k;   gzip_comp_level 6;   gzip_types application/atom+xml application/geo+json application/javascript application/x-javascript application/json application/ld+json application/manifest+json application/rdf+xml application/rss+xml application/xhtml+xml application/xml font/eot font/otf font/ttf image/svg+xml text/css text/javascript text/plain text/xml; }

关于 Gzip 压缩更多内容可以参考 《VuePress 博客优化之开启 Gzip 压缩》

3. 数据统计

添加数据统计后,可以看到网站的访问和来源情况,常添加的也就是百度统计和谷歌统计,在国内建议用百度统计。

添加统计代码很简单,往往只用在统计平台生成代码后,添加到站点就行,就比如百度的统计代码为:

<script> var _hmt = _hmt || []; (function() {   var hm = document.createElement("script");   hm.src = "https://hm.baidu.com/hm.js?82a3f80007c4e88c786f3602d0b8a215";   var s = document.getElementsByTagName("script")[0];    s.parentNode.insertBefore(hm, s); })(); </script> 

只是要注意,由于 VuePress 是单页应用,页面切换过程中,不会重新加载页面,自然也不会触发百度统计。所以我们只能统计到用户访问了页面,但具体点开了哪些文章,跳转了哪些路由并不知道。为了实现路由切换时的数据统计,我们还需要监听路由改变,手动上报数据。

更具体的步骤可以参考《VuePress 博客优化之添加数据统计功能》

4. 功能插件

如果要给站点添加各种功能,不一定就要自己手写各种代码,也可以直接利用现成的插件。

比如公告插件:

代码复制插件:

背景音乐插件:

看板娘插件:

更多的插件和效果参考《搭建 VuePress 博客,你可能会用到的一些插件》

5. 评论功能

一个网站如果有评论功能,可以与读者建立交流,优化站点,也可以及时更新文章中的错误。

添加评论功能,主流是使用 Valine 和 Vssue。

Valine 是一款基于 LeanCloud 的快速、简洁且高效的无后端评论系统,而 LeanCloud 是一种 Serverless 云服务,提供了一站式的后端服务,如数据存储、即时通讯等等。使用 Valine,需要注册 LeanCloud,注册 LeanCloud 并使用服务,需要实名认证,最终实现的效果如下:

具体的操作步骤参考《VuePress 博客优化之增加 Valine 评论功能》

而 Vssue 是一个 Vue 驱动的、基于 Issue 的评论插件,虽然有多个托管平台可以使用,这里我使用的是 GitHub,并且实现了与我 GitHub 的文章 issues 打通,实现了同步。最终的效果如下:

具体的操作步骤参考 《VuePress 博客优化之增加 Vssue 评论功能》

6. 全文搜索

VuePress 的内置搜索只会为页面的标题、h2 、 h3 以及 tags 构建搜索索引。 如果你需要全文搜索,可则以使用 Algolia 搜索。

Algolia 是一个数据库实时搜索服务,能够提供毫秒级的数据库搜索服务,并且其服务能以 API 的形式方便地布局到网页、客户端、APP 等多种场景。

像 VuePress 官方文档就是使用的 Algolia 搜索,使用 Algolia 搜索最大的好处就是方便,它会自动爬取网站的页面内容并构建索引,你只用申请一个 Algolia 服务,在网站上添加一些代码,就像添加统计代码一样,然后就可以实现一个全文搜索功能:

具体的步骤参考 《VuePress 博客优化之开启 Algolia 全文搜索》

7. SEO

如果希望自己的站点能被搜索引擎做到,就要做好 SEO,而 SEO 牵涉的地方有很多,新手建议先看下基础的文档进行学习:

  1. 《百度搜索引擎优化指南2.0》https://ziyuan.baidu.com/college/courseinfo?id=193&page=3
  2. Google 搜索中心《搜索引擎优化 (SEO) 新手指南 》https://developers.google.com/search/docs/beginner/seo-starter-guide?hl=zh-cn

很多事情是一定要做的,比如自定义标题、描述、关键词,优化链接、重定向、生成 sitemap,并提交到搜索引擎平台,再辅助使用多个站长平台,及时发现和优化问题。

具体可以参考:

  1. VuePress 博客之 SEO 优化(一)sitemap 与搜索引擎收录
  2. VuePress 博客之 SEO 优化(二)重定向
  3. VuePress 博客之 SEO 优化(三)标题、链接优化
  4. VuePress 博客之 SEO 优化(四) Open Graph protocol
  5. VuePress 博客之 SEO 优化(五)添加 JSON-LD 数据
  6. VuePress 博客之 SEO 优化(六)站长工具

8. PWA 兼容

PWA,英文全称:Progressive Web Apps, 中文翻译:渐进式 Web 应用。

实现 PWA,可以方便的让我们的网站实现桌面图标、离线缓存、推送通知等功能。

要实现 PWA 参考 《VuePress 博客优化之兼容 PWA》

9. 修改样式

网站样式总有一些不满足你期望的地方,有的时候,就需要自己修改代码。

如果你要修改主题色,VuePress 定义一些变量供以后使用,你可以创建一个 .vuepress/styles/palette.styl 文件:

// 颜色 $accentColor = #3eaf7c $textColor = #2c3e50 $borderColor = #eaecef $codeBgColor = #282c34 $arrowBgColor = #ccc $badgeTipColor = #42b983 $badgeWarningColor = darken(#ffe564, 35%) $badgeErrorColor = #DA5961  // 布局 $navbarHeight = 3.6rem $sidebarWidth = 20rem $contentWidth = 740px $homePageWidth = 960px  // 响应式变化点 $MQNarrow = 959px $MQMobile = 719px $MQMobileNarrow = 419px

如果你要自定义样式,你可以创建一个 .vuepress/styles/index.styl 文件。这是一个 Stylus文件,但你也可以使用正常的 CSS 语法。

更多的颜色修改参考 VuePress 的 palette.styl

10. 手写插件

有的时候,现有的插件实在满足不了要求,你就需要自己写一个插件了,但是你还要注意,我们写的是一个 VuePress 插件还是一个 markdown-it 插件,比如我们复制代码,我们可以使用 VuePress 插件来实现,但是如果我们要给代码块加一个 try 按钮,点击跳转到对应的 playground 页面,那就是拓展 markdown 语法了,就需要写一个 markdown-it 插件了。

但无论你写哪种插件,都提供了文章:

  1. VuePress 插件:《从零实现一个 VuePress 插件》
  2. Markdown-it 插件:《VuePress 博客优化之拓展 Markdown 语法》

系列文章

博客搭建系列,讲解如何使用 VuePress 搭建、优化博客,并部署到 GitHub、Gitee、私有服务器等平台。

系列预计 20 篇左右,本篇为第 33 篇,全系列文章地址:https://github.com/mqyqingfen...

微信:「mqyqingfeng」,进低调务实优秀的中国好青年群,PS:这是一个正经的前端群。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。

No comments:

Post a Comment