《手把手教你》系列技巧篇(十五)-java+ selenium自动化测试-元素定位大法之By xpath中卷(详细教程)

news2024/9/23 17:22:26

1.简介

 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath。xpath 的定位方法, 非常强大。  使用这种方法几乎可以定位到页面上的任意元素。

2.什么是xpath?

xpath 是XML Path的简称, 由于HTML文档本身就是一个标准的XML页面,所以我们可以使用Xpath 的用法来定位页面元素。

XPath 是XML 和Path的缩写,主要用于xml文档中选择文档中节点。基于XML树状文档结构,XPath语言可以用在整棵树中寻找指定的节点。XPath 定位和CSS定位相比有更大的灵活性。XPath 在文档树中某个节点既可以向前搜索,也可以向后搜索,CSS定位只能在文档树中向前搜索,但XPath的定位速度比CSS 慢一些。

3.xpath定位的缺点

xpath 这种定位方式, webdriver会将整个页面的所有元素进行扫描以定位我们所需要的元素, 这是个非常费时的操作, 如果脚本中大量使用xpath做元素定位的话, 脚本的执行速度可能会稍慢。

4.常用定位方法(8种)

(1)id
(2)name
(3)class name
(4)tag name
(5)link text
(6)partial link text
(7)xpath(今天讲解)
(8)css selector

5.自动测试实战

以百度首页为例,将xpath的各种定位方法一一讲解和分享一下。

5.1大致步骤

1.访问度娘首页。

2.通过xpath定位到元素,点击一下。

5.2使用索引号定位

索引号定位,以‘//’开头,具体格式为

xxx.By.xpath("//标签[x]")

具体例子:

//form/div[1]:表示 form 下的第一个 div
//form/div[last()]:表示 form 下的最后一个 div
//form/div[last()-1]:表示 form 下的倒数第二个 div

具体步骤:

在被测试百度网页中,按照宏哥在上卷中5.2中的方法 (1)查找输入框并输入“北京宏哥”,如下图所示:(2)查找“百度一下”按钮,如下图所示:(3)点击“百度一下”按钮。

XPath表达式:

(1)//form/span/input[1]

(2)//form/span[2]/input[1]

java定位语句:

(1)WebElement SearchBox = driver.findElement(By.xpath( "//form/span/input[1]" ));

(2)WebElement SearchButton = driver.findElement(By.xpath("//form/span[2]/input[1]"));
5.2.1代码设计

5.2.2参考代码
package lessons;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

/**
 * @author 北京-宏哥
 * 
 * 《手把手教你》系列技巧篇(十四)-java+ selenium自动化测试-元素定位大法之By xpath上篇(详细教程)
 * 
 * 2021年8月4日
 */
public class ByXpath {
    
    public  static  void  main(String [] args) throws InterruptedException {
        
        System.setProperty("webdriver.gecko.driver", ".\\Tools\\chromedriver.exe"); //指定驱动路径
 
        WebDriver driver = new ChromeDriver ();
        //最大化窗口  
        driver.manage().window().maximize();  
        driver.get("http://wwww.baidu.com");
        
        //By xpath 定位
        WebElement SearchBox  = driver.findElement(By.xpath( "//form/span/input[1]" ));
        
        SearchBox.sendKeys("北京宏哥");
        
        WebElement SearchButton  = driver.findElement(By.xpath("//form/span[2]/input[1]"));
        
        SearchButton .click();
        
        //定位到文本,将文本高亮显示
        //创建一个JavascriptExecutor对象
        JavascriptExecutor js =(JavascriptExecutor)driver;
 
        //新闻文本高亮显示颜色
        js.executeScript ( "arguments[0].setAttribute('style', arguments[1]);",SearchBox,"background: orange; border: 2px solid red;");
 
        Thread.sleep (5000);
 
        driver.quit();
    }

}
5.2.3运行代码

1.运行代码,右键Run AS->java Application,控制台输出,如下图所示:

2.运行代码后电脑端的浏览器的动作,如下小视频所示:

根据元素类型在页面中出现的先后顺序,可以使用序号来查找指定的页面元素。本实例的XPath表达式表示查找页面中第二个出现的span中的input元素,即被测试页面上的按钮元素。 如果使用span/input[1],会发现固定位出输入框和按钮元素,这是因为页面中含有两个span节点,每个span节点都包含input元素,XPath在查找的时候,把每个span节点都当作相同的其实层级开始查找,所以input[1]能查出两个元素。
因此在使用序号进行页面定位元素的时候,需要注意网页HTML代码中是否包含多个层级完全相同的代码结构。如果使用XPath表达式同时定位多个页面元素,将定位到多个元素存储到List对象中。
在实际使用中,如果元素经常有新增或减少的情况,不建议使用索引号定位的方式,因为页面的变化会导致使用索引号的XPath表达式定位失败。

5.3使用页面属性定位

标签属性定位,相对比较简单,也要求属性能够定位到唯一一个元素,如果存在多个相同条件的标签,默认只是第一个,具体格式:

xxx.By.xpath("//标签[@属性='属性值']")

属性判断条件:最常见为id,name,class等等,目前属性的类别没有特殊限制,只要能够唯一标识一个元素都是可以的

具体例子:

xxx.By.xpath("//a[@href='/industryMall/hall/industryIndex.html']")
xxx.By.xpath("//input[@value='确定']")
xxx.By.xpath("//div[@class = 'submit']/input")

(1)当某个属性不足以唯一区别某一个元素时,也可以采取多个条件组合的方式,具体例子

xxx..By.xpath("//input[@type='name' and @name='kw1']")

(2)当标签属性很少,不足以唯一区别元素时,但是标签中间中间存在唯一的文本值,也可以定位,其具体格式

xxx.By.xpath("//标签[contains(text(),'文本值')]")

具体例子:

xxx.By.xpath("//iunpt[contains(text(),'型号:')]")

注意:尽量在html中复制此段文本,避免因为肉眼无法分辨的字符导致定位失败

(3) 其他的属性值如果太长,也可以采取模糊方法定位,直接上示例

xxx.By.xpath(“//a[contains(@href, ‘logout')]”)

(4)XPath 关于网页中的动态属性的定位,例如,ASP.NET应用程序中动态生成id属性值,可以有以下四种方法:

- starts-with例子: //input[starts-with(@id,'ctrl')] 解析:匹配以ctrl开始的属性值

- ends-with 例子://input[ends-with(@id,'userName')] 解析:匹配以userName结尾的属性值

- contains() 例子://input[contains(@id,'userName')] 解析:匹配含有userName属性值

当然,如果上面的单一方法不能完成定位,也可以采取组合式定位 类似

("//input[@id='kw1']//input[start-with(@id,'nice']/div[1]/form[3])

(5) .//和//的区别

//是指从全文上下文中搜索//后面的节点,而.//则是指从前面的节点的子节点中进行查找

(6) 选取若干路径|

这个符号用于在一个xpath中写多个表达式用,用|分开,每个表达式互不干扰,意思是一个xpath可以匹配多个不同条件的元素,例如:如下图所示,xpath可以匹配到满足条件的i标签元素和满足条件的span标签元素。

//i[@class='c-icon'] | //span[@class='hot-refresh-text']

具体步骤:

在被测试百度网页中,按照宏哥在上卷中5.2中的方法  (1)查找输入框并输入“北京宏哥”,(2)查找“百度一下”按钮,(3)点击“百度一下”按钮。因为上卷中的相对路径宏哥已经用了id,在这里宏哥就是用一下其他的属性。

XPath表达式:

(1)//input[@name='wd']

(2)//input[@value='百度一下']

java定位语句:

(1)WebElement searchBox = driver.findElement(By.xpath( "//input[@name='wd']" ));

(2)WebElement SearchButton = driver.findElement(By.xpath("//input[@value='百度一下']"));
5.3.1代码设计

5.3.2参考代码
package lessons;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

/**
 * @author 北京-宏哥
 * 
 * 《手把手教你》系列技巧篇(十四)-java+ selenium自动化测试-元素定位大法之By xpath上篇(详细教程)
 * 
 * 2021年8月4日
 */
public class ByXpath {
    
    public  static  void  main(String [] args) throws InterruptedException {
        
        System.setProperty("webdriver.gecko.driver", ".\\Tools\\chromedriver.exe"); //指定驱动路径
 
        WebDriver driver = new ChromeDriver ();
        //最大化窗口  
        driver.manage().window().maximize();  
        driver.get("http://wwww.baidu.com");
        
        //By xpath 定位
        WebElement SearchBox  = driver.findElement(By.xpath( "//input[@name='wd']" ));
        
        SearchBox.sendKeys("北京宏哥");
        
        WebElement SearchButton  = driver.findElement(By.xpath("//input[@value='百度一下']"));
        
        SearchButton .click();
        
        //定位到文本,将文本高亮显示
        //创建一个JavascriptExecutor对象
        JavascriptExecutor js =(JavascriptExecutor)driver;
 
        //新闻文本高亮显示颜色
        js.executeScript ( "arguments[0].setAttribute('style', arguments[1]);",SearchBox,"background: orange; border: 2px solid red;");
 
        Thread.sleep (5000);
 
        driver.quit();
    }

}
5.3.3运行代码

1.运行代码,右键Run AS->java Application,控制台输出,如下图所示:

2.运行代码后电脑端的浏览器的动作,如下小视频所示:

在定位页面元素的时候,会遇到各种结构复杂的网页,并且经常出现无法使用ID,name等方式进行定位。有不想使用感觉对路径的定位方式,也没法搞清使用什么序号来定位元素,那么则推荐使用属性值定位元素的方法。

被测试网页的元素一般都包含各种各样的属性值,并且很多属性值具有唯一性。因此非常建议使用相对路径结合属性值定位的方式来编写XPath定位表达式,基于此定位方法可以解决大部分的页面元素定位问题。

5.4使用XPath的轴(Axis)进行元素定位

使用Aixs方法可依据在文档书中的元素相对位置关系进行定位。先找到一个相对好定位的元素,再根据这个元素和要定位的相对位置进行定位,可以解决一些元素难以定位的问题。

5.4.1轴示意图

5.4.2XPath常用的关键字

XPath常用的关键字如下所示:

XPath轴关键字

轴含义

实例

表达式解释

ancestor

选取当前节点的所有先辈(父、祖父等)

//img[@alt=’div2-img2’]/ancestor::div

查找alt属性值为div2-img的图片,并基于图片位置找到它的上级div页面元素。

ancestor-or-self

选取当前节点的所有先辈(父、祖父等)以及当前节点本身

//img[@alt=’div2-img2’]/ ancestor-or-self::*

查找alt属性值为div2-img的图片,并基于图片位置找到它全部上级元素,包括它本身。

attribute

选取当前节点的所有属性

//img[@alt=’div2-img2’]/ attribute::*

查找alt属性值为div2-img的图片并返回该节点下的所有属性节点

child

选取当前节点的所有子元素。

//div[@id=’div1’]/child::img

查找ID属性为div1的div页面元素,并基于div的位置找到它下层节点中的img页面元素

descendant

选取当前节点的所有后代元素(子、孙等)。

//div[@name=’div2’]/ descendant::img

查找name属性值为div2的元素,并基于div位置找到它下级的所有节点中的img页面元素。

descendant-or-self

选取当前节点的所有后代元素(子、孙等)以及当前节点本身。

//div[@name=’div2’]/ descendant::div

查找name属性值为div2的元素,并基于div位置找到它下级的(包括自己)所有节点中的div页面元素。其实就是它本身。

following

选取文档中当前节点的结束标签之后的所有节点。

//div[@id=’div1’]/ following::img

查找到ID属性值为div1的div页面,并基于div找到它后面节点中的img页面元素

parent

选取当前节点的父节点。

//img[@alt=’div2-img2’]/ parent::div

查找到alt属性值为div2-img的图片并基于图片位置找到它上一级的div页面元素。

preceding

选择当前节点前面的所有节点

//img[@alt=’div2-img2’]/preceding::div

查找alt属性值为div2-img2的照片页面元素,并基于图片的位置找到它前面节点中的div页面元素。

preceding-sibling

选取当前节点之前的所有同级节点。

//img[@alt=’div2-img2’]/ preceding-sibling::a[1]

查找alt属性值为div2-img2的照片页面元素,并基于图片的位置找到它前面同级节点中的第二个链接页面元素

self

选取当前节点。

5.4.3使用方法

轴名称::节点测试[谓语]

例如:

//form/div[last()-1]/ancestor::div[@class='modal-content']
5.4.4实践

1.descendant表示取当前节点的所有后代元素,宏哥演示定位百度首页的“百度一下”按钮,如下图所示:

可以看到,input标签的父元素是span标签,而span标签的父元素是form标签,所以可以通过先定位form标签,然后利用descendant定位input标签

xpath路径如下:

//定位思路:
//(1)form[@id='form']表示找到id属性为'form'的<form>标签,
//(2)descendant::input表示找到<form>标签的所有后代<input>标签,
//(3)然后通过[@id='su']精准定位到id属性为'su'的<input>标签

xpath= "//form[@id='form']/descendant::input[@id='su']"

把路径放到浏览器控制台,按下Ctrl+F,然后输入xpath路径,查看一下,确实定位到了标签(在执行程序之前,可以通过这种方式来验证一下写的xpath路径是否正确)

2.following表示选取当前节点结束标签之后的所有节点

注意这里说的是“结束标签之后”,所以在用这个轴进行定位时要看清目标标签的与辅助定位标签的层级关系,所以上例中就不能通过标签结合following来定位,因为标签在标签里面;

分析一下:input标签的上级是一个span标签,这个span标签上面也有一个span标签,可以通过它(span)来定位

//定位思路:
//(1)span[@id='s_kw_wrap']表示定位到id属性为s_kw_wrap的<span>标签,
//(2)following::input[@id='su']表示找到<span>结束标签(即</span>)后的所有input标签,
//(3)然后通过[@id='su']精准定位到id属性为'su'的<input>标签

xpath= "//span[@id='s_kw_wrap']/following::input[@id='su']"

按上边的方法,宏哥再次验证一下,成功定位到“百度一下”按钮,如下图所示:

3.parent可指定要查找的当前节点的直接父节点

例如,父节点是个div,即可写成parent::div,如果要找的元素不是直接父元素,则不可使用parent,可使用ancestor,代表父辈、祖父辈等节点,child::表示直接子节点元素,following-sibling只会标识出当前节点结束标签之后的兄弟节点,而不包含其他子节点;

以https://www.guru99.com/这个网站为例,如下图所示:

 定位网页中的python:

//定位思路:
//(1)先定位Java,然后找到Java的父节点li,
//(2)然后再找li的兄弟节点,即包含Python的那个li标签,
//(3)然后再找li的孩子节点,也就是a标签

xpath="//a[text()='Java']/parent::li/following-sibling::li/child::a[text()='Python']"

//或者

xpath="//a[text()='Java']/parent::li/following-sibling::li[2]/a"

宏哥这里就不做验证了,有兴趣或者不信的小伙伴或者童鞋们,可以按照宏哥的方法自己验证一下。

6.小结

好了,今天分享的前边两种xpath的定位方法比较简单,第三种比较难一点,不过慢慢积累经验时间久了也就那么回事。今天到此宏哥就分享完了。后边还有一些,敬请期待。

7.拓展

① Xpath 定位扩展
使用通过子节点定位父节点
..代表父节点;../..爷爷节点

//span[contains(text(),'1.jpg')]/..

② Xpath 还支持布尔定位

Xpath = //input[@id='kw1' and @name='wd']

可以 and ,当然也可以 or :

Xpath = //input[@id='kw1' or @name='wd']

 每天学习一点,今后必成大神-

往期推荐(由于跳转参数丢失了,所有建议选中要访问的右键,在新标签页中打开链接即可访问)或者微信搜索: 北京宏哥  公众号提前解锁更多干货。

Appium自动化系列,耗时80天打造的从搭建环境到实际应用精品教程测试

Python接口自动化测试教程,熬夜87天整理出这一份上万字的超全学习指南

Python+Selenium自动化系列,通宵700天从无到有搭建一个自动化测试框架

Java+Selenium自动化系列,仿照Python趁热打铁呕心沥血317天搭建价值好几K的自动化测试框架

Jmeter工具从基础->进阶->高级,费时2年多整理出这一份全网超详细的入门到精通教程

Fiddler工具从基础->进阶->高级,费时100多天吐血整理出这一份全网超详细的入门到精通教程

Pycharm工具基础使用教程

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

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

相关文章

六、矩阵问题

73、矩阵置零&#xff08;中等&#xff09; 题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a…

OpenAI工作环境曝光:高薪背后的996;Quora的转变:由知识宝库至信息垃圾场

&#x1f989; AI新闻 &#x1f680; OpenAI工作环境曝光&#xff1a;高薪背后的996 摘要&#xff1a;近日&#xff0c;多位OpenAI匿名员工在求职网站Glassdoor上披露了公司的工作环境和公司文化&#xff0c;包括高薪水和优厚的福利待遇&#xff0c;但同时伴随着996的加班文化…

pdf编辑软件哪个好用?5款PDF编辑器分享

pdf编辑软件哪个好用&#xff1f;PDF编辑软件在现代办公和学术研究中发挥着举足轻重的作用&#xff0c;它们不仅具备基础的编辑和修改功能&#xff0c;还能够支持多种注释工具&#xff0c;帮助我们高效地管理和整理PDF文件。无论是需要调整文档布局、添加文本或图像&#xff0c…

程序员的金三银四求职宝典:如何在关键时期脱颖而出?

个人主页&#xff1a;17_Kevin-CSDN博客 随着春天的脚步渐近&#xff0c;程序员们的求职热潮也随之而来。在这个被称为“金三银四”的招聘季&#xff0c;如何从众多求职者中脱颖而出&#xff0c;成为了许多程序员关注的焦点。本文将为你提供一份全面的求职宝典&#xff0c;助你…

现货大宗软件数据处理模块源码

现货大宗软件数据处理模块源码&#xff1a;揭秘背后的技术魅力 在当今的大数据时代&#xff0c;无论是金融、贸易还是其他领域&#xff0c;数据处理都显得尤为重要。特别是对于现货大宗交易来说&#xff0c;数据处理不仅关乎交易的速度与效率&#xff0c;更直接影响到交易的成…

基于嵌入式的车载导航定位系统设计

一、前言 1.1 项目介绍 【1】项目背景 随着汽车工业的飞速发展和智能化技术的不断突破&#xff0c;车载导航系统作为现代汽车不可或缺的一部分&#xff0c;在人们的日常生活中扮演着越来越重要的角色。它不仅能够提供精确的路线导航&#xff0c;还能提供丰富的地理信息和娱乐…

Java:JVM基础

文章目录 参考JVM内存区域程序计数器虚拟机栈本地方法栈堆方法区符号引用与直接引用运行时常量池字符串常量池直接内存 参考 JavaGuide JVM内存区域 程序计数器 程序计数器是一块较小的内存空间&#xff0c;可以看做是当前线程所执行的字节码的行号指示器&#xff0c;各线程…

mprpc分布式RPC网络通信框架

mprpc 项目介绍 该项目是一个基于muduo、Protobuf和Zookeeper实现的轻量级分布式RPC网络通信框架。 可以把任何单体架构系统的本地方法调用&#xff0c;重构成基于TCP网络通信的RPC远程方法调用&#xff0c;实现同一台机器的不同进程之间的服务调用&#xff0c;或者不同机器…

[ai笔记14] 周鸿祎的ai公开课笔记1

欢迎来到文思源想的ai空间&#xff0c;这是技术老兵重学ai以及成长思考的第14篇分享&#xff01; 本周二月的最后一周&#xff0c;并不是闲下来了&#xff0c;反而是开始进行一些更多的深入实践&#xff0c;关于gpt的主体架构、关于prompt&#xff0c;同时也看了不少书和直播&…

【Linux】基本指令(中)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:Linux ⚙️操作环境:Xshell (操作系统:CentOS 7.9 64位) 目录 man指令 cp指令 mv指令 cat指令 more指令 less指令 head指令 …

Go语言中的时间控制:定时器技术详细指南

Go语言中的时间控制&#xff1a;定时器技术详细指南 引言定时器基础创建和使用time.Timer使用time.Ticker实现周期性任务定时器的内部机制小结 使用time.Timer实现简单的定时任务创建和启动定时器停止和重置定时器定时器的实际应用小结 利用time.Ticker处理重复的定时任务创建和…

PHP【swoole】

前言 Swoole官方文档&#xff1a;Swoole 文档 Swoole 使 PHP 开发人员可以编写高性能高并发的 TCP、UDP、Unix Socket、HTTP、 WebSocket 等服务&#xff0c;让 PHP 不再局限于 Web 领域。Swoole4 协程的成熟将 PHP 带入了前所未有的时期&#xff0c; 为性能的提升提供了独一无…

[晓理紫]CCF系列会议截稿时间订阅

CCF系列会议截稿时间订阅 关注{晓理紫|小李子}&#xff0c;每日更新最新CCF系列会议信息&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持&#xff01;&#xff01; 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新CCF…

学习使用paddle来构造hrnet网络模型

1、首先阅读了hrnet的网络结构分析&#xff0c;了解到了网络构造如下&#xff1a; 参考博文姿态估计之2D人体姿态估计 - &#xff08;HRNet&#xff09;Deep High-Resolution Representation Learning for Human Pose Estimation&#xff08;多家综合&#xff09;-CSDN博客 最…

3、Redis Cluster集群运维与核心原理剖析

Redis集群方案比较 哨兵模式 在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态&#xff0c;如果master节点异常&#xff0c;则会做主从切换&#xff0c;将某一台slave作为master&#xff0c;哨兵的配置略微复杂&#xff0c;并且性能和高可用性…

用numpy搭建自己的神经网络

搭建之前的基础与思考 构建模型的基本思想&#xff1a; 构建深度学习的过程&#xff1a;产生idea&#xff0c;将idea转化成code&#xff0c;最后进行experiment&#xff0c;之后根据结果修改idea&#xff0c;继续idea–>code–>experiment的循环&#xff0c;直到最终训练…

前后端分离vue+nodejs高校体育运动会比赛系统08fv2-python-php-java

实现了一个完整的高校体育运动会比赛系统系统&#xff0c;其中主要有运动项目模块、学生模块、项目类型模块、用户表模块、token表模块、关于我们模块、收藏表模块、公告信息模块、留言板模块、运动论坛模块、配置文件模块、裁判员模块、比赛成绩模块、比赛报名模块、关于我们模…

Java网络通信TCP

目录 TCP两个核心类 服务端 1.用ServerSocker类创建对象并且手动指定端口号 2.accept阻塞连接服务端与客户端 3.给客户端提供处理业务方法 4.处理业务 整体代码 客户端 1.创建Socket对象&#xff0c;并连接服务端的ip与端口号 2.获取Socket流对象&#xff0c;写入数据…

uniapp 部署h5,pdf预览

1.hubuilderx 打包h5。 2.上传部署包到服务器。 解压部署包&#xff1a;unzip h5.zip 。 3.nginx配置。 user root; worker_processes 1; #worker_cpu_affinity 0001 0010 0100 1000; #error_log logs/error.log; #error_log logs/error.log notice; error_log /var/l…

C#/.NET/.NET Core优秀项目和框架2024年2月简报

前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架&#xff08;每周至少会推荐两个优秀的项目和框架当然节假日除外&#xff09;&#xff0c;公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等&#xff08;打不开或者打开GitHub很慢的同学…