前端学习线路之:HTML5

HTML5

学习内容:原生页面布局、标签语义化;
应用学习:vscode等;
学习成效:读懂布局、公共模块抽离等;
适用范围:PC端、Ipad端、移动端web、H5项目、Hybrid APP混合开发等等;基本上web端项目都离不开布局;

一、html 常用标签

1、块标签:独占一行
<div>基础标签:没有默认样式</div>

<h1>标题标签:h1~h6</h1>

<p>段落标签:默认有上下外边距</p>

<ul>
  <li>无序列表标签:有列表默认样式黑点</li>
  <li>列表1</li>
  <li>列表2</li>
  <li>列表3</li>
</ul>

<ol>
  <li>有序列表标签:有列表默认样式数字</li>
  <li>列表1</li>
  <li>列表2</li>
  <li>列表3</li>
</ol>

<dl>
  <dt>自定义列表标题</dt>
  <dd>列表内容1</dd>
  <dd>列表内容2</dd>
  <dd>列表内容3</dd>
</dl>

<br /> 换行标签

在这里插入图片描述

2、内联标签:不是独占一行,写宽高样式不生效,需要转换成块
<!-- 链接 -->
<a href="http://www.baidu.com" target="_blank">跳转链接,target属性定义跳转时用新窗口打开</a>
<!-- 图片 -->
<img src="图片路径" alt="图片加载失败显示的文字">
<!-- 无属性 -->
<span>通常用于文本中某些字变色、加粗等</span>
3、表格(了解)
<table border="1">
  <tr>
    <th>第一行第一列</th>
    <th>第一行第二列</th>
  </tr>
  <tr>
    <td>第二行第一列</td>
    <td>第二行第二列</td>
  </tr>
</table>

在这里插入图片描述

4、表单标签: form表单,action提交到的文件路径,method提交方式post或get
<form action="demo_form.php" method="get">
  文本框: <input type="text" name="username">
  <br />
  密码框: <input type="password" name="password">
  <br />
  单选:注意name值要相同 checked选中状态
  <input type="radio" name="sex" value="male" checked><br />
  <input type="radio" name="sex" value="female"><br />
  复选框:
  <input type="checkbox" name="vehicle" value="Bike">学习
  <input type="checkbox" name="vehicle" value="Car">游泳
  <br />
  提交按钮:
  <input type="submit" value="Submit">
  <br />
  多行文本域 cols宽度,rows高度
  <textarea rows="10" cols="30"></textarea>
  <br />
  下拉选择框
  <select>
    <option value="volvo">选项一</option>
    <option value="saab">选项二</option>
    <option value="mercedes">选项三</option>
  </select>
  <br />
  按钮标签
  <button>我是一个按钮</button>
</form>

在这里插入图片描述

二、html5新增功能

1、新增语义标签
<header>页头</header>
<nav>导航</nav>
<section>小节区段</section>
<article>独立的内容区域</article>
<aside>侧边栏内容</aside>
<footer>页脚</footer>
2、input新增类型和属性(了解)

新增类型

选择你喜欢的颜色: <input type="color" name="favcolor">
从一个日期选择器选择一个日期:<input type="date" name="bday">
生日 (日期和时间): <input type="datetime" name="bdaytime">
生日 (月和年): <input type="month" name="bdaymonth">
选择时间: <input type="time" name="usr_time">
选择周: <input type="week" name="week_year">
会自动验证 email 域的值是否合法有效: <input type="email" name="email">
数量 ( 1 到 5 之间 ): <input type="number" name="quantity" min="1" max="5">
搜索: <input type="search" name="googlesearch">
电话号码: <input type="tel" name="usrtel">

新增属性

// 当用户在自动完成域中开始输入时,浏览器应该在该域中显示填写的选项
<input type="email" name="email" autocomplete="off">
// 属性规定在页面加载时,域自动地获得焦点
<input type="text" name="fname" autofocus>
// multiple 属性规定<input> 元素中可选择多个值
<input type="file" name="img" multiple>
// required 属性规定必须在提交之前填写输入域(不能为空)
<input type="text" name="usrname" required>
3、视频(了解)

controls控制,source资源,type类型,src路径

<video width="320" height="240" controls>
  <source src="movie.mp4" type="video/mp4">
  <source src="movie.ogg" type="video/ogg">
</video>
4、音频(了解)
<audio controls>
  <source src="horse.ogg" type="audio/ogg">
  <source src="horse.mp3" type="audio/mpeg">
</audio>
5、web存储(重要)

localStorage - 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去除。
sessionStorage - 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。

保存数据:localStorage.setItem(key,value);
读取数据:localStorage.getItem(key);
删除单个数据:localStorage.removeItem(key);
删除所有数据:localStorage.clear();
得到某个索引的key:localStorage.key(index);

在这里插入图片描述

6、了解 web
worker、WebSocket(知道有这个东西就行)

web worker - 是运行在后台的 JavaScript,不会影响页面的性能,通常不用于如此简单的脚本,而是用于更耗费 CPU 资源的任务

// demo_workers
var i=0;

function timedCount()
{
    i=i+1;
    postMessage(i); // 它用于向 HTML 页面传回一段消息
    setTimeout("timedCount()",500);
}

timedCount();
<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>web worker</title> 
</head>
<body>
 
<p>计数: <output id="result"></output></p>
<button onclick="startWorker()">开始工作</button> 
<button onclick="stopWorker()">停止工作</button>
 
<p><strong>注意:</strong> Internet Explorer 9 及更早 IE 版本浏览器不支持 Web Workers.</p>
 
<script>
var w;
 
function startWorker() {
    if(typeof(Worker) !== "undefined") {
        if(typeof(w) == "undefined") {
            w = new Worker("demo_workers.js");
        }
        // onmessage监听事件
        w.onmessage = function(event) {
            document.getElementById("result").innerHTML = event.data; // event.data取参数
        };
    } else {
        document.getElementById("result").innerHTML = "抱歉,你的浏览器不支持 Web Workers...";
    }
}
 
function stopWorker() 
{ 
    w.terminate(); // 终止worker方法
    w = undefined; // 重置w必要,否则worker不会再执行
}
</script>
 
</body>
</html>

WebSocket - 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议

7、SVG(了解)

HTML5 支持内联 SVG
SVG 是一种使用 XML 描述 2D 图形的语言
什么是SVG?

  • SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
  • SVG 用于定义用于网络的基于矢量的图形
  • SVG 使用 XML 格式定义图形
  • SVG 图像在放大或改变尺寸的情况下其图形质量不会有损失
  • SVG 是万维网联盟的标准

SVG优势
与其他图像格式相比(比如 JPEG 和 GIF),使用 SVG 的优势在于:

  • SVG 图像可通过文本编辑器来创建和修改
  • SVG 图像可被搜索、索引、脚本化或压缩
  • SVG 是可伸缩的
  • SVG 图像可在任何的分辨率下被高质量地打印
  • SVG 可在图像质量不下降的情况下被放大
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="190">
  <polygon points="100,10 40,180 190,60 10,60 160,180"
  style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;">
</svg>
8、Canvas(了解)

Canvas 通过 JavaScript 来绘制 2D 图形

 // 一个画布在网页中是一个矩形框,通过 <canvas> 元素来绘制
 <canvas id="myCanvas" width="200" height="100"></canvas>
 // canvas 元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内部完成
 var c=document.getElementById("myCanvas"); // 首先,找到 <canvas> 元素:
 var ctx=c.getContext("2d"); // 然后,创建 context 对象
 ctx.fillStyle="#FF0000"; // 下面的两行代码绘制一个红色的矩形
 ctx.fillRect(0,0,150,75);

三、拖放drag&drop(了解)

1、draggable 属性

通过 draggable 告诉浏览器哪些元素需要实现拖拽功能。有三个可选值:

true: 元素可以被拖拽

false:元素不能被拖拽

auto:浏览器自己判断元素是否能被拖拽 ( 默认 )

2、对象拖放事件

dragstart:按下鼠标键并开始移动时触发

drag:在元素拖拽过程中持续触发----相似与mousemove

dragend:元素拖拽停止时触发

0a81d5c13c6f4d8b9c8b284dbf5f7079.png

3、投放区事件流程

元素被拖动到有效的放置目标时,下列事件会依次发生:

  1. dragenter:当拖拽对象进入投放区时触发

  2. dragover :拖拽对象在投放区内移动时持续触发

  3. dragleave:元素被拖出了投放区时触发

  4. drop :拖拽对象投放在投放区时触发

在ondragover中一定要执行 preventDefault()否则ondrop事件不会被触发

4、dataTransfer 对象–常用方法

setDragImage (图标,图标距指针X轴偏移值,Y轴偏移值 )

指定一个图标,当拖动发生时,显示在光标下方

5、dataTransfer 对象–常用属性:

dropEffect 表示被拖动的元素能够执行哪种放置行为

可能的值:

“none” : 不能把拖动的元素放在这里

“move”: 把拖动的元素移动到放置目标

“copy”: 把拖动的元素复制到放置目标

“link”: 放置目标会打开拖动的元素(有URL)

effectAllowed 允许拖动元素的哪种dropEffect

允许值:

“copyLink” : 允许值为 copy 和 link 的 dropEffect

“copyMove”: 允许值为 copy 和 move 的 dropEffect

“linkMove” : 允许值为 link和 move 的 dropEffect

“all” : 允许任意的 dropEffect

注意:dropEffect属性搭配effectAllowed属性使用

在dragstart事件处理程序中设置effectAllowed属性

在dragover事件处理程序中设置dropEffect属性

dropEffect 的每个可能值都会导致光标显示为不同的符号

6、files 文件

dataTransfer.files:如果是拖放文件,则返回正在拖放的文件列表FileList

FileReader:专门用于读取文件,FileReader 接口提供一些读取文件的方法与一个包含读取结果的事件模型

FileReader.readAsDataURL方法:参数为要读取的文件对象,将文件读取为DataUrl

FileReader.onload事件:当读取文件成功完成的时候触发此事件,在事件触发后,你可以通过this.result来获取读取的文件数据,如果是图片,将返回base64格式的图片数据。

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title>
<style type="text/css">
#div1 {width:350px;height:70px;padding:10px;border:1px solid #aaaaaa;}
</style>
<script>
// 放到何处 - ondragover;ondragover 事件规定在何处放置被拖动的数据。默认地,无法将数据/元素放置到其他元素中。如果需要设置允许放置,我们必须阻止对元素的默认处理方式。这要通过调用 ondragover 事件的 event.preventDefault() 方法
function allowDrop(ev)
{
	ev.preventDefault();
}
// 拖动什么 - ondragstart 和 setData();然后,规定当元素被拖动时,会发生什么。在上面的例子中,ondragstart 属性调用了一个函数,drag(event),它规定了被拖动的数据。dataTransfer.setData() 方法设置被拖数据的数据类型和值;Text 是一个 DOMString 表示要添加到 drag object 的拖动数据的类型。值是可拖动元素的 id ("drag1")
function drag(ev)
{
	ev.dataTransfer.setData("Text",ev.target.id);
}
// 进行放置 - ondrop;当放置被拖数据时,会发生 drop 事件。在上面的例子中,ondrop 属性调用了一个函数,drop(event)
function drop(ev)
{
	ev.preventDefault();
	var data=ev.dataTransfer.getData("Text");
	ev.target.appendChild(document.getElementById(data));
}
</script>
</head>
<body>

<p>拖动 RUNOOB.COM 图片到矩形框中:</p>

<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<br>
<!-- 首先,为了使元素可拖动,把 draggable 属性设置为 true -->
<img id="drag1" src="/images/logo.png" draggable="true" ondragstart="drag(event)" width="336" height="69">

</body>
</html>

四、meta属性(了解)

name:keywords、description、viewport、 robots
author、generator、revisit-after、 renderer
主要用于描述网页

1.<meta name=”viewport” content=”width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no”>:在移动设备浏览器上,禁用缩放(zooming)功能,用户只能滚动屏幕。
2.<meta name=”description” content=””>:告诉搜索引擎,当前页面的主要内容是xxx。
3.<meta name=”keywords” content=””>:告诉搜索引擎,当前页面的关键字。
4.<meta name=”author” content=””>:告诉搜索引擎,标注网站作者是谁。
5.<meta name=”copyright” content=””>:标注网站的版权信息。
6.<meta name="viewport" content="width=device-width, initial-scale=1.0">:适应移动端缩放

http-equiv:content-Type、X-UA- Compatible、cache-control、expires、 refresh
http-equiv顾名思义,相当于http的文件头作用,它可以向浏览器传回一些有用的信息,以帮助正确和精确地显示网页内容

1.<meta http-equiv=”Set-Cookie” content=”cookievalue=xxx; expires=Friday,12-Jan-2001 18:18:18 GMT; path=/”>:如果网页过期,那么存盘的cookie将被删除。必须使用GMT的时间格式。
2.<meta http-equiv='expires' content='时间' >:用于设定网页的到期时间。一旦网页过期,必须到服务器上重新传输。
3.<meta http-equiv=”Refresh” content=”5;URL”>:告诉浏览器在【数字】秒后跳转到【一个网址】
4.<meta http-equiv=”content-Type” content=”text/html; charset=utf-8″>:设定页面使用的字符集。
  <meta charset=”utf-8″>:在HTML5中设定字符集的简写写法。
5.<meta http-equiv=”Pragma” content=”no-cache”>:禁止浏览器从本地计算机的缓存中访问页面内容。访问者将无法脱机浏览。
6.<meta http-equiv=”Window-target” content=”_top”>:用来防止别人在iframe(框架)里调用自己的页面,这也算是一个非常实用的属性。
7.<meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'> :强制浏览器按照特定的版本标准进行渲染。但不支持IE7及以下版本。如果是ie浏览器就用最新的ie渲染,如果是双核浏览器就用chrome内核。

五、浏览器解析机制(URL从输入到页面展示的过程)

首先,我们假设输入的url的请求为最简单的Http请求,以GET请求为例,大致分以下几个步骤:

  1. 用户在浏览器的地址栏输入访问的URL地址。浏览器会先根据这个URL查看浏览器缓存-系统缓存-路由器缓存,若缓存中有,直接跳到第6步操作,若没有,则按照下面的步骤进行操作。

  2. 浏览器根据输入的URL地址解析出主机名。

  3. 浏览器将主机名转换成服务器ip地址。浏览器先查找首先查看本地硬盘的 hosts 文件,看看其中有没有和这个域名对应的规则,然后本地DNS缓存列表,看缓存里面是否存在这个ip,如果有则进入第4步,如果缓存中不存在这个ip地址,就再向浏览器默认的DNS服务器发送查询请求,同时缓存当前这个ip到DNS缓存列表中

  4. 拿到ip地址后,浏览器再从URL中解析出端口号

  5. 拿到ip和端口后,浏览器会建立一条与目标Web服务器的TCP连接,也就是传说中的三次握手。

  6. 浏览器向服务器发送一条HTTP请求报文。

  7. 服务器向浏览器返回一条HTTP响应报文。

  8. 关闭连接 浏览器解析文档。

如果文档中有资源则重复6、7、8动作,直至资源全部加载完毕。

六、html的重排,重构与性能优化

为什么操作DOM会很慢

虽然DOM是由JavaScript实现的,但是在浏览器中都是把DOM和JavaScript分开来实现的,因此,每一次在通过js操作DOM的时候,就需要先去连接js和DOM,我们可以这样理解:把DOM和JavaScript比作两个岛,他们之间通过一个收费的桥连接着,每一次访问DOM的时候,就需要经过这座桥,并且给“过路费”,访问的次数越多,路费就会越高,并且访问到DOM后,操作具体的DOM还需要给“操作费”,由于浏览器访问DOM的操作很多,因此,“路费”和“操作费”自然会增加,这就是为什么操作DOM会很慢的原因

浏览器渲染HTML的步骤

HTML渲染大致分为如下几步:

  1. HTML被HTML解析器解析成DOM Tree, css则被css解析器解析成CSSOM Tree。

  2. DOM Tree和CSSOM Tree解析完成后,被附加到一起,形成渲染树(Render Tree)。

  3. 节点信息计算(重排),这个过程被叫做Layout(Webkit)或者Reflow(Mozilla)。即根据渲染树计算每个节点的几何信息。

  4. 渲染绘制(重绘),这个过程被叫做(Painting 或者 Repaint)。即根据计算好的信息绘制整个页面。

以上4步简述浏览器的一次渲染过程,理论上,每一次的dom更改或者css几何属性更改,都会引起一次浏览器的重排/重绘过程,而如果是css的非几何属性更改,则只会引起重绘过程。所以说重排一定会引起重绘,而重绘不一定会引起重排

重排(Relayout/Reflow)

在弄明白什么是重排之前,我们要知道:浏览器渲染页面默认采用的是流式布局模型(Flow Based Layout),这一点很重要。

所谓重排,实际上是根据渲染树中每个渲染对象的信息,计算出各自渲染对象的几何信息(DOM对象的位置和尺寸大小),并将其安置在界面中的正确位置。

由于浏览器渲染界面是基于流式布局模型的,也就是某一个DOM节点信息更改了,就需要对DOM结构进行重新计算,重新布局界面,再次引发回流,只是这个结构更改程度会决定周边DOM更改范围,即全局范围和局部范围,全局范围就是从根节点html开始对整个渲染树进行重新布局,例如当我们改变了窗口尺寸或方向或者是修改了根元素的尺寸或者字体大小等;而局部布局可以是对渲染树的某部分或某一个渲染对象进行重新布局。

在此,总结会引起重排的操作有:

  • 页面首次渲染。
  • 浏览器窗口大小发生改变。
  • 元素尺寸或位置发生改变。
  • 元素内容变化(文字数量或图片大小等等)。
  • 元素字体大小变化。
  • 添加或者删除可见的DOM元素。
  • 激活CSS伪类(例如::hover)。
  • 设置style属性

查询某些属性或调用某些方法。
在这里插入图片描述

重绘(Repainting)

相比重排,重绘就简单多了,所谓重绘,就是当页面中元素样式的改变并不影响它在文档流中的位置时,例如更改了字体颜色,浏览器会将新样式赋予给元素并重新绘制的过程称。

常见引起浏览器绘制过程的属性包含:
在这里插入图片描述

性能优化

我们知道操作DOM是一个高成本的操作,不仅是因为本身js与DOM的链接访问,还包括操作DOM后悔引起一连串的连锁反应(重排),因此,从性能优化角度,我们可以从以下几个方面着手:

  • 减少DOM操作
    • 最小化DOM访问次数,尽量缓存访问DOM的样式信息,避免过度触发回流。
    • 如果在一个局部方法中需要多次访问同一个dom,则先暂存它的引用。
  • 采用更优的API替代消费高的api,转换优化消费高的集合
    • 用querySelectorAll()替代getElementByXX()。
    • 开启动画的GPU加速,把渲染计算交给GPU。
    • 少用HTML集合(类数组)来遍历,因为集合遍历比真数组遍历耗费更高。
    • 用事件委托来减少事件处理器的数量。
  • 减少重排
    • 避免设置大量的style属性,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow,所以最好是使用class属性
    • 实现元素的动画,它的position属性,最好是设为absoulte或fixed,这样不会影响其他元素的布局
    • 动画实现的速度的选择。比如实现一个动画,以1个像素为单位移动这样最平滑,但是reflow就会过于频繁,大量消耗CPU资源,如果以3个像素为单位移动则会好很多。
    • 不要使用table布局,因为table中某个元素旦触发了reflow,那么整个table的元素都会触发reflow。那么在不得已使用table的场合,可以设置table-layout:auto;或者是table-layout:fixed这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围
  • css及动画处理
    • 少用css表达式
    • 减少通过JavaScript代码修改元素样式,尽量使用修改class名方式操作样式或动画;
    • 动画尽量使用在绝对定位或固定定位的元素上;
    • 隐藏在屏幕外,或在页面滚动时,尽量停止动画;

本篇文章主要抓取url从输入到最后渲染成界面这一流程中的浏览器解析渲染HTML这一步骤来探讨前端优化的思路和原因,核心思想基于重排和重绘的关系来展开讨论,主题大致有如下几点:

  • url从输入到最后渲染的大致环节。
  • 重排一定会重绘,重绘不一定有重排。
  • Js操作DOM是一个高消费过程。
  • 会引起重排/重绘的属性和方法列举
  • 优化思路(减少dom操作、替换高性能api、暂存引用、减少重排、开启硬件加速等)。

七、浏览器缓存原理(了解)

浏览器对于缓存的处理是根据第一次请求资源时返回的响应头来确定的

  • 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
  • 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中

以上两点结论就是浏览器缓存机制的关键,它确保了每个请求的缓存存入与读取,只要我们再理解浏览器缓存的使用规则,那么所有的问题就迎刃而解了,本文也将围绕着这点进行详细分析。为了方便大家理解,这里我们根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分,分别是强缓存和协商缓存。
强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control。

协商缓存:就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:

协商缓存生效,返回304和Not Modified
协商缓存失效,返回200和请求结果

所谓用户行为对浏览器缓存的影响,指的就是用户在浏览器如何操作时,会触发怎样的缓存策略。主要有 3 种:

打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。
强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control: no-cache(为了兼容,还带了 Pragma: no-cache),服务器直接返回 200 和最新内容。

八、h5应用缓存manifest(了解)

  1. 什么是Manifest
    其实Manifest是一个简单的 文本文件,它的扩展名是任意的推荐.appcache,定义需要缓存的文件、资源,当第一次打开时,浏览器会自动缓存相应的资源。

  2. Manifest 的特点
    离线浏览:即当网络断开时,可以继续访问你的页面。
    访问速度快:将文件缓存到本地,不需每次都从网络上请求。
    稳定性:做了Manifest缓存,遇到突发网络故障或者服务器故障,继续访问本地缓存

引入index.appcache文件

<!DOCTYPE HTML>
<html manifest="index.appcache">
	...
</html>

manifest 文件可分为三个部分:

CACHE MANIFEST - 在此标题下列出的文件将在首次下载后进行缓存
NETWORK - 在此标题下列出的文件需要与服务器的连接,且不会被缓存
FALLBACK - 在此标题下列出的文件规定当页面无法访问时的回退页面(比如 404 页面)

CACHE MANIFEST
/theme.css
/logo.gif
/main.js

NETWORK:
login.asp

FALLBACK:
/html5/ /404.html

九、常见的状态码

200 -表示请求被成功完成,请求的资源发送回客户端
304 -自从上次请求后,请求的网页未修改过,服务器返回此响应时,不会返回网页内容,代表上次的文档已经被缓存了,还可以继续使用
400 -客户端请求语法错误,不能被服务器所理解
403 -禁止访问,服务器收到请求,但是拒绝提供服务
404 -服务器无法取得所请求的网页,请求资源不存在。
500-服务器内部错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值