xml地图|网站地图|网站标签 [设为首页] [加入收藏]
移动端开发小记,机制详解与
分类:web前端

JavaScript Event Loop 机制详解与 Vue.js 中实践应用

2017/09/09 · CSS · Event Loop, Vue

原文出处: 王下邀月熊   

JavaScript Event Loop 机制详解与 Vue.js 中实践应用归纳于笔者的现代 JavaScript 开发:语法基础与实践技巧系列文章。本文依次介绍了函数调用栈、MacroTask 与 MicroTask 执行顺序、浅析 Vue.js 中 nextTick 实现等内容;本文中引用的参考资料统一声明在 JavaScript 学习与实践资料索引。

前端小知识:为什么你写的 height:100% 不起作用

2018/01/08 · CSS · 3 评论 · height

原文出处: JiaXinYi   

移动端开发小记 – Flexbox

2015/11/12 · CSS · Flexbox

原文出处: 淘宝前端团队(FED)- 凌恒   

图片 1

在开发移动端页面的时候,出去布局方便和减少代码量的考虑,使用了 Flexbox 的布局方式,在其中也遇到了一些问题,简单记录下。

1. 事件循环机制详解与实践应用

JavaScript 是典型的单线程单并发语言,即表示在同一时间片内其只能执行单个任务或者部分代码片。换言之,我们可以认为某个同域浏览器上下中 JavaScript 主线程拥有一个函数调用栈以及一个任务队列(参考 whatwg 规范);主线程会依次执行代码,当遇到函数时,会先将函数入栈,函数运行完毕后再将该函数出栈,直到所有代码执行完毕。当函数调用栈为空时,运行时即会根据事件循环(Event Loop)机制来从任务队列中提取出待执行的回调并执行,执行的过程同样会进行函数帧的入栈出栈操作。每个线程有自己的事件循环,所以每个 Web Worker有自己的,所以它才可以独立执行。然而,所有同属一个 origin 的窗体都共享一个事件循环,所以它们可以同步交流。

Event Loop(事件循环)并不是 JavaScript 中独有的,其广泛应用于各个领域的异步编程实现中;所谓的 Event Loop 即是一系列回调函数的集合,在执行某个异步函数时,会将其回调压入队列中,JavaScript 引擎会在异步代码执行完毕后开始处理其关联的回调。

图片 2

在 Web 开发中,我们常常会需要处理网络请求等相对较慢的操作,如果将这些操作全部以同步阻塞方式运行无疑会大大降低用户界面的体验。另一方面,我们点击某些按钮之后的响应事件可能会导致界面重渲染,如果因为响应事件的执行而阻塞了界面的渲染,同样会影响整体性能。实际开发中我们会采用异步回调来处理这些操作,这种调用者与响应之间的解耦保证了 JavaScript 能够在等待异步操作完成之前仍然能够执行其他的代码。Event Loop 正是负责执行队列中的回调并且将其压入到函数调用栈中,其基本的代码逻辑如下所示:

JavaScript

while (queue.waitForMessage()) { queue.processNextMessage(); }

1
2
3
while (queue.waitForMessage()) {
  queue.processNextMessage();
}

完整的浏览器中 JavaScript 事件循环机制图解如下:图片 3

在 Web 浏览器中,任何时刻都有可能会有事件被触发,而仅有那些设置了回调的事件会将其相关的任务压入到任务队列中。回调函数被调用时即会在函数调用栈中创建初始帧,而直到整个函数调用栈清空之前任何产生的任务都会被压入到任务队列中延后执行;顺序的同步函数调用则会创建新的栈帧。总结而言,浏览器中的事件循环机制阐述如下:

  • 浏览器内核会在其它线程中执行异步操作,当操作完成后,将操作结果以及事先定义的回调函数放入 JavaScript 主线程的任务队列中。
  • JavaScript 主线程会在执行栈清空后,读取任务队列,读取到任务队列中的函数后,将该函数入栈,一直运行直到执行栈清空,再次去读取任务队列,不断循环。
  • 当主线程阻塞时,任务队列仍然是能够被推入任务的。这也就是为什么当页面的 JavaScript 进程阻塞时,我们触发的点击等事件,会在进程恢复后依次执行。

为什么你写的height:100%不起作用?

这个知识不算冷门的,但是用的时候可能还是会有些懵逼,不能生效时搜一搜就能找到答案了,但是你真的懂了吗?为什么想要设置一个全屏元素的时候,高度不受%的控制?

什么是 Flexbox

说到 Flexbox,大家应该都不陌生,网络上也有 N 多的教程。但是在这里还是简单说一下,就当回顾知识了吧。

CSS 2.1 定义了四种布局模式 ― 由一个盒与其兄弟、祖先盒的关系决定其尺寸与位置的算法:

  • 块布局 ― 为了呈现文档而设计出来的布局模式;
  • 行内布局 ― 为了呈现文本而设计出来的布局模式;
  • 表格布局 ― 为了用格子呈现 2D 数据而设计出来的布局模式;
  • 定位布局 ― 为了非常直接地定位元素而设计出来的布局模式,定位元素基本与其他元素毫无关。

而 Flexbox(伸缩布局)是为了呈现复杂的应用与页面而设计出来的,一种更加方便有效,能够在未知或者动态尺寸的情况下自由分配容器空间的布局方式。

要说明 Flexbox 的布局模型,就必须要放规范上的这张图:

图片 4

  • main axis(主轴)
    • main dimension(主轴方向)
    • The main axis of a flex container is the primary axis along which flex items are laid out. It extends in the main dimension.
    • 主轴是伸缩项目在伸缩容器里分布所遵循的主要轴线,在主轴方向上延伸。
  • main-start(主轴起点)
    • main-end(主轴终点)
    • The flex items are placed within the container starting on the main-start side and going toward the main-end side.
    • 伸缩项目从容器的主轴起点开始放置,直到主轴终点。
  • main size(主轴尺寸)
    • main size property(主轴尺寸属性)
    • A flex item’s width or height, whichever is in the main dimension, is the item’s main size. The flex item’s main size property is either the width or height property, whichever is in the main dimension.
    • 伸缩项目在主轴方向上的长或者宽是这个项目的主轴尺寸。一个伸缩项目的主轴属性是在主轴方向上的长或者宽属性。
  • cross axis(侧轴)
    • cross dimension(侧轴方向)
    • The axis perpendicular to the main axis is called the cross axis. It extends in the cross dimension.
    • 和主轴垂直的轴叫做侧轴,它在侧轴方向上延伸。
  • cross-start(侧轴起点)
    • cross-end(侧轴终点)
    • Flex lines are filled with items and placed into the container starting on the cross-start side of the flex container and going toward the cross-end side.
    • 包含伸缩元素的伸缩行从容器的侧轴起点开始放置,直到侧轴终点。
  • cross size(侧轴尺寸)
    • cross size property(侧轴尺寸属性)
    • The width or height of a flex item, whichever is in the cross dimension, is the item’s cross size. The cross size property is whichever of width or height that is in the cross dimension.
    • 伸缩项目在侧轴方向上的长或者宽是它的侧轴尺寸。侧轴尺寸属性则是在侧轴方向上的长或者宽属性。

2. 函数调用栈与任务队列

在变量作用域与提升一节中我们介绍过所谓执行上下文(Execution Context)的概念,在 JavaScript 代码执行过程中,我们可能会拥有一个全局上下文,多个函数上下文或者块上下文;每个函数调用都会创造新的上下文与局部作用域。而这些执行上下文堆叠就形成了所谓的执行上下文栈(Execution Context Stack),便如上文介绍的 JavaScript 是单线程事件循环机制,同时刻仅会执行单个事件,而其他事件都在所谓的执行栈中排队等待:图片 5

而从 JavaScript 内存模型的角度,我们可以将内存划分为调用栈(Call Stack)、堆(Heap)以及队列(Queue)等几个部分:图片 6

其中的调用栈会记录所有的函数调用信息,当我们调用某个函数时,会将其参数与局部变量等压入栈中;在执行完毕后,会弹出栈首的元素。而堆则存放了大量的非结构化数据,譬如程序分配的变量与对象。队列则包含了一系列待处理的信息与相关联的回调函数,每个 JavaScript 运行时都必须包含一个任务队列。当调用栈为空时,运行时会从队列中取出某个消息并且执行其关联的函数(也就是创建栈帧的过程);运行时会递归调用函数并创建调用栈,直到函数调用栈全部清空再从任务队列中取出消息。换言之,譬如按钮点击或者 HTTP 请求响应都会作为消息存放在任务队列中;需要注意的是,仅当这些事件的回调函数存在时才会被放入任务队列,否则会被直接忽略。

譬如对于如下的代码块:

JavaScript

function fire() { const result = sumSqrt(3, 4) console.log(result); } function sumSqrt(x, y) { const s1 = square(x) const s2 = square(y) const sum = s1 + s2; return Math.sqrt(sum) } function square(x) { return x * x; } fire()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function fire() {
    const result = sumSqrt(3, 4)
    console.log(result);
}
function sumSqrt(x, y) {
    const s1 = square(x)
    const s2 = square(y)
    const sum = s1 + s2;
    return Math.sqrt(sum)
}
function square(x) {
    return x * x;
}
 
fire()

其对应的函数调用图(整理自这里)为:图片 7

这里还值得一提的是,Promise.then 是异步执行的,而创建 Promise 实例 (executor) 是同步执行的,譬如下述代码:

JavaScript

(function test() { setTimeout(function() {console.log(4)}, 0); new Promise(function executor(resolve) { console.log(1); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(2); }).then(function() { console.log(5); }); console.log(3); })() // 输出结果为: // 1 // 2 // 3 // 5 // 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(function test() {
    setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);
})()
// 输出结果为:
// 1
// 2
// 3
// 5
// 4

我们可以参考 Promise 规范中有关于 promise.then 的部分:

JavaScript

promise.then(onFulfilled, onRejected) 2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1]. Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.

1
2
3
4
5
promise.then(onFulfilled, onRejected)
 
2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
 
Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.

规范要求,onFulfilled 必须在执行上下文栈(Execution Context Stack) 只包含 平台代码(platform code) 后才能执行。平台代码指引擎,环境,Promise 实现代码等。实践上来说,这个要求保证了 onFulfilled 的异步执行(以全新的栈),在 then 被调用的这个事件循环之后。

1.百分比宽高的设定

按照w3c中的width和height属性,可以明确%设定宽高是根据父元素的宽高来的:
http://www.w3school.com.cn/cs…
http://www.w3school.com.cn/cs…

图片 8

使用 Flexbox

现在大部分的主流浏览器都已经支持了 Flexbox 或者它的旧版语法。如果是使用在移动端,基本上是都支持的。为了兼容新老版本的语法,可以这样使用( Less ):

LESS

//父元素 .flex-box() { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; } //子元素 .flex(@v) { -webkit-box-flex: @v; -moz-box-flex: @v; -webkit-flex: @v; -ms-flex: @v; flex: @v; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//父元素
.flex-box() {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
}
//子元素
.flex(@v) {
  -webkit-box-flex: @v;
  -moz-box-flex:  @v;
  -webkit-flex:  @v;
  -ms-flex:  @v;
  flex:  @v;
}

 

为子元素设置了  flex: 1 ,这样容器里面的子元素可以均分容器的空间。当然,可以为某个子元素指定一个宽度,这样剩下的子元素就会平分剩下的空间。

如下图中的品牌墙:

图片 9

本文由澳门新葡亰手机版发布于web前端,转载请注明出处:移动端开发小记,机制详解与

上一篇:简单了解CSS3的all属性,研究人员称HTML5可以被用 下一篇:实例对比,波纹效果按钮
猜你喜欢
热门排行
精彩图文