vue-cli (vue2.5.2&webpack3.6.0) 配置文件全註釋

vue-cli 配置文件註釋

  • 新的項目組同事整了一個webpack3.*的腳手架
  • (先前本身弄了一個2.0的腳手架)裏面的配置項摸索了一遍,就想看看3.*在配置文件上面update了什麼
  • 工做閒時花了一點時間將配置文件中都加上了註釋,相信看了就和我同樣從不那麼太懂到有那麼一點懂了. ^^
  • 平時用的blog寫這種文章十分不友好,因此記錄在這邊順便和你們一塊兒學習(時不時會update一些修正項在最後面)
  • 若是有須要的朋友能夠上個人github下載,裏面還有舊的2.0版本的腳手架.github連接

基礎準備

  • node v7.10.0
  • vue v2.8.2
  1. vue init webpack 項目名字
  2. 各類下一步,(沒啥用eslint 沒使用測試的那個模塊)
  3. 生成的目錄以下腳手架文件目錄

基礎配置文件(webpack.base.conf.js)

這裏的文件是公用的一些基礎配置項
'use strict'
// 基礎配置文件

const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')

// 定義的路徑拼接方法(join直接的拼接, path.resolve等於在終端輸入路徑)
function resolve (dir) {
  // http://nodejs.cn/api/path.html#path_path_join_paths
  return path.join(__dirname, '..', dir)
}

const entries = utils.getEntry('./src/module/*/*.js');

/*
    __dirname 指的是當前你這個當前文件在 硬盤文件夾全路徑 
    例如這個base.conf.js文件是在 build文件夾裏
    那麼 __dirname = /硬盤路徑/build
*/

module.exports = {
  // 基礎目錄,絕對路徑,用於從配置中解析入口起點
  context: path.resolve(__dirname, '../'),
  /*
    起點或是應用程序的起點入口。從這個起點開始,應用程序啓動執行。若是傳遞一個數組,那麼數組的每一項都會執行。

    若是是多入口的狀況
    entry: {
      home: "./home.js",
      about: "./about.js",
      contact: "./contact.js"
    }

    入口對象的key值是結合dev.conf.js中的HtmlWebpackPlugin 做爲url訪問的路徑
    例如
    entry: {
      home: "./home.js"
    }
    訪問路徑:localhost:port/home.html

    entry: {
      fgh/home: './home.js'
    }
    訪問路徑:localhost:port/fgh/home.html

  */
  entry: entries,
  // 輸出
  output: {
    // 編譯輸出的靜態資源根路徑
    path: config.build.assetsRoot,
    // 此選項決定了每一個輸出 bundle 的名稱。 
    // [name]是指入口名稱 [id]是指chunk id [hash]是指構建完的hash [chunkhash]是指每一個內容的hash
    filename: '[name].js',
    // 正式發佈環境下編譯輸出的上線路徑的根路徑
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  // 選項能設置模塊如何被解析
  resolve: {
    // 自動補全對應模塊的後綴
    extensions: ['.js', '.vue', '.json'],
    // 路徑別名
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  /*
    https://doc.webpack-china.org/plugins/provide-plugin/#src/components/Sidebar/Sidebar.jsx
    ProvidePlugin 能夠全局加載模塊,例以下面若是想全局加載一個jq
  */
  // plugins: [
  //   new webpack.ProvidePlugin({
  //     $: 'jquery'  
  //   })
  // ],
  
  // 各類loader的配置
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      /*
        關於.babelrc的配置 
        部分可參考
        https://segmentfault.com/a/1190000008159877
        https://babeljs.cn/docs/plugins/preset-env/
        https://doc.webpack-china.org/loaders/babel-loader/

        1.babel-preset-env 是指動態的require瀏覽器所缺的轉換babel插件.
        這個動態是經過文件裏面的配置,
        "env", {
          //是否將模塊編譯爲 amd cmd commonjs等
          "modules": false,
          "targets": {
            //指瀏覽器最新的2個版本 或者safari大於7的版本 >5%是指 市場率超過5%的瀏覽器
            "browsers": ["last 2 versions", "safari >= 7"]
          }
        }
        若是用了env 沒有加任何配置的話 那麼默認與 babel-preset-latest同樣

        2.babel-preset-stage 有4個版本
        Stage 0 - 稻草人: 只是一個想法,多是 babel 插件。
        Stage 1 - 提案: 初步嘗試。
        Stage 2 - 初稿: 完成初步規範。
        Stage 3 - 候選: 完成規範和瀏覽器初步實現。
        Stage 4(隱藏版本表示已經完成 將會在新的一版所發佈) 等同於es2015 es2016...

        3.在plugin中有 babel-plugin-transform-runtime 是動態的模塊加載所需的轉換模塊
        由於如文檔所說
        Babel 幾乎能夠編譯全部時新的 JavaScript 語法,但對於 APIs 來講卻並不是如此。
        例如: Promise、Set、Map 等新增對象,Object.assign、Object.entries等靜態方法。

        --說到runtime就會提到babel-polyfill
        (babel-polyfill 的作法是將全局對象統統污染一遍)

        babel-runtime 更像是分散的 polyfill 模塊,只會在模塊裏單獨引入須要用到的api, 不會影響全局,
        例子: 在模塊中 import Promise from 'babel-runtime/core/promise'

        可是每一個模塊單獨這樣引入也是麻煩, 因此能夠經過配置babel-plugin-transform-runtime來簡單操做

        --- 有一個小細節你們注意
        api中說到, babel-plugin-transform-runtime默認是和babel-runtime捆綁出現, 前面是開發依賴,後面是生產依賴.
        可是vue-cli構造出來的項目中,package.json裏面並無在生產依賴中出現babel-runtime??

        後面請教了人,
        原來在node_module中 babel-plugin-transform-runtime這個包裏面的package.json裏面已經把babel-runtime加入了生產.
        因此當install的時候會自動將一連串的東西都裝上!!

        4.還有一個縮短build的構造時間, 在下面的babel-loader裏面去 exclude掉整個 node_modules的文件夾. 能夠縮短1半時間..
      */
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          // 限制300kb如下的圖片均變爲base64. 否則若是使用background-image 打包以後會找不到資源 (limit是按照字節來)
          limit: 307200,
          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]')
        }
      }
    ]
  },
  /*
    https://doc.webpack-china.org/configuration/node/#node
    每一個屬性都是 Node.js 全局變量或模塊的名稱
    true:提供 polyfill。
    false: 什麼都不提供。
    "empty":提供空對象。
  */
  node: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    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'
  }
}

開發環境(webpack.dev.conf.js)

'use strict'
// 開發模式配置文件

// 引入工具集合的文件
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
// 合併插件相似object.assign合併公共部分
const merge = require('webpack-merge')
// node的path工具函數
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
// webpack 複製文件和文件夾的插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 自動生成 html 而且注入到 .html 文件中的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// 自動檢索下一個可用端口
const portfinder = require('portfinder')

/*
  http://nodejs.cn/api/process.html#process_process_env
  process.env 是在node環境中 返回一個包含用戶環境信息的對象。

  ------
  這裏的p.e屬性是node環境原生的,與下面用webpack.d*去定義出來不一樣,
  要改變裏面的值可參考 ./build.js
  直接
  process.env.NODE_ENV = 'production'
  ------

*/

// 獲取在process.env 定義的host和port
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)

// 多頁面html路徑集合
const pages = utils.getEntry('./src/module/*/*.html');

const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    // css-loader的加工
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  
  // 檢測開發環境下是否生成source map(而且生成的模式是怎麼樣)
  devtool: config.dev.devtool,

  /*
    https://doc.webpack-china.org/configuration/dev-server/#src/components/Sidebar/Sidebar.jsx
    webpack-dev-server 的配置

    package.json中
    "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
    --progess 將運行進度輸出到控制檯。即npm run dev 顯示module 的loading
    --inline  應用程序啓用內聯模式(inline mode)。
              這意味着一段處理實時重載的腳本被插入到你的包(bundle)中,而且構建消息將會出如今瀏覽器控制檯
    (--inline = false 關閉這種模式 那麼將不會出現修改代碼後實時刷新)
  */
  devServer: {
    // 開發過程當中,可配置控制檯顯示的內容
    clientLogLevel: 'warning',
    // History API 當遇到 404 響應時會被替代爲 index.html
    historyApiFallback: {
      rewrites: [
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    /*
      啓用 webpack 的模塊熱替換特性

      api提到,打開了這個選項 webpack會自動加載HMR的相關內容(HotModuleReplacement),因此不須要額外的配置.

      --------
      與舊版的vue-cli+webpack2.0的 dev-middleware結合hot-middleware 實現相似的功能
      若是你使用了 webpack-dev-middleware 而沒有使用 webpack-dev-server,
      請使用 webpack-hot-middleware package 包,以在你的自定義服務或應用程序上啓用 HMR。
      --------

    */
    hot: true,
    // 告訴服務器從哪裏提供內容。
    contentBase: false, // since we use CopyWebpackPlugin.
    // 一切服務都啓用gzip 壓縮
    compress: true,
    host: HOST || config.dev.host,
    port: PORT || config.dev.port,
    // 是否自動打開瀏覽器
    // openPage這個屬性能夠配置默認打開瀏覽器的頁面
    open: config.dev.autoOpenBrowser,
    // 是否全屏彈窗的形式顯示編譯過程當中的錯誤
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,
    // 指的是url的訪問路徑前綴
    publicPath: config.dev.assetsPublicPath,
    // 代理
    proxy: config.dev.proxyTable,
    // 除了初始啓動信息以外的任何內容都不會被打印到控制檯
    quiet: true, // necessary for FriendlyErrorsPlugin
    // 與監視文件相關的控制選項。
    watchOptions: {
      poll: config.dev.poll,
    }
  },
  plugins: [
    /*
      https://doc.webpack-china.org/plugins/define-plugin/
      容許在環境中產生一個全局變量, 例以下面'process.env', 就等於隔壁文件夾 dev.env.js export出來的內容
      具體的規則看上方api

      可是如下定義的變量在配置文件中去引用會報錯,只容許在服務中編寫的代碼中使用
    */
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),

    /*
      模塊熱替換(Hot Module Replacement 或 HMR)

      當上面的 devServer中 hot:true時, 這個模塊必須存在,否則webpack會報錯.
      這個模塊結合上面的hot是用於,
      檢測到頁面更新,在不刷新頁面的狀況下替換內容,
      若是hot: true與這個模塊均不存在, 則跟舊版本的 dev-middleware/hot-*同樣,修改即會刷新
    */
    new webpack.HotModuleReplacementPlugin(),

    // 當開啓 HMR 的時候使用該插件會顯示模塊的相對路徑,建議用於開發環境。
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.

    // 在編譯出現錯誤時,使用 NoEmitOnErrorsPlugin 來跳過輸出階段。這樣能夠確保輸出資源不會包含錯誤。
    new webpack.NoEmitOnErrorsPlugin(),
    
    /*
      https://doc.webpack-china.org/plugins/html-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
      該插件將爲你生成一個HTML5文件

      配置項文檔
      https://github.com/jantimon/html-webpack-plugin#configuration

      會結合base.conf.js設置中的 入口文件和輸出文件,
      將內容根據輸出filename.生成js文件 script到當前的html種
    */
    // new HtmlWebpackPlugin({
    //   // 生成html的名稱
    //   filename: 'index.html',
    //   // 生成html所需的模板路徑
    //   template: 'index.html',
      
    //     這個配置項指js文件插入的位置 

    //     選項: true body head false
    //     true和body相同,插入body最後
    //     head 插入head裏面
      
    //   inject: true
    // }),

    /*
      https://doc.webpack-china.org/plugins/copy-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
      這個插件是用於複製文件和文件夾,在這裏是將靜態文件夾的內容拷貝一份在開發環境中

      new CopyWebpackPlugin([patterns], options)
      A pattern looks like: { from: 'source', to: 'dest' }
    */
    new CopyWebpackPlugin([
      {
        // 拷貝的路徑
        from: path.resolve(__dirname, '../static'),
        // 訪問的路徑
        to: config.dev.assetsSubDirectory,
        // 忽略拷貝的內容(具體的文件名或模糊的路徑)
        ignore: ['.*']
      }
    ])
  ]
})

for(let pathName in pages){
  /*
    https://doc.webpack-china.org/plugins/html-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
  */
  let options = {
    filename: `${pathName}.html`,
    template: pages[pathName],
    inject: true,
    // 指定當前的html插入的模塊(若是不設定會將全部頁面的js都插入, - -)
    chunks: [pathName]
  }

  devWebpackConfig.plugins.push(new HtmlWebpackPlugin(options))
}


module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  // 使用插件去判斷當前端口是否可用
  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

      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        // 添加終端提示內容
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))

      resolve(devWebpackConfig)
    }
  })
})

生成環境

webpack.prod.conf.js

'use strict'
// 生產環境配置文件

const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
// 基礎設置配置文件
const baseWebpackConfig = require('./webpack.base.conf')
// webpack 複製文件和文件夾的插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 自動生成 html 而且注入到 .html 文件中的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 整合css的工具 https://github.com/webpack-contrib/extract-text-webpack-plugin
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 壓縮提取出的css 並解決ExtractTextPlugin分離出的重複問題
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 壓縮代碼
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

const env = require('../config/prod.env')

// 多頁面html路徑集合
const pages = utils.getEntry('./src/module/*/*.html');

const webpackConfig = merge(baseWebpackConfig, {
  module: {
    /*
      在utils.js已經配置好相關對extractTextPlugin的css抽取配置.經過extract: true便可觸發

      若是要觸發這個 extract 須要在plugins裏面註冊一下
    */
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true
    })
  },
  // 檢測生產環境下是否生成source map(而且生成的模式是怎麼樣)
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // 指的是經過CommonsChunkPlugin提取出來模塊的命名規則
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    // 定義全局變量
    new webpack.DefinePlugin({
      'process.env': env
    }),

    // https://doc.webpack-china.org/plugins/uglifyjs-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
    new UglifyJsPlugin({
      // 壓縮uglify的配置
      uglifyOptions: {
        compress: {
          // 壓縮後刪除沒有用到的代碼時不輸出警告
          warnings: false
        }
      },
      // 是否使用sourcemap作關聯
      sourceMap: config.build.productionSourceMap,
      // 壓縮代碼中是否使用多進程進行構建
      parallel: true
    }),
    
    // 將每一個模塊的css提取到一個文件裏面
    new ExtractTextPlugin({
      // 提取出來的文件名稱
      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: true,
    }),

    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    // 刪除重複的css內容
    new OptimizeCSSPlugin({
      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

    /*
      https://doc.webpack-china.org/plugins/html-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
    */
    // new HtmlWebpackPlugin({
    //   // 生成html的名稱
    //   filename: config.build.index,
    //   template: 'index.html',
    //   // 這個配置項指js文件插入的位置 
    //   inject: true,
    //   // 額外的精簡配置項
    //   minify: {
    //     // 刪去html中的註釋項
    //     removeComments: true,
    //     // 摺疊html中的空白字符
    //     collapseWhitespace: true,
    //     // 刪去沒必要要的屬性
    //     removeAttributeQuotes: true
    //     // https://github.com/kangax/html-minifier#options-quick-reference
    //   },
    //   // necessary to consistently work with multiple chunks via CommonsChunkPlugin
    //   // 控制生成的js插入位置的順序(能夠結合chunks進行選擇)
    //   chunksSortMode: 'dependency'
    // }),

    // keep module.id stable when vendor modules does not change
    // 該插件會根據模塊的相對路徑生成一個四位數的hash做爲模塊id
    new webpack.HashedModuleIdsPlugin(),

    // enable scope hoisting
    /*
      https://doc.webpack-china.org/plugins/module-concatenation-plugin/#src/components/Sidebar/Sidebar.jsx
      webpack3.0 新特性,
      從本來的每一個bundle模塊打包成多個單獨閉包去調用,
      變爲如今的在一個大閉包裏面去調用各個模塊,
      提高了效率
    */
    new webpack.optimize.ModuleConcatenationPlugin(),

    // split vendor js into its own file
    /*
      提取公共模塊(將公共的import模塊 提取到一個文件中.)
      https://doc.webpack-china.org/plugins/commons-chunk-plugin
      
    */
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      // 模塊被抽離出來至vendor文件中的判斷
      minChunks (module) {
        /*
          通常來說這裏的module會返回整個項目所用到的組件庫包,和import的東西
          而後經過這個函數去控制一下哪一些放入vendor的文件

          能夠經過具體的數值或者Boolean值來控制抽取的顆粒度.
          返回true, 是會將全部的import模塊都提取,
          返回false,是將重複的提取出來,
          具體的數值,就會做爲調用模塊的次數 來提取,
        */
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    /*
      https://doc.webpack-china.org/concepts/manifest/
      當編譯器(compiler)開始執行、解析和映射應用程序時,設置好的 /src文件夾就會被打散
      但會保留全部模塊的詳細要點。這個數據集合稱爲 "Manifest"
      當完成打包併發送到瀏覽器時,會在運行時經過 Manifest 來解析和加載模塊。

      若是不提取manifest的數據,每次build打包 上面vendor文件的hash值也會被改變,致使若是發版本,
      未改變vendor的代碼由於hash改變 緩存也會被幹掉
    */
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      // 傳入 `Infinity` 會立刻生成 公共獨立文件
      minChunks: Infinity
    }),
    
    /*
      https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
      經過children和async屬性,
      將那種又被父組件和子組件一塊兒公用的模塊抽取出來,
    */
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'app',
    //   // (建立一個異步 公共chunk)
    //   async: 'vendor-async',
    //   children: true,
    //   // (在提取以前須要至少三個子 chunk 共享這個模塊)
    //   minChunks: 3
    // }),

    /*
      https://doc.webpack-china.org/plugins/copy-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
      這個插件是用於複製文件和文件夾,在這裏是將靜態文件夾的內容拷貝一份在開發環境中

      new CopyWebpackPlugin([patterns], options)
      A pattern looks like: { from: 'source', to: 'dest' }
    */
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        // 忽略拷貝的內容(具體的文件名或模糊的路徑)
        ignore: ['tpl/*'] //不打包模板文件夾
      }
    ])
  ]
})

// gzip壓縮
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
    })
  )
}

// bundle分析
if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

for(let pathName in pages){
  /*
    https://doc.webpack-china.org/plugins/html-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
  */
  let options = {// 生成html的名稱
    filename: `${pathName}.html`,
    template: pages[pathName],
    // 這個配置項指js文件插入的位置 
    inject: true,
    // 額外的精簡配置項
    minify: {
      // 刪去html中的註釋項
      removeComments: true,
      // 摺疊html中的空白字符
      collapseWhitespace: true,
      // 刪去沒必要要的屬性
      removeAttributeQuotes: true
      // https://github.com/kangax/html-minifier#options-quick-reference
    },
    // 控制生成的js插入位置的順序(能夠結合chunks進行選擇)
    chunksSortMode: 'dependency'
  }

  //判斷處理的路徑是否在入口配置裏
  if(pathName in webpackConfig.entry){
    options.chunks = ['manifest', 'vendor', pathName];
    // 加上hash值進行緩存處理
    options.hash = true;
  }

  webpackConfig.plugins.push(new HtmlWebpackPlugin(options))
}

module.exports = webpackConfig

build.js

'use strict'
require('./check-versions')()

// 在process.env加入生產標識
process.env.NODE_ENV = 'production'

// loading的插件 https://github.com/sindresorhus/ora
const ora = require('ora')
// 能夠在 node 中執行`rm -rf`的工具
// https://github.com/isaacs/rimraf
const rm = require('rimraf')
const path = require('path')

// 在終端輸出帶顏色的文字
// https://github.com/chalk/chalk
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
//生產配置文件
const webpackConfig = require('./webpack.prod.conf')

// 實例化ora loading的插件
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, // 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'
    ))
  })
})

其餘文件

  • vue-loader.conf.js 配置vue-loader的內容
  • check-versions.js 顧名思義檢查版本的文件

公共方法文件(utils.js)

'use strict'
const path = require('path')
const config = require('../config')
// 打包css方法
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
// 匹配文件夾路徑組件 https://www.cnblogs.com/waitforyou/p/7044171.html
const glob = require('glob')

exports.assetsPath = function (_path) {
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory

  return path.posix.join(assetsSubDirectory, _path)
}

exports.cssLoaders = function (options) {
  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
  function generateLoaders (loader, loaderOptions) {
    // 判斷是否使用postcss
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    // (which is the case during production build)
    /*
      https://www.npmjs.com/package/extract-text-webpack-plugin
      下面這一塊的配置是指,是否須要使用extract這個插件,將css總體抽取出來.

      其餘倆屬性看一下api,
      有一個關鍵的配置屬性.
      publicPath   這個屬性是指,改寫css中資源引用的路徑.

      --
      若是不配置這個屬性,部分例如background-image對本地文件夾圖片url的引用.抽取後會致使路徑出錯
    */
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: 'vue-style-loader',
        publicPath: '../../../'
      })
    } 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: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')
  }
}

// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
  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
}

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')
    })
  }
}

// 多頁面模式的路徑返回方法
exports.getEntry = function (globPath) {
  var entries = {},       //路徑集合
      basename = '',      //路徑的標識
      key = '',           //路徑的key值
      tmp;                //處理數組

  //能夠得到傳入path路徑下的全部文件(返回數組)
  glob.sync(globPath).forEach(function (item) {
    /*
      http://nodejs.cn/api/path.html

      path.basename(path[, ext]) 是用於返回路徑的最後部分

      path.basename('/foo/bar/baz/asdf/quux.html');
      // 返回: 'quux.html'

      path.basename('/foo/bar/baz/asdf/quux.html', '.html');
      // 返回: 'quux'

      -------
      path.extname(path) 方法返回 path 的擴展名
      path.extname('index.html');
      // 返回: '.html'
    */
    basename = path.basename(item, path.extname(item));
    tmp = item.split('/').splice(2);    //默認進來的地址是 ./src/頁面文件夾/***  從src後面那個開始處理
    key = tmp.shift() + '/' + basename; //獲取存放頁面的文件夾名稱 (拼接成 頁面文件夾/對應頁面的格式)
    entries[key] = item;
  })

  return entries;
}

config文件夾

一些被提取了出來的配置標識項, dev.env.js標識開發環境 prod.env.js生產環境(不過其實我不知道這2個有什麼用 - -)

index.js

'use strict'
// 配置項
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
  dev: { // 開發環境

    // 靜態文件夾路徑
    assetsSubDirectory: 'static',
    // 編譯後資源訪問的路徑
    assetsPublicPath: '/',
    // 代理
    proxyTable: {
        /*
            param:
            *:   表示掛代理時,識別的請求前綴
            url: 表示代理的地址
            例如
            '/api': {
                target: 'http://www.baidu.com',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': '/api'
                } //=> localhost:8000/api => http://www.baidu.com/api
            }

            代理的proxy格式能夠參照如下github
            https://github.com/chimurai/http-proxy-middleware
        */
    },

    // Various Dev Server settings
    // 解決了本來僅爲localhost 不能使用ip地址開發的問題
    host: '0.0.0.0',
    // 端口
    port: 8080,
    // 是否自動打開瀏覽器標識
    autoOpenBrowser: false,
    // webpack-dev-server中 是否全屏彈窗的形式顯示編譯過程當中的錯誤標識
    errorOverlay: true,
    // 配合 friendly-errors-webpack-plugin
    notifyOnErrors: true,
    // dev-server 與監視文件相關的控制選項標識
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

    
     /*
        這裏是關於代碼的 sourcemap 模式的選擇
        可參考如下各類類型 https://segmentfault.com/a/1190000008315937
        幾個類型的關鍵字
        cheap
        eval
        module
    */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'cheap-module-eval-source-map',

    // If you have problems debugging vue-files in devtools,
    // set this to false - it *may* help
    // https://vue-loader.vuejs.org/en/options.html#cachebusting
    // 指的是緩存破壞,but找不到api
    cacheBusting: false,
    /*
        是否開啓 CSS 的 source maps,
        開啓的話將在head頭部的style中加入 source map的相關信息
    */
    cssSourceMap: false
  },

  build: { //生產環境
    // index模板文件
    index: path.resolve(__dirname, '../dist/index.html'),

    // 路徑
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    // 資源引用的路徑(須要注意)
    assetsPublicPath: '../',

    /**
     * Source Maps
     */

    // 是否開啓source map的標識
    productionSourceMap: true,
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    // gzip壓縮
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  }
}

總結

  • 目前這個版本的註釋我改造了一下改爲了多頁面模式,(原來寫的不合適,修改一下, 若是須要用spa的模式請麻煩從新拉個包,配置文件同樣的,不過在config中生產的配置項目assetsPublicPath要改成'./' 不過有一些不改也能夠,具體看發包後狀況)
  • 整理完2.0和3.0的配置內容,我發現了3.0用瞭如下一些不一樣的東西:javascript

    1. 我發現3.0用了一直宣傳的ModuleConcatenationPlugin.
    2. 2.*在開發過程熱加載和預編譯用的是 express+dev-middleware+hot-middleware(熱加載時會刷新頁面)
    3. 3.*在開發過程熱加載和預編譯直接就用回了 webpack-dev-server(彷佛熱加載不刷新頁面了直接覆蓋掉)
  • 剛剛升級3.0用的腳手架有一個問題.就是wepback-dev-server是2.9.7版本,這個版本client裏面的代碼用了es6.致使一些舊版本的ios(我本身是ios9.3.5)直接傻嗶了,最近拉下來的升級到2.11.0了並且在base.conf.js中babel-loader加了對應的include
  • 其餘文件例如 .babelrc是用於babel的配置文件/.postcssrc.js是postcss的一系列配置內容
  • 0129 18點,下班的moment我在assets加入了公共的配置項,拉下來就能直接搞了.(你們互相學習咯) ^^
  • 0208 更新webpack打包完致使靜態引用路徑錯誤的問題:
    1.由於在開發環境中,將靜態文件夾經過CopyWebpackPlugin插件複製到了(config.dev.assetsSubDirectory)的文件夾路徑中,因此使用下面2種方式在開發環境中去引用均可以
    路徑圖片

    那麼問題就來了build打包完以後,目錄結構是這樣.
    打包完目錄結構圖css

    那麼使用上面 /static這種標識在根目錄的static就會找不到資源了.因此建議使用../../的方式,或者到base.conf.js中配置alias使用.這樣去使用.
    靜態資源調用html

  • 0324 更新如何解決以前發現build後,使用background-image路徑報錯的問題.
    最開始發現,直接npm build的時候,在css中使用的資源路徑(主要是圖片資源)引用會報錯.
    由於build完文件夾都會被從新處理擺放,

由於在base.conf.js中,用url-loader對圖片進行了處理:
url-loader配置前端

  1. 未超過limit的字節數將打包成dataURL的格式,
  2. 超過了會放到static/img的文件夾

build打包完文件位置與開發不一樣,致使了css路徑就會有問題...vue

後面研究了一下build時會觸發的庫,發現是使用了extract-text-webpack-plugin將css都抽取到了一塊兒.看了一下api裏面有一個配置項publicPath.調整一下到正確的引用便可解決這個問題.
extract配置項java

相關文章
相關標籤/搜索