手把手带你把vue+webpack 单页面改多页面(适合上手),支持多级目录

前言

公司做的后台管理系统,越做越庞大,而且系统之间还需要经常的来回嵌套页面,单页面已经不太顺手了。
所以我把vue+elementui+webpack单页面改为多页面的经验在这里唠叨唠叨,这里作展示的是一个简单的后台管理系统,利用iframe模拟的路由跳转,很简单也很容易上手,可以把这个项目下载到本地,然后把你们的页面慢慢移进去,希望能帮到一些小伙伴。

建议,把项目下载下来跑起来,结合着项目看这篇文章,不然会蒙。
gitHub:欢迎各位star
在线预览:网站不太稳定,最好下载到本地
效果预览:
图片描述

思路

一.普遍的实现方案:

1.手动改造webpack,配置多个入口,配置多个出口;
2.每个目录下边都有个.html文件来承载实例,.js文件引入Vue等等模块,.vue页面来书写逻辑等,如图:
clipboard.png

二.改进的方案

1.用node自带的模块fs来动态的匹配目录,动态的生成入口文件,动态的生成打包出口文件;
2.我们写一个公共的html文件来承载实例,写一个公共的js来引入各种依赖,根据动态传入.vue文件模块生成实例
如图:

clipboard.png

3.解释一下template下的config.js:
因为我们现在只用config.js来去引入各种依赖,就像单页面的mian.js一样,不同的是,
需要被挂载的vue文件是动态的,所以我们用localStorage来传递,我们在页面目录下边只用写一个.vue文件,一个.js文件,
这个.js文件里只用写上当前对应的.vue文件所在的目录,不用在每个js里写那么多引入的依赖

文件目录下的js:

let baseUrl = "china/china"
localStorage.setItem('baseUrl', baseUrl)

template/config.js

// 如果你想用自己创建的实例,在自己单独的js文件里加上:let baseUrl="noNeed" / localStorage.setItem('baseUrl',baseUrl)
let baseMounted = localStorage.getItem('baseUrl')
console.log(baseMounted)

import '../src/style/index.scss' //公用样式

if (baseMounted === 'noNeed') {
  // 组件自己创建了实,将不会再引入下边的资源
  throw new Error("当前组件自己创建了实例,不会引用公用方法、实例");
}
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
// 动态捕获要挂载实例的.vue文件的路径
var App = resolve => require.ensure([], () => resolve(require("../src/views/" + baseMounted + ".vue")));
new Vue({
  el: '#app',
  template: '<App/>',
  components: { App }
})

总结:就是你每次切换页面的时候,会先加载自己目录下的js文件,js里用localStorage存储.vue的路径(当然你也可以写成'noNeed',这样你自己就得在js里去引入各种依赖了),
再去引用template/config.js的时候,利用localStorage告诉挂载实例的模板是哪个。

三.最重要的webpack配置

webpack的复杂程度我就不累赘了(我自己也是个入门选手),这个需要自己花时间去看,我就直接讲下实现的结果。
主要的实现逻辑就是下边的这个js文件,出处我翻不到了,不知道是哪位大神研究出来的,
这个js文件主要实现了webpack匹配出入口文件的功能,
一开始,我也是研究了研究里边的方法,然后在原版的基础上改造了好几版,实现了现在的效果,
主要改了公共模板html和js,以及js的加载顺序。

build下的这三个文件都要改成multipage-helper.js里的方法

clipboard.png

multipage-helper.js:

var path = require('path')
var fs = require("fs")
const resolve = (p) => path.resolve(__dirname, "..", p)
var HtmlWebpackPlugin = require('html-webpack-plugin')
const templatePath = resolve("template/index.html")
const templateJs = "./template/config.js"

var moduleList //缓存多页面模块列表
var moduleRootPath = './src/views' //模块根目录(这个可以根据自己的需求命名)

/**
 * 获取js入口数组
 */
exports.getEntries = function getEntries() {
  //缓存js入口数组
  var entries = {}
  //初始化模块列表
  this.getModuleList()
  //变量模块列表
  moduleList.forEach(function(module) {
    if (module.moduleID != "" && module.moduleJS != "") {
      entries[module.moduleID] = module.moduleJS
    }
  })
  entries.app=templateJs
  console.log("*********************************** entries ***********************************")
  console.log(entries)
  return entries
}

/**
 * 获取多页面模块列表
 * @returns {模块的信息集合}
 */
exports.getModuleList = function getModuleList() {
  //判断是否为空,不为空则直接返回
  if (moduleList) {
    return moduleList
  } else { //为空则读取列表
    moduleList = new Array();
    readDirSync(moduleRootPath, "")
    console.log("*********************************** moduleList ***********************************")
    console.log(moduleList)
    return moduleList
  }
}

/**
 * 获取dev的Html模板集合
 * @returns {dev的Html模板集合}
 */
exports.getDevHtmlWebpackPluginList = function getDevHtmlWebpackPluginList() {
  console.log("*********************************** devHtmlWebpackPluginList ***********************************")
  //缓存dev的Html模板集合
  var devHtmlWebpackPluginList = []
  //获取多页面模块集合
  var moduleList = this.getModuleList()
  //遍历生成模块的HTML模板
  moduleList.forEach(function(mod) {
    //生成配置
    var conf = {
      filename: mod.moduleID + ".html",
      template: mod.moduleHTML ? mod.moduleHTML : templatePath,
      chunks: [mod.moduleID,'app'],
      chunksSortMode: 'manual',
      inject: true
    }
    console.log(conf)
    //添加HtmlWebpackPlugin对象
    devHtmlWebpackPluginList.push(new HtmlWebpackPlugin(conf))
  })
  return devHtmlWebpackPluginList
}

/**
 * 获取prod的Html模板集合
 * @returns {prod的Html模板集合}
 */
exports.getProdHtmlWebpackPluginList = function getProdHtmlWebpackPluginList() {
  console.log("*********************************** prodHtmlWebpackPluginList ***********************************")
  //缓存dev的Html模板集合
  var prodHtmlWebpackPluginList = []
  //获取多页面模块集合
  var moduleList = this.getModuleList()
  //遍历生成模块的HTML模板
  moduleList.forEach(function(mod) {
    //生成配置
    var conf = {
      filename: mod.moduleID + ".html",
      template: mod.moduleHTML ? mod.moduleHTML : templatePath,
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency',
      chunks: ['manifest', 'vendor', mod.moduleID,'app'],
      chunksSortMode: 'manual'
    }
    console.log(conf)
    //添加HtmlWebpackPlugin对象
    prodHtmlWebpackPluginList.push(new HtmlWebpackPlugin(conf))
  })
  return prodHtmlWebpackPluginList
}

/**
 * 深度遍历目录,并整理多页面模块
 * @param path 需要变量的路径
 * @param moduleName 模块名称
 */
function readDirSync(path, moduleName) {
  //缓存模块对象
  var module = { moduleID: "", moduleHTML: "", moduleJS: "" }
  //获取当前模块ID
  var moduleID = path.replace(moduleRootPath + "/", "")
  if (path == moduleRootPath) {
    moduleID = ""
  }
  module.moduleID = moduleID
  //获取目录下所有文件及文件夹
  var pa = fs.readdirSync(path)
  pa.forEach(function(ele, index) {
    var info = fs.statSync(path + "/" + ele)
    if (info.isDirectory()) {
      // console.log("dir: "+ele)
      readDirSync(path + "/" + ele, ele)
    } else {
      //判断当前模块的html是否存在
      if (moduleName+".html" == ele){
        module.moduleHTML = path+"/"+ele
      }
      // module.moduleHTML = templatePath

      //判断当前模块的js是否存在
      if (moduleName + ".js" == ele) {
        module.moduleJS = path + "/" + ele
      }
      // console.log("file: "+ele)
    }
  })
  //判断模块是否真实(可能只是个分级目录)
  if ((module.moduleID != "" && module.moduleHTML != "") || (module.moduleID != "" && module.moduleJS != "")) {
    moduleList.push(module)
  }
}

鄙人的webpack正在学习中,所以可能有些细节讲的不会太清楚,但能用,希望大家的指正,也希望能帮到大家,嘻嘻嘻。

可以假装很努力,但结果不会骗你

1.1k 声望
165 粉丝
0 条评论
推荐阅读
mpvue写小程序遇到的问题总结(自己用mpvue实现一个小程序的tabbar,vantUi的使用)
公司要开发一个小程序,面向用户分为3种角色,所以要自己手写个tabbar(文章编辑中,届时将链接放过来)因为也是时隔很久写小程序,就选择了跟vue相近的mpvue来写,或多或少也遇到了点问题,现总结一下。

大师兄5阅读 5.4k评论 4

【已结束】SegmentFault 思否写作挑战赛!
SegmentFault 思否写作挑战赛 是思否社区新上线的系列社区活动在 2 月 8 日 正式面向社区所有用户开启;挑战赛中包含多个可供作者选择的热门技术方向,根据挑战难度分为多个等级,快来参与挑战,向更好的自己前进!

SegmentFault思否20阅读 5.6k评论 10

封面图
Vue2 导出excel
2020-07-15更新 excel导出安装 {代码...} src文件夹下新建一个libs文件夹,新建一个excel.js {代码...} vue页面中使用 {代码...} ===========================以下为早期的文章今天在开发的过程中需要做一个Vue的...

原谅我一生不羁放歌搞文艺14阅读 19.9k评论 9

「彻底弄懂」this全面解析
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在 哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在 函数执行的过程中用到...

wuwhs17阅读 2.4k

封面图
反编译微信小程序获取小程序前端源码wxapkg
研究反编译的原因就是我自己辛苦了半个月写的小程序,忘记备份放在桌面,心急体验Win11系统 重装系统忘记备份源码,后悔莫及。 后来网上找了反编译的教程,反编译已经上线了的小程序 于是自己尝试了一下,真的可...

TANKING13阅读 10k评论 7

封面图
用了那么久的 SVG,你还没有入门吗?
其实在大部分的项目中都有 直接 或 间接 使用到 SVG 和 Canvas,但是在大多数时候我们只是选择 简单了解 或 直接跳过,这有问题吗?没有问题,毕竟砖还是要搬的!

熊的猫17阅读 1.5k评论 2

封面图
嘿,vue中keep-alive有个「大坑」你可能还不知道
背景是这样的,我们使用vue2开发一个在线客服使用的IM应用,基本布局是左边是访客列表,右边是访客对话,为了让对话加载更友好,我们将对话的路由使用&lt;keep-alive&gt;缓存起来。但是如果将所有对话都缓存,未...

wuwhs12阅读 2.5k

封面图

可以假装很努力,但结果不会骗你

1.1k 声望
165 粉丝
宣传栏