-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
第 40 题:在 Vue 中,子组件为何不可以修改父组件传递的 Prop,如果修改了,Vue 是如何监控到属性的修改并给出警告的。 #60
Comments
if (process.env.NODE_ENV !== 'production') {
var hyphenatedKey = hyphenate(key);
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
("\"" + hyphenatedKey + "\" is a reserved attribute and cannot be used as component prop."),
vm
);
}
defineReactive$$1(props, key, value, function () {
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
);
}
});
} 在initProps的时候,在defineReactive时通过判断是否在开发环境,如果是开发环境,会在触发set的时候判断是否此key是否处于updatingChildren中被修改,如果不是,说明此修改来自子组件,触发warning提示。
|
子组件为何不可以修改父组件传递的 Prop?-> 因为每当父组件属性值修改时,该值都将被覆盖;如果要有不同的改变,可以用基于prop的data或者computed |
貌似是 极客时间 里面 那个作者提出来的问题~ 不过楼主你真的很认真哟~ |
漂亮, 还可以这么玩! |
@LzOo0oO,怎么玩?来,come on!举个例子? |
为何不能修改:为了保证数据的单向流动,便于对数据进行追踪,避免数据混乱。官网有详细的信息 prop
vue如何知道修改了父组件传来的props? 下面的代码就是实现Vue提示修改props的操作,在组件 // 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.
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
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
)
}
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 {
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()
}
})
} 如果传入的props是基本数据类型,子组件修改父组件传的props会警告,并且修改不成功,如果传入的是引用数据类型,那么修改改引用数据类型的某个属性值时,对应的props也会修改,并且vue不会抱警告。 |
然而 props传入的是对象的话 是可以直接在子组件里更改的, 因为是同一个引用 |
这道题不错 |
个人感觉 就从父组件底下有未知个子组件 如果子组件可以直接修改父组件的值 别的子组件也依赖父组件的这个值的话就乱了 ,不好追踪, 子组件通过$emit方式实际还是父组件修改好了再通过数据流传过来而已 |
原因很简单,一个父组件下不只有你一个子组件。 所以我们需要将修改数据的源头统一为父组件,子组件像要改 prop 只能委托父组件帮它。从而保证数据修改源唯一 |
说子组件不可以修改父组件传递的props是不够严谨的,我们在用this[key]来修改props的时候,完全可以改变该组件的vm._props,只是父组件传递的数据源是不会改变的,保证了数据源的唯一性。 function initProps (vm: Component, propsOptions: Object) {
//获取父组件传入的props对象。
const propsData = vm.$options.propsData || {}
/*这里没有用 defineReactive 函数直接处理 propsDatas, 而是用一个新变量来接受props来接受
defineReactive的处理 */
const props = vm._props = {}
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
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
)
}
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 {
defineReactive(props, key, value)
}
if (!(key in vm)) {
/*把_props的属性经过属性代理 ,方便我们可以用 this[key] 直接访问 vm下边的_props 里边的属性*/
proxy(vm, `_props`, key)
} 综上,也就是说我们在用 this[key] = newVal来改变组件中的props的时候,实际改变的是vm下边的_props, 而由父组件传来的propsOptions并没有发生改变,这里应该注意的是当this._props[key]是基础数据类型的时候,this._props[key] 就是 propsData[key] 的拷贝值,当this._props[key]为引用类型的时候,this._props[key] 和 propsData[key] 指向的是同一个栈内存,**也就是说我们修改this._props[key]下的属性时候, propsData[key]也发生了改变,也就是父元素的数据发生了改变 |
props也是深度数据响应吧,用的同一个defineReactive方法来实现 |
vue数据传递是单项数据流,如果父组件传递过来的props为引用类型,直接修改了props的值会导致其他用到此props的子组件的值也发生改变,导致数据混乱,以及难以追踪的bug。如果props是基础类型,不会造成其他影响,但也会是整个组件数据混乱 |
那如果父级传入到子组件的不是一个引用而是一个copy的对象,是不是子组件就可以改呢? 这样是不是就不会影响到其他的子组件了呢? |
为什么修改引用类型的数据不会触发提示呢,源码里没看到对基本属性和引用类型的判断 |
为什么修改引用类型的数据不会触发提示呢,源码里似乎没看到对基本属性和引用类型的判断 |
No description provided.
The text was updated successfully, but these errors were encountered: