让你明明白白学知识,有代码,有讲解,抄的走,学的会!
什么是cdn?
cdn是一套内容分发服务,其本质是将一些公共资源缓存到离用户最近的服务器上,减少直接从数据源服务器获取资源的响应时间。 我们使用公共的cnd资源,可以极大的减轻对服务器的压力,实质是减少了对我们自己服务器的http请求
使用cdn前后对比
在没有使用cnd的情况下,假设某个项目使用到如下的技术栈
- vue
- vuex
- vue-router
- echarts
- axios
- .... 等
我们一次性引入,不做按需加载的情况下,资源大小
总共资源 16.8M, 还是比较大的,目前这个没有加入 babel的编译成ES5代码的情况下,已经这么大了
上面这个是开发环境, 没有Tree-Shaking 的情况下,总共有16.8M这么大的资源需要加载;
如果不使用CDN, 在生产环境下,使用 Tree-Shaking 剔除冗余代码,代码体量会微微比 16.8M小一些,不过也不会有太明显的变化,因为,那些依赖包 vue、vuex等依旧是被打包进 chunk-vendor中了
main.js
import Vue from 'vue'
import App from './App.vue'
import ELEMENT from 'element-ui'
import "element-ui/lib/theme-chalk/index.css";
import axios from 'axios'
import * as echarts from 'echarts';
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import router from './router'
Vue.use(ELEMENT)
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.prototype.$http = axios
Vue.prototype.$echarts = echarts
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router
}).$mount('#app')
生产环境下,没有使用CDN的火焰图, vue-cli 3脚手架自动开启了tree-shaking
开启CDN加速
我们要将 vue、 vue-router、 vuex、element-ui 从 vendor.js 中分离出来,使用CDN资源引入。国内的CDN服务推荐使用 BootCDN
我们需要修改以下几个地方 vue-cli 3脚手架,需要在项目根目录新建一个 vue.config.js 文件,导出我们的webpack配置
vue-cli 2脚手架,需要添加对应的配置,其本质都是一样的,只不过vue-cli3 将一些配置隐藏了
vue.config.js
module.exports = {
configureWebpack: {
externals: {
// 排除一些包,不会打包进vendor中
// 左侧为我们在业务中引入的包名, 右侧 为对应库提供给外部引用的名字
"vue": "Vue",
"vue-router": "VueRouter",
"vuex": "Vuex",
"axios": "axios",
"element-ui": "ELEMENT",
"echarts": "echarts"
}
}
}
左侧的的名字 是我们 import elementUI from 'element-ui' 包的名字; 右侧的 “ELEMENT” 是每个包根据 UMD 规范,导出的名称。
UMD怎么查看对外暴露给浏览器使用的包名
以element-ui 为例 这是 cdn的地址
!(function(e, t) {
// t就是实际的代码
// 判断是不是node 使用 CommonJS规范
"object" == typeof exports && "object" == typeof module ?
// 是CommonJS-直接引入包
module.exports = t(require("vue")) :
// 不是 CommonJS,再判断AMD
"function" == typeof define && define.amd ?
// 是AMD的加载方式,t就是具体包的自执行函数
define("ELEMENT", ["vue"], t) : "object" == typeof exports ? exports.ELEMENT = t(require("vue")) :
// 不是上面2重,原生,全局加载 挂载到window对象上
e.ELEMENT = t(e.Vue)
}
)("undefined" != typeof self ? self : this, function somepackage(e) {// 。。。 do something
})
// ELEMENT 就是暴力给浏览器用的名字
我们只需要将前面的一部分代码拎出来看,具体某个somepackage包的实现 不用管
现在我们是cdn,那么就需要找到浏览器能够识别的方式去加载我们的依赖包
有的库是明确告诉你 使用CDN 对外爆露的包名是什么
其他的包名,都是类似的方式找
需要修改的文件
因为引入的包名和 该包对外暴露的名字一致,所以,不需要修改什么,下面就将包的css注释掉即可 main.js
import Vue from 'vue'
import App from './App.vue'
import ELEMENT from 'element-ui'
// 注释,这个已经通过CDN 加载了
// import "element-ui/lib/theme-chalk/index.css";
import axios from 'axios'
import * as echarts from 'echarts';
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import router from './router'
Vue.use(ELEMENT)
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.prototype.$http = axios
Vue.prototype.$echarts = echarts
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router
}).$mount('#app')
修改 public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<!--别忘记引入element-ui的css资源-->
<link href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/theme-chalk/index.css" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/index.js"></script>
</body>
</html>
这里直接使用bootcdn 提供的线上资源
下图是 开发环境加载的截图,明显 总体资源的包体积缩小,这还没有开启GZip压缩和使用tree-shaking的状态
整体资源 3.1M
生产环境,使用cdn, 有使用tree-shaking, 无gzip压缩的截图
整体资源 1.8M ,chunk-vendor 25.7k
打包,生成焰火图
vue-cli3 脚手架内置了 webpack-bundle-analyzer
vue-cli4+ 及以上版本 见下面的单独设置
在打包之前,我先将package.json中的 dependencies包从 node_modules中删除
npm uninstall axios echarts element-ui vue vue-router vuex --save
在 package.json 中添加一行 命令
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
// 新增这个
"analyze": "vue-cli-service build --report"
},
运行,生成焰火图, 会打包到dist目录下
npm run analyze
要查看焰火图,必须以一个服务的形式运行,也就是 http://localhost:8080/ 这种,不能是 file:// xxx/report.html 这种file协议
推荐使用 live-server 这个包
// 全局安装
npm i live-server -g
// 切换到dist目录
cd dist
live-server report.html
如果开启GZip压缩以后,文件体积会更小
只有整体资源只有11k左右
nginx 部署,开启GZip压缩
将代码打包 , 将dis下面文件都拷贝到 nginx指定目录
window下启动 nginx
// 打开 cmd ,直接nginx 没有输出,就是最好的输出,那就是启动成功了
nginx
这是没有开始 GZip压缩的样子
开启以后
源文件4.4k, 压缩以后 2.2k
nginx 设置
http {
gzip on;
gzip_static on;
gzip_min_length 1024;
gzip_buffers 4 16k;
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;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
}
如果你要觉得哪种类型的文件没有被压缩,那可能就是 gzip_types 没有配置对, 还有gzip_comp_level压缩等级也是需要关注的,压缩等级越高,说明服务器需要耗费的时间就越长; 太高的压缩等级没有什么意义,根据实际的需求,取一个差不多的值,即可
修改完nginx配置以后,一定要重启; 然后 window上启动nginx以后,发现停不了,请使用 ‘任务管理器’ 手动在 ‘详细信息’ 里面 结束任务, linux中不存在这种情况
在线网站查看是否已经开启了Gzip压缩 这个只能是线上的,不能是本地的,除非有 内网穿透工具,将本地的服务映射成外网,也可以使用
vue-cli4, vue-cli5 开启 analyzer
# 运行
npx vue-cli-service -help
发现没有 vue-cli3 脚手架中的 --report 选项了
analyzer 本质是一个webpack 插件, 手动配置下
安装依赖
npm i cross-env webpack-bundle-analyzer -D
# 顺便标注下我演示的几个核心版本
vue-cli-service: 4.4.6
"cross-env": "^7.0.3",
"webpack-bundle-analyzer": "^4.5.0"
package.json增加脚本
{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"analyzer": "cross-env NODE_ENV=analyzer vue-cli-service build"
}
}
这里增加 NODE_ENV=analyzer 可以根据自身需求调整 vue.config.js 中 chainWebpack 的场景判断
修改vue.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
/**
* 静态CDN文件
*/
const assetsCDN = {
externals: {
// 左侧key为项目中引入的名称,右侧value为包对外提供的名称
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
'element-ui': 'ELEMENT'
},
css: [
'//cdn.jsdelivr.net/npm/element-ui@2.15.0/lib/theme-chalk/index.css'
],
js: [
'//cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router@3.4.9/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex@3.6.0/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/echarts@5.3.0/dist/echarts.min.js',
'//cdn.jsdelivr.net/npm/element-ui@2.15.0/lib/index.js'
]
}
function injectCDN(config) {
// 非生产环境,不启用CDN
if(process.env.NODE_ENV !== 'production') return
config.set('externals', assetsCDN.externals)
config.plugin('html').tap(args => {
args[0].cdn = assetsCDN
return args
})
}
module.exports = {
chainWebpack(config) {
injectCDN(config)
}
}
修改public/index.html
<!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的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
<% } %>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
注意事项:
- index.html 中增加的注入js的脚本代码,不能放在 body 标签后面, 需要放在body标签里面