> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Vue CLI 配置

## 依赖安装

```bash title="安装全局插件"
npm install -g @vue/cli
# or
yarn global add @vue/cli
```

```bash title="创建项目"
vue create my-project
# or
vue ui
```

```bash title=" package.json 启动命令配置"
{
    "scripts": {
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build"
    }
}
```

- 配置文件`vue.config.js`

```bash title="serve 启动配置"
用法：vue-cli-service serve [options] [entry]

选项：
    --open    在服务器启动时打开浏览器
    --copy    在服务器启动时将 URL 复制到剪切版
    --mode    指定环境模式 (默认值：development)
    --host    指定 host (默认值：0.0.0.0)
    --port    指定 port (默认值：8080)
    --https   使用 https (默认值：false)
```

```bash title="build 打包配置"
用法：vue-cli-service build [options] [entry|pattern]

选项：
    --mode          指定环境模式 (默认值：production)
    --dest          指定输出目录 (默认值：dist)
    --modern        面向现代浏览器带自动回退地构建应用
    --target        app | lib | wc | wc-async (默认值：app)
    --name          库或 Web Components 模式下的名字 (默认值：package.json 中的 "name" 字段或入口文件名)
    --no-clean      在构建项目之前不清除目标目录的内容
    --report        生成 report.html 以帮助分析包内容
    --report-json   生成 report.json 以帮助分析包内容
    --watch         监听文件变化
```

## 打包配置

- 配置文件`babel.config.js`

```javascript title="babel.config.js"
module.exports = {
    presets: [
        "@vue/cli-plugin-babel/preset",
        [
            "@babel/preset-env",
            {
                modules: false,
            },
        ],
    ],
    compact: false,
    plugins: [
        [
            "component",
            // element-ui 按需加载
            {
                libraryName: "element-ui",
                styleLibraryName: "theme-chalk",
            },
        ],
    ],
    env: {
        development: {
            // 组件动态导入配置 "@babel/plugin-syntax-dynamic-import"
            plugins: ["dynamic-import-node"],
        },
        production: {
            // 生产环境移除console
            plugins: ["transform-remove-console"],
        },
    },
};
```

:::tip babel 配置文件相关插件

- `@vue/cli-plugin-babel`
- `@babel/preset-env`
- `@babel/plugin-syntax-dynamic-import`
- `transform-remove-console`
- `@babel/plugin-transform-runtime`

:::

- 配置文件`vue.config.js`

```javascript title="vue.config.js"
module.exports = {
    // 忽略依赖包 babel 转码
    transpileDependencies: true,
    publicPath: "/",
    outputDir: "dist",
    assetsDir: "static",
    // 代码检查
    lintOnSave: isProd ? false : "warning",
    // 打包代码不生成 source map
    productionSourceMap: false,
    devServer: {
        host: "0.0.0.0",
        port: 8080,
        // 浏览器热更新
        hot: true,
        compress: true,
        proxy: {
            // 代理API置
            [process.env.VUE_APP_API]: {
                target: "http://localhost:8080",
                changeOrigin: true,
                pathRewrite: {
                    ["^" + process.env.VUE_APP_API]: "",
                },
            },
        },
        client: {
            // 浏览器控制台错误提示
            overlay: false,
            // 进度条
            progress: true,
        },
    },
    css: {
        loaderOptions: {
            sass: {
                // 全局变量 scss 文件引入
                additionalData: `@import "~@/assets/css/variables.scss";`,
                sassOptions: { outputStyle: "expanded" },
            },
        },
    },
};
```

```javascript title="vue.config.js"
// 打包压缩插件
const CompressionWebpackPlugin = require("compression-webpack-plugin");

module.exports = defineConfig({
    configureWebpack: {
        // externals: {
        // 	vue: "Vue",
        // 	vuex: "Vuex",
        // 	axios: "axios",
        // 	"vue-router": "VueRouter",
        // 	"element-ui": "ELEMENT"
        // } /* CDN */,
        cache: {
            type: "filesystem",
            allowCollectingMemory: isProd ? false : true,
        },
        target: isProd ? "browserslist" : "web",
        name: name,
        performance: {
            hints: false,
        },
        plugins: [
            new CompressionWebpackPlugin({
                algorithm: "gzip",
                filename: "[path][base].gz",
                test: /\.(js|css|json|html)(\?.*)?$/i,
                threshold: 10240,
                minRatio: 0.8,
            }),
        ],
    },
});
```

- chainWebpack 配置

```javascript title="vue.config.js"
const { defineConfig } = require("@vue/cli-service");
// 打包优化插件
const { ESBuildMinifyPlugin } = require("esbuild-loader");
// 代码跳转编辑器插件
const { codeInspectorPlugin } = require("code-inspector-plugin");
// 是否生产环境
const isProd = process.env.NODE_ENV === "production";
// 代码跳转编辑器配置
const devCIP = codeInspectorPlugin({
    bundler: "webpack",
    hideConsole: true,
    hideDomPathAttr: true,
});

module.exports = defineConfig({
    chainWebpack: (config) => {
        if (isProd) {
            // 删除预加载文件
            config.plugins.delete("preload");
            config.plugins.delete("prefetch");
            // 打包优化
            const rule = config.module.rule("js");
            rule.uses.clear();
            rule.use("esbuild-loader").loader("esbuild-loader");
            config.optimization.minimizers.delete("terser");
            config.optimization
                .minimizer("esbuild")
                .use(ESBuildMinifyPlugin, [{ minify: true, css: true }]);
        }
        if (process.env.NODE_ENV === "development")
            config.plugin("code-inspector-plugin").use(devCIP);
        // 打包分片
        config.when(process.env.NODE_ENV == "production", (config) => {
            config.optimization.splitChunks({
                chunks: "all",
                cacheGroups: {
                    vendors: {
                        name: "chunk-vendors",
                        chunks: "initial",
                        test: /[\\/]node_modules[\\/]/,
                        priority: 1,
                    },
                    elementUI: {
                        name: "chunk-elementUI",
                        priority: 2,
                        test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
                        reuseExistingChunk: true,
                    },
                    common: {
                        name: "chunk-common",
                        chunks: "all",
                        test: /[\\/]src[\\/]/,
                        minSize: 0,
                        minChunks: 2,
                        priority: 1,
                        reuseExistingChunk: true,
                    },
                    styles: {
                        name: "styles",
                        test: /\.(s?css|less|sass)$/,
                        chunks: "all",
                        enforce: true,
                    },
                },
            });
            config.optimization.runtimeChunk("single");
        });
    },
});
```

::: info webpack 配置

- [webpack 配置文档](https://webpack.docschina.org/configuration/)
- [webpack 配置参考](https://cli.vuejs.org/zh/guide/webpack.html)

:::

## 完整配置

```javascript title="vue.config.js 完整配置"
const { defineConfig } = require("@vue/cli-service");
// 打包优化插件
const { ESBuildMinifyPlugin } = require("esbuild-loader");
// 代码跳转编辑器插件
const { codeInspectorPlugin } = require("code-inspector-plugin");
// 打包压缩插件
const CompressionWebpackPlugin = require("compression-webpack-plugin");
// 网站名称配置
const name = process.env.VUE_APP_TITLE;
// 是否生产环境
const isProd = process.env.NODE_ENV === "production";
// 代码跳转编辑器配置
const devCIP = codeInspectorPlugin({
    bundler: "webpack",
    hideConsole: true,
    hideDomPathAttr: true,
});

module.exports = defineConfig({
    transpileDependencies: true,
    publicPath: "/",
    outputDir: "dist",
    assetsDir: "static",
    lintOnSave: isProd ? false : "warning",
    productionSourceMap: false,
    devServer: {
        host: "0.0.0.0",
        port: 8080,
        hot: true,
        compress: true,
        proxy: {
            [process.env.VUE_APP_API]: {
                target: "http://localhost:8080",
                changeOrigin: true,
                pathRewrite: {
                    ["^" + process.env.VUE_APP_API]: "",
                },
            },
        },
        client: {
            overlay: false,
            progress: true,
        },
    },
    css: {
        loaderOptions: {
            sass: {
                additionalData: `@import "~@/assets/css/variables.scss";`,
                sassOptions: { outputStyle: "expanded" },
            },
        },
    },
    chainWebpack: (config) => {
        if (isProd) {
            // 删除预加载文件
            config.plugins.delete("preload");
            config.plugins.delete("prefetch");
            // 打包优化
            const rule = config.module.rule("js");
            rule.uses.clear();
            rule.use("esbuild-loader").loader("esbuild-loader");
            config.optimization.minimizers.delete("terser");
            config.optimization
                .minimizer("esbuild")
                .use(ESBuildMinifyPlugin, [{ minify: true, css: true }]);
        }
        if (process.env.NODE_ENV === "development")
            config.plugin("code-inspector-plugin").use(devCIP);
        // 打包分片
        config.when(process.env.NODE_ENV == "production", (config) => {
            config.optimization.splitChunks({
                chunks: "all",
                cacheGroups: {
                    vendors: {
                        name: "chunk-vendors",
                        chunks: "initial",
                        test: /[\\/]node_modules[\\/]/,
                        priority: 1,
                    },
                    elementUI: {
                        name: "chunk-elementUI",
                        priority: 2,
                        test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
                        reuseExistingChunk: true,
                    },
                    common: {
                        name: "chunk-common",
                        chunks: "all",
                        test: /[\\/]src[\\/]/,
                        minSize: 0,
                        minChunks: 2,
                        priority: 1,
                        reuseExistingChunk: true,
                    },
                    styles: {
                        name: "styles",
                        test: /\.(s?css|less|sass)$/,
                        chunks: "all",
                        enforce: true,
                    },
                },
            });
            config.optimization.runtimeChunk("single");
        });
    },
    configureWebpack: {
        // externals: {
        // 	vue: "Vue",
        // 	vuex: "Vuex",
        // 	axios: "axios",
        // 	"vue-router": "VueRouter",
        // 	"element-ui": "ELEMENT"
        // } /* CDN */,
        cache: {
            type: "filesystem",
            allowCollectingMemory: isProd ? false : true,
        },
        target: isProd ? "browserslist" : "web",
        name: name,
        performance: {
            hints: false,
        },
        plugins: [
            new CompressionWebpackPlugin({
                algorithm: "gzip",
                filename: "[path][base].gz",
                test: /\.(js|css|json|html)(\?.*)?$/i,
                threshold: 10240,
                minRatio: 0.8,
            }),
        ],
    },
});
```

:::info 打包配置总结

- 打包输出目录设置
- `publicPath`: 设置静态资源路径，如：`/static`
- `outputDir`: 设置打包输出目录，如：`dist`、`build`、`output`
- `assetsDir`: 设置静态资源目录，如：`static`、`assets`、`public`
- `lintOnSave`: 是否开启 eslint 语法检查，默认为 `true`，可以设置为 `false` 或 `warning`
- `devServer`: 开发环境配置，如：`host`、`port`、`hot`、`compress`、`proxy`、`client`
- `css`: 设置 css 配置，如：`loaderOptions`
- `chainWebpack`: 链式配置，可以添加、删除、修改 webpack 配置，如：`externals`、`splitChunks`、`runtimeChunk`、`plugin` 等

:::

## 四、CDN 与 externals 减包

若打包后 `vendor.*.js` 体积过大导致首屏偏慢，可把 `vue`、`element-ui`、`vue-router`、`axios` 等稳定依赖改为 **CDN** 引入，并在构建配置中标记为外部依赖，使其不进入产物。

首先在页面模板中引入 CDN 链接：

```html title="index.html"
<script src="https://cdn.staticfile.org/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.staticfile.org/element-ui/2.10.1/index.js"></script>
<script src="https://cdn.staticfile.org/vue-router/3.0.7/vue-router.min.js"></script>
<script src="https://cdn.staticfile.org/axios/0.19.0/axios.min.js"></script>
```

Vue CLI 2.x 项目在 `webpack.base.conf.js` 中配置 `externals`：

```javascript title="webpack.base.conf.js"
module.exports = {
    // 配置 cdn 包
    externals: {
        vue: "Vue",
        element: "element-ui",
        "vue-router": "VueRouter",
        axios: "axios",
    },
};
```

Vue CLI 3.x 及以上项目，在 `vue.config.js` 中通过 `configureWebpack` 配置：

```javascript title="vue.config.js"
module.exports = {
    configureWebpack: {
        externals: {
            vue: "Vue",
            element: "ELEMENT",
            "vue-router": "VueRouter",
            axios: "axios",
        },
    },
};
```

:::details 小结

- 通过 CDN 链接方式加载依赖，可以显著减少打包产物的文件大小；
- 采用 CDN 后需注掉对应的 `import XXX from 'XXX'` 与 `Vue.use(xxx)`，否则仍会被打包；
- 内网或离线部署场景不建议使用 CDN，改为拆分 `vendor` 或开启 gzip 压缩。

:::

## 参考文档

[Vue CLI 官方文档](https://cli.vuejs.org/zh)「👈 点击查阅」
