From b2fc2e49a58b08abed7bd9725159b6207588602e Mon Sep 17 00:00:00 2001 From: guoyangyang Date: Tue, 12 Dec 2017 17:57:11 +0800 Subject: [PATCH] give the vue dev tool the ablity to select the correspond component when inspect dom elment in the page. --- src/backend/index.js | 35 +++++++++- src/devtools/index.js | 3 + .../views/components/ComponentTree.vue | 65 +++++++++++++++++++ src/devtools/views/components/module.js | 6 +- src/util.js | 33 ++++++++++ 5 files changed, 138 insertions(+), 4 deletions(-) diff --git a/src/backend/index.js b/src/backend/index.js index 04f318bf5..61b475e2d 100644 --- a/src/backend/index.js +++ b/src/backend/index.js @@ -59,17 +59,46 @@ function connect () { } }) - bridge.on('select-instance', id => { + // when select element in dev tool element panel + //find the selected elment and select it in VUE component tree + bridge.on('element-inspected', uuid => { + function findParentComponent (instanceMap, uuid) { + let instanceContainer = [] + instanceMap.forEach((instance, id) => { + let curSelectedDom = document.querySelector(`[data-v_track_id="${uuid}"]`) + let isAncestorInstance = instance.$el.contains(curSelectedDom) + if (isAncestorInstance) { + instanceContainer.push(instance) + } + }) + //sort ancestor + instanceContainer.sort(function (m, n) { + return m._uid < n._uid + }) + return instanceContainer[0] + } + + let selectedComponent = findParentComponent(instanceMap, uuid) + if (selectedComponent) { + let id = selectedComponent.__VUE_DEVTOOLS_UID__ + selectInstance(id, true) + } + }) + + function selectInstance (id, isSelectedByInspector = false) { currentInspectedId = id const instance = instanceMap.get(id) - if (instance) { + if (instance && !isSelectedByInspector) { scrollIntoView(instance) highlight(instance) } + bridge.send('select-instance-by-inspector', isSelectedByInspector) bindToConsole(instance) flush() bridge.send('instance-details', stringify(getInstanceDetails(id))) - }) + } + + bridge.on('select-instance', selectInstance) bridge.on('filter-instances', _filter => { filter = _filter.toLowerCase() diff --git a/src/devtools/index.js b/src/devtools/index.js index 574915f6e..82e8b04fc 100644 --- a/src/devtools/index.js +++ b/src/devtools/index.js @@ -83,6 +83,9 @@ function initApp (shell) { bridge.on('instance-details', details => { store.commit('components/RECEIVE_INSTANCE_DETAILS', parse(details)) }) + bridge.on('select-instance-by-inspector', isByInspector => { + store.commit('components/SELECT_BY_INSPECTOR', isByInspector) + }) bridge.on('vuex:init', snapshot => { store.commit('vuex/INIT', snapshot) diff --git a/src/devtools/views/components/ComponentTree.vue b/src/devtools/views/components/ComponentTree.vue index de85a53fc..b33faaf54 100644 --- a/src/devtools/views/components/ComponentTree.vue +++ b/src/devtools/views/components/ComponentTree.vue @@ -24,6 +24,7 @@ import ActionHeader from 'components/ActionHeader.vue' import ComponentInstance from './ComponentInstance.vue' import keyNavMixin from '../../mixins/key-nav' +import { findPath } from 'src/util' export default { mixins: [keyNavMixin], @@ -35,6 +36,11 @@ export default { props: { instances: Array }, + watch: { + '$store.state.components.inspectedInstance.id':function (newVal) { + this.intoView(newVal) + } + }, methods: { filterInstances (e) { bridge.send('filter-instances', e.target.value) @@ -68,7 +74,66 @@ export default { } else { findByIndex(all, currentIndex + 1).select() } + }, + // listen element panel dom selected event + //and send select-dom event to notify content-script + listenDomSelected () { + let _this = this + function changeHandler () { + //return if not inspecting + /* if(!_this.$store.state.components.isInspectingComponent){ + return + }*/ + let uuid = Date.now() + chrome.devtools.inspectedWindow.eval(`$0.dataset.v_track_id=${uuid}`, ()=>{ + bridge.send('element-inspected', uuid) + }); + } + chrome.devtools.panels.elements.onSelectionChanged.addListener(changeHandler) + }, + //scroll the selected component into view + intoView (selectedId) { + // if not inspect component from element panel, return + if (!this.$store.state.components.isSelectedByInspector) { + return + } + let root = {id: '-1:-1', children: this.instances} + + let resolvedPaths = findPath(root, selectedId, (nodeId, id) => { + function getId (str) { + return str.split(':')[1] + } + + return getId(nodeId) === getId(id) + }) + resolvedPaths.slice(1).forEach((instance) => { + this.$store.dispatch('components/toggleInstance', { + instance, + expanded: true + }) + }) + function scrollIntoView (instance) { + if (instance.$el) { + instance.$el.scrollIntoView() + } + } + + this.$nextTick(() => { + const all = getAllInstances(this.$refs.instances) + if (!all.length) { + return + } + all.forEach((instance) => { + if (instance.instance.id === selectedId) { + scrollIntoView(instance) + } + }) + }) } + }, + + mounted () { + this.listenDomSelected() } } diff --git a/src/devtools/views/components/module.js b/src/devtools/views/components/module.js index 975ef083d..31610661c 100644 --- a/src/devtools/views/components/module.js +++ b/src/devtools/views/components/module.js @@ -5,7 +5,8 @@ const state = { inspectedInstance: {}, instances: [], expansionMap: {}, - events: [] + events: [], + isSelectByInspecting: false } const mutations = { @@ -27,6 +28,9 @@ const mutations = { }, TOGGLE_INSTANCE ({ expansionMap }, { id, expanded }) { Vue.set(expansionMap, id, expanded) + }, + SELECT_BY_INSPECTOR (state, isPassive) { + state.isSelectedByInspector = isPassive } } diff --git a/src/util.js b/src/util.js index ae1158acf..627883b96 100644 --- a/src/util.js +++ b/src/util.js @@ -163,3 +163,36 @@ export function sortByKey (state) { return 0 }) } + +/** + * find a path to the specify node with the id. + * @param root + * @param id + * @param comparator + * @returns {*} + */ +export function findPath (root, id, comparator) { + let paths = [root] + let NOT_FOUND = undefined + if (comparator(root.id, id)) { + return paths + } + if (root.children) { + let isNotFound = true + root.children.some((node) => { + let resolvedPath = findPath(node, id, comparator) + if (resolvedPath) { + paths = [...paths, ...resolvedPath] + isNotFound = false + return true + } + }) + if(isNotFound) { + return NOT_FOUND + } + + }else{ + return NOT_FOUND + } + return paths +} \ No newline at end of file