1. 首页
  2. IT资讯

小斑瘦身记:4M 到 300K 的华丽变身

console.info

Hello ~ 我是小斑,一个富文本编辑器。今天,咱来聊聊体重,对,没错!就是那令人头疼的体重!任何事物都烦这个体重,当然也包括我,当我的创造者阿飞发布小斑的第一个版本时,小斑足足有 4M 之重!看着控制台茫茫多的流量消耗,他愣住了:你。。。特么的这么怎么胖,足足 4M(包括 CSS)!但小斑我也很无奈呀!代码是你写的,怪我咯!

软件发展总归避免不了两个过程:野蛮生长 & 精心优化,小斑长算是长出来了,但这一身肥膘得好好减减!

image

哝!上图,就是我刚诞生时,所包含的 JS 模块,足足 3M 之多!但小斑的核心代码仅仅存在于右下角蓝色的方块内,其他都属于增强体验的代码,包括 UI 框架,代码高亮,表情的便捷输入等等。

接下来,小斑瘦身记,正式开始。

数据压缩:从 4M 到 1.5M

提到数据压缩,就不得不提一个鼎鼎大名的数据压缩算法:Gzip

Gzip 全名: GNU zip 开源的数据压缩算法,广泛应用于网络传输。大家可能会有疑惑,数据压缩算法这么多,凭啥 Gzip 如此出名?因为压缩比高?

no!no!no!仅仅因为它是 GNU 开头,互联网诞生于一个专利横飞的年代,各种好用的压缩算法受限于专利,并不能使用在开放的互联网上,因此大家需要一个高效且开源的压缩算法,Gzip 因此诞生!

使用 Gzip 很简单,过程大概也就 3 步:

  1. 服务端使用 Gzip 将需要发送的数据进行压缩;
  2. 客户端将接收的数据并进行 Gzip 解压;
  3. 客户端使用解压后的数据;

因此,Gzip 是服务端对输出内容的优化,且需要客户端支持(客户端需要有解压的能力)。

等等!需要客户端支持?会不会很麻烦?

当然不!别忘了 Gzip 的全名是 GNU zip ,它可是开源的!只要客户端内置 Gzip 模块,就可以完全使用 Gzip 压缩后的数据!因此可以简单的认为:只要服务端进行了 Gzip 压缩,文件的体积能瞬间减少一半!

至于如何开启 Gzip ,各个服务平台都有与之相对应的方法。小斑由 Nginx 提供服务,开启 Gzip 模块后,小斑体积骤减至 1.5M ,看起来像个瘦子了!但也仅仅是看起来!我依然是个胖子,只不过神奇的压缩算法把我给变瘦了,滤镜下的胖子,再怎么好看依旧是个胖子!

ps:并不是所有的浏览器都内置了 Gzip 模块,IE 系列就没有(大家赶紧抛弃 IE 吧),因此真实环境下客户端需要一些特殊的请求头,来控制服务器返回的具体内容。

移除冗余代码:锐减 1.4M

冗余代码:就是那些打包到项目中,却没被使用的代码。看到这个标题,有些小伙伴可能会心生不屑,哼!这人连 Webpack 支持 TreeShaking 了都不知道,也不知道是用那个版本的 Webpack,还在说这些老掉牙的问题,真没意思!

稍等!小斑诞生环境中的 Webpack 早已 4.42.0,完全支持 TreeShaking,在这里,小斑只想问一个简单的问题:TreeShaking 能解决样式冗余吗?

不能,直到现在为止,依然没有一种较好的办法,在代码打包时,解决样式冗余的问题。但在代码打包前,也就是编写代码的时候,却可以!

一行代码,700K 的体积!

相信使用过 Antd 组件库的小伙伴,使用 Antd 时第一行代码都长这样:

import "antd/dist/antd.css";

但我悄悄告诉你,这个文件,整整 700K !请问:有何感想?

小斑为此深入查看了 Antd 代码,了解到其实每个组件下都有单独的样式文件,单独引入样式,就能可以移除多余 CSS 代码。示例如下:

// antd 的公共样式,必须引入
import "antd/lib/style/core/index.less";

// 组件样式,单独引入即可
import "antd/lib/button/style/index.less";
import "antd/lib/xxx/style/index.less";

考虑到,编辑器内,用到了一些高级特性,需要较高版本浏览器的支持,故只考虑了最近几个版本的 Chrome(包括使用 Chromium 核心的其他浏览器) 、FireFox 、Safari ,一些已经成为标准的 CSS3 样式前缀就没必要兼容了,因此小斑的兼容性如下:

"browserslist": [
     "last 1 chrome version",
     "last 1 firefox version",
     "last 1 safari version"
 ]

不要问小斑为什么不兼容 IE,连它亲爸都不要它了,兼容它干嘛!

一番下来,小斑的样式得到了最大程度的精简(因为 Antd 组件内的样式包含了组件的所有样式,会有部分冗余),样式体积骤减,由原本 900K 减至不到 200K!经过 Gzip 发生到客户端的体积,不到 100K !

想高亮吗?收下这 1M 代码!

在小斑诞生之初,便已确定,编辑器必须支持代码高亮,那自然就需要引入 highlight.js,但这句代码比 Antd 的样式代码更加夸张,好嘛!整整 1M 的 JS!看到上图中那个大大的 highlight.js 代码块了吗?臃肿不堪,经仔细研究后发现,核心的高亮代码其实很少,绝大部分被 languages 占用!

怎么办?去官网瞧瞧呗,得嘞,不看不知道,一看吓一跳,通过以下形式引入的 highlight.js 包含了整整 189 种的语言解析器!

import hljs from 'highlight.js';

什么?为啥 TreeShaking 没起作用?这和 TreeShaking 的关系可真心不大,高亮是在打包后才用到的模块!那该如何精简?其实官方已经给了答案:按需加载,仅加载需要引入的语言解析器,甚至官方已经为我们推荐好了常用的 38 种语言,一一注入即可!

import hljs from 'highlight.js/lib/core';
import javascript from 'highlight.js/lib/languages/javascript';

hljs.registerLanguage('javascript', javascript);

虽然整体的代码量由于需要注册 38 种语言增加了一点点,但换来的却是 1M 到 300K 打包体积的变化,值吗?非常的值!

image

Δ 移除非常用语言后的代码高亮后的整体打包体积。highlight.js 仅占了其中的一小块。

不变的内容,静态化!

有用过小斑编辑器应该知道,小斑支持选择代码的高亮风格以及文章的主题。一开始,小斑包含这部分内容,但后来发现,每次发版,这部分内容永远不变!高亮风格和文章主题不过是一段样式信息而已,为什么不静态化?把这部分内容独立出去!这样不仅小斑可以使用,大家也都可以使用了!

最后,阿飞又折腾出一个新项目:zebra-editor-theme 专注于文章样式的管理,希望大家能多多关注!

当然,开源意味着共享,站点内所有的代码高亮风格,以及文章主题,大家都可以获取,使用,一下是文章主题以及代码风格的样式文件获取方式,当然来目前的文章主题还很少,欢迎大家多多 PR ~

异步:并不是所有的代码,都需要及时加载!

冗余,静态代码已优化完毕,看着这依然有 2.5M 的小斑,阿飞叹了口气:哎,你个胖子啊,看来不能一口气把所有的代码都给你。异步加载模块得用上了!

模块异步加载

异步加载是什么?

一种延迟加载的技术,可以让资源在需要加载的时候,才进行加载。

如何实现?

很简单!一句话的事:

const loadMdAst = () =>
  import(
    /* webpackChunkName: "mdast", webpackPrefetch: true */ "@textlint/markdown-to-ast"
  );

如上,就动态的引入了 Markdown 语法解析器。

如何使用?

import 函数返回 Promise,该 Promise 会在资源加载结束后进入 resolve 状态,就能获得模块的默认导出了!

const mdAstParse = await loadMdAst();

如上就能在 async 函数中获取 Markdwon 的解析器。但该种方式有个弊端,由于异步函数是有传染性的!会导致一层层的函数都变成异步函数。

React 异步组件

import {lazy, Suspense} from "react";

const Panel = lazy(() =>
  import(/* webpackChunkName: "color", webpackPrefetch: true */ "./color-panel"),
);

const ColorBtn = () => {
  return (
    <Suspense fallback={<Loading size={80} />}>
      <Panel />
    </Suspense>
  )
}

一个简单例子:颜色选择框通过异步组件的形式,加载到项目中,相关内容可翻阅文档:React-Suspense

异步,真的值吗?

异步的模块或是组件可以延缓加载时间,但却会导致两个问题:

  • 代码被异步污染,任何使用异步加载模块的地方,都会被污染成异步函数;
  • 并没有真正的减少请求的体积,但却增加了请求数;

对于第一点,如果说在 async 函数出来之前,大部分小伙伴内心估计是抗拒的,原本流程明确的代码,被套用在一个 Promise 链里,想想都不舒服,不过现在 async/await 函数规范以及制定,异步的获取结果也就一个 await 的事,一点也不麻烦!所以目前,第一点可以忽略不计!

关于第二点,这么说,如果项目中的模块首屏就会被用到,虽然使用了异步的形式,但却依然要等到模块加载完毕才能展示首屏,这类异步的模块其实没有必要。

但如果说项目中有个功能,使用者始终不会用到,不加载应该是最好的选择。还有,如果该功能,不会出现在首屏,但却需要长达 1s 的加载时间,那么它以一种后台加载的形式,默默的在用户使用的时候加载好,对用户来说,也是非常棒的体验了!

因此,对于异步模块,只要满足这两点中的一点,就可以使用了:

  1. 首屏用不到的模块;
  2. 需要在一定情况下才显示或使用的模块;

异步模块,虽然不能减少小斑的代码总量,但却能让小斑早一点与你们相见。你说值吗?当然非常的值呀!

缓存:并不是所有的代码,都需要再次加载!

熟悉 Webpack 的小伙伴都应该知道,Webpack 拥有代码分类打包的能力,具体到 Webpack4 其实就是 optimization.splitChunks 这一块大家讨论的很多了,复制粘贴官网的文档也没有意义,就不多说了。在这小斑推荐大家结合官网和网上总结一起看,因为网络上文章的并不一定是最新的,记录下为什么要代码分块(知其所以然比知其然更加重要!):

  • 浏览器有自己的缓存策略,分为强缓存和协商缓存,缓存生效的前提在于内容不变;
  • 由于项目代码并非一层不变,因此为了避免缓存导致的问题,需要生成版本号;
  • 由于版本号的不同,浏览器的缓存策略失效了;
  • 但项目的公共代码(比如说 UI 库)其实是可以缓存的;
  • 如果这些公共代码分包成一个单独的 bundle 不添加版本号,就能被浏览器缓存了!
  • 但公共代码也会变呀!比如说修复漏洞,增加新功能等!
  • 那还是得生成版本号!如果根据打包出来的内容生成版本号,问题迎刃而解!

分包,说到底,是让浏览器缓存生效的一种策略,如果说所有的代码都在一个 bundle 中,为了避免缓存导致的影响,不得已需要添加版本号,但分包之后就不同了,即使版本需要更新,一些包的内容其实没有发生变化,打包出来的文件名也就不会变化,这极大的利用了缓存!

image

Δ 经异步加载,内容分包后的最终成果

首屏需要加载的内容块如下:

  • willChange: 一些可能会发生变化的项目依赖,如 antd,因为可能会使用新的组件;
  • library: 固定不会的项目依赖,如 react
  • main: 小斑的核心代码;

未经 Gzip 前,一共:554K + 344K + 338K ≈ 1MGzip 后仅仅 340K 小斑瘦身大获成功!但是,一切并没有结束!

终极利器 Service Work:0K!

Service Work 可以简单理解为一个介于客户端和服务器之间的一个代理,客户端的请求会通过 Service Work 对外发送,并获取内容,因此 Service Work 具有了控制返回内容的权利,它可以缓存返回的内容,并在第二次请求的时候直接返回数据,这点和缓存很像,但区别就在于,它是可控的,甚至可以提前获取资源,并缓存!

想像一个具体场景,你的网页发版了,用户焦急的等待着浏览器下载新版网页的资源,但一旁使用 Service Work 的网站依旧瞬间打开了老版页面,开始浏览了起来,看起来好像你隔壁的网页没更新。但第二天,你却发现隔壁的网页,不仅更新到了最新版,而且依旧瞬间打开!作为一个积极向上的开发者,我想,你应该理解到了这在用户体验上的差距!

Service Work 可以检查更新,输出原先内容的同时,下载最新内容,并在合适的时候更新缓存内容,对于用户来说,除了第一次访问,之后的每一次都是瞬间打开,完全不需要下载新内容!

这时候有些小伙伴就会问了:这么牛逼的技术,一定很难咯!不!它不仅不难,还很简单,简单到如果你使用脚手架,都不需要考虑这个问题!

所以,你要做的仅仅是打开脚手架里的 Service Work 选项 ~

最后

啰啰嗦嗦写了将近 6000 字,当然,还有一些比如 JS 的代码压缩插件 TerserPluginCSS 的压缩插件 MiniCssExtractPlugin 之类的不提也罢,都是形式化的东西,这些脚手架已经帮开发者弄好了,不需要考虑,当然了解还是需要了解的,那就先把 Webpack 文档看一遍吧 ~

虽然文章仅用了几天写成,但具体到小斑的优化过程却是是一个较长的过程,总结不易,给个 Star 呗。

本篇文章由斑马编辑器编辑并生成,我是小斑,我为自己带盐 ~

本文来自投稿,不代表程序员编程网立场,如若转载,请注明出处:http://www.cxybcw.com/202365.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code