
最近回顧前端,先是 React 現(xiàn)在因工作需又要開始 vue 了。開發(fā)過程中多半時(shí)間我們需要結(jié)構(gòu),有層次有上下級,有秩序。有了秩序也就是約束,有了約束也就是有了規(guī)則。從而減少我們思考時(shí)間。因?yàn)辂湲?dāng)勞只是在 10 點(diǎn)半前提供早餐,我們就無需考慮在 10 點(diǎn)半后買到經(jīng)濟(jì)實(shí)惠的早餐。

但有時(shí)候我們還需要打破秩序,逆流而上。做 web 開發(fā)有一段時(shí)間,最近遇到復(fù)雜業(yè)務(wù)還是有點(diǎn)摸不到頭腦,再加上時(shí)間緊任務(wù)重往往寫出繁瑣難懂 code。一直在想如何能夠輕松地開發(fā)出流暢 web。
其實(shí) web 應(yīng)用中,數(shù)據(jù)流是從服務(wù)端流出在流回到服務(wù)端,知道程序啟動(dòng)了這個(gè)數(shù)據(jù)就開始流動(dòng)。但是有的人會(huì)問,那么從服務(wù)端獲取列表數(shù)據(jù)展示,數(shù)據(jù)也沒有流回到服務(wù)端,這樣可以想象為原封不動(dòng)地流回到服務(wù)端。希望我們多一點(diǎn)想象。
我們應(yīng)用是界面是一顆 UI 樹,每一個(gè)節(jié)點(diǎn)可能是原生 DOM 或是自定義 component ,component 也是 DOM 組成。樹接口是非線性的,但是也是有層次的。我們控制數(shù)據(jù)在這棵樹上流動(dòng)。數(shù)據(jù)讓我們應(yīng)用之樹變得絢麗多彩,數(shù)據(jù)是 UI 樹的血液,數(shù)據(jù)讓 UI 樹有了生命力。
我們將樹進(jìn)行劃分區(qū)域來控制數(shù)據(jù),我們控制數(shù)據(jù)流動(dòng)范圍,控制數(shù)據(jù)流動(dòng)方向。
數(shù)據(jù)多數(shù)流三種方式,框架都應(yīng)該給出這種三種流動(dòng)方式給出答案
- 從父級向子級傳遞數(shù)據(jù)
- 從子級向父級傳遞數(shù)據(jù)
- 跨越多重級別傳遞數(shù)據(jù)
從父組件向子組件傳遞數(shù)據(jù)(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>
- 父級調(diào)用子組件(User) 可以通過屬性 message 傳遞字符串 hello 給子組件,這種方式我們只能傳遞字符串。如果傳遞 data 中一個(gè)對象 user 這種方式是無法接受到 user 而是接受到 ”user“ 的字符串。這時(shí)候我們就需要用到動(dòng)態(tài)綁定。
動(dòng)態(tài)綁定
- 下面示例是一個(gè)在父級獲取用戶列表然后將列表中 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
}
}
};
從子組件向父組件傳遞數(shù)據(jù)(事件)
父組件
這個(gè)實(shí)例是這樣的,一個(gè)課程列表,列表中每個(gè)課程都有一個(gè)等級,通過 rating 屬性來表示為 1 - 5 。我們選擇了一個(gè)課程,這是現(xiàn)實(shí)該課程的等級,等級信息是從父級組件 Tuts
傳遞給 Rating 子組件,不過在子組件如果修改了 Rating 反過來也會(huì)修改父級組件中的該對象的 Rating 的值,也就是(從子組件向父組件傳遞數(shù)據(jù))
<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>
- 先來看父組件如何向子組件傳遞數(shù)據(jù),數(shù)據(jù)中有兩個(gè)數(shù)據(jù) tuts 課程列表和 selectedTut 表示選中的課程,在 mouted 階段獲取 tuts 數(shù)據(jù),如果沒有選擇任何課程,selectedTut 默認(rèn)為空,通過 v-if 控制 Rating 子組件顯示,沒有選擇也就意味不會(huì)顯示 Rating。如果有選擇,這回通過 :selectedTutRating 將課程的等級數(shù)據(jù)傳遞給子組件 Rating 進(jìn)行顯示。
- 在 setSelectedTut 這個(gè)方法在選擇課程后會(huì)把選擇課程設(shè)置為 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>
- 在子組件中關(guān)鍵是用了 watch 監(jiān)控 tutRating 和 selectTutRating 進(jìn)行控制,當(dāng)我們在 Rating 進(jìn)行選擇就是通過 tutRating 方法中 $emit 發(fā)送給用戶新選擇的等級給父組件的
v-on:ratingUpdated="changeDisplayRating" 父組件是通過監(jiān)聽ratingUpdated 來從子組件中獲取等級數(shù)據(jù)來實(shí)現(xiàn)從子組件到父組件傳遞數(shù)據(jù)的。