Vue 组件间通讯

vuejs

最近回顾前端,先是 React 现在因工作需又要开始 vue 了。开发过程中多半时间我们需要结构,有层次有上下级,有秩序。有了秩序也就是约束,有了约束也就是有了规则。从而减少我们思考时间。因为麦当劳只是在 10 点半前提供早餐,我们就无需考虑在 10 点半后买到经济实惠的早餐。


mc 早餐

但有时候我们还需要打破秩序,逆流而上。做 web 开发有一段时间,最近遇到复杂业务还是有点摸不到头脑,再加上时间紧任务重往往写出繁琐难懂 code。一直在想如何能够轻松地开发出流畅 web。

其实 web 应用中,数据流是从服务端流出在流回到服务端,知道程序启动了这个数据就开始流动。但是有的人会问,那么从服务端获取列表数据展示,数据也没有流回到服务端,这样可以想象为原封不动地流回到服务端。希望我们多一点想象。

我们应用是界面是一颗 UI 树,每一个节点可能是原生 DOM 或是自定义 component ,component 也是 DOM 组成。树接口是非线性的,但是也是有层次的。我们控制数据在这棵树上流动。数据让我们应用之树变得绚丽多彩,数据是 UI 树的血液,数据让 UI 树有了生命力。

我们将树进行划分区域来控制数据,我们控制数据流动范围,控制数据流动方向。

数据多数流三种方式,框架都应该给出这种三种流动方式给出答案

  • 从父级向子级传递数据
  • 从子级向父级传递数据
  • 跨越多重级别传递数据

从父组件向子组件传递数据(props)

<template>
    <div>
        <div class="text-4xl text-white bg-blue-200">{{title}}</div>
    </div>
</template>
  • 在子组件中定义 props 可以接受从父级传过来变量
  • 父组件
<template>
    <div >
        <Title class="text-lg" title="user list"/>
        <div class="" v-for="user in users" :key="user.id">
            <User message="hello" v-bind:user="user"/>
        </div>
    </div>
</template>
  • 父级调用子组件(User) 可以通过属性 message 传递字符串 hello 给子组件,这种方式我们只能传递字符串。如果传递 data 中一个对象 user 这种方式是无法接受到 user 而是接受到 ”user“ 的字符串。这时候我们就需要用到动态绑定。

动态绑定

  • 下面示例是一个在父级获取用户列表然后将列表中 User 对象传递给 User 组件显示出来用户信息。
父组件
<template>
    <div >
        <div class="" v-for="user in users" :key="user.id">
            <User message="hello" v-bind:user="user"/>
        </div>
    </div>
</template>

<script>
import axios from "axios";
import User from "../components/User.vue";
export default {
  name: "Users",
  components: {
    User
  },
  data: () => {
    return {
      users: []
    };
  },
  created() {
    axios.get("https://jsonplaceholder.typicode.com/users").then(response => {
      console.log(response.data);
      this.users = response.data;
    });
  }
};
</script>

<style>
</style>

  • v-bind:user 这种方式可以将 user 对象传递给子组件 User
子组件
<template>
    <div>
        <p>{{user.name}}</p>
        <p>{{user.email}}</p>
    </div>
</template>

<script>
export default {
  name: "User",
  props: ["user", "message"],
  data: () => {
    return {};
  }
};
</script>

<style>
</style>

我们可以为 prop 定义类型,定义了类型也就是约束少了一些选择,选择少了我们就更明确我们能够得到什么 string 类型必填的 title 的属性。

export default {
  name: "Title",
  props: {
    title: {
      type: String,
      required: true
    }
  }
};

从子组件向父组件传递数据(事件)

父组件

这个实例是这样的,一个课程列表,列表中每个课程都有一个等级,通过 rating 属性来表示为 1 - 5 。我们选择了一个课程,这是现实该课程的等级,等级信息是从父级组件 Tuts
传递给 Rating 子组件,不过在子组件如果修改了 Rating 反过来也会修改父级组件中的该对象的 Rating 的值,也就是(从子组件向父组件传递数据)

<template>
    <div class="flex flex-row justify-center align-center">
        <div class="flex flex-col">
            <div class="mr-12" v-for="tut in tuts" :key="tut.id" @click="setSelectedTut(tut)">
            <div >{{tut.name}}({{tut.rating}})</div>
         </div>
        </div>
        <Rating v-if="selectedTut" :selectedTutRating="selectedTut.rating" v-on:ratingUpdated="changeDisplayRating"/>
    </div>
</template>

<script>
const tutList = [
  { id: 0, name: "angularjs", rating: 3 },
  { id: 1, name: "react", rating: 1 },
  { id: 2, name: "vue", rating: 5 }
];
import Rating from "../components/Rating.vue";
export default {
  name: "tuts",
  data() {
    return {
      tuts: [],
      selectedTut: null
    };
  },
  mounted() {
    this.tuts = this.getTuts();
  },
  methods: {
    setSelectedTut(tut) {
      this.selectedTut = tut;
    },
    changeDisplayRating(val) {
      this.tuts[this.selectedTut.id].rating = val;
    },
    getTuts() {
      return [...tutList];
    }
  },
  components: {
    Rating
  }
};
</script>

<style>
</style>

  • 先来看父组件如何向子组件传递数据,数据中有两个数据 tuts 课程列表和 selectedTut 表示选中的课程,在 mouted 阶段获取 tuts 数据,如果没有选择任何课程,selectedTut 默认为空,通过 v-if 控制 Rating 子组件显示,没有选择也就意味不会显示 Rating。如果有选择,这回通过 :selectedTutRating 将课程的等级数据传递给子组件 Rating 进行显示。
  • 在 setSelectedTut 这个方法在选择课程后会把选择课程设置为 selectedTut。
子组件代码
<template>
    <div class="inline-block md:inline-flex">
        <form action="">
            <div><input type="radio" value="1" id="oneStarts" v-model="tutRating"><label for="oneStarts">1</label></div>
            <div><input type="radio" value="2" id="twoStarts" v-model="tutRating"><label for="twoStarts">2</label></div>
            <div><input type="radio" value="3" id="threeStarts" v-model="tutRating"><label for="threeStarts">3</label></div>
            <div><input type="radio" value="4" id="fourStarts" v-model="tutRating"><label for="fourStarts">4</label></div>
            <div><input type="radio" value="5" id="fiveStarts" v-model="tutRating"><label for="fiveStarts">5</label></div>
        </form>
    </div>
</template>

<script>
export default {
  data() {
    return {
      tutRating: null
    };
  },
  props: {
    selectedTutRating: {
      type: Number,
      required: false
    }
  },
  mounted() {
    this.tutRating = this.selectedTutRating;
  },
  methods: {
    clearRating() {
      this.tutRating = null;
    }
  },
  watch: {
    tutRating: function(newRating, oldRating) {
      let success = true;
      if (success) {
        this.$emit("ratingUpdated", newRating);
      }
    },
    selectedTutRating: function(newRating, oldRating) {
      this.tutRating = newRating;
    }
  }
};
</script>

<style>
</style>
  • 在子组件中关键是用了 watch 监控 tutRating 和 selectTutRating 进行控制,当我们在 Rating 进行选择就是通过 tutRating 方法中 $emit 发送给用户新选择的等级给父组件的
    v-on:ratingUpdated="changeDisplayRating" 父组件是通过监听ratingUpdated 来从子组件中获取等级数据来实现从子组件到父组件传递数据的。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容