Vue-基础入门(中)--Part.1(6-10)

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>

需要注意的是:

  1. 使用具名插槽的时候,插入的内容要用<template>占位符包裹一下
  2. 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-itemcommon-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 基础语法知识点查漏补缺

  1. 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

  2. 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>
    

    在这里插入图片描述

  3. 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理解到这里就差不多了,更深层的东西后面再讲^ ^

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值