• JavaScript Event


    基础知识

       在文档、浏览器、标签元素等元素在特定状态下触发的行为即为事件,比如用户的单击行为、表单内容的改变行为即为事件,我们可以为不同的事件定义处理程序。Js使用异步事件驱动的形式管理事件。

    事件类型

      Js为不同的事件定义的类型,也可以称为事件名称。

    事件目标

      事件目标指产生事件的对象,比如<a>标签被点击那么<a>标签就是事件目标。元素是可以嵌套的,所以在进行一次点击行为时可能会触发多个事件目标。

    处理程序

      事件的目的是要执行一段代码,我们称这类代码为事件处理(监听)程序。当在对象上触发事件时就会执行定义的事件处理程序。

    HTML绑定

      可以在html元素上设置事件处理程序,浏览器解析后会绑定到DOM属性中

    <button onclick="alert('弹窗')">点我出现弹窗</button>
    

      往往事件处理程序业务比较复杂,所以绑定方法或函数会有常见

      绑定函数或方法时需要加上括号

    <body>
            <button onclick="show()">点我出现弹窗</button>
    </body>
    <script>
    
            function show() {
                    alert("弹窗");
            }
    
    </script>
    

      绑定类的静态方法

    <body>
            <button onclick="User.show()">点我出现弹窗</button>
    </body>
    <script>
    
            class User{
                    static show(){
                            alert("弹窗");
                    }
            }
    
    </script>
    

      在绑定事件的HTML的元素上,可以进行参数的传递

      this:即事件目标本身

      event:事件对象

    <body>
            <button onclick="show(this,event)">点我</button>
    </body>
    <script>
    
            function show(self,event){
                    console.log(self);   // <button onclick="show(this,event)">点我</button>
                    console.log(event);  // 包含很多信息
            }
    
    </script>
    

    DOM绑定

       也可以将事件处理程序绑定到DOM属性中

      使用setAttribute方法设置事件处理程序无效

      属性名区分大小写

       使用普通函数进行DOM绑定时,this即为事件目标本身

    <body>
            <button>点我</button>
    </body>
    <script>
    
            undefined
    
            let button = document.querySelector("button");
            button.onclick = function () {
                    console.log(this)  // <button>点我</button> 
            }
    
    
    </script>
    

       在使用DOM绑定时不推荐使用箭头函数,因为这会使this的指向为windowundefined,但是我们可以通过事件对象提取出事件目标本身

    <body>
            <button>点我</button>
    </body>
    <script>
    
            let button = document.querySelector("button");
            button.onclick = (event)=>{
                    console.log(event.target)  // <button>点我</button> 
            }
    
    
    </script>

       使用DOM绑定时不允许对同一事件进行多次处理,只会依照最后的处理程序为准

    <body>
            <button>点我</button>
    </body>
    <script>
    
            let button = document.querySelector("button");
            button.onclick = function () {
                    console.log(this);  // 不打印
            }
    
            button.onclick = function () {
                    alert("弹窗");  // 打印
            }
    
    </script>
    

    事件监听

      使用HTMLDOM绑定都有缺陷,建议使用新的事件监听绑定方式

    方法说明
    addEventListener 添加事件处理程序
    removeEventListener 移除事件处理程序

      addEventListener

      使用addEventListener添加事件处理程序

      transtionend / DOMContentLoaded 等事件类型只能使用 addEventListener处理

      同一事件类型设置多个事件处理程序,按设置的顺序先后执行

      也可以对未来添加的元素绑定事件

      参数说明如下

    参数说明
    参数1 事件类型
    参数2 事件处理程序
    参数3 定制选项

      参数3的定制项

       once:true :只执行一次事件

       capture:true/false :捕获阶段传播到该 EventTarget 时触发

       passive:truelistener 永远不会调用 preventDefault()

      使用addEventListener可对同一事件进行多次监听

    <body>
            <button>点我</button>
    </body>
    <script>
    
            let button = document.querySelector("button");
            button.addEventListener("click", function () {
                    console.log(this);  // 打印
            })
    
            button.addEventListener("click",function () {
                    alert("弹窗");  // 打印
            }) 
    
    </script>
    

      对象绑定

      如果事件处理程序可以是对象,对象的 handleEvent方法会做为事件处理程序执行。下面将元素的事件统一交由对象处理

    <body>
            <div style=" 300px;height: 300px;background: red;"></div>
    </body>
    <script>
    
            class DivEvent {
    
                    handleEvent(e){
                            this[e.type](e.target);  // e是事件对象
                    }
    
                    click(self) {
                            console.log("鼠标点击事件",self);  // self即为事件目标 div标签
                    }
                    mouseover(self) {
                            console.log("鼠标移动事件",self);
                    }
            }
    
            let div = document.querySelector("div");
            let divEvent = new DivEvent();
    
            div.addEventListener("click",divEvent);
            div.addEventListener("mouseover",divEvent);
    
    </script>
    

      removeEventListener

      使用removeEventListener删除绑定的事件处理程序

      事件处理程序单独定义函数或方法,这可以保证事件处理程序是同一个

      以下示例中,每次点击<div>都会令其内容+1,当点击删除事件按钮后点击无效。

    <body>
            <div style=" 100px;height: 100px;background: red;color: #fff;text-align: center;line-height: 100px;">
            </div>
            <button>删除事件</button>
    </body>
    <script>
    
            function add(event) {
                    if (!event.target.innerText) {
                            event.target.innerText = 1;
                    } else {
                            event.target.innerText++;
                    }
    
    
            }
    
            document.querySelector("div").addEventListener("click", add);
    
            document.querySelector("button").addEventListener("click", (event) => {
                    document.querySelector("div").removeEventListener("click",add)
            });
    
    </script>
    

    注意事项

       通过HTMLDOM进行事件处理程序的绑定,需要在事件名前加入on,比如click就变为onclickcopy变为oncopy

       而使用事件监听的方式则不需要加上on,这与jQuery的做法如出一辙。

    事件对象

      执行事件处理程序时,会产生当前事件相关信息的对象,即为事件对象。

      系统会自动做为参数传递给事件处理程序。

      大部分浏览器将事件对象保存到window.event中

      有些浏览器会将事件对象做为事件处理程序的参数传递

      事件对象常用属性如下:

    属性说明
    type 事件类型
    target 事件目标对象,冒泡的父级通过该属性可以找到在哪个元素上执行了事件
    currentTarget 当前执行事件的对象
    timeStamp 事件发生时间
    x 相对窗口的X坐标
    y 相对窗口的Y坐标
    clientX 相对窗口的X坐标
    clientY 相对窗口的Y坐标
    screenX 相对计算机屏幕的X坐标
    screenY 相对计算机屏幕的Y坐标
    pageX 相对于文档的X坐标
    pageY 相对于文档的Y坐标
    offsetX 相对于事件对象的X坐标
    offsetY 相对于事件对象的Y坐标
    layerX 相对于父级定位的X坐标
    layerY 相对于父级定位的Y坐标
    path 冒泡的路径
    altKey 是否按了alt键
    shiftKey 是否按了shift键
    metaKey 是否按了媒体键
    window.pageXOffset 文档参考窗口水平滚动的距离
    window.pageYOffset 文档参考窗口垂直滚动的距离

    冒泡捕获

    冒泡行为

      标签元素是嵌套的,在一个元素上触发的事件,同时也会向上执行父级元素的事件处理程序,一直到HTML标签元素。

      大部分事件都会冒泡,但像focus事件则不会

      event.target 可以在事件中(包括父级元素中)得到事件目标元素即最底层的产生事件的对象

      event.currentTarget == this 即当前执行事件的对象

      以下示例有标签的嵌套,并且父子标签都设置了事件,当在子标签上触发事件事会冒泡执行父级标签的事件

      简而言之,如果嵌套的标签设置有相同的事件,会按照最外层的标签处理程序为准。

      

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
                    body {
                             100vw;
                            height: 100vh;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                    }
    
                    section {
                             200px;
                            height: 200px;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            background: blueviolet;
                    }
    
                    article {
                             100px;
                            height: 100px;
                            background: deeppink;
                    }
            </style>
    
    </head>
    
    <body>
            <section>
                    <article></article>
            </section>
    </body>
    <script>
    
            document.body.addEventListener("click", (evnet) => {
                    event.target.style.background = "green";  // 爷爷body是绿色
            });
    
            document.querySelector("section").addEventListener("click", (event) => {
                    event.target.style.background = "red";  // 爸爸section是红色
            });
    
            document.querySelector("article").addEventListener("click", (event) => {
                    event.target.style.background = "deepskyblue";  // 孙子deepskyblue是蓝色
    
            });
    
    
    </script>
    
    </html>
    

    阻止冒泡

      冒泡过程中的任何事件处理程序中,都可以执行 event.stopPropagation() 方法阻止继续进行冒泡传递

      event.stopPropagation() 用于阻止冒泡,如果同一类型事件绑定多个事件处理程序event.stopPropagation() 只阻止当前的事件处理程序

      event.stopImmediatePropagation()阻止事件冒泡并且阻止相同事件的其他事件处理程序被调用,也就是说不仅仅是当前的事件处理程序不会冒泡了,所有关于该标签的同类型事件处理程序都会阻止冒泡的发生。

      所以推荐使用event.stopImmediatePropagation()

      为上图添加阻止冒泡

      

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
                    body {
                             100vw;
                            height: 100vh;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                    }
    
                    section {
                             200px;
                            height: 200px;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            background: blueviolet;
                    }
    
                    article {
                             100px;
                            height: 100px;
                            background: deeppink;
                    }
            </style>
    
    </head>
    
    <body>
            <section>
                    <article></article>
            </section>
    </body>
    <script>
    
            document.body.addEventListener("click", (evnet) => {
                    event.target.style.background = "green";  // 爷爷body是绿色
            });
    
            document.querySelector("section").addEventListener("click", (event) => {
                    event.stopImmediatePropagation();
                    event.target.style.background = "red";  // 爸爸section是红色
            });
    
            document.querySelector("article").addEventListener("click", (event) => {
                    event.stopImmediatePropagation();
                    event.target.style.background = "deepskyblue";  // 孙子deepskyblue是蓝色
    
            });
    
    
    </script>
    
    </html>
    

    事件捕获

      事件执行顺序为 捕获 > 事件目标 > 冒泡阶段执行,在向下传递到目标对象的过程即为事件捕获。事件捕获在实际使用中频率不高。

      通过设置第三个参数为true{ capture: true }在捕获阶段执行事件处理程序

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
                    body {
                             100vw;
                            height: 100vh;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                    }
    
                    section {
                             200px;
                            height: 200px;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            background: blueviolet;
                    }
    
                    article {
                             100px;
                            height: 100px;
                            background: deeppink;
                    }
            </style>
    
    </head>
    
    <body>
            <section>
                    <article></article>
            </section>
    </body>
    <script>
    
            document.querySelector("section").addEventListener("click", (event) => {
                    event.target.style.background = "red";  // 爸爸section是红色
            },{ capture: true }); // 捕获阶段执行事件处理程序,这与阻止冒泡并无关系
    
            document.querySelector("article").addEventListener("click", (event) => {
                    event.target.style.background = "deepskyblue";  // 孙子deepskyblue是蓝色
    
            },{ capture: true }); // 捕获阶段执行事件处理程序,这与阻止冒泡并无关系
    
    </script>
    
    </html>
    

    事件代理

      借助冒泡思路,我们可以不为子元素设置事件,而将事件设置在父级。然后通过父级事件对象的event.target查找子元素,并对他做出处理。

      在jQuery中对事件代理的操作极其简单,但是在原生的JavaScript中还是有一些复杂的。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
                    .show{
                            list-style: none;
                            border: 1px solid #ddd;
                            margin:20px;
                    }
            </style>
    </head>
    
    <body>
            <ul>
                    <li>1</li>
                    <li>2</li>
                    <li>3</li>
            </ul>
    </body>
    <script>
        document.querySelector("ul").addEventListener("click", () => {
                    if(event.target.tagName === "LI"){  // 如果事件目标是li,则添加样式
                            event.target.classList.toggle("show");
                    }
            })
    </script>
    
    </html>
    

      我们可以将事件代理做成jQuery那样

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
                    section {
                             200px;
                            padding: 10px;
    
    
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            flex-flow: column;
    
                            outline: 1px solid #ddd;
                    }
    
                    div {
                            display: flex;
                            justify-content: center;
                            align-items: center;
                             180px;
                            margin: 10px;
                            background-color: darkviolet;
                            color: #fff;
                    }
            </style>
    
    </head>
    
    <body>
            <section>
                    <div>1</div>
                    <div>2</div>
                    <div>3</div>
            </section>
    </body>
    
    <script>
            function EventProxy(ProxyElement, ElementType, EventElement, func) {
                    ProxyElement.addEventListener(ElementType, () => {
                            if (event.target.tagName == EventElement.toUpperCase()) {
                                    func(event);
                            }
                    });
            }
    
            let section = document.querySelector("section");
    
    
            // 委托的DOM  事件类型  事件目标  ele即为事件对象
            EventProxy(section, "click", "div", (ele) => {
                    ele.target.style.backgroundColor = "red";
            });
    
    
    </script>
    
    
    </html>
    

      模拟出jQuery的事件代理

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
                    section {
                             200px;
                            padding: 10px;
    
    
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            flex-flow: column;
    
                            outline: 1px solid #ddd;
                    }
    
                    div {
                            display: flex;
                            justify-content: center;
                            align-items: center;
                             180px;
                            margin: 10px;
                            background-color: darkviolet;
                            color: #fff;
                    }
            </style>
    
    </head>
    
    <body>
            <section>
                    <div>1</div>
                    <div>2</div>
                    <div>3</div>
            </section>
    </body>
    
    <script>
            Element.prototype.on = function (ElementType, EventElement, func) {
                    this.addEventListener(ElementType, () => {
                            if (event.target.tagName == EventElement.toUpperCase()) {
                                    func(event);
                            }
                    });
            }
    
            let section = document.querySelector("section");
    
            // 委托的DOM  事件类型  事件目标  ele即为事件对象
            section.on("click", "div", (ele) => {
                    ele.target.style.backgroundColor = "red";
            });
    
    </script>
    
    
    </html>
    

    未来元素

       未来元素是指后期通过Js新增的元素,对于这些元素而言我们可以通过父级事件代理的形式让它们也能进行一些事件的处理,而不用一个一个再去进行事件绑定。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
                    section {
                             200px;
                            padding: 10px;
    
    
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            flex-flow: column;
    
                            outline: 1px solid #ddd;
                    }
    
                    div {
                            display: flex;
                            justify-content: center;
                            align-items: center;
                             180px;
                            margin: 10px;
                            background-color: darkviolet;
                            color: #fff;
                    }
            </style>
    
    </head>
    
    <body>
            <section>
    
            </section>
    </body>
    
    <script>
            Element.prototype.on = function (ElementType, EventElement, func) {
                    this.addEventListener(ElementType, () => {
                            if (event.target.tagName == EventElement.toUpperCase()) {
                                    func(event);
                            }
                    });
            }
    
            let section = document.querySelector("section");
            let div = document.createElement("div");  // 未来元素,不用绑定任何事件,其父级元素进行事件代理即可
            div.innerText = "newElement";
            section.append(div);  // 添加未来元素
    
            // 委托的DOM  事件类型  事件目标  ele即为事件对象
            section.on("click", "div", (ele) => {
                    ele.target.style.backgroundColor = "red";
            });
    
    </script>
    
    
    </html>
    

    默认行为

       Js会有些对象会设置默认事件处理程序,比如<a>链接在点击时会进行跳转。一般默认处理程序会在用户定义的处理程序后执行,所以我们可以在我们定义的事件处理程序员取消默认事件处理程序的执行。

      使用onclick绑定的事件处理程序,return false可以阻止默认行为

      推荐使用event.preventDefault()阻止默认行为

       以下示例将展示使用event.preventDefault()阻止submit的默认行为。

    <body>
            <form action="http://www.google.com">
                    <p><input type="text" name="username"></p>
                    <button type="submit">提交</button>
            </form>
    </body>
    
    <script>
    
            let button = document.querySelector("button");
            button.addEventListener("click",(event)=>{
                    event.preventDefault();
                    console.log("已阻止默认行为...");
            })
    
    </script>
    

    窗口文档

    事件类型

    事件名说明
    window.onload 文档解析及外部资源加载后
    DOMContentLoaded 文档解析后不需要外部资源加载,只能使用addEventListener设置
    window.beforeunload 文档刷新或关闭时
    window.unload 文档卸载时
    scroll 页面滚动时

    实例操作

      window.onload事件在文档解析后及图片、外部样式文件等资源加载完后执行。

      推荐代码书写至该事件处理函数中

    <script>
            window.onload = () => {
                    console.log("逻辑代码...")
            }
    </script>
    
    

      DOMContentLoaded事件在文档标签解析后执行,不需要等外部图片、样式文件、Js文件等资源加载

      需要等待前面引入的CSS样式文件加载解析后才执行

      只能使用addEventListener设置

    <script>
            window.addEventListener("DOMContentLoaded", () => {
                    console.log("逻辑代码...")
            });
    </script>
    

      当浏览器窗口关闭或者刷新时,会触发beforeunload事件,可以取消关闭或刷新页面。

      返回值为非空字符串时,有些浏览器会做为弹出的提示信息内容

      部分浏览器使用addEventListener无法绑定事件

    <script>
            window.onbeforeunload = function(e){
                    return "不要走,10000元宝等您领取!"
            };
    </script>
    

      window.unload事件在文档资源被卸载时执行,在beforeunload后执行

      不能执行alertconfirm等交互指令

      发生错误也不会阻止页面关闭或刷新

    <script>
            window.addEventListener('unload', function (e) {
                    localStorage.setItem('name', 'yunya');
            });
    </script>
    

    鼠标事件

    事件类型

      针对鼠标操作的行为有多种事件类型

      鼠标事件会触发在Z-INDEX最高的那个元素上

    事件名说明
    click 鼠标单击事件,同时触发 mousedown/mouseup
    dblclick 鼠标双击事件
    contextmenu 点击右键后显示的所在环境的菜单
    mousedown 鼠标按下(长按)
    mouseup 鼠标抬起时
    mousemove 鼠标移动时
    mouseover 鼠标移动时
    mouseout 鼠标从元素上离开时
    mouseup 鼠标抬起时
    mouseenter 鼠标移入时触发,不产生冒泡行为
    mouseleave 鼠标移出时触发,不产生冒泡行为
    copy 复制内容时触发
      某一段文本被选中时触发
    scroll 元素滚动时,可以为元素设置overflow:auto; 产生滚动条来测试

    事件对象

       鼠标事件产生的事件对象包含相对应的属性

    属性说明
    which 执行mousedown/mouseup时,显示所按的键 1左键,2中键,3右键
    clientX 相对窗口X坐标
    clientY 相对窗口Y坐标
    pageX 相对于文档的X坐标
    pageY 相对于文档的Y坐标
    offsetX 目标元素内部的X坐标
    offsetY 目标元素内部的Y坐标
    altKey 是否按了alt键
    ctrlKey 是否按了ctlr键
    shiftKey 是否按了shift键
    metaKey 是否按了媒体键
    relatedTarget mouseover事件时从哪个元素来的,mouseout事件时指要移动到的元素。当无来源(在自身上移动)或移动到窗口外时值为null

    实例操作

       禁止复制内容

    <body>
            abcdefg
    </body>
    
    <script>
    
            document.body.addEventListener("copy", event => {
                    event.preventDefault();
                    alert("禁止复制");
            });
    
    </script>
    

      relatedTarget是控制鼠标移动事件的来源和目标对象的

      如果移动过快会跳转中间对象

    <body>
    
            <ul>
                    <li>1</li>
                    <li>2</li>
                    <li>3</li>
                    <li>4</li>
                    <li>5</li>
            </ul>
    </body>
    <script>
    
            document.body.addEventListener("mouseout", () => {
                    console.log(event.target);  // 显示鼠标在哪个位置上
                    console.log(event.relatedTarget);   
            });
    
    </script>
    

      mouseentermouseleave不会产生冒泡,即子元素和父元素(当前事件对象)来回移动时不产生事件

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <style>
                    body {
                            display: flex;
                            justify-content: center;
                            align-items: center;
                             100vw;
                            height: 100vh;
                    }
    
                    section {
                            display: flex;
                            justify-content: center;
                            align-items: center;
                             200px;
                            height: 200px;
                            border: 1px solid #ddd;
                    }
    
                    article {
                             100px;
                            height: 100px;
                            background-color: darkviolet;
                    }
            </style>
    </head>
    
    <body>
            <section>
                    <article></article>
            </section>
    </body>
    <script>
    
            document.querySelector("section").addEventListener("mouseenter", (event) => {
                    event.target.style.backgroundColor = "red";
            });
    
            document.querySelector("article").addEventListener("mouseenter", (event) => {
                    event.target.style.backgroundColor = "yellow";
            });
    
    
    </script>
    
    </html>
    

    键盘事件

    事件类型

       针对键盘输入操作的行为有多种事件类型

    事件名说明
    Keydown 键盘按下时,一直按键不松开时keydown事件会重复触发
    keypress 某个键盘按键被按下并且松开
    keyup 按键抬起时

    事件对象

       键盘事件产生的事件对象包含相对应的属性

    属性说明
    keyCode 返回键盘的ASCII字符数字
    code 按键码,字符以Key开始,数字以Digit开始,特殊字符有专属名子。左右ALT键字符不同。 不同布局的键盘值会不同
    key 按键的字符含义表示,大小写不同。不能区分左右ALT等。不同语言操作系统下值会不同
    altKey 是否按了alt键
    ctrlKey 是否按了ctlr键
    shiftKey 是否按了shift键
    metaKey 是否按了媒体键

    表单事件

       对于表单元素来说,可有以下事件提供处理

    事件类型说明
    focus 获取焦点事件
    blur 失去焦点事件
    element.focus() 让元素强制获取焦点
    element.blur() 让元素失去焦点
    change 文本框在内容发生改变并失去焦点时触发,select/checkbox/radio选项改变时触发事件
    input 内容改变时触发,包括粘贴内容或语音输入内容都会触发事件
    submit 确认按钮被点击,提交表单时触发
  • 相关阅读:
    重装系统后texstudio拼写检查不工作
    git bash使用端口转发连接服务器
    YCSB-mapkeeper
    编译thrift外篇-关于默认链接包-(使用mapkeeper运行leveldb成功)
    编译Thrift
    Could not resolve view with name 'sys/login' in servlet with name 'dispatcher'
    Eclipse创建一个Maven Web项目
    Maven安装配置
    使用Maven创建Web应用程序项目
    org.apache.jasper.JasperException: Unable to compile class for JSP:
  • 原文地址:https://www.cnblogs.com/Yunya-Cnblogs/p/13531826.html
Copyright © 2020-2023  润新知