1. 首页
  2. 编程面试题
  3. 前端
  4. Vue

在 Vue 中,子组件为何不可以修改父组件传递的 Prop



为了保证数据的单向流动,便于对数据进行追踪,避免数据混乱。

> 所有的 prop 都使得其父子 prop 之间形成了一个**单向下行绑定**:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会**防止从子组件意外改变父级组件的状态**,从而导致你的应用的数据流向难以理解。
设想一个场景,某个父组件下不只使用了一个子组件。而且都使用到了这份 prop 数据,那么一旦某个子组件更改了这个prop数据,会连带着其他子组件的prop数据也被更改。这会导致数据混乱,而且由于修改数据的源头不止一处,在出错后debug时难以定位错误原因。
**所以我们需要将修改数据的源头统一为父组件,子组件想要改 prop 只能委托父组件帮它。从而保证数据修改源唯一**
另外 props 传入的值如果对象的话,是可以直接在子组件里更改的,因为是同一个引用。

**如果修改了,Vue 是如何监控到属性的修改并给出警告的**

下面的代码就是实现Vue提示修改props的操作,在组件 `initProps` 方法的时候,会对props进行defineReactive操作,传入的第四个参数是自定义的set函数,该函数会在触发props的set方法时执行,当props修改了,就会运行这里传入的第四个参数,然后进行判断,如果不是root根组件,并且不是更新子组件,那么说明更新的是props,所以会警告

js
// 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不会报警告。

发布者:admin,如若转载,请注明出处:https://ai1024.vip/28242.html

QR code
//