perf: 优化代码分割与懒加载以提升应用性能
- 将 PdfViewer 和 VideoPlayer 组件改为异步加载,减少初始包体积 - 为视频播放器相关依赖添加动态导入,避免未使用时加载 - 在打包配置中添加手动分包策略,将 vue-office 和图像工具库分离为独立 chunk - 优化 Safari 浏览器检测逻辑,避免直接导入 video.js 以支持懒加载
Showing
5 changed files
with
33 additions
and
17 deletions
| ... | @@ -116,8 +116,9 @@ | ... | @@ -116,8 +116,9 @@ |
| 116 | </template> | 116 | </template> |
| 117 | 117 | ||
| 118 | <script setup> | 118 | <script setup> |
| 119 | -import { ref, watch, nextTick, onUnmounted } from 'vue'; | 119 | +import { defineAsyncComponent, ref, watch, nextTick, onUnmounted } from 'vue'; |
| 120 | -import PDF from "pdf-vue3"; | 120 | + |
| 121 | +const PDF = defineAsyncComponent(() => import("pdf-vue3")); | ||
| 121 | 122 | ||
| 122 | /** | 123 | /** |
| 123 | * 组件属性定义 | 124 | * 组件属性定义 | ... | ... |
| ... | @@ -62,11 +62,18 @@ | ... | @@ -62,11 +62,18 @@ |
| 62 | </template> | 62 | </template> |
| 63 | 63 | ||
| 64 | <script setup> | 64 | <script setup> |
| 65 | -import { ref } from "vue"; | 65 | +import { defineAsyncComponent, ref } from "vue"; |
| 66 | -import { VideoPlayer } from "@videojs-player/vue"; | ||
| 67 | -import "video.js/dist/video-js.css"; | ||
| 68 | import { useVideoPlayer } from "@/composables/useVideoPlayer"; | 66 | import { useVideoPlayer } from "@/composables/useVideoPlayer"; |
| 69 | 67 | ||
| 68 | +const VideoPlayer = defineAsyncComponent(async () => { | ||
| 69 | + await import("video.js/dist/video-js.css"); | ||
| 70 | + await import("videojs-hls-quality-selector/dist/videojs-hls-quality-selector.css"); | ||
| 71 | + await import("videojs-contrib-quality-levels"); | ||
| 72 | + await import("videojs-hls-quality-selector"); | ||
| 73 | + const mod = await import("@videojs-player/vue"); | ||
| 74 | + return mod.VideoPlayer; | ||
| 75 | +}); | ||
| 76 | + | ||
| 70 | const props = defineProps({ | 77 | const props = defineProps({ |
| 71 | options: { | 78 | options: { |
| 72 | type: Object, | 79 | type: Object, | ... | ... |
| 1 | import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'; | 1 | import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'; |
| 2 | import { wxInfo } from "@/utils/tools"; | 2 | import { wxInfo } from "@/utils/tools"; |
| 3 | -import videojs from "video.js"; | ||
| 4 | import { buildVideoSources, canPlayHlsNatively } from "./videoPlayerSource"; | 3 | import { buildVideoSources, canPlayHlsNatively } from "./videoPlayerSource"; |
| 5 | import { useVideoProbe } from "./useVideoProbe"; | 4 | import { useVideoProbe } from "./useVideoProbe"; |
| 6 | import { useVideoPlaybackOverlays } from "./useVideoPlaybackOverlays"; | 5 | import { useVideoPlaybackOverlays } from "./useVideoPlaybackOverlays"; |
| 7 | -// 新增:引入多码率切换插件 | 6 | + |
| 8 | -import 'videojs-contrib-quality-levels'; // 用于读取 m3u8 中的多码率信息 | 7 | +const is_safari_browser = () => { |
| 9 | -import 'videojs-hls-quality-selector'; // 用于在播放器控制条显示“清晰度”切换菜单(支持 Auto/720p/480p 等)。 | 8 | + if (typeof navigator === 'undefined') return false; |
| 10 | -import 'videojs-hls-quality-selector/dist/videojs-hls-quality-selector.css'; | 9 | + const ua = navigator.userAgent || ''; |
| 10 | + const is_safari = /safari/i.test(ua) && !/chrome|crios|android|fxios|edg/i.test(ua); | ||
| 11 | + return is_safari; | ||
| 12 | +}; | ||
| 11 | 13 | ||
| 12 | /** | 14 | /** |
| 13 | * - 使用方法 :您无需修改业务代码。只要传入的视频 URL 是七牛云生成的多码率 .m3u8 地址,播放器控制条右下角会自动出现“齿轮”图标,用户点击即可切换清晰度(或选择 Auto 自动切换)。 | 15 | * - 使用方法 :您无需修改业务代码。只要传入的视频 URL 是七牛云生成的多码率 .m3u8 地址,播放器控制条右下角会自动出现“齿轮”图标,用户点击即可切换清晰度(或选择 Auto 自动切换)。 |
| ... | @@ -271,7 +273,7 @@ export function useVideoPlayer(props, emit, videoRef, nativeVideoRef) { | ... | @@ -271,7 +273,7 @@ export function useVideoPlayer(props, emit, videoRef, nativeVideoRef) { |
| 271 | // 5. Video.js 播放器逻辑 (PC/Android) | 273 | // 5. Video.js 播放器逻辑 (PC/Android) |
| 272 | const shouldOverrideNativeHls = computed(() => { | 274 | const shouldOverrideNativeHls = computed(() => { |
| 273 | if (!isM3U8.value) return false; | 275 | if (!isM3U8.value) return false; |
| 274 | - if (videojs.browser.IS_SAFARI) return false; | 276 | + if (is_safari_browser()) return false; |
| 275 | // 非 Safari 且不具备原生 HLS 时,强制 video.js 的 VHS 来解 m3u8 | 277 | // 非 Safari 且不具备原生 HLS 时,强制 video.js 的 VHS 来解 m3u8 |
| 276 | return !canPlayHlsNatively(); | 278 | return !canPlayHlsNatively(); |
| 277 | }); | 279 | }); | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-10-22 10:45:51 | 2 | * @Date: 2025-10-22 10:45:51 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-10-22 10:54:10 | 4 | + * @LastEditTime: 2026-01-24 14:00:37 |
| 5 | * @FilePath: /mlaj/src/views/study/PdfPreviewPage.vue | 5 | * @FilePath: /mlaj/src/views/study/PdfPreviewPage.vue |
| 6 | - * @Description: 文件描述 | 6 | + * @Description: PDF预览页 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | <div class="pdf-preview-page"> | 9 | <div class="pdf-preview-page"> |
| ... | @@ -12,9 +12,10 @@ | ... | @@ -12,9 +12,10 @@ |
| 12 | </template> | 12 | </template> |
| 13 | 13 | ||
| 14 | <script setup> | 14 | <script setup> |
| 15 | -import { computed, onMounted, onBeforeUnmount } from 'vue' | 15 | +import { computed, defineAsyncComponent, onMounted, onBeforeUnmount } from 'vue' |
| 16 | import { useRoute, useRouter } from 'vue-router' | 16 | import { useRoute, useRouter } from 'vue-router' |
| 17 | -import PdfViewer from '@/components/media/PdfViewer.vue' | 17 | + |
| 18 | +const PdfViewer = defineAsyncComponent(() => import('@/components/media/PdfViewer.vue')) | ||
| 18 | 19 | ||
| 19 | const route = useRoute() | 20 | const route = useRoute() |
| 20 | const router = useRouter() | 21 | const router = useRouter() |
| ... | @@ -37,7 +38,7 @@ const pdfTitle = computed(() => { | ... | @@ -37,7 +38,7 @@ const pdfTitle = computed(() => { |
| 37 | const handleClose = () => { | 38 | const handleClose = () => { |
| 38 | // 清除刷新标记 | 39 | // 清除刷新标记 |
| 39 | sessionStorage.removeItem('pdf-preview-refreshed') | 40 | sessionStorage.removeItem('pdf-preview-refreshed') |
| 40 | - | 41 | + |
| 41 | const returnId = route.query.returnId | 42 | const returnId = route.query.returnId |
| 42 | const openMaterials = route.query.openMaterials | 43 | const openMaterials = route.query.openMaterials |
| 43 | if (returnId) { | 44 | if (returnId) { | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-03-20 19:53:12 | 2 | * @Date: 2025-03-20 19:53:12 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2026-01-18 23:17:57 | 4 | + * @LastEditTime: 2026-01-24 13:56:05 |
| 5 | * @FilePath: /mlaj/vite.config.js | 5 | * @FilePath: /mlaj/vite.config.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -111,6 +111,11 @@ export default ({ mode }) => { | ... | @@ -111,6 +111,11 @@ export default ({ mode }) => { |
| 111 | chunkFileNames: 'static/js/[name]-[hash].js', | 111 | chunkFileNames: 'static/js/[name]-[hash].js', |
| 112 | entryFileNames: 'static/js/[name]-[hash].js', | 112 | entryFileNames: 'static/js/[name]-[hash].js', |
| 113 | assetFileNames: 'static/[ext]/[name]-[hash].[ext]', | 113 | assetFileNames: 'static/[ext]/[name]-[hash].[ext]', |
| 114 | + manualChunks: (id) => { | ||
| 115 | + if (!id.includes('node_modules')) return; | ||
| 116 | + if (id.includes('@vue-office/docx') || id.includes('@vue-office/excel') || id.includes('@vue-office/pptx')) return 'vue-office'; | ||
| 117 | + if (id.includes('html2canvas') || id.includes('html-to-image')) return 'image-tools'; | ||
| 118 | + }, | ||
| 114 | }, | 119 | }, |
| 115 | input: { // 多页面应用模式, 打包时配置,运行配置要处理root | 120 | input: { // 多页面应用模式, 打包时配置,运行配置要处理root |
| 116 | main: path.resolve(__dirname, 'index.html'), | 121 | main: path.resolve(__dirname, 'index.html'), | ... | ... |
-
Please register or login to post a comment