vue项目优化

代码压缩
代码压缩值得是文件级别的压缩,类似于你用的winrar以及好压等压缩工具来压缩的结果。压缩的文件一般以.gz结尾。比如:app.ea467643.js.gz。
服务端压缩
以nginx为例,找到配置文件,添加如下配置即可

server {
  gzip on;
  gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
  ...
}

开启前,文件大小1.7MB

 

开启后,文件大小646Kb

 

如何确定,后端返回的是压缩过的文件
如果服务器对资源进行压缩编码了,它就会通过响应头Content-Encoding告知当前请求用了什么编码格式,当然如果服务器没干这事,则不会返回这个响应头,比如某个请求用gzip压缩了返回的内容

 

 

 

客户端压缩
webpack打包时直接使用gzip压缩。
返回内容是在请求服务器的时候使用gzip进行动态压缩。这样存在的问题时,对于同一个资源的不同请求,反复压缩,这无疑会增加服务器的CPU和内存消耗。使用webpack去提前压缩好,然后让后端服务器拿去用现成压缩好的,则就可以避免这个问题。
安装压缩工具包

npm install compression-webpack-plugin --save-dev

在vue.config.js里配置如下(其实就是在配置webpack)

const CompressionPlugin = require('compression-webpack-plugin');
const compressionPlugin = new CompressionPlugin({
  test: /\.(js|css)?$/i, // 哪些文件要压缩
  filename: '[path].gz[query]',// 压缩后的文件名
  algorithm: 'gzip',// 使用gzip压缩
  minRatio: 1,// 压缩率小于1才会压缩
  deleteOriginalAssets: false // 删除未压缩的文件,谨慎设置,如果希望提供非gzip的资源,可不设置或者设置为false
})

module.exports = {
  configureWebpack:{ // 配置压缩gz插件
    plugins:[compressionPlugin]
  }
};

然后配置nginx.conf
gzip_static设置为on之后,这样在访问资源的时候,如果存在“资源路径.gz”的文件,则会直接返回该文件,其优先级高于动态的gzip。

server {
    ...
    gzip on;
    gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
    gzip_static on;
}

最后重启服务器,刷新页面就可以看到效果了。

类库不打包,引用外部模块
在 webpack 里有个 externals,可以忽略不需要打包的库。这样,就大大减少了chunk-vendors包的体积。
防止将某些 import 的包 ( package ) 打包到 bundle 中,而是在运行时 (runtime ) 再去从外部获取这些扩展依赖 ( external dependencies )。
即webpack外部扩展,依赖前置。利用cdn加速
这是没有使用外部模块前的体积,1.7MB

 

这是使用之后的,43kb

 

 

使用配置如下
在vue.config.js中

module.exports = {
  configureWebpack:{
    externals: {  // 使用外部资源
      'vue': 'Vue',
      'vue-router': 'VueRouter',
      'vuex': 'Vuex',
      'axios': 'axios',
      'echarts': 'echarts',
      'element-ui':'ELEMENT',
      'lodash': '_',
      'dayjs': 'dayjs'
    }
  }
};

在public/index.html中,配置如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vuex/3.2.0/vuex.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.3/vue-router.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/echarts/4.7.0/echarts.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/index.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.8.27/dayjs.min.js"></script>
    <title>xxx</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

 

其他注意的
开发环境下是不需要如上这些配置的,所以如果追求极致,可以额外判断一下
index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <% if(NODE_ENV==='production'){ %>
      <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/vuex/3.2.0/vuex.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.3/vue-router.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/echarts/4.7.0/echarts.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/index.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.8.27/dayjs.min.js"></script>
    <% } %>
    <title>xxx</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
View Code

vue.config.js

externals: process.env.NODE_ENV === 'production'?{  // 使用外部资源(开发环境下不用这个)
      'vue': 'Vue',
      'vue-router': 'VueRouter',
      'vuex': 'Vuex',
      'axios': 'axios',
      'echarts': 'echarts',
      'element-ui':'ELEMENT',
      'lodash': '_',
      'dayjs': 'dayjs'
}:{}
View Code

 

打包分析工具
在如上的讲解中,都是通过network来分析查看,工具出库前后包大小,请求时长的对比。实际上,有个专业的工具包来做此事,那就是BundleAnalyzerPlugin。
是一个webpack的插件,使用也很简单。

const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
const bundleAnalyzerPlugin = new BundleAnalyzerPlugin({});

module.exports = {
  configureWebpack:{ 
    plugins:[
        bundleAnalyzerPlugin // 打包分析
    ]
  }
};    

然后,每次npm run build成功之后,就会自动打开8888端口号的该分析结果页面
这个分析的更加清晰

vendors提取的是第三方公共库,公共资源,这些文件会打到dist/js/chunk-vendors.js里面 ,提取规则是每个页面都引入的才会打到chunk-vendors.js里面(如vue.js)
 

完整配置
提出上边完整的vue.config.js配置

// eslint-disable-next-line @typescript-eslint/no-var-requires
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const CompressionPlugin = require('compression-webpack-plugin');
const bundleAnalyzerPlugin = new BundleAnalyzerPlugin({

});
const compressionPlugin = new CompressionPlugin({
  test: /\.(js|css)?$/i, // 哪些文件要压缩
  filename: '[path].gz[query]',// 压缩后的文件名
  algorithm: 'gzip',// 使用gzip压缩
  minRatio: 1,// 压缩率小于1才会压缩
  deleteOriginalAssets: false // 删除未压缩的文件,谨慎设置,如果希望提供非gzip的资源,可不设置或者设置为false
})


module.exports = {
  publicPath: process.env.BASE_URL,
  devServer: {
    proxy: process.env.VUE_APP_SERVER,  // 代理
    open: true // 启动项目自动开启浏览器
  },
  configureWebpack:{
    plugins:[  //插件
      bundleAnalyzerPlugin, // 打包分析
      compressionPlugin // gz压缩
    ],
    externals: {  // 使用外部资源
      'vue': 'Vue',
      'vue-router': 'VueRouter',
      'vuex': 'Vuex',
      'axios': 'axios',
      'echarts': 'echarts',
      'element-ui':'ELEMENT',
      'lodash': '_',
      'dayjs': 'dayjs'
    }
  }
};

 

路由和组件懒加载

const routes = [
  {
    path: '/',
    name: 'data-home',
    component: () => import(/* webpackChunkName: "data-home" */ '../views/data/index.vue'),
    children: [
      {
        path: '/',
        name: 'today-data',
        component: () => import(/* webpackChunkName: "today-data" */ '../views/data/today-data/index.vue')
      },
      {
        path: '/our-data',
        name: 'our-data',
        component: () => import(/* webpackChunkName: "our-data" */ '../views/data/our-data/index.vue')
      },
      {
        path: '/customer-data',
        name: 'customer-data',
        component: () => import(/* webpackChunkName: "customer-data" */ '../views/data/customer-data/index.vue')
      }
    ]
  },
  {
    path: '/login',
    name: 'login',
    component: () => import(/* webpackChunkName: "login" */ '../views/accounts/login.vue')
  }

];

这样的话,单个组件或文件依赖其他部分,并不会在打包的时打在一起,而非根据懒加载关键字来切割。
当需要的使用的时候,再去请求并加载。
这样就减少了单个模块或文件的体积,从而加快相应速度(尤其是首屏)。

需要注意的是在vue-cli3升级之后,配置了webpack的预加载,这时候除了路由懒加载需要的当前页面资源,其他资源也会被请求,但不会被解析。
白色文件就是预加载的文件,仍然会耗时下载,只是不会被解析。这些文件属于prefetch,也就是预读取,他的加载基本不会影响到当前页面的打开,而且在读取完成后,可以降低页面跳转的等待时间,是一个非常好的功能。
预加载技术(prefetch)是在用户需要前我们就将所需的资源加载完毕,不是所有浏览器都支持,主要是Chrome浏览器。
有人说:单如果由于预加载文件比较多,反而阻塞了实际需要的文件下载,首页渲染速度可能也会导致变慢。
但是我不这么认为,因为这是浏览器提供的功能,它会处理好这个关系。倒是会给开发人员造成疑惑“我明明懒加载”了,则么还是一起请求

解决办法

// vue.config.js
module.exports = {
  chainWebpack: config => {
    // 移除 prefetch 插件
    config.plugins.delete('prefetch')

    // 或者
    // 修改它的选项:
    config.plugin('prefetch').tap(options => {
      options[0].fileBlacklist = options[0].fileBlacklist || []
      options[0].fileBlacklist.push(/myasyncRoute(.)+?\.js$/)
      return options
    })
  }
}

 


 

 

 

 

 

参考:
https://www.jb51.net/article/182323.htm
https://blog.csdn.net/crazywoniu/article/details/73480344
https://www.cnblogs.com/HYZhou2018/p/10419703.html
https://www.jianshu.com/p/078afc10fd3c
https://segmentfault.com/q/1010000018392785
https://blog.csdn.net/huangpb123/article/details/84170557
https://www.cnblogs.com/callmesummer/p/4345197.html

posted @ 2020-05-24 22:05  丁少华  阅读(467)  评论(0编辑  收藏  举报