開篇
webpack 在如今的前端開發中,算是不可繞過的一個工具吧。特別是在開發SPA應用的時候,無論是開發環境,還是打包上線,都十分依賴webpack。
開發環境
win10
node -v: 10.15.0
npm -v: 6.4.1
Let's go。
體驗0配置
進入到工作目錄,然後建立專案
mkdir spa-webpack-demo
複製程式碼
初始化
npm init -y
複製程式碼
先來體驗下 webpack4
的 0配置
:
安裝 webpack
npm i -D webpack
複製程式碼
安裝好 webpack 依賴後,建立 src 資料夾,並在 src 中新建一個 index.js
。
mkdir src
cd src
type nul > index.js
複製程式碼
修改 package.json
,在 scripts
選項中,新增兩個命令:
"dev": "webpack --mode=development",
"prod": "webpack --mode=production"
複製程式碼
好,完事了。接下來跑命令列,測試一下
npm run dev
複製程式碼
正常情況下,控制檯會有一段如下提示,因為 webpack
命令需要依賴 webpack-cli
,我們安裝即可
Do you want to install 'webpack-cli' (yes/no): yes
複製程式碼
webpack-cli
安裝完成之後,會自動繼續跑我們的 npm run dev
指令,即可看到專案中多了一個 dist 目錄,而且多了一個 main.js
。
接下來繼續嘗試 npm run prod
,可以看到 dist/main.js
已被壓縮。
這就是 webpack
號稱的零配置,主要的工作就是定義了預設的entry
路徑src/index.js
,定義了預設的output
路徑dist/main.js
,然後加了一個mode
引數,根據mode
引數的不同幫我們新增一些預置的打包規則。
循序漸進
上述的流程裡,只是體驗了一把零配置的感覺,連html檔案都沒有,這裡開始加上。
加個index.html
在根目錄新建 index.html
,隨便編寫點內容
type nul > index.html
複製程式碼
說到處理 html
檔案,肯定少不了 html-webpack-plugin
, 安裝它
npm i -D html-webpack-plugin
複製程式碼
然後再專案根目錄
新建一個 webpack.config.js
,webpack會自動使用它
type nul > webpack.config.js
複製程式碼
webpack.config.js
的內容如下
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
module: {
rules: []
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html'
})
]
}
複製程式碼
執行npm run dev
,即可看到 dist
資料夾多了個index.html
,這個index.html
自動引入了打包後的dist/main.js
。
加個本地伺服器
index.html
是生成了,可總不能每次手動開啟它在瀏覽器裡面預覽吧, webpack 官方推薦我們用 webpack-dev-server
做伺服器,安裝它
npm i -D webpack-dev-server
複製程式碼
安裝成功後, 修改webpack.config.js
,新增 devServer
選項 和 webpack.HotModuleReplacementPlugin
外掛。
對於檔案中已經新增過的內容,後面我都會用註釋表示。
const path = require('path');
const webpack = require('webpack');
function resolve(dir) {
return path.join(__dirname, './', dir)
}
module.exports = {
// module - 略
devServer: {
contentBase: resolve('dist'), // 根目錄
hot: true, // 是否開啟熱替換,無須手動重新整理瀏覽器
port: 8081, // 埠
open: true, // 是否自動開啟瀏覽器
noInfo: true // 不提示打包資訊,錯誤和警告仍然會顯示
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// HtmlWebpackPlugin - 略
]
}
複製程式碼
然後修改 package.json
scripts
的 dev
選項
"dev": "webpack-dev-server --mode=development",
複製程式碼
注意:當 devServer
的 hot
引數為true
時,記得要在外掛裡新增new webpack.HotModuleReplacementPlugin()
,
或者你可以在命令列中帶上hot
引數,這樣就不需要自己再往plugins
中新增外掛了。
"dev": "webpack-dev-server --hot --mode=development"
複製程式碼
然後npm run dev
,就可以嘗試靜態資源
熱替換功能了。
處理js, css 等其他靜態資源
首先我們要清楚一點,webpack 它本身是不知道應該如何處理靜態資源的,但是它提供了loader
和plugin
機制。
loader
的作用,顧名思義:載入器,就是匹配到的靜態資源,都要經過loader
的內部處理,再返回處理之後的結果。我覺得,loader
像是一個攔截器。
說到js,我們會想到 babel-loader
,babel-loader
是幹嗎的?常規操作是,將匹配到的js檔案
的ES6程式碼 根據 babelrc檔案內的配置
編譯成對應的 ES5程式碼。
我們這裡先新增一個.babelrc
檔案
新增.babelrc
檔案
type nul > .babelrc
複製程式碼
編輯 .babelrc
內容
{
// 預設定,告訴Babel要轉換的原始碼使用了哪些新的語法特性
// targets, useBuiltIns 等選項用於編譯出相容目標環境的程式碼
// 其中 useBuiltIns 如果設為 "usage"
// Babel 會根據實際程式碼中使用的 ES6/ES7 程式碼,以及與你指定的 targets,按需引入對應的 polyfill
// 而無需在程式碼中直接引入 import '@babel/polyfill',避免輸出的包過大,同時又可以放心使用各種新語法特性。
"presets": [
["@babel/preset-env", {
// modules 是否 將 ES6 的 import/export模組化 轉為 babel 的 CommonJs 規範模組化
"modules": false,
"targets": {
// "> 1%" : 支援市場份額超過1%的瀏覽器,
// ""last 2 versions"": 支援每個瀏覽器最後兩個版本
// "not ie <= 8": 在之前條件的瀏覽器中,排除 IE8 以下的瀏覽器
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
"useBuiltIns": "usage"
}]
],
// 所用外掛
// transform-runtime 外掛表示不管瀏覽器是否支援ES6,只要是ES6的語法,它都會進行轉碼成ES5
// 這個是需要優化的
"plugins": ["@babel/plugin-transform-runtime"]
}
複製程式碼
安裝 babel
依賴,注意:
babel 7+ 已經廢棄了presets 中 stage-x 的用法,改為在plugins中新增。並且應用了npm scope包,程式碼全部在 @babel 中,避免以前那種 babel-preset-xxx, babel-plugin-xxx 的用法
最新的 babel-loader 版本是8+,需要依賴 babel-core 版本7+,包名為 @babel/core, 版本6+的包名為 babel-core
。
再分析上面的 .babelrc 檔案,它用到了@babel/preset-env, @babel/plugin-transform-runtime, 這些依賴都要我們安裝好
如果使用了 @babel/preset-env,則不支援在 plugins 中 新增 stage-x
npm i -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime
複製程式碼
談到css,我們就應該 想到 style-loader
和 css-loader
。先安裝它們
npm i -D style-loader css-loader
複製程式碼
再安裝 url-loader
用於解析靜態資源,如圖片,字型等
npm i -D url-loader
複製程式碼
然後修改 webpack.config.js
的rules
, 新增如下程式碼
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [
resolve('src'),
resolve('node_modules/webpack-dev-server/client')
]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude: [],
options: {
limit: 10000,
name: 'img/[name].[hash:7].[ext]'
}
}
]
},
// devServer - 略
// plugins - 略
}
複製程式碼
接下來準備開發了,用 vue 吧。
vue 就不用裝在 devDependencies 中了。
npm i -S vue
// vue-loader 依賴 vue-template-compiler 和 vue-style-loader
npm i -D vue-loader vue-template-compiler vue-style-loader
複製程式碼
修改 webpack.config.js
, 新增 如下程式碼
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
// 其他 - 略
]
},
// devServer - 略
plugins: [
// 加在最前面
new VueLoaderPlugin()
// 其他 - 略
]
}
複製程式碼
src 下 新建一個 views 目錄 和 assets 目錄,
我在 assets 目錄下,增加了一個 logo.png 檔案
views下建立一個 myTest 元件,myTest/index.vue
, 編輯 index.vue
<template>
<div>
<i class="logo"></i>
</div>
</template>
<script>
export default {
name: 'myTest'
}
</script>
<style scoped>
.logo {
display: block;
margin: auto;
width: 400px;
height: 400px;
background: url(../../assets/logo.png);
}
</style>
複製程式碼
src
目錄下新建一個 App.vue
, 內容如下
<template>
<div id="app">
<my-test></my-test>
</div>
</template>
<script>
import myTest from "./views/myTest/index";
export default {
name: "App",
components: {
myTest
}
};
</script>
複製程式碼
編輯 src
目錄下的 index.js
,內容如下:
import Vue from 'vue';
import App from './App';
new Vue({
el: '#app',
render: h => h(App)
})
複製程式碼
最後npm run dev
,檢視效果。
錦上添花
新增 打包進度條 資訊,如下
npm i -D progress-bar-webpack-plugin
複製程式碼
修改 webpack.config.js
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
// ....
plugins: [
// 其他 - 略
new ProgressBarPlugin()
]
複製程式碼
新增 打包結果訊息通知
npm i -D webpack-build-notifier
複製程式碼
修改 webpack.config.js
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
// ....
plugins: [
// 其他 - 略
new WebpackBuildNotifierPlugin()
]
複製程式碼
歸類打包資訊
npm i -D webpack-dashboard
複製程式碼
修改 webpack.config.js
const DashboardPlugin = require('webpack-dashboard/plugin');
// ....
plugins: [
// 其他 - 略
new DashboardPlugin()
]
複製程式碼
修改 package.json
"dev": "webpack-dashboard -- webpack-dev-server --mode=development"
複製程式碼
這個我使用了,感覺效果不是很理想啊,會新開一個視窗,而且還不能滾動檢視資訊,不清楚是不是哪裡用錯了。
效果如圖:
整個程式碼結構如圖:
尚未完成的功能
production
環境,需要使用mini-css-extract-plugin
和optimize-css-assets-webpack-plugin
外掛,抽離並優化 css 檔案production
環境,需要使用UglifyJsPlugin
外掛,壓縮 js 檔案,這個外掛允許多核編譯production
環境,需要使用optimization
選項splitChunks
vue-router
與vuex
的引入- 等等
寫在最後
希望本文的流程能幫助到有需要的讀者,另外本文的打包功能實現的比較粗糙,打包速度比較慢,如果看官有啥建議,請在評論下告知下我。
如果有錯誤的地方,還請指出。謝謝閱讀。
程式碼地址 spa-webpack-demo