從vue-cli學(xué)webpack配置1——針對webpack2

上一篇 《webpack基礎(chǔ)使用》
  前言:webpack的配置其實(shí)挺多巷帝,而且更多的是體現(xiàn)在loader和plugin方面的配置扫夜,上篇我們只是簡單介紹webpack基礎(chǔ)使用,因?yàn)槲矣X得更多細(xì)節(jié)方面可以在vue-cli生成的工程中學(xué)習(xí)到现拒。大家現(xiàn)在用工具生成出來的是基于webpack 3x版本的望侈,比2x版本的配置更簡潔清晰,不同點(diǎn)是:2x版本的用了webpack-dev-middle和webpack-hot-middleware插件提供模塊熱更新侥猬,而3x版本的配置則是用webpack-dev-server捐韩;其實(shí)兩者有好有壞荤胁,當(dāng)然相比之下,我覺得webpack-dev-server會(huì)更直接點(diǎn)。

vue-cli的使用

//vue-cli工具很簡單,命令行里:
npm install vue-cli  -g
// 安裝完之后垢油,就有vue命令了
vue init  webpack  vue-webpack2     // 初始化一個(gè)webpack工程滩愁,工程名字為vue-webpack2  
//下面圖片中,eslint那一行選擇了yes廉丽,eslint是用于管理代碼格式的妻味,良好的編碼格式是很重要,unit test 和nigtht watch 就選擇no蔑匣,因?yàn)檫@次我們主要是研究webpack配置哈
vue-cli的使用

命令執(zhí)行完后,在當(dāng)前目錄下就會(huì)看到新的文件夾凿将,也就是你的工程vue-webpack2(現(xiàn)在vue-cli出來的工程是基于webpack3的牧抵, 我給大家提供一個(gè)webpack2配置的版本,下一篇文章里再講基于webpack3)妹孙。
目錄結(jié)構(gòu):
src:放我們自己代碼
build和config:webpack配置获枝,我們學(xué)習(xí)的重點(diǎn)
其他配置文件:稍后講

工程的目錄結(jié)構(gòu)

第一部分:非重點(diǎn)但注意的配置文件
.editorconfig文件
這個(gè)文件主要是對編輯器的編輯做設(shè)置省店,里面主要設(shè)置一個(gè)tab縮進(jìn)多少個(gè)空格,換行符(linux系統(tǒng)的是lf雹舀, window系統(tǒng)則是ctlf)说榆,還有編碼設(shè)置等。這個(gè)文件生效需要你安裝editorconfig插件串慰,這個(gè)插件支持眾多ide編輯器荠卷,像sublime、vscode掂碱、eclipse慎冤,主要是為了統(tǒng)一編輯蚁堤,使得我們的js能夠運(yùn)行到其他操作系統(tǒng)。(為什么java不需要撬即,因?yàn)閖vm虛擬機(jī)最終執(zhí)行的java文件編譯后的二進(jìn)制.class文件呈队,不同平臺有不同jvm宪摧,所以java是跨平臺的)

.eslintrc.js
eslint是用于統(tǒng)一團(tuán)隊(duì)之間的編碼風(fēng)格的工具,以前看過一些老代碼蕊苗,風(fēng)格不統(tǒng)一沿彭,看起來很痛苦膝蜈。有些人是分號黨,有些人卻不是非剃。eslint對換行推沸、空格等都可以配置一套規(guī)則,團(tuán)隊(duì)里基于這套規(guī)則寫出的代碼肺素,在閱讀性就做到了統(tǒng)一倍靡。大家可以參考https://eslint.org/了解其詳細(xì)的配置。上手很容易他挎,npm install eslint 然后捡需,eslint src/main.js ,工具就根據(jù).eslintrc.js配置開始檢查main.js站辉。這種用法比較初級,我們可以看一下我們的工程里是怎么使用的殊霞。

package.json
這個(gè)就不用說了吧汰蓉,我們可以了解一下npm script的使用技巧古沥,看下圖
工程里給我們配置了四個(gè)任務(wù)娇跟,所以你就可以執(zhí)行npm run dev 或者執(zhí)行npm run build 苞俘,以及npm run lint。
比如 npm run lint 乞封,實(shí)際執(zhí)行的就是對應(yīng)的: eslint --ext .js, .vue src 這個(gè)就是告訴eslint幫我們檢查src下面的js文件和vue文件岗憋。另外你也可以添加配置:
"test": "npm run lint & npm run dev"
當(dāng)你執(zhí)行npm run test 就會(huì)執(zhí)行npm run lint后再執(zhí)行 npm run dev

package.json

--其他文件仔戈,下面講webpack配置會(huì)講到拧廊,好進(jìn)入第二部分--

第二部分: webpack配置()
bulid目錄下的webpack.base.conf.js

var path = require('path')      // node path模塊
var utils = require('./utils')
var config = require('../config')  // config目錄吧碾,vue-cli工程分成兩個(gè)環(huán)境倦春,一個(gè)是開發(fā)的dev環(huán)境落剪,一個(gè)是生產(chǎn)環(huán)境production
var vueLoaderConfig = require('./vue-loader.conf')  // 引入vue-loader的配置,vue-loader是處理.vue文件使用的

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: {
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot,
    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')
    }
  },
  module: {   // 定義對文件的處理loader
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}

這個(gè)文件定義webpack的基礎(chǔ)配置:
引入config:對應(yīng)config目錄的index.js: 這個(gè)主要是為區(qū)分開發(fā)環(huán)境與生產(chǎn)環(huán)境的不同,比如開發(fā)環(huán)境是本機(jī)測試的暮胧,那么我的publicPath設(shè)置為空问麸,但是生產(chǎn)環(huán)境則設(shè)置aliyu.cdn.com严卖,所以output里面的publicPath根據(jù)process.env.NODE_ENV 是否為開發(fā)環(huán)境,引用config對應(yīng)的屬性来颤。比如開發(fā)環(huán)境引入圖片的url是<img src='/pic.jpg'>,而生產(chǎn)環(huán)境則是<img src='http://aliyun.cdn.com/pic.jpg'>稠肘。這就是output的publicPath的作用项阴,設(shè)置config就是為了區(qū)分開發(fā)環(huán)境還是生產(chǎn)環(huán)境

resolve:
extentions: 配置這個(gè)參數(shù)后略荡,可以省略擴(kuò)展名歉胶。如:import * from 'test.js' 可以寫成 import * from 'test'
alias: 重命名跨扮。路徑上的重命名验毡,比如你要import一個(gè)模塊晶通,路徑是D://project/vue-webapck2/src/modules/car 寫完整路徑很長很累哟玷,工程中配置了,我們可以寫成 @/modules/car

下面則是對文件處理定義了loader:
loader是定義在module.rules中喉脖,其實(shí)意思就是對模塊文件的處理規(guī)則树叽。因?yàn)閣ebpack把每個(gè)一個(gè)文件谦絮,哪怕是圖片視頻都當(dāng)成一個(gè)模塊层皱,只不過它識別不了需要這loader處理工具來幫助它。
test:正則匹配草冈,匹配.vue文件用vue-loader處理瓮增,.js用babel-loader處理
loader: 指定處理的loader工具
options: loader怎么處理文件也需要你設(shè)置參數(shù)绷跑,你可以用個(gè)options傳遞你設(shè)置的參數(shù)給它

bable-loader: 對js文件做處理,這樣我們可以用es6、es7規(guī)范來寫js带膜,babel-loader會(huì)根據(jù)項(xiàng)目根目錄下的.bablerc文件的配置對于你的js代碼進(jìn)行轉(zhuǎn)義鸳谜,有些瀏覽器沒有實(shí)現(xiàn)es6 或者es7規(guī)范咐扭,所以這就是bable存在的意義滑废。
url-loader:對資源文件做base64編碼蠕趁,它有個(gè)參數(shù)limit辛馆,比如一張圖片小于這個(gè)limit的值昙篙,那url-loader會(huì)幫你轉(zhuǎn)成base64編碼嵌入引用這張圖片的qit模塊中,這樣瀏覽器就不需要多一個(gè)網(wǎng)絡(luò)請求缴挖,去請求圖片焚辅,增加網(wǎng)頁的響應(yīng)時(shí)間法焰。當(dāng)然超過這個(gè)值的話,還是給你提供成url鏈接乙濒。工程中的配置颁股,可以看到它對圖片傻丝,視頻,字體文件都可以轉(zhuǎn)亏掀。
vue-loader:vue官方提供的對vue文件的處理滤愕,它會(huì)將vue文件中css的部分交由webpack指定的css-loader處理怜校,js和模板交由webpack的js指定loader也就是babel-loader處理;處理具體配置不多講茄茁,參考https://github.com/vuejs/vue-loader

~~~~~~~~~~~~~~分割線~~~~~~~~~~~~~~~~~~~

現(xiàn)在我們知道了巩割,webpack.base.conf.js配置了基礎(chǔ)性的配置宣谈,然后我們的配置暴露出去机蔗。那我們怎么使用它呢萝嘁?首先我們知道package.json幫我們配置了build 和 dev 兩個(gè)任務(wù),它們分別對應(yīng)執(zhí)行的 npm run lint && node build/build.js 和 node build/dev-server.js酸钦。npm run build, 先執(zhí)行eslint卑硫,幫忙lint一下代碼的風(fēng)格蚕断,然后執(zhí)行node build/build.js 亿乳。那我們先看build.js

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

process.env.NODE_ENV = 'production'

var ora = require('ora')       // 一個(gè)用于在命令窗口提示類似程序處理中,loading中之類文字障陶,以起到提醒標(biāo)注作用
var rm = require('rimraf')     // rm 刪除目錄抱究,清空目錄的工具包
var path = require('path')
var chalk = require('chalk')      // 在命令窗口輸出有顏色的文字工具包
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf')

var spinner = ora('building for production...')   // 命令窗口會(huì)出現(xiàn)一個(gè)loading轉(zhuǎn)圈
spinner.start()

// rm 幫我們每次構(gòu)建前带斑,清理一下之前構(gòu)建好的舊文件勋磕,清理完后執(zhí)行回調(diào)函數(shù)
// 回調(diào)函數(shù)里執(zhí)行webpack打包
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  webpack(webpackConfig, function (err, stats) {   //webpack打包后執(zhí)行回調(diào)函數(shù),向控制臺輸出自己構(gòu)建結(jié)果信息
    spinner.stop()
    if (err) throw err
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      children: false,
      chunks: false,
      chunkModules: false
    }) + '\n\n')

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

webapck有兩種使用方式:
第一種: 命令行里webpack --config src/main.js
第二種: 就是require('webpack')醋安, 給webpack傳入config配置對象墓毒,然后執(zhí)行這段node腳本所计,即node build/build.js

然后我們就可以知道团秽,webpack配置參數(shù)config是從webpack.prod.conf引入的:
webpack.prod.conf.js: 這里主要是用了webpack-merge 合并基礎(chǔ)的配置习勤,根據(jù)環(huán)境的不同,添加不同的配置夷都。prod就是prodution生產(chǎn)環(huán)境囤官。這里面用到了一些插件蛤虐,具體我都注釋到上面

var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

var env = config.build.env

// 利用webpack-merge 合并我們的baseWebpackConfig配置驳庭。 webpack-merge能夠讓你動(dòng)態(tài)改變webpack配置
var webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true
    })
  },
  devtool: config.build.productionSourceMap ? '#source-map' : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
    // 添加chunkhash值嚷掠,指每次構(gòu)建的值都不一樣,業(yè)務(wù)代碼經(jīng)常變化贯城,添加chunkhash避免瀏覽器緩存使用舊代碼
    // chunkhash與hash區(qū)別在于:前者是每次構(gòu)建都不一樣能犯,后者是只要你的文件名是一樣的犬耻,是不會(huì)變化的枕磁,一般用chunkhash多一些
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    // DefinePlugin用于在webpack構(gòu)建中,定義參數(shù)茸苇,然后你可以在webpack構(gòu)建配置中引用這個(gè)參數(shù)做一些配置上的判斷学密,賦值
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // js壓縮插件,用于代碼壓縮彤守,然后去掉注釋哭靖,生成soucemap便于調(diào)試定位問題
    // 構(gòu)建生產(chǎn)環(huán)境生成sourcemap比較耗時(shí)款青,一般你也可以不用抡草,在開發(fā)環(huán)境才生成sourcemap
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      },
      sourceMap: true
    }),
    // extract css into its own file
    // 正如上面的英文注釋一樣,這個(gè)插件主要是將css內(nèi)容獨(dú)立抽出來燎含,而不是變成一個(gè)js模塊綁如bundle中
    // 官網(wǎng)說:這樣能夠加快整體構(gòu)建速度屏箍,同時(shí)有利于js和css分開
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css')
    }),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    // 用于壓縮css的插件
    new OptimizeCSSPlugin({
      cssProcessorOptions: {
        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
    // 這個(gè)是老朋友了赴魁,將我們的bundle注入到index.html,同時(shí)對html進(jìn)行壓縮處理钝诚,
    // 這里要注意一下:
    // 1.minify壓縮配置
    // 2.chunkSortMode:這個(gè)參數(shù)一般選擇dependency凝颇,因?yàn)槟憧梢园阉心K打包成一個(gè)文件,但是這樣效率最低芦岂,一般我們會(huì)抽出
    //                 公共模塊禽最,產(chǎn)生多個(gè)bundle,引入bundle的順序就由這插件來引入瓢喉;選擇 dependency,意思就是誰先被依賴决左,誰先被引入
    // 3.inject: 有三種方式 true/'head'/'body',其實(shí)就是指指定你要把這些bundle在什么地方引入佛猛,跟你引入js文件的script標(biāo)簽放在哪里是一個(gè)意思
    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
    // 這個(gè)是指定一個(gè)公共模塊插件继找,這個(gè)插件用于定義哪些可以算是公共模塊
    // 構(gòu)建過程中,這插件會(huì)根據(jù)我們minChunks的配置判斷哪些是公共模塊幻锁,抽取出來合一個(gè)name為ventor的bundle
    // 我們可以看出:只要是從node_modules中出來的判定為公共模塊
    // 另外name為什么不是'vendor[chunkhash:7]',name不加hash值是充分利用瀏覽器的緩存哄尔,因?yàn)槲覀児材K一般不會(huì)變化(除非技術(shù)棧升級),瀏覽器端有了緩存就不用重復(fù)請求
    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']
    }),
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

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

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

module.exports = webpackConfig

到這里就很清晰了:
webpack的配置思維:

var   webpack = require('webpack')
var   merge = require('webpack-merge')
webpack(merge(baseconfig,diffent_config))  // diffent_config指根據(jù)開發(fā)環(huán)境或生產(chǎn)環(huán)境做不同的配置

~~~~~~~~~~~~~~分割線~~~~~~~~~~~~~~~~~~~
現(xiàn)在我們來看看開發(fā)環(huán)境怎么配置鸣戴,一般我們會(huì)喜歡每個(gè)以模塊改動(dòng)后粘拾,能夠自動(dòng)更新半哟,同時(shí)不需要刷新瀏覽器就能看到修改。帶著疑問盯串,我們看看工程里是如何配置的体捏。
npm run dev 對應(yīng)著 node build/dev-server.js(package.json寫,別忘了哈)
我們看看dev-server.js

require('./check-versions')()  // 就是對應(yīng)check-version.js 檢查你當(dāng)前 node和npm 的版本看看是否符合要求

var config = require('../config')
if (!process.env.NODE_ENV) {
  process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}

var opn = require('opn')
var path = require('path')
var express = require('express')   //express,一個(gè)node的web框架
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = require('./webpack.dev.conf')

// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port   // 這里其實(shí)也是使用config配置的dev.port;
// automatically open browser, if not set will be false
var autoOpenBrowser = !!config.dev.autoOpenBrowser   // 服務(wù)啟動(dòng)成功后是否自動(dòng)打開瀏覽器,看config里面配置了true or false
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable    //

var app = express()   // 新建node http server 年栓,大家可以學(xué)一下express框架某抓,很簡單卻很強(qiáng)大
var compiler = webpack(webpackConfig)

// webpack-dev-middleware插件是將webpack返回的compiler傳給node server服務(wù)
// 這個(gè)插件的一個(gè)好處是:webpack構(gòu)建的bundle都是存在內(nèi)存中否副,而不是向硬盤輸出
// 配合webpack-hot-midlleware使用备禀,達(dá)到熱更新的目的
var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  quiet: true
})

// 這個(gè)就是我們的熱更新了,當(dāng)你改動(dòng)一個(gè)模塊(比如test.vue),改完按保存時(shí)赋续,這個(gè)插件會(huì)通知compile重新對這個(gè)模塊更新打包
// compile更新后,又會(huì)由devMiddleware插件將構(gòu)建的內(nèi)容傳給node server 服務(wù)队腐,并通知瀏覽器更新蚕捉,達(dá)到我們不需要手動(dòng)刷新瀏覽器就能看到我們的改動(dòng)的內(nèi)容
// heartbeat 心跳機(jī)制,每隔2秒檢查模塊是否發(fā)生變化(它怎么檢查柴淘,是一件有技術(shù)的事情迫淹,通過對比chunk的id,具體怎么實(shí)現(xiàn)要看源碼了)
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
  log: false,
  heartbeat: 2000
})
// force page reload when html-webpack-plugin template changes
// 編譯器處理的一個(gè)編譯完成的鉤子函數(shù)
// 完成是調(diào)用为严,其實(shí)就是編譯完成通知hotMiddleware 發(fā)布reload  action給瀏覽器
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {

    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// proxy api requests
// 在config中有個(gè)配置代理的敛熬,很多情況下第股,我們開發(fā)在本地应民,請求數(shù)據(jù)的接口在其他域名下,這個(gè)時(shí)候我們需要配置代理
// 這種配置其實(shí)個(gè)人覺得沒有那么方便夕吻,因?yàn)槟阃晖耆梢灾苯觓pp.use代理一個(gè)請求诲锹,代碼更加直觀些
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(options.filter || context, options))
})

// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets
// express 托管靜態(tài)資源
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))
var uri = 'http://localhost:' + port

var _resolve
var readyPromise = new Promise(resolve => {
  _resolve = resolve
})

console.log('> Starting dev server...')

// devMiddleware 監(jiān)聽編譯器編譯完成后執(zhí)行回調(diào)函數(shù)
// 這里判斷了config是否設(shè)置了自動(dòng)打開瀏覽器
devMiddleware.waitUntilValid(() => {
  console.log('> Listening at ' + uri + '\n')
  // when env is testing, don't need open it
  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
    opn(uri)
  }
  _resolve()
})

var server = app.listen(port)  // 啟動(dòng)服務(wù)

module.exports = {
  ready: readyPromise,
  close: () => {
    server.close()
  }
}

dev環(huán)境思想就是:利用devmiddle 中間件,講webpack的編譯器傳給node server 服務(wù)涉馅,并搭配hotmiddle 心跳監(jiān)測模塊是否更改归园,當(dāng)更改后,編譯完成稚矿,由hotmiddle發(fā)布一個(gè)reload的action庸诱,然后瀏覽器更新顯示捻浦。
而webpack的配置則是引用webpack.dev.conf.js

webpack.dev.conf.js: 同樣也是引入基礎(chǔ)配置,然后merge合并一下桥爽。有個(gè)注意點(diǎn)朱灿,它修改了entry,里面entry本來只是main.js钠四,現(xiàn)在變成兩個(gè)盗扒,build/dev-client.js 和main.js。dev-client注入個(gè)事件回調(diào)缀去,當(dāng)event.action = 'reload',是window.local.reload() 這個(gè)時(shí)候你就明白环疼,hotmiddle發(fā)布了reload的action,瀏覽器為什么會(huì)更新

var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')  // 同樣的是引入基礎(chǔ)配置
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')  // 增加友好報(bào)錯(cuò)插件朵耕,讓我們開發(fā)中,能夠更好了解報(bào)錯(cuò)信息

// 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])
})

module.exports = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
  },
  // cheap-module-eval-source-map is faster for development
  devtool: '#cheap-module-eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      'process.env': config.dev.env
    }),
    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    new FriendlyErrorsPlugin()
  ]
})

最后有個(gè)小問題:css的處理的loader到哪里去了淋叶?
其實(shí)因?yàn)関ue支持less 阎曹、sass、stylus 三種預(yù)編譯css語言煞檩,所以在工程里給我們封裝了一個(gè)util.js处嫌,里面有個(gè)styleloader的方法,主要是根據(jù)你的vue組件里面<style>標(biāo)簽的lang屬性斟湃,動(dòng)態(tài)增加對應(yīng)loader處理熏迹。大家可以看看里面是什么,挺有趣的凝赛。

系列文章:
《什么是構(gòu)建注暗? webpack打包思想?》
《webpack基礎(chǔ)使用》
《從vue-cli學(xué)webpack配置1——針對webpack2》
《從vue-cli學(xué)webpack配置2——針對webpack3》
《webpack 墓猎、mainfest 捆昏、runtime 、緩存與CommonsChunkPlugin》
《webpack打包慢的解決方案》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毙沾,一起剝皮案震驚了整個(gè)濱河市骗卜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌左胞,老刑警劉巖寇仓,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異烤宙,居然都是意外死亡遍烦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門门烂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乳愉,“玉大人兄淫,你說我怎么就攤上這事÷Γ” “怎么了捕虽?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坡脐。 經(jīng)常有香客問我泄私,道長,這世上最難降的妖魔是什么备闲? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任晌端,我火速辦了婚禮,結(jié)果婚禮上恬砂,老公的妹妹穿的比我還像新娘咧纠。我一直安慰自己,他們只是感情好泻骤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布漆羔。 她就那樣靜靜地躺著,像睡著了一般狱掂。 火紅的嫁衣襯著肌膚如雪演痒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天趋惨,我揣著相機(jī)與錄音鸟顺,去河邊找鬼。 笑死器虾,一個(gè)胖子當(dāng)著我的面吹牛讯嫂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兆沙,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼端姚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了挤悉?” 一聲冷哼從身側(cè)響起渐裸,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎装悲,沒想到半個(gè)月后昏鹃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诀诊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年洞渤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片属瓣。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡载迄,死狀恐怖讯柔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情护昧,我是刑警寧澤魂迄,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站惋耙,受9級特大地震影響捣炬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绽榛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一湿酸、第九天 我趴在偏房一處隱蔽的房頂上張望灭美。 院中可真熱鬧届腐,春花似錦美莫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窝撵。三九已至傀顾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碌奉,已是汗流浹背短曾。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嫉拐,地道東北人魁兼。 一個(gè)月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓婉徘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咐汞。 傳聞我的和親對象是個(gè)殘疾皇子盖呼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容