Skip to content

vue-cli配置文件详解

该webpack版本号为3.6.0

这段时间以来,项目一直都是用Vue全家桶及vue-cli,于是便把vue-cli配置文件给整理了以下,希望对大家能有所帮助。

一:文件的配置结构如下

html
├─build │ ├─build.js │ ├─check-versions.js │ ├─logo.png │ ├─utils.js │
├─vue-loader.conf.js │ ├─webpack.base.conf.js │ ├─webpack.dev.conf.js │
├─webpack.prod.conf.js ├─config │ ├─dev.env.js │ ├─index.js │ ├─prod.env.js
├─... └─package.json

二:文章内容

文章主要对生产环境和开发环境以及打包所相关的文件进行分析。

首先看package.json里面的scripts字段。

js
  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "node build/build.js"
  },

npm run dev 这里默认执行的是开发环境。

开发环境和生产环境区别

开发环境(development)和生产环境(production)的构建目标差异很大。在开发环境中,我们需要具有强大的、具有实时重新加载(live reloading)或热模块替换(hot module replacement)能力的 source map 和 localhost server。而在生产环境中,我们的目标则转向于关注更小的 bundle,更轻量 的 source map,以及更优化的资源,以改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置。

三:build文件夹分析

3.1 build/webpack.dev.conf.js

当执行npm run dev时,我们执行的就是该js文件,该文件主要完成以下任务:

  • 引入相关插件和配置
  • 生成处理各种样式的规则
  • 配置开发环境,如热更新、监听端口号,是否自动打开浏览器等都在webpack中的devServer中配置完成
  • 寻找可利用的端口和添加显示程序编译运行时的错误信息。 文件内容如下(一些插件和语句的具体作用见注释):
js
"use strict";
const utils = require("./utils");
// utils提供工具函数,包括生成处理各种样式语言的loader,获取资源文件存放路径的工具函数。
const webpack = require("webpack");
// 引入webpack模块
const config = require("../config");
// 默认是index文件
const merge = require("webpack-merge");
// 将基础配置和开发环境配置或者生产环境配置合并在一起的包管理
const baseWebpackConfig = require("./webpack.base.conf");
// 引入基本webpack基本配置
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 文件名及时更改,自动打包并且生成响应的文件在index.html里面
// 传送门:https://webpack.js.org/plugins/html-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
// Friendly-errors-webpack-plugin可识别某些类型的webpack错误并清理,汇总和优先化它们以提供更好的开发者体验。
// 传送门:http://npm.taobao.org/package/friendly-errors-webpack-plugin
const portfinder = require("portfinder");
// 查看空闲端口位置,默认情况下搜索8000这个端口,
// 传送门:https://www.npmjs.com/package/portfinder
const HOST = process.env.HOST;
//processs为node的一个全局对象获取当前程序的环境变量,即host,
// 传送门:http://javascript.ruanyifeng.com/nodejs/process.html#toc5
const PORT = process.env.PORT && Number(process.env.PORT); //
//processs为node的一个全局对象获取当前程序的环境变量,即host,
// 传送门:http://javascript.ruanyifeng.com/nodejs/process.html#toc5
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.dev.cssSourceMap,
      usePostCSS: true,
    }),
    //自动生成了css,postcss,less等规则,并进行模块转换,转换成webpack可识别的文件,进行解析转换
  },
  devtool: config.dev.devtool,
  // 增加调试信息
  // 传送门:https://doc.webpack-china.org/configuration/devtool
  devServer: {
    clientLogLevel: "none",
    // 在开发工具(DevTools)的控制台将显示消息【如:在重新加载之前,在一个错误之前,或者模块热替换(HMR)启动时,这可能显示得很繁琐】
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-clientloglevel
    historyApiFallback: true,
    // 当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html。
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-historyapifallback
    hot: true,
    // 启动模块热更新特性
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-hot
    compress: true,
    // 一切服务都启动用gzip方式进行压缩代码
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-compress
    host: HOST || config.dev.host,
    // 指定使用一个host,默认是localhost,获取HOST地址,该文件定义或config中index里的dev配置里获取
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-host
    port: PORT || config.dev.port,
    // 指定要监听的端口号
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-port
    open: config.dev.autoOpenBrowser,
    // 发开服务器是否自动代开默认浏览器
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-open
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,
    // 当出现编译器错误或警告时,在浏览器中显示全屏叠加,覆盖到浏览器的项目页面的上方。{warning:false,errors:true}这个选项为 显示错误和警告
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-overlay
    publicPath: config.dev.assetsPublicPath,
    // 服务器假设运行在http://localhost:8080并且output.filename被设置为bundle.js默认。publicPath是"/",所以你的包(束)通过可以http://localhost:8080/bundle.js访问。
    // 比如将config中的index.js dev对象的中的assertsPublicPath设置为"/asserts/"那么文件打开后将通过http://localhost:8080/asserts/来进行访问
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-publicpath-
    proxy: config.dev.proxyTable,
    // 如果你有单独的后端开发服务器API,并且希望在同域名下发送API请求,那么代理某些URL将很有用.简称就是API代理,中间件  需引入 http-proxy-middleware
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-proxy
    quiet: false, // necessary for FriendlyErrorsPlugin
    // 启用quiet后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自的WebPack的错误或警告在控制台不可见。
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-quiet-
    watchOptions: {
      poll: config.dev.poll,
    },
    // webpack使用文件系统(file system)获取文件改动的通知
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-watchoptions-
  },

  plugins: [
    new webpack.DefinePlugin({
      "process.env": require("../config/dev.env"),
    }),
    // 将DefinePlugin允许您创建可在配置全局常量的编译时间。这对于允许开发构建和发布构建之间的不同行为是有用的
    // 传送门:https://webpack.js.org/plugins/define-plugin/#src/components/Sidebar/Sidebar.jsx
    new webpack.HotModuleReplacementPlugin(),
    // 永远不能用在生产模式,模块热更新,修改文件的内容,允许在运行时更新各种模块,而无需进行完全刷新。
    // 传送门:https://doc.webpack-china.org/guides/hot-module-replacement/
    new webpack.NamedModulesPlugin(),
    // 当进行热更新时,相关文件名会被展示出来
    // 传送门:https://webpack.js.org/plugins/named-modules-plugin/#src/components/Sidebar/Sidebar.jsx
    new webpack.NoEmitOnErrorsPlugin(),
    // 跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误。
    // 传送门:https://webpack.js.org/plugins/no-emit-on-errors-plugin/#src/components/Sidebar/Sidebar.jsx

    new HtmlWebpackPlugin({
      filename: "index.html",
      template: "index.html",
      inject: true,
    }),
    // 该插件可自动生成一个 html5 文件或使用模板文件将编译好的代码注入进去
    // 传送门:https://webpack.js.org/plugins/html-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
    // https://github.com/ampedandwired/html-webpack-plugin
  ],
});

module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port;
  //由于portfinder这个插件本身是从8000开始查找,这里设置查找的默认端口号
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err);
    } else {
      // publish the new Port, necessary for e2e tests
      process.env.PORT = port;
      // 如果端口被占用就对进程设置端口
      devWebpackConfig.devServer.port = port;
      // 如果端口被占用就设置devServer的端口
      // Add FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(
        new FriendlyErrorsPlugin({
          compilationSuccessInfo: {
            messages: [
              `Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`,
            ],
          },
          // 添加提示信息,所在域名和端口的提示信息
          onErrors: config.dev.notifyOnErrors
            ? utils.createNotifierCallback()
            : undefined,
        }),
      );
      // 窗口提示信息,调用utils工具函数的createNotifierCallBack()方法

      resolve(devWebpackConfig);
      // 如果找到能用的端口号,就把配置信息提示抛出去
    }
  });
});

3.2 build/webpack.base.conf.js

从webpack.dev.conf.js来看文件中引入了utils.js工具类函数和config文件夹中的index.js环境配置模块,以及引入了webpack.base.conf.js基础(开发和生产环境公共)配置。所以这里先分析webpack.base.conf.js文件。webpack.base.conf.js主要完成了下面这些事件:

  • 配置webpack编译入口
  • 配置webpack输出路径和命名规则
  • 配置模块resolve规则
  • 配置不同类型模块的处理规则 文件内容如下(一些插件和语句的具体作用见注释):
js
"use strict";
const path = require("path");
// node.js的文件路径,用来处理文件当中的路径问题
// 传送门:http://www.jianshu.com/p/fe41ee02efc8
const utils = require("./utils");
//引入utils.js模块
const config = require("../config");
// 默认是index文件,引入index.js模块
const vueLoaderConfig = require("./vue-loader.conf");
// vue-loader.conf配置文件是用来解决各种css文件的,定义了诸如css,less,sass之类的和样式有关的loader
function resolve(dir) {
  return path.join(__dirname, "..", dir);
}
// 此函数是用来返回当前目录的平行目录的路径,因为有个'..'

module.exports = {
  context: path.resolve(__dirname, "../"),
  //基础目录(绝对路径),用于从配置中解析入口点和加载程序
  // 传送门:https://webpack.js.org/configuration/entry-context/#context
  entry: {
    app: "./src/main.js",
  },
  // 定义入口文件
  output: {
    path: config.build.assetsRoot,
    // 打包生成的出口文件所放的位置
    filename: "[name].js",
    // 打包生成app.js文件
    publicPath:
      process.env.NODE_ENV === "production"
        ? config.build.assetsPublicPath
        : config.dev.assetsPublicPath,
    // 项目上线地址,也就是真正的文件引用路径,如果是production环境,其实这里都是'/'
    // path和publicPath的区别传送门:https://www.webpackjs.com/concepts/output/
  },
  resolve: {
    extensions: [".js", ".vue", ".json"],
    // 省略扩展名,比方说import index from '../js/index'会默认去找index文件,然后找index.js,index.vue,index.json文件
    alias: {
      vue$: "vue/dist/vue.esm.js",
      "@": resolve("src"),
    },
    // 使用别名  使用上面的resolve函数,意思就是用@代替src的绝对路径
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
        options: vueLoaderConfig,
      },
      {
        test: /\.js$/,
        loader: "babel-loader",
        include: [resolve("src"), resolve("test")],
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 10000,
          name: utils.assetsPath("img/[name].[hash:7].[ext]"),
        },
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 10000,
          name: utils.assetsPath("media/[name].[hash:7].[ext]"),
        },
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 10000,
          name: utils.assetsPath("fonts/[name].[hash:7].[ext]"),
        },
      },
    ],
  },
  // 不同文件模块使用不同的loader
  node: {
    setImmediate: false,
    dgram: "empty",
    fs: "empty",
    net: "empty",
    tls: "empty",
    child_process: "empty",
  },
  //这些选项可以配置是否 polyfill 或 mock 某些 Node.js 全局变量和模块。这可以使最初为 Node.js 环境编写的代码,在其他环境(如浏览器)中运行.
  //传送门:http://www.css88.com/doc/webpack/configuration/node/
};

3.3 build/utils.js
utils提供工具函数,包括生成处理各种样式语言的loader,获取资源文件存放路径的工具函数。

  • 计算资源文件存放路径
  • 生成cssLoaders用于加载.vue文件中的样式
  • 生成styleLoaders用于加载不在.vue文件中的单独存在的样式文件
  • 处理程序在编译过程中出现的错误,并在桌面进行错误信息的提示 文件内容如下(一些插件和语句的具体作用见注释):
js
"use strict";
const path = require("path");
// node.js的文件路径,用来处理文件当中的路径问题
// 传送门:http://www.jianshu.com/p/fe41ee02efc8
const config = require("../config");
// 引入config中的index.js文件
const ExtractTextPlugin = require("extract-text-webpack-plugin");
// extract-text-webpack-plugin可以提取bundle中的特定文本,将提取后的文本单独存放到另外的文件
// 传送门:https://webpack.js.org/plugins/extract-text-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const packageConfig = require("../package.json");
// 引入包的json文件
exports.assetsPath = function (_path) {
  const assetsSubDirectory =
    process.env.NODE_ENV === "production"
      ? config.build.assetsSubDirectory
      : config.dev.assetsSubDirectory;

  return path.posix.join(assetsSubDirectory, _path);
  // path.posix以posix兼容的方式交互,是跨平台的,如果是path.win32的话,只能在win上
  // 传送们:https://nodejs.org/api/path.html#path_path_posix
};
// 资源存放的路径,区别在于生产环境和开发环境
exports.cssLoaders = function (options) {
  options = options || {};

  const cssLoader = {
    loader: "css-loader",
    options: {
      sourceMap: options.sourceMap,
      // 是否使用sourceMap
      // 传送门:https://webpack.js.org/loaders/css-loader/#src/components/Sidebar/Sidebar.jsx
    },
  };

  const postcssLoader = {
    loader: "postcss-loader",
    options: {
      sourceMap: options.sourceMap,
      // 是否使用sourceMap,postcss-loader用来解决各浏览器的前缀问题
      // 传送门:https://webpack.js.org/loaders/postcss-loader/#src/components/Sidebar/Sidebar.jsx
    },
  };

  function generateLoaders(loader, loaderOptions) {
    const loaders = options.usePostCSS
      ? [cssLoader, postcssLoader]
      : [cssLoader];

    // 判断将cssLoader和postcssLoader推入loaders数组
    if (loader) {
      loaders.push({
        loader: loader + "-loader",
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap,
        }),
      });
    }
    // 处理各种各样的loader,并且将各种各样的loader推入loaders数组当中去
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: "vue-style-loader",
      });
    }
    //如果options.extract存在,则用extract-text-plugin提取样式
    else {
      return ["vue-style-loader"].concat(loaders);
    }
    // 无需提取样式则简单使用vue-style-loader配合各种样式loader去处理vue当中的<style>里面的样式
    // 传送门:https://vue-loader.vuejs.org/en/configurations/extract-css.html
  }
  return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders("less"),
    sass: generateLoaders("sass", {
      indentedSyntax: true,
    }),
    scss: generateLoaders("sass"),
    stylus: generateLoaders("stylus"),
    styl: generateLoaders("stylus"),
  };
};
// cssLoaders将各种loader 转成对象形式以便styleLoaders进行处理
exports.styleLoaders = function (options) {
  const output = [];
  const loaders = exports.cssLoaders(options);
  // 调用cssLoaders方法
  // 这时的usePostCSS存在
  for (const extension in loaders) {
    const loader = loaders[extension];
    output.push({
      test: new RegExp("\\." + extension + "$"),
      use: loader,
    });
  }
  return output;
};
//  styleLoaders进行再加工处理
//  生成处理单独的.css、.sass、.scss等样式文件的规则
exports.createNotifierCallback = () => {
  const notifier = require("node-notifier");
  return (severity, errors) => {
    if (severity !== "error") return;
    const error = errors[0];
    // 每次捕获第一个错误
    const filename = error.file && error.file.split("!").pop();
    // 错误文件的名称所在位置
    notifier.notify({
      title: packageConfig.name,
      // 错误提示项目名字
      message: severity + ": " + error.name,
      // 错误提示类型
      subtitle: filename || "",
      //  错误提示副标题
      icon: path.join(__dirname, "logo.png"),
      // 错误提示图示标
    });
  };
};
// 引入node-notifier模块,这个模块是用来在桌面窗口提示信息,如果想要关闭直接return掉或者在webpack.dev.conf.js中关掉
// 传送门:https://www.npmjs.com/package/node-notifier

3.4 build/vue-loader.js

该文件的主要作用就是处理.vue文件,解析这个文件中的每个语言块(template、script、style),转换成js可用的js模块。

  • 配置vue-loader,将一些遇见的文件转换成可供webpack进行模块化处理。文件内容如下(一些插件和语句的具体作用见注释):
js
"use strict";
const utils = require("./utils");
// 引入utils.js相关模块
const config = require("../config");
// 默认是index.js文件
const isProduction = process.env.NODE_ENV === "production";
//判断是否为生产环境
const sourceMapEnabled = isProduction
  ? config.build.productionSourceMap
  : config.dev.cssSourceMap;
//判定特定环境下sourceMap的值
module.exports = {
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    extract: isProduction,
    // 是否将样式提取到单独的文件
  }),
  cssSourceMap: sourceMapEnabled,
  // 是否开启cssSourceMap, 关闭可以避免 css-loader 的 some relative path related bugs 同时可以加快构建速度。
  // 传送门:https://vue-loader.vuejs.org/zh-cn/options.html#csssourcemap
  cacheBusting: config.dev.cacheBusting,
  // 是否通过将哈希查询附加到文件名来生成具有缓存清除的源映射[疑问,求解]
  // 传送门:https://vue-loader.vuejs.org/en/options.html#cachebusting
  transformToRequire: {
    video: ["src", "poster"],
    source: "src",
    img: "src",
    image: "xlink:href",
  },
  // 在模版编译过程中,编译器可以将某些属性,如 src 路径,转换为 require 调用,以便目标资源可以由 webpack 处理.
  // 传送门:https://vue-loader.vuejs.org/zh-cn/options.html#transformtorequire
};

3.5 build/webpack.prod.conf.js   构建的时候用到的webpack配置来自webpack.prod.conf.js,该配置同样是在webpack.base.conf基础上的进一步完善。主要完成下面几件事情:   1.合并基础的webpack配置   2.配置样式文件的处理规则,styleLoaders   3.配置webpack的输出   4.配置webpack插件   5.gzip模式下的webpack插件配置   6.webpack-bundle分析   文件内容如下(一些插件和语句的具体作用见注释):

js
"use strict";
const path = require("path");
// node.js的文件路径,用来处理文件当中的路径问题
// 传送门:http://www.jianshu.com/p/fe41ee02efc8
const utils = require("./utils");
//引入utils.js模块
const webpack = require("webpack");
// 引入webpack模块
const config = require("../config");
// 默认是index文件,引入index.js模块
const merge = require("webpack-merge");
// 将基础配置和开发环境配置或者生产环境配置合并在一起的包管理
const baseWebpackConfig = require("./webpack.base.conf");
// 引入基本webpack基本配置
const CopyWebpackPlugin = require("copy-webpack-plugin");
// 在webpack中拷贝文件和文件夹
// 传送门:https://doc.webpack-china.org/plugins/copy-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 文件名即使更改,自动打包并且生成响应的文件在index.html里面
// 传送门:https://webpack.js.org/plugins/html-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const ExtractTextPlugin = require("extract-text-webpack-plugin");
// 它会将所有的入口 chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件
// https://doc.webpack-china.org/plugins/extract-text-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const OptimizeCSSPlugin = require("optimize-css-assets-webpack-plugin");
//一个用来压缩优化CSS大小的东西
// 传送门:http://npm.taobao.org/package/optimize-css-assets-webpack-plugin
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
// 一个用来压缩优化JS大小的东西
// 传送门:https://webpack.js.org/plugins/uglifyjs-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const env = require("../config/prod.env");
// 引入生产环境
const webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true,
    }),
  },
  // 将webpack基本配置和生产环境配置合并在一起,生成css,postcss,less等规则,并进行模块转换,转换成webpack可识别的文件,进行解析
  // 将CSS提取到单独文件中去
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  // 是否使用sourcemap
  output: {
    path: config.build.assetsRoot,
    // 文件打包的输出路径
    filename: utils.assetsPath("js/[name].[chunkhash].js"),
    // 主文件入口文件名字
    chunkFilename: utils.assetsPath("js/[id].[chunkhash].js"),
    // 非主文件入口文件名,可以存放cdn的地址
    // 传送门:http://react-china.org/t/webpack-output-filename-output-chunkfilename/2256
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      "process.env": env,
    }),
    // DefinePlugin 允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。
    // https://doc.webpack-china.org/plugins/define-plugin/#src/components/Sidebar/Sidebar.jsx
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false,
        },
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true,
    }),
    // 一个用来压缩优化JS大小的东西
    // 传送门:https://webpack.js.org/plugins/uglifyjs-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
    new ExtractTextPlugin({
      filename: utils.assetsPath("css/[name].[contenthash].css"),
      allChunks: false,
    }),
    // 它会将所有的入口 chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件
    // https://doc.webpack-china.org/plugins/extract-text-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap
        ? {
            safe: true,
            map: {
              inline: false,
            },
          }
        : {
            safe: true,
          },
    }),
    //一个用来压缩优化CSS大小的东西
    // 传送门:http://npm.taobao.org/package/optimize-css-assets-webpack-plugin
    new HtmlWebpackPlugin({
      filename: config.build.index,
      template: "index.html",
      inject: true,
      minify: {
        removeComments: true,
        // 删除index.html中的注释
        collapseWhitespace: true,
        // 删除index.html中的空格
        removeAttributeQuotes: true,
        // 删除各种html标签属性值的双引号
      },
      chunksSortMode: "dependency",
      // 注入依赖的时候按照依赖先后顺序进行注入,比如,需要先注入vendor.js,再注入app.js
    }),
    new webpack.HashedModuleIdsPlugin(),
    // 该插件会根据模块的相对路径生成一个四位数的hash作为模块id, 建议用于生产环境。
    // 传送门:https://doc.webpack-china.org/plugins/hashed-module-ids-plugin/#src/components/Sidebar/Sidebar.jsx
    new webpack.optimize.ModuleConcatenationPlugin(),
    // 预编译所有模块到一个闭包中,提升你的代码在浏览器中的执行速度。
    // 传送门:https://doc.webpack-china.org/plugins/module-concatenation-plugin/#src/components/Sidebar/Sidebar.jsx
    new webpack.optimize.CommonsChunkPlugin({
      name: "vendor",
      minChunks(module) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(path.join(__dirname, "../node_modules")) === 0
        );
      },
    }),
    // 将所有从node_modules中引入的js提取到vendor.js,即抽取库文件
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin({
      name: "manifest",
      minChunks: Infinity,
    }),
    // 把webpack的runtime和manifest这些webpack管理所有模块交互的代码打包到[name].js文件中,防止build之后vendor的hash值被更新[疑问]
    // 传送门:https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: "app",
      async: "vendor-async",
      children: true,
      minChunks: 3,
    }),
    // 传送门:https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, "../static"),
        to: config.build.assetsSubDirectory,
        ignore: [".*"],
      },
    ]),
    // 在webpack中拷贝文件和文件夹
    // 传送门:https://doc.webpack-china.org/plugins/copy-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
  ],
});

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require("compression-webpack-plugin");

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: "[path].gz[query]",
      algorithm: "gzip",
      test: new RegExp(
        "\\.(" + config.build.productionGzipExtensions.join("|") + ")$",
      ),
      threshold: 10240,
      minRatio: 0.8,
    }),
  );
}
// 提供带 Content-Encoding 编码的压缩版的资源
// 传送门:https://doc.webpack-china.org/plugins/compression-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin =
    require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
  webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}
// 分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。
// 传送门:https://github.com/webpack-contrib/webpack-bundle-analyzer
module.exports = webpackConfig;

3.6 build/check-versions.js
若要执行build.js构建打包文件,必须先进行npm和node版本的检测。

  • 进行npm、node版本检查(版本不相对时,会出现无法打包,某些文件无法正常编译运行的情况)文件内容如下(一些插件和语句的具体作用见注释):
js
"use strict";

const chalk = require("chalk");
// chalk插件,他的作用是在控制台中输出不同的颜色的字,大致这样用chalk.blue('Hello world'),这款插件只能改变命令行中的字体颜色
// 传送门:https://github.com/chalk/chalk
const semver = require("semver");
// npm版本号的检查
// 传送门:https://docs.npmjs.com/misc/semver
const packageConfig = require("../package.json");
// 引入包的json文件
const shell = require("shelljs");
// shelljs插件,作用是用来执行Unix系统命
function exec(cmd) {
  return require("child_process").execSync(cmd).toString().trim();
}
//脚本可以通过child_process 模块新建子进程,从而执行 Unix 系统命令
//这段代码实际就是把cmd这个参数传递的值转化成前后没有空格的字符串,也就是版本号
const versionRequirements = [
  {
    name: "node",
    currentVersion: semver.clean(process.version),
    // 当前node版本信息
    versionRequirement: packageConfig.engines.node,
    //package.json里面所要求的node版本信息
  },
];
// 检查node版本信息。
// 一:使用semver插件把版本信息转化成规定格式,也就是 '  =v1.2.3  ' -> '1.2.3' 这种功能
// 二:这是规定的pakage.json中engines选项的node版本信息 "node":">= 4.0.0"
if (shell.which("npm")) {
  versionRequirements.push({
    name: "npm",
    currentVersion: exec("npm --version"),
    // 动调用npm --version命令,检查版本信息
    versionRequirement: packageConfig.engines.npm,
    // 规定要求的npm版本信息
  });
}
// 检查npm版本信息
// 一:自动调用npm --version命令,并且把参数返回给exec函数,从而获取纯净的版本号
// 二:这是规定的pakage.json中engines选项的node版本信息 "npm": ">= 3.0.0"
module.exports = function () {
  const warnings = [];
  for (let i = 0; i < versionRequirements.length; i++) {
    const mod = versionRequirements[i];
    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
      warnings.push(
        mod.name +
          ": " +
          chalk.red(mod.currentVersion) +
          " should be " +
          chalk.green(mod.versionRequirement),
      );
    }
  }
  // 进行版本检查,这里主要用到semver的模块,semver.satisfies(version,range),如果版本不在这个范围内,则进行报错
  // range  eg:
  // ^1.2.3 := >=1.2.3 <2.0.0
  // ^0.2.3 := >=0.2.3 <0.3.0
  // ^0.0.3 := >=0.0.3 <0.0.4

  if (warnings.length) {
    console.log("");
    console.log(
      chalk.yellow(
        "To use this template, you must update following to modules:",
      ),
    );
    console.log();

    for (let i = 0; i < warnings.length; i++) {
      const warning = warnings[i];
      console.log("  " + warning);
    }

    console.log();
    process.exit(1);
    // 能否正常的运行dev-server
    // 传送门:http://javascript.ruanyifeng.com/nodejs/process.html#toc9
    // process.exit方法用来退出当前进程。它可以接受一个数值参数,如果参数大于0,表示执行失败;如果等于0表示执行成功。
  }
};

3.7 build/build.js
构建环境下的配置,执行”npm run build”的时候首先执行的是build/build.js文件,build.js主要完成下面几件事。

  • 进行node和npm的版本检查
  • 打包时产生loading动画
  • 删除目标文件夹
  • 输出打包信息

文件内容如下(一些插件和语句的具体作用见注释):

js
"use strict";
require("./check-versions")();
// 进行npm和node版本检查

process.env.NODE_ENV = "production";
// 当前node的环境变量设置为production
const ora = require("ora");
// cnpm run build显示的进度条
// 传送门:https://github.com/sindresorhus/ora
const rm = require("rimraf");
// 用于删除文件或者文件夹的插件,
// 传送门:https://github.com/isaacs/rimraf
const path = require("path");
// node.js的文件路径,用来处理文件当中的路径问题
// 传送门:http://www.jianshu.com/p/fe41ee02efc8
const chalk = require("chalk");
//  chalk插件,他的作用是在控制台中输出不同的颜色的字,大致这样用chalk.blue('Hello world'),这款插件只能改变命令行中的字体颜色
// 传送门:https://github.com/chalk/chalk
const webpack = require("webpack");
// 引入webpack模块
// 传送门:https://github.com/webpack/webpack
const config = require("../config");
// 引入config中的index.js文件
const webpackConfig = require("./webpack.prod.conf");
// 引入webpack生产环境配置
const spinner = ora("building for production...");
// 填写打包时所显示的提示信息
spinner.start();
// 开启loading动画
rm(
  path.join(config.build.assetsRoot, config.build.assetsSubDirectory),
  (err) => {
    // node.js将文件目录拼接起来,默认是/dist/static
    if (err) throw err;
    webpack(webpackConfig, (err, stats) => {
      spinner.stop();
      if (err) throw err;
      process.stdout.write(
        stats.toString({
          colors: true,
          modules: false,
          children: false,
          chunks: false,
          chunkModules: false,
        }) + "\n\n",
      );
      // process.stdout.write这一串是控制打包后详细文件的输出情况
      if (stats.hasErrors()) {
        console.log(chalk.red("  Build failed with errors.\n"));
        process.exit(1);
      }
      // 打包失败,显示错误信息,并退出程序
      console.log(chalk.cyan("  Build complete.\n"));
      console.log(
        chalk.yellow(
          "  Tip: built files are meant to be served over an HTTP server.\n" +
            "  Opening index.html over file:// won't work.\n",
        ),
      );
      // 打包成功在控制台上显示打包成功提示信息
    });
  },
);

四:config文件夹分析

4.1 config/index.js
config文件夹下,最主要的就是index.js文件,保存着开发环境和生产环境所需要的信息。
文件内容如下(一些插件和语句的具体作用见注释):

js
"use strict";
// Template version: 1.2.5
// see http://vuejs-templates.github.io/webpack for documentation.
// 这个文件描述了开发和生产环境的配置
const path = require("path");
// node.js的文件路径,用来处理文件当中的路径问题
// 传送门:http://www.jianshu.com/p/fe41ee02efc8
module.exports = {
  dev: {
    assetsSubDirectory: "static",
    // 二级目录,存放静态资源文件的目录,位于dist文件夹下
    assetsPublicPath: "/",
    // 发布路径,如果构建后的产品文件有用于CDN或者放到其他域名服务器,可以在这里设置,当然本地打包,本地浏览一般都将这里设置为"./"
    // 设置之后的构建的产品在注入到index.html中就会带上这里的发布路径
    proxyTable: {},
    // 代理示例: proxy: [{context: ["/auth", "/api"],target: "http://localhost:3000",}]
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-proxy

    // 各种开发服务器的配置
    host: "localhost",
    // 这个可以被process.env.HOST重写
    port: 8080,
    // process.env.PORT重写  在端口空闲的情况下
    autoOpenBrowser: true,
    // 是否自动打开浏览器
    errorOverlay: true,
    // 当出现编译器错误或警告时,在浏览器中显示全屏叠加,覆盖到浏览器的项目页面的上方。{warning:false,errors:true}这个选项为 显示错误和警告
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-overlay
    notifyOnErrors: true,
    // 是否允许窗口弹出错误信息
    // 传送门:https://www.npmjs.com/package/node-notifier
    poll: false,
    // webpack使用文件系统(file system)获取文件改动的通知
    // 传送门:https://doc.webpack-china.org/configuration/dev-server/#devserver-watchoptions-
    useEslint: true,
    // 如果使用了eslint,您的代码将在捆绑和连接错误和警告将在控制台中显示。
    showEslintErrorsInOverlay: false,
    // 如果使用了eslint,违反eslint规则的错误和警告也将被显示在浏览器的透明黑色层上面

    devtool: "eval-source-map",
    // 开启调试的类型
    //传送门: https://webpack.js.org/configuration/devtool/#development
    cacheBusting: true,
    // 是否通过将哈希查询附加到文件名来生成具有缓存清除的源映射[疑问,求解]
    // 传送门:https://vue-loader.vuejs.org/en/options.html#cachebusting
    cssSourceMap: false,
    // 开发环境下,不显示cssSourceMap
  },

  build: {
    index: path.resolve(__dirname, "../dist/index.html"),
    //  获得绝对路径,inde.html的模板文件
    // 传送门:http://javascript.ruanyifeng.com/nodejs/path.html#toc1
    assetsRoot: path.resolve(__dirname, "../dist"),
    // 获得dist文件夹的绝对路径
    // 传送门:http://javascript.ruanyifeng.com/nodejs/path.html#toc1
    assetsSubDirectory: "static",
    // 二级目录
    assetsPublicPath: "/",
    // 发布路径,如果构建后的产品文件有用于CDN或者放到其他域名服务器,可以在这里设置,当然本地打包,本地浏览一般都将这里设置为"./"
    // 设置之后的构建的产品在注入到index.html中就会带上这里的发布路径

    productionSourceMap: true,
    // production环境下生成sourceMap文件
    // 传送门:https://webpack.js.org/configuration/devtool/#production
    devtool: "#source-map",
    // 开启调试的类型
    //传送门: https://webpack.js.org/configuration/devtool/#development

    productionGzip: false,
    productionGzipExtensions: ["js", "css"],
    // gzip模式下需要压缩的文件的扩展名,设置js、css之后就只会对js和css文件进行压

    bundleAnalyzerReport: process.env.npm_config_report,
    // 是否展示webpack构建打包之后的分析报告
  },
};

4.2 config/dev.env.js
该文件主要来设置开发环境变量。
文件内容如下(一些插件和语句的具体作用见注释):

js
"use strict";
const merge = require("webpack-merge");
// 引入webpack-merge模块
// 传送门:https://doc.webpack-china.org/guides/production/
const prodEnv = require("./prod.env");
// 引入生产环境配置
module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
});
// 配置NODE_ENV来决定开发环境
// 这个就是用来上线的时候用到,来决定是开发环境还是生产环境,来进行相关的配置解决方案

4.2 config/prod.env.js
该文件主要来设置生产环境变量。
文件内容如下(一些插件和语句的具体作用见注释):

js
"use strict";
module.exports = {
  NODE_ENV: '"production"',
};
//NODE_ENV决定生产环境

五:总结

相信通过大家对以上内容阅读,已经对vue-cli有个大概的了解,希望能在大家配置项目时有所帮助。 参考文章(部分):

上次更新于: