refactor: 改善剪贴板监听性能 修复大图卡顿与CPU高占用的问题

This commit is contained in:
ZiuChen 2022-09-17 22:39:56 +08:00
parent ba841b67a8
commit f78b87783b
11 changed files with 70 additions and 50 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
.DS_Store .DS_Store
node_modules /node_modules
/dist /dist
# local env files # local env files

View File

@ -4,6 +4,7 @@
"build": "vue-cli-service build" "build": "vue-cli-service build"
}, },
"dependencies": { "dependencies": {
"clipboard-event": "^1.6.0",
"core-js": "^3.6.4", "core-js": "^3.6.4",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"less": "^4.1.3", "less": "^4.1.3",

17
pnpm-lock.yaml generated
View File

@ -2,10 +2,10 @@ lockfileVersion: 5.4
specifiers: specifiers:
'@vue/cli-service': ^5.0.8 '@vue/cli-service': ^5.0.8
clipboard-event: ^1.6.0
copy-webpack-plugin: ^6.0.2 copy-webpack-plugin: ^6.0.2
core-js: ^3.6.4 core-js: ^3.6.4
crypto: ^1.0.1 crypto: ^1.0.1
deepmerge: ^4.2.2
less: ^4.1.3 less: ^4.1.3
less-loader: ^11.0.0 less-loader: ^11.0.0
licia: ^1.23.0 licia: ^1.23.0
@ -15,6 +15,7 @@ specifiers:
webpack: 4.37.0 webpack: 4.37.0
dependencies: dependencies:
clipboard-event: registry.npmmirror.com/clipboard-event/1.6.0
core-js: registry.npmmirror.com/core-js/3.24.1 core-js: registry.npmmirror.com/core-js/3.24.1
crypto: registry.npmmirror.com/crypto/1.0.1 crypto: registry.npmmirror.com/crypto/1.0.1
less: registry.npmmirror.com/less/4.1.3 less: registry.npmmirror.com/less/4.1.3
@ -25,7 +26,6 @@ dependencies:
devDependencies: devDependencies:
'@vue/cli-service': registry.npmmirror.com/@vue/cli-service/5.0.8_rd7zcyopzq3edztmqqbboasfsu '@vue/cli-service': registry.npmmirror.com/@vue/cli-service/5.0.8_rd7zcyopzq3edztmqqbboasfsu
copy-webpack-plugin: registry.npmmirror.com/copy-webpack-plugin/6.4.1_webpack@4.37.0 copy-webpack-plugin: registry.npmmirror.com/copy-webpack-plugin/6.4.1_webpack@4.37.0
deepmerge: registry.npmmirror.com/deepmerge/4.2.2
less-loader: registry.npmmirror.com/less-loader/11.0.0_less@4.1.3+webpack@4.37.0 less-loader: registry.npmmirror.com/less-loader/11.0.0_less@4.1.3+webpack@4.37.0
uglifyjs-webpack-plugin: registry.npmmirror.com/uglifyjs-webpack-plugin/2.2.0_webpack@4.37.0 uglifyjs-webpack-plugin: registry.npmmirror.com/uglifyjs-webpack-plugin/2.2.0_webpack@4.37.0
vue-template-compiler: registry.npmmirror.com/vue-template-compiler/2.7.8 vue-template-compiler: registry.npmmirror.com/vue-template-compiler/2.7.8
@ -2056,6 +2056,12 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
registry.npmmirror.com/clipboard-event/1.6.0:
resolution: {integrity: sha512-a69QYimd43xM+5hcHkucs0V/QoiZz1fqEFRTnewOITVQOtypRLbCx76Q91Djn6h7O24817dQw44sFUxRYWIuYA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/clipboard-event/-/clipboard-event-1.6.0.tgz}
name: clipboard-event
version: 1.6.0
dev: false
registry.npmmirror.com/clipboardy/2.3.0: registry.npmmirror.com/clipboardy/2.3.0:
resolution: {integrity: sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz} resolution: {integrity: sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz}
name: clipboardy name: clipboardy
@ -2849,13 +2855,6 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
registry.npmmirror.com/deepmerge/4.2.2:
resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/deepmerge/-/deepmerge-4.2.2.tgz}
name: deepmerge
version: 4.2.2
engines: {node: '>=0.10.0'}
dev: true
registry.npmmirror.com/default-gateway/6.0.3: registry.npmmirror.com/default-gateway/6.0.3:
resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/default-gateway/-/default-gateway-6.0.3.tgz} resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/default-gateway/-/default-gateway-6.0.3.tgz}
name: default-gateway name: default-gateway

21
public/node_modules/clipboard-event/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Sudhakar R
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

29
public/node_modules/clipboard-event/index.js generated vendored Normal file
View File

@ -0,0 +1,29 @@
const { EventEmitter } = require('events');
const path = require('path');
const { execFile } = require('child_process');
class ClipboardEventListener extends EventEmitter {
constructor() {
super();
this.child = null;
}
startListening() {
const { platform } = process;
const file = `platform/clipboard-event-handler-${platform}${platform === 'win32' ? '.exe' : ''}`
if(platform !== 'win32' && platform !== 'darwin' && platform !== 'linux') {
throw new Error(`ClipboardEventListener is not supported on ${platform}`);
}
this.child = execFile(path.join(__dirname, file));
this.child.stdout.on('data', (data) => {
if (data.trim() === 'CLIPBOARD_CHANGE') {
this.emit('change');
}
});
}
stopListening() {
const res = this.child.kill();
return res;
}
}
module.exports = new ClipboardEventListener();

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,19 +6,13 @@
const fs = require('fs') const fs = require('fs')
const crypto = require('crypto') const crypto = require('crypto')
const listener = require('clipboard-event')
const { clipboard } = require('electron') const { clipboard } = require('electron')
const time = require('./time')
const homePath = utools.getPath('home') const sep = utools.isWindows() ? '\\' : '/'
const userDataPath = utools.getPath('userData') const DBPath = `${
const dbName = '_utools_clipboard_manager_storage' utools.isMacOs() ? utools.getPath('userData') : utools.getPath('home')
}${sep}_utools_clipboard_manager_storage`
const isMacOs = utools.isMacOs()
const isWindows = utools.isWindows()
const sep = isWindows ? '\\' : '/'
const DBPath = `${isMacOs ? userDataPath : homePath}${sep}${dbName}`
let globalImageOversize = false
class DB { class DB {
constructor(path) { constructor(path) {
@ -133,7 +127,6 @@ const pbpaste = () => {
// image // image
const image = clipboard.readImage() // 大图卡顿来源 const image = clipboard.readImage() // 大图卡顿来源
const data = image.toDataURL() const data = image.toDataURL()
globalImageOversize = data.length > 3e5
if (!image.isEmpty()) { if (!image.isEmpty()) {
return { return {
type: 'image', type: 'image',
@ -142,24 +135,6 @@ const pbpaste = () => {
} }
} }
const watchClipboard = async (db, fn) => {
let prev = db.dataBase.data[0] || {}
function loop() {
time.sleep(250).then(loop)
const item = pbpaste()
if (!item) return
item.id = crypto.createHash('md5').update(item.data).digest('hex')
if (item && prev.id != item.id) {
// 剪切板元素 与最近一次复制内容不同
prev = item
fn(item)
} else {
// 剪切板元素 与上次复制内容相同
}
}
loop()
}
const copy = (item, isHideMainWindow = true) => { const copy = (item, isHideMainWindow = true) => {
switch (item.type) { switch (item.type) {
case 'text': case 'text':
@ -221,9 +196,12 @@ const focus = (isBlur = false) => {
const toTop = () => (document.scrollingElement.scrollTop = 0) const toTop = () => (document.scrollingElement.scrollTop = 0)
const resetNav = () => document.querySelectorAll('.clip-switch-item')[0]?.click() const resetNav = () => document.querySelectorAll('.clip-switch-item')[0]?.click()
watchClipboard(db, (item) => { listener.startListening()
// 此函数不断执行
listener.on('change', () => {
const item = pbpaste()
if (!item) return if (!item) return
item.id = crypto.createHash('md5').update(item.data).digest('hex')
if (db.updateItemViaId(item.id)) { if (db.updateItemViaId(item.id)) {
// 在库中 由 updateItemViaId 更新 updateTime // 在库中 由 updateItemViaId 更新 updateTime
return return
@ -235,10 +213,6 @@ watchClipboard(db, (item) => {
}) })
utools.onPluginEnter(() => { utools.onPluginEnter(() => {
if (globalImageOversize) {
utools.copyText('ImageOverSized')
globalImageOversize = false
}
toTop() toTop()
resetNav() resetNav()
}) })

View File

@ -1,2 +0,0 @@
// time.js author: inu1255
const path=require("path");function newPromise(fn){let a,b;var tmp={resolve(x){if(this.pending){a(x);this.resolved=true;this.pending=false}},reject(e){if(this.pending){b(e);this.rejectd=true;this.pending=false}},pending:true,resolved:false,rejected:false};var pms=new Promise(function(resolve,reject){a=resolve;b=reject;if(fn)fn(tmp.resolve,tmp.reject)});return Object.assign(pms,tmp)}let cbIdx=1;const cbMap=new Map;function getWorker(){if(getWorker.worker)return getWorker.worker;const worker=new Worker(path.join(__dirname,"time.worker.js"));getWorker.worker=worker;worker.onmessage=e=>{if(e.data&&cbMap.has(e.data.cb)){cbMap.get(e.data.cb).apply(null,e.data.args)}};return worker}function call(method,args){const cb=cbIdx++;let pms=newPromise();cbMap.set(cb,function(err,data){if(err)pms.reject(err);else pms.resolve(data)});getWorker().postMessage({method:method,args:args,cb:cb});return pms}function sleep(ms){return call("sleep",[ms])}exports.sleep=sleep;

View File

@ -1,2 +0,0 @@
// time.worker.js author: inu1255
const apis={sleep(ms){return new Promise(resolve=>setTimeout(resolve,ms))}};onmessage=event=>{const data=event.data;if(!data)return;const{cb,method,args}=data;if(!apis[method]){postMessage({cb:cb,err:"no such method"});return}apis[method].apply(null,args).then(res=>postMessage({cb:cb,data:res}),err=>postMessage({cb:cb,err:err}))};