Vue组件相关的知识点
Vue组件相关
学习内容:
Vue组件相关的知识点
1、 全局组件
2、 局部组件
3、 组件间相互传参
4、 组件相互访问
5、插槽
说点啥:
嗯~~又是充实的一天,今天学习了组件相关的知识点,记录下来,方便以后自己查阅,努力,加油,欧里给学习产出:
组件分类: 页面级组件、业务上可重复的基础组件、与业务无关的独立组件
全局组件的创建
简洁
app.component("box1",{
template:`
<div style="width:350px;height:200px;background-color:#f00">
<Mybox></Mybox>
</div>
`
})
具体示例
<script>
const app = Vue.createApp({
data(){return{msg:1}}
}
})
// 注册全局组件
// 全局组件可在当前实例中任意位置使用
app.component("Mybox",{
template:`
<div style="width:150px;height:100px;background-color:#faf"></div>
`
})
// 另一个全局组件
// 如果两个全局组件想要相互只用,需要在对应的组件的模板中调用
app.component("box1",{
template:`
<div style="width:350px;height:200px;background-color:#f00">
<Mybox></Mybox>
</div>
`
})
app.mount('#app')
局部组件
vue的局部组件只能用在注册其的页面上
eg:有两个子组件被vue实例引用了,这个vue实例可以用这两个子组件,但是这两个子组件不能相互使用,如果a子组件想用b子组件,那么需要在a中注册一下
let b={
template:`
<div style="width:350px;height:200px;background-color:#f00">
<a></a>
</div>
`,
components:{
a
}
}
组件间传参
注意:组件传参的属性名不能使用驼峰式,因为浏览器都会将其解析成小写
父→子
父组件中:
在调用子组件的时候 通过动态绑定属性的方式,将数据传过去,属性名和数据名最好一样
<div id="app">
<h1>父传子</h1>
<box :colleges="msg2" :branch="branch"></box>
</div>
子组件中:
在子组件中通过prop来接收父组件传过来的数据,prop里是父组件传过来的属性名,要加引号
<script>
const Box = {
template: "#box",
// 接收父祖件的数据
props: ["colleges", "branch"]
}
</script>
prop的类型
- 1.只指定名称
props: [“colleges”, “branch”] - 2.指定名称和类型
注意此时的prop已经是对象了
props:{
colleges:String,
branch:Number
} - 3.指定名称/类型/必要性/默认值
props:{
colleges:{type:String,required:true,default:xxx},
branch:{type:Number,required:true,default:xxx},
}
prop中数据的类型有
String/Number/Boolean/Array/Object/Date/Function/Symbol/自定义类型
子→父
使用场景,子组件更改某个操作希望通知父组件,让父组件做出操作、或子组件像父组件传递数据
子组件中:
创建子组件,当子组件里的按钮被点了触发btnClick事件,然后通过$emit会发射一个事件
<script>
const box = {
template: "#box",
methods:{
btnClick(){
alert("你是🐕🐕*10")
// 发射自定义事件
// 这里发射事件的名称不能是驼峰命名
// 两个参数,第一个是事件名称,第二个是数据
// 多个数据的传递一般采用对象或者数组的方式
// this.$emit("send-click","我是恁爹的数据","1","2")
// 可以发射多个不同的自定义事件
this.$emit("send-click",{name:"张三",age:13})
}
}
}
</script>
父组件中:
父组件绑定子组件发射的自定义事件,当子组件一发射这个事件,那么就会执行绑定的函数
<div id="app">
<box @send-click="clickFun"></box>
</div>
<script>
//methods中
methods:{
// 接收父组件自定义事件是没有事件对象的
// 子组件自定义函数中的形参不是事件对象,而是子组件传来的值
clickFun(val){
console.log("子组件的事件被点击了");
console.log(val);
},
}
</script>
组件间的访问
父组件访问子组件
在调用子组件的时候需要为其设置一个ref名称,代表该子组件
父组件:
<div id="app">
<!-- 这里需要给子组件绑定一个ref,代表当前组件的名称 -->
<lk-box ref="box1"></lk-box>
<lk-box ref="box2"></lk-box>
<lk-box ref="box3"></lk-box>
<button @click="getChildrenComponent">click me </button>
</div>
在调用该子组件的父组件中通过this.$refs.ref名访问
父组件:
<script>
methods:{
getChildrenComponent(){
// $refs每个实例都会有
// this.$refs.box1表示拿到box1组件了,然后可以其经行操作
console.log(this.$refs.box1.msg);
// this.$refs.box1.btnClick()
this.$refs.box2.msg="我偷偷的更改了信息呢"
}
}
</script>
子组件访问父组件
(不建议使用,破坏了子组件的封闭性)
设有两两个组件a和b,组件a引用了组件b,
此时组件a就是组件b的父组件,如果组件b
想要访问组件a,直接this.\$parent
(表示父组件a)即可,对其内容 . 操作即可。
访问根组件:this.$root
<script>
const Btn = {
template: `
<button @click="plus">点击了{{count}}</button>
`,
data() {
return {
count: 0,
}
},
methods: {
plus() {
this.count++;
// this.$parent代表引用当前子组件的父组件
console.log(this.$parent.a);//哈哈哈
// console.log(this.$parent.$parent.msg);//郑工学院
// this.$root表示访问根组件
console.log(this.$root.msg);
}
}
}
</script>
插槽
插槽 v-slot(匿名插槽和具名插槽)
插槽是组件里面的一个知识点,所谓插槽就是在子组件中预留一个位置,
然后父组件在调用子组件的时候,可以在这块位置经行操作。
(比如说,一个页头好多地方用但是里面的细节不同需要单独设置,
但是整个结构是不变的)
匿名插槽
子组件:
在子组件中用占位,slot标签中可以写东西,当父组件没有插入东西时,就使用默认的
<template id="box">
<div style="width: 200px;height:150px;background-color: #f00;">
<p>淘!淘你喜欢!</p>
<!-- <button>淘一下</button> -->
<!-- 预留插槽 -->
<slot>插槽的默认内容</slot>
</div>
</template>
<script>
const hhBox={
template:"#box",
}
</script>
父组件:
父组件在调用子组件的时候,在标签内部写东西,就会替换掉子组件的slot标签
<div id="app" >
<hhbox>
<button>淘一下</button>
</hhbox>
<hhbox>
<input type="text" placeholder="请输入内容">
</hhbox>
<hhbox>
</div>
具名插槽
子组件:
将每个插槽命名
<template id="box">
<!-- 头部 -->
<header>
<slot name="header">头部插槽</slot>
</header>
<!-- 主体 -->
<!-- 此插槽是匿名插槽 -->
<main>
<slot >主体插槽</slot>
</main>
<!-- 脚部 -->
<footer>
<slot name="footer">脚部插槽</slot>
</footer>
<p>======================================</p>
</template>
<script>
const MyBox = {
template: "#box",
}
</script>
父组件:
使用v-slot:插槽名即可将对应的内容替换到插槽中,template标签会被内容替换掉不占用位置
匿名插槽和具名插槽的相互使用:
在子组件中,有匿名插槽也有具名插槽,在父组件中,具名插槽用v-slot:插槽名占用,用v-slot:default的模板会自动替换到匿名插槽中
<div id="app">
<mybox>
<template v-slot:header>
<button>我是头部</button>
</template>
<!-- 使用v-slot:default可以将此模板替换为子组件的匿名插槽 -->
<template v-slot:default>
<input type="text" placeholder="我是主体">
</template>
<template v-slot:footer>
<img src="1.jpg">
</template>
</mybox>
</div>
渲染作用域
结论:父级模板里的所有内容都是在父级作用域中编译的
子级模板的所有内容都是在子级作用域中编译的
<div id="app">
<!-- div会显示,说明用的数据是父组件里面的 -->
//box是子组件 子组件和父组件中都有isShow,但是会调用父组件中的数据
<box v-show="isShow"></box>
</div>
作用域插槽:
子组件在预留插槽的时候,通过绑定属性的方式将子组的数据暂存到当前插槽中,父组件在使用插槽的时候通过变量接住数据,即可实现父组件中可以调用子组件的数据(直接在视图层经行操作),$refs必须实在js中经行操作
简而言之:数据是子组件的,由父组件传结构过来
子组件
通过绑定的属性的方式将数据绑定到插槽中
<template id="box">
<!-- data:属性名,即父组件接收数据对象的键名,nameArr是值,可自定义 -->
<slot :data="nameArr"></slot>
</template>
<script>
const box = {
template:"#box",
data(){
return{
nameArr:["张三","李四","王五","赵六"]
}
}
}
</script>
父组件
在调用子组件的时候,通过具名或者匿名插槽的方式,在其后面给个变量接住传过来的值,变量名可自定义,这个变量是一个对象,接住了子组件传来的数据
<box>
//v-slot:default表示调用匿名插槽,也可以用具名插槽
<template v-slot:default="slotProps">
<!-- slotProps代表子组件插槽传来的数据对象 -->
<!-- { "data": [ "张三", "李四", "王五", "赵六" ] } -->
<div> {{slotProps}}</div>
</template>
</box>
动态组件
当组件之间,切换的时候,有时候会想保持这些组件的状态,以避免反复重复渲染导致的性能问题
根据数据的变化,结合component标签,来动态切换组件的显示
用is属性动态绑定组件,
eg:按钮组件和输入框组件只能显示其一
首先创建两个全局组件
<script>
app.component("c-button", {
template: `
<button>百度一下</button>
`,
});
app.component("c-input", {
template: `
<input type="text" placeholder="请输入内容"/>
`,
})
</script>
然后在调用该组件的时候
通过component标签和is属性,is后面的变量名称与哪个组件名相同,就会显示哪个组件
keep-alive标签:失活组件可以缓存
失活:比如切换输入框显示/隐藏的时候切换前输入的内容会没有,用来这个标签就不会出现这种情况
<div id="app">
<keep-alive>
<component :is="cItem"></component>
</keep-alive>
<p>-----------</p>
<button @click="isShow">切换</button>
</div>
//vue实例中
<script>
data() {
return {
cItem: "c-button",
}
}
</script>
异步组件
在大型应用中,我们可能将应用分割成一些小块的代码,并且只在需要的时候才从服务器加载一个模块
Vue中通过Vue.defineAsyncComponent来创建里面必须用Promise语法
<script>
app.component("async-item", Vue.defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
template: `
<div style="background-color:#faf;">这是一个从服务器端异步拿过来的全局组件的数据</div>
`,
})
}, 2000)
})
}
))
</script>
生命周期函数
这里没写完,具体的可以去官方手册查看
侦听器watch
注意:要监听的数据和函数要同名
每个监听函数都有两个参数,一个代表当前的值,一个代表之前的值
<script>
const app = Vue.createApp({
data(){
return{
price:5,
}
},
watch:{
// 监听属性有两个参数 current:现在的值 prev之前的值
price(current,prev){
console.log(current,prev);
}
},
</script>
-计算属性和监听器的区别:
- 计算属性底层是靠watch实现的
- 计算属性在调用时需要在模板中渲染
- 计算属性默认深度依赖,watch默认浅度依赖
- 计算属性适合做筛选,不可异步,watch适合执行异步或开销较大的操作
单项数据流
vue中组件传参是单向数据
单项数据流:在Vue中都是组件化开发,比如父组件的数据要给子组件用,也要给孙组件以及更深层次的子组件,这时要保证子组件不能修改数据,否则其他的数据都会变,这就是数据单向流
组件传参的快捷语法
注意:传参的时候以对象的方式,对象的键名要与接收参数的组件的props里面的名称相对应,然后在要调用的子组件标签以v-bind的方式传入
<div id="app">
<p>键名必须和子组件中props中的名称对应</p>
<box v-bind="numObj"></box>
<!-- 使用简洁语法底层解析是这样的 -->
<!-- <box :num1="numObj.num1" :num2="numObj.num2" :num3="numObj.num3"></box> -->
</div>
<script>
const box = {
props:["num1","num2","num3"],
template: `
<div>数据1:{{num1}}</div>
<div>数据2:{{num2}}</div>
<div>数据3:{{num3}}</div>
`,
}
const app = Vue.createApp({
data() {
return {
numObj:{
num1:100,
num2:200,
num3:300
}
}
},
components: {
box
}
</script>
no-props
正统的数据传递,父组件在调用子组件的时候,通过属性绑定的方式将数据传递给子组件,子组件通过props接收即可,但是当想要传递一个样式或者class类的时候,用no-props传递比较简单,就是子组件内不用props接收
父组件:
<div id="app">
<box msg="我是" style="background-color:red"></box>
</div>
全局子组件中:
- 父组件传值过来,子组件不用props接收,那么就会子组件的模板的元素中添加对应的属性
- 一般用no-props给子组件传递样式或class,如果子组件没有props接收数据,这时就会多出来样式属性
结果:<div msg=“我是” style=“background-color:red”>我是全局子组件</div>
- 默认如果子组件内部只有一层包裹就会作用到爹身上,如果有多个平级元素no-props不生效
- 如果子组件的模板中有多个同一级别的标签,在每个标签上v-bind="$attrs",使得当前标签no-props生效
- 如果父组件传来多个属性,只需要个别,使用 :自定义名称="$attrs.想要的传来的属性名" eg: :style="$attrs.style"
- $attrs其实就是一个对象,用来接收传来的所有属性
- 使用inheritAttrs:false,不接受父组件传来的属性
<script>
app.component("box",{
// props:["msg"],
// 不接受no-props属性
// inheritAttrs:false,
template:`
<div :s1="$attrs.msg">我是全局子组件</div>
<div v-bind="$attrs">我是全局子组件</div>
`,
</script>
})
完结了这篇张,纪念一下,🍺🍺🍺,