十四、使用 Vue Router 开发单页应用(4)

news2024/11/18 5:49:05

本周概要

  • 导航守卫
    • 全局守卫
    • 路由独享的守卫
    • 组件内守卫
    • 导航解析流程

14.10 导航守卫

在 14.4 嵌套路由 小节中已经使用过一个组件内的导航守卫:beforeRouteUpdate 。Vue Router 提供的导航守卫主要用于在导航过程中重定向或取消路由,或添加权限验证、数据获取等业务逻辑。
导航守卫分为3类:全局守卫、路由独享的守卫、组件内守卫,可以用于路由导航过程中的不同阶段。每一个导航守卫都有两个参数:to 和 from ,其含义已在 14.4 嵌套路由 小节介绍过,此处不再赘述。

14.10.1 全局守卫

全局守卫分为全局前置守卫、全局解析守卫和全局后置钩子。

  1. 全局前置守卫

当一个导航触发时,全局前置守卫按照创建的顺序调用。守卫可以是异步解析执行,此时导航在所有守卫解析完之前一直处于挂起状态。全局前置守卫使用 router.beforeEach() 注册。代码如下所示:

const router = createRouter({...})
router.beforeEach((to,from) => {
  // ...
  // 显式返回 false 以取消导航
  return false;
})

除了返回 false 取消导航外,还可以返回一个路由位置对象,这将导致路由重定向到另一个位置,如同正在调用 router.push() 方法一样,可以传递诸如 replace:true 或 name:‘home’ 之类的选项。返回路由位置对象时,将删除当前导航,并使用相同的 from 创建一个新的导航。

如果遇到意外情况,也可能抛出一个 Error 对象,这也将取消导航并调用通过 router.onError() 注册的任何回调。
如果没有任何返回值、undefined 或 true ,则验证导航,并调用下一个导航守卫。
上面所有的工作方式都与异步函数和 Promise 相同。例如:

router.beforeEach(async (to,from) => {
  // canUserAccess() 返回 true 或 false
  return await canUserAccess(to)
})
  1. 全局解析守卫

全局解析守卫使用 router.beforeResolve() 注册。它和 router.beforeEach() 类似,区别在于,在导航被确认之前,在所有组件内守卫和异步路由组件被解析之后,解析守卫被调用。
下面的例子用于确保用户已经定义了自定义 meta 属性 requiresCamera 的路由提供了对相机的访问。

router.beforeResolve(async to => {
  if(to.meta.requiresCamera){
    try {
      await askForCameraPermission()
    } catch (error) {
      // ... 处理错误,然后取消导航
      return false
    }else{
    	// ...意外错误,取消导航并将错误传递给全局处理程序
    	throw error
    }
  }
})
  1. 全局后置钩子

全局后置钩子使用 router.afterEach() 注册,它在导航被确认之后调用。

router.afterEach((to,from) => {
  sendToAnalytics(to.fullPath)
})

与守卫不同的是,全局后置钩子不接受可选的 next() 函数,也不会改变导航。
全局后置钩子对于分析、更改页面标题、可访问性功能(如发布页面)和许多其他功能都非常有用。
全局后置钩子还可以接受一个表示导航失败的 failure 参数,作为第 3 个参数。代码如下:

router.afterEach((to,from,failure) => {
  if(!failure){
    sendToAnalytics(to.fullPath)
  }
})
  1. 实际应用

下面利用全局守卫来解决两个实际开发中的问题。
(1)登录验证
第一个问题是登录验证。对于受保护的资源,需要用户登录后才能访问,如果用户没有登录,那么就将用户导航到登录页面。为此,可以利用全局前置守卫来完成用户登录与否的判断。
继续前面的例子,在components 目录下新建 Login.vue。如下:

<template>
    <div>
        <h3>{{ info }}</h3>
        <table>
            <caption>用户登录</caption>
            <tbody>
                <tr>
                    <td><label>用户名:</label></td>
                    <td><input type="text" v-model.trim="username"></td>
                </tr>
                <tr>
                    <td><label>密码:</label></td>
                    <td><input type="password" v-model.trim="password"></td>
                </tr>
                <tr>
                    <td cols="2">
                        <input type="submit" value="登录" @click.prevent="login" />
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</template>
<script>
export default {
    data() {
        return {
            username: "",
            password: "",
            info: ""   //用于保存登录失败后的提示信息
        }
    },
    methods: {
        login() {
            //实际场景中,这里应该通过Ajax向服务端发起请求来验证
            if ("lisi" == this.username && "1234" == this.password) {
                //sessionStorage中存储的都是字符串值,因此这里实际存储的将是字符串"true"
                sessionStorage.setItem("isAuth", true);
                this.info = "";
                //如果存在查询参数
                if (this.$route.query.redirect) {
                    let redirect = this.$route.query.redirect;
                    //跳转至进入登录页前的路由
                    this.$router.replace(redirect);
                } else {
                    //否则跳转至首页
                    this.$router.replace('/');
                }
            }
            else {
                sessionStorage.setItem("isAuth", false);
                this.username = "";
                this.password = "";
                this.info = "用户名或密码错误";
            }

        }
    }
}
</script>

修改路由配置文件 index.js ,如下:

import {createRouter, createWebHistory} from 'vue-router'
//import Home from '@/components/Home'
import News from '@/components/News'
import Books from '@/components/Books'
import Videos from '@/components/Videos'
import Book from '@/components/Book'
import Login from '@/components/Login'

const router = createRouter({
  //history: createWebHashHistory(),
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      redirect: {
        name: 'news'
      }
    },
    {
      path: '/news',
      name: 'news',
      component: News,
    },
    {
      path: '/books',
      name: 'books',
      component: Books,
      /*
      children: [
        {path: '/book/:id', name: 'book', component: Book, props: true}
      ]*/
    },
    {
      path: '/videos',
      name: 'videos',
      component: Videos,
    },
    {
      path: '/book/:id',
      name: 'book',
      components: {bookDetail: Book},
    },
    {
      path: '/login',
      name: 'login',
      component: Login,
    }
  ]
})

router.beforeEach(to => {
  //判断目标路由是否是/login,如果是,则直接返回true
  if(to.path == '/login'){
    return true;
  }
  else{
    //否则判断用户是否已经登录,注意这里是字符串判断
    if(sessionStorage.isAuth === "true"){
      return true;
    } 
    //如果用户访问的是受保护的资源,且没有登录,则跳转到登录页面
    //并将当前路由的完整路径作为查询参数传给Login组件,以便登录成功后返回先前的页面
    else{
      return {
        path: '/login',
        query: {redirect: to.fullPath}
      }
    }
  }
})

router.afterEach(to => {
  document.title = to.meta.title;
})

export default router

需要注意的是:代码中的 if(to.path == ‘/login’){ return true } 不能缺少,如果写成下面的代码,会造成死循环。

router.beforEach( to => {
  if(sessionStorage.isAuth === 'true'){
    return true;
  }else{
    return {
      path:'/login',
      query:{redirect:to.fullPath}
    }
  }
} )

例如初次访问 /news ,此时用户还没有登录,条件判断为 false,进入 else 语句,路由跳转到 /login ,然后又执行 router.beforeEach() 注册全局前置守卫,条件判断依然为 false,再次进入 else 语句,最后导致页面死掉。

为了方便访问登录页面,可以在 App.vue 中添加一个登录的导航链接。如下:

<router-link to:"{name:'login'}">登录</router-link>

完成上述修改后,运行项目。出现登录页面后,输入正确的用户名(lisi)和密码(1234),看看路由的跳转,如下:
在这里插入图片描述

在这里插入图片描述

之后输入错误的用户名和密码,再看看路由的跳转,如下:
在这里插入图片描述

(2)页面标题
下面解决第二个问题,就是路由跳转后的页面标题问题。因为在单页应用程序中,实际只有一个页面,因此在页面切换时,标题不会发生改变。

在定义路由时,在 routes 配置中的每个路由对象(也称为路由记录)都可以使用一个 meta 字段来为路由对象提供一个元数据信息。我们可以为每个组件在它的路由记录里添加 meta 字段,在该字段中设置页面的标题,然后在全局后置钩子中设置目标路由页面的标题。

全局后置钩子是在导航确认后,DOM 更新前调用,因此在这个钩子中设置页面标题是比较合适的。
修改路由配置文件 index.js 。如下:

import {createRouter, createWebHistory} from 'vue-router'
//import Home from '@/components/Home'
import News from '@/components/News'
import Books from '@/components/Books'
import Videos from '@/components/Videos'
import Book from '@/components/Book'
import Login from '@/components/Login'

const router = createRouter({
  //history: createWebHashHistory(),
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      redirect: {
        name: 'news'
      }
    },
    {
      path: '/news',
      name: 'news',
      component: News,
      meta: {
        title: '新闻'
      }

    },
    {
      path: '/books',
      name: 'books',
      component: Books,
      meta: {
        title: '图书列表'
      }
      /*
      children: [
        {path: '/book/:id', name: 'book', component: Book, props: true}
      ]*/
    },
    {
      path: '/videos',
      name: 'videos',
      component: Videos,
      meta: {
        title: '视频'
      }
    },
    {
      path: '/book/:id',
      name: 'book',
      meta: {
        title: '图书'
      },
      components: {bookDetail: Book},
    },
    {
      path: '/login',
      name: 'login',
      component: Login,
      meta: {
        title: '登录'
      }
    }
  ]
})

router.afterEach(to => {
  document.title = to.meta.title;
})

export default router

运行项目,此时每个页面有自己的标题。如下:
在这里插入图片描述

在这里插入图片描述

meta 字段也可以用于对有限资源的保护,在需要保护的路由对象中添加一个需要验证属性,然后在全局前置守卫中判断,如果访问的是受保护的资源,继续判断用户是否已经登录,如果没有,则跳转到登录页面。如下:

{
  path: '/videos',
  name: 'videos',
  component: Videos,
  meta: {
    title: '视频',
    requiresAuth:true
  }
}

在全局前置守卫中进行判断(index.js)。如下:

import { createRouter, createWebHistory } from 'vue-router'
//import Home from '@/components/Home'
import News from '@/components/News'
import Books from '@/components/Books'
import Videos from '@/components/Videos'
import Book from '@/components/Book'
import Login from '@/components/Login'

const router = createRouter({
  //history: createWebHashHistory(),
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      redirect: {
        name: 'news'
      }
    },
    {
      path: '/news',
      name: 'news',
      component: News,
      meta: {
        title: '新闻'
      }

    },
    {
      path: '/books',
      name: 'books',
      component: Books,
      meta: {
        title: '图书列表'
      }
      /*
      children: [
        {path: '/book/:id', name: 'book', component: Book, props: true}
      ]*/
    },
    {
      path: '/videos',
      name: 'videos',
      component: Videos,
      meta: {
        title: '视频',
        requiresAuth: true
      }
    },
    {
      path: '/book/:id',
      name: 'book',
      meta: {
        title: '图书'
      },
      components: { bookDetail: Book },
    },
    {
      path: '/login',
      name: 'login',
      component: Login,
      meta: {
        title: '登录'
      }
    }
  ]
})

router.beforeEach(to => {
  // 判断该路由是否需要登录权限
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 路由需要验证,判断用户是否已经登录
    if (sessionStorage.isAuth === "true") {
      return true;
    } else {
      return {
        path: '/login',
        query: { redirect: to.fullPath }
      }
    }
  } else {
    return true
  }
})

router.afterEach(to => {
  document.title = to.meta.title;
})

export default router

路由位置对象的 matched 属性是一个数组,包含了当前路由的所有嵌套路径片段的路由记录。

重启项目,此时访问首页没问题,如下:
在这里插入图片描述

但是访问视频则需要登录,如下:
在这里插入图片描述

登录之后,显示如下:
在这里插入图片描述

14.10.2 路由独享的守卫

路由独享的守卫是在路由的配置对象中直接定义的 beforeEnter 守卫。代码如下所示:

const routes = [
  {
    path:'/users/:id',
    component:UserDetails,
    beforeEnter:( to,from ) => {
      // reject the navigation
      return false;
    }
  }
]

beforeEnter 守卫在全局前置守卫调用后,只在进入路由时触发,他们不会再参数、查询参数或 hash 发生变化时触发。
例如,从 /user/2 到 /user/3 ,或者从 /user/2#info 到 /user/2#project ,均不会触发 beforeEnter 守卫。beforeEnter 守卫只有在从不同的路由导航过来时才会触发。
也可以给 beforeEnter 传递一个函数数组,这在为不同的路由复用守卫时很有用。代码如下:

function removeQueryParams(to){
  if (Object.key(to.query).length) {
    return {
      path:to.path,
      query:{},
      hash:to.hash
    }
  }
}
function removeHash (to){
  if(to.hash) {
    return {
      path:to.path,
      query:to.query,
      hash:''
    }
  }
}
const routes = [
  {
    path:'/users/:id',
    component:UserDetails,
    beforeEnter:[removeQueryParams,removeHash]
  },
  {
    path:'/about',
    component:UserDetails,
    beforeEnter:[removeQueryParams]
  }
]

14.10.3 组件内守卫

在 14.4 节 中使用的 beforeRouteUpdate 守卫就是组件内守卫。除此之外,还有两个组件内守卫:beforeRouteEnter 和 beforeRouteLeave。

const UserDetails = {
  template : '...',
  beforeRouteEnter(to,from){
    // 在渲染该组件的路由被确认之前调用
    // 不能通过 this 访问组件实例,因为在守卫执行前,组件实例还没有被创建
  },
  beforeRouteUpdate(to,from){
    // 在渲染该组件的路由,但是在该组件被复用时调用
    // 例如,对于一个带参数的路由 /users/:id,在 /user/1 和 /user/2 之间跳转时
    // 相同的 UserDetails 组件实例将会被复用,而这个守卫就会在这种情况下被调用
    // 可以访问组件实例的 this
  }
  beforeRouteLeave(to,from){
  	// 导航即将离开该组件的路由时调用
  	// 可以访问组件实例的 this
  }
}

beforeRouteEnter 守卫不能访问 this,因为该守卫是在导航确认钱被调用,这是新进入的组件基本还没有创建。
但是,可以通过向可选的 next() 函数参数传递一个回调来访问实例,组件实例将作为参数传递给回调。当导航确认后会执行回调,而这个时候,组件实例已经创建完成。如下:

beforeRouteEnter(to,from,next){
  next(vm => {
    // 通过 vm 访问组件实例
  })
}

需要注意的是,beforeRouteEnter 是唯一支持将回调传递给 next() 函数的导航守卫。对于 beforeRouteUpdate 和 beforeRouteLeave ,由于this 已经可用,因此不需要传递回调,自然也就没必要支持想 next() 函数传递回调了。
下面利用 beforeRouteEnter 的这个机制,修改 Book.vue ,将 created 钩子用 beforeRouteEnter 守卫替换。如下:

Book.vue

<template>
    <p> 图书ID:{{ book.id }} </p>
    <p> 标题:{{ book.title }} </p>
    <p> 描述:{{ book.desc }} </p>
</template>

<script>
import Books from '@/assets/books'
import { onBeforeRouteUpdate } from 'vue-router';
export default {
    data() {
        return {
            book: {}
        }
    },
    // created() {
    //     this.book = Books.find((item) => item.id == this.$route.params.id);
    //     this.$watch(
    //         () => this.$route.params,
    //         (toParams) => {
    //             console.log(toParams)
    //             this.book = Books.find((item) => item.id == toParams.id);
    //         }
    //     )
    // }
    methods:{
        setBook(book){
            this.book = book;
        }
    },
    beforeRouteEnter (to,from,next){
        let book = Books.find((item) => item.id == to.params.id);
        next (vm => vm.setBook(book));
    },
    beforeRouteUpdate(to){
        this.book = null;
        this.book = Books.find((item) => item.id == to.params.id)
    }
}
</script>

beforeRouteLeave 守卫通常用来防止用户在还未保存修改前突然离开,可以通过返回 false 取消导航。如下:

beforeRouteLeave (to,from){
  const answer = window.confirm('Do you really want to leave ? you have unsaved changes!');
  if(!answer){
    return false;
  }
}

14.10.4 导航解析流程

完整的导航解析流程如下:

  1. 导航被触发
  2. 在失活的组件中调用 beforeRouteLeave 守卫
  3. 调用全局的 beforeEach 守卫
  4. 在复用的组件中调用 beforeRouteUpdate 守卫
  5. 调用路由配置中的 beforeEnter 守卫
  6. 解析异步路由组件
  7. 在被激活的组件中调用 beforeRouteEnter 守卫
  8. 调用全局的 beforeResolve 守卫
  9. 导航被确认
  10. 调用全局的 afterEach 钩子
  11. 触发 DOM 更新
  12. 用创建好的实例调用 beforeRouteEnter 守卫传给 next() 函数的回调函数

可以在 14.1.1 小节中的 routes.html 页面添加所有的导航守卫,利用 console.log() 语句输出守卫信息,然后观察一下各个守卫调用的顺序,就能更好的理解守卫调用的时机。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77863.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python基于用户画像和协同过滤实现电影推荐系统

1、概要 传统电影推荐系统大多使用协同过滤算法实现电影推荐&#xff0c;主要实现机理是通过用户评分及用户观影历史数据抽象为多维向量利用欧式距离或其他向量计算公式实现推荐&#xff0c;本文中将采用常用的机器学习算法Kmeans聚类算法协同过滤算法word2vec搜索推荐模型多模…

【猿如意】MySQL的下载、安装、使用,这一文足够了~

大家好&#xff0c;我是笑小枫&#xff0c;本篇文章为大家分享一个好用的工具-【猿如意】 对于这个工具呢&#xff0c;怎么说呢&#xff1f;简单点就是&#xff1a;不经意间回首&#xff0c;放眼望去&#xff0c;满眼是你~ 下面就已Mysql下载安装的过程来和大家一起体验下我们的…

【代码审计-.NET】基于.NET框架开发的代码审计

目录 一、审计方法 1、从黑盒到白盒 2、白盒审计 3、灰盒审计 二、审计过程 1、功能点追踪 2、功能函数 3、目录扫描 4、getshell 5、安全模块未引用 6、自动扫描工具 一、审计方法 1、从黑盒到白盒 --->从一个网站前端 --->查看其页面的源代码&#xff0c;并分…

【日常系列】LeetCode《20·数据结构设计》

数据规模->时间复杂度 <10^4 &#x1f62e;(n^2) <10^7:o(nlogn) <10^8:o(n) 10^8<:o(logn),o(1) 内容 lc 155 【剑指 30】【top100】&#xff1a;最小栈 https://leetcode.cn/problems/min-stack/ 提示&#xff1a; -2^31 < val < 2^31 - 1 pop、top 和…

快看梅西射门了,这是梅西的大力抽射~阿根廷加油,我们是冠军

&#x1f5b1; ⌨个人主页&#xff1a;Love And Program的个人主页 &#x1f496;&#x1f496;如果对你有帮助的话希望三连&#x1f4a8;&#x1f4a8;支持一下博主 来自梅西的大力抽射&#x1f970;致昨晚的梅西思路加入阿根廷元素加入足球元素源码致昨晚的梅西 昨晚上阿根廷…

springcloud 从头开始构建分布式微服务脚手架

必备服务&#xff08;Windows开发本机环境&#xff09; Java maven mysql&#xff1a;自启动服务&#xff0c;后台运行 127.0.0.1:3306 MySQL57 root/root Redis&#xff1a;手动运行&#xff0c;前台运行 127.0.0.1:6379 执行命令redis-server.exe redis.windows.conf na…

Weblogic漏洞 - 通杀方式

文章目录简介恶意文件把恶意文件部署到攻击机&#xff0c;并开启http服务写入文件写入反弹shell命令执行反弹shell命令拿到目标机器权限[linux] WebLogic Server 版本: 12.2.1.3简介 最早在 CVE-2019-2725 被提出&#xff0c;对于所有Weblogic版本均有效。 构造一个XML文件&…

CALC-python和shell对字符的解析差异

好久没看题了&#xff0c;记录一道感觉还挺有意思的题目 一进去题目界面非常简洁&#xff0c;一个计算器 这个简洁的界面&#xff0c;好像似曾相识&#xff0c;总感觉好像以前做题时遇到的ssti题目的界面&#xff0c;果断来一波ssti emem有WAF&#xff0c;尝试下绕过&#xff…

C++ 实现守护进程

文章目录1.守护进程概念1.什么是守护进程2.守护进程的特点3.如何查看linux系统中已存在的守护进程2.守护进程编写的步骤3.示例1.守护进程概念 1.什么是守护进程 Linux Deamon守护进程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或者等待处理某些事…

系统小工具API接口,免费好用

1、前言 系统小工具接口&#xff0c;可生成长短不重复id&#xff0c;可获取服务器标准时间。 查看接口完整信息&#xff1a;https://www.idmayi.com/doc/detail?id23 2、接口明细 注意&#xff1a;app_id和app_secret是临时秘钥&#xff0c;如果真正使用&#xff0c;需要去 ht…

ArrayList源码解析

ArrayList源码解析 简介 ArrayList 是一种以数组实现的 List&#xff0c;与数组相比&#xff0c;它具有动态扩展的能力&#xff0c;因此也可称之为动态数组。 ArrayList 线程不安全&#xff0c;除该类未实现同步外&#xff0c;其余跟 Vector 大致相同。 Java 泛型只是编译器…

赛先生的新旅行:昇腾AI带来的科学智能变革

《三体》动画即将开播&#xff0c;又一次引发了全球科幻迷的无限热情。提到《三体》&#xff0c;大家有很多深刻记忆。其中之一是三体人在入侵地球之前&#xff0c;首先是派智子锁死地球的基础科学进步&#xff0c;从而限制人类的整体发展。很多人会担心&#xff0c;物理、化学…

LeetCode题解12 (146,97) LRU缓存<HashMap + 双向链表>,二叉树的中序遍历

文章目录LRU缓存(146)完整代码解答:二叉树的中序遍历(94)完整代码解答:LRU缓存(146) 从题上看存在Key和Value因此我们采用HashMap来进行存储,这里我们采用HashMap双向链表来实现,我们需要实现里面的get,put方法。 我们需要先创建1个链表节点 //先定义一个节点类 class ListNode…

一个合格的程序员也是一名合格的侦探---Debug篇

调试 文章目录调试1.什么是bug&#xff1f;1.1bug概念1.2bug的起源2.什么是调试&#xff0c;调试的重要性2.1调式的概念2.2 调试的基本步骤2.3Debug版本和Release版本2.3.1Debug版本2.3.2Release版本2.3.3区别2.3.4同一代码在Debug和Release下的差别3.Windows下visual stdio的调…

ioDraw - 免费的在线图表制作工具

介绍&#xff1a; ioDraw是一款数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表&#xff0c;支持折线图、柱状图、饼图、散点图等 地址&#xff1a;https://www.iodraw.com/chart 特点&#xff1a; 图表…

springboot实现License证书的授权和许可到期验证

前言 在客户服务器部署软件项目后&#xff0c;为了项目版权管控或者对项目进行授权收费处理的&#xff0c;就需要实现项目的授权和许可验证。 在这里讲解的是使用 license证书 的形式实现授权和许可验证&#xff08;已通过测试&#xff09;。 主要是通过 IP地址、MAC地址、CP…

PyTorch - 线性回归

文章目录普通实现准备数据反向传播构建模型 实现实例化模型、损失函数、优化器训练数据评估模型普通实现 准备数据 import torch import matplotlib.pyplot as plt # 1、准备数据 # y 2 * x 0.8 x torch.rand([500, 1]) y_true 2 * x 0.8 # 2、通过模型计算 y_predict …

MyBatis面试题(2022最新版)

整理好的MyBatis面试题库&#xff0c;史上最全的MyBatis面试题&#xff0c;MyBatis面试宝典&#xff0c;特此分享给大家 MyBatis简介 MyBatis是什么&#xff1f; MyBatis 是一款优秀的持久层框架&#xff0c;一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它…

Kotlin 开发Android app(二十一):协程launch

什么是协程&#xff0c;这可是这几年才有的概念&#xff0c;我们也不用管它是什么概念&#xff0c;先看看他能做什么。 创建协程 添加依赖&#xff1a; implementation org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9implementation org.jetbrains.kotlinx:kotlinx-cor…

DCDC电感下方铜箔如何处理

挖&#xff1a;电感在工作时&#xff0c;其持续变化的电流产生的电磁波会或多或少的泄露出来&#xff0c;电感下方的铜箔受电磁波影响&#xff0c;就会有涡流出现&#xff0c;这个涡流&#xff0c;①可能对线路板上的信号线有干扰&#xff0c;②铜箔内的涡流会产生热量&#xf…