5

项目启动页加载太慢?我们一起掌握几个优化方案!

 2 years ago
source link: https://www.fly63.com/article/detial/11733
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

TienChin 项目也是一个前后端分离项目,前后端分离项目如果做成 SPA(单页面)的形式,就必然面临一个首屏加载的问题,因为默认情况下首页文件比较大,可能超过 1 MB,进而带来首页加载很慢的问题。所以我们要通过优化,来提高首页的加载速度。

问题的解决,一般来说有这样几种思路:

  • UI 组件按需加载
  • 路由懒加载
  • 组件重复打包

这些加载方式中,UI 组件按需加载和 gzip 是两种比较常用的方案,另外两种优化方式则要结合具体的项目,看看是否具备相关条件。

所以,本文松哥就先来和大家聊一聊 UI 组件懒加载和 gzip。

1. ElementUI 按需加载

1.1 问题复现

不做任何优化,我们一般是在 main.js 中按照如下方式来引入 ElementUI 的:

import ElementUI from'element-ui';
import'element-ui/lib/theme-chalk/index.css';
vue.use(ElementUI,{size:'small'});

css 这个不用说,肯定得引入。但是按照上面这种引入方式,除了 css,其他组件全部都引入到项目中了,最新版的 ElementUI 中的组件已经接近 60 个了,但是我们项目中用的组件可能没有这么多,这些最终没用上的组件就会造成资源浪费。

在不做任何优化的情况下,我们通过如下命令对项目生成 report.html 用来帮助我们分析包内容:

vue-cli-service build --report

该命令执行的日志如下(截取了关键部分):

warning

entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  app (1.17 MiB)
      css/chunk-vendors.9948ce82.css
      js/chunk-vendors.11959501.js
      css/app.4e8a7623.css
      js/app.ce6f575c.js

  File                                    Size              Gzipped

  dist/js/chunk-vendors.11959501.js       840.77 KiB        227.94 KiB
  dist/js/app.ce6f575c.js                 99.08 KiB         30.95 KiB
  dist/js/chunk-64435448.d0a0516e.js      26.52 KiB         5.87 KiB
  dist/js/chunk-0c17a57a.d553638c.js      23.79 KiB         5.49 KiB
  dist/js/chunk-a3fdbb9c.ddc4c008.js      13.30 KiB         3.45 KiB
  dist/js/chunk-54277bc7.2882c4cd.js      10.40 KiB         2.95 KiB
  dist/js/chunk-4e552d82.c64f4d10.js      1.78 KiB          0.63 KiB
  dist/js/chunk-18458ebc.32fb57c9.js      1.54 KiB          0.62 KiB
  dist/js/chunk-2d0d03c8.3a093d55.js      0.52 KiB          0.36 KiB
  dist/js/chunk-2d237c54.0b312051.js      0.43 KiB          0.33 KiB
  dist/css/chunk-vendors.9948ce82.css     258.19 KiB        41.36 KiB
  dist/css/app.4e8a7623.css               3.46 KiB          1.09 KiB
  dist/css/chunk-0c17a57a.9fe19f94.css    0.86 KiB          0.35 KiB
  dist/css/chunk-64435448.3755e146.css    0.30 KiB          0.15 KiB

从这段日志中我们可以看到,项目入口文件的大小超出了官方建议的 244KB,而这可能会影响网页的表现。

此时我们把打包后的文件拷贝到 Spring Boot 的 resources/static 目录下,启动后端项目,来看下浏览器的加载情况:

62a976e520deb.jpg

可以看到,最大的 chunk-vendors.11959501.js 文件加载用了 369ms。

同时大家注意到,此时在前端 dist 目录下还有一个文件叫做 report.html,这是生成的打包报告,我们在浏览器打开这个页面,如下:

62a976ea2541d.jpg

在这个 html 页面中,通过可视化页面向我们展示了到底是谁把 js 文件撑大的,从图中我们可以看到,chunk-vendors.11959501.js 文件之所以比较大,是因为它里边的 element-ui.commons.js 这个文件比较大。

实际上,每个模块都可以去想办法优化,但是枪打出头鸟,因为 element-ui.commons.js 实在太大了,我们就先把它给优化了。

1.2 问题解决

优化办法其实很简单,ElementUI 官网也给出了办法,首先我们加载安装 babel-plugin-component:

npm install babel-plugin-component -D

然后修改 babel.config.js 文件,如下:

module.exports = {
    presets: [
        '@vue/app',
        ['@babel/preset-env', {
            modules: false
        }]
    ],
    plugins: [
        [
            "component",
            {
                "libraryName": "element-ui",
                "styleLibraryName": "theme-chalk"
            }
        ]
    ]
}

配置完成后,再去修改 main.js 文件,将我们需要用到的组件一个一个引入进来:

import {
    Button,
    Input,
    Table,
    TableColumn,
    Dialog,
    Card,
    Container,
    Icon,
    Select,
    Form,
    Tag,
    Tree,
    Pagination,
    Badge,
    Loading,
    Message,
    MessageBox,
    Menu,
    Tabs,
    TabPane,
    Breadcrumb,
    BreadcrumbItem,
    Dropdown,
    Steps,
    Tooltip,
    Popover,
    Collapse,
    FormItem,
    Checkbox,
    Header,
    DropdownMenu,
    DropdownItem,
    Aside,
    Main,
    MenuItem,
    Submenu,
    Option,
    Col,
    Row,
    Upload,
    Radio,
    DatePicker,
    RadioGroup,
    CollapseItem,
    Switch
} from'element-ui';
import'element-ui/lib/theme-chalk/index.css';
Vue.prototype.$ELEMENT = {size: 'small', zIndex: 3000};
Vue.use(Switch);
Vue.use(CollapseItem);
Vue.use(Radio);
Vue.use(RadioGroup);
Vue.use(DatePicker);
Vue.use(Upload);
Vue.use(Row);
Vue.use(Col);
Vue.use(Option);
Vue.use(Submenu);
Vue.use(MenuItem);
Vue.use(Header);
Vue.use(DropdownMenu);
Vue.use(DropdownItem);
Vue.use(Aside);
Vue.use(Main);
Vue.use(Checkbox);
Vue.use(FormItem);
Vue.use(Collapse);
Vue.use(Popover);
Vue.use(Menu);
Vue.use(Tabs);
Vue.use(TabPane);
Vue.use(Breadcrumb);
Vue.use(BreadcrumbItem);
Vue.use(Dropdown);
Vue.use(Steps);
Vue.use(Tooltip);
Vue.use(Tree);
Vue.use(Pagination);
Vue.use(Badge);
Vue.use(Loading);
Vue.use(Button);
Vue.use(Input);
Vue.use(Table);
Vue.use(TableColumn);
Vue.use(Dialog);
Vue.use(Card);
Vue.use(Container);
Vue.use(Icon);
Vue.use(Select);
Vue.use(Form);
Vue.use(Tag);
Vue.prototype.$alert = MessageBox.alert
Vue.prototype.$confirm = MessageBox.confirm

这里的代码倒是不难,有两个需要注意的地方:

  • MessageBox 的引入方式和其他组件不太一样,需要注意。
  • 给组件统一定制 size 和 zIndex 的方式有所变化。

配置完成后,我们再次执行 vue-cli-service build --report ,查看打包结果:

entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  app (1.03 MiB)
      css/chunk-vendors.26d2c5b9.css
      js/chunk-vendors.e2a11728.js
      css/app.4e8a7623.css
      js/app.c5dd78e5.js

  File                                    Size              Gzipped

  dist/js/chunk-vendors.e2a11728.js       683.05 KiB        177.91 KiB
  dist/js/app.c5dd78e5.js                 101.70 KiB        31.90 KiB
  dist/js/chunk-64435448.d0a0516e.js      26.52 KiB         5.87 KiB
  dist/js/chunk-0c17a57a.d553638c.js      23.79 KiB         5.49 KiB
  dist/js/chunk-33b8cd94.7bbae1a0.js      13.30 KiB         3.46 KiB
  dist/js/chunk-df7e035a.414b548f.js      10.40 KiB         2.95 KiB
  dist/js/chunk-4e552d82.c64f4d10.js      1.78 KiB          0.63 KiB
  dist/js/chunk-18458ebc.32fb57c9.js      1.54 KiB          0.62 KiB
  dist/js/chunk-2d0d03c8.3a093d55.js      0.52 KiB          0.36 KiB
  dist/js/chunk-2d237c54.0b312051.js      0.43 KiB          0.33 KiB
  dist/css/chunk-vendors.26d2c5b9.css     262.71 KiB        42.11 KiB
  dist/css/app.4e8a7623.css               3.46 KiB          1.09 KiB
  dist/css/chunk-0c17a57a.9fe19f94.css    0.86 KiB          0.35 KiB
  dist/css/chunk-64435448.3755e146.css    0.30 KiB          0.15 KiB

和前面的日志比较后发现,将 ElementUI 按需引入后,还是有效果的,只是效果不太明显。这个时候我们再来打开 report.html 页面来看下:

62a976f4390cd.jpg

可以看到,规模庞大的 element-ui.commons.js 已经不见了,取而代之的是一众小喽啰。相关文件大小也减少了 150kb 左右。

这感觉效果有限。

2. gzip

所以,在前面代码的基础上,我们来通过 gzip 继续压缩。

通过 gzip 来压缩,我们有两种思路。这两种思路和前后端分离的两种不同部署方式有关。

前端编译打包后拷贝到后端,直接部署后端项目即可

前后端分离部署,前端通过 Nginx 来部署(推荐)

2.1 服务端配置

如果使用第一种方式,前端可以不用做额外工作,还是之前编译后的文件。我们在后端 application.yml 中添加如下配置,开启 gzip 压缩:

server:
  compression:
    enabled:true

配置完成后,重启后端项目,访问项目首页,如下,可以看到文件基本上都被压缩了:

62a976fa1c312.jpg

点开一个请求,可以看到 gzip 已经生效了:

62a976fe5945b.jpg

2.2 前端配置

Nginx 中配置前端的 gzip 压缩,有两种思路:

  • Nginx 动态压缩,静态文件还是普通文件,请求来了再压缩,然后返回给前端。
  • Nginx 静态压缩,提前把文件压缩成 .gz 格式,请求来了,直接返回即可。

2.2.1 Nginx 动态压缩

动态压缩 Vue 还是使用普通的打包编译后的文件,将前端编译打包后的文件拷贝到 Nginx 的 html 目录下,然后访问 nginx:http://192.168.91.129

确保 nginx 运行成功后,接下来对 nginx 进行配置:

gzip  on;  # 开启 gzip
gzip_min_length 2k;# 超过 2kb 进行压缩
gzip_disable msie6; # ie6 不适用 gzip
gzip_types text/css application/javascript text/javascript image/jpeg image/png image/gif; # 需要处理的文件

配置完成后,重启 Nginx:

./nginx -s reload

启动成功后,再去访问前端页面,就可以看到压缩效果了。

2.2.2 Nginx 静态压缩

上面的动态压缩有一个问题,就是每次请求响应的时候都要压缩,其实都是相同的文件,总是压缩有点浪费资源。

我们可以提前将文件压缩好,就保存在服务端,需要用的时候直接返回,就会方便很多。

这需要我们首先在前端安装压缩插件:

npm install compression-webpack-plugin -D

安装成功之后,接下来在 vue.config.js 中进行配置:

const CompressionPlugin = require("compression-webpack-plugin")
module.exports = {
    devServer: {
        host: 'localhost',
        port: 8080,
        proxy: proxyObj
    },
    configureWebpack: config => {
        if (process.env.NODE_ENV === 'production') {
            return {
                plugins: [
                    new CompressionPlugin({
                        test: /\.js$|\.html$|\.css/,
                        threshold: 1024,
                        deleteOriginalAssets: false
                    })
                ]
            }
        }
    }
}
  • threshold 表示超过 1kb 的文件就进行压缩。
  • deleteOriginalAssets 表示压缩后是否删除原文件。

配置完成后,再次执行打包命令 vue-cli-service build。这次打包完成后,我们可以在 js 目录下看到 .gz 文件,如下:

62a97705293cd.jpg

接下来将文件上传到 Nginx 服务器,然后对 Nginx 重新进行编译打包。想让 Nginx 返回已经压缩好的文件,需要用到 Nginx 中的 http_gzip_static_module 模块,这个模块可以发送以 .gz 作为文件扩展名的预压缩文件,所以我们要对 Nginx 重新进行编译打包:

./configure --with-http_gzip_static_module
make
make install

然后在 Nginx 配置文件中开启 gzip_static,如下:

gzip_static  on;

注意,开启了 gzip_static 后,gzip_types 就失效了,所以也没必要配置这个属性了。

配置完成后,重启 Nginx,再去访问,查看浏览器日志,就会发现 gzip 已经生效了。

「注意:」

静态压缩返回的 gzip 压缩文件都是提前准备好的,没有 .gz 格式的文件就会自动返回原文件。这是一种和动态压缩不同的响应策略。动态压缩是根据 Nginx 中的配置,超过配置的大小就会自动进行压缩。

好了,这一波操作下来,首屏加载速度提高了 5 倍左右。

来源: 江南一点雨

链接: https://www.fly63.com/article/detial/11733


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK