請手寫一個webpack4.0配置
原文segmentfault.com/a/1190000015611030 確認過眼神,你還是沒有準備秋招的人?時間倉促。自京東6月8號開啟管培生的招聘,就意味著秋招的開始。然而你還在等著秋天的到來?今年形勢應該更為嚴峻,隨著各大技術(vue,webpack,react,微信小程式)生態越來越成熟,這也意味著我們要更加深入的去了解他們,掌握他們。比如你寫vue專案,知道vue-cli。寫的專案也無以倫比,但是你是否又會去了解下vue-cli到底發生了什麼?或許這就是面試官要問你的問題:請手寫一個webpack4.0的配置。
前言
機會總是留給有準備的人,要想在這兵荒馬亂的求職季裡出類拔萃,拿下你心中期待已久的offer。那麼你更因該知道很多別人不知道的東西,你的羽翼才能豐滿,才能翱翔於天際。鷹擊長空,靠的不是天生,而是年少時斷崖之險。笨鳥先飛,靠的不是智慧,而是孜孜不倦的努力。
webpack詳解
webpack是一個打包工具,他的宗旨是一切靜態資源即可打包。有人就會問為什麼要webpack?webpack是現代前端技術的基石,常規的開發方式,比如jquery,html,css靜態網頁開發已經落後了。現在是MVVM的時代,資料驅動介面。webpack將現代js開發中的各種新型有用的技術,集合打包。webpack的描述鋪天蓋地,我就不浪費大家的時間了。理解下這種圖就知道webpack的生態圈了: [圖片上傳失敗...(image-a38d5f-1539330434707)]
webpack4.0的配置
const path = require('path'); //引入node的path模組 const webpack = require('webpack'); //引入的webpack,使用lodash const HtmlWebpackPlugin = require('html-webpack-plugin') //將html打包 const ExtractTextPlugin = require('extract-text-webpack-plugin') //打包的css拆分,將一部分抽離出來 const CopyWebpackPlugin = require('copy-webpack-plugin') // console.log(path.resolve(__dirname,'dist')); //實體地址拼接 module.exports = { entry: './src/index.js', //入口檔案 在vue-cli main.js output: { //webpack如何輸出 path: path.resolve(__dirname, 'dist'), //定位,輸出檔案的目標路徑 filename: '[name].js' }, module: { //模組的相關配置 rules: [ //根據檔案的字尾提供一個loader,解析規則 { test: /\.js$/, //es6 => es5 include: [ path.resolve(__dirname, 'src') ], // exclude:[], 不匹配選項(優先順序高於test和include) use: 'babel-loader' }, { test: /\.less$/, use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: [ 'css-loader', 'less-loader' ] }) }, { //圖片loader test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader' //根據檔案地址載入檔案 } ] } ] }, resolve: { //解析模組的可選項 // modules: [ ]//模組的查詢目錄 配置其他的css等檔案 extensions: [".js", ".json", ".jsx",".less", ".css"], //用到檔案的副檔名 alias: { //模快別名列表 utils: path.resolve(__dirname,'src/utils') } }, plugins: [ //插進的引用, 壓縮,分離美化 new ExtractTextPlugin('[name].css'), //[name] 預設 也可以自定義name 宣告使用 new HtmlWebpackPlugin({ //將模板的頭部和尾部新增css和js模板,dist 目錄釋出到伺服器上,專案包。可以直接上線 file: 'index.html', //打造單頁面運用 最後執行的不是這個 template: 'src/index.html' //vue-cli放在跟目錄下 }), new CopyWebpackPlugin([ //src下其他的檔案直接複製到dist目錄下 { from:'src/assets/favicon.ico',to: 'favicon.ico' } ]), new webpack.ProvidePlugin({ //引用框架 jquery lodash工具庫是很多元件會複用的,省去了import '_': 'lodash' //引用webpack }) ], devServer: { //服務於webpack-dev-server 內部封裝了一個express port: '8080', before(app) { app.get('/api/test.json', (req, res) => { res.json({ code: 200, message: 'Hello World' }) }) } } }
一、前端環境搭建
我們使用npm或yarn來安裝webpack
npm install webpack webpack-cli -g
# 或者
yarn global add webpack webpack-cli
為什麼webpack會分為兩個檔案呢?在webpack3中,webpack本身和它的cli以前都是在同一個包中,但在第4版中,他們已經將兩者分開來更好地管理它們。
新建一個webpack的資料夾,在其下新建一個try-webpack(防止init時專案名和安裝包同名)並初始化和配置webpack。
npm init -y //-y 預設所有的配置 yarn add webpack webpack-cli -D //-D webpack安裝在devDependencies環境中
二、部署webpack
在上面搭建好的環境專案中,我們來到package.json裡配置我們的scripts,讓webpack
"scripts": {
"build": "webpack --mode production" //我們在這裡配置,就可以使用npm run build 啟動我們的webpack
},
"devDependencies": {
"webpack": "^4.16.0",
"webpack-cli": "^3.0.8"
}
配置好我們webpack的執行環境時,聯想下vue-cli。平時使用vue-cli會自動幫我們配置並生成專案。我們在src下進行專案的開發,最後npm run build 打包生成我們的dist的目錄。不知道你是否還記得,還是讓我們進入下一節讓我們感受下這其中的正個流程吧。
三、npm run build 發生了什麼
在我們的根專案下try-webpack新建一個src目錄。在src目錄下新建一個index.js檔案。在裡面我們可以寫任意的程式碼,以案例為主:
const a = 1;
寫完之後我們在終端執行我們的命令 npm run build;你就會發現新增了一個dist目錄,裡面存放著webpack打包好的main.js檔案。這和我們在vue-cli裡操作是一樣的。
四、webpackp配置流程篇
我們在開發是一般會打包src下的什麼檔案呢?我們可以回憶一下,其實vue-cli專案src下不就這幾點嘛:
- 釋出時需要的html,css,js
- css預編譯器stylus,less,sass
- es6的高階語法
- 圖片資源.png,.gif,.ico,.jpg
- 檔案間的require
- 別名@等修飾符
那麼我將會分這幾點來講解webpack中webpack.config.js的配置,跟著腳步,一步一步的來完成我們的流程線。
✍️Html在webpack中的配置
在專案的根目錄try-webpack下新建webpack.config.js檔案,以commonJS模組化機制向外輸出,並且新建一個index.html。
module.exports = {}
配置我們的入口entry,在vue-cli裡相當於跟目錄下的main.js,我們的出口output。我們可以把webpack理解為一個工廠,進入相當於把各種各樣的原料放進我們的工廠了,然後工廠進行一系列的打包操作把打包好的東西,向外輸出,然後就可以去出售了(上線)。
const path = require('path'); //引入我們的node模組裡的path
//測試下 console.log(path.resolve(__dirname,'dist')); //實體地址拼接
module.exports = {
entry: './src/index.js', //入口檔案 在vue-cli main.js
output: { //webpack如何向外輸出
path: path.resolve(__dirname, 'dist'),//定位,輸出檔案的目標路徑
filename: '[name].js' //檔名[name].js預設,也可自行配置
},
HTML打包我們需要安裝引入html-webpack-plugin
yarn add html-webpack-plugin -D //在開發環境中安裝
const HtmlWebpackPlugin = require('html-webpack-plugin') //引入打包我們的HTML
在module.exports裡配置我們的plugins(外掛):
plugins: [ //插進的引用, 壓縮,分離美化
new HtmlWebpackPlugin({ //將模板的頭部和尾部新增css和js模板,dist 目錄釋出到伺服器上,專案包。可以直接上線
file: 'index.html', //打造單頁面運用 最後執行的不是這個
template: 'src/index.html' //vue-cli放在跟目錄下
}),
],
配置好後,在終端輸入npm run dev後webpack將我們的html打包好並且自動將我們的js引進來了。
<body>
<p class="main">Hello World</p>
<script type="text/javascript" src="main.js"></script>
</body>
live-sever我們的dist目錄,啟動一個8080埠,我們就可以看到我們的Hello World了。這就是我們上線版的頁面。
?css在webpack中的配置
在我們vue-cli裡,我們可以使用css去寫我們的樣式,也可以使用高階stylus,less,sass等預編譯器。這裡就以less為例,看看webpack怎麼將他打包成一個css。
.main {
color: red;
}
在src目錄下新建我們的style.less檔案,在配置之前我們需要npm我們的css-loader和sass-loader, sass
yarn add css-loader less less-loader style-loader -D
執行完上述命令我們在packge.json裡可以看到我們的配置檔案
"devDependencies": {
"css-loader": "^1.0.0",
"html-webpack-plugin": "^3.2.0",
"sass": "^1.9.0",
"sass-loader": "^7.0.3",
"webpack": "^4.16.0",
"webpack-cli": "^3.0.8"
}
安裝好後,我們開始配置webpack.config.js檔案。這裡申明一下,我們的css在dist目錄下需要和我們的HTML分離,這是還需引入我們的extract-text-webpack-plugin,先然我們安裝下
yarn add extract-text-webpack-plugin -D
這裡有一個坑,extract-text-webpack-plugin在4.0並不支援這樣的安裝,大家可自行chrome。於是我們選擇換一種方式,選擇4.00-beta.0版本的
yarn add [email protected] -D
來到我們的module.exports裡,完成moudel的配置
const ExtractTextPlugin = require('extract-text-webpack-plugin') //打包的css拆分,將一部分抽離出來
module: { //模組的相關配置
rules: [ //根據檔案的字尾提供一個loader,解析規則
{
test: /\.less$/, //正則匹配我們以.less結尾的檔案
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'less-loader'
]
})
},
]},
plugins:[
new ExtractTextPlugin('[name].css'), //[name] 預設 也可以自定義name 宣告使用
]
我們在執行我們的npm run build之後並沒有我們的css,為什麼呢?原來在webpack配置裡css in js。意思是在打包是我們的css是打包在我們的js裡的,所有我們引入了extract-text-webpack-plugin外掛將css從裡面剝離出來。不過又一個問題,require的機制?
在我們打包過程中,檔案的引用require 按照順序來打包,這就是檔案依賴的機制。
打包好後我們在live-server,發現我們的樣式也上去了,並且css部分分離出來了。
?js在webpack中的配置
現在隨著es6的普及,越來越多的程式碼使用es6了,但是很多瀏覽器並不支援es6,比如async/awiat,const。因此需要我們引用babe來把我們es6的程式碼編譯為es5。在跟目錄下新建.babelrc,簡單配置下
{"presets": ["env"]}
安裝我們的babel並在webpack.config.js裡module/rules下進行配置
yarn add babel-loader babel-core abel-preset-env -D //babel基本的三個檔案
{
test: /\.js$/, //es6 => es5
include: [
path.resolve(__dirname, 'src')
],
// exclude:[], 不匹配選項(優先順序高於test和include)
use: 'babel-loader'
},
?圖片資源在webpack中的配置
在src目錄下新建一個assets檔案,裡面放置幾張圖片。安裝file-loader根據檔案地址載入檔案
yarn add file-loader -D 在webpack.config.js裡module/rules
{
test: /\.(png|jpg|gif)$/, //匹配所有格式的圖片資源
use: [
{
loader: 'file-loader'
}
]
}
?別名(@)在webpack中的配置
在src/index.js裡我們引入
const format = require('utils/format') // utils ? 沒有相對路徑 回想@ => 別名
在src新建相應的檔案。在format.js裡接受一個引數並把它轉成大寫
module.exports = function format(chars) {
return chars.toUpperCase()
}
在webpack中如何配置我們的別名呢?在vue-cli中我們經常@一個資料夾,其意思就是在src目錄下,現在我們去一探究竟。在module下,注意跟rules同級
resolve: { //解析模組的可選項
// modules: [ ]//模組的查詢目錄 配置其他的css等檔案
extensions: [".js", ".json", ".jsx",".less", ".css"], //用到檔案的副檔名
alias: { //模快別名列表
utils: path.resolve(__dirname,'src/utils')
}
},
?其他一些靜態資源在webpack中的配置
- src下其他的檔案直接複製到dist目錄下,並不是每個檔案都需要打包處理的,很多資源可能直接就可以複製過去。使用我們的 CopyWebpackPlugin外掛
- 引用框架 jquery lodash工具庫是很多元件會複用的,省去了import。使用webpack.ProvidePlugin外掛
五、npm run dev 發生了什麼
在vue-cli中我們啟動監聽npm run dev可以時時監控我們src下檔案的改動,那他到底發生了什麼呢。在webpack裡其實建立了一個node程序,通過webpack-dev-server其內部封裝了一個node的express模組,其配置項如下
"scripts": { //在package.json裡宣告下使用指令碼 npm run dev
"build": "webpack --mode production",
"start": "webpack-dev-server --mode development"
},
devServer: { //在webpack.config.js裡配置port
port: '8080',
before(app) {
app.get('/api/test.json', (req, res) => {
res.json({
code: 200,
message: 'Hello World'
})
})
}
} //服務於webpack-dev-server 內部封裝了一個express
[圖片上傳失敗...(image-3cd814-1539330434706)]
寄語
webpack裡面博大精深,這只是剛入門。要研究其裡面真正的東西,並非一朝一夕。tapable、webpack的生命週期等等。如果你想深入的理解webpack,推薦騰訊ivweb團隊關於webpack的深入研究。這也是我最近在看的,只有理解更多,你才能飛的更遠。夜深了,時間過的很快。樓主也太困了,停筆於此,望多多包涵。附上我github關於webpack4.0配置的學習demo點這裡。