欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Vue项目优化小结

程序员文章站 2024-02-08 21:39:58
...

最近花了点时间优化了一下这几个月开发的项目,在网上搜集了很多并实践,如有不对欢迎指出;如有更好的优化方法,请告诉我!谢谢!

调试工具network中的几个参数

DOMContentLoaded、Finish和Load的含义

Vue项目优化小结

  • DOMContentLoaded: DOM树构建完成;

  • Load:页面加载完毕,包括DOM树构建和请求css、图片等外部资源;

  • Finish:页面上所有http请求发送到响应完成的时间。

    HTTP1.0/1.1 协议限定,单个域名的请求并发量是 6 个,即 Finish 是所有请求(不只是XHR请求,还包括DOC,img,js,css等资源的请求)在并发量为6的限制下完成的时间

Load > DOMContentLoaded意味着请求外部资源时间较大;

Finish > Load意味着页面请求量(和时间)较大(较长)。

Waterfall参数解析

颜色解析

  • 浅灰:查询中;
  • 深灰:停滞,代理转发,请求发送;
  • 橙色:初始连接;
  • 绿色:等待中;
  • 蓝色:内容下载。

优化方向

  • 减少瀑布宽度,即减少资源的加载时间;
  • 减少瀑布的高度,即减少请求的数量;
  • 将绿色的”开始渲染“线向左移动,即通过优化资源请求顺序来加快渲染时间。

Vue项目优化实践

脚手架是v-cli3版本的。

前期准备

生成打包报告

npm run build --report

可打开打包后的report.html查看打包体积,根据体积进行优化。

使用PageSpeed Insights插件

Vue项目优化小结

可以对网址进行评分,还会给出一些建议。

接下来分几种方向进行优化。

修改vue.config.js,优化打包体积

路由懒加载

不使用路由懒加载的话,会在一开始就下载完所有路由对应的组件文件。

// 修改前
import xxx from '@/components/xxx';
routes:[ 
    path: 'xxx', 
    name: 'xxx', 
    component: xxx 
]

// 修改后
 routes:[ 
     path: 'xxx',
     name: 'xxx',
     component: () => import(/* webpackChunkName: "Chat" */ './components/xxx')
]

关闭预加载模块

虽然在上面我们配置了异步加载,但是因为 vuecli 3默认开启 prefetch(预先加载模块),提前获取用户未来可能会访问的内容,在首屏会把这十几个路由文件,都一口气下载了,所以我们要关闭这个功能。

// vue.config.js
module.exports = {
    ...
    chainWebpack: (config) => {
        // 移除 prefetch 插件
        config.plugins.delete('prefetch-index')
        // 移除 preload 插件
        config.plugins.delete('preload-index');
  	},
    ...
}

Preload 是一个新的控制特定资源如何被加载的新的 Web 标准,这是已经在 2016 年 1 月废弃的 subresource prefetch 的升级版。这个指令可以在 中使用,比如 。一般来说,最好使用 preload 来加载你最重要的资源,比如图像,CSS,JavaScript 和字体文件。这不要与浏览器预加载混淆,浏览器预加载只预先加载在HTML中声明的资源。preload 指令事实上克服了这个限制并且允许预加载在 CSS 和JavaScript 中定义的资源,并允许决定何时应用每个资源。

Prefetch 是一个低优先级的资源提示,允许浏览器在后台(空闲时)获取将来可能用得到的资源,并且将他们存储在浏览器的缓存中。一旦一个页面加载完毕就会开始下载其他的资源,然后当用户点击了一个带有 prefetched 的连接,它将可以立刻从缓存中加载内容。有三种不同的 prefetch 的类型,link,DNS 和 prerendering。

按需加载

项目中使用vant框架,加载依赖包时应该这么操作:

// 优化前
import vantUI from 'vant'
Vue.use(vantUI)

// 优化后
import { Button, Toast, Picker, List, Cell, PullRefresh, Rate, Popup, Loading, DatetimePicker, ActionSheet  } from 'vant'
import 'vant/lib/index.css';

Vue.use(Button);
Vue.use(Picker);
...

gzip压缩

nmp install compression-webpack-plugin -D
// vue.config.js
const CompressionWebpackPlugin = require('compression-webpack-plugin');
module.exports = {
    ...
    configureWebpack: () => {
        let plugins = [];
        ...
        if (process.env.NODE_ENV === "'production'"){
          plugins.push(new CompressionWebpackPlugin({
            filename: "[path].gz[query]",
            algorithm: "gzip",
            test: productionGzipExtensions,
            threshold: 10240,
            minRatio: 0.8,
            deleteOriginalAssets:false//是否删除源文件
          }))
        }
        return {
          output: { // 输出重构  打包编译后的 文件名称  【模块名称.版本号.时间戳】
            filename: `js/[name].${process.env.VUE_APP_Version}.${Timestamp}.js`,
            chunkFilename: `js/[name].${process.env.VUE_APP_Version}.${Timestamp}.js`
          },

          plugins
        }

      },
    ...
}

打包之后文件被压缩了~不过需要告诉服务器做相应的配置,如果发送请求的浏览器支持 gzip,就发送给它 gzip格式的文件。

CSS拆分

CSS是否要拆分就看具体环境了,拆分了之后请求的资源数会变多,不拆分请求的体积可能会很大。

module.exports = {
    ...
    // css相关配置
      css: {
        // 是否使用css分离插件 ExtractTextPlugin,不需要改成false即可
        extract: true,
        // 开启 CSS source maps?
        sourceMap: false,
        // css预设器配置项
        loaderOptions: {},
        // 启用 CSS modules for all css / pre-processor files.
        modules: false
      },
    ...
}

CDN

是否使用CDN也看具体环境了,我用了CDN请求资源数更多了,首屏加载又变慢了°(°ˊДˋ°) °。

<!-- index.html -->
<script src="https://cdn.bootcss.com/vue/2.6.11/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<script src="https://cdn.bootcss.com/vuex/3.1.2/vuex.min.js"></script>
<script src="https://cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
// vue.config.js
module.exports = {
    ...
    configureWebpack: () => {
        ...
        externals:{
        vue: 'Vue',
        'vue-router': 'VueRouter',
        vuex: 'Vuex',
        axios: 'axios'
      }
    }
    ...
}

其他

在我的打包报告里面,lodash的体积占比很大,整个项目中,只有三处地方使用了lodashthrottle方法。

// 优化前
import * as _ from 'lodash'
private togglePicker = _.throttle(this.togglePickerAction, 400);

// 优化后
import * as _ from 'lodash/throttle'
private togglePicker = _(this.togglePickerAction, 400);

这样打包的时候只会打包有关lodashthrottle的东西,再次打包会发现lodash中的体积已经大大减少。

补充

const path = require('path')
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']

const resolve = (dir) => path.join(__dirname, dir)
const Timestamp = new Date().getTime();
module.exports = {
  productionSourceMap: false, // 是否在构建生产包时生成 sourceMap 文件,false将提高构建速度
  outputDir: `release/${process.env.VUE_APP_Version}`,
  lintOnSave: false,
  publicPath: process.env.NODE_ENV === 'production' ? '/online/' : './',
  chainWebpack: (config) => {
    config.module.rule('pug')
      .test(/\.pug$/)
      .use('pug-html-loader')
      .loader('pug-html-loader')
      .end()
    config.resolve.alias
      .set('@', resolve('src'))
      .end()
    // 移除 prefetch 插件
    config.plugins.delete('prefetch-index')
    // 移除 preload 插件
    config.plugins.delete('preload-index');
  },
  // lintOnSave: true,
  pluginOptions: {
    'style-resources-loader': {
      preProcessor: 'less',
      patterns: [
        path.resolve(__dirname, 'src/assets/style/common.less')
      ]
    }
  },
  // css相关配置
  css: {
    // 是否使用css分离插件 ExtractTextPlugin
    extract: true,
    // 开启 CSS source maps?
    sourceMap: false,
    // css预设器配置项
    loaderOptions: {},
    // 启用 CSS modules for all css / pre-processor files.
    modules: false
  },
  configureWebpack: () => {
    let plugins = [];
    plugins.push(new SpeedMeasurePlugin());
    if (process.env.NODE_ENV === "'production'"){
      plugins.push(new CompressionWebpackPlugin({
        filename: "[path].gz[query]",
        algorithm: "gzip",
        test: productionGzipExtensions,
        threshold: 10240,
        minRatio: 0.8,
        deleteOriginalAssets:false//是否删除源文件
      }))
    }
    return {
      output: { // 输出重构  打包编译后的 文件名称  【模块名称.版本号.时间戳】
        filename: `js/[name].${process.env.VUE_APP_Version}.${Timestamp}.js`,
        chunkFilename: `js/[name].${process.env.VUE_APP_Version}.${Timestamp}.js`
      },

      plugins
    }

  },

  pages: {
    index: {
      // page 的入口
      entry: 'src/main.ts',
      // 模板来源
      template: 'public/index.html',
      // 在 dist/index.html 的输出
      filename: 'index.html',
      // 当使用 title 选项时,
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title: 'Index',
      // 在这个页面中包含的块,默认情况下会包含
      // 提取出来的通用 chunk 和 vendor chunk。
      chunks: ['chunk-vendors', 'chunk-common', 'index']
    }
  },
  parallel: require('os').cpus().length > 1,
  devServer: {
    proxy: {}
  }
}

运行优化

合理使用v-if和v-show

v-for加上key

使用Object.freeze优化长列表

初始化时,vue会对data做getter、setter改造,在现代浏览器里,这个过程实际上挺快的,但仍然有优化空间。

Object.freeze() 可以冻结一个对象,冻结之后不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。该方法返回被冻结的对象。

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter,这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

但 Vue 在遇到像 Object.freeze() 这样被设置为不可配置之后的对象属性时,不会为对象加上 setter getter 等数据劫持的方法

作者:Mr_Google
链接:https://juejin.im/post/5d5e89aee51d453bdb1d9b61

本项目里面有很多纯展示列表,所以修改如下:

this.list = Object.freeze(this.list.concat(list));

监听事件、eventBus和计时器等即时销毁

activated () {
    document.addEventListener('click', this.handleClick)
 },
 deactivated () {
    document.removeEventListener('click', this.handleClick)
 },
someMethod(){
    eventBus.$on(this.action);
}

beforeDestroy(){
	eventBus.$off(this.action);
}

编译优化

项目开发时候热更新打包好慢…修改了.env.dev,在里面加入这么一句:

VUE_CLI_BABEL_TRANSPILE_MODULES:true

其他优化

合并CSS,精简JS,优化图片等。

首次发布于:Vue项目优化小结

相关标签: 实践总结