Initialize the Vue3 project

Using Vite:

npm init vite-app vue3-demo
Copy the code

The setup function

1. Setup

SRC/App. Vue file:

<template>
  <div>{{ readersNumber }} {{ book.title }}</div>
</template>

<script>
  import { ref, reactive } from 'vue'

  export default {
    setup() {
      const readersNumber = ref(0)
      const book = reactive({ title: 'Vue 3 Guide' })

      // expose to template
      return {
        readersNumber,
        book
      }
    }
  }
</script>
Copy the code

Setup exists so that you can use the new composition API. And these composite apis can only be used within the setup function,

The setup call is timed to create a component instance, initialize props, and then invoke the setup function. The new Setup component option is executed before the component is created, once the props is resolved, and acts as an entry point to the composition API.

From the perspective of the lifecycle hook, it is called before the beforeCreate hook. Since the component instance has not been created when setup is executed, this is not available in the setup option, which means that the context for this is not available in setup. Setup will not be able to access any properties declared in the component — local state, computed properties, or methods — except props.

Comparison of Vue2.0 and Vue3.0 life cycles:

Vue 2.0 Vue 3.0
beforeCreate setup()
created setup()
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted
activated onActivated
deactivated onDeactivated
errorCaptured onErrorCaptured

In addition to beforeCreate and Created, there are nine old lifecycle hooks that we can access in our setup method.

Setup () is executed before beforeCreate and Created before starting component creation. You create data and method.

OnBeforeMount () : Function executed before the component is mounted to the node.

3. OnMounted () : a function executed after components are mounted.

OnBeforeUpdate () : Function executed before component updates.

5, onUpdated() : function executed after the component is updated.

OnBeforeUnmount () : Function executed before component unmounts.

7. OnUnmounted () : a function executed after the component is unmounted.

8. OnActivated () : called when activated by a keep-alive cached component.

9. OnDeactivated () : called when A component cached by keep-alive is disabled, such as switching from component A to component B, and when component A disappears.

OnErrorCaptured () : Activates the hook function when an exception from a descendant component is captured.

SRC/App. Vue file:

<template>
  <div>{{ readersNumber }} {{ book.title }}</div>
</template>

<script>
  import { ref, reactive } from 'vue'

  export default {
    setup() {
      const readersNumber = ref(0)
      const book = reactive({ title: 'Vue 3 Guide' })

      // expose to template
      return {
        readersNumber,
        book
      }
    }
  }
</script>
Copy the code

Variables wrapped by ref and reactive are given reactive capabilities, which means that when you change their values in the setup function, the template displays the changed values as shown below:

<template>
  <div>{{ readersNumber }} {{ book.title }}</div>
</template>

<script>
import { ref, reactive } from 'vue'

export default {
  setup() {
    const readersNumber = ref(0)
    const book = reactive({ title: 'Vue 3 Guide' })

    setTimeout(() = > {
      // If a variable is wrapped by ref, you need to change the variable to.value. You do not need to use.value in the template.
      readersNumber.value = 1
      book.title = 'Vue3 Book'
    }, 2000)

    // Expose to template
    return {
      readersNumber,
      book,
    }
  },
}
</script>
Copy the code

Reactive defines complex data types, and Ref recommends defining basic data types, so to use Reactive to define basic data types, we need to wrap the data in Reactive

The render function (h) /JSX method in Setup

The render function h is an alias for createElement

Note: this must be written as h, not any other name

<template>
  <div>{{ readersNumber }} {{ book.title }}</div>
</template>

<script>
import { ref, reactive, h } from 'vue'

export default {
  setup() {
    const readersNumber = ref(0)
    const book = reactive({ title: 'Vue 3 Guide' })

    setTimeout(() = > {
      // If a variable is wrapped by ref, you need to change the variable to.value. You do not need to use.value in the template.
      readersNumber.value = 1
      book.title = 'Vue3 Book'
    }, 2000)

    // Expose to template

    // return {
    // readersNumber,
    // book,
    // }

    return () = > h('h1',readersNumber,book.title)
  },
}
</script>
Copy the code

3. Parameters in setup

The setup function takes two arguments, the props object and the context context

3.1 Props object

SRC/App. Vue file:

<template>
  <HelloWorld :count1="count" />
</template>

<script>
import { ref, reactive } from 'vue'
import HelloWorld from './components/HelloWorld.vue'

export default {
  components: {
    HelloWorld,
  },
  setup() {
    const count = ref(0)
    // Expose to template
    return { count }
  },
}
</script>

</script>
Copy the code

The components/HelloWorld. Vue file:

<template>
  <h2>{{ count1 }}</h2>
</template>

<script>
export default {
  name: 'HelloWorld'.props: {
    count1: Number
  },
  // data() {
  // return {
  // count: 0
  / /}
  // }
	setup(props) {
		console.log('props',props);
		console.log(props.count1); }}</script>

Copy the code

This is the core API for Vue 3.0 to implement responsiveness. That is, the count1 variable passed from the parent component to the child component is already responsive data.

The props object is reactive — that is, it is updated when a new props is passed in, and can be observed and responded to using watchEffect or Watch:

<template>
  <h2>{{ count1 }}</h2>
</template>

<script>
import { watchEffect } from 'vue'
export default {
  name: 'HelloWorld'.props: {
    count1: Number,},// data() {
  // return {
  // count: 0
  / /}
  // }
  setup(props) {
    // console.log('props',props);
    // console.log(props.count1);
    watchEffect(() = > {
      console.log(`props.count1:`, props.count1)
    })
  },
}
</script>


Copy the code

Do not deconstruct the props object because it will become unresponsive:

  • Such as the setup (… Props), which makes the props unresponsive.

  • export default {
      name: 'HelloWorld'.props: {
        count1: Number,},setup({ count1 }) {
        watchEffect(() = > {
          console.log(`count1:`, count1) // No response}})},Copy the code

3.2 contextcontext

The second argument passed to the setup function is context. Context is a normal JavaScript object that selectively exposes some properties from the original Vue 2.0 this. Context provides us with three properties:

  • attrs
  • slots
  • emit
export default {
  setup(props, context) {
    // Attribute (non-responsive object)
    console.log(context.attrs)

    // slot (non-responsive object)
    console.log(context.slots)

    // Trigger event (method)
    console.log(context.emit)
  },
}
Copy the code

Context is a normal JavaScript object, that is, it is not reactive, which means you can safely use ES6 deconstruction of the context.

export default { setup(props, { attrs, slots, emit }) { ... }}Copy the code

attrsattribute

Attrs gives us the latest data passed in. If we use attrs, the same variable name is declared in props, and the variable is not available in props, as follows:

The components/HelloWorld. Vue file:

<template>
  <h2>{{ count1 }}</h2>
</template>

<script>
import { watchEffect } from 'vue'
export default {
  name: 'HelloWorld'.// Variables in props do not conflict with variables in attrs in context
  /* props: { count1: Number }, */
  setup(props, context) {
    // console.log(context.attrs.count1)
    return {
      count1: context.attrs.count1,
    }
  },
}
</script>

</script>

Copy the code

App. Vue file:

<template>
  <HelloWorld :count1="count" />
</template>

<script>
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'

export default {
  components: {
    HelloWorld,
  },
  setup() {
    const count = ref(100)
    return { count }
  },
}
</script>
Copy the code

In the console, we can get the value of count1 with $vm.ctx.count1 (CTX is the context). As shown below:

emitattribute

The child component can send a method to the parent component via ctx.emit, which can receive arguments from the child component via events

The components/HelloWorld. Vue file:

<template>
  <div>
    <h2>{{ count1 }}</h2>
    <button @click="addHanle">AddHanle method</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld'.props: {
    count1: Number,},setup(props, ctx) {
    const addHanle = () = > {
      ctx.emit('addClick'.50.1)}return {
      addHanle,
    }
  },
}
</script>

Copy the code

App. Vue file:

<template>
  <HelloWorld :count1="count" @addClick="add" />
</template>

<script>
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'

export default {
  components: {
    HelloWorld,
  },
  setup() {
    const count = ref(0)
    // num1, num2 is 50,1 passed from ctx.emit in the HelloWorld component
    const add = (num1, num2) = > {
      // console.log('num1',num1); / / 50
      // console.log('num2',num2); / / 1
      count.value += num1 + num2
    }
    return {
      count,
      add,
    }
  },
}
</script>

Copy the code

Code execution flow:

In the steup function, the props parameter must be the same as the props parameter, but the CTX parameter must be the same as the props parameter. The second argument is CTX).

The final effect is as follows:

Responsive apis

The responsive API is how to realize the responsive function of Vue in Vue3. This paper introduces the usage methods of six common responsive apis: Reactive, REF, computed, Readonly, watchEffect and Watch.

1. Responsive base APIs

1.1 reactive

Reactive is the method for implementing reactive data provided in Vue 3.0. In Vue 2.0, reactive data is implemented through Object defineProPerty, while in Vue 3.0, reactive data is implemented through ES2015 Proxy.

Parameter to the reactive method must be an object (JSON, Array)

<template>
  <div>
    <h1>{{book.bookName}}</h1>
    <h2>{{book.authorName}}</h2>
  </div>
</template>

<script>
import { reactive } from 'vue'

export default {
  setup() {
    const book = reactive({
      bookName: 'Vue3 Basics'.authorName: 'xiao yu bamboo',})return {
      book,
    }
  },
}
</script>

Copy the code

Display effect:

Reactive wrapped objects are already enabled by Proxy, so we can modify the value in the following form, which is directly reflected in the template template.

<template>
  <div>
    <h1>{{book.bookName}}</h1>
    <h2>{{book.authorName}}</h2>
  </div>
</template>

<script>
import { reactive } from 'vue'

export default {
  setup() {
    const book = reactive({
      bookName: 'Vue3 Basics'.authorName: 'xiao yu bamboo',})setTimeout(() = > {
      book.bookName = 'Hello Vue3'
      book.authorName = 'xiaozhu'
    }, 1000)

    return {
      book,
    }
  },
}
</script>

Copy the code

After 1 second you will see “Vue3 basics” become “Hello Vue3” and xiaozhu become xiaozhu on your browser.

Reactive transformation is “deep” — it affects all nested properties. In an ES2015 Proxy-based implementation, the returned Proxy is not equal to the original object. It is recommended to use only reactive proxies and avoid relying on raw objects.

It is recommended to use the value returned by Reactive instead of the original value.

1.2 ref

Ref, like Reactive, is a way to implement responsive data. In business development, we can use it to define some simple data (basic data types), as follows:

<template>
  <div>
    {{ count }}
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(100)
    return { count }
  },
}
</script>

Copy the code

Modify data:

<template>
  <div>
    {{ count }}
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(100)
    // Change the count value
    count.value = 10
    return { count }
  },
}
</script>

Copy the code

To modify data, use count.value = 1 syntax. But why does it need to change variables like this, when reactive returns objects that can be modified directly like state.title = Vue3?

Vue 3.0 implicitly converts refs to reactive.

ref(0) => reactive({ value: 0 })
Copy the code

So count is a value returned by reactive, and depending on how reactive modifies the value, you can understand why the value returned by ref is modified by.value.

When ref is returned as an attribute of the rendering context (that is, in the object returned by Setup ()) and used in the template, it is automatically unwrapped without the need to write an additional.value inside the template. This is done automatically because Vue 3.0 determines whether a variable in the template is of type REF when the template is being parsed. If so, add.value, and if not, delegate the response set data created for Reactive.

We can print the object console.log(count) created by ref. The browser console looks like this:

The __v_isRef variable is used to determine whether a template variable is of type REF. The type can also be determined by isRef method, as follows:

<template>
  <div>
    {{ count }}
  </div>
</template>

<script>
import { isRef, ref } from 'vue'

export default {
  setup() {
    const count = ref(100)
    // Change the count value
    count.value = 10
    console.log('isRef(count)', isRef(count)) // true
    return { count }
  },
}
</script>

Copy the code

In Vue 2.0, we can add the ref=” XXX “attribute to an element and then retrieve the corresponding element in the logical code through this.$refs.xxx.

After Vue 3.0, the setup function does not have this context, so we can retrieve it through the ref method, and we need to retrieve the element after the page is mounted.

<template>
  <div ref="xiaozhuRef">Xiao yu bamboo</div>
</template>

<script>
import { onMounted, ref } from 'vue'

export default {
  setup() {
    const xiaozhuRef = ref(null)
    console.log(xiaozhuRef.value) // null
    // onMounted is a function executed after the page is mounted. Xiaozhuref. value needs to be retrieved after the page is mounted
    onMounted(() = > {
      console.log(xiaozhuRef.value) // 
       
}) return { xiaozhuRef } }, }
</script> Copy the code

1.3 toRef

ToRef is used to create a new REF for an attribute on a source responsive object, thus maintaining a reactive connection to its source object attribute. It takes two parameters: the source responsive object and the attribute name, and returns a REF data. For example, when using props data passed by a parent component, it is useful to reference a property of the props and maintain a responsive connection.

  • The value of the data is added.value
  • toRef After therefData is not a copy of the original data, but a reference, and changing the value of the resulting data also changes the original data
<template>
  <div class="hello">
    <h1>{{ obj.name }}</h1>
    <h1>{{ newObj }}</h1>
    <button @click="changeName">Change the MSG</button>
  </div>
</template>

<script>
import { toRef } from 'vue'
export default {
  name: 'HelloWorld'.setup() {
    let obj = { name: 'xiao yu bamboo'.age: 18 }
    const newObj = toRef(obj, 'name')
    const changeName = () = > {
      newObj.value = 'xiaozhu'
      console.log('obj', obj)
      console.log('newObj', newObj)
    }

    return {
      obj,
      newObj,
      changeName,
    }
  },
}
</script>

Copy the code

As can be seen from the above code, when changeName is triggered, the original data of OBj is also changed by changing the result data of newObj. However, it is important to note that if you change the reactive data created by toRef, the UI interface will not be updated. So, toRef is essentially a reference, associated with the original data.

Ref sample code (compared to toRef) :

<template>
  <div class="hello">
    <h1>{{ obj.name }}</h1>
    <h1>{{ newObj }}</h1>
    <button @click="changeName">Change the MSG</button>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'HelloWorld'.setup() {
    let obj = { name: 'xiao yu bamboo'.age: 18 }
    const newObj = ref(obj.name)
    const changeName = () = > {
      newObj.value = 'xiaozhu'
      console.log('obj', obj)
      console.log('newObj', newObj)
    }

    return {
      obj,
      newObj,
      changeName,
    }
  },
}
</script>

Copy the code

In the above code, when the changeName function is triggered, newObj responsive data will change, while the original data OBj will not change, and the UI interface will be updated. The reason is that ref is a copy in nature, and there is no reference relationship with the original data.

The difference between ref and toRef (1). Ref is essentially copy, and modifying responsive data does not affect the original data; The essence of toRef is reference relationship. Modifying responsive data will affect the original data (2). If the ref data changes, the interface will automatically update; ToRef interface will not automatically update when data changes (3). ToRef takes two arguments, the first which object and the second which property of the object

So if you want reactive data to be associated with previous data, and you want to update reactive data without updating the UI, use toRef.

1.4 toRefs

The toRefs() function transforms a reactive object created by Reactive () into a normal object, except that each attribute node on the object is reactive data of type REF ()

Sometimes, we want to change multiple attributes of an object to reactive data, and require the reactive data to be associated with the original data, and update the reactive data without updating the interface. ToRefs can be used to batch set multiple data to reactive data. (toRef can only set one data at a time)

<template>
  <div class="hello">
    <h1>{{ obj.name }}</h1>
    <h1>{{ newObj.name.value }}</h1>
    <button @click="changeName">Change the MSG</button>
  </div>
</template>

<script>
import { toRefs } from 'vue'
export default {
  name: 'HelloWorld'.setup() {
    let obj = { name: 'xiao yu bamboo'.age: 18 }
    const newObj = toRefs(obj)
    const changeName = () = > {
      newObj.name.value = 'xiaozhu'
      console.log('obj', obj)
      console.log('newObj', newObj)
    }

    return {
      obj,
      newObj,
      changeName,
    }
  },
}
</script>

Copy the code

ToRefs can also be used in conjunction with Reactive, as shown below:

<template>
  <div class="hello">
    <! - < h1 > {{data. MSG}} < / h1 > < button @ click = "data. ChangeMsg" > change MSG < / button > -- >
    <h1>{{  msg }}</h1>
    <button @click="changeMsg">Change the MSG</button>
  </div>
</template>

<script>
// import { ref } from 'vue';
import { reactive,toRefs } from 'vue';
export default {
  name: 'HelloWorld'.setup(){
    // const msg = ref('hello vue3');
    // const changeMsg = () =>{

    // msg.value = 'hello Vue3';
    // }
    // return{
    // msg,
    // changeMsg
    // }
    const data = reactive({
      msg:'hello Vue3'.changeMsg:() = > {
        data.msg = 'hello Vue3'}});return {
      // data. toRefs(data)// Deconstruct the data}}}</script>
Copy the code

1.5 computed

Computed is an options API in Vue 2.0+, but in Vue 3.0+ it will be in the form of a hook function. Let’s start by modifying the code under app.vue as follows:

<template>
	<div>{{ show }}</div>
</template>

<script>
import { reactive, computed } from 'vue'

export default {
  setup() {
    const obj = reactive({
      name:'xiao yu bamboo'.age:18
    })

    const show = computed(() = > {
      console.log(obj); // Proxy {name: "", age: 18}
      return obj.name + obj.age
    })

    setTimeout(() = >{
      obj.age = 16
    },2000)

    return {
      show
    }
  }

}
</script>
Copy the code

The code above concatenates the name and age variables using a computed function and returns show rendered on the template.

Note here that after 2 seconds, the age variable will be reassigned, so computed with obj.age in it, it will be computed dynamically, return show, and the browser will change as follows:

If you change the functions in the computed method as follows:

<template>
	<div>{{ show }}</div>
</template>

<script>
import { reactive, computed } from 'vue'

export default {
  setup() {
    const obj = reactive({
      name:'xiao yu bamboo'.age:18
    })

    const show = computed(() = > {
      console.log(obj); // Proxy {name: "", age: 18}
      return obj.name
    })

    setTimeout(() = >{
      obj.age = 16
    },2000)

    return {
      show
    }
  }

}
</script>
Copy the code

If you change the functions in the computed method as follows:

<template>
  <div>{{ show }}</div>
</template>

<script>
import { reactive, computed } from 'vue'

export default {
  setup() {
    const obj = reactive({
      name: 'xiao yu bamboo'.age: 18,})const show = computed(() = > {
      console.log(obj) // Proxy {name: "", age: 18}
      return obj.name
    })

    setTimeout(() = > {
      obj.age = 16
    }, 2000)

    return {
      show,
    }
  },
}
</script>

Copy the code

Without obj.name, 2 seconds after the timer, console.log(obj) in computed does not print again because no detection is performed inside the function.

Values returned by computed are non-modifiable, and values returned by GET and set are modifiable, as shown below:

<template>
  <div class="main">
    <div>
      <input type="text" v-model="obj.name" />
    </div>
    <h1>Calculate attribute</h1>
    <div>
      <input type="text" v-model="show" />
    </div>
  </div>
</template>

<script>
import { computed, reactive } from "vue";
export default {
  setup() {
    const obj = reactive({
      name: "Xiao yu bamboo"});// If an object is passed in the vue3 attribute function, it represents get and set
    const show = computed({
      get() {
        return obj.name;
      },
      set(val) {
				// Monitor the change of the current property value, execute when the property value changes, update the related property data
        // val is the latest property value of obj.name
        console.log(val);
        let nameVal = val.split(""); obj.name = val; }});return{ obj, show, }; }};</script>

Copy the code

Get and set are rarely used to return values.

1.4 readonly

Readonly as the name implies, creates a read-only data, and all contents are read-only and cannot be modified. Let’s look at the following code:

<template>
  <div>{{ obj.name }}</div>
	<button @click="handle">change</button>
</template>

<script>
import { computed, readonly } from "vue";
export default {
  setup() {
    const obj = readonly({
      name: "Xiao yu bamboo".age: 18
    })
    const handle = () = > {
      console.log('I got clicked.');
      obj.name = 'xiaozhu'
      obj.age = 16
    }
     return{ obj, handle}
   }
};
</script>

Copy the code

When you click the change button, you cannot change the properties of obj.name and obj.age, and the console warns ⚠️ that setting the properties fails. Obj is a read-only object.

1.5 watchEffect

WatchEffect tracks changes to responsive data and is executed immediately upon the first rendering, as shown below:

<template>
  <div>{{ obj.age }}</div>
  <button @click="change">Change the age</button>
</template>

<script>
import { reactive, watchEffect } from 'vue'
export default {
  setup() {
    const obj = reactive({
      age: 18,
    })
    watchEffect(() = > {
      console.log(obj.age)
    })
    const change = () = > {
      obj.age = 16
    }
    return { obj, change }
  },
}
</script>


Copy the code

WatchEffect takes a callback function as an argument, which is executed when the change method is used to change the obj. Age variable, as shown below:

The watchEffect function returns a new function that can be stopped by executing this function or when the component is uninstalled, as shown below:

<template>
  <div>{{ obj.age }}</div>
  <button @click="change">Change the age</button>
</template>

<script>
import { reactive, watchEffect } from 'vue'
export default {
  setup() {
    const obj = reactive({
      age: 18,})const stop = watchEffect(() = > {
      console.log(obj.age)
    })
    setTimeout(() = > {
      stop()
    }, 1000)
    const change = () = > {
      obj.age = 16
    }
    return { obj, change }
  },
}
</script>

Copy the code

When the stop method is executed after 1 second, the console no longer prints data after listening is stopped. As shown below:

WatchEffect takes a callback function and a function, onInvalidate, that clears the side effects of the invalid callback. The onInvalidate function will be executed before the watchEffect variable changes.

<template>
  <div>{{ obj.age }}</div>
  <button @click="change">Change the age</button>
</template>

<script>
import { reactive, watchEffect } from 'vue'
export default {
  setup() {
    const obj = reactive({
      age: 18,})const stop = watchEffect((onInvalidate) = > {
      console.log(obj.age)
      onInvalidate(() = > {
        console.log('onInvalid executed ')})})const change = () = > {
      obj.age = 16
    }
    return { obj, change }
  },
}
</script>


Copy the code

<template>
  <div>{{ obj.time }}</div>
  <button @click="nowTime">Check the present time</button>
</template>

<script>
import { reactive, watchEffect } from 'vue'
export default {
  setup() {
    let timer = null
    const obj = reactive({
      time: Date.now(),
    })
    watchEffect((onInvalidate) = > {
      console.log(obj.time)

      timer = setTimeout(() = > {
        console.log('Simulate asynchronous request, return content after 2 seconds')},2000)

      onInvalidate(() = > {
        console.log('Clear timer')
        clearInterval(timer)
      })
    })

    const nowTime = () = > {
      obj.time = Date.now()
    }

    return { obj, nowTime }
  },
}
</script>


Copy the code

In the watchEffect callback, I use setTimeout to simulate an asynchronous request with a response time of 2 seconds. The code above can be interpreted as saying that if you do not change the time variable within 2 seconds, the page will successfully return interface data. If you change the time variable by clicking the button again within 2 seconds, onInvalidate will be triggered to clean up the previous interface request and execute the new request based on the new time variable. The effect is shown below:

1.7 watch

The functions of the Vue 3.0 Watch are exactly the same as the previous Vue 2.0 Watch. In contrast to watchEffect, watch must listen for a specific variable and does not execute the callback function by default. Instead, it waits until the variable it is listening for has changed (so it is lazy). And you can get the before and after values as follows:

<template>
  <div>{{ obj.time }}</div>
  <button @click="handleChange">Check the present time</button>
</template>

<script>
import { reactive, watch } from 'vue'
export default {
  setup() {
    let timer = null
    const obj = reactive({
      time: Date.now(),
    })
    watch(
      () = > {
        return obj.time
      },
      (beforeData, afterData) = > {
        console.log(`beforeData`, beforeData)
        console.log(`afterData`, afterData)
      }
    )
    const handleChange = () = > {
      obj.time = Date.now()
    }

    return { obj, handleChange }
  },
}
</script>
<template>
  <div>{{ obj.time }}</div>
  <button @click="handleChange">Check the present time</button>
</template>

<script>
import { reactive, watch } from 'vue'
export default {
  setup() {
    let timer = null
    const obj = reactive({
      time: Date.now(),
    })
    watch(
      () = > {
        return obj.time
      },
      (beforeData, afterData) = > {
        console.log(`beforeData`, beforeData)
        console.log(`afterData`, afterData)
      }
    )
    const handleChange = () = > {
      obj.time = Date.now()
    }

    return { obj, handleChange }
  },
}
</script>


Copy the code

4, Life cycle hook functions, Provide/Inject

Review of Vue2.0 lifecycle hook functions

Tag 1: new Vue() initializes an instance;

Tag 2: initializes the event and component life cycle by executing the beforeCreate hook function, which is executed before the component is created.

Tag 3: Initializes injection and responsiveness, where the data data has been created and the Created hook function is executed;

Tag 4: check if there is an EL option. If there is an EL option, go to tag 5. If not, manually mount it by vm.$mount(el).

new Vue({
  el: '#app',})Copy the code

If there is a template option, it goes to tag 6.

new Vue({
  el: '#app'.template: Japan's bamboo '< div > < / div >',})Copy the code

Tag 6: If tag 5 has the template option, it will compile the template template into the render function;

Tag 7: If there is no template option in tag 5, the EL’s outerHTML is retrieved and compiled as a template;

BeforeMount hook function is triggered, converts template to AST tree, AST tree to render function, and finally convert virtual DOM to hang on real DOM node.

Tag 9: represents the lifecycle of the component when it is loaded, and the hook functions that are triggered before and after the update.

Flag 10: indicates that the component is uninstalled, including listeners. You can do some things here after the component is destroyed.

Vue3.0 lifecycle hook functions

2.1 contrast between the lifecycle hooks of the OPTION API of Vue2.0 and the combined API of Vue3.0

  • beforeCreate– > usesetup()
  • created– > usesetup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

Write the Vue3.0 lifecycle hook function to app.vue as follows:

<template>
  <div>
    <h1>Lifecycle of Vue3 {{count}}</h1>
    <div v-if="isShow">
      <HelloWorld />
    </div>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import {
  onBeforeMount,
  onErrorCaptured,
  onMounted,
  onUnmounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  reactive,
  toRefs,
} from 'vue'

export default {
  components: {
    HelloWorld,
  },
  setup() {
    const data = reactive({
      count: 0.isShow: true,})setTimeout(() = > {
      data.count = 10
      data.isShow = false
    }, 2000)

    onBeforeMount(() = > {
      console.log('onBeforeMount')
    })

    onMounted(() = > {
      console.log('onMounted')
    })

    onBeforeUpdate(() = > {
      console.log('onBeforeUpdate')
    })

    onUpdated(() = > {
      console.log('onUpdate')
    })

    onBeforeUnmount(() = > {
      console.log('onBeforeUnmount')
    })

    onUnmounted(() = > {
      console.log('onUnmounted')
    })

    onErrorCaptured(() = > {
      console.log('onErrorCaptured')})// let { count, isShow} = toRefs(data)
    return {
      ...toRefs(data), // Turn data into responsive data through toRefs and deconstruct it}}},</script>
<template>
  <div>
    <h1>Lifecycle of Vue3 {{count}}</h1>
    <div v-if="isShow">
      <HelloWorld />
    </div>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'
import {
  onBeforeMount,
  onErrorCaptured,
  onMounted,
  onUnmounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  reactive,
  toRefs,
} from 'vue'

export default {
  components: {
    HelloWorld,
  },
  setup() {
    const data = reactive({
      count: 0.isShow: true,})setTimeout(() = > {
      data.count = 10
      data.isShow = false
    }, 2000)

    onBeforeMount(() = > {
      console.log('onBeforeMount')
    })

    onMounted(() = > {
      console.log('onMounted')
    })

    onBeforeUpdate(() = > {
      console.log('onBeforeUpdate')
    })

    onUpdated(() = > {
      console.log('onUpdate')
    })

    onBeforeUnmount(() = > {
      console.log('onBeforeUnmount')
    })

    onUnmounted(() = > {
      console.log('onUnmounted')
    })

    onErrorCaptured(() = > {
      console.log('onErrorCaptured')})// let { count, isShow} = toRefs(data)
    return {
      ...toRefs(data), // Turn data into responsive data through toRefs and deconstruct it}}},</script>

Copy the code

Helloworld.vue sub-component:

<template>
  <div>Page: child components</div>
</template>

<script>
import { onBeforeUnmount, onUnmounted } from 'vue'
export default {
  name: 'HelloWorld'.setup() {
    onBeforeUnmount(() = > {
      console.log('onBeforeUnmount in child component')
    })
    onUnmounted(() = > {
      console.log('onUnmounted in child component')})}},</script>


Copy the code

Here are the results:

Execute onBeforeMount first before rendering the page, and then onMounted. OnBeforUpadate is executed first when there is a variable update in the component that causes the page to change. Instead of executing onUpdate immediately, onBeforeUnmount and onUnmounted in the children are executed first because the children are rendered in the parent. The onUpdate lifecycle hook function in the parent component is not executed until the page changes are complete.

2.2. Provide/Inject some new capital into the market.

Commonly used parent-child component communication is the data that the parent component is bound to transmit to the child component, which is received by the props attribute. Once the component hierarchy becomes too many, it is very troublesome to transfer the value level by level in this way, and the code is not readable enough for later maintenance.

Vue provides provide and Inject to help us solve multi-level nested nested communication problems. Provide data to be passed to descendant components in provide, which inject data passed by the parent component.

The following scenario is displayed: A parent component is introduced and used in an ancestor component, and a child component is introduced and used in the parent component. When we need to pass data from the ancestor component to the child component, we can directly use Provide/Inject instead of passing data from the ancestor component to the parent component. The parent component passes data layer by layer to the child component. Now we just need to declare provide in the parent component and inject in the child component to get data.

Vue2.0 code:

App. Vue file

<template>
  <div>
    <h1>Dojo.provide and inject</h1>
    <Father />
  </div>
</template>

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

export default {
  components: {
    Father,
  },
  provide: {
    name: 'xiao yu bamboo'.age: 18,}}</script>


Copy the code

Father. Vue file

<template>
  <h2>Page: the parent component</h2>
  <Son />
</template>

<script>
import Son from './Son.vue'
export default {
  name: 'Father'.components: {
    Son,
  },
}
</script>


Copy the code

Son. Vue file

<template>
  <div>Page: child component, which is the value passed from the ancestor component: Name: {{name}}, age: {{age}}</div>
</template>

<script>
export default {
  name: 'Son'.inject: ['name'.'age'],}</script>


Copy the code

The browser looks like this:

Vue3.0 code:

When using provide in setup(), we first import the provide method explicitly from vue. This allows us to call provide to define each property.

The provide function allows you to define a property with two arguments:

  1. name (<String>Type)
  2. value

When using Inject in setup(), it also needs to be imported explicitly from vue. After the import, we can call it to define the component methods exposed to us.

The Inject function takes two parameters:

  1. The name of the property to inject
  2. Default value (Optional)

App. Vue file

<template>
  <div>
    <h1>Dojo.provide and inject</h1>
    <Father />
  </div>
</template>

<script>
import { provide } from 'vue'
import Father from './components/Father.vue'

export default {
  components: {
    Father,
  },
  setup() {
    provide('name'.'xiao yu bamboo') // The form provided individually
    provide('status', {
      age: 18.hobby: 'work',})// Multiple supplied forms}},</script>
<template>
  <div>
    <h1>Dojo.provide and inject</h1>
    <Father />
  </div>
</template>

<script>
import { provide } from 'vue'
import Father from './components/Father.vue'

export default {
  components: {
    Father,
  },
  setup() {
    provide('name'.'xiao yu bamboo') // The form provided individually
    provide('status', {
      age: 18.hobby: 'work',})// Multiple supplied forms}},</script>


Copy the code

Father. Vue file

<template>
  <h2>Page: the parent component</h2>
  <Son />
</template>

<script>
import Son from './Son.vue'
export default {
  name: 'Father'.components: {
    Son,
  },
}
</script>


Copy the code

Son. Vue file

<template>
  <div>Page: child component, which is the value passed from the ancestor component: Name: {{name}}, age: {{age}} Hobby: {{hobby}}</div>
</template>

<script>
import { inject, toRefs } from 'vue'
export default {
  name: 'Son'.setup() {
    const name = inject('name'.'xiaozhu')
    const status = inject('status')

    return{ name, ... toRefs(status), } }, }</script>


Copy the code

The browser looks like this:

** Reading Vue3’s official website, ** When using reactive provide/Inject values, it is recommended to limit all changes to the reactive property as much as possible to the component that defines provide. It is recommended that we modify the data in the component declared as provide, so we should modify the data in app. vue, not in son. vue.

App. Vue file

<template>
  <div>
    <h1>Dojo.provide and inject</h1>
    <Father />
  </div>
</template>

<script>
import { provide, reactive, ref } from 'vue'
import Father from './components/Father.vue'

export default {
  components: {
    Father,
  },
  setup() {
    const name = ref('xiao yu bamboo')
    const status = reactive({
      age: 18.hobby: 'work',
    })
    provide('name', name)
    provide('status', status)

    const changeStates = () = > {
      name.value = 'xiaozhu'
      status.age = 19
      console.log(status.age)
    }
    provide('changeStates', changeStates)
  },
}
</script>


Copy the code

Father. Vue file

<template>
  <h2>Page: the parent component</h2>
  <Son />
</template>

<script>
import Son from './Son.vue'
export default {
  name: 'Father'.components: {
    Son,
  },
}
</script>


Copy the code

Son. Vue file

<template>
  <div>Page: child component, which is the value passed from the ancestor component: Name: {{name}}, age: {{age}} Hobby: {{hobby}}</div>
  <button @click="changeStates">Modify the information</button>
</template>

<script>
import { inject, toRefs } from 'vue'
export default {
  name: 'Son'.setup() {
    const name = inject('name'.'xiaozhu')
    const status = inject('status')
    const changeStates = inject('changeStates')

    return{ name, ... toRefs(status), changeStates, } }, }</script>


Copy the code

Finally, to ensure that data passed by provide will not be changed by inject’s components, we recommend readonly for the provider’s property.

Description: In the son.vue component, it is possible to directly modify the injected value. Although it is possible to directly modify the injected value, this is not recommended because the data itself is declared in the app. vue file, and the result is modified in other components. The data is cluttered, there are no rules, and the page components become difficult to maintain, so it’s important to control the data and keep a single data flow.

App. Vue file

<template>
  <div>
    <h1>Dojo.provide and inject</h1>
    <Father />
  </div>
</template>

<script>
import { provide, reactive, ref, readonly } from 'vue'
import Father from './components/Father.vue'

export default {
  components: {
    Father,
  },
  setup() {
    const name = ref('xiao yu bamboo')
    const status = reactive({
      age: 18.hobby: 'work',
    })
    provide('name', readonly(name))
    provide('status', readonly(status))

    const changeStates = () = > {
      name.value = 'xiaozhu'
      status.age = 19
      console.log(status.age)
    }
    provide('changeStates', changeStates)
  },
}
</script>


Copy the code

Five, the Vue – the Router

Vue Router is the official route of vue.js. It is deeply integrated with the vue.js core, making it a breeze to build single-page applications with vue.js.

Download the latest routing plug-in from NPM install vue-router@next

Or download the current routing plug-in from NPM install vue-router@4

The preceding two installation methods can install the latest vuE-Router4. X routes

1. Router-link and router-View components

Create the views and Router directories in the SRC directory. Then create the home. vue and about. vue files in the Views directory, and create the index.js file in the Router directory.

Home. Vue file

<template>
  <h2>
    <router-link to="/about">Home Page</router-link>
  </h2>
</template>

<script>
export default {
  name: 'Home',}</script>
<style scope></style>

Copy the code

About the vue file

<template>
  <h2>About page</h2>
</template>

<script>
export default {
  name: 'About',}</script>
<style scope></style>

Copy the code

App. Vue file

<template>
  <div>
    <router-view></router-view>
  </div>
</template>
Copy the code

The router/index. Js file

import { createRouter, createWebHashHistory } from 'vue-router'

// 1. Define routing components (following are routing components imported from other files)
import Home from '.. /views/Home.vue'
import About from '.. /views/About.vue'

// 2. Define a route to map the route to the corresponding component
const routes = [
  {
    path: '/'.component: Home,
  },
  {
    path: '/about'.component: About,
  },
]
// 3. Create a route instance and mount the defined route to the instance
const router = createRouter({
  history: createWebHashHistory(),
  routes,
})
// Export the route instance
export default router
Copy the code

Mian. Js file

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

// Import the routing file
import router from '.. /src/router'
// Create App instance
const app = createApp(App)

// Make the entire App routable
app.use(router)
// Mount the instance to the #app node
app.mount('#app')
Copy the code

The effect is as follows:

Note that the browser does not refresh the page by clicking the jump. This is the ability of routing to switch components from one page to another, components that change the viewable area without refreshing the page.

The reason why routing can not refresh the page and can bring different contents in the visual area is shown in the following figure:

The following figure shows the process of implementing single-page applications without refreshing pages through front-end routing.

If we change the router-link to a normal A tag href jump, we can also switch between page components, but this will cause the browser page refresh, which is not our original intention.

2. Programmatic navigation

2.1. Form without parameters

The home. vue file is modified as follows, while other files remain unchanged.

<template>
  <div>
    <h2>Home Page</h2>
    <button @click="linkTo">to About Page</button>
  </div>
</template>

<script>
// Since we don't have this in setup, we can no longer access this.$router or this.$route directly. Instead, use the useRouter function
import { useRouter } from 'vue-router'
export default {
  name: 'Home'.setup() {
    const router = useRouter()
    console.log('router', router) // Print the router to view some internal property methods
    const linkTo = () = > {
      // String path
      // router.push('/about')

      // Object with path
      router.push({ path: '/about'})}return {
      linkTo,
    }
  },
}
</script>
<style scope></style>

Copy the code

The useRouter generates the router instance, which is filled with routing-specific methods, such as jump methods, route guards, return methods, and so on. You can view some of the internal property methods by printing the Router, as shown in the figure below.

2.2 Named route + Params (with parameters

Sometimes it is more convenient to identify a route by name, especially when linking a route or performing some jumps. You can set the name of a route in the Routes configuration when creating the Router instance.

The router/index.js file is modified as follows:

import { createRouter, createWebHashHistory } from 'vue-router'

// 1. Define routing components (following are routing components imported from other files)
import Home from '.. /views/Home.vue'
import About from '.. /views/About.vue'

// 2. Define a route to map the route to the corresponding component
const routes = [
  {
    name: 'home'.path: '/'.component: Home,
  },
  {
    name: 'about'.path: '/about'.component: About,
  },
]
// 3. Create a route instance and mount the defined route to the instance
const router = createRouter({
  history: createWebHashHistory(),
  routes,
})

export default router
Copy the code

The home. vue file is modified as follows

<template>
  <div>
    <h2>Home Page</h2>
    <button @click="linkTo">to About Page</button>
  </div>
</template>

<script>
// Since we don't have this in setup, we can no longer access this.$router or this.$route directly. Instead, use the useRouter function
import { useRouter } from 'vue-router'
export default {
  name: 'Home'.setup() {
    const router = useRouter()
    // console.log('router',router);
    const linkTo = () = > {
      // String path
      // router.push('/about')

      // Object with path
      // router.push({path:'/about'})

      // Name the route with parameters
      router.push({ name: 'about'.params: { id: 123}})}return {
      linkTo,
    }
  },
}
</script>
<style scope></style>

Copy the code

Note: Params cannot be used with path; if path is provided, params will be ignored.

// 'params' cannot be used with' path '
const my = 'my'
router.push({ path: '/about'.params: { my } }) // Can jump to the About page, can not jump to my page
Copy the code

The about. vue file receives the following params from the home.vue file:

<template>
  <h2>About page</h2>
</template>

<script>
// Since we don't have this in setup, we can no longer access this.$route directly. Instead, use the useRoute function to get the parameters
import { useRoute } from 'vue-router'
export default {
  name: 'About'.setup() {
    const route = useRoute()
    const { id } = route.params
    console.log('id', id) / / 123}},</script>
<style scope></style>

Copy the code

Note that if you pass the parameter as params, you will not get the params parameter when you manually refresh the About Page again, as shown below:

Solution 1: Set the URL parameter in the route definition when params sends parameters

The router/index.js file is modified as follows:

/ /...

// 2. Define a route to map the route to the corresponding component
const routes = [
  {
    name: 'home'.path: '/'.component: Home,
  },
  {
    name: 'about'.path: '/about/:id'.// This sets params
    component: About,
  },
]

/ /...
Copy the code

When you refresh the browser again on the About Page, the params parameter is still available and http://localhost:3000/#/about/123 is displayed in the browser address bar

Solution 2: Use the query (with query parameters) form (see below)

2.3. Form of query (with query parameters)

The home. vue file is modified as follows

<template>
  <div>
    <h2>Home Page</h2>
    <button @click="linkTo">to About Page</button>
  </div>
</template>

<script>
// Since we don't have this in setup, we can no longer access this.$router or this.$route directly. Instead, use the useRouter function
import { useRouter } from 'vue-router'
export default {
  name: 'Home'.setup() {
    const router = useRouter()
    // console.log('router',router);
    const linkTo = () = > {
      // String path
      // router.push('/about')

      // Object with path
      // router.push({path:'/about'})

      // Named route with parameters,
      // router.push({ name: 'about', params: { id: 123 } })

      // with query parameters, the result is /about? id=123
      router.push({ path: '/about'.query: { id: 123}})}return {
      linkTo,
    }
  },
}
</script>
<style scope></style>

Copy the code

The about. vue file receives the following params from the home.vue file:

<template>
  <h2>About page</h2>
</template>

<script>
// Since we don't have this in setup, we can no longer access this.$route directly. Instead, use the useRoute function to get the parameters
import { useRoute } from 'vue-router'
export default {
  name: 'About'.setup() {
    const route = useRoute()
    const { id } = route.query
    console.log('id', id) / / 123}},</script>
<style scope></style>

Copy the code

The router/index.js file is modified as follows:

/ /...

// 2. Define a route to map the route to the corresponding component
const routes = [
  {
    name: 'home'.path: '/'.component: Home,
  },
  {
    name: 'about'.path: '/about'.// Remember to remove /:id
    component: About,
  },
]

/ /...
Copy the code

When you refresh the browser again on the About Page, the query parameter is still available and http://localhost:3000/#/about? Is displayed in the browser address bar. id=123

2.4. Replace the current position

The home. vue file is modified as follows:

<template>
  <div>
    <h2>Home Page</h2>
    <button @click="linkTo">to About Page</button>
  </div>
</template>

<script>
// Since we don't have this in setup, we can no longer access this.$router or this.$route directly. Instead, use the useRouter function
import { useRouter } from 'vue-router'
export default {
  name: 'Home'.setup() {
    const router = useRouter()
    // console.log('router',router);
    const linkTo = () = > {
      // String path
      // router.push('/about')

      // Object with path
      // router.push({path:'/about'})

      // Named route with parameters,
      // router.push({ name: 'about', params: { id: 123 } })

      // with query parameters, the result is /about? id=123
      // router.push({ path: '/about', query: { id: 123 } })

      // replace, which acts like router.push, except that it does not add a new record to history while navigating
      // router.replace({ path: '/about' })

      // You can also write the following
      router.push({ path: '/about'.replace: true})}return {
      linkTo,
    }
  },
}
</script>
<style scope></style>

Copy the code

The about. vue file is modified as follows:

<template>
  <h2>About page</h2>
</template>

<script>
export default {
  name: 'About',}</script>
<style scope></style>

Copy the code

Let’s talk about vue-routerrouter.pushandrouter.replaceThe difference between:

router.push

Description: Jumps to a different URL, but this method adds a record to the history stack, and clicking back returns you to the previous page.

router.replace

This method does not add a new record to history. Click Back to return to the previous page. The last record does not exist.

2.5. Route lazy loading + nested routines

In order to avoid our future packaging Vue project a js file after the packing is too large, affect the page loading speed, we can put the base of different routing corresponding divided into different blocks of code, and then only when routing is access to load the corresponding components, this will reduce the packaging volume and page load speed, and more efficient.

The route writing method in the router/index.js file is not lazy route loading method. The components corresponding to the following routes are loaded first regardless of whether the routes are accessed, which affects the page access speed and packaging volume.

import { createRouter, createWebHashHistory } from 'vue-router'

// 1. Define routing components (following are routing components imported from other files)
import Home from '.. /views/Home.vue'
import About from '.. /views/About.vue'

// 2. Define a route to map the route to the corresponding component
const routes = [
  {
    name: 'home'.path: '/'.component: Home,
  },
  {
    name: 'about'.path: '/about'.component: About,
  },
]
// 3. Create a route instance and mount the defined route to the instance
const router = createRouter({
  history: createWebHashHistory(),
  routes,
})

export default router
Copy the code

The above modification of router/index.js is a lazy route loading method

Import {createRouter, createWebHashHistory} from 'vue-router' //- import Home from '.. /views/Home.vue'
- import About from '.. /views/About.vue'Const routes = [{name: 'home', path: '/',+ component: () => import('.. /views/Home.vue')
	},
	{
		name: 'about',
		path: '/about',
+ component: () => import('.. /views/About.vue')}] // createRouter = createRouter({history: createWebHashHistory(), routes, }) export default router;Copy the code

In some cases, we need to trigger an event in a page on another component of the current page. In this case, we can use nested routines. Nested routines are written as follows:

router/index.js

import { createRouter, createWebHashHistory } from 'vue-router'

// 1. Define routing components (following are routing components imported from other files)
// import Home from '.. /views/Home.vue'
// import About from '.. /views/About.vue'

// 2. Define a route to map the route to the corresponding component
const routes = [
  {
    name: 'home'.path: '/'.component: () = > import('.. /views/Home.vue'),}, {name: 'about'.path: '/about'.component: () = > import('.. /views/About.vue'),
    children: [{name: 'type'.path: 'type'.component: () = > import('.. /views/Type.vue'),},],},]// 3. Create a route instance and mount the defined route to the instance
const router = createRouter({
  history: createWebHashHistory(),
  routes,
})

export default router
Copy the code

Create a new type. vue file in the Views directory and write it as follows:

<template>
  <div>
    <h2>Type Page</h2>
    <p>I am a child of the About page</p>
  </div>
</template>

<script>
import { useRoute } from 'vue-router'
export default {
  name: 'Type'.setup() {
    const route = useRoute()
    const id = route.query
    console.log(id)
  },
}
</script>
<style scope></style>

Copy the code

The about. vue file is modified as follows:

<template>
  <div>
    <h2>About page</h2>
    <button @click="linkShow">Show the Type component</button>
    <router-view></router-view>
  </div>
</template>

<script>
import { useRouter } from 'vue-router'
export default {
  name: 'About'.setup() {
    const router = useRouter()
    const linkShow = () = > {
      router.push({ path: '/about/type'.query: { id: 955}})}return { linkShow }
  },
}
</script>
<style scope></style>

Copy the code

The effect is as follows:

2.6. Redirects and aliases

redirect

“Redirect” means, for example, that when the user accesses /index, the URL will be replaced with/and the route will be matched with /.

Redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect redirect

import { createRouter, createWebHashHistory } from 'vue-router'

// 1. Define routing components (following are routing components imported from other files)
// import Home from '.. /views/Home.vue'
// import About from '.. /views/About.vue'

// 2. Define a route to map the route to the corresponding component
const routes = [
  {
    path: '/index'./ / write 1
    // redirect: '/'

    / / write 2
    // redirect: {
    // name: 'home'
    // }

    / / writing 3
    redirect: (to) = > {
      return {
        path: '/',}},}, {name: 'home'.path: '/'.component: () = > import('.. /views/Home.vue'),}, {name: 'about'.path: '/about'.component: () = > import('.. /views/About.vue'),
    children: [{name: 'type'.path: 'type'.component: () = > import('.. /views/Type.vue'),},],},]// 3. Create a route instance and mount the defined route to the instance
const router = createRouter({
  history: createWebHashHistory(),
  routes,
})

export default router
Copy the code

The effect is as follows:

The alias

Alias/to /home means that when the user accesses /home, the URL will still be /home, but will be matched as if the user is accessing /

Import {createRouter, createWebHashHistory} from 'vue-router' // import Home from '.. /views/Home.vue' // import About from '.. /views/ about.vue '/ / 2, const routes = [{path: '/index', // 开 法 1 // redirect: 2 / '/' / / writing/redirect: {/ / name: 'home' / /} / / writing 3 redirect: to = > {return {path: '/'}}}, {name: 'home', path: '/',+ alias: '/home',component: () => import('.. /views/Home.vue') }, { name: 'about', path: '/about', component: () => import('.. /views/About.vue'), children: [{ name: 'type', path: 'type', component: () => import('../views/ type.vue ')}]}] // router = createRouter({history: createWebHashHistory(), routes, }) export default router;Copy the code

The effect is as follows:

3. Navigation Guard

Many times you need to listen for routing changes to implement a business logic, such as:

1, jump to the page of a website, to determine whether the user is logged in, if not, jump to the login page, if already logged in, you can jump to other pages of the site.

2, when doing background management system permission control, if the user does not have permission to access a page, then it will do some corresponding logic processing.

All of this is done through the navigation guard.

3.1 Global Guard (Route Exclusive Guard)

BeforeEach and afterEach methods receive a callback that takes three arguments **to, from, and next**.

  • To: a goal to be entered into;
  • From: Current navigation Route that is about to leave
  • next: Some things to do, execution dependsnextMethod call argument:
    • Next (): Goes to the next hook in the pipe. If all hooks are executed, the navigation state is confirmed.
    • next(false): interrupts current navigation. If the browser URL changes (either manually by the user or by the browser back button), the URL is reset tofromIndicates the IP address of the route.
    • Next (‘/’) or next({path: ‘/’}): jumps to a different address. The current navigation is interrupted and a new navigation is performed.
    • next(error): If passed innextThe argument to is oneErrorInstance, the navigation is terminated and the error is passed torouter.onError()Registered callback.

Therefore, routing changes can be monitored through the above parameters. We modify app. vue as follows:

<template>
  <div>
    <router-view></router-view>
  </div>
</template>
<script>
import { useRouter } from 'vue-router'
export default {
  name: 'App'.setup() {
    const router = useRouter()
    const HAS_LOGIN = false // The login id, where the login interface is normally called

    // router.beforeEach: global front-guard
    router.beforeEach((to, from, next) = > {
      console.log('to', to)
      console.log('from'.from)

      // to: Where to, the destination route object to be entered
      // from where does the current navigation come from
      // Next goes on to do something

      // If you jump to a page that is not the login page
      if(to.name ! = ='login') {
        // If already logged in, continue execution
        if (HAS_LOGIN) {
          next()
        } else {
          // If you are not logged in, go to the login page
          next({ name: 'login'})}// If the login page is redirected
      } else {
        // If you are already logged in, jump to the home page
        if (HAS_LOGIN) {
          next({ name: 'home'})}else {
          // If not logged in, continue execution
          next()
        }
      }
    })
    AfterEach: global afterhook
    router.afterEach((to, from) = > {
      // loading = false // Make loading unscheduled}})},</script>

Copy the code

Add the login route configuration to the router/index.js file

import { createRouter, createWebHashHistory } from 'vue-router'

// 1. Define routing components (following are routing components imported from other files)
// import Home from '.. /views/Home.vue'
// import About from '.. /views/About.vue'

// 2. Define a route to map the route to the corresponding component
const routes = [
  {
    path: '/index'./ / write 1
    // redirect: '/'

    / / write 2
    // redirect: {
    // name: 'home'
    // }

    / / writing 3
    redirect: (to) = > {
      return {
        path: '/',}},}, {name: 'home'.path: '/'.alias: '/home'.component: () = > import('.. /views/Home.vue'),}, {name: 'about'.path: '/about'.component: () = > import('.. /views/About.vue'),
    children: [{name: 'type'.path: 'type'.component: () = > import('.. /views/Type.vue'),},],}, {name: 'login'.path: '/login'.component: () = > import('.. /views/Login.vue'),},]// 3. Create a route instance and mount the defined route to the instance
const router = createRouter({
  history: createWebHashHistory(),
  routes,
})

export default router
Copy the code

Add the login. vue file to the views directory as follows:

<template>
  <div>
    <h2>Login Page</h2>
  </div>
</template>

<script>
export default {}
</script>
<style scope></style>

Copy the code

The effect is as follows:

Add: In the beforeEach and afterEach callback functions, the router.currentRoute callback function can also get the current path argument.

App.vue

<template> <div> <router-view></router-view> </div> </template> <script> import { useRouter } from 'vue-router' export default { name: 'App', setup() {const router = useRouter() const HAS_LOGIN = false // Router.beforeeach: BeforeEach ((to, from, next) => {// console.log('to', to) // console.log('from', from)+ console.log(router.currentRoute)// To: where to go, the destination route object to enter // from where to from, the current navigation route to leave // next to do something //... }) / /...... }, } </script>Copy the code

Console. log(router.currentroute) displays the following information on the browser console:

3.2. Internal guard of components

We can also define the routing navigation guard directly within the routing component. Note that the beforeRouteEnter is not currently supported in Vue Router 4.x. The setup function can be replaced with onBeforeRouteLeave and onBeforeRouteUpdate apis.

First, change the login identifier in app. vue to true to facilitate subsequent debugging

Const HAS_LOGIN = true // Login id, where login interface is normally invokedCopy the code

The about. vue file is modified as follows:

<template>
  <div>
    <h2>About page</h2>
    <div>{{ userData }}</div>
    <button @click="linkShow">Show the Type component</button>
    <router-view></router-view>
  </div>
</template>

<script>
import { useRouter, onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
export default {
  name: 'About'.setup() {
    const userData = ref(0)
    const router = useRouter()
    const linkShow = () = > {
      router.push({ path: '/about/type'.query: { id: 955}})}// Same as beforeRouteLeave, 'this' cannot be accessed
    onBeforeRouteUpdate((to, from) = > {
      userData.value = to.query.id
    })
    // Same as beforeRouteLeave, 'this' cannot be accessed
    onBeforeRouteLeave((to, from) = > {
      const answer = window.confirm('Are you sure you want to leave? No info saved yet! ')
      // Unnavigate and stay on the same page
      if(! answer)return false
    })
    return {
      linkShow,
      userData,
    }
  },
}
</script>
<style scope></style>

Copy the code

The effect is as follows:


Welcome to 💪.