|
|
@@ -785,39 +785,58 @@ export const Rooter = () => {
|
|
|
return \`rec-\${tag}-\${contentHash}\${parentPath}\`;
|
|
|
}
|
|
|
|
|
|
- // 2. 注入固定 record-id(劫持 Vue 元素创建)
|
|
|
- // Vue 2 适配
|
|
|
- if (window.Vue && Vue.prototype._c) {
|
|
|
- const originalCreateElement = Vue.prototype._c;
|
|
|
- Vue.prototype._c = function(tag, data = {}, children, normalizationType) {
|
|
|
- const vnode = originalCreateElement.call(this, tag, data, children, normalizationType);
|
|
|
- const originalMounted = vnode.hook?.insert || vnode.hook?.mounted;
|
|
|
- vnode.hook = vnode.hook || {};
|
|
|
- vnode.hook.insert = vnode.hook.mounted = function(el) {
|
|
|
- originalMounted?.call(this, el);
|
|
|
- if (!el.hasAttribute('record-id')) el.setAttribute('record-id', getFixedRecordId(el));
|
|
|
- };
|
|
|
- return vnode;
|
|
|
- };
|
|
|
- }
|
|
|
- console.debug('window.Vue', window.Vue)
|
|
|
- // Vue 3 适配
|
|
|
- if (window.Vue && Vue.createVNode) {
|
|
|
- const originalCreateVNode = Vue.createVNode;
|
|
|
- Vue.createVNode = function(type, props = {}, children, patchFlag, dynamicProps, shapeFlag) {
|
|
|
- const vnode = originalCreateVNode.call(this, type, props, children, patchFlag, dynamicProps, shapeFlag);
|
|
|
- const originalMounted = vnode.mount;
|
|
|
- vnode.mount = function(el) {
|
|
|
- const result = originalMounted?.call(this, el) || el;
|
|
|
- const targetEl = result.el || result;
|
|
|
- if (targetEl && !targetEl.hasAttribute('record-id')) targetEl.setAttribute('record-id', getFixedRecordId(targetEl));
|
|
|
- return result;
|
|
|
- };
|
|
|
- return vnode;
|
|
|
- };
|
|
|
+ // 2. 劫持原生 DOM 创建方法(框架无关方案)
|
|
|
+ const originalCreateElement = document.createElement;
|
|
|
+ document.createElement = function(tagName, options) {
|
|
|
+ const element = originalCreateElement.call(this, tagName, options);
|
|
|
+
|
|
|
+ // 延迟设置 record-id,确保元素已插入 DOM
|
|
|
+ setTimeout(() => {
|
|
|
+ if (element.parentNode && !element.hasAttribute('record-id')) {
|
|
|
+ element.setAttribute('record-id', getFixedRecordId(element));
|
|
|
+ }
|
|
|
+ }, 0);
|
|
|
+
|
|
|
+ return element;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 3. 劫持 innerHTML 设置,为动态创建的元素添加 record-id
|
|
|
+ const originalSetInnerHTML = Element.prototype.__lookupSetter__('innerHTML');
|
|
|
+ if (originalSetInnerHTML) {
|
|
|
+ Element.prototype.__defineSetter__('innerHTML', function(html) {
|
|
|
+ originalSetInnerHTML.call(this, html);
|
|
|
+
|
|
|
+ // 为新创建的元素添加 record-id
|
|
|
+ setTimeout(() => {
|
|
|
+ const elements = this.querySelectorAll('*');
|
|
|
+ elements.forEach(el => {
|
|
|
+ if (!el.hasAttribute('record-id')) {
|
|
|
+ el.setAttribute('record-id', getFixedRecordId(el));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }, 0);
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- // 3. Broadcast Channel 同步点击
|
|
|
+ // 4. 劫持 insertAdjacentHTML
|
|
|
+ const originalInsertAdjacentHTML = Element.prototype.insertAdjacentHTML;
|
|
|
+ Element.prototype.insertAdjacentHTML = function(position, html) {
|
|
|
+ originalInsertAdjacentHTML.call(this, position, html);
|
|
|
+
|
|
|
+ // 为新创建的元素添加 record-id
|
|
|
+ setTimeout(() => {
|
|
|
+ const elements = this.querySelectorAll('*');
|
|
|
+ elements.forEach(el => {
|
|
|
+ if (!el.hasAttribute('record-id')) {
|
|
|
+ el.setAttribute('record-id', getFixedRecordId(el));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }, 0);
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('DOM 元素创建劫持已启用');
|
|
|
+
|
|
|
+ // 5. Broadcast Channel 同步点击
|
|
|
const bc = new BroadcastChannel('vue-click-sync'); // 同一频道名实现通信
|
|
|
|
|
|
// 监听自身点击,发送到另一个窗口
|