Depth selector (>>>)

Strictly speaking, this should be a vue-loader function.” Vue – loader “:” ^ 12.2.0″

In project development, if the business is relatively complex, especially for functions such as mid-stage or B-side pages, it is inevitable to use third-party component libraries, and products sometimes want to customize some UI aspects for these components. If these components are using scoped CSS, it is difficult for the parent component to customize the styles of third-party components.

Depth selector (>>> operator) can help you.

<template>
<div>
  <h1 class="child-title"</h1> </div> </template> <script>export default {
  name: 'child'.data() {
    return{} } } </script> <! -- Add"scoped" attribute to limit CSS to this component only -->
<style scoped>
.child-title {
  font-size: 12px;
}
</style>Copy the code

The.child-title scoped CSS in the child component above set the font size to 12px and now want to customize it to 20px and red in the parent component.

<template>
<div>
  <child class="parent-custom"></child>
</div>
</template>
<script>
import Child from './child'
export default {
  name: 'parent',
  components: {
    Child
  },
  data() {
    return {}
  }
}
</script>
<style>
.parent-custom  >>> .child-title {
  font-size:20px;
  color: red;
}
</style>Copy the code

It works fine. But don’t get too excited. Note that the style above uses pure CSS syntax. If you use less syntax, you may get a WebPack error message.

<style lang="less">
.parent-custom {
  >>> .child-title {
    font-size:20px;
    color: red;
  }
}
</style>
ERROR in./~/css-loader! ./~/vue-loader/lib/style-compiler? {"vue":true."id":"data-v-960c5412"."scoped":false."hasInlineConfig":false}! ./~/postcss-loader! ./~/less-loader! ./~/vue-loader/lib/selector.js?type=styles&index=0! ./src/components/parent.vue Module build failed: Unrecognised input @ /src/components/parent.vue (line 22, column 6) near lines: .parent-custom { >>> .child-title { font-size:20px;Copy the code

The error message is less >>>. (There is a suggestion on the Github issue of less to support >>> operators, but v2.7.3 used in this article has this issue)

The solution is scaping less and Variable Interpolation

<style lang="less">
@deep: ~'> > >';
.parent-custom {
  @{deep} .child-title {
    font-size:20px;
    color: red;
  }
}
</style>Copy the code

As for the other CSS preprocessors, I don’t comment on them because I don’t use them very much.

Some preprocessors like Sass don’t parse >>> correctly. In this case you can use the /deep/ operator instead – this is a >>> alias that will work just as well.

InheritAttrs, $listeners, and $listeners

2.4.0 new

The component configuration item inheritAttrs

We all know that if we pass prop A, B, and C when using a child component, and the props option only declares PROPS A and B,c will be rendered as an HTML custom property on the root element of the child component.

If you don’t want this, you can set the inheritAttrs: False configuration item for the child component and the root element will be much cleaner.

<script>
export default {
  name: 'child',
  props: ['a'.'b'],
  inheritAttrs: false
}
</script>Copy the code

Component instance attributes $attrs and $Listeners

$attrs = vm.$attrs = vm

vm.$attrs

Type: {[key: string]: string}

read-only

Contains property bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, all parent-scoped bindings (except class and style) are included, and internal components can be passed in via V-bind = “$attrs” — useful when creating higher-level components.

It can be summed up in two points:

Vm. $attrs is a built-in property of the component, and the value is any prop passed by the parent that is not declared by the component (except class and style).

Again, the previous Child component example

// parent.vue
<template>
  <div>
    <child 
      class="parent-custom" 
      a="a" 
      b="b" 
      c="c">
    </child>
  </div>
</template>
// child.vue
<script>
export default {
  name: 'child',
  props: ['a'.'b'],
  inheritAttrs: false.mounted() {// console output: // child:$attrs: {c: "c"}
    console.log('child:$attrs:', this.$attrs)
  }
}
</script>Copy the code

Components can further pass values to their children by using v-bind=’attrs’ on their own children. That is, the child component treats the value of attrs as if it were a prop passed in, following the rules of point 1.

// parent.vue
<template>
  <div>
    <child 
      a="a" 
      b="b" 
      c="c">
    </child>
  </div>
</template>
// child.vue
<template>
  <div>
    <grand-child 
      v-bind="$attrs" 
      d="d">
    </grand-child>
  </div>
</template>
<script>
export default {
  name: 'child',
  props: ['a'.'b'],
  inheritAttrs: false
}
</script>
// grandchild.vue
<script>
export default {
  name: 'grandchild',
  props: [],
  // props: ['c'],
  inheritAttrs: false.mounted// console output: // grandchild:$attrs: {d: "d", c: "c"}
    console.log('grandchild:$attrs:', this.$attrs// If props: ['c'// grandchild:$attrs: {d: "d"}
  }
}
</script>Copy the code

vm.$listeners

Type: {[key: string] : the Function | Array}

read-only

Contains v-ON event listeners in the parent scope (without the.native modifier). It can be passed into internal components via V-on = “$Listeners” — useful when creating higher-level components.

To sum up, there are two points:

  1. The vm.$Listeners are built-in properties of the component, and their values are V-on event listeners of the parent component (without the.native modifier).

  2. Components can further pass values to their child components by using V-on =’$Listeners’ on them. If the child component is already bound to a listener of the same name in the Listener, the two listener functions are executed in a bubbling fashion.

// parent.vue
<template>
  <div>
    <child @update="onParentUpdate"></child>
  </div>
</template>
<script>
export default {
  name: 'parent',
  components: {
    Child
  },
  methods: {
    onParentUpdate() {
      console.log('parent.vue:onParentUpdate')
    }
  }
}
</script>
// child.vue
<template>
  <div>
    <grand-child 
      @update="onChildUpdate" 
      v-on="$listeners">
    </grand-child>
  </div>
</template>
<script>
export default {
  name: 'child',
  components: {
    GrandChild
  },
  methods:{
    onChildUpdate() {
      console.log('child.vue:onChildUpdate')
    }
  }
}
</script>
// grandchild.vue
<script>
export default {
  name: 'grandchild'.mounted// console output: // grandchild:$listeners: {update: ƒ} the console log ('grandchild:$listeners:',this.$listeners); / / the console output: / / child. The vue: onChildUpdate / / parent. Vue: onParentUpdate this.$listeners.update()
  }
} 
</script>Copy the code

Provide /inject component options

2.2.0 new

If you list communication methods between Vue components, it is commonly said that prop, custom events, event bus, Vuex, provide/inject provide another method.

This pair of options needs to be used together to allow an ancestor component to inject a dependency into all of its descendants, regardless of how deep the component hierarchy is, and remain in effect as long as the upstream and downstream relationship is established.

If you’re familiar with React, this is similar to the React context feature.

Note, however, that direct use in applications is not recommended in the documentation.

Provide and Inject mainly provide use cases for high-level plug-in/component libraries. Direct use in application code is not recommended.

//parent.vue
<template>
  <div>
    <child></child>
  </div>
</template>
<script>
export default {
  name: 'parent',
  provide: {
    data: 'I am parent.vue'
  },
  components: {
    Child
  }
}
</script>
// child.vue
<template>
  <div>
    <grand-child></grand-child>
  </div>
</template>
<script>
export default {
  name: 'child',
  components: {
    GrandChild
  }
}
</script>
// grandchild.vue
<script>
export default {
  name: 'grandchild',
  inject: ['data'].mounted() {// console output: // grandChild :inject: I am parent.vue console.log('grandchild:inject:',this.data);
  }
}
</script>Copy the code

The provide option should be an object or a function that returns an object. This object contains properties that can be injected into its descendants.

The Inject option should be an array of strings or an object whose key represents the name of the local binding and whose value is the key to be evaluated in provide.

In 2.5.0+, when inject option is object, you can also specify from to represent the source attribute, default specifies the default value (a factory method is used if the value is non-raw).

const Child = {
  inject: {
    foo: {
      from: 'bar',
      default: 'foo'
      //default: () => [1, 2, 3]
    }
  }
}Copy the code

Slot-scope

2.1.0 new

In 2.5.0+, slot-scope is no longer limited to template elements, but can be used with any element or component in the slot.

Scope slots are well documented. Here is an example to illustrate the application scenario.

The list page and edit page display the same data, the only difference is that the data processing logic is different on different pages. The same data presentation block can be extracted as a component, and different areas can be implemented through the scope slot.

// data-show.vue
<template>
<div>
  <ul>
    <li v-for="item in list">
      <span>{{item.title}}</span>
      <slot v-bind:item="item"> < / slot > < / li > < / ul > < / div > < / template > / / list. The vue < template > page < / p > < p > list < data - show: list ="list">
    <template slot-scope="slotProps">
      <span v-if="slotProps.item.complete">✓</span> <span v-else>x</span> </template> </data-show> </template> // edit.vue <template> <p> <data-show :list=, ✓ /span> <span v-else>x</span> </template> </data-show> </template> // edit"list">
  <template slot-scope="slotProps">
    <a v-if="slotProps.item.complete"> view < / a > < a v - else > change < / a > < / template > < / data - show > < / template >Copy the code

Vue error capture

Configure errorHandler globally

As of 2.2.0, this hook also catches errors in component lifecycle hooks.

Since 2.4.0 this hook will also catch errors within Vue custom event handlers.

See the documentation errorHandler for more details

Lifecycle hooks errorCaptured

2.5.0 + added

ErrorCaptured is available for more details

If you’re familiar with React, you’ll notice that it’s very similar to the concept of Error Boundaries, and it actually does work that way.

Error Handling with errorCaptured Hook is a good example

Vue.component('ErrorBoundary', {
  data: () => ({ error: null }),
  errorCaptured (err, vm, info) {
    this.error = `${err.stack}\n\nfound in ${info} of component`
    return false
  },
  render (h) {
    if (this.error) {
      return h('pre', { style: { color: 'red' }}, this.error)
    }
    // ignoring edge cases for the sake of demonstration
    return this.$slots.default[0]
  }
})
<error-boundary>
  <another-component/>
</error-boundary>Copy the code

It’s important to note that errorCaptured does not capture its own errors and asynchronous errors (such as web requests, mouse events, etc.).

In 2.5 we introduce the new errorCaptured hook. A component with this hook captures all errors (excluding those fired In async callbacks) from its child component tree (excluding itself).

reference

  • Github.com/vuejs/vue/r…

  • Github.com/vuejs/vue-l…

  • Github.com/less/less.j…