webpack筆記三:版本的差異性

前言

webpack和gulp最大的區別在于它是一個打包工具,它串聯起了整個前端工程化
的每一項內容。我非常慶幸的是經歷過webpack1到2的升級,也經歷了2到3的升級,打包的相關內容也越來越多。如今webpack從3變成4,很多人抱怨webpack的配置太過于復雜,在webpack4以后它的配置會變得越來越簡單,對于開發者來說,entry,output,loader,plugin四大板塊是必須要清楚的。

  • entry 輸入
  • output 輸出
  • loader 打包規則
  • plugin 插件生態

當你了解了上面的這些內容,還不夠。你需要了解各個版本間的差異性,這樣你才能充分利用它所有的功能。Webpack1到2最大的升級是tree-shaking,其次是配置文件的對象化,再其次包括插件的寫法優化。Webpack2到3的最大升級是scope-hoisting。3到4簡化了整個打包配置操作。

code-spliting

code-spliting(代碼分割)應該是所有前端人都知道的優化點。當你單頁面做越做越大的時候,非首屏的頁面就會考慮到不優先加載。但是怎么去劃分懶加載的包,最高效的方法就是路由懶加載。

舉個栗子,在你使用vue路由的時候,你可能會考慮到除了第一頁的內容,不會預先加載,會延時加載后面幾頁的功能。這七個頁面會從 app.js 中拆分成為7個js包。這樣的代碼分割,在大型的單頁面應用中,我們必須使用到因為后面的頁面我們不需要提前加載。

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語法的進化,上面的代碼也可以被寫成這樣子。

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 });

每個懶加載的背后都附送一個鉤子。使用了code-splitting,webpack會根據你可以將一些首屏不顯示的內容額外打包成為一個獨立的js。webpack2中懶加載打包會連同樣式以內聯的形式一起打入JS中,這樣的好處在于公共樣式也被細化抽離,但是可能會造成樣式冗余。webpack3則提供了ExtractTextPlugin中提供了抽取公共樣式的方法,公共樣式可以額外抽離。

tree-shaking

tree-shaking是rollup提出的一款技術,反哺到了webpack2的升級版本中。這可以說一個非常難以理解的概念,就像lodash這樣的公共方法,在項目編寫會積累的越來越多,但是我們不希望將這些方法全部打包入一個js文件當中。常見的方法有:

  1. 項目解耦,將一個大型項目拆分成幾個小型項目
  2. 使用tree-shaking,它只打包有用的方法,沒有用的方法則不會進行打包

tree-shaking默認是不會觸發的。在webpack3,你需要配置babel,uglifyjs-webpack-plugin等才能觸發。在webpack4,production模式默認觸發。首先,如果在編寫代碼過程中必須使用得當,純函數對于tree項目打包有相當大的優勢,也就是你的變量盡量要保持函數間的干凈,不要相互污染。

情景一:最簡單的例子

index.js引入另一個js中的兩個方法,webpack4的打包結果是只會存在console.log(1)。而console.log(2)已不會進入打包的范圍當中。

//core.js
export function test1() {
    console.log(1)
}

export function test2() {
    console.log(2)
}
//index.js
import {test1} from './core'
test1()

情景二:存在一個常量或者變量

core.js有個全局變量a=2,這個變量可能會在別的函數中改變,webpack會檢查該函數是否在打包范圍內。不會在該范圍內的,如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()

情景三:存在一個對象

如果你輸出的是一個對象,你只需要其中的一個方法,此時你同樣結構只需要一個方法test2,別的方法是不會被打包進去。

//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

其實結果很明顯,由于別的方法會在實例化的時候聲明,由于被實例化的新的對象相互間是有聯系的,這也注定了它會被打包進去。

scope-hoisting

它的中文名就是作用域提升,這個名字非常熟悉。說到底,javascript的模塊化就是通過閉包來實現作用域的隔離,但是當我們模塊化程度達到一定程度之后,過多閉包會讓某些變量沒法銷毀,造成性能劣勢。作用域提升即是把兩個閉包合成一個閉包。

通過Scope Hoisting優化Webpack輸出里面講了最最基礎的一個作用域提升的例子,我在這里借用這個例子。首先寫兩個js,它們相互之間是引用關系。這里采用webpack4版本舉例,在development mode時沒有采用scope hoisting,而production mode時默認開啟了該優化。

// core.js
export const a = 'Hello,Webpack';
// index.js 
import { a } from './base.js';
console.log(a);
development mode
production mode

如果你在項目中使用webpack3,你需要開啟webpack.optimize.ModuleConcatenationPlugin來滿足作用域提升的功能。如果你在項目中使用webpack4,那么你在開發模式即是關閉作用域提升,在生產模式開啟該功能。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容