- 好的程序员能够独立的解决某个技术难题,主动的关心项目进度与潜在瓶颈,能够负责模块小组,合理地分配任务,与项目经理、产品经理、美工、测试、服务端的同事高效包容地沟通。
- 好的架构师的标准则是整个项目的工程化程度:前端工程化。
更详细的线路图:Frontend Roadmap。
服务端Node.js与各种终端的涌现,让前端进入了大前端范畴,这时的前端,已远远不只是浏览器端的页面实现技术,而是后端服务与人机界面的连接器。
前端涉及:
性能(用户体验),稳定性(监控告警),开发效率(工具,开发、构建流程,开发者体验)
行业趋势总结
参考:张云龙:前端工程——基础篇。
第一阶段:库/框架选型
提升开发效率。(使用自动化工具也能够提升开发效率,如:浏览器自动刷新、IDEs)
第二阶段:简单构建优化
提升运行性能。
对代码进行压缩、校验,以页面为单位进行简单的资源合并。
第三阶段:JS/CSS模块化开发
提升维护效率。
分而治之
CommonJS/AMD/CMD/ES6 Module/UMD
CSS模块化方案:
Sass/Less/Stylus等CSS预处理器的import
、mixin
特性支持实现。
第四阶段:前端工程化
优化部署、开发。
组件化开发
模块化开发的升华。
资源管理
静态资源加载的技术实现。
性能优化是一个工程问题。
页面内容下载速度 -> 页面解析、渲染速度和流畅性 -> 用户交互流畅性 的具体优化。
URL输入:
服务端(运维)对HTTP请求、资源发布和缓存、服务器配置的优化。
服务器开启gzip(如:nginx)。
前端查看Response头是否有:
Content-Encoding: gzip
。
对资源进行缓存:
载入页面时,优化CRP(Critical Rendering Path,关键渲染路径,优先显示与用户操作有关内容):
减少关键资源、减少HTTP请求:
非首屏资源延迟异步加载:
增量加载资源:
AJAX加载。
使AJAX可缓存。
当用GET方式时添加缓存HTTP头:Expires
Cache-Control
Last-Modified/If-Modified-Since
。
使用缓存代替每次请求。
客户端: Web Storage(localStorage
、sessionStorage
)、cookie、IndexDB等;服务端:Redis等。
<script>
添加defer/async
属性、动态创建或修改<script>
)、第三方资源使用统一的CDN服务和设置<link>
预加载。<img>
、<link>
、<script>
、<iframe>
(老版本浏览器依旧会请求)。最小化字节:
图片优化
小图合并雪碧图。
大图切小图:单个大文件需要多次HTTP请求获取。
合理使用:Base64、WebP(SharpP)、srcset
属性、不同ppi的设备使用不同分辨率的图片。
- 服务端(或CDN)处理图片资源,提供返回多种图片类型的接口(如:七牛)。
- 判断浏览器是否支持WebP,对不同浏览器请求不同的图片类型。
- 用
<source>/<img>
的type
、srcset
、sizes
、media
等属性,让浏览器自动选择使用哪种资源(浏览器自动跳过不支持的资源)。navigator.connection
(实验中的功能,兼容性差、不靠谱、基本没用)获取浏览器网络情况,从而在不同网络(2G/3G/4G/wifi)使用不同尺寸的图片。
缩短CRP长度:
CSS放在HTML顶部,JS放在HTML底部。
用户体验:
本质上是减弱用户对加载时长的感知,并没有真的提高程序性能。
载入页面后进行的页面解析、渲染、线程执行性能:
大部分情况下的浏览器是单线程执行,因此要尽量做到「最小化主线程的责任」,来确保渲染流畅和交互响应及时。
CSS性能:
JS代码性能优化:
使用性能好的代码方式(微优化)
scroll
、mousemove
、touchmove
)使用函数防抖、函数节流,避免在高频事件中进行运行时间长的代码。使用Web Worker
处理复杂的计算。
针对在用框架,使用合理的特性实现业务逻辑。
HTML:
target="_blank"
的<a>
中添加rel="noopener"
。客户端配合优化
配合客户端开发落地优化方案。
优先优化对性能影响大、导致瓶颈的部分
1. 在客户端运行[`window.performance`](https://github.com/realgeoffrey/knowledge/blob/master/网站前端/前端内容/标准库文档.md#performance)查看页面从打开到加载完成的时间数据。 2. DevTools: 1. Performance查询运行时导致**帧数**过高的代码。 2. Rendering、Layers查看CSS渲染情况。 3. Memory、JavaScript Profiler、Performance monitor查询内存占用情况。 3. 打开各种分析工具,根据建议逐条对照修改 1. [lighthouse](https://github.com/GoogleChrome/lighthouse) 1. DevTools的Audits 2. Chrome的扩展程序:[Lighthouse](https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk) 3. Node.js全局安装[lighthouse](https://www.npmjs.com/package/lighthouse)并执行`lighthouse 域名` 2. 分析网站: 1. google的性能分析[PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/) 2. W3C 1. [标签验证](https://validator.w3.org/) 2. [CSS验证](https://jigsaw.w3.org/css-validator/validator.html.zh-cn) 3. [链接测试](https://validator.w3.org/checklink) 3. [性能测试](https://gtmetrix.com/)网络应用的生命期建议:
1. load 1000ms内完成CRP。 2. idle 进行50ms内的空闲时期预加载,包括图片、多媒体文件、后续内容(如:评论)。 3. animations 保证16ms/f的浏览器渲染时间。 4. response 100ms内对用户的操作做出响应。
约定
1. 判断是否DOM构造:打印该DOM。 2. 判断是否完成新的渲染:查看页面显示的DOM结果(DOM结构和样式效果)。 3. 可以利用抓包工具(如:Charles),breakpoints静态资源来模拟加载缓慢或加载失败,从而判断是否会影响后面的解析或渲染。 4. 利用DevTools的Network中的Waterfall判断资源加载的开始时间、是否并行。
解析HTML(parse HTML)
获取.html文件后,对文件进行从上到下解析:增量式生成一个文档对象模型(DOM构造)、生成CSS对象模型(CSSOM)。
总结
1. 解析HTML基本严格按照HTML内容从上到下进行。 2. 渲染引擎通过各种线程并行的措施,尽可能快速解析HTML: 并行进行:DOM构造、生成CSSOM、渲染。 1. 异步脚本(`defer`或`async`的`
加载DOM中所有CSS,生成CSSOM(recalculate style),描述对页面内容如何设置样式。
<body>
中时,若该文件还在下载,则阻止解析HTML,直到下载完成(下载中的CSS文件若在<head>
中,则不会阻止解析HTML)。异步添加的CSS(JS动态添加样式),不会阻塞渲染、不会阻止解析HTML。
已经被提取的CSS(
以下代码可以实时在页面中编辑样式 ```html a标签 ``` </details><link>
、、
style
内嵌样式),若再次修改或删除(或新添加),会再次影响CSSOM构造。
加载DOM中所有JS,对DOM和CSSOM进行访问和更改。
解析到JS,在脚本执行完之前,阻塞解析HTML(DOM构造被暂停)。
- 解析到JS时,若该文件还在下载,则保持阻塞解析HTML,直到下载并执行完毕。
- 带有
defer
或async
的<script>
是异步加载的JS,不会阻塞解析HTML。
执行脚本,访问、更改DOM和CSSOM。
一个
1. 已经执行过的脚本(``,若添加外部脚本`src`或添加内嵌脚本,会执行一次。 </details>最多执行一次。
脚本执行完毕,继续 解析HTML(DOM构造)。
事件完成顺序
执行同步的JS和CSS
构造DOM完毕;
构造DOM完毕
-> <script>
的defer
脚本执行完毕 -> document
的DOMContentLoaded
事件触发 或 jQuery的$(document).ready(function () {})
执行回调
解析HTML完成、且所有资源加载完毕(包括:<img>
等资源文件、样式引用的background
图片、异步加载的JS、动态加载的资源)。
完毕后触发:window
的load
事件。
DOM和CSSOM构造完成后(解析HTML完成),进行渲染:
Render Tree(渲染树):Layout -> Paint -> Composite
- 只有可见的元素才会进入渲染树。
- DOM不存在伪元素(CSSOM中才有定义),伪元素存在render tree中。
渲染在每一帧都会进行
页面每一帧刷新时,会使用当前最新解析完成的DOM和CSSOM进行渲染。阶段性生成CSSOM完成之前(生成CSSOM进行时),会阻塞渲染。
JS阻止浏览器执行渲染方式:
alert
debugger
e.g.
```html123```
「增量」原则:
「增量下载」是前端在工程上有别于客户端GUI软件的根本原因。
前端应用没有安装过程,其所需程序资源都部署在远程服务器,用户使用浏览器访问不同的页面来加载不同的资源,随着页面访问的增加,渐进式地将整个程序下载到本地运行。
由「增量」原则引申出的前端优化技巧几乎成为了性能优化的核心:
XSS
跨站脚本(Cross-Site Scripting,XSS)是恶意代码注入网页。利用用户对指定网站的信任。
攻击方式
所有可输入的地方,若没有对输入数据进行处理的话,则都存在XSS漏洞。
通过巧妙的方法注入恶意指令代码(HTML、JS、Flash)到网页内容,使用户加载并执行恶意程序。
攻击成功后,能够:盗取用户cookie、破坏页面结构、重定向到其它地址、利用用户机器执行各种非客户意愿的行为(如:发起请求、向其他网站发起DDoS攻击)等。
防御措施:
过滤用户输入(白名单)
e.g. js-xss
HttpOnly
cookie设置为HttpOnly不能在客户端使用document.cookie访问。
Content-Security-Policy
设置不允许加载白名单外的域名资源
HTTP响应头:
Content-Security-Policy: 具体指令
.html
的<meta>
<meta http-equiv="Content-Security-Policy" content="具体指令">
Flash的安全沙盒机制配置跨域传输:crossdomian.xml
CSRF
跨站请求伪造(Cross-Site Request Forgery,CSRF)是挟制用户在已登录的网页上执行非本意操作。利用网站对用户浏览器的信任。
攻击方式
当用户已经得到目标网站的认可后,对目标网站进行请求操作。
攻击成功后,能够:进行所有目标网站的请求操作。
防御措施
检查HTTP请求的Referer字段
Referer:请求来源地址。
地址栏直接输入内容不会提供
Referer
。
添加校验token
token:判断用户当前的会话状态是否有效(短时效性)。
目标网站请求需要额外提供保存在页面中的随机校验码(不保存在cookie即可)。可以放进请求参数、或自定义HTTP请求头。保证用户必须实时在目标网站才能获取到校验token。
验证码
保证用户必须和目标网站进行交互后才可以发起请求。
跨域请求的限制(利用CORS等)
其他攻击
注入型劫持
通过在正常的网页中注入广告代码(JS代码、<iframe>
、其他标签等),实现页面弹窗提醒或者底部广告等。
攻击方式
运营商劫持。
防御措施
全链路HTTPS(若使用CDN,则必须CDN请求和回源都是HTTPS)。
额外增加劫持难度:前端还可以用子资源完整性(SRI)验证加载文件的数字签名。
DNS攻击
使域名指往不正确的IP地址。
攻击方式
防御措施
SQL注入(SQL Injection)
运行非法的SQL。
OS命令注入攻击(OS Command Injection)
通过Web应用,执行非法的操作系统命令。
HTTP头部注入攻击(HTTP Header Injection)
通过在响应头部字段内插入换行,添加任意响应头部或主体。
邮件头部注入攻击(Mail Header Injection)
向邮件头部To或Subject内任意添加非法内容,可对任意邮件地址发送广告邮件或病毒邮件。
目录遍历攻击(Directory Traversal,Path Traversal)
对本无意公开的文件目录,通过非法截断其目录路径后,达成访问目的。
远程文件包含漏洞(Remote File Inclusion)
当部分脚本内容需要从其他文件读入时,利用指定外部服务器的URL充当依赖文件,让脚本读取之后,就可运行任意脚本。
强制浏览(Forced Browsing)
从安置在Web服务器的公开目录下的文件中,浏览那些原本非自愿公开的文件。
不正确的错误消息处理(Error Handling Vulnerability)
Web应用的错误信息内包含对攻击者有用的信息。
开放重定向(Open Redirect)
假如指定的重定向URL到某个具有恶意的Web网站,那么用户就会被诱导至那个Web网站。
会话劫持(Session Hijack)
通过某种手段拿到了用户的会话ID,并非法使用此会话ID伪装成用户。
会话固定攻击(Session Fixation)
强制用户使用攻击者指定的会话ID。
点击劫持(ClickJacking)、界面伪装(UI Redressing)
利用透明的按钮或链接做成陷阱,覆盖在Web页面上。然后诱使用户在不知情的情况下,点击那个链接访问内容。
密码破解(Password Cracking)
穷举法(Brute-force Attack,暴力破解法)
对所有密钥集合构成的密钥空间(Keyspace)进行穷举。即,用所有可行的候选密码对目标的密码系统试错。
字典攻击
利用事先收集好的候选密码(经过各种组合方式后存入字典),枚举字典中的密码。
如:生日日期数值化。
一种安全的服务端存储密码方式:
先利用给密码加盐(salt)的方式增加额外信息,再使用散列(hash)函数计算出散列值后保存。
加盐
由服务器随机生成的一个字符串,保证长度足够长,且是真正随机生成。然后把它和密码字符串相连接(前后都可以)生成散列值。当两个用户使用了同一个密码时,由于随机生成的salt值不同,对应的散列值也将不同。这样一来,很大程度上减少了密码特征,攻击者也就很难利用自己手中的密码特征库进行破解。
DoS攻击(Denial of Service attack)、服务停止攻击或拒绝服务攻击
运行中的服务呈停止状态的攻击。
集中利用访问请求造成资源过载。
DDoS(Distributed Denial of Service attack)利用多台计算机发起Dos攻击。
通过攻击安全漏洞使服务停止。
Hash Collision DoS
Hash碰撞的拒绝式服务攻击(Hash Collision DoS)是对服务器进行恶意负载。
攻击方式
- Hash:把任意长度的输入,通过散列算法,输出固定长度的散列值。
- Hash Collision DoS:利用各语言Hash算法的「非随机性」,制造出无数value不同、key相同的数据,让Hash表成为一张单向链表,而导致整个网站的运行性能下降。
找到hash算法漏洞,不断提交服务器请求导致无数hash碰撞,进而形成类似单向链表的存储结构。
攻击成功后,能够:hash堆积、查询缓慢、服务器CPU高负荷、服务器内存溢出。
防御措施
验证码
仅能预防机器行为:防止广告机注册、发帖、评论,防止暴力破解密码。
- 前端页面是一种特殊的GUI软件,很可能需要
基于图形用户交互界面测试
多于接口测试
。- 接口测试用下面的方法论比较容易实现,但GUI测试比较复杂,目前大部分公司还是通过人工测试(较少自动化测试)。
- 因为自动化测试时间人力成本大、又业务需要快速迭代频繁变动,前端业务在自动化测试方面可能较少投入,尤其是UI测试基本都有专人进行人工测试(组件或库进行自动化测试居多)。
单元测试(unit testing)
通过模拟输入和预测输出的方式测试独立的函数或类。
组件测试库
端到端(E2E、end to end)测试
功能测试,站在用户视角,使用各种功能、各种交互,是用户的真实使用场景的仿真。
集成测试(Integration Test)的一种:在单元测试的基础上,将所有模块按照设计要求组装成为子系统或系统,进行集成测试。
代码覆盖率(code coverage)测试
通过计算测试过程中被执行的源代码占全部源代码的比例,进而间接度量软件质量的方法。
UI测试
大部分手工测试。