随着前端各种结构的日益完善,一些根本的性能优化和加载优化都现已很完善了,可是有些必要的优化还是得开发者自己去做。vue.js是一个比较流行的前端结构,与react.js、angular.js相比来说,vue.js入手曲线更加流通,不论把握多少都可以快速上手。可是单页面运用也都有其弊端,有时分首屏加载慢的让人捏舌。今日咱们以vue cli2.x来说一说怎么卓有成效的缓解此问题!以项目为例,输入网址今后会呈现十几秒的空白页,假如是后台办理体系还能接受,嵌套式的H5面对的是C端用户,产品肯定是无法接受的。仔细分析了下,首要是打包后的app.js太大,以及咱们引证的一些插件装置包加载比较慢,在网上搜了很多处理加载慢的计划,最终优化的时刻移动端H5页面2秒多,后台办理体系5秒多。下面,将自己在平常项目上所做的优化策略实践分享于大家。文章较长,耐性看完会有收成的。或许可以到我的个人博客里边查看更多的技能

计划一、路由懒加载

首屏加载慢的原因无非便是单页面运用需求加载完好个路由表上的页面,而路由懒加载便是来处理这个问题的。假如咱们能把不同路由对应的组件切割成不同的代码块,然后当路由被拜访的时分才加载对应组件,这样就更加高效了。下面这个便是vue路由懒加载的一个详细比如。办法很简略,假如您不想深入了解,只需依照这个格局引进路由就可以了。假如您对路由懒加载感兴趣,请移步vue-router路由懒加载。

此办法会把本来打包到一个app.js文件分开成多个js文件打包,这样会减小单个文件的巨细,可是不会减小整个js文件夹的巨细。

经过这种方法可以做到按需加载,只加载单个页面的js文件。

  {
    path: '/home',
    name: 'home',
    component: r => require.ensure([], function (require) {
      r(require('views/hls/Home/Home.vue'))
    }, 'home'),
    meta: {
      title: '主页',
      keepAlive: false,
      hasTop: true,
      hasBottom: true
    }
  }

计划二、组件按需加载(代码切割)

webpack将打包资源都打包在了一个bundle.js中,其间首要包含了开发的源代码 和 第三方依靠node_modules。 咱们可以对node_modules第三方依靠 打包资源拆分细化成多个资源文件,凭借浏览器支撑HTTP一起发起多个恳求特性,使得资源异步并行加载,然后提高资源加载速度。 webpack供给了splitChunks来支撑这一装备。

1. 首先引进按需加载东西 babel-plugin-import

babel-plugin-import是babel它会在编译过程中将 import 的写法主动转换为按需引进的方法。

npm install babel-plugin-import --save-dev

2. 在项目根目录创立.babelrc文件并装备按需加载内容:

{
 "plugins": [["import", {
  "libraryName": "iview",
  "libraryDirectory": "src/components"
 }]]
}

3. webpack装备:

// webpack.config.js
module.exports = function ({ production = false, development = false }) {
  ...
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'static/js/[name].[contenthash:8].js', // 主文件切割出的文件命名
    chunkFilename: 'static/js/[name].[contenthash:8].chunk.js', // splitChunks 切割出的文件命名
  },
  optimization: {
      minimize: true,
      ...
      splitChunks: {
        chunks: 'all',
        cacheGroups: {
          default: false,
          vendors: false,
          // 多页面运用,或许 webpack import() 多个 chunk 文件中,有 import 其他模块两次或许多次时,会打包生成 common
          common: {
            chunks: "all",
            minChunks: 2,
            name: 'common',
            enforce: true,
            priority: 5
          }, 
          // node_modules 中的公共模块抽离
          vendor: {
            test: /[\/]node_modules[\/]/,
            chunks: 'initial',
            enforce: true,
            priority: 10,
            name: 'vendor'
          },
          // @materual-ui
          material: {
            name: 'chunk-material',
            priority: 20, // 优先级高于 vendor
            test: /[\/]node_modules[\/]_?@material-ui(.*)/
          },
        }
      },
      runtimeChunk: { // 运行时代码(webpack执行时所需的代码)从主文件中抽离
        name: entrypoint => `runtime-${entrypoint.name}`,
      },
    },
})

4. 在main.js装备项目需求加载的组件

下面是iview的一个比如:

Vue.component("Icon", Icon)
Vue.component("Notice", Notice)
Vue.component("Button", Button)

这里需求留意全局注册的组件需求挂在到vue原型上,例如咱们需求运用Notice组件,那我就需求

Vue.prototype.$Notice = Notice;

这样咱们就可以正常的运用iview的组件了。

计划三、第三方组件库UI结构,运用按需引进

1. 凭借 babel-plugin-component ,引进咱们需求的组件,削减项目体积

npm install babel-plugin-component -D

2. 修正 babel.config.js 的内容

//babel.config.js 全文内容如下
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [
    [
      'component',
      {
        libraryName: 'element-ui',
        styleLibraryName: 'theme-chalk'
      }
    ]
  ]
}

3. 创立文件 element.js(名字自定义)

// element.js 全文内容如下,按自己需求引进就好了
import Vue from 'vue'
import {
    Button,
    Form,
    FormItem,
    Input,
    Message,
    Container,
    Header,
    Aside,
    Main,
    Menu,
    Submenu,
    MenuItem,
    Breadcrumb,
    BreadcrumbItem,
    Card,
    Row,
    Col,
    Table,
    TableColumn,
    Switch,
    Tooltip,
    Pagination,
    Dialog,
    MessageBox,
    Tag,
    Tree,
    Select,
    Option,
    Cascader,
    Alert,
    Tabs,
    TabPane
} from 'element-ui'
Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
Vue.use(Container)
Vue.use(Header)
Vue.use(Aside)
Vue.use(Main)
Vue.use(Menu)
Vue.use(Submenu)
Vue.use(MenuItem)
Vue.use(Breadcrumb)
Vue.use(BreadcrumbItem)
Vue.use(Card)
Vue.use(Row)
Vue.use(Col)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(Switch)
Vue.use(Tooltip)
Vue.use(Pagination)
Vue.use(Dialog)
Vue.use(Tag)
Vue.use(Tree)
Vue.use(Select)
Vue.use(Option)
Vue.use(Cascader)
Vue.use(Alert)
Vue.use(Tabs)
Vue.use(TabPane)
Vue.prototype.$message = Message
Vue.prototype.$confirm = MessageBox.confirm

4. 最后在 main.js 中引进这个文件

//main.js 中增加下面这行代码(途径和文件名按自己的来)
import './plugins/element.js'

别的在main.js中需求增加以下引进:

import ElementUI from 'element-ui';
Vue.use(ElementUI);

计划四、运用CDN减小代码体积加快恳求速度

在项目开发中,咱们会用到很多第三方库,假如可以按需引进,咱们可以只引进自己需求的组件,来削减所占空间,

但也会有一些不能按需引进,咱们可以采用CDN外部加载,在index.html中从CDN引进组件,去掉其他页面的组件import,修正webpack.base.config.js,在externals中加入该组件,这是为了防止编译时找不到组件报错。

所以运用CDN首要处理两个问题:

  1. 打包时刻太长、打包后代码体积太大,恳求慢
  2. 服务器网络不稳带宽不高,运用cdn可以回避服务器带宽问题

咱们将vue,vue-router,vuex,axios,echarts,element,moment运用CDN资源引进。国内的CDN服务推荐运用bootCDN

1. 在index.html里引进线上cdn,为了便利后续引证其他资源的cdn,所以咱们需求把index.html里边的引进cdn装备成动态的。

<html>
  ...
  <body>
    <!-- 运用CDN加速的JS文件,装备在vue.config.js下 -->
    <% for (var i in
    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
    <% } %>
  </body>
</html>

2. 在vue.config.js装备中装备是为了在加载的时分,引证cdn资源 而不是node_modules里的包

....
const externals = {
  vue: 'Vue',
  'vue-router': 'VueRouter',
  vuex: 'Vuex',
  axios: 'axios'
}
....
configureWebpack: config => {
    if (isProduction) { // 判别是否是出产环境的包
      Object.assign(config, {
        externals: externals
      })
    }
    config.module.unknownContextCritical = false
  }

一起注销掉main.js文件傍边的import

计划五、gzip打包,nginx敞开gzip紧缩

1、gizp紧缩是一种http恳求优化方法,经过削减文件体积来提高加载速度。html、js、css文件乃至json数据都可以用它紧缩,可以减小60%以上的体积。

2、之后便是nginx合作敞开gzip形式,这个比较简略,只需你对nginx有一点了解,咱们在nginx.conf中的http中装备一些代码。

compression-webpack-plugin这个依靠在npm run build是会生成.gz文件。之后项目拜访的文件便是这个.gz文件,正常的项目打包体积会削减一半还要多,先看下打包后的文件:

前端加载优化之速度优化处理计划

1. webpack装置compression-webpack-plugin插件,新版本有问题的话下载1.1.12版本装备vue.config.js

npm i compression-webpack-plugin --save-dev

vue.config.js装备:

const CompressionWebpackPlugin = require('compression-webpack-plugin')
configureWebpack:{
	plugins:[
      new CompressionWebpackPlugin({
        filename: '[path].gz[query]',
        algorithm: 'gzip',
        // test: /.js$|.html$|.json$|.css/,
        test: /.js$|.json$|.css/,
        threshold: 10240, // 只要巨细大于该值的资源会被处理
        minRatio: 0.8, // 只要紧缩率小于这个值的资源才会被处理
        // deleteOriginalAssets: true // 删去原文件
      })
    ],

2. 需求nginx服务器,更改nginx.conf文件, 加在如图所示方位

# 敞开gzip。
gzip on
# 敞开后假如能找到 .gz 文件,直接回来该文件,不会启用服务端紧缩。
gzip_static on
# 文件大于指定 size 才紧缩,以 kb 为单位。
gzip_min_length 1;
# 用于辨认http协议的版本,前期的浏览器不支撑gzip紧缩,用户会看到乱码,所以为了支撑前期版本加了此选项,现在此项根本可以忽略
gzip_http_version 1.1;
# 紧缩级别,1-9,值越大紧缩比越大,但更加占用 CPU,且紧缩功率越来越低。
gzip_comp_level 9;
# 紧缩的文件类型。
gzip_types text/css application/javascript application/json;
root /dist;

修正装备后从头加载生效:nginx -s reload

前端加载优化之速度优化处理计划

3. 验证是否装备成功

这步就很简略了只需求查看chunk类文件的Response Headers的Content-Encoding是否是gzip即可

前端加载优化之速度优化处理计划

计划六、打包文件中去掉map文件

打包的app.js过大,别的还有一些生成的map文件。先将多余的map文件去掉,找到config文件夹下index.js文件,把这个build里边的productionSourceMap改成false即可。

   /*
   Source Maps
   */
    productionSourceMap: true,  // 把这边的true改为false
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

计划七、紧缩代码,移除console.log

npm install uglifyjs-webpack-plugin --save-dev

装备vue.config.js:

const isProduction = process.env.NODE_ENV === 'production';
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
chainWebpack(config) {
    const plugins = [];
    if (isProduction) {
      plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            output: {
              comments: false, // 去掉注释
            },
            warnings: false,
            compress: {
              drop_console: true,
              drop_debugger: false,
              pure_funcs: ['console.log']//移除console
            }
          }
        })
      )
    }
  }

计划八、打包经过image-webpack-loader插件对图片紧缩优化

vue正常打包之后一些图片文件很大,使打包体积很大,经过image-webpack-loader插件可将大的图片进行紧缩然后缩小打包体积.

1. 先装置webpack依靠插件image-webpack-loader

npm install image-webpack-loader --save-dev

2. 在vue.config.js中module.exports修正

module.exports = {
  productionSourceMap: false,
  chainWebpack: config => {
    // ============紧缩图片 start============
    config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({ bypassOnDebug: true })
      .end()
    // ============紧缩图片 end============
  }
}

计划十、公共代码的抽离

在vue.config.js module.exports configureWebpack 里边新增,直接放在gzip紧缩下边即可

...
// 公共代码抽离
configureWebpack: config => {
  config.optimization = {
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: 'all',
          test: /node_modules/,
          name: 'vendor',
          minChunks: 1,
          maxInitialRequests: 5,
          minSize: 0,
          priority: 100
        },
        common: {
          chunks: 'all',
          test: /[\/]src[\/]js[\/]/,
          name: 'common',
          minChunks: 2,
          maxInitialRequests: 5,
          minSize: 0,
          priority: 60
        },
        styles: {
          name: 'styles',
          test: /.(sa|sc|c)ss$/,
          chunks: 'all',
          enforce: true
        },
        runtimeChunk: {
          name: 'manifest'
        }
      }
    }
  }
}
...

计划十一、Vue预烘托(Prerendering)

服务器端烘托 vs 预烘托 (SSR vs Prerendering)

假如你调研服务器端烘托(SSR)仅仅用来改进少数营销页面(例如 /, /about, /contact 等)的SEO,那么你或许需求预烘托。无需运用web服务器实时动态编译 HTML,而是运用预烘托方法,在构建时简略地生成针对特定路由的静态HTML文件。长处是设置预烘托更简略,并可以将你的前端作为一个完全静态的站点。

假如你运用webpack,你可以运用prerender-spa-plugin轻松地增加预烘托。它现已被Vue运用程序广泛测试 – 事实上,作者是Vue核心团队的成员。

三种不同烘托方法的差异:

客户端烘托: 用户拜访url,恳求html文件,前端依据路由动态烘托页面内容。要害链路较长,有必定的白屏时刻;

服务端烘托: 用户拜访url,服务端依据拜访途径恳求所需数据,拼接成 html 字符串,回来给前端。前端接收到html时已有当前url下的完好页面;

预烘托: 构建阶段生成匹配预烘托途径的html文件(留意:每个需求预烘托的路由都有一个对应的 html)。构建出来的html文件现已有静态数据,需求ajax数据的部分未构建。

预烘托处理的问题:

SEO: 单页运用的网站内容是依据当前途径动态烘托的,html文件中往往没有内容,网络爬虫不会等到页面脚本执行完再抓取;

弱网环境: 当用户在一个弱环境中拜访你的站点时,你会想要尽或许快的将内容呈现给他们。乃至是在 js 脚本被加载和解析前;

低版本浏览器: 用户的浏览器或许不支撑你运用的js特性,预烘托或服务端烘托可以让用户至少可以看到首屏的内容,而不是一个空白的网页。

Vue预烘托完成流程

1. prerender-spa-plugin装置,建议运用淘宝镜像的cnpm,因为npm装置经常失利,不然会呈现意向不到的问题,而且功率低,比较浪费时刻。

cnpm install prerender-spa-plugin --save-dev

2. vue.config.js装备文件

const path = require('path');
const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
在plugins下面,找到plugins对象,直接加到上面就行
// 预烘托装备
new PrerenderSPAPlugin({
    //要求-给的WebPack-输出运用程序的途径预烘托。
    staticDir: path.join(__dirname, 'dist'),
    //必需,要烘托的道路。
    routes: ['/login'],
    //必须,要运用的实践烘托器,没有则不能预编译
    renderer: new Renderer({
        inject: {
            foo: 'bar'
        },
        headless: false, //烘托时显现浏览器窗口。对调试很有用。  
        //等待烘托,直到检测到指定元素。
        //例如,在项目入口运用`document.dispatchEvent(new Event('custom-render-trigger'))` 
        renderAfterDocumentEvent: 'render-event'
    })
})

3. router.js装备文件,需求把vue的router形式设置成history形式

4. main.js文件的修正

在创立vue实例的mounted里边加一个事情,跟PrerenderSPAPlugin实例里边的renderAfterDocumentEvent对应上。

mounted () {
    document.dispatchEvent(new Event('render-event'))
 }

这是预烘托的根本装备,npm run build 一下,假如dist文件夹多了你想预烘托的文件夹,那么恭喜你,成功了!假如项目是用nginx做的署理,nginx还需求做一些装备,详细是:

location = / {
  try_files /home/index.html /index.html;
}
location / {
  try_files $uri $uri/ /index.html;
}

5. 相关页面情况

前端加载优化之速度优化处理计划

前端加载优化之速度优化处理计划