前端开发中的加载性能优化

不同于需要安装的安卓 App 或者 iOS App,前端 App 所有的资源文件都要通过网络进行下载,所以加载速度越快对用户的体验来说就越好。因为我们也需要在加载的时候进行优化,减少不必要的资源请求。

# 文本内容的优化

# 压缩代码

HTML,JS,CSS 都有相应的工具可以进行代码压缩,可以使用 webpack 工具方便的进行代码压缩。JS 使用工具terser (opens new window),CSS 使用CSS Nano (opens new window), HTML 使用HTMLMinifier (opens new window)进行代码压缩。

# 使用 GZIP 进行压缩

现代化浏览器都支持 GZIP 进行压缩,并且服务器端 NGINX 也可以很方便的配置使用 GZIP 压缩请求的资源文件。

# 减少第三方 JS 的引用

在 2010 年左右,人们都爱使用 jQuery 框架进行开发,因为 jQuery 抹平了浏览器之间的差异性,大大提高了开发效率。但随着浏览器吸收了 jQuery 的各种优点,例如可以使用document.querySelector很方便的像 jQuery 一样选择元素。现在的前端开发已经不那么需要 jQuery 了,你可以访问You might not need jQuery (opens new window)来查找替代的方法。

# webpack 相关的优化

可以使用 webpack 的SplitChunksPlugin (opens new window), 分割 JS 文件,只打包首次载入需要的 JS 文件来减少网络请求。 我们也可以使用Tree Shaking (opens new window)来引入需要的 JS 库,避免不使用的代码被打包进来。

# 图像内容的优化

# 移除不使用的图像

由于网站项目的更新,很多时候有一些不被使用的图像资源被打包进输出目录。这样会导致无谓的资源加载,需要及时发现和删除。

# 选择合适的图像格式

众所周知,png 图像可以展示透明效果,但是 jpg 图像做不到。但是因为 png 比 jpg 多了一个 alpha 通道,所以占用的空间也大,如果不需要透明效果展示,相同的图像效果,jpg 会占用更小的空间。

# 移除图像的 Metadata

图像的 Metadata 对于 web 资源来说并没有什么意义,所以移除 Metadata 可以减少图像占用空间。

# 调整图像尺寸

如果显示的尺寸和实际尺寸差距过大,也会造成不必要的资源浪费。

# 调整图像质量

在很多情况下,对图像质量没有过高的要求,可以损失质量来换取更小的空间。

# 压缩图像

现在有很多算法可以有损或者无损的压缩图像,在发布之前,可以使用压缩工具减少图像的使用空间。

# HTTP 请求优化

可以使用 HTTP2 协议来提高网站的载入速度,HTTP2 允许多路复用,多个请求可以使用单一的 HTTP 连接来处理,大大提高了载入速度。 这里有一个demo (opens new window) 大家可以使用浏览器打开,感受一下。

# HTTP Cache

可以通过缓存技术来提高网站的载入速度,当用户再次访问网站的时候可以直接使用缓存而不必再次请求资源文件。我们甚至可以使用Service Workers (opens new window)缓存整个网站,保证可以离线使用。

# 总结

载入优化需要从各个方面入手,查找分析前端 App 的资源文件,并从网络请求和缓存下手才能达到最优的效果。

# 参考

Loading Performance (opens new window)

vue实现datepicker组件

前端组件化的今天,选择日期组件算是最复杂的一个了吧。我们需要考虑日期显示的多语言,弹框的显示位置,以及日期月份的计算。

# 日期相关计算

计算每个月的天数,这个是不变的。一三五七八十腊,三十一天永不差;四六九冬三十整,惟有二月二十八,闰年还要把一日加。

function getDayCountOfMonth(year: number, month: number) {
  if (month === 3 || month === 5 || month === 8 || month === 10) {
    return 30;
  }

  if (month === 1) {
    if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
      return 29;
    } else {
      return 28;
    }
  }

  return 31;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

计算每个月的 1 号是周几:

function getFirstDayOfMonth(date: Date) {
  const temp = new Date(date.getTime());
  temp.setDate(1);
  return temp.getDay();
}
1
2
3
4
5

其他计算函数可以去看完整代码date.ts (opens new window)

# 弹窗定位相关

点击日期输入框,需要弹出下拉菜单,下拉菜单位置的计算并不简单,需要考虑到浏览器的窗口,如果输入框在下方,需要在上方弹出,如果输入框在上方,需要在下方弹出。 并且用户滑动到可视范围以外的话,还要可以隐藏。所以我们这里引用了一个比较成熟的第三方库来解决这个问题。 popperjs (opens new window) 这个库可以帮我们解决上面的问题,节约开发的成本。

# 多语言支持

日期选择组件,是为数不多需要做多语言支持的组件。因为我们要显示月份和每周的名字。就不得不做多语言处理了。目前我做的 demo 里面包含了三种语言的支持。

# 总结

日期选择组件,常见的需求有单日期选择和日期范围选择。优秀的组件需要同时满足这两个需求。并且尽量保证代码的简洁性。为此,我在 Github 写了一个 demo 库,希望可以给大家参考。

vue-datepicker (opens new window)

前端中debounce和throttle函数说明

前端开发中,debounce 和 throttle 函数经常会被使用,但是很多人分不清两者的区别,今天就来说明一下。

# debounce

我们首先来看 debounce (防抖动)的代码实现:

function debounce(fn: Function, time: number): Function {
  let timeout = 0;

  return (...args: any) => {
    window.clearTimeout(timeout);
    timeout = window.setTimeout(() => {
      fn(...args);
    }, time);
  };
}
1
2
3
4
5
6
7
8
9
10

从代码中可以看出,该函数在执行的时候会清除上次的定时器,然后设置一个新的定时器,等待一定时间之后执行。所以在短时间内调用多次的话,只会执行最后一次。

# throttle

throttle (节流)函数的代码实现:

function throttle(fn: Function, time: number): Function {
  let inThrottle: boolean;

  return (...args: any) => {
    if (!inThrottle) {
      inThrottle = true;
      fn(...args);
      setTimeout(() => (inThrottle = false), time);
    }
  };
}
1
2
3
4
5
6
7
8
9
10
11

从代码中可以看出,函数在设置的阈值时间之内只会执行一次。

# 总结

debounce: 将频繁触发的事件合并为一次执行,适用场景例如输入名称进行搜索,使用 debounce 可以减少对服务器的请求,在用户输入完毕之后再进行请求。

throttle: 设置一个阈值,在阈值内函数只会执行一次,例如 resize 事件或者 scoll 事件,防止浏览器频繁执行,降低网页响应速度。

# 参考

The Difference Between Throttling and Debouncing (opens new window)