Webpack simple
以下均在 Webpack 4.42.1 版本環境下
mkdir webpack-simple
cd webpack-simple
npm init
npm i --save-dev webpack webpack-cli
touch index.html
touch webpack.config.js
mkdir src
cd src
touch main.js
在 index.html 中引入 bundle.js
<script src="./dist/bundle.js"></script>
在 main.js 中寫上如下代碼
console.info('hello webpack');
webpack.config.js 如下
const path = require('path'); // node path 模塊用來處理文件路徑
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist') // __dirname 爲當前文件路徑,dist 爲絕對路徑
}
}
執行「webpack」命令,在根目錄「dist」文件夾下就會看到一個「bundle.js」文件
打開文件後你會發現代碼量很多,實際上我們卻只寫了一行代碼,這其中前面的代碼都是 webpack 生成的「runtime」和「mainfest」代碼,後面會講到。
用瀏覽器打開 index.html,F12 能看到打印輸出了 “hello webpack”
loaders
有 html 還不夠,我們還得寫樣式呀,現在在 src 目錄下新建 style.css 文件
body {
background: pink;
}
在 main.js 中引入 style.css
import './style.css'
console.info('hello webpack')
現在我們再次執行「webpack」命令,會發現報下面的錯誤
這是因爲「webpack」只能對「js」文件進行打包編譯,碰到「css」文件它識別不出來,這個時候我們就要加上「loader」來讓它識別「css」文件。
執行 “npm i --save-dev style-loader css-loader”
修改「webpack.config.js」文件如下
const path = require('path')
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 處理,返回的結果交給 style-loader 處理。
css-loader 將 css 內容存爲 js 字符串,並且把 background,@font-face 等引用的圖片,
字體文件交給指定的 loader 打包
*/
use: ['style-loader', 'css-loader']
}
]
}
}
再次執行「webpack」命令,就不會報錯了,打開「index.html」就能看到添加樣式成功了。引入的圖片、字體、html等,都有相應的 loader 來編譯。
rules: [
{
/*
使用 babel 編譯 ES6 / ES7 / ES8 爲 ES5 代碼
使用正則表達式匹配後綴名爲 .js 的文件
*/
test: /\.js$/,
// 排除 node_modules 目錄下的文件,npm 安裝的包不需要編譯
exclude: /node_modules/,
/*
use 指定該文件的 loader,值可以是字符串或者數組。
這裏先使用 eslint-loader 處理,返回的結果交給 babel-loader 處理。loader 的處理順序是從最後一個到第一個
eslint-loader 用來檢查代碼,如果有錯誤,編譯的時候會報錯
babel-loader 用來編譯 js 文件
*/
use: ['babel-loader', 'eslint-loader']
},
{
// 匹配 html 文件
test: /\.html$/,
/*
使用 html-loader,將 html 內容存爲 js 字符串,比如當遇到
import htmlString from './template.html';
template.html 的文件內容會被轉成一個 js 字符串,合併到 js 文件裏
*/
use: 'html-loader'
},
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 處理,返回的結果交給 style-loader 處理。
css-loader 將 css 內容存爲 js 字符串,並且把 background,@font-face 等引用的圖片,
字體文件交給指定的 loader 打包,類似上面的 html-loader,用什麼 loader 同樣在 loaders 對象中定義,等會下面就會看到
*/
use: ['style-loader', 'css-loader']
},
{
/*
匹配各種格式的圖片和字體文件
上面 html-loader 會把 html 中 <img> 標籤的圖片解析出來,文件名匹配到這裏的 test 的正則表達式,
css-loader 引用的圖片和字體同樣會匹配到這裏的 test 條件
*/
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
/*
使用 url-loader,它接受一個 limit 參數,單位爲字節(byte)
當文件體積小於 limit 時,url-loader 把文件轉爲 Data URI 的格式內聯到引用的地方
當文件大於 limit 時,url-loader 會調用 file-loader,把文件儲存到輸出目錄,並把引用的文件路徑寫成輸出後的路徑
比如 views/foo/index.html 中
<img src="smallpic.png">
會被編譯成
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAA...">
而
<img src="largepic.png">
會被編譯成
<img src="/f78661bef717cf2cc2c2e5158f196384.png">
*/
use: [
{
loader: 'url-loader',
options: {
limit: 10000
}
}
]
}
]
plugins
這時候如果我們修改「webpack.config.js」中「output」的「filename」的名字,「index.html」中的名字也要修改,有沒有辦法在編譯的時候,自動生成「index.html」並且自動引入「entry」文件?
「html-webpack-plugin」就是用來做這個的
安裝
npm i --save-dev html-webpack-plugin
修改「webpack.config.js」
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 處理,返回的結果交給 style-loader 處理。
css-loader 將 css 內容存爲 js 字符串,並且把 background,@font-face 等引用的圖片,
字體文件交給指定的 loader 打包
*/
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
/*
template 參數指定入口 html 文件路徑,插件會把這個文件交給 webpack 去編譯,
webpack 按照正常流程,找到 loaders 中 test 條件匹配的 loader 來編譯,那麼這裏 html 就是匹配的 html-loader
html-loader 編譯後產生的字符串,會由 html-webpack-plugin 儲存爲 html 文件到輸出目錄,默認文件名爲 index.html
可以通過 filename 參數指定輸出的文件名
html-webpack-plugin 也可以不指定 template 參數,它會使用默認的 html 模版
*/
template: './index.html'
})
]
}
刪除「index.html」中引入「bundle.js」的腳本,執行「webpack」命令,成功後可以看到在「dist」目錄下多了一個「index.html」,並且已經引入了「bundle.js」,用瀏覽器打開的效果跟剛纔是一樣的。
開發
在「main.js」中打印一個錯誤日誌,並重新運行「wenpack」編譯,用瀏覽器打開「index.html」
import './style.css'
console.info('hello webpack')
console.error('這是一個錯誤')
我們看到,瀏覽器只會提示我們「bundle.js」中有錯誤,但是定位不到原始文件的位置,這個時候我們要使用「source map」,在「webpack.config.js」中增加配置
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devtool: 'inline-source-map',
...
}
重現編譯,刷新瀏覽器,我們就能看到精確定位代碼位置
當我們在「main.js」中加入包含「ES6」語法的代碼時,運行「npm run build」編譯時並沒有報錯
function printArr(){
const arr = Array(5).fill('6');
arr.map(item => {
console.log(item)
})
};
printArr();
這是因爲 webpack 4 依賴包中使用「terser-webpack-plugin」替代了之前一直使用的「uglifyjs-webpack-plugin」作爲它的內置插件,並做代碼壓縮處理.
重新加載(live reloading)
每次要編譯代碼時,手動運行 webpack 就會變得很麻煩。
「webpack-dev-server」 提供了一個簡單的 web 服務器,並且能夠實時重新加載(live reloading)。
npm i --save-dev webpack-dev-server
webpack.config.js 加入配置
module.exports = {
devServer: {
contentBase: './dist'
}
}
以上配置告知 webpack-dev-server,在 localhost:8080 下建立服務,將 dist 目錄下的文件,作爲可訪問文件。
讓我們添加一個 script 腳本,可以直接運行開發服務器(dev server):
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open"
}
現在執行「npm run dev」,瀏覽器會自動打開頁面,修改「main.js」的內容
console.log('hello webpack-dev-server')
我們可以看到瀏覽器上的內容已經實時更新了,並不用再刷新瀏覽器來查看結果
HMR(模塊熱替換)
模塊熱替換(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允許在運行時更新各種模塊,而無需進行完全刷新。
啓用此功能實際上相當簡單。而我們要做的,就是更新 webpack-dev-server 的配置,和使用 webpack 內置的 HMR 插件。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack'); // +
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
hot: true // +
},
module: {
rules: [
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 處理,返回的結果交給 style-loader 處理。
css-loader 將 css 內容存爲 js 字符串,並且把 background,@font-face 等引用的圖片,
字體文件交給指定的 loader 打包,類似上面的 html-loader,用什麼 loader 同樣在 loaders 對象中定義,等會下面就會看到
*/
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
}),
new webpack.NamedModulesPlugin(), // +
new webpack.HotModuleReplacementPlugin() // +
]
}
分離配置
新建三個文件「webpack.base.conf.js」「webpack.dev.conf.js」「webpack.prod.conf.js」
// webpack.base.conf.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/main.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
// 匹配 css 文件
test: /\.css$/,
/*
先使用 css-loader 處理,返回的結果交給 style-loader 處理。
css-loader 將 css 內容存爲 js 字符串,並且把 background,@font-face 等引用的圖片,
字體文件交給指定的 loader 打包,類似上面的 html-loader,用什麼 loader 同樣在 loaders 對象中定義,等會下面就會看到
*/
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
// webpack.dev.conf.js
const webpack = require('webpack')
const merge = require('webpack-merge'); // 合併配置
const common = require('./webpack.base.conf');
module.exports = merge(common,{
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
})
// webpack.prod.conf.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.base.conf');
module.exports = merge(common,{
mode: 'production',
})
// package.json
"scripts": {
"start": "webpack-dev-server --inline --progress --config webpack.dev.conf.js",
"build": "webpack --config webpack.prod.conf.js"
}
代碼分離
先引入「lodash」
npm i --save lodash
main.js 中添加如下代碼, 並執行「npm run build」命令
function component() {
var element = document.createElement('div')
element.innerHTML = _.join(['hello', 'webpack'], '')
return element
}
document.body.appendChild(component())
在「dist」目錄下,我們能看到「app.bundle.js」和「index.html」兩個文件,但是打開「bundle.js」會發現我們「main.js」中引入的「lodash」也打包進去了,這樣在後續引入多個第三方包後會使得我們「app.bundle」文件越來越大,所以我們要把第三方插件抽離出來,統一打包到一個文件中去
1、第三方插件分離
module.exports = {
optimization: {
splitChunks: { // 替代 webpack.optimize.CommonsChunkPlugin
cacheGroups: {
vendors: {
chunks: "all",
test: /[\\/]node_modules[\\/]/,
name: "vendors",
minChunks: 1,
minSize: 30000,
priority: 10 // 設置優先級
}
}
}
}
}
此時在「dist」目錄下會多一個「vendors.cd5da2e8640d819f8b41.js」文件,打開就會發現裏面是「lodash」的代碼