Vue.prototype详解及使用方式
作者:sslcsq
我们可能会在很多组件里用到数据/实用工具,但是不想污染全局作用域。
这种情况下,可以通过在原型上定义它们使其在每个 Vue 的实例中可用。
1. 基本示例
在main.js中添加一个变量到 Vue.prototype
Vue.prototype.$appName = 'My App'
这样 $appName 就在所有的 Vue 实例中可用了,甚至在实例被创建之前就可以
beforeCreate: function () { console.log(this.$appName) }
控制台会打印出 My App
2. 为实例prototype设置作用域
为什么 appName 要以 $ 开头?
$
是在 Vue 所有实例中都可用的 property
的一个简单约定
。
这样做会避免和已被定义的数据、方法、计算属性产生冲突。
如果我们设置:
Vue.prototype.appName = 'My App'
export default { data(){ return{ appName:'组件实例中的appName' } }, beforeCreate: function () { console.log(this.appName) }, created: function () { console.log(this.appName) }, } </script>
日志中会先出现 “My App”,然后出现 “组件实例中的appName”,因为 this.appName 在实例被创建之后被 data 覆写了。
我们通过 $ 为实例 property 设置作用域来避免这种事情发生。
3. 注册和使用全局变量
每个组件都是一个vue实例,Vue.prototype加一个变量,只是给每个组件加了一个属性,这个属性的值并不具有全局性。
比如以下例子:
Vue.prototype.$appName = 'main'
给所有组件注册了一个属性 $appName,赋予初始值 'main' ,所有组件都可以用 this.$appName 访问此变量;
如果组件中没有赋值,初始值都是'main'
app.vue
<template> <div id="app"> 主组件name-》{{this.$appName}} <p>{{newName}}</p> <button @click="changeName">更改name</button> <button @click="$router.push('/cs')">跳转</button> <hr> <router-view></router-view> </div> </template>
<script> export default { data(){ return{ newName:'' } }, methods:{ changeName(){ this.$appName = "changeName" this.newName=this.$appName } } } </script>
ce.vue
<template> <div class="ce"> 跳转页面name-》{{this.$appName}} </div> </template>
在app.vue中点击更改name,$appName值已发生改变,但cs.vue页面的值没有发生变化
如果要实现全局变量的功能,需要把属性变为引用类型
Vue.prototype.$appName = { name: 'main' }
使用 this.$appName.name 改变和引用相应的值
app.vue
<template> <div id="app"> 主组件name-》{{this.$appName.name}} <p>{{newName}}</p> <button @click="changeName">更改name</button> <button @click="$router.push('/cs')">跳转</button> <hr> <router-view></router-view> </div> </template>
<script> export default { data(){ return{ newName:'' } }, methods:{ changeName(){ this.$appName.name = "changeName" this.newName=this.$appName.name } } } </script>
cs.vue
<template> <div class="ce"> 跳转页面name-》{{this.$appName.name}} </div> </template>
在app.vue中点击更改name,$appName值已发生改变,cs.vue页面的值也发生了变化
4. 原型方法的上下文
在 JavaScript 中一个原型的方法会获得该实例的上下文,也就是说可以使用 this 访问:数据、计算属性、方法或其它任何定义在实例上的东西。
让我们将其用在一个名为 $reverseText 的方法上:
// main.js Vue.prototype.$reverseText = function (propertyName) { this[propertyName] = this[propertyName] .split('') .reverse() .join('') }
<script> export default { data() { return{ message: 'Hello' } }, created() { console.log(this.message) // => "Hello" this.$reverseText('message') console.log(this.message) // => "olleH" } } </script>
5. 应用示例
引入bus
const bus = new Vue() Vue.prototype.$bus = bus
this.$bus.$emit("fun",'A组件传来的值')
axios…
6.Vue.prototype中的api
Vue.prototype是Vue.js框架中一个重要的原型对象,通过它可以在全局范围内定义和共享Vue实例方法、指令、过滤器等。
在Vue.prototype对象上定义的属性和方法,会被挂载到所有Vue实例的原型链上,从而可以在组件中通过this访问。
一些常见的Vue.prototype中的API包括:
$emit(eventName[, ...args])
:触发当前实例上的事件。可以通过该方法向父组件或同级组件传递数据。$on(eventName, callback)
:监听当前实例上的事件。可以通过该方法在组件间传递数据。$nextTick(callback)
:在下次 DOM 更新循环结束之后执行延迟回调。常用于更新后立即操作 DOM。$watch(exprOrFn, callback[, options])
:监听一个表达式或计算属性的变化,并在回调函数中处理变化。$set(target, key, value)
:在一个已有的响应式对象上添加一个属性,并确保这个新属性同样是响应式的,可以通过该方法解决对象添加新属性时无法响应式更新的问题。$delete(target, key)
:删除一个对象的属性,可以通过该方法解决对象删除属性时无法响应式更新的问题。$refs
:一个对象,持有所有注册过 ref 的子组件。$el
:当前组件的根 DOM 元素。$options
:当前实例的初始化选项对象,包括组件的各种选项。
我们可以解析某个api源码
7.$nextTick源码
$nextTick是Vue.js框架中一个常用的异步更新方法,用于在下一次DOM更新循环结束后执行回调函数。
其源码如下:
Vue.prototype.$nextTick = function(fn) { return nextTick(fn, this) } // _nextTickId存储下一个tick的id号 let _nextTickId = 0 // _callbacks存储回调函数 let _callbacks = [] // _pending存储是否正在执行 let _pending = false // nextTick函数 function nextTick(fn, ctx) { let id, callback callback = () => { // 如果传入了fn,则执行回调函数 if (fn) { try { fn.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (callback) { callback.id = null // 如果没有传入fn,但存在回调函数,则从_callbacks中移除该回调函数 let index = _callbacks.indexOf(callback) if (index > -1) { _callbacks.splice(index, 1) } } } // 每次nextTick都会将该回调函数推入_callbacks中,等待执行 _callbacks.push(callback) if (!_pending) { _pending = true // 使用微任务将回调函数异步执行 if (typeof Promise !== 'undefined') { id = Promise.resolve().then(flushCallbacks) } else if (typeof MutationObserver !== 'undefined') { let observer = new MutationObserver(flushCallbacks) let textNode = document.createTextNode(String(_nextTickId)) observer.observe(textNode, { characterData: true }) id = () => { textNode.data = String(++_nextTickId) } } else if (typeof setImmediate !== 'undefined') { id = setImmediate(flushCallbacks) } else { id = setTimeout(flushCallbacks, 0) } } // 返回id,方便使用者手动取消nextTick if (!fn && typeof Promise !== 'undefined') { return id } } // flushCallbacks函数,用于执行_callbacks中的所有回调函数 function flushCallbacks() { _pending = false const copies = _callbacks.slice(0) _callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
$nextTick方法首先将回调函数推入_callbacks数组中,并使用一个_pending变量记录是否有回调函数正在执行。
如果_pending为false,则说明当前没有回调函数正在执行,需要异步执行flushCallbacks函数,从而依次执行_callbacks数组中的所有回调函数。
在异步执行时,$nextTick方法会优先使用Promise的微任务方式执行回调函数,如果浏览器不支持Promise,则会尝试使用MutationObserver、setImmediate和setTimeout等方式执行。
当传入的回调函数为空时,$nextTick方法会返回一个id,方便使用者手动取消nextTick。
需要注意的是,$nextTick方法只会在组件实例的更新周期内生效,如果需要在Vue.js框架初始化后立即执行回调函数,可以使用Vue.nextTick()全局方法。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。