vue多页实践-开发

1,505 阅读4分钟

环境

  • node v12.5.0
  • 系统 win
  • vuecli @vue/cli 4.5.15
  • ide WebStorm 2021.1 x64

本文是将vue-cli创建的spa应用改造为多页应用。

我们可以把多页应用理解为由多个单页构成的应用,而何谓多个单页呢?其实你可以把一个单页看成是一个 html 文件,那么多个单页便是多个 html 文件,多页应用便是由多个 html 组成的应用,如下图所示:

image.png

开始改造

更改目录

  • 多页应用的每个单页都可以拥有单页应用 src 目录下的文件及功能
  • src 下新建 pages 文件夹,将 public文件夹中 index.html、src 文件夹中 App.vue、main.js 复制到相应的应用文件夹内修改文件名称,然后删除即可。
  • 创建如下图所示文件目录,文件夹的名称不做要求但需要同名的入口js、html文件边配置多页会用到。
  • 拿index文件夹来举例说明
    • index.js 相当于 main.js
    • index.html 相当于 public文件夹中 index.html
    • index.vue 相当于 src 文件夹中 App.vue
  • src 下的 router、store 文件夹保留与否 看具体场景,是所用应用共用一个还是需要分开。比如应用的 router、store 文件差别很大,那就需要分开或者使用命名空间,如果几乎一样那就可以共用一个文件(数据不共享)。

image.png

更改 vue.config.js

image.png

  • 单页应用中入口文件默认指定 main.js,多页应用入口文件则包含了 page1.js、page2.js、index.js等,数量取决于 pages 文件夹下目录的个数,动态生成 pages 对象是一个很好的选择。

动态获取 pages 对象

  • 安装 yarn add glob 扫描文件夹,并返回我们需要的文件
const path = require('path')
const glob = require('glob')

// 配置pages多页面获取当前文件夹下的html和js
function getEntry (globPath) {
  const entries = {}
  let basename
  let tmp
  let pathname
  glob.sync(globPath).forEach(function (entry) {
    basename = path.basename(entry, path.extname(entry))
    tmp = entry.split('/').splice(-3)
    pathname = basename // 正确输出js和html的路径
    entries[pathname] = {
      entry: 'src/' + tmp[0] + '/' + tmp[1] + '/' + tmp[1] + '.js',
      template: 'src/' + tmp[0] + '/' + tmp[1] + '/' + tmp[2],
      title: tmp[2],
      filename: tmp[2],
      chunks: ['chunk-vendors', 'chunk-common', tmp[1]]
    }
  })
  return entries
}

const pages = getEntry('./src/pages/**?/*.html')
  • 获取到的对象 可以在 vue.config.js 进行打印 image.png
  • vue.config.js 中的 module.exports 对象写入多页配置(也就是比单页多了一个 pages 配置)

image.png

配置动态引入js css文件

  • 配置动态引入需要使用 html-webpack-plugin 插件,多页会存在多个配置 只配置一个就会抛错 如下图。

image.png

  • 错误也让人摸不着头脑 image.png
  • 可以这样做

image.png

image.png

完整的配置

const path = require('path')
const glob = require('glob')

// 配置pages多页面获取当前文件夹下的html和js
function getEntry (globPath) {
  const entries = {}
  let basename
  let tmp
  let pathname
  glob.sync(globPath).forEach(function (entry) {
    basename = path.basename(entry, path.extname(entry))
    tmp = entry.split('/').splice(-3)
    pathname = basename // 正确输出js和html的路径
    entries[pathname] = {
      entry: 'src/' + tmp[0] + '/' + tmp[1] + '/' + tmp[1] + '.js',
      template: 'src/' + tmp[0] + '/' + tmp[1] + '/' + tmp[2],
      title: tmp[2],
      filename: tmp[2],
      chunks: ['chunk-vendors', 'chunk-common', tmp[1]]
    }
  })
  return entries
}

const pages = getEntry('./src/pages/**?/*.html')

module.exports = {
  pages,
  publicPath: '/',
  outputDir: 'dist',
  assetsDir: 'assets',
  filenameHashing: true,
  lintOnSave: true,
  productionSourceMap: false,
  devServer: {
    index: 'index.html', // 默认启动serve 打开的页面
    host: '0.0.0.0',
    port: 8889,
    https: false,
    hotOnly: false,
    proxy: null
  },
  chainWebpack: config => {
    Object.keys(pages).forEach(entryName => {
      // 配置动态引入js、css
      config
        .plugin(`html-${entryName}`)
        .tap(args => {
          args[0].cdn = []
          return args
        })

      config.plugins.delete(`prefetch-${entryName}`)
    })
  }
}

运行-打包

上述配置运行 npm run serve 会运行所有应用

+ index 访问 `http://localhost:8889/#/`
+ page1 访问 `http://localhost:8889/page1/#/`
+ page2 访问 `http://localhost:8889/page2/#/`

image.png

上述的配置运行 运行npm run build 会打包所有应用

image.png

  • 打包后访问应用(使用node库 http-server), 默认会访问index.html 所以除了 index 外其他应用访问需要指定 html文件
    • index 访问 http://localhost:8889/#/
    • page1 访问 http://localhost:8889/page1.html/#/
    • page2 访问 http://localhost:8889/page2.html/#/
  • 所以使用路径做一些判断的时候需要自行处理 比如 使用字符换替换html 等无关字符。

image.png

打包单一应用-以page1文件夹为例

  • 多个页面 pages 多个配置,同理可得 单个应用 pages 只有一个配置。
  • 这样也就需要有一个配置的地方,这里使用env文件配置。

image.png

  • 根目录新建 .env、.env.dev_page1、.env.pro_page1 并写入对应数据。
    • .env 所有环境都被载入 在默认打包所有应用时使用
    • .env.dev_page1 dev 本地开发时加载
    • .env.pro_page1 pro 打包后加载

image.png

  • 更改 package.jsonscripts配置

image.png

image.png

优化

  • 上述 动态配置js,css写死了两示例,同样可以将数据配置在 .env文件中,指定环境加载指定文件
  • 多个入口js文件,引入文件 配置重复,可以提取出一个公共的js,导出一个初始化 vue 的函数,将store、router传进去做初始化。
  • 其他压缩打包体积等不在 本文讨论范围 (各位想必都有一手绝活!!!)

参考文章

+掘金小册 Vue 项目构建与开发入门