不到一秒才叫优化

简介: 之前做完的一个项目,业务逻辑写完之后,首屏渲染能到3~4秒,这对于用户体验是不能接受的,所以忙里偷闲把项目优化完之后http发送到响应时间:705ms,DOM构建完毕:452ms,页面加载完毕:678ms,清爽的感觉很上头~看来优化还是很有必要的!所以本篇记录一下优化过程。

网络异常,图片无法展示
|

之前做完的一个项目,业务逻辑写完之后,首屏渲染能到3~4秒,这对于用户体验是不能接受的,所以忙里偷闲把项目优化完之后:

网络异常,图片无法展示
|

http发送到响应时间:705ms,DOM构建完毕:452ms,页面加载完毕:678ms,清爽的感觉很上头~看来优化还是很有必要的!所以本篇记录一下优化过程。


正文


  1. UglifyJsPlugin
    UglifyJsPlugin 是一个webpack的解析、混淆、压缩JS的工具,低版本的 UglifyJsPlugin 是不支持ES6的,所以在使用的时候要注意要么升级版本,要么添加文件 .bablelrc,在里面添加
{
  "pressets": ["es2015"]
}
  1. cdn
    cdn 的话,尽可能把所有资源都使用cdn,比如 vuevuexvue-routeraxiosechart,举个例子:
// cdn链接
const cdn = {
  externals: {
    echarts: "echarts",
    "ant-design-vue": "AntDesignVue",
    vue: "Vue",
    "vue-router": "VueRouter",
    vuex: "Vuex",
    axios: "axios"
  },
  // cdn的css链接
  css: [],
  // cdn的js链接
  js: [
    "https://cdn.jsdelivr.net/npm/echarts@4/dist/echarts.min.js",
    "https://cdn.bootcss.com/vue/2.6.10/vue.min.js",
    "https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js",
    "https://cdn.bootcss.com/vue-router/3.0.7/vue-router.min.js",
    "https://cdn.bootcss.com/axios/0.18.0/axios.min.js"
  ]
};
  1. 然后在两个地方使用,一个是webpack的 chainWebpack 配置,一个是 configureWebpack 的配置:
// 是否为生产环境
const isProduction = process.env.NODE_ENV !== "development";
// 本地环境是否需要使用cdn
const devNeedCdn = true;
module.exports = {
  // webpack 配置
  chainWebpack: config => {
    // ...
    config.plugin("html").tap(args => {
      // 生产环境或者本地需要cdn时,才注入cdn
      if (isProduction || devNeedCdn) args[0].cdn = cdn;
      return args;
    });
  },
  configureWebpack: config => {
    // 用cdn方式引入,则构建时要忽略相关资源
    if (isProduction || devNeedCdn) config.externals = cdn.externals;
  }
}
  1. 这样 cdn 就配置好了,如果想在开发环境也使用它的话,需要在入口文件 index.html 中把CSS和JS的资源加载改成:
<!-- 使用CDN的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn &&
   htmlWebpackPlugin.options.cdn.css) { %>
<link
   href="<%= htmlWebpackPlugin.options.cdn.css[i] %>"
   rel="stylesheet"
/>
<% } %>
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn &&
  htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script> 
<% } %>
  1. 相当于动态加载了webpack配置里的cdn文件。
  2. CompressionWebpackPlugin
    CompressionWebpackPlugin 是webpack的 Gzip 压缩插件,官方介绍是:提供带 Content-Encoding 编码的压缩版的资源,下面是它的配置项:
配置项 类型 默认值 描述
test {RegExp} . 处理所有匹配此 {RegExp} 的资源
asset {String} [path].gz[query] 目标资源名称。[file] 会被替换成原资源。[path] 会被替换成原资源路径, [query] 替换成原查询字符串
filename {Function} false 一个 (asset) => asset 函数,接收原资源名(通过 asset 选项)返回新资源名
algorithm {String|Function} gzip 可以是 (buffer, cb) => cb(buffer) 或者是使用 zlib 里面的算法的 {String}
threshold {Number} 0 只处理比这个值大的资源。按字节计算
minRatio {Number} 0.8 只有压缩率比这个值小的资源才会被处理
deleteOriginalAssets {Boolean} false 是否删除原资源
  1. 这边我用了网上大家常用的配置:
configureWebpack: config => {
  const productionGzipExtensions = ["html", "js", "css"];
  config.plugins.push(
    new CompressionWebpackPlugin({
      filename: "[path].gz[query]",
      algorithm: "gzip",
      test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"),
      threshold: 10240, // 只有大小大于该值的资源会被处理 10240
      minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
      deleteOriginalAssets: false // 删除原文件
    })
  );
}
  1. 注意,如果使用 Gzip 的话,打包后会出现一个 x.gz 文件,比如 index.html,打包后会多一个 index.html.gz 文件,如果服务器端(nginx等)不开启 gzip 功能,加载的其实还是 index.html,开启之后就会加载 index.html.gz 来替代加载 index.html了。
    在 nginx 中可以这么配置(nginx.conf):
#gzip  on;
# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式,后面的图片压缩不需要的可以自行删除
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript ;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 设置压缩所需要的缓冲区大小     
gzip_buffers 4 16k;
  1. 重启之后查看请求接口的响应头的 Content-Encodinggzip 的话说明开启成功。
  2. moment
    由于用到了 moment,所以也得把它压缩一下,它可是贼大的,因为它的npm包里加载了很多的语言版本,所以一般情况下,只需要中文版就可以了,所以在webpack的配置里,把moment的语言包给忽略掉,然后在 main.js 中加载一个语言包 import "moment/locale/zh-cn",这样体积就会小很多。
  3. splitChunks
    splitChunks 可以对大包进行拆分,对小包进行合并,防止模块重复打包,从而减少请求资源的大小和此时,这个要根据实际情况去调整。
  4. 代码逻辑加了以上的操作之后,可以使用插件webpack-bundle-analyzer来看下优化的效果,如果还有大块的,就把业务继续优化,主要从引用、封装入手,能抽出来的尽量抽出来,把工具和业务分开。以vue为例的话,可以注意以下几点:
  1. v-if 和 v-show
    v-if 适合条件改变很少的情况,v-show 适合频繁切换条件的情况
  2. computed 和 watch
    computed 适合在计算和依赖场景使用,watch 适合数据变化的异步操作
  3. v-for 必须添加 key
    vue的 diff 算法会关联到 key,所以当状态更新时,key的作用很大,为了优化效率,建议添加key(现在开发工具已经做了提醒)
  4. v-for 里不使用 v-if
    v-for 的优先级要比 v-if 高,如果vue的循环节点每次都重新遍历整个数组,就需要花很多时间
  5. event
    在页面上使用了事件之后,离开的时候最好把事件给销毁(全局除外),可以防止内存泄漏(比如图表的加载和重绘)
  6. 图片懒加载
    图片资源懒加载可以使用 vue-lazyload 插件,保证只加可视区域内的图片
  7. 路由懒加载
    如果不用路由懒加载的话,页面访问就会把所有路由都加载完毕之后再加载,如果资源过多,白屏的出现几率就会大大增加,所以路由要改成只在访问的时候加载:component: () => import("./views/Login.vue")
  8. 三方插件按需引入
    很多三方插件包很大,所以它们大部分也都给了按需加载的使用说明
  9. 防抖和节流
    对于触发事件的时间和时机判定,防止用户频繁点击和误操作,它们两个也算优化的一部分
目录
相关文章
|
11月前
|
人工智能 前端开发 程序员
通义灵码 AI 程序员与开发者结伴编程,全栈开发电商工程的前后端功能需求
当你又收到了项目新需求的时候,可以尝试下载并使用通义灵码,让通义灵码 AI 程序员跟你一起结伴编程,它具备多文件代码修改和工具使用的能力,可以与你结伴协同完成编码任务,如需求实现、缺陷修复、单元测试生成、批量代码修改等,成为你的左膀右臂。下面我们就跟AI程序员结伴编程完成前后端需求的开发吧!
799 1
|
atlas
Cesium导入geojson数据
这篇文章详细描述了如何在Cesium中导入GeoJSON数据来绘制地理矢量数据,并提供了实现的具体方法和示例代码。
756 2
Cesium导入geojson数据
|
前端开发 JavaScript 应用服务中间件
Nginx 开启 gzip 压缩,让网站飞一会!
一起来了解下nginx的gzip压缩
694 3
|
关系型数据库 PostgreSQL
postgresql如何将没有关联关系的两张表的字段合并
【6月更文挑战第2天】postgresql如何将没有关联关系的两张表的字段合并
413 3
|
安全 物联网 数据安全/隐私保护
车联网
对于车联网的操作,我们可以按照以下步骤进行,这些步骤涵盖了从初始设置到日常使用的大部分关键流程。请注意,具体步骤可能会因车型、汽车制造商以及所选的服务提供商而有所不同。
|
SQL 分布式计算 Oracle
sqoop用法之mysql与hive数据导入导出
Sqoop是一个用来将Hadoop和关系型数据库中的数据相互转移的工具,可以将一个关系型数据库(例如:MySQL、Oracle、Postgres等)中的数据导进到Hadoop的HDFS中,也可以将HDFS的数据导进到关系型数据库中。对于某些NoSQL数据库它也提供了连接器。Sqoop,类似于其他ETL工具,使用元数据模型来判断数据类型并在数据从数据源转移到Hadoop时确保类型安全的数据处理。Sqoop专为大数据批量传输设计,能够分割数据集并创建Hadoop任务来处理每个区块。
1828 0
|
SQL 分布式计算 大数据
利用SparkSQL Logical Plan Parse 打造大数据平台SQL诊断利器
利用SparkSQL Logical Plan Parse 打造大数据平台SQL诊断利器
372 0
|
Kubernetes Cloud Native Docker
如何将自己的镜像使用 helm 部署
如何将自己的镜像使用 helm 部署
520 0
如何将自己的镜像使用 helm 部署
|
存储 Linux iOS开发
【Picgo指南】图床神器之Picgo工具的安装与快速上手详细教程
【Picgo指南】图床神器之Picgo工具的安装与快速上手详细教程
3115 6
|
Java Spring 容器
深入理解BeanDefinition和Spring Beans
本文深入探讨了Spring框架中的BeanDefinition和Spring Beans。BeanDefinition是Bean的元数据,包含类名、作用域、构造函数参数和属性值等信息。Spring Beans是根据BeanDefinition实例化的对象。文章详细阐述了BeanDefinition的属性,如类名、作用域(如单例和原型)及构造函数和属性值。此外,还介绍了如何使用BeanDefinition动态注册、延迟加载和实现依赖注入。通过示例代码,展示了如何创建和自定义BeanDefinition以满足特定需求。理解BeanDefinition有助于更高效地开发和维护Spring应用程序。
384 0