Vue的组件传值
首先Vue我认为有几个最大的特点,
第一点数据驱动,无需手动操作dom。以及MVVM模式第二点支持模块化开发vNode虚拟dom diff算法
vue是数据驱动视图更新的框架, 所以对于vue来说组件间的数据通信非常重要,那么就需要知道在vue中组件之间存在什么样的关系, 才更容易理解他们的通信方式,并且在模块化开发时,一定是需要拆分的,也就是我们常用的路由以及组件等方式,所以衍生出了这个问题,如何进行组件之间的传值问题,又为什么必须要这么去做呢?
首先模块化开发,使用的是树形结构,必须要有一个根,根组件向下传递是很容易实现的事情,定义一个属性,然后在下级组件中使用props进行接收即可。在vue中组件间的关系存在两种关系:即父子关系,和非父子关系
父级向下传递数据
<body><div id="app"><child :day='day'></child></div></body><script src="./js/vue.js"></script><script type="text/javascript">var child = {// props形式一:数组形式props: ['day'],// props形式二:对象形式props: {day: {default: '日',type: String}},template: '<p>星期{{day}}</p>'}const vm = new Vue({el: '#app',data: {day: '五'},components: {child}})</script>
但是问题在于传递个子组件的值,子组件可以任意进行更改,而对于父组件来说,不仅仅只有一个子组件,这样就有可能破坏了其他子组件的引用关系,从而出现问题,并且最大的问题在于,它破坏了我们的树状结构,由下而上的更改我们父级的值,打破了规则,这是完全不能接受的。所以不能直接在子组件中去操作父组件传递过来的数据!!那么如果想要操作我们有两个取巧的方法,当然仍然遵守规则,子组件不可更改父组件内的数据。 第一个方法:在子组件中自己定义一个变量去保存父组件的数据,然后修改我们保存下来的变量的值达到效果第二个方法: 使用computed来改变可以将父组件传递的数据仅作为初始化数据,接下来的数据变化时使用改变数据
const child = {props: ["titleValue","propValue","x","y","z"], template:`<div>{{title2}}</div>`,data(){return {/* 个人理解: 简单来说 我们将父级传递来的数据本地化,初始化那么第一次使用的时候就是父级传递来的值,下次再改变时,也不会修改父级传递值的内容类似于 重新赋值*/title: this.titleValue, // 只被渲染一次 }},mounted () {// 单向数据流 如果在这里改变父组件传递过来的值 父级props的更新会流动到子组件中,// 但是反过来不行,这样会防止从子组件以外变更父级组件的状态,从而导致你的应用数据流难以理解// 如果想要修改父组件传递过来的值,那么我们可以将这个值setTimeout(()=>{this.title="world"},2000)console.log(typeof this.propValue);},// 方法二: 我们通过computed 来改变computed: {title2(){return this.title+"!!!";}},}
父级向下传递数据 二
使用provide和inject依赖注入 总是成对出现使用方法:使用provide在父组件中返回要传输的数据,使用inject在下级组件中进行注入数据
// 父组件provide(){return {user:this.user,} },
// 子组件inject:{user:{default:()=>{}},
另外再说一个小知识点
const child3={//如果写props验证 就可以使用传入数据在template中的标签内使用//如果不写 会默认将传入数据 放在组件的"根"元素上 简单的理解 就是写在了标签上(一鱼两吃!) 做了一个活口//inheritAttrs属性是 禁止Attribute的继承 也就是即便不写props也不会被继承//但是有例外 只有stype和class仍然会被设置在标签上 其他即便是id也不会被设置inheritAttrs: false,template:`<div></div>` }
子级向上传递数据
讲完了父组件向子组件传递,再来将子组件如何向父组件传递数据 子组件模版内容中用$emit()
定义自定义事件
,$emit()
方法至少有2个参数 第一个参数为自定义的事件名称(不要和内置的事件重名,例如click、change等)abc第二个参数为需要传递的数据(可选,如果传可以是任何格式的数据)…
父组件模板内容中的子组件占位标签上用v-on(或@)绑定子组件定义的自定义事件名,监听子组件的事件,实现通信
示例代码:每点击子组件按钮给父组件字体加9像素
<body><div id="app"><child @anlarge-text='bigger'></child><p :style="{fontSize: fontSize + 'px'}">{{msg}}</p></div></body><script src="./js/vue.js"></script><script type="text/javascript">// 子组件var child = {template: `<button @click="$emit('anlarge-text',9)">点我给父组件字体加9px</button>`,}// 根组件(父)const vm = new Vue({el: '#app',data: {msg: 'hello vue',fontSize: 12},components: {child},methods: {bigger: function (n){this.fontSize += n}}})</script>
个人理解:在这个地方,并不是很难理解,拿案例来说,如果我想点击变大,那么父组件如何知道我进行了点击,那么就通过一个自定义事件,来将父子组件联系起来,当子组件进行点击事件触发了自定义事件并且传参,那么父组件同样可以接收到自定义事件的触发,从而触发他的事件处理函数,即子组件点击->自定义事件触发->父组件触发事件处理。 通过事件的方式将数据进行了传递。
子级向上传递数据 方法二
使用$parent.获取父组件对象,然后再获取数据对象
mounted() {this.msg2=this.$children[0].msg }
补充: 在父传子时同样可以使用
this.msg22 = this.$parent.msg2; 来获取
子级向上传递数据 方法三
父去取子的数据信息。
ref
属性被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的$refs
对象上。如果在普通的 DOM 元素上使用ref
属性,则引用指向的就是 DOM 元素;如果ref
属性用在子组件上,引用就指向子组件实例。
ref
放在标签上,拿到的是原生节点。ref
放在组件上 拿到的是组件实例原理:在父组件中通过ref
属性(会被注册到父组件的$refs
对象上)拿到组件/DOM对象,从而得到组件/DOM中的所有的信息,也包括值
<!-- 普通DOM --><p ref="p">hello</p><!-- 子组件 --><child-comp ref="child"></child-comp><script>new Vue({el: '#app',data: {},mounted: function(){console.log(this.$refs.p);console.log(this.$refs.child);this.$p.msg = '123' // 修改值}})</script>
**非父子传递数据 **
先介绍一个Bus总线的方法 那么它能够做什么呢?它不仅可以进行非父子组件的数据传输,同样可以进行父子组件的数据传输,听起来非常好用,但是它会破坏规则,并且在大型项目开发时,会导致关系混乱,所以并没有想象中那么好用
EventBus
在Vue中通过单独的事件中心
来管理非父子关系
组件(兄弟)间的通信:
这里来讲一下什么是Bus总线,举个栗子:某情侣因为异地分居这时候没有办法直接见到彼此,所以就每天需要电话通信,男方就是组件A,女方就是组件B,事件中心就是运营商,当男方或者女方给对方打电话时,就会触发运营商的监听事件,发送消息给另外一方就实现了通信了。
核心步骤建立事件中心
const eventBus = new Vue()
传递数据
eventBus.$emit('自定义事件名',传递的数据)
接收数据
eventBus.$on('自定义事件名'[,callback])
销毁事件中心
eventBus.$off('自定义事件名')
示例代码:实现回合制互相伤害
<body><div id="app"><zj_one></zj_one><hr/><zj_two></zj_two><hr/><button @click='destoryBus'>炸掉事件中心</button></div></body><script src="./js/vue.js"></script><script type="text/javascript">// 定义事件中心const eventBus = new Vue()ponent('zj_one',{data: function(){return {data: 100}},template: `<div><div>{{data}}</div><div><button @click='fn1'>点我让对方受到1点伤害</button></div></div>`,methods:{fn1: function(){eventBus.$emit('zj2_event',1)}},mounted:function(){eventBus.$on('zj1_event',val => this.data -= val)}})ponent('zj_two',{data: function(){return {data: 100}},template: `<div><div>{{data}}</div><div><button @click='fn2'>点我让对方受到2点伤害</button></div></div>`,methods:{fn2: function(){eventBus.$emit('zj1_event',2)}},mounted:function(){eventBus.$on('zj2_event',val => this.data -= val)}})new Vue({el: '#app',methods: {destoryBus: function(){// 销毁双方监听的事件eventBus.$off('zj1_event')eventBus.$off('zj2_event')}}})</script>
总结
Vue 推出了一个状态管理工具 Vuex,可以很方便实现组件之间的参数传递。