当前位置: 首页 > news >正文

Web IDE 在线编辑器综合实践(Web IDE 技术探索 三)

前言

        前面两篇文章,我们简单讲述了 WebContainer/api 、Terminal 的基本使用,离完备的在线代码编辑器就差一个代码编辑了。今天通过 monaco editor ,来实现初级代码编辑功能,讲述的是整个应用的搭建,并不单独针对monaco editor的使用哈,因为Monaco editor 确实有些难度,仅在使用到的API 、功能模块上做讲解。如果大家有需要,可以留言,会考虑后期做一篇monaco的保姆级教程。

页面布局

        初始化 pnpm、vite、typescript的项目,将页面初始化为下:

 文件树

        此处的文件树,是指项目左侧的文件列表,使用ElementPlus tree 组件进行渲染,如下:

// 定义 filemenu tree data
export interface ITreeDataFile {id: string;icon?: string;label: string;suffix: string;
}
// 文件夹数据结构
export interface ITreeDataFolder {id: string;label: string;isFolder: boolean;children: ITreeDataFile[];
}
// 可能是新建文件
export interface ITreeDataIsNew {id: string;isNew: boolean;isFolder: boolean;
}

        针对新建文件/文件夹时,需要知道当前层级,例如我是在 根目录新建 还是在src内新建,因此,需要监听tree 的click 事件:

 /*** 节点点击回调 - 通过该参数实现识别当前的目录层级* @param data*/function nodeClick(data: ITree) {currentNodeKey.value = data.id;}

        同时,在点击外部时,还需要取消目录选中:

  /*** cancelChecked*/function cancelChecked() {//  .is-current 通过该类实现的当前文件激活样式currentNodeKey.value = "";treeRef.value?.setCurrentKey();}

事件响应

  // 折叠所有文件function collapseAll() {// 全部展开 - 可用于定位某个文件// Object.values(treeRef.value!.store.nodesMap).forEach((v) => v.expand())Object.values(treeRef.value!.store.nodesMap).forEach((v) => v.collapse());}

         新建文件/文件夹的核心就是blur后,使用 newFileName push到指定位置上:

  /*** confirm 新建文件/文件夹确认事件*/function confirm() {removeNewItem(dataSource);if (!newFileName.value) return;// 不然,就根据当前位置,push 真实的数据到dataTree中,通过 newFileFlag.value 识别是文件还是文件夹const fileSuffix = newFileName.value.split(".")[1];const data: ITreeDataFile | ITreeDataFolder = {id: `${new Date().getTime()}`,label: newFileName.value,isFolder: !newFileFlag.value,children: [],icon: newFileFlag.value ? getFileIcon(fileSuffix) : "",};if (currentNodeKey.value) {// 如果有节点被选中,则看是文件,还是文件夹,是文件-在父级添加,是文件夹-直接在当前添加const currentNode = treeRef.value?.getNode(currentNodeKey.value);if (currentNode?.data.isFolder) {// 如果是文件夹,则在当前节点下添加treeRef.value?.append(data, currentNodeKey.value);} else {// 如果是文件,则在 Tree 中给定节点后插入一个节点treeRef.value?.insertAfter(data, currentNodeKey.value);}} else {// 如果没有节点被选中,则直接添加到根目录dataSource.push(data);}}

Terminal

        这块应该是简单的,参考上篇文章哈Terminal Web终端基础(Web IDE 技术探索 二)

        往后可能需要拓展多终端场景,因此设计上需要考虑周全,剩下的功能待开发时再细说。

Web Container

       这里强调下哈!Web Container的API基本都是 async / await 方式,因此,在使用时一定需要注意执行时机和等待结果!!!

        配置 WebContainer/api 跨源隔离:

headers: {"Cross-Origin-Embedder-Policy": "require-corp","Cross-Origin-Opener-Policy": "same-origin",}

        WebContainer的很多事件都需要await执行,在设计上需要考虑周全,因为多处需要共享container的状态,因此我们直接使用pinia实现全局状态管理:

// Web Container 共享文件,因为 fileTree Container对象需要在其他文件中共享
import { WebContainer } from "@webcontainer/api";
import { defineStore } from "pinia";// 第一个参数是应用程序中商店的唯一 id
export const useContainerStore = defineStore("container", {state: () => {return {container: <InstanceType<typeof WebContainer> | null>null,boot: false, // 定义容器是否启动};},actions: {// 1. bootContainer 启动容器async bootContainer() {// @ts-ignorethis.container = await WebContainer.boot();this.boot = true;},},
});

        在App页面监听 boot 实现loading效果:

    <!-- loading --><div class="loading" v-if="!containerStore.boot"><div class="loader"></div><span>Wait for the web container to boot...</span></div>

         在Container中,需要频繁监听输出流,统一做事件封装处理:

    // 封装统一的输出函数 - 监听容器输出async output(stdout: WebContainerProcess, fun: voidFun) {stdout.output.pipeTo(new WritableStream({write(data) {fun(data);},}));},

        封装统一的命令执行函数,提供给terminal执行:

    // 3. 执行 terminal 命令async runTerminal(cmd: string, fun: voidFun) {if (!this.container) return;const command = cmd.split(" "); // 这里是按空格进行分割const state = await this.container.spawn(command[0], command.slice(1));// 如果是下载命令,则需要获取状态码if (command[1] === "install" || command[1] === "i") {const code = await state.exit;if (code === 0) // ... 执行相关代码}// 不管成功还是失败,都输出this.output(state, fun);},

         在terminal 中,监听 command事件,直接传递到 container中执行,通过回传参数实现terminal的终端显示:

function command(cmdKey: string,command: string,success: voidFun,failed: voidFun,name: string
) {containerStore.runTerminal(command, (content) => {success({ content });console.log(name, "执行command:", command);});

 

文件菜单与FileSystemTree

        在逻辑上,是先有的文件,才去执行 mounted 操作,因此,当我新建文件的时候,都去调用 mounted 。在初始化时,我们提供三种基本的项目结构:mockVueProject、mockNodeProject、mockReactProject,用Vue 举例哈,其他类似,具体的FileSystemTree可以参考我的上篇文章File System Tree:

 读取成树结构

        通过以上的树结构,读取成El-tree 组件的数据源,应该不是难事,递归实现即可,在上一篇中已经实现了,但是注意哈,需要在结束时,进行排序,先排目录结构 isFolder,在排name属性,这样就是与vscode类似的效果:

新增文件

  /***  将新建的文件/文件夹挂载到Web Container File System Tree 中*/function mountedFileSystemTree() {tryCatch(async () => {let path = "/";// 如果有选中节点,则需要处理选中节点的路径问题if (currentNodeKey.value) {// 需要在这里加上父级 - 这里还需要判断激活的是文件还是文件夹const currentNode = treeRef.value?.getNode(currentNodeKey.value); // 当前激活节点const dataMap = JSON.parse(JSON.stringify(dataSource)) as TFullData;let fullpath = <string[]>getFullPath(dataMap, currentNodeKey.value);if (currentNode?.data.isFolder) path += fullpath?.join("/");else {// 删除最后一项fullpath = fullpath?.slice(0, -1);path += fullpath?.join("/");}path += "/";}// 如果没有选中节点,则直接拼接文件名称,放置到根路径下即可// 例如 /vite.config.jspath += newFileName.value;console.log("### path ==> ", path);newFileFlag.value? containerStore.addFile(path): containerStore.addFolder(path);});}

Monaco Editor

        上诉简单介绍了整个系统的文件系统、container与termina的关系与核心实现,并通过新增文件/文件夹实现Web Container FileSystemTree的文件挂载、写入、创建文件夹,但是还是没有实质性的文件内容编辑,现在通过monaco editor 插件实现文件内容编辑,monaco确实是有难度的,本文不过及底层原理,仅在应用层面上做叙述。

create

// use monaco editor
import { editor } from "monaco-editor";/*** init monaco*/function initMonaco(selector: string) {const dom = document.querySelector(selector) as HTMLElement;editor.create(dom, {value: "function x() {\n\tconsole.log('Hello world!');\t\n}",language: "javascript",});}

        但是这样是要报错的:Uncaught Error: Unexpected usage,详见ISSUES,解决办法:

// 解决 monaco editor 报错 Uncaught Error: Unexpected usage:import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";export function fixEnvError() {window.MonacoEnvironment = {getWorker(_, label) {if (label === "json") {return new jsonWorker();}if (label === "css" || label === "scss" || label === "less") {return new cssWorker();}if (label === "html" || label === "handlebars" || label === "razor") {return new htmlWorker();}if (label === "typescript" || label === "javascript") {return new tsWorker();}return new editorWorker();},};
}

         create 之前,先调用 fixEnvError 方法,导入需要的worker文件:

  function initMonaco(selector: string) {fixEnvError();const dom = document.querySelector(selector) as HTMLElement;editor.create(dom, {value: "function x() {\n\tconsole.log('Hello world!');\t\n}",language: "javascript",});}

动态设置属性

    /** 为了避免Vue响应式对编辑器的影响,使用toRaw转成普通对象 */getEditor() {return toRaw(this.editor);},/** 设置编辑器的值 + 设置语言模型 */setValue(value: string, language: string) {this.getEditor()?.setValue(value);// 1. 文件后缀与语言模型匹配const languageModel = this.languages.find((item) => {return item.extensions?.includes(`.${language}`);});editor.setModelLanguage(this.getEditor()?.getModel()!,languageModel?.id || "");},/** 获取编辑器的值 */getValue() {return this.getEditor()?.getValue();},

         在菜单点击时,获取文件内容,进行editor赋值,处理上,直接使用 this.editor.setValue会导致页面卡死,转成普通对象,避免响应式的影响,同时,在设置值上,需要动态调整语言类型,不然不会高亮显示:

监听保存事件

        通过保存事件,实现真正的文件存储:

    onKeyDownHandle(e: any) {// 通过keycode/ctrlKey/shiftKey/altKey 的状态唯一确定一个事件- 有值为true,无值为falseconst eventMap: TKeyMap<string, voidFun> = {"49/true/false/false": () => {console.log("Ctrl S");},};const key = `${e.keyCode}/${e.ctrlKey}/${e.shiftKey}/${e.altKey}`;if (eventMap[key]) {eventMap[key]();e.browserEvent.preventDefault();}},
    // eventCtrlSeventSave() {const containerStore = useContainerStore();const fileMenuStore = useFileMenuStore();// 1. 获取当前编辑器的内容const contents = this.getEditor()?.getValue() as string;// 2. 调用 container 的 saveFile 方法containerStore.writeFile(fileMenuStore.filePath, contents);},

针对依赖下载的优化

// 特殊的命令需要单独处理if (installCmdList.includes(command)) {// 执行下载依赖,应该用回显模式success(flash);containerStore.runTerminal(command, (content) => {console.log(content, content.includes("Done"));if (content.includes("Done")) {flash.finish();// 把最后的信息输出success({ content: "✅ " + content });} else flash.flush(content);});}

        使用回显模式展示依赖下载,会更加合适

多tab页模式 

        tab 切换的和核心,是通过记录editor 的状态及语言模型实现的:

  // 1. 关键参数 mapconst fileStateMap = new Map();//   切换文件 - 需要保存 stateasync switchFile(index: number) {const fileSuffix = this.fileList[index].suffix;// 2. 跳转到指定文件this.currentFile = index;// 3. 看看跳转后文件时候有 model 有的话直接使用,没有就创建新的const file = this.fileStateMap.get(this.getCurrentFileID());if (file && file.model) {this.setModel(toRaw(file.model));this.restoreViewState(toRaw(file.state)); // 恢复文件的编辑状态} else {// 2. 读取文件内容赋给monacoconst contents = await this.containerStore.readFile(this.fileMenuStore.filePath);const model = this.createModel(contents || "",this.getLanguageModel(fileSuffix)?.id as string);this.setModel(model);this.fileStateMap.set(this.getCurrentFileID(), {model: this.getModel(),state: this.saveViewState(),});}this.getEditor()?.focus();},

        关闭则是通过监听事件实现:

window.addEventListener("mouseup", (e: MouseEvent) => {const span = e.target as HTMLElement;if (e.button === 1 && span.getAttribute("data-key") === "closeFileButton") {// 1. 先保存monacoStore.eventSave();// 2. 关闭文件const index = span.getAttribute("data-index");monacoStore.deleteFile(Number(index));}
});

        在你关闭的是其他tab页的时候,涉及到不同的model获取内容,因此,需要先跳转到需要关闭的页面,获取完内容,再跳转回正常的页面,类似VScode,不然你获取的内容是不对的哈!

总结

        通过WebContainer、Terminal、MonacoEditor的结合,初步实现了Web IDE在线编辑器的开发,整体实现过程还是比较顺利的,但是monaco的应用太痛苦了,全英文,官网API还是.d.ts类型文件!

        不过不得不说,monaco的强大之处,远不止这么简单,支持git冲突模型对比:

        利用yjs 原生支持 y- monaco:

         大家感兴趣,后续会考虑整理Monaco Editor的保姆级使用教程,大家多多支持呀~

相关文章:

Web IDE 在线编辑器综合实践(Web IDE 技术探索 三)

前言 前面两篇文章&#xff0c;我们简单讲述了 WebContainer/api 、Terminal 的基本使用&#xff0c;离完备的在线代码编辑器就差一个代码编辑了。今天通过 monaco editor &#xff0c;来实现初级代码编辑功能&#xff0c;讲述的是整个应用的搭建&#xff0c;并不单独针对monac…...

Less is more VS 精一 [生活感悟]

"Less is More”和王阳明的“精一”思想确实有相似之处。 王阳明的“精一”思想强调的是专注于一件事&#xff0c;将其做到极致&#xff0c;这与"Less is More”中提倡的通过减少数量来提高质量的理念不谋而合。两者都强调了专注和深度的重要性&#xff0c;而不是追…...

函数的概念及图像

注&#xff1a; 判断两函数是否相同&#xff0c;只看定义域和对应法则。 1. 函数的定义 一般的&#xff0c;在一个变化过程中有两个变量 x&#xff0c;y。如果对于x在某个变化范围内的每一个确定值&#xff0c;按照某个对应法则&#xff0c;都有唯一确定的值y和他对应。那么y就…...

Linux中Apache网站基于Http服务的访问限制(基于地址/用户)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f468;‍&#x1f4bb;Linux高级管理专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月3日11点44分 &#x1f004;️文章质量&#xff1a;95分 为了更好地控制对网站资源的访问&#xff0c;可…...

滚动条详解:跨平台iOS、Android、小程序滚动条隐藏及自定义样式综合指南

滚动条是用户界面中的图形化组件&#xff0c;用于指示和控制内容区域的可滚动范围。当元素内容超出其视窗边界时&#xff0c;滚动条提供可视化线索&#xff0c;并允许用户通过鼠标滚轮、触屏滑动或直接拖动滑块来浏览未显示部分&#xff0c;实现内容的上下或左右滚动。它在保持…...

06 Linux 设备驱动模型

1、Overview Linux-2.6 引入的新的设备管理机制 - kobject 降低设备多样性带来的 Linux 驱动开发的复杂度,以及设备热拔插处理、电源管理等将硬件设备归纳、分类,然后抽象出一套标准的数据结构和接口驱动的开发,就简化为对内核所规定的数据结构的填充和实现驱动模型是 Linu…...

检测五个数是否一样的算法

目录 算法算法的输出与打印效果输出输入1输入2 打印打印1打印2 算法的流程图总结 算法 int main() {int arr[5] { 0 };int i 0;int ia 0;for (i 0; i < 5; i) { scanf("%d", &arr[i]); }for (i 1; i < 5; i) {if (arr[0] ! arr[i]) {ia 1;break;} }…...

java 原生http服务器 测试JS前端ajax访问实现跨域传post数据

后端 java eclipse 字节流转字符 package Httpv3;import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer;import java.io.IOException; import java.i…...

【机器学习】消息传递神经网络(MPNN)在分子预测领域的医学应用

1. 引言 1.1. 分子性质预测概述 分子性质预测是计算机辅助药物发现流程中至关重要的任务之一&#xff0c;它在许多下游应用如药物筛选和药物设计中发挥着核心作用&#xff1a; 1.1.1. 目的与重要性&#xff1a; 分子性质预测旨在通过分子内部信息&#xff08;如原子坐标、原…...

Python Flask实现蓝图Blueprint配置和模块渲染

Python基础学习&#xff1a; Pyhton 语法基础Python 变量Python控制流Python 函数与类Python Exception处理Python 文件操作Python 日期与时间Python Socket的使用Python 模块Python 魔法方法与属性 Flask基础学习&#xff1a; Python中如何选择Web开发框架&#xff1f;Pyth…...

Vue10-事件修饰符

一、示例&#xff1a;<a>标签不执行默认的跳转行为 1-1、方式一 <a href"http://www.baidu.com" onclick"event.preventDefault();">点击我</a> 1-2、方式二 1-3、方式三&#xff1a;事件修饰符 二、Vue的六种事件修饰符 2-1、prevent&…...

oracle中如何查询特定日期?

1. select last_day(to_date(20230101,YYYYMMDD)) from dual; select last_day(to_date(V_END_DATE,YYYYMMDD)) from dual; --查询任意一天 当月的最后一天 2. select to_char(to_date(20230101,YYYYMMDD)-1,YYYYMMDD) INTO V_START_DATE FROM DUAL; select to_char(to_dat…...

Python使用rosbag使用getattr只能获取一层的数据,不能直接获取多层数据例如 a.b.c.d。使用for range写一个递归用来获取多层数据

使用for循环和range来遍历属性列表确实是一个更简单直观的方式&#xff0c;特别是不需要考虑性能优化和异常处理时。以下是使用for循环代替递归的示例代码&#xff1a; python def get_nested_attr(obj, attr_str): attrs attr_str.split(.) for attr in attrs: # 尝试获取下…...

LNWT--篇章三小测

问题1: BERT训练时候的学习率learning rate如何设置? 在训练初期使用较小的学习率&#xff08;从 0 开始&#xff09;&#xff0c;在一定步数&#xff08;比如 1000 步&#xff09;内逐渐提高到正常大小&#xff08;比如上面的 2e-5&#xff09;&#xff0c;避免模型过早进入…...

【NoSQL】Redis练习

1、redis的编译安装 systemctl stop firewalld systemctl disable firewalld setenforce 0 yum install -y gcc gcc-c make wget cd /opt wget https://download.redis.io/releases/redis-5.0.7.tar.gz tar zxvf redis-5.0.7.tar.gz -C /opt/cd /opt/redis-5.0.7/ # 编译 make…...

Git 和 Github 的使用

补充内容&#xff1a;EasyHPC - Git入门教程【笔记】 文章目录 常用命令配置信息分支管理管理仓库 概念理解SSH 密钥HTTPS 和 SSH 的区别在本地生成 SSH key在 Github 上添加 SSH key 使用的例子同步本地仓库的修改到远程仓库拉取远程仓库的修改到本地仓库拉取远程仓库的分支并…...

学习分享-断路器Hystrix与Sentinel的区别

断路器&#xff08;Circuit Breaker&#xff09;简介 断路器&#xff08;Circuit Breaker&#xff09;是一种用于保护分布式系统的服务稳定性和容错性的设计模式。它的主要作用是在检测到某个服务的调用出现故障&#xff08;如超时、异常等&#xff09;时&#xff0c;快速失败…...

社区物资交易互助平台的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;基础数据管理&#xff0c;论坛管理&#xff0c;公告信息管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;论坛&#xff0c;求助留言板&#xff0c;公…...

19-Nacos-服务实例的权重设置

19-Nacos-服务实例的权重设置 1.根据权重负载均衡&#xff1a; 1.服务器设备性能有差异&#xff0c;部分实例所在及其性能较高&#xff0c;有一些较差&#xff0c;我们希望性能好的机器承担更多的用户请求 Nacos提供了权重配置来控制访问频率&#xff0c;权重越大则访问频率…...

R语言数据探索和分析23-公共物品问卷分析

第一次实验使用最基本的公共物品游戏&#xff0c;不外加其他的treatment。班里的学生4人一组&#xff0c;一共44/411组。一共玩20个回合的公共物品游戏。每回合给15秒做决定的时间。第十回合后&#xff0c;给大家放一个几分钟的“爱心”视频&#xff08;链接如下&#xff09;&a…...

Webix前端界面框架:深度解析与应用实践

Webix前端界面框架&#xff1a;深度解析与应用实践 Webix&#xff0c;作为一款功能强大的前端界面框架&#xff0c;近年来在开发社区中逐渐崭露头角。本文将从四个方面、五个方面、六个方面和七个方面&#xff0c;深入剖析Webix的特性、优势、应用实践以及面临的挑战&#xff…...

Qt基于SQLite数据库的增删查改demo

一、效果展示 在Qt创建如图UI界面&#xff0c;主要包括“查询”、“添加”、“删除”、“更新”&#xff0c;四个功能模块。 查询&#xff1a;从数据库中查找所有数据的所有内容&#xff0c;并显示在左边的QListWidget控件上。 添加&#xff1a;在右边的QLineEdit标签上输入需…...

新书推荐:2.2.4 第11练:消息循环

/*------------------------------------------------------------------------ 011 编程达人win32 API每日一练 第11个例子GetMessage.c&#xff1a;消息循环 MSG结构 GetMessage函数 TranslateMessage函数&#xff1a;将虚拟键消息转换为字符消息 DispatchMessage函数…...

MASA:匹配一切、分割一切、跟踪一切

文章目录 摘要1、引言2、相关工作2.1、学习实例级关联2.2、Segment and Track Anything 模型 3、方法3.1、预备知识&#xff1a;SAM3.2、通过分割任何事物来匹配任何事物3.2.1、MASA流程3.2.2、MASA适配器3.2.3、推理 4、实验4.1、实验设置4.2、与最先进技术的比较4.3、消融研究…...

Websocket前端传参:深度解析与实战应用

Websocket前端传参&#xff1a;深度解析与实战应用 在现代Web开发中&#xff0c;Websocket作为一种双向通信协议&#xff0c;已经广泛应用于实时数据传输场景。前端传参作为Websocket通信的重要组成部分&#xff0c;其正确性和高效性直接影响到应用的性能和用户体验。本文将深…...

造假高手——faker

在测试写好的代码时通常需要用到一些测试数据&#xff0c;大量的真实数据有时候很难获取&#xff0c;如果手动制造测试数据又过于繁重无聊&#xff0c;显得不够优雅&#xff0c;今天我们介绍的faker这个轮子可以完美的解决这个问题。faker是一个用于生成各种类型假数据的库&…...

前端工程化工具系列(十二)—— PostCSS(v8.4.38):CSS 转换工具

PostCSS 是转换 CSS 语法的工具。它提供 API 来对 CSS 文件进行分析和修改它的规则。 PostCSS 本身并不能直接使用&#xff0c;主要是使用基于 PostCSS 编写的插件。 1 安装 pnpm add -D postcss-import postcss-nested postcss-preset-env cssnano2 配置 在项目根目录下创…...

Scanpy(3)单细胞数据分析常规流程

单细胞数据分析常规流程 面对高效快速的要求上,使用R分析数据越来越困难,转战Python分析,我们通过scanpy官网去学习如何分析单细胞下游常规分析。 数据3k PBMC来自健康的志愿者,可从10x Genomics免费获得。在linux系统上,可以取消注释并运行以下操作来下载和解压缩数据。…...

【Stable Diffusion】(基础篇二)—— Stable Diffusion图形界面介绍和基本使用流程

本系列笔记主要参考B站nenly同学的视频教程&#xff0c;传送门&#xff1a;B站第一套系统的AI绘画课&#xff01;零基础学会Stable Diffusion&#xff0c;这绝对是你看过的最容易上手的AI绘画教程 | SD WebUI 保姆级攻略_哔哩哔哩_bilibili 在上一篇博客中&#xff0c;我们成功…...

OpenCv之简单的人脸识别项目(动态处理页面)

人脸识别 准备九、动态处理页面1.导入所需的包2.设置窗口2.1定义窗口外观和大小2.2设置窗口背景2.2.1设置背景图片2.2.2创建label控件 3.定义视频处理脚本4.定义相机抓取脚本5.定义关闭窗口的函数6.按钮设计6.1视频处理按钮6.2相机抓取按钮6.3返回按钮 7.定义关键函数8.动态处理…...

呼伦贝尔旅游包车网站咋做/竞价排名服务

一.什么是Pipe?就是管道&#xff0c;简单来说&#xff0c;管道的作用就是传输。并且不同的管道具有不同的作用。(其实就是处理数据)二.pipe用法{{ 输入数据 | 管道 : 管道参数}} (其中‘|’是管道操作符)三.Angular自带的pipe函数 管道功能 DatePipe 日期管道&#xff0c;格…...

百度不收录网站/seo专业培训费用

为什么80%的码农都做不了架构师&#xff1f;>>> Question 345. Reverse Vowels of a String Solution 思路&#xff1a;交换元音&#xff0c;第一次遍历&#xff0c;先把出现元音的索引位置记录下来&#xff0c;第二遍遍历元音的索引并替换。 Java实现&#xff1a;…...

各种类型网站建设/seo优化主要做什么

1. 什么是fastjson? fastjson是阿里巴巴的开源JSON解析库&#xff0c;它可以解析JSON格式的字符串&#xff0c;支持将Java Bean序列化为JSON字符串&#xff0c;也可以从JSON字符串反序列化到JavaBean。 Fastjson使用场景 Fastjson已经被广泛使用在各种场景&#xff0c;包括cac…...

做特卖的网站雅美盛典/企业品牌推广方案

今天小编要跟大家分享的文章是关于Linux运维人员面试必备网络运维面试题汇总。准备参加Linux面试的小伙伴们来和小编一起看一看吧&#xff0c;看看能答对几道题&#xff0c;希望本篇文章能够对大家有所帮助。1、如何查看Linux系统每个ip的连接数?netstat -n | awk ‘/^tcp/ {p…...

广东网站制作哪家强/外贸网站搭建推广

SSM养老院信息管理系统摘 要随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设…...

用dw建设个人网站视频/广东清远今天疫情实时动态防控

1&#xff0e;设有一个n*m方格的棋盘&#xff08;1≤m,n≤100&#xff09;。 求出该棋盘中包含多少个正方形、多少个长方形&#xff08;不包括正方形&#xff09;。 求内切圆就是求正方形。 //xmin(m,n)-1 //长方形里面数正方形的个数计算公式:m*n(m-1)*(n-1).....(m-x)*(n-x…...