人人都是webpack使用者,小白從入門到精通秒變大神
什麼是WebPack,為什麼要使用它?
因為別人都在用啊,我不會用怎麼跟他們一起裝逼?
別人說的這些是什麼?我根本不懂:
類似gulp把自己定位為stream building tools一樣,webpack把自己定位為module building system。
在webpack看來,所以的檔案都是模組,只是處理的方式依賴不同的工具而已。
webpack同時也把node的IO和module system發揮的淋漓盡致。 webpack在配合babel(ES6/7)和tsc(typescript)等類似DSL語言預編譯工具的時候,駕輕就熟,為開發者帶來了幾乎完美的體驗。
weback在web構建工具的激烈競爭中逐漸脫引而出。 無論是編譯速度、報錯提示、可擴充套件性等都給前端開發者耳目一新的感覺。
畢竟小白,無所謂別人說什麼高大上的從gulp到grunt再到webpack的威水史,我們的目的是學會執行一個webpack demo然後跟別人吹吹:“其實webpack這東西也就那樣,百度都有!”
好!廢話不多說我們go
準備:
node環境
npm(cnpm)環境
開始:
1.新建一個資料夾,命名為webpack
2.開啟終端(黑視窗),cd到這個資料夾
3.使用npm init命令自動(所謂的自動就是一直按enter鍵,畢竟是練習所以全部預設)建立package.json檔案
4.執行npm install webpack -g
安裝全域性的webpack 和它的腳手架 npm install webpack-cli --g
5.將webpack這個元件寫到我們專案的package.json裡面,–save就是寫進package.json中的意思-dev是放到package.json裡面的devDependencies物件中,這裡的東西都是打包的時候都不會放進線上程式碼中去的,dependencies中的才是線上真正要的東西
// 安裝Webpack
npm install --save-dev webpack
媽的,這程式碼執行的時候竟然報錯了?咩咩咩?
大概就是說,你package中的專案名叫做webpack了,你不能再安裝這個模組了;講究。。。
好吧,我的錯,我們將package中的”name”: “webpack”改個更加low的名字”name”: “webpack1”
再執行
npm install --save-dev webpack
於是我們的檔案變成了這個鬼樣!
然後在webpack資料夾下建立一個webpack.config.js檔案
並在裡面建立兩個資料夾,app資料夾和public資料夾,app資料夾用來存放原始資料和我們將寫的JavaScript模組,public資料夾用來存放之後供瀏覽器讀取的檔案(包括使用webpack打包生成的js檔案以及一個index.html檔案)。接下來我們再建立檔案:
index.html –放在public資料夾中;
entry.js– 放在app資料夾中;
//webpack.config.js
module.exports = {
entry : './app/entry.js',//入口檔案
output : {//輸出檔案
filename : 'index.js',//輸出檔名
path : __dirname+'/public'//輸出檔案路徑
},
}
//entry.js
console.log('你好,老鐵!');
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>ss</title>
</head>
<body>
老王,你好!
<script src="index.js"></script>
</body>
</html>
然後直接執行webpack命令
發現打包成功了,public資料夾裡多了一個index.js,用瀏覽器開啟index.html也發現console.log出了:你好,老鐵!
我們再試試將css進行打包吧!先按照對應的解析器,主要用到了css-loader style-loader這裡兩個解析器,後面兩個是給圖片檔案用的。
npm install css-loader style-loader url-loader file-loader --save-dev
真是專案中還有許多要用的解析器,你們自個去豔遇她們吧!
然後再app裡面多加一個style.css檔案
//style.css
body {
font-size: 40px;
background: peachpuff;
color: green;
}
在entry.js裡面引入
//entry.js
require('./style.css');//引入css檔案
console.log('你好,老鐵!');
增加css檔案的解析規則
//webpack.config.js
module.exports = {
entry : './app/entry.js',//入口檔案
output : {//輸出檔案
filename : 'index.js',//輸出檔名
path : __dirname+'/public/'//輸出檔案路徑
},
module : {
rules: [
{test: /.css$/, use: ['style-loader', 'css-loader']},/*解析css, 並把css新增到html的style標籤裡*/
]
},
}
再次執行webpack命令,執行index.html你會發現老王綠了!
我們又來看看圖片這類的檔案怎麼打包的。放一張圖片到app資料夾中,命名為1.png
//entry.js
require('./style.css');//引入css檔案
console.log('你好,老鐵!');
var img = new Image();
img.src = require('./1.png');//當成模組引入圖片
document.body.appendChild(img);
主要用了url-loader file-loader 這兩個解析器
//webpack.config.js
module.exports = {
entry : './app/entry.js',//入口檔案
output : {//輸出檔案
filename : 'index.js',//輸出檔名
publicPath: __dirname + '/public/',//新增靜態資源, 否則會出現路徑錯誤
path : __dirname+'/public/'//輸出檔案路徑
},
module : {
rules: [
{test: /.css$/, use: ['style-loader', 'css-loader']},/*解析css, 並把css新增到html的style標籤裡*/
{test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,use: [{loader: 'url-loader',options: {limit: 10000}}]}
]
},
}
再次執行webpack命令,執行index.html新增的圖片出現了!
加深一點:
1.如果是多檔案
entry : {index1: './app/entry.js', index2: './app/entry2.js'},
output : {
filename : '[name].js',//這樣就可以生成兩個js檔案, 名字分別為index1.js, 和index2.js
}
2.別人用npm run build很酷,我們也可以,在package.json裡面修改一下即可。
"scripts": {
"build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
3.新手如何在瀏覽器中除錯打包後的專案呢?
加入devtool: ‘source-map’ 什麼是devtool
//webpack.config.js
module.exports = {
entry : './app/entry.js',//入口檔案
output : {//輸出檔案
filename : 'index.js',//輸出檔名
publicPath: __dirname + '/public/',//新增靜態資源, 否則會出現路徑錯誤
path : __dirname+'/public/'//輸出檔案路徑
},
module : {
rules: [
{test: /.css$/, use: ['style-loader', 'css-loader']},/*解析css, 並把css新增到html的style標籤裡*/
{test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,use: [{loader: 'url-loader',options: {limit: 10000}}]}
]
},
devtool: 'source-map'
}
在瀏覽器的sources下可以看到webpack資料夾下有許多專案的檔案,方便除錯;
小白教程基本結束,是不是不難,哇咔咔!
webpack整體架構(以webpack.config主要部分進行劃分)
1.entry: 定義整個編譯過程的起點
2.output: 定義整個編譯過程的終點
3.module: 定義模組module的處理方式
4.plugin 對編譯完成後的內容進行二度加工
5.resolve.alias 定義模組的別名
webpack的核心module
無論你是jsx,tsx,html,css,scss,less,png檔案,webpack一視同仁為module。並且每個檔案[module]都會經過相同的編譯工序 loader==> plugin。
關於以上這點,以如下一個簡單的webpack.config檔案為例。看下webpack會做什麼
module.exports = {
watch: true,
entry: './index.js',
devtool: 'source-map',
output: {
path: path.resolve(process.cwd(),'dist/'),
filename: '[name].js'
},
resolve: {
alias:{ jquery: 'src/lib/jquery.js', }
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
_: 'underscore',
React: 'react'
}),
new WebpackNotifierPlugin()
],
module: {
loaders: [{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.less$/,
loaders:['style-loader', 'css-loader','less-loader']
}, {
test: /\.(png|jpg|gif|woff|woff2|ttf|eot|svg|swf)$/,
loader: "file-loader?name=[name]_[sha512:hash:base64:7].[ext]"
}, {
test: /\.html/,
loader: "html-loader?" + JSON.stringify({minimize: false })
} ]
}
};
webpack是如何處理如上webpack.config檔案解析
1.確定webpack編譯上下文context
預設情況下就是node啟動的工作目錄process.cwd(),當然也可以在配置中手動指定context。
webpack在確定webpack.config中entry的路徑依賴時,會根據這個context確定每個要編譯的檔案(assets)的絕對路徑。
2.entry和output 確定webpack的編譯起點和終點
顧名思義,entry定義webpack編譯起點,入口模組。 對應的結果為compolation.assets
output定義webpack編譯的終點,匯出目錄
3.module.loaders 和 module.test 確定模組預編譯處理方式
以babel為例,當webpack發現模組名稱匹配test中的正則/js[x]?的時候。
它會將當前模組作為引數傳入babel函式處理,babel([當前模組資源的引用])。
函式執行的結果將會快取在webpack的compilation物件上,並分配唯一的id 。
以上的這一步,非常非常關鍵。唯一的id值決定了webpack在最後的編譯結果中,是否會存在重複程式碼。
而快取在compilation物件上,則決定了webpack可以在plugin階段直接拿取模組資源進行二度加工。
4.plugin階段貫穿於webpack的整個編譯流程,一般用來做一些優化操作。
比如webpack.ProvidePlugin,它會在對編譯結果再加工的操作過程中進行自定義的變數注入,當模組中碰到比如這個變數的時候,webpack將從快取的module中取出underscore模組載入進引用的檔案(compilation.assets)。
比如WebpackNotifierPlugin,它會在編譯結果ready的時通知開發者,output已經就緒。
5.resolve.alias的作用就是對module模組提供別名,並沒有什麼特殊的。
【副作用】 webpack編譯過程中的電腦卡慢?
在weback經歷以上流程的時候,檢視你的記憶體,你會發現,記憶體飆升!!!
這一般都是loader階段,對DSL進行AST抽象語法樹分析的時候,由於大量應用遞迴,記憶體溢位的情
況也是非常常見。
output目錄不是一個漸進的編譯目錄,只有在最後compilation結果ready的時候,才會寫入,造成開發者等待的時候,output目錄始終為空。
【webpack編譯物件compilation】 webpack將編譯結果匯出到output是怎麼做到的
如上,webpack在plugin結束前,將會在記憶體中生成一個compilation物件檔案模組tree。
這個階段是webpack的done階段 : webpack寫入output目錄的分割點。
這棵樹的枝葉節點就是所有的module[由import或者require為標誌,並配備唯一moduleId],
這棵樹的主枝幹就是所有的assets,也就是我們最後需要寫入到output.path資料夾裡的檔案內容。
最後,這個compilation物件也是所有webpackPlugin的處理的時候的arguments。
總結
對於開發者來說,整體而言webpack的編譯過程細節比較多,但是大體的框架還是比較直觀。
裡面涉及到的類似DSL,AST的概念及模組快取等等,在構建工具中還是比較常見的,配合watch模式,debug模式,對於開發者來說實在是一大利器。
一切檔案皆為模組也和react的一切dom都可以變為JS一樣,對前端世界帶來了新的開發理念。
但是,對於普通前端開發者來說,這些配置實在太繁瑣和困難了,特別是在處理大專案上,必須要有能架構整個前端工程的人把這個東西搭好,然後再讓小前端們敲component;不然就像小公司那樣傻瓜式vue-cli-人人都是架構師。