Vue组件化

152 阅读1分钟

使用

  1. 创建组件构造器

    Vue.extend()

  2. 注册组件

    Vue.component()

  3. 使用组件

<div id="app">
   <!-- 3.使用组件(必须已经被Vue托管) -->
   <Mycpn></Mycpn>
</div>
<script type="text/javascript">
   // 1.创建组件构造器
   const cpnC = Vue.extend({
      template:`
	<div>
		<h2>我是标题二</h2>
		<p>我是段落</p>
		<p>我是段落</p>
   </div>
`
   });
   // 2.注册组件(全局) 对Vue对象托管的元素生效
   //Vue.component('my-cpn',cpnC); 标签名最多支持首字母大写,不能使用驼峰命名法
   const app = new Vue({
      el:'#app',
      data:{

      },
      //2.注册组件(局部) 只对被该Vue对象托管的元素生效
      components:{
         //标签名:组件构造器
         Mycpn:cpnC
      }
   });
</script>
</script>

父组件子组件

<div id="app">
    <cpn></cpn>
</div>
<script type="text/javascript">
    const cpnC1 = Vue.extend({
        template:`
	<div>
		<h2>我是子</h2>
		<p>我是段落</p>
		<p>我是段落</p>
    </div>
`
    });
    const cpnC2 = Vue.extend({
        template:`
	<div>
		<h2>我是父</h2>
		<p>我是段落</p>
		<p>我是段落</p>
		<cpn1/>
    </div>
`,
        components:{
            cpn1:cpnC1//在哪里注册,只能在哪里使用
        }
    });
    const app = new Vue({
        el:'#app',
        data:{

        },
        components:{
            cpn:cpnC2
        }
    });
</script>

语法糖

//无需创建组件构造器
Vue.component('cpn',{
    template:`
	<div>
		<h2>我是子</h2>
		<p>我是段落</p>
		<p>我是段落</p>
	</div>
	`
})


const app = new Vue({
	el:'#app',
	data:{
	},
	components:{
		cpn:{
			template:`
				<div>
					<h2>我是父</h2>
					<p>我是段落</p>
					<p>我是段落</p>
					<cpn1/>
				</div>
				`
		}
	}
});

模板的分离写法

<div id="app">
   <cpn></cpn>
</div>
<template id="tpl">
   <div>
      <h2>我是子</h2>
      <p>我是段落</p>
      <p>我是段落</p>
   </div>
</template>
<script type="text/javascript">
   const cpnC1 = Vue.extend({
      template:'#tpl'
   });
   // 2.注册组件(全局)
   Vue.component('cpn',cpnC1);
   const app = new Vue({
      el:'#app',
      data:{

      }
   });
</script>
<div id="app">
   <cpn></cpn>
</div>
<script type="text/x-template" id="tpl">
      	<div>
      		<h2>我是子</h2>
      		<p>我是段落</p>
      		<p>我是段落</p>
   </div>
</script>
<script type="text/javascript">
   const cpnC1 = Vue.extend({
      template:'#tpl'
   });
   // 2.注册组件(全局)
   Vue.component('cpn',cpnC1);
   const app = new Vue({
      el:'#app',
      data:{

      }
   });
</script>

组件中的数据存放

<div id="app">
   <cpn></cpn>
</div>
<template id="tpl">
   <div>
      <h2>我是{{who}}</h2>
      <p>我是段落</p>
      <p>我是段落</p>
   </div>
</template>
<script type="text/javascript">
   const cpnC1 = Vue.extend({
      template:'#tpl',
      //组件不能访问Vue实例中的data
      data(){//必须是个函数形式而且名为data,为了多次使用的实例可以有独立的data
         return {//返回一个对象
            who:'王灿'
         }
      }
   });
   Vue.component('cpn',cpnC1);
   const app = new Vue({
      el:'#app',
      data:{

      }
   });
</script>

父子组件之间的通信

  • 父——>子:props属性
  • 子——>父:$emit Events

父——>子

<div id="app">
   <father></father>
</div>
<template id="father">
   <div>
      <son :cmovies='movies' :cnames='names'></son>
      <!-- 必须使用v-bind,不然则会把‘movies’这个字符串传入下层 -->
   </div>
</template>
<template id="son">
   <ul>
      <li v-for="item in cmovies">{{item}}</li>
      <li v-for="item in cnames">{{item}}</li>
   </ul>
</template>
<script type="text/javascript">

   Vue.component('father',{
      template:'#father',
      data(){
         return{
            movies:['asd','dsa'],
            names:['xxx','zzzz']
         }
      },
      components:{
         'son':{
            template:'#son',
            props:['cmovies','cnames']//当作变量名解析,写在子组件中
         }
      }
   });
   const app = new Vue({
      el:'#app',
      data:{
      }
   });
</script>

props属性形式

props:['asd','asdd'];//可以是数组
props:{//最好是对象
   //1.类型限制
   cmovies:Array,
   cnames:String,
   //2.提供默认值
   cmovies:{
      type:Array,//可以是自定义类型
      default:['asd','asdd']//类型为数组或对象时,版本不同,可能这种不行,酌情选择
      default(){
      	return ['asd','asdd']
      }
      required:true//使用该子组件必须传值
   }
   cMovies{}//props属性名若是驼峰命名法
   //则v-bind绑定属性时,不能使用cMovies,而要使用c-movies 驼峰要变为-小写
}

子——>父

<div id="app">
   <father ></father>
</div>
<template id="father">
   <div>
      <son @btnclick='myclick'></son>
      <!-- 2.使用子组件发射的函数=自定义方法 -->
   </div>
</template>
<template id="son">
   <ul>
      <button v-for="item in sorts" @click='itemclick(item)'>
         {{item}}
      </button>
   </ul>
</template>
<script type="text/javascript">
   Vue.component('father',{
      template:'#father',
      data(){
         return{
         }
      },
      methods:{
         myclick(item){//3.自定义方法中,使用子组件的数据
            console.log(item);
         }
      },
      components:{
         'son':{
            template:'#son',
            data(){
               return{
                  sorts:['家电','数码','母婴','虚拟']
               }
            },
            methods:{
               itemclick(item){
                  this.$emit('btnclick',item)
                  // 1.在事件函数中使用this.$emit('方法名',参数)发射函数
               }
            }
         }
      }
   });
   const app = new Vue({
      el:'#app',
      data:{
      }
   });

父子访问

父访问子

  • 使用$children访问子组件(不推荐使用)
  • 使用$refs访问子组件
<div id="app">
   <son></son>
   <son></son>
   <son></son>
   <son ref='whatever'></son>
   <!-- 设置该属性后,就可以从$refs中访问了 -->
   <button @click='btnClick'>访问子</button>
</div>
<template id="son">
   <div>
      我是son
   </div>
</template>
<script type="text/javascript">
   var app = new Vue({
      el:'#app',
      data:{
         message:'你好'
      },
      methods:{
         btnClick(){
            // console.log(this.$children);
            // this.$children[0].showMessage();
            // 开发中,极少使用$children,一般用来获取所有子组件
            console.log(this.$refs.whatever);
            // 绝大部分使用$refs来访问我们需要的子组件
         }
      },
      components:{
         son:{
            template:'#son',
            methods:{
               showMessage(){
                  console.log('我是子组件的message');
               }
            },
         }
      }
   });
</script>

子访问父

  • 使用$parent访问父组件
  • 使用$root访问根组件(顶层Vue实例)
<div id="app">
   <son></son>
</div>
<template id="son">
   <div style="background-color: #00BFFF;display: inline-block;">
      <h2>我是子组件</h2>
      <button @click="btnClick">访问父</button>
      <!-- 这里父组件为顶层Vue实例 也就是$root-->
      <grandson></grandson>
   </div>
</template>
<template id="grandson">
   <div style="background-color: #19692C;display: inline-block;">
      <h2>我是孙子组件</h2>
      <button @click="visitParent">访问父</button>
      <button @click="visitRoot">访问根</button>
   </div>
</template>
<script type="text/javascript">
   var app = new Vue({
      el:'#app',
      data:{
         message:'你好'
      },
      methods:{
      },
      components:{
         son:{
            template:'#son',
            methods:{
               btnClick(){
                  console.log(this.$parent);
               }
            },
            components:{
               grandson:{
                  template:'#grandson',
                  methods:{
                     visitParent(){
                        console.log(this.$parent);
                        // 很少使用子访问父,Vue组件化的意义在于可复用性,每个使用场景的父组件不一定相同,下同
                     },
                     visitRoot(){
                        console.log(this.$root);
                     }
                  }
               }
            }
         }
      }
   });
</script>

插槽

基本使用

<div id="app">
   <son></son>
   <son><span>文字</span></son>
   <son><button>按钮</button></son>
   <!-- 双标签内部的元素会被全部替换到slot标签的位置 -->
   <!-- 若没有元素,则使用slot标签中的默认元素 -->
</div>
<template id="son">
   <div>
      <h2>我是子组件</h2>
      <slot><i>我是默认内容</i></slot>
      <!-- slot插槽,预留一个位置放不同的内容 -->
      <!-- 不是具名插槽会全部被替换为相同内容-->
   </div>
</template>
<script type="text/javascript">
   var app = new Vue({
      el:'#app',
      components:{
         son:{
            template:'#son'
         }
      }
   });
</script>
</body>

具名插槽的使用

<div id="app">
   <son>
      <span slot="left">左边的</span>
      <span slot="right">右边的</span>
      <!-- 会替换slot属性指定的name -->
   </son>
</div>
<template id="son">
   <div>
      <slot name="left"><span>left</span></slot>
      <slot>我没有具名,我不会被替换,</slot>
      <slot name="right"><span>right</span></slot>
   </div>
</template>
<script type="text/javascript">
   var app = new Vue({
      el:'#app',
      components:{
         son:{
            template:'#son'
         }
      }
   });
</script>

变量作用域

<div id="app">
   <cpn v-show="isShow"></cpn>
   <!-- 此处cpn在Vue看来和普通标签没区别,在使用isShow时,会查找当前实例的data,这里的当前实例为app -->
</div>
<template id="cpn">
   <div>
      <h2>我是标题</h2>
      <p v-show="isShow">我是内容</p>
      <!-- 这里的当前实例为cpn -->
      <!-- 总结:在哪里写的用哪里的 -->
   </div>
</template>
<script type="text/javascript">
   const app = new Vue({
      el:'#app',
      data:{
         isShow:true
      },
      components:{
         cpn:{
            template:'#cpn',
            data(){
               return{
                  isShow:false
               }
            }
         }
      }
   })
</script>

作用域插槽

<!-- 目的:我们希望第二个子组件不按照slot默认行为来渲染,但是又需要拿到子组件的数据 -->
<div id="app">
   <cpn></cpn>
   <!-- 这里的x随便命名 -->
   <cpn v-slot='x'>
      <span>{{x.data.join('——')}}</span>
   </cpn>
   <!--  -->
   <cpn></cpn>
</div>
<template id="cpn">
   <div>
      <!-- 这里的data随便命名 -->
      <slot :data='language'>
         <ul>
            <li v-for="i in language">{{i}}</li>
         </ul>
      </slot>
   </div>
</template>
<script type="text/javascript">
   const app = new Vue({
      el:'#app',
      components:{
         cpn:{
            template:'#cpn',
            data(){
               return{
                  language:['C','Python','Javascript','Java','C#']
               }
            }
         }
      }
   })
</script>