项目优化-针对vue

优化

1. 代码层面

  • 部分代码使用懒加载方式,比如点击事件,回调函数内进行import('xxx').then()

  • vue路由使用懒加载: component: ()=> import('@/views/about.vue')

  • v-if/v-show: ① 尽量多的使用v-if,减少渲染dom数量,加快首屏加载速度。② 权限控制使用v-if

  • v-for遍历时,必须加上key,加上key是为了① 准确找到该条数据;② 在虚拟dom执行diff时,尽可能的复用dom节点

  • v-for遍历避免同时使用v-if。可以先判断好条件,再进行v-for。比如在computed中先处理好数据computed:{ activedUsers(){return this.users.filter(user => user.isActived)}},在template中使用activedUsers进行遍历<li v-for="(user,i) in activedUsers">{{user.name}}</li>

  • 防抖

  • computed、watch:
    ① 需要数值计算,并需要依赖其他数据,使用computed,因为可以利用computed的缓存特性,避免每次获取值时,都需要重新计算;
    ② 当我们在数据变化时,需要执行异步或者开销比较大的操作时,比如 图标渲染,使用watch,允许我们执行异步操作(访问一个api),限制我们执行该操作的频率(比如,只有在数据发生改变的时候,才可以做什么)

  • vue长列表新能优化:
    ① vue的数据响应式是通过 Object.defineProperty()进行数据劫持,实现视图响应数据变化。但一些长列表仅仅只是展示作用,不会有任何改变,我们就不需要vue来劫持我们的数据,在大量数据展示的情况下,可以明显减少组件初始化的时间
    ② 使用Object.freeze()方法来冻结一个对象,一旦被冻结,这个对象就不可以进行修改了,对于data或vuex里使用freeze冻结了的对象,vue不会做getter和setter的转换。

    export default{
      data(){return{
        users:[]
      }},
      async created(){
        const { users } = await axios.get('/api/users')
        // vue 不对 this.users存储的 users 进行响应式拦截
        this.users = Object.freeze(users)
      }
    }
    
  • 在beforeDestory/destroyed中进行 ① 事件注销解绑(事件有在mounted中进行注册) ② 清除定时器 ③ 定义的全局变量 设为null后者undefined ④ 在beforeDestory中进行注销eventBus

    mounted(){
      let self = this;
      //监听窗口变化,重绘echarts
      window.onresize = () => { 
        return (() => {
          if (!!self.fansCome) { 
            self.fansCome.resize();
          }
        })()
      };
    
      eventBus.$on('vipUpdate', () => { //声明一个vipUpdate的eventBus事件
        this.openDialog(this.curTab);
      })
    },
    beforeDestory(){
      eventBus.$off('vipUpdate'); //页面销毁前注销该事件,避免vipUpdate事件的多次触发 
    },
    destroyed() { 
      //页面销毁时需要清除全局的onresize事件,便面其他页面触发onresize事件中的方法
      window.onresize = null;
    },
    
  • 图片资源懒加载: vue-lazyload图片过多的页面,使用图片懒加载。等图片到可是区域内,在进行加载图片
    npm i vue-lazyload --save-dev

    import VueLazyload from 'vue-lazyload'
    vue.use(VueLazyload)
    
  • 第三方插件按需引入

  • 无线长列表优化: 使用虚拟屏幕滚动,只渲染窗口处+上线部分数据

  • 服务端渲染 ssr

  • 减少或合并dom操作,或使用虚拟dom操作(非框架,原生html)

    // 不好的方式
    var elem = $('#elem');
    for (var i = 0; i < 100; i++) {
     elem.append('<li>element '+i+'</li>');
    }
    
    // 好的方式
    var elem = $('#elem' ),
    arr = [];
    for (var i = 0;  i < 100; i++) {
      arr.push('<li>element ' +i+'</li>' );
    }
    elem.append(arr. join(''));
    
  • 对大量数据计算,使用缓存

    // ❌
    for(var i = 0;i < data.length;i++){
      // do something...
    }
    // ✅
    for(var i = 0,len = data.length;i < len;i++){
      // do something...
    }
    

2. webpack配置

  • 图片loader,小于10k的图片转变为base64、或者使用 svg 来代替小图标

  • 图片 image-webpack-loader, 对大图进行压缩

  • js配置babel时,使用corejs的usage来实现按需兼容:useBuiltIns: 'usage',corejs:{version: 3}

  • HMR

  • source-map:

    1. vue.config.js中productionSourceMap:false, 去掉.map文件;
    2. webpack.config.js
      ① 开发环境:eval-source-map
      ② source-map 或者 hidden-source-map(隐藏源代码,但会提示构建后代码的错误信息)
  • oneOf:loader加载时,用oneOf来保证一个文件只被一个rule进行处理,当然如果确实需要多个rule进行处理,要注意顺序

  • 缓存:
    ① babel的配置项中添加cacheDirectory: options:{cacheDirectory: true},开启babel缓存,第二次构建时就不需要再次打包一些公共文件,构建速度更快。 (或者使用 babel-plugin-transform-runtime插件)
    ② 服务器资源缓存,使用hash、contenthash,配合http的缓存策略,设置过期时间为一年,只有文件改变(hash值改变)文件才会改变,否则使用缓存

  • 多进程打包:thread-loader
    当js代码比较多时,开启多进程打包: 在匹配js的rule中设置。 多进程打包,开启进程是需要消耗时间的600ms左右,进程通信也是需要时间的,所以文件不大,不需要开启多进程打包

    {
      loader: 'thread-loader',
      options: {
        workers: 2 // 进程数为2
      }
    },
    
  • code split:
    第三方依赖包js中动态导入 import('xxx')文件单独打包
    webpack.config.js

    optimization: {
      /* splitChunks根据不同策略来分割bundle */
      splitChunks: { chunks: 'all' } // 默认值:async ; all: 同时分割同步和异步代码
    }
    
  • externals: 在webpack.config.js中配置externals,忽略依赖包打包,手动在html中加入cdn引入 => 减少文件体积,加快首屏速度
    index.html

    <script src="https://cdn.bootcss.com/jquery/1.11.0/jquery.min.js"></script>
    <script src="//cdn.bootcss.com/vue/2.2.2/vue.min.js"></script>
    <script src='//cdn.bootcss.com/mint-ui/2.2.3/index.js'></script>
    

    index.js

    import $ from 'jquery'
    

    webpack.config.js

    module.exports = {
      // ...
      externals: {
        'jquery': 'jQuery',
        'vue': 'Vue',
        'mint-ui': 'MINT'
      }
    }
    
  • main.js去中引入的第三方包的样式表去除,该在index.html中使用cdn引入
    减小打包体积,加快首屏加载速度

  • dll分包,针对第三方模块:dll是webpack内置模块,webpack只处理一次第三方模块,后续打包告诉webpack不需要处理这些依赖

    1. dll.config.js: 使用dll将指定第三方包单独打包出来,并生成对应的manifest.json(映射关系表)
    2. 在package.json中配置dll命令,并运行 dll命令,将第三方依赖单独打包出来
    3. npm i add-asset-html-webpack-plugin --save-dev
    4. webpack.config.js:
      ① 告诉webpack哪些包不参与打包,同时使用的名字也得变,manifest.json
      ② 在html中自动引入这些单独打包出来的文件
      dll.config.js
    /* 
    webpack.dll.js
    运行命令: webpack --config webpack.dll.js
    
    使用dll技术,将指定第三方依赖包单独打包成相应的单独文件, ① 减小打包后文件体积; ② 减少第三方包的打包次数,加快构建速度(因为某些指定库已经打包过,再次构建不需要打包这些库,要构建的文件变少,速度变快)
    
    使用插件: 
    生成一个manifest.json,提供与第三方依赖包的映射关系
    new webpack.DllPlugin({
      name: '[name]_[hash]', // 映射库的暴露名称
      path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
    })
    
    */
    const { resolve } = require('path')
    const  webpack = require('webpack')
    
    module.exports = {
      entry:{
        // vue: ['vue/dist/vue.esm.js','vue-router'],
        vue: ['vue/dist/vue.esm.js'],
        vendors: ['jquery', 'lodash', 'axios']
      },
      output: {
        filename: '[name].js',
        path: resolve(__dirname, 'dll'),
        library: '[name]_[hash]' // 打包的库里面向外暴露的内容的名称叫什么名字
      },
      plugins: [
        new webpack.DllPlugin({
          name: '[name]_[hash]', // 映射库向外暴露的内容名称
          path: resolve(__dirname, 'dll', '[name].manifest.json') // 输出文件路径
        })
      ],
      mode: 'production'
    }
    

    package.json

    "scripts":{
      // ...
      "dll": "webpack --config dll.config.js"
    }
    

    webpack.config.js

    const { resolve } = require('path')
    const plugins = [
      // 插件 ...
    ]
    
    /* 
    利用fs读取dll目录中,单独打包出来的js文件,并自动添加插件到plugins中
    */
    
    fs.readdirSync(resolve(__dirname, 'dll')).forEach(file => {
      if(/.*\.manifest.json$/.test(file)){
        plugins.push(new webpack.DllReferencePlugin({ manifest: resolve(__dirname, `dll/${file}`)}))
      }
      if(/\.js$/.test(file)){
        plugins.push(new AddAssetHtmlWebpackPlugin({filepath: resolve(__dirname, `dll/${file}`), publicPath: './'}))
      }
    })
    
    module.exports = {
      entry: './src/index.js',
      output:{ filename: './static/js/[name][contenthash:6].js', path: resolve(__dirname, 'dist')},
      module:{
        rules: [
          {
            oneOf: [
             // ...
          ]
          }
        ]
      },
      plugins: [...plugins],
      optimization:{
        splitChunks:{ chunks: 'all'}
      },
      devtool: 'source-map',
      mode: 'production'
    }
    
  • vue服务器渲染 ssr(实际项目中并未使用过,具体的不太了解)

3. 基础web技术层面

  • nginx开启gzip压缩
  • 对静态资源进行缓存,强缓存的expire、协商缓存etag、last-modify
  • 合理使用cdn请求静态资源(要综合平衡首屏加载时间问题)
    • 加快响应速度,可以是请求总时间降低
    • 加大浏览器的同时请求数量,因为使用了不同域名,一个域名可以并发4-8个请求(为什么图片要分多个域名?)
    • 减少cookie
  • 使用骨架屏
  • 优化http请求
    • 资源缓存:合理使用cache control来代替http请求
      • 强缓存expire时间设为1年
      • 优化响应:配合 etags,浏览器请求的文件与服务器一至,返回304, 浏览器使用 缓存
      • 资源文件名加上hash,只有打包的文件hash值改变,才会请求新的资源,否则一至使用缓存
      • 优化响应:nginx开启gzip压缩
    • 合理合并js、css文件,使用webpack构建工具进行打包boundle,同时要平衡首屏加载速度
    • 合理使用浏览器的并发请求数,一个域名下可同时并发多个请求,ie可以同时请求下载4个,谷歌可以同时请求下载8个:所以可以适当使用cdn请求一些静态资源,图片使用懒加载,
    • 小图使用inline image,即base64减小cookie体积,每个请求都会携带cookie,不滥用cookie
    • 大图可以使用webpack进行大图压缩
  • 图片
    • 懒加载
    • webpack进行配置
      • 小图 base64
      • 大图 图片压缩
    • 图片尽量使用webp格式(有更优化的算法,体积更小)
    • 重要图片,写上alt属性
  • app加上过渡动画,loading...优化用户体验
posted @ 2021-11-16 16:20  shine_lovely  阅读(91)  评论(0编辑  收藏  举报