python_way day16 DOM
Python_way day16
1、Dom (找到html中的标签)
一、DOM
1、查找元素
- 直接查找
1 2 3 4 | document.getElementById 根据ID获取一个标签 --->这里是获取一个标签中间没有s document.getElementsByName 根据name属性获取标签集合 document.getElementsByClassName 根据 class 属性获取标签集合/ 兼容性不太好 ----> 这些都是获取多个标签中间有s document.getElementsByTagName 根据标签名获取标签集合 |
- 间接查找
1 2 3 4 5 6 7 8 9 10 11 12 13 | parentNode // 父节点 childNodes // 所有子节点 firstChild // 第一个子节点 lastChild // 最后一个子节点 nextSibling // 下一个兄弟节点 previousSibling // 上一个兄弟节点<br>#以上的nodes既包含标签,也包含文本内容<br><div><br> 我写的这些文字不算元素,只算节点所以使用node可以获得,并且也会把下面的a标签一起获得<br> <a>1</a><br> <a>2</a><br></div><em id="__mceDel"> parentElement // 父节点标签元素 children // 所有子标签 firstElementChild // 第一个子标签元素 lastElementChild // 最后一个子标签元素 nextElementtSibling // 下一个兄弟标签元素 previousElementSibling // 上一个兄弟标签元素<br>只包含标签</em> |
直接查找:
Nodes包含了所有子节点。
2、操作
a、获取和修改
- 对于有值的标签使用value获取或者设置
1 2 3 4 5 6 7 8 9 10 11 12 13 | t = document.getElementById( "i2" ) <div id= "i2" >…</div> t.children [<input type= "text" >] text = t.children[0] <input type= "text" > text.value "" text.value //获取当前input框中输入的值 "123" text.value = 345 //设置input框中的值 345 |
- 对于没有值的但是有文本内容的比如a标签
使用innetText或者innerHTML这两个标签
innetText
1 2 3 4 5 6 | t = document.getElementById( "i3" ) <a id= "i3" href= "http://www.baidu.com" >百度</a> t.innerText //获取a标签中的内容 "百度" t.innerText = "是百度" //设置a标签中的内容,让其在页面上变化 "是百度" |
innerHTML
1 2 3 4 5 6 | <em><a id= "i3" href= "http://www.baidu.com" >百<span>666</span>度</a></em><br>t = document.getElementById( "i3" ) <a id= "i3" href= "http://www.baidu.com" >…</a> t.innerText "百666度" t.innerHTML "百<span>666</span>度" HTML会将文本中的所有标签都拿到 |
实例一:搜索框中的文字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>搜索框文字消失</title> </head> <body> <input id= "i1" type= "text" value= "请输入搜索内容" onfocus= "Focus();" onblur= "Blur()" /> //onfocus:获取鼠标点击时的动作, onblur:获取鼠标移除时的动作 <script type= "text/javascript" > function Focus() { var tag = document.getElementById( "i1" ); //获取id=i1的标签 if (tag.value== "请输入搜索内容" ){ //如果这个表亲的内容是请输入请输入搜索内容,一点就清空,否则就不管 tag.value = "" ; } } function Blur() { var tag = document.getElementById( "i1" ); var tag_value = tag.value; if (tag_value.trim().length==0){ //如果搜索框去空格后的长度是0,就证明用户没有输入,那么就将内容恢复 tag.value = "请输入搜索内容" ; } } </script> </body> </html> |
b、class操作
1 2 3 | 增加样式<br>className // 获取所有类名 classList.remove(cls) // 删除指定类 classList.add(cls) // 添加类<br><br>直接改变样式<br><input type="text" style="color:red; font-size:40px;"/><br>tag.style.color = "red" . //定义文字的颜色<br>tag.style.fontSize = "40px" //定义字体大小 就是把css中的 - 去掉第二个单词首字母大写,别的样式都是这个规律 |
实例二:摸态对话框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | <! DOCTYPE html> < html lang="en"> < head > < meta charset="UTF-8"> < title >莫泰对话框</ title > < style > .hide{ display: none !important; /*定义一个display = none的标签,让后面使用*/ } .b{ position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,.6); z-index: 9; } .modal{ height: 200px; width: 400px; background-color: lightskyblue; position: fixed; z-index: 10; left: 50%; top: 50%; margin-left: -200px; margin-top: -100px; } .out_input{ margin-left: 98px; height: 100px; margin-top: 10px; } .d_input{ display: inline-block; width: 173px; height: 21px; margin-top: 10px; } .login_back{ height: 50px; width: 204px; background-color: dodgerblue; margin: 0 auto; text-align: center; line-height:50px; border: 1px darkblue solid; } input{ width: 200px; height: 30px; } .login_icon{ top: 16px; left: 68px; float: inherit; } .modal_in{ height: 30px; width: 100px; float:right; } </ style > </ head > < body > < input class="reg" type="button" value="登陆" onclick="ShowModal();"> <!--一旦点击这个button按钮,onclick,就触发showmodal这个js函数--> < div id ="black" class="b hide"></ div > <!--让背景这个样式使用两个类属性,一个是颜色,布局,一个就是隐藏,现不显示出来--> < div id ="mod" class="modal hide"> <!--让中间整个的这个模块使用两个类属性,一个是颜色,布局,一个就是隐藏,现不显示出来--> < div class="login_back"> < span class="login_icon"> 账户登陆 </ span > </ div > < div class="out_input"> < input type="text"/> < span class="input_imp"></ span > < span class="d_input"> < input type="text" class="input_d"/> < span class="input_imp"></ span > </ span > </ div > < button class="modal_in" onclick="HideModal();">返回</ button > <!--一旦点击这个button按钮,onclick,就触发HideModal这个js函数--> </ div > < script > function ShowModal() { var t1 = document.getElementById("black"); var t2 = document.getElementById("mod"); //< div id="black" class="b"></ div > //< div id="mod" class="modal"></ div > t1.classList.remove("hide"); //移除tag1这个list中的class:hide t2.classList.remove("hide"); //移除tag2这个list中的class: hide } function HideModal() { var t1 = document.getElementById("black"); var t2 = document.getElementById("mod"); //< div id="black" class="b hide"></ div > //< div id="mod" class="modal hide"></ div > t1.classList.add("hide"); //移除tag1这个list中的class:hide t2.classList.add("hide"); //移除tag2这个list中的class: hide } </ script > </ body > </ html > |
小知识点:
1 | <a href= "javascript:void (0)" >取消</a> //表示这个a标签什么都不做 |
修改样式:
1 2 3 4 5 6 7 | </ body > < div id="i1" style="background-color: red;font-size:40px"></ div > //定义好的样式 style </ body > < script > tag=document.getElementById("i1"); tag.style.backgroundColor = "green"; //这里直接修改样式 tag.style.fontSize="50px"< br ></ script > |
c、属性
id class 或者自定义属性中的值通过属性进行修改。
1 2 3 4 5 6 7 8 | t = document.getElementById("i1") //获取i1dom的所有内容 < input id="i1" name="123" today="monday" type="text" style="color:red;font-size: 40px"> t.getAttribute("type") //获取type的value "text" t.setAttribute("type","password") //把type属性改成password undefined t < input id="i1" name="123" today="monday" type="password" style="color:red;font-size: 40px">< br >t.remove("type") //删除type属性 |
实例三:全选反选
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | <! DOCTYPE html> < html lang="en"> < head > < meta charset="UTF-8"> < title >全选,反选,取消</ title > </ head > < body > < table border="1"> < thead > < tr > < th >序号</ th > < th >用户名</ th > < th >密码</ th > </ tr > </ thead > < tbody id="tb"> < tr > < td >< input type="checkbox"></ td > < td >1</ td > < td >11</ td > </ tr > < tr > < td >< input type="checkbox"></ td > < td >2</ td > < td >22</ td > </ tr > < tr > < td >< input type="checkbox"></ td > < td >3</ td > < td >33</ td > </ tr > </ tbody > </ table > < hr /> < input type="button" value="全选" onclick="CheckAll()"> < input type="button" value="反选" onclick="turnchose()"> < input type="button" value="取消" onclick="CancleALL()"> < script > function CheckAll() { var tb = document.getElementById("tb"); var trs = tb.children; for (var i = 0; i < trs.length ; i++) { var tr = trs[i]; var ch = tr.firstElementChild.firstElementChild; ch.checked = true; //这两种方法都可以,但是要和反选配合使用,反选用checked=true这里也要用, ch.setAttribute("checked","checked"); //其实所有的选择都使用checked=true就可以实现,但是为了使用set,remove这些功能就把这些都加上了 } } function CancleALL() { var tb=document.getElementById("tb"); var trs = tb.children; for (var i=0;i<trs.length;i++){ var tr=trs[i]; var ch = tr.firstElementChild.firstElementChild; console.log(ch); if (ch.checked){ ch.checked=false; // ch.checked就是获取web页面点击框后的状态 ch.removeAttribute("checked") } } } function turnchose() { var tb=document.getElementById("tb"); var trs = tb.children; for (var i=0;i<trs.length;i++){ var tr=trs[i]; var ch = tr.firstElementChild.firstElementChild; console.log(ch.checked); if (ch.checked){ ch.checked = false; ch.removeAttribute("checked"); } else{ ch.checked = true; ch.setAttribute("checked","checked") } } } </script> </ body > </ html > |
d、标签操作
- 创建标签
1 2 3 4 5 6 7 8 | // 方式一 var tag = document.createElement('a') tag.innerText = "hanxu" tag.className = "c1" tag.href = "http://www.cnblogs.com/python-way/" // 方式二 var tag = "< a class='c1' href='http://www.cnblogs.com/python-way/'>hanxu的python_way</ a >" |
优先使用方式一,用对象来创建标签。
- 操作标签
1 2 3 4 5 6 7 8 9 10 11 12 | p1 = document.getElementById("i1") //获取现在有的对象 < div id="i1">< p >hello</ p ></ div > tag1 = document.createElement("a") //1、使用对象的方式创建一个新的a标签 < a ></ a > tag1.innerText="111" //给这个a标签填写内容 "111" tag1 < a >111</ a > //重点在这里,tag的值不是字符串,而是一个真正的标签< br >< br > var tag2 = "< a class='c1' href='http://www.cnblogs.com/python-way/'>hanxu的python_way</ a >" //我们再用字符串创建一个标签 undefined tag2 "< a class='c1' href='http://www.cnblogs.com/python-way/'>hanxu的python_way</ a >" //重点这里还有,这样创建出来的标签是一个字符串< br >< br >以上创建出来的两种标签的操作方式不一样< br >< br >1、对象创建出来的标签是使用 obj.insertAdjacentElement('位置',obj)来操作< br >2、字符串创建出来的标签是使用 obj.insertAdjacentText('位置'.obj)来操作 |
对象形式添加标签:
方法1、obj.appendChile(obj1)
1 2 3 4 5 6 7 8 9 10 | p = document.getElementById("i1") < div id="i1">…</ div > tag = document.createElement("a") < a ></ a > tag.innerText='111' "111" tag < a >111</ a > p.appendChild(tag) < a >111</ a > |
这样添加是添加到 i1 child 的最后面(append)
方式2:
1 2 3 4 5 6 7 8 9 10 11 12 | = document.getElementById("i1") < div id="i1">…</ div > tag = document.createElement("a") < a ></ a > tag.innerText='111' "111" tag < a >111</ a > p.appendChild(tag) < a >111</ a > p.insertBefore(tag,p.children[0]) //添加到p标签的第一个孩子的前面 < a >111</ a > |
字符串形式的标签2中方法: HTML Text
1 2 3 | tag2,字符串创建标签有两种插入方式:< br >p.insertAdjacentHTML("beforeEnd",tag2) undefined p.insertAdjacentText("beforeEnd",tag2) |
#位置说明:位置一共有4个地方分别是 'beforeBegin', 'afterBegin', 'beforeEnd', 'afterEnd 这四种
beforeBegin : 在获取的标签 i1 平级的前面 (before:在...之前,begin:首先,开始)
afterBegin :在获取的标签i1里面的最前面 (after:在...之后,begin:首先,开始)
beforeEnd : 在获取标签的里面的最后(bafore:在...之前,end:最后)
afterEnd:在获取的标签平级的后面(after:在...之后,end:最后)
实例四:点赞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>点赞</title> <style> .item{ padding: 50px; cursor: pointer; position: relative; } </style> </head> <body> <!--先定义好点赞的位置--> <div class = "item" > <a onclick= "Fover(this)" >赞1</a> <!--获取点击事件,执行Fover函数,并且将自己a标签整体传到这个函数中( this 就是关键字)--> </div> <div class = "item" > <a onclick= "Fover(this)" >赞2</a> </div> <div class = "item" > <a onclick= "Fover(this)" >赞3</a> </div> <div class = "item" > <a onclick= "Fover(this)" >赞4</a> </div> <script> function Fover(ths) { // 先定义一些变量,一会用 var top = 49; //字体距离顶部的告诉 var left = 71; //字体距离左侧的距离 var op = 1; //透明度 var fontSize = 18; //字体大小初始值 // 然后创建一个span标签 var tag = document.createElement( "span" ); // 定义span标签的样式 tag.innerText = "+1" ; //span的文本内容 tag.style.top = top + "px" ; //设置字体的位置 tag.style.left = left + "px" ; tag.style.opacity = op; //设置字体的透明度 tag.style.position = "absolute" ; //让这个标签相对于item浮起来 tag.style.fontSize = fontSize + "px" ; //设置字体大小 ths.parentElement.appendChild(tag); //然后将上面创建的字体添加到父标签的子标签中,就是添加到了自己的旁边<br> //或者写成这样<br> // par = ths.parentElement; //获取自己的父标签<br> // par.insertAdjacentElement("afterBegin",tag); //添加tag标签 <br> var interval = setInterval( function () { //然后执行一个定时器, top -= 10; //每次执行定时器就让字体的属性变化 left += 10; op -= 0.1; fontSize += 5; tag.style.top = top + "px" ; tag.style.left = left + "px" ; tag.style.opacity = op; tag.style.position = "absolute" ; tag.style.fontSize = fontSize + "px" ; if (op <= 0.2) { //如果透明度小于0.2 clearInterval(interval); //删除这个定时器 ths.parentElement.removeChild(tag); //并且将这个+1的span删除,如果不删除到0.2透明度时字体会停在那里。 } },40); //定时器每40毫秒执行一次 } </script> </body> </html> |
e、定时器
1 2 3 4 | setInterval 多次定时器 clearInterval 清除多次定时器 <br>setTimeout 单次定时器 clearTimeout 清除单次定时器 |
实例五:定时让字消失
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>定时器</title> </head> <body> <div id= "status" style= "color: red" > </div> <input type= "submit" onclick= "DeleteStatus();" value= "删除" > <script> function DeleteStatus() { var s = document.getElementById( "status" ); s.innerText= "删除成功" ; var set = setTimeout( function () { //一次性定时器 var s = document.getElementById( "status" ); s.innerText= "" ; },5000); //等待5秒钟 } </script> </body> </html> |
f、位置操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 总文档高度 document.documentElement.offsetHeight 当前文档占屏幕高度 document.documentElement.clientHeight 自身高度 tag.offsetHeight 距离上级定位高度(定位标签:在整个标签中寻找有postion reltive,如果有找到距离这个标签的高度,如果整个页面没有postion,则计算到顶部的高度) tag.offsetTop 父定位标签 tag.offsetParent 滚动高度 tag.scrollTop /* clientHeight -> 可见区域:height + padding clientTop -> border高度 //标签边框的高度<br> offsetHeight -> 可见区域:height + padding + border <br> #比如div中的内容很多,出现了滚动条, 这offsetHeight 和 offsetTop 高度只能显示div 这个框的高度,但是实际滚动条中的高度要高的多。 offsetTop -> 上级定位标签的高度 //和postion 定位功能的标签的高度<br> <br> scrollHeight -> 全文高:height + padding scrollTop -> 滚动高度 特别的: document.documentElement代指文档根节点 */ |
滚动条种类:窗口滚动条,标签滚动条
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>高度</title> </head> <body> <!--窗口的滚动条--> <div style= "background-color: cadetblue; height: 2000px" ></div> //窗口滚动条 <div style= "background-color: aqua;height: 200px; overflow: auto" > //标签滚动条 <p>abs</p> <p>abs</p> <p>abs</p> <p>abs</p> <p>abs</p> // ---------->这里的内容超过了上面div定义的200px,再配合overflow:auto 所以就出现了标签的滚动条 <p>abs</p> <p>abs</p> </div> </body> </html> |
- 获取windowns滚动条
实例六:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>高度</title> <style> input{ /*定义按钮的位置在windowns的右下角*/ position: fixed; right: 20px; bottom: 20px; } .hide{ /*隐藏*/ display: none; } </style> </head> <body onscroll= "ShowButton()" > <!--因为要返回整个窗口的最上面,在body这里获取活动时间onscroll--> <!--窗口的滚动条--> <div style= "background-color: cadetblue; height: 2000px" ></div> <input id= "b" class = "hide" type= "button" value= "返回顶部" onclick= "BackTop()" /><!--点击这个按钮的时候执行BackTop函数 --> <!--把隐藏的属性先用上,默认在最上面的时候是不显示返回顶部的按钮的--> <script> function BackTop() { document.body.scrollTop = 0; } function ShowButton() { //body那里只要获取滚动事件一下就执行一下这个函数 var s = document.body.scrollTop; //获取当前距离窗口滚动的顶部 var t = document.getElementById( "b" ); //再获取一下bid的这个input标签 if (s>=100){ //如果判断滚动条距离窗口顶部已经到了100 t.classList.remove( "hide" ); //那么就要显示返回顶部按钮,就是把隐藏的功能去掉 } else { t.classList.add( "hide" ) //相反,就是添加进来 } } </script> </body> </html> |
g、提交表单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>提交表单</title> </head> <body> <form> <input type= "text" /> <a onclick= "Submit(this)" >提交</a> <!--a标签本身没有提交表单的功能,但是通过script就可以了--> </form> <script> function Submit(ths) { var par = ths.parentElement; //因为提交表单需要form这个标签,所以我们获取这个form par.submit(); //然后通过form提交 } </script> </body> </html> |
h、其他操作
1 2 3 4 5 6 7 8 9 10 | console.log 输出框 alert 弹出框 confirm 确认框<br>会弹出和alert一样的确认框,点击确认为 true ,点击取消为faule // URL和刷新 location.href 获取当前浏览器的URL location.href = "url" 重定向 , #重新给浏览器复制url<br> location.reload() 重新加载, 刷新当前页面 location.href // 获取当前阅览器的url location.href = "https://www.baidu.com" 是重新设置新的url并且跳转 |
实例七:是否删除 confirm
1 2 3 4 5 6 7 8 9 10 11 12 13 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>是否删除</title> </head> <body> <script> var ret = confirm( "是否删除" ) console.log(ret) </script> </body> </html> |
3、事件
事件:
1、this,当前触发事件的标签
2、全局事件绑定 windown.onKeyDown = function(){}
3、event 包含了事件相关内容
4、默认事件 : 基本的标签自定义事件会在默认事件前执行,唯独checkbox是默认时间先执行
实例8:事件的优先与执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <html lang= "en" > <head> <meta charset= "UTF-8" > <title>默认事件</title> </head> <body> <form action= "https://www.baidu.com" > <input id= "username" type= "text" /> <input type= "submit" value= "提交" onclick= "return SubMit();" /> <!--因为事件会有默认的优先值,在我们自定义的事件后 submit的优先值就会是自定义的优先,然后再执行默认的事件,--> <!--我们可以通过第一个值的返回值来判断是否执行默认时间,这个时候就要在onclick里面再写一个 return 的参数这个参数--> <!--是ture就执行后面的事件,如果是 false 就不执行后面时间--> </form> <script> function SubMit() { var inp = document.getElementById( "username" ); //获取username的文本内容 if (inp.value.length > 0) { //判断文本内容是否大于0,如果大于就可以提交,否则就不能提交 return true //然后这里面给return定义值 } else { alert( "用户名不能为空" ); return false } } </script> </body> </html> |
实例9:event与全局事件绑定用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>事件</title> <style> #i2{ position: fixed; background-color: rgba(0,0,0,.6); top:0; left:0; bottom: 0; right: 0; z-index: 10; } #i1{ position:relative;background-color: #D5023D; height: 2000px; z-index: 9; } .hide{ display: none; } </style> </head> <body> <div id=i1 ></div> <div id= "i2" ></div> <script> window.onkeydown = function (event) { //这个event里面保存着所有的onkeydown事件的信息 if (event.key== "Escape" ){ //其中的event.key 就是Escape 按下esc的时间 Hide(); //当触发这个时间时我执行隐藏磨砂框的功能 } }; function Hide() { var c = document.getElementById( "i2" ); c.classList.add( "hide" ); } </script> </body> </html> |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· 《HelloGitHub》第 108 期
· Windows桌面应用自动更新解决方案SharpUpdater5发布
· 我的家庭实验室服务器集群硬件清单
· Supergateway:MCP服务器的远程调试与集成工具
· C# 13 中的新增功能实操