深刻淺出的webpack構建工具---webpack基本配置(一)

深刻淺出的webpack構建工具---webpack基本配置(一)javascript

閱讀目錄css

1. 安裝webpack到全局

在學習構建以前,咱們來在本地文件新建一個存放項目的文件夾,好比叫demo1這個項目,而後進入demo1該項目的根目錄後,執行命令 npm init
運行下,一路回車(先簡單的來),就會生成一個package.json文件。

在項目中直接運行以下命令,就能夠把webpack安裝到全局去;以下命令:

npm install -g webpack

2. 安裝webpack到本項目。

在本項目中,執行以下命令,就能夠把webpack安裝到本地項目中,以下命令:

npm install --save-dev webpack

3. 如何使用webpack?

在編寫webpack代碼以前,咱們先搭建好目錄結構以下:

### 目錄結構以下:
demo1                                       # 工程名
|   |--- dist                               # 打包後生成的目錄文件             
|   |--- node_modules                       # 全部的依賴包
|   |--- js                                 # 存放全部js文件
|   | |-- demo1.js  
|   | |-- main.js                           # js入口文件
|   |
|   |--- webpack.config.js                  # webpack配置文件
|   |--- index.html                         # html文件
|   |--- styles                             # 存放全部的css樣式文件                              
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json

index.html代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app"></div>
  <script src="./dist/bundle.js"></script>
</body>
</html>

js/demo1.js 代碼假如是以下:

function demo1Func() {
  console.log(11);
};
module.exports = demo1Func;

js/main.js 代碼以下:

const demo1Func = require('./demo1.js');

demo1Func();

如上的簡單的代碼,咱們先從上面的簡單的代碼來學起,而後逐漸會慢慢的深刻的講解webpack知識點;咱們在項目的根目錄中新建 webpack.config.js件;編寫配置文件以下:

const path = require('path');

module.exports = {
  entry: './js/main.js',

  output: {
    // 將全部依賴的模塊合併輸出到一個叫bundle.js文件內
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, './dist')
  }
};

一切編寫文件好了之後,咱們通常狀況下會在項目的根目錄下 運行webpack命令;可是當咱們運行webpack後,會報錯以下信息:

One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:
 - webpack-cli (https://github.com/webpack/webpack-cli)
   The original webpack full-featured CLI.
 - webpack-command (https://github.com/webpack-contrib/webpack-command)
   A lightweight, opinionated webpack CLI.
We will use "npm" to install the CLI via "npm install -D".
Which one do you like to install (webpack-cli/webpack-command):

所以咱們能夠在項目的目錄下 安裝下 webpack-cli ,以下命令安裝:

npm install webpack-cli -g

安裝完成後,咱們再執行webpack命令後,就會在項目中根目錄生成dist文件夾,該文件夾內會生成 bundle.js 文件。這時候咱們再打開index.html, 運行後,會發現執行了依賴的demo1.js了。

3.1 理解配置文件 entry(入口)和 出口 (output)

入口(entry) 是指示webpack應該使用哪一個模塊,來做爲構建內部依賴js的開始,進入入口起點後,webpack會找出有哪些模塊和庫是入口js依賴的。
出口(output) 是告訴webpack在什麼地方輸出它所建立的bundles,以及如何命名這些文件,默認值爲 './dist'.

4. 使用loader

webpack的構建是一個採用CommonJS規範的模塊化項目,如今咱們仍是繼續上面的項目,咱們再在main.js會引入一個css文件。所以會在main.js代碼修改以下:

require('../styles/main.css');

const demo1Func = require('./demo1.js');

demo1Func();

而後main.css代碼以下:

#app {
  font-size: 18px;
}

若是咱們如今直接去執行webpack的話,就會報錯,所以咱們須要在webpack中加入Loader的機制。將webpack代碼改爲以下:

const path = require('path');

module.exports = {
  entry: './js/main.js',

  output: {
    // 將全部依賴的模塊合併輸出到一個叫bundle.js文件內
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        // 用正則去匹配以 .css結尾的文件,而後須要使用loader進行轉換
        test: /\.css$/,
        use: ['style-loader', 'css-loader?minimize']
      }
    ]
  }
};

如上代碼,咱們在webpack編譯以前須要安裝 style-loader 和 css-loader, 以下命令進行安裝:

npm install --save-dev style-loader css-loader

如上配置代碼中的 module.rules 數組配置了一組規則,是來告訴webpack在遇到哪些文件時使用哪些loader去加載和轉換,好比上面的使用test正則去匹配
以 .css 結尾的文件,會先使用 css-loader 讀取css文件,而後使用 style-loader將css的內容注入到javascript裏面去。

注意:
1. use屬性的值是一個使用Loader名稱組成的數組,Loader的執行順序是由後往前的。因爲loader有順序的,所以咱們在配置中不能以下編寫loader;
代碼以下:

module: {
    rules: [
      {
        // 用正則去匹配以 .css結尾的文件,而後須要使用loader進行轉換
        test: /\.css$/,
        use: ['css-loader?minimize', 'style-loader']
      }
    ]
  }

2. 每一個Loader均可以經過 URL queryString 的方式傳入參數,好比 css-loader?minimize是告訴css-loader是開啓css壓縮。

如今咱們能夠在項目中的根目錄運行 webpack命令了,一切正常後,咱們打開index.html後,查看代碼,發現css被加載到style標籤內了;

style-loader的原理:是將css的內容使用javascript的字符串存儲起來,在網頁執行javascript時經過DOM操做,動態地向HTML head標籤裏插入 HTML style標籤。

3. 配置loader的方式也可使用Object來實現,好比如上的匹配css代碼,能夠改爲以下:

use: [
    'style-loader', 
    {
      loader: 'css-loader',
      options: {
        minimize: true
      }
    }
] 

所以webpack全部的配置代碼變成以下:

const path = require('path');
  module.exports = {
    entry: './js/main.js',
    output: {
      // 將全部依賴的模塊合併輸出到一個叫bundle.js文件內
      filename: 'bundle.js',
      // 將輸出的文件都放在dist目錄下
      path: path.resolve(__dirname, './dist')
    },
    module: {
      rules: [
        {
          // 用正則去匹配以 .css結尾的文件,而後須要使用loader進行轉換
          test: /\.css$/,
          use: [
            'style-loader', 
            {
              loader: 'css-loader',
              options: {
                minimize: true
              }
            }
          ]
        }
      ]
    }
  };

5. 使用插件(Plugin)

loader的做用是被用於轉換某些類型的模塊,而插件則能夠用於執行範圍更廣的任務,插件的範圍包括,從打包優化和壓縮,一直到從新定義環節中的變量。
若是想要使用一個插件,咱們只須要require()它,而後把它添加到 plugins數組中。咱們能夠在一個配置文件中由於不一樣的目的屢次使用用一個插件,所以
咱們可使用new操做符來建立它的實列。

上面咱們是經過loader加載了css文件到js中去,下面咱們經過plugin將注入的bundle.js文件裏的css提取到單獨的文件中,咱們須要使用到
extract-text-webpack-plugin 插件,配置代碼以下:

const path = require('path');

// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  entry: './js/main.js',

  output: {
    // 將全部依賴的模塊合併輸出到一個叫bundle.js文件內
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        // 使用正則去匹配要用該loader轉換的css文件
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          // 轉換 .css文件須要使用的Loader
          use: ['css-loader']
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      // 從js文件中提取出來的 .css文件的名稱
      filename: `main.css`
    })
  ]
};

要使用 extract-text-webpack-plugin 插件的話,首先咱們須要安裝該插件,安裝插件的命令以下:

npm install --save-dev extract-text-webpack-plugin

當安裝成功後,咱們須要運行webpack命令後,發現命令行報錯了,報錯信息以下:

(node:86210) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
/usr/local/lib/node_modules/webpack/lib/Chunk.js:802

解決的辦法是 安裝 extract-text-webpack-plugin 時,須要以下安裝,安裝命令以下:

npm install extract-text-webpack-plugin@next

安裝成功後,再運行webpack就能夠了,咱們能夠看到在dist文件夾內會多一個main.css, 所以咱們能夠在index.html中把 css文件引入進去便可。

6. 使用DevServer

前面是使用webpack進行打包,可是在開發中咱們還須要一個本地文件的服務器,而且當咱們保存代碼的時候會自動進行打包,而且還支持 Source Map,
以方便代碼調試等功能,所以咱們如今須要使用到 DevServer了。

首先咱們須要安裝 webpack-dev-server, 以下安裝命令:

npm install --save-dev webpack-dev-server

固然咱們還須要全局安裝一下,安裝命令以下:

npm install webpack-dev-server -g

而後當咱們在命令行輸入 webpack-dev-server 運行後,發現報錯了,報錯以下信息:

The CLI moved into a separate package: webpack-cli.
Please install 'webpack-cli' in addition to webpack itself to use the CLI.
-> When using npm: npm install webpack-cli -D
-> When using yarn: yarn add webpack-cli -D
module.js:471

所以咱們須要從新運行下以下命令:

npm install webpack-cli -D

當成功後,咱們再次運行 webpack-dev-server 能夠看到已經啓動了服務器了,端口號默認是 8080, 而後咱們訪問 http://localhost:8080/index.html  就能夠訪問到咱們項目中頁面了。

6.1 實時預覽
webpack在啓動時能夠開啓監聽模式,默認是關閉的,開啓後webpack會監聽本地文件系統的變化,在發生變化時候會從新構建出新的結果,在上面運行
webpack-dev-server 後,咱們須要打開一個新的命令行,在命令行中輸入以下命令來開啓監聽模式:

webpack --watch

當項目中入口文件或入口依賴的文件有改變的時候,它會自動從新構建,構建完成後會自動刷新下頁面,可是若是修改的不是入口文件或依賴的文件
是不會有任何效果的,好比修改的是index.html 是不會從新構建的。可是要完成上面的實時預覽,html頁面的bundle.js 要直接引入便可:以下html頁面代碼:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app"></div>
  <script src="bundle.js"></script>
</body>
</html>

那爲何 http://localhost:8080/bundle.js 這樣也能訪問的到呢?緣由是DevServer會將webpack構建出的文件保存在內存中,DevServer不會理會
webpack.config.js裏配置的output.path屬性的。

6.2 模塊熱替換

除了上面介紹的改動本地入口文件或依賴文件後,會自動打包,而後會自動刷新瀏覽器便可看到更新效果外,咱們還可使用模塊熱替換技術,

模塊熱替換技術能作到在不從新加載整個網頁的狀況下,經過將已更新的模塊替換舊模塊,它默認是關閉的,要開啓模塊熱替換,咱們只需在啓動DevServer時帶上 --inline 參數便可。以下命令:

webpack-dev-server --inline  或 webpack-dev-server --inline --hot

webpack-dev-server 有以下兩種啓動模式:

iFrame: 該模式下修改代碼後會自動打包,可是瀏覽器不會自動刷新。
inline: 內聯模式,該模式下修改代碼,webpack將自動打包並刷新瀏覽器。

6.3 支持Source Map
在瀏覽器中運行javascript代碼都是編譯器輸出的代碼,可是若是在代碼中碰到一個bug的時候,咱們很差調式,所以咱們須要 Source Map來映射到源代碼上,Webpack支持生成 Source Map, 只需在啓動時帶上 --devtool source-map參數便可;以下命令:

webpack-dev-server --inline --hot --devtool source-map

注意:每次運行 如上命令,感受很是長,所以咱們能夠在項目的根目錄的package.json文件的scripts配置中添加以下配置:

"scripts": {
  "dev": "webpack-dev-server --devtool source-map --hot --inline"
}

加上如上配置後,咱們只須要在命令行中 運行 npm run dev 便可;

其餘配置常見的選項:
--quiet 控制檯中不輸出打包的信息
--compress 開啓gzip的壓縮
--progress 顯示打包的進度

所以在項目中scripts常常會作以下配置:

"scripts": {
  "dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
  "build": "webpack --progress --colors"
}

這樣的話,打包的時候會顯示打包進度。

1. context

基礎目錄,絕對路徑,用於從配置中解析入口起點和加載器。什麼意思呢?好比以下代碼配置:
context: path.resolve(__dirname, 'js'), 含義是使用當前目錄下的js文件下查找入口文件。好比以下代碼的配置也是能夠的:

module.exports = {
  context: path.resolve(__dirname, 'js'),
  entry: './main.js'
}

含義是從當前項目目錄下的js文件查找main.js文件做爲入口文件,若是在當前目錄沒有找到該入口文件,就會報錯。
固然咱們也能夠以下寫配置代碼也是能夠的:以下代碼配置:

module.exports = {
  context: path.resolve(__dirname, ''),
  entry: './js/main.js'
};

或者context配置項不要,它也是默認從當前目錄下查找的,所以若是使用context的話,建議加上第二個參數,是從當前目錄下那個文件內查找的。或者直接不要這個配置項。

2. entry

應用程序的起點入口,從這個起點開始,應用程序啓動執行,若是傳遞一個數組的話,那麼數組的每一項都會執行。它的類型能夠是String, array, 或 object;

2.1 string(類型爲字符串): 以下配置:

module.exports = {
  entry: './js/main.js',
  output: {
    // 將全部依賴的模塊合併輸出到一個叫bundle.js文件內
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, './dist')
  }
};

2.2 類型爲數組

module.exports = {
  entry: ['./js/main.js', './js/main2.js'],
  output: {
    // 將全部依賴的模塊合併輸出到一個叫bundle.js文件內
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, './dist')
  }
};

若是類型爲數組的話,將會建立多個主入口,而且把數組中的js打包在一塊兒到一個文件裏面去。

2.3 類型爲對象時

module.exports = {
  entry: {
    'main': './js/main.js',
    'main2': './js/main2.js'
  },
  output: {
    filename: '[name].js', // [name] 的值是entry的鍵值, 會輸出多個入口文件
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, './dist')
  }
};

3. Output

output配置是輸出最終想要的代碼,它是一個object, 裏面包含不少配置項。

3.1 filename 和 path的理解
filename: 輸出文件的名稱,爲string類型.
1) 若是隻有一個輸出文件,能夠將名稱寫死;如:filename: 'bundle.js';
path: 文件被寫入硬盤的位置。必須是string類型的絕對路徑,通常經過Node.js的path模塊獲取絕對路徑,以下代碼:
path: path.resolve(__dirname, './dist')

單個入口配置代碼以下:

{
  entry: './js/main.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist')
  }
}

多個入口

若是有多個chunk要輸出時,就須要藉助[name]變量了,webpack會爲每一個chunk取一個名稱,所以咱們根據chunk的名稱來區分輸出的文件名。以下:
filename: '[name].js'

多個入口配置代碼以下:

module.exports = {
  entry: {
    'main': './js/main.js',
    'main2': './js/main2.js'
  },
  output: {
    filename: '[name].js', // [name] 的值是entry的鍵值, 會輸出多個入口文件
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, './dist')
  }
};

如上配置,會輸出 main.js 和 main2.js。
內置變量除了包括name,還包括,id, hash, chunkhash。

id: Chunk的惟一標識,從0開始。
hash: Chunk的惟一標識的Hash值。好比[hash:8] 表明8位的hash值。默認是20位。
chunkhash: Chunk內容的Hash值。

以下hash配置代碼:

entry: {
  'main': './js/main.js',
  'main2': './js/main2.js'
},
output: {
  filename: '[name]_[hash:8].js', // [name] 的值是entry的鍵值, 會輸出多個入口文件
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, './dist')
}

就會生成 main_xxxx.js 和 main2_xxxx.js 其中xxxx是hash值的隨機八位。

3.2 chunkFilename的理解
chunkFilename 和 filename很是相似,可是chunkFilename是未被列在entry中的,可是又須要被打包出來的文件命名配置,什麼場景須要這樣的呢?
好比 異步按需加載模塊的時候,通常這樣的文件沒有被列在entry中,好比以下項目在js文件內再新建plugins文件夾,存放好比js插件使用的,目錄
結構以下:

### 目錄結構以下:
demo1                                       # 工程名
|   |--- dist                               # 打包後生成的目錄文件             
|   |--- node_modules                       # 全部的依賴包
|   |--- js                                 # 存放全部js文件
|   | |-- demo1.js  
|   | |-- main.js                           # js入口文件
|   | |-- main2.js                          # js的多個入口文件
|   | |-- plugins                           # plugins文件夾,存放js插件類的
|   | | |--- a.js
|   |
|   |--- webpack.config.js                  # webpack配置文件
|   |--- index.html                         # html文件
|   |--- styles                             # 存放全部的css樣式文件                              
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json

如上目錄結構 在js文件夾內,再新建plugins文件夾,裏面包含一個a.js文件;代碼以下:

function a() {
  console.log('a.js');
}
module.exports = a;

而後咱們在main2.js入口文件以下編寫代碼;使用異步方式引用a.js; 代碼以下:

require.ensure(['./plugins/a.js'], function(require) {
  var aModule = require('./plugins/a.js');
}, 'a');

而後在webpack的output配置添加 chunkFilename 配置以下:

module.exports = {
  context: path.resolve(__dirname, ''),
  entry: {
    'main': './js/main.js',
    'main2': './js/main2.js'
  },
  output: {
    filename: '[name]_[hash:8].js', // [name] 的值是entry的鍵值, 會輸出多個入口文件
    chunkFilename: '[name].min.js', 
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, './dist')
  }
};

其中上面的 require.ensure() API的第三個參數是給這個模塊命名的,所以在webpack配置完成後,繼續打包下,會在dist文件夾下打出 a.min.js 文件
了。因此這就是 chunkFilename 的用途場景了。

3.3 publicPath的理解

output.path 是指全部輸出文件的本地文件目錄(絕對路徑)。好比配置以下:

output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist')
}

那麼webpack會將全部文件輸出到 dist/下。也就是說path是存放打包後的文件的輸出目錄。

正式環境下publicPath的理解:
publicPath正式環境能夠理解爲改變相對目錄下的靜態資源文件的路徑爲正確的路徑。好比圖片引入的是相對路徑,能夠配置publicPath成爲正確的路徑。
它是指定資源文件引用的目錄(相對於服務器的根目錄來說)。

先看styles/main.css代碼以下:

#app {
  font-size: 18px;
  width: 200px;
  height: 200px;
  backround: url('../images/1.jpg') no-repeat;
}

iamges文件夾內有一張圖片爲1.jpg, 所以上面是css的引入路徑。而後手動建立 index.html代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link href="dist/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
  <div id="app"></div>
  <script src="dist/bundle.js"></script>
</body>
</html>

如今咱們執行 npm run build 進行打包後,打開index.html發現css中圖片的引用的路徑爲:backround: url(1.jpg) no-repeat; 很明顯圖片的引用路徑不對,它引入是根目錄下的圖片,所以若是咱們在打包的時候加上 publicPath,配置以下:

output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist'),
  publicPath: '/dist/'
}

從新打包下,再看下css引入路徑爲 backround: url(/dist/1.jpg) no-repeat;說明引入是對的。

開發環境下publicPath的理解:

output的配置以下:

output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist'),
  // publicPath: '/dist/'
}

而後把dist目錄刪除掉,執行 npm run dev後,開發環境打包後並無生成dist目錄,而咱們的index.html引入了 dist/bundle.js 和 dist/main.css, 所以頁面會發現找不到js和css文件。那麼打包後文件放在那裏去了呢?咱們能夠看以下圖:

啓動了webpack-dev-server, 而後整個項目運行在localhost:8080下面,也就是服務器地址是localhost:8080, 當咱們在瀏覽器中輸入服務器地址,服務器會籤就會到服務器請求js 文件,js的地址是dist/bundle.js, 沒有,報錯了。
看如上截圖的:wepback output is served from / , 這裏指明瞭webpack 打包後文件放到了/ 目錄下,也就是根目錄下,webpack-dev-server 進行打包時
它默認把css和js及圖片打包到根目錄下。
如今把output配置改爲以下:

output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist'),
  publicPath: '/dist/'
};

再進行運行下 npm run dev 後看到以下所示:

wepback output is served from /dist/ 這裏指明瞭webpack 打包後文件放到了/dist/ 目錄下了,由於js和css文件能夠從新訪問了。

3.4 crossOriginLoading

Webpack輸出的部分代碼有可能須要異步加載,而異步加載是經過JSON方式實現的。JSONP的原理是動態地向HTML中插入一個<script src="url">
</script>,crossOriginLoading則是用於配置這個異步插入的標籤的 crossorigin的值。
script標籤的crossorigin屬性能夠取如下的值:
1. anonymous(默認),啓用跨域加載,在加載此腳本資源時不會帶上用戶的Cookies; 即發送不帶憑據的 credential的請求。
2. use-credentials 啓用跨域加載,在加載此腳本資源時會帶上用戶的Cookies. 發送帶憑據的credential的請求。
3. false 禁用跨域加載。

3.5 libraryTarget 和 library

這兩個屬性你們可能比較陌生,通常項目中不須要關注這兩個屬性,可是當咱們開發類庫,使用webpack去構建一個能夠被其餘模塊導入使用的庫時會使用到。通常狀況下,webpack對js模塊進行打包,即多個js模塊和一個入口模塊,打包成一個bundle文件,能夠直接被瀏覽器或者其餘javascript引擎執行,

至關於直接編譯生成一個完成的可執行文件。可是當咱們須要發佈一個javascript庫的時候,好比在npm社區中發佈本身的庫,這個時候咱們的webpack就須要相應的配置.

libraryTarget 是配置以何種方式導出庫。能夠是 var, commonjs, 'commonjs2', amd, this,umd模式等。
library 配置導出庫的名稱。
他們通常都組合使用的。

咱們下面來編寫一個簡單的庫,假如名字叫 demo1.js,代碼以下:

function sayHello() {
  console.log('Hello');
}

function sayFunc() {
  console.log('say');
}

export default {
  sayHello: sayHello,
  sayFunc: sayFunc
}

而後咱們在main.js代碼引入該文件

import foo from './demo1.js';

console.log(foo);
foo.sayHello(); // 能夠執行

webpack打包配置以下: 

entry: './js/main.js',
output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist')
}

這樣會輸出一個當即執行的函數,bundle代碼大體結構以下:

(function(modules) { // webpackBootstrap
  var installedModules = {};
  function __webpack_require__(moduleId) {
    // ....
  }
  return __webpack_require__(__webpack_require__.s = "./js/main.js");
})
({
  "./js/demo1.js":
  (function(module, __webpack_exports__, __webpack_require__) {
      "use strict";
      eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n  console.log('Hello');\n}\n\nfunction sayFunc() {\n  console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n  sayHello: sayHello,\n  sayFunc: sayFunc\n});\n\n//# sourceURL=webpack:///./js/demo1.js?");
  }),
  "./js/main.js":
  (function(module, __webpack_exports__, __webpack_require__) {
      "use strict";
      eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\nconsole.log(_demo1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n_demo1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"].sayHello();\n\n\n\n//# sourceURL=webpack:///./js/main.js?");
  })
});

3.5.1 commonjs2 模式
若是咱們須要將打包返回值交給編譯後的文件 module.export, 所以咱們這邊可使用 libraryTarget 和 library, webpack配置以下:

entry: './js/main.js',
output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist'),
  libraryTarget: 'commonjs2',
  library: 'util'
}

main.js 代碼以下:

import demo1 from './demo1.js';

demo1.js 代碼以下:

function sayHello() {
  console.log('Hello');
}

function sayFunc() {
  console.log('say');
}

export default {
  sayHello: sayHello,
  sayFunc: sayFunc
}

打包後的文件是以下格式代碼:

module.exports = (function(modules) { // webpackBootstrap
  var installedModules = {};
  function __webpack_require__(moduleId) {
    // ....
  }
  return __webpack_require__(__webpack_require__.s = "./js/main.js");
})
({
  "./js/demo1.js":
  (function(module, __webpack_exports__, __webpack_require__) {
      "use strict";
      eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n  console.log('Hello');\n}\n\nfunction sayFunc() {\n  console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n  sayHello: sayHello,\n  sayFunc: sayFunc\n});\n\n//# sourceURL=webpack:///./js/demo1.js?");
  }),
  "./js/main.js":
  (function(module, __webpack_exports__, __webpack_require__) {
      "use strict";
      eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\nconsole.log(_demo1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n_demo1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"].sayHello();\n\n\n\n//# sourceURL=webpack:///./js/main.js?");
  })
});

那麼從npm社區下載庫後,使用庫的方法是以下:

const util1 = require('library-name-in-npm');
util1.xxx(); // xxx就是 某個方法名稱

3.5.2 commonjs
編寫的庫將經過commonjs導出。
webpack 配置代碼以下:

entry: './js/main.js',
output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist'),
  libraryTarget: 'commonjs',
  library: 'util'
}

打包後的文件變成以下代碼:

exports["util"] =
  (function(modules) { // webpackBootstrap
    var installedModules = {};
    function __webpack_require__(moduleId) {
      // ....
    }
    return __webpack_require__(__webpack_require__.s = "./js/main.js");
  })
  ({
    "./js/demo1.js":
   (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n  console.log('Hello');\n}\n\nfunction sayFunc() {\n  console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n  sayHello: sayHello,\n  sayFunc: sayFunc\n});\n\n//# sourceURL=webpack://util/./js/demo1.js?");
  }),
    "./js/main.js":
    (function(module, __webpack_exports__, __webpack_require__) {
      "use strict";
      eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\n\n\n\n//# sourceURL=webpack://util/./js/main.js?");

    })
});

如上代碼:配置了 output.library = 'LibraryName'; webpack就會輸出代碼格式爲:
exports['LibraryName'] = lib_code;

使用庫的方法代碼以下:

require('library-name-in-npm')['LibraryName'].doSomething();

注意:lib_code 爲全部的webpack打包的代碼;library-name-in-npm 是指模塊被髮布到npm代碼倉庫的名稱。

3.5.3 this
編寫的庫將經過this被賦值給library指定的名稱。

webpack配置代碼以下:

entry: './js/main.js',
output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist'),
  libraryTarget: 'this',
  library: 'util'
}

打包後的js文件以下格式:

this["util"] =
  (function(modules) { // webpackBootstrap
    var installedModules = {};
    function __webpack_require__(moduleId) {
      // ....
    }
    return __webpack_require__(__webpack_require__.s = "./js/main.js");
  })
  ({
    "./js/demo1.js":
   (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n  console.log('Hello');\n}\n\nfunction sayFunc() {\n  console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n  sayHello: sayHello,\n  sayFunc: sayFunc\n});\n\n//# sourceURL=webpack://util/./js/demo1.js?");
  }),
    "./js/main.js":
    (function(module, __webpack_exports__, __webpack_require__) {
      "use strict";
      eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\n\n\n\n//# sourceURL=webpack://util/./js/main.js?");

    })
});

使用庫的方法以下:

this.LibraryName.doSomething();

3.5.4 window
編寫的庫將經過window賦值給library指定的名稱,輸出的代碼格式以下:
window['LibraryName'] = lib_code;

webpack的配置以下:

entry: './js/main.js',
output: {
  filename: 'bundle.js',
  // 將輸出的文件都放在dist目錄下
  path: path.resolve(__dirname, 'dist'),
  libraryTarget: 'window',
  library: 'util'
}

打包後的js代碼以下:

window["util"] =
  (function(modules) { // webpackBootstrap
    var installedModules = {};
    function __webpack_require__(moduleId) {
      // ....
    }
    return __webpack_require__(__webpack_require__.s = "./js/main.js");
  })
  ({
    "./js/demo1.js":
   (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n  console.log('Hello');\n}\n\nfunction sayFunc() {\n  console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n  sayHello: sayHello,\n  sayFunc: sayFunc\n});\n\n//# sourceURL=webpack://util/./js/demo1.js?");
  }),
    "./js/main.js":
    (function(module, __webpack_exports__, __webpack_require__) {
      "use strict";
      eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\n\n\n\n//# sourceURL=webpack://util/./js/main.js?");

    })
});

使用庫的方法以下代碼:

window.LibraryName.doSomething();

3.5.5 global
編寫的庫將經過global賦值給經過library指定的名稱,即把庫掛載到global上,輸出格式的代碼以下:
global['LibraryName'] = lib_code;

和上面的window是同樣的,無非就是把window改爲global;

使用庫的方法以下所示:

global.LibraryName.doSomething();

也能夠打包成 'amd' 或 'umd' 模式結構代碼, 在此介紹省略哦~

4. 模式(mode)

提供mode配置項,告訴webpack使用對應的模式。

在webpack配置中提供mode選項。以下代碼:

module.exports = {
  mode: 'production' // 或開發環境下 'development'
};

注意:使用 development模式代碼不會被壓縮,使用 production 代碼會被壓縮。

也能夠在CLI參數中傳遞,好比以下代碼:
webpack --mode=production

5.理解使用Loader

咱們都知道webpack是適用於資源進行打包的,裏面的全部資源都是模塊,內部實現了對模塊資源進行加載機制,可是webpack只能處理js模塊,若是要處理其餘類型的文件,就須要使用loader進行轉換。Loader能夠理解爲是模塊和資源的轉換器,它自己也是一個函數,接收源文件做爲參數,返回轉換的結果。

配置loader,須要使用rules模塊來讀取和解析規則,它是一個數組,數組裏面中的每一項描述瞭如何處理部分文件。
1. 條件匹配: 經過配置test,include,exclude三個配置項來選中loader須要應用規則的文件。
2. 應用規則: 對選中的文件經過use配置項來應用loader,能夠只應用一個loader或者按照從右往左的順序應用一組loader(切記:loader使用順序是從右往左的),也能夠向loader傳入參數。
3. 重置順序: Loader執行的順序默認是從右到左執行的,可是咱們能夠經過enforce選項能夠將其中一個Loader的執行順序放到最前或最後。

具體的配置代碼能夠參考以下:

module.exports = {
  module: {
    rules: [
      {
        // 正則匹配 以 js結尾的
        test: /\.js$/,
        // babel 轉換js文件,?cacheDirectory 用於緩存babel的編譯結果,加快從新編譯的速度
        use: ['babel-loader?cacheDirectory'],
        // include只包含src目錄下的js文件,加快查找速度
        include: path.resolve(__dirname, 'src')
      },
      {
        // 正則匹配以 styl結尾的文件
        test: /\.styl$/,
        /* 
          use使用順序從右到左執行,先使用stylus-loader插件去解析以styl結尾的文件成css文件,
          再使用css-loader插件去讀取css文件,最後由 style-loader 將css的內容注入到javascript裏面。
        */
        use: ['style-loader', 'css-loader', 'stylus-loader'],

        // 排除node_modules目錄下的文件
        exclude: path.resolve(__dirname, 'node_modules')
      },
      {
        // 對非文本文件採用file-loader去加載
        test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
        use: ['file-loader']
      }
    ]
  }
}

注意:在loader須要傳入多個參數時,咱們能夠經過一個Object來描述,以下面是babel-loader配置:

use: [
  {
    loader: 'babel-loader',
    options: {
      cacheDirectory: true
    },
    /*
      enforce: 'post' 的含義是將Loader執行的順序放到最後
      enforce: 'pre' 的含義是將Loader執行順序放到最前面
    */
    enforce: 'post'
  }
]

固然,上面的test,include,exclude配置也能夠傳入多個,所以他們也支持數組的方式來傳遞了。好比以下配置:

{
  test: [
    /\.js$/,
    /\.jsx$/
  ],
  include: [
    path.resolve(__dirname, 'src'),
    path.resolve(__dirname, 'src2')
  ],
  exclude: [
    path.resolve(__dirname, 'node_modules'),
    path.resolve(__dirname, 'node_modules2')
  ]
}

6.理解noParse

該配置項可讓webpack忽略對部分未採用模塊化文件的遞歸解析和處理,該忽略的文件不能包含import,require, define等模塊化語句。
那麼這樣作的好處是能夠提升構建性能,好比像一些庫jquery等就沒有必要去使用webpack去遞歸解析和處理了。
使用配置代碼以下:

module.exports = {
  module: {
    noParse: /jquery|xxjs/
  }
}

也可使用函數,可是webpack須要從3.0版本才支持;以下配置代碼:

module.exports = {
  module: {
    noParse: (content) => {
      // content表明一個模塊的文件路徑
      return /jquery|xxjs/.test(content);
    }
  }
}

7. 理解alias

resolve.alias 是經過別名來將原導入路徑映射成一個新的導入路徑。好比以下配置:

module.exports = {
  entry: './js/main.js',
  output: {
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist')
  },
  resolve: {
    alias: {
      components: './src/components'
    }
  }
}

如上代碼配置,當我經過 import xxx from 'components/xxx' 導入時,實際上被alias替換成了
import xxx from './src/components/xxx';

8.理解extensions

在使用 import 導入文件時,有時候沒有帶入文件的後綴名,webpack會自動帶上後綴去訪問文件是否存在,那麼默認的後綴名爲
['.js', '.json']; 即:
extensions: ['.js', '.json']; 若是咱們想本身配置.vue後綴名,防止在導入文件時候不須要加上後綴;咱們可使用webpack作以下配置:

module.exports = {
  entry: './js/main.js',
  output: {
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist')
  },
  resolve: {
    alias: {
      components: './src/components'
    },
    extensions: ['.js', '.vue'];
  }
}

9.理解Externals

Externals 用來告訴webpack在構建代碼時使用了不處理應用的某些依賴庫,依然能夠在代碼中經過AMD,CMD或window全局方式訪問。
什麼意思呢?就是說咱們在html頁面中引入了jquery源文件後,好比以下代碼:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="http://code.jquery.com/jquery-1.12.0.min.js"></script>
  <link href="dist/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
  <div id="app"></div>
  <script src="dist/bundle.js"></script>
</body>
</html>

可是咱們想在模塊化的源代碼裏導入和使用jquery,首先咱們確定須要安裝下 jquery依賴包,npm install --save jquery, 而後咱們編寫以下代碼進行使用:

const $ = require('jquery');
console.log($);

構建打包後咱們會發現輸出的文件裏面包含jquery的內容,這致使了jquery庫引入了兩次,而且致使了bundle.js文件變得更大,浪費加載流量。所以 Externals 配置項就是來解決這個問題的。

經過externals 能夠告訴webpack在javascript運行環境中已經內置了哪些全局變量,不用將這些代碼打包到代碼裏面去。
所以咱們能夠作以下配置:

module.export = {
  externals: {
    jquery: 'jQuery'
  }
};
相關文章
相關標籤/搜索