【Vue 2】处理边界情况

访问元素和组件

通过Vue 2 组件基础一文的学习,我们知道组件之间可以通过传递props事件来进行通信。

但在一些情况下,我们使用下面的方法将更有用。


1.访问根实例

根实例可通过this.$root获取。

我们在所有子组件中都可以像上面那样访问根实例,它就像一个vuex中的全局store

这样我们可以在根实例上定义方法和属性,这些方法和属性可以在所有组件中访问。

根实例也可以用作事件总线,你可以触发根实例上的自定义事件,在其他组件上监听这些事件以进行通信。一个示例如下:

<!DOCTYPE html>
<html>

<head>
    <title>Vue 事件总线</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <button @click="sendMessage">Send Message By Root</button>
        <child-component></child-component>
    </div>
    <script>
        //  注册组件
        Vue.component('child-component', {
            data: function () {
                return {
                    childMessage:'childMsg'
                }
            },
            template: 
            `
            <div>
                <p>Message in ChildComponent:{{childMessage}}</p>
            </div>
            `,
            created(){
                //通过 this.$root 监听`message-sent`事件
                this.$root.$on('message-sent',message=>{
                    this.childMessage=message
                })
            }
        });
        var vm = new Vue({
            el: '#app',
            data:{
                message:'rootMsg'
            },
            methods:{
                sendMessage(){
                    //在根实例上触发自定义事件'message-sent'
                    this.$emit('message-sent',this.message)
                }
            }
        });
    </script>
</body>

</html>
<script>

在这里插入图片描述


2.访问父组件实例、访问子组件实例或子元素

之前我们可以通过传递prop来达到 子组件访问父组件实例 或者称为 父组件向子组件传值 的目的。

现在我们可以使用$parent property来访问父级组件实例。

之前我们也通过触发自定义事件传递抛出值的方式来访问子组件实例。

同样的,我们可以通过ref attribute为子组件或子元素添加一个ID引用。

<child-component ref="child"></child-component>

于是可以使用this.$refs.child来访问子组件实例。

通过ref引用子组件示例:

<!DOCTYPE html>
<html>

<head>
    <title>Vue ref 示例</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <button @click="toggleColor">Toggle Color</button>
        <p :style="{ color: textColor }">This text color can be toggled.</p>
        <child-component ref="childRef"></child-component>
    </div>
    <script>
        // 子组件
        Vue.component('child-component', {
            template:
                `
            <div>
                <p>This is a child component.</p>
            </div>
            `,
            methods: {
                sayHello() {
                    console.log('Hello from child component!');
                }
            }
        });

        var vm = new Vue({
            el: '#app',
            data: {
                textColor: 'black',
                isChildVisible: true
            },
            methods: {
                toggleColor() {
                    this.textColor = this.textColor === 'black' ? 'red' : 'black';
                    // 通过 ref 访问子组件实例中的方法
                    this.$refs.childRef.sayHello();
                }
            }
        });
    </script>
</body>

</html>

在这里插入图片描述


3.依赖注入

不管是this.$parentthis.$refs,都可能会陷入深层嵌套的风险,我们可能会通过this.$parent.$parent.$parent来访问上层某个组件实例。这无疑是不合理的。

针对较深层级的组件访问,Vue设计了依赖注入的模式。它允许我们将一个依赖注入到某个组件里,以便组件可以访问这个依赖而不需要显示传递它。这对于共享全局配置或服务非常有用,例如国际化(i18n)配置、数据请求服务等。

具体的,我们通过在父组件provide选项提供依赖,然后在子组件的inject选项注入依赖。

示例:

//在父组件中
provide:{
	userSevice:new UserService()
}

//在子组件中
inject:['userService'],
created(){
    this.userService.doSomething()
}

为什么我们不使用$root实现这种方案呢?我认为,依赖注入适合小范围的配置共享,而全局共享则适用于$root

程序化的事件监听器

$emit触发的事件可以被v-on监听,但这不是我们这章的内容。

本文主要介绍 Vue 2 中程序化的事件监听器,我们可以使用它来动态地添加和删除事件监听器。

Vue实例上有一系列方法来处理事件,包括$on$once$off。它们都包含两个参数:eventNameeventHandler$on侦听一个事件;$once只侦听一个事件一次;$off停止侦听一个事件。

$emit$on$off与浏览器的EventTarget API 中的dispatchEventaddEventListenerremoveEventListener并不等同。

$emit用于触发自定义事件,dispatchEvent用于触发各种DOM事件。addEventListenerremoveEventListener同理。

组件的循环引用

1.递归组件

我们可以在组件模板内部调用自身,这称为递归组件。递归组件要注意设置递归终止条件,例如v-if的值为false。

假设我们有一个表示文件夹结构的数据对象:

<!DOCTYPE html>
<html>

<head>
    <title>Vue 2 递归组件 Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <recursive-folder :folder="folder"></recursive-folder>
    </div>
    <script>
        Vue.component('recursive-folder', {
            props: ['folder'],
            template: `
                <div>
                    <span>{{ folder.name }}</span>
                    <div v-if="folder.children.length > 0">
                        <ul>
                            <li v-for="childFolder in folder.children" :key="childFolder.name">
                                <recursive-folder :folder="childFolder"></recursive-folder>
                            </li>
                        </ul>
                    </div>
                </div>
            `
        });
        var vm = new Vue({
            el: '#app',
            name: 'recursive-folder',
            data: {
                folder: {
                    name: 'Folder A',
                    children: [
                        {
                            name: 'Subfolder A.1',
                            children: [
                                {
                                    name: 'File A.1.1',
                                    children: []
                                },
                                // 可能有更多子项
                            ]
                        },
                        {
                            name: 'File A.2',
                            children: []
                        },
                        // 可能有更多子项
                    ]
                }
            },
        });
    </script>
</body>

</html>

在这里插入图片描述


2.循环组件

循环组件是不同组件互相使用,递归组件是自身使用自身

使用Vue.component全局注册组件时,Vue帮助我们自动解决组件之间的依赖关系,因此你可以随意使用它们。

但是,当我们使用模块系统(如Webpack)时,便可能出现循环依赖的情况。

为了解决该问题,你可以在组件的beforeCreate生命周期钩子函数中动态注册依赖组件,或者使用异步组件加载来确保组件能够正确解析和注册:

1)在组件的beforeCreate生命周期钩子函数中动态注册依赖组件

beforeCreate(){
    this.$options.components.AnotherComponent=require("./another-component.vue").default
}

2)使用异步组件加载

参考Vue 2 动态组件和异步组件一文。


模板定义的新方式

通常,我们使用组件内的template选项定义模板字符串,或者在.vue文件的<template>元素中定义模板。

1.内联模板

如果在子组件上添加inline-template attribute,那么子组件将使用其内部内容作为模板,而不会将其作为插槽内容

<child-component inline-template>
	<div>
        <!--模板内容-->
    </div>
</child-component>

2.X-Template

<script type="text/x-template" id="my-template">
	<div>
		<!--模板内容-->
	</div>
</script>
Vue.component('my-component',{
    template:'#my-template'
})

这两种方式适用于我们做做Demo或很小的项目。

控制更新

控制更新指强制更新阻止更新


1.强制更新

使用$forceUpdate方法强制更新一个组件,即使没有任何状态变化也会触发重新渲染。

不过,只有调用它的组件实例及该实例中的子组件的插槽内容会被强制更新,子组件本身不受影响。

通过递归算法可以强制更新整个组件树:

function foreceUpdateRecursive(vm){
    vm.$forceUpdate()
    vm.$children.forEach(child=>{
        foreceUpdateRecursive(child)
    })
}

2.阻止更新:使用v-once创建低开销的静态组件

静态意味着数据不再改变,也就不用重新渲染。

v-once保证了只渲染该元素和其子元素一次,且将它们的数据缓存起来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值