深刻淺出Webpack

核心概念

  • Entry:入口,Webpack執行構建的第一步將從Entry開始,可抽象成輸入。
  • Module:模塊,在Webpack裏一切皆模塊,一個模塊對應一個文件。Webpack會從配置的Entry開始遞歸找出全部依賴的模塊。
  • Chunk:代碼塊,一個Chunk由多個模塊組合而成,用於代碼合併與分割。
  • Loader:模塊轉換器,用於將模塊的原內容按照需求轉換成新內容。
  • Plugin:拓展插件,在wp構建流程中的特定時機注入拓展邏輯。
  • Output:輸出結果,在Webpack通過一系列處理並得出最終想要的代碼後輸出結果.

Entry

入口,Webpack執行構建的第一步將從Entry開始,可抽象成輸入。css

單頁面單入口文件配置

module.exports = {
	entry: './path/to/my/entry/file.js'
}
複製代碼

多頁面多入口文件配置

module.exports = {
	entry: {
		one: './path/one.js', 
		two: './path/two.js'
	},
	plugins:[
		new HtmlWebpackPlugin({
			title:'第一個頁面',
			template: './pages/one.html', // 指定第一個頁面的模板
			filename: './pages/one.html', // 指定第一個頁面打包完成後的文件名
			chunks: ['one','two'] // 指定第一個頁面要打包進入的js
		}),	
		new HtmlWebpackPlugin({
			title:'第二個頁面',
			template: './pages/two.html', // 指定第二個頁面的模板
			filename: './pages/two.html', // 指定第二個頁面打包完成後的文件名
			chunks: ['one','two'] // 指定第二個頁面要打包進入的js
		}),	
	]
}
複製代碼

Webpack構建單頁面和多頁面的實例

網上關於單頁面和多頁面的優勢和缺點都有比較詳細的描述,具體須要應用單頁面仍是多頁面得根據項目的需求來選擇。html

通常單頁面的配置都有相應的腳手架,好比vue-cli,集成了wp,減小了配置webpack的不少繁瑣的工做vue

多頁面應用如今沒有腳手架,能夠進行配置,具體的實例可參考這篇:node

juejin.im/post/5a2257…react

多頁面想向單頁面實現組件共用和封裝,能夠參考這篇進行配置,須要引入ejs模板,參考這篇:jquery

juejin.im/post/5a35f6…webpack

Module

模塊,在Webpack裏一切皆模塊,一個模塊對應一個文件。Webpack會從配置的Entry開始遞歸找出全部依賴的模塊。 ####配置Loaderweb

  • test:匹配要進行轉換的文件,使用正則表達式來匹配。正則表達式

  • include: 只包含指定目錄的文件進行轉換,加快webpack的編譯速度。vue-cli

  • exclude: 排除某個文件的轉換,加快編譯和搜索速度。

  • use:對use後面加參數,好比進行緩存和壓縮,也能夠加快編譯的速度。

    module:{
      	rules: [
      		{
      			//解析js文件
      			test: /\.js$/,
      			// 用babel-loader轉換js文件
      			 // ?cacheDirectory表示傳給babel-loader的參數,用於緩存babel的編譯結果,加快從新編譯的速度
      			use: ['babel-loader?cacheDirectory']
      			 // 只命中src目錄裏的Js文件,加快webpack的編譯速度
      			 include: path.resolve(_dirname,'src')
      		},
      		{
      			//解析Scss文件
      			test: /\.scss$/,
      			// 使用一組loader去處理scss文件
      			// 處理順序爲從後到前,即先交給scss-loader處理,再將結果交給css-loader,最後交給style-loader
      			use: ['style-loader','css-loader','sass-loader'],
      			// 排除node_modules目錄下的文件
      			exclude: path.resolve(__dirname,'node_modules')
      		},
      		{
      			// 對非文本文件採用file-loader加載
      			test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
      			use: ['file-loader']
      		}
      	]
      }
    複製代碼

在上面的例子中,test、include、exclude只傳入了一個字符串或正則,其實他們也支持數組類型

{
	test:[
		/\.jsx?$/,
		/\.tsx?$/
	],
	include:[
		path.resolve(__dirname,'src'),
		path.resolve(__dirname,'tests')
	],
	exclude:[
		path.resolve(__dirname, 'node_modiles'),
		path.resolve(__dirname, 'bower_modules')
	]
}
複製代碼

noParse

noPaese配置項可讓Webpack忽略對部分沒采用模塊化的文件的遞歸解析和處理,這樣作的好處是能提升構建性能。 緣由是一些庫如jQuery,ChartJS龐大又沒有采用模塊化的標準,讓Webpack去解析這些文件既耗時又沒有意義。

noParse: /jquery|chartjs/
複製代碼
使用函數,從Webpack3.0.0開始支持
noParse: (content) => {
	//content表明一個模塊的文件路徑
	//返回true或false
	return /jquery|chartjs/.test(content)
}
複製代碼

parse

由於Webpack是以模塊化的js文件爲入口的,因此內置了對模塊化js的解析功能,支持AMD,CommonJS,SystemJS,ES6 parse屬性能夠更細粒度地配置哪些模塊語法被解析,哪些不被解析。

同noParse配置項的區別在於.parser能夠精確到語法層面,而noParse只能控制哪些文件不被解析。

parse的使用方法以下:

modele:{
	rules:[
		test: /\.js$/,
		use: ['babel-loader'],
		parser: {
			amd: false, //禁用AMD
			commonjs: false, // 禁用CommonJS
			system: false, // 禁用 SystemJS
			harmony: false, // 禁用ES6 import/export
			requireInclude: false, // 禁用requireInclude
			requireEnsure: false, // 禁用requireEnsure
			requireContext: false, // 禁用requireContext
			browserify: false,  // 禁用browserify
			requireJs: false // 禁用requirejs
		}
	]
}
複製代碼

Loader

模塊轉換器,用於將模塊的原內容按照需求轉換成新內容。

  • Loader的執行順序是由後到前的。

  • 每Loader均可以經過URL querystring的方式傳入參數,例如 css-loader?minimize中的minimize告訴css-loader要開啓css壓縮。

  • 向loader中傳入屬性的方式除了能夠經過querystring實現,還能夠經過object實現。

    user:[
      	'style-loader',{
      		loader:'css-loader',
      		options:{
      			minimize:true
      		}
      	}
      ]
    複製代碼

在Loader須要傳入不少參數時,咱們還能夠經過一個Object來描述,例如在上面的babel-loader配置中有以下代碼

use:[
	{
		loader:'babel-loader',
		options:{
			cacheDirectory:true
		},
		// enforce: 'post'的含義是將該loader的執行順序放到最後
		// enforce: 'pre'的含義是將loader的執行順序放到最前面
	}
]
複製代碼

Plugin

拓展插件,在Webpack構建流程中的特定時機注入拓展邏輯。

Plugin的配置很簡單,plugins的配置項接受一個數組,數組裏的每一項都是一個要使用的Plugin的實例,Plugin須要的參數經過構造函數傳入。

const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin')
module.exports = {
	plugins: [
		// 全部頁面都會用到的公共代碼被提取到common代碼塊中
		new CommonsChunkPlugin({
			name: 'common',
			chunks: ['a','b']
		})
	]
}
複製代碼

將css文件單獨提取出來

const ExtractTextPluginn = require('extract-text-webpack-plugin')

	module:{
		reules:[
			{
				textL /\.css$/,
				loaders: ExtractTextPlugin.extract({
					use:[
						'css-loader'
					]
				}),
			}
		]
	},
	plugins: [
		new ExtractTextPlugin({
			//從.js文件中提取出來的.css文件的名稱
			filename: `[name]_[contenthash:8].css`
		})	
	]
複製代碼

Resolve

webpack在啓動後會從配置的入口模塊出發找出全部依賴的模塊,resolve配置webpack如何查找模塊所對應的文件

alias

resolve.alias配置項經過別名來將原導入路徑映射成一個新的導入路徑。例如使用一下配置:

resolve:{
	alias:{
		components: './src/components'
	}
}
複製代碼

當經過**import Button from 'components/button'**導入時

實際上被alias等價替換成了import Button from './src/components/button'

extensions

在導入語句沒帶文件後綴時,Webpack會自動帶上後綴後去嘗試訪問文件是否存在。

resolve.extensions用於配置在嘗試過程當中用到的後綴列表,默認是:

extensionsL ['.js','.json']
複製代碼

也就是說,當遇到require('./data')這樣的導入語句時,Webpack會先尋找./data.js文件,若是該文件不存在,就去找./data.json文件,若是仍是找不到就報錯。

escriptionFiles

若是resolve.enforceExtension被配置爲true,則全部導入語句都必須帶文件後綴,例如開啓前import './foo'能正常工做,開啓後就必須寫成**import './foo.js' **

Devtool

devltool配置Webpack如何生成source map,默認值是false,即不生成source map,若想構建出的代碼生成source map以方便調試,則能夠這樣配置:

module.export = {
	devtool: 'source-map'
}
複製代碼

開啓source-map會方便咱們開發中的調試,方便咱們定位到具體的代碼問題,當也會影響一下相關的構建性能問題,全部要作出多配置文件,開發環境配置和生產環境配置

source-map模式下會輸出質量最高且最詳細的Source Map,這會形成構建速度緩慢,特別是在開發過程當中須要頻繁修改時會增長等待時間

在Source-Map模式下會將Source Map暴露,若構建發佈到線上的代碼的source map暴力,就等同於源碼被泄露

爲了解決以上兩個問題,能夠這樣作,以下所述

  • 在開發環境下devtool設置成cheap-module-eval-source-map,由於生成這種source map的速度最快,能加速構建。因爲在開發環境下不會作代碼壓縮,因此在source map的即便沒有列信息,也不會影響斷電調試.

  • 在生產環境下將devtool設置成hidden-source-map,意思是生成最詳細的source map,但不會將source map暴露出去。因爲生產環境下會作代碼壓縮,一個js文件只有一行,因此須要列信息。

在生產環境下一般不會將Source Map上傳到http服務器讓用戶獲取,而是上傳到JavaScript錯誤收集系統,在錯誤收集系統上根據Source Map和收集到的JavaScript運行錯誤隊棧,計算出錯誤所在源碼的位置。

不要在生產環境下使用inline模式的Source Map,由於這會使JavaSctipt文件變的很大,並且會泄露源碼。

Externals

External用來告訴Webpack要構建的代碼中使用了哪些不用被打包的模塊,也就是說這些模板是外部環境提供的,Webpack在打包時能夠忽略它們

經過externals能夠告訴Webpack在js運行環境中已經內置了哪些全局變量,不用將這些全局變量打包到代碼中而是直接使用它們。

moudle.export = {
	externals: {
		//將導入語句裏的jquery替換成運行環境裏的全局變量jQuery
		jquery: 'jQuery'
	}
}
複製代碼

Webpack優化

  • 優化開發體驗

    優化開發體驗的目的是提高開發效率,減小每次構建的耗時
      1. 優化構建速度
      2. 優化使用體驗,經過自動化手段完成一些重複的工資哦,讓咱們專一於解決問題自己。
    複製代碼
  • 優化輸出質量

    呈現用戶體驗更好的網頁,減小首屏加載時間,提高性能流暢度。
    
      1. 縮小文件的搜索範圍
      2. 優化Loader的配置,經過include去命中 只有哪些文件去處理,經過exclude去去除哪些文件不須要處理,好比node_module
      3. 優化resolve.modules配置
      resolve.modules用於配置Webpack去哪些目錄下尋找第三方模塊。
      
      resolve.modelus的默認值是['node_modules']含義是先去當前目錄的./node_modules目錄下去找咱們想找的模塊,若是沒找到,就去上一級目錄../node_modules中找,再沒有就去../../node_modules中找,以此類推
      
      當安裝的第三方模塊都放在項目根目錄的./node_modules目錄下時,就沒有必要按照默認的方式去一層層的尋找,能夠指明存放第三方模塊的絕對路徑,以減小尋找,配置以下:
      
      	module.exports = {
      		resolve: {
      			// 使用絕對路徑指明第三方模塊存放的位置,以減小搜索步驟
      			// 其中,__dirname表示當前工做目錄,也就是項目根目錄
      			modules: [path.resolve(__dirname,'node_modules')]
      		}
      	}
      4.優化resolve.alias配置,跳過遞歸解析操做
      
      5.優化resolve.extensions配置減小後綴,後綴要儘量少,提高速度
      
      6.優化noParse配置
    複製代碼

使用DllPlugin

包含大量複用模塊的動態連接庫只需被編譯一次,在以後的構建過程當中被動態連接庫包含的模塊將不會被從新編譯,而是直接使用動態連接庫中的代碼。因爲動態連接庫中大多數包含的是經常使用的第三方模塊,例如react,react-dom,因此只要不升級這些模塊的版本,動態連接庫就不用從新編譯。

Webpack已經內置了對動態連接庫的支持,須要經過如下兩個內置的額插件接入。

  • DllPlugin插件:用於打包粗一個個單獨的鼎泰連接庫文件.
  • DllReferecePlugin插件:用於在主要的配置文件中引入DllPlugin插件打包好的動態連接庫文件

HappyPack

運行在Node.js之上的Webpack是單線程模型,Happy Pack將任務分解給多個子進程去併發執行,子進程處理完後再講結果發送給主進程,因爲js是單線程模型,因此想要發揮多核cpu的功能,就只能經過多進程實現,而沒法經過多線程實現。

整個Webpack構建流程中,最耗時的流程可能就是loader對文件的轉換操做了,由於要轉換的文件數據量巨大,並且這些轉換操做都只能一個一個地處理。HappyPack的核心原理就是將這部分任務分解到多個進程中去並行處理,從而減小總的構事件。

ParallelUglifyPlugin

本來會使用Uglifyjs去一個一個壓縮再輸出

Paralleuglifyplugin會開啓多個子線程,將對多個文件的壓縮工做分配給多個子進程完成,每一個子進程其實仍是經過uglify去壓縮代碼,可是變成了並行執行,因此Paralleuglifyplugin能更快地完成對多個文件的壓縮工做

文件監聽

文件監聽是發現源碼發生變化時,自動從新構建出新的輸出文件.

讓Webpack開啓監聽模式,有以下兩種方式。

  • 在配置文件webpack.config.js中設置watch:true
  • 在執行啓動webpack的命令時帶上--watch參數,完成的命令是webpack--watch

優化文件的性能

忽略node_modules

module.export = {
	watchOptions:{
		ignored: /node_modules/
	}
}
複製代碼

自動刷新瀏覽器

開啓模塊熱替換

##區分環境 區分開發環境和生產環境,指定對用的不一樣調試模式Source Map,是否開啓壓縮,是否提取公共代碼等

提取公共代碼

相同的資源被重複加載,浪費用戶的流量和服務器成本

頁面須要加載的資源太大,會致使網頁首屏加載緩慢,影響用戶體驗。

webpackchunkplugin
複製代碼

代碼分割,按需加載。

如何按需加載

  1. 在爲單頁應用作按需加載優化時,通常採用如下原則

  2. 將整個網站劃分紅一個個小功能,再按照每一個功能的相關程度將它們分紅幾類

  3. 將每一類合併爲一個chunk,按需加載對應的chunk.

  4. 不要按需加載用戶首次打開網站是須要看到的畫面所對應的功能,將其放到執行入口所在的Chunk中,以減小用戶能感知的網頁加載時間。

相關文章
相關標籤/搜索