一、前言
个人网站最近上线了,欢迎大家访问
苏浩的个人博客
写过几个vue的案例也好,项目也好都没有真正上线, 就连公司的项目都是直接打包给后台统一部署, 因此打包相关经验比较少, 大学一直在做的一个个人博客在部署到阿里服务器以后访问一直都很慢(学生机带宽只有1M), 之前听别人说过可能是DNS劫持, 仔细研究发现其实并没有, 在控制台能看到一直在请求各种文件, 图片, js, css, 图标等等, 加载时间竟然达到了60s, 实在难以忍受, 因此研究了一下打包以及优化, 压缩以后加载时间达到1.9s - 2.7s(图片背景占据1s左右)
二、着手点
仔细看了一下加载的文件, 有个接近1M的favicon.ico, 和大量图片, 还有就是体积庞大的js文件了, 加起来体积接近6M, 实在难以想象…
下面是修改明细
1. 图片和favicon.ico进行压缩
图片在线压缩, 可以通过这个进行压缩, 如果没有特殊要求, 一般可以压缩到60%,经过压缩以后图片只有16k, 其余图片也只有几十k, 基本达到效果, 压缩过度图片就会模糊
2. 相关依赖转用CDN
打包以后体积比较大的也就是chunk-vendors.c5b1fb2f.js这个文件了, 也就是各种第三方js都被打进这个包里, 因此体积达到了2M左右(之前是因为熟悉Element, 又觉得iview更好看, 因此引入了两个第三方UI库, 作死…后来又全部换成了element), 而element-ui或者其他第三方库其实不必打包, 只需要在html头部引入CDN地址即可, 开源CDN很多, 比如BootCDN等
修改webpack配置文件, 我这里用的是vue-cli4, 因此webpack的配置也细化到vue.config.js里面了, 通过查询vue-cli4配置文档发现可以通过配置configureWebpack来进行配置, 代码如下
configureWebpack: config => {
let externals = {};
if (process.env.NODE_ENV === "production") {
externals = {
vue: "Vue",
"vue-router": "VueRouter",
vuex: "Vuex",
axios: "axios",
echarts: "echarts",
"element-ui": "ELEMENT",
lodash: "_",
nprogress: "NProgress",
qs: "Qs",
turndown: "TurndownService",
wangeditor: "wangEditor",
swiper: "Swiper",
};
}
config.externals = { ...config.externals, ...externals };
}
在index.html还需要加入你排除依赖的CDN地址, 不然项目运行无法引入相关依赖, 这些依赖都是我项目真实引用的
<!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" />
<title><%= htmlWebpackPlugin.options.title %></title>
<% if (process.env.NODE_ENV !== 'development') { %>
<!-- ElementUI -->
<link
href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.0/theme-chalk/index.min.css"
rel="stylesheet"
/>
<!-- NProgress -->
<link
href="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css"
rel="stylesheet"
/>
<!-- wangEditor -->
<link
href="https://cdn.bootcdn.net/ajax/libs/wangEditor/3.1.1/wangEditor.min.css"
rel="stylesheet"
/>
<!-- swiper -->
<link href="https://cdn.bootcdn.net/ajax/libs/Swiper/5.4.3/css/swiper.min.css" rel="stylesheet">
<% }%>
</head>
<body>
<noscript>
<strong
>We're sorry but blog doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong
>
</noscript>
<div id="app"></div>
<% if (process.env.NODE_ENV !== 'development') { %>
<!-- Vue -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
<!-- ElementUI -->
<script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.0/index.js"></script>
<!-- Vue Router -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.4.8/vue-router.min.js"></script>
<!-- Vuex -->
<script src="https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js"></script>
<!-- axios -->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js"></script>
<!-- echarts -->
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts-en.min.js"></script>
<!-- lodash -->
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.core.min.js"></script>
<!-- NProgress -->
<script src="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
<!-- qs -->
<script src="https://cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.min.js"></script>
<!-- turndown -->
<script src="https://cdn.bootcdn.net/ajax/libs/turndown/6.0.0/turndown.min.js"></script>
<!-- wangEditor -->
<script src="https://cdn.bootcdn.net/ajax/libs/wangEditor/3.1.1/wangEditor.min.js"></script>
<!-- swiper -->
<script src="https://cdn.bootcdn.net/ajax/libs/Swiper/5.4.3/js/swiper.min.js"></script>
<% }%>
</body>
</html>
加入这段代码后, 在进行打包发现体积较少非常明显
有人可能会有疑问, 不需要排除main,js以及其他文件中对这些模块的引用吗?其实并不需要, 因为webpack会自动查找相关的引用, 并自动屏蔽, 比如import Vue from ‘vue’, 而在配置文件 -> externals有vue这一项, 因此import语句不会从node_modules中引入相关依赖, 也就没必要注释
可能还有同学有疑问, 配置的externals都是从哪里找的对应关系, 其实很简单, 前面的值就是你通过import引入的名, 也就是依赖名, 而后面的就是对应依赖抛出的全局对象, 拿element举例, 在cdn地址https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js中抛出一个全局对象ELEMENT, 那就是这个依赖的全局对象了, 就作为后面的值.
其余依赖都是类似
3. 使用gzip压缩
没使用nginx可以忽略这一步
通过以上两步修改以后体积减少了大概60%以上, 但是第三方js还是体积过大, 因此采用gzip进行压缩, 其实gzip压缩就和压缩文件类似, 需要拷贝给别人的时候打成一个压缩包, 别人用的时候在解压出来, 我们只需要通过相关插件将js文件打包成gz格式, 上传到服务器, 等需要的时候下载下来, 前端进行解析就可以, 而浏览器正好可以直接解析这种格式的文件
修改刚才的配置文件, 加入以下部分
const CompressionPlugin = require("compression-webpack-plugin");
configureWebpack: config => {
let plugins = [];
if (process.env.NODE_ENV === "production") {
plugins.push(
new CompressionPlugin({
test: /\.js$|\.html$|\.css/,
threshold: 10240,
deleteOriginalAssets: true
})
);
}
config.plugins = [...config.plugins, ...plugins];
}
需要安装一个依赖
npm i compression-webpack-plugin -S
然后在进行打包, 几百k的js就会压缩到几十k, 但是玩好gzip压缩, 还需要服务器支持
修改nginx配置文件nginx/conf/nginx.conf
# 在对应server加入以下代码
gzip on;
gzip_static on;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript ;
然后重启nginx服务器即可,
最终效果:
请求时间减少到1.9s左右, 因为我是登录页面, 有个背景图片, 请求时间在1.2s, 如果没有要求, 可以采用纯色背景, 打开时间会更快,
网站还在开发, 欢迎大家访问,并提出宝贵意见Rambler_Designer
三、附件(vue.config.js全文件)
const defaultSettings = require("./src/settings.js");
const path = require("path");
const port = process.env.port || 8080;
const CompressionPlugin = require("compression-webpack-plugin");
function resolve(dir) {
return path.join(__dirname, dir);
}
module.exports = {
publicPath: "./", //打包后静态资源的相对路径,build的时候改成./
// 在开发环境下eslint-loader 会将 lint 错误输出为编译警告
lintOnSave: process.env.NODE_ENV === "development",
// 关闭生产环境的 sourceMap
productionSourceMap: false,
// 将webpack部分配置提取出来,如entry和template等
pages: {
index: {
// 入口
entry: "src/main.js",
// 模板来源
template: "public/index.html",
// 在 dist/index.html 的输出
filename: "index.html",
// 当使用 title 选项时,
// template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: defaultSettings.title,
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ["chunk-vendors", "chunk-common", "index"]
}
},
devServer: {
port: port,
open: true,
// 让浏览器 overlay 同时显示警告和错误(显示遮罩层并提示错误)
overlay: {
warnings: false,
errors: true
},
// 开发环境的代理
proxy: {
[process.env.VUE_APP_BASE_API]: {
target: `http://152.136.112.189:9999`,
// target: `http://localhost:9999/`,
changeOrigin: true,
pathRewrite: {
["^" + process.env.VUE_APP_BASE_API]: ""
},
headers: {
host: "http://152.136.112.189:9999",
origin: "http://152.136.112.189:9999"
// host: "http://localhost:9999/",
// origin: "http://localhost:9999/"
}
}
}
},
css: {
loaderOptions: {
// 全局引入stylus变量, 通过main.js引入不生效
stylus: {
import: "~styl/variable.styl"
}
},
// css开启sourceMap, 即不压缩,不加密,开发时明确知道哪里有问题
sourceMap: true
},
// webpack相关配置
chainWebpack: config => {
// config
// // https://webpack.js.org/configuration/devtool/#development
// .when(process.env.NODE_ENV === 'development',
// config => config.devtool('source-map')
// )
config.resolve.alias
.set("styl", resolve("src/styles"))
.set("assets", resolve("src/assets"));
if (process.env.NODE_ENV === "development") {
config.devtool("source-map");
}
},
// 打包相关配置
configureWebpack: config => {
let plugins = [];
let externals = {};
if (process.env.NODE_ENV === "production") {
plugins.push(
new CompressionPlugin({
test: /\.js$|\.html$|\.css/,
threshold: 10240,
deleteOriginalAssets: true
})
);
externals = {
vue: "Vue",
"vue-router": "VueRouter",
vuex: "Vuex",
axios: "axios",
echarts: "echarts",
"element-ui": "ELEMENT",
lodash: "_",
nprogress: "NProgress",
qs: "Qs",
turndown: "TurndownService",
wangeditor: "wangEditor",
swiper: "Swiper",
};
}
config.plugins = [...config.plugins, ...plugins];
config.externals = { ...config.externals, ...externals };
}
};
欢迎大家指导 !!!