concepts

webpack是JS應用程序的靜態模塊打包工具。webpack在處理你的應用時,會遞歸的構建依賴項,這些依賴項包括你的應用程序所須要的全部模塊,而後把這些模塊打包到一個或多個bundles中。javascript

1、Entrycss

entry point是項目的入口文件,告訴webpack從哪一個模塊開始構建內部依賴。進入入口文件後,webpack會直接或間接的找到其依賴的模塊和庫。html

一、定義入口的方式有多種:java

1)簡寫形式(string | array<string>)node

module.exports = {
  entry: './path/to/my/entry/file.js'
}

module.exports = {
  entry: {
    main: './path/to/my/entry/file.js'
  }
}

上面的寫法其實是下面寫法的簡寫形式webpack

multi-main entry: entry是一個存放文件路徑的數組,當你須要將多個依賴文件一塊兒注入並將它們的依賴關係打包成一個chunk時,可使用這種方式。git

2)對象形式({ [entryChunkName]: string|array<string> })github

const config = {
  entry: {
    app: './src/app.js',
    vendors: './src/vendors.js'
  }
}

 

二、應用場景web

1)分離app和vendor入口json

const config = {
  entry: {
    app: './src/app.js',
    vendors: './src/vendors.js'
  }
}

 這種形式告訴webpack從app.js和vendors.js開始建立分離的相互獨立的依賴。這在單頁面應用中很經常使用。這種設置容許你利用CommonsChunkPlugin將應用中任何vendor的引用抽離到vendor bundle. 這樣在你的應用程序bundle中就沒有vendor代碼。

2)多頁面應用

const config = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }
};

 這種設置方式告訴webpack咱們有3個分離的依賴項。在多頁面應用中,服務器須要獲取新的HTML文檔,頁面從新加載新的文檔和文件須要從新下載,這樣咱們就可使用CommonsChunkPlugin將頁面間共享的代碼提取打包。

黃金條例:每一個HTML文檔使用一個入口。

 

2、Output

output屬性告訴webpack打包文件的輸出位置和名字。 output是一個對象,通常須要文件名(filename)和輸出路徑(path);

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

一、多入口文件

當你的設置會有多個chunk(好比多個入口文件或者使用相似CommonsChunkPlugin)時使用,但你須要保證每一個文件都有惟一的名字。

{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist'
  }
}

 

二、publicPath

當你將應用所需文件存放在CDN或者hash時

output: {
  path: "/home/proj/cdn/assets/[hash]",
  publicPath: "http://cdn.example.com/assets/[hash]/"
}

 若是編譯時不肯定最終的publicPath,這個值能夠設爲空白或者不設置,而後在入口文件中使用__webpack_public_path__動態設置。

__webpack_public_path__ = myRuntimePublicPath

// rest of your application entry

 

3、Loaders

loaders能夠幫助webpack處理更多類型的文件,不只限於javascript(webpack自身只能處理JS)。loaders會將各類類型的文件轉化爲webpack能處理的合法模塊。

一、使用方式

1) 配置文件

const path = require('path');

module.exports = {
  entry: './path/file.js',
  output:{
    path: path.resolve(__dirname,'dist'),
    filename:'bundle.js',
  },
  modules:{
    rules: [
      {test: /\.txt$/, use: 'raw-loader'},
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
     ] 
   } 
}

 

2)inline

import Styles from 'style-loader!css-loader?modules!./styles.css';

 options能夠經過query參數形式傳遞,好比?key=value&foo=bar,或者JSON對象形式,好比?{"key":"value","foo":"bar"} 

 

3) CLI

webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'

 

4、Plugins

plugins能夠作更多的事情,能夠優化和更小化打包文件,或者定義環境變量。

plugin是一個JS對象,有apply屬性。apply屬性會被webpack的編譯器調用,容許進入整個編譯週期。

function ConsoleLogOnBuildWebpackPlugin() {

};

ConsoleLogOnBuildWebpackPlugin.prototype.apply = function(compiler) {
  compiler.plugin('run', function(compiler, callback) {
    console.log("The webpack build process is starting!!!");

    callback();
  });
};
 plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]

 

5、Module Resolution

resolver幫助webpack定位模塊位置。依賴模塊可能來自應用程序中的代碼或者第三方庫,resolver能夠幫助webpack找到模塊代碼,這些模塊代碼經過require/import引入,並被打包到bundle中。

一、resolving rules

1)絕對路徑

import "/home/me/file";

import "C:\\Users\\me\\file";

 由於已經包含了文件絕對路徑,因此不須要其餘的resolution。

2)相對路徑

import "../src/file1";
import "./file2";

 在這種狀況下,源文件所在文件夾將做爲context directory,而後將這些相對路徑加入到context路徑中生成模塊的絕對路徑。

3)Module path

import "module";
import "module/lib/file";

 resolve.modules下的全部文件夾都會被搜索。你可使用resolve.alias建立原始Module 路徑的別名。

一旦經過上面的規則解析出了路徑,resolve會檢查該路徑指向的是一個文件仍是一個文件夾。

若是是文件:

 該路徑帶有文件擴展名,那麼文件直接被打包;不然,使用resolve.extensions解析出文件擴展名。

若是指向的是文件夾:

 文件夾包含package.json文件,package.json中出現的第一項resolve.mainFields配置項中的文件決定最終的文件路徑。若是文件夾中沒有package.json文件,或者main fields中沒有返回一個合法的路徑,依次匹配resolve.mainFields配置項中指定的文件名,看是否找到匹配的文件路徑。至於文件擴展名,仍然使用resolve.extensions來解析。

二、resolving loaders

三、caching

每一個文件系統都是被緩存的,所以對一個文件的多個平行或者一系列請求會很快。在watch mode,只有修改過的文件會從緩存中刪去。若是watch mode被關閉,那麼緩存會在每次編譯前清除。

 

6、Targets

由於JS能夠用在服務端和瀏覽器端,你能夠在webpack配置文件中配置部署目標。

一、使用

module.exports = {
  target: 'node'
};

 在上面的例子中,使用node,webpack會編譯一個相似nodejs的環境。target默認設置爲web

 

二、multiple target

雖然webpack不支持給target傳多個字符串,可是能夠構建兩個獨立的配置來建立同構的庫

var path = require('path');
var serverConfig = {
  target: 'node',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'lib.node.js'
  }
  //
};

var clientConfig = {
  target: 'web', // <=== can be omitted as default is 'web'
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'lib.js'
  }
  //
};

module.exports = [ serverConfig, clientConfig ];

 這樣會在你的dist文件夾下生成lib.js和lib.node.js兩個文件。

 https://github.com/TheLarkInn/compare-webpack-target-bundles

 

7、manifest

基本上在一個基於webpack的應用中,會出現三種類型的code:

  • 應用源代碼
  • 第三方庫或者源代碼依賴的vendor code
  • 聯繫各個模塊的runtime 和 manifest

一、 Runtime

當你的應用在瀏覽器中運行時,runtime和manifest數據基本上是webpack聯繫你的模塊化應用所需的全部code。它包含你的模塊之間交互時,連接各個模塊所須要的加載和解析邏輯。這包括了鏈接已經加載到瀏覽器中的模塊,以及延遲加載的模塊。

二、manifest

那麼當你的應用以index.html文件在瀏覽器中運行時,那些bundles和其餘的文件是什麼樣的呢?src目錄下你寫的代碼都沒有了,那麼webpack是怎麼處理modules之間的交互的呢?這就是manifest data用到的地方。。。

當編譯器進入,解析並映射你的應用時,會一直關注你的modules。這些數據集合就是manifest,當這些modules bundled而且加載進瀏覽器時,runtime就會利用manifest來解析和加載modules。無論你用的是什麼模塊語言,import和require都會變爲指向module identifiers的__webpack_require__方法。使用manifest中的數據,runtime能夠找到這些identifiers後的module位置。

三、problem

這些內容平時通常都用不到,可是若是你想要使用瀏覽器緩存優化你的應用性能,就須要知道上面這些概念了。

將bundle文件名哈希表示,你能夠告訴瀏覽器文件內容改變了,緩存已經沒用了。但若是你這樣作,你會發現,有時候文件內容沒變,文件名的哈希表示也會變化。這就是由於runtime和manifest在每次構建時都會改變。

 

8、Hot Module Replacement

應用程序在運行時不須要從新加載頁面,HMR就能夠修改,添加和刪除modules。這在幾個方面加速開發過程:

  • 保存應用在從新加載時會丟失的狀態;
  • 只更新改變的內容,節約寶貴的開發時間
  • 更快的調整樣式,幾乎能夠和瀏覽器開發者模式中調整樣式同樣快速生效

How it works

一、in the application

1)應用程序要求HMR runtime檢查更新

2)runtime異步下載更新並通知應用程序

3)應用程序要求runtime應用更新

4)runtime同步應用更新

設置HMR,上面的步驟就會自動發生處理,或者也能夠經過用戶的交互來更新

 

二、in the compiler

除了更新普通的文件,編譯器還須要發出更新信號,來容許從舊的版本更新到新的版本。這個更新包括兩個部分:

  • 更新後的manifest(JSON)
  • 一個或多個更新後的chunks(JS)

manifest包含新的編譯hash和全部更新後的chunks清單。每個這種chunks包含全部更新過的modules 的新code(或者是一個flag表示module被刪除)。

編譯器會確保在這些構建中module id和chunk id一致,並將這些ID保存在內存中或者JSON文件中。

 

三、in a module

HMR只會影響包含HMR code的modules。好比style-loader,它實現了HMR接口,當從HMR接收到更新,會用新的樣式替換舊的樣式。

一樣的,當在一個module實現HMR接口時,你能夠根據module是否更新來決定接下來應該作什麼。可是,在大多數狀況下,不須要強制在每一個module中寫HMR code。若是一個module沒有HMR處理機制,更新會冒泡。這意味着,一個單獨的處理器能夠更新整個module tree。若是一個單獨的module被更新了,那麼全部依賴項都會從新加載。

 

四、in the runtime

對於module system runtime,會有額外的代碼來追蹤module的parents和children。在管理方面,runtime支持兩個方法:check和apply。

check向更新的manifest發送HTTP請求。若是請求失敗,表示沒有可用更新。若是成功,會對比更新後的chunks清單和當前已加載chunks清單。對於每一個已加載chunk,相應的更新chunk會被下載。全部module更新都保存在runtime。當全部更新的chunks被下載下來,而且已經準備應用更新,runtime會把狀態調整爲ready狀態。

apply方法將全部更新modules標誌爲無效。對於每個無效module,在module或者他的parents處須要有一個更新處理程序。不然,無效標誌會向上冒泡,將Parents也標誌爲無效。冒泡會持續冒到應用程序的入口或者到達一個有更新處理程序的module。若是從entry point開始冒泡,那麼進程失敗。

而後全部無效modules經過卸載程序被卸載。當前hash被更新,全部accept handlers被調用。runtime切換回idle狀態,一切迴歸正常。

相關文章
相關標籤/搜索