Webpack Tapable Source Code

Introduction

Webpack 作为主流的打包工具,提供了自定义扩展的loaderplugin,丰富了周边生态。如果想自己写一个plugin,需要对相关的hooks有所了解,而 webpack 对hooks的实现则是建立在tapable这个库上的,本篇文章希望通过对tapable源码的梳理,以加深webpack的相关知识以及部分设计模式的理解。

Read more

Guide To Babel in 2021

Introduction

Babel 在 2021 年一共进行了 2 个minor版本的更新,增加了一些Stage 4 proposals的支持,以及一些Top-level的配置项(targets, assumptions)。伴随着这些更新,结合babel-loaderbabel-preset-react-app我们来探究一下在 2021 年该如何使用 Babel。

@babel/preset-env

@babel/preset-env是官方推荐的preset,只需要配置相关的targets就可以转换当前代码到目标环境的代码,遵循browserslist的相关配置,主要配置项如下:

targets

配置目标环境,如果不指定,则会转换所有 ES2015-ES2020 的代码到 ES5.而不是使用 browserslist 的defaults配置(> 0.5%, last 2 versions, Firefox ESR, not dead)。

useBuiltIns

配置@babel/preset-env如何处理polyfills,可选项为"usage"|"entry"|false

"entry"

这个配置会自动将import "core-js/stable";import "regenerator-runtime/runtime"转换为目标环境的按需引入,举个例子:

1
2
import 'core-js/stable';
import 'regenerator-runtime/runtime';

在不同环境下可能转换为:

1
2
import 'core-js/modules/es.string.pad-start';
import 'core-js/modules/es.string.pad-end';

但是有个缺点是用不到的 polyfill 也可能会引入进来,因为entry配置只针对目标环境,而不是具体代码

"usage"

这个配置则会自动引入代码中需要的 polyfill,且不需要显示声明import core-js,推荐使用该配置

false

不自动添加 polyfill,也不自动转换import core-js为按需引入

corejs

当 useBuiltIns 配置项为entryusage时生效,默认值为"2.0",建议配置为minor version的具体版本号

Read more

CommonJS modules in Node.js

Introduction

最初的 JavaScript 设计时没有模块化相关的概念,但是随着前端的发展及其工程化的引进和借鉴,相关的模块化思想也被引入了前端领域,其中CommonJS的模块化解决方案一直屹立不倒。为了更好的理解CommonJS,本篇文章依据Module:CommonJS modules相关文档,借鉴loader.js的相关源码,自己手动实现一个CommonJS module;

The Module Wrapper

每个被 require 的模块,在执行之前都被一个函数包裹,类似于:

1
2
3
(function (exports, require, module, __filename, __dirname) {
//Module code actually lives in here
});

这样做可以防止模块内的定义的变量提升到全局作用域,同时提供了moduleexports等变量供当前模块使用

What require() does:

require()加载模块的顺序具体可以参考这段伪代码,简单概括如下:

  1. 优先加载诸如path,fs等核心模块
  2. 先从缓存中找对应的模块是否已加载,没有则进行下一步
  3. 加载对应的以/, ./, ../开头的文件,如果不带文件后缀,则按照.js, .json,.node依次尝试,有的话加载对应的文件内容
  4. 加载对应的以/, ./, ../开头的文件夹,先查找文件夹下的package.json,如果package.json定义了”main”,则加载”main”对应的内容,没有则寻找对应的文件夹下的 index 文件,然后按照第三步的加载文件顺序依次查找并加载
  5. 加载对应的node_modules文件夹下的内容
  6. 抛出”not found”错误

对于最新版本的Node.js则增加了ECMAScript modules相关的加载规则,有兴趣的可以了解一下。

Read more

Binary Data In Frontend

Introduction

在日常的前端开发中,涉及到的工作内容大多和 UI 有关,比如说页面样式调整之类的基础工作。但是随着前端的发展,很多业务逻辑都放到了前端,例如文件生成和下载,图片处理等功能,这时候就涉及到了前端的二进制相关的内容,这篇文章从二进制相关内容和 API 出发,探究前端二进制相关的用途。

ArrayBuffer

ArrayBuffer 是一段基础的,固定长度的二进制数据,类似于其他语言的byte array。不能直接修改相关的内容,但是可以通过TypedArrayDataView进行读写。

1
2
3
4
5
const buffer = new ArrayBuffer(8);

const slicedBuffer = buffer.slice(0, 3);

console.log(buffer.byteLength, slicedBuffer.byteLength); // 8 3

如上所示,可以通过 new ArrayBuffer 创建新的 buffer, slice 截取 buffer 内容,slice 的操作类似于Array.prototype.slice

TypedArray

TypedArray 提供了多种类型用来处理和操作二进制数据,如下所示[1]
TypedArray

Uint8Array vs Uint8ClampedArray

Uint8Array 在处理小数位的时候采用的是向下取整,Uint8ClampedArray 则是采用四舍五入的形式取整,举个例子:

1
2
Uint8Array([0.9]); // 0
Uint8ClampedArray([0.9]); // 1

Uint8ClampedArray 当赋值在区间[0,255]之外,则只有取值为 0 或者 255 的两种情况,所以更多的运用于防止溢出的情况,例如增加图片的亮度[2]

Read more

Node.js Package Manager Part I - Package.json fields & Sematic Version

Introduction

成熟的编程语言一般都有相应的包管理工具,比如说 Python 中的 pip,Java 中的 Maven,PHP 中的 composer 等等。
对于 Node.js 来说,可选择的包管理工具比较多,比较有名的包括 npm,yarn 和 pnpm。
作为 Node.js 包管理系列文章的第一篇,这里先简单介绍 package 及其相关的周边内容

Package & Module

package 和 module 是相对的概念,官方文档上是这么定义的:

A package is a file or directory that is described by a package.json file. A package must contain a package.json file in order to be published to the npm registry.

A module is any file or directory in the node_modules directory that can be loaded by the Node.js require() function
可以看出来 package 和 module 相互纠缠,相互包含,共同组成了 Node.js 的生态

Read more

Javascript Position Attributes

引言

在日常的工作中,很多场景下需要知道一个元素的位置信息与尺寸信息,但是涉及到这方面的属性与方法很多,有时候难以抉择使用哪个方法或属性比较合适,这就需要我们详尽掌握元素位置与尺寸相关的知识,才能熟能生巧,灵活运用。

元素的尺寸

clientHeight&clientWidth

这两个属性都是整数形式的只读的属性,其中:

1
2
clientHeight = CSS height + CSS padding - height of horizontal scrollbar
clientWidth = CSS width + CSS padding

如下图所示:
MDN clientHeight and clientWidth schematic diagram

offsetHeight&offsetWidth

这两个属性也是整数形式的只读的属性,其中:

1
2
offsetHeight = CSS height(include horizontal scrollbar if rendered) + CSS padding + CSS borders (可以理解为元素包括边框的高度)
offsetWidth = CSS width(include vertical scrollbar if rendered) + CSS padding + CSS borders(可以理解为元素包括边框的宽度)

如下图所示:
MDN offsetHeight and offsetWidth schematic diagram

Read more

Regex

引言

正则表达式(Regular Expression)是使用单个字符串来描述、匹配一系列符合某个句法规则的一串字符。我在之前的学习工作中,遇到正则表达式,往往都是直接搜索相关问题的结果,或者尝试用其他方法解决。也尝试过学习相关的规则,但是过一段时间不用之后,往往会生疏乃至忘记。这篇文章尝试着理解并消化正则表达式相关的知识。

字符与位置

正则表达式是匹配模式,不是匹配字符,就是匹配位置

匹配字符

特殊字符

正则表达式除了匹配常规的 a-z,A-Z,0-9 等字符外,还存在着一些特殊字符,如下所示

字符 记忆方法
\t tab
\v vertical tab
\f form feed
\r return
\n new line

字符组

虽然叫字符组,但是只匹配字符组中的某个字符,比如[abc]只匹配 a,b,c 中的一个。对应的如果不想匹配某几个字符,则需要在字符组最前面加上**脱字符(^),形如[^abc],则表示匹配除 a,b,c 之外的其他字符。使用连字符(-)**则可以匹配连续的一段字符,形如 a-z,0-9。另外有如下相关的简写形式来表示对应的字符区间。

正则表达式 匹配区间 记忆方法
\d [0-9] digit
\D [^0-9] opposite to \d
\w [0-9a-zA-Z_] word**(注意有下划线_)**
\W [^0-9a-za-z_] opposite to \w
\s [\t\v\n\r\f] space character
\S [^\t\v\n\r\f] opposite to \s
. [^\n\r\u2028\u2029] 匹配除换行符之外的所有字符
Read more