Two orioles singing cui Liu, a pile of bugs on the west.

Writing repetitive code at work every day, when a CV boy, busy until eight or nine, low work efficiency, feel that I have no improvement. Learn these Vue tips so you can leave work early and date a goddess. Here are some new Vue tips to help you get things done faster. You guys work overtime, and I’ll go shopping with the goddess.

HookEvent ‘, the original way to listen for the component lifecycle

1. Internal listening for lifecycle functions

Today, the product manager sent me another requirement to develop a chart. I got the requirement and took a look at it. Then I went to the Echarts website to copy the sample code

<template>
  <div class="echarts"></div>
</template>
<script>
export default {
  mounted() {
    this.chart = echarts.init(this.$el)
    // Request data, assign data, etc.
    // The listener window changes, resize the component
    window.addEventListener('resize'.this.$_handleResizeChart)
  },
  updated() {
    // Have done a lot of work
  },
  created() {
    // Have done a lot of work
  },
  beforeDestroy() {
    // When the component is destroyed, the listener event is destroyed
    window.removeEventListener('resize'.this.$_handleResizeChart)
  },
  methods: {
    $_handleResizeChart() {
      this.chart.resize()
    },
    // A bunch of other methods}}</script>
Copy the code

After writing the function, I was happy to test it. There was no problem in the test. The product manager said it was great. However, during code review, the tech guy said, this is a problem.

This is not a good way to write it. You should put the resize event and the resize event together. Now the two pieces of code are separated by hundreds of lines of code, which is not readable. Big guy: 'Hook' have you heard? Me: 'Vue3.0' just have ah, zha, we want to upgrade 'Vue'?Copy the code

Then the tech guy ignored me and threw a piece of code at me

export default {
  mounted() {
    this.chart = echarts.init(this.$el)
    // Request data, assign data, etc.

    // The listener window changes, resize the component
    window.addEventListener('resize'.this.$_handleResizeChart)
    // The hook listener destroys the hook function and cancels the listener event
    this.$once('hook:beforeDestroy'.() = > {
      window.removeEventListener('resize'.this.$_handleResizeChart)
    })
  },
  updated() {},
  created() {},
  methods: {
    $_handleResizeChart() {
      // this.chart.resize()}}}Copy the code

After reading the code, I realized that Vue can also listen to life cycle functions in this way.

$on(‘hook:updated’, () => {}) {$on(‘hook:updated’, () => {})

2. Externally listen for lifecycle functions

Today, my colleague asked me in the company group, is there any way to listen to the component’s life cycle function externally?

My colleague uses a third-party component and needs to monitor data changes of the third-party component. However, the component does not provide the change event, so my colleague has no way out. So I decided to monitor the updated hook function of the component externally. After a look, Vue supports listening externally to the component’s lifecycle hook functions.

<template>
  <! -- Listen for the component's updated hook function via @hook:updated
  <! All lifecycle hooks of a component can be listened for by @hook: hook function name.
  <custom-select @hook:updated="$_handleSelectUpdated" />
</template>
<script>
import CustomSelect from '.. /components/custom-select'
export default {
  components: {
    CustomSelect
  },
  methods: {
    $_handleSelectUpdated() {
      console.log('Custom-select component's updated hook function is fired')}}}</script>

Copy the code

Small projectsVuex? withVue.observableWrite a status management by hand

In the front-end project, there are a lot of data need to convey Shared between the various components, by this time then you need a state management tools, in general, we will use Vuex, but for small projects, like Vuex’s official website said: “if you don’t intend to develop large single-page applications, using Vuex may be onerous redundancy. That’s true — if your application is simple, you’d better not use Vuex.” This allows you to manually build a Vuex using Vue2.6’s new API vue. Observable

1. Createstore

import Vue from 'vue'

// Create a respondable object with vue.Observable
export const store = Vue.observable({
  userInfo: {},
  roleIds: []})// define mutations, modify attributes
export const mutations = {
  setUserInfo(userInfo) {
    store.userInfo = userInfo
  },
  setRoleIds(roleIds) {
    store.roleIds = roleIds
  }
}
Copy the code

2. Reference in the component

<template>
  <div>
    {{ userInfo.name }}
  </div>
</template>
<script>
import { store, mutations } from '.. /store'
export default {
  computed: {
    userInfo() {
      return store.userInfo
    }
  },
  created() {
    mutations.setUserInfo({
      name: 'zijun'}}})</script>
Copy the code

Developing global components, you may want to knowVue.extend

Vue.extend is a global Api. It is rarely used in business development. However, when we want to develop some global components such as Loading,Notify,Message, etc., we can use vue. extend.

Students might write this in code when using elemental-UI loading

/ / display loading
const loading = this.$loading()
/ / close the loading
loading.close()
Copy the code

It might not be unusual to write it this way, but if you write it this way, right

const loading = this.$loading()
const loading1 = this.$loading()
setTimeout(() = > {
  loading.close()
}, 1000 * 3)
Copy the code

You will notice that I called loading twice, but only one loading occurred, and I only turned loading off, but loading1 was also turned off. How does this work? We now use vue. extend + singleton mode to implement a loading

1. The developmentloadingcomponent

<template> <transition name="custom-loading-fade"> <! --loading mask --> <div v-show="visible" class="custom-loading-mask"> <! <div class="custom-loading-spinner"> < I class="custom-spinner-icon"></ I ><! <p class="custom-loading-text">{{text}}</p> </div> </div> </transition> </template> <script> Export default {props: {// Whether to display loading Visible: {type: Boolean, default: false}, // loading visible text: {type: String, default: '' } } } </script>Copy the code

Once the loading component is developed, if you need to use it directly, you need to use it this way

<template> <div class="component-code"> <! <custom loading :visible="visible" text=" loading "/> </div> </template> <script> export default {data() { return { visible: false } } } </script>Copy the code

But that’s not what we need

  1. The closure can be displayed by calling the method directly through JS
  2. loadingYou can mask the entire page

2. ByVue.extendTransform a component into a global component

1. The transformationloadingComponent, will component ofpropsInstead ofdata

export default {
  data() {
    return {
      text: ' '.visible: false}}}Copy the code

2. ByVue.extendTransform component

// loading/index.js
import Vue from 'vue'
import LoadingComponent from './loading.vue'

// Wrap the component as a subclass by vue.extend
const LoadingConstructor = Vue.extend(LoadingComponent)

let loading = undefined

LoadingConstructor.prototype.close = function() {
  // If Loading has a reference, remove the reference
  if (loading) {
    loading = undefined
  }
  // Hide the component first
  this.visible = false
  // Wait 300 ms for loading to close the animation and then destroy the component
  setTimeout(() = > {
    // Remove the mounted DOM element
    if (this.$el && this.$el.parentNode) {
      this.$el.parentNode.removeChild(this.$el)
    }
    // Call the component's $destroy method for component destruction
    this.$destroy()
  }, 300)}const Loading = (options = {}) = > {
  // If the component is already rendered, return it
  if (loading) {
    return loading
  }
  // The element to mount
  const parent = document.body
  // Component properties
  const opts = {
    text: ' '. options }// Initializing a component via a constructor is equivalent to new Vue()
  const instance = new LoadingConstructor({
    el: document.createElement('div'),
    data: opts
  })
  // Attach the loading element to parent
  parent.appendChild(instance.$el)
  / / display loading
  Vue.nextTick(() = > {
    instance.visible = true
  })
  // Assign the component instance to Loading
  loading = instance
  return instance
}

export default Loading
Copy the code

3. Use loading on the page

import Loading from './loading/index.js'
export default {
  created() {
    const loading = Loading({ text: 'Loading... ' })
    // Close in 3 seconds
    setTimeout(() = > {
      loading.close()
    }, 3000)}}Copy the code

Loading is now available globally, but if you want to mount it to vue. prototype like element-ui, you’ll need to do it with this.$loading

4. Mount components toVue.prototypeThe above

Vue.prototype.$loading = Loading
// Bind the Loading method before export
export default Loading

// Use within the component
this.$loading()
Copy the code

Custom instructions to solve problems from the bottom up

What is a directive? An order is when your girlfriend points at you and says, “Over there washboard, get down on your knees, that’s an order!” . Just kidding. Programmers don’t have girlfriends.

In the last section, we developed a loading component. After the development, other developers put forward two requirements when using it

  1. Can beloadingMounted to an element, can now only be used in full screen
  2. You can mount the specified element using the commandloading

If you need it, we’ll do it

1. The developmentv-loadinginstruction

import Vue from 'vue'
import LoadingComponent from './loading'
// Construct a component subclass using vue.extend
const LoadingContructor = Vue.extend(LoadingComponent)

// Define a directive named loading
Vue.directive('loading', {
  /** * is called only once, when the directive is first bound to the element. You can do some initialization here@param {*} The element * to which the el directive is bound@param {*} Binding directives pass information, including {name:' directive name ', value: 'directive binding value ',arg:' directive parameter V-bind :text corresponding text'} */
  bind(el, binding) {
    const instance = new LoadingContructor({
      el: document.createElement('div'),
      data: {}
    })
    el.appendChild(instance.$el)
    el.instance = instance
    Vue.nextTick(() = > {
      el.instance.visible = binding.value
    })
  },
  /** * is called when the VNode of the component is updated@param {*} el
   * @param {*} binding* /
  update(el, binding) {
    // Check whether loading is displayed by changing the ratio
    if(binding.oldValue ! == binding.value) { el.instance.visible = binding.value } },/** * is called only once, when the instruction is unbound from the element@param {*} el* /
  unbind(el) {
    const mask = el.instance.$el
    if (mask.parentNode) {
      mask.parentNode.removeChild(mask)
    }
    el.instance.$destroy()
    el.instance = undefined}})Copy the code

2. Use directives on elements

<template>
  <div v-loading="visible"></div>
</template>
<script>
export default {
  data() {
    return {
      visible: false}},created() {
    this.visible = true
    fetch().then(() = > {
      this.visible = false}}})</script>

Copy the code

3. Which scenarios in the project can be customized with instructions

  1. Add a loading effect to the component

  2. Push-button level permission controls V-Permission

  3. Code burial points, which define instructions according to operation types

  4. Input The input box automatically gets focus

  5. And so on…

The depth of thewatchwithwatchTrigger a callback immediately, and I can listen in on your every move

In the development of Vue project, we often use Watch to monitor the changes of data, and then do a series of operations after the changes.

1. Basic usage

For example, on a list page, we hope that the search can be triggered automatically when the user enters the search keyword in the search box. At this time, in addition to monitoring the change event of the search box, we can also monitor the change of the search keyword through watch

<template>
  <! This example uses element-ui-->
  <div>
    <div>
      <span>search</span>
      <input v-model="searchValue" />
    </div>
    <! -- list, code omitted -->
  </div>
</template>
<script>
export default {
  data() {
    return {
      searchValue: ' '}},watch: {
    // Reload the data after the value changes
    searchValue(newValue, oldValue) {
      // Judge the search
      if(newValue ! == oldValue) {this.$_loadData()
      }
    }
  },
  methods: {
    $_loadData() {
      // Reload the data}}}</script>
Copy the code

2. Trigger immediately

It is now possible to load data when the value changes, but to load data when the page is initialized, we need to call the $_loadData method again in the Created or Mounted lifecycle hook. Instead of writing this, you can configure the immediate trigger property for Watch to do just that

/ / watch
export default {
  watch: {
    // Reload the data after the value changes
    searchValue: {
    // Use handler to listen for property changes. The first call to newValue is an empty string and oldValue is undefined
      handler(newValue, oldValue) {
        if(newValue ! == oldValue) {this.$_loadData()
        }
      },
      // Configure immediate execution of properties
      immediate: true}}}Copy the code

3. Deep Listening (I can see your inner workings)

A form page that needs to be changed to the modified state after the user modifies any item in the form. If we follow the writing method of watch in the above example, then we need to monitor every attribute of the form, which is too troublesome. In this case, we need to use the deep monitor of Watch

export default {
  data() {
    return {
      formData: {
        name: ' '.sex: ' '.age: 0.deptId: ' '}}},watch: {
    // Reload the data after the value changes
    formData: {
      // Note that newValue and oldValue are always equal because of object references
      handler(newValue, oldValue) {
        // Mark the page edit status here
      },
      // By setting the deep property to true, watch listens for every change in the value of the object
      deep: true}}}Copy the code

Listen in, call it off. Check it out$watch

There is a requirement that you have a form and you need to listen for changes to the form as you edit it, save button enabled if it changes, save button disabled otherwise. At this time, for new forms, watch can be directly used to listen to formData (assuming formData), as mentioned in the above example. But for editing forms, the form needs to backfill data, and the value of formData will be modified, which will trigger watch, and it is impossible to accurately determine whether to enable the save button. Now you need to know about $watch

export default {
  data() {
    return {
      formData: {
        name: ' '.age: 0}}},created() {
    this.$_loadData()
  },
  methods: {
    // Simulate an asynchronous request for data
    $_loadData() {
      setTimeout(() = > {
        / / value ascribed
        this.formData = {
          name: 'zijun'.age: 18
        }
        // After the form data is backfilled, monitor whether the data changes
        const unwatch = this.$watch(
          'formData'.() = > {
            console.log('The data has changed.')}, {deep: true})// The simulated data has changed
        setTimeout(() = > {
          this.formData.name = 'Joe'
        }, 1000)},1000)}}}Copy the code

We can use this.$watch to listen for data changes if needed. $this.$watch returns a function called unwatch. If unwatch() is required, unwatch() is required

Functional components, functions are components, right?

What is a functional component? A functional component is a function that is a component, which feels like a word game. Those of you who have used React will be familiar with functional components. Functional components, we can think of as having no internal state, no lifecycle hook functions, and no this(components that do not need to be instantiated).

In the process of daily writing bug, often develop some pure display business components, such as some details page, list interface, etc., they have a common characteristic is just outside into the data show, don’t need to have internal state, don’t need in the life cycle hook function do processing, you can consider to use functional components.

1. Start with the code for a functional component

export default {
  // Specify components as functional by configuring the functional property
  functional: true.// External properties received by the component
  props: {
    avatar: {
      type: String}},/** * render function *@param {*} h
   * @param {*} Context function components don't have this, props, slots, etc. All hang */ on context
  render(h, context) {
    const { props } = context
    if (props.avatar) {
      return <img src={props.avatar}></img>
    }
    return <img src="default-avatar.png"></img>}}Copy the code

In the example above, we define an avatar component that displays the incoming avatar if it is passed in from the outside, and the default avatar otherwise. In the above code you can see that there is a render function, this is Vue using JSX writing, about JSX, small series will be in the subsequent article will be a detailed tutorial to use.

2. Why use functional components

  1. The main and key reason is that functional components do not require instantiation, are stateless, and have no life cycle, so render performance is better than normal components
  2. The functional component structure is simpler and the code structure is clearer

3. Differences between functional components and normal components

  1. Functional components need to specify functional in the declared component
  2. Functional components do not need to be instantiated, so nothis.thisthroughrenderThe second argument to the function
  3. Functional components have no lifecycle hook functions, cannot use computed properties, watches, and so on
  4. Functional components cannot expose events through $emit, calling events can only be exposed throughcontext.listeners.clickTo call an event passed in externally
  5. Because functional components are not instantiated, they pass externallyrefWhen a component is referenced, it is actually referencedHTMLElement
  6. Of functional componentspropsCan not display the declaration, so there is nopropsAll declared properties are automatically and implicitly resolved topropAll undeclared attributes of a normal component are resolved to$attrsInside, and automatically mount to the component root elementinheritAttrsAttribute disallow)

I don’t want to use itJSXCan functional components be used?

Prior to Vue2.5, functional components could only be used through JSX; after that, functional components could be generated through template syntax

<! -- Add functional property to template -->
<template functional>
  <img :src="props.avatar ? props.avatar : 'default-avatar.png'" />
</template>
<! -- according to section 6 of the previous section, the statement props can be omitted.

Copy the code

conclusion

Don’t blow out your inspiration and your imagination; Don’t be a slave to your model. — Vincent Van Gogh

❤️ Love triple punch

1. If you think this article is not bad, please follow it, like it, and bookmark it so that more people can see it

2. Pay attention to the public number [front-end some play], regularly push fresh dry goods good articles for you.

3. Wear a mask for personal protection at special stage.