Проанализируйте проект веб-пакета, созданный vue-cli@2.9.3.

внешний интерфейс Командная строка Vue.js Webpack

предисловие

Было много анализовVue-cliПочему вы должны сами писать статью о стройке? Обучение похоже на большую гору: люди взбираются на гору по разным дорогам и делятся пейзажами, которые видят. Возможно, вы не сможете увидеть пейзаж, который видят другие, и почувствовать настроение других. Только когда вы отправляетесь в горы самостоятельно, вы можете увидеть разные пейзажи и испытать более глубокие переживания.

Проект размещен в авторскойgithubначальство,Проанализируйте проект веб-пакета, созданный vue-cli@2.9.3.. Всем удобно клонировать и скачивать, или просматривать онлайн. Также попроситеstar ^_^, но и своего рода поощрение и поддержка автора.

Текст начинается здесь~

использоватьvue-cliинициализацияwebpackпроект

// # 安装
npm install -g vue-cli
// 安装完后vue命令就可以使用了。实际上是全局注册了vue、vue-init、vue-list几个命令

// # ubuntu 系统下
// [vue-cli@2.9.3] link /usr/local/bin/vue@ -> /usr/local/lib/node_modules/vue-cli/bin/vue
// [vue-cli@2.9.3] link /usr/local/bin/vue-init@ -> /usr/local/lib/node_modules/vue-cli/bin/vue-init
// [vue-cli@2.9.3] link /usr/local/bin/vue-list@ -> /usr/local/lib/node_modules/vue-cli/bin/vue-list

vue list
// 可以发现有browserify、browserify-simple、pwa、simple、webpack、webpack-simple几种模板可选,这里选用webpack。

// # 使用 vue init
vue init <template-name> <project-name>

// # 例子
vue init webpack analyse-vue-cli

Болееvue-cliКак это работает, можете проверить эту статьюКак работает vuecliили проанализируйте исходный код Vue-cli, чтобы просмотреть этоВведите исходный код Vue-cli и самостоятельно создайте интерфейсный инструмент формирования шаблонов., или непосредственно просмотретьИсходный код репозитория vue-cli на github

если правильноwebpackНе очень понимание, вы можете просмотретьКонцепции в официальной документации веб-пакета, хоть и последняя версия, но концепции похожи.

package.json

Анализ проекта, как правило, начиная сpackage.jsonввод командыscriptsНачинать.

"scripts": {
  // dev webpack-dev-server --inline 模式 --progress 显示进度 --config 指定配置文件(默认是webpack.config.js)
  "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
  "start": "npm run dev",
  // jest测试
  "unit": "jest --config test/unit/jest.conf.js --coverage",
  // e2e测试
  "e2e": "node test/e2e/runner.js",
  // 运行jest测试和e2e测试
  "test": "npm run unit && npm run e2e",
  // eslint --ext 指定扩展名和相应的文件
  "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
  // node 执行build/build.js文件
  "build": "node build/build.js"
},

Npm ScriptОсновной принцип достигается путем звонковShellДля запуска команд скрипта.npm run startэквивалентно бегуnpm run dev.

Npm ScriptЕще одной важной особенностью является возможность запуска установки в каталог проекта.node_modulesисполняемый модуль в формате .

например по командеnpm i -D webpack-dev-serverБудуwebpack-dev-serverПосле установки в проект невозможно передать команду напрямую в корневую директорию проектаwebpack-dev-serverвыполнитьwebpack-dev-serverпостроен, но передать команду./node_modules/.bin/webpack-dev-serverвыполнить.

Npm ScriptЭта проблема может быть легко решена простымscriptsОпределите задачу в поле, например:

"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"

Npm Scriptсначала пойдет в каталог проектаnode_modulesУзнать, есть ли исполняемый файлwebpack-dev-serverфайл, используйте локальный, если он есть, и используйте глобальный, если нет. Итак, теперь выполнитеwebpack-dev-serverПри запуске службы вам нужно только выполнитьnpm run devдостигать.

Давайте посмотрим на npm run devwebpack-dev-serverНа самом делеnode.jsприложение, это черезJavaScriptразвивающийся. выполнить в командной строкеnpm run devкоманда эквивалентна выполнениюnode ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --progress --config build/webpack.dev.conf.js. Можешь попробовать.

Болееpackage.jsonэлементы конфигурации, вы можете просмотретьФайл package.json статьи Руана Ифэна

npm run devуказанныйbuild/webpack.dev.conf.jsНастройте для запуска службы, затем посмотрим, что делает этот файл.

build/webpack.dev.conf.js webpackКонфигурация среды разработки

Этот файл в основном выполняет следующие функции:
1. Ввести различные зависимости, а также ввестиconfigПеременные и конфигурации в папке и служебная функцияbuild/utils.js,
2. Объединитьbuild/webpack.base.conf.jsфайл конфигурации,
3, оснащенный некоторыми средами разработкиdevServer,pluginи т. д. конфигурация,
4. Наконец-то экспортировалPromise, в соответствии с настроенным портом ищем доступный порт для запуска службы.

В частности, вы можете увидетьbuild/webpack.dev.conf.jsЭтот файл отмечает:

'use strict'
// 引入工具函数
const utils = require('./utils')
// 引入webpack
const webpack = require('webpack')
// 引入config/index.js配置
const config = require('../config')
// 合并webpack配置
const merge = require('webpack-merge')
const path = require('path')
// 基本配置
const baseWebpackConfig = require('./webpack.base.conf')
// 拷贝插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 生成html的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 友好提示的插件 https://github.com/geowarin/friendly-errors-webpack-plugin
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// 查找可用端口 // github仓库 https://github.com/indexzero/node-portfinder
const portfinder = require('portfinder')


// process模块用来与当前进程互动,可以通过全局变量process访问,不必使用require命令加载。它是一个EventEmitter对象的实例。

// 后面有些process模块用到的,所以这里统一列举下。
// 更多查看这篇阮一峰的这篇文章 http://javascript.ruanyifeng.com/nodejs/process.html

// process对象提供一系列属性,用于返回系统信息。
// process.pid:当前进程的进程号。
// process.version:Node的版本,比如v0.10.18。
// process.platform:当前系统平台,比如Linux。
// process.title:默认值为“node”,可以自定义该值。
// process.argv:当前进程的命令行参数数组。
// process.env:指向当前shell的环境变量,比如process.env.HOME。
// process.execPath:运行当前进程的可执行文件的绝对路径。
// process.stdout:指向标准输出。
// process.stdin:指向标准输入。
// process.stderr:指向标准错误。

// process对象提供以下方法:
// process.exit():退出当前进程。
// process.cwd():返回运行当前脚本的工作目录的路径。_
// process.chdir():改变工作目录。
// process.nextTick():将一个回调函数放在下次事件循环的顶部。

// host
const HOST = process.env.HOST
// 端口
const PORT = process.env.PORT && Number(process.env.PORT)

// 合并基本的webpack配置
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    // cssSourceMap这里配置的是true
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  // cheap-module-eval-source-map is faster for development
  // 在开发环境是cheap-module-eval-source-map选项更快
  // 这里配置的是cheap-module-eval-source-map
  // 更多可以查看中文文档:https://webpack.docschina.org/configuration/devtool/#devtool
  // 英文 https://webpack.js.org/configuration/devtool/#development
  devtool: config.dev.devtool,

  // these devServer options should be customized in /config/index.js
  devServer: {
    // 配置在客户端的日志等级,这会影响到你在浏览器开发者工具控制台里看到的日志内容。
    // clientLogLevel 是枚举类型,可取如下之一的值 none | error | warning | info。
    // 默认为 info 级别,即输出所有类型的日志,设置成 none 可以不输出任何日志。
    clientLogLevel: 'warning',
    // historyApiFallback boolean object 用于方便的开发使用了 HTML5 History API 的单页应用。
    // 可以简单true 或者 任意的 404 响应可以提供为 index.html 页面。
    historyApiFallback: {
      rewrites: [
        // config.dev.assetsPublicPath 这里是 /
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    // 开启热更新
    hot: true,
    // contentBase 配置 DevServer HTTP 服务器的文件根目录。
    // 默认情况下为当前执行目录,通常是项目根目录,所有一般情况下你不必设置它,除非你有额外的文件需要被 DevServer 服务。
    contentBase: false, // since we use CopyWebpackPlugin.
    // compress 配置是否启用 gzip 压缩。boolean 为类型,默认为 false。
    compress: true,
    // host
    // 例如你想要局域网中的其它设备访问你本地的服务,可以在启动 DevServer 时带上 --host 0.0.0.0
    // 或者直接设置为 0.0.0.0
    // 这里配置的是localhost
    host: HOST || config.dev.host,
    // 端口号 这里配置的是8080
    port: PORT || config.dev.port,
    // 打开浏览器,这里配置是不打开false
    open: config.dev.autoOpenBrowser,
    // 是否在浏览器以遮罩形式显示报错信息 这里配置的是true
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,
      // 这里配置的是 /
    publicPath: config.dev.assetsPublicPath,
    // 代理 这里配置的是空{},有需要可以自行配置
    proxy: config.dev.proxyTable,
    // 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。
    // 开启后一般非常干净只有类似的提示 Your application is running here: http://localhost:8080
    quiet: true, // necessary for FriendlyErrorsPlugin
    // webpack-dev-middleware
    // watch: false,
    // 启用 Watch 模式。这意味着在初始构建之后,webpack 将继续监听任何已解析文件的更改。Watch 模式默认关闭。
    // webpack-dev-server 和 webpack-dev-middleware 里 Watch 模式默认开启。
    // Watch 模式的选项
    watchOptions: {
      // 或者指定毫秒为单位进行轮询。
      // 这里配置为false
      poll: config.dev.poll,
    }
    // 更多查看中文文档:https://webpack.docschina.org/configuration/watch/#src/components/Sidebar/Sidebar.jsx
  },
  plugins: [
    // 定义为开发环境
    new webpack.DefinePlugin({
      // 这里是 { NODE_ENV: '"development"' }
      'process.env': require('../config/dev.env')
    }),
    // 热更新插件
    new webpack.HotModuleReplacementPlugin(),
    // 热更新时显示具体的模块路径
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
    // 在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。
    new webpack.NoEmitOnErrorsPlugin(),
    // github仓库 https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      // inject 默认值 true,script标签位于html文件的 body 底部
      // body 通true, header, script 标签位于 head 标签内
      // false 不插入生成的 js 文件,只是单纯的生成一个 html 文件
      inject: true
    }),
    // copy custom static assets
    // 把static资源复制到相应目录。
    new CopyWebpackPlugin([
      {
        // 这里是 static
        from: path.resolve(__dirname, '../static'),
        // 这里是 static
        to: config.dev.assetsSubDirectory,
        // 忽略.开头的文件。比如这里的.gitkeep,这个文件是指空文件夹也提交到git
        ignore: ['.*']
      }
    ])
  ]
})
// 导出一个promise
module.exports = new Promise((resolve, reject) => {
  // process.env.PORT 可以在命令行指定端口号,比如PORT=2000 npm run dev,那访问就是http://localhost:2000
  // config.dev.port 这里配置是 8080
  portfinder.basePort = process.env.PORT || config.dev.port
  // 以配置的端口为基准,寻找可用的端口,比如:如果8080占用,那就8081,以此类推
  // github仓库 https://github.com/indexzero/node-portfinder
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // publish the new Port, necessary for e2e tests
      process.env.PORT = port
      // add port to devServer config
      devWebpackConfig.devServer.port = port

      // Add FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        // notifyOnErrors 这里配置是 true
        // onErrors 是一个函数,出错输出错误信息,系统原生的通知
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))

      resolve(devWebpackConfig)
    }
  })
})

build/utils.jsВспомогательная функция

надbuild/webpack.dev.conf.jsупомянул о введенииbuild/utils.jsвспомогательная функция.
Этот файл в основном записывает следующие служебные функции:
1,assetsPathвозвращает выходной путь,
2,cssLoadersвернуть соответствующийcss-loaderнастроить,
3.styleLoadersВозвращает конфигурацию соответствующего стиля обработки,
4.createNotifierCallbackОбратный вызов для оперативной информации при возникновении ошибки при создании службы запуска.

Конкретную конфигурацию можно увидеть в комментариях к этому файлу:

'use strict'
const path = require('path')
// 引入配置文件config/index.js
const config = require('../config')
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 引入package.json配置
const packageConfig = require('../package.json')
// 返回路径
exports.assetsPath = function (_path) {
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    // 二级目录 这里是 static
    ? config.build.assetsSubDirectory
    // 二级目录 这里是 static
    : config.dev.assetsSubDirectory

  // 生成跨平台兼容的路径
  // 更多查看Node API链接:https://nodejs.org/api/path.html#path_path_posix
  return path.posix.join(assetsSubDirectory, _path)
}

exports.cssLoaders = function (options) {
  // 作为参数传递进来的options对象
  // {
  //   // sourceMap这里是true
  //   sourceMap: true,
  //   // 是否提取css到单独的css文件
  //   extract: true,
  //   // 是否使用postcss
  //   usePostCSS: true
  // }
  options = options || {}

  const cssLoader = {
    loader: 'css-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  const postcssLoader = {
    loader: 'postcss-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  // generate loader string to be used with extract text plugin
  // 创建对应的loader配置
  function generateLoaders (loader, loaderOptions) {
    // 是否使用usePostCSS,来决定是否采用postcssLoader
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        // 合并 loaderOptions 生成options
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    // (which is the case during production build)
    if (options.extract) {
      // 如果提取使用ExtractTextPlugin插件提取
      // 更多配置 看插件中文文档:https://webpack.docschina.org/plugins/extract-text-webpack-plugin/
      return ExtractTextPlugin.extract({
        // 指需要什么样的loader去编译文件
        // loader 被用于将资源转换成一个 CSS 导出模块 (必填)
        use: loaders,
        // loader(例如 'style-loader')应用于当 CSS 没有被提取(也就是一个额外的 chunk,当 allChunks: false)
        fallback: 'vue-style-loader'
      })
    } else {
      return ['vue-style-loader'].concat(loaders)
    }
  }

  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
  return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders('less'),
    // sass indentedSyntax 语法缩进,类似下方格式
    // #main
    //   color: blue
    //   font-size: 0.3em
    sass: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')
  }
}

// Generate loaders for standalone style files (outside of .vue)
// 最终会返回webpack css相关的配置
exports.styleLoaders = function (options) {
  // {
  //   // sourceMap这里是true
  //   sourceMap: true,
  //   // 是否提取css到单独的css文件
  //   extract: true,
  //   // 是否使用postcss
  //   usePostCSS: true
  // }
  const output = []
  const loaders = exports.cssLoaders(options)

  for (const extension in loaders) {
    const loader = loaders[extension]
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      use: loader
    })
  }

  return output
}

// npm run dev 出错时, FriendlyErrorsPlugin插件 配置 onErrors输出错误信息
exports.createNotifierCallback = () => {
  // 'node-notifier'是一个跨平台系统通知的页面,当遇到错误时,它能用系统原生的推送方式给你推送信息
  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')
    })
  }
}

build/webpack.base.conf.js webpackБазовый файл конфигурации

надbuild/webpack.dev.conf.jsупомянул о введенииbuild/webpack.base.conf.jsэтоwebpackБазовый файл конфигурации.
Этот файл в основном выполняет следующие функции:
1. Внедрить различные плагины, конфигурации и т. д., которые вводятbuild/vue-loader.conf.jsсоответствующая конфигурация,
2. СоздатьeslintКонфигурация правил, включенная по умолчанию,
3. Экспортwebpackобъект конфигурации, который содержитcontext,Входentry, выходoutput,resolve,moduleвнизrules(правила обработки соответствующего файла) иnodeсоответствующая конфигурация и т. д.

Подробности смотрите в комментариях к этому файлу:

// 使用严格模式,更多严格模式可以查看
// [阮一峰老师的es标准入门](http://es6.ruanyifeng.com/?search=%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F&x=0&y=0#docs/function#%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F)
'use strict'
const path = require('path')
// 引入工具函数
const utils = require('./utils')
// 引入配置文件,也就是config/index.js文件
const config = require('../config')
// 引入vue-loader的配置文件
const vueLoaderConfig = require('./vue-loader.conf')
// 定义获取绝对路径函数
function resolve (dir) {
  return path.join(__dirname, '..', dir)
}
// 创建eslint配置
const createLintingRule = () => ({
  test: /\.(js|vue)$/,
  loader: 'eslint-loader',
  // 执行顺序,前置,还有一个选项是post是后置
  // 把 eslint-loader 的执行顺序放到最前面,防止其它 Loader 把处理后的代码交给 eslint-loader 去检查
  enforce: 'pre',
  // 包含文件夹
  include: [resolve('src'), resolve('test')],
  options: {
    // 使用友好的eslint提示插件
    formatter: require('eslint-friendly-formatter'),
    // eslint报错提示是否显示以遮罩形式显示在浏览器中
    // 这里showEslintErrorsInOverlay配置是false
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})

module.exports = {
  // 运行环境的上下文,就是实际的目录,也就是项目根目录
  context: path.resolve(__dirname, '../'),
  // 入口
  entry: {
    app: './src/main.js'
  },
  // 输出
  output: {
    // 路径 这里是根目录下的dist
    path: config.build.assetsRoot,
    // 文件名
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      // 这里是 /,但要上传到github pages等会路径不对,需要修改为./
      ? config.build.assetsPublicPath
      // 这里配置是 /
      : config.dev.assetsPublicPath
  },
  // Webpack 在启动后会从配置的入口模块出发找出所有依赖的模块,Resolve 配置 Webpack 如何寻找模块所对应的文件。
  resolve: {
    // 配置了这个,对应的扩展名可以省略
    extensions: ['.js', '.vue', '.json'],
    alias: {
      // 给定对象的键后的末尾添加 $,以表示精准匹配 node_modules/vue/dist/vue.esm.js
      // 引用 import Vue from 'vue'就是引入的这个文件最后export default Vue 导出的Vue;
      // 所以这句可以以任意大写字母命名 比如:import V from 'vue'
      'vue$': 'vue/dist/vue.esm.js',
      // src别名 比如 :引入import HelloWorld from '@/components/HelloWorld'
      '@': resolve('src'),
    }
  },
  // 定义一些文件的转换规则
  module: {
    rules: [
      // 是否使用eslint 这里配置是true
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/,
        // vue-loader中文文档:https://vue-loader-v14.vuejs.org/zh-cn/
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        // js文件使用babel-loader转换
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        // 图片文件使用url-loader转换
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          // 限制大小10000B(bytes)以内,转成base64编码的dataURL字符串
          limit: 10000,
          // 输出路径 img/名称.7位hash.扩展名
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        // 视频文件使用url-loader转换
        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]')
        }
      }
    ]
  },
  // 这里的node是一个对象,其中每个属性都是 Node.js 全局变量或模块的名称,每个 value 是以下其中之一
  // empty 提供空对象。
  // false 什么都不提供。
  // 更多查看 中文文档:https://webpack.docschina.org/configuration/node/
  node: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    // 防止webpack注入一些polyfill 因为Vue已经包含了这些。
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

build/vue-loader.conf.js vue-loaderконфигурационный файл

надbuild/webpack.dev.conf.jsупомянул о введенииbuild/vue-loader.conf.js.

Этот файл в основном экспортируетVue-loaderКонфигурация, Есть:loaders,cssSourceMap,cacheBusting,transformToRequire.

В частности, чтобы увидеть документ Примечания:

'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
  // 这里是true
  ? config.build.productionSourceMap
  // 这里是true
  : config.dev.cssSourceMap
// 更多配置 可以查看vue-loader中文文档:https://vue-loader-v14.vuejs.org/zh-cn/
module.exports = {
  // cssLoaders 生成相应loader配置,具体看utils文件中的cssLoader
  loaders: utils.cssLoaders({
    // 是否开启sourceMap,便于调试
    sourceMap: sourceMapEnabled,
    // 是否提取vue单文件的css
    extract: isProduction
  }),
  // 是否开启cssSourceMap,便于调试
  cssSourceMap: sourceMapEnabled,
  // 这里是true
  // 缓存破坏,进行sourceMap debug时,设置成false很有帮助。
  cacheBusting: config.dev.cacheBusting,
  // vue单文件中,在模板中的图片等资源引用转成require的形式。以便目标资源可以由 webpack 处理。
  transformToRequire: {
    video: ['src', 'poster'],
    source: 'src',
    img: 'src',
    // 默认配置会转换 <img> 标签上的 src 属性和 SVG 的 <image> 标签上的 xlink:href 属性。
    image: 'xlink:href'
  }
}

После чтения соответствующей конфигурации этих файлов соответствующая конфигурация среды разработки нанизывается вместе. вconfig/Конфигурация в папке была аннотирована авторомbuild/В соответствующем файле под папкой, так что отдельно объяснять не буду.

Оглядываясь назад,package.jsonизscriptsсерединаnpm run buildнастроить,node build/build.jsНа самом деле, используяnodeвыполнитьbuild/build.jsдокумент.

build/build.js npm run buildуказанный файл для выполнения

Этот файл в основном выполняет следующие функции:
1. Представьтеbuild/check-versionsфайл, проверитьnodeа такжеnpmверсия,
2. Внедрите соответствующие плагины и конфигурации, которые вводятwebpackКонфигурация производственной средыbuild/webpack.prod.conf.js,
3. Первый консольный выводloading,удалятьdistФайлы в каталоге, начало сборки, ошибка сборки и успешная сборка дадут соответствующую оперативную информацию.

Подробности смотрите в соответствующих примечаниях:

'use strict'
// 检查node npm的版本
require('./check-versions')()

process.env.NODE_ENV = 'production'
// 命令行中的loading
const ora = require('ora')
// 删除文件或文件夹
const rm = require('rimraf')
// 路径相关
const path = require('path')
// 控制台输入样式 chalk 更多查看:https://github.com/chalk/chalk
const chalk = require('chalk')
// 引入webpack
const webpack = require('webpack')
// 引入config/index.js
const config = require('../config')
// 引入 生产环境webpack配置
const webpackConfig = require('./webpack.prod.conf')

// 控制台输入开始构建loading
const spinner = ora('building for production...')
spinner.start()

// 删除原有构建输出的目录文件 这里是dist 和 static
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  // 如果出错,抛出错误
  if (err) throw err
  webpack(webpackConfig, (err, stats) => {
    // 关闭 控制台输入开始构建loading
    spinner.stop()
    // 如果出错,抛出错误
    if (err) throw err
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
      chunks: false,
      chunkModules: false
    }) + '\n\n')

    // 如果有错,控制台输出构建失败
    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'
    ))
  })
})

build/check-versionsэкзаменnodeа такжеnpmВерсия

упомянутый вышеbuild/check-versionsэкзаменnodeа такжеnpmверсии, этот файл в основном представляет некоторые плагины и конфигурации и, наконец, экспортирует функцию, которая выводит предупреждение, если версия не соответствует ожиданиям.

Подробности смотрите в этом комментарии к файлу конфигурации:

'use strict'
// 控制台输入样式 chalk 更多查看:https://github.com/chalk/chalk
const chalk = require('chalk')
// 语义化控制版本的插件 更多查看:https://github.com/npm/node-semver
const semver = require('semver')
// package.json配置
const packageConfig = require('../package.json')
// shell 脚本 Unix shell commands for Node.js 更多查看:https://github.com/shelljs/shelljs
const shell = require('shelljs')

function exec (cmd) {
  return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
  {
    name: 'node',
    currentVersion: semver.clean(process.version),
    // 这里配置是"node": ">= 6.0.0",
    versionRequirement: packageConfig.engines.node
  }
]
// 需要使用npm
if (shell.which('npm')) {
  versionRequirements.push({
    name: 'npm',
    currentVersion: exec('npm --version'),
    // 这里配置是"npm": ">= 3.0.0"
    versionRequirement: packageConfig.engines.npm
  })
}
// 导出一个检查版本的函数
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)
      )
    }
  }

  // 如果有警告,全部输出到控制台
  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)
  }
}

build/webpack.prod.conf.js webpackКонфигурация производственной среды

надbuild/build.jsКак уже упоминалось, этот файл конфигурации был представлен.
Этот файл в основном выполняет следующие функции:
1. Введите некоторые плагины и конфигурации, которые вводятbuild/webpack.base.conf.js webpackбазовый файл конфигурации,
2. ИспользуйтеDefinePluginопределить окружение,
3. Объедините базовую конфигурацию и определите собственную конфигурациюwebpackConfig, настроить некоторыеmodulesвнизrules,devtoolsнастроить,outputВыходная конфигурация, некоторая обработкаjs,извлекатьcss, компрессияcss, выходhtmlПлагины, извлечение общедоступного кода и т. д.plugins,
4. Если включеноgzip, а затем использовать соответствующую обработку плагина,
5. Если подключаемый модуль пакета анализа включен, используйтеwebpack-bundle-analyzer,
6. Наконец, экспортируйте эту конфигурацию.

Для получения дополнительной информации вы можете просмотреть комментарии к конфигурации в этом файле:

'use strict'
// 引入node路径相关
const path = require('path')
// 引入utils工具函数
const utils = require('./utils')
// 引入webpack
const webpack = require('webpack')
// 引入config/index.js配置文件
const config = require('../config')
// 合并webpack配置的插件
const merge = require('webpack-merge')
// 基本的webpack配置
const baseWebpackConfig = require('./webpack.base.conf')
// 拷贝文件和文件夹的插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 压缩处理HTML的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 压缩处理css的插件
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 压缩处理js的插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

// 用DefinePlugin定义环境
const env = process.env.NODE_ENV === 'testing'
  // 这里是 { NODE_ENV: '"testing"' }
  ? require('../config/test.env')
  // 这里是 { NODE_ENV: '"production"' }
  : require('../config/prod.env')
// 合并基本webpack配置
const webpackConfig = merge(baseWebpackConfig, {
  module: {
    // 通过styleLoaders函数生成样式的一些规则
    rules: utils.styleLoaders({
      // sourceMap这里是true
      sourceMap: config.build.productionSourceMap,
      // 是否提取css到单独的css文件
      extract: true,
      // 是否使用postcss
      usePostCSS: true
    })
  },
  // 配置使用sourceMap true 这里是 #source-map
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    // 这里是根目录下的dist
    path: config.build.assetsRoot,
    // 文件名称 chunkhash
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // chunks名称 chunkhash
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    // 定义具体是什么环境
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // 压缩js插件
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          // 警告
          warnings: false
          // 构建后的文件 常用的配置还有这些
          // 去除console.log 默认为false。  传入true会丢弃对console函数的调用。
          // drop_console: true,
          // 去除debugger
          // drop_debugger: true,
          // 默认为null. 你可以传入一个名称的数组,而UglifyJs将会假定那些函数不会产生副作用。
          // pure_funcs: [ 'console.log', 'console.log.apply' ],
        }
      },
      // 是否开启sourceMap 这里是true
      sourceMap: config.build.productionSourceMap,
      // 平行处理(同时处理)加快速度
      parallel: true
    }),
    // extract css into its own file
    // 提取css到单独的css文件
    new ExtractTextPlugin({
      // 提取到相应的文件名 使用内容hash contenthash
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      // Setting the following option to `false` will not extract CSS from codesplit chunks.
      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
      // allChunks 默认是false,true指提取所有chunks包括动态引入的组件。
      allChunks: true,
    }),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      // 这里配置是true
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      // 输出html名称
      filename: process.env.NODE_ENV === 'testing'
        ? 'index.html'
        // 这里是 根目录下的dist/index.html
        : config.build.index,
      // 使用哪个模板
      template: 'index.html',
      // inject 默认值 true,script标签位于html文件的 body 底部
      // body 通true, header, script 标签位于 head 标签内
      // false 不插入生成的 js 文件,只是单纯的生成一个 html 文件
      inject: true,
      // 压缩
      minify: {
        // 删除注释
        removeComments: true,
        // 删除空格和换行
        collapseWhitespace: true,
        // 删除html标签中属性的双引号
        removeAttributeQuotes: true
        // 更多配置查看html-minifier插件
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      // 在chunk被插入到html之前,你可以控制它们的排序。允许的值 ‘none’ | ‘auto’ | ‘dependency’ | {function} 默认为‘auto’.
      // dependency 依赖(从属)
      chunksSortMode: 'dependency'
    }),
    // keep module.id stable when vendor modules does not change
    // 根据代码内容生成普通模块的id,确保源码不变,moduleID不变。
    new webpack.HashedModuleIdsPlugin(),
    // enable scope hoisting
    // 开启作用域提升 webpack3新的特性,作用是让代码文件更小、运行的更快
    new webpack.optimize.ModuleConcatenationPlugin(),
    // split vendor js into its own file
    // 提取公共代码
    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
        )
      }
    }),
    // 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({
      // 把公共的部分放到 manifest 中
      name: 'manifest',
      // 传入 `Infinity` 会马上生成 公共chunk,但里面没有模块。
      minChunks: Infinity
    }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    // 提取动态组件
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      // 如果设置为 `true`,一个异步的  公共chunk 会作为 `options.name` 的子模块,和 `options.chunks` 的兄弟模块被创建。
      // 它会与 `options.chunks` 并行被加载。可以通过提供想要的字符串,而不是 `true` 来对输出的文件进行更换名称。
      async: 'vendor-async',
      // 如果设置为 `true`,所有  公共chunk 的子模块都会被选择
      children: true,
      // 最小3个,包含3,chunk的时候提取
      minChunks: 3
    }),

    // copy custom static assets
    // 把static资源复制到相应目录。
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        // 这里配置是static
        to: config.build.assetsSubDirectory,
        // 忽略.开头的文件。比如这里的.gitkeep,这个文件是指空文件夹也提交到git
        ignore: ['.*']
      }
    ])
  ]
})
// 如果开始gzip压缩,使用compression-webpack-plugin插件处理。这里配置是false
// 需要使用是需要安装 npm i compression-webpack-plugin -D
if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      // asset: 目标资源名称。 [file] 会被替换成原始资源。
      // [path] 会被替换成原始资源的路径, [query] 会被替换成查询字符串。默认值是 "[path].gz[query]"。
      asset: '[path].gz[query]',
      // algorithm: 可以是 function(buf, callback) 或者字符串。对于字符串来说依照 zlib 的算法(或者 zopfli 的算法)。默认值是 "gzip"。
      algorithm: 'gzip',
      // test: 所有匹配该正则的资源都会被处理。默认值是全部资源。
      // config.build.productionGzipExtensions 这里是['js', 'css']
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      // threshold: 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。
      threshold: 10240,
      // minRatio: 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。
      minRatio: 0.8
    })
  )
}

// 输出分析的插件 运行npm run build --report
// config.build.bundleAnalyzerReport这里是 process.env.npm_config_report
// build结束后会自定打开 http://127.0.0.1:8888 链接
if (config.build.bundleAnalyzerReport) {
  // 更多查看链接地址:https://www.npmjs.com/package/webpack-bundle-analyzer
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
// 当然也可以用官方提供的网站 http://webpack.github.io/analyse/#home
// 运行类似 webpack --profile --json > stats.json 命令
// 把生成的构建信息stats.json上传即可


// 最终导出 webpackConfig
module.exports = webpackConfig

На данный момент мы закончили анализpackage.jsonсерединаnpm run devа такжеnpm run buildдве команды. Сходства, связанные с тестами, опущены.

npm run lint,.eslintrc.jsКонфигураций не так много, больше можно посмотретьдокументация eslint на английском языкеилиeslintкитайский официальный сайт, так что игнорируй. Но упомяните, чтоeslintинтегрирован вgitРабочий процесс. может быть установленhusky,npm i husky -S. После установки настройтеpackage.jsonизscriptsв, настроитьprecommit, подробности следующим образом:

"scripts": {
  "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
  "precommit": "npm run lint",
},

После настройки каждый разgit commit -mSubmit проверит, проходит ли кодeslintПроверка, если проверка не проходит, отправка не выполняется. также можно настроитьprepush.huskyОн постоянно обновляется, и сейчас он может не совпадать с исходной конфигурацией, подробнее см.хриплый репозиторий github. Принципgit-hooks,pre-commitкрюк. правильноshellУчащиеся, знакомые со сценариями, также могут писать свои собственные.pre-commit. скопировано в проект.git/hooks/pre-commitсередина. нет зависимостейhuskyМешок. Наша компания используетshellсценарий.

Последнее упоминание.babelrcконфигурация в файле.

.babelrc babelСвязанная конфигурация

Настроены некоторые правила транскодирования. Вот две ссылки:babelанглийский официальный сайта такжеbabelкитайский официальный сайт.

Подробнее см. в примечаниях к конфигурации в файле:

{
  // presets指明转码的规则
  "presets": [
    // env项是借助插件babel-preset-env,下面这个配置说的是babel对es6,es7,es8进行转码,并且设置amd,commonjs这样的模块化文件,不进行转码
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  // plugins 属性告诉 Babel 要使用哪些插件,插件可以控制如何转换代码。
  // transform-vue-jsx 表明可以在项目中使用jsx语法,会使用这个插件转换
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  // 在特定的环境中所执行的转码规则,当环境变量是下面的test就会覆盖上面的设置
  "env": {
    // test 是提前设置的环境变量,如果没有设置BABEL_ENV则使用NODE_ENV,如果都没有设置默认就是development
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}

в файлеpresetsимеет конфигурациюenvа такжеstage-2, может не знаю что. Цитата здесьУглубленный веб-пакетКнига, третья глава,3-1использоватьES6Параграф в языковом разделе, объясните.

presetsсвойство говоритBabelКакие новые синтаксические функции используются в исходном коде, который нужно преобразовать, пресеты обеспечивают поддержку набора новых синтаксических функций, несколькоPresetsМожно накладывать.PresetsНа самом деле группаPluginsколлекция каждогоPluginЗавершите преобразование новой грамматики.PresetsсогласноECMAScriptОрганизованный по проекту, его обычно можно разделить на следующие три категории (книга относится к трем категориям, я нашел два пункта~~~):
1. Возможности, прописанные в стандарте ECMAScript.Поскольку новые функции добавляются в стандарт каждый год, их можно разделить на:
es2015 содержит новые функции, добавленные в 2015 году;
es2016 содержит новые функции, добавленные в 2016 году;
es2017 содержит новые функции, добавленные в 2017 году;
es2017 содержит новые функции, добавленные в 2017 году;
env содержит последние функции всех текущих стандартов ECMAScript.
2. Предложено сообществом, но еще не написаноECMAScriptСтандартные функции, которые делятся на следующие четыре:
stage0Просто хорошая и радикальная идея, сBabelПлагины реализуют поддержку этих функций, но не уверен, что они станут стандартными;
stage1Особенности, достойные включения в стандарт;
stage2Спецификация функций составлена ​​и будет включена в стандарт;
stage3Спецификация функции была завершена, и основные производители браузеров и «сообщества» приступили к ее реализации;
stage4Он будет добавлен в стандарт в следующем году.

На данный момент, даже если относительно полный анализ завершенVue-cli(Версияv2.9.3) построенwebpackПроект. Я надеюсь быть полезным.
Проект размещен в авторскойgithubначальство,Проанализируйте проект веб-пакета, созданный vue-cli@2.9.3.. Всем удобно клонировать и скачивать, или просматривать онлайн. Также попроситеstar ^_^, но и своего рода поощрение и поддержка автора.
Знания и возможности автора ограничены, если в статье что-то не так, пожалуйста, укажите~

резюме

1. Потребуется некоторое время, чтобы проанализировать их и прокомментировать построчно. Некоторые из них не очень ясны, пожалуйста, своевременно проверяйте соответствующие официальные документы и подключаемые документы (рекомендуется читать документы на английском языке и последние документы), но если документы написаны нечетко, вы можете поискать некоторые другие статьи в блоге. , которые относительно ясны.
2. Фронтенд-разработка идет слишком быстро, этоVue-cli@2.9.3 webpackверсия ещеv3.x, теперь официальная версия webpackv4.12.0, я верю, что скоро,Vue-cliПоддержка также будет выпущенаwebpack v4.xверсия,v3.0.0ужеbeta.16.
3. В доработке есть свободные мощности, и можно продолжить анализ новой версии.vue-cliпостроенwebpackПроект.

о

Автор: Чанг ИВакагаваНазвание смешано в реках и озерах. По дороге на фронт | Энтузиасты РРТ | Знаю очень мало, только хорошо учусь.
личный блог
segmentfaultПередняя колонка обзора, открылПередний планКолонка, добро пожаловать на внимание ~
Колонка самородков, добро пожаловать, обратите внимание~
Знайте переднюю колонку видения, открылПередний планКолонка, добро пожаловать на внимание ~
github blog, спроситьstar^_^~