因為最近在搞百度地圖API的時候到了webpack的externals,才發現我之前都只是用webpack做一些搭建完項目後的“收尾工作”——即打包,而沒有把它納入到項目開發的“主體過程”中來,真是“物不盡其用”。於是就有了我今天的這篇文章。 本文提綱:1.複習webpack的知識 2.詳解webp... ...
[前言]:因為最近在搞百度地圖API的時候用到了webpack的externals,才發現我之前都只是用webpack做一些搭建完項目後的“收尾工作”——即打包,而沒有把它納入到項目開發的“主體過程”中來,真是“物不盡其用”。於是就有了我今天的這篇學習文章:利用webpack-dev-server搭建一個webpack的伺服器 參考資料: webpack-dev-server的github地址:https://github.com/webpack/webpack-dev-server webpack1官方文檔http://webpack.github.io/docs/webpack-dev-server.html(推薦看2的文檔) webpack2官方文檔https://webpack.js.org/configuration/dev-server/#devserver(推薦讀這個) 提綱: 1.複習webpack的知識 2.詳解webpack-dev-server的配置屬性 3.webpack-dev-server的自動刷新和模塊熱替換機制 4.webpack下配置伺服器的三種方式 複習一下webpack的知識
我將目錄結構簡化之後長這樣: 我的webpack.config.js:
var webpack = require('webpack') var path =require('path') module.exports = { entry:{ app:path.join(__dirname,'src','index.js') }, output:{ filename:'bundle.js', path:path.join(__dirname,'dist') } }
我的src/index.js:
require('./a.js') require('./b.js') console.log('我是index.js')我的src/a.js:
console.log('我是a.js')我的src/b.js:
console.log('我是b.js')我的dist/index.html:
/*其他部分省略*/ <body> <script src="./bundle.js"></script> </body>當我們在終端運行“webpack”命令後,目錄變為:
一張圖複習一下webpack的機制:
OK,下麵,讓我們開始搭建一個伺服器吧: 怎麼用最簡單的方式搭建一個伺服器? 1.你需要一個安裝一個模塊 在終端中進入項目目錄下,敲下npm install webpack-dev-server --save-dev回車 2.在終端運行一段命令: node_modules/.bin/webpack-dev-server(網上有的說直接輸webpack-dev-server那是錯的) 成功!輸出的是這一段信息:
然後進入預設的localhost:8080頁面: 伺服器的根目錄就是我們工程的目錄 到這裡,我們要做的第一步就成功啦! 進入dist後,我們發現報了這樣一段錯誤: what?沒有找到bundle.js? 所以我們要在webpack.config.js里寫一下配置:
module.exports = { /*這裡省略entry和output,參照上面寫的內容*/ devServer: { contentBase: path.join(__dirname, "dist") } }保存,後打開頁面看控制台,報錯已經消失了!正確列印了被打包的文件內容:
詳解webpack-dev-server的配置屬性
1.devServer.contentBase contentBase是我們今天要講的第一個webpack-dev-server的配置屬性,那麼,contentBase做了什麼事情呢?——它指定了伺服器資源的根目錄,如果不寫入contentBase的值,那麼contentBase預設是項目的目錄。 在上面例子中產生錯誤和後來解決錯誤的原因: 產生錯誤:因為bundle.js被"放在了"我們的項目根目錄里,在dist/html里<script src="./bundle.js"></script>此時顯然不能根據路徑找到bundle.js 解決錯誤:通過配置contentBase: path.join(__dirname, "dist")將bundle.js"放在了"dist目錄下,此時bundle.js和dist/index.html位於同一目錄下,通過 src="./bundle.js"自然就找到bundle.js了 webpack打包和webpack-dev-server開啟服務的區別—— webpack輸出真實的文件,而webpack-dev-server輸出的文件只存在於記憶體中,不輸出真實的文件!(註意下麵兩張圖的區別) webpack:當我們在終端運行"webpack"後:webpack-dev-server:當我們在終端運行"node_modules/.bin/webpack-dev-server後: 這也就是我在上面的闡述中將bundle.js"放在了"加上雙引號的原因 2.devServer.port port配置屬性指定了開啟服務的埠號:
devServer: { port:7000 }設置埠號為7000: 運行:node_modules/.bin/webpack-dev-server 這個時候就不是預設的8080的埠了,而是我們設置的7000 3.devServer.host host設置的是伺服器的主機號: 修改配置為:
devServer: { contentBase: path.join(__dirname, "dist"), port:7000, host:'0.0.0.0' }
此時localhost:7000和0.0.0.0:7000都能訪問成功 4.devServer.historyApiFallback 在文檔裡面說的很清楚,這個配置屬性是用來應對返回404頁面時定向到特定頁面用的(the index.html page will likely have to be served in place of any 404 responses) 在dist目錄下新增一個HTML頁面:
/*剩下的都是很常規的HTML內容,故省略*/ <p>這裡是404界面</p>我們把webpack.config.js修改如下:
module.exports = { /*這裡省略entry和output,參照上面寫的內容*/ devServer: { contentBase: path.join(__dirname, "dist"), historyApiFallback:{ rewrites:[ {from:/./,to:'/404.html'} ] } } }打開頁面,輸入一個不存在的路由地址:
5.devServer.overlay 這個配置屬性用來在編譯出錯的時候,在瀏覽器頁面上顯示錯誤,預設是false,可設置為true 首先我們人為製造一個編譯錯誤:在我們尚未配置babel loader的項目里使用ES6寫法: 在src/index.js里寫入“const a” 在shell里提示編譯錯誤: 但在瀏覽器里沒有提示: 所以我們把webpack.config.js修改為:
module.exports = { /*這裡省略entry和output,參照上面寫的內容*/ devServer: { contentBase: path.join(__dirname, "dist"), overlay: true } }再重新運行node_modules/.bin/webpack-dev-server,瀏覽器上把錯誤顯示出來了
6.devServer.stats(字元串) 這個配置屬性用來控制編譯的時候shell上的輸出內容,我們沒有設置devServer.stats時候編譯輸出是這樣子的: (其中看起來有許多看似不重要的文件也被列印出來了)
stats: "errors-only"表示只列印錯誤: 我們把配置改成:
devServer: { contentBase: path.join(__dirname, "dist"), stats: "errors-only" }因為只有錯誤才被列印,所以,大多數信息都略過了 除此之外還有"minimal","normal","verbose",這裡不多加贅述 7.devServer.quiet 當這個配置屬性和devServer.stats屬於同一類型的配置屬性 當它被設置為true的時候,控制台只輸出第一次編譯的信息,當你保存後再次編譯的時候不會輸出任何內容,包括錯誤和警告 來做個對比吧: quiet:false(預設): 第一次編譯: 第二次編譯(當你保存的時候)
quiet:true
第一次編譯同上 第二次編譯什麼都不輸出 【吐槽】這樣看的話感覺這個配置好像只有負面作用呢..... 8.devServer.compress 這是一個布爾型的值,當它被設置為true的時候對所有的伺服器資源採用gzip壓縮 採用gzip壓縮的優點和缺點: 優點:對JS,CSS資源的壓縮率很高,可以極大得提高文件傳輸的速率,從而提升web性能 缺點:服務端要對文件進行壓縮,而客戶端要進行解壓,增加了兩邊的負載 9.devServer.hot和devServer.inline 在這之前,首先要說一下的是webpack-dev-server的自動刷新和模塊熱替換機制 webpack-dev-server的自動刷新和模塊熱替換機制 這兩個機制是緊緊聯繫在一起的 從外部角度看——自動刷新 當我們對業務代碼做了一些修改然後保存後(command+s),頁面會自動刷新,我們所做的修改會直接同步到頁面上,而不需要我們刷新頁面,或重新開啟服務 (The webpack-dev-server supports multiple modes to automatically refresh the page) 從內部角度看——模塊熱替換 在熱替換(HMR)機制里,不是重載整個頁面,HMR程式會只載入被更新的那一部分模塊,然後將其註入到運行中的APP中 (In Hot Module Replacement, the bundle is notified that a change happened. Rather than a full page reload, a Hot Module Replacement runtime could then load the updated modules and inject them into a running app.) webpack-dev-server有兩種模式可以實現自動刷新和模塊熱替換機制 1. Iframe mode(預設,無需配置) 頁面被嵌入在一個iframe裡面,並且在模塊變化的時候重載頁面 2.inline mode(需配置)添加到bundle.js中 當刷新頁面的時候,一個小型的客戶端被添加到webpack.config.js的入口文件中 例如在我們的例子中,在使用inline mode的熱替換後,相當於入口文件從entry:{ app:path.join(__dirname,'src','index.js') }變成了:
entry:{ app:[path.join(__dirname,'src','index.js'), 'webpack-dev-server/client?http://localhost:8080/' ] }從一個入口變成了兩個入口,並實現刷新 那怎麼才能inline mode模式的刷新呢? 你需要做這些: 1在配置中寫入devServer.hot:true和devServer.inline:true 2增加一個插件配置webpack.HotModuleReplacementPlugin() 例如:
var webpack = require('webpack') module.exports = { /*省略entry ,output等內容*/ plugins:[ new webpack.HotModuleReplacementPlugin() ], devServer: { inline:true, hot:true } }打開頁面: 如果有上面兩行輸出則表明你已經配置成功 現在還有一個問題,那就是每次直接輸入node_modules/.bin/webpack-dev-server來啟動伺服器對我們來說簡直就是莫大的痛苦,那麼怎麼對這一過程進行簡化呢? 答案:把這個運行腳本寫到package.json里就行了! 這裡我把我的package.json寫成:
{ "name": "webpackTest2", "dependencies": {}, "devDependencies": {}, "scripts": { "start": "node_modules/.bin/webpack-dev-server" } }在終端運行npm start: 運行成功! 配置服務的三種方式: 1在webpack.config.js輸出對象中的devServer屬性中寫配置(也就是我們上述所有例子的做法) 2寫在package.json中,寫在node 命令對應的腳本中,例如我們可以寫成: "scripts": { "start": "node_modules/.bin/webpack-dev-server --port 8000 --inline true " } (但這顯然並不是一個值得推薦的方式) 3使用純node的API實現,下麵是一個官方給的例子
var config = require("./webpack.config.js"); config.entry.app.unshift("webpack-dev-server/client?http://localhost:8080/"); var compiler = webpack(config); var server = new WebpackDevServer(compiler, { /*我們寫入配置的地方*/ }); server.listen(8080);