banner
Chenh

Chenh

Vue

Vue#

1. Introduction to Vue#

### Imperative vs Declarative
- Imperative
	Directly implement each step using native JS
- Declarative
	List what to do first, then automatically generate logic during the compilation process
tip. Sacrifice performance to enhance programming experience.
### Virtual DOM
- The virtual DOM is a JS object abstraction of the real DOM. The virtual DOM is the JS object itself.
- The real DOM has many properties, and directly manipulating the DOM puts too much pressure on the browser's retrieval, leading to insufficient performance. Therefore, the virtual DOM is used, and changes in the virtual DOM (diff) are mapped to operations on the real DOM to improve performance.
- In Vue, the components of the virtual DOM are VNodes (JS objects), and each VNode has children - an array of VNodes, forming a tree structure, which is the virtual DOM tree.
- VNode.elm points to the real Node, and VNode.context points to the Vue instance.
### Runtime vs Compile Time
- Runtime
	- Uses the render function to directly convert the virtual DOM to the real DOM
	- There is a certain performance overhead at runtime
	- The structure of the virtual DOM is similar to that of the real DOM, and it is a direct mapping relationship, making it impossible to analyze user-provided content.
- Compile Time
	- Parses the Template content and converts it to the real DOM
	- The compilation process can read template syntax and analyze user content.
	- Better performance
- Runtime + Compile Time Vue
	- Uses the template to generate the render function
	- Uses the render function to convert the virtual DOM to the real DOM
### Vue UI
- Template description is declarative
- The render function is imperative; the return value of the render function (h function) is a virtual DOM (VNode)
### Initial Renderer
- The renderer object is generated using the createRenderer function
- Parameters: virtual DOM object (VNode) and mount container (real DOM element)
- The role of the renderer is to mount the virtual DOM on the real DOM element
### Distinction
- Render is the way Vue provides to users to operate the virtual DOM
- Renderer is the tool Vue uses to convert the virtual DOM to the real DOM

2. Reactive System#

### Side Effect Functions and Reactive Data
1. There is a global variable A, which is associated with the function innerText and the view.
2. When we modify variable A, the associated elements do not update.
3. Only by executing innerText again will the view update.
- We call innerText a side effect function.
- The variable A that involves view changes is reactive data.
### Core Logic
- Must have setter and getter / accessors
- Similar to accessors in high-level languages, both virtual DOM objects and data objects can use accessors to implement data binding patterns similar to high-level languages.
### Basic Principles
- Getter: Reactive data
	When a read operation is triggered, the side effect function is added to the "list."
- Setter: Reactive data
	When a set operation is triggered, the side effect function is executed again to update the view.
### Implementation Principles
- In Vue2, Object.defineProperty() is used.
- In Vue3, Proxy() and Reflect are used.
### Reactive Scheduling
- When reactive data changes, the "list" of side effect functions is executed, making it important to control the execution order of the "list."
- The key to reactive scheduling lies in asynchronous programming and the Promise queue model jobQueue.
- **Lazy Execution**
- **Computed Properties** also rely on the scheduling system.
- Watch listeners execute callbacks when the listened object changes.
- If there are asynchronous operations in the listener's callback, it may lead to unexpected results; use onInvalidate() to stop expired asynchronous requests.
### Implementation
- Object reactivity: Proxy + Reflect
- Non-object reactivity (basic types): ref

3. Rendering#

4. Component Principles#

5. Compiler#

### DSL Compiler
- A specific language compiler
- Essentially compiles the template into a render function
1. parse template -> AST object
2. transform AST -> Javascript AST
3. generate Js AST -> render function

Vue-Basics#

Template Syntax#

  1. Insert reactive text in tags using {{ msg }}
  2. Set reactive data for attributes using v-bind:id="" or shorthand :
  3. If the data bound in 2 is of boolean type, Vue will handle the existence or non-existence of that attribute.
  4. Using the v-bind="" syntax without parameters allows binding a JSON object with multiple attributes to a tag.
  5. All data bindings in Vue support JS expressions
  6. You can use a method exposed by a component, which will be called every time the component updates, so it should not produce any side effects, such as changing data or asynchronous operations.
  7. Expressions in templates can only access a limited list of global objects**, which can be explicitly added in app.config.globalProperties.
## Directives
- Special attributes prefixed with `v-`
- The expected value of a directive is a JS expression used to respond to updates when the expression changes
- `v-bind:href="xxx.api"` can be followed by directive parameters
- `:[attributeObj]="xxx.api"` parameters can be dynamic, but the expression needs to be enclosed in square brackets
- **HTML characters will be automatically processed to lowercase and cannot contain spaces**
- **It is recommended to use computed properties**

directive.69c37117

Reactive Basics#

** Using Composition API **

Creating a Reactive Object or Array#

import { reactive } from 'vue'
const state = reactive({ count: 0 })

Vue3 uses Javascript Proxy to listen to reactive objects.

Use the setup function to define and return the reactive object for template use.

import { reactive } from 'vue'
// Default behavior of the component
export default {
  // setup is the hook function for the Composition API
  setup(){
    // Define a reactive object
    const state = reactive({ count: 0 })
    // Define a method
    function increment(){
      state.count++
      // Global API updates DOM on the next tick
      nextTick(()=>{
        // xxx
      })
    }
    // Expose state to the template
    return {state, increment}
  }
}
<template>
	<button @click="increment">
    {{ state.count }}
  </button>
</template>

Using <script setup>

DOM Updates#

Vue maintains a DOM update sequence, so data modifications and DOM updates are not strictly synchronized.

To ensure updates are complete, you can use nextTick()

Deep Reactivity#

All states in Vue are by default deeply reactive, meaning changes can be detected regardless of how deep the object or array is.

Proxy#

The object processed by reactive() returns a proxy of its original object.

Changing the original object does not trigger reactivity.

Calling reactive() multiple times on the same object will return the same proxy.

Calling reactive() on a proxy will return the proxy itself.

⚠️ Note

  1. reactive() only works on JSON objects and is ineffective on primitive types.

  2. Reactive objects (proxies) should not be arbitrarily replaced (assigned new values), or assigned/destructured to local variables (which will not have reactivity), or passed as parameters to functions (which will not have reactivity, equivalent to destructuring).

Using ref()#

ref(1) creates a reactive proxy of an object that has a value property, which is the referenced primitive type variable.

ref() can also accept an object; in this case, accessing the value property of that reference will automatically get the result of reactive().

When accessed as a top-level property in templates, ref() will automatically unpack, no need for .value.

<script setup>
	import {ref} from 'vue'
  const count = ref(0)
  function increment(){
    count.value++
  }
  const obj = { foo: ref(1) }
</script>
<template>
	<button @click="increment">
    {{ count }}
  </button>
	<button>
    {{ obj.foo + 1 }} <!--[object:object]1 -->
  </button>
</template>

Reactive Computed Properties#

  • Example and usage instructions
<script setup>
  import { reactive, computed } from 'vue'
  const author = reactive({
    name: 'John Doe',
    books: [
      'Vue 2' - 'Advanced Guide',
      'Vue 3' - 'Advanced Guide',
      'Vue 4' - 'Advanced Guide',
    ]
  })
  // Reactive computed property
  const publishedBooksMessage = computed(()=>{
    return author.books.length > 0 ? 'Yes':'No'
  })
</script>
<template>
	<p>
    Has Published books:
  </p>
	<span>{{ author.books.length > 0? 'Yes':'No'}}</span>
	<span>{{ publishedBooksMessage }}</span>
</template>

computed() expects to accept a getter function that returns a computed property ref; Vue will automatically track the expression dependencies, and any changes will be updated in real-time.

  • Computed Properties vs Functions

    Computed properties are cached based on reactive dependencies, while functions are executed when the component re-renders.

  • Writable Computed Properties

    <script setup>
    	import {ref, computed} from 'vue'
      const firstName = ref('John')
      const lastName = ref('Doe')
      
      const fullName = computed({
        get(){
          return firstName.value + ' ' + lastName.value
        },
        set(newValue){
          [firstName.value, lastName.value] = newValue.split(' ')
        }
      })
    </script>
    
  • ⚠️ Note

    1. Getters should not have any side effects, such as asynchronous requests or changing the DOM.
    2. The computed property returns a "temporary snapshot"; when the source changes, a new snapshot will be generated. Changing the computed result is meaningless; computed properties should be treated as read-only.

Class and Style Binding#

Binding Class#

<script setup>
	import { reactive, ref } from 'vue'
  
  const isActive = ref(true)
  const hasError = ref(false)
  
  const classObject = reactive({
    active: true,
    'text-danger': false
  })
  const classComput = computed(()=>({
    active: isActive.value && !hasError.value,
    'text-danger': hasError.value && hasError.value.type ==='fatal'
  }))
</script>
<template>
	<div class="static" :class="{ active: isActive, 'text-danger': hasError }"> Bind ref element </div>
  <div :class="classObject"> Bind object </div>
	<div :class="classComput"> Bind computed property </div>
	<div :class="[isActive ? activeClass : '', errorClass]"> Bind object array </div>
</template>

Binding Style#

<script setup>
	import { reactive, ref } from 'vue'
  
  const activeColor = ref('red')
  const fontSize = ref(30)
  
  const styleObject = reactive({
    color: 'red',
    fontSize: '13px'
  })
</script>
<template>
	<div :style="{ color: activeColor, fontSize: fontSize + 'px'}"> Camel case style element </div>
	<div :style="{ 'font-size': fontSize + 'px' }"> Hyphen style actual style-key name </div>
	<div :style="styleObject"> Bind object </div>
	<div :style="[baseStyles, overridingStyles]"> Bind array </div>
</template>

Vue will automatically add style prefixes based on browser support, and when multiple values are provided for a property, it will automatically select the value suitable for the current environment.

Conditional Rendering#

<script setup>
import { ref } from 'vue'

const awesome = ref(true)
</script>

<template>
  <button @click="awesome = !awesome">toggle</button>
	<template v-if="awesome">
    <h1>Vue is awesome!</h1>
    <h1>Oh no 😢</h1>
  </template>
</template>

Conditional rendering with v-if, v-else-if, v-else supports usage on template tags.

v-show will have a similar effect but does not support usage on template tags.

v-show only changes the display property; the element still exists in the DOM.

v-if has a higher toggle overhead, while v-show has a higher initial rendering overhead.

⚠️ When v-if and v-for coexist, v-if takes precedence. It is not recommended.

Loop Rendering#

<script setup>
	import { reactive, ref } from 'vue'
  const items = ref([{message: 'foo'}, {message: 'bar'}])
  const object = reactive({
    title: "how to do lists in Vue",
    author: 'Jane Doe',
    publishedAt: '2016-04-10'
  })
</script>
<!-- v-for is available in templates -->
<template v-for="it in 1">
	<li v-for="n in 10">{{n}}</li>
	<!-- In repeated elements, you can fully access the properties and variables of the parent scope; item is only accessible internally -->
	<li v-for="item in items">{{item.message}}</li>
	<!-- Iterate over an object, returning all properties of the object, ordered based on Object.keys -->
	<li v-for="value in object">{{value}}</li>
	<!-- Iterate over an object, returning two/three parameters -->
	<li v-for="(value, key, index) in object">{{key}}:{{value}}</li>
</template>

⚠️ Vue defaults to in-place updates to improve performance. In this case, changing the order of the list will not change the rendering order.

By adding a :key attribute to the loop elements, this behavior can be changed.

⚠️ Vue detects array operation functions to respond to changes in the array. However, some functions do not change the original array but return a new array; in this case, Vue will not detect the change.

Event Handling#

  • Inline Event Handlers

    The inline JavaScript statement executed when the event is triggered.

  • Method Event Handlers

    A property name or path pointing to a method defined on the component.

** Inline Event Handling **

<script setup>
	// ...
  const count = ref(0)
</script>
<template>
	<button @click="count++">
    Add 1
  </button>
	<p>
    Count is: {{ count }}
  </p>
</template>

** Method Event Handling **

<script setup>
	const name = ref('world!')
  
  function greet(event){
    alert('Hello ${name.value}')
    // event is the native DOM event
    if(event){
      // Use event.target to get the native event object
      alert(event.target.tagName)
    }
  }
</script>
<template>
	<!-- greet -->
	<button @click="greet">
    Greet
  </button>
</template>

⚠️ It can be confusing to distinguish between inline and method events.

You can use $event to pass the native DOM event.

  • Event Modifiers
    1. .stop
    2. .prevent
    3. .self
    4. .capture
    5. .once
    6. .passive
  • Key Modifiers
  • System Key Modifiers
    • .exact controls key combinations; excess key combinations will not trigger.
  • Mouse Button Modifiers

Form Handling#

** Binding data for input components can be cumbersome **

<input :value='text'
       @input="event => text=event.target.value">

Simplifying with v-model#

<input v-model="text">

⚠️ v-model will automatically handle different HTML tag input properties.

⚠️ v-model will ignore input during the spelling phase; to cancel this behavior, please use the input event handling method.

Single Binding and Array Binding for Checkboxes#

<!-- ☑️ true 🔲 false -->
<!-- :true-value :false-value -->
<input type='checkbox' v-model="checked"/>
<label for="checkbox">{{ checked }}</label>
<!-- The value of the selected items will be added to the array -->
<script setup>
	const checkNames = ref([])
</script>
<template>
	<div>
    Checked Names: {{ checkNames }}
  </div>
	<input type="checkbox" value="Jack" v-model="checkNames">
	<label for="jack">Jack</label>
	<input type="checkbox" value="Tom" v-model="checkNames">
	<label for="tom">Tom</label>
	<input type="checkbox" value="Mike" v-model="checkNames">
	<label for="mike">Mike</label>
</template>

Radio Button Group#

<div>
  Picked: {{picked}}
</div>
<input type='radio' value="One" v-model="picked"/>
<input type='radio' value="Two" v-model="picked"/>
<!-- Vue uses the value to distinguish different buttons -->

Selector#

<div>Selected: {{ selected }}</div>

<select v-model="selected">
  <!-- It is recommended to provide a disabled option with an empty value -->
  <option disabled value="">Please select one</option>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>

⚠️ Binding to an array allows for multiple selections.

💡 Using v-for to loop render options is more convenient.

  • The above three components can use v-bind to bind dynamic data.
  • Modifiers
    1. .lazy changes input trigger to change trigger.
    2. .number automatically converts to a number; when type='number', it will be automatically enabled.
    3. .trim automatically removes leading and trailing spaces.

Component Lifecycle Hooks#

At various stages of a component's lifecycle, users can define hook functions to perform specific actions.

lifecycle.16e4c08e

Watchers#

Basic Usage#

<script setup>
	import {ref, watch} from 'vue'
  const question = ref('')
  const answer = ref('Questions usually contain ...')
  const myobj = reactive({ count: 0 })
  
  watch(question, async(newvalue, oldvalue)=>{...})
  // Note: For a reactive object, you cannot watch its members because you get a value
  // You should use a Getter function
  watch(()=>myobj.count,(count)=>{ ... })
</script>

Deep Watching#

When watching a reactive object (the first parameter of the watch function is an object), deep watching is automatically enabled; any member updates of the object will trigger actions. However, since the watched object remains the same, the callback function's newval, oldval remains unchanged.

Watching a getter of an object will only trigger when the object itself changes.

<script setup>
  import { reactive } from 'vue'
	const obj = reactive({ count:0 })
  watch(obj,()=>{
    // Automatically enable deep watching
  })
  watch(()=>obj,(newv,oldv)=>{
    // Only returns a different obj object will trigger.
  },{deep:true/* Force deep watching */})
</script>

Immediate Execution#

The watcher will only trigger when the data source changes by default.

If you want to trigger it immediately after creation, you can pass the immediate: true parameter.

watchEffect()#

Using this function, you can trigger reactivity without providing a reactive data source; Vue will automatically trigger based on the dependencies of the callback function. Additionally, this function defaults to having the immediate: true property.

<script setup>
	const todoId = ref(1)
  const data = ref(null)
  
  watch(todoId, async()=>{
    const response = await fetch(
    	'https://xxx.xxx.com/api/todos/${todoId.value}'
    )
    data.value = await response.json()
  },{immediate:true})
  // Equivalent to
  watchEffect(async()=>{
    // ...
  })
</script>

😄 When the callback depends on many data points, the effect of watchEffect becomes very significant. Additionally, this function listens to object properties at a surface level, not deeply.

⚠️ In reactive updates, user listener callbacks are by default executed before DOM updates, so the DOM accessed during the callback is not the latest. To change this behavior, attach the flush:'post' property. Or use the post-refresh listener function watchPostEffect().

⚠️ Typically, watchers created in <script setup> will automatically bind to the component upon creation and stop automatically upon component unmounting. However, watchers created with asynchronous statements need to be manually stopped to prevent memory leaks. Use the return value of watch/watchEffect to stop a watcher.

Template Refs#

Template refs can help you access the underlying DOM elements.

Accessing Template Refs#

<script setup>
	import {ref, onMounted} from 'vue'
  
  // Declare a ref to store the template reference
  const input = ref(null)
  onMounted(()=>{
    input.value.focus()
  })
</script>
<template>
	<!-- The ref name must match the variable name in the script. -->
	<input ref="input"/>
</template>

⚠️ The template ref will only have a value after the component is mounted. Initially, during the first render, the reference is null. If you want to listen to a template ref, you must consider its null state.

v-for Template Refs#

Declare a reference type array to store all elements of the list.

⚠️ The ref array cannot guarantee the same order as the original array.

Function Template Refs#

<input :ref="(el)=>{ ... }"/>

Function templates will trigger when the component updates and is destroyed. During destruction, el===null.

Components#

Defining Components#

  • When using build steps, Vue components are defined in a .vue file, known as Single File Components (SFC).

    <script setup>
    	import { ref } from 'vue'
      const count = ref(0)
    </script>
    <template>
    	<button @click="count++">
        You clicked me {{ count }} times.
      </button>
    </template>
    
  • When not using build steps, a Vue component is represented by a specific JavaScript object.

    <script>
    	import {ref} from 'vue'
      export default {
        setup() {
          const count = ref(0)
          return { count }
        },
        template: '
        	<button @click="count++">
        		You clicked me {{count}} times.
      		</button>'
      }
    </script>  
    

Using Components#

By using <script setup>, imported components can be used directly in the template.

Globally registered components can be made available throughout the entire Vue App.

To use a component, simply create a tag with the same name as the component.

⚠️ In Vue files, component tags can be case-sensitive, but if directly embedded in an HTML file, they follow browser parsing rules.

Props#

Props are akin to "parameters" for components. When calling a component, different content can be displayed based on the passed props.

Use the defineProps macro to define component Props.

Composition Style

<script setup>
	defineProps(['title'])
</script>
<template>
	<h4>
    {{ title }}
  </h4>
</template>

Options Style

export default {
  props: ['title'],
  // Props must be passed as parameters to setup
  setup(props) {
    console.log(props.title)
  }
}

Listening to Events#

To allow a child component to trigger an event that is received by the parent component, you need to:

  1. Use the macro defineEmits(['enlarge-text']) to declare the event list.
  2. Write the same-named event trigger in the component @click="$emit('enlarge-text')"
  3. Write the event trigger callback in the parent component @enlarge-text=" ... "

Similar to props, using options style to declare the event list, and the second parameter of the setup function, ctx context can access the ctx.emit event list.

Slots#

Slots allow our custom components to pass content like regular HTML components.

<script setup>
	import AlertBox from '/alertbox.vue'
</script>
<template>
	<AlertBox>
  	The Slot Message.
  </AlertBox>
</template>
<!-- Here is the alertbox.vue child component -->
<template>
	<slot/>
</template>

Dynamic Components - Implementing Component Switching#

#DOM Parsing ⚠️#

Vue Advanced#

Component Registration#

Global Registration#

<!-- App.vue -->
<script>
	import { createApp } from 'vue'
  import MyComponent from './mycomponent.vue'
  const app = createApp({})
  // 1. Direct registration
  app.component('componentName', {/* component implementation */})
  // 2. Single file registration
  app.component('componentName', MyComponent)
  // 3. Chained calls
  app
    .component('componentName', MyComponent)
    .component('componentName', MyComponent)
    .component('componentName', MyComponent)
</script>

Local Registration#

  1. Components imported in the <script setup> tag can be used directly.
  2. When using options syntax, you need to explicitly register in components:{ nameA, nameB }.

Component Name Format#

  1. PascalCase is the recommended component name format; in single file components, this name can also distinguish well from regular HTML tags.
  2. In DOM templates, please use the corresponding kebab-case name.
  3. Vue will automatically handle the conversion of the two formats and usage scenarios.

Props#

Declaring Props#

  1. In <script setup>, use defineProps(['prop1,prop2']).
  2. In options style, use props: ['prop1','prop2'].
  3. Use a JavaScript object format with type names.
<script setup>
	defineProps({
    title: String,
    likes: Number
  })
  export default {
    props: {
      title: String,
      likes: Number
    }
  }
</script>

Writing Format#

  1. Using camelCase naming for Props allows direct use of attributes in the template without quotes.
  2. In the template, please use kebab-case format to unify with DOM writing style.
  3. Vue will automatically handle the correspondence of different writing styles.

Vuex#

** A store for storing global state. All states stored within are reactive. **

** The state stored cannot be changed directly; it can only be changed by committing mutations. **

Example#

import { createApp } from 'vue'
import { createStore } from 'vuex'

const store = createStore({
  state(){
    return {
      count: 0
    }
  },
  mutations: {
    increment(state){
      state.count++
    }
  }
})
const app = createApp({})
app.use(store)
// Use store.commit('name') to trigger state changes
// Use store.state to access the object
// In all Vue components, use this.$store to access the store instance (options style)
// In setup, import useStore and use it to access the store. (composition style)

Concepts#

  • localStorage

    • A local storage feature supported by H5, solving the problem of insufficient cookie space.
    • Can only store string data.
    • .getItem('key')
    • .removeItem('key')
    • .clear()
    • .key()
  • State

    • Equivalent to a centralized, global reactive database; state is the name of these reactive variables.
    • When calling the state in a component, to maintain reactivity, it should be accessed using arrow functions.
    • Using mapState() can return multiple states, and it can also return computed states in a function form. Inside the function, this can be used to access local component variables.
    • Using **object spread operator ...mapState can mix the returned object into the upper-level properties.
  • Getter

  • Mutation

    • The only way to change the state. By using commit, the mutation method changes the state.

    • The mutation is an object containing multiple functions, each function having different state change operations.

    • The first parameter of the mutation function is the state.

    • ⚠️ The functions defined in the mutation can only be triggered by the commit method; the first parameter of the commit method is the mutation function name, and subsequent parameters can be passed to the mutation function. In function definitions, the first parameter is usually the state, and subsequent parameters correspond to the second and later parameters of the commit.

    • The submission method can be an object. Writing the second parameter in the form of a JSON object will submit the entire object as a payload to the mutation function. When using object style, the function name can be added as a type property to the JSON object for overall submission.

    • Mutation must be synchronous functions.

    • ⚠️ Using constants instead of mutation event names is a development pattern.

      // mutation-types.js
      export const SOME_MUTATION = 'SOME_MUTATION'
      // store.js
      import { createStore } from 'vuex'
      import { SOME_MUTATION } from './mutation-types'
      const store = createStore({
        state:{...},
        mutations: {
          [SOME_MUTATION](state){...}
        }
      })
      
    • ⚠️ mapMutations is a method to improve code readability, and it is equivalent to direct calls.

      import { mapMutations } from 'vuex'
      export default {
        methods: {
          ...mapMutations([
            'increment',
            // => 'increment'=$store.commit('increment')
            add: 'increment',
          ])
        }
      }
      
  • Action

    • Can handle asynchronous functions for mutations.
  • Module

    • Splits the store into different modules when using createStore({modules:{}}) to create the store.

Vue Router#

** Simple Example **

<p>
  <router-link to="/">GO to home</router-link>
  <router-link to="/about">Go to About</router-link>
</p>
<router-view>Route outlet</router-view>

Using router tags instead of HTML hyperlink tags delegates the navigation to the router.

Using the router-view tag receives the routing processing results. It can display components or other actions.

// 1. Example component
const Home = {template: '<div>Home</div>'}
const About = {template: '<div>About</div>'}
// 2. Define the route object
// Each route object contains a path and a component
const routes = [
  {path:'/', component: Home},
  {path:'/about', component: About},
]
// 3. Create a router instance and set the routing configuration.
const router = VueRouter.createRouter({
  history: VueRouter.createWebHashHistory(),
  routes, // => routes: routes
})

// Mount the router to the vueApp
const app = Vue.createApp({})
app.use(router)
app.mount("#app")

The router is now enabled. Inside components, you can access the current route with this.$router.

In the setup function, use useRouter() and useRoute() to get the router instance.

Dynamic Route Matching#

Scenario: When displaying a user’s homepage, dynamic fields need to pass the user ID.

  • Use :id colon + path parameter.
  • At this point, the path parameter will be stored in this.$route.params.
  • Additionally, $route.query will store parameters in the URL (e.g., ?parameter).

Route Parameter Response#

Scenario: When transitioning from /users/a to /users/b, the same component will be reused, improving performance, but the component will not be rebuilt, and related functions will not be called again.

In this case, the page needs to respond to changes in route parameters.

  • Use watch to listen to this.$route.params.
  • Use async beforeRouteUpdate(to, from) navigation guards.

Regular Expressions#

Regular route parameters will only match characters between /.

If you want to escape the delimiter restrictions, you can use custom parameter regex.

const routes = [
  {path:'/:pathMatch(.*)*'}
]

The (...) after the route parameter is a regular expression; the string matching the expression is assigned to the parameter pathMatch, and the trailing * indicates optional repetition.

Advanced Matching#

...

Route Matching Syntax#

Typically, using static routes /about and a dynamic route ending with a parameter /users/:userId can meet business needs.

Vue-router additionally supports

Custom Regex for Parameters#

Scenario: The following two routes will match the same URL /:orderId /:productName. To differentiate, there are the following methods:

  • Add static parts in front.

    const routes = [
      {path: '/o/:orderId'},
      {path: '/p/:productName'}
    ]
    
  • Use regular expressions to modify parameter matching rules.

    const routes = [
      // Note the escape character; pure numbers at the end are matched to orderId
      {path: '/:orderId(\\d+)'},
      {path: '/:productName'},
    ]
    

Repeatable Parameters#

  • * indicates that it can match 0 or more routes (segments surrounded by /).
  • + indicates that it can match 1 or more routes.
  • ? indicates that it can match 0 or 1 route.
  • At this point, the parameter received is no longer a string but an array.
  • It can be used together with regular expressions.

Configuring Case Sensitivity and Strict Mode#

By default, /users, /users/, /Users/ are the same route.

Strict mode will exclude case 2, and sensitive mode will exclude case 3.

const router = createRouter({
  // ... 
  routes: [/* ... */],
  strict: true,
  sensitive: true,
})

Named Routes#

Allows adding a name property to route objects to utilize the feature of automatically constructing URLs.

Named routes can prevent manual URL input errors.

<template>
	<router-link :to="{ name: 'user', params: { username: 'erina' }}">
  User
	</router-link>
</template>
<script>
	router.push({ name: 'user', params: { username: 'erina' } })
</script>

Nested Routes#

  • When a child component has a <router-view> tag, nested routes can be set.
  • That is, the route sets a children: property containing an array of routes.
  • An empty path route object can be set for nested routes to still render appropriate content for the parent route path.

Programmatic Routing#

In addition to using <router-link> tags in templates, navigation can also be implemented in script by accessing the this.$router instance.

Navigation#

Use the method router.push to add a new record to the history stack.

<router-link :to="..."> is equivalent to router.push().

  • Several usages of the push method

    // String path
    router.push('/users/eduardo')
    
    // Object with path
    router.push({ path: '/users/eduardo' })
    
    // Named route with parameters, allowing the route to construct the URL
    router.push({ name: 'user', params: { username: 'eduardo' } })
    
    // With query parameters, resulting in /register?plan=private
    router.push({ path: '/register', query: { plan: 'private' } })
    
    // With hash, resulting in /about#team
    router.push({ path: '/about', hash: '#team' })
    
  • ⚠️ The path parameter will ignore params, so please use named routes.

    const username = 'eduardo'
    // We can manually construct the URL, but we must handle encoding ourselves
    router.push(`/user/${username}`) // -> /user/eduardo
    // Likewise
    router.push({ path: `/user/${username}` }) // -> /user/eduardo
    // If possible, use `name` and `params` to benefit from automatic URL encoding
    router.push({ name: 'user', params: { username } }) // -> /user/eduardo
    // `params` cannot be used with `path`.
    router.push({ path: '/user', params: { username } }) // -> /user
    
  • ⚠️ params accepts string or number (repeatable parameters accept arrays); undefined, false, etc., will be automatically serialized. Optional parameters can be skipped with "".

  • router.push returns a Promise.

Replacing the Current Position#

The action is similar to push, but it does not add a record to the history.

<router-link :to="..." replace> is equivalent to router.replace().

It is equivalent to router.push({path:'...', replace: true}).

Cross History#

// Move forward one record, equivalent to router.forward()
router.go(1)

// Return one record, equivalent to router.back()
router.go(-1)

// Move forward 3 records
router.go(3)

// If there are not that many records, it fails silently
router.go(-100)
router.go(100)

Named Views#

Scenario: So far, a single route's Component only has one. If our routing target needs to place multiple components in different positions, multiple route outlets and multiple components are needed.

Use named views and specify different components for different views in the route object.

<template>
  <router-view class="view left-sidebar" name="LeftSidebar"></router-view>
  <router-view class="view main-content"></router-view>
  <router-view class="view right-sidebar" name="RightSidebar"></router-view>
</template>
<script></script>

SCSS#

  • Needs a compiler to compile into CSS files to be recognized by the browser. It compensates for the repetitiveness of CSS files and allows programmers to define variables, functions, etc.

Variables#

  • Variables start with $, and underscores and hyphens are indistinguishable.
  • Variables have different scopes based on their definition position.
  • Defining the same variable multiple times is equivalent to an assignment operation, which will override.

Selectors#

  • Nested Selectors

  • Parent Selector &

  • Nested Combined Selectors , > + ~

    .container ul{
      border: 1px solid #aaa;
      list-style: none;
      
      li{
        float:left;
        
        >a{
          display: inline-block;
          padding: 6px 12px;
        }
      }
      
      &:after {
        display: block;
        content: "";
        clear: both;
      }
    }
    
  • Nested Properties

    li{
      border: 1px solid #aaa {
        left: 0;
        right: 0;
      }
    }
    

Importing Files#

  • @import App2.scss imports a sass file.
  • Using the !default variable suffix declares a default value, meaning it will not replace variables with the same name.
  • The import behavior is equivalent to inserting the file at the import location.
  • Sass content can be imported locally.
  • @import 'app2.css' uses native CSS import.

Comments#

  • /* */ will be retained in the CSS file.
  • / / will not be retained.

Mixins (Functions)#

  • Use @mixin to declare a mixin.

    @mixin get-border-radius($border-radius, $color:red){
      -moz-border-radius: $border-radius;
      -webkit-border-radius: $border-radius;
      border-radius: $border-radius;
      color: $color;
    }
    
  • Use @include to use the mixin.

  • Mixins can use any SCSS code.

Inheritance#

  • % starts a defined style to be inherited.

  • Use @extend to complete the inheritance.

  • Inheritance differs from mixins in that it can inherit existing CSS rules.

    .container {
      @extend %border-style;
      color: red;
    }
    .container1 {
      @extend .container;
    }
    

Calculations#

  • SCSS can use standard arithmetic operations + - * / %.
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.