/* * @Author trsoliu/[email protected] * @Contact wechat:trsoliu * @Date 2021-04-04 * @LastEditTime: Vue3.0 + Vite2.0 + Vant +TypeScript principles and practices * @link https://juejin.cn/user/4248168659433607 * @ making https://github.com/trsoliu/vue3.0-vite2.0-vant-TypeScript * / demo source code
Copy the code

preface

This article assumes that the reader has a knowledge base related to VUe2.0 and has some practical project experience.

Why learn Vite?

With the development of VUE, it has become one of the three mainstream frameworks, which is even more popular in China and has become a necessary skill for job hunting. With the release of VUE3, vite2.0, a new development tool, has also come into the sight of developers.

If Webpack is a martial arts master, Vite is a prodigy with a unique skill.

Faster, faster, faster is the word I’ve seen most praised for Vite2.0 lately. Vite2.0 will most likely be the next generation of packaging tools (rollup/ Parcel /webpack, etc. ->vite) in ESM +esbuild(Go) on the basis of commonJS+ Nodejs (js) based on webpack dimension reduction blow, and Vite embrace most of the same type of Vue framework.

Vite and Webpack basic working process and principle

webpack

Webpack Dev Server needs to be built for cold startup and hot update, and the build process is time-consuming. The browser loads all the code under the service module on demand according to the route. Therefore, vite will feel much faster than vue-CLI-service serve.

Webpack packaging process

1.Identify entry files (e.g. Index.html);2.Identify module dependencies layer by layer (Webpack will be aware of Commonjs, AMD, or ES6 dependencies)importForm modules for analysis to obtain code dependencies);3.Analyze code => convert code => compile code => output code;4.Form code package;Copy the code

Webpack principle

1.Firstly, the dependency map was constructed by recursive identification step by step.2.Transform code into an AST abstract syntax tree;3.Work with code in the AST phase;4.Turn the AST abstract syntax tree into code that the browser can recognize, and then print it out.Copy the code

Advantages and disadvantages of Webpack versus Vite

Advantages:1.Rich ecology, high maturity; Disadvantages:1.As project iterations and dependency packages grow, the startup time of the development server that captures and builds the application increases.2.Hot update times also increase as the project size increases.Copy the code

vite

Vite is a lighter, faster Web application development tool for modern browsers. Vite Serve (KOA) internally starts the Web Server directly during cold startup and hot update, without compiling all the code files first (distinguishing source code from dependencies).

It is implemented based on the ECMASCRIPT standard native module system (ES Modules). With the support of import/export syntax in modern browsers, and the introduction of esbuild tools written in Go language, vite directly compiled into navtive code running in the browser engine, the browser can load business modules on demand after routing. Run on-demand dependencies against the module code (effectively leaving the browser with the task of packaging the code);

Vite2.0 will be released on 2020/20/18;

Vite packaging process

In the Development environment, the serve is not packaged and only esbuild is used for pre-build dependencies. In the production environment, rollup is used for build (see Webpack).

Vite principle

//index.html
// Declare a script tag of type module in the entry file index. HTML and import the file "main.js"
<script type="module" src="/src/main.js"></script>
// The browser makes a request to the server (vite Server startup port 3000) to get the entry file index.html

//main.js
/ / by entry document request to http://localhost:3000/src/main.js. The main js file.
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

// When the browser requests the main.js file, it detects that there is an import package inside it, and makes an HTTP request to the import reference inside it to obtain the content file of the module
// For example, GET http://localhost:3000/@modules/vue.js
/ / such as: GET http://localhost:3000/src/App.vue
/ / vite's main function is through the browser hijack the requests, and corresponding processing in the back-end will be used in the project file with a simple decomposition and integration, and then returned to the browser, not the packaging on file compiling vite the process, so its running speed than the original webpack development compilation speed a lot!
Copy the code

Pros and cons of Vite versus WebPack

Advantages:1.Millisecond service startup time (< 300ms)2.Module hot replacement time (HMR) at millisecond level (< 100ms);3.True on-demand compilation; Disadvantages:1.Vite's ecology is not as rich as Webpack's;2.Use esbuild only in the development environment, proD environment builds currently use rollup (esbuild is unfriendly to CSS and code splitting etc.);Copy the code

Vite2.0 unpacking

Install vite scaffolding

# has been abandoned
npm install -g create-vite-app
# or 
# has been abandoned
yarn add -g create-vite-app
Copy the code

Initialize the project

// Initialize the Settings to vue TS template
npm init @vitejs/app <project-name>  --template vue-ts
cnpm install
// Important dependencies
cnpm install vuex@next --save
cnpm install vue-router@4 --save
cnpm install vuex-persistedstate --save
cnpm install axios --save
cnpm install less --save
// Reset the browser style, the introduction of vant can not add
cnpm install normalize.less --save
// Introduce the third-party UI library vant
cnpm install vant@next --save
// Progress bar during route loading
cnpm install nprogress --save
//@vite --------------
// Can't find module "path" or its corresponding type declaration
cnpm install @types/node --save-dev
cnpm install @vitejs/plugin-vue --save-dev
cnpm install @vitejs/plugin-vue-jsx --save-dev
// Solve the less import problem, third party UI library styles are loaded on demand
cnpm install vite-plugin-style-import --save-dev
// Automatically add CSS prefix plugins
cnpm install autoprefixer --save-dev 
//px2vw
cnpm install postcss-px-to-viewport --save-dev
// Browser compatible
cnpm install @vitejs/plugin-legacy --save-dev
/ / compress reference documentation: https://github.com/anncwb/vite-plugin-compression
cnpm install vite-plugin-compression --save-dev

Copy the code

Matters needing attention

  • 1. Node version >= 12.0.0;
  • 2. Run the command on CNPMcnpm init @vitejs/app project-name

The default project name is init;

  • 3. In the domestic network environment, YARN, NPM, CNPM or CNPM fast (except private server);
  • 4.Vue. Js devTools uses the variables used in the beta version/template. Make sure to define the variables first, otherwise the page will have a lot of Vue WARN stuck.

Vite2.0 environment variables and patterns

The environment variable

Vite exposes environment variables on a special import.meta.env object;

// Universal built-in variables
// Equivalent to process.env.node_env
import.meta. Env. MODE: {string} The [MODE] on which the application is running (HTTPS:/ / cn. Vitejs. Dev/guide/env - and - mode. Html# modes).

import.meta.env.BASE_URL: {string} Base URL at which the application is being deployed. It consists of the [base configuration item](HTTPS:/ / cn. Vitejs. Dev/config / # base) decision.

import.meta.env.PROD: {Boolean} Specifies whether the application is running in the production environmentimport.meta.env.DEV: {Boolean} Whether the application is running in a development environment (always withimport. Meta. Env. PROD instead)import.meta.globEager: {object} List object of project files//const modules = import.meta.globEager('./modules/**/*.ts')
Copy the code

model

By default, the development server (serve command) runs in development mode and the build command runs in Production mode.

# This means that when a vite build is executed, it will automatically load environment variables that may exist in.env.production:
# In your application, you can use import.meta.env.vite_app_title as the render title.
# .env.production
VITE_APP_TITLE=My App

Copy the code
# However, it is important to understand that patterns are a broader concept than just development and production. A typical example is that you might want to have a "staging" mode, which should have production-like behavior, but the environment variables are slightly different from the production environment.
You can override the default mode used by the command by passing the --mode option flag. For example, if you want to build an application for our hypothetical staging mode:
vite build --mode staging
Copy the code
We also need a.env.staging file to make our application perform the desired behavior:
# .env.staging
NODE_ENV=production
VITE_APP_TITLE=My App (staging)
Copy the code

Vite2.0 (vite2.0) configuration

/* * @description: vite.config.ts Vite2.0 configuration * @version: 2.0 * @autor: trsoliu * @date: 2021-04-04 * @lasteditors: trsoliu * @LastEditTime: 2021-04-20 */
/* eslint-disable prettier/prettier */
// import { defineConfig } from 'vite'
import { UserConfigExport } from "vite";
import path from "path";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import styleImport from "vite-plugin-style-import";
import autoprefixer from "autoprefixer";
import pxtoviewport from "postcss-px-to-viewport";
import legacy from "@vitejs/plugin-legacy";
import viteCompression from "vite-plugin-compression";

// export default defineConfig({})
export default function (meta: any) {
  // console.log(meta,8989)
  // meta:{ mode: 'development', command: 'serve' }
  // process.env.node_env === "development" is the same as meta. Mode === "development" in the executing project environment variable import.meta.env.mode === "development"
  const resolve = (dir: string) = > {
    return path.resolve(__dirname, ".", dir);
  };
  const myPlugin: any = (conf: any) = > {
    const virtualFileId = "@my-virtual-file";
    / / hooks
    return {
      name: "my-plugin".// Yes, will be displayed in warning and error
      // Generic hook ---------------------
      options(e) {
        // console.log("options:",e)
      },
      resolveId(id) {
        // console.log("resolveId:",id)
        // if (id === virtualFileId) {
        // return virtualFileId
        // }
      },
      load(id) {
        // console.log("load1:", id);
        // if (id === virtualFileId) {
        // return `export const msg = "from virtual file"`;
        // }
      },
      transform(id) {
        // console.log("transform:",id)
        // if (id === virtualFileId) {
        // return `export const msg = "from virtual file"`
        // }
      },
      config(config) {
        // console.log("config:",config)
      },
      configResolved(resolvedConfig) {
        // Stores the final parsed configuration
        // console.log("resolvedConfig:",resolvedConfig)
        // config = resolvedConfig
      },
      // Unique hooks ---------------------
      configureServer(server) {
        // Returns a post-hook that is called after the internal middleware is installed
        return () = > {
          server.middlewares.use((req, res, next) = > {
            // console.log("req:",req,"res:", res)
            // Custom request handling...
            next();
          });
        };
      },
      transformIndexHtml(html) {
        // console.log("html:",html)
        // return html.replace(
        // /(.*?) <\/title>/,
        // `Title replaced! `
        // );}}; };const config: UserConfigExport = {
    base: ". /".// Package path, similar to "publicPath: './'" of vue.config.js
    css: {
      preprocessorOptions: {
        less: {
          // additionalData:`@import "node_modules/normalize.less/normalize.less"; `,
          // @vue/cli can use style-resources-loader to inject global variables. Vite can only find this injection method
          // The following link discusses what modifyvars. hack is all about
          // https://stackoverflow.com/questions/60809336/customizing-ant-designs-theme-what-exactly-is-that-hack-key-in-the-modifyva
          modifyVars: {
            hack: `true; @import "node_modules/normalize.less/normalize.less"; `.hack1: `true; @import "${resolve('src/assets/styles/common.less')}"; `.// hack: `true; @import "${resolve('src/styles/antd.less')}"; `,
            // hack2: `true; @import "${resolve('src/assets/styles/variables.less')}"; `,
            // hack3: `true; @import "${resolve('src/styles/transition/index.less')}"; `,
          },
          javascriptEnabled: true,}},postcss: {
        plugins: [
          autoprefixer(), // Automatic prefix
          pxtoviewport({
            / / px viewport
            viewportWidth: 750.selectorBlackList: ["van-"],}),],},},resolve: {
      / / alias
      alias: {
        "@": resolve("src"),
        vue: "vue/dist/vue.esm-bundler.js".// Define an alias for vue, which may be used if other plug-ins are used}},plugins: [
      vue(),
      vueJsx(),
      myPlugin(),
      styleImport({
        libs: [
          // Load vant module styles on demand
          {
            libraryName: "vant".esModule: true.resolveStyle: (name) = > {
              return `vant/es/${name}/style/index`; }},]}),// Browser compatible
      legacy({
        targets: ["defaults"."not IE 11"],})./ / compression
      viteCompression({
        verbose: true.disable: false.threshold: 1024.algorithm: "gzip".ext: ".gz",})],server: {
      open: "/#/index".// The server starts up and automatically opens the application in the browser
      port: 8888.// Set the port number to 3000 by default
      https: false./ / the default HTTP
      cors: true.// Any source is enabled and allowed by default
      // proxy: {
      // //
      // '/ API ': 'http://xxxx'// proxy url
      // },
      proxy: {
        //https://api.bilibili.com/x/web-interface/search/all/v2?keyword=vue
        "/x": {
          target:'http://api.bilibili.com'.changeOrigin: true.headers: {
            referer: 'http://api.bilibili.com'.origin: 'http://api.bilibili.com',}},// http://i2.hdslb.com/bfs
        "/bfs": {
          target:'http://i2.hdslb.com'.changeOrigin: true.headers: {
            referer: 'http://api.bilibili.com'.origin: 'http://api.bilibili.com',}},},},build: {
      target: "esnext".// Set the browser compatibility target for the final build
      // Remove the console and debugger
      terserOptions: {
        compress: {
          drop_console: true.// drop_debugger: true,}},}};return config;
}
Copy the code

Vue3.0 unpacking

Comparison of VUE2 and VUe3

  • ES6 Proxy has more listening forms and higher efficiency than ES5 defineProperty.
  • Vue3 is developed in TypeScript, which is naturally more developer-friendly;
  • Vue3, inspired by ReactHook, leaves the object programming Options API and opens the function programming Composition API;
  • More convenient support for JSX
  • Vue3 uses fragments to manage the virtual root node and virtual parent. Multiple templates, that is, root labels, can be created in a. Vue file.
  • Vue3 rewrites the virtual DOM and optimizes the compilation of templates.

DefineProperty and Proxy

DefineProperty is proxy-to-static value level and can only redefine the read (get) and set (set) behavior of attributes. Proxy is proxy-to-object, intercepting up to 13 operations. Proxy easily solves the problem that Vue2.0 cannot manipulate array subscripts. A common feature of Proxy and defineProperty is that it does not support object nesting and needs to be implemented recursively.

DefineProperty mock code

<! --src/views/DefineProperty.vue-->
<template>
 <div class="demo-box">
   <h4>defineProperty</h4>
   <input id="input" />
   <div id="defineProperty">hello defineProperty</div>
 </div>
</template>

<script setup> 
import { onMounted } from 'vue'
// //1. Bind a single attribute
// // emulates the data option in Vue
// let data = {
// msg: 'hello',
// }
// // Emulates an instance of Vue
// let vm = {}
// // data hijacking: Intervene when accessing or setting members of a VM
// Object.defineProperty(vm, 'msg', {
// // enumerable (traversable)
// enumerable: true,
// // configurable (can be deleted using delete, can be redefined via defineProperty)
// configurable: true,
// // executes when the value is fetched
// get () {
// console.log('get: ', data.msg)
// return data.msg
/ /},
// // Executed when the value is set
// set (newValue) {
// console.log('set: ', newValue)
// if (newValue === data.msg) {
// return
/ /}
// data.msg = newValue
// // Data changes, updates the DOM value
// document.querySelector('#defineProperty').textContent = data.msg
/ /}
// })
The // // page structure is loaded
// onMounted(() => {
// // The detection object changes.
// const input = document.getElementById('input');
// // When the input box data changes, the data changes
// input.oninput = function () {
// vm.msg = this.value;
/ /}
// // Initialize the binding and update the view
// console.log(data.msg, 21)
// vm.msg = 11;
// console.log("vm1:", vm)
// })


// //2. Bind a single object and its properties
// // emulates the data option in Vue
// let data = {
// msg: 'hello',
// count: 10
// }
// // Emulates an instance of Vue
// let vm={}
// function proxyData (obj) {
// // iterate over all properties of the obj object
// Object.keys(obj).forEach(key => {
// console.log("obj:", obj)
// // converts properties in OBj into setters/setters for vm
// Object.defineProperty(vm, key, {
// enumerable: true,
// configurable: true,
// get () {
// console.log('get: ', key, obj[key])
// return obj[key]
/ /},
// set (newValue) {
// console.log('set: ', key, newValue)
// if (newValue === obj[key]) {
// return
/ /}
// obj[key] = newValue
// // Data changes, updates the DOM value
// document.querySelector('#defineProperty').textContent = obj[key]
/ /}
/ /})
/ /})
// }
// // Perform binding
// proxyData(data)
The // // page structure is loaded
// onMounted(() => {
// // The detection object changes.
// const input = document.getElementById('input');
// // When the input box data changes, the data changes
// input.oninput = function () {
// vm.msg = this.value;
/ /}
// // Initialize the binding and update the view
// console.log(data.msg,21)
// vm.msg = 11;
// console.log("vm2:", vm)
// })


//3. Multilevel subattribute binding
// Emulate the data option in Vue
let data = {
 valueObj: {
   value: 'hello'
 },
 msg: 'hello'.count: 10.obj: {
   name: 'Uncle Li'.age: 80.children: {
     name: 'bill'.age: 50.children: {
       name: 'Li Xiao Si'.age: 20}}}}function ProxyData (obj) {
 // debugger
 // Loop to get the object property name
 for (let key of Object.keys(obj)) {
   // When the value of the attribute is an object, the recursive parsing continues
   if (typeof obj[key] === 'object') {
     obj[key] = new ProxyData(obj[key]);
   }
   // Use Object,definPropert's set to detect data changes with input parameters: current Object, current property.
   Object.defineProperty(this, key, {
     enumerable: true.configurable: true,
     get () {
       console.log('get-key:', key);
       return obj[key];
     },
     set (newVal) {
       console.log('set-key:' + key);
       console.log('newVal:', newVal);
       if (newVal === obj[key]) {
         return true;
       }
       obj[key] = newVal;
       document.querySelector('#defineProperty')?document.querySelector('#defineProperty').textContent = vm.valueObj.value : ' '; }}}})// Simulate the Vue instance
const vm = new ProxyData(data);
onMounted(() = > {
 // Detect object changes.
 const input = document.getElementById('input');
 // When the input box data changes, the data changes
 input.oninput = function () {
   console.log("this.value:".this.value, vm)
   vm.valueObj.value = this.value;
 }
 console.log("data3:", vm)
})

</script>

Copy the code

Proxy simulation code

Proxy objects are used to create a Proxy for an object to intercept and customize basic operations (such as property lookup, assignment, enumeration, function calls, and so on). Proxy Deep Proxy refer to the code in the Reactive and REF principles below. Proxy has 13 data hijacking operations, as follows:

methods describe
get Gets the value of a key
set Sets the value of a key
has Use the in operator to determine whether a key exists (Boolean)
apply Function call, valid only if the proxy object is function
ownKeys Gets all the keys of the target object
construct The function is invoked by instantiation only if the proxy object is function
isExtensible Determines whether an object is extensible
deleteProperty Delete an attribute
defineProperty Define a new property
getPrototypeOf Get the prototype object
setPrototypeOf Set the prototype object
preventExtensions Set the object to be unextensible
getOwnPropertyDescriptor Gets the property description of a free property
<! --src/views/Proxy.vue-->
<template>
  <div class="demo-box">
    <h4>proxy</h4>
    <input id="input" />
    <div id="proxy">hello proxy</div>
  </div>
</template>
<script setup> 
import { onMounted } from 'vue'
// Emulate the data option in Vue
let data = {
  msg: 'hello proxy'.count: 10
}
const vm = new Proxy(data, {
  // a function that performs the agent's behavior
  // when a member accesses the VM, it executes
  get (target, key) {
    console.log('get-key-target: ', key, target)
    return target[key]
  },
  // When the vm member is set, it is executed
  set (target, key, newValue) {
    console.log('set-key-newValue: ', key, newValue)
    if (target[key] === newValue) {
      return true
    }
    target[key] = newValue
    document.querySelector('#proxy')?document.querySelector('#proxy').textContent = newValue : ' ';
    return true}})console.log(vm)
onMounted(() = > {
  // Detect object changes.
  const input = document.getElementById('input');
  // When the input box data changes, the data changes
  input.oninput = function () {
    vm.msg = this.value; }})</script>

Copy the code

Reactive and REF principles

Vue2.0 uses Object.defineProperty() to hijack attributes to achieve data-driven purposes; In VUe3.0, the data responsive listening methods REF and Reactive are implemented by Proxy.

<! --src/views/ReactiveRef.vue-->
<!--ref和reactive底层实现-->
<template>
  <div class="demo-box">
    <h4>reactive</h4>
    <input id="reactiveInput" />
    <div id="reactive">hello reactive</div>
    <h4>ref</h4>
    <input id="refInput" />
    <div id="ref">hello ref</div>
  </div>
</template>
<script setup> 
import { onMounted } from 'vue'

let reactiveObj = {
  name: 'Uncle Li'.age: 80.children: {
    name: 'bill'.age: 50.children: {
      name: 'Li Xiao Si'.age: 20}}}// reactive
function reactive (obj) {
  // Obj is a proxy for objects
  if (typeof obj === 'object') {
    if (obj instanceof Array) {
      // If it is an array, fetch each element in the array and determine whether each element is an object. If it is an object, it also needs to be wrapped as a Proxy
      obj.forEach(item= > {
        if (typeof (item, index) === 'object') {
          item[index] = reactive(item)
        }
      })
    } else {
      // If it is an object, take out every value in the object to determine whether the value of the object's attribute is an object. If it is, it also needs to be wrapped as a Proxy
      for (let key in obj) {
        if (typeof obj[key] === 'object') {
          obj[key] = reactive(obj[key])
        }
      }
    }
  } else {
    console.warn('Incoming content:${obj}Not an object)}return new Proxy(obj, {
    get (obj, key) {
      console.log("Get value")
      return obj[key]
    },
    set (obj, key, newValue) {
      console.log("Change value", obj, key, newValue)
      obj[key] = newValue
      document.querySelector('#reactive')?document.querySelector('#reactive').textContent = reactiveState.children.children.name : ' ';
      document.querySelector('#ref')?document.querySelector('#ref').textContent = refState.value : ' ';
      return true  // The operation succeeds and go to the next step}})}// ref
function ref (val) {
  return reactive({ value: val })
}

let reactiveState = reactive(reactiveObj)

console.log(1, reactiveState)
console.log(2, reactiveState.name)
console.log(3, reactiveState.children.name)
console.log(4, reactiveState.children.children.name)
let refState = ref("Zhang")
console.log("refState:", refState)
onMounted(() = > {
  // Detect object changes.
  const reactiveInput = document.getElementById('reactiveInput');
  const refInput = document.getElementById('refInput');
  console.log("reactiveInput:", reactiveInput)
  // When the input box data changes, the data changes
  reactiveInput.oninput = function () {
    console.log("this.value:".this.value)
    reactiveState.children.children.name = this.value;
  }
  refInput.oninput = function () {
    refState.value = this.value; }})</script>

Copy the code

Composition API

The setup function

The setup() function is a new attribute in VUe3 that is specifically provided for components. It provides a unified entry point for using vue3’s Composition API features. Setup functions are executed before beforeCreate and Created, which vue3 also removes. This function is equivalent to a life cycle function. Data, methods and watch in vUE are all written in the setup() function with corresponding new API.

  • Props should pay attention to read incoming problem (@ / components/SearchList. Experience in vue).
<! --src/views/Setup.vue-->
<template>
 <div>Setup<div>
<template>

<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  setup(props, context) {
    //context.attrs
    //context.slots
    //context.emit
    return{}}});</script>
Copy the code
  • Props: Used to receive props data.
  • Context is used to define the context. The context object contains some useful properties. These properties can be accessed through this in vue 2.x.
  • Return value: return {}, which returns reactive data, the function used in the template;

LifeCycle Hooks (Vue3.0 LifeCycle)

The lifecycle functions of Vue3.0 can be imported into components on demand and can only be used in setup() functions, while vue3.0 retains the original options API usage.

Option type API Hook inside setup
beforeCreate Not needed*
created Not needed*
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered
<! --src/views/LifecycleHooks.vue-->
<template>
  <div class="demo-box">
    <h4>Lifecycle Hooks</h4>
  </div>
</template>

<script lang="ts">
import { defineComponent,onBeforeMount,onMounted, onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onErrorCaptured,onRenderTracked,onRenderTriggered } from 'vue'

export default defineComponent({
  // Options API object programming
  beforeCreate(){
    console.log('--beforeCreate! ')},created(){
    console.log('--created! ')},beforeMount(){
    console.log('--beforeMount! ')},mounted(){
    console.log('--mounted! ')},beforeUpdate(){
    console.log('--beforeUpdate! ')},updated(){
    console.log('--updated! ')},beforeUnmount(){
    console.log('--beforeUnmount! ')},unmounted(){
    console.log('--unmounted! ')},errorCaptured(){
    console.log('--errorCaptured! ')},renderTracked(){
    console.log('--renderTracked! ')},renderTriggered(){
    console.log('--renderTriggered! ')},// Composition API function programming
  setup(props, context) {
    console.log("setup!")

    onBeforeMount(() = >{
      console.log('onBeforeMount! ')
    })
    onMounted(() = >{
      console.log('onMounted! ')
    })
    onBeforeUpdate(() = > {
      console.log('onBeforeUpdate! ')
    })
    onUpdated(() = > {
      console.log('onUpdated! ')
    })
    onBeforeUnmount(() = > {
      console.log('onBeforeUnmount! ')
    })
    onUnmounted(() = > {
      console.log('onUnmounted! ')
    })
    onErrorCaptured(() = > {
      console.log('onErrorCaptured! ')
    })
    onRenderTracked(() = > {
      console.log('onRenderTracked! ')
    })
    onRenderTriggered(() = > {
      console.log('onRenderTriggered! ')})return{}}})</script>
Copy the code

Provide / Inject

Provide () and Inject () enable data transfer between nested components. These two functions can only be used in the setup() function. The parent component passes data down through the provide() function, and the child component retrieves data from the upper layer through inject().

<! -- Parent component provide -->
<template>
 <div class="demo-box">
   <h4>Provide:</h4>
   <h4>{{title0}}</h4>
   <Inject/>
</div>
</template>
<script lang="ts">
import {defineComponent, ref , provide } from 'vue'
import Inject from "@/views/modules/Demo/Inject.vue"
export default defineComponent({
  components: {
   Inject,
 },
  setup() {
  let title0 = ref('I'm the parent component. ')
  provide('title', title0);  
    return {
      title0
    }
  }
})
</script>

<! -- Subcomponent inject -->
<template>
 <div class="demo-box">
   <h4>Inject:</h4>
   <h4>{{title1}}</h4>
</div>
</template>
<script lang="ts">
import {defineComponent, inject } from 'vue'
export default defineComponent({
  setup() {
  let title1 = inject("title");  
  return {
    title1
  }
}
})
</script>
Copy the code

GetCurrentInstance function

GetCurrentInstance () can only be called from a Setup or lifecycle hook and supports access to internal component instances for higher-order usage or library development.

<! --src/views/GetCurrentInstance.vue-->
<template>
 <div class="demo-box">
   <h4>getCurrentInstance</h4>
   <div ref="gti"></div>
 </div>
</template>

<script lang="ts">
import { defineComponent, getCurrentInstance } from 'vue'

export default defineComponent({
 mounted() {
   console.log('this:'.this)},setup(props, context) {
   const instance: any = getCurrentInstance() // Can replace this in the Options API
   console.log(
     'instance.appContext.config.globalProperties:',
     instance.appContext.config.globalProperties // Read the global property
   ) / / access globalProperties
   console.log('instance:', instance)
   return{}}})</script>

</script>

Copy the code

Reactivity API

Reactive function

Reactive () takes in a normal object and returns a reactive data object. It is easy to use the created reactive data by returning it from setup and calling it directly from template.

 <! --src/views/Reactive.vue-->
 <template>
  <div class="demo-box">
    <h4>Reactive</h4>
    <div>{{ state.valueObj.count }}</div>
    <button @click="plus">+</button>
    <button @click="minus">-</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from 'vue'
export default defineComponent({
  setup() {
    const state = reactive({
      valueObj: {
        count: 100.value: 'hello',},msg: 'hello proxy'.count: 10.obj: {
        name: 'Uncle Li'.age: 80.children: {
          name: 'bill'.age: 50.children: {
            name: 'Li Xiao Si'.age: 20,},},},})console.log('state:', state)
    const plus = () = > {
      state.valueObj.count++
    }
    const minus = () = > {
      state.valueObj.count--
    }
    return {
      state,
      plus,
      minus,
    }
  },
})
</script>

Copy the code

IsReactive function

IsReactive () checks whether an object is created by isReactive().

  <! --src/views/isReactive.vue-->
 <template>
  <div class="demo-box">
    <h4>isReactive</h4>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, isReactive } from 'vue'
export default defineComponent({
  setup() {
    const obj = {
      name: 'Uncle Li'.age: 80.children: {
        name: 'bill'.age: 50.children: {
          name: 'Li Xiao Si'.age: 20,,}}}const state=reactive(obj)
    console.log('isReactive(state):', isReactive(state)) // true
    console.log('isReactive(obj):', isReactive(obj)) // false
    return{}}})</script>

Copy the code

Ref function

The ref() function is used to create a responsive data object based on the given value. The return value of the ref() call is an object containing only one value attribute.

 <! --src/views/Ref.vue-->
 <template>
  <div class="demo-box">
    <h4>Ref</h4>
    <div>{{ count }}</div>
    <button @click="plus">+</button>
    <button @click="minus">-</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
  setup() {
    const count = ref<number>(99)
    console.log('count:', count)
    const plus = () = > {
      count.value++
    }
    const minus = () = > {
      count.value--
    }
    return {
      count,
      plus,
      minus,
    }
  },
})
</script>
Copy the code

IsRef function

IsRef () is used to determine whether a value is an object created by ref().

 <! --src/views/IsRef.vue-->
 <template>
  <div class="demo-box">
    <h4>isRef</h4>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, isRef } from 'vue'
export default defineComponent({
  setup() {
    const name=ref<string>("Zhang");
    const age:number=18;
    console.log("isRef(name):",isRef(name)); // true
    console.log("isRef(age):",isRef(age)); // false
    return{}}})</script>

Copy the code

ToRefs function

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

 <! --src/views/ToRefs.vue-->
 <template>
  <div class="demo-box">
    <h4>toRefs</h4>
    <div>name:{{name}}</div>
    <div>state.name:{{state.name}}</div>
    <div>age:{{age}}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from 'vue'
export default defineComponent({
  setup() {
    const state = reactive({
      name: 'Joe',})const age = ref(18)

    return {
      ...toRefs(state),
      state,
      age
    }
  },
})
</script>
Copy the code

computed

Computed () is used to create computed attributes, and as in the past, the value it returns is a ref object. It can pass methods, or an object containing set() and get() methods.

<! --src/views/Computed.vue-->
<template>
  <div class="demo-box">
    <h4>Computed</h4>
    <div>count:{{ count }}</div>
    <button @click="plus">+</button>
    <button @click="minus">-</button>
    <div>readonlyNum:{{ readonlyNum }}</div>
    <! -- <button @click="plus2">+</button> <button @click="minus2">-</button> -->
    <div>computedNum:{{computedNum}}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref,computed } from 'vue'
export default defineComponent({
  setup() {
    const count = ref<number>(99)
    const num = ref<number>(99)
    const number = ref<number>(99)
    console.log('count:', count)
    const plus = () = > {
      count.value++
    }
    const minus = () = > {
      count.value--
    }
    
    // 1. Create a read-only compute property
    // Based on the value of num, create a reactive calculation attribute readonlyCount that automatically evaluates and returns a new ref based on the dependent ref
    const readonlyNum = computed(() = > num.value+1) 
    console.log('readonlyNum:', readonlyNum.value) //readonlyNum: 100
    // const plus2 = () => {
    // readonlyNum.value++ //Cannot assign to 'value' because it is a read-only property.
    // }
    // const minus2 = () => {
    // readonlyNum.value-- //Cannot assign to 'value' because it is a read-only property.
    // }


    // 2. Set () and get() to create a readable and writable computational property
    // Based on the value of num, create a reactive computedNum property that automatically computes and returns a new ref based on the dependent ref
    const computedNum=computed({
      get:() = >{
        return number.value+10 
      },
      set:val= >{
         setTimeout(() = >{
           number.value=val-1
         },2000)}})console.log("computedNum1:",computedNum.value) / / 109
    // The set function is triggered when the value of num. Value is updated to 201
    computedNum.value=202
    // The get function is triggered here, and the computedNum. Value value is updated to 201+10=>211
    console.log("computedNum2:",computedNum.value) / / 211
    return {
      count,
      readonlyNum,
      plus,
      minus,
      // plus2,
      // minus2,
      computedNum,
    }
  },
})
</script>

Copy the code

WatchEffect function

  • 1. Execute immediately without inertia. The page is executed when it is first loaded.
  • 2. There is no need to indicate the content to be monitored, as long as a callback function is passed, automatic detection of internal code aware code dependency, code dependency will be executed;
  • 3. The current and original values cannot be obtained.
  • 4. Data monitoring for asynchronous operations.
<! --src/views/WatchEffect.vue-->
<template>
  <div class="demo-box">
    <h4>watchEffetc</h4>
    <div>reactiveState.valueObj.count:{{ reactiveState.valueObj.count }}</div>
    <div>reactiveState.msg:{{ reactiveState.msg }}</div>
    <div>reactiveState.count:{{ reactiveState.count }}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, watchEffect, reactive } from 'vue'
export default defineComponent({
  setup(props, ctx) {
    // 1. Listen to data sources declared by Reactive
    const reactiveState = reactive({
      valueObj: {
        count: 100.value: 'hello',},msg: 'hello'.count: 10.obj: {
        name: 'Uncle Li'.age: 80.children: {
          name: 'bill'.age: 50.children: {
            name: 'Li Xiao Si'.age: 20,},},},})// Run a function immediately when its dependencies are tracked responsively, and re-run it when the dependency changes.
    watchEffect(() = > {
      console.log('reactiveState.valueObj.count:', reactiveState.valueObj.count)
      console.log('reactiveState.valueObj.msg:', reactiveState.msg)
      / / reactiveState valueObj. Count and reactiveState. MSG changes will trigger execution surveillance, and reactiveState. Count++
      reactiveState.count++
    })
    setTimeout(() = > {
      reactiveState.valueObj.count = 201
    }, 2000)
    setTimeout(() = > {
      reactiveState.msg = 'hello watchEffetc'
    }, 3000)
    return {
      reactiveState,
    }
  },
})
</script>

Copy the code

Watch function

The watch() function listens for a specific data source and executes the business code in the callback function. The default is lazy, meaning that callbacks are executed only when the source data being listened for changes.

  • 1. There is a certain inertia, the first page display will not be executed, only when the data changes will be executed;
  • 2. Monitor the value before and after the change, and obtain the parameter newValue and oldValue.
  • 3. Can listen to the changes of multiple data, with a listener to load.
Listen to data sources declared with Reactive
 <! --src/views/WatchReactive.vue-->
<template>
  <div class="demo-box">
    <h4>watchEffetc</h4>
    <div>reactiveState.valueObj.count:{{ reactiveState.valueObj.count }}</div>
    <br />
    <div>reactiveState.msg:{{ reactiveState.msg }}</div>
    <br />
    <div>reactiveState.count:{{ reactiveState.count }}</div>
    <br />
  </div>
</template>

<script lang="ts">
import { defineComponent, watchEffect, reactive } from 'vue'
export default defineComponent({
  setup(props, ctx) {
    // 1. Listen to data sources declared by Reactive
    const reactiveState = reactive({
      valueObj: {
        count: 100.value: 'hello',},msg: 'hello'.count: 10.obj: {
        name: 'Uncle Li'.age: 80.children: {
          name: 'bill'.age: 50.children: {
            name: 'Li Xiao Si'.age: 20,},},},})// Run a function immediately when its dependencies are tracked responsively, and re-run it when the dependency changes.
    watchEffect(() = > {
      console.log('reactiveState.valueObj.count:', reactiveState.valueObj.count)
      console.log('reactiveState.valueObj.msg:', reactiveState.msg)
      / / reactiveState valueObj. Count and reactiveState. MSG changes will trigger execution surveillance, and reactiveState. Count++
      reactiveState.count++
    })
    setTimeout(() = > {
      reactiveState.valueObj.count = 201
    }, 2000)
    setTimeout(() = > {
      reactiveState.msg = 'hello watchEffetc'
    }, 3000)
    return {
      reactiveState,
    }
  },
})
</script>
Copy the code
Listen for the data source declared with ref
<! --src/views/WatchRef.vue-->
<template>
  <div class="demo-box">
    <h4>watch-ref</h4>
    <div>refState:{{ refState }}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, watch, watchEffect, ref } from 'vue'
export default defineComponent({
  setup(props, ctx) {
    // 1. Listen on the data source declared with ref
    const refState = ref<number>(100)
    watch(
      () = > refState,
      (newVal, oldVal) = > {
        console.log('oldVal:', oldVal) / / 100
        console.log('newVal:', newVal) / / 201})// Run a function immediately when its dependencies are tracked responsively, and re-run it when the dependency changes.
    watchEffect(() = > console.log('refState.value:', refState.value))
    setTimeout(() = > {
      // When refState is modified, the watch callback will be triggered to print the values before and after the change
      refState.value = 201
    }, 2000)

    return {
      refState,
    }
  },
})
</script>
Copy the code
Listening to multiple data sources
<! --src/views/WatchMult.vue-->
<template>
<div class="demo-box">
  <h4>watch-mult</h4>
  <! -- <div>refState:{{ refState }}</div> -->
  <div>oldVal:{{ watchState.oldVal }}</div>
  <br />
  <div>newVal:{{ watchState.newVal }}</div>
</div>
</template>

<script lang="ts">
import { defineComponent, watch, watchEffect, reactive, ref, toRefs } from 'vue'
export default defineComponent({
setup(props, ctx) {
  // 1. Listen to data sources declared by Reactive
  const reactiveState = reactive({
    valueObj: {
      count: 100.value: 'hello',},msg: 'hello proxy'.count: 10.obj: {
      name: 'Uncle Li'.age: 80.children: {
        name: 'bill'.age: 50.children: {
          name: 'Li Xiao Si'.age: 20,},},},})const watchState = reactive<any>({
    oldVal: [].newVal: [],})//2. Listen on the data source declared with ref
  const refState = ref<number>(99)
  // Listen to multiple data sources
  watch(
    [() = > reactiveState.valueObj.count, refState],
    (newVal: any, oldVal: any) = > {
      console.log('oldVal:', oldVal) //[proxy(reactiveState-oldVal),99]
      watchState.oldVal = oldVal
      console.log('newVal:', newVal) //[proxy(reactiveState-newVal),201]
      watchState.newVal = newVal
    },
    { deep: true})// Run a function immediately when its dependencies are tracked responsively, and re-run it when the dependency changes.
  watchEffect(() = >
    console.log('reactiveState.valueObj.count:', reactiveState.valueObj.count)
  )
  watchEffect(() = > console.log('refState.value:', refState.value))
  setTimeout(() = > {
    / / modify reactiveState. ValueObj. Count and refState triggered when watch callback, print changes before and after the value
    reactiveState.valueObj.count = 101
    refState.value = 201
  }, 2000)

  return {
    reactiveState,
    refState,
    watchState,
  }
},
})
</script>i.Copy the code
Stop listening to data sources

Watch monitoring, created within the setup() function, stops automatically when the current component is destroyed. If you want to explicitly stop a monitoring, you can call the return value of the watch() function and execute it with the following syntax:

<! --src/views/WatchStop.vue-->
<template>
  <div class="demo-box">
    <h4>watch-stop</h4>
    <! -- <div>refState:{{ refState }}</div> -->
    <div>oldVal:{{ watchState.oldVal }}</div>
    <br />
    <div>newVal:{{ watchState.newVal }}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, watch, watchEffect, reactive, ref, toRefs } from 'vue'
export default defineComponent({
  setup(props, ctx) {
    // 1. Listen to data sources declared by Reactive
    const reactiveState = reactive({
      valueObj: {
        count: 100.value: 'hello',},msg: 'hello proxy'.count: 10.obj: {
        name: 'Uncle Li'.age: 80.children: {
          name: 'bill'.age: 50.children: {
            name: 'Li Xiao Si'.age: 20,},},},})const watchState = reactive<any>({
      oldVal: [].newVal: [],})//2. Listen on the data source declared with ref
    const refState = ref<number>(99)
    // Listen to multiple data sources
    const stop = watch(
      [() = > reactiveState.valueObj.count, refState],
      (newVal: any, oldVal: any) = > {
        console.log('oldVal:', oldVal) //[proxy(reactiveState-oldVal),99]
        watchState.oldVal = oldVal
        console.log('newVal:', newVal) //[proxy(reactiveState-newVal),201]
        watchState.newVal = newVal
      },
      { deep: true})// Run a function immediately when its dependencies are tracked responsively, and re-run it when the dependency changes.
    watchEffect(() = >
      console.log('reactiveState.valueObj.count:', reactiveState.valueObj.count)
    )
    watchEffect(() = > console.log('refState.value:', refState.value))
    setTimeout(() = > {
      / / modify reactiveState. ValueObj. Count and refState triggered when watch callback, print changes before and after the value
      reactiveState.valueObj.count = 101
      refState.value = 201
    }, 2000)
    // Stop listening manually
    setTimeout(() = > {
      console.log('stop:', stop)
      stop()
      / / modify reactiveState. ValueObj. Count and refState triggered when watch callback, print changes before and after the value
      reactiveState.valueObj.count = 501
      refState.value = 601
    }, 5000)

    return {
      reactiveState,
      refState,
      watchState,
    }
  },
})
</script>
Copy the code

Built-In Components

teleport

Teleport provides a clean way to control which parent node in the DOM renders HTML without having to resort to global state or split it into two components.

  • Note that this will move the actual DOM node rather than being destroyed and recreated, and it will also keep any component instances active. All stateful HTML elements (that is, the video played) will remain in their state.
<! --src/views/Teleport.vue-->
<template>
 <div class="demo-box">
   <h4>teleport</h4>
   <button @click="modalOpen = true">
     Open full screen modal! (With teleport!)
   </button>
   <! -- <teleport> props -->
   <! -- to specify the target element to move <teleport> content -->
   <! -- disabled-boolean. This optional property can be used to disable the functionality of <teleport>, which means that its slot contents will not be moved to any location, but instead rendered at the location where you specified <teleport> in the surrounding parent component. -->
   <teleport to="body" :disabled="disabled">
     <div v-if="modalOpen" class="modal">
       <div>
         I'm a teleported modal! (My parent is "body")
         <button @click="modalOpen = false">Close</button>
       </div>
     </div>
   </teleport>
 </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
 setup() {
   const modalOpen = ref<boolean>(false)
   const disabled = ref<boolean>(true)
   return {
     modalOpen,
     disabled,
   }
 },
})
</script>
Copy the code

What is state management

The status asset management application consists of the following parts:

  • State: Data source that drives the application (for example, stateObj);
  • View: map the declared state to the view (template);
  • Actions: Responds to state changes caused by user actions on the view (e.g., triggering click).

That is, one-way data flow

<template>
  <div class="demo-box">
    <div>state.name:{{state.name}}</div>
    <button @click="change">change</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from 'vue'
export default defineComponent({
  setup() {
    const stateObj = reactive({
      name: 'Joe',})const change = () = > {
        stateObj.name = "Bill"
    }

    return {
      stateObj,
      change
    }
  },
})
</script>
Copy the code

The simplicity of one-way data flow can easily be broken when our application encounters state shared by multiple components:

  • Multiple views depend on the same state: the method of passing parameters can be cumbersome for multi-layer nested components, and cannot be used to pass state between sibling components.
  • Behavior from different views requires changing the same state: we often use parent-child components to change and synchronize multiple copies of state either directly or through events. But these patterns are fragile and often result in unmaintainable code.

Flux, Redux, Vuex

Flux, Redux, and Vuex are common data source status management modes.

Flux

Flux is Facebook’s architecture for building client-side Web applications, complementing React’s composable view component with its one-way data flow. Flux is a pattern rather than a formal framework.

The composition of Flux
  • View: View layer;
  • Action: an Action, i.e. a message object (triggered by an event, triggered by a test case, etc.) in which data changes:
    • Store changes can only be made through Action;
    • The processing logic for specific actions is stored in stores.
    • The Action object contains type and payload.
  • -Dispatcher: Take Actions, send them to all stores.
  • Store: Data layer, Store application status and update status method, once changes, remind Views to update the page.

The characteristics of the Flux
  • One-way data flow, view events or external test cases send actions via Dispatcher to Store, Store will trigger corresponding methods to update data, update view;
  • There can be more than one Store;
  • A Store not only stores data, but also encapsulates methods for processing it.

Redux

Redux is a JavaScript state container that provides predictable state management. Redux also supports other interface libraries in addition to React.

The composition of the story
  • Store: Stores the application state and the dispatch method used to trigger state updates, etc. There is only a single Store for the entire application. Several apis are provided in the Store:

    • Store.getstate (): gets the current state;
    • Store.dispatch (Action): used by the View to issue an action;
    • Store.subscribe (listener): Sets the listener function to execute when state changes (if the view update function is passed as a listener, automatic view rendering is triggered).
  • Action: Like Flux, Action is the message object used to update state, which is issued by the View;

    • There is an Action Creator that generates actions;
  • Reducer: is a pure function used to change state (same return results for the same parameters, no modification of parameters, no dependence on external variables), that is, new state is derived through application state and Action: (previousState, Action) => newState. Reducer returns a new state.

The characteristics of the story
  • One-way data flow. The View sends Action (store.dispatch(Action)), and the store calls Reducer to calculate the new state. If the state changes, Call the listener function to re-render the View (store.subscribe(render));
  • Single data source, only one Store;
  • State is read-only and only one new state can be returned after each status update;
  • Instead of a Dispatcher, the dispatch method is integrated into the Store, where store.dispatch() is the only way a View can issue an Action.

Vuex (v4.x)

Vuex is a state management mode (similar to Redux for React) developed specifically for vue.js applications. It uses centralized storage to manage the state of all components of an application and rules to ensure that the state changes in a predictable way.

The composition of Vuex
  • Store: Vuex adopts a single state tree, and each application has only one Store instance, which includes state, Actions, mutations, getters, modules;
  • State: Vuex is a single data source:
    • State can be accessed as a calculated property through the mapState helper function, or this.$store.state can be accessed after state is injected globally through Store;
      • The State update view is implemented through vUE’s bidirectional binding mechanism;
  • Getters: Getters are similar to filters in that State can be modified to output after filtering.
  • Mutation: Mutaion is the only way to change State in VUEX (in strict mode) and can only be a synchronous operation. Mutation is called in Vuex via store.mit ();
  • Action: Some asynchronous operations on State can be placed in the Action and changed by submitting Mutaion on the Action:
    • Action is triggered by the store.dispatch() method;
    • You can map the METHODS of a Vue component to a store.dispatch call via a mapActions helper function (by injecting store at the root node first);

Module: When the Store object is too large, it can be divided into multiple modules according to specific business requirements. Each Module has its own state, mutation, action and getter (namespace needs to be enabled).

The characteristics of Vuex
  • One-way data flow. The View calls the Action through store.dispatch(), calls the Mutation State through store.mit () after the Action performs the asynchronous operation, and updates the View through the responsive mechanism of VUE.
  • Single data source, just like Redux, there is only one Store instance globally;
  • State can be modified directly.

Vuex is a state management library designed specifically for VUE. Vuex extracts the shared state of components and manages it in a global singleton mode. In this mode, the number of components forms a huge “view” and any component can obtain the state activation behavior no matter where in the tree. Code becomes more structured and maintainable by defining and isolating concepts in state management and by enforcing rules to maintain independence between views and states.

Application scenarios of vuEX

Vuex can easily manage shared data state in large, single-page applications with complex data operations that are shared. When the business logic is not complex, lightweight dependency injection tools provide and Inject functions can be used to replace VUEX.

Create a Vuex Store

  • Example demo can refer to bilibili module example source code
import { createStore } from "vuex"; <! Create a vuex store-->export default createStore({
modules: {
    mymodules: {namespace:true.// Enable the namespace to isolate service module data
          state: {
            count:99
          },
          getters: {
            getCountString(state) {
                return "Calculation result:" + state.count
            }
          },
          mutations: {
            PLUS(state, payload) {
              state.count= state.count + payload
            },
          },
          actions: {
            ADD({ commit }, payload) {
              setTimeout( () = > {
                  commit("PLUS", payload)
              },2000(}}}}});Copy the code

Several ways to get data in Get State

Get the data in state directly
<! -- Vue component -->
<template>
  <div class="demo-box">
    <div>count:{{count}}</div> 
    <div>count1:{{count1}}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive , computed} from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
  setup() {
    const store = useStore();
    // Get it directly
    const count = store.state.mymodules.count; / / 99
    // Obtained using computed data
    const count1 = computed(() = > store.state.mymodules.count); / / 99
    
    return {
      count,
      count1
    }
  },
})
</script>
Copy the code
Use getters to decorate the data in the retrieved state
<! -- Vue component -->
<template>
  <div class="demo-box">
    <div>countString:{{countString}}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive , computed} from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
  setup() {
    const store = useStore();
    const countString = store.getters['mymodules/settlement'];// Result: 99
    
    return {
      countString
    }
  },
})
</script>
Copy the code

Several methods of data in set state

Use the COMMIT method to trigger the methods in mutation to synchronously update the data in state
<! -- Vue component -->
<template>
  <div class="demo-box">
    <div>countString:{{countString}}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive , computed} from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
  setup() {
    const store = useStore();
    store.commit('mymodules/PLUS'.100);// The state. Count value is 199
    
    return{}}})</script>
Copy the code
Use the Dispatch method to trigger the methods in the Action to update the data in state

The commit method is triggered internally in the Action method to update state, often used for asynchronous interface requests.

<! -- Vue component -->
<template>
  <div class="demo-box">
    <div>countString:{{countString}}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive , computed} from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
  setup() {
    const store = useStore();
    store.dispatch('mymodules/ADD'.200);// After two seconds, state.count will be 299
    
    return{}}})</script>
Copy the code

vue-router(v4.x)

  • Example demo can refer to bilibili module example source code

What is front-end routing

In Single Page Application (SPA), routing describes the mapping between URL and UI. This mapping is unidirectional, that is, THE CHANGE of URL causes UI update (no Page refresh is required).

There are two ways of routing

hash

Implementation principle of Hash
<! DOCTYPEhtml>
<html lang="en">
<body>
<ul>
    <ul>
        <! -- Define route -->
        <li><a href="#/home">home</a></li>
        <li><a href="#/about">about</a></li>

        <! Render route UI -->
        <div id="routeView"></div>
    </ul>
</ul>
</body>
<script>
    let routerView = document.getElementById("routeView");
    // Initialize page load reason management
    window.addEventListener('DOMContentLoaded'.() = >{
        if(! location.hash){// If no hash value exists, redirect to #/
            location.hash="/"
        }else{// Render the UI if there is a hash value
            let hash = location.hash;
            routerView.innerHTML = hash
        }
    })
    // Listen for hash changes to the address and then switch the contents of the router View
    window.addEventListener('hashchange'.() = >{
        let hash = location.hash;
        routerView.innerHTML = hash
    })
   
</script>
</html>
Copy the code

Configure the hash mode on vue-router

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

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    / /...],})Copy the code

history

How history works

<! Start the service locally -->
<! DOCTYPEhtml>
<html lang="en">
  <body>
    <ul>
      <ul>
        <li><a href="./home">home</a></li>
        <li><a href="./about">about</a></li>

        <div id="routeView"></div>
      </ul>
    </ul>
  </body>
  <script>
    let routerView = document.getElementById("routeView");
    // Initialize page load reason management
    window.addEventListener("DOMContentLoaded".() = > {
      routerView.innerHTML = location.pathname;
      var linkList = document.querySelectorAll("a[href]");
      console.log("linkList:",linkList);
      // Add a listener to each route jump event
      linkList.forEach((el) = >
        el.addEventListener("click".function (e) {
          e.preventDefault();
          console.log(history, el.getAttribute("href"));
          // Add routing address to address stack
          history.pushState(null."", el.getAttribute("href"));
          console.log("history:", history);
          // Update the router ViewrouterView.innerHTML = location.pathname; })); });// Listen for popState changes in the address, and then switch the contents of the Router View
    window.addEventListener("popstate".() = > {
      routerView.innerHTML = location.pathname;
    });
  </script>
</html>
Copy the code

The history mode is configured on vue-router

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

const router = createRouter({
  history: createWebHistory(),
  routes: [
    / /...],})Copy the code

Routing to intercept

Global front guard
router.beforeEach((to, from, next) = > {
    console.log("to:", to); // Destination routing information
    console.log("from:".from); // The current navigation is about to leave the routing information object
    // Page loading progress
    NProgress.start();
    const token = getToken();
    if(! token && to.name ! = ="login") {
      // Intercept and specify a jump route
      next({
        name: "login"}); }else {
      // Routing continues
      next();
    }
    // Return false to cancel navigation
    // return false 或者 next(false)
  });
  
  / / asynchronous
router.beforeEach(async (to, from) = > {CanUserAccess () returns' true 'or' false '
  return await canUserAccess(to)
 })
Copy the code
Global parsing guard
Global post-hook

Make sure you call the next method or the hook won’t be resolved

Embedded routines by

Declarative navigation

A label

Programmatic navigation

push

Similar to the window. The history. PushState

replace

Similar to the window. The history. ReplaceState

router.push({ path: '/home'.replace: true })
/ / equivalent to
router.replace({ path: '/home' })
Copy the code
go

Similar to the window. The history. Go