<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>
複製代碼
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
複製代碼
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
複製代碼
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
複製代碼
總體來講能夠分爲傳遞靜態的值
和經過v-bind 傳遞動態的值
javascript
post: {
id: 1,
title: 'My Journey with Vue'
}
複製代碼
如下兩種方式是等價的html
<blog-post v-bind="post"></blog-post>
複製代碼
<blog-post v-bind:id="post.id" v-bind:title="post.title" ></blog-post>
複製代碼
首先建立一個文件來演示props
傳值(父組件的數據傳遞給子組件)vue
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue-prop</title>
</head>
<body>
<div id="app">
{{ message }}
<hr />
<ol>
<!-- 建立一個 todo-item 組件的實例 -->
<todo-item todo="學習"></todo-item>
</ol>
</div>
<script src="./vue.js"></script>
<script> // 組件本質上是一個擁有預約義選項的一個 Vue 實例 // 註冊一個TODO組件 Vue.component("todo-item", { template: ` <div> <li>{{todo}}</li> <button @click = "changeProps">嘗試改變父組件傳來的prop</button></div>`, props: ["todo"], methods: { changeProps() { console.log(`子組件的按鈕觸發`); this.todo = "玩耍"; } } }); var vm = new Vue({ el: "#app", data() { return { message: "hello" }; } }); </script>
</body>
</html>
複製代碼
結果是什麼,數據也是能夠修改爲功的,可是控制檯會報一個警告java
vue.js:634 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "todo" 複製代碼
全部的 prop 都使得其父子 prop 之間造成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,可是反過來則不行。這樣會防止從子組件意外改變父級組件的狀態,從而致使你的應用的數據流向難以理解。react
額外的,每次父級組件發生更新時,子組件中全部的 prop 都將會刷新爲最新的值。這意味着你不該該在一個子組件內部改變 prop。若是你這樣作了,Vue 會在瀏覽器的控制檯中發出警告。git
簡單的來講,vue這樣處理從父組件來的數據,是爲了方便監測數據的流動,若是一旦出現的錯誤,能夠更爲迅速的定位到錯誤的位置,github
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
複製代碼
藉助data
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
複製代碼
藉助計算屬性
這裏咱們能夠去源碼裏找答案,畢竟真實的警告暗示是vue
來給出的數組
src>core>instance>state.js // 源碼的位置
複製代碼
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
// cache prop keys so that future props updates can iterate using Array
// instead of dynamic object key enumeration.
// 緩存prop的keys 爲了是未來更新的props能夠使用數組進行迭代,而不是動態的對象枚舉
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
// 不是root根組件
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
/* istanbul ignore else */
// 經過判斷是否在開發環境
if (process.env.NODE_ENV !== 'production') {
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
// 若是不是,說明此修改來自子組件,觸發warning提示
/** * 傳入的第4個函數是自定義的set函數,當props被修改的時候就會觸發第四個參數的函數 */
defineReactive(props, key, value, () => {
if (!isRoot && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${key}"`,
vm
)
}
})
} else {
// 若是是開發環境,會在觸發Set的時候判斷是否此key是否處於updatingChildren中被修改
defineReactive(props, key, value)
}
// static props are already proxied on the component's prototype
// during Vue.extend(). We only need to proxy props defined at
// instantiation here.
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
複製代碼
src>core>observer>index.js
複製代碼
/** * Define a reactive property on an Object. */
export function defineReactive ( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean ) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
複製代碼
若是是傳入的是引用的數據類型,控制檯會警告嘛?瀏覽器
<todo-item todo="學習" :todolist="todolist"></todo-item>
複製代碼
var vm = new Vue({
el: "#app",
data() {
return {
message: "hello",
todolist: [
{
id: "1",
todo: "吃飯"
}
]
};
}
});
複製代碼
若有本身的理解也能夠在評論區一塊學習,若有錯誤也請指出,感謝你讀到這裏~~緩存