由于vue-cli3及以上版本将webpack的基础配置全部内嵌了,这就导致我们初始化项目完成之后发现原先的webpack的config配置全部都消失不见了,vue-cli3预留了一个 vue.config.js 的js文件供我们对webpack进行自定义配置,参考 vue.config.js官方配置指南
转载一篇 有关于 webpack 配置的 文章 https://presentations.survivejs.com/webpack-the-good-parts/#/3
1、在项目根目录下新建 vue.config.js 文件与package.json同级
这个文件应该导出一个包含了选项的对象:
// vue.config.js
module.exports = {
// 选项...
}
baseUrl -- publicPath -- 从 Vue CLI 3.3 起已弃用,请使用publicPath
。
// vue.config.js
module.exports = {
/* 部署生产环境和开发环境下的项目路径:可对当前环境进行区分,baseUrl 从 Vue CLI 3.3 起已弃用,要使用publicPath */
/* 如果是部署在根路径,那么可以直接使用 默认值 ‘/’ ,也可以被设置为空字符串 ('') 或是相对路径 ('./'),这样所有的资源都会被链接为相对路径,这样打出来的包可以被部署在任意路径 */
/* baseUrl: process.env.NODE_ENV === 'production' ? '/' : './' */
publicPath: process.env.NODE_ENV === 'production' ? '' : './',
/* 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,例如 https://www.my-app.com/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.my-app.com/public/,则设置 publicPath 为 /public/。 */
publicPath: process.env.NODE_ENV === 'production' ? '/public/' : '/',
}
// vue.config.js
module.exports = {
/* 输出文件目录:在npm run build时,生成文件的目录名称 默认是dist */
/* 当我在build 时,若是区分了不同环境,那么我可能会需要针对不同环境的 build ,需要一个不同的包名 */
/* 如果build 的是测试环境,也就是运行的是 "build:test": "vue-cli-service build --mode test" 这个脚本命令 ,那么我希望我的包名是 dist_test*/
/* 那么我可以在 .env.test 这个环境模式中,增加一个变量,用来定义不同环境下的包名,然后 使用这个变量来定义输出的包名 */
outputDir: process.env.OUT_PUT_DIR,
}
// .env.test 模式
NODE_ENV = 'test'
VUE_APP_TITLE = '2'
OUT_PUT_DIR = dist_test //包名变量
// vue.config.js
module.exports = {
/* 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录 */
/* 一般会将这些静态文件,放置在 src 文件夹下的 assets 文件夹下 */
assetsDir: "assets",
}
每个“page”应该有一个对应的 JavaScript 入口文件。其值应该是一个对象,
1、对象的 key 是入口的名字,也就是 index 。
2、value 存在两种情况
entry
, template
, filename
, title
和 chunks
的对象 (除了 entry
之外都是可选的);entry
的字符串通常情况下,value只指定了 entry
, template
, filename 这三个属性
// vue.config.js
module.exports = {
pages: {
index: {
// page 的入口 ,其实也就是 main.js 文件的路径
entry: 'src/index/main.js',
// 模板来源 ,就是public文件夹中 index.html 文件的路径
template: 'public/index.html',
// 在 dist/index.html 的输出,打包之后的文件夹中,index.html 文件的路径
filename: 'index.html',
// 当使用 title 选项时,
// template 中的 title 标签需要是 <%= htmlWebpackPlugin.options.title %>
title: 'Index Page',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// 当使用只有入口的字符串格式时,
// 模板会被推导为 `public/subpage.html`
// 并且如果找不到的话,就回退到 `public/index.html`。
// 输出文件名会被推导为 `subpage.html`。
subpage: 'src/subpage/main.js'
}
}
在编译时,通过eslint-loader
来 lint 代码。
设置为 true 或者 'waring' 会将 lint 错误输出为编译警告。默认情况下,警告仅仅会被输出到命令行,且不会使得编译失败。
设置为 'error' 会强制将 lint 错误输出为编译错误,同时也意味着 lint 错误将会导致编译失败。
如果需要在生产环境构建时禁用,以免编译失败,可以采用以下配置
// vue.config.js
module.exports = {
lintOnSave: process.env.NODE_ENV !== "production" ? true : "warning",
}
如果你不需要生产环境的 source map,可以将其设置为 false
以加速生产环境构建。但是一般我都是不区分环境,直接 false
有关于 source map:可以参考这篇文章 sourceMap是个啥
// vue.config.js
module.exports = {
productionSourceMap: false,
}
调整 webpack 配置最简单的方式就是在 vue.config.js
中的 configureWebpack
选项提供一个对象:该对象将会被 webpack-merge 合并入最终的 webpack 配置。
module.exports = {
configureWebpack: {
plugins: [
new MyAwesomeWebpackPlugin()
]
}
}
如果这个值是一个函数,则会接收被解析的配置作为参数。该函数既可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。
修改配置并不返回任何东西:添加插件的时候需要使用 config.plugins.push()
// 内部配置,是基于项目需求
configureWebpack: (config) => {
// 以在浏览器开发工具的性能/时间线面板中启用对组件初始化、编译、渲染和打点
config.performance = {
hints:'warning',
//入口起点的最大体积 整数类型(以字节为单位)
maxEntrypointSize: 50000000,
//生成文件的最大体积 整数类型(以字节为单位 300k)
maxAssetSize: 30000000,
//只给出 js 文件的性能提示
assetFilter: function(assetFilename) {
return assetFilename.endsWith('.js');
}
}
// 修改配置,添加插件
config.plugins.push(
// 自动加载模块,而不必到处 import 或 require ,在这里加载模块之后,组件内部就不用inport引入了
new webpack.ProvidePlugin({
echarts: "echarts"
}),
// 扩展环境变量
new webpack.DefinePlugin({
APPTYPE: JSON.stringify(env),
RULEENV: JSON.stringify(process.env.RULE_TYPE),
PROTYPE: JSON.stringify(process.env.PRODUCTION_TYPE)
}),
// HTML文件的配置插件
new HtmlWebpackPlugin({
filename: "index.html",
template: "./public/index.html",
cdn: cdn,
minify: {
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联css
}
})
),
// 防止将某些 import 的包打包,而是在运行时再去从外部获取这些扩展依赖
// 比如 我的vue现在是通过cdn引入的,我不希望在打包的时候,将vue打包进去,而是通过cdn调用vue
(config.externals = {
vue: "Vue",
vuex: "Vuex",
"vue-router": "VueRouter",
"element-ui": "ELEMENT"
});
// 针对不同环境进行 配置
if(isProduction) {
// 配置插件
config.plugins.push(
// new swPlugin('oem' + newversion),
new GenerateSW({
cacheId: 'pro-cache',
skipWaiting: true, //跳过waiting状态
clientsClaim: true, //通知让新的sw立即在页面上取得控制权
cleanupOutdatedCaches: true,//删除过时、老版本的缓存
include: [
],
//缓存规则,可用正则匹配请求,进行缓存
//这里将js、css、还有图片资源分开缓存,可以区分缓存时间(虽然这里没做区分。。)
runtimeCaching: [
{
urlPattern: /.*\.js$/i,
handler: 'CacheFirst',
options: {
cacheName: 'rys-js',
expiration: {
maxEntries: 30, //最多缓存20个,超过的按照LRU原则删除
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
},
},
},
{
urlPattern: /.*css.*/,
handler: 'CacheFirst',
options: {
cacheName: 'rys-css',
expiration: {
maxEntries: 30, //最多缓存30个,超过的按照LRU原则删除
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
},
},
}
]
}),
new CompressionWebpackPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: new RegExp("\\.(" + ["js", "css"].join("|") + ")$"), // 匹配文件名
threshold: 10240, // 对10K以上的数据进行压缩
minRatio: 0.8,
deleteOriginalAssets: false // 是否删除源文件
}),
// 压缩代码,去除 生产环境的 console 和 debugger
new UglifyJsPlugin({
uglifyOptions: {
compress: {
dead_code: true,
// warnings: false,
drop_debugger: true,
drop_console: true,
},
},
sourceMap: false,
parallel: true,
})
)
}
},
返回一个被克隆或合并过的配置版本 : 添加插件的时候,可以直接使用 plugins :[]
// 将某些配置抽离出来,形成公共配置,直接引用
const productionGzipExtensions = ["js", "css"]
// 防止将某些 import 的包打包,而是在运行时再去从外部获取这些扩展依赖
// 比如 我的vue现在是通过cdn引入的,我不希望在打包的时候,将vue打包进去,而是通过cdn调用vue
const externals = {
vue: "Vue",
vuex: "Vuex",
"vue-router": "VueRouter",
"element-ui": "ElementUI"
}
// 配置插件,可以放置多个插件,以数组形式存储,通过数组扩展运算符展开调用
const commonPlugin = [
// 扩展环境变量
new webpack.DefinePlugin({
BASE_URL: JSON.stringify(process.env.BASE_URL),
APP_TYPE: JSON.stringify(process.env.APP_TYPE),
BASE_URL_XZ: JSON.stringify(process.env.BASE_URL_XZ)
})
]
configureWebpack: () => {
// 区分环境进行配置
if (process.env.NODE_ENV === "production") {
// 返回一个合并后的配置对象
return {
// 配置插件
plugins: [
// 调用外部配置
...commonPlugin,
new CompressionPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"), // 匹配文件名
threshold: 10240, // 对10K以上的数据进行压缩
minRatio: 0.8,
deleteOriginalAssets: false // 是否删除源文件
})
],
externals
}
} else {
return {
plugins: commonPlugin,
externals
}
}
},
是一个函数,会接收一个基于 webpack-chain 的 ChainableConfig
实例。允许对内部的 webpack 配置进行更细粒度的修改。
这个属性暂时不了解,后续会做详细跟进。
chainWebpack: config => {
//配置别名
config.resolve.alias
.set("@",resolve("src"))
.set("@img",resolve("src/assets/img"))
.set("@scss",resolve("src/assets/css/common"))
//生产环境配置
if(isProduction){
//删除预加载
config.plugins.delete('preload');
config.plugins.delete('prefetch');
//压缩代码
config.optimization.minimize(true);
//分割代码
config.optimization.splitChunks({
chunks: 'all'
})
}
}
#CSS
css: {
// 是否将组件中的 CSS 提取至一个独立的 CSS 文件中 (而不是动态注入到 JavaScript 中的 inline 代码)。生产环境下是 true,开发环境下是 false
extract: true,
// 是否为 CSS 开启 source map。设置为 true 之后可能会影响构建的性能。默认为 false
sourceMap: false,
// 向 CSS 相关的 loader 传递选项,相比于使用 chainWebpack 手动指定 loader 更推荐上面这样做
loaderOptions: {
css: {
// 这里的选项会传递给 css-loader
},
postcss: {
// 这里的选项会传递给 postcss-loader
}
},
// 启用 CSS modules for all css / pre-processor files.
// 从 v4 起已弃用,请使用css.requireModuleExtension。 在 v3 中,这个选项含义与 css.requireModuleExtension 相反
modules: false
requireModuleExtension: false
},
如果你有单独的后端开发服务器 API,并且希望在同域名下发送 API 请求 ,那么代理某些 URL 会很有用
devServer: {
// 指定使用一个 host。默认是 localhost。如果你希望服务器外部可访问,指定如下:
host: "0.0.0.0",
// 指定要监听请求的端口号
port: 8080
// 当出现编译器错误或警告时,浏览器中全屏显式错误信息,且覆盖页面。默认禁用。
overlay: {
warnings: true, // 如果你想要只显示编译器错误,可以只写这个
error: true // 如果想要显示警告和错误,两个属性都写上
},
// 设置为 true 时,此选项绕过主机检查。不建议这样做,因为不检查主机的应用程序容易受到 DNS 重新连接攻击。
disableHostCheck: true,
// 配置多个代理
proxy: {
"^/api": {
target: "http://xxx.com", // 要访问的接口域名
changeOrigin: true, //开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite: {
"^/api": ""
}
},
"/message": {
target: "http://xxx", //目标地址
changeOrigin: false, //是否跨域
pathRewrite: {
"^/message": "" //url重写
}
}
}
},
针对于 配置代理 ,详细解释
1、在 localhost:3000
上有后端服务的话,你可以这样启用代理:请求到 /api/users
现在会被代理到请求 http://localhost:3000/api/users
。
module.exports = {
devServer: {
proxy: {
'/api': 'http://localhost:3000'
}
}
};
2、 如果你不想始终传递 /api
,则需要重写路径:请求到 /api/users
现在会被代理到请求 http://localhost:3000/users
。
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000', // 代理路径地址
pathRewrite: {'^/api' : ''} // 重写路径,避免每次代理都带上 /api/
}
}
}
};
3、 代理跨域
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'^/api' : ''}, // 重写路径,避免每次代理都带上 /api/
changeOrigin: true, //开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
}
}
}
};