真香!vue-cli-service项目升级vite

2023-01-09  本文已影响0人  月上秦少
095afad2-2fff-4dc5-88bb-9366d8abcf01.png

原文:真香!vue-cli-service项目升级vite

1、项目概览

image image image

admin-web管理后台是我们业务的管理中心,开发、运营、产品等工作人员会频繁使用,是我们目前业务的核心配置中心,这个项目从建立之初一直更新迭代,迄今为止有655次commits,681
次CI,可见更新次数还是很频繁的。随着日积月累,代码量越来越多,vue单页面已经有了100+ pages,30+ vuecomponents,因此启动速度越来越慢。

2、vue-cli-service和vite启动速度对比

将vue-cli-service升级切换到vite之后启动速度得到了巨幅提升,下面是对比结果:

1.使用vue-cli-service serve启动:

image

2.使用vite启动:

image

可见启动速度提升差不多10倍,平均每次启动节省约13s。

下面介绍项目改造过程:

3、项目概览

1、目录结构

改造前的目录结构,使用tree -aL 3 -I "node_modules" > tree.txt输出:

.
├── .editorconfig
├── .env.development
├── .env.production
├── .env.staging
├── .eslintignore
├── .eslintrc.js
├── .git
├── .gitignore
├── .gitlab-ci.yml
├── .idea
├── .travis.yml
├── Dockerfile.prod
├── Dockerfile.test
├── LICENSE
├── README.md
├── babel.config.js
├── build
├── dist
├── docker-compose.yml
├── jest.config.js
├── jsconfig.json
├── mock
├── nginx.conf
├── package.json
├── plopfile.js
├── postcss.config.js
├── public
│     ├── favicon.ico
│     └── index.html
├── scripts
│     └── notify.sh
├── src
│     ├── App.vue
│     ├── api
│     │   ├── activity.js
│     │   ├── config.js
│...      ...
│     ├── assets
│     ├── components
│     │   └── VideoPlay
│     ├── directive
│     ├── clipboard
│     ├── el-drag-dialog
│     ├── permission
│     ├── filters
│     ├── icons
│     │     ├── index.js
│     │     ├── svgo.yml
│           │           └── svg # 多个svg图标
│     │             ├── 404.svg
│...                    ...
│     │                     └── zip.svg
│     ├── layout
│     ├── main.js
│     ├── permission.js
│     ├── router
│     │     ├── activity.js
│     │     ├── config.js
│...         ...
│     ├── settings.js
│     ├── store
│     │     ├── getters.js
│     │     ├── index.js
│           │           └── modules # 多个store
│     │             ├── app.js
│...                    ...
│     │                     └── user.js
│     ├── store
│     ├── styles
│     ├── utils
│     └── views
│           ├── activity
│...         ...
│           └── withdrawal
├── vue.config.js
└── yarn.lock

使用vite改造后的目录:

.
├── .editorconfig
├── .env.development
├── .env.production
├── .env.staging
├── .eslintignore
├── .eslintrc.js
├── .git
├── .gitignore
├── .gitlab-ci.yml
├── .idea
├── .travis.yml
├── Dockerfile.prod
├── Dockerfile.test
├── LICENSE
├── README.md
├── babel.config.js
├── build
├── dist
├── docker-compose.yml
├── index.html  # 新增
├── jest.config.js
├── jsconfig.json
├── mock
├── nginx.conf
├── package.json
├── plopfile.js
├── postcss.config.js
├── public
│     ├── favicon.ico
│     └── index.html
├── scripts
│     └── notify.sh
├── src
│     ├── App.vue
│     ├── api
│     │   ├── activity.js
│     │   ├── config.js
│...      ...
│     ├── assets
│     ├── components
│     │   └── VideoPlay
│     ├── directive
│     ├── clipboard
│     ├── el-drag-dialog
│     ├── permission
│     ├── filters
│     ├── icons
│     │     ├── index.js
│     │     ├── svgo.yml
│           │           └── svg # 多个svg图标
│     │             ├── 404.svg
│...                    ...
│     │                     └── zip.svg
│     ├── layout
│     ├── main.js
│     ├── permission.js
│     ├── router
│     │     ├── activity.js
│     │     ├── config.js
│...         ...
│     ├── settings.js
│     ├── store
│     │     ├── getters.js
│     │     ├── index.js
│           │           └── modules # 多个store
│     │             ├── app.js
│...                    ...
│     │                     └── user.js
│     ├── styles
│     ├── utils
│     └── views
│           ├── activity
│...         ...
│           └── withdrawal
├── vite.config.js # 新增
├── vue.config.js
└── yarn.lock

新增了两个文件:vite.config.js和 index.html

2、node package包

改造前:package.json

{
  "name": "admin-web",
  "version": "1.0.0",
  "description": "管理后台系统",
  "scripts": {
    "dev": "vue-cli-service serve",
    "lint": "eslint --ext .js,.vue src",
    "build:prod": "vue-cli-service build",
    "build:stage": "vue-cli-service build --mode staging",
    "preview": "node build/index.js --preview",
    "new": "plop",
    "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
    "test:unit": "jest --clearCache && vue-cli-service test:unit",
    "test:ci": "npm run lint && npm run test:unit"
  },
  "dependencies": {
    "axios": "0.18.1",
    "clipboard": "2.0.4",
    "codemirror": "5.45.0",
    "core-js": "^3.6.5",
    "driver.js": "0.9.5",
    "dropzone": "5.5.1",
    "echarts": "4.2.1",
    "element-ui": "^2.15.7",
    "file-saver": "2.0.1",
    "fuse.js": "3.4.4",
    "js-cookie": "2.2.0",
    "jszip": "3.2.1",
    "md5": "2.2.1",
    "normalize.css": "7.0.0",
    "nprogress": "0.2.0",
    "path-to-regexp": "2.4.0",
    "screenfull": "4.2.0",
    "script-loader": "0.7.2",
    "sortablejs": "1.8.4",
    "tui-editor": "1.3.3",
    "v-viewer": "^1.6.4",
    "video.js": "^7.18.1", # 删除,使用cdn
    "videojs-contrib-hls": "^5.15.0",  # 删除,使用@videojs/http-streaming cdn代替
    "vue": "2.6.10",
    "vue-count-to": "1.0.13",
    "vue-router": "3.0.2",
    "vue-splitpane": "1.0.4",
    "vuedraggable": "2.20.0",
    "vuex": "3.1.0",
    "xlsx": "0.14.1"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "4.4.4",
    "@vue/cli-plugin-eslint": "4.4.4",
    "@vue/cli-plugin-unit-jest": "4.4.4",
    "@vue/cli-service": "4.4.4",
    "@vue/test-utils": "1.0.0-beta.29",
    "autoprefixer": "9.5.1",
    "babel-eslint": "10.1.0",
    "babel-jest": "23.6.0",
    "babel-plugin-dynamic-import-node": "2.3.3",
    "chalk": "2.4.2",
    "chokidar": "2.1.5",
    "connect": "3.6.6",
    "eslint": "6.7.2",
    "eslint-plugin-vue": "6.2.2",
    "html-webpack-plugin": "3.2.0",
    "husky": "1.3.1",
    "lint-staged": "8.1.5",
    "mockjs": "1.0.1-beta3",
    "plop": "2.3.0",
    "runjs": "4.3.2",
    "sass": "1.33.0",
    "sass-loader": "8.0.2",
    "script-ext-html-webpack-plugin": "2.1.3",
    "serve-static": "1.13.2",
    "svg-sprite-loader": "4.1.3",
    "svgo": "1.2.0",
    "vue-template-compiler": "2.6.10"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ],
  "engines": {
    "node": ">=8.9",
    "npm": ">= 3.0.0"
  },
  "lint-staged": {
    "src/**/*.{js,vue}": [
      "eslint --fix",
      "git add"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  }
}

改造后:(针对vue2项目使用vite2.x版本

{
  "name": "admin-web",
  "version": "2.0.0",
  "description": "管理后台系统",
  "scripts": {
    "dev": "vue-cli-service serve",
    "lint": "eslint --ext .js,.vue src",
    "lint:fix": "eslint --ext .js,.vue src --fix",
    "build:prod": "vue-cli-service build",
    "build:stage": "vue-cli-service build --mode staging",
    "preview": "node build/index.js --preview",
    "new": "plop",
    "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
    "test:unit": "jest --clearCache && vue-cli-service test:unit",
    "test:ci": "npm run lint && npm run test:unit",
    "dev-vite": "vite", # 新增
    "build-vite": "vite build",  # 新增
    "preview-vite": "vite preview"  # 新增
  },
  "dependencies": {
    "axios": "0.18.1",
    "clipboard": "2.0.4",
    "codemirror": "5.45.0",
    "core-js": "^3.6.5",
    "driver.js": "0.9.5",
    "dropzone": "5.5.1",
    "echarts": "4.2.1",
    "element-ui": "^2.15.7",
    "file-saver": "2.0.1",
    "fuse.js": "3.4.4",
    "js-cookie": "2.2.0",
    "jszip": "3.2.1",
    "md5": "2.2.1",
    "normalize.css": "7.0.0",
    "nprogress": "0.2.0",
    "path-browserify": "^1.0.1",
    "path-to-regexp": "2.4.0",
    "screenfull": "4.2.0",
    "script-loader": "0.7.2",
    "sortablejs": "1.8.4",
    "tui-editor": "1.3.3",
    "v-viewer": "^1.6.4",
    "vue": "2.6.10",
    "vue-count-to": "1.0.13",
    "vue-router": "3.0.2",
    "vue-splitpane": "1.0.4",
    "vuedraggable": "2.20.0",
    "vuex": "3.1.0",
    "xlsx": "0.14.1"
  },
  "devDependencies": {
    "@babel/plugin-syntax-jsx": "^7.18.6", # 新增
    "@originjs/vite-plugin-require-context": "^1.0.9", # 新增
    "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", # 新增
    "@vue/babel-preset-jsx": "^1.4.0", # 新增
    "@vue/cli-plugin-babel": "4.4.4",
    "@vue/cli-plugin-eslint": "4.4.4",
    "@vue/cli-plugin-unit-jest": "4.4.4",
    "@vue/cli-service": "4.4.4",
    "@vue/test-utils": "1.0.0-beta.29",
    "autoprefixer": "9.5.1",
    "babel-eslint": "10.1.0",
    "babel-jest": "23.6.0",
    "babel-plugin-dynamic-import-node": "2.3.3",
    "chalk": "2.4.2",
    "chokidar": "2.1.5",
    "connect": "3.6.6",
    "eslint": "6.7.2",
    "eslint-plugin-vue": "6.2.2",
    "html-webpack-plugin": "3.2.0",
    "husky": "1.3.1",
    "lint-staged": "8.1.5",
    "mockjs": "1.0.1-beta3",
    "plop": "2.3.0",
    "progress-bar-webpack-plugin": "^2.1.0", # 新增(可忽略),查看vue-cli-service构建速度
    "runjs": "4.3.2",
    "sass": "1.33.0",
    "sass-loader": "8.0.2",
    "script-ext-html-webpack-plugin": "2.1.3",
    "serve-static": "1.13.2",
    "svg-sprite-loader": "4.1.3",
    "svgo": "1.2.0",
    "vite": "^2.9.15", # 新增
    "vite-plugin-commonjs": "^0.6.1", # 新增
    "vite-plugin-components": "^0.13.2", # 新增
    "vite-plugin-env-compatible": "^1.1.1", # 新增
    "vite-plugin-optimize-persist": "^0.1.2", # 新增
    "vite-plugin-package-config": "^0.1.1", # 新增
    "vite-plugin-svg-icons": "^2.0.1", # 新增
    "vite-plugin-vue2": "^2.0.3", # 新增
    "vue-template-compiler": "2.6.10"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ],
  "bugs": {
    "url": "https://github.com/PanJiaChen/vue-element-admin/issues"
  },
  "engines": {
    "node": ">=8.9",
    "npm": ">= 3.0.0"
  },
  "lint-staged": {
    "src/**/*.{js,vue}": [
      "eslint --fix",
      "git add"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  }
}

3、入口文件index.html

  1. 位置变换:

root/public/index.htmlroot/index.html

  1. 内容变化:

改造前:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="renderer" content="webkit">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">
  <title><%= webpackConfig.name %></title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

改造后:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="renderer" content="webkit">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/favicon.ico"/>
  <link href="https://unpkg.com/video.js/dist/video-js.css" rel="stylesheet">
  <title>melon admin</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<script type="module" src="/src/main.js"></script>
<script src="https://unpkg.com/video.js/dist/video.js"></script>
<script src="https://unpkg.com/@videojs/http-streaming/dist/videojs-http-streaming.js"></script>
</body>
</html>

4.新增vite配置文件

vite.config.js

import {defineConfig} from 'vite'
import commonjs from 'vite-plugin-commonjs'
import ViteRequireContext from '@originjs/vite-plugin-require-context'
import viteComponents, {
  VuetifyResolver
} from 'vite-plugin-components'
import envCompatible from 'vite-plugin-env-compatible'
import OptimizationPersist from 'vite-plugin-optimize-persist'
import PkgConfig from 'vite-plugin-package-config'
import {createSvgIconsPlugin} from 'vite-plugin-svg-icons'
import {createVuePlugin} from 'vite-plugin-vue2'

import path from 'path'

// const { createVuePlugin } = require('vite-plugin-vue2')
// const path = require('path')
const REPLACEMENT = `${path.resolve(__dirname, './src')}/`
export default defineConfig({
    server: {
      host: '0.0.0.0',
      https: false,
      port: 8088,
      proxy: {
        '/melon.admin.s': {
          // 路径中有 /api 的请求都会走这个代理 , 可以自己定义一个,下面移除即可
          target: 'https://xxx.xxx.com', // 目标代理接口地址,实际跨域要访问的接口,这个地址会替换掉 axios.defaults.baseURL
          // target: 'http://localhost:9000', // 目标代理接口地址,实际跨域要访问的接口,这个地址会替换掉 axios.defaults.baseURL
          secure: false,
          changeOrigin: true, // 开启代理,在本地创建一个虚拟服务端
          ws: true //,       // 是否启用  websockets;
        }
      }
    },
    resolve: {
      alias: [
        {
          find: '@/',
          replacement: REPLACEMENT
        },
        {
          find: 'src/',
          replacement: REPLACEMENT
        },
        {
          find: /^~@\//,
          replacement: REPLACEMENT
        }
      ],
      extensions: ['.vue', '.js', '.jsx', '.mjs', '.ts', '.tsx', '.json', '.css', '.scss']
    },
    plugins: [
      createVuePlugin({jsx: true}),
      viteComponents({
        customComponentResolvers: [
          VuetifyResolver()
        ]
      }),
      createSvgIconsPlugin({
        iconDirs: [path.resolve(__dirname, './src/icons/svg')],
        symbolId: 'icon-[dir]-[name]'
      }),
      commonjs(/* options */),
      ViteRequireContext(/* options */),
      envCompatible(),
      PkgConfig(),
      OptimizationPersist()
    ]
  }
)

至于为什么要这样配置,当然是一步步踩坑,填坑至此,说多了都是泪啊

5、切换vite问题汇总

1.页面显示:找不到此 localhost 页面

需要把index.html从public文件夹移动到root目录下

2.'URI malformed'报错,标题显示<%= webpackConfig.name %>

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="renderer" content="webkit">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">
  <link href="https://unpkg.com/video.js/dist/video-js.css" rel="stylesheet">
  <title><%= webpackConfig.name %></title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

需要切换路径,<%= webpackConfig.name %>和<%= BASE_URL %>webpack能识别,vite识别不了,修改如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="renderer" content="webkit">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <!--    public下的文件使用路径即可-->
  <link rel="icon" href="/favicon.ico"/>
  <!--    标题直接配置在儿-->
  <title>admin web</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

3.访问成功,单页面空白,控制台无任何信息

vite以esm的形式加载js文件,需要手动引入一下。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="renderer" content="webkit">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <!--    public下的文件使用路径即可-->
  <link rel="icon" href="/favicon.ico"/>
  <link href="https://unpkg.com/video.js/dist/video-js.css" rel="stylesheet">
  <!--    标题直接配置在儿-->
  <title>melon admin</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<!--    以module的模式加载main.js-->
<script type="module" src="/src/main.js"></script>
</body>
</html>

如果继续空白,先把报错的代码注释掉,面向谷歌编程,一个个解决就完事了

4.vite无法识别alias路径

[plugin:vite:import-analysis] Failed to resolve import "@/styles/index.scss" from "src\main.js". Does the file exist?

加载alias文件路径失败,需要配置alias,vite才能识别

import {defineConfig} from 'vite'

import path from 'path'

const REPLACEMENT = `${path.resolve(__dirname, './src')}/`
export default defineConfig({
  resolve: {
    alias: [
      {
        find: '@/',
        replacement: REPLACEMENT
      }
    ]
  }
})

5.vite无法自动查询文件后缀名,文件Not Found

GET http://localhost:8089/src/layout net::ERR_ABORTED 404 (Not Found)

import Layout from '/src/layout

直接导入某个目录下的index.js、index.css、index.json时可以省略具体的文件名,让webpack自动查找,vite无法识别,需要配置****extensions:

import {defineConfig} from 'vite'

import path from 'path'

const REPLACEMENT = `${path.resolve(__dirname, './src')}/`
export default defineConfig({
  resolve: {
    alias: [
      {
        find: '@/',
        replacement: REPLACEMENT
      }
    ],
    extensions: ['.vue', '.js', '.jsx', '.mjs', '.ts', '.tsx', '.json', '.css', '.scss']
  }
})

6.vite识别不了 jsx, Uncaught ReferenceError: React is not defined

需要配置jsx插件:

import {defineConfig} from 'vite'
import {createVuePlugin} from 'vite-plugin-vue2'

export default defineConfig({
  plugins: [
    createVuePlugin({jsx: true}),
  ]
})

7.jsx语法未注明:[plugin:vite-plugin-commonjs] Unexpected token

if (icon) {
19 |        if (icon.includes('el-icon')) {
20 |          vnodes.push(<i class={[icon, 'sub-el-icon']} />)
   |                      ^
21 |        } else {
22 |          vnodes.push(<svg-icon icon-class={icon}/>)}

<script>标签下的js中包含jsx的语法需要注明:<script lang="jsx">

9.vite无法识别require: Uncaught ReferenceError: require is not defined

commonjs使用require来加载js,vite直接加载esm,vite无法识别require,解决方式有二:

  1. 把commonjs的require方式改为esm的import方式

  2. 使用插件使vite支持require:

import {defineConfig} from 'vite'
import commonjs from 'vite-plugin-commonjs'

export default defineConfig({
  plugins: [
    createVuePlugin({jsx: true}),
    commonjs(/* options */),
    // ...  ...
  ]
})

当然****module.exports****定义的js文件强烈建议修改为****export default****拥抱ESM吧

8.动态加载require无法识别:Uncaught ReferenceError: require is not defined at index.js:8:22

1.store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'

Vue.use(Vuex)

// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)

// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // set './app.js' => 'app'
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

const store = new Vuex.Store({
  modules,
  getters
})

export default store

这里使用了webpack的requirecontext来读取store/modules目录下的多个store,否则需要一行行手动引入。

2.icons/index.js

import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg component

// register globally
Vue.component('svg-icon', SvgIcon)

const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

这里使用了webpack的requirecontext来读取icons/svg目录下的多个icon,否则需要一行行手动引入。

1.解决方式一:require.context相关代码删除,然后手动一个个引入import testIcon from "./svg/test.svg",但是太过于繁琐,增加好多代码量。

2.解决方式二:使用vite插件@originjs/vite-plugin-require-context,让vite可以使用识别require.context

import {defineConfig} from 'vite'
import ViteRequireContext from '@originjs/vite-plugin-require-context'

export default defineConfig({
  plugins: [
    createVuePlugin({jsx: true}),
    ViteRequireContext(/* options */)
    // ...  ...
  ]
})

9.svgo图标无法显示

切换vite前,正常显示:

image

使用vite后图标不见了:

image

这里是我解决requirerequire.context之后正常加载时的页面,这个两个问题如果没有解决的话,页面是直接进入不了的。所以前提是把这两个问题解决了才行。

打开控制台发现js并没有报错,只是svg没有成功显示的问题。


<div data-v-4441ce11="">
  <div data-v-4441ce11="" class="el-tooltip icon-item" aria-describedby="el-tooltip-8235" tabindex="0">
    <svg data-v-c8a70580="" data-v-4441ce11="" aria-hidden="true" class="svg-icon disabled">
      <use data-v-c8a70580="" xlink:href="#icon-404"></use>
    </svg>
    <span data-v-4441ce11="">404</span>
  </div>
</div>

可以看到这里使用了svg<use> - SVG: Scalable Vector Graphics | MDN (mozilla.org)

搜索svg:use vite,找到解决方案在vite中使用svg(vue) - 掘金 (juejin.cn)

借助vite插件vite-plugin-svg-icons使vite可以完美支持svg:use:

import {defineConfig} from 'vite'
import {createSvgIconsPlugin} from 'vite-plugin-svg-icons'

export default defineConfig({
    plugins: [
      createVuePlugin({jsx: true}),
      createSvgIconsPlugin({
        iconDirs: [path.resolve(__dirname, './src/icons/svg')],
        symbolId: 'icon-[dir]-[name]'
      }),
      // ...    ...
    ]
  }
)

10.vite无法识别scss变量导入

报错error和代码:

    [plugin:vite:import-analysis] Cannot read properties of undefined (reading 'url')
    D:/work/gitlab/admin-web/src/styles/element-variables.scss
    
    import variables from '@/styles/element-variables.scss'
    @import "~element-ui/packages/theme-chalk/src/index";

这里需要修改为*.module.scss功能 | Vite 官方中文文档 (vitejs.dev)

其实在解决这个问题之前,页面的主题配置还有一些和变量相关的样式都是混乱的,修复这个问题之后瞬间一片清明!!!

11.videojs报错导致Maximum call stack size exceeded

1.使用vite之后videojs-contrib-hls(用来播放m3u8视频的videojs插件)报错:

image

2.注释掉videojs-contrib-hls,只要打开引入了videojs组件的页面就卡住了:

image

3.然后我尝试使用CDN的方式引入,视频可以成功播放了,但是还是有error

image

index.html增加cdn

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="renderer" content="webkit">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/favicon.ico"/>
  <!-- 新增 -->
  <link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.21.1/alt/video-js-cdn.min.css" rel="stylesheet"/>
  <title>melon admin</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<script type="module" src="/src/main.js"></script>
<!-- 新增 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.21.1/video.min.js"></script>
<!-- 新增 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/videojs-contrib-hls/5.15.0/videojs-contrib-hls.min.js"></script>
</body>
</html>

VideoPlay.vue


<template>
  <div>
    <div class="VideoPlay">
      <video
          :id="`video-${videoId}`"
          ref="videoRef"
          class="video-js vjs-default-skin vjs-big-play-centered"
          muted
          plays-inline
          controls
          preload="auto"
          height="400px"
          width="100%"
      />
    </div>
  </div>
</template>

<script>
// 注释掉videojs npm导入
// import videojs from 'video.js'
// import 'video.js/dist/video-js.css'
// import 'videojs-contrib-hls'

const config = {
  bigPlayButton: true,
  textTrackDisplay: false,
  posterImage: true,
  errorDisplay: false,
  controlBar: {
    currentTimeDisplay: true, // 当前时间
    timeDivider: true, // 时间分割线
    durationDisplay: true, // 总时间
    progressControl: true, // 进度条
    remainingTimeDisplay: {
      displayNegative: true
    }, //
    fullscreenToggle: true // 全屏按钮
  },
  playbackRates: [0.8, 1, 1.25, 1.5, 2],
  html5: {
    vhs: {
      withCredentials: false
    }
  }
}

export default {
  name: 'VideoPlay',
  props: {
    videoId: {
      default: Math.random().toString().slice(2, 6),
      type: [String, Number]
    },
    videoSrc: {
      default: '',
      type: String
    }
  },
  data() {
    return {
      videoPlayerInstance: null,
    }
  },
  mounted() {
    try {
      this.videoSrc && this.getVideoPlayInstance()
    } catch (e) {
      console.error('init video')
    }
  },
  destroyed() {
    try {
      this.videoPlayerInstance && this.videoPlayerInstance.dispose()
    } catch (e) {
      console.error('destroyed video', e)
    }
  },
  methods: {
    setVideo(id, source) {
      try {
        config.sources = [{
          type: 'application/x-mpegURL',
          src: source
        }]
        // const instance = videojs(
        // 使用全局window.videojs
        const instance = window.videojs(
            id,
            config,
            () => {
              instance.play()
            })
        return instance
      } catch (e) {
        console.error('setVideo', e)
      }
    },
    getVideoPlayInstance() {
      if (!this.videoPlayerInstance) {
        this.videoPlayerInstance = this.setVideo(`video-${this.videoId}`, this.videoSrc)
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.VideoPlay {
  width: 100%;
  height: 500px;

  .video-js {
    width: 100%;
    height: 100%;
  }
}

.audioPlay {
  width: 100%;
  height: 30px;

  .video-js {
    width: 100%;
    height: 100%;
    background: transparent;
  }
}
</style>

4.解决第3步的报错,切换了其他版本还是有这个错误,于是打算把videojs-contrib-hls源码clone一份看看报错的地方能不能修改下源码,发现README.md

image

Video.js Blog | Video.js (videojs.com)博客显示videojs7使用videojs/http-streaming插件来替代videojs-contrib-hls

videojs/http-streaming: HLS, DASH, and future HTTP streaming protocols library for video.js

刚巧项目中的videojs也是7.x版本,于是我切换了videojs/http-streamingCDN,看了官方DEMO,需要把标签video替换为video-js,成功解决error,但是多了一个warning,
问题不大

image

这个就先忍了吧_

image

最终的index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="renderer" content="webkit">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/favicon.ico"/>
  <link href="https://unpkg.com/video.js/dist/video-js.css" rel="stylesheet">
  <title>melon admin</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<script type="module" src="/src/main.js"></script>
<script src="https://unpkg.com/video.js/dist/video.js"></script>
<script src="https://unpkg.com/@videojs/http-streaming/dist/videojs-http-streaming.js"></script>
</body>
</html>

在解决这个问题之前,我内心是崩溃的,找了诸多方式都无法解决,就差一点儿就放弃了,行百里者半九十,我坚持找解决方案,最终终于成功了,点个赞!

为了渐进升级,目前保留`vue-cli-service`和`vite`共存,两者都可以使用,后续可以把`vue-cli-service`相关的依赖和代码删除即可

6.参考文档

上一篇下一篇

猜你喜欢

热点阅读