背景

前司和现司都会存在这种业务场景:有很多 H5 页面是不相关的,如果使用 SPA 的话,对于很多落地页和活动页不太友好,有一些纯前端页面加载过慢,所以就萌生了创建一个多页面 MPA 的框架。

起初想着使用 vue-cli3 去创建,因为 vue-cli3 本身带有多页面配置的选项,直接修改 pages 这个选项就可以完成多页面配置,需要的小伙伴可以进行参考,链接:vue-cli3 的 pages。但是因为要兼容安卓 4.4 以下系统(有一些请求库中包含 E6 语法,如:axios,安卓 4.4 以下系统无法识别,所以会导致打开页面是空白的问题),pages 的入口不能配置数组,没办法添加 babel-polyfill,不能兼容低版本原生系统,所以最终采用了 webpack4 来进行多页面打包。

技术栈

本项目涉及到的技术栈主要是:webpack4,vue2,vuex3,vue-router,eslint。主要是 webpack4 的配置,其实 vue,vuex,vue-router 使用起来都是一样的。

先附上git仓库地址,然后再细说:webpack-vue-multipage。

框架解决的问题

  • webpack 根据页面不同进行打包

其实原理是 webpack 根据页面入口文件,将一个 SPA 项目分成多个 SPA 进行打包。

  • 安卓 4.4 以下手机的兼容

  • 页面 router 和 支持文件夹层级打包

这两种方式都是为了支持同一个项目下有多个页面,比如我们做的一个简易版商城也是在这个多页面中,这个时候商城可以使用 router 去控制页面路由,也可以使用层级的方式去创建多个 html 页面去实现,这个可以根据自己的业务去采用不同的方案,我们两种方式都会介绍。

  • 不同页面可以根据不同的 html 打包

有些 js 需要直接在 html 模板中引入,打包直接生成在 html 中,但是有些页面不需要引入其他的 js,比如一些纯静态页面。

  • git commit 提交时根据 eslint 进行校验

保证一个团队提交代码的统一性,可以参考我之前掘金的文章,手摸手带你实践标准的前端开发规范

介绍的差不多了,废话不多说,直接开整:

如何使用

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 中有一个 getentryconfig.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。

写文章没点图怎么行,不上代码了,这次上截图:

getentryconfig.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,我会及时为大家解答。

参考链接:

  • webpack4-vue2-multiPage

  • vue-multi-page

阅读完后三部曲

非常感谢各位花时间阅读完,衷心希望各位小伙伴可以花少量的时间帮忙做两件事:

  • 动动你的手指,帮忙点个在看吧,你的鼓励是对我最大的动力。

  • 有兴趣的可以添加我微信,我邀请你加入前端讨论群,有惊喜哦~

git仓库地址:https://github.com/Shiyanping/webpack-vue-multipage

▼原创系列推荐▼1.JavaScript 重温系列(22篇全)
2.ECMAScript 重温系列(10篇全)
3.JavaScript设计模式 重温系列(9篇全)
4.正则 / 框架 / 算法等 重温系列(16篇全)5.【汇总】59篇原创系列汇总

你点的每个赞,我都认真当成了喜欢

【Webpack】315- 手把手教你搭建基于 webpack4 的 vue2 多页应用的更多相关文章

  1. 庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境

    庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境 一.介绍 说起微服务架构来,有一个环节是少不了的,那就是CI/CD持续集成的环境.当然,搭建CI/CD环境的工具很多, ...

  2. 手把手教你搭建Pytest+Allure2.X环境详细教程,生成让你一见钟情的测试报告(非常详细,非常实用)

    简介 宏哥之前在做接口自动化的时候,用的测试报告是HTMLTestRunner,虽说自定义模板后能满足基本诉求,但是仍显得不够档次,高端,大气,遂想用其他优秀的report框架替换之.一次偶然的机会, ...

  3. 手把手教你搭建 ELK 实时日志分析平台

    本篇文章主要是手把手教你搭建 ELK 实时日志分析平台,那么,ELK 到底是什么呢? ELK 是三个开源项目的首字母缩写,这三个项目分别是:Elasticsearch.Logstash 和 Kiban ...

  4. 手把手教你搭建自己的Angular组件库 - DevUI

    摘要:DevUI 是一款面向企业中后台产品的开源前端解决方案,它倡导沉浸.灵活.至简的设计价值观,提倡设计者为真实的需求服务,为多数人的设计,拒绝哗众取宠.取悦眼球的设计.如果你正在开发 ToB 的工 ...

  5. 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接

    本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...

  6. 手把手教你写基于C++ Winsock的图片下载的网络爬虫

    手把手教你写基于C++ Winsock的图片下载的网络爬虫 先来说一下主要的技术点: 1. 输入起始网址,使用ssacnf函数解析出主机号和路径(仅处理http协议网址) 2. 使用socket套接字 ...

  7. 大数据江湖之即席查询与分析(下篇)--手把手教你搭建即席查询与分析Demo

    上篇小弟分享了几个“即席查询与分析”的典型案例,引起了不少共鸣,好多小伙伴迫不及待地追问我们:说好的“手把手教你搭建即席查询与分析Demo”啥时候能出?说到就得做到,差啥不能差人品,本篇只分享技术干货 ...

  8. 手把手教你搭建FastDFS集群(下)

    手把手教你搭建FastDFS集群(下) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/u0 ...

  9. 手把手教你搭建FastDFS集群(中)

    手把手教你搭建FastDFS集群(中) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/u0 ...

随机推荐

  1. nyoj 168-房间安排 (贪心)

    168-房间安排 内存限制:64MB 时间限制:3000ms 特判: No 通过数:33 提交数:71 难度:2 题目描述: 2010年上海世界博览会(Expo2010),是第41届世界博览会.于20 ...

  2. nyoj 273-字母小游戏 (getline(cin, string))

    273-字母小游戏 内存限制:64MB 时间限制:1000ms 特判: No 通过数:16 提交数:24 难度:0 题目描述: 给你一个乱序的字符串,里面包含有小写字母(a--z)以及一些特殊符号,请 ...

  3. 破解网站二维码验证,Java实现,不调用任何平台api接口

    package image.images; import java.io.File; import java.io.IOException; import java.io.InputStream; i ...

  4. leetcode-242 判断两个字符串是不是 Anagram ?

    题目描述 假设给定两个字符串 s 和 t, 让我们写出一个方法来判断这两个字符串是否是字母异位词? 字母异位词就是,两个字符串中含有字母的个数和数量都一样,比如: Example 1: Input: ...

  5. DNS简单配置

    ——主要执行的程序:/usr/sbin/named ——系统服务:named ——默认端口:53 ——运行时的虚拟根环境:/var/named/chroot ——主配置文件:/etc/named.co ...

  6. 4.vim编辑器

    把光标移动文件头 gg 把光标移动文件尾 G 移动到行首 ^ 移动到行尾 $ 移动到指定行 :n 回车

  7. labview连接mysql数据库

    前期准备:安装MySQL 并设置可远程连接 第一步 安装 mysql connector odbc https://www.cr173.com/soft/50794.html 第二步:创建数据源 本机 ...

  8. 2019-10-9:渗透测试,基础学习,php文件上传,mysql基础

    header("Content-Type:text/html;charst="utf-8")设置头部信息,解决编码问题setcookie("loginStrin ...

  9. 前端开发单位em

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 题解 P3954 【成绩】

    题目评级: ★ (水题) 内容及算法: 无,简单模拟计算即可 代码: /** *@author little_frog */ #include <bits/stdc++.h> using ...