复杂庞大的知识体系大部分是建立在几个基础概念或理论之上,比如,经典力学建立在“牛顿三大定律”的基础上,整个几何学建立在几条公理之上,产品经理的知识体系建立在用户、需求、产品框架上,光学的知识体系建立在光的本质、传播和光与物质作用上。所以只要找到这几个元理论或者元概念,就可以重新推导整个知识体系。

前端的知识体系肯定也是一样。这篇文章是一个不一样的八股文准备思路,目的是重塑你的前端知识体系,而且是有意义的深刻记忆。每个人都应该用这个方法自已推导下。

什么是第一性原理?

第一性原理就是事物的本质,也就是最基本的组成部分。这个概念来自于古希腊哲学家亚里士多德的思想。当然很多听到这个概念并不是学哲学学到的,很可能是从老板同事学长学姐口中听到过,或者从马斯克的短视频切片中刷到过。

了解这个概念确实很有用,因为专业知识很琐碎又很广大,全记住肯定不可能,而且你工作中用到的知识只是几条路径的不断重复,所以最好得掌握一些方法和捷径去学习这个庞大的知识库。

我记性很差,我已经失去了小学轻松背古诗词的能力,遇到新概念,我必须得加工一下形成自己的理解,然后小心翼翼地放在我大脑的知识树上的某个节点上,不然新概念只是一堆名词,一段时间就忘了。

所以我很喜欢用第一性原理这个方法。

第一性原理的实践步骤是这样的:

  1. 定义问题。问题组成,包含什么假设,问题元素的准确定义
  2. 明确目标。做什么用,解决什么问题,目标是什么
  3. 考虑最基本的原理和事实。是由什么组成的
  4. 基于基本原理,重新推导

举个例子,如果想要设计一款更加节能的汽车。传统上,设计者可能会基于已有的汽车设计框架来改进,例如提升发动机的效率,或者减少车身重量。但从第一性原理出发,你需要重新定义问题,考虑以下基本事实:

  • 交通工具的基本功能是提供运输,并且遵循物理定律。
  • 车辆的能耗与速度、重量、空气阻力、以及动力来源等因素有关。

然后,通过重新审视这些最基本的原理,你可以提出不同的创新方案,可能包括:利用电力而非传统燃油、设计更为流线型的车体以减少空气阻力,甚至考虑全新的动力系统(如氢燃料电池)等。

另外,用第一性原理可以破解很多虚无的千古难题,比如分析“人之初、性本善还是性本恶”这个问题。用第一性原理是这样思考的:

  1. 定义问题。

    • 问题:“人之初,人的本性究竟是善还是恶?”,这是在讨论人类在出生的时候是否是具备某种先天的善的性格倾向,还是恶的性格倾向。
    • 准确定义:人指什么?什么是初?人可能指的是人类这个整体,也可能是具体的个人;对应的初,是指人类进化的起点;人的出生起点
    • 假设:人的性格在初时就是预先确定的,要么善,要么恶;人性是可以归纳为某个单一的“本质”;人有一个明确的初始点
  2. 明确目标:回答善还是恶,或者颠覆命题

  3. 最基本的事实

    • 进化论:进化树是一个多分枝、多样化的分裂过程,人类的起点没有一个明确的节点定义
    • 生物学:人是生物体,性格受环境和基因影响,如果一个具体的人的起点是出生点,他的性格也是受基因和环境影响的
    • 心理学:人从出生后的情绪反应,比如哭泣、依附,这些是善恶的表现吗
    • 分析哲学:善和恶是道德框架下的讨论,性是心理框架的讨论,初是物理框架的下的讨论,拼凑在一起没有什么逻辑
    • 社会学:人是社会群体,个体与社会的互动多样且复杂,问这个问题目的是寻找一个确定答案来简化社会相处模式的倾向?
  4. 基于基本原理,重新推导

    • 问题不明确,无法回答
    • 替代问题:人的性格,在什么基因和环境的作用下,更倾向于对某个具体的人有恶的表现,和有善的表现

可以发现第一性原理很容易颠覆初始命题,破坏已有路径,这篇文章的目的是重新演绎和梳理前端的发展脉络,所以我当然不是严格的第一性原理去颠覆和重新开辟路径,况且我也没有这个能力,但是第一性原理的“从基本的事实出发”这一点很有启发,所以我可以这样做:

  1. 整体了解前端的发展路径
  2. 找到前端的起点和基本事实,这个环节我一般最多找3个,应为容易记忆
  3. 基于事实解释和总结前端发展的每个环节

前端=结构+表现+交互

现在我要找一个落脚的起点。

什么是起点呢?如果从宇宙大爆炸讲起太夸张了,我得花很长篇幅才能圆到和前端的联系上。

所以还是简单点,从我们日常出发,想象你睁眼看这个世界的过程,你是如何看到眼前的东西,你又是如何被吸引注意力的,这些东西为什么在你的大脑有了含义。

这些问题又可以套“人类的生理与心理基础+客观物理世界”这个公式来回答,具体来讲,人的身心结构决定了我们是怎么接受、处理外界信息,并且做出反应的。

人脑接受外界信息的过程,类似一个从输入到输出的系统,主要分为以下几步:

  1. 感知。就是用你的感官系统,视觉、听觉、触觉等接收外界的刺激信息。比如,通过眼睛看到一个物体的形状、颜色和大小,这就是我们对“外界内容”的初步感知。
    我们感知到的大部分信息来自视觉系统,人脑特别擅长通过视觉系统将世界分解成不同的元素,然后识别结构和模式,比如物体的空间位置和大小形态,这些是信息的基础构成。也就是意义的起点。

  2. 认知。感知到的信息会进入大脑的认知系统进行解读、分类和组织。例如,当看到一组物体时,我们可以识别出物体的大小差异,还会找相同点,还会调用大脑的知识和经验进行联想,对物体赋予“意义”。

  3. 行为。基于感知和认知的信息,人类会做出相应的反应。比如你看到火,就知道这个东西烧到会痛,就会避开它。

行为可以直接改造外界世界,塑造成我们大脑熟悉的样子,之后,外界世界反过来又进一步成为我们感知信息的来源,然后我们又通过感知-认知-行为这条路径对外界世界做出改变。这种无终止的动态互动,每当我意识到,就觉得amazing。

当然,更让人惊叹的是感知-认知-行为和前端的紧密联系

感知这一层就是结构和模式,和前端的结构层很相似,比如我们用文本、图片、音视频这些标签对内容进行分类;认知和前端的表现层很相似,赋予含义就相当于给基本内容加上颜色、阴影和动画,这些信息使我们对基本内容有更深的感知。

行为层就是前端的交互层,通过点击滚动等行为和页面进行互动,页面变化,用户的心智也会产生变化。用户变化的心智进而又影响页面结构和布局。

所以,写到这里,我们可以总结一下:人类与外界世界的无限互动机制来源于感知、认知和行为这条路径;前端与用户的无限互动机制来源于结构、表现和交互这条路径

我们暂且认为结构、表现和交互是前端知识体系的元概念,优质的结构、极致的表现和流畅的交互是前端的基本事实

无限互动 infinitely engaging 太让人着迷了,而且我特别喜欢engage这个单词,这个单词对我来说很有魔力,一个原因是它的发音/ɪnˈɡeɪdʒ/,每个音节发音都很清晰,不会糊成一团,有点像色彩理论的正交色,更重要的原因是它的第一个发音in,读起来像“引”,很容易让我联想到吸引力,正如这个单词本身的意思,吸引、交涉、投入、陷入…,适用的场景很多,比如吸引注意力、投入精力做某件事情、订婚(形象地无法用中文表达)。总之,这个单词对我有力的作用,让我无法抵抗。

Web = 网络基础设施 + 通信协议 + 资源表达

在互联网之前,书是主要的信息载体。书的内容呈现也离不开结构、表现和交互这个框架。

  1. 结构。书本上,文字的排列、章节的组织、段落和标题的设置可以看作是“结构”的体现。书籍通过文本来构建信息的框架。
  2. 表现。排版、字体、封面设计、插图等是信息的表现层。它们不仅影响信息的可读性,还决定了书本上内容的视觉效果。
  3. 交互。书本身是静态的,但我们可以通过翻页、做笔记和书进行互动。

但是书能传递的信息非常受空间和时间的限制,有了互联网后就不一样了,信息可以以光的速度传播,通过网络七层模型,最终在最顶层应用层Web进行信息展示和交互。

从第一性原理来看,Web可以解构成什么?

首先,Web的核心是互联上的信息交换。信息交换多方参与,客户端和服务器。客户端和服务器通信涉及到通信协议和信息表达,通信协议是HTTP或者HTTPS协议,信息表达是HTML, CSS, JavaScript,HTML用来构建网页结构,CSS负责样式,JavaScript处理交互。

样式和交互对于本质的Web来说不重要,所以应该不是Web的第一性原理,但是考虑到现代Web发展,表现和交互深入人心,必不可少,所以还是把它考虑上。

Web是建构在互联网上的,所以离不开网络基础设施。

所以Web = 网络基础设施 + 通信协议 + 资源表达

回顾一下早期的Web,是用来分享和展示研究文档的,Tim Berners-Lee 发明了HTTP超文本传输协议和HTML超文本标记语言,用户可以在互联网上点击超链接浏览不同的文档,信息在互联网上通过超链接实现了关联,像一个网状结构。

为什么需要HTTP协议?因为底层的TCI/IP协议只能传递数据,只具有运输能力,为了让数据在应用层有含义,就需要一种协议规范数据格式、描述数据内容,以及规定双方如何沟通和处理这些数据。

而超文本标记语言就是用来展示内容的,通过标记语言描述文档的组成部分。

所以,早期的Web是建构在底层网络设施之上的 HTTP + HTML。

Web发展到现在,HTTP协议已经进化出了HTTP1, HTTP2, HTTP3和HTTPs,资源的表达对于浏览器来讲主要还是HTML, CSS和JS,后面又有Web components, Web assembly,而且对于开发这来讲,主要是框架开发范式,而不是写原生三件套了。

浏览器 = 网络解析 + 渲染 + 执行JS

浏览器的出现是为了解决Web上信息的展示和交互问题,从这个角度看,浏览器就是在Web上的遵守HTTP协议,解析和渲染资源的图形化工具。所以浏览器的核心至少包含网络处理和资源处理。

浏览器是我们获取互联网信息的第一个窗口。访问网页的一个简单的路径是地址栏输入url链接,按输入键,几秒后就会内容在页面上展示出来。

网页内容是由HTML, CSS和JavaScript编写的,输入的URL必须联网才能获取到页面。所以这样看,浏览器最基本的功能就是处理网络请求,解析和渲染HTML, CSS资源,执行JS 。

这三个基本点是技术角度的第一性原理,也就是在当前技术背景下,解决互联网上信息的结构、表现和交互的答案。用第一性原理分析浏览器的核心目标,那就要紧贴前端的核心目标结构、表现和交互来回答,也就是高效获取资源、精准解析内容、流畅实现交互。

前端八股文关于浏览器最重要的问题就是浏览器的渲染机制,讲到这里,你应该就明白了为啥这个问题这么重要。因为前端=结构+表现+交互,浏览器是前端表达的容器。

知道了浏览器技术实现的第一性原理,还有Web这个公式,你就能有了回答这个问题的框架,剩下的细节就是你需要记忆的步骤,当然如果你不想记忆太多,那就继续用第一性原理递归解构,比如我记浏览器渲染步骤,主要就是三个:两个产物DOM, CSSOM;一个公式DOM+CSSOM=render tree;布局和渲染;执行JS。产物继续分解就是AST树的构建,也就是词法分析,语法分析;执行JS继续分解就是编译、解释加执行。

讲到这里,我有一个问题,为什么CSSOM 要作为单独的一个产物来控制页面元素的样式?CSS 不能脱离内容而存在,我们喂给浏览器的HTML代码,为什么不先组装上内联的样式?这个问题又可以用前端的第一性原理来看,因为表现和结构一样重要,必须有相同的自由度,CSS单独解析成CSSOM是为了更好地实现表现能力和更优的性能。

知道了浏览器的第一性原理,可以用来理解前端新技术发展趋势,比如Web assembly是JS 引擎的扩展;Webgpu是渲染引擎的升级,pwa 是网络模块的增强,shadow DOM和Web components 是渲染引擎的封装能力提升。

所有新特性均围绕资源获取、内容解析、交互实现这核心三要素展开,通过底层能力增强或高层抽象简化,最终服务于更优质的结构,更极致的表现,更流畅的交互的Web体验。

HTML, CSS, JavaScript

HTML, CSS, JavaScript是前端三剑客,你可以不知道浏览器原理,也可以不懂Web的底层协议,但是你至少得懂这三样你才能开发页面。

HTML, CSS, JS直接作用于前端的结构、表现和交互,三足鼎立。如何解构这三种代码呢?

HTML = 标签+属性+DOM

标签是内容的容器,属性是标签的额外信息和功能,比如 src、href 属性链接外部资源,还有和CSS建立关联的选择器属性。

DOM是HTML的抽象概念,有两个作用,一个是浏览器解析产物,另一个是JS操作结构的接口。

各种意义化标签,还有链接资源的属性。就是为了让结构更加丰富,承载更多的内容。

CSS=选择器+属性和值+盒子模型

CSS的目标是样式和布局,所以肯定还有描述样式的属性,为了和HTML元素关联,还需要选择器,这个时候选择器的优先级成为了代码复用的副产物。

属性从表现角度看,可以分为三类:静态样式、动态样式和布局属性。静态样式是颜色、字体、边框,这些属性在页面加载后保持固定不变;动态样式就是静态样式加上时间的维度。布局分为二维平面,层级和时间。

盒子模型是布局的基础,浏览器靠盒子模型计算每个元素的大小、位置和边界,开发者也要理解这个机制来避免布局问题。布局可以把所有元素简化成盒子,最简单的布局思想是基于平面坐标系的绝对定位,但是CSS 设计很神奇,有基于文档流的流式布局,基于响应式这个目标,CSS会有flex布局和grid布局,还有媒体查询,容器查询。

JS = 事件驱动和异步处理+操作DOM+运行在浏览器上

页面的交互本质是监听用户的动作,比如输入、点击、滚动,然后动态修改页面结构和表现。从第一性原理出发,浏览器需要怎么样的语言实现交互。

第一点,这门语言肯定得擅长事件驱动和异步处理。事件驱动是交互的来源,而且用户行为不可预测的,可以随时点击、输入、滚动,因此这门语言得响应各种异步,能够在不阻塞其他任务的情况下处理这些事件。

第二点,能操作DOM。交互不仅仅在于监听事件,还要根据用户行为动态修改页面结构和样式。浏览器解析 HTML 生成 DOM 树,语言必须能拿到这个DOM。

第三点,运行在浏览器上。

还有,这门语言必须具备图灵完备性,能够执行复杂的计算和逻辑,支持所有必需的编程范式,从而实现丰富的交互逻辑。

这门语言就是JavaScript。

所以为什么八股文关于JS的事件循环和异步很重要,因为这是实现交互的基础。

关于交互,有一个很重要的前端发展历史节点:有了ajax 技术出现,Web1.0变成了Web2.0,在写这篇文章之前,我不理解,我只是纯机械式记忆局部刷新,动态交互,不会重载这些关键词。

这次推导过程中我才有了深刻感悟。用第一性原理,是这样思考的:前端的交互需求最重要的是满足什么?是元素的显影吗?是页面的导航吗?是页面的下拉滚动吗?元素的忽闪忽现,在页面上翩翩起舞,像动画一样优美灵动吗?

都不是,这些只是精彩的交互方式,可以增强用户体验,但是不是交互的核心目标,交互最根本的目标是快速响应与反馈,也就是说要保证用户在执行任何操作后能够获得即时反馈。

从这个角度看,ajax的异步请求和局部更新完美地解决了页面重载痛点,成为Web发展的转折点确实不为过。

除了事件循环和异步特性,JS和其他编程语言差不多,剩下就是变量和函数。

对了,闭包和原型链是JS的高阶知识点,八股文也特别喜欢考,喜欢考就考,因为可以理解,不考这个JS 就没其他更高级的特性了。但是我想了好久也没想出来这个和前端的交互什么关系。

最后我只能牵强地这样圆一下:闭包和原型链功能很强大,可以让前端开发们写JS 更灵活,把JS写好是给快速响应用户交互的重要一步。

为什么JS要面向对象范式呢?这一点我不强硬和交互挂边了。看看了看前端历史,我的结论是创造者的私人乐趣和历史有限的考量,就像取名java一样,其实和java没有关系,但是当时编程语言java和c++很火,可以蹭热度,还有这些语言都是面向对象编程,JS 也支持面向对象可以让减少程序员的学习曲线,让更多人投入进来促进前端的发展。

Brendan Eich,谢谢你,给我带来的学习包袱。

不过情有可原,因为计算机是人造系统,设计受限于历史包袱和开发者偏好,这很正常,确实和物理学不一样,只看客观事实,自然规律是怎么样就是怎么样。

复杂度上升=代码复杂度+流程复杂度+系统复杂度

在大量业务刺激下,前端开发的复杂度不断上升。前端技术开始了从三件套到 jQuery 再到现代框架的演进。

前端开发的复杂度可以分成代码复杂度,流程复杂度和系统复杂度。

代码复杂度指的是代码本身的结构、逻辑、可读性、可维护性的复杂度。流程复杂度是项目开发流程的复杂度,比如开发流程、前后端协作、文档管理、需求变更管理。系统复杂度是项目级的复杂度,比如不同业务域的应用管理。

工程化=抽象化与建模+标准化与模块化+自动化与流程化

复杂度上升的解决办法是工程化。

工程化本质上就是把一个零散的没有结构的解决方案,转化成一个系统化、可重复、可扩展、可管理的“工程”。如果从这一最基本的原理看,工程化的标准路径是这样的:

  1. 抽象化与建模(理解问题本质、建立模型)
  2. 标准化与模块化(制定统一规范,分解系统)
  3. 自动化与流程化(利用自动化工具,建立标准流程)

下面我们按照这个思路解读下前端是怎么通过工程化的手段解决代码、流程和架构复杂度的。

代码复杂度

主要分为代码本身膨胀和依赖关系混乱。

  1. 抽象化与建模:组件化、模块化、MVVM、状态管理

  2. 标准化与模块化
    框架、组件库、npm 工具包,声明式开发这些框架开发范式;lint格式化,类型检查和团队CR规范这些编码规范。

  3. 自动化和流程化
    工具链、脚手架、打包构建、CICD、日志自动采集和监控

流程复杂度

抽象与建模:职责明确、业务流程明确

  1. 标准化和模块化:标准化文档模版、瀑布流、敏捷开发模式

  2. 自动化和流程化:站会、钉钉、月排

系统复杂度

前后端接口约定、自动化文档生成、Monorepo、微前端、自动化接口测试、

性能优化=压缩+缓存+懒加载

上面讲的内容都是一条走到底的前端发展路径,最后前端还剩下性能和安全这两个渗透在各个环节方方面面的两个点。

性能是一个很抽象的词,不同技术的性能指标不一样,比如算法的性能是时间资源和空间资源。如果没有一个体系的建构前端知识体系,我理解的前端性能优化的目标简单讲就是让用户更快看到页面内容,减少白屏。

但是自从有了前端=结构+表现+交互,我觉得前端的性能指标应该是更快地看到内容,表现和交互。这个描述还不具体,好在谷歌给了三个的具体指标定义:

  1. 最大内容绘制(LCP):最大内容绘制时间,用来衡量页面加载性能,和结构对应。好的性能是页面上的最大内容在页面开始加载后2.5秒内完成渲染。

  2. 累计布局偏移(CLS):累计布局偏移得分,用来衡量页面的视觉稳定性,和表现对应。理想状态下,页面在加载过程中布局应保持稳定,其CLS得分应低于0.1。

  3. 交互到下一绘制(INP):交互到下一绘制帧的时间,用来衡量页面的响应性,和交互对应。好的性能是要求用户交互后的响应时间在200毫秒以内

根据这三个指标,我们可以梳理下前端性能优化的主要方法。

1. LCP

代码优化: Tree Shaking, Code Splitting,Minification,Web Workers,
资源优化:图片优化,字体优化,资源懒加载,预加载与预渲染
网络优化:CDN 加速,HTTP/2 或 HTTP/3, 强缓存、协商缓存缓存策略,
渲染优化:减少重排与重绘,虚拟列表,服务端渲染(SSR)或静态站点生成(SSG),GPU加速合成,虚拟DOM、虚拟列表

2. CLS

资源预占位、动画和过渡设计

3. INP

节流防抖,任务分片和调度

安全=输入安全+代码安全+执行安全

从第一性原理的角度分析前端的安全,可以从最基本的输入、输出、执行这三大要素出发,从根本上说,前端的安全就是对输入、代码和执行的控制。

这三个核心点可以结合结构层、表现层、交互层来分析:

结构层

输入安全和输出控制

  • 输入验证和输出转义。确保所有用户输入在生成HTML之前进行严格的验证和转义,防止恶意脚本注入,从而避免 XSS(跨站脚本攻击)、SQL 注入(通过前端触发)。确保输出可信,对输出进行转义,防止未转义的 HTML、JavaScript 或 URL 导致代码执行。

  • 内容安全策略CSP。通过 HTTP 头限制脚本来源,阻止内联脚本执行。

表现层

在这一层,安全的目标是防止恶意第三方内容影响页面样式和用户体验。

  • 沙箱机制:通过将第三方内容隔离到沙箱环境中,比如广告,评论区,确保它们的执行不会干扰页面的核心内容。例如,通过 <iframe> 的 sandbox 属性限制第三方脚本的行为,禁止执行 JavaScript、提交表单、改变页面等操作。

  • 水印:可以用来保护版权,防止内容被盗用和转载,可以在表现层夹水印,但是要考虑水印被删除问题。

交互层

这个层面主要内容是如何安全地处理事件、数据传输以及如何执行代码。

  • 同源策略(SOP):限制不同源的脚本访问 DOM、Cookie 等资源。
  • 跨域资源共享(CORS):通过 HTTP 头(如 Access-Control-Allow-Origin)精细化控制跨域请求。
  • 沙箱化执行环境:通过 iframe 的 sandbox 属性隔离第三方内容。
  • 第三方依赖风险:npm 包供应链攻击(如恶意代码植入)。
  • 动态代码执行:eval()、new Function() 或 innerHTML 引入不可控逻辑。
  • DOM 型 XSS:因操作 DOM 触发的非预期脚本执行。

总结

这次梳理总的来讲很费脑,但是很有意思,其实还有很多点在写的过程中没有思考清楚,后面还需要花时间在思考下。不过这个版本的文章就到此为止了。