2021-09-12 vue基础知识 (5)

动态组件

有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个新闻网站实现多新闻内容选项卡,可以通过 Vue 的 component元素加一个特殊的is 特性来实现:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id='app'>
    <button v-for='tab in tabs' :key='tab' @click='current_tab=tab'>{{tab}}</button>
    <!-- <component is='tab-Home'></component> -->
    <component :is='new_tap'></component>
  </div>

  <script src='./js/vue.js'></script>
  <script>
    Vue.component('tab-home', {
      template: '<div>家的组件</div>'
    })
    Vue.component('tab-canteen', {
      template: '<div>食堂的组件</div>'
    })
    Vue.component('tab-company', {
      template: '<div>公司的组件</div>'
    })
    const app = new Vue({
      el: '#app',
      data: {
        current_tab: 'home',
        tabs: ['home', 'canteen', 'company']
      },
      computed: {
        new_tap() {
          return 'tab-' + this.current_tab
        }
      }
    })
  </script>
</body>

在上述示例中,component相当于一个占位符,帮我们把位置占着,我们输入什么组件就显示什么组件
is后面的内容可以包括1.一个组件的名字 2.一个组件的选项对象

组件名注册名称要求

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id='app'>
    <!-- <myDemo></myDemo> -->
    <my-demo></my-demo>
  </div>
  <script src='./js/vue.js'></script>
  <script>
    Vue.component('myDemo', {
      template: '<div>mydemo</div>'
    })
    // Vue.component('my-demo', {
    //   template: '<div>my-demo</div>'
    // })

    const app = new Vue({
      el: '#app',
      data: {

      }
    })
  </script>
</body>
</html>

props属性名大小写要求

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id='app'>
    {{name}}
    <cpn :give-cpn='name' :give_title='title'></cpn>
  </div>
  <script src='./js/vue.js'></script>
  <script>
    Vue.component('cpn', { //app的子组件
      template: '<div><h1>{{giveCpn}}{{give_title}}</h1>123</div>',
      props: ['giveCpn', 'give_title']
      // props: {
      //   give_cpn: String,
      //   give_hello: {
      //     type: String,
      //     default: '我是默认值',
      //   },
      // }
    })
    const app = new Vue({
      el: '#app',
      data: {
        name: '我是app里面的内容',
        title: 'hello'
      }
    })
  </script>
</body>
</html>

组件的属性Prop之单向数据流

所有的 prop 都使得其父子prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

每次父级组件发生更新时,子组件中所有的prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

<body>
  <div id='app'>
    {{title}}
    <cpn :give_cpn='title'></cpn>
  </div>

  <template id="demo">
    <div>
      <h1>props: {{give_cpn}}</h1>
      <input type="text" v-model='give_cpn'>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        title: 'hello啊!树哥.'
      },
      components: {
        cpn: {
          template: '#demo',
          props: {
            give_cpn: {
              type: String,
              default: '我是默认值'
            }
          },
        }
      }
    })
  </script>
</body>

请添加图片描述

如图所示,控制台向我们发出了警告,因为是警告,并不是报错,所以效果还是有的,从警告信息中我们可以知道,vue不希望我们在子组件中对props进行修改,并且推荐我们使用data或者computed计算属性去解决修改props的业务

一般来说, 会改变Prop的场景有如下两种:

  1. 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
<body>
  <div id='app'>
    {{title}}
    <cpn :give_cpn='title'></cpn>
  </div>
  <template id="demo">
    <div>
      <h1>props: {{give_cpn}}</h1>
      <input type="text" v-model='give_cpn'>
      <h1>data: {{d_give_cpn}}</h1>
      <input type="text" v-model='d_give_cpn'>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        title: 'hello啊!树哥.'
      },
      components: {
        cpn: {
          template: '#demo',
          props: {
            give_cpn: {
              type: String,
              default: '我是默认值'
            }
          },
          data() {
            return {
              d_give_cpn: this.give_cpn
            }
          }
        }
      }
    })
  </script>
</body>

我们还可以尝试一下,修改vue实例里面的数据

<body>
  <div id='app'>
    {{title}}
    <cpn :give_cpn='title' @give_cpn_change='give_cpn_change'></cpn>
  </div>
  <template id="demo">
    <div>
      <!-- 绑定v-model,vue警告不要这样做 -->
      <h1>props: {{give_cpn}}</h1>
      <input type="text" v-model='give_cpn'>
      <!-- 通过data修改 -->
      <h1>data: {{d_give_cpn}}</h1>
      <input type="text" v-model='d_give_cpn'>
      <!-- 修改vue实例数据 -->
      <hr>
      <h1>data: {{d_give_cpn}}</h1>
      <input type="text" :value='d_give_cpn' @input='fn_give_cpn'>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        title: 'hello啊!树哥.'
      },
      methods: {
        give_cpn_change(value) {
          this.title = value
        }
      },
      components: {
        cpn: {
          template: '#demo',
          props: {
            give_cpn: {
              type: String,
              default: '我是默认值'
            }
          },
          data() {
            return {
              d_give_cpn: this.give_cpn
            }
          },
          methods: {
            fn_give_cpn(event) {
              this.d_give_cpn = event.target.value
              this.$emit('give_cpn_change', this.d_give_cpn)
            }
          }
        }
      }
    })
  </script>
</body>

2.这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性

<body>
  <div id='app'>
    {{title}}
    {{num}}
    <cpn :give_cpn='title' :give_num='num'></cpn>
  </div>

  <template id="demo">
    <div>
      <h1>props: {{give_cpn}}</h1>
      <input type="text" v-model='give_cpn'>
      <h1>data: {{d_give_cpn}}</h1>
      <input type="text" v-model='d_give_cpn'>
      <hr>
      <!-- <h1>data: {{give_num}}</h1> -->
      <h1>computed: {{give_num_com}}</h1>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        title: 'hello啊!树哥.',
        num: 123
      },

      components: {
        cpn: {
          template: '#demo',
          props: {
            give_cpn: {
              type: String,
              default: '我是默认值'
            },
            give_num: {
              type: Number,
              default: 400
            }
          },
          data() {
            return {
              d_give_cpn: this.give_cpn
            }
          },
          computed: {
            give_num_com() {
              let new_num = this.give_num * 100
              return new_num
            }
          }
        }
      }
    })
  </script>
</body>

组件的属性Prop之非 Prop 的特性

一个非 prop 特性是指传向一个组件,但是该组件并没有相应prop 定义的特性。因为显式定义的prop 适用于向一个子组件传入信息,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上。

最常见的就是Vue项目和其他的UI框架结合一起用, 比如layui,elementUI啥的

<body>
  <div id='app'>
    <cpn :class='main' :get_msg='msg'></cpn>
  </div>
  <template id="demo">
    <div type='world'>{{get_msg}}</div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        main: 'main',
        msg: '我爱你,你爱我,蜜雪冰城甜蜜蜜',
      },
      components: {
        cpn: {
          template: '#demo',
          props: {
            get_msg: {
              type: String,
              default: '默认值'
            },
          }
        }
      }
    })
  </script>
</body>

组件的属性Prop之替换/合并已有的特性

对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。所以如果传入type=“hello" 就会替换掉type=“world" 并把它破坏!庆幸的是,class和 style特性会稍微智能一些,即两边的值会被合并起来,从而得到最终的值:

<body>
  <div id='app'>
    <cpn class="222" type='hello'></cpn>
  </div>

  <template id="demo">
    <div class="111" type='world'>111</div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {

      },
      components: {
        cpn: {
          template: '#demo'
        }
      }
    })
  </script>
</body>

组件的属性Prop之禁用特性继承

如果你不希望组件的根元素继承特性,你可以在组件的选项中设置inheritAttrs: false

<body>
  <div id='app'>
    <cpn :class='main' :get_msg='msg' type='world'></cpn>
  </div>
  <template id="demo">
    <div type='hello'>{{get_msg}}</div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        main: 'main',
        msg: '我爱你,你爱我,蜜雪冰城甜蜜蜜',
      },
      components: {
        cpn: {
          inheritAttrs: false,
          template: '#demo',
          props: {
            get_msg: {
              type: String,
              default: '默认值'
            },
          }
        }
      }
    })
  </script>
</body>

插槽的基本用法

Vue 实现了一套内容分发的API,这套 API 的设计灵感源自WebComponents 规范草案,将 元素作为承载分发内容的出口。它允许你像这样合成组件:当组件渲染的时候,将会被替换为我们自己再模板标签里面写的字。插槽内可以包含任何模板代码,包括HTML:

<body>
  <div id='app'>
    <cpn>
      <div type='123'>456</div>
    </cpn>
  </div>
  <template id="demo">
    <div>
      <div>我是cpn</div>
      <slot></slot>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
      },
      components: {
        cpn: {
          template: '#demo'
        }
      }
    })
  </script>
</body>

如果 没有包含一个 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。

插槽的编译作用域

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

<body>
  <div id='app'>
    <cpn>
      <div type='1![请添加图片描述](https://img-blog.csdnimg.cn/77b7d4e6b7c9463eafc0910f4e023a46.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5paw5pe25Luj5Yac5rCR5bel5a6Y5pa56K6k6K-B56CB5YacLS3oj5zpuK3lrZDmr5vmr5t-,size_9,color_FFFFFF,t_70,g_se,x_16)
3'>456</div>
      <i>{{msg_obj}}</i>
    </cpn>
  </div>

  <template id="demo">
    <div>
      <div>我是cpn</div>
      <i>{{msg}}</i>
      <slot></slot>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        msg_obj: '插槽外信息内容'
      },
      components: {
        cpn: {
          template: '#demo',
          data() {
            return {
              msg: '插槽内信息内容'
            }
          }
        }
      }
    })
  </script>
</body>

插槽的默认内容

有时为一个插槽设置具体的后备(也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。

<body>
  <div id='app'>
    <cpn>
      <!-- <div type='123'></div> -->
      <!-- <i>{{msg_obj}}</i> -->
    </cpn>
  </div>

  <template id="demo">
    <div>
      <div>我是cpn</div>
      <i>{{msg}}</i>
      <slot>我是默认内容</slot>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        msg_obj: '插槽外信息内容'
      },
      components: {
        cpn: {
          template: '#demo',
          data() {
            return {
              msg: '插槽内信息内容'
            }
          }
        }
      }
    })
  </script>
</body>

具名插槽

有时我们需要多个插槽 元素有一个特殊的特性:name。这个特性可以用来定义额外的插槽:一个不带name 的出口会带有隐含的名字“default”。

比如说我们现在有一个需求是:要把头部区域和底部区域用插槽的形式填入,如果我们直接填入的话:

<body>
  <div id='app'>
    <cpn>
      <template>
        头部
      </template>
      <template>
        底部
      </template>
    </cpn>
  </div>

  <template id="demo">
    <div>
      <div>我是cpn</div>
      <header>
        <slot></slot>
      </header>
      <footer>
        <slot></slot>
      </footer>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        msg_obj: '插槽外信息内容'
      },
      components: {
        cpn: {
          template: '#demo',
          data() {
            return {
              msg: '插槽内信息内容'
            }
          }
        }
      }
    })
  </script>
</body>

结果是:

请添加图片描述

这显然不是我们想要的结果,我们可以给插槽取一个名字:

<body>
  <div id='app'>
    <cpn>
      <template v-slot:header>
        头部
      </template>
      <template v-slot:footer>
        底部
      </template>
    </cpn>
  </div>

  <template id="demo">
    <div>
      <div>我是cpn</div>
      <header>
        <slot name='header'></slot>
      </header>
      <footer>
        <slot name='footer'></slot>
      </footer>
    </div>
  </template>
  <script src='./js/vue.js'></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        msg_obj: '插槽外信息内容'
      },
      components: {
        cpn: {
          template: '#demo',
          data() {
            return {
              msg: '插槽内信息内容'
            }
          }
        }
      }
    })
  </script>
</body>



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值