webpack多頁面打包實踐

前不久從零開始寫了一個webpack多頁面打包boilerplate(webpack4-boilerplate),方便之後工做能夠開箱即用,特此記錄下開發過程當中的要點。javascript

注意:本文不會詳細介紹webpack的基礎知識,若是徹底不會,建議看下我以前寫過的基礎文章css

多頁打包的緣由

首先發出一個直擊靈魂的拷問:爲何要多頁面打包?html

習慣了React,Vue全家桶的同窗,可能以爲寫代碼不就是:npm run dev npm run build一把梭嗎?前端

然而現實是骨感的,不少場景下,單頁應用的開發模式並不適用。好比公司常常開發一些活動頁: https://www.demo.com/activity/activity1.html https://www.demo.com/activity/activity2.html https://www.demo.com/activity/activity3.htmlvue

上述三個頁面是徹底不相干的活動頁,頁面之間並無共享的數據。然而每一個頁面都使用了React框架,而且三個頁面都使用了通用的彈框組件。在這種場景下,就須要使用webpack多頁面打包的方案了:java

  1. 保留了傳統單頁應用的開發模式:使用Vue,React等前端框架(固然也可使用jQuery),支持模塊化打包,你能夠把每一個頁面當作是一個單獨的單頁應用
  2. 獨立部署:每一個頁面相互獨立,能夠單獨部署,解耦項目的複雜性,你甚至能夠在不一樣的頁面選擇不一樣的技術棧

所以,咱們能夠把多頁應用當作是乞丐版的前端微服務node

多頁面打包的原理

首先咱們約定: src/pages目錄下,每一個文件夾爲單獨的一個頁面。每一個頁面至少有兩個文件配置:react

  • app.js: 頁面的邏輯入口webpack

  • index.html: 頁面的html打包模板git

src/pages
├── page1
│   ├── app.js
│   ├── index.html
│   ├── index.scss
└── page2
    ├── app.js
    ├── index.html
    └── index.scss
複製代碼

前面咱們說過:每一個頁面能夠當作是個獨立的單頁應用

單頁應用怎麼打包的?單頁應用是經過配置webpack的的entry

module.exports = {
  entry: './src/main.js', // 項目的入口文件,webpack會從main.js開始,把全部依賴的js都加載打包
  output: {
      path: path.resolve(__dirname, './dist'), // 項目的打包文件路徑
      filename: 'build.js' // 打包後的文件名
  }
};
複製代碼

所以,多頁應用只需配置多個entry便可

module.exports = {
  entry: {
    'page1': './src/pages/page1/app.js', // 頁面1
    'page2': './src/pages/page2/app.js', // 頁面2
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'js/[name]/[name]-bundle.js', // filename不能寫死,只能經過[name]獲取bundle的名字
  }
}
複製代碼

同時,由於多頁面的index.html模板各不相同,因此須要配置多個HtmlWebpackPlugin。

注意:HtmlWebpackPlugin必定要配chunks,不然全部頁面的js都會被注入到當前html裏

module.exports = {
  plugins: [
    new HtmlWebpackPlugin(
    {
      template: './src/pages/page1/index.html',
      chunks: ['page1'],
    }
  ),
  new HtmlWebpackPlugin(
    {
      template: './src/pages/page2/index.html',
      chunks: ['page2'],
    }
  ),
  ]
}
複製代碼

多頁面打包的原理就是:配置多個entry和多個HtmlWebpackPlugin

多頁打包的細節

代碼分割

  1. 把多個頁面共用的第三方庫(好比React,Fastclick)單獨打包出一個vendor.js
  2. 把多個頁面共用的邏輯代碼和共用的全局css(好比css-reset,icon字體圖標)單獨打包出common.jscommon.css
  3. 把運行時代碼單獨提取出來manifest.js
  4. 把每一個頁面本身的業務代碼打包出page1.jspage1.css

前3項是每一個頁面都會引入的公共文件,第4項纔是每一個頁面本身單獨的文件。

實現方式也很簡單,配置optimization便可:

module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        // 打包業務中公共代碼
        common: {
          name: "common",
          chunks: "initial",
          minSize: 1,
          priority: 0,
          minChunks: 2, // 同時引用了2次纔打包
        },
        // 打包第三方庫的文件
        vendor: {
          name: "vendor",
          test: /[\\/]node_modules[\\/]/,
          chunks: "initial",
          priority: 10,
          minChunks: 2, // 同時引用了2次纔打包
        }
      }
    },
    runtimeChunk: { name: 'manifest' } // 運行時代碼
  }
}
複製代碼

hash

最後打包出來的文件,咱們但願帶上hash值,這樣能夠充分利用瀏覽器緩存。webpack中有hashchuckhashcontenthash:生產環境時,咱們通常使用contenthash,而開發環境其實能夠不指定hash。

// dev開發環境
module.exports = {
  output: {
    filename: 'js/[name]/[name]-bundle.js',
    chunkFilename: 'js/[name]/[name]-bundle.js',
  },
}

// prod生產環境
module.exports = {
  output: {
    filename: 'js/[name]/[name]-bundle.[contenthash:8].js',
    chunkFilename: 'js/[name]/[name]-bundle.[contenthash:8].js',
  },
}
複製代碼

mock和proxy

開發環境,一般須要mock數據,還須要代理api到服務器。咱們能夠經過devServer配合mocker-api第三方庫實現。

const apiMocker = require('mocker-api');

// dev開發環境
module.exports = {
  devServer: {
    before(app) { // 本地mock數據
      apiMocker(app, path.resolve(__dirname, '../mock/index.js'))
    },
    proxy: { // 代理接口
      '/api': {
        target: 'https://anata.me', // 後端聯調地址
        changeOrigin: true,
        secure: false,
      },
    }
  },
}
複製代碼

拆分webpack配置

爲了通用配置,把webpack的配置文件分紅3份。

build
├── webpack.base.js // 共用部分
├── webpack.dev.js // dev
└── webpack.prod.js // 生產
複製代碼

devprod配置的主要區別:

  • dev配置devServer,方便本地調試開發
  • prod打包壓縮文件,單獨提取css (dev不提取是爲了css熱更新),生成靜態資源清單manifest.json

關於爲何要生成一份manifest.json,以及打包後的代碼如何部署,我將會在下一篇文章詳細介紹。

相關文章
相關標籤/搜索