首页 > web前端 > js教程 > 正文

JavaScript模板引擎的实现原理

狼影
发布: 2025-09-20 19:04:01
原创
858人浏览过
&lt;blockquote&gt;JavaScript模板引擎的核心原理是将含标记的字符串转换为可接收数据并生成HTML的函数。它通过正则解析模板中的占位符与逻辑语句,生成拼接HTML的函数体,利用new Function()创建渲染函数,实现数据与视图的高效结合,提升开发效率与代码可维护性,同时需关注编译缓存、执行性能及XSS防护等安全问题。&lt;/blockquote&gt; &lt;p&gt;&lt;img src=&quot;https://img.php.cn/upload/article/001/253/068/175836624265190.jpg&quot; alt=&quot;javascript模板引擎的实现原理&quot;&gt;&lt;/p&gt; &lt;p&gt;JavaScript模板引擎的核心原理,在我看来,其实就是把一段带有特定语法标记的字符串(也就是我们写的模板),通过一套解析机制,转换成一个能够接收数据并最终吐出渲染好的HTML字符串的JavaScript函数。这个过程,有点像编译器,把我们易读的模板代码“编译”成了机器(这里是JS运行时)能直接执行的、高效生成DOM片段的代码。它不是直接操作DOM,而是先生成一个字符串,再由&lt;a style=&quot;color:#f60; text-decoration:underline;&quot; title=&quot;浏览器&quot; href=&quot;https://www.php.cn/zt/16180.html&quot; target=&quot;_blank&quot;&gt;浏览器&lt;/a&gt;解析成DOM。&lt;/p&gt; &lt;h3&gt;解决方案&lt;/h3&gt; &lt;p&gt;要深入理解JavaScript模板引擎,我们得从它“化腐朽为神奇”的几个关键步骤入手。想象一下,你写了一段HTML,里面夹杂着&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;<%= name %>&lt;/pre&gt;
登录后复制
&lt;/div&gt;这样的占位符,或者&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;<% if (user) { %>&lt;/pre&gt;
登录后复制
&lt;/div&gt;这样的逻辑控制。引擎拿到这段字符串后,第一件事就是解析。它会用一些预设的规则(通常是&lt;a style=&quot;color:#f60; text-decoration:underline;&quot; title=&quot;正则表达式&quot; href=&quot;https://www.php.cn/zt/15947.html&quot; target=&quot;_blank&quot;&gt;正则表达式&lt;/a&gt;)去识别这些特殊的标记。&lt;/p&gt; &lt;p&gt;比如,它会把纯HTML部分原封不动地保留下来,而遇到&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;<%= ... %>&lt;/pre&gt;
登录后复制
&lt;/div&gt;这种数据输出的标记,它就知道这里需要把某个变量的值插入进来。遇到&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;<% ... %>&lt;/pre&gt;
登录后复制
&lt;/div&gt;这种逻辑控制标记,比如&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;if&lt;/pre&gt;
登录后复制
&lt;/div&gt;语句或者&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;for&lt;/pre&gt;
登录后复制
&lt;/div&gt;循环,它就明白这里要执行一段JavaScript代码。&lt;/p&gt; &lt;p&gt;解析完成后,最关键的一步来了:代码生成。引擎会把这些解析出来的片段,巧妙地拼接成一个全新的JavaScript函数体。这个函数体内部,会维护一个用于收集最终HTML字符串的变量(可能是数组,然后&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;join('')&lt;/pre&gt;
登录后复制
&lt;/div&gt;,或者直接字符串拼接)。纯HTML部分会作为字符串字面量直接加入到这个变量中;数据输出部分会被转换成类似&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;_output.push(data.name)&lt;/pre&gt;
登录后复制
&lt;/div&gt;这样的语句;而逻辑控制部分,则会原封不动地嵌入为真正的JavaScript控制流语句。&lt;/p&gt; &lt;p&gt;&lt;span&gt;立即学习&lt;/span&gt;“&lt;a href=&quot;https://pan.quark.cn/s/c1c2c2ed740f&quot; style=&quot;text-decoration: underline !important; color: blue; font-weight: bolder;&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Java免费学习笔记(深入)&lt;/a&gt;”;&lt;/p&gt; &lt;p&gt;最后,这个动态生成的JavaScript函数,通常会通过&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;new Function()&lt;/pre&gt;
登录后复制
&lt;/div&gt;构造函数来创建。当我们调用这个函数,并把需要渲染的数据对象作为参数传进去时,它就会执行内部的逻辑,把数据和模板结合起来,最终返回那个我们期待已久的、完整的HTML字符串。这就是模板引擎的“黑箱”操作,一个从文本到可执行代码,再到最终文本的循环。&lt;/p&gt; &lt;h3&gt;为什么我们需要模板引擎,以及它解决了哪些痛点?&lt;/h3&gt; &lt;p&gt;在我刚开始接触&lt;a style=&quot;color:#f60; text-decoration:underline;&quot; title=&quot;前端&quot; href=&quot;https://www.php.cn/zt/15813.html&quot; target=&quot;_blank&quot;&gt;前端&lt;/a&gt;开发的时候,手动拼接HTML字符串简直是噩梦。尤其是在处理动态列表或者复杂的条件渲染时,代码里充斥着大量的&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;+&lt;/pre&gt;
登录后复制
&lt;/div&gt;号和引号,不仅写起来费劲,维护起来更是痛苦不堪。一旦逻辑稍微复杂一点,比如一个循环里套着一个条件判断,那代码的可读性就直线下降,调试起来也让人头大。&lt;/p&gt; &lt;p&gt;模板引擎的出现,完美地解决了这些痛点。首先,它让&lt;strong&gt;视图层和数据逻辑分离&lt;/strong&gt;变得更清晰。我们可以在&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;.html&lt;/pre&gt;
登录后复制
&lt;/div&gt;或者&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;.tpl&lt;/pre&gt;
登录后复制
&lt;/div&gt;文件里专注于页面的结构和展示,而把数据的获取和处理放在JavaScript里。这大大提升了代码的可读性和可维护性。其次,它&lt;strong&gt;简化了复杂UI的构建&lt;/strong&gt;。通过模板里的循环和条件语句,我们可以用更声明式的方式来描述UI,而不用去写一堆命令式的DOM操作代码。这不仅提高了开发效率,也减少了出错的概率。&lt;/p&gt; &lt;p&gt;再者,对于一些支持&lt;strong&gt;前&lt;a style=&quot;color:#f60; text-decoration:underline;&quot; title=&quot;后端&quot; href=&quot;https://www.php.cn/zt/17190.html&quot; target=&quot;_blank&quot;&gt;后端&lt;/a&gt;同构(SSR)&lt;/strong&gt;的框架来说,模板引擎是核心。同一套模板代码既可以在服务器端预渲染生成HTML,也可以在客户端进行数据绑定和更新,这对于首屏加载速度和SEO都非常有益。最后,虽然不是所有引擎都默认提供,但很多现代模板引擎会内置&lt;strong&gt;XSS(跨站脚本攻击)防护&lt;/strong&gt;机制,自动对输出内容进行转义,这无疑提升了应用的安全性。在我看来,模板引擎不仅仅是一个&lt;a style=&quot;color:#f60; text-decoration:underline;&quot; title=&quot;工具&quot; href=&quot;https://www.php.cn/zt/16887.html&quot; target=&quot;_blank&quot;&gt;工具&lt;/a&gt;,它更是一种编程范式的转变,让&lt;a style=&quot;color:#f60; text-decoration:underline;&quot; title=&quot;前端开发&quot; href=&quot;https://www.php.cn/zt/17277.html&quot; target=&quot;_blank&quot;&gt;前端开发&lt;/a&gt;变得更加优雅和高效。&lt;/p&gt; &lt;h3&gt;核心实现机制:从字符串到可执行函数&lt;/h3&gt; &lt;p&gt;要理解模板引擎的“魔法”,我们得深入到它如何将模板字符串转化为可执行函数的细节。这其中,正则表达式的运用是绕不开的。大部分模板引擎,无论是早期的EJS、Handlebars,还是后来的Underscore.&lt;a style=&quot;color:#f60; text-decoration:underline;&quot; title=&quot;js&quot; href=&quot;https://www.php.cn/zt/15802.html&quot; target=&quot;_blank&quot;&gt;js&lt;/a&gt;模板功能,都会利用正则表达式来识别模板中的特殊标记。&lt;/p&gt; &lt;div class=&quot;aritcle_card&quot;&gt; &lt;a class=&quot;aritcle_card_img&quot; href=&quot;/ai/2290&quot;&gt; &lt;img src=&quot;https://img.php.cn/upload/ai_manual/001/246/273/175686939683618.png&quot; alt=&quot;AiPPT模板广场&quot;&gt; &lt;/a&gt; &lt;div class=&quot;aritcle_card_info&quot;&gt; &lt;a href=&quot;/ai/2290&quot;&gt;AiPPT模板广场&lt;/a&gt; &lt;p&gt;AiPPT模板广场-PPT模板-word文档模板-excel表格模板&lt;/p&gt; &lt;div class=&quot;&quot;&gt; &lt;img src=&quot;/static/images/card_xiazai.png&quot; alt=&quot;AiPPT模板广场&quot;&gt; &lt;span&gt;147&lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;a href=&quot;/ai/2290&quot; class=&quot;aritcle_card_btn&quot;&gt; &lt;span&gt;查看详情&lt;/span&gt; &lt;img src=&quot;/static/images/cardxiayige-3.png&quot; alt=&quot;AiPPT模板广场&quot;&gt; &lt;/a&gt; &lt;/div&gt; &lt;p&gt;例如,一个简单的模板引擎可能会使用这样的正则:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;/<%=(.*?)%>/g&lt;/pre&gt;
登录后复制
&lt;/div&gt; 来匹配需要输出的变量,比如&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;<%= name %>&lt;/pre&gt;
登录后复制
&lt;/div&gt;。&lt;/li&gt; &lt;li&gt;&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;/<%(.*?)%>/g&lt;/pre&gt;
登录后复制
&lt;/div&gt; 来匹配需要执行的JavaScript代码块,比如&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;<% if (user) { %>&lt;/pre&gt;
登录后复制
&lt;/div&gt;。&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;当引擎拿到模板字符串后,它会遍历整个字符串,根据这些正则匹配出不同的部分:纯文本、输出表达式和逻辑代码。然后,它会开始构建一个JavaScript函数的字符串表示。这个过程通常会涉及到一个累积结果的变量,我们称之为&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;_output&lt;/pre&gt;
登录后复制
&lt;/div&gt;。&lt;/p&gt; &lt;p&gt;想象一下,模板引擎在内部会做这样的事情:&lt;/p&gt;&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class='brush:javascript;toolbar:false;'&gt;// 假设这是模板引擎内部生成的函数字符串 var fnBody = &amp;quot;var _output = [];\n&amp;quot;; // 初始化一个数组来收集结果 fnBody += &amp;quot;_output.push('<div>Hello, ');\n&amp;quot;; // 遇到纯文本,直接push fnBody += &amp;quot;_output.push(data.name);\n&amp;quot;; // 遇到 <%= name %>,转换为push(data.name) fnBody += &amp;quot;_output.push('!</div>');\n&amp;quot;; // 遇到 <% if (data.show) { %> fnBody += &amp;quot;if (data.show) {\n&amp;quot;; fnBody += &amp;quot;_output.push('<p>This is visible.</p>');\n&amp;quot;; fnBody += &amp;quot;}\n&amp;quot;; // 遇到 <% } %> fnBody += &amp;quot;return _output.join('');&amp;quot;; // 最后拼接成字符串返回&lt;/pre&gt;
登录后复制
&lt;/div&gt;&lt;p&gt;这个&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;fnBody&lt;/pre&gt;
登录后复制
&lt;/div&gt;字符串,最终会被&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;new Function('data', fnBody)&lt;/pre&gt;
登录后复制
&lt;/div&gt;这样的方式,转换成一个真正的JavaScript函数。这里的&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;'data'&lt;/pre&gt;
登录后复制
&lt;/div&gt;是这个函数的参数名,我们就可以通过这个参数将实际的数据传入。&lt;/p&gt; &lt;p&gt;早期的很多模板引擎,为了方便在模板内部直接访问数据对象的属性(比如直接写&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;name&lt;/pre&gt;
登录后复制
&lt;/div&gt;而不是&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;data.name&lt;/pre&gt;
登录后复制
&lt;/div&gt;),会使用JavaScript的&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;with&lt;/pre&gt;
登录后复制
&lt;/div&gt;语句。例如:&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;with (data) { ... }&lt;/pre&gt;
登录后复制
&lt;/div&gt;。然而,&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;with&lt;/pre&gt;
登录后复制
&lt;/div&gt;语句因为其性能问题(会影响JS引擎的优化)和潜在的变量&lt;a style=&quot;color:#f60; text-decoration:underline;&quot; title=&quot;作用域&quot; href=&quot;https://www.php.cn/zt/35787.html&quot; target=&quot;_blank&quot;&gt;作用域&lt;/a&gt;混淆,在现代JavaScript开发中已经被强烈不推荐使用。所以,现在的模板引擎通常会选择显式地传递数据对象,或者在生成函数时对变量进行预处理,以避免&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;with&lt;/pre&gt;
登录后复制
&lt;/div&gt;的弊端。理解这个从字符串到可执行函数的转换过程,是理解模板引擎工作原理的关键。&lt;/p&gt; &lt;h3&gt;性能考量与安全性挑战&lt;/h3&gt; &lt;p&gt;在实际应用中,模板引擎的性能和安全性是两个非常重要的考量点。&lt;/p&gt; &lt;p&gt;从&lt;strong&gt;性能&lt;/strong&gt;角度看,模板引擎的工作可以分为两个阶段:编译(Parsing and Code Generation)和执行(Execution)。&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;strong&gt;编译阶段&lt;/strong&gt;的性能,主要取决于模板的大小和复杂性,以及引擎的解析和代码生成效率。对于大型应用,如果每次渲染都重新编译模板,那性能开销会非常大。因此,大多数模板引擎都会引入&lt;strong&gt;缓存机制&lt;/strong&gt;,将编译好的渲染函数存储起来。第一次编译后,后续的渲染请求可以直接使用缓存中的函数,大大提升了效率。&lt;/li&gt; &lt;li&gt; &lt;strong&gt;执行阶段&lt;/strong&gt;的性能,则取决于生成的JavaScript代码的效率。例如,频繁的字符串拼接(尤其是在老旧浏览器中)可能不如数组&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;join('')&lt;/pre&gt;
登录后复制
&lt;/div&gt;高效。现代JS引擎对字符串拼接已经做了很多优化,但这依然是一个需要注意的细节。此外,模板内部的逻辑是否复杂、数据量是否庞大,也会直接影响执行速度。预编译(Pre-compilation)是另一个提升性能的手段,即在项目构建阶段就将模板编译成JS文件,避免了运行时编译的开销。&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;而&lt;strong&gt;安全性&lt;/strong&gt;方面,最主要的就是&lt;strong&gt;XSS(跨站脚本攻击)&lt;/strong&gt;问题。如果模板引擎不加处理地将用户输入的数据直接渲染到HTML中,恶意用户就可以注入JavaScript代码,从而窃取用户信息、篡改页面内容。例如,如果用户在某个输入框输入了&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;<script>alert('hack')</script>&lt;/pre&gt;
登录后复制
&lt;/div&gt;,而模板引擎直接将其输出,那么这段脚本就会在用户的浏览器中执行。&lt;/p&gt; &lt;p&gt;为了应对XSS,模板引擎通常会提供&lt;strong&gt;内容转义(Escaping)&lt;/strong&gt;功能。这意味着,当数据被插入到HTML中时,像&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;<&lt;/pre&gt;
登录后复制
&lt;/div&gt;、&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;>&lt;/pre&gt;
登录后复制
&lt;/div&gt;、&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;&amp;&lt;/pre&gt;
登录后复制
&lt;/div&gt;、&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;&quot;&lt;/pre&gt;
登录后复制
&lt;/div&gt;这样的特殊字符会被转换为对应的HTML实体(如&lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;&lt;&lt;/pre&gt;
登录后复制
&lt;/div&gt;, &lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;&gt;&lt;/pre&gt;
登录后复制
&lt;/div&gt;, &lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;&amp;&lt;/pre&gt;
登录后复制
&lt;/div&gt;, &lt;div class=&quot;code&quot; style=&quot;position:relative; padding:0px; margin:0px;&quot;&gt;&lt;pre class=&quot;brush:php;toolbar:false;&quot;&gt;&quot;&lt;/pre&gt;
登录后复制
&lt;/div&gt;)。这样,即使数据中包含恶意脚本,浏览器也会将其视为普通文本而非可执行代码。一个好的模板引擎应该默认对所有输出内容进行转义,除非开发者明确声明不转义(这通常用于插入可信赖的HTML片段)。在我看来,安全性永远是第一位的,任何可能的用户输入都应该被视为不可信,并进行严格的转义处理。&lt;/p&gt;

以上就是JavaScript模板引擎的实现原理的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
热门推荐
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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