前言
一起重温下 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 // 全局安装SASS
sass 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 两种方案实现?