Vue Cli自定义配置

由于 Vue Cli 是默认集成 Webpack 的,为了减少烦琐的配置,默认情况下 Webpack 的相关配置项是不会暴露给开发者的,除了前面提到的采用 inspect 命令查看外。但是我们可以通过 vue.config.js 来修改和覆盖 Vue Cli 的默认 Webpack 配置。注意,由于本书不涉及 Webpack 的详细讲解,所以本章的大部分 Webpack 配置项需要读者事先学习 Webpack 的详细知识后才能理解,可以预先在互联网上了解 Webpack。

默认情况下,vue.config.js 文件并不存在,我们需要在项目的根目录(和 package.json 同级)下创建它,并且返回一个 JSON 格式对象,代码如下:

// vue.config.js
module.exports = {
    ...
}

由于是覆盖默认的 Webpack 配置,Vue Cli 提供了两种方案,一种是基于 configureWebpack 项来直接覆盖同名配置,另一种是采用 chainWebpack 项来直接修改默认配置。

configureWebpack配置

configureWebpack 选项提供了一个对象,该对象将会被 webpack-merge 合并入最终的 Webpack 配置,代码如下:

// vue.config.js
module.exports = {
    configureWebpack: {
        plugins: [
            new MyAwesomeWebpackPlugin()
        ]
    }
}

如果用户需要基于环境有条件地配置,或者想要直接修改配置,可以将 configureWebpack 配成一个函数方法(该函数方法会在环境变量被设置之后执行)。该函数方法的第一个参数会收到已经解析好的配置。在函数方法内,用户可以直接修改配置,或者返回一个将会被合并的对象,代码如下:

// vue.config.js
module.exports = {
    configureWebpack: config => {
        if (process.env.NODE_ENV === 'production') {
            // 为生产环境修改配置
        } else {
            // 为开发环境修改配置
        }
    }
}

采用 configureWebpack 的方式会直接覆盖同名的配置,如果只想修改某个配置的某一个子项,例如 loaderplugin 的选项,则很可能会覆盖掉不想要覆盖的默认配置,除非把这些默认配置再写一遍。

chainWebpack 这个配置项提供了颗粒度更细的配置修改。

chainWebpack配置

chainWebpack 选项是一个函数,会接收一个基于 webpack-chain 的实例,该配置项提供了一个 Webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。

chainWebpack 可以进行更细粒度的修改,修改 loader 的代码如下:

// vue.config.js
module.exports = {
    chainWebpack: config => {
        config.module
            .rule('vue')
            .use('vue-loader')
            .tap(options => {
                // 修改它的选项
                return options
            })
    }
}

替换一个规则中的 loader,代码如下:

// vue.config.js
module.exports = {
    chainWebpack: config => {
        const svgRule = config.module.rule('svg')
        // 清除已有的所有loader
        // 如果你不这样做,接下来的 loader 会附加在该规则现有的loader之后
        svgRule.uses.clear()
        // 添加要替换的loader
        svgRule
            .use('vue-svg-loader')
            .loader('vue-svg-loader')
    }
}

修改插件选项,代码如下:

// vue.config.js
module.exports = {
    chainWebpack: config => {
        config
            .plugin('html')
            .tap(args => {
                return [/* 传递给 html-webpack-plugin's 构造函数的新参数 */]
            })
    }
}

比起直接修改 configureWebpack 配置,chainWebpack 的表达能力更强,也更为安全。比如用户想要将 index.html 默认的路径从 /Users/username/proj/public/index.html 改为 /Users/username/proj/app/templates/index.html。通过参考 html-webpack-plugin,可以看到一个可以传入的选项列表。我们可以在下列配置中传入一个新的模板路径来改变它,代码如下:

// vue.config.js
module.exports = {
    chainWebpack: config => {
        config
            .plugin('html')
            .tap(args => {
                args[0].template = '/Users/username/proj/app/templates/index.html'
                return args
            })
    }
}

无论采用 configureWebpack 还是 chainWebpack,当我们对配置进行修改后,都可以通过命令:

npx vue-cli-service inspect > output.js

查看最终生成的配置文件是否是我们所需要的,该命令会在 vue.config.js 同级生成一个 output.js 文件,其内容就是完整的配置内容。

其它配置

除了前面介绍的两种配置方式外,vue.config.js 还有一些比较基础的配置项,我们举几个比较常用的例子。

page

默认情况下,项目为单页应用,即只有一个 index.html 页面作为出口,我们也可以通过 page 选项来设置多个页面,每个 page 应该有一个对应的 JavaScript 入口文件,其值应该是一个对象,对象的 key 是入口的名字,value 是一个指定了 entrytemplatefilenametitlechunks 的对象(除了 entry 之外都是可选的),或者是一个指定了 entry 的字符串,代码如下:

module.exports = {
  pages: {
    index: {
      // page 的入口
      entry: 'src/index/main.js',
      // 模板来源
      template: 'public/index.html',
      // 在 dist/index.html 的输出
      filename: 'index.html',
      // 当使用 title 选项时,
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></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'
  }
}

publicPath

部署应用包时的基本路径。用法和 Webpack 本身的 output.publicPath 一致,但是 Vue Cli 在其他地方也需要用到这个值,所以要始终使用 publicPath,而不要直接修改 Webpack 的 output.publicPath,代码如下:

module.exports = {
    publicPath: process.env.NODE_ENV === 'production'
        ? '/production-sub-path/'
        : '/'
}

css.extract

在大多数的 Vue 项目中,每个组件都有自己的样式代码,即放在单文件组件 <style> 里面的内容,当 css.extract 属性设置成 true 时,每个组件的样式代码被提取并合并到一个 CSS 文件中,在项目启动后会被加载。当 css.extract 被设置成 false 时,则不会被合并,而是采用 <style> 标签的形式添加在 HTML 页面中。默认情况下,开发模式为 false,生产模式为 true

devServer

Vue Cli 会提供静态资源服务 devServer,其基于 webpack-dev-server,即在执行 npm run serve 后,本地会开启一个默认 8080 端口的静态资源服务,这个服务相关的配置就可以通过 devServer 设置,例如 hostporthttps 可以被重新设置。

如果用户的前端应用和后端 API 服务器没有运行在同一个主机上,则需要在开发环境下将 API 请求代理到 API 服务器,通过添加 proxy 项来配置一些代理功能,代码如下:

module.exports = {
    devServer: {
        proxy: {
            '/api': { // 匹配的路径
                target: '<url>',  // 转换的目标路径
                ws: true, // 是否代理 websocket
                changeOrigin: true // 配合跨域设置
            },
            '^/foo': {
                target: '<other_url>'
            }
        }
    }
}

其中 proxykey 值表示需要匹配的路径,支持正则,只有命中这个规则的请求才会被代理。changeOrigin 设置成 false 表示请求头中的 host 仍然是浏览器发送过来的 host,如果设置成 true,则表示请求头中的 host 会设置成 target。如果需要跨域,则可以设置成 truedevServer 基于 http-proxy-middleware 的更多选项配置,可以参考 http-proxy-middleware 的官方网站。

这里暂时只列举这些使用比较多的配置,更多的配置可以参考 vue.config.js 的配置文档地址。