WebPack學習實踐筆記(一)

一、準備

nodejs安裝教程:https://blog.csdn.net/FED_AF/article/details/105747632

二、安裝

(1)全局安裝

:: 初始化npm
C:\Users\Administrator> npm init -y
package name: (administrator) webpack_test
::後面全部回車默認
::全局安裝(webpack版本4.43.0,webpack-cli版本3.3.11)
C:\Users\Administrator> npm i webpack webpack-cli -g
::提供sw代碼運行的服務器環境
C:\Users\Administrator> npm i serve -g

(2)項目安裝

:: 初始化npm
項目根目錄> npm init -y
package name: (webpack_test) 名稱自己取,只要不和全局的一樣就行
::後面全部回車默認
::安裝webpack和webpack-cli
項目根目錄> npm i webpack webpack-cli -D

(3)模塊和插件安裝

::安裝webpack-dev-server
項目根目錄> npm i webpack-dev-server -D
::用來打包css
項目根目錄> npm i css-loader style-loader -D
::用來打包sass(這裏是需要裝css-loader和style-loader的,但前面已經裝過就不再裝)
項目根目錄> npm i sass sass-loader -D
::用來打包html
項目根目錄> npm i html-webpack-plugin -D
::用來打包css引入的圖片
項目根目錄> npm i url-loader file-loader -D
::用來打包html的img元素引入的圖片
項目根目錄> npm i html-loader -D


::用來提取css成單獨文件
項目根目錄> npm i mini-css-extract-plugin -D
::用來處理css兼容性
項目根目錄> npm i postcss-loader postcss-preset-env -D
::用來壓縮css代碼
項目根目錄> npm i optimize-css-assets-webpack-plugin -D
::用來檢查規範js代碼(airbnb風格)
項目根目錄> npm i eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import -D
::用來處理js代碼的兼容性
項目根目錄> npm i babel-loader @babel/preset-env @babel/core @babel/polyfill core-js -D
::用來實現PWA技術
項目根目錄> npm i workbox-webpack-plugin serve -D
::用來多進程打包
項目根目錄> npm i thread-loader -D
::用來配合dll使用,功能是將某個文件打包輸出去,並在html中自動引入
項目根目錄> npm i add-asset-html-webpack-plugin -D
::優化生產環境代碼壓縮
項目根目錄> npm i terser-webpack-plugin -D

(4)第三方庫安裝

::jquery
項目根目錄> npm i jquery -D

三、結構

項目根目錄
    源代碼目錄src
    構建後代碼目錄build
    webpack.config.js

四、配置

  • 項目根目錄下的webpack.dll.js(初始化動態鏈接庫):

/*
	使用dll技術,對某些庫(第三方庫:jquery、vue)進行單獨打包
*/
const {resolve} = require('path');
const webpack = require('webpack');
module.exports = {
	entry: {
		// 最終打包生成的[name]
		// ['jquery']要打包的庫是jquery
		jquery: ['jquery']
	},
	output: {
		filename: '[name].js',
		path: resolve(__dirname, 'dll'),
		// 打包的庫向外暴露出去的內容叫什麼名字
		library: '[name]_[hash]',
	},
	plugins: [
		// 打包生成一個manifest.json,提供和jquery的映射關係
		new webpack.DllPlugin({
			// 映射庫的暴露的內容名稱
			name: '[name]_[hash]',
			// 輸出文件路徑
			path: resolve(__dirname, 'dll/manifest.json')
		})
	],
	mode: 'production'
};
  • 項目根目錄下的webpack.dev.js(開發環境):

// resolve用來拼接絕對路徑的方法
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
/*
	樣式文件可以使用HMR功能,因爲style-loader內部實現了
	js文件默認不能使用HMR功能
	解決:需要修改js代碼,添加支持HMR功能的代碼,只能處理非入口js文件的其他文件
	html文件默認不能使用HMR功能(html不存在引用問題,沒有優化的空間),同時會導致修改html文件後頁面刷新無果
	解決:修改entry入口,將html文件引入
 */
// 設置node環境變量
process.env.NODE_ENV = 'development';
module.exports = { // webpack配置
	// 入口起點
	entry: ['./src/index.js', './src/index.html'],
	// 輸出
	output: {
		// 輸出文件名
		filename: 'built.js',
		// 輸出路徑
		// __dirname是nodejs的變量,代表當前文件的目錄絕對路徑
		path: resolve(__dirname, 'build')
	},
	// loader的配置
	module: {
		rules: [
            // 詳細loader配置
            oneOf: [
				{
					// 匹配哪些文件
					test: /\.css$/,
					// 使用哪些loader進行處理,多個loader用use數組
					  use: [
						// use數組中loader的執行順序:從右到左,從下到上,依次執行
						// 創建style標籤,將js中的樣式資源插入進去,添加到head中生效
						'style-loader',
						// 將css文件變成commonjs模塊加載到js中,裏面的內容是樣式字符串
						'css-loader'
					]
				},
				{
					test: /\.sass$/,
					use: [
						'style-loader',
						'css-loader',
						// 將sass文件編譯成css文件
						'sass-loader',
						'sass'
					]
				},
				{
					// 處理圖片資源
					test: /\.(jpg|png|gif)$/,
					// 使用一個loader,直接用loader
					// 需要下載url-loader和file-loader
					loader: 'url-loader',
					options: {
						// 當發現圖片大小小於8kb,就會被base64處理,轉化爲字符串
						// 優點:能夠減少請求數量,減輕服務器壓力
						// 缺點:圖片體積會更大,文件請求速度更慢
						limit: 8 * 1024,
						// 問題:因爲url-loader默認使用es6模塊化解析,而html-loader引入圖片是commonjs
						// 解析時會出現問題:[object Module]
						// 解決:關閉url-loader的es6模塊化,使用commonjs解析
						esModule: false
					}
				},
				{
					// 處理圖片資源
					test: /\.html$/,
					// 處理html文件的img圖片(負責引入img,從而能被url-loader處理)
					// 需要下載html-loader
					loader: 'html-loader'
				},
				{
					// 處理其他資源
					exclude: /\.(css|js|html|jpg|png|gif|sass|json)$/,
					loader: 'file-loader',
					options: {
						// 限制輸出的文件名哈希值長度爲10位,在加上本來的擴展名
						name: '[hash:10].[ext]'
					}
				}
			]	
		]
	},
	// plugins的配置
	plugins: [
		// 詳細plugins的配置
		// html-webpack-plugin
		// 功能:默認會創建一個空的HTML,自動引入打包輸出的所有資源(JS/CSS)
		// 需求:需要有結構的HTML文件
		new HtmlWebpackPlugin({
			// 將 './src/icons/icon.ico'文件作爲網頁標籤的圖標引入
			// favicon: './src/icons/icon.ico',
			// 複製 './src/index.html'文件,並自動引入打包輸出的所有資源
			// 多個html需要多個HtmlWebpackPlugin,通過filename指定構建後的html路徑和文件名
			template: './src/index.html'
		}),
		new HtmlWebpackPlugin({
			template: './src/inde.html',
			filename: './build/c/inde.html'
		}),
		// 告訴webpack哪些庫不參與打包,同時使用時的名稱也得改
		new webpack.DllReferencePlugin({
			manifest: resolve(__dirname, 'dll/manifest.json')
		}),
		// 將某個文件打包輸出去,並在html中自動引入
		new AddAssetHtmlWebpackPlugin({
			filepath: resolve(__dirname, 'dll/jquery.js')
		})
	],
	// 模式
	// mode: 'production'
	mode: 'development',
	// 開發服務器devServer:用來自動化(自動編譯,自動打開瀏覽器,自動刷新瀏覽器)
	// 特點:只會在內存中編譯打包,不會有任何輸出
	devServer: {
		contentBase: resolve(__dirname, 'build'),
		// 啓動gzip壓縮
		compress: true,
		// 自動打開瀏覽器
		// open: true,
		// 按如下配置即可在其他終端訪問
		host: '0.0.0.0',
		// 開啓熱模塊替換(HMR功能),省去不必要刷新
		hot: true,
		// 端口號
		port: 3000
	},
	devtool: "eval-source-map"
};
/*
	source-map是一種提供源代碼到構建後代碼映射的技術。用法如下:
	[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
	source-map:外部
		會提示錯誤代碼準確信息和源代碼的錯誤位置
	inline-source-map:內聯,將source-map嵌入到入口js中,構建速度更快
		會提示錯誤代碼準確信息和源代碼的錯誤位置
	hidden-source-map:外部,將source-map輸出到一個map文件中
		會提示錯誤代碼錯誤原因但是沒有錯誤位置,而且只能提示構建後代碼的錯誤位置
	eval-source-map:內聯,每一個文件都生成對應的source-map
		會提示錯誤代碼準確信息和源代碼的錯誤位置(只多了一個js文件的hash值)
	nosources-source-map:外部
		會提示錯誤代碼準確信息,但是沒有任何源代碼信息
	cheap-source-map:外部
		會提示錯誤代碼準確信息,但只能定位到行
	cheap-module-source-map:外部
		會提示錯誤代碼準確信息,但只能定位到行


	速度快:eval-cheap-source-map>eval-source-map>inline-source-map>cheap-source-map>...
	調試友好:source-map>cheap-module-source-map>cheap-source-map>...

	開發環境通常選擇eval-source-map或eval-cheap-module-source-map
	生產環境通常選擇source-map或cheap-module-source-map

 */
  • 項目根目錄下的webpack.pro.js(生產環境):

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
/*
	緩存:
	babel緩存:讓第二次打包構建速度更快

	文件資源緩存:讓代碼上線運行更好使用。
	每次webpack構建時會生成一個唯一的hash值加入到文件名中,以此保證當文件修改時刷新緩存
	使用contenthash保證只對更改過的文件重新生成hash值
*/
/*
	tree shaking: 去除無用代碼
	前提: 1.必須使用ES6模塊化  2.開啓production環境
	作用:減少代碼體積
	在packaage.json中配置"sideEffects": ["*.css"]表示除css文件外所有代碼都進行tree shaking
*/
/*
	code split: 代碼分割
	將較大的js文件分割成多個較小的js文件,從而實現並行加載,速度更快
*/
/*
	PWA:漸進式網絡開發應用程序(離線可訪問)
	通過workbox實現PWA
 */
// 複用loader
const commonCssLoader = [
					// 提取js中的css成單獨文件
					MiniCssExtractPlugin.loader,
					'css-loader',
					// css兼容性處理:postcss --> postcss-loader postcss-preset-env
					// 幫postcss找到package.json中browserslist裏面的配置,通過配置加載指定的css兼容性樣式
					/*	"browserslist": {
						// 開發環境 --> 設置node環境變量:process.env.NODE_ENV = development
						"development": [
						"last 1 chrome version",//兼容谷歌瀏覽器最近的一個版本
						"last 1 firefox version",
						"last 1 safari version"
						],
						// 生產環境:默認是生產環境
						"production": [
						">0.2%",//考慮99.8%的瀏覽器的兼容性
						"not dead",//不考慮不再使用的瀏覽器的兼容性
						"not op_mini all"//不考慮op_mini瀏覽器的全部版本的兼容性
						]
					}*/
					// 使用loader的默認配置
					// 'postcss-loader',
					// 修改loader的默認配置
					{
						loader: 'postcss-loader',
						options: {
							ident: 'postcss',
							plugins: () => [
								// postcss的插件
								require('postcss-preset-env')()
							]
						}
					}

];
// 設置node環境變量
// process.env.NODE_ENV = 'development';
module.exports = { 
	entry: './src/index.js',
	output: {
		filename: 'built.[contenthash:10].js',
		path: resolve(__dirname, 'build')
	},
	module: {
		rules: [
			/*{
				// 用eslint做js語法檢查
				// 只檢查自己寫的源代碼,第三方的庫是不用檢查的
				// 在package.json中的eslintConfig設置檢查規則:
				// 使用airbnb風格
				test: /\.js$/,
				// 排除node模塊
				exclude: /node_modules/,
				// 優先執行
				enforce: 'pre',
				loader: 'eslint-loader',
				options: {
					// 自動規範源js的語法
					fix: true
				}
			},*/
			{
				// 以下loader只會匹配一個,造成衝突的可以放到oneOf外面
				oneOf: [
					{
						test: /\.css$/,
						use: [...commonCssLoader]
					},
					{
						test: /\.sass$/,
						use: [...commonCssLoader, 'sass-loader', 'sass']
					},
					{
						test: /\.(jpg|png|gif)$/,
						loader: 'url-loader',
						options: {
							limit: 8 * 1024,
							name: '[hash:10].[ext]',
							outputPage: 'images',
							esModule: false
						}
					},
					{
						test: /\.html$/,
						loader: 'html-loader'
					},
					{
						exclude: /\.(css|js|html|jpg|png|gif|sass|json|ico)$/,
						loader: 'file-loader',
						options: {
							outputPage: 'media',
							name: '[hash:10].[ext]'
						}
					},
					{
						// 用babel做js兼容性處理
						test: /\.js$/,
						exclude: /node_modules/,
						use: [
							// 開啓多進程打包。
							// 進程啓動時間大概爲600ms,進程通信也有開銷。
							// 只有工作消耗時間比較長,才需要多進程打包
							{

								loader: 'thread-loader',
								options: {
									workers: 2 // 進程2個
								}
							},
							{
								loader: 'babel-loader',
								options: {
									// 預設:指示babel做怎麼樣的兼容性處理
									// @babel/preset-env:基本兼容性處理:只能轉換基本語法,不能轉換promise等
									// @babel/polyfill:全部兼容性處理(在入口js文件中引入即可:import '@babel/polyfill';):所有兼容性代碼全部引入,代碼體積太大
									// core-js:按需處理兼容性
									presets: [
										[
											'@babel/preset-env',
											{
												// 按需加載
												useBuiltIns: 'usage',
												corejs: {
													// 指定core-js的版本
													version: 3
												},
												// 指定兼容性做到哪個版本瀏覽器
												targets: {
													chrome: '60',
													firefox: '60',
													ie: '8',
													safari: '10',
													edge: '17'
												}
											}
										]
									],
									// 開啓babel緩存
									// 第二次構建時,會讀取之前的緩存
									cacheDirectory: true
								}
							}
						]
					}
				]
			}
		]
	},
	plugins: [
		new HtmlWebpackPlugin({
			// favicon: './src/icons/icon.ico',
			template: './src/index.html',
			// 壓縮html代碼
			minify: {
				// 移除空格
				collapseWhitespace: true,
				// 移除註釋
				removeComments: true
			}
		}),
		new MiniCssExtractPlugin({
			// 對輸出的文件重命名
			filename: 'css/main.[contenthash:10].css'
		}),
		// 壓縮css
		new OptimizeCssAssetsWebpackPlugin(),
		// 壓縮css
		new WorkboxWebpackPlugin.GenerateSW({
			// 幫助serviceworker快速啓動
			// 刪除舊的serviceworker
			// 生成一個serviceworker配置文件
			clientsClaim: true,
			skipWaiting: true,
		}),
		// 告訴webpack哪些庫不參與打包,同時使用時的名稱也得改
		new webpack.DllReferencePlugin({
			manifest: resolve(__dirname, 'dll/manifest.json')
		}),
		// 將某個文件打包輸出去,並在html中自動引入
		new AddAssetHtmlWebpackPlugin({
			filepath: resolve(__dirname, 'dll/jquery.js')
		})
	],
	// 對於單頁面應用,可以將node_module中代碼單獨打包一個chunk最終輸出
	// 對於多頁面應用,可以自動分析多入口trunk中有沒有公共的文件。如果有會打包成單獨一個chunk
	optimization: {
		splitChunks: {
			chunks: 'all'
		}
	},
	mode: 'production',
	// 下面這一行不是註釋,是忽略eslint報的no-console警告
	// eslint-disable-next-line
	// mode: 'development',
	devtool: "source-map",
	externals: {
		// 忽略庫名 -- npm包名
		jquery: 'jQuery'
	}
};
  • 項目根目錄下的package.json:

{
  "name": "webpack_code",
  "version": "1.0.0",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "@babel/core": "^7.9.6",
    "@babel/polyfill": "^7.8.7",
    "@babel/preset-env": "^7.9.6",
    "add-asset-html-webpack-plugin": "^3.1.3",
    "babel-loader": "^8.1.0",
    "core-js": "^3.6.5",
    "css-loader": "^3.5.3",
    "eslint": "^7.0.0",
    "eslint-config-airbnb-base": "^14.1.0",
    "eslint-loader": "^4.0.2",
    "eslint-plugin-import": "^2.20.2",
    "file-loader": "^6.0.0",
    "html-loader": "^1.1.0",
    "html-webpack-plugin": "^4.3.0",
    "jquery": "^3.5.1",
    "mini-css-extract-plugin": "^0.9.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.7.0",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2",
    "serve": "^11.3.0",
    "style-loader": "^1.2.1",
    "thread-loader": "^2.1.3",
    "url-loader": "^4.1.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.11.0",
    "workbox-webpack-plugin": "^5.1.3"
  },
  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  },
  "sideEffects": [
    "*.css",
    "*.sass"
  ]
}
  • 項目根目錄下的server.js:

/*
	服務器代碼
	啓動服務器指令:node server.js
*/
const express = require('express');
const app = express();
// 緩存保留時長
app.use(express.static('build', {maxAge: 1000 * 3600}));
// 監聽3000端口
app.listen(3000);
  • 項目入口js:

...
import './print.js';
...

//熱模塊替換
if (module.hot) { // 用於開發環境
	// 一旦module.hot爲true,說明開啓了HMR功能
		/* 
			1、accept方法會監聽print.js的變化,一旦發生變化,默認不會重新打包構建其他js,而是會執行後面的回調函數
			2、每引入一個(自己寫的)js,就要在if裏面加一個用來監聽的accept函數
		 */
	module.hot.accept('./print.js', function() {})
}

//代碼分割、懶加載、預加載
用於生產環境,通過import動態導入語法能將某個文件單獨打包成一個chunk
//通過/* webpackChunkName: 'test' */將輸出的chunk設置爲固定的名字
// 正常加載是並行加載(同時加載多個文件,文件越多加載速度越慢)
// 懶加載:當文件需要使用時才加載
// 預加載 prefetch:等其他資源加載完畢,瀏覽器空閒了,再偷偷加載資源
// 懶加載或預加載都建立在代碼分割的基礎上
// 但是下面的語法eslint會報錯,提示你import語句只能放在頂部,解決的方法,要麼打包時關掉eslint,要麼使用webpack.pro.js中的optimization實現按需加載
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
	console.log(mul(4, 5));
});

/*
	eslint不認識window、navigator全局變量
	解決:需要修改package.json中eslintConfig配置
    "env": { 
      "browser": true // 支持瀏覽器端全局變量
    }
    sw代碼必須運行在服務器上
	npm i serve -g
	serve -s build	啓動服務器,將build目錄下所有資源作爲靜態資源暴露出去
	sw只能用https訪問,本地除外
 */
// 註冊serviceWorker
// 處理兼容性問題
if ('serviceWorker' in navigator) {
	window.addEventListener('load', () => {
		navigator.serviceWorker.register('/service-worker.js').then(() => {
			console.log('註冊成功');
		}).catch(() => {
			console.log('註冊失敗');

		});
	});
}

五、性能優化

1、開發環境性能優化

側重:

(1)優化打包構建速度

 - HMR

 - oneOf

(2)優化代碼調試

 - source-map

2、生產環境性能優化

側重:

(1)優化打包構建速度

 - oneOf

 - babel緩存

 - 多進程打包

(2)優化代碼運行的性能

 - 文件資源緩存(hash-chunkhash-contenthash)

 - tree shaking

 - code split

 - 懶加載/預加載

 - pwa

 - externals

 - dll

六、使用

  • 手動打包

進入項目根目錄,執行webpack命令,默認會將webpack.config.js作爲配置文件,開發環境中,可以將webpack.dev,js複製爲webpack.config.js。生產環境同理,並不是不能直接指定或修改配置文件,只是這樣來得快。對於某些第三方庫,可以先使用webpack.dll.js打包,以後每次打包再用webpack.pro.js

  • 開發環境實時監聽打包

devServer配置:見webpack.dev.js

進入項目根目錄,執行npx webpack-dev-server命令

  • 生產環境監聽

服務器配置:見server.js

進入項目根目錄,執行node server.js命令(只是創建了服務器,並不能自動打包和刷新頁面)

運行servicework的服務器啓動命令:serve -s build

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章