Vue advanced features you should know

Time:2020-10-18

Vue version used in this article: 2.6.10

Vue provides us with many advanced features. Learning and mastering them will help improve your code level.

1、 Watch advanced

When we first started to learn about Vue, we used the listening properties as follows:

watch:{
	a(){
	 //doSomething
	}
}

In fact, Vue provides many advanced uses for watch.

Handler function

Define a listening attribute in the form of object and handler function. Handler is the function to handle the change of listening

watch:{
	a:{
		handler:'doSomething'
	}
},
methods:{
	doSomething(){
		//When a changes, do something about it
	}
}

What’s the use of handler? Is it unnecessary? There are two main uses:

  • The processing logic is abstracted and reused in the way of method
  • There is room for writing to define the following two important properties

Property deep

I don’t know if you noticed that?

When the watch is an object type data, if a value inside the object changes, the watch action will not be triggered!

That is to say, by default, watch does not monitor changes in internal nested data. But in many cases, we need to monitor!

To solve this problem, we need to use the deep attribute:

watch:{
	obj:{
		handler:'doSomething',
		deep:true
	}
},
methods:{
	doSomething(){
		//When obj changes, do something about it
	}
}

The default value of the deep attribute is false, which is our common watch mode.

Immediate property

watchOfhandlerFunction is usually triggered only when the listening property changes.

But sometimes, we want to execute the handler function immediately after the component is created, or when the watch is declared and bound. This requires theimmediateProperty, which is false by default. If it is changed to true, handler will be executed immediately.

watch:{
	obj:{
		handler:'doSomething',
		deep:true,
		immediate:true
	}
},
methods:{
	doSomething(){
		//When obj changes, do something about it
	}
}

Executing multiple methods simultaneously

Use array to set multiple items, including string, function and object

watch: {
    //You can pass in an array of callbacks, which are called one by one
    a: [
        
      'handle1',
        
      function handle2 (val, oldVal) { /* ... */ },
        
      {
        handler: function handle3 (val, oldVal) { /* ... */ },
        /* ... */
      }
        
    ],
    
  }

2、 Different performance of $event

$eventIt is a special variable of event object. In two scenarios, it has different meanings and represents different objects.

  • Represents the event itself in the native event. It can be done through$event.targetGet the DOM object where the event is located, and then get the specific value through value.
export default {
    methods: {
        inputHandler(msg, e) {
            console.log(e.target.value)
        }
    }
}
  • When a parent-child component communicates through a custom event, it represents the parameter value passed from the child component

Consider the following example:

//Template of blog post component


  Enlarge text

When the parent component listens for this event, you can use the$eventAccess toblog-postThe value of 0.1 passed by the sub component:

At this point,$eventThe value of is 0.1, not the previous event object.

3、 Update queue asynchronously

  • Vue is theasynchronousImplemented.
  • Whenever a data change is heard, Vue opens a queue and buffers all data changes that occur in the same event loop.
  • If the same watcher is triggered multiple times, it will be pushed into the queue only once.

This kind of de duplication in buffering is very important to avoid unnecessary computation and DOM manipulation. Then, in the next event loop “tick,” Vue refreshes the queue and performs the actual (de duplicated) work. Vue attempts internally to use native for asynchronous queuesPromise.thenMutationObserverandsetImmediateIf the execution environment does not supportsetTimeout(fn, 0)Instead.

For example, when you setvm.someData = 'new value'The component does not immediately re render. When the queue is refreshed, the component is updated in the next event loop “tick.”.

Most of the time we don’t need to care about this process, but it can be tricky if you want to do something based on the updated DOM state.

Although Vue.js Developers are generally encouraged to think in a “data driven” way and avoid direct contact with DOM, but sometimes we have to. To wait for Vue to finish updating the DOM after the data changes, you can use it immediately after the data changesVue.nextTick(callback)

In this way, the callback function will be called after the DOM update is completed. For example:

{{message}}
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message  ='new message' // change data
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

Use within componentsvm.$nextTick()The instance method is particularly convenient because it does not need to be globalVueAnd call back thethisAutomatically bind to the current Vue instance:

Vue.component('example', {
  template: '{{ message }}',
  data: function () {
    return {
      Message: 'not updated'
    }
  },
  methods: {
    updateMessage: function () {
      this.message  ='updated'
      console.log (this.$ el.textContent )// = > 'not updated'
      this.$nextTick(function () {
        console.log (this.$ el.textContent )// = > 'updated'
      })
    }
  }
})

because$nextTick()Return onePromiseObject, so you can use the new es2017async/awaitGrammar does the same thing:

methods: {
  updateMessage: async function () {
    this.message  ='updated'
      //As you can see here, the message is not executed immediately
      //Understand the difference between page refresh and code execution speed
      //Usually, we can see the results immediately on the page, because a round of queue execution is very fast, and we can't feel the process of DOM refresh and the time consumed
      //However, for the code execution, it belongs to the immediate level. If the DOM is not updated, there will be problems
    console.log (this.$ el.textContent )// = > 'not updated'
      
    await this.$nextTick()
    console.log (this.$ el.textContent )// = > 'updated'
  }
}

Popular explanation

  • Vue’s DOM refresh mechanism is an asynchronous queue, which is not the immediate, immediate, and instant updates you imagine!

  • This asynchronous queue is executed round by round and refreshed

  • The problem mentioned above is that some operations that can only be performed after DOM update (such as event binding for newly added DOM elements) cannot be executed immediately and must wait for a round of queue execution to complete

  • The most easy place to encounter the above problem: the DOM operation in the created lifecycle hook function

  • Solution: usethis.nextTick (callback function)Method to use the operation on the DOM as its callback function.

4、 Functional component

Because the traditional ability to write templates is insufficient, we introduce the rendering function createElement. We wanted more flexibility, so we introduced JSX. Finally, we found that some simple templates can be more simple and compact, so we introduced functional components. Vue always tries to provide different capabilities for each scenario.

This kind of component has the following characteristics:

  • It’s relatively simple
  • No state is managed, that is, stateless, no responsive data
  • It is not listening for any state passed to it
  • Life cycle method not written
  • Essentially, it’s just a function that receives some props
  • No instance, no this context

Then this component can be defined as a functional component. Compared with ordinary components, functional components are stateless, can not be instantiated, without any life cycle and method. It is suitable for components that only depend on the changes of external data. Because of its lightweight, the rendering performance will be improved.

Creating functional components

  • To define global components
Vue.component('my-component', {
  functional: true,
  //Props are optional
  props: {
    // ...
  },
  //To make up for the missing examples
  //Provide a second parameter as the context
  render: function (createElement, context) {
    // ...
  }
})

Pay attention to thefunctional: true,

In Vue 2.3.0 or later, you can omitpropsOption, attributes on all components are automatically implicitly resolved to prop.

When functional components are used, the reference will be HtmlElement because they are stateless and instantiated.

  • For single file components, the way to create functional components is to addfunctionalattribute
...



...



...

The most important context parameter

Because there is no state and no this context, everything a functional component needs is passed through thecontextParameter, which is an object with the following fields:

  • props: provides all prop objects
  • children: an array of vnode child nodes
  • slots: a function that returns the object containing all slots
  • scopedSlots: (2.6.0 +) an object that exposes the incoming scope slot. Normal slots are also exposed as functions.
  • data: the entire data object passed to the component ascreateElementThe second parameter of is passed into the component
  • parent: a reference to the parent component
  • listeners: (2.3.0 +) an object that contains all event listeners registered by the parent component for the current component. This isdata.onAn alias for.
  • injections: (2.3.0 +) if usedinjectOption, the object contains the properties that should be injected.

Application scenarios

A typical application scenario of functional components is as a wrapper component, such as when you meet the following requirements:

  • Choose one of the multiple components to render instead;
  • In the futurechildrenpropsdataManipulate the subcomponents before passing them on.

Here’s onesmart-listComponent, which can render more specific components according to the value of the passed in prop:

var EmptyList = { /* ... */ }
var TableList = { /* ... */ }
var OrderedList = { /* ... */ }
var UnorderedList = { /* ... */ }

Vue.component('smart-list', {
  functional: true,
  props: {
    items: {
      type: Array,
      required: true
    },
    isOrdered: Boolean
  },
  render: function (createElement, context) {
    function appropriateListComponent () {
      var items = context.props.items

      if (items.length === 0)           return EmptyList
      if (typeof items[0] === 'object') return TableList
      if (context.props.isOrdered)      return OrderedList

      return UnorderedList
    }

    return createElement(
      appropriateListComponent(),
      context.data,
      context.children
    )
  }
})

5、 Monitoring the lifecycle of a subcomponent

Suppose we have a parent componentParentAnd subcomponentsChildIf you need to listen to the child component’s mounted life cycle function in the parent component and do some logical processing, the conventional writing method may be as follows:

// Parent.vue


//Child.vue
mounted(){
    this.$emit('mounted');
}

However, Vue provides us with a simpler way to use the child component when the parent component refers to the child component without any processing@hookThe code is as follows:

// Parent.vue

  

methods:{
    doSth(){
        //some codes here
    }
}

The core is@hook:mounted="doSth"How to write it!

Of course, we can not only monitor mounted, but also other life cycles, such as created and updated.

6、 Style penetration

As we know, in the style of a single file componentscopedProperty, the style of the parent component will not penetrate into the child component.

However, the root node of a child component is affected by both its parent’s scoped CSS and its child’s scoped CSS. This design is to enable the parent component to adjust the style of its child component root element from the perspective of layout.

If you want thescopedA selector in the style can act “deeper”, such as influencing sub components. You can use the depth selector:>>>Operator.

.a >>> .b { /* ... */ }

The above code will be compiled into:

.a[data-v-f3f3eg9] .b { /* ... */ }

However, some preprocessors like sass cannot parse correctly>>>。 You can use it in this case/deep/or::v-deepOperator, both are>>>To achieve the same function.

We all know that throughv-htmlThe DOM content created is not affected by scoped style, and can be used by depth selector>>>To set the style for them.

7、 Props property of route

Generally, routing parameters are used in components. Most people will do this:

export default {
    methods: {
        getParamsId() {
            return this.$route.params.id
        }
    }
}

When you use it casually and hand in hand, it doesn’t matter. After all, it solves the demand.

But we should always remember:Components are used for reuse! Components should be highly enclosed!

Using in components$routeIt will make it highly coupled with the routing system, so that the components can only be used in the project that uses the routing function, or on some specific URLs, which limits its flexibility.

Imagine if your component is reused by someone, but that person does not use the routing system, but passes the ID parameter in other ways, what should he do?

The correct way is to pass thepropsdecoupling

First, define a component calledidThe prop of

export default {
    props: ['id'],
    methods: {
        getParamsId() {
            return this.id
        }
    }
}

If the component does not have a corresponding route, the ID can also be used by passing values from the parent component to the child component.

If a route is used, the ID value can be passed through the prop attribute of the route:

const router = new VueRouter({
    routes: [{
        path: '/user/:id',
        component: User,
        props: true
    }]
})

Will be routedpropsProperty totrueAfter that, the module can pass thepropsReceivedparamsparameter

In addition, you can also use function mode to returnprops

const router = new VueRouter({
    routes: [{
        path: '/user/:id',
        component: User,
        props: (route) => ({
            id: route.query.id
        })
    }]
})

In fact, the above skills are explained in the official files of vuerouter.

8、 Asynchronous component

In large applications, we may need to split the application into smaller code blocks and load a module from the server only when needed.

For simplicity, Vue allows you to define your components as a factory function that parses your component definitions asynchronously. Vue triggers the factory function only when the component needs to be rendered, and caches the results for future re rendering. For example:

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    //Pass the component definition to the 'resolve' callback
    resolve({
      template: 'I am async!'
    })
  }, 1000)
})

As you can see, the factory function receives aresolveCallback. This callback function will be called when you get the component definition from the server.

You can also callreject(reason)To indicate a load failure. theresetTimeoutIt’s for demonstration. How to get components is up to you.

A recommended approach is to use asynchronous components in conjunction with the code splitting function of webpack

Vue.component('async-webpack-example', function (resolve) {
  //This special 'require' syntax will tell webpack
  //Automatically cut your build code into multiple packages, these packages
  //The load is requested via Ajax
  require(['./my-async-component'], resolve)
})

You can also return aPromiseSo add the webpack 2 and es2015 syntax together, and we can write it as follows:

Vue.component(
  'async-webpack-example',
  //The 'import' function returns a 'promise' object.
  () => import('./my-async-component')
)

When using local registration components, you can also provide a return directlyPromiseFunction of:

new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

If you want to realize the function of loading components asynchronously and improve the display speed of the first screen, you can use the method of defining components in the above example, that is:Arrow function + import statement!

Processing load status

2.3.0 + NEW

The factory function of an asynchronous component can also return an object in the following format to flexibly customize the asynchronous loading process:

const AsyncComponent = () => ({
  //Component to load (should be a 'promise' object)
  component: import('./MyComponent.vue'),
  //Components used when asynchronous components are loaded
  loading: LoadingComponent,
  //Components to use when loading fails
  error: ErrorComponent,
  //Shows the delay time of the component when loading. The default value is 200 (MS)
  delay: 200,
  //If a timeout is provided and the component load times out,
  //The component used when the load failed. The default is: 'infinity'`
  timeout: 3000
})

Note that if you want to use the above syntax in the routing component of Vue router, you must use Vue router version 2.4.0 +.

9、 Batch import components

Most of the time, we will write some basic components, such as input boxes or buttons. They are relatively common components, called basic components. They will be frequently used in larger components.

This can easily lead to a long list of statements to import basic components in a large component, such as:

import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'
//More imports

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}

When you have a lot of basic components, the process will be repetitive, cumbersome and boring.

require.context()

If you happen to use webpack (or use webpack’s Vue cli 3 +) internally, you can use itrequire.contextMethod to batch import these components, and then register them as global components, so that you can use them directly anywhere, and you don’t have to worry about importing any more!

Here is an example code:

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

const requireComponent = require.context(
  //The relative path of its component directory
  './components',
  //Do you want to query its subdirectories
  false,
  //A regular expression that matches the file name of the underlying component
  /Base[A-Z]\w+\.(vue|js)$/
)

requireComponent.keys().forEach(fileName => {
  //Get the configuration of the component, that is, the specific content, the specific definition, and the code of the component itself
  const componentConfig = requireComponent(fileName)

  //Get the Pascal case name of the component to normalize the component name
  const componentName = upperFirst(
    camelCase(
      //Gets the file name independent of the directory depth
      fileName
        .split('/')
        .pop()
        .replace(/\.\w+$/, '')
    )
  )

  //Global registration component
  Vue.component(
    componentName,
    //If this component option is exported through 'export default',
    //Then it will take precedence over '. Default',
    //Otherwise, go back to the root of the using module.
    componentConfig.default || componentConfig
  )
})

rememberGlobally registered behavior must be in the root Vue instance (vianew Vue)Occurs before creation

For more information, please visit: https://www.liujiangblog.com

For more video tutorials, visit: https://www.liujiangblog.com/video/

Recommended Today

Singularity iPhone version officially launched

Recently, I haven’t updated my short book, technology blog, CocoaChina, etc. I’ve been busy developing my own product singularity app. I hope to solve our technical problems in this high-quality “app ape” product, so that more people know the singularity. We dig high-quality Internet technology articles every day for you to recommend (currently, it supports […]