最新的文章通过 Issues 形式发布了,请移步 Issues。 PS. 老了,hexo 也懒得折腾了,😅

原文:Optimising the front end for the browser
笔记:涂鸦码龙

优化关乎速度和满意度。

  • 从用户体验(UX)角度,我们希望前端网页可以快速加载
  • 从开发体验(DX)角度,我们希望前端是快速,简洁,规范的

    浏览器都做了什么

我们希望浏览器打开一个简单的网页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<title>The "Click the button" page</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<h1>
Click the button.
</h1>
<button type="button">Click me</button>
<script>
var button = document.querySelector("button");
button.style.fontWeight = "bold";
button.addEventListener("click", function () {
alert("Well done.");
});
</script>
</body>
</html>

浏览器如何渲染网页

  1. 使用 HTML 创建文档对象模型(DOM
  2. 使用 CSS 创建 CSS 对象模型(CSSOM
  3. 基于 DOM 和 CSSOM 执行脚本(Scripts
  4. 合并 DOM 和 CSSOM 形成渲染树(Render Tree
  5. 使用渲染树布局(Layout)所有元素
  6. 渲染(Paint)所有元素

附图

阅读全文 »

原文:“JavaScript Array Methods: Mutating vs. Non-Mutating
笔记:涂鸦码龙

JavaScript 提供了多种新增,移除,替换数组元素的方法,但是有些会影响原来的数组;有些则不会,它是新建了一个数组。

注意:区分以下两个方法的不同点:

  1. array.splice() 影响原来的数组
  2. array.slice() 不影响原来的数组

I. 新增:影响原数组

使用 array.push()array.ushift() 新增元素会影响原来的数组。

1
2
3
let mutatingAdd = ['a', 'b', 'c', 'd', 'e'];
mutatingAdd.push('f'); // ['a', 'b', 'c', 'd', 'e', 'f']
mutatingAdd.unshift('z'); // ['z', 'b', 'c', 'd', 'e' 'f']
阅读全文 »

题外话,说几句我对 React 与 React Native 关系的理解:

  • React 主要用于浏览器端实现一些 UI 组件,也可用于服务端渲染。React 可以使用 HTML 提供的标签,也可封装自定义的组件,React 也提供直接操作 DOM 的方法;
  • React Native 主要用于实现客户端应用(App)的 UI 组件,它只能使用 Facebook 封装的 Native 组件,或者自己封装的 Native 组件。开发中主要借助 JavaScript,基本告别 HTML 和 CSS 了,不过优点是可以用 ES6。

言归正传,正文开始

之前没搞过 React ,直接开撸的 React Native,使用过程也是各种踩坑填坑,磕磕绊绊,这里简单总结一下我用过的组件通信的几种方式吧。

React 最基础的 props 和 state

组件内部用 state

1
2
3
4
5
6
7
8
9
10
11
12
13
constructor(props) {
super(props);
this.state = {
isOnline: true //组件 state
};
}
render() {
if(this.state.isOnline){
//...剩余代码
}
//...剩余代码
}
阅读全文 »

原文:Filtered background with fallback for legibility
翻译:涂鸦码龙

你知道现在有多火吗?用这种很大的,高质量的,支持 Retina 屏的模糊的 JPEG 图片作为 header 背景 :

See the Pen Web site header, circa 2016 by Taylor Hunt (@tigt) on CodePen.

潜在的问题是如果浏览器不支持滤镜 filter,文字将不可读 。这违背了可访问性的原则,再完美的视觉也无济于事。

阅读全文 »

原文:4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them
笔记:涂鸦码龙

译者注:本文并没有逐字逐句的翻译,而是把我认为重要的信息做了翻译。如果您的英文熟练,可以直接阅读原文。

本文将探索常见的客户端 JavaScript 内存泄漏,以及如何使用 Chrome 开发工具发现问题。

简介

内存泄漏是每个开发者最终都要面对的问题,它是许多问题的根源:反应迟缓,崩溃,高延迟,以及其他应用问题。

什么是内存泄漏?

本质上,内存泄漏可以定义为:应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收。编程语言管理内存的方式各不相同。只有开发者最清楚哪些内存不需要了,操作系统可以回收。一些编程语言提供了语言特性,可以帮助开发者做此类事情。另一些则寄希望于开发者对内存是否需要清晰明了。

JavaScript 内存管理

JavaScript 是一种垃圾回收语言。垃圾回收语言通过周期性地检查先前分配的内存是否可达,帮助开发者管理内存。换言之,垃圾回收语言减轻了“内存仍可用”及“内存仍可达”的问题。两者的区别是微妙而重要的:仅有开发者了解哪些内存在将来仍会使用,而不可达内存通过算法确定和标记,适时被操作系统回收。

JavaScript 内存泄漏

垃圾回收语言的内存泄漏主因是不需要的引用。理解它之前,还需了解垃圾回收语言如何辨别内存的可达与不可达。

Mark-and-sweep

大部分垃圾回收语言用的算法称之为 Mark-and-sweep 。算法由以下几步组成:

  1. 垃圾回收器创建了一个“roots”列表。Roots 通常是代码中全局变量的引用。JavaScript 中,“window” 对象是一个全局变量,被当作 root 。window 对象总是存在,因此垃圾回收器可以检查它和它的所有子对象是否存在(即不是垃圾);
  2. 所有的 roots 被检查和标记为激活(即不是垃圾)。所有的子对象也被递归地检查。从 root 开始的所有对象如果是可达的,它就不被当作垃圾。
  3. 所有未被标记的内存会被当做垃圾,收集器现在可以释放内存,归还给操作系统了。

现代的垃圾回收器改良了算法,但是本质是相同的:可达内存被标记,其余的被当作垃圾回收。

不需要的引用是指开发者明知内存引用不再需要,却由于某些原因,它仍被留在激活的 root 树中。在 JavaScript 中,不需要的引用是保留在代码中的变量,它不再需要,却指向一块本该被释放的内存。有些人认为这是开发者的错误。

为了理解 JavaScript 中最常见的内存泄漏,我们需要了解哪种方式的引用容易被遗忘。

阅读全文 »

原文:Debouncing and Throttling Explained Through Examples
笔记:涂鸦码龙

防抖(Debounce)和节流(throttle)都是用来控制某个函数在一定时间内执行多少次的技巧,两者相似而又不同。

当我们给 DOM 绑定事件的时候,加了防抖和节流的函数变得特别有用。为什么呢?因为我们在事件和函数执行之间加了一个控制层。记住,我们是无法控制 DOM 事件触发频率的。

看下滚动事件的例子:

See the Pen Scroll events counter by Corbacho (@dcorb) on CodePen.

当使用触控板,滚动滚轮,或者拖拽滚动条的时候,一秒可以轻松触发30次事件。经我的测试,在智能手机上,慢慢滚动一下,一秒可以触发事件100次之多。这么高的执行频率,你的滚动回调函数压力大吗?

早在2011年,Twitter 网站抛出了一个问题:向下滚动 Twitter 信息流的时候,变得很慢,很迟钝。John Resig 发表了一篇博客解释这个问题,文中解释到直接给 scroll 事件关联昂贵的函数,是多么糟糕的主意。

John(5年前)建议的解决方案是,在 onScroll 事件外部,每 250ms 循环执行一次。简单的技巧,避免了影响用户体验。

现如今,有一些稍微高端的方式处理事件。我来结合用例介绍下 Debounce,Throttle 和 requestAnimationFrame 吧。

阅读全文 »

原文: Say Hello To ES2015
笔记:涂鸦码龙

介绍 ES2015

ES2015 是新版的 JavaScript,Node.js 已经完全支持,浏览器端可以用 Babel 库编译。

运行本文的示例代码,可以用 JSBin 环境,也可以结合原文中的测试题检测学习效果。

使用 let 和 const

ES2015 可以用 constlet 替换 var ,它们定义了块级作用域变量。

示例代码:

1
2
3
4
5
for (var lc=0; lc < 10; lc++) {
let value = lc;
}
console.log(value); //抛出错误

变量 value 只能在 for 循环中使用。

constlet 很像,但是它定义的变量值无法改变。

示例代码:

1
2
3
const user = {name: "Arunoda"};
user.name = "Susiripala";
console.log(user.name);

改变的是变量 user 内部的属性,并没有改变 user 本身。

许多人更喜欢用 const 代替 let

使用箭头函数整理你的代码

熟悉的方式:

1
2
3
4
5
const numbers = [10, 20, 30, 50];
const multiplyBy10 = numbers.map(function(a) {
return a * 10;
});
console.log(multiplyBy10);

使用箭头函数以后:

1
2
3
const numbers = [10, 20, 30, 50];
const multiplyBy10 = numbers.map(a => a * 10);
console.log(multiplyBy10);

如果方法接受不止1个参数,可以这么写:

1
2
3
const numbers = [10, 20, 30, 50];
const multiplyByIndex = numbers.map((a, i) => a * i);
console.log(multiplyByIndex);

箭头函数返回一个对象的话,需要加圆括号:

1
2
3
const numbers = [10, 20, 30, 50];
const multiplyBy10 = numbers.map(a => ({res: a * 10}));
console.log(multiplyBy10);
阅读全文 »

原文:‘flex-grow’ is weird. Or is it?
翻译:涂鸦码龙

当我刚接触 flex-grow 时,为了探寻它的工作原理,做了一个简单的例子

我以为理解的挺透彻了,但是当我把它应用到同事的网站上时,效果跟我想象的完全不同。无论怎么改,布局都无法像我的demo那样展示。这时我才意识到,我并没有完全掌握 flex-grow

flex-grow 为何不正常

在我深入剖析 flex-grow 的功能之前,我想解释一下我起初犯了什么错。

我认为所有 flex 元素的 flex-grow 如果设置为 1 ,它们将一样宽。如果某一项的 flex-grow 设置为 2 ,它将是其它元素的二倍宽。

一切听起来顺理成章。我上面的例子 貌似也印证了这点。父元素是900px宽,flex-grow: 2 的 section 元素计算后是600px宽,flex-grow: 1 的 aside 元素计算后是300px宽。

如你所见,它在这个例子中展现的近乎完美,可是在真实的例子中却不尽人意,即使我们用了完全相同的 CSS。事实证明,问题不在 CSS,而在于内容(或者说缺乏内容)。我的测试用例只用了两个空元素,无法展示这个属性最重要的细节。

flex-grow 到底如何工作

啰嗦了半天,我终于要解释 flex-grow 没有尽如人意的原因了。

为了阐明原因,我又搞了个栗子 ,所有的设置跟第一个栗子 完全一致,只不过 section 和 aside 元素不再是空的。看吧,两个元素的比例不再是 2 : 1,flex-grow 为 1 的元素的确比 flex-grow 为 2 的元素宽不少呐。

阅读全文 »

原文:Moving along a curved path in CSS with layered animation
翻译:涂鸦码龙

译者注:部分代码示例在原文中可以看效果(作者写在博文里面了…),我偷懒把它做成Gif图了。

CSS 的 animations (动画) 和 transitions(变换)擅于实现从点 A 到点 B 的直线运动,运动轨迹是直线路径。给一个元素添加了 animation 或者 transition 以后,无论你如何调整贝塞尔曲线,都无法让它沿着弧形路径运动。你可以通过自定义 timing function 属性,做出弹动的效果,但是它沿着 X 和 Y 轴相对移动的值永远是相同的。

与其使用 JavaScript 实现外观自然的运动,不如尝试用这种简单的方式:分层动画,绕过已有的限制。通过使用两个或多个元素实现动画效果,我们可以更加细粒度地控制某个元素的路径,沿着 X 轴运动使用一种 timing function ,沿着 Y 轴运动使用另一种 timing function 。

问题所在

附图1

当我们深入探讨解决方案之前,看看到底问题在哪。CSS animationstransitions 限制我们只能沿直线路径运动。元素总是沿着点 A 到点 B 的最短路径运动,如果我们另辟蹊径,告诉 CSS 沿着“更好的路径”,而不是“最短路径”运动呢?

用 CSS (开启硬件加速)实现两点之间的运动,最直截了当的方式是使用 transformtranslate 在一定时间内移动某个元素。这就产生了直线运动。在 @keyframes 中,我们打算在 (0,0) 和 (100,-100) 间来回运动,见上图例子:

1
2
3
4
5
6
7
8
9
@keyframes straightLine {
50% {
transform: translate3D(100px, -100px, 0);
}
}
.dot {
animation: straightLine 2.5s infinite linear;
}

这些看起来并不难懂,但我们稍等片刻,思考一下我们需要的解决方案,拆分开来的动画,视觉上长什么样子呢。

0% 时,元素从 (0,0) 出发,50% 时,我们用了 translate3D(100px, -100px, 0) 把它移动到 (100,-100),然后原路返回。换句话说,我们把元素向右移动了 100px,向上移动了 100px,两个方向联合作用使元素沿着一个角度运动。

阅读全文 »

原文:Checkbox Trickery with CSS
翻译:涂鸦码龙

Checkbox 复选框相当好用,加对 CSS 魔法有奇效。此文旨在展示一些利用 checkbox 实现的有创意的东西,并且文中的例子没用 JavaScript 哟。

基本配方

从 HTML 着手。

1
2
<input id="toggle" type="checkbox">
<label for="toggle">

此处无技巧可言。<label>for 属性匹配 <input>id 属性,因此点击 <label> 可以控制 <input> 复选框。这点尤其重要,因为下一步将隐藏 <input>

1
2
3
4
input {
position: absolute;
left: -9999px;
}

为什么不用 display: none ?因为屏幕阅读机和键盘 Tab 会忽略它。此方法让 <input> 保持在文档流中,但是让它离屏隐藏(超出屏幕可见范围达到隐藏)。

隐藏 <input> 以后,我们更容易大展身手。我们仍需传达选中/未选两种状态,但是可以通过 <label> 完成。真正的派对开始啦。

1
2
3
input:checked + label {
/* 牛X闪闪的样式 */
}

我们使用 :checked 伪类, 和相邻兄弟元素选择器(+)的组合达到目的,当复选框选中时,找到紧随其后的 <label>元素,加上想要的样式。还可以利用 <label> 中的伪元素( ::before::after)实现更有创意的想法。

1
2
3
input:checked + label::before {
/* 指示器的样式 */
}

来,看看实际效果吧。例子用到了以上提及的基本配方,把一个普普通通的复选框改造得当人眼前一亮。

阅读全文 »