# Vue CLIWebpack 配置

这一节我们来讲一下 Vue CLI 中的关于 Webpack 中的配置,Vuewebpack 有了自己的一套封装,这使得在 vue 中配置 webpack 更加简单了。

# 安装

我们通过一下方式安装 @vue/cli

npm install -g @vue/cli
#OR
yarn global add @vue/cli

我们通过以下命令查看一下 vueversion

vue --version // @vue/cli 4.2.2

接着我们创建项目:

vue create vue-cli-demo

他会提示我们一个 preset,我们选择默认设置:

安装成功后,我们执行:

# 打开项目目录
cd vue-cli-demo

# 启动项目
yarn serve

# or
npm run serve

看到如下界面,我们的项目变安装成功了:

具体的目录如下:

vue-cli 还提供了可视化界面,我们可以通过:

vue ui

运行此命令后,浏览器会打开以下界面:

我们可以添加我么当前这个项目,添加完成之后,就可以对项目做相应的配置了。比如安装插件、代码编译、热更新、检查等。

更多的笔者在这里就不详述了,大家可以自己动手尝试。

# Vue CLI 中的 Webpack

vue-cli 2.x 中,通过其构建出来的目录会包含相应的 webpack 的配置文件。但在最新的 Vue CLI 中,我们找不到一份关于 Webpack 的配置文件,这是因为最新的版本为了让我们用的更加简单,提供了一种开箱即用的模式,即我们不用配置 webpack 就可以运行项目,同时它提供了 vue.config.js 文件来满足我们对开发者对其封装的 Webpack 默认配置的修改,如下图:

create-react-app 中通过 npm run eject 弹出 webpack 原生配置不同,vue 提供了一套自己的配置,帮助我们简化对 webpack 的配置,底层通过 @vue/cli-servicevue.config.js 翻译成 webpack 的原生配置。

所以在 Vue CLI 中,我们需要学习的可能不是 webpack 官网的东西了,而是学习 vue.config.js 这个配置文件。

# vue.config.js 常用配置

# outputDir

当运行 vue-cli-service build 时生成的生产环境构建文件的目录。默认是 dist 目录,如果你想将构建好的文件打包输出到 output 文件夹下,我们可以配置:

// vue.config.js
module.exports = {
  ...
  outputDir: 'output',
  ...
}

这其实改变了 webpack 配置中 output 下的 path 项,修改了文件的输出路径。

# productionSourceMap

该配置项用于设置是否为生产环境构建生成 source map,一般在生产环境下为了快速定位错误信息,我们都会开启 source map

// vue.config.js
module.exports = {
  ...
  productionSourceMap: true,
  ...
}

该配置会修改 webpackdevtool 项的值为 source-map

# publicPath

部署应用包时的基本 URL。用法和 webpack 本身的 output.publicPath 一致。默认值是 /

例如 https://www.my-app.com/,如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.my-app.com/my-app/,则设置 publicPath/my-app/

# chainWebpack

chainWebpack 配置项允许我们更细粒度的控制 webpack 的内部配置,其集成的是 webpack-chain 这一插件,该插件可以让我们能够使用链式操作来修改配置,比如:

// 用于做相应的合并处理
const merge = require('webpack-merge');

module.exports = {
  ... 
  // config 参数为已经解析好的 webpack 配置
  chainWebpack: config => {
    config.module
      .rule('images')
      .use('url-loader')
      .tap(options =>
        merge(options, {
          limit: 5120,
        })
      )
  }
  ...
}

以上操作我们可以成功修改 webpackmodule 项里配置 rules 规则为图片下的 url-loader 值,将其 limit 限制改为 5M,修改后的 webpack 配置代码如下:

{
  ...
  module: {
    rules: [
      {   
        /* config.module.rule('images') */
        test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
        use: [
          /* config.module.rule('images').use('url-loader') */
          {
            loader: 'url-loader',
            options: {
              limit: 5120,
              name: 'img/[name].[hash:8].[ext]'
            }
          }
        ]
      }
    ]
  }
  ...
}

关于 webpack-chain 的使用可以参考其 github 官方地址:github.com/mozilla-neu…,它提供了操作类似 javaScript SetMap 的方式,以及一系列速记方法。

# configureWebpack

除了上述使用 chainWebpack 来改变 webpack 内部配置外,我们还可以使用 configureWebpack 来进行修改。

两者的不同点在于 chainWebpack 是链式修改,而 configureWebpack 更倾向于整体替换和修改。

  • 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中。

  • 如果这个值是一个函数,则会接收被解析的配置作为参数。该函数既可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。

示例代码如下:

// vue.config.js
module.exports = {
  ...
  // config 参数为已经解析好的 webpack 配置
  configureWebpack: config => {
    // config.plugins = []; // 这样会直接将 plugins 置空
    // 使用 return 一个对象会通过 webpack-merge 进行合并,plugins 不会置空
    return {
      plugins: []
    }
  }
  ...
}

我们可以在项目目录下运行 vue inspect 来查看你修改后的 webpack 完整配置,当然你也可以缩小审查范围,比如:

# 只查看 plugins 的内容
vue inspect plugins

# devServer

vue.config.js 还提供了 devServer 项用于配置 webpack-dev-server 的行为,使得我们可以对本地服务器进行相应配置。

所有 webpack-dev-server 的选项他都支持:

// vue.config.js
module.exports = {
  ...
  devServer: {
    open: true, // 是否自动打开浏览器页面
    host: '0.0.0.0', // 指定使用一个 host。默认是 localhost
    port: 8080, // 端口地址
    https: false, // 使用https提供服务
    proxy: null, // string | Object 代理设置
        
    // 提供在服务器内部的其他中间件之前执行自定义中间件的能力
    before: app => {
      // `app` 是一个 express 实例
    }
  }
  ...
}

以上是 vue.config.js 中一些常用的配置项功能,更多大家可以参考 vue.config.js

上面我们提到了可以通过 vue inspect 查看 webpack 的完整配置,我们还可以通过可视化工具来查看 Vue CLI 默认的插件配置,以及他们的用处,我们可以如下图进行操作:

我们可以从配置项中找到 plugins 数组,其包含了如下插件(配置项已经省略,增加了定义插件的代码):

// vue-loader是 webpack 的加载器,允许你以单文件组件的格式编写 Vue 组件
const VueLoaderPlugin = require('vue-loader/lib/plugin');

// webpack 内置插件,用于创建在编译时可以配置的全局常量
const { DefinePlugin } = require('webpack');

// 用于强制所有模块的完整路径必需与磁盘上实际路径的确切大小写相匹配
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');

// 识别某些类型的 webpack 错误并整理,以提供开发人员更好的体验。
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');

// 将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

// 用于在 webpack 构建期间优化、最小化 CSS文件
const OptimizeCssnanoPlugin = require('optimize-css-assets-webpack-plugin');

// webpack 内置插件,用于根据模块的相对路径生成 hash 作为模块 id, 一般用于生产环境
const { HashedModuleIdsPlugin } = require('webpack');

// 用于根据模板或使用加载器生成 HTML 文件
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 用于在使用 html-webpack-plugin 生成的 html 中添加 <link rel ='preload'> 或 <link rel ='prefetch'>,有助于异步加载
const PreloadPlugin = require('preload-webpack-plugin');

// 用于将单个文件或整个目录复制到构建目录
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  plugins: [
    /* config.plugin('vue-loader') */
    new VueLoaderPlugin(), 
        
    /* config.plugin('define') */
    new DefinePlugin(),
        
    /* config.plugin('case-sensitive-paths') */
    new CaseSensitivePathsPlugin(),
        
    /* config.plugin('friendly-errors') */
    new FriendlyErrorsWebpackPlugin(),
        
    /* config.plugin('extract-css') */
    new MiniCssExtractPlugin(),
        
    /* config.plugin('optimize-css') */
    new OptimizeCssnanoPlugin(),
        
    /* config.plugin('hash-module-ids') */
    new HashedModuleIdsPlugin(),
        
    /* config.plugin('html') */
    new HtmlWebpackPlugin(),
        
    /* config.plugin('preload') */
    new PreloadPlugin(),
        
    /* config.plugin('copy') */
    new CopyWebpackPlugin()
  ]
}

我们可以看到每个插件上方都添加了使用 chainWebpack 访问的方式,同时我也添加了每个插件相应的用途注释。

# env 文件与环境设置

# 配置文件

create-react-appenv.js 一样,我们同样可以在项目根目录中创建以下形式的文件进行不同环境下变量的配置:

.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]         # 只在指定的模式中被载入
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略

npm run serve 的默认环境是 developmentnpm run build 的默认环境是 production

我们在 vue.config.js 中打印出项目的 NODE_ENV

// vue.config.js

console.log(process.env.NODE_ENV); // 在终端会输出 development
console.log(process.env.VUE_APP_TITLE); // 在终端会输出 undefined

我们可以在项目根目录中创建 .env.development.local,我们可以添加一个 VUE_APP_TITLE

NODE_ENV=development
VUE_APP_TITLE=development mode

重新运行 npm run server,我们可以看到在终端中 VUE_APP_TITLE 会输出 development mode

create-react-app 一样,环境文件的权重如下:

.env.[mode].local > .env.[mode] > .env.local > .env 

但是需要注意的是,除了相同配置项权重大的覆盖小的,不同配置项它们会进行合并操作,类似于 javascript 中的 Object.assign 的用法。

如果我们需要更改环境 modestage,则需要相应的 scripts 命令下增加相应的 mode

"scripts": {
    "serve": "vue-cli-service serve --mode stage",
}

# 环境注入

create-react-app 一样,我们可以通过 webpack.DefinePlugin 插件将环境变量注入到 js 代码之中,其实就只在 vue 前端代码中使用 process.env 相关变量:

// webpack 配置
{
  ...
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      }
    }),
  ],
  ...
}

我们可以通过这个方式来引入,但是 Vue CLI 中已经帮助我们完成了这个任务,我们可以直接在 src/main.js 下打印出来 process.env 看一看:

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

console.log(process.env);

new Vue({
  render: h => h(App),
}).$mount('#app')

我们可以得到如下图所示:

这里跟 create-react-app 只有 REACT_APP_ 开头的变量才能注入一样,Vue CLI 中也是仅支持注入环境配置文件中以 VUE_APP_ 开头的变量,而 NODE_ENVBASE_URL 这两个特殊变量除外。

比如我们在 .env.development.local 中新增 NAME=vue 字段:

NODE_ENV=development
VUE_APP_TITLE=development mode
NAME=vue

我们尝试在 vue.config.js 中打印 process.env,打印出如下图所示:

但是在入口文件 main.js 中还是没有 NAME

# 额外配置

以上我们通过新建配置文件的方式为项目不同环境配置不同的变量值,能够实现项目基本的环境管理,但是 .env 这样的配置文件中的参数目前只支持静态值,无法使用动态参数。

在某些情况下无法实现特定需求,这时候我们可以在根目录下新建 config 文件夹用于存放一些额外的配置文件。

/* 配置文件 index.js */

// 公共变量
const com = {
  IP: JSON.stringify('xxx')
};

module.exports = {
  // 开发环境变量
  dev: {
    env: {
      TYPE: JSON.stringify('dev'),
      ...com
    }
  },
  
  // 生产环境变量
  build: {
    env: {
      TYPE: JSON.stringify('prod'),
      ...com
    }
  }
}

上方代码我们把环境变量分为了公共变量、开发环境变量和生产环境变量,当然这些变量可能是动态的,比如用户的 ip 等。现在我们要在 vue.config.js 里注入这些变量,我们可以使用 chainWebpack 修改 DefinePlugin 中的值:

/* vue.config.js */
const configs = require('./config');

// 用于做相应的 merge 处理
const merge = require('webpack-merge');

// 根据环境判断使用哪份配置
const cfg = process.env.NODE_ENV === 'production' ? configs.build.env : configs.dev.env;

module.exports = {
  ...
  chainWebpack: config => {
    config.plugin('define')
      .tap(args => {
        let name = 'process.env';
        // 使用 merge 保证原始值不变
        args[0][name] = merge(args[0][name], cfg);
        return args
    })
  },
  ...
}

这个时候在 main.js 中我们就可以得到对应的 Typeip 了:

# 移动端场景

比如在非线上环境我们需要给自己的移动端项目开启 vConsole 调试,但是在线上环境肯定不需要开启这一功能,我们可以在入口文件中进行设置,代码如下:

首先安装 vconsole

npm install vconsole -D

main.js 中使用:

import Vue from 'vue'
import App from './App.vue'
import VConsole from 'vconsole';

Vue.config.productionTip = false

// 如果是非正式环境,加载 VConsole
if (process.env.NODE_ENV !== 'production') {
  var vConsole = new VConsole();
}

Vue.use(vConsole)

console.log(process.env);

new Vue({
  render: h => h(App),
}).$mount('#app')

接着我们就可以手机端查看相应的 console 代码了:

# 一些说明

这篇文章主要是讲解了 Vue CLI 的一些基本的配置内容,更详细的资料大家可以参考 Vue Cli 官网

其中这篇文章配置方面的内容基本上是参考了 掘金小册——Vue 项目构建与开发入门 的内容,作者写的很清晰易懂,笔者就把这方面的内容搬了过来。

# 相关链接

# 示例代码

示例代码可以看这里: