vue学习记录十四:路由(router)学习(五):Vue Router基本原理实现第二种方法

news2024/11/15 15:46:26

vue学习记录十四:路由(router)学习(四):Vue Router基本原理实现二

  • 一、vueRouter目录结构
  • 二、模拟vueRouter目录结构
  • 三、index.js文件实现
  • 四、install.js文件实现
  • 五、创建组件
  • 六、解析路由规则
  • 七、match实现
  • 八、历史记录处理
  • 九、Init方法实现
  • 十、设置响应式的_route
  • 十一、\$route/\$router创建
  • 十二、Router-View创建

一、vueRouter目录结构

在这里插入图片描述
components目录下面,有两个文件。分别为link.jsview.js文件。

link.js文件创建RouterLink组件

view.js文件创建RouterView组件。

history目录下的文件是记录路由的历史记录(hash.js文件是关于hash模式,html5.js关于html5的方式,base.js公共的内容,abstract.js是在服务端渲染中实现的路由历史记录)。

index.js文件是用来创建VueRouter

install.js文件是关于install方法

二、模拟vueRouter目录结构

创建一个myvueRouter文件夹,创建index.js

三、index.js文件实现

//导出这个MyvueRouter类
export default class MyvueRouter{
  //在创建MyvueRouter对象的时候,会传递选项
  constructor(options) {
  //options是创建路由实例传递的对象
    //获取routes选项,该选项中定义路由规则
    //this是router实例 
    this._options = options.routes || [];
  }
  // 注册路由变化的事件。该方法的参数是一个Vue实例,后期完善
  init(Vue) {}
}

四、install.js文件实现

export let _Vue = null; //将其导出,在其它文件中也可以使用Vue实例,而不需要单独的引入Vue的js文件
export default function install(Vue) {
  //获取Vue构造函数
  _Vue = Vue;
  //全局混入
  //所有vue实例都会执行全局混入
  _Vue.mixin({
    //通过混入以后,所有的Vue实例中都会有beforeCreate方法
    beforeCreate() {
      //判断是否为Vue的实例,如果条件成立为Vue的实例,否则为其它对应的组件(因为在创建Vue实例的时候会传递选项)
      //$options就是创建vue实例传递的选项,里面就有router的
      //new Vue({
              //router,
             //  render:h=>h(App)
             // }).$mount('#apps')
             //判断是否是vue实例
      if (this.$options.router) {
        //通过查看源码发现,Vue的实例会挂在到当前的私有属性_routerRoot属性上
        //this是vue实例,把vue实例挂载到_routerRoot属性上
        this._routerRoot = this;
        //this.$options.router获取的是路由实例
        //this._router就是路由实例
        this._router = this.$options.router;
        //调用index.js文件中定义的init方法
        this._router.init(this);
      } else {
      //不是vue实例是组件的话
      //this.$parent就是根组件,
        this._routerRoot = this.$parent && this.$parent._routerRoot;
      }
    },
  });
}

之后需要将install方法挂载到VueRouter上。

import install from "./install";
export default class VueRouter {
  //在创建VueRouter对象的时候,会传递选项
  constructor(options) {
    //获取routes选项,该选项中定义路由规则
    this._routes = options.routes || [];
  }
  // 注册路由变化的事件。
  init(Vue) {}
}
//将install方法挂载到VueRouter上
VueRouter.install = install;

五、创建组件

在view.js文件里面

export default {
  render(h) {
    return h("div", "router-view");
  },
};

以上是Router-View组件的基本功能
link.js文件的实现如下:

export default {
  props: {
    to: {
      type: String,
      required: true,
    },
  },
  render(h) {
      //通过插槽获取`a`标签内的文本。
    return h("a", { domProps: { href: "#" + this.to } }, [this.$slots.default]);
  },
};

install.js文件中,导入上面的组件进行测试。

import View from "./components/view";
import Link from "./components/link";
export let _Vue = null; //将其导出,在其它文件中也可以使用Vue实例,而不需要单独的引入Vue的js文件
export default function install(Vue) {
  //获取Vue构造函数
  _Vue = Vue;
  _Vue.mixin({
    //通过混入以后,所有的Vue实例中都会有beforeCreate方法
    beforeCreate() {
      //判断是否为Vue的实例,如果条件成立为Vue的实例,否则为其它对应的组件(因为在创建Vue实例的时候会传递选项)
      if (this.$options.router) {
        //通过查看源码发现,Vue的实例会挂在到当前的私有属性_routerRoot属性上
        this._routerRoot = this;

     
        this._router = this.$options.router;
        //调用index.js文件中定义的init方法
        this._router.init(this);
      } else {
    
        this._routerRoot = this.$parent && this.$parent._routerRoot;
      }
    },
  });
    //完成组件的注册
  Vue.component("RouterView", View);
  Vue.component("RouterLink", Link);
}

在上面的代码中,导入组件,并且完成组件的注册。

之后进行测试

src目录下,在router.js文件中导入自己定义的VueRouter.

import Router from "./my-vue-router";

六、解析路由规则

在源码的index.js文件中,创建了VueRouter类,对应的构造方法中,有如下代码:

this.matcher = createMatcher(options.routes || [], this)

createMatcher方法是在create-matcher.js文件中创建的。

该方法返回的matcher 就是一个匹配器,其中有两个成员,match,另外一个是addRoutes

match:根据路由地址匹配相应的路由规则对象。

addRoutes动态添加路由

首先在我们自己的index.js文件中添加如下的代码:

import install from "./install";
import createMatcher from "./create-matcher";
export default class VueRouter {
  //在创建VueRouter对象的时候,会传递选项
  constructor(options) {
    //获取routes选项,该选项中定义路由规则
    this._routes = options.routes || [];
    this.matcher = createMatcher(this._routes);
  }
  // 注册路由变化的事件。
  init() {}
  //init(Vue){}
}
//将install方法挂载到VueRouter上
VueRouter.install = install;

在上面的代码中,导入了createMatcher方法。

并且在调用该方法的时候传递了路由规则。

create-matcher.js 文件的代码如下:

import createRouteMap from "./create-route-map";
export default function createMatcher(routes) {
  
  const { pathList, pathMap } = createRouteMap(routes);
  function match() {}
  function addRoutes(routes) {
 
    createRouteMap(routes, pathList, pathMap);
  }
  return {
    match,
    addRoutes,
  };
}

下面,我们需要在create-route-map.js 文件中实现createRouteMap这个方法。

export default function createRouteMap(routes, oldPathList, oldPathMap) {
  const pathList = oldPathList || [];
  const pathMap = oldPathMap || {};
  //遍历所有的路由规则,进行解析。同时还要考虑children的形式,
  //所以这里需要使用递归的方式。
  routes.forEach((route) => {
    addRouteRecord(route, pathList, pathMap);
  });

  return {
    pathList,
    pathMap,
  };
}

//解析路由规则
function addRouteRecord(route, pathList, pathMap, parentRecord) {
  //从路由规则中获取path。
  //parentRecord 父路由
  //拼接父路由
  //父路由不存在
  //就直接写子路由
  const path = parentRecord ? `${parentRecord.path}/${route.path}` : route.path;
  //构建记录
  const record = {
    path,
    component: route.component,
    parent: parentRecord,
     //如果是子路由的话,记录子路由对应的父record对象(该对象中有path,component),相当于记录了父子关系
  };
  //如果已经有了path,相同的path直接跳过
  if (!pathMap[path]) {
    pathList.push(path);
    pathMap[path] = record;
  }
  //判断route中是否有子路由
  if (route.children) {
    //遍历子路由,把子路由添加到pathList与pathMap中。
    route.children.forEach((childRoute) => {
      addRouteRecord(childRoute, pathList, pathMap, record);
    });
  }
}

下面测试一下上面的代码。

import createRouteMap from "./create-route-map";
export default function createMatcher(routes) {

  const { pathList, pathMap } = createRouteMap(routes);
  console.log("pathList==", pathList);
  console.log("pathMap==", pathMap);
  function match() {}
  function addRoutes(routes) {
   
    createRouteMap(routes, pathList, pathMap);
  }
  return {
    match,
    addRoutes,
  };
}

在上面的代码中,我们打印了pathListpathMap.

当然,现在在我们所定义的路由规则中,还没有添加children,构建相应的子路由。下面重新修改一下。

在项目根目录下的router.js文件中,添加对应的子路由规则。

import Vue from "vue";
// import Router from "vue-router";
// import Router from "./vuerouter";
import Router from "./my-vue-router";
import Login from "./components/Login.vue";
import Home from "./components/Home.vue";
import About from "./components/About.vue";
import Users from "./components/Users";
Vue.use(Router);
export default new Router({
  // model: "history",
  routes: [
    { path: "/", component: Home },
    { path: "/login", component: Login },
    {
      path: "/about",
      component: About,
      children: [{ path: "users", component: Users }],
    },
  ],
});

这时候可以查看对应的输出结果。

七、match实现

create-matcher.js文件中,我们实现了createRouteMap方法,同时还需要实现match方法。

match方法的作用就是根据路由地址,匹配一个路由对象。其实就是从pathMap中根据路由地址,找出对应的路由记录。路由记录中记录了组件信息,找到以后就可以完成组件的创建,渲染了。

function match(path) {
    const record = pathMap[path];
    if (record) {
      //根据路由地址,创建route路由规则对象
      return createRoute(record, path);
    }
    return createRoute(null, path);
  }

在上面的代码中,我们调用match方法的时候,会传递过来一个路径,我们根据这个路径可以从pathMap中找到对应的路由记录信息(这块在上一小节已经创建完毕),如果找到了,我们还需要做进一步的处理,为什么呢?因为,我们传递过来的路径有可能是子路径,这时不光要获取到对应的子路由信息,我们还需要去查找对应的父路由的信息。所以这里需要进一步的处理,关于这块的处理封装到了createRoute这个方法中,而该方法在其它位置还需要,所以我们定义到util这个目录下import createRoute from "./util/route";

create-matcher.js文件完整代码如下:

import createRouteMap from "./create-route-map";
import createRoute from "./util/route";
export default function createMatcher(routes) {
 
  const { pathList, pathMap } = createRouteMap(routes);
  console.log("pathList==", pathList);
  console.log("pathMap==", pathMap);
    //实现match方法
  function match(path) {
    const record = pathMap[path];
    if (record) {
      //根据路由地址,创建route路由规则对象
      return createRoute(record, path);
    }
    return createRoute(null, path);
  }
  function addRoutes(routes) {
   
    createRouteMap(routes, pathList, pathMap);
  }
  return {
    match,
    addRoutes,
  };
}

下面我们需要在my-vue-router目录下面在创建一个util目录,在该目录下面创建route.js文件,该文件实现的代码如下:

export default function createRoute(record, path) {

  const matched = [];
 
  while (record) {
    matched.unshift(record);
    record = record.parent;
  }

  return {
    path,
    matched,
  };
}

总结:match这个方法的作用就是根据路径,创建出路由规则对象,而所谓的路由规则对象其实就是包含了路径以及对应的路由记录的信息(这里有可能包含了父路由以及子路由记录,这块内容存储到一个数组中)。

以后,我们就可以根据路径直接获取到包含了整个路由记录的这个数组,从而可以将对应的组件全部创建出来。

八、历史记录处理

关于路由有三种模式:hash模式,html5模式,abstract模式(该模式与服务端渲染有关)

在这里我们实现hash模式的历史记录管理,不管是哪种模式,都有相同的内容,这里我们相同的内容定义到

父类中。

在该父类中主要有如下内容:

router属性:路由对象(ViewRouter

current属性,记录当前路径对应的路由规则对象{path:'/',matched:[]},关于该对象,我们在前面已经处理完了。也就是在createRoute方法中返回的内容。

transitionTo()

跳转到指定的路径,根据当前路径获取匹配的路由规则对象route,然后更新视图。

my-vue-router目录下的,history目录下的base.js文件,编写如下的代码:

import createRoute from "../util/route";
export default class History {
  // router路由对象ViewRouter
  constructor(router) {
    this.router = router;
  
    this.current = createRoute(null, "/");
  }
  
  transitionTo(path, onComplete) {
 
    this.current = this.router.matcher.match(path);
    //该回调函数在调用transitionTo方法的时候,会传递过来。
    onComplete && onComplete();
  }
}

父类已经实现了,下面实现对应的子类。也就是HashHistory

HashHistory继承History, 同时要确保首次访问的地址为#/.

History中还需要定义两个方法,第一个方法为:getCurrentLocation( ) 获取当前的路由地址(# 后面的部分)

setUpListener( )方法监听路由地址改变的事件(hashchange)。

history目录下的hash.js文件中的代码实现如下:

import History from "./base";
export default class HashHistory extends History {
  constructor(router) {
    //将路由对象传递给父类的构造函数
    super(router);
    //确保 首次 访问地址加上 #/  (//由于没有添加this,为普通方法)
    ensureSlash();
  }
  // 获取当前的路由地址 (# 后面的部分)所以这里需要去除#
  getCurrentLocation() {
    return window.location.hash.slice(1);
  }
  // 监听hashchange事件
  //也就是监听路由地址的变化
  setUpListener() {
    window.addEventListener("hashchange", () => {
      //当路由地址发生变化后,跳转到新的路由地址。
      this.transitionTo(this.getCurrentLocation());
    });
  }
}

function ensureSlash() {
  //判断当前是否有hash
  // 如果单击的是链接,肯定会有hash
  if (window.location.hash) {
    return;
  }
  
  window.location.hash = "/";
}

九、Init方法实现

我们知道当创建VueRouter 的时候,需要可以传递mode,来指定路由的形式,例如是hash模式还是html5模式等。

所以这里需要根据指定的mode的模式,来选择history目录中中不同js来处理。
所以在my-vue-router目录中的index.js文件中,做如下的修改:


import install from "./install";
import createMatcher from "./create-matcher";
import HashHistory from "./history/hash";
import HTML5History from "./history/html5";
export default class VueRouter {
  //在创建VueRouter对象的时候,会传递选项
  constructor(options) {
    //获取routes选项,该选项中定义路由规则
    this._routes = options.routes || [];
    this.matcher = createMatcher(this._routes);
    //获取传递过来的选项中的mode,mode中决定了用户设置的路由的形式。
    //这里给VueRouter添加了mode属性
    const mode = (this.mode = options.mode || "hash");
    switch (mode) {
      case "hash":
        this.history = new HashHistory(this);
        break;
      case "history":
        this.history = new HTML5History(this);
        break;
      default:
        throw new Error("mode error");
    }
  }
  // 注册路由变化的事件。
  init() {}
  //init(Vue){}
}
//将install方法挂载到VueRouter上
VueRouter.install = install;

首先导入HashHistoryHTML5History.

import HashHistory from "./history/hash";
import HTML5History from "./history/html5";

下面获取选项中的mode,如果在创建VueRouter对象的时候,没有指定mode,那么默认的值为hash.

下面就对获取到的mode进行判断,根据mode的不同的值,创建不同的history的实例。

//获取传递过来的选项中的mode,mode中决定了用户设置的路由的形式。
    //这里给VueRouter添加了mode属性
    const mode = (this.mode = options.mode || "hash");
    switch (mode) {
      case "hash":
        this.history = new HashHistory(this);
        break;
      case "history":
        this.history = new HTML5History(this);
        break;
      default:
        throw new Error("mode error");
    }

同时html5.js文件,添加了基本的代码

import History from "./base";
export default class HTML5History extends History {}

关于Html5的形式这里不在实现了。

下面完善一下init方法

// 注册路由变化的事件。
  init() {}

实现代码如下:

  // 注册路由变化的事件(初始化事件监听器,监听路由地址的变化)。
  init() {
    const history = this.history;
    const setUpListener = () => {
      history.setUpListener();
    };
    history.transitionTo(
      history.getCurrentLocation(),
      //如果直接history.setUpListener
      // 这样的话setUpListener里面的this会有问题。
      setUpListener
    );
  }

在这里,调用了transitionTo方法的原因是,在hash.js文件中的ensureSlash方法中,完成了一次地址的修改,所以这里需要跳转一次。

同时完成了hashchange事件的绑定(路由变化的事件)。

下面可以进行测试一下,在base.js文件中的transitionTo方法中,打印出current属性的值。

 transitionTo(path, onComplete) {
    this.current = this.router.matcher.match(path);
    console.log("current===", this.current);
 //该回调函数在调用transitionTo方法的时候,会传递过来。
    onComplete && onComplete();
  }

下面,在浏览器的地址栏中输入了不同的URL地址后,在控制台上呈现出了不同的路由规则对象,也就是路由记录信息。

http://localhost:8080/#/about/users

输入以上地址,该地址为子路由的地址,最终也输出了对应的父路由的记录信息。

后期就可以获取具体的组件来进行渲染。

十、设置响应式的_route

下面我们要做的就是渲染组件。

这里我们先创建一个与路由有关的响应式属性,当路由地址发生变化了,对应的该属性也要发生变化,从而完成页面的重新渲染。

install.js文件中添加如下的代码:

 Vue.util.defineReactive(this, "_route", this._router.history.current);

以上完成了响应式属性的创建,但是要注意的是defineReactive方法为Vue的内部方法,不建议平时通过该方法来创建响应式对象。

beforeCreate() {
      //判断是否为Vue的实例,如果条件成立为Vue的实例,否则为其它对应的组件(因为在创建Vue实例的时候会传递选项)
      if (this.$options.router) {
        //通过查看源码发现,Vue的实例会挂在到当前的私有属性_routerRoot属性上
        this._routerRoot = this;
        this._router = this.$options.router;
        //调用index.js文件中定义的init方法
        this._router.init(this);
          
          
        //在Vue的实例上创建一个响应式的属性`_route`.
        Vue.util.defineReactive(this, "_route", this._router.history.current);
      } 

下面要考虑的就是当路由地址发生了变化后,需要修改_route属性的值。

在哪完成_route属性值的修改呢?

base.js文件中,因为在该文件中定义了transitionTo方法,而该方法就是用来完成地址的跳转,同时完成组件的渲染。

base.js文件修改后的代码如下:

import createRoute from "../util/route";
export default class History {
  // router路由对象ViewRouter
  constructor(router) {
    this.router = router;
    this.current = createRoute(null, "/");
    //这个回调函数是在hashhistory中赋值,作用是更改vue实例上的_route,_route的值发生变化,视图会进行刷新操作
    this.cb = null;
  }
  //给cb赋值
  listen(cb) {
    this.cb = cb;
  }

  transitionTo(path, onComplete) {
   
    this.current = this.router.matcher.match(path);
    // 调用cb
    this.cb && this.cb(this.current);
    // console.log("current===", this.current);

    //该回调函数在调用transitionTo方法的时候,会传递过来。
    onComplete && onComplete();
  }
}

History中的构造方法中初始化cb函数。

this.cb = null;

定义listen方法给cb函数赋值。

//给cb赋值
  listen(cb) {
    this.cb = cb;
  }

transitionTo 方法中调用cb函数,同时传递获取到的当前的路由规则对象也就是路由记录信息。

  this.cb && this.cb(this.current);

在什么地方调用listen方法呢?

index.js文件中的init方法中完成listen方法的调用。

// 注册路由变化的事件(初始化事件监听器,监听路由地址的变化)。
  init(app) {
    const history = this.history;
    const setUpListener = () => {
      history.setUpListener();
    };
    history.transitionTo(
      history.getCurrentLocation(),
      //如果直接history.setUpListener
      // 这样的话setUpListener里面的this会有问题。
      setUpListener
    );
    //调用父类的中的listen方法
    history.listen((route) => {
      app._route = route;
    });
  }

在上面的代码中调用了父类中的listen方法,然后将箭头函数传递到了listen中。

这时候,在transitionTo方法中调用cb,也就是调用箭头函数,这时传递过来的参数route,为当前更改后的路由规则信息,交给了app中的_route属性。

app这个参数其实就是Vue的实例,因为在install.js文件中调用了init方法,并且传递的就是Vue的实例。

这样就完成了对Vue实例上的响应式属性_route值的修改,从而会更新组件。

十一、$route/$router创建

创建$route$router的目的是能够在所有的Vue实例(组件)中,可以获取到。

$route是路由规则对象,包含了path,component等内容

$router为路由对象(ViewRouter对象)。

通过查看源码(install.js)可以发现,其实就是将$router$route挂载到了Vue的原型上。

所以可以直接将源码内容复制过来就可以了。

Object.defineProperty(Vue.prototype, "$router", {
    get() {
      return this._routerRoot._router;
    },
  });

  Object.defineProperty(Vue.prototype, "$route", {
    get() {
      return this._routerRoot._route;
    },
  });

通过上面的代码,可以看到$route$router 都是只读的,因为对应的值,在前面已经设置完毕,这里只是获取。

$router 是通过_routerRoot来获取。

$route是通过_routerRoot._route来获取。

Vue.util.defineReactive(this, "_route", this._router.history.current);

Vue对象上创建了_route属性,该属性的值为路由规则内容

十二、Router-View创建

router-view就是一个占位符,会用具体的组件来替换该占位符。

router-view的创建过程如下:

  • 获取当前组件的$route路由规则对象
  • 找到路由规则对象里面的matched 匹配的record(里面有component)
  • 如果是/about ,matched匹配到一个record,直接渲染对应的组件
  • 如果是/about/users,matched 匹配到两个record(第一个是父组件,第二个是子组件)

my-vue-router/components目录下的view.js代码如下:

export default {
  render(h) {
    //获取当前匹配的路由规则对象
    const route = this.$route;
    //获取路由记录对象.只有一个内容,所以获取的是`matched`中的第一项。
    const record = route.matched[0];
    if (!record) {
      return h();
    }
    //获取记录中对应的组件
    const component = record.component;
    return h(component);
  },
};

以上的代码处理的是没有子路由的情况。

下面,看一下子路由情况的处理。

当然在编写子路由的处理代码之前,我们先把案例中的路由完善一下。

src目录下的App.vue这个组件中,添加一个“关于”的链接。

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link>
      <router-link to="/login">Login</router-link>
      <router-link to="/about">About</router-link>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {};
</script>

对应在About 这个组件中,完成子路由应用

<template>
  <div>
    关于组件
    <router-link to="/about/users">用户</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>

下面完善一下对子路由的处理。

export default {
  render(h) {
    //获取当前匹配的路由规则对象
    const route = this.$route;
    let depth = 0;
    //记录当前组件为RouterView
    this.routerView = true;
    let parent = this.$parent;
    while (parent) {
      if (parent.routerView) {
        depth++;
      }
      parent = parent.$parent;
    }
    //获取路由记录对象.
    // 如果是子路由,例如:子路由/about/users
    //子路由是有两部分内容,matched[0]:是父组件内容,matched[1]是子组件内容
    const record = route.matched[depth];
    if (!record) {
      return h();
    }
    //获取记录中对应的组件
    const component = record.component;
    return h(component);
  },
};

假如,现在我们在浏览器的地址栏中输入了:http://localhost:8080/#/about地址,

是没有父组件,那么depth属性的值为0,这时候获取的第一个组件然后进行渲染。

如果地址栏的内容为:http://localhost:8080/#/about/users 这时候有子组件。对应的获取对应的父组件内容,开始进行循环。

在循环的时候,做了一个判断,判断的条件就是当前的父组件必须为:RouterView组件(子组件中router-view与父组件中的router-view构成了父子关系),才让depth加1,然后取出子组件进行渲染。

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

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

相关文章

浅谈人工智能之基于anaconda的AutoGen Studio环境搭建

浅谈人工智能之基于anaconda的AutoGen Studio环境搭建 AutoGen Studio 是一个基于 AutoGen 框架的图形用户界面&#xff08;GUI&#xff09;工具。它使开发人员能够更轻松地创建和管理多智能体应用&#xff0c;而无需编写代码。AutoGen Studio 提供了拖放式界面和各种预构建模…

绿色革命:‘众店‘如何重塑零售业并引领消费新潮流

在数字化浪潮的推动下&#xff0c;传统零售业正面临深刻的变革。在这一过程中&#xff0c;绿色积分系统作为一种创新的消费模式&#xff0c;逐渐成为市场的新宠。 一、"众店"平台的迅猛发展 "众店"平台仅用两年时间就实现了跨越式发展&#xff0c;交易额突…

JS基础【双重for循环的实现与应用、打印九九乘法表】

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主、华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f970; &#…

springboot+MySQL流浪猫狗疾病预约救治系统-计算机毕设 附源码 13676

springboot流浪猫狗疾病预约救治系统 摘 要 现如今在中国&#xff0c;随着人民生活质量的逐渐提高&#xff0c;以及人民群众消费能力的日渐增长&#xff0c;各种各样的家养小动物&#xff0c;已经逐渐成为人类越来越亲密的生活伴侣。并且&#xff0c;现如今社会竞争及其激烈&am…

钢铁百科:A633D交货状态、A633D对应牌号、A633D力学性能解析

A633D钢板是一种美标低合金高强度结构钢板&#xff0c;也被称为A633GrD钢板&#xff0c;其执行标准为ASTM/A633M。这种钢板以其高屈服强度、良好的韧性、抗腐蚀性和耐疲劳性而著称&#xff0c;广泛应用于建筑、桥梁、船舶、汽车、铁路、重型机械及矿山机械等多个领域。 一、A6…

【CSS】如何写渐变色文字并且有打光效果

效果如上&#xff0c;其实核心除了渐变色文字的设置 background: linear-gradient(270deg, #d2a742 94%, #f6e2a7 25%, #d5ab4a 48%, #f6e2a7 82%, #d1a641 4%);color: #e8bb2c;background-clip: text;color: transparent;还有就是打光效果&#xff0c;原理其实就是两块遮罩&am…

微信小程序知识点(一)

1.条件判断&#xff1a; wx:if&#xff0c;wx:elif&#xff0c;wx:else 和Hidden的区别 wx:if等是动态实现组件的&#xff0c;符合条件&#xff0c;页面上就新增一个组件&#xff0c;不符合&#xff0c;就会在也页面上加载&#xff0c;而Hidden只是控制页面的组件的显示与否&…

C#绘制常用工业控件(仪表盘,流动条,开关等)

目录 1&#xff0c;使用Graphics绘制Toggle。 效果&#xff1a; 测试代码&#xff1a; Toggle控件代码&#xff1a; 2&#xff0c;使用Graphics绘制Switch。 效果&#xff1a; 测试代码&#xff1a; Switch控件代码&#xff1a; 3&#xff0c;使用Graphics绘制PanelHe…

【Nest 学习笔记】AOP切片编程

切片编程 AOP 把通用逻辑抽离出来&#xff0c;通过切面的方式添加到某个地方&#xff0c;可以复用和动态增删切面逻辑。 中间件 Middleware Middleware 中间件属于全局中间件&#xff08;Middleware 是 Express 的概念&#xff09; 常用于对请求接口进行日志记录 // main.ts …

七. 部署YOLOv8检测器-load-save-tensor

目录 前言0. 简述1. 案例运行2. 补充说明3. 代码分析3.1 main.cpp3.2 create_data.py 结语下载链接参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习课程第六章—部署分类器&…

GMeLLo:结合知识图谱的 LLM 多跳问答技术,效果显著提升

1. GMeLLo 提出的背景 1.1 多跳问答 多跳问答的难度往往比较大&#xff0c;因为不仅要追溯事实&#xff0c;还要聚合与串联事实。 事实的来源可以是知识图谱、表格、自由格式文本&#xff0c;或者是这些来源的异构组合。 随着大型语言模型的发展&#xff0c;基于提示的方法…

安科瑞ACR10R网络电力仪表 CE认证 带外置互感器

产品概述&#xff1a; ‌‌安科瑞ACR10R网络电力仪表是一种集成了多种电力参数测量、电能计量、电能监测和考核管理等多种功能于一体的电力仪表。它适用于冶炼、钢铁、电焊、半导体等高能耗行业的节能改造工程&#xff0c;同时也适用于分布式光伏并网柜的功率监测、电力需求侧…

sqli-labs靶场通关攻略 56-60

主页有sqli-labs靶场通关攻略 1-55 第五六关 less-56 步骤一&#xff1a;闭合方式&#xff1a;?id1)-- 步骤二&#xff1a;查询数据库 ?id-1) union select 1,2,database() -- 步骤三&#xff1a;联合查询 查出网站的数据库表名 ?id-1) union select 1,2,group_concat(t…

探索AntSKPro AI知识库一体机:离线智能的便捷之选

在数字化时代&#xff0c;信息的获取和处理速度是企业和个人效率的关键。然而&#xff0c;网络连接的不稳定性常常成为阻碍。AntSKPro AI知识库一体机&#xff0c;一款专为离线环境设计的智能设备&#xff0c;以其卓越的性能和用户友好的设计&#xff0c;正在重新定义离线AI解决…

双指针--优选算法

个人主页&#xff1a;敲上瘾-CSDN博客 个人专栏&#xff1a;游戏、数据结构、c语言基础、c学习、OJ题 前言&#xff1a; 该篇文章我们主要来学习的是双指针算法&#xff0c;对于该类算法我们可以直接来做题&#xff0c;从题中去感知该算法的魅力&#xff0c;最后再从题中做总…

【数据结构】—— 线性表

目录 前言一、顺序表1.1 顺序表的定义及其特点1.2 顺序表的C语言实现1.2.1 定义顺序表1.2.2 初始化1.2.3 插入1.2.4 删除1.2.5 查找 二、链表2.1 链表的定义2.2 单向链表的实现2.2.1 定义单向链表2.2.2 创建链表2.2.3 插入元素2.2.4 删除元素2.2.5 查找 2.3 双向循环链表 前言 …

选刊风向标!985大学近十年发文热门IEEE期刊盘点

本期盘点同济大学近十年有关IEEE旗下发文较多的期刊&#xff0c;一起来看看哪些是双一流大学热门发文期刊&#xff1a; 1、IEEE Transactions on Cybernetics • 影响因子&#xff1a;9.4 • JCR1区&#xff0c;中科院1区-Top • 检索数据库&#xff1a;SCIE • 期刊分区&a…

stable diffusion的安装

stable diffusion的安装 一、前言二、安装python环境1、已经安装python环境&#xff0c;但非3.10.6版本&#xff08;可以不看&#xff09; 三、安装stable diffusion四、运行五、启动报错1、Torch is not able to use GPU2、Installing open_clip 卡住3、报错提示 "git&qu…

systemverilog中的DPI-C用例介绍

文章目录 前言一、dpi_longint二、dpi_packed_array三、dpi_structure四、相关参考总结 前言 本文主要基于VCS内置的三个关于DPI-C的使用用例&#xff0c;记录一下DPI-C的使用方法。测试用例的路径为$VCS_HOME/doc/examples/testbench/sv/。测试用例包括&#xff1a;dpi_longi…

自然语言处理:第四十三章 视觉RAG:变革传统深度学习模型开发流程,开创下一代多模态视觉模型的新时代

文章链接:微信公众平台 (qq.com) 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff0c;笔芯!!! 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff0c;笔芯!!! 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff…