寫給后端看的前端技術——webpack(上)
不懂“前端”說的virtual dom、ReactJS、Vue、Angularjs這一大堆東西,也不懂前端說的ES6的優雅,也不知道為啥我用Bootstrap、jQuery就得“剁手”。世界上總得有一篇文章是寫給后端工程師看的,后端寫給后端看的,不裝逼,認認真真。
我決定用webpack作為學習前端的第一步,一方面是由于“Build工具”幾乎是學習前端的第一道門檻;另一方面它已經“千秋萬代一統江湖”了所以請無視——gulp、grunt之類的吧。
一、前端工具鏈和Webpack
工具鏈是前端經常被吐槽的一個梗,我認為這不是前端技術更新太快,而是前端技術通俗易懂——山寨個輪子分分鐘的事情。用別人的工具不如自己做一個——多有面子。(或許這就是“文人相輕”吧)。用一幅圖表示webpack的位置
支撐整個B/S系統技術由三個組成——HTML、CSS、JavaScript。其中JavaScript比較特殊,借助V8引擎它可以被放到服務器端執行這就會Node.js。Node.js之于JavaScript猶如JVM之于Java,它為JavaScript提供了一個“運行環境”,這就給出了一個信號——我們可以用JavaScript做更多事情。
最開始嘗試的是用Node.js寫服務器端,這場運動造就了另一個東西——npm(Node Package Manager),通過npm定義的規范為JavaScript引入了“包”的概念,刺激了社區的發展一時間社區出現了非常豐富的、可以復用的庫(比如,出現了Express之類的Web Framework、甚至是ORM Framework)。
有了Node.js、npm之后進入了“全面造輪子”的時代,各種工具、各種庫、各種場景野蠻生長。其中有一小撮群眾迫切的需要一個“打包工具”。JavaScript代碼、CSS代碼越來越多我們期望能夠有一個工具可以合并JavaScript、合并CSS,如果可能捎帶“壓縮”一下大小。當然這種事情用Python、Java都可以做到問題是——“用別人家的語言多丟人啊”,我們現在有了Node.js分分鐘自己寫一個。于是就有了gulp、grunt、webpack之類的,當然這些工具功能更多(比如合并小圖片、作為開發服務器)但是它本質上還是一個“打包工具”(Python的PIP、Java的Maven)。
二、npm
就像前面說的那樣,你使用webpack必須安裝node.js——它是用JavaScript寫的一個工具所以必須要有運行環境。完整完node.js后你會驚喜的發現多了一個npm(恩,買一送一)——畢竟現在一個語言不帶上“包管理”都不好意思說自己是“現代編程語言了”。
首先我們需要一個符合npm標準的工程
npm的規范很簡單,只要你有一個package.json的配置文件就可以了,我們通過npm init來幫我們生成了一個。接下來用編輯器打開package.json就行了。
你可能已經猜出了了很多東西(沒猜到?請瀏覽npmjs.com上的package.json來理解每個配置項的含義)。我們重點關注script,它可以讓我們利用npm執行命令行(Shell),我來修改一下代碼
然后執行
以“>”開頭的輸出是npm的日志,最后的一句話才是“echo”執行的結果。
三、初探webpack
1. 前期準備
為了便于實驗我準備了兩個文件——index.js、index.html
index.html
index.js
用瀏覽器打開index.html就可以看到彈出的對話框了。
2. 初探Webpack
webpack是npm的一個標準庫,所以通過npm安裝它,指定--save-dev參數會自動修改package.json添加依賴(npm會在當前目錄創建一個node_modules文件夾,webpack和它的依賴都放在這里,你如果膽子大進去看一下目錄吧。恩,就是這么神奇。。就是這么多依賴~~~~)。
安裝完成后package.json被添加了webpack的依賴關系。(dev不用猜你也知道了,這是“開發環境”依賴,npm在執行打包的時候不會把它復制到生產環境,也就是說webpack其實是一個“開發工具”。)
webpack提供了一個命令行腳本路徑是node_modules/webpack/bin/webpack.js,我們可以直接在shell中執行這個js文件。(打開它你會發現第一行是#!/usr/bin/env node,所以它其實是由NodeJS執行的)現在讓webpack幫我們“打包”index.js,生成的文件叫bundle.js
修改index.html
程序是正常工作的,以后我們對外發布的時候不再使用main.js而是使用bundle.js。
四、引入jQuery
下面修改代碼,在頁面中放入一個按鈕,通過jQuery綁定按鈕的Click事件,點擊之后彈出Hello。首先需要添加jQuery依賴,通過--save讓npm保存jQuery依賴,這里沒有執行dev所以jQuery會被帶到生產環境
index.html
index.js
特別解釋一下第一句,這個是JavaScript的“模塊化”。JavaScript語言沒有模塊(或者叫“包”)、類等模塊化的概念這就給大家留下了很多想象空間,nodejs定義了require用來支持模塊化,通過這個語句會自動引入jquery.js文件(讀取node_modules/jquery/package.json中的main字段)——這就是CommonJS。但是NodeJS不能工作在瀏覽器段,于是就有了瀏覽器端的“模塊化”——AMD、CMD之類的。時至今日ES6已經作為JavaScript的新規范被大家接受,它終于引入了模塊化的語法——import xx from xx。
webpack同時支持CommonJS、ES6兩種語法,打包的時候會把所有的JavaScript和相關資源重新組合(比如合并JavaScript文件,合并小圖片,合并CSS),我們可以選擇一次性加載所有JavaScript也可以通過插件分成若干個Chunk加載。
在webpack中兩種語法沒有什么本質區別,我習慣性的會選擇require作為主要的方式,它可以指定完整路徑名非常便于兼容非npm模塊,還可以引入css文件、圖片,寫法也更加清晰。項目里有ES6的時候我會選擇用import語法。
繼續執行webpack生成build.js,這次我們會看到一些關于jquery的信息。
打開頁面刷新、點擊、程序是正常工作的,打開buildle.js看一下是不是發現webpack把jquery.js和我們的代碼合并到一起了?
4. 自動化
上面我們編輯完代碼之后還需要自己執行一下webpack,刷新頁面。我們希望編輯index.js后希望可以自動觸發webpack編譯輸出dist/build.js,為了實現這個必須引入兩個東西
- webpack.config配置文件
- webpack-dev-server插件
一直來我們都是通過命令行指定“源JavaScript”和“目標JavaScript”,現實中我們一般是通過配置文件指定的就是——webpack.config.js
現在執行node_modules/webpack/bin/webpack.js不加參數,webpack就會使用這個配置文件了。每次都輸入這么長的名字也是比較煩的,我們可以通過npm來調用webpack。仔細想想npm中的scripts定義的都是shell命令,所以我們可以修改成
npm執行命令行的時候會把node_modules下的一些bin目錄(比如 webpack的是node_modules/webpack/bin)加入到PATH環境變量中而且允許以.js結尾的文件不指定后綴名。所以build這里我們直接寫一個webpack就行了。(webpack其實是寫成node_modules/webpack/bin/webpack.js的縮寫)
通過npm 執行build
我們希望webpack可以自動“檢查”index.js,發現更新后可以自動編譯。這時候就必須安裝web-dev-server這個插件了
修改webpack.config.js
新增了devServer部分,publicPath指定了“發布路徑”。webpack-dev-server會檢測index.js的變化輸出的時候并不是把目標文件寫到硬盤中而是在內存里,此處指定輸出的文件是bundle.js,如果不指定publicPath,我們訪問它的路徑應該是localhost:3000/bundle.js。訪問index.html的路徑是localhost:3000/index.html,而HTML引入的路徑是dist/bundle.js所以此處指定publicPath。(webpack-dev-server輸出路徑就會變成localhost:3000/dist/bundle.js,剛好和html呼應)
修改package.json,引入新的run命令
執行npm run build會輸出文件到dist/bundle.js用于發布生產,執行npm run start會執行dev-server,不使用硬盤上的bundle.js用于開發測試。
(你可以已經猜到了webpack-dev-server也有一個bin目錄,它的原理和webpack在script字段中的原理是一樣的)
五、代碼
https://github.com/fireflyc/front-demo/tree/v1
【本文是51CTO專欄作者“邢森”的原創文章,轉載請聯系作者本人獲取授權】