父子组件通信
1, 父子组件通过 prop 传递数据
父组件可以将一条数据传递给子组件, 这条数据可以是动态的, 父组件的数据更改的时候, 子组件接收的也会变化.
子组件被动的接收父组件的数据, 子组件不要再更改这条数据了.
组件实例的作用域是孤立的, 父组件不能直接使用子组件的数据, 子组件也不能直接使用父组件的数据.
父组件在调用子组件的时候给子组件传递数据:
- <template id="father">
- <div class="father">
- <p > 我是父组件, 这是我的 fMsg:{{fMsg}}</p>
- <input type = "text" v-model = "fMsg">
- <hr>
- <son msg = "你好"></son>
- </div>
- </template>
父组件给子组件传递数据的时候, 子组件需要利用 props 的属性来确定自己的预期数据, 如果儿子没有通过 props 属性接受传递过来的数据, 则数据会以自定义属性的方式, 放在儿子最外层的根元素上面.
image
子组件通过 props 来接受父组件传递过来的数据, 并且通过 {{msg}} 使用
- components:{
- son:{
- template:"<div > 我是 son 子组件! 这是父组件传递给我的 msg:{{msg}}</div>",
- // 接收父组件传递来的属性 msg
- props:["msg"]
- }
- }
image
2, 父组件通过 v-bind 指令传递自身变量给子组件
我们可以用 v-bind 来动态地将 prop 绑定到父组件的数据. 每当父组件的数据变化时, 该变化也会传导给子组件.
- <template id="father">
- <div class="father">
- <p > 我是父组件, 这是我的 fMsg:{{fMsg}}</p>
- <input type = "text" v-model = "fMsg">
- <hr>
- <!-- <son msg = "你好"></son> -->
- <son :msg = "fMsg"></son>
- </div>
- </template>
如果如果父组件传递属性给子组件的时候键名有'-'
<son :f-msg = "fMsg"></son>
子组件接收, 使用的时候写成小驼峰的模式
- components:{
- son:{
- template:"<div > 我是 son 子组件! 这是父组件传递给我的 msg:{{fMsg}}</div>",
- // 接收父组件传递来的属性 msg
- props:["fMsg"]
- }
- }
3,prop 验证传递过来的数据
我们可以为组件的 prop 指定验证规则. 如果传入的数据不符合要求, vue 会发出警告. 这对于开发给他人使用的组件非常有用
验证主要分为: 类型验证, 必传验证, 默认值设置, 自定义验证
类型验证
- // 类型验证
- // num:Number // 父组件传递过来的 num 必须是 Number 类型
- // 添加多个类型
- num: [Number,String],
规定传递过来的数值类型必须是 number, 否则系统会报错
image
必传验证
规定父组件必须给子组件传递该值
- // 必传验证
- // num:{
- // required: true
- //
- }
默认值设置
当父组件不给子组件传递该值的时候, 给子组件设定一个默认值
- // 默认值设置
- // num:{
- // default:100
- //
- }
自定义验证
- // 自定义验证
- num:{
- validator(val){
- return val> 100
- }
- }
规定传递过来的数值必须要大于 100, 否则系统会报错
image
4, 父子组件依靠应用类型的地址传递共享数据
单向数据流
Prop 是单向绑定的: 当父组件的属性变化时, 将传递给子组件, 但是反过来不会. 这是为了防止子组件无意间修改了父组件的状态, 来避免应用的数据流变得难以理解.
- <template id="father">
- <div class="father">
- <input type = "text" v-model = "message">
- <hr>
- <son :message = "message"></son>
- </div>
- </template>
- <template id = "son">
- <div>
- <p > 这是子组件</p>
- <input type = "text" v-model = "message"></input>
- </div>
- </template>
另外, 每次父组件更新时, 子组件的所有 prop 都会更新为最新值. 这意味着你不应该在子组件内部改变 prop. 如果你这么做了, Vue 会在控制台给出警告.
image
所以如果我们想实现父子间的数据共享, 依靠的就是应用类型的地址传递, 应将 message 写成对象的形式, 传递的时候将对象传递给子组件, 子组件引用的时候使用对象的 value 值.
- <template id="father">
- <div class="father">
- <input type = "text" v-model = "message.value">
- <hr>
- <!-- 传递的时候将对象传递给子组件 -->
- <son :message = "message"></son>
- </div>
- </template>
- <template id = "son">
- <div>
- <p > 这是子组件</p>
- <!-- 引用的时候使用对象的 value 值 -->
- <input type = "text" v-model = "message.value"></input>
- </div>
- </template>
这时候更改父组件的 value 值, 子组件的数据同步更改, 子组件修改 value 值的时候也同步修改了父组件的数据. 这是因为不管是子组件还是父组件, 我们操作的都是同一个对象, 父组件直接把引用类型的地址传递给子组件, 子组件没有直接修改对象, 只是更改了里面的属性值.
父组件如果将一个引用类型的动态数据传递给子组件的时候, 数据会变成双向控制的, 子组件改数据的时候父组件也能接收到数据变化, 因为子组件改的时候不是在改数据(地址), 而是在改数据里的内容, 也就是说引用类型数据的地址始终没有变化, 不算改父组件数据.
注意: 在 JavaScript 中对象和数组是引用类型, 指向同一个内存空间, 如果 prop 是一个对象或数组, 在子组件内部改变它会影响父组件的状态. message:{val:""}
5,viewmodel 关系链
在组件间可以用过 ref 形成 ref 链, 组件还拥有一个关系链($parent), 通过这两种链; 理论来说, 任意的两个组件都可以互相访问, 互相进行通信.
$parent: 父组件
$children: 子组件
$root: 根组件
image
当子组件在 set 方法中修改父组件传递过来的值时, 系统会报错, 因为子组件不能修改父组件的数据.
- Vue.component("bbb",{
- template:"#bbb",
- props:["msg"],
- computed:{
- /* ownMessage(){
- return this.msg;
- } */
- ownMessage:{
- get(){
- return this.msg;
- },
- set(val){
- this.msg = val // 系统报错: 子组件不能更改父组件传递的数据
- }
- }
- }
- })
image
所以这时候要使用 $parent, 让父组件自己更改自己的数据
- set(val){
- // this.msg = val // 系统报错: 子组件不能更改父组件传递的数据
- // console.log(this)
- // 相当于父组件自己更改了 msg 数据
- this.$parent.msg = val;
- }
6, 父组件通过 ref 标记获取子组件的数据
父组件在调用子组件的时候使用 ref 做标记
- <template id="aaa">
- <div>
- <button @click = "get">点击获取 bbb 数据</button>
- <!-- 组件间不仅可以用过 $root/$parent/$children 来获取对应关系的组件, 父组件还可以主动的通过 ref 为子组件做标记 -->
- <bbb ref = "b"></bbb>
- </div>
- </template>
父组件的 this 属性上有 $refs 标记, 通过 refs 标记拿到子组件
image
- // 通过 ref 标记更改子组件的数据
- // this.$refs.b.message = "哈哈"
组件间不仅可以用过 $parent/children/root 来获取对应关系的组件, 父组件还可以主动的通过 ref 为子组件做标记 也可以给 dom 做标记, 也会形成 ref 链, 也可以交互.
- <button ref="btn" @click="get">
- get
- </button>
- <bbb ref="b></bbb>
image
注意多个子组件标记的是同一个键名, 获取到的应该是一个数组
<bbb ref = "b" v-for = "(item,index) in 3" :key = "index"></bbb>
image
- // 通过下标修改对应的数值
- this.$refs.b[0].message = "哈哈"
运行效果:
image
子父组件通信
1, 子组件通过父组件传递的方法来更改父组件的数据
父组件可以将更改自身数据的方法传递给子组件, 子组件调用这个方法的时候, 就可以给父组件传递数据, 父组件被动的接收子组件的数据.
子组件声明一条自身的 msg
- Vue.component("son",{
- template:"#son",
- // 子组件接收父组件传递过来的方法
- props:["change"],
- data(){
- return{
- msg:"我是子组件"
- }
- }
- })
父组件先声明一条自己的数据
- data(){
- return{
- // 父组件先声明一条自己的数据
- parentMsg:""
- }
- }
再写一个可以更改自身数据的方法
- methods:{
- // 写一个可以更改自身数据的方法
- change(msg){
- this.parentMsg = msg
- }
- }
将写好的 change 方法传递给子组件
- <template id="father">
- <div>
- <p > 这是父组件</p>
- <p > 子组件传递过来的值是:{{parentMsg}}</p>
- <hr>
- <!-- 调用子组件的时候, 将更改自身数据的方法传递给子组件 -->
- <son :change = "change"></son>
- </div>
- </template>
子组件通过 props 接收父组件传递过来的 change 方法
props:["change"]
给 p 标签添加点击事件, 点击即触发 change 方法, 同时将自身的 msg 传递给父组件, 相当于父组件的 change 方法被执行.
- <template id="son">
- <div>
- <p > 子组件说:{{msg}}</p>
- <p @click = "change(msg)">点击我触发父亲的 change 方法</p>
- </div>
- </template>
父组件可以在页面中渲染子组件传递过来的数据
<p > 子组件传递过来的值是:{{parentMsg}}</p>
运行效果:
image
2, 通过自定义事件实现子父通信
每一个组件或者实例都会有自定义事件, 和触发事件的能力, 父组件给子组件绑定一个自定义事件, 这个事件的处理程序却是父组件的一个方法, 当子组件触发这个事件的时候, 相当于父组件的方法被执行.
父组件想获取子组件的数据时, 在调用子组件的时候给子组件绑定一个自定义事件 change-event
- <template id="father">
- <div>
- <p > 这是父组件</p>
- <p > 子组件传递过来的值是:{{parentMsg}}</p>
- <hr>
- <!-- 给子组件绑定一个自定义事件 -->
- <son @change-event = "change"></son>
- </div>
- </template>
在子组件中定义一个点击事件, 点击 p 标签执行 changeWord 方法
<p @click = "changeWord">点击我触发父亲的 change 方法</p>
在方法中编写 changeWord 方法, 通过 this.$emit 来触发绑定在自己身上的自定义事件, 第一个参数为事件名称 change-event, 第二个参数为触发这个函数的时候给他传递的数值: 自身的 msg.
- methods:{
- changeWord(){
- // 触发自身绑定的 change 事件
- this.$emit("change-event",this.msg)// 第一个参数为触发事件的名字, 第二个参数为触发这个函数的时候给他传递的数值
- }
- }
一旦触发绑定在自身上的自定义事件, 相当于父组件的 change 方法被执行.
兄弟组件通信
1, 通过 viewmodel 关系链
定义哥哥组件, 给哥哥组件添加一个点击事件, 点击触发 hitLittle 方法
- <template id = "big-brother">
- <div>
- <p > 我是哥哥</p>
- <button @click = "hitLittle">打弟弟</button>
- </div>
- </template>
定义弟弟组件, 给弟弟组件添加一个 p 标签, 由 crying 数据控制其显示与隐藏
- <template id="little-brother">
- <div>
- <p > 我是弟弟</p>
- <p v-if = "crying">呜呜呜</p>
- </div>
- </template>
在弟弟组件的 data 中声明 crying 数据, 默认为 false
- Vue.component("little-brother",{
- template:"#little-brother",
- data(){
- return{
- crying:false
- }
- }
- })
在哥哥组件的 methods 中定义 hitLittle 方法, 通过 viewmodel 关系链更改弟弟组件中的 crying 方法
- Vue.component("big-brother",{
- template:"#big-brother",
- methods:{
- hitLittle(){
- // 在兄弟组件之间的通信, 可以采用关系链和 ref 链去使用, 解决兄弟之间通信问题.
- this.$parent.$children[1].crying = true;// 让 littel 改变自身的 crying 状态
- }
- }
- })
运行效果:
image
2,viewmodel 关系链 + ref 链
在弟弟组件中添加 ref 标记
<little-brother ref = "little"></little-brother>
在哥哥组件的 hitLittle 方法中通过 viewmodel 和 ref 链配合使用更改弟弟组件中的 crying 数据
- hitLittle(){
- // 在兄弟组件之间的通信, 可以采用关系链和 ref 链去使用, 解决兄弟之间通信问题.
- // this.$parent.$children[1].crying = true;// 让 littel 改变自身的 crying 状态
- //viewmodel 链和 ref 链配合使用
- this.$parent.$refs.little.crying = true;
- }
3,eventbus 事件总线
创建一个空的实例
var angle = new Vue();
弟弟组件自己定义一个更改自身状态的方法
- methods:{
- cry(){
- this.crying = true
- }
- }
在 mounted 生命周期函数中绑定一个自定义事件, 第一个参数为自定义事件名, 第二个函数为需要处理的函数
- mounted(){
- // 绑定一个自定义事件, 第一个参数为自定义事件名, 第二个函数为需要处理的函数
- angle.$on("hit-little",this.cry)
- }
在哥哥组件中触发自定义事件
- hitLittle(){
- // 触发 little-brother 组件的 hit-little 事件
- angle.$emit("hit-little")
- }
4,vuex 状态管理
vuex 是 vue 提供的一个全局的状态管理工具, 主要处理项目中多组件间状态共享.
Vuex 是 vue 官方的一款状态管理工具, 什么是状态呢? 我们在前端开发中有一个概念: 数据驱动, 页面中任意的显示不同, 都应该有一条数据来控制, 而这条数据又叫做 state, 状态.
在 vue 中. 组件间进行数据传递, 通信很频繁, 而父子组件和非父子组件的通信功能也比较完善, 但是, 唯一困难的就是多组件间的数据共享, 这个问题由 vuex 来处理
(1)创建 store
- import Vue from 'vue'
- import Vuex from 'vuex'
- Vue.use(Vuex)
(2)设置 state
state 就是一个纯对象, 上面有一些状态挂载
- state: {
- num:0,
- name:"list"
- }
(3)在根实例里配置 store
这样, 我们就可以在任意的组件中通过 this.$store 来使用关于 store 的 API
- import store from './store/index'
- Vue.config.productionTip = false
- new Vue({
- router,
- store,
- render: h => h(App)
- }).$mount('#app')
(4)在 Home 组件中使用 state
- computed:{
- ...mapState({
- num:state=>state.num
- })
- },
(5)使用 mutations 更改 state
mutations 也是一个纯对象, 里面包含很多更改 state 的方法, 这些方法的形参接收到 state, 在函数体里更改, 这时, 组件用到的数据也会更改, 实现响应式.
- mutations: {
- changeNum(state){
- state.num++
- }
- }
(6)在组件中调用 mutations 方法, 更改组件中的 state.
- // 使用 vuex 提供的 mapMutations 帮助我们在组件中调用 mutations 方法
- ...mapMutations(["changeNum"]),
- // 给按钮添加点击事件
- <button @click = "changeNum">点击更改 num 值</button>
运行效果:
image
来源: http://www.jianshu.com/p/ae8d4a98165d