前端技术架构与工程
2020-04-16 11:10:14 4 举报
AI智能生成
《前端技术架构与工程》阅读笔记
作者其他创作
大纲/内容
3. 组件化/设计子模块
组件化旨在解决的主要现实问题
解耦和复用
组件和模块的定义和边界
自然语言
component 是比 module 粒度更细、更小的片段
软件编程
前端领域内模块和组件的语义为:
- 模块和组件均为可分离的、有独立功能的一种封装对象
- 模块强调功能性,并非一定与视图相关。一个完整的应用程序由多个模块组成
- 组件强调组合性,是一个视图片段的逻辑抽象。粒度比模块细,一个模块可包含一个或多个组件
应用程序
模块
组件
对象
编程语言基元
Web Components 规范解读
是 W3C 推出的一套用于封装具有复用性、互用性前端组件的技术规范,旨在提供一种标准的组件化模式。
是一系列技术的集合
自定义元素(Custom Elements)
创建自定义 HTML 元素或扩展内置元素
前端组件三要素
结构
表现
行为
缺点
缺少与全局隔离的命名空间
Shadow DOM
创建隔离作用域,实现 DOM、样式和逻辑的封装
与 iframe 的区别
iframe 封装了一个完整的执行上下文
Shadow DOM 封装了一个较轻量级的局部作用域
缺点
CSS 代码和 HTML 结构混在在组件的内部逻辑中,
长字符串令代码过于臃肿,缺乏良好的可读性和可复用性
长字符串令代码过于臃肿,缺乏良好的可读性和可复用性
HTML template
是一个有既定格式的处理器,输入数据输出 HTML 字符串
扩展组件
slot 机制
缺点
使用时,要引入 JS 文件并在 HTML 文档中编写 <template> 结构才能使用自定义元素 <my-dialog>。
组件源码和业务代码耦和在了一起。
组件源码和业务代码耦和在了一起。
更友好的编码方式
优化
将组件源码和业务代码分离,外层业务代码只需要引入一个 JS 文件即可使用对应的组件
对于组件本身,基于结构、表现和行为分离的原则,保持其 CSS、JS 和 HTML 代码的相对独立性
分离后的组件源码需要经过转换后才能正常工作
compile.js
编译器
需要使用 node 中的 fs 模块读取文件
runtime.js
能在运行时将<template>元素注入 HTML 文档中
组件源码的组织结构
多文件组件
CSS、JS 和 HTML 模版分别为独立的文件
单文件组件
将 JS、CSS 和 HTML 汇总在一个文件中编写
其中使用到了 jsdom 将文本转化为 document 对象
优缺点
缺点
核心围绕 DOM 展开,数据传递依靠 DOM 的属性,Shadow DOM 和 <template> 本质上也是 DOM。
自然而然具有 DOM 的一些特性和限制:
自然而然具有 DOM 的一些特性和限制:
- DOM 属性只能传递 String 类型的数据
- 浏览器无法理解自定义元素的语义??
- DOM 操作性能消耗大
优点
自定义元素的生命周期设计理念
前端组件的设计模式
减少 DOM 操作
具体代码编写细节
利用浏览器执行帧机制,尽可能批量处理对 DOM API 的调用
架构模式
借助 Canvas 取代一部分 DOM
虚拟 DOM
生命周期的设计艺术
命名
由于 JS 的单线程特性,钩子函数的命名绝不能使用正在进行时
注意
DOM 自身的一些钩子函数,如 onload、onerror 等,在对应事件完成之后被触发
总结
Web Components 规范对前端组件化的引导意义远大于现实意义
为前端组件化制定了相对统一的模式和方向
自定义元素的生命周期理念也为组件的设计提供了优秀的参考
前后端分离
前后端分离的助力
前端技术的演进
交互逻辑复杂度的提升
是面向对象思想中关注点分离原则的一种实践模式
考虑因素
业务特征(核心)
人员配备
组织架构
关注点
HTML 渲染(前后端耦和最紧密的环节)
会话管理??指的是 session 管理?
用户认证和鉴权
跨域处理
关注点分离
(前后端分离在
业务、架构
和工程角度
的不同关注点)
(前后端分离在
业务、架构
和工程角度
的不同关注点)
前后端的两个关联
数据接口
借助 AJAX 可以实现数据接口的松耦合
HTML 渲染
舍弃 SSR 的 SPA 完成了 HTML 渲染的绝对解耦,
但需要考虑两者对 SEO 和用户体验的影响
但需要考虑两者对 SEO 和用户体验的影响
Node.js 中间层兼顾渲染和接口代理,
但如果要达到更好的 SEO,则需要保证客户端路由和 Node.js 服务路由之间的一一映射和优雅降级;
为提高用户体验还需考虑同构动态组件的客户端恢复。
其在架构上的复杂性远大于传统的 SSR Web 应用架构
但如果要达到更好的 SEO,则需要保证客户端路由和 Node.js 服务路由之间的一一映射和优雅降级;
为提高用户体验还需考虑同构动态组件的客户端恢复。
其在架构上的复杂性远大于传统的 SSR Web 应用架构
业务关注点
所有软件产品的核心关注点
用户和市场
实施方向
推广产品争取更多用户
前端途径
SEO
对于内容偏静态或完全静态的 Web 网站来说,
SEO 是非常重要的市场推广途径之一
SEO 是非常重要的市场推广途径之一
如何在前后端解耦的同时兼顾 SSR 以保障 SEO,是前后端分离架构设计过程中的核心关注点之一
其他途径
改进产品提高现有用户黏性
前端途径
保障产品功能的正确性和稳定性(根本)
提高交互流畅度、快速反馈用户操作,
即提高用户可见性能
即提高用户可见性能
前端性能优化方向
提高首屏渲染速度
普遍意义上,在首屏响应速度方面 SSR 比 CSR 表现更好
缩短关键渲染路径(CRP)
用户在页面上的操作得到快速响应
在客户端建立完善的临时数据和路由管理体系??
的 SPA 能够提供更快速的操作反馈,
并且可以支持离线场景,
这是采用 SSR 的常规 Web 架构不可比拟的优势
的 SPA 能够提供更快速的操作反馈,
并且可以支持离线场景,
这是采用 SSR 的常规 Web 架构不可比拟的优势
其他途径
在设计前后端分离架构之前必须确定的基本准则
依据自身业务对 SEO 和 性能 两项指标的偏重程度来决定具体的架构模式
架构关注点
前后端耦和违背分层的理念,在于将交互逻辑与业务逻辑混合在一起
前后端分离在架构层面的核心优势是让前后端各自拥有高度的独立性和内聚性,
类似于黑盒或纯函数,只需保证输入与输出的匹配(通常为接口)而无须关注彼此内部的逻辑
类似于黑盒或纯函数,只需保证输入与输出的匹配(通常为接口)而无须关注彼此内部的逻辑
工程关注点
提升效率,降低人力和时间成本
效率切入点
开发的保质高效产出
前后端并行开发
单元测试??
动静资源分离部署
bug 的保质快速解决
前后端开发者明确的职责划分
最常见的两种分离模式
SPA
SPA 这种极端分离模式
的基本架构模型和路由管理方案
的基本架构模型和路由管理方案
优点
实现了前后端的绝对解耦,接口是两个领域唯一的关联。
与前后端分离在架构与工程方面所有的关注点几乎完全契合
与前后端分离在架构与工程方面所有的关注点几乎完全契合
动态性优势??
在浏览器环境下建立前端路由策略将所有子页面进行统筹管理,
页面之间的跳转被映射为组件的更换或函数的调用,
向服务端发起的网络请求仍然只是接口的调用,将渲染完全从服务端剥离。
页面之间的跳转被映射为组件的更换或函数的调用,
向服务端发起的网络请求仍然只是接口的调用,将渲染完全从服务端剥离。
缺点
弱 SEO
虽然有途径可以弥补,但是难以达到 SSR 的水准,并且实施成本非常高。
所以在现实工作中,SPA 项目大多不考虑 SEO,典型的是 Hybrid 应用和短期的活动页。
所以在现实工作中,SPA 项目大多不考虑 SEO,典型的是 Hybrid 应用和短期的活动页。
首屏时间长
需要先等待 JS 文件加载完成,然后请求接口,最后渲染 HTML。
所以需要在性能优化上花费更多精力,包括设计和技术,
如骨架屏、JS 模块化按需加载、图片懒加载等。
所以需要在性能优化上花费更多精力,包括设计和技术,
如骨架屏、JS 模块化按需加载、图片懒加载等。
前端路由
的实现方式
的实现方式
Hash 模式
使用 URL 的 hash 标识作为路由标记,通过监听 hashchange 事件实现回调逻辑
优点
不需要服务端支持,是前端完全自主的路由极致
hash 标识改变后,
浏览器并不会将其判定为新的 URL 进而以新 URL 发起网络请求
浏览器并不会将其判定为新的 URL 进而以新 URL 发起网络请求
兼容性更优
History 路由依赖 HTML5 规范新增的 History API 的 History 路由
History 模式
使用 URL 的 path 作为路径标记,借助 History API 及其相关事件实现跳转和回调逻辑
在前后端整体架构上不同于 Hash 路由
最显著的一点是需要服务端的配合
最显著的一点是需要服务端的配合
History 路由支持刷新的前提条件是
服务端将所有子路由的请求 rewrite (不是 redirect)到根路由,
然后前端在浏览器环境下进行子路由恢复
服务端将所有子路由的请求 rewrite (不是 redirect)到根路由,
然后前端在浏览器环境下进行子路由恢复
代码中借助了 CustomEvent API 实现自定义事件
优点
能被爬虫程序抓取到路径,
然而仅路径被抓取还远不足以支撑 SEO,
因为页面的本质仍然是 CSR 的 SPA
我的理解:既然没有实际的用处,那就不算是优点
然而仅路径被抓取还远不足以支撑 SEO,
因为页面的本质仍然是 CSR 的 SPA
我的理解:既然没有实际的用处,那就不算是优点
Node.js 中间层
以 Node.js 作为中间渲染层
的同构 JS 编程基本模式
的同构 JS 编程基本模式
优点
更好的 SEO
接口仍然是前后端唯一的关联,
但具体的关联方式可以根据 Node.js 中间层
是否具有代理功能分为两种
但具体的关联方式可以根据 Node.js 中间层
是否具有代理功能分为两种
无代理 Node.js 服务器
输出
Node.js 服务器只输出 html
有代理 Node.js 服务器
输出
输出 html 文件,并且会代理 js 文件??
优势
后端开发人员不必在跨域问题上花费精力(虽然并不复杂)
可以承担一定的验证功能??
可以通过汇总多个接口在一定程度上降低交互逻辑的复杂度??
Node.js 中间层的目的
将渲染从后端解耦
为前后端(这里的后端指 Node.js 中间层)
同构 JavaScript 编程提供了可行性(对前端的革命性影响)
同构 JavaScript 编程提供了可行性(对前端的革命性影响)
同构 JavaScript
目的
令 JS 编写的代码既可以在浏览器端工作,也可以在服务端工作
三层含义
语言同构
(最表象含义)
(最表象含义)
客户端和服务端使用同一种编程语言
组件同构
一个组件同时兼容客户端和服务端运行环境
功能同构
(最终目标)
(最终目标)
客户端和服务端可以实现相同的功能??
主要功能
渲染
工作本质
字符串处理
接下来的内容没有看懂,工作中要用到 SSR 时先看 Vue 的 SSR 文档,之后再来理解此书的内容
对于常规??的前端项目而言,
如果硬件??和学习成本在可接受范围内,
兼顾 SEO、性能和客户端动态性的同构 JS 是非常理想的前后端分离架构模式
如果硬件??和学习成本在可接受范围内,
兼顾 SEO、性能和客户端动态性的同构 JS 是非常理想的前后端分离架构模式
性能
性能优化的两个方面
键入 URL 后尽可能快地将内容展示给用户
(首屏渲染速度)
(首屏渲染速度)
可用多维度可量化的客观指标衡量
提高用户操作的反馈速度和流畅度
在动态交互场景下用户的感受有一定的主观色彩
Web 应用性能评估模型
根据 Web 应用特征对
客户端场景参数进行细化
客户端场景参数进行细化
设备特征
类型
PC
平板
手机
硬件配置
CPU
内存大小
操作系统及其版本
Windows
Android
iOS
浏览器特征
品牌及版本
网络情况
连接方式
光纤
WiFi
4G
运营商
地区
带宽
为以上参数赋予固定的值从而将应用限定在一致的客户端场景中,
然后再逐指标进行对比,这是制定性能评估模型最基础的原则
然后再逐指标进行对比,这是制定性能评估模型最基础的原则
Web 应用程序的生命周期
首屏加载阶段
(加载性能)
(加载性能)
从用户输入 URL 并按下回车键到浏览器屏幕被网页填满
优化目标
尽快完成首屏呈现
衡量指标
视觉角度
提高网站内容的渲染速度
提高网站内容的渲染速度
白屏时间
计时起点
将用户在浏览器地址栏中输入 URL 按下回车键那一刻
计时终点
有可视化的图像被渲染到浏览器视窗中的那一刻
期间浏览器的工作
(可参考 性能脑图 中
浏览器如何工作部分)
(可参考 性能脑图 中
浏览器如何工作部分)
域名查询
注意:DNS 的解析工作由 DNS 服务器完成,
浏览器是请求发起方与响应接收方
浏览器是请求发起方与响应接收方
与 Web 服务器建立 TCP 连接
发送 HTTP 请求
接收并渲染响应的 HTML 文档
首屏时间
计时起点
解读1
用户按下回车键开始
解读2
白屏时间的终点时刻
(本书是按照此描述)
(本书是按照此描述)
计时终点
浏览器视窗的第一屏首次完全渲染完毕的时刻
交互角度
缩短从打开网站到可交互之间的时间间隔
缩短从打开网站到可交互之间的时间间隔
可交互节点
网站首次可以响应用户操作的那一刻
这里的操作反馈指的是通过 JS 实现的交互功能
业务性能指标
首次有效绘制
优先级最高的内容被首次渲染的时间节点
广告可视节点
更快将广告展示给用户,能间接提高与广告商之间的议价
可交互阶段
(动态性能)
(动态性能)
首屏内容全部加载完成后,网站便进入可交互阶段
衡量指标
反馈速度
尽可能快地响应用户的操作
动画帧率
在涉及数据量庞大、计算密集且强交互的应用中,
实现 50 帧以上的动画并不简单
实现 50 帧以上的动画并不简单
业务指标
关键渲染路径
(必要元素渲染路径)
(必要元素渲染路径)
性能指标类型
与业务无关的技术性能指标
业务性能指标
(有时候比技术性能指标要重要很多)
(有时候比技术性能指标要重要很多)
浏览器运行 Web 应用的机制
浏览器的大致架构
应用层
可视的交互功能模块
书签管理器
窗口管理器
...
不可见数据管理模块
历史记录管理
...
内核层
渲染引擎
(优化 Web 应用性能
的主要突破点)
(优化 Web 应用性能
的主要突破点)
HTML、CSS、SVG 等语言的解释器
JS 引擎
布局、绘制等与渲染相关的模块
相对底层的功能模块集合
多媒体解码器
图形库
...
操作系统
提供一些浏览器所需的系统 API,比如多线程、文件 IO 等
加载性能与动态性能的症结所在
宏观结构
网络
减少 RTT
(Round-trip delay time,
通信往返时间)
总数量
(Round-trip delay time,
通信往返时间)
总数量
keep-alive
(持久连接)
(持久连接)
建立 TCP 连接成功之后始终保持连接状态以实现复用
避免了每次 HTTP 请求都关闭 TCP 连接的 4 次握手,
以及新请求重新建立 TCP 连接的 3 次握手,减少了 3 RTT 和慢启动 耗时
以及新请求重新建立 TCP 连接的 3 次握手,减少了 3 RTT 和慢启动 耗时
需要消耗更多的服务器资源,加重了服务器负载,但与带来的收益相比,这些成本投入是值得的
并行请求
在 HTTP2.0 尚未普及之前,各浏览器为提高处理 HTTP 请求效率的方式非常简单粗暴,
目前主流浏览器支持每个域名同时打开 4-8 个 TCP 连接。
目前主流浏览器支持每个域名同时打开 4-8 个 TCP 连接。
域名发散
以浏览器并行请求为前提,为充分发挥并行请求的优势,
目前一种主流的优化方案是将 Web 服务接口根据业务归属到不同的子域名下,
静态资源托放的 CDN 服务器也划分出多个子域名,这种方案被称为域名发散。
目前一种主流的优化方案是将 Web 服务接口根据业务归属到不同的子域名下,
静态资源托放的 CDN 服务器也划分出多个子域名,这种方案被称为域名发散。
缺点
维持多个持久连接必然需要占用客户端以及服务端更多的资源(CPU 和内存等),
如果一个 Web 应用主要面向的是硬件配置相对较差的移动设备(手机),需要格外慎重
如果一个 Web 应用主要面向的是硬件配置相对较差的移动设备(手机),需要格外慎重
域名发散并不意味着可以无节制增加子域名的数量,域名的增加必然产生更多的 DNS 查询和 TCP 慢启动,
这些行为带来的延迟在本身就存在高延迟的移动网络环境下被进一步放大。
这些行为带来的延迟在本身就存在高延迟的移动网络环境下被进一步放大。
域名收敛
移动端 Web 应用常使用的一种优化方案是合理减少域名数目
目的
减少无线网络下大量 DNS 查询造成的高延迟
这在一定程度上放弃了并行请求的优势,
如果资源数量很多,可以使用 HTTP combo 减少请求数量,进而提升应用的加载性能
如果资源数量很多,可以使用 HTTP combo 减少请求数量,进而提升应用的加载性能
HTTP combo
是一种应用层方案
实现原理
常规模式
http://static.app.com/a.js
http://static.app.com/b.js
http://static.app.com/b.js
HTTP combo 模式
http://static.app.com/?a.js&b.js
将多个静态文件作为 URL 参数发送给服务器,然后服务器将两个文件合并为一个 JS 文件回传给客户端。
支持 combo 的服务器必须能够解析 URL 参数,解析工作可以在 Ngnix 层完成。
支持 combo 的服务器必须能够解析 URL 参数,解析工作可以在 Ngnix 层完成。
缩短 RTT 时长
CDN
原理
广泛采用各种缓存服务器(存放频繁访问内容的服务器),将这些缓存服务器分布到用户访问相对集中的地区或网络中,
在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,
由缓存服务器直接响应用户请求。
在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,
由缓存服务器直接响应用户请求。
被广泛应用于搭建静态文件服务
HTTP 2.0
(HTTP 脑图中的“演变”部分)
(HTTP 脑图中的“演变”部分)
从底层解决问题的革新技术
网络优化策略
短时间内 HTTP 2.0 难得到普及
(这里和 mdn 文档中说的不一样),
所以网络优化策略仍主要面向HTTP1.1
(这里和 mdn 文档中说的不一样),
所以网络优化策略仍主要面向HTTP1.1
浏览器兼容性不理想??
服务器迁移成本太高??
要如何迁移??
要如何迁移??
整体架构
持久连接
使用CDN
无法合并的小体积文件使用 HTTP combo
控制域名数目
前端
压缩文件体积
减少请求文件数量
合并小体积文件,使用雪碧图和字体图标
按需加载,避免不必要的下载
合理使用缓存
渲染
查看性能脑图中的
“浏览器如何工作/4. 渲染”与
“js 的 async 与 defer attribute”
“浏览器如何工作/4. 渲染”与
“js 的 async 与 defer attribute”
代码层面
运算
指前端代码逻辑而非浏览器本身的运算能力
内存管理
极限运算性能
今天的复杂图形类 Web 应用(如游戏、地图、WebVR 等)也非常接近浏览器运算能力的瓶颈。
对于从事这些行业的前端开发者来说,必须想尽一切办法将浏览器的运算能力发挥到极致。
对于从事这些行业的前端开发者来说,必须想尽一切办法将浏览器的运算能力发挥到极致。
从 URL 到图像的完整流程
性能脑图中的“浏览器如何工作”部分
性能脑图中的“浏览器如何工作”部分
浏览器运行 Web 应用的机制
以及 GC 策略对性能优化的启示
以及 GC 策略对性能优化的启示
探索浏览器运算性能的极限
研究前提
业务是核心出发点
前端架构与工程概述
架构与工程
工程
软件工程初衷是为了解决软件危机
典型的软件危机
成本超标
硬件成本
人员成本
时间成本
性能不理想或功能不稳定
开发过程混乱无序难以管理
代码不规范,维护成本高
在架构之外,从更宏观的角度完善开发和维护流程管控
目标
- 规范
- 有序
- 可控
- 高效
- 辅助
架构
目标
- 高可用
- 可扩展
- 高性能
- 安全
业务
业务逻辑
交互逻辑
前端架构师职责
根据业务特征
设计合理的前端技术架构
设计合理的前端技术架构
当架构难以满足日益增长的功能需求时,如果任其野蛮生长,系统碎片化会越来越严重、越来越无序,最终达到一个难以继续维护和扩展的阈值,便会得到一种极端的结果:彻底重构。
架构师需要通过及时、有效的干预,保持系统的有序。
架构师需要通过及时、有效的干预,保持系统的有序。
基本原则
- 通过合理解耦各个组件/层级的功能提高系统的高效性和灵活性
- 所有组件/层级在完成各自功能的同时组合为完整的软件系统
软件架构的两个基本要素
分治
分解问题,各个击破
聚合
合并模块,协同运行
前端架构的
几个层级
几个层级
编程语言
在充分了解语言特性的前提下 制定适用于业务类型的开发规范和技术栈
HTML 与 CSS
是标记类语言,原始语法简单且缺乏可编程性,所以通常会借助一些工具和框架,另源码具有可编程性,以便维护和迭代。
前端架构师的工作之一便是选择适用于业务类型、同时可提高开发和维护效率的工具和框架,并制定相应的开发规范。
前端架构师的工作之一便是选择适用于业务类型、同时可提高开发和维护效率的工具和框架,并制定相应的开发规范。
JavaScript
灵活性
会给大型项目和复杂架构带来了一定隐患。所以很多情况下需要通过框架或规范去限制 JavaScript 的灵活性
异步编程
避免回调地狱令源码更利于迭代和维护并同时兼顾性能
技术规范
内容
编码规范
源码组织结构
依赖管理
第三方技术选型
原则
一致性
组件化
质量指标
封装性
怎样才叫做封装的好??目前觉得可以先看设计模式,然后再找其他的组件化资料
扩展性
可组合性
可复用性
分类
基础组件
业务组件
前后端分离
目标
将前端开发和后端开发解耦,实现开发、维护、部署甚至发布的相对独立性,提高开发效率和快速响应问题
方案
SPA 架构
优点
成本低
可离线??
缺点
不利于传统的 SEO,适用于无 SEO 需求的应用,如 Hybrid 应用
Node.js 渲染层
优点
首屏速度快
SEO 友好
同构编程的可能性??
缺点
实施成本高
性能
用户角度
首屏加载时间
操作响应速度
技术角度
Web 应用整体架构的任何环节(包括软件和硬件)都能影响网站的性能
服务器分布式架构
负载均衡
数据缓存层
前端角度
加载性能
执行性能
快速响应用户操作
⚠️ 注意:要在性能和功能之间进行权衡
根据架构特征
搭建高效的前端工程服务体系
搭建高效的前端工程服务体系
核心是成本控制
降低开发本身所消耗的人力成本
降低跨团队协作消耗的沟通成本
跨团队协作消耗的沟通成本非常昂贵
开发
三种开发模式
的成本消耗
的成本消耗
个人独立开发
重复性体力劳动
团队协作开发
历史代码交接、模块集成消耗的人力和沟通成本
跨团队协作开发
由各团队技术规范差异和开发进度不同步所消耗的时间、人力和沟通成本
前端工程服务体系
针对开发阶段的目标
针对开发阶段的目标
- 减少重复性体力劳动
- 建立统一的技术规范
- 辅助跨团队并行开发
构建
编程语言
将源代码转换为客户端可执行代码
性能优化
压缩混淆、自动生成 CSS Sprite、动态模块按需加载
部署策略
给静态资源 URL 加入 Hash 指纹和 CDN 路径???等
开发效率
文档生成、动态构建???等
审查评估
规范审查、性能评估等
测试
困难与解决
GUI(图形用户界面(Graphical User Interface)) 应用普遍难以测试
得益于 React\Vue 等前端框架的流行,在 Node.js 环境中将 HTML 文档内容渲染为字符串,从而可以进行 UI 快照测试???
前端技术过于灵活
制定统一代码规范甚至编程范式对 JavaScript 的灵活性进行一定程度的限制,使其更容易测试
部署
简单讲是把代码“放”到指定的服务器上
监控与统计
前端稳定性监控
前端本身的交互逻辑
数据接口
一旦出现 bug,用户的第一反应是前端的问题。前端团队接受反馈调试发现是服务端的问题,然后反馈至相关团队。这是一个非常漫长的过程,并且会消耗大量的沟通成本。
如果可以在前端调用接口没有返回预期结果时(通过何种机制来判断接口是否返回了预期结果??)立即反馈至监控平台并且报告错误信息,开发团队便可以在第一时间定位到问题的症结,缩短修复问题的时间
1. 编程语言/确立所选编程语言
的开发模式
的开发模式
为什么要从架构层面思考编程语言
为了选择适用于业务的架构模式和技术选型
根据语言特征制定技术规范和开发范式,提高个人和团队的开发与维护效率
由于前端语言的单一性,“编程语言仅仅是一种工具”的论调并不适用于前端
HTML
SSR
工作流程
- 浏览器向网站发起请求
- 服务器接收到请求后先查询数据库中的动态数据,然后将数据通过模板引擎编译为 HTML 字符串返回给浏览器
- 浏览器接收到 HTML 后将其渲染为可视化的 UI
优势
支持 SEO
首屏时间短
有更深的技术沉淀和产业生态
应用场景
交互简单、以静态内容为主并且有 SEO 需求的业务产品
具体实现
传统模板引擎
借助于服务端编程语言或框架搭配的 HTML 模板引擎将数据编译为 HTML 文档,比如适用于 Java 的 FreeMarker、适用于 PHP的 Smarty 以及适用于 Node.js 的 Pug 等。
模板引擎可以被理解为一个功能强大的字符串处理工厂。
模板引擎可以被理解为一个功能强大的字符串处理工厂。
优势
缓存
Pug 也提供了 pug.render() 系列的函数,它们把编译和渲染两个步骤合二为一。
当然,在每次执行 render 的时候,这样一个模板函数都需要被重新编译一遍,这会在一定程度上影响性能。
但同时,您也可以在执行 render 的时候配合使用 cache 选项,它将会把编译出来的函数自动存储到内部缓存中。
当然,在每次执行 render 的时候,这样一个模板函数都需要被重新编译一遍,这会在一定程度上影响性能。
但同时,您也可以在执行 render 的时候配合使用 cache 选项,它将会把编译出来的函数自动存储到内部缓存中。
快速
适用场景
- 非 Node.js 技术栈
- 渲染速度要求高
React/Vue SSR
优势
同构编程
(这里的意思是同一组件
在客户端和服务端都能被
编译成HTML字符串)
(这里的意思是同一组件
在客户端和服务端都能被
编译成HTML字符串)
- 节约了一定的开发成本
- 在一定程度上加强前后端试图逻辑的统一性
劣势
必须依赖 Node.js 环境
技术栈迁移成本高
性能差
SSR仅仅使用框架中将数据编译为字符串的几个API,其他只适用于浏览器环境的API(如生命周期钩子)便成了冗余
适用场景
- Node.js 技术栈
- 渲染速度要求略低
CSR
工作流程
- 浏览器向网站发起请求
- 服务器接收到请求后立即返回静态的HTML部分,这部分内容通常是与用户无关的静态数据
- 浏览器解析HTML文档,待JavaScript脚本加载完成之后发起异步请求,获取动态数据
- 服务器接收到异步请求后查询数据库并将动态数据返回给浏览器
- 浏览器接收动态数据后使用JavaScript将数据编译为HTML字符串并渲染为可视化的UI
劣势
首屏渲染速度慢
服务器的硬件配置通常优于个人终端设备,同时搭配模板引擎的缓存功能,SSR在速度上的优势要远远大于CSR。
随着个人终端设备硬件性能的不断提升,CSR与SSR在速度上的差距逐渐减小;
网络传输速度和浏览器性能的提升也缩短了首屏等待时间。
随着个人终端设备硬件性能的不断提升,CSR与SSR在速度上的差距逐渐减小;
网络传输速度和浏览器性能的提升也缩短了首屏等待时间。
弱SEO
优势
更好地支持前后端分离方案的实施
更好地支持离线场景??
基于组件的前端路由管理在速度和灵活性上优于依赖服务器路由驱动的MVC模式
骨架屏
是交互设计上的优化
饿了么自动化生成骨架屏方案
预渲染与其缺点
所谓预渲染,就是在项目的构建过程中,通过一些渲染机制,比如 puppeteer 或则 jsdom 将页面在构建的过程中就渲染好,然后插入到 html 中,这样在页面启动之前首先看到的就是预渲染的页面了。
但是该方案最终也抛弃了,预渲染渲染的页面数据是在构建过程中就已经打包到了 html 中, 当真实访问页面的时候,真实数据可能已经和预渲染的数据有了很大的出入,而且预渲染的页面也是一个不可交互的页面,在页面没有启动之前,用户无法和预渲染的页面进行任何交互,预渲染页面中的数据反而会影响到用户获取真实的信息,当涉及到一些价格、金额、地理位置的地方甚至会导致用户做出一些错误的决定。因此我们最终没有选择预渲染方案。
但是该方案最终也抛弃了,预渲染渲染的页面数据是在构建过程中就已经打包到了 html 中, 当真实访问页面的时候,真实数据可能已经和预渲染的数据有了很大的出入,而且预渲染的页面也是一个不可交互的页面,在页面没有启动之前,用户无法和预渲染的页面进行任何交互,预渲染页面中的数据反而会影响到用户获取真实的信息,当涉及到一些价格、金额、地理位置的地方甚至会导致用户做出一些错误的决定。因此我们最终没有选择预渲染方案。
如何构建骨架屏
基本方案
通过 puppeteer 在服务端操控 headless Chrome 打开开发中的需要生成骨架屏的页面,
在等待页面加载渲染完成之后,在保留页面布局样式的前提下,
通过对页面中元素进行删减或增添??,对已有元素通过层叠样式进行覆盖??,
这样达到在不改变页面布局下,隐藏图片和文字,通过样式覆盖,使得其展示为灰色块。
然后将修改后的 HTML 和 CSS 样式提取出来,这样就是骨架屏了。??
在等待页面加载渲染完成之后,在保留页面布局样式的前提下,
通过对页面中元素进行删减或增添??,对已有元素通过层叠样式进行覆盖??,
这样达到在不改变页面布局下,隐藏图片和文字,通过样式覆盖,使得其展示为灰色块。
然后将修改后的 HTML 和 CSS 样式提取出来,这样就是骨架屏了。??
Puppeteer
Headless Chrome Node.js API
Headless Chrome
Headless Chrome is shipping in Chrome 59.
It's a way to run the Chrome browser in a headless environment.
Essentially, running Chrome without chrome!
It brings all modern web platform features provided by Chromium and the Blink rendering engine
to the command line.
It's a way to run the Chrome browser in a headless environment.
Essentially, running Chrome without chrome!
It brings all modern web platform features provided by Chromium and the Blink rendering engine
to the command line.
Why is that useful?
A headless browser is a great tool for automated testing and server environments where you don't need a visible UI shell.
For example, you may want to run some tests against a real web page, create a PDF of it, or just inspect how the browser renders an URL.
For example, you may want to run some tests against a real web page, create a PDF of it, or just inspect how the browser renders an URL.
通过 webpack 将骨架屏打包到项目中
为什么在开发过程中生成骨架屏
为了骨架屏的可编辑
在开发过程中对骨架屏进行编辑,修改部分样式,中部骨架屏可以进行实时预览,
这之间的通信都是通过websocket 来完成的
这之间的通信都是通过websocket 来完成的
<div id="app"><!-- shell.html --></div>
我们在项目构建的过程中,将骨架 插入到上面代码注释的位置,这样在应用启动前,展示的是骨架屏,
当应用启动后,将真实数据渲染的页面替换骨架屏页面。
我们在项目构建的过程中,将骨架 插入到上面代码注释的位置,这样在应用启动前,展示的是骨架屏,
当应用启动后,将真实数据渲染的页面替换骨架屏页面。
怎样将骨架屏打包到项目中
Webpack 在整个打包的过程中提供了众多生命周期事件,比如compilation 、after-emit 等,
我们最终将骨架屏插入到 html 中就是在after-emit 钩子函数中进行的
我们最终将骨架屏插入到 html 中就是在after-emit 钩子函数中进行的
SEO
(Search Engine
Optimization)
(Search Engine
Optimization)
普遍的弥补方案
构建阶段预渲染SPA的静态内容到 index.html
优点
实施成本低,不涉及服务端开发
缺点
本质上与首页 ssr、动态内容 ajax 的模式大同小异,
对于存在大量路由和动态数据的 SPA 项目而言,对 SEO 的提升微乎其微。
对于存在大量路由和动态数据的 SPA 项目而言,对 SEO 的提升微乎其微。
服务器判断请求来源,
将爬虫请求重定向到预渲染服务器,
使用 Headless Chorme 预渲染 index.html
将爬虫请求重定向到预渲染服务器,
使用 Headless Chorme 预渲染 index.html
优点
可以将完整的 HTML 文档提供给爬虫程序
缺点
部署成本昂贵
额外的开发工作
硬件设备支持
可以使用第三方云服务
prerender.io
优化
在构建阶段将 SPA 各页面提前解析并存放于预渲染服务器中
SPA 的路由管理必须使用 HTML5 History 模式而非 Hash 模式,并且需要服务器支持。
对于需要兼容低版本浏览器的SPA项目来说,此方案性价比不高。
对于需要兼容低版本浏览器的SPA项目来说,此方案性价比不高。
Hash 模式使用的是 URL 的片段标识符,本身的语义是主文档的一个片段标记,并非路由标记。
Hash 改变后不会向服务器发起请求,所以无法给爬虫程序提供完整的子页面数据。
Hash 改变后不会向服务器发起请求,所以无法给爬虫程序提供完整的子页面数据。
总结
根据业务特征(SEO 的重要程度)
选择
选择
资讯类网站首页、内容详情等需要 SEO 的页面使用 SSR;
用户中心、管理后台等无须 SEO 的页面可以使用 CSR
用户中心、管理后台等无须 SEO 的页面可以使用 CSR
CSS
css 本身的缺陷
书写繁琐(不支持嵌套)
样式易冲突(没有作用域概念)
缺少变量(不便于一键换主题)
...
样式易冲突(没有作用域概念)
缺少变量(不便于一键换主题)
...
preprocessor 和 PostCss
预处理器,
以 LESS 为例
以 LESS 为例
优点
嵌套语法
更易于编写和维护
变量、混合、模块、继承等特性
弥补了 CSS 逻辑处理和复用性方面的不足
缺点
大而全,导致代码规范难以约束?? 没有一些类似 jsLint 的工具来检测??
难扩展
只有极少数编译器支持开发者扩展自定义插件,
核心架构过于封闭,缺少插件生态
核心架构过于封闭,缺少插件生态
PostCss
最初被定位为“CSS后处理器”,
但后续的演进并未被束缚在后处理器的范畴,
逐渐进化成一个全面的“CSS转化工具”
但后续的演进并未被束缚在后处理器的范畴,
逐渐进化成一个全面的“CSS转化工具”
PostCss 的内核并不会对 CSS 做任何转化,
而是将原始的 CSS 代码转化为抽象语法树并传递给各个插件,
插件根据用户配置对 AST 进行处理后还原为最终的 CSS 代码
而是将原始的 CSS 代码转化为抽象语法树并传递给各个插件,
插件根据用户配置对 AST 进行处理后还原为最终的 CSS 代码
CSS Modules
CSS-in-JS
jsx 已经将 js 和 html 框定到一个组件中,但是css 依然处于分离状态,
这就导致了每次引用组件却还需要显示引入 css。
css-in-js 是正式彻底组件化的解决方案。
这就导致了每次引用组件却还需要显示引入 css。
css-in-js 是正式彻底组件化的解决方案。
优点
模块
原生 CSS 和 SASS、LESS 的模块实质上仅仅是子文件,并非真正意义上的模块。
将 JS 的模块体系带入 CSS 开发领域可以有效弥补 CSS 模块体系的补不足。
将 JS 的模块体系带入 CSS 开发领域可以有效弥补 CSS 模块体系的补不足。
命名空间
CSS-in-JS 在编译时(有些是在运行时)为组件产生唯一的 classname 以及对应的选择器规则,
将组件及其内部元素的样式限制在唯一的命名空间内,从而实现样式的隔离
将组件及其内部元素的样式限制在唯一的命名空间内,从而实现样式的隔离
动态性
依据我目前的了解,这和“命名空间”的优点,CSS Modules 都有
一定程度上减少无效代码
大概率指的是 webpack 中对 js 的 tree-shaking 功能
总结
非常适用于组件化架构
缺点
一定程度上限制了代码的可移植性
指的是不好移植其他 CSS 技术方案的项目中??
不好进行CSS代码的复用??
这需要对此方案有更深入的了解,以后需要用到时再研究
不好进行CSS代码的复用??
这需要对此方案有更深入的了解,以后需要用到时再研究
昂贵的学习成本
额外工具引入
All-in-JS 模式目前仍存在争议
编程过程中需要在 HTML、CSS、JS 三种上下文语境之间频繁切换,
在某种意义上背离了关注点分离原则。
在某种意义上背离了关注点分离原则。
关注点分离
关注点分离是日常生活和生产中广泛使用的解决复杂问题的一种系统思维方法。
大体思路是先将复杂问题做合理的分解,再分别仔细研究问题的不同侧面(关注点),最后综合各方面的结果,合成整体的解决方案。
关注点分离是计算科学和软件工程在长期实践中确立的一项方法论原则。
此原则在业界更多的时候以分而治之的面目出现,即将整体看成为部分的组合体并对各部分分别加以处理。
模块化是其中最有代表性的具体设计原则之一。
大体思路是先将复杂问题做合理的分解,再分别仔细研究问题的不同侧面(关注点),最后综合各方面的结果,合成整体的解决方案。
关注点分离是计算科学和软件工程在长期实践中确立的一项方法论原则。
此原则在业界更多的时候以分而治之的面目出现,即将整体看成为部分的组合体并对各部分分别加以处理。
模块化是其中最有代表性的具体设计原则之一。
库
根据不完全统计,各种 CSS in JS 的库至少有47种
styled-components
css-in-js 库我都没有实际用过,以后需要了再好好调查选择与实践
- 首先,styled-components 所有语法都是标准 css 语法,同时支持 scss 嵌套等常用语法,覆盖了所有 css 场景。
- 在样式复写场景下,styled-components 支持在任何地方注入全局 css,就像写普通 css 一样
- styled-components 支持自定义 className,两种方式,一种是用 babel 插件, 另一种方式是使用 styled.div.withConfig({ componentId: "prefix-button-container" }) 相当于添加 className="prefix-button-container"
- className 语义化更轻松,这也是 class 起名的初衷
- 更适合组件库使用,直接引用 import "module" 即可,否则你有三条路可以走:像 antd 一样,单独引用 css,你需要给 node_modules 添加 css-loader;组件内部直接 import css 文件,如果任何业务项目没有 css-loader 就会报错;组件使用 scss 引用,所有业务项目都要配置一份 scss-loader 给 node_modules;这三种对组件库来说,都没有直接引用来的友好
- 当你写一套组件库,需要单独发包,又有统一样式的配置文件需求,如果这个配置文件是 js 的,所有组件直接引用,对外完全不用关注。否则,如果是 scss 配置文件,摆在面前还是三条路:每个组件单独引用 scss 文件,需要每个业务项目给 node_modules 添加 scss-loader(如果业务用了 less,还要装一份 scss 是不);或者业务方只要使用了你的组件库,就要在入口文件引用你的 scss 文件,比如你的组件叫 button,scss 可能叫 common-css,别人听都没听过,还要查文档;或者业务方在 webpack 配置中单独引用你的 common-css,这也不科学,如果用了3个组件库,天天改 webpack 配置也很不方便。
- 当 css 设置了一半样式,另一半真的需要 js 动态传入,你不得不 css + css-in-js 混合使用,项目久了,维护的时候发现某些 css-in-js 不变了,可以固化在 css 里,css 里固定的值又因为新去求变得可变了,你又得拿出来放在 css-in-js 里,实践过就知道有多么烦心。
Houdini
思想是将 CSS 引擎的部分功能权限开放给开发者,以便于开发者扩展和自定义 CSS 特性
浏览器厂商为什么愿意推出全新的 Houdini
也不实现 CSS 的标准规范
也不实现 CSS 的标准规范
CSS规范在浏览器核心竞争力中优先级较低
CSS 规范的实现是一个非常漫长且需要持续跟进的过程
Houdini 是一个“一劳永逸”的方案
厂商真实意图
将规范的实现交给开发者
优点
减轻浏览器厂商的工作压力
丰富 CSS 生态
一定程度上推动 CSS 规范的演进
JS
JS 的灵活性
优点
JS 引擎可以“帮忙”处理一些繁琐的工作,如类型转换。
开发者可以集中精力在逻辑处理上。
开发者可以集中精力在逻辑处理上。
缺点
细节的疏忽容易造成一些低级失误
结论
在进行复杂架构设计时既要借助统一的规范、框架和工具对其灵活性加以约束,同时又不能矫枉过正
灵活性的主要表现
及其约束途径
及其约束途径
语言本身的灵活性
技术选型
技术栈
底层技术栈
TS 或者 Flow
解决语言层面的问题
要解决的两个主要问题
静态类型
数据不可变性
实现层技术栈
根据学习曲线、生态等因素的综合评定
实现方案的多样性
代码规范
代码风格
检测与较正
eslint 等工具
方案选择
典型案例是 JS 异步编程
标准
可行性
可扩展性
易读性
可维护性
检测
人工审查
静态类型
JS 是一种动态类型语言,开发者在声明变量时无须指明变量的类型,解释器根据变量的赋值判断其类型
TS
是什么
TypeScript is an open-source language which builds on JavaScript, one of the world’s most used tools, by adding static type definitions.
Types provide a way to describe the shape of an object, providing better documentation, and allowing TypeScript to validate that your code is working correctly.
Writing types can be optional in TypeScript, because type inference allows you to get a lot of power without writing additional code.??
Types provide a way to describe the shape of an object, providing better documentation, and allowing TypeScript to validate that your code is working correctly.
Writing types can be optional in TypeScript, because type inference allows you to get a lot of power without writing additional code.??
是 JS 的超集,一种编译性编程语言
目前看来ts只是在编写代码的时候有提示作用,编译后的线上代码并不健壮
Flow
JS 的一种静态类型检查工具
不可变性
JS 函数的传参方式只有值传参
function fn(obj){
obj.a=1
}
let obj={a:0}
fn(obj)
console.log(obj) //{ a: 1 }
obj.a=1
}
let obj={a:0}
fn(obj)
console.log(obj) //{ a: 1 }
传给 fn 的参数并非外部 obj 指针本身,而是它的一个拷贝指针
兴起原因
redux 的流行,在状态管理这种特定场景下,不可变性令数据可预测、可追踪
在不可变性操作中,每次“改变”数据的操作等同于先复制原数据再改变其属性或值,
表面上看上去性能非常低,尤其是对象类型的数据。
应用时通常需要辅以性能优化的逻辑,比如 immutable.js 使用一种叫做 structural sharing 的数据共享方案。
表面上看上去性能非常低,尤其是对象类型的数据。
应用时通常需要辅以性能优化的逻辑,比如 immutable.js 使用一种叫做 structural sharing 的数据共享方案。
异步编程
JS 异步编程的基本原理
(以 ajax 请求为例)
(以 ajax 请求为例)
1. JS 引擎首先调用发起 AJAX 请求的函数本身,
请求被发出之后顺序执行任务队列中的下一个任务
请求被发出之后顺序执行任务队列中的下一个任务
JS 核心概念脑图中的 JS 引擎
2. 浏览器的网络模块得到 HTTP 响应之后通知 JS 引擎,
Event Loop 检测调用栈为空闲时将回调函数加入任务队列并执行
Event Loop 检测调用栈为空闲时将回调函数加入任务队列并执行
回调函数、Promise、Generator、async/await 均是在此基础上的具象封装
在多个可行的异步编程方案
之间进行选择时主要考虑因素
之间进行选择时主要考虑因素
简洁性
错误处理方式
相对底层的异步编程模式
回调函数
缺点
回调地狱
Promise
优点
解决了回调函数的回调地狱
缺点
单纯用 Promise 取代回调函数会产生新的 then hell 问题
较上层的异步编程模式
Generator
async/await
async 函数返回值
返回一个 Promise,它将会用异步函数的返回值来 resolved,
或者用异步函数内部没有捕捉到的错误来 rejected。
或者用异步函数内部没有捕捉到的错误来 rejected。
在简洁性和错误处理上都优于 Generator
2. 制定技术规范
在前端编程领域内,技术规范尤其是编码风格不仅仅是一种约定,而且在一定程度上决定了应用程序的技术架构
技术选型
相对理性的评定标准
相对理性的评定标准
功能
(基本指标)
(基本指标)
满足现阶段产品的需求
满足可预见的后续版本中可能增加的需求
功能的具体实现模式、API 的简洁性
性能
稳定性
(重要指标)
(重要指标)
生态
一个框架/工具的生态系统
分为两部分
分为两部分
与其组合为完整系统的扩展插件或框架,如 Redux 之于 React,Vuex 之于 Vue
针对应用架构
其开发和调试工具的丰富性
针对工程体系
学习曲线
作者
总体而言,相对于个人项目,知名公司和团队制作的框架/工具的品质更高,稳定性也更有保障
社区
社区的活跃度能够反映项目的受欢迎程度,一个框架/工具被更多人使用和讨论也有助于问题的解决
开源协议
使用开源软件之前务必仔细研究其许可协议
资源管理
前端资源管理最基本的规则/下限规则
源码文件、构建产出文件以及第三方模块文件目录分离
构建产出文件和第三方模块不参与版本管理
目录结构,归类文件
通用原则
源码文件单独目录存放
src
构建产出文件单独目录存放
dist
第三方框架/工具单独目录存放
vendors
工程配置文件单独目录存放
config
单元测试文件单独目录存放
test
媒体资源(图片/视频等)单独目录存放
assets
源码文件归类因子
按类型
不同类型文件归类到一起,html 文件放一起,css、js 文件分别放一起
随着交互逻辑复杂度的提升、业务和组件数量的增长,
在一定程度上限制了架构的可伸缩性,并且会对开发者的工作效率产生负面影响
在一定程度上限制了架构的可伸缩性,并且会对开发者的工作效率产生负面影响
按组件
规范
业务资源目录命名语义化,并且首字母大写
公共组件统一存放在二级目录 _components 内,且按组件归类源码文件
优点
强化了文件的语义,每个文件的作用一目了然
一定程度上提高了架构的可伸缩性,有益于开发和维护效率的提升
改造的成本非常低,基本不会涉及架构逻辑层面的改动
命名规范,强化语义
两项基本原则
语义性(核心)
一致性
编码风格
良好编码风格的基本原则
统一性
底线
两个方面
保证团队成员使用统一的编码风格
在版本迭代中保持风格一致
新成员或项目交接后的开发者更容易理解历史代码并遵循既定的编程风格进行迭代,
可减少由于版本更新积累的历史包袱
可减少由于版本更新积累的历史包袱
语义化
语义化的范畴不仅仅是命名,明确参数的类型和结构(ts在这点上很有帮助)同样属于语义化的一部分
恰到好处的注释
分为两部分
使用既定的格式对代码的元素进行说明,比如函数参数和返回类型
在语义化之外,通过自然语义描述以帮助开发者更容易理解源码
注释不宜过长,冗长且无营养的注释反而有碍于开发者理解代码
兼顾美观性
有强主观色彩。
只要不违法语法规则,团队成员保持形式统一,就是美观的代码。
只要不违法语法规则,团队成员保持形式统一,就是美观的代码。
可以使用 prettier 来自动化规范统一代码风格
HTML
尽量使用语义化的 HTML 标签
JS
高性能与易读性
JS 语言的一些API或语法之间有性能差异
数组循环操作
原始的 for 或 for...of 循环
随着数据量的增长,for...of 相比 reduce 的性能优势越来越明显
forEach、map、reduce 等可读性更好的API
可读性为性能让步通常体现在大数据、计算密集型项目上,典型的是图形类应用,比如数据可视化、地图、游戏、WebVR 等。
图形编程类项目的数据规模的计算量往往超出常规前端项目几个量级,计算量接近设备硬件或浏览器的性能瓶颈,提高计算逻辑的性能是唯一的突破口。
图形编程类项目的数据规模的计算量往往超出常规前端项目几个量级,计算量接近设备硬件或浏览器的性能瓶颈,提高计算逻辑的性能是唯一的突破口。
CSS
CSS编程范式与面向对象
通过命名模拟既定的编程范式
较普遍的做法是给HTML元素的 classname、id、data 等属性(以 classname 为主)的名称施加某种命名规范,
将 CSS 带入对应的编程范式语境之下,遵循编程范式的一些系统性的既定原则来提高 CSS 代码的开发和维护效率。
将 CSS 带入对应的编程范式语境之下,遵循编程范式的一些系统性的既定原则来提高 CSS 代码的开发和维护效率。
OOCSS
Object Oriented CSS(面向对象的 CSS )
理念
将网页的完整 UI 分解为一系列组件,每个组件的颗粒度尽可能细,从而最大程度地被复用
将每个 classname 视为一个功能单一的 CSS 组件,业务组件通过组合多个 CSS 组件实现综合的样式效果
UI 组件和 CSS 组件
CSS 组件
语义指向对应的样式,如 f12(font-size:12px)、m5(margin:5px)这类classname
通常要放在 UI 组件 classname 之前
UI 组件
语义指向UI组件功能,如 content 这类 classname
优点
可组合性高,非常适用于可高度扩展的前端项目,比较典型的是Strikingly之类的建站工具
Strikingly
缺点
一个复杂样式的 HTML 元素 classname 太过冗长
不适合常规的前端项目
绝大多数组件都是业务指向性的,不需要很高的灵活性,所以 OOCSS 最大优势得不到体现,反而将其缺点暴露无遗
BEM
是 Block、Element、Modifier 的简称,是 OOCSS 方法论的一种实现模式
三种实体
Block
一个逻辑或功能独立的组件,如 logo 组件
Element
Block 内部的子组件,不能被外部访问
Modifier
描述一个 Block 或 Element 的表现或者行为
命名格式
block-name__<element-name>--<modifier-name>_modifier-value
实体命名均使用小写字母
只使用一个 classname 作为选择器,[0 0 1 0] 规则
优点
非常适用于公共组件,可令组件的样式定制具有很高的灵活性,
因为 [0 0 1 0] 选择器的权重非常低
因为 [0 0 1 0] 选择器的权重非常低
0 条评论
下一页