bt365体育投注.主頁欢迎您!!

    <acronym id="zvmrr"></acronym>
    <td id="zvmrr"></td>
  • <tr id="zvmrr"><label id="zvmrr"></label></tr>
  • <acronym id="zvmrr"></acronym>
  • 阿宝哥

    阿宝哥 查看完整档案

    厦门编辑集美大学  |  自动化 编辑  |  填写所在公司/组织 www.semlinker.com 编辑
    编辑

    http://www.semlinker.com/
    聚焦全栈,专注分享 Angular、TypeScript、Node.js/Java 、Spring 技术栈等全栈干货

    欢迎各位小伙伴关注本人公众号全栈修仙之路

    个人动态

    阿宝哥 发布了文章 · 12月1日

    写了 200 多篇文章后,我总结的写作心得

    本文由突破自己、写作心得、写作技巧和写作辅助工具四部分组成,每个部分都有不同的侧重点。希望阅读完本文之后,能对大家能所有启发或帮助。好的,废话不多说,我们马上进入正题。

    突破自己

    人最大的敌人是自己

    2014 年 阿宝哥 完成了 JavaEE 开发者到 Web 前端开发者角色的转换,也开启了个人博客之旅。时间一晃来到了 2017 年,那一年阿宝哥开始在思否写 Angular 修仙之路专栏,目前该专栏已有 「156」 篇原创文章。其实开始写专栏的时候,也犹豫了好多天。因为始终觉得写专栏是很慎重的一件事情,另外又很担心自己写的文章,质量不够好,不能给读者带来价值或启发,然后被读者 ”吐槽“。

    在纠结了很多天之后,阿宝哥最终下定了决心,因为想清楚了自己的初衷。那时候刚好公司项目要从 AngularJS 升级到 Angular 2,所以阿宝哥要重新开始学习 Angular 2 相关的技术。那时国内 Angular 2 的学习资源很少,官方文档主要还是英文的,学习起来相对比较吃力。

    在学习过程中自己也走了不少弯路,也遇到了挺多难点,为了让别人少走弯路,同时做好个人的知识沉淀,想清楚自己的初衷之后,阿宝哥迈出了第一步。之后,阿宝哥很用心输出了几篇文章,收到了读者不错的评价。这给阿宝哥很大的鼓励,也就开启了疯狂输出的模式,最终收获了思否两季 Top Writer 和思否年度优秀文章作者。

    说来惭愧,后面由于某些原因,Angular 修仙之路专栏 就断更了。直到 2019 年 11 月受到好友 前端自习课 号主 “安总” 的影响,阿宝哥又继续开始写作。他坚持每天不间断发文,截止 2020 年 11 月 29 日,他已经连续更新 792 天,这种毅力和恒心非常人能及,前端的小伙伴们可以多多关注他哟。

    2020 年是不平凡的一年,今年阿宝哥也迈出了一大步,写了三本免费的 PDF 电子书。此外,在不断地努力下,掘金等级也从 Lv3 升到了 Lv6,这里衷心感谢掘金老铁和公众号粉丝一直以来的鼓励与支持??????。

    关注「全栈修仙之路」阅读阿宝哥原创的 3 本免费电子书(累计下载近2万)及 50 几篇 “重学TS” 教程。

    为什么开篇阿宝哥会讲述如何 “突破自己” 呢?这是因为阿宝哥经常鼓励身边的同事或好友到 掘金思否 等技术社区写博客,细聊之后发现他们大部分人也有阿宝哥当初写 Angular 修仙之路专栏 的困惑,害怕文章写得不够好,不能给读者带来价值或启发,被读者 “吐槽”。因此,阿宝哥也跟他们分享了自己的写作经历与心得,之后他们中的一些人终于迈出了第一步,开始在技术社区发文了。当然,有点可惜的是,很多人只是迈出了第一步,没有再继续坚持了。

    好的,接下来我们步入正题,阿宝哥将分享这几年的写作心得。

    写作心得

    如何选定主题

    如何选定写作主题,相信这对很多小伙伴来说是一个比较头疼的问题。针对这个问题,阿宝哥有以下几个方向供参考:

    当然,上面只是列举了一小部分,其他的写作方向,大家可以参考一下技术社区的排行榜或者分析一下你所关注的技术大佬,平时都是写哪些类型的文章。相信一些小伙伴们写简历时,也很头疼不知道写哪些内容。简历上的项目经验,只是介绍用了什么技术栈,开发了什么功能。其实这些内容对面试官来说,并没有多大的参考价值。他们更希望在你的简历中,看到项目中的亮点和难点或者你对项目或团队可量化的贡献度。

    那么如何找到项目的 “亮点” 呢?“生活中不是缺少美,而是缺少发现”。同样,在完成项目的某个功能之后,可以多问问自己。比如,这个功能背后的技术,自己掌握了么?有没有其他相关的技术?这项技术还有哪些应用场景?这项技术有没有什么缺点?这个功能还能不能做得更好?等等。当你慢慢养成这种习惯之后,你的写作素材会变得越来越多。然而当你定好写作方向时,接下来该如何开始写作呢?

    如何开始写作

    当你定好写作方向后,阿宝哥建议你先不急着动手,而是先想好文章的大纲。这里阿宝哥以 “你不知道的 XXX” 系列文章为例,来介绍一下写 Web API 文章的通用大纲:

    image

    整个系列文章大纲的结构基本一致,对应的通用大纲如下所示:

    • XXX 是什么/ 什么是 XXX
    • XXX API 简介
    • XXX 应用场景
    • XXX 与相关知识点的区别

    如果你也想写 Web API 相关的文章,可以试试阿宝哥介绍的通用大纲。那如果写非 Web API 的文章,应该怎么办?针对这种情形,你可以在主流的技术社区搜索相关类型的文章,挑选阅读量和点赞量较多的文章,然后分析一下这些文章的目录结构,进而整理一份适合自己的目录结构

    定好文章的目录结构之后,你就可以开始写作了。接下来,阿宝哥将分享一些写作技巧。

    写作技巧

    当然除了上述的写作技巧之外,阿宝哥还有以下几个写作建议:

    • 写技术文章时,若用到一些重要的概念或技术的话,最好能提前做一些解释;
    • 文章中引用的代码,最好能标识一下路径。如果是自己写的可执行代码,最好能运行一下,保证一下代码的正确性和可用性;
    • 尽量减少读者的不必要的操作,为引用的内容添加快速链接。比如介绍一些不错的开源项目,不仅仅是给出开源项目的名称,最好还能为每个开源项目添加一个快速链接,避免感兴趣的读者还得手动进行搜索;
    • 写文章时,多画图,特别是源码分析的文章,避免贴一大堆代码;不知道怎么画图的话,可以利用搜索引擎,使用相应的关键字进行搜索,然后筛选一些不错的图片进行参考;
    • 写完文章后,最好能自己校对 1-2 遍,避免出现过多的错别字,影响读者体验;
    • 写完文章后,建议发到主流的技术社区,这样的话,你不仅可以让更多人看到你的文章,还可以收到不同读者的反馈。如果文章对其他人有所帮助的话,你还能得到读者的认可与鼓励,从而让你拥有继续写作的动力。当然如果你写的文章质量都比较高,你还能建立自己的个人品牌,这对以后的发展还是挺有帮助的。

    另外,如果你对如何写源码分析的文章感兴趣的话,可以阅读一下 使用这些思路与技巧,我读懂了多个优秀的开源项目 这篇文章。俗话说得好,“工欲善其事,必先利其器”,最后阿宝哥将介绍一些自己常用的写作辅助工具。

    写作辅助工具

    Markdown 工具

    Typora
    Typora —— 一款 Markdown 编辑器,可用于写文章和制作 PDF 电子书。

    https://typora.io/

    (图片来源:https://typora.io/

    Markdown Nice
    Markdown Nice —— 在线 Markdown 排版工具,支持微信公众号、知乎和掘金。

    https://mdnice.com/

    (图片来源:https://mdnice.com/

    绘图工具

    drawio
    DrawIo —— 一款免费的绘图工具,支持绘制流程图、实体关系图和系统架构图等。

    https://github.com/jgraph/dra...

    (图片来源:https://www.diagrams.net/

    ProcessOn
    ProcessOn —— 在线作图、实时协作,支持流程图、思维导图、原型图、UML、网络拓扑图、组织结构图等。

    https://www.processon.com/

    (图片来源:https://www.processon.com/

    excalidraw
    excalidraw —— 虚拟白板,用于绘制手绘的示意图。

    https://github.com/excalidraw...

    (图片来源:https://github.com/excalidraw...

    其实画完图之后,还有一个很重要的环节,即图形配色。好的配色能让你画的图,更高端大气。这里阿宝哥推荐两个在线配色的网站:https://colordrop.io/https://coolors.co/

    源代码处理

    Carbon
    Carbon —— 创建并共享源代码的精美图片。

    https://carbon.now.sh/

    (图片来源:https://carbon.now.sh/

    UML 生成
    TypeScript UML Playground —— 在线 TypeScript UML 图生成。

    https://tsuml-demo.firebaseap...

    文章封面

    当你写完文章之后,如果要发到微信公众号或掘金上,你还需要为你的文章配一张封面。这时你可以考虑使用 创客贴凡科快图 等在线的设计工具,从而利用平台提供的模板来快速设计文章封面。而阿宝哥用得比较多的是从 pixabaypexels 这两个免费的图片素材库挑选封面。

    pixabay
    Pixabay —— 可免费使用的正版高清图片素材库

    https://pixabay.com/zh/

    (图片来源:https://pixabay.com/zh/

    pexels
    Pexels —— 免费和图片和影片库。

    https://www.pexels.com/

    (图片来源:https://www.pexels.com/

    上面介绍的工具都是 阿宝哥 在写作过程中常用的辅助工具,如果你有其他不错的工具欢迎给阿宝哥留言。当然如果你在写作时,有遇到问题或困惑的话,也可以随时跟阿宝哥交流哟。

    关注「全栈修仙之路」阅读阿宝哥原创的 3 本免费电子书(累计下载近2万)及 8 篇源码分析系列教程。

    这么多年下来,个人感觉写作是一个很不错的知识输出方式,通过写作可以让你对输入的知识进行消化和吸收。阿宝哥这几年坚持下来,感觉收获蛮大的。不仅仅是写作能力,思考/总结能力和画图能力都有挺大的提升。希望看完这篇文章之后,还没迈出第一步的小伙伴能给自己定个小目标,勇于突破自己,开始开启你的写作之旅。

    查看原文

    赞 12 收藏 8 评论 7

    阿宝哥 赞了文章 · 11月29日

    2020 中国技术先锋年度评选启动,三大类别奖项申报中

    2020 中国技术先锋年度评选

    前言

    如果列举 2020 年中国技术“热词”,频频被刷屏的“新基建”一定位列其中。

    突如其来的疫情让全人类经历了一次“数字化生存”大考,政企上云、传统行业的数字化转型也在大环境中被催化。作为新基建的底层支撑,芯片、服务器、操作系统、中间件、数据库等一系列信创技术,在全国范围内被广泛关注。

    日新月异的技术革命,数字经济的新一轮爆发,背后是无数开发者和科技企业夜以继日的付出。他们面对不断变化的外部环境,扎根行业,他们信奉技术力量敢于技术创新践行技术信仰,他们是技术先锋,探索改变世界的方向。

    SegmentFault 思否作为中国领先的新一代开发者社区,依托数百万开发者用户数据分析,及各科技企业厂商和个人在国内技术领域的行为、影响力指标,即将展开第二届“中国技术先锋”年度评选

    入选企业和个人将受邀参加 2020 中国技术先锋颁奖晚宴,并获得 SegmentFault 思否优先报道持续关注流量扶持的机会,我们将始终关注那些用技术推动时代变革的先锋企业、用技术探索未来的先锋开发者。

    奖项名单(拟)如下,现报名通道已正式开启,欢迎点击文末 “阅读原文”,填写申报表,推荐或自荐。

    中国技术品牌影响力企业榜

    1、 榜单必须以公司为主体申报参选,可推荐或自荐;
    2、 参选公司必须为科技型企业,拥有成熟的产品服务、顶级技术人才储备和不断创新的技术能力;
    3、 参评公司必须持续关注开发者生态,有各种形式的技术内容输出,如:积极参与或举办技术活动、有持续更新的技术博客、有成熟开源项目/积极参与开源等;
    4、 参评公司技术品牌美誉度较高,在开发者人群中具备较高知名度或影响力;

    参与榜单评选的公司,可结合自身优势同时申报以下 1 项或多项奖项,奖项可以以公司为主体申报,也可以以团队 / 产品线为主体申报。

    生态运营奖

    通过输出技术内容、参与或组织技术活动等形式,在构建和持续运营开发者生态上取得突出成果的企业、团队或产品线。

    技术向善奖

    具备较强社会责任感,能够将技术品牌构建与社会、经济、教育、公益相结合,或产品技术成果对于社会有较大贡献的企业、团队或产品线。

    技术文化奖

    企业以技术为导向或具备较好的技术文化,能够通过技术博客、开发者社区、技术活动将前沿技术成果和经验进行系统性对外&对内输出的企业、团队或产品线。

    最佳技术服务奖

    为行业提供基础技术服务的创新型科技企业或产品线,数字经济的基石。

    最佳技术应用奖

    能够将行业领先技术应用到某一特定行业领域,并取得突出成绩的企业或产品线。

    中国开源先锋30 人

    1、 榜单必须以个人为主体申报参评,可推荐或自荐;
    2、 参选个人必须持续关注开源,以不同形式积极参与开源,如:拥有成熟的开源项目、积极参与代码贡献、积极布道或组织开源活动、主导开源社区建设等;
    3、 参选个人可以是开源项目的开发者,也可以是开源社区的运营人员,不限技术背景,但应在开源领域取得了一定建树;

    最受开发者欢迎的技术活动

    1、 参选主体必须为在 2020 年 1 月 1 日至 12 月 31 日已举办完成的技术活动,可推荐或自荐;
    2、 参选活动的主要受众必须为开发者,且开发者占参会人员比例 80% 以上;
    3、 参选活动可以是线上/线下技术沙龙、技术竞赛、技术大会等,形式不限,但应为面向开发者的公开活动,在开发者人群中具备较高知名度或影响力/闭门会/培训活动/公司内部活动不在本次评选范围中;
    4、 参选活动须具备独特性或亮点,活动美誉度高,能对技术创新、开发者成长带来正向影响。

    评选维度:

    1、 活动规模及影响力:活动参与和影响的开发者人数;
    2、 活动内容质量:是否有知名技术专家参会,是否有较为丰富的技术内容输出和沉淀;
    3、活动品牌影响力:是否为系列活动,活动 IP 打造等。

    TopWriter年度榜单

    SegmentFault 思否社区有一群卓越的开发者,他们热衷于分享知识与经验,他们布道技术未来,他们让众多开发者受益,他们叫「Top Writer」。

    开发者是社区的基石,也是行业发展、技术发展的源动力。

    SegmentFault 思否将根据社区用户行为大数据(如文章 & 问答发布数量、获得声望 & 点赞量等)等综合分析,从「技术问答」和「专栏文章」两个维度进行了本年度「Top Writer」的评选。

    评选流程 & 参选方式

    1.报名征集:即日起至 2020?年 11 月 30?日
    2.走访/调研 & 评选评审
    3.榜单发布:2020?年 12 月
    4.报名链接:报名报单

    2020 中国技术先锋年度评选

    评选咨询:pr@sifou.com
    评选咨询:微信搜索 sifous
    媒体合作:bd@sifou.com

    查看原文

    赞 22 收藏 1 评论 0

    阿宝哥 发布了文章 · 11月24日

    这些开源项目,让你轻松应对十大工作场景

    俗话说得好,工欲善其事必先利其器。本文阿宝哥将介绍一些优秀的开源项目,利用这些开源项目,你将能能轻松应对以下十个工作场景:文件上传、图片处理、文档处理、网络请求、数据存储、微前端、表单设计器、H5 页面设计器、文档管理和 API 管理。

    文件上传

    uppy

    The next open source file uploader for web browsers ??

    https://github.com/transloadi...

    uppy 是一个体验顺滑、模块化的 JavaScript 文件上传器,可以无缝地与任何应用程序集成。它速度快,使用方便,可以让你专注比构建文件上传器更重要的问题。该库拥有以下特性:

    • 支持 I18n 及可访问性;
    • 轻量,基于模块化的插件架构,可按需加载;
    • 通过开放的 tus 标准,可恢复文件上传,可以解决上传过程中网络故障的问题。

    filepond

    ?? A flexible and fun JavaScript file upload library

    https://github.com/pqina/file...

    filepond 是一个 JavaScript 库,可以上传你扔给它的任何内容,优化图像以加快上传速度,并提供出色的,可访问的,柔滑的用户体验。该库拥有以下特性:

    • 接受目录,文件,Blobs,本地 URL,远程 URL 和 Data URIs;
    • 图像优化,自动调整图像大小,支持裁剪,过滤和修复 EXIF 方向;
    • 支持拖拽文件,从文件系统选择文件,复制和粘贴文件或使用 API 添加文件;
    • 使用 AJAX 进行异步上传,支持分块上传,可以将文件编码为 base64 数据,并可以通过表单提交。

    ? 扩展阅读 ?

    关注「全栈修仙之路」阅读阿宝哥原创的 3 本免费电子书(累计下载近2万)及 50 几篇 “重学TS” 教程。

    图片处理

    tui.image-editor

    ???? Full-featured photo image editor using canvas. It is really easy, and it comes with great filters.

    https://github.com/nhn/tui.im...

    tui.image-editor 是使用 HTML5 Canvas 的全功能图像编辑器。它易于使用,并提供强大的过滤器。同时它支持对图像进行裁剪、翻转、旋转、绘图、形状、文本、遮罩和图片过滤等操作。该库的浏览器兼容情况如下:

    • Chrome
    • Edge
    • Safari
    • Firefox
    • IE 10+

    cropperjs

    JavaScript image cropper.

    https://github.com/fengyuanch...

    Cropper.js 是一款非常强大却又简单的图片裁剪工具,它可以进行非常灵活的配置,支持手机端使用,支持包括 IE9 以上的现代浏览器。它可以用于满足诸如裁剪头像上传、商品图片编辑之类的需求。该库拥有以下特性:

    • 支持 39 个配置选项;
    • 支持 27 个方法;
    • 支持 6 种事件;
    • 支持 touch(移动端);
    • 支持缩放、旋转和翻转;
    • 支持在画布上裁剪;
    • 支持在浏览器端通过画布裁剪图像;
    • 支持处理 Exif 方向信息;
    • 跨浏览器支持。

    ? 扩展阅读 ?

    文档处理

    kkFileView

    使用 Spring Boot 打造文件文档在线预览项目解决方案,支持 doc、docx、ppt、pptx、xls 等文件在线预览。

    https://github.com/kekingcn/k...

    kkFileView 为文件文档在线预览解决方案,该项目使用流行的 Spring Boot 搭建,易上手和部署,基本支持主流办公文档的在线预览,如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等。该库拥有以下特性:

    • 一键部署,快速接入:支持 Windows、Linux 平台一键部署,两行 JS 代码就可以接入预览;
    • 支持常见文件格式,兼容新版 Office 文档:支持文本、图片、Office 文档、WPS 文档、PDF、视频、音频、压缩包等常见文件类型预览;
    • 支持多种预览模式灵活切换:支持 PDF、懒加载分页图、轮播图片等预览模式动态配置、灵活切换;
    • 独立部署,提供 Restful 接口,适用于微服务场景:独立于业务系统外,提供 Restful Http 接口,开发语言无关,微服务场景下直接提供在线预览服务。

    Luckysheet

    Luckysheet is an online spreadsheet like excel that is powerful, simple to configure, and completely open source.

    https://github.com/mengshukej...

    Luckysheet ,一款纯前端类似 excel 的在线表格,功能强大、配置简单、完全开源。该库拥有以下特性:

    • 格式设置:样式、条件格式、文本对齐及旋转、支持文本的截断、溢出、自动换行和单元格多样式;
    • 单元格:拖拽选取来修改单元格、选取下拉填充、自动填充选项、多选区操作、查找与替换和合并单元格;
    • 操作体验:撤销/重做、复制/粘贴/剪切操作、快捷键支持和格式刷;
    • 公式和函数:内置公式、公式支持数组、远程公式和自定义公式;
    • 数据透视图:字段拖拽、聚合方式、筛选数据和数据透视表下钻。

    ? 扩展阅读 ?

    网络请求

    Axios

    Promise based HTTP client for the browser and node.js

    https://github.com/axios/axios

    Axios 是一个基于 Promise 的 HTTP 客户端,该库拥有以下特性:

    • 支持 Promise API;
    • 能够拦截请求和响应;
    • 能够转换请求和响应数据;
    • 客户端支持防御 CSRF 攻击;
    • 同时支持浏览器和 Node.js 环境;
    • 能够取消请求及自动转换 JSON 数据。

    react-query

    ?? Hooks for fetching, caching and updating asynchronous data in React

    https://github.com/tannerlins...

    react-query 是一个用在 React 项目中,用于获取、缓存和更新异步数据的钩子。该库拥有以下特性:

    • 多层缓存 + 自动垃圾回收;
    • 支持分页和基于游标的查询;
    • 支持加载更多、无限滚动查询和滚动恢复;
    • 自动缓存 + 重新获取(重新验证时有效,窗口重新聚焦,轮询/实时)。

    ? 扩展阅读 ?

    数据存储

    PouchDB

    ?? - PouchDB is a pocket-sized database.

    https://github.com/pouchdb/po...

    PouchDB 是一个浏览器内数据库,允许应用程序在本地保存数据,以便用户即使在离线时也可以享受应用程序的所有功能。另外,数据在客户端之间是同步的,因此用户可以随时随地保持最新状态。

    PouchDB 也在 Node.js 中运行,可以用作与 CouchDB 兼容的服务器的直接接口。该 API 在每个环境中工作都是相同的,因此你可以花更少的时间来担心浏览器的差异,而花更多的时间来编写干净、一致的代码。

    PouchDB 支持所有现代浏览器:

    • Firefox 29+ (Including Firefox OS and Firefox for Android)
    • Chrome 30+
    • Safari 5+
    • Internet Explorer 10+
    • Opera 21+
    • Android 4.0+
    • iOS 7.1+
    • Windows Phone 8+

    PouchDB 在幕后使用 IndexedDB,若当前环境不支持 IndexedDB 则回退到 Web SQL。

    Rxdb

    ?? ?? ?? A realtime Database for JavaScript Applications.

    https://github.com/pubkey/rxdb

    RxDB(Reactive Database 的缩写)是 NoSQL 数据库,用于 JavaScript 应用程序,如网站,混合应用程序,Electron Apps,Progressive Web Apps 和 Node.js。响应式意味着你不仅可以查询当前状态,还可以订阅所有状态更改,比如查询的结果或文档的单个字段。

    RxDB 支持以下特性:

    • Mango-Query:支持 mquery API 从集合中获取数据,支持链式的 mongoDB 查询风格。
    • Replication:因为 RxDB 依赖于 PouchDB,因此很容易实现终端设备与服务器之间的数据同步。
    • Reactive:RxDB 使得同步 DOM 的状态变得很简单。
    • MultiWindow/Tab:当 RxDB 的两个实例使用相同的存储引擎,它们的状态和操作流将会被广播。这意味着对于两个浏览器窗口,窗口 #1 的数据变化也会自动影响窗口 #2 的数据状态。
    • Schema:通过 jsonschema 来定义 Schemas,它们用来描述数据格式。
    • Encryption:通过将模式字段设置为encrypted,该字段的值将以加密模式存储,没有密码就无法读取。

    ? 扩展阅读 ?

    微前端

    qiankun

    ?? ?? Blazing fast, simple and completed solution for micro frontends.

    https://github.com/umijs/qiankun

    qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。目前 qiankun 已在蚂蚁内部服务了超过 200+ 线上应用,在易用性及完备性上,绝对是值得信赖的。

    该库拥有以下特性:

    • ?? 基于 single-spa 封装,提供了更加开箱即用的 API;
    • ?? 技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架;
    • ?? HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单;
    • ?? 样式隔离,确保微应用之间样式互相不干扰;
    • ?? JS 沙箱,确保微应用之间 全局变量/事件 不冲突;
    • ?? 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度;
    • ?? umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。

    single-spa

    The router for easy microfrontends

    https://github.com/single-spa...

    Single-spa 是一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架。 使用 single-spa 进行前端架构设计可以带来很多好处,例如:

    • 独立部署每一个单页面应用;
    • 改善初始加载时间,迟加载代码;
    • 新功能使用新框架,旧的单页应用不用重写可以共存;
    • 在同一页面上使用多个前端框架 而不用刷新页面 (React,AngularJS,Angular,Ember 等)。

    ? 扩展阅读 ?

    表单设计器

    form-generator

    ?Element UI 表单设计及代码生成器

    https://github.com/JakHuang/f...

    image

    form-render

    ???♀? 易用的跨组件体系的表单渲染引擎 - 通过 JSON Schema 快速生成自定义表单配置界面

    https://github.com/alibaba/fo...

    H5 页面设计器

    gods-pen

    基于 vue 的高扩展在线网页制作平台,可自定义组件,可添加脚本,可数据统计。

    https://github.com/ymm-tech/g...

    luban-h5

    类似易企秀的 H5 制作、建站工具、可视化搭建系统.

    https://github.com/ly525/luba...

    image

    文档管理

    storybook

    ?? The UI component explorer. Develop, document, & test for React, Vue, Angular, Ember, Web Components, & more!

    https://github.com/storybookj...

    Storybook 是一个 UI 组件的开发环境。它允许你能够浏览一个组件库,查看每个组件的不同状态,以及支持交互式的方式开发和测试组件。

    Storybook 在你的应用程序之外运行。这允许你能够独立的开发 UI 组件,你可以提高组件的可重用性、可测试性和开发速度。你可以快速构建,而无需担心应用程序特定的依赖项。

    TypeDoc

    Documentation generator for TypeScript projects.

    在线地址:https://typedoc.org/

    TypeDoc 用于将 TypeScript 源代码中的注释转换为 HTML 文档或 JSON 模型。它可灵活扩展,并支持多种配置。

    API 管理

    yapi

    YApi 是一个可本地部署的、打通前后端及QA的、可视化的接口管理平台

    https://github.com/ymfe/yapi

    YApi 是高效易用功能强大的 api 管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API,YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以及简单的点击操作就可以实现接口的管理。

    该项目拥有以下特性:

    • 基于 Json5 和 Mockjs 定义接口返回数据的结构和文档,效率提升多倍;
    • 扁平化权限设计,即保证了大型企业级项目的管理,又保证了易用性;
    • 类似 Postman 的接口调试;
    • 自动化测试, 支持对 Response 断言;
    • MockServer 除支持普通的随机 mock 外,还增加了 Mock 期望功能,根据设置的请求过滤规则,返回期望数据;
    • 支持 postman, har, swagger 数据导入;
    • 免费开源,内网部署,信息再也不怕泄露了。

    APIJSON

    ??码云最有价值开源项目 ??后端接口和文档自动化,前端(客户端) 定制返回 JSON 的数据和结构!

    https://github.com/Tencent/AP...

    APIJSON 是一种专为 API 而生的 JSON 网络传输协议 以及 基于这套协议实现的 ORM 库。APIJSON 为 “简单的增删改查、复杂的查询、简单的事务操作” 提供了完全自动化的 API,能大幅降低开发和沟通成本,简化开发流程,缩短开发周期。它适合中小型前后端分离的项目,尤其是 BaaS、Serverless、互联网创业项目和企业自用项目。

    该项目拥有以下特性:

    • 通过自动化API,前端可以定制任何数据、任何结构!
    • 大部分 HTTP 请求后端再也不用写接口了,更不用写文档了!
    • 前端再也不用和后端沟通接口或文档问题了!再也不会被文档各种错误坑了!
    • 后端再也不用为了兼容旧接口写新版接口和文档了!再也不会被前端随时随地没完没了地烦了!

    关注「全栈修仙之路」阅读阿宝哥原创的 3 本免费电子书(累计下载近2万)及 7 篇源码分析系列教程。

    本文阿宝哥介绍了可应对十大工作场景的开源项目,希望对大家能有所帮助或启发。如果你有其他好的开源项目,欢迎留言推荐给阿宝哥哟。

    查看原文

    赞 34 收藏 26 评论 3

    阿宝哥 发布了文章 · 11月23日

    postMessage 还能这样玩

    在日常工作中,消息通信是一个很常见的场景。比如大家熟悉 B/S 结构,在该结构下,浏览器与服务器之间是基于 HTTP 协议进行消息通信:

    然而除了 HTTP 协议之外,在一些对数据实时性要求较高的场景下,我们会使用 WebSocket 协议来完成消息通信:

    对于这两种场景,相信大家都不会陌生。接下来,阿宝哥将介绍消息通信的另外一种场景,即父页面与 iframe 加载的子页面之间,如何进行消息通信。

    为什么会突然写这个话题呢?其实是因为在近期项目中,阿宝哥需要实现父页面与 iframe 加载的子页面之间的消息通信。另外,刚好近期阿宝哥在写 源码分析 专题,所以就到 Github 上搜索 ?? 了一番,然后找到了一个不错的项目 —— Postmate

    在阅读完 Postmate 源码之后,阿宝哥觉得该项目的一些设计思想挺值得借鉴的,所以就写了这篇文章来跟大家分享一下。阅读完本文之后,你将学到以下知识:

    • 消息系统中握手的作用及如何实现握手;
    • 消息模型的设计及如何实现消息验证来保证通信安全;
    • postMessage 的使用及如何利用它实现父子页面的消息通信;
    • 消息通信 API 的设计与实现。

    好的,废话不多说,我们先来简单介绍一下 Postmate

    关注「全栈修仙之路」阅读阿宝哥原创的 3 本免费电子书(累计下载近2万)及 50 几篇 “重学TS” 教程。

    一、Postmate 简介

    Postmate 是一个强大,简单,基于 Promise 的 postMessage 库。它允许父页面以最小的成本与跨域的子 iframe 进行通信。该库拥有以下特性:

    • 基于 Promise 的 API,可实现优雅而简单的通信;
    • 使用 消息验证 来保护双向 父 <-> 子 消息通信的安全;
    • 子对象公开父对象可以访问的可检索的模型对象;
    • 子对象可派发父对象已监听的事件;
    • 父对象可以调用子对象中的函数;
    • 零依赖。如果需要可以为 Promise API 提供自定义 polyfill 或抽象;
    • 轻量,大小约 1.6 KB(minified & gzipped)。

    接下来阿宝哥将从如何进行握手、如何实现双向消息通信和如何断开连接,这三个方面来分析一下 Postmate 这个库。另外,在此期间还会穿插介绍 Postmate 项目中一些好的设计思路。

    二、如何进行握手

    TCP 建立连接的时候,需要进行三次握手。同样,当父页面与子页面通信的时候,Postmate 也是通过 “握手” 来确保双方能正常通信。因为 Postmate 通信的基础是基于 postMessage,所以在介绍如何握手之前,我们先来简单了解一下 postMessage API。

    2.1 postMessage 简介

    对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议、端口号以及主机时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

    2.1.1 postMessage() 语法
    otherWindow.postMessage(message, targetOrigin, [transfer]);
    • otherWindow:其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象等。
    • message:将要发送到其他 window 的数据,它将会被结构化克隆算法序列化。
    • targetOrigin:通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串 "*"(表示无限制)或者一个 URI。
    • transfer(可选):是一串和 message 同时传递的 Transferable 对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

    发送方通过 postMessage API 来发送消息,而接收方可以通过监听 message 事件,来添加消息处理回调函数,具体使用方式如下:

    window.addEventListener("message", receiveMessage, false);
    
    function receiveMessage(event) {
      let origin = event.origin || event.originalEvent.origin; 
      if (origin !== "http://semlinker.com") return;
    }

    2.2 Postmate 握手的实现

    在电信和微处理器系统中,术语握手(Handshake,亦称为交握)具有以下含义:

    • 在数据通信中,由硬件或软件管理的事件序列,在进行信息交换之前,需要对操作模式的状态互相达成协定。
    • 在接收站和发送站之间建立通信参数的过程。

    对于通信系统来说,握手是在通信电路建立之后,信息传输开始之前。 握手用于达成参数,如信息传输率,字母表,奇偶校验, 中断过程,和其他协议特性

    而对于 Postmate 这个库来说,握手是为了确保父页面与 iframe 子页面之间可以正常的通信,对应的握手流程如下所示:

    在 Postmate 中,握手消息是由父页面发起的,在父页面中要发起握手信息,首先需要创建 Postmate 对象:

    const postmate = new Postmate({
      container: document.getElementById('some-div'), // iframe的容器
      url: 'http://child.com/page.html', // 包含postmate.js的iframe子页面地址
      name: 'my-iframe-name' // 用于设置iframe元素的name属性
    });

    在以上代码中,我们通过调用 Postmate 构造函数来创建 postmate 对象,在 Postmate 构造函数内部含有两个主要步骤:设置 Postmate 对象的内部属性和发送握手消息:

    以上流程图对应的代码相对比较简单,这里阿宝哥就不贴详细的代码了。感兴趣的小伙伴可以阅读 src/postmate.js 文件中的相关内容。为了能够响应父页面的握手信息,我们需要在子页面中创建一个 Model 对象:

    const model = new Postmate.Model({
      // Expose your model to the Parent. Property values may be functions, promises, or regular values
      height: () => document.height || document.body.offsetHeight
    });

    其中 Postmate.Model 构造函数的定义如下:

    // src/postmate.js
    Postmate.Model = class Model {
      constructor(model) {
        this.child = window;
        this.model = model;
        this.parent = this.child.parent;
        return this.sendHandshakeReply();
      }
    }

    在 Model 构造函数中,我们可以很清楚地看到调用 sendHandshakeReply 这个方法,这里我们只看核心的代码:

    现在我们来总结一下父页面和子页面之间的握手流程:当子页面加载完成后,父页面会通过 postMessage API 向子页面发送 handshake 握手消息。在子页面接收到 handshake 握手消息之后,同样也会使用 postMessage API 往父页面回复 handshake-reply 消息。

    另外,需要注意的是,为了保证子页面能收到 handshake 握手消息,在 sendHandshake 方法内部会启动一个定时器来执行发送操作:

    // src/postmate.js
    class Postmate {
      sendHandshake(url) {
        return new Postmate.Promise((resolve, reject) => {
          const loaded = () => {
            doSend();
            responseInterval = setInterval(doSend, 500);
          };
    
          if (this.frame.attachEvent) {
            this.frame.attachEvent("onload", loaded);
          } else {
            this.frame.addEventListener("load", loaded);
          }
          
          this.frame.src = url;
        });
      }
    }

    当然为了避免发送过多无效的握手信息,在 doSend 方法内部会限制最大的握手次数:

    const doSend = () => {
      attempt++;
      this.child.postMessage(
        {
          postmate: "handshake",
          type: messageType,
          model: this.model,
        },
        childOrigin
      );
      // const maxHandshakeRequests = 5;
      if (attempt === maxHandshakeRequests) {
         clearInterval(responseInterval);
      }
    };

    在主应用和子应用双方完成握手之后,就可以进行双向消息通信了,下面我们来了解一下如何实现双向消息通信。

    三、如何实现双向消息通信

    在调用 PostmatePostmate.Model 构造函数之后,会返回一个 Promise 对象。而当 Promise 对象的状态从 pending 变为 resolved 之后,就会分别返回 ParentAPIChildAPI 对象:

    Postmate

    // src/postmate.js
    class Postmate {
      constructor({
        container = typeof container !== "undefined" ? container : document.body,
        model, url, name, classListArray = [],
      }) {
        // 省略设置 Postmate 对象的内部属性
        return this.sendHandshake(url);
      }
      
      sendHandshake(url) {
        // 省略部分代码
        return new Postmate.Promise((resolve, reject) => {
          const reply = (e) => {
            if (!sanitize(e, childOrigin)) return false;
            if (e.data.postmate === "handshake-reply") {
              return resolve(new ParentAPI(this));
            }
            return reject("Failed handshake");
          };
        });
      }
    }

    ParentAPI

    class ParentAPI{
      +get(property: any) // 获取子页面中Model对象上的property属性上的值
      +call(property: any, data: any) // 调用子页面中Model对象上的方法
      +on(eventName: any, callback: any) // 监听子页面派发的事件
      +destroy() // 移除事件监听并删除iframe
    }

    Postmate.Model

    // src/postmate.js
    Postmate.Model = class Model {
      constructor(model) {
        this.child = window;
        this.model = model;
        this.parent = this.child.parent;
        return this.sendHandshakeReply();
      }
    
      sendHandshakeReply() {
        // 省略部分代码
        return new Postmate.Promise((resolve, reject) => {
          const shake = (e) => {
            if (e.data.postmate === "handshake") {
              this.child.removeEventListener("message", shake, false);
              return resolve(new ChildAPI(this));
            }
            return reject("Handshake Reply Failed");
          };
          this.child.addEventListener("message", shake, false);
        });
      }
    };

    ChildAPI

    class ChildAPI{
      +emit(name: any, data: any)
    }

    3.1 子页面 -> 父页面

    3.1.1 子页面发送消息
    const model = new Postmate.Model({
      // Expose your model to the Parent. Property values may be functions, promises, or regular values
      height: () => document.height || document.body.offsetHeight
    });
    
    model.then(childAPI => {
      childAPI.emit('some-event', 'Hello, World!');
    });

    在以上代码中,子页面可以通过 ChildAPI 对象提供的 emit 方法来发送消息,该方法的定义如下:

    export class ChildAPI {
      emit(name, data) {
        this.parent.postMessage(
          {
            postmate: "emit",
            type: messageType,
            value: {
              name,
              data,
            },
          },
          this.parentOrigin
        );
      }
    }
    3.1.2 父页面监听消息
    const postmate = new Postmate({
      container: document.getElementById('some-div'), // iframe的容器
      url: 'http://child.com/page.html', // 包含postmate.js的iframe子页面地址
      name: 'my-iframe-name' // 用于设置iframe元素的name属性
    });
    
    postmate.then(parentAPI => {
      parentAPI.on('some-event', data => console.log(data)); // Logs "Hello, World!"
    });

    在以上代码中,父页面可以通过 ParentAPI 对象提供的 on 方法来注册事件处理器,该方法的定义如下:

    export class ParentAPI {
      constructor(info) {
        this.parent = info.parent;
        this.frame = info.frame;
        this.child = info.child;
    
        this.events = {};
    
        this.listener = (e) => {
          if (!sanitize(e, this.childOrigin)) return false;
                // 省略部分代码
          if (e.data.postmate === "emit") {
            if (name in this.events) {
              this.events[name].forEach((callback) => {
                callback.call(this, data);
              });
            }
          }
        };
    
        this.parent.addEventListener("message", this.listener, false);
      }
    
      on(eventName, callback) {
        if (!this.events[eventName]) {
          this.events[eventName] = [];
        }
        this.events[eventName].push(callback);
      }
    }

    3.2 消息验证

    为了保证通信的安全,在消息处理时,Postmate 会对消息进行验证,对应的验证逻辑被封装到 sanitize 方法中:

    const sanitize = (message, allowedOrigin) => {
      if (typeof allowedOrigin === "string" && message.origin !== allowedOrigin)
        return false;
      if (!message.data) return false;
      if (typeof message.data === "object" && !("postmate" in message.data))
        return false;
      if (message.data.type !== messageType) return false;
      if (!messageTypes[message.data.postmate]) return false;
      return true;
    };

    对应的验证规则如下:

    • 验证消息的来源是否合法;
    • 验证是否含有消息体;
    • 验证消息体中是否含有 postmate 属性;
    • 验证消息的类型是否为 "application/x-postmate-v1+json"
    • 验证消息体中的 postmate 对应的消息类型是否合法;

    以下是 Postmate 支持的消息类型:

    const messageTypes = {
      handshake: 1, 
      "handshake-reply": 1, 
      call: 1,
      emit: 1, 
      reply: 1, 
      request: 1,
    };

    其实要实现消息验证的提前,我们还需要定义标准的消息体模型:

    {
       postmate: "emit", // 必填:"request" | "call" 等等
       type: messageType, // 必填:"application/x-postmate-v1+json"
       // 自定义属性
    }

    了解完子页面如何与父页面进行通信及如何进行消息验证之后,下面我们来看一下父页面如何与子页面进行消息通信。

    3.3 父页面 -> 子页面

    3.3.1 调用子页面模型对象上的方法

    在页面中,通过 ParentAPI 对象提供的 call 方法,我们就可以调用子页面模型对象上的方法:

    export class ParentAPI {
        call(property, data) {
        this.child.postMessage(
          {
            postmate: "call",
            type: messageType,
            property,
            data,
          },
          this.childOrigin
        );
      }
    }

    ChildAPI 对象中,会对 call 消息类型进行对应的处理,相应的处理逻辑如下所示:

    export class ChildAPI {
      constructor(info) {
            // 省略部分代码
        this.child.addEventListener("message", (e) => {
          if (!sanitize(e, this.parentOrigin)) return;
          const { property, uid, data } = e.data;
          
          // 响应父页面发送的call消息类型,用于调用Model对象上的对应方法
          if (e.data.postmate === "call") {
            if (
              property in this.model &&
              typeof this.model[property] === "function"
            ) {
              this.model[property](data);
            }
            return;
          }
        });
      }
    }

    通过以上代码我们可知,call 消息只能用来调用子页面 Model 对象上的方法并不能获取方法调用的返回值。然而在一些场景下,我们是需要获取方法调用的返回值,接下来我们来看一下 ParentAPI 是如何实现这个功能。

    3.3.2 调用子页面模型对象上的方法并获取返回值

    若需要获取调用后的返回值,我们需要调用 ParentAPI 对象上提供的 get 方法:

    export class ParentAPI {
        get(property) {
        return new Postmate.Promise((resolve) => {
          // 从响应中获取数据并移除监听
          const uid = generateNewMessageId();
          const transact = (e) => {
            if (e.data.uid === uid && e.data.postmate === "reply") {
              this.parent.removeEventListener("message", transact, false);
              resolve(e.data.value);
            }
          };
          
          // 监听来自子页面的响应消息
          this.parent.addEventListener("message", transact, false);
    
          // 向子页面发送请求
          this.child.postMessage(
            {
              postmate: "request",
              type: messageType,
              property,
              uid,
            },
            this.childOrigin
          );
        });
      }
    }

    对于父页面发送的 request 消息,在子页面中会通过 resolveValue 方法来获取返回结果,然后通过 postMessage 来返回结果:

    // src/postmate.js
    export class ChildAPI {
      constructor(info) {
        this.child.addEventListener("message", (e) => {
          if (!sanitize(e, this.parentOrigin)) return;
          const { property, uid, data } = e.data;
          
          // 响应父页面发送的request消息
          resolveValue(this.model, property).then((value) =>
            e.source.postMessage(
              {
                property,
                postmate: "reply",
                type: messageType,
                uid,
                value,
              },
              e.origin
            )
          );
        });
      }
    }

    以上代码中的 resolveValue 方法实现也很简单:

    const resolveValue = (model, property) => {
      const unwrappedContext =
        typeof model[property] === "function" ? model[property]() : model[property];
      return Postmate.Promise.resolve(unwrappedContext);
    };

    3.4 模型扩展机制

    Postmate 提供了非常灵活的模型扩展机制,让开发者可以根据需求,扩展子页面的 Model 对象:

    对应的扩展机制实现起来并不复杂,具体的实现如下所示:

    Postmate.Model = class Model {
      constructor(model) {
        // 省略部分代码
        return this.sendHandshakeReply();
      }
    
      sendHandshakeReply() {
        return new Postmate.Promise((resolve, reject) => {
          const shake = (e) => {
            // 省略部分代码
            if (e.data.postmate === "handshake") {
              // 使用父页面提供的模型对象来扩展子页面已有的模型对象
              const defaults = e.data.model;
              if (defaults) {
                Object.keys(defaults).forEach((key) => {
                  this.model[key] = defaults[key];
                });
              }
              return resolve(new ChildAPI(this));
            }
          };
        });
      }
    };

    此时,我们已经介绍了 Postmate 如何进行握手及如何实现双向消息通信,最后我们来介绍一下如何断开连接。

    四、如何断开连接

    当父页面与子页面完成消息通信之后,我们需要断开连接。这时我们可以调用 ParentAPI 对象上的 destroy 方法来断开连接。

    // src/postmate.js
    export class ParentAPI {
        destroy() {
        window.removeEventListener("message", this.listener, false);
        this.frame.parentNode.removeChild(this.frame);
      }
    }
    关注「全栈修仙之路」阅读阿宝哥原创的 3 本免费电子书(累计下载近2万)及 7 篇源码分析系列教程。

    本文阿宝哥以 Postmate 这个库为例,介绍了如何基于 postMessage 来实现父页面和 iframe 子页面之间优雅的消息通信。如果你还意犹未尽的话,可以阅读阿宝哥之前写的与通信相关的文章:如何优雅的实现消息通信?你不知道的 WebSocket

    五、参考资源

    查看原文

    赞 16 收藏 11 评论 0

    阿宝哥 赞了文章 · 11月17日

    BATJTMD,大厂招聘,都招什么样Java程序员?

    作者:小傅哥
    博客:https://bugstack.cn
    Github:https://github.com/fuzhengwei/CodeGuide/wiki

    沉淀、分享、成长,让自己和他人都能有所收获!??

    一、前言

    Java学到什么程度可以找工作?

    最近总看到类似这样的问题,也有一些工作3年左右的小伙伴问小傅哥,该怎么进大厂。其实你说 Java 学多少可以找到工作,主要看你想在哪个城市找、找什么样的公司、找什么样的待遇。因你的要求高低不同,你遇到的面试要求也会随之改变。

    因此,为了让大家更清楚的看到学多少Java能找工作,我抽取了北京头部互联网公司的大量职位招聘要求,分析出一份可以让你明卷面试的考点。看过都说??学习有方向、面试也不慌!

    二、互联网公司都分布在哪里

    知己知彼,百战不殆,先看看有哪些互联网公司,都分布在北京的哪些地方,也能方便你,面试不迷路、跳槽不辛苦。筛选了部分公司,还有很多不一一列举了!

    1. 常见大厂

    图 11-1 常见互联网大厂,筛选20家

    • 没有排名,随机筛选,随机排序
    • 公司包括:华为、联想、新浪、百度、小米、58同城、搜狗、爱奇艺、腾讯、去哪网、美团、饿了吗、汽车之家、字节跳动、当当网、CSDN、亚马逊、京东、360、滴滴

    2. 地理位置

    图 11-2 互联网大厂地理位置分布

    • 从图上可以看到大部分互联网公司都分布在北边,??让人怪不好意思的,集中在一块挺好,下楼吃个饭就跳槽了。
    • 就我自己而言更喜欢靠边一点的公司,因为租房便宜、不用挤地铁、不用把时间浪费在路上、不用听马路的嘈杂。

    三、什么样的技术能进入大厂

    1. 你的简历

    可能很大一部分1~3年找工作的小伙伴,只是按照模板填写好简历就完事了,很少考虑公司都需要什么、自己的职位是否匹配。

    但你可能忽略了,你的这份简历才更多的决定了你会遇到一个什么样的公司、什么样的面试官、什么样的考题。最终决定你与这家公司的匹配的程度。

    在与很多小伙伴沟通中发现,其实很大一部分程序员都不会写简历的,或者说写不好简历。好像是有话说不出来,或者是不知道该把这些话说在哪。一份简历主要得体现出你个人的信息、技术栈广度和深度、项目经验以及最后一块拓展内容。

    而这份简历想达到最终的效果,也就是拿Offer。那么一定要给面试官挖坑,当然这个坑不是真坑。而是你要在简历中突出自己的优势项、技术亮点、优秀经历,也同时在这些点中留出技术话题,让面试官可以和你有的

    但如果说你胡乱写简历,说自己懂HashMap。那面试官来劲了,问你:Hash为什么用31计算扰动函数的作用是什么,以及它可以被应用在哪些地方负载因子嘎哈的HashMap是开放寻址还是拉链寻址链表什么时候树化以及迁移数据算法是什么2-3树和红黑树有什么关系等等,你不晕才怪,也不能给面试官留下好印象。如果你还不会这些技术,赶快看小傅哥的面经手册 ? 拿大厂Offer 吧!

    2. 大厂考题

    以下这部分考题分析数据是通过抽样的方式,从Boss直聘中选取六个互联公司,每个公司找3~5个,工作1~3年岗位应聘要求,从中分析各面试考点综合汇总。

    样例数据

    2.1 阿里、百度、腾讯

    图 11-3 阿里、百度、腾讯,1~3年招聘要求梳理

    每个公司的每个职位要求会略有不同,所以不能一概而论,某一行没有写某项技术点也不能代表什么。以上更多的是参考以及自己在面试求职时可以按照这个方式进行梳理。

    • 阿里,在技术上会更加希望你有深度和广度,也善于把技术能应用到项目中,并有一定的学习能力。同时在工作中,要有责任心、沟通能力和解决问题的落地的能力。
    • 百度,同样希望可以精通一些框架的深层次内容,有一定的技术经验,更偏向于落地技能。同时也希望你是爱学习的面试者,最好有Github相关内容。工作中积极、主动、抗压,认真,善于沟通。
    • 腾讯,除了基础语言学习外,要有一些扩展,同时要深入理解语言特性。这可能和腾讯本身是用C、C++有关,要知其然,知其所以然。同时希望在数据结构和算法上有一定的了解和认知,也可以在工作中有责任心、抗压能力以及问题分析和解决能力。

    2.2 字节、美团、京东

    图 11-4 字节、美团、京东,1~3年招聘要求梳理

    分析完上面三家公司,再看看这三家互联网对应聘者的要求。注意数据依旧是抽样,所以面试者在投递简历时,一定要自己拆解分析

    • 字节,更注重Java基础、算法、数据结构,同时对于常用的技术要有一定的了解深度。对代码方面要有良好的设计和代码品味追求,同时希望你关注业界最新技术,有好奇心和进取心。工作中,有产品意识。对于研发来说,产品意识很重要
    • 美团,美团的技术科目属于比较面的比较广和深的,尤其是字节码编程技术,在其他一些招聘中是没有看到的。除此之外更希望你有一定的学习能力,参与过Github开源项目,有技术钻研精神。在工作中,有较强的思维逻辑,难点攻克,复杂问题推进落地,责任感等。这可能也和美团的技术氛围有关,他们的技术博客做的也不错。百度搜美团技术,https://tech.meituan.com/
    • 京东,各家公司都非常注重Java基本功,这些面试题可能不难但也很难。另外在常用框架和相应的技术深度上要有一定了解,尤其是各个框架的原理和实现机制,如果你能自己动手写一个,那么会更好。另外在技术经验上,可以有分布式、高并发等经验,也可以非常用的运用设计模式,编写出不错的代码。同时希望你有一定的学习能力,博客、开源代码、Github、Gitee等。工作中,有过程控制意识、风险意识以及良好的沟通和解决问题的能力。

    3. 考点总结

    图 11-5 面试官考点总结

    综上,各家公司的招聘要求,梳理出七个方向的考点,包括:基本功底、常用技术、技术深度、技术经验、学习能力、工作能力、项目经验。

    • 基本功底,是一个程序员的主科目语言的学习程度的一个基本考察,这部分内容需要平时大量积累和总结。否则一本简单的Java书很难全部给你讲透彻,因为Java中包括了太多的内容,远不止API使用。
    • 常用技术,这个聊的是你的技术广度,和岗位技术匹配度。比如需要用到过RPC,那你用过Dubbo。如果你的公司暂时用的技术不多,或者还是处于单体服务,那么需要自己补充。
    • 技术深入,除了技术广度接下来就是技术深入,在你常用的技术栈中,你有多了解他们,了解源码吗、了解运行机制吗、了解设计原理吗。这部分内容常被人说是造火箭,但这部分内容非常重要,可以承上启下的贯穿个人修为和薪资待遇。
    • 技术经验,什么是技术经验呢?这是落地能力,除了你可能认为上面一些是纸上谈兵,是造火箭。那么接下来这部分内容就是你是否真造过一个火箭,真完成过一个难题。所以这部分是从结果证明,不是你会什么,而是你做过什么。
    • 学习能力,作为程序员你是否保持热情,是否依旧在积极努力的关注技术,是否为自己的成长不断添砖加瓦、是否还有好奇心和较强的求知欲。一般会从这里看你是不是一个真正的Coder!
    • 工作能力,以上的种种能力,最终要体现到工作上,要能看出你的交付能力。否则即使你再优秀,也不能把你当成一个吉祥物。工作能力的体现,才是真的为团队、为部门、为公司,贡献价值的。
    • 项目经验,这项内容会根据不同公司的不同业务线而不同,就像你懂交易、支付,那么面试花呗、借呗、白条等工作岗位就会很吃香。

    四、突破成长瓶颈的技术书籍

    根据以上大厂岗位要求,总结了可以破敌的技术书籍!

    技术成长的每一个阶段都会遇到一个与之匹配的、难以跨越的,技术瓶颈期!这个阶段没有一次能解决的神药,只有自己不断的积累、沉淀、破局,到最后的爆发。而这些知识可能最开始都是枯燥的,就像看了大A不会小a,看了小a又牵扯出小b,没办法只能一层层的扒,一层层的学。

    书籍下载:扫码关注公众号:bugstack虫洞栈,回复:电子书

    1. 推荐

    - 小傅哥的《重学 Java 设计模式》 ????

    本书是作者小傅哥,投入50天时间,从互联网实际业务开发中抽离出,交易、营销、秒杀、中间件、源码等22个真实业务场景,编写了18万字271页的实战型Java编程资料。如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。

    - 小傅哥的《字节码编程》 ???

    让人怪不好意思的,说是出书有点膨胀,毕竟这不是走出版社的流程,选题、组稿、编著、审读、加工到出版发行。但全书共计107页,11万7千字,20个章节涵盖三个字节码框架(ASM、Javassist、Byte-budy)和JavaAgent使用并附带整套案例源码!

    - 《JAVA核心知识点整理》 ????

    一份整理的蛮不错的Java核心知识点。覆盖了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

    - 计算机是怎样跑起来的(日)矢泽久雄 ????? - 点击购买(支持作者)

    矢泽久雄,曾在Software House做过程序员,电脑作家之友会会长。工作之余笔耕不辍,从电路到编程语言均有涉及。代表作有《程序是怎样跑起来的》等。本书以图配文,以计算机的三大原则为开端、相继介绍了计算机的结构、手工汇编、程序流程、算法、数据结构、面向对象编程、数据库、TCP/IP 网络、数据加密、XML、计算机系统开发以及SE 的相关知识。

    2. Java

    1. 《Java虚拟机规范(Java SE 7)》 ???? - 点击购买(支持作者)

    如果不太熟悉jvm,这个读起来非常乏味(只有规范,没有多余解释),但如果阅读过相关深入java虚拟机,再读这个,会非常有亲切感。

    2. 《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》???? - 点击购买(支持作者)

    这是一部从工作原理和工程实践两个维度深入剖析JVM的著作,是计算机领域公认的经典,繁体版在台湾也颇受欢迎。作者周志明,是资深Java技术、机器学习和企业级开发技术专家,现任远光软件研究院院长,人工智能博士在读。

    3. 《JAVA核心知识点整理》????

    一份整理的蛮不错的Java核心知识点。覆盖了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

    4. 《On Java 8 编程思想》????

    本书原作者为 [美] Bruce Eckel,即《Java 编程思想》的作者。译者在翻译中同时参考了谷歌、百度、有道翻译的译文以及《Java编程思想》第四版中文版的部分内容(对其翻译死板,生造名词,语言精炼度差问题进行规避和改正)。最后结合译者自己的理解进行本地化,尽量做到专业和言简意赅,方便大家更好的理解学习。

    5. 深入浅出+Java+多线程 ????

    笔者在读完市?上关于Java并发编程的资料后,感觉有些知识点不是很清晰,于是
    在RedSpider社区内展开了对Java并发编程原理的讨论。鉴于开源精神,我们决定
    将我们讨论之后的Java并发编程原理整理成书籍,分享给?家。
    如果您或者您的单位愿意赞助本书或本社区,请发送邮件到RedSpider社区邮件组redspider@qun.mail.163.com或加微信redspider-worker进?洽谈。

    6. 《Java核心技术 I》 ???? - 点击购买(支持作者)

    不同于一般的 Java入门书,此书对api的讲解非常详细,细节部门颇多。举个例子,java对象序列化时会写入什么内容,本书都有详细的介绍

    7. 《effective java 3》 ???? - 点击购买(支持作者)

    全书以一种比较松散的方式将这些条目组织成11章,每一章都涉及软件设计的一个主要方面。因此,本书并不一定需要按部就班地从头读到尾,因为每个条目都有一定程度的独立性。这些条目相互之间经常交叉引用,因此可以很容易地在书中找到自己需要的内容。

    8. 《Java解惑》 ???

    挺有意思的一本书,它列举了许多平常不太注意的细节问题,可能大部分时候我们都不会碰到此类问题,但如果读过,一旦碰到,就会留意此类问题。

    9. 《Thinking In Java 4》????? - 点击购买(支持作者)

    值得用2年深入抚摸的书籍,对于学习java技术除了最开始的入门书籍后,这是一本非常值得阅读的书籍。

    10. 《Java内存模型》 ????

    Java线程之间的通信由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

    11. 《Java并发编程实践(全)》 ???? - 点击购买(支持作者)

    随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序中取得了显著的进步,提高了Java虚拟机的性能以及并发类的可伸缩性,并加入了丰富的新并发构建块。在《JAVA并发编程实践》中,这些便利工具的创造者不仅解释了它们究竟如何工作、如何使用,还阐释了创造它们的原因,及其背后的设计模式。

    12. 《Spring揭秘》 ????? - 点击购买(支持作者)

    本书内容全面,论述深刻入理,必将成为每个Java专业开发人员必备的Spring图书。

    难得的国产良心技术书籍,既没有大量堆砌Spring源码,也没有原封不动地翻译官方文档。作者以自己的深厚功力和独特视角一步一步地把Spring框架抽丝剥茧地展现在读者面前,从谋篇布局和字里行间都能看出作者的用心之处。如果你想深入了解Spring的方方面面,这本书非读不可,而且读一遍都不够,要每隔一段时间反复阅读,尤其第二部分IOC和第三部分AOP,它们是Spring的基础,也是这本书的精华所在。这里不妨给个建议,以这本书为蓝本把Spring框架用到的设计模式和原则认真梳理一遍,结合源码认真理解为何要这样设计。如若这样,假以时日,功力必有小成啊。最后感谢作者,感谢我能遇到这本书

    13. 《Spring源码深度解析》 ??? - 点击购买(支持作者)

    由浅入深、由易到难地对Spring源码展开了系统的讲解,包括Spring的设计理念和整体架构、容器的基本实现、默认标签的解析、自定义标签的解析、bean的加载、容器的功能扩展、AOP、数据库连接JDBC、整合MyBatis、事务、SpringMVC、远程服务、Spring消息服务等内容。

    14. 《深入理解SpringCloud与微服务构建》???? - 点击购买(支持作者)

    本书以微服务的基本概念介绍性开篇,逐步引出Java平台下打造微服务的利器SpringBoot微框架。书中从SpringBoot微框架的“出身”开始,循序渐进,为大家剖析SpringBoot微框架的设计理念和原理,并对框架的重点功能和模块进行了逐一讲解。

    15. 《美团技术后台篇》 ????

    2019年美团点评高级技术汇总,深入且清晰设计到Java核心技术。包括;字节码编程、全链路监控、美团分布式 ID 生成服务开源等。

    16. Java Concurrency in Practice ?????

    This book covers:
    Basic concepts of concurrency and thread safety
    Techniques for building and composing thread-safe classes
    Using the concurrency building blocks in java.util.concurrent
    Performance optimization dos and don'ts
    Testing concurrent programs
    Advanced topics such as atomic variables, nonblocking algorithms, and the Java Memory Model

    3. DB

    1. 《MySQL王者晋级之路》 ???? - 点击购买(支持作者)

    十年磨一剑,汇集作者多年MySQL数据库领域的一线实战与教学经验,由浅入深剖析MySQL的体系结构、备份恢复、复制、高可用集群架构、优化、故障排查、新版本特性、监控、升级及技术面试宝典等知识点。

    2. 《数据库索引设计与优化》???? - 点击购买(支持作者)

    作者通过系统的讲解及大量的案例清晰地阐释了关系型数据库的访问路径选择原理,以及表和索引的扫描方式,详尽地讲解了如何快速地估算SQL 运行的CPU 时间及执行时间,帮助读者从原理上理解SQL、表及索引结构、访问方式等对关系型数据库造成的影响,并能够运用量化的方法进行判断和优化,指导关系型数据库的索引设计。此书适用于已经具备了SQL 这一关系型语言相关知识,希望通过理解SQL 性能相关的内容,或者希望通过了解如何有效地设计表和索引而从中获益的人员。

    3. MYSQL技术内幕:INNODB存储引擎 - ???? - 点击购买(支持作者)

    这本书更深入地介绍InnoDB存储引擎的内核,例如latch、B+树索引、事务、锁等,从源代码的角度深度解析了InnoDB的体系结构、实现原理、工作机制,并给出了大量最佳实践,帮助用户真正了解一个数据库存储引擎的开发。

    4. Redis

    1. 《Redis设计与实现》???? - 点击购买(支持作者)

    黄健宏 软件开发者,他喜欢函数式编程,热爱开源软件。出于对数据库的强烈兴趣,他开始阅读和分析 Redis 源代码,并对 Redis 2.6 和 Redis 3.0 的源代码进行了详细注释。

    2. 《Redis 深度历险:核心原理与应用实践》???? - 点击购买(支持作者)

    作者:钱文品,老钱。可以说这是一本深挖到redis骨头的技术书籍,整个内容涵盖;基础和应用篇、原理篇、集群篇、拓展篇、源码篇共 5 大块内容,值得学习。

    3. 《Redis开发与运维》 ????- 点击购买(支持作者)

    付磊,张益军 | 搜狐视频高级研发工程师,都拥有多年Redis开发运维经验,为公司多个核心业务提供Redis服务。两人共同编写了本书,在本书中全面讲解Redis基本功能及其应用,并结合线上开发与运维监控中的实际使用案例,深入分析并总结了实际开发运维中遇到的“陷阱”,以及背后的原因, 包含大规模集群开发与管理的场景、应用案例与开发技巧,为高效开发运维提供了大量实际经验和建议。

    5. 架构&设计

    1. 《代码整洁之道》 ???? - 点击购买(支持作者)

    本书提出一种观念:代码质量与其整洁度成正比。干净的代码,既在质量上较为可靠,也为后期维护、升级奠定了良好基础。作为编程领域的佼佼者,本书作者给出了一系列行之有效的整洁代码操作实践。

    2. 《Head_First设计模式(中文版)》???? - 点击购买(支持作者)

    Head First陆续的介绍了策略模式、观察者模式、装饰者模式、工厂方法模式、抽象工厂模式、单件模式、命令模式、适配器模式、外观模式、模板方法模式、迭代器模式、组合模式、状态模式、代理模式,在介绍各种模式的期间,用简单的应用场景、通俗的语言引导读者去思考这些模式是如何利用和遵循相应OO原则的,然后再清晰的总结出每种模式的定义。

    3. 《编写可读代码的艺术》 ??? - 点击购买(支持作者)

    细节决定成败,思路清晰、言简意赅的代码让程序员一目了然;而格式凌乱、拖沓冗长的代码让程序员一头雾水。除了可以正确运行以外,优秀的代码必须具备良好的可读性,编写的代码要使其他人能在最短的时间内理解才行。本书旨在强调代码对人的友好性和可读性。

    4. 《重构 改善既有代码的设计》 ???? - 点击购买(支持作者)

    “不要容忍破窗户” 如果两个或更多的地方实现同一职责,则改变时会带来麻烦。所以要遵循DRY原则,单一职责。这本书的每一项都介绍一种经过实证的代码变换手法(code transformation)的动机和技术。向你讲述如何有效的重构以及是否进行重构。

    5. 《设计数据密集型应用 Designing Data Intensive Applications》 ???? - 点击购买(支持作者)

    作者是英国剑桥大学的一名分布式系统研究员,在此之前他曾是软件工程师和企业家,在 Linkedin 和 Rapportive 工作过,从事大规模数据基础设施相关的工作。书中包含:深入分析你已经在使用的系统,并学习如何更高效地使用和运维这些系统、理解分布式系统研究,这些研究是现代数据库构建的基石、了解一致性、可伸缩性、容错性和复杂度之间的权衡。推荐人:BK

    6. 《企业应用架构模式》 ???? - 点击购买(支持作者)

    作者是当今面向对象软件开发的权威,他在一组专家级合作者的帮助下,将40多种经常出现的解决方案转化成模式,最终写成这本能够应用于任何一种企业应用平台的、关于解决方案的、不可或缺的手册。

    6. 其他系列

    1. 《阿里工程师的自我修养》 ????

    从入门到进阶,从普通员工到主管,从知识到落地,从量的积累到质的飞跃,在不确定性的世界中,你遇到的种种难题,阿里工程师正在探索着最优解。3大思维、10个技巧、10年感悟……每经过一次大的战役,阿里工程师都会复盘、沉淀,这些经验值得细品。

    2. Java开发手册(嵩山版) ????

    《阿里巴巴 Java 开发手册》主要提炼了阿里巴巴集团技术团队的集体编程经验和软件设计智慧,可全面、立体地帮助开发者的成长和团队代码文化形成。嵩山版首次新增前后端规约等内容,可帮助开发者码出规范,码出质量。

    3. 《Http权威指南》???? - 点击购买(支持作者)

    如果以前没有深入了解http,读了此书,会觉得以前了解的关于http的内容都弱爆了。经典书籍,中文版2012年发版,翻译的还不错,值得阅读。

    4. Elasticsearch 权威指南 中文版 ???? - 点击购买(支持作者)

    这本指南都会帮助你了解其中最基本的概念,从最基本的操作开始学习 Elasticsearch。同时将向你介绍讲解结构化搜索、统计、查询过滤、地理定位、自动完成以及你是不是要查找的提示。并且探讨如何给数据建模能提升 Elasticsearch 的性能,以及在生产环境中如何配置、监视你的集群。

    5. 计算机是怎样跑起来的(日)矢泽久雄 ????? - 点击购买(支持作者)

    矢泽久雄,曾在Software House做过程序员,电脑作家之友会会长。工作之余笔耕不辍,从电路到编程语言均有涉及。代表作有《程序是怎样跑起来的》等。本书以图配文,以计算机的三大原则为开端、相继介绍了计算机的结构、手工汇编、程序流程、算法、数据结构、面向对象编程、数据库、TCP/IP 网络、数据加密、XML、计算机系统开发以及SE 的相关知识。

    6. 编码:隐匿在计算机软硬件背后的语言 ????? - 点击购买(支持作者)

    是一本讲述计算机工作原理的书。不过,你千万不要因为“工作原理”之类的字眼就武断地认为它是晦涩而难懂的。作者用丰富的想象和清晰的笔墨将看似烦杂的理论阐述得通俗易懂,你丝毫不会感到枯燥和生硬。更重要的是,你会因此更加深刻地理解计算机的工作原理。这种理解不是抽象层面上的,而是具有一定深度的,这种深度甚至不逊于“电气工程师”和“程序员”的理解。

    7. 计算机程序的构造和解释(原书第2版) ?????

    《计算机程序的构造和解释(原书第2版)》1984年出版,成型于美国麻省理工学院(MIT)多年使用的一本教材,1996年修订为第2版。在过去的二十多年里,《计算机程序的构造和解释(原书第2版)》对于计算机科学的教育计划产生了深刻的影响。第2版中大部分重要程序设计系统都重新修改并做过测试,包括各种解释器和编译器。作者根据其后十余年的教学实践,还对其他许多细节做了相应的修改。

    8. 代码大全(第2版) ?????

    第2版的《代码大全》是著名IT畅销书作者史蒂夫·迈克康奈尔11年前的经典著作的全新演绎:第2版不是第一版的简单修订增补,而是完全进行了重写;增加了很多与时俱进的内容。这也是一本完整的软件构建手册,涵盖了软件构建过程中的所有细节。它从软件质量和编程思想等方面论述了软件构建的各个问题,并详细论述了紧跟潮流的新技术、高屋建瓴的观点、通用的概念,还含有丰富而典型的程序示例。

    9. 编程匠艺 ????

    如果你可以编写出合格的代码,但是想更进一步、创作出组织良好而且易于理解的代码,并希望成为一名真正的编程专家或提高现有的职业技能,那么Pete Goodliffe编写的这本本书都会为你给出答案。本书的内容涵盖编程的各个要素,如代码风格、变量命名、错误处理和安全性等。此外,本书还对一些更广泛的编程问题进行了探讨,如有效的团队合作、开发过程和文档编写,等等。

    10. Zookeeper 分布式过程 ???? - 点击购买(支持作者)

    作者介绍Flavio Junqueira 是微软研究院在英国剑桥大学的研究人员之一。他拥有美国加州大学圣地亚哥分校计算机科学博士学位。他的研究范围涉及分布式系统的各个方面,包括分布式算法、并发性和可扩展性。他是Apache项目如Apache ZooKeeper(PMC主席和提交者)和Apache BookKeeper(提交者)的积极贡献者。他一有空就喜欢睡觉。

    11. 黑客与画家 ???? - 点击购买(支持作者)

    本书是硅谷创业之父Paul Graham 的文集,主要介绍黑客即优秀程序员的爱好和动机,讨论黑客成长、黑客对世界的贡献以及编程语言和黑客工作方法等所有对计算机时代感兴趣的人的一些话题。书中的内容不但有助于了解计算机编程的本质、互联网行业的规则,还会帮助读者了解我们这个时代,迫使读者独立思考。

    五、总结

    • 面试也是一场有准备的战斗,知己知彼才能游刃有余。面试怎么面主要是看简历怎么写,最终是你来主导面试,还是被主导,更多也是依赖于你的技术身家。
    • 任何时候都需要主动学习、有技术眼光和魄力,既能吹得了造火箭的牛、也能落地出实际的产物、技能帮公司实现价值,也能让自己有一定的收入。才是你应该永久追求的目标,和突破瓶颈的价值。
    • 少一些躁动、少一些不安,多一些沉稳、多一些沉淀,只要你愿意积累就一定会突破瓶颈,都是这条路上的打工人,不要总让自己的大脑被别人牵着走,也不要活在别人嘴里。奥利给??阅读原文,进入知识宝藏!

    六、系列推荐

    查看原文

    赞 9 收藏 5 评论 0

    阿宝哥 发布了文章 · 11月16日

    这些高阶的函数技术,你掌握了么

    在 JavaScript 中,函数为一等公民(First Class),所谓的 “一等公民”,指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或作为其它函数的返回值

    接下来阿宝哥将介绍与函数相关的一些技术,阅读完本文,你将了解高阶函数、函数组合、柯里化、偏函数、惰性函数和缓存函数的相关知识。

    一、高阶函数

    在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

    • 接受一个或多个函数作为输入;
    • 输出一个函数。

    接收一个或多个函数作为输入,即函数作为参数传递。这种应用场景,相信很多人都不会陌生。比如常用的 Array.prototype.map()Array.prototype.filter() 高阶函数:

    // Array.prototype.map 高阶函数
    const array = [1, 2, 3, 4];
    const map = array.map(x => x * 2); // [2, 4, 6, 8]
    
    // Array.prototype.filter 高阶函数
    const words = ['semlinker', 'kakuqo', 'lolo', 'abao'];
    const result = words.filter(word => word.length > 5); // ["semlinker", "kakuqo"]

    而输出一个函数,即调用高阶函数之后,会返回一个新的函数。我们日常工作中,常见的 debouncethrottle 函数就满足这个条件,因此它们也可以被称为高阶函数。

    关注「全栈修仙之路」阅读阿宝哥原创的 3 本免费电子书及 50 几篇 “重学TS” 教程。

    二、函数组合

    函数组合就是将两个或两个以上的函数组合生成一个新函数的过程:

    const compose = function (f, g) {
      return function (x) {
        return f(g(x));
      };
    };

    在以上代码中,fg 都是函数,而 x 是组合生成新函数的参数。

    2.1 函数组合的作用

    在项目开发过程中,为了实现函数的复用,我们通常会尽量保证函数的职责单一,比如我们定义了以下功能函数:

    在拥有以上功能函数的基础上,我们就可以自由地对函数进行组合,来实现特定的功能:

    function lowerCase(input) {
      return input && typeof input === "string" ? input.toLowerCase() : input;
    }
    
    function upperCase(input) {
      return input && typeof input === "string" ? input.toUpperCase() : input;
    }
    
    function trim(input) {
      return typeof input === "string" ? input.trim() : input;
    }
    
    function split(input, delimiter = ",") {
      return typeof input === "string" ? input.split(delimiter) : input;
    }
    
    const trimLowerCaseAndSplit = compose(trim, lowerCase, split);
    trimLowerCaseAndSplit(" a,B,C "); // ["a", "b", "c"]

    在以上的代码中,我们通过 compose 函数实现了一个 trimLowerCaseAndSplit 函数,该函数会对输入的字符串,先执行去空格处理,然后在把字符串中包含的字母统一转换为小写,最后在使用 , 分号对字符串进行拆分。利用函数组合的技术,我们就可以很方便的实现一个 trimUpperCaseAndSplit 函数。

    2.2 组合函数的实现

    function compose(...funcs) {
      return function (x) {
        return funcs.reduce(function (arg, fn) {
          return fn(arg);
        }, x);
      };
    }

    在以上的代码中,我们通过 Array.prototype.reduce 方法来实现组合函数的调度,对应的执行顺序是从左到右。这个执行顺序与 Linux 管道或过滤器的执行顺序是一致的。

    不过如果你想从右往左开始执行的话,这时你就可以使用 Array.prototype.reduceRight 方法来实现。

    其实每当看到 compose 函数,阿宝哥就情不自禁想到 “如何更好地理解中间件和洋葱模型” 这篇文章中介绍的 compose 函数:

    function compose(middleware) {
      // 省略部分代码
      return function (context, next) {
        let index = -1;
        return dispatch(0);
        function dispatch(i) {
          if (i <= index)
            return Promise.reject(new Error("next() called multiple times"));
          index = i;
          let fn = middleware[i];
          if (i === middleware.length) fn = next;
          if (!fn) return Promise.resolve();
          try {
            return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
          } catch (err) {
            return Promise.reject(err);
          }
        }
      };
    }

    利用上述的 compose 函数,我们就可以实现以下通用的任务处理流程:

    三、柯里化

    柯里化(Currying)是一种处理函数中含有多个参数的方法,并在只允许单一参数的框架中使用这些函数。这种转变是现在被称为 “柯里化” 的过程,在这个过程中我们能把一个带有多个参数的函数转换成一系列的嵌套函数。它返回一个新函数,这个新函数期望传入下一个参数。当接收足够的参数后,会自动执行原函数。

    在理论计算机科学中,柯里化提供了简单的理论模型,比如:在只接受一个单一参数的 lambda 演算中,研究带有多个参数的函数的方式。与柯里化相反的是 Uncurrying,一种使用匿名单参数函数来实现多参数函数的方法。比如:

    const func = function(a) {
      return function(b) {
        return a * a + b * b;
      }
    }
    
    func(3)(4); // 25

    Uncurrying 不是本文的重点,接下来我们使用 Lodash 提供的 curry 函数来直观感受一下,对函数进行 “柯里化” 处理之后产生的变化:

    const abc = function(a, b, c) {
      return [a, b, c];
    };
     
    const curried = _.curry(abc);
     
    curried(1)(2)(3); // => [1, 2, 3]
    curried(1, 2)(3); // => [1, 2, 3]
    curried(1, 2, 3); // => [1, 2, 3]
    _.curry(func, [arity=func.length])

    创建一个函数,该函数接收 func 的参数,要么调用func返回的结果,如果 func 所需参数已经提供,则直接返回 func 所执行的结果。或返回一个函数,接受余下的func 参数的函数,可以使用 func.length 设置需要累积的参数个数。

    来源:https://www.lodashjs.com/docs...

    这里需要特别注意的是,在数学和理论计算机科学中的柯里化函数,一次只能传递一个参数。而对于 JavaScript 语言来说,在实际应用中的柯里化函数,可以传递一个或多个参数。好的,介绍完柯里化的相关知识,接下来我们来介绍柯里化的作用。

    3.1 柯里化的作用

    3.1.1 参数复用
    function buildUri(scheme, domain, path) {
      return `${scheme}://${domain}/${path}`;
    }
    
    const profilePath = buildUri("https", "github.com", "semlinker/semlinker");
    const awesomeTsPath = buildUri("https", "github.com", "semlinker/awesome-typescript");

    在以上代码中,首先我们定义了一个 buildUri 函数,该函数可用于构建 uri 地址。接着我们使用 buildUri 函数构建了阿宝哥 Github 个人主页awesome-typescript 项目的地址。对于上述的 uri 地址,我们发现 httpsgithub.com 这两个参数值是一样的。

    假如我们需要继续构建阿宝哥其他项目的地址,我们就需要重复设置相同的参数值。那么有没有办法简化这个流程呢?答案是有的,就是对 buildUri 函数执行柯里化处理,具体处理方式如下:

    const _ = require("lodash");
    
    const buildUriCurry = _.curry(buildUri);
    const myGithubPath = buildUriCurry("https", "github.com");
    const profilePath = myGithubPath("semlinker/semlinker");
    const awesomeTsPath = myGithubPath("semlinker/awesome-typescript");
    3.1.2 延迟计算/运行
    const add = function (a, b) {
      return a + b;
    };
    
    const curried = _.curry(add);
    const plusOne = curried(1);

    在以上代码中,通过对 add 函数执行 “柯里化” 处理,我们可以实现延迟计算。好的,简单介绍完柯里化的作用,我们来动手实现一个柯里化函数。

    3.2 柯里化的实现

    现在我们已经知道了,当柯里化后的函数接收到足够的参数后,就会开始执行原函数。而如果接收到的参数不足的话,就会返回一个新的函数,用来接收余下的参数。基于上述的特点,我们就可以自己实现一个 curry 函数:

    function curry(func) {
      return function curried(...args) {
        if (args.length >= func.length) { // 通过函数的length属性,来获取函数的形参个数
          return func.apply(this, args);
        } else {
          return function (...args2) {
            return curried.apply(this, args.concat(args2));
          };
        }
      }
    }

    四、偏函数应用

    在计算机科学中,偏函数应用(Partial Application)是指固定一个函数的某些参数,然后产生另一个更小元的函数。而所谓的元是指函数参数的个数,比如含有一个参数的函数被称为一元函数。

    偏函数应用(Partial Application)很容易与函数柯里化混淆,它们之间的区别是:

    • 偏函数应用是固定一个函数的一个或多个参数,并返回一个可以接收剩余参数的函数;
    • 柯里化是将函数转化为多个嵌套的一元函数,也就是每个函数只接收一个参数。

    了解完偏函数与柯里化的区别之后,我们来使用 Lodash 提供的 partial 函数来了解一下它如何使用。

    4.1 偏函数的使用

    function buildUri(scheme, domain, path) {
      return `${scheme}://${domain}/${path}`;
    }
    
    const myGithubPath = _.partial(buildUri, "https", "github.com");
    const profilePath = myGithubPath("semlinker/semlinker");
    const awesomeTsPath = myGithubPath("semlinker/awesome-typescript");
    _.partial(func, [partials])

    创建一个函数。 该函数调用 func,并传入预设的 partials 参数。

    来源:https://www.lodashjs.com/docs...

    4.2 偏函数的实现

    偏函数用于固定一个函数的一个或多个参数,并返回一个可以接收剩余参数的函数。基于上述的特点,我们就可以自己实现一个 partial 函数:

    function partial(fn) {
      let args = [].slice.call(arguments, 1);
      return function () {
        const newArgs = args.concat([].slice.call(arguments));
        return fn.apply(this, newArgs);
      };
    }

    4.3 偏函数实现 vs 柯里化实现

    五、惰性函数

    由于不同浏览器之间存在一些兼容性问题,这导致了我们在使用一些 Web API 时,需要进行判断,比如:

    function addHandler(element, type, handler) {
      if (element.addEventListener) {
        element.addEventListener(type, handler, false);
      } else if (element.attachEvent) {
        element.attachEvent("on" + type, handler);
      } else {
        element["on" + type] = handler;
      }
    }

    在以上代码中,我们实现了不同浏览器 添加事件监听 的处理。代码实现起来也很简单,但存在一个问题,即每次调用的时候都需要进行判断,很明显这是不合理的。对于上述这个问题,我们可以通过惰性载入函数来解决。

    5.1 惰性载入函数

    所谓的惰性载入就是当第 1 次根据条件执行函数后,在第 2 次调用函数时,就不再检测条件,直接执行函数。要实现这个功能,我们可以在第 1 次条件判断的时候,在满足判断条件的分支中覆盖掉所调用的函数,具体的实现方式如下所示:

    function addHandler(element, type, handler) {
      if (element.addEventListener) {
        addHandler = function (element, type, handler) {
          element.addEventListener(type, handler, false);
        };
      } else if (element.attachEvent) {
        addHandler = function (element, type, handler) {
          element.attachEvent("on" + type, handler);
        };
      } else {
        addHandler = function (element, type, handler) {
          element["on" + type] = handler;
        };
      }
      // 保证首次调用能正常执行监听
      return addHandler(element, type, handler);
    }

    除了使用以上的方式,我们也可以利用自执行函数来实现惰性载入:

    const addHandler = (function () {
      if (document.addEventListener) {
        return function (element, type, handler) {
          element.addEventListener(type, handler, false);
        };
      } else if (document.attachEvent) {
        return function (element, type, handler) {
          element.attachEvent("on" + type, handler);
        };
      } else {
        return function (element, type, handler) {
          element["on" + type] = handler;
        };
      }
    })();

    通过自执行函数,在代码加载阶段就会执行一次条件判断,然后在对应的条件分支中返回一个新的函数,用来实现对应的处理逻辑。

    六、缓存函数

    缓存函数是将函数的计算结果缓存起来,当下次以同样的参数调用该函数时,直接返回已缓存的结果,而无需再次执行函数。这是一种常见的以空间换时间的性能优化手段。

    要实现缓存函数的功能,我们可以把经过序列化的参数作为 key,在把第 1 次调用后的结果作为 value 存储到对象中。在每次执行函数调用前,都先判断缓存中是否含有对应的 key,如果有的话,直接返回该 key 对应的值。分析完缓存函数的实现思路之后,接下来我们来看一下具体如何实现:

    function memorize(fn) {
      const cache = Object.create(null); // 存储缓存数据的对象
      return function (...args) {
        const _args = JSON.stringify(args);
        return cache[_args] || (cache[_args] = fn.apply(fn, args));
      };
    };

    定义完 memorize 缓存函数之后,我们就可以这样来使用它:

    let complexCalc = (a, b) => {
      // 执行复杂的计算
    };
    
    let memoCalc = memorize(complexCalc);
    memoCalc(666, 888);
    memoCalc(666, 888); // 从缓存中获取
    关注「全栈修仙之路」阅读阿宝哥原创的 3 本免费电子书及 50 几篇 “重学TS” 教程。

    七、参考资源

    查看原文

    赞 21 收藏 13 评论 0

    阿宝哥 发布了文章 · 11月10日

    如何更好地理解中间件和洋葱模型

    相信用过 KoaReduxExpress 的小伙伴对中间件都不会陌生,特别是在学习 Koa 的过程中,还会接触到 “洋葱模型”

    本文阿宝哥将跟大家一起来学习 Koa 的中间件,不过这里阿宝哥不打算一开始就亮出广为人知的 “洋葱模型图”,而是先来介绍一下 Koa 中的中间件是什么?

    学习更多知识,可以访问 ?? 阿宝哥 Github 个人主页

    一、Koa 中间件

    @types/koa-compose 包下的 index.d.ts 头文件中我们找到了中间件类型的定义:

    // @types/koa-compose/index.d.ts
    declare namespace compose {
      type Middleware<T> = (context: T, next: Koa.Next) => any;
      type ComposedMiddleware<T> = (context: T, next?: Koa.Next) => Promise<void>;
    }
      
    // @types/koa/index.d.ts => Koa.Next
    type Next = () => Promise<any>;

    通过观察 Middleware 类型的定义,我们可以知道在 Koa 中,中间件就是普通的函数,该函数接收两个参数:contextnext。其中 context 表示上下文对象,而 next 表示一个调用后返回 Promise 对象的函数对象。

    了解完 Koa 的中间件是什么之后,我们来介绍 Koa 中间件的核心,即 compose 函数:

    function wait(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms || 1));
    }
    
    const arr = [];
    const stack = [];
    
    // type Middleware<T> = (context: T, next: Koa.Next) => any;
    stack.push(async (context, next) => {
      arr.push(1);
      await wait(1);
      await next();
      await wait(1);
      arr.push(6);
    });
    
    stack.push(async (context, next) => {
      arr.push(2);
      await wait(1);
      await next();
      await wait(1);
      arr.push(5);
    });
    
    stack.push(async (context, next) => {
      arr.push(3);
      await wait(1);
      await next();
      await wait(1);
      arr.push(4);
    });
    
    await compose(stack)({});
    以上代码来源:https://github.com/koajs/comp...

    对于以上的代码,我们希望执行完 compose(stack)({}) 语句之后,数组 arr 的值为 [1, 2, 3, 4, 5, 6]。这里我们先不关心 compose 函数是如何实现的。我们来分析一下,如果要求数组 arr 输出期望的结果,上述 3 个中间件的执行流程:

    1.开始执行第 1 个中间件,往 arr 数组压入 1,此时 arr 数组的值为 [1],接下去等待 1 毫秒。为了保证 arr 数组的第 1 项为 2,我们需要在调用 next 函数之后,开始执行第 2 个中间件。

    2.开始执行第 2 个中间件,往 arr 数组压入 2,此时 arr 数组的值为 [1, 2],继续等待 1 毫秒。为了保证 arr 数组的第 2 项为 3,我们也需要在调用 next 函数之后,开始执行第 3 个中间件。

    3.开始执行第 3 个中间件,往 arr 数组压入 3,此时 arr 数组的值为 [1, 2, 3],继续等待 1 毫秒。为了保证 arr 数组的第 3 项为 4,我们要求在调用第 3 个中间的 next 函数之后,要能够继续往下执行。

    4.当第 3 个中间件执行完成后,此时 arr 数组的值为 [1, 2, 3, 4]。因此为了保证 arr 数组的第 4 项为 5,我们就需要在第 3 个中间件执行完成后,返回第 2 个中间件 next 函数之后语句开始执行。

    5.当第 2 个中间件执行完成后,此时 arr 数组的值为 [1, 2, 3, 4, 5]。同样,为了保证 arr 数组的第 5 项为 6,我们就需要在第 2 个中间件执行完成后,返回第 1 个中间件 next 函数之后语句开始执行。

    6.当第 1 个中间件执行完成后,此时 arr 数组的值为 [1, 2, 3, 4, 5, 6]

    为了更直观地理解上述的执行流程,我们可以把每个中间件当做 1 个大任务,然后在以 next 函数为分界点,在把每个大任务拆解为 3 个 beforeNextnextafterNext 3 个小任务。

    在上图中,我们从中间件一的 beforeNext 任务开始执行,然后按照紫色箭头的执行步骤完成中间件的任务调度。在 77.9K 的 Axios 项目有哪些值得借鉴的地方 这篇文章中,阿宝哥从 任务注册、任务编排和任务调度 3 个方面去分析 Axios 拦截器的实现。同样,阿宝哥将从上述 3 个方面来分析 Koa 中间件机制。

    1.1 任务注册

    在 Koa 中,我们创建 Koa 应用程序对象之后,就可以通过调用该对象的 use 方法来注册中间件:

    const Koa = require('koa');
    const app = new Koa();
    
    app.use(async (ctx, next) => {
      const start = Date.now();
      await next();
      const ms = Date.now() - start;
      console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
    });

    其实 use 方法的实现很简单,在 lib/application.js 文件中,我们找到了它的定义:

    // lib/application.js
    module.exports = class Application extends Emitter {  
      constructor(options) {
        super();
        // 省略部分代码 
        this.middleware = [];
      }
      
     use(fn) {
       if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
       // 省略部分代码 
       this.middleware.push(fn);
       return this;
      }
    }

    由以上代码可知,在 use 方法内部会对 fn 参数进行类型校验,当校验通过时,会把 fn 指向的中间件保存到 middleware 数组中,同时还会返回 this 对象,从而支持链式调用。

    1.2 任务编排

    77.9K 的 Axios 项目有哪些值得借鉴的地方 这篇文章中,阿宝哥参考 Axios 拦截器的设计模型,抽出以下通用的任务处理模型:

    在该通用模型中,阿宝哥是通过把前置处理器和后置处理器分别放到 CoreWork 核心任务的前后来完成任务编排。而对于 Koa 的中间件机制来说,它是通过把前置处理器和后置处理器分别放到 await next() 语句的前后来完成任务编排。

    // 统计请求处理时长的中间件
    app.use(async (ctx, next) => {
      const start = Date.now();
      await next();
      const ms = Date.now() - start;
      console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
    });

    1.3 任务调度

    通过前面的分析,我们已经知道了,使用 app.use 方法注册的中间件会被保存到内部的 middleware 数组中。要完成任务调度,我们就需要不断地从 middleware 数组中取出中间件来执行。中间件的调度算法被封装到 koa-compose 包下的 compose 函数中,该函数的具体实现如下:

    /**
     * Compose `middleware` returning
     * a fully valid middleware comprised
     * of all those which are passed.
     *
     * @param {Array} middleware
     * @return {Function}
     * @api public
     */
    function compose(middleware) {
      // 省略部分代码
      return function (context, next) {
        // last called middleware #
        let index = -1;
        return dispatch(0);
        function dispatch(i) {
          if (i <= index)
            return Promise.reject(new Error("next() called multiple times"));
          index = i;
          let fn = middleware[i];
          if (i === middleware.length) fn = next;
          if (!fn) return Promise.resolve();
          try {
            return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
          } catch (err) {
            return Promise.reject(err);
          }
        }
      };
    }

    compose 函数接收一个参数,该参数的类型是数组,调用该函数之后会返回一个新的函数。接下来我们将以前面的例子为例,来分析一下 await compose(stack)({}); 语句的执行过程。

    1.3.1 dispatch(0)

    由上图可知,当在第一个中间件内部调用 next 函数,其实就是继续调用 dispatch 函数,此时参数 i 的值为 1

    1.3.2 dispatch(1)

    由上图可知,当在第二个中间件内部调用 next 函数,仍然是调用 dispatch 函数,此时参数 i 的值为 2

    1.3.3 dispatch(2)

    由上图可知,当在第三个中间件内部调用 next 函数,仍然是调用 dispatch 函数,此时参数 i 的值为 3

    1.3.4 dispatch(3)

    由上图可知,当 middleware 数组中的中间件都开始执行之后,如果调度时未显式地设置 next 参数的值,则会开始返回 next 函数之后的语句继续往下执行。当第三个中间件执行完成后,就会返回第二中间件 next 函数之后的语句继续往下执行,直到所有中间件中定义的语句都执行完成。

    分析完 compose 函数的实现代码,我们来看一下 Koa 内部如何利用 compose 函数来处理已注册的中间件。

    const Koa = require('koa');
    const app = new Koa();
    
    // 响应
    app.use(ctx => {
      ctx.body = '大家好,我是阿宝哥';
    });
    
    app.listen(3000);

    利用以上的代码,我就可以快速启动一个服务器。其中 use 方法我们前面已经分析过了,所以接下来我们来分析 listen 方法,该方法的实现如下所示:

    // lib/application.js
    module.exports = class Application extends Emitter {  
      listen(...args) {
        debug('listen');
        const server = http.createServer(this.callback());
        return server.listen(...args);
      }
    }

    很明显在 listen 方法内部,会先通过调用 Node.js 内置 HTTP 模块的 createServer 方法来创建服务器,然后开始监听指定的端口,即开始等待客户端的连接。另外,在调用 http.createServer 方法创建 HTTP 服务器时,我们传入的参数是 this.callback(),该方法的具体实现如下所示:

    // lib/application.js
    const compose = require('koa-compose');
    
    module.exports = class Application extends Emitter {  
      callback() {
        const fn = compose(this.middleware);
        if (!this.listenerCount('error')) this.on('error', this.onerror);
    
        const handleRequest = (req, res) => {
          const ctx = this.createContext(req, res);
          return this.handleRequest(ctx, fn);
        };
        return handleRequest;
      }
    }

    callback 方法内部,我们终于见到了久违的 compose 方法。当调用 callback 方法之后,会返回 handleRequest 函数对象用来处理 HTTP 请求。每当 Koa 服务器接收到一个客户端请求时,都会调用 handleRequest 方法,在该方法会先创建新的 Context 对象,然后在执行已注册的中间件来处理已接收的 HTTP 请求:

    module.exports = class Application extends Emitter {  
      handleRequest(ctx, fnMiddleware) {
        const res = ctx.res;
        res.statusCode = 404;
        const onerror = err => ctx.onerror(err);
        const handleResponse = () => respond(ctx);
        onFinished(res, onerror);
        return fnMiddleware(ctx).then(handleResponse).catch(onerror);
      }
    }

    好的,Koa 中间件的内容已经基本介绍完了,对 Koa 内核感兴趣的小伙伴,可以自行研究一下。接下来我们来介绍洋葱模型及其应用。

    二、洋葱模型

    2.1 洋葱模型简介

    (图片来源:https://eggjs.org/en/intro/eg...

    在上图中,洋葱内的每一层都表示一个独立的中间件,用于实现不同的功能,比如异常处理、缓存处理等。每次请求都会从左侧开始一层层地经过每层的中间件,当进入到最里层的中间件之后,就会从最里层的中间件开始逐层返回。因此对于每层的中间件来说,在一个 请求和响应 周期中,都有两个时机点来添加不同的处理逻辑。

    2.2 洋葱模型应用

    除了在 Koa 中应用了洋葱模型之外,该模型还被广泛地应用在 Github 上一些不错的项目中,比如 koa-router 和阿里巴巴的 midwayumi-request 等项目中。

    介绍完 Koa 的中间件和洋葱模型,阿宝哥根据自己的理解,抽出以下通用的任务处理模型:

    上图中所述的中间件,一般是与业务无关的通用功能代码,比如用于设置响应时间的中间件:

    // x-response-time
    async function responseTime(ctx, next) {
      const start = new Date();
      await next();
      const ms = new Date() - start;
      ctx.set("X-Response-Time", ms + "ms");
    }

    对于每个中间件来说,前置处理器和后置处理器都是可选的。比如以下中间件用于设置统一的响应内容:

    // response
    async function respond(ctx, next) {
      await next();
      if ("/" != ctx.url) return;
      ctx.body = "Hello World";
    }

    尽管以上介绍的两个中间件都比较简单,但你也可以根据自己的需求来实现复杂的逻辑。Koa 的内核很轻量,麻雀虽小五脏俱全。它通过提供了优雅的中间件机制,让开发者可以灵活地扩展 Web 服务器的功能,这种设计思想值得我们学习与借鉴。

    好的,这次就先介绍到这里,后面有机会的话,阿宝哥在单独介绍一下 ReduxExpress 的中间件机制。

    三、参考资源

    查看原文

    赞 16 收藏 10 评论 0

    阿宝哥 赞了文章 · 11月3日

    刚毕业不久,接私活赚了2万块!


    作者:小傅哥
    博客:https://bugstack.cn

    沉淀、分享、成长,让自己和他人都能有所收获!??

    一、前言

    5:30下班让我有更多的时间!

    ??13年~15年,我还从来没想过一天上班,还能干到6点以后!因为我上班的第一家公司是偏传统的外包公司,与互联网公司不同。不知道现在如何了,但当时:

    • 9点到公司,6点下班,但基本大家5:30也就走了。每天下班都能看见日落??
    • 一个C#写的项目代码能运行十年。一个Java程序员写了两年C#
    • 一年会出那么几次差,全国各个地方都几乎会去。一年去了四次上海!

    也正是因为这样,5:30 下班回家的我,吃完饭也就7点。剩下了大把的时间让我不停的折腾。也就是那段时间接了一些私活,共计赚了2万块。那段期间租住在距离卢沟桥很近的大瓦窑,村落里的平房小院子住起来还是蛮舒服的,开开心心的住了两年。

    小傅哥,租住小平房的那些年!

    哈哈,是不是很大一部分程序员都害怕接私活被发现了不好。而我最开始工作的前两年除了有时间以外,还有一个非常赞的领导。既带着我出去嗨、也领我去他家烤肉,接的私活还告诉要多少钱合适「因为他主要负责带项目」我是程序员。

    ??但,自从到了互联网是彻底没这个时间和精力了。当然另外一个很重要的原因,是要把时间放在更多的正事上,只有可持续的投入和可持续的回报,才更有意义。

    二、2万元私活收入

    1. 企业门户网站(5000元)

    企业门户网站(5000元)

    • 指数:????
    • 背景:刚上班一年左右,高中同学问我学计算机能帮他们公司做个网站吗,就模仿老罗那个锤子公司的样式就行,5000块钱。
    • 结果:我接了,可能也是初生牛犊不怕虎,人家需要用PHP语言写!我一个学Java的,写了快一年的C#,之后用PHP给人家做一个企业门户网站,该说不说胆子挺大!
    • 收获:项目顺利部署完成,5000块钱如约到手,买了我第一个苹果手机 iPhone 4s,仍然在我身边。

    我第一个苹果手机 iPhone 4s

    2. 卖家具宣传网站(2000元)

    卖家具宣传网站(2000元)

    • 指数:???
    • 背景:14年年初,亲戚家开了一个制作水族箱的小作坊,也是得知我是学计算的。锣鼓喧天的找到我说做一个宣传他们公司商品的网站外面找人做太贵了!
    • 结果:??钱咱也不好意思要,只是把服务器和域名等费用的钱要了,不过后来给了我个大红包 2000 元,嘿嘿,手一抖,收了!
    • 收获:得益于我已经接过一个项目,所以PHP开发起来也是很容易,按照他们当时喜欢的样式,做了一个仿照点点网的风格网站布局。这次赚的钱交房租了!

    3. Netty通信框架(2000元)

    Netty通信框架(2000元)

    • 指数:???
    • 背景:14年左右,开始喜欢搞Netty。可能也是当时网上的资料并不多,很多人因为我写了一整套的Netty案例找到我。也就有了这么一次问我能给写个Netty的通信框架不,2000元。
    • 结果:这也是当时头一次不用PHP,而是用Java语言赚到的钱。对我来说还是蛮简单的,1个5:30下班回家就写完了,第二天就给过去了。
    • 收获:知识真的可以变成钱,尤其是那些稍微有点难度,又搞的人不多的时候。

    4. 毕设、讲课、数据采集(11000元)

    除了上面接到的私活,还接到了不少七七八八的小活。

    • 本科生毕业设计,1000元。来自猪八戒网。
    • 研究生加密算法,2000元。一个研究生伙伴跟我一起设计出来的,给我从他们学院申请的费用。
    • 在线给一个学生讲课,好像一天是50元,将来快1个月,1000元。
    • 一个物流数据综合平台,其实功能不算多,有点像记录外贸订单的,5000元。
    • 协助一个自己接项目的老板,写了一周Netty编解码部分代码,对接下位机。2000元。

    就这样,七七八八的在那两年,赚了2万多块钱。当然还有一部分小的收入,不足1000的。也有被骗过,比如人家拿到项目了就不给钱了或是拿到截图了「我没加水印」,人家够演示的了,也不给钱了。

    ,这些都比不了我一个伙伴,他每年接的私活收入会有 20~40 几万,还是挺牛的!

    三、心得体会

    • 接私活让我学会了很多新的技能,比如正儿八经的去部署一个网站,所有的流程都会门清。这对一个只做后端开发的程序员来讲,清楚全流程还是非常重要的。
    • 但就我个人而言,如果你下班已经很晚。那么,不建议接私活,基本是很难交付的,也可能会耽误你的职业发展。在有限的时间里要做于未来更有意义的事,每一条路上的小钱可能都会让你耽误很多时间。尤其是那种仅能赚到点小钱又不能有什么技能长进的事,但也有例外,比如很多人就靠接私活发家了。
    • 透过现象看本质,要做可长期投入和长期回报的事情。工作、学习、沉淀、破局运动、健身、跑步,自律

    四、系列推荐

    查看原文

    赞 3 收藏 0 评论 3

    阿宝哥 发布了文章 · 10月30日

    中了源码的毒,给你一副良药

    近期阿宝哥在团队内搞了一个 如何读源码 的专题,主要目的是让团队的小伙伴们了解读源码的思路与技巧。在此期间,阿宝哥也写了 77.9K 的 Axios 项目有哪些值得借鉴的地方从 12.9K 的前端开源项目我学到了啥如何让你的 Express 飞起来 三篇源码解析的文章。其中前两篇在 掘金社区 获得不错的评价,平均 705+ 个 ??,所以阿宝哥就想写一篇文章来分享一下本人读源码的思路、技巧与工具。

    好的,让我们开始出发吧!在进入正题之前,我们先来个读源码前的 灵魂四连问 热热身。

    一、灵魂四连问

    1.1 为什么要读源代码

    1.2 如何选择项目

    1.3 如何阅读源码

    1.4 有实际的案例么

    既然前两篇文章比较受大家喜欢,接下来阿宝哥就以最受欢迎的 Axios 为例,来分享一下读源码的思路与技巧。

    二、如何品读 Axios?

    2.1 走进 Axios

    Axios 是一个基于 Promise 的 HTTP 客户端,同时支持浏览器和 Node.js 环境。它是一个优秀的 HTTP 客户端,被广泛地应用在大量的 Web 项目中。

    由上图可知,Axios 项目的 Star 数为 78.1K,Fork 数也高达 7.3K,是一个很优秀的开源项目,所以值得大家细细品读。

    2.2 发现 Axios 的美

    在确认 Axios 为 “追求目标” 之后,下一步我们就需要来发现它身上的优点(特性):

    每个人对 “美” 都有不同的看法,对于阿宝哥来说,我看中了图中已选中的三点。因此,它们也很光荣地成为读源码的三个切入点。当然切入点也不是越多越好,可以先找自己最感兴趣的地方作为切入点。需要注意的是,如果切入点之间有关联关系的话,建议做个简单的排序。

    2.3 感受 Axios 的美

    选择切入点之后,我们就可以开始逐一感受 Axios 的设计之美。以 能够拦截请求与响应 这个切入点为例,首先我们就会接触到 拦截器 的概念。所以我们需要先了解拦截器是什么、拦截器有什么作用以及如何使用拦截器,这里我们可以从项目的 官方文档 或者项目中的 README.md 文档入手。

    2.3.1 拦截器的作用

    Axios 提供了请求拦截器和响应拦截器来分别处理请求和响应,它们的作用如下:

    • 请求拦截器:该类拦截器的作用是在请求发送前统一执行某些操作,比如在请求头中添加 token 字段。
    • 响应拦截器:该类拦截器的作用是在接收到服务器响应后统一执行某些操作,比如发现响应状态码为 401 时,自动跳转到登录页。
    2.3.2 拦截器的使用
    // 添加请求拦截器 —— 处理请求配置对象
    axios.interceptors.request.use(function (config) {
      config.headers.token = 'added by interceptor';
      return config;
    });
    
    // 添加响应拦截器 —— 处理响应对象
    axios.interceptors.response.use(function (data) {
      data.data = data.data + ' - modified by interceptor';
      return data;
    });
    
    axios({
      url: '/hello',
      method: 'get',
    }).then(res =>{
      console.log('axios res.data: ', res.data)
    });

    在了解完拦截器的作用和用法之后,我们就会把焦点聚焦到 axios 对象,因为注册拦截器和发送请求都与它有紧密的联系。不过在看具体源码之前,阿宝哥建议先对功能点做一下梳理。以下是阿宝哥的分析思路:

    Axios 的作用是用于发送 HTTP 请求,请求拦截器和响应拦截器分别对应于 HTTP 请求的不同阶段,它们的本质是一个实现特定功能的函数。这时我们就可以按照功能把发送 HTTP 请求拆解成不同类型的子任务,比如有 用于处理请求配置对象的子任务用于发送 HTTP 请求的子任务用于处理响应对象的子任务。当我们按照指定的顺序来执行这些子任务时,就可以完成一次完整的 HTTP 请求。

    既然已经提到了任务,我们就会联想到任务管理系统的基本功能:任务注册、任务编排(优先级排序)和任务调度等。因此我们就可以考虑从 任务注册、任务编排和任务调度 三个方面来分析 Axios 拦截器的实现。

    2.3.3 任务注册
    // 添加请求拦截器 —— 处理请求配置对象
    axios.interceptors.request.use(function (config) {
      config.headers.token = 'added by interceptor';
      return config;
    });
    
    // 添加响应拦截器 —— 处理响应对象
    axios.interceptors.response.use(function (data) {
      data.data = data.data + ' - modified by interceptor';
      return data;
    });

    lib/axios.js 路径下,我们可以找到 axios 对象的定义。为了能直观地了解对象之间的关系,阿宝哥建议大家在读源码的过程中,多动手画画图。比如阿宝哥使用下图来总结一下 Axios 对象与 InterceptorManager 对象的内部结构与关系:

    2.3.4 任务编排

    现在我们已经知道如何注册拦截器任务,但仅仅注册任务是不够,我们还需要对已注册的任务进行编排,这样才能确保任务的执行顺序。

    同样对于任务编排,也可以使用图的形式来展现任务编排后的结果。 这里有一个小技巧,就是可以采用对比的形式来展示任务编排后的结果,这样子会更加清楚任务编排的处理逻辑。

    2.3.5 任务调度

    任务编排完成后,要发起 HTTP 请求,我们还需要按编排后的顺序执行任务调度。

    需要注意的是:在阅读源码过程中,不要太在意细节。比如在研究 Axios 拦截器原理时,不需要再深入了解 dispatchRequest 背后的具体实现,只需知道该方法用于实现发送 HTTP 请求即可,这样才不会把整个线路拉得太长。

    在分析完特定的功能点之后,也许你已经读懂的具体的源代码。但阿宝哥觉得这并不是最重要的,更重要的是思考它的设计思想,这样设计有什么好处,对于我们有没有什么值得借鉴和学习的地方。比如参考 Axios 拦截器的设计模型,我们就可以抽出以下通用的任务处理模型:

    上面阿宝哥以 Axios 的拦截器为例,分享了读 Axios 源码的思路与技巧。接下来阿宝哥来分享一些读源码的建议和辅助工具。

    三、读源码的建议

    四、读源码辅助工具

    如果你对下列辅助工具感兴趣的话,可以通过以下图片来源的链接,来直接打开每个工具的在线地址。

    (图片来源:https://www.processon.com/vie...

    五、总结

    其实除了上面的内容之外,读优秀开源项目还有挺多值得关注的地方。阿宝哥在学习 BetterScroll 项目源码时,总结了一张思维导图

    (图片来源:https://www.processon.com/vie...

    下面阿宝哥用一张图来总结一下 axiosbetter-scroll 这两个开源项目的学习路线:

    1、Axios 项目的切入点是从 Github 中的功能特性中筛选出来的;

    2、BetterScroll 的切入点是从掘金上 BetterScroll 2.0 发布:精益求精,与你同行 这篇文章中介绍的功能亮点中找到的。

    除此之外,阿宝哥也来简单总结一下本文介绍的读源码的思路与技巧:

    • 站在巨人的肩膀,提前阅读一些项目相关的优质文章;
    • 汇总学习或工作中遇到的问题,带着问题进行源码学习;
    • 明确阅读源码的主线或切入点;
    • 尽可能从简单的示例出发来分析每个功能点;
    • 先梳理清楚主要流程,不要太在意细节,避免把整个线路拉得太长;
    • 在阅读源码过程中,要多多画图,这样理解起来会更加直观。

    本文阿宝哥分享了个人读源码的思路、技巧与工具,希望阅读完本文能对你有所启发或帮助。如果你有读源码更好的思路与技巧,欢迎随时跟阿宝哥交流哈。有写得不好的地方,也请各位见谅哈。

    六、参考资源

    查看原文

    赞 30 收藏 22 评论 5

    阿宝哥 赞了文章 · 10月26日

    今天你写博客了吗?

    作者:小傅哥
    <br/>博客:https://bugstack.cn

    沉淀、分享、成长,让自己和他人都能有所收获!?

    一、前言

    灵魂拷问你今天...看?书了吗、写?博客了吗、去?跑步了吗?

    ?害羞了吧,几乎每一项都是灵魂拷问。其实大多数的你和我,也都想让自己做一些这样有意义的事,只不过成年人确实没有一个简单的周末。

    经常有粉丝问我:看你写了那么多文章,你的时间是怎么管理的呢? 就我个人而言也没有一个明确的时间管理,只是一股脑的把空闲时间全部投入到我喜欢的事情上。比如:我会早上6:30起来跑步,是因为我想享受早上的阳光我会深夜和周末码文、是因为我在做着自己喜欢的事情不存在坚持,同样即使是在医院陪床、陪媳妇逛街或是突然梦中醒来,只要想到好的内容会随手?记录在手机上。

    所以,我也没有所谓的时间管理,只不过是把更多的时间放在了自己喜欢的事情上。喜欢做一件事,往往来自做了一件喜欢的事!

    二、写博客很重要!

    你总是很急,急到地基都不想打,就想飞!

    一个月了解XXX、一天搞懂XXX、一文学会XXX。越来越快,恨不得这知识最好一小时就学会!但又怎么可能呢,要不文章内容太过于碎片化偏向于介绍,要不文章冗长接近上万字。但作为一个新人来说,这样的文章几乎就是收藏最佳,收藏完就会了,学是不可能学的,就是感觉爽!

    1. 碎片知识整理

    如果不是成体系的系统学习,大部分我们接受的知识都是碎片化的。就像有人说,HashMap是数组加链表和红黑树实现、HashMap用到了拉链式寻址、HashMap为了让数据存放更均匀使用到了扰动函数等等,这些知识点可能并不是第一次你就全部接受或者学习到,而是随着你个人技术栈的了解广度和深度逐步了解的。那么最终把这些碎片化的内容,整理成完整的知识项记录到自己的博客中就非常重要了,因为它能加深你的印象,也能让你把整个知识串联起来。

    关于碎片化的知识整理可以使用思维导图,想到哪就写到哪,最后再把整体的内容梳理成文章。

    示例,思维导图梳理知识项目

    2. 脑力深度思考

    心流(英语:Mental flow)在心理学中是指一种人们在专注进行某行为时所表现的心理状态。

    你是否当你一低头一抬头过去了4、5个小时,因为你在全情的投入到某个事情上了,同时脑力也是深度思考执行的。这个感觉被称为心流。

    有时候我会想,是不是我深度学习某个知识时大脑中是在建立一些神经元凸起,就相当于在铺路所以大脑会热。而长时间打游戏,最后就恶心难受,是因为这种消极短暂的快乐,会把修好的路拆掉,最后脑瓜疼?!

    可能是因为内卷吧,至少目前程序员这个行业如果想突破一定阶段的瓶颈,就需要不断的学习,甚至是深度学习。这样你才能有一定的核心竞争力,像几年前60分及格就能找个好工作的事情越来越少了。

    写博客算是其中一个可以深度思考的方式,当你要把某项知识整理的完整时,你会需要查阅大量的资料和实践验证。总之无论是为自己的责任心或者是害怕内容写错被喷,你都会认认真真的研究、思考、验证,最终把这样一篇内容梳理、整理、编写出来。

    3. 不断折腾学习

    关于写博客其实各大平台就够用了,比如:CSDN、掘金、开源中国、思否、腾讯云+社区、知乎、博客园、简书等等,都可以记录博客。

    但对于有点喜欢折腾的人来说还会自建博客,在这个过程中会需要挺多的知识来支撑,比如:

    自建博客知识栈,博客、论坛、域名、服务器

    从上学开始到现在,这些各种各样的博客和论坛,我都一一折腾过。虽然它们都因为各种原因最后死了,不过我保留了一些截图,如下是其中的一个。

    早年间小傅哥建的论坛,也是风生水起~.-

    三、写博客的收获

    有小伙伴问我,看你博客文章怎么还得关注公众号,不纯粹,恶心?!

    我知道他肯定是没有理解我的初心,所有才有了这样的疑问,在我回复后,摆平?:

    1. 我和你一样,都是走在这条路上的程序员
    2. 但从某一天开始我做公众号了,为了可以有更多的粉丝关注,我确实需要做一些营销策略
    3. 这些博客、公众号、GitHub、文章等,都需要投入大量的时间、经历和一部分资金
    4. 而我也没有接广告、或者其他割韭菜内容,所以我是一个人在投入个人经历,建设一个技术社区
    5. 最终,让大家关注公众号,也是为了可以阅读到我更多的文章,知道我的技术内容推送
    6. 好了,希望理解,也可以添加我的微信:fustack

    1. 建站一年突破20万+PV

    对我来说,建站博客撸文章是一定要给人看的,越火越好,否则是满足不了我的虚荣心的?。

    看的人越多,反馈的越多,我能学到也越多。好在这一路走来没有胡乱喷我的人,也让我积攒了不少的流量。虽然整体不多,但对我来说还是蛮开心的!

    建站一年突破20万+PV

    2. 两个破千Star的Github项目

    可能作为程序员????都想在这个男人的社区里有一个破千的Star项目来撑厂子!哈哈哈,我也是!

    https://github.com/fuzhengwei

    最开始不足100Star的时候,我也是发到群里求点赞,不过也经常被喷:什么是金子不需要这样、我的项目从来不需要点赞、把这憨批T喽...

    但其实做过营销系统的人就知道,根本不是这样,如果你不分享是不会被发现的。即使你有再好的内容,只要你没有与之匹配的渠道,永远也不会闪亮。当一个不错的项目突破500Star后,会陆续循环带来流量,由这500人,一人分享一次可能就已经突破到1k!

    所以,做好你要做到事,别相信这条路上还没启程的他她它!

    这虽然不是一个牛皮,但也要让数据证明你的闪光!

    ?看到这,点个星星?去吧!https://github.com/fuzhengwei/CodeGuide/wiki

    3. 一本全网下载量超过10万+的PDF

    你的一本PDF电子书,被全网下载10万+,还没有人喷你?嘿嘿!

    《重学Java设计模式》PDF,全网10万+下载。这是写公众号以来,最大的收获了!这是一本什么样的书籍呢,会有这么大的魅力!其实更多的是广大号主和粉丝的帮忙,是他们让这本书传播的更广,帮助更多的研发人员,感谢!

    重学Java设计模式

    这是一本共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF。从互联网实际业务开发中抽离出,交易、营销、秒杀、中间件、源码等22个真实场景,来学习设计模式实践使用的应用可上手技能。

    下载 关注公众号:bugstack虫洞栈,回复:设计模式。可下载书籍和全部案例源码。

    四、总结

    • 我的Github:https://github.com/fuzhengwei/CodeGuide/wiki
    • 本篇介绍了写博客的经历,以及吹吹牛?!希望文章里那些正确的事,才是你该学习的地方。
    • ?读不在三更五鼓,功只怕一曝十寒!岁月从不辜负奋斗的人,愿努力拼搏学习的你,都能收获美好的前程!

    五、系列推荐

    查看原文

    赞 3 收藏 0 评论 0

    认证与成就

    • 获得 3961 次点赞
    • 获得 20 枚徽章 获得 1 枚金徽章, 获得 10 枚银徽章, 获得 9 枚铜徽章

    擅长技能
    编辑

    开源项目 & 著作
    编辑

    • Angular FAQ

      Angular 常见问题汇总(2.x ~ 4.x)

    • Angular 2 & Ionic 2 资料汇总

      Angular 2 & Ionic 2 资料汇总

    • HTTP资源大全

      涉及 B/S、URI、MIME、HTTP请求和响应报文、HTTP 请求方法和状态码,并收录了 HTTP 经典教程和相关工具,如 Cookie 与 Session、HTTP 缓存、CORS、HTTP/2、HTTP爬虫、HTTPS及常用的HTTP抓包工具、Chrome相关插件、各平台HTTP包、压力测试工具等资料

    注册于 2017-03-09
    个人主页被 35.8k 人浏览

    bt365体育投注