🌙 vue-cli-service项目升级vite
🌙 
🌙 1、项目概览
admin-web管理后台是我们业务的管理中心,开发、运营、产品等工作人员会频繁使用,是我们目前业务的核心配置中心,这个项目从建立之初一直更新迭代,迄今为止有655次commits,681 次CI,可见更新次数还是很频繁的。随着日积月累,代码量越来越多,vue单页面已经有了100+ pages,30+ vuecomponents,因此启动速度越来越慢。
项目源自:https://github.com/PanJiaChen/vue-element-admin/
🌙 2、vue-cli-service和vite启动速度对比
将vue-cli-service升级切换到vite之后启动速度得到了巨幅提升,下面是对比结果:
1.使用vue-cli-service serve启动:
平均耗时:15s
最大耗时:40s
最小耗时:10s
2.使用vite启动:
平均耗时:2s
最大耗时:5s
最小耗时:1s
可见启动速度提升差不多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"
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
改造后:(针对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"
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
🌙 3、入口文件index.html
- 位置变换:
由 root/public/index.html
到 root/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="<%= BASE_URL %>favicon.ico">
<title><%= webpackConfig.name %></title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
改造后:
<!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>web 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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
🌙 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: {
'/api': {
// 路径中有 /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()
]
}
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
至于为什么要这样配置,当然是一步步踩坑,填坑至此,说多了都是泪啊
🌙 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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
需要切换路径,<%= 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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
🌙 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>web 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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
如果继续空白,先把报错的代码注释掉,面向谷歌编程,一个个解决就完事了
🌙 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
}
]
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🌙 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']
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
🌙 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}),
]
})
2
3
4
5
6
7
8
9
🌙 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,解决方式有二:
把commonjs的require方式改为esm的import方式
使用插件使vite支持require:
import {defineConfig} from 'vite'
import commonjs from 'vite-plugin-commonjs'
export default defineConfig({
plugins: [
createVuePlugin({jsx: true}),
commonjs(/* options */),
// ... ...
]
})
2
3
4
5
6
7
8
9
10
11
当然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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
这里使用了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)
2
3
4
5
6
7
8
9
这里使用了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 */)
// ... ...
]
})
2
3
4
5
6
7
8
9
10
🌙 9.svgo图标无法显示
切换vite前,正常显示:
使用vite后图标不见了:
这里是我解决
require
和require.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>
2
3
4
5
6
7
8
9
可以看到这里使用了svg:Scalable Vector Graphics | MDN(mozilla.org) (opens new window)
搜索_svg:use vite,_找到解决方案在vite中使用svg(vue) - 掘金 (juejin.cn) (opens new window):
借助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]'
}),
// ... ...
]
}
)
2
3
4
5
6
7
8
9
10
11
12
13
14
🌙 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";
2
3
4
5
这里需要修改为*.module.scss功能 | Vite 官方中文文档 (vitejs.dev) (opens new window)
其实在解决这个问题之前,页面的主题配置还有一些和变量相关的样式都是混乱的,修复这个问题之后瞬间一片清明!!!
🌙 11.videojs报错导致Maximum call stack size exceeded
1.使用vite之后videojs-contrib-hls
(用来播放m3u8视频的videojs插件)报错:
2.注释掉videojs-contrib-hls
,只要打开引入了videojs
组件的页面就卡住了:
3.然后我尝试使用CDN的方式引入,视频可以成功播放了,但是还是有error
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>web 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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
4.解决第3步的报错,切换了其他版本还是有这个错误,于是打算把videojs-contrib-hls (opens new window)源码clone一份看看报错的地方能不能修改下源码,发现README.md (opens new window):
Video.js Blog | Video.js (videojs.com) (opens new window)博客显示videojs7使用videojs/http-streaming (opens new window)插件来替代videojs-contrib-hls (opens new window)。
videojs/http-streaming (opens new window): HLS, DASH, and future HTTP streaming protocols library for video.js
刚巧项目中的videojs也是7.x版本,于是我切换了videojs/http-streaming (opens new window)CDN,看了官方DEMO (opens new window),需要把标签video替换为video-js,成功解决error,但是多了一个warning, 问题不大
这个就先忍了吧^_^
最终的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>web 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" defer></script>
<script src="https://unpkg.com/browse/@videojs/http-streaming@2.6.0/dist/videojs-http-streaming.min.js" defer></script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在解决这个问题之前,我内心是崩溃的,找了诸多方式都无法解决,就差一点儿就放弃了,行百里者半九十,我坚持找解决方案,最终终于成功了,点个赞!
为了渐进升级,目前保留`vue-cli-service`和`vite`共存,两者都可以使用,后续可以把`vue-cli-service`相关的依赖和代码删除即可
video组件推荐使用xgplayer (opens new window)
🌙 6.生产环境vite
为了在生产环境使用vite构建,我们需要把项目中部分代码优化一下:
- 1.项目中使用
require
方式导入的包,全部修改未esm的import
, - 2.为了多环境测试部署,需要配置环境变量,使用vite的配置:
分别创建
.env.development
、.env.test
、.env.production
比如:.env.development
NODE_ENV=development
# just a flag
ENV='development'
# base api
VUE_APP_BASE_API='/'
2
3
4
5
6
- 3.打包脚本:
package.json
{
"scripts": {
"dev": "vite",
"build:prod": "vite build --mode production",
"build:test": "vite build --mode test",
"preview": "vite preview"
}
}
2
3
4
5
6
7
8