【web高级 01vue 】 vue预习课06 组件化

一、组件基础

在这里插入图片描述

组件是可服用的Vue实例,带有一个名字,我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。

1.1组件注册、使用及其数据传递

Vue.component(name,optiosn)可用于注册组件
范例:提取food新增和食物列表组件

 <!-- 列表组件 -->
<food-list :foods="foodList"></food-list>

<script>
 	//食物列表组件
	Vue.component("food-list", {
     
     
		 data() {
     
     
		   return {
     
     
		     selectedFood: "",
		   };
		 },
		 props: {
     
     
		   foods: {
     
     
		     type: Array,
		     default: [],
		   },
		 },
		
		 template: `
		 <div>
		         <!-- 条件渲染 -->
		         <p v-if="foods.length == 0">没有任何数据</p>
		
		         <div v-else>
		                 <div v-for="item in foods"
		                 :key="item"
		                 :style="{backgroundColor:selectedFood == item ? '#ddd' : 'transparent' }"
		                 @click="selectedFood = item"
		                 >
		                 {
      
      {item}}
		                 </div>    
		         </div>    
		 
		 </div>
		 
		 `,
	});
</script>

组件基础

1.2自定义事件及其监听

当子组件需要和父组件进行通信,可以派发并监听自定义事件。
范例:新增课程组件派发事件

<!-- 新增数据 -->
<!-- 自定义事件及其监听 -->
<!-- 
  当子组件需要和父级组件进行通信,可以派发并监听自定义事件。
-->
<food-add @add-food="addFood"></food-add>


<script>
	//食物新增组件
	Vue.component("food-add", {
     
     
	    data() {
     
     
	      return {
     
     
	        food: "",
	      };
	    },
	    template: `
	            <div>
	                    <!--用户输入 -->
	                    <p>
	                            <input v-model="food" type="text" v-on:keydown.enter="addCourse" />
	                            <button v-on:click="addFood">新增</button>
	                    </p>     
	            </div>
	            
	            `,
	    methods: {
     
     
	      addFood() {
     
     
	        //派发事件,通知父组件新增数据
	        //自定义事件的名称,不建议驼峰,建议羊肉串
	        this.$emit("add-food", this.food);
	        this.food = "";
	      },
	    },
	});

	const app = new Vue({
     
     
		el: "#app",
			data() {
     
     
				return {
     
     
				foodList: ["萝卜", "白菜"],
			};
		},
		methods: {
     
     
			addFood(food) {
     
     
				console.log("food", food);
				this.foodList.push(food);
			},
		},
	 });
</script>

1.3在组件上使用v-model

组件实现v-model

范例:改造food-add为支持v-model的版本

<!-- 

在组件上使用v-model 

<food-add v-model="food"  @add-food = "addFood"></food-add> 

-->

<!-- 

  在自定义组件使用双向数据绑定
  在开发中,可能有这样的需求
  food这个变量确实不想放在子组件中去维护
  food可能和其他各种各样的数据,都在在父组件中一起去维护的
  父组件只是把food这个变量交给子组件去维护
  改变之后,最终使用的还是父组件

-->

<!-- 

  语法糖
  默认情况下:

  v-model="food"

  会变成

  :value="food"
  @input="food=$event"
  $event是固定的名字,是子组件传递出来的参数
  父组件把参数赋值给food
 -->

<!-- 
   在子组件中我们要做的是,
   1.派发一个默认的input事件,并且要把最新的值传递出来,作为参数
   2.在子组件接收一个属性,value,但是不去改变他
-->
<food-add  v-model="food"  @add-food="addFood"></food-add>

<script>
  	  //食物新增组件
      Vue.component("food-add", {
     
     
        //1.这个不在需要,放在父组件中
        // data() {
     
     
        //   return {
     
     
        //     food: "",
        //   };
        // },
        //1.addFood中不需要维护food
        //2.接收父组件传递的值  props: ["value"],
        //3.input不需要v-model,改为:value="value"
        //4.增加input事件
        props: ["value"],
        template: `
	        <div>
	                <!--用户输入 -->
	                <p>
	                        <input 
	                        :value="value" 
	                        type="text" 
	                        @input="onInput"
	                        v-on:keydown.enter="addCourse" />
	                        <button v-on:click="addFood">新增</button>
	                </p>     
	        </div>
        
        `,
        methods: {
     
     
          addFood() {
     
     
            //this.$emit("add-food", this.food);
            //this.food="";
            
            //以上代码中,子组件已经没有food了,就不需要维护

            this.$emit("add-food");
          },
          //4.增加input事件
          //5.在app组件的data增加food
          onInput(e) {
     
     
            this.$emit("input", e.target.value);
          },
        },
      });
      
      const app = new Vue({
     
     
        el: "#app",
        data() {
     
     
          return {
     
     
            food: "",  //还原food
            foodList: ["萝卜", "白菜"],
          };
        },
        methods: {
     
     
          //   addFood(food) {
     
     
          //     console.log("food", food);
          //     this.foodList.push(food);
          //   },
          addFood() {
     
       //还原addFood
            //food称为父组件的当前的data,不需要接参数了
            this.foodList.push(this.food);
            this.food = "";
          },
        },

      });
 </script>

v-mode默认转换是:value和@input,如果想要修改这种行为,可以通过model选项

Vue.component('food-add',{
    
    
	model:{
    
    
		prop:'value',
		event:'change'
	}
});

1.4通过插槽分发内容

通过使用vue提供的<slot>元素可以给组件传递内容
范例:弹窗组件

<style>
  .active {
     
     
    background-color: #ddd;
  }

  .message-box {
     
     
    padding: 10px 20px;
    background: #4fc08d;
    border: 1px solid #42b983;
  }

  .message-box-colse {
     
     
    float: right;
    cursor: pointer;
  }
</style>

<!-- 弹窗组件 -->
<!-- 
    :show="true"

    show前面加冒号,等号后面是boolean
    show前面不加冒号,等号后面是string
-->

<message :show="isShow"  @close="isShow = $event "> 新增成功! </message> 
<script>
//弹窗组件
/*
  <span class="message-box-colse"  @click="$emit('close',false)">X</span>

  在关闭按钮这里增加一个自定义组件的派发 

  methods: {
    addFood() {
      this.$emit("add-food",true);
    },
    onInput(e) {
      this.$emit("input", e.target.value);
    },
  },

   直接写表达式,这样写,就不用写methods
   @click = "$emit("close",false)"
  */
Vue.component("message", {
     
     
  props: ["show"], //使用props弹窗是否展示是由父组件决定的
  template: `
          <div class="message-box" v-if="show">

              <!--通过slot获取父组件传入的内容-->
              <slot></slot>

              <span class="message-box-colse"  @click="$emit('close',false)">X</span>
          </div>
      `,
});

const app = new Vue({
     
     
	 el: "#app",
	 data() {
     
     
		   return {
     
     
			     isShow: false,
		   };
	 },
	 methods: {
     
     
		   addFood(show) {
     
     
			     //显示弹窗
			     this.isShow = show;
		   },
	 },

});

</script>

用.sync改进

<!-- 
   @close="isShow = $event"

   父组件的 isShow = 子组件传递出来的参数 $event

   语法上显得很啰嗦,2.4版本新增了 .sync  修饰符
   1.  @close="isShow = $event"  去掉

   2.  :show="isShow"   改为    :show.sync="isShow"  show 后面增加 .sync

   3.  @update:show = ""   @updata:变量名  变量名就是 .sync修饰的变量的名称

   父组件约定好了   子组件 自定义事件的名字 

   对show的控制权是当前父组件,子组件要求传自定义事件的名字就固定了

   4.message子组件中  
   <span class="message-box-colse"  @click="$emit('close',false)">X</span>

   关闭事件的自定义事件名close   改为     update:show

   <span class="message-box-colse"  @click="$emit('update:show',false)">X</span>
             
-->

<message :show.sync="isShow"> 新增成功! </message>

<script>
 Vue.component("message", {
     
     
	  props: ["show"], //使用props弹窗是否展示是由父组件决定的
	  template: `
          <div class="message-box" v-if="show">

			  <!--通过slot获取父组件传入的内容-->
              <slot></slot>

              <!--  用.sync需要修改自定义的事件名字为:update:show
                  <span class="message-box-colse"  @click="$emit('close',false)">X</span>
              -->

              <span class="message-box-colse"  @click="$emit('update:show',false)">X</span>
          </div>
	 `,
});
	
</script>

具名插槽

 <!-- 
          具名插槽 :把指定内容放在指定位置
        1.   
     -->
<message :show.sync="isShow">
	   <!--
	       具名插槽
	       命名为title的插槽内容
	
		   1.使用  <template></template>
	
	       2.用v-slot指令指定名字
	       v-slot:title
	       v-slot:后面的title就指定了子组件的插槽名称
	
	       3.在子组件中增加    <slot name="title"></slot>
	       
	   -->
	   <template v-slot:title>
	     	<strong>恭喜</strong>
	   </template>
	
	   <!-- 
	        默认插槽内容
	        不起名字 等同   v-slot:default 
	    -->
	   <template> 新增成功! </template>
</message>

<script>
Vue.component("message", {
     
     
        props: ["show"], //使用props弹窗是否展示是由父组件决定的
        template: `
            <div class="message-box" v-if="show">
                <!--具名插槽-->
                <slot name="title">默认标题</slot>


                <!--通过slot获取父组件传入的内容-->
                <slot></slot>


                <!--
                    <span class="message-box-colse"  @click="$emit('close',false)">X</span>
                -->

                <span class="message-box-colse"  @click="$emit('update:show',false)">X</span>
            </div>
        `,
      });

</script>

作用域插槽

<!-- 

        作用域插槽

        
        <template v-slot:title>
          <strong>{
    
    {title}}</strong>
        </template>

        title获取的场所,应该是当前使用message的父组件中去查找的

        应该定义的位置是根示例的这个title

        const app = new Vue({
            el: "#app",
            data() {
            return {
                title: "hello vue!",
            };
            },
        });


        现在有一个特殊的需求:
        title的值不是来自于当前上下文,而是来自于message的子组件,那如何来获取这个值呢?
        使用的方式就是 作用域插槽

        <template v-slot:title="slotProps">
          <strong>{
    
    {slotProps.title}}</strong>
        </template>


         <slot name="title"  title="来自message的标题">默认标题</slot>


         title来自于哪里,决定了要不要用作用于插槽
-->

<message :show.sync="isShow">
        
        <!-- slotProps  名字自定义   上下文-->
        <template v-slot:title="slotProps">
          <!-- <strong>{
    
    {title}}</strong> -->
          <strong>{
   
   {slotProps.title}}</strong>
        </template>

        <template> 新增成功! </template>
</message>

<script>
Vue.component("message", {
     
     
        props: ["show"], //使用props弹窗是否展示是由父组件决定的
        template: `
                <div class="message-box" v-if="show">
                    <!--具名插槽-->
                    <!--
                        <slot name="title">默认标题</slot>
                    -->
                    <slot name="title"  title="来自message的标题">默认标题</slot>



                    <!--通过slot获取父组件传入的内容-->
                    <slot></slot>



                    <!--
                        <span class="message-box-colse"  @click="$emit('close',false)">X</span>
                    -->
                    <span class="message-box-colse"  @click="$emit('update:show',false)">X</span>
                </div>
            `,
      });
</script>

二、Vue组件化的理解

2.1组件化的本质

组件化是Vue的精髓,Vue应用就是由一个个组件构成的。Vue的组件化涉及到的内容非常多,当面试时被问到:谈一下你对组件化的理解。这时候有可能无从下手。可以从以下几点阐述:

定义:组件是可复用的Vue实例,准确将他们是VueComponent的实例,继承自Vue。

优点:组件化可以增加代码的复用性、可维护性可测试性

**使用场景:**什么时候使用组件?以下分类可以作为参考:

  • 通用组件:实现最基本的功能,具有通用性、复用性,例如按钮组件、输入框组件、布局组件等。
  • 业务组件:它们完成具体业务,具有一定的复用性,例如登录组件、轮播图组件。
  • 页面组件:组织应用各部分独立内容,需要时在不同页面组件切换,例如列表页、详情页组件

如何使用组件

定义:Vue.component() , components选项 , sfc   
分类:有状态组件,functional , abstract   
通信:props , $emit() / $on() , provide / inject , $children / $parent / $root  / $attrs / $listeners    
内容分发:<slot> , <template> , v-slot     
使用及优化:is , keep-alive , 异步组件

组件的本质
vue中的组件经历如下过程
组件配置 => VueComponent实例 => render() => Virtual DOM => DOM
所以组件的本质是产生虚拟DOM

猜你喜欢

转载自blog.csdn.net/weixin_42580704/article/details/110388767