好久之前,互聯網行業有個職位叫作 「軟件開發工程師」 在那個時代,你們可能會蒙,放在現今社會,你們會以爲這個職位太過籠統,因此在此提一下那個時候的互聯網行業狀態。javascript
第一個階段就是開始對着W3C的文檔,拿着txt文本文件一個字母字母的敲着代碼,那個年代,真的單純舒服,上來就是一個項目的文件夾,而後就開始img、js、css三個完美的文件夾,再接上一個index.html,就開始到網上各類下載類庫jquery、underscore.js,而後手動的引入各類類庫,固然過程也伴隨着痛苦。php
第二個階段就是在接觸到Vue的時候直接上vue-cli的時候,幾行腳本就能夠啓動本地開發服務和打包線上資源,初次嘗試了webpack這個打包工具,好像對其餘問題不須要作過多的考慮,直接開始業務開發,其餘的事情cli都幫助處理好了。vue init webpack helloWorld, cd helloWorld && cnpm install && npm run dev,真香警告,對工程化只知其一;不知其二,可是好用,方便css
第三個階段就是給公司項目升級webpack過程當中體驗到了更細粒度的控制工程,體驗工程化的過程,實際的體驗到了工程化爲咱們作的事情html
這兩類是不一樣的東西,一個能夠理解爲是任務執行器而另一個是模塊打包器,任務執行器是能夠自動化的執行一些之前你須要手動操做的過程,見下面簡單的代碼前端
// coffee 源碼 console.log 'Hello World' //coffee轉 js coffee -c a.coffee (function(){ console.log('Hello World'); }).call(this); //執行編譯壓縮 uglify -s a.js -o a.min.js (function(){console.log("Hello World")}).call(this);
coffee須要編譯成瀏覽器支持的js,你須要手動的去執行上面的幾個命令,若是如今須要去修改源碼,在Hello World 後面加上一個!,加完你須要手動的去執行兩條命令重複的去編譯操做。以 gulp 爲例,編寫 gulpfile.js來幫助咱們完成重複的工做vue
gulp = require('gulp') coffee = require('gulp-coffee') uglify = require('gulp-uglify') rename = require('gulp-rename') file = './src/js/a.coffee' gulp.task('coffee', function(){ gulp.src(file) .pipe(coffee()) // 編譯 .pipe(uglify()) // 壓縮 .pipe(rename({ extname: ".min.js" })) .pipe(gulp.dest('./build/js')) gulp.task('watch', function(){ gulp.watch(file, ['coffee']) }) gulp.task('default', ['coffee'])
我只要執行一下 gulp watch 就能夠檢測到coffee文件的變化,而後爲你執行一系列的自動化操做,一樣的操做也發生在less, scss, 這些css的預處理器上。在修改到源文件的狀況下的編譯,壓縮這些重複操做都交由它來完成,在我看來Grunt / Gulp 算是一個可以自動化執行一些繁瑣重複的操做,提升生產效率,算是一個任務執行器。java
這些操做一樣也能夠由webpack完成,接下來咱們看一下官網給出的webpack的定義。node
webpack is a module bundler
官方給出的解釋是,webpack是一個模塊打包器,不是一個任務執行器,它是能夠配合Grunt / Gulp 使用的,相關連接webpack集成 。webpack打包器(bundler)幫助你生成準備用於部署的 JavaScript 和樣式表,將它們轉換爲適合瀏覽器的可用格式。例如,JavaScript的壓縮、chunk split和懶加載,以提升性能。因此webpack和Gulp/Grunt之間是有必定功能的重疊,可是處理合適,是能夠一塊兒配合工做的,不存在所謂的誰替代誰,只是在某些場景下webpack的可以獨當一面,完成了Grunt/ Gulp的功能。jquery
rollup以及Parcel的出現,號稱零配置,足以讓一個配置成本比較高的webpack出現了4.0版本,固然也號稱零配置使用,開箱即用,如今來一塊兒看看webpack4.x和3.x比較大的區別,先給出一個Release連接webpack Release v4.0.0下面簡要的介紹一些大的改動webpack
首先先看下圖總體瞭解一下webpack的一些經常使用配置項
接下來簡單的瞭解一下webpack的一些基本概念
先簡短的介紹一下項目,升級的項目爲多入口項目,每一個module模塊代碼一個入口,而後共用不少業務組件
*/build webpack.base.conf.js // dev和prod共用部分 webpack.dev.conf.js // dev模式下特有配置 webapck.prod.conf.js // prod模式下特有配置 */common components // 多個模塊共用的組件 assets // 靜態資源 */src module1 // 模塊1的代碼 index.js // 模塊1代碼的入口 module2 // 模塊2的代碼 module3 // 模塊3的代碼 */mock mock-xxx.js // mock api的接口 */config // 配置文件 index.js dev.js prod.js
升級webpack,安裝webpack-cli。全局安裝npm-check-updates,查看package.json中可升級的依賴的版本。
cnpm install -g npm-check-updates //全局安裝 // 在項目根目錄下執行ncu ncu // 可升級的依賴,列舉部分依賴狀況 axios 0.17.1 --> 0.18.0 webpack 3.5.5 --> 4.16.5 webpack-merge 4.1.0 --> 4.1.4 // 升級webpack 安裝webpack-cli cnpm install webpack webpack-cli --save-dev // 升級相應的loader和plugin cnpm install url-loader file-loader vue-loader sass-loader css-loader babel-loader html-webpack-plugin --save-dev
公司使用的vue-cli2.0的腳手架生成的項目,簡單的列舉關於webpack的目錄,針對須要可進行自行調整
*/build webpack.base.conf.js // dev和prod共用部分 webpack.dev.conf.js // dev模式下特有配置 webapck.prod.conf.js // prod模式下特有配置 // 經過webpack-merge合併配置輸出最後的webpack配置
1:增長mode模式
// webpack.dev.conf.js 開發模式的配置 開啓webpack默認的配置優化 mode: 'development' // webpack.prod.conf.js 生產模式的配置 mode: 'production'
2:升級vue-loader,vue-loaderv.15版本和以前的有所區別,vue-loader不在使用自身的配置,而是解析.vue文件以後使用webpack裏配置的loader,詳細文檔見Vue-loader的使用,補充一點:在.vue 文件中style提供的scoped標記,就是經過vue-loader去實如今template中加入了適當的hash,配合樣式去作到組件內樣式的獨立
// webpack.config.js const VueLoaderPlugin = require('vue-loader/lib/plugin') // 插件解析.vue文件把template script style 分別交給webpack配置的loader去處理 module.exports = { // ... plugins: [ new VueLoaderPlugin() ] }
3:部分webpack的插件已經配置停用。若有的話,按照提示刪除掉,好比module裏的loaders,webpack4再也不支持。而後部分插件轉化爲經過optimazation 進行配置,主要配置點在於code split以及mainifest文件的提取,一樣見下文splitChunks和runtimeChunks的分析
4:開發模式加入devServer。項目是否須要經過手動node搭建本地服務器,取決因而否須要node層面去處理其餘東西,在公司項目中啓動服務前有兩個操做,編譯scss文件(皮膚文件),以及mock文件夾(mock接口),因此仍是保留了手動檔搭建node層面啓動服務,其實徹底能夠有webpck-dev-server的before,after配置項完成
// 下面代碼塊針對devServer的部分配置項作說明,結合相關知識進行分析 devServer: { contentBase: path.join(__dirname, '../static'), // 靜態資源提供,不是webpack生成的bundle,生成的bundle在內存中見註釋1 host: host || 'localhost', // 主機名,若是你但願服務器外部可訪問,須要設置爲0.0.0.0,默認localhost port: process.env.PORT || port, // 端口號 historyApiFallback: true, // handle fallback for HTML5 history API 瞭解一下這個東西見註釋2 proxy: proxyTable, // 後端接口轉發見註釋3 hot: true, // 熱更新見註釋4 quiet: true, // webpack打包信息省略 publicPath: assetsPublicPath, // bundle 的位置 outpath: publicPath 相似 clientLogLevel: "none" // HRM WDS 在瀏覽器控制檯的輸出 } // 註釋1:寫過express的就知道有一個指定static中間件用來指定資源目錄的 const express = require('express') const app = express() app.use(staticPath, express.static(xxx)) // xxx/js/vendor.js,就能夠經過localhost:prot/staticPath/js/vendor.js訪問相關靜態資源。因此contentBase就是利用express靜態中間價提供個一種訪問靜態資源文件的能力 // 註釋2:vue-router的history模式充分利用 history.pushState API 來完成 URL 跳轉而無須從新加載頁面 example: history.pushState({a:1}, '測試', '/attendance/index') ,而後內部去處理相應的router對象的展現Vue頁面和邏輯,因此這就是你順着程序點路由能夠進去,可是刷新的時候,就顯示404的緣由,由於該路由在服務器上不是真實存在的,而是在index.html中經過JS去解析模擬的,這就須要咱們生產模式下生產的dist文件全部的請求都轉發到index.html。處理方式:在服務器上經過nignx 代理,或者起一個express服務,經過第三方類庫 connect-history-api-fallback,固然也能夠原生Node去寫,此時Node只是一個文件服務器 const http = require('http') const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { fs.readFile('index.htm', 'utf-8', (err, content) => { if (err) { console.log('We cannot open "index.htm" file.') } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) res.end(content) }) }).listen(httpPort, () => { console.log('Server listening on: http://localhost:%s', httpPort) }) // 註釋3:請求代理,經常被問起跨域請求有哪些方案,jsonp、CORS(後端配合)、window iframe等方式,還有一個就是經過node代碼轉發請求,服務端請求不存在跨域的概念,webpack中能夠node本身使用http-proxy-middleware 去進行代理,本次更新使用的wbepack-dev-server模塊同時也依賴了http-proxy-middleware的庫,目前Node不做爲純後端,而是做爲中間代碼,鏈接純後端(java php)和前端。 // 註釋4:熱更新,它容許在運行時更新各類模塊,而無需進行徹底刷新,目前的公司項目都是純刷新的方式,熱更新HRM,這個是須要你是用的loader或插件幫助你完成,他們能監聽webpck complier期間得鉤子,而後給出相應源碼更新後須要patch,推送到前端,打補丁,而後實現熱更新,而不是刷新整個頁面去從新加載頁面。好處天然提升開發效率,在修改.vue文件的tempalte和style以及script中不是vue生命週期函數時,是可以保留到當時的vue的各類狀態
5:針對生產模式的優化配置,廢棄生產模式中使用的優化插件,轉爲webpack4的optimization.* 配置
下面爲升級以前的配置
// 依賴module(require, import 導入的文件)來自node_modules且以.js 結尾的文件將會被打包到名爲vendor的bundle中 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 ) } }), // 在vendor的bundle中把manifest提取出來(mainifest算是webpack實現的在瀏覽器端進行模擬加載模塊和具體加載邏輯的代碼塊,這個最好拆出來,否則無法作緩存,具體見到長效緩存分析) new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', chunks: ['vendor'] }) // 打包以後就會生成一個vendor.js (裏面有着來自node_modules 的第三方類庫)和 mainifest.js (具體加載邏輯)
升級以後,完成原有的配置很簡單,簡單的照着API實現便可,本次升級伴隨着兩個重點,開發模式的rebuild時間,生成環境的打包文件體積和時間,因此還須要作其餘優化
optimization: { splitChunks: { cacheGroups: { // module只要知足下面就會從原來的chunks中抽取出來打包到對應的chunks之中 // 這個vendors是在至少同時有兩個initial Chunk中引入的來自node_modules的第三方類庫會被打包到chunks-vendors中 vendors: { name: 'chunk-vendors', test: /[\\\/]node_modules[\\\/]/, priority: -10, minChunks: 2, // 至少同時有幾個chunk知足纔會有可能從這些chunks中提取一些代碼到新的chunk中,也就是至少兩個共用纔會提取,否則就直接打包都所在module的init chunk中 chunks: 'initial' // chunk的概念:代碼分割這些操做做用於那些chunk文件,initial是經過提供給webpack入口生成的chunks, async是經過以前提到的import() 路由懶加載造成的chunk, all就是全部的chunk文件 }, // 這個common跟上面的區別在於沒有test檢測module來源,只是只要有兩個chunk共用就是提取出來,很顯然就範圍來說,下面的大於上面,這時候到底這個來自node_modules的module進入哪一個chunk,取決於priority(優先級),誰高進入哪一個chunk common: { name: 'chunk-common', minChunks: 2, priority: -12, chunks: 'initial', minSize: 0 } } }, runtimeChunk: { name: 'manifest' } } // 好比某些第三方類庫只在某個Vue文件中使用,經過動態引入import('vue-qrcode-component').then() 一個類庫只在一個地方用,徹底沒有必要打包到vendor中(由於vue-qrcode-component 來自node_module) // import 就是新建一個chunk 這個chunk是沒有名字的,須要經過/* webpackChunkName: "loadsh"*/ 生成這個loadsh的chunk.name import(/* webpackChunkName: "loadsh"*/ 'loadsh').then(m => { const _ = m.default console.log(_.join(['hello', 'world'])) })
總結:通常通過這幾步驟就能完成一個webapck項目的升級,對於本身項目的複雜的地方須要額外的處理,寫着寫着發現篇幅愈來愈長了,把上文一直出現的splitChunk和runtimeChunk留到下個篇幅着重介紹一下。在公司作升級以前,給的指標不只僅是升級框架,還須要在dev模式開發的rebuild的速度更快(修改一個地方,rebuild的時間12s左右才能看到效果,痛苦,能夠經過lessModule來解決),在prod模式下打包的項目文件體積減少已經打包所須要的時間更短(一次測試環境發佈須要5,6分鐘)。如今vue-cli3.0已經出現了,找時間把vue-cli3.0源碼給你們分析一下,簡單的包裝了一下操做