|
90 | 90 | <meta property="og:description" content="404"> |
91 | 91 | <meta property="og:locale" content="zh_CN"> |
92 | 92 | <meta property="og:image" content="https://blog.mhuig.top/lib/favicon/android-chrome-192x192.png"> |
93 | | -<meta property="article:published_time" content="2025-11-12T20:37:01.705Z"> |
94 | | -<meta property="article:modified_time" content="2025-11-12T20:37:01.705Z"> |
| 93 | +<meta property="article:published_time" content="2025-11-14T01:55:25.450Z"> |
| 94 | +<meta property="article:modified_time" content="2025-11-14T01:55:25.450Z"> |
95 | 95 | <meta property="article:author" content="MHuiG"> |
96 | 96 | <meta property="article:tag" content="Blog, MHuiG, @MHuiG, iMHuiG, 博客, Magicland, 魔法世界"> |
97 | 97 | <meta name="twitter:card" content="summary"> |
|
2085 | 2085 |
|
2086 | 2086 | <script> |
2087 | 2087 | /************这个文件存放不需要重载的全局变量和全局函数*********/ |
2088 | | - window.volantis = {}; // volantis 全局变量 |
| 2088 | + // window.volantis = {}; // volantis 全局变量 |
| 2089 | +/** |
| 2090 | + * 内存安全的全局 volantis 对象实现 |
| 2091 | + * 特性: |
| 2092 | + * 1. 支持 window.volantis.xxx = yyy 直观赋值语法 |
| 2093 | + * 2. 基于 WeakMap 存储数据,自动回收内存 |
| 2094 | + * 3. 支持属性枚举(如 Object.keys(window.volantis)) |
| 2095 | + * 4. 支持删除属性(delete window.volantis.xxx) |
| 2096 | + */ |
| 2097 | +(function initializeVolantis() { |
| 2098 | + // 1. 初始化 WeakMap 存储实际数据(键:volantisProxy 实例,值:属性键值对) |
| 2099 | + const volantisDataStore = new WeakMap(); |
| 2100 | + |
| 2101 | + // 2. 创建 Proxy 代理处理属性访问 |
| 2102 | + const volantisProxy = new Proxy({}, { |
| 2103 | + /** |
| 2104 | + * 拦截属性读取(如 window.volantis.dom) |
| 2105 | + * @param {Object} target - 代理目标对象(仅作为占位符,不存储数据) |
| 2106 | + * @param {string} prop - 访问的属性名 |
| 2107 | + * @returns {any} 属性值或 undefined |
| 2108 | + */ |
| 2109 | + get(target, prop) { |
| 2110 | + const data = volantisDataStore.get(volantisProxy); |
| 2111 | + return data?.[prop]; // 使用可选链避免数据未初始化时的错误 |
| 2112 | + }, |
| 2113 | + |
| 2114 | + /** |
| 2115 | + * 拦截属性赋值(如 window.volantis.dom = "a") |
| 2116 | + * @param {Object} target - 代理目标对象 |
| 2117 | + * @param {string} prop - 赋值的属性名 |
| 2118 | + * @param {any} value - 赋值的属性值 |
| 2119 | + * @returns {boolean} 赋值是否成功 |
| 2120 | + */ |
| 2121 | + set(target, prop, value) { |
| 2122 | + // 初始化数据容器(若不存在) |
| 2123 | + if (!volantisDataStore.has(volantisProxy)) { |
| 2124 | + volantisDataStore.set(volantisProxy, Object.create(null)); // 使用无原型对象避免原型污染 |
| 2125 | + } |
| 2126 | + // 存储属性值 |
| 2127 | + const data = volantisDataStore.get(volantisProxy); |
| 2128 | + data[prop] = value; |
| 2129 | + return true; // 符合 Proxy 规范,返回成功标志 |
| 2130 | + }, |
| 2131 | + |
| 2132 | + /** |
| 2133 | + * 拦截属性删除(如 delete window.volantis.dom) |
| 2134 | + * @param {Object} target - 代理目标对象 |
| 2135 | + * @param {string} prop - 要删除的属性名 |
| 2136 | + * @returns {boolean} 删除是否成功 |
| 2137 | + */ |
| 2138 | + deleteProperty(target, prop) { |
| 2139 | + const data = volantisDataStore.get(volantisProxy); |
| 2140 | + if (!data || !Object.prototype.hasOwnProperty.call(data, prop)) { |
| 2141 | + return false; // 属性不存在,删除失败 |
| 2142 | + } |
| 2143 | + delete data[prop]; |
| 2144 | + // 若数据为空,清理 WeakMap 条目(优化内存回收) |
| 2145 | + if (Object.keys(data).length === 0) { |
| 2146 | + volantisDataStore.delete(volantisProxy); |
| 2147 | + } |
| 2148 | + return true; |
| 2149 | + }, |
| 2150 | + |
| 2151 | + /** |
| 2152 | + * 拦截属性枚举(如 Object.keys(window.volantis)) |
| 2153 | + * @param {Object} target - 代理目标对象 |
| 2154 | + * @returns {string[]} 可枚举的属性名数组 |
| 2155 | + */ |
| 2156 | + ownKeys(target) { |
| 2157 | + const data = volantisDataStore.get(volantisProxy); |
| 2158 | + return data ? Object.keys(data) : []; |
| 2159 | + }, |
| 2160 | + |
| 2161 | + /** |
| 2162 | + * 拦截属性描述符查询(如 Object.getOwnPropertyDescriptor) |
| 2163 | + * @param {Object} target - 代理目标对象 |
| 2164 | + * @param {string} prop - 属性名 |
| 2165 | + * @returns {PropertyDescriptor|undefined} 属性描述符 |
| 2166 | + */ |
| 2167 | + getOwnPropertyDescriptor(target, prop) { |
| 2168 | + const data = volantisDataStore.get(volantisProxy); |
| 2169 | + if (!data || !Object.prototype.hasOwnProperty.call(data, prop)) { |
| 2170 | + return undefined; |
| 2171 | + } |
| 2172 | + return { |
| 2173 | + value: data[prop], |
| 2174 | + writable: true, |
| 2175 | + enumerable: true, |
| 2176 | + configurable: true |
| 2177 | + }; |
| 2178 | + } |
| 2179 | + }); |
| 2180 | + |
| 2181 | + // 3. 挂载到 window 对象,暴露全局访问入口 |
| 2182 | + Object.defineProperty(window, 'volantis', { |
| 2183 | + value: volantisProxy, |
| 2184 | + writable: true, // 允许后续手动设置为 null 触发清理 |
| 2185 | + configurable: true, |
| 2186 | + enumerable: true |
| 2187 | + }); |
| 2188 | + |
| 2189 | +})(); |
2089 | 2190 | volantis.debug = "false"; // 调试模式 |
2090 | 2191 | volantis.dom = {}; // 页面Dom see: /source/js/app.js etc. |
2091 | 2192 | /******************** volantis.EventListener ********************************/ |
2092 | 2193 | // 事件监听器 see: /source/js/app.js |
2093 | | - volantis.EventListener = {} |
| 2194 | + /*volantis.EventListener = {} |
2094 | 2195 | // 这里存放pjax切换页面时将被移除的事件监听器 |
2095 | 2196 | volantis.EventListener.list = [] |
2096 | 2197 | //构造方法 |
|
2105 | 2206 | i.ele.removeEventListener(i.type, i.f, false) |
2106 | 2207 | }) |
2107 | 2208 | volantis.EventListener.list = [] |
2108 | | - } |
| 2209 | + }*/ |
| 2210 | +volantis.EventListener = (() => { |
| 2211 | + // 用 WeakMap 存储 DOM 元素与监听器的映射(键为 DOM 元素,值为监听器数组) |
| 2212 | + const listenerWeakMap = new WeakMap(); |
| 2213 | + |
| 2214 | + return { |
| 2215 | + // 注册事件监听器(替代原有的构造方法) |
| 2216 | + add(type, f, ele) { |
| 2217 | + if (!ele || typeof ele.removeEventListener !== 'function') { |
| 2218 | + console.error('Invalid DOM element:', ele); |
| 2219 | + return; |
| 2220 | + } |
| 2221 | + |
| 2222 | + // 为元素添加监听器并存储到 WeakMap |
| 2223 | + ele.addEventListener(type, f, false); |
| 2224 | + if (!listenerWeakMap.has(ele)) { |
| 2225 | + listenerWeakMap.set(ele, []); // 初始化元素的监听器数组 |
| 2226 | + } |
| 2227 | + listenerWeakMap.get(ele).push({ type, f }); // 存储监听器元数据 |
| 2228 | + }, |
| 2229 | + |
| 2230 | + // 移除指定元素的所有监听器 |
| 2231 | + removeByElement(ele) { |
| 2232 | + if (!listenerWeakMap.has(ele)) return; |
| 2233 | + |
| 2234 | + // 遍历并移除该元素的所有监听器 |
| 2235 | + listenerWeakMap.get(ele).forEach(({ type, f }) => { |
| 2236 | + ele.removeEventListener(type, f, false); |
| 2237 | + }); |
| 2238 | + listenerWeakMap.delete(ele); // 清除 WeakMap 中的记录 |
| 2239 | + }, |
| 2240 | + |
| 2241 | + // 移除所有元素的监听器(用于 pjax 页面切换等场景) |
| 2242 | + remove() { |
| 2243 | + // WeakMap 无法直接遍历键,需通过临时数组缓存元素 |
| 2244 | + const elements = Array.from(listenerWeakMap.keys()); |
| 2245 | + elements.forEach(ele => this.removeByElement(ele)); |
| 2246 | + } |
| 2247 | + }; |
| 2248 | +})(); |
2109 | 2249 | /******************** volantis.dom.$ ********************************/ |
2110 | 2250 | // 注:这里没有选择器,也没有forEach一次只处理一个dom,这里重新封装主题常用的dom方法,返回的是dom对象,对象包含了以下方法,同时保留dom的原生API |
2111 | 2251 | function volantisDom(ele) { |
|
2142 | 2282 | this.ele.on = (c, f, r = 1) => { |
2143 | 2283 | this.ele.addEventListener(c, f, false) |
2144 | 2284 | if (r) { |
2145 | | - volantis.EventListener.list.push(new volantisEventListener(c, f, this.ele)) |
| 2285 | + //volantis.EventListener.list.push(new volantisEventListener(c, f, this.ele)) |
| 2286 | + volantis.EventListener.add(c, f, this.ele); |
2146 | 2287 | } |
2147 | 2288 | return this.ele |
2148 | 2289 | } |
|
2497 | 2638 | root: '/Test/', |
2498 | 2639 | debug: false, |
2499 | 2640 | default: {"avatar":"https://static.mhuig.top/npm/volantis-static@0.0.1660614606622/media/placeholder/avatar/round/3442075.svg","link":"https://static.mhuig.top/npm/volantis-static@0.0.1660614606622/media/placeholder/link/8f277b4ee0ecd.svg","cover":"https://static.mhuig.top/npm/volantis-static@0.0.1660614606622/media/placeholder/cover/76b86c0226ffd.svg","image":"https://static.mhuig.top/npm/volantis-static@0.0.1660614606622/media/placeholder/image/2659360.svg"}, |
2500 | | - lastupdate: new Date(1762979826528), |
| 2641 | + lastupdate: new Date(1763085330336), |
2501 | 2642 | cdn: { |
2502 | 2643 | izitoast_css: 'https://cdn.bootcdn.net/ajax/libs/izitoast/1.4.0/css/iziToast.min.css', |
2503 | 2644 | izitoast_js: 'https://cdn.bootcdn.net/ajax/libs/izitoast/1.4.0/js/iziToast.min.js', |
|
2547 | 2688 | } |
2548 | 2689 | } |
2549 | 2690 | </script> |
| 2691 | + |
2550 | 2692 |
|
2551 | | - <script type="application/ld+json">[{"@context":"http://schema.org","@type":"Organization","name":"MHuiG Magicland","url":"https://blog.mhuig.top/Test/","logo":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png","width":192,"height":192}},{"@context":"http://schema.org","@type":"Person","name":"MHuiG","image":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png"},"url":"https://blog.mhuig.top/Test/","sameAs":["https://github.com/MHuiG","https://twitter.com/iMHuiG","https://t.me/MHuiG","https://keybase.io/MHuiG"],"description":"「Be Yourself, Make a Difference.」"},{"@context":"http://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"https://blog.mhuig.top/Test/","name":"Magicland"}}]},{"@context":"http://schema.org","@type":"WebSite","name":"MHuiG Magicland","url":"https://blog.mhuig.top/Test/","keywords":"Blog, MHuiG, @MHuiG, iMHuiG, 博客, Magicland, 魔法世界","description":"MHuiG Blog (MHuiG的博客) Magicland(MHuiG的魔法世界) —— MHuiG(@MHuiG) 随便写写画画的地方 - Magic Island","author":{"@type":"Person","name":"MHuiG","image":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png"},"url":"https://blog.mhuig.top/Test/","description":"「Be Yourself, Make a Difference.」"},"publisher":{"@type":"Organization","name":"MHuiG Magicland","url":"https://blog.mhuig.top/Test/","logo":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png","width":192,"height":192}},"potentialAction":{"@type":"SearchAction","name":"Coco | The Cat of MHuiG","target":{"@type":"EntryPoint","urlTemplate":"https://blog.mhuig.top/havefun/Coco/?s={search_term_string}"},"query-input":"required name=search_term_string"}},{"@context":"http://schema.org","@type":"BlogPosting","description":"MHuiG Blog (MHuiG的博客) Magicland(MHuiG的魔法世界) —— MHuiG(@MHuiG) 随便写写画画的地方 - Magic Island","inLanguage":"zh-CN","mainEntityOfPage":{"@type":"WebPage","@id":"https://blog.mhuig.top/404"},"author":{"@type":"Person","name":"MHuiG","image":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png"},"url":"https://blog.mhuig.top/Test/"},"publisher":{"@type":"Organization","name":"MHuiG Magicland","logo":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png","width":192,"height":192}},"url":"https://blog.mhuig.top/404","wordCount":0,"datePublished":"2025-11-12T20:37:01.705Z","dateModified":"2025-11-12T20:37:01.705Z","image":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png","width":192,"height":192}}]</script> |
| 2693 | + <script type="application/ld+json">[{"@context":"http://schema.org","@type":"Organization","name":"MHuiG Magicland","url":"https://blog.mhuig.top/Test/","logo":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png","width":192,"height":192}},{"@context":"http://schema.org","@type":"Person","name":"MHuiG","image":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png"},"url":"https://blog.mhuig.top/Test/","sameAs":["https://github.com/MHuiG","https://twitter.com/iMHuiG","https://t.me/MHuiG","https://keybase.io/MHuiG"],"description":"「Be Yourself, Make a Difference.」"},{"@context":"http://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"https://blog.mhuig.top/Test/","name":"Magicland"}}]},{"@context":"http://schema.org","@type":"WebSite","name":"MHuiG Magicland","url":"https://blog.mhuig.top/Test/","keywords":"Blog, MHuiG, @MHuiG, iMHuiG, 博客, Magicland, 魔法世界","description":"MHuiG Blog (MHuiG的博客) Magicland(MHuiG的魔法世界) —— MHuiG(@MHuiG) 随便写写画画的地方 - Magic Island","author":{"@type":"Person","name":"MHuiG","image":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png"},"url":"https://blog.mhuig.top/Test/","description":"「Be Yourself, Make a Difference.」"},"publisher":{"@type":"Organization","name":"MHuiG Magicland","url":"https://blog.mhuig.top/Test/","logo":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png","width":192,"height":192}},"potentialAction":{"@type":"SearchAction","name":"Coco | The Cat of MHuiG","target":{"@type":"EntryPoint","urlTemplate":"https://blog.mhuig.top/havefun/Coco/?s={search_term_string}"},"query-input":"required name=search_term_string"}},{"@context":"http://schema.org","@type":"BlogPosting","description":"MHuiG Blog (MHuiG的博客) Magicland(MHuiG的魔法世界) —— MHuiG(@MHuiG) 随便写写画画的地方 - Magic Island","inLanguage":"zh-CN","mainEntityOfPage":{"@type":"WebPage","@id":"https://blog.mhuig.top/404"},"author":{"@type":"Person","name":"MHuiG","image":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png"},"url":"https://blog.mhuig.top/Test/"},"publisher":{"@type":"Organization","name":"MHuiG Magicland","logo":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png","width":192,"height":192}},"url":"https://blog.mhuig.top/404","wordCount":0,"datePublished":"2025-11-14T01:55:25.450Z","dateModified":"2025-11-14T01:55:25.450Z","image":{"@type":"ImageObject","url":"/lib/favicon/android-chrome-192x192.png","width":192,"height":192}}]</script> |
2552 | 2694 |
|
2553 | 2695 | <!-- import head_end begin --> |
2554 | 2696 | <!-- import head_end end --> |
|
0 commit comments