Friday, June 25, 2021

SegmentFault 最新的文章

SegmentFault 最新的文章


Windows 11 官宣,首次支持安卓 APP,全新应用商店机制抗衡苹果

Posted: 24 Jun 2021 08:17 PM PDT

在 5 月底举办的微软 Build 2021 大会上,微软 CEO 萨提亚 · 纳德拉表示正在测试 Windows 系统,这将是过去十年 Windows 最重要的一次更新。

6 月 24 日晚,Windows 11 正式官宣。除了前不久被泄露的界面更新外,Windows 11 还集成了 Microsoft Team、发布全新的 Microsoft Store,更重要的是首次将 Android 应用程序引入 Windows。

纳德拉表示,Windows 不只是一个操作系统,它还是平台创建者的平台 (platform for platform creators)。微软的野心一览无余。

UI 界面重新设计

Windows 11 简化了界面设计和用户操作,使用全新的 "开始" 键和任务栏,图标居中。

此外,新一代 Windows 系统还推出了全新的贴靠布局、贴靠群组和虚拟桌面功能,帮助用户轻松访问所有应用以及进行多任务处理。用户可以更轻松地整理窗口和优化屏幕空间,在干净整洁的布局中按需查看内容,还可以单独创建虚拟桌面,并根据自己的喜好进行定制。

集成 Microsoft Teams,Widgets 回归

Windows 11 将基于 Microsoft Teams 技术的 Chat 整合到任务栏中,提供新的人际连接方式。使用 Microsoft Teams,用户可以直接从桌面连接到想要联系的人。无论是在 Windows、Android 或 iOS 平台,用户都可以通过打字、聊天、语音或视频与所有的联系人,在任何地点、任何平台或设备上进行实时互联。

Windows 11 还展示了小工具的回归。其新推出的 Windows Widgets 是一项个性化提示功能,由 AI 及性能卓群的 Microsoft Edge 浏览器驱动。用户可以借助 Microsoft Edge 和众多小组件,快速及时地了解资讯、信息和娱乐内容。

全新 Microsoft Store,支持安卓应用

Windows 11 推出了全新的 Microsoft Store,速度更快。用户可以更便捷地探索和发现应用程序、游戏、视频节目、电影等内容。

但微软的野心不止于此。

其全新的支付机制允许开发者绕过微软的支付机制,即微软零抽成。开发者也可以使用微软的支付机制在 Microsoft Store 上架应用和游戏,微软的抽成分别为 15% 和 12%,远低于苹果 APP Store。

此外,微软宣布将 Android 应用程序引入 Windows。今年晚些时候,用户能够在 Microsoft Store 中找到 Android 应用程序,并通过亚马逊应用商店下载。

纳德拉表示:平台只有在允许基础创新和类别创造的情况下,才能服务社会。正因为此,微软引入了新的 store 商业模式和策略,在 Windows 上支持更多 app,包括安卓 app。

免费升级,今年晚些时候推出

除了以上介绍的亮点,Windows 11 还针对触屏进行了一系列优化,并对 Windows PC 游戏体验进行了改进,支持用户喜爱的 PC 游戏配件和外设。通过 Xbox Game Pass for PC 或 Xbox Game Pass Ultimate,玩家可以随时访问超过百款优秀的 PC 游戏,以及其它不断加入的新游戏。

微软称,Windows 11 支持免费升级,但对电脑系统有一定要求,最低系统要求见下图:

目前,Windows 11 尚未发布,将于今年稍晚些时候推出。

中科院发布国产 RISC-V 处理器“香山”:已成功运行 Linux/Debian

Posted: 24 Jun 2021 01:03 PM PDT

本周,首届 RISC-V 中国峰会在上海科技大学举办。这是 RISC-V 第一次在北美以外地区举办同等规模的峰会。在本届大会上,中科院大学教授、中科院计算所研究员包云岗公布了国产开源高性能 RISC-V 处理器核心——香山,其核心以"湖"来命名架构代号,第一代叫做"雁栖湖","雁栖湖"RTL代码于今年4月完成,计划于7月基于台积电28nm工艺流片。第二代架构叫做"南湖",将采用中芯国际14nm工艺,预计今年年底流片。北京微核芯参与了第一期的设计工作,目前团队正招募香山处理器二期联合开发合作伙伴,加入的企业已有字节跳动等公司。

作者 | 包云岗,2003年本科毕业于南京大学,2008年获中科院计算所博士学位,2010-2012年普林斯顿大学博士后,现为中科院计算所研究员,所长助理,先进计算机系统研究中心主任,中国科学院大学岗位教授,博士生导师,中国开放指令生态(RISC-V)联盟秘书长。

22号下午关于香山的报告,因为Zoom直播出现了技术故障,导致大家未能听到完整的报告,稍有些遗憾。考虑了一下,这里就把报告PPT直接贴出来,再加上我们在香山开发过程中的一些考虑和想法,跟大家分享。

图片

这个报告主要回答四个问题:

  • 一、为什么要做香山?
  • 二、香山什么水平?
  • 三、香山怎么做的?
  • 四、香山未来如何发展?

一、为什么要做香山?

2010年RISC-V诞生,迄今已有11年。如今,在RISC-V国际基金会网站上登记的各类商业或开源的RISC-V处理器核就有上百个(如下链接),为什么还要做一个开源的高性能RISC-V核?

RISC-V Exchange: Cores & SoCs - RISC-V International

对于这个问题,我们和很多业界企业交流过,也做了很多调研与分析,这都让我们判断认为业界需要一个开源的高性能RISC-V核。另一方面,我们也在思考一个问题——为什么CPU领域还没有一个像Linux那样的开源主线?1991年开源的Linux诞生,到今天正好30年。如今,Linux不仅被工业界广泛应用,也成为学术界开展操作系统研究的创新平台。

RISC-V是开放开源的指令集,允许全世界任何人免费实现一个RISC-V处理器,可以是商用,也可以开源,这是和公司私有的X86/ARM指令集相比最大的区别之一。但是,十年过去了,到现在还未能形成一个像Linux那样的开源主线。Berkeley的BOOM目标是一个高性能开源RISC-V核,但是BOOM代码仓库相对不开放,官方建议其他人实现任何功能都要事先和他们沟通,以确保不要与他们的计划产生冲突。根据GitHub官方的统计页面显示,从2014年1月至今,为BOOM提交过超过100行代码修改的仅有8人。由此可见,一定程度上因为BOOM严格的外部贡献政策,开源社区对BOOM的参与度并不高。

所以,团队的唐丹博士和我一直认为要建立一个像Linux那样的开源RISC-V核主线,既能被工业界广泛应用,又能支持学术界试验创新想法。最关键的是,一定要让它像Linux那样至少存活30年!

于是,"香山"诞生了。

图片

我们做了一年多的准备工作——申请经费,启动"一生一芯"计划培养人才,建立团队,寻找合作伙伴……这期间得到了太多太多人的支持和帮助:计算所孙凝晖院士帮我们多处找经费,国科大全力支持"一生一芯"计划,鹏城实验室支持我们建立起后端物理设计团队,多位计算所老所友毅然决定参与开源主线等等,就不一一列举了。

终于,香山正式启动了——2020年6月11日,香山在GitHub上建立了代码仓库。

短短的的一年时间里,25位同学和老师参与了香山的开发。821次主分支代码合并,3296次代码提交(commit),5万余行代码,400多个文档,记录了香山的成长过程。我们的理念是代码开源、流程开放、文档公开。这期间,有企业直接参与开发,也有企业表达参与意向,都因为认同开源理念,愿意一起来共建开源的香山。这些来自工业界的积极反馈,给予我们极大的鼓舞和信心,让我们更坚定地去践行"科研重工业模式"。

"科研重工业模式",是 2020年1月我为《中国计算机学会通讯(CCCF)》写了一篇卷首语《伯克利科研模式的启发》中提出的:

袁岚峰:CCCF卷首语 :伯克利科研模式的启发 | 包云岗

回顾伯克利的科研历程,可以发现他们在过去几十年研制了大量的原型系统,不仅推动了技术进步甚至颠覆产业,也培养了一代代杰出人才(其中多位获得图灵奖):1950年代CALDIC系统(Doug Englebart),1960年代Project Genie系统(Butler Lampson与Chuck Thacker),1970年代BSD Unix操作系统与INGRES数据库系统(Michael Stonebraker),1980年代RISC处理器(David Patterson),1990年代RAID存储系统与NOW机群系统……如果用一句话来总结伯克利的科研模式,那就是——热衷于研制真正能改变现状的原型系统,哪怕需要大量工程投入。国重主任孙凝晖院士称之为"科研重工业模式"。

"科研重工业模式",我们不想纸上谈兵,我们要用行动去实践。

图片

二、香山什么水平?

香山是一款开源RISC-V处理器核,它的架构代号以湖命名。第一版架构代号是"雁栖湖",这是带有浓重国科大情节的同学们起的名字,因为他们研一都在怀柔雁栖湖待了一年。"雁栖湖"RTL代码于2021年4月完成,计划于7月基于TSMC 28nm工艺流片,目前频率为1.3GHz。

第二版架构代号是"南湖",这是向建党100周年致敬。"南湖"计划在今年年底流片,将采用中芯国际14nm工艺,目标频率是2GHz。

图片

香山选择什么开源许可证?这个问题纠结了我们好一阵子。后来,我们专门向北京大学周明辉教授请教,小伙伴们制定了4种开源许可证方案。在反复对比权衡后,最终选择了如下表格中的方案①——木兰宽松版许可证(MulanPSLv2)。在此,特别感谢北大周明辉老师的专业指导!

图片

开源许可证方案对比(徐易难整理)

"雁栖湖"架构是一个**11级流水、6发射、4个访存部件的乱序处理器核。**在发射宽度上已经可以和一些ARM高端处理器核相当,但还未进行充分优化,因此实际性能还有不小的差距。我们希望未来通过持续迭代优化("南湖"-->"X湖"-->"Y湖"-->……),性能达到ARM A76的水平。

图片

image.png

我们基于GitHub CI构建了一套流程化的自动回归测试框架,并在过去大半年不断增加测试负载,从cputest,risc-tests到Linux,到SPECCPU workload。这套自动回归测试框架在保障和验证芯片的正确性。

图片

每个大项目总会有一些激动人心的时刻,这段30秒的小视频记录了香山在FPGA上启动Linux/Debian的时刻,略带喜感。

视频链接:香山在FPGA上启动Linux/Debian

三、香山怎么做的?

香山开发初期速度非常快:6月11日建立代码仓库,7月6日乱序流水线便已完成,能正确运行CoreMark,不到一个月时间;9月12日,Linux正确启动10月22日,Debian正确启动。

接下来便是大半年的结构优化、性能调优、时序优化工作,香山架构几乎相当于重构了一遍。一个典型的例子,香山的第一版分支预测器(BPU)参考了BOOM的BPU,但后端评估频率只能达到800MHz(TSMC 28nm)。于是负责BPU设计的勾凌睿在几位老师的指导下,不断优化BPU结构,最终将频率提升到了1.4GHz。

这期间,小伙伴们纷纷自己动手,开发了各种各样的优化和调试工具,大大地加速了优化和验证环节。这让我真心佩服这批90后——他们真是充满了创造力,从工作到生活,而主要驱动力之一就是"省(tou)时(lan)"。比如宁可自己写个程序自动点外卖,也懒得打开手机看菜单点。

图片

香山的开发至少有两个重要的决策,第一个便是选择敏捷设计语言Chisel。很多人质疑Chisel,排斥Chisel,但是我们在充分评估后,还是决定使用Chisel。

我们团队是在2016年开始使用Chisel,一开始组里也充满质疑。2018年,我们设计了两组定量的对比实验,找了2位同学用Chisel、1位工程师用Verilog分别设计一个L2 Cache模块。通过一系列量化对比,得出了如下三个结论:

  1. Chisel开发效率远高于Verilog;
  2. 实现相同的功能,Chisel代码量仅为Verilog的1/5(因此香山的5万行Chisel代码相当于25万行Verilog代码);
  3. Chisel的开发质量不比Verilog差。

后来将实验结果发表在2019年1月的《计算机研究与发展》。最近去华为交流,才知道这些对比结果也推动了华为内部组建了Chisel开发团队,如今华为也是Chisel的支持者。

2020年,我们又基于Chisel完成了一款8核标签化RISC-V处理器的流片,这是基于Rocket处理器核进行了标签化体系结构改造,采用TSMC 28nm工艺流片。虽然因为时间紧张,并没有进行细致的后端优化,但芯片返回后也还能正常运行在1.2GHz。这是一颗有一定复杂度的8核SoC芯片,但Chisel能应对。所以,我们相信Chisel可用来开发复杂芯片。

图片

在开发香山的过程中,我们团队积累了丰富Chisel开发经验。小伙伴们(徐易难、王凯帆、蔺嘉炜、余子濠、金越)准备了6个报告,将会在6月25日的CCC Workshop上和大家分享。

图片

另一个重要决策就是高度重视构建支持敏捷设计的流程与工具。

图片

我们在开发香山的过程中,一直在强调流程、平台、基础设施的重要性。我更多是扮演了啦啦队队长的角色,而小伙伴们则真正将理念落实到了具体行动。

图片

为了更好地支持Chisel开发与调试,为了更快地捕捉、复现和定位bug,为了更准确地评估优化技术的性能收益,小伙伴们开发十余种各具特色的工具。这些工具支撑起了一套处理器芯片敏捷开发的流程。当然,这套流程还比较初级,尚不系统化。我们也期待更多的开源开发者加入,一起完善这套敏捷设计流程。

图片

下面举几个工具的例子。NEMU是由余子濠在南大本科时便开始开发的一款教学模拟器。在计算所读博期间,他凭借一人之力一直在持续改进和优化NEMU,使NEMU成为一个效率接近QEMU的高性能解释器——启动Debian甚至比QEMU还要快18.2%(9.87s vs. 12.07s)。

更重要的是NEMU是指令解释器,可以针对每一条指令进行动态分析;相比而言,QEMU的翻译粒度是基本块,无法跟踪每一条指令。事实上,NEMU的这种指令解释器机制,成为了香山开发中正确性验证框架Difftest的基础。(余子濠将会在6月23日下午介绍NEMU)

图片

Cache是处理器中非常核心的模块,尤其是要支持一致性协议的Cache更为复杂。为此,小伙伴们开发了一套专门验证支持TileLink一致性协议的Cache模块测试框架Agent Faker,发现了好几个Cache模块的bug。(张传奇将会在6月25日上午介绍这个工作)

图片

Difftest是一个基于NEMU的指令集在线差分验证框架。它的一端是模拟器,提供处理器执行的黄金标准;另一端是运行RTL的仿真器,在仿真过程中会将指令数、中断、MMIO、微结构状态等信息发送给NEMU进行比对,从而判断RTL实现的正确性。

Difftest最早是由余子濠实现,后来王凯帆进行优化,其中一个最重要的改进就是SMP-Difftest,支持多核SMP的全系统仿真,并且支持Cache一致性、内存一致性等需要软硬件协同的问题。(王凯帆将会在6月24日下午介绍Difftest)

图片

如何快速捕捉、复现、定位bug是调试过程中非常关键的步骤,很多时间都是消耗在这个阶段。小伙伴们提出了一种创新的轻量级仿真快照技术——把整个仿真程序看成是一个进程,利用fork机制创建子进程。然后父进程继续执行,子进程暂停。当父进程出错时,则可以恢复到子进程进行调试。LightSSS这个机制和Verilator仿真器自带的Savable机制相比,单次快照时间缩短了近7000倍!(余子濠将会在6月23日下午介绍LightSSS)

图片

很多人质疑Chisel不方便调试。小伙伴们则充分利用了Chisel的可以自定义Firrtl Transform的特点,设计了一套新型的硬件敏捷调试栈,可将基于波形的调试转换为基于事件的调试。我们设计了一套工具,可以直接将高层语义新型从波形中提取出来,并进行可视化。为此,还专门设计了一个Xiang语言。(蔺嘉炜将在6月23日下午介绍该工作)

图片

处理器性能优化环节最关键是要快速准确地评估优化技术带来的性能收益。如果评估过程需要几天时间,那将会严重影响迭代优化效率。小伙伴们设计了一个敏捷性能评估框架BetaPoint,它利用了三个机制——Sampling机制、Generic Full System Checkpoint机制和Functional Warmup机制,实现了可以在10个小时内估算出处理器的SPEC分数。(周耀阳将会在6月23日傍晚介绍BetaPoint)

图片

整个香山开发团队将在这次峰会上和大家分享22个技术报告。这些报告都是清一色的90后,很多都是95后:勾凌睿、胡博涵、金越、李昕、刘志刚、蔺嘉炜、王华强、王诲喆、王凯帆、徐易难、余子濠、张传奇、张发旺、张林隽、张紫飞、张梓悦、周耀阳、周意可、邹江瑞;此外还有多位参与香山开发的同学这次并没有投稿。这些小伙伴们在香山的开发过程中做出了不可替代的贡献。

图片

四、香山未来如何发展?

目前香山正在进行下一代架构"南湖"的开发,目标是今年年底流片,基于中芯国际的14nm工艺频率达到2GHz,SPECCPU分值达到10分/GHz。这是一个很有挑战的目标,需要对架构进行大幅度的优化改进。

前几天,小伙伴们专门去了一趟嘉兴南湖,研讨香山未来的发展。除了技术,我们再一次聚焦到流程与平台。此前构建的敏捷设计流程与平台支撑了20多人的开发团队,这远远不够。现在我们需要考虑的是该如何构建一套开源、开放、规范的开放流程,能支撑2000人的开源社区一起开发。

支持成千上万人一起开发开源软件,这已经有成功的经验。但是如何支持数千人一起开发开源处理器,目前还没有可以参考的案例,只能靠我们自己摸索。也期待各界专家给我们更多的指导和建议。

图片

我们有一个愿望——希望"香山"能存活30年;我们有一个约定——30年后再一起聚聚,到时再看看香山会变成什么样。但是,要实现这个愿望,还有很多很多问题和挑战需要去解决。

真诚地期待有更多伙伴加入香山的开发队伍!

图片

香山是在中科院计算所、鹏城实验室的支持下,通过中国开放指令生态(RISC-V)联盟联合业界企业一起开发一款开源高性能RISC-V处理器核,也得到了北京智源人工智能研究院的支持。在此,要特别感谢北京微核芯公司的资深专家给予香山的开发强力的支持,他们非常认同开源理念,也是第一家与香山联合开发的企业。很高兴"南湖"架构的开发有了更多的合作伙伴,感谢对香山的支持。

欢迎联系我们一起加入香山开源社区!

图片

五、花絮

  • 一本关于香山的书。很多人觉得CPU设计很难,很高大上,我们倒是希望能把这个门槛给降一降——所以打算出一本类似伯克利的《TCP/IP协议栈详解》和毛德操老师的《Linux内核源代码场景分析》那样的书,结合香山的源代码来分析CPU设计细节和诀窍(know-how)。这本书也可以用开源的方式由社区一起撰写,欢迎感兴趣的朋友一起参加。
  • "一生一芯"计划的回报。首期"一生一芯"计划的五位同学(金越、王华强、王凯帆、张林隽、张紫飞)在本科一毕业便加入了香山的开发,成为了技术骨干。很多人都问他们什么时候毕业,可能需要再等等了。他们现在才研一,还要再等两年才会有同学毕业。
  • 疫情中的封闭开发。2020年6月初,北京新发地忽然出现了新冠疫情,导致同学们都无法回到北京。感谢深圳鹏城实验室的大力支持,整个团队全部集中到深圳,在鹏城实验室集中了三个月的封闭开发。那段时间基本上每周都有超过150个commit,反而成为开发效率最高的一个时间段。
  • 香山的Logo。我们尝试了很多个香山logo设计,最后经过大家投票选择了左下角的那个方案。不过有朋友好心告知香山的红叶主要是黄栌叶。好在香山也有五角枫,暂时就先定这个logo了。


最后几句

有幸和阿里巴巴的戚总(戚肖宁)一起担任首届RISC-V中国峰会的联席大会主席,但其实幕后是中科院软件所和上海科技大学的老师们为这次峰会的筹备和保障工作付出了巨大的努力。

由于疫情带来极大的不确定性,组委会始终保持高度紧张状态:一开始只开放了大约1500位线下参会名额,但很快就报满了;后来又开放了两次补报机会,但每次也只开放了200个名额,最后一共是2600人次(有的同时报名参加主会和分会)。

但即使如此,最后因为广东那边的疫情原因,不得不安排大家远程参会。在此,对未能报上名的朋友说声抱歉,对报上名但因为疫情而选择放弃现场参会的朋友道声感谢。不过这次峰会提供了4个直播渠道,全程直播101个报告,并在后续会有视频回放。很抱歉直播中间也遇到了一些技术问题(有些上午调试好了下午还出错),在此再说声抱歉。

特别感谢中科院软件所的吴伟老师和武延军老师,他们为筹备这次峰会而殚精竭虑,真的付出太多了。感谢上海科技大学信息学院周平强院长,协调上海本地各方资源,全力支持和保障峰会的顺利召开。也要感谢所有为峰会默默付出的筹备组成员和志愿者们!

大家因RISC-V而汇聚到一起,正是因为RISC-V所带来的开源、开放、共享、共治是大家的共识,也是因为RISC-V为我们带来了无限的想象空间。如今,RISC-V已在中国蓬勃发展,中国也在RISC-V生态中贡献越来越多的力量——首届RISC-V中国峰会就是最好的写照。

阿里腾讯面试梳理&个人成长经历分享

Posted: 03 Apr 2021 11:35 PM PDT

前言

好久没有更新了,最近忙着面试,写毕业设计和论文。

不过不想停下记笔记的习惯,所以偷偷的发面经,然后"惊艳"老铁们。

校招面经,面试难度中等,看官老爷们看个热闹就行。

历经一个月战线,投了阿里和腾讯,具体部门这里不展开了,都是核心部门,提供的舞台很大,至于最后选择去哪一家公司,可以关注文末。

接下来复盘一下这一个月来的面试感受吧。

本文不会泄题,只是根据我的简历,来展开谈一谈,可能会被问到的题目。

最后以下都是个人观点,个人能力有限,如果有错误,希望能纠正我。

简单介绍

2021届毕业生,掘金里面的TianTianUp,双非本科院校(江西师大),没有听错,那个彩礼"天价"的省份。

21届里面,应该算起步比较晚的了。2020年暑假7月份,才开始认认真真"复习"前端,当时连js作用域,闭包,浏览器缓存都不清楚。因为起步晚,所以后续错过了秋招。

现在我才深刻意识到,视野的重要性。

当时大三学了点vue,做了点看起来牛逼的东西,就觉得很牛了,于是春招投了阿里,结果惨不忍睹。

重点当时没有放心上,还是继续玩,继续打游戏。

还好之后觉悟过来,才有了后面网易的故事。

嗯,也导致后续很多环节跟不上其他同届大佬。

秋招的时候,jack,三元同学,林不渡,单哥,承和等大佬都是offer收割机,各种腾讯,快手,字节ssp,无不冲击着我这个萌新。

暗自下决心: 我也要去大厂核心部门,去做有意思的事情。

有了规划,接下来就是看技术博客,动手实践,总结梳理,写博客。

熬了无数的夜,无数个周末在社区徘徊,这个知识点不懂,查文档,看不明白就换一篇文章,争取做到一遍过。

这么坚持了半年,2021年的春招,结果让我满意,但结果还是有些遗憾的。

满意的地方: 借面试机会,跟着大佬交流学习了一波。

遗憾的地方: 其他大厂没有去尝试,没有趁着面试机会去接触其他领域的大佬。我觉得原因可以归咎于:

  • 找不到大厂核心部门的内推,希望去核心部门。
  • 需要笔试,觉得流程很慢,没有人跟进。

所以后面提到了面试机会。

整个春招的过程中,聊一聊心态发生的改变吧,迷茫--紧张--坦然--自信,最后到意向书,到拿到offer。

小tips⬇️

如果你是在校生,推荐你阅读三元同学博客,干货满满。

面试机会

基本上我是靠内推的,腾讯文档,是一位校招大佬帮忙推的,觉得履历还行,给了个面试机会,才勉强给我通过的。真的是勉强给的机会,因为学校确实跟985比起来,差距很大,还好有网易实习经历buff,加上写了点博客笔记等笔记输出,才有这个面试机会。

内推,内推,内推,这是最快,最有效的方式,可以第一时间知道面试结果,面评,以及后续的面试流程。

至于其他的,美团,百度,shopee走的都是官网流程,需要笔试。腾讯这边到四面总监面的时候,我基本上都放弃了。

最好的方式就是找靠谱的内推吧,比如秋风大佬,字节找ssh(昊神),很靠谱的。

好的履历也很重要:

  • 学校985,211。
  • 大厂实习经历,众多offer。
  • 扎实的基础,读过源码,理解框架设计思想。
  • 社区有一定影响力,坚持做博客产出。
  • 开源贡献,开源项目维护者,热爱前端。

基本上,我会把握其中的一两个点,争取做好,才会有后续的面试机会。

有了面试机会后,剩下的就是如何去做好一场面试的准备工作了。

如何准备

这个链接里面总结的特别好,我基本上看它复习的。

https://bitable.feishu.cn/app8Ok6k9qafpMkgyRbfgxeEnet?from=logout&table=tblEnSV2PNAajtWE&view=vewJHSwJVd

不过面完,给我感觉,重在平时的积累,基本上都是围绕项目展开的,之前做的项目得好好准备一下,比如项目难点,项目如何做优化的,项目的亮点。

这里推荐一个链接:

面试被问项目经验不用慌,按这个步骤回答绝对惊艳

https://juejin.cn/post/6844904102795706375

至于其他的部分,更多的是向面试官展示自己的优点,闪光点。

面经心得

基本上都是围绕我的简历来的,有兴趣的话,可以联系我要pdf,可以给你参考下。

![专业技能]个人简历

简历上技能写的不多,基本上能保证的是我写的都能聊上两句。

从我简历部分来看,主要涉及几个点:

  • React部分,这里面可以问的内容也很多。
  • 浏览器相关的。
  • 计算机网络部分。
  • 算法与数据结构,基本上这算是我的一个杀手锏吧。
  • webpack相关的,同时会涉及到babel。

我现在回顾我的面试部分,都是围绕这些点张开的。可能我是应届生校招的原因,很多时候,考察的是我学习能力,理解能力,对前端热爱的程度这些方面。

基于这几点,展开聊聊我面试过程中会注意的点,以及围绕我写的专业技能,会聊到的一些话题。


自我介绍

很重要的一个环节,说不定可以奠定本次面试的节奏,会根据你说的内容,来张开问你,同时也会给给面试官一个缓冲的时间来重新熟悉你的简历

面试自我介绍,要:

遵守极简原则,突出重点,忽略细节。

小参考:

  1. 我是谁。
  2. 我的亮点,可以描述一下几个点,最近的经历。
  3. 我为什么需要这份工作(其实这个我自我介绍都是忽略的)。

围绕上面的点,我会着重突出,我在大学的经历,主要是算法相关的经历,接着就是我在网易实习期间的收获,以及中间会持续更新博客,写总结。

基本上,面试官对我的掘金博客,以及大学搞算法的经历感兴趣,进入了自己擅长的领域,就会轻松很多。

记住一个点,面试是跟人打交道,学会去展示自己,以及及时跟面试官做合理的沟通很重要。

以上是我会注意的一些点,我也是这么去做的,尽量围绕自己的亮点去展开,当然了,有些面试官有自己的风格,可能一上来就是问你项目,那么我们应该如何把握呢。


项目相关

我觉得项目相关的话题,是个挺难的话题,围绕它展开的话,可以延伸出很多话题,我列举了些有意思的:

  • 项目难点。(如何发现问题,解决思路,最后结果)
  • 项目考虑过优化吗,你是如何优化的,思路是什么。
  • 项目的组织架构,你对它的现有架构的理解,哪些优点值得借鉴,哪些缺点需要改进。
  • 如果让你从0到1建一个项目,你考虑的点是什么,有哪些流程需要注意的。
  • 项目中代码规范,你们项目有方案吗,你了解的代码规范有哪些方案。
  • 说一说项目中你们是如何测试的,有哪些单元测试方案,能不能说一说。
  • 项目中引入TS的原因,为什么这么做。

不过对于应届生来说,问项目都是围绕你简历来问的,比如我当时的项目,我觉得没有好的难点,我就着重说明了下,我对项目是如何优化的,优化了哪些点。

整个面试下来,都会问,你项目写到的优化的地方,能不能具体聊一聊。

这个时候,真的要把握机会了,问到你会的点,你得有清晰的思路,可以参考STAR法则。

重新回顾一下 STAR 法则四要素:

  • Situation:事情是在什么情况下发生,基于一个怎样的背景;
  • Task:你是如何明确你的任务的;
  • Action:针对这样的情况分析,你采用了什么行动方式,具体做了哪些工作内容;
  • Result:结果怎样,带来了什么价值,在整个过程中你学到了什么,有什么新的体会。

这里有段话,我觉得说的很不错:

往往大部分同学一上来就直接介绍做了什么以及实现的过程,条理也比较清晰,内容也颇具技术含量。但很多同学很容易忽略了 Situation 和 Result 的部分也就是背景和结果。或者是在面试官进一步了解追问细节的时候容易惊慌失措。这些原因往往都是由于面试前对自己的经历没有将来龙去脉讲清楚以及总结不够全面和深入。

基本上,我对项目这块目前能聊的就是这么多,对了,如果你面大厂核心部门,可能还需要关注的点是,项目的组织架构理解,让你实现一个项目,整个流程需要注意考虑哪些点。

这块真的可以着重准备一下,以我这次春招经验来谈,是重点。

聊完项目,接下来就围绕我简历写到的专业技能,依此来梳理一下。


JS基础

考察的是一些基础问题,这里最好过一遍,切记不要丢大分。

比如我写了对异步编程有理解,所以得看一下,异步编程的解决方案,它的发展过程,每种解决方案的优缺点,得了得,最后我会拓展一下async方案存在的缺陷,对错误的捕获,聊到babel相关的,现有的babel社区是否实现了对async语法做错误捕获处理,以及谈了下,社区里面的这个插件存在的缺陷。

总结的话,我会围绕面试官问的基础点展开,把相关连的部分描述一下, 面试官有兴趣的话,会顺着你的思路往下问。

至于面试过程中问到JS基础相关的题,这里就不列举了。


React相关

第二部分,简历上写了React相关的,那我总结了些可以会问到内容:

  • React框架的原理,聊一聊它是如何执行的。
  • React更新流程,主要经历了哪些步骤。
  • React中setState的理解,需要注意哪些。
  • vue和React区别,如何从项目的选型上区分它们。

React框架的理解,vdom,patch,批量更新,渲染的过程,经历了啥。

你对React源码很感兴趣,可以关注一下卡颂大佬,他的自顶向下学 React 源码值得推荐学习一下。

当然了,React真的要问,太多可以问你的啦:

  • 比如让你手写一个Hoc组件,实现类似怎么样的功能,思路是怎么样的。
  • 比如Hook你对它的理解,当时面试问到了Hook实现原理,还好看平时喜欢折腾,看了下大致流程。
  • React-fiber机制引入,解决了什么问题,具体的流程是怎么样的,数据结构上发生了哪些变化。
  • React中的时间切片了解嘛,如何实现的,流程能不能说一说。

这次问到好几个React相关的问题,更关注的点是你对原理的过程,有关注嘛,大致是一个怎么样的过程,最后又从中学到了哪些点。简历上写看过React原理相关的,得慎重啊!!!

-快进到浏览器部分。


浏览器相关

这也是我简历上专业技能写到的一个点,这里也写了几个点。

  • 输入URL到页面渲染。
  • DNS解析的端口号是多少,为什么采用UDP协议。
  • 你渲染流程知道的这么详细,图层,分层,绘制,栅格化线程池,GPU加速,能不能说一下,对你开发有啥影响呢。(其实这个时候,被无情打脸了,知道这个有什么用,当时反问我)
  • 浏览器缓存。(可以拓展一下,结合webpack如何命中缓存,然后就到你熟悉的webpack领域了)
  • 浏览器进程架构历史。
  • 浏览器内核。(这个可以了解)
  • 浏览器的垃圾回收机制

经典八股文了,不过其实我感觉也是可以拓展的,问到浏览器缓存的时候,结合webpack谈一谈如何命中缓存的。比如讲到浏览器的渲染流程时,我们都知道里面的一些具体流程,比如分层,绘制,栅格化操作,然后其实可以结合实际去思考一下,有哪些具体的用途呢,这里我们需要借助浏览器中的performance来看看。

image

里面有很多关键性指标,比如 Long Task执行的细节,跟面试官交流了下,排查性能问题,我是如何通过这个Performance相关的细节去把握的,当然了,里面的内存使用情况,是否出现内存泄露问题,都可以借助它。

非要我总结的话,我觉得哪怕是八股文,我们也要尽可能的展示出亮点来。

最近也在学习性能优化相关的部分,发现Performance相关的部分,还是有很多地方可以学习的,可以持续关注我噢~


webpack相关

基本上,看我简历上写了这部分的,会问到,部分问题:

  • webpack打包原理。
  • import最终被webpack编译打包成什么。
  • 路由懒加载的原理是啥,能不能结合webpack说一说。
  • 写过webpack loader 或者插件吗。
  • webpack 如何做性能优化。
  • babel的原理了解吗,babel是用来干嘛的。
  • 说一说常见的babel库,你有使用过哪些。
  • 写过 babel 插件吗?用来干啥的?怎么写的 babel 插件
  • 知道怎么转化成 AST 的吗?

基本上考察,你是否实践过,因为自己简历上写了这方面的,可能问的比较多。

比如问到babel原理可以这么答:

babel的转译过程分为三个阶段:parsing、transforming、generating,以ES6代码转译为ES5代码为例,babel转译的具体过程如下:

  1. ES6代码输入
  2. babylon 进行解析得到 AST
  3. plugin 用 babel-traverse 对 AST 树进行遍历转译,得到新的AST树
  4. 用 babel-generator 通过 AST 树生成 ES5 代码

基本上至少得知道它大致的原理,平时如果有积累的话,其实问题不大。

比如babel的话,推荐可以看看babel是如何处理async错误捕获转换的,这个有点意思,正好借这个机会跟面试官交流一下这块的知识点,个人觉得还是很有意思。

学的时候,多折腾一下,应该就问题不大了。


算法与数据结构相关

其实这块,我希望面试官问我多一些,因为大学期间,更多的精力放在算法。有幸碰到过面试官也是打过ICPC,CCPC等比赛的,曾经都是热血青年,最后因为生活,选择了向生活低头。

聊得过程还是很愉快的~ 也是第一次,跟面试官聊到了,线段树,主席树,最小费用流,二分图匹配(匈牙利)算法,差分约束,Graham扫描法,有限状态自动机,AC自动机。

似乎找回了大二的那份对算法对热情。

记得退役的时候,在QQ个签上,写过这么一句话:我永远热爱算法,无论它虐我千百遍。

可能是真的熬了无数的夜,才会有这样子的感慨吧。有点跑题了,感兴趣的话,后续会更新,我是如何走上算法这条路的,最后给我的成长收获又是哪些。

回到正题,至于这部分的话,我觉得没有一个很明确的范围吧,毕竟算法太广了,过一遍吧。

  • 有向图判断是否有环。
  • 给定一个数组,如何拆分,保证两个数组的和接近。(想到的是贪心,正解应该是dp)
  • Json.parse()实现。
  • 常见的数据结构。
  • 数组和链表区别。
  • 时间复杂度和空间复杂度计算。
  • 排序算法
  • 你是如何看待算法的,给你的收获是什么。

算法很重要的,对你未来的发展,我觉得还是很有必要去学习的。至于怎么学,我没有能力回答这个问题,要我给建议的话,看书是一个选择,如果要临时应对面试,刷一刷leetcode来说,只能说短期有效果。

个人建议:

可以结合书的内容,分专题去刷,比如动态规划dp专题很难,我记得我当时看完书后,对它的理解就很清晰了,当然做题是另外一回事,通过题目不断巩固你对一个算法知识点的理解,我觉得是没有问题的,但是至于完完全全刷leetcode,应付面试,不从最基础的开始,这样子"超车",觉得意义不大。

以上是我个人理解,能力有限,说的不对,您就当看个笑话。


个人经历

问到这部分的话,可能是总监面你的时候会问,也可能是hr会问,到hr环节的话,基本十拿九稳了。不过也不能大意,说不定一不留神,卡你学历啥的,一个月的面试付诸东流,还是要认真对待的,罗列了几个点:

  • 整个经历自我介绍,越详细越好,什么时候接触计算机,什么时候接触前端。
  • 整个经历中,你认为最值得骄傲的事情,最难的事情是什么。
  • 什么事情让你自豪,什么事情让你有挫败感。
  • 未来的发展,自己的规划。
  • 为什么现在才来秋招,不选择秋招。

拿我来说,我双非本科,最担心的就是卡学历。当时hr明确问了我,你对现在的学校满意嘛,师范类的院校,怎么选择走软件工程呀,诸如此类的问题很多,无不困扰着我。

不过我也不搞虚的。实话实话,为什么高中成绩差,又为什么选择软件工程,争取做到坦诚相待的态度,表达出自己最真实的想法吧。


小结

看到这里的话,可能对你来说,没有具体说明白,哪些知识点会考,哪些不会考察。

我面腾讯和阿里的时候,也没有看到我这个部门具体的面试题。

我觉得更重要的是,把握好专业技能和项目经历的表达,引导面试官去往你所知道的领域交流问题,最后在这个领域拿出自己的杀手锏,这样子面试官会更有好感,接下来的交流会更加愉快。

对简历感兴趣的话,可以参考下:

个人简历
需要获取简历模板pdf的话,私信我(文末有联系方式),或者公众号回复,个人简历,即可获取。

最后

作为一个即将毕业的我来说,第一份工作很重要。在我面临选择的时候,慎重考虑以及找前辈交流过后,最后选择了腾讯

在即将毕业的最后阶段,我应该算交上了一份满意的答卷,未来的路还很长,让我们拭目以待吧!

有想交流的问题,欢迎联系我⬇️

微信:DayDay2021

最后做一个简单,自由,有爱的技术人吧。

2021年,让我们手写一个mini版本的vue2.x和vue3.x框架

Posted: 24 Jun 2021 07:15 AM PDT

mini版本的vue.js2.X版本框架

模板代码

首先我们看一下我们要实现的模板代码:

<div id="app">     <h3>{{ msg }}</h3>     <p>{{ count }}</p>     <h1>v-text</h1>     <p v-text="msg"></p>     <input type="text" v-model="count">     <button type="button" v-on:click="increase">add+</button>     <button type="button" v-on:click="changeMessage">change message!</button>     <button type="button" v-on:click="recoverMessage">recoverMessage!</button> </div>

逻辑代码

然后就是我们要编写的javascript代码。

const app = new miniVue({     el:"#app",     data:{         msg:"hello,mini vue.js",         count:666     },     methods:{         increase(){             this.count++;         },         changeMessage(){             this.msg = "hello,eveningwater!";         },         recoverMessage(){             console.log(this)             this.msg = "hello,mini vue.js";         }     } });

运行效果

我们来看一下实际运行效果如下所示:

思考一下,我们要实现如上的功能应该怎么做呢?你也可以单独打开以上示例:

点击此处

源码实现-2.x

miniVue类

首先,不管三七二十一,既然是实例化一个mini-vue,那么我们先定义一个类,并且它的参数一定是一个属性配置对象。如下:

 class miniVue {      constructor(options = {}){          //后续要做的事情      }  }

现在,让我们先初始化一些属性,比如data,methods,options等等。

//在miniVue构造函数的内部 //保存根元素,能简便就尽量简便,不考虑数组情况 this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el; this.$methods = options.methods; this.$data = options.data; this.$options = options;

初始化完了之后,我们再来思考一个问题,我们是不是可以通过在vue内部使用this访问到vue定义的数据对象呢?那么我们应该如何实现这一个功能呢?这个功能有一个专业的名词,叫做代理(proxy)

代理数据

因此我们来实现一下这个功能,很明显在这个miniVue类的内部定义一个proxy方法。如下:

//this.$data.xxx -> this.xxx; //proxy代理实例上的data对象 proxy(data){     //后续代码 }

接下来,我们需要知道一个api,即Object.defineProperty,通过这个方法来完成这个代理方法。如下:

//proxy方法内部 // 因为我们是代理每一个属性,所以我们需要将所有属性拿到 Object.keys(data).forEach(key => {     Object.defineProperty(this,key,{         enumerable:true,         configurable:true,         get:() => {             return data[key];         },         set:(newValue){             //这里我们需要判断一下如果值没有做改变就不用赋值,需要排除NaN的情况             if(newValue === data[key] || _isNaN(newValue,data[key]))return;             data[key] = newValue;         }     }) })

接下来,我们来看一下这个_isNaN工具方法的实现,如下:

function _isNaN(a,b){     return Number.isNaN(a) && Number.isNaN(b); }

定义好了之后,我们只需要在miniVue类的构造函数中调用一次即可。如下:

// 构造函数内部 this.proxy(this.$data);

代理就这样完成了,让我们继续下一步。

数据响应式观察者observer类

我们需要对数据的每一个属性都定义一个响应式对象,用来监听数据的改变,所以我们需要一个类来管理它,我们就给它取个名字叫Observer。如下:

class Observer {     constructor(data){         //后续实现     } }

我们需要给每一个数据都添加响应式对象,并且转换成getter和setter函数,这里我们又用到了Object.defineProperty方法,我们需要在getter函数中收集依赖,在setter函数中发送通知,用来通知依赖进行更新。我们用一个方法来专门去执行定义响应式对象的方法,叫walk,如下:

//再次申明,不考虑数组,只考虑对象 walk(data){     if(typeof data !== 'object' || !data)return;     // 数据的每一个属性都调用定义响应式对象的方法     Object.keys(data).forEach(key => this.defineReactive(data,key,data[key])); }

接下来我们来看defineReactive方法的实现,同样也是使用Object.defineProperty方法来定义响应式对象,如下所示:

defineReactive(data,key,value){     // 获取当前this,以避免后续用vm的时候,this指向不对     const vm = this;     // 递归调用walk方法,因为对象里面还有可能是对象     this.walk(value);     //实例化收集依赖的类     let dep = new Dep();     Object.defineProperty(data,key,{         enumerable:true,         configurable:true,         get(){             // 收集依赖,依赖存在Dep类上             Dep.target && Dep.add(Dep.target);             return value;         },         set(newValue){             // 这里也判断一下             if(newValue === value || __isNaN(value,newValue))return;             // 否则改变值             value = newValue;             // newValue也有可能是对象,所以递归             vm.walk(newValue);             // 通知Dep类             dep.notify();         }     }) }

Observer类完成了之后,我们需要在miniVue类的构造函数中实例化一下它,如下:

//在miniVue构造函数内部 new Observer(this.$data);

好的,让我们继续下一步。

依赖类

defineReactive方法内部用到了Dep类,接下来,我们来定义这个类。如下:

class Dep {     constructor(){         //后续代码     } }

接下来,我们来思考一下,依赖类里面,我们需要做什么,首先根据defineReactive中,我们很明显就知道会有add方法和notify方法,并且我们需要一种数据结构来存储依赖,vue源码用的是队列,而在这里为了简单化,我们使用ES6的set数据结构。如下:

//构造函数内部 this.deps = new Set();

接下来,就需要实现add方法和notify方法,事实上这里还会有删除依赖的方法,但是这里为了最简便,我们只需要一个addnotify方法即可。如下:

add(dep){     //判断dep是否存在并且是否存在update方法,然后添加到存储的依赖数据结构中     if(dep && dep.update)this.deps.add(dep); } notify(){     // 发布通知无非是遍历一道dep,然后调用每一个dep的update方法,使得每一个依赖都会进行更新     this.deps.forEach(dep => dep.update()) }

Dep类算是完了,接下来我们就需要另一个类。

Watcher类

那就是为了管理每一个组件实例的类,确保每个组件实例可以由这个类来发送视图更新以及状态流转的操作。这个类,我们把它叫做Watcher

class Watcher {     //3个参数,当前组件实例vm,state也就是数据以及一个回调函数,或者叫处理器     constructor(vm,key,cb){         //后续代码     } }

再次思考一下,我们的Watcher类需要做哪些事情呢?我们先来思考一下Watcher的用法,我们是不是会像如下这样来写:

//3个参数,当前组件实例vm,state也就是数据以及一个回调函数,或者叫处理器 new Watcher(vm,key,cb);

ok,知道了使用方式之后,我们就可以在构造函数内部初始化一些东西了。如下:

//构造函数内部 this.vm = vm; this.key = key; this.cb = cb; //依赖类 Dep.target = this; // 我们用一个变量来存储旧值,也就是未变更之前的值 this.__old = vm[key]; Dep.target = null;

然后Watcher类就多了一个update方法,接下来让我们来看一下这个方法的实现吧。如下:

update(){     //获取新的值     let newValue = this.vm[this.key];     //与旧值做比较,如果没有改变就无需执行下一步     if(newValue === this.__old || __isNaN(newValue,this.__old))return;     //把新的值回调出去     this.cb(newValue);     //执行完之后,需要更新一下旧值的存储     this.__old = newValue; }

编译类compiler类

初始化

到了这一步,我们就算是完全脱离vue源码了,因为vue源码的编译十分复杂,涉及到diff算法以及虚拟节点vNode,而我们这里致力于将其最简化,所以单独写一个Compiler类来编译。如下:

class Compiler {     constructor(vm){         //后续代码     } }
注意:这里的编译是我们自己根据流程来实现的,与vue源码并没有任何关联,vue也有compiler,但是与我们实现的完全不同。

定义好了之后,我们在miniVue类的构造函数中实例化一下这个编译类即可。如下:

//在miniVue构造函数内部 new Compiler(this);

好的,我们也看到了使用方式,所以接下来我们来完善这个编译类的构造函数内部的一些初始化操作。如下:

//编译类构造函数内部 //根元素 this.el = vm.$el; //事件方法 this.methods = vm.$methods; //当前组件实例 this.vm = vm; //调用编译函数开始编译 this.compile(vm.$el);

compile方法

初始化操作算是完成了,接下来我们来看compile方法的内部。思考一下,在这个方法的内部,我们是不是需要拿到所有的节点,然后对比是文本还是元素节点去分别进行编译呢?如下:

compile(el){     //拿到所有子节点(包含文本节点)     let childNodes = el.childNodes;     //转成数组     Array.from(childNodes).forEach(node => {         //判断是文本节点还是元素节点分别执行不同的编译方法         if(this.isTextNode(node)){             this.compileText(node);         }else if(this.isElementNode(node)){             this.compileElement(node);         }         //递归判断node下是否还含有子节点,如果有的话继续编译         if(node.childNodes && node.childNodes.length)this.compile(node);     }) }

这里,我们需要2个辅助方法,判断是文本节点还是元素节点,其实我们可以使用节点的nodeType属性来进行判断,由于文本节点的nodeType值为3,而元素节点的nodeType值为1。所以这2个辅助方法我们就可以实现如下:

isTextNode(node){     return node.nodeType === 3; } isElementNode(node){     return node.nodeType === 3; }

编译文本节点

接下来,我们下来看compileText编译文本节点的方法。如下:

//{{ count }}数据结构是类似如此的 compileText(node){     //后续代码 }

接下来,让我们思考一下,我们编译文本节点,无非就是把文本节点中的{{ count }}映射成为0,而文本节点不就是node.textContent属性吗?所以此时我们可以想到根据正则来匹配{{}}中的count值,然后对应替换成数据中的count值,然后我们再调用一次Watcher类,如果更新了,就再次更改这个node.textContent的值。如下:

compileText(node){     //定义正则,匹配{{}}中的count     let reg = /\{\{(.+?)\}\}/g;     let value = node.textContent;     //判断是否含有{{}}     if(reg.test(value)){         //拿到{{}}中的count,由于我们是匹配一个捕获组,所以我们可以根据RegExp类的$1属性来获取这个count         let key = RegExp.$1.trim();         node.textContent = value.replace(reg,this.vm[key]);         //如果更新了值,还要做更改         new Watcher(this.vm,key,newValue => {             node.textContent = newValue;         })     } }

编译文本节点到此为止了,接下来我们来看编译元素节点的方法。

编译元素节点

指令

首先,让我们想一下,我们编译元素节点无非是想要根据元素节点上的指令来分别执行不同的操作,所以我们编译元素节点就只需要判断是否含有相关指令即可,这里我们只考虑了v-text,v-model,v-on:click这三个指令。让我们来看看compileElement方法吧。

compileElement(node){     //指令不就是一堆属性吗,所以我们只需要获取属性即可     const attrs = node.attributes;     if(attrs.length){         Array.from(attrs).forEach(attr => {             //这里由于我们拿到的attributes可能包含不是指令的属性,所以我们需要先做一次判断             if(this.isDirective(attr)){                 //根据v-来截取一下后缀属性名,例如v-on:click,subStr(5)即可截取到click,v-text与v-model则subStr(2)截取到text和model即可                 let attrName = attr.indexOf(':') > -1 ? attr.subStr(5) : attr.subStr(2);                 let key = attr.value;                 //单独定义一个update方法来区分这些                 this.update(node,attrName,key,this.vm[key]);             }         })     } }

这里又涉及到了一个isDirective辅助方法,我们可以使用startsWith方法,判断是否含有v-值即可认定这个属性就是一个指令。如下:

isDirective(dir){     return dir.startsWith('v-'); }

接下来,我们来看最后的update方法。如下:

update(node,attrName,key,value){     //后续代码 }

最后,让我们来思考一下,我们update里面需要做什么。很显然,我们是不是需要判断是哪种指令来执行不同的操作?如下:

//update函数内部 if(attrName === 'text'){     //执行v-text的操作 }else if(attrName === 'model'){     //执行v-model的操作 }else if(attrName === 'click'){     //执行v-on:click的操作 }

v-text指令

好的,我们知道,根据前面的编译文本元素节点的方法,我们就知道这个指令的用法同前面编译文本元素节点。所以这个判断里面就好写了,如下:

//attrName === 'text'内部 node.textContent = value; new Watcher(this.vm,key,newValue => {     node.textContent = newValue; })

v-model指令

v-model指令实现的是双向绑定,我们都知道双向绑定是更改输入框的value值,并且通过监听input事件来实现。所以这个判断,我们也很好写了,如下:

//attrName === 'model'内部 node.value = value; new Watcher(this.vm,key,newValue => {     node.value = newValue; }); node.addEventListener('input',(e) => {     this.vm[key] = node.value; })

v-on:click指令

v-on:click指令就是将事件绑定到methods内定义的函数,为了确保this指向当前组件实例,我们需要通过bind方法改变一下this指向。如下:

//attrName === 'click'内部 node.addEventListener(attrName,this.methods[key].bind(this.vm));

到此为止,我们一个mini版本的vue2.x就算是实现了。继续下一节,学习vue3.x版本的mini实现吧。

mini版本的vue.js3.x框架

模板代码

首先我们看一下我们要实现的模板代码:

<div id="app"></div>

逻辑代码

然后就是我们要编写的javascript代码。

const App = {     $data:null,     setup(){         let count = ref(0);         let time = reactive({ second:0 });         let com = computed(() => `${ count.value + time.second }`);         setInterval(() => {             time.second++;         },1000);         setInterval(() => {             count.value++;         },2000);         return {             count,             time,             com         }     },     render(){         return `             <h1>How reactive?</h1>             <p>this is reactive work:${ this.$data.time.second }</p>             <p>this is ref work:${ this.$data.count.value }</p>             <p>this is computed work:${ this.$data.com.value  }</p>         `     } } mount(App,document.querySelector("#app"));

运行效果

我们来看一下实际运行效果如下所示:

思考一下,我们要实现如上的功能应该怎么做呢?你也可以单独打开以上示例:

点击此处

源码实现-3.x

与vue2.x做比较

事实上,vue3.x的实现思想与vue2.x差不多,只不过vue3.x的实现方式有些不同,在vue3.x,把收集依赖的方法称作是副作用effect。vue3.x更像是函数式编程了,每一个功能都是一个函数,比如定义响应式对象,那就是reactive方法,再比如computed,同样的也是computed方法...废话不多说,让我们来看一下吧!

reactive方法

首先,我们来看一下vue3.x的响应式方法,在这里,我们仍然只考虑处理对象。如下:

function reactive(data){     if(!isObject(data))return;     //后续代码 }

接下来我们需要使用到es6的proxyAPI,我们需要熟悉这个API的用法,如果不熟悉,请点击此处查看。

我们还是在getter中收集依赖,setter中触发依赖,收集依赖与触发依赖,我们都分别定义为2个方法,即track和trigger方法。如下:

function reactive(data){     if(!isObject(data))return;     return new Proxy(data,{         get(target,key,receiver){             //反射api             const ret = Reflect.get(target,key,receiver);             //收集依赖             track(target,key);             return isObject(ret) ? reactive(ret) : ret;         },         set(target,key,val,receiver){             Reflect.set(target,key,val,receiver);             //触发依赖方法             trigger(target,key);             return true;         },         deleteProperty(target,key,receiver){             const ret = Reflect.deleteProperty(target,key,receiver);             trigger(target,key);             return ret;         }     }) }

track方法

track方法就是用来收集依赖的。我们用es6的weakMap数据结构来存储依赖,然后为了简便化用一个全局变量来表示依赖。如下:

//全局变量表示依赖 let activeEffect; //存储依赖的数据结构 let targetMap = new WeakMap(); //每一个依赖又是一个map结构,每一个map存储一个副作用函数即effect函数 function track(target,key){     //拿到依赖     let depsMap = targetMap.get(target);     // 如果依赖不存在则初始化     if(!depsMap)targetMap.set(target,(depsMap = new Map()));     //拿到具体的依赖,是一个set结构     let dep = depsMap.get(key);     if(!dep)depsMap.set(key,(dep = new Set()));     //如果没有依赖,则存储再set数据结构中     if(!dep.has(activeEffect))dep.add(activeEffect) }

收集依赖就这么简单,需要注意的是,这里涉及到了es6的三种数据结构即WeakMap,Map,Set。下一步我们就来看如何触发依赖。

trigger方法

trigger方法很明显就是拿出所有依赖,每一个依赖就是一个副作用函数,所以直接调用即可。

function trigger(target,key){     const depsMap = targetMap.get(target);     //存储依赖的数据结构都拿不到,则代表没有依赖,直接返回     if(!depsMap)return;     depsMap.get(key).forEach(effect => effect && effect()); }

接下来,我们来实现一下这个副作用函数,也即effect。

effect方法

副作用函数的作用也很简单,就是执行每一个回调函数。所以该方法有2个参数,第一个是回调函数,第二个则是一个配置对象。如下:

function effect(handler,options = {}){     const __effect = function(...args){         activeEffect = __effect;         return fn(...args);     }     //配置对象有一个lazy属性,用于computed计算属性的实现,因为计算属性是懒加载的,也就是延迟执行     //也就是说如果不是一个计算属性的回调函数,则立即执行副作用函数     if(!options.lazy){         __effect();     }     return __effect; }

副作用函数就是如此简单的实现了,接下来我们来看一下computed的实现。

computed的实现

既然谈到了计算属性,所以我们就定义了一个computed函数。我们来看一下:

function computed(handler){     // 只考虑函数的情况     // 延迟计算 const c = computed(() => `${ count.value}!`)     let _computed;     //可以看到computed就是一个添加了lazy为true的配置对象的副作用函数     const run = effect(handler,{ lazy:true });     _computed = {         //get 访问器         get value(){             return run();         }     }     return _computed; }

到此为止,vue3.x的响应式算是基本实现了,接下来要实现vue3.x的mount以及compile。还有一点,我们以上只是处理了引用类型的响应式,但实际上vue3.x还提供了一个ref方法用来处理基本类型的响应式。因此,我们仍然可以实现基本类型的响应式。

ref方法

那么,我们应该如何来实现基本类型的响应式呢?试想一下,为什么vue3.x中定义基本类型,如果修改值,需要修改xxx.value来完成。如下:

const count = ref(0); //修改 count.value = 1;

从以上代码,我们不难得出基本类型的封装原理,实际上就是将基本类型包装成一个对象。因此,我们很快可以写出如下代码:

function ref(target){     let value = target;     const obj = {         get value(){             //收集依赖             track(obj,'value');             return value;         },         set value(newValue){             if(value === newValue)return;             value = newValue;             //触发依赖             trigger(obj,'value');         }     }     return obj; }

这就是基本类型的响应式实现原理,接下来我们来看一下mount方法的实现。

mount方法

mount方法实现挂载,而我们的副作用函数就是在这里执行。它有2个参数,第一个参数即一个vue组件,第二个参数则是挂载的DOM根元素。所以,我们可以很快写出以下代码:

function mount(instance,el){     effect(function(){         instance.$data && update(instance,el);     });     //setup返回的数据就是实例上的数据     instance.$data = instance.setup();     //这里的update实际上就是编译函数     update(instance,el); }

这样就是实现了一个简单的挂载,接下来我们来看一下编译函数的实现。

update编译函数

这里为了简便化,我们实现的编译函数就比较简单,直接就将定义在组件上的render函数给赋值给根元素的innerHTML。如下:

//这是最简单的编译函数 function update(instance,el){     el.innerHTML = instance.render(); }

如此一来,一个简单的mini-vue3.x就这样实现了,怎么样,不到100行代码就搞定了,还是比较简单的。

详细文档收录网站

我陪伴一起思否成长的日子  | 思否的九周年

Posted: 24 Jun 2021 02:56 AM PDT

思否文化衫

故事的开始

  • 2019年3月18日,一个平淡无奇的日子,我通过一位朋友得知了segmentFault社区的存在,故事就开始了

初见思否

  • 那个时候的思否,UI还没有改版,进来之后是一个列表,UI风格一下就把我给吸引住了,我当时想:哇,这个风格我喜欢,小清新风格,绿绿的,我马上就要注册+收藏网站!
  • 陆陆续续的看了一些文章以后,我想着,要不我写一点?刚好那个时候技术比较差,需要写一些自己的实践来记录那些踩过的坑,方便自己用来找工作,于是就陆陆续续写了一些现在看来比较差劲的文章,但是好在,写了以后没人碰我,反倒是有一些比我还小白的小白,会在下面评论鼓励我。这也坚定了我继续写作的信心
  • 写文章的日子持续了一个多月,不温不火,但是这个时间对我来说,我觉得是人生中至关重要的一个节点,因为这个节点我刚好入职了一家深圳IM公司,技术氛围很好,那家公司的CEO COO CTO,都非常棒,对于我一个小白来说,能做到这么复杂的项目,又有这么好的环境,所以造就了我的野蛮生长,我那段时间,简直没日没夜的研究技术,就是为了想把产品做好一点,技术原理更深入了解一些,经过那段日子的沉淀,我觉得,我可以适当的写一些应用层+原理结合的文章了
  • 例如下面这些,单页面应用原理,私有协议,手写react框架等,这些都是跟我的工作息息相关,因为我的工作对性能要求太高了:
    image.png
  • 接下来,我遇到了更大的挑战,项目的难度远超我的想象,对于一些业务划分边界不够清晰,以及大家技术层次不齐,导致项目做得不太理想,我想,这个时候可能我需要更深入的去了解一些技术了,于是,在学习的过程中,又写了下面这些文章,大家可以看到,之前我有手写react,通信协议,单页面原理解析等,现在是偏向基础,webpack构建原理,keep-alive等,甚至涉及到一些架构方面的内容

image.png

这段日子,给我养成了一个好习惯,遇到工作上不懂的点,我一定会去了解他的原理,为什么会这样,所以这么久的沉淀,我可以这样说,现在公司的SAAS平台出了问题,我只要看一眼就大概什么问题。不管遇到什么问题,只要是前端相关的问题,我一定会有合理的解决方案,对自己的技术非常自信。(当然有时候也要去求助,找大佬提问)
  • 在这里,我也是提倡大家写总结文章,这样能让别人避免出现这种问题,也能沉淀自己,关键是沉淀自己解决问题的思路,在一些复杂的框架源码里面穿梭,你会发现他们大都差不多。好的人和事物都有共性,但是差的人和事情总是千奇百怪。

首次爆发

  • 第一次爆发,应该是下面这篇文章,我想着投稿一下infoQ,没想到成功了。在思否也受到了挺大的反响
    image.png
  • 要感谢思否,真的是不吝啬流量,推荐不说,只要写得好,官方公众号都一起转发文章。(这也是我喜欢思否的一个点,就是带着互相成就的思想来对待创作者,现在我粉丝多了以后,微信上经常有不认识的人问我,这个事该怎么做?问完了如果有结果不说话跑了,没有结果把我删了。我第一反应,就是这个人自私,想不劳而获

     这一次投稿infoQ成功,在思否也成功受到了不少人的喜欢,极大的增强了我的信心,我觉得这个时候,应该开始做一个自己的公众号了,于是模仿某个大号,叫:前端巅峰,开启我的自媒体之路

渐入佳境

  • 接下来,我疯了一样的原创,一年写了将近100篇文章,在思否和公众号发布。同时收获了很多粉丝,这些都是让我坚持原创写文章的动力,对我自己的提升也非常大。(所以持续做一个事,真的会有意想不到的收获)
  • 接下来,我收获了思否的年度TOP Writer,也就收获了上面那些文化衫,我个人非常喜欢穿,特别是回湖南老家,非常暖,我现在还会让我老婆穿,不过因为目前在深圳,只会穿那些短袖文化衫。
  • 在去年,思否来深圳举行了一次创作者聚餐,思否CEO高阳和运营负责人波波都到场了,这让我更喜欢思否了,极度开放的思想文化,为开发者谋福利,为创作者谋福利。至今,我也践行思否这两个字的含义:独立思考,敢于否定

    在这段日子,我积累了将近50万阅读量,接近3万的粉丝量,一次偶然的机会,有了跟华为云合作的机会,我作为思否优秀创作者代表,与华为云合作,让我在华为云上实践了一把CI、CD

image.png

 如影随形

  • 思否近期又上线了点评功能,我也成为了产品体验官,只要是思否的小活动,我都会积极参与,因为大家可以看到,我的成长,离不开思否社区,离不开思否社区的小姐姐,高阳和波波,还有你们这些在社区的共建者。
  • 想着随便写点吧,突然又写了不少,可能是这两年真的在社区得到了很多的成长,感谢思否社区,以后的日子如影随形,共同成长。

如何平衡兴趣与收入 —— 听尤雨溪访谈有感

Posted: 23 Jun 2021 07:05 PM PDT

README是一款Github推出的访谈节目,每期会采访一位开源大佬,挖掘开源项目背后的故事。

采访尤雨溪的这期标题是如何从一个想法发展成整个JS社区生态

主要讲述了尤雨溪的成长历程,Vue的诞生过程以及发展。

这次访谈最让我印象深刻的是:尤雨溪在人生的几个关键节点上是如何做出选择的。

一句话总结:尤从求学到工作的每个关键节点,都是平衡兴趣收益后的利益最大化选择。

如果你正面临选择或身处迷茫,这篇文章或许可以为你提供一些有趣的视角。

大佬也是被爸"逼"的

尤高中毕业就赴美求学,本科就读于Colgate University,毕业时获得艺术史学位

曾几何时,他也想将艺术作为职业方向。但是父亲指出了一个很现实的问题:

作为外国人,在艺术领域你很难找到提供工作签证的工作

就这个问题他与父亲发生了激烈的争吵,但最终不得不承认父亲是对的。

于是,硕士阶段,尤选择了ParsonsMFA(结合设计与软件开发的专业),并在就读期间自学了JS

这是其结合兴趣收益做出的第一个重要抉择,完成了从冷门的艺术专业向软件领域的靠拢。

错位竞争

作为一个本科、硕士都是艺术相关专业,会编程的学生。在面临毕业时,如何才能找到好工作?

换做是你,会怎么做?思考十秒。

如果你能想到的,只是刷面经,投简历。那么以下内容值得你好好思考一下。

当时市面上正流行一款以流畅交互体验著称的TODO list APP —— clear

他的丝滑交互体验,使其在短时间内收获大量关注。

尤在看到这款App时首先想到:我能不能将这种交互体验迁移到Web上?

在研究了几天touch事件、CSS transform后,他成功了。

注意接下来他做的事:

我制作了一个视频并发布到网上,大家为Web也能实现如此流畅的体验感到兴奋。最终,我的作品登上了Hacker News头条

不管是应届生还是在职求职,很多低学历非本专业的朋友都在抱怨:大公司学历歧视、专业歧视。

但是,站在公司的视角,每天这么多人投简历,如何区分能力强弱?

显然学历专业工作年限是最简单粗暴的筛选方式。

但如果你能以某种方式证明自己的能力,并广而告之,那么在求职时就能跳出简单粗暴的筛选方式。

这也是为什么很多人升职、求职、创业前会写书的原因。

君不见,玖五(阿里最年轻的前端技术专家)只有中专学历,但他的另一个身份是《深入浅出Vue.js》作者。

探索兴趣导向的人生

说回主题,凭借错位竞争,尤成功获得大公司关注,并在毕业后入职Google Creative Lab

在这一时期,尤的工作主要是做各种试验性web项目原型的探索。

随着在大公司的新鲜感减退,尤逐渐发现这份工作的局限性:只做项目原型,就无法参与项目的生产落地。

不面向终端用户的产品,始终是空中楼阁。

于是,尤决定独立开发一个项目。

这个项目的契机是:为了快速开发原型,需要提效的框架工具。

彼时Angular1已经发布,但对他这样的非计算机背景的开发者来说Angular1太重了。

于是,一款以简单好用为目标的视图层框架被构思出来。几经辗转,最终命名为Vue

艺术计算机的跨度,再到入职Google Creative Lab兴趣导向加上错位竞争的理念为尤不断带来正反馈。

很自然的,尤开始思考:我可不可以全职从事Vue的开发?

理想主义实干者

很多人都有不上班,全职做自己感兴趣的事的美好愿望。

尤与他们不同的是,尤认真评估了可行性后,做出了实际的努力。

摆在面前的困难起码有三条:

  • 能否适应全职做开源的生活节奏
  • 全职开源能否养活自己
  • 如果失败了能否重回大公司

让我们看看尤是如何步步规划,最终解决这三个问题的。

能否适应全职做开源的生活节奏

从谷歌离职后,尤没有立刻全职开发Vue,而是加入一家创业公司Meteor全职远程工作。

做远程工作不仅能提前适应全职搞开源的节奏(在家办公),也能为Vue在社区的发酵提供缓冲期。

正是在这一期间,VueTaylor OtwellLaravel作者)所知,并推广到Laravel社区。

Taylor Otwell

全职开源能否养活自己

收入是最现实的问题。

当通过Vue的开发积累了名气与社区生态后,尤尝试通过Patreon为自己募资。

Patreon是一家为艺术家和创作者募资的公司,其理论基础来源于Kevin Kelly1000粉丝理论

从事创作和艺术工作的人,如作家、摄影师只要能获得1000忠实粉丝就能维持生活。这1000位粉丝是那种认可你价值观,被你的内容吸引,愿意为你做口碑传播和知识付费的,你要做的就是找到、维护好他们

与此同时,尤还从一个朋友公司的开源基金会获得捐赠。

当他决定全职开发Vue时,他已经可以每月从Patreon和基金会募捐中收入3-4k刀。

如果失败了能否重回大公司

当以上两个问题都解决后,第三个问题也不攻自破 —— 如果你能独立开发如此成功的项目(Vue),还愁找不到工作?

启示

当我知道尤雨溪时,他已经是大神了。

这就给我一种幻觉:大神一直是大神。我之所以不能全职做感兴趣的事,是因为我不是大神。

然而,剖析他的成长经历,我们看到的是一个有冒险精神的理想主义实干者,一个头脑清醒、规划清晰的普通人。

我始终觉得《The Pursuit of Happyness》翻译为《当幸福来敲门》并不妥当。

幸福不会自己来敲门,幸福需要审慎规划、大胆求证。

在此过程中,还需要平衡兴趣收益、需要有错位竞争的意识、需要一点点与众不同的小勇气,需要有失败后的Plan B

开源项目的 5 年长跑,runc v1.0 终于正式发布!

Posted: 22 Jun 2021 07:47 PM PDT

本文我来分享下与我们(搞容器化/K8S 从业者)息息相关的一个基础项目 runc 是如何自 2016 年发布了 v1.0.0-rc1 到现在历经 5 年长跑,从 rc1 一直到 rc95 ,如今终于正式发布 v1.0 版本的过程,及这中间的故事。

大家好,我是张晋涛。

在 2018 年 11 月底时,我写了一篇文章 《runc 1.0-rc6 发布之际》 , 那应该是我第一次公开介绍 runc。如果你还不了解 runc 是什么,以及如何使用它,请参考我那篇文章。本文中不再对其概念和用法等进行说明。

在 2019 年 3 月底时,我写了另一篇文章 《runc 1.0-rc7 发布之际》,介绍 runc 1.0-rc7 发布的原因,及那个版本中最主要的修复 CVE-2019-5736 。其中也介绍了关于 runc/Docker 等对于 Linux 内核兼容性的问题,感兴趣的小伙伴可以看看。

关注我的朋友们,应该也在 K8S 生态周报 中多次看到过我对 runc 的介绍,包括其特性及安全漏洞等方面。

在 2015 年 6 月, Docker ,CoreOS 和其他一些公司共同成立了 OCI (开放容器计划) 组织,其最主要的内容有两个:

  • 容器运行时规范
  • 容器镜像规范

Docker 将其运行时捐赠给了 OCI ,作为容器运行时规范的基础实现,托管在了 https://github.com/opencontai... 也就是现在大家看到的 runc 了。

发布历程

我们来看看 runc 版本发布的历程,以便了解它为何长跑 5 年。

runc versionrelease timeruntime-spec version备注
runc v1.0-rc12016.06.04v1.0.0-rc1
runc v1.0-rc22016.10.01v1.0.0-rc2-38-g1c7c27d
runc v1.0-rc32017.03.22v1.0.0-rc5
runc v1.0-rc42017.08.09v1.0.0runtime-spec 首次发布 v1.0
runc v1.0-rc52018.02.27v1.0.0首次计划作为最后一个 rc 版本
runc v1.0-rc62018.11.21v1.0.1-49-g5684b8a计划是 1.0 之前的最后一个功能版本,包含了一些规范合规性的修正
runc v1.0-rc72019.03.28v1.0.1-59-g29686db修复 CVE-2019-5736
runc v1.0-rc82019.04.26v1.0.1-59-g29686db修复 v1.0.0-rc7
runc v1.0-rc92019.10.05v1.0.1-59-g29686db修复 CVE-2019-16884
runc v1.0-rc102020.01.23v1.0.1-59-g29686db修复 CVE-2019-19921
runc v1.0-rc902020.05.12v1.0.1-59-g29686db与 runc v1.0-rc10 相同,是为了修正 version scheme
runc v1.0-rc912020.07.02v1.0.2-8-g237cc4f开始支持 cgroup v2 ;一些规范性的问题得到解决
runc v1.0-rc922020.08.06v1.0.2-23-g4d89ac9修复我在 runc v1.0-rc91 中发现的 bug
runc v1.0-rc932021.02.04v1.0.2-35-ge6143cacgroup v2 得到稳定支持,
runc v1.0-rc942021.05.10v1.0.2-57-g1c3f411修复 runc v1.0-rc93 中的 regressions
runc v1.0-rc952021.05.19v1.0.2-57-g1c3f411修复 CVE-2021-30465
runc v1.02021.06.22v1.0.2-57-g1c3f411

我在上面的表格中,专门增加了一列 runtime-spec version ,表示 OCI 组织中的容器运行时规范的版本。我们来总结下这个发布进程:

  • 在 runc v1.0-rc5 之前,runc 其实也没打算发布正式版,毕竟标准还没正式完成呢,实现也不可能先出稳定版;
  • runc v1.0-rc7 , rc 9 ~ rc 10 均是为了修正严重的安全问题;
  • runc v1.0-rc90 纯粹是解决 version scheme 的问题;
  • runc v1.0-rc91~rc93 主要功能点是在 cgroup v2 的支持,以及一些跟规范集成的问题;
  • runc v1.0-93 之后,其实就基本控制代码冻结了,直到 runc v1.0-rc95 修复了一个安全漏洞;
  • 目前主要的几个仓库也都已经测试了跟 runc 代码仓库中最新代码的集成,相关的问题也已经修复。

从这里看到的三个主要耗时的点如下:

  • runtime-spec 尚未正式发布 v1.0 版本;
  • 修复安全漏洞和自身的 bug ;
  • 完成新特性的耗时;

规范未发布 v1.0 耗时的部分这里就不多说了,这也是个依赖项,对于大多数的项目/软件开发都会有类似的情况,只能去推动规范的发布了;

至于特性,bug 和安全漏洞等的耗时,这其实跟 runc 项目的功能和其定位有关。runc 偏底层了一些,这就需要有更多相关领域的知识来支撑。就拿我在 runc v1.0-rc 91 中发现的那个bug 来说,对 Linux 内核源码不太了解的人,确实会花费比较多时间的。

- switch { - case mode&unix.S_IFBLK == unix.S_IFBLK: + switch mode & unix.S_IFMT { + case unix.S_IFBLK:     devType = configs.BlockDevice - case mode&unix.S_IFCHR == unix.S_IFCHR: + case unix.S_IFCHR:     devType = configs.CharDevice - case mode&unix.S_IFIFO == unix.S_IFIFO: + case unix.S_IFIFO:     devType = configs.FifoDevice default:     return nil, ErrNotADevice

有趣的是 runc v1.0 版本 Release 的标题是 "A wizard is never late, nor is he early, he arrives precisely when he means to." 这大概也很符合 runc 的发布历程了吧 :)

但无论如何,runc 经过 5 年长跑,终于发布了 v1.0 版本!感谢每一个为之付出过的小伙伴!

欢迎大家下载更新! https://github.com/opencontai...


欢迎订阅我的文章公众号【MoeLove】

TheMoeLove

No comments:

Post a Comment