Golang之OpenGL(一)
使用OpenGL实现窗口中绘制三角形(纯色|彩色)、正方形(变色)
- 一、简单实现窗口绘制三角形
- 二、绘制的多颜色三角形(基于 ‘ 简单实现窗口绘制三角形 ’ )
- 1、在顶点着色器和片段着色器中添加了颜色的输入和输出处理。
- 2、makeVao 函数中启用了顶点位置和颜色的属性,并分别设置了它们的属性指针。
- 3、draw 函数中修改了绘制的顶点数量计算,因为现在每个顶点包含位置和颜色共 6 个值。
- 三、绘制不停变换颜色三角形/正方形(基于 ‘ 简单实现窗口绘制三角形 ’ )
- 1、如果需要展示三角形那么 triangle 就不动,如若正方形就需要自己调整对应的xyz坐标
- 2、片段着色器中使用时间来计算颜色的变化。
- 3、在draw函数中,获取当前时间并传递给片段着色器的uniform变量。同时,确保在初始化OpenGL时启用垂直同步,以控制渲染速率。
本文只是跟随此 链接 文章简单实现并了解OpenGL,如若你有兴趣可去实现本文衍生的【生命游戏】
在 OpenGL 中,大多数复杂的形状最终都是通过三角形来绘制和构建的,这是因为三角形是最简单且不可再分的多边形,具有良好的稳定性和通用性, 几乎所有的图形都可以通过组合多个三角形来近似表示。
/
例如矩形可以用两个三角形组成,圆形可以通过多个小三角形来逼近。
/
OpenGL 也支持其他基本图元,如点(GL_POINTS)、线(GL_LINES、GL_LINE_STRIP、GL_LINE_LOOP)等。但对于构建复杂的形状,三角形仍然是最常用和基础的元素。所以,虽然不是所有形状都严格地只能用三角形绘制,但在实际应用中,三角形是非常重要和常用的基础构建块。
一、简单实现窗口绘制三角形
// @Author cory 2024/8/1 15:59:00
package openGLimport ("fmt""github.com/go-gl/gl/v4.1-core/gl""github.com/go-gl/glfw/v3.2/glfw""log""runtime""strings"
)const (//定义画布参数width = 600height = 600
)// @Title OneMain 2024/8/1 16:05:00
// @Description 主循环
// @Auth Cory
func OneMain() {//1、运行时包指示给 LockOSThread(),这确保我们将始终在同一操作系统线程中执行,这对于 GLFW 很重要,因为 GLFW 必须始终从初始化它的同一线程中调用runtime.LockOSThread()//2、调用 initGlfw 来获取窗口引用,并延迟终止。然后在for循环中使用窗口引用,在for循环中,我们说只要窗口应该保持打开状态,就做一些事情。window := initGlfw()defer glfw.Terminate()program := initOpenGL() //初始化OpenGL并返回一个初始化的程序。vao := makeVao(triangle) //根据定义的三角形来返回顶点数组对象的指针//3、在当前循环中可以做很多事情for !window.ShouldClose() {// TODO// 检查链接状态var status int32gl.GetProgramiv(program, gl.LINK_STATUS, &status)if status == gl.FALSE {var logLength int32gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &logLength)log := strings.Repeat("\x00", int(logLength+1))gl.GetProgramInfoLog(program, logLength, nil, gl.Str(log))fmt.Println("链接程序错误:", log)continue}draw(vao, window, program)}
}// @Title initGlfw 2024/8/1 16:04:00
// @Description 创建窗口
// @Auth Cory
// @Return error ---> "返回窗口"
func initGlfw() *glfw.Window {//1、初始化 GLFW 包err := glfw.Init()if err != nil {panic(err)}//2、定义一些全局 GLFW 属性glfw.WindowHint(glfw.Resizable, glfw.False) //指定用户是否可以调整窗口的大小。glfw.WindowHint(glfw.ContextVersionMajor, 4) //指定创建的上下文必须与之兼容的客户端API版本。OR 2glfw.WindowHint(glfw.ContextVersionMinor, 1) //指定创建的上下文必须与之兼容的客户端API版本。glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) //指定要为哪个OpenGL配置文件创建上下文。硬约束。glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) //指定OpenGL上下文是否应该是向前兼容的。硬约束。//3、创建一个 glfw (就是我们要进行未来绘画的地方,只需告诉它我们想要的宽度和高度,以及标题,然后调用 window.MakeContextCurrent,将窗口绑定到我们当前的线程。最后返回窗口)window, err := glfw.CreateWindow(width, height, "铁憨憨_自学GL", nil, nil)if err != nil {panic(err)}window.MakeContextCurrent()return window
}// compileShader 此函数的用途是以字符串及其类型的形式接收着色器源代码,并返回指向生成的编译着色器的指针。如果它编译失败,我们将返回一个包含详细信息的错误。
func compileShader(source string, shaderType uint32) (uint32, error) {shader := gl.CreateShader(shaderType)csources, free := gl.Strs(source)gl.ShaderSource(shader, 1, csources, nil)free()gl.CompileShader(shader)var status int32gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)if status == gl.FALSE {var logLength int32gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)log := strings.Repeat("\x00", int(logLength+1))gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))return 0, fmt.Errorf("failed to compile %v: %v", source, log)}return shader, nil
}/*
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
*/const (vertexShaderSource = `#version 410in vec3 vp;void main() {gl_Position = vec4(vp, 1.0);}` + "\x00" // GLSL 源代码 顶点着色器fragmentShaderSource = `#version 410out vec4 frag_colour;void main() {frag_colour = vec4(241.0/255.0, 148.0/255.0, 138.0/255.0, 1.0);}` + "\x00" // GLSL 源代码 片段着色器
)// 三角形参数
var (triangle = []float32{0, 0, 0, // top-1, 0, 0, // left0, -1, 0, // right}
)// initOpenGL 初始化OpenGL并返回一个初始化的程序。
func initOpenGL() uint32 {// 1、初始化 OpenGL 库if err := gl.Init(); err != nil {panic(err)}// 2、获取 OpenGL 版本信息并打印日志version := gl.GoStr(gl.GetString(gl.VERSION))log.Println("OpenGL version", version)// 3、编译顶点着色器vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)if err != nil {panic(err)}// 4、编译片段着色器fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)if err != nil {panic(err)}// 5、创建一个 OpenGL 程序对象prog := gl.CreateProgram()// 将顶点着色器和片段着色器附加到程序对象上gl.AttachShader(prog, vertexShader)gl.AttachShader(prog, fragmentShader)// 链接程序对象gl.LinkProgram(prog)// 6、返回链接后的程序对象的标识符return prog
}// makeVao 从提供的点初始化并返回顶点数组。
// 解释如下:
// 1、makeVao 函数的主要目的是创建和配置一个用于存储顶点数据的顶点数组对象(Vertex Array Object,简称 VAO)
// 2、points 数组包含了定义图形(例如三角形)的顶点坐标数据。通过一系列的 OpenGL 函数调用,将这些顶点数据存储在缓冲区对象(VBO)中,并将 VBO 与 VAO 进行关联和配置。
// 3、此处的uint32实际上是一个无符号的标识符,用来指向已经配置好的顶点数组对象,在后续的渲染代码中,可以通过 gl.BindVertexArray(vao) 来绑定这个创建好的 VAO,然后执行绘制操作
func makeVao(points []float32) uint32 {var vbo uint32gl.GenBuffers(1, &vbo)gl.BindBuffer(gl.ARRAY_BUFFER, vbo)gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)var vao uint32gl.GenVertexArrays(1, &vao)gl.BindVertexArray(vao)gl.EnableVertexAttribArray(0)gl.BindBuffer(gl.ARRAY_BUFFER, vbo)gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)return vao
}func draw(vao uint32, window *glfw.Window, program uint32) {// 1、清除颜色缓冲区和深度缓冲区,为新的绘制操作准备干净的画布gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)// 2、使用指定的程序进行后续的绘制操作gl.UseProgram(program)// 3、绑定之前创建的顶点数组对象(VAO)gl.BindVertexArray(vao)// 4、使用 `gl.DrawArrays` 函数进行绘制// `gl.TRIANGLES` 表示绘制三角形// `0` 表示从顶点数组的起始位置开始绘制// `int32(len(triangle)/3)` 表示要绘制的顶点数量,这里假设 `triangle` 存储了顶点数据,并且每 3 个顶点构成一个三角形gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/3))// 5、让 GLFW 检查是否有鼠标或键盘等事件glfw.PollEvents()// 6、交换前后缓冲区,将之前在后台缓冲区绘制的内容显示到屏幕上window.SwapBuffers()
}
二、绘制的多颜色三角形(基于 ‘ 简单实现窗口绘制三角形 ’ )
1、在顶点着色器和片段着色器中添加了颜色的输入和输出处理。
const (vertexShaderSource = `#version 410in vec3 vp;in vec3 color; out vec3 fragColor; void main() {gl_Position = vec4(vp, 1.0);fragColor = color; }` + "\x00" // GLSL 源代码 顶点着色器fragmentShaderSource = `#version 410in vec3 fragColor; out vec4 outColor;void main() {outColor = vec4(fragColor, 1.0); }` + "\x00" // GLSL 源代码 片段着色器
)// 三角形参数,包含位置和颜色(前三xyz,后三rgb)
var (triangle = []float32{0, 0, 0, 1.0, 0.0, 0.0, // 红色顶点-1, 0, 0, 0.0, 1.0, 0.0, // 绿色顶点0, -1, 0, 0.0, 0.0, 1.0, // 蓝色顶点}
)
2、makeVao 函数中启用了顶点位置和颜色的属性,并分别设置了它们的属性指针。
gl.EnableVertexAttribArray(0) 修改为:
// 启用顶点位置和颜色属性gl.EnableVertexAttribArray(0)gl.EnableVertexAttribArray(1)
此处为什么要两个EnableVertexAttribArray,为什么不能一步到位,直接要设置1的哪个?
因为在这个场景中,不能只使用 gl.EnableVertexAttribArray(1) 而省略 gl.EnableVertexAttribArray(0)
原因如下:
在您的顶点着色器中,定义了两个输入属性:顶点位置 vp 和顶点颜色 color 。
gl.EnableVertexAttribArray 用于启用顶点属性数组,以便在渲染时能够使用它们。
gl.EnableVertexAttribArray(0) 启用的是 顶点位置 属性,
gl.EnableVertexAttribArray(1) 启用的是 顶点颜色 属性。
如果只启用 1 而不启用 0 ,那么顶点位置属性将不会被激活,导致在渲染时无法正确获取和使用顶点的位置信息,从而可能导致图形无法正确显示。每个顶点属性都需要单独启用,以确保 OpenGL 知道在渲染时应该从相应的缓冲区中获取和使用这些数据。
然后在 gl.VertexAttribPointer 后面追加
// 为顶点颜色设置属性指针gl.VertexAttribPointer(1, 3, gl.FLOAT, false, 6*4, gl.PtrOffset(3*4))
3、draw 函数中修改了绘制的顶点数量计算,因为现在每个顶点包含位置和颜色共 6 个值。
gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/3)) 修改为:
// `int32(len(triangle)/6)` 表示要绘制的顶点数量,每个顶点包含位置和颜色gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/6))
三、绘制不停变换颜色三角形/正方形(基于 ‘ 简单实现窗口绘制三角形 ’ )
1、如果需要展示三角形那么 triangle 就不动,如若正方形就需要自己调整对应的xyz坐标
切片包含 9 个值,三角形的每个顶点对应 3 个值。顶线 0、0.5、0 是表示为 X、Y 和 Z 坐标的顶点,第二条线是左顶点,第三条线是右顶点。这三对中的每一对都表示顶点相对于窗口中心的 X、Y 和 Z 坐标,介于 -1 和 1 之间。因此,最高点的 X 为零,因为它的 X 位于窗口的中心,Y 为 0.5 表示它将相对于窗口中心向上移动四分之一(因为范围为 -1 比 1),Z 为零。
–
正方形为两个三角形组合
triangle = []float32{0, 0, 0, // top-1, 0, 0, // left0, -1, 0, // right-1, 0, 0, // top-1, -1, 0, // left0, -1, 0, // right}
2、片段着色器中使用时间来计算颜色的变化。
//使用时间信息来动态计算颜色。这里使用正弦和余弦函数来实现周期性的颜色变化效果。fragmentShaderSource = `#version 410out vec4 frag_colour;uniform float time;void main() {// 根据时间计算颜色float r = sin(time) * 0.5 + 0.5;float g = cos(time) * 0.5 + 0.5;float b = 0.5;frag_colour = vec4(r, g, b, 1.0);}` + "\x00"
3、在draw函数中,获取当前时间并传递给片段着色器的uniform变量。同时,确保在初始化OpenGL时启用垂直同步,以控制渲染速率。
// 获取当前时间(秒为单位)time := float32(glfw.GetTime())// 获取uniform变量的位置timeUniform := gl.GetUniformLocation(program, gl.Str("time\x00"))// 将时间值传递给片段着色器的uniform变量gl.Uniform1f(timeUniform, time)
或者自己来调整颜色变换的速度
此时 fragmentShaderSource 就不能是常量了,因为多了一个speed来控制速度
fragmentShaderSource = `#version 410out vec4 frag_colour;uniform float time;uniform float speed;void main() {// 根据时间和速度计算颜色float r = sin(time * speed) * 0.5 + 0.5;float g = cos(time * speed) * 0.5 + 0.5;float b = 0.5;frag_colour = vec4(r, g, b, 1.0);}` + "\x00" // GLSL 源代码 片段着色器
// 获取当前时间(秒为单位)time := float32(glfw.GetTime())// 获取uniform变量的位置timeUniform := gl.GetUniformLocation(program, gl.Str("time\x00"))// 获取 speed uniform 变量的位置speedUniform := gl.GetUniformLocation(program, gl.Str("speed\x00"))// 将时间值传递给片段着色器的uniform变量gl.Uniform1f(timeUniform, time)// 设置 speed 的值 (数越大变换的速度越快)gl.Uniform1f(speedUniform, 2.0)
相关文章:

Golang之OpenGL(一)
使用OpenGL实现窗口中绘制三角形(纯色|彩色)、正方形(变色) 一、简单实现窗口绘制三角形二、绘制的多颜色三角形(基于 ‘ 简单实现窗口绘制三角形 ’ )1、在顶点着色器和片段着色器中添加了颜色的输入和输出…...

122. Go反射中与结构体相关的常用方法与应用
文章目录 encoding/jsonreflect 简介reflect.Value 常用方法reflect.Type 常用方法 应用一:使用 reflect 实现 encoding/json序列化反序列化 应用二:使用Tag实现字段级别的访问控制tag 行为自定义案例:结构体字段访问控制 总结 在使用 Go 语言…...

Java入门、进阶、强化、扩展、知识体系完善等知识点学习、性能优化、源码分析专栏分享
场景 作为一名Java开发者,势必经历过从入门到自学、从基础到进阶、从学习到强化的过程。 当经历过几年企业级开发的磨炼,再回头看之前的开发过程、成长阶段发现确实是走了好多的弯路。 作为一名终身学习的信奉者,秉承Java体系需持续学习、…...

Spring-bean销毁
bean销毁(找到销毁的bean) 在bean的声明周期中,存在一个记录bean销毁方法的阶段,以备于spring关闭的时候可以执行bean的销毁方法(单例bean) v1.0 registerDisposableBeanIfNecessary protected void registerDisposableBeanIfNec…...

【4】BlazorUI库
【4】BlazorUI库 一、Blazorise二、Ant Design Blazor三、Radzen Blazo四、Radzen Blazo 一、Blazorise Blazorise Blazorise 是一个广泛使用的 UI 框架,提供了丰富的组件库和多个主题支持,如 Bootstrap、Bulma、Material 和 AntDesign。 二、Ant Desig…...

树与二叉树【下】
目录 三. 哈夫曼树3.1 带权路径长度3.2 哈夫曼树的定义3.3 哈夫曼树的构造3.4 哈夫曼编码(经常考察) 四. 并查集4.1 如何表示“集合”关系?4.2 “并查集”的代码实现4.3 “并查集”的优化4.4 “并查集”的进一步优化 \quad 三. 哈夫曼树 \qua…...

ElementPlus 中el-select自定义指令实现触底加载请求options数据
1) 背景: 老项目翻新时,发现一个下拉框数据非常多,客户呢,希望全部数据一起展示,意思就是全部数据一起返回给前端用于展示。但这会造成明显的卡顿。~~明显的不合理! QAQ!~~ 于是压力给到前端,查询资料,各种…...

基于Selenium实现操作网页及操作windows桌面应用
Selenium操作Web页面 Why? 通常情况下,网络安全相关领域,更多是偏重于协议和通信。但是,如果协议通信过程被加密或者无法了解其协议构成,是无法直接通过协议进行处理。此时,可以考虑模拟UI操作,进而实现相…...

科普文:linux系列之操作系统内存管理简介
概叙 操作系统内存管理是计算机系统中的核心技术之一,页式管理、段式管理和段页式管理各有优缺点。页式管理通过固定大小的页框减少了外部碎片,但可能导致内部碎片;段式管理符合程序逻辑,提供了灵活的内存保护,但可能…...

【已解决】关于MyBatis的collection集合中只能取到一条数据的问题
一、问题 在涉及多表查询的时候,使用collection元素来映射集合属性时,出现了只能查询到一条数据的情况,但用sql语句在数据库中查询会有多条记录。 二、原因 如果两表联查,主表和明细表的主键都是id的话,明细表的多条…...

前端的学习-CSS(弹性布局-flex)
一:什么是弹性布局-Flex flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。 语法: .box{display: flex; } .box{display: inline-flex; } 注意,设为 Flex 布局以后࿰…...

vue3集成LuckySheet实现导入本地Excel进行在线编辑,以及导出功能
第一步:克隆或者下载下面的代码 git clone https://github.com/dream-num/Luckysheet.git第二步:安装依赖 npm install npm install gulp -g 第三步:运行 npm run dev效果如下图所示 第四步:打包 打包执行成功后,…...

【征求意见】同济大学--城镇给水厂碳排放核算与评价方法
城镇给水厂保障城镇居民正常生活,是社会经济良性发展的重要基础性设施,对于我国双碳战略目标的实现至关重要。 随着城镇化的发展,城镇供水量不断升高,加上 水资源与生态环境问题不断涌现,人们对水的安全和品质的需求日…...

【Python】后台开发返回方法和状态码类的实现
Python 后台开发中,获取返回的类方法,以及状态码类的实现 代码备份 Code - response.py """ Response class for quick generate response """ from loguru_logger import get_loggerlogger get_logger(__name__)clas…...

opencloudosV8.6和openEuler 24安装 k8s
在三台机器上部署 Kubernetes 集群 1.环境准备2.在所有节点上进行以下步骤1. 更新系统和安装必要的软件包2. 禁用交换分区3. 禁用防火墙和SElinux4.系统主机名5.设置主机名与IP地址解析6.配置内核转发及网桥过滤7. 配置 Docker Cgroup 驱动8. 添加 Kubernetes 仓库并安装 kubea…...

Tensor安装和测试
1: 打开git官方 https://github.com/NVIDIA/TensorRT 2: 下载得到:TensorRT-10.2.0.19.Linux.x86_64-gnu.cuda-11.8.tar.gz 3: 下载后配置环境变量,上面地址记得改成真实地址。 4: 如果想python使用tensorrt,那么 解压后目录,…...

ELK对业务日志进行收集
ELK对业务日志进行收集 下载httpd 进到文件设置收集httpd的文件进行 设置 编辑内容 用于收集日志的内容 将日志的内容发送到实例当中 input {file{path > /etc/httpd/logs/access_logtype > "access"start_position > "beginning"}file{path &g…...

新质生产力
新质生产力”是一个相对较新的概念,指的是在数字化、智能化背景下,依托新技术、新业态、新模式,提升生产力质量和效率的一种生产力形态。它强调的是技术和创新对生产力的提升作用,尤其是在人工智能、大数据、互联网等新兴技术的推…...

《LeetCode热题100》---<5.②普通数组篇五道>
本篇博客讲解LeetCode热题100道普通数组篇中的六道题 第三道:轮转数组(中等) 第四道:除自身以外数组的乘积(中等) 第三道:轮转数组(中等) 方法一:使用额外的数…...

【面试题】【C语言】寻找两个正序数组的中位数
寻找两个正序数组的中位数 仅供学习 题目 算法时间复杂度 二分查找算法,时间复杂度为 O(log(min(m, n))),其中 m 和 n 分别是两个数组的长度。 子函数 查找两个数字的最大值 int max(int a, int b) {return a > b ? a : b; }查找两个数字的最小…...

原始的原型链是怎样玩的
带着问题看代码: 1、原始的继承是怎样实现继承的? A类的prototype 属性 B类的实例 2、实现继承后,连B类的中实例的属性(放在了A类的prototype中)和原型链的上的东西都可以用 3、A.prototype.constructor实际上已经指向…...

RabbitMQ高级篇(如何保证消息的可靠性、如何确保业务的幂等性、延迟消息的概念、延迟消息的应用)
文章目录 1. 消息丢失的情况2. 生产者的可靠性2.1 生产者重连2.2 生产者确认2.3 生产者确认机制的代码实现2.4 如何看待和处理生产者的确认信息 3. 消息代理(RabbitMQ)的可靠性3.1 数据持久化3.2 LazyQueue( 3.12 版本后所有队列都是 Lazy Qu…...

正点原子imx6ull-mini-Linux驱动之platform设备驱动实验(14)
我们在前面几章编写的设备驱动都非常的简单,都是对IO进行最简单的读写操作像I2C、 SPI、LCD 等这些复杂外设的驱动就不能这么去写了,Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,在这个思路下诞生…...

z3基础学习
z3基础学习 z3是一个微软出品的开源约束求解器,能够解决很多种情况下的给定部分约束条件寻求一组满足条件的解的问题。 安装:pip install z3-solver 1. 简单使用 from z3 import * x Int(x) #创建名为x的int类型变量 y Int(y) solve(x y10,2*x…...

开发助手专业版,有反编译等多种功能
软件介绍 开发助手能够用来快速调试应用以及查看手机软硬件相关信息,包括:快速打开或关闭开发者选项中的选项。 将原来几十秒的操作缩短为一次点击。包括显示布局边界,显示 GPU 过度绘制。显示布局更新。强制 GPU 渲染 显示 GPU 视图更新&a…...

嵌入式初学-C语言-十一
#接嵌入式初学-C语言-十,以及部分例题# 循环结构 break和continue break 功能: 1. 用在switch中,用来跳出switch的case语句;如果case没有break,可能会产生case穿透。 2. 用在循环中(while、do..while、for..&#…...

浅谈几个常用OJ的注册方式
众所周知,好的OJ是成功的一半,但是有些英文OJ的注册很让人伤脑筋。 CodeForces 点进官网 戳这里 然后就会进入这个页面 在这一页里面里填写好信息即可 最后,一个邮件就会发到你的邮箱上,点击其中的链接即可激活账号 AtCoder …...

Html实现全国省市区三级联动
目录 前言 1.全国省市区的Json数据 2.找到Json数据文件(在此博文绑定资源)之后,放到resource目录下。 3.通过类加载器加载资源文件,读取Json文件 3.1 创建JsonLoader类 3.2 注入JsonLoader实体,解析Json文件 4.构建前端Html页面 5.通过…...

前端构建工具Webpack 与 Vite 大对比
在现代前端开发领域,构建工具扮演着至关重要的角色。它们不仅可以帮助我们管理项目依赖关系,还可以优化我们的代码,使其在生产环境中运行得更快更高效。其中两个最受欢迎的构建工具就是 Webpack 和 Vite。在这篇文章中,我们将深入…...

Ubuntu-22.04环境搭建
安装wget(一般ubuntu会自带) sudo apt-get install wget 更换国内软件源 先备份原来的/etc/apt/source.list⽂件 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 防止修改错误 导致无可挽回 将下列国内镜像源 写入原来的/etc/apt/source.list⽂件(注…...