更新逻辑,缓存
144
.gitignore
vendored
@ -23,3 +23,147 @@ pnpm-debug.log*
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
@ -67,9 +67,9 @@ if (!self.define) {
|
||||
});
|
||||
};
|
||||
}
|
||||
define(['./workbox-5ea419d9'], (function (workbox) { 'use strict';
|
||||
define(['./workbox-20a2f87f'], (function (workbox) { 'use strict';
|
||||
|
||||
importScripts("sw-cache-manager.js");
|
||||
importScripts("/sw-cache-manager.js");
|
||||
self.skipWaiting();
|
||||
workbox.clientsClaim();
|
||||
|
||||
@ -82,55 +82,42 @@ define(['./workbox-5ea419d9'], (function (workbox) { 'use strict';
|
||||
"url": "suppress-warnings.js",
|
||||
"revision": "d41d8cd98f00b204e9800998ecf8427e"
|
||||
}, {
|
||||
"url": "/",
|
||||
"revision": "0.0lkakoc2in8"
|
||||
"url": "index.html",
|
||||
"revision": "0.vjgf6ab2p68"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/"), {
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||
allowlist: [/^\/$/]
|
||||
}));
|
||||
workbox.registerRoute(/\.(?:js)$/i, new workbox.StaleWhileRevalidate({
|
||||
"cacheName": "js-cache",
|
||||
workbox.registerRoute(({
|
||||
url
|
||||
}) => url.pathname.startsWith("/assets/"), new workbox.CacheFirst({
|
||||
"cacheName": "assets-cache",
|
||||
plugins: [new workbox.ExpirationPlugin({
|
||||
maxEntries: 100,
|
||||
maxAgeSeconds: 604800
|
||||
maxEntries: 200,
|
||||
maxAgeSeconds: 5184000
|
||||
}), new workbox.CacheableResponsePlugin({
|
||||
statuses: [0, 200]
|
||||
})]
|
||||
}), 'GET');
|
||||
workbox.registerRoute(/\.(?:css)$/i, new workbox.StaleWhileRevalidate({
|
||||
"cacheName": "css-cache",
|
||||
workbox.registerRoute(({
|
||||
url
|
||||
}) => url.pathname.startsWith("/pwa/"), new workbox.StaleWhileRevalidate({
|
||||
"cacheName": "pwa-cache",
|
||||
plugins: [new workbox.ExpirationPlugin({
|
||||
maxEntries: 50,
|
||||
maxAgeSeconds: 604800
|
||||
})]
|
||||
}), 'GET');
|
||||
workbox.registerRoute(/\.(?:html)$/i, new workbox.NetworkFirst({
|
||||
"cacheName": "html-cache",
|
||||
plugins: [new workbox.ExpirationPlugin({
|
||||
maxEntries: 20,
|
||||
maxAgeSeconds: 86400
|
||||
})]
|
||||
}), 'GET');
|
||||
workbox.registerRoute(/\.(?:png|jpg|jpeg|svg|gif)$/i, new workbox.StaleWhileRevalidate({
|
||||
"cacheName": "images-cache",
|
||||
plugins: [new workbox.ExpirationPlugin({
|
||||
maxEntries: 50,
|
||||
maxAgeSeconds: 2592000
|
||||
})]
|
||||
}), 'GET');
|
||||
workbox.registerRoute(/\/cdn-cgi\/.*/i, new workbox.NetworkFirst({
|
||||
"cacheName": "cdn-cgi-cache",
|
||||
"networkTimeoutSeconds": 10,
|
||||
plugins: [new workbox.ExpirationPlugin({
|
||||
maxEntries: 50,
|
||||
maxAgeSeconds: 86400
|
||||
}), new workbox.CacheableResponsePlugin({
|
||||
statuses: [0, 200]
|
||||
})]
|
||||
}), 'GET');
|
||||
workbox.registerRoute(({
|
||||
url
|
||||
}) => {
|
||||
return url.origin !== self.location.origin;
|
||||
const path = url.pathname;
|
||||
return !(path.includes("/assets/") || path.includes("/pwa/"));
|
||||
}, new workbox.NetworkFirst({
|
||||
"cacheName": "external-resources",
|
||||
"cacheName": "other-resources",
|
||||
"networkTimeoutSeconds": 10,
|
||||
plugins: [new workbox.ExpirationPlugin({
|
||||
maxEntries: 100,
|
||||
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"studentList": [
|
||||
"张三",
|
||||
"李四",
|
||||
"王五",
|
||||
"赵六",
|
||||
"钱七"
|
||||
],
|
||||
"homeworkArrange": [
|
||||
[
|
||||
"语文",
|
||||
"数学",
|
||||
"英语"
|
||||
],
|
||||
[
|
||||
"物理",
|
||||
"化学",
|
||||
"生物"
|
||||
],
|
||||
[
|
||||
"政治",
|
||||
"历史",
|
||||
"地理"
|
||||
]
|
||||
],
|
||||
"url": "http://localhost:3030"
|
||||
}
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 368 B |
Before Width: | Height: | Size: 526 B After Width: | Height: | Size: 526 B |
Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 368 B |
Before Width: | Height: | Size: 554 B After Width: | Height: | Size: 554 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 670 B After Width: | Height: | Size: 670 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 297 B |
@ -41,4 +41,4 @@ self.addEventListener('message', (event) => {
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Cache Manager extension loaded');
|
||||
console.log('Cache Manager extension loaded');
|
@ -92,22 +92,22 @@ export default {
|
||||
},
|
||||
async refreshCaches() {
|
||||
if (!this.serviceWorkerActive) return;
|
||||
|
||||
|
||||
this.loading = true;
|
||||
this.message = '';
|
||||
this.caches = [];
|
||||
|
||||
|
||||
try {
|
||||
// 获取所有缓存名称
|
||||
const cacheNames = await this.sendMessageToSW({ type: 'CACHE_KEYS' });
|
||||
|
||||
|
||||
// 获取每个缓存的内容
|
||||
for (const cacheName of cacheNames.cacheNames) {
|
||||
const cacheContent = await this.sendMessageToSW({
|
||||
type: 'CACHE_CONTENT',
|
||||
cacheName
|
||||
const cacheContent = await this.sendMessageToSW({
|
||||
type: 'CACHE_CONTENT',
|
||||
cacheName
|
||||
});
|
||||
|
||||
|
||||
this.caches.push({
|
||||
name: cacheName,
|
||||
urls: cacheContent.urls || []
|
||||
@ -122,11 +122,11 @@ export default {
|
||||
async clearCache(cacheName) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const result = await this.sendMessageToSW({
|
||||
type: 'CLEAR_CACHE',
|
||||
cacheName
|
||||
const result = await this.sendMessageToSW({
|
||||
type: 'CLEAR_CACHE',
|
||||
cacheName
|
||||
});
|
||||
|
||||
|
||||
if (result.success) {
|
||||
this.showMessage(`已清除缓存: ${this.formatCacheName(cacheName)}`, 'success');
|
||||
await this.refreshCaches();
|
||||
@ -142,12 +142,12 @@ export default {
|
||||
async clearUrl(cacheName, url) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const result = await this.sendMessageToSW({
|
||||
type: 'CLEAR_URL',
|
||||
const result = await this.sendMessageToSW({
|
||||
type: 'CLEAR_URL',
|
||||
cacheName,
|
||||
url
|
||||
url
|
||||
});
|
||||
|
||||
|
||||
if (result.success) {
|
||||
this.showMessage(`已从缓存中删除: ${this.getFileName(url)}`, 'success');
|
||||
await this.refreshCaches();
|
||||
@ -164,11 +164,11 @@ export default {
|
||||
if (!confirm('确定要清除所有缓存吗?这可能会导致应用需要重新下载资源。')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.loading = true;
|
||||
try {
|
||||
const result = await this.sendMessageToSW({ type: 'CLEAR_ALL_CACHES' });
|
||||
|
||||
|
||||
if (result.success) {
|
||||
this.showMessage('已清除所有缓存', 'success');
|
||||
await this.refreshCaches();
|
||||
@ -187,14 +187,14 @@ export default {
|
||||
reject(new Error('Service Worker 未控制页面'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const messageChannel = new MessageChannel();
|
||||
messageChannel.port1.onmessage = (event) => {
|
||||
resolve(event.data);
|
||||
};
|
||||
|
||||
|
||||
navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);
|
||||
|
||||
|
||||
// 设置超时
|
||||
setTimeout(() => {
|
||||
reject(new Error('Service Worker 响应超时'));
|
||||
@ -218,14 +218,15 @@ export default {
|
||||
const urlObj = new URL(url);
|
||||
const pathParts = urlObj.pathname.split('/');
|
||||
return pathParts[pathParts.length - 1] || urlObj.hostname;
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
console.error('获取文件名失败:', error);
|
||||
return url;
|
||||
}
|
||||
},
|
||||
showMessage(message, type = 'info') {
|
||||
this.message = message;
|
||||
this.messageType = type;
|
||||
|
||||
|
||||
// 5秒后自动清除消息
|
||||
setTimeout(() => {
|
||||
if (this.message === message) {
|
||||
@ -235,4 +236,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
@ -1,117 +0,0 @@
|
||||
<template>
|
||||
<v-card border class="mb-4">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon icon="mdi-account-question" class="mr-2" />
|
||||
随机点名设置
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-switch
|
||||
v-model="randomPickerEnabled"
|
||||
label="启用随机点名功能"
|
||||
color="primary"
|
||||
hide-details
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="randomPickerAnimation"
|
||||
label="启用随机点名动画效果"
|
||||
color="primary"
|
||||
hide-details
|
||||
class="mb-4"
|
||||
:disabled="!randomPickerEnabled"
|
||||
/>
|
||||
|
||||
<v-slider
|
||||
v-model="defaultCount"
|
||||
label="默认抽取人数"
|
||||
min="1"
|
||||
max="10"
|
||||
step="1"
|
||||
thumb-label
|
||||
:disabled="!randomPickerEnabled"
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-divider class="my-4" />
|
||||
|
||||
<div class="text-subtitle-1 mb-2">学生过滤设置</div>
|
||||
|
||||
<v-switch
|
||||
v-model="excludeAbsent"
|
||||
label="排除请假学生"
|
||||
color="primary"
|
||||
hide-details
|
||||
class="mb-2"
|
||||
:disabled="!randomPickerEnabled"
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="excludeLate"
|
||||
label="排除迟到学生"
|
||||
color="primary"
|
||||
hide-details
|
||||
class="mb-2"
|
||||
:disabled="!randomPickerEnabled"
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="excludeExcluded"
|
||||
label="排除不参与学生"
|
||||
color="primary"
|
||||
hide-details
|
||||
class="mb-2"
|
||||
:disabled="!randomPickerEnabled"
|
||||
/>
|
||||
|
||||
<v-alert
|
||||
v-if="randomPickerEnabled"
|
||||
type="info"
|
||||
variant="tonal"
|
||||
class="mt-4"
|
||||
density="compact"
|
||||
>
|
||||
随机点名功能将在主页显示一个按钮,点击后可以随机抽取学生。
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSetting, setSetting } from '@/utils/settings';
|
||||
|
||||
export default {
|
||||
name: 'RandomPickerSettingsCard',
|
||||
data() {
|
||||
return {
|
||||
randomPickerEnabled: getSetting('randomPicker.enabled'),
|
||||
randomPickerAnimation: getSetting('randomPicker.animation'),
|
||||
defaultCount: getSetting('randomPicker.defaultCount'),
|
||||
excludeAbsent: getSetting('randomPicker.excludeAbsent'),
|
||||
excludeLate: getSetting('randomPicker.excludeLate'),
|
||||
excludeExcluded: getSetting('randomPicker.excludeExcluded')
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
randomPickerEnabled(newValue) {
|
||||
setSetting('randomPicker.enabled', newValue);
|
||||
},
|
||||
randomPickerAnimation(newValue) {
|
||||
setSetting('randomPicker.animation', newValue);
|
||||
},
|
||||
defaultCount(newValue) {
|
||||
setSetting('randomPicker.defaultCount', newValue);
|
||||
},
|
||||
excludeAbsent(newValue) {
|
||||
setSetting('randomPicker.excludeAbsent', newValue);
|
||||
},
|
||||
excludeLate(newValue) {
|
||||
setSetting('randomPicker.excludeLate', newValue);
|
||||
},
|
||||
excludeExcluded(newValue) {
|
||||
setSetting('randomPicker.excludeExcluded', newValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -2,8 +2,51 @@
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<h1 class="text-h4 mb-4">缓存管理</h1>
|
||||
<p class="mb-6">在这里您可以查看和管理应用的缓存文件。清除缓存可能会导致应用需要重新下载资源。</p>
|
||||
<div class="d-flex align-center mb-6">
|
||||
<v-icon size="x-large" color="primary" class="mr-3">mdi-database-cog-outline</v-icon>
|
||||
<div>
|
||||
<h1 class="text-h4 ">缓存管理</h1>
|
||||
<div class="text-subtitle-1 text-grey">管理应用的本地缓存资源</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-card class="mb-6" variant="tonal" color="info" density="compact">
|
||||
<v-card-text class="d-flex align-center">
|
||||
<v-icon color="info" class="mr-2">mdi-information-outline</v-icon>
|
||||
<span>在这里您可以查看和管理应用的缓存文件。清除缓存可能会导致应用需要重新下载资源,但有助于解决某些显示问题。</span>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="8">
|
||||
<v-card class="mb-4" variant="tonal">
|
||||
<v-card-text>
|
||||
<div class="d-flex align-center mb-2">
|
||||
<v-icon color="primary" class="mr-2">mdi-information</v-icon>
|
||||
<span class="text-h6">什么是缓存?</span>
|
||||
</div>
|
||||
<p>缓存是浏览器在本地存储的网站资源副本,如图片、脚本和样式表等。这些缓存可以加快页面加载速度,减少数据使用,并在离线时提供基本功能。</p>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="4">
|
||||
<v-card class="mb-4" variant="tonal">
|
||||
<v-card-text>
|
||||
<div class="d-flex align-center mb-2">
|
||||
<v-icon color="warning" class="mr-2">mdi-lightbulb-outline</v-icon>
|
||||
<span class="text-h6">何时清除缓存?</span>
|
||||
</div>
|
||||
<ul class="pl-4">
|
||||
<li>应用显示过时的内容</li>
|
||||
<li>界面出现异常</li>
|
||||
<li>应用功能不正常</li>
|
||||
</ul>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<CacheManager />
|
||||
</v-col>
|
||||
</v-row>
|
||||
@ -17,6 +60,9 @@ export default {
|
||||
name: 'CacheManagementPage',
|
||||
components: {
|
||||
CacheManager
|
||||
},
|
||||
metaInfo: {
|
||||
title: '缓存管理'
|
||||
}
|
||||
}
|
||||
</script>
|
@ -134,7 +134,7 @@
|
||||
<v-col cols="12">
|
||||
<echo-chamber-card border />
|
||||
</v-col>
|
||||
|
||||
|
||||
<!-- 关于卡片 -->
|
||||
<v-col cols="12">
|
||||
<about-card />
|
||||
@ -156,12 +156,10 @@
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<random-picker-settings-card />
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
|
||||
<!-- 消息记录组件 -->
|
||||
<message-log ref="messageLog" />
|
||||
</div>
|
||||
@ -189,7 +187,6 @@ import AboutCard from '@/components/settings/AboutCard.vue';
|
||||
import '../styles/settings.scss';
|
||||
import dataProvider from '@/utils/dataProvider';
|
||||
import SettingsExplorer from '@/components/settings/SettingsExplorer.vue';
|
||||
import RandomPickerSettingsCard from '@/components/settings/cards/RandomPickerSettingsCard.vue';
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
@ -205,8 +202,7 @@ export default {
|
||||
DataProviderSettingsCard,
|
||||
ThemeSettingsCard,
|
||||
EchoChamberCard,
|
||||
SettingsExplorer,
|
||||
RandomPickerSettingsCard
|
||||
SettingsExplorer
|
||||
},
|
||||
setup() {
|
||||
const { mobile } = useDisplay();
|
||||
|
115
vite.config.mjs
@ -19,86 +19,62 @@ export default defineConfig({
|
||||
Layouts(),
|
||||
Vue({
|
||||
template: { transformAssetUrls }
|
||||
}),
|
||||
}),
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
devOptions: {
|
||||
navigateFallback: '/',
|
||||
navigateFallback: 'index.html',
|
||||
enabled: true,
|
||||
suppressWarnings: true,
|
||||
},
|
||||
|
||||
lang: 'zh-CN',
|
||||
|
||||
lang: 'zh-CN',
|
||||
injectRegister: 'auto',
|
||||
strategies: 'generateSW',
|
||||
|
||||
|
||||
workbox: {
|
||||
globPatterns: ['**/*.{js,css,html,png,svg,jpg,jpeg,gif,ico,woff,woff2,ttf,eot}'],
|
||||
navigateFallback: '/',
|
||||
globPatterns: ['*'],
|
||||
navigateFallback: 'index.html',
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: /\.(?:js)$/i,
|
||||
handler: 'StaleWhileRevalidate',
|
||||
urlPattern: ({ url }) => url.pathname.startsWith('/assets/'),
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'js-cache',
|
||||
cacheName: 'assets-cache',
|
||||
expiration: {
|
||||
maxEntries: 100,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 7 // 7 天
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
urlPattern: /\.(?:css)$/i,
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'css-cache',
|
||||
expiration: {
|
||||
maxEntries: 50,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 7 // 7 天
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
urlPattern: /\.(?:html)$/i,
|
||||
handler: 'NetworkFirst',
|
||||
options: {
|
||||
cacheName: 'html-cache',
|
||||
expiration: {
|
||||
maxEntries: 20,
|
||||
maxAgeSeconds: 60 * 60 * 24 // 1 天
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/i,
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'images-cache',
|
||||
expiration: {
|
||||
maxEntries: 50,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 天
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
urlPattern: /\/cdn-cgi\/.*/i,
|
||||
handler: 'NetworkFirst',
|
||||
options: {
|
||||
cacheName: 'cdn-cgi-cache',
|
||||
expiration: {
|
||||
maxEntries: 50,
|
||||
maxAgeSeconds: 60 * 60 * 24 // 1 天
|
||||
maxEntries: 200,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 60 // 60 天
|
||||
},
|
||||
networkTimeoutSeconds: 10
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// 匹配除了当前域名以外的所有请求
|
||||
urlPattern: ({ url }) => url.pathname.startsWith('/pwa/'),
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'pwa-cache',
|
||||
expiration: {
|
||||
maxEntries: 50,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 7 // 7 天
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// 匹配除了上述规则外的所有请求
|
||||
urlPattern: ({ url }) => {
|
||||
return url.origin !== self.location.origin;
|
||||
const path = url.pathname;
|
||||
// 排除已经由其他规则处理的路径
|
||||
return !(path.includes('/assets/') || path.includes('/pwa/'));
|
||||
},
|
||||
handler: 'NetworkFirst',
|
||||
options: {
|
||||
cacheName: 'external-resources',
|
||||
cacheName: 'other-resources',
|
||||
expiration: {
|
||||
maxEntries: 100,
|
||||
maxAgeSeconds: 60 * 60 * 24 // 1 天
|
||||
@ -108,12 +84,13 @@ export default defineConfig({
|
||||
statuses: [0, 200]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
],
|
||||
additionalManifestEntries: [],
|
||||
clientsClaim: true,
|
||||
skipWaiting: true,
|
||||
importScripts: ['sw-cache-manager.js']
|
||||
importScripts: ['/sw-cache-manager.js']
|
||||
},
|
||||
manifest: {
|
||||
name: 'Classworks作业板',
|
||||
@ -128,22 +105,22 @@ export default defineConfig({
|
||||
},
|
||||
icons: [
|
||||
{
|
||||
src: '/image/pwa-64x64.png',
|
||||
src: '/pwa/image/pwa-64x64.png',
|
||||
sizes: '64x64',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: '/image/pwa-192x192.png',
|
||||
src: '/pwa/image/pwa-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: '/image/pwa-512x512.png',
|
||||
src: '/pwa/image/pwa-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: '/image/maskable-icon-512x512.png',
|
||||
src: '/pwa/image/maskable-icon-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable'
|
||||
@ -154,9 +131,15 @@ export default defineConfig({
|
||||
name: '随机点名',
|
||||
short_name: '随机点名',
|
||||
url: '/#random-picker',
|
||||
icons: [
|
||||
{
|
||||
src: '/pwa/image/pwa-64x64.png',
|
||||
sizes: '64x64',
|
||||
type: 'image/png'
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
|
||||
}
|
||||
}),
|
||||
// https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme
|
||||
|