贝er

贝er 查看完整档案

深圳编辑武汉工商学院  |  计算机科学与技术 编辑Forchange  |  前端工程师 编辑 youyi2016.github.io/TechBlog/ 编辑
编辑

不仅仅是程序员

个人动态

贝er 赞了文章 · 9月22日

程序员:写作能收获什么?

简介: 很多程序员已经通过自己的个人博客或者公众号来进行技术沉淀,记录自己的成长。越来越多的程序员们也开始意识到了写作的重要性。程序员为什么需要写作?写作能带来什么收获?又有哪些额外的惊喜?本文介绍三位长期坚持写作的程序员,分享他们在写作道路上的心得和收获,希望对同学们有所启发。

image.png
你有写作的习惯吗?很多程序员的回答是:我为什么要写作呢?很多人觉得写作是一件有难度的事情,其实写作的动机就藏在日常工作中,那些在酝酿中的奇思妙想,那些稍纵即逝的编程思路,那些金光闪闪的 debug 瞬间……都是写作的素材。

输出是最好的输入,养成写作的习惯,对技术提升和个人成长都有很大的帮助。改变世界的程序员,同样需要写作记录世界。如果你还没有开始写作,那你可能需要重新思考“写东西”这件事的意义。

今天,我们采访了 3 位坚持写作的程序员,看看写作给他们带来了什么。

  • 寒雁:阿里巴巴前端技术专家,连续 5 年更新博客
  • Hollis:阿里巴巴技术专家,20 万粉丝公众号号主
  • Frank: Wuhan2020 开源项目发起人,个人博主

我为什么要开始写作?

Hollis: 写作让我思考,与志同道合的朋友讨论技术

2015 年毕业后,我加入了阿里巴巴从事后台开发工作,也是这一年,我写了第一篇文章,内容是我参加阿里校招之后总结的“面经”。因为在找工作之前复习的阶段,我看了很多其他人的面试总结,给了我很大的帮助。写这篇文章一方面想要对自己的校招做一个总结,另一方面也希望帮助到其他人。

从这篇文章以后,我收到了很多评论,还有很多人私下找到我讨论技术,我发现写作给我带来了很多的乐趣。通过写作我可以进行自我思考、自我总结,也可以和志同道合的朋友们一起讨论技术,所以我开始坚持写作。

最开始写的内容都比较简单,只是记录一些工作中遇到的问题的总结,渐渐的我开始主动去学习一些东西,然后文章内容逐渐演变成原理分析、最佳实践等。

一开始文章只是发表在自己的博客中(hollischuang.com),后来一次偶然的机会,我发现公众号上面的读者可以有更多的互动,于是就把自己的文章同步到公众号(Hollis)中,现在公众号已经积累了将近 20 万的读者。

去年还把自己写过的一些内容整理出来,和朋友一起出本了一本书《程序员的三门课》,在书中写了很多自己的经验和思考。

寒雁:写作是我的工作日志,可以帮助产品带来用户

作为程序员,我们每天都会遇到各种各样的技术问题,而我在遇到棘手一点的问题时,并不会急着去解决问题,而是会把问题记录清楚,包括代码、报错日志、截图,甚至解决问题的过程和一些参考链接。这些内容再加上一些原理层面的知识点,一篇记录问题的工作日志其实也就是一篇博客。所以,我刚开始写的博客,也就是这种类似于工作日志的内容,还是挺简单的。

后来,研究生毕业后,我选择了和朋友一起创业。前期不太懂运营,用户增长不知道怎么做。后来发现自己写博客还是挺擅长的,能给产品带来不少用户,于是就养成了写作的习惯。

翻译过不少博客,也原创了不少,写过一些挺受欢迎的博客,也写过一些很幼稚、类似于标题党的内容。不过整理来看,写作水平一直有在提高。来阿里之后,我写了一篇《写作的意义》,也在团队做了一次关于写作的分享《关于写作的那些事:寒雁聊聊 10 万+背后的思考》。我是真的挺喜欢写作的,也觉得写作收获蛮大的。

Frank: 我用写作记录开发“黑科技”,分享我的想法

时间回退到四年前,毕业后成为了一名游戏开发工程师,进入了一个全新的领域,每天都在接触新的东西,而且游戏开发中有大量互联网常规开发中难以见到的“黑科技”,令人目不暇接。从那时起,其实就一直有写作的习惯,由于很多技术细节并不适合对外,所以当时是使用很多笔记类软件进行记录的,例如印象笔记。

后来开始在开源圈中进行一些开发工作,依然保持着印象笔记来记录自己工作内容的习惯,但由于开源的开放性,很多时候也非常希望可以把这些想法和内容分享出来,于是博客就成了一个更好的选择,也是为什么现在选用博客 (blog.frankzhao.cn) 来做写作记录的原因。

程序员写作有什么好处?

寒雁:写作是对自己的长期投资,也是最佳个人品牌

写作是一件具有长期价值的事情,这一点类似于健身与读书。我想大多数人都认同,不管工作再忙,也应该坚持健身,坚持读书,因为这是对自己的长期投资,不少人也是这么做的。在我看来,写作其实也一样,只是很少有人会意识到这一点,更少人可以做到这一点。

提升工作效率

写作最重要的职业技能。我挺喜欢写工作日志的,从另一个角度理解,我每天的工作并不只是在写代码,而是在写工作日志,比如技术问题、技术方案、沟通备忘录、会议纪要等一切与工作相关的内容我都会记下来。在与同事沟通或者寻求帮助之前,我都会写一个完整的文档,这样沟通会高效很多。

写作即是学习

写作是最有效的学习方式。这里原理是费曼学习法,通过输出倒逼输入。因为我们在写作过程中会发现自己的一些知识盲点以及思维盲区,如果可以静下心研究清楚,然后用最通俗易懂的语言表达出来,这其实是很好的学习和提升自己的机会。写作其实挺锻炼思考能力的,因为表达一个观点相对简单,如何将观点论述地清晰、完整、深刻,结构化地表达出来,取决于我们是否真的想清楚了。

创建个人品牌

写作是最佳的个人品牌。互联网已经 30 岁了,但是它的游戏规则其实没变:通过流量变现。文章写得好,有读者就有流量,有流量就可以变现。自媒体时代很多“草根”作者崛起,也是这个道理。现在是视频时代,表达内容的媒介变了,但是本质没变,因为视频内容的含金量取决于文案。作为程序员,没有必要去靠写作赚钱,但是通过写作打造个人品牌还是挺重要的,这对于求职、招聘、同行交流以及未来创业都很有帮助。

Frank:写作让你换一个角度发现问题的全貌

就我本人而言,工作的前几年都以记录技术为主,但后来,尤其是近一年读博的期间,可能更多的写作发生的社科类学科上。坚持写作有诸多的好处:

记录技术成长

写作可以让自己更好的记录技术成长的历程,时常回顾会有更多的成长。虽然我本人现在已经不再做游戏相关的开发工作,但我很庆幸自己当初有大量的笔记可以让我回顾一些技术细节和设计理念,这些理念事实上在很多场景下都是通用的,可以很好的指导之后其他领域的开发工作。

换一个角度发现问题的全貌

很多时候你以为你明白了一个技术要点,但当你用文字去表达的时候你会发现有很多的盲点你可能都忽略了。例如你解决的是工作中的一个具体问题,当你解决了这个问题时,你以为你明白了。但当你用文字记录时,尤其是你把自己放在一个读者的角度去阅读时,才会发现你忽略的东西,例如具体的环境、版本,出现问题的情景、依赖等,当用文字去记录时,就会刻意补足这些内容,而这些才构成了解决这个问题的全景。而且就我个人感觉,记录过程中的成长可能要比单纯解决问题中的成长大得多。

提升写作能力和逻辑编排能力

写作能力绝不仅仅是一个文字工作者需要,尤其在这个愈加开放的时代,写作是通过的基础。练习写作能力,不仅可以帮助你更好的与他人沟通,而且也是一种梳理逻辑的过程。好的技术文章同样需要有优秀的逻辑编排,由浅入深,层层递进。而且相信我,这是任何工作,也包括一般的程序员工作中非常重要的一种能力。

分享让你的文章“升值”

如果你写的文章与他人分享,则这个文章的“价格”会比个人获得的更多,帮到其他的人的感觉会更好。在研究生之前,我曾有机会修改 JavaMail 的源码,使其支持需要基本认证的 HTTP 代理服务器进行邮件操作,而当时的 JavaMail 还仅支持 Socks 代理服务器。直到现在,我还是会偶尔收到有人邮件询问我实现细节,我能感受到我在真正的帮助他人。但可惜当时不了解开源,否则应该可以帮助到更多的人。

Hollis:写作提升技术能力,可以帮助更多人

技术提升

写文章的过程中,自己会想办法保证写出来的内容都是正确的,所以就会查阅很多资料,这个过程中,自己就会学习到很多知识,可以很好的提升自己的技术能力。尤其是写系列文章的时候,可以很好的完善自己的知识体系。正所谓“教是最好的学”。

不断纠错

没有人写出来的东西都是完全对的,所以有的时候写完的文章会收到一些不同的观点,这时候就可以帮助自己纠错,不断的提升自己。

帮助自己更好地记忆

很多人都会发现有一种现象,就是一个知识自己学过之后过段时间就忘了。有了博客之后就可以解决这个问题,可以把知识以自己的理解写到博客中,一方面可以加强自己的理解与记忆,另外也方便日后回头翻看与复习。

提升个人影响力

因为自己写作,可以大大提升自己在行业内的影响力,因为自己写了很多文章,有很多程序员都看过我的文章,我曾经大致统计过,我的技术文章,在全网的阅读量有数千万。最近几年,经常有公司的同事过来问我:你是不是Hollis?原来你就是Hollis?我看过你的文章等等。

帮助他人成就自己

在自己刚刚接触 Java 不久的时候,一直想找到一份学习路径,但是一直都没有找到,于是自己就利用业务时间总结了一份 Java 工程师的学习路径——《Java工程师成神之路》。这篇文章现在上百万人读过,我也接到很多留言,都说对他们帮助很大。最近两年,经常有读者在我的公众号和博客后台留言,说自己因为看了我的文章找到了某大厂的工作等等的好消息。看到自己的一点点努力,可以帮助到很多人,开始很有成就感的。

写作给你带来了什么额外惊喜?

寒雁:更好的职业发展以及对世界的认知

我之所以来阿里,也是因为我的文章,因为是主管看了我的博客,了解了我做的产品,然后邀请我来面试的。其实我自己去招人也是这样,如果你的博客写得足够好,我也会特别留意。

写作让我的阅读能力也明显提高了,在信息爆炸的时代,如何甄别真正值得阅读的内容还挺重要的,我可以在极短时间内判断一篇文章的层次,然后决定是否认真阅读。另外,因为我自己熟悉写作的套路,因此知道哪些话是真正有价值的,哪些话只是作者的话术,哪些要点是作者漏掉了。

Hollis:交友、招聘以及出书

因为写作,我认识了很多志同道合的朋友,他们很多人都是做程序员的,同时也是专业书籍的作者、知名博客的博主等。还有很多读者来自于各个互联网公司,有着不同的背景,有些都是工作经验比较丰富的大牛,和他们交流的过程中,自己也能学到很多东西。

因为我有自己有博客和公众号,又积累了很多读者,每次发布招聘信息都能收到很多简历,最近帮助团队招聘到了几个新的同事。因为我在一些招聘文章上面的昵称也是 Hollis,所以我遇到过几次,我在招聘网站上面“勾搭”候选人,都被人问:你是不是有个公众号?

因为自己写博客有很多读者,所以经常有出版社的编辑找到我询问出版书籍的事情。之前也有朋友找到我想要一起出书的等。2019 年和朋友共同出版了一本《程序员的三门课》。在那之后,自己还出了多本免费的电子书:《Java 开发手册灵魂 13 问》、《Java 开发手册灵魂 15 问》、《Java工程师成神之路——基础篇》等。

Frank:走上开源研究道路

我个人可能是程序员中不太常见的比较喜欢社科类记录的同学,要说惊喜,不如说是潜移默化的改变。

通过写作记录,逐渐加深对于社会科学的理解,对于人类写作历史的理解,是我最终走上开源研究道路很重要的原因。回头来看,每一个微小的习惯在长年累月中都会重新塑造自己,使你的人生走向一个完全不同的方向。而写作,或者说写作背后所代表的一种终身学习的态度,会给所有人都带来无限的可能。

每一个程序员都应该尝试技术写作,一旦开始,你就会发现自己成长的速度在增快,你看待文字背后的世界会变得不同。每一个程序员也不应该仅仅局限于技术写作,对于世界、历史、人文的理解同样需要记录和分享,希望写作让你面向屏幕时,不止面向代码,更是面向星辰大海。

查看原文

赞 3 收藏 2 评论 0

贝er 关注了专栏 · 9月16日

冴羽的JavaScript博客

冴羽写JS文章的地方,预计写四个系列:JavaScript深入系列、JavaScript专题系列 、ES6系列、React系列。

关注 1432

贝er 赞了文章 · 9月9日

前端之未来

简介: 编程是一种修行,用心写下你的每行代码。

作者 | 平侠

image.png

引言

近期学到一个词:Digital Twin(数字孪生),简单来讲就是在计算机世界给现实世界的物体(飞船/城市)、系统(电力/交通)建立数字化镜像,让数字世界和物理世界更好地交互,数字大屏也可以算这种技术一种应用场景。这个概念由 Dr.Michael Grieves 于 2002 年提出,随着 IOT、AI、图形技术、工业 4.0 的飞速发展,应用前景越来越广阔。Google 一下 就会发现:NASA、GE、Microsoft、SAP、IBM 等巨头早已布局和应用这项技术,它还是 Gartner Top 10 Strategic Technology Trends for 2019 中的一项。

image.png
图片来自:How the visualization has changed by Digital Twin Technology

前端为应用而生,人机交互技术是我们的生存之本,以应用为桥连接用户和数字世界是我们的使命。但长期以来前端的生存空间被压缩在 Web 领域,Digital Twin 这种新形态无疑将为我们打开一片新天地。Gartner 这份报告中还有两项技术和前端紧密相关:

1、Immersive technologies:Augmented Reality(AR)、Virtual Reality(VR)、Mixed Reality(MR) 等

2、Smart Spaces:智慧城市、智能汽车、智能商店等

看到未来有三项战略性技术居然和前端有关,着实让人兴奋不已。不过莫急,既然是战略,也就意味着三五年内未必能开花结果。再仔细分析会发现:当下的前端技术远不足以胜任这些领域,不由得让人心忧。远方的风景很美,但要成功抵达就需要早做准备。站在 201X 的末尾、互联网下半场的开始,不妨先结合 Gartner 这份报告一起分析下:下一个 10 年前端技术的变革在哪里、有哪些值得大力投入的方向?

回顾过去

“以史为鉴,可以知兴替”,如果历史是一组波,它过去的相位所形成的轨迹已足以预见未来。回顾 Web 技术的发展历程,可以清晰地看到三类促使变革发生的关键因子:

引擎:有四大引擎显得尤为重要:

1. V8 :不仅提升了 JS 的执行效率,助力 ES 规范落地,而且催生了 Node.js

1. 浏览器引擎:以 Webkit、Blink、Chromium 为典型代表,浏览器的高速发展为 Web 的繁荣奠定了基础

1. Node.js :大大拓展了前端的生存空间,以至于“Any application that can be written in JavaScript, will eventually be written in JavaScript.”

1. Hybrid 容器:让被 App 统治的移动互联网时代也给 Web 开发留下了一席之地,小程序是典型代表

开发套件:语法、框架、工具、类库在社区的推动下一直在蓬勃发展,优秀的开源项目灿若星河,前端生态也成为技术圈中最活跃的。虽然以 React 为核心的主流技术栈上手成本还比较高,也做不到让开发人员只关心业务逻辑,但不可否认应用开发正在变简单。有些类型的应用甚至做到了无需 Coding 通过专门的可视化搭建平台就可以完成,比如:门户网站、营销活动、问卷调查等。

分工模式:前后端分离、BFF(Backend For Frontend)、全栈、全端、大前端等分工模式的创新不仅提高了前端和其它工种的协作效率,也让前端有机会承担应用研发。由“前端 + 设计”组合形成的“体验技术部”也成为很多业务的标配,部分前端团队甚至发展为应用研发团队并且拥有了自研产品。前端的影响圈已经从应用开发延展到了用户体验甚至产品设计,以人机交互为本的 体验科技 也开始崭露头角。

这些变革因子的背后是两条主线:

1. 让现有研发工作做得更好:开发套件是主要推手,一些分工模式(比如:前后端分离)的创新也归属这条线

1. 开辟新战场:引擎是主要推手,一些分工模式(比如:全栈)的创新也归属这条线上

这些变革之所以会发生,是因为有一个刚需:客户端软件的生产力水平满足不了飞速增长的互联网应用诉求,而前端技术恰好能提升应用研发的生产力水平。虽然移动互联网的崛起曾一度让前端缺少发力之处,但寄生于超级 App 上的 Hybrid 容器又让前端焕发了生机,小程序更是将之推向了和 PC 时代同样重要的地位。应用虽然琳琅满目,但其形态演化也是有迹可循的,要讲清楚得专门写一篇文档,这里不过多展开,简单介绍几个我认为最重要的:

UGC 内容的主流载体在变:文本 -> 图片 -> 短视频/直播,用户创作内容的成本越来越低了

终端的主流交互方式在变:PC(键盘/鼠标) -> 手机/PAD(触屏/摄像头/语音),交互越来越自然、简单了

信息获取的主流方式在变:主动获取 -> 被动推送 -> 智能推荐,异步 -> 实时,信息已触手可得

一个刚需、两条主线、三类因子也是我们预判未来的重要依据。

立足当下

在当下可实践的新技术中,前端相关的有:AI、Serverless/FaaS、Blockchain、IOT、AR/VR/MR、智能硬件、可视化应用开发。不可否认,它们都是能在一些领域带来颠覆性革命的技术,但是否会给前端带来变革呢?让我们详细分析一下:

AI:核心是云,而且 AI 应用的典型特点是“重引擎轻 UI”,所以前端不在主赛道,只能在应用开发中使用它。目前市面上的 AI 应用,多数是大数据技术的延伸,离 Intelligence 还太远。作为人类技术的巅峰之作,AI 应该在人类最难解决的问题上发挥价值,比如:语言文字、医疗、科研、教育、环境等。个人看好机器翻译,打破语言界限将会是人类文明一次划时代的变革。作为前端,我们也应该去关注这些基础领域,并学习和掌握 AI 技术。

Serverless/FaaS:核心是云,前端能做的是基于这种技术优化 Node.js 在服务端的 Runtime 和运维方式,把服务端复杂的技术细节屏蔽掉,让 Node.js 开辟出来的服务端战场可以延续,让端上所需的数据能以简洁、低成本的方式存储和获取。当然,也可以基于它优化现有的工具体系,让开发越来越简单。

Blockchain:核心还是云,和前端最相关的是 Decentralized Application(DApp)、IPFS(the InterPlanetary File System)。但 DApp 是一种新的应用形态,IPFS 改变的是网络协议。这两者还处于非常早期的阶段,发展形势还不明朗,最好是静观其变,在合适的时候基于它们开发应用。

IOT:核心是端,但关键技术是硬件及嵌入式系统,和前端交集较少,发挥空间很有限。除了在之上开发应用外,能看到的还有两个潜在方向:把 Node.js、浏览器内核移植到 IOT 设备,打造一个可运行前端代码的 Runtime;部分设备需要展示数据,可以基于图形技术打造专属渲染引擎。

AR/VR/MR:核心是硬件及交互方式的变化,前端能参与到类库及应用开发中。但受设备所限,目前还不是应用的主流,需要结合业务特点寻找切入点。

智能硬件:非常酷的一项技术,智能音箱算是当下最成功的应用,机器人则是这项技术的终极形态。其核心在 AI 、自动化控制及硬件上,给前端带来的更多是应用形态和交互方式的升级。

可视化应用开发:不写/少写代码就完成开发,这是前端的一个夙愿,不可能完全达成,但在特定场景下是可以做到的。MFC、Dreamweaver、Flash、Microsoft 是该领域的先驱,Wix、Webflow、Bubble、Node-RED、FrameX、PowerApps 是当下值得关注的。其本质上仍是通过更好的开发套件提升应用生产效率,其最大竞品是成品 SaaS,毕竟拿来就用比搭建更简单,这就如同当需要一台电脑时多数人会选择买成品而不是买配件组装。

综上来看,这些新技术的关键路径和核心技术多数都不在前端,但我们能以使用者的身份参与进去,结合业务特点进行实践,让现有研发工作做得更好,让应用的交互体验更好。不过,还是有两个前端强相关的技术有望带来变革:

1、IOT 上的 App Runtime :其背后就是一个引擎,能让前端应用运行在越来越多的新型终端上

2、可视化应用开发:有望把部分功能和应用的开发成本降到最小

一窥未来

应用形态日新月异,新技术风起云涌,未来扑朔迷离...作为前端,我们究竟该往那些方向进行技术储备呢?要找到有指导意义的技术路线图,除了前文这些分析外,还得回到前端的本质中去探寻变革背后那些不变的东西,只有它们才能让我们以不变应万变,把技术转换为生产力。

前端技术发展变化虽然很快,但从程序的视角来看,有两个东西一直未变:

终端的形态和交互方式一直在变,其本质未变:

渲染数据:把数字世界的数据转换成可被人感知的声音或图像,图形技术、音频技术、排版技术是核心

采集数据:把物理世界的数据、人脑中的知识转换到数字世界,传感器技术、编辑器技术是核心

应用的类别和交互方式在变,但应用研发始终可以分解为四大部分:

1、云:提供应用运行所需的数据,托管资源及可执行代码

2、端:依托某个 App Runtime 提供 UI 给最终用户

3、专项技术:业务是技术之本,脱离了业务,前端将是无根之萍,而每个业务都有其专业属性和专项技术

4、App Development Engine:也即“开发套件”,在普通工程师完全自主可控的范围内提升生产力

结合以上分析以及我参与研发的语雀这个业务的特点,整理了这幅前端技术大图以抛砖引玉:

image.png

图中的每个区域都是一个不小的技术领域,要完全讲清楚得专门写文章,再加不少领域我只是略知一二,就不过多展开了,只简单介绍几个我认为比较重要的方向。

App Development Engine

当下的 Web 应用开发真的让人揪心,学习曲线非常陡峭、新概念层出不穷、技术更新换代太快...应用复杂度并不比十年前高多少,但要学习和掌握的东西是之前的很多倍。身处产品研发这个战场的前线,我们的装备一点都不精良,大把精力耗在作战无关的地方。生产力虽然在提升,但完全赶不上业务增长速度,App Developement Engine 这个提升生产力的关键因子在当下显得尤为重要。

这个领域是创新最活跃的地方,从过去的发展历程中能看到一些演进脉络:

从 Engine 的角度看,演进的背后有两种理念:

1、Coding Less:通过强大的 SDK、框架和工具让工程师更好地 Coding,专注在实现业务上

2、No Coding:通过可视化 IDE 达成不写代码,通过拖拽、编写配置文件就能完成应用开发

从开发者角度看,对 Engine 有三个期待:

1、Productivity:必须能提升生产力,让工程师可以高效地写出健壮、易维护的代码

2、Simple & Stupid:KISS 原则 的核心,让开发变简单不仅能提升效率,还能让更多人成为前端工程师

3、Business More:研发资源非常宝贵,让工程师专注在业务上是提升效能的关键

未来的演化也会遵循这些脉络,Coding Less、No Coding 各有其应用场景,需要结合业务特点选择侧重点进行投入。但有一点我觉得是必然的:要开发优质应用,还得靠 Coding,不过写的代码会越来越少。No Coding 过于完美,应用场景有限,再加上有成品 SaaS 作为更好的替代品,我更倾向于用 Coding Less 模式去实现业务主线,把一些机械性、重复性、一次性的开发工作通过 No Coding 模式搞定。不过 No Coding 的一个分支 Visual Programming 非常值得关注,它在编程教育领域应用前景非常好,Scratch 、Blockly 是典型代表,而编程教育不仅蕴藏着巨大的商机,而且还会给我们带来源源不断的生力军。

专项技术

专项技术会因业务不同而有所不同,这里以语雀为例进行介绍

语雀致力于打通「知识」和「大脑」之间的双向通道,背后是两大关键技术:

  1. 编辑器:知识从大脑到数字世界的关键,语雀当下的文本编辑器、目录编辑器还很初级
  2. 知识可视化:知识被人脑感知和理解的关键,给知识最佳的呈现形态有助于知识的理解和传播

“数据可视化”和“数字孪生”在当下和语雀的关联并不大,但在未来一定会和语雀紧密相关:

1、数据不仅是科学研究的基础,其背后本身就蕴藏着知识,数据可视化可以让这些知识更好地呈现给人们

2、数字孪生和知识结合蛮有想象空间的,比如:数字博物馆、虚拟天文馆、仿真生态系统

App Runtime

为什么 App Rutime 会是变革的生力军呢?因为历史告诉我们:引擎的革新是开辟新战场的核心驱动力,而 App Rutime 的核心恰好就是引擎及其之上的 SDK。在很长一段时间里,前端技术得益并受限于浏览器引擎,广大前端工程师只能在 App Development Engine 层发力。但时至今日,终端、应用形态的多样性越来越强,移动互联网已经打破浏览器一统天下的局面,不难看出浏览器技术的进化速度已不能满足越来越多的新型终端和应用形态了

可以大胆地预测在这个领域必然会有一场变革,变革有可能由浏览器进化产生,也有可能是一些新的引擎。已经能看到一些苗头了:

1、Office 以惊人的速度和 UI 一致性覆盖了所有终端,猜测其背后有一套跨端 UI 方案

2、超级 App 打破了浏览器一统天下格局,小程序已是新的 Runtime

3、Flutter 在探索的就是一个新的跨端 UI 开发方式

4、Fuchsia 则是 Google 在探索的一个更大的局,从 OS 层面推动变革

更关键的是:这场变革是前端工程师有机会参与进去的,因为引擎的核心技术——图形技术已经在数据可视化、H5 Game 的推动下成为不少团队能熟练使用的技术。而应用类型的多样性也会给一些垂直市场的 App Runtime 留下生存空间,这块已经有一些成功案例,比如:游戏领域的 Cocos 引擎 和 白鹭引擎、桌面软件开发领域的 Electron,未来应该还会有更多。

领域服务

“领域服务”对前端来说可能比较陌生,它的背后是大名鼎鼎的 领域驱动设计(DDD:Domain-Driven Design),是应用在云端部分的高度抽象,是系统中的稳定部分,这也正是前端总是改版而服务端接口却可以不变的原因。服务端研发有这样一条演化主线:解决编程语言层面的问题 -> 解决开发框架 & 类库问题 -> 专注于解决业务问题,Java -> Spring -> DDD & Sofaware Architecure 就是典型案例。服务端之所以抗变能力这么强,正式由于把主要精力放在了领域模型抽象和系统架构设计上。

为何领域服务在当下对前端也这么重要,因为:

1、领域模型和领域服务是对业务的抽象,也是理解业务的直观体现,好的领域模型能大大降低前端开发成本

2、领域设计的相关思想、理念可以借鉴到前端中,给前端带来解决问题的新思路

3、前端处于研发的十字路口,信息面最广,极有潜力协调产品、设计、服务端等工种共同梳理领域模型和业务链路

关于 DDD,可以从这三种非常容易理解和掌握的经典架构入门:

  1. Hexagonal Architecture(Ports and Adapters)
  2. CQRS - Command Query Responsibility Segregation
  3. The Clean Architecture

全功能型团队

前端是工程师中离用户最近的群体,很多前端心中都有一个产品梦,这个产品可能是一个技术产品也可能是一个用户产品。很长一段时间内,我们疲于生存,只能忙里偷闲折腾个小工具或者参加下 Hackathon。但随着技术的演进,不少团队已经具备了全栈能力,并承担了 App Development Engine 中的一些平台的自主研发,让这个梦近了一些。但要完整实现,光有全栈能力是远远不够的,还需要一个包含技术、产品、设计、运营的全功能团队才能达成,这种团队不仅能提升协作效率、保证交付质量,还有可能催生出商业化产品。很多公司都在往大中台 + 小前台的方向发展,而支撑小前台的恰恰就是全功能型团队。所以在时机成熟时可以组建全功能型团队,逐步承接业务中人机交互部分的研发工作,让“用户体验”能落到实处。

砥砺前行

image.png

前端技术的未来,没有标准大图,这份图蕴含在每个业务中,更需要脚踏实地把未来亲手打造出来,业界趋势、新技术、新产品形态都是可以借力的因素。如果你已经有一个愿意与之一起奋斗和成长的业务,就可以结合业务定期推演适合它不同发展阶段的技术大图。如果还没有,也不用急,打好技术基础,做好手上的每个项目,结合业务在 Appliaction Development Engine 领域探索和实践,逐步寻找真正想做的业务、想服务的用户。

软件研发是一项理论和实践并重的技术,实践尤为重要,因为最终我们是要写出健壮运行的代码给用户用的。不管未来如何,在持续学习和实践中强化对编程、技术、业务的理解才是根本。除了学习和实践与业务最相关的技术外,建议按自己的专长和兴趣把重点放在这些领域:

  1. 领域驱动设计:强化领域建模和系统设计能力,力争懂业务、成为领域专家
  2. 软件架构设计和软件设计哲学:它们会为系统、框架、类库注入灵魂,让代码有生命力
  3. 图形技术:在应用、引擎两层都有广阔的场景,最关键的是图形应用在未来的占比一定会越来越高
  4. AI :不必深入到底层,但需要掌握其使用,不妨先从 TensorFlow 开始

编程是一种修行,应用修行的产物,也是我们与世界交流的方式。未来在哪里并不重要,重要的是以空杯心态持续学习和实践,用心写下每行代码

查看原文

赞 2 收藏 1 评论 0

贝er 关注了专栏 · 9月9日

阿里技术

阿里巴巴官方技术号,关于阿里巴巴经济体的技术创新、实战经验、技术人的成长心得均呈现于此。

关注 1696

贝er 赞了文章 · 8月7日

vue下使用perfect-scrollbar(在特定框架里使用一款并非为该框架定制的库/插件)

其实,如何在特定框架里使用一款并非为该框架定制的库/插件,只有两点:
1.熟悉这个框架
2.熟悉这个库/插件的工作原理

说完废话。进入正题。
perfect-scrollbar是一款轻量级的滚动插件,具体介绍详见其官网
你只需要知道它绝对不是为了vue设计的,和vue没半毛钱关系。那么如何完美的融合其中呢?

以下是我的步骤
首先,安装包

npm install perfect-scrollbar --save

其次,引入包。为了能够在项目中信手拈来的使用而不是每个要用的组件都去引入一遍,我们应该在主入口引入并注册为自定义指令。

//main.js

//引入核心框架
import Vue from 'vue';
//插件的包
import PerfectScrollbar from 'perfect-scrollbar';
//对应的css
import "perfect-scrollbar/css/perfect-scrollbar.css";

/**
 * @description 自动判断该更新PerfectScrollbar还是创建它
 * @param {HTMLElement} el - 必填。dom元素
 */
const el_scrollBar = (el) => {
    //在元素上加点私货,名字随便取,确保不会和已有属性重复即可,我取名叫做_ps_
    if (el._ps_ instanceof PerfectScrollbar) {
        el._ps_.update();
    } else {
        //el上挂一份属性
        el._ps_ = new PerfectScrollbar(el, {
            // 要配什么属性自己看官网,此处不会解释任何其配置项的含义
            suppressScrollX: true,
        });
    }
};

//接着,自定义Vue指令,指令名你自己随便编一个,我们假定它叫scrollBar
Vue.directive("scrollBar", {
    //使用inserted钩子函数(初次创建dom)获取使用自定义指令处的dom
    inserted(el, binding, vnode) {
        //判断其样式是否存在position 并且position为"fixed", "absolute"或"relative"
        //如果不符合条件,抛个错误。当然你也可以抛个警告然顺便给其position自动加上"relative"
        //为什么要这么做呢,因为PerfectScrollbar实现原理就是对dom注入两个div,一个是x轴一个是y轴,他们两的position都是absolute。
        //对css稍有常识的人都知道,absolute是相对于所有父节点里设置了position属性的最近的一个节点来定位的,为了能够正确定位,我们要给其设置position属性
        const rules = ["fixed", "absolute", "relative", "sticky"];
        if (!rules.includes(window.getComputedStyle(el, null).position)) {
            console.error(`perfect-scrollbar所在的容器的position属性必须是以下之一:${rules.join("、")}`)
        }
        //el上挂一份属性
        el_scrollBar(el);
    },
    //更新dom的时候
    componentUpdated(el, binding, vnode, oldVnode) {
        //vnode.context其实就是vue实例,这里其实无需实例也直接用Vue的静态方法
        //故而也可以写成Vue.nextTick
        vnode.context.$nextTick(
            () => {
                el_scrollBar(el);
            }
        )
    }
})

至此,我们完成了PerfectScrollbar在vue的准备工作
接下来我们试试如何调用。
用法相当简单,在要用到的地方直接写一个v-scrollBar

//具体组件

<template>
    <div class="container">
        <ul class="list" v-scrollBar>
           <li>巴拉巴拉</li>
           <li>炫光舞法</li>
           <!--想象这里有一堆li-->
           <li>天舞台</li>
        </ul>
    </div>
</template>

<style lang="less" scoped>
.list{
    position:relative;
    /*不写高度说明高度自适应,既然高度都无限了根本就不会出现滚动条*/
    height:300px;
}
</style>

是不是很简单,只要在要用到滚动条的那个ul上面加一个v-scrollBar即可,其余什么都不用操心。

最后,还要注意一下兼容性
perfect-scrollbar官网描述。它对ie的支持仅完美支持ie11,然后ie10勉强能用
那么vue是支持到ie9的。我们至少要让他在ie9上能用

那么现在的这个状态直接在ie9上试试。应该会得到一个类似于
Uncaught TypeError: Cannot read property 'add' of undefined
的报错。

那么我们打开perfect-scrollbar的源码看看

clipboard.png

有.add的地方就这两处 都是classList的方法
那么classList是什么,参见mdn
ie9上没有对其实现,所以我们挂一个对其的垫片
再来安装一个包

npm install classlist-polyfill --save

然后在引入perfect-scrollbar包之前(其实在它被实例化之前就行)引入它

//main.js 头部引包部分改一下

//引入核心框架
import Vue from 'vue';
//classList的垫片
import "classlist-polyfill";
//插件的包
import PerfectScrollbar from 'perfect-scrollbar';
//对应的css
import "perfect-scrollbar/css/perfect-scrollbar.css";

至此大功告成

查看原文

赞 23 收藏 20 评论 5

贝er 收藏了文章 · 8月4日

浏览器解析 CSS 样式的过程

个人专栏 ES6 深入浅出已上线,深入ES6 ,通过案例学习掌握 ES6 中新特性一些使用技巧及原理,持续更新中,←点击可订阅。

点赞再看,养成习惯

本文 GitHubhttps://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。


为了保证的可读性,本文采用意译而非直译。

解析

一旦 CSS 被浏览器下载,CSS 解析器就会被打开来处理它遇到的任何CSS。这可以是单个文档内的CSS、<style>标记内的CSS,也可以是 DOM 元素的style属性内嵌的 CSS。所 有CSS 都根据语法规范进行解析和标记。解析完成后,就会生成有一个包含所有选择器、属性和属性各自值的数据结构。

例如,考虑以下 CSS:

.fancy-button {
    background: green;
    border: 3px solid red;
    font-size: 1em;
}

以上 CSS 片段将生成如下数据结构,以便在后续的过程中方便使用:

图片描述

值得注意的一件事是,浏览器将 backgroundborder 的简写还原成普通写法,也就是一个一个属性的声明,因为简单写主要方便开发人员的编写,但从这里开始,浏览器只处理普通写法。完解析成之后,浏览器引擎继续构建 DOM 树。

计算

既然我们已经解析了现有内容中的所有样式,接着就是对它们进行样式计算了。我们尝试尽量对所有值减少到一个标准化的计算值。当离开计算阶段时,任何维度值都被缩减为三个可能的输出之一:auto、百分比或像素值。为了清晰起见,让我们看几个例子,看 web 开发人员写了什么,以及计算后的结果:

图片描述

现在我们已经计算了数据存储中的所有值,是时候处理级联了。

级联

由于 CSS 来源有多种,所以浏览器需要一种方法来确定哪些样式应该应用于给定的元素。为此,浏览器使用一个名为 特殊性(specificity) 的公式,它计算选择器中使用的标记、类、id 和属性选择器的数值,以及 !important声明的数值。

通过内联 style 属性在元素上定义的样式被赋予一个等级,该等级优先于 <style> 块或外部样式表中的任何样式。如果 Web 开发人员使用 !important 某个值,则该值将胜过任何 CSS,无论其位置如何,除非还有 !important 内联。

同一级别的个数,数量多的优先级高,假设同样即比较下一级别的个数。至于各级别的优先级例如以下:

!important > 内联 > ID > 类 > 标签 | 伪类 | 属性选择 > 伪对象 > 通配符 > 继承

图片描述

选择器的特殊性由选择器本身的组件确定,特殊性值表述为5个部分,如:

0,0,1,0,1

(1)、对于选择器中给定的各个 !important 属性值,加 1,0,0,0,0 。

(2)、对于选择器中给定的各个ID属性值,加 0,0,1,0,0 。

(3)、对于选择器中给定的各个类属性值、属性选择器或伪类,加 0,0,0,1,0 。

(4)、对于选择器中给定的各个元素和伪元素,加 0,0,0,0,1 。伪元素是否具有特殊性?在这方面CSS2有些自相矛盾,不过CSS2.1很清楚的指出,伪元素具有特殊性,而且特殊性为 0,0,0,0,1,同元素特殊性相同。

(4)、结合符(+ > [] ^= $= 等等特殊符号)和通配符(*)对特殊性没有任何贡献,此外通配符的特殊性为 0,0,0,0,0。全是 0 有什么意义呢?当然有意义!子元素继承祖先元素的样式根本没有特殊性,因此当出现这种情况后,通配符选择器定义的样式声明也要优先于子元素继承来的样式声明。因为就算特殊性是0,也比没有特殊性可言要强。

为了说明这一点,让我们说明一些选择器及其计算后的权重数值:

clipboard.png

而当优先级与多个CSS声明中任意一个声明的优先级相等的时候,CSS 中最后的那个声明将会被应用到元素上。

在下面的示例中,div 将具有蓝色背景。

div {
  background: red;
}

div {
  background: blue;
}

现在CSS将生成以下数据结构,在本文中,我们将继续在此基础上进行构建。

clipboard.png

来源

CSS也有来源,但它们的用途不同:

CSS信息可以从各种来源提供,这些来源可以是 用户(user) 和 作者(author) 及 用户代理/浏览器(user agent),优先级如下:

用户样式

浏览器还允许用户设置网页的样式,例如,我们用IE浏览网站的时候,都可以通过浏览器查看菜单下的样式或者文字大小子菜单来设置网页实际的显示效果。

作者样式

网页创建者建立的样式表,一般会css文件出现或者是在页面头部里定义的style,也就是网站源代码的一部分。例如,大家看百度和谷歌的页面就不一样,这就是作者样式不一样的结果。

用户代理/浏览器样式

也就是浏览器自身设置用来显示网站的样式,不同的浏览器可能有不同的样式表,例如IE和Firefox的就不一样,所以大家分别使用这两种浏览器访问同一个网站的时候,看到实际效果可能就不同。

通常情况下,作者样式具有最高的重要性,其次是用户样式,最后才是浏览器样式,但是如果出现了 !important 标记的话,那么规则会被改变,通过 !important 可以提高某种样式的重要性,让它的优先级高于其他没有加该声明的所有样式。

让我们进一步扩展我们的数据集,看看当用户将浏览器的字体大小设置为最小 2em 时会发生什么:

clipboard.png

做级联

当浏览器拥有一个完整的数据结构,包含来自所有源的所有声明时,它将按照规范对它们进行排序。首先,它将按来源排序,然后按特性(specificity)排序,最后按文档顺序排序。

clipboard.png

从上图可知,类名为 .fancy-button优先级最高(表中越上面优先级越高)。例如,从上表中,人会注意到用户的浏览器首选项设置优先 于Web 开发人员的设置样式。现在,浏览器找到与选择器匹配的所有 DOM 元素,并将得到的计算样式挂载到匹配的元素,在本例中 div 为类名为 .fancy-button

clipboard.png

如果您希望了解更多关于级联的工作原理,请查看官方规范

CSS对象模型

虽然到目前为止我们已经做了很多,但还没有完成。现在我们需要更新 CSS对象模型(CSSOM)。 CSSOM 位于document.stylesheets 中,我们需要对其进行更新,以便让它知道我们目前为止已经解析和计算的所有内容。

Web 开发人员可能在没有意识到的情况下使用这些信息。例如,当调用 getComputedStyle() 时,如果需要,运行上面指出的相同过程

布局

现在我们已经应用了一个具有样式的 DOM 树,然后开始构建一个用于可视化目的的树了。这棵树出现在所有现代引擎中,被称为盒子树(box tree)。为了构造这棵树,我们遍历 DOM 树并创建零个或多个 CSS 盒子,每个盒子都有一个 marginborderpaddingcontent

在本节中,我们将讨论以下 CSS 布局概念:

  • 格式化上下文(FC):有许多类型的格式化上下文,其中大多数 Web 开发人员通过更改 display 元素的值来调用。一些最常见的格式化上下文是块(块格式化上下文或BFC),flex,grid,table-cells 和 inline。其他一些CSS也可以强制使用新的格式化上下文,例如 position: absolutefloat 或使用 multi-colum
  • 包含块:这是用于解析样式的祖先块。
  • 内联方向:这是文本布局的方向,由元素的书写模式决定。 在拉丁语言中,这是水平轴,在 CJK 语言中,这是垂直轴。
  • 块方向:此行为与内联方向完全相同,但与内联轴垂直。因此,对于基于拉丁语的语言,这是垂直轴,而在CJK语言中,这是水平轴。

解析 Auto

请记住,在计算阶段,维度值可以是三个值之一:auto、百分数或像素。布局的目的是在Box Tree中调整所有盒子的大小和位置,使它们为绘制做好准备。

下面示例可以更容易地理解Box Tree是如何构建的。为了便于理解,这里不显示单独的CSS框,只显示主盒(principal box)。让我们看看一个基本的 “Hello world” 布局使用以下代码:

<body>
<p>Hello world</p>
<style>
    body {
        width: 50px;
    }
</style>
</body>

图片描述

浏览器从 body 元素开始,生成它的主盒(principal box),它的宽度为50px,默认高度为auto

图片描述

现在移动到 p 标签并生成其主盒(principal box),并且由于 p 标签默认有边距(margin),这将影响正文的高度,如下所示:

图片描述

现在浏览器移动到 “Hello world” 文本,这是 DOM 中的文本节点。因此,我们在布局中生成一个 行内盒(line box) 。请注意,文本溢出了正文,我们将在下一步处理这个问题。

图片描述

因为加上“world”长度后实际长度比较设置大并且我们没有设置 overflow 属性,所以引擎会向其父级报告它在布局文本时停止的位置。

图片描述

由于父级已收到其子级无法完成所有内容布局的指令,因此它会克隆包含所有样式的 行内盒(line box),并传递该框的信息以完成布局。

布局完成后,浏览器会返回 box tree,解析尚未解决的所有基于 auto 或基于百分比的值。 在图中,可以看到正文和段落现在包含所有 “Hello world”,因为它的 height 设置为 auto

处理浮动 float

现在让布局变得更复杂一点。我们将使用一个普通布局,其中有一个按钮,内容为 “Share It”,并将其浮动到一段文本的左侧。浮动本身被认为是“shrink-to-fit” 上下文。之所以将其称为“shrink-to-fit”,是因为如果尺寸是自动的,则该框将围绕其内容进行收缩。

浮动盒子是与这种布局类型匹配的盒子的一种类型,但是还有许多其他的盒子,例如绝对定位盒子(包括 position: fixed)和基于自动调整大小的表格单元格,如下代码:

<article>
    <button>SHARE IT</button>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pellentesq</p>
</article>
<style>
    article {
        min-width: 400px;
        max-width: 800px;
        background: rgb(191, 191, 191);
        padding: 5px;
    }

    button {
        float: left;
        background: rgb(210, 32, 79);
        padding: 3px 10px;
        border: 2px solid black;
        margin: 5px;
    }

    p {
        margin: 0;
    }
</style>

图片描述

该过程开始时遵循与“Hello world”示例相同的模式,因此我将跳到我们开始处理浮动按钮的位置。

图片描述

由于浮动创建了一个新的块格式化上下文(BFC),并且是一个 shrink-to-fit 上下文,因此浏览器执行一种称为内容度量的特定布局类型。

在这种模式下,它看起来与其他布局相同,但有一个重要的区别,即它是在无限空间中完成的。在此阶段,浏览器所做的就是以 BFC 的最大和最小宽度布局 BFC 树。

在本例中,它使用文本布局一个按钮,因此其最窄的大小(包括所有其他CSS框)将是最长单词的大小。在最宽的地方,它将是一行的所有文本,加上 CSS Box。注意:这里按钮的颜色不是文字的颜色。这只是为了说明问题。

图片描述

现在我们知道最小宽度是86px,最大宽度是115px,我们将此信息传递回父类的box,让它决定宽度并适当地放置按钮。在这个场景中,有足够的空间来适应浮动的最大大小,这就是按钮的布局方式。

图片描述

为了确保浏览器遵循标准,并且内容围绕浮动,浏览器更改了 article 的 BFC 的几何形状。这个几何图形被传递给段落,以便在段落布局期间使用。

图片描述

从这里开始,浏览器遵循与第一个示例相同的布局过程——但是它确保任何内联内容的内联和块的起始位置都位于浮动所占用的约束空间之外。

图片描述

当浏览器继续沿着树向下移动并克隆节点时,它将越过约束空间的块位置。这允许最后一行文本(以及它之前的一行)以内联方向开始于content box 的开头。然后浏览器返回到树中,根据需要解析 auto 和百分数。

了解片段(UNDERSTANDING FRAGMENTATION

关于布局如何工作的最后一个方面是碎片化。 如果你曾经打印过网页或使用过CSS多列,那么你已经利用了碎片。 碎片化是将内容分开以使其适合不同几何形状的逻辑。 让我们来看看同一个例子,利用 CSS 多列情况:

<body>
    <div>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh orci, tincidunt eget enim et, pellentesque condimentum risus. Aenean sollicitudin risus velit, quis tempor leo malesuada vel. Donec consequat aliquet mauris. Vestibulum ante ipsum primis in faucibus
        </p>
    </div>
<style>
    body {
        columns: 2;
        column-fill: auto;
        height: 300px;
    }
</style>
</body>

图片描述

一旦浏览器到达 multicol 格式化上下文盒子,它就会看到它有一组设定的列。

图片描述

它遵循以前类似的克隆模型,并创建了一个具有正确维度的碎片处理程序,以满足作者对其列的要求。

图片描述

然后浏览器按照与之前相同的模式尽可能多地布局行,然后浏览器创建另一个碎片管理器,并继续完成布局。

绘画(Painting)

来回顾一下我们现在的情况,我们取出所有的 CSS 内容,对其进行解析,将其级联到DOM 树中,并完成布局。但是我们还没有对布图应用颜色、边框、阴影和类似的设计处理——处理这些过程被称为绘画

绘画基本上是由CSS标准化的,简单地说,你可以按照以下顺序绘画:

  • background;
  • border;
  • and content.

更多绘画的顺序可查看 CSS 2.2 Appendix E

因此,如果我们从前面的“SHARE IT”按钮开始,并遵循这个过程,它绘制过程大致如下:

图片描述

完成后,它将转换为位图,最终每个布局元素(甚至文本)都成为引擎中的图像。

 关于 Z-INDEX

现在,我们大多数的网站都不是由单一的元素组成的。此外,我们经常希望某些元素出现在其他元素之上。为了实现这一点,我们可以利用 z-index 的特性将一个元素叠加到另一个元素上。

这可能感觉就像我们在设计软件中使用图层一样,但是唯一存在的图层是在浏览器的合成器中。看起来好像我们在使用 z-index 创建新层,但实际上并不是这样,那么到底是怎么样呢?

我们要做的是创建一个新的堆栈上下文。创建一个新的堆叠上下文可以有效地改变你绘制元素的顺序。让我们来看一个例子:

<body>
    <div id="one">
        Item 1
    </div>
    <div id="two">
        Item 2
    </div>
    <style>
    body {
        background: lightgray;
    }
    div {
        width: 300px;
        height: 300px;
        position: absolute;
        background: white;
        z-index: 2;
    }
    #two {
        background: green;
        z-index: 1;
    }
    </style>
</body>

如果没有使用 z-index,上面的文档将按照文档顺序绘制,这将把 “Item 2” 置于 “Item 1” 之上。但由于 z-index 的影响,绘画顺序发生了变化。让我们逐步完成每个阶段,类似于我们之前完成布局的方式。

图片描述

浏览器以根框开头,我们在后台画画。

图片描述

然后浏览器按照文档顺序遍历较低层次的堆栈上下文(在本例中是“Item 2”),并开始按照上面的规则绘制该元素。

图片描述

然后它遍历到下一个最高的堆栈上下文(在本例中是“Item 1”),并按照 CSS 2.2中定义的顺序绘制它。

z-index 不影响颜色,只影响哪些元素对用户可见,因此也不影响哪些文本和颜色可见。

组成(COMPOSITION)

在这个阶段,我们至少有一个位图从绘画传递到合成。合成程序的工作是创建一个或多个层,并将位图呈现到屏幕上供最终用户查看。

此时一个合理的问题是,“为什么任何站点都需要不止一个位图或合成层?”,根据我们目前看到的例子,我们真的不会这么做。我们来看一个稍微复杂一点的例子。假设在一个假设的世界中,Office 团队想让 Clippy 重新上线,他们想通过 CS S转换让Clippy 跳动来吸引人们对他的注意。

动画 Clippy 的代码可以是这样的:

<div class="clippy"></div>
<style>
.clippy {
    width: 100px;
    height: 100px;
    animation: pulse 1s infinite;
    background: url(clippy.svg);
}

@keyframes pulse {
    from {
        transform: scale(1, 1);
    }
    to {
        transform: scale(2, 2);
    }
}
</style>

当浏览器读取 web 开发人员希望在无限循环中为 Clippy 添加动画时,它有两个选项:

  • 它可以返回到动画的每一帧的重绘阶段,并生成一个新的位图以返回合成器。
  • 或者它可以生成两个不同的位图,并允许合成程序仅在应用了该动画的层上执行动画本身。

在大多数情况下,浏览器将选择选项2并生成以下内容(我有意简化了Word Online为此示例生成的图层数量):

图片描述

然后,它将重新组合剪辑位图在正确的位置,并处理脉动动画。这对于性能来说是一个很好的优势,因为在许多引擎中,合成程序是在它自己的线程上的,这样就可以解除主线程的阻塞。如果浏览器选择上面的选项1,它将不得不阻塞每一帧以完成相同的结果,这将对最终用户的性能和响应能力产生负面影响。

图片描述

创造互动的视觉

正如我们刚刚了解到的,我们使用了所有的样式和DOM,并生成了一个呈现给最终用户的图像。那么浏览器如何创建交互性的假象呢?嗯,我相信你现在已经学过了,所以让我们看一个例子,用我们的 “SHARE IT” 按钮作为类比:

button {
    float: left;
    background: rgb(210, 32, 79);
    padding: 3px 10px;
    border: 2px solid black;
}

button:hover {
    background: teal;
    color: black;
}

我们在这里添加的是一个伪类,它告诉浏览器在用户悬停在按钮上时更改按钮的背景和文本颜色。这就引出了一个问题,浏览器如何处理这个问题?

浏览器不断跟踪各种输入,当这些输入正在移动时,它会经历称为命中测试的过程。 对于此示例,该过程如下所示:

图片描述

  1. 用户将鼠标移到按钮上。
  2. 浏览器触发鼠标已移动的事件,并进入命中测试算法,该算法本质上是问“鼠标正在触摸哪个 box”
  3. 该算法返回链接到我们的 “SHARE IT” 按钮。
  4. 浏览器会问这个问题:“既然有鼠标在你上方盘旋,我应该做什么?”。
  5. 它快速运行此框及其子框的样式/级联,并确定:hover 在声明块内部有一个仅使用绘制样式调整的伪类。
  6. 它将这些样式挂起 DOM 元素(正如我们在级联阶段所学到的),在这种情况下是按钮。
  7. 它跳过布局,直接绘制一个新的位图。
  8. 新的位图被传递给合成程序,然后传递给用户。

总结

希望这部分对你关于css 解析过程多多少少有点帮助,共进步!

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

你的点赞是我持续分享好东西的动力,欢迎点赞!

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

贝er 发布了文章 · 5月31日

【前端工程】单页应用与多页应用

单页应用与多页应用

单页应用与多页应用的优缺点

单页应用:首次加载单个 HTML 页面,在用户与应用程序交互时动态更新该页面的 web 应用程序。

多页应用:由多个完整的 html 页面组成,更新页面时会重新加载页面的所有的资源。

对比单页多页
优点js 渲染,局部刷新页面,页面切换速度快,减少请求数据,用户体验更好首页加载速度快,利于做 seo
缺点首次加载慢、不利于 seo每次加载页面都需要加载所有资源,切面切换速度慢,会出现卡顿空白问题。公用模块需要重复加载

单页应用实现

前端路由,这里主要分析vue-router 路由的两种模式:hash & history

hash模式

原理是onhashchange事件。

window.addEventListener('hashchange',function(e) {     
console.log(e.oldURL); console.log(e.newURL) 
},false);

通过window.location.hash属性获取和设置hash值。

由于 hash 发生变化的 url 都会被浏览器记录下来,所以浏览器的前进后退可以使用,尽管浏览器没有请求服务器,但是页面状态和 url 关联起来。后来人们称其为前端路由,成为单页应用标配。

hash 模式的特点在于 hash 出现在 url 中,但是不会被包括在 HTTP 请求中,对后端没有影响,不会重新加载页面。

history模式

主要利用了 HTML5 History Interface 中新增的pushState()replaceState(),它们提供了对历史记录进行修改的功能。

参考资料

单页应用和多页应用

spa 与 mpa 的对比

单页应用和多页应用的区别

查看原文

赞 1 收藏 1 评论 0

贝er 发布了文章 · 5月19日

【前端工程】基于React antd二次封装前端组件库的思考

组件库建立的意义

设计组件库好处是什么?

  1. 设计组件库能提高协作效率。(这是站在研发效率和时间成本的角度)
  2. 设计组件库能成为公司的技术资产,新同事来公司后可以快速调用和上手,就算有同事离职,也不会对产品的整体体验造成影响。(这是站在公司招聘成本的角度)
  3. 设计组件库可以让产品体验统一,有助于公司建立统一的产品品牌认知,方便销售和运营包装、运营、策划产品方案。(这是站在销售运营部门的角度)

组件开发思想

我们基于公司项目UI做定制化的组件库具体在做什么?

1、样式上:基于开源框架的组件封装成公司的产品UI设计的特有样式的组件,以及封装常用的布局样式。
2、功能上:沉淀开源框架中没有的常用组件。

开发准则

  • 小步快跑快速迭代
  • 测试组件库的可用性

组件的类型

组件的种类可以分为哪些?
  • 按照逻辑和视图划分:容器型组件(Container)与展示型组件(stateless)
  • 按照状态划分:有状态组件和无状态组件
  • 按照 UI 划分:布局组件和内容组件
  • 纯组件和非纯组件

容器型组件(Container)与展示型组件(stateless)
前者关注逻辑,后者关注视图UI,两者的区别,3个方面来详情区分:关注点、数据源、组织形式。

image.png

展示型组件比如:一个菜单栏组件,接收props传递的数据,只用来渲染数据展示UI。

image.png

import  React  from  'react'

import  'antd/es/menu/style'

import { Menu } from  'antd'

import { MenuProps } from  'antd/lib/Menu'

import  './side-menu.less'

import { colorMap } from  './util'

class  SideMenu  extends  React.PureComponent<MenuProps, any\> {

render() {

const { ...other } = this.props

const  props: any = this.props

let  header = null

let  theme = props.titleTheme

let  styleName = colorMap\[theme\] ? colorMap\[theme\] : colorMap\['default'\]

  

if (props.title) {

    header = (

    <div  className\={\`side-menu-title ${styleName}\`}\>

    <span\>{props.title}</span\>

    </div\>

    )

}

  

return (

    <div  className\="panda-side-menu"\>

    {header}

    <Menu  {...other}  style\={{ flex:  'auto', overflowY:  'auto' }}\>

    {props.children}

    </Menu\>

    </div\>

  )
 }
}

export  default  SideMenu

容器组件比如:一个带搜索功能的Tree组件,由多个展示组件构成,同时包含了获取tree数据源的请求逻辑。

image.png

分离逻辑和视图的主要方式有:

  • hooks
  • 高阶组件
  • Render Props
  • Context

布局组件和内容组件

开发思想

基于antd二次封装组件或自定义antd没有的组件。

基于antd二次封装什么?

在保证完全具备原有antd具备的组件功能的情况下封装如下组件:

  • 基于antd组件封装公司内部设计的独有样式组件
  • 基于antd组件扩展业务组件
  • 沉淀常用业务模版

通用组件设计原则
单一原则

未完待续。。

参考资料
React组件设计实践总结02 - 组件的组织
如何用项目思维,从0-1建立设计组件库
前端组件设计原则
React组件化开发

查看原文

赞 0 收藏 0 评论 0

贝er 发布了文章 · 5月17日

【复盘】ios部分机型页面白屏问题

前言

 本篇主要记录我解决ios10.0+白屏问题的过程,内容包含如下:
1、ios移动端线上debug方式
2、ios10利用webpack打包uglifyjs-webpack-plugin压缩白屏问题

* swiper包问题排查
* uglifyjs-webpack-plugin依赖的uglify-es不能压缩Es6问题排查
* 替换成terser-webpack-plugin
* 替换成babel-minify-webpack-plugin
* 替换成terser-webpack-plugin-legacy

ios移动端线上debug方式

找原因并验证解决问题

现有问题

image.png

找原因

通过收集可能的原因

基于第一个问题:

* swiper打包问题
* 现用uglifyjs-webpack-plugin不能压缩Es6问题

验证&解决问题

项目中没有使用swiper,删除swiper相关包,排除影响先定位uglifyjs-webpack-plugin不能压缩Es6问题。

参考syntaxerror-cannot-declare-a-let-variable-twice的解答 ,是因为压缩包不能压缩Es6,现有terser-webpack-plugin插件配合webpack可以压缩Es6。
利用terser-webpack-plugin复现包不能压缩Es6,不兼容safari10的情况:

main.js:

import Vue from  'vue'
import App from  './App.vue'

let a = 2222;
let arr = \[2, 3, 4, 5, 6, 7\]
for (let a = 0; a < arr.length; a++) {
  console.log(arr\[a\])
}
console.log(a)
new  Vue({
el: '#app',
router: '',
render: h  \=>  h(App)
})

webpack.config.js:

const path = require('path')
// const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
entry: {
  app: './src/main.js'
},

devtool: 'inline-source-map',
devServer: {
    contentBase: './dist/js',
    hot: true
},

output: {
    filename: '\[name\].bundle.js',
    path: path.resolve(\_\_dirname, 'dist/js'),
    publicPath: ''
},

module: {
rules: \[{
    test: /\\.vue$/,
    loader: 'vue-loader'
},
{
    test: /\\.css$/,
    use: \['style-loader', 'css-loader'\]
},

{
    test: /\\.(png|jpe?g|gif|svg)(\\?.\*)?$/,
    use: \['url-loader'\]
}
\]
},

plugins: \[
// make sure to include the plugin for the magic
new  VueLoaderPlugin(),
new  HtmlWebpackPlugin({
  title: 'Output Management'
}),

new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new  TerserPlugin({
    sourceMap: true,
    parallel: 4,
    terserOptions: {
    compress: {},
    mangle: true,
    // safari10: true,
    }
})
\]
}

image.png

打开safari10: true确实可以解决这个问题。

可以确认是因为safari 10.0+ 不支持通过let定义重复的变量名,let也没有被转化成es5的语法。

换支持压缩Es6的压缩包

  • 更改uglifyjs-webpack-plugin的依赖uglify-es至3.2.2版本
    参考解决方案: webpack uglify
  • 更改其他支持压缩Es6的压缩插件:

    • 替换成terser-webpack-plugin
    • 替换成babel-minify-webpack-plugin
    • 替换成terser-webpack-plugin-legacy

最终解决方案
基于现有项目以上方案都未解决,最后通过yarn uprade升级了所有的包才解决问题。

基于upgrade uglify-es这个解决方案试着解决undefined is not an object,意外将所有的bug解决了。

几种支持压缩Es6的压缩插件

uglifyjs-webpack-plugin
uglifyjs-webpack-plugin@^1.2.7

terser-webpack-plugin
需要webpack4.0+,如果从webpack3.0+升级到webpack4.0+需要对依赖包做兼容上的修改。

webpack4.0+基于webpack3.0+做了哪些升级?

未完待续。

参考资料

webpack uglify

webpack升级详情

探讨npm依赖管理之peerDependencies

查看原文

赞 0 收藏 0 评论 0

贝er 发布了文章 · 5月9日

【前端工程】前端打包之webpack(一)

认识webpack

webpack_是一个现代 JavaScript 应用程序的_静态模块打包器(module bundler)_。当 webpack 处理应用程序时,它会递归地构建一个_依赖关系图(dependency graph)_,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个_bundle_。

webpack打包初步

1、创建文件、初始化化项目生成package.json、安装webpack、webpack-cli(可以在命令行执行webpack)

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev

2、创建目录
创建src/index.js、dist/index.html
image.png

最初我们都是通过非模块化开发方式开发一个应用页面,将逻辑写在js文件中,然后在html中通过script引入js文件。但当应用程序越来越复杂,页面需要引入的js文件就会越来越多,在浏览器端每一个文件的加载都会做一次请求。而webpack的好处之一就是可以将应用程序依赖的所有的包打包成一个js文件供index.html主页面引用,大大减少了加载的文件数量。

3、执行npx webpack 打包文件

npx会执行webpack包中的二进制文件,默认将src中的index.js作为应用程序的文件入口,将所以依赖的文件打包成一个main.js输出到dist文件夹中。index.html引入main.js文件就可以访问到页面内容。

4、自定义webpack配置文件

对于复杂的应用程序需要更复杂的配置可以通过创建webpack.config.js文件来自定义配置。

例如自定义入口文件、输出文件的名称和目录等等。

const path = require('path')
module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
}

5、package.json中配置脚本

scrips中配置命令行,npm run build代替npx webpack 打包文件。

image.png

执行npm run build就会将entry指定的入口文件./src/index.js中的内容打包到指定的dist/bundle.js中。

6、自动刷新页面

参考资料:

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 11 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-12-31
个人主页被 387 人浏览