使用Vue-CLI怎么實現(xiàn)多頁分目錄打包

背景

使用VUE搭建多頁面應用,實現(xiàn)公司共享頁面的需求。

設計思想

所有系統(tǒng)都在同一目錄下,配置多入口多出口。各系統(tǒng)間可以鏈接,但是各系統(tǒng)內部依然采用SPA模式開發(fā)。

復用性

將所有系統(tǒng)的公共組件和方法放在系統(tǒng)目錄的最外層,以達到復用的目的。在系統(tǒng)內部依然可以單獨封裝私有組件,這樣可以最大限度的提高組件的復用性。

路由

各系統(tǒng)單獨進行路由配置

數(shù)據管理

各系統(tǒng)數(shù)據倉庫單獨處理

整體目錄結構

為了便于打包,我們創(chuàng)建一個views的文件夾,在其下創(chuàng)建子文件夾代表每個應用系統(tǒng)。每個子文件夾中建立各自的spa應用體系,這樣做的好處是,我們在配置webpack的打包入口時,比較好操作,而且這樣的結構也較為清晰。


image.png

下面以example為例展開說明:

image.png

注意需要將默認的 html 模板文件 public/index.html 移動到根目錄下。

目錄 釋義
build 項目構建(webpack)相關代碼,dev-modules.js用于自定義本地開發(fā)時需要編譯的模塊,index.js用于配置執(zhí)行構建命令,循環(huán)執(zhí)行 vue-cli-service buildpages.js用于獲取vue-cli需要的多頁對象
public 公共資源目錄
src 源碼目錄
.editorconfig 定義代碼格式
.env.all 打包所有文件的開發(fā)環(huán)境配置
.env.development 默認打包的開發(fā)環(huán)境配置
.env.production 生產環(huán)境配置
.env.test 測試環(huán)境配置
.eslintrc.js eslint配置
.gitignore git上傳需要忽略的文件格式
babel.config.js ES6語法編譯配置
package.json 項目基本信息,包依賴信息等
vue.config.js webpack配置
dist 項目打包后產生的目錄

src目錄說明

目錄 目錄釋義
assets 靜態(tài)資源目錄
components 公共組件庫
filters 公共過濾器
styles 公共樣式表
utils 公共方法文件
views 各系統(tǒng)spa應用體系

src/views/example example目錄說明

目錄 目錄釋義
api example api文件
components example組件庫
pages example頁面資源庫
router example路由配置
store example數(shù)據管理文件
utils example公共方法文件
App.vue example入口文件
example.html example入口頁面模板
example.js example核心文件

打包方式

各系統(tǒng)模塊應用獨立打包

打包后的整體資源路徑

目錄 目錄釋義
example example資源文件夾

打包后的各文件夾內資源路徑,以example為例:

目錄 目錄釋義
img example圖片資源文件夾
js example js資源文件夾
favicon.ico example應用的瀏覽器圖標
index.html example頁面入口

依賴

// package.json
{
  "name": "object-name",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "serve:all": "vue-cli-service serve --mode all",
    "build:test": "node build/index.js",
    "build:prod": "node build/index.js",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "tasksfile": "^5.1.1",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/eslint-config-standard": "^5.1.2",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.0",
    "eslint-plugin-vue": "^6.2.2",
    "lint-staged": "^9.5.0",
    "node-sass": "^4.12.0",
    "sass-loader": "^8.0.2",
    "vue-template-compiler": "^2.6.11"
  },
  "engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "@vue/standard"
    ],
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ],
  "gitHooks": {
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "*.{js,jsx,vue}": [
      "vue-cli-service lint",
      "git add"
    ]
  }
}

獲取vue cli需要的多頁對象

// build/pages.js
const path = require('path')
const glob = require('glob')
const fs = require('fs')

const isProduction = process.env.NODE_ENV === 'production'

// 自定義不同模塊的頁面 title
const titleMap = {
  index: '首頁'
}

function getPages (globPath) {
  const pages = {}
  glob.sync(globPath).forEach((item) => {
    const stats = fs.statSync(item)
    if (stats.isDirectory()) {
      const basename = path.basename(item, path.extname(item))
      const template = `${item}/${basename}.html`

      pages[basename] = {
        entry: `${item}/${basename}.js`,
        title: titleMap[basename] || '默認頁面',
        template,
        // 兼容開發(fā)和生產時 html 頁面層級一致
        filename: isProduction ? 'index.html' : `${basename}/index.html`
      }
    }
  })
  return pages
}

const pages = getPages(path.join(__dirname, '../src/views/*'))

module.exports = pages

執(zhí)行構建命令,循環(huán)執(zhí)行 vue-cli-service build

// build/index.js
const chalk = require('chalk')
const rimraf = require('rimraf')
const { sh } = require('tasksfile')

const PAGES = require('./pages')

// vue-cli-service --mode 值
const mode = process.env.MODE || 'prod'

// 模塊名,可能為多個
const moduleNames = process.argv[2]

// 全部頁面列表
const pageList = Object.keys(PAGES)

// 有效模塊列表 未指定則為全部頁面列表
const validPageList = moduleNames ? moduleNames.split(',').filter((item) => pageList.includes(item)) : pageList
if (!validPageList.length) {
  console.log(chalk.red('**模塊名不正確**'))
  return
}

console.log(chalk.blue(`有效模塊:${validPageList.join(',')}`))

// 刪除 dist 目錄
rimraf.sync('dist')

console.time('總編譯時間')
const count = validPageList.length
let current = 0
// 逐個執(zhí)行模塊編譯
for (let i = 0; i < validPageList.length; i += 1) {
  const moduleName = validPageList[i]
  process.env.MODULE_NAME = moduleName

  console.log(chalk.blue(`${moduleName} 模塊開始編譯`))

  // 通過 vue-cli-service build 編譯
  sh(`vue-cli-service build --mode ${mode}`, { async: true }).then(() => {
    console.log(chalk.blue(`${moduleName} 模塊編譯完成`))
    console.log()
    current += 1
    if (current === count) {
      console.log(chalk.blue('-----全部模塊編譯完成-----'))
      console.timeEnd('總編譯時間')
    }
  })
}

自定義本地開發(fā)時需要編譯的模塊,模塊名為 src/pages 下的文件夾名

// dev-modules.js
// 本地開發(fā)需要編譯的模塊
module.exports = [
    'example'
]

vue.config.js

// vue.config.js
const chalk = require('chalk')
const devModuleList = require('./build/dev-modules')
const isProduction = process.env.NODE_ENV === 'production'
// 總的頁面
const PAGES = require('./build/pages')

for (const basename in PAGES) {
  if (Object.prototype.hasOwnProperty.call(PAGES, basename)) {
    PAGES[basename].chunks = [
      'chunk-vue',
      'chunk-vendors',
      'chunk-common',
      `${basename}`
    ]
  }
}
let pages = {}
const moduleName = process.env.MODULE_NAME
if (isProduction) {
  // 構建模塊的名稱
  if (!PAGES[moduleName]) {
    console.log(chalk.red('**模塊名不正確**'))
    return
  }
  pages[moduleName] = PAGES[moduleName]
} else {
  // 本地開發(fā)編譯的模塊
  // 編譯全部
  if (process.env.DEV_MODULE === 'all') {
    pages = PAGES
  } else {
    // 編譯部分模塊
    const moduleList = [
      'index',  // 固定編譯的模塊
      ...devModuleList  // 自定義編譯的模塊
    ]
    moduleList.forEach(item => {
      pages[item] = PAGES[item]
    })
  }
}

module.exports = {
  // 這行代碼很重要
  publicPath: isProduction ? './' : '/',
  pages,
  // 這行代碼很重要
  outputDir: isProduction ? `dist/${moduleName}` : 'dist',
  productionSourceMap: false,
  chainWebpack: (config) => {
    config.optimization.splitChunks({
      cacheGroups: {
        vue: {
          name: 'chunk-vue',
          test: /[\\/]node_modules[\\/]_?(vue|vue-router|vuex)(@.*)?[\\/]/,
          priority: -1,
          chunks: 'initial'
        },
        vendors: {
          name: 'chunk-vendors',
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        common: {
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    })
  }
}

多頁面路由配置

const router = new VueRouter({
  mode: 'history',
  base: '/example/',    // 注意這里
  routes
})

多頁面部署到服務器報錯

Uncaught (in promise) Error: Error
解決方案:將vue-router升級為最新

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容