1-6 组件间双向绑定高级内容
先提一下,v-model
只能绑定一个值,我们不能<div v-model="a" v-moldel="b">
这样写是错误的!
当我们父子组件通信时,一个值用v-model
,那其他的值就只能用“老方法”传,但是上面也提到过“重命名”的方式,我们可以用这种方法来“拓展”v-model
<script>
const app = Vue.createApp({
data () {
return {
count: 1,
count1: 200
}
},
template: `
<counter v-model:add="count" v-model:minus="count1"/>
`
})
app.component('counter', {
props:['add', 'minus'],
methods: {
handleClick() {
this.$emit('update:add', this.add + 2)
},
handleClick1() {
this.$emit('update:minus', this.minus - 2)
},
},
template: `
<div @click="handleClick">{{add}}</div>
<div @click="handleClick1">{{minus}}</div>
`
})
const vm = app.mount('#root');
</script>
然后就是提醒一下v-model
后面可以加修饰符,前面提到过的,比如v-model.lazy
,这些修饰符是在input
框里生效的,如果我们在其他标签上添加修饰符属于是自定义修饰符,举个🌰
<script>
const app = Vue.createApp({
data () {
return {
word: 'a'
}
},
template: `
<counter v-model.uppercase="word"/>
`
})
app.component('counter', {
props:{
'modelValue': String,
'modelModifiers': {
default: () => ({})
}
},
methods: {
handleClick() {
let newValue = this.modelValue + 'b'
if(this.modelModifiers.uppercase) {
newValue = newValue.toUpperCase()
}
this.$emit('update:modelValue', newValue)
},
},
template: `
<div @click="handleClick">{{modelValue}}</div>
`
})
const vm = app.mount('#root');
</script>
解读一下:
modelModifiers
用来接收自定义修饰符,default
也就是默认值,意思是:如果没有写默认值,就返回一个空对象,如果就,自定义的修饰符就放在返回的对象里
handleClick
里就不解释了自己看得懂^ ^
1-7 使用插槽和具名插槽解决组件内容传递问题
<script>
const app = Vue.createApp({
template: `
<myform />
<myform />
`
})
app.component('myform', {
template: `
<div>
<input />
<button>提交</button>
</div>
`
})
const vm = app.mount('#root');
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WtMqlF9H-1640165554897)(E:/typora%E5%9B%BE%E7%89%87/image-20211112223156383.png)]
假如我想让第一个输入框提交不是按钮而是div
,一个很麻烦的方法就是再写一个子组件。很麻烦!!这时候 插槽 出现了!!那么插槽怎么用呢?。? 举个🌰
<script>
const app = Vue.createApp({
template: `
<myform>
<div>提交</div>
</myform>
<myform>
<button>提交</button>
</myform>
`
})
app.component('myform', {
template: `
<div>
<input />
<slot></slot>
</div>
`
})
const vm = app.mount('#root');
</script>
也就是说<slot></slot>
替换掉<div>提交</div>
和<button>提交</button>
这两个内容
也就是写在子组件标签里的内容都可以被<slot></slot>
替换掉
但是需要注意一点就是<slot></slot>
上面不可以绑定事件,比如:
<slot @click="handleClick"></slot>
这样写是错误的,那如果要绑定事件怎么办捏(。・ω・。)
<span @click="handleClick">
<slot />
</span>
这样在外面包裹一个span
标签就行了
然后就是写插值表达式时需要注意的:
<script>
const app = Vue.createApp({
data () {
return {
text: '提交'
}
},
template: `
<myform>
<div>{{text}}</div>
</myform>
`
})
app.component('myform', {
data () {
return {
text: 'heloo'
}
},
template: `
<div>
<input />
<slot />
</div>
`
})
const vm = app.mount('#root');
</script>
子组件和父组件都有text
变量,那么到底是用的哪一个呢?
用的是父组件自己的text
!!
注意:(slot
中使用的数据,作用域的问题)
父模板里调用的数据属性,使用的都是父模板里的数据
子模板里调用的数据属性,使用的都是子模板里的数据
再看一个代码:
<script>
const app = Vue.createApp({
data () {
return {
text: '提交'
}
},
template: `
<myform>
<div>{{text}}</div>
</myform>
<myform>
</myform>
`
})
app.component('myform', {
template: `
<div>
<input />
<slot></slot>
</div>
`
})
const vm = app.mount('#root');
</script>
当子组件里没有写内容的时候,就什么也不展示,但是我想要的效果是:就算我不插入内容,但是也有默认的东西展示出来,我们只需要这样做:
<slot>hello</slot>
里面的值就是默认值(✪ω✪)
最后再讲一下 具名插槽,先举个例子(๑>︶<)و
<script>
const app = Vue.createApp({
template: `
<content>
</content>
`
})
app.component('content', {
template: `
<div>
<div>main</div>
</div>
`
})
const vm = app.mount('#root');
</script>
我想实现的是,在main
上面插入header
,下面插入footer
,想一下
const app = Vue.createApp({
template: `
<content>
<div>header</div>
<div>footer</div>
</content>
`
})
app.component('content', {
template: `
<div>
<slot></slot>
<div>main</div>
<slot></slot>
</div>
`
})
这样行吗?(ง •_•)ง
嘻嘻 这样是不行的,看一下运行出来的结果
子组件里的内容作为一个整体放在了插槽里面
所以我们需要使用具名插槽来做到把内容精准的放在该放的位置
<script>
const app = Vue.createApp({
template: `
<content>
<template v-slot:header>
<div>header</div>
</template>
<template v-slot:footer>
<div>header</div>
</template>
</content>
`
})
app.component('content', {
template: `
<div>
<slot name="header"></slot>
<div>main</div>
<slot name="footer"></slot>
</div>
`
})
const vm = app.mount('#root');
</script>
需要注意的是:
- 使用具名插槽的时候,插入的内容要用
<template>
占位符包裹一下 v-slot:xx
不是v-slot="xx"
完美٩(๑>◡<๑)۶
小tips:v-slot:xx
可以简写成#xx
1-8 作用域插槽
我想循环展示一个数组里的内容,我们可以:
<script>
const app = Vue.createApp({
template: `
<list>
</list>
`
})
app.component('list', {
data () {
return {
list: ['1', '2', '3']
}
},
template: `
<div>
<div v-for="item in list">{{item}}</div>
</div>
`
})
const vm = app.mount('#root');
</script
但是这时候,哎,我不想写的那么死,我不仅想在div
里循环,我还想在span
标签里循环,或者h1
这时候就要用到作用域插槽啦,我们可以这样写(๑╹◡╹)ノ"""
<script>
const app = Vue.createApp({
template: `
<list v-slot="slotProps">
<div>{{slotProps.item}}</div>
</list>
`
})
app.component('list', {
data () {
return {
list: ['1', '2', '3']
}
},
template: `
<div>
<slot v-for="item in list" :item="item" />
</div>
`
})
const vm = app.mount('#root');
</script>
理一下,父组件在调用子组件的时候,子组件通过:item="item"
将值传给父组件,然后我们用v-slot="slotProps"
接收子组件传过来的值,在div
里循环展示
我们可以任意修改循环展示的标签:
<list v-slot="slotProps">
<li>{{slotProps.item}}</li>
</list>
还有一个小tips就是我们还可以用解构赋值的方法来接收子组件传过来的值
<list v-slot="{item}">
<li>{{item}}</li>
</list>
这样写也是ok的(´・︶・`)
1-9 动态组件和异步组件
先说 动态组件
<script>
const app = Vue.createApp({
data () {
return {
currentItem: 'commonItem'
}
},
methods: {
handleBtnClick() {
this.currentItem === 'commonItem' ? this.currentItem = 'inputItem' : this.currentItem = 'commonItem'
}
},
template: `
<input-item v-show="currentItem === 'inputItem'" />
<common-item v-show="currentItem === 'commonItem'" />
<button @click="handleBtnClick">切换</button>
`
})
app.component('input-item', {
template: `
<input />
`
})
app.component('common-item', {
template: `
<div>hello</div>
`
})
const vm = app.mount('#root');
</script>
这一段代码实现的功能就是点击按钮在input-item
和common-item
两个子组件间切换
这样写不太“优雅”,假如我们不止切换两个,有很多,这样写就很麻烦。我们可以换个方式来,也就是 动态组件٩(๑❛ᴗ❛๑)۶
<component :is="currentItem" />
<button @click="handleBtnClick">切换</button>
这样看起来是不是简洁很多 ^ ^
还有个小细节的地方,就是假如我们已经在input
框里输入了一些内容,点击切换,再点击切换,回来发现input
框里的内容已经没有了,那要怎么做呢?这里就引出了<keep-alive>
,这个挺重要的,后面源码会细讲,这里先了解一下
<keep-alive>
<component :is="currentItem" />
</keep-alive>
<button @click="handleBtnClick">切换</button>
这样写就ok啦
说了这么多,那动态组件是什么呢?
**动态组件:**根据数据的变化,结合component
这个标签,来随时动态切换组件的显示
异步组件
先看下面这段代码:
<script>
const app = Vue.createApp({
template: `
<common-item />
`
})
app.component('common-item', {
template: `
<div>this is a synchronization component</div>
`
})
const vm = app.mount('#root');
</script>
父组件调用子组件,就直接展示子组件的内容,这叫做同步组件,那么异步组件是怎么回事?
<script>
const app = Vue.createApp({
template: `
<common-item />
<async-common-item />
`
})
app.component('common-item', {
template: `
<div>this is a synchronization component</div>
`
})
app.component('async-common-item', Vue.defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
template: `<div>this is an async component</div>`
})
}, 4000)
})
}))
const vm = app.mount('#root');
</script>
4s后就会展示<async-common-item>
组件的内容,记住调用Vue.defineAsyncComponent()
这个方法创建异步组件^ ^关于Promise
函数(后面在其他专栏会补充)
**异步组件:**异步执行某些组件的逻辑
1-10 基础语法知识点查漏补缺
-
v-once
让某个元素标签只渲染一次
<script> const app = Vue.createApp({ data () { return { count: 1 } }, methods: { handleClick() { this.count += 1 } }, template: ` <div @click="handleClick" v-once>{{count}}</div> ` }) const vm = app.mount('#root'); </script>
点击
div
标签,count
不会变化,仍然为1 -
ref
,可获取 Dom 节点 / 组件引用的一个语法(不建议频繁进行Dom操作)获取 Dom 节点
<script> const app = Vue.createApp({ data () { return { count: 1 } }, mounted () { console.log(this.$refs.count) }, template: ` <div ref="count" >{{count}}</div> ` }) const vm = app.mount('#root'); </script>
获取组件引用
<script> const app = Vue.createApp({ mounted () { console.log(this.$refs.child.count) }, template: ` <child ref="child" /> ` }) app.component('child', { data () { return { count: 1 } }, methods: { handleClick() { this.count += 1 } }, template:` <div @click="handleClick">这是子组件{{count}}</div> ` }) const vm = app.mount('#root'); </script>
-
provide / inject
:跨组件、多组件之间值的传递我们知道父子组件之间的传值使用
props
,那如果子组件里还有子组件呢,看下面代码:<script> const app = Vue.createApp({ data () { return { count: 123546 } }, template: ` <child :count="count" /> ` }) app.component('child', { props: ['count'], template:` <child-child :count="count" /> ` }) app.component('child-child', { props: ['count'], template: ` <div>{{count}}</div> ` }) const vm = app.mount('#root'); </script>
假如层级很深,这一层一层的传真的巨!麻!烦!
这里就要介绍 provide/inject 了,我们看一下用法
<script> const app = Vue.createApp({ provide: { count: 12345 }, template: ` <child /> ` }) app.component('child', { template:` <child-child /> ` }) app.component('child-child', { inject: ['count'], template: ` <div>{{count}}</div> ` }) const vm = app.mount('#root'); </script>
这样是不是很方便^ ^
但是这个也是有弊端的:
假如我们想传
data
里的值,就需要把provide
写出一个函数<script> const app = Vue.createApp({ data () { return { count: 123456 } }, provide() { return { count: this.count } }, template: ` <child /> ` }) app.component('child', { template:` <child-child /> ` }) app.component('child-child', { inject: ['count'], template: ` <div>{{count}}</div> ` }) const vm = app.mount('#root'); </script>
然后就是,传的这个
count
不是动态的,举个🌰<script> const app = Vue.createApp({ data () { return { count: 123456 } }, provide() { return { count: this.count } }, methods: { handleClick() { this.count += 1 } }, template: ` <child @click="handleClick" /> ` }) app.component('child', { template:` <child-child /> ` }) app.component('child-child', { inject: ['count'], template: ` <div>{{count}}</div> ` }) const vm = app.mount('#root'); </script>
实际上
count
已经发生变化了,但是子子组件里展示的内容没有更新。关于
provide/inject
理解到这里就差不多了,更深层的东西后面再讲^ ^