
前言
一起重温下 HTML,CSS 和 JS 中核心的知识点,浏览器的解析渲染原理,CSS面向对象编程,最后通过实践 Promise 封装一个Ajax请求(面试专用)。
前端基础知识源码地址
源码地址:https://github.com/dkypooh/front-end-develop-demo/tree/master/base/
通过本章读者可以学习到什么?
- 浏览器的解析原理是如何,服务端传输过来的
text/html字符串是如何绘制成页面? - H5 的 Meta 标签配置是如何,不同属性有什么作用?
- CSS 面向对象编程,BEM的规范又是什么,它们两者是如何结合?
- CSS 主流的布局,如何轻松应对页面布局?
- Promise 的实现原理,应用场景,以及如何和 async await 配合使用?
浏览器解析原理
浏览器总的解析原理
浏览器的解析过程:浏览器文档流 -> html DOM结构 --> CSS结合 --> 布局 --> 绘制页面。
HTML的渲染过程中,DOM 和 CSSOM 结构可以并行渲染。

第一步 HTML 转换成 DOM
服务端返回 `text/html` 格式的文档流, HTML 字符串描述了一个页面的结构,浏览器会把 HTML 字符串解析成 DOM 树形结构。

第二步 生成 CSSOM 结构
CSS 样式可以在 WEB 页面里映射成 CSSOM(CSS对象模型),它和 DOM 结构比较像, 不是增量模式,而是组合模式。

第三步 CSSOM 树和 DOM 树合并成渲染树

第四步 完整 DOM 结构
DOM 结构有两个规则:一个是 HTML 文档对象,一个是通过接口获取 DOM 元素。通过 `document.getElementById()` 以获取元素节点

性能优化策略
基于上面介绍的浏览器构建原理,DOM 树型结构的构建顺序,可以对页面渲染做些优化,提升体验。
- JS优化:
<script>标签加上defer属性和async属性, 不阻塞页面文档解析,控制脚本的下载和执行。- defer属性: 用于开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。
- async属性: HTML5 新增属性,用于异步下载脚本文件,下载完毕立即解释执行代码。
- Preload优化: preload(预加载) 是一个声明式 fetch,可以强制浏览器在不阻塞 document 的 onload 事件的情况下请求资源。
preload 有如下配置属性,<link> 为标签, ref="preload" 为预加载属性配置,href="/test.css" 为加载的资源,as="style" 为加载的资源类型。
用例代码
我们预加载了CSS和JavaScript文件,所以在随后的页面渲染中,一旦需要使用它们,它们就会立即可用,同时也可以预加载图片,字体等文件。
<head><meta charset="utf-8"><title>JS and CSS preload example</title>// 1. 浏览器可以预先加载style.css, main.js的资源<link rel="preload" href="style.css" as="style"><link rel="preload" href="main.js" as="script">// 2. 使用style.css样式<link rel="stylesheet" href="style.css"></head><body><h1>bouncing balls</h1><canvas></canvas>// 3. 使用main.js文件<script src="main.js"></script></body>
细节实践可以参考 通过rel=”preload”进行内容预加载
CSS
CSS 层叠样式表 (Cascading Style Sheets,缩写为 CSS),是一种样式表语言。
CSS 像素
CSS像素分为了 物理像素(physical pixel) 和 设备独立(逻辑)像素(density-independent pixel)。
设备像素比(devicePixelRatio) = 物理像素 / 设备独立像素。
历代IPhone的分辨率
| 设备 | 逻辑像素(point) | 物理像素(pixel) | 屏幕尺寸 | 设备像素比(dpr) |
|---|---|---|---|---|
| iPhone 3 | 320 × 480 | 320 × 480 | 3.5寸 | @1x |
| iPhone 4/4S | 320 × 480 | 640 × 960 | 3.5寸 | @2x |
| iPhone 6/7/8 | 375 × 667 | 750 × 1334 | 4.7寸 | @2x |
| iPhone 6P/7P/8P | 414 × 736 | (1242x2208)1080x1920 | 5.5寸 | @3x |
| iPhone X | 375 × 812 | 1125 × 2436 | 5.8寸 | @3x |
解释说明:dpr = pixel / point, 由于 Plus 系列比较特殊,可以近似于3倍屏。
物理像素(physical pixel)
物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件,retina设备像素为独立像素的2倍, 例如 iphone 6/7/8 系列
设备独立像素(density-independent pixel)
设备独立像素也称为密度无关像素,又称为逻辑像素,一个点代表一个可以由程序使用的虚拟像素(PX)。
缩合上述的几个概念,用一张图来解释:

CSS布局
Web中主流的两种布局方式:Flexbox布局 和 CSS Grid布局。
Flex布局
Flex布局 旨在提供一个更加有效的方式制定、调整和分布一个容器里的项目布局,即使他们的大小是未知或者是动态的。
Flex布局主要思想是具有伸缩性特点,可以取向改变、缩放、拉伸和收缩。
在Flexbox布局中有主轴(Main Axis)和侧轴(Cross Axis)两个概念:
实现一个水平垂直居中
<div class="container"><div class="block"></div></div>.container {display: flex; // 默认为水平布局,主轴线为水平justify-content: center; // 水平居中align-items: center; // 垂直居中}.block {height: 100px;width: 100px;}
CSS Grid布局
CSS Grid布局(又名”网格”),是一个基于二维网格布局的系统,主要目的是改变我们基于网格设计的用户接口方式。
CSS 面向对象
OOCSS “Object Oriented CSS(面向对象的CSS)”,是一种写 CSS 的方法,其思想就是鼓励你把样式表看作“对象”的集合:创建可重用性、可重复性的代码段让你可以在整个网站中多次使用。
基于SCSS预编译器,使用 OOCSS 和 BEM 结合的编码方式,可以让 CSS 编写更加规范,更加高效。
BEM
BEM(Block-Element-Modifier),是一种用于 HTML 和 CSS 类名的命名约定。BEM 最初是由 Yandex 提出的,拥有巨大的代码库和可伸缩性。
BEM 是 Block,Element和 Modifier 的缩写。
- Block: 有实际意义的独立元素,例如:
header,container,menu,checkbox,input - Element: Block 的子元素,没有独立的含义,例如:
menu item,list item等定义成menu__item,list_item - Modifier: 表示
Blcok和Element元素的不同行为和状态。例如:disable,highlighted等定义成menu-diable,menu_item-highlighted。

CSS定义
.button {display: inline-block;}.button--state-success {color: #FFF;background: #569E3D linear-gradient(#79D858, #569E3D) repeat-x;}.button--state-danger {color: #900;}
OOCSS 和 BEM 案例结合
BEM 和 OOCSS 不是CSS的标准,也是定义了一种 CSS 命名规范,下面就结合实际例子来分析, 安装 sass 编译器。
npm i sass -g // 全局安装SASSsass bem.scss bem.css // SCSS文件编译成CSS文件
HTML
<article class="m-card"><h1 class="card__title">Adorable 2BR in the sunny Mission</h1><div class="card__body"><p>Vestibulum id ligula porta felis euismod semper.</p></div></article>
SCSS
结合 BEM 规范,更加清晰语义化的表达,同时配合 SCSS 级联语法,使 CSS 书写更加清晰,更加语义化。
.m-card {display: flex;flex-direction: column;&__title {line-height: 30px}&__body {line-height: 20px;p {margin: 0}}}
SCSS编译后的CSS
.m-card {display: flex;flex-direction: column;}.m-card__title {line-height: 30px;}.m-card__body {line-height: 20px;}.m-card__body p {margin: 0;}
Promise原理及实践
Promise原理是通过 Promise.prototype.then 和 Promise.prototype.catch 方法将观察者方法注册到被观察者 Promise 对象中,同时返回一个新的 Promise 对象,以便可以链式调用。
Promise 内部进行 等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected) 的状态流转。

三种回调比较 JS Callback VS Promise VS Async await
JS Callback : 产生 地狱般的回调嵌套,一旦嵌套次数过多,就很容易使我们的代码难以理解和维护。
Promise: 通过 链式调用的方法 去解决回调嵌套的问题,使我们的代码更容易理解和维护,同时Promise 还增加了许多有用的特性,让我们处理异步编程得心应手。
Async await: 是 ES7 引入的新的异步代码 规范,它提供了一种新的编写异步代码的方式,这种方式在语法层面提供了一种形式上非常接近于 同步代码的异步非阻塞 代码风格。
支持情况
Promise 在 Chrome33 版本开始支持,全球用户比例89.69%, Async await 在 Chrome55 版本开始支持,全球用户比例85.87%。
S7 标准 Async 原先需要 Babel编译,在Chrome55可以直接使用,不需要Babel编译
Promise支持情况

Async await支持情况

Ajax 封装案例实践
本小节会实现一个基于 XMLHttpRequest 对象实现一个 Promise 的 Ajax 方法封装(面试专用考题),再对比 Promise 和 Async Await的差异,以及他们的优缺点。
Promise实现
/*** 请求服务* @param {Object} param* @param {String} param.url 请求URL* @param {String} param.type 默认 'GET'** @return new Promise*/function fetchRequest (param) {const type = param.type || 'GET',const url = param.url;if (!url) {new TypeError('param url must be set...')}return new Promise( (resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open(type, url, true);// 1. 监听状态xhr.onreadystatechange = function () {// 2. readyState = 4, status = 200 是请求成功的标识if (xhr.readyState === 4) {if (xhr.status === 200) {resolve(xhr.responseText, xhr);} else {reject({code: xhr.status,message: xhr.response}, xhr);}}}xhr.send();})}
Promise VS Async await 使用区别
异步调用的最大问题是不能 Catch 到错误信息,同时编码上产生大量的 回调函数 或者 链式调用。
Async await 调用是把 Promise 的链式调用同步化,同时可以 Catch 到错误栈信息。
Promise 使用
const getGithubHooks = () => {return fetchRequest({api: 'https://github.com/xxx'}).then(result => {return result;// result data}.catch(err => {// error})}
Async await 使用
async function getGithubHooks() {let result = null// 同步 try catch 错误try {result = await fetchRequest({api: 'https://github.com/xxx'})return result} catch(err) {return err;}}
结语
前端开发涉及的基础知识点非常多,非常杂,本章带精选了项目开发实践中的核心知识点,带大家进行了结构化的梳理。
这是基础到进阶重要的一步,后面一章会和大家一起学习 Typescript 知识,如果对 ES6 语法 还不是很熟悉的话,可以参考阅读 ECMAScript 6 入门
思考题
Q: 除了Flexible通过JS方式来动态设置的方式,还有其他方案来处理多端H5适配问题?
Q: 如何实现三栏布局,中间自适应,采用 Flex 和 Grid 两种方案实现?
