中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Webpack是怎么工作的

發布時間:2022-10-14 09:38:56 來源:億速云 閱讀:129 作者:iii 欄目:web開發

這篇“Webpack是怎么工作的”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Webpack是怎么工作的”文章吧。

Webpack簡介

Webpack是一款模塊打包工具。它為不同的依賴創建模塊,將其整體打包成可管理的輸出文件。這一點對于單頁面應用(如今Web應用的事實標準)來說特別有用。

假設我們有一個可以執行兩個簡單數學任務(加法和乘法)的應用程序,為了方便維護,我們決定切分這些函數到不同的文件中去。

index.html

<html>
<head>
    <script src="src/sum.js"></script>
    <script src="src/multiply.js"></script>
    <script src="src/index.js"></script>
</head>
</html>

index.js

var totalMultiply = multiply(5, 3);
var totalSum = sum(5, 3);
console.log('Product of 5 and 3 = ' + totalMultiply);
console.log('Sum of 5 and 3 = ' + totalSum);

multiply.js

var multiply = function (a, b) {
    var total = 0;
    for (var i = 0; i < b; i++) {
        total = sum(a, total);
    }
    return total;
};

sum.js

var sum = function (a, b) {
    return a + b;
};

這個應用程序的輸出應該是:

Product of 5 and 3 = 15
Sum of 5 and 3 = 8

Webpack是如何幫助我們的?

我們不能僅僅只是使用工具,而不知道這些工具能幫助我們做什么。那么,Webpack幫我們做了什么呢?

用模塊來拯救依賴

在上面的代碼中,我們可以看到,multiply.jsindex.js均依賴于sum.js。因此,如果index.html導入依賴時使用了錯誤的順序,那么我們的應用就無法工作。舉個例子,如果index.js最先被導入,或者sum.jsmultiply.js之后被導入,都會得到錯誤。

基于上面的例子,讓我們想象一下,一個真實的Web應用往往可能會包含多達幾十個依賴項,這些依賴項之間還可能存在依賴關系,維護這些依賴項之間的順序想想就讓人窒息。這里還可能存在變量被其它依賴覆蓋的風險,而這將會導致難以發現的BUG。

為了解決這個痛點,Webpack會將依賴轉換為作用域更小的模塊,從而避免變量被覆蓋。依賴轉換為模塊帶來的額外好處是,Webpack可以為我們管理這些依賴。具體做法是,Webpack會在需要時,把依賴模塊引入進來,并匹配對應的作用域。

通過打包來減少HTTP請求次數

我們還是回看一下index.html,這個文件中我們需要下載三個獨立的文件。當然這里文件比較少還能夠應付,但還是之前提到的問題,真實的Web應用中,依賴項可能會很多,而這將會導致用戶不得不等待所有依賴項挨個下載完成后,主應用才能運行。

而這就引出了Webpack的另一特性——打包。Webpack可以將所有的依賴打包成一個文件,而這就意味著,用戶只需要下載一個依賴項,主應用就可以運行。

綜上所述,Webpack的主要特性就是打包模塊化。通過使用插件和加載器,我們可以擴展Webpack的這兩大特性。

讓依賴可用,并組合它們

我們將使用CommonJS模塊語法,作為初始設置。當然,這里也有諸如AMD,ES2015等其它選擇,但這里我們將先使用CommonJS,稍后遷移到ES2015。

CommonJS將模塊導出,使得其它代碼可以使用導出模塊中的函數或變量。我們可以通過require將導出模塊中的值讀出來。

index.html

<html>
<head>
    <script src="./dist/bundle.js""></script>
</head>
</html>

index.js

var multiply = require('./multiply');
var sum = require('./sum');
var totalMultiply = multiply(5, 3);
var totalSum = sum(5, 3);
console.log('Product of 5 and 3 = ' + totalMultiply);
console.log('Sum of 5 and 3 = ' + totalSum);

multiply.js

var sum = require('./sum');
var multiply = function (a, b) {
    var total = 0;
    for (var i = 0; i < b; i++) {
        total = sum(a, total);
    }
    return total;
};
module.exports = multiply;

sum.js

var sum = function (a, b) {
    return a + b;
};
module.exports = sum;

觀察上面的代碼,不難發現,為了讓函數sum與函數multiply能夠被使用,我們在sum.jsmultiply.js腳本中,導出了這兩個函數。這里有個細節,不知道大家是否注意到了,在index.html中我們現在僅需要導入一個bundle.js文件。

這可幫大忙了!我們現在不再需要關注依賴的順序,可以暴露我們想暴露的內容,并使得其它內容仍然保持私有。同時,我們現在僅需要導入一個文件,而不是三個,這有助于提高應用的加載速度。

Webpack的初始配置

為了實現我們上面要達到的效果,我們需要對Webpack做一些初始的配置。

var path = require('path');
module.exports = {
  entry: './src/index.js',    
  output: {
    path: path.resolve(__dirname, './dist/'),
    filename: 'bundle.js'
  }
}

這里我們實現了一個最簡單的配置。我們至少需要告訴Webpack,我們應用的入口在哪,輸出結果應該是什么。我們來詳細看看每個屬性所代表的含義。

  • entry: 這個屬性表示應用的入口。入口就意味著,這是我們加載程序和程序邏輯的起點。Webpack將從這個入口開始,遍歷整棵依賴樹。根據遍歷結果建立一個依賴間關系圖,并創建需要的模塊。

  • output.path: 這個屬性表示存放打包結果的絕對路徑。這里為了方便使用,我們采用了Node.js自帶的函數path,這個函數能夠根據我們程序所處的位置,動態的創建絕對路徑。其中,__dirname是Node.js的一個工具變量,它表示當前文件的目錄名。

  • output.filename: 這個屬性表示打包結果的文件名。它的名字可以是任意的,只不過我們習慣叫它bundle.js

來看看bundle.js

閱讀生成的bundle.js代碼,可以給我們帶來一些啟發。

// the webpack bootstrap
(function (modules) {
    // The module cache
    var installedModules = {};
    // The require function
    function __webpack_require__(moduleId) {
        // Check if module is in cache
        // Create a new module (and put it into the cache)
        // Execute the module function
        // Flag the module as loaded
        // Return the exports of the module
    }


    // expose the modules object (__webpack_modules__)
    // expose the module cache
    // Load entry module and return exports
    return __webpack_require__(0);
})
/************************************************************************/
([
    // index.js - our application logic
    /* 0 */
    function (module, exports, __webpack_require__) {
        var multiply = __webpack_require__(1);
        var sum = __webpack_require__(2);
        var totalMultiply = multiply(5, 3);
        var totalSum = sum(5, 3);
        console.log('Product of 5 and 3 = ' + totalMultiply);
        console.log('Sum of 5 and 3 = ' + totalSum);
    },
    // multiply.js
    /* 1 */
    function (module, exports, __webpack_require__) {
        var sum = __webpack_require__(2);
        var multiply = function (a, b) {
            var total = 0;
            for (var i = 0; i < b; i++) {
                total = sum(a, total);
            }
            return total;
        };
        module.exports = multiply;
    },
    // sum.js
    /* 2 */
    function (module, exports) {
        var sum = function (a, b) {
            return a + b;
        };
        module.exports = sum;
    }
]);

從上面的代碼可以看出,Webpack把每一個js腳本都封裝到一個模塊中,并把這些模塊放進數組中。模塊數組被傳入Webpack的引導程序中,引導程序會把這些模塊加入Webpack并執行,使得模塊可用。

這里bundle.js返回的是__webpack_require__(0),而這剛好對應了模塊數組中的index.js部分。基于此我們同樣可以得到正確的運行結果,而不需要處理依賴管理,下載依賴的次數也僅需要一次。

Loader讓Webpack更好用

Webpack僅能理解最基本的JavaScript ES5代碼,它自身僅支持創建模塊并打包JavaScript ES5。如果我們不僅僅局限于JavaScript ES5,例如我們想使用ES2015,這就需要告訴Webpack如何處理ES2015。這里我們的處理方式往往是,我們需要將其它語言(如TypeScript)或其它版本(如ES2015)預處理成JavaScript ES5,再讓Webpack做打包。這里就需要使用Babel來做轉換,把ES2015轉換為ES5(當然Babel能做的事情遠不止如此)。

為了說明這個過程,我們使用ES2015重寫之前的功能。

index.js

import multiply from './multiply';
import sum from './sum';
const totalMultiply = multiply(5, 3);
const totalSum = sum(5, 3);
console.log(`Product of 5 and 3 = ${totalMultiply}`);
console.log(`Sum of 5 and 3 = ${totalSum}`);

multiply.js

import sum from './sum';
const multiply = (a, b) => {
    let total = 0;
    for(let i=0;i<b;i++) {
        total = sum(a, total);
    }
    return total;
};
export default multiply;

sum.js

const sum = (a, b) => a + b;
export default sum;

這里我們使用了很多ES2015的新特性,例如箭頭函數、const關鍵字、模板字符串和ES2015的導入導出。ES2015的代碼Webpack無法處理,所以我們需要Babel來進行轉換。想要讓Babel配合Webpack完成工作,我們就需要用到Babel Loader。事實上,Loader就是Webpack處理JavaScript ES5以外內容的方式。有了加載器,我們就可以讓Webpack處理各式各樣的文件。

想要在Webpack中使用Babel Loader,我們還需要三個Babel依賴:

  • babel-loader: 提供Babel與Webpack之間的接口;

  • babel-core: 提供讀取和解析代碼的功能,并生成對應的輸出;

  • babel-preset-es2015: 提供將ES2015轉換為ES5的Babel規則;

在Webpack中配置Babel Loader的代碼,差不多是下面這樣子:

const path = require('path');
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './dist/'),
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            {
                test: /.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                query: {
                    presets: ['es2015']
                }
            }
        ]
    }
};

這段代碼你可以在webpack.config.js中找到。值得注意的是,Webpack中是支持同時存在多個Loader的,所以提供的值是一個數組。接著,還是讓我們來看看每個屬性代表的含義。

  • test: 我們只希望Loader處理JavaScript文件,這里通過一個正則表達式匹配.js文件;

  • loader: 要使用的Loader,這里使用了babel-loader

  • exclude: 哪些文件不需要被處理,這里我們不希望Babel處理node_modules下的任何文件;

  • query.presets: 我們需要使用哪個規則,這里我們使用Babel轉換ES2015的規則;

配置好這些內容后,再次查看打包生成的bundle.js,其中的內容看起來就像下面這樣:

/* 2 */
function(module, exports) {
  var sum = function sum(a, b) {
    return a + b;
    };
    
    module.exports = sum;
}

可以看到,Babel Loader已經把ES2015的代碼變成了ES5的代碼。

CSS與樣式,讓Webpack看起來更出色

接下來,讓我們拓展上面的例子,輸出計算結果。我們將在頁面上創建一個body,然后把乘積與加和的結果添加到span中。

import multiply from './multiply';
import sum from './sum';

const totalMultiply = multiply(5, 3);
const totalSum = sum(5, 3);

// create the body
const body = document.createElement("body");
document.documentElement.appendChild(body);

// calculate the product and add it to a span
const multiplyResultsSpan = document.createElement('span');
multiplyResultsSpan.appendChild(document.createTextNode(`Product of 5 and 3 = ${totalMultiply}`));

// calculate the sum and add it to a span
const sumResultSpan = document.createElement('span');
sumResultSpan.appendChild(document.createTextNode(`Sum of 5 and 3 = ${totalSum}`));

// add the results to the page
document.body.appendChild(multiplyResultsSpan);
document.body.appendChild(sumResultSpan);

這段代碼的輸出結果,應該與之前是一致的,區別僅在于顯示在頁面上。

Product of 5 and 3 = 15 Sum of 5 and 3 = 8

我們可以使用CSS來美化這個結果,比如,我們可以讓每個結果都獨占一行,并且給每個結果都加上邊框。為了實現這一點,我們可以使用如下的CSS代碼。

span {
    border: 5px solid brown;
    display: block;
}

我們需要將這個CSS也導入應用中。這里最簡單的解決方案是,在我們的html中添加一個link標簽。但有了Webpack提供的強大功能,我們可以先導入它,再用Webpack來處理這個樣式。

在代碼中導入CSS帶來的另一個好處是,開發者可以清晰的看到CSS與其使用之間的關聯。這里需要注意的是,CSS的作用域并不局限于它所導入的模塊本身,其作用域仍然是全局的,但從開發者的角度看,這樣使用更加清晰。

import multiply from './multiply';
import sum from './sum';

// import the CSS we want to use here
import './math_output.css';

const totalMultiply = multiply(5, 3);
const totalSum = sum(5, 3);

// create the body
const body = document.createElement("body");
document.documentElement.appendChild(body);

// calculate the product and add it to a span
const multiplyResultsSpan = document.createElement('span');
multiplyResultsSpan.appendChild(document.createTextNode(`Product of 5 and 3 = ${totalMultiply}`));

// calculate the sum and add it to a span
const sumResultSpan = document.createElement('span');
sumResultSpan.appendChild(document.createTextNode(`Sum of 5 and 3 = ${totalSum}`));

// add the results to the page
document.body.appendChild(multiplyResultsSpan);
document.body.appendChild(sumResultSpan);

這段代碼中,與前面代碼的唯一區別在于,我們導入了CSS。我們需要兩個Loader來處理我們的CSS:

  • css-loader: 用于處理CSS導入,具體來說,獲取導入的CSS并加載CSS文件內容;

  • style-loader: 獲取CSS數據,并添加它們到HTML文檔中;

現在我們在webpack.config.js中的Webpack配置看起來像這樣:

const path = require('path');
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './dist/'),
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            {
                test: /.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                query: {
                    presets: ['es2015']
                }
            },
            {
                test: /.css$/,
                loaders: ['style-loader', 'css-loader']
            }
        ]
    }
};

我們還是來看看新增的CSS配置屬性所表示的內容。

  • test: 我們需要告訴Loader,我們只需要它處理CSS文件。這里的正則表達式僅匹配CSS文件。

  • loaders: 這里與前面不同的是,我們使用了多個Loader。還有一個需要注意的細節是,Webpack從右向左處理loader,因此css-loader處理的結果(讀出CSS文件內容)會被傳遞給style-loader,最終得到的是style-loader的處理結果(將樣式添加到HTML文檔中)。

假如我們現在需要提取CSS,并輸出到一個文件中,再導入該文件。為了實現這一點,我們就要用到Plugin。Loader的作用在于,在數據被打包輸出前進行預處理。而Plugin則可以阻止預處理的內容直接出現在我們的打包結果中。

我們的Webpack配置現在變成了這樣:

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './dist/'),
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            {
                test: /.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                query: {
                    presets: ['es2015']
                }
            },
            {
                test: /.css$/,
                loader: ExtractTextPlugin.extract('css-loader')
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin('style.css')
    ]
};

在這段代碼的頂部,我們導入了ExtractTextPlugin,并使用這個插件改造了之前的CSS Loader。這里的作用是,css-loader的處理結果不再直接返回給Webpack,而是傳遞給ExtractTextPlugin。在底部我們配置了這個Plugin。

這里配置的作用是,對于傳遞給ExtractTextPlugin的CSS樣式數據,將會被保存在名為style.css的文件中。這樣做的好處與之前處理JavaScript時一樣,我們可以將多個獨立的CSS文件合并為一個文件,從而減少加載樣式時的下載次數。

最終我們可以直接使用我們合并好的CSS,實現和之前一致的效果。

<html>
  <head>
    <link rel="stylesheet" href="dist/style.css"/>
    <script src="./dist/bundle.js""></script>
  </head>
</html>

使用Webpack處理圖片

現在我們開始嘗試向應用中添加圖片,并讓Webpack來協助我們處理這些圖片。這里我們添加了兩張圖片,一個用于求和,一個用于求積。為了讓Webpack有能力處理這些圖片,我們使用這兩個Loader:

  • image-webpack-loader: 嘗試幫助我們自動壓縮圖片體積;

  • url-loader: 如果image-webpack-loader的輸出圖片體積小,就內聯使用這些圖片,如果image-webpack-loader的輸出圖片體積大,就將圖像包含在輸出目錄中;

我們準備了兩張圖片,用于求積的圖片(multiply.png)相對較大,用于求和的圖片(sum.png)相對較小。首先,我們添加一個圖片工具方法,這個方法會為我們創建圖片,并將圖片添加到文檔中。

image_util.js

const addImageToPage = (imageSrc) => {
    const image = document.createElement('img');
    image.src = imageSrc;
    image.style.height = '100px';
    image.style.width = '100px';
    document.body.appendChild(image);
};
export default addImageToPage;

接著,讓我們導入這個圖片工具方法,以及我們想要添加到index.js中的圖片。

import multiply from './multiply';
import sum from './sum';

// import our image utility
import addImageToPage from './image_util';

// import the images we want to use
import multiplyImg from '../images/multiply.png';
import sumImg from '../images/sum.png';

// import the CSS we want to use here
import './math_output.css';

const totalMultiply = multiply(5, 3);
const totalSum = sum(5, 3);

// create the body
const body = document.createElement("body");
document.documentElement.appendChild(body);

// calculate the product and add it to a span
const multiplyResultsSpan = document.createElement('span');
multiplyResultsSpan.appendChild(document.createTextNode(`Product of 5 and 3 = ${totalMultiply}`));

// calculate the sum and add it to a span
const sumResultSpan = document.createElement('span');
sumResultSpan.appendChild(document.createTextNode(`Sum of 5 and 3 = ${totalSum}`));

// add the results to the page
addImageToPage(multiplyImg);
document.body.appendChild(multiplyResultsSpan);
addImageToPage(sumImg);
document.body.appendChild(sumResultSpan);

最后,我們還是來修改webpack.config.js,配置兩個新的Loader來處理這些圖片。

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, './dist/'),
        filename: 'bundle.js',
        publicPath: 'dist/'
    },
    module: {
        loaders: [
            {
                test: /.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                query: {
                    presets: ['es2015']
                }
            },
            {
                test: /.css$/,
                loader: ExtractTextPlugin.extract('css-loader')
            },
            {
                test: /.png$/,
                loaders: [
                    'url-loader?limit=5000',
                    'image-webpack-loader'
                ]
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin('style.css')
    ]
};

還是老規矩,我們來看看新增的參數都表示什么含義。

  • output.publicPath: 讓url-loader知道,保存到磁盤的文件需要添加指定的前綴。例如,我們需要保存一個output_file.png,那么最終保存的路徑應該是dist/output_file.png

  • test: 還是和之前一樣,通過正則表達式匹配圖像文件,非圖像文件不處理;

  • loaders: 這里還是要再強調一下,Webpack從右向左處理loader,因此image-webpack-loader的處理結果將會被傳遞給url-loader繼續處理。

現在我們執行Webpack打包,會得到下面三個東西。

38ba485a2e2306d9ad96d479e36d2e7b.png
bundle.js
style.css

這里的38ba485a2e2306d9ad96d479e36d2e7b.png實際上就是我們的大圖片multiply.png,較小的圖片sum.png會被內聯到bundle.js中,就像下面這樣。

module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAMAAAACDyzWAAAC6FBMVEUAuv8AgL...."

這其實相當于

img.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAMAAAACDyzWAAAC6FBMVEUAuv8AgL..."

以上就是關于“Webpack是怎么工作的”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

辽源市| 乐亭县| 固安县| 吉安县| 广宁县| 汉沽区| 冕宁县| 尉氏县| 堆龙德庆县| 浦江县| 德庆县| 安西县| 霍林郭勒市| 乡城县| 临沧市| 丹凤县| 察雅县| 昂仁县| 梁平县| 新化县| 禹州市| 长兴县| 邢台县| 礼泉县| 广宁县| 法库县| 都匀市| 灵璧县| 吴堡县| 南城县| 临潭县| 张家港市| 鄂尔多斯市| 杂多县| 扶绥县| 杭锦旗| 昭苏县| 介休市| 永兴县| 南部县| 华蓥市|