Flutter WebView性能优化使h5像原生页面一样优秀

目录
  • 引言
  • 服务端渲染
    • css 放哪里
    • 更新 css
    • 如何利用本地 css 快速显示页面
  • 浏览器渲染
  • 如何启动本地server
  • 如何让 WebView 的页面请求走本地服务
  • 优化图片请求
    • 代码实现
    • 代码逻辑
    • 关于图片类型
    • 关于图片地址
    • 把图片缓存到磁盘。
  • 总结一下
    • 服务端染页面方案
    • 浏览器渲染方案
    • 图片缓存
  • 番外

引言

WebView 的文章分两篇

  • 在 Flutter 中使用 webview_flutter 4.0 | js 交互
  • Flutter WebView 性能优化,让 h5 像原生页面一样优秀(本文)

本篇和大家一起讨论下性能优化的问题。

WebView 页面的体验上之所以不如原生页面,主要是因为原生页面可以马上显示出页面骨架,一下子就能看到内容。WebView 需要先根据 url 去加载 html,加载到 html 后才能加载 css ,css 加载完成后才能正常显示页面内容,至少多出两步网络请求。有的页面是用 js 渲染的,这样时间会更长。要想让 WebView 页面能接近 Flutter 页面的体验,主要就是要省掉网络请求的时间。

做优化要考虑到很多方面,在成本与收益之间做平衡。如果不是新开项目,需要考虑项目当前的情况。下面分两种情况讨论一下。

服务端渲染

页面 html 已经在服务端拼接完成。只需要 html,css 就可以正常查看页面(主要内容不受影响)。如果你的项目的页面是这样的,那么我们已经有了一个好的起点。

WebView 要显示一个页面,需要串行下面的过程。通过 url 加载到 html 后再加载 css,css 加载完成后显示页面。

url -> html -> css -> 显示

我们可以对 css 的请求做一下优化。优化方案有两种

  • 内联 css 到 html
  • 把 css 缓存到本地。

第一种方案比较容易做,修改一下页面的打包方案即可。很容易实现一份代码打包出两个页面,一个外链 css ,一个内联css。但坏处也是很明显的,每次都加载同样的 css,会增加网络传输,如果网络不佳的话,对首屏时间可能会产生明显的影响。就算抛开首屏时间,也会对用户的流量造成浪费。

第二种方案可以解决 css 重复打包的问题。首先要考虑的问题是:css 放在本地的哪个地方?

css 放哪里

有两个地方可以放

  • 放在 asset,和 app 一起打包发布,好处是简单可靠,坏处是不方便更新。
  • 放在 文档目录,好处是可以随时更新,坏处是逻辑上会复杂一些。

文档目录用于存储只能由该应用访问的文件,系统不会清除该目录,只有在删除应用时才会消失。

从技术上来说,这两种方案都是可以的。先说下不方便更新的问题:既然 app 的其它页面都不能随便更新,为什么不能接受这个页面的样式不能随便更新?如果是害怕版本冲突,那也好解决,发一次版,更新一次页面地址,每个版本都有其对应的页面地址,这样就不会冲突了。根本原因是掌控的诱因,即使你能控制住诱因,你的老板也控制不住。所以还是老老实实选第二种方案吧。

放哪里的问题解决了,接下来要考虑的是如何更新 css 的问题。

更新 css

因为有可能 app 启动后第一个展示的就是这个页面,所以要在 app 启动后第一时间就更新 css。但又有一个问题,每次启动都更新同样的内容是在浪费流量。解决办法是加一个配置,每次启动后第一时间加载这个配置,通过配置信息来判断要不要更新 css。

这个配置一定要很小,比如可以用二进制 01 表示true false,当然了可能不需要这么极端,用一个 map 就好。

如何利用本地 css 快速显示页面

在 app 上启动一个本地 http server 提供 css。 我们可以在打包的时候把 css 的外链写成本地 http,比如 http://localhost:8080/index.css

除了 css,页面的重要图片,字体等静态资源也可以放在本地,只要加载到 html 就可以立即显示页面,省了一步需要串行的网络请求。

到这里服务端渲染页面的优化就完成了,还是很简单的吧,示例代码在后面。

浏览器渲染

近年来,随着 vue,react 的兴起,由 js 在浏览器中拼接 html 逐渐成为主流。虽然可以用同构的方案,但那样会增加成本,除非必须,一般都是只在浏览器渲染。可能你的页面正是这样的。我们来分析一下。

WebView 要显示一个页面,需要串行下面的过程。通过 url 加载到 html 后再加载 css、js,js 请求完数据后才能显示页面。

url -> html -> css,js -> js 去加载数据 -> 显示

和服务端渲染的页面相比,首次请求时间更长。多出了 js 加载数据的时间。除了要缓存 css,还要缓存 js 和数据。缓存 js 是必须的,缓存数据是可选的。好消息是 html 只有骨架,没有内容,可以连 html 也一起缓存。

缓存 js,html 的方案和缓存 css 的方案是一样的。缓存数据会面临数据更新的难题,所以只可以缓存少量不需要时时更新的少量重要数据,不需要所有数据都缓存。app 的原生页面也是需要加载数据的,也不是每种数据都要缓存。

数据更新之所以说是一个难题,是因为很多内容数据是需要即时更新的。但数据已经下发到客户端,已经缓存起来,客户端不再发起新的请求,如何通知客户端进行数据更新?虽然有轮询,socket,服务端推送等方案可以尝试,但开发成本都比较高,和获得的收益相比,代价太大。

当缓存了 html,css,js 等静态资源后,h5 就已经和原生页面站在同一起跑线上了,对于只读的页面,体验上相差无几。

加载数据后还有js 拼接 html 的时间,和加载的时间相比,只要硬件还可以的情况下,消耗的时间可以忽略

图片不适合用缓存 css 的方案,因为图片太大也太多。只能预加载少量最重要的图片,其它大量图片只能对二次加载做优化,我们会在后面讨论

浏览器渲染的页面也需要打包的配合,需要把所有的要缓存的静态资源地址都换成本地地址,这就要求发布的时候一份代码需要发布两个页面。一个是给浏览器用的,资源都通过网络加载。一个是给 WebView 用的,资源都从本地获取。

思路已经有了,具体实现就简单了。下面我给出关键环节的示例代码,供大家参考。

如何启动本地server

本地不需要 https,用 http 用行了,但是需要在 AndroidManifest.xml 的 applictation 中做如下配置 android:usesCleartextTraffic="true"

import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_static/shelf_static.dart';
import 'package:path_provider/path_provider.dart';
Future<void> initServer(webRoot) async {
  var documentDirectory = await getApplicationDocumentsDirectory();
  var handler =
      createStaticHandler('${documentDirectory.path}/$webRoot', defaultDocument: 'index.html');
  io.serve(handler, 'localhost', 8080);
}

createStaticHandler 负责处理静态资源。

如果要兼容 windows 系统,路径需要用 path 插件的 join 方法拼接

如何让 WebView 的页面请求走本地服务

两种方案:

  • 打包的时候需要缓存的页面的地址都改成本地地址
  • 对页面请求 在 WebView 中进行拦截,让已经缓存的页面走本地 server。

相比之下,第 2 种方案都好一些。可以通过配置文件灵活修改哪些页面需要缓存。

在下面的示例代码中 ,cachedPagePaths 存储着需要缓存的页面的 path。

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class MyWebView extends StatefulWidget {
  const MyWebView({super.key, required this.url, this.cachedPagePaths = const []});
  final String url;
  final List<String> cachedPagePaths;
  @override
  State<MyWebView> createState() => _MyWebViewState();
}
class _MyWebViewState extends State<MyWebView> {
  late final WebViewController controller;
  @override
  void initState() {
    controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(NavigationDelegate(
        onNavigationRequest: (request) async {
          var uri = Uri.parse(request.url);
          // TODO: 还应该判断下 host
          if (widget.cachedPagePaths.contains(uri.path)) {
            var url = 'http://localhost:8080/${uri.path}';
            Future.microtask(() {
              controller.loadRequest(Uri.parse(url));
            });
            return NavigationDecision.prevent;
          } else {
            return NavigationDecision.navigate;
          }
        },
      ))
      ..loadRequest(Uri.parse(widget.url));
    super.initState();
  }
  @override
  void didUpdateWidget(covariant MyWebView oldWidget) {
    if(oldWidget.url!=widget.url){
      controller.loadRequest(Uri.parse(widget.url));
    }
    super.didUpdateWidget(oldWidget);
  }
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [Expanded(child: WebViewWidget(controller: controller))],
    );
  }
}

优化图片请求

如果页面中有很多图片,你会发现,体验上还是不如 Flutter 页面,为什么呢?原来 Flutter Image Widget 使用了缓存,把请求到的图片都缓存了起来。 要达到相同的体验,h5 页面也需要实现相同的缓存功能。

关于 Flutter 图片请参见 快速掌握 Flutter 图片开发核心技能

代码实现

要如何实现呢?只需要两步。

  • 打包的时候需要把图片的外链请求改成本地请求
  • 本地 server 对图片请求进行拦截,优先读缓存,没有再去请求网络。

第 1 条我举个例子,比如图片的地址为 https://juejin.com/logo.png ,打包的时候需要修改为 http://localhost:8080/logo.png

第 2 条的实现上,我们取个巧,借用 Flutter 中的 NetworkImage,NetworkImage 有缓存的功能。

下面给出完整示例代码,贴到 main.dart 中就能运行。运行代码后看到一段文字和一张图片。

注意先安装相关的插件,插件的名字 import 里有。

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_static/shelf_static.dart';
import 'dart:ui' as ui;
import 'package:webview_flutter/webview_flutter.dart';
const htmlString = '''
<!DOCTYPE html>
<head>
<title>webview demo | IAM17</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0,
  maximum-scale=1.0, user-scalable=no,viewport-fit=cover" />
<style>
*{
  margin:0;
  padding:0;
}
body{
   background:#BBDFFC;
   text-align:center;
   color:#C45F84;
   font-size:20px;
}
img{width:90%;}
p{margin:30px 0;}
</style>
</head>
<html>
<body>
<p>大家好,我是 17</p>
<img src='http://localhost:8080/tos-cn-i-k3u1fbpfcp/
c6208b50f419481283fcca8c44a2e3af~tplv-k3u1fbpfcp-watermark.image'/>
</body>
</html>
''';
void main() async {
  runApp(const MyApp());
}
class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
  WebViewController? controller;
  @override
  void initState() {
    init();
    super.initState();
  }
  init() async {
    var server = Server17(remoteHost: 'p6-juejin.byteimg.com');
    await server.init();
    var filePath = '${server.webRoot}/index.html';
    var indexFile = File(filePath);
    await indexFile.writeAsString(htmlString);
    setState(() {
      controller = WebViewController()
        ..loadRequest(Uri.parse('http://localhost:${server.port}/index.html'));
    });
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      body: SafeArea(
        child: controller == null
            ? Container()
            : WebViewWidget(controller: controller!),
      ),
    ));
  }
}
class Server17 {
  Server17(
      {this.remoteSchema = 'https',
      required this.remoteHost,
      this.port = 8080,
      this.webFolder = 'www'});
  final String remoteSchema;
  final String remoteHost;
  final int port;
  final String webFolder;
  String? _webRoot;
  String get webRoot {
    if (_webRoot == null) throw Exception('请在初始化后读取');
    return _webRoot!;
  }
  init() async {
    var documentDirectory = await getApplicationDocumentsDirectory();
    _webRoot = '${documentDirectory.path}/$webFolder';
    await _createDir(_webRoot!);
    var handler = Cascade()
        .add(getImageHandler)
        .add(createStaticHandler(_webRoot!, defaultDocument: 'index.html'))
        .handler;
    io.serve(handler, InternetAddress.loopbackIPv4, port);
  }
  _createDir(String path) async {
    var dir = Directory(path);
    var exist = dir.existsSync();
    if (exist) {
      return;
    }
    await dir.create();
  }
  Future<Uint8List?> loadImage(String url) async {
    Completer<ui.Image> completer = Completer<ui.Image>();
    ImageStreamListener? listener;
    ImageStream stream = NetworkImage(url).resolve(ImageConfiguration.empty);
    listener = ImageStreamListener((ImageInfo frame, bool sync) {
      final ui.Image image = frame.image;
      completer.complete(image);
      if (listener != null) {
        stream.removeListener(listener);
      }
    });
    stream.addListener(listener);
    var uiImage = await completer.future;
    var pngBytes = await uiImage.toByteData(format: ui.ImageByteFormat.png);
    if (pngBytes != null) {
      return pngBytes.buffer.asUint8List();
    }
    return null;
  }
  FutureOr<Response> getImageHandler(Request request) async {
    if (RegExp(
      r'\.(png|image)$',
    ).hasMatch(request.url.path)) {
      var url = '$remoteSchema://$remoteHost/${request.url.path}';
      var imageData = await loadImage(url);
      //TODO: 如果 imageData 为空,改成错误图片
      return Response.ok(imageData);
    } else {
      return Response.notFound('next');
    }
  }
}

代码逻辑

  • 在本地文档目录的 www 文件夹中准备了一个 index.html 文件
  • 启动本地 server,通过访问 http://localhost:8080/index.html 请求本地页面。
  • server 收到请求后,对图片请求进行拦截,通过 NetworkImage 返回图片。

第 2 条。本例中是直接访问的 localhost,实际应用中,页面地址是外链地址,通过拦截的方式请求本地。如何做页面地址拦截前面已经给出示例了。

第 3 条。打包后的时候对所有图片地址都写成了本地地址,改成本地地址的目的就是为了让图片请求都由本地 server 响应。本地 server 拿到 图片地址后,再改回网络地址,通过 NetworkImage 请求图片。NetworkImage 会首先判断有没有缓存,有直接用,没有就发起网络请求,然后再缓存。

可能你觉得有点绕,既然最后还要用网络地址,为什么还要先写成本地地址,象拦截页面请求那样拦截图片请求不香吗?答案是不可以。两个原因。

  • webview_flutter 只能拦截页面请求。
  • 本地 server 不方便拦截 443 端口。

对比于拦截 443 端口,修改打包方案要容易的多。

关于图片类型

在示例代码中,用 RegExp( r'\.(png|image)$',) 判断是否要响应请求。从正则可以看出,以 png 或 image 结果的图片都能响应请求。判断 image 是因为示例中的图片地址是以 image 结尾的。

示例代码只能支持 png 格式的图片,示例图片虽然是 image 结尾,但格式也是 png 格式。如果要支持更多格式的图片,需要用到第三方库。

关于图片地址

如果图片地址失改,可以自行换一个,随使在网上找个 png 图片 地址就行。

把图片缓存到磁盘。

我们演示了把图片缓存到内存,当 app 被杀掉,缓存都没了,除非缓存到磁盘。这项工作已经有插件帮我们做了。 用 cached_network_image 替换 NetworkImage,稍加改动就可以实现磁盘缓存了。

总结一下

服务端染页面方案

  • 打包的时候需要打出两个页面,一个页面的 css 外链接是外网,一个页面的 css 链接是本地。
  • 在 App 启动的时候根据配置信息预加载 css 存到文档目录。
  • 启动本地 server 响应 css 的请求。

浏览器渲染方案

  • 打包的时候需要打出两个页面,一个页面的 css,js 链接是外网,一个页面的 css,js 链接是本地。
  • 在 App 启动的时候根据配置信息预加载 html,css,js 存到文档目录。
  • 根据配置信息拦截页面请求,已经缓存的页面改走本地 server。
  • 启动本地 server 响应 html,css,js 的请求

图片缓存

如果不做图片缓存,通过前面两个方案,h5 速度就已经得到大大提高了。如果有余力,可以做图片缓存。图片缓存是可选的,是对前面两种方案的加强。

  • 给 app 用的页面打包的时候把图片地址换成本地地址。
  • 启动本地 server 响应图片请求,有缓存就读缓存,没有缓存走网络。

可能你的项目不同,有不同的方案,欢迎一起讨论。

本文到这里就结束了,谢谢观看。

番外

为了给自己一点压力,上一篇 在 Flutter 中使用 webview_flutter 4.0 | js 交互 中我就预告说今天要发这篇性能优化的文章。结果压力是有的了,但却没能按时完工(理想情况是周日下午完工,这样可以休息一下)。一个原因是 升级 flutter 报错,浪费了一个上午,再有就是写了一版后,并不满意,又重写了一版,最后才定稿。一直写到深夜才把主要内容写完。早上起来又做了补充修改。

以上就是Flutter WebView性能优化使h5像原生页面一样优秀的详细内容,更多关于Flutter WebView页面优化的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter SizedBox布局组件Widget使用示例详解

    目录 正文 child 的 constrains 确定自己的大小 SizedBox 的命名构造函数们 SizedBox.expand SizedBox.shrink SizedBox.fromSize SizedBox.square 应用场景 为 child 提供 tight 约束. 为 children 之间提供空白. 占位 正文 Flutter Sizedbox 是一个 布局组件,用来给 child 添加 tight 约束的,也可以用来添加空白. width,height是 Sizedbox

  • Flutter 图片开发核心技能快速掌握教程

    目录 正文 使用网络图片 把网络图片缓存到磁盘 使用 assets 图片 适配浅色与深色模式 在不同的设备使用不同分辨率的图片 关于设备 dpr 不完全匹配的处理 忽略 dpr 信息 使用相册图片 使用相机拍摄的图片 使用内存图片 图片用做装饰 图片预加载 centerSlice centerSlice 只能放大,不能缩小. 全局缓存 ImageCache 的设置 图片类之间的关系 ImageProvider obtainKey(ImageConfiguration) 方法 resolve(Im

  • flutter升级3.7.3报错Unable to find bundled Java version解决

    目录 引言 升级过程很顺利,一跑应用傻眼了,报错! build gradle 指定 compileSdkVersion 33报错 引言 Android studio 是2020 年的版本,有点老,昨天突发想法,升级到了 Android Studio Electric Eel 2022.1. 计划今天和明天写那个 Flutter WebView 优化的文章,这篇是 在 Flutter 中使用 webview_flutter 4.0 | js 交互 的续集.早上起来,发现 Flutter 有新版本了

  • Flutter 枚举值enum和int互相转化总结

    目录 一.需求来源 二.搞清楚 Flutter 枚举属性和方法 三.实现需求(以 PageView 滚动方式为例) 最后 一.需求来源 工作中偶尔会用到枚举值和 int 的互相转化,今天总结一下: 二.搞清楚 Flutter 枚举属性和方法 三.实现需求(以 PageView 滚动方式为例) 枚举值转 int:在当前索引值后加 .index 即可(默认从 0 开始): int 转枚举值:需要扩展枚举方法实现,实现如下: 定义枚举 PageViewScrollType /// PageView 滚

  • Flutter配置代理抓包实现过程详解

    目录 背景 工具准备 配置Flutter代理 方式一.http请求库配置代理 web_socket_channel配置代理 方式二.重写原生方法 背景 在开发Flutter中,我们经常需要对网络请求进行调试,而Flutter自带的devtool的network又不太好用,有时会出现请求成功,但是又看不到response返回(难道是我姿势不对?).于是我就尝试通过抓包来查看请求 工具准备 安装charles 有时我们需要抓https的请求,此时用charles抓包的内容是加密的,看不到明文,这时候

  • 原生JS实现图片懒加载之页面性能优化

    在项目开发中,我们往往会遇到一个页面需要加载很多图片的情况.我们可以一次性加载全部的图片,但是考虑到用户有可能只浏览部分图片.所以我们需要对图片加载进行优化,只加载浏览器窗口内的图片,当用户滚动时,再加载更多的图片.这种加载图片的方式叫做图片懒加载,又叫做按需加载或图片的延时加载.这样做的好处是:1.可以加快页面首屏渲染的速度:2.节约用户的流量. 一.实现思路 1.图片img标签自定义一个属性data-src来存放真实的地址. 2.当滚动页面时,检查所有的img标签,判断是否出现在事业中,如果

  • Android APP性能优化分析

    本文通过Android APP性能优化的四个方面做了详细分析,并对原理和重点做了详细解释,以下是全部内容: 说到 Android 系统手机,大部分人的印象是用了一段时间就变得有点卡顿,有些程序在运行期间莫名其妙的出现崩溃,打开系统文件夹一看,发现多了很多文件,然后用手机管家 APP 不断地进行清理优化 ,才感觉运行速度稍微提高了点,就算手机在各种性能跑分软件面前分数遥遥领先,还是感觉无论有多大的内存空间都远远不够用.相信每个使用 Android 系统的用户都有过以上类似经历,确实,Android

  • Flutter WebView 预加载实现方法(Http Server)

    目录 背景 分析 HttpServer 接下来? 资源配置 下载解压与本地存储 版本管理与更新 获取LocalServer Url并加载Webview 兜底措施 统一管理 展示与分析 总结 Demo 背景 WebView是在APP中,可以很方便的展示web页面,并且与web交互APP的数据.方便,并且更新内容无需APP发布新版本,只需要将最新的web代码部署完成,用户重新刷新即可. 在WebView中,经常能够听到的一个需求就是:减少首次白屏时间,加快加载速度.因为加载web页面,必然会受到网络

  • 浅谈Vue 初始化性能优化

    前言 一般来说,你不需要太关心vue的运行时性能,它在运行时非常快,但付出的代价是初始化时相对较慢.在最近开发的一个Hybrid APP里,Android Webview初始化一个较重的vue页面竟然用了1200ms ~ 1400ms,这让我开始重视vue的初始化性能,并最终优化到200 ~ 300ms,这篇文章分享我的优化思路. 性能瓶颈在哪里? 先看一下常见的vue写法:在html里放一个app组件,app组件里又引用了其他的子组件,形成一棵以app为根节点的组件树. <body> <

  • jQuery性能优化技巧分析

    本文较为详细分析了jQuery性能优化技巧.分享给大家供大家参考.具体分析如下: 一.使用最新版本的jQuery类库 jQuery新版本会较上个版本进行Bug修复和一些优化,不过需要注意的是,在更换版本之后,不要忘记测试你的代码,毕竟有时候不是完全向后兼容的. 二.使用合适的选择器 jQuery选择器性能最佳到最差方式如下: id选择器,如$('#id', context) 标签选择器,如$('p', context) class选择器,如$('.class', context) 属性选择器,如

  • jQuery性能优化28条建议你值得借鉴

    jQuery性能优化28条建议 一直在寻找有关jQuery性能优化方面的小窍门,能让我那臃肿的动态网页应用变得轻便些.找了很多文章后,我决定将最好最常用的一些优化性能的建议列出来.我也做了一个jQuery性能优化的简明样式表,你可以打印出来或者设为桌面背景. 一.选择器性能优化建议 1. 总是从#id选择器来继承 这是jQuery选择器的一条黄金法则.jQuery选择一个元素最快的方法就是用ID来选择了. 复制代码 代码如下: $('#content').hide(); 或者从ID选择器继承来选

  • 基于JavaScript 性能优化技巧心得(分享)

    JavaScript 作为当前最为常见的直译式脚本语言,已经广泛应用于 Web 应用开发中.为了提高Web应用的性能,从 JavaScript 的性能优化方向入手,会是一个很好的选择. 本文从加载.上下文.解析.编译.执行和捆绑等多个方面来讲解 JavaScript 的性能优化技巧,以便让更多的前端开发人员掌握这方面知识. 什么是高性能的 JavaScript 代码? 尽管目前没有高性能代码的绝对定义,但却存在一个以用户为中心的性能模型,可以用作参考:RAIL模型. 响应 如果你的应用程序能在1

  • mpvue性能优化实战技巧(小结)

    最近一直在折腾mpvue写的微信小程序的性能优化,分享下实战的过程. 先上个优化前后的图: 可以看到打包后的代码量从813KB减少到387KB,Audits体验评分从B到A,效果还是比较明显的.其实这个指标说明不了什么,而且轻易就可以做到,更重要的是优化小程序运行过程中的卡顿感,请耐心往下看. 常规优化 常规的Web端优化方法在小程序中也是适用的,而且不可忽视. 一.压缩图片 这一步最简单,但是容易被忽视.在tiny上在线压缩,然后下载替换即可. 我这项目的压缩率高达72%,可以说打包后的代码从

  • iOS性能优化教程之页面加载速率详解

    前言 我认为在编码过程中时刻注意性能影响是有必要的,但凡事都有个度,不能为了性能耽误了开发进度.在时间紧急的情况下我们往往采用"quick and dirty"的方案来快速出成果,后面再迭代优化,即所谓的敏捷开发.与之相对应的是传统软件开发中的瀑布流开发流程. 卡顿产生的原因 在 iOS 系统中,图像内容展示到屏幕的过程需要 CPU 和 GPU 共同参与.CPU 负责计算显示内容,比如视图的创建.布局计算.图片解码.文本绘制等.随后 CPU 会将计算好的内容提交到 GPU 去,由 GP

  • 写给前端工程师的Web前端性能优化的10点建议

    1. 减少HTTP请求 在浏览器(客户端)和服务器发生通信时,就已经消耗了大量的时间,尤其是在网络情况比较糟糕的时候,这个问题尤其的突出. 一个正常HTTP请求的流程简述:如在浏览器中输入"www.xxxxxx.com"并按下回车,浏览器再与这个URL指向的服务器建立连接,然后浏览器才能向服务器发送请求信息,服务器在接受到请求的信息后再返回相应的信息,浏览器接收到来自服务器的应答信息后,对这些数据解释执行. HTTP协议是无状态的应用层协议,意味着每次HTTP请求都需要建立通信链路.进

随机推荐