【Vue】Vue基础自用笔记&Day04_①Vue组件②Vue插槽

1. Vue组件 - component

什么是组件:
组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可。

组件化和模块化的不同:
模块化:是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一
组件化:是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用

一个优秀的组件 / 模块应该做到:高内聚,低耦合

耦合性:每个模块之间相互联系的紧密程度,模块之间联系越紧密,则耦合性越高,模块的独立性就越差

内聚性:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即‘高内聚’

高内聚是说模块内部要高度聚合,低耦合是说模块与模块之间的耦合度要尽量低。 前者说的是模块内部的关系,后者说的是模块与模块之间的关系。 看似不同,实又相关。

如一个项目中有15个模块,需要在另一个项目中调用其中的一个模块,但是必须把全部的15个模块都移植过去才能使用,这就是高耦合,只移入所需的一个模块就可以使用,就是低耦合

然后在一个模块中,各个元素(语句、程序段等)之间的联系程度越高,则内聚性越高,即高内聚


定义全局组件

1.使用 Vue.extend 配合 Vue.component 方法

var login = Vue.extend({
    template: '<h1>登录</h1>'
});
//第一个为组件名称,第二个为绑定的组件的名称
Vue.component('login', login); 

2.直接使用 Vue.component 方法

Vue.component('register', {
  template: '<h1>注册</h1>'
});

3.将模板字符串,定义到script标签中,同时,需要使用 Vue.component 来定义组件

<script id="tmpl" type="x-template">
  <div><a href="#">登录</a> | <a href="#">注册</a></div>
</script>

<script>
	Vue.component('account', {
	  template: '#tmpl'
	});
</script>

4.将模板字符串,定义到template标签中

< template id="tmpl">
  <div><a href="#">登录</a> | <a href="#">注册</a></div>
</ template >

同时,需要使用 Vue.component 来定义组件

Vue.component('account', {
  template: '#tmpl'
});

一般只用第四种方式定义全局组件。

注意:
1.组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹,
  即最外层不能有同级的HTML标签,一般用一个 div 将其他的DOM元素包裹起来。
  
2.命名不能和 H5 中已有的标签重名,且驼峰命名不好使,因为HTML大小写不敏感,
  可以用-连接

定义私有组件

在Vue实例中添加 components 属性即可:

const vm = new Vue({
  el: '#app',
  data: {
    flag: true
  },
  methods: {
  },
  components: {
    "register": {
      template: "#register"
    },
    "test": {
      template: "#login"
    },
    // 必须写ID,不可以写类名
    // "lll": {
    //   template: ".lllllll"
    // }
  }
})

引用组件:

 <div id='app'>
   <register v-if="flag"></register>
   <test v-if="flag"></test>
 </div>

组件中数据和方法的调用

组件中也有data属性、methods属性、八个生命周期钩子函数等,但是data属性的属性值
由Vue实例中的对象变为了函数,并且函数中必须要有一个返回值,返回值里是组件可调用
的数据,如:
Vue.component("hello", {
  template: "#hello",
  data() {
    return {
      msg: "Hello World",
      nums: 666
    }
  },
  methods: {
    add() {
      this.nums += 6
    }
  }
})

其目的是为了组件复用时的数据隔离, 否则不同的组件都共用一个数据的话,一个发生变化每一个都会发生变化,一般情况下这不是我们想要的效果。

案例:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/vue-router@3.0.0/dist/vue-router.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.27.2/axios.js"></script>
</head>

<body>
  <div id='app'>
    <button @click="flag=!flag">销毁Hello</button>
    <hello v-if="flag"></hello>
  </div>
  <template id="hello">
    <div>{{msg}}
      <button @click="add">增6👉</button>
      <span>{{nums}}</span>
    </div>
  </template>
</body>
<script>
  Vue.component("hello", {
    template: "#hello",
    // 组件之间数据隔离
    data() {
      return {
        msg: "Hello World",
        nums: 666
      }
    },
    methods: {
      add() {
        this.nums += 6
      }
    },
    beforeCreate() {
      console.log("创造哈喽前");
    },
    created() {
      console.log("创造哈喽后,可以调用数据了");
    },
    beforeDestroy() {
      console.log("销毁哈喽前");
    },
    destroyed() {
      console.log("销毁哈喽后");
    }
  })
  const vm = new Vue({
    el: '#app',
    data: {
      flag: true
    },
    methods: {
    },
  })
</script>

</html>

使用 <component> 标签来引用组件,并通过is属性来指定要加载的组件,is属性也可进行数据绑定

//指定引用的组件为'hello'
<component is="hello"></component>
//加上冒号后,引号里的内容就变成了变量引用,需要注意单/双引号的添加
<component :is="flag?'hello':'nohello'"></component>

组件动画

<transition> 标签中有一个 mode 属性,来设置组件的动画是先出后入还是先入后出,先出后入为 mode="out-in" ,先入后出为 mode="in-out",通常先出后入的情况比较符合审美。

---------------------------------------
<p>先出后入的动画:</p>
<transition mode="out-in">
  <component :is="flag?'hello':'nohello'"></component>
</transition>
---------------------------------------
<p>先入后出的动画:</p>
<transition mode="in-out">
  <component :is="flag?'hello':'nohello'"></component>
</transition>

完整案例:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/vue-router@3.0.0/dist/vue-router.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.27.2/axios.js"></script>
</head>
<style>
  .v-enter,
  .v-leave-to {
    transform: translateX(200px);
  }

  .v-enter-active,
  .v-leave-active {
    transition: all 1s;
  }

  .v-enter-to,
  .v-leave {
    transform: translateX(0px);
  }
</style>

<body>
  <div id='app'>
    <button @click="flag=!flag">销毁Hello</button>
    <hello v-if="flag"></hello>
    <nohello v-else></nohello>
    -------------------------------------
    <component is="hello"></component>
    ---------------------------------------
    <!-- 注意是变量还是字符串 -->
    <component :is="flag?'hello':'nohello'"></component>
    ---------------------------------------
    <p>先出后入的动画:</p>
    <transition mode="out-in">
      <component :is="flag?'hello':'nohello'"></component>
    </transition>
    ---------------------------------------
    <p>先入后出的动画:</p>
    <transition mode="in-out">
      <component :is="flag?'hello':'nohello'"></component>
    </transition>
  </div>
  <template id="hello">
    <div>{{msg}}
      <button @click="add">增6👉</button>
      <span>{{nums}}</span>
    </div>
  </template>
  <template id="nohello">
    <div>
      ByeByeWorld
    </div>
  </template>
</body>
<script>
  Vue.component("hello", {
    template: "#hello",
    // 组件之间数据隔离
    data() {
      return {
        msg: "Hello World",
        nums: 666
      }
    },
    methods: {
      add() {
        this.nums += 6
      }
    },
  })
  Vue.component("nohello", {
    template: "#nohello"
  })
  const vm = new Vue({
    el: '#app',
    data: {
      flag: true
    },
    methods: {
    },
  })
</script>

</html>

也可以选择不使用 <component> 标签,而是直接使用组件加 v-ifv-else,如:

<p>先出后入的动画:</p>
 <transition mode="out-in">
   <hello v-if="flag"></hello>
   <nohello v-else></nohello>
 </transition>

父组件传值子组件

1.子组件中设置 props属性(和data同级),在props属性中设置接收数据的名称与类型,
可以使用数组设置多个接收类型,也可以使用 default函数 设置默认值;

Vue.component("course", {
  template: "#course",
  props: {
    type: String,
    'page-num': [Number, String],
    'page-size': [Number, String],
     test: {
        type: String,
        default: function () {
			return '这是一个默认的名字'
        }
      },
  },
  data() {
    return {
      courseList: [],
      baseUrl: "http://1.117.81.216:8086",
    }
  },
})

2.设置完成后在子组件标签中绑定props中定义的参数,属性值可以是父组件中的数据或者其他字符串,但是在标签中绑定参数时不能使用驼峰命名法,必须用kebaba-case命名法(即以-连接)

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/vue-router@3.0.0/dist/vue-router.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.27.2/axios.js"></script>
</head>

<body>
  <div id='app'>
  <!-- 子组件标签中绑定props中定义的参数,属性值可以是父组件的数据 -->
    <test :msgson="msgFather"></test>
  </div>
  <template id="test">
    <div>
      <p>{{msg}}------{{msgson}}</p>
    </div>
  </template>
</body>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      msgFather:"我是父组件的文字信息"
    },
    methods: {
    },
    components:{
      "test":{
        template:"#test",
        data(){
          return{
            msg:"我是子组件的文字信息",
          }
        },
        props:{
          msgson:String
        }
      }
    }
  })
  </script>
</html>

子组件传值父组件

子组件传值父组件需要我们在子组件标签中自定义传值事件,如:

<son @son-to-father="theFather" ref="myson"></son>

son-to-father 是在子组件的 methods 里写的方法,在方法中调用 this.$emit() 函数向父组件传值。

自定义事件不会自己调用,需要我们根据需求设置在什么情况下触发此事件去给父组件传值。

this.$emit() 的第一个参数是我们自己自定义的事件的名称,第二个参数是子组件给父组件传递的内容,可以是子组件 data 中的数据、一段字符串等等。

即:this.$emit(‘事件名称’,传递的数据)

theFather 是在父组件的 methods 里写的方法,触发sonToFather自定义事件后,会自动接收到子组件传递过来的数据,第一个参数就是接收到的数据,接收到后进行自己需要的操作即可。

代码举例:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
  <div id='app'>
    <p>{{fatherMsg}}</p>
    <button @click="changeMyson">改变子组件文字</button>
    <son @son-to-father="theFather" ref="myson"></son>
  </div>
  <template id="son">
    <div>
      <p>{{msg}}</p>
      <button @click="sonToFather">向父组件传送</button>
    </div>
  </template>
</body>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      fatherMsg: "默认的父组件文字"
    },
    methods: {
      theFather(data) {
        console.log(data);
        this.fatherMsg = data
      },
      changeMyson() {
        this.$refs.myson.msg = "改成父组件中的文字"
      }
    },
    components: {
      "son": {
        template: "#son",
        data() {
          return {
            msg: "这里是子组件中的文字",
          }
        },
        methods: {
          sonToFather() {
            this.$emit("son-to-father", this.msg)
          }
        }
      }
    }
  })
</script>

</html>

2. Vue插槽 - slot

插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。

插槽有 具名插槽默认插槽

具名插槽就是给插槽取个名字,一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中

默认插槽就是没有名字,只有一个<slot></slot>标签。

<!-- 课程组件 -->
<course page-size='10'>
  <!-- <slot>默认插槽</slot> -->
  <template v-slot:free>免费</template>
  <!-- <template v-slot:disc>限时折扣</template> -->
</course>

<course type="boutique">
  <template v-slot:free>精品</template>
</course>

<course type="discount">
  <template v-slot:discount>限时折扣</template>
</course>

<template id="course">
  <div>
    <h2>
      <!-- 默认插槽:<slot></slot> -->
      <!-- 具名插槽:<slot name="free"></slot>课程 -->
      <slot name="free"></slot>
      <slot name="discount"></slot>课程
    </h2>
    <!-- <slot></slot> -->
    <!-- <slot name="disc"></slot> -->
  </div>
</template>

如果出现具名插槽没有效果,但是也没有报错,极有可能是Vue版本引入的问题!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值