一字一句的搞懂vue-cli之vue webpack template配置

webpack--神同樣的存在。不管寫了多少次,再次相見,還是初見。有的時候開發vue項目,對尤大的vue-cli感激不盡。可是,可是,可是。。。不是本身的東西,真的很不想折騰。因此,咱們就得深刻內部,cp them us。因此呢,就利用兩天時間,參考了一些他人的文章,查閱了一些官方的配置,就在此先稍微記錄一下。javascript

這份配置解析是基於最新版本的 vue webpack template。不過,我很是建議,先別看個人文章,本身一句一句的通讀一遍。而後再來瞅瞅,畢竟,碰撞的思惟才能創造新的發現。

vue webpack的配置文件仍是挺多的,下面是關於此配置的基本目錄結構:css

config
├── dev.env.js //dev環境變量配置
├── index.js // dev和prod環境的一些基本配置
└── prod.env.js // prod環境變量配置
build
├── build.js // npm run build所執行的腳本
├── check-versions.js // 檢查npm和node的版本
├── logo.png
├── utils.js // 一些工具方法,主要用於生成cssLoader和styleLoader配置
├── vue-loader.conf.js // vueloader的配置信息
├── webpack.base.conf.js // dev和prod的公共配置
├── webpack.dev.conf.js // dev環境的配置
└── webpack.prod.conf.js // prod環境的配置

下面咱們就按照以下的順序分析源碼:html

config/index.js -> build/utils.js -> build/vue-loader.conf.js -> build/webpack.base.conf.js -> build/webpack.dev.conf.js -> build/webpack.prod.conf.js -> build/check-versions.js -> build/build.jsvue

config/index.js: 一些基本屬性的配置(咱們能夠根據本身的須要來更改這些配置)

'use strict'
// 這個文件主要是對開發環境和生產環境的一個基本的配置
const path = require('path')

module.exports = {
  // 開發環境的一個基本配置
  dev: {
    // 編譯輸出的二級目錄
    assetsSubDirectory: 'static',
    // 編譯發佈的根目錄,可配置爲資源服務器域名或者cdn域名
    assetsPublicPath: '/',
    // 須要使用proxyTable代理的接口(能夠跨域)
    proxyTable: {},

    // 開發時候的訪問域名。能夠經過環境變量本身設置。
    host: 'localhost', // can be overwritten by process.env.HOST
    // 開發時候的端口。能夠經過環境變量PORT設定。若是端口被佔用了,會隨機分配一個未被使用的端口
    port: 8080, 
    // 是否自動打開瀏覽器
    autoOpenBrowser: false,
    // 下面兩個都是瀏覽器展現錯誤的方式
    //  在瀏覽器是否展現錯誤蒙層
    errorOverlay: true,
    // 是否展現錯誤的通知
    notifyOnErrors: true,

    // 這個是webpack-dev-servr的watchOptions的一個選項,指定webpack檢查文件的方式
    // 由於webpack使用文件系統去獲取文件改變的通知。在有些狀況下,這個可能不起做用。例如,當使用NFC的時候,
    // vagrant也會在這方面存在不少問題,在這些狀況下,使用poll選項(以輪詢的方式去檢查文件是否改變)能夠設定爲true
    // 或者具體的數值,指定文件查詢的具體週期。
    poll: false, 
    // 是否使用eslint loader去檢查代碼
    useEslint: true,
    
    // 若是設置爲true,在瀏覽器中,eslint的錯誤和警告會以蒙層的方式展示。
    showEslintErrorsInOverlay: false,

    /**
     * Source Maps
     */

    // source maps的格式
    devtool: 'eval-source-map',

    // 指定是否經過在文件名稱後面添加一個查詢字符串來建立source map的緩存
    cacheBusting: true,
    // 關閉css的source map
    cssSourceMap: false,
  },

  build: {
    // html文件的生成的地方
    index: path.resolve(__dirname, '../dist/index.html'),

    // 編譯生成的文件的目錄
    assetsRoot: path.resolve(__dirname, '../dist'),
    // 編譯生成的靜態文件的目錄
    assetsSubDirectory: 'static',
    // 編譯發佈的根目錄,可配置爲資源服務器域名或者cdn域名
    assetsPublicPath: '/',

    /**
     * Source Maps
     */

    productionSourceMap: true,
    
    devtool: '#source-map',
    
    // 是否開啓生產環境的gzip壓縮
    productionGzip: false,
    // 開啓gzip壓縮的文件的後綴名稱
    productionGzipExtensions: ['js', 'css'],

    // 若是這個選項是true的話,那麼則會在build後,會在瀏覽器中生成一份bundler報告
    bundleAnalyzerReport: process.env.npm_config_report
  }
}

 

build/utils.js: 主要用於生成css loader和style loader的一些方法

'use strict'
// 引入nodejs的path模塊,用於操做路徑
const path = require('path')
// 引入模板的配置文件,下面就須要去這個文件中看看有什麼基本的配置
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'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory

  // path.posix是path模塊跨平臺的實現(不一樣平臺的路徑表示是不同的)
  return path.posix.join(assetsSubDirectory, _path)
}

// 爲不一樣的css預處理器提供一個統一的生成方式,也就是統一處理各類css類型的打包問題。
// 這個是爲在vue文件中的style中使用的css類型
exports.cssLoaders = function (options) {
  options = options || {}

  // 打包css模塊
  const cssLoader = {
    loader: 'css-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  // 編譯postcss模塊
  const postcssLoader = {
    // 使用postcss-loader來打包postcss模塊
    loader: 'postcss-loader',
    // 配置source map
    options: {
      sourceMap: options.sourceMap
    }
  }

  // 建立loader加載器字符串,結合extract text插件使用
  /**
   * 
   * @param {loader的名稱} loader 
   * @param {loader對應的options配置對象} loaderOptions 
   */
  function generateLoaders (loader, loaderOptions) {
    // 經過usePostCSS 來標明是否使用了postcss
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    // 若是指定了具體的loader的名稱
    if (loader) {
      // 向loaders的數組中添加該loader對應的加載器
      // 一個很重要的地方就是,一個數組中的loader加載器,是從右向左執行的。
      loaders.push({
        // loader加載器的名稱
        loader: loader + '-loader',
        // 對應的加載器的配置對象
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // 若是明確指定了須要提取靜態文件,則使用
    // ExtractTextPlugin.extract({})來包裹咱們的各類css處理器。
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        // fallback這個選項咱們能夠這樣理解
        // webpack默認會按照loaders中的加載器從右向左調用編譯各類css類型文件。若是一切順利,在loaders中的
        // 各個加載器運行結束以後就會把css文件導入到規定的文件中去,若是不順利,則繼續使用vue-style-loader來處理
        // css文件
        fallback: 'vue-style-loader'
      })
    } else {
      // 若是沒有提取行爲,則最後再使用vue-style-loader處理css
      return ['vue-style-loader'].concat(loaders)
    }
  }

  return {
    // css-loader
    css: generateLoaders(),
    // postcss-loader
    postcss: generateLoaders(),
    // less-loader
    less: generateLoaders('less'),
    // sass-loader 後面的選項代表sass使用的是縮進的愈發
    sass: generateLoaders('sass', { indentedSyntax: true }),
    // scss-loader
    scss: generateLoaders('sass'),
    // stylus-loader stylus文件有兩種後綴名.stylus和styl
    stylus: generateLoaders('stylus'),
    // stylus-loader
    styl: generateLoaders('stylus')
  }
}

// 使用這個函數,爲那些獨立的style文件建立加載器配置。
exports.styleLoaders = function (options) {
  // 保存加載器配置的變量
  const output = []
  // 獲取全部css文件類型的loaders
  const loaders = exports.cssLoaders(options)

  for (const extension in loaders) {
    const loader = loaders[extension]
    // 生成對應的loader配置
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      use: loader
    })
  }

  return output
}

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
      icon: path.join(__dirname, 'logo.png')
    })
  }
}

 

build/vue-loader.conf.js:vue-loader的一些基本配置

'use strict'
const utils = require('./utils')
const config = require('../config')
// 設置是否是生產環境
const isProduction = process.env.NODE_ENV === 'production'
// 根據不一樣的環境,引入不一樣的source map配置文件
const sourceMapEnabled = isProduction
  ? config.build.productionSourceMap
  : config.dev.cssSourceMap

module.exports = {
  // vue文件中的css loader配置
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    // 生產環境下就會把css文件抽取到一個獨立的文件中
    extract: isProduction
  }),
  // css source map文件的配置
  cssSourceMap: sourceMapEnabled,
  // css source map文件緩存控制變量
  cacheBusting: config.dev.cacheBusting,
  transformToRequire: {
    video: ['src', 'poster'],
    source: 'src',
    img: 'src',
    image: 'xlink:href'
  }
}

 

build/weback.base.conf.js:dev和prod環境下的公共配置

'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
// 生成相對於根目錄的絕對路徑
function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

// eslint的規則
const createLintingRule = () => ({
  // 對.js和.vue結尾的文件進行eslint檢查
  test: /\.(js|vue)$/,
  // 使用eslint-loader
  loader: 'eslint-loader',
  // enforce的值多是pre和post。其中pre有點和webpack@1中的preLoader配置含義類似。
  // post和v1中的postLoader配置含義類似。表示loader的調用時機
  // 這裏表示在調用其餘loader以前須要先調用這個規則進行代碼風格的檢查
  enforce: 'pre',
  // 須要進行eslint檢查的文件的目錄存在的地方
  include: [resolve('src'), resolve('test')],
  // eslint-loader配置過程當中須要指定的選項
  options: {
    // 文件風格的檢查的格式化程序,這裏使用的是第三方的eslint-friendly-formatter
    formatter: require('eslint-friendly-formatter'),
    // 是否須要eslint輸出警告信息
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})

// 下面就是webpack基本的配置信息(能夠當即成是開發環境和生產環境公共的配置)
module.exports = {
  // webpack解析文件時候的根目錄(若是把webpack.config.js)放在了項目的根目錄下面,這個配置能夠省略
  context: path.resolve(__dirname, '../'),
  // 指定項目的入口文件
  entry: {
    app: './src/main.js'
  },
  // 項目的輸出配置
  output: {
    // 項目build的時候,生成的文件的存放路徑(這裏的路徑是../dist)
    path: config.build.assetsRoot,
    // 生成文件的名稱
    filename: '[name].js',
    // 輸出解析文件的目錄,url 相對於 HTML 頁面(生成的html文件中,css和js等靜態文件的url前綴)
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  // 配置模塊解析時候的一些選項
  resolve: {
    // 指定哪些類型的文件能夠引用的時候省略後綴名
    extensions: ['.js', '.vue', '.json'],
    // 別名,在引入文件的時候可使用
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      // 能夠在引入文件的時候使用@符號引入src文件夾中的文件
      '@': resolve('src'),
    }
  },
  // 下面是針對具體的模塊進行的具體的配置
  // 下面的配置語法採用的是version >= @2的版本
  module: {
    // rules是一個數組,其中的每個元素都是一個對象,這個對象是針對具體類型的文件進行的配置。
    rules: [
      // .vue文件的配置
      {
        // 這個屬性是一個正則表達式,用於匹配文件。這裏匹配的是.vue文件
        test: /\.vue$/,
        // 指定該種類型文件的加載器名稱
        loader: 'vue-loader',
        // 針對此加載器的具體配置
        // 針對前面的分析,這個配置對象中包含了各類css類型文件的配置,css source map的配置 以及一些transform的配置
        options: vueLoaderConfig
      },
      {
        // .js文件的配置
        test: /\.js$/,
        // js文件的處理主要使用的是babel-loader。在這裏沒有指定具體的編譯規則,babel-loader會自動
        // 讀取根目錄下面的.babelrc中的babel配置用於編譯js文件
        /**
         * {
         * // 使用的預設
            "presets": [
              // babel-preset-env: 根據你所支持的環境自動決定具體類型的babel插件
              ["env", {
                // modules設置爲false,不會轉換module
                "modules": false
              }],
              // babel-preset-stage-2: 可使用全部>=stage2語法
              "stage-2"
            ],
            // 使用的插件
            // babel-plugin-transform-runtime: 只會對es6的語法進行轉換而不會對新的api進行轉換
            // 若是須要支持新的api,請引入babel-polyfill
            "plugins": ["transform-runtime"]
          }

         */
        loader: 'babel-loader',
        // 指定須要進行編譯的文件的路徑
        // 這裏表示只對src和test文件夾中的文件進行編譯
        include: [resolve('src'), resolve('test')]
      },
      {
        // 對圖片資源進行編譯的配置
        // 指定文件的類型
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        // 使用url-loader進行文件資源的編譯
        loader: 'url-loader',
        // url-loader的配置選項
        options: {
          // 文件的大小小於10000字節(10kb)的時候會返回一個dataUrl
          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]')
        }
      }
    ]
  },
  // 這些選項用於配置polyfill或mock某些node.js全局變量和模塊。
  // 這可使最初爲nodejs編寫的代碼能夠在瀏覽器端運行
  node: {
    // 這個配置是一個對象,其中的每一個屬性都是nodejs全局變量或模塊的名稱
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    // false表示什麼都不提供。若是獲取此對象的代碼,可能會由於獲取不到此對象而觸發ReferenceError錯誤
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    // 設置成empty則表示提供一個空對象
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

 

build/weboack.dev.conf.js:dev環境的配置

'use strict'
// 首先引入的是一些工具方法,下面咱們就須要去util文件種看一下有哪些對應的工具方法
const utils = require('./utils')
// 引入webpack模塊
const webpack = require('webpack')
// 引入配置文件
// 這個配置文件中包含了一些dev和production環境的基本配置
const config = require('../config')
// 引入webpack-merge模塊。這個模塊用於把多個webpack配置合併成一個配置,後面的配置會覆蓋前面的配置。
const merge = require('webpack-merge')
// 引入webpack的基本設置,這個設置文件包含了開發環境和生產環境的一些公共配置
const baseWebpackConfig = require('./webpack.base.conf')
// 用於生成html文件的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 這個插件可以更好的在終端看到webpack運行時的錯誤和警告等信息。能夠提高開發體驗。
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// 查找一個未使用的端口
const portfinder = require('portfinder')

// 獲取host環境變量,用於配置開發環境域名
const HOST = process.env.HOST
// 獲取post環境變量,用於配置開發環境時候的端口號
const PORT = process.env.PORT && Number(process.env.PORT)

// 開發環境的完整的配置文件,
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    // 爲那些獨立的css類型文件添加loader配置(沒有寫在vue文件的style標籤中的樣式)
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  // 開發環境使用'eval-source-map'模式的source map
  // 由於速度快
  devtool: config.dev.devtool,

  // these devServer options should be customized in /config/index.js
  // 下面是對webpack-dev-server選項的基本配置,這些配置信息,咱們能夠在/config/index.js
  // 文件中進行自定義配置。
  devServer: {
    // 用於配置在開發工具的控制檯中顯示的日誌級別
    // 注意這個不是對bundle的錯誤和警告的配置,而是對它生成以前的消息的配置
    clientLogLevel: 'warning',
    // 表示當使用html5的history api的時候,任意的404響應都須要被替代爲index.html
    historyApiFallback: true,
    // 啓用webpack的熱替換特性
    hot: true,
    // 一切服務都須要使用gzip壓縮
    // 能夠在js,css等文件的response header中發現有Content-Encoding:gzip響應頭
    compress: true,
    // 指定使用一個 host。默認是 localhost
    // 若是但願服務器外部能夠訪問(經過咱們電腦的ip地址和端口號訪問咱們的應用)
    // 能夠指定0.0.0.0
    host: HOST || config.dev.host,
    // 指定要監聽請求的端口號
    port: PORT || config.dev.port,
    // 是否自動打開瀏覽器
    open: config.dev.autoOpenBrowser,
    // 當編譯出現錯誤的時候,是否但願在瀏覽器中展現一個全屏的蒙層來展現錯誤信息
    overlay: config.dev.errorOverlay
    // 表示只顯示錯誤信息而不顯示警告信息
    // 若是二者都但願顯示,則把這兩項都設置爲true
      ? { warnings: false, errors: true }
      // 設置爲false則表示啥都不顯示
      : false,
      // 指定webpack-dev-server的根目錄,這個目錄下的全部的文件都是能直接經過瀏覽器訪問的
      // 推薦和output.publicPath設置爲一致
    publicPath: config.dev.assetsPublicPath,
    // 配置代理,這樣咱們就能夠跨域訪問某些接口
    // 咱們訪問的接口,若是符合這個選項的配置,就會經過代理服務器轉發咱們的請求
    proxy: config.dev.proxyTable,
    // 啓用 quiet 後,除了初始啓動信息以外的任何內容都不會被打印到控制檯。這也意味着來自 webpack 的錯誤或警告在控制檯不可見。
    quiet: true, // necessary for FriendlyErrorsPlugin
    // 與監視文件相關的控制選項。
    watchOptions: {
      // 若是這個選項爲true,會以輪詢的方式檢查咱們的文件的變更,效率很差
      poll: config.dev.poll,
    }
  },
  plugins: [
    // 建立一個在編譯時能夠配置的全局變量
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    // 啓用熱替換模塊
    // 記住,咱們永遠不要再生產環境中使用hmr
    new webpack.HotModuleReplacementPlugin(),
    // 這個插件的主要做用就是在熱加載的時候直接返回更新文件的名稱,而不是文件的id
    new webpack.NamedModulesPlugin(),
    // 使用這個插件能夠在編譯出錯的時候來跳過輸出階段,這樣能夠確保輸出資源不會包含錯誤。
    new webpack.NoEmitOnErrorsPlugin(),

    // 這個插件主要是生成一個html文件
    new HtmlWebpackPlugin({
      // 生成的html文件的名稱
      filename: 'index.html',
      // 使用的模板的名稱
      template: 'index.html',
      // 將全部的靜態文件都插入到body文件的末尾
      inject: true
    }),
  ]
})

module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  // 這種獲取port的方式會返回一個promise
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // 把獲取到的端口號設置爲環境變量PORT的值
      process.env.PORT = port
      // 從新設置webpack-dev-server的端口的值
      devWebpackConfig.devServer.port = port

      // 將FriendlyErrorsPlugin添加到webpack的配置文件中
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        // 編譯成功時候的輸出信息
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        // 當編譯出錯的時候,根據config.dev.notifyOnErrors來肯定是否須要在桌面右上角顯示錯誤通知框
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))
      // resolve咱們的配置文件
      resolve(devWebpackConfig)
    }
  })
})

 

build/webpack.prod.conf.js:prod環境的基本配置

'use strict'
// 引入path模塊
const path = require('path')
// 引入工具方法
const utils = require('./utils')
// 引入webpack模塊
const webpack = require('webpack')
// 引入基本的配置
const config = require('../config')
// 引入webpack-merge模塊
const merge = require('webpack-merge')
// 引入開發環境和生產環境公共的配置
const baseWebpackConfig = require('./webpack.base.conf')
// 引入copy-webpack-plugin模塊
// 這個模塊主要用於在webpack中拷貝文件和文件夾
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 引入html-webpack-plugin插件
// 這個插件主要是用於基於模版生成html文件的
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入extract-text-webpack-plugin插件
// 這個插件主要是用於將入口中全部的chunk,移到獨立的分離的css文件中
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 引入optimize-css-assets-webpack-plugin插件
// 這個插件主要是用於壓縮css模塊的
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 引入uglifyjs-webpack-plugin插件
// 這個插件主要是用於壓縮js文件的
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// 引入用於生產環境的一些基本變量
const env = require('../config/prod.env')

// 合併公共配置和生產環境獨有的配置並返回一個用於生產環境的webpack配置文件
const webpackConfig = merge(baseWebpackConfig, {
  // 用於生產環境的一些loader配置
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      // 在生產環境中使用extract選項,這樣就會把thunk中的css代碼抽離到一份獨立的css文件中去
      extract: true,
      usePostCSS: true
    })
  },
  // 配置生產環境中使用的source map的形式。在這裏,生產環境使用的是#source map的形式
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    // build所產生的文件的存放的文件夾地址
    path: config.build.assetsRoot,
    // build以後的文件的名稱
    // 這裏[name]和[chunkhash]都是佔位符
    // 其中[name]指的就是模塊的名稱
    // [chunkhash]chunk內容的hash字符串,長度爲20
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // [id]也是一個佔位符,表示的是模塊標識符(module identifier)
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // 壓縮javascript的插件
    new UglifyJsPlugin({
      // 壓縮js的時候的一些基本配置
      uglifyOptions: {
        // 配置壓縮的行爲
        compress: {
          // 在刪除未使用的變量等時,顯示警告信息,默認就是false
          warnings: false
        }
      },
      // 使用 source map 將錯誤信息的位置映射到模塊(這會減慢編譯的速度)
      // 並且這裏不能使用cheap-source-map
      sourceMap: config.build.productionSourceMap,
      // 使用多進程並行運行和文件緩存來提升構建速度
      parallel: true
    }),

    // 提取css文件到一個獨立的文件中去
    new ExtractTextPlugin({
      // 提取以後css文件存放的地方
      // 其中[name]和[contenthash]都是佔位符
      // [name]就是指模塊的名稱
      // [contenthash]根據提取文件的內容生成的 hash
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      
      // 從全部額外的 chunk(additional chunk) 提取css內容
      // (默認狀況下,它僅從初始chunk(initial chunk) 中提取)
      // 當使用 CommonsChunkPlugin 而且在公共 chunk 中有提取的 chunk(來自ExtractTextPlugin.extract)時
      // 這個選項須要設置爲true
      allChunks: false,
    }),
    // duplicated CSS from different components can be deduped.
    // 使用這個插件壓縮css,主要是由於,對於不一樣組件中相同的css能夠剔除一部分
    new OptimizeCSSPlugin({
      // 這個選項的全部配置都會傳遞給cssProcessor
      // cssProcessor使用這些選項決定壓縮的行爲
      cssProcessorOptions: config.build.productionSourceMap
      // safe我不是很明白是什麼意思???求留言告知。。。
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    
    // 建立一個html文件
    new HtmlWebpackPlugin({
      // 生成的文件的名稱
      filename: config.build.index,
      // 使用的模板的名稱
      template: 'index.html',
      // 把script和link標籤放在body底部
      inject: true,
      // 配置html的壓縮行爲
      minify: {
        // 移除註釋
        removeComments: true,
        // 去除空格和換行
        collapseWhitespace: true,
        // 儘量移除屬性中的引號和空屬性
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // 控制chunks的順序,這裏表示按照依賴關係進行排序
      // 也能夠是一個函數,本身定義排序規則
      chunksSortMode: 'dependency'
    }),
    // keep module.id stable when vender modules does not change
    // 根據模塊的相對路徑生成一個四位數的hash做爲模塊id
    new webpack.HashedModuleIdsPlugin(),

    // webpack2處理過的每個模塊都會使用一個函數進行包裹
    // 這樣會帶來一個問題:下降瀏覽器中JS執行效率,這主要是閉包函數下降了JS引擎解析速度。
    // webpack3中,經過下面這個插件就可以將一些有聯繫的模塊,
    // 放到一個閉包函數裏面去,經過減小閉包函數數量從而加快JS的執行速度。
    new webpack.optimize.ModuleConcatenationPlugin(),

    // 這個插件用於提取多入口chunk的公共模塊
    // 經過將公共模塊提取出來以後,最終合成的文件可以在最開始的時候加載一次
    // 而後緩存起來供後續使用,這會帶來速度上的提高。
    new webpack.optimize.CommonsChunkPlugin({
      // 這是 common chunk 的名稱
      name: 'vendor',
      // 把全部從mnode_modules中引入的文件提取到vendor中
      minChunks (module) {
      
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),

    // 爲了將項目中的第三方依賴代碼抽離出來,官方文檔上推薦使用這個插件,當咱們在項目裏實際使用以後,
    // 發現一旦更改了 app.js 內的代碼,vendor.js 的 hash 也會改變,那麼下次上線時,
    // 用戶仍然須要從新下載 vendor.js 與 app.js——這樣就失去了緩存的意義了。因此第二次new就是解決這個問題的
    // 參考:https://github.com/DDFE/DDFE-blog/issues/10
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

    // copy custom static assets
    // 拷貝靜態資源到build文件夾中
    new CopyWebpackPlugin([
      {
        // 定義要拷貝的資源的源目錄
        from: path.resolve(__dirname, '../static'),
        // 定義要拷貝的資源的目標目錄
        to: config.build.assetsSubDirectory,
        // 忽略拷貝指定的文件,可使用模糊匹配
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  // 若是開啓了生產環境的gzip
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      // 目標資源的名稱
      // [path]會被替換成原資源路徑
      // [query]會被替換成原查詢字符串
      asset: '[path].gz[query]',
      // gzip算法
      // 這個選項能夠配置成zlib模塊中的各個算法
      // 也能夠是(buffer, cb) => cb(buffer)
      algorithm: 'gzip',
      // 處理全部匹配此正則表達式的資源
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      // 只處理比這個值大的資源
      threshold: 10240,
      // 只有壓縮率比這個值小的資源纔會被處理
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  // 若是須要生成一分bundle報告,則須要使用下面的這個插件
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

 

build/check-versions.js:檢查npm和node的版本

'use strict'
// 在終端爲不一樣字體顯示不一樣的風格
const chalk = require('chalk')
// 解析npm包的version
const semver = require('semver')
// 引入package.json文件
const packageConfig = require('../package.json')
// node版本的uninx shell命令
const shell = require('shelljs')

// 執行命令的函數
function exec (cmd) {
  return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
  {
    name: 'node',
    // node的版本
    // process.version就是node的版本
    // semver.clean('v8.8.0') => 8.8.0
    currentVersion: semver.clean(process.version),
    // package.json中定義的node版本的範圍 
    versionRequirement: packageConfig.engines.node
  }
]

// 至關於 which npm
if (shell.which('npm')) {
  // 若是npm命令存在的話
  versionRequirements.push({
    name: 'npm',
    // 檢查npm的版本 => 5.4.2
    currentVersion: exec('npm --version'),
    // package.json中定義的npm版本
    versionRequirement: packageConfig.engines.npm
  })
}

module.exports = function () {
  const warnings = []

  for (let i = 0; i < versionRequirements.length; i++) {
    const mod = versionRequirements[i]

    // semver.satisfies()進行版本之間的比較
    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
      // 若是現有的npm或者node的版本比定義的版本低,則生成一段警告
      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/build.js: build項目

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

// 設置環境變量NODE_ENV的值是production
process.env.NODE_ENV = 'production'

// 終端的spinner
const ora = require('ora')
// node.js版本的rm -rf
const rm = require('rimraf')
// 引入path模塊
const path = require('path')
// 引入顯示終端顏色模塊
const chalk = require('chalk')
// 引入webpack模塊
const webpack = require('webpack')
// 引入基本的配置文件
const config = require('../config')
// 引入webpack在production環境下的配置文件
const webpackConfig = require('./webpack.prod.conf')

// 
const spinner = ora('building for production...')
spinner.start()

// 刪除打包目標目錄下的文件
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  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')

    // 若是打包出現錯誤
    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'
    ))
  })
})

 

 

看着不錯,就轉過來了。html5

轉載自:https://segmentfault.com/a/1190000012472099java

相關文章
相關標籤/搜索