开发适合el-dialog的拉伸拖拽自定义指令和适配自定义的图片查看组件

目录

一、应用场景

二、开发流程

1.自定义指令

2.功能原理

3.难点

三、详细开发

四、总结


一、应用场景

我之前有开发过一个图片查看的组件,这个组件可在单页面打开,也可以在弹窗里打开,但是弹窗因为是比较固定,所以有一些局限性,只能拖拽,不能改变弹窗大小,于是有了开发【可以拖拽改变大小的弹窗】组件

原先的图片查看组件的博客地址:仿照elemenet-image的预览开发图片切换和放大缩小等功能_vue3 <el-image> 下方一行缩略图 可左右-CSDN博客

上方的组件实现效果如此:

  1. 目标:这次我需要实现的是满足上方的图片查看的功能(去掉底部的轮播图,弹窗不太需要),还需要满足弹窗的拖拽边框可以改变弹窗大小,并且弹窗的顶部可以被拖拽着移动位置。
  2. 实现方案:因为我是使用的是el-dialog,所以本身弹窗就可以拖拽,只是不能被手动改变大小,查找了一些解决方案后,于是借助一些思想,实现了一个自定义指令,期间踩了一些坑。
  3. 实现功能:
  • 拖动弹窗:通过拖动弹窗的头部 (.el-dialog__header),可以在页面上自由移动弹窗的位置。鼠标按下头部并拖动时,会实时更新弹窗的位置。但是不能移出左侧和上侧视图范围,这部分也在下面的调整大小里有限制。
  • 双击全屏/还原:双击弹窗的头部,可以在全屏和恢复到之前的大小和位置之间切换。全屏状态下,弹窗覆盖整个视口,头部不可拖动。再次双击会恢复到初始的大小和位置。

  • 调整大小

    • 右下角调整:通过右下角的一个小区域 (se-resize),可以拖动调整弹窗的宽度和高度,同时保持最小宽度和高度限制。
    • 左右侧调整:通过左右两侧的小区域 (w-resize),可以水平调整弹窗的宽度。同样,宽度不能小于设定的最小值。
    • 下侧调整:通过下边的小区域 (n-resize),可以垂直调整弹窗的高度,高度不能小于设定的最小值

4.实现效果:


二、开发流程

这里对创建自定义指令做一些简单介绍

1.自定义指令

首先需要了解以下知识:

Vue 3 的自定义指令提供了一些生命周期钩子,用于在指令应用到元素的不同阶段执行特定的操作:

  • beforeMount:指令绑定到元素并插入父节点之前调用。
  • mounted:指令绑定到元素后调用。
  • beforeUpdate:指令所在组件的 VNode 更新之前调用。
  • updated:指令所在组件的 VNode 更新之后调用。
  • beforeUnmount:指令所在组件销毁之前调用。
  • unmounted:指令绑定的元素移出 DOM 之后调用。

简单的示例:
 

// 在 main.js 中注册全局自定义指令
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

app.directive('focus', {
  mounted(el) {
    el.focus();
  }
});

app.mount('#app');
 

使用:
<template> <input v-focus /> </template>

我在开发过程中反复测试,得出我们在updated进行绑定就行,这样才能保证弹窗的创建和绑定。

如果需要实现一个可以拖拽改变弹窗大小的指令,那么首先建立一个文件夹,如下:

这里先不讨论dialog.js的具体内容,先创建如下的内容:

// src\directive\index.js
import drag from './dialog'
export default function (app) {app.directive('dialogDrag', drag)
}// src\directive\dialog.jsexport const dialogDrag = (el, binding, vnode, oldVnode) => {//这里是补充逻辑的地方
}export default {updated(el, binding, vnode, prevVnode) {dialogDrag(el, binding, vnode, prevVnode)}
}

2.功能原理

为了实现我想要的功能,可以通过 JavaScript 操作 DOM 元素的样式和事件监听器,来实现拖拽拉伸移动等等,开发前,先进行三项功能的原理整理:

 1. 弹窗拖拽功能——通过拖动弹窗的标题栏来移动整个弹窗的位置

  • 通过 el.querySelector('.el-dialog__header') 获取弹窗的标题栏元素(.el-dialog__header)。
  • 设置标题栏的 cursormove,提示用户可以拖动该区域。
  • 通过 mousedown 事件监听用户按下鼠标的动作,计算并记录鼠标点击位置与弹窗左上角的偏移量。
  • 当鼠标移动时,通过 mousemove 事件更新弹窗的位置,使其跟随鼠标移动。在 mouseup 事件中移除 mousemovemouseup 事件监听,以终止拖拽操作。

2. 弹窗拉伸功能——通过拖动弹窗的边缘或角落来调整弹窗的尺寸。

  • 在弹窗的右下角(se-resize)、右边(w-resize)、左边(w-resize)、下边(n-resize)分别添加拉伸控制块,这些控制块是通过 document.createElement('div') 动态创建并插入到弹窗中。
  • 每个控制块绑定一个 mousedown 事件,用于监听用户的拉伸操作。根据用户鼠标移动的方向,计算弹窗的宽度或高度变化,并更新弹窗的 widthheight 样式属性。
  • 拉伸结束时,通过 mouseup 事件移除 mousemovemouseup 事件监听,停止尺寸调整操作。

3. 双击全屏与还原——通过双击弹窗的标题栏实现弹窗全屏和还原

  • 双击标题栏时(dblclick 事件),根据当前弹窗是否全屏状态(由 isFullScreen 标志控制)执行全屏或还原操作。
  • 全屏时,将弹窗的位置和尺寸调整为占满整个视窗(100VW, 100VH),并移除标题栏的拖拽功能。
  • 还原时,恢复弹窗到全屏前的尺寸和位置,并重新启用标题栏的拖拽功能。

3.难点

  1. 同步尺寸和位置:在拖拽或拉伸时,需要实时同步弹窗的位置和尺寸,这涉及到对鼠标移动的精确跟踪,并处理弹窗在不同浏览器窗口尺寸下的表现。
  2. 边界处理:在拖拽和拉伸时,防止弹窗超出窗口的可视区域,尤其是避免标题栏被拖出窗口顶部。
  3. 多方向拉伸的冲突处理:在实现多方向拉伸时,确保各方向的拉伸控制块不会互相冲突。例如,右下角的拉伸控制块涉及同时调整宽度和高度,需要正确处理与单方向拉伸控制块之间的优先级问题。

三、详细开发

注意!我先写踩坑的点,如下:

第一步,我们要找到我们需要在哪里使用,我的应用场景就是在弹窗的地方使用,所以我就想定义一个弹窗的指令,理想的情况是这样的:

  <el-dialogv-model="dialogVisible"v-dialogDrag  ///这里width="50%"top="0vh":z-index="2080":modal="false":close-on-click-modal="false"modal-class="dialog_class"><div class="image-view-container"><ImageView :url="dialogImageUrl" style="width: 100%" @changeImage="changeImage" /></div></el-dialog>

然后在指令里去写获取当前弹窗的DOM,比如这样:

export const dialogDrag = (el, binding, vnode, oldVnode) => {const dialogElement = el.querySelector('.el-dialog')// console.log(dialogElement) // 这里是 el-dialog 元素的 DOMif (!dialogElement) {return}
}

就会发现怎么也获取不到当前的dom。 

我一开始以为我是钩子时机不对,updated 钩子可能会在元素还未完全渲染时触发,这可能导致无法获取到子元素。所以为了确保 DOM 结构已经完全渲染,尝试使用 mounted 钩子,结果也一样,然后我尝试用我常用的方法:nextTick,也无法实现……于是第一步就卡在这里了。

问题就在于:el-dialog 组件可能还未完全渲染完成,无法正确获取到 DOM 元素。

当然可以通过添加一些调试信息,检查 el-dialog 是否确实存在,我在写的过程中,确实这样写无法实现。

出现这样的问题:

所以经过多次调试,我选择了这样的方式:

<div v-dialogDrag class="image-view"><el-dialogv-model="dialogVisible"width="50%"top="0vh":z-index="2080":modal="false":close-on-click-modal="false"modal-class="dialog_class"><div class="image-view-container"><ImageView :url="dialogImageUrl" style="width: 100%" @changeImage="changeImage" /></div></el-dialog></div>

那么具体的指令的代码如下:

export const dialogDrag = (el, binding, vnode, oldVnode) => {const dialogElement = el.querySelector('.el-dialog')// console.log(dialogElement) // 这里是 el-dialog 元素的 DOMif (!dialogElement) {return}//弹框可拉伸最小宽高let minWidth = 400let minHeight = 400//初始非全屏let isFullScreen = false//当前宽高let nowWidth = 0let nowHight = 0//当前顶部高度let nowMarginTop = 0//获取弹框头部(这部分可双击全屏)const dialogHeaderEl = el.querySelector('.el-dialog__header')//弹窗const dragDom = el.querySelector('.el-dialog')//弹窗bodyconst dialogBodyEl = el.querySelector('.el-dialog__body')// 设置body的最小高宽dialogBodyEl.style.minWidth = minWidth - 5 + 'px'dialogBodyEl.style.minHeight = minHeight - 100 + 'px'dialogBodyEl.style.height = '100%'//给弹窗加上overflow auto;不然缩小时框内的标签可能超出dialog;dragDom.style.overflow = 'auto'//清除选择头部文字效果dialogHeaderEl.onselectstart = new Function('return false')//头部加上可拖动cursordialogHeaderEl.style.cursor = 'move'// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)let moveDown = (e) => {// 鼠标按下,计算当前元素距离可视区的距离const disX = e.clientX - dialogHeaderEl.offsetLeftconst disY = e.clientY - dialogHeaderEl.offsetTop// 计算弹窗样式中的 --el-dialog-margin-top 值const dialogStyles = window.getComputedStyle(dragDom)const marginTopVh = parseFloat(dialogStyles.getPropertyValue('--el-dialog-margin-top'))// 计算初始弹窗顶部相对于可视区域顶部的偏移量const dialogMarginTopPx = window.innerHeight * (marginTopVh / 100)const initialTop = dialogMarginTopPx// 获取初始弹窗距离窗口左侧的距离const dialogMarginLeft = getComputedStyle(dragDom).marginLeftconst initialLeft = parseFloat(dialogMarginLeft)// 获取到的值带px 正则匹配替换let styL, styT// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为pxif (sty.left.includes('%')) {styL = +document.body.clientWidth * (+sty.left.replace(/%/g, '') / 100)styT = +document.body.clientHeight * (+sty.top.replace(/%/g, '') / 100)} else {styL = +sty.left.replace(/px/g, '')styT = +sty.top.replace(/px/g, '')}document.onmousemove = function (e) {// 通过事件委托,计算移动的距离const l = e.clientX - disXconst t = e.clientY - disY// 计算弹窗的左边界,不能超过窗口的左侧const minLeft = -initialLeft// 控制弹窗的左边界const left = Math.max(minLeft, l + styL)// 移动当前元素dragDom.style.left = `${left}px`dragDom.style.top = `${Math.max(-initialTop, t + styT)}px` //确保了拖拽过程中弹窗头部不会超出窗口的顶部//将此时的位置传出去//binding.value({x:e.pageX,y:e.pageY})}document.onmouseup = function (e) {document.onmousemove = nulldocument.onmouseup = null}}dialogHeaderEl.onmousedown = moveDown//双击(头部)效果不想要可以注释dialogHeaderEl.ondblclick = (e) => {if (isFullScreen == false) {nowHight = dragDom.clientHeightnowWidth = dragDom.clientWidthnowMarginTop = dragDom.style.marginTopdragDom.style.left = 0dragDom.style.top = 0dragDom.style.height = '100VH'dragDom.style.width = '100VW'dragDom.style.marginTop = 0dragDom.style.marginBottom = 0isFullScreen = truedialogHeaderEl.style.cursor = 'initial'dialogHeaderEl.onmousedown = null} else {dragDom.style.height = 'auto'dragDom.style.width = nowWidth + 'px'dragDom.style.marginTop = nowMarginTopisFullScreen = falsedialogHeaderEl.style.cursor = 'move'dialogHeaderEl.onmousedown = moveDown}}//拉伸右下方let resizeEl = document.createElement('div')dragDom.appendChild(resizeEl)//在弹窗右下角加上一个10-10px的控制块resizeEl.style.cursor = 'se-resize'resizeEl.style.position = 'absolute'resizeEl.style.height = '10px'resizeEl.style.width = '10px'resizeEl.style.right = '0px'resizeEl.style.bottom = '0px'resizeEl.style.zIndex = '99'//鼠标拉伸弹窗resizeEl.onmousedown = (e) => {// 记录初始x位置let startX = e.clientX// 鼠标按下,计算当前元素距离可视区的距离let disX = e.clientX - resizeEl.offsetLeftlet disY = e.clientY - resizeEl.offsetTopdocument.onmousemove = function (e) {e.preventDefault() // 移动时禁用默认事件// 通过事件委托,计算移动的距离//这里 由于elementUI的dialog控制居中的,所以水平拉伸效果是双倍//比较最小宽高和现在的宽高的大小,取大值dragDom.style.width = `${Math.max(minWidth, e.clientX - disX + (e.clientX - startX))}px`dragDom.style.height = `${Math.max(minHeight, e.clientY - disY)}px`}//拉伸结束document.onmouseup = function (e) {document.onmousemove = nulldocument.onmouseup = null}}//拉伸右边let resizeElR = document.createElement('div')dragDom.appendChild(resizeElR)//在弹窗右下角加上一个10-10px的控制块resizeElR.style.cursor = 'w-resize'resizeElR.style.position = 'absolute'resizeElR.style.height = '100%'resizeElR.style.width = '10px'resizeElR.style.right = '0px'resizeElR.style.top = '0px'//鼠标拉伸弹窗resizeElR.onmousedown = (e) => {let elW = dragDom.clientWidthlet initialOffsetLeft = dragDom.offsetLeft// 记录初始x位置let startX = e.clientXdocument.onmousemove = function (e) {e.preventDefault() // 移动时禁用默认事件//右侧鼠标拖拽位置if (startX > initialOffsetLeft + elW - 20 && startX < initialOffsetLeft + elW) {//往左拖拽if (startX > e.clientX) {dragDom.style.width = `${Math.max(minWidth, elW - (startX - e.clientX) * 2)}px`}//往右拖拽if (startX < e.clientX) {dragDom.style.width = `${elW + (e.clientX - startX) * 2}px`}}}//拉伸结束document.onmouseup = function (e) {document.onmousemove = nulldocument.onmouseup = null}}//拉伸左边let resizeElL = document.createElement('div')dragDom.appendChild(resizeElL)//在弹窗右下角加上一个10-10px的控制块resizeElL.style.cursor = 'w-resize'resizeElL.style.position = 'absolute'resizeElL.style.height = '100%'resizeElL.style.width = '10px'resizeElL.style.left = '0px'resizeElL.style.top = '0px'//鼠标拉伸弹窗resizeElL.onmousedown = (e) => {let elW = dragDom.clientWidthlet initialOffsetLeft = dragDom.offsetLeft// 记录初始x位置let startX = e.clientXdocument.onmousemove = function (e) {e.preventDefault() // 移动时禁用默认事件//左侧鼠标拖拽位置if (startX > initialOffsetLeft && startX < initialOffsetLeft + 20) {//往左拖拽if (startX > e.clientX) {dragDom.style.width = `${elW + (startX - e.clientX) * 2}px`}//往右拖拽if (startX < e.clientX) {dragDom.style.width = `${Math.max(minWidth, elW - (e.clientX - startX) * 2)}px`}}}//拉伸结束document.onmouseup = function (e) {document.onmousemove = nulldocument.onmouseup = null}}// 拉伸下边let resizeElB = document.createElement('div')dragDom.appendChild(resizeElB)//在弹窗右下角加上一个10-10px的控制块resizeElB.style.cursor = 'n-resize'resizeElB.style.position = 'absolute'resizeElB.style.height = '10px'resizeElB.style.width = '100%'resizeElB.style.left = '0px'resizeElB.style.bottom = '0px'// 鼠标拉伸弹窗resizeElB.onmousedown = (e) => {// 记录初始鼠标位置和弹窗尺寸let startY = e.clientYlet elH = dragDom.clientHeightdocument.onmousemove = function (e) {e.preventDefault() // 移动时禁用默认事件dragDom.style.height = `${Math.max(minHeight, elH + (e.clientY - startY) * 2)}px`}// 拉伸结束document.onmouseup = function (e) {document.onmousemove = nulldocument.onmouseup = null}}
}export default {updated(el, binding, vnode, prevVnode) {dialogDrag(el, binding, vnode, prevVnode)}
}

 mousemovemouseupmousedown 是 JavaScript 中用于处理鼠标交互的事件,分别对应鼠标的移动、按下和松开操作,所以上述代码的实现也是注意借助这几个事件来实现的。当然加一些防抖,效果会更好。


四、总结

说下难点,第一个就是生命周期的选择和指令使用的位置,一定套一个div。

其他难点就是,需要动态计算弹窗的位置与尺寸,因为弹窗的位置和尺寸是动态计算的,涉及到鼠标的实时位置和弹窗初始位置之间的关系。为了确保用户体验,处理窗口边界的限制也是一个难点,确保弹窗不会拖出可视区域(这里我的可视区域是左边和上面不能拖出,但是右边和下边可以),还有一个比较难的就是弹窗内的图片查看组件的样式适配,因为要对弹窗边框拖拽改变大小时,图片也要自适应的改变,所以这个样式方面就做了很多功夫,代码也贴上去了,仅供参考~

至于可以优化的点,应该就是拖拽边框的时候更丝滑和防抖吧,如果有其他建议,麻烦评论区指出~

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

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

相关文章

常见限流算法-固定窗口、滑动窗口、漏桶、令牌桶

为什么需要限流 限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理…

OpenCV颜色空间转换(1)颜色空间转换函数cvtColor()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将图像从一个颜色空间转换到另一个颜色空间。 此函数将输入图像从一个颜色空间转换到另一个颜色空间。在进行 RGB 颜色空间之间的转换时&#x…

怎样查看电脑文件夹大小?文件夹数据丢失怎么找回

在日常使用电脑的过程中&#xff0c;‌我们经常需要查看文件夹的大小&#xff0c;‌以便更好地管理磁盘空间。‌然而&#xff0c;‌有时我们可能会遇到文件夹数据丢失的问题&#xff0c;‌这可能是由于误删除、‌系统错误或病毒感染等多种原因造成的。‌本文旨在介绍如何查看电…

【苍穹外卖】Day 7 地址簿、下单、定时处理

1 地址簿模块 地址簿实体对象&#xff1a; package com.sky.entity;import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor;import java.io.Serializable;/*** 地址簿*/ Data Builder NoArgsConstructor AllArgsCon…

001集——CAD—C#二次开发入门——开发环境基本设置

CAD C#二次开发首先需要搭建一个舒服的开发环境&#xff0c;软件安装后&#xff0c;需要修改相关设置。本文为保姆级入门搭建开发环境教程&#xff0c;默认已成功安装vs和cad 。 第一步&#xff1a;创建类库 第二步&#xff1a;进行相关设置&#xff0c;如图&#xff1a; 下一…

JavaEE:多线程进阶(CAS)

文章目录 CAS什么是 CASCAS 伪代码 CAS有哪些应用CAS的ABA问题什么是ABA问题ABA问题带来的BUG解决方案 CAS 什么是 CAS CAS: 全称Compare and swap&#xff0c;字面意思:”比较并交换“&#xff0c;一个 CAS 涉及到以下操作: 我们假设内存中的原数据V&#xff0c;旧的预期值A…

刷题记录(2)

1. HWOD机试 - 模拟消息队列(100) package com.yue.test;import org.junit.Test;import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List;/*** Author: 夜雨* Date: 2021-12-08-10:31* Description:* Version 1.0*/ public…

【Android】ViewPager基本用法总结

文章目录 一、添加ViewPager控件二、构建适配器类三、在 MainActivity 中设置适配器示例一&#xff1a;图片切换适配器MainActivity 示例二&#xff1a;Fragment切换适配器FragmentMainActivity ViewPager 是 Android 中一个用于在同一屏幕上滑动不同页面&#xff08;通常是左右…

OBS怎么设置录制配置?3个电脑录屏小技巧妥妥教会你

OBS Studio是一款广受好评的开源录屏和直播软件&#xff0c;它以其强大的功能和用户友好的操作界面而闻名。对于初次接触OBS的用户来说&#xff0c;可能会对软件的众多按钮感到困惑。本文将为你提供一份简洁明了的OBS录屏指南&#xff0c;帮助你快速上手。 演示机型&#xff1a…

Hive数据库与表操作全指南

目录 Hive数据库操作详解 创建数据库 1&#xff09;语法 2&#xff09;案例 查询数据库 1&#xff09;展示所有数据库 &#xff08;1&#xff09;语法 &#xff08;2&#xff09;案例 2&#xff09;查看数据库信息 &#xff08;1&#xff09;语法 &#xff08;2&#…

Simulink:循环计数器 Counter Free-Running

原文&#xff1a;Matlab生成stm32代码_matlab stm32-CSDN博客 使用“Counter Free-Running”模块进行计数&#xff0c;参数配置如下 此配置的意思为每0.5秒计数一次&#xff0c;计数的最大值为2^Nbits – 1&#xff0c;其中Nbits为所填的2&#xff0c;所以这里最大值为3。 示波…

【多线程】死锁

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 死锁的三种情况1.1 一个线程一把锁&#xff08;同一个线程给同一个对象加两次锁的情况&#xff09;1.2 两…

彻底解决 node/npm, Electron下载失败相关问题, 从底层源码详解node electron 加速配置

最近玩了一下electron项目, 总是会遇到electron的下载失败问题, 于是看了一下node源码, 做一个记录. node/npm 加速配置 这个配置通过设置node配置里面的registry 这个配置项来完成加速. 配置方法 npm config set registry https://registry.npmmirror.com上面的命令就是将当…

【全网最全】2024年数学建模国赛C题超详细保奖思路+可视化图表+成品论文+matlab/python代码等(后续会更新

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片&#xff0c;那是获取资料的入口&#xff01; 基于优化模型的农作物的种植策略 摘要 随着农业生产向集约化和智能化方向发展&#xff0c;优化种植策略以最大化经济收益成为当前农业研究中的重要问题。本…

车载测试协议:ISO-14229、ISO-15765、ISO-11898、ISO-26262【车企项目实操学习】②

FOTA模块中OTA的知识点&#xff1a;1.测试过程中发现哪几类问题&#xff1f; 可能就是一个单键的ecu&#xff0c;比如升了一个门的ecu&#xff0c;他的升了之后就关不上&#xff0c;还有就是升级组合ecu的时候&#xff0c;c屏上不显示进度条。 2.在做ota测试的过程中&#xff…

已入职华为!!关于我成功拿下华为大模型算法岗经验总结

方向:大模型算法工程师 整个面试持续了1小时10分钟&#xff0c;能够看出面试官是典型搞技术的&#xff0c;问的很专业又很细&#xff0c;全程感觉压力好大&#xff0c;面完后感觉丝丝凉意&#xff0c;不过幸好还是成功拿下了Offer 一面: 自我介绍 简历项目深度交流 1.项目的背…

Java笔试面试题AI答之JDBC(2)

文章目录 7. 列出Java应该遵循的JDBC最佳实践&#xff1f;8. Statement与PreparedStatement的区别,什么是SQL注入&#xff0c;如何防止SQL注入Statement与PreparedStatement的区别什么是SQL注入如何防止SQL注入 9. JDBC如何连接数据库&#xff1f;1. 加载JDBC驱动程序2. 建立数…

[网络原理]关于网络的基本概念 及 协议

文章目录 一. 关于网络的概念介绍1. 局域⽹LAN2. ⼴域⽹WAN3. 主机4. 路由器5. 交换机IP地址端口号 二. 协议协议分层TCP/IP五层模型(或四层)OSI七层模型封装分用 一. 关于网络的概念介绍 1. 局域⽹LAN 局域⽹&#xff0c;即 Local Area Network&#xff0c;简称LAN。 Local …

二叉树的层次遍历(10道)

&#xff08;写给未来遗忘的自己&#xff09; 102.二叉数的层序遍历&#xff08;从上到下&#xff09; 题目&#xff1a; 代码&#xff1a; class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> r…

JVM系列(十) -垃圾收集器介绍

一、摘要 在之前的几篇文章中,我们介绍了 JVM 内部布局、对象的创建过程、运行期的相关优化手段以及垃圾对象的回收算法等相关知识。 今天通过这篇文章,结合之前的知识,我们一起来了解一下 JVM 中的垃圾收集器。 二、垃圾收集器 如果说收集算法是内存回收的方法论,那么…