Basics: Take you to open the door of Vue (middle)

Basics: Take you to open the door of Vue (middle)

Part VI: Vue component basics

Components are reusable Vue instances.
Component-based development refers to the extraction of a complete page into individual components, and finally, the functions of the entire page (project) are completed through these individual components.
Advantages of component-based development/Function: Reuse

1. The basic use of components (register first, then use)

1.1 Global component registration and use

Register global components
Vue.component( 'child' , { template: `<h1 class="red">This is the child component</h1>` }) //Or <template id= "one" > < div > < h1 > hello, world </h1 > </div > </template> Vue.component( 'child' , { template: '#one' }) /** * The first parameter: Component name (the component name is connected by dashes (my-component) or camel case (myComponent), and converted to dashes when used) * The second parameter: is a configuration object, which is almost identical to the configuration object of the Vue instance * In other words, the configuration items used in the vue instance are almost the same as the configuration items in the component (el item is replaced by template item) */ Copy code

In fact, this step is simplified a bit:

//1. Create a component constructor object const cnpc = Vue.extend({ template : ` <div> <h1>I am the title</h1> <P>I am the content</p> </div>` }); /* 2. Register the component: pass in the component tag name and the component constructor object This registration method is a global component registered globally, which can be used under multiple Vue instances */ Vue.component( "myComponentOne" , cnpc); Copy code
Use global components
< Div ID = "App" > <-! 3. Using Components -> <My-One-Component "> </My-One-Component > </div > copy the code

1.2. Partial registration and use

Each component instance has components property, and we can register partial components in it. The registered component is only valid under the scope of the instance.

const vm = new Vue({ el : "#app" , //Syntax sugar to register partial components components : { "my-component-two" : { template : ` <div> <h1>I am the title</h1> <P>I am the content</p> </div>` , }, }, }); Copy code

Of course, you can extract the component content in components and define it in the script tag. If the component

template
If the structure inside is too complicated, you can separate it: define it outside the script.

<div id= "app" > < my-button > </my-button > </div> < template id = "button" > < div > {{msg}} </div > </template > < script > let myButton = { data ( ) { return { msg : "aaa" }; }, template : "#button" , }; vm = new Vue({ el : "#app" , data : {}, components : { myButton, }, }); </script > copy code

Use local components

<div id= "app" > <!-- 3. Use components--> < my-component-two > </my-component-two > </div> Copy code

1.3 Why is the data of a component a function?

  • Components are reusable Vue instances. After a component is created, it may be used in various places. No matter how many times the component is reused, the data data in the component should be isolated from each other and not affect each other.
  • If the data of the component is a reference type object, when the component is reused, it all points to the same heap memory, and all the reused components operate on the same data.
  • If the data of the component is a function and the return value is an object, the data function will be called every time the component is reused to form an independent data storage space.

2. Component communication

2.1 The parent component passes
props
Pass information (data) to subcomponents

  • Child components use props to receive the data passed by the parent component. The properties defined in props act on the component itself and can be used directly in template, computed, and methods.
  • The specific steps are: bind the data in the parent component through v-bind in the child component (son) tag in the parent component (father); then receive the data through props in the instance option of the child component (son).
< div id = "app" > <!-- The first step: bind data through v-bind in the parent component Camel case name cannot be used in component use --> < son :father-name = "name" > </son > </div > < template id = "son" > < div > < h1 > I am a child component </h1 > < h2 > {{fatherName}} </h2 > </div > </template > < script > const son = { template : "#son" , //Step 2: The child component receives data through props props : [ "fatherName" ], }; //Follow the component as the parent component const vm = new Vue({ el : "#app" , components : { son, }, data : { name : "I am the follower component and the parent component" , }, }); </script > copy code
Features of props:
  • One-way transfer: Vue uses one-way data flow, that is, when the data of the parent component changes, it will be passed to the child component, but not vice versa. This is to prevent child components from inadvertently modifying the state of the parent component.
  • If you really want to modify the properties in props, save them in your own data.
  • There are two values of props, one is a string, the other is an object
//The first kind of props :[ 'name' , 'number' , 'msg' ] //The second kind of props :{ msg :{ type : Object , default () { return {} } } } Copy code
  • The name of props used in the template should be replaced by a dash

2.2 Sub-components passed
$emit()
Trigger events to the parent component (and carry data)

  • The child component triggers an event through $emit(), and the parent component uses v-on to listen to the custom event triggered by the child component on the custom label of the child component.
  • When the parent component uses the child component, the method of binding the parent component (
    <son @parentsay="say"></son>
    ), the method of the parent component accepts the data passed by the child component.
  • Bind an event in the child component
    <button @click="sonSay">Said the son</button>
    , Send the event in the event function of the child component, and pass the parameters of the child component.
sonSay () { this .$emit( "parentsay" , this .msg); }, Copy code

2.3 Communication between components-custom events (EventBus)

Define an empty Vue instance as the central event bus (event center), use it to trigger events and monitor events, cleverly and lightly realize the communication between any components, including father and child, brothers, and cross-level. When our project is relatively large, we can choose a better state management solution vuex. Specific implementation method:

var Event = new Vue(); Event.$emit(event name, data); Event.$on(event name, data => ()); Copy code
< div id = "app" > < my-a > </my-a > < my-b > </my-b > </div > < template id = "a" > < div > < h3 > A component : {{Name}} </h3 > < button @ click = "send" > a component sends data to b component </button > </div > </template > < template id = "b" > < div > < h3 > B component: {{name}} </h3 > </div > </template > < script > //The first step: define an empty Vue instance as an event bus var Event = new Vue(); var A = { template : "#a" , data ( ) { return { name : "tom" , }; }, methods : { send () { /* Step 2: Send the data formed by a to b, and click send to send an event named add The first parameter is the event name, and the second parameter is the data of the current component */ Event.$emit( "add" , this .name); }, }, }; var B = { template : "#b" , data () { return { name : "" , }; }, methods : { addText ( name ) { console .log(name); this .name = name; }, }, mounted () { //Step 3: Receive the event in the mounted event of the b component //The first parameter is the event name, and the second is the callback function (it was renamed for the convenience of unbinding) Event.$on( "add" , this .addText); }, beforeDestroy () { //Step 4: Unbind the event to prevent memory leak Event.$off( "add" , this .addText); }, }; var vm = new Vue({ el : "#app" , components : { "my-a" : A, "my-b" : B, }, }); </script > copy code
Step 1: Initialize EventBus directly in main.js in the project //Vue.prototype.$EventBus = new Vue() new Vue({ el : "#root" , render : ( h ) => h(App), beforeCreate () { Vue.prototype.$EventBus = this } }); Use his three methods: $on() $emit() $off() Step 2: Bind event monitoring to vm via $on() Step 3: Distribute events through $emit() Copy code

3. The life cycle of the component

The life cycle of a single component

Divided into the mounting phase, the update phase, and the destruction
phase.

created
The execution of the hook function indicates that the instance initialization is complete and the data data can be obtained, but the root DOM element el used by the Vue instance has not been initialized yet
beforeMount
When executed: data and el have been initialized, but el is not rendered into the data at this time, and the value of el is a "virtual" element node.

mounted
The execution of the hook function indicates that the interface has been rendered and mounted on the instance. You can do some Ajax to obtain information, bind events, and dom operations.

In the update phase,
only the data changes will be called

beforeUpdata
with
upDated
, The execution of beforeUpdate indicates that the data in el has been updated, and when updated is triggered, it indicates that the data in el has been rendered and the component dom is updated.

Destruction phase

beforeDestroy
Hook function means preparation before destruction, we can in it, unbinding custom events, destroying sub-components, event listeners, timers, etc.

All life cycle hooks automatically bind this context to the instance, so you cannot use arrow functions to define a life cycle method (for example, created: () => this.fetchTodos()), which will cause this to point to the parent.

Life cycle with parent and child components

The created creation phase (instance initialization phase) is to create the parent component first, and then create the child component.
The mounted rendering stage is to ensure that the child component is rendered before rendering the parent component

Father beforeCreate->parent created->parent beforeMount->child beforeCreate->child created->child beforeMount->child mounted->parent mounted

The update stage is: the data of the parent component is modified first (update is triggered first), the child component is modified again (the update is triggered again), the child component is updated (rendering), and the parent component says that it is updated (rendering)

Parent beforeUpdate->child beforeUpdate->child updated->parent updated

The destruction phase is:

Parent beforeDestroy->child beforeDestroy->child destroyed->parent destroyed

4. Slot distribution content

  • If the parent component wants to insert something into the label of the child component, it will use the slot slot.
  • The popular understanding of slot is "occupying a hole". Slots are used to occupy a good position in the component template. When the component label is used, the content in the component label will automatically fill in the hole (replace the slot position in the component template)

Basic use

The use of the slot is very simple, divided into two steps:

Step 1: Write the slot label in the template of the sub-component

<slot>Default content</slot>
; Step 2: When the parent component uses the child component, write the content you want to insert in the label of the child component, such as
<one>hello, Cai Xukun</one>

Scope: In the parent component, the scope of the content distributed by the slot (filling the hole for the slot) is the parent component, and it can directly bind the methods and data in the parent component

Named slot

  • As the name implies, a named slot is a slot with a name. If the parent component wants to distribute multiple content to the child component, it needs to dig multiple holes in the child component. At this time, the named slot needs to be used.

Steps to use the named slot :

1. If multiple slots are used in the sub-assembly, you can use the name of the slot label to give it a name. And put it in the corresponding position
2. Write the content you want to insert in the parent component, and write it in the outermost label (you can use the template label):

v-slot: slot name
,
v-slot:
Can be abbreviated as
#
. For example, v-slot:header='slotProps' can be abbreviated as
#header='slotProps'

Scope slot

  • If the parent component is inserted into the content section of the subcomponent slot, it can only access the data and methods of the parent component. If you want to access the data and methods of the subcomponent, you need to use it to act on the slot.

step:

Step 1: Bind the attributes to be transferred to the slot of the subcomponent

<slot name="footer" v-bind:user="name"></slot>

Step 2: Use v-slot to set a value in the parent component to define the name of the slot we provide:

v-slot:footer="slotProps"

Step 3: Pass

slotProps.user
You can use the data

Exclusive default slot abbreviation

When only one default slot is used, we want to pass the data of the sub-component, we can use the sub-component tag as the outer element, and write it

v-slot='...'
Can

Other ways to write v-slot (deconstruct the slot)

Pay attention to the wording of v-slot:

< div id = "app" > < one v-slot = "{childName}" > {{childName}} {{name}} </one > </div > < template id = "one" > < div > < header > < slot :child-name = "name" > </slot > < span > I am the head </span > </header > </div > </template > copy code
<div id= "app" > < one v-slot = "{childName:aaa}" > {{aaa}} {{name}} </one > </div> < template id = "one" > < div > < header > < slot :child-name = "name" > </slot > < span > I am the head </span > </header > </div > </template > copy code

5. Advanced features of components

5.1 Custom v-model

5.2 $nextTick() and refs

  • Vue will perform asynchronous rendering for performance considerations: that is, when the data changes, the dom will not render immediately, but will update the data changes to the view once when the data no longer changes.
  • After the DOM is updated, it will execute
    this.$nextTick(()=>{})
    In the callback function, some related operations are performed in the callback function, which allows us to obtain the latest dom node after operating the data.
  • We can combinerefsTo getdomnode. Register in the parent element tag first'ref=xxx',Then pass'this.refs to get the dom node. First register `ref=xxx` in the parent element tag, and then pass `this. Refs.xxx` acquisition dom node, which is a read-only attribute.
<!DOCTYPE html > < html lang = "en" > < head > < meta charset = "UTF-8"/> < meta name = "viewport" content = "width=device-width, initial-scale=1.0"/> < title > Document </title > < script src = "https://cdn.jsdelivr.net/npm/vue/dist/vue.js" > </script > </head > < body> < div id = "app" > < ul ref = "ul1" > < li v-for = "item in list" > {{item}} </li > </ul > < button @ click = "addItem" > Add 1 item </button > </div > < script > vm = new Vue({ el : "#app" , data: { list : [ "a" , "b" , "c" ], }, methods : { addItem () { this .list.push( ` ${ Date .now()} ` ); this .list.push( ` ${ Date .now()} ` ); this .list.push( ` ${ Date .now ()) ` ); //Get the dom element //Write ref='xxx' in an element, you can get the dom element through this.$refs const ulEle = this .$refs.ul1; console .log(ulEle .childNodes.length); //3 will not include everything, because after the data is changed, the dom will not change immediately. If you want to get all the nodes of the DOM, you must use `this.$nextTick()` //as follows: //1. Asynchronous rendering, $nextTick waits for the dom to be rendered and then callback //2. When the page is rendered, the data modification will be integrated, and multiple data modifications will only be rendered once this .$nextTick( () => { const ulEles = this .$refs.ul1; console .log( "NextTick added" , ulEles.childNodes.length); console .log( "NextTick added" , ulEles); }); }, }, }); </script > </body > </html > Copy code

5.3 Dynamic and asynchronous components

Dynamic component

One comes with Vue

<component>
Label, this label is used to dynamically display components. It has an is attribute, through
v-bind:is="dynamic component name"
Properties to select the components to be mounted.

<div id= "app" > < button @ click = "onChangeComponentClick(1)" > To ComponentA </button > < button @ click = "onChangeComponentClick(2)" > To ComponentB </button > < button @ click = " onChangeComponentClick(3)" > To ComponentC </button > <!-- Declared area--> < component :is = "componentId" > </component > </div> <!-- Define the content of the component template --> < template id = "component-a" > < div > component-a </div > </template > < template id = "component-b" > < div > component-b </div > </template > < template id = "component-c" > < div > component-c </div > </template > < script type = "text/javascript" > //Register component A/B/C var commponentA = { template : "#component-a" , }; var commponentB = { template : "#component-b" , }; var commponentC = { template : "#component-c" , }; var vm = new Vue({ el : "#app" , data : function () { return { componentId : commponentA, }; }, methods : { //Pass in different values through a click event to determine the component to be mounted in the dynamic block onChangeComponentClick : function ( type ) { switch (type) { case 1 : this .componentId = commponentA; break ; case 2 : this .componentId = commponentB; break ; case 3 : this .componentId = commponentC; break ; } }, }, }); </script > copy code

1. Such a pass

<component>
To declare the area, bind the component (componentId) through v-bind:is, and then change the display content by changing the component (componentId) direction, which can be called a dynamic component.

2. Every time we switch the component, the template (template in the component) will always be re-rendered, and we know that each DOM rendering is actually a very performance-consuming operation, so if you want to avoid such repeated rendering, you can by

<keep-alive>
The tag tells Vue to cache components that have been rendered.

3.

<keep-alive>
Is very simple to use, just use
<keep-alive>
Come wrap
<component :is="componentId"></component>

Asynchronous component

Let's take a common phenomenon in life to see what is asynchronous: you boil water, and brush your teeth without waiting for the water to boil. When the water is boiled, it will make a sound to tell you, and then you will deal with the things after the water is boiled! After reading this, to understand the official explanation is a bit clear!

In large applications, we may need to divide the application into smaller code blocks and load a module from the server only when needed. To simplify, Vue allows you to define your component as a factory function, which will parse your component definition asynchronously. Vue will trigger the factory function only when the component needs to be rendered, and will cache the result for future re-rendering.
One of the most important functions of asynchronous loading is to speed up page access. For example, we can make some non-first-screen pages load asynchronously.

<div id= "app" > < async-example > </async-example > </div> < template id = "demo" > < div > I am an asynchronous component </div > </template > < script type = "text/javascript" > //Register component var resCom = { template : " #demo " } Vue.component( 'async-example' , function ( resolve, reject ) { setTimeout ( function () { //Pass component definition to resolve callback resolve(resCom); }, 1000 ); }); var vm = new Vue({ el : "#app" }) </script > copy code
  • In the above code, first declare a variable resCom, and assign it a template to point to async-example, which is consistent with the operation performed when declaring a local component.
  • Then through Vue.component('async-example', function (resolve, reject){} to create a factory function, this function contains two parameters resolve, reject. These two parameters represent two callback methods, we can pass resolve(resCom) allows the program to asynchronously load the defined resCom component. You can also use reject('loading failure description content'); to indicate the loading failure. Here setTimeout is only used to simulate asynchronous operations. Of course, the above code can also be passed The way to use partial components:
var vm = new Vue({ el : '#app' , components : { 'async-example' : function ( resolve, reject ) { setTimeout ( function () { resolve(resCom); //reject('Description of loading failure'); }, 1000 ); } } }); Copy code

repair it a little:

//Register component var resCom = { template : "#async-example" }; var promise = new Promise ( function ( resolve, reject ) { setTimeout ( function () { resolve(resCom) }, 1000 ); }); var vm = new Vue({ el : "#app" , components :{ 'async-example' : function () { return promise } } }) Copy code

It can be built like this in webpack:

//Global registration Vue.component( 'async-webpack-example' , //This `import` function will return a `Promise` object. () => import ( './my-async-component' ) ) //Partial registration new Vue({ //... components : { 'my-component' : () => import ( './my-async-component' ) } }) Copy code

Advanced asynchronous registration:

<script type= "text/javascript" > //Register component var resCom = { template : "#async-example" }; var promise = new Promise ( function ( resolve, reject ) { setTimeout ( function () { resolve(resCom) }, 2000 ); }); var LoadingComponent = { template : '<div>Component displayed during loading</div>' }; var ErrorComponent = { template : '<div>Asynchronous component failed to load</div>' }; const AsyncComponent = function () { return { //The component that needs to be loaded (should be a `Promise` object) component : promise, //The component used when the asynchronous component is loaded loading : LoadingComponent, //The component used when the loading fails error : ErrorComponent, //Show the delay time of the component when loading. The default value is 200 (milliseconds) delay : 200 , //If a timeout period is provided and the component loading has timed out, //use the component used when the loading fails. The default value is: `Infinity` //PS: Component loading timeout time, timeout means loading failed and ErrorComponent will be displayed. //For example, when we change the setTimeout in Promise to 4000, it will display ErrorComponent timeout : 3000 } } var vm = new Vue({ el : "#app" , //Note the difference between this and the previous writing method, because we proposed this method to assign to the variable components of AsyncComponent :{ 'async-example' : AsyncComponent } }) </script> Copy code

Register asynchronous components through the registration routing method:

//When registering a route, there will be such a statement, which is actually registering an asynchronous component. //The `import` function will return a `Promise` object //Use the asynchronous component and webpack's code-splitting function together component : ( ) => Import ( /* webpackChunkName: "index" */ './views/Index.vue' ) copying the code

5.4 keep-alive cache component

1. What is keep-alive?
  • Keep-alive is a built-in component of Vue. When it wraps dynamic components, it will cache inactive component instances instead of destroying them (
    The main purpose is to save the component state and avoid re-rendering
    ). Similar to transition, keep-alive is an abstract component: it will not render itself as a DOM element, nor will it appear in the parent component chain.

Take a look at the specific usage scenarios :

  • The first more common scenario, when we go back from the home page -> list page -> business detail page -> and then back, the list page should be keep-alive at this time.
  • 2. when we go from the home page -> list page -> business details page -> return to the list page (caching required) -> return to the home page (caching required) -> enter the list page again (no caching required), this time It is to control the keep-alive of the page on demand.
2. Keep-alive Props
  • include-string or regular expression. Only components with matching names will be cached.
  • exclude-string or regular expression. Any components with matching names will not be cached.
  • max-number. The maximum number of component instances that can be cached.
3. Life cycle function

In the component or route wrapped by keep-alive, there will be two more hook functions: activated and deactivated. When the component is switched inside, its two life cycle hook functions, activated and deactivated, will be executed accordingly.
After using exclude, these two hook functions will not be called even if they are wrapped in keep-alive! In addition, this hook function will not be called when rendering on the server side.

  • 1.activated : Called when the keep-alive component is activated, this hook function is not called during server-side rendering.
  • Using keep-alive will keep the data in the memory. If you want to get the latest data every time you enter the page, you need to get the data in the activated phase and assume the task of getting the data in the original created hook function.
  • 2.deactivated : called when the keep-alive component is deactivated, the hook is not called during server-side rendering
Application in dynamic components
< Keep-Alive : the include = "WhiteList" : the exclude = "BLACKLIST" : max = "AMOUNT" > < Component : IS = "currentComponent" > </Component > </Keep-Alive > copy the code
Application in vue-router
Cache all pages (in App.vue)
< template > < div id = "app" > < keep-alive > < router-view/> </keep-alive > </div > </template > < script > export default { name : 'App' } </script > copy code
Cache pages based on conditions (in App.vue)
//include defines the cache whitelist, keep-alive will cache the hit components; //exclude defines the cache blacklist, and the hit components will not be cached; //max defines the upper limit of the cache component, and the LRU policy replacement is used if the upper limit is exceeded Cache data. <template> < div id = "app" > //1. The component whose name is test will be cached < keep-alive include = 'test' > < router-view/> </keep-alive > //2. Cache the components whose name is a or b, and use them in combination with dynamic components < keep-alive include = 'a,b' > < router-view/> </keep-alive > //3. To use regular expressions, v-bind is required < keep-alive :include = '/a|b/' > < router-view/> </keep-alive > //5. Dynamic judgment < keep-alive :include = 'includedComponents' > < router-view/> </keep-alive > //5. The component whose name is test will not be cached < keep-alive exclude = 'test' > < router-view/> </keep-alive > </div > </template> < script > export default { name : 'App' } </script > copy code
Combined with Router, cache some pages (in the index.js file under the router directory)

Use the meta object provided by vue-router to add a field to the cache, such as the homepage, list page, business details, etc., to determine whether the user is going forward or back and whether keep-alive is required

Import Vue from 'VUE' Import Router from 'VUE-Router' const Home = Resolve => the require ([ '@/Components/Home/Home' ], Resolve) const Goods = Resolve => the require ([ '@/Components/home/goods' ], resolve) const Ratings = resolve => require ([ '@/components/home/ratings' ], resolve) const Seller = resolve => require ([ '@/components/home/seller' ],resolve) Vue.use(Router) export default new Router({ mode : 'history' , routes : [ { path : '/' , name : 'home' , component : Home, redirect : 'goods' , children : [ { path : 'goods' , name : 'goods' , component : Goods, meta : { keepAlive : false //no need to cache } }, { path : 'ratings' , name : 'ratings' , component : Ratings, meta : { keepAlive : true //need to be cached } }, { path : 'seller' , name : 'seller' , component : Seller, meta : { keepAlive : true //need to be cached } } ] } ] }) Copy code
Inside app.vue ($route.meta.keepAlive)
< template > < div id = "app" > < keep-alive > < router-view v-if = "$route.meta.keepAlive" > </router-view > </keep-alive > < router-view v -if = "!$route.meta.keepAlive" > </router-view > </div > </template > < script > export default { name : 'App' } </script > copy code
Now set up component caching on demand for the scene

router.js

//Home { path : '*' , name : 'Home' , //Route lazy loading: component : () => import ( /* webpackPreload: true */ '@/views/home' ), meta : { keepAlive : true , deepth : 1 } }, //Product list { path : '/product' , name : 'Product' , component : () => import ( '@/views/product' ), meta : { keepAlive : true , deepth : 2 } }, //product details { path : '/detail' , name : 'Detail' , component : () => import ( '@/views/detail' ), meta : { keepAlive : true , deepth : 3 } }, Copy code

under app.vue

<template> < div id = "app" > < keep-alive :include = "include" > < router-view v-if = "$route.meta.keepAlive"/> </keep-alive > < router-view v-if = "!$route.meta.keepAlive"/> </div > </template> export default { data () { return { include : [] }; }, watch : { $route(to, from ) { //If the page to be to (enter) needs to be cached by keepAlive, push the name into the include array if (to.meta.keepAlive) { ! this .include.includes(to.name) && this .include.push(to.name); } //If the page to form (leave) is cached by keepAlive, //then judge whether to go forward or backward based on deepth //If it is backward: if ( from .meta.keepAlive && to.meta.deepth < from .meta. deepth) { const index = this .include.indexOf( from .name); index !==- 1 && this .include.splice(index, 1 ); } } } }; Copy code
Use meta.keeAlive and key value

First of all, we must still use the meta.keeAlive field to make judgments, but there is no need to define the depth.

Enter the app.vue page, we are

<router-view>
Add a key. This key is just like what we defined using the v-for loop. Everyone knows that the key is an identifier, right? It acts on the vue to perform the diff algorithm in the virtual dom to improve the rendering efficiency.

<template> < div id = "app" > < keep-alive > < router-view v-if = "$route.meta.keepAlive" :key = "key"/> </keep-alive > < router-view v-if = "!$route.meta.keepAlive" :key = "key"/> </div > </template> < script > export default { computed : { key () { return this .$route.fullPath; } } }; </script > copy code
//Then we add a timestamp to the page parameters that need to be forced to refresh, so that on-demand keep-alive can be achieved. onClick () { this .$router.push({ path : '/product' , query : { t : + new Date () } }) } Copy code

5.5 The mixin component abstracts away the common logic

Mixin provides a very flexible way to distribute reusable functions in Vue components. A mixin object can contain any component options. When a component uses a mixed-in object, all the options of the mixed-in object will be "mixed" into the options of the component itself.

  • 1. When the component uses a mixed object, all the options of the mixed object will be "mixed" into the options of the component itself;
  • 2. The data objects will be recursively merged internally, and the component data will be given priority when conflicts occur. The hook functions of the same name will be merged into an array, so all will be called. In addition, the hook of the mixed object will be called before the hook of the component itself;
  • 3. The options that are objects, such as methods, components, and directives, will be merged into the same object. When the key names of two objects conflict, take the key-value pair of the component object;
  • 4. Please use global mixin with caution, because it will affect each Vue instance created separately (including third-party components).
Basic use

The first step: src/mixin/demo.js

export default { data () { return { msg : "This is the data of mixin" , mixinMsg : "This is the data of mixin" , } }, created () { console .log( 123 ) }, methods :{ onClick () { console .log( 'onClick in mixin was triggered' ) } } } Copy code

Step 2: Introduce and use in the component, only use mixin in the component.

<template> < div class = 'container' > < div > {{msg}} </div > < div > {{mixinMsg}} </div > < div @ click = "onClick" > click </div > </div > </template> < Script > Import a mixin from '@/a mixin/demo.js' ; Export default { as mixins : [a mixin], //1.data attributes when the key conflict, the data components will be preferentially data () { return { msg : 'data in the component' } }, //2. The hook functions of the same name will be merged into an array and will be called in turn. The hook function of the mixed object will call created ( before the component spawns the hook function) { console .log( 'created within the component' ) }, //3. Options whose values are objects, such as methods, components and directives, will be merged into the same object. When the key names of two objects conflict, the key value of the component object takes precedence. methods : { onClick () { console .log( 'onClick in the component is triggered' ) } }, } </script > copy code
Global component mixin

Before initializing Vue, call Vue.mixin() for global mixing, which can be used in main.js:

Vue.mixin({ data () { return { $_globalMsg : "Global mixin data" } }, created () { console .log( 'Created to trigger global mixin' ) }, methods :{ $_globalMixin(){ console .log( '$_globalMixin' ) } } }) Copy code
Case-mixed into lazy loading image function

Add lazy loading to all pictures in the component

The first step: src/mixin/demo.js

Import logo from '@/Assets/logo.png' Export default { Data () { return { baseImg : logo, $_timer : null } }, mounted () { //Load the first screen lazily once this .$_lazyLoadImage(); //Listen to the scroll event window .addEventListener ( 'scroll' , this .$_handelScroll); //Remove the scroll event this .$on( 'hook:beforeDestroy ' , () => { window .removeEventListener( 'scroll' , this .$_handelScroll) }) }, methods : { $_handelScroll() { clearTimeout ( this .$_timer); this .$_timer = setTimeout ( () => { this .$_lazyLoadImage(); }, 20 ); }, //Lazy loading pictures $_lazyLoadImage() { const imgList = this .$_getNeedLoadingImg(); if (imgList.length <= 0 ) return ; //Determine whether the picture is displayed imgList.forEach( img => { if ( this .$_imgInView(img)) { this .$_showImg (img) } }) }, //Get the picture that needs to be loaded $_getNeedLoadingImg() { let images = Array .from( document .querySelectorAll( 'img[data_src]' )); images = images.filter( ele => { return !ele.getAttribute( 'isloaded' ) }) return images }, //Calculate the position of the picture and determine whether the picture is displayed $_imgInView(img) { return window .innerHeight + document .documentElement.scrollTop >= img.offsetTop }, //Show pictures $_showImg(img) { const image = new Image(); const src = img.getAttribute( 'data_src' ) image.src = src; image.onload = () => { img.src = src; //The tag has been loaded img.setAttribute( 'isloaded' , true ); } } } } Copy code

Step 2: Use in the required components

<template> < div class = 'container' > < div > < img :src = "baseImg" alt = "" > </div > < div v-for = "(item,index) in imgSrc" :key = " index" > < img :src = "baseImg" :data_src = "item" alt = "" > </div > </div > </template > < Script > Import a mixin from '@/a mixin/demo.js' ; Export default { as mixins : [a mixin], Data ( ) { return { imgSrc :[ 'Put picture link' ] } } } </script > < style lang = "scss" scoped > img { width : 200px ; height : 200px ; } </style > Copy code

Part 7: Custom commands and filters

Custom instruction (v-xxx)

In Vue, in addition to the default built-in instructions (v-model and v-show) of the core functions, Vue also allows the registration of custom instructions. Use custom instructions to perform low-level operations on common DOM elements to achieve the purpose of reuse.

Registration instructions are divided into global registration and partial registration

Custom global directive

Generally register directly in the main.js file, and you can use instructions in the component

//Pseudo code: vue.directive( 'Custom instruction name' , { Life cycle name: function ( el ) { Instruction business logic code } }); //Example: v-color Vue.directive( "color" , { //Here el is the element of the bound instruction bind : function ( el ) { el.style.color = "red" ; } }); < p v-color > I am a paragraph </p > copy code
Custom local instructions
  • Add directives to the object passed when creating a Vue instance
  • Can only be used in the customized Vue instance
let vm = new Vue({ el : '#app' , directives : { "color" : { bind : function ( el, obj ) { el.style.color = obj.value; } } } }); Copy code

Custom instruction life cycle hook function

bind

When the instruction is bound to the dom element, it will be executed. Since it is only bound once, it will only be executed once. Here you can perform a one-time initialization setting.

inserted

Called when the bound element is inserted into the parent node (only the parent node is guaranteed to exist, but not necessarily already inserted into the document)

update

Called when the VNode of the component is updated, but it may happen before its child VNode is updated. The value of the instruction may or may not have changed. But you can ignore unnecessary template updates by comparing the values before and after the update.

componentUpdated

Called after the VNode of the component where the instruction is located and its sub-VNodes are all updated.

unbind

Called when the instruction is unbound from the dom element (the bound Dom element is removed by Vue), only called once.

#### 3. Custom command transfer parameters

  • For example: v-model="name", we can also pass and pass in our custom instructions.
  • When executing the method corresponding to the custom instruction, in addition to passing el to us, an object is also passed to us. This object stores the parameters passed by the instruction.
<div id= "app" > < p v-color = "curColor" > I am a paragraph </p > </div> < script > Vue.directive( "color" , { //where el is the element of the bound instruction bind : function ( el, obj ) { el.style.color = obj.value; } }); let vue = new Vue({ el : '#app' , data : { curColor : 'green' }, methods : { } }); </script > copy code

filter

  • Vue.js allows you to customize filters, which can be used for some common text formatting. Filters can be used in two places: double curly brace interpolation and v-bind expressions (the latter is supported from 2.1.0+). The filter should be added at the end of the JavaScript expression, indicated by the "pipe" symbol:
  • Filters and functions and calculated attributes are used to process data, but filters are generally used to format inserted text data.
//The value on the left is handed over to the filter on the right for processing <!-- in double curly braces --> {{ message | capitalize }} <- In! `V-bind` in -> < div v-the bind: the above mentioned id = " rawId | formatid " > </div > Copy the code
  • By default, the data processing function receives a parameter, which is the data currently to be processed, and the filter can be used continuously.
< div id = "app" > <!--Vue will pass the name to the specified filter for processing, and then insert the processed result into the specified element for rendering --> < p > {{name | formartStr2}} </p > </div > < script > Vue.filter( "formartStr2" , function ( value ) { console .log(value); //Tsinghua University, Massachusetts Institute of Technology , Imperial College //Regular expression `/University/g` is to match the college globally, and then replace it with college value = value.replace( /University/g , "College" ); console .log(value); //Tsinghua Wudaokou College, Massachusetts Institute of Technology, Imperial College, Shrek College return value; }); let vue = new Vue({ el : "#app" , data : { name : "Tsinghua Wudaokou University, Massachusetts Institute of Technology, Imperial College, Shrek University" , }, }); </script > copy code

Part 8: Rendering functions and JSX syntax

1. Virtual DOM

  • Virtual DOM is described using ordinary JavaScript objects == DOM == element. In Vue, each virtual node is an instance of VNode. The high execution performance of Vue.js is due to the virtual DOM mechanism.
  • Virtual DOM objects are ordinary JavaScript objects, and accessing JavaScript objects is much faster than accessing the real DOM. Before updating the real DOM, Vue will compare the differences in the virtual DOM structure before and after the update, and then use the asynchronous update queue. Way to update the difference part to the real DOM.

The real DOM structure:

<div id= "app" > < h1 > hello, vue </h1 > </div> Copy code

JavaScript objects created by Vue.js's virtual DOM:

var vNode = { tag : 'div' , data :{ attrs :{ id : 'app' } }, children :{ //h1 node } } Copy code

2.render function

Concept :

  • The render() function provided by Vue allows us to generate html templates using JavaScript programming. In most cases, we use the template template to build HTML in the Vue instance.
  • The render function is the same as template to create html templates, but in some scenarios, using template to achieve the code is tedious and has a lot of repetition. In this case, you can use the render function.

Parameters :

  • The render function accepts a function parameter createElement.
  • The return value of the render() function must be the createElement() method, which is used to create a virtual node.

createElement() has three parameters :

  • 1. The first parameter is an HTML tag string, component option object, or an async function that parses any of the above. Type: {String | Object | Function}.
    Required.
  • 2. The second parameter is a data object containing template-related attributes. You can use these features in the template. Type: {Object}. Optional. The simple point is the attribute collection of the element (including common attributes, porp, event attributes, custom commands, etc.);
  • The third parameter is the child node information, which is output in the form of an array. If the element has only text child nodes, it is directly given as a string. If there are other child elements, the createElement function will continue to be called.
  • All VNodes in the component tree must be unique. If you want to repeat elements/components many times, you can use factory functions to achieve.
render: function ( createElement ) { return createElement( 'div' , Array .apply( null , { length : 20 }).map( function () { return createElement( 'p' , 'hi' ) }) ) } Copy code
  • JavaScript replaces template functions

When we used templates to build HTML before, we could use directives. When creating a template through the render function, these instructions cannot be provided, so we can only write JavaScript to achieve it.

//v-if and v-for <ul v- if = "items.length" > < li v-for = "item in items" > {{ item.name }} </li > </ul> Copy code

To complete the construction of the above HTML page through the render function, the process is as follows:

props: [ 'items' ], render : function ( createElement ) { if ( this .items.length) { return createElement( 'ul' , this .items.map( function ( item ) { return createElement( 'li' , item .name) })) } else { return createElement( 'p' , 'No items found.' ) } } //determine the length of the parameter item, if the length is 0, p returns a label, if the length is not zero, it returns a ul tag, and the loop to create a child node li duplicated code
  • v-model

The essence of v-model is to use the value of value as prop while listening to input events, and the above code is implemented according to the logic of v-model.

props: [ 'value' ], render : function ( createElement ) { var self = this return createElement( 'input' , { domProps : { value : self.value }, on : { input : function ( event ) { self.$emit( 'input' , event.target.value) } } }) } Copy code

3.
render:h => h(App)

  • The render function is to render a view and then provide it to el to mount. If there is no render, the page will not come out.
  • 1. As a function, the render method of the Vue instance option object accepts the passed-in parameter h function and returns the function call result of h(App).
  • 2. Secondly, when Vue creates a Vue instance, it renders the DOM tree of the instance by calling the render method.
  • 3. Finally, when Vue calls the render method, it will pass in a createElement function as a parameter, that is, the actual parameter of h here is the createElement function, and then createElement will be called with APP as the parameter.
render: h => h(App) is an abbreviation of the following: render : function ( createElement ) { return createElement(App); Copy code

4.JSX

Although the render function solves our problem, it is too troublesome. This is why there is a Babel plugin for using JSX syntax in Vue, which allows us to return to the syntax that is closer to the template.

import AnchoredHeading from './AnchoredHeading.vue' new Vue({ el : '#demo' , render : function ( h ) { return ( < AnchoredHeading level = {1} > < span > Hello </span > world! </AnchoredHeading > ) } }) Copy code

Part 9: Transitions in Vue

  • In the Vue project, we can use the transition component encapsulated by Vue, so that we can add transitions and animations to any element and component
  • Generally used with v-if, v-show, dynamic components, and component root nodes. Add animation when displaying hidden components in v-if and v-show; switch components to realize page switching, add transition animation, and fade in and out effects to enhance user experience.

The following tools are included :

  • Automatically apply classes in CSS transitions and animations
  • Can be used with third-party CSS animation libraries, such as Animate.css
  • Use JavaScript in the transition hook function to directly manipulate the DOM
  • Can be used with third-party JavaScript animation libraries, such as Velocity.js

1. Transition class name

Animation enter :

  • v-enter: The animation enters the previous initial state. It takes effect before the element is inserted, and is removed in the next frame after the element is inserted.
  • v-enter-to: The ending state after the animation enters. The next frame takes effect after the element is inserted (at the same time the v-enter is removed), and is removed after the transition/animation is completed.
  • v-enter-active: The time period when the animation enters. Applied throughout the transition phase, it takes effect before the element is inserted, and is removed after the transition/animation is complete. This class can be used to define the process time, delay and curve function of the entry transition.

PS: The first and second are time points; the third is time period.
Animation leaving :

  • v-leave: the initial state before the animation leaves. It takes effect immediately when the exit transition is triggered, and the next frame is removed.
  • v-leave-to: The ending state of the animation after it leaves. The next frame takes effect after the leave transition is triggered (at the same time v-leave is deleted), and is removed after the transition/animation is completed.
  • v-leave-active: The time period when the animation leaves. Applied in the entire phase of the exit transition, it takes effect immediately when the exit transition is triggered, and is removed after the transition/animation is completed. This class can be used to define the process time, delay and curve function of leaving the transition.

PS: The first and second are time points; the third is time period.

Note that for these class names that are switched in the transition, if you use an unnamed

<transition>
, Then v- is the default prefix for these class names. If you use
<transition name="my-transition">
, Then v-enter will be replaced with my-transition-enter.

Principle description :

  • 1. The most basic point is that if you want to implement transition animation in a single element/single component, then you need to wrap a layer of label outside the HTML tag where the element/component is located. This package is required for both transition and animation. stand up.
  • 2. As mentioned above, when an element/component is wrapped by a label, Vue will automatically build the animation process, that is, automatically add/remove the corresponding CSS class name at a certain time node. Vue actually provides 6 corresponding class names, as above As shown in the figure.

2. Transition

Specific implementation process:

< style > /* Different entry and exit animations can be set*/ /* Set duration and animation function*/ .v-enter { transform : translateX ( 10px ); opacity : 0 ; } .v-enter-active { transition : all. 3s ease; } /*v-enter-to and v-leave are the same, you can write only one, or you can omit it, just write v-enter and v-leave-to and the functions of the entry and exit stages*/ .v-enter-to { } .v-leave {} .v-leave-active { transition : all. 8s cubic-bezier ( 1.0 , 0.5 , 0.8 , 1.0 ); } .v-leave-to { transform : translateX ( 10px ); opacity : 0 ; } </style > < transition > < p v-if = "show" > hello </p > </transition > Copy code

enter:

  • Before the transition starts, the p tag will be added with the class v-enter-active and the class v-enter; when the transition starts, the class v-enter is removed and the class v-enter-to is added. After entering the transition: the v-enter-active and v-enter-to classes are removed.

go away:

  • Before leaving, the class v-leave-active and v-leave will theoretically be added; at the beginning of the transition, the class v-leave will be removed and the class v-leave-to will be added. Because in theory the v-leave class is added and removed in an instant, I have not observed it; according to the actual effect, I think the class v-leave does not work.

3.CSS animation

//The first step: Write the keyframe function @keyframes bounce- in { 0 % { transform : scale( 0 ); } 50 % { transform : scale( 1.5 ); } 100 % { transform : scale( 1 ); } } //The second step: write the status of entering and leaving .v-enter-active { animation : bounce- in .5s; } .v-leave-active { animation : bounce- in .5s reverse; } //The third step: use in the style <transition> < p v-if = "show" > Lorem ipsum dolor sit amet </p > </transition> Copy code

4. Transition-group list transition

The transition-group will generate a real dom node, which is span by default. The key value is required to switch the transition-group by default through tag="ul".

5. CSS animation: Animate.css library

Animate.css library address

1.1 Import Animate.css library

Method 1: npm install animate.css --save Way two: < head > < link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> </head > Copy code

1.2 Bind the required class name to the property during execution

< h1 class = "animate__animated animate__bounce" > An animated element </h1 > //or .my-element { display: inline-block; margin: 0 0.5rem; animation: bounce;/* referring directly to the animation's @keyframe declaration */ animation-duration: 2s;/* don't forget to set a duration! */ } Copy code

Part 10: Vue-Router

  • Vue's single-page application is based on routing and components. Routing is used to set access paths and map paths and components. Traditional page applications use some hyperlinks to switch pages and jump. In the vue-router single-page application, it is the switching between paths, which is actually the switching of components. Routing is the path manager of SPA (Single Page Application).
  • It is relatively simple to implement routing in Vue. Because all the content in our page is componentized, we only need to associate the path with the component, and then render the component on the page.

Vue Router is the official route manager of Vue.js. It is deeply integrated with the core of Vue.js, making it easy to build single-page applications. The functions included are:

  • Nested routing/view table
  • Modular, component-based routing configuration
  • Routing parameters, queries, wildcards
  • View transition effect based on Vue.js transition system
  • Fine-grained navigation control links with automatically activated CSS class
  • HTML5 history mode or hash mode, automatically downgrade in IE9
  • Custom scroll bar behavior

1. How to use vue-router?

  • Step 1: install vue-router
npm install vue-router -S duplicated code
  • Step 2: Use Vue.use() to load the VueRouter plugin in index.js under the router file
import Vue from "vue" ; import VueRouter from "vue-router" ; Vue.use(VueRouter); Copy code
  • Step 3: Configure routing (define routing and instantiate routing objects)
//1. Define routing rules const routes = [ { path : '/' , name : 'Home' , component : Home }, { path : '/about' , name : 'About' , component : () => import ( /* webpackChunkName: "about" */ '.. /views /About.vue' ) } ] //2. Instantiate routing object const router = new VueRouter({ routes }) //3. Export routing instance object Export default Router copy the code
  • Step 4: Mount the routing instance object to the root instance (main.js)
import App from './App.vue' import router from './ router ' new Vue({ router, render : h => h(App) }). Mount $ ( '#app' ) copying the code
  • Step 5: Set up the jump navigation

After clicking, jump to the corresponding routing component (shown as a change in url)

< template > < div id = "app" > < div id = "nav" > <!--Jump navigation--> < router-link to = "/" > Home </router-link > | < router- link to = "/about" > About </router-link > </div > < router-view/> </div > </template > Copy code
  • Step 6: Set the routing exit of the root component template (use routing)

Display the current routing component content according to the url path

<template> < div id = "app" > < div id = "nav" > < router-link to = "/" > Home </router-link > | < router-link to = "/about" > About </router-link > </div > <!-- Route exit--> < router-view/> </div > </template> Copy code

2. Routing mode (hash h5 history)

Front-end routing : It is to ensure that there is only one HTML page, and the page is not refreshed or redirected when interacting with the user, while matching a special URL for each view display form in the SPA. This special URL is used for refresh, forward, backward and SEO. To achieve this goal, we need to do the following two things:

  • Change the url and prevent the browser from sending requests like the server.
  • Can monitor the change of url

The model of vue-router has two modes: hash mode and history mode.

hash mode

  • Hash mode is to realize the browser rendering the specified components by changing the value behind #. Vue defaults to hash mode.

Description:

  • The hash here refers to the # sign and the following characters after the url.
  • win.loc.hash is obtained.
  • In hash mode, only the content before the hash symbol will be included in the request, and the change of the hash value will not cause the browser to send the request like the server.
  • When the hash changes, the url will be recorded by the browser, so you can use the browser's back.
  • Changing the hash (forward and backward of the browser) will not reload the page, but will trigger the hashchange event.
API used in hash mode:
window .location.hash = 'hash string' ; //used to set the hash value let hash = window .location.hash; //Get the current hash value //Listen to the hash change, and click the browser forward and back to trigger the window .addEventListener ( 'hashchange' , function ( event ) { let newURL = event.newURL; //The new url after the hash change let oldURL = event.oldURL; /before/hash to change the old URL }, to false ) copy the code

Before HTML5, browsers already had a history object. But in the early history, it can only be used for multi-page jumps: history-utilizes the new pushState() and replaceState() methods in the HTML5 History Interface. (Requires specific browser support) These two methods are applied to the browser's history stack. Based on the currently existing back, forward, and go, they provide the function of modifying the history. It's just that when they perform the modification, although the current URL is changed, the browser will not immediately send a request to the backend.

history mode

History mode is to modify the browser's browsing records through the pushState() method to achieve the effect of rendering without requesting the backend. However, it is recommended that the actual project still use the history mode.

const router = new VueRouter({ mode : 'history' , //If not written here, the route defaults to hash mode routes : [...] }) Copy code

Before HTML5, browsers already had a history object. But in the early history, it can only be used

Multiple pages
The jump:

history.go (- . 1 ); //a reverse history.go ( 2 ); //advance two history.forward (); //advance a history.back (); //a reverse copy the code

In the HTML5 specification, history has added the following APIs:

window .history .pushState (state, title, url) /* //Add a new state to the history state stack, while keeping the existing history record, add the url to the history record. state: The data that needs to be saved, a valid Javascript object, this data can be obtained in event.state when the popstate event is triggered title: title, basically useless, generally pass null url: Set the url of the new history record. The origin of the new url and the current url must be the same, otherwise it will be thrown error. The url can be an absolute path or a relative path. If the current url is https://www.baidu.com/a/, execute history.pushState(null, null,'./qq/'), it becomes https://www.baidu.com/a/qq/, Execute history.pushState(null, null,'/qq/'), it becomes https://www.baidu.com/qq/ */ window .history .replaceState (state, title, url) /* //Replace the current state with the new state, and replace the current page history in the history with url. Basically the same as pushState, but it is to modify the current history record, and pushState is to create a new history record */ window .addEventListener ( "the popstate", function () { //Return the current state object //listen to the browser's forward and backward events, pushState and replaceState methods will not be triggered }); Copy code

MDN explanation : HTML5 introduced history.pushState() and history.replaceState() methods, which can add and modify history entries respectively. These methods are usually used in conjunction with window.onpopstate. Since history.pushState() and history.replaceState() can change the url without refreshing the page, histroy in HTML5 has the ability to implement front-end routing.

The change of history does not trigger any events, so we cannot directly monitor the change of history and make corresponding changes. Therefore, we need to change our thinking. We can list all the situations that may trigger the change of history, and intercept these methods one by one, and monitor the changes of history in disguise. For the history mode of a single-page application, url changes can only be caused by the following four ways:

  • Click the browser s forward or back button
  • Click the a label
  • Trigger the history.pushState function in the JS code
  • Trigger the history.replaceState function in the JS code

summary:

  • The pushState() method can change the URL address without sending a request, and the replaceState() method can read the history stack and can also modify the browser record. These two methods are applied to the history record stack of the browser. Based on the existing back, forward, and go, they provide the function of modifying the history record. It's just that when they perform the modification, although the current URL is changed, the browser will not immediately send a request to the backend.
  • Under history, you can freely modify the path. When refreshing, if there is no corresponding response or resource in the server, the 404 page will be refreshed. The way the history mode changes the url will cause the browser to send a request to the server. This is not what we want to see. We need to do the processing on the server side: if no static resources are matched, the same html page should always be returned.

3. Dynamic routing matching

We often need to map all routes matched by a certain pattern to the same component. For example, we have a User component, which is used to render all users with different IDs. Then, we can use "dynamic segment" (dynamic segment) in the routing path of vue-router to achieve this effect:

3.1 params pass parameters

const User = { //Step 3: In the current component, you can use the parameter template : '<div>{{$route.params.name}}</div>' } const router = new VueRouter({ routes : [ //Step 1: The dynamic path parameters start with a colon { path : '/user/:name' , component : User} ] }) //In a routing link <router-link to= "/user/:jack" >user</router-link> //The two settings are equivalent: the corresponding values will be set to $route.params. For example: {name:'jack' } Copy code
<router-link :to= "{name:'User',params:{name:' ',age:10}}" >User</router-link> Copy code

3.2 Query for configuration and parameter transfer

const User = { //The third step: parameters can be used in the current component template: ' < div > {{this.$route.query.id}} </div > ' } const router = new VueRouter({ routes: [ //The first step: query parameters are passed, the routing configuration remains unchanged {path:'/user', component: User} ] }) //In a certain routing link, the beginning of the question mark < router-link to = "/user?id=111" > User </router-link > Copy code

vue-route will automatically encapsulate the id=foo after the? into this.route.queryin.at this time,In the componentthis.route.query. At this point, this is in the component. Route.query.id value 'foo'. == addition query parameters can also be transmitted through the navigation programming shown later through to the router-link attribute. ==

3.3 There is a change in the monitoring path

  • After the dynamic routing configuration is used, all the routes correspond to the same component, for example, to: user/:id when configuring the route, and set separately when setting the route jump
    /user/foo
     with 
    /user/bar
    , When using routing parameters, it is the User component that is reused. At this time, the life cycle hook of the component will not be called again. If you want to perform some initialization operations when switching paths, you can use the following two solutions:
  • In the component watch (monitor changes) $route object:
const User = { template : '...' , watch : { '$route' (to, from ) { //Respond to routing changes... } } } Copy code
  • Or .beforeRouteUpdate navigation guard If the destination is the same as the current route, only the parameters have changed (such as from one user profile to another/users/1 ->/users/2), you need to use beforeRouteUpdate to respond to this change (such as grabbing Take user information).
const User = { template : '...' , beforeRouteUpdate (to, from , next) { //react to route changes... //don't forget to call next() } } Copy code

4. Router, routes, and route are silly and confused?

  • 1.router: generally refers to the routing instance. Such as $router.
  • 2.routes: Refers to the routes API of the router routing instance. Used to configure multiple route routing objects. (Route configuration array)
  • 3.route: refers to the routing object. For example, $route refers to the current routing object.

4.1 The method provided by router can be used for programmatic navigation

router: router object, contains some functional functions for operating routing to realize programmatic navigation. Generally refers to access routing within any component. Such as:

$router.push() for routing programmatic navigation

The parameter of this method can be a string path, or an object describing the address. For example:

//String router.push( 'home' ) //Object router.push({ path : 'home' }) //named router router.push({ name : 'user' , params : { userId : '123' }}) //query parameter tape into/Register Plan = Private? Router.push ({ path : 'Register' , Query : { Plan : 'Private' }}) copy the code

Note: If path is provided, params will be ignored, you need to provide the name of the route or handwritten complete path with parameters:

const userId = '123' router.push({ name : 'user' , params : {userId }}) //->/user/123 router.push({ path : `/user/${userId} ` }) //->/User/123 //the params here do not take effect router.push ({ path : '/User' , the params : the userId {}}) //->/User copy the code
router.replace method

router.replace is very similar to router.push, written in the same way. But the actual effect is different. Push is to add a new record to the history. And replace is to directly replace the current browser history record! What is the most direct consequence? for example:

  • Using the push method, page 1 jumps to page 2, and you can use the browser back to return to page 1.
  • Using the replace method, page 1 is replaced with page 2, and you use the back of the browser. At this time, you can't go back to page 1, and you can only go back to the previous page of page 1, page 0.

When will replace be used?

  • When you don't want the user to go back to the previous page, it is common for permission verification.After verification, the user will not be allowed to go back to the login page to repeat the verification.
router.go(n) method

The parameter of this method is an integer, which means how many steps forward or backward in the history record. Similar to window.history.go(n). In this way, you can control how many steps the page forwards or backwards.

4.2 routes

Routes create configuration items for the vue-router routing instance. Used to configure multiple route routing objects

4.3 route

5. Nested routing

A corresponding display is a component, so there are two main points in implementing nested routing:

  • Define sub-routes in the route object (nested sub-routes)
  • Within the component
    <router-view/>
    usage of.
//The first step: define sub-routes in the route object (nested sub-routes) { path : "/user" , component : User, name : "user" , //Nested routing is written in the children configuration, written in the same way as routes. children : [ { path : "" , //Here let him display in the user view by default component : UserDefault, name : "default" , }, { path : "foo" , component : UserFoo, name : "foo" }, //Write the child routing path directly, at this time path is equivalent to'/user/foo', and the child routing will inherit the path of the parent routing. ], }, //Step 2: Use of <router-view/> in the component. < template > < div > < div > user component </div > < router-link to > default user </router-link > < router-link to = "/user/foo" > foo user </Router-Link > < Router-View > </Router-View > </div > </Template > copy the code

6. Name the view (let the common components of the same level in the routing rules display)

We know that clicking the jump link of the current routing path will display the corresponding routing component, but if we want him to jump and also display his brother non-routing components, we need to add it at the same level as the default routing exit. Named routing outlets for display by non-routing components at the same level. Look at the picture to understand.

7. Route redirection and alias

7.1 Route redirection

Redirection is actually intercepting the path through routing, and then replacing the url to jump to the route specified by redirect. Redirection is done through routes configuration

//Redirect from/a to/b const router = new VueRouter({ routes :[ { path : '/a' , redirect : '/b' } ] }) ///Redirect from/a to the route named'foo ' const router = new VueRouter({ routes : [ { path : '/a' , redirect : { name : 'foo' }} ] }) //Even a method that dynamically returns the redirection target: const router = new VueRouter({ routes : [ { path : '/a' , redirect : to => { //The method receives the target route as a parameter //return the redirected string path/path object const {hash, params, query} = to //ES6 is used here The deconstruction method of to corresponds to the hash mode, params, and query parameters of to. Deconstruction is not explained in detail here. if (query.to === 'foo' ) { return { path : '/foo' , query : null } } if (hash === '#baz' ) { return { name : 'baz' , hash : '' } } if (params.id) { return '/with-params/:id' } else { return '/bar' } }} ] }) Copy code

7.2 Aliases

  • "Redirect" means that when a user visits/a, the URL will be replaced with/b, and then the matching route will be/b. So what is the "alias"?
  • The alias of/a is/b, which means that when a user visits/b, the URL will remain/b, but the route matching will be/a, just like the user visits/a.
  • The alias is that a route has two paths. Both paths can jump to the route.
//The alias is configured in the alias in rutes: const router = new VueRouter({ routes : [ { path : '/a' , component : A, alias : '/b' } ] }) //Use alias <router-link to= "/b" >About</router-link>| Copy code

8. Routing components pass parameters

  • Routing parameters can be passed through the params and query described earlier. The two methods of passing parameters essentially put the parameters on the url and change the url. This will cause the height of the parameters and components coupling.
  • If I want to pass parameters, I can be more free and get rid of the shackles of url. At this time, I can use rute props for decoupling. Improve the reuse of components without changing the url.
//1. The component receives const Hello = { props : [ 'name' ], //When using rute props to pass parameters, the corresponding component must add props to receive, otherwise the pass parameter template is not available at all : '< div>Hello {{ $route.params}} and {{this.name}}</div>' //If this.name has a value, then name has successfully become an attribute of the component and passed the parameter successfully } //2. Routing configuration: const router = new VueRouter({ mode : 'history' , routes : [ //Case 1: No parameters are passed so the component can't get anything { path : '/' , component : Hello }, //Case 2: Boolean mode: props is set to true, at this time route.params (name here) will be set as component properties. { path : '/hello/:name' , component : Hello, props : true }, //Object mode: At this time, it has nothing to do with params. The name at this time will be directly passed to the Hello component. Note: At this time, the props must be static! { path : '/static' , component: Hello,/* Function mode:props : { name : 'world' }}, 1.This function can accept one parameter by default, which is the current route object. 2.What this function returns is an object. 3.In this function, you can process static values and routing-related values. */ { path : '/dynamic/:years' , component : Hello, props : dynamicPropsFn }, { path : '/attrs' , component : Hello, props : { name : 'attrs' }} ] }) function dynamicPropsFn ( route ) { return { name : ( new Date ().getFullYear() + parseInt (route.params.years)) + '!' } } new Vue({ router, el : '#app' }) <!--html part--> < div id = "app" > < h1 > Route props </h1 > < ul > < li > < router-link to = "/" >/</router-link > </li > < li > < router- link to = "/hello/you" >/hello/you </router-link > </li > < li >< router-link to = "/static" >/static </router-link > </li > < li > < router-link to = "/dynamic/1" >/dynamic/1 </router-link > </li > < li > < router-link to = "/attrs" >/attrs </router-link > </li > </ul > < router-view ></router-view > </ div > copy code

9. Routing lazy loading

  • Vue is mainly used for single-page applications. At this time, webpack will pack a large number of files, which will cause too many resources to be loaded on the home page, and the first screen time is too long, giving users an unfriendly experience.
  • If you use routing lazy loading, only load the relevant page when you route the jump. In this way, the home page loads less things and the first screen time is also reduced.
  • The lazy loading of vueRouter mainly relies on Vue's asynchronous components and Webpack's code splitting function to easily realize the lazy loading of routing components. You only need to introduce the component in the form of a promise.
{ path : "/about" , name : "About" , alias : "/a1111" , component : () => import ( "../views/About.vue" ), }, or const Foo = () => import ( './Foo.vue' ) routes : [ { path : '/foo' , component : Foo } ] //Or group components into blocks const Foo = () => import ( /* webpackChunkName: "group-foo" */ ' ./Foo.vue' ) const Bar = () => import ( /* webpackChunkName: "group-foo" */ ' ./Bar.vue' ) const Baz = () => Import ( /* webpackChunkName: "Group-foo" */ './Baz.vue' ) copying the code

10. Navigation guard (routing hook)

Route navigation guards, in layman's terms, are route hooks. The role is also similar to life cycle hooks, which are used for operation control during the route jump process. The route guard is generally used for some verification of route jumps, such as login authentication (no login can not enter the individual Hub page) etc.

10.1 Classification of navigation guards

  • 1. Global front guard
    beforeEach
    : When a navigation is triggered, the global front guards are called in the order of creation. The guard is parsed and executed asynchronously. At this time, the navigation is waiting until all guards are resolved.
//1. It can be set in main.js or in a separate routing configuration file router.js router.beforeEach( ( to, from , next ) => { ... next(); }); //2, you can also set this inside the component . $router.beforeEach( ( to, from , next ) => { ... next(); }); //3, Detailed instructions on the use of functions and next() router.beforeEach( ( to, from , next ) => { next(); //When using, don't miss writing next!!!, which means to go directly to the next hook. //next(false) interrupt the current navigation //next('/path path') or object form next( (path:'/pathPATH' )) Jump to path routing address //next({path:'/shotcat',name:'shotCat',replace:true,query:{logoin:true}...}) You can add a lot of this kind of object writing. The to prop of router-link and the options in router.push (see the official api documentation for details) can all be added, and then explain, replace: true means replacement The current routing address is often used for routing modification after permission judgment. //The usage of next(error), (requires 2.4.0+) }).catch( ()=> { //Jump failed page next(( path : '/error' , replace : true , query : { back : false }}) }) //If you want to jump and report an error, you can use router.onError() router.onError( callback => { console .log( ' Something went wrong!' , callback); }); Copy code
  • 2. Global resolution guard
    beforeResolve
    Before the navigation is confirmed, and after the guards and asynchronous routing components in all components are parsed, the parsing guard is called.
  • 3. Global rear guard afterEach: These hooks will not accept the next function and will not change the navigation itself:
  • 4. Route exclusive guard: you can directly define the beforeEnter guard on the route configuration:
const router = new VueRouter({ routes : [ { path : '/foo' , component : Foo, beforeEnter : ( to, from , next ) => { //... //The usage is the same as beforeEach above } } ] }) Copy code
  • 5.Guards in the component:
    beforeRouteEnter
    ,
    beforeRouteUpdate (New in 2.2)
    ,
    beforeRouteLeave
    You can directly define routing navigation guards in the routing component:
const Foo = { template : `...` , beforeRouteEnter ( to, from , next ) { //Called before the corresponding route for rendering this component is confirmed//No! can! Get the component instance `this` //Because the component instance has not been created before the guard is executed //However, you can access the component instance by passing a callback to next. The callback is executed when the navigation is confirmed, and the component instance is used as the parameter of the callback method. next( vm => { //Access component instance through `vm` }) }, beforeRouteUpdate ( to, from , next ) { //Called when the current route is changed, but the component is reused //For example, for a path with dynamic parameters/foo/:id, in/foo/1 and When jumping between/foo/2, //the same Foo component will be rendered, so the component instance will be reused. And this hook will be called in this case. //Can access the component instance `this` this .name = to.params.name }, beforeRouteLeave ( to, from , next ) { //Called when the navigation leaves the corresponding route of the component //Leave the current route, at this time it can be used to save data, or initialize the data, or close the timer, etc. //You can access the component instance `this` const answer = window .confirm( 'Do you really want to leave? you have unsaved changes!' ) if (answer) { next() } else { next( false ) } } } Copy code

10.2 Complete navigation analysis process

Navigation is triggered. Call the beforeRouteLeave guard in the deactivated component. Call the global beforeEach guard. Call the beforeRouteUpdate guard (2.2+) in the reused component. Call beforeEnter in the routing configuration. Resolve asynchronous routing components. Call beforeRouteEnter in the activated component. Call the global beforeResolve guard (2.5+). Navigation is confirmed. Call the global afterEach hook. Trigger DOM update. Call the callback function passed to next in the beforeRouteEnter guard, and the created component instance will be passed in as the parameter of the callback function. Copy code

10.3 Navigation guard parameters

Each guard method receives three parameters:

  • to: Route: the target route to be entered
  • from: Route: The route object that the current navigation is about to leave
  • next: Function: This method must be called to resolve this hook. The execution effect depends on the call parameters of the next method.
  • next(): Go to the next hook in the pipeline. If all hooks are executed, the status of the navigation is confirmed.
  • next(false): Interrupt the current navigation. If the URL of the browser changes (perhaps manually by the user or the browser back button), the URL address will be reset to the address corresponding to the from route.
  • next('/') or next({ path:'/' }): Jump to a different address. The current navigation is interrupted, and then a new navigation is performed. You can pass any location object to next, and you are allowed to set options such as replace: true, name:'home', and any options used in router-link to prop or router.push.
  • next(error): (2.4.0+) If the parameter passed to next is an Error instance, the navigation will be terminated and the error will be passed to the callback registered by router.onError().

11. Routing Meta Information

When connecting to the route configuration, add a custom meta object to each route. Some states can be set in the meta object to perform some operations. It is perfect for login verification. To be elegant and implicitly convey information, use meta objects!

const router = new VueRouter({ routes : [ { path : '/foo' , component : Foo, children : [ { path : 'bar' , component : Bar, //a meta field meta : { requiresAuth : true } } ] } ] }) Copy code
/* $route.matched: An array containing routing records of all nested path fragments of the current route */ router.beforeEach( ( to, from , next ) => { if (to.matched.some( record => record.meta.requiresAuth)) { //Array some method, if meta.requiresAuth is true, return true. At this point, it means that you need to determine whether the user is logged in before entering the route if (!auth.loggedIn()) { //If not logged in, jump to the login page next({ path : '/login' , query : { redirect : to.fullPath} //This small detail of the official example is very good. Save the routing path to be redirected through query. After logging in, you can directly get the path. Jump directly to the route to go before logging in }) } else { next() } } else { next() //make sure to call next() } }) Copy code
  • We can determine whether login verification is required through the status set in the meta. If the requiresAuth in meta is true, then you need to determine whether you are already logged in, and then jump to the login page if you are not logged in. If you are logged in, continue to jump.
  • At this point, some students may say that the path, params, and query mentioned above can all store information as a status mark for login verification. Indeed, they can also achieve the same effect. If it is a small number of individual verifications, there is no problem with using them. Big.
  • But what if multiple routes require login verification? Path, params, and query explicitly store the information on the url. And multiple paths add the same state information to the url. This makes the url It's no longer simple, and it's also very inelegant.

So if you want to be elegant and implicitly transmit information, use meta objects!

Recommend the previous article Basic article: Take you to open the door of Vue ( Part 1) Portal

For follow-up articles, see the next one: VueX and Vue-cli

Part 11: VueX

Basic concepts of VueX

state

mutation

getter

action

Used for Vue components

dispatch

commit

mapState

mapGetters

mapActions

mapMutations

Part Twelve: Vue-cli