From 20a3e2bdea4e02fe58c14dba8bcbddc3967e644d Mon Sep 17 00:00:00 2001 From: ZiuChen <457353192@qq.com> Date: Thu, 27 Apr 2023 09:15:50 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20ZiuChen/?= =?UTF-8?q?ZiuChen.github.io@c7c120ae1b1d193fc8bcda16c41cc90ecf525da7=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 404.html | 2 +- .../【2023】青训营 - 前端练习题汇总解析.html | 6 +- article/【字节跳动】前端面试题总结.html | 6 +- article/【快手】深入理解前端面试题.html | 6 +- article/【用友金融】前端面试题总结.html | 6 +- article/一文读懂事件冒泡与事件捕获.html | 6 +- article/一文读懂伪类与伪元素.html | 6 +- article/一文读懂函数中this指向问题.html | 6 +- article/从0实现一个年度报告.html | 6 +- ...懂对象的数据属性描述符、存储属性描述符.html | 6 +- ...析defineProperty与Proxy实现的双向绑定.html | 6 +- article/深入JavaScript数据类型.html | 6 +- ...Vue3源码,看看Vue.use后究竟发生了什么?.html | 6 +- article/深入理解Proxy与Reflect.html | 6 +- article/深入理解浏览器缓存机制.html | 6 +- article/深入理解浏览器运行原理.html | 6 +- ...icle_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.js} | 2 +- ...【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.lean.js} | 2 +- ...s => article_【字节跳动】前端面试题总结.md.1f4ca571.js} | 2 +- ...article_【字节跳动】前端面试题总结.md.1f4ca571.lean.js} | 2 +- ...s => article_【快手】深入理解前端面试题.md.7657b1d3.js} | 2 +- ...article_【快手】深入理解前端面试题.md.7657b1d3.lean.js} | 2 +- ...s => article_【用友金融】前端面试题总结.md.9fca7220.js} | 2 +- ...article_【用友金融】前端面试题总结.md.9fca7220.lean.js} | 2 +- ...s => article_一文读懂事件冒泡与事件捕获.md.055b150b.js} | 2 +- ...article_一文读懂事件冒泡与事件捕获.md.055b150b.lean.js} | 2 +- ...5.js => article_一文读懂伪类与伪元素.md.c653a705.js} | 2 +- ...=> article_一文读懂伪类与伪元素.md.c653a705.lean.js} | 2 +- ...=> article_一文读懂函数中this指向问题.md.1ffeb261.js} | 2 +- ...ticle_一文读懂函数中this指向问题.md.1ffeb261.lean.js} | 2 +- ...5.js => article_从0实现一个年度报告.md.cbf4ba30.js} | 2 +- ...=> article_从0实现一个年度报告.md.cbf4ba30.lean.js} | 2 +- ...cle_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.js} | 2 +- ...底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.lean.js} | 2 +- ...析defineProperty与Proxy实现的双向绑定.md.627b5302.js} | 2 +- ...fineProperty与Proxy实现的双向绑定.md.627b5302.lean.js} | 2 +- ...> article_深入JavaScript数据类型.md.986f85d7.js} | 2 +- ...icle_深入JavaScript数据类型.md.986f85d7.lean.js} | 2 +- ...入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.js} | 2 +- ...Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.lean.js} | 2 +- ... article_深入理解Proxy与Reflect.md.5b8c49e4.js} | 2 +- ...cle_深入理解Proxy与Reflect.md.5b8c49e4.lean.js} | 2 +- ....js => article_深入理解浏览器缓存机制.md.44e94909.js} | 2 +- ...> article_深入理解浏览器缓存机制.md.44e94909.lean.js} | 2 +- ....js => article_深入理解浏览器运行原理.md.4ae70423.js} | 2 +- ...> article_深入理解浏览器运行原理.md.4ae70423.lean.js} | 2 +- ...ex.md.92aa3f05.js => index.md.a39be407.js} | 2 +- ...3f05.lean.js => index.md.a39be407.lean.js} | 2 +- ...md.945cb9b3.js => note_CSS.md.6a7eedec.js} | 2 +- ...3.lean.js => note_CSS.md.6a7eedec.lean.js} | 2 +- ...note_Front-end Engineering.md.c6edf7d1.js} | 18 ++-- ...Front-end Engineering.md.c6edf7d1.lean.js} | 2 +- ...afaa.js => note_JavaScript.md.d5705bda.js} | 2 +- ...js => note_JavaScript.md.d5705bda.lean.js} | 2 +- ...=> note_JavaScriptEnhanced.md.0ff38e63.js} | 2 +- ...te_JavaScriptEnhanced.md.0ff38e63.lean.js} | 2 +- ....8ab2ead8.js => note_MySQL.md.0b6704a4.js} | 2 +- ...lean.js => note_MySQL.md.0b6704a4.lean.js} | 2 +- ....0514d155.js => note_React.md.eeaf8749.js} | 99 +++++++++++++++-- ...lean.js => note_React.md.eeaf8749.lean.js} | 2 +- ...md.9e639932.js => note_SSR.md.f248ec9c.js} | 2 +- ...2.lean.js => note_SSR.md.f248ec9c.lean.js} | 2 +- ...ipboardManager_guide_index.md.1455cfc8.js} | 2 +- ...rdManager_guide_index.md.1455cfc8.lean.js} | 2 +- ...ect_ClipboardManager_index.md.e0db2213.js} | 2 +- ...lipboardManager_index.md.e0db2213.lean.js} | 2 +- ...ClipboardManager_log_index.md.e245d280.js} | 2 +- ...oardManager_log_index.md.e245d280.lean.js} | 2 +- ...ardManager_statement_index.md.01b45ff7.js} | 2 +- ...nager_statement_index.md.01b45ff7.lean.js} | 2 +- ...ClipboardManager_vip_index.md.5375ae9a.js} | 2 +- ...oardManager_vip_index.md.5375ae9a.lean.js} | 2 +- ... => project_JSRunner_index.md.97c8b761.js} | 2 +- ...roject_JSRunner_index.md.97c8b761.lean.js} | 2 +- ...project_JSRunner_log_index.md.5fa1e94d.js} | 2 +- ...ct_JSRunner_log_index.md.5fa1e94d.lean.js} | 2 +- ... => project_Markdown_index.md.316f1afd.js} | 2 +- ...roject_Markdown_index.md.316f1afd.lean.js} | 2 +- ...project_Markdown_log_index.md.aab65e4a.js} | 2 +- ...ct_Markdown_log_index.md.aab65e4a.lean.js} | 2 +- ...ct_Markdown_shortcut_index.md.0ea191a3.js} | 2 +- ...rkdown_shortcut_index.md.0ea191a3.lean.js} | 2 +- ...oject_SmartWordBreak_index.md.59e896fe.js} | 2 +- ..._SmartWordBreak_index.md.59e896fe.lean.js} | 2 +- ...t_SmartWordBreak_log_index.md.dc4c02ce.js} | 2 +- ...rtWordBreak_log_index.md.dc4c02ce.lean.js} | 2 +- ...tWordBreak_statement_index.md.e7ffa382.js} | 2 +- ...Break_statement_index.md.e7ffa382.lean.js} | 2 +- ....1f3b81e1.js => self_index.md.cd26854a.js} | 2 +- ...lean.js => self_index.md.cd26854a.lean.js} | 2 +- ...1.js => works_contribution.md.fe233dc7.js} | 2 +- ...=> works_contribution.md.fe233dc7.lean.js} | 2 +- ...9b9.js => works_opensource.md.6dc8588c.js} | 2 +- ...s => works_opensource.md.6dc8588c.lean.js} | 2 +- hashmap.json | 2 +- index.html | 4 +- note/CSS.html | 6 +- note/Front-end Engineering.html | 22 ++-- note/JavaScript.html | 6 +- note/JavaScriptEnhanced.html | 6 +- note/MySQL.html | 6 +- note/React.html | 101 ++++++++++++++++-- note/SSR.html | 6 +- project/ClipboardManager/guide/index.html | 6 +- project/ClipboardManager/index.html | 6 +- project/ClipboardManager/log/index.html | 6 +- project/ClipboardManager/statement/index.html | 6 +- project/ClipboardManager/vip/index.html | 6 +- project/JSRunner/index.html | 6 +- project/JSRunner/log/index.html | 6 +- project/Markdown/index.html | 6 +- project/Markdown/log/index.html | 6 +- project/Markdown/shortcut/index.html | 6 +- project/SmartWordBreak/index.html | 6 +- project/SmartWordBreak/log/index.html | 6 +- project/SmartWordBreak/statement/index.html | 6 +- self/index.html | 6 +- works/contribution.html | 6 +- works/opensource.html | 6 +- 119 files changed, 393 insertions(+), 223 deletions(-) rename assets/{article_【2023】青训营 - 前端练习题汇总解析.md.751c7e25.js => article_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.js} (99%) rename assets/{article_【2023】青训营 - 前端练习题汇总解析.md.751c7e25.lean.js => article_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.lean.js} (87%) rename assets/{article_【字节跳动】前端面试题总结.md.da9d1eec.js => article_【字节跳动】前端面试题总结.md.1f4ca571.js} (99%) rename assets/{article_【字节跳动】前端面试题总结.md.da9d1eec.lean.js => article_【字节跳动】前端面试题总结.md.1f4ca571.lean.js} (86%) rename assets/{article_【快手】深入理解前端面试题.md.fd1db545.js => article_【快手】深入理解前端面试题.md.7657b1d3.js} (99%) rename assets/{article_【快手】深入理解前端面试题.md.fd1db545.lean.js => article_【快手】深入理解前端面试题.md.7657b1d3.lean.js} (86%) rename assets/{article_【用友金融】前端面试题总结.md.f9565228.js => article_【用友金融】前端面试题总结.md.9fca7220.js} (99%) rename assets/{article_【用友金融】前端面试题总结.md.f9565228.lean.js => article_【用友金融】前端面试题总结.md.9fca7220.lean.js} (86%) rename assets/{article_一文读懂事件冒泡与事件捕获.md.aa1902b6.js => article_一文读懂事件冒泡与事件捕获.md.055b150b.js} (99%) rename assets/{article_一文读懂事件冒泡与事件捕获.md.aa1902b6.lean.js => article_一文读懂事件冒泡与事件捕获.md.055b150b.lean.js} (86%) rename assets/{article_一文读懂伪类与伪元素.md.b7e97075.js => article_一文读懂伪类与伪元素.md.c653a705.js} (99%) rename assets/{article_一文读懂伪类与伪元素.md.b7e97075.lean.js => article_一文读懂伪类与伪元素.md.c653a705.lean.js} (86%) rename assets/{article_一文读懂函数中this指向问题.md.a2e876e0.js => article_一文读懂函数中this指向问题.md.1ffeb261.js} (99%) rename assets/{article_一文读懂函数中this指向问题.md.a2e876e0.lean.js => article_一文读懂函数中this指向问题.md.1ffeb261.lean.js} (86%) rename assets/{article_从0实现一个年度报告.md.cfdfead5.js => article_从0实现一个年度报告.md.cbf4ba30.js} (99%) rename assets/{article_从0实现一个年度报告.md.cfdfead5.lean.js => article_从0实现一个年度报告.md.cbf4ba30.lean.js} (71%) rename assets/{article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.93ffc2d7.js => article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.js} (99%) rename assets/{article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.93ffc2d7.lean.js => article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.lean.js} (88%) rename assets/{article_浅析defineProperty与Proxy实现的双向绑定.md.981b1858.js => article_浅析defineProperty与Proxy实现的双向绑定.md.627b5302.js} (99%) rename assets/{article_浅析defineProperty与Proxy实现的双向绑定.md.981b1858.lean.js => article_浅析defineProperty与Proxy实现的双向绑定.md.627b5302.lean.js} (87%) rename assets/{article_深入JavaScript数据类型.md.e8c468fa.js => article_深入JavaScript数据类型.md.986f85d7.js} (99%) rename assets/{article_深入JavaScript数据类型.md.e8c468fa.lean.js => article_深入JavaScript数据类型.md.986f85d7.lean.js} (86%) rename assets/{article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.4b28aa3d.js => article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.js} (99%) rename assets/{article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.4b28aa3d.lean.js => article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.lean.js} (76%) rename assets/{article_深入理解Proxy与Reflect.md.7dd623e9.js => article_深入理解Proxy与Reflect.md.5b8c49e4.js} (99%) rename assets/{article_深入理解Proxy与Reflect.md.7dd623e9.lean.js => article_深入理解Proxy与Reflect.md.5b8c49e4.lean.js} (71%) rename assets/{article_深入理解浏览器缓存机制.md.b2eac69d.js => article_深入理解浏览器缓存机制.md.44e94909.js} (99%) rename assets/{article_深入理解浏览器缓存机制.md.b2eac69d.lean.js => article_深入理解浏览器缓存机制.md.44e94909.lean.js} (86%) rename assets/{article_深入理解浏览器运行原理.md.811243b5.js => article_深入理解浏览器运行原理.md.4ae70423.js} (99%) rename assets/{article_深入理解浏览器运行原理.md.811243b5.lean.js => article_深入理解浏览器运行原理.md.4ae70423.lean.js} (86%) rename assets/{index.md.92aa3f05.js => index.md.a39be407.js} (94%) rename assets/{index.md.92aa3f05.lean.js => index.md.a39be407.lean.js} (94%) rename assets/{note_CSS.md.945cb9b3.js => note_CSS.md.6a7eedec.js} (99%) rename assets/{note_CSS.md.945cb9b3.lean.js => note_CSS.md.6a7eedec.lean.js} (87%) rename assets/{note_Front-end Engineering.md.ca61b8ad.js => note_Front-end Engineering.md.c6edf7d1.js} (98%) rename assets/{note_Front-end Engineering.md.ca61b8ad.lean.js => note_Front-end Engineering.md.c6edf7d1.lean.js} (84%) rename assets/{note_JavaScript.md.5dbfafaa.js => note_JavaScript.md.d5705bda.js} (91%) rename assets/{note_JavaScript.md.5dbfafaa.lean.js => note_JavaScript.md.d5705bda.lean.js} (91%) rename assets/{note_JavaScriptEnhanced.md.63433dba.js => note_JavaScriptEnhanced.md.0ff38e63.js} (99%) rename assets/{note_JavaScriptEnhanced.md.63433dba.lean.js => note_JavaScriptEnhanced.md.0ff38e63.lean.js} (92%) rename assets/{note_MySQL.md.8ab2ead8.js => note_MySQL.md.0b6704a4.js} (99%) rename assets/{note_MySQL.md.8ab2ead8.lean.js => note_MySQL.md.0b6704a4.lean.js} (83%) rename assets/{note_React.md.0514d155.js => note_React.md.eeaf8749.js} (96%) rename assets/{note_React.md.0514d155.lean.js => note_React.md.eeaf8749.lean.js} (76%) rename assets/{note_SSR.md.9e639932.js => note_SSR.md.f248ec9c.js} (99%) rename assets/{note_SSR.md.9e639932.lean.js => note_SSR.md.f248ec9c.lean.js} (83%) rename assets/{project_ClipboardManager_guide_index.md.89147de8.js => project_ClipboardManager_guide_index.md.1455cfc8.js} (99%) rename assets/{project_ClipboardManager_guide_index.md.89147de8.lean.js => project_ClipboardManager_guide_index.md.1455cfc8.lean.js} (97%) rename assets/{project_ClipboardManager_index.md.2a3cb14f.js => project_ClipboardManager_index.md.e0db2213.js} (96%) rename assets/{project_ClipboardManager_index.md.2a3cb14f.lean.js => project_ClipboardManager_index.md.e0db2213.lean.js} (94%) rename assets/{project_ClipboardManager_log_index.md.714cf9de.js => project_ClipboardManager_log_index.md.e245d280.js} (99%) rename assets/{project_ClipboardManager_log_index.md.714cf9de.lean.js => project_ClipboardManager_log_index.md.e245d280.lean.js} (85%) rename assets/{project_ClipboardManager_statement_index.md.95f4a6ec.js => project_ClipboardManager_statement_index.md.01b45ff7.js} (99%) rename assets/{project_ClipboardManager_statement_index.md.95f4a6ec.lean.js => project_ClipboardManager_statement_index.md.01b45ff7.lean.js} (85%) rename assets/{project_ClipboardManager_vip_index.md.453f0553.js => project_ClipboardManager_vip_index.md.5375ae9a.js} (99%) rename assets/{project_ClipboardManager_vip_index.md.453f0553.lean.js => project_ClipboardManager_vip_index.md.5375ae9a.lean.js} (96%) rename assets/{project_JSRunner_index.md.47b54e96.js => project_JSRunner_index.md.97c8b761.js} (96%) rename assets/{project_JSRunner_index.md.47b54e96.lean.js => project_JSRunner_index.md.97c8b761.lean.js} (96%) rename assets/{project_JSRunner_log_index.md.cc34efe6.js => project_JSRunner_log_index.md.5fa1e94d.js} (95%) rename assets/{project_JSRunner_log_index.md.cc34efe6.lean.js => project_JSRunner_log_index.md.5fa1e94d.lean.js} (84%) rename assets/{project_Markdown_index.md.69c4ad3a.js => project_Markdown_index.md.316f1afd.js} (96%) rename assets/{project_Markdown_index.md.69c4ad3a.lean.js => project_Markdown_index.md.316f1afd.lean.js} (93%) rename assets/{project_Markdown_log_index.md.c952cd03.js => project_Markdown_log_index.md.aab65e4a.js} (97%) rename assets/{project_Markdown_log_index.md.c952cd03.lean.js => project_Markdown_log_index.md.aab65e4a.lean.js} (84%) rename assets/{project_Markdown_shortcut_index.md.5d0eac56.js => project_Markdown_shortcut_index.md.0ea191a3.js} (96%) rename assets/{project_Markdown_shortcut_index.md.5d0eac56.lean.js => project_Markdown_shortcut_index.md.0ea191a3.lean.js} (84%) rename assets/{project_SmartWordBreak_index.md.dc5e4245.js => project_SmartWordBreak_index.md.59e896fe.js} (96%) rename assets/{project_SmartWordBreak_index.md.dc5e4245.lean.js => project_SmartWordBreak_index.md.59e896fe.lean.js} (92%) rename assets/{project_SmartWordBreak_log_index.md.07b2bd26.js => project_SmartWordBreak_log_index.md.dc4c02ce.js} (97%) rename assets/{project_SmartWordBreak_log_index.md.07b2bd26.lean.js => project_SmartWordBreak_log_index.md.dc4c02ce.lean.js} (85%) rename assets/{project_SmartWordBreak_statement_index.md.8c93a584.js => project_SmartWordBreak_statement_index.md.e7ffa382.js} (96%) rename assets/{project_SmartWordBreak_statement_index.md.8c93a584.lean.js => project_SmartWordBreak_statement_index.md.e7ffa382.lean.js} (85%) rename assets/{self_index.md.1f3b81e1.js => self_index.md.cd26854a.js} (99%) rename assets/{self_index.md.1f3b81e1.lean.js => self_index.md.cd26854a.lean.js} (86%) rename assets/{works_contribution.md.c63c44a1.js => works_contribution.md.fe233dc7.js} (95%) rename assets/{works_contribution.md.c63c44a1.lean.js => works_contribution.md.fe233dc7.lean.js} (84%) rename assets/{works_opensource.md.aad189b9.js => works_opensource.md.6dc8588c.js} (98%) rename assets/{works_opensource.md.aad189b9.lean.js => works_opensource.md.6dc8588c.lean.js} (84%) diff --git a/404.html b/404.html index 08a0a707..d1349686 100644 --- a/404.html +++ b/404.html @@ -13,7 +13,7 @@
Skip to content

404

PAGE NOT FOUND

But if you don't change your direction, and if you keep looking, you may end up where you are heading.

Released under the MIT License.

- diff --git a/article/【2023】青训营 - 前端练习题汇总解析.html b/article/【2023】青训营 - 前端练习题汇总解析.html index 5cbfdcca..126b2e87 100644 --- a/article/【2023】青训营 - 前端练习题汇总解析.html +++ b/article/【2023】青训营 - 前端练习题汇总解析.html @@ -10,7 +10,7 @@ - + @@ -276,8 +276,8 @@ } return true } -} - diff --git a/article/【字节跳动】前端面试题总结.html b/article/【字节跳动】前端面试题总结.html index 86ce53ef..a7a73a1e 100644 --- a/article/【字节跳动】前端面试题总结.html +++ b/article/【字节跳动】前端面试题总结.html @@ -10,7 +10,7 @@ - + @@ -42,8 +42,8 @@ false true false

问答题

HTTP缓存的请求头与响应头有哪些

宏任务与微任务有哪些区别?简述他们的应用场景

宏任务与微任务优先级不同

常见的宏任务与微任务

算法

中文数字转为阿拉伯数字

给定一个字符串,返回该字符串的所有组合

js
输入 abc 
-输出 ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']

回溯算法

- diff --git a/article/【快手】深入理解前端面试题.html b/article/【快手】深入理解前端面试题.html index c7e8c371..4a57c619 100644 --- a/article/【快手】深入理解前端面试题.html +++ b/article/【快手】深入理解前端面试题.html @@ -10,7 +10,7 @@ - + @@ -184,8 +184,8 @@ } console.log(twoSum([1, 2, 3, 4, 4], 5)) // 2 -console.log(twoSum([1, 1, 2, 3, 4, 4], 5)) // 3 - diff --git a/article/【用友金融】前端面试题总结.html b/article/【用友金融】前端面试题总结.html index ba91d303..07339b76 100644 --- a/article/【用友金融】前端面试题总结.html +++ b/article/【用友金融】前端面试题总结.html @@ -10,7 +10,7 @@ - + @@ -66,8 +66,8 @@ console.log(a(1, 1, 1))

结果为:

12

CSS权重的优先级

内联样式 > ID选择器 > 类选择器 > 标签选择器 > 通配符

HTTP状态码

越多越好

BFC(块级格式上下文)

下列选项对产生BFC描述错误的是:

元素浮动

设置元素浮动后,元素的display值哪个是正确的?

HTTP请求方法

下面哪个选项不是HTTP的请求方法

HTTP请求方法有:

GET POST DELETE PUT OPTIONSCONNECT HEAD PATCH TRACE

跨域问题

通常有哪些方法解决跨域问题?

了解Webpack的哪些配置项

判断变量类型

直接使用typeof无法判断引用类型变量的类型

js
console.log(typeof []) // object
 console.log(typeof {}) // object
-console.log(typeof null) // object

JS浮点数相加精度问题

简单讲讲Vue Router原理

ES6熟悉吗

ES6教程

CSS如何实现水平居中与垂直居中

参见CSS学习笔记

- diff --git a/article/一文读懂事件冒泡与事件捕获.html b/article/一文读懂事件冒泡与事件捕获.html index ace2089e..c90e3f21 100644 --- a/article/一文读懂事件冒泡与事件捕获.html +++ b/article/一文读懂事件冒泡与事件捕获.html @@ -10,7 +10,7 @@ - + @@ -50,8 +50,8 @@ });

需要注意的是,return false 的方法不仅阻止了事件冒泡,而且阻止了默认事件。

默认事件:DOM元素的默认行为,选中复选框是点击复选框的默认行为。下面这个例子说明了怎样阻止默认行为的发生

另一种阻止默认事件的方法是 .preventDefault()

js
document.querySelector(".div1")((e) => {
   // some code ...
   e.preventDefault()
-});

相关链接

事件冒泡及捕获

- diff --git a/article/一文读懂伪类与伪元素.html b/article/一文读懂伪类与伪元素.html index 04e58d9b..bb1f5e63 100644 --- a/article/一文读懂伪类与伪元素.html +++ b/article/一文读懂伪类与伪元素.html @@ -10,7 +10,7 @@ - + @@ -72,8 +72,8 @@ /* 父元素宽度任意 */ .father { width: 100px; -}

相关链接

代码片段

CSS选择器

- diff --git a/article/一文读懂函数中this指向问题.html b/article/一文读懂函数中this指向问题.html index 4a05ce25..d305048f 100644 --- a/article/一文读懂函数中this指向问题.html +++ b/article/一文读懂函数中this指向问题.html @@ -10,7 +10,7 @@ - + @@ -154,8 +154,8 @@ person1.foo4()() // 隐式绑定: person1 person1.foo4.call(person2)() // 显式绑定: person2 -person1.foo4().call(person2) // 隐式绑定: person1 - diff --git a/article/从0实现一个年度报告.html b/article/从0实现一个年度报告.html index 12f319d6..acf3fe63 100644 --- a/article/从0实现一个年度报告.html +++ b/article/从0实现一个年度报告.html @@ -10,7 +10,7 @@ - + @@ -103,8 +103,8 @@ } else if (e.key === 'ArrowDown') { nextPage() } - })

代码打包

由于最后需要将项目放到码上掘金平台运行,所以需要考虑静态资源的加载问题

这里我使用到了Vite提供的类似file-loader的功能,可以将大小在指定阈值下的图片资源直接转为行内的DataURL,配置选项是config.build.assetsInlineLimit,这样所有的图片资源都不必考虑外部引入的问题,直接内嵌进代码。

技术介绍

主界面使用的是Vue3的SFC,主要逻辑都在单文件组件中完成。通过JSX语法编写不同页面的内容,这样更方便我们为每个节点添加不同的动画。

JSX编写的组件通过全局注册后,在SFC中通过<Component>动态加载。

图片资源方面,使用到了雪碧图,部署后可以降低客户端发起HTTP请求频次,提高性能

代码复用方面,样式代码都抽离为单个的xxxx.less文件,哪里用到了直接导入即可

使用到了Pinia状态管理库,将switching pageId audioStatus等全局状态放到其中管理非常方便,避免了provideinject的繁琐

功能介绍

Demo展示

Demo(Vercel)

jcode

- diff --git a/article/彻底搞懂对象的数据属性描述符、存储属性描述符.html b/article/彻底搞懂对象的数据属性描述符、存储属性描述符.html index 02578b16..7e23f009 100644 --- a/article/彻底搞懂对象的数据属性描述符、存储属性描述符.html +++ b/article/彻底搞懂对象的数据属性描述符、存储属性描述符.html @@ -10,7 +10,7 @@ - + @@ -82,8 +82,8 @@ } function foo() { console.log("resetted address value once") - } - diff --git a/article/浅析defineProperty与Proxy实现的双向绑定.html b/article/浅析defineProperty与Proxy实现的双向绑定.html index 20728c8f..13bce4f7 100644 --- a/article/浅析defineProperty与Proxy实现的双向绑定.html +++ b/article/浅析defineProperty与Proxy实现的双向绑定.html @@ -10,7 +10,7 @@ - + @@ -34,8 +34,8 @@ vm.text = e.target.value }) </script> - </body>

通过 defineProperty 实现的响应式,不能检测数组和对象的变化:

对于对象:

Vue 无法检测 property 的添加或移除。

var vm = new Vue({ data:{ a:1 } }) // vm.a 是响应式的 vm.b = 2 // vm.b 是非响应式的

对于数组:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

🔰 Vue3的响应式原理

- diff --git a/article/深入JavaScript数据类型.html b/article/深入JavaScript数据类型.html index 7213567a..879ec82c 100644 --- a/article/深入JavaScript数据类型.html +++ b/article/深入JavaScript数据类型.html @@ -10,7 +10,7 @@ - + @@ -47,8 +47,8 @@ return res.slice(8, -1) } -console.log(classof({})) // Object

核心原理

深入Object.prototype.toString

参考ECMA6规范文档:Object.prototype.toString()

ES5标准下 Object.prototype.toString 执行原理

ES6标准下 Object.prototype.toString 执行原理

Object.prototype.toString()被调用时,会进行如下步骤:

Object.prototype.toString()

在ES6里,之前的内部属性 [[Class]] 不再使用,取而代之的是一系列的 internal slot

- diff --git a/article/深入Vue3源码,看看Vue.use后究竟发生了什么?.html b/article/深入Vue3源码,看看Vue.use后究竟发生了什么?.html index 75f63037..e1bdf559 100644 --- a/article/深入Vue3源码,看看Vue.use后究竟发生了什么?.html +++ b/article/深入Vue3源码,看看Vue.use后究竟发生了什么?.html @@ -10,7 +10,7 @@ - + @@ -114,8 +114,8 @@ import registerElement from './global/register-element.ts' const app = createApp(App).use(registerElement) -app.mount('#app')

当有新的需要使用的组件时,只需要到register-element.ts文件中引入一次即可。

参考阅读

Vue文档: App.use

Vue文档: Plugins

- diff --git a/article/深入理解Proxy与Reflect.html b/article/深入理解Proxy与Reflect.html index 25052bfa..3668456a 100644 --- a/article/深入理解Proxy与Reflect.html +++ b/article/深入理解Proxy与Reflect.html @@ -10,7 +10,7 @@ - + @@ -159,8 +159,8 @@ } const stu = new Reflect.construct(Person, ['ziu', 18], Student) -console.log(stu) - diff --git a/article/深入理解浏览器缓存机制.html b/article/深入理解浏览器缓存机制.html index 7809852d..bc02cb3e 100644 --- a/article/深入理解浏览器缓存机制.html +++ b/article/深入理解浏览器缓存机制.html @@ -10,7 +10,7 @@ - + @@ -21,8 +21,8 @@ const fileLastModifiedTime = stat.mtime.getTime() // 文件的最后更改时间 // 数字都用16进制表示 return `${fileLength.toString(16)}-${fileLastModifiedTime.toString(16)}` -}

Last-Modified与If-Modified-Since

Last-Modified是该资源文件最后一次更改时间,服务器会在ResponseHeader里返回,同时浏览器会将这个值保存起来,在下一次发送请求时,放到RequestHeader里的If-Modified-Since里,服务器在接收到后也会做比对,如果相同则命中协商缓存。

If-None-Match的优先级要高于If-Modified-Since,即:如果浏览器同时存在

两种协商缓存的区别

内存缓存与硬盘缓存

当我们打开一个新网页,服务器返回200,将资源发送给浏览器,浏览器做本地缓存

当我们刷新标签页,浏览器从内存缓存获得资源

当我们关闭标签页重新打开,浏览器从硬盘缓存获得资源

在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(MemoryCache);而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(DiskCache)。

用户对浏览器缓存的控制

参考资料

[稀土掘金] 彻底理解浏览器的缓存机制

[微信公众号] 浏览器的缓存机制小结

[微信公众号] 浏览器缓存机制剖析

[RFC-9111] Expires

[MDN] Expires

[MDN] Cache-Control

[MDN] ETag

- diff --git a/article/深入理解浏览器运行原理.html b/article/深入理解浏览器运行原理.html index 0dc8c640..773ab057 100644 --- a/article/深入理解浏览器运行原理.html +++ b/article/深入理解浏览器运行原理.html @@ -10,7 +10,7 @@ - + @@ -65,8 +65,8 @@ <script src="./defer.js" defer></script>
// defer.js
 console.log('defer script enter')

上述代码在控制台的输出为:

script enter
 defer script enter
-DOMContentLoaded enter

async属性

async属性也可以做到:让脚本异步加载而不阻塞DOM树的构建,它与defer的区别:

要使用async属性标记的script操作DOM,必须在其中使用DOMContentLoaded监听器的回调函数,在该事件触发(DOM树构建完毕)后,执行相应的回调函数

- diff --git a/assets/article_【2023】青训营 - 前端练习题汇总解析.md.751c7e25.js b/assets/article_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.js similarity index 99% rename from assets/article_【2023】青训营 - 前端练习题汇总解析.md.751c7e25.js rename to assets/article_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.js index e4de096d..fd02c934 100644 --- a/assets/article_【2023】青训营 - 前端练习题汇总解析.md.751c7e25.js +++ b/assets/article_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.js @@ -1,4 +1,4 @@ -import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const d=JSON.parse('{"title":"【2023】青训营 - 前端练习题汇总解析","description":"","frontmatter":{},"headers":[],"relativePath":"article/【2023】青训营 - 前端练习题汇总解析.md","lastUpdated":1682528161000}'),p={name:"article/【2023】青训营 - 前端练习题汇总解析.md"},e=l(`

【2023】青训营 - 前端练习题汇总解析

汇总了青训营官方账号每天发布的练习题,并且给出了答案、做了简单解析与知识扩充,有不足之处欢迎一起交流学习。

每天的选择题不同,而编程题是一样的,直接去Leetcode刷题即可。

选择题

DAY 1

题目描述

选择题 1:  
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const d=JSON.parse('{"title":"【2023】青训营 - 前端练习题汇总解析","description":"","frontmatter":{},"headers":[],"relativePath":"article/【2023】青训营 - 前端练习题汇总解析.md","lastUpdated":1682586876000}'),p={name:"article/【2023】青训营 - 前端练习题汇总解析.md"},e=l(`

【2023】青训营 - 前端练习题汇总解析

汇总了青训营官方账号每天发布的练习题,并且给出了答案、做了简单解析与知识扩充,有不足之处欢迎一起交流学习。

每天的选择题不同,而编程题是一样的,直接去Leetcode刷题即可。

选择题

DAY 1

题目描述

选择题 1:  
 下列哪些是 HTML5 的新特性?  
 A. 语义标签  
 B. Canvas 绘图  
diff --git a/assets/article_【2023】青训营 - 前端练习题汇总解析.md.751c7e25.lean.js b/assets/article_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.lean.js
similarity index 87%
rename from assets/article_【2023】青训营 - 前端练习题汇总解析.md.751c7e25.lean.js
rename to assets/article_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.lean.js
index 58958170..89a0ba86 100644
--- a/assets/article_【2023】青训营 - 前端练习题汇总解析.md.751c7e25.lean.js	
+++ b/assets/article_【2023】青训营 - 前端练习题汇总解析.md.c9420cf3.lean.js	
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const d=JSON.parse('{"title":"【2023】青训营 - 前端练习题汇总解析","description":"","frontmatter":{},"headers":[],"relativePath":"article/【2023】青训营 - 前端练习题汇总解析.md","lastUpdated":1682528161000}'),p={name:"article/【2023】青训营 - 前端练习题汇总解析.md"},e=l("",150),o=[e];function r(c,t,i,y,C,b){return n(),a("div",null,o)}const A=s(p,[["render",r]]);export{d as __pageData,A as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const d=JSON.parse('{"title":"【2023】青训营 - 前端练习题汇总解析","description":"","frontmatter":{},"headers":[],"relativePath":"article/【2023】青训营 - 前端练习题汇总解析.md","lastUpdated":1682586876000}'),p={name:"article/【2023】青训营 - 前端练习题汇总解析.md"},e=l("",150),o=[e];function r(c,t,i,y,C,b){return n(),a("div",null,o)}const A=s(p,[["render",r]]);export{d as __pageData,A as default};
diff --git a/assets/article_【字节跳动】前端面试题总结.md.da9d1eec.js b/assets/article_【字节跳动】前端面试题总结.md.1f4ca571.js
similarity index 99%
rename from assets/article_【字节跳动】前端面试题总结.md.da9d1eec.js
rename to assets/article_【字节跳动】前端面试题总结.md.1f4ca571.js
index 1ed9ddef..e55431e6 100644
--- a/assets/article_【字节跳动】前端面试题总结.md.da9d1eec.js
+++ b/assets/article_【字节跳动】前端面试题总结.md.1f4ca571.js
@@ -1,4 +1,4 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【字节跳动】前端面试题总结","description":"","frontmatter":{},"headers":[],"relativePath":"article/【字节跳动】前端面试题总结.md","lastUpdated":1682528161000}'),p={name:"article/【字节跳动】前端面试题总结.md"},o=l(`

【字节跳动】前端面试题总结

看代码说结果

代码输出结果1

js
console.log(['1', '2', '3'].map(parseInt))
[1, NaN, NaN]

代码输出结果2

js
let [a = 1, b] = []
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【字节跳动】前端面试题总结","description":"","frontmatter":{},"headers":[],"relativePath":"article/【字节跳动】前端面试题总结.md","lastUpdated":1682586876000}'),p={name:"article/【字节跳动】前端面试题总结.md"},o=l(`

【字节跳动】前端面试题总结

看代码说结果

代码输出结果1

js
console.log(['1', '2', '3'].map(parseInt))
[1, NaN, NaN]

代码输出结果2

js
let [a = 1, b] = []
 console.log(a, b)
1 undefined

代码输出结果3

js
Promise.resolve()
   .then(() => {
     Promise.resolve()
diff --git a/assets/article_【字节跳动】前端面试题总结.md.da9d1eec.lean.js b/assets/article_【字节跳动】前端面试题总结.md.1f4ca571.lean.js
similarity index 86%
rename from assets/article_【字节跳动】前端面试题总结.md.da9d1eec.lean.js
rename to assets/article_【字节跳动】前端面试题总结.md.1f4ca571.lean.js
index 3593c9af..f75cb22a 100644
--- a/assets/article_【字节跳动】前端面试题总结.md.da9d1eec.lean.js
+++ b/assets/article_【字节跳动】前端面试题总结.md.1f4ca571.lean.js
@@ -1 +1 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【字节跳动】前端面试题总结","description":"","frontmatter":{},"headers":[],"relativePath":"article/【字节跳动】前端面试题总结.md","lastUpdated":1682528161000}'),p={name:"article/【字节跳动】前端面试题总结.md"},o=l("",36),e=[o];function r(c,t,y,D,i,C){return a(),n("div",null,e)}const d=s(p,[["render",r]]);export{A as __pageData,d as default};
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【字节跳动】前端面试题总结","description":"","frontmatter":{},"headers":[],"relativePath":"article/【字节跳动】前端面试题总结.md","lastUpdated":1682586876000}'),p={name:"article/【字节跳动】前端面试题总结.md"},o=l("",36),e=[o];function r(c,t,y,D,i,C){return a(),n("div",null,e)}const d=s(p,[["render",r]]);export{A as __pageData,d as default};
diff --git a/assets/article_【快手】深入理解前端面试题.md.fd1db545.js b/assets/article_【快手】深入理解前端面试题.md.7657b1d3.js
similarity index 99%
rename from assets/article_【快手】深入理解前端面试题.md.fd1db545.js
rename to assets/article_【快手】深入理解前端面试题.md.7657b1d3.js
index 69166081..714a2e99 100644
--- a/assets/article_【快手】深入理解前端面试题.md.fd1db545.js
+++ b/assets/article_【快手】深入理解前端面试题.md.7657b1d3.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【快手】深入理解前端面试题","description":"","frontmatter":{},"headers":[],"relativePath":"article/【快手】深入理解前端面试题.md","lastUpdated":1682528161000}'),p={name:"article/【快手】深入理解前端面试题.md"},o=l(`

【快手】深入理解前端面试题

快手一面

Vue生命周期

beforeCreate created beforeMount mounted

beforeUpdate updated

beforeDestory destoryed

Vue3移除了beforeCreate created两个声明周期钩子,这是因为setup发生在开始创建组件之前,在beforeCreatecreated之前执行

可以在setup中使用的生命周期函数:onMounted onUpdated onUnmounted onBeforeUpdate这几个,

Vue 生命周期

网络请求一般在什么时候发起,为什么

越早越好,一般是放在createdonMounted或者setup

  • created(vue2) 此时组件内的基本数据已经创建好,组件的模板结构尚未生成
  • mounted(vue2) onMounted(vue3) 组件挂载到DOM树上,可以获取到DOM
  • setup(vue3) 时机要早于beforeCreatedcreated 所以在setup中发起网络请求也可以

setup的执行时机相当于哪个生命周期

setup的执行要早于beforeCreatedcreated,可以认为相当于这两个生命周期

Vue2和Vue3的响应式原理

Vue2响应式原理

全部使用Object.defineProperty()中的set与get函数

Vue3响应式原理

ref使用的是Object.defineProperty(),而reactive使用的是Proxy

Proxy可以直接深度代理一个对象,通过设置handler中的捕获器可以对对象创建一个代理,将各种行为监听并且同步到对象本身上

js
const obj = {
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【快手】深入理解前端面试题","description":"","frontmatter":{},"headers":[],"relativePath":"article/【快手】深入理解前端面试题.md","lastUpdated":1682586876000}'),p={name:"article/【快手】深入理解前端面试题.md"},o=l(`

【快手】深入理解前端面试题

快手一面

Vue生命周期

beforeCreate created beforeMount mounted

beforeUpdate updated

beforeDestory destoryed

Vue3移除了beforeCreate created两个声明周期钩子,这是因为setup发生在开始创建组件之前,在beforeCreatecreated之前执行

可以在setup中使用的生命周期函数:onMounted onUpdated onUnmounted onBeforeUpdate这几个,

Vue 生命周期

网络请求一般在什么时候发起,为什么

越早越好,一般是放在createdonMounted或者setup

  • created(vue2) 此时组件内的基本数据已经创建好,组件的模板结构尚未生成
  • mounted(vue2) onMounted(vue3) 组件挂载到DOM树上,可以获取到DOM
  • setup(vue3) 时机要早于beforeCreatedcreated 所以在setup中发起网络请求也可以

setup的执行时机相当于哪个生命周期

setup的执行要早于beforeCreatedcreated,可以认为相当于这两个生命周期

Vue2和Vue3的响应式原理

Vue2响应式原理

全部使用Object.defineProperty()中的set与get函数

Vue3响应式原理

ref使用的是Object.defineProperty(),而reactive使用的是Proxy

Proxy可以直接深度代理一个对象,通过设置handler中的捕获器可以对对象创建一个代理,将各种行为监听并且同步到对象本身上

js
const obj = {
   name: 'Ziu',
   age: 18
 }
diff --git a/assets/article_【快手】深入理解前端面试题.md.fd1db545.lean.js b/assets/article_【快手】深入理解前端面试题.md.7657b1d3.lean.js
similarity index 86%
rename from assets/article_【快手】深入理解前端面试题.md.fd1db545.lean.js
rename to assets/article_【快手】深入理解前端面试题.md.7657b1d3.lean.js
index 8c2eb271..14893b1c 100644
--- a/assets/article_【快手】深入理解前端面试题.md.fd1db545.lean.js
+++ b/assets/article_【快手】深入理解前端面试题.md.7657b1d3.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【快手】深入理解前端面试题","description":"","frontmatter":{},"headers":[],"relativePath":"article/【快手】深入理解前端面试题.md","lastUpdated":1682528161000}'),p={name:"article/【快手】深入理解前端面试题.md"},o=l("",87),e=[o];function r(t,c,y,F,i,D){return n(),a("div",null,e)}const d=s(p,[["render",r]]);export{A as __pageData,d as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【快手】深入理解前端面试题","description":"","frontmatter":{},"headers":[],"relativePath":"article/【快手】深入理解前端面试题.md","lastUpdated":1682586876000}'),p={name:"article/【快手】深入理解前端面试题.md"},o=l("",87),e=[o];function r(t,c,y,F,i,D){return n(),a("div",null,e)}const d=s(p,[["render",r]]);export{A as __pageData,d as default};
diff --git a/assets/article_【用友金融】前端面试题总结.md.f9565228.js b/assets/article_【用友金融】前端面试题总结.md.9fca7220.js
similarity index 99%
rename from assets/article_【用友金融】前端面试题总结.md.f9565228.js
rename to assets/article_【用友金融】前端面试题总结.md.9fca7220.js
index b07a21ae..d030d983 100644
--- a/assets/article_【用友金融】前端面试题总结.md.f9565228.js
+++ b/assets/article_【用友金融】前端面试题总结.md.9fca7220.js
@@ -1,4 +1,4 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【用友金融】前端面试题总结","description":"","frontmatter":{},"headers":[],"relativePath":"article/【用友金融】前端面试题总结.md","lastUpdated":1682528161000}'),e={name:"article/【用友金融】前端面试题总结.md"},p=l(`

【用友金融】前端面试题总结

回流与重绘

下列关于回流和重绘的说法错误的是

  • 回流的性能开销大于重绘的性能开销

  • 当页面结构、尺寸等改变时会发生回流

  • 回流一定会引起重绘,重绘也一定会引起回流(x)

  • 当页面结构不改变只是样式发生改变时会发生重绘,例如背景颜色改变时会发生重绘

  • display: none 指的是元素完全不陈列出来,不占据空间,涉及到了DOM结构,故产生reflow与repaint

  • visibility: hidden 指的是元素不可见但存在,保留空间,不影响结构,故只产生repaint,但不可触发绑定事件

  • opacity: 0 指的是元素不可见但存在,保留空间,不影响结构,并且,如果该元素已经绑定一些事件,如click事件,那么点击该区域,也能触发点击事件的

CSS属性

下列选项中哪个描述对于visibility: hidden;与display: none;是正确的

  • visibility属性不可继承
  • visibility: hidden; 不占据页面空间
  • display: none; 不占据页面空间(√)
  • 都无法通过DOM交互

函数执行结果

题目1

js
(function () {
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【用友金融】前端面试题总结","description":"","frontmatter":{},"headers":[],"relativePath":"article/【用友金融】前端面试题总结.md","lastUpdated":1682586876000}'),e={name:"article/【用友金融】前端面试题总结.md"},p=l(`

【用友金融】前端面试题总结

回流与重绘

下列关于回流和重绘的说法错误的是

  • 回流的性能开销大于重绘的性能开销

  • 当页面结构、尺寸等改变时会发生回流

  • 回流一定会引起重绘,重绘也一定会引起回流(x)

  • 当页面结构不改变只是样式发生改变时会发生重绘,例如背景颜色改变时会发生重绘

  • display: none 指的是元素完全不陈列出来,不占据空间,涉及到了DOM结构,故产生reflow与repaint

  • visibility: hidden 指的是元素不可见但存在,保留空间,不影响结构,故只产生repaint,但不可触发绑定事件

  • opacity: 0 指的是元素不可见但存在,保留空间,不影响结构,并且,如果该元素已经绑定一些事件,如click事件,那么点击该区域,也能触发点击事件的

CSS属性

下列选项中哪个描述对于visibility: hidden;与display: none;是正确的

  • visibility属性不可继承
  • visibility: hidden; 不占据页面空间
  • display: none; 不占据页面空间(√)
  • 都无法通过DOM交互

函数执行结果

题目1

js
(function () {
   var a = (b = 5);
 })();
 
diff --git a/assets/article_【用友金融】前端面试题总结.md.f9565228.lean.js b/assets/article_【用友金融】前端面试题总结.md.9fca7220.lean.js
similarity index 86%
rename from assets/article_【用友金融】前端面试题总结.md.f9565228.lean.js
rename to assets/article_【用友金融】前端面试题总结.md.9fca7220.lean.js
index 52909e19..a416ee56 100644
--- a/assets/article_【用友金融】前端面试题总结.md.f9565228.lean.js
+++ b/assets/article_【用友金融】前端面试题总结.md.9fca7220.lean.js
@@ -1 +1 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【用友金融】前端面试题总结","description":"","frontmatter":{},"headers":[],"relativePath":"article/【用友金融】前端面试题总结.md","lastUpdated":1682528161000}'),e={name:"article/【用友金融】前端面试题总结.md"},p=l("",71),o=[p];function r(c,t,i,y,C,D){return a(),n("div",null,o)}const F=s(e,[["render",r]]);export{A as __pageData,F as default};
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"【用友金融】前端面试题总结","description":"","frontmatter":{},"headers":[],"relativePath":"article/【用友金融】前端面试题总结.md","lastUpdated":1682586876000}'),e={name:"article/【用友金融】前端面试题总结.md"},p=l("",71),o=[p];function r(c,t,i,y,C,D){return a(),n("div",null,o)}const F=s(e,[["render",r]]);export{A as __pageData,F as default};
diff --git a/assets/article_一文读懂事件冒泡与事件捕获.md.aa1902b6.js b/assets/article_一文读懂事件冒泡与事件捕获.md.055b150b.js
similarity index 99%
rename from assets/article_一文读懂事件冒泡与事件捕获.md.aa1902b6.js
rename to assets/article_一文读懂事件冒泡与事件捕获.md.055b150b.js
index 1213d434..27b3fa48 100644
--- a/assets/article_一文读懂事件冒泡与事件捕获.md.aa1902b6.js
+++ b/assets/article_一文读懂事件冒泡与事件捕获.md.055b150b.js
@@ -1,4 +1,4 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"一文读懂事件冒泡与事件捕获","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂事件冒泡与事件捕获.md","lastUpdated":1682528161000}'),e={name:"article/一文读懂事件冒泡与事件捕获.md"},o=l(`

一文读懂事件冒泡与事件捕获

💡 从例子入手

这是一个简单的 Demo,点击的 Display video 按钮后,将视频展示出来。

其中的视频 <video> 标签被 <div> 包裹,<div><video> 上都绑定了自己的 click 事件。

代码片段

我们的预期是:点击 <video> 时播放视频,点击 <div> 时隐藏视频,然而实际上你会发现,点击视频后,不仅视频虽然正常播放,但同时也被隐藏了。

点击子元素,父元素的事件也被触发,导致这种现象的原因正是:浏览器的事件冒泡机制

🤔 什么是事件冒泡机制?事件捕获又是什么?

现代浏览器提供了两种事件处理阶段:捕获阶段与冒泡阶段

bubbling-capturing.png

在捕获阶段:

  • 浏览器检查元素的最外层祖先 <html> ,是否在捕获阶段中注册了一个 onclick 事件处理程序,如果是,则运行它。
  • 然后,它移动到 <html> 中单击元素的下一个祖先元素,执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。

在冒泡阶段,与上述顺序相反:

  • 浏览器检查实际点击的元素是否在冒泡阶段中注册了一个 onclick 事件处理程序,如果是,则运行它
  • 然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达 <html> 元素。

当一个事件被触发时,浏览器先运行捕获阶段,后运行冒泡阶段,并且在默认情况下,所有事件处理程序都在冒泡阶段进行注册

针对上面提到的问题,我们可以知道:当 <video> 点击事件触发后,虽然我们没有主动触发 <div> 上绑定的点击事件,但由于冒泡机制,点击事件冒泡到了 <div> 上,并触发了绑定在其上的监听回调函数,将 <video> 标签隐藏。

📌 用例子验证结论

下面是一个用于验证上述结论的Demo:

页面中包括由外向内的三个类名不同的div标签: div1 div2 div3,并为他们在捕获阶段/冒泡阶段分别绑定了不同的事件函数 clickdblclick

代码片段

当点击最内部的 div3 后,浏览器控制台输出:

> 捕获 click div1
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"一文读懂事件冒泡与事件捕获","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂事件冒泡与事件捕获.md","lastUpdated":1682586876000}'),e={name:"article/一文读懂事件冒泡与事件捕获.md"},o=l(`

一文读懂事件冒泡与事件捕获

💡 从例子入手

这是一个简单的 Demo,点击的 Display video 按钮后,将视频展示出来。

其中的视频 <video> 标签被 <div> 包裹,<div><video> 上都绑定了自己的 click 事件。

代码片段

我们的预期是:点击 <video> 时播放视频,点击 <div> 时隐藏视频,然而实际上你会发现,点击视频后,不仅视频虽然正常播放,但同时也被隐藏了。

点击子元素,父元素的事件也被触发,导致这种现象的原因正是:浏览器的事件冒泡机制

🤔 什么是事件冒泡机制?事件捕获又是什么?

现代浏览器提供了两种事件处理阶段:捕获阶段与冒泡阶段

bubbling-capturing.png

在捕获阶段:

  • 浏览器检查元素的最外层祖先 <html> ,是否在捕获阶段中注册了一个 onclick 事件处理程序,如果是,则运行它。
  • 然后,它移动到 <html> 中单击元素的下一个祖先元素,执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。

在冒泡阶段,与上述顺序相反:

  • 浏览器检查实际点击的元素是否在冒泡阶段中注册了一个 onclick 事件处理程序,如果是,则运行它
  • 然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达 <html> 元素。

当一个事件被触发时,浏览器先运行捕获阶段,后运行冒泡阶段,并且在默认情况下,所有事件处理程序都在冒泡阶段进行注册

针对上面提到的问题,我们可以知道:当 <video> 点击事件触发后,虽然我们没有主动触发 <div> 上绑定的点击事件,但由于冒泡机制,点击事件冒泡到了 <div> 上,并触发了绑定在其上的监听回调函数,将 <video> 标签隐藏。

📌 用例子验证结论

下面是一个用于验证上述结论的Demo:

页面中包括由外向内的三个类名不同的div标签: div1 div2 div3,并为他们在捕获阶段/冒泡阶段分别绑定了不同的事件函数 clickdblclick

代码片段

当点击最内部的 div3 后,浏览器控制台输出:

> 捕获 click div1
 > 捕获 click div2
 > 捕获 click div3
 > 冒泡 click div3
diff --git a/assets/article_一文读懂事件冒泡与事件捕获.md.aa1902b6.lean.js b/assets/article_一文读懂事件冒泡与事件捕获.md.055b150b.lean.js
similarity index 86%
rename from assets/article_一文读懂事件冒泡与事件捕获.md.aa1902b6.lean.js
rename to assets/article_一文读懂事件冒泡与事件捕获.md.055b150b.lean.js
index 626a87ac..4cd1e319 100644
--- a/assets/article_一文读懂事件冒泡与事件捕获.md.aa1902b6.lean.js
+++ b/assets/article_一文读懂事件冒泡与事件捕获.md.055b150b.lean.js
@@ -1 +1 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"一文读懂事件冒泡与事件捕获","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂事件冒泡与事件捕获.md","lastUpdated":1682528161000}'),e={name:"article/一文读懂事件冒泡与事件捕获.md"},o=l("",63),p=[o];function t(r,c,i,d,D,y){return a(),n("div",null,p)}const C=s(e,[["render",t]]);export{u as __pageData,C as default};
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"一文读懂事件冒泡与事件捕获","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂事件冒泡与事件捕获.md","lastUpdated":1682586876000}'),e={name:"article/一文读懂事件冒泡与事件捕获.md"},o=l("",63),p=[o];function t(r,c,i,d,D,y){return a(),n("div",null,p)}const C=s(e,[["render",t]]);export{u as __pageData,C as default};
diff --git a/assets/article_一文读懂伪类与伪元素.md.b7e97075.js b/assets/article_一文读懂伪类与伪元素.md.c653a705.js
similarity index 99%
rename from assets/article_一文读懂伪类与伪元素.md.b7e97075.js
rename to assets/article_一文读懂伪类与伪元素.md.c653a705.js
index d9d80fa2..84213a45 100644
--- a/assets/article_一文读懂伪类与伪元素.md.b7e97075.js
+++ b/assets/article_一文读懂伪类与伪元素.md.c653a705.js
@@ -1,4 +1,4 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"一文读懂伪类与伪元素","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂伪类与伪元素.md","lastUpdated":1682528161000}'),p={name:"article/一文读懂伪类与伪元素.md"},e=l(`

一文读懂伪类与伪元素

🔰 什么是伪类?

伪类是添加到选择器的 关键字 ,指定要选择的元素的特殊状态。

典型的伪类关键字

在大多数情况下,伪类都与基础选择器搭配使用,下述是伪类在一些典型场景下的应用。

:hover

指针在 <button> 上悬停,但没有激活它时,按钮颜色变为蓝色

css
button:hover {
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"一文读懂伪类与伪元素","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂伪类与伪元素.md","lastUpdated":1682586876000}'),p={name:"article/一文读懂伪类与伪元素.md"},e=l(`

一文读懂伪类与伪元素

🔰 什么是伪类?

伪类是添加到选择器的 关键字 ,指定要选择的元素的特殊状态。

典型的伪类关键字

在大多数情况下,伪类都与基础选择器搭配使用,下述是伪类在一些典型场景下的应用。

:hover

指针在 <button> 上悬停,但没有激活它时,按钮颜色变为蓝色

css
button:hover {
   color: blue;
 }

注意: 在触摸屏上 :hover 基本不可用。不同的浏览器上:hover 伪类表现不同。网页开发人员不要让任何内容只能通过悬停才能展示出来,不然这些内容对于触摸屏使用者来说是很难或者说不可能看到。

:not

:not() 用来匹配不符合一组选择器的元素。由于它的作用是防止特定的元素被选中,它也被称为反选伪类negation pseudo-class)。

将所有不是<p>的元素颜色改为蓝色:

css
body:not(p) {
   color: blue;
diff --git a/assets/article_一文读懂伪类与伪元素.md.b7e97075.lean.js b/assets/article_一文读懂伪类与伪元素.md.c653a705.lean.js
similarity index 86%
rename from assets/article_一文读懂伪类与伪元素.md.b7e97075.lean.js
rename to assets/article_一文读懂伪类与伪元素.md.c653a705.lean.js
index 2c92b969..9d3cbcbd 100644
--- a/assets/article_一文读懂伪类与伪元素.md.b7e97075.lean.js
+++ b/assets/article_一文读懂伪类与伪元素.md.c653a705.lean.js
@@ -1 +1 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"一文读懂伪类与伪元素","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂伪类与伪元素.md","lastUpdated":1682528161000}'),p={name:"article/一文读懂伪类与伪元素.md"},e=l("",56),o=[e];function c(r,t,i,D,y,C){return a(),n("div",null,o)}const d=s(p,[["render",c]]);export{b as __pageData,d as default};
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"一文读懂伪类与伪元素","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂伪类与伪元素.md","lastUpdated":1682586876000}'),p={name:"article/一文读懂伪类与伪元素.md"},e=l("",56),o=[e];function c(r,t,i,D,y,C){return a(),n("div",null,o)}const d=s(p,[["render",c]]);export{b as __pageData,d as default};
diff --git a/assets/article_一文读懂函数中this指向问题.md.a2e876e0.js b/assets/article_一文读懂函数中this指向问题.md.1ffeb261.js
similarity index 99%
rename from assets/article_一文读懂函数中this指向问题.md.a2e876e0.js
rename to assets/article_一文读懂函数中this指向问题.md.1ffeb261.js
index df33f0e6..c05a91b3 100644
--- a/assets/article_一文读懂函数中this指向问题.md.a2e876e0.js
+++ b/assets/article_一文读懂函数中this指向问题.md.1ffeb261.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"一文读懂函数中this指向问题","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂函数中this指向问题.md","lastUpdated":1682528161000}'),e={name:"article/一文读懂函数中this指向问题.md"},p=l(`

一文读懂函数中this指向问题

函数中this指向

函数在调用时, Javascript会默认为this绑定一个值

// 定义一个函数
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"一文读懂函数中this指向问题","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂函数中this指向问题.md","lastUpdated":1682586876000}'),e={name:"article/一文读懂函数中this指向问题.md"},p=l(`

一文读懂函数中this指向问题

函数中this指向

函数在调用时, Javascript会默认为this绑定一个值

// 定义一个函数
 function foo() {
   console.log(this)
 }
diff --git a/assets/article_一文读懂函数中this指向问题.md.a2e876e0.lean.js b/assets/article_一文读懂函数中this指向问题.md.1ffeb261.lean.js
similarity index 86%
rename from assets/article_一文读懂函数中this指向问题.md.a2e876e0.lean.js
rename to assets/article_一文读懂函数中this指向问题.md.1ffeb261.lean.js
index 2795621b..88c3f390 100644
--- a/assets/article_一文读懂函数中this指向问题.md.a2e876e0.lean.js
+++ b/assets/article_一文读懂函数中this指向问题.md.1ffeb261.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"一文读懂函数中this指向问题","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂函数中this指向问题.md","lastUpdated":1682528161000}'),e={name:"article/一文读懂函数中this指向问题.md"},p=l("",59),o=[p];function c(r,i,t,b,u,C){return n(),a("div",null,o)}const m=s(e,[["render",c]]);export{A as __pageData,m as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const A=JSON.parse('{"title":"一文读懂函数中this指向问题","description":"","frontmatter":{},"headers":[],"relativePath":"article/一文读懂函数中this指向问题.md","lastUpdated":1682586876000}'),e={name:"article/一文读懂函数中this指向问题.md"},p=l("",59),o=[p];function c(r,i,t,b,u,C){return n(),a("div",null,o)}const m=s(e,[["render",c]]);export{A as __pageData,m as default};
diff --git a/assets/article_从0实现一个年度报告.md.cfdfead5.js b/assets/article_从0实现一个年度报告.md.cbf4ba30.js
similarity index 99%
rename from assets/article_从0实现一个年度报告.md.cfdfead5.js
rename to assets/article_从0实现一个年度报告.md.cbf4ba30.js
index 9b57b05e..ccea8744 100644
--- a/assets/article_从0实现一个年度报告.md.cfdfead5.js
+++ b/assets/article_从0实现一个年度报告.md.cbf4ba30.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"从0实现一个年度报告","description":"","frontmatter":{},"headers":[],"relativePath":"article/从0实现一个年度报告.md","lastUpdated":1682528161000}'),p={name:"article/从0实现一个年度报告.md"},o=l(`

从0实现一个年度报告

每到年底各大应用都会推出自己的年终总结报告,统计出用户一年来在应用内的行为展示给用户,供用户记录、分享。

今年掘金社区推出了自己的2022掘友年度报告,这次我们仿照这个报告,从0开始自己实现一个年终总结报告页面

实现难点

1. 数据模拟

一般情况下是根据用户UID,到后端去请求相关接口获得统计数据。

例如掘金的接口为https://api.juejin.cn/event_api/v1/annual/annual_summary?aid=xxxxxx

本次后端使用NodeJS实现了一个爬虫,可以将用户数据统计完成后导出JSON格式的数据,将此数据粘贴到前端页面的输入框即可生成自己的报告

2. 屏幕适配

可以观察到,在PC端和在手机端访问年度报告展示的效果是不一样的。

本次考虑使用媒体查询来实现这个功能:

  • 宽屏则展示背景,页面切换也使用背景中的上下切换按钮
  • 小屏则隐藏背景,让内容填满屏幕,页面切换通过滑动事件监听

3. 动画效果

动画分为文本与背景元素的动画

  • 背景元素的动画使用了SVG动画
    • 动画中不动的部分直接使用.png图片
    • 运动的部分使用SVG动画绘制,如克里克的眼睛、尾巴
  • 文本的动画使用了CSS Animation渐显的效果
    • 不同段落之间通过animation-delay属性,彼此相差1000ms

背景动画容器的四个位置:左上角 右下角 中间部分 中间(悬浮气泡)。不同位置的动画容器都采用绝对定位position: absolute;,辅以z-index实现层叠

囿于工期,本次的背景动画直接采用静态图片+CSS Animation实现上下浮动的效果

4. 音乐播放

通过Audio接口访问网络音乐链接,控制音乐相关功能

  • 进入页面开始播放
  • 离开页面暂停播放
  • 支持点击按钮切换播放状态

用户数据

用户数据内容

- 用户名
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"从0实现一个年度报告","description":"","frontmatter":{},"headers":[],"relativePath":"article/从0实现一个年度报告.md","lastUpdated":1682586876000}'),p={name:"article/从0实现一个年度报告.md"},o=l(`

从0实现一个年度报告

每到年底各大应用都会推出自己的年终总结报告,统计出用户一年来在应用内的行为展示给用户,供用户记录、分享。

今年掘金社区推出了自己的2022掘友年度报告,这次我们仿照这个报告,从0开始自己实现一个年终总结报告页面

实现难点

1. 数据模拟

一般情况下是根据用户UID,到后端去请求相关接口获得统计数据。

例如掘金的接口为https://api.juejin.cn/event_api/v1/annual/annual_summary?aid=xxxxxx

本次后端使用NodeJS实现了一个爬虫,可以将用户数据统计完成后导出JSON格式的数据,将此数据粘贴到前端页面的输入框即可生成自己的报告

2. 屏幕适配

可以观察到,在PC端和在手机端访问年度报告展示的效果是不一样的。

本次考虑使用媒体查询来实现这个功能:

  • 宽屏则展示背景,页面切换也使用背景中的上下切换按钮
  • 小屏则隐藏背景,让内容填满屏幕,页面切换通过滑动事件监听

3. 动画效果

动画分为文本与背景元素的动画

  • 背景元素的动画使用了SVG动画
    • 动画中不动的部分直接使用.png图片
    • 运动的部分使用SVG动画绘制,如克里克的眼睛、尾巴
  • 文本的动画使用了CSS Animation渐显的效果
    • 不同段落之间通过animation-delay属性,彼此相差1000ms

背景动画容器的四个位置:左上角 右下角 中间部分 中间(悬浮气泡)。不同位置的动画容器都采用绝对定位position: absolute;,辅以z-index实现层叠

囿于工期,本次的背景动画直接采用静态图片+CSS Animation实现上下浮动的效果

4. 音乐播放

通过Audio接口访问网络音乐链接,控制音乐相关功能

  • 进入页面开始播放
  • 离开页面暂停播放
  • 支持点击按钮切换播放状态

用户数据

用户数据内容

- 用户名
 - 注册时间 距今天数
 - 创作相关
     - 发布文章数
diff --git a/assets/article_从0实现一个年度报告.md.cfdfead5.lean.js b/assets/article_从0实现一个年度报告.md.cbf4ba30.lean.js
similarity index 71%
rename from assets/article_从0实现一个年度报告.md.cfdfead5.lean.js
rename to assets/article_从0实现一个年度报告.md.cbf4ba30.lean.js
index 0a447441..f48d6bf0 100644
--- a/assets/article_从0实现一个年度报告.md.cfdfead5.lean.js
+++ b/assets/article_从0实现一个年度报告.md.cbf4ba30.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"从0实现一个年度报告","description":"","frontmatter":{},"headers":[],"relativePath":"article/从0实现一个年度报告.md","lastUpdated":1682528161000}'),p={name:"article/从0实现一个年度报告.md"},o=l("",59),e=[o];function t(c,r,F,D,y,i){return n(),a("div",null,e)}const A=s(p,[["render",t]]);export{u as __pageData,A as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"从0实现一个年度报告","description":"","frontmatter":{},"headers":[],"relativePath":"article/从0实现一个年度报告.md","lastUpdated":1682586876000}'),p={name:"article/从0实现一个年度报告.md"},o=l("",59),e=[o];function t(c,r,F,D,y,i){return n(),a("div",null,e)}const A=s(p,[["render",t]]);export{u as __pageData,A as default};
diff --git a/assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.93ffc2d7.js b/assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.js
similarity index 99%
rename from assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.93ffc2d7.js
rename to assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.js
index 3bd29677..e670c193 100644
--- a/assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.93ffc2d7.js
+++ b/assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const C=JSON.parse('{"title":"彻底搞懂对象的数据属性描述符、存储属性描述符","description":"","frontmatter":{},"headers":[],"relativePath":"article/彻底搞懂对象的数据属性描述符、存储属性描述符.md","lastUpdated":1682528161000}'),p={name:"article/彻底搞懂对象的数据属性描述符、存储属性描述符.md"},o=l(`

彻底搞懂对象的数据属性描述符、存储属性描述符

属性描述符

js
let obj = {
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const C=JSON.parse('{"title":"彻底搞懂对象的数据属性描述符、存储属性描述符","description":"","frontmatter":{},"headers":[],"relativePath":"article/彻底搞懂对象的数据属性描述符、存储属性描述符.md","lastUpdated":1682586876000}'),p={name:"article/彻底搞懂对象的数据属性描述符、存储属性描述符.md"},o=l(`

彻底搞懂对象的数据属性描述符、存储属性描述符

属性描述符

js
let obj = {
     name: "ziu",
     age: 18
   }
diff --git a/assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.93ffc2d7.lean.js b/assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.lean.js
similarity index 88%
rename from assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.93ffc2d7.lean.js
rename to assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.lean.js
index cc784b3a..19058f79 100644
--- a/assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.93ffc2d7.lean.js
+++ b/assets/article_彻底搞懂对象的数据属性描述符、存储属性描述符.md.590e5575.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const C=JSON.parse('{"title":"彻底搞懂对象的数据属性描述符、存储属性描述符","description":"","frontmatter":{},"headers":[],"relativePath":"article/彻底搞懂对象的数据属性描述符、存储属性描述符.md","lastUpdated":1682528161000}'),p={name:"article/彻底搞懂对象的数据属性描述符、存储属性描述符.md"},o=l("",18),e=[o];function t(c,r,D,y,F,i){return n(),a("div",null,e)}const d=s(p,[["render",t]]);export{C as __pageData,d as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const C=JSON.parse('{"title":"彻底搞懂对象的数据属性描述符、存储属性描述符","description":"","frontmatter":{},"headers":[],"relativePath":"article/彻底搞懂对象的数据属性描述符、存储属性描述符.md","lastUpdated":1682586876000}'),p={name:"article/彻底搞懂对象的数据属性描述符、存储属性描述符.md"},o=l("",18),e=[o];function t(c,r,D,y,F,i){return n(),a("div",null,e)}const d=s(p,[["render",t]]);export{C as __pageData,d as default};
diff --git a/assets/article_浅析defineProperty与Proxy实现的双向绑定.md.981b1858.js b/assets/article_浅析defineProperty与Proxy实现的双向绑定.md.627b5302.js
similarity index 99%
rename from assets/article_浅析defineProperty与Proxy实现的双向绑定.md.981b1858.js
rename to assets/article_浅析defineProperty与Proxy实现的双向绑定.md.627b5302.js
index eda79116..a58c8c45 100644
--- a/assets/article_浅析defineProperty与Proxy实现的双向绑定.md.981b1858.js
+++ b/assets/article_浅析defineProperty与Proxy实现的双向绑定.md.627b5302.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const C=JSON.parse('{"title":"浅析defineProperty与Proxy实现的双向绑定","description":"","frontmatter":{},"headers":[],"relativePath":"article/浅析defineProperty与Proxy实现的双向绑定.md","lastUpdated":1682528161000}'),e={name:"article/浅析defineProperty与Proxy实现的双向绑定.md"},p=l(`

浅析defineProperty与Proxy实现的双向绑定

文章内容总结自Vue官网 深入响应式原理

🔰 Vue2的响应式原理

image.png

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

Vue2的响应式原理,利用的是 Object.defineProperty()setter 属性:

defineProperty() 方法用于精确定义一个对象的属性,能够指定属性的各种特征,其中的 set 属性能够为对象指定一个 setter 函数,每次该属性的值发生修改,就会调用此函数。

更多可以配置的属性请参看:什么是对象的数据属性描述符?存储属性描述符?

这也是Vue2实现响应式数据、数据双向绑定的原理。

可以使用此方法实现一个简单的数据双向绑定的Demo:

image.png

  • 输入框内的内容改变,.vBox 展示的文本会随之改变。
  • 点击按钮修改 vm.text,输入框内的值和 .vBox 的文本都会发生改变。
html
<body>
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const C=JSON.parse('{"title":"浅析defineProperty与Proxy实现的双向绑定","description":"","frontmatter":{},"headers":[],"relativePath":"article/浅析defineProperty与Proxy实现的双向绑定.md","lastUpdated":1682586876000}'),e={name:"article/浅析defineProperty与Proxy实现的双向绑定.md"},p=l(`

浅析defineProperty与Proxy实现的双向绑定

文章内容总结自Vue官网 深入响应式原理

🔰 Vue2的响应式原理

image.png

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

Vue2的响应式原理,利用的是 Object.defineProperty()setter 属性:

defineProperty() 方法用于精确定义一个对象的属性,能够指定属性的各种特征,其中的 set 属性能够为对象指定一个 setter 函数,每次该属性的值发生修改,就会调用此函数。

更多可以配置的属性请参看:什么是对象的数据属性描述符?存储属性描述符?

这也是Vue2实现响应式数据、数据双向绑定的原理。

可以使用此方法实现一个简单的数据双向绑定的Demo:

image.png

  • 输入框内的内容改变,.vBox 展示的文本会随之改变。
  • 点击按钮修改 vm.text,输入框内的值和 .vBox 的文本都会发生改变。
html
<body>
     <input type="text" id="input" />
     <button onclick="vm.text='Hello, World.'">Modify vm.text</button>
     <div class="vBox"></div>
diff --git a/assets/article_浅析defineProperty与Proxy实现的双向绑定.md.981b1858.lean.js b/assets/article_浅析defineProperty与Proxy实现的双向绑定.md.627b5302.lean.js
similarity index 87%
rename from assets/article_浅析defineProperty与Proxy实现的双向绑定.md.981b1858.lean.js
rename to assets/article_浅析defineProperty与Proxy实现的双向绑定.md.627b5302.lean.js
index 35852a14..2b13eee5 100644
--- a/assets/article_浅析defineProperty与Proxy实现的双向绑定.md.981b1858.lean.js
+++ b/assets/article_浅析defineProperty与Proxy实现的双向绑定.md.627b5302.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const C=JSON.parse('{"title":"浅析defineProperty与Proxy实现的双向绑定","description":"","frontmatter":{},"headers":[],"relativePath":"article/浅析defineProperty与Proxy实现的双向绑定.md","lastUpdated":1682528161000}'),e={name:"article/浅析defineProperty与Proxy实现的双向绑定.md"},p=l("",20),o=[p];function t(r,c,y,D,F,i){return n(),a("div",null,o)}const u=s(e,[["render",t]]);export{C as __pageData,u as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const C=JSON.parse('{"title":"浅析defineProperty与Proxy实现的双向绑定","description":"","frontmatter":{},"headers":[],"relativePath":"article/浅析defineProperty与Proxy实现的双向绑定.md","lastUpdated":1682586876000}'),e={name:"article/浅析defineProperty与Proxy实现的双向绑定.md"},p=l("",20),o=[p];function t(r,c,y,D,F,i){return n(),a("div",null,o)}const u=s(e,[["render",t]]);export{C as __pageData,u as default};
diff --git a/assets/article_深入JavaScript数据类型.md.e8c468fa.js b/assets/article_深入JavaScript数据类型.md.986f85d7.js
similarity index 99%
rename from assets/article_深入JavaScript数据类型.md.e8c468fa.js
rename to assets/article_深入JavaScript数据类型.md.986f85d7.js
index 284d066b..e4c3ad5f 100644
--- a/assets/article_深入JavaScript数据类型.md.e8c468fa.js
+++ b/assets/article_深入JavaScript数据类型.md.986f85d7.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const o="/assets/Object.prototype.toString.70e8308c.png",d=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入JavaScript数据类型.md","lastUpdated":1682528161000}'),p={name:"article/深入JavaScript数据类型.md"},e=l(`

深入JavaScript数据类型

JavaScript包含以下几种数据类型:

  • Number 数字
  • String 字符串
  • Boolean 布尔值
  • Symbol 符号 (ES6新增)
  • Object 对象
    • Function 函数
    • Array 数组
    • Date 日期
    • RegExp 正则表达式
    • ...
  • null 空
  • undefined 未定义

从语言底层值的可变与不可变,可以将JS中的数据分为两种:不可变值(原始类型)和可变值(引用类型)

除了Object及继承自Object的特殊对象,其他的类型都为原始类型

typeof运算符

除了null,所有原始类型都可以通过typeof运算符得到不同的结果

而null与object通过typeof运算符得到的结果都为'object'

javascript
// 除了 null 其他原始类型的变量都可以通过 typeof 得到其类型
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const o="/assets/Object.prototype.toString.70e8308c.png",d=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入JavaScript数据类型.md","lastUpdated":1682586876000}'),p={name:"article/深入JavaScript数据类型.md"},e=l(`

深入JavaScript数据类型

JavaScript包含以下几种数据类型:

  • Number 数字
  • String 字符串
  • Boolean 布尔值
  • Symbol 符号 (ES6新增)
  • Object 对象
    • Function 函数
    • Array 数组
    • Date 日期
    • RegExp 正则表达式
    • ...
  • null 空
  • undefined 未定义

从语言底层值的可变与不可变,可以将JS中的数据分为两种:不可变值(原始类型)和可变值(引用类型)

除了Object及继承自Object的特殊对象,其他的类型都为原始类型

typeof运算符

除了null,所有原始类型都可以通过typeof运算符得到不同的结果

而null与object通过typeof运算符得到的结果都为'object'

javascript
// 除了 null 其他原始类型的变量都可以通过 typeof 得到其类型
 // 而 null 与 object 通过 typeof 运算得到的都是 'object'
 const targets = [18, 'Ziu', true, Symbol(''), {}, null, undefined]
 
diff --git a/assets/article_深入JavaScript数据类型.md.e8c468fa.lean.js b/assets/article_深入JavaScript数据类型.md.986f85d7.lean.js
similarity index 86%
rename from assets/article_深入JavaScript数据类型.md.e8c468fa.lean.js
rename to assets/article_深入JavaScript数据类型.md.986f85d7.lean.js
index 4c0799e9..85808ea7 100644
--- a/assets/article_深入JavaScript数据类型.md.e8c468fa.lean.js
+++ b/assets/article_深入JavaScript数据类型.md.986f85d7.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const o="/assets/Object.prototype.toString.70e8308c.png",d=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入JavaScript数据类型.md","lastUpdated":1682528161000}'),p={name:"article/深入JavaScript数据类型.md"},e=l("",46),t=[e];function c(r,i,y,D,A,F){return n(),a("div",null,t)}const b=s(p,[["render",c]]);export{d as __pageData,b as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const o="/assets/Object.prototype.toString.70e8308c.png",d=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入JavaScript数据类型.md","lastUpdated":1682586876000}'),p={name:"article/深入JavaScript数据类型.md"},e=l("",46),t=[e];function c(r,i,y,D,A,F){return n(),a("div",null,t)}const b=s(p,[["render",c]]);export{d as __pageData,b as default};
diff --git a/assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.4b28aa3d.js b/assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.js
similarity index 99%
rename from assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.4b28aa3d.js
rename to assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.js
index e4608f73..3c851b7c 100644
--- a/assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.4b28aa3d.js
+++ b/assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"深入Vue3源码,看看Vue.use后究竟发生了什么?","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入Vue3源码,看看Vue.use后究竟发生了什么?.md","lastUpdated":1682528161000}'),p={name:"article/深入Vue3源码,看看Vue.use后究竟发生了什么?.md"},e=l(`

深入Vue3源码,看看Vue.use后究竟发生了什么?

从全局注册组件库入手

如果我们自定义了几个自定义组件,当我们想在.vue文件中使用它们时,需要手动import导入组件并在component中注册:

html
<script>
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"深入Vue3源码,看看Vue.use后究竟发生了什么?","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入Vue3源码,看看Vue.use后究竟发生了什么?.md","lastUpdated":1682586876000}'),p={name:"article/深入Vue3源码,看看Vue.use后究竟发生了什么?.md"},e=l(`

深入Vue3源码,看看Vue.use后究竟发生了什么?

从全局注册组件库入手

如果我们自定义了几个自定义组件,当我们想在.vue文件中使用它们时,需要手动import导入组件并在component中注册:

html
<script>
 import CustomInput from '@/component/CustomInput.vue'
 
 export default {
diff --git a/assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.4b28aa3d.lean.js b/assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.lean.js
similarity index 76%
rename from assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.4b28aa3d.lean.js
rename to assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.lean.js
index 0bc61450..fd71096f 100644
--- a/assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.4b28aa3d.lean.js
+++ b/assets/article_深入Vue3源码,看看Vue.use后究竟发生了什么?.md.84a383ed.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"深入Vue3源码,看看Vue.use后究竟发生了什么?","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入Vue3源码,看看Vue.use后究竟发生了什么?.md","lastUpdated":1682528161000}'),p={name:"article/深入Vue3源码,看看Vue.use后究竟发生了什么?.md"},e=l("",26),o=[e];function t(c,r,y,i,F,D){return n(),a("div",null,o)}const C=s(p,[["render",t]]);export{u as __pageData,C as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"深入Vue3源码,看看Vue.use后究竟发生了什么?","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入Vue3源码,看看Vue.use后究竟发生了什么?.md","lastUpdated":1682586876000}'),p={name:"article/深入Vue3源码,看看Vue.use后究竟发生了什么?.md"},e=l("",26),o=[e];function t(c,r,y,i,F,D){return n(),a("div",null,o)}const C=s(p,[["render",t]]);export{u as __pageData,C as default};
diff --git a/assets/article_深入理解Proxy与Reflect.md.7dd623e9.js b/assets/article_深入理解Proxy与Reflect.md.5b8c49e4.js
similarity index 99%
rename from assets/article_深入理解Proxy与Reflect.md.7dd623e9.js
rename to assets/article_深入理解Proxy与Reflect.md.5b8c49e4.js
index 915436a4..8687c902 100644
--- a/assets/article_深入理解Proxy与Reflect.md.7dd623e9.js
+++ b/assets/article_深入理解Proxy与Reflect.md.5b8c49e4.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const i=JSON.parse('{"title":"深入理解Proxy与Reflect","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解Proxy与Reflect.md","lastUpdated":1682528161000}'),p={name:"article/深入理解Proxy与Reflect.md"},o=l(`

深入理解Proxy与Reflect

监听对象的操作

可以使用Proxy对象将原对象包裹,此后的操作都对proxy进行,每次getset被触发时都会自动执行相应代码

js
const obj = {
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const i=JSON.parse('{"title":"深入理解Proxy与Reflect","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解Proxy与Reflect.md","lastUpdated":1682586876000}'),p={name:"article/深入理解Proxy与Reflect.md"},o=l(`

深入理解Proxy与Reflect

监听对象的操作

可以使用Proxy对象将原对象包裹,此后的操作都对proxy进行,每次getset被触发时都会自动执行相应代码

js
const obj = {
   name: 'ziu',
   age: 18,
   height: 1.88
diff --git a/assets/article_深入理解Proxy与Reflect.md.7dd623e9.lean.js b/assets/article_深入理解Proxy与Reflect.md.5b8c49e4.lean.js
similarity index 71%
rename from assets/article_深入理解Proxy与Reflect.md.7dd623e9.lean.js
rename to assets/article_深入理解Proxy与Reflect.md.5b8c49e4.lean.js
index d9ec060c..14f4ee68 100644
--- a/assets/article_深入理解Proxy与Reflect.md.7dd623e9.lean.js
+++ b/assets/article_深入理解Proxy与Reflect.md.5b8c49e4.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const i=JSON.parse('{"title":"深入理解Proxy与Reflect","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解Proxy与Reflect.md","lastUpdated":1682528161000}'),p={name:"article/深入理解Proxy与Reflect.md"},o=l("",44),e=[o];function c(t,r,y,D,F,A){return n(),a("div",null,e)}const b=s(p,[["render",c]]);export{i as __pageData,b as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const i=JSON.parse('{"title":"深入理解Proxy与Reflect","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解Proxy与Reflect.md","lastUpdated":1682586876000}'),p={name:"article/深入理解Proxy与Reflect.md"},o=l("",44),e=[o];function c(t,r,y,D,F,A){return n(),a("div",null,e)}const b=s(p,[["render",c]]);export{i as __pageData,b as default};
diff --git a/assets/article_深入理解浏览器缓存机制.md.b2eac69d.js b/assets/article_深入理解浏览器缓存机制.md.44e94909.js
similarity index 99%
rename from assets/article_深入理解浏览器缓存机制.md.b2eac69d.js
rename to assets/article_深入理解浏览器缓存机制.md.44e94909.js
index 1787b89e..198594d4 100644
--- a/assets/article_深入理解浏览器缓存机制.md.b2eac69d.js
+++ b/assets/article_深入理解浏览器缓存机制.md.44e94909.js
@@ -1,4 +1,4 @@
-import{_ as e,o as a,c as s,R as o}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"深入理解浏览器缓存机制","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器缓存机制.md","lastUpdated":1682528161000}'),l={name:"article/深入理解浏览器缓存机制.md"},n=o(`

深入理解浏览器缓存机制

浏览器有两种缓存规则:强制缓存与协商缓存

  1. 强制缓存:不会向服务器发送请求,直接从缓存中读取资源
  2. 协商缓存:向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源
  • 共同点:都是从客户端缓存中读取资源
  • 不同点:强制缓存不会发请求,协商缓存会发请求

强制缓存

什么是强制缓存

浏览器在服务器发起真正请求前,先检查浏览器缓存:

  • 如果命中缓存,且缓存未过期,那么直接使用缓存资源
  • 如果未命中缓存,或缓存已过期失效,那么向服务器发出请求

强制缓存的规则

服务器通过向响应头添加ExpiresCache-Control字段来标识强制缓存的状态,浏览器会将这两个信息缓存到本地,后续有相同请求时,优先到浏览器缓存中检查资源是否到期。

其中Cache-Control优先级比Expires高,即:二者同时存在时,浏览器以Cache-Control为标准,检查缓存资源是否过期

Expires与Cache-Control

Expires

Expires表示当前资源的失效时间,它的值是一个HTTP-日期时间戳,例如:Expires: Thu, 01 Dec 1994 16:00:00 GMT

使用Expires存在一些弊端:

  • 代表的是绝对时间,如果浏览器和服务器的时间不同步,会导致缓存目标时间存在偏差
  • 如果服务端设置的日期格式不规范,那么等同于无缓存
  • ExpiresHTTP/1.0的字段,但是现在浏览器默认使用的是HTTP/1.1

在某些不支持HTTP1.1的环境下,Expires就会发挥用处

所以Expires其实是过时的产物,现阶段它的存在只是一种兼容性的写法

Cache-Control

如果在Cache-Control响应头设置了"max-age"或者"s-max-age"指令,那么Expires头会被忽略

设置Cache-Control的值有以下规则:

  • 不区分大小写,但建议使用小写
  • 多个指令以逗号分隔
  • 具有可选参数,可以用令牌或者带引号的字符串语法

常用的指令:

  • public:所有内容都将被缓存,即使是通常不可缓存的内容(如POST请求)。
  • private:所有内容只有客户端可以缓存,不能作为共享缓存(即代理服务器不能缓存它),这也是Cache-Control的默认取值
  • no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存,即不使用任何缓存。
  • max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效

举几个例子:

此次请求之后的600秒内,如果浏览器再次发起请求,那么直接使用缓存中的资源:

Cache-Control: max-age=600

浏览器可以缓存资源,但每次使用缓存资源前都必须重新验证其有效性:

Cache-Control: no-cache
Cache-Control: max-age=0, must-revalidate

这意味着每次都会发起 HTTP 请求,但当缓存内容仍有效时可以跳过 HTTP 响应体的下载

协商缓存

当浏览器检查本地的强制缓存已经失效后,浏览器携带该资源的协商缓存标识向服务器发起请求,由服务器根据缓存标识决定是否继续使用本地缓存。

  • 协商缓存生效,服务器返回304,通知浏览器继续使用本地缓存
  • 协商缓存失效,服务器返回200,与最新的请求资源

协商缓存的规则

服务器与浏览器通过两两成对的请求头来控制协商缓存:

  • Etag If-None-Match
  • Last-Modified If-Modified-Since

其中,EtagLast-Modified是由服务器设置的响应头的字段,If-None-MatchIf-Modified-Since则是浏览器向服务器发送的请求头的字段

Etag与Last-Modified

Etag是上一次加载资源时,服务器返回的ResponseHeader,是对该资源的一种唯一标识,只要资源有变化,Etag就会重新生成。浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到RequestHeader里的If-None-Match里,服务器接受到If-None-Match的值后,会拿来跟该资源文件的Etag值做比较,如果相同,则表示资源文件没有发生改变,命中协商缓存。

  • Etag由服务器生成,标志当前资源的唯一标识,一般包含大小、修改时间等信息
  • If-None-Match浏览器缓存到本地的Etag

HTTP协议并未规定Etag的内容是如何生成的,但一般包含大小、修改时间等信息

Node.js下生成Etag的示例:

js
// 根据文件的fs.Stats信息计算出etag
+import{_ as e,o as a,c as s,R as o}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"深入理解浏览器缓存机制","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器缓存机制.md","lastUpdated":1682586876000}'),l={name:"article/深入理解浏览器缓存机制.md"},n=o(`

深入理解浏览器缓存机制

浏览器有两种缓存规则:强制缓存与协商缓存

  1. 强制缓存:不会向服务器发送请求,直接从缓存中读取资源
  2. 协商缓存:向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源
  • 共同点:都是从客户端缓存中读取资源
  • 不同点:强制缓存不会发请求,协商缓存会发请求

强制缓存

什么是强制缓存

浏览器在服务器发起真正请求前,先检查浏览器缓存:

  • 如果命中缓存,且缓存未过期,那么直接使用缓存资源
  • 如果未命中缓存,或缓存已过期失效,那么向服务器发出请求

强制缓存的规则

服务器通过向响应头添加ExpiresCache-Control字段来标识强制缓存的状态,浏览器会将这两个信息缓存到本地,后续有相同请求时,优先到浏览器缓存中检查资源是否到期。

其中Cache-Control优先级比Expires高,即:二者同时存在时,浏览器以Cache-Control为标准,检查缓存资源是否过期

Expires与Cache-Control

Expires

Expires表示当前资源的失效时间,它的值是一个HTTP-日期时间戳,例如:Expires: Thu, 01 Dec 1994 16:00:00 GMT

使用Expires存在一些弊端:

  • 代表的是绝对时间,如果浏览器和服务器的时间不同步,会导致缓存目标时间存在偏差
  • 如果服务端设置的日期格式不规范,那么等同于无缓存
  • ExpiresHTTP/1.0的字段,但是现在浏览器默认使用的是HTTP/1.1

在某些不支持HTTP1.1的环境下,Expires就会发挥用处

所以Expires其实是过时的产物,现阶段它的存在只是一种兼容性的写法

Cache-Control

如果在Cache-Control响应头设置了"max-age"或者"s-max-age"指令,那么Expires头会被忽略

设置Cache-Control的值有以下规则:

  • 不区分大小写,但建议使用小写
  • 多个指令以逗号分隔
  • 具有可选参数,可以用令牌或者带引号的字符串语法

常用的指令:

  • public:所有内容都将被缓存,即使是通常不可缓存的内容(如POST请求)。
  • private:所有内容只有客户端可以缓存,不能作为共享缓存(即代理服务器不能缓存它),这也是Cache-Control的默认取值
  • no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存,即不使用任何缓存。
  • max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效

举几个例子:

此次请求之后的600秒内,如果浏览器再次发起请求,那么直接使用缓存中的资源:

Cache-Control: max-age=600

浏览器可以缓存资源,但每次使用缓存资源前都必须重新验证其有效性:

Cache-Control: no-cache
Cache-Control: max-age=0, must-revalidate

这意味着每次都会发起 HTTP 请求,但当缓存内容仍有效时可以跳过 HTTP 响应体的下载

协商缓存

当浏览器检查本地的强制缓存已经失效后,浏览器携带该资源的协商缓存标识向服务器发起请求,由服务器根据缓存标识决定是否继续使用本地缓存。

  • 协商缓存生效,服务器返回304,通知浏览器继续使用本地缓存
  • 协商缓存失效,服务器返回200,与最新的请求资源

协商缓存的规则

服务器与浏览器通过两两成对的请求头来控制协商缓存:

  • Etag If-None-Match
  • Last-Modified If-Modified-Since

其中,EtagLast-Modified是由服务器设置的响应头的字段,If-None-MatchIf-Modified-Since则是浏览器向服务器发送的请求头的字段

Etag与Last-Modified

Etag是上一次加载资源时,服务器返回的ResponseHeader,是对该资源的一种唯一标识,只要资源有变化,Etag就会重新生成。浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到RequestHeader里的If-None-Match里,服务器接受到If-None-Match的值后,会拿来跟该资源文件的Etag值做比较,如果相同,则表示资源文件没有发生改变,命中协商缓存。

  • Etag由服务器生成,标志当前资源的唯一标识,一般包含大小、修改时间等信息
  • If-None-Match浏览器缓存到本地的Etag

HTTP协议并未规定Etag的内容是如何生成的,但一般包含大小、修改时间等信息

Node.js下生成Etag的示例:

js
// 根据文件的fs.Stats信息计算出etag
 const genEtag = (stat) => {
   const fileLength = stat.size // 文件的大小
   const fileLastModifiedTime = stat.mtime.getTime() // 文件的最后更改时间
diff --git a/assets/article_深入理解浏览器缓存机制.md.b2eac69d.lean.js b/assets/article_深入理解浏览器缓存机制.md.44e94909.lean.js
similarity index 86%
rename from assets/article_深入理解浏览器缓存机制.md.b2eac69d.lean.js
rename to assets/article_深入理解浏览器缓存机制.md.44e94909.lean.js
index c14d1302..79ba6f5c 100644
--- a/assets/article_深入理解浏览器缓存机制.md.b2eac69d.lean.js
+++ b/assets/article_深入理解浏览器缓存机制.md.44e94909.lean.js
@@ -1 +1 @@
-import{_ as e,o as a,c as s,R as o}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"深入理解浏览器缓存机制","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器缓存机制.md","lastUpdated":1682528161000}'),l={name:"article/深入理解浏览器缓存机制.md"},n=o("",66),c=[n];function t(i,r,p,d,h,u){return a(),s("div",null,c)}const C=e(l,[["render",t]]);export{b as __pageData,C as default};
+import{_ as e,o as a,c as s,R as o}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"深入理解浏览器缓存机制","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器缓存机制.md","lastUpdated":1682586876000}'),l={name:"article/深入理解浏览器缓存机制.md"},n=o("",66),c=[n];function t(i,r,p,d,h,u){return a(),s("div",null,c)}const C=e(l,[["render",t]]);export{b as __pageData,C as default};
diff --git a/assets/article_深入理解浏览器运行原理.md.811243b5.js b/assets/article_深入理解浏览器运行原理.md.4ae70423.js
similarity index 99%
rename from assets/article_深入理解浏览器运行原理.md.811243b5.js
rename to assets/article_深入理解浏览器运行原理.md.4ae70423.js
index 7a55f5c2..c64daaca 100644
--- a/assets/article_深入理解浏览器运行原理.md.811243b5.js
+++ b/assets/article_深入理解浏览器运行原理.md.4ae70423.js
@@ -1,4 +1,4 @@
-import{_ as s,o as a,c as n,R as e}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"深入理解浏览器运行原理","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器运行原理.md","lastUpdated":1682528161000}'),l={name:"article/深入理解浏览器运行原理.md"},p=e(`

深入理解浏览器运行原理

网页解析过程

输入域名 => DNS解析为IP => 目标服务器返回index.html

DNS:Domain Name System

HTML解析过程

  • 浏览器开始解析index.html文件,当遇到<link>则向服务器请求下载.css文件
  • 遇到<script>标签则向服务器请求下载.js文件
浏览器解析HTML过程浏览器是和如何工作的

How browsers work

生成CSS规则

在解析的过程中,如果遇到<link>元素,那么会由浏览器负责下载对应的CSS文件

  • 注意:下载CSS文件不会影响到DOM解析
  • 有单独一个线程对CSS文件进行下载与解析

浏览器下载完CSS文件后,就会对CSS文件进行解析,解析出对应的规则树:

  • 我们可以称之为CSSOM(CSS Object Model,CSS对象模型)

构建Render Tree

有了DOM Tree和CSSOM Tree之后,就可以将二者结合,构建Render Tree了

此时,如果有某些元素的CSS属性display: none;那么这个元素就不会出现在Render Tree中

  • 下载和解析CSS文件时,不会阻塞DOM Tree的构建过程
  • 但会阻塞Render Tree的构建过程:因为需要对应的CSSOM Tree

布局和绘制(Layout & Paint)

第四步是在渲染树(Render Tree)上运行布局(Layout),以计算每个节点的几何体

  • 渲染树会表示显示哪些节点以及其他的样式,但是不表示每个节点的尺寸、位置等信息
  • 布局是确定呈现树中所有节点的宽度、高度和位置信息

第五步是将每个节点绘制(Paint)到屏幕上

  • 在绘制阶段,浏览器布局阶段计算的每个frame转为屏幕上实际的像素点
  • 包括将元素的可见部分进行绘制,比如文本、颜色、边框、阴影、替换元素

回流和重绘(Reflow & )

回流也可称为重排

理解回流(Reflow):

  • 第一次确定节点的大小和位置,称之为布局(layout)
  • 之后对节点的大小、位置修改重新计算,称之为回流

什么情况下会引起回流?

  • DOM 结构发生改变(添加新的节点或者移除节点)
  • 改变了布局(修改了width height padding font-size等值)
  • 窗口resize(修改了窗口的尺寸等)
  • 调用getComputedStyle方法获取尺寸、位置信息

理解重绘(Repaint):

  • 第一次渲染内容称之为绘制(paint)
  • 之后的重新渲染称之为重绘

什么情况下会引起重绘?

  • 修改背景色、文字颜色、边框颜色、样式等

回流一定会引起重绘,所以回流是一件很消耗性能的事情

  • 开发中要尽量避免发生回流

  • 修改样式尽量一次性修改完毕

    • 例如通过cssText一次性设置样式,或通过修改class的方式修改样式
  • 尽量避免频繁的操作DOM

    • 可以在一个DocumentFragment或者父元素中,将要操作的DOM操作完成,再一次性插入到DOM树中
  • 尽量避免通过getComputedStyle获取元素尺寸、位置等信息

  • 对某些元素使用position的absolute或fixed属性

    • 并不是不会引起回流,而是开销相对较小,不会对其他元素产生影响

特殊解析: composite合成

在绘制的过程中,可以将布局后的元素绘制到多个合成图层中

  • 这是浏览器的一种优化手段
  • 将不同流生成的不同Layer进行合并
标准流 => LayouTree => RenderLayer
+import{_ as s,o as a,c as n,R as e}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"深入理解浏览器运行原理","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器运行原理.md","lastUpdated":1682586876000}'),l={name:"article/深入理解浏览器运行原理.md"},p=e(`

深入理解浏览器运行原理

网页解析过程

输入域名 => DNS解析为IP => 目标服务器返回index.html

DNS:Domain Name System

HTML解析过程

  • 浏览器开始解析index.html文件,当遇到<link>则向服务器请求下载.css文件
  • 遇到<script>标签则向服务器请求下载.js文件
浏览器解析HTML过程浏览器是和如何工作的

How browsers work

生成CSS规则

在解析的过程中,如果遇到<link>元素,那么会由浏览器负责下载对应的CSS文件

  • 注意:下载CSS文件不会影响到DOM解析
  • 有单独一个线程对CSS文件进行下载与解析

浏览器下载完CSS文件后,就会对CSS文件进行解析,解析出对应的规则树:

  • 我们可以称之为CSSOM(CSS Object Model,CSS对象模型)

构建Render Tree

有了DOM Tree和CSSOM Tree之后,就可以将二者结合,构建Render Tree了

此时,如果有某些元素的CSS属性display: none;那么这个元素就不会出现在Render Tree中

  • 下载和解析CSS文件时,不会阻塞DOM Tree的构建过程
  • 但会阻塞Render Tree的构建过程:因为需要对应的CSSOM Tree

布局和绘制(Layout & Paint)

第四步是在渲染树(Render Tree)上运行布局(Layout),以计算每个节点的几何体

  • 渲染树会表示显示哪些节点以及其他的样式,但是不表示每个节点的尺寸、位置等信息
  • 布局是确定呈现树中所有节点的宽度、高度和位置信息

第五步是将每个节点绘制(Paint)到屏幕上

  • 在绘制阶段,浏览器布局阶段计算的每个frame转为屏幕上实际的像素点
  • 包括将元素的可见部分进行绘制,比如文本、颜色、边框、阴影、替换元素

回流和重绘(Reflow & )

回流也可称为重排

理解回流(Reflow):

  • 第一次确定节点的大小和位置,称之为布局(layout)
  • 之后对节点的大小、位置修改重新计算,称之为回流

什么情况下会引起回流?

  • DOM 结构发生改变(添加新的节点或者移除节点)
  • 改变了布局(修改了width height padding font-size等值)
  • 窗口resize(修改了窗口的尺寸等)
  • 调用getComputedStyle方法获取尺寸、位置信息

理解重绘(Repaint):

  • 第一次渲染内容称之为绘制(paint)
  • 之后的重新渲染称之为重绘

什么情况下会引起重绘?

  • 修改背景色、文字颜色、边框颜色、样式等

回流一定会引起重绘,所以回流是一件很消耗性能的事情

  • 开发中要尽量避免发生回流

  • 修改样式尽量一次性修改完毕

    • 例如通过cssText一次性设置样式,或通过修改class的方式修改样式
  • 尽量避免频繁的操作DOM

    • 可以在一个DocumentFragment或者父元素中,将要操作的DOM操作完成,再一次性插入到DOM树中
  • 尽量避免通过getComputedStyle获取元素尺寸、位置等信息

  • 对某些元素使用position的absolute或fixed属性

    • 并不是不会引起回流,而是开销相对较小,不会对其他元素产生影响

特殊解析: composite合成

在绘制的过程中,可以将布局后的元素绘制到多个合成图层中

  • 这是浏览器的一种优化手段
  • 将不同流生成的不同Layer进行合并
标准流 => LayouTree => RenderLayer
 \`position:fixed;\` => RenderLayer

默认情况,标准流中的内容都是被绘制在同一个图层(Layer)中的

而一些特殊的属性,浏览器会创建一个新的合成层(CompositingLayer),并且新的图层可以利用GPU来加速绘制

  • 每个合成层都是单独渲染的
  • 单独渲染可以避免所有的动画都在同一层中渲染导致性能问题
  • 在各自的层中渲染完成后,只需要将渲染结果更新回合成层即可

当元素具有哪些属性时,浏览器会为其创建新的合成层呢?

  • 3D Transforms
  • video canvas iframe
  • opacity 动画转换时
  • position: fixed
  • will-change: 一个实验性的属性,提前告诉浏览器此元素可能发生哪些变化
  • animation 或 transition设置了opacity、transform

案例1:同一层渲染

.box1 {
   width: 100px;
   height: 100px;
diff --git a/assets/article_深入理解浏览器运行原理.md.811243b5.lean.js b/assets/article_深入理解浏览器运行原理.md.4ae70423.lean.js
similarity index 86%
rename from assets/article_深入理解浏览器运行原理.md.811243b5.lean.js
rename to assets/article_深入理解浏览器运行原理.md.4ae70423.lean.js
index 53dded0a..3e90b587 100644
--- a/assets/article_深入理解浏览器运行原理.md.811243b5.lean.js
+++ b/assets/article_深入理解浏览器运行原理.md.4ae70423.lean.js
@@ -1 +1 @@
-import{_ as s,o as a,c as n,R as e}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"深入理解浏览器运行原理","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器运行原理.md","lastUpdated":1682528161000}'),l={name:"article/深入理解浏览器运行原理.md"},p=e("",87),i=[p];function r(o,c,t,d,b,u){return a(),n("div",null,i)}const C=s(l,[["render",r]]);export{h as __pageData,C as default};
+import{_ as s,o as a,c as n,R as e}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"深入理解浏览器运行原理","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器运行原理.md","lastUpdated":1682586876000}'),l={name:"article/深入理解浏览器运行原理.md"},p=e("",87),i=[p];function r(o,c,t,d,b,u){return a(),n("div",null,i)}const C=s(l,[["render",r]]);export{h as __pageData,C as default};
diff --git a/assets/index.md.92aa3f05.js b/assets/index.md.a39be407.js
similarity index 94%
rename from assets/index.md.92aa3f05.js
rename to assets/index.md.a39be407.js
index d61741ed..075aeae4 100644
--- a/assets/index.md.92aa3f05.js
+++ b/assets/index.md.a39be407.js
@@ -1 +1 @@
-import{_ as e,o as t,c as i}from"./chunks/framework.a304f0f7.js";const m=JSON.parse('{"title":"主页","description":"","frontmatter":{"layout":"home","title":"主页","hero":{"name":"ZiuChen","text":"无限进步.","tagline":"Infinite Progress...","actions":[{"theme":"brand","text":"Get Started","link":"/self/"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/ZiuChen"}]},"features":[{"icon":"📋","title":"Clipboard Manager","details":"A Powerful clipboard management tool"},{"icon":"✍🏻","title":"Super Markdown","details":"Powerful Markdown editor"},{"icon":"🚗","title":"JS Runner","details":"Run JavaScript dynamicly in Browser/Node.js"},{"icon":"🔑","title":"Bytemd Plugin","details":"Bytemd Plugin Library"},{"icon":"🍬","title":"ASOUL Browser Pet","details":"Keep an A-SOUL member as a pet in your browser"},{"icon":"🔧","title":"Typein","details":"Typein text, quickly perform browser operations"}]},"headers":[],"relativePath":"index.md","lastUpdated":1682528161000}'),n={name:"index.md"};function a(r,o,s,l,d,c){return t(),i("div")}const u=e(n,[["render",a]]);export{m as __pageData,u as default};
+import{_ as e,o as t,c as i}from"./chunks/framework.a304f0f7.js";const m=JSON.parse('{"title":"主页","description":"","frontmatter":{"layout":"home","title":"主页","hero":{"name":"ZiuChen","text":"无限进步.","tagline":"Infinite Progress...","actions":[{"theme":"brand","text":"Get Started","link":"/self/"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/ZiuChen"}]},"features":[{"icon":"📋","title":"Clipboard Manager","details":"A Powerful clipboard management tool"},{"icon":"✍🏻","title":"Super Markdown","details":"Powerful Markdown editor"},{"icon":"🚗","title":"JS Runner","details":"Run JavaScript dynamicly in Browser/Node.js"},{"icon":"🔑","title":"Bytemd Plugin","details":"Bytemd Plugin Library"},{"icon":"🍬","title":"ASOUL Browser Pet","details":"Keep an A-SOUL member as a pet in your browser"},{"icon":"🔧","title":"Typein","details":"Typein text, quickly perform browser operations"}]},"headers":[],"relativePath":"index.md","lastUpdated":1682586876000}'),n={name:"index.md"};function a(r,o,s,l,d,c){return t(),i("div")}const u=e(n,[["render",a]]);export{m as __pageData,u as default};
diff --git a/assets/index.md.92aa3f05.lean.js b/assets/index.md.a39be407.lean.js
similarity index 94%
rename from assets/index.md.92aa3f05.lean.js
rename to assets/index.md.a39be407.lean.js
index d61741ed..075aeae4 100644
--- a/assets/index.md.92aa3f05.lean.js
+++ b/assets/index.md.a39be407.lean.js
@@ -1 +1 @@
-import{_ as e,o as t,c as i}from"./chunks/framework.a304f0f7.js";const m=JSON.parse('{"title":"主页","description":"","frontmatter":{"layout":"home","title":"主页","hero":{"name":"ZiuChen","text":"无限进步.","tagline":"Infinite Progress...","actions":[{"theme":"brand","text":"Get Started","link":"/self/"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/ZiuChen"}]},"features":[{"icon":"📋","title":"Clipboard Manager","details":"A Powerful clipboard management tool"},{"icon":"✍🏻","title":"Super Markdown","details":"Powerful Markdown editor"},{"icon":"🚗","title":"JS Runner","details":"Run JavaScript dynamicly in Browser/Node.js"},{"icon":"🔑","title":"Bytemd Plugin","details":"Bytemd Plugin Library"},{"icon":"🍬","title":"ASOUL Browser Pet","details":"Keep an A-SOUL member as a pet in your browser"},{"icon":"🔧","title":"Typein","details":"Typein text, quickly perform browser operations"}]},"headers":[],"relativePath":"index.md","lastUpdated":1682528161000}'),n={name:"index.md"};function a(r,o,s,l,d,c){return t(),i("div")}const u=e(n,[["render",a]]);export{m as __pageData,u as default};
+import{_ as e,o as t,c as i}from"./chunks/framework.a304f0f7.js";const m=JSON.parse('{"title":"主页","description":"","frontmatter":{"layout":"home","title":"主页","hero":{"name":"ZiuChen","text":"无限进步.","tagline":"Infinite Progress...","actions":[{"theme":"brand","text":"Get Started","link":"/self/"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/ZiuChen"}]},"features":[{"icon":"📋","title":"Clipboard Manager","details":"A Powerful clipboard management tool"},{"icon":"✍🏻","title":"Super Markdown","details":"Powerful Markdown editor"},{"icon":"🚗","title":"JS Runner","details":"Run JavaScript dynamicly in Browser/Node.js"},{"icon":"🔑","title":"Bytemd Plugin","details":"Bytemd Plugin Library"},{"icon":"🍬","title":"ASOUL Browser Pet","details":"Keep an A-SOUL member as a pet in your browser"},{"icon":"🔧","title":"Typein","details":"Typein text, quickly perform browser operations"}]},"headers":[],"relativePath":"index.md","lastUpdated":1682586876000}'),n={name:"index.md"};function a(r,o,s,l,d,c){return t(),i("div")}const u=e(n,[["render",a]]);export{m as __pageData,u as default};
diff --git a/assets/note_CSS.md.945cb9b3.js b/assets/note_CSS.md.6a7eedec.js
similarity index 99%
rename from assets/note_CSS.md.945cb9b3.js
rename to assets/note_CSS.md.6a7eedec.js
index 93ccb554..16399bdf 100644
--- a/assets/note_CSS.md.945cb9b3.js
+++ b/assets/note_CSS.md.6a7eedec.js
@@ -1,4 +1,4 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const e="/assets/BFC-1.ed3fcfd0.jpg",o="/assets/BFC-2.3c4a8bbc.jpg",p="/assets/BFC-3.7daed619.jpg",c="/assets/BFC-4.b58515a7.jpg",h=JSON.parse('{"title":"CSS基础","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/CSS.md","lastUpdated":1682528161000}'),t={name:"note/CSS.md"},r=l(`

CSS基础

CSS提供了三种方法,可以将CSS样式应用到元素上:

  • 内联样式
    • 直接将样式写到元素的style属性上
  • 内部样式表
    • 将样式通过<style>标签写在<head>标签中,通过选择器应用到元素上
  • 外部样式表
    • 将样式写在外部文件如style.css中,再通过<link>标签引入

文本样式(text)

  • text-decoration 框线样式
    • line-through 删除线
    • overline 上划线
    • underline 下划线
    • none 不设置装饰线
  • text-transform
    • text-transform 属性指定如何将元素的文本大写。
    • 它可以用于使文本显示为全大写或全小写,也可单独对每一个单词进行操作。
  • text-indent
    • text-indent 属性能定义一个块元素首行文本内容之前的缩进量。
  • text-align
  • word/letter-spacing

text-align(重要)

行内内容(例如文字)如何相对它的块父元素对齐,left靠左 center居中 right靠右 justify两端对齐

默认值为left

需要注意的是,当子元素是div时,对父元素设置text-align是不生效的:

css
.box {
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const e="/assets/BFC-1.ed3fcfd0.jpg",o="/assets/BFC-2.3c4a8bbc.jpg",p="/assets/BFC-3.7daed619.jpg",c="/assets/BFC-4.b58515a7.jpg",h=JSON.parse('{"title":"CSS基础","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/CSS.md","lastUpdated":1682586876000}'),t={name:"note/CSS.md"},r=l(`

CSS基础

CSS提供了三种方法,可以将CSS样式应用到元素上:

  • 内联样式
    • 直接将样式写到元素的style属性上
  • 内部样式表
    • 将样式通过<style>标签写在<head>标签中,通过选择器应用到元素上
  • 外部样式表
    • 将样式写在外部文件如style.css中,再通过<link>标签引入

文本样式(text)

  • text-decoration 框线样式
    • line-through 删除线
    • overline 上划线
    • underline 下划线
    • none 不设置装饰线
  • text-transform
    • text-transform 属性指定如何将元素的文本大写。
    • 它可以用于使文本显示为全大写或全小写,也可单独对每一个单词进行操作。
  • text-indent
    • text-indent 属性能定义一个块元素首行文本内容之前的缩进量。
  • text-align
  • word/letter-spacing

text-align(重要)

行内内容(例如文字)如何相对它的块父元素对齐,left靠左 center居中 right靠右 justify两端对齐

默认值为left

需要注意的是,当子元素是div时,对父元素设置text-align是不生效的:

css
.box {
   height: 500px;
   text-align: center;
   background-color: red;
diff --git a/assets/note_CSS.md.945cb9b3.lean.js b/assets/note_CSS.md.6a7eedec.lean.js
similarity index 87%
rename from assets/note_CSS.md.945cb9b3.lean.js
rename to assets/note_CSS.md.6a7eedec.lean.js
index 621b6dc7..92c5029e 100644
--- a/assets/note_CSS.md.945cb9b3.lean.js
+++ b/assets/note_CSS.md.6a7eedec.lean.js
@@ -1 +1 @@
-import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const e="/assets/BFC-1.ed3fcfd0.jpg",o="/assets/BFC-2.3c4a8bbc.jpg",p="/assets/BFC-3.7daed619.jpg",c="/assets/BFC-4.b58515a7.jpg",h=JSON.parse('{"title":"CSS基础","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/CSS.md","lastUpdated":1682528161000}'),t={name:"note/CSS.md"},r=l("",535),i=[r];function D(d,y,F,C,u,b){return a(),n("div",null,i)}const A=s(t,[["render",D]]);export{h as __pageData,A as default};
+import{_ as s,o as a,c as n,R as l}from"./chunks/framework.a304f0f7.js";const e="/assets/BFC-1.ed3fcfd0.jpg",o="/assets/BFC-2.3c4a8bbc.jpg",p="/assets/BFC-3.7daed619.jpg",c="/assets/BFC-4.b58515a7.jpg",h=JSON.parse('{"title":"CSS基础","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/CSS.md","lastUpdated":1682586876000}'),t={name:"note/CSS.md"},r=l("",535),i=[r];function D(d,y,F,C,u,b){return a(),n("div",null,i)}const A=s(t,[["render",D]]);export{h as __pageData,A as default};
diff --git a/assets/note_Front-end Engineering.md.ca61b8ad.js b/assets/note_Front-end Engineering.md.c6edf7d1.js
similarity index 98%
rename from assets/note_Front-end Engineering.md.ca61b8ad.js
rename to assets/note_Front-end Engineering.md.c6edf7d1.js
index a4e9ca60..4d67a30e 100644
--- a/assets/note_Front-end Engineering.md.ca61b8ad.js	
+++ b/assets/note_Front-end Engineering.md.c6edf7d1.js	
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=JSON.parse('{"title":"前端工程化","description":"","frontmatter":{},"headers":[],"relativePath":"note/Front-end Engineering.md","lastUpdated":1682528161000}'),o={name:"note/Front-end Engineering.md"},e=l(`

前端工程化

Node.js

  • 什么是Node.JS Node的应用场景
  • JS代码执行
  • Node的输入和输出
  • Node的全局对象

什么是Node.js

Node.js是一个基于V8 JavaScript引擎JavaScript运行时环境

  • V8可以嵌入到任何C++应用程序中,无论是Chrome还是Node.js,事实上都嵌入了V8引擎来执行JavaScript代码
  • 在Chrome浏览器中,还需要解析、渲染HTML、CSS等相关渲染引擎,另外还需要支持浏览器操作的API、浏览器自己的事件循环
  • 在Node.js中我们也需要进行一些额外操作:文件系统读写、网络IO、加密、压缩解压文件等

可以简单总结出Node.js和浏览器的区别

  • Chrome浏览器

    • Blink负责解析HTML文档,遇到JavaScript标签时将内容交给V8引擎

    • Blink 是 Google Chrome 浏览器的渲染引擎,V8 是 Blink 内置的 JavaScript 引擎

      • 预分析:检查语法错误但不生成AST树
      • 生成AST:语法分析、词法分析后,生成抽象语法树(AST)
        • AST 为每一行代码定义键值对。初始类型标识符定义 AST 属于一个程序,然后所有代码行将定义在主体内部,主体是一个对象数组。
      • 生成字节码:基线编译器(Ignition)将 AST 转换为字节码
      • 生成机器代码:优化编译器 (Turbofan) 将字节码转换为优化的机器代码。另外,在逐行执行字节码的过程中,如果一段代码经常被执行,V8会直接将这段代码转换并保存为机器码,下次执行不需要经过字节码,优化了执行速度
  • Node.js

    • 只处理JavaScript代码 内部V8引擎负责JS代码的执行
    • JavaScript代码 -> V8 -> Node.js Bindings -> LibUV
    • LibUV是使用C语言编写的库,提供了事件循环、文件系统读写、网络IO、线程池等等内容

![The Node.js System](Front-end Engineering.assets/The Node.js System.jpeg)

Node.js的应用场景

  • 前端开发的库都是以node包形式管理的
  • npm yarn pnpm成为前端开发使用最多的工具
  • 使用Node.js作为Web服务器开发、中间件、代理服务器
  • 借助Node.js完成前后端渲染的同构应用
  • 编写脚本工具 构建项目 打包代码等
  • Electron桌面应用程序

Node.js的参数传递

process.argv

process.argv返回一个数组

  • 在代码中通过process.argv[2]读取来自命令行的额外参数
  • process.argv[0] process.argv[1]分别为node.exe的绝对路径和目标文件的绝对路径
js
// sum.js
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=JSON.parse('{"title":"前端工程化","description":"","frontmatter":{},"headers":[],"relativePath":"note/Front-end Engineering.md","lastUpdated":1682586876000}'),o={name:"note/Front-end Engineering.md"},e=l(`

前端工程化

Node.js

  • 什么是Node.JS Node的应用场景
  • JS代码执行
  • Node的输入和输出
  • Node的全局对象

什么是Node.js

Node.js是一个基于V8 JavaScript引擎JavaScript运行时环境

  • V8可以嵌入到任何C++应用程序中,无论是Chrome还是Node.js,事实上都嵌入了V8引擎来执行JavaScript代码
  • 在Chrome浏览器中,还需要解析、渲染HTML、CSS等相关渲染引擎,另外还需要支持浏览器操作的API、浏览器自己的事件循环
  • 在Node.js中我们也需要进行一些额外操作:文件系统读写、网络IO、加密、压缩解压文件等

可以简单总结出Node.js和浏览器的区别

  • Chrome浏览器

    • Blink负责解析HTML文档,遇到JavaScript标签时将内容交给V8引擎

    • Blink 是 Google Chrome 浏览器的渲染引擎,V8 是 Blink 内置的 JavaScript 引擎

      • 预分析:检查语法错误但不生成AST树
      • 生成AST:语法分析、词法分析后,生成抽象语法树(AST)
        • AST 为每一行代码定义键值对。初始类型标识符定义 AST 属于一个程序,然后所有代码行将定义在主体内部,主体是一个对象数组。
      • 生成字节码:基线编译器(Ignition)将 AST 转换为字节码
      • 生成机器代码:优化编译器 (Turbofan) 将字节码转换为优化的机器代码。另外,在逐行执行字节码的过程中,如果一段代码经常被执行,V8会直接将这段代码转换并保存为机器码,下次执行不需要经过字节码,优化了执行速度
  • Node.js

    • 只处理JavaScript代码 内部V8引擎负责JS代码的执行
    • JavaScript代码 -> V8 -> Node.js Bindings -> LibUV
    • LibUV是使用C语言编写的库,提供了事件循环、文件系统读写、网络IO、线程池等等内容

![The Node.js System](Front-end Engineering.assets/The Node.js System.jpeg)

Node.js的应用场景

  • 前端开发的库都是以node包形式管理的
  • npm yarn pnpm成为前端开发使用最多的工具
  • 使用Node.js作为Web服务器开发、中间件、代理服务器
  • 借助Node.js完成前后端渲染的同构应用
  • 编写脚本工具 构建项目 打包代码等
  • Electron桌面应用程序

Node.js的参数传递

process.argv

process.argv返回一个数组

  • 在代码中通过process.argv[2]读取来自命令行的额外参数
  • process.argv[0] process.argv[1]分别为node.exe的绝对路径和目标文件的绝对路径
js
// sum.js
 const x = process.argv[2]
 const y = process.argv[3]
 console.log(x + y)
sh
# 通过命令行运行node执行脚本 并传入参数
@@ -47,7 +47,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=
 })()
 
 // moduleB.js
-console.log(moduleA.name) // 在其他模块中调用

CommonJS

CommonJS是一种规范,当初命名为ServerJS,旨在浏览器以外的地方使用,后为体现其广泛性,改名为CommonJS,简称CJS

规范 是用来指导 实现的

  • Node 是CommonJS在服务端的代表实现
  • Browserify 是CommonJS在浏览器中的一种实现 (正在被淘汰)
  • WebPack 打包工具具备支持CommonJS的支持和转换

所以,Node.js对CommonJS进行了支持和实现,让JavaScript在Node上运行时可以实现模块化开发

  • 每个.js文件都是一个单独的模块
  • 每个模块中都包含变量exports module.exports require
js
// env.js
+console.log(moduleA.name) // 在其他模块中调用

CommonJS

CommonJS是一种规范,当初命名为ServerJS,旨在浏览器以外的地方使用,后为体现其广泛性,改名为CommonJS,简称CJS

规范 是用来指导 实现的

  • Node 是CommonJS在服务端的代表实现
  • Browserify 是CommonJS在浏览器中的一种实现 (正在被淘汰)
  • WebPack 打包工具具备支持CommonJS的支持和转换

所以,Node.js对CommonJS进行了支持和实现,让JavaScript在Node上运行时可以实现模块化开发

  • 每个.js文件都是一个单独的模块
  • 每个模块中都包含变量exports module.exports require
js
// env.js
 exports.name = 'Ziu'
 exports.age = 18
js
// utils.js
 module.exports = {
@@ -62,7 +62,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=
 sum(1, 2) // 3
 
 const { name, age } = require('env.js')
-console.log(name, age) // Ziu 18

exports的本质

exportsrequire在Node中的本质

  • exports是一个对象,我们可以在这个对象中添加很多属性,添加的属性则会被导出
    • 在没有向该对象添加任何属性之前,它是一个空对象
  • 当通过require导入时:const env = require('env.js')
    • env这个变量等于env.js中的exports对象
    • 本质上是envexports对象的引用赋值
    • { id: '...', exports: { ... }, loaded: true, ... }
  • 后续即使再次执行require导入模块,模块中的代码也不会重新执行(module.loaded属性)
    • 当从模块中取值时,会从已经加载的exports对象缓存上取值
js
// utils.js
+console.log(name, age) // Ziu 18

exports的本质

exportsrequire在Node中的本质

  • exports是一个对象,我们可以在这个对象中添加很多属性,添加的属性则会被导出
    • 在没有向该对象添加任何属性之前,它是一个空对象
  • 当通过require导入时:const env = require('env.js')
    • env这个变量等于env.js中的exports对象
    • 本质上是envexports对象的引用赋值
    • { id: '...', exports: { ... }, loaded: true, ... }
  • 后续即使再次执行require导入模块,模块中的代码也不会重新执行(module.loaded属性)
    • 当从模块中取值时,会从已经加载的exports对象缓存上取值
js
// utils.js
 exports.a = 0
 
 // 1s后修改a值
@@ -87,14 +87,14 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=
 module.exports = {
   name,
   run
-}

二者的区别

既然如此,为什么还要存在exports这个概念呢?

  • 在CommonJS中是没有module.exports的概念的
  • 为了实现模块的导出,Node.js使用的是Module类,每一个模块都是Module的实例,也就是module
  • 所以在Node.js中真正用于导出的并不是exports,而是module.exports
  • module对象中的exports属性是exports对象的一个引用
    • module.exports === exports === utils

如果module.exports不再引用exports对象了,修改exports对象也就没有意义了

js
// utils.js
+}

二者的区别

既然如此,为什么还要存在exports这个概念呢?

  • 在CommonJS中是没有module.exports的概念的
  • 为了实现模块的导出,Node.js使用的是Module类,每一个模块都是Module的实例,也就是module
  • 所以在Node.js中真正用于导出的并不是exports,而是module.exports
  • module对象中的exports属性是exports对象的一个引用
    • module.exports === exports === utils

如果module.exports不再引用exports对象了,修改exports对象也就没有意义了

js
// utils.js
 module.exports = {
   name: 'Ziu'
 }
 exports.age = 18
js
// index.js
 const utils = require('utils.js')
 console.log(utils.name) // Ziu
-console.log(utils.age) // undefined

当使用module.exports = { ... }后,模块中原有的exports不再被导入识别,导入的内容将变为module.exports指定的对象内容

require的本质

require是一个函数,可以帮助我们导入一个文件(模块)中导出的对象

  • 为什么可以省略掉.js后缀,直接使用require('./utils')
  • 为什么可以省略掉index.js,直接使用require('./tools')导入tools/index.js

这涉及到require在匹配路径后的查找规则:

分为三种情况:内置模块、自定义路径、包名

  • 导入Node.js内置的模块,如const path = require('path')
    • 直接返回该内置模块 并停止后续的查找
  • 根据路径导入自定义的模块,如const utils = require('./{filename}')
    • 按照路径寻找该模块./ ../ /
    • 如果指定了后缀名,则按照后缀名查找
    • 如果未指定后缀名,则:
      1. 直接查找该文件
      2. 查找{filename}.js文件
      3. 查找{filename}.json文件
      4. 查找{filename}.node文件
    • 如果按照上述方式没找到文件,则{filename}作为路径继续查找
    • 查找目录下的index文件 {filename}/index
      1. 查找{filename}/index.js文件
      2. ··· ···
    • 没找到:报错Cannot find module 'xxx'
  • 包名,如const lodash = require('lodash')
    • 到项目根目录的node_modules中查找
    • node_modules/{package_name}/index.js
    • 当前项目目录的node_modules找不到则继续向上查找,直到查找到根目录的node_modules

模块的加载过程

  • 模块在被第一次引入时,模块中的JS代码会被运行一次
    • 代码执行顺序与require的位置相关
  • 模块如果被多次引入,会被缓存,最终只加载一次
    • 这是因为每个模块对象module上都有一个属性loaded
    • loaded === false表示该模块尚未被加载
    • 第二次被require引入时会检查该属性是否为true
  • 如果有循环引用,加载顺序如何?
    • 数据结构:图结构(graph)遍历时有深度优先搜索(DFS)、广度优先搜索(BFS)两种算法
    • Node采用的是深度优先算法

CommonJS的缺点

  • 加载模块是同步加载的
    • 只有等到对应的模块加载完毕,当前模块中的内容才能被执行
    • 当然,在服务器中加载JS文件都是本地文件,加载速度非常快,不会受影响
  • 但是在浏览器中使用CommonJS
    • 需要先从服务器下载JS文件,后加载运行
    • 阻塞JS执行 阻塞页面加载
  • 在WebPack中使用CommonJS
    • CommonJS会被WebPack解析
    • 将CommonJS代码转化为bundle 浏览器可以直接运行

ESModule

  • ES6 模块采用编译时加载,使得编译时就能确定模块的依赖关系,有助于静态优化
  • CommonJS模块在运行时加载,且必须借助对象加载模块内容

exportimport用法概览

ESModule借助exportimport导入导出内容,需要注意的是导入导出的并不是对象

export定义的是当前模块导出的接口import可以导入来自其他不同模块的接口

  • export default可以设置默认导出对象
  • export { ... }可以统一导出多个内容
  • exportimport都可以使用as关键字重命名导出/导入的接口
  • import * from 'xxx' export * from 'xxx'批量导入/导出
js
// utils.js
+console.log(utils.age) // undefined

当使用module.exports = { ... }后,模块中原有的exports不再被导入识别,导入的内容将变为module.exports指定的对象内容

require的本质

require是一个函数,可以帮助我们导入一个文件(模块)中导出的对象

  • 为什么可以省略掉.js后缀,直接使用require('./utils')
  • 为什么可以省略掉index.js,直接使用require('./tools')导入tools/index.js

这涉及到require在匹配路径后的查找规则:

分为三种情况:内置模块、自定义路径、包名

  • 导入Node.js内置的模块,如const path = require('path')
    • 直接返回该内置模块 并停止后续的查找
  • 根据路径导入自定义的模块,如const utils = require('./{filename}')
    • 按照路径寻找该模块./ ../ /
    • 如果指定了后缀名,则按照后缀名查找
    • 如果未指定后缀名,则:
      1. 直接查找该文件
      2. 查找{filename}.js文件
      3. 查找{filename}.json文件
      4. 查找{filename}.node文件
    • 如果按照上述方式没找到文件,则{filename}作为路径继续查找
    • 查找目录下的index文件 {filename}/index
      1. 查找{filename}/index.js文件
      2. ··· ···
    • 没找到:报错Cannot find module 'xxx'
  • 包名,如const lodash = require('lodash')
    • 到项目根目录的node_modules中查找
    • node_modules/{package_name}/index.js
    • 当前项目目录的node_modules找不到则继续向上查找,直到查找到根目录的node_modules

模块的加载过程

  • 模块在被第一次引入时,模块中的JS代码会被运行一次
    • 代码执行顺序与require的位置相关
  • 模块如果被多次引入,会被缓存,最终只加载一次
    • 这是因为每个模块对象module上都有一个属性loaded
    • loaded === false表示该模块尚未被加载
    • 第二次被require引入时会检查该属性是否为true
  • 如果有循环引用,加载顺序如何?
    • 数据结构:图结构(graph)遍历时有深度优先搜索(DFS)、广度优先搜索(BFS)两种算法
    • Node采用的是深度优先算法

CommonJS的缺点

  • 加载模块是同步加载的
    • 只有等到对应的模块加载完毕,当前模块中的内容才能被执行
    • 当然,在服务器中加载JS文件都是本地文件,加载速度非常快,不会受影响
  • 但是在浏览器中使用CommonJS
    • 需要先从服务器下载JS文件,后加载运行
    • 阻塞JS执行 阻塞页面加载
  • 在WebPack中使用CommonJS
    • CommonJS会被WebPack解析
    • 将CommonJS代码转化为bundle 浏览器可以直接运行

ESModule

  • ES6 模块采用编译时加载,使得编译时就能确定模块的依赖关系,有助于静态优化
  • CommonJS模块在运行时加载,且必须借助对象加载模块内容

exportimport用法概览

ESModule借助exportimport导入导出内容,需要注意的是导入导出的并不是对象

export定义的是当前模块导出的接口import可以导入来自其他不同模块的接口

  • export default可以设置默认导出对象
  • export { ... }可以统一导出多个内容
  • exportimport都可以使用as关键字重命名导出/导入的接口
  • import * from 'xxx' export * from 'xxx'批量导入/导出
js
// utils.js
 export function sum(a, b) {
   return a + b
 }
@@ -114,7 +114,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=
 
 sum(1, 2) // 3
 sub(2, 3) // -1
-log(name, age, ENV_VARIABLE) // 'Ziu' 18 'Hello, World!'

需要注意的是,在浏览器中要使用ESModule,需要为<script>标签添加module标记:

<script src="index.js" type="module"></script>

  • 当浏览器解析到type="module"的JS代码后,会分析模块中导入的ESModule模块
  • 每导入一个ESModule模块,浏览器都会发起一个HTTP请求去加载它
  • 在本地运行时加载不同协议头的文件会遇到跨域问题,需要开启本地Web服务器

另外,exportimport必须位于模块的顶层,如果位于作用域内会报错,因为这就无法对代码进行静态分析优化了

export详解

export有两种导出方式:

  • 命名导出 export const name = 'Ziu' export { v1, v2 } export * from 'xxx'
    • 导出时需要指定名字
    • 导入时也需要知道对应的名字
  • 默认导出 export default AGE = 18
    • 在从其他位置导入时需要为此默认导出指定新的名字
    • 给用户方便:不必阅读文档就可以加载模块

值的动态绑定

  • ESModule模块通过export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值
  • CommonJS模块输出的是值的缓存,不存在动态更新

我们援引之前介绍CJS时的案例,将后缀名改为mjs即可在Node中运行ESModule模块代码

初始获得的a值为0,经过1s后,在utils.mjs中修改了a的值,这时导入utils.mjs模块的其他模块可以获取到a最新的值

js
// utils.mjs
+log(name, age, ENV_VARIABLE) // 'Ziu' 18 'Hello, World!'

需要注意的是,在浏览器中要使用ESModule,需要为<script>标签添加module标记:

<script src="index.js" type="module"></script>

  • 当浏览器解析到type="module"的JS代码后,会分析模块中导入的ESModule模块
  • 每导入一个ESModule模块,浏览器都会发起一个HTTP请求去加载它
  • 在本地运行时加载不同协议头的文件会遇到跨域问题,需要开启本地Web服务器

另外,exportimport必须位于模块的顶层,如果位于作用域内会报错,因为这就无法对代码进行静态分析优化了

export详解

export有两种导出方式:

  • 命名导出 export const name = 'Ziu' export { v1, v2 } export * from 'xxx'
    • 导出时需要指定名字
    • 导入时也需要知道对应的名字
  • 默认导出 export default AGE = 18
    • 在从其他位置导入时需要为此默认导出指定新的名字
    • 给用户方便:不必阅读文档就可以加载模块

值的动态绑定

  • ESModule模块通过export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值
  • CommonJS模块输出的是值的缓存,不存在动态更新

我们援引之前介绍CJS时的案例,将后缀名改为mjs即可在Node中运行ESModule模块代码

初始获得的a值为0,经过1s后,在utils.mjs中修改了a的值,这时导入utils.mjs模块的其他模块可以获取到a最新的值

js
// utils.mjs
 export let a = 0
 
 // 1s后修改a值
@@ -152,7 +152,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=
 } else {
   import('light.js').then(() => ...)
 }

传入动态值

js
let moduleName = () => ['Home', 'History', 'User'][0]
-import(\`./\${moduleName()}.js\`)

import.meta

ES2020引入了import.meta,它仅能在模块内部使用,包含一些模块自身的信息,即模块元信息

  • import.meta.url 返回当前模块的URL路径
    • 浏览器加载ESModule都是通过HTTP发起请求
      • 例如当前模块为fetchData.js,要在模块内引入一个名为data.json的数据:
      • import( new URL('data.json', import.meta.url) )
    • Node.js环境下,该值都是file://协议的链接
  • import.meta.scriptElement
    • 浏览器特有的属性
    • 返回加载模块的<script>标签,相当于document.currentScript

规范中并未规定import.meta中包含哪些属性,一般包括上面两个属性

深入理解模块加载

ESModule的解析过程

ESModule的解析过程可以分为三个阶段:

  • 构建 Construction
    • 根据地址查找JS文件,并发起HTTP请求下载,将其解析为模块记录 Module Record
  • 实例化 Instatiation
    • 对模块记录进行实例化,并为其分配内存空间
    • 解析ESModule模块的导入和导出语句,将模块指向对应的内存地址
    • 例如export const name = 'Ziu',会将变量name添加到模块环境记录中 Module Enviroment Record
  • 运行 Evaluation
    • 运行代码,计算值,并且将值填充到内存地址中
    • 将导入导出的赋给对应的变量name = 'Ziu'

![ESModule解析过程](Front-end Engineering.assets/esmodule-phases.png)

文章推荐:ES modules: A cartoon deep-dive

MJS和CJS的区别

  • CommonJS模块输出的是值的拷贝,而ESModule模块输出的是值的引用
    • CJS导出的变量,其值如果在模块内发生变化,外部导入是不会同步更新的,除非导出的是一个取值函数
    • MJS导出变量,外部模块每次访问时都会得到该变量最新的值,即使变量在模块内被修改了
  • CommonJS模块是运行时加载,而ESModule是编译时输出接口
    • CJS是通过对象实现的导入导出,它在运行时才被确定依赖关系和其值
    • MJS则是通过静态定义,在代码运行之前的静态解析阶段即可确定模块的导入导出内容
  • CommonJS模块的require()是同步加载模块,而ESModule模块的import命令是异步加载模块
    • import命令拥有一个独立的模块依赖的解析阶段

CJS中的循环加载

设想有以下两文件 a.jsb.js

js
// a.js
+import(\`./\${moduleName()}.js\`)

import.meta

ES2020引入了import.meta,它仅能在模块内部使用,包含一些模块自身的信息,即模块元信息

  • import.meta.url 返回当前模块的URL路径
    • 浏览器加载ESModule都是通过HTTP发起请求
      • 例如当前模块为fetchData.js,要在模块内引入一个名为data.json的数据:
      • import( new URL('data.json', import.meta.url) )
    • Node.js环境下,该值都是file://协议的链接
  • import.meta.scriptElement
    • 浏览器特有的属性
    • 返回加载模块的<script>标签,相当于document.currentScript

规范中并未规定import.meta中包含哪些属性,一般包括上面两个属性

深入理解模块加载

ESModule的解析过程

ESModule的解析过程可以分为三个阶段:

  • 构建 Construction
    • 根据地址查找JS文件,并发起HTTP请求下载,将其解析为模块记录 Module Record
  • 实例化 Instatiation
    • 对模块记录进行实例化,并为其分配内存空间
    • 解析ESModule模块的导入和导出语句,将模块指向对应的内存地址
    • 例如export const name = 'Ziu',会将变量name添加到模块环境记录中 Module Enviroment Record
  • 运行 Evaluation
    • 运行代码,计算值,并且将值填充到内存地址中
    • 将导入导出的赋给对应的变量name = 'Ziu'

![ESModule解析过程](Front-end Engineering.assets/esmodule-phases.png)

文章推荐:ES modules: A cartoon deep-dive

MJS和CJS的区别

  • CommonJS模块输出的是值的拷贝,而ESModule模块输出的是值的引用
    • CJS导出的变量,其值如果在模块内发生变化,外部导入是不会同步更新的,除非导出的是一个取值函数
    • MJS导出变量,外部模块每次访问时都会得到该变量最新的值,即使变量在模块内被修改了
  • CommonJS模块是运行时加载,而ESModule是编译时输出接口
    • CJS是通过对象实现的导入导出,它在运行时才被确定依赖关系和其值
    • MJS则是通过静态定义,在代码运行之前的静态解析阶段即可确定模块的导入导出内容
  • CommonJS模块的require()是同步加载模块,而ESModule模块的import命令是异步加载模块
    • import命令拥有一个独立的模块依赖的解析阶段

CJS中的循环加载

设想有以下两文件 a.jsb.js

js
// a.js
 exports.done = false
 const b = require('./b.js')
 console.log('在 a.js 之中,b.done = %j', b.done)
@@ -169,7 +169,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=
 b.js 执行完毕
  a.js 之中,b.done = true
 a.js 执行完毕
- main.js 之中, a.done=true, b.done=true

总结:

  • CJS的模块导出是输出值的拷贝,而不是引用,值的变化不是动态的,而是会被缓存的
  • 循环加载时,CJS模块导出的值是当前已经执行部分代码产生的结果的值,而不是模块代码完全执行完后的最终值

MJS中的循环加载

ESModule的导入和导出与CommonJS有本质不同:

js
// a.mjs
+ main.js 之中, a.done=true, b.done=true

总结:

  • CJS的模块导出是输出值的拷贝,而不是引用,值的变化不是动态的,而是会被缓存的
  • 循环加载时,CJS模块导出的值是当前已经执行部分代码产生的结果的值,而不是模块代码完全执行完后的最终值

MJS中的循环加载

ESModule的导入和导出与CommonJS有本质不同:

js
// a.mjs
 import { bar } from './b.mjs'
 console.log('a.mjs')
 console.log(bar)
@@ -177,7 +177,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=
 import { foo } from './a.mjs'
 console.log('b.mjs')
 console.log(foo)
-export let bar = 'bar'

执行a.mjs后发现报错了:ReferenceError: Cannot access 'foo' before initialization,变量foo未定义

  • MJS模块在代码执行前会进行静态分析
  • 分析a.mjs的依赖关系时,发现其依赖了b.mjs
  • 于是加载b.mjs并解析它的依赖关系
  • 解析b.mjs的过程中,发现它又依赖了a.mjs
  • 这时引擎不会再去加载a.mjs 而是认为a.mjs这个模块的Module Record已经存在了
  • 继续向下执行,执行到console.log(foo)时发现foo未定义 抛出错误

要实现预期效果,可以将foobar改写为取值函数,这时执行就不会报错了:

js
// a.mjs
+export let bar = 'bar'

执行a.mjs后发现报错了:ReferenceError: Cannot access 'foo' before initialization,变量foo未定义

  • MJS模块在代码执行前会进行静态分析
  • 分析a.mjs的依赖关系时,发现其依赖了b.mjs
  • 于是加载b.mjs并解析它的依赖关系
  • 解析b.mjs的过程中,发现它又依赖了a.mjs
  • 这时引擎不会再去加载a.mjs 而是认为a.mjs这个模块的Module Record已经存在了
  • 继续向下执行,执行到console.log(foo)时发现foo未定义 抛出错误

要实现预期效果,可以将foobar改写为取值函数,这时执行就不会报错了:

js
// a.mjs
 import { bar } from './b.mjs'
 console.log('a.mjs')
 console.log(bar())
diff --git a/assets/note_Front-end Engineering.md.ca61b8ad.lean.js b/assets/note_Front-end Engineering.md.c6edf7d1.lean.js
similarity index 84%
rename from assets/note_Front-end Engineering.md.ca61b8ad.lean.js
rename to assets/note_Front-end Engineering.md.c6edf7d1.lean.js
index 4564aaad..431e6aa2 100644
--- a/assets/note_Front-end Engineering.md.ca61b8ad.lean.js	
+++ b/assets/note_Front-end Engineering.md.c6edf7d1.lean.js	
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=JSON.parse('{"title":"前端工程化","description":"","frontmatter":{},"headers":[],"relativePath":"note/Front-end Engineering.md","lastUpdated":1682528161000}'),o={name:"note/Front-end Engineering.md"},e=l("",254),p=[e];function c(r,t,i,d,y,D){return n(),a("div",null,p)}const u=s(o,[["render",c]]);export{F as __pageData,u as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const F=JSON.parse('{"title":"前端工程化","description":"","frontmatter":{},"headers":[],"relativePath":"note/Front-end Engineering.md","lastUpdated":1682586876000}'),o={name:"note/Front-end Engineering.md"},e=l("",254),p=[e];function c(r,t,i,d,y,D){return n(),a("div",null,p)}const u=s(o,[["render",c]]);export{F as __pageData,u as default};
diff --git a/assets/note_JavaScript.md.5dbfafaa.js b/assets/note_JavaScript.md.d5705bda.js
similarity index 91%
rename from assets/note_JavaScript.md.5dbfafaa.js
rename to assets/note_JavaScript.md.d5705bda.js
index 2e471224..02f5663c 100644
--- a/assets/note_JavaScript.md.5dbfafaa.js
+++ b/assets/note_JavaScript.md.d5705bda.js
@@ -1 +1 @@
-import{_ as t,o as e,c as r,z as a,a as c}from"./chunks/framework.a304f0f7.js";const S=JSON.parse('{"title":"JavaScript 基础","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/JavaScript.md","lastUpdated":1682528161000}'),s={name:"note/JavaScript.md"},i=a("h1",{id:"javascript-基础",tabindex:"-1"},[c("JavaScript 基础 "),a("a",{class:"header-anchor",href:"#javascript-基础","aria-label":'Permalink to "JavaScript 基础"'},"​")],-1),n=a("p",null,"JavaScript 组成",-1),o=a("ul",null,[a("li",null,"ECMAScript 定义语言规范"),a("li",null,"DOM 用于操作文档的API"),a("li",null,"BOM 用于操作浏览器的API")],-1),l=[i,n,o];function p(d,_,h,u,v,f){return e(),r("div",null,l)}const J=t(s,[["render",p]]);export{S as __pageData,J as default};
+import{_ as t,o as e,c as r,z as a,a as c}from"./chunks/framework.a304f0f7.js";const S=JSON.parse('{"title":"JavaScript 基础","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/JavaScript.md","lastUpdated":1682586876000}'),s={name:"note/JavaScript.md"},i=a("h1",{id:"javascript-基础",tabindex:"-1"},[c("JavaScript 基础 "),a("a",{class:"header-anchor",href:"#javascript-基础","aria-label":'Permalink to "JavaScript 基础"'},"​")],-1),n=a("p",null,"JavaScript 组成",-1),o=a("ul",null,[a("li",null,"ECMAScript 定义语言规范"),a("li",null,"DOM 用于操作文档的API"),a("li",null,"BOM 用于操作浏览器的API")],-1),l=[i,n,o];function p(d,_,h,u,v,f){return e(),r("div",null,l)}const J=t(s,[["render",p]]);export{S as __pageData,J as default};
diff --git a/assets/note_JavaScript.md.5dbfafaa.lean.js b/assets/note_JavaScript.md.d5705bda.lean.js
similarity index 91%
rename from assets/note_JavaScript.md.5dbfafaa.lean.js
rename to assets/note_JavaScript.md.d5705bda.lean.js
index 2e471224..02f5663c 100644
--- a/assets/note_JavaScript.md.5dbfafaa.lean.js
+++ b/assets/note_JavaScript.md.d5705bda.lean.js
@@ -1 +1 @@
-import{_ as t,o as e,c as r,z as a,a as c}from"./chunks/framework.a304f0f7.js";const S=JSON.parse('{"title":"JavaScript 基础","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/JavaScript.md","lastUpdated":1682528161000}'),s={name:"note/JavaScript.md"},i=a("h1",{id:"javascript-基础",tabindex:"-1"},[c("JavaScript 基础 "),a("a",{class:"header-anchor",href:"#javascript-基础","aria-label":'Permalink to "JavaScript 基础"'},"​")],-1),n=a("p",null,"JavaScript 组成",-1),o=a("ul",null,[a("li",null,"ECMAScript 定义语言规范"),a("li",null,"DOM 用于操作文档的API"),a("li",null,"BOM 用于操作浏览器的API")],-1),l=[i,n,o];function p(d,_,h,u,v,f){return e(),r("div",null,l)}const J=t(s,[["render",p]]);export{S as __pageData,J as default};
+import{_ as t,o as e,c as r,z as a,a as c}from"./chunks/framework.a304f0f7.js";const S=JSON.parse('{"title":"JavaScript 基础","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/JavaScript.md","lastUpdated":1682586876000}'),s={name:"note/JavaScript.md"},i=a("h1",{id:"javascript-基础",tabindex:"-1"},[c("JavaScript 基础 "),a("a",{class:"header-anchor",href:"#javascript-基础","aria-label":'Permalink to "JavaScript 基础"'},"​")],-1),n=a("p",null,"JavaScript 组成",-1),o=a("ul",null,[a("li",null,"ECMAScript 定义语言规范"),a("li",null,"DOM 用于操作文档的API"),a("li",null,"BOM 用于操作浏览器的API")],-1),l=[i,n,o];function p(d,_,h,u,v,f){return e(),r("div",null,l)}const J=t(s,[["render",p]]);export{S as __pageData,J as default};
diff --git a/assets/note_JavaScriptEnhanced.md.63433dba.js b/assets/note_JavaScriptEnhanced.md.0ff38e63.js
similarity index 99%
rename from assets/note_JavaScriptEnhanced.md.63433dba.js
rename to assets/note_JavaScriptEnhanced.md.0ff38e63.js
index 5f4be275..bcdebaac 100644
--- a/assets/note_JavaScriptEnhanced.md.63433dba.js
+++ b/assets/note_JavaScriptEnhanced.md.0ff38e63.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p="/assets/image-20221118222207332-16687813334481.64935e06.png",o="/assets/image-20221118222311200-16687813941873.8c07f79f.png",e="/assets/image-20221122103111654.29f48e34.png",c="/assets/image-20221122103256116.b7d76f81.png",r="/assets/image-20221122103715428.587185d1.png",t="/assets/image-20221125090752249.baeedc2b.png",y="/assets/image-20221125094148365.3adfbe2d.png",g=JSON.parse('{"title":"JavaScript 高级教程","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/JavaScriptEnhanced.md","lastUpdated":1682528161000}'),F={name:"note/JavaScriptEnhanced.md"},D=l(`

JavaScript 高级教程

函数中this指向

函数在调用时, Javascript会默认为this绑定一个值

js
// 定义一个函数
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p="/assets/image-20221118222207332-16687813334481.64935e06.png",o="/assets/image-20221118222311200-16687813941873.8c07f79f.png",e="/assets/image-20221122103111654.29f48e34.png",c="/assets/image-20221122103256116.b7d76f81.png",r="/assets/image-20221122103715428.587185d1.png",t="/assets/image-20221125090752249.baeedc2b.png",y="/assets/image-20221125094148365.3adfbe2d.png",g=JSON.parse('{"title":"JavaScript 高级教程","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/JavaScriptEnhanced.md","lastUpdated":1682586876000}'),F={name:"note/JavaScriptEnhanced.md"},D=l(`

JavaScript 高级教程

函数中this指向

函数在调用时, Javascript会默认为this绑定一个值

js
// 定义一个函数
 function foo() {
   console.log(this)
 }
diff --git a/assets/note_JavaScriptEnhanced.md.63433dba.lean.js b/assets/note_JavaScriptEnhanced.md.0ff38e63.lean.js
similarity index 92%
rename from assets/note_JavaScriptEnhanced.md.63433dba.lean.js
rename to assets/note_JavaScriptEnhanced.md.0ff38e63.lean.js
index 8137501d..59c11fce 100644
--- a/assets/note_JavaScriptEnhanced.md.63433dba.lean.js
+++ b/assets/note_JavaScriptEnhanced.md.0ff38e63.lean.js
@@ -1 +1 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p="/assets/image-20221118222207332-16687813334481.64935e06.png",o="/assets/image-20221118222311200-16687813941873.8c07f79f.png",e="/assets/image-20221122103111654.29f48e34.png",c="/assets/image-20221122103256116.b7d76f81.png",r="/assets/image-20221122103715428.587185d1.png",t="/assets/image-20221125090752249.baeedc2b.png",y="/assets/image-20221125094148365.3adfbe2d.png",g=JSON.parse('{"title":"JavaScript 高级教程","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/JavaScriptEnhanced.md","lastUpdated":1682528161000}'),F={name:"note/JavaScriptEnhanced.md"},D=l("",668),i=[D];function A(C,b,u,d,m,h){return n(),a("div",null,i)}const E=s(F,[["render",A]]);export{g as __pageData,E as default};
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p="/assets/image-20221118222207332-16687813334481.64935e06.png",o="/assets/image-20221118222311200-16687813941873.8c07f79f.png",e="/assets/image-20221122103111654.29f48e34.png",c="/assets/image-20221122103256116.b7d76f81.png",r="/assets/image-20221122103715428.587185d1.png",t="/assets/image-20221125090752249.baeedc2b.png",y="/assets/image-20221125094148365.3adfbe2d.png",g=JSON.parse('{"title":"JavaScript 高级教程","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"note/JavaScriptEnhanced.md","lastUpdated":1682586876000}'),F={name:"note/JavaScriptEnhanced.md"},D=l("",668),i=[D];function A(C,b,u,d,m,h){return n(),a("div",null,i)}const E=s(F,[["render",A]]);export{g as __pageData,E as default};
diff --git a/assets/note_MySQL.md.8ab2ead8.js b/assets/note_MySQL.md.0b6704a4.js
similarity index 99%
rename from assets/note_MySQL.md.8ab2ead8.js
rename to assets/note_MySQL.md.0b6704a4.js
index 59756741..41e3afe2 100644
--- a/assets/note_MySQL.md.8ab2ead8.js
+++ b/assets/note_MySQL.md.0b6704a4.js
@@ -1,4 +1,4 @@
-import{_ as s,o as a,c as l,R as n}from"./chunks/framework.a304f0f7.js";const D=JSON.parse('{"title":"MySQL","description":"","frontmatter":{},"headers":[],"relativePath":"note/MySQL.md","lastUpdated":1682528161000}'),e={name:"note/MySQL.md"},t=n(`

MySQL

MySQL基础篇

MySQL简单使用

在命令行窗口输入

sh
mysql -uroot -p1234 -hlocalhost -P3306

指定用户名为 root 密码为 1234 连接host为 localhost 端口号为 3306

除了以明文方式输入密码,也可以通过另一种方式登录:

sh
mysql -u root -p
+import{_ as s,o as a,c as l,R as n}from"./chunks/framework.a304f0f7.js";const D=JSON.parse('{"title":"MySQL","description":"","frontmatter":{},"headers":[],"relativePath":"note/MySQL.md","lastUpdated":1682586876000}'),e={name:"note/MySQL.md"},t=n(`

MySQL

MySQL基础篇

MySQL简单使用

在命令行窗口输入

sh
mysql -uroot -p1234 -hlocalhost -P3306

指定用户名为 root 密码为 1234 连接host为 localhost 端口号为 3306

除了以明文方式输入密码,也可以通过另一种方式登录:

sh
mysql -u root -p
 1234

进入mysql命令行工具后,查看所有表:

sql
show databases;

MySQL默认为我们创建了四个表 information_schema mysql performance_schema sys

创建一个新的数据库:

sql
create database dbtest1;

使用数据库:

sql
use dbtest1;

创建一张表,初始化idname字段:

sql
create table employees(id int, name varchar(15));

查看表中数据:

sql
select * from emoloyees;

插入一条数据:

sql
insert into employees values(1001, 'Tom');
 insert into employees values(1002, 'Jack');

当我们向表中插入中文数据时,5.7版本的MySQL会报错,而8.0版本则不会:

sql
insert into employees values(1003, '杰瑞');

检查一下表的信息:

sql
show create table employees;

可以发现,表的默认字符集是 CHARSET=latin1 拉丁字符集,不包含汉字。

查看编码与比较规则:

百分号%表示一个到多个字符

sql
show variables like 'character_%';
 show variables like 'collation_%';

若是5.7版本,默认的编码字符集为latin1,而最新的8.0utf8。配置文件可以在my.ini中修改

删除一个数据库

sql
drop database dbtest1;

基本的SELECT语句

SQL分类

  • DDL DataDefinitionLanguage 用于定义数据库对象(数据库 表 字段)
    • 主要语句关键字包括CREATE DROP ALERT
  • DML DataManipulationLanguage 用于对数据库表中的数据进行增删改查
    • 主要语句关键字包括INSERT DELETE UPDATE SELECT
    • SELECT是SQL语言的基础,最为重要
  • DQL DataQueryLanguage 用来查询数据库中表的记录
    • 由于查询语句使用的非常频繁,将查询语句单拎出来自成一类
  • DCL DataControlLanguage 用来创建数据库用户、控制数据库的访问权限
    • 主要的语句关键字包括GRANT REVOKE COMMIT ROLLBACK SAVEPOINT

SQL规则和规范

  • SQL语句可以单行或多行书写,为了提高可读性,各子句分行写,必要时使用缩进,以分号结尾
  • 每条命令以 ;\\g\\G 结束
  • 关键字不能被缩写也不能分行
  • 关于标点符号
    • 必须保证所有的()、单引号、双引号是成对结束的
    • 必须使用英文状态下的半角输入方式
    • 字符串型和日期时间类型的数据可以使用单引号(' ')表示
    • 列的别名,尽量使用双引号(" "),而且不建议省略as

SQL大小写规则

  • MySQL 在 Windows 环境下是大小写不敏感的
  • MySQL 在 Linux 环境下是大小写敏感的
    • 数据库名、表名、表的别名、变量名是严格区分大小写的
    • 关键字、函数名、列名(或字段名)、列的别名(字段的别名) 是忽略大小写的。
  • 推荐采用统一的书写规范:
    • 数据库名、表名、表别名、字段名、字段别名等都小写
    • SQL 关键字、函数名、绑定变量等都大写

注释书写方法

  • 单行注释:--注释内容# 注释内容 (MySQL独有)
  • 多行注释: /* 注释内容 */

DDL - 数据库操作

  • 查询
    • 查询所有数据库 SHOW DATABASES;
    • 查询当前数据库 SELECT DATABASE();
  • 创建
    • CREATE DATABASE [IF NOT EXISTS] 数据库名 [DEFAULT CHARSET 字符集] [COLLATE 排序规则];
  • 删除
    • DROP DATABSE [IF EXISTS] 数据库名
  • 使用
    • USE 数据库名
shell
mysql -u root -p # 进入mysql
sql
SHOW DATABASES; # 展示所有数据库
diff --git a/assets/note_MySQL.md.8ab2ead8.lean.js b/assets/note_MySQL.md.0b6704a4.lean.js
similarity index 83%
rename from assets/note_MySQL.md.8ab2ead8.lean.js
rename to assets/note_MySQL.md.0b6704a4.lean.js
index 16823c1b..e3bf63c0 100644
--- a/assets/note_MySQL.md.8ab2ead8.lean.js
+++ b/assets/note_MySQL.md.0b6704a4.lean.js
@@ -1 +1 @@
-import{_ as s,o as a,c as l,R as n}from"./chunks/framework.a304f0f7.js";const D=JSON.parse('{"title":"MySQL","description":"","frontmatter":{},"headers":[],"relativePath":"note/MySQL.md","lastUpdated":1682528161000}'),e={name:"note/MySQL.md"},t=n("",71),p=[t];function o(r,c,C,y,i,d){return a(),l("div",null,p)}const F=s(e,[["render",o]]);export{D as __pageData,F as default};
+import{_ as s,o as a,c as l,R as n}from"./chunks/framework.a304f0f7.js";const D=JSON.parse('{"title":"MySQL","description":"","frontmatter":{},"headers":[],"relativePath":"note/MySQL.md","lastUpdated":1682586876000}'),e={name:"note/MySQL.md"},t=n("",71),p=[t];function o(r,c,C,y,i,d){return a(),l("div",null,p)}const F=s(e,[["render",o]]);export{D as __pageData,F as default};
diff --git a/assets/note_React.md.0514d155.js b/assets/note_React.md.eeaf8749.js
similarity index 96%
rename from assets/note_React.md.0514d155.js
rename to assets/note_React.md.eeaf8749.js
index 237aae6d..b65be5b6 100644
--- a/assets/note_React.md.0514d155.js
+++ b/assets/note_React.md.eeaf8749.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p="/assets/react-life-cycle.67e890c0.png",o="/assets/prototype-setState.e1acb60c.png",e="/assets/redux-usage.7df84df8.svg",t="/assets/redux-async-action.0dc40e11.svg",d=JSON.parse('{"title":"React","description":"","frontmatter":{},"headers":[],"relativePath":"note/React.md","lastUpdated":1682528161000}'),c={name:"note/React.md"},r=l(`

React

邂逅React

React开发依赖

  • react 包含React的核心代码
  • react-dom 将React渲染到不同平台需要的核心代码
  • babel 将JSX转换成React代码的工具

为什么要拆分成这么多的包?

  • 不同的库各司其职,让库变得纯粹
  • react包中包含了 React Web 和 React Native 共同拥有的核心代码
  • react-dom 针对Web和Native完成的事情不同
    • Web端:react-dom会将JSX渲染成真实DOM,展示在浏览器中
    • Native端:react-dom会将JSX渲染成原生的控件(如Android中的Button,iOS中的UIButton)

Babel与React的关系

Babel是什么?

  • Babel又名Babel.js
  • 是目前前端使用非常广泛的编译器、转换器(Compiler/Transformer)
  • 提供对ES6语法的Polyfill,将ES6语法转为大多数浏览器都支持的ES5语法

二者之间的联系

  • 默认情况下React开发可以不使用Babel
  • 但是我们不可能使用React.createElement来编写代码
  • 通过Babel,我们可以直接编写JSX(JavaScript XML),让Babel帮我们转化为React.createElement

React初体验

我们通过CDN方式引入react、react-dom、babel这三个依赖

并且创建#root根节点,作为渲染React组件的容器,再新建一个script标签,键入以下内容

html
<div id="root"></div>
+import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p="/assets/react-life-cycle.67e890c0.png",o="/assets/prototype-setState.e1acb60c.png",e="/assets/redux-usage.7df84df8.svg",t="/assets/redux-async-action.0dc40e11.svg",d=JSON.parse('{"title":"React","description":"","frontmatter":{},"headers":[],"relativePath":"note/React.md","lastUpdated":1682586876000}'),c={name:"note/React.md"},r=l(`

React

邂逅React

React开发依赖

  • react 包含React的核心代码
  • react-dom 将React渲染到不同平台需要的核心代码
  • babel 将JSX转换成React代码的工具

为什么要拆分成这么多的包?

  • 不同的库各司其职,让库变得纯粹
  • react包中包含了 React Web 和 React Native 共同拥有的核心代码
  • react-dom 针对Web和Native完成的事情不同
    • Web端:react-dom会将JSX渲染成真实DOM,展示在浏览器中
    • Native端:react-dom会将JSX渲染成原生的控件(如Android中的Button,iOS中的UIButton)

Babel与React的关系

Babel是什么?

  • Babel又名Babel.js
  • 是目前前端使用非常广泛的编译器、转换器(Compiler/Transformer)
  • 提供对ES6语法的Polyfill,将ES6语法转为大多数浏览器都支持的ES5语法

二者之间的联系

  • 默认情况下React开发可以不使用Babel
  • 但是我们不可能使用React.createElement来编写代码
  • 通过Babel,我们可以直接编写JSX(JavaScript XML),让Babel帮我们转化为React.createElement

React初体验

我们通过CDN方式引入react、react-dom、babel这三个依赖

并且创建#root根节点,作为渲染React组件的容器,再新建一个script标签,键入以下内容

html
<div id="root"></div>
 <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
 <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
 <script src="https://unpkg.com/babel-standalone@6/babel.min.js" crossorigin></script>
@@ -1024,7 +1024,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p=
       </div>
     )
   }
-}

Context跨组件传参

非父子组件之间的数据共享

  • props层层传递 跨组件会很不方便 对于中间那些本不需要这些props数据的组件是冗余的
  • 第三方状态库 外置于React 如Redux (实际开发中较为常用)
  • 事件总线 ...

针对跨组件传参的场景,React提供了一个API名为Context

  • Context 提供了一个在组件之间共享此类值的方式,而不是显式地通过组件树逐层传递props
  • 使用 Context 共享那些全局的数据,如主题色、用户登录状态、locales等

用Context实现跨组件传参

假设有App Profile UserCard三个嵌套组件,我们希望App中的 isDarkMode 状态能够透传到UserCard组件中

  • 全局通过 createContext 创建一个上下文
  • 根组件通过 DarkModeContext.Provider 标签与 value 传递值到上下文中
  • 需要使用到该值的子组件通过 UserCard.contextType = DarkModeContext 绑定到上下文
  • 随后即可在子组件中通过 this.context 获取到此上下文当前绑定的状态值
tsx
// context.js
+}

Context跨组件传参

非父子组件之间的数据共享

  • props层层传递 跨组件会很不方便 对于中间那些本不需要这些props数据的组件是冗余的
  • 第三方状态库 外置于React 如Redux (实际开发中较为常用)
  • 事件总线 ...

针对跨组件传参的场景,React提供了一个API名为Context

  • Context 提供了一个在组件之间共享此类值的方式,而不是显式地通过组件树逐层传递props
  • 使用 Context 共享那些全局的数据,如主题色、用户登录状态、locales等

用Context实现跨组件传参

假设有App Profile UserCard三个嵌套组件,我们希望App中的 isDarkMode 状态能够透传到UserCard组件中

  • 全局通过 createContext 创建一个上下文
  • 根组件通过 DarkModeContext.Provider 标签与 value 传递值到上下文中
  • 需要使用到该值的子组件通过 UserCard.contextType = DarkModeContext 绑定到上下文
  • 随后即可在子组件中通过 this.context 获取到此上下文当前绑定的状态值
tsx
// context.js
 import { createContext } from 'react'
 
 export const DarkModeContext = createContext()
tsx
// App.jsx
@@ -2159,7 +2159,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p=
 }
 const className = Object.entries(classNameMap).map(item => !!item[1] ? item[0] : '').join(' ')

手动编写动态绑定的className较为繁琐,可以依靠第三方库classnames帮我们完成(Vue已将类似的功能内置)

bash
npm i classnames

Usage Sample:

tsx
classNames('foo', 'bar')
 classNames('foo', { bar: true })
-classNames(...['foo', 'bar'])

Redux

  • Redux的核心思想
  • Redux的基本使用
  • React结合Redux
  • Redux的异步操作
  • redux-devtool
  • reducer的模块拆分

理解JavaScript的纯函数

  • 函数式编程中有一个非常重要的概念 纯函数,JavaScript符合函数式编程的范式,所以也有纯函数的概念
    • 在React开发中,纯函数被多次提及:
    • React组件被要求像一个纯函数(为什么是像,因为还有类组件)
    • Redux中有一个reducer的概念,同样是要求必须是一个纯函数
  • 掌握纯函数对于理解很多框架的设计都是有帮助的

一个纯函数必然具备以下特征:

  • 确定的输入一定产生确定的输出
  • 函数的执行过程中,不能产生副作用

为什么需要Redux

  • JS需要管理的状态越来越多,越来越复杂
  • 状态不断发生变化之间又相互依赖,这要求视图层也能同步更新
  • React提供了自动更新视图的方法,但状态仍需要手动管理
  • Redux可以帮我们管理状态,提供了可预测的状态管理
  • 框架无关,体积只有2KB大小

Redux的核心理念

Redux的核心理念 Store

  • 定义一个统一的规范来操作数据,这样就可以做到对数据的跟踪
  • list.push() list[0].age = 18

Redux的核心理念 Action

  • Redux要求:要修改数据,必须通过Action来修改
  • 所有数据的变化,必须通过派发(Patch)Action来更新
  • Action是一个普通的JS对象,用来描述此次更新的type与content
  • const action = { type: 'ADD_ITEM', item: { name: 'Ziu', age: 18 } }

Redux的核心理念 Reducer

  • 如何将Store和Action联系在一起?
  • reducer是一个纯函数
  • 完成的工作就是:将传入的state和action结合起来,生成一个新的state
  • patch => reducer => newState => Store

Redux Demo

下例中,通过createStore创建了一个Store(已经不推荐了)

  • initialState用于在调用createStore时作为默认值传入reducer
  • 后续每次store.dispatch都会调用reducer
  • 通过reducer更新state中的数据

在React中,可以通过store.subscribe注册State变化的监听回调

  • 当state发生变化时,通过调用this.forceUpdate触发组件的更新
  • 一般情况下,我们在componentDidMount注册监听回调,在componentWillUnmount解除监听
tsx
// App.jsx
+classNames(...['foo', 'bar'])

Redux

  • Redux的核心思想
  • Redux的基本使用
  • React结合Redux
  • Redux的异步操作
  • redux-devtool
  • reducer的模块拆分

理解JavaScript的纯函数

  • 函数式编程中有一个非常重要的概念 纯函数,JavaScript符合函数式编程的范式,所以也有纯函数的概念
    • 在React开发中,纯函数被多次提及:
    • React组件被要求像一个纯函数(为什么是像,因为还有类组件)
    • Redux中有一个reducer的概念,同样是要求必须是一个纯函数
  • 掌握纯函数对于理解很多框架的设计都是有帮助的

一个纯函数必然具备以下特征:

  • 确定的输入一定产生确定的输出
  • 函数的执行过程中,不能产生副作用

为什么需要Redux

  • JS需要管理的状态越来越多,越来越复杂
  • 状态不断发生变化之间又相互依赖,这要求视图层也能同步更新
  • React提供了自动更新视图的方法,但状态仍需要手动管理
  • Redux可以帮我们管理状态,提供了可预测的状态管理
  • 框架无关,体积只有2KB大小

Redux的核心理念

Redux的核心理念 Store

  • 定义一个统一的规范来操作数据,这样就可以做到对数据的跟踪
  • list.push() list[0].age = 18

Redux的核心理念 Action

  • Redux要求:要修改数据,必须通过Action来修改
  • 所有数据的变化,必须通过派发(Patch)Action来更新
  • Action是一个普通的JS对象,用来描述此次更新的type与content
  • const action = { type: 'ADD_ITEM', item: { name: 'Ziu', age: 18 } }

Redux的核心理念 Reducer

  • 如何将Store和Action联系在一起?
  • reducer是一个纯函数
  • 完成的工作就是:将传入的state和action结合起来,生成一个新的state
  • patch => reducer => newState => Store

Redux Demo

下例中,通过createStore创建了一个Store(已经不推荐了)

  • initialState用于在调用createStore时作为默认值传入reducer
  • 后续每次store.dispatch都会调用reducer
  • 通过reducer更新state中的数据

在React中,可以通过store.subscribe注册State变化的监听回调

  • 当state发生变化时,通过调用this.forceUpdate触发组件的更新
  • 一般情况下,我们在componentDidMount注册监听回调,在componentWillUnmount解除监听
tsx
// App.jsx
 import React, { PureComponent } from 'react'
 import store from './store'
 
@@ -2232,7 +2232,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p=
 
 const store = createStore(reducer)
 
-export default store

redux-usage

进一步封装

可以将耦合在一起的代码拆分到不同文件中

  • reducer抽取出来reducer.js,简化store/index.js内容
  • action.type抽取为常量constants.js,使用时做导入,以保证一致性
  • action抽取出来actionFactory.js,用于外部dispatch时规范类型
tsx
// store/index.js
+export default store

redux-usage

进一步封装

可以将耦合在一起的代码拆分到不同文件中

  • reducer抽取出来reducer.js,简化store/index.js内容
  • action.type抽取为常量constants.js,使用时做导入,以保证一致性
  • action抽取出来actionFactory.js,用于外部dispatch时规范类型
tsx
// store/index.js
 import { createStore } from 'redux'
 import reducer from './reducer'
 
@@ -2307,7 +2307,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p=
       </div>
     )
   }
-}

Redux的三大原则

单一数据源

  • 整个应用程序的状态都被存储在一棵Object Tree上
  • 且这个Object Tree只存储在一个Store中
  • 但Redux并不强制限制创建多Store,不利于数据维护
  • 单一数据源有利于整个应用程序的维护、追踪、修改

State属性是只读的

  • 允许修改State的方法只有patch action,不要直接修改State
  • 确保了View或网络请求都不能修改State
  • 保证所有的修改都能被追踪、按照严格的顺序执行,不用担心竞态(race condition)的问题

使用纯函数来执行修改

  • 通过reducer将旧State与新State联系在一起,并且返回一个新的State
  • 随着应用程序复杂程度增加,可以将reducer拆分为多个小的reducer,分别用于操作不同State Tree的某一部分
  • 所有的reducer都应该是纯函数,不能产生任何的副作用

优化重复代码

当编写了一些案例的时候会发现,React结合Redux时会编写很多重复的代码

在每个需要用到Redux中状态的组件中,都需要在不同生命周期做添加订阅/解除订阅的处理,组件初始化时还要从store中取最新的状态

针对重复代码的问题,可以使用之前学到的高阶组件来做优化

Redux官方提供的库react-redux,可以让我们更方便的在React中使用Redux

bash
npm i react-redux

在Profile组件中,通过高阶函数connect实现的

将store中需要的状态通过mapStoreToProps转为props,并将需要使用store中状态的组件传入调用connect返回的函数中

Profile组件中就可以从props中获取到store中的状态

tsx
// App.jsx
+}

Redux的三大原则

单一数据源

  • 整个应用程序的状态都被存储在一棵Object Tree上
  • 且这个Object Tree只存储在一个Store中
  • 但Redux并不强制限制创建多Store,不利于数据维护
  • 单一数据源有利于整个应用程序的维护、追踪、修改

State属性是只读的

  • 允许修改State的方法只有patch action,不要直接修改State
  • 确保了View或网络请求都不能修改State
  • 保证所有的修改都能被追踪、按照严格的顺序执行,不用担心竞态(race condition)的问题

使用纯函数来执行修改

  • 通过reducer将旧State与新State联系在一起,并且返回一个新的State
  • 随着应用程序复杂程度增加,可以将reducer拆分为多个小的reducer,分别用于操作不同State Tree的某一部分
  • 所有的reducer都应该是纯函数,不能产生任何的副作用

优化重复代码

当编写了一些案例的时候会发现,React结合Redux时会编写很多重复的代码

在每个需要用到Redux中状态的组件中,都需要在不同生命周期做添加订阅/解除订阅的处理,组件初始化时还要从store中取最新的状态

针对重复代码的问题,可以使用之前学到的高阶组件来做优化

Redux官方提供的库react-redux,可以让我们更方便的在React中使用Redux

bash
npm i react-redux

在Profile组件中,通过高阶函数connect实现的

将store中需要的状态通过mapStoreToProps转为props,并将需要使用store中状态的组件传入调用connect返回的函数中

Profile组件中就可以从props中获取到store中的状态

tsx
// App.jsx
 import React, { PureComponent } from 'react'
 import { Provider } from 'react-redux'
 import store from './store'
@@ -2377,7 +2377,7 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p=
       )
     }
   }
-)

本质上是connect内部对操作进行了封装,把逻辑隐藏起来了:

  • 调用connect这个高阶函数,返回一个高阶组件
  • 为高阶组件传入映射目标组件,最后高阶组件返回一个新组件
  • 新组件的props包含了来自Store中状态/dispatch的映射

异步Action

有些场景下,我们希望组件能够直接调用Store中的action来触发网络请求,并且获取到数据

但是dispatch只允许派发对象类型的Action,不能通过dispatch派发函数

可以通过中间件redux-thunk来对Redux做增强,让dispatch能够对函数进行派发

bash
npm i redux-thunk

通过applyMiddleware引入redux-thunk这个中间件:

tsx
// store/index.js
+)

本质上是connect内部对操作进行了封装,把逻辑隐藏起来了:

  • 调用connect这个高阶函数,返回一个高阶组件
  • 为高阶组件传入映射目标组件,最后高阶组件返回一个新组件
  • 新组件的props包含了来自Store中状态/dispatch的映射

异步Action

有些场景下,我们希望组件能够直接调用Store中的action来触发网络请求,并且获取到数据

但是dispatch只允许派发对象类型的Action,不能通过dispatch派发函数

可以通过中间件redux-thunk来对Redux做增强,让dispatch能够对函数进行派发

bash
npm i redux-thunk

通过applyMiddleware引入redux-thunk这个中间件:

tsx
// store/index.js
 import { createStore, applyMiddleware } from 'redux'
 import thunk from 'redux-thunk'
 import reducer from './reducer'
@@ -2464,4 +2464,89 @@ import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p=
     - constants.js
     - index.js
     - reducer.js
-  - ...

combineReducer函数

前面拆分Store时用到了combineReducer函数,将多个模块reducer组合到一起,函数内部是如何处理的?

  • 将传入的reducers合并到一个对象中,最终返回一个combination函数(相当于未拆分时传给createStorereducer函数)
  • 在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state
  • 新state会触发订阅者发生对应更新,而旧state可以有效地组织订阅者发生刷新
`,609),D=[r];function F(y,i,C,A,b,u){return n(),a("div",null,D)}const h=s(c,[["render",F]]);export{d as __pageData,h as default}; + - ...

combineReducer函数

前面拆分Store时用到了combineReducer函数,将多个模块reducer组合到一起,函数内部是如何处理的?

  • 将传入的reducers合并到一个对象中,最终返回一个combination函数(相当于未拆分时传给createStorereducer函数)
  • 在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state
  • 新state会触发订阅者发生对应更新,而旧state可以有效地组织订阅者发生刷新

下面简单写了一下combineReducer的实现原理

ts
// 使用
+combineReducer({
+  counter: combineReducer,
+  postList: postListReducer
+})
+
+// 创建一个新的reducer
+function reducer(state = {}, action) {
+  // 返回一个对象 是Store的state
+  return {
+    counter: counterReducer(state.counter, action),
+    postList: postListReducer(state.postList, action)
+  }
+}

ReduxToolkit

  • ReduxToolkit重构
  • ReduxToolkit异步
  • connect高阶组件
  • 中间件的实现原理
  • React状态管理选择

认识ReduxToolkit

之前在使用createStore创建Store时会出现deprecated标识,推荐我们使用@reduxjs/toolkit包中的configureStore函数

Redux Toolkit是官方推荐编写Redux逻辑的方法

  • 在前面学习Redux时已经发现,Redux的逻辑编写过于繁琐、麻烦
  • 代码分拆在不同模块中,存在大量重复代码
  • Redux Toolkit旨在成为编写Redux逻辑的标准方式,从而解决上面提到的问题
  • 这个包常被称为:RTK

使用ReduxToolkit重写Store

Redux Toolkit依赖于react-redux包,所以需要同时安装这二者

bash
npm i @reduxjs/toolkit react-redux

Redux Toolkit的核心API主要是下述几个:

  • configureStore 包装createStore以提供简化的配置选项和良好的默认值
    • 可以自动组合你的slice reducer 添加你提供的任何Redux中间件
    • 默认包含redux-thunk,并启用Redux DevTools Extension
  • createSlice 创建切片 片段
    • 接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有actions
  • createAsyncThunk
    • 接受一个动作类型字符串和一个返回Promise的函数
    • 并生成一个pending / fullfilled / rejected基于该承诺分派动作类型的thunk

写一个Demo:

tsx
// store/index.js
+import { configureStore } from '@reduxjs/toolkit'
+import counterSlice from './features/counter'
+
+const store = configureStore({
+  reducer: {
+    counter: counterSlice
+  }
+})
+
+export default store
tsx
// store/features/counter.js
+import { createSlice } from '@reduxjs/toolkit'
+
+const counterSlice = createSlice({
+  name: 'counter',
+  initialState: {
+    count: 0
+  },
+  reducers: {
+    addCount(state, action) {
+      const { payload } = action
+      state.count += payload
+    },
+    subCount(state, action) {
+      const { payload } = action
+      state.count -= payload
+    }
+  }
+})
+
+const { actions, reducer } = counterSlice
+
+export const { addCount, subCount } = actions
+
+export default reducer
tsx
// Counter.jsx
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { addCount, subCount } from '../store/features/counter'
+
+const mapStateToProps = (state) => ({
+  count: state.counter.count
+})
+
+const mapDispatchToProps = (dispatch) => ({
+  increment: (count) => {
+    const action = addCount(count)
+    return dispatch(action)
+  },
+  decrement: (count) => {
+    const action = subCount(count)
+    return dispatch(action)
+  }
+})
+
+export default connect(
+  mapStateToProps,
+  mapDispatchToProps
+)(
+  class Counter extends Component {
+    render() {
+      const { count } = this.props
+
+      return (
+        <div>
+          <h2>Counter</h2>
+          <div>count: {count}</div>
+          <button onClick={() => this.props.increment(1)}>+1</button>
+          <button onClick={() => this.props.decrement(1)}>-1</button>
+        </div>
+      )
+    }
+  }
+)

createSlice 函数参数解读

  • name 标记Slice 展示在dev-tool中
  • initialState 初始化状态
  • reducers 对象 对应之前的reducer函数
  • 返回值: 一个对象 包含所有actions

configureStore 解读

  • reducer 将slice中的reducer组成一个对象,传入此参数
  • middleware 额外的中间件
    • RTK已经为我们集成了redux-thunkredux-devtool两个中间件
  • devTools 布尔值 是否启用开发者工具

使用RTK执行异步dispatch

实际场景中都是在组件中发起网络请求,并且将状态更新到Store中

之前的开发中,我们通过redux-thunk这个中间件,让dispatch中可以进行异步操作

ReduxToolkit默认已经给我们集成了Thunk相关的功能:createAsyncThunk

下面我们使用RTK实现一下这个场景:在Profile中请求数据并保存在Store中,在App中展示

`,633),D=[r];function F(y,i,C,A,b,u){return n(),a("div",null,D)}const h=s(c,[["render",F]]);export{d as __pageData,h as default}; diff --git a/assets/note_React.md.0514d155.lean.js b/assets/note_React.md.eeaf8749.lean.js similarity index 76% rename from assets/note_React.md.0514d155.lean.js rename to assets/note_React.md.eeaf8749.lean.js index 471946a5..6d5f52c9 100644 --- a/assets/note_React.md.0514d155.lean.js +++ b/assets/note_React.md.eeaf8749.lean.js @@ -1 +1 @@ -import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p="/assets/react-life-cycle.67e890c0.png",o="/assets/prototype-setState.e1acb60c.png",e="/assets/redux-usage.7df84df8.svg",t="/assets/redux-async-action.0dc40e11.svg",d=JSON.parse('{"title":"React","description":"","frontmatter":{},"headers":[],"relativePath":"note/React.md","lastUpdated":1682528161000}'),c={name:"note/React.md"},r=l("",609),D=[r];function F(y,i,C,A,b,u){return n(),a("div",null,D)}const h=s(c,[["render",F]]);export{d as __pageData,h as default}; +import{_ as s,o as n,c as a,R as l}from"./chunks/framework.a304f0f7.js";const p="/assets/react-life-cycle.67e890c0.png",o="/assets/prototype-setState.e1acb60c.png",e="/assets/redux-usage.7df84df8.svg",t="/assets/redux-async-action.0dc40e11.svg",d=JSON.parse('{"title":"React","description":"","frontmatter":{},"headers":[],"relativePath":"note/React.md","lastUpdated":1682586876000}'),c={name:"note/React.md"},r=l("",633),D=[r];function F(y,i,C,A,b,u){return n(),a("div",null,D)}const h=s(c,[["render",F]]);export{d as __pageData,h as default}; diff --git a/assets/note_SSR.md.9e639932.js b/assets/note_SSR.md.f248ec9c.js similarity index 99% rename from assets/note_SSR.md.9e639932.js rename to assets/note_SSR.md.f248ec9c.js index a75c9f10..e9e72e44 100644 --- a/assets/note_SSR.md.9e639932.js +++ b/assets/note_SSR.md.f248ec9c.js @@ -1,4 +1,4 @@ -import{_ as l,o as e,c as o,R as a}from"./chunks/framework.a304f0f7.js";const D=JSON.parse('{"title":"服务端渲染(SSR)","description":"","frontmatter":{},"headers":[],"relativePath":"note/SSR.md","lastUpdated":1682528161000}'),i={name:"note/SSR.md"},t=a(`

服务端渲染(SSR)

  • 邂逅SPA和SSR
  • Node服务搭建
  • Vue3 + SSR搭建
  • SSR + Hydration 水合
  • Vue SSR + Router
  • Vue SSR + Pinia

邂逅SPA和SSR

我们使用Vue开发的网页一般都是单页面应用程序(SPA Single Page Application)

  • SPA应用是在客户端呈现的,我们称这个渲染过程为CSR(Client Side Rendering)
  • 常见的B端Web应用开发模式,渲染工作在客户端进行,服务器压力较轻,服务器直接返回未经加工的.html文件
  • 所需要的资源(HTML CSS JS等),在一次请求中就加载完成,首屏时间更长,需要额外的首屏优化

与之相对的,就是服务端渲染(SSR Server Side Rendering)

  • SSR并不是什么新鲜的概念,早期的JSP或PHP就已经体现了服务端渲染的原理
  • 但是传统开发模式,代码耦合度较高,不容易维护

于是,同构SSR应运而生,我们称之为BFF(Backend for Frontend 服务于前端的后端)

  • 前后端一体化,一套Vue / React代码在服务器上运行一遍,在到达浏览器又运行一遍。
  • 前后端都参与到渲染中,并且首次渲染出的HTML相同

浏览器请求.html文件 => 服务端运行Vue / React代码并生成.html => 发送.html文件给浏览器 => 浏览器显示网页内容

=> 浏览器加载JS文件 => 绑定DOM事件 客户端渲染接管界面 => 再次跳转路由就是客户端渲染 无需请求后台

SPA的优点与缺点

  • SPA的优点
    • 只需要加载一次 更好的用户体验
      • 只有一个.html文件,页面切换不需要重新加载,所以比传统Web应用程序更快
    • 轻松构建功能丰富的Web应用程序
  • SPA的缺点
    • SPA应用默认只返回一个空HTML文件,不利于SEO
    • 首屏加载资源过大,影响首屏渲染速度
    • 不利于复杂项目构建

SEO优化

  • 语义性HTML标记
    • 标题用 h1,一个页面只应当由一个 h1 标签,副标题用 h2 - h6
    • 不要过度使用 h 标签,多次使用不会增加SEO
    • 段落用 p 标签 列表用 ul 标签,且 li 只放在 ul
  • 每个页面都需要包含标题+内部链接
    • 每个页面对应的title,同一个网站所有页面都有内链可以指向首页
  • 保证链接可供抓取
    • <a href="https://www.example.com" />
    • <a href="/relative/path/file" />
  • meta标签优化:设置description和keywords等
  • 文本标记和img
    • 比如<b><strong>加粗文本的标签,爬虫会关注到该内容
    • img标签添加alt属性,图片加载失败时供爬虫读取alt内容
  • robots.txt 文件,规定爬虫可以访问网址上的哪些页面
  • sitemap.xml 站点地图,在站点地图列出所有网页,确保爬虫不会漏掉某些网页

服务端渲染 SSR

服务端渲染 SSR (Server Side Rendering)

  • 页面是在服务端渲染的,用户每请求一个SSR页面,都会先在服务端渲染
  • 服务端渲染完成后,返回给浏览器呈现,浏览器发现JS脚本,解析脚本,向服务器发起请求,之后网页就可以交互了
    • app = createSSRApp(App) renderToString(app) => App String Html
    • client_bundle.js 客户端通过脚本激活应用程序 让应用程序可以进行交互,这个过程叫水合( Hydration)
  • Vue Nuxt / React Next.js,SSR应用也称为同构应用(server_bundle.js & client_bundle.js)

SSR的优点

  • 更快的首屏渲染速度
    • 浏览器显示静态页面的内容要比JavaScript动态生成的内容快得多
    • 用户访问首页时立刻返回静态页面内容,而不需要等待浏览器先加载完整个SPA应用的JS代码
  • 更好的SEO
    • 爬虫擅长爬取HTML页面,服务端直接返回一个静态的HTML给浏览器
    • 有利于爬虫快速爬取网页内容,并编入索引,有利于SEO
  • SSR 应用程序在 Hydration 之后仍然可以保留Web应用程序的可交互性
    • 如:前端路由、响应式数据、虚拟DOM等

SSR的缺点

  • SSR 通过需要对服务器进行更多的API调用,在服务端渲染需要消耗更多的服务器资源,成本较高
  • 100个人访问这个网站,服务器就要为每个人渲染100次
  • 增加了一定的开发成本,开发者需要关心哪些代码是运行在服务端的,哪些则是运行在浏览器的
  • SSR配置站点的缓存通常会比SPA站点要更复杂

SSR 解决方案

  • 传统方案:PHP JSP ...
  • 从0开始,搭建SSR项目:Node+Webpack+Vue/React
  • 选用现成的框架
    • React Next.js
    • Vue3 + Nuxt3 || Vue2 + Nuxt.js

静态网站生成 SSG

静态网页生成 SSG(Static Site Generate)

  • SSG应用在构建阶段就确定了.html页面的内容
  • 用户访问网站,服务器直接返回.html文件给客户端,相当于一个静态资源
  • 优点
    • 直接返回静态的html文件,有利于SEO
    • 相比于SSR,不需要每次请求都由服务端处理,所以可以大幅减轻服务端压力,也可以将文件放到CDN上优化访问速度
    • 保留了SPA应用的特性,比如前端路由、响应式数据、虚拟DOM等
  • 缺点
    • 如果网站的内容需要更新,那么需要重新构建与部署
    • 只能生成偏静态的页面,不利于与用户的交互,所有用户获取到的页面都是相同的
  • 哪些应用场景:文档站、个人博客、新闻站点等

SSR与SSG的优势

  • 更短的首屏时间
    • 只需要请求一个HTML文件就能展示出页面
    • 虽然在服务端仍然需要调取相关接口,但是服务器-服务器之间的通信远比客户端快,有时甚至是同一台服务器的本地接口
    • 不再需要大量的js文件请求,这就使得SSR/SSG可以拥有更短的首屏时间

跨请求状态污染

  • 在SPA中,整个生命周期只有一个App对象实例/一个Router对象实例/一个Store对象实例

    • 因为每个用户使用SPA时,各自的渲染进程都是在自己的浏览器上独立进行的,这是一种单例模式
  • 然而在SSR的环境下,App模块通常只在

    服务器启动时

    初始化一次,同一个应用模块会在多个服务器请求之间被复用

    • 单例状态对象也会在多个请求之间被复用:
    • 某个用户对共享的单例状态进行修改,那么这个状态可能会意外地泄露给另一位正在请求的用户
    • 我们将这种情况称为:跨请求状态污染
  • 为了避免这种跨请求状态污染,SSR的解决方案是:

    • 在每个请求中,为整个应用创建一个全新的实例,包括后面的Router和Store等实例
    • 带来的缺点就是:需要消耗更多的服务器资源

Nuxt3 系统学习

  • 邂逅Nuxt3
  • Nuxt3 初体验
  • Nuxt3 全局配置
  • Nuxt3 内置组件
  • Nuxt3 样式和资源
  • Nuxt3 页面和导航
  • Nuxt3 动态路由

认识Nuxt3

  • 支持数据双向绑定和组件化:Vue.js
  • 处理客户端导航:Vue Router
  • 支持开发中热模块替换、生产环境代码打包:Webpack5 Vite
  • 兼容旧版浏览器,支持ES6+语法转译:ESBuild
  • 支持开发环境服务器,支持服务端渲染/API接口转发
  • 使用h3实现部署可移植性h3是一个极小的高性能http框架)
    • 如:支持在Serverless、Worker、Node.js环境中运行

搭建Nuxt3环境

使用命令行工具 Nuxi 初始化Nuxt:

  • npx nuxi init project-name
  • pnpm dlx nuxi init project-name
  • npm install -g nuxi && nuxi init project-name

解读脚手架创建的初始化项目的package.json中的脚本:

json
{
+import{_ as l,o as e,c as o,R as a}from"./chunks/framework.a304f0f7.js";const D=JSON.parse('{"title":"服务端渲染(SSR)","description":"","frontmatter":{},"headers":[],"relativePath":"note/SSR.md","lastUpdated":1682586876000}'),i={name:"note/SSR.md"},t=a(`

服务端渲染(SSR)

  • 邂逅SPA和SSR
  • Node服务搭建
  • Vue3 + SSR搭建
  • SSR + Hydration 水合
  • Vue SSR + Router
  • Vue SSR + Pinia

邂逅SPA和SSR

我们使用Vue开发的网页一般都是单页面应用程序(SPA Single Page Application)

  • SPA应用是在客户端呈现的,我们称这个渲染过程为CSR(Client Side Rendering)
  • 常见的B端Web应用开发模式,渲染工作在客户端进行,服务器压力较轻,服务器直接返回未经加工的.html文件
  • 所需要的资源(HTML CSS JS等),在一次请求中就加载完成,首屏时间更长,需要额外的首屏优化

与之相对的,就是服务端渲染(SSR Server Side Rendering)

  • SSR并不是什么新鲜的概念,早期的JSP或PHP就已经体现了服务端渲染的原理
  • 但是传统开发模式,代码耦合度较高,不容易维护

于是,同构SSR应运而生,我们称之为BFF(Backend for Frontend 服务于前端的后端)

  • 前后端一体化,一套Vue / React代码在服务器上运行一遍,在到达浏览器又运行一遍。
  • 前后端都参与到渲染中,并且首次渲染出的HTML相同

浏览器请求.html文件 => 服务端运行Vue / React代码并生成.html => 发送.html文件给浏览器 => 浏览器显示网页内容

=> 浏览器加载JS文件 => 绑定DOM事件 客户端渲染接管界面 => 再次跳转路由就是客户端渲染 无需请求后台

SPA的优点与缺点

  • SPA的优点
    • 只需要加载一次 更好的用户体验
      • 只有一个.html文件,页面切换不需要重新加载,所以比传统Web应用程序更快
    • 轻松构建功能丰富的Web应用程序
  • SPA的缺点
    • SPA应用默认只返回一个空HTML文件,不利于SEO
    • 首屏加载资源过大,影响首屏渲染速度
    • 不利于复杂项目构建

SEO优化

  • 语义性HTML标记
    • 标题用 h1,一个页面只应当由一个 h1 标签,副标题用 h2 - h6
    • 不要过度使用 h 标签,多次使用不会增加SEO
    • 段落用 p 标签 列表用 ul 标签,且 li 只放在 ul
  • 每个页面都需要包含标题+内部链接
    • 每个页面对应的title,同一个网站所有页面都有内链可以指向首页
  • 保证链接可供抓取
    • <a href="https://www.example.com" />
    • <a href="/relative/path/file" />
  • meta标签优化:设置description和keywords等
  • 文本标记和img
    • 比如<b><strong>加粗文本的标签,爬虫会关注到该内容
    • img标签添加alt属性,图片加载失败时供爬虫读取alt内容
  • robots.txt 文件,规定爬虫可以访问网址上的哪些页面
  • sitemap.xml 站点地图,在站点地图列出所有网页,确保爬虫不会漏掉某些网页

服务端渲染 SSR

服务端渲染 SSR (Server Side Rendering)

  • 页面是在服务端渲染的,用户每请求一个SSR页面,都会先在服务端渲染
  • 服务端渲染完成后,返回给浏览器呈现,浏览器发现JS脚本,解析脚本,向服务器发起请求,之后网页就可以交互了
    • app = createSSRApp(App) renderToString(app) => App String Html
    • client_bundle.js 客户端通过脚本激活应用程序 让应用程序可以进行交互,这个过程叫水合( Hydration)
  • Vue Nuxt / React Next.js,SSR应用也称为同构应用(server_bundle.js & client_bundle.js)

SSR的优点

  • 更快的首屏渲染速度
    • 浏览器显示静态页面的内容要比JavaScript动态生成的内容快得多
    • 用户访问首页时立刻返回静态页面内容,而不需要等待浏览器先加载完整个SPA应用的JS代码
  • 更好的SEO
    • 爬虫擅长爬取HTML页面,服务端直接返回一个静态的HTML给浏览器
    • 有利于爬虫快速爬取网页内容,并编入索引,有利于SEO
  • SSR 应用程序在 Hydration 之后仍然可以保留Web应用程序的可交互性
    • 如:前端路由、响应式数据、虚拟DOM等

SSR的缺点

  • SSR 通过需要对服务器进行更多的API调用,在服务端渲染需要消耗更多的服务器资源,成本较高
  • 100个人访问这个网站,服务器就要为每个人渲染100次
  • 增加了一定的开发成本,开发者需要关心哪些代码是运行在服务端的,哪些则是运行在浏览器的
  • SSR配置站点的缓存通常会比SPA站点要更复杂

SSR 解决方案

  • 传统方案:PHP JSP ...
  • 从0开始,搭建SSR项目:Node+Webpack+Vue/React
  • 选用现成的框架
    • React Next.js
    • Vue3 + Nuxt3 || Vue2 + Nuxt.js

静态网站生成 SSG

静态网页生成 SSG(Static Site Generate)

  • SSG应用在构建阶段就确定了.html页面的内容
  • 用户访问网站,服务器直接返回.html文件给客户端,相当于一个静态资源
  • 优点
    • 直接返回静态的html文件,有利于SEO
    • 相比于SSR,不需要每次请求都由服务端处理,所以可以大幅减轻服务端压力,也可以将文件放到CDN上优化访问速度
    • 保留了SPA应用的特性,比如前端路由、响应式数据、虚拟DOM等
  • 缺点
    • 如果网站的内容需要更新,那么需要重新构建与部署
    • 只能生成偏静态的页面,不利于与用户的交互,所有用户获取到的页面都是相同的
  • 哪些应用场景:文档站、个人博客、新闻站点等

SSR与SSG的优势

  • 更短的首屏时间
    • 只需要请求一个HTML文件就能展示出页面
    • 虽然在服务端仍然需要调取相关接口,但是服务器-服务器之间的通信远比客户端快,有时甚至是同一台服务器的本地接口
    • 不再需要大量的js文件请求,这就使得SSR/SSG可以拥有更短的首屏时间

跨请求状态污染

  • 在SPA中,整个生命周期只有一个App对象实例/一个Router对象实例/一个Store对象实例

    • 因为每个用户使用SPA时,各自的渲染进程都是在自己的浏览器上独立进行的,这是一种单例模式
  • 然而在SSR的环境下,App模块通常只在

    服务器启动时

    初始化一次,同一个应用模块会在多个服务器请求之间被复用

    • 单例状态对象也会在多个请求之间被复用:
    • 某个用户对共享的单例状态进行修改,那么这个状态可能会意外地泄露给另一位正在请求的用户
    • 我们将这种情况称为:跨请求状态污染
  • 为了避免这种跨请求状态污染,SSR的解决方案是:

    • 在每个请求中,为整个应用创建一个全新的实例,包括后面的Router和Store等实例
    • 带来的缺点就是:需要消耗更多的服务器资源

Nuxt3 系统学习

  • 邂逅Nuxt3
  • Nuxt3 初体验
  • Nuxt3 全局配置
  • Nuxt3 内置组件
  • Nuxt3 样式和资源
  • Nuxt3 页面和导航
  • Nuxt3 动态路由

认识Nuxt3

  • 支持数据双向绑定和组件化:Vue.js
  • 处理客户端导航:Vue Router
  • 支持开发中热模块替换、生产环境代码打包:Webpack5 Vite
  • 兼容旧版浏览器,支持ES6+语法转译:ESBuild
  • 支持开发环境服务器,支持服务端渲染/API接口转发
  • 使用h3实现部署可移植性h3是一个极小的高性能http框架)
    • 如:支持在Serverless、Worker、Node.js环境中运行

搭建Nuxt3环境

使用命令行工具 Nuxi 初始化Nuxt:

  • npx nuxi init project-name
  • pnpm dlx nuxi init project-name
  • npm install -g nuxi && nuxi init project-name

解读脚手架创建的初始化项目的package.json中的脚本:

json
{
   "private": true,
   "scripts": {
     "build": "nuxt build",
diff --git a/assets/note_SSR.md.9e639932.lean.js b/assets/note_SSR.md.f248ec9c.lean.js
similarity index 83%
rename from assets/note_SSR.md.9e639932.lean.js
rename to assets/note_SSR.md.f248ec9c.lean.js
index 68f589b4..d7f9b3e6 100644
--- a/assets/note_SSR.md.9e639932.lean.js
+++ b/assets/note_SSR.md.f248ec9c.lean.js
@@ -1 +1 @@
-import{_ as l,o as e,c as o,R as a}from"./chunks/framework.a304f0f7.js";const D=JSON.parse('{"title":"服务端渲染(SSR)","description":"","frontmatter":{},"headers":[],"relativePath":"note/SSR.md","lastUpdated":1682528161000}'),i={name:"note/SSR.md"},t=a("",80),s=[t];function n(c,r,d,p,u,h){return e(),o("div",null,s)}const g=l(i,[["render",n]]);export{D as __pageData,g as default};
+import{_ as l,o as e,c as o,R as a}from"./chunks/framework.a304f0f7.js";const D=JSON.parse('{"title":"服务端渲染(SSR)","description":"","frontmatter":{},"headers":[],"relativePath":"note/SSR.md","lastUpdated":1682586876000}'),i={name:"note/SSR.md"},t=a("",80),s=[t];function n(c,r,d,p,u,h){return e(),o("div",null,s)}const g=l(i,[["render",n]]);export{D as __pageData,g as default};
diff --git a/assets/project_ClipboardManager_guide_index.md.89147de8.js b/assets/project_ClipboardManager_guide_index.md.1455cfc8.js
similarity index 99%
rename from assets/project_ClipboardManager_guide_index.md.89147de8.js
rename to assets/project_ClipboardManager_guide_index.md.1455cfc8.js
index 7cb4ccb1..0974bd0d 100644
--- a/assets/project_ClipboardManager_guide_index.md.89147de8.js
+++ b/assets/project_ClipboardManager_guide_index.md.1455cfc8.js
@@ -1,4 +1,4 @@
-import{_ as l,D as p,o as t,c,z as s,a,G as e,R as n}from"./chunks/framework.a304f0f7.js";const r="/assets/gi3.c83cee76.png",i="/assets/gi3-2.8cc1f8a9.png",d="/assets/gi3-3.47dffc9b.png",D="/assets/sign-mac.2c3e272f.png",u="/assets/sign-mac-2.5861d44c.png",F="/assets/mac-chmod.bf888724.jpg",y="/assets/guide-jianguo-1.a7e6fa82.png",h="/assets/guide-jianguo-2.14924b1b.png",m="/assets/gi4.e4051a3e.png",b="/assets/gi5.f517b425.png",O=JSON.parse('{"title":"使用指南","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/guide/index.md","lastUpdated":1682528161000}'),_={name:"project/ClipboardManager/guide/index.md"},C=s("h1",{id:"使用指南",tabindex:"-1"},[a("使用指南 "),s("a",{class:"header-anchor",href:"#使用指南","aria-label":'Permalink to "使用指南"'},"​")],-1),g=s("h2",{id:"快捷键一览",tabindex:"-1"},[a("快捷键一览 "),s("a",{class:"header-anchor",href:"#快捷键一览","aria-label":'Permalink to "快捷键一览"'},"​")],-1),A=n("
  • Shift进入多选模式 按下空格连续向下选择 支持跨标签合并复制/粘贴
  • 鼠标左键复制并粘贴 鼠标右键仅复制
  • 选中历史记录 按下直接粘贴 Ctrl+C仅复制
  • 切换分类 Tab键连续切换分类
  • Ctrl+数字键快速粘贴 Alt+数字键快速复制
  • 输入任意字母或数字/Ctrl+F聚焦搜索框并输入 支持使用空格同时检索多个关键词
  • ",6),q=s("code",null,"Ctrl/Command+鼠标左键",-1),f=n('

    如何手动安装clipboard-event-handler

    新版 超级剪贴板 对剪贴板内容更新事件的监听,依赖于可执行文件:

    • Windows系统: clipboard-event-handler-win32.exe
    • Linux系统: clipboard-event-handler-linux
    • MacOS系统: clipboard-event-handler-mac

    插件每次启动时,将自动检查剪贴板数据文件所在目录下是否存在剪贴板监听程序,如存在,则使用性能更优秀的新的监听策略,如不存在,则仍然使用旧的策略。


    1. 下载监听程序

    点击此处(百度网盘)或访问node-clipboard-event手动下载对应系统的文件,并将其移动到剪贴板数据文件所在目录

    插件使用的二进制文件拷贝自node-clipboard-event,请避免从其它不可信的来源下载文件,并在下载文件后比较哈希,有能力的也可以从仓库源代码自行编译

    2. 找到剪贴板数据文件所在目录

    进入设置页(顶部导航栏/齿轮图标),点击数据库路径这一项右侧的打开按钮

    3. 将监听程序拷贝到目录中

    Windows:

    Linux:

    Mac:

    4. 【重要】注意事项

    MacOS需要特殊配置

    针对MacOS系统,在将剪贴板监听程序拷贝到指定目录后,请在重启插件后,进入设置页,检查监听程序是否生效

    如果监听程序未生效,请尝试以下操作,对监听程序进行签名与授权:

    1. 签名

    打开终端输入以下命令

    sh
    sudo codesign --force --deep --sign -

    然后将此目录中的clipboard-event-handler-mac文件拖入终端执行命令

    2. 授权

    左上角🍎 -> 系统偏好设置 -> 安全性与隐私 -> 通用 -> 点击允许

    监听程序不生效如何排查?

    请务必完整按照教程完成安装,如果程序仍未生效,请按照如下方式排查:

    • 监听程序拷贝完成后,需要重启插件方可生效
    • 设置页剪贴板监听程序状态选项是否为已安装
    • 系统进程管理器中是否存在clipboard-event-handler-xxxxx的进程

    使用中遇到任何问题,请尝试通过论坛或加入QQ群反馈

    如何实现多端同步

    ',35),x={id:"webdav同步",tabindex:"-1"},v=s("a",{class:"header-anchor",href:"#webdav同步","aria-label":'Permalink to "WebDav同步 "'},"​",-1),k=s("div",{class:"tip custom-block"},[s("p",{class:"custom-block-title"},"TIP"),s("p",null,[a("自"),s("code",null,"v2.0.0"),a("起,可以通过开通插件会员启用"),s("strong",null,"WebDav同步功能")])],-1),E=s("p",null,[a("👉👉👉"),s("a",{href:"./../vip/#webdav同步功能"},"插件会员/WebDav同步功能"),a("👈👈👈")],-1),P={id:"坚果云同步文件夹",tabindex:"-1"},w=s("a",{class:"header-anchor",href:"#坚果云同步文件夹","aria-label":'Permalink to "坚果云同步文件夹 "'},"​",-1),S=n('

    WARNING

    以下方法仅适用于v2.0.0之前的版本

    数据库文件默认是直接存放在用户文件夹根目录下的,如果需要使用同步功能,请使用插件内提供的数据库路径自定义功能,将数据库路径改为其他路径,而后才能通过下文中讲述的同步文件夹实现云同步。

    坚果云官网安装好软件后,找到_utools_clipboard_manager_storage文件所在的目录

    右键目录,坚果云/同步该文件夹,将此文件夹加入到坚果云的同步服务中

    这样,每次剪贴板内容更新都将自动触发坚果云的同步服务,将剪贴板数据同步到云端

    其他安装了坚果云的设备也将自动同步更新

    如何迁移数据

    剪贴板数据默认存放在

    • Windows Linux用户:{home}\\_utools_clipboard_manager_storage
    • Mac用户:{userData}\\_utools_clipboard_manager_storage

    也可以在设置页中点击按钮打开数据库路径。要手动迁移数据,只需要在新设备上运行一次插件,而后将原设备上的数据文件拷贝并替换新设备中的数据文件即可

    如何创造自己的功能按钮

    v1.4.0起,插件为用户提供了自定义功能按钮的能力

    这让超级剪贴板真正变得“超级”起来,用户可以通过编写json实现携带数据跳转到任何其他插件,这项功能给超级剪贴板带来了无限可能。

    插件中,默认内置了若干使用样例:

    • 讯飞OCR识别
    • 百度搜索
    • 百度识图
    • 统计文本字数
    • 颜色管理
    • 识别图片中二维码
    • 上传到图床
    • 翻译

    下面我将从这些样例出发对这项功能做简单介绍:

    这项功能的原理是utools.redirect(),在不分离插件的情况下,在不同插件之间的跳转体验是连贯的。

    百度搜索为例,是通过网页快开提供的关键词实现的,我们可以编写以下json:

    json
    {
    +import{_ as l,D as p,o as t,c,z as s,a,G as e,R as n}from"./chunks/framework.a304f0f7.js";const r="/assets/gi3.c83cee76.png",i="/assets/gi3-2.8cc1f8a9.png",d="/assets/gi3-3.47dffc9b.png",D="/assets/sign-mac.2c3e272f.png",u="/assets/sign-mac-2.5861d44c.png",F="/assets/mac-chmod.bf888724.jpg",y="/assets/guide-jianguo-1.a7e6fa82.png",h="/assets/guide-jianguo-2.14924b1b.png",m="/assets/gi4.e4051a3e.png",b="/assets/gi5.f517b425.png",O=JSON.parse('{"title":"使用指南","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/guide/index.md","lastUpdated":1682586876000}'),_={name:"project/ClipboardManager/guide/index.md"},C=s("h1",{id:"使用指南",tabindex:"-1"},[a("使用指南 "),s("a",{class:"header-anchor",href:"#使用指南","aria-label":'Permalink to "使用指南"'},"​")],-1),g=s("h2",{id:"快捷键一览",tabindex:"-1"},[a("快捷键一览 "),s("a",{class:"header-anchor",href:"#快捷键一览","aria-label":'Permalink to "快捷键一览"'},"​")],-1),A=n("
  • Shift进入多选模式 按下空格连续向下选择 支持跨标签合并复制/粘贴
  • 鼠标左键复制并粘贴 鼠标右键仅复制
  • 选中历史记录 按下直接粘贴 Ctrl+C仅复制
  • 切换分类 Tab键连续切换分类
  • Ctrl+数字键快速粘贴 Alt+数字键快速复制
  • 输入任意字母或数字/Ctrl+F聚焦搜索框并输入 支持使用空格同时检索多个关键词
  • ",6),q=s("code",null,"Ctrl/Command+鼠标左键",-1),f=n('

    如何手动安装clipboard-event-handler

    新版 超级剪贴板 对剪贴板内容更新事件的监听,依赖于可执行文件:

    • Windows系统: clipboard-event-handler-win32.exe
    • Linux系统: clipboard-event-handler-linux
    • MacOS系统: clipboard-event-handler-mac

    插件每次启动时,将自动检查剪贴板数据文件所在目录下是否存在剪贴板监听程序,如存在,则使用性能更优秀的新的监听策略,如不存在,则仍然使用旧的策略。


    1. 下载监听程序

    点击此处(百度网盘)或访问node-clipboard-event手动下载对应系统的文件,并将其移动到剪贴板数据文件所在目录

    插件使用的二进制文件拷贝自node-clipboard-event,请避免从其它不可信的来源下载文件,并在下载文件后比较哈希,有能力的也可以从仓库源代码自行编译

    2. 找到剪贴板数据文件所在目录

    进入设置页(顶部导航栏/齿轮图标),点击数据库路径这一项右侧的打开按钮

    3. 将监听程序拷贝到目录中

    Windows:

    Linux:

    Mac:

    4. 【重要】注意事项

    MacOS需要特殊配置

    针对MacOS系统,在将剪贴板监听程序拷贝到指定目录后,请在重启插件后,进入设置页,检查监听程序是否生效

    如果监听程序未生效,请尝试以下操作,对监听程序进行签名与授权:

    1. 签名

    打开终端输入以下命令

    sh
    sudo codesign --force --deep --sign -

    然后将此目录中的clipboard-event-handler-mac文件拖入终端执行命令

    2. 授权

    左上角🍎 -> 系统偏好设置 -> 安全性与隐私 -> 通用 -> 点击允许

    监听程序不生效如何排查?

    请务必完整按照教程完成安装,如果程序仍未生效,请按照如下方式排查:

    • 监听程序拷贝完成后,需要重启插件方可生效
    • 设置页剪贴板监听程序状态选项是否为已安装
    • 系统进程管理器中是否存在clipboard-event-handler-xxxxx的进程

    使用中遇到任何问题,请尝试通过论坛或加入QQ群反馈

    如何实现多端同步

    ',35),x={id:"webdav同步",tabindex:"-1"},v=s("a",{class:"header-anchor",href:"#webdav同步","aria-label":'Permalink to "WebDav同步 "'},"​",-1),k=s("div",{class:"tip custom-block"},[s("p",{class:"custom-block-title"},"TIP"),s("p",null,[a("自"),s("code",null,"v2.0.0"),a("起,可以通过开通插件会员启用"),s("strong",null,"WebDav同步功能")])],-1),E=s("p",null,[a("👉👉👉"),s("a",{href:"./../vip/#webdav同步功能"},"插件会员/WebDav同步功能"),a("👈👈👈")],-1),P={id:"坚果云同步文件夹",tabindex:"-1"},w=s("a",{class:"header-anchor",href:"#坚果云同步文件夹","aria-label":'Permalink to "坚果云同步文件夹 "'},"​",-1),S=n('

    WARNING

    以下方法仅适用于v2.0.0之前的版本

    数据库文件默认是直接存放在用户文件夹根目录下的,如果需要使用同步功能,请使用插件内提供的数据库路径自定义功能,将数据库路径改为其他路径,而后才能通过下文中讲述的同步文件夹实现云同步。

    坚果云官网安装好软件后,找到_utools_clipboard_manager_storage文件所在的目录

    右键目录,坚果云/同步该文件夹,将此文件夹加入到坚果云的同步服务中

    这样,每次剪贴板内容更新都将自动触发坚果云的同步服务,将剪贴板数据同步到云端

    其他安装了坚果云的设备也将自动同步更新

    如何迁移数据

    剪贴板数据默认存放在

    • Windows Linux用户:{home}\\_utools_clipboard_manager_storage
    • Mac用户:{userData}\\_utools_clipboard_manager_storage

    也可以在设置页中点击按钮打开数据库路径。要手动迁移数据,只需要在新设备上运行一次插件,而后将原设备上的数据文件拷贝并替换新设备中的数据文件即可

    如何创造自己的功能按钮

    v1.4.0起,插件为用户提供了自定义功能按钮的能力

    这让超级剪贴板真正变得“超级”起来,用户可以通过编写json实现携带数据跳转到任何其他插件,这项功能给超级剪贴板带来了无限可能。

    插件中,默认内置了若干使用样例:

    • 讯飞OCR识别
    • 百度搜索
    • 百度识图
    • 统计文本字数
    • 颜色管理
    • 识别图片中二维码
    • 上传到图床
    • 翻译

    下面我将从这些样例出发对这项功能做简单介绍:

    这项功能的原理是utools.redirect(),在不分离插件的情况下,在不同插件之间的跳转体验是连贯的。

    百度搜索为例,是通过网页快开提供的关键词实现的,我们可以编写以下json:

    json
    {
       "id": "custom.1663490859",
       "title": "百度搜索",
       "icon": "🔍",
    diff --git a/assets/project_ClipboardManager_guide_index.md.89147de8.lean.js b/assets/project_ClipboardManager_guide_index.md.1455cfc8.lean.js
    similarity index 97%
    rename from assets/project_ClipboardManager_guide_index.md.89147de8.lean.js
    rename to assets/project_ClipboardManager_guide_index.md.1455cfc8.lean.js
    index baaaeaf9..c8e616cd 100644
    --- a/assets/project_ClipboardManager_guide_index.md.89147de8.lean.js
    +++ b/assets/project_ClipboardManager_guide_index.md.1455cfc8.lean.js
    @@ -1 +1 @@
    -import{_ as l,D as p,o as t,c,z as s,a,G as e,R as n}from"./chunks/framework.a304f0f7.js";const r="/assets/gi3.c83cee76.png",i="/assets/gi3-2.8cc1f8a9.png",d="/assets/gi3-3.47dffc9b.png",D="/assets/sign-mac.2c3e272f.png",u="/assets/sign-mac-2.5861d44c.png",F="/assets/mac-chmod.bf888724.jpg",y="/assets/guide-jianguo-1.a7e6fa82.png",h="/assets/guide-jianguo-2.14924b1b.png",m="/assets/gi4.e4051a3e.png",b="/assets/gi5.f517b425.png",O=JSON.parse('{"title":"使用指南","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/guide/index.md","lastUpdated":1682528161000}'),_={name:"project/ClipboardManager/guide/index.md"},C=s("h1",{id:"使用指南",tabindex:"-1"},[a("使用指南 "),s("a",{class:"header-anchor",href:"#使用指南","aria-label":'Permalink to "使用指南"'},"​")],-1),g=s("h2",{id:"快捷键一览",tabindex:"-1"},[a("快捷键一览 "),s("a",{class:"header-anchor",href:"#快捷键一览","aria-label":'Permalink to "快捷键一览"'},"​")],-1),A=n("",6),q=s("code",null,"Ctrl/Command+鼠标左键",-1),f=n("",35),x={id:"webdav同步",tabindex:"-1"},v=s("a",{class:"header-anchor",href:"#webdav同步","aria-label":'Permalink to "WebDav同步 "'},"​",-1),k=s("div",{class:"tip custom-block"},[s("p",{class:"custom-block-title"},"TIP"),s("p",null,[a("自"),s("code",null,"v2.0.0"),a("起,可以通过开通插件会员启用"),s("strong",null,"WebDav同步功能")])],-1),E=s("p",null,[a("👉👉👉"),s("a",{href:"./../vip/#webdav同步功能"},"插件会员/WebDav同步功能"),a("👈👈👈")],-1),P={id:"坚果云同步文件夹",tabindex:"-1"},w=s("a",{class:"header-anchor",href:"#坚果云同步文件夹","aria-label":'Permalink to "坚果云同步文件夹 "'},"​",-1),S=n("",28);function T(j,B,N,V,W,I){const o=p("Badge");return t(),c("div",null,[C,g,s("ul",null,[A,s("li",null,[q,a("点击文本/图片/文件 直接进入预览/打开所在文件夹 "),e(o,{type:"warning",text:"插件会员"})])]),f,s("h3",x,[a("WebDav同步 "),e(o,{type:"tip",text:"^2.0.0"}),a(),v]),k,E,s("h3",P,[a("坚果云同步文件夹 "),e(o,{type:"warning",text:"仅旧版本"}),a(),w]),S])}const R=l(_,[["render",T]]);export{O as __pageData,R as default};
    +import{_ as l,D as p,o as t,c,z as s,a,G as e,R as n}from"./chunks/framework.a304f0f7.js";const r="/assets/gi3.c83cee76.png",i="/assets/gi3-2.8cc1f8a9.png",d="/assets/gi3-3.47dffc9b.png",D="/assets/sign-mac.2c3e272f.png",u="/assets/sign-mac-2.5861d44c.png",F="/assets/mac-chmod.bf888724.jpg",y="/assets/guide-jianguo-1.a7e6fa82.png",h="/assets/guide-jianguo-2.14924b1b.png",m="/assets/gi4.e4051a3e.png",b="/assets/gi5.f517b425.png",O=JSON.parse('{"title":"使用指南","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/guide/index.md","lastUpdated":1682586876000}'),_={name:"project/ClipboardManager/guide/index.md"},C=s("h1",{id:"使用指南",tabindex:"-1"},[a("使用指南 "),s("a",{class:"header-anchor",href:"#使用指南","aria-label":'Permalink to "使用指南"'},"​")],-1),g=s("h2",{id:"快捷键一览",tabindex:"-1"},[a("快捷键一览 "),s("a",{class:"header-anchor",href:"#快捷键一览","aria-label":'Permalink to "快捷键一览"'},"​")],-1),A=n("",6),q=s("code",null,"Ctrl/Command+鼠标左键",-1),f=n("",35),x={id:"webdav同步",tabindex:"-1"},v=s("a",{class:"header-anchor",href:"#webdav同步","aria-label":'Permalink to "WebDav同步 "'},"​",-1),k=s("div",{class:"tip custom-block"},[s("p",{class:"custom-block-title"},"TIP"),s("p",null,[a("自"),s("code",null,"v2.0.0"),a("起,可以通过开通插件会员启用"),s("strong",null,"WebDav同步功能")])],-1),E=s("p",null,[a("👉👉👉"),s("a",{href:"./../vip/#webdav同步功能"},"插件会员/WebDav同步功能"),a("👈👈👈")],-1),P={id:"坚果云同步文件夹",tabindex:"-1"},w=s("a",{class:"header-anchor",href:"#坚果云同步文件夹","aria-label":'Permalink to "坚果云同步文件夹 "'},"​",-1),S=n("",28);function T(j,B,N,V,W,I){const o=p("Badge");return t(),c("div",null,[C,g,s("ul",null,[A,s("li",null,[q,a("点击文本/图片/文件 直接进入预览/打开所在文件夹 "),e(o,{type:"warning",text:"插件会员"})])]),f,s("h3",x,[a("WebDav同步 "),e(o,{type:"tip",text:"^2.0.0"}),a(),v]),k,E,s("h3",P,[a("坚果云同步文件夹 "),e(o,{type:"warning",text:"仅旧版本"}),a(),w]),S])}const R=l(_,[["render",T]]);export{O as __pageData,R as default};
    diff --git a/assets/project_ClipboardManager_index.md.2a3cb14f.js b/assets/project_ClipboardManager_index.md.e0db2213.js
    similarity index 96%
    rename from assets/project_ClipboardManager_index.md.2a3cb14f.js
    rename to assets/project_ClipboardManager_index.md.e0db2213.js
    index 25db4a0f..646c162d 100644
    --- a/assets/project_ClipboardManager_index.md.2a3cb14f.js
    +++ b/assets/project_ClipboardManager_index.md.e0db2213.js
    @@ -1 +1 @@
    -import{D as e,o as r,c as n,G as o,a4 as i,a5 as d,z as l,R as g}from"./chunks/framework.a304f0f7.js";const m="/assets/logo.98e788ed.png",p="/assets/img1.ed7d5c23.png",_="/assets/img2.79214eff.png",u="/assets/img3.e381051c.png",h="/assets/img4.3572b25f.png",f="/assets/img5.f7491650.png",b="/assets/img6.083aa794.png",q="/assets/img7.dd9f1b9f.png",T=l("br",null,null,-1),k=g('

    🔰 开始使用

    首次安装需要设置“跟随主程序同时启动”

    • ✅ 监听剪贴板并持续将新内容更新到本地磁盘 数据读写完全本地化
    • ✅ 快速收藏/转存/分词/复制/删除/打开文件&目标文件夹
    • ✅ 功能按钮 定义无限可能 OCR识别 百度搜索 百度识图 统计文本字数 颜色管理 识别图片中二维码 上传到图床 翻译
    • 鼠标左键 复制并粘贴 鼠标右键 仅复制
    • ✅ 按下Shift空格进入多选模式 连续选择多条内容合并复制 支持跨标签合并复制/粘贴
    • ✅ 键盘 选中历史记录,按下回车直接粘贴
    • ✅ 键盘 切换分类 Tab键连续切换分类
    • ✅ 使用 Ctrl/Alt+数字键 快速粘贴
    • ✅ 插件内输入任意字母或数字自动聚焦搜索框 支持使用空格同时检索多个关键词
    • 超级Markdown合并剪贴板内容导出图文 超级粘贴 直接转存为文件 智慧分词 快速拖选指定内容
    • ✅ 优雅的界面动效与交互 跟随系统的深色模式
    • ✅ 优秀的剪贴板监听性能 强大的自定义功能按钮 自搭建多端同步 ···

    📚 安装方式

    催更群 769115389 Github

    ',6),x=JSON.parse('{"title":"超级剪贴板","description":"","frontmatter":{"title":"超级剪贴板","navbar":false},"headers":[],"relativePath":"project/ClipboardManager/index.md","lastUpdated":1682528161000}'),C={name:"project/ClipboardManager/index.md"},D=Object.assign(C,{setup(S){const t={subTitle:"✨ 强大的剪贴板管理工具。",logo:m,linkList:[{content:"⭐ 开源代码",target:"https://github.com/ZiuChen/ClipboardManager"},{content:"🚀 使用指南",target:"./guide/"},{content:"🌎 疑难解答",target:"./statement/"},{content:"👑 插件会员",target:"./vip/"},{content:"🚚 更新日志",target:"./log/"}]},c=[{src:p},{src:_},{src:u},{src:h},{src:f},{src:b},{src:q}];return(P,I)=>{const s=e("Title"),a=e("ImgSlider");return r(),n("div",null,[o(s,i(d(t)),null,16),T,o(a,{imgSliderList:c}),k])}}});export{x as __pageData,D as default}; +import{D as e,o as r,c as n,G as o,a4 as i,a5 as d,z as l,R as g}from"./chunks/framework.a304f0f7.js";const m="/assets/logo.98e788ed.png",p="/assets/img1.ed7d5c23.png",_="/assets/img2.79214eff.png",u="/assets/img3.e381051c.png",h="/assets/img4.3572b25f.png",f="/assets/img5.f7491650.png",b="/assets/img6.083aa794.png",q="/assets/img7.dd9f1b9f.png",T=l("br",null,null,-1),k=g('

    🔰 开始使用

    首次安装需要设置“跟随主程序同时启动”

    • ✅ 监听剪贴板并持续将新内容更新到本地磁盘 数据读写完全本地化
    • ✅ 快速收藏/转存/分词/复制/删除/打开文件&目标文件夹
    • ✅ 功能按钮 定义无限可能 OCR识别 百度搜索 百度识图 统计文本字数 颜色管理 识别图片中二维码 上传到图床 翻译
    • 鼠标左键 复制并粘贴 鼠标右键 仅复制
    • ✅ 按下Shift空格进入多选模式 连续选择多条内容合并复制 支持跨标签合并复制/粘贴
    • ✅ 键盘 选中历史记录,按下回车直接粘贴
    • ✅ 键盘 切换分类 Tab键连续切换分类
    • ✅ 使用 Ctrl/Alt+数字键 快速粘贴
    • ✅ 插件内输入任意字母或数字自动聚焦搜索框 支持使用空格同时检索多个关键词
    • 超级Markdown合并剪贴板内容导出图文 超级粘贴 直接转存为文件 智慧分词 快速拖选指定内容
    • ✅ 优雅的界面动效与交互 跟随系统的深色模式
    • ✅ 优秀的剪贴板监听性能 强大的自定义功能按钮 自搭建多端同步 ···

    📚 安装方式

    催更群 769115389 Github

    ',6),x=JSON.parse('{"title":"超级剪贴板","description":"","frontmatter":{"title":"超级剪贴板","navbar":false},"headers":[],"relativePath":"project/ClipboardManager/index.md","lastUpdated":1682586876000}'),C={name:"project/ClipboardManager/index.md"},D=Object.assign(C,{setup(S){const t={subTitle:"✨ 强大的剪贴板管理工具。",logo:m,linkList:[{content:"⭐ 开源代码",target:"https://github.com/ZiuChen/ClipboardManager"},{content:"🚀 使用指南",target:"./guide/"},{content:"🌎 疑难解答",target:"./statement/"},{content:"👑 插件会员",target:"./vip/"},{content:"🚚 更新日志",target:"./log/"}]},c=[{src:p},{src:_},{src:u},{src:h},{src:f},{src:b},{src:q}];return(P,I)=>{const s=e("Title"),a=e("ImgSlider");return r(),n("div",null,[o(s,i(d(t)),null,16),T,o(a,{imgSliderList:c}),k])}}});export{x as __pageData,D as default}; diff --git a/assets/project_ClipboardManager_index.md.2a3cb14f.lean.js b/assets/project_ClipboardManager_index.md.e0db2213.lean.js similarity index 94% rename from assets/project_ClipboardManager_index.md.2a3cb14f.lean.js rename to assets/project_ClipboardManager_index.md.e0db2213.lean.js index c3293b9d..d963930a 100644 --- a/assets/project_ClipboardManager_index.md.2a3cb14f.lean.js +++ b/assets/project_ClipboardManager_index.md.e0db2213.lean.js @@ -1 +1 @@ -import{D as e,o as r,c as n,G as o,a4 as i,a5 as d,z as l,R as g}from"./chunks/framework.a304f0f7.js";const m="/assets/logo.98e788ed.png",p="/assets/img1.ed7d5c23.png",_="/assets/img2.79214eff.png",u="/assets/img3.e381051c.png",h="/assets/img4.3572b25f.png",f="/assets/img5.f7491650.png",b="/assets/img6.083aa794.png",q="/assets/img7.dd9f1b9f.png",T=l("br",null,null,-1),k=g("",6),x=JSON.parse('{"title":"超级剪贴板","description":"","frontmatter":{"title":"超级剪贴板","navbar":false},"headers":[],"relativePath":"project/ClipboardManager/index.md","lastUpdated":1682528161000}'),C={name:"project/ClipboardManager/index.md"},D=Object.assign(C,{setup(S){const t={subTitle:"✨ 强大的剪贴板管理工具。",logo:m,linkList:[{content:"⭐ 开源代码",target:"https://github.com/ZiuChen/ClipboardManager"},{content:"🚀 使用指南",target:"./guide/"},{content:"🌎 疑难解答",target:"./statement/"},{content:"👑 插件会员",target:"./vip/"},{content:"🚚 更新日志",target:"./log/"}]},c=[{src:p},{src:_},{src:u},{src:h},{src:f},{src:b},{src:q}];return(P,I)=>{const s=e("Title"),a=e("ImgSlider");return r(),n("div",null,[o(s,i(d(t)),null,16),T,o(a,{imgSliderList:c}),k])}}});export{x as __pageData,D as default}; +import{D as e,o as r,c as n,G as o,a4 as i,a5 as d,z as l,R as g}from"./chunks/framework.a304f0f7.js";const m="/assets/logo.98e788ed.png",p="/assets/img1.ed7d5c23.png",_="/assets/img2.79214eff.png",u="/assets/img3.e381051c.png",h="/assets/img4.3572b25f.png",f="/assets/img5.f7491650.png",b="/assets/img6.083aa794.png",q="/assets/img7.dd9f1b9f.png",T=l("br",null,null,-1),k=g("",6),x=JSON.parse('{"title":"超级剪贴板","description":"","frontmatter":{"title":"超级剪贴板","navbar":false},"headers":[],"relativePath":"project/ClipboardManager/index.md","lastUpdated":1682586876000}'),C={name:"project/ClipboardManager/index.md"},D=Object.assign(C,{setup(S){const t={subTitle:"✨ 强大的剪贴板管理工具。",logo:m,linkList:[{content:"⭐ 开源代码",target:"https://github.com/ZiuChen/ClipboardManager"},{content:"🚀 使用指南",target:"./guide/"},{content:"🌎 疑难解答",target:"./statement/"},{content:"👑 插件会员",target:"./vip/"},{content:"🚚 更新日志",target:"./log/"}]},c=[{src:p},{src:_},{src:u},{src:h},{src:f},{src:b},{src:q}];return(P,I)=>{const s=e("Title"),a=e("ImgSlider");return r(),n("div",null,[o(s,i(d(t)),null,16),T,o(a,{imgSliderList:c}),k])}}});export{x as __pageData,D as default}; diff --git a/assets/project_ClipboardManager_log_index.md.714cf9de.js b/assets/project_ClipboardManager_log_index.md.e245d280.js similarity index 99% rename from assets/project_ClipboardManager_log_index.md.714cf9de.js rename to assets/project_ClipboardManager_log_index.md.e245d280.js index 7f7edf61..e922bd4b 100644 --- a/assets/project_ClipboardManager_log_index.md.714cf9de.js +++ b/assets/project_ClipboardManager_log_index.md.e245d280.js @@ -1 +1 @@ -import{_ as l,o as i,c as a,R as e}from"./chunks/framework.a304f0f7.js";const s=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/log/index.md","lastUpdated":1682528161000}'),o={name:"project/ClipboardManager/log/index.md"},t=e('

    更新日志

    v2.1.3

    2023-04-12

    • fix: 修正合并粘贴功能
    • fix: 合并图文导出时过滤掉文件内容
    • fix: 调整使用记录的存储组织方式
    • fix: 调整合并图文导出的插件关键字

    v2.1.2

    2023-04-09

    • feat: 支持多选后批量删除
    • feat: 支持合并图文保存到超级Markdown
    • fix: 优化深色模式下多选条目背景色
    • fix: 修复图片过宽时溢出容器的问题

    v2.1.1

    2023-04-05

    • feat: 支持在设置页一键清理所有图片
    • fix: 修正本地数据版本较新时被云端覆盖的问题

    v2.1.0

    2023-03-29

    • feat: 收藏功能增强 支持为收藏内容添加备注
    • feat: 优化界面动效

    v2.0.11

    2023-03-01

    • fix: 优化导航栏展示/隐藏逻辑
    • fix: 修复某些场景下ESC无法退出插件的问题

    v2.0.10

    2023-02-27

    • fix: 修复轮询监听器执行错误导致插件白屏的问题

    v2.0.9

    2023-02-27

    • feat: 添加键盘操作模式 屏蔽鼠标滑动 高效快捷
    • feat: 添加极简模式 隐藏页面不必要组件
    • feat: 顶部导航栏添加展示/隐藏动效
    • feat: 移除部分动画效果 让动作简洁有力
    • feat: 添加底部横幅提醒监听器状态
    • fix: 优化轮询监听器性能
    • fix: 修复某些情况下导致的白屏问题
    • fix: 优化代码执行细节 提升插件稳定性

    v2.0.8

    2023-02-24

    • feat: 添加插件会员用户彩蛋
    • fix: 修复某些情况导致的插件白屏问题

    v2.0.7

    2023-02-24

    • fix: 修复某些情况下插件主页渲染被阻塞导致的白屏问题
    • fix: 修复初始化时重复执行Webdav上传的问题

    v2.0.6

    2023-02-22

    • fix: 优化列表展示性能
    • fix: 优化本地读写稳定性 修复数据损坏导致的白屏问题
    • fix: 优化代码高亮依赖缓存 避免多次请求下载

    v2.0.5

    2023-02-19

    • fix: 修复插件启动时WebDav上传失败报错的问题
    • fix: 修复插件启动时会员用户登录联网失败的问题

    v2.0.4

    2023-02-13

    • feat: 支持修改代码高亮主题、样式与是否启用
    • feat: 右下角常驻当前选中条目的更新日期
    • feat: 页面滚动条样式优化
    • fix: 修复错误识别文本内容为网址的问题
    • fix: 修复删除条目后插件卡死的问题
    • fix: 修复某些情况下数据不再向下展示的问题

    v2.0.3

    2023-02-10

    • feat: 按住Ctrl/Command点击文本/链接 可以直接预览/跳转
    • feat: 按住Ctrl/Command点击图片 可以直接全局预览
    • feat: 按住Ctrl/Command点击文件 可以打开其所在文件夹
    • feat: 调整预览页展开样式
    • fix: 多选状态无法通过点击鼠标添加选项
    • fix: 某些情况下预览页无法执行Ctrl+C部分复制

    v2.0.2

    2023-02-10

    • fix: 更新到新版后首次进入白屏问题
    • fix: 新设备首次进入插件数据库路径读取问题
    • fix: 优化联网失败的弹窗逻辑
    • fix: 普通用户图标颜色异常

    v2.0.1

    2023-02-10

    • feat: 工具栏按钮顺序调整 会员用户添加金色标识
    • fix: 切换分类时出现明显卡顿
    • fix: 未登录用户进入用户中心白屏
    • fix: 添加网络问题导致登录验证失败的弹窗提醒

    v2.0.0

    2023-02-07

    • feat: 界面焕新 简洁高效
    • feat: 支持WebDav自动同步功能
    • feat: 支持预览页代码高亮
    • feat: 支持自定义分类卡片的顺序
    • feat: 支持识别文本内容为色值、链接等
    • feat: 支持左右键切换导航
    • feat: 支持关闭剪贴板图片记录
    • feat: 支持手动设置偏好主题
    • fix: 清空数据库保留收藏内容
    • fix: 滚动到底部触发懒加载失败
    • refactor: 组件重构 优化大列表运行时性能
    • refactor: 本地读写性能优化

    v1.4.7

    2022-11-03

    • feat: 设置页支持使用ESC返回上一级
    • fix: 移除了启动监听程序时对MacOS的特殊判断
    • fix: 移除了缺少监听程序时的通知报错

    v1.4.6

    2022-11-01

    • feat: 调整设置页图标与界面按钮细节
    • fix: 会员用户由于数据同步导致的插件白屏问题
    • fix: 多选复制时条目排序被颠倒

    v1.4.5

    2022-10-10

    • feat: 调整过期天数列表 最长支持31天
    • feat: 设置页添加handler安装引导
    • fix: 清空数据时内存数据未清空
    • fix: 多端同步时内存数据未更新
    • fix: 开发者模式下listener错误挂载

    v1.4.4

    2022-09-26

    • fix: 修复重复粘贴的问题

    v1.4.3

    2022-09-25

    • feat: 增加空格快捷键 按下连续多选的功能
    • feat: 多选状态下移动鼠标不再激活列表条目
    • feat: 支持展示剪贴板监听程序状态
    • fix: 删除某条历史记录时不再返回顶部
    • fix: 多选图片无法合并复制
    • fix: 内置按钮颜色管理关键字错误
    • fix: 设置页下拉框过长时被卡片遮挡
    • fix: 设置页滚动到底部触发懒加载

    v1.4.2

    2022-09-21

    • chore: 移除了插件内的二进制文件 恢复旧的监听机制 性能问题有待解决

    v1.4.1

    2022-09-21

    • fix: 修复了新的监听机制在 Mac 下无法正确获取剪贴板更新事件的问题

    v1.4.0

    2022-09-20

    插件能够正确在Linux上运行 离不开 小千 不厌其烦的测试与调试 💖

    感谢每一位为超级剪贴板提出过建议、参与测试与调试的用户

    • feat: 支持自定义数据库路径/存储条数/过期时间/展示在主界面的按钮
    • feat: 功能按钮支持自定义 创造属于你自己的功能按钮
    • feat: 添加了9个内置的自定义功能按钮
    • feat: 增加了更优雅的弹窗与提示界面
    • feat: 预览页更宽了 支持预览图片
    • feat: 导航栏图标样式修改
    • fix: 解决了CPU占用高、浏览大图卡顿的性能问题
    • fix: 从后台进入插件自动清空搜索框
    • refactor: 改变了监听剪贴板的方式 性能更优
    • refactor: 引入了ElementPlus组件库与图标库

    v1.3.4

    2022-09-19

    • feat: 移除插件内收藏Tab
    • feat: 点击收藏按钮后将携带数据跳转至备忘快贴
    • refactor: 优化代码执行逻辑 移除冗余代码

    v1.3.3

    2022-09-16

    • feat: 提高主色在深色模式下的对比度 改善长文本表现
    • feat: 添加快存功能按钮 配合超级粘贴插件实现快速转存
    • feat: 支持在预览页展示全部功能按钮
    • feat: 重要版本更新自动展示通知卡片
    • fix: 修正多选快捷键Shift影响检索输入的问题
    • fix: 检索词更新后 未自动激活首条记录

    v1.3.2

    2022-09-15

    • feat: 鼠标悬停展示具体日期、完整数据
    • feat: 调整历史记录上限为800条
    • fix: 修复鼠标置于功能按钮时上下键失效的问题
    • fix: 调整大图片判定尺寸

    v1.3.1

    2022-09-12

    • feat: 支持通过历史记录列表进入分词
    • feat: 减小插件体积 对智慧分词的支持改为插件跳转

    v1.3.0

    2022-09-09

    • feat: 添加多选功能 支持跨标签合并文本/图片/文件
    • feat: 支持通过按下Shift进入多选功能 支持按住Shift快速选择
    • feat: 支持使用Ctrl+CEnter快捷合并复制/粘贴
    • feat: 添加智慧分词功能 可对文本进行分割提取
    • feat: 长文本以蓝色高亮显示 不再提供查看全部按钮
    • feat: 优化界面动效

    v1.2.3

    2022-09-08

    • fix: 修复了检索内容时搜索记录不准确的问题

    v1.2.2

    2022-09-07

    • feat: 搜索框支持使用空格分词以同时检索多个关键词
    • fix: 修复了剪贴板数据记录遗漏的问题
    • fix: 收藏内容不再计入到条数限制中
    • fix: 修复了非预期的搜索框聚焦行为
    • fix: 预览页的按钮改为固定在顶部

    v1.2.1

    2022-09-05

    • feat: 支持通过功能按钮进入任意数据的预览页面
    • feat: 支持通过功能按钮打开文件所在目录
    • feat: 添加取消收藏功能按钮
    • feat: 搜索框支持展开/收起 输入任意内容展开并聚焦搜索
    • feat: 侧栏预览页添加复制全部智慧分词按钮
    • feat: 功能按钮改为使用图标展示
    • feat: 优化了界面样式
    • refactor: 清理定时器 优化插件性能

    v1.2.0

    2022-09-04

    • feat: 添加右侧操作栏 支持复制/收藏/删除操作
    • feat: 添加标签页收藏 支持在此页面管理所有收藏
    • feat: 调整侧栏宽度 调整界面样式细节

    v1.1.7

    2022-08-30

    • fix: 定时器检查剪贴板,修复了不记录剪贴板的问题

    v1.1.6

    2022-08-27

    • feat: 添加Alt+数字键Ctrl+数字键快速选择功能
    • feat: 调整界面样式 调整查看全部位置 移除图片背景色 增加动效
    • feat: 搜索框增加检索条数展示
    • fix: 改善插件读取图片的性能

    v1.1.5

    2022-08-25

    • fix: 提高剪贴板读取频率,避免高频复制时丢数据的情况

    v1.1.4

    2022-08-20

    • feat: 增加清空搜索框的按钮
    • feat: 优化插件内按下ESC的功能: 退出完整预览/清空搜索框
    • feat: 进入插件自动选中框内全部文本
    • fix: Mac在分离窗口状态左键会粘贴到搜索框 #13

    v1.1.3

    2022-08-19

    • feat: 调整界面在深色模式下的表现样式
    • fix: 在侧栏全部数据中使用Ctrl+C复制部分文本失效
    • fix: Mac下使用Ctrl+C复制单条记录失效
    • fix: 通过换行符个数区分超长文本
    • fix: 窗口分离下鼠标单击导致粘贴到搜索框
    • chore: 原插件名剪贴板改为超级剪贴板

    v1.1.2

    2022-08-18

    • feat: 执行复制后只隐藏主界面而不退出插件到后台
    • feat: 搜索时不再区分大小写
    • fix: 文本内容会出现异常首行缩进
    • fix: 图片展示在列表中的时候 右侧多出一个图块
    • fix: 移除鼠标hover时数据底部的色块

    v1.1.1

    2022-08-17

    • fix: 复制超大图片进入插件时崩溃

    v1.1.0

    2022-08-16

    • feat: 支持使用 键切换选中记录
    • feat: 支持使用 Ctrl+C 复制选中记录
    • feat: 支持使用 Enter 复制选中记录并粘贴

    v1.0.4

    2022-08-16

    • feat: 进入插件自动回到顶部、切换至全部分类
    • feat: 监听到键盘事件自动聚焦到搜索框
    • feat: 调整鼠标hover动画与历史记录的active样式
    • fix: 使用Tab切换导航失效
    • fix: 超长图片显示越界

    v1.0.3

    2022-08-16

    • fix: 路径分隔符导致写入错误的数据文件

    v1.0.2

    2022-08-15

    • fix: Mac下由权限导致的无法写入数据

    v1.0.1

    2022-08-15

    • feat: 区分鼠标点击行为:左键复制并粘贴,右键仅复制
    • feat: 适配深色模式
    • feat: 移除了右侧的查看更多按钮
    • feat: 单次展示的条数增加到了15条
    • feat: 增加条数限制500条 增加存储日期限制14天
    • feat: 增加了多平台支持
    • fix: 执行粘贴后主输入框未隐藏
    • refactor: Vue3重构

    v1.0.0

    2022-08-14

    • release: v1.0.0发布
    ',141),d=[t];function r(c,h,f,u,v,n){return i(),a("div",null,d)}const p=l(o,[["render",r]]);export{s as __pageData,p as default}; +import{_ as l,o as i,c as a,R as e}from"./chunks/framework.a304f0f7.js";const s=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/log/index.md","lastUpdated":1682586876000}'),o={name:"project/ClipboardManager/log/index.md"},t=e('

    更新日志

    v2.1.3

    2023-04-12

    • fix: 修正合并粘贴功能
    • fix: 合并图文导出时过滤掉文件内容
    • fix: 调整使用记录的存储组织方式
    • fix: 调整合并图文导出的插件关键字

    v2.1.2

    2023-04-09

    • feat: 支持多选后批量删除
    • feat: 支持合并图文保存到超级Markdown
    • fix: 优化深色模式下多选条目背景色
    • fix: 修复图片过宽时溢出容器的问题

    v2.1.1

    2023-04-05

    • feat: 支持在设置页一键清理所有图片
    • fix: 修正本地数据版本较新时被云端覆盖的问题

    v2.1.0

    2023-03-29

    • feat: 收藏功能增强 支持为收藏内容添加备注
    • feat: 优化界面动效

    v2.0.11

    2023-03-01

    • fix: 优化导航栏展示/隐藏逻辑
    • fix: 修复某些场景下ESC无法退出插件的问题

    v2.0.10

    2023-02-27

    • fix: 修复轮询监听器执行错误导致插件白屏的问题

    v2.0.9

    2023-02-27

    • feat: 添加键盘操作模式 屏蔽鼠标滑动 高效快捷
    • feat: 添加极简模式 隐藏页面不必要组件
    • feat: 顶部导航栏添加展示/隐藏动效
    • feat: 移除部分动画效果 让动作简洁有力
    • feat: 添加底部横幅提醒监听器状态
    • fix: 优化轮询监听器性能
    • fix: 修复某些情况下导致的白屏问题
    • fix: 优化代码执行细节 提升插件稳定性

    v2.0.8

    2023-02-24

    • feat: 添加插件会员用户彩蛋
    • fix: 修复某些情况导致的插件白屏问题

    v2.0.7

    2023-02-24

    • fix: 修复某些情况下插件主页渲染被阻塞导致的白屏问题
    • fix: 修复初始化时重复执行Webdav上传的问题

    v2.0.6

    2023-02-22

    • fix: 优化列表展示性能
    • fix: 优化本地读写稳定性 修复数据损坏导致的白屏问题
    • fix: 优化代码高亮依赖缓存 避免多次请求下载

    v2.0.5

    2023-02-19

    • fix: 修复插件启动时WebDav上传失败报错的问题
    • fix: 修复插件启动时会员用户登录联网失败的问题

    v2.0.4

    2023-02-13

    • feat: 支持修改代码高亮主题、样式与是否启用
    • feat: 右下角常驻当前选中条目的更新日期
    • feat: 页面滚动条样式优化
    • fix: 修复错误识别文本内容为网址的问题
    • fix: 修复删除条目后插件卡死的问题
    • fix: 修复某些情况下数据不再向下展示的问题

    v2.0.3

    2023-02-10

    • feat: 按住Ctrl/Command点击文本/链接 可以直接预览/跳转
    • feat: 按住Ctrl/Command点击图片 可以直接全局预览
    • feat: 按住Ctrl/Command点击文件 可以打开其所在文件夹
    • feat: 调整预览页展开样式
    • fix: 多选状态无法通过点击鼠标添加选项
    • fix: 某些情况下预览页无法执行Ctrl+C部分复制

    v2.0.2

    2023-02-10

    • fix: 更新到新版后首次进入白屏问题
    • fix: 新设备首次进入插件数据库路径读取问题
    • fix: 优化联网失败的弹窗逻辑
    • fix: 普通用户图标颜色异常

    v2.0.1

    2023-02-10

    • feat: 工具栏按钮顺序调整 会员用户添加金色标识
    • fix: 切换分类时出现明显卡顿
    • fix: 未登录用户进入用户中心白屏
    • fix: 添加网络问题导致登录验证失败的弹窗提醒

    v2.0.0

    2023-02-07

    • feat: 界面焕新 简洁高效
    • feat: 支持WebDav自动同步功能
    • feat: 支持预览页代码高亮
    • feat: 支持自定义分类卡片的顺序
    • feat: 支持识别文本内容为色值、链接等
    • feat: 支持左右键切换导航
    • feat: 支持关闭剪贴板图片记录
    • feat: 支持手动设置偏好主题
    • fix: 清空数据库保留收藏内容
    • fix: 滚动到底部触发懒加载失败
    • refactor: 组件重构 优化大列表运行时性能
    • refactor: 本地读写性能优化

    v1.4.7

    2022-11-03

    • feat: 设置页支持使用ESC返回上一级
    • fix: 移除了启动监听程序时对MacOS的特殊判断
    • fix: 移除了缺少监听程序时的通知报错

    v1.4.6

    2022-11-01

    • feat: 调整设置页图标与界面按钮细节
    • fix: 会员用户由于数据同步导致的插件白屏问题
    • fix: 多选复制时条目排序被颠倒

    v1.4.5

    2022-10-10

    • feat: 调整过期天数列表 最长支持31天
    • feat: 设置页添加handler安装引导
    • fix: 清空数据时内存数据未清空
    • fix: 多端同步时内存数据未更新
    • fix: 开发者模式下listener错误挂载

    v1.4.4

    2022-09-26

    • fix: 修复重复粘贴的问题

    v1.4.3

    2022-09-25

    • feat: 增加空格快捷键 按下连续多选的功能
    • feat: 多选状态下移动鼠标不再激活列表条目
    • feat: 支持展示剪贴板监听程序状态
    • fix: 删除某条历史记录时不再返回顶部
    • fix: 多选图片无法合并复制
    • fix: 内置按钮颜色管理关键字错误
    • fix: 设置页下拉框过长时被卡片遮挡
    • fix: 设置页滚动到底部触发懒加载

    v1.4.2

    2022-09-21

    • chore: 移除了插件内的二进制文件 恢复旧的监听机制 性能问题有待解决

    v1.4.1

    2022-09-21

    • fix: 修复了新的监听机制在 Mac 下无法正确获取剪贴板更新事件的问题

    v1.4.0

    2022-09-20

    插件能够正确在Linux上运行 离不开 小千 不厌其烦的测试与调试 💖

    感谢每一位为超级剪贴板提出过建议、参与测试与调试的用户

    • feat: 支持自定义数据库路径/存储条数/过期时间/展示在主界面的按钮
    • feat: 功能按钮支持自定义 创造属于你自己的功能按钮
    • feat: 添加了9个内置的自定义功能按钮
    • feat: 增加了更优雅的弹窗与提示界面
    • feat: 预览页更宽了 支持预览图片
    • feat: 导航栏图标样式修改
    • fix: 解决了CPU占用高、浏览大图卡顿的性能问题
    • fix: 从后台进入插件自动清空搜索框
    • refactor: 改变了监听剪贴板的方式 性能更优
    • refactor: 引入了ElementPlus组件库与图标库

    v1.3.4

    2022-09-19

    • feat: 移除插件内收藏Tab
    • feat: 点击收藏按钮后将携带数据跳转至备忘快贴
    • refactor: 优化代码执行逻辑 移除冗余代码

    v1.3.3

    2022-09-16

    • feat: 提高主色在深色模式下的对比度 改善长文本表现
    • feat: 添加快存功能按钮 配合超级粘贴插件实现快速转存
    • feat: 支持在预览页展示全部功能按钮
    • feat: 重要版本更新自动展示通知卡片
    • fix: 修正多选快捷键Shift影响检索输入的问题
    • fix: 检索词更新后 未自动激活首条记录

    v1.3.2

    2022-09-15

    • feat: 鼠标悬停展示具体日期、完整数据
    • feat: 调整历史记录上限为800条
    • fix: 修复鼠标置于功能按钮时上下键失效的问题
    • fix: 调整大图片判定尺寸

    v1.3.1

    2022-09-12

    • feat: 支持通过历史记录列表进入分词
    • feat: 减小插件体积 对智慧分词的支持改为插件跳转

    v1.3.0

    2022-09-09

    • feat: 添加多选功能 支持跨标签合并文本/图片/文件
    • feat: 支持通过按下Shift进入多选功能 支持按住Shift快速选择
    • feat: 支持使用Ctrl+CEnter快捷合并复制/粘贴
    • feat: 添加智慧分词功能 可对文本进行分割提取
    • feat: 长文本以蓝色高亮显示 不再提供查看全部按钮
    • feat: 优化界面动效

    v1.2.3

    2022-09-08

    • fix: 修复了检索内容时搜索记录不准确的问题

    v1.2.2

    2022-09-07

    • feat: 搜索框支持使用空格分词以同时检索多个关键词
    • fix: 修复了剪贴板数据记录遗漏的问题
    • fix: 收藏内容不再计入到条数限制中
    • fix: 修复了非预期的搜索框聚焦行为
    • fix: 预览页的按钮改为固定在顶部

    v1.2.1

    2022-09-05

    • feat: 支持通过功能按钮进入任意数据的预览页面
    • feat: 支持通过功能按钮打开文件所在目录
    • feat: 添加取消收藏功能按钮
    • feat: 搜索框支持展开/收起 输入任意内容展开并聚焦搜索
    • feat: 侧栏预览页添加复制全部智慧分词按钮
    • feat: 功能按钮改为使用图标展示
    • feat: 优化了界面样式
    • refactor: 清理定时器 优化插件性能

    v1.2.0

    2022-09-04

    • feat: 添加右侧操作栏 支持复制/收藏/删除操作
    • feat: 添加标签页收藏 支持在此页面管理所有收藏
    • feat: 调整侧栏宽度 调整界面样式细节

    v1.1.7

    2022-08-30

    • fix: 定时器检查剪贴板,修复了不记录剪贴板的问题

    v1.1.6

    2022-08-27

    • feat: 添加Alt+数字键Ctrl+数字键快速选择功能
    • feat: 调整界面样式 调整查看全部位置 移除图片背景色 增加动效
    • feat: 搜索框增加检索条数展示
    • fix: 改善插件读取图片的性能

    v1.1.5

    2022-08-25

    • fix: 提高剪贴板读取频率,避免高频复制时丢数据的情况

    v1.1.4

    2022-08-20

    • feat: 增加清空搜索框的按钮
    • feat: 优化插件内按下ESC的功能: 退出完整预览/清空搜索框
    • feat: 进入插件自动选中框内全部文本
    • fix: Mac在分离窗口状态左键会粘贴到搜索框 #13

    v1.1.3

    2022-08-19

    • feat: 调整界面在深色模式下的表现样式
    • fix: 在侧栏全部数据中使用Ctrl+C复制部分文本失效
    • fix: Mac下使用Ctrl+C复制单条记录失效
    • fix: 通过换行符个数区分超长文本
    • fix: 窗口分离下鼠标单击导致粘贴到搜索框
    • chore: 原插件名剪贴板改为超级剪贴板

    v1.1.2

    2022-08-18

    • feat: 执行复制后只隐藏主界面而不退出插件到后台
    • feat: 搜索时不再区分大小写
    • fix: 文本内容会出现异常首行缩进
    • fix: 图片展示在列表中的时候 右侧多出一个图块
    • fix: 移除鼠标hover时数据底部的色块

    v1.1.1

    2022-08-17

    • fix: 复制超大图片进入插件时崩溃

    v1.1.0

    2022-08-16

    • feat: 支持使用 键切换选中记录
    • feat: 支持使用 Ctrl+C 复制选中记录
    • feat: 支持使用 Enter 复制选中记录并粘贴

    v1.0.4

    2022-08-16

    • feat: 进入插件自动回到顶部、切换至全部分类
    • feat: 监听到键盘事件自动聚焦到搜索框
    • feat: 调整鼠标hover动画与历史记录的active样式
    • fix: 使用Tab切换导航失效
    • fix: 超长图片显示越界

    v1.0.3

    2022-08-16

    • fix: 路径分隔符导致写入错误的数据文件

    v1.0.2

    2022-08-15

    • fix: Mac下由权限导致的无法写入数据

    v1.0.1

    2022-08-15

    • feat: 区分鼠标点击行为:左键复制并粘贴,右键仅复制
    • feat: 适配深色模式
    • feat: 移除了右侧的查看更多按钮
    • feat: 单次展示的条数增加到了15条
    • feat: 增加条数限制500条 增加存储日期限制14天
    • feat: 增加了多平台支持
    • fix: 执行粘贴后主输入框未隐藏
    • refactor: Vue3重构

    v1.0.0

    2022-08-14

    • release: v1.0.0发布
    ',141),d=[t];function r(c,h,f,u,v,n){return i(),a("div",null,d)}const p=l(o,[["render",r]]);export{s as __pageData,p as default}; diff --git a/assets/project_ClipboardManager_log_index.md.714cf9de.lean.js b/assets/project_ClipboardManager_log_index.md.e245d280.lean.js similarity index 85% rename from assets/project_ClipboardManager_log_index.md.714cf9de.lean.js rename to assets/project_ClipboardManager_log_index.md.e245d280.lean.js index 6ac1ed35..a7b5cd9a 100644 --- a/assets/project_ClipboardManager_log_index.md.714cf9de.lean.js +++ b/assets/project_ClipboardManager_log_index.md.e245d280.lean.js @@ -1 +1 @@ -import{_ as l,o as i,c as a,R as e}from"./chunks/framework.a304f0f7.js";const s=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/log/index.md","lastUpdated":1682528161000}'),o={name:"project/ClipboardManager/log/index.md"},t=e("",141),d=[t];function r(c,h,f,u,v,n){return i(),a("div",null,d)}const p=l(o,[["render",r]]);export{s as __pageData,p as default}; +import{_ as l,o as i,c as a,R as e}from"./chunks/framework.a304f0f7.js";const s=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/log/index.md","lastUpdated":1682586876000}'),o={name:"project/ClipboardManager/log/index.md"},t=e("",141),d=[t];function r(c,h,f,u,v,n){return i(),a("div",null,d)}const p=l(o,[["render",r]]);export{s as __pageData,p as default}; diff --git a/assets/project_ClipboardManager_statement_index.md.95f4a6ec.js b/assets/project_ClipboardManager_statement_index.md.01b45ff7.js similarity index 99% rename from assets/project_ClipboardManager_statement_index.md.95f4a6ec.js rename to assets/project_ClipboardManager_statement_index.md.01b45ff7.js index a386de62..e4b0d7d4 100644 --- a/assets/project_ClipboardManager_statement_index.md.95f4a6ec.js +++ b/assets/project_ClipboardManager_statement_index.md.01b45ff7.js @@ -1 +1 @@ -import{_ as a,o as e,c as o,R as l}from"./chunks/framework.a304f0f7.js";const _=JSON.parse('{"title":"Q&A","description":"","frontmatter":{"navbar":false},"headers":[],"relativePath":"project/ClipboardManager/statement/index.md","lastUpdated":1682528161000}'),r={name:"project/ClipboardManager/statement/index.md"},i=l('

    Q&A

    插件启动时报错/白屏

    请依次尝试以下方法:

    1. 完全退出插件后重新启动插件
    2. 重新启动 uTools
    3. 删除本地数据库文件(位于C盘用户根目录下 文件名为_utools_clipboard_manager_storage
    4. 格式化 uTools 内的插件数据 (账号与数据/查看存储的文档数据)

    如果上述方法都无法解决你的问题,欢迎论坛回帖或加入QQ群 769115389 交流具体情况

    自动粘贴(自动上屏)功能失灵了

    目前已知:自动粘贴功能在 Windows微信 的聊天输入框内是不可用的。在其他场景下,此功能都能够正常使用

    插件内操作延迟、卡顿

    插件内复制/收藏/删除操作卡顿,可能是因为数据文件过大。

    当插件内的操作有明显延迟卡顿时,请尝试清理无用的图片记录,可以帮你显著地降低数据文件体积,提高插件响应速度

    剪贴板记录丢失

    一般是由于插件退出后台运行导致的, 插件需要保持后台运行才能记录剪贴板, 请检查:

    • 是否正确设置了插件跟随主程序启动
    • 是否手动清理或关闭了后台插件
      • 在分离模式下关闭插件(由uTools的机制决定,分离模式下关闭窗口则退出插件)
      • 使用了 clear 命令
      • 在插件列表主动退出了插件
    • 插件在高频复制的场景下,可能会漏掉某些记录

    如何设置快捷键快速打开插件?

    uTools偏好设置 / 全局快捷键 / 将Clipboard设置为你想要的快捷键

    后续通过快捷键即可快速唤出插件

    为什么偶尔CPU占用特别高? 为什么预览图片时插件卡顿?

    这是由超级剪贴板监听剪贴板内容更新的机制决定的,为了不漏数据,超级剪贴板的监听策略为每300ms读取一次剪贴板内容并与上一次读取到的内容作比较。如果剪贴板当前位置是一张大图片,那么插件会反复读取这张图片,导致大量计算被用在了无意义的读取与对比上。

    插件市场中相同原理的同类插件的处理方法是:

    • 轮询比较两次剪贴板内容,如果检查到当前剪贴板内容数据量较大,则降低轮询时间间隔。
      • 这避免了读取大图片/长文本时的高占用,然而这很容易导致漏数据
    • 轮询比较两次剪贴板内容,由用户自定义轮询时间
      • 用户设置的轮询间隔时间长,读取大图片/长文本时不会导致高占用,但很容易漏数据。
      • 用户设置的轮询间隔时间较短,则又会造成高占用

    v1.4.0版本更新中,超级剪贴板换用了clipboard-event库监听剪贴板更新事件,这种方式性能更优,不需要反复读取剪贴板内容来做比较,而只需要在检查到剪贴板更新事件时通知插件读取一次剪贴板即可,完全解决了CPU占用高、浏览图片卡顿的问题。

    然而该库包含了二进制文件,这触发了uTools插件市场的安全限制而无法上架。所以自v1.4.2起,插件不再内置监听剪贴板更新事件的二进制文件,用户要使用低占用、高性能的剪贴板监听方案,可以自行下载并将其移动到剪贴板数据库文件所在目录,详见如何手动安装clipboard-event-handler,插件会自动判断使用新的抑或是旧的方案监听剪贴板更新。同时,自v1.4.3起,你可以在设置页查看当前剪贴板监听程序的运行状态。

    为什么官方的剪贴板插件没有CPU占用高这个问题?

    官方的剪贴板内容更新事件监听函数也是从一个二进制文件引出的,这个二进制文件跟随主程序启动,不受剪贴板插件的打开或关闭的影响。

    我对这个插件的安全性有担忧, 插件偷窃我的隐私怎么办?

    首先, 我写这个插件不是为了获取你的隐私的, 我对你的隐私没有兴趣, 这一点你完全可以放心;

    其次uTools官方在插件上架前会对代码进行审查, 如果插件有高危行为, 那也不会过审;

    再其次, uTools大部分用户是程序员, 如果我真的在代码里藏了"毒", 那他们也有办法发现, 如果你真的对你的隐私十分关心, 可以选择从开源仓库下载代码自行构建

    开源版本和插件市场版本的区别?

    开源版本后续将只提供必要的BUG修复, 不再添加新功能

    • 开源版本: 包含完整的基本功能, 可以自行构建开源版本, 通过安装离线插件方式使用
    • 市场版本: 包含后续更新的新功能插件会员功能, 可以直接从插件应用市场安装

    为什么不开源了, 为什么要开始收费?

    目前由我个人维护的开源版本已经趋于稳定, 可以满足绝大部分场景的需求

    • 代码开源的出发点不是为了让不愿付费的人白嫖开发者的劳动, 而是为开发提供更多的经验和思路, 开源的代码已经启发了一些开发者上架了自己的剪贴板插件应用
    • 开发和维护插件需要时间和精力, 插件付费可以鼓励我更积极的更新
    • 目前插件内已有的基本功能都不会转为收费, 可以放心使用
    ',34),t=[i];function d(c,n,s,h,p,u){return e(),o("div",null,t)}const m=a(r,[["render",d]]);export{_ as __pageData,m as default}; +import{_ as a,o as e,c as o,R as l}from"./chunks/framework.a304f0f7.js";const _=JSON.parse('{"title":"Q&A","description":"","frontmatter":{"navbar":false},"headers":[],"relativePath":"project/ClipboardManager/statement/index.md","lastUpdated":1682586876000}'),r={name:"project/ClipboardManager/statement/index.md"},i=l('

    Q&A

    插件启动时报错/白屏

    请依次尝试以下方法:

    1. 完全退出插件后重新启动插件
    2. 重新启动 uTools
    3. 删除本地数据库文件(位于C盘用户根目录下 文件名为_utools_clipboard_manager_storage
    4. 格式化 uTools 内的插件数据 (账号与数据/查看存储的文档数据)

    如果上述方法都无法解决你的问题,欢迎论坛回帖或加入QQ群 769115389 交流具体情况

    自动粘贴(自动上屏)功能失灵了

    目前已知:自动粘贴功能在 Windows微信 的聊天输入框内是不可用的。在其他场景下,此功能都能够正常使用

    插件内操作延迟、卡顿

    插件内复制/收藏/删除操作卡顿,可能是因为数据文件过大。

    当插件内的操作有明显延迟卡顿时,请尝试清理无用的图片记录,可以帮你显著地降低数据文件体积,提高插件响应速度

    剪贴板记录丢失

    一般是由于插件退出后台运行导致的, 插件需要保持后台运行才能记录剪贴板, 请检查:

    • 是否正确设置了插件跟随主程序启动
    • 是否手动清理或关闭了后台插件
      • 在分离模式下关闭插件(由uTools的机制决定,分离模式下关闭窗口则退出插件)
      • 使用了 clear 命令
      • 在插件列表主动退出了插件
    • 插件在高频复制的场景下,可能会漏掉某些记录

    如何设置快捷键快速打开插件?

    uTools偏好设置 / 全局快捷键 / 将Clipboard设置为你想要的快捷键

    后续通过快捷键即可快速唤出插件

    为什么偶尔CPU占用特别高? 为什么预览图片时插件卡顿?

    这是由超级剪贴板监听剪贴板内容更新的机制决定的,为了不漏数据,超级剪贴板的监听策略为每300ms读取一次剪贴板内容并与上一次读取到的内容作比较。如果剪贴板当前位置是一张大图片,那么插件会反复读取这张图片,导致大量计算被用在了无意义的读取与对比上。

    插件市场中相同原理的同类插件的处理方法是:

    • 轮询比较两次剪贴板内容,如果检查到当前剪贴板内容数据量较大,则降低轮询时间间隔。
      • 这避免了读取大图片/长文本时的高占用,然而这很容易导致漏数据
    • 轮询比较两次剪贴板内容,由用户自定义轮询时间
      • 用户设置的轮询间隔时间长,读取大图片/长文本时不会导致高占用,但很容易漏数据。
      • 用户设置的轮询间隔时间较短,则又会造成高占用

    v1.4.0版本更新中,超级剪贴板换用了clipboard-event库监听剪贴板更新事件,这种方式性能更优,不需要反复读取剪贴板内容来做比较,而只需要在检查到剪贴板更新事件时通知插件读取一次剪贴板即可,完全解决了CPU占用高、浏览图片卡顿的问题。

    然而该库包含了二进制文件,这触发了uTools插件市场的安全限制而无法上架。所以自v1.4.2起,插件不再内置监听剪贴板更新事件的二进制文件,用户要使用低占用、高性能的剪贴板监听方案,可以自行下载并将其移动到剪贴板数据库文件所在目录,详见如何手动安装clipboard-event-handler,插件会自动判断使用新的抑或是旧的方案监听剪贴板更新。同时,自v1.4.3起,你可以在设置页查看当前剪贴板监听程序的运行状态。

    为什么官方的剪贴板插件没有CPU占用高这个问题?

    官方的剪贴板内容更新事件监听函数也是从一个二进制文件引出的,这个二进制文件跟随主程序启动,不受剪贴板插件的打开或关闭的影响。

    我对这个插件的安全性有担忧, 插件偷窃我的隐私怎么办?

    首先, 我写这个插件不是为了获取你的隐私的, 我对你的隐私没有兴趣, 这一点你完全可以放心;

    其次uTools官方在插件上架前会对代码进行审查, 如果插件有高危行为, 那也不会过审;

    再其次, uTools大部分用户是程序员, 如果我真的在代码里藏了"毒", 那他们也有办法发现, 如果你真的对你的隐私十分关心, 可以选择从开源仓库下载代码自行构建

    开源版本和插件市场版本的区别?

    开源版本后续将只提供必要的BUG修复, 不再添加新功能

    • 开源版本: 包含完整的基本功能, 可以自行构建开源版本, 通过安装离线插件方式使用
    • 市场版本: 包含后续更新的新功能插件会员功能, 可以直接从插件应用市场安装

    为什么不开源了, 为什么要开始收费?

    目前由我个人维护的开源版本已经趋于稳定, 可以满足绝大部分场景的需求

    • 代码开源的出发点不是为了让不愿付费的人白嫖开发者的劳动, 而是为开发提供更多的经验和思路, 开源的代码已经启发了一些开发者上架了自己的剪贴板插件应用
    • 开发和维护插件需要时间和精力, 插件付费可以鼓励我更积极的更新
    • 目前插件内已有的基本功能都不会转为收费, 可以放心使用
    ',34),t=[i];function d(c,n,s,h,p,u){return e(),o("div",null,t)}const m=a(r,[["render",d]]);export{_ as __pageData,m as default}; diff --git a/assets/project_ClipboardManager_statement_index.md.95f4a6ec.lean.js b/assets/project_ClipboardManager_statement_index.md.01b45ff7.lean.js similarity index 85% rename from assets/project_ClipboardManager_statement_index.md.95f4a6ec.lean.js rename to assets/project_ClipboardManager_statement_index.md.01b45ff7.lean.js index b4994c95..fd6b518f 100644 --- a/assets/project_ClipboardManager_statement_index.md.95f4a6ec.lean.js +++ b/assets/project_ClipboardManager_statement_index.md.01b45ff7.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as o,R as l}from"./chunks/framework.a304f0f7.js";const _=JSON.parse('{"title":"Q&A","description":"","frontmatter":{"navbar":false},"headers":[],"relativePath":"project/ClipboardManager/statement/index.md","lastUpdated":1682528161000}'),r={name:"project/ClipboardManager/statement/index.md"},i=l("",34),t=[i];function d(c,n,s,h,p,u){return e(),o("div",null,t)}const m=a(r,[["render",d]]);export{_ as __pageData,m as default}; +import{_ as a,o as e,c as o,R as l}from"./chunks/framework.a304f0f7.js";const _=JSON.parse('{"title":"Q&A","description":"","frontmatter":{"navbar":false},"headers":[],"relativePath":"project/ClipboardManager/statement/index.md","lastUpdated":1682586876000}'),r={name:"project/ClipboardManager/statement/index.md"},i=l("",34),t=[i];function d(c,n,s,h,p,u){return e(),o("div",null,t)}const m=a(r,[["render",d]]);export{_ as __pageData,m as default}; diff --git a/assets/project_ClipboardManager_vip_index.md.453f0553.js b/assets/project_ClipboardManager_vip_index.md.5375ae9a.js similarity index 99% rename from assets/project_ClipboardManager_vip_index.md.453f0553.js rename to assets/project_ClipboardManager_vip_index.md.5375ae9a.js index 97d25142..1d2dc0e9 100644 --- a/assets/project_ClipboardManager_vip_index.md.453f0553.js +++ b/assets/project_ClipboardManager_vip_index.md.5375ae9a.js @@ -1 +1 @@ -import{_ as a,D as n,o as c,c as s,z as e,a as l,G as o,R as i}from"./chunks/framework.a304f0f7.js";const r="/assets/vip-webdav.2e7c539c.png",d="/assets/vip-highlight.e3827906.png",R=JSON.parse('{"title":"插件会员","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/vip/index.md","lastUpdated":1682528161000}'),p={name:"project/ClipboardManager/vip/index.md"},h=i('

    插件会员

    TIP

    超级剪贴板是一款 “随心所欲” 的软件。您可以永久免费使用它。

    但是,如果您真的喜欢它,您可以付费支持它的发展。作为答谢,您将获得更多更方便的功能。这取决于您,如果您不想,也没关系。谢谢你,祝你有美好的一天!☀️

    会员权益

    ',3),u=e("strong",null,"插件会员",-1),_=e("code",null,"WebDav数据多端同步",-1),g=e("code",null,"支持保存2000条历史记录",-1),b=e("code",null,"不限制历史记录过期时间",-1),m=e("code",null,"文本/图片/文件预览增强, 按住Ctrl/Command点击 解锁更多操作",-1),k=e("code",null,"键盘操作模式 屏蔽鼠标 高效快捷",-1),v=e("code",null,"简洁模式 隐藏页面中不必要的组件与元素",-1),f=e("code",null,"自定义分类排序",-1),x=e("code",null,"预览页代码高亮",-1),q=e("code",null,"关闭剪贴板图片记录",-1),T=e("code",null,"插件使用统计",-1),D=e("li",null,[e("strong",null,"...")],-1),y=e("strong",null,"uTools会员",-1),N=e("code",null,"插件使用统计",-1),C=i('

    会员定价

    WARNING

    • 插件会员为大版本买断制,购买后即可享用当前版本(2.x)后续所有更新的会员内容
    • 插件会员为虚拟商品,购买后不支持退款
    • 插件会员的价格是浮动的,随着功能和权益的增加,不排除涨价的可能
    • 购买插件会员后若未能及时到账,请加入QQ群反馈

    插件会员定价10元,登录uTools账号后,点击插件导航栏中个人中心图标进入购买页面

    购买后可使用会员功能,不购买不影响基本功能的使用,希望大家多多理解,按需购买

    WebDav同步功能

    WARNING

    如果剪贴板数据体积过大,可能出现同步时间过长、同步失败等问题

    建议及时删除保存在超级剪贴板内的图片,或关闭记录图片到剪贴板

    插件会员支持通过WebDav同步剪贴板数据

    获取服务器地址 账号 密钥后,仅需在插件内的设置页面添加WebDav配置后,

    每次本地数据库发生变化,都会将最新数据同步到同步到服务器(须开启插件内的自动同步 否则需要手动同步)

    WebDav

    • 如果配置了WebDav 则每天第一次进入插件时会自动从服务器拉取最新数据
    • 如果设置了自动同步 每次本地数据变化将自动同步本地数据到服务器
      • 插件会缓存云端数据文件的ETag 如果云端数据有更新 则采用云端数据覆盖本地数据
      • 数据文件的上传和下载都开启了gzip以节省流量与带宽
      • 清空本地数据库的同时也会触发云端文件清空
      • 可以选择关闭图片记录 以降低同步的数据文件体积 提高同步效率
    • 插件仅在每次启动时主动从云端拉取数据 云端数据的更新不会自动同步到本地
    • 可以通过点击工具栏按钮主动拉取云端数据到本地或上传数据到云端

    相关链接:

    代码高亮功能

    插件使用Shiki作为代码高亮库,为了压缩插件体积、提高代码执行效率,其相关依赖都将从CDN动态加载

    支持在设置页修改代码高亮的主题、可选语言、CDN地址,设置的首个语言为展开预览后的默认语言

    HighLight

    • 默认CDN地址: https://unpkg.com/shiki@0.14.1/
    • 默认主题: one-dark-pro
    • 默认语言: javascript css html json markdown php python ruby shellscript sql java c cpp csharp go

    更多可用主题/语言,见Github仓库:Shiki

    更多内容

    疑难解答

    ',21);function P(w,W,S,V,j,A){const t=n("Badge");return c(),s("div",null,[h,e("ul",null,[e("li",null,[u,e("ul",null,[e("li",null,[_,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[g,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[b,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[m,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[k,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[v,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[f,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[x,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[q,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[T,l(),o(t,{type:"tip",text:"已上线"})]),D])]),e("li",null,[y,e("ul",null,[e("li",null,[N,l(),o(t,{type:"tip",text:"已上线"})])])])]),C])}const B=a(p,[["render",P]]);export{R as __pageData,B as default}; +import{_ as a,D as n,o as c,c as s,z as e,a as l,G as o,R as i}from"./chunks/framework.a304f0f7.js";const r="/assets/vip-webdav.2e7c539c.png",d="/assets/vip-highlight.e3827906.png",R=JSON.parse('{"title":"插件会员","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/vip/index.md","lastUpdated":1682586876000}'),p={name:"project/ClipboardManager/vip/index.md"},h=i('

    插件会员

    TIP

    超级剪贴板是一款 “随心所欲” 的软件。您可以永久免费使用它。

    但是,如果您真的喜欢它,您可以付费支持它的发展。作为答谢,您将获得更多更方便的功能。这取决于您,如果您不想,也没关系。谢谢你,祝你有美好的一天!☀️

    会员权益

    ',3),u=e("strong",null,"插件会员",-1),_=e("code",null,"WebDav数据多端同步",-1),g=e("code",null,"支持保存2000条历史记录",-1),b=e("code",null,"不限制历史记录过期时间",-1),m=e("code",null,"文本/图片/文件预览增强, 按住Ctrl/Command点击 解锁更多操作",-1),k=e("code",null,"键盘操作模式 屏蔽鼠标 高效快捷",-1),v=e("code",null,"简洁模式 隐藏页面中不必要的组件与元素",-1),f=e("code",null,"自定义分类排序",-1),x=e("code",null,"预览页代码高亮",-1),q=e("code",null,"关闭剪贴板图片记录",-1),T=e("code",null,"插件使用统计",-1),D=e("li",null,[e("strong",null,"...")],-1),y=e("strong",null,"uTools会员",-1),N=e("code",null,"插件使用统计",-1),C=i('

    会员定价

    WARNING

    • 插件会员为大版本买断制,购买后即可享用当前版本(2.x)后续所有更新的会员内容
    • 插件会员为虚拟商品,购买后不支持退款
    • 插件会员的价格是浮动的,随着功能和权益的增加,不排除涨价的可能
    • 购买插件会员后若未能及时到账,请加入QQ群反馈

    插件会员定价10元,登录uTools账号后,点击插件导航栏中个人中心图标进入购买页面

    购买后可使用会员功能,不购买不影响基本功能的使用,希望大家多多理解,按需购买

    WebDav同步功能

    WARNING

    如果剪贴板数据体积过大,可能出现同步时间过长、同步失败等问题

    建议及时删除保存在超级剪贴板内的图片,或关闭记录图片到剪贴板

    插件会员支持通过WebDav同步剪贴板数据

    获取服务器地址 账号 密钥后,仅需在插件内的设置页面添加WebDav配置后,

    每次本地数据库发生变化,都会将最新数据同步到同步到服务器(须开启插件内的自动同步 否则需要手动同步)

    WebDav

    • 如果配置了WebDav 则每天第一次进入插件时会自动从服务器拉取最新数据
    • 如果设置了自动同步 每次本地数据变化将自动同步本地数据到服务器
      • 插件会缓存云端数据文件的ETag 如果云端数据有更新 则采用云端数据覆盖本地数据
      • 数据文件的上传和下载都开启了gzip以节省流量与带宽
      • 清空本地数据库的同时也会触发云端文件清空
      • 可以选择关闭图片记录 以降低同步的数据文件体积 提高同步效率
    • 插件仅在每次启动时主动从云端拉取数据 云端数据的更新不会自动同步到本地
    • 可以通过点击工具栏按钮主动拉取云端数据到本地或上传数据到云端

    相关链接:

    代码高亮功能

    插件使用Shiki作为代码高亮库,为了压缩插件体积、提高代码执行效率,其相关依赖都将从CDN动态加载

    支持在设置页修改代码高亮的主题、可选语言、CDN地址,设置的首个语言为展开预览后的默认语言

    HighLight

    • 默认CDN地址: https://unpkg.com/shiki@0.14.1/
    • 默认主题: one-dark-pro
    • 默认语言: javascript css html json markdown php python ruby shellscript sql java c cpp csharp go

    更多可用主题/语言,见Github仓库:Shiki

    更多内容

    疑难解答

    ',21);function P(w,W,S,V,j,A){const t=n("Badge");return c(),s("div",null,[h,e("ul",null,[e("li",null,[u,e("ul",null,[e("li",null,[_,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[g,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[b,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[m,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[k,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[v,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[f,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[x,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[q,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[T,l(),o(t,{type:"tip",text:"已上线"})]),D])]),e("li",null,[y,e("ul",null,[e("li",null,[N,l(),o(t,{type:"tip",text:"已上线"})])])])]),C])}const B=a(p,[["render",P]]);export{R as __pageData,B as default}; diff --git a/assets/project_ClipboardManager_vip_index.md.453f0553.lean.js b/assets/project_ClipboardManager_vip_index.md.5375ae9a.lean.js similarity index 96% rename from assets/project_ClipboardManager_vip_index.md.453f0553.lean.js rename to assets/project_ClipboardManager_vip_index.md.5375ae9a.lean.js index 1f7db4ce..4fd3fed0 100644 --- a/assets/project_ClipboardManager_vip_index.md.453f0553.lean.js +++ b/assets/project_ClipboardManager_vip_index.md.5375ae9a.lean.js @@ -1 +1 @@ -import{_ as a,D as n,o as c,c as s,z as e,a as l,G as o,R as i}from"./chunks/framework.a304f0f7.js";const r="/assets/vip-webdav.2e7c539c.png",d="/assets/vip-highlight.e3827906.png",R=JSON.parse('{"title":"插件会员","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/vip/index.md","lastUpdated":1682528161000}'),p={name:"project/ClipboardManager/vip/index.md"},h=i("",3),u=e("strong",null,"插件会员",-1),_=e("code",null,"WebDav数据多端同步",-1),g=e("code",null,"支持保存2000条历史记录",-1),b=e("code",null,"不限制历史记录过期时间",-1),m=e("code",null,"文本/图片/文件预览增强, 按住Ctrl/Command点击 解锁更多操作",-1),k=e("code",null,"键盘操作模式 屏蔽鼠标 高效快捷",-1),v=e("code",null,"简洁模式 隐藏页面中不必要的组件与元素",-1),f=e("code",null,"自定义分类排序",-1),x=e("code",null,"预览页代码高亮",-1),q=e("code",null,"关闭剪贴板图片记录",-1),T=e("code",null,"插件使用统计",-1),D=e("li",null,[e("strong",null,"...")],-1),y=e("strong",null,"uTools会员",-1),N=e("code",null,"插件使用统计",-1),C=i("",21);function P(w,W,S,V,j,A){const t=n("Badge");return c(),s("div",null,[h,e("ul",null,[e("li",null,[u,e("ul",null,[e("li",null,[_,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[g,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[b,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[m,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[k,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[v,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[f,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[x,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[q,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[T,l(),o(t,{type:"tip",text:"已上线"})]),D])]),e("li",null,[y,e("ul",null,[e("li",null,[N,l(),o(t,{type:"tip",text:"已上线"})])])])]),C])}const B=a(p,[["render",P]]);export{R as __pageData,B as default}; +import{_ as a,D as n,o as c,c as s,z as e,a as l,G as o,R as i}from"./chunks/framework.a304f0f7.js";const r="/assets/vip-webdav.2e7c539c.png",d="/assets/vip-highlight.e3827906.png",R=JSON.parse('{"title":"插件会员","description":"","frontmatter":{},"headers":[],"relativePath":"project/ClipboardManager/vip/index.md","lastUpdated":1682586876000}'),p={name:"project/ClipboardManager/vip/index.md"},h=i("",3),u=e("strong",null,"插件会员",-1),_=e("code",null,"WebDav数据多端同步",-1),g=e("code",null,"支持保存2000条历史记录",-1),b=e("code",null,"不限制历史记录过期时间",-1),m=e("code",null,"文本/图片/文件预览增强, 按住Ctrl/Command点击 解锁更多操作",-1),k=e("code",null,"键盘操作模式 屏蔽鼠标 高效快捷",-1),v=e("code",null,"简洁模式 隐藏页面中不必要的组件与元素",-1),f=e("code",null,"自定义分类排序",-1),x=e("code",null,"预览页代码高亮",-1),q=e("code",null,"关闭剪贴板图片记录",-1),T=e("code",null,"插件使用统计",-1),D=e("li",null,[e("strong",null,"...")],-1),y=e("strong",null,"uTools会员",-1),N=e("code",null,"插件使用统计",-1),C=i("",21);function P(w,W,S,V,j,A){const t=n("Badge");return c(),s("div",null,[h,e("ul",null,[e("li",null,[u,e("ul",null,[e("li",null,[_,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[g,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[b,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[m,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[k,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[v,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[f,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[x,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[q,l(),o(t,{type:"tip",text:"已上线"})]),e("li",null,[T,l(),o(t,{type:"tip",text:"已上线"})]),D])]),e("li",null,[y,e("ul",null,[e("li",null,[N,l(),o(t,{type:"tip",text:"已上线"})])])])]),C])}const B=a(p,[["render",P]]);export{R as __pageData,B as default}; diff --git a/assets/project_JSRunner_index.md.47b54e96.js b/assets/project_JSRunner_index.md.97c8b761.js similarity index 96% rename from assets/project_JSRunner_index.md.47b54e96.js rename to assets/project_JSRunner_index.md.97c8b761.js index c652ef79..bf49f5dd 100644 --- a/assets/project_JSRunner_index.md.47b54e96.js +++ b/assets/project_JSRunner_index.md.97c8b761.js @@ -1 +1 @@ -import{D as e,o as r,c,G as n,a4 as d,a5 as u,z as t,a as s}from"./chunks/framework.a304f0f7.js";const g="/assets/logo.df975406.png",m="/assets/img1.49b1ec51.png",p="/assets/img2.a5b117fb.png",_="/assets/img3.a5b785fc.png",h="/assets/img4.cd285ff7.png",S=t("br",null,null,-1),f=t("h2",{id:"🔰-开始使用",tabindex:"-1"},[s("🔰 开始使用 "),t("a",{class:"header-anchor",href:"#🔰-开始使用","aria-label":'Permalink to "🔰 开始使用"'},"​")],-1),b=t("ul",null,[t("li",null,"运行JavaScript代码 快速验证代码逻辑"),t("li",null,"支持切换NodeJS/浏览器运行环境"),t("li",null,[t("code",null,"Ctrl/Command+R"),s(" 快速运行代码")]),t("li",null,"支持代码回溯 实时记录代码运行历史"),t("li",null,"支持手动触发代码运行/实时运行代码"),t("li",null,"适配深色模式")],-1),k=JSON.parse('{"title":"超级JavaScript","description":"","frontmatter":{"title":"超级JavaScript","navbar":false},"headers":[],"relativePath":"project/JSRunner/index.md","lastUpdated":1682528161000}'),J={name:"project/JSRunner/index.md"},C=Object.assign(J,{setup(v){const a={subTitle:"✨ JavaScript运行器 支持多种运行环境 快速验证代码逻辑",logo:g,linkList:[{content:"🕶️ 在线体验",target:"https://ziuchen.github.io/JSRunner"},{content:"👨🏻‍💻 开源地址",target:"https://github.com/ZiuChen/JSRunner"},{content:"🚚 更新日志",target:"./log/"}]},l=[{src:m},{src:p},{src:_},{src:h}];return(x,R)=>{const o=e("Title"),i=e("ImgSlider");return r(),c("div",null,[n(o,d(u(a)),null,16),S,n(i,{imgSliderList:l}),f,b])}}});export{k as __pageData,C as default}; +import{D as e,o as r,c,G as n,a4 as d,a5 as u,z as t,a as s}from"./chunks/framework.a304f0f7.js";const g="/assets/logo.df975406.png",m="/assets/img1.49b1ec51.png",p="/assets/img2.a5b117fb.png",_="/assets/img3.a5b785fc.png",h="/assets/img4.cd285ff7.png",S=t("br",null,null,-1),f=t("h2",{id:"🔰-开始使用",tabindex:"-1"},[s("🔰 开始使用 "),t("a",{class:"header-anchor",href:"#🔰-开始使用","aria-label":'Permalink to "🔰 开始使用"'},"​")],-1),b=t("ul",null,[t("li",null,"运行JavaScript代码 快速验证代码逻辑"),t("li",null,"支持切换NodeJS/浏览器运行环境"),t("li",null,[t("code",null,"Ctrl/Command+R"),s(" 快速运行代码")]),t("li",null,"支持代码回溯 实时记录代码运行历史"),t("li",null,"支持手动触发代码运行/实时运行代码"),t("li",null,"适配深色模式")],-1),k=JSON.parse('{"title":"超级JavaScript","description":"","frontmatter":{"title":"超级JavaScript","navbar":false},"headers":[],"relativePath":"project/JSRunner/index.md","lastUpdated":1682586876000}'),J={name:"project/JSRunner/index.md"},C=Object.assign(J,{setup(v){const a={subTitle:"✨ JavaScript运行器 支持多种运行环境 快速验证代码逻辑",logo:g,linkList:[{content:"🕶️ 在线体验",target:"https://ziuchen.github.io/JSRunner"},{content:"👨🏻‍💻 开源地址",target:"https://github.com/ZiuChen/JSRunner"},{content:"🚚 更新日志",target:"./log/"}]},l=[{src:m},{src:p},{src:_},{src:h}];return(x,R)=>{const o=e("Title"),i=e("ImgSlider");return r(),c("div",null,[n(o,d(u(a)),null,16),S,n(i,{imgSliderList:l}),f,b])}}});export{k as __pageData,C as default}; diff --git a/assets/project_JSRunner_index.md.47b54e96.lean.js b/assets/project_JSRunner_index.md.97c8b761.lean.js similarity index 96% rename from assets/project_JSRunner_index.md.47b54e96.lean.js rename to assets/project_JSRunner_index.md.97c8b761.lean.js index c652ef79..bf49f5dd 100644 --- a/assets/project_JSRunner_index.md.47b54e96.lean.js +++ b/assets/project_JSRunner_index.md.97c8b761.lean.js @@ -1 +1 @@ -import{D as e,o as r,c,G as n,a4 as d,a5 as u,z as t,a as s}from"./chunks/framework.a304f0f7.js";const g="/assets/logo.df975406.png",m="/assets/img1.49b1ec51.png",p="/assets/img2.a5b117fb.png",_="/assets/img3.a5b785fc.png",h="/assets/img4.cd285ff7.png",S=t("br",null,null,-1),f=t("h2",{id:"🔰-开始使用",tabindex:"-1"},[s("🔰 开始使用 "),t("a",{class:"header-anchor",href:"#🔰-开始使用","aria-label":'Permalink to "🔰 开始使用"'},"​")],-1),b=t("ul",null,[t("li",null,"运行JavaScript代码 快速验证代码逻辑"),t("li",null,"支持切换NodeJS/浏览器运行环境"),t("li",null,[t("code",null,"Ctrl/Command+R"),s(" 快速运行代码")]),t("li",null,"支持代码回溯 实时记录代码运行历史"),t("li",null,"支持手动触发代码运行/实时运行代码"),t("li",null,"适配深色模式")],-1),k=JSON.parse('{"title":"超级JavaScript","description":"","frontmatter":{"title":"超级JavaScript","navbar":false},"headers":[],"relativePath":"project/JSRunner/index.md","lastUpdated":1682528161000}'),J={name:"project/JSRunner/index.md"},C=Object.assign(J,{setup(v){const a={subTitle:"✨ JavaScript运行器 支持多种运行环境 快速验证代码逻辑",logo:g,linkList:[{content:"🕶️ 在线体验",target:"https://ziuchen.github.io/JSRunner"},{content:"👨🏻‍💻 开源地址",target:"https://github.com/ZiuChen/JSRunner"},{content:"🚚 更新日志",target:"./log/"}]},l=[{src:m},{src:p},{src:_},{src:h}];return(x,R)=>{const o=e("Title"),i=e("ImgSlider");return r(),c("div",null,[n(o,d(u(a)),null,16),S,n(i,{imgSliderList:l}),f,b])}}});export{k as __pageData,C as default}; +import{D as e,o as r,c,G as n,a4 as d,a5 as u,z as t,a as s}from"./chunks/framework.a304f0f7.js";const g="/assets/logo.df975406.png",m="/assets/img1.49b1ec51.png",p="/assets/img2.a5b117fb.png",_="/assets/img3.a5b785fc.png",h="/assets/img4.cd285ff7.png",S=t("br",null,null,-1),f=t("h2",{id:"🔰-开始使用",tabindex:"-1"},[s("🔰 开始使用 "),t("a",{class:"header-anchor",href:"#🔰-开始使用","aria-label":'Permalink to "🔰 开始使用"'},"​")],-1),b=t("ul",null,[t("li",null,"运行JavaScript代码 快速验证代码逻辑"),t("li",null,"支持切换NodeJS/浏览器运行环境"),t("li",null,[t("code",null,"Ctrl/Command+R"),s(" 快速运行代码")]),t("li",null,"支持代码回溯 实时记录代码运行历史"),t("li",null,"支持手动触发代码运行/实时运行代码"),t("li",null,"适配深色模式")],-1),k=JSON.parse('{"title":"超级JavaScript","description":"","frontmatter":{"title":"超级JavaScript","navbar":false},"headers":[],"relativePath":"project/JSRunner/index.md","lastUpdated":1682586876000}'),J={name:"project/JSRunner/index.md"},C=Object.assign(J,{setup(v){const a={subTitle:"✨ JavaScript运行器 支持多种运行环境 快速验证代码逻辑",logo:g,linkList:[{content:"🕶️ 在线体验",target:"https://ziuchen.github.io/JSRunner"},{content:"👨🏻‍💻 开源地址",target:"https://github.com/ZiuChen/JSRunner"},{content:"🚚 更新日志",target:"./log/"}]},l=[{src:m},{src:p},{src:_},{src:h}];return(x,R)=>{const o=e("Title"),i=e("ImgSlider");return r(),c("div",null,[n(o,d(u(a)),null,16),S,n(i,{imgSliderList:l}),f,b])}}});export{k as __pageData,C as default}; diff --git a/assets/project_JSRunner_log_index.md.cc34efe6.js b/assets/project_JSRunner_log_index.md.5fa1e94d.js similarity index 95% rename from assets/project_JSRunner_log_index.md.cc34efe6.js rename to assets/project_JSRunner_log_index.md.5fa1e94d.js index 3699e312..2516e319 100644 --- a/assets/project_JSRunner_log_index.md.cc34efe6.js +++ b/assets/project_JSRunner_log_index.md.5fa1e94d.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,R as o}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/JSRunner/log/index.md","lastUpdated":1682528161000}'),i={name:"project/JSRunner/log/index.md"},l=o('

    更新日志

    v1.0.2

    2023-04-24

    • fix: 修复NodeJS环境下无法导入第三方模块的问题

    v1.0.1

    2023-04-23

    • feat: 关于页添加项目开源地址
    • fix: 即时执行模式下代码为空时忽略警告
    • fix: 调整历史回溯的数量限制

    v1.0.0

    2023-04-19

    • 插件发布
    ',10),r=[l];function n(d,c,s,_,h,u){return a(),t("div",null,r)}const v=e(i,[["render",n]]);export{f as __pageData,v as default}; +import{_ as e,o as a,c as t,R as o}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/JSRunner/log/index.md","lastUpdated":1682586876000}'),i={name:"project/JSRunner/log/index.md"},l=o('

    更新日志

    v1.0.2

    2023-04-24

    • fix: 修复NodeJS环境下无法导入第三方模块的问题

    v1.0.1

    2023-04-23

    • feat: 关于页添加项目开源地址
    • fix: 即时执行模式下代码为空时忽略警告
    • fix: 调整历史回溯的数量限制

    v1.0.0

    2023-04-19

    • 插件发布
    ',10),r=[l];function n(d,c,s,_,h,u){return a(),t("div",null,r)}const v=e(i,[["render",n]]);export{f as __pageData,v as default}; diff --git a/assets/project_JSRunner_log_index.md.cc34efe6.lean.js b/assets/project_JSRunner_log_index.md.5fa1e94d.lean.js similarity index 84% rename from assets/project_JSRunner_log_index.md.cc34efe6.lean.js rename to assets/project_JSRunner_log_index.md.5fa1e94d.lean.js index 24c523e9..f5f6d55b 100644 --- a/assets/project_JSRunner_log_index.md.cc34efe6.lean.js +++ b/assets/project_JSRunner_log_index.md.5fa1e94d.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,R as o}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/JSRunner/log/index.md","lastUpdated":1682528161000}'),i={name:"project/JSRunner/log/index.md"},l=o("",10),r=[l];function n(d,c,s,_,h,u){return a(),t("div",null,r)}const v=e(i,[["render",n]]);export{f as __pageData,v as default}; +import{_ as e,o as a,c as t,R as o}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/JSRunner/log/index.md","lastUpdated":1682586876000}'),i={name:"project/JSRunner/log/index.md"},l=o("",10),r=[l];function n(d,c,s,_,h,u){return a(),t("div",null,r)}const v=e(i,[["render",n]]);export{f as __pageData,v as default}; diff --git a/assets/project_Markdown_index.md.69c4ad3a.js b/assets/project_Markdown_index.md.316f1afd.js similarity index 96% rename from assets/project_Markdown_index.md.69c4ad3a.js rename to assets/project_Markdown_index.md.316f1afd.js index cb2aeaef..9c8334fe 100644 --- a/assets/project_Markdown_index.md.69c4ad3a.js +++ b/assets/project_Markdown_index.md.316f1afd.js @@ -1 +1 @@ -import{D as t,o as s,c as r,G as e,a4 as l,a5 as c,z as d,R as _}from"./chunks/framework.a304f0f7.js";const m="/assets/logo.16601c26.png",g="/assets/img1.7a494e3a.png",p="/assets/img2.4ebfc061.png",u="/assets/img3.105ee736.png",h=d("br",null,null,-1),k=_('

    🔰 开始使用

    • 由ByteMD强力驱动,功能丰富、性能强劲
    • 支持GFM扩展语法、脚注、Gemoji、KaTeX数学公式、Mermaid图表
    • 支持通过Frontmatter设置多种主题、代码高亮样式
    • 支持多级目录,目录支持无限嵌套
    • 支持通过粘贴/拖拽的方式批量上传图片、支持截取屏幕截图
    • 支持Markdown文件的批量导入、批量导出
    • 支持插件多开,同时编辑/参考多个文章
    • 支持实时同步预览、自动保存
    ',2),w=JSON.parse('{"title":"超级Markdown","description":"","frontmatter":{"title":"超级Markdown","navbar":false},"headers":[],"relativePath":"project/Markdown/index.md","lastUpdated":1682528161000}'),M={name:"project/Markdown/index.md"},P=Object.assign(M,{setup(S){const a={subTitle:"✨ 强大的Markdown编辑器",logo:m,linkList:[{content:"🕶️ 在线体验",target:"https://ziuchen.github.io/SuperMarkdown"},{content:"👨🏻‍💻 开源地址",target:"https://github.com/ZiuChen/SuperMarkdown"},{content:"🚀 快捷键一览",target:"./shortcut/"},{content:"🚚 更新日志",target:"./log/"}]},o=[{src:g},{src:p},{src:u}];return(T,b)=>{const n=t("Title"),i=t("ImgSlider");return s(),r("div",null,[e(n,l(c(a)),null,16),h,e(i,{imgSliderList:o}),k])}}});export{w as __pageData,P as default}; +import{D as t,o as s,c as r,G as e,a4 as l,a5 as c,z as d,R as _}from"./chunks/framework.a304f0f7.js";const m="/assets/logo.16601c26.png",g="/assets/img1.7a494e3a.png",p="/assets/img2.4ebfc061.png",u="/assets/img3.105ee736.png",h=d("br",null,null,-1),k=_('

    🔰 开始使用

    • 由ByteMD强力驱动,功能丰富、性能强劲
    • 支持GFM扩展语法、脚注、Gemoji、KaTeX数学公式、Mermaid图表
    • 支持通过Frontmatter设置多种主题、代码高亮样式
    • 支持多级目录,目录支持无限嵌套
    • 支持通过粘贴/拖拽的方式批量上传图片、支持截取屏幕截图
    • 支持Markdown文件的批量导入、批量导出
    • 支持插件多开,同时编辑/参考多个文章
    • 支持实时同步预览、自动保存
    ',2),w=JSON.parse('{"title":"超级Markdown","description":"","frontmatter":{"title":"超级Markdown","navbar":false},"headers":[],"relativePath":"project/Markdown/index.md","lastUpdated":1682586876000}'),M={name:"project/Markdown/index.md"},P=Object.assign(M,{setup(S){const a={subTitle:"✨ 强大的Markdown编辑器",logo:m,linkList:[{content:"🕶️ 在线体验",target:"https://ziuchen.github.io/SuperMarkdown"},{content:"👨🏻‍💻 开源地址",target:"https://github.com/ZiuChen/SuperMarkdown"},{content:"🚀 快捷键一览",target:"./shortcut/"},{content:"🚚 更新日志",target:"./log/"}]},o=[{src:g},{src:p},{src:u}];return(T,b)=>{const n=t("Title"),i=t("ImgSlider");return s(),r("div",null,[e(n,l(c(a)),null,16),h,e(i,{imgSliderList:o}),k])}}});export{w as __pageData,P as default}; diff --git a/assets/project_Markdown_index.md.69c4ad3a.lean.js b/assets/project_Markdown_index.md.316f1afd.lean.js similarity index 93% rename from assets/project_Markdown_index.md.69c4ad3a.lean.js rename to assets/project_Markdown_index.md.316f1afd.lean.js index 76d6945a..f4c234bb 100644 --- a/assets/project_Markdown_index.md.69c4ad3a.lean.js +++ b/assets/project_Markdown_index.md.316f1afd.lean.js @@ -1 +1 @@ -import{D as t,o as s,c as r,G as e,a4 as l,a5 as c,z as d,R as _}from"./chunks/framework.a304f0f7.js";const m="/assets/logo.16601c26.png",g="/assets/img1.7a494e3a.png",p="/assets/img2.4ebfc061.png",u="/assets/img3.105ee736.png",h=d("br",null,null,-1),k=_("",2),w=JSON.parse('{"title":"超级Markdown","description":"","frontmatter":{"title":"超级Markdown","navbar":false},"headers":[],"relativePath":"project/Markdown/index.md","lastUpdated":1682528161000}'),M={name:"project/Markdown/index.md"},P=Object.assign(M,{setup(S){const a={subTitle:"✨ 强大的Markdown编辑器",logo:m,linkList:[{content:"🕶️ 在线体验",target:"https://ziuchen.github.io/SuperMarkdown"},{content:"👨🏻‍💻 开源地址",target:"https://github.com/ZiuChen/SuperMarkdown"},{content:"🚀 快捷键一览",target:"./shortcut/"},{content:"🚚 更新日志",target:"./log/"}]},o=[{src:g},{src:p},{src:u}];return(T,b)=>{const n=t("Title"),i=t("ImgSlider");return s(),r("div",null,[e(n,l(c(a)),null,16),h,e(i,{imgSliderList:o}),k])}}});export{w as __pageData,P as default}; +import{D as t,o as s,c as r,G as e,a4 as l,a5 as c,z as d,R as _}from"./chunks/framework.a304f0f7.js";const m="/assets/logo.16601c26.png",g="/assets/img1.7a494e3a.png",p="/assets/img2.4ebfc061.png",u="/assets/img3.105ee736.png",h=d("br",null,null,-1),k=_("",2),w=JSON.parse('{"title":"超级Markdown","description":"","frontmatter":{"title":"超级Markdown","navbar":false},"headers":[],"relativePath":"project/Markdown/index.md","lastUpdated":1682586876000}'),M={name:"project/Markdown/index.md"},P=Object.assign(M,{setup(S){const a={subTitle:"✨ 强大的Markdown编辑器",logo:m,linkList:[{content:"🕶️ 在线体验",target:"https://ziuchen.github.io/SuperMarkdown"},{content:"👨🏻‍💻 开源地址",target:"https://github.com/ZiuChen/SuperMarkdown"},{content:"🚀 快捷键一览",target:"./shortcut/"},{content:"🚚 更新日志",target:"./log/"}]},o=[{src:g},{src:p},{src:u}];return(T,b)=>{const n=t("Title"),i=t("ImgSlider");return s(),r("div",null,[e(n,l(c(a)),null,16),h,e(i,{imgSliderList:o}),k])}}});export{w as __pageData,P as default}; diff --git a/assets/project_Markdown_log_index.md.c952cd03.js b/assets/project_Markdown_log_index.md.aab65e4a.js similarity index 97% rename from assets/project_Markdown_log_index.md.c952cd03.js rename to assets/project_Markdown_log_index.md.aab65e4a.js index 3a042750..2fddfbe3 100644 --- a/assets/project_Markdown_log_index.md.c952cd03.js +++ b/assets/project_Markdown_log_index.md.aab65e4a.js @@ -1 +1 @@ -import{_ as a,o as e,c as i,R as l}from"./chunks/framework.a304f0f7.js";const p=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/Markdown/log/index.md","lastUpdated":1682528161000}'),t={name:"project/Markdown/log/index.md"},o=l('

    更新日志

    v1.1.4

    2023-04-23

    • feat: 关于页增加插件开源地址

    v1.1.3

    2023-04-17

    • feat: 优化侧栏操作逻辑
    • fix: 修正XML标签代码高亮异常的问题
    • fix: 修正侧栏滚动条样式

    v1.1.2

    2023-04-12

    • feat: 调整合并图文导出关键字定义
    • feat: 新建文章自动在标题跟随日期
    • fix: 优化不同场景下编辑器聚焦行为

    v1.1.1

    2023-04-10

    • fix: 调整插件描述
    • fix: 修复关键字跳转问题
    • fix: 移除页面切换动效

    v1.1.0

    2023-04-09

    • feat: 支持通过关键字创建文章
    • feat: 支持从超级剪贴板多选合并创建文章
    • feat: 支持从文本/图片快速创建文章
    • feat: 支持批量导入Markdown文档
    • feat: 支持点击设置页关键字跳转
    • fix: 修复脚标点击跳转问题
    • fix: 修复部分场景下滚动条样式问题
    • fix: 修复部分场景下工具栏展示问题

    v1.0.1

    2023-04-05

    • feat: 支持插件多开 同时编辑/参考多个文档
    • fix: 修复图片被滚动条遮挡的问题
    • fix: 修复部分场景下标题栏与侧栏的bug

    v1.0.0

    2023-04-01

    • 插件发布
    ',22),r=[o];function d(c,n,h,s,f,u){return e(),i("div",null,r)}const _=a(t,[["render",d]]);export{p as __pageData,_ as default}; +import{_ as a,o as e,c as i,R as l}from"./chunks/framework.a304f0f7.js";const p=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/Markdown/log/index.md","lastUpdated":1682586876000}'),t={name:"project/Markdown/log/index.md"},o=l('

    更新日志

    v1.1.4

    2023-04-23

    • feat: 关于页增加插件开源地址

    v1.1.3

    2023-04-17

    • feat: 优化侧栏操作逻辑
    • fix: 修正XML标签代码高亮异常的问题
    • fix: 修正侧栏滚动条样式

    v1.1.2

    2023-04-12

    • feat: 调整合并图文导出关键字定义
    • feat: 新建文章自动在标题跟随日期
    • fix: 优化不同场景下编辑器聚焦行为

    v1.1.1

    2023-04-10

    • fix: 调整插件描述
    • fix: 修复关键字跳转问题
    • fix: 移除页面切换动效

    v1.1.0

    2023-04-09

    • feat: 支持通过关键字创建文章
    • feat: 支持从超级剪贴板多选合并创建文章
    • feat: 支持从文本/图片快速创建文章
    • feat: 支持批量导入Markdown文档
    • feat: 支持点击设置页关键字跳转
    • fix: 修复脚标点击跳转问题
    • fix: 修复部分场景下滚动条样式问题
    • fix: 修复部分场景下工具栏展示问题

    v1.0.1

    2023-04-05

    • feat: 支持插件多开 同时编辑/参考多个文档
    • fix: 修复图片被滚动条遮挡的问题
    • fix: 修复部分场景下标题栏与侧栏的bug

    v1.0.0

    2023-04-01

    • 插件发布
    ',22),r=[o];function d(c,n,h,s,f,u){return e(),i("div",null,r)}const _=a(t,[["render",d]]);export{p as __pageData,_ as default}; diff --git a/assets/project_Markdown_log_index.md.c952cd03.lean.js b/assets/project_Markdown_log_index.md.aab65e4a.lean.js similarity index 84% rename from assets/project_Markdown_log_index.md.c952cd03.lean.js rename to assets/project_Markdown_log_index.md.aab65e4a.lean.js index 1d29dacd..98cbeaa2 100644 --- a/assets/project_Markdown_log_index.md.c952cd03.lean.js +++ b/assets/project_Markdown_log_index.md.aab65e4a.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as i,R as l}from"./chunks/framework.a304f0f7.js";const p=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/Markdown/log/index.md","lastUpdated":1682528161000}'),t={name:"project/Markdown/log/index.md"},o=l("",22),r=[o];function d(c,n,h,s,f,u){return e(),i("div",null,r)}const _=a(t,[["render",d]]);export{p as __pageData,_ as default}; +import{_ as a,o as e,c as i,R as l}from"./chunks/framework.a304f0f7.js";const p=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/Markdown/log/index.md","lastUpdated":1682586876000}'),t={name:"project/Markdown/log/index.md"},o=l("",22),r=[o];function d(c,n,h,s,f,u){return e(),i("div",null,r)}const _=a(t,[["render",d]]);export{p as __pageData,_ as default}; diff --git a/assets/project_Markdown_shortcut_index.md.5d0eac56.js b/assets/project_Markdown_shortcut_index.md.0ea191a3.js similarity index 96% rename from assets/project_Markdown_shortcut_index.md.5d0eac56.js rename to assets/project_Markdown_shortcut_index.md.0ea191a3.js index 6b161ac2..02991f2a 100644 --- a/assets/project_Markdown_shortcut_index.md.5d0eac56.js +++ b/assets/project_Markdown_shortcut_index.md.0ea191a3.js @@ -1 +1 @@ -import{_ as e,o,c as d,R as c}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"快捷键一览","description":"","frontmatter":{},"headers":[],"relativePath":"project/Markdown/shortcut/index.md","lastUpdated":1682528161000}'),p={name:"project/Markdown/shortcut/index.md"},t=c('

    快捷键一览

    粗体 Cmd-B

    斜体 Cmd-I

    链接 Cmd-K

    图片 Shift-Cmd-I

    代码 Shift-Cmd-K

    代码块 Shift-Cmd-C

    无序列表 Shift-Cmd-U

    有序列表 Shift-Cmd-O

    Markdown 语法

    一级标题 # 标题

    二级标题 ## 标题

    三级标题 ### 标题

    粗体 **粗体文本**

    斜体 *斜体文本*

    引用 > 引用文本

    链接 [链接描述](url)

    图片 ![alt](url "图片描述")

    代码 代码

    代码块 ```编程语言↵

    无序列表 - 项目

    有序列表 1. 项目

    分割线 ---

    删除线 ~~文本~~

    任务列表 - [ ] 待办事项

    行内公式 $公式$

    块级公式 $$↵公式↵$$

    Mermaid图表 ```mermaid

    ',28),a=[t];function r(n,i,s,_,m,h){return o(),d("div",null,a)}const f=e(p,[["render",r]]);export{u as __pageData,f as default}; +import{_ as e,o,c as d,R as c}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"快捷键一览","description":"","frontmatter":{},"headers":[],"relativePath":"project/Markdown/shortcut/index.md","lastUpdated":1682586876000}'),p={name:"project/Markdown/shortcut/index.md"},t=c('

    快捷键一览

    粗体 Cmd-B

    斜体 Cmd-I

    链接 Cmd-K

    图片 Shift-Cmd-I

    代码 Shift-Cmd-K

    代码块 Shift-Cmd-C

    无序列表 Shift-Cmd-U

    有序列表 Shift-Cmd-O

    Markdown 语法

    一级标题 # 标题

    二级标题 ## 标题

    三级标题 ### 标题

    粗体 **粗体文本**

    斜体 *斜体文本*

    引用 > 引用文本

    链接 [链接描述](url)

    图片 ![alt](url "图片描述")

    代码 代码

    代码块 ```编程语言↵

    无序列表 - 项目

    有序列表 1. 项目

    分割线 ---

    删除线 ~~文本~~

    任务列表 - [ ] 待办事项

    行内公式 $公式$

    块级公式 $$↵公式↵$$

    Mermaid图表 ```mermaid

    ',28),a=[t];function r(n,i,s,_,m,h){return o(),d("div",null,a)}const f=e(p,[["render",r]]);export{u as __pageData,f as default}; diff --git a/assets/project_Markdown_shortcut_index.md.5d0eac56.lean.js b/assets/project_Markdown_shortcut_index.md.0ea191a3.lean.js similarity index 84% rename from assets/project_Markdown_shortcut_index.md.5d0eac56.lean.js rename to assets/project_Markdown_shortcut_index.md.0ea191a3.lean.js index 20c83a78..5094219a 100644 --- a/assets/project_Markdown_shortcut_index.md.5d0eac56.lean.js +++ b/assets/project_Markdown_shortcut_index.md.0ea191a3.lean.js @@ -1 +1 @@ -import{_ as e,o,c as d,R as c}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"快捷键一览","description":"","frontmatter":{},"headers":[],"relativePath":"project/Markdown/shortcut/index.md","lastUpdated":1682528161000}'),p={name:"project/Markdown/shortcut/index.md"},t=c("",28),a=[t];function r(n,i,s,_,m,h){return o(),d("div",null,a)}const f=e(p,[["render",r]]);export{u as __pageData,f as default}; +import{_ as e,o,c as d,R as c}from"./chunks/framework.a304f0f7.js";const u=JSON.parse('{"title":"快捷键一览","description":"","frontmatter":{},"headers":[],"relativePath":"project/Markdown/shortcut/index.md","lastUpdated":1682586876000}'),p={name:"project/Markdown/shortcut/index.md"},t=c("",28),a=[t];function r(n,i,s,_,m,h){return o(),d("div",null,a)}const f=e(p,[["render",r]]);export{u as __pageData,f as default}; diff --git a/assets/project_SmartWordBreak_index.md.dc5e4245.js b/assets/project_SmartWordBreak_index.md.59e896fe.js similarity index 96% rename from assets/project_SmartWordBreak_index.md.dc5e4245.js rename to assets/project_SmartWordBreak_index.md.59e896fe.js index 11a60a0d..20a4b49e 100644 --- a/assets/project_SmartWordBreak_index.md.dc5e4245.js +++ b/assets/project_SmartWordBreak_index.md.59e896fe.js @@ -1 +1 @@ -import{D as a,o as l,c as o,G as r,a4 as i,a5 as s,R as n}from"./chunks/framework.a304f0f7.js";const c="/assets/img1.c8b019e1.png",_="/assets/img2.e389ad4d.png",p="/assets/logo.b98f7a12.png",d=n('

    🔰 开始使用

    服务器不会保留处理的数据, 但请避免使用此功能处理敏感数据

    • ✅ 支持选中文本后通过超级面板直接进入分词
    • ✅ 支持直接读取剪贴板文本分词
    • ✅ 支持单选/拖拽/跨段落快速拖选
    • ✅ 支持一键合并复制/粘贴/翻译/搜索
    • ✅ 普通用户有每日免费额度 每日0:00重置
    • ✅ 优雅、迅速的动效与交互 适配深色模式
    • ✅ 可以通过插件内入口获取更多额度
    • ✅ 与超级剪贴板插件集成,一次购买 多处使用

    📚 安装方式

    ',8),b=JSON.parse('{"title":"超级分词","description":"","frontmatter":{"title":"超级分词","navbar":false},"headers":[],"relativePath":"project/SmartWordBreak/index.md","lastUpdated":1682528161000}'),m={name:"project/SmartWordBreak/index.md"},T=Object.assign(m,{setup(u){const t={subTitle:"✨ 智慧分词,快速提取文本关键词。",logo:p,linkList:[{content:"⭐ 插件发布页",target:"https://yuanliao.info/d/5722/29"},{content:"🌎 Q&A",target:"./statement/"},{content:"🚚 更新日志",target:"./log/"}]};return(g,h)=>{const e=a("Title");return l(),o("div",null,[r(e,i(s(t)),null,16),d])}}});export{b as __pageData,T as default}; +import{D as a,o as l,c as o,G as r,a4 as i,a5 as s,R as n}from"./chunks/framework.a304f0f7.js";const c="/assets/img1.c8b019e1.png",_="/assets/img2.e389ad4d.png",p="/assets/logo.b98f7a12.png",d=n('

    🔰 开始使用

    服务器不会保留处理的数据, 但请避免使用此功能处理敏感数据

    • ✅ 支持选中文本后通过超级面板直接进入分词
    • ✅ 支持直接读取剪贴板文本分词
    • ✅ 支持单选/拖拽/跨段落快速拖选
    • ✅ 支持一键合并复制/粘贴/翻译/搜索
    • ✅ 普通用户有每日免费额度 每日0:00重置
    • ✅ 优雅、迅速的动效与交互 适配深色模式
    • ✅ 可以通过插件内入口获取更多额度
    • ✅ 与超级剪贴板插件集成,一次购买 多处使用

    📚 安装方式

    ',8),b=JSON.parse('{"title":"超级分词","description":"","frontmatter":{"title":"超级分词","navbar":false},"headers":[],"relativePath":"project/SmartWordBreak/index.md","lastUpdated":1682586876000}'),m={name:"project/SmartWordBreak/index.md"},T=Object.assign(m,{setup(u){const t={subTitle:"✨ 智慧分词,快速提取文本关键词。",logo:p,linkList:[{content:"⭐ 插件发布页",target:"https://yuanliao.info/d/5722/29"},{content:"🌎 Q&A",target:"./statement/"},{content:"🚚 更新日志",target:"./log/"}]};return(g,h)=>{const e=a("Title");return l(),o("div",null,[r(e,i(s(t)),null,16),d])}}});export{b as __pageData,T as default}; diff --git a/assets/project_SmartWordBreak_index.md.dc5e4245.lean.js b/assets/project_SmartWordBreak_index.md.59e896fe.lean.js similarity index 92% rename from assets/project_SmartWordBreak_index.md.dc5e4245.lean.js rename to assets/project_SmartWordBreak_index.md.59e896fe.lean.js index 62814d92..cd1b9b0d 100644 --- a/assets/project_SmartWordBreak_index.md.dc5e4245.lean.js +++ b/assets/project_SmartWordBreak_index.md.59e896fe.lean.js @@ -1 +1 @@ -import{D as a,o as l,c as o,G as r,a4 as i,a5 as s,R as n}from"./chunks/framework.a304f0f7.js";const c="/assets/img1.c8b019e1.png",_="/assets/img2.e389ad4d.png",p="/assets/logo.b98f7a12.png",d=n("",8),b=JSON.parse('{"title":"超级分词","description":"","frontmatter":{"title":"超级分词","navbar":false},"headers":[],"relativePath":"project/SmartWordBreak/index.md","lastUpdated":1682528161000}'),m={name:"project/SmartWordBreak/index.md"},T=Object.assign(m,{setup(u){const t={subTitle:"✨ 智慧分词,快速提取文本关键词。",logo:p,linkList:[{content:"⭐ 插件发布页",target:"https://yuanliao.info/d/5722/29"},{content:"🌎 Q&A",target:"./statement/"},{content:"🚚 更新日志",target:"./log/"}]};return(g,h)=>{const e=a("Title");return l(),o("div",null,[r(e,i(s(t)),null,16),d])}}});export{b as __pageData,T as default}; +import{D as a,o as l,c as o,G as r,a4 as i,a5 as s,R as n}from"./chunks/framework.a304f0f7.js";const c="/assets/img1.c8b019e1.png",_="/assets/img2.e389ad4d.png",p="/assets/logo.b98f7a12.png",d=n("",8),b=JSON.parse('{"title":"超级分词","description":"","frontmatter":{"title":"超级分词","navbar":false},"headers":[],"relativePath":"project/SmartWordBreak/index.md","lastUpdated":1682586876000}'),m={name:"project/SmartWordBreak/index.md"},T=Object.assign(m,{setup(u){const t={subTitle:"✨ 智慧分词,快速提取文本关键词。",logo:p,linkList:[{content:"⭐ 插件发布页",target:"https://yuanliao.info/d/5722/29"},{content:"🌎 Q&A",target:"./statement/"},{content:"🚚 更新日志",target:"./log/"}]};return(g,h)=>{const e=a("Title");return l(),o("div",null,[r(e,i(s(t)),null,16),d])}}});export{b as __pageData,T as default}; diff --git a/assets/project_SmartWordBreak_log_index.md.07b2bd26.js b/assets/project_SmartWordBreak_log_index.md.dc4c02ce.js similarity index 97% rename from assets/project_SmartWordBreak_log_index.md.07b2bd26.js rename to assets/project_SmartWordBreak_log_index.md.dc4c02ce.js index 0304d25c..01ef3c28 100644 --- a/assets/project_SmartWordBreak_log_index.md.07b2bd26.js +++ b/assets/project_SmartWordBreak_log_index.md.dc4c02ce.js @@ -1 +1 @@ -import{_ as a,o as e,c as l,R as t}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/SmartWordBreak/log/index.md","lastUpdated":1682528161000}'),o={name:"project/SmartWordBreak/log/index.md"},i=t('

    更新日志

    v1.0.5

    2022-12-17

    • feat: 限免活动 每日免费额度调整为99 移除充值入口
    • fix: 修复历史记录显示问题

    v1.0.4

    2022-09-25

    • feat: 分词上限提高到了1000个字符
    • feat: token支持本地缓存

    v1.0.3

    2022-09-19

    • feat: 增加历史记录功能
    • fix: 移除了在tag外按下鼠标拖选的功能

    v1.0.2

    2022-09-13

    • feat: 支持跨段快速拖选
    • feat: 分词页添加了搜索粘贴功能
    • feat: 优化换行符在分词页的表现 支持包含换行符拖选

    v1.0.1

    2022-09-13

    • feat: 增加了拖选过程中的动效
    • feat: 调整了按钮样式与颜色
    • fix: 移除了碍事的toast

    v1.0.0

    2022-09-11

    • release: v1.0.0发布
    ',19),r=[i];function d(c,n,h,s,u,v){return e(),l("div",null,r)}const p=a(o,[["render",d]]);export{f as __pageData,p as default}; +import{_ as a,o as e,c as l,R as t}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/SmartWordBreak/log/index.md","lastUpdated":1682586876000}'),o={name:"project/SmartWordBreak/log/index.md"},i=t('

    更新日志

    v1.0.5

    2022-12-17

    • feat: 限免活动 每日免费额度调整为99 移除充值入口
    • fix: 修复历史记录显示问题

    v1.0.4

    2022-09-25

    • feat: 分词上限提高到了1000个字符
    • feat: token支持本地缓存

    v1.0.3

    2022-09-19

    • feat: 增加历史记录功能
    • fix: 移除了在tag外按下鼠标拖选的功能

    v1.0.2

    2022-09-13

    • feat: 支持跨段快速拖选
    • feat: 分词页添加了搜索粘贴功能
    • feat: 优化换行符在分词页的表现 支持包含换行符拖选

    v1.0.1

    2022-09-13

    • feat: 增加了拖选过程中的动效
    • feat: 调整了按钮样式与颜色
    • fix: 移除了碍事的toast

    v1.0.0

    2022-09-11

    • release: v1.0.0发布
    ',19),r=[i];function d(c,n,h,s,u,v){return e(),l("div",null,r)}const p=a(o,[["render",d]]);export{f as __pageData,p as default}; diff --git a/assets/project_SmartWordBreak_log_index.md.07b2bd26.lean.js b/assets/project_SmartWordBreak_log_index.md.dc4c02ce.lean.js similarity index 85% rename from assets/project_SmartWordBreak_log_index.md.07b2bd26.lean.js rename to assets/project_SmartWordBreak_log_index.md.dc4c02ce.lean.js index 3eb1cc83..3dfbc579 100644 --- a/assets/project_SmartWordBreak_log_index.md.07b2bd26.lean.js +++ b/assets/project_SmartWordBreak_log_index.md.dc4c02ce.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as l,R as t}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/SmartWordBreak/log/index.md","lastUpdated":1682528161000}'),o={name:"project/SmartWordBreak/log/index.md"},i=t("",19),r=[i];function d(c,n,h,s,u,v){return e(),l("div",null,r)}const p=a(o,[["render",d]]);export{f as __pageData,p as default}; +import{_ as a,o as e,c as l,R as t}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"更新日志","description":"","frontmatter":{},"headers":[],"relativePath":"project/SmartWordBreak/log/index.md","lastUpdated":1682586876000}'),o={name:"project/SmartWordBreak/log/index.md"},i=t("",19),r=[i];function d(c,n,h,s,u,v){return e(),l("div",null,r)}const p=a(o,[["render",d]]);export{f as __pageData,p as default}; diff --git a/assets/project_SmartWordBreak_statement_index.md.8c93a584.js b/assets/project_SmartWordBreak_statement_index.md.e7ffa382.js similarity index 96% rename from assets/project_SmartWordBreak_statement_index.md.8c93a584.js rename to assets/project_SmartWordBreak_statement_index.md.e7ffa382.js index 56894e29..5ae9aa6b 100644 --- a/assets/project_SmartWordBreak_statement_index.md.8c93a584.js +++ b/assets/project_SmartWordBreak_statement_index.md.e7ffa382.js @@ -1 +1 @@ -import{_ as e,o as a,c as o,R as t}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"Q&A","description":"","frontmatter":{"navbar":false},"headers":[],"relativePath":"project/SmartWordBreak/statement/index.md","lastUpdated":1682528161000}'),l={name:"project/SmartWordBreak/statement/index.md"},i=t('

    Q&A

    • Q: 为什么要收费
    • A: 此功能需要服务器资源, 而服务器资源有限, 故暂时只开放部分的免费资源给大家使用, 普通用户每日有部分免费额度, 额度每日0:00重置

    • Q: 我支付了,但是没有收到额度
    • A: 支付成功后,插件会自动获取最新用户信息,如果信息没有自动更新,请尝试退出插件后重新进入,如果仍未收到额度,请加入QQ群 769115389并联系我,我会尽快核实并为你解决

    • Q: 我之前赞赏过,可以有优惠吗
    • A: 2022年9月13日0:00前,赞赏过我的uTools用户,请发送:
      • UUID(超级分词插件内展示)
      • 赞赏金额
      • 赞赏时间
      • 支付截图
    • 到邮箱ZiuChen@outlook.com,邮箱主题为:申请超级分词赞赏额度,我将在核实后为你提供赞赏金额+15%的额度,感谢你的支持!💖。
    ',6),r=[i];function c(d,n,s,_,m,p){return a(),o("div",null,r)}const f=e(l,[["render",c]]);export{h as __pageData,f as default}; +import{_ as e,o as a,c as o,R as t}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"Q&A","description":"","frontmatter":{"navbar":false},"headers":[],"relativePath":"project/SmartWordBreak/statement/index.md","lastUpdated":1682586876000}'),l={name:"project/SmartWordBreak/statement/index.md"},i=t('

    Q&A

    • Q: 为什么要收费
    • A: 此功能需要服务器资源, 而服务器资源有限, 故暂时只开放部分的免费资源给大家使用, 普通用户每日有部分免费额度, 额度每日0:00重置

    • Q: 我支付了,但是没有收到额度
    • A: 支付成功后,插件会自动获取最新用户信息,如果信息没有自动更新,请尝试退出插件后重新进入,如果仍未收到额度,请加入QQ群 769115389并联系我,我会尽快核实并为你解决

    • Q: 我之前赞赏过,可以有优惠吗
    • A: 2022年9月13日0:00前,赞赏过我的uTools用户,请发送:
      • UUID(超级分词插件内展示)
      • 赞赏金额
      • 赞赏时间
      • 支付截图
    • 到邮箱ZiuChen@outlook.com,邮箱主题为:申请超级分词赞赏额度,我将在核实后为你提供赞赏金额+15%的额度,感谢你的支持!💖。
    ',6),r=[i];function c(d,n,s,_,m,p){return a(),o("div",null,r)}const f=e(l,[["render",c]]);export{h as __pageData,f as default}; diff --git a/assets/project_SmartWordBreak_statement_index.md.8c93a584.lean.js b/assets/project_SmartWordBreak_statement_index.md.e7ffa382.lean.js similarity index 85% rename from assets/project_SmartWordBreak_statement_index.md.8c93a584.lean.js rename to assets/project_SmartWordBreak_statement_index.md.e7ffa382.lean.js index f4e3debd..d285fd75 100644 --- a/assets/project_SmartWordBreak_statement_index.md.8c93a584.lean.js +++ b/assets/project_SmartWordBreak_statement_index.md.e7ffa382.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as o,R as t}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"Q&A","description":"","frontmatter":{"navbar":false},"headers":[],"relativePath":"project/SmartWordBreak/statement/index.md","lastUpdated":1682528161000}'),l={name:"project/SmartWordBreak/statement/index.md"},i=t("",6),r=[i];function c(d,n,s,_,m,p){return a(),o("div",null,r)}const f=e(l,[["render",c]]);export{h as __pageData,f as default}; +import{_ as e,o as a,c as o,R as t}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"Q&A","description":"","frontmatter":{"navbar":false},"headers":[],"relativePath":"project/SmartWordBreak/statement/index.md","lastUpdated":1682586876000}'),l={name:"project/SmartWordBreak/statement/index.md"},i=t("",6),r=[i];function c(d,n,s,_,m,p){return a(),o("div",null,r)}const f=e(l,[["render",c]]);export{h as __pageData,f as default}; diff --git a/assets/self_index.md.1f3b81e1.js b/assets/self_index.md.cd26854a.js similarity index 99% rename from assets/self_index.md.1f3b81e1.js rename to assets/self_index.md.cd26854a.js index d978943d..9efd62c2 100644 --- a/assets/self_index.md.1f3b81e1.js +++ b/assets/self_index.md.cd26854a.js @@ -1 +1 @@ -import{_ as e,o,c as a,R as t}from"./chunks/framework.a304f0f7.js";const l="/logo.png",i="/assets/2.7aeaaf23.png",s="/assets/1.136ffa4a.png",q=JSON.parse('{"title":"个人介绍","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"self/index.md","lastUpdated":1682528161000}'),r={name:"self/index.md"},d=t('

    个人介绍

    logo

    北京交通大学(BeijingJiaoTong University)电子信息工程学院本科在读

    • 熟悉ES6特性, 有前端领域开发经验, 能独立完成基础的前端开发工作;
    • 熟练使用Vue框架及相关工具开发应用, 阅读过部分Vue源码, 了解Vue响应式实现原理;
    • 掌握NodeJS基本使用, 能够基于NodeJS编写开发工具或搭建Web服务;
    • 了解Webpack基本配置和使用, 有Webpack Vite等前端工程化工具使用经验;
    • 了解Typescript, 有Typescript的项目使用经历;
    • 良好的Git操作, 清晰的Commit提交, 保证代码质量;
    • 对前端学习抱有热情, 有良好的学习能力, 能够快速学习掌握新知识;

    技术栈

    前端技术

    Vue Router Vuex Pinia Element Plus Echart WangEditor

    Sass/Less Axios Ajax ...

    后端技术

    相关技能

    文档处理:

    Microsoft Word Microsoft PowerPoint Microsoft Excel

    平面设计:

    Adobe PhotoShop Adobe Lightroom

    影音制作:

    DaVinci Resolve Studio Adobe Premiere Adobe After Effects

    专业软件:

    MATLAB ICEDA Multisim Keil uVision5

    获得奖项

    Microsoft Edge 浏览器开拓者大赛 开拓之星

    稀土掘金2022编程挑战赛 三等奖

    入选“扬帆计划·中央和国家机关大学生实习”

    100周年现场志愿者

    优秀学生干部、优秀共青团员

    社会工作优秀奖学金

    相关链接

    Github哔哩哔哩稀土掘金CSDNGitee

    ',35),c=[d];function p(h,g,m,n,f,u){return o(),a("div",null,c)}const _=e(r,[["render",p]]);export{q as __pageData,_ as default}; +import{_ as e,o,c as a,R as t}from"./chunks/framework.a304f0f7.js";const l="/logo.png",i="/assets/2.7aeaaf23.png",s="/assets/1.136ffa4a.png",q=JSON.parse('{"title":"个人介绍","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"self/index.md","lastUpdated":1682586876000}'),r={name:"self/index.md"},d=t('

    个人介绍

    logo

    北京交通大学(BeijingJiaoTong University)电子信息工程学院本科在读

    • 熟悉ES6特性, 有前端领域开发经验, 能独立完成基础的前端开发工作;
    • 熟练使用Vue框架及相关工具开发应用, 阅读过部分Vue源码, 了解Vue响应式实现原理;
    • 掌握NodeJS基本使用, 能够基于NodeJS编写开发工具或搭建Web服务;
    • 了解Webpack基本配置和使用, 有Webpack Vite等前端工程化工具使用经验;
    • 了解Typescript, 有Typescript的项目使用经历;
    • 良好的Git操作, 清晰的Commit提交, 保证代码质量;
    • 对前端学习抱有热情, 有良好的学习能力, 能够快速学习掌握新知识;

    技术栈

    前端技术

    Vue Router Vuex Pinia Element Plus Echart WangEditor

    Sass/Less Axios Ajax ...

    后端技术

    相关技能

    文档处理:

    Microsoft Word Microsoft PowerPoint Microsoft Excel

    平面设计:

    Adobe PhotoShop Adobe Lightroom

    影音制作:

    DaVinci Resolve Studio Adobe Premiere Adobe After Effects

    专业软件:

    MATLAB ICEDA Multisim Keil uVision5

    获得奖项

    Microsoft Edge 浏览器开拓者大赛 开拓之星

    稀土掘金2022编程挑战赛 三等奖

    入选“扬帆计划·中央和国家机关大学生实习”

    100周年现场志愿者

    优秀学生干部、优秀共青团员

    社会工作优秀奖学金

    相关链接

    Github哔哩哔哩稀土掘金CSDNGitee

    ',35),c=[d];function p(h,g,m,n,f,u){return o(),a("div",null,c)}const _=e(r,[["render",p]]);export{q as __pageData,_ as default}; diff --git a/assets/self_index.md.1f3b81e1.lean.js b/assets/self_index.md.cd26854a.lean.js similarity index 86% rename from assets/self_index.md.1f3b81e1.lean.js rename to assets/self_index.md.cd26854a.lean.js index 6daebdd1..5f5846f7 100644 --- a/assets/self_index.md.1f3b81e1.lean.js +++ b/assets/self_index.md.cd26854a.lean.js @@ -1 +1 @@ -import{_ as e,o,c as a,R as t}from"./chunks/framework.a304f0f7.js";const l="/logo.png",i="/assets/2.7aeaaf23.png",s="/assets/1.136ffa4a.png",q=JSON.parse('{"title":"个人介绍","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"self/index.md","lastUpdated":1682528161000}'),r={name:"self/index.md"},d=t("",35),c=[d];function p(h,g,m,n,f,u){return o(),a("div",null,c)}const _=e(r,[["render",p]]);export{q as __pageData,_ as default}; +import{_ as e,o,c as a,R as t}from"./chunks/framework.a304f0f7.js";const l="/logo.png",i="/assets/2.7aeaaf23.png",s="/assets/1.136ffa4a.png",q=JSON.parse('{"title":"个人介绍","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"self/index.md","lastUpdated":1682586876000}'),r={name:"self/index.md"},d=t("",35),c=[d];function p(h,g,m,n,f,u){return o(),a("div",null,c)}const _=e(r,[["render",p]]);export{q as __pageData,_ as default}; diff --git a/assets/works_contribution.md.c63c44a1.js b/assets/works_contribution.md.fe233dc7.js similarity index 95% rename from assets/works_contribution.md.c63c44a1.js rename to assets/works_contribution.md.fe233dc7.js index ca306edc..71d208ba 100644 --- a/assets/works_contribution.md.c63c44a1.js +++ b/assets/works_contribution.md.fe233dc7.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,R as r}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"社区贡献","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"works/contribution.md","lastUpdated":1682528161000}'),o={name:"works/contribution.md"},n=r('

    社区贡献

    稀土掘金助手

    稀土掘金助手: 腾讯云函数部署

    源代码

    B站粉丝牌助手文档

    B站粉丝牌助手文档

    源代码Demo

    ',7),i=[n];function s(h,l,c,_,d,p){return a(),t("div",null,i)}const b=e(o,[["render",s]]);export{f as __pageData,b as default}; +import{_ as e,o as a,c as t,R as r}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"社区贡献","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"works/contribution.md","lastUpdated":1682586876000}'),o={name:"works/contribution.md"},n=r('

    社区贡献

    稀土掘金助手

    稀土掘金助手: 腾讯云函数部署

    源代码

    B站粉丝牌助手文档

    B站粉丝牌助手文档

    源代码Demo

    ',7),i=[n];function s(h,l,c,_,d,p){return a(),t("div",null,i)}const b=e(o,[["render",s]]);export{f as __pageData,b as default}; diff --git a/assets/works_contribution.md.c63c44a1.lean.js b/assets/works_contribution.md.fe233dc7.lean.js similarity index 84% rename from assets/works_contribution.md.c63c44a1.lean.js rename to assets/works_contribution.md.fe233dc7.lean.js index f7e91ad9..c74b1a89 100644 --- a/assets/works_contribution.md.c63c44a1.lean.js +++ b/assets/works_contribution.md.fe233dc7.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,R as r}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"社区贡献","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"works/contribution.md","lastUpdated":1682528161000}'),o={name:"works/contribution.md"},n=r("",7),i=[n];function s(h,l,c,_,d,p){return a(),t("div",null,i)}const b=e(o,[["render",s]]);export{f as __pageData,b as default}; +import{_ as e,o as a,c as t,R as r}from"./chunks/framework.a304f0f7.js";const f=JSON.parse('{"title":"社区贡献","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"works/contribution.md","lastUpdated":1682586876000}'),o={name:"works/contribution.md"},n=r("",7),i=[n];function s(h,l,c,_,d,p){return a(),t("div",null,i)}const b=e(o,[["render",s]]);export{f as __pageData,b as default}; diff --git a/assets/works_opensource.md.aad189b9.js b/assets/works_opensource.md.6dc8588c.js similarity index 98% rename from assets/works_opensource.md.aad189b9.js rename to assets/works_opensource.md.6dc8588c.js index ea88ca77..3419ff25 100644 --- a/assets/works_opensource.md.aad189b9.js +++ b/assets/works_opensource.md.6dc8588c.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,R as t}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"个人作品","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"works/opensource.md","lastUpdated":1682528161000}'),o={name:"works/opensource.md"},i=t('

    个人作品

    Vue3+TypeScript后台管理系统

    Vue3+TypeScript后台管理系统

    源代码

    Typein 效率工具集

    Typein 是一个运行在现代浏览器上的拓展插件,它可以帮助你在保持专注的情况下,高效完成各种操作。

    源代码

    宣传视频

    Microsoft Edge Add-on Store

    A-SOUL浏览器宠物

    在浏览器里养一只A-Soul成员当宠物

    源代码

    宣传视频

    北京交通大学课程平台功能增强

    北京交通大学课程平台功能增强脚本,实现信息聚合,附件上传,让你高效处理课程信息。

    源代码

    超级剪贴板

    uTools插件,一款强大的剪贴板管理工具。基于Vue3构建

    源代码

    主页

    超级粘贴

    uTools插件,将剪切板内容直接粘贴为文件。功能基于NodeJS

    源代码

    超级分词

    uTools插件,前台使用Vue3+ElementPlus构建,后台基于Express封装结巴分词并暴露API接口

    超级连点器

    uTools插件,界面使用Vue3+AntDesign构建,功能基于Worker

    PopNotify

    仿 Element UI 的通知卡片。

    源代码

    Demo

    UserScripts

    用户脚本合集,生产力工具、效率工具、自动化脚本

    源代码

    ',34),h=[i];function p(n,l,s,c,u,d){return a(),r("div",null,h)}const m=e(o,[["render",p]]);export{b as __pageData,m as default}; +import{_ as e,o as a,c as r,R as t}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"个人作品","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"works/opensource.md","lastUpdated":1682586876000}'),o={name:"works/opensource.md"},i=t('

    个人作品

    Vue3+TypeScript后台管理系统

    Vue3+TypeScript后台管理系统

    源代码

    Typein 效率工具集

    Typein 是一个运行在现代浏览器上的拓展插件,它可以帮助你在保持专注的情况下,高效完成各种操作。

    源代码

    宣传视频

    Microsoft Edge Add-on Store

    A-SOUL浏览器宠物

    在浏览器里养一只A-Soul成员当宠物

    源代码

    宣传视频

    北京交通大学课程平台功能增强

    北京交通大学课程平台功能增强脚本,实现信息聚合,附件上传,让你高效处理课程信息。

    源代码

    超级剪贴板

    uTools插件,一款强大的剪贴板管理工具。基于Vue3构建

    源代码

    主页

    超级粘贴

    uTools插件,将剪切板内容直接粘贴为文件。功能基于NodeJS

    源代码

    超级分词

    uTools插件,前台使用Vue3+ElementPlus构建,后台基于Express封装结巴分词并暴露API接口

    超级连点器

    uTools插件,界面使用Vue3+AntDesign构建,功能基于Worker

    PopNotify

    仿 Element UI 的通知卡片。

    源代码

    Demo

    UserScripts

    用户脚本合集,生产力工具、效率工具、自动化脚本

    源代码

    ',34),h=[i];function p(n,l,s,c,u,d){return a(),r("div",null,h)}const m=e(o,[["render",p]]);export{b as __pageData,m as default}; diff --git a/assets/works_opensource.md.aad189b9.lean.js b/assets/works_opensource.md.6dc8588c.lean.js similarity index 84% rename from assets/works_opensource.md.aad189b9.lean.js rename to assets/works_opensource.md.6dc8588c.lean.js index e5001ed9..24a08a46 100644 --- a/assets/works_opensource.md.aad189b9.lean.js +++ b/assets/works_opensource.md.6dc8588c.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,R as t}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"个人作品","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"works/opensource.md","lastUpdated":1682528161000}'),o={name:"works/opensource.md"},i=t("",34),h=[i];function p(n,l,s,c,u,d){return a(),r("div",null,h)}const m=e(o,[["render",p]]);export{b as __pageData,m as default}; +import{_ as e,o as a,c as r,R as t}from"./chunks/framework.a304f0f7.js";const b=JSON.parse('{"title":"个人作品","description":"","frontmatter":{"editLink":false},"headers":[],"relativePath":"works/opensource.md","lastUpdated":1682586876000}'),o={name:"works/opensource.md"},i=t("",34),h=[i];function p(n,l,s,c,u,d){return a(),r("div",null,h)}const m=e(o,[["render",p]]);export{b as __pageData,m as default}; diff --git a/hashmap.json b/hashmap.json index 1cc36091..bc6cdb5c 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"article_深入理解浏览器缓存机制.md":"b2eac69d","article_深入理解浏览器运行原理.md":"811243b5","article_【快手】深入理解前端面试题.md":"fd1db545","article_【字节跳动】前端面试题总结.md":"da9d1eec","article_浅析defineproperty与proxy实现的双向绑定.md":"981b1858","article_彻底搞懂对象的数据属性描述符、存储属性描述符.md":"93ffc2d7","article_【用友金融】前端面试题总结.md":"f9565228","article_一文读懂函数中this指向问题.md":"a2e876e0","article_一文读懂伪类与伪元素.md":"b7e97075","article_一文读懂事件冒泡与事件捕获.md":"aa1902b6","article_深入理解proxy与reflect.md":"7dd623e9","article_【2023】青训营 - 前端练习题汇总解析.md":"751c7e25","index.md":"92aa3f05","article_深入javascript数据类型.md":"e8c468fa","note_javascript.md":"5dbfafaa","note_mysql.md":"8ab2ead8","note_ssr.md":"9e639932","project_clipboardmanager_guide_index.md":"89147de8","project_clipboardmanager_index.md":"2a3cb14f","project_clipboardmanager_log_index.md":"714cf9de","project_clipboardmanager_statement_index.md":"95f4a6ec","project_jsrunner_index.md":"47b54e96","project_jsrunner_log_index.md":"cc34efe6","project_markdown_index.md":"69c4ad3a","project_markdown_log_index.md":"c952cd03","project_markdown_shortcut_index.md":"5d0eac56","project_smartwordbreak_index.md":"dc5e4245","project_smartwordbreak_log_index.md":"07b2bd26","project_smartwordbreak_statement_index.md":"8c93a584","self_index.md":"1f3b81e1","works_contribution.md":"c63c44a1","article_从0实现一个年度报告.md":"cfdfead5","project_clipboardmanager_vip_index.md":"453f0553","note_front-end engineering.md":"ca61b8ad","article_深入vue3源码,看看vue.use后究竟发生了什么?.md":"4b28aa3d","works_opensource.md":"aad189b9","note_css.md":"945cb9b3","note_javascriptenhanced.md":"63433dba","note_react.md":"0514d155"} +{"note_ssr.md":"f248ec9c","project_clipboardmanager_guide_index.md":"1455cfc8","project_clipboardmanager_log_index.md":"e245d280","project_clipboardmanager_statement_index.md":"01b45ff7","project_clipboardmanager_vip_index.md":"5375ae9a","project_jsrunner_index.md":"97c8b761","project_jsrunner_log_index.md":"5fa1e94d","project_markdown_index.md":"316f1afd","project_markdown_log_index.md":"aab65e4a","project_markdown_shortcut_index.md":"0ea191a3","project_smartwordbreak_index.md":"59e896fe","project_smartwordbreak_log_index.md":"dc4c02ce","project_smartwordbreak_statement_index.md":"e7ffa382","self_index.md":"cd26854a","works_contribution.md":"fe233dc7","works_opensource.md":"6dc8588c","project_clipboardmanager_index.md":"e0db2213","article_浅析defineproperty与proxy实现的双向绑定.md":"627b5302","article_深入vue3源码,看看vue.use后究竟发生了什么?.md":"84a383ed","article_深入理解proxy与reflect.md":"5b8c49e4","article_深入理解浏览器缓存机制.md":"44e94909","note_javascript.md":"d5705bda","article_深入理解浏览器运行原理.md":"4ae70423","index.md":"a39be407","note_front-end engineering.md":"c6edf7d1","article_深入javascript数据类型.md":"986f85d7","article_一文读懂函数中this指向问题.md":"1ffeb261","article_【用友金融】前端面试题总结.md":"9fca7220","article_一文读懂事件冒泡与事件捕获.md":"055b150b","article_一文读懂伪类与伪元素.md":"c653a705","article_【快手】深入理解前端面试题.md":"7657b1d3","article_从0实现一个年度报告.md":"cbf4ba30","article_【字节跳动】前端面试题总结.md":"1f4ca571","article_彻底搞懂对象的数据属性描述符、存储属性描述符.md":"590e5575","article_【2023】青训营 - 前端练习题汇总解析.md":"c9420cf3","note_mysql.md":"0b6704a4","note_css.md":"6a7eedec","note_javascriptenhanced.md":"0ff38e63","note_react.md":"eeaf8749"} diff --git a/index.html b/index.html index b2277e8a..93ea7af3 100644 --- a/index.html +++ b/index.html @@ -10,13 +10,13 @@ - +
    Skip to content

    ZiuChen

    无限进步.

    Infinite Progress...

    📋

    Clipboard Manager

    A Powerful clipboard management tool

    ✍🏻

    Super Markdown

    Powerful Markdown editor

    🚗

    JS Runner

    Run JavaScript dynamicly in Browser/Node.js

    🔑

    Bytemd Plugin

    Bytemd Plugin Library

    🍬

    ASOUL Browser Pet

    Keep an A-SOUL member as a pet in your browser

    🔧

    Typein

    Typein text, quickly perform browser operations

    Released under the MIT License.

    - diff --git a/note/CSS.html b/note/CSS.html index a23ff1de..2a469217 100644 --- a/note/CSS.html +++ b/note/CSS.html @@ -10,7 +10,7 @@ - + @@ -416,8 +416,8 @@ <source src="./assets/video.webm" /> </video>

    audio标签

    html
    <audio src="./assets/music.mp4" controls></audio>

    input元素

    扩展属性

    • placeholder 占位文本
    • multiple 多选标签
    • autofocus 自动聚焦输入框

    扩展type属性的值

    • date
    • time
    • number
    • tel
    • color
    • email
    • ···

    全局新增属性 data-*

    在HTML5中,新增了一种全局属性的格式data-* 用于自定义数据属性

    • data设置的属性可以在JavaScript的DOM操作中通过dataset轻松获取到
    • 通常用于HTML和JavaScript数据之间的传递
    • 在小程序中,就是通过data-*来传递数据的
    html
    <div class="item" age="18" data-name="ziu" data-age="18"></div>
    js
    const el = document.querySelector('.item')
     console.log(el.dataset.age) // 18
    -console.log(el.dataset.name) // 'ziu'

    Released under the MIT License.

    - diff --git a/note/Front-end Engineering.html b/note/Front-end Engineering.html index 0324d7e6..babd768a 100644 --- a/note/Front-end Engineering.html +++ b/note/Front-end Engineering.html @@ -10,7 +10,7 @@ - + @@ -64,7 +64,7 @@ })() // moduleB.js -console.log(moduleA.name) // 在其他模块中调用

    CommonJS

    CommonJS是一种规范,当初命名为ServerJS,旨在浏览器以外的地方使用,后为体现其广泛性,改名为CommonJS,简称CJS

    规范 是用来指导 实现的

    • Node 是CommonJS在服务端的代表实现
    • Browserify 是CommonJS在浏览器中的一种实现 (正在被淘汰)
    • WebPack 打包工具具备支持CommonJS的支持和转换

    所以,Node.js对CommonJS进行了支持和实现,让JavaScript在Node上运行时可以实现模块化开发

    • 每个.js文件都是一个单独的模块
    • 每个模块中都包含变量exports module.exports require
    js
    // env.js
    +console.log(moduleA.name) // 在其他模块中调用

    CommonJS

    CommonJS是一种规范,当初命名为ServerJS,旨在浏览器以外的地方使用,后为体现其广泛性,改名为CommonJS,简称CJS

    规范 是用来指导 实现的

    • Node 是CommonJS在服务端的代表实现
    • Browserify 是CommonJS在浏览器中的一种实现 (正在被淘汰)
    • WebPack 打包工具具备支持CommonJS的支持和转换

    所以,Node.js对CommonJS进行了支持和实现,让JavaScript在Node上运行时可以实现模块化开发

    • 每个.js文件都是一个单独的模块
    • 每个模块中都包含变量exports module.exports require
    js
    // env.js
     exports.name = 'Ziu'
     exports.age = 18
    js
    // utils.js
     module.exports = {
    @@ -79,7 +79,7 @@
     sum(1, 2) // 3
     
     const { name, age } = require('env.js')
    -console.log(name, age) // Ziu 18

    exports的本质

    exportsrequire在Node中的本质

    • exports是一个对象,我们可以在这个对象中添加很多属性,添加的属性则会被导出
      • 在没有向该对象添加任何属性之前,它是一个空对象
    • 当通过require导入时:const env = require('env.js')
      • env这个变量等于env.js中的exports对象
      • 本质上是envexports对象的引用赋值
      • { id: '...', exports: { ... }, loaded: true, ... }
    • 后续即使再次执行require导入模块,模块中的代码也不会重新执行(module.loaded属性)
      • 当从模块中取值时,会从已经加载的exports对象缓存上取值
    js
    // utils.js
    +console.log(name, age) // Ziu 18

    exports的本质

    exportsrequire在Node中的本质

    • exports是一个对象,我们可以在这个对象中添加很多属性,添加的属性则会被导出
      • 在没有向该对象添加任何属性之前,它是一个空对象
    • 当通过require导入时:const env = require('env.js')
      • env这个变量等于env.js中的exports对象
      • 本质上是envexports对象的引用赋值
      • { id: '...', exports: { ... }, loaded: true, ... }
    • 后续即使再次执行require导入模块,模块中的代码也不会重新执行(module.loaded属性)
      • 当从模块中取值时,会从已经加载的exports对象缓存上取值
    js
    // utils.js
     exports.a = 0
     
     // 1s后修改a值
    @@ -104,14 +104,14 @@
     module.exports = {
       name,
       run
    -}

    二者的区别

    既然如此,为什么还要存在exports这个概念呢?

    • 在CommonJS中是没有module.exports的概念的
    • 为了实现模块的导出,Node.js使用的是Module类,每一个模块都是Module的实例,也就是module
    • 所以在Node.js中真正用于导出的并不是exports,而是module.exports
    • module对象中的exports属性是exports对象的一个引用
      • module.exports === exports === utils

    如果module.exports不再引用exports对象了,修改exports对象也就没有意义了

    js
    // utils.js
    +}

    二者的区别

    既然如此,为什么还要存在exports这个概念呢?

    • 在CommonJS中是没有module.exports的概念的
    • 为了实现模块的导出,Node.js使用的是Module类,每一个模块都是Module的实例,也就是module
    • 所以在Node.js中真正用于导出的并不是exports,而是module.exports
    • module对象中的exports属性是exports对象的一个引用
      • module.exports === exports === utils

    如果module.exports不再引用exports对象了,修改exports对象也就没有意义了

    js
    // utils.js
     module.exports = {
       name: 'Ziu'
     }
     exports.age = 18
    js
    // index.js
     const utils = require('utils.js')
     console.log(utils.name) // Ziu
    -console.log(utils.age) // undefined

    当使用module.exports = { ... }后,模块中原有的exports不再被导入识别,导入的内容将变为module.exports指定的对象内容

    require的本质

    require是一个函数,可以帮助我们导入一个文件(模块)中导出的对象

    • 为什么可以省略掉.js后缀,直接使用require('./utils')
    • 为什么可以省略掉index.js,直接使用require('./tools')导入tools/index.js

    这涉及到require在匹配路径后的查找规则:

    分为三种情况:内置模块、自定义路径、包名

    • 导入Node.js内置的模块,如const path = require('path')
      • 直接返回该内置模块 并停止后续的查找
    • 根据路径导入自定义的模块,如const utils = require('./{filename}')
      • 按照路径寻找该模块./ ../ /
      • 如果指定了后缀名,则按照后缀名查找
      • 如果未指定后缀名,则:
        1. 直接查找该文件
        2. 查找{filename}.js文件
        3. 查找{filename}.json文件
        4. 查找{filename}.node文件
      • 如果按照上述方式没找到文件,则{filename}作为路径继续查找
      • 查找目录下的index文件 {filename}/index
        1. 查找{filename}/index.js文件
        2. ··· ···
      • 没找到:报错Cannot find module 'xxx'
    • 包名,如const lodash = require('lodash')
      • 到项目根目录的node_modules中查找
      • node_modules/{package_name}/index.js
      • 当前项目目录的node_modules找不到则继续向上查找,直到查找到根目录的node_modules

    模块的加载过程

    • 模块在被第一次引入时,模块中的JS代码会被运行一次
      • 代码执行顺序与require的位置相关
    • 模块如果被多次引入,会被缓存,最终只加载一次
      • 这是因为每个模块对象module上都有一个属性loaded
      • loaded === false表示该模块尚未被加载
      • 第二次被require引入时会检查该属性是否为true
    • 如果有循环引用,加载顺序如何?
      • 数据结构:图结构(graph)遍历时有深度优先搜索(DFS)、广度优先搜索(BFS)两种算法
      • Node采用的是深度优先算法

    CommonJS的缺点

    • 加载模块是同步加载的
      • 只有等到对应的模块加载完毕,当前模块中的内容才能被执行
      • 当然,在服务器中加载JS文件都是本地文件,加载速度非常快,不会受影响
    • 但是在浏览器中使用CommonJS
      • 需要先从服务器下载JS文件,后加载运行
      • 阻塞JS执行 阻塞页面加载
    • 在WebPack中使用CommonJS
      • CommonJS会被WebPack解析
      • 将CommonJS代码转化为bundle 浏览器可以直接运行

    ESModule

    • ES6 模块采用编译时加载,使得编译时就能确定模块的依赖关系,有助于静态优化
    • CommonJS模块在运行时加载,且必须借助对象加载模块内容

    exportimport用法概览

    ESModule借助exportimport导入导出内容,需要注意的是导入导出的并不是对象

    export定义的是当前模块导出的接口import可以导入来自其他不同模块的接口

    • export default可以设置默认导出对象
    • export { ... }可以统一导出多个内容
    • exportimport都可以使用as关键字重命名导出/导入的接口
    • import * from 'xxx' export * from 'xxx'批量导入/导出
    js
    // utils.js
    +console.log(utils.age) // undefined

    当使用module.exports = { ... }后,模块中原有的exports不再被导入识别,导入的内容将变为module.exports指定的对象内容

    require的本质

    require是一个函数,可以帮助我们导入一个文件(模块)中导出的对象

    • 为什么可以省略掉.js后缀,直接使用require('./utils')
    • 为什么可以省略掉index.js,直接使用require('./tools')导入tools/index.js

    这涉及到require在匹配路径后的查找规则:

    分为三种情况:内置模块、自定义路径、包名

    • 导入Node.js内置的模块,如const path = require('path')
      • 直接返回该内置模块 并停止后续的查找
    • 根据路径导入自定义的模块,如const utils = require('./{filename}')
      • 按照路径寻找该模块./ ../ /
      • 如果指定了后缀名,则按照后缀名查找
      • 如果未指定后缀名,则:
        1. 直接查找该文件
        2. 查找{filename}.js文件
        3. 查找{filename}.json文件
        4. 查找{filename}.node文件
      • 如果按照上述方式没找到文件,则{filename}作为路径继续查找
      • 查找目录下的index文件 {filename}/index
        1. 查找{filename}/index.js文件
        2. ··· ···
      • 没找到:报错Cannot find module 'xxx'
    • 包名,如const lodash = require('lodash')
      • 到项目根目录的node_modules中查找
      • node_modules/{package_name}/index.js
      • 当前项目目录的node_modules找不到则继续向上查找,直到查找到根目录的node_modules

    模块的加载过程

    • 模块在被第一次引入时,模块中的JS代码会被运行一次
      • 代码执行顺序与require的位置相关
    • 模块如果被多次引入,会被缓存,最终只加载一次
      • 这是因为每个模块对象module上都有一个属性loaded
      • loaded === false表示该模块尚未被加载
      • 第二次被require引入时会检查该属性是否为true
    • 如果有循环引用,加载顺序如何?
      • 数据结构:图结构(graph)遍历时有深度优先搜索(DFS)、广度优先搜索(BFS)两种算法
      • Node采用的是深度优先算法

    CommonJS的缺点

    • 加载模块是同步加载的
      • 只有等到对应的模块加载完毕,当前模块中的内容才能被执行
      • 当然,在服务器中加载JS文件都是本地文件,加载速度非常快,不会受影响
    • 但是在浏览器中使用CommonJS
      • 需要先从服务器下载JS文件,后加载运行
      • 阻塞JS执行 阻塞页面加载
    • 在WebPack中使用CommonJS
      • CommonJS会被WebPack解析
      • 将CommonJS代码转化为bundle 浏览器可以直接运行

    ESModule

    • ES6 模块采用编译时加载,使得编译时就能确定模块的依赖关系,有助于静态优化
    • CommonJS模块在运行时加载,且必须借助对象加载模块内容

    exportimport用法概览

    ESModule借助exportimport导入导出内容,需要注意的是导入导出的并不是对象

    export定义的是当前模块导出的接口import可以导入来自其他不同模块的接口

    • export default可以设置默认导出对象
    • export { ... }可以统一导出多个内容
    • exportimport都可以使用as关键字重命名导出/导入的接口
    • import * from 'xxx' export * from 'xxx'批量导入/导出
    js
    // utils.js
     export function sum(a, b) {
       return a + b
     }
    @@ -131,7 +131,7 @@
     
     sum(1, 2) // 3
     sub(2, 3) // -1
    -log(name, age, ENV_VARIABLE) // 'Ziu' 18 'Hello, World!'

    需要注意的是,在浏览器中要使用ESModule,需要为<script>标签添加module标记:

    <script src="index.js" type="module"></script>

    • 当浏览器解析到type="module"的JS代码后,会分析模块中导入的ESModule模块
    • 每导入一个ESModule模块,浏览器都会发起一个HTTP请求去加载它
    • 在本地运行时加载不同协议头的文件会遇到跨域问题,需要开启本地Web服务器

    另外,exportimport必须位于模块的顶层,如果位于作用域内会报错,因为这就无法对代码进行静态分析优化了

    export详解

    export有两种导出方式:

    • 命名导出 export const name = 'Ziu' export { v1, v2 } export * from 'xxx'
      • 导出时需要指定名字
      • 导入时也需要知道对应的名字
    • 默认导出 export default AGE = 18
      • 在从其他位置导入时需要为此默认导出指定新的名字
      • 给用户方便:不必阅读文档就可以加载模块

    值的动态绑定

    • ESModule模块通过export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值
    • CommonJS模块输出的是值的缓存,不存在动态更新

    我们援引之前介绍CJS时的案例,将后缀名改为mjs即可在Node中运行ESModule模块代码

    初始获得的a值为0,经过1s后,在utils.mjs中修改了a的值,这时导入utils.mjs模块的其他模块可以获取到a最新的值

    js
    // utils.mjs
    +log(name, age, ENV_VARIABLE) // 'Ziu' 18 'Hello, World!'

    需要注意的是,在浏览器中要使用ESModule,需要为<script>标签添加module标记:

    <script src="index.js" type="module"></script>

    • 当浏览器解析到type="module"的JS代码后,会分析模块中导入的ESModule模块
    • 每导入一个ESModule模块,浏览器都会发起一个HTTP请求去加载它
    • 在本地运行时加载不同协议头的文件会遇到跨域问题,需要开启本地Web服务器

    另外,exportimport必须位于模块的顶层,如果位于作用域内会报错,因为这就无法对代码进行静态分析优化了

    export详解

    export有两种导出方式:

    • 命名导出 export const name = 'Ziu' export { v1, v2 } export * from 'xxx'
      • 导出时需要指定名字
      • 导入时也需要知道对应的名字
    • 默认导出 export default AGE = 18
      • 在从其他位置导入时需要为此默认导出指定新的名字
      • 给用户方便:不必阅读文档就可以加载模块

    值的动态绑定

    • ESModule模块通过export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值
    • CommonJS模块输出的是值的缓存,不存在动态更新

    我们援引之前介绍CJS时的案例,将后缀名改为mjs即可在Node中运行ESModule模块代码

    初始获得的a值为0,经过1s后,在utils.mjs中修改了a的值,这时导入utils.mjs模块的其他模块可以获取到a最新的值

    js
    // utils.mjs
     export let a = 0
     
     // 1s后修改a值
    @@ -169,7 +169,7 @@
     } else {
       import('light.js').then(() => ...)
     }

    传入动态值

    js
    let moduleName = () => ['Home', 'History', 'User'][0]
    -import(`./${moduleName()}.js`)

    import.meta

    ES2020引入了import.meta,它仅能在模块内部使用,包含一些模块自身的信息,即模块元信息

    • import.meta.url 返回当前模块的URL路径
      • 浏览器加载ESModule都是通过HTTP发起请求
        • 例如当前模块为fetchData.js,要在模块内引入一个名为data.json的数据:
        • import( new URL('data.json', import.meta.url) )
      • Node.js环境下,该值都是file://协议的链接
    • import.meta.scriptElement
      • 浏览器特有的属性
      • 返回加载模块的<script>标签,相当于document.currentScript

    规范中并未规定import.meta中包含哪些属性,一般包括上面两个属性

    深入理解模块加载

    ESModule的解析过程

    ESModule的解析过程可以分为三个阶段:

    • 构建 Construction
      • 根据地址查找JS文件,并发起HTTP请求下载,将其解析为模块记录 Module Record
    • 实例化 Instatiation
      • 对模块记录进行实例化,并为其分配内存空间
      • 解析ESModule模块的导入和导出语句,将模块指向对应的内存地址
      • 例如export const name = 'Ziu',会将变量name添加到模块环境记录中 Module Enviroment Record
    • 运行 Evaluation
      • 运行代码,计算值,并且将值填充到内存地址中
      • 将导入导出的赋给对应的变量name = 'Ziu'

    ![ESModule解析过程](Front-end Engineering.assets/esmodule-phases.png)

    文章推荐:ES modules: A cartoon deep-dive

    MJS和CJS的区别

    • CommonJS模块输出的是值的拷贝,而ESModule模块输出的是值的引用
      • CJS导出的变量,其值如果在模块内发生变化,外部导入是不会同步更新的,除非导出的是一个取值函数
      • MJS导出变量,外部模块每次访问时都会得到该变量最新的值,即使变量在模块内被修改了
    • CommonJS模块是运行时加载,而ESModule是编译时输出接口
      • CJS是通过对象实现的导入导出,它在运行时才被确定依赖关系和其值
      • MJS则是通过静态定义,在代码运行之前的静态解析阶段即可确定模块的导入导出内容
    • CommonJS模块的require()是同步加载模块,而ESModule模块的import命令是异步加载模块
      • import命令拥有一个独立的模块依赖的解析阶段

    CJS中的循环加载

    设想有以下两文件 a.jsb.js

    js
    // a.js
    +import(`./${moduleName()}.js`)

    import.meta

    ES2020引入了import.meta,它仅能在模块内部使用,包含一些模块自身的信息,即模块元信息

    • import.meta.url 返回当前模块的URL路径
      • 浏览器加载ESModule都是通过HTTP发起请求
        • 例如当前模块为fetchData.js,要在模块内引入一个名为data.json的数据:
        • import( new URL('data.json', import.meta.url) )
      • Node.js环境下,该值都是file://协议的链接
    • import.meta.scriptElement
      • 浏览器特有的属性
      • 返回加载模块的<script>标签,相当于document.currentScript

    规范中并未规定import.meta中包含哪些属性,一般包括上面两个属性

    深入理解模块加载

    ESModule的解析过程

    ESModule的解析过程可以分为三个阶段:

    • 构建 Construction
      • 根据地址查找JS文件,并发起HTTP请求下载,将其解析为模块记录 Module Record
    • 实例化 Instatiation
      • 对模块记录进行实例化,并为其分配内存空间
      • 解析ESModule模块的导入和导出语句,将模块指向对应的内存地址
      • 例如export const name = 'Ziu',会将变量name添加到模块环境记录中 Module Enviroment Record
    • 运行 Evaluation
      • 运行代码,计算值,并且将值填充到内存地址中
      • 将导入导出的赋给对应的变量name = 'Ziu'

    ![ESModule解析过程](Front-end Engineering.assets/esmodule-phases.png)

    文章推荐:ES modules: A cartoon deep-dive

    MJS和CJS的区别

    • CommonJS模块输出的是值的拷贝,而ESModule模块输出的是值的引用
      • CJS导出的变量,其值如果在模块内发生变化,外部导入是不会同步更新的,除非导出的是一个取值函数
      • MJS导出变量,外部模块每次访问时都会得到该变量最新的值,即使变量在模块内被修改了
    • CommonJS模块是运行时加载,而ESModule是编译时输出接口
      • CJS是通过对象实现的导入导出,它在运行时才被确定依赖关系和其值
      • MJS则是通过静态定义,在代码运行之前的静态解析阶段即可确定模块的导入导出内容
    • CommonJS模块的require()是同步加载模块,而ESModule模块的import命令是异步加载模块
      • import命令拥有一个独立的模块依赖的解析阶段

    CJS中的循环加载

    设想有以下两文件 a.jsb.js

    js
    // a.js
     exports.done = false
     const b = require('./b.js')
     console.log('在 a.js 之中,b.done = %j', b.done)
    @@ -186,7 +186,7 @@
     b.js 执行完毕
      a.js 之中,b.done = true
     a.js 执行完毕
    - main.js 之中, a.done=true, b.done=true

    总结:

    • CJS的模块导出是输出值的拷贝,而不是引用,值的变化不是动态的,而是会被缓存的
    • 循环加载时,CJS模块导出的值是当前已经执行部分代码产生的结果的值,而不是模块代码完全执行完后的最终值

    MJS中的循环加载

    ESModule的导入和导出与CommonJS有本质不同:

    js
    // a.mjs
    + main.js 之中, a.done=true, b.done=true

    总结:

    • CJS的模块导出是输出值的拷贝,而不是引用,值的变化不是动态的,而是会被缓存的
    • 循环加载时,CJS模块导出的值是当前已经执行部分代码产生的结果的值,而不是模块代码完全执行完后的最终值

    MJS中的循环加载

    ESModule的导入和导出与CommonJS有本质不同:

    js
    // a.mjs
     import { bar } from './b.mjs'
     console.log('a.mjs')
     console.log(bar)
    @@ -194,7 +194,7 @@
     import { foo } from './a.mjs'
     console.log('b.mjs')
     console.log(foo)
    -export let bar = 'bar'

    执行a.mjs后发现报错了:ReferenceError: Cannot access 'foo' before initialization,变量foo未定义

    • MJS模块在代码执行前会进行静态分析
    • 分析a.mjs的依赖关系时,发现其依赖了b.mjs
    • 于是加载b.mjs并解析它的依赖关系
    • 解析b.mjs的过程中,发现它又依赖了a.mjs
    • 这时引擎不会再去加载a.mjs 而是认为a.mjs这个模块的Module Record已经存在了
    • 继续向下执行,执行到console.log(foo)时发现foo未定义 抛出错误

    要实现预期效果,可以将foobar改写为取值函数,这时执行就不会报错了:

    js
    // a.mjs
    +export let bar = 'bar'

    执行a.mjs后发现报错了:ReferenceError: Cannot access 'foo' before initialization,变量foo未定义

    • MJS模块在代码执行前会进行静态分析
    • 分析a.mjs的依赖关系时,发现其依赖了b.mjs
    • 于是加载b.mjs并解析它的依赖关系
    • 解析b.mjs的过程中,发现它又依赖了a.mjs
    • 这时引擎不会再去加载a.mjs 而是认为a.mjs这个模块的Module Record已经存在了
    • 继续向下执行,执行到console.log(foo)时发现foo未定义 抛出错误

    要实现预期效果,可以将foobar改写为取值函数,这时执行就不会报错了:

    js
    // a.mjs
     import { bar } from './b.mjs'
     console.log('a.mjs')
     console.log(bar())
    @@ -226,8 +226,8 @@
     }

    其中keywords author license都会展示在此包的npm网站上,除此之外还可以定义homepage repository字段

    npm包的开发调试

    在npm包的开发过程中,如果要调试代码,那我们不可能每次修改代码都通过提交到npm后,重新下载到本地来调试代码

    一般来讲有三种解决方案:

    • 直接修改导入路径 简单粗暴
      • 如:将之前的import lodash from 'lodash-es'修改为import lodash from '... debug/lodash-es'
      • 也可以修改package.json中的依赖版本为file:包的路径
      • 纯人工操作,容易误操作,不推荐
    • 通过npm link命令 软连接
      • 首先在本地开发中的包项目根目录执行npm link,将提示:success Registered "my-project".成功注册名为my-project的包
      • 随后,在引入了此包的项目根目录下执行npm link "my-project" 即可将当前项目的node_modules中的此包软连接到正在开发中的包的路径
      • 在许多情况下可能有效,但它通常会带来严重的限制和依赖关系解决、文件系统之间的符号链接互操作性等问题
    • yalc模拟发布npm包
      • yalc可以将模拟发布的npm包存放在全局存储中,这样可以得到类似于发布-安装的效果
      • 可以避免npm link导致的依赖关系问题
        • 全局安装npm i yalc -g 模拟发布yalc publish
        • 引入方法1 yalc link my-project:向node_modules创建指向包内容的符号链接,并且不会修改package.json中的内容
        • 引入方法2 yalc add my-project:将包拉到当前项目根目录.yalc中,并将 file:.yalc/my-project 依赖关系注入到package.json中(file:包的路径

    PNPM

    解决了哪些痛点

    传统包管理工具如npm yarn都有以下两个痛点:

    • node_modules太重 硬盘压力较大
    • 幽灵依赖(为了复用 所有的包都被铺平在node_modules中 导致即使某些间接依赖虽然没有被安装 却能正常引入)

    PNPM(performant npm)有以下优点:

    • 比其他包管理器更快
    • node_modules中的文件链接自特定的内容寻址存储库
    • 支持 monorepos(单仓多包)
    • PNPM创建了一个非平铺的node_modules 代码无法访问任意包(避免了幽灵依赖)

    硬链接和软链接

    • 硬链接 (hard link)
      • 电脑文件系统中的多个文件平等地共享同一个文件存储单元
      • 删除一个文件名字后,还可以通过其他名字继续访问该文件
    • 符号链接 (软链接 soft link、Symbolic link)
      • 符号链接 是一类特殊的文件
      • 其包含有一条以绝对路径或者相对路径的形式指向其他文件或者目录的引用

    ![hard-link and soft-link](Front-end Engineering.assets/hard-link-and-soft-link.jpg)

    操作系统使用不同的文件系统对真实的硬盘读写操作做了一层抽象,借由文件系统,我们得以方便地操作和访问文件的真实数据

    • 软链接例:你为一个文件创建了一个快捷方式,这个快捷方式本质上保存着目标文件的路径
      • 访问快捷方式的本质是通过路径访问原文件
      • 删除快捷方式时,不会对原文件的数据产生影响
    • 硬链接例:F:/Video/abc.mp4F:/Video/cba.mp4指向了同一片硬盘数据空间
      • 在文件系统中删除abc.mp4cba.mp4之一,并不会影响到其他文件对该片数据空间的读取
      • 注意:硬链接不是拷贝,文件拷贝意味着有新的数据空间在硬盘上被开辟,拷贝出的文件和原文件指向不同的数据空间

    实操硬链接和软链接

    • 文件的拷贝
      • Windows: copy foo.js foo_copy.js
      • MacOS: cp foo.js foo_copy.js
    • 文件的硬链接
      • Windows: mklink /H aaa_hard.js aaa.js
      • MacOS: ln foo.js foo_hard.js
    • 文件的软链接
      • Windows: mklink aaa_soft.js aaa.js
      • MacOS: ln -s foo.js foo_copy.js

    打开符号链接文件,会直接跳转到原文件,且符号链接文件不可写

    PNPM的工作细节

    硬链接
    • 当使用npm或yarn时,如果有100个项目,且每个项目都有一个相同的依赖包
      • 此依赖包将会在你的硬盘上存储100份,占据100份硬盘数据空间
    • 当使用pnpm时,此依赖包将被存储在一个统一的位置
      • 所有其他项目引用该版本的依赖,将使用这同一份文件
      • 如果依赖有不同版本,则不同版本的依赖包分别存储

    这样,多个项目之间可以方便地共享相同版本的依赖包,减小了硬盘的压力。

    以安装axios为例,我们在某个项目中执行了pnpm add axios

    • PNPM拉取Axios的代码 将其保存在全局仓库.pnpm/axios中,并且硬链接到硬盘中的数据文件
    • 当前项目中的node_modules创建文件夹axios,将其中的文件硬链接到硬盘中的相同位置
    • 硬链接是不能操作目录的 目录需要创建 而其中的文件可以使用硬链接
    • 以后如果有其他项目也用到了相同版本的axios,在执行pnpm add axios后:只需要在各自的node_modules创建硬链接到那片数据空间即可
    非扁平的node_modules
    • 使用npm或yarn Classic安装依赖包时 所有包都将被提升到node_modules的根目录下
      • 这是为了防止依赖之间多层嵌套,才想出的解决方案
      • 这就导致:源码可以访问到间接依赖,访问到本不属于此项目的依赖包,导致幽灵依赖问题
    • 而PNPM仅有项目依赖会放在node_modules目录下
      • 这些在node_modules根目录中的依赖包,是node_modules/.pnpm中硬链接文件的的符号链接
      • node_modules/.pnpm中,包含了附加版本信息的真实文件(硬链接到硬盘数据的文件)
      • 所有间接依赖,都通过软链接的方式,链接到被铺平在.pnpm文件夹中对应版本的硬链接文件上

    ![how pnpm works](Front-end Engineering.assets/how-pnpm-works.jpg)

    常用命令

    • pnpm === npm init -y 项目初始化
    • pnpm install === npm i 安装项目依赖
    • pnpm add <pkg> === npm install <pkg> 安装npm包
    • pnpm remove <pkg> === npm uninstall <pkg> 移除npm包
    • pnpm <script> === npm run <script> 执行项目脚本

    存储Store的位置

    • 在PNPM 7.0之前,统一的存储位置是~/.pnpm-store
      • 例如C:/Users/Ziu/.pnpm-store
        • ~/path === $HOME/path 用户目录
        • ./path 相对路径
        • /path 根目录
    • 在7.0版本之后,统一存储位置在<pnpm home directory>/store
      • Linux: ~/.local/share/pnpm/store
      • Windows: %LOCALAPPDATA%/pnpm/store
      • MacOS: ~/Library/pnpm/store

    可以通过命令行,获取到这个目录:

    sh
    pnpm store path # 获取到硬链接store的路径

    如果当前目录创建了很多项目,尽管PNPM已经节省了空间,但是由于所有依赖都通过硬链接存放在同一个store中

    随着文件数量增长,导致store越来越大,可以通过命令行,移除冗余文件造成的体积占用

    sh
    pnpm store prune # prune修剪 从store中删除当前未被引用的包 以释放store的空间

    系统学习Webpack

    • 认识webpack工具
    • webpack基本打包
    • webpack配置文件
    • 编写和打包CSS文件
    • 编写Less文件
    • PostCSS工具处理CSS

    Node内置模块 path

    • path模块用于对路径和文件进行处理
    • 在不同系统上,文件路径的分隔符是不同的
      • MacOS和Linux的分隔符是/
      • Windows上的分隔符是\ \\ 目前也支持 /

    可以通过Node的path模块来抹平不同平台的差异

    常用API

    • path.extname 后缀名

    • path.basename 文件名

    • path.dirname 文件夹名

    • path.join 拼接多个路径

      • 下例中,由于p2通过../指定了上一级路径,所以从abc紧接着就是why
    js
    const path = require('path')
     const p1 = 'abc/cba'
     const p2 = '../why/kobe/james.txt'
    -console.log(path.join(p1, p2)) // \\abc\\why\\kobe\\james.txt
    • path.resolve 将多个路径片段解析、拼接为一个绝对路径
      • 从右往左处理传入的路径序列,每个路径依次被解析,直到构造完成一个绝对路径
      • 如果在处理完所有给定path的段之后,还没有生成绝对路径,则使用当前工作目录
      • 生成的路径被规范化并删除尾部斜杠,零长度path段被忽略
      • 如果没有传递path段,则path.resolve()将返回当前工作目录的绝对路径

    初识Webpack

    path模块在webpack的使用中有较大作用,所以预先介绍了一下,下面正式进入Webpack的学习

    • Webpack能够帮助我们进行模块化开发
    • 高级特性提升开发效率和代码安全性、可维护性,如 ES6+ TypeScript SASS Less 等都需要构建工具
    • 实时文件监听(开发热更新)、代码压缩合并等

    Released under the MIT License.

    - diff --git a/note/JavaScript.html b/note/JavaScript.html index cdec84d4..69536652 100644 --- a/note/JavaScript.html +++ b/note/JavaScript.html @@ -10,13 +10,13 @@ - + - - diff --git a/note/JavaScriptEnhanced.html b/note/JavaScriptEnhanced.html index 460caa90..ea701e8d 100644 --- a/note/JavaScriptEnhanced.html +++ b/note/JavaScriptEnhanced.html @@ -10,7 +10,7 @@ - + @@ -1869,8 +1869,8 @@ // 支持移除回调 setTimeout(() => { eventBus.off('navClick', callBack) // 移除某个事件的某次回调 -}, 5000)

    Released under the MIT License.

    - diff --git a/note/MySQL.html b/note/MySQL.html index fd811011..a2a56ea7 100644 --- a/note/MySQL.html +++ b/note/MySQL.html @@ -10,7 +10,7 @@ - + @@ -71,8 +71,8 @@ alter table 表名 rename to 新表名
    sql
    alter table emp add nickname varchar(20) comment '昵称'
     alter table emp modify nickname username varchar(30)
     alter table emp drop username
    -alter table emp rename to employee

    MySQL数据类型

    数值类型

    在定义字段时,通过关键字UNSIGNED确定其无符号 / 有符号

    类型大小范围(有符号)范围(无符号)用途
    TINYINT1 Bytes(-128,127)(0,255)小整数值
    SMALLINT2 Bytes(-32 768,32 767)(0,65 535)大整数值
    MEDIUMINT3 Bytes(-8 388 608,8 388 607)(0,16 777 215)大整数值
    INT或INTEGER4 Bytes(-2 147 483 648,2 147 483 647)(0,4 294 967 295)大整数值
    BIGINT8 Bytes(-9,223,372,036,854,775,808,9 223 372 036 854 775 807)(0,18 446 744 073 709 551 615)极大整数值
    FLOAT4 Bytes(-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38)0,(1.175 494 351 E-38,3.402 823 466 E+38)单精度 浮点数值
    DOUBLE8 Bytes(-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308)0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308)双精度 浮点数值
    DECIMAL对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2依赖于M和D的值依赖于M和D的值小数值

    字符串类型

    类型大小用途
    CHAR0-255 bytes定长字符串
    VARCHAR0-65535 bytes变长字符串
    TINYBLOB0-255 bytes不超过 255 个字符的二进制字符串
    TINYTEXT0-255 bytes短文本字符串
    BLOB0-65 535 bytes二进制形式的长文本数据
    TEXT0-65 535 bytes长文本数据
    MEDIUMBLOB0-16 777 215 bytes二进制形式的中等长度文本数据
    MEDIUMTEXT0-16 777 215 bytes中等长度文本数据
    LONGBLOB0-4 294 967 295 bytes二进制形式的极大文本数据
    LONGTEXT0-4 294 967 295 bytes极大文本数据

    注意:char(n) 和 varchar(n) 中括号中 n 代表字符的个数,并不代表字节个数,比如 CHAR(30) 就可以存储 30 个字符。

    CHAR 和 VARCHAR 类型类似,但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。CHAR性能更优

    BINARY 和 VARBINARY 类似于 CHAR 和 VARCHAR,不同的是它们包含二进制字符串而不要非二进制字符串。也就是说,它们包含字节字符串而不是字符字符串。这说明它们没有字符集,并且排序和比较基于列值字节的数值值。

    BLOB 是一个二进制大对象,可以容纳可变数量的数据。有 4 种 BLOB 类型:TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB。它们区别在于可容纳存储范围不同。

    有 4 种 TEXT 类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。对应的这 4 种 BLOB 类型,可存储的最大长度不同,可根据实际情况选择。

    日期时间类型

    类型大小 ( bytes)范围格式用途
    DATE31000-01-01/9999-12-31YYYY-MM-DD日期值
    TIME3'-838:59:59'/'838:59:59'HH:MM:SS时间值或持续时间
    YEAR11901/2155YYYY年份值
    DATETIME81000-01-01 00:00:00/9999-12-31 23:59:59YYYY-MM-DD HH:MM:SS混合日期和时间值
    TIMESTAMP41970-01-01 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07YYYYMMDD HHMMSS混合日期和时间值,时间戳

    Released under the MIT License.

    - diff --git a/note/React.html b/note/React.html index 02d739d3..8c3e120c 100644 --- a/note/React.html +++ b/note/React.html @@ -10,7 +10,7 @@ - + @@ -1041,7 +1041,7 @@ </div> ) } -}

    Context跨组件传参

    非父子组件之间的数据共享

    • props层层传递 跨组件会很不方便 对于中间那些本不需要这些props数据的组件是冗余的
    • 第三方状态库 外置于React 如Redux (实际开发中较为常用)
    • 事件总线 ...

    针对跨组件传参的场景,React提供了一个API名为Context

    • Context 提供了一个在组件之间共享此类值的方式,而不是显式地通过组件树逐层传递props
    • 使用 Context 共享那些全局的数据,如主题色、用户登录状态、locales等

    用Context实现跨组件传参

    假设有App Profile UserCard三个嵌套组件,我们希望App中的 isDarkMode 状态能够透传到UserCard组件中

    • 全局通过 createContext 创建一个上下文
    • 根组件通过 DarkModeContext.Provider 标签与 value 传递值到上下文中
    • 需要使用到该值的子组件通过 UserCard.contextType = DarkModeContext 绑定到上下文
    • 随后即可在子组件中通过 this.context 获取到此上下文当前绑定的状态值
    tsx
    // context.js
    +}

    Context跨组件传参

    非父子组件之间的数据共享

    • props层层传递 跨组件会很不方便 对于中间那些本不需要这些props数据的组件是冗余的
    • 第三方状态库 外置于React 如Redux (实际开发中较为常用)
    • 事件总线 ...

    针对跨组件传参的场景,React提供了一个API名为Context

    • Context 提供了一个在组件之间共享此类值的方式,而不是显式地通过组件树逐层传递props
    • 使用 Context 共享那些全局的数据,如主题色、用户登录状态、locales等

    用Context实现跨组件传参

    假设有App Profile UserCard三个嵌套组件,我们希望App中的 isDarkMode 状态能够透传到UserCard组件中

    • 全局通过 createContext 创建一个上下文
    • 根组件通过 DarkModeContext.Provider 标签与 value 传递值到上下文中
    • 需要使用到该值的子组件通过 UserCard.contextType = DarkModeContext 绑定到上下文
    • 随后即可在子组件中通过 this.context 获取到此上下文当前绑定的状态值
    tsx
    // context.js
     import { createContext } from 'react'
     
     export const DarkModeContext = createContext()
    tsx
    // App.jsx
    @@ -2176,7 +2176,7 @@
     }
     const className = Object.entries(classNameMap).map(item => !!item[1] ? item[0] : '').join(' ')

    手动编写动态绑定的className较为繁琐,可以依靠第三方库classnames帮我们完成(Vue已将类似的功能内置)

    bash
    npm i classnames

    Usage Sample:

    tsx
    classNames('foo', 'bar')
     classNames('foo', { bar: true })
    -classNames(...['foo', 'bar'])

    Redux

    • Redux的核心思想
    • Redux的基本使用
    • React结合Redux
    • Redux的异步操作
    • redux-devtool
    • reducer的模块拆分

    理解JavaScript的纯函数

    • 函数式编程中有一个非常重要的概念 纯函数,JavaScript符合函数式编程的范式,所以也有纯函数的概念
      • 在React开发中,纯函数被多次提及:
      • React组件被要求像一个纯函数(为什么是像,因为还有类组件)
      • Redux中有一个reducer的概念,同样是要求必须是一个纯函数
    • 掌握纯函数对于理解很多框架的设计都是有帮助的

    一个纯函数必然具备以下特征:

    • 确定的输入一定产生确定的输出
    • 函数的执行过程中,不能产生副作用

    为什么需要Redux

    • JS需要管理的状态越来越多,越来越复杂
    • 状态不断发生变化之间又相互依赖,这要求视图层也能同步更新
    • React提供了自动更新视图的方法,但状态仍需要手动管理
    • Redux可以帮我们管理状态,提供了可预测的状态管理
    • 框架无关,体积只有2KB大小

    Redux的核心理念

    Redux的核心理念 Store

    • 定义一个统一的规范来操作数据,这样就可以做到对数据的跟踪
    • list.push() list[0].age = 18

    Redux的核心理念 Action

    • Redux要求:要修改数据,必须通过Action来修改
    • 所有数据的变化,必须通过派发(Patch)Action来更新
    • Action是一个普通的JS对象,用来描述此次更新的type与content
    • const action = { type: 'ADD_ITEM', item: { name: 'Ziu', age: 18 } }

    Redux的核心理念 Reducer

    • 如何将Store和Action联系在一起?
    • reducer是一个纯函数
    • 完成的工作就是:将传入的state和action结合起来,生成一个新的state
    • patch => reducer => newState => Store

    Redux Demo

    下例中,通过createStore创建了一个Store(已经不推荐了)

    • initialState用于在调用createStore时作为默认值传入reducer
    • 后续每次store.dispatch都会调用reducer
    • 通过reducer更新state中的数据

    在React中,可以通过store.subscribe注册State变化的监听回调

    • 当state发生变化时,通过调用this.forceUpdate触发组件的更新
    • 一般情况下,我们在componentDidMount注册监听回调,在componentWillUnmount解除监听
    tsx
    // App.jsx
    +classNames(...['foo', 'bar'])

    Redux

    • Redux的核心思想
    • Redux的基本使用
    • React结合Redux
    • Redux的异步操作
    • redux-devtool
    • reducer的模块拆分

    理解JavaScript的纯函数

    • 函数式编程中有一个非常重要的概念 纯函数,JavaScript符合函数式编程的范式,所以也有纯函数的概念
      • 在React开发中,纯函数被多次提及:
      • React组件被要求像一个纯函数(为什么是像,因为还有类组件)
      • Redux中有一个reducer的概念,同样是要求必须是一个纯函数
    • 掌握纯函数对于理解很多框架的设计都是有帮助的

    一个纯函数必然具备以下特征:

    • 确定的输入一定产生确定的输出
    • 函数的执行过程中,不能产生副作用

    为什么需要Redux

    • JS需要管理的状态越来越多,越来越复杂
    • 状态不断发生变化之间又相互依赖,这要求视图层也能同步更新
    • React提供了自动更新视图的方法,但状态仍需要手动管理
    • Redux可以帮我们管理状态,提供了可预测的状态管理
    • 框架无关,体积只有2KB大小

    Redux的核心理念

    Redux的核心理念 Store

    • 定义一个统一的规范来操作数据,这样就可以做到对数据的跟踪
    • list.push() list[0].age = 18

    Redux的核心理念 Action

    • Redux要求:要修改数据,必须通过Action来修改
    • 所有数据的变化,必须通过派发(Patch)Action来更新
    • Action是一个普通的JS对象,用来描述此次更新的type与content
    • const action = { type: 'ADD_ITEM', item: { name: 'Ziu', age: 18 } }

    Redux的核心理念 Reducer

    • 如何将Store和Action联系在一起?
    • reducer是一个纯函数
    • 完成的工作就是:将传入的state和action结合起来,生成一个新的state
    • patch => reducer => newState => Store

    Redux Demo

    下例中,通过createStore创建了一个Store(已经不推荐了)

    • initialState用于在调用createStore时作为默认值传入reducer
    • 后续每次store.dispatch都会调用reducer
    • 通过reducer更新state中的数据

    在React中,可以通过store.subscribe注册State变化的监听回调

    • 当state发生变化时,通过调用this.forceUpdate触发组件的更新
    • 一般情况下,我们在componentDidMount注册监听回调,在componentWillUnmount解除监听
    tsx
    // App.jsx
     import React, { PureComponent } from 'react'
     import store from './store'
     
    @@ -2249,7 +2249,7 @@
     
     const store = createStore(reducer)
     
    -export default store

    redux-usage

    进一步封装

    可以将耦合在一起的代码拆分到不同文件中

    • reducer抽取出来reducer.js,简化store/index.js内容
    • action.type抽取为常量constants.js,使用时做导入,以保证一致性
    • action抽取出来actionFactory.js,用于外部dispatch时规范类型
    tsx
    // store/index.js
    +export default store

    redux-usage

    进一步封装

    可以将耦合在一起的代码拆分到不同文件中

    • reducer抽取出来reducer.js,简化store/index.js内容
    • action.type抽取为常量constants.js,使用时做导入,以保证一致性
    • action抽取出来actionFactory.js,用于外部dispatch时规范类型
    tsx
    // store/index.js
     import { createStore } from 'redux'
     import reducer from './reducer'
     
    @@ -2324,7 +2324,7 @@
           </div>
         )
       }
    -}

    Redux的三大原则

    单一数据源

    • 整个应用程序的状态都被存储在一棵Object Tree上
    • 且这个Object Tree只存储在一个Store中
    • 但Redux并不强制限制创建多Store,不利于数据维护
    • 单一数据源有利于整个应用程序的维护、追踪、修改

    State属性是只读的

    • 允许修改State的方法只有patch action,不要直接修改State
    • 确保了View或网络请求都不能修改State
    • 保证所有的修改都能被追踪、按照严格的顺序执行,不用担心竞态(race condition)的问题

    使用纯函数来执行修改

    • 通过reducer将旧State与新State联系在一起,并且返回一个新的State
    • 随着应用程序复杂程度增加,可以将reducer拆分为多个小的reducer,分别用于操作不同State Tree的某一部分
    • 所有的reducer都应该是纯函数,不能产生任何的副作用

    优化重复代码

    当编写了一些案例的时候会发现,React结合Redux时会编写很多重复的代码

    在每个需要用到Redux中状态的组件中,都需要在不同生命周期做添加订阅/解除订阅的处理,组件初始化时还要从store中取最新的状态

    针对重复代码的问题,可以使用之前学到的高阶组件来做优化

    Redux官方提供的库react-redux,可以让我们更方便的在React中使用Redux

    bash
    npm i react-redux

    在Profile组件中,通过高阶函数connect实现的

    将store中需要的状态通过mapStoreToProps转为props,并将需要使用store中状态的组件传入调用connect返回的函数中

    Profile组件中就可以从props中获取到store中的状态

    tsx
    // App.jsx
    +}

    Redux的三大原则

    单一数据源

    • 整个应用程序的状态都被存储在一棵Object Tree上
    • 且这个Object Tree只存储在一个Store中
    • 但Redux并不强制限制创建多Store,不利于数据维护
    • 单一数据源有利于整个应用程序的维护、追踪、修改

    State属性是只读的

    • 允许修改State的方法只有patch action,不要直接修改State
    • 确保了View或网络请求都不能修改State
    • 保证所有的修改都能被追踪、按照严格的顺序执行,不用担心竞态(race condition)的问题

    使用纯函数来执行修改

    • 通过reducer将旧State与新State联系在一起,并且返回一个新的State
    • 随着应用程序复杂程度增加,可以将reducer拆分为多个小的reducer,分别用于操作不同State Tree的某一部分
    • 所有的reducer都应该是纯函数,不能产生任何的副作用

    优化重复代码

    当编写了一些案例的时候会发现,React结合Redux时会编写很多重复的代码

    在每个需要用到Redux中状态的组件中,都需要在不同生命周期做添加订阅/解除订阅的处理,组件初始化时还要从store中取最新的状态

    针对重复代码的问题,可以使用之前学到的高阶组件来做优化

    Redux官方提供的库react-redux,可以让我们更方便的在React中使用Redux

    bash
    npm i react-redux

    在Profile组件中,通过高阶函数connect实现的

    将store中需要的状态通过mapStoreToProps转为props,并将需要使用store中状态的组件传入调用connect返回的函数中

    Profile组件中就可以从props中获取到store中的状态

    tsx
    // App.jsx
     import React, { PureComponent } from 'react'
     import { Provider } from 'react-redux'
     import store from './store'
    @@ -2394,7 +2394,7 @@
           )
         }
       }
    -)

    本质上是connect内部对操作进行了封装,把逻辑隐藏起来了:

    • 调用connect这个高阶函数,返回一个高阶组件
    • 为高阶组件传入映射目标组件,最后高阶组件返回一个新组件
    • 新组件的props包含了来自Store中状态/dispatch的映射

    异步Action

    有些场景下,我们希望组件能够直接调用Store中的action来触发网络请求,并且获取到数据

    但是dispatch只允许派发对象类型的Action,不能通过dispatch派发函数

    可以通过中间件redux-thunk来对Redux做增强,让dispatch能够对函数进行派发

    bash
    npm i redux-thunk

    通过applyMiddleware引入redux-thunk这个中间件:

    tsx
    // store/index.js
    +)

    本质上是connect内部对操作进行了封装,把逻辑隐藏起来了:

    • 调用connect这个高阶函数,返回一个高阶组件
    • 为高阶组件传入映射目标组件,最后高阶组件返回一个新组件
    • 新组件的props包含了来自Store中状态/dispatch的映射

    异步Action

    有些场景下,我们希望组件能够直接调用Store中的action来触发网络请求,并且获取到数据

    但是dispatch只允许派发对象类型的Action,不能通过dispatch派发函数

    可以通过中间件redux-thunk来对Redux做增强,让dispatch能够对函数进行派发

    bash
    npm i redux-thunk

    通过applyMiddleware引入redux-thunk这个中间件:

    tsx
    // store/index.js
     import { createStore, applyMiddleware } from 'redux'
     import thunk from 'redux-thunk'
     import reducer from './reducer'
    @@ -2481,8 +2481,93 @@
         - constants.js
         - index.js
         - reducer.js
    -  - ...

    combineReducer函数

    前面拆分Store时用到了combineReducer函数,将多个模块reducer组合到一起,函数内部是如何处理的?

    • 将传入的reducers合并到一个对象中,最终返回一个combination函数(相当于未拆分时传给createStorereducer函数)
    • 在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state
    • 新state会触发订阅者发生对应更新,而旧state可以有效地组织订阅者发生刷新

    Released under the MIT License.

    - diff --git a/note/SSR.html b/note/SSR.html index 84511eb1..8f50721f 100644 --- a/note/SSR.html +++ b/note/SSR.html @@ -10,7 +10,7 @@ - + @@ -27,8 +27,8 @@ "devDependencies": { "nuxt": "^3.2.0" } -}
    • build 构建正式版本,将被输出到.output文件夹
    • dev 开发环境
    • generate 打包正式版本项目,但是会预渲染每个路由(nuxt build --prerender
    • preview 对打包项目启动本地预览服务器
    • postinstall 该脚本为NPM的生命周期函数,将在npm install后执行

    针对postinstall中执行的nuxi prepare脚本,官方文档有如下解释:

    The prepare command creates a .nuxt directory in your application and generates types. This can be useful in a CI environment or as a postinstall command in your package.json.

    执行该脚本后,将在项目根目录创建.nuxt文件夹

    • 这个文件夹将作为默认构建输出的文件夹
    • 其中的文件都以.d.ts结尾,是Nuxt的TS类型声明

    配置 Configuration

    • 通过 nuxt.config.ts 文件,对Nuxt进行自定义配置
    • runtimeConfig 运行时配置 即定义环境变量
      • 直接定义在 runtimeConfig 中的值,仅在服务端可以访问到
        • 定义在runtimeConfig.public中的变量,在客户端和服务端中都能读取到
        • 也可以将环境变量定义在.env文件中,优先级.env > runtimeConfig
          • NUXT_开头的会作为私有环境变量读取到运行时
          • NUXT_PUBLIC_开头的会作为公共变量读取到运行时
      • appConfig 应用配置,定义在构建时确定的公共变量,如 theme
        • 配置会和app.config.ts合并,优先级app.config.ts > appConfig
      • app app的配置
      • head 给每个页面设置head信息,也支持useHead配置和内置组件
        • 在这个配置中定义的标签,会注入到所有页面的head标签中
        • 也可以在某些页面动态插入head标签内容 使用useHead函数
        • 或者在template中使用Nuxt的内置组件Head
      • ssr 指定应用渲染模式
        • 默认值为true 即采用SSR方式渲染应用
        • 如果指定了ssr: false 则会采用SPA的方式渲染应用,即客户端渲染
      • router 配置路由相关的信息,比如在客户端渲染可以配置hash路由
        • 需要注意的是:SSR并不支持哈希路由
        • router: { options: { hashMode: false } }
      • alias 路径别名
        • 默认已经为我们配置好了一些别名,详情可以参阅文档
      • modules 配置Nuxt扩展的模块,比如@pinia/nuxt @nuxt/image
      • routeRules 定义路由规则,可以更改路由的渲染模式或分配基于路由缓存策略
      • builder 指定使用Vite还是Webpack来构建应用,默认是Vite,如切换为Webpack还需要安装额外依赖

    runtimeConfig 与 appConfig

    • runtimeConfig: 定义环境变量,比如:运行时需要指定的私有/公共的token等
    • appConfig: 定义公共变量,比如:构建时确定的公共token、网站配置等

    针对他们的比较,官方文档提供了一个表格可以参阅:

    FeatureruntimeConfigapp.config
    Client SideHydratedBundled
    Environment Variables✅ Yes❌ No
    Reactive✅ Yes✅ Yes
    Types support✅ Partial✅ Yes
    Configuration per Request❌ No✅ Yes
    Hot Module Replacement❌ No✅ Yes
    Non primitive JS types❌ No✅ Yes

    runtimeconfig-vs-appconfig

    区分Client和Server环境

    Nuxt为我们扩展了Node的process对象,并为我们在浏览器环境提供了process对象:

    • Nuxt会在服务端的process对象中注入属性dev server client以供使用
    • 也会在浏览器网页中注入process对象,包含上述的三个属性
    • 也可以手动判断typeof window === 'object'检查是服务器环境/浏览器环境

    页面与组件 View and Component

    Nuxt会自动为我们:注册组件、注册页面路由,约定>配置

    • 位于pages/下的页面都会被注册路由
      • 路由使用内置组件NuxtPage占位,相当于router-view
      • 相对应的,可以使用NuxtLink执行跳转,相当于router-link
      • 因为底层是vue-router,所以动态路由、嵌套路由都是支持的
    • 位于components/下的组件都会被自动全局注册

    Nuxt3 内置组件

    Nuxt3 框架提供了一些内置的组件,常用的如下:

    • SEO组件:Html Body Head Title Meta Style Link NoScript Base
      • 这些组件的作用是,向页面中不同部分插入标签,在SSR的过程中渲染出来并返回给客户端
      • 这样爬虫就会在同步获取页面数据时获取到这些标签
    • NuxtPage:是Nuxt自带的页面占位组件
      • 需要显示位于目录中的顶级或嵌套页面pages/
      • 是对 router-view 的封装
    • ClientOnly:该组件包裹的内容只会在客户端渲染
      • 其中内容不会出现在服务端返回的.html文件中
      • 会在客户端通过JS脚本动态渲染出来
        • 类似于Vue3新增的内置组件Suspence
        • 可以为其传入具名插槽#fallback展示组件被渲染前的加载中状态
    • ServerOnly:该组件包裹的内容只会在服务端渲染

    创建页面

    文件目录即路由,,可以手动创建 也可以通过命令行快速创建页面

    • npx nuxi pages category/index: 创建pages/category/index.vue

    • npx nuxi pages home/index: 创建pages/home/index.vue

    • npx nuxi pages detail/[id]: 创建pages/detail/[id].vue 动态路由

    • 页面路由

      • 页面水合之后,页面导航会通过前端路由来实现,可以防止整页刷新
      • 当然,手动输入URL之后,点击刷新浏览器也可以导航,但这会导致整页刷新

    路由中间件

    Nuxt提供了一个可定制的路由中间件,用来监听路由的导航,包括:局部和全局监听

    • 匿名中间件
      • 在页面中,通过definePageMeta定义
      • 可以监听局部路由,当注册多个中间件时,会按照注册顺序执行
      • 首次访问会在双端执行,后续都只会在客户端执行
    • 命名路由中间件
      • middleware目录下定义,会自动加载中间件
      • 首次访问会在双端执行,后续都只会在客户端执行
    • 全局路由中间件
      • middleware目录中,需要带.global后缀的文件,每次路由更改会自动运行
      • 与前两者不同,每次页面切换,双端都会执行全局中间件

    路由验证 Validate

    Nuxt支持对每个页面路由进行验证,可以通过definePageMeta中的validate数学对路由进行验证

    • validate属性接收一个回调函数,回调函数以route作为参数
      • 此回调返回一个布尔值,来决定是否放行路由
        • false 拦截路由 默认重定向到404页面
        • true 放行路由 正常跳转
      • 返回一个对象
        • { statusCode: 401 } 返回自定义的 401 页面 验证失败
    • 可以自定义错误页面
      • 在项目根目录 新建error.vue

    页面布局 Layout

    Layout布局是页面的包装器,可以将多个页面共性的东西抽取到Layout布局中

    例如:每个页面的页眉和页脚,这些具有共性的组件,我们可以写到一个Layout布局中

    本质上是Vue3的<slot>组件

    • 默认布局,创建layouts/default.vue
      • 然后在app.vue中通过内置组件<NuxtLayout>使用
    • 自定义布局
      • 创建layouts/custom-layout.vue
      • 然后在app.vue中,为<NuxtLayout>传入name属性(具名插槽)

    渲染模式

    浏览器和服务器都可以解释JavaScript代码,都可以将Vue.js组件呈现为HTML元素,此过程称为渲染

    • 在客户端渲染组件为HTML元素的过程,称为客户端渲染
    • 在服务端完成这个此操作的过程,称为服务端渲染

    而Nuxt3支持多种渲染模式

    • 之前在配置文件中提到的ssr选项,可以选择以SSR模式渲染,还是CSR方式渲染
    • 混合渲染模式(SSR | CSR | SSG | SWR):需要在 routeRules 根据每个路由动态配置渲染模式

    Released under the MIT License.

    - diff --git a/project/ClipboardManager/guide/index.html b/project/ClipboardManager/guide/index.html index 7d426cd2..666b701d 100644 --- a/project/ClipboardManager/guide/index.html +++ b/project/ClipboardManager/guide/index.html @@ -10,7 +10,7 @@ - + @@ -27,8 +27,8 @@ "icon": "🚀", "match": ["image", { "type": "file", "regex": ".(?:jpg|jpeg|png)$" }], "command": "redirect:上传到图床" -}

    这个功能除了可以匹配图片,还可以将符合正则的图片文件匹配上,在匹配上的历史记录上展示上传到图床按钮,携带数据跳转到图床插件,一键上传。

    需要注意的是,因为自定义功能按钮实现的是携带数据跳转不同插件,所以redirect后的内容并不应该是普通关键字(普通关键字仅能作为插件入口,而不能携带数据),而应该是文本/图片/文件或文件夹

    在未来的版本更新中,超级剪贴板将开放更多自定义功能给高级用户,帮助你更高效率的管理、使用剪贴板。

    - diff --git a/project/ClipboardManager/index.html b/project/ClipboardManager/index.html index 2a3ca41d..fb348866 100644 --- a/project/ClipboardManager/index.html +++ b/project/ClipboardManager/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    🔰 开始使用

    首次安装需要设置“跟随主程序同时启动”

    • ✅ 监听剪贴板并持续将新内容更新到本地磁盘 数据读写完全本地化
    • ✅ 快速收藏/转存/分词/复制/删除/打开文件&目标文件夹
    • ✅ 功能按钮 定义无限可能 OCR识别 百度搜索 百度识图 统计文本字数 颜色管理 识别图片中二维码 上传到图床 翻译
    • 鼠标左键 复制并粘贴 鼠标右键 仅复制
    • ✅ 按下Shift空格进入多选模式 连续选择多条内容合并复制 支持跨标签合并复制/粘贴
    • ✅ 键盘 选中历史记录,按下回车直接粘贴
    • ✅ 键盘 切换分类 Tab键连续切换分类
    • ✅ 使用 Ctrl/Alt+数字键 快速粘贴
    • ✅ 插件内输入任意字母或数字自动聚焦搜索框 支持使用空格同时检索多个关键词
    • 超级Markdown合并剪贴板内容导出图文 超级粘贴 直接转存为文件 智慧分词 快速拖选指定内容
    • ✅ 优雅的界面动效与交互 跟随系统的深色模式
    • ✅ 优秀的剪贴板监听性能 强大的自定义功能按钮 自搭建多端同步 ···

    📚 安装方式

    催更群 769115389 Github

    Released under the MIT License.

    - diff --git a/project/ClipboardManager/log/index.html b/project/ClipboardManager/log/index.html index 8b162842..728a3eb9 100644 --- a/project/ClipboardManager/log/index.html +++ b/project/ClipboardManager/log/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    更新日志

    v2.1.3

    2023-04-12

    • fix: 修正合并粘贴功能
    • fix: 合并图文导出时过滤掉文件内容
    • fix: 调整使用记录的存储组织方式
    • fix: 调整合并图文导出的插件关键字

    v2.1.2

    2023-04-09

    • feat: 支持多选后批量删除
    • feat: 支持合并图文保存到超级Markdown
    • fix: 优化深色模式下多选条目背景色
    • fix: 修复图片过宽时溢出容器的问题

    v2.1.1

    2023-04-05

    • feat: 支持在设置页一键清理所有图片
    • fix: 修正本地数据版本较新时被云端覆盖的问题

    v2.1.0

    2023-03-29

    • feat: 收藏功能增强 支持为收藏内容添加备注
    • feat: 优化界面动效

    v2.0.11

    2023-03-01

    • fix: 优化导航栏展示/隐藏逻辑
    • fix: 修复某些场景下ESC无法退出插件的问题

    v2.0.10

    2023-02-27

    • fix: 修复轮询监听器执行错误导致插件白屏的问题

    v2.0.9

    2023-02-27

    • feat: 添加键盘操作模式 屏蔽鼠标滑动 高效快捷
    • feat: 添加极简模式 隐藏页面不必要组件
    • feat: 顶部导航栏添加展示/隐藏动效
    • feat: 移除部分动画效果 让动作简洁有力
    • feat: 添加底部横幅提醒监听器状态
    • fix: 优化轮询监听器性能
    • fix: 修复某些情况下导致的白屏问题
    • fix: 优化代码执行细节 提升插件稳定性

    v2.0.8

    2023-02-24

    • feat: 添加插件会员用户彩蛋
    • fix: 修复某些情况导致的插件白屏问题

    v2.0.7

    2023-02-24

    • fix: 修复某些情况下插件主页渲染被阻塞导致的白屏问题
    • fix: 修复初始化时重复执行Webdav上传的问题

    v2.0.6

    2023-02-22

    • fix: 优化列表展示性能
    • fix: 优化本地读写稳定性 修复数据损坏导致的白屏问题
    • fix: 优化代码高亮依赖缓存 避免多次请求下载

    v2.0.5

    2023-02-19

    • fix: 修复插件启动时WebDav上传失败报错的问题
    • fix: 修复插件启动时会员用户登录联网失败的问题

    v2.0.4

    2023-02-13

    • feat: 支持修改代码高亮主题、样式与是否启用
    • feat: 右下角常驻当前选中条目的更新日期
    • feat: 页面滚动条样式优化
    • fix: 修复错误识别文本内容为网址的问题
    • fix: 修复删除条目后插件卡死的问题
    • fix: 修复某些情况下数据不再向下展示的问题

    v2.0.3

    2023-02-10

    • feat: 按住Ctrl/Command点击文本/链接 可以直接预览/跳转
    • feat: 按住Ctrl/Command点击图片 可以直接全局预览
    • feat: 按住Ctrl/Command点击文件 可以打开其所在文件夹
    • feat: 调整预览页展开样式
    • fix: 多选状态无法通过点击鼠标添加选项
    • fix: 某些情况下预览页无法执行Ctrl+C部分复制

    v2.0.2

    2023-02-10

    • fix: 更新到新版后首次进入白屏问题
    • fix: 新设备首次进入插件数据库路径读取问题
    • fix: 优化联网失败的弹窗逻辑
    • fix: 普通用户图标颜色异常

    v2.0.1

    2023-02-10

    • feat: 工具栏按钮顺序调整 会员用户添加金色标识
    • fix: 切换分类时出现明显卡顿
    • fix: 未登录用户进入用户中心白屏
    • fix: 添加网络问题导致登录验证失败的弹窗提醒

    v2.0.0

    2023-02-07

    • feat: 界面焕新 简洁高效
    • feat: 支持WebDav自动同步功能
    • feat: 支持预览页代码高亮
    • feat: 支持自定义分类卡片的顺序
    • feat: 支持识别文本内容为色值、链接等
    • feat: 支持左右键切换导航
    • feat: 支持关闭剪贴板图片记录
    • feat: 支持手动设置偏好主题
    • fix: 清空数据库保留收藏内容
    • fix: 滚动到底部触发懒加载失败
    • refactor: 组件重构 优化大列表运行时性能
    • refactor: 本地读写性能优化

    v1.4.7

    2022-11-03

    • feat: 设置页支持使用ESC返回上一级
    • fix: 移除了启动监听程序时对MacOS的特殊判断
    • fix: 移除了缺少监听程序时的通知报错

    v1.4.6

    2022-11-01

    • feat: 调整设置页图标与界面按钮细节
    • fix: 会员用户由于数据同步导致的插件白屏问题
    • fix: 多选复制时条目排序被颠倒

    v1.4.5

    2022-10-10

    • feat: 调整过期天数列表 最长支持31天
    • feat: 设置页添加handler安装引导
    • fix: 清空数据时内存数据未清空
    • fix: 多端同步时内存数据未更新
    • fix: 开发者模式下listener错误挂载

    v1.4.4

    2022-09-26

    • fix: 修复重复粘贴的问题

    v1.4.3

    2022-09-25

    • feat: 增加空格快捷键 按下连续多选的功能
    • feat: 多选状态下移动鼠标不再激活列表条目
    • feat: 支持展示剪贴板监听程序状态
    • fix: 删除某条历史记录时不再返回顶部
    • fix: 多选图片无法合并复制
    • fix: 内置按钮颜色管理关键字错误
    • fix: 设置页下拉框过长时被卡片遮挡
    • fix: 设置页滚动到底部触发懒加载

    v1.4.2

    2022-09-21

    • chore: 移除了插件内的二进制文件 恢复旧的监听机制 性能问题有待解决

    v1.4.1

    2022-09-21

    • fix: 修复了新的监听机制在 Mac 下无法正确获取剪贴板更新事件的问题

    v1.4.0

    2022-09-20

    插件能够正确在Linux上运行 离不开 小千 不厌其烦的测试与调试 💖

    感谢每一位为超级剪贴板提出过建议、参与测试与调试的用户

    • feat: 支持自定义数据库路径/存储条数/过期时间/展示在主界面的按钮
    • feat: 功能按钮支持自定义 创造属于你自己的功能按钮
    • feat: 添加了9个内置的自定义功能按钮
    • feat: 增加了更优雅的弹窗与提示界面
    • feat: 预览页更宽了 支持预览图片
    • feat: 导航栏图标样式修改
    • fix: 解决了CPU占用高、浏览大图卡顿的性能问题
    • fix: 从后台进入插件自动清空搜索框
    • refactor: 改变了监听剪贴板的方式 性能更优
    • refactor: 引入了ElementPlus组件库与图标库

    v1.3.4

    2022-09-19

    • feat: 移除插件内收藏Tab
    • feat: 点击收藏按钮后将携带数据跳转至备忘快贴
    • refactor: 优化代码执行逻辑 移除冗余代码

    v1.3.3

    2022-09-16

    • feat: 提高主色在深色模式下的对比度 改善长文本表现
    • feat: 添加快存功能按钮 配合超级粘贴插件实现快速转存
    • feat: 支持在预览页展示全部功能按钮
    • feat: 重要版本更新自动展示通知卡片
    • fix: 修正多选快捷键Shift影响检索输入的问题
    • fix: 检索词更新后 未自动激活首条记录

    v1.3.2

    2022-09-15

    • feat: 鼠标悬停展示具体日期、完整数据
    • feat: 调整历史记录上限为800条
    • fix: 修复鼠标置于功能按钮时上下键失效的问题
    • fix: 调整大图片判定尺寸

    v1.3.1

    2022-09-12

    • feat: 支持通过历史记录列表进入分词
    • feat: 减小插件体积 对智慧分词的支持改为插件跳转

    v1.3.0

    2022-09-09

    • feat: 添加多选功能 支持跨标签合并文本/图片/文件
    • feat: 支持通过按下Shift进入多选功能 支持按住Shift快速选择
    • feat: 支持使用Ctrl+CEnter快捷合并复制/粘贴
    • feat: 添加智慧分词功能 可对文本进行分割提取
    • feat: 长文本以蓝色高亮显示 不再提供查看全部按钮
    • feat: 优化界面动效

    v1.2.3

    2022-09-08

    • fix: 修复了检索内容时搜索记录不准确的问题

    v1.2.2

    2022-09-07

    • feat: 搜索框支持使用空格分词以同时检索多个关键词
    • fix: 修复了剪贴板数据记录遗漏的问题
    • fix: 收藏内容不再计入到条数限制中
    • fix: 修复了非预期的搜索框聚焦行为
    • fix: 预览页的按钮改为固定在顶部

    v1.2.1

    2022-09-05

    • feat: 支持通过功能按钮进入任意数据的预览页面
    • feat: 支持通过功能按钮打开文件所在目录
    • feat: 添加取消收藏功能按钮
    • feat: 搜索框支持展开/收起 输入任意内容展开并聚焦搜索
    • feat: 侧栏预览页添加复制全部智慧分词按钮
    • feat: 功能按钮改为使用图标展示
    • feat: 优化了界面样式
    • refactor: 清理定时器 优化插件性能

    v1.2.0

    2022-09-04

    • feat: 添加右侧操作栏 支持复制/收藏/删除操作
    • feat: 添加标签页收藏 支持在此页面管理所有收藏
    • feat: 调整侧栏宽度 调整界面样式细节

    v1.1.7

    2022-08-30

    • fix: 定时器检查剪贴板,修复了不记录剪贴板的问题

    v1.1.6

    2022-08-27

    • feat: 添加Alt+数字键Ctrl+数字键快速选择功能
    • feat: 调整界面样式 调整查看全部位置 移除图片背景色 增加动效
    • feat: 搜索框增加检索条数展示
    • fix: 改善插件读取图片的性能

    v1.1.5

    2022-08-25

    • fix: 提高剪贴板读取频率,避免高频复制时丢数据的情况

    v1.1.4

    2022-08-20

    • feat: 增加清空搜索框的按钮
    • feat: 优化插件内按下ESC的功能: 退出完整预览/清空搜索框
    • feat: 进入插件自动选中框内全部文本
    • fix: Mac在分离窗口状态左键会粘贴到搜索框 #13

    v1.1.3

    2022-08-19

    • feat: 调整界面在深色模式下的表现样式
    • fix: 在侧栏全部数据中使用Ctrl+C复制部分文本失效
    • fix: Mac下使用Ctrl+C复制单条记录失效
    • fix: 通过换行符个数区分超长文本
    • fix: 窗口分离下鼠标单击导致粘贴到搜索框
    • chore: 原插件名剪贴板改为超级剪贴板

    v1.1.2

    2022-08-18

    • feat: 执行复制后只隐藏主界面而不退出插件到后台
    • feat: 搜索时不再区分大小写
    • fix: 文本内容会出现异常首行缩进
    • fix: 图片展示在列表中的时候 右侧多出一个图块
    • fix: 移除鼠标hover时数据底部的色块

    v1.1.1

    2022-08-17

    • fix: 复制超大图片进入插件时崩溃

    v1.1.0

    2022-08-16

    • feat: 支持使用 键切换选中记录
    • feat: 支持使用 Ctrl+C 复制选中记录
    • feat: 支持使用 Enter 复制选中记录并粘贴

    v1.0.4

    2022-08-16

    • feat: 进入插件自动回到顶部、切换至全部分类
    • feat: 监听到键盘事件自动聚焦到搜索框
    • feat: 调整鼠标hover动画与历史记录的active样式
    • fix: 使用Tab切换导航失效
    • fix: 超长图片显示越界

    v1.0.3

    2022-08-16

    • fix: 路径分隔符导致写入错误的数据文件

    v1.0.2

    2022-08-15

    • fix: Mac下由权限导致的无法写入数据

    v1.0.1

    2022-08-15

    • feat: 区分鼠标点击行为:左键复制并粘贴,右键仅复制
    • feat: 适配深色模式
    • feat: 移除了右侧的查看更多按钮
    • feat: 单次展示的条数增加到了15条
    • feat: 增加条数限制500条 增加存储日期限制14天
    • feat: 增加了多平台支持
    • fix: 执行粘贴后主输入框未隐藏
    • refactor: Vue3重构

    v1.0.0

    2022-08-14

    • release: v1.0.0发布

    Released under the MIT License.

    - diff --git a/project/ClipboardManager/statement/index.html b/project/ClipboardManager/statement/index.html index e22ba0a2..0f115d87 100644 --- a/project/ClipboardManager/statement/index.html +++ b/project/ClipboardManager/statement/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    Q&A

    插件启动时报错/白屏

    请依次尝试以下方法:

    1. 完全退出插件后重新启动插件
    2. 重新启动 uTools
    3. 删除本地数据库文件(位于C盘用户根目录下 文件名为_utools_clipboard_manager_storage
    4. 格式化 uTools 内的插件数据 (账号与数据/查看存储的文档数据)

    如果上述方法都无法解决你的问题,欢迎论坛回帖或加入QQ群 769115389 交流具体情况

    自动粘贴(自动上屏)功能失灵了

    目前已知:自动粘贴功能在 Windows微信 的聊天输入框内是不可用的。在其他场景下,此功能都能够正常使用

    插件内操作延迟、卡顿

    插件内复制/收藏/删除操作卡顿,可能是因为数据文件过大。

    当插件内的操作有明显延迟卡顿时,请尝试清理无用的图片记录,可以帮你显著地降低数据文件体积,提高插件响应速度

    剪贴板记录丢失

    一般是由于插件退出后台运行导致的, 插件需要保持后台运行才能记录剪贴板, 请检查:

    • 是否正确设置了插件跟随主程序启动
    • 是否手动清理或关闭了后台插件
      • 在分离模式下关闭插件(由uTools的机制决定,分离模式下关闭窗口则退出插件)
      • 使用了 clear 命令
      • 在插件列表主动退出了插件
    • 插件在高频复制的场景下,可能会漏掉某些记录

    如何设置快捷键快速打开插件?

    uTools偏好设置 / 全局快捷键 / 将Clipboard设置为你想要的快捷键

    后续通过快捷键即可快速唤出插件

    为什么偶尔CPU占用特别高? 为什么预览图片时插件卡顿?

    这是由超级剪贴板监听剪贴板内容更新的机制决定的,为了不漏数据,超级剪贴板的监听策略为每300ms读取一次剪贴板内容并与上一次读取到的内容作比较。如果剪贴板当前位置是一张大图片,那么插件会反复读取这张图片,导致大量计算被用在了无意义的读取与对比上。

    插件市场中相同原理的同类插件的处理方法是:

    • 轮询比较两次剪贴板内容,如果检查到当前剪贴板内容数据量较大,则降低轮询时间间隔。
      • 这避免了读取大图片/长文本时的高占用,然而这很容易导致漏数据
    • 轮询比较两次剪贴板内容,由用户自定义轮询时间
      • 用户设置的轮询间隔时间长,读取大图片/长文本时不会导致高占用,但很容易漏数据。
      • 用户设置的轮询间隔时间较短,则又会造成高占用

    v1.4.0版本更新中,超级剪贴板换用了clipboard-event库监听剪贴板更新事件,这种方式性能更优,不需要反复读取剪贴板内容来做比较,而只需要在检查到剪贴板更新事件时通知插件读取一次剪贴板即可,完全解决了CPU占用高、浏览图片卡顿的问题。

    然而该库包含了二进制文件,这触发了uTools插件市场的安全限制而无法上架。所以自v1.4.2起,插件不再内置监听剪贴板更新事件的二进制文件,用户要使用低占用、高性能的剪贴板监听方案,可以自行下载并将其移动到剪贴板数据库文件所在目录,详见如何手动安装clipboard-event-handler,插件会自动判断使用新的抑或是旧的方案监听剪贴板更新。同时,自v1.4.3起,你可以在设置页查看当前剪贴板监听程序的运行状态。

    为什么官方的剪贴板插件没有CPU占用高这个问题?

    官方的剪贴板内容更新事件监听函数也是从一个二进制文件引出的,这个二进制文件跟随主程序启动,不受剪贴板插件的打开或关闭的影响。

    我对这个插件的安全性有担忧, 插件偷窃我的隐私怎么办?

    首先, 我写这个插件不是为了获取你的隐私的, 我对你的隐私没有兴趣, 这一点你完全可以放心;

    其次uTools官方在插件上架前会对代码进行审查, 如果插件有高危行为, 那也不会过审;

    再其次, uTools大部分用户是程序员, 如果我真的在代码里藏了"毒", 那他们也有办法发现, 如果你真的对你的隐私十分关心, 可以选择从开源仓库下载代码自行构建

    开源版本和插件市场版本的区别?

    开源版本后续将只提供必要的BUG修复, 不再添加新功能

    • 开源版本: 包含完整的基本功能, 可以自行构建开源版本, 通过安装离线插件方式使用
    • 市场版本: 包含后续更新的新功能插件会员功能, 可以直接从插件应用市场安装

    为什么不开源了, 为什么要开始收费?

    目前由我个人维护的开源版本已经趋于稳定, 可以满足绝大部分场景的需求

    • 代码开源的出发点不是为了让不愿付费的人白嫖开发者的劳动, 而是为开发提供更多的经验和思路, 开源的代码已经启发了一些开发者上架了自己的剪贴板插件应用
    • 开发和维护插件需要时间和精力, 插件付费可以鼓励我更积极的更新
    • 目前插件内已有的基本功能都不会转为收费, 可以放心使用

    Released under the MIT License.

    - diff --git a/project/ClipboardManager/vip/index.html b/project/ClipboardManager/vip/index.html index 755837ac..348047d7 100644 --- a/project/ClipboardManager/vip/index.html +++ b/project/ClipboardManager/vip/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    插件会员

    TIP

    超级剪贴板是一款 “随心所欲” 的软件。您可以永久免费使用它。

    但是,如果您真的喜欢它,您可以付费支持它的发展。作为答谢,您将获得更多更方便的功能。这取决于您,如果您不想,也没关系。谢谢你,祝你有美好的一天!☀️

    会员权益

    • 插件会员
      • WebDav数据多端同步 已上线
      • 支持保存2000条历史记录 已上线
      • 不限制历史记录过期时间 已上线
      • 文本/图片/文件预览增强, 按住Ctrl/Command点击 解锁更多操作 已上线
      • 键盘操作模式 屏蔽鼠标 高效快捷 已上线
      • 简洁模式 隐藏页面中不必要的组件与元素 已上线
      • 自定义分类排序 已上线
      • 预览页代码高亮 已上线
      • 关闭剪贴板图片记录 已上线
      • 插件使用统计 已上线
      • ...
    • uTools会员
      • 插件使用统计 已上线

    会员定价

    WARNING

    • 插件会员为大版本买断制,购买后即可享用当前版本(2.x)后续所有更新的会员内容
    • 插件会员为虚拟商品,购买后不支持退款
    • 插件会员的价格是浮动的,随着功能和权益的增加,不排除涨价的可能
    • 购买插件会员后若未能及时到账,请加入QQ群反馈

    插件会员定价10元,登录uTools账号后,点击插件导航栏中个人中心图标进入购买页面

    购买后可使用会员功能,不购买不影响基本功能的使用,希望大家多多理解,按需购买

    WebDav同步功能

    WARNING

    如果剪贴板数据体积过大,可能出现同步时间过长、同步失败等问题

    建议及时删除保存在超级剪贴板内的图片,或关闭记录图片到剪贴板

    插件会员支持通过WebDav同步剪贴板数据

    获取服务器地址 账号 密钥后,仅需在插件内的设置页面添加WebDav配置后,

    每次本地数据库发生变化,都会将最新数据同步到同步到服务器(须开启插件内的自动同步 否则需要手动同步)

    WebDav

    • 如果配置了WebDav 则每天第一次进入插件时会自动从服务器拉取最新数据
    • 如果设置了自动同步 每次本地数据变化将自动同步本地数据到服务器
      • 插件会缓存云端数据文件的ETag 如果云端数据有更新 则采用云端数据覆盖本地数据
      • 数据文件的上传和下载都开启了gzip以节省流量与带宽
      • 清空本地数据库的同时也会触发云端文件清空
      • 可以选择关闭图片记录 以降低同步的数据文件体积 提高同步效率
    • 插件仅在每次启动时主动从云端拉取数据 云端数据的更新不会自动同步到本地
    • 可以通过点击工具栏按钮主动拉取云端数据到本地或上传数据到云端

    相关链接:

    代码高亮功能

    插件使用Shiki作为代码高亮库,为了压缩插件体积、提高代码执行效率,其相关依赖都将从CDN动态加载

    支持在设置页修改代码高亮的主题、可选语言、CDN地址,设置的首个语言为展开预览后的默认语言

    HighLight

    • 默认CDN地址: https://unpkg.com/shiki@0.14.1/
    • 默认主题: one-dark-pro
    • 默认语言: javascript css html json markdown php python ruby shellscript sql java c cpp csharp go

    更多可用主题/语言,见Github仓库:Shiki

    更多内容

    疑难解答

    Released under the MIT License.

    - diff --git a/project/JSRunner/index.html b/project/JSRunner/index.html index 5b14cdf1..ba7ac4dd 100644 --- a/project/JSRunner/index.html +++ b/project/JSRunner/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page
    logo

    ✨ JavaScript运行器 支持多种运行环境 快速验证代码逻辑

    🕶️ 在线体验👨🏻‍💻 开源地址🚚 更新日志

    🔰 开始使用

    • 运行JavaScript代码 快速验证代码逻辑
    • 支持切换NodeJS/浏览器运行环境
    • Ctrl/Command+R 快速运行代码
    • 支持代码回溯 实时记录代码运行历史
    • 支持手动触发代码运行/实时运行代码
    • 适配深色模式

    Released under the MIT License.

    - diff --git a/project/JSRunner/log/index.html b/project/JSRunner/log/index.html index 413d2ab2..26870fd6 100644 --- a/project/JSRunner/log/index.html +++ b/project/JSRunner/log/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    更新日志

    v1.0.2

    2023-04-24

    • fix: 修复NodeJS环境下无法导入第三方模块的问题

    v1.0.1

    2023-04-23

    • feat: 关于页添加项目开源地址
    • fix: 即时执行模式下代码为空时忽略警告
    • fix: 调整历史回溯的数量限制

    v1.0.0

    2023-04-19

    • 插件发布

    Released under the MIT License.

    - diff --git a/project/Markdown/index.html b/project/Markdown/index.html index b5032146..618630c3 100644 --- a/project/Markdown/index.html +++ b/project/Markdown/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    🔰 开始使用

    • 由ByteMD强力驱动,功能丰富、性能强劲
    • 支持GFM扩展语法、脚注、Gemoji、KaTeX数学公式、Mermaid图表
    • 支持通过Frontmatter设置多种主题、代码高亮样式
    • 支持多级目录,目录支持无限嵌套
    • 支持通过粘贴/拖拽的方式批量上传图片、支持截取屏幕截图
    • 支持Markdown文件的批量导入、批量导出
    • 支持插件多开,同时编辑/参考多个文章
    • 支持实时同步预览、自动保存

    Released under the MIT License.

    - diff --git a/project/Markdown/log/index.html b/project/Markdown/log/index.html index e2a865ca..7236dfe2 100644 --- a/project/Markdown/log/index.html +++ b/project/Markdown/log/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    更新日志

    v1.1.4

    2023-04-23

    • feat: 关于页增加插件开源地址

    v1.1.3

    2023-04-17

    • feat: 优化侧栏操作逻辑
    • fix: 修正XML标签代码高亮异常的问题
    • fix: 修正侧栏滚动条样式

    v1.1.2

    2023-04-12

    • feat: 调整合并图文导出关键字定义
    • feat: 新建文章自动在标题跟随日期
    • fix: 优化不同场景下编辑器聚焦行为

    v1.1.1

    2023-04-10

    • fix: 调整插件描述
    • fix: 修复关键字跳转问题
    • fix: 移除页面切换动效

    v1.1.0

    2023-04-09

    • feat: 支持通过关键字创建文章
    • feat: 支持从超级剪贴板多选合并创建文章
    • feat: 支持从文本/图片快速创建文章
    • feat: 支持批量导入Markdown文档
    • feat: 支持点击设置页关键字跳转
    • fix: 修复脚标点击跳转问题
    • fix: 修复部分场景下滚动条样式问题
    • fix: 修复部分场景下工具栏展示问题

    v1.0.1

    2023-04-05

    • feat: 支持插件多开 同时编辑/参考多个文档
    • fix: 修复图片被滚动条遮挡的问题
    • fix: 修复部分场景下标题栏与侧栏的bug

    v1.0.0

    2023-04-01

    • 插件发布

    Released under the MIT License.

    - diff --git a/project/Markdown/shortcut/index.html b/project/Markdown/shortcut/index.html index 7be8f2d5..bf4ec461 100644 --- a/project/Markdown/shortcut/index.html +++ b/project/Markdown/shortcut/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    快捷键一览

    粗体 Cmd-B

    斜体 Cmd-I

    链接 Cmd-K

    图片 Shift-Cmd-I

    代码 Shift-Cmd-K

    代码块 Shift-Cmd-C

    无序列表 Shift-Cmd-U

    有序列表 Shift-Cmd-O

    Markdown 语法

    一级标题 # 标题

    二级标题 ## 标题

    三级标题 ### 标题

    粗体 **粗体文本**

    斜体 *斜体文本*

    引用 > 引用文本

    链接 [链接描述](url)

    图片 ![alt](url "图片描述")

    代码 代码

    代码块 ```编程语言↵

    无序列表 - 项目

    有序列表 1. 项目

    分割线 ---

    删除线 ~~文本~~

    任务列表 - [ ] 待办事项

    行内公式 $公式$

    块级公式 $$↵公式↵$$

    Mermaid图表 ```mermaid

    Released under the MIT License.

    - diff --git a/project/SmartWordBreak/index.html b/project/SmartWordBreak/index.html index 794554a6..047bfb66 100644 --- a/project/SmartWordBreak/index.html +++ b/project/SmartWordBreak/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page
    logo

    ✨ 智慧分词,快速提取文本关键词。

    ⭐ 插件发布页🌎 Q&A🚚 更新日志

    🔰 开始使用

    服务器不会保留处理的数据, 但请避免使用此功能处理敏感数据

    • ✅ 支持选中文本后通过超级面板直接进入分词
    • ✅ 支持直接读取剪贴板文本分词
    • ✅ 支持单选/拖拽/跨段落快速拖选
    • ✅ 支持一键合并复制/粘贴/翻译/搜索
    • ✅ 普通用户有每日免费额度 每日0:00重置
    • ✅ 优雅、迅速的动效与交互 适配深色模式
    • ✅ 可以通过插件内入口获取更多额度
    • ✅ 与超级剪贴板插件集成,一次购买 多处使用

    📚 安装方式

    Released under the MIT License.

    - diff --git a/project/SmartWordBreak/log/index.html b/project/SmartWordBreak/log/index.html index d47a22ab..ec95fb2c 100644 --- a/project/SmartWordBreak/log/index.html +++ b/project/SmartWordBreak/log/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    更新日志

    v1.0.5

    2022-12-17

    • feat: 限免活动 每日免费额度调整为99 移除充值入口
    • fix: 修复历史记录显示问题

    v1.0.4

    2022-09-25

    • feat: 分词上限提高到了1000个字符
    • feat: token支持本地缓存

    v1.0.3

    2022-09-19

    • feat: 增加历史记录功能
    • fix: 移除了在tag外按下鼠标拖选的功能

    v1.0.2

    2022-09-13

    • feat: 支持跨段快速拖选
    • feat: 分词页添加了搜索粘贴功能
    • feat: 优化换行符在分词页的表现 支持包含换行符拖选

    v1.0.1

    2022-09-13

    • feat: 增加了拖选过程中的动效
    • feat: 调整了按钮样式与颜色
    • fix: 移除了碍事的toast

    v1.0.0

    2022-09-11

    • release: v1.0.0发布

    Released under the MIT License.

    - diff --git a/project/SmartWordBreak/statement/index.html b/project/SmartWordBreak/statement/index.html index 9d8851b7..46da321c 100644 --- a/project/SmartWordBreak/statement/index.html +++ b/project/SmartWordBreak/statement/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    Q&A

    • Q: 为什么要收费
    • A: 此功能需要服务器资源, 而服务器资源有限, 故暂时只开放部分的免费资源给大家使用, 普通用户每日有部分免费额度, 额度每日0:00重置

    • Q: 我支付了,但是没有收到额度
    • A: 支付成功后,插件会自动获取最新用户信息,如果信息没有自动更新,请尝试退出插件后重新进入,如果仍未收到额度,请加入QQ群 769115389并联系我,我会尽快核实并为你解决

    • Q: 我之前赞赏过,可以有优惠吗
    • A: 2022年9月13日0:00前,赞赏过我的uTools用户,请发送:
      • UUID(超级分词插件内展示)
      • 赞赏金额
      • 赞赏时间
      • 支付截图
    • 到邮箱ZiuChen@outlook.com,邮箱主题为:申请超级分词赞赏额度,我将在核实后为你提供赞赏金额+15%的额度,感谢你的支持!💖。

    Released under the MIT License.

    - diff --git a/self/index.html b/self/index.html index 263cba75..47b7c5a2 100644 --- a/self/index.html +++ b/self/index.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    个人介绍

    logo

    北京交通大学(BeijingJiaoTong University)电子信息工程学院本科在读

    • 熟悉ES6特性, 有前端领域开发经验, 能独立完成基础的前端开发工作;
    • 熟练使用Vue框架及相关工具开发应用, 阅读过部分Vue源码, 了解Vue响应式实现原理;
    • 掌握NodeJS基本使用, 能够基于NodeJS编写开发工具或搭建Web服务;
    • 了解Webpack基本配置和使用, 有Webpack Vite等前端工程化工具使用经验;
    • 了解Typescript, 有Typescript的项目使用经历;
    • 良好的Git操作, 清晰的Commit提交, 保证代码质量;
    • 对前端学习抱有热情, 有良好的学习能力, 能够快速学习掌握新知识;

    技术栈

    前端技术

    Vue Router Vuex Pinia Element Plus Echart WangEditor

    Sass/Less Axios Ajax ...

    后端技术

    相关技能

    文档处理:

    Microsoft Word Microsoft PowerPoint Microsoft Excel

    平面设计:

    Adobe PhotoShop Adobe Lightroom

    影音制作:

    DaVinci Resolve Studio Adobe Premiere Adobe After Effects

    专业软件:

    MATLAB ICEDA Multisim Keil uVision5

    获得奖项

    Microsoft Edge 浏览器开拓者大赛 开拓之星

    稀土掘金2022编程挑战赛 三等奖

    入选“扬帆计划·中央和国家机关大学生实习”

    100周年现场志愿者

    优秀学生干部、优秀共青团员

    社会工作优秀奖学金

    相关链接

    Github哔哩哔哩稀土掘金CSDNGitee

    Released under the MIT License.

    - diff --git a/works/contribution.html b/works/contribution.html index 00e4ec75..a775b96e 100644 --- a/works/contribution.html +++ b/works/contribution.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    社区贡献

    稀土掘金助手

    稀土掘金助手: 腾讯云函数部署

    源代码

    B站粉丝牌助手文档

    B站粉丝牌助手文档

    源代码Demo

    Released under the MIT License.

    - diff --git a/works/opensource.html b/works/opensource.html index 04b79bb3..9048d7ab 100644 --- a/works/opensource.html +++ b/works/opensource.html @@ -10,13 +10,13 @@ - + -
    Skip to content
    On this page

    个人作品

    Vue3+TypeScript后台管理系统

    Vue3+TypeScript后台管理系统

    源代码

    Typein 效率工具集

    Typein 是一个运行在现代浏览器上的拓展插件,它可以帮助你在保持专注的情况下,高效完成各种操作。

    源代码

    宣传视频

    Microsoft Edge Add-on Store

    A-SOUL浏览器宠物

    在浏览器里养一只A-Soul成员当宠物

    源代码

    宣传视频

    北京交通大学课程平台功能增强

    北京交通大学课程平台功能增强脚本,实现信息聚合,附件上传,让你高效处理课程信息。

    源代码

    超级剪贴板

    uTools插件,一款强大的剪贴板管理工具。基于Vue3构建

    源代码

    主页

    超级粘贴

    uTools插件,将剪切板内容直接粘贴为文件。功能基于NodeJS

    源代码

    超级分词

    uTools插件,前台使用Vue3+ElementPlus构建,后台基于Express封装结巴分词并暴露API接口

    超级连点器

    uTools插件,界面使用Vue3+AntDesign构建,功能基于Worker

    PopNotify

    仿 Element UI 的通知卡片。

    源代码

    Demo

    UserScripts

    用户脚本合集,生产力工具、效率工具、自动化脚本

    源代码

    Released under the MIT License.

    -