Vue3迁移:如何全局注册响应式实例供所有组件访问
Vue3 全局响应式设备状态实现方案
Vue3 移除了Vue.prototype的全局挂载逻辑,要实现和Vue2版本一致的全局可访问、响应式的设备判断实例,有两种成熟实现,其中第一种可以做到组件内原有业务代码零修改。
方案一:全局属性挂载(兼容原有this.device访问习惯)
这种方案完全对齐你在Vue2中的使用方式,原有组件里的this.device.xxx代码不需要做任何改动。
- 首先重构原有的设备判断模块,不再通过
new Vue创建实例,直接使用Vue3的响应式API创建独立的响应式状态对象:
// utils/device.js import { reactive } from 'vue' // 响应式状态对象,替代原有new Vue生成的实例 export const device = reactive({ isTablet: false, isMobile: false, isDesktop: false }) // 原有的屏幕尺寸计算方法、事件逻辑直接迁移即可 const updateDeviceStatus = () => { const screenWidth = window.innerWidth device.isMobile = screenWidth < 768 device.isTablet = screenWidth >= 768 && screenWidth < 1200 device.isDesktop = screenWidth >= 1200 // 原有自定义计算逻辑可直接写在这里 } // 初始化绑定resize监听 export const initDevice = () => { updateDeviceStatus() window.addEventListener('resize', updateDeviceStatus) } // 卸载时解绑监听,避免内存泄漏 export const destroyDevice = () => { window.removeEventListener('resize', updateDeviceStatus) } // 原有实例上的自定义方法,直接挂载到device对象上即可,和原methods调用方式一致 // 例:device.customMethod = () => {}
- 在项目入口文件main.js中做全局注册,替代原有
Vue.prototype的挂载逻辑:
// main.js import { createApp } from 'vue' import App from './App.vue' import { device, initDevice, destroyDevice } from './utils/device' const app = createApp(App) // 全局挂载设备状态对象,所有组件实例可直接通过this.device访问 app.config.globalProperties.device = device app.mount('#app') // 初始化状态监听 initDevice() // 页面卸载时清理监听 window.addEventListener('beforeunload', destroyDevice)
配置完成后,原有组件代码完全不需要调整,直接通过this.device.isMobile等属性即可访问响应式状态、调用挂载的方法,状态变更时组件会自动触发更新。
方案二:Provide/Inject 依赖注入(适配Composition API 语法)
如果你的项目大量使用<script setup>语法(该语法下没有组件实例this),可以配合依赖注入的方式实现全局访问,更符合Vue3的设计规范:
- 第一步的device状态模块和方案一完全一致,不需要改动。
- 在根组件App.vue中向下注入device对象:
<!-- App.vue 选项式写法 --> <script> import { device } from './utils/device' import { initDevice, destroyDevice } from './utils/device' export default { provide: { device }, mounted() { initDevice() }, beforeUnmount() { destroyDevice() } } </script>
如果根组件用<script setup>语法:
<!-- App.vue script setup写法 --> <script setup> import { provide, onMounted, onBeforeUnmount } from 'vue' import { device, initDevice, destroyDevice } from './utils/device' provide('device', device) onMounted(() => initDevice()) onBeforeUnmount(() => destroyDevice()) </script>
- 任意子组件中可以直接注入使用:
- 选项式API组件:
export default { inject: ['device'], methods: { foo() { // 直接通过this.device访问,和原有写法一致 if(this.device.isMobile) { // 业务逻辑 } } } }<script setup>语法组件:
<script setup> import { inject } from 'vue' const device = inject('device') const foo = () => { if(device.isMobile) { // 业务逻辑 } } </script>
注意:必须使用
reactive包裹设备状态对象,不要直接挂载普通对象,否则状态变更不会触发组件的响应式更新,这是和Vue2中new Vue实例自带响应式特性最核心的差异。
内容的提问来源于stack exchange,提问作者Vladimir
相关产品推荐
相关产品推荐

