Understand the Prop

Basic usage

<! -- kebab-case in HTML -->
<blog-post post-title="hello!"></blog-post>
Copy the code
Vue.component('blog-post', {
  // In JavaScript it is camelCase
  props: ['postTitle'].template: '<h3>{{ postTitle }}</h3>'
})
Copy the code

Common type

An array of strings

props: ['title'.'likes'.'isPublished'.'commentIds'.'author']
Copy the code

Form of object

props: {
  title: String.likes: Number.isPublished: Boolean.commentIds: Array.author: Object.callback: Function.contactsPromise: Promise // or any other constructor
}
Copy the code

summary

In general, it can be divided into passing static values and passing dynamic values through V-bind

  • Passing a number
  • Pass a Boolean value
  • Passing in an array
  • Passing in an object
  • Pass in all attributes of an object

post: {
  id: 1.title: 'My Journey with Vue'
}
Copy the code

The following two approaches are equivalent

<blog-post v-bind="post"></blog-post>
Copy the code
<blog-post
  v-bind:id="post.id"
  v-bind:title="post.title"
></blog-post>
Copy the code

In Vue, why can’t a child component modify a Prop passed by its parent?

What happens when you try to modify it

Start by creating a file to demonstrate props passing values (parent component passing data to child components)


      
<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>
        <! Create a todo-item component instance -->
        <todo-item todo="Learning"></todo-item>
      </ol>
    </div>

    <script src="./vue.js"></script>
    <script>
      // A component is essentially a Vue instance with predefined options
      // Register a TODO component
      Vue.component("todo-item", {
        template: '
       
  • {{todo}}
  • < button@click = "changeProps"> Try to change prop
    ' from the parent component
    .props: ["todo"].methods: { changeProps() { console.log('button trigger for child component'); this.todo = "Play"; }}});var vm = new Vue({ el: "#app", data() { return { message: "hello"}; }});
    </script> </body> </html> Copy the code

    What is the result? The data can also be modified successfully, but the console will send a warning

    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"
    Copy the code

    Unidirectional data flow

    All prop forms a one-way downlink binding between their parent prop: updates to the parent prop flow down to the child, but not the other way around. This prevents accidental changes in the state of the parent component from the child, which can make the data flow of your application difficult to understand.

    Additionally, every time the parent component is updated, all prop in the child component will be refreshed to the latest value. This means that you should not change a prop inside a child component. If you do, Vue will issue a warning in the browser console.

    To put it simply, vUE processes the data from the parent component in this way to facilitate the monitoring of the flow of data. Once an error occurs, it can locate the error position more quickly.

    Under what circumstances do we change this prop

    props: ['initialCounter'].data: function () {
      return {
        counter: this.initialCounter
      }
    }
    Copy the code
    • Case one: This prop is used to pass an initial value; This child component next wants to use it as a local prop data. In this case, it is best to define a local data property and use the prop as its initial value.With the help of the data
    props: ['size'].computed: {
      normalizedSize: function () {
        return this.size.trim().toLowerCase()
      }
    }
    Copy the code
    • The second caseThis prop is passed in as a raw value and needs to be converted. In this case, it is best to use the value of the prop to define a calculated propertyBy calculating properties

    If so, how does Vue monitor and warn about property changes

    Here we can go to the source code to find the answer, after all, the real warning hints are given by vue

    SRC >core>instance>state.jsCopy the code
    
    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.
      // Cache prop keys for future updates to props can iterate with arrays instead of dynamic object enumerations
      const keys = vm.$options._propKeys = []
      constisRoot = ! vm.$parent// root instance props should be converted
      // Not root component
      if(! isRoot) { toggleObserving(false)}for (const key in propsOptions) {
        keys.push(key)
        const value = validateProp(key, propsOptions, propsData, vm)
        /* istanbul ignore else */
        // Check whether it is in a development environment
        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
            )
          }
          // If not, the change is from a child component, triggering a warning
          /** * the fourth function passed in is a custom set function, which triggers the fourth function */ when props is modified
          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 {
          // In a development environment, Set is triggered to determine whether the key is being modified in 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(! (keyin vm)) {
          proxy(vm, `_props`, key)
        }
      }
      toggleObserving(true)}Copy the code
    src>core>observer>index.js
    Copy the code
    /** * 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]
      }
    
      letchildOb = ! 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() } }) }Copy the code

    thinking

    Does the console warn if it is passed a referenced data type?

     <todo-item todo="Learning" :todolist="todolist"></todo-item>
    Copy the code
    Var vm = new Vue({el: "#app", data() {return {message: "hello", todolist: [{id: "1", todo: "dinner"}]}; }});Copy the code

    The last

    If you have your own understanding, you can also learn in the comments section, if there is any mistake, please point out, thank you for reading here ~~

    Recommended reading

    • Vue official website props section
    • github-Daily-Interview-Question