精华内容
下载资源
问答
  • 我们在做h5或者页面的时候,经常会遇到首屏优化首屏优化的方式很多,比如懒加载等,最近从师姐那里学来一个,做一个记录。我们的需求是这样,因为数据比较少,没有做分页,需要首屏一次性加在完成,但是用户再打开...

    我们在做h5或者页面的时候,经常会遇到首屏优化,首屏优化的方式很多,比如懒加载等,最近从师姐那里学来一个,做一个记录。

    我们的需求是这样,因为数据比较少,没有做分页,需要首屏一次性加在完成,但是用户再打开页面的时候仅仅能看到的只是第一屏的数据,比如用户只看到了3-5条数据,而我们一次性把所有数据渲染出来还是会影响加载速度的,所以我们就产生了一个方案:我们进入页面的时候,只做用户看到的行为,其他的行为延迟加载,具体就是,我们可以先加载用户第一眼看到的3-5条数据,然后延迟把剩下的加载一下。

    代码如下:

    * {

    margin: 0;

    padding: 0;

    }

    p {

    line-height: 100px;

    border-bottom: solid 1px #ccc;

    }

    const data = [

    {

    name: "kshdhd"

    },

    {

    name: "rdfdgdg"

    },

    {

    name: "4565646456"

    },

    {

    name: "45646563ggg"

    },

    {

    name: "dgg435435"

    },

    {

    name: "666666666666666666gdgfg465"

    },

    {

    name: "4546464dgfdg"

    },

    {

    name: "4645665gdgdgdfdgdgdg"

    },

    {

    name: "etet564646445646"

    },

    {

    name: "kshdhd"

    },

    {

    name: "rdfdgdg"

    },

    {

    name: "4565646456"

    },

    {

    name: "45646563ggg"

    },

    {

    name: "dgg435435"

    },

    {

    name: "gdgfg465"

    },

    {

    name: "4546464dgfdg"

    },

    {

    name: "4645665gdgdgdfdgdgdg"

    },

    {

    name: "etet564646445646"

    }

    ]

    let res = data.slice(0, 6);

    document.getElementById('app').innerHTML = res.map(val => {

    return `

    ${val.name}

    `;

    }).join('');

    setTimeout(() => {

    document.getElementById('app').innerHTML = [...res, ...data.slice(6, data.length)].map(val => {

    return `

    ${val.name}

    `;

    }).join('');

    }, 1000);

    欢迎关注小程序,感谢您的支持!

    643d0cbe423265bc928203babbc7e1a7.png

    展开全文
  • 文章目录实战篇traceview 工具使用操作步骤操作步骤2systrace 工具使用操作步骤AOP 工具 hugo 的使用使用方法效果BlockCanary 使用AOP 工具使用 AspectJ操作步骤优化方案:异步线程优化:针对主线程需要异步线程的结果...

    • 我会先把实战篇写在前面, 把理论篇写在后面. 我太清楚实用党是怎么想的了,只想抓紧时间吧APP的性能搞好. 关于原理方面都往后放. 哈哈 . 因为我以前也是这样的. 为了方便快速使用 直接上干货. 先按照教程做,后面有功夫再看理论.

    实战篇

    卡顿工具 BlockCanary

    http://blog.zhaiyifan.cn/2016/01/16/BlockCanaryTransparentPerformanceMonitor/

    traceview 工具使用

    操作步骤

    利用Debug类的startMethodTracing和stopMethodTracing函数分别来打开和关闭日志记录功能。记录两个方法中间所有函数执行的时间统计.
    方法执行完后,会在手机sdcard中生成一个dmtrace.trace文件

    1. 第一步: 在MyApplication 的onCreate 最前面输入如下代码
     if (BuildConfig.DEBUG) {
                Debug.startMethodTracing("APP");
            }
    
    1. 第二步: 在MyApplication 的onCreate 结束位置输入如下代码
     if (BuildConfig.DEBUG) {
                Debug.stopMethodTracing();
        }
    
    1. 第三步: 找到生成的文件

    在/sdcard/Android/data/packageName/files 目录下面 有一个 APP.trace 的文件

    从androidstudio 上面打开来看 就有下图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Kv6rsBg-1612277341019)(https://raw.githubusercontent.com/liudao01/picture/master/img/QQ%E5%9B%BE%E7%89%8720210130123716.png)]

    操作步骤2

    使用android studio profiler 双击cpu -> record 按钮 即可有文件

    systrace 工具使用

    操作步骤

    1. 第一步: 在MyApplication 的onCreate 最前面输入如下代码
            if (BuildConfig.DEBUG) {
                TraceCompat.beginAsyncSection("App_onCreate",0);
    //            Debug.startMethodTracing("APP");
            }
    
    1. 第二步: 在MyApplication 的onCreate 结束位置输入如下代码
            if (BuildConfig.DEBUG) {
                TraceCompat.endSection();
    //            Debug.stopMethodTracing();
            }
    
    1. 第三步: 进入sdk目录 使用脚本命令

    我的在这里

    E:\dev\sdk\platform-tools\systrace>
    
    

    执行下面的命令
    package-name 是app的名字

    ./systrace.py -b -t 5 sched gfx view wm am app webview -a <package-name>
    
    ./systrace.py -b 32768 -t 5  -a <package-name> 
    
    systrace.py -b 32768 -t 5  -a com.sinochem.www.car.owne -o performance.html sched gfx view wm am app
    

    遇到问题: ImportError: No module named win32con

    下载地址 https://pypi.org/project/pypiwin32/219/#files
    PyPI(Python Package Index)是python官方的第三方库的仓库,找python2.7对应的版本。

    打开网页即可

    AOP 工具 hugo 的使用

    为什么用这个: 用于埋点计算函数运行的时间. 非侵入性.

    使用方法

    1. 项目根目录build.gradle添加hugo插件依赖
    classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1'
    
    1. 主工程或者library的录build.gradle中声明hugo插件。
    apply plugin: 'com.jakewharton.hugo'
    

    注意在每个lib里面都要加上 你想在那个lib里面打印就在那个lib里面加上这句
    3. 在类或方法上声明@DebugLog注解。

    
        @Override @DebugLog
        public void onCreate() {
            super.onCreate();
    }
    

    效果

    2021-01-31 15:03:01.871 26943-26943/com.sinochem.www.car.owner V/MyApplication: ⇢ onCreate()
    2021-01-31 15:03:02.161 26943-26943/com.sinochem.www.car.owner V/MyApplication: ⇠ onCreate [289ms]
    
    

    可以看到运行时间289ms

    参考资料:

    https://github.com/JakeWharton/hugo

    https://juejin.cn/post/6844903945186476040

    https://xiaozhuanlan.com/topic/1428095376

    BlockCanary 使用

    AOP 工具使用 AspectJ

    https://ciruy.blog.csdn.net/article/details/102621417?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

    https://blog.csdn.net/eclipsexys/article/details/54425414

    为什么用这个: 用于埋点计算函数运行的时间. 非侵入性.

    操作步骤

    Android中使用AspectJ

    1. 第一步:
      在项目的根目录的build.gradle文件中添加依赖,修改后文件如下
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:1.0.8'
    
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
    
    1. 然后在项目或者库的build.gradle文件中添加AspectJ的依赖
    compile 'org.aspectj:aspectjrt:1.8.9'
    

    同时在该文件中加入AspectJX模块

    apply plugin: 'android-aspectjx'
    
    1. aspectjx默认会遍历项目编译后所有的.class文件和依赖的第三方库去查找符合织入条件的切点,为了提升编译效率,可以加入过滤条件指定遍历某些库或者不遍历某些库。

    includeJarFilter和excludeJarFilter可以支持groupId过滤,artifactId过滤,或者依赖路径匹配过滤

    aspectjx {
        //织入遍历符合条件的库
        includeJarFilter 'universal-image-loader', 'AspectJX-Demo/library'
        //排除包含‘universal-image-loader’的库
        excludeJarFilter 'universal-image-loader'
    }
    
    1. 创建性能收集类PreformanceAop
      代码如下,直接拷贝
    package android.androidlib.utils;
    
    import android.util.Log;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    
    /**
     * @author liuml
     * @explain
     * @time 2021/1/30 20:47
     */
    @Aspect
    public class PreformanceAop {
    
        /**
         * 针对调用的接口(日志的形式)观察执行时间
         */
        private static final String MyApplication = "call(** com.application.MyApplication.*(..))";
        private static final String AcgGlobalInitImpl = "call(** com.init.AcgGlobalInitImpl.*(..))";
        private static final String SplashActivity = "call(** com.biz.splash.SplashActivity.*(..))";
    
        /**
         * 方法的执行时长
         */
        @Around(MyApplication + "||" + AcgGlobalInitImpl + "||" + SplashActivity)
        public void getComicsApplicationTime(ProceedingJoinPoint joinPoint) {
            getTime(joinPoint);
        }
        /**
         * 获取一个类中所有方法的执行时长
         * call : 代表的是在方法调用的地方添加代码
         * 第一个 * : 代表的方法的修饰符
         * 第二个 * : 代表的是方法的返回值类型
         * 第三个 * : 类名
         * 第四个 * : 代表的是方法名
         * (..)    : 参数列表
         * 该方法拦截的是: com.example.qydemo1包下的所有类的方法
         */
    
        private void getTime(ProceedingJoinPoint joinPoint) {
            Signature signature = joinPoint.getSignature();
            String name = signature.toShortString();
            long time = System.currentTimeMillis();
            try {
                joinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            LogUtils.d(name + " 花费 " + (System.currentTimeMillis() - time)+" 毫秒");
    //        LogUtils.d(name + " cost " + (System.currentTimeMillis() - time));
        }
    
    }
    
    
    
    • 注意 MyApplication = “call(** com.application.MyApplication.*(…))”; 是你想拦截的类,每个人的都不一样我这里只是demo样例

    AspectJ 语法

    https://juejin.cn/post/6844903941054922760

    https://zhuanlan.zhihu.com/p/72575832

    https://www.jianshu.com/p/691acc98c0b8

    优化方案:

    异步线程优化:

    在MyApplication 初始化一些第三方库的时候使用线程池异步加载

    原来的时间:

    2021-01-31 15:03:01.871 26943-26943/com.sinochem.www.car.owner V/MyApplication: ⇢ onCreate()
    2021-01-31 15:03:02.161 26943-26943/com.sinochem.www.car.owner V/MyApplication: ⇠ onCreate [289ms]
    

    效果:

    2021-01-31 16:46:45.652 26568-26568/com.sinochem.www.car.owner V/MyApplication: ⇢ onCreate()
    2021-01-31 16:46:45.882 26568-26568/com.sinochem.www.car.owner V/MyApplication: ⇠ onCreate [229ms]
    

    优化了60多 毫秒.

    针对主线程需要异步线程的结果所采用的方案 CountDownLauch:

    不仅可以用在这里. 也可以用在其他地方.

    比如在第一个页面就需要异步的结果,就可以使用 CountDownLauch

    countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。

    是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

    使用方法:

    1. 创建
     private CountDownLatch mCounDownLatch = new CountDownLatch(1);
    

    这里的1 是指有几个地方用到了,也就是说异步任务有几个需要主线程确定执行完毕的.

    1. 使用
       ThreadManager.getDownloadPool().execute(new Runnable() {
                @Override
                public void run() {
                    //屏幕适配
                    configUnits();
                    mCounDownLatch.countDown();
                }
            });
    
    
    //在代码的最后 也就是需要确定的地方加上这个
       try {
                mCounDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    

    这样就形成了 必须等屏幕适配结束 才继续.

    使用启动器

    为什么采用启动器. 比如多个异步任务,第二个任务需要第一个任务的结果.这样就不好处理了,任务之间有以来关系不好处理. 另一方面不好统计, 代码也不优雅且维护成本高.

    启动器核心思想: 充分利用cpu多核,自动梳理任务顺序

    • 代码task化,启动逻辑抽象为Task(比如初始化每一个初始化的操作进行抽象,以及分类)
    • 根据素有任务以来关系排序生成一个有向无环图.(对所有任务进行排序)
    • 多现场按照排序后的优先级依次执行

    这个有点像gradle 的task了 思想应该是共通的

    启动流程
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kNtifQG7-1612277341022)(https://raw.githubusercontent.com/liudao01/picture/master/img/20210131172532.png)]

    我的APP内应用效果

    理论篇

    启动时间测量方式

    1 ADB命令

    adb shell am start -W packegname/首屏activity
    
    • thisTime : 最后一个Activity启动耗时
    • TotalTime : 所有Activity 启动耗时
    • WaitTime : AMS 启动Activity 的总耗时.

    特征: 线下方便. 非严谨, 无法带到线上.

    启动时间测量方式

    2 手动打点 使用LaunchTimer工具类记录时间

    • 抽取一个类LaunchTimer 用作工具打点的类
    • 代码如下: 拷贝拿走
    public class LaunchTimer {
    
        private static long sTime;
    
        public static void startRecord() {
            sTime = System.currentTimeMillis();
        }
    
        public static void endRecord() {
            endRecord("");
        }
    
        public static void endRecord(String msg) {
            long cost = System.currentTimeMillis() - sTime;
            LogUtils.d(msg + " cost " + cost + "ms");
        }
    
    }
    
    
    • 首页若有列表,在列表adapter里面打点
    boolean mHasRecorde;
    
    
      if (helper.getAdapterPosition() == 0 && !mHasRecorde) {
                mHasRecorde = true;
                helper.getView(R.id.item_container).getViewTreeObserver()
                        .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                            @Override
                            public boolean onPreDraw() {
                                helper.getView(R.id.item_container).getViewTreeObserver().removeOnPreDrawListener(this);
                                LaunchTimer.endRecord("feed show");
                                return true;
                            }
                        });
            }
    
    • 在MainActivity里面的 onWindowFocesChanged 里面打点

    • 代码如下

     @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            super.onWindowFocusChanged(hasFocus);
            LaunchTimer.endRecord("onWindowFocusChanged");
        }
    
    
    • 打印效果如下:
     feed show cost 1611909626616ms
     onWindowFocusChanged cost 1611909627011ms
    
    • 手动打点误区: onWindowFocusChanged 只是首帧时间 并不是用户所看到的时间
    • 正解: 真实数据展示, (首页若有列表)feed第一条展示的时候,在adapter里面加上监听/ 页面展示的时候

    所以我们认为应当将feed第一条展示的时候,当做启动开始时间,在onWindowFocusChanged 是当做启动时间是偏早的.

    traceview 工具使用 使用说明

    startMethodTracing 有几个重载的方法.

         * @param tracePath Path to the trace log file to create. If {@code null},
         *            this will default to "dmtrace.trace". If the file already
         *            exists, it will be truncated. If the path given does not end
         *            in ".trace", it will be appended for you.
         * @param bufferSize The maximum amount of trace data we gather. If not
         *            given, it defaults to 8MB.
         */
        public static void startMethodTracing(String tracePath, int bufferSize) {
            startMethodTracing(tracePath, bufferSize, 0);
        }
    
    

    下面是解析

      /**
         * tracePath 是路径  bufferSize 文件最大的大小 默认8M  0是个flag 只有一个选项
         */
        startMethodTracing(tracePath, bufferSize, 0);
    

    下表是Traceview界面每个字段表示的含义

    列名描述
    Name该线程运行过程中所调用的函数名
    Incl Cpu Time某函数占用的CPU时间,包含内部调用其它函数的CPU时间
    Excl Cpu Time某函数占用的CPU时间,但不含内部调用其它函数所占用的CPU时间
    Incl Real Time某函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间
    Excl Real Time某函数运行的真实时间(以毫秒为单位),不含调用其它函数所占用的真实时间
    Call+Recur Calls/Total某函数被调用次数以及递归调用占总调用次数的百分比
    Cpu Time/Call某函数调用CPU时间与调用次数的比。相当于该函数平均执行时间
    Real Time/Call同CPU Time/Call类似,只不过统计单位换成了真实时间
    • 图表如下

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J3qxR0Li-1612277341026)(https://raw.githubusercontent.com/liudao01/picture/master/img/QQ%E5%9B%BE%E7%89%8720210130123716.png)]

    图表说明:

    Thread 说明: 看图可知有12个线程.

    左边的图表作用很大:

    • 系统API 是橙色
    • 应用自身的调用是绿色
    • 第三方的 sdk是蓝色

    flam chart

    重要性不高

    火焰图 只用来收集相同函数调用的次数

    topDown

    告诉我们函数的调用列表, 右键点击 jump to source 可以跳转到源码

    buttomUp

    重要性不高

    用于显示一个函数的调用列表, 和topDown 是相反的

    使用traceview注意事项

    • 运行时开销很大 程序一定会变慢. 可能会带骗优化方向
    • 新版本是用cpu profiler

    知道哪个函数耗时,就可以逐个优化耗时操作了

    systrace 工具使用 使用说明

    • 结合Android内核的数据,生成html报告

    • API18以上使用,推荐TraeCompat

    • systrace 实际上是个python脚本.

    • 脚本解析

    systrace.py -b 32768 -t 5  -a com.sinochem.www.car.owne -o performance.html sched gfx view wm am app
    

    -b 是生成最大文件的大小 , -t 是监听事件 , -a 后面跟的是包名 , -o 后面是生成的文件名.

    展开全文
  • vue单页面的首屏优化

    2021-03-09 16:41:42
    一:缩小webpack打包生成...这种优化,就是将每个组件的js代码独立出来,在使用到这个组件时,才向服务器请求文件,并且请求过一次后就会缓存下来,再次使用到这个组件时,就会使用缓存,不再发送请求。 import Vue fro

    一:缩小webpack打包生成体积

    1.尽量按需引入,不要在main.ts里全局引入
    2.webpack-bundle-analyzer可以知道生成的包中哪个依赖占据着空间,哪个库占用的空间最多

    二:使用cdn(内容分发网络)

    使用cdn把一些必要的库在index.html里面引入进去,这样也能加快加载速度。

    三:动态路由分块加载

    这种优化,就是将每个组件的js代码独立出来,在使用到这个组件时,才向服务器请求文件,并且请求过一次后就会缓存下来,再次使用到这个组件时,就会使用缓存,不再发送请求。

    import Vue from 'vue'
    import Router from 'vue-router'
    
    Vue.use(Router);
    
    export default new Router({
      //mode: 'history',
      //linkActiveClass: 'router-link-active',
      
      routes: [
        {
          path: 'map',
          name: 'map'
          
          // 把component打包到一个文件里面,初次就读取全部
          // component: () => import('./views/map/Main.vue')
          
          // 异步模块获取,每次访问这个路由只会调用这个文件,按需加载
          // 这里只需要把原来从外部引入的组件换成以下的语句就可以了
          component: resolve => require(['./views/map/Main.vue'], resolve)
        },
      ]
      
    })
    

    参考:
    1.Vue单页面应用首屏加载时间优化

    展开全文
  • 那么针对SSR,CSR,预渲染,同构等首屏优化技术,你要如何进行选择呢,接下来该如何解决这个问题呢? 先来看看服务器渲染 服务器渲染指在服务器中生成整个页面的HTML,以此响应请求的技术。避免了在客户端上进行...

    作为开发者,你可能经常会面对这样一件事情,需要你做出影响整个应用架构的决策,而web开发者的核心决策之一就是应用逻辑渲染工作的实现,判断渲染工作应该处于架构中的什么位置,是客户端还是服务器呢?由于现在有很多不同构建网站的方法,因此这些决策变得更加困难,为了更好地理解在做出决定时所选择的架构,我们需要对每种方法有充分的理解,并且在谈到它们时能够有一致的术语,关于对术语的理解,我们可以先来看看与渲染有关的词:

    • SSR: 指的是服务器渲染(Server-Side-Rendering),也就是在服务器上将客户端或者通用(universal)应用程序渲染成HTML。
    • CSR: 说的是客户端渲染(Client-Side-Rendering)也就是在浏览器中渲染APP,通常使用DOM。
    • Prerender:说的是预渲染(Prerendering),也就是说在构建时运行客户端应用程序,这样将它的初始状态捕获为静态HTML。
    • Isomorphism:说的是同构(Isomorphism),是指服务器端渲染(SSR)时或者Native渲染时复用浏览器端的javascript组件。

    性能相关的术语:

    • TTFB:首字节时间(Time To Firt Bype)也就是从点击链接到接收第一个字节内容之间的时间。
    • FP: 首次绘制(First paint)也就是第一次有像素对用户可见的时间。
    • FCP:首次内容绘制(First Contentful Paint)也就是请求内容变得可见的时间。
    • TTI: 可交互时间(Time To Interactive)主要指的是页面变为可交互的时间(事件绑定等)。

    那么针对SSR,CSR,预渲染,同构等首屏优化技术,你要如何进行选择呢,接下来该如何解决这个问题呢?

    先来看看服务器渲染

    服务器渲染指在服务器中生成整个页面的HTML,以此响应请求的技术。避免了在客户端上进行数额外据获取的往返(round-trips)和模板处理。因为这些工作在浏览器获得响应之前已由服务处理了。

    服务渲染通常会得到快速的首次绘制和首次内容绘制,在服务器上运行页面逻辑和渲染。可以避免向客户端发送大量Javascript,也有助于实现快速的可交互时间TTI,这方法之所以运行的通,是因为服务器渲染的本质只是向用户浏览器发送文本和链接,可以看出这种方法是适用于广泛的设备和网络的,并且能够触发一些有趣的浏览器优化。比如流文档解析。

    使用服务器渲染,用户不再需要在客户端上等待CPU相关的Javascript处理后,然后才能访问站点,即使第三方JS无法避免,使用服务器渲染来减少自己的JS成本,也能提供更多的性能预算。但是这种方法有一个主要的缺点,那就是在服务器上生成页面有一定的耗时,可能会导致较慢的首字节时间TTFB,其实服务器渲染是否满足应用程序,很大程度上取决于构建目标的体验类型。关于服务器渲染和客户端渲染的正确应用,存在长期的争论,但重要的是我们可以选择对某些页面使用服务器渲染,而对于其余页面不使用。

    一些网站已经成功采用混合渲染技术,Netflix服务器渲染相对于静态的落地页面,同时为交互繁重的页面预拉去JS,为这些重客户端页面提供更快的加载能力,许多现代框架,库,架构使得在客户端和服务器上渲染相同的应用程序成为可能,这些技术可以用于服务器渲染,但是要注意,在服务器和客户端进行渲染的架构都是各框架自家的解决方案具有不同的性能特点和权衡

    • React用户可以使用renderToString(), 或者在它上上面构建的解决方案,比如Next.js用于服务器渲染。
    • Vue用户可以查看Vue的服务器渲染指南 或者 Nuxt。
    • Angular有Universal。

     由于大部分流行的解决方案采用某种Hydration的形态,因此在选择工具之前要注意使用的方法。

     

     

     

    静态渲染(StaticRendering)

    静态渲染在构建时进行,并提供快速的FP,FCP和TTI。

    也就是假设客户端JS的体积得当,不过与服务器渲染不同,它还致力于实现始终如一的快速首字节时间,因为页面的HTMl不必动态生成,通常情况下,静态渲染意味着提前为每个URL生成单独的HTML文件,通过预生成HTML响应,可以将静态渲染部分部署到多个CDN来利用边缘缓存,也就是页面静态化,页面静态化方案选择很多,像Gatsby这样的工具,目的就是在于让开发人员感觉它们的应用程序是动态渲染的,而不是构建过程中生成的,JekylMetalsmith(静态)提供更多的模板启动的方法,更加符合它们的静态特质。

    静态渲染的缺点:必须为每个可能的URL生成单独的HTML文件。如果无法提前预测这些URL的内容,或者对于具有大量不同页面的网站,这可能具有挑战性,甚至是不可行的。

    对于react用户而言,可能比较成熟的Gatsby,Next.js静态导出 或 Navi,它们都可以方便使用组件,但是了解静态渲染和预渲染之间的区别也非常重要。

    静态渲染页面是无需执行太多客户端JS就可以交互的,而预渲染也改进了单页面的FP或FCP。由于单页面应用,因此必须等待客户端启动过程,通过这样来使页面真正具有可交互性,不过如果你不确定是选择静态渲染还是预渲染方案,我建议你可以尝试下这个测试:

    禁用Javascript并加载创建的网页,对于静态页面渲染的页面而言,大多数功能在没启用Javascript下仍然可以正常运作,而对于预渲染页面来说,一些基本的功能,比如链接能正常展现,但其余部分无法正常展现。

    还有一个有效的测试就是使用ChromeDevTools减慢网络速度,并观察在页面变为可交互之前,已经下载了多少Javascript,预渲染通常需要更多的JavaScript来实现交互,并且这些JS往往比静态渲染使用的渐进增强方法更加复杂。

    不过服务器渲染并不是银弹,因为它的动态特性也带来了显著的计算成本,许多服务器渲染解决方案会有耗时,导致延迟的TTFB或者成倍的数据传输,在React中renderToString可能很慢,因为它是同步和单线程的。

     

    客户端渲染(Client Side Rendering)

    客户端渲染意味着使用Javascript直接在浏览器中渲染页面,所有的逻辑、数据获取、模板、路由都在客户端处理,而不是在服务器上。

    客户端渲染时很难在移动端做到很快的,如果做好压缩工作,严格控制Javascript的预算,并在尽可能少的RTT中提供内容,它可以接近纯服务器渲染的性能,使用HTTP2 ServerPush或者<link rel=reload>可以更快的提供关键脚本和数据,这将使解析器更快地完成工作,像PRPL这样的模式值得评估,通过这样来确保初始和后续导航时的即时感。

    那么客户端渲染的主要缺点是什么呢?随着应用程序的发展,所需的Javascript数量会增加,随着添加新的Javascript库,polyfill和第三方代码,更是一发不可收拾,这些代码会竞争处理能力,并且通常必须在渲染页面内容之前完成处理,构建依赖大型Javascript的CSR应使用时,应该考虑积极的代码分割,并确保延迟加载Javascript,对于很少或没有交互性的页面来说,服务器渲染可以作为更具有扩展性的解决方案。

     

    通用渲染

    通过Rehydration将服务器渲染和CSR相结合的这种方法,它通常被称为通用性渲染。或者简称SSR

    它试图通过两者兼顾来来平滑客户端渲染和服务器渲染之前的权衡,页面请求交由服务器处理,将应用程序渲染为HTML,然后把用于渲染的Javascript和数据,嵌入到生成的文档中,只要处理得当,这就像服务器渲染一样实现了快捷的FCP,然后通过称为(Re)hydration的技术,在客户端上再次拾取来渲染,这是一种新颖的解决方案,但也具有一些明显的性能缺陷,那么Rehydration后的SSR主要的缺点是什么呢?

    主要就在于它会对可交互时间产生显著的负面影响,即使它改善了首次绘制,SSR页面通常看起来具有欺骗性的加载完成和可交互性,但在执行客户端js并绑定事件处理之前,页面实际上无法响应输入,这在移动设备上可能持续几秒甚至几分钟,也许你自己也经历过这种情况,在页面看起来已经加载后的一段时间内,点击或触摸什么都没反应,这很快变得令人沮丧,同时也伴随着一些疑问,为什么没有反应,为什么我不能滚动,由于js特性,Rehydration问题往往比延迟交互更糟糕,一个Rehydration问题可能存在应用的双重成本问题

    为了使客户端Javascript不需要再次重新请求服务器,就能准确地获取到服务器返回的用于呈现其HTML时的所有数据,当前的SSR解决方案是通常将UI的数据响应序列化,以Script标签形式存放在HTML中。

    结果是生成的HTML文档包含大量重复片段,正像你所看到的服务器除了返回应用程序UI来响应页面请求,还返回了用于组成该UI的源数据,以及生成相同UI的实现代码,立即在客户端上显示,只有在bundle.js完成加载和执行后页面才会变得可交互,从使用Rehydration SSR站点收集的性能数据显示,这种用法应该极力避开使用,归根结底,原因在于用户体验上,这很容易让用户处于不明所以的状态。

    不够RehydrationSSR也不是没有希望,在短期内,只将SSR用于高度可缓存的内容,可以减少TTFB延迟,从而达到预渲染类似的效果。

     

    三方同构渲染 TrisomorphicRendering

    如果可以使用serviceworker,Trisomorphic渲染也是很有意思的,这个技术是指利用流式服务器渲染初始页面。

    等ServiceWorker加载后,接管HTML的渲染工作,是可以利用缓存的组件和模板保持最新,并启用SPA式的导航来保证在同一会话中渲染新视图,当可以在服务器、客户端页面以及ServiceWorker之间共享相同模板和路由时这个方法最有效。

     

    那么在选择渲染策略时,都需要考虑哪些呢,其实不少团队会考虑对SEO的影响,为了让爬虫能够轻松获得完整页面,我认为服务器渲染时不二的选择,虽然爬虫可能会理解Javascript,但是在渲染方式上的局限性上也需要注意,如果你的应用非常注重Javascript,最近的动态渲染解决方案也是一个值得考虑的选择。如果有疑问?

    Mobile-FriendlyTest工具,对于测试你选择的方法是否符合预期,是非常有用的,它展示了Google爬虫渲染页面的预览,执行Javascript的后序列化的HTML内容,以及渲染过程中发生的错误。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 移动端首屏优化

    2021-06-12 13:20:12
    一般页面在 dom 渲染后能显示雏形,在这之前用户看到的都是白屏,等到下载渲染图片后整个页面才完整显示,首屏秒开优化就是要减少这个过程的耗时。 ## 前端优化 上述打开一个页面的过程有很多优化点,包括前端和...
  • angular 首屏优化

    2020-12-21 10:18:19
    前一段时间把公司的一个angular项目做了一次大的优化,记录一下过程。起因:起因是用户反映网站加载时间过长,从loading画面显示到页面可响应要13s,对于一般的页面恐怕没有用户愿意等待这么久。因为这是一个在线...
  • 【Vue】首屏优化

    2021-12-09 19:30:03
    首屏加载优化 gzip 首先安装插件compression-webpack-plugin,安装5.0.1版本,高版本会出错 npm install --save-dev compression-webpack-plugin@5.0.1 然后在vue.config.js里面加入代码: 首先在开头加入 ...
  • 通过这种异步加载组件的方式,在打包的时候就不会把相关的组件打包到首屏相关的js中,可以减少首屏渲染相关文件的大小。 形如: () => import('@/views/abc.vue') 不管是路由 还是页面,都可以用这种形式。 大致这...
  • 如果有外部加载如cssjs,先看缓存有没有,没有就请求,注意这里浏览器做了优化,就是边解析dom边加载资源。 注意2,当html解释器遇到js标签会阻塞,可以用async/defer解决。 第2步 开始计算css和构建布局树 主线程...
  • 下载 compression-webpack-plugin压缩插件 const CompressionPlugin = require(‘compression-webpack-plugin’) // 如果版本过高不支持压缩 当前版本6.1.1 const UglifyJsPlugin = require(‘uglifyjs-webpack-...
  • 使用CDN资源,减小服务器带宽压力 路由懒加载 将一些静态js css放到其他地方(如OSS),减小服务器压力 按需加载三方资源,如iview,建议按需引入iview中的组件 使用nginx开启gzip减小网络传输的流量大小 若首屏为登录...
  • 特别在移动端,单页面应用的首屏加载优化更是绕不开的话题。下面我会写出我在项目中做的一些优化,希望大家能够相互讨论,共同进步。 我的项目vue-cli3构建的,vue+vue-router+vuex,UI框架选用 element-ui,ajax...
  • vue首屏优化

    2021-11-29 17:34:36
    先分析加载慢的原因,再做优化 1. 首先安装webpack的可视化资源分析工具,命令行执行: npm i webpack-bundle-analyzer -D 2. 然后在webpack的dev开发模式配置中,引入插件,代码如下: const ...
  • Vue首屏渲染优化

    2021-01-31 10:53:17
    Vue首屏渲染速度优化 webpack构建工具会将vue及vue全家桶打包成一个vendors.js文件,体积至少会有一个1mb以上,导致首屏加载会出现长时间的白屏。 目录 import异步加载 CND 静态资源压缩 分包 开启Gizp import异步...
  • web的首屏加载优化

    2021-05-18 10:57:34
    白屏加载和首屏加载时间的区别 白屏时间是指浏览器从响应用户输入网址...然而现在的很多前端开发者,都没能重视对首屏加载的优化。 正常来说,首屏加载时间不应该超过2s。有些成熟的网页比如京东和淘宝,虽然页面模
  • 优化首屏时间,可以分为以下几种情况: 首屏渲染的内容较多,需要集合多份数据进行渲染。这种情况需要开发者把内容分优先级,把优先级高的内容做优先展示,缩短白屏时间; 首屏内容依赖的数据从服务端请求的时间太...
  • vue首屏优化方案

    2021-11-26 14:43:43
    在vue项目中,通过npm安装到工程中所有的js,css文件,在编译时都会被打包进vendor.js,浏览器在加载该文件后才开始显示首屏。我们可以使用 CDN 代替这部分不经常变化的公共库。将vue,vue-router,vuex,axios,...
  • 小游戏优化-首屏渲染

    2021-01-18 17:48:42
    目的:快速进入首屏渲染 // 创建一个 2d context const gl = window.canvas.getContext(‘webgl’); let needLoadNum = 0; let image = wx.createImage(); image.onload = function () { //webgl显示 initWebgl(); /...
  • 有没有对首屏做过优化* 1)代码层面的优化 v-if 和 v-show 区分使用场景 computed 和 watch 区分使用场景 v-for 遍历必须为 item 添加 key,且避免同时使用 v-if 长列表性能优化 事件的销毁 addEventlisenter 事件...
  • 2.延迟加载:非重要的库、非首屏图片延迟加载,SPA的组件懒加载等; 3.减少请求内容的体积:开启服务器Gzip压缩,JS、CSS文件压缩合并,减少cookies大小,SSR直接输出渲染后的HTML等; 4.浏览器渲染
  • 这篇文章主要介绍了Vue SPA单页应用首屏优化实践,内容挺不错的,现在分享给大家,也给大家做个参考。1.代码压缩(gzip)如果你用的是nginx服务器,请修改配置文件(其他web server 类似):sudo nano /etc/nginx/nginx....
  • 最近有业务代表反馈某个系统首屏展加载时间相对其他系统要慢不少,希望能够加快速度,提高用户的满意度。于是越好技术人员,一起对生产环境的系统进行性能测试,确实正如业务同学所述,系统完全加载出来的时间需要近...
  • vue 的特点是 SPA 单页面应用。当打包时会把所有的页面代码都打包到 一个 app.js 的文件中 打包命令:npm run build 1、利用路由的懒加载写法,可以把页面的代码 拆分到不同的 JS 文件中, 不再都放在 app.js 里 ...
  • 关于性能优化,个人感慨要有针对性的性能优化,比如首屏速度/压缩文件/整合资源,要学会观察请求响应 - 分析 - 解决问题。 来源:pro-xiaoy https://juejin.cn/post/6964352706832302117 最后 “在看和转发”就是...
  • vue首屏加载优化

    2021-05-14 15:41:14
    'ResetPassword', require: true }, component: () => import('@/base/register/ResetPassword'), }, 2、不打包库文件 spa首屏加载慢,主要是打包后的js文件过大,阻塞加载所致。那么如何减小js的体积呢? 那就是把...
  • 转载地址: 移动 H5 首屏秒开优化方案探讨 美团-WebView性能、体验分析与优化 手机QQ-70%以上业务由H5开发,手机QQ Hybrid 的架构如何优化演进?
  • SPA首屏加载速度慢的解决 一、什么是首屏加载 1、首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗...
  • 首屏加载优化

    2021-03-21 17:04:47
    cdn缓存 懒加载 图片压缩 减少请求的数量 缓存(前后端都可以做) localStorange 、redis,静态文件缓存方案 script标签的async和defer这两个属性 静态文件缓存方案(cache-control) 使用nginx开启gzip减小网络传输...
  • 写在前面本文记录笔者在Vue SPA项目首屏加载优化过程中遇到的一些坑及优化方案!我们以 vue-cli 工具为例,使用 vue-router 搭建SPA应用,UI框架选用 element-ui , ajax方案选用 axios, 并引入 vuex ,使用 vuex-...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,602
精华内容 15,840
关键字:

首屏优化