辰風依恛
文章35
标签0
分类11
Vue2打包优化方案

Vue2打包优化方案

Vue2打包优化方案

记录一些Vue2打包优化方案,方便后续复用

当前优化方案会整理到一个demo中方便复用

demo使用到的版本

生产环境关闭source-map

代码错误定位的功能,如果关闭了就没法定位到错误的代码位置,不方便调试

建议只在生产环境中关闭,或者生产环境运行一段时间后关闭

代码

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
const { defineConfig } = require('@vue/cli-service')
const isProd = process.env.NODE_ENV === 'production'

module.exports = defineConfig({
/** 兼容低版本 */
transpileDependencies: true,
/** 生产环境使用路径 */
publicPath: '/',
/** 生产环境不生成错误定位 */
productionSourceMap: isProd,
})

CDN加速

CDN(Content Delivery Network,内容分发网络)是一组地理位置分散的 Web 服务器,通过智能调度把用户请求导向最近/最优节点

  1. 缩短物理传输距离 → 降低 RTT
  2. 避开公网拥塞 → 提高吞吐量
  3. 边缘缓存静态资源 → 减少回源流量
  4. 隐藏源站真实 IP → 抗 DDoS

最直接的就是:公共的CND多个网站共用一个,共用一个缓存;就近服务器请求。

通常前端只处理使用的库:vue、vuex、vue-router、axios、element-ui等

优化前后对比

vue2demo:vue、vuex、vue-router、axios、element-u,不包含其他的库了

直接打包:5mb左右

使用cdn后:500kb不到

代码

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const { defineConfig } = require('@vue/cli-service')
const isProd = process.env.NODE_ENV === 'production'
const cdnModules = {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
'element-ui': 'ELEMENT'
}

// 对应 CDN 链接(版本写死,可自己升级)
const cdn = {
css: [
'https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css'
],
js: [
'https://unpkg.com/vue@2.7.16/dist/vue.js',
'https://unpkg.com/vue-router@3.6.5/dist/vue-router.js',
'https://unpkg.com/vuex@3.6.2/dist/vuex.js',
'https://unpkg.com/axios@1.12.2/dist/axios.min.js',
'https://unpkg.com/element-ui@2.15.14/lib/index.js'
]
}

module.exports = defineConfig({
/** 兼容低版本 */
transpileDependencies: true,
/** 生产环境使用路径 */
publicPath: '/',
/** 生产环境不生成错误定位 */
productionSourceMap: isProd,
devServer: {
historyApiFallback: true,
/** 代理请求 */
// proxy: {
// '/api': {
// target: 'http://localhost:8081',
// changeOrigin: true
// }
// }
},
// webpack 层面 externals
configureWebpack: config => {
if (isProd) {
config.externals = cdnModules // 等价于 webpack 的 externals
}
},

// 通过 html-webpack-plugin 钩子自动插标签
chainWebpack: config => {
config.plugin('html').tap(args => {
args[0].cdn = isProd ? cdn : { css: [], js: [] } // 开发时不注入
return args
})
}
})

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="">
<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" />
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- CDN 样式 -->
<% for (var i in htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
<% } %>

<!-- CDN 脚本(放前面可提前加载) -->
<% for (var i in htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</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>

注意:如果不使用cdn,index.html中CDN脚本代码需要删除,webpack识别不了注释,不删除打包会报错

公共cdn

服务商 节点数 HTTP/2 Brotli 国内备案 备注
jsDelivr 750+ (Cloudflare + 网宿) 被墙概率低
unpkg Cloudflare 单栈 回源 npm,更新快
cdnjs Cloudflare 库最全

启用 Gzip

传输体积再降 60% 左右,会生成一些.gz的文件,需要服务器配置

90%的项目前端可以无脑加上Gzip压缩,如果服务器或用户的浏览器不支持也会走默认的文件不会出现错误

安装

1
npm i compression-webpack-plugin -D

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 引入compression-webpack-plugin
const CompressionPlugin = require('compression-webpack-plugin')

// 在configureWebpack中加入
// 生产环境开启 gzip 压缩
config.plugins.push(
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html|svg|json)(\?.*)?$/i,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false
})
)

懒加载

通常最常见的就是路由懒加载,还可以给引入的组件加上懒加载,以及对图片进行懒加载

懒加载主要是减少首屏加载,避免浏览器第一时间加载一些没有使用到的请求

路由懒加载和组件懒加载

路由懒加载和组件懒加载只需要引入使用下面这种即可,一般路由懒加载可以考虑加上webpackChunkName,打包后的名称就会带有home作为名称的一部分

1
const Home = () => import(/* webpackChunkName: "home" */ '@/views/Home.vue')

图片懒加载

图片没有进入视口不进行加载,直接加上loading=”lazy”,热门浏览器支持

1
<img src="placeholder.jpg" loading="lazy" alt="示例" />

vue-lazyload

兼容低版本浏览器或其他非热门浏览器则可以使用一些开源的组件库

安装

1
npm i vue-lazyload@^1.3.3 -S

全局注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Vue from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'

/* 任意 1×1 透明 gif,也可换成自己的 loading 图 */
import loading from '@/assets/loading.gif'
import error from '@/assets/error.png'

Vue.use(VueLazyload, {
preLoad: 1.3, // 提前 1.3 屏开始加载
error, // 失败占位
loading, // 加载中占位
attempt: 1, // 重试次数
listenEvents: ['scroll', 'wheel', 'mousewheel', 'resize', 'animationend', 'transitionend']
})

new Vue({
render: h => h(App)
}).$mount('#app')

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<template>
<div class="demo">
<h3>vue-lazyload 演示(Vue2)</h3>

<!-- 普通图片,不使用默认的src -->
<img
v-lazy="img.src"
:key="img.src"
class="u-img"
v-for="img in list"
:alt="img.alt"
/>

<!-- 背景图 -->
<div
v-lazy:background-image="bg"
:key="bg"
class="u-bg"
></div>
</div>
</template>

<script>
export default {
data() {
return {
bg: 'https://picsum.photos/800/400?random=0',
list: Array.from({ length: 30 }, (_, i) => ({
src: `https://picsum.photos/400/300?random=${i + 1}`,
alt: `pic-${i + 1}`
}))
}
}
}
</script>

<style scoped>
.demo {
max-width: 800px;
margin: 0 auto;
}
.u-img {
display: block;
width: 100%;
height: 300px;
object-fit: cover;
margin-bottom: 20px;
background: #f5f5f5;
}
.u-bg {
width: 100%;
height: 300px;
background-size: cover;
background-position: center;
}
</style>

代码分割(拆包)

可以将一些大文件分割成一些小一点的文件,加快请求速度,但是不要分割太细了,分割太细了反而增加了请求数量得不偿失

浏览器有6-8个请求通道,如果因为一个大一点的文件影响了请求就可以考虑代码分割了,具体大小需要根据实际的服务器带宽、用户的网络等多种因素决定

代码分割不是万能的,有些文件就是拆不了的,不要太执着

代码

vue.config.js

1
2
3
4
5
6
7
8
9
10
// configureWebpack中加入
// 代码分割配置
config.optimization = {
...config.optimization,
splitChunks: {
chunks: 'all',
minSize: 20 * 1024, // 小于多少不再分割
maxSize: 200 * 1024, // 大于多少进行分割
}
}

图片压缩

将图片进行压缩,使用一些第三方的库可以压缩图片,但是很多库下载都缺少各种文件,而且很多还下载不下来,最终压缩的大小也一般,大图考虑在设计那边下载小一点的图片,或者使用在线的压缩网站手动压缩,小图转base64直接加载

转base64是为了加快请求速度,但是base64对应稍微大点的图片反而会增加大小,建议不要超过10kb

迅捷:https://yasuo.xunjiepdf.com/img/

代码

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
// chainWebpack中加入
...
// 小图转base64
config.module
.rule("images")
.test(/\.(png|jpe?g|gif|ico)$/i)
.type("asset")
.parser({
dataUrlCondition: {
maxSize: 4 * 1024, // 4kb
},
});

删除打印

通常打印只是开发阶段使用,生产阶段不需要,或者是说稳定运行的版本不需要

代码

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引入terser-webpack-plugin,脚手架以及有这个了不需要单独安装
const TerserPlugin = require('terser-webpack-plugin')

// configureWebpack中加入

/** 删除console.* 函数调用 */
config.optimization.minimizer = [
new TerserPlugin({
terserOptions: {
compress: {
// drop_console: true, // 删除所有的 console 语句
pure_funcs: ['console.log', 'console.error'], // 移除指定的函数调用
drop_debugger: true,
}
}
})
]

其他方案

  • 服务器设置静态资源缓存,cdn加速等
  • HTTP/2 + Server Push 多文件 并行加载
  • 字体抽离,例如某些字体只使用了几个字,可以找一些工具进行抽离
  • 或者安装一些工具查看体积,按需优化
    • webpack:webpack-bundle-analyzer
    • vite:rollup-plugin-visualizer

如果都折腾完了还不行就花点钱升级服务器吧

本文作者:辰風依恛
本文链接:https://766187397.github.io/2025/10/12/Vue2%E6%89%93%E5%8C%85%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可
×