webpack並不強制你使用某種模塊化方案,而是經過兼容全部模塊化方案讓你無痛接入項目。有了webpack,你能夠隨意選擇你喜歡的模塊化方案,至於怎麼處理模塊之間的依賴關係及如何按需打包,webpack會幫你處理好的。css
關於模塊化的一些內容,能夠看看我以前的文章:js的模塊化進程html
正如js文件能夠是一個「模塊(module)」同樣,其餘的(如css、image或html)文件也可視做模 塊。所以,你能夠require(‘myJSfile.js’)亦能夠require(‘myCSSfile.css’)。這意味着咱們能夠將事物(業務)分割成更小的易於管理的片斷,從而達到重複利用等的目的。前端
傳統的模塊打包工具(module bundlers)最終將全部的模塊編譯生成一個龐大的bundle.js文件。可是在真實的app裏邊,「bundle.js」文件可能有10M到15M之大可能會致使應用一直處於加載中狀態。所以Webpack使用許多特性來分割代碼而後生成多個「bundle」文件,並且異步加載部分代碼以實現按需加載。node
每一個文件都是一個資源,能夠用require/import導入js 每一個入口文件會把本身所依賴(即require)的資源所有打包在一塊兒,一個資源屢次引用的話,只會打包一份 對於多個入口的狀況,其實就是分別獨立的執行單個入口狀況,每一個入口文件不相干(可用CommonsChunkPlugin優化)webpack
webpack只是一個打包模塊的機制,只是把依賴的模塊轉化成能夠表明這些包的靜態文件。並非什麼commonjs或者amd之類的模塊化規範。webpack就是識別你的 入口文件。識別你的模塊依賴,來打包你的代碼。至於你的代碼使用的是commonjs仍是amd或者es6的import。webpack都會對其進行分析。來獲取代碼的依賴。webpack作的就是分析代碼,轉換代碼,編譯代碼,輸出代碼。webpack自己是一個node的模塊,因此webpack.config.js是以commonjs形式書寫的(node中的模塊化是commonjs規範的)es6
webpack中每一個模塊有一個惟一的id,是從0開始遞增的。整個打包後的bundle.js是一個匿名函數自執行。參數則爲一個數組。數組的每一項都爲個function。function的內容則爲每一個模塊的內容,並按照require的順序排列。web
// webpack.config.js
module.exports = {
entry:'./a.js',
output:{
filename:'bundle.js'
}
};
複製代碼
// a.js
var b = require('./b.js');
console.log('a');
b.b1();
複製代碼
// b.js
exports.b1 = function () {
console.log('b1')
};
exports.b2 = function () {
console.log('b2')
};
複製代碼
以上代碼咱們打包處理的js爲express
// bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var b = __webpack_require__(1);
console.log('a');
b.b1();
/***/ },
/* 1 */
/***/ function(module, exports) {
exports.b1 = function () {
console.log('b1')
};
exports.b2 = function () {
console.log('b2')
};
/***/ }
/******/ ]);
複製代碼
咱們看到_webpack_require是模塊加載函數,接收模塊id(對,webpack中每一個模塊都會有一個獨一無二的id,其實也就是在IIFE傳參數組中的索引值(0,1,2.....) a依賴b,因此在a中調用webpack加載模塊的函數npm
// 1是模塊b的id
var b = __webpack_require__(1);
複製代碼
上面是使用的commonjs規範書寫的json
不管什麼模塊規範書寫。咱們的webpack進行識別後打包的內容不會相差不少,webpack有優秀的語法分析能力,支持 CommonJs AMD 等規範。
webpack爲何能把任何形式的資源都視做模塊呢?
由於loader機制。不一樣的資源採用不一樣的loader進行轉換。CMD、AMD 、import、css 、等都有相應的loader去進行轉換。那爲何咱們平時寫的es6的模塊機制,不用增長import的loader呢。由於咱們使用了babel把import轉換成了require。而且Webpack 2 將增長對 ES6 模塊的原生支持而且混用 ES六、AMD 和 CommonJS 模塊。這意味着 Webpack 如今能夠識別 import 和 export 了,不須要先把它們轉換成 CommonJS 模塊的格式:
webpack對於es模塊的實現,也是基於本身實現的webpack_require 和webpack_exports ,裝換成相似於commonjs的形式。es6 module是靜態的依賴,因此在運行前進行代碼轉換,這裏的實現是將全部導出項做爲一個對象的屬性,在入口文件執行時,去遞歸的加載模塊。
在解析對於文件,會自動去調用響應的loader,loader 本質上是一個函數,輸入參數是一個字符串,輸出參數也是一個字符串。固然,輸出的參數會被當成是 JS 代碼,從而被 esprima 解析成 AST,觸發進一步的依賴解析。
webpack會按照從右到左的順序執行loader。
假設前端在3000端口,服務端在4000端口,咱們經過 webpack 配置的方式去實現跨域。 首先,咱們在本地建立一個 server.js:
let express = require('express');
let app = express();
app.get('/api/user', (req, res) => {
res.json({name: '劉小夕'});
});
app.listen(4000);
複製代碼
執行代碼(run code),如今咱們能夠在瀏覽器中訪問到此接口: http://localhost:4000/api/user。
在 index.js 中請求 /api/user,修改 index.js 以下:
//須要將 localhost:3000 轉發到 localhost:4000(服務端) 端口
fetch("/api/user")
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.log(err));
複製代碼
咱們但願經過配置代理的方式,去訪問 4000 的接口。 配置代理 修改 webpack 配置:
//webpack.config.js
module.exports = {
//...
devServer: {
proxy: {
"/api": "http://localhost:4000"
}
}
}
複製代碼
從新執行 npm run dev,能夠看到控制檯打印出來了 {name: "劉小夕"},實現了跨域。
大多狀況,後端提供的接口並不包含 /api,即:/user,/info、/list 等,配置代理時,咱們不可能羅列出每個api。
修改咱們的服務端代碼,並從新執行。
//server.js
let express = require('express');
let app = express();
app.get('/user', (req, res) => {
res.json({name: '劉小夕'});
});
app.listen(4000);
複製代碼
儘管後端的接口並不包含 /api,咱們在請求後端接口時,仍然以 /api 開頭,在配置代理時,去掉 /api,修改配置:
//webpack.config.js
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'http://localhost:4000',
pathRewrite: {
'/api': ''
}
}
}
}
}
複製代碼
從新執行 npm run dev,在瀏覽器中訪問: http://localhost:3000/,控制檯中也打印出了{name: "劉小夕"},跨域成功,
目前爲止咱們 webpack 的配置,都定義在了 webpack.config.js 中,對於須要區分是開發環境仍是生產環境的狀況,咱們根據 process.env.NODE_ENV 去進行了區分配置,可是配置文件中若是有多處須要區分環境的配置,這種顯然不是一個好辦法。
更好的作法是建立多個配置文件,如: webpack.base.js、webpack.dev.js、webpack.prod.js。
webpack-merge 專爲 webpack 設計,提供了一個 merge 函數,用於鏈接數組,合併對象。
npm install webpack-merge -D
複製代碼
const merge = require('webpack-merge');
merge({
devtool: 'cheap-module-eval-source-map',
module: {
rules: [
{a: 1}
]
},
plugins: [1,2,3]
}, {
devtool: 'none',
mode: "production",
module: {
rules: [
{a: 2},
{b: 1}
]
},
plugins: [4,5,6],
});
//合併後的結果爲
{
devtool: 'none',
mode: "production",
module: {
rules: [
{a: 1},
{a: 2},
{b: 1}
]
},
plugins: [1,2,3,4,5,6]
}
複製代碼
webpack.config.base.js 中是通用的 webpack 配置,以 webpack.config.dev.js 爲例,以下:
//webpack.config.dev.js
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.config.base');
module.exports = merge(baseWebpackConfig, {
mode: 'development'
//...其它的一些配置
});
複製代碼
而後修改咱們的 package.json,指定對應的 config 文件:
//package.json
{
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --config=webpack.config.dev.js",
"build": "cross-env NODE_ENV=production webpack --config=webpack.config.prod.js"
},
}
你可使用 merge 合併,也可使用 merge.smart 合併,merge.smart 在合併loader時,會將同一匹配規則的進行合併,webpack-merge 的說明文檔中給出了詳細的示例。
複製代碼
todo
todo