前言
webpack和gulp最大的區(qū)別在于它是一個(gè)打包工具挽霉,它串聯(lián)起了整個(gè)前端工程化
的每一項(xiàng)內(nèi)容樱调。我非常慶幸的是經(jīng)歷過webpack1到2的升級胸完,也經(jīng)歷了2到3的升級公条,打包的相關(guān)內(nèi)容也越來越多。如今webpack從3變成4硅急,很多人抱怨webpack的配置太過于復(fù)雜覆享,在webpack4以后它的配置會變得越來越簡單,對于開發(fā)者來說营袜,entry撒顿,output,loader连茧,plugin四大板塊是必須要清楚的核蘸。
- entry 輸入
- output 輸出
- loader 打包規(guī)則
- plugin 插件生態(tài)
當(dāng)你了解了上面的這些內(nèi)容,還不夠啸驯。你需要了解各個(gè)版本間的差異性客扎,這樣你才能充分利用它所有的功能。Webpack1到2最大的升級是tree-shaking罚斗,其次是配置文件的對象化徙鱼,再其次包括插件的寫法優(yōu)化。Webpack2到3的最大升級是scope-hoisting针姿。3到4簡化了整個(gè)打包配置操作袱吆。
code-spliting
code-spliting(代碼分割)應(yīng)該是所有前端人都知道的優(yōu)化點(diǎn)。當(dāng)你單頁面做越做越大的時(shí)候距淫,非首屏的頁面就會考慮到不優(yōu)先加載绞绒。但是怎么去劃分懶加載的包,最高效的方法就是路由懶加載榕暇。
舉個(gè)栗子蓬衡,在你使用vue路由的時(shí)候,你可能會考慮到除了第一頁的內(nèi)容彤枢,不會預(yù)先加載狰晚,會延時(shí)加載后面幾頁的功能。這七個(gè)頁面會從 app.js 中拆分成為7個(gè)js包缴啡。這樣的代碼分割壁晒,在大型的單頁面應(yīng)用中,我們必須使用到因?yàn)楹竺娴捻撁嫖覀儾恍枰崆凹虞d业栅。
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [
{
path: '/',
component: resolve => require(['../views/Map'], resolve),
},
{
path: '/setting',
component: resolve => require(['../views/Setting'], resolve),
},
{
path: '/cities',
component: resolve => require(['../views/Cities'], resolve),
},
{
path: '/discovery',
component: resolve => require(['../views/Discovery'], resolve),
},
{
path: '/about',
component: resolve => require(['../views/About'], resolve),
},
{
path: '/more',
component: resolve => require(['../views/More'], resolve),
},
{
path: '/weather',
component: resolve => require(['../views/Weather'], resolve),
},
];
const router = new VueRouter({ mode: 'history', base: '/app/', routes });
隨著webpack2語法的進(jìn)化秒咐,上面的代碼也可以被寫成這樣子。
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [
{
path: '/',
component:() => import('../views/Map'),
},
{
path: '/setting',
component: () => import('../views/Setting'),
},
{
path: '/cities',
component: () => import('../views/Cities'),
},
{
path: '/discovery',
component: () => import('../views/Discovery'),
},
{
path: '/about',
component: () => import('../views/About'),
},
{
path: '/more',
component: () =>import('../views/More'),
},
{
path: '/weather',
component: () => import('../views/Weather'),
},
];
const router = new VueRouter({ mode: 'history', base: '/app/', routes });
每個(gè)懶加載的背后都附送一個(gè)鉤子碘裕。使用了code-splitting反镇,webpack會根據(jù)你可以將一些首屏不顯示的內(nèi)容額外打包成為一個(gè)獨(dú)立的js。webpack2中懶加載打包會連同樣式以內(nèi)聯(lián)的形式一起打入JS中娘汞,這樣的好處在于公共樣式也被細(xì)化抽離歹茶,但是可能會造成樣式冗余。webpack3則提供了ExtractTextPlugin中提供了抽取公共樣式的方法你弦,公共樣式可以額外抽離惊豺。
tree-shaking
tree-shaking是rollup提出的一款技術(shù),反哺到了webpack2的升級版本中禽作。這可以說一個(gè)非常難以理解的概念尸昧,就像lodash這樣的公共方法,在項(xiàng)目編寫會積累的越來越多旷偿,但是我們不希望將這些方法全部打包入一個(gè)js文件當(dāng)中烹俗。常見的方法有:
- 項(xiàng)目解耦爆侣,將一個(gè)大型項(xiàng)目拆分成幾個(gè)小型項(xiàng)目
- 使用tree-shaking,它只打包有用的方法幢妄,沒有用的方法則不會進(jìn)行打包
tree-shaking默認(rèn)是不會觸發(fā)的兔仰。在webpack3,你需要配置babel蕉鸳,uglifyjs-webpack-plugin等才能觸發(fā)乎赴。在webpack4,production模式默認(rèn)觸發(fā)潮尝。首先榕吼,如果在編寫代碼過程中必須使用得當(dāng),純函數(shù)對于tree項(xiàng)目打包有相當(dāng)大的優(yōu)勢勉失,也就是你的變量盡量要保持函數(shù)間的干凈羹蚣,不要相互污染。
情景一:最簡單的例子
在index.js
引入另一個(gè)js中的兩個(gè)方法乱凿,webpack4的打包結(jié)果是只會存在console.log(1)
度宦。而console.log(2)
已不會進(jìn)入打包的范圍當(dāng)中。
//core.js
export function test1() {
console.log(1)
}
export function test2() {
console.log(2)
}
//index.js
import {test1} from './core'
test1()
情景二:存在一個(gè)常量或者變量
當(dāng)core.js
有個(gè)全局變量a=2
告匠,這個(gè)變量可能會在別的函數(shù)中改變戈抄,webpack會檢查該函數(shù)是否在打包范圍內(nèi)。不會在該范圍內(nèi)的后专,如test1
划鸽,則不會被打包。
//core.js
let a = 2;
export function test1() {
a = 1;
console.log(a);
}
export function test2() {
console.log(a);
}
//index.js
import {test2} from './core'
test2()
情景三:存在一個(gè)對象
如果你輸出的是一個(gè)對象戚哎,你只需要其中的一個(gè)方法裸诽,此時(shí)你同樣結(jié)構(gòu)只需要一個(gè)方法test2,別的方法是不會被打包進(jìn)去型凳。
//core.js
let a = 2;
function test1() {
a = 1;
console.log(a);
}
function test2() {
console.log(a);
}
function test3() {
console.log(3)
}
function test4() {
console.log(4)
}
export {
test1,
test2,
test3,
test4
}
情景四:存在prototype或者class
其實(shí)結(jié)果很明顯丈冬,由于別的方法會在實(shí)例化的時(shí)候聲明,由于被實(shí)例化的新的對象相互間是有聯(lián)系的甘畅,這也注定了它會被打包進(jìn)去埂蕊。
scope-hoisting
它的中文名就是作用域提升,這個(gè)名字非常熟悉疏唾。說到底蓄氧,javascript的模塊化就是通過閉包來實(shí)現(xiàn)作用域的隔離,但是當(dāng)我們模塊化程度達(dá)到一定程度之后槐脏,過多閉包會讓某些變量沒法銷毀喉童,造成性能劣勢。作用域提升即是把兩個(gè)閉包合成一個(gè)閉包顿天。
通過Scope Hoisting優(yōu)化Webpack輸出里面講了最最基礎(chǔ)的一個(gè)作用域提升的例子堂氯,我在這里借用這個(gè)例子蔑担。首先寫兩個(gè)js,它們相互之間是引用關(guān)系咽白。這里采用webpack4版本舉例啤握,在development mode時(shí)沒有采用scope hoisting,而production mode時(shí)默認(rèn)開啟了該優(yōu)化局扶。
// core.js
export const a = 'Hello,Webpack';
// index.js
import { a } from './base.js';
console.log(a);
如果你在項(xiàng)目中使用webpack3恨统,你需要開啟webpack.optimize.ModuleConcatenationPlugin
來滿足作用域提升的功能叁扫。如果你在項(xiàng)目中使用webpack4三妈,那么你在開發(fā)模式即是關(guān)閉作用域提升,在生產(chǎn)模式開啟該功能莫绣。