🌟給webpack一個認識吧!

1. webpack是什麼

webpack是一個現代Javascript應用程序的靜態模塊打包器,指的是webpack在不進行特殊配置時,就只能處理JavaScript這一種語言,打包指的就是多個js文件不須要人爲的去理清其的依賴關係,並且還能將其合成爲一個文件。css

爲何要打包呢?由於前端項目中的邏輯多了,文件多了,複雜度提升了(好比一個項目依賴多個模塊),因而提出多種模塊化的標準,webpack就是這麼一種優秀的模塊化方案。
webpack不只強大,並且靈活(每一個功能可插拔)。html

webpack除了打包還能幹什麼?前端

  • 翻譯!加入loader,把代碼翻譯成瀏覽器看的懂的代碼。
  • 作點別的操做,能力擴展類,在webpack的plugin裏。

下面會解釋loader和pluginnode

2. webpack中的關鍵人物

  • loader:指文件加載器,執行一個文件的編譯轉譯功能,例若有,es6轉es5,就有babel-loader,文件中引入css文件就須要css-loaderstyle-loader
  • plugin:用於加強一個項目裏面webpack的能力,其機制就是強調一個事件監聽的能力,在項目裏監聽一些事件,而且改變一些文件打包輸出的結果。好比使用一個叫UglifyJSPlugin的插件來對js文件進行壓縮,從而減少js文件的大小,加速load速度。

Loader負責文件轉換,那麼Plugin即是負責功能擴展。Loader和Plugin做爲Webpack的兩個重要組成部分,承擔着兩部分不一樣的職責。react

3. 前端模塊化

什麼是模塊呢? 舉個例子:一個公司須要正常運轉,就有市場部,技術部,人事部等等,這每一個部門就至關於一個模塊,在前端項目中也就有好比專門網絡請求的模塊,錯誤處理的模塊,專門渲染的模塊。webpack

瞭解模塊化以前瞭解下做用域的概念先。es6

傳統作法會引入多個js腳本,它們共處於全局做用域下,就容易致使全局做用域變量衝突(例如同名變量衝突),而發生一些不可預測的事情。 例如:web

/*moduleA.js裏*/
var a=10;

/*moduleB.js裏*/
var a=11;

/*index.html裏*/
<body>
    <script src="./moduleA.js"></script>
    <script src="./moduleB.js"></script>
    <script src="./moduleC.js"></script>
</body>
複製代碼

當出現上面得衝突後,a的值還能肯定嗎?——不能!面試

而後就有人想出,每一個js腳本里都使用一個對象包裹,造成一個局部做用域。api

// 定義模塊內的局部做用域,以moduleA爲例
    var Susan = {
    	name: "susan",
        sex: "female",
        tell: function(){
        	console.log("im susan")
        }
    }
複製代碼

可是這樣又有各嚴重的問題,就是對象裏值咱們能更改,沒法保證模塊屬性內部安全性,對於好比說用戶名密碼等數據的情景就很嚴重了。

因而又改進到了當即執行函數和閉包的形式。

// 定義模塊內的閉包做用域(模塊做用域),以moduleA爲例
    var SusanModule = (function(){
    	var name = "susan"
        var sex = "female"
        functioon tell(){//這樣就不能更改其中的數據了
        	console.log("I'm ", this.name)
        }
    })()
複製代碼

咱們再改進下寫法,爲當即執行函數寫入參數爲window

// 定義模塊內的閉包做用域(模塊做用域),以moduleA爲例
    (function(window){
    	var name = "susan"
        var sex = "female"
        functioon tell(){
        	console.log("I'm ", this.name)
        }
        window.susanModule = {tell}
    })(window)// window做爲參數傳給
//////////////////////
//測試
window.susanModule.tell(); //im susan
複製代碼

這樣大概就是早期的模塊化的形式了。

如今的模塊化方案有

  • AMD(Asynchronous Module Definition 異步模塊定義)
//大概形式以下
//定義
define("mymodule", ["dep1", "dep2"], function(d1, d2) {
});

// 加載
require(["module", "../file"], function(module, file) {
});
複製代碼
  • CommonJs:Node.js 專用, 該方案的核心思想就是容許模塊經過require方案同步加載依賴的其餘模塊,經過exports或module.exports來暴露出須要的接口。
// 經過require函數來引用
const math = require("./math");

// 經過exports將其導出
exports.getSum = function(a,b){
    return a + b;
}
複製代碼
  • ES6 Module:該方案最大的特色就是靜態化,靜態化的優點在於能夠在編譯的時候肯定模塊的依賴關係以及輸入輸出的變量。上面提到的CommonJs和AMD都只能在運行時肯定這些東西。
// 經過import函數來引用
import math from "./math";

// 經過export將其導出
export function sum(a, b){
    return a + b;
}
複製代碼

此外說說ES6模塊化和CommonJs的模塊化的區別:

  • CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。

    注意:CommonJS 模塊輸出的是值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值

    ES6 模塊是動態引用,而且不會緩存值,模塊裏面的變量綁定其所在的模塊。

  • CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。

    緣由:CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完纔會生成。而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。

  • CommonJS 模塊的require()是同步加載模塊,ES6 模塊的import命令是異步加載,有一個獨立的模塊依賴的解析階段。

前端模塊化主要解決了兩個問題:「命名空間衝突」,「文件依賴管理」

和介紹webpack又有什麼關係呢?
在webpack中,一切皆模塊。咱們在模塊化開發的時候,一般會使用ES Module或者CommonJS規範導出或引入依賴模塊,webpack打包編譯的時候,會統一替換成本身的__webpack_require__來實現模塊的引入和導出,從而實現模塊緩存機制,以及抹平不一樣模塊規範之間的一些差別性。

4. webpack打包的核心思路

  • 初始化:啓動構建,讀取與合併配置參數,加載 Plugin,實例化 Compiler
  • 編譯:從 Entry入口文件出發,針對每一個 Module 串行調用對應的 Loader 去翻譯文件的內容,再找到該 Module 依賴的 Module,遞歸地進行編譯處理
  • 輸出:將編譯後的 Module 組合成 Chunk,將 Chunk 轉換成文件,輸出到文件系統中

5. 一個簡單的webpack配置示例

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: path.resolve(__dirname, '../src/index.js'), //指定入口文件,程序從這裏開始編譯,__dirname當前所在目錄, ../表示上一級目錄, ./同級目錄
    output: {
        path: path.resolve(__dirname, '../dist'), // 輸出的路徑
        filename: 'app/[name]_[hash:8].js'  // 打包後文件
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['es2015', 'react'],
                    }
                },
                exclude: /node_modules/
            }
        ]
    },
   plugins: [//plugin下的插件
     new HtmlWebpackPlugin({
       template: path.resolve(__dirname, '../src/index.template.html'),
       inject: true
     })
   ]
複製代碼

loader下

  • babel-loader: babel加載器

  • babel-preset-es2015: 支持es2015

  • babel-preset-react: jsx 轉換成js

loader是支持以數組的形式配置多個的,當Webpack在轉換該文件類型的時候,會按順序鏈式調用每個loader,前一個loader返回的內容會做爲下一個loader的入參。

plugin下

html-webapck-plugin插件的兩個主要做用:

  • html文件中引入的外部資源如scriptlink動態添加每次compile後的hash,防止引用緩存的外部文件問題

  • 能夠生成建立html入口文件,好比單頁面能夠生成一個html文件入口,配置N個html-webpack-plugin能夠生成N個頁面入口

webpack基於發佈訂閱模式,在運行的生命週期中會廣播出許多事件,plugin插件經過監聽這些事件,就能夠在特定的階段執行本身的插件任務,從而實現本身想要的功能。

一些常見的loader和plugin能夠看看這裏,最好對這些有個大概瞭解和記憶。。

6.總結

到這裏就爲理解webpack下一個簡單定義:webpack做用於模塊打包,將不一樣模塊的文件打包整合在一塊兒,而且保證它們之間的引用正確,執行有序。而且經過其的loader機制,讓webpack可以去處理其餘類型的文件,並將它們轉換爲有效模塊,以供應用程序使用,以及被添加到依賴圖中。且再經過webpack的Plugin機制,咱們還能夠進一步實現諸如按需加載,代碼壓縮等一系列擴展功能。

參考文章:

當面試官問Webpack的時候他想知道什麼

再來一打webpack面試考題

相關文章
相關標籤/搜索