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

web前端性能优化

程序员文章站 2022-05-14 13:07:12
...

1、图片处理

  • 图片压缩:使用图片图片压缩、优化工具TinyPNG、TinyJPG压缩图片,或者使用其Gulp 组件gulp-tinypng结合到自动化构件流程中;
  • 图片格式转为base64: 使用webpack的url-loader,自动根据文件大小决定要不要做成内联 base64;
  • 图片懒加载: vue.js可用使用vue-lazyload;

2、CDN优化

  • 将依赖的静态资源如vuevue-routervuex等,全部改为通过CDN链接获取。
  • 借助HtmlWebpackPlugin,可以方便的使用循环语法在index.html里插入js和css的CDN链接。推荐CDN使用 jsDelivr 提供的。
  • index.html文件中
<!-- 使用CDN加速的CSS文件,配置在vue.config.js下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
<% } %>
<!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
<% } %>
复制代码
  • vue.config.js下添加如下代码,这使得在使用CDN引入外部文件的情况下,依然可以在项目中使用import的语法来引入这些第三方库,也就意味着你不需要改动项目的代码
// 转为CDN外链方式的npm包,键名是引入的npm包名,键值是该库暴露的全局变量
const externals = {
    vue: 'Vue',
    'vue-router': 'VueRouter',
    vuex: 'Vuex',
    axios: 'axios',
    vant: 'vant',
    'pdfjs-dist': 'pdfjs',
};
// 添加CDN参数到htmlWebpackPlugin配置中;
        config.plugin('html').tap((args) => {
            if (process.env.NODE_ENV === 'production') {
                args[0].cdn = CDN.build;
            }
            return args;
        });
复制代码

3、Prerender 预渲染

4、Gzip 优化

  • 后台开启gzip压缩,以nginx服务器为例
# 开启和关闭gzip模式
    gzip on;
    # nginx对于静态文件的处理模块,开启后会寻找以.gz结尾的文件,直接返回,不会占用cpu进行压缩,如果找不到则不进行压缩
    gzip_static on;
    # gizp压缩起点,文件大于1k才进行压缩
	gzip_min_length 1k;
    # 设置压缩所需要的缓冲区大小,以4k为单位,如果文件为7k则申请2*4k的缓冲区 
	gzip_buffers 4 16k;
    # gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时
	gzip_comp_level 2;
    # 进行压缩的文件类型。
	gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    # 禁用IE 6 gzip
	gzip_disable "MSIE [1-6]\.";
复制代码
  • 前端gzip压缩
    • nginx设置gzip_static on,就会使用同名的.gz文件,不会占用服务器的CPU资源去压缩
const CompressionPlugin = require('compression-webpack-plugin');
configureWebpack: () => {
        if (process.env.NODE_ENV === 'production') {
            // 开启gzip压缩 同名的.gz文件 不会占用服务器的CPU资源去压缩
            const compressionTest = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
            return {
                plugins: [
                    new CompressionPlugin({
                        test: compressionTest, // 匹配文件名
                        minRatio: 0.99, // 压缩率
                    }),
                ],
            };
        }
    },
复制代码
const path = require('path')
const chalk = require('chalk')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const PrerenderSPAPlugin = require('prerender-spa-plugin')

// 存放build结果的文件夹(主要是为了填prerender在配置了baseUrl后带来的坑,下面会说)
const DIST_ROOT = 'dist'
// 项目部署在服务器里的绝对路径,默认'/',参考https://cli.vuejs.org/zh/config/#baseurl
const BASE_URL = '/my-app/'
// 转为CND外链方式的npm包,键名是import的npm包名,键值是该库暴露的全局变量,参考https://webpack.js.org/configuration/externals/#src/components/Sidebar/Sidebar.jsx
const externals = {
  'vue': 'Vue',
  'vue-router': 'VueRouter',
  'vuex': 'Vuex',
  'axios': 'axios',
  'element-ui': 'ELEMENT'
}
// CDN外链,会插入到index.html中
const cdn = {
  // 开发环境
  dev: {
    css: [
      'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
    ],
    js: []
  },
  // 生产环境
  build: {
    css: [
      'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
    ],
    js: [
      'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js',
      'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js',
      'https://cdn.jsdelivr.net/npm/[email protected]/dist/vuex.min.js',
      'https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js',
      'https://unpkg.com/element-ui/lib/index.js'
    ]
  }
}
// 是否使用预渲染
const productionPrerender = true
// 需要预渲染的路由
const prerenderRoutes = ['/', '/contacts']
// 是否使用gzip
const productionGzip = true
// 需要gzip压缩的文件后缀
const productionGzipExtensions = ['js', 'css']

module.exports = {
  baseUrl: BASE_URL,
  outputDir: DIST_ROOT + BASE_URL, // prerendner会借助一个express服务器来预渲染,改变baseUrl后要保证这个模拟服务器能够找到所需的资源
  assetsDir: 'static',
  productionSourceMap: false,
  configureWebpack: config => {
    const myConfig = {}
    if (process.env.NODE_ENV === 'production') {
      // 1. 生产环境npm包转CDN
      myConfig.externals = externals
      // 2. 使用预渲染,在仅加载html和css之后即可显示出基础的页面,提升用户体验,避免白屏
      myConfig.plugins = []
      productionPrerender && myConfig.plugins.push(
        new PrerenderSPAPlugin({
          staticDir: path.resolve(__dirname, DIST_ROOT), // 作为express.static()中间件的路径
          outputDir: path.resolve(__dirname, DIST_ROOT + BASE_URL),
          indexPath: path.resolve(__dirname, DIST_ROOT + BASE_URL + 'index.html'),
          routes: prerenderRoutes,
          minify: {
            collapseBooleanAttributes: true,
            collapseWhitespace: true,
            decodeEntities: true,
            keepClosingSlash: true,
            sortAttributes: true
          },
          postProcess (renderedRoute) {
            /**
             * 懒加载模块会自动注入,无需直接通过script标签引入
             * 而且预渲染的html注入的是modern版本的懒加载模块
             * 这会导致在低版本浏览器出现报错,需要剔除
             * 这并不是一个非常严谨的正则,不适用于使用了 webpackChunkName: "group-foo" 注释的懒加载
             */
            renderedRoute.html = renderedRoute.html.replace(
              /<script[^<]*chunk-[a-z0-9]{8}\.[a-z0-9]{8}.js[^<]*><\/script>/g,
              function (target) {
                console.log(chalk.bgRed('\n\n剔除的懒加载标签:'), chalk.magenta(target))
                return ''
              }
            )
            return renderedRoute
          }
        })
      )
      // 3. 构建时开启gzip,降低服务器压缩对CPU资源的占用,服务器也要相应开启gzip
      productionGzip && myConfig.plugins.push(
        new CompressionWebpackPlugin({
          test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
          threshold: 8192,
          minRatio: 0.8
        })
      )
    }
    if (process.env.NODE_ENV === 'development') {
      /**
       * 关闭host check,方便使用ngrok之类的内网转发工具
       */
      myConfig.devServer = {
        disableHostCheck: true
      }
    }
    return myConfig
  },
  chainWebpack: config => {
    /**
     * 删除懒加载模块的prefetch,降低带宽压力
     * https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch
     * 而且预渲染时生成的prefetch标签是modern版本的,低版本浏览器是不需要的
     */
    config.plugins
      .delete('prefetch')
    /**
     * 添加CDN参数到htmlWebpackPlugin配置中
     */
    config
      .plugin('html')
      .tap(args => {
        if (process.env.NODE_ENV === 'production') {
          args[0].cdn = cdn.build
        }
        if (process.env.NODE_ENV === 'development') {
          args[0].cdn = cdn.dev
        }
        return args
      })
    /**
     * 无需使用@import在每个scss文件中引入变量或者mixin,也可以避免大量@import导致build变慢
     * sass-resources-loader 文档链接:https://github.com/shakacode/sass-resources-loader
     */
    const oneOfsMap = config.module.rule('scss').oneOfs.store
    const sassResources = ['color.scss', 'mixin.scss', 'common.scss'] // scss资源文件,可以在里面定义变量,mixin,全局混入样式等
    oneOfsMap.forEach(item => {
      item
        .use('sass-resources-loader')
        .loader('sass-resources-loader')
        .options({
          resources: sassResources.map(file => path.resolve(__dirname, 'src/style/' + file))
        })
        .end()
    })
  }
}

复制代码

转载于:https://juejin.im/post/5c889c7ff265da2de450b6e0