微前端——无界wujie

news2024/9/30 9:23:02

B站课程视频
课程视频

课程课件笔记:
1.微前端

2.无界

现有的微前端框架:iframe、qiankun、Micro-app(京东)、EMP(百度)、无届

前置

初始化

新建一个文件夹
1.通过npm i typescript -g安装ts
2.然后可以使用tsc --init初始化项目,这样项目目录下会有tsconfig.json配置文件
3.再新建index.ts
4.使用tsc -w命令可以实时的编译index.ts出一个index.js文件
5.通过index.html引入index.js即可

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./index.js"></script>
</head>
<body>
</body>
</html>

webComponents初使用

主要是为了样式隔离

wu-jie初体验

index.ts文件中书写

// webComponents 的写法
window.onload = () => {
  // 初始化
  class Wujie extends HTMLElement {
    // 类的基本用法
    constructor() {
      super()

      // this.attachShadow创建shadowdom并打开就具有样式隔离的性质,不会影响外层的样式
      let dom = this.attachShadow({mode: 'open'})
      // 获取template
      let template = document.querySelector('#wujie') as HTMLTemplateElement
      // 并绑定,这里是把template的内容深度克隆一份给dom,而不是直接给,true表示深度克隆
      dom.appendChild(template.content.cloneNode(true))
      // 这样就把template的内容渲染到了<wu-jie></wu-jie>的组件里面
      
      console.log(this.getAttr('url'), this.getAttr('age'))
    }

    // 获取传参this.getAttribute 传入属性名即可
    private getAttr(attr: string) {
      return this.getAttribute(attr)
    }
  }
  // webComponents挂载,注意名字不能启成驼峰的但是-横线连接可以,第二个参数是上面类的名字
  // 类似vue组件  原生js写的一个组件 可以在html中使用了<wu-jie></wu-jie>
  window.customElements.define('wu-jie', Wujie)
}

在上述建立的index.html中书写

<body>
  <!-- 相当于组件,也可以传参 -->
  <wu-jie url='xxxxx' age='18'></wu-jie>
  <div>我是外层的div</div>

  <template id="wujie">
    <div>我是 template 里面的div</div>
    <style>
      div{
        background: red;
      }
    </style>
  </template>
</body>

无届的三个生命周期

//生命周期自动触发有东西插入
connectedCallback () {
  console.log('类似于vue 的mounted');
}
//生命周期卸载
disconnectedCallback () {
      console.log('类似于vue 的destory');
}
//跟watch类似
attributeChangedCallback (name:any, oldVal:any, newVal:any) {
    console.log('跟vue 的watch类似,有属性发生变化自动触发');
}

pnpm介绍

1.全局安装npm i pnpm -g
2.使用 pnpm -v可以查看版本号
3.pnpmnpm的优势,在pnpm中文网的动机这样写pnpm官网
在这里插入图片描述
简单来说就是,但有100个vue项目,每个项目都会去安装相应的依赖包,这样导致磁盘的缩小和安装速度慢(每次都会重新下载依赖包),很麻烦。
但是pnpm使用软链接硬链接仓库解决

硬链接

1.使用cmd查看提示,表示 /H就是硬链接
在这里插入图片描述

2.创建命令含义 mklink /H 硬链接的名字 通过谁创建
例如:在pnpm文件目录下建立index.js文件,并通过mklink /H ying.js index.js创建了硬链接
在这里插入图片描述
3.并且目录下多出了一个ying.js
在这里插入图片描述

4.然后打开文件发现ying.js中内容和index.js中一样
在这里插入图片描述
5.而且当修改index.js中的内容时,ying.js中的内容会跟着修改,因为他们共享的同一个磁盘地址

软链接(符号链接)

1.创建软链接需要管理员权限,所以管理员打开cmd后进入目录可以使用cd ../../等回退到根目录下,然后直接 D: 进入相应盘,再cd命令
2.默认就是软链接 mklink ruan.js index.js 即可,不用加任何修饰符
在这里插入图片描述
3.成功创建后VScode中会有一个标志
在这里插入图片描述
4.且文件管理器中查看也是一个0字节的,因为他只记录一个路径(快捷方式,并不会占用资源),点击会跳转,指向的还是index.js
在这里插入图片描述

pnpm如何利用硬链接、软链接解决上述问题

1.使用pnpm init命令创建package.json
2.以安装vue包为例,使用pnpm i vue 安装vue依赖,可以看到node_modules下面有vue的软链接(快捷方式,地址指向)
在这里插入图片描述
3.实际上上述软链接指向在.pnpm包下找到真正的vue@3.3.13的包下面的vue文件夹,这个vue硬链接指向.pnpm store
在这里插入图片描述
4.实际上就对应了pnpm官网的这张图,非扁平化的方式(可能嵌入,因为这个库可能还依赖其他库,这样依次来)
在这里插入图片描述

绿色黄色实线为软链接、红色虚线为硬链接
在这里插入图片描述

pnpm的CLI命令管理

假设你之前使用的npm 安装包,会生成一个package-lock.json,或者使用yarn安装过,会生成一个yarn.lock

但是你再通过pnpm import命令 他会把你的上述的软件包管理器的 lockfile 生成 pnpm-lock.yaml文件

monorepo项目

创建monorepo项目目录

1.使用命令npm init vue创建vue项目,并创建项目名为main
2.创建web文件目录存放子应用
3.创建vue子应用,使用npm init vite选择vue+typescript
4.创建react子应用,使用npm init vite选择react+typescript
5.在web文件夹下有多个子应用,分别安装有点繁琐,所以在monorepo文件夹下使用命令pnpm init生成文件,并手动创建pnpm-workspace.yaml文件和配置。配置

官网是:
packages:
  # all packages in direct subdirs of packages/
  - 'packages/*'
  # all packages in subdirs of components/
  - 'components/**'
  # exclude packages that are inside test directories
  - '!**/test/**'

替换为自己的目录
packages:
  # all packages in direct subdirs of packages/
  - 'main/*'
  # all packages in subdirs of components/
  - 'web/**'

此时目录如下:
在这里插入图片描述
6.替换好pnpm-workspace.yaml之后可以在根目录直接pnpm i即可自动为所有的项目安装包
最外层根目录下的node_modules是所有项目公共的包,而vue项目或者react项目里面的node_modules是他们单独所需要的,这样结构更加清晰。
在这里插入图片描述

子项目启动:

假设这时候我们要跨级执行命令,想要执行react-demo项目中package.jsondev命令,可以使用pnpm -F react-demo dev,其中F是过滤filter

子模块复用:

1.新建目录common下,并进入,想要common目录下的东西其余子项目(vue、react等都可以使用)
2.使用pnpm init初始化生成package.json文件。在pnpm-workspace.yaml中写

packages:
  # all packages in direct subdirs of packages/
  - 'main/*'
  # all packages in subdirs of components/
  - 'web/**'
  - 'common'

3.安装axios包了pnpm i axios
在这里插入图片描述
3.新建index.ts,编写一些公共代码

import axios from 'axios'

// 公共的提取出来
export const a = axios.get('xxx')

4.进入main目录使用命令pnpm -F main add common则把common添加进main项目的依赖中,查看main/package.json文件里面有
在这里插入图片描述
5.然后就可以在main/src/main.ts中引入

import { a } from 'common'

6.同理,也可以去给react-demo项目添加或者vue-demo添加:pnpm -F vue-demo add common,一样查看那个package.json有common包,然后可以导入进行使用

无届

安装

1.使用pnpm i wujie,在main.ts中引入wujie并配置启动的参数

import { startApp } from 'node_modules/wujie/esm/index'
// 启动的参数
startApp({name, url, el})

2.如果是vue项目,可以直接安装npm i wujie-vue3进行安装

import Wujie from 'wujie-vue3'
app.use(router).use(Wujie)

3.然后分别启动好子应用,记录端口就可以在App.vue中使用了

<template>
  <div>
    <h1>这是主应用</h1>
    <!-- 分别是子应用,子应用启动之后各自的端口 -->
    <WujieVue url="http://127.0.0.1:5174" name="vue3"></WujieVue>
    <WujieVue url="http://127.0.0.1:5175" name="react"></WujieVue>
  </div>
</template>

在main.ts中,全部代码如下:

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import Wujie from 'wujie-vue3' // 引入一下对应的框架
import { preloadApp } from 'wujie'

const app = createApp(App)

app.use(router).use(Wujie) // 注册无界
app.mount('#app')

// exac预加载为true即可,那么components/react.vue和vue3.vue就会有url
preloadApp({ name: "vue3", url: "http://127.0.0.1:5173", exec: true})
preloadApp({ name: "react", url: "http://127.0.0.1:5174", exec: true})

封装无届

1.创建的目录

封装无届

1.创建的目录文件如下:
其中使用pnpm init创建package.json
使用tsc --init创建 tsconfig.json
在这里插入图片描述
2.使用pnpm i wujie安装无届
3.使用pnpm i vue -D把vue装在开发环境中
4.安装webpack,使用pnpm i webpack webpack-cli -D
5.安装typescript,使用 pnpm i typescript -D
6.安装typescript解析器,使用pnpm i ts-loader -D

书写相关配置代码

配置代码部分可以看源码参照下面的代码

写完后使用 npm run lib 打包

1.在env.d.ts中书写

import { defineComponent, h, getCurrentInstance, onMounted, watch, onBeforeUnmount } from 'vue'
import type { App, PropType } from 'vue'
import { Props } from './type'
import { startApp, bus } from 'wujie'

// 函数式定义组件
const wujie = defineComponent({
  props: {
    width: { type: String, default: "" },
    height: { type: String, default: "" },
    name: { type: String, default: "", required: true },
    loading: { type: HTMLElement, default: undefined },
    url: { type: String, default: "", required: true },
    sync: { type: Boolean, default: undefined },
    prefix: { type: Object, default: undefined },
    alive: { type: Boolean, default: undefined },
    props: { type: Object, default: undefined },
    attrs: { type: Object, default: undefined },
    replace: { type: Function as PropType<Props['replace']>, default: undefined },
    fetch: { type: Function as PropType<Props['fetch']>, default: undefined },
    fiber: { type: Boolean, default: undefined },
    degrade: { type: Boolean, default: undefined },
    plugins: { type: Array as PropType<Props['plugins']>, default: null },
    beforeLoad: { type: Function as PropType<Props['beforeLoad']>, default: null },
    beforeMount: { type: Function as PropType<Props['beforeMount']>, default: null },
    afterMount: { type: Function as PropType<Props['afterMount']>, default: null },
    beforeUnmount: { type: Function as PropType<Props['beforeUnmount']>, default: null },
    afterUnmount: { type: Function as PropType<Props['afterUnmount']>, default: null },
    activated: { type: Function as PropType<Props['activated']>, default: null },
    deactivated: { type: Function as PropType<Props['deactivated']>, default: null },
  },
  setup(props, { emit }) {
    // 读取当前组件实例
    // this.$refs.wujie // 在vue2中可以通过this获取当前组件实例
    const instance = getCurrentInstance() // vue3中通过getCurrentInstance获取组件实例

    // 封装启动函数
    const init = () => {
      // startApp({name, url, el})
      //初始化无界
      startApp({
        name: props.name,
        url: props.url,
        el: instance?.refs.wujie as HTMLElement,
        loading: props.loading,
        alive: props.alive,
        fetch: props.fetch,
        props: props.props,
        attrs: props.attrs,
        replace: props.replace,
        sync: props.sync,
        prefix: props.prefix,
        fiber: props.fiber,
        degrade: props.degrade,
        plugins: props.plugins,
        beforeLoad: props.beforeLoad,
        beforeMount: props.beforeMount,
        afterMount: props.afterMount,
        beforeUnmount: props.beforeUnmount,
        afterUnmount: props.afterUnmount,
        activated: props.activated,
        deactivated: props.deactivated,
      })
    }
    watch([props.name, props.url], () => {
      init() // 如果发生变化就重新执行startApp
    })

    const handlerEmit = (event:string, ...args:any[]) => {
      emit(event, ...args)
    }

    onMounted(() => {
      // 发布订阅模式
      bus.$onAll(handlerEmit) 
      init()
    })

    onBeforeUnmount(() => {
      bus.$offAll(handlerEmit)
    })

    // 定义渲染函数
    return ()=> h('div', {
      style: {
        width: props.width,
        height: props.height
      },
      ref: "wujie" // 方便之后读取
    })
  }
})
// install 方法给vue使用的,app.use(router).use(wujie)会调用install
wujie.install = function(app: App) {
  app.component('WujieVue', wujie)
}

export default wujie

2.type.ts中写

import type { plugin } from 'wujie'
type lifecycle = (appWindow: Window) => any;
interface Props {
    /** 唯一性用户必须保证 */
    name: string;
    /** 需要渲染的url */
    url: string;
    /** 需要渲染的html, 如果用户已有则无需从url请求 */
    html?: string;
    /** 渲染的容器 */
    loading?: HTMLElement;
    /** 路由同步开关, false刷新无效,但是前进后退依然有效 */
    sync?: boolean;
    /** 子应用短路径替换,路由同步时生效 */
    prefix?: { [key: string]: string };
    /** 子应用保活模式,state不会丢失 */
    alive?: boolean;
    /** 注入给子应用的数据 */
    props?: { [key: string]: any };
    /** js采用fiber模式执行 */
    fiber?: boolean;
    /** 子应用采用降级iframe方案 */
    degrade?: boolean;
    /** 自定义运行iframe的属性 */
    attrs?: { [key: string]: any };
    /** 自定义降级渲染iframe的属性 */
    degradeAttrs?: { [key: string]: any };
    /** 代码替换钩子 */
    replace?: (codeText: string) => string;
    /** 自定义fetch,资源和接口 */
    fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
    /** 子应插件 */
    plugins: Array<plugin>;
    /** 子应用生命周期 */
    beforeLoad?: lifecycle;
    /** 没有做生命周期改造的子应用不会调用 */
    beforeMount?: lifecycle;
    afterMount?: lifecycle;
    beforeUnmount?: lifecycle;
    afterUnmount?: lifecycle;
    /** 非保活应用不会调用 */
    activated?: lifecycle;
    deactivated?: lifecycle;
};

export { Props } 

3.webpack.config.js中写

const { Configuration } = require('webpack')
const path = require('path')

/**
 * @type {Configuration} //配置智能提示
 */

const config = {
  entry: "./src/index.ts", // 入口文件
  mode: "none",
  output: {
    filename: "index.js",
    path: path.resolve(__dirname, 'lib')
  },
  externals: { // 防止打包后的代码包含wujie和vue代码显得过多
    vue: "vue",
    wujie: "wujie"
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader"
      }
    ]
  }

}

module.exports = config

SWC和babel

SWC官网
1.swc比babel快20倍,如果是4核情况下,快70倍
使用pnpm add -D @swc/score swc-loader安装命令

2.然后替换webpack.config.js中的use

module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader" // babel的ts-loader,换成swc-loader
      }
    ]
  }

3.因为swc底层是rust写的,rust性能是js的好几倍所很快

4.然后再打包npm run lib打包后还不能直接使用,
因为前端有很多代码规范,AMD, CMD,CommonJS,ESModule,所以统一配置一个UMD,
5.安装pnpm i -D @swc/cli @swc/core以便使用swc-cli
6.新建配置文件.swcrc

{
  "$schema": "https://json.schemastore.org/swcrc",
  "jsc": {
  "parser": {
  "syntax": "typescript", // js是ecmascript
  // "jsx": false,
  // "dynamicImport": false,
  // "privateMethod": false,
  // "functionBind": false,
  // "exportDefaultFrom": false,
  // "exportNamespaceFrom": false,
  // "decorators": false,
  // "decoratorsBeforeExport": false,
  // "topLevelAwait": false,
  // "importMeta": false
  },
  // "transform": null,
  "target": "es5",
  "loose": false,
  "externalHelpers": false,
  // Requires v1.2.50 or upper and requires target to be es2016 or upper.
  "keepClassNames": false
  },
  "minify": false
 }

7.在根目录的package.json的 scripts中编写打包命令写"esm": "swc src/index.ts -d esm",
8.控制台执行 npm run swc即可打包在esm文件中
9.修改增添package.json中
设定一些版本号和启动的入口

"name": "vuejie-setup",
"version": "0.0.1",
 "main": "lib/index.js", 
"module": "esm/index.js",

添加files

"files": [
    "lib",
    "esm",
    "index.d.ts"
  ],

10.在根目录下的index.d.ts声明文件中写

// import { bus, preloadApp, destroyApp, setupApp } from "wujie";
import type { App } from 'vue';

declare const WujieVue: {
    // bus: typeof bus;
    // setupApp: typeof setupApp;
    // preloadApp: typeof preloadApp;
    // destroyApp: typeof destroyApp;
    install: (app: App) => void
};

export default WujieVue;

发布包

1.切换镜像 mmp use,输入 npm
2.使用 npm adduser命令,然后输入npm的用户名
在这里插入图片描述
3.使用npm login
4.使用 npm publish发布

使用

1.在MONOREPO项目中切换进主应用cd main
2.命令安装pnpm i vuejie-setup
3.在main.ts中引入import Wujie from 'vuejie-setup'
4.使用npm run dev启动各种主应用或者子应用查看效果

无届的预加载和FPS

1.requestldleCallback函数
MDN的解释
在这里插入图片描述

2.FPS扩展
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

无届的传参

总共三种方法

第一种方法

无届的架构是把JS单独存放在了iframe中,那么iframe可以通过window和父级进行通讯,所以第一种传参就是通过
window.parent.变量名 进行读取。

1.在vue-demo/App.vue中添加按钮,绑定函数获取a

<script setup lang="ts">
const send = () => {
  alert(window.parent.a) //
}
</script>
<button @click="send">点击</button>

2.在控制台输入变量 var a = '控制台的输入'
3.点击按钮,发现可以弹出
在这里插入图片描述

在这里插入图片描述

第二种方法

通过props 传参,子应用不需要绑定无界,主应用接进来之后会自动捕获vue实例,所以子应用可以访问到无界实例对象获取参数

1.例如在主应用的components/App.vue组件中挂载数据,通过props传参

<template>
  <WujieVue :props="{name: '张三', age: '18'}" url="http://127.0.0.1:5173" name="vue3"></WujieVue>
</template>

2.然后在vue-demo子项目中的App.vue中写JS代码

const send = () => {
  // alert(window.parent.a) // 第一种方法
  console.log(window.$wujie) // 第二种方式
}

3.为了防止上述$wujie报错,可以在main.ts中添加

declare global {
  interface Window {
    $wujie: {
      props:  Record<string, any>
      bus: {
        $emit: any
      }
    }
  }
}

4.打印的结果如下(可以看到还有bus,第三种方法获取)
在这里插入图片描述
4.上述可以访问props console.log(window.$wujie)进行打印

d第三种方案eventBus发布订阅

可以双向数据传递,直接使用wujie的bus.$on
1.在 主应用的App.vue中通过bus.$on绑定数据

<script lang="ts">
import { bus } from 'wujie'
bus.$on('vue3', (data: any) => {
  console.log(data, '我是主应用的bus.$on')
})
</script>

2.在子应用vue-demo的App.vue中通过实例直接使用

const send = () => {
  // alert(window.parent.a)
  // console.log(window.$wujie.props) // 获取到App.vue下的props
  window.$wujie.bus.$emit('vue3', '我是子应用vuedemo')
}

模块联邦

1.创建文件如下,目录
在这里插入图片描述

2.安装下面四个依赖包
npm i webpack webpack-cli webpack-server html-webpack-plugin -D
在这里插入图片描述
3.书写代码,略,结果如下,总之host里面没有的List数据却通过引入到了remote的数据进行展示
在这里插入图片描述
在这里插入图片描述
4.pnpm run build进行打包host项目下
点开dist/bundle.js,可以看到使用CDN的方式进行引入的
在这里插入图片描述
之前:10个项目引用同一个模块,通过把这个项目发布到npm上面,然后这是个项目可以install这个模块,但是当这个模块发生改变的时候,例如从1.0.0 -> 1.0.1,那么这十个项目得重新下载install一遍
现在的CDN,当你的模块修改了,直接就是最新的版本,通过这个链接,远程调用联邦的技术正是用的这种

报错

1.在tsconfig.app.json中报错:没有 “node” 模块解析策略的情况下,无法指定选项 “-resolveJsonModule”。

修改tsconfig.app.json 文件中 compilerOptions 选项配置 “moduleResolution”: “node”

{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
  "exclude": ["src/**/__tests__/*"],
  "compilerOptions": {
    "composite": true,
    /*
     * 模块解析策略,ts默认用node的解析策略,即相对的方式导入, 可选值:node、classic
     * 如果未指定,则 --module commonjs 默认为 node,否则默认为 classic(包括 --module 设置为 amd、system、umd、es2015、esnext 等)
     * Node 模块解析是 TypeScript 社区中最常用的,推荐用于大多数项目。 
     * 如果您在 TypeScript 中遇到导入和导出的解析问题,请尝试设置 moduleResolution: “node” 以查看它是否解决了问题。
     */
    "moduleResolution": "node",    
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

参考自

一些文件爆红

1.找不到模块“./App.vue”或其相应的类型声明
2.ts.config.app.json找不到文件vue tsconfig tsconfig dom json

如果报错vite找不到之类的,以及奇怪的错误请删除两个子应用重新建立和安装和初始化最外层的包pnpm init

ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL vue-demo@0.0.0 dev: `vite` Exit status 1

一些常用的命令

pnpm i 和 pnpm init

pnpm i是 pnpm install的简写,表示安装包,会根据package.json的包版本进行安装,
pnpm init是初始化一个项目,一般只会在空项目文件下创建package.json一些必要的包

pnpm run dev 和 pnpm run start

前者是开发环境,后者是打包之后的生产环境运行项目

使用脚手架建立项目

因为涉及诸多配置,建议按照vite、webpack官网来看

使用vite脚手架建立vue、react项目

使用webpack脚手架建立vue、react项目

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

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

相关文章

【教学类-42-02】20231224 X-Y 之间加法题判断题2.0(按2:8比例抽取正确题和错误题)

作品展示&#xff1a; 0-5&#xff1a; 21题&#xff0c;正确21题&#xff0c;错误21题42题 。小于44格子&#xff0c;都写上&#xff0c;哪怕输入2:8&#xff0c;实际也是5:5 0-10 66题&#xff0c;正确66题&#xff0c;错误66题132题 大于44格子&#xff0c;正确66题抽取44*…

python pip安装依赖的常用软件源

目录 引言 一、什么是镜像源&#xff1f;​​​​​​​ 二、清华源 三、阿里源 四、中科大源 五、豆瓣源 六、更多资源 引言 在软件开发和使用过程中&#xff0c;我们经常需要下载和更新各种软件包和库文件。然而&#xff0c;由于网络环境的限制或者服务器的负载&#…

canvas入门笔记(上)

Canvas Canvas简介 Canvas API 提供了一个通过JavaScript 和 HTML的元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。 Canvas API 主要聚焦于 2D 图形。而同样使用<canvas>元素的 WebGL API 则用于绘制硬件加速的 2D 和…

资深老鸟整理,Java接口自动化测试总结,从0到1自动化...

这几年接口自动化变得越来越热门&#xff0c;相对比于UI自动化&#xff0c;接口自动化有一些优势 1&#xff09;运行比UI更稳定&#xff0c;让BUG更容易定位 2&#xff09;UI自动化维护成本太高&#xff0c;接口相对低一些 接口测试其实有很多方式&#xff0c;主要有两种&…

基于SpringBoot + Vue的图书管理系统

功能概述 该图书管理系统提供了一系列功能&#xff0c;包括图书管理、图书类型管理、读者借阅归还图书、用户管理和重置密码等。 在图书管理功能中&#xff0c;管理员可以方便地进行图书信息的管理。他们可以添加新的图书记录&#xff0c;包括书名、作者、出版社、ISBN等信息&a…

进行鸿蒙开发前的一些工具了解

文章概叙 文章主要讲的是开发的一些工具&#xff0c;如DevEco Studio,以及ArkTs的一些基础。 为啥要学习鸿蒙开发 抛开各种遥遥领先不讲&#xff0c;现在打开BOSS直聘&#xff0c;已经可以看到在BOSS上有不少的岗位是关于鸿蒙的&#xff0c;甚至是华为的岗位&#xff0c;而在…

Report Design

ERP_ENT_STD-CSDN博客

mac传输文件到windows

前言 由于mac系统与windows系统文件格式不同&#xff0c;通过U盘进行文件拷贝时&#xff0c;导致无法拷贝。官方解决方案如下&#xff0c;但是描述的比较模糊。看我的操作步骤即可。 https://support.apple.com/zh-cn/guide/mac-help/mchlp1657/12.0/mac/12.6 前提条件 mac与…

产品设计 之 创建完美产品需求文档的4个核心要点

客户描述他们想要的产品和最终交付的产品之间的误解一般很大&#xff0c;设计者和客户的角度不同&#xff0c;理解的程度也不同&#xff0c;就需要一个统一的交流中介。这里包含PRD。 为了说明理解误差的问题。下面这张有趣的图画可以精准阐述。 第一张图片展示了客户所描述…

什么是网站监控?

网站监控是跟踪网站的可用性和性能&#xff0c;以最小化宕机时间&#xff0c;优化性能并确保顺畅的用户体验。维护网站正常运行对于任何企业来说都是至关重要的&#xff0c;因而对大多数业务来说&#xff0c;网站应用监控都是一个严峻的挑战。Applications Manager网站应用监控…

计算机图形学理论(4):缓冲区

本系列根据国外一个图形小哥的讲解为本&#xff0c;整合互联网的一些资料&#xff0c;结合自己的一些理解。 什么是缓冲区&#xff1f; 缓冲区是保存某些数据的临时存储空间。 为什么我们需要缓冲区&#xff1f;原因很简单&#xff0c;当数据量很大时&#xff0c;因为计算机无…

自学SLAM(9)《第五讲:特征点法视觉里程计》作业

文章目录 1.ORB特征点1.1 ORB提取1.2 ORB描述1.3 暴力匹配1.4 最后&#xff0c;请结合实验&#xff0c;回答下⾯⼏个问题 2.从 E 恢复 R&#xff0c;t3.用 G-N 实现 Bundle Adjustment4.* 用 ICP 实现轨迹对齐 1.ORB特征点 1.1 ORB提取 ORB(Oriented FAST and BRIEF) 特征是 S…

插入排序之C++实现

描述 插入排序是一种简单直观的排序算法。它的基本思想是将一个待排序的数据序列分为已排序和未排序两部分&#xff0c;每次从未排序序列中取出一个元素&#xff0c;然后将它插入到已排序序列的适当位置&#xff0c;直到所有元素都插入完毕&#xff0c;即完成排序。 实现思路…

外星人Alienware Area-51 R2原厂Win10预装系统

大三角外星人Area 15 R2原装出厂WINDOWS10系统 链接&#xff1a;https://pan.baidu.com/s/1JwDuHx1j7fRABtIpLmKW_g?pwdq4pd 提取码&#xff1a;q4pd 原厂系统自带所有驱动、外星人出厂主题壁纸、专属LOGO标志、Office办公软件、MyAlienware、外星人控制中心等预装程序 文…

T-Dongle-S3开发板信息

相关学习网站 ESP32保姆级教程开始学习ESP32_哔哩哔哩_bilibili Wokwi - Online ESP32, STM32, Arduino Simulator T-Dongle-S3 资料:https://spotpear.cn/index/study/detail/id/1069.html 主控芯片&#xff1a; ESP32-S3 Xtensa 芯片集成了 Xtensa 32 位 LX7 双核处理器…

如何用Python画坤坤?

前言 最近闲得慌&#xff0c;突然想起坤坤了&#xff0c;那就画一个吧。 一、Python画坤坤 坤坤大家都熟悉不过了&#xff0c;也就是蔡徐坤。 代码&#xff1a; from turtle import * from math import * #高级椭圆参数方程&#xff08;颜色&#xff09;&#xff0c;sita为…

[java] 注释

目录 单行注释: 多行注释: 文档注释: doc标签 单行注释: 多行注释: 注意多行注释不能嵌套多行注释: 文档注释: 注释内容可以被JDK提供的工具 javadoc 所解析&#xff0c;生成一套以网页文件形式体现的该程序的说明文档&#xff0c;一般写在类。 doc标签 标签 描述 示例 …

[论文阅读笔记28] 对比学习在多目标跟踪中的应用

这次做一篇2D多目标跟踪中使用对比学习的一些方法. 对比学习通过以最大化正负样本特征距离, 最小化正样本特征距离的方式来实现半监督或无监督训练. 这可以给训练MOT的外观特征网络提供一些启示. 使用对比学习做MOT的鼻祖应该是QDTrack, 本篇博客对QDTrack及其后续工作做一个总…

设计模式--命令模式

实验16&#xff1a;命令模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解命令模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用命令模式解决实际问题。 [实验任务]&#xff1a;多次撤销和重复的命令模式 某系…

算法学习系列(十):用数组模拟链表、双链表、栈、队列、单调栈、单调队列

目录 引言一、数组模拟链表1.模板2.例题3.测试 二、数组模拟双链表1.模板2.例题3.测试 三、数组模拟栈1.模板2.例题3.测试 四、数组模拟队列1.模板2.例题3.测试 五、数组模拟单调栈1.例题模板2.测试 六、数组模拟单调队列1.例题模板2.测试 引言 首先说一下为什么要拿数组来模拟…