webpack4學習筆記(三)
前言
這是我花了幾個星期學習webpack4的學習筆記。內容不夠細,因為一些相對比較簡單的,就隨意帶過了。希望文章能給大家帶來幫助。如有錯誤,希望及時指出。例子都在learn-webpack
倉庫上。如果你從中有所收穫的話,希望你能給我的github
點個star
。
library
當你要開發第三方庫供別人使用時,就需要用到library
和libraryTarget
這兩個配置了。
library
output: { filename: 'library.js', library: 'library', libraryTarget: 'umd' },
library
: 當配置了這個library
引數後,會把library
這個key
對應的value
即上面程式碼library
掛載到全域性作用域中。html
用script
標籤引入,可以通過訪問全域性變數library
訪問到我們自己開發的庫。
libraryTarget
:這個預設值為var
。意思就是讓library定義的變數掛載到全域性作用域下。當然還有瀏覽器環境的window
,node
環境的global
,umd
等。當設定了window
、global
,library
就會掛載到這兩個物件上。當配置了umd
後,你就可以通過import
,require
等方式引入了。
externals
exterals
是開發公共庫很重要的一個配置。當你的公共庫引入了第三方庫的時候,公共庫會把該第三方庫也打包進你的模組裡。當使用者也引入了這個第三方庫後,這個時候打包就會又打了一份第三方庫進來。
所在在公共模組庫中配置如下程式碼
externals: { // 前面的lodash是我的庫裡引入的包名 比如 import _ from 'lodash' // 後面的lodash是別人業務程式碼需要注入到他自己模組的lodash 比如 import lodash from 'lodash',注意不能import _ from 'lodash',因為配置項寫了lodash 就不能import _ lodash: 'lodash' },
前面的lodash
是我的庫裡引入的包名 比如import _ from 'lodash'
,後面的lodash
是別人業務程式碼需要注入到他自己模組的lodash
比如import lodash from 'lodash'
,注意不能import _ from 'lodash'
,因為配置項寫了lodash
就不能import _
。
本人做了個試驗,當自己開發的包不引入lodash
,業務程式碼中也不引入lodash
,那麼打包業務程式碼的時候,webpack
會把lodash
打進你業務程式碼包裡。
當然externals
,配置還有多種寫法,如下
externals: { lodash: { commonjs: 'lodash', commonjs2: 'lodash', amd: 'lodash', root: '_' } } externals: ['lodash', 'jquery'] externals: 'lodash'
具體請參考官網externals
釋出自己開發的npm包
學了上面的配置後,就需要學習下如何將自己的包釋出到npm
倉庫上了。
-
package.json
的入口要改成dist
目錄下的js檔案如:"main": "./dist/library.js"
- 註冊npm賬號。npm會發送一份郵件到你的郵箱上,點選下里面的連結進行啟用。
-
命令列輸入
npm login
進行登入,或者npm adduser
新增賬號 -
npm publish
當出現如下提示代表釋出成功
// 當出現類似如下程式碼時,表示你已經發布成功 ➜library git:(master) ✗ npm publish + [email protected]
遇到的問題:
當你遇到npm ERR! you must verify your email before publishing a new package
說明你還沒有啟用你的郵箱,去郵箱裡點選下連結啟用下就ok了
當你已經登入了提醒npm ERR! 404 unauthorized Login first
,這個時候你就要注意下你的npm
源了,看看是否設定了淘寶源等。記得設定回來npm config set registry https://registry.npmjs.org/
PWA
http-server
workbox-webpack-plugin
相信很多朋友都有耳聞過PWA
這門技術,PWA
是Progressive Web App
的英文縮寫, 翻譯過來就是漸進式增強WEB應用, 是Google 在2016年提出的概念,2017年落地的web技術。目的就是在移動端利用提供的標準化框架,在網頁應用中實現和原生應用相近的使用者體驗的漸進式網頁應用。
優點:
- 可靠 即使在不穩定的網路環境下,也能瞬間載入並展現
- 快 快速響應,並且 動畫平滑流暢
應用場景:
當你訪問正常執行的伺服器頁面的時候,頁面正常載入。可當你伺服器掛了的時候,頁面就無法正常載入了。
這個時候就需要使用到pwa技術了。
這裡我編寫最簡單的程式碼重現下場景:
// webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { mode: 'production', entry: './index.js', output: { filename: 'bundle.js' }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin() ] } // index.js console.log('this is outer console') // package.json { "name": "PWA", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack --config webpack.config.js", "start": "http-server ./dist" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "clean-webpack-plugin": "^2.0.1", "html-webpack-plugin": "^3.2.0", "http-server": "^0.11.1", "webpack": "^4.30.0", "webpack-cli": "^3.3.1", } }
執行下npm run build
. ├── bundle.js └── index.html
為了模擬伺服器環境,我們安裝下http-server
npm i http-server -D
配置下package.json
,"start": "http-server ./dist"
執行npm run start
來啟動dist資料夾下的頁面
這個時候控制檯會正常打印出'this is outer console'
當我們斷開http-server
服務後,在訪問該頁面時,頁面就報404了
這個時候就需要使用到pwa技術了
使用步驟:
安裝:npm i workbox-webpack-plugin -D
webpack配置檔案配置:
// webpack.config.js const {GenerateSW} = require('workbox-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { mode: 'production', entry: './index.js', output: { filename: 'bundle.js' }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin(), new GenerateSW({ skipWaiting: true, // 強制等待中的 Service Worker 被啟用 clientsClaim: true // Service Worker 被啟用後使其立即獲得頁面控制權 }) ] }
這裡我們寫一個最簡單的業務程式碼,在註冊完pwa之後列印下內容:
// index.js console.log('this is outer console') // 進行 service-wroker 註冊 if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('./service-worker.js') .then(registration => { console.log('====== this is inner console ======') console.log('SW registered: ', registration); }) .catch(registrationError => { console.log('SW registration failed: ', registrationError); }); }); }
執行下打包命令:npm run build
. ├── bundle.js ├── index.html ├── precache-manifest.e21ef01e9492a8310f54438fcd8b1aad.js └── service-worker.js
打包之後會生成個service-worker.js
與precache-manifest.e21ef01e9492a8310f54438fcd8b1aad.js
接下來再重啟下http-server
服務:npm run start
頁面將會打印出
this is outer console ====== this is inner console ====== ...
然後我們再斷開http-server
服務
重新整理下頁面,竟然打印出了相同的程式碼。說明pwa離線快取成功。
typescript
使用webpack打包ts檔案,就需要安裝ts-loader
安裝:npm i ts-loader typescript -D
webpack.config.js
檔案中新增解析typescript
程式碼的loader
const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { mode: 'production', entry: './src/index.ts', output: { filename: 'bundle.js' }, module: { rules: [ { test: /\.ts$/, loader: 'ts-loader', exclude: /node_modules/ } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin() ] }
配置了webpack.config.js
還不行,還得在根目錄檔案下新增個.tsconfig.json
檔案
{ "compilerOptions": { "outDir": "./dist/", // 預設解析後的檔案輸出位置 "noImplicitAny": true, // 存在隱式 any 時拋錯 "module": "es6", // 表示這是一個es6模組機制 "target": "es5", // 表示要講ts程式碼轉成es5程式碼 "allowJs": true // 表示允許引入js檔案。TS 檔案指拓展名為 .ts、.tsx 或 .d.ts 的檔案。如果開啟了 allowJs 選項,那 .js 和 .jsx 檔案也屬於 TS 檔案 } }
新建index.ts
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } } let greeter = new Greeter("world"); let button = document.createElement('button'); button.textContent = "Say Hello"; button.onclick = function() { alert(greeter.greet()); } document.body.appendChild(button);
執行打包命令,訪問打包後的頁面,頁面正常執行。
當需要使用lodash
等庫時,
需安裝:npm i @types/lodash -D
修改頁面程式碼 引入lodash
import * as _ from 'lodash' class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } } let greeter = new Greeter("world"); let button = document.createElement('button'); button.textContent = "Say Hello"; button.onclick = function() { alert(_.join(['lodash', greeter.greet()], '-')); } document.body.appendChild(button);
提醒:ts使用的包,可通過https://microsoft.github.io/TypeSearch
這個網址去查對應的包使用指南
使用WebpackDevServer
實現請求轉發
當我們工作本地開發某一個需求的時候,需要將這塊需求的請求地址轉發到某個後端同事的本地伺服器或某個伺服器上,就需要用到代理。然後其他頁面的請求都走測試環境的請求。那麼我們該怎樣攔截某個請求,並將其轉發到我們想要轉發的介面上呢?
這個時候就需要用到webpack-dev-server
主要看devServer
配置:
// webpack.config.js const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: "development", entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './dist', open: true, hot: true }, plugins: [ new HtmlWebpackPlugin(), new CleanWebpackPlugin() ] }
// package.json scripts: { "server": "webpack-dev-server" }
// index.js import axios from 'axios' const div = document.createElement('div') div.innerHTML = 'hahahha' div.addEventListener('click', () => { alert('hahah') axios.get('/list').then(res => { console.log(res) }) }) document.body.appendChild(div)
在寫一個本地啟動的服務端程式碼
const express = require('express') const app = express() app.get('/api/list', (req, res) => { res.json({ success: true }) }) app.listen(8888, () => { console.log('listening localhost:8888') })
執行npm run server
命令,瀏覽器會自動開啟頁面。點選div後,會發起請求。
瀏覽器提示http://localhost:8080/api/list 404 (Not Found)
,表示該介面不存在。
因為我們webpack
啟動靜態資源伺服器預設埠為8080,所以他求會直接請求到8080的/api/list介面。所以會提示找不到該介面。
為了解決這個問題,我們就需要將該請求從8080埠代理到8888埠(也就是我們自己本地啟動的服務)
配置webpack.config.js
這裡我只展示devServer
程式碼
// webpack.config.js devServer: { contentBase: './dist', open: true, hot: true, proxy: { '/api': { target: 'http://localhost:8888' } } },
配置devServer
的proxy
欄位的引數,將請求/api
開頭的請求都轉發到http://localhost:8888
,
通過這個方法可以解決一開始提到的本地開發的時候,只想把部分介面轉發到某臺部署新需求的伺服器上。比如當你這個專案請求很多,不同介面部署在不同的埠或者不同的伺服器上。那麼就可以通過配置第一個路徑 ,來區分不同的模組。並轉發到不同的服務上。如:
// webpack.config.js devServer: { contentBase: './dist', open: true, hot: true, proxy: { '/module1': { target: 'http://localhost:8887' }, '/module2': { target: 'http://localhost:8888' }, '/module3': { target: 'http://localhost:8889' } } },
當你要代理到某個https的介面上,就需要設定secure: false
// webpack.config.js devServer: { proxy: { '/api': { target: 'https://other-server.example.com', secure: false } } }
target: '', // 代理的目標地址 secure: false, // 請求https的需要設定 changeOrigin: true,// 跨域的時候需要設定 headers: { host: 'http://www.baidu.com', //修改請求域名 cookie: '' } ...
其他關於devServer
的配置詳見devServer
WebpackDevServer解決單頁面路由404問題
相信大家都是開發過vue或者react單頁面帶路由的應用。這裡就忽略業務程式碼,介紹下devServer
的historyApiFallback
引數
devServer: { historyApiFallback: true, // 當設定為true時,切換路由就不會出現路由404的問題了 }
詳見historyApiFallback
eslint
安裝eslint
:npm i eslint -D
目錄下新建.eslintrc.json
檔案。
environment
: 指定指令碼的執行環境
globals
: 指令碼在執行期間訪問的額外全域性變數。
rules
: 啟動的規則及其各自的錯誤級別。
解析器選項
: 解析器選項
編輯你的eslint
的規則
{ "parserOptions": { "ecmaVersion": 6, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, "rules": { "semi": 2 } }
vscode
安裝eslint
外掛。
配置下webpack.config.js
配置。
... devServer: { overlay: true, contentBase: './dist', hot: true, open: true }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: ['eslint-loader'] }] } ...
eslint-loader
是用於檢查js
程式碼是否符合eslint
規則。
這裡devServer
中的overlay
的作用是,當你eslint報錯的時候,頁面會有個報錯蒙層。這樣就不侷限於編輯器(vscode)的報錯提醒了。
如果js程式碼使用了多個loader,那麼eslint-loader一定要寫在最右邊。如果不寫在最後一個的話,需在裡面新增enforce: "pre"
,這樣不管寫在哪個位置都會優先使用eslint-loader
校驗下程式碼規範。
{ loader: 'eslint-loader', options: { enforce: "pre", } }
提升webpack
打包速度的方法
1. 跟上技術的迭代
-
升級
webpack
版本node
版本npm
等版本
2. 儘可能少的模組上應用loader
-
include
exclude
3. 儘可能少的使用plugin
4.resolve
resolve: { extensions: ['.js'], alias: { 'src': path.resolve(__dirname, '../src') } }
extensions
: 可以讓你import模組的時候不寫格式,當你不寫格式的時候,webpack會預設通過extensions中的格式去相應的資料夾中找
alias
:當你import
的路徑很長的時候,最好使用別名,能簡化你的路徑。
比如:import index.js from '../src/a/b/c/index.js'
設定別名:
resolve: { alias: { '@c': path.resolve(__dirname, '../src/a/b/c') } }
這樣你的import
匯入程式碼就可以改成import index.js from '@c/index.js'
5. dllPlugin
我們先記錄下不使用dll
打包時間787ms
:
Time: 787ms Built at: 2019-05-04 18:32:29 AssetSizeChunksChunk Names bundle.js861 KiBmain[emitted]main index.html396 bytes[emitted]
接下來我們就嘗試使用dll
技術
我們先配置一個用於打包dll
檔案的webpack
配置檔案,生成打包後的js
檔案與描述動態連結庫的manifest.json
// webpack.dll.config.js const path = require('path') const webpack = require('webpack') module.exports = { entry: { vendor: ['jquery', 'lodash'] // 要打包進vendor的第三方庫 }, output: { filename: '[name].dll.js', // 打包後的檔名 path: path.resolve(__dirname, './dll'), // 打包後儲存的位置 library: '[name]_[hash]' // 掛載到全域性變數的變數名,這裡要注意 這裡的library一定要與DllPlugin中的name一致 }, plugins: [ new webpack.DllPlugin({ // 用於打包出一個個單獨的動態連結庫檔案 name: '[name]_[hash]', // 引用output打包出的模組變數名,切記這裡必須與output.library一致 path: path.join(__dirname, './dll', '[name].manifest.json') // 描述動態連結庫的 manifest.json 檔案輸出時的檔名稱 }) ] }
重點:這裡引入的DllPlugin外掛,該外掛將生成一個manifest.json檔案,該檔案供webpack.config.js中加入的DllReferencePlugin使用,使我們所編寫的原始檔能正確地訪問到我們所需要的靜態資源(執行時依賴包)。
配置下package.json
檔案的scripts
:"build:dll": "webpack --config webpack.dll.config.js"
執行下npm run build:dll
Time: 548ms Built at: 2019-05-04 18:54:09 AssetSizeChunksChunk Names vendor.dll.js157 KiB0[emitted]vendor
除了打包出dll
檔案之外,還得再主webpack
配置檔案中引入。這裡就需要使用到DllReferencePlugin
。具體配置如下:
// webpack.config.js new webpack.DllReferencePlugin({ manifest: require('./dll/vendor.manifest.json') }),
這裡的manifest
:需要配置的是你dllPlugin
打包出來的manifest.json
檔案。讓主webpack
配置檔案通過這個
描述動態連結庫manifest.json
檔案,讓js
匯入該模組的時候,直接引用dll
資料夾中打包好的模組。
看似都配置好了,接下來執行下命令npm run build
使用dll
打包後時間:
Time: 471ms Built at: 2019-05-04 18:19:49 AssetSizeChunksChunk Names bundle.js6.43 KiBmain[emitted]main index.html182 bytes[emitted]
直接從最開始的787ms
降低到471ms
,當你抽離的第三方模組越多,這個效果就越明顯。
瀏覽器跑下html
頁面,會報錯
Uncaught ReferenceError: vendor_e406fbc5b0a0acb4f4e9 is not defined
這是因為index.html
還需要通過script
標籤引入這個dll
打包出來的js
檔案
我們如果每次自己手動引入的話會比較麻煩,如果dll
檔案非常多的話,就難以想象了。
這個時候就需要藉助add-asset-html-webpack-plugin
這個包了。
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin') new AddAssetHtmlPlugin({ filepath: path.resolve(__dirname, './dll/vendor.dll.js') })
通過這包,webpack
會將dll
打包出來的js
檔案通過script
標籤引入到index.html
檔案中
這個時候你在npm run build
,訪問下頁面就正常了
6. 控制包檔案大小
tree-shaking 等
7. thread-loader parallel-webpack happypack等多程序打包
8. 合理使用sourceMap
9. 結合stats分析打包結果
藉助線上或者本地打包分析工具
10. 開發環境記憶體編譯
開發環境的時候不會生成dist資料夾,會直接從記憶體中讀取,因為記憶體讀取比硬碟讀取快