vue.config.js文件配置
const CompressionPlugin = require('compression-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const { HashedModuleIdsPlugin } = require('webpack')
const isProduction = process.env.NODE_ENV === 'production'
// cdn预加载使用
const externals = {
'element-ui': 'ELEMENT',
'element-ui/lib/theme-chalk/index.css': 'ELEMENT',
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
echarts: 'echarts'
}
const cdn = {
css: [
'https://cdn.jsdelivr.net/npm/element-ui@2.13.2/lib/theme-chalk/index.css'
],
js: [
'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js',
'https://cdn.jsdelivr.net/npm/vue-router@3.4.5/dist/vue-router.min.js',
'https://cdn.jsdelivr.net/npm/vuex@3.0.1/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/element-ui@2.13.2/lib/index.js',
'https://cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js',
'https://cdn.jsdelivr.net/npm/echarts@5.1.2/dist/echarts.min.js'
]
}
module.exports = {
// 打包是否生成.map文件
productionSourceMap: false,
lintOnSave: false,//设置是否在开发环境下每次保存代码时都启用 eslint验证。
publicPath: './',//静态资源配置统一的资源标识符vue.config.js文件配置
assetsDir: 'static',//放置生成静态资源的目录
chainWebpack: config => {
config.optimization.delete('splitChunks')
config.plugins.delete('preload')
},
configureWebpack: config => {
const plugins = []
if (isProduction) {
// 公共代码抽离
// 优化项配置
config.optimization = {
splitChunks: { // 分割代码块
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name (module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`
}
}
}
}
}
plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false // 去掉注释
},
warnings: false,
compress: {
drop_console: true,
drop_debugger: false,
pure_funcs: ['console.log']// 移除console
}
}
})
)
// 服务器也要相应开启gzip
plugins.push(
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css)$/, // 匹配文件名
threshold: 10000, // 对超过10k的数据压缩
deleteOriginalAssets: false, // 不删除源文件
minRatio: 0.8 // 压缩比
})
)
// 用于根据模块的相对路径生成 hash 作为模块 id, 一般用于生产环境
plugins.push(
new HashedModuleIdsPlugin()
)
// plugins.push(
// new BundleAnalyzerPlugin()
// )
// 取消webpack警告的性能提示
config.performance = {
hints: 'warning',
// 入口起点的最大体积
maxEntrypointSize: 1000 * 500,
// 生成文件的最大体积
maxAssetSize: 1000 * 1000,
// 只给出 js 文件的性能提示
assetFilter: function (assetFilename) {
return assetFilename.endsWith('.js')
}
}
}
// 打包时npm包转CDN
config.externals = externals
return { plugins }
},
pages: {
index: {
entry: 'src/main.js',
template: 'public/index.html',
filename: 'index.html',
title: '无限科技公司',
chunks: ['chunk-vendors', 'chunk-common', 'index'],
cdn
}
},
css: {
extract: isProduction ? {
ignoreOrder: true
} : false
}
}
compression-webpack-plugin插件(webpack)
const CompressionPlugin = require('compression-webpack-plugin')
//安装
npm install compression-webpack-plugin --save-dev
//打包的时候开启gzip可以很大程度减少包的大小,非常适合于上线部署。更小的体积对于用户体验来说
就意味着更快的加载速度以及更好的用户体验。
uglifyjs-webpack-plugin
此插件使用uglify-js进行js文件的压缩
该模块需要的环境: node 6.9.0 webpack 4.0.0 版本以上。
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
npm install uglifyjs-webpack-plugin --save-dev
使用:
然后将该插件添加到你工程webpcak的config。例如:
webpack.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
//...
optimization: {
minimizer: [new UglifyJsPlugin()]
}
};
webpack-bundle-analyzer
webpack-bundle-analyzer打包文件分析工具
npm install webpack-bundle-analyzer –save-dev
配置
//在build/webpack.prod.config.js中的module.exports = webpackConfig这句话的上面增加
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
默认配置,一般不用修改
new BundleAnalyzerPlugin({
// 可以是`server`,`static`或`disabled`。
// 在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
// 在“静态”模式下,会生成带有报告的单个HTML文件。
// 在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
analyzerMode: 'server',
// 将在“服务器”模式下使用的主机启动HTTP服务器。
analyzerHost: '127.0.0.1',
// 将在“服务器”模式下使用的端口启动HTTP服务器。
analyzerPort: 8888,
// 路径捆绑,将在`static`模式下生成的报告文件。
// 相对于捆绑输出目录。
reportFilename: 'report.html',
// 模块大小默认显示在报告中。
// 应该是`stat`,`parsed`或者`gzip`中的一个。
// 有关更多信息,请参见“定义”一节。
defaultSizes: 'parsed',
// 在默认浏览器中自动打开报告
openAnalyzer: true,
// 如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
generateStatsFile: false,
// 如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
// 相对于捆绑输出目录。
statsFilename: 'stats.json',
// stats.toJson()方法的选项。
// 例如,您可以使用`source:false`选项排除统计文件中模块的来源。
// 在这里查看更多选项:https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
statsOptions: null,
logLevel: 'info' // 日志级别。可以是'信息','警告','错误'或'沉默'。
})
使用:
npm run build —report
启动后的命令窗口效果如下:
浏览器打开127.0.0.1:8888地址可以看到一下效果图:
webpack打包工具
① Webpack的运行需要依赖Node.js,因此需要先安装Node.js。
中文网下载地址:http://nodejs.cn/download/
安装完成之后在命令行窗口输入下面两行命令,若有出现版本号则安装成功。
node -v
npm -v
② 接着就可以通过npm(一个基于Node.js的包管理工具)来安装Webpack咯~
首先,通过下面的一行命令先生成package.json
npm init
③ 接着就能通过npm完成webpack的安装啦
因为npm的源在国外,所以安装速度可能比较慢。建议大家可以用淘宝的npm镜像。但是要注意的一点是,淘宝npm镜像中有一些包会不太一样(一般来说不影响使用)
淘宝 NPM 官网:https://npmmirror.com/
通过下面这行代码即可完成cnpm的配置
npm install -g cnpm --registry=https://registry.npm.taobao.org
通过下面两行代码即可完成webpack的安装
// 要是不想使用淘宝的npm镜像,就用npm代替cnpm
cnpm install -D webpack
cnpm install -D webpack-cli
④ 安装完之后在package.json同级目录新建一个webpack.config.js文件。然后再在package.json中增加一个“scripts”配置项。
// 这是package.json
{
"name": "zhihu_demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/rehellinen/zhihu_demo.git"
},
"author": "",
"license": "ISC"
}
接着在命令行中输入下面的这行代码就可以启动webpack啦。
npm run build
publicPath 属性
这个配置的作用是在开发阶段和生产阶段,为静态资源配置统一的资源标识符。
绝对和相对之分
在讲配置前,首先来明确两个概念绝对路径 ‘/‘ 和 相对路径 ‘./‘。这两个概念的区别就是以哪个目录作为起点,可以理解为绝对路径就是绝对错不了的路径,始终以一个固定的目录作为起点。而相对路径容易错,可以是任意一个目录节点作为起点。而在网站的访问中,可以认为如果静态资源使用了绝对路径,那么他始终以当前这个网站来作为访问的根节点。
在 vue.config.js 中,publicPath 既可配置成绝对路径 ,也可以配置成相对路径。
绝对路径
publicPath: '/',
// 或带有二级路径的网站, 如访问地址为 http://my-app.com/sub/
publicPath: '/sub/'
相对路径
publicPath: './',
// 或者
publicPath: '.',
// 或者
publicPath: ''
关于publicPath
打开 @vue/cli 的源码 在 packages/@vue/cli-service/lib/options.js 文件下找到了答案。
可以看到脚手架默认赋予的 publicPath 的值为 ‘/‘,也就是说 @vue/cli 默认使用的是绝对路径。
关于 router 中的 base 参数
除了publicPath 这个参数外,还有一个与之有紧密关系的参数就是 router 当中的 base 。
打开 vue-router 的官方文档可以看到对应的解释,base 是应用的基路径,即当前应用的所有的路由地址,都会被自动拼接上这个基路径,一般应用在部署有二级目录的网站上。
router/index.js 中 base 的 默认值为 process.env.BASEURL,那么这个初始值是什么呢?继续翻找 @vue/cli 的源码,在以下文件中找到了答案
目录:packages/@vue/cli-service/lib/config/base.js
此文件是 @vue/cli 对 webpack 的配置文件,可以看到脚手架中的环境变量来自于 util/resolveClientEnv 这个工具函数,然后调用 webpack.DefinePlugin 的插件,将这些环境变量生成最终可访问的常量
目录:packages/@vue/cli-service/lib/util/resolveClientEnv.js
此文件是 @vue/cli 用来生成环境变量的工具函数,可以看到 BASE_URL 的 值来自 getBaseUrl 这个函数,而参数 options 就是我们的 vue.config.js 所导出的 json 文件。此外还可以看到在脚手架中默认只有 VUE_APP开头、NODE_ENV、BASE_URL 这三种环境变量才可以被 @vue/cli 所识别。
目录:packages/@vue/cli-service/lib/util/.js
可以看到当 publicPath 被设置为 ‘auto’ 后其值是 ‘’,而其他情况下,这个值就是我们所配置的publicPath,那基本可以理解为,publicPath 和 router中的base 参数,在实际项目的开发中理应是同一个值。
而这里单独处理 ‘auto’ ,是为了适配 webpack,因为 webpack 的 output.publicPath 可以设置为 ‘auto’ 并有其自己的处理逻辑。但一旦使用了 ‘auto’ 这个值,在启动 serve 后,auto 就会被当成二级路径比如 http://localhost:8080/auto/,所以 @vue/cli 要在这里强制将其置空。
这个问题的讨论地址:[https://github.com/vuejs/vue-…]
(https://github.com/vuejs/vue-…)
webpack 关于 publicPath 的文档:https://webpack.docschina.org/configuration/output/#outputpublicpath
publicPath 分别在绝对和相对路径下应该怎么用。
publicPath: ‘/‘,使用绝对路径,即默认配置
开发环境
生产环境
结论:使用默认配置,无论是在开发阶段还是生产阶段,静态资源都会被挂上 ‘/‘,这样静态资源就会以当前网站服务作为根节点,然后加载相应的静态资源。
这时在本地静态访问
而这样打出来的 dist/index.html 也是无法直接访问的,因为使用了绝对路径,当直接使用浏览器打开时,会直接去到根目录的盘符下作为访问的根路径,导致静态资源加载失败。当然生产包打出来一般都会使用服务器进行部署,因为大多数应用还有和后台接口的交互,必须要通过服务器启动后才能正常访问。
那么有没有不用服务器就能启动进行访问的么?这时候你可能需要的就是相对路径。
publicPath: ‘./‘,使用相对路径
开发环境
生产环境
结论:使用相对路径,可以看到只是比绝对路径在静态资源访问上少了 ‘/‘,这样的优点就是可以让当前应用被部署在任意目录,而不必担心静态资源有丢失的情况。比如本地访问。
可以看到使用相对路径进行打包后,直接本地访问 index.html,静态资源都是加载成功的,页面上显示了一部分东西,但是 home 页面的内容却丢失了。这其实是因为我们使用的路由模式是 history,虽然资源加载没有问题,但是因为地址栏路径与前端设置的访问路由不一致,导致前端路由不能正确加载,因此页面不能正确显示。
把路由修改为 hash 模式。
publicPath: ‘/sub/‘,采用二级路径 (也是绝对路径)
结论:可以看到,二级路径其实也是绝对路径,只不过在所有静态资源前统一拼接上了 ‘/sub/‘。这种配置可以在同一个域下部署不同的项目,依靠二级路径来做到逻辑上的划分。
为什么当网站配置了二级访问路径,前端也要加上相应资源标识符呢?
当使用绝对路径时,所谓根路径,其实是以当前 域名+端口号后面的位置开始的。如以http://localhost:8080/test 这个网址为例,假如此时前端配置的 publicPath: ‘/‘,即默认值。那么打出来的前端静态资源在被加载时,就会变成 http://localhost:8080/js/xxxx…,因为端口后才是当前网站的根目录。如此一来,静态资源在物理层面已经无法被访问到了,导致资源加载失败,页面白屏等。
所以当网站设置了二级目录时,前端这时候就需要注意了,可以有不同的解决方案防止白屏。
- 设置带有二级目录的绝对路径,publicPath: ‘/test/‘,这样打包出来的静态资源可以和当前网站的配置保持一致,可以避免资源加载失败。
- 设置相对路径,publicPath: ‘./‘。但是这样一来,如果此时使用的是 history 模式的路由配置,因为网站加了二级目录会导致前端的路由地址匹配失败,所以还需要将 router 的 base 设置为 ‘/test/‘ 需要与网站的二级目录保持一致。或者直接采用 hash 的路由模式,这样可以忽略掉 base 参数的配置,比较省心。
关于vue-cli 的官方文档对 publicPath 字段的解释
这个值在开发环境下同样生效。如果你想把开发服务器架设在根路径,你可以使用一个条件式的值:
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/production-sub-path/'
: '/'
}
总结
官方文档说的很清楚,总结以下几种配置模式方便记忆:
- 傻瓜模式:路由采用 hash 模式,publicPath 采用相对路径 ‘./‘ 模式,此种组合配置操作简单,可以部署到任意路径上,并且能支持二级目录,比较省心。即便是直接访问打包后的 index.html 也能看到效果,应用的场景可能如下:
- 一个纯前端应用,不需要和后台的接口进行交互,支持本地静态展示。
- 只是用来部署到 github gh-page 分支下的纯前端展示应用。
- 如使用 vue 开发的 h5 项目,然后使用 hbuilder 进行打包的 hybird app。
- 部署在带有二级路径的网站上。
- 进阶模式:路由采用 history 模式,publicPath 采用绝对路径 ‘/‘ 模式,此这种模式需要服务器来对前端资源进行部署。同时,还需要在服务器上配置404重定向到 index.html。
- 升级模式:路由采用 history 模式,publicPath 采用了 ‘/sub/‘ 二级路径,相应的 router base 参数也应该设置为 ‘/sub/‘ (通常使用脚手架默认提供的 process.env.BASE_URL 即可)。此种模式即在多个项目同时部署到一个域名和端口下时,采用不同的二级路径来对项目进行逻辑上的划分。与进阶模式的服务部署方式相同。
Vue中使用cdn加载资源
搭建vue的时候,一般都使用vue官方推荐的命令行工具。需要打包的时候,默认会把所有代码合并生产新文件,其中包括各种库,就会导致打包出来很大。如果使用cdn的话,会更利于程序的加载速度。
在Vue项目中,引入到工程中的所有js、css文件,编译时都会被打包进vendor.js,浏览器在加载该文件之后才能开始显示首屏。若是引入的库众多,那么vendor.js文件体积将会相当的大,影响首开的体验。
解决方法是,将引用的外部js、css文件剥离开来,不编译到vendor.js中,而是用资源的形式引用,这样浏览器可以使用多个线程异步将vendor.js、外部的js等加载下来,达到加速首开的目的。
外部的库文件,可以使用CDN资源,或者别的服务器资源等。
下面,以引入vue、vuex、vue-router为例,说明处理流程。
一、资源引入
在index.html中,添加CDN资源,例如bootstrap上的资源:
<body>
<div id="app"></div>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
<script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
</body>
二、添加配置
在bulid/webpack.base.conf.js文件中,增加externals,将引用的外部模块导入,如下:
module.exports = {
entry: {
app: './src/main.js'
},
externals:{
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex':'Vuex'
}
注意一点:
格式为 ‘aaa’ : ‘bbb’, 其中,aaa表示要引入的资源的名字,bbb表示该模块提供给外部引用的名字,由对应的库自定。例如,vue为Vue,vue-router为VueRouter.
三、去掉原有的引用
去掉import,如:
// import Vue from 'vue'
// import Router from 'vue-router'
去掉Vue.use(XXX),如:
Vue.use(Router)
测试
重新npm run build,会看到 vendor.js体积有所下降了。我自已的个人主页中,将所有的外部模块,使用CDN引入之后,vendor.js从1M,降为30k左右。
通过开发者模式的Network工具,可以看到vue.js、vuex.js、vendor.js等文件会分别由一个线程进行加载。且因为使用了CDN,减轻了带宽压力。