Webpack系統學習

1-1如何在瀏覽器端實現模塊化

簡介
需要的前置知識:ES6、模塊化、包管理器、git
合適的深度:webpack使用層面很簡單,但原理層面非常複雜
合適的廣度:webpack生態圈極其繁榮,有海量的第三方庫可以融入到webpack
瀏覽器端的模塊化

問題:
1.效率問題:精細的模塊劃分帶來了更多的JS文件,更多的JS文件帶來了更多的請求,降低了頁面訪問效率
2.兼容性問題:瀏覽器目前僅支持ES6的模塊化標準,並且還存在兼容性問題
3.工具問題:瀏覽器不支持npm下載的第三方包
這些僅僅是前端工程化的一個縮影

△當開發一個具有規模的程序,你將遇到非常多的非業務問題,這些問題包括:執行效率、兼容性、代碼的可維護性可擴展性、團隊協作、測試等等等等,我們將這些問題稱之爲工程問題。工程問題與業務無關,但它深刻的影響到開發進度,如果沒有一個好的工具解決這些問題,將使得開發進度變得極其緩慢,同時也讓開發者陷入技術的泥潭。


根本原因
思考:上面提到的問題,爲什麼在node端沒有那麼明顯,反而到了瀏覽器端變得如此嚴重呢?

:在node端,運行的JS文件在本地,因此可以本地讀取文件,它的效率比瀏覽器遠程傳輸文件高的多

根本原因:在瀏覽器端,開發時態(devtime)和運行時態(runtime)的側重點不一樣

開發時態,devtime

1.模塊劃分越細越好
2.支持多種模塊化標準
3.支持npm或其他包管理器下載的模塊
4.能夠解決其他工程化的問題
5.運行時態,runtime:

運行時態,runtime

1.文件越少越好
2.文件體積越小越好
3.代碼內容越亂越好
4.所有瀏覽器都要兼容
5.能夠解決其他運行時的問題,主要是執行效率問題

這種差異在小項目中表現的並不明顯,可是一旦項目形成規模,就越來越明顯,如果不解決這些問題,前端項目形成規模只能是空談

解決辦法
既然開發時態和運行時態面臨的局面有巨大的差異,因此,我們需要有一個工具,這個工具能夠讓開發者專心的在開發時態寫代碼,然後利用這個工具將開發時態編寫的代碼轉換爲運行時態需要的東西。

這樣的工具,叫做構建工具

這樣一來,開發者就可以專注於開發時態的代碼結構,而不用擔心運行時態遇到的問題了。

常見的構建工具
webpack
grunt
gulp
browserify
fis
其他

1-2webpack的安裝和使用

webpack官網:https://www.webpackjs.com/ 目前的最新版本:webpack4


webpack簡介
webpack是基於模塊化的打包(構建)工具,它把一切視爲模塊

它通過一個開發時態的入口模塊爲起點,分析出所有的依賴關係,然後經過一系列的過程(壓縮、合併),最終生成運行時態的文件。

webpack的特點

1.爲前端工程化而生:webpack致力於解決前端工程化,特別是瀏覽器端工程化中遇到的問題,讓開發者集中注意力編寫業務代碼,而把工程化過程中的問題全部交給webpack來處理
2.簡單易用:支持零配置,可以不用寫任何一行額外的代碼就使用webpack
3.強大的生態:webpack是非常靈活、可以擴展的,webpack本身的功能並不多,但它提供了一些可以擴展其功能的機制,使得一些第三方庫可以融於到webpack中
基於nodejs:由於webpack在構建的過程中需要讀取文件,因此它是運行在node環境中的
4.基於模塊化:webpack在構建過程中要分析依賴關係,方式是通過模塊化導入語句進行分析的,它支持各種模塊化標準,包括但不限於CommonJS、ES6 Module

webpack的安裝

webpack通過npm安裝,它提供了兩個包:
webpack:核心包,包含了webpack構建過程中要用到的所有api
webpack-cli:提供一個簡單的cli命令,它調用了webpack核心包的api,來完成構建過程

安裝方式:
在這裏插入圖片描述
全局安裝:可以全局使用webpack命令,但是無法爲不同項目對應不同的webpack版本
本地安裝:推薦,每個項目都使用自己的webpack版本進行構建

使用webpack
在這裏插入圖片描述

默認情況下,webpack會以./src/index.js作爲入口文件分析依賴關係,打包到./dist/main.js文件中通過–mode選項可以控制webpack的打包結果的運行環境

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build":"webpack --mode=production",
    "dev":"webpack --mode=development"
  },

在這裏插入圖片描述
在這裏插入圖片描述

1-3模塊化兼容性

由於webpack同時支持CommonJS和ES6 module,因此需要理解它們互操作時webpack是如何處理的

同模塊化標準
如果導出和導入使用的是同一種模塊化標準,打包後的效果和之前學習的模塊化沒有任何差異
在這裏插入圖片描述
在這裏插入圖片描述
不同模塊化標準

不同的模塊化標準,webpack按照如下的方式處理

在這裏插入圖片描述
在這裏插入圖片描述

最佳實踐

代碼編寫最忌諱的是精神分裂,選擇一個合適的模塊化標準,然後貫徹整個開發階段。

1-6配置文件

webpack提供的cli支持很多的參數,例如–mode,但更多的時候,我們會使用更加靈活的配置文件來控制webpack的行爲

默認情況下,webpack會讀取webpack.config.js文件作爲配置文件,但也可以通過CLI參數–config來指定某個配置文件

配置文件中通過CommonJS模塊導出一個對象,對象中的各種屬性對應不同的webpack配置


注意:配置文件中的代碼,必須是有效的node代碼(所以只能用commonJS不能用ES6),配置文件中的代碼是要經過編譯運行的在node環境中。

當命令行參數與配置文件中的配置出現衝突時,以命令行參數爲準。

基本配置:

1.mode:編譯模式,字符串,取值爲development或production,指定編譯結果代2.碼運行的環境,會影響webpack對編譯結果代碼格式的處理
2.entry:入口,字符串(後續會詳細講解),指定入口文件
3.output:出口,對象(後續會詳細講解),指定編譯結果文件

webpack.config.js

var path = require('path')
module.exports = {
    mode:'development',
    module:{//模塊的匹配規則
        rules:[
            {
                test:/index\.js$/ ,//正則表達式,匹配模塊路徑
                use:[//匹配到了以後使用哪些加載器;
                {
                    loader:"./loaders/test-loader"
                }//每個加載器的使用是一個對象

                 ] },
            // 規則1
            // 規則2
        ],//模塊的匹配規則

    },
    entry:{
        main:"./src/index.js",//屬性名:chunk名稱,屬性值:入口模塊(啓動模塊)
        a:"./src/a.js"    
    },
    output:{
        path:path.resolve(__dirname,'target'),//必須要配置一個絕對路徑
        filename:"[name].js"//配合的合併的js文件規則

    },
    devtool:"source-map"
}

1-7devtool 配置

source map 源碼地圖
本小節的知識與 webpack 無關

前端發展到現階段,很多時候都不會直接運行源代碼,可能需要對源代碼進行合併、壓縮、轉換等操作,真正運行的是轉換後的代碼

這就給調試帶來了困難,因爲當運行發生錯誤的時候,我們更加希望能看到源代碼中的錯誤,而不是轉換後代碼的錯誤

jquery壓縮後的代碼:https://code.jquery.com/jquery-3.4.1.min.js

爲了解決這一問題,chrome瀏覽器率先支持了source map,其他瀏覽器紛紛效仿,目前,幾乎所有新版瀏覽器都支持了source map

source map實際上是一個配置,配置中不僅記錄了所有源碼內容,還記錄了和轉換後的代碼的對應關係

下面是瀏覽器處理source map的原理

最佳實踐:

source map 應在開發環境中使用,作爲一種調試手段
source map 不應該在生產環境中使用,source map的文件一般較大,不僅會導致額外的網絡傳輸,還容易暴露原始代碼。即便要在生產環境中使用source map,用於調試真實的代碼運行問題,也要做出一些處理規避網絡傳輸和代碼暴露的問題。
webpack中的source map
使用 webpack 編譯後的代碼難以調試,可以通過 devtool 配置來優化調試體驗

具體的配置見文檔:https://www.webpackjs.com/configuration/devtool/

1-8webpack 編譯過程

webpack 的作用是將源代碼編譯(構建、打包)成最終代碼

在這裏插入圖片描述

整個過程大致分爲三個步驟

初始化
編譯
輸出


初始化
此階段,webpack會將CLI參數、配置文件、默認配置進行融合,形成一個最終的配置對象。

對配置的處理過程是依託一個第三方庫yargs完成的

此階段相對比較簡單,主要是爲接下來的編譯階段做必要的準備

目前,可以簡單的理解爲,初始化階段主要用於產生一個最終的配置

編譯
1.創建chunk
chunk是webpack在內部構建過程中的一個概念,譯爲塊,它表示通過某個入口找到的所有依賴的統稱。

根據入口模塊(默認爲./src/index.js)創建一個chunk
在這裏插入圖片描述

每個chunk都有至少兩個屬性:

1)name:默認爲main
1)id:唯一編號,開發環境和name相同,生產環境是一個數字,從0開始

2.構建所有依賴模塊

在這裏插入圖片描述

AST在線測試工具:https://astexplorer.net/

3.產生chunk assets
在第二步完成後,chunk中會產生一個模塊列表,列表中包含了模塊id和模塊轉換後的代碼

接下來,webpack會根據配置爲chunk生成一個資源列表,即chunk assets,資源列表可以理解爲是生成到最終文件的文件名和文件內容
在這裏插入圖片描述

chunk hash是根據所有chunk assets的內容生成的一個hash字符串 hash:一種算法,具體有很多分類,特點是將一個任意長度的字符串轉換爲一個固定長度的字符串,而且可以保證原始內容不變,產生的hash字符串就不變

合併chunk assets
將多個chunk的assets合併到一起,併產生一個總的hash

輸出
此步驟非常簡單,webpack將利用node中的fs模塊(文件處理模塊),根據編譯產生的總的assets,生成相應的文件。

總過程
在這裏插入圖片描述

在這裏插入圖片描述

涉及術語

1.module:模塊,分割的代碼單元,webpack中的模塊可以是任何內容的文件,不僅限於JS
2.chunk:webpack內部構建模塊的塊,一個chunk中包含多個模塊,這些模塊是從入口模塊通過依賴分析得來的
3.bundle:chunk構建好模塊後會生成chunk的資源清單,清單中的每一項就是一個4.bundle,可以認爲bundle就是最終生成的文件
5.hash:最終的資源清單所有內容聯合生成的hash值
6.chunkhash:chunk生成的資源清單內容聯合生成的hash值
7.chunkname:chunk的名稱,如果沒有配置則使用main
8.id:通常指chunk的唯一編號,如果在開發環境下構建,和chunkname相同;如果是生產環境下構建,則使用一個從0開始的數字進行編號

1-9入口和出口

node內置模塊 - path: https://nodejs.org/dist/latest-v12.x/docs/api/path.html

出口

這裏的出口是針對資源列表的文件名或路徑的配置

出口通過output進行配置

入口

入口真正配置的是chunk

入口通過entry進行配置

規則:

name:chunkname
hash: 總的資源hash,通常用於解決緩存問題
chunkhash: 使用chunkhash
id: 使用chunkid,不推薦

1-10入口和出口的最佳實踐

具體情況具體分析

下面是一些經典場景

一個頁面一個JS

源碼結構

|—— src
    |—— pageA   頁面A的代碼目錄
        |—— index.js 頁面A的啓動模塊
        |—— ...
    |—— pageB   頁面B的代碼目錄
        |—— index.js 頁面B的啓動模塊
        |—— ...
    |—— pageC   頁面C的代碼目錄
        |—— main1.js 頁面C的啓動模塊1 例如:主功能
        |—— main2.js 頁面C的啓動模塊2 例如:實現訪問統計的額外功能
        |—— ...
    |—— common  公共代碼目錄
        |—— ...

webpack配置

module.exports = {
    entry:{
        pageA: "./src/pageA/index.js",
        pageB: "./src/pageB/index.js",
        pageC: ["./src/pageC/main1.js", "./src/pageC/main2.js"]
    },
    output:{
        filename:"[name].[chunkhash:5].js"
    }

}
這種方式適用於頁面之間的功能差異巨大、公共代碼較少的情況,這種情況下打包出來的最終代碼不會有太多重複

一個頁面多個JS

源碼結構

|—— src
    |—— pageA   頁面A的代碼目錄
        |—— index.js 頁面A的啓動模塊
        |—— ...
    |—— pageB   頁面B的代碼目錄
        |—— index.js 頁面B的啓動模塊
        |—— ...
    |—— statistics   用於統計訪問人數功能目錄
        |—— index.js 啓動模塊
        |—— ...
    |—— common  公共代碼目錄
        |—— ...
webpack配置

module.exports = {
    entry:{
        pageA: "./src/pageA/index.js",
        pageB: "./src/pageB/index.js",
        statistics: "./src/statistics/index.js"
    },
    output:{
        filename:"[name].[chunkhash:5].js"
    }
}
這種方式適用於頁面之間有一些獨立、相同的功能,專門使用一個chunk抽離這部分JS有利於瀏覽器更好的緩存這部分內容。

思考:爲什麼不使用多啓動模塊的方式?

單頁應用
所謂單頁應用,是指整個網站(或網站的某一個功能塊)只有一個頁面,頁面中的內容全部靠JS創建和控制。 vue和react都是實現單頁應用的利器。



源碼結構

|—— src
    |—— subFunc   子功能目錄
        |—— ...
    |—— subFunc   子功能目錄
        |—— ...
    |—— common  公共代碼目錄
        |—— ...
    |—— index.js
webpack配置

module.exports = {
    entry: "./src/index.js",
    output:{
        filename:"index.[hash:5].js"
    }
}

1-11 loader

webpack做的事情,僅僅是分析出各種模塊的依賴關係,然後形成資源列表,最終打包生成到指定的文件中。 更多的功能需要藉助webpack loaders和webpack plugins完成。

webpack loader: loader本質上是一個函數,它的作用是將某個源碼字符串轉換成另一個源碼字符串返回。

loader函數的將在模塊解析的過程中被調用,以得到最終的源碼。

全流程:

chunk中解析模塊的流程:

chunk中解析模塊的更詳細流程:

處理loaders流程:

loader配置:

完整配置

module.exports = {
    module: { //針對模塊的配置,目前版本只有兩個配置,rules、noParse
        rules: [ //模塊匹配規則,可以存在多個規則
            { //每個規則是一個對象
                test: /\.js$/, //匹配的模塊正則
                use: [ //匹配到後應用的規則模塊
                    {  //其中一個規則
                        loader: "模塊路徑", //loader模塊的路徑,該字符串會被放置到require中
                        options: { //向對應loader傳遞的額外參數

                        }
                    }
                ]
            }
        ]
    }
}

簡化配置

module.exports = {
    module: { //針對模塊的配置,目前版本只有兩個配置,rules、noParse
        rules: [ //模塊匹配規則,可以存在多個規則
            { //每個規則是一個對象
                test: /\.js$/, //匹配的模塊正則
                use: ["模塊路徑1", "模塊路徑2"]//loader模塊的路徑,該字符串會被放置到require中
            }
        ]
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章