手把手教你搭建基於 webpack4 的 vue2 多頁應用

背景

前司和現司都會存在這種業務場景:有不少 H5 頁面是不相關的,若是使用 SPA 的話,對於不少落地頁和活動頁不太友好,有一些純前端頁面加載過慢,因此就萌生了建立一個多頁面 MPA 的框架。css

起初想着使用 vue-cli3 去建立,由於 vue-cli3 自己帶有多頁面配置的選項,直接修改 pages 這個選項就能夠完成多頁面配置,須要的小夥伴能夠進行參考,連接:vue-cli3 的 pages。可是由於要兼容安卓 4.4 如下系統(有一些請求庫中包含 E6 語法,如:axios,安卓 4.4 如下系統沒法識別,因此會致使打開頁面是空白的問題),pages 的入口不能配置數組,沒辦法添加 babel-polyfill,不能兼容低版本原生系統,因此最終採用了 webpack4 來進行多頁面打包。html

技術棧

本項目涉及到的技術棧主要是:webpack4,vue2,vuex3,vue-router,eslint。主要是 webpack4 的配置,其實 vue,vuex,vue-router 使用起來都是同樣的。前端

先附上git倉庫地址,而後再細說:webpack-vue-multipagevue

框架解決的問題

  • webpack 根據頁面不一樣進行打包

其實原理是 webpack 根據頁面入口文件,將一個 SPA 項目分紅多個 SPA 進行打包。webpack

  • 安卓 4.4 如下手機的兼容ios

  • 頁面 router 和 支持文件夾層級打包git

這兩種方式都是爲了支持同一個項目下有多個頁面,好比咱們作的一個簡易版商城也是在這個多頁面中,這個時候商城可使用 router 去控制頁面路由,也可使用層級的方式去建立多個 html 頁面去實現,這個能夠根據本身的業務去採用不一樣的方案,咱們兩種方式都會介紹。es6

  • 不一樣頁面能夠根據不一樣的 html 打包

有些 js 須要直接在 html 模板中引入,打包直接生成在 html 中,可是有些頁面不須要引入其餘的 js,好比一些純靜態頁面。github

  • git commit 提交時根據 eslint 進行校驗

保證一個團隊提交代碼的統一性,能夠參考我以前掘金的文章,手摸手帶你實踐標準的前端開發規範 web

介紹的差很少了,廢話很少說,直接開整:

如何使用

git clone https://github.com/Shiyanping/webpack-vue-multipage.git

cd webpack-vue-multipage

npm install

npm run dev
# 啓動以後在瀏覽器訪問便可,http://localhost:8022/index.html

# eslint
npm run eslint

# 格式化代碼
npm run prettier

# build
npm run build
複製代碼

webpack 的配置

多頁面和單頁面的區別,主要是在 entry 上,因此咱們首先對 entry 進行處理。

entry

多頁面和單頁面最大的不一樣點,就在於入口的不一樣。

  • 多頁:最終打包生成多個入口( html 頁面),通常每一個入口文件除了要引入公共的靜態文件( js/css )還要另外引入頁面特有的靜態資源

  • 單頁:只有一個入口( index.html ),頁面中須要引入打包後的全部靜態文件,全部的頁面內容全由 JavaScript 控制

直接看代碼吧,在 utils 中有一個 get_entry_config.js 去獲取 entry 的配置,其中包括了入口選擇性引用模板 html,babel-polyfill 加到入口的配置中。註釋其實寫的聽明白的,各位看官有什麼不知道的能夠像老哥我諮詢。

const fs = require("fs");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
const config = require("./../../config/page_config"); // 多頁面的配置項

const resolve = dir => {
  return path.resolve(process.cwd(), dir);
};

let HTMLPlugins = [];
let Entries = {};

config.HTMLDirs.forEach(item => {
  let filename = `${item.page}.html`;

  /** * 支持多級目錄,dir/page.html * 多頁面框架中能夠採用這種方式增長層級目錄,一個目錄下有多個頁面 * 也可使用 router 進行同級目錄下一個html,經過 router 控制路由 */
  if (item.dir) {
    filename = `${item.dir}/${item.page}.html`;
  }

  // 每一個頁面的文件夾下能夠含有一個本身的index.html,若是有會根據這個模板進行build
  let pageHtml = `src/pages/${item.page}/index.html`;

  // 若是文件夾下沒有制定的模板,則採用默認的模板 build
  if (!fs.existsSync(pageHtml)) {
    pageHtml = "src/template/default.html";
  }

  const htmlPlugin = new HTMLWebpackPlugin({
    title: item.title, // 生成的html頁面的標題
    filename: filename, // 生成到dist目錄下的 html 文件名稱
    template: resolve(pageHtml), // 模板文件,不一樣入口能夠根據須要設置不一樣模板
    chunks: [item.page, "vendor"] // html文件中須要要引入的 js模塊,這裏的 vendor 是 webpack 默認配置下抽離的公共模塊的名稱
  });

  HTMLPlugins.push(htmlPlugin);

  // 添加 babel-polyfill 解決安卓 4.4 如下兼容問題
  Entries[item.page] = [
    "babel-polyfill",
    resolve(`src/pages/${item.page}/index.js`)
  ];
});

module.exports = {
  HTMLPlugins,
  Entries
};
複製代碼

上面的 js 中引用了一個 page_config.js,這個 js 中,主要是多頁面的配置信息:

module.exports = {
  HTMLDirs: [
    {
      page: "index",
      title: "首頁"
    },
    {
      page: "list",
      title: "列表頁",
      dir: "content" // 支持設置多級目錄
    },
    {
      page: "detail",
      title: "詳情頁"
    }
  ]
};
複製代碼

最後在 webpack.config.js 中引入相關配置:

module.exports = {
  entry: entryConfig.Entries,
  plugins: [
    ...entryConfig.HTMLPlugins // 利用 HTMLWebpackPlugin 插件合成最終頁面
  ]
};
複製代碼

這些就是咱們多頁面的主要設置,也就是多頁面的入口。

不一樣頁面使用不一樣的 html 模板

其實說白了多頁面就是將多個小項目彙總到一個大項目,這個是 webpack 幫咱們作的事,只不過這些小項目之間的關聯性不大,因此作成了多頁面。

在實際開發中,有些頁面須要直接在 html 中引入的 js 文件,好比公司的公共 jsbridge,沒有封裝成 npm 包,只能用下面這種方式引入了:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>
      <%= htmlWebpackPlugin.options.title %>
    </title>
  </head>
  <body>
    <div id="app"></div>
    <script src="http://www.xxx.com/jsbridge.min.js"></script>
  </body>
</html>
複製代碼

可是有些分享出去的頁面,並不須要這個 js,若是咱們都使用同一個 html 模板打包,那至關於打包出去的多頁面,每一個頁面都有這個 js,這樣就會致使頁面請求慢一些問題。

這個時候就有必要對不一樣的小項目使用不一樣的 html 模板了。

其實主要的代碼就是下面這幾句,很簡單:

// 每一個頁面的文件夾下能夠含有一個本身的index.html,若是有會根據這個模板進行build
  let pageHtml = `src/pages/${item.page}/index.html`;

  // 若是文件夾下沒有制定的模板,則採用默認的模板 build
  if(!fs.existsSync(pageHtml)){
    pageHtml = 'src/template/default.html';
  }

  const htmlPlugin = new HTMLWebpackPlugin({
    ...
    template: resolve(pageHtml), // 模板文件,不一樣入口能夠根據須要設置不一樣模板
    ...
  });
複製代碼

根據文件夾目錄去引用 html 模板,若是當前頁面文件夾下有本身的 index.html,那咱們就使用本身的,若是沒有就使用默認的。這樣在打包生成 html 的時候就能夠按照不一樣頁面引用不一樣的 html 模板了,不會形成不想要的 js 被引用的問題存在。

安卓 4.4 如下兼容問題

這個問題提及來不少人都不想弄,其實我也不想,可是沒辦法啊,公司的用戶羣體中安卓機佔了很大一部分,而且安卓 4.4 如下機型佔了 20%,這樣的狀況就必需要對頁面作兼容了。

其實單頁面作兼容很簡單,在 webpack 的 entry 配置一下 babel-polyfill,而後在單頁面的 main.js 中,直接引入 babel-polyfill 和 es6-promise 就能夠了。以下:

// webpack.config.js
entry = ['babel-polyfill', resolve('src/main.js')];

// main.js
import 'babel-polyfill';
import promise from 'es6-promise';
promise.polyfill();
複製代碼

這樣SPA就能夠解決兼容問題,MPA就有一點麻煩了,觸類旁通,咱們要在entry的每個入口增長 babel-polyfill,而後在每一個page下的index.js中引入 babel-polyfill 和 es6-promise。

寫文章沒點圖怎麼行,不上代碼了,此次上截圖:

get_entry_config.js:

感受看着不舒服,算了,仍是上代碼吧~

// 添加 babel-polyfill 解決安卓 4.4 如下兼容問題
Entries[item.page] = ['babel-polyfill', resolve(`src/pages/${item.page}/index.js`)]; // 根據配置設置入口js文件
複製代碼

而後在每一個文件夾的index.js中都要加入編譯的代碼。

// src/pages/**/index.js
import Vue from 'vue';
// import '@styles/lib/main.scss';
import Tpl from './index.vue';
import store from '../../store';
import 'babel-polyfill';
import promise from 'es6-promise';
promise.polyfill();

new Vue({
  store,
  render: (h) => h(Tpl)
}).$mount('#app');
複製代碼

這樣編譯以後就能夠解決安卓 4.4 如下的兼容了,親測有效哦~

頁面 router 和 支持文件夾層級打包

每一個小項目中,可能會涉及到一些頁面相對來講比較多的項目,好比一個簡易版的商城,包括商品列表頁,商品詳情頁,訂單頁。

這個時候咱們可使用兩種方式:

  • 使用vue-router控制路由

這個我以爲不用多說了吧,在須要使用路由的文件夾下建立一個router.js,而且引入vue-router,必定要在某個文件夾下建立哦,不然幾個頁面公用一個router,會有意想不到的結果。其實咱們就能夠把MPA想象成多個SPA,一個SPA一個路由,他們之間沒有關聯,純頁面的東西不用路由就不須要建立。

這樣就能夠實現用路由的方式去控制不一樣頁面的走向了。

結構以下:

├── components
│   ├── About
│   │   └── Index.vue
│   └── Home
│       └── Index.vue
├── index.js
├── index.vue
└── router.js
複製代碼

使用方式和開發其餘SPA沒區別。

  • 使用層級打包的方式

這是另一種方式,就是經過打包成有層級目錄的方式控制頁面的走向,這個裏面沒有涉及到路由,只是單純的打包加一個層級就行。

主要有兩點須要控制,一個是頁面配置 page_config.js,另一個是 webpack 處理入口這塊 get_entry_config.js,看代碼:

page_config.js:

{
  page: 'list',
  title: '列表頁',
  dir: 'content' // 支持設置多級目錄
}
複製代碼

get_entry_config.js:

let filename = `${item.page}.html`;

/** * 支持多級目錄,dir/page.html * 多頁面框架中能夠採用這種方式增長層級目錄,一個目錄下有多個頁面 * 也可使用 router 進行同級目錄下一個html,經過 router 控制路由 */
if (item.dir) {
  filename = `${item.dir}/${item.page}.html`;
}
複製代碼

這樣打包以後的文件就能實現如下層級的關係:

├── list.html
└── list1.html
複製代碼

訪問的時候就能夠不須要依賴路由,直接訪問頁面便可。

git commit 鉤子校驗 eslint

這個我就不細說了,主要是爲了保持團隊中每一個人提交代碼以前進行不合格的校驗,確保git倉庫中的代碼是沒問題的,而且格式是同樣的,這個還能夠搭配prettier使用,能夠自行百度。

配置的問題,能夠參考我以前的文章,手摸手帶你實踐標準的前端開發規範

有一點須要注意,一開始你clone的是我倉庫,若是想實現提交就校驗eslint,須要將文件夾中.git刪除掉,關聯到你的git倉庫,而後從新安裝husky 包。

總結

基本的功能都實現了,不過還不是很完美,有不少功能都沒加進來,好比移動端的樣式適配,網絡請求庫封裝,公共方法的提取...,因此說還有不少不足之處,歡迎你們在個人github倉庫上進行pr和提issue,我會及時爲你們解答。

參考連接:

閱讀完後三部曲

很是感謝各位花時間閱讀完,衷心但願各位小夥伴能夠花少許的時間幫忙作兩件事:

  • 動動你的手指,幫忙點個贊吧,你的點贊是對我最大的動力。

  • 有興趣的能夠添加我微信,我邀請你加入前端討論羣,有驚喜哦~

  • 但願各位關注一下個人公衆號,新的文章第一時間發到公衆號,公衆號主要發一些我的隨筆、讀書筆記、還有一些技術熱點和實時熱點,而且還有很是吸引人的我我的自費抽獎活動哦~

相關文章
相關標籤/搜索