perf: 优化本地资源

This commit is contained in:
kuaifan 2024-11-12 12:34:45 +08:00
parent 9bc3e56c79
commit 9f00047fdd
6 changed files with 161 additions and 4 deletions

68
electron/electron.js vendored
View File

@ -1,7 +1,7 @@
const fs = require('fs')
const os = require("os");
const path = require('path')
const {app, BrowserWindow, ipcMain, dialog, clipboard, nativeImage, shell, Tray, Menu, globalShortcut, Notification, BrowserView, nativeTheme} = require('electron')
const {app, BrowserWindow, ipcMain, dialog, clipboard, nativeImage, shell, Tray, Menu, globalShortcut, Notification, BrowserView, nativeTheme, protocol} = require('electron')
const {autoUpdater} = require("electron-updater")
const log = require("electron-log");
const electronConf = require('electron-config')
@ -52,6 +52,70 @@ if (fs.existsSync(devloadCachePath)) {
devloadUrl = fs.readFileSync(devloadCachePath, 'utf8')
}
// 在最开始就注册协议为特权协议
protocol.registerSchemesAsPrivileged([
{
scheme: 'dootask-resources',
privileges: {
standard: true,
secure: true,
supportFetchAPI: true,
corsEnabled: true
}
}
])
/**
* 创建协议
*/
function createProtocol() {
protocol.handle('dootask-resources', async (request) => {
const url = request.url.replace(/^dootask-resources:\/\//, '')
if (url.includes('..')) {
return new Response('Access Denied', { status: 403 })
}
try {
const filePath = path.join(__dirname, devloadUrl ? '..' : '.', url)
if (!fs.existsSync(filePath)) {
return new Response('Not Found', { status: 404 })
}
const data = await fs.promises.readFile(filePath)
const mimeType = getMimeType(filePath)
return new Response(data, {
headers: {
'Content-Type': mimeType
}
})
} catch (error) {
console.error('Protocol handler error:', error)
return new Response('Internal Error', { status: 500 })
}
})
}
/**
* MIME类型判断
* @param filePath
* @returns {*|string}
*/
function getMimeType(filePath) {
const ext = path.extname(filePath).toLowerCase()
const mimeTypes = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.webp': 'image/webp'
}
return mimeTypes[ext] || 'application/octet-stream'
}
/**
* 创建主窗口
*/
@ -596,6 +660,8 @@ if (!getTheLock) {
isReady = true
// SameSite
utils.useCookie()
// 创建协议
createProtocol()
// 创建主窗口
createMainWindow()
// 创建托盘

View File

@ -62,6 +62,7 @@ import TagInput from './components/TagInput.vue'
import TableAction from './components/TableAction.vue'
import QuickEdit from './components/QuickEdit.vue'
import UserAvatar from './components/UserAvatar'
import Imgs from './components/Replace/Imgs'
import ImgView from './components/ImgView.vue'
import Scrollbar from './components/Scrollbar'
@ -72,6 +73,7 @@ Vue.component('TagInput', TagInput)
Vue.component('TableAction', TableAction);
Vue.component('QuickEdit', QuickEdit);
Vue.component('UserAvatar', UserAvatar);
Vue.component('Imgs', Imgs);
Vue.component('ImgView', ImgView);
Vue.component('Scrollbar', Scrollbar);

View File

@ -0,0 +1,26 @@
<template>
<img :src="processedSrc" :alt="alt">
</template>
<script>
import {publicImageResources} from "./utils";
export default {
props: {
src: {
type: String,
required: true
},
alt: {
type: String,
default: ''
}
},
computed: {
processedSrc({src}) {
return publicImageResources(src)
}
},
}
</script>

View File

@ -0,0 +1,62 @@
const publicImageResources = (() => {
let initialized = false
let appPreUrl = null
let serverPreUrl = null
let urlRegex = null
// 将 escapeRegExp 移到闭包内部
const escapeRegExp = (string) => {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
// 初始化函数
const initialize = () => {
if (initialized) return
// 设置应用前缀URL
if ($A.isEEUiApp) {
appPreUrl = $A.eeuiAppRewriteUrl('../public/')
} else if ($A.Electron) {
appPreUrl = "dootask-resources://public/"
}
// 如果没有特殊前缀,提前返回
if (!appPreUrl) return
// 获取服务器URL
serverPreUrl = $A.mainUrl()
const escapedPreUrl = escapeRegExp(serverPreUrl)
// 固定的模式
const patterns = [
'images/',
// 可以继续添加其他路径...
]
// 计算转义后的模式
const escapedPatterns = patterns.map(pattern => escapeRegExp(pattern))
// 编译正则表达式
urlRegex = new RegExp(`${escapedPreUrl}(${escapedPatterns.join('|')})`)
initialized = true
}
// 返回实际的处理函数
return (url) => {
// 第一次调用时初始化
initialize()
// 如果没有特殊前缀直接返回原URL
if (!appPreUrl) {
return url
}
if (urlRegex.test(url)) {
return url.replace(serverPreUrl, appPreUrl)
}
return url
}
})()
export {publicImageResources}

View File

@ -1,4 +1,5 @@
import {MarkdownPreview} from "../store/markdown";
import {publicImageResources} from "../components/Replace/utils";
/**
* 页面专用
@ -349,7 +350,7 @@ import {MarkdownPreview} from "../store/markdown";
const value = item.original
.replace(/\s+width=/, ` original-width=`)
.replace(/\s+height=/, ` original-height=`)
.replace(/\s+src=(["'])([^'"]*)\1/i, ` style="width:${data.width}px;height:${data.height}px" src="${data.src}"`)
.replace(/\s+src=(["'])([^'"]*)\1/i, ` style="width:${data.width}px;height:${data.height}px" src="${publicImageResources(data.src)}"`)
text = text.replace(item.original, value)
} else {
text = text.replace(item.original, `<div class="no-size-image-box">${item.original}</div>`);

View File

@ -17,7 +17,7 @@
<Scrollbar>
<ul :class="[type, 'no-dark-content']">
<li v-for="item in list" @click="onSelect($event, item)">
<img v-if="item.type === 'emoticon'" :src="item.src" :title="item.name" :alt="item.name"/>
<Imgs v-if="item.type === 'emoticon'" :src="item.src" :title="item.name" :alt="item.name"/>
<span v-else v-html="item.html" :title="item.name"></span>
</li>
</ul>
@ -32,7 +32,7 @@
<span class="no-dark-content">&#128512;</span>
</li>
<li v-for="item in emoticonData" :class="{active: type === 'emoticon' && emoticonPath == item.path}" @click="onEmoticon(item.path)">
<img :title="item.name" :alt="item.name" :src="item.src"/>
<Imgs :title="item.name" :alt="item.name" :src="item.src"/>
</li>
<li v-if="showEmojiMenuScrollRightBtn" @click="onEmojiMenuScroll('right')" class="right-btn"><i class="taskfont">&#xe733;</i></li>
</ul>