前端基础能力 - 前端基础知识 - 图1

前言

一起重温下 HTML,CSS 和 JS 中核心的知识点,浏览器的解析渲染原理,CSS面向对象编程,最后通过实践 Promise 封装一个Ajax请求(面试专用)。

前端基础知识源码地址

源码地址:https://github.com/dkypooh/front-end-develop-demo/tree/master/base/

通过本章读者可以学习到什么?

  1. 浏览器的解析原理是如何,服务端传输过来的 text/html字符串是如何绘制成页面?
  2. H5 的 Meta 标签配置是如何,不同属性有什么作用?
  3. CSS 面向对象编程,BEM的规范又是什么,它们两者是如何结合?
  4. CSS 主流的布局,如何轻松应对页面布局?
  5. Promise 的实现原理,应用场景,以及如何和 async await 配合使用?

浏览器解析原理

浏览器总的解析原理

  1. 浏览器的解析过程:浏览器文档流 -> html DOM结构 --> CSS结合 --> 布局 --> 绘制页面。

HTML的渲染过程中,DOM 和 CSSOM 结构可以并行渲染。

前端基础能力 - 前端基础知识 - 图2

第一步 HTML 转换成 DOM

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

前端基础能力 - 前端基础知识 - 图3

第二步 生成 CSSOM 结构

  1. CSS 样式可以在 WEB 页面里映射成 CSSOMCSS对象模型),它和 DOM 结构比较像, 不是增量模式,而是组合模式。

前端基础能力 - 前端基础知识 - 图4

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

前端基础能力 - 前端基础知识 - 图5

第四步 完整 DOM 结构

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

前端基础能力 - 前端基础知识 - 图6

性能优化策略

基于上面介绍的浏览器构建原理,DOM 树型结构的构建顺序,可以对页面渲染做些优化,提升体验。

  • JS优化: <script> 标签加上 defer属性async属性, 不阻塞页面文档解析,控制脚本的下载和执行。
    • defer属性: 用于开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。
    • async属性: HTML5 新增属性,用于异步下载脚本文件,下载完毕立即解释执行代码。
  • Preload优化: preload(预加载) 是一个声明式 fetch,可以强制浏览器在不阻塞 document 的 onload 事件的情况下请求资源。

preload 有如下配置属性,<link> 为标签, ref="preload" 为预加载属性配置,href="/test.css" 为加载的资源,as="style" 为加载的资源类型。

用例代码

我们预加载了CSS和JavaScript文件,所以在随后的页面渲染中,一旦需要使用它们,它们就会立即可用,同时也可以预加载图片,字体等文件。

  1. <head>
  2. <meta charset="utf-8">
  3. <title>JS and CSS preload example</title>
  4. // 1. 浏览器可以预先加载style.css, main.js的资源
  5. <link rel="preload" href="style.css" as="style">
  6. <link rel="preload" href="main.js" as="script">
  7. // 2. 使用style.css样式
  8. <link rel="stylesheet" href="style.css">
  9. </head>
  10. <body>
  11. <h1>bouncing balls</h1>
  12. <canvas></canvas>
  13. // 3. 使用main.js文件
  14. <script src="main.js"></script>
  15. </body>

细节实践可以参考 通过rel=”preload”进行内容预加载

CSS

CSS 层叠样式表 (Cascading Style Sheets,缩写为 CSS),是一种样式表语言。

CSS 像素

CSS像素分为了 物理像素(physical pixel) 和 设备独立(逻辑)像素(density-independent pixel)。

  1. 设备像素比(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)。

缩合上述的几个概念,用一张图来解释:

前端基础能力 - 前端基础知识 - 图7

CSS布局

Web中主流的两种布局方式:Flexbox布局CSS Grid布局

Flex布局

Flex布局 旨在提供一个更加有效的方式制定、调整和分布一个容器里的项目布局,即使他们的大小是未知或者是动态的。

Flex布局主要思想是具有伸缩性特点,可以取向改变、缩放、拉伸和收缩。

在Flexbox布局中有主轴(Main Axis)和侧轴(Cross Axis)两个概念:

前端基础能力 - 前端基础知识 - 图8

Flex 参考资料下载

实现一个水平垂直居中

  1. <div class="container">
  2. <div class="block"></div>
  3. </div>
  4. .container {
  5. display: flex; // 默认为水平布局,主轴线为水平
  6. justify-content: center; // 水平居中
  7. align-items: center; // 垂直居中
  8. }
  9. .block {
  10. height: 100px;
  11. width: 100px;
  12. }

CSS Grid布局

CSS Grid布局(又名”网格”),是一个基于二维网格布局的系统,主要目的是改变我们基于网格设计的用户接口方式。
前端基础能力 - 前端基础知识 - 图9

Grid 参考资料下载

CSS 面向对象

OOCSS “Object Oriented CSS(面向对象的CSS)”,是一种写 CSS 的方法,其思想就是鼓励你把样式表看作“对象”的集合:创建可重用性、可重复性的代码段让你可以在整个网站中多次使用。

基于SCSS预编译器,使用 OOCSS 和 BEM 结合的编码方式,可以让 CSS 编写更加规范,更加高效。

BEM

BEM(Block-Element-Modifier),是一种用于 HTML 和 CSS 类名的命名约定。BEM 最初是由 Yandex 提出的,拥有巨大的代码库和可伸缩性。
BEM 是 BlockElementModifier 的缩写。

  • Block: 有实际意义的独立元素,例如:header, container, menu, checkbox, input
  • Element: Block 的子元素,没有独立的含义,例如:menu item, list item等定义成 menu__item, list_item
  • Modifier: 表示 BlcokElement 元素的不同行为和状态。例如:disable, highlighted等定义成 menu-diablemenu_item-highlighted
    前端基础能力 - 前端基础知识 - 图10

CSS定义

  1. .button {
  2. display: inline-block;
  3. }
  4. .button--state-success {
  5. color: #FFF;
  6. background: #569E3D linear-gradient(#79D858, #569E3D) repeat-x;
  7. }
  8. .button--state-danger {
  9. color: #900;
  10. }

OOCSS 和 BEM 案例结合

BEMOOCSS 不是CSS的标准,也是定义了一种 CSS 命名规范,下面就结合实际例子来分析, 安装 sass 编译器。

  1. npm i sass -g // 全局安装SASS
  2. sass bem.scss bem.css // SCSS文件编译成CSS文件

HTML

  1. <article class="m-card">
  2. <h1 class="card__title">Adorable 2BR in the sunny Mission</h1>
  3. <div class="card__body">
  4. <p>Vestibulum id ligula porta felis euismod semper.</p>
  5. </div>
  6. </article>

SCSS

结合 BEM 规范,更加清晰语义化的表达,同时配合 SCSS 级联语法,使 CSS 书写更加清晰,更加语义化。

  1. .m-card {
  2. display: flex;
  3. flex-direction: column;
  4. &__title {
  5. line-height: 30px
  6. }
  7. &__body {
  8. line-height: 20px;
  9. p {
  10. margin: 0
  11. }
  12. }
  13. }

SCSS编译后的CSS
  1. .m-card {
  2. display: flex;
  3. flex-direction: column;
  4. }
  5. .m-card__title {
  6. line-height: 30px;
  7. }
  8. .m-card__body {
  9. line-height: 20px;
  10. }
  11. .m-card__body p {
  12. margin: 0;
  13. }

Promise原理及实践

Promise原理是通过 Promise.prototype.thenPromise.prototype.catch 方法将观察者方法注册到被观察者 Promise 对象中,同时返回一个新的 Promise 对象,以便可以链式调用。

Promise 内部进行 等待态(Pending)执行态(Fulfilled)拒绝态(Rejected) 的状态流转。

前端基础能力 - 前端基础知识 - 图11

三种回调比较 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支持情况

前端基础能力 - 前端基础知识 - 图12

Async await支持情况

前端基础能力 - 前端基础知识 - 图13

Ajax 封装案例实践

本小节会实现一个基于 XMLHttpRequest 对象实现一个 Promise 的 Ajax 方法封装(面试专用考题),再对比 PromiseAsync Await的差异,以及他们的优缺点。

Promise实现

  1. /**
  2. * 请求服务
  3. * @param {Object} param
  4. * @param {String} param.url 请求URL
  5. * @param {String} param.type 默认 'GET'
  6. *
  7. * @return new Promise
  8. */
  9. function fetchRequest (param) {
  10. const type = param.type || 'GET',
  11. const url = param.url;
  12. if (!url) {
  13. new TypeError('param url must be set...')
  14. }
  15. return new Promise( (resolve, reject) => {
  16. let xhr = new XMLHttpRequest();
  17. xhr.open(type, url, true);
  18. // 1. 监听状态
  19. xhr.onreadystatechange = function () {
  20. // 2. readyState = 4, status = 200 是请求成功的标识
  21. if (xhr.readyState === 4) {
  22. if (xhr.status === 200) {
  23. resolve(xhr.responseText, xhr);
  24. } else {
  25. reject({
  26. code: xhr.status,
  27. message: xhr.response
  28. }, xhr);
  29. }
  30. }
  31. }
  32. xhr.send();
  33. })
  34. }

Promise VS Async await 使用区别

异步调用的最大问题是不能 Catch 到错误信息,同时编码上产生大量的 回调函数 或者 链式调用。
Async await 调用是把 Promise 的链式调用同步化,同时可以 Catch 到错误栈信息。

Promise 使用

  1. const getGithubHooks = () => {
  2. return fetchRequest({
  3. api: 'https://github.com/xxx'
  4. }).then(result => {
  5. return result;
  6. // result data
  7. }.catch(err => {
  8. // error
  9. })
  10. }

Async await 使用

  1. async function getGithubHooks() {
  2. let result = null
  3. // 同步 try catch 错误
  4. try {
  5. result = await fetchRequest({
  6. api: 'https://github.com/xxx'
  7. })
  8. return result
  9. } catch(err) {
  10. return err;
  11. }
  12. }

结语

前端开发涉及的基础知识点非常多,非常杂,本章带精选了项目开发实践中的核心知识点,带大家进行了结构化的梳理。

这是基础到进阶重要的一步,后面一章会和大家一起学习 Typescript 知识,如果对 ES6 语法 还不是很熟悉的话,可以参考阅读 ECMAScript 6 入门

思考题

Q: 除了Flexible通过JS方式来动态设置的方式,还有其他方案来处理多端H5适配问题?

Q: 如何实现三栏布局,中间自适应,采用 Flex 和 Grid 两种方案实现?

参考文献(部分需要翻墙)