【webpack】理解配置文件


學習鏈接:

http://blog.csdn.net/hongchh/article/details/55113751

https://segmentfault.com/a/1190000009356434

http://www.jianshu.com/p/42e11515c10f

主要分析開發(dev)和構建(build)兩個過程涉及到的文件,故下面文件結構僅列出相應的內容。

├─build
│   ├─build.js
│   ├─check-versions.js
│   ├─dev-client.js
│   ├─dev-server.js
│   ├─utils.js
│   ├─vue-loader.conf.js
│   ├─webpack.base.conf.js
│   ├─webpack.dev.conf.js
│   ├─webpack.prod.conf.js
│   └─webpack.test.conf.js
├─config
│   ├─dev.env.js
│   ├─index.js
│   ├─prod.env.js
│   └─test.env.js
├─...
└─package.json

package.json

關注scripts字段的內容:

  "scripts": {
    "dev": "node build/dev-server.js",
    "build": "node build/build.js",
    "lint": "eslint --ext .js,.vue src"
  },

直接看”dev”和”build”。運行”npm run dev”的時候執行的是build/dev-server.js文件,運行”npm run build”的時候執行的是build/build.js文件,我們可以從這兩個文件開始進行代碼閱讀分析。

build文件夾

build/dev-server.js

 

首先來看執行”npm run dev”時候最先執行的build/dev-server.js文件。該文件主要完成下面幾件事情:

 

  1. 檢查node和npm的版本
  2. 引入相關插件和配置
  3. 創建express服務器和webpack編譯器
  4. 配置開發中間件(webpack-dev-middleware)和熱重載中間件(webpack-hot-middleware)
  5. 掛載代理服務和中間件
  6. 配置靜態資源
  7. 啟動服務器監聽特定端口(8080)
  8. 自動打開瀏覽器並打開特定網址(localhost:8080)

說明: express服務器提供靜態文件服務,不過它還使用了http-proxy-middleware,一個http請求代理的中間件。前端開發過程中需要使用到后台的API的話,可以通過配置proxyTable來將相應的后台請求代理到專用的API服務器。

require('./check-versions')()   /*檢查NodeJS和npm的版本*/
var config = require('../config')  // 獲取配置
// 如果Node的環境變量中沒有設置當前的環境(NODE_ENV),則使用config中的配置作為當前的環境
if (!process.env.NODE_ENV) process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
// 一個可以調用默認軟件打開網址、圖片、文件等內容的插件
// 這里用它來調用默認瀏覽器打開dev-server監聽的端口,例如:localhost:8080
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var opn = require('opn')
// 一個express中間件,用於將http請求代理到其他服務器
// 例:localhost:8080/api/xxx  -->  localhost:3000/api/xxx
// 這里使用該插件可以將前端開發中涉及到的請求代理到API服務器上,方便與服務器對接
var proxyMiddleware = require('http-proxy-middleware')
// 引入相應的 webpack 配置
var webpackConfig = require('./webpack.dev.conf')
// dev-server 監聽的端口,默認為config.dev.port設置的端口,即8080
// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
  // Define HTTP proxies to your custom API backend
  // https://github.com/chimurai/http-proxy-middleware
  // 定義 HTTP 代理表,代理到 API 服務器
var proxyTable = config.dev.proxyTable
// 創建1個 express 實例
var app = express()

// 數據mock
var appData = require('../static/data.json')//在根目錄下放置的一個data.json文件,用以模擬請求的數據
var seller = appData.seller//獲取data中的數據對象
var goods = appData.goods
var ratings = appData.ratings

var apiRoutes = express.Router()//創建一個路由實例

apiRoutes.get('/seller', function(req, res) {//獲取數據
  res.json({
    errno: 0,
    data: seller
  })
})

apiRoutes.get('/goods', function(req, res) {
  res.json({
    errno: 0,
    data: goods
  })
})

apiRoutes.get('/ratings', function(req, res) {
  res.json({
    errno: 0,
    data: ratings
  })
})

app.use('/api', apiRoutes)// express使用api
// 根據webpack配置文件創建Compiler對象
var compiler = webpack(webpackConfig)
// webpack-dev-middleware使用compiler對象來對相應的文件進行編譯和綁定
// 編譯綁定后將得到的產物存放在內存中而沒有寫進磁盤
// 將這個中間件交給express使用之后即可訪問這些編譯后的產品文件
var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
})
// webpack-hot-middleware,用於實現熱重載功能的中間件
var hotMiddleware = require('webpack-hot-middleware')(compiler)
  // force page reload when html-webpack-plugin template changes
  // 當html-webpack-plugin提交之后通過熱重載中間件發布重載動作使得頁面重載
compiler.plugin('compilation', function(compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function(data, cb) {
    hotMiddleware.publish({
      action: 'reload'
    })
    cb()
  })
})
// 將 proxyTable 中的代理請求配置掛在到express服務器上
// proxy api requests
Object.keys(proxyTable).forEach(function(context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = {
      target: options
    }
  }
  app.use(proxyMiddleware(context, options))
})

// handle fallback for HTML5 history API
// 重定向不存在的URL,常用於SPA
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
// 使用webpack開發中間件
// 即將webpack編譯后輸出到內存中的文件資源掛到express服務器上
app.use(devMiddleware)
// 將熱重載中間件掛在到express服務器上
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets// 靜態資源的路徑// 將靜態資源掛到express服務器上
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))
// 啟動express服務器並監聽相應的端口(8080)
module.exports = app.listen(port, function(err) {
  if (err) {
    console.log(err)
    return
  }
// 應用的地址信息,例如:http://localhost:8080
var uri = 'http://localhost:' + port console.log('Listening at ' + uri + '\n') // 如果符合自動打開瀏覽器的條件,則通過opn插件調用系統默認瀏覽器打開對應的地址uri // when env is testing, don't need open it if (process.env.NODE_ENV !== 'testing') { opn(uri) } })

 build/webpack.base.conf.js

從代碼中看到,dev-server使用的webpack配置來自build/webpack.dev.conf.js文件(測試環境下使用的是build/webpack.prod.conf.js,這里暫時不考慮測試環境)。而build/webpack.dev.conf.js中又引用了webpack.base.conf.js,所以先分析webpack.base.conf.js。

webpack.base.conf.js主要完成了下面這些事情:

  1. 配置webpack編譯入口
  2. 配置webpack輸出路徑和命名規則
  3. 配置模塊resolve規則
  4. 配置不同類型模塊的處理規則

說明: 這個配置里面只配置了.js、.vue、圖片、字體等幾類文件的處理規則,如果需要處理其他文件可以在module.rules里面配置。

var path = require('path')
var config = require('../config')
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')// 給出正確的絕對路徑 var env = process.env.NODE_ENV
  // check env & config/index.js to decide weither to enable CSS Sourcemaps for the
  // various preprocessor loaders added to vue-loader at the end of this file
var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
var useCssSourceMap = cssSourceMapDev || cssSourceMapProd

module.exports = {
  entry: {// 配置webpack編譯入口
    app: './src/main.js'
  },
  output: {// 配置webpack輸出路徑和命名規則
    path: config.build.assetsRoot,// webpack輸出的目標文件夾路徑(例如:/dist)
    publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,// webpack編譯輸出的發布路徑
    filename: '[name].js'  // webpack輸出bundle文件命名格式
  },
  resolve: {// 配置模塊resolve的規則
    extensions: ['', '.js', '.vue'],// 自動resolve的擴展名
    fallback: [path.join(__dirname, '../node_modules')],
    alias: {// 創建路徑別名,有了別名之后引用模塊更方便,例如 // import Vue from 'vue/dist/vue.common.js'可以寫成 import Vue from 'vue' 'vue$': 'vue/dist/vue.common.js',
      'src': path.resolve(__dirname, '../src'),
      'assets': path.resolve(__dirname, '../src/assets'),
      'components': path.resolve(__dirname, '../src/components'),
      'common': path.resolve(__dirname, '../src/common'),
      'img': path.resolve(__dirname, '../resource/img')
    }
  },
  resolveLoader: {
    fallback: [path.join(__dirname, '../node_modules')]
  },
  module: {// 配置不同類型模塊的處理規則
    preLoaders: [{
      test: /\.vue$/,
      loader: 'eslint',
      include: projectRoot,
      exclude: /node_modules/
    }, {
      test: /\.js$/,// 對所有.vue文件使用eslint
      loader: 'eslint',
      include: projectRoot,
      exclude: /node_modules/
    }],
    loaders: [{
      test: /\.vue$/,
      loader: 'vue'
    }, {
      test: /\.js$/,// 對src和test文件夾下的.js文件使用babel-loader
      loader: 'babel',
      include: projectRoot,
      exclude: /node_modules/
    }, {
      test: /\.json$/,
      loader: 'json'
    }, {
      test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
      loader: 'url',
      query: {
        limit: 10000,
        name: utils.assetsPath('img/[name].[hash:7].[ext]')
      }
    }, {// 對字體資源文件使用url,query.name指明了輸出的命名規則
      test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
      loader: 'url',
      query: {
        limit: 10000,
        name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
      }
    }]
  },
  eslint: {
    formatter: require('eslint-friendly-formatter')
  },
  vue: {
    loaders: utils.cssLoaders({
      sourceMap: useCssSourceMap
    }),
    postcss: [
      require('autoprefixer')({
        browsers: ['last 2 versions']
      })
    ]
  }
}

build/webpack.dev.conf.js

接下來看webpack.dev.conf.js,這里面在webpack.base.conf的基礎上增加完善了開發環境下面的配置,主要包括下面幾件事情:

  1. 將hot-reload相關的代碼添加到entry chunks
  2. 合並基礎的webpack配置
  3. 使用styleLoaders
  4. 配置Source Maps
  5. 配置webpack插件
var config = require('../config')
var webpack = require('webpack')
// 一個可以合並數組和對象的插件
var merge = require('webpack-merge') var utils = require('./utils') var baseWebpackConfig = require('./webpack.base.conf')
// 一個用於生成HTML文件並自動注入依賴文件(link/script)的webpack插件
var HtmlWebpackPlugin = require('html-webpack-plugin') // add hot-reload related code to entry chunks Object.keys(baseWebpackConfig.entry).forEach(function (name) { baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) }) // 合並基礎的webpack配置 module.exports = merge(baseWebpackConfig, {
// 配置樣式文件的處理規則,使用styleLoaders module: { loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) },
// eval-source-map is faster for development// 配置Source Maps devtool: '#eval-source-map', plugins: [// 配置webpack插件 new webpack.DefinePlugin({ 'process.env': config.dev.env }), // https://github.com/glenjamin/webpack-hot-middleware#installation--usage new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true }) ] })

 build/webpack.prod.conf.js

構建的時候用到的webpack配置來自webpack.prod.conf.js,該配置同樣是在webpack.base.conf基礎上的進一步完善。主要完成下面幾件事情:

  1. 合並基礎的webpack配置
  2. 使用styleLoaders
  3. 配置webpack的輸出
  4. 配置webpack插件
  5. gzip模式下的webpack插件配置
  6. webpack-bundle分析

說明: webpack插件里面多了丑化壓縮代碼以及抽離css文件等插件。

var path = require('path')
var config = require('../config')
var utils = require('./utils')
var webpack = require('webpack')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var ExtractTextPlugin = require('extract-text-webpack-plugin')// 用於從webpack生成的bundle中提取文本到特定文件中的插件 // 可以抽取出css,js文件將其與webpack輸出的bundle分離 var HtmlWebpackPlugin = require('html-webpack-plugin')
var env = config.build.env

var webpackConfig = merge(baseWebpackConfig, {// 合並基礎的webpack配置
  module: {
    loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true })
  },
  devtool: config.build.productionSourceMap ? '#source-map' : false,
  output: {// 配置webpack的輸出
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  vue: {
    loaders: utils.cssLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true
    })
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({// 抽離css文件 'process.env': env
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),
    new webpack.optimize.OccurrenceOrderPlugin(),
    // extract css into its own file
    new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
    // 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({
      filename: config.build.index,
      template: 'index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function (module, count) {
        // 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({
      name: 'manifest',
      chunks: ['vendor']
    })
  ]
})

if (config.build.productionGzip) {
  var 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
    })
  )
}

module.exports = webpackConfig

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM