VUE3学习(黑马)

224 阅读10分钟

webpack

webpack.config.js

//导入node.js中专门操作路径的模块
const path = require("path");
//导入HTML插件,得到一个构造函数
const HtmlPlugin = require('html-webpack-plugin');
//创建HTML插件的实例对象
const htmlPlugin = new HtmlPlugin({
      //指定原文件的存放路径
      template: './src/index.html',
      //指定生成的文件的存放路径
      filename: './index.html'
})
module.exports = {
  mode: "development",
  //打包入口文件路径
  entry: path.join(__dirname, "./src/index.js"),
  output: {
    //输出文件的存放路径
    path: path.join(__dirname, "./dist"),
    //输出文件的名称
    filename: "bundle.js",
  },
  //通过plugins节点,使htmlPlugin插件生效
  plugins:[htmlPlugin],
  devServer: {
      //初次打包完成后,自动打开浏览器
      open:true,
      //实时打包所使用的主机地址
      host: '127.0.0.1',
      //实时打包所使用的端口号
      port: 80
  }
};


mode的可选值

  • development
    • 开发环境
    • 不会对打包生成的文件进行代码压缩和性能优化
    • 打包速度快,适合在开发阶段使用
  • production
    • 生产环境
    • 会对打包生成的文件进行代码压缩和性能优化
    • 打包速度很慢,仅适合项目发布阶段使用

自定义打包的入口和出口

  • 默认值:
    • 默认的打包入口文件为src->index.js
    • 默认的输出文件路径为dist->main.js
  • 自定义:在webpack.config.js配置文件中,通过entry节点指定打包的入口。通过output节点指定打包的出口
entry: path.join(__dirname, "./src/index.js"),
  output: {
    //输出文件的存放路径
    path: path.join(__dirname, "./dist"),
    //输出文件的名称
    filename: "bundle.js",
  },

webpack插件

  • webpack-dev-server
    • 类似于node.js阶段用到的nodemon工具
    • 每当修改了源代码,webpack会自动进行项目的打包和构建
  • html-webpack-plugin
    • webpack中的HTML插件(类似于一个模板引擎插件)
    • 可以通过此插件自定制index.html页面内容

webpack-dev-server

会启动一个实时打包的http服务器
webpack5和webpack-dev-server@4有兼容问题

  • 安装:npm i webpack-dev-server@3.11.0 -D
  • 配置
    • 修改
    "scripts": {
        "dev": "webpack serve"
      },
    
    • 再运行npm run dev命令,重新进行项目的打包
    • 在浏览器中访问http://localhost:8080地址,查看打包效果

打包生成的文件哪儿去了?

  • 不配置webpack-dev-server的情况下,webpack打包生成的文件,会存放到实际的物理磁盘上
    • 严格遵守开发者在webpack.config.js中指定配置
    • 根据output节点指定路径进行存放
  • 配置webpack-dev-server的情况下,打包生成的文件,会存放到内存上
    • 不再根据output节点指定的路径,存放到实际的物理磁盘上
    • 提高了实时打包输出的性能,因为内存比物理磁盘速度快很多

生成到内存中的文件该如何访问

webpack-dev-server生成到内存中的文件,默认放到了项目的根目录中,而且是虚拟的、看不见的

  • http://localhost:8080/xxx
  • /xxx

html-webpack-plugin

webpack5和html-webpack-plugin@4有兼容问题

  • 安装:npm i html-webpack-plugin@5.5.0 -D
  • 配置:
//导入HTML插件,得到一个构造函数
const HtmlPlugin = require('html-webpack-plugin');
//创建HTML插件的实例对象
const htmlPlugin = new HtmlPlugin({
      //指定原文件的存放路径
      template: './src/index.html',
      //指定生成的文件的存放路径
      filename: './index.html'
})
module.exports = {
  mode: "development",
  //通过plugins节点,使htmlPlugin插件生效
  plugins:[htmlPlugin],
};

  • 通过HTML插件复制到项目根目录中的index.html页面,也被放到了内存中
  • HTML插件在生成index.html页面底部,自动注入了打包的bundle.js文件

devServer节点

在webpack.config.js配置文件中,可以通过devServer节点对webpack-dev-server插件进行更多的配置

devServer: {
      //初次打包完成后,自动打开浏览器
      open:true,
      //实时打包所使用的主机地址
      host: '127.0.0.1',
      //实时打包所使用的端口号
      port: 80
  }

loader

webpack5 loader配置链接 在时间开发过程中,webpack默认只能打包处理以.js后缀名结尾的模块。其他非.js后缀名结尾的模块,webpack默认处理不了,需要调用loader加载器才可以正常打包,否则就会报错


loader加载器的作用

协助webpack打包处理特定的文件模块

  • css-loader 可以打包处理.css相关文件
  • less-loader 可以打包处理.less相关文件
  • babel-loader 可以打包处理webpack无法处理的高级JS语法

image.png


style-loader和css-loader

  • 安装:npm i style-loader css-loader -D
  • 配置:"style-loader", "css-loader"的位置不能写反
module: {
    rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }],
  },
  • use数组中指定的loader顺序是固定的
  • 多个loader的调用顺序是:从后往前调用

less-loader

  • 安装:npm i less-loader less -D
  • 配置: less是内置依赖项,不需要显示调用
module: {
    rules: [{ test: /\.less$/, use: ["style-loader", "css-loader","less-loader"] }],
  },

url-loader和file-loader

在webpack5中file-loader和url-loader已经被弃用,要使用它俩时,将模块类型设置为Javascript/auto 打包处理样式表中与url路径相关的文件

  • 安装:npm i url-loader file-loader -D
  • 配置:file-loader是url-loader的内置依赖项,不需要显示配置
module: {
    rules: [{ test: /\.jpg|png|gif$/, use: 'url-loader?limit=22229' }],
  },

或者

module: {
    rules: [
        {
        test: /\.jpg|png|gif$/,
        use: {
          loader: "url-loader",//通过loader属性指定要调用的loader
          options: {//通过options属性指定参数项
            limit: 22229,
          },
        },
      },
    ],
  },
  • ?后面的limit
    • limit用来指定图片的大小,单位是字节(byte)
    • 只有≦limit大小的图片,才会被转为base64格式的图片

babel-loader

打包处理js文件中的高级语法

  • 安装:npm i babel-loader@8.2.1 @babel/core@7.12.3 @babel/plugin-proposal-class-properties@7.12.1 -D

把js文件统一生成到js目录中

配置:

entry: path.join(__dirname, "./src/index.js"),
  output: {
    //输出文件的存放路径
    path: path.join(__dirname, "./dist"),
    //输出文件的名称
    filename: "js/bundle.js",
  },

把图片统一放到image目录下

module: {
    rules: [
        {
        test: /\.jpg|png|gif$/,
        use: {
          loader: "url-loader",//通过loader属性指定要调用的loader
          options: {//通过options属性指定参数项
            limit: 22229,
          },
          outputPath: 'image'
        },
      },
    ],
  },

自动·清理dist目录下的旧文件

image.png


Source-Map

Source Map 就是一个信息文件,里面存储着位置信息。也就是说,Source Map文件中存储着代码压缩混淆前后的对应关系

问题

开发环境下默认生成的Source Map,记录的是生成后的代码的位置。会导致运行时报错的行数与源代码的行数不一致的问题


解决

eval-source-map仅限在开发模式下使用,不建议在“生产环境”下使用, 此选项生成的Source Map 能够保证“运行时报错的行数”与“源代码的行数”保持一致

module.exports = {
    mode: 'development',
    devtool: 'eval-source-map'
}

webpack生产环境下的Source Map

在生产环境下,如果省略了devtool选项,则最终生成的文件中不包含Source Map。这能防止源代码通过Source Map的形似暴露给别有所图的人

只定位行数不暴露代码

module.exports = {
    mode: 'development',
    devtool: 'nosources-source-map'
}

只定位行数不暴露代码

module.exports = {
    mode: 'development',
    devtool: 'source-map'
}

Vue

image.png

image.png

全局注册和局部注册

全局

import xxx from 'yyy'
app.component('xxx',xxx)

局部

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

export default {
  name: 'App',
  components: {
    xxx
  }
}

组件注册时的大小写

  • kebab-case命名法(短横线命名法):使用时必须使用短横线命名来使用
  • PascalCase命名法(帕斯卡命名法或大驼峰命名法):可以用大驼峰命名法使用,也可以转短横线命名法使用
  • 可以把组件name作为注册时的名称app.component(xxx.name,xxx)

组件样式冲突问题

问题描述 默认情况下,写在.vue组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。导致组件之间样式冲突的根本原因是:

  • SPA(单页面应用),所有组件的DOM结构,都是基于唯一的index.html页面进行展示的
  • 每个组件中的样式,都会影响整个index.html页面中的DOM元素

问题解决 解决原理:为每一个标签分配唯一的自定义属性

image.png

  • style节点的scoped属性,为了提高开发效率和开发体验,vue为style节点提供了scoped属性,从而防止组件之间样式冲突的问题
  • style节点的scoped属性,用来自动为每一个组件分配唯一的自定义属性, 并自动为当前组件的DOM标签和style样式应用这个自定义属性,防止组件的样式冲突
<style scoped>
...
</style>

/deep/样式穿透,加了scoped之后想让某些样式对子组件生效,可以使用/deep/深度选择器

image.png

基本语法

指令

  • 指令绑定:v-on:xxx="方法名",@xxx="方法名"
  • @xxx="语句"
  • 方法如果没有传参,可以接收到参数e,就是事件触发源(就是那个标签,按钮,输入框啥的)
  • $event是vue提供的特殊变量,用来表示原生的事件参数对象event。$event可以解决事件对象event被覆盖的问题

image.png


事件修饰符

使用:

<template>
      <a href="http://www.baidu.com" @click.prevent="onClick">百度</a>
</template>

<script>
export default {
    name: '',
    methods:{
      onClick(){
            window.alert(12)
      }
    }
}
</script>
<style scoped lang='less'>
</style>

image.png


按钮修饰符

在监听键盘事件时,我们经常需要判断详细的按键。此时,可以为键盘相关的事件添加按键修饰符

<template>
      <input type="text" @keyup.enter="onKey">
</template>

<script>
export default {
    name: '',
    methods:{
      onKey(e){
            console.log(e.target.value);
      }
    }
}
</script>
<style scoped lang='less'>
</style>

v-model指令的修饰符

为了方便对用户输入的内容进行处理,vue为v-model提供了三个修饰符:

image.png


v-if和v-show的区别

  • 实现原理不同:

    • v-if指令会动态的创建或移除DOM元素,从而控制元素在页面上的显示和隐藏
    • v-show指令会动态为元素添加或移除style="display:none"的样式,从而控制元素的显示和隐藏
  • 性能消耗不同:v-if有更高的切换开销,而v-show有更高的初始渲染开销

    • 如果需要非常频繁地切换,则使用v-show比较好
    • 如果在运行时条件很少改变,则使用v-if比较好

使用key维护列表的状态

当列表的数据变化时,默认情况下,vue尽可能的复用已存在的DOM元素,从而提升渲染的性能。但这种默认的性能优化策略,会导致有状态的列表无法被正确更新
为了给vue一个提示,以便它能跟踪每个节点的身份,从而在保证有状态的列表被正确更新的前提下,提升渲染的性能。此时,需要为每项提供一个唯一的key属性


key的注意事项

  • key的值只能是字符串或数字类型
  • key的值必须具有唯一性(不重复)
  • 建议把数据项id属性的值作为key值
  • 使用index的值当作key的值没有任何意义(index不具有唯一性)
  • 建议使用v-for指令时一定要指定key的值

props

一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute

props命名

虽然理论上你也可以在向子组件传递 props 时使用 camelCase 形式 (使用 DOM 模板时例外),但实际上为了和 HTML attribute 对齐,我们通常会将其写为 kebab-case 形式:

props声明

  • 字符串数组:props: ['foo']
  • 对象:props: { title: String, likes: Number }

单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。

props验证

export default {
  props: {
    // 基础类型检查
    //(给出 `null` 和 `undefined` 值则会跳过任何类型检查)
    propA: Number,
    // 多种可能的类型
    propB: [String, Number],
    // 必传,且为 String 类型
    propC: {
      type: String,
      required: true
    },
    // Number 类型的默认值
    propD: {
      type: Number,
      default: 100
    },
    // 对象类型的默认值
    propE: {
      type: Object,
      // 对象或者数组应当用工厂函数返回。
      // 工厂函数会收到组件所接收的原始 props
      // 作为参数
      default(rawProps) {
        return { message: 'hello' }
      }
    },
    // 自定义类型校验函数
    propF: {
      validator(value) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // 函数类型的默认值
    propG: {
      type: Function,
      // 不像对象或数组的默认,这不是一个工厂函数。这会是一个用来作为默认值的函数
      default() {
        return 'Default function'
      }
    }
  }
}

计算属性

  • 计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建

自定义事件

在调用 this.$emit() 方法触发自定义事件时,可以通过第2个参数为自定义事件传参,可以通过事件处理函数的形参接收到

watch监听器

值也可以是一个方法名称的字符串 (通过 methods 声明),或包含额外选项的对象。当使用对象语法时,回调函数应被声明在 handler 中。额外的选项包含

export default {
  data() {
    return {
      a: 1,
      b: 2,
      c: {
        d: 4
      },
      e: 5,
      f: 6
    }
  },
  watch: {
    // 侦听根级属性
    a(val, oldVal) {
      console.log(`new: ${val}, old: ${oldVal}`)
    },
    // 字符串方法名称
    b: 'someMethod',
    // deep: true,该回调将会在被侦听的对象的属性改变时调动,无论其被嵌套多深
    c: {
      handler(val, oldVal) {
        console.log('c changed')
      },
      deep: true
    },
    // 侦听单个嵌套属性:
    'c.d': function (val, oldVal) {
      // do something
    },
    // immediate: true,该回调将会在侦听开始之后立即调用
    e: {
      handler(val, oldVal) {
        console.log('e changed')
      },
      immediate: true
    },
    // 你可以传入回调数组,它们将会被逐一调用
    f: [
      'handle1',
      function handle2(val, oldVal) {
        console.log('handle2 triggered')
      },
      {
        handler: function handle3(val, oldVal) {
          console.log('handle3 triggered')
        }
        /* ... */
      }
    ]
  },
  methods: {
    someMethod() {
      console.log('b changed')
    },
    handle1() {
      console.log('handle 1 triggered')
    }
  },
  created() {
    this.a = 3 // => new: 3, old: 1
  }
}

this.$watch()

我们也可以使用组件实例的 $watch() 方法来命令式地创建一个侦听器:

export default {
  created() {
    this.$watch('question', (newQuestion) => {
      // ...
    })
  }
}

停止侦听器

const unwatch = this.$watch('foo', callback)
// ...当该侦听器不再需要时
unwatch()

生命周期

image.png

  • created():在组件实例处理完所有与状态相关的选项后调用。
  • mounted():在组件被挂载之后调用。(第一次被渲染到页面上)
  • unmounted():在一个组件实例被卸载之后调用。
  • updated():在组件因为一个响应式状态变更而更新其 DOM 树之后调用。

image.png

数据共享

父向子 v-bind,props


子向父 自定义事件


父子数据双向绑定 v-model,props,emits:['update:xxx'])


兄弟组件之间进行通话

兄弟组件之间实现数据共享的方案是 EventBus ,可以借助1第三方包 mitt 来创建 eventBus 对象,从而实现兄弟组件之间的数据共享

image.png


后代组件之间的通信 provide和inject

父节点使用 provide 向下共享数据时,可以结合 computed 函数向下共享响应式的数据。

provide(){
   return{
     color:computed(()=>this.color)
   }
}

如果父节点共享的是响应式数据,则子孙节点必须以 .value 的形式进行使用

全局配置axios

main.js 入口文件中,通过 app.config.globalProperties 全局挂载 axios

image.png

ref获取DOM元素

this.$ref.xxx

在父组件的子组件上绑定ref,通过this.$ref.xxx.methods方法调用子组件上的 methods 方法

动态组件

<!-- currentTab 改变时组件也改变 -->
<component :is="currentTab"></component>

keep-alive保持状态

当使用 <component :is="..."> 来在多个组件间作切换时,被切换掉的组件会被卸载。我们可以通过 <KeepAlive> 组件强制被切换掉的组件仍然保持“存活”的状态。

<keep-alive>
  <component :is="comName"></component>
</keep-alive>

插槽

具名插槽

如果在封装组件时需要预留多个插槽节点,则需要为每个<slot>插槽指定具体的name名称。这种带具体名称的插槽叫做具名插槽

没有指定名称的插槽name默认为default

如果我们想在父组件没有提供任何插槽内容时在 <button> 内渲染“Submit”,只需要将“Submit”写在 <slot> 标签之间来作为默认内容:

<button type="submit">
  <slot>
    Submit <!-- 默认内容 -->
  </slot>
</button>

简写:v-slot:header等同于#header


作用域插槽

在封装组件的过程中,可以预留的 <slot> 插槽绑定props数据,这种带有props数据的 <slot> 叫做"作用域插槽"

!!!不一定命名为scope,甚至可以写成解构赋值的形式{xxx,xxx}

<!-- 子 -->
<slot :info="info" :msg="msg"></slot>
<!-- 父 -->
<template v-slot:default="scope">
      <p>{{scope}}</p>
</template>

自定义指令

指令钩子

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

!!! 在vue2中,mounted->bind,updated->update


指令参数

指令的钩子会传递以下几种参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。

  • binding:一个对象,包含以下属性。

    • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。

  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。


全局指令

app.directive('focus',{
      mounted(el){
            el.focus();
      }
})

函数简写

如果mounted和updated函数中的逻辑完全相同,则可以简写为

app.directive('focus',()=>{
     el.focus();
})

Vue-Router

基本使用

安装:npm i vue-router@next -S

  • 新建 router.js
import { createRouter, createWebHashHistory } from "vue-router";

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    { path: "/", redirect: "/home" },
    { path: "/home", component: () => import("./Home.vue") },
    { path: "/msg", component: () => import("./msg.vue") },
  ],
});
export default router
  • main.js中调用
import { createApp } from 'vue'
import App from './App.vue'
import router from './components/vuerouter/router'
const app = createApp(App)
app.use(router)
app.mount('#app')
  • App.vue 中使用
<template>
  <router-link to="/home">home</router-link>
  <router-link to="/msg">msg</router-link>
  <router-view></router-view>
</template>

设置高亮

  • 被激活的路由链接,默认会应用一个叫做router-link-active的类名。开发者可以使用此类名选择器,为激活的路由链接设置高亮效果
  • 在创建路由的实例对象时,开发者可以基于linkActiveClass属性,自定义路由链接被激活时所应用的类名:
const router = createRouter({
  history: createWebHashHistory(),
  linkActiveClass:'xxxx',
  routes: [
    { path: "/", redirect: "/home" },
    { path: "/home", component: () => import("./Home.vue") },
  ],
});
export default router

嵌套路由

//App.vue
<router-link to="/home/home1">home</router-link>
//router.js
{
  path: "/home",
  component: () => import("./Home.vue"),
  //在进入'/home'会重定向到'/home/home1'
  redirect:'/home/home1'
  children: [
    {
      path: "/home1",
      component: () => import("./Home.vue"),
    },
  ],
},

动态路由链接

动态路由指的是:把Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性。在vue-router中使用:来定义路由的参数项

{ path: "/msg/:id", component: () => import("./msg.vue") }

image.png

  • 使用props接收动态路由参数:
    • 声明props:true:{ path: "/msg/:id", component: () => Msg, props: true },
    • 组件内接收:props:['id']

编程式导航

  • this.$router.push('hash地址')
  • this.$router.go(数值n)

this.$router.push

// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path

const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user

this.$router.replace

router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })

this.$router.go

// 向前移动一条记录,与 router.forward() 相同
router.go(1)

// 返回一条记录,与 router.back() 相同
router.go(-1)

// 前进 3 条记录
router.go(3)

// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)

命名路由

<router-link :to="{ name: 'user', params: { username: 'erina' }}">
  User
</router-link>

等同于

router.push({ name: 'user', params: { username: 'erina' } })

命名视图

//App.vue
<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>
//router.js
const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },
    },
  ],
})

导航守卫

image.png

//router.js
router.beforeEach((to,from,next)=>{
      ...
})
  • to:目标路由对象
  • from:当前导航正要离开的路由对象
  • next:next是一个函数,表示放行
    • 不声明next,则默认允许用户访问每一个路由
    • 声明next,则必须调用 next() 函数,否则不允许用户访问任何一个路由
      • 直接放行:next()
      • 强制停留在当前页面:next(false)
      • 强制跳转到登陆页面:next('/xxx')

不声明next,使用return进行是否跳转的判断

  • false:取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址
  • 路由地址:通过一个路由地址跳转到一个不同的地址,就像你调用 router.push() 一样,你可以设置诸如 replace: truename: 'home' 之类的配置。当前的导航被中断,然后进行一个新的导航,就和 from 一样。
  • 如果什么都没有,undefined 或返回 true,则导航是有效的,并调用下一个导航守卫
router.beforeEach(async (to, from) => {
   if (
     // 检查用户是否已登录
     !isAuthenticated &&
     // ❗️ 避免无限重定向
     to.name !== 'Login'
   ) {
     // 将用户重定向到登录页面
     return { name: 'Login' }
   }
 })

路由元信息

有时,你可能希望将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的 `meta` 属性来实现,并且它可以在路由地址和导航守卫上都被访问到。定义路由的时候你可以这样配置 `meta` 字段

声明

//router.js
const router = createRouter({
  routes: [
    {
      path: "/home",
      component: () => import("./Home.vue"),
      meta:{isActive:true}
    },
  ],
});
export default router;

访问

//Home.vue
<p>{{$route.meta.isActive}}</p>

组合式API(setup)

import { useRouter, useRoute } from 'vue-router'

export default {
  setup() {
    const router = useRouter()
    const route = useRoute()

    function pushWithQuery(query) {
      router.push({
        name: 'search',
        query: {
          ...route.query,
        },
      })
    }
  },
}