業務場景分析
隨著Vue、React生態的蓬勃發展,我們在開發單頁應用時,一般已離不開利用腳手架自動生成專案模板,熟悉的有
create-react-app project
vue create project
等命令。
但在很多應用場景下,我們往往需要走出前端框架的“溫室”來進行開發,常見的有:
- H5單頁面製作
- 傳統網頁開發(以
JQuery
,Bootstrap
為代表,以適應低版本瀏覽器如 IE) - 展示為主導的靜態頁面
- ...
優化點
可能大家在遇到上述開發場景時,可能就二話不說,直接開啟編輯器,擼起袖子就是“程式碼一把梭”。
不妨靜下心來,仔細剖析,一般流程我們有以下幾個優化點:
- 程式碼變更,頁面自動重新整理(不必再瘋狂蹂躪F5)
- 自動處理靜態資源的優化,快取,打包,壓縮(減少資源載入時間,減少資源HTTP請求)
- 用上
ES6,ES7,Sass,PostCSS
...(什麼瀏覽器廠商字首,import載入,輕鬆實現) - 開發和生產兩套配置(開發和生產職責分明,易於維護)
解決方案
webpack
是一個現代 JavaScript
應用程式的靜態模組打包器(module bundler
)。當 webpack
處理應用程式時,它會遞迴地構建一個依賴關係圖(dependency graph
),其中包含應用程式需要的每個模組,然後將所有這些模組打包成一個或多個 bundle
。
通俗的來說,webpack
就像一個大廚,HTML
是原料,而圖片、CSS、JS
則是調味料,我們只需提供原料和調味料,webpack
就會以“高超的廚藝”幫我們,烹飪出一道佳餚,也就是構建我們的Web
應用程式。
初嘗webpack
下面讓我們從一個例子入手,利用webpack4.x
逐步構建一個高複用,功能俱全,模組清晰的前端腳手架。
在命令列輸入以下命令
mkdir my-webpack && cd my-webpack
npm init -y // 可以 npm i nrm 下載 nrm 使用 nrm use taobeo 切換淘寶映象,提升下載速度,nrm ls 檢視當前使用的npm源
npm i webpack webpack-cli -g // 先全域性安裝,為了可以使用webpack命令
npm i webpack webpack-cli -D // 再專案本地安裝,因為專案所需webpack版本是固定的,而全域性webpack版本會隨時更新,為了防止版本衝突
複製程式碼
建立webpack.config.js、src/index.js、dist/index.html
目錄如下圖所示
<!-- dist/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My Webpack</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
複製程式碼
// src/index.js
console.log('hello webpack!');
複製程式碼
// webpack.config.js
const path = require('path');
const pathResolve = targetPath => path.resolve(__dirname, targetPath)
module.exports = {
entry: {
index: pathResolve('src/index.js') // 入口檔案,即要打包的js檔案
},
output: {
path: pathResolve('dist'), // 打包檔案的位置
filename: 'bundle.js' // 打包檔案的名稱
},
}
複製程式碼
然後在當前目錄下,在命令列輸入webpack
可以看到
Hash: c949bc1db5e801df578d
Version: webpack 4.28.4
Time: 377ms
Built at: 2019-01-15 20:57:39
Asset Size Chunks Chunk Names
bundle.js 959 bytes 0 [emitted] index
Entrypoint index = bundle.js
[0] ./src/index.js 47 bytes {0} [built]
WARNING in configuration // 通過設定mode的值('development' or 'production')即可解除警告
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
複製程式碼
這表明我們打包成功,dist
資料夾生成了bundle.js
供index.html
使用。開啟dist/index.html
,如圖所示:
webpack
也不過如此,只不過把js檔案換了個名字(index -> bundle
),反而更麻煩,還不如按部就班敲程式碼。No,這才是第一步,接來下可要坐穩了。
新增loader和plugin
我們發現,dist/index.html
還要手寫<script>
指令碼引入,實在麻煩,何不由webpack
自動生成,再來加點佐料(CSS,圖片),豈不是更完美!我們刪除dist
資料夾,在src
路徑下新增一張你喜歡的圖片。
在命令列執行以下命令。
npm i html-webpack-plugin style-loader css-loader url-loader file-loader -D
複製程式碼
修改以下檔案
/* src/index.css */
body {
background: #ff4040;
}
複製程式碼
// src/index.js
import './index.css'; // 匯入css檔案
import icon from './leaf.png'; 匯入圖片url
console.log('hello webpack!');
// 建立img標籤給src賦值,並插入html文件
const img = document.createElement('img');
img.setAttribute('src', icon);
document.body.appendChild(img);
複製程式碼
// webpack.config.js
const path = require('path');
const pathResolve = (targetPath) => path.resolve(__dirname, targetPath);
const htmlWebpackPlugin = require('html-webpack-plugin'); // 匯入‘html-webpack-plugin’外掛
module.exports = {
entry: {
index: pathResolve('src/index.js'),
},
output: {
path: pathResolve('dist'),
filename: '[name].js',
},
module: { // loader專注於處理制定格式的檔案,來載入各種各樣的資源
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的檔案
use: ['style-loader', 'css-loader'], // loader從右向左執行 先使用css-loader提取css資源,然後使用style-laoder通過<style>標籤插入<head>
},
{
test: /\.(png|jpg|jpeg|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192, // 小於8kb的檔案則轉為dataURL:Base64形式,大於8kb則交由file-loader處理,生成新圖片並打包
name: '[name].[hash:7].[ext]', // hash值對應本次build的hash值,每次打包都不相同
outputPath: 'img', // 輸出路徑,即dist/img
},
},
],
},
],
},
plugins: [ // 外掛專注於處理webpack在編譯過程中某個特定的任務
new htmlWebpackPlugin({ // 該外掛可以自動生成HTML檔案,並匯入靜態資源
filename: pathResolve('dist/index.html'), // 生成HTML檔名稱
title: 'my-webpack' // 文件名稱
}),
],
};
複製程式碼
在命令列執行webpack
,可以看到自動生成了dist
資料夾,開啟dist
資料夾下的index.html
,可以看到:
新增devServer和熱更新
當我們每次修改程式碼時,都要輸入webpack命令重新構建,令人頭痛,有沒有簡單的辦法呢?當然有! 我們可以利用webpack-dev-server來為我們搭建一個本地伺服器,提供及時瀏覽和熱更新。
在命令列輸入以下命令
npm i webpack-dev-server -D
複製程式碼
修改以下檔案
// package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
+ "dev" : "webpack-dev-server --mode development",
+ "build": "webpack --mode production"
},
複製程式碼
// webpack.config.js
+ const webpack = require('webpack');
module.exports = {
+ devServer: {
contentBase: pathResolve('dist'), // 本地伺服器所載入檔案的目錄
port: '8080', // 設定埠號為8080
inline: true, // 檔案修改後實時重新整理(瀏覽器重新整理)
historyApiFallback: true, // 使用HTML5 History API時,對於任何404響應,返回index.html
hot: true // 熱更新(瀏覽器不重新整理)
},
plugins: [
+ new webpack.HotModuleReplacementPlugin(), // 熱更新外掛
+ new webpack.NamedModulesPlugin()
]
}
複製程式碼
// src/index.js
...
// 如果存在熱更新,則啟用
if (module.hot) {
module.hot.accept()
}
複製程式碼
執行npm run dev
src/index.js
// src/index.js
+ console.log('hot replacement!');
複製程式碼
可以立刻看到:
很明顯,此處網頁並未重新整理,而是發生了熱更新。使用Sass,postCSS,分離CSS
回過頭看dist
下的檔案,並沒有任何css
相關檔案,html
檔案中頁沒有style
標籤,原來經過打包後,css
被整合在js
中。
CSS
從中分離出來,並且嘗試使用Sass(一種CSS前處理器語言)和postCSS來新增瀏覽器廠商字首 。
在命令列輸入以下命令
npm i mini-css-extract-plugin node-sass sass-loader postcss-loader autoprefixer -D
複製程式碼
新增src/index.scss
/* src/index.scss */
$link-color: #f1f1f1;
body {
background: $link_color;
}
#avatar {
width: 200px;
height: 200px;
background: url('./packet.png'); // 準備一張大於8kb的圖片
border-radius: 50%;
}
複製程式碼
新增src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>my-webpack</title>
</head>
<body>
<img id="avatar"></img>
</body>
</html>
複製程式碼
新增postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')({ // 自動管理瀏覽器字首的外掛
browsers: ['last 10 versions', 'Firefox >= 20', 'Android >= 4.0', 'iOS >= 8']
})
]
}
複製程式碼
修改以下檔案
// src/index.js
- import './index.css';
+ import './index.scss';
複製程式碼
// webpack.config.js
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module:{
rules:[
+ {
test: /\.(sa|sc|c)ss$/,
use: [{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../' // CSS中最終路徑 = publicPath + background: url,設定publicPath確保圖片取得到
}
},
'css-loader',
'postcss-loader',
'sass-loader',
],
},
]
},
plugins: [
+ new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:7].css', // contenthash 根據css檔案內容生成hash值
}),
new htmlWebpackPlugin({
- title: 'my-webpack',
+ template: pathResolve('src/index.html'), // HTML模板
}),
],
},
],
複製程式碼
執行npm run build
,可以看到,CSS
檔案已被分離成獨立檔案,在head
頭部以link
方式引入,並且自動新增了webkit
moz
ms
等相容性廠商字首。
結語
第一篇主要研究了什麼是webpack以及loader、plugin的用法,這樣我們的前端腳手架已經可以處理基本的業務需求。
第二篇將對我們的前端腳手架做進一步的優化,包括但不限於程式碼分割,壓縮,快取處理,多頁面。
本人水平有限,不足之處還希望大家在評論區提出,???再次感謝大家的閱讀!!!