【前端100問】Q40:在 Vue 中九火,子組件為何不可以修改父組件傳遞的 Prop,如果修改了册招,Vue 是如何監(jiān)控到屬性的修改并給出警告的岔激。

寫在前面

此系列來源于開源項目:前端 100 問:能搞懂 80%的請把簡歷給我
為了備戰(zhàn) 2021 春招
每天一題,督促自己
從多方面多角度總結答案是掰,豐富知識
在 Vue 中虑鼎,子組件為何不可以修改父組件傳遞的 Prop,如果修改了键痛,Vue 是如何監(jiān)控到屬性的修改并給出警告的炫彩。
簡書整合地址:前端 100 問

正文回答

  1. 子組件為何不可以修改父組件傳遞的 Prop
  • 一個父組件下不只有你一個子組件絮短。同樣江兢,使用這份 prop 數據的也不只有你一個子組件。如果每個子組件都能修改 prop 的話丁频,將會導致修改數據的源頭不止一處杉允。
  • 單向數據流,易于監(jiān)測數據的流動席里,出現了錯誤可以更加迅速的定位到錯誤發(fā)生的位置叔磷。
  1. 如果修改了,Vue 是如何監(jiān)控到屬性的修改并給出警告的奖磁。
// 在initProps的時候改基,在defineReactive時通過判斷是否在開發(fā)環(huán)境
// 如果是開發(fā)環(huán)境,會在觸發(fā)set的時候判斷是否此key是否處于updatingChildren中被修改
// 如果不是署穗,說明此修改來自子組件寥裂,觸發(fā)warning提示
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
      );
    }
  });
}

需要特別注意的是,當你從子組件修改的 prop 屬于基礎類型時會觸發(fā)提示案疲。這種情況下封恰,你是無法修改父組件的數據源的, 因為基礎類型賦值時是值拷貝褐啡。你直接將另一個非基礎類型(Object, array)賦值到此 key 時也會觸發(fā)提示(但實際上不會影響父組件的數據源)诺舔, 當你修改 object 的屬性時不會觸發(fā)提示,并且會修改父組件數據源的數據

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.
  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();
    },
  });
}
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末低飒,一起剝皮案震驚了整個濱河市许昨,隨后出現的幾起案子褥赊,更是在濱河造成了極大的恐慌糕档,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拌喉,死亡現場離奇詭異速那,居然都是意外死亡,警方通過查閱死者的電腦和手機尿背,發(fā)現死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門端仰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人田藐,你說我怎么就攤上這事荔烧。” “怎么了汽久?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵鹤竭,是天一觀的道長。 經常有香客問我景醇,道長诺擅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任啡直,我火速辦了婚禮烁涌,結果婚禮上,老公的妹妹穿的比我還像新娘酒觅。我一直安慰自己撮执,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布舷丹。 她就那樣靜靜地躺著抒钱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颜凯。 梳的紋絲不亂的頭發(fā)上谋币,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音症概,去河邊找鬼蕾额。 笑死,一個胖子當著我的面吹牛彼城,可吹牛的內容都是我干的诅蝶。 我是一名探鬼主播退个,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼调炬!你這毒婦竟也來了语盈?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤缰泡,失蹤者是張志新(化名)和其女友劉穎刀荒,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體棘钞,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡照棋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了武翎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡溶锭,死狀恐怖宝恶,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情趴捅,我是刑警寧澤垫毙,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站拱绑,受9級特大地震影響综芥,放射性物質發(fā)生泄漏。R本人自食惡果不足惜猎拨,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一膀藐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧红省,春花似錦额各、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至痕寓,卻和暖如春傲醉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呻率。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工硬毕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人礼仗。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓昭殉,卻偏偏與公主長得像苞七,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挪丢,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容