【C++】《C++ Primer 》第十六章
// JS
.js .jsx .coffee .ts(TypeScript 類 C# 語言)
// CSS
.css .less .sass .scss
// Images
.jpg .png .gif .bmp .svg
// 字型檔案
.svg .ttf .eot .woff .woff2
// 模板檔案
.ejs .jade .vue【這是在webpack中定義元件的方式,推薦這麼用】
2、可以使用之前學過的requireJS、也可以使用webpack可以解決各個包之間的複雜依賴關係。
webpack 是前端的一個專案構建工具,它是基於Node.js開發出來的一個前端工具
藉助於webpack這個前端自動化構建工具,可以完美實現資源的合併、打包、壓縮、混淆等諸多功能。
npm i webpack -g # 全域性安裝,這樣就能全域性使用webpack命令
在根專案中執行
npm init
npm i webpack --save-dev # 安裝到專案依賴中
在main.js中我們使用ES6高階語法:匯入jquery,實現隔行換色
// 注意:如果要通過路徑的形式,去引入node_modules中相關的檔案,可以直接省略 路徑前面的// node_modules 這一層目錄,直接寫 包的名稱,然後後面跟上具體的檔案路徑 // 不寫node_modlues 這一層目錄,預設就回去 node_modules 中查詢。 import $ from 'jquery' $(function(){ $('li:odd').css('backgroundColor','red'); $('li:even').css('backgroundColor','blue'); })
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <!-- 直接引用main.js會報語法錯誤,因為瀏覽器不能使用ES6 --> <!-- <script src="./main.js"></script> --> </head> <body> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> </body> </html>
執行webpack命令,將ES6程式碼轉為能被瀏覽器解析的ES5
// webpack src_file -o dist_file // 進入專案的根目錄下,執行命令 webpack .\src\main.js -o .\dist\bundle.js // -o表示這是一箇舊版的webpack命令,新版的已經不在使用該命令,但是為了能讓新版的 // webpack使用舊版的命令要加-o
最後我們在index.html頁面中引入dist目錄下的bundle.js,即可看見隔行換色。
webpack .\src\main.js -o .\dist\bundle.js
1、在專案的根目錄下建立webpack.config.js檔案,配置如下:
const path = require('path') // 這個配置檔案,起始就是一個 JS 檔案,通過 Node 中的模組 // 操作,向外暴露一個配置物件 module.exports = { // 既然用配置檔案來替代命令方式,那 // 必然是要指定一些入口和出口的 entry: path.join(__dirname, './src/main.js'),// 入口,表示,要使用webpack 打包哪個檔案 output: { // 輸出檔案相關的配置 path: path.join(__dirname, './dist'), // 指定 打包好的檔案,輸出到哪個目錄中去 filename: 'bundle.js' // 這是指定 輸出的檔案的名稱 } // 當我們在控制檯,直接輸入webpack 命令執行的時候,webpack做了以下幾步: // 1.首先,webpack 發現,我們並沒有通過命令的形式,給它指定入口和出口 // 2.webpack就會去 專案的 根目錄中,查詢一個叫做'webpack.config.js'的 // 配置檔案 // 3.當找到配置檔案後,webpack 會去解析執行這個 配置檔案,當解析執行完配置檔案後,就 // 得到了 配置檔案中,匯出的配置物件 // 4.當webpack拿到配置物件後,就拿到了配置物件中,指定的 入口 和出口,然後進行打包構建 }
webpack
但是現在還是有問題啊,它並沒有幫助我們簡化啊,我每次改動程式碼還是要在命令列裡執行一下 webpack 命令,方便在哪了呢?除了命令少敲了幾個字母沒感覺哪裡方便了。好,為了更進一步的方便我們每次改動程式碼儲存後webpack都重新編譯一下,webpack提供了這麼一個工具叫:webpack-dev-server,這個工具能實時的監聽配置檔案裡所監聽的檔案的改動,每當改動並儲存後,該工具就會自動的編譯。來看一下怎麼使用。
npm init
cnpm i webpack-dev-server -D # -D是-dev的簡寫表示開發依賴,-S是--save的簡寫表示專案依賴
注意:webpack-dev-server必須安裝到專案裡,不能安裝到全域性,為什麼馬上在做說明。
webpack-dev-server要求你的webpack必須在本地安裝(安裝到全域性的webpack它檢測不到),所以我們還有安裝警告資訊,安裝一下本地的webpack
cnpm i webpack -D
// 根目錄下直接執行 webpack-dev-server webpack-dev-server
我們明明安裝了這個工具啊?但是為什麼不能用?因為我們是在本地安裝的,不是在全局裡安裝的,所以不能直接執行。所以我們只好嘗試另外一種,在package.json檔案裡定義命令然後執行。
{ "name": "webpack-study", "version": "1.0.0", "description": "webpack study", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack-dev-server" }, "author": "@liuwei", "license": "ISC", "devDependencies": { "jquery": "^3.4.1", "webpack": "^4.35.3", "webpack-cli": "^3.3.5", "webpack-dev-server": "^3.7.2" } }
如上在package.json的scripts裡新增一個執行命令為"dev",該命令的指令碼即為我們剛才執行失敗的webpack-dev-server,然後儲存,在根目錄下執行:
npm run dev
首先執行npm run dev時就會執行我們的webpack-dev-server,但是報了一個錯,錯誤的大概意思好像是說我們還要安裝一個webpack-cli,好吧我們裝上試試:
cnpm install webpack-cli
npm run dev
> [email protected] dev d:\前端\Study\Vue\webpack-study > webpack-dev-server i 「wds」: Project is running at http://localhost:8080/ i 「wds」: webpack output is served from / i 「wds」: Content not from webpack is served from d:\前端\Study\Vue\webpack-study i 「wdm」: Hash: 15fbb07cc912bfcbd911 Version: webpack 4.35.3 Time: 877ms Built at: 2019-07-09 21:35:05 Asset Size Chunks Chunk Names bundle.js 674 KiB main [emitted] main Entrypoint main = bundle.js [0] multi ./node_modules/[email protected]@webpack-dev-server/client?http://localhost ./src/main.js 40 bytes {main} [built] [./node_modules/[email protected]@ansi-html/index.js] 4.16 KiB {main} [built] [./node_modules/[email protected]@ansi-regex/index.js] 135 bytes {main} [built] [./node_modules/[email protected]@html-entities/index.js] 231 bytes {main} [built] [./node_modules/[email protected]@jquery/dist/jquery.js] 274 KiB {main} [built] [./node_modules/[email protected]@strip-ansi/index.js] 161 bytes {main} [built] [./node_modules/[email protected]@webpack-dev-server/client/index.js?http://localhost] ./node_modules/[email protected]@webpack-dev-server/client?http://localhost 4.29 KiB {main} [built] [./node_modules/[email protected]@webpack-dev-server/client/overlay.js] 3.51 KiB {main} [built] [./node_modules/[email protected]@webpack-dev-server/client/socket.js] 1.53 KiB {main} [built] [./node_modules/[email protected]@webpack-dev-server/client/utils/createSocketUrl.js] 2.77 KiB {main} [built] [./node_modules/[email protected]@webpack-dev-server/client/utils/log.js] 964 bytes {main} [built] [./node_modules/[email protected]@webpack-dev-server/client/utils/reloadApp.js] 1.63 KiB {main} [built] [./node_modules/[email protected]@webpack-dev-server/client/utils/sendMessage.js] 402 bytes {main} [built] [./node_modules/webpack/hot sync ^\.\/log$] ./node_modules/webpack/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built] [./src/main.js] 139 bytes {main} [built] + 19 hidden modules i 「wdm」: Compiled successfully.
注意看:wds提示中,說這個專案已經被webpack-dev-server託管在了8080埠上,我們去訪問一下,還真是。緊接著下面一句:
i 「wds」: webpack output is served from /
這句話的意思大概是說:webpack打包成功的輸出檔案要以/去訪問。我們在瀏覽器上訪問一下:http://localhost:8080/bundle.js
真的出現了,那就有問題了,這個/bundle.js和我們根目錄下二點dist/bundle.js有什麼關係?我們嘗試來更改一下程式碼,會發現webpack-dev-server確實又重新編譯了,但是我們重新整理頁面卻發現剛才改的並沒有在頁面時呈現,這是為啥啊?
注意:webpack-dev-server會在記憶體中建立一個bundle.js(我們的output),這個bundle.js並不是在磁碟上的/dist/bundle.js檔案,而我們專案裡依賴的確實磁碟上的/dist/bundle.js,所以才會導致這個問題。
不過反過來你細細想一下:如果我們每次save程式碼,webpack-dev-server都重新編譯然後在寫到磁碟上這樣效率會大大降低,所以webpack-dev-server將其放在記憶體中是合理的。
「wds」: webpack output is served from /
我們要依賴這個記憶體中的bundle.js,要更改一下index.html的·依賴:
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <!-- <script src="./main.js"></script> --> <!-- <script src="../dist/bundle.js"></script> --> <!-- 不在依賴於磁碟上的bundle.js,而是依賴於webpack-dev-server“實時”編譯的的記憶體中的bundle.js --> <script src="/bundle.js"></script> </head>
我們在來改變一下main.js裡的程式碼,就會發現webpack-dev-server又重新編譯了,看一下瀏覽器,你會發現,webpack-dev-server還幫我們自動重新整理了瀏覽器,也就是說每當我們儲存程式碼,webpack會先編譯好,然後重新整理瀏覽器,這樣就做到實時的了。
我們可以認為, webpack-dev-server 把打包好的 檔案,以一種虛擬的形式,託管到了 咱們專案的 根目錄中,雖然我們看不到它,但是,可以認為, 和 dist src node_modules 平級,有一個看不見的檔案,叫做 bundle.js。
既然webpack-dev-server能啟動一個伺服器,那我怎麼改這個伺服器的預設埠呢?怎麼讓它一編譯完成就自動啟動我的瀏覽器?
--contentBase src:設定預設開啟瀏覽器後訪問的根路徑為src
--hot:以補丁的形式更新編譯的程式碼,而不是重新生成bundle.js
既然我們能把編譯好的js檔案讓webpack-dev-server放在記憶體中託管,那麼我們的html頁面能不能也被託管到記憶體中?html-webpack-plugin外掛就是用來實現這個功能的。
cnpm i html-webpack-plugin -D
const path = require('path') // 引入html-webpack-plugin外掛 const htmlWebpackPlugin = require('html-webpack-plugin') // 這個配置檔案,起始就是一個 JS 檔案,通過 Node 中的模組 // 操作,向外暴露一個配置物件 module.exports = { // 既然用配置檔案來替代命令方式,那 // 必然是要指定一些入口和出口的 entry: path.join(__dirname, './src/main.js'),// 入口,表示,要使用webpack 打包哪個檔案 output: { // 輸出檔案相關的配置 path: path.join(__dirname, './dist'), // 指定 打包好的檔案,輸出到哪個目錄中去 filename: 'bundle.js' // 這是指定 輸出的檔案的名稱 }, // 新增外掛 plugins:[ // 建立一個html-wabpack-plugin外掛 new htmlWebpackPlugin({ // 建立一個 在記憶體中生成 HTML 頁面的外掛 template:path.join(__dirname,'./src/index.html'), // 指定 模板頁面,將來 // 會根據指定的頁面路徑,去生成記憶體中的頁面 filename:'index.html' // 指定生成的頁面名稱 // 注意filename要和指定的模板頁面名稱一致,否則webpack-dev-server不會使用 }) ] // 當我們在控制檯,直接輸入webpack 命令執行的時候,webpack做了以下幾步: // 1.首先,webpack 發現,我們並沒有通過命令的形式,給它指定入口和出口 // 2.webpack就會去 專案的 根目錄中,查詢一個叫做'webpack.config.js'的 // 配置檔案 // 3.當找到配置檔案後,webpack 會去解析執行這個 配置檔案,當解析執行完配置檔案後,就 // 得到了 配置檔案中,匯出的配置物件 // 4.當webpack拿到配置物件後,就拿到了配置物件中,指定的 入口 和出口,然後進行打包構建 }
然後如果你對比了之前的頁面你會發現記憶體中的頁面和之前訪問磁碟上的頁面大有不同,我們來對比一下:
對比你會發現,訪問記憶體中webpack-dev-server幫我們管理的index.html中的末尾自動幫我們引入了記憶體中的bundle.js【這兩者都存在於記憶體中,或者說虛擬根目錄中,在同級目錄下引入所以沒加/】。既然它會自動幫我們引入記憶體中的bundle.js那我們頁面不就不用手動引入了嗎?試一試:去除index.html裡的bundle.js,重啟服務,果然。
那到此我們知道了html-webpack-plugin外掛會將我們的html頁面也在記憶體中"實時"[save]生成一份,而且它還會幫我們自動引入bundle.js,我就有個疑問了,我們頁面中並沒有引入bundle.js它是怎麼知道我們要往頁面中引入這個js檔案的?羊毛出在羊身上,我們還是在webpack.config.js檔案裡找答案,在這個js配置檔案中,我們將需要引入html頁面的main.js經過webpack編譯在記憶體中生成了bundle.js,我覺得webpack就是根據這個來的,webpack會將所有的這樣的js都引進記憶體中的某個html檔案,這是它的一種處理方式,畢竟它的理念就是儘量減少在頁面中引入其他依賴。
cnpm i style-loader css-loader --save-dev
import $ from 'jquery' // 使用import 語法,匯入css樣式表 import './css/index.css' // 注意:webpack,預設只能打包處理 JS 型別的檔案,無法處理 其他的非 JS 型別的檔案; // 如果要處理 非JS型別的檔案,我們需要手動安裝一些 合適 第三方 loader 載入器; // 1.如果想要打包處理 css檔案,需要安裝cnpm i style-loader css-loader -D // 2.開啟webpack.config.js這個配置檔案,在裡面,新增一個配置節點,叫做 // module,它是一個物件;在這個module物件身上,有個rules屬性,這個rules屬性是個陣列; // 這個陣列中,存放了,所有第三方的匹配和處理規則 $(function () { $('li:odd').css('backgroundColor', 'yellow'); $('li:even').css('backgroundColor', 'blue'); })
const path = require('path') const htmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: path.join(__dirname, './src/main.js'),// 入口,表示,要使用webpack 打包哪個檔案 output: { // 輸出檔案相關的配置 path: path.join(__dirname, './dist'), // 指定 打包好的檔案,輸出到哪個目錄中去 filename: 'bundle.js' // 這是指定 輸出的檔案的名稱 }, plugins: [ // 建立一個html-wabpack-plugin外掛 new htmlWebpackPlugin({ // 建立一個 在記憶體中生成 HTML 頁面的外掛 template: path.join(__dirname, './src/index.html'), // 指定 模板頁面,將來 // 會根據指定的頁面路徑,去生成記憶體中的頁面 filename: 'index.html' }) ], module: { // 這個節點,用於配置 所有 第三方模組 載入器 rules: [ // 所有第三方模組的 匹配規則 { test: /\.css/, use: ['style-loader', 'css-loader'] } // 匹配處理 .css 檔案的第三方loader規則 ] } }
webpack 只處理打包JS檔案型別,那麼對於第三方的檔案型別比如.css,.less等怎麼辦?
1、發現這個要處理的檔案不是JS檔案,然後就去 配置檔案中,查詢有沒有對應的第三方 loader 規則。
2、如果能找到對應的規則,就會呼叫 對應的loader 處理 這種檔案型別;
3、在呼叫loader的時候,是從後往前呼叫的;css-loader -> style-loader
4、當最後一個laoder 呼叫完畢後,會把處理的結果,直接交給webpack 進行 打包合併,最終輸出到bundle.js中去。
import './css/index.less' // 需要安裝less-loader 和 less
module: { // 這個節點,用於配置 所有 第三方模組 載入器 rules: [ // 所有第三方模組的 匹配規則 { test: /\.css/, use: ['style-loader', 'css-loader'] }, // 匹配處理 .css 檔案的第三方loader規則 { test: /\.less/, use: ['style-loader', 'css-loader','less-loader'] } // 匹配處理 .less 檔案的第三方loader規則 ] }
我們常常在css等檔案中使用url來引入其他依賴比如說圖片,字型等等,預設情況下webpack能不能處理呢?
又報了這個錯誤,說我們要處理圖片缺少一個loader,好,現在我們來裝這個loader:
cnpm i url-loader -D
module: { // 這個節點,用於配置 所有 第三方模組 載入器 rules: [ // 所有第三方模組的 匹配規則 // 匹配處理 .css 檔案的第三方loader規則 { test: /\.css/, use: ['style-loader', 'css-loader'] }, // 匹配處理 .less 檔案的第三方loader規則 { test: /\.less/, use: ['style-loader', 'css-loader', 'less-loader'] }, // node-sass是sass-loader的內部依賴,不需要寫在這裡面 { test: /\.scss/, use: ['style-loader', 'css-loader', 'sass-loader'] }, // 如果要加limit引數需要在安裝一個url-loader內部依賴包file-loader { test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader' } ] }
圖片竟然使用的是base64碼,對於使用base64碼的檔案一般都是小圖片,如果是大圖片,就會生成很長的base碼,所以對於大檔案我們不推薦使用base64碼,怎麼辦?要解決這個問題我們還要安裝一個包:
cnpm i file-loader -D
這個包是url-loader依賴的包。所以我們不必新增在use裡,對於圖片的loader只需要一個所以我們可以不使用陣列。
然後在webpack.config.js檔案中要給url-loader傳遞引數了?怎麼傳遞?url-loader提供了一種類似於瀏覽器地址傳參的方式傳遞引數,我們可以直接在其後追加?param1=val1¶m2=val2的方式加。
limit:限定檔案大小,如果檔案大小超過指定的size值,就不在轉換為base碼,小於或等於的話都會被轉換為base碼。
name:指定展示的檔名稱,可選值為:[hash:8或64]-[name].[ext],表示以8位或64位hash碼-原檔名稱.原始檔字尾名。注意:如果你選擇只使用原始檔.字尾名的形式可能會存在一些問題:原因是因為,使用url引入的檔案如果是以原檔案字尾名,webpack會將其像css或js檔案一樣存在記憶體裡管理,存在記憶體裡管理就有問題了,記憶體裡都是在相同路徑下的,我們在windows系統上的檔案是不允許重名的,但在記憶體中缺沒有這麼一說,如果我們有兩個命名相同的圖片,都使用名稱加字尾的形式存在記憶體中,那麼頁面中該引入哪個呢?所以,如果你想在頁面中使用原檔名,最好在前面加上hash,webpack在轉換的時候,會自動生成唯一的hash,這樣圖片檔案就不會重名,引用也就不會出現問題了。
由於字型檔案也是通過url引入的,所以對於字型檔案我們還要在webpack.config.js中做配置。
module: { // 這個節點,用於配置 所有 第三方模組 載入器 rules: [ // 所有第三方模組的 匹配規則 // 匹配處理 .css 檔案的第三方loader規則 { test: /\.css/, use: ['style-loader', 'css-loader'] }, // 匹配處理 .less 檔案的第三方loader規則 { test: /\.less/, use: ['style-loader', 'css-loader', 'less-loader'] }, // node-sass是sass-loader的內部依賴,不需要寫在這裡面 { test: /\.scss/, use: ['style-loader', 'css-loader', 'sass-loader'] }, // 如果要加limit引數需要在安裝一個url-loader內部依賴包file-loader { test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=6989&name=[hash:8]-[name].[ext]' }, // url-loader除了可以處理圖片外還可以處理字型檔案,因為字型檔案也是通過url()的方式引入樣式表的 { test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' }, ] }
在webpack中,預設只能處理 一部分 ES6 的新語法,一些跟高階的ES6語法或者ES7語法,webpack 是處理不了的;這時候,就需要藉助於第三方的loder,來幫助webpack處理這些高階的語法,當第三方loader把高階語法轉為低階語法之後,會把結果交給webpack去打包到bundle.js中。
如圖:在main.js中我們使用了ES6的高階語法,然後啟動服務報錯資訊告訴我們需要裝一些loader來處理。
// 使用babel要裝兩套包 // 1 cnpm i babel-core babel-loader babel-plugin-transform-runtime -D // 2 cnpm i babel-preset-env babel-preset-stage-0 -D
安裝過程中可能會有依賴上的錯誤,比如我裝的時候babel-loader@8需要依賴babel-core@7但是cnpm給我裝的是6,我只好根據提示降低babel-loader@7版本為7,這個根據錯誤提示修改即可。
開啟webpack的配置檔案,在module節點下rules陣列中,新增一個新的匹配規則:
// 配置babel來轉換高階語法 { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
注意:在配置babel的loader規則的時候,必須把 node_modules目錄,通過exclude選項排除掉,原因有倆:
1、如果不排除node_modules,則Babel會把 node_modules中所有的第三方JS檔案,都打包編譯,這樣,會非常銷燬CPU,同時,打包速度非常慢。
2、哪怕,最終,Babel把所有node_modules中的JS轉換完畢了,但是,專案也無法正常執行!
在專案的根目錄下,新建一個叫做 .babelrc 的Babel配置檔案,這個配置檔案,屬於JSON格式,所以,在寫 .babelrc 配置的時候,必須符合JSON語法規範:不能寫註釋,字串必須是雙引號。
{ "presets": [ "env", "stage-0" ], "plugins": [ "transform-runtime" ] }
presets:表示預設,這裡面配置我們安裝的外掛【帶有preset的】