Monaco Editor系列(一)启动项目与入门示例解析

前言:作为一名程序员,我们工作中的每一天都在与代码编辑器打交道,相信各位前端程序员对 VS Code 一定都不陌生,VS Code 可以为我们提供代码高亮、代码对比等等功能,让我们在开发的时候,不需要对着暗淡无光的脚本编辑,而是对着五彩缤纷的界面编辑,并且一个右键或者快捷键,编辑器就可以帮我们实现很多的功能。这个系列我讲述的是 VS Code 的 弟弟 – Monaco Editor,它是一款基础代码编辑器,VS Code 内部的代码编辑模块和它是一样一样的,只不过 VS Code 比它增加了文件管理系统,可以通过Node.js操作文件。
另外,Monaco Editor 特别有出息,市面上很多的浏览器端的代码编辑器都有 Monaco Editor 的身影,例如 github的浏览器端代码编辑器
在这里插入图片描述
等等我就不举例啦
为什么要学习 Monaco Editor ?
对于我自己来说,是折服于代码编辑器的丝滑体验和强大功能,好奇内部是怎么实现的,另外就是通过学习这么一个优秀的项目的源码,学习优秀的编码思维和编码技巧。
下面迫不及待的进入 Monaco Editor 的学习吧!
环境说明
🥗 Macbook Apple芯片
🥗 Node16
🥗 编辑器 idea2023 (虽然我讲了 vs code 的弟弟,但我用的是 idea 哈哈哈,主要是平时开发前后端不分离,用习惯了🤣)

一、启动项目

1、下载源码

源码地址:github源码地址
既然我们要学习Monaco Editor的源码,就需要把源码下载下来,而不是使用 npm 安装,那样只会作为依赖出现在项目里,而现如今,Monaco Editor 从依赖变成了研究对象,转正了!
安装方式有两种,一种是在命令行使用 git clone https://github.com/microsoft/monaco-editor.git,安装,但是我的网络不争气,下载不成功,所以只能用笨蛋方法,下载压缩包,我的版本是 0.46.0
在这里插入图片描述

2、使用编辑器打开项目,在项目的根目录下运行 npm i . 安装依赖
3、编译本地项目

package.json 中有编译项目的命令的定义
在这里插入图片描述
运行 npm run build-monaco-editor ,会构建出来一个本地的 monaco-editor。

4、进入本地的 monaco-editor 目录: cd website
5、在当前 /website 目录安装依赖:npm i .
6、然后要编译typescript,根据 package.json 中的命令定义,运行 npm run typedoc

在这里插入图片描述
此处我运行之后报错了
在这里插入图片描述
omg,源码也出错吗?我产生了深深的怀疑,于是就怀疑自己的Node.js的版本不兼容,为此特地下载了 nvm 工具切换了好几个版本的 Node,报错依然如旧,好嘛,还是老老实实分析一下代码。这里的报错就是说有几个变量是undefined,所以取不到它的name属性,typescript 使用对象时提供了安全保护,只需要增加一个 ? ,就可以规避对象不存在的风险,所以这里就是给出错的几个可能不存在的对象后面加一个 ?
在这里插入图片描述
然后在运行 npm run typedoc 就成功了,并且 typedoc 文件夹中多出来了一个 dist 目录,里面存放的应该就是将 typescript 编译之后的结果咯,
在这里插入图片描述

7、启动项目

运行 npm run dev
然而此时又出现了一丢丢的报错,不要慌!定睛一看,是在说 webpack.config.ts 里面的插件的引入方法不对
在这里插入图片描述
改成
import CopyPlugin from "copy-webpack-plugin";
这两个都需要改一下引入方式,改成下面这种,虽然飘红,但是能用
在这里插入图片描述
就可以啦!!
npm run dev 成功!!
本地的 Monaco Editor 就是介个样子的啦
在这里插入图片描述

二、入口文件

1、寻找入口文件

咱们这个项目使用 webpack 构建的,这么一堆代码,该从哪里开始看呢?其实就是从 webpack.config.ts 开始,因为它会告诉我们谁是入口文件!

entry: {
	index: r("src/website/index.tsx"),
	playgroundRunner: r("src/runner/index.ts"),
	monacoLoader: r("src/website/monaco-loader-chunk.ts"),
},

根据 webpack.config.ts 的配置我们可以知道,入口文件就是 src/website/index.tsx。咦,文件路径前面的 r 是什么意思呢?其实是定义的读取文件的方法,这也是我们在实际开发中可以借鉴的一个小技巧

const r = (file: string) => path.resolve(__dirname, file);

关于 chunk 的理解,可以参考文章:webpack 理解 chunk
webpack 的作用就是将我们写的代码模块 module 打包在一起,模块就是我们编写的所有文件。chunk 就是 webpack 在打包的过程中,一些 module 的集合。Webpack 的打包是通过入口文件开始的,入口文件会引用其他模块,其他模块还会引用其他模块,webpack 从入口文件开始,把一系列引用的模块打包为一个 chunk。
当前的入口是对象的形式,每一个属性都会生成一个 chunk,属性名会被当成chunk的名称。
从这里的 entry 我们可以看到,当前的项目是一个多页面应用,每个页面都需要通过 HtmlWebpackPlugin 插件生成页面模版文件,生成页面模版文件的代码在下面的 plugins 配置项里面,这里我们先只关注 index chunk 下的配置

new HtmlWebpackPlugin({
    chunks: ["monacoLoader", "index"],
    templateContent: getHtml(),
    chunksSortMode: "manual",  // 控制排序:手动,就是根据 chunks 数组的顺序依次引入 
}),
new HtmlWebpackPlugin({
    chunks: ["index"],
    filename: "playground.html",
    templateContent: getHtml(),
}),
 new HtmlWebpackPlugin({
    chunks: ["index"],
    filename: "docs.html",
    templateContent: getHtml(),
}),
new HtmlWebpackPlugin({
    chunks: ["index"],
    filename: "monarch.html",
    templateContent: getHtml(),
}),

html-webpack-plugin 插件可以为应用程序生成 HTML 文件,具体的配置项可以查看 插件文档。

  • chunks:这个属性主要在多页面应用的时候才有用哦!webpack 打包的过程中会生成多个 chunk,这些 chunk 其实是 js 文件,使用 chunks 属性就可以指定当前生成的这个 HTML 使用哪一个 js 文件
  • filename:生成的html文件的文件名,默认为 index.html
  • templateContent:模版文件
  • chunksSortMode:是用来控制 HTML 所引用的 chunk 的顺序,可选项:‘none’ | ‘auto’ | ‘manual’ | {Function}。详细的含义就不解释了,这里使用的 manual 就是按照 chunks 数组的顺序引入资源

以第一个 HtmlWebpackPlugin 为例,这个文件没有设置 filename 属性,生成的就是根路由对应的页面,也就是请求 localhost 根页面返回的 html 文件,这个文件需要依赖两个 chunk ,其实就是要依次执行这两个 chunk 对应的入口文件中的代码。
第一个是 monacoLoader chunk,它的入口文件是 src/website/monaco-loader-chunk.ts,为了验证,我们可以在里面打印 111
在这里插入图片描述

第二个是 index chunk ,对应的入口文件是 src/website/index.tsx,为了验证我们也打印 222
在这里插入图片描述

然后我们访问根目录,在控制台中的 network 栏,可以看到,第一个请求就是请求的 localhost 的根页面,页面会通过 script 标签依次引入上述的两个 chunk 生成的 js 文件

在这里插入图片描述

另外,我们看一下控制台输出,果然不出我所料,输出了 111 和 222

在这里插入图片描述

引入的几个 js 文件,就是 webpack 打包之后的结果,可以在 sources 栏看到

在这里插入图片描述

2、查看入口文件

index chunk 的入口文件的内容如下,我稍微做了一点小注释:
src/website/index.tsx

// 引入react
import * as React from "react";
import * as ReactDOM from "react-dom";
// 引入样式文件
import "./bootstrap.scss";
import "./style.scss";
// 引入应用
import { App } from "./pages/App";

// 创建 root 容器
const elem = document.createElement("div");
elem.className = "root";
// 插入到文档中
document.body.append(elem);
// 将应用渲染到 root 中
ReactDOM.render(<App />, elem);
3、应用的定义

接下来我们顺藤摸瓜,看一下 App 应用的定义吧!
src/website/pages/App.tsx

// 引入 Home 组件
import { Home } from "./home/Home";
// 引入 PlaygroundPage 组件
import { PlaygroundPage } from "./playground/PlaygroundPage";
// 引入 路由
import { docs, home, monarch, playground } from "./routes";
// 引入 React
import React = require("react");
// 引入 DocsPage
import { DocsPage } from "./DocsPage";
// 引入 MonarchPage
import { MonarchPage } from "./MonarchPage";

export class App extends React.Component {
	// 根据路由返回指定的组件
	render() {
		if (home.isActive) {
			return <Home />;
		} else if (playground.isActive) {
			return <PlaygroundPage />;
		} else if (docs.isActive) {
			return <DocsPage />;
		} else if (monarch.isActive) {
			return <MonarchPage />;
		}
		return <>Page does not exist</>;
	}
}

App 干的最主要的事情,就是根据路由返回指定的页面。
插一句题外话,我发现我的项目执行 npm run dev 没有自动在浏览器中打开,如此不智能,还需要我动用发财的小手亲自点开链接,不可原谅!我们只需要在 webpack.config.ts 的 devServer 配置项中增加一个 open:true 配置就可以让我们的 Monaco 变聪明啦!另外还可以加上 hot:true,就可以在我们更改代码的时候,自动刷新浏览器中网页的内容。

devServer: {
    open: true,
    hot: true,
    //...
}
4、路由的配置

接下来我们来看一看路由的配置
src/website/pages/routes.ts

// 定义路由类
export class Route {
	// Route 接收 href 为参数
	constructor(public readonly href: string) {}

	// 路由实例上的属性
	// get 语法 访问对象的isActive属性时,得到的是 isActive() 方法的返回值
	// isActive 通过url路径判断当前访问的url是对应哪一个路由
	get isActive(): boolean {
		const target = new URL(this.href, window.location.href);
		return (
			trimEnd(target.pathname, ".html") ===
			trimEnd(window.location.pathname, ".html")
		);
	}
}

function trimEnd(str: string, end: string): string {
	if (str.endsWith(end)) {
		return str.substring(0, str.length - end.length);
	}
	return str;
}

// 通过new创建路由实例
export const home = new Route("./");
export const playground = new Route("./playground.html");
export const docs = new Route("./docs.html");
export const monarch = new Route("./monarch.html");

从路由配置和 App 的配置中可以看出,根路径对应的就是 Home 组件,/playground.html 路径对应的就是 Playground 组件,然后我们现在访问一下 /playground.html 路由,记得加后面的 .html 哦。就会展示代码编辑页面

在这里插入图片描述

三、Hello World案例

进入 Playground 组件,映入眼帘的就是 Monaco 给我们提供的 Hello World 案例,左侧是源码,右侧是展示效果。

1、左侧源码

怎么找代码的位置呢?咱们直接复制代码,全局搜索,这个 hello-world 文件夹就是
在这里插入图片描述

左侧区域的三个编辑区域就分别是该文件夹中的 js、html、css 文件的内容
在这里插入图片描述
源码定义的部分找到了,但是它是怎么来到编辑框里的呢?

其实找源码这个过程可以有两条路,一条是通过 html 中的类名,找模版中的定义,一条是通过 /hello-world 这个文件夹的路径找,因为肯定有地方通过 import 引入了这几个文件。 实不相瞒,第一条路我走过了,有点曲折,所以直接说第二条路。

通过全局搜索 /hello world 可以找到website/src/website/data/playground-samples/all.js 中引入了所有示例文件定义的数据,并且将这些文件和对应的内容定义为json文件,放到了变量 PLAY_SAMPLES 中。
那么此时我们再全局搜索一下 all.js 路径,看看哪个地方引入了这个文件,好吧,并没有找到。不要心灰,不要失望,这是全局搜索一下它的父文件夹 playground-samples,就会发现一个可疑的目标website/src/website/pages/playground/playgroundExamples.tsx

在这个文件里,调用了 require.context() 方法,这个方法是 Webpack 的api,用来检索目录内容,

// require.context:
// 参数一:表示检索的目录
// 参数二:表示是否检索子文件夹
// 参数三:匹配文件的正则表达式

const descriptions = require.context<{ title: string; sortingKey?: number }>(
	"../../data/playground-samples",
	true,
	/json$/
);

这是把所有的 json 文件都检索了一遍,我们在控制台看一下输出,发现返回值是一个方法
在这里插入图片描述
返回值可以通过 .keys().forEach() 的方式遍历文件夹中的所有文件

descriptions.keys().forEach(item=>{
	console.log(item)
})

打印一下发现是文件夹中所有 json 文件的路径
在这里插入图片描述

website/src/website/pages/playground/playgroundExamples.tsx 文件最后导出了一个方法 getPlaygroundExamples() ,这个方法最后有一个返回值 result 。中间的具体过程先不看,先打印一下这个 result,是一个下图所示,保存了所有的示例数据的数组
在这里插入图片描述
虽然这里面并没有 html 、js 、 css 文件内容 🥹,但是有一个 load() 方法
website/src/website/pages/playground/playgroundExamples.tsx 中有定义

async load() {
	const [css, js, html] = await Promise.all([
		files(path + "/sample.css"),
		files(path + "/sample.js"),
		files(path + "/sample.html"),
	]);
	return {
		css: css.default,
		html: html.default,
		js: js.default,
	};
},

所以执行 load() 方法就可以获取对应的 html 、css 、js 内容

那么一定有一个地方引用了这个方法,我们全局搜索一下,发现是在website/src/website/pages/playground/PlaygroundPageContent.tsx 里面

 <Select<PlaygroundExample>
  values={getPlaygroundExamples().map(
        (e) => ({
            groupTitle:
            e.chapterTitle,
            items: e.examples,
        })
    )}
    value={ref(
        model,
        "selectedExample"
    )}
    getLabel={(i) =>
        i.title
    }
/>

看来返回值是切换示例的下拉框的数据
在这里插入图片描述
大胆猜测一下, 切换下拉框选项的时候,就会执行对应对象的 load() 方法,加载对应的 html、css、js 的内容,展示在页面上

如下代码控制了 value 的绑定,value 绑定了 model 对象上的 selectedExample,当 value 变化的时候,就会触发 selectedExampleset 方法设置它的值

value={ref(
        model,
        "selectedExample"
    )}

浅浅看一下 ref 的定义

export function ref<T, TProp extends keyof T>(
	obj: T,
	prop: TProp
): IReference<T[TProp]> {
	return {
		get: () => obj[prop],
		set: (value) => (obj[prop] = value),
	};
}

然后我们全局搜一下 selectedExample 对象的定义
website/src/website/pages/playground/PlaygroundModel.ts 方法里面找到了这个对象的 set 方法,即 value 变化的时候会执行的方法

public set selectedExample(value: PlaygroundExample | undefined) {
	this._selectedExample = value;
	this.selectedExampleProject = undefined;
	if (value) {
		value.load().then((p) => {
			runInAction("update example", () => {
				this.selectedExampleProject = {
					example: value,
					project: p,
				};
				this.reloadKey++;
				this.setState(p);
			});
		});
	}
}

这里终于看到了 load() 方法的执行,可以输出一下 p,终于看到这几个内容了
在这里插入图片描述
setState() 方法就是赋值操作

public setState(state: IPlaygroundProject) {
	this.html = state.html;
	this.js = state.js;
	this.css = state.css;
}

当前是 PlaygroundModel 类, PlaygroundPageContent 类里面,在创建的时候,传递了 props 参数

export class PlaygroundPageContent extends React.Component<
    { model: PlaygroundModel },
    {}
> {}

PlaygroundPageContent 类的内部,可以通过 this.props.model 访问 PlaygroundModel
html、css、js 文件的编辑区域是自定义组件 Editor

 <Editor
    language={"javascript"}
    value={ref(model, "js")}
/>

model.js 的值赋给 Editor 类中的 value 属性
进入到 Editor 类的内部
website/src/website/pages/playground/PlaygroundPageContent.tsx

private readonly model = getLoadedMonaco().editor.createModel(
    this.props.value.get(),
    this.props.language
);
render() {
    return (
        <MonacoEditor
            model={this.model}
            onEditorLoaded={(editor) => this.initializeEditor(editor)}
            height={this.props.height}
            className="editor-container"
        />
    );
}

最后返回的是 MonacoEditor 自定义标签,我们在往这个类里瞅一瞅 MonacoEditor
注意这个文件里面定义了好几个类,别找错了哟
website/src/website/components/monaco/MonacoEditor.tsx

private readonly divRef = React.createRef<HTMLDivElement>();
render() {
	const height = "100%";
	return (
		<div
			style={{
				height,
				minHeight: 0,
				minWidth: 0,
			}}
			className="monaco-editor-react"
			ref={this.divRef}
		/>
	);
}

上面代码使用 ref 将变量 divRef 和元素 div 绑定在一起,在 componentDidMount 生命周期钩子函数中,有对这个数据的操作,即以当前的div为容器创建 Monaco Editor 编辑器实例

componentDidMount() {
	// 获取 dom 元素
	const div = this.divRef.current;
	if (!div) {
		throw new Error("unexpected");
	}
	this.resizeObserver.observe(div);
	this.editor = getLoadedMonaco().editor.create(div, {
		model: this.props.model,
		scrollBeyondLastLine: false,
		minimap: { enabled: false },
		automaticLayout: false,
		theme: this.props.theme,
		readOnly: this.props.readOnly,
	});
	this.editor.onDidContentSizeChange((e) => {
		this.setState({ contentHeight: e.contentHeight });
	});
	if (this.props.onEditorLoaded) {
		this.props.onEditorLoaded(this.editor);
	}
}

通过在浏览器中打断点,我发现渲染 html、css、js 的步骤是在 this.props.onEditorLoaded() 方法中实现的,这个方法是通过属性传递过来的,上面的代码里也有,可以全局搜一下

<MonacoEditor
    model={this.model}
    onEditorLoaded={(editor) => this.initializeEditor(editor)}
    height={this.props.height}
    className="editor-container"
/>

那么此时就是要执行 this.initializeEditor(editor) 方法,进行编辑器中内容的初始化。

 initializeEditor(editor: monaco.editor.IStandaloneCodeEditor) {
    this.editor = editor;
    this.disposables.push(this.editor);
    this.disposables.push(
        this.editor.onDidChangeModelContent((e) => {
            this.ignoreChange = true;
            try {
                this.props.value.set(this.editor!.getValue());
            } finally {
                this.ignoreChange = false;
            }
        })
    );
}

最主要的一句代码就是 this.props.value.set(this.editor!.getValue()); ,例如在这里写一个测试数据,页面上就会渲染测试数据:
this.props.value.set(this.editor.getValue());
在这里插入图片描述
至于 value 又是怎么渲染的,原谅此时的我实在是找不到啦!那么就往下继续看吧,不要让暂时的困难困住我们前进的脚步

2、右侧展示效果

右侧的效果就是左侧代码运行之后的结果,我们来验证一下,比如说在 html 里面,增加 Hello 文本,它就会直接出现在右侧

在这里插入图片描述
ok,到这里我发现不学react已经进行不下去了,所以我去react官网逛了一圈,React 官网提供了线上运行的案例,使用的是 codesandbox 线上代码编辑器,然而我发现这个网站用的也是我们的 Monaco !怪不得体验感这么好。不得不说 React 官网的教程真的清晰易懂,生动形象!但是翻译泰拉垮啦🙀 不如看英文
在这里插入图片描述
一万年以后~~~~ React 入门案例学成归来,继续学习 Monaco
接下来我们还是,首先要找到代码在哪里呀,通过检查模式可以看出,右侧部分是一个 iframe,并且其父级类名是 preview

在这里插入图片描述

就可以使用类名full-iframe进行全局搜索,父级类名还得是 preview,发现了它的踪迹就在 /src/website/pages/playground/Preview.tsx 文件里

<iframe
	className="full-iframe"
	key={this.counter}
	sandbox="allow-scripts allow-modals"
	frameBorder={0}
	ref={this.handleIframe}
	src="./playgroundRunner.html"
/>

这个 iframe 的 src 的指向,就是它的具体内容。但是很奇怪,根据指定的目录,找不到 ./playgroundRunner.html 文件,再用这个文件名全局搜索一下子,发现奥秘原来在 webpack.config.ts 里面

module.exports = {
	entry: {
        index: r("src/website/index.tsx"),
        playgroundRunner: r("src/runner/index.ts"),
        monacoLoader: r("src/website/monaco-loader-chunk.ts"),
    },
    plugins: [
    	 new HtmlWebpackPlugin({
            chunks: ["playgroundRunner"],
            filename: "playgroundRunner.html",
            templateContent: getHtml(),
        }),
    ]
}

介个 html 文件的内容,首先是由 templateContent 属性指定的模版确定了一部分。那么就让我们来揭开,这个属性对应的值 getHtml() 方法的真面目吧!其实非常非常的简单,就在 webpack.config.ts 的下面定义的,就是返回了一个基础的 Document 文档结构

function getHtml(): string {
    return `
<!DOCTYPE html>
<html>
	<head>
	<meta charset="utf-8">
	<title>Monaco Editor</title>
	</head>
	<body>
	</body>
</html>`;
}

这个模版只是给出了html内容的模版,具体的内容还是要看 playgroundRunner chunk 的入口文件执行的代码是怎么样的。它的入口文件就是src/runner/index.ts,里面的内容不得不说,还挺复杂。另外,要注意当前 chunk 生成的 html 文件是通过 iframe 的方式使用的哦!
这个入口文件做的事情,大致来说,就是给 iframe 增加了一个事件监听,用来监听父级 window 发送过来的数据,如果父级 window 发布了要该 iframe 初始化的命令,它就会乖乖的执行初始化的方法
父级传递给这个 iframe 的东东,其实就是左侧编辑区域的代码,咱们可以在初始化的方法里面打印一下这个方法接收到的参数
在这里插入图片描述
在这里插入图片描述
在初始化方法里面,会把参数中的 css、html 都加到当前 iframe 里面,并且会通过 eval() 方法,执行 js 里面的代码,执行完 js 里面的代码,右侧就会创建出来一个 Monaco Editor 编辑器实例啦,并且里面的内容是我们可以通过左侧编辑器修改的。

四、总结

本篇从 Monaco Editor 下载、启动开始,顺着入口文件以及示例 Hello World 项目的脉络,学习了源码的执行过程。学习这种大型项目的源码一定要有耐心,另外就是在学习的过程中需要查漏补缺,遇到不会的东西补一补,慢慢的积累。

参考文章:
1、https://juejin.cn/post/6844903889393680392
2、html-webpack-plugin
3、require.context()的用法详解

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis模拟小例子

我们模拟游戏中的一个角色&#xff0c;这个角色被动技能就是受到攻击的时候&#xff0c;会有十分之三的概率爆出金币&#xff0c;而在一个回合之中&#xff0c;爆出的金币个数有限制&#xff0c;限制为两个&#xff0c;假设攻击是按照一定的频率进行的&#xff0c;而一个回合的…

海外云手机如何帮助亚马逊引流?

随着全球化的推进&#xff0c;出海企业和B2B外贸企业越来越注重海外市场的开拓&#xff0c;这已成为企业争夺市场份额的重要策略。本文将重点探讨海外云手机在优化亚马逊店铺引流方面的作用和优势。 海外云手机是一种在云端运行的虚拟手机&#xff0c;能够在单一芯片上多开几个…

20---复位电路设计

视频链接 复位电路设计01_哔哩哔哩_bilibili 复位电路设计 1、复位介绍 复位电路又叫初始化电路&#xff0c;它的作用是将芯片的工作状态回到初始状态&#xff01; 复位电路在硬件设计中至关重要&#xff0c;在实际调试的过程中&#xff0c;与复位相关的点必核查&#xff…

极路由4获取不到local_token和uuid的解决方案

今天淘了个二手极路由4(HC5962)&#xff0c;想刷个Openwrt系统来着&#xff0c;就按着网上的教程来进行。 打开极路由ROOT local-ssh利用工具 (hiwifi.wtf)这个网站&#xff0c;然后第一步获取local_token就出问题了&#xff0c;显示的字是"找不到文件..."&#xff…

Zookeeper(五)Zokeeper 环境搭建与Curator使用

目录 一 环境搭建1.1 单机环境搭建1.2 可视化工具ZooKeeper Assistant1.3 集群环境搭建 二 常用命令1.1 命令行语法1.2 数据节点信息1.3 节点类型 三 CuratorAPI使用3.1 依赖3.1 创建会话3.2 基本使用增删改查3.3 ACL权限控制3.4 分布式锁3.5 分布式计数器3.6 分布式Barrier3.7…

【python】2.pycharm中请选择有效的python解释器

欢迎来CILMY23的博客喔&#xff0c;本篇为【python】2.pycharm中请选择有效的python解释器&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 在上一篇博客中&#xff0c;我们已经在电脑上安装了python3.12.2和pycharm&#xff0c;本期…

python社区垃圾分类管理平台的设计与实现flask-django-php-nodejs

近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;社区垃圾分类管理平台利用计算机网络实现信息化管理&#xff0c;使整个社区垃圾分类管理的发展和服务水平有显著提升。 语言&#xf…

spark RDD 创建及相关算子

RDD编程入口 RDD编程入口对象是SparkContext对象&#xff0c;想要调用相关的计算api都需要通过构造出的sparkcontext对象调用 RDD的创建 通过并行化集合创建RDD&#xff08;本地集合转为分布式&#xff09;&#xff0c;api如下 rdd sc.parrallize(param1, param2)参数1是本…

设计模式之简单工厂模式详解

简单工厂模式 工厂模式&#xff1a;工厂方法模式&#xff1b; 低阶&#xff1a;简单工厂模式&#xff1b; 高阶&#xff1a;抽象工厂模式&#xff1b; 1&#xff09;概述 定义一个工厂类&#xff0c;根据参数的不同返回不同类的实例&#xff0c;被创建的实例通常都具有共同…

分布式游戏服务器

1、概念介绍 分布式游戏服务器是一种专门为在线游戏设计的大型系统架构。这种架构通过将游戏服务器分散部署到多台计算机&#xff08;节点&#xff09;上&#xff0c;实现了数据的分散存储和计算任务的并行处理。每个节点都负责处理一部分游戏逻辑和玩家请求&#xff0c;通过高…

TinTin Web3 Bounty 挑战杯开启,Sui 向你发出挑战邀请

以下文章来源于TinTinLand &#xff0c;作者TinTinLand。 2024 年开年最火的是什么&#xff1f; 对 Web3 来说&#xff0c;Bounty 任务应该是普通人获得行业“一杯羹”的重要捷径&#xff01; 通过深入学习各类 Web3 技术&#xff0c;凭借实战锻炼开发创新项目&#xff0c;就…

【Linux】传输层协议:TCP/UDP

目录 netstat pidof UDP协议 TCP协议 TCP协议段格式 TCP协议的相关机制 确认应答&#xff08;ACK&#xff09;机制 超时重传机制 连接管理机制 服务端状态转换 客户端状态转化 流量控制 流量控制常见问题&#xff1a; 滑动窗口 拥塞控制 延迟应答 面向字节流…

uniapp ios证书失效

前面是按照网上查找的方法 作者大大的地址 1、一个ios账户&#xff08;688付费版&#xff09; 2、登录 Apple Developer 3、创建Identifiers ps&#xff1a;创建时需继承苹果的sdk&#xff0c;只需要一个就行 点击continue再点击Register即可 4、创建.cer证书 &…

OpenAI CEO透露GPT-4表现“有点糟糕”;通义听悟音视频问答登场;Adobe整合AI功能助力3D设计创作

&#x1f989; AI新闻 &#x1f680; OpenAI CEO透露GPT-4表现“有点糟糕” 摘要&#xff1a;OpenAI的首席执行官Sam Altman在与Lex Fridman的访谈中表示&#xff0c;GPT-4的表现并不令人满意&#xff0c;认为其“有点糟糕”&#xff0c;同时对即将到来的GPT-5寄予厚望。Altm…

联想笔记本的声音键没有反应怎么办?

如果我的联想笔记本电脑上的声音按钮没有响应&#xff0c;该怎么办&#xff1f; 如果我的联想笔记本电脑上的声音按钮没有响应&#xff0c;该怎么办&#xff1f; 按下按钮后我无法控制声音。 我该怎么办&#xff1f; 以下是我为您整理的关于联想笔记本声音按键无反应的相关资料…

Power BI学习(数据可视化)

另一个也可以的工具是&#xff1a;Tableau 还有一个是&#xff1a;神策&#xff0c;主要是用于互联网的app的数据埋点 数据分析的过程&#xff1a; 数据源--数据清洗&#xff08;power query&#xff09;-构建指标 新建度量值&#xff08;power pivot&#xff09;-可视化&…

【重温设计模式】策略模式及其Java示例

策略模式的基本概念 策略模式&#xff0c;是一种常见的行为设计模式&#xff0c;主要用于处理程序中的一些相同行为&#xff0c;但具有不同实现方式的问题。在策略模式中&#xff0c;我们将每一种行为封装为一个个策略类&#xff0c;通过策略类的组合和切换&#xff0c;可以灵…

数据仓库的魅力及其在企业中的应用实践

数据仓库&#xff0c;这一创新性的概念来自于比尔恩门&#xff0c;从1980年代末提出以来&#xff0c;便凭借其独特的架构设计和强大的数据处理能力&#xff0c;在全球商业领域中掀起了一场革命。它不仅是解决企业海量数据存储和查询需求的关键技术&#xff0c;更是推动企业实现…

MYSQL数据库管理基本操作

一、数据库的基本操作 1、登录数据库 [rootmysql-server ~]#mysql -uroot -p123456 ###直接回车&#xff0c;则进入数据库[rootmysql-server ~]#mysql -u root -p ###直接回车 Enter password: ###输入密码 方法一&#xff1a…

第3关:创建零件表P,并插入数据

任务描述 零件表P由零件代码&#xff08;PNO&#xff09;、零件名(PNAME)、颜色(COLOR)、重量(WEIGHT)组成。创建零件表P(PNO,PNAME,COLOR,WEIGHT)&#xff0c;并在P表中插入下图数据。 相关知识 1、MySQL创建表的基本语法如下&#xff1a; 其中&#xff0c;table_name 是要创…