Vue学习-组件和生命周期

前言

本人准备大四,做一些项目学习一些新的技能,如果文章有写的不好的或者不对的地方欢迎评论或者私信指出,谢谢!
欢迎参观本人的个人博客:https://linzyblog.netlify.app/

一、组件基础

通常一个应用会以一棵嵌套的组件树的形式来组织:
在这里插入图片描述
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

1、为什么我们要用组件

了解传统方式到组件化方式的更新。
在这里插入图片描述

传统方式编写网页,会导致依赖关系混乱,代码复用率低,所以我们需要用到组件,不同组件的用来实现局部功能的代码和资源。

在这里插入图片描述

2、基本使用

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。至此,我们的组件都只是通过 component 方法全局注册的:

1)全局注册

组件是用来实现局部功能的代码和资源的集合,是带有名称的可复用实例

const app = Vue.createApp({})

app.component('my-component-name', {
  // ... 选项 ...
})

全局注册的组件可以在应用中的任何组件的模板中使用。

2)局部注册

父组件 App.vue
子组件 header.vue、message.vue、bottom.vue

  • header.vue
<script>
export default {
    data() {
        return {
            msg: "这里是头部子组件"
        }
    }
}
</script>

<template>
    <h1>{{ msg }}</h1>
</template>
  • message.vue
<script>
export default {
    data() {
        return {
            msg: "这里是内容子组件"
        }
    }
}
</script>

<template>
    <h1>{{ msg }}</h1>
</template>
  • bottom.vue
<script>
export default {
    data() {
        return {
            msg: "这里是底部子组件"
        }
    }
}
</script>

<template>
    <h1>{{ msg }}</h1>
</template>
  • App.vue
<script>
//声明式渲染,可以提高开发效率
//导入各个组件
import heade from "./components/header.vue"
import bottom from "./components/bottom.vue"
import message from "./components/message.vue"
export default {
  components: {
    heade,
    bottom,
    message,
  }
};
</script>

<template>
  <div>
    <heade></heade>
  </div>
  <div>
    <message></message>
  </div>
  <div>
    <bottom></bottom>
  </div>
</template>

在这里插入图片描述

3、通过 Prop 向子组件传递数据

Prop 是你可以在组件上注册的一些自定义 attribute。为了给hello子组件传递一个message,我们可以用 props 选项将其包含在该组件可接受的 prop 列表中:

1)传递值

App.vue

<script>
//声明式渲染,可以提高开发效率
import hello from './components/hello.vue'
export default {
  data() {
    return {
    }
  },
  components: {
    hello
  }
};
</script>

<template>
  <div>
    <!-- 组件复用 -->
    <hello title="我是头部"></hello>
    <hello title="我是内容"></hello>
    <hello title="我是底部"></hello>
  </div>
</template >

hello.vue

<script>
export default {
    props: ['title'],
}
</script>
<template>
    <div>
        <h1>{{ title }}</h1>
    </div>
</template>

在这里插入图片描述

当一个值被传递给一个 prop attribute 时,它就成为该组件实例中的一个 property。该 property 的值可以在模板中访问,就像任何其他组件 property 一样。
一个组件可以拥有任意数量的 prop,并且在默认情况下,无论任何值都可以传递给 prop。

2)用 v-bind 来动态传递 prop

App.vue

<script>
//声明式渲染,可以提高开发效率
import hello from './components/hello.vue'
export default {
  data() {
    return {
      message: "hello vue",
    }
  },
  components: {
    hello
  }
};
</script>

<template>
  <div>
    <hello :msg="message"></hello>
    <h1>父组件中</h1>
    <input type="text" v-model="message">
  </div>
</template >

hello.vue

<script>
export default {
    props: ['msg'],
}
</script>
<template>
    <div>
        <h1>hello子组件中</h1>
        <h1>{{ msg }}</h1>
    </div>
</template>

在这里插入图片描述

我们传递给子组件的值是双向绑定的,我在父组件更新内容,子组件也会同样获取更新后的内容

4、监听子组件事件

我们在开发子组件时,它的一些功能可能需要与父级组件进行沟通,例如我在子组件改变特定值时,我们想要父组件也同样接收到,就需要父组件去监听子组件事件

父组件去监听子组件事件

  1. 在子组件中设置方法,通过$emit来触发事件
  2. 在父组件中,通过v-on监听子组件中自定义的事件,接收子组件传来的值

App.vue

<script>
import Content from './components/Content.vue'
//声明式渲染,可以提高开发效率
export default {
  data() {
    return {
      message: "hello vue",
    }
  },
  components: {
    Content
  },
  methods: {
    getMsg: function (msg) {
      console.log(msg);
      this.message = msg
    }
  }
};
</script>

<template>
  <div>
    <!-- 拿到子组件Content的数据,通过自定义事件 -->
    <!-- 2、在父组件中,通过v-on监听子组件中自定义的事件 -->
    <content @injectMsg="getMsg"></content>
    <h1>{{ message }}</h1>
  </div>
</template>

父级组件可以像处理原生 DOM 事件一样通过 v-on 或 @ 监听子组件实例的任意事件

content.vue

<script>
import Hello from './HelloWorld.vue'
export default {
    data() { //让每个组件都返回一个新的对象,不会造成数据污染
        //局部变量
        return {
            msg: "我是content",
            list: [1, 2, 3, 4]
        }
    },
    components: {
        Hello
    },
    methods: {
        //1、在子组件中,通过$emit来触发事件
        sendParent: function () {
            // this.emit('自定义事件名称', '发送参数')
            this.$emit('injectMsg', this.msg)
        }
    }
}
</script>

<template>
    <div>
        <!-- 组件是带有名称的可复用实例,带有单独模板的封装 -->
        <Hello :message="msg" aaa="123" :list="list"></Hello>
        <h2>我是content组件</h2>
        <h2>{{ msg }}</h2>
        <button @click="msg = '你好'">改变msg</button>
        <!-- <Hello></Hello> -->
        <button @click="sendParent">发送数据到父组件</button>
    </div>
</template>

同时子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件

helloworld.vue

<script>
export default {
  data() {
    return {
      msg: "我是hello"
    }
  },
  props: {//对象
    //1、类型限制
    message: String,
    //2、设置默认值、必须传值
    message: {
      type: String,
      default: "我是linzy",
      required: true,
    },
    list: {
      type: Array,
      default: [],
    }
  },
  mounted() {
    console.log(this.$parent);
    console.log(this.$root);
  }
}
</script>

<template>
  <div>
    <h4>hello</h4>
    <h1>{{ message }}</h1>
    <!-- <h1>{{ aaa }}</h1> -->
    <h1>{{ list }}</h1>
  </div>
</template>

我们可以把props变为对象形式,这样我们在接收到的值进行条件限定,可以限定类型、设置默认值、必须传入值等。

在这里插入图片描述
在这里插入图片描述

5、插槽

和 HTML 元素一样,我们经常需要向一个组件传递内容,就是我们使用 < slot> 作为我们想要插入内容的占位符

1)插槽内容

App.vue

<script>
//声明式渲染,可以提高开发效率
import Content from './components/Content.vue'
export default {
  data() {
    return {
      message: "hello vue",
    }
  },
  components: {
    Content
  }
};
</script>

<template>
  <div>
    <!-- 传递字符串 -->
    <Content>我是插槽</Content>
    <!-- 传递一个按钮 -->
    <Content><button>按钮</button></Content>
    <!-- 传递一个文本框 -->
    <Content><input type="text"></Content>
  </div>
</template>

content.vue

<template>
    <div>
        <h1>我是Content组件</h1>
        <div>
            <slot></slot>
        </div>
    </div>
</template>

在这里插入图片描述

如果有多个值,同时放入组件进行替换时,一起作为替换元素

2)渲染作用域

父级模板里的所有内容都在父级作用域中编译 子模板里的所有内容都是在子作用域中编译

<todo-button>
  Delete a {{ item.name }}
</todo-button>

在这里插入图片描述

3)备用内容

有时为一个插槽指定备用 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 组件中:

<button type="submit">
  <slot>Submit</slot>
</button>

现在当我们在一个父级组件中使用 并且不提供任何插槽内容时

<submit-button></submit-button>

备用内容“Submit”将会被渲染:

<button type="submit">
  Submit
</button>

但是如果我们提供内容,则这个提供的内容将会被渲染从而取代备用内容

4)具名插槽

有时我们需要多个插槽,具名插槽可以根据slot的name进行分配,< slot> 元素有一个特殊的 attribute:name。通过它可以为不同的插槽分配独立的 ID,也就能够以此来决定内容应该渲染到什么地方。

<template>
    <div>
        <h1>我是Content组件</h1>
        <div>
            <slot></slot>
            <slot name="header"></slot>
            <slot name="main"></slot>
            <slot name="bottom"></slot>
        </div>
    </div>
</template>

一个不带 name 的 出口会带有隐含的名字“default”

在向具名插槽提供内容的时候,我们可以在一个 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

<script>
//声明式渲染,可以提高开发效率
import Content from './components/Content.vue'
export default {
  components: {
    Content
  }
};
</script>

<template>
  <div>
    <Content>
      <!-- v-slot 只能添加在 <template> 上  -->
      <template v-slot:default>
        <p>我不是具名插槽.</p>
      </template>
      <template v-slot:header><button>header按钮</button></template>
      <template v-slot:main><input type="text" value="main文本框"></template>
      <template v-slot:bottom>
        <h1>我是bottom插槽</h1>
      </template>
    </Content>
  </div>
</template>

在这里插入图片描述

注意,v-slot 只能添加在 < template> 上

5)作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的。当一个组件被用来渲染一个项目数组时,这是一个常见的情况,我们希望能够自定义每个项目的渲染方式。

简而言之,可能每个页面插槽渲染的数据可能会不同,所以我们通过访问子组件中的数据来渲染,来实现相同的效果

App.vue

<script>
//声明式渲染,可以提高开发效率
import Content from './components/Content.vue'
export default {
  components: {
    Content
  }
};
</script>

<template>
  <div>
    <!-- 无序列表 -->
    <Content>
      <template v-slot:default="slotProps">
        <ul>
          <li v-for="item in slotProps.item" :key="item">{{ item }}</li>
        </ul>
      </template>
    </Content>
    <!-- 有序列表 -->
    <Content>
      <template v-slot:default="slotProps">
        <ol>
          <li v-for="item in slotProps.item" :key="item">{{ item }}</li>
        </ol>
      </template>
    </Content>
  </div>
</template>

content.vue

<script>
export default {
    data() {
        return {
            arr: [1, 3, 6, 1, 7]
        }
    }

}
</script>

<template>
    <div>
        <h1>我是Content组件</h1>
        <div>
            <slot :item="arr"></slot>
        </div>
    </div>
</template>

在这里插入图片描述
在这里插入图片描述

6、跨级通信Provide / Inject

**通常,当我们需要从父组件向子组件传递数据时,我们使用 props。**想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。

对于这种情况,我们可以使用一对 provide 和 inject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。
在这里插入图片描述
例如,我们有这样的层次结构:

Root
└─ TodoList
   ├─ TodoItem
   └─ TodoListFooter
      ├─ ClearTodosButton
      └─ TodoListStatistics

如果要将 todo-items 的长度直接传递给 TodoListStatistics,我们要将 prop 逐级传递下去:TodoList -> TodoListFooter -> TodoListStatistics。通过 provide/inject 的方式,我们可以直接执行以下操作:

  1. 祖先组件用provide值传递
  2. 子孙组件用inject接收值传递

1)值传递/引用传递

App.vue

<script>
import Content from "./components/Content.vue"
//声明式渲染,可以提高开发效率
export default {
  data() {
    return {
      message: "hello vue",
      obj: {
        message: "hello linzy",
      }
    }
  },
  components: {
    Content
  },
  //provide/inject并不是响应式的
  // provide: { message: this.message }
  //如果想去访问组件实例的属性
  provide() {
    return {
      //值传递
      message: this.message,
      //引用传递 
      //1、响应式对象方式
      obj: this.obj
    }
  }
};
</script>

<template>
  <div>
    <h1>HomeView----{{ message }}</h1>
    <h1>HomeView----{{ obj.message }}</h1>
    <p>--------------------</p>
    <Content></Content>
    <button @click="message = '勇敢牛牛'">改变message按钮</button>
    <button @click="obj.message = '哈哈哈'">改变对象里的message按钮</button>
  </div>
</template>


content.vue

<script>
export default {
    //值传递
    inject: ['message'],
    //引用传递
    inject: ['obj']
}
</script>

<template>
    <h2>hello---{{ obj.message }}</h2>
    <h1>{{ message }}</h1>
</template>

在这里插入图片描述
注意:值传递就相当于,在子组件开辟了新的属性变量,跟父组件的属性变量没有关系了,引用传递,可以看做c语言里的指针的概念,传递了一个地址,都是属于一个内存空间的,所以子组件里对象的属性变量改变了,父组件的也会改变

父组件不需要知道哪些子组件使用了它 provide 的 property
子组件不需要知道 inject 的 property 来自哪里

2)处理响应性

这是因为默认情况下,provide/inject 绑定并不是响应式的
我们可以通过传递一个 ref property 或 reactive 对象给 provide 来改变这种行为。在我们的例子中,如果我们想对祖先组件中的更改做出响应。
App.vue

<script>
import Content from "./components/Content.vue"
//声明式渲染,可以提高开发效率
export default {
  data() {
    return {
      message: "hello vue",
      obj: {
        message: "hello linzy",
      }
    }
  },
  components: {
    Content
  },
  //provide/inject并不是响应式的
  // provide: { message: this.message }
  //如果想去访问组件实例的属性
  provide() {
    return {
      //函数返回响应式数据
      message: () => this.message,
    }
  }
};
</script>

<template>
  <div>
    <h1>HomeView----{{ message }}</h1>
    <p>--------------------</p>
    <Content></Content>
    <button @click="message = '勇敢牛牛'">改变message按钮</button>
  </div>
</template>

content.vue

<script>
export default {
    //值传递
    inject: ['message'],
}
</script>

<template>
    <h1>{{ message() }}</h1>
</template>

在这里插入图片描述

二、生命周期

在这里插入图片描述

  1. 实例化一个Vue对象
  2. 初始化事件和生命周期
  3. 执行beforeCreate钩子函数(Vue实例还没创建,获取不到Dom节点,拿不到data> 数据和methods方法)
  4. 初始化响应式、数据代理和数据监测
  5. 执行created钩子函数(Vue实例已经创建完毕,data数据和methods方法这些已经成功绑定到Vue实例中,但是Dom元素还没有生成,还不能调用)
  6. 有没有template选项
    1)Yes,把template编译成渲染函数
    2)No,我们将el挂载的html编译成template
  7. 执行beforeMount钩子函数(页面还没渲染前,el还挂载在虚拟Dom里)
  8. 将编译好的html去替换掉el属性里的Dom对象 ,把虚拟Dom变为真实Dom放入页面
  9. 执行mounted钩子函数(页面已经渲染出来,用来获取数据或者发送网络请求)
  10. 实时监听数据变化,随时更新Dom,当我们的数据发生变化时
    1) 执行beforeUpdate钩子函数(在数据改变之前,虚拟Dom已经更新完了,做一些更新之前需要做的事情)
    2)虚拟Dom重新渲染成真实Dom,对比虚拟Dom和真实Dom节点的不同,找需要更新的节点,从而更新
    3)执行updated钩子函数(数据改变之后)
    4)直到Vue实例对象被销毁
  11. 执行beforeUnmout钩子函数(Vue实例销毁之前,data数据和methods方法之类还没有被销毁,还能调用,可以解绑事件监听或者清除掉定时器之类事件)
  12. 执行unmouted钩子函数(Vue实例销毁之后)

三、生命周期钩子

App.vue

<script>
//声明式渲染,可以提高开发效率
import content from './components/Content.vue'
export default {
  data() {
    return {
      message: "hello vue",
      isShow: true,
    }
  },
  components: {
    content
  }
};
</script>

<template>
  <div>
    <content v-if="isShow"></content>
    <button @click="isShow = !isShow">销毁hello组件</button>
  </div>
</template>

content.vue

<script>
export default {
    data() {
        return {
            counter: 0
        }
    },
    beforeCreate() {
        console.log('beforeCreate');
    },
    created() {
        console.log('created');
    },
    beforeMount() {
        console.log('beforeMount');
    },
    mounted() {
        console.log('mounted');
    },
    //页面渲染之后
    beforeUpdate() {
        console.log('beforeUpdate');
    },
    updated() {
        console.log('updated');
    },
    beforeUnmount() {
        console.log('beforeUnmount');
    },
    unmounted() {
        console.log('unmounted');
    }
}
</script>

<template>
    <div>
        <h1>hello</h1>
        <h1>{{ counter }}</h1>
        <button @click="counter++">按钮</button>
    </div>
</template>

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vue生命周期是指Vue实例从创建到销毁的整个过程中,会触发一系列的钩子函数。而组件生命周期是指组件在被创建、更新、销毁等过程中,会触发一系列的钩子函数。 Vue实例的生命周期包括以下几个阶段: 1. 创建阶段: - beforeCreate:实例刚在内存中被创建,数据观测和事件未初始化。 - created:实例已经完成数据观测和事件初始化,但尚未挂载到DOM上。 2. 挂载阶段: - beforeMount:实例已经完成编译,但尚未挂载到DOM上。 - mounted:实例已经挂载到DOM上,此时可以进行DOM操作。 3. 更新阶段: - beforeUpdate:数据更新时,虚拟DOM重新渲染之前调用。 - updated:虚拟DOM重新渲染和打补丁之后调用。 4. 销毁阶段: - beforeDestroy:实例销毁之前调用。 - destroyed:实例销毁后调用,此时Vue实例中的所有指令和事件监听器都已被移除。 组件生命周期Vue实例的生命周期类似,但有一些区别: 1. 创建阶段: - beforeCreate:同上。 - created:同上。 - beforeMount:同上。 - mounted:同上。 2. 更新阶段: - beforeUpdate:同上。 - updated:同上。 3. 销毁阶段: - beforeDestroy:同上。 - destroyed:同上。 此外,组件还有两个额外的生命周期钩子函数: - beforeUnmount:在组件卸载之前调用。 - unmounted:在组件卸载之后调用。 需要注意的是,在Vue 3中,beforeDestroy和destroyed被重命名为beforeUnmount和unmounted。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lin钟一

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

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

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

打赏作者

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

抵扣说明:

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

余额充值