Iwen's blog Iwen's blog
首页
  • 前端文章

    • JavaScript
    • Vue
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《TypeScript 从零实现 axios》
    • 小程序笔记
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • Linux
  • 学习
  • 面试
  • 心情杂货
  • 友情链接
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
复盘
关于

Iwen

不摸鱼的哥哥
首页
  • 前端文章

    • JavaScript
    • Vue
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《TypeScript 从零实现 axios》
    • 小程序笔记
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • Linux
  • 学习
  • 面试
  • 心情杂货
  • 友情链接
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
复盘
关于
  • JavaScript文章

  • Vue文章

    • Vue项目开发前的一些准备工作
    • 记一次有意思的性能优化
      • 问题复现
      • 分析问题
      • 解决问题
      • 重构时遇到的难题
      • 小结
    • flex布局页面自适应滚动条问题
    • 前后端分离开发请求接口跨域如何携带cookie的问题
    • Vue CLi3 修改webpack配置
    • Vue中的scoped和scoped穿透
    • Vue项目使用mock数据的几种方式
  • 学习笔记

  • Vue-iView疑难杂症

  • 微前端

  • 原创经验

  • 前端
  • Vue文章
Mr.w
2021-06-25

记一次有意思的性能优化

# 记一次有意思的性能优化

这几天针对资产系统的会签流程进行了部分重构,但在重构过程中,总是遇到页面卡顿、点击页面无响应或者等好半天页面才调跳转的问题。这样子的交互和页面渲染,让人很难受,更别说调试代码了,遂进行了页面渲染性能问题进行了排查和分析。

# 问题复现

具体问题如下图所示:

可以看到,当点击进入 新增页面后,选择资产 的组件就已经在开始请求大量图片了,而且是高清图片,请求资源的量级也是没有上限的,这就导致了页面一直下载图片资源,造成了浏览器抢占资源和线程的问题出现,导致点击菜单和页面无响应问题。

page_bug

# 分析问题

  • 数据来源分析

    • 首选查看了请求资产列表的接口,见如下截图: 20210626204139

    可以看到 type-page 是个分页接口,但返回的数据量明显不是分页的数据,而更像是一个list集合,把所有的数据一起返回了,这样做会造成服务器額外的性能浪费,另一个页面渲染数据也会变慢,如果不使用缩略图的话。

    20210626204622

  • 业务组件加载图片的方式,见如下截图:

    20210626231249

    采用了直接使用加载原图的方式,而原图则是高清的图片,一般都有4M~7M左右,那么加载图片直接是http方式请求,就会造成资源抢占,线程堵塞的问题,导致页面卡顿无响应。

  • 代码层面优化重构

public async onSave() {
        let message = this.service.getPureText();
        if (!this.noCheck && !this.selection.length) {
            return this.$message.warning("请选择处理人");
        }
        const users = this.selection.$clone();
d
        let params: any = {
            recordId: this.recordId,
            message: message,
            taskId: this.taskId,
            nodeInfo: {
                targetNodeId: "",
                participants: [
                    {
                        type: "",
                        value: ""
                    }
                ]
            }
        };

        let voteusers: Array<any> = users.map(user => {
            return { type: user.type, value: user.id };
        });
        params = { ...params, nodeInfo: { targetNodeId: users[0].nodeId, participants: voteusers }};

        // if(users) {
        //     params = {
        //         ...params,
        //         nodeInfo: {
        //             targetNodeId: users[0].nodeId,
        //             participants: [
        //                 {
        //                     type: users[0].type,
        //                     value: users[0].id
        //                 }
        //             ]
        //         }
        //     };
        // }

        this.isLoading = success;

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

可以看到,此块代码

let voteusers: Array<any> = users.map(user => {
    return { type: user.type, value: user.id };
});
params = { ...params, nodeInfo: { targetNodeId: users[0].nodeId, participants: voteusers }};
1
2
3
4

是对注释区域的重构。现在我们应提倡 FP(函数式编程)思想的运用,一是可以大量简化代码,二是代码实现逻辑清晰明了,三是还可以装下13。^_^

# 解决问题

  • 显然,从上面分析的可知

    • 优化资产分页请求接口 20210627120340

    • 图片加载的方式,尽量采用缩略图的方式加载,节省资源,避免线程堵塞问题 20210627121534

    • 简化代码,使之业务逻辑清晰明了

  • 附上最终优化后的效果

page_perf

# 重构时遇到的难题

重构缩略图组件时,需要监听图片弹窗关闭的事件,但在网上找了很多资料,都没有看到 v-viewer 的弹窗关闭事件的处理,后来再仔细查阅 v-viewer 的API后,发现其是继承了viewer.js的相关属性和方法,而且是可以通过options传参解决的。最后附上自己的解决方法:

        <div :class="['thumbnail', fixClassName]">
            <div class="thumbnail-img">
                <img
                    :src="thumbnail"
                    :key="thumbnail"
                    loading="lazy"
                    @click.stop="showViewer"
                    @error="onHandleError"
                />
            </div>
            <!--v-viewer注册为全局组件-->
            <viewer 
                v-if="data.show" 
                :images="images" 
                @inited="initViewer" 
                :options="options" 
                style="display:none">
                <img 
                    class="image" 
                    v-for="(image, index) in images"
                    :src="image" 
                    :key="image" 
                    @error="onHandleError"
                />
                <template v-if="!images.length">
                    <div class="empty-message">暂无图片</div>
                </template>
            </viewer>
        </div>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    @charset "UTF-8";
    .thumbnail {
        @include size;
        position: relative;
    
        &-img {
            @include size;
            cursor: pointer;
        
            > img {
                height: 100%;
            }
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { Vue, Component, Prop } from "vue-property-decorator";
    // import { randomPics } from "@/common";
    import "./index.scss";
    
    @Component({
        template: require("./index.html")
    })
    export default class ThumbNailViewer extends Vue {
    
        public viewer: any = {};
    
        @Prop({ default: () => new Object() })
        public data!: any;
    
        @Prop({ type: String, default: () => "" })
        public thumbnail!: string;
    
        @Prop({ type: String, default: () => "" })
        public fixClassName!: string;
    
        @Prop({ type: [Array, String], default: () => [] })
        public images!: Array<any> | any;
    
        @Prop({ default: () => require("@/assets/images/common/error/error-image.png") })
        public errorImg!: any;
    
        public options: any = {
            hide: () => this.onViewerHide()
        };
    
        public initViewer (viewer: any) {
            this.viewer = viewer;
            // console.dir(this.viewer);
        }
    
        public showViewer() {
            this.$set(this.data, "show", true);
            // this.images = randomPics(5).map((g: any) => g.source);
            if(!this.viewer) return;
            this.$nextTick(() => {
                this.viewer.show();
            });
        }
    
        public onViewerHide() {
            this.$set(this.data, "show", false);
        }
    
        public onHandleError(evt: any) {
            let img = evt.srcElement;
            img.src = this.errorImg;
            img.onerror = null; // 防止闪图
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // Make sure to add code blocks to your code group

    # 小结

    性能优化前路漫漫,但我始终相信,一个有心人,总会看到不一样的细节。

    只要坚持付出,总会有不同色彩的收获。

    Vue项目开发前的一些准备工作
    flex布局页面自适应滚动条问题

    ← Vue项目开发前的一些准备工作 flex布局页面自适应滚动条问题→

    最近更新
    01
    flex布局页面自适应滚动条问题
    12-28
    02
    前后端分离开发请求接口跨域如何携带cookie的问题
    12-28
    03
    怎么实现div长宽比固定width-height4比3
    12-28
    更多文章>
    Theme by Vdoing | Copyright © 2017-2022 Iwen | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式