mars3d基于vue3.0的widget使用

news2024/10/7 14:27:42

mars3d在vue3.0生态上开发了两个gis相关开源项目 mars3d-vue-example 和 mars3d-vue-project,在这两个项目中widget都是非常重要的一个模块。通过widget可以在复杂的场景下非常清晰的管理功能模块之间的互斥关系,管理内存,完成不同的功能模块之间的解耦,并且可以实现功能模块之间的交互。下面就来介绍一下,widget模块的使用。

前置依赖: 使用widget模块,则必须依赖vuex,通过vuex实现widget管理、交互等。

widget相关的代码一般会被放置在src/widgets目录下,按照功能或者业务来新建子目录管理widget代码,一般每个目录下的 index.vue 就是此widget的入口,通常也会包括一个map.ts文件用于编写地图相关代码。在某些特殊的场景下,也会将多个vue文件放置在同一个widget目录下,方便共享map.ts的命名空间,后面会针对这些特殊场景具体介绍。下面是一个标准的widget目录位置和结构

创建好widget之后,下面就应该来配置widget,标准写法是在入口main.ts 同级新建widget-store.ts 文件,下面这个示例演示了这个文件的基本结构和一个widget的配置

import { defineAsyncComponent, markRaw } from"vue"import { WidgetState } from"@mars/common/store/widget"import { StoreOptions } from"vuex"conststore: StoreOptions<WidgetState> = {
  state: {
    // 自定义widget的默认值defaultOption: {},
    // widgets 配置列表widgets: [
      {
        // required widget使用的异步组件component: markRaw(defineAsyncComponent(() =>import("@mars/widgets/demo/menu/index.vue"))),
        // required widget的唯一标识name: "menu",
        // 是否可以被自动关闭 default trueautoDisable: false,
        // 此widget打开时,是否自动关闭其他可关闭的widget default truedisableOther: false,
        /*
         * widget分组, widget打开时会强制关闭所有同组的其他widget
         * 优先级高于 disableOther autoDisable
        */group: "demo"
      }
    ],
    // 默认打开的widgetopenAtStart: ["menu"]
  }
}

exportdefault store
复制代码

WidgetState接口定义如下

// 为 store state 声明类型exportinterfaceDefaultOption {
  autoDisable?: boolean
  disableOther?: boolean | string[]
  group?: string// group相同的widget一定是互斥的
  meta?: any// 额外参数 不会在每次关闭后清除
}

exportinterfaceWidget {
  name: string// 唯一标识
  key?: string// 作为vue diff 环节的key,用于控制组件重载
  component?: any// widget关联的异步组件
  autoDisable?: boolean// 是否能够被自动关闭
  disableOther?: boolean | string[] // 是否自动关闭其他widget,或通过数组指定需要被关闭的widget
  group?: string// group相同的widget一定是互斥的
  visible?: boolean// 显示隐藏
  data?: any// 额外传参 会在每次关闭后清除
  meta?: any// 额外参数 不会在每次关闭后清除
}

exportinterfaceWidgetState {
  widgets: Widget[] // widget具体配置openAtStart: string[] // 默认加载的widget
  defaultOption?: DefaultOption// 支持配置默认参数
}
复制代码

还需要在main.ts中初始化配置

import { createApp } from"vue"importApplicationfrom"./App.vue"import { injectState, key } from"@mars/common/store/widget"import { store as testStore, key as testKey } from"@mars/common/store/test"import store from"./widget-store"const app = createApp(Application)

// 此处传入key是为了让widget的store有自己的独立命名空间,不影响项目中的其他store
app.use(injectState(store), key)
// 项目中的其他store
app.use(testStore, testKey)

app.mount("#app")
复制代码

完成上述配置工作之后,就可以编写具体的widget代码了,widget本身其实就是一个vue组件,原则上只负责ui相关的展示和逻辑,地图相关的代码新建map.ts文件进行逻辑上的解耦,widget提供了api来完成组件和map的交互。下面是一个标准的 widget组件的基本结构。

<template><mars-buttonclass="small-btn" @click="onDraw">绘制</mars-button></template><scriptsetuplang="ts">import { onUnmounted, ref } from"vue"import useLifecycle from"@mars/common/uses/use-lifecycle"import * as mapWork from"./map"// 激活map.ts生命周期useLifecycle(mapWork)

constonClick = () => {
  // 调用 map.ts 中的方法
  mapWork.onDraw()
}
// 监听 map.ts 中抛出的事件,基于 mars3d.BaseClass 类实现
mapWork.eventTarget.on("drawExtent", function (event: any) {
  // 此处用于处理事件console.log(event)
})

onUnmounted(() => {
  // 销毁操作
})
</script><stylelang="less"></style>复制代码

上面代码中的 useLifecycle 是一个比较重要的api,他接受map.ts 的导出模块作为参数,建立了vue和map.ts生命周期的关联。可用的map.ts 可用的生命周期为 onMounted 和 onUnmounted,map.ts 标准结构如下。

import * as mars3d from"mars3d"letmap: mars3d.Map// 地图对象// 事件对象,用于抛出事件给vueexportconst eventTarget = new mars3d.BaseClass()

// 初始化当前业务exportfunctiononMounted(mapInstance: mars3d.Map): void {
  map = mapInstance // 记录map
}

// 释放当前业务exportfunctiononUnmounted(): void {
  map.graphicLayer.clear()
  eventTarget = null
  map = null
}

// 绘制矩形(演示map.js与index.vue的交互)exportfunctiononDraw(): void {
  map.graphicLayer.clear()
  // 绘制矩形
  map.graphicLayer.startDraw({
    type: "rectangle",
    style: {
      fill: true,
      color: "rgba(255,255,0,0.2)",
      outline: true,
      outlineWidth: 2,
      outlineColor: "rgba(255,255,0,1)"
    },
    success: function (graphic: mars3d.graphic.RectangleEntity) {
      const rectangle = graphic.getRectangle({ isFormat: true })
      // 抛出事件可以在vue中进行监听
      eventTarget.fire("drawExtent", { extent: JSON.stringify(rectangle) })
    }
  })
}
复制代码

到此我们就初步了解了widget的使用,继续来看一下widget在各种不同的场景下的使用。具体包括和marsui搭配使用、参数配置、动态传参、api、多个根组件场景的处理、widget间的动态交互等。

在学习具体业务场景的使用之前,要先明确一个概念,widget本身并不会负责ui方面的任何事情,总体来说,widget只做了三件事,1. 对ui部分和map部分进行解耦,关联生命周期。2. 对widget各种行为的封装,通过简单的配置和api,完成widget各种交互。 3. 支持对prop的配置和动态修改。当widget的这些特性结合到marsui中的pannel或者dialog使用时,就能够轻松的应对一些复杂场景。

  1. 搭配marsui使用

widget的入口是一个vue单文件组件,在这个组件的模板中我们可以写任何的html或者没有html,在gis的场景中经常需要一些操作面板,这些就可以在组件的模板中来编写这些代码,这个时候又免不了需要去封装一些组件,幸运的是mars-ui恰好解决了这个问题。在火星科技的vue项目中,mars-dialog 和 mars-pannel 通常都会搭配widget一起使用来实现这种操作面板的场景。下面是一个简单的示例

<template><!-- 这是一个宽 370px 位于左上角的固定面板 --><mars-pannelwidth="370"left="10"top="10"><a-space><mars-button>按钮1</mars-button><mars-button>按钮2</mars-button><mars-button>按钮3</mars-button></a-space></mars-pannel></template><template><!-- 这是一个宽 370px 位于左上角的可移动可缩放的面板 --><mars-dialogwidth="370"left="10"top="10"><a-space><mars-button>按钮1</mars-button><mars-button>按钮2</mars-button><mars-button>按钮3</mars-button></a-space></mars-dialog></template>复制代码

有一个细节,上面的示例并没有去传入visible参数,那么pannel或者dialog是如何显示的呢,这里就涉及到组件的属性继承。widget已经在外部传入了visible属性,因为两个示例都只存在一个根组件,所以就自动继承的visible属性。我们也可以手动的进行属性继承,后面会这对这种场景具体分析。

  1. 参数配置

WidgetState 接口定义了widget相关的配置参数,具体可参考上一篇文档,(下文中提到的defaultOption、 meta、data等概念也请参看上一篇文档)。这里主要来介绍一下在widget中对ui控件的一些配置。首先在defaultOption中可以为所有的widget配置prop,写法如下:

//widget-store.ts//这里配置的参数会被合并到所有组件的prop中,组件中的相同属性将会被覆盖defaultOption: {
  meta: {
    props: {
      top:50,
      bottom:50,
      left:50
    }
  }
}
复制代码

除了defaultOption 也可以在每一个widget配置中传入自己的个性化参数,写法如下:

// widget-store.ts
// 这里配置的参数会被合并到本组件的prop中,组件和defaultOption中的相同属性都将会被覆盖
{
  component: markRaw(defineAsyncComponent(() => import("your-widget.vue"))),
  name: "your-widget",
  meta: {
    props: {
      top: 50,
      bottom: 50,
      left: 50
    }
  }
}
复制代码

以上都是通过配置的方式来设置组件的prop值,widget还支持通过动态调用的方式,进行prop的设置:

import { useWidget } from"@mars/common/store/widget"const { activate, disable, getWidget } = useWidget()

// 激活 your-widgetactivate({
    name: "your-widget",
    data: {
      // 为这个组件设置prop,此处属性的优先级,高于所有配置中的属性
      props: {
        top: 50,
        bottom: 50,
        left: 50
      }
    }
  })
})
复制代码
  1. API

上面的示例中用到了 activate 这个api,用来激活widget并传递data,他通过useWidget 函数导出,useWidget提供了多个api来操作widget。

exportconstuseWidget: () => {
  // 本页面widget配置数组widgets: ComputedRef<Widget[]>
  // 默认开启的widgetopenAtStart: ComputedRef<string[]>
  // 获取指定的widgetgetWidget: (name: string) =>Widget// 出发对应widget的onUpdateupdateWidget: (name: string, ...args: any[]) => viod
  // 获取widget的当前激活状态isActivate: (name: string) =>boolean// 激活指定 widget模块activate: (widget: string | Widget, reload = true) =>void// 释放指定的widgetdisable: (name: string) =>void// 关闭释放所有widget ,hasAll传true值强制释放所有widget(默认autoDisable为false的widet不会释放)disableAll: (hasAll?: boolean) =>void
}
复制代码
  1. 多个根组件的场景,假设一个widget的组件像下面这样。

<template><mars-pannel:width="305"left="15"top="170"bottom="40"></mars-pannel><mars-pannel:width="340"right="10"top="10"bottom="40"></mars-pannel></template>复制代码

此时会发现,这个widget激活之后无法在页面中显示,这是因为这里有两个根组件,此时vue无法自动继承prop,所以需要我们手动的来完成这个继承的操作,所以需要改成下面这样。

<template><mars-pannel:width="305"left="15"top="170"bottom="40"v-bind="attrs"></mars-pannel><mars-pannel:width="340"right="10"top="10"bottom="40"v-bind="attrs"></mars-pannel></template><scriptsetuplang="ts">import { useAttrs } from"vue"const attrs = useAttrs()
</script>复制代码

这种方式就可以实现具有多个面板的widget,我们也可以根据自身的需求来确定让哪些面板来继承外部的props。甚至在只有一个面板时我们也可以对外部的props做一些自定义的处理。只需要再加上inheritAttrs: false 来禁用继承。

// 这里禁用了属性继承,并且将内联的prop优先级提升到最高。
<template><mars-pannelv-bind="attrs":width="305"left="15"top="170"bottom="40"></mars-pannel></template><scriptsetuplang="ts">import { useAttrs } from"vue"const attrs = useAttrs()
</script><scriptlang="ts">exportdefault {
  inheritAttrs: false
}
</script>复制代码
  1. widget间的交互

一些复杂的场景中可能会需要两个widget之间有一些数据的传递,这里是通过updateWidget来实现的。

// widget1
<template><mars-pannelwidth="370"left="10"top="10"><mars-button @click="change">按钮1</mars-button></mars-pannel></template><scriptsetuplang="ts">import { useWidget } from"@mars/common/store/widget"const { updateWidget } = useWidget()
constchange = () => {
  updateWidget("widget2", { 
    /* 传递参数,这里的参数将会被处理成响应式数据,如果需要传递复杂对象,请使用markRaw */ 
  })
}
</script>复制代码
// widget2
<scriptsetuplang="ts">import { useWidget } from"@mars/common/store/widget"const { getWidget } = useWidget()

const widget = getWidget("widget2") // 获取自身的widget

widget.onUpdate((...args: any[]) => {
    // 监听 updateWidget 的调用console.log(...args)
})
</script>复制代码

接下来就针对实际需求看一下widget本身的代码是如何编写的。我们将一起来完成一个widget的编写,其中会介绍到我们在开发项目时会用到的一些技巧以及遵循的规范。由于gis项目本身的特殊性,如果不想后续当业务变的复杂之后造成一些难以排查的bug或者性能问题,最好也和我们一样遵循这些简单的规范。

功能:我们接下来要实现的是底图切换的面板,具体功能如下图

下面我们来一步步的实现

  1. 创建文件

我们在widgets目录下新建一个目录 src/widgets/basic/manage-basemap 这里我们要求widget都统一存放在widgets下。然后创建 index.vue文件作为入口。由于将要实现的功能,包括一些地图相关的操作,所以我们还需要创建 map.ts 文件用于编写这些代码。这两个文件基础的代码已经在前面的两篇中有详细的说明,这里不做赘述。

  1. 获取底图

获取地图这个操作本身很简单 map.getBasemap(true),但是需要考虑的是,我们需要通过map进行调用,所以这个操作需要放在 map.ts 文件中。这也是我们项目中的的另一个规范:只要是与mars3d相关的代码,我们都会放在 map.ts 中。看起来这个规范过于严苛,但是确实能够很好的帮我们将ui部分与地图部分进行接耦。我们初步的代码就变成了这样。

// map.ts export function onMounted(mapInstance: mars3d.Map): void {
  map = mapInstance // 记录mapconst baseMaps = map.getBasemaps(true) // 获取底图const hasTerrain = map.hasTerrain // 是否显示地形
}
复制代码
  1. 接下来又面临一个问题,此时我们将地图与ui强制分离之后,ui部分该如何获取到 baseMaps 呢?此时就用到了我们之前说过的 eventTarget 了。代码很简单就直接放在下面了。

// map.ts exportconst eventTarget = new mars3d.BaseClass()

export function onMounted(mapInstance: mars3d.Map): void {
  map = mapInstance // 记录mapconst baseMaps = map.getBasemaps(true) // 获取底图const hasTerrain = map.hasTerrain // 是否显示地形

  eventTarget.fire("mapLoaded", { baseMaps, hasTerrain }) // 触发了自定义事件
}
复制代码
// index.vueimport { onUnmounted, ref, markRaw } from"vue"import useLifecycle from"@mars/common/uses/use-lifecycle"import * as mapWork from"./map"const baseMaps = ref<any[]>([]) // 底图列表const active = ref("") // 当前高亮的底图const chkHasTerrain = ref(false) // 是否显示地形

mapWork.eventTarget.on("mapLoaded", initData)

onUnmounted(() => {
  mapWork.eventTarget.off("mapLoaded", initData)
})

functioninitData(e: any) {
  baseMaps.value = e.baseMaps.map((m: any) => {
    if (m.isAdded && m.show) {
      active.value = m.uuid
    }
    return {
      name: m.name,
      uuid: m.uuid,
      options: markRaw(m.options)
    }
  })

  chkHasTerrain.value = e.hasTerrain || false
}
复制代码

上面的代码很好理解,不过有些细节还是要注意一下,请关注一下26行,可以看到这里是用了一个特殊的函数 markRaw。这是vue提供的一个函数,用于表示此对象不需要被vue进行响应式处理。这里的目的是mars3d本身会产生一些复杂的对象,比如map对象,矢量数据、图层等,这些复杂对象如果再次被vue进行一次响应式处理,将会带来极大的性能开销。

  1. 现在我们来实现,点击之后切换底图和切换地形,直接放代码。

// map.tsexport function changeBaseMaps(id: string){
  map.basemap = id
}

export function changeTerrain(value: boolean) {
  map.hasTerrain = value
}
复制代码
// index.vue
function changeBaseMaps(item: any) {
  mapWork.changeBaseMaps((active.value = item.uuid))
}

function changeTerrain() {
  mapWork.changeTerrain(chkHasTerrain.value)
}
复制代码
  1. 目前就剩下最后一步,支持外部更新高亮底图,用于实现通过其他方式切换底图,自动更新高亮的图块的功能。这里就要使用到widget的一个api widget.onUpdate来实现。其实就像相当于一个事件的订阅者。代码如下。

// index.vue
const { getWidget } = useWidget()

const widget = getWidget("manage-map" /* widget的name */)

widget.onUpdate((e) => {
  changeBaseMaps(e)
})
复制代码
// 外部改变底图时
const { updateWidget } = useWidget()

const changeBaseMap = (item) => {
  updateWidget("manage-map", item)
}
复制代码

over~~~~

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

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

相关文章

ZBC通证月内已翻倍,Nautilus Chain 上线前夕的“开门红”

近日&#xff0c;Zebec Protocol生态通证ZBC迎来了大涨&#xff0c;据悉该通证月内最高涨幅接近了100%&#xff0c;为一众投资者、社区用户、Zepoch节点等带来了可观的回报&#xff0c;并为生态发展注入了十足的信心。我们看到&#xff0c;Zebec Protocol生态在近期宣布了“销毁…

D1s RDC2022纪念版开发板开箱评测及点屏教程

作者new_bee 本文转自&#xff1a;https://bbs.aw-ol.com/topic/3005/ 目录 芯片介绍开发板介绍RT-Smart用户态系统编译使用感想引用 1. 芯片介绍 RISC-V架构由于其精简和开源的特性&#xff0c;得到业界的认可&#xff0c;近几年可谓相当热门。操作系统方面有RT-Thread&am…

Kubernetes 如何通过ingress-nginx实现应用灰度发布?

在日常的工作中&#xff0c;我们会经常对应用进行发版升级&#xff0c;在互联网公司尤为频繁&#xff0c;主要是为了满足快速的业务发展。我们经常用到的发布方式有滚动更新、蓝绿发布、灰度发布。滚动更新&#xff1a;依次进行新旧替换&#xff0c;直到旧的全部被替换为止。蓝…

FATFS函数浅谈 看完学会FATSFS,建议收藏

目录 一、注册工作区域 二、打开文件夹 三、读取文件夹 四、打开\新建一个文件 五、读取文件 六、写文件 七、移动文件指针 八、截断文件 九、刷新缓存消息 十、新建文件夹 十一、删除文件或文件夹 十二、重命名\移动文件或文件夹 十三、获取文件信息 十四、改变…

KNN算法及Python实现

0 建议学时 2学时 1 KNN算法 1.1 KNN原理 KNN&#xff1a;K Nearest Neighbors&#xff0c;即K个最近的邻居&#xff1b; 预测一个新值xxx&#xff0c;根据距离最近的K个点的类别来判断xxx属于哪一类。 算法核心要点&#xff1a; K值的选取非常重要&#xff1b; 距离公式…

山东大学电磁场与电磁波期末试题

文章目录一、电磁场的基本规律二、静态电磁场及其边值问题的解三、分离变量法四、均匀平面波的反射与透射五、时变电磁场与均匀平面波在无界空间中的传播六、导行电磁波七、电磁辐射往年真题回忆复习建议一、电磁场的基本规律 设在 x<0x<0x<0 处为真空&#xff0c;x&…

LeetCode分类刷题----回溯算法

回溯1.回溯问题77.组合216.组合总和|||17.电话号码的字母组合39.组合总和40.组合总和||131.分割回文串93.复原IP地址78.子集90.子集||491.递增子序列46.全排列47.全排列||51.N皇后37.解数独1.回溯问题 77.组合 思路&#xff1a; 回溯的本质是用一棵树来描述&#xff0c;用pat…

Gitee码云 操作

1&#xff1a;Git团队协作机制1.1&#xff1a;团队内协作1.2&#xff1a;跨团队协作2&#xff1a;Gitee码云 操作码云网址&#xff1a; https://githee.com/2.1&#xff1a;创建远程仓库2.2&#xff1a;远程仓库操作命令名称作用git remote -v查看当前所有远程地址别名git remo…

Java缓存面试题——Redis应用

文章目录1、为什么要使用Redis做缓存&#xff1f;2、为什么Redis单线程模型效率也能那么高&#xff1f;3、Redis6.0为什么要引入多线程呢&#xff1f;4、Redis常见数据结构以及使用场景字符串&#xff08;String&#xff09;哈希(Hash)列表&#xff08;list&#xff09;集合&am…

【机器学习】马尔可夫链与隐马尔可夫模型(HMM)

1.马尔可夫链(Markov Chain) 马尔可夫链&#xff08;Markov chain&#xff09;&#xff0c;又称离散时间马尔可夫链&#xff08;discrete-time Markov chain&#xff09;&#xff0c;因俄国数学家安德烈马尔可夫&#xff08;A.A.Markov&#xff09;得名。描述的是状态空间中经过…

Win11系统user profile service服务登录失败解决方法

Win11系统user profile service服务登录失败解决方法分享。有用户在使用电脑的时候遇到了一些问题&#xff0c;系统的user profile service服务无法登录了。出现这个问题可能是系统文件损坏&#xff0c;或者中了病毒。接下来我们一起来看看如何解决这个问题的操作方法分享吧。 …

【unity细节】基于unity子对象(如相机)为什么无法进行z轴的拖拽移动和z轴自动归位的问题

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity细节和bug ⭐基于unity子对象为什么无法进行z轴的拖拽移动和z轴自动归位⭐ 文章目录⭐基于u…

学习系统编程No.5【虚拟地址空间】

引言: 北京时间&#xff1a;2023/2/22&#xff0c;离补考期末考试还有5天&#xff0c;不慌&#xff0c;刚午觉睡醒&#xff0c;闹钟2点20&#xff0c;拖到2点50&#xff0c;是近以来&#xff0c;唯一一次有一种睡不醒的感觉&#xff0c;但是现在却没有精神&#xff0c;因为听了…

【Spring Cloud Alibaba】007-Nacos 配置*

【Spring Cloud Alibaba】007-Nacos 配置* 文章目录【Spring Cloud Alibaba】007-Nacos 配置*一、概述1、概述2、对比 spring cloud config二、基本使用1、在管理界面新建配置2、启动权限3、 搭建 nacos-config 服务第一步&#xff1a;引入依赖第二步&#xff1a;修改 yaml 配置…

交叉编译 MQTT/Mosquitto

交叉编译 MQTT/Mosquitto 概述 Eclipse Mosquitto 是一个开源&#xff08;EPL/EDL许可&#xff09;消息代理&#xff0c;它实现了 MQTT 协议版本 5.0、3.1.1 和 3.1。Mosquitto 重量轻&#xff0c;适用于从低功耗单板计算机到全服务器的所有设备。 MQTT 协议提供了一种使用发…

【Linux】-- 开发工具(vim、gcc、g++、make/Makefile)

目录 Linux编辑器-vim使用 vim的基本概念 vim的简单配置 vim的基本操作 vim进阶命令集 插入模式 从插入模式切换为命令模式 移动光标 删除文字 复制 替换 撤销上一次操作 更改 跳至指定的行 vim底行模式命令集 列出行号 跳到文件中的某一行 查找字符 保存文…

黑马程序员 Java 项目《瑞吉外卖》

教程链接&#xff1a;https://www.bilibili.com/video/BV13a411q753 Gitee 仓库&#xff1a;https://gitee.com/weixinyang1980/reggie_take_out 运行视频&#xff1a; 瑞吉外卖后台运行视频 瑞吉外卖用户端运行视频 目录开发环境搭建数据库环境搭建Maven 项目搭建后台登录功能…

通过连接另一个数组的子数组得到一个数组

给你一个长度为 n 的二维整数数组 groups &#xff0c;同时给你一个整数数组 nums 。 你是否可以从 nums 中选出 n 个 不相交 的子数组&#xff0c;使得第 i 个子数组与 groups[i] &#xff08;下标从 0 开始&#xff09;完全相同&#xff0c;且如果 i > 0 &#xff0c;那么…

Python四大主题之一【 Web】 编程框架

目前Python的网络编程框架已经多达几十个&#xff0c;逐个学习它们显然不现实。但这些框架在系统架构和运行环境中有很多共通之处&#xff0c;本文带领读者学习基于Python网络框架开发的常用知识,及目前的4种主流Python网络框架&#xff1a;Django、Tornado、Flask、Twisted。 …

论文复现:风电、光伏与抽水蓄能电站互补调度运行(MATLAB-Yalmip全代码)

论文复现:风电、光伏与抽水蓄能电站互补调度运行(MATLAB-Yalmip全代码) 针对风电、光伏与抽水蓄能站互补运行的问题,已有大量通过启发式算法寻优的案例,但工程上更注重实用性和普适性。Yalmip工具箱则是一种基于MATLAB平台的优化软件工具箱,被广泛应用于工程界优化问题和…