最近回顾前端, 先是 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 来从子组件中获取等级数据来实现从子组件到父组件传递数据的.
来源: http://www.jianshu.com/p/6d489acf3408