【Vue2】组件

前言

没有用脚手架构建项目的组件化开发,都是在html中导入vue.js,来进行开发的
(如下)在这里插入图片描述
用一个html文件做组件化开发:

<body>
    <div id="root">
        <leftmenu></leftmenu>	<!-- 使用组件标签 -->
    </div>
</body>
<script src="./vue.js"></script>
<script type="text/javascript">
     Vue.config.productionTip = false
    // Vue.component('Aside',aside)
    const leftmenu = Vue.extend({	// 一个组件
            template:`<div>
                <h2 style="border:1px solid black;width:10%">{{msg}}</h2>
                </div>`,
            data() {
                return {
                    msg:'我是子组件',
                }
            },
        })
        
    new Vue({
        el:'#root',
        data:{
            // father:'父组件'
        },
        components:{	//注册组件
            leftmenu
        }
    })
</script>

效果:
在这里插入图片描述

通过npm下载vue的脚手架之后,用脚手架创建一个vue项目↓
请添加图片描述
在项目中进行组件化开发,这才是完整的Vue项目。
其中App.vue是主体,components文件夹中的School.vue和Student.vue都是组件。
其中assets文件夹是存放静态资源的。
其中main.js:

import Vue from 'vue'
import App from './App.vue'

//  关闭vue的生产提示
Vue.config.productionTip = false

new Vue({
  render: h => h(App),  //渲染解析项目中App.vue文件的内容
}).$mount('#app')

其中new Vue({…}).$mount(‘#app’) 相当于
在这里插入图片描述

public中 index.html 文件各行代码的含义

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <!-- 针对IE浏览器的特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 开启移动端的理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- BASE_URL 是public的路径,↓配置页签图标 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 配置网页标题,找package.json中的"name": "vue_demo"作为网站标题 -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- 如果浏览器不支持js时,noscript标签中的元素就会被渲染 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <!-- 容器↓ -->
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

组件文件的命名规范:首字母大写,后面驼峰命名


ref、refs实现父子组件通信

在App.vue中的 <子组件标签 ref=“xxx”> …</子组件标签> 中加ref类似于一个id,在方法中可以通过this.$refs.xxx 来获取这个子组件的实例对象VueComponent (vc),

如果是加在 <普通标签 ref=“xxx”> … 中 ,则获取的是这个普通标签的DOM

this.$refs可以输出页面中所有加了ref 的标签的集合 ,是对象形式

例 :App.vue中

<template>
  <div>
      <button  ref="btn"> 按钮</button>
      <school ref="sch"></school>
      <student ref="stu" ></student>
  </div>
</template>

<script>

import school from "./components/School";
import student from "./components/Student";

export default {
 components: {
    school,
    student
  },
 mounted(){
    console.log(this.$refs);
  },
};
</script>

在这里插入图片描述

只要获取了子组件的实例对象,就能实现父子组件间的通信等操作。
如下例:

父组件App.vue:

<template>
  <div>
      <school ref="sch"></school>
  </div>
</template>

<script>

import school from "./components/School";

export default {
 components: {
    school
  },
  methods:{
      // 自定义事件↓
    mymethod(arg){
        console.log(arg);
    }
 }
 mounted(){
    // 自定义事件shijian  在子组件school的$emit中能触发,触发时调用这边的mymethod方法
    this.$refs.sch.$on('shijian',this.mymethod)
	/*  this.$refs.sch.$on('shijian', (arg) =>{ //同上,只是把mymethod写成回调函数
      console.log(this);
    }) */
  },
};
</script>

子组件school.vue:

<template>
    <div class="school" >
        <h1 @click="showAddress">学校地址:{{address}}</h1>
    </div>
</template>

<script>
export default {
    name:"MySchool",
    data(){
        return{
            address:'东莞'
        }
    },
    methods: {
        showAddress(){
            this.$emit('shijian',this.address)	//第一个参数对应父组件中设置好的$refs.ref名.$on,第二个参数是传递给父组件的实参
        }
    },
</script>

此时点击学校地址,子组件就会($ emit)调用父组件($on)注册好的’shijian’中的函数,打印内容:

在这里插入图片描述
此例子就实现了子组件的参数传给父组件的效果,即子传父

另外,如果上面父组件中的this.mymethod 函数用回调函数的形式书写,普通函数里面的this 会指向子组件实例对象vc ,

this.$refs.子组件.$on('shijian',function (arg) { console.log(this);})
这个this是子组件的实例对象 (VueComponent),

而如果这个函数用箭头函数的方式写,this则是指向父组件的实例对象。
this.$refs.子组件.$on('shijian',(arg) =>{ console.log(this); })
这个this是当前函数外层的实例对象(也就是父组件)


prop、props父传子

例子
父组件:App.vue

<template>
	<div>
	      <student ref="stu" name="abc" sex="" age=18></student>
	</div>
</template>
。。。

子组件student.vue:

<template>
    <div class="student">
        <h2>学生姓名:{{name}}</h2>
        <h2>学生年龄:{{age}}</h2>
        <h2>学生性别:{{sex}}</h2>
    </div>
</template>

<script>
export default {
    name:'MyStudent',
    data(){
        return{
            // 这里变量的命名不能和props中的相同!!!
            Myage:this.age // 可以得到props中的age,props先编译,在组件的实例对象vc中有了age了,然后可以用this.调用,赋值给本地data数据
        }
    },
    // 接收父组件传过来的参数
   //方式一 props:['name','age','sex'] 	 不限制数据类型的写法 
   /* 方式二  props:{							// 限制数据类型的写法
        		name:String,
        		age:Number,
        		sex:String
    		}*/
    // 方式三  配置		(用的多)
    props:{
        name:{
            type:String, //数据类型	字符串
            required:true,	// 是否必须要传过来有值;属性是否是必须的
        },
        age:{
        	type:Number, 	//数据类型 数值
            default:18		// 默认值
    	},
    	sex:{
            type:String,	//数据类型	字符串
            ...
        }
    }
}
</script>

子组件接收了参数后不建议修改其值,会报错(修改 传过来的对象.xxx 不报错但不推荐)。

因为props是只读的,Vue底层会监测用户对props 的修改,如果进行了修改,就会发出警告,若业务需要修改值,建议复制props的内容到本地data中的数据,然后修改data中的数据即可。

如果子组件没有用props接收,那他的实例对象vc里面也会有接收,在this.$attrs中,虽然可以调用到,缺点是不能限制其类型,是否必须,默认值等。


子传父

方式一 父子共用函数,通过参数传值

父组件:

定义一个方法,需要参数,把这个方法传给子组件 <子组件标签 :方法名=“方法名” > </子组件标签>
App.vue:

 <template>
  <div class="total"><
	<school :zichuanfu="zichuanfu" ></school> <!-- 函数传给子组件-->
  </div>
</template>

<script>
import school from "./components/School";

export default {
  components: {
    school,
  },
  methods: {
    zichuanfu(arg){
      console.log("子传父传过来的参数:",arg);
    },
</script>

子组件

props :[‘方法名’]接收这个方法,调用的时候传参 方法名( 子组件的某个要传给父组件的实参 )
School.vue:

<template>
    <div>
        <h1>学校名称:{{name}}</h1>
        <h1>学校地址:{{address}}</h1>
        <button @click="zichuanfu(address)">子传父</button>	
<!-- 点击时调用props传过来的函数zichuanfu,传入一个实参,就能把这个参数数据传给父组件了 -->
    </div>
</template>

<script>
export default {
    name:"MySchool",
    data(){
        return{
            name:'DGCC',
            address:'东莞'
        }
    },
    props:["zichuanfu"] //注意点:双引号
}
</script>

回到父组件

方法名 ( 参数 ){ 这个参数就是从子组件传过来的变量 }

  methods: {
    zichuanfu(arg){
      console.log("子传父传过来的参数:",arg);
    },
    //此函数被执行,接收子组件传过来的参数,打印这个参数

效果:

在这里插入图片描述

方式二 自定义事件

父组件中:

<子组件标签 v-on:自定义事件名=“触发函数名” />

v-on: 可以写成@,这就类似于@keyup,@click这些js自带的事件,只是这一次@后面的名称是我们自己定义的,所以叫自定义事件。

<student v-on:zidingyi="zidingyi" />		

shijian后面加.once 则该自定义 事件只能触发一次

子组件中:

在某个函数中触发自定义事件, 方法中写 this.$emit.(‘自定义事件名’,参数) ,这样就会去触发自定义事件了

data(){
     return{
     	age:18
     }
 },
methods:{
 sendAge(){
   this.$emit('zidingyi',this.age)}	//'zidingyi'是自定义事件名,this.age是传过去的参数
 }

父组件中:

在methods中写一个触发函数,接收形参,这个形参就是子组件传过来的值,由此实现了子传父

    // 自定义事件触发的函数↓
    zidingyi(args){	//可以有多个参数a,b,c,a,...params
        console.log(args);
    }

组件实例对象在销毁之后,全部的自定义事件将自动解绑失效,在父组件销毁后,子组件的自定义事件都失效;某一个子组件销毁,该子组件自身的自定义事件失效。

当然,也可以在不销毁组件实例对象的情况下,手动解绑自定义事件

在 某个方法() { 。。。} 中解绑自定义事件,如

  unbind() {
	this.$off('zidingyi')
  },

综上例子:
父组件App.vue:

<template>
  <div>
     <Test @zidingyi="zidingyi"></Test>
  </div>
</template>

<script>
import Test from "./components/Test.vue";

export default {
  components: {
    Test,
  },
  methods: {
    zidingyi(arg){
      console.log("自定义事件zidingyi被调用,参数是:",arg);
    },
 }
 </script>

子组件Test.vue:

<template>
    <div>
    	<button @click="emitZidingyi" >触发自定义事件</button>
        <button @click="unbind">手动解绑自定义事件</button>
    </div>
</template>

<script>

export default {
    name:"Test",
    data(){
        return{
            testData:'test'
        }
    },
    methods:{
    	emitZidingyi(){
	        this.$emit('zidingyi',this.testData)
	    },
	    unbind() {
			this.$off('zidingyi')
	  	},
  	}
}
</script>

效果:
在这里插入图片描述

解绑的写法有三个:

  • this.$off(‘事件名’) 解绑一个自定义事件
  • this.$off([‘事件1’,‘事件2’]) 解绑多个自定义事件
  • this.$off() 解绑所有自定义事件

.native

在父组件文件中,给子组件标签加@click,子组件默认会认为是自定义事件,此时,当你点击子组件的时候,是不会触发点击事件的,只有 @click.native 才能保持原样(也就是js的点击事件),而不被子组件解析成自定义事件。

小总结

通过对子组件标签上绑定的自定义事件 和 给子组件标签上的ref,在this.$ refs.xxx.$on中绑定的事件,在子组件中,他们都是用this. $emit(‘事件名’,参数 ) 来调用的。


mixins混入

在main.js同级目录下,建一个mixin.js文件:默认暴露

export default  {
    methods: {
        showName() {
            console.log(this.name);
        }
    },
}

在子组件中使用:

<template>
    <div class="school" >
        <h1 @click="showName">学校名称:{{name}}</h1>
    </div>
</template>

<script>
import hunru from '../mixin' ;
export default {
    name:"MySchool",
    data(){
        return{
            name:'DGCC',
        }
    },
    mixins:[hunru]
    //    mixins:[hunru , 可以有多个 用逗号分开],	// 注意 是mixins 有s!!!
}
</script>

上面例子,在子组件中看不到showName方法,但是点击时可以调用,因为导入的hunru来自mixin.js文件,mixin.js中写好了showName方法,此时在子组件中调用的是mixin.js中写好的方法。

在这里插入图片描述
但是,如果在子组件文件中也写了showName方法,则会覆盖mixin.js里面用的东西。

mixin.js中的变量也可以共同使用:
在这里插入图片描述
但是如果在mixin.js中写了 生命周期的函数,则不覆盖,两边都执行。

上面是局部混入,下面介绍全局混入,但是因为其有弊端,所以用的少。

请添加图片描述
在App.vue中写了全局混入,那其他子组件可以不用导入。默认会导入 引用


其他父子组件通信方式还有 全局事件总线,消息订阅与发布

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值