0

0

jQuery.support 的实现方式

巴扎黑

巴扎黑

发布时间:2017-07-08 09:55:58

|

1950人浏览过

|

来源于php中文网

原创

jQuery.support


     jQuery.support 用于检查浏览器对各项特性的支持。检查项多达 27 个。
     首先,让我们用一段代码测试一下 support 中包含的检查项:

<script src='jquery.js'></script>
<script>
support = $.support;
for (key in support) {
	document.write('support.' + key + ' = ' + support[key] + '<br />');
}
</script>


    IE 下的输出结果为:

support.leadingWhitespace = false
support.tbody = false
support.htmlSerialize = false
support.style = false
support.hrefNormalized = false
support.opacity = false
support.cssFloat = false
support.checkOn = true
support.optSelected = false
support.getSetAttribute = false
support.submitBubbles = false
support.changeBubbles = false
support.focusinBubbles = true
support.deleteExpando = false
support.noCloneEvent = false
support.inlineBlockNeedsLayout = false
support.shrinkWrapBlocks = true
support.reliableMarginRight = true
support.noCloneChecked = false
support.optDisabled = true
support.radioValue = false
support.checkClone = undefined
support.appendChecked = false
support.boxModel = false
support.reliableHiddenOffsets = false
support.ajax = true
support.cors = false


    FireFox 中的显示结果为:

support.leadingWhitespace = true
support.tbody = true
support.htmlSerialize = true
support.style = true
support.hrefNormalized = true
support.opacity = true
support.cssFloat = true
support.checkOn = true
support.optSelected = true
support.getSetAttribute = true
support.submitBubbles = true
support.changeBubbles = true
support.focusinBubbles = false
support.deleteExpando = true
support.noCloneEvent = true
support.inlineBlockNeedsLayout = false
support.shrinkWrapBlocks = false
support.reliableMarginRight = true
support.noCloneChecked = true
support.optDisabled = true
support.radioValue = true
support.checkClone = undefined
support.appendChecked = true
support.boxModel = true
support.reliableHiddenOffsets = true
support.ajax = true
support.cors = true


    需要注意的是,源代码中 checkClone 的检查是有问题的。这将在后面提到。
    接下来,我们就对这些检查项进行逐一的分析。

leadingWhitespace


    检查用 innerHTML 赋值时,是否会保留前面的空白符。
    IE中为 false , Firefox 中为 true 。
    实现方式如下:

$ = function(){    
    var p = document.createElement( "p" );
    p.innerHTML = "   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
    
    var support = {
        leadingWhitespace: (p.firstChild.nodeType === 3)     
    }
    
    return { support : support }
}();


    这段代码中,先创建了一个 p ,之后就用 innerHTML 为 p 赋值,
    然后检查 p 的第一个子元素的 nodeType 是否为 3 (表示文本),
    是的话则空白字符被保留,否则未被保留。

tbody


    检查是否会自动为 table 插入 tbody ,会的话为 false ,不会则为 true 。
    IE中为 false , FireFox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...
    
    var support = {
        leadingWhitespace: (p.firstChild.nodeType === 3),
        tbody: !p.getElementsByTagName("tbody").length
    }
    
    // Other codes ...
}();


    这段代码在原有基础上为 support 增加了 tbody 属性, 并检查是否能够获取 tbody 标签。
    由于 innerHTML 中已经包含了 table 标签,能获取 tbody 标签的话则表示会自动为 table 插入 tbody,否则表示不会自动插入 tbody 。

htmlSerialize


    检查link标签能否被正确地序列化。
    IE中为 false , FireFox 中为true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    var a = p.getElementsByTagName("a")[0];

    var support = {
        // Other codes ...
        tbody: !p.getElementsByTagName( "tbody" ).length,
        htmlSerialize: !!p.getElementsByTagName("link").length
    }
    
    return { support : support }
}();


    这段代码在原有基础上为 support 增加了 htmlSerialize 属性,并检查能否获取 link 标签。
    由于 innerHTML 中已经包含了 link 标签,能通过getElementsByTagName 获取 link 标签的话,表示 link 可以被正确地序列化,否则表示 link 标签不能被正确地序列化。

style


    检查是否能通过 “style” 属性获取 DOM Element 的样式。
    IE中为 false , Firefox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    var a = p.getElementsByTagName("a")[0];

    var support = {
        // Other codes ...
        htmlSerialize: !!p.getElementsByTagName("link").length,
        style: /top/.test(a.getAttribute("style"))
    }
    
    return { support : support }
}();


    这段代码先是通过 getElementsByTagName("a") 获取表示链接的 DOM Element,
    然后在原有基础上为 support 增加了 style 属性,并检查能否通过 getAttribute("style") 获取样式。

hrefNormalized


    检查链接的 “href” 属性能否被正常地序列化。
    IE中为 false , FireFox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    var support = {
        // Other codes ...
        style: /top/.test(a.getAttribute("style")),
        hrefNormalized: (a.getAttribute("href") === "/a")
    }

    return { support : support }
}();


    这段代码通过 getAttribute("href") 来获取 “href” 属性的值,
    能正确获取则表示链接的 “href”属性能够被正确地序列化。

opacity


    检查 css 样式中的透明度设置能够被有效支持。
    IE 中为 false , FireFox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    var support = {
        // Other codes ...
        hrefNormalized: (a.getAttribute("href") === "/a"),
        opacity: /^0.55$/.test(a.style.opacity)
    }
    
    return { support : support }
}();


    这段代码通过 a.style.opacity 来获取透明度设置,并用正则表达式 /^0.55$/ 进行检查,通过检查表示 css 样式中的透明度能够被有效地支持,否则表示不能。

cssFloat


    检查 css 样式中的 float 属性能够被有效支持。
    IE 中为 false , FireFox中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    var support = {
        // Other codes ...
        opacity: /^0.55$/.test(a.style.opacity),
        cssFloat: !!a.style.cssFloat
    }
    
    return { support : support }
}();


    这段代码通过 a.style.cssFloat 来获取 float 属性,并通过 !! 将属性转为 boolean 值(undefined 将被转为 false ,而其他值将被转为 true)。

checkOn


    检查 chebox 的 value 是否为 “on”。
    IE 和 FireFox 中都为true,而 Chrome 中则为 false 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    var a = p.getElementsByTagName("a")[0],
        input = p.getElementsByTagName("input")[0];

    var support = {
        // Other codes ...
        cssFloat: !!a.style.cssFloat,
        checkOn: (input.value === "on")
    }
    
    return { support : support }
}();


    这段代码先通过 getElementsByTagName("input") 获取 checkbox,然后检查 checkbox 的 value 是否为 “on”。

optSelected


    检查 select 中的第一个 option 能否被默认选中。
    IE 中为 false , FireFox 中为 true 。

$ = function(){  
    // Other codes ...

    var select = document.createElement("select");
    var opt = select.appendChild(document.createElement("option"));

    var support = {
        // Other codes ...
        checkOn: (input.value === "on"),
        optSelected: opt.selected
    }
    
    return { support : support }
}();


    这段代码先通过 createElement("select") 创建了一个 select,
    然后将一个 “option” 添加到了 select 中。
    接着在 support 中增加了属性 optSelected,检查 opt.selected 是否为true 。

getSetAttribute


    检查能够功过 getAttribute("calssName") 和 setAttribute("className", "...") 来获取和设置 p 的 css class。
    实际上只检查 setAttribute("className", "...")。
    IE 中为 false , FireFox 中为 true 。

$ = function(){  
    // Other codes ...

    p.setAttribute("className", "t");
    
    var support = {
        // Other codes ...
        optSelected: opt.selected,
        getSetAttribute: p.className !== "t"
    }
    
    return { support : support }
}();


    这段代码通过 p.setAttribute("classsName", "t") 将 p 的 css class 设置为 “t”,然后在 support 中增加 getSetAttribute 属性, 检查 p 的 className 是否为 “t”。

submitBubbles, changeBubbles, focusinBubbles


    检查 submit、change、focus 事件是否在“冒泡阶段”触发。实际上只针对 IE 进行检查。因为大多数浏览器(及IE9)使用 addEventListener 附加事件,函数的第三个参数 useCapture (是否在“捕捉阶段”触发事件)既可以为 false ,也可以为 true 。
    而 IE (IE9之前)使用 attachEvent 函数附加事件,该函数无法指定在哪个阶段触发事件,一律都为“冒泡阶段”触发。

    关于 Bubble Event 的更多内容,可以参考 iteye 里其他相关的文章:
    http://www.iteye.com/search?query=bubble+event&type=blog

    实现方式如下:

$ = function(){    
    // Other codes ...
    
    var support = {
        // Other codes ...
        getSetAttribute: p.className !== "t",
        submitBubbles: true,
        changeBubbles: true,
        focusinBubbles: false
    }

    if (p.attachEvent) {
        for(var i in { submit: 1, change: 1, focusin: 1 }) {
            var eventName = "on" + i;
            var isSupported = (eventName in p);
            if (!isSupported) {
                p.setAttribute(eventName, "return;");
                isSupported = ( typeof p[eventName] === "function" );
            }
            support[i + "Bubbles"] = isSupported;
        }
    }

    return { support : support }
}();


    首先,在 support 中增加 submitBubbles, changeBubbles, focusinBubbles,默认值分别为 true, true, false ,这是针对大多数浏览器的。
    然后,针对IE (也就是存在 DOMElement.attachEvent 函数的情况),检查 "onXXX" 事件是否存在,以及能否通过 setAttribute(eventName, xxx)进行设置,可以的话就判断为“冒泡阶段”触发(即只要支持该事件,就判断为“冒泡阶段”触发)。

    事实上,jQuery 中的 focusin 事件是在 focus 的基础上进行模拟的,浏览器并不支持该事件,所以 focusinBubbles 总是为 false。

    从源代码中的注释来看,似乎还考虑到了跨站脚本攻击:
    Short-circuiting here helps us to avoid an eval call (in setAttribute) which can cause CSP to go haywire.

    大意是说在这里进行简短的检查(typeof p[eventName] === "function"),而不是直接用 eval 执行事件,可以避免不可控的跨站脚本攻击。(我不确定有没有翻译错)

deleteExpando


    检查是否允许删除附加在 DOM Element 上的数据。
    IE 中为 false , FireFox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...
    
    var support = {
        // Other codes ...
        focusinBubbles: false,
        deleteExpando: true
    }

    // Other codes ...

    try {
        delete p.test;
    } catch(e) {
        support.deleteExpando = false;
    }

    return { support : support }
}();


    首先在 support 中增加属性 deleteExpando ,默认值为 true 。
    然后尝试删除 p.test ,发生错误则将 deleteExpando 设为 false 。

noCloneEvent


    检查复制 DOM Element 时是否会连同 event 一起复制,会则为 false , 不会则为true 。
    IE 中为 false , FireFox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...
    
    var support = {
        // Other codes ...
        deleteExpando: true,
        noCloneEvent: true
    }

    // Other codes ...

    if (!p.addEventListener && p.attachEvent && p.fireEvent) {
        p.attachEvent( "onclick", function click() {
            support.noCloneEvent = false;
            p.detachEvent( "onclick", click );
        });
        p.cloneNode(true).fireEvent("onclick");
    }

    return { support : support }
}();


    首先在 support 中增加属性 noCloneEvent , 默认值为 true 。
    然后复制 p, 并触发其 “onclick” 事件,触发成功则为将 noCloneEvent 设为 false。
   从判断条件来看,依旧是针对 IE 的事件体系的检查。

inlineBlockNeedsLayout, shrinkWrapBlocks


    都是针对 offsetWidth 的检查。

    inlineBlockNeedsLayout 表示将原本 display 为 block 的 DOM Element 设置为 disylay: inline 时,是否与 inline 形式的 DOM Elemnt 一致( offsetWidth 为 2 )。
    IE 8 及之前的浏览器中为 true , FireFox 中为 false 。

    shrinkWrapBlocks 表示内部 DOM Element 的样式是否会影响外部 DOM Element 的样式。
    IE 6 中为 true , 多数浏览器中为 false 。
   实现方式如下:

$ = function(){    
    // Other codes ...
    
    var support = {
        // Other codes ...
        noCloneEvent: true,
        inlineBlockNeedsLayout: false,
        shrinkWrapBlocks: false
    }

    // Other codes ...

    if ( "zoom" in p.style ) {
        p.style.display = "inline";
        p.style.zoom = 1;
        support.inlineBlockNeedsLayout = ( p.offsetWidth === 2 );

        p.style.display = "";
        p.innerHTML = "<p style='width:4px;'></p>";
        support.shrinkWrapBlocks = ( p.offsetWidth !== 2 );
    }

    return { support : support }
}();


    首先在 support 中增加这两个属性,然后将 p 的 css 样式中的 display 设为 inline ,并检查 offsetWidth ,以确定 inlineBlockNeedsLayout 的值。
    接着在 p 内部增加一个 p, 并将其宽度设为 4px ,并检查 offsetWith 的值,以确定外部的 p 是否会受到影响而收缩。

reliableMarginRight


    检查 Margin Right 的计算是否可靠。 各浏览器中都为 true 。
    原注释中提到某些老版本的 Webkit 内核的浏览器中为 false 。
    实现方式如下:

$ = function(){    
    // Other codes ...
    
    var support = {
        // Other codes ...
        shrinkWrapBlocks: false,
        reliableMarginRight: true
    }

    // Other codes ...

    if ( document.defaultView && document.defaultView.getComputedStyle ) {
        var marginp = document.createElement( "p" );
        marginp.style.width = "0";
        marginp.style.marginRight = "0";
        p.appendChild( marginp );
        support.reliableMarginRight =
            ( parseInt( document.defaultView.getComputedStyle( marginp, null ).marginRight, 10 ) || 0 ) === 0;
    }

    return { support : support }
}();


    简单地说,就是将 width 和 marginRight 设为 0 时,获取的 marginRignt 应为 0 。

noCloneChecked


    检查复制 checkbox 时是否连选中状态也一同复制,若复制则为 false ,否则为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    input.checked = true;
    support.noCloneChecked = input.cloneNode(true).checked;
    
    return { support : support }
}();


    这段代码将 input 的选中状态设为 true ,然后复制 input ,并检查 checked 是否为 true 。

optDisabled


    已经被设为 disable 的 select ,其内部的 option 的 disable 不应为 true 。
    这个名称有一定的误导性,可能称为 “optNotDisabled” 更合适一些。
    在各浏览器上的值都为 true 。
    根据原注释,某些老版本的 Webkit 内核的浏览器上,该值为 false 。
    实现如下:

$ = function(){    
    // Other codes ...

    select.disabled = true;
    support.optDisabled = !opt.disabled;
    
    return { support : support }
}();


    这段代码先将 select 的状态设为 disable , 然后检查其中的 option 的 disable 的值。

radioValue


    检查 input 元素被设为 radio 类型后是否仍然保持原来的值。
    IE 中为 false , FireFox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    input = document.createElement("input");
    input.value = "t";
    input.setAttribute("type", "radio");
    support.radioValue = input.value === "t";

    return { support : support }
}();


    这段代码先创建了 input 元素,将 value 设为 “t” ,然后将其类型设置为 “radio”,最后检查器 input 原来的值是否仍然保留。

checkClone


    检查 fragment 中的 checkbox 的选中状态是否能被复制,IE 中为 false ,FireFox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    p.innerHTML = "";

    input.setAttribute("checked", "checked");
    p.appendChild( input );
    fragment = document.createDocumentFragment();
    fragment.appendChild( p.firstChild );

    support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;

    return { support : support }
}();


    这段代码创建了一个 fragment ,并将一个处于选中状态的 checkbox 加入,连续复制两遍后检查 checkbox 是否为选中状态。(这里源代码中的实现似乎有问题,没有将 p 的 innerHTML 清空,导致 p 的 firstChild 并非 checkbox,使得 checkclone 在各浏览器中的值均为 undefined 。)

appendChecked


    检查被添加到 DOM 中的 checkbox 是否仍然保留原来的选中状态。
    IE 中为 false ,FireFox 中为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    support.appendChecked = input.checked;

    return { support : support }
}();


    实际上只是简单地检查之前添加到 fragment 中的 checkbox 的选中状态。

boxModel


    检查页面渲染是否符合 W3C Box Model 。
    在 IE 中没有 DocType 声明时为 false ,其余情况为 true 。
    实现方式如下:

$ = function(){    
    // Other codes ...

    p.innerHTML = "";
    p.style.width = p.style.paddingLeft = "1px";

    body = document.createElement( "body" );
    bodyStyle = {
        visibility: "hidden",
        width: 0,
        height: 0,
        border: 0,
        margin: 0,
        background: "none"
    };
    for (var i in bodyStyle ) {
        body.style[i] = bodyStyle[i];
    }
    body.appendChild(p);
    document.documentElement.appendChild(body);

    support.boxModel = p.offsetWidth === 2;

    body.innerHTML = "";
    document.documentElement.removeChild( body );

    return { support : support }
}();


    将 p 的 width 和 paddingLeft 设为 1px ,然后将它添加到 body 中,检查 p 的 offsetWidth 是否为 2 。

reliableHiddenOffsets


    检查 hidden 状态下的  offsetWidth 和 offsetHeight 是否正确。
    IE 中为 false , FireFox 中为 true 。
    实现方式如下(插入到清空 body.innerHTML 之前):

$ = function(){    
    // Other codes ...
    
    p.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
    var tds = p.getElementsByTagName("td");

    var isSupported = (tds[0].offsetHeight === 0);

    tds[0].style.display = "";
    tds[1].style.display = "none";

    support.reliableHiddenOffsets = isSupported && (tds[0].offsetHeight === 0);

    body.innerHTML = "";
    document.documentElement.removeChild( body );
    
    return { support : support }
}();


    检查将一个 td 隐藏时, 相邻的 td 的 offsetHeight 是否为 0 。

ajax, cors


    检查是否支持 ajax 请求,以及是否支持跨域 ajax。
    IE 与 FireFox 中 ajax 均为 true ,IE 中 cors 为 false , FireFox 中 cros 为 false 。
   实现方式如下:

$ = function(){    
    // Other codes ...

    var xhr = window.ActiveXObject ?
        !this.isLocal && createStandardXHR() || createActiveXHR() :
        createStandardXHR();

    support.ajax = !!xhr;
    support.cors = !!xhr && ( "withCredentials" in xhr )
   
    function createStandardXHR() {
        try {
            return new window.XMLHttpRequest();
        } catch( e ) {}
    }

    function createActiveXHR() {
        try {
            return new window.ActiveXObject("Microsoft.XMLHTTP");
        } catch( e ) {}
    }

 return { support : support }
}();


    尝试创建 ajax 对象,创建成功,则 ajax 为 true ;如果 ajax 对象中包含了 "withCredentials" 属性,则表示支持跨域 ajax。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

797

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

272

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

144

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

25

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

92

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

53

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

15

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

717

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

64

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 5.9万人学习

PostgreSQL 教程
PostgreSQL 教程

共48课时 | 9.5万人学习

Git 教程
Git 教程

共21课时 | 3.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号