100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【Vue】9 - 组件(全局 局部) 通信(props $meit $refs) 插槽slot component $nextTick等

【Vue】9 - 组件(全局 局部) 通信(props $meit $refs) 插槽slot component $nextTick等

时间:2024-06-29 15:28:01

相关推荐

【Vue】9 - 组件(全局 局部) 通信(props $meit $refs) 插槽slot component $nextTick等

文章目录

1. 组件命名2 . 全局组件3. 局部组件4. 嵌套组件5.发布订阅6. 组件间通信父 传 子(属性传递)子 传 父(触发事件传递:传子的数据,触发父的方法)案例:模态框父组件操作子组件的方法--ref非父子组件通信--eventBus事件车7. 插槽slot8. 组件切换 与 缓存9. 强调 this.$nextTick(callback)

项目很庞大的时候,希望开发的时候能分模块开发,就像现在提倡的组件化开发,一个自定义标签 - vue就会把他看成一个组件,vue可以给这些标签赋予一定的意义

根据功能—组件分两类:

页面级组件基础组件- 将可复用的部分抽离出来

根据用法—组件分为:

全局组件— 声明一次,在任何地方使用 (一般写插件的时候,用全局组件多一些;)局部组件— 必须告诉这个组件属于谁

1. 组件命名

组件名不要带有大写(最多可以首字母大写),多个单词用中划线(蛇形/烤串命名发)<My-div></My-div>/<my-div></my-div>组件名 和定义名字要相同html中采用短横线隔开命名法,js中转小驼峰也是可以的

2 . 全局组件

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><my-div></my-div></div><script src="node_modules/vue/dist/vue.js"></script><script> let vm = new Vue({el:'#app', data:{a:'hello',arr:[1,2,3],},})//定义全局组件ponent('my-div',{//'my-div'是组件名, 一个对象可以看成一个组件template:'<div>{{msg}}</div>', //在自己的模板中使用自己的数据; 用模板里的内容,替换掉<my-div></my-div>data(){//组件中的数据必须是函数类型的,return返回一个实例 对象 作为组件的数据return {msg:'你好'}}})</script></body></html>

3. 局部组件

局部组件使用三部曲:1.创建组件 2.注册组件 3.使用组件组件是相互独立的,不能跨作用域,vm实例也是一个组件–所以vm上的数据,组件是取不到的

子组件不能直接使用父组件(vm)的数据;–组件之间的数据交互实例上的声明周期函数,组件也有自己的;如果组件公用了数据,会导致同时更新(这样组件就没有 独立性 了)组件可以套组件,组件理论上可以无限嵌套;

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><comp1></comp1><comp2></comp2><!-- 3.使用组件 --></div><script src="node_modules/vue/dist/vue.js"></script><script>//局部组件使用三部曲:1.创建组件2.注册组件 3.使用组件let comp1 = {template:'<div>你好</div>'}; //1.创建组件,组件就是一个对象,里面必须有template属性let comp2 = {template:'<div>hello</div>',data(){//为什么写个函数,返回一个对象就独立了?因为掉用一个函数,产生一个新作用域(新对象),调用两个函数返回的对象,(空间)永远都不会一样,不会出现共用数据的问题,所以需要是个函数;return {};}};let vm = new Vue({el:'#app', components:{comp1, //2.注册组件,comp:comp, es6中名字一样可以简写为 compcomp2,}})</script></body></html>

为什么组件里,写个函数返回一个对象就独立了?

因为调用一个函数,产生一个新作用域(新对象),调用两个函数返回的对象,(空间)永远都不会一样,不会出现共用数据的问题,所以需要是个函数;

4. 嵌套组件

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><parent></parent><!-- 33.使用组件 --></div><script src="node_modules/vue/dist/vue.js"></script><script>let grandson = {template:'<div>grandson</div>'}; let son = {template:'<div>son<grandson></grandson></div>',components:{grandson,}};let parent = {template:`<div>parent<son></son></div>`,components:{son,}}; let vm = new Vue({el:'#app', components:{parent, }})</script></body></html>

5.发布订阅

发布订阅:一对多的依赖关系,让多个订阅者对象同时监听一个主题的变化,模拟发布订阅:

//发布订阅:一对多的依赖关系,让多个订阅者对象同时监听一个主题的变化// vm.$on绑定事件 vm.$once绑定一次 vm.$off解绑事件 vm.$emit 触发事件function Girl(){//构造函数this._events = {}; //这个变量,on 和 emit 方法都能调用,但是又是私有的,所以卸载构造函数里} //让每个实例都有 on /emit 方法,就把方法写到构造函数的原型上Girl.prototype.on = function(eventName,callback){// {shilian:[fn1,fn2,fn3]} --组成这个形式-一对多的关系if(this._events[eventName]){//判断是不是第一次给这个事件订阅方法this._events[eventName].push(callback);}else{this._events[eventName] = [callback];}}Girl.prototype.emit = function(eventName,...args){// Array.from(arguments).slice(1);// [].slice.call(arguments,1);//触发事件eventName,执行该事件订阅的方法if(this._events[eventName]){//判断是否订阅过方法this._events[eventName].forEach(ele =>{ele(...args);// ele.apply(this,args);})}}let girl = new Girl(); //实例化一个girl// 定义方法let fn1 = (arg1,arg2)=>{console.log(`${arg1}哭`);}let fn2 = (arg1,arg2)=>{console.log(`在${arg2}十六了,哭`);}let fn3 = (arg1,arg2)=>{console.log('买');}// 绑定/订阅 方法到实例上,等实例事件触发的时候调用订阅的方法girl.on('shilian', fn1); // {shilian:[fn1]}girl.on('shilian', fn2); // {shilian:[fn1,fn2]}girl.on('shilian', fn3); //把一个事件订阅的方法放在一起:{shilian:[fn1,fn2,fn3]}//触发/发布 事件girl.emit('shilian','参数1','参数2'); //触发失恋,这个事件订阅的方法都会被调用

6. 组件间通信

父 传 子(属性传递)

父组件在使用子组件时,通过属性传递数据<son :a='111'></son>,子组件在定义时,通过props来接收数据props:['a']

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><parent></parent><!-- 33.使用组件 --></div><script src="node_modules/vue/dist/vue.js"></script><script>let grandson = {template:`<div>grandson{{m}}</div>`,props:{//对象形式可以进行验证m:{type:[String,Function,Number], //限制传的数据类型,会报错但不会阻止代码执行// default:0,required:true, //不能和default共用validator(val){return val > 300;}}}}; let son = {template:`<div>son{{a}}<grandson :m='400'></grandson></div>`,components:{grandson,},props:['a'] //子组件中接收父组件传的数据};let parent = {template:`<div>parent<son :a='111'></son></div>`,components:{son,}}; let vm = new Vue({el:'#app', components:{parent, }})</script></body></html>

子 传 父(触发事件传递:传子的数据,触发父的方法)

使用组件时,所有属性名和事件名都是子组件的,属性值 和 事件方法都是父组件

vm.$on绑定事件vm.$once绑定一次vm.$off解绑事件vm.$emit触发事件给父亲绑定一些事件,儿子触发事件时将数据传递过去单向数据流:父亲数据刷新,儿子数据就刷新属性值(方法)是父亲的,属性是儿子的(自定义属性/事件名)

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><!-- 子传父3: 通过子组件自己的 自定义事件名,找到绑定的父组件的 方法 --><son @sonclick='parentFn' :parentnum='num'></son> <!-- 属性值(方法)是父亲的,属性是儿子的(自定义属性/事件名)--></div><script src="node_modules/vue/dist/vue.js"></script><script>let son = {// 子传父1: 点击子组件里某元素,触发自己的事件addtemplate:`<div @click = 'add'>点击后加{{a}}变为:{{parentnum}}</div>`, //使用父组件里的数据props:['parentnum'], //子组件中接收父组件传的数据data(){return {a:10,}},methods:{add(){// 子传父2: 在事件add中,触发自己的自定义事件名,并传参数过去this.$emit('sonclick',this.a);}}};let vm = new Vue({el:'#app', data:{num:9,}, components:{son, },methods:{// 子传父4: 在方法中操作子组件传来的数据parentFn(val){this.num += val;}}})</script></body></html>

案例:模态框

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"><style>.mask{width:100%;height:100%;position: fixed;top:0;left:0;background: #000;opacity:0.5;}.dialog{width:400px;height:300px;background:#fff;position:fixed;left: 50%;top:50%;transform:translate3d(-50%,-50%,0);}</style></head><body><div id="app"><button @click='flag = !flag'>弹窗</button><model :turn = 'flag' @myclose='fn'></model></div><template id='model'><div class="mask" v-if='turn'><div class="dialog"><button @click='close'>关闭</button></div></div></template><script src="node_modules/vue/dist/vue.js"></script><script>let model = {template:'#model',data(){return {}},props:['turn'],methods:{close(){this.$emit('myclose')}}}; let vm = new Vue({el:'#app', components:{model, },data:{flag:false,},methods:{fn(){this.flag = false;}}})</script></body></html>

父组件操作子组件的方法–ref

<comp ref='load'></comp>使用组件的时候给一个ref属性,然后再父组件里

this.$refs.load是comp组件实例,

this.$refs.load.close()是获取子组件里的方法并执行

this.$refs.load.$el是获取子组件comp的根标签div元素

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><comp ref='load'></comp> </div><script src="node_modules/vue/dist/vue.js"></script><script>let comp = {template:`<div v-show='flag'>加载中...</div>`,data(){return {flag:true,}},methods:{close(){this.flag = false;}}}; let vm = new Vue({el:'#app', mounted(){//mounted里dom元素加载完成了,关闭掉loading//ref属性加在dom上,this.$refs获取的是dom,如果ref放在组件上,获取的是组件的实例,并不是组件的dom元素// this.$refs.load.close(); this.$refs.load.$el.style.background = 'red';},components:{comp, },})</script></body></html>

非父子组件通信–eventBus事件车

下面是最初的想法,但是由于发布和订阅不在同一个实例上,无法互相找到和被触发,所以引入一个实例(创建一个第三方实例)作为中间体,发布和订阅都写到这上面

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"> <brother1></brother1><brother2></brother2></div><script src="node_modules/vue/dist/vue.js"></script><script>// 组件都有自己独立的作用域,非父子关系的组件的通信,如果是简单业务就用eventbus(),复杂的就用vueXlet brother1 = {template:`<div>{{color}} <button @cha>让2变成红色</button></div>` ,created(){/* 发布订阅:点击2 要改变1的颜色,1是被改变的--在1上订阅,所以在1careated里一加载完就放上事件监听 点击2--在2上触发,所以在2里写点击 时 触发 1上监听的事件 最初的想法是这样的,但是由于发布和订阅不在同一个实例上,无法互相找到和触发,所以引入一个实例(创建一个第三方实例)作为中间体,发布和订阅都写到这上面*/// 1. 先在自己组件里一加载到时候,监听一个事件,这个事件被触发时执行后面的函数this.$on('changeGreen',(val)=>{//这里必须是箭头函数,回调用function里面的this都是window,用箭头函数this都指向当前实例this.color = val;})},data(){return {color:'1原始红色',old:'红色'}}}let brother2 = {template:`<div>{{color}} <button @click='change'>让1变成绿色</button></div>` ,data(){return {color:'2原始绿色',old:'绿色'}},methods:{// 2.点击事件时调用change方法,然后在方法里触发订阅的changeGreenchange(){this.$emit('changeGreen',this.old);}}}let vm = new Vue({el:'#app',data:{}, components:{brother1,brother2},methods:{}})</script></body></html>

最终写法:

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"> <brother1></brother1><brother2></brother2></div><script src="node_modules/vue/dist/vue.js"></script><script>// 组件都有自己独立的作用域,非父子关系的组件的通信,如果是简单业务就用eventbus(),复杂的就用vueXlet eventBus = new Vue; //3. 定义一个事件车,不需要传参let brother1 = {template:`<div>{{color}} <button @cha>让2变成红色</button></div>` ,created(){/* 发布订阅:点击2 要改变1的颜色,1是被改变的--在1上订阅,所以在1careated里一加载完就放上事件监听 点击2--在2上触发,所以在2里写点击 时 触发 1上监听的事件 最初的想法是这样的,但是由于发布和订阅不在同一个实例上,无法互相找到和触发,所以引入一个实例(创建一个第三方实例)作为中间体,发布和订阅都写到这上面*/// 1. 先在自己组件里一加载到时候,监听一个事件,这个事件被触发时执行后面的函数eventBus.$on('changeGreen',(val)=>{//这里必须是箭头函数,回调用function里面的this都是window,用箭头函数this都指向当前实例this.color = val; //4.把监听事件放到eventBus})},data(){return {color:'1原始红色',old:'红色'}}}let brother2 = {template:`<div>{{color}} <button @click='change'>让1变成绿色</button></div>` ,data(){return {color:'2原始绿色',old:'绿色'}},methods:{// 2.点击事件时调用change方法,然后在方法里触发订阅的changeGreenchange(){eventBus.$emit('changeGreen',this.old); //5.触发的时候触发eventBus上的事件}}}let vm = new Vue({el:'#app',data:{}, components:{brother1,brother2},methods:{}})</script></body></html>

7. 插槽slot

slot作用定制模板,用共同的模板插入不同的内容

没有solt属性的内容 默认放到 name为default的slot标签中

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><comp>lxz</comp> <!--1. 如果没有 slot属性,组件标签中的内容 会被插到组件里的 <slot></slot>中,也就是<slot name='default'></slot>--><hr><comp><p slot='msg1'>你好!</p><span slot='msg2'>请登录!</span><!--2. 如果有 slot属性,标签中的内容 会被插到组件里的slot标签中对应的name属性的标签里 <slot name='msg2'></slot>--></comp></div><template id='comp'><div><slot></slot> <!--1. <slot></slot> 等于 <slot name='default'></slot> --><slot name='msg1'></slot> <!-- 2.显示传入的 --><slot name='msg3'>传就显示传的内容,没有传入内容,就显示这里的默认内容</slot><!-- 3.传就显示传的,没传就显示默认的 --><slot name='msg2'></slot><slot name='msg4'></slot><!-- 4.传就显示,没传就不显示 --></div> </template><script src="node_modules/vue/dist/vue.js"></script><script>let comp = {template:'#comp',}; let vm = new Vue({el:'#app', components:{comp, },})</script></body></html>

8. 组件切换 与 缓存

1.<component :is='val'></component>这是vue自带的标签,不用声明可以直接用类似的还有template、slot 、transition、transiton-group

2. 组件切换的时候,会进行销毁旧的然后挂载新的,但是我们反复切换的时候,希望缓存组件,可以把组件放到keep-alive标签里

<keep-alive><component :is='val'></component></keep-alive>

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><input type="radio" v-model='val' value='home'>home<input type="radio" v-model='val' value='list'>list <keep-alive><component :is='val'></component></keep-alive><!-- 这是vue自带的标签,不用声明可以直接用类似的还有template、slot 、transition、transiton-group --><!-- 组件切换的时候,会进行销毁旧的然后挂载新的,但是我们反复切换的时候,希望缓存组件,可以把组件放到keep-alive标签里 --><!-- <keep-alive></keep-alive> 一般用作缓存:为后面的路由做准备以前页面的切换都是点击一下重新渲染一下,但是路由的功能是可以缓存,把组件缓存起来如果缓存了就不会再走created 、mounted等钩子函数了--><!-- 子组件和父组件同时拥有mounted方法,会先走谁的?mounted-挂载完,父组件挂载前需要先确定子组件挂载完了需要等待子组件挂载完成后再触发父组件的挂载--></div><script src="node_modules/vue/dist/vue.js"></script><script>//keep-alive 保持连接 ,组件有个方法:销毁let home ={template:'<div>home</div>'}let list ={template:'<div>list</div>'}let vm = new Vue({el:'#app', data:{val:'home'},components:{home, list},})</script></body></html>

9. 强调 this.$nextTick(callback)

dom渲染是异步的,当我们改变数据时,有可能dom重新渲染还没渲染完,强烈建议把dom操作放在this.$nextTick(callback)的回调里,等dom渲染完再执行回调函数

<!DOCTYPE html><html lang="en"><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>Document</title><link rel="shortcut icon" href="#" type="image/x-icon"></head><body><div id="app"><son ref='son'></son></div><script src="node_modules/vue/dist/vue.js"></script><script>let vm = new Vue({el:'#app', mounted(){console.log(this.$refs.son.$el.innerHTML); //<ul><li>1</li><li>2</li><li>3</li></ul> //2.然后走父组件的mounted,此时子组件的dom渲染还没渲染完,所以获取到的是之前没改数据前的this.$nextTick( ()=>{console.log(this.$refs.son.$el.innerHTML) ; //<ul><li>1</li><li>2</li><li>3</li></ul> //3.所以强烈建议把dom操作放在this.$nextTick(callback)的回调里,等dom渲染完再执行回调函数});},components:{son:{template:`<div><ul><li v-for='item in arr'>{{item}}</li></ul></div>`,data(){return {arr:[1,2,3]}},mounted(){//1.此时dom已经渲染完了,页面数据是123,先执行子组件的mounted,在mounted里数据改了,然后重新渲染dom,但dom渲染是异步的,this.arr = [4,5,6];}}},})</script></body></html>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。