這是我花了幾個星期學習webpack4的學習筆記。內容不夠細,由於一些相對比較簡單的,就隨意帶過了。但願文章能給你們帶來幫助。若有錯誤,但願及時指出。例子都在learn-webpack倉庫上。若是你從中有所收穫的話,但願你能給個人github
點個star
。javascript
當你要開發第三方庫供別人使用時,就須要用到library
和libraryTarget
這兩個配置了。html
library
vue
output: { filename: 'library.js', library: 'library', libraryTarget: 'umd' },
library
: 當配置了這個library
參數後,會把library
這個key
對應的value
即上面代碼library
掛載到全局做用域中。html
用script
標籤引入,能夠經過訪問全局變量library
訪問到咱們本身開發的庫。java
libraryTarget
:這個默認值爲var
。意思就是讓library定義的變量掛載到全局做用域下。固然還有瀏覽器環境的window
,node
環境的global
,umd
等。當設置了window
、global
,library
就會掛載到這兩個對象上。當配置了umd
後,你就能夠經過import
,require
等方式引入了。node
externals
react
exterals
是開發公共庫很重要的一個配置。當你的公共庫引入了第三方庫的時候,公共庫會把該第三方庫也打包進你的模塊裏。當使用者也引入了這個第三方庫後,這個時候打包就會又打了一份第三方庫進來。jquery
所在在公共模塊庫中配置以下代碼webpack
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 _
。ios
本人作了個試驗,當本身開發的包不引入lodash
,業務代碼中也不引入lodash
,那麼打包業務代碼的時候,webpack
會把lodash
打進你業務代碼包裏。git
固然externals
,配置還有多種寫法,以下
externals: { lodash: { commonjs: 'lodash', commonjs2: 'lodash', amd: 'lodash', root: '_' } } externals: ['lodash', 'jquery'] externals: 'lodash'
具體請參考官網externals
學了上面的配置後,就須要學習下如何將本身的包發佈到npm
倉庫上了。
package.json
的入口要改爲dist
目錄下的js文件如: "main": "./dist/library.js"
npm login
進行登陸,或者npm adduser
添加帳號npm publish
當出現以下提示表明發佈成功
// 當出現相似以下代碼時,表示你已經發布成功 ➜ library git:(master) ✗ npm publish + atie-first-module-library@1.0.0
遇到的問題:
當你遇到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/
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離線緩存成功。
使用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
相信你們都是開發過vue或者react單頁面帶路由的應用。這裏就忽略業務代碼,介紹下devServer
的historyApiFallback
參數
devServer: { historyApiFallback: true, // 當設置爲true時,切換路由就不會出現路由404的問題了 }
安裝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
打包速度的方法webpack
版本 node
版本 npm
等版本loader
include
exclude
plugin
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'
咱們先記錄下不使用dll
打包時間787ms
:
Time: 787ms Built at: 2019-05-04 18:32:29 Asset Size Chunks Chunk Names bundle.js 861 KiB main [emitted] main index.html 396 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 Asset Size Chunks Chunk Names vendor.dll.js 157 KiB 0 [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 Asset Size Chunks Chunk Names bundle.js 6.43 KiB main [emitted] main index.html 182 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
,訪問下頁面就正常了
tree-shaking 等
藉助線上或者本地打包分析工具
開發環境的時候不會生成dist文件夾,會直接從內存中讀取,由於內存讀取比硬盤讀取快