This is the 13th day of my participation in the August More Text Challenge.More challenges in August

If we had an application that put all of its logic in one component, that component would become very bloated and difficult to maintain

So the core idea of componentization should be to take components apart, break them down into smaller components, and then assemble and nest them together to form our application

Component split

<template>
  <div>
    <! -- When importing components, you can use either the underlined method (recommended) or the hump method -->
    <child-cpn></child-cpn>
    <ChildCpn></ChildCpn>

    <! -- If the component has no content (slot is not used), it can be written as a single tag, but must have closed -->
    <child-cpn />

    <cpn />
    <Cpn />
  </div>
</template>

<script>
  // Although vue-cli helps us set the vue suffix for webpack's reolve.extensions
  // Allows us to introduce vue components without adding suffixes
  // However, it is recommended to use the vue suffix during development
  // There are two reasons for this:
  // 1. If you have a vue suffix, click on the file to change it
  // 2. The vue suffix will prompt you not to add it when using components in the template
  import ChildCpn from './components/ChildCpn.vue'

  export default {
    name: 'App'.components: {
      // ChildCpn: short for ChildCpn
      // The name used by the component: component object
      ChildCpn,

      // If the registered name is CPN, the component can only be used with CPN
      // However, if the registered name is Cpn, the component can be used through Cpn or Cpn
      Cpn: ChildCpn
   }
}
</script>
Copy the code

Styles in components

CSS scope

If the scoped attribute is applied to a component’s style, the scope of the style set on the component is local only within the current component

If scoped is not set, the style is global in scope, that is, the global style

<style>
  /* The style set here is global */
  h2 {
    color: red;
  }
</style>
Copy the code
<style scoped>
  /* The style set here is local */

  /* This is possible because vue generates a unique hash for the current component and sets it as an attribute of the component in the form of data-v-[hash value], for example: Data-v-7ba5bd90 will be changed to h2[data-V-7ba5bd90], so the corresponding style will only apply to the current component and will not pollute the styles of other components */
  h2 {
    color: red;
  }
</style>
Copy the code

Style hash value

The parent component

<template>
  <div>
    <h2>Parent Component</h2>
    <child-cpn />
  </div>
</template>

<script>
  import ChildCpn from './components/ChildCpn.vue'

  export default {
    name: 'App'.components: {
      ChildCpn
    }
  }
</script>

<style scoped>
  h2 {
    color: red;
  }
</style>
Copy the code

Child components

<template>
  <div>
    <h2>Child Component</h2>
    <h2>Child Component</h2>
  </div>
</template>

<script>
export default {
  name: 'ChildCpn'
}
</script>

<style scoped>/style>
Copy the code

The actual compiled HTML format is:

As you can see, the component hash attribute is loaded on the root element of the component and on every immediate child element

This means that the root element of the child component will also be appended to the hash value of the parent component

In VUe2, it is required that each of our components must have one and only one root component

In Vue3, however, we don’t need a root component. Vue3 will automatically fragment the outermost layer of our component at compile time

So subcomponents can sometimes be written as

<template>
   <h2>Child Component</h2>
   <h2>Child Component</h2>
</template>

<script>
  export default {
    name: 'ChildCpn'
  }
</script>

<style scoped></style>
Copy the code

The compiled HTML is as follows:

It can be argued that the child component is compiled at the time.

If there are multiple elements, a root element tag fragment is automatically wrapped around the outermost element that will not be rendered as an actual element

So the parent component’s style hash value is loaded onto the Fragment tag, but the fragment is eventually removed during rendering

So if the child component has more than one element, the parent component’s style hash value is not added to the top element of the child component

But if you style the child, because the style hash value will be set on the root element and the direct child element,

So even if the fragment is removed, there will still be the hash value of the style on the top element

But this is only if the child component has multiple top-level elements, if the child element only has one root element

The Vue compiler doesn’t add a fragment tag for us because it’s not necessary,

There is a problem when using styles set by the style picker

Because the corresponding HTML will compile to:

As you can see, the element of the child component is also typed with the parent component’s style hash value,

So the styles set by the parent component (mainly those using the label selector, such as h2{color: blue; }) contaminates elements in child components

It makes no sense to set the scoped property on the parent component’s style tag

It is not recommended to use label selectors too much in vUE to set styles

Component communication

During development, we often encounter the need for components to communicate with each other (data passing)

  • For example, the App may use multiple headers, and each Header displays different content, so we need the user to pass some data to the Header for display

  • For example, if we request Banner data and ProductList data once in Main, we need to pass them to display

  • It could also be that an event has occurred in a child component that needs to be done by the parent, which requires the child to pass the event to the parent

Parent-child communication

How do parent and child components communicate with each other?

  • Parent component to child component: via the props property
  • Child component passes to parent: events are emitted via $emit

Parent component passes to child component

Parent component to child component can communicate between components through props

props

What are Props?

  • Props is short for properties
  • Props are some custom attributes that you can register on the component
  • The parent component assigns values to these attributes, and the child component obtains the corresponding values from the attribute name

There are two common uses for Props: string arrays and object types

“Array of Strings”

The parent component

<template>
  <div>
    <! Pass the corresponding props to the child component -->
    <child-cpn title="Title" content="Specific contents" />
  </div>
</template>

<script>
  import ChildCpn from './components/ChildCpn.vue'

  export default {
    name: 'App'.components: {
      ChildCpn
    }
  }
</script>
Copy the code

Child components

<template>
  <h2>{{ title }}</h2>
  <p>{{ content }}</p>
</template>

<script>
export default {
  name: 'ChildCpn'.// The value received from the parent component is automatically added to the vUE responsive system, and can be retrieved directly from this
  props: ['title'.'content']}</script>
Copy the code

Properties of deconstruction

<template>
  <div>
    <! If the props passed are all properties of an object, you can use the following method to decompose the properties:
    <child-cpn v-bind="info" />
    <! -- The corresponding results of the above method and the following method are consistent -->
    <child-cpn :title="info.title" :content="info.content" />
  </div>
</template>

<script>
  import ChildCpn from './components/ChildCpn.vue'

  export default {
    name: 'App'.components: {
      ChildCpn
    },

    data() {
      return {
        info: {
          title: 'title'.content: 'Specific content'}}}}</script>
Copy the code

Object Type

Array usage only indicates the name of the attribute passed in, and does not restrict it in any way

If you need more constraints, you need to use object syntax for more constraints

props: {
  // Note that null and undefined pass any form of type detection other than required: true
  // By default, null and undefined are considered subtypes of any type
  title: String.content: String
}
Copy the code
props: {
  title: {
    / / the type of optional value String | Number | Boolean | Array | Object | Date | Function | Symbol
    type: String.// Set the corresponding type
    required: true // The value must be undefined
  },
    
  content: {
    type: String.// Default and rquire are mutually exclusive operations, so just set one
    // If the parent component does not pass any value to the child component, the value of the property corresponding to the child component is undefined
    // When the parent component is displayed (undefined) or implicit (no specific value is set), the corresponding default value is used in the child component
    default: 'Default content' // The default value to display when no value is passed}}Copy the code
title: {
  type: [String.Number].// Multiple possible types
  require: true
}
Copy the code
props: {
  foo: {
    type: Array.default() {
      // If the value of props is a reference type, then when setting the default value, you need to set it as a function
      // The corresponding default value should be set to the return value for the function
      // The purpose is to avoid data contamination when multiple component instances use props of the same reference type
      return[]}}}Copy the code
props: {
  title: {
    type: String.// Type Verification has a higher priority than user-defined verification
    required: true.// Validator can be used with other attributes
    // default: 'chart',


    // Use the validator to customize the validation rules -- values passed by the parent component are passed as validator arguments
    // The return value should be Boolean, if true, the validation will pass, otherwise the validation will fail
    validator(v) {
      return ['article'.'chart'.'push'].includes(v)
    }
  }
}
Copy the code
props: {
  foo: {
    type: Function.// When a function is set to a default value, it does not need to pass a function to return a new function
    // The corresponding address space is automatically released after the function is called
    default() {
      return 'default foo value'}}}Copy the code

“Prop Case naming rules for Case (camelCase vs Kebab-case)”

  • Attribute names in HTML are case insensitive, so the browser interprets all uppercase characters as lowercase characters

  • This means that when you use templates in the DOM,

    CamelCase (camel nomenclature) prop names need to be named using their equivalent kebab-case (dash delimited naming)

  • If the current template is a string template, it will be converted by VUE before being handed to the browser for parsing

    So the props can use either camelCase (camel name) or kebab-case (dash delimit name)

  • Therefore, when passing props in vue, it is recommended to use kebab-case to set the name of the props

The Attribute of the Prop

When we pass a component an Attribute that is not declared using props or emits, the Attribute is called a non-prop Attribute. For non-prop attributes, Vue does Attribute inheritance by default

Ps: Attributes such as style and class on the component are also stored in the $attrs object as attributes for non-props

In the following example, the id attribute on some elements of the component is used to make it easier to determine which structures in the compiled HTML are from the parent component and which structures are from the child component, with no actual logical meaning

The parent component

<template>
  <div id="parent">
     <! -- Parent component, which has a non-props attribute title -->  
     <child-cpn :title="title" />
  </div>
</template>
Copy the code

Child components

  1. Single root node

When a component has a single root node, non-prop attributes are automatically inherited from the root attributes

<template>
  <div id="child">
    <span>ChildCpn</span>
  </div>
</template>
Copy the code

  1. Multiple root nodes

    If there is no displayed binding for multiple root attribute nodes, Vue cannot automatically bind the attribute and will send a warning without any inheritance. We must manually specify which attribute to bind to

    <template>
      <div id="child">ChildCpn</div>
      <div id="child" :title="$attrs.title">ChildCpn</div>
      <div id="child">ChildCpn</div>
    </template>
    Copy the code

  2. Components do not want to inherit attributes

    Set inheritAttrs: False in the child component

    <script>
      export default {
        name: 'ChildCpn'.inheritAttrs: false
      }
    </script>
    Copy the code
$attrs

$attrs is a proxy object that holds all attributes for non-props

The default value of $attrs is an empty proxy object

We can access all non-props attributes with $attrs

// vue2
console.log(this.$attrs)
Copy the code
// vue3
import { useAttrs } from 'vue'
console.log(useAttrs())
Copy the code
Child component passes to parent component

We need the child to communicate to the parent when something in the child component wants to be passed to the parent or when the child component changes so that the parent component needs to change accordingly

The parent component

<template>
  <div id="parent">
    <! The name of the event that is being listened on and the name of the reactive function that is actually being fired need not be the same.
    <child-cpn @sendMsg="handleSendMessage" />
    <p>{{ msg }}</p>
  </div>
</template>

<script>
  import ChildCpn from './components/ChildCpn.vue'

  export default {
    name: 'App'.components: {
      ChildCpn
    },

    data() {
     return {
       msg: ' '}},methods: {
    handleSendMessage(v) {
      this.msg = v
    }
  }
}
</script>
Copy the code

Child components

<template>
  <button @click="sendMessage">click me</button>
</template>

<script>
export default {
  name: 'ChildCpn'.Vue3 adds an option to declare events that need to be triggered
  // It can be an array of strings or an object
  // If set to object, events can be validated
  emits: ['sendMsg'].// Even if the corresponding event is not registered in emits, the code will not report an error
  // Unlike vue2, in vue3 events are also stored in $attrs as properties of non-props
  // Events are removed from the $attrs attribute only after emits is declared
  $attrs = {onSendMsg: function body}
  $attrs becomes {} after it is declared in emits.
  // Note that when $attrs is stored, the name of the event is onSendMsg, not the incoming sendMsg, which is automatically prefixed with on

  props: ['title'].methods: {
    sendMessage() {
      // It is not necessary that the name of the event being listened on and triggered be the same
      this.$emit('sendMsg'.'Data to be passed')}}}</script>
Copy the code

In the example above, the value of emits is in array format, and the value of emits can also be in object format

emits: {
  // If the attribute value is empty, no verification is performed
  sendMsg: null
}
Copy the code
emits: {
  // Parameter list is the list of parameters to be passed when the event is triggered
  // When the event is triggered, several parameters are passed, which can be received here
  // This function is a custom validator that returns a Boolean value
  // If true, the verification succeeds
  // If false, the verification fails - the code will still execute normally, but the corresponding warning message will be printed in the console
  sendMsg(v) {
    return v > 10}}Copy the code