Skyphobia

无责任分享一些Vue的奇技淫巧(二)

Front-EndJavaScriptVue2.xVue3.x

以下任何内容请在家长陪同下观看,不要轻易在生产环境中尝试。

四、强制开启 Vue devtools(Vue 2.x)

通常我们会像这样在生产环境关闭 Vue devtools:

JavaScript
01Vue.config.devtools = true

但其实考虑到 Vue 2.x 版本的生产环境还保留了 __vue__ 属性,所以理论上只要能够开启 vue-devtools 的面板,Vue devtools 就可以正常工作并使用。那么问题来了,如何强制开启这个面板?

通过源码可知,Vue devtools 仅在开发工具面板打开时,根据 Vue.config.devtools 的配置决定是否开启,那只要在 Vue devtools 打开前劫持这个配置项,把 false 改成 true 就行了。

下面是实操:

  1. 以 vue-router 官网为例,默认生产环境是关闭了 Vue devtools 的:

  2. 打开开发者工具的 Sources 面板,找到 devtools 对应的配置代码,一般长下面这个样子。这里配置对象是 D!1就是 false,表示关闭 devtools:

  3. 在这个对象的定义下面打上断点,刷新页面触发断点准备修改这个对象的 devtools:

  4. 切换到 Console 面板,把 D.devtools 设置为 true

  5. 最后重启下开发者工具就能看到 vue-devtools 已经检测到了当前页面为 Vue 开发的应用,并且开发者工具 vue-devtools 的面板也已经被开启了:

五、通过魔法属性 __vueParentComponent 获取 vue 实例(Vue 3.x)

之前在《无责任分享一些Vue的奇技淫巧(一)》中提到过如何通过 __vue__ 魔法属性获取 Vue 的实例。时隔三年,Vue 3.x 的版本已经移除了这个属性,仅在 development 环境中为 DOM 挂载了 __vueParentComponent 属性以供Vue devtools 使用,对应的 Vue 实例获取方式为 __vueParentComponent.ctx。但生产包中会剔除相关的挂载实例的代码,并且目前生产环境也不支持开启 vue-devtool,因此可以合理推断目前没有比较好的手段去劫持生产环境的组件实例了。

代码

JavaScript
01const app = Vue.createApp({
02 template: `
03 <div id="parent">
04 <h1>Hello {{msg}}!</h1>
05 <demo></demo>
06 </div>
07 `,
08 data () {
09 return {
10 msg: 'Vue 3'
11 }
12 }
13})
14
15app.component('demo', {
16 template: '<button @click="onclick">click</button>',
17 methods: {
18 onclick () {
19 // 通过 __vueParentComponent.ctx 获取父级实例,并篡改 msg 数据
20 const $vm = document.querySelector('#parent').__vueParentComponent.ctx
21 $vm.$data.msg = '__vueParentComponent.ctx'
22 }
23 }
24 })
25
26app.mount('#app')

演示

六、通过魔法属性 __vue_app 获取应用实例(Vue 3.x)

__vue__ 属性的移除使得对生产环境组件实例的劫持成为了不可能,但 Vue3.x 还是暴露了一个通过 createApp 创建的实例在应用挂载的 DOM 上:

JavaScript
01// 被 Vue 应用 mount 的 DOM
02const $app = document.querySelector('#app')
03
04// 可以获取到 Vue.createApp 返回的应用实例
05console.log($app.__vue_app)

这个属性的可靠性和应用还有待考察,各位看官请谨慎使用。