一步一步学习Vue(十)

本篇说一下组件通信的问题,父子组件通信,前面的博客中已有说明,vue也推荐props in,event out;兄弟节点通信如何做呢?官方其实也给出了实现方式,我们以下面的场景来实现一下:

上图中,实现如下功能:搜索表单组件中,包含各种搜索条件,当点击搜索按钮时,加载数据到列表组件中渲染。

这里会给出三种实现方式,不涉及合适与否,只为演示。

1、使用父组件进行封装,把所有操作都移到父组件中

2、搜索组件,触发事件到父组件,父组件监听到事件发生,则执行查询操作,传递props 到列表组件,这也是我们前面实现过的方式,这里简单写一个demo。

首先定义我们的组件:SearchComponent 、AppComponent、ListComponent

<!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>demo4</title>
    <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>

</head>

<body>
    <div id="app">
        <app></app>
    </div>
    <script>
        var SearchComponent = {
            template:`
            <div class="toolbar">
                <input type="text" placeholder="keyword"  v-model="keyword"/>
                <input type="text" placeholder="description" v-model="desc"/>
                <input type="button" value="search" @click="search()" />
             </div>
            `,
            data:function(){
                return {
                    keyword:'',
                    desc:''
                }
            },
            methods:{
                search:function(){
                    this.$emit('onsearch',{keyword:this.keyword,desc:this.desc});
                }
            }
        }

        var ListComponent = {
            template:`
            <div class="list" >
                {{list}}
            </div>
            `,
            props:['list']
        }

        var AppComponent={
            template:`
            <div class="container">
                <search @onsearch="search($event)" ></search>
                <list :list="datas" ></list>
            </div>
            `,
            components:{
                'list':ListComponent,
                'search':SearchComponent
            },
            methods:{
                search:function($e){
                    this.datas=JSON.stringify({
                        data:[],
                        info:'info'
                    });
                }
            },
            data:function(){
                return {
                    datas:null
                }
            }
        }

        var app=new Vue({
            el:'#app',
            components:{
                'app':AppComponent
            }
        });
    </script>
</body>

</html>

点击搜索按钮,运行效果如下:

上面的例子非常简单,而且所写代码在前面的博文中都有所介绍,这里就不详述了,在这里数据流流向如下:

1、点击按钮,数据由 search组件流向父组件

2、父组件监听onsearch ,监听到事件后,处理并给list赋值,此时数据由 父组件 流向 list组件

父组件这里的作用就是一个中转站,提供了一种数据流的中转功能。那么如果没有父组件,能否实现上述功能呢,毕竟我们不可能每次兄弟组件通信都创建一个多余父组件过来,这样如果嵌套层数过多也是很大的问题,对于兄弟组件通信的问题,官方也提到了叫做event bus的实现方式,下面我们就实现一下第二种方案,基于event bus:

修改我们的代码如下:

<!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>demo4</title>
    <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>

</head>

<body>
    <div id="app">
        <search></search>
        <list></list>
    </div>
    <script>
        var eventBus = new Vue();

        var SearchComponent = {
            template: `
            <div class="toolbar">
                <input type="text" placeholder="keyword"  v-model="keyword"/>
                <input type="text" placeholder="description" v-model="desc"/>
                <input type="button" value="search" @click="search()" />
             </div>
            `,
            data: function () {
                return {
                    keyword: '',
                    desc: ''
                }
            },
            methods: {
                search: function () {
                    // this.$emit('onsearch',{keyword:this.keyword,desc:this.desc});
                    eventBus.$emit('onsearch', { keyword: this.keyword, desc: this.desc });
                }
            }
        }

        var ListComponent = {
            template: `
            <div class="list" >
                {{list}}
            </div>
            `,

            data: function () {
                return {
                    list: null
                }

            },
            created: function () {
                var self = this;
                eventBus.$on('onsearch', function ($e) {
                    console.log($e);
                    self.list = JSON.stringify($e);
                })
            }
        }



        var app = new Vue({
            el: '#app',
            components: {
                // 'app':AppComponent
                'list': ListComponent,
                'search': SearchComponent
            }
        });
    </script>
</body>

</html>

这里借助一个全局的vue空实例,来实现一个全局的eventbus,当然我们也可以使用或者实现自己的eventbus,这个是比较简单的,(大致思路是:定义一个回调列表数组,定义两个方法,一个on一个emit,on即是向回调数组push key 和对应的function,emit就是触发key对应的function)有兴趣的可以简单做一下,保存后运行即可。

对于简单的兄弟组件通信,其实这种方案或者第一种方案已经满足,但是如果兄弟节点过多或者组件层次很深的时候,使用第一种方案我们必须一层一层的传递几乎重复的代码,使用第二种方案所有组件又全部依赖于全局vue实例或者说全局eventbus,有没有更好的状态管理方案呢?能否把状态管理独立出来呢,这就是我们接下来要说的vuex。

Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel调试、状态快照导入导出等高级调试功能,如果打开官网,你会看到上面这段话。

这都是什么乱七八糟的,可能让人看不明白,虽然看起来逼格很高,其实它只是做的比我们的eventbus 更高级一点:

每一个 Vuex 应用的核心就是 store(仓库)。"store" 基本上就是一个容器,它包含着你的应用中大部分的状态(state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交(commit) mutations。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

vuex中包含很多概念和约定,今天我们就开始体验一下,话不多说,同样的功能基于vuex重构一下:

<!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>demo4</title>
    <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>
    <script src="https://cdn.bootcss.com/vuex/2.3.1/vuex.js"></script>

</head>

<body>
    <div id="app">
        <search></search>
        <list></list>
    </div>
    <script>
        // var eventBus = new Vue();
        var store = new Vuex.Store({
            state: {
                list: null
            },
            mutations: {
                search: function (state, payload) {
                    state.list = JSON.stringify(payload);
                }
            }
        })

        var SearchComponent = {
            template: `
            <div class="toolbar">
                <input type="text" placeholder="keyword"  v-model="keyword"/>
                <input type="text" placeholder="description" v-model="desc"/>
                <input type="button" value="search" @click="search()" />
             </div>
            `,
            data: function () {
                return {
                    keyword: '',
                    desc: ''
                }
            },
            methods: {
                search: function () {
                   this.$store.commit("search",{ keyword: this.keyword, desc: this.desc })
                    //eventBus.$emit('onsearch', { keyword: this.keyword, desc: this.desc });
                }
            }
        }

        var ListComponent = {
            template: `
            <div class="list" >
                {{list}}
            </div>
            `,
            computed:{
                list:function(){
                    return this.$store.state.list;
                }
            }
           
        }



        var app = new Vue({
            el: '#app',
            store:store,
            components: {
                // 'app':AppComponent
                'list': ListComponent,
                'search': SearchComponent
            }
        });
    </script>
</body>

</html>

这里我们创建了一个全局store,store是唯一的,里面保存着所有的状态(这种状态建议是全局的或者共享的,我们这里假设list组件中的state属于共享,大家不要较真,而search中的state属于组件本身状态),我们做如下约定:不要直接修改状态,要通过提交mutations来修改状态,mutations相当于在react中使用setState去修改状态一样。直接修改会运行时异常。

针对上面的代码,主要包括如下几个知识点:

1、vuex的实例化:直接new Vuex.Store ,创建全局唯一store,通过配置参数,设置state(全局共享的)、mutations(只支持同步操作)

2、vuex和vue的联系,通过new Vue实例时,注入store,这里和前文中注入router类似,注入后,在任何子组件中,就可以通过this.$store来访问store了

3、store中的state是响应式的,所以建议定义为组件计算属性,每次通过mutations提交修改,则可直接驱动view的变化。

本节主要引入vuex,算是vuex的开篇,不介绍过多内容,让我们有一个简单的认识,接下来会向介绍vue-router一样,慢慢的深入其它的方方面面。敬请期待。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

一个Web页面的问题分析

几个月之前我接到一个新的开发任务,要在一个旧的Web页面上面增添一些新的功能。在开发的过程中发现旧的代码中有很多常见的不合适的写法,结合这些问题,如何写出更好的...

1849
来自专栏深度学习计算机视觉

java反射学习笔记

首先搞清楚什么是java反射机制(下面是百度百科的定义) JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,...

2629
来自专栏架构师之路

perl语言十分钟入门【零基础可入】

零基础,perl语言,10分钟入门 1.Hello,World #!/usr/bin/perl -w print ("hello,world!\n"); #pr...

3547
来自专栏Angular&服务

Angular2 组件的使用

3.在 @Component 中 ,设置selector、template 和 styles 等元数据

1193
来自专栏北京马哥教育

Python工程师面试必备25条Python知识点

1.到底什么是Python?你可以在回答中与其他技术进行对比 下面是一些关键点: Python是一种解释型语言。这就是说,与C语言和C的衍生语言不同,Pytho...

2986
来自专栏恰同学骚年

ASP.Net请求处理机制初步探索之旅 - Part 4 WebForm页面生命周期

开篇:上一篇我们了解了所谓的请求处理管道,在众多的事件中微软开放了19个重要的事件给我们,我们可以注入一些自定义的业务逻辑实现应用的个性化设计。本篇,我们来看看...

542
来自专栏大数据文摘

谷歌R语言格式指南

1513
来自专栏轮子工厂

2. C语言 -- printf 的花式操作

(。・∀・)ノ゙嗨!大家好,我是呆博~很开心可以在这里给接着大家分享我的 C 语言学习笔记~因为微信对于代码块的支持并不是很好,所以代码部分以截图形式呈现,如果...

1157
来自专栏JAVA同学会

JAVA9模块化详解(二)——模块的使用

各自的模块可以在模块工件中定义,要么就是在编译期或者运行期嵌入的环境中。为了提供可靠的配置和强健的封装性,在分块的模块系统中利用他们,必须确定它们的位置,然后决...

522
来自专栏青枫的专栏

Java培训实战教程之Java基础知识精华部分(一)(二)(三)

482

扫码关注云+社区