前端性能优化:去除多余代码、插件按需引入、使用SVG图标、项目打包优化

304 阅读3分钟

在前端开发中,我一直很注重性能优化,这篇文章就给大家分享一下我的优化思路与方案。

当使用浏览器打开一个URL时,浏览器会先查看当前URL是否有缓存,没有缓存则进行DNS域名解析,将域名解析为IP地址,建立TCP链接,会经历三次握手,向服务器发送HTTP请求,服务器返回HTML文档;

浏览器开始渲染页面,首先解析HTML文档生成DOM树,然后下载CSS文件,解析CSS文件生成CSSOM树,DOM树和CSSOM树相结合生成渲染树。

根据渲染树里面元素的大小和位置,进行网页布局;布局完成后,再遍历render渲染树,根据渲染树里面的颜色、尺寸等显示属性,进行绘制,最终将网页显示在屏幕上。

访问URL会经历很复杂的一个过程,所以我从下面几个部分提高前端性能。

优化流程

移除多余文件

移除不用插件

按需引入插件

小图片转base64格式

使用SVG图标

当前项目技术栈:Vue + Element

分析项目文件大小

在项目优化之前我都会对前端项目进行分析,利用webpack包分析器,分析文件大小

安装插件

npm install webpack-bundle-analyzer -D

在vue.config.js里面配置

// vue.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
  chainWebpack: config => {
    // 结果输出分析
    config.plugin('webpack-bundle-analyzer')
      .use(
        new BundleAnalyzerPlugin({
          analyzerMode: 'server', // 'server' | 'static' | 'disabled',
	        analyzerHost: '127.0.0.1',
	        analyzerPort: 8889
        })
      )
  }
}

运行项目,自动打开分析页面

npm run serve

3359e8f2545427d3353cc99a794d396.png

利用插件分析无用文件

安装插件

npm i useless-files-webpack-plugin -D

在vue.config.js里面配置

const UselessFile = require('useless-files-webpack-plugin')

module.exports = {
  chainWebpack: config => {
    // 结果输出分析
     config.plugin('useless-files-webpack-plugin')
      .use(
        new UselessFile({
          root: './src', // 项目目录
          out: './fileList.json', // 输出文件列表
          clean: false,// 删除文件,
          // exclude: path // 排除文件列表, 格式为文件路径数组
        })
      )
  }
}

运行项目,自动生成无用文件列表

npm run serve

当运行项目时会自动生成fileList.json文件,里面显示着没有用到的文件,理论上是都可以删除的,但是建议根据实际情况进行删除,尽量不要删除以后可能会用到的文件。

Echarts 按需引入

安装Echarts

npm install echarts --save

如果只有首页用到了Echarts,建议直接在文件夹内引入,若很多页面都用到Echarts可在main.js里全局引入

// 引入基本模板
import * as echarts from 'echarts/lib/echarts'
// 引入柱状图组件
import 'echarts/lib/chart/bar'
// 引入提示框和title组件,图例
import 'echarts/lib/component/tooltip'
import 'echarts/lib/component/title'
import 'echarts/lib/component/legend'
import 'echarts/lib/component/grid'

Element 按需引入

安装插件

// 安装插件
npm install element-ui --save
// 安装 babel-plugin-component
npm install babel-plugin-component -D

引入

import { Button, Row } from 'element-ui';

Vue.use(Button);

因为按照官方配置 .babelrc会报错Cannot find module 'babel-preset-es2015' ,所以跳过该配置,进行下面配置

安装插件

npm install babel-preset-es2015 -D

配置babel.config.js

module.exports = {
  'presets': [
    '@vue/cli-plugin-babel/preset'
  ],
  'plugins': [
		[
			'component',
			{
				'libraryName': 'element-ui',
				'styleLibraryName': 'theme-chalk'
			}
		]
	]
}

按需引入全局尺寸配置

Vue.prototype.$ELEMENT = { size: 'small' }

使用SVG

安装插件

npm install svg-sprite-loader -D

在vue.config.js里面配置

const path = require('path')

function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  chainWebpack: config => {
    // set svg-sprite-loader
    config.module
      .rule('svg')
      .exclude.add(resolve('src/assets/icons'))
      .end()
    config.module
      .rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/assets/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()
  }
}

然后在src/assets目录下创建icon文件夹,内部有svg文件夹,用于存放svg文件,还有index.js文件。

// index.js 文件
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg component

// register globally
Vue.component('svg-icon', SvgIcon)

const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

然后在components 中新建组件 SvgIcon.vue

<template>
  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :href="iconName" />
  </svg>
</template>

<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'

export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    }
  },
  computed: {
    isExternal() {
      return isExternal(this.iconClass)
    },
    iconName() {
      return `#icon-${this.iconClass}`
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    },
    styleExternalIcon() {
      return {
        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
      }
    }
  }
}
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}

.svg-external-icon {
  background-color: currentColor;
  mask-size: cover!important;
  display: inline-block;
}
</style>

内部引入的isExternal函数

export function isExternal(path) {  
    return /^(https?:|mailto:|tel:)/.test(path)
}

最后在main.js文件中引入icon

// main.js
import './assets/icons' // icon

在vue文件里使用即可

<svg-icon icon-class="email" />

小图片转base64格式

安装插件

cnpm install url-loader file-loader -D

vue.config.js配置

configureWebpack: {
  // limit 单位为B 1000 = 1k
   chainWebpack(config) {
     config.module
      .rule('images')
      .use('url-loader')
      .loader('url-loader')
      .tap(options => Object.assign(options, { limit: 200000,esModule: false}))
   }
}