一 前言
文章介紹了一個現代化的專案的webpack4環境是什麼樣的。這裡只是介紹了基礎的功能,如果需要詳細的相關只是可以去webpack官網去查閱。
程式碼地址:github
環境特點:
1.使用了webpack-dev-middleware,在檔案內容更改之後自動編譯;
2.使用了webpack-hot-middleware,在熱編譯之後會自動重新整理頁面更改的內容,而不是重新整理整個頁面
3.使用了server.js檔案來自己控制啟動http服務,後期可以擴充套件簡單的後端功能
文章將webpack的配置檔案寫成了三份:公用部分檔案、開發環境檔案、線上環境檔案,具體在文章中會有詳細的介紹。
注意:文章介紹很詳細,適合新手入門使用,請耐心閱讀。
二 正文
1.path相關內容
文件:http://nodejs.cn/api/path.html
我們經常用到的就是path.resolve和path.join,那麼我們就講一下兩者的用法和區別:
相關文章:Difference between path.resolve and path.join invocation?
(1)path.join
path.join() 方法使用平臺特定的分隔符把全部給定的 path 片段連線到一起,並規範化生成的路徑。
path.join('/a', '/b') // Outputs '/a/b'
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); // outputs '/foo/bar/baz/asdf'
summary:path.join通常用到的是簡單的將字串進行拼接。
(2)path.resolve
path.resolve() 方法會把一個路徑或路徑片段的序列解析為一個絕對路徑。
給定的路徑的序列是從右往左被處理的,後面每個 path 被依次解析,直到構造完成一個絕對路徑(就會停止解析)。
例如,給定的路徑片段的序列為:/foo、/bar、baz,則呼叫 path.resolve('/foo', '/bar', 'baz') 會返回 /bar/baz。
如果處理完全部給定的 path 片段後還未生成一個絕對路徑,則當前工作目錄會被用上。
生成的路徑是規範化後的,且末尾的斜槓會被刪除,除非路徑被解析為根目錄。
長度為零的 path 片段會被忽略。
如果沒有傳入 path 片段,則 path.resolve() 會返回當前工作目錄的絕對路徑。
例子:
path.resolve('/foo/bar', './baz');
// 返回: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/');
// 返回: '/tmp/file
(3)work directory 與 dirname
工作目錄和檔案目錄並不是一直相等,我們以'./src/view/index.js'檔案為例:
檔案目錄是固定的,就是檔案所在的目錄:./src/view/index.js
工作目錄是不確定的,檢視當前所在目錄:pwd
如果你是在./src/view/ 下執行的index.js,那麼就和檔案路徑相同./src/view/index.js
但是如果你在./src下執行的index.js,那工作路徑則是./src
2.從server.js談起
我們要知道server.js都做了些什麼:
1.開啟一個http伺服器
2.使用熱編譯中介軟體,實時編譯修改過的內容
3.使用熱替換,實時檢視最新的頁面UI
4.【擴充套件】可以做一些後端的東西
程式碼如下:
const http = require('http');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
// 使用express啟用一個伺服器
const express = require('express');
// 引用開發環境下的webpack配置檔案
const config = require('./webpack.dev');
const app = express();
const webpackConfig = webpack(config);
const devMiddlewareCompiler = webpackDevMiddleware(webpackConfig,{
publicPath:config.output.publicPath
});
const hotMiddlewareCompiler = webpackHotMiddleware(webpackConfig,{
log: false,
heartbeat: 2000,
})
app.use(devMiddlewareCompiler);// 使用熱編譯中介軟體
app.use(hotMiddlewareCompiler);// 使用熱替換中介軟體
app.listen(8080,function(){
console.log('Example app listening on port 8080!\n');
});
注意:為什麼要把熱編譯的功能放在node裡面呢?如果引用wepack-dev-server會自動管理熱編譯,它的原理也還是利用express開啟了一個小型伺服器,只不過我們看不到它。所以如果你要自己控制,並且想簡單方便的在後端做點小東西,可以完全使用上面的方法。如果是後端比較重就不建議這麼寫了,你需要開啟兩臺服務,透過代理的方式模擬進行前後端通訊了。
3.webpack common
webpack.common,js配置了無論是開發還是釋出都需要的東西,比如一些loader的轉譯,程式碼的打包壓縮等。
具體程式碼如下:
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js' // 入口檔案index.js
},
module:{
rules:[{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
],
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Production'
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
}
};
下面講一講主要做了什麼事:
3-1 file-loader
a. 載入圖片
如果我們要想像引用模組那樣引用一個圖片,例如:
import Rose from './img/rose.jpg'
在 webpack 裡負責圖片翻譯的是 file-loader。而且,webpack 在最終構建時,會自動將模組中引用的圖片複製到相應目錄。如果你檢查此元素,你將看到實際的檔名已更改為像 5c999da72346a995e7e2718865d019c8.png 一樣。這意味著 webpack 在 src 資料夾中找到我們的檔案,併成功處理過它!
b. 載入字型
css檔案中引用字型:
@font-face {
font-family: 'MyFont';
src: url('./my-font.woff2') format('woff2'),
url('./my-font.woff') format('woff');
font-weight: 600;
font-style: normal;
}
【擴充套件】file-loader與url-loader
如果我們希望在頁面引入圖片(包括img的src和background的url)。當我們基於webpack進行開發時,引入圖片會遇到一些問題。其中一個就是引用路徑的問題。拿background樣式用url引入背景圖來說,我們都知道,webpack最終會將各個模組打包成一個檔案,因此我們樣式中的url路徑是相對入口html頁面的,而不是相對於原始css檔案所在的路徑的。這就會導致圖片引入失敗。這個問題是用file-loader解決的,file-loader可以解析專案中的url引入(不僅限於css),根據我們的配置,將圖片複製到相應的路徑,再根據我們的配置,修改打包後檔案引用路徑,使之指向正確的檔案。另外,如果圖片較多,會發很多http請求,會降低頁面效能。這個問題可以透過url-loader解決。url-loader會將引入的圖片編碼,生成dataURl。相當於把圖片資料翻譯成一串字元。再把這串字元打包到檔案中,最終只需要引入這個檔案就能訪問圖片了。當然,如果圖片較大,編碼會消耗效能。因此url-loader提供了一個limit引數,小於limit位元組的檔案會被轉為DataURl,大於limit的還會使用file-loader進行copy。
//url-loader封裝了file-loader。url-loader不依賴於file-loader,即使用url-loader時,只需要安裝url-loader即可
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
}
3-2 css loader與style-loader
如果你在JS中使用:
import './index.css'
我們需要 CSS 載入器:
(1) css-loader - 預處理 CSS 檔案
(2) style-loader - 將 CSS 插入到 DOM 中的 style 標籤
要檢視 webpack 做了什麼,請檢查頁面(不要檢視頁面原始碼,因為它不會顯示結果),並檢視頁面的 head 標籤。它應該包含我們在 index.js 中匯入的 style 塊元素:<style>內容</style>。
注意:
(1)載入器的順序是從後往前的,loader 的順序很重要:如果把 style-loader 放到 css-loader 後面,我們就會撞見錯誤。
(2)我們如果只使用了 css-loader,則 webpack 只是將 CSS 檔案預處理成模組然後打包到構建檔案中,並不會插入到頁面。
【擴充套件】將CSS單獨打包:
webpack1/2/3:extract-text-webpack-plugin
webpack4:mini-css-extract-plugin
3-3 htm-lwebpack-plugin
詳細介紹見:html-webpack-plugin npm
這是一個webpack外掛,可以簡化建立HTML檔案以便為webpack包提供服務。 這對於webpack包來說特別有用,它在檔名中包含一個hash,用於更改每個編譯。 你可以讓外掛為你生成一個HTML檔案,使用lodash模板提供你自己的模板或使用你自己的載入器。
3-4 clean-webpack-plugin
詳細介紹見:clean-wenpack-plugin
一個在建立之前清除你build資料夾的webpack外掛。在打包生成新的build檔案的時候清除之前生成的,這非常有用。
4.webpack development
兩種環境的配置在webpack4中都支援mode的配置:development/production,具體的預設配置查詢可以移步這裡:webpack4 Mode的預設設定 。
webpack.dev.config.js:
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode:'development',
plugins:[
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
]
});
這裡做了兩件事:
(1)mode:'development':定義環境為開發環境。在webpack4之後省了很多操作,只需要指定為開發環境,就會自動設定source map等資訊
(2)HotModuleReplacementPlugin:模組熱替換
使用熱替換需要兩步:
首先: Add the following plugins to the plugins array
plugins: [
// OccurenceOrderPlugin is needed for webpack 1.x only
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
// Use NoErrorsPlugin for webpack 1.x
new webpack.NoEmitOnErrorsPlugin()
]
Occurence ensures consistent build hashes, hot module replacement is somewhat self-explanatory, no errors is used to handle errors more cleanly.
其次:Add 'webpack-hot-middleware/client' into the entry array.
This connects to the server to receive notifications when the bundle rebuilds and then updates your client bundle accordingly.
5.webpack production
兩種環境的配置在webpack4中都支援mode的配置:development/production,具體的預設配置查詢可以移步這裡:webpack4 Mode的預設設定 。
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
// const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
output:{
// publicPath:path.resolve(__dirname, 'dist'),
},
mode:'production',
});
指定環境為生產環境,預設開啟UglifyJSPlugin。
【擴充套件】將檔案標記為無副作用(side-effect-free)
src/math.js:
export function square(x) {
return x * x;
}
export function cube(x) {
return x * x * x;
}
src/index.js:
import { cube } from './math.js';
math.js檔案的square函式沒有被匯入,但是,它仍然被包含在 bundle 中。
在一個純粹的 ESM 模組世界中,識別出哪些檔案有副作用很簡單。然而,我們的專案無法達到這種純度,所以,此時有必要向 webpack 的 compiler 提供提示哪些程式碼是“純粹部分”。
這種方式是透過 package.json 的 "sideEffects" 屬性來實現的。
{
"name": "your-project",
"sideEffects": false
}
如同上面提到的,如果所有程式碼都不包含副作用,我們就可以簡單地將該屬性標記為 false,來告知 webpack,它可以安全地刪除未用到的 export 匯出。
如果你的程式碼確實有一些副作用,那麼可以改為提供一個陣列:
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js"
]
}