记一次 Vue CLI 项目修改打包资源输出目录
问题背景
某天同事在上架浙政钉应用的时候碰到个问题,Vue 项目打包默认输出资源路径的目录结构不符合上架要求,要求是输出的所有资源需要打平放在在 build
根目录下。如下示例:
打包默认输出路径的目录结构为

需要改成如下图

问题分析
项目是基于 Vue CLI 生成的 Vue 2 项目,依赖 webpack 4.x 版本。使用 Vue CLI 创建的模板项目中,通过 package.json
可以看到安装了依赖包 @vue/cli-service
。
@vue/cli-service
这个包集成了一套 webpack 的默认配置,用户可以通过配置文件 vue.config.js
进行配置,通过它提供的命令 vue-cli-service
,去启动本地开发服务器(vue-cli-service serve
)、打包(vue-cli-service build
)等。
我们知道 webpack 处理各种资源需要配置相应 loader 去做处理,我们先查看 @vue/cli-service
中 webpack 相关的默认配置,以此作为参考,然后在配置文件 vue.config.js
中添加或修改相关配置,实现我们的目的。
怎么查看 @vue/cli-service
内置的 webpack 配置呢?可以通过它提供的 inspect
命令输出 webpack 配置信息,也可通过源码查看 webpack 相关配置。
vue inspect
命令
@vue/cli-service
提供了一个 inspect
命令来审查一个 Vue CLI 项目的 webpack 配置,我们可以通过该命令将配置信息输出到 output.js 文件中。
vue inspect > output.js--mode=production
# 或者
npx @vue/cli-service inspect > output.js --mode=production
相关文档:Vue CLI - 审查项目的 webpack 配置
输出的 output.js 如下图,注意它输出的并不是一个有效的 webpack 配置文件,而是一个用于审查的被序列化的格式。

从 output.js 文件中,可以看到 js、css、图片等资源输出的相关配置。
js 文件输出路径在 js 目录下;

css 文件输出路径在 css 目录下;

图片、字体等静态资源,输出路径在 img、media、fonts 目录下;

@vue/cli-service
相关源码
@vue/cli-service
中相关 webpack 配置在 lib/config
目录;涉及到的相关文件如下:
-
app.js
文件;里面包含 js 资源输出相关配置,可以看到默认配置输出在 js 目录下。app.js 源码 -
base.js
文件;里面包含静态资源相关配置,可以看到默认配置不同类型资源输出在img
、media
、fonts
目录下,使用file-loader
、url-loader
去处理。base.js 源码PS: 最新源码升级到 webpack 5,将这块配置抽离成了
assets.js
文件,具体见 PR:feat!: support and use webpack 5 as default; 并且不再使用file-loader
、url-loader
去处理,而是改成通过 webpack 5 的 Assets Modules 配置,静态资源内联条件由之前配置小于 4k 变成小于 8 k(webpack 5 的默认配置),具体见 PR:feat!: remove url-loader and file-loader in favor of asset modules ; -
css.js
文件;里面包含 css 相关配置,可以看到默认配置输出在 css 目录下。css.js 源码
问题解决
了解了 @vue/cli-service
内置的 webpack 相关配置,接下来一个个处理问题,在配置文件 vue.config.js
修改或添加配置。
-
处理输出目录名为
build
;通过配置项[outputDir](https://cli.vuejs.org/zh/config/#outputdir "outputDir")
将它配置为build
即可。// vue.config.js module.exports = { outputDir: 'build', // ... 其他配置 }
-
处理
js
文件资源输出路径;默认配置是输出在 js 目录下,我们通过配置项chainWepack
添加如下配置,也可以通过configureWepack
配置。// vue.config.js module.exports = { chainWebpack: (config) => { config.output .filename('[name].[hash:8].js') .chunkFilename('[name].[hash:8].js'); }, // ... 其他配置 } // 或者 module.exports = { configureWebpack: { output: { filename: "[name].[hash:8].js", chunkFilename: "[name].[hash:8].js", }, }, // ... 其他配置 }
-
处理
css
文件资源输出路径;默认配置是输出在 css 目录下,我们通过 css.extract 配置项添加如下配置。// vue.config.js module.exports = { css: { extract: { filename: "[name].[hash:8].css", chunkFilename: "[name].[hash:8].css", }, }, }
-
处理图片等静态资源;参考内置的默认配置,添加如下配置;
-
webpack 4 采用如下配置
// vue.config.js with webpack 4 const filename = '[name].[hash:8].[ext]'; const genUrlLoaderOptions = (dir) => { return { limit: 4096, // use explicit fallback to avoid regression in url-loader>=1.1.0 fallback: { loader: require.resolve("file-loader"), options: { name: filename, }, }, }; }; module.exports = { chainWebpack: (config) => { config.module .rule("images") .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/) .use("url-loader") .loader(require.resolve("url-loader")) .options(genUrlLoaderOptions()); // do not base64-inline SVGs. // https://github.com/facebookincubator/create-react-app/pull/1180 config.module .rule("svg") .test(/\.(svg)(\?.*)?$/) .use("file-loader") .loader(require.resolve("file-loader")) .options({ name: filename, }); config.module .rule("media") .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/) .use("url-loader") .loader(require.resolve("url-loader")) .options(genUrlLoaderOptions()); config.module .rule("fonts") .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i) .use("url-loader") .loader(require.resolve("url-loader")) .options(genUrlLoaderOptions()); }, };
-
wepack 5 采用如下配置
// vue.config.js with webpack 5 module.exports = { chainWebpack: (config) => { const filename = `[name].[hash:8].[ext]` config.module .rule("svg") .test(/\.(svg)(\?.*)?$/) // do not base64-inline SVGs. // https://github.com/facebookincubator/create-react-app/pull/1180 .set("type", "asset/resource") .set("generator", { filename, }); config.module .rule("images") .test(/\.(png|jpe?g|gif|webp|avif)(\?.*)?$/) .set("type", "asset") .set("generator", { filename, }); config.module .rule("media") .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/) .set("type", "asset") .set("generator", { filename, }); config.module .rule("fonts") .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i) .set("type", "asset") .set("generator", { filename, }); }, }
-
添加以上配置就可以解决问题了,最后汇总在配置文件 vue.config.js
添加的所有配置如下;
-
webpack 4 采用如下配置
// vue.config.js with webpack 4 const filename = '[name].[hash:8].[ext]'; const genUrlLoaderOptions = (dir) => { return { limit: 4096, // use explicit fallback to avoid regression in url-loader>=1.1.0 fallback: { loader: require.resolve("file-loader"), options: { name: filename, }, }, }; }; module.exports = { outputDir: 'build', css: { extract: { filename: "[name].[hash:8].css", chunkFilename: "[name].[hash:8].css", }, }, chainWebpack: (config) => { config.module .rule("images") .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/) .use("url-loader") .loader(require.resolve("url-loader")) .options(genUrlLoaderOptions()); // do not base64-inline SVGs. // https://github.com/facebookincubator/create-react-app/pull/1180 config.module .rule("svg") .test(/\.(svg)(\?.*)?$/) .use("file-loader") .loader(require.resolve("file-loader")) .options({ name: filename, }); config.module .rule("media") .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/) .use("url-loader") .loader(require.resolve("url-loader")) .options(genUrlLoaderOptions()); config.module .rule("fonts") .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i) .use("url-loader") .loader(require.resolve("url-loader")) .options(genUrlLoaderOptions()); }, }
-
webpack 5 采用如下配置
// vue.config.js with webpack 5 const { defineConfig } = require("@vue/cli-service"); module.exports = defineConfig({ outputDir: "build", css: { extract: { filename: "[name].[hash:8].css", chunkFilename: "[name].[hash:8].css", }, }, chainWebpack: (config) => { // js file config.output .filename('[name].[hash:8].js') .chunkFilename('[name].[hash:8].js'); const filename = `[name].[hash:8].[ext]` config.module .rule("svg") .test(/\.(svg)(\?.*)?$/) // do not base64-inline SVGs. // https://github.com/facebookincubator/create-react-app/pull/1180 .set("type", "asset/resource") .set("generator", { filename, }); config.module .rule("images") .test(/\.(png|jpe?g|gif|webp|avif)(\?.*)?$/) .set("type", "asset") .set("generator", { filename, }); config.module .rule("media") .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/) .set("type", "asset") .set("generator", { filename, }); config.module .rule("fonts") .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i) .set("type", "asset") .set("generator", { filename, }); }, });
总结
首先我们需要分析下项目使用的相关工具,知道了项目是使用 Vue CLI 生成的,本地开发、打包都是基于命令 vue-cli-service
,该命令是由包 @vue/cli-service
提供,从文档中了解到它是”基于 webpack 构建,并带有合理的默认配置。“,并且提供了 inspect
命令用于审查项目的 webpack 配置。接下来通过查看 inspect
命令输出的配置信息和 @vue/cli-service
源码中的有关配置,参考它的配置,然后在配置文件 vue.config.js
添加相应配置,实现修改打包输出的资源路径。
转载自:https://juejin.cn/post/7184718449422106681