3.3.3JavaScript网页编程——WebAPI(JS之BOM含正则)

BOM

回忆DOM:文档对象模型

BOM(Browser Object Model ) :是浏览器对象模型

window 是浏览器内置中的全局对象,我们所学习的所有 Web APIs 的知识内容都是基于 window 对象实现的

  • window 对象下包含了 navigator、location、document、history、screen 5个属性,即所谓的 BOM (浏览器对象模
    型)
  • document 是实现 DOM 的基础,它其实是依附于 window 的属性。
  • 注:依附于 window 对象的所有属性和方法,使用时可以省略 window
    在这里插入图片描述

常用省略的:
在这里插入图片描述

window对象

定时器-延时函数setTimeout

JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window。

应用场景:可以用作时间到自己清除
在这里插入图片描述
在这里插入图片描述

结合递归函数可以使用 setTimeout 实现 setInterval 一样的功能

在这里插入图片描述

两种定时器对比:
 setInterval 的特征是重复执行,首次执行会延时
 setTimeout 的特征是延时执行,只执行 1 次
 setTimeout 结合递归函数,能模拟 setInterval 重复执行
 clearTimeout 清除由 setTimeout 创建的定时任务

JS执行机制(执行栈、任务队列)面试要问

  • 经典面试题

下面两个输出都是1111 3333 2222 2222…;
第一个不难理解,因为定时器要等待一秒,页面一加载就会执行的,所以不会等定时器一秒后而先输出3333;
第二个就是本节知识

在这里插入图片描述
在这里插入图片描述

  1. JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。
  2. 比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。
  3. 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
  • 同步和异步
    为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许JavaScript 脚本创建多个线程。于是,JS 中出现了同步(一个接一个顺序单个去做)和异步(在做这件事可以做其他的)。

他们的本质区别: 这条流水线上各个流程的执行顺序不同

同步任务都在主线程上执行,形成一个执行栈
JS 的异步是通过回调函数实现的。

一般而言,异步任务有以下三种类型:
1、普通事件,如 click、resize 等
2、资源加载,如 load、error 等
3、定时器,包括 setInterval、setTimeout 等异步任务相关添加到任务队列中(任务队列也称为消息队列)。

  • 执行过程
  1. 先执行执行栈中的同步任务。
  2. 异步任务放入任务队列中。
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
  4. 事件循环( event loop):执行执行栈里面的任务,执行完毕再去任务队列里面看看是否有任务,如果有,则得到放入执行栈中执行,再次循环。

在这里插入图片描述

下面可能是1234也可能是1243,看点击和定时器的具体操作
在这里插入图片描述
在这里插入图片描述

location对象

location 的数据类型是对象,它拆分并保存了 URL 地址的各个组成部分

  • 常用属性和方法:
     href 属性获取完整的 URL 地址,对其赋值时用于地址的跳转 (可用于支付界面几秒后返回跳转)
     search 属性获取地址中携带的参数,符号 ?后面部分
     hash 属性获取地址中的啥希值,符号 # 后面部分
     reload 方法用来刷新当前页面,传入参数 true 时表示强制刷新

location.href (获取完整url或者赋值)

在这里插入图片描述

  • 案例——五秒后跳转
    在这里插入图片描述

location.search (获取?后面的)

search 属性获取地址中携带的参数,符号 ?后面部分

location.hash(获取#号后面的)

hash 属性获取地址中的哈希值,符号 # 后面部分

后期vue路由的铺垫,经常用于不刷新页面,显示不同页面,比如 网易云音乐

location.reload

reload 方法用来刷新当前页面,传入参数 true 时表示强制刷新(相当于刷新按钮)括号参数是默认false表示刷新,true表示强制刷新=ctrl+f5,前者如果进入过网页就直接本地有缓存很快,后者强制从网上加载可能会有空白时刻

在这里插入图片描述

navigator对象(检测浏览器移动端pc端)

navigator的数据类型是对象,该对象下记录了浏览器自身的相关信息,通过 userAgent 检测浏览器的版本及平台,即const userAgent = navigator.userAgent。(代码不用背)
地址换成移动端的地址
在这里插入图片描述

histor对象(前进forward()、后退back()、go(参数))

作了解

history 的数据类型是对象,该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等。history 对象一般在实际开发中比较少用,但是会在一些 OA 办公系统中见到。

在这里插入图片描述

在这里插入图片描述
上面代码两个按钮:
1.前进的用法:在这个初始两按钮界面地址a栏重新输入地址b,然后在浏览器点击后退箭头,这时候回到初始两按钮界面a,可以点击前进按钮前进了,前进到界面b。
2.后退的用法:复制这个初始两按钮的界面地址a,重新在界面地址c删掉那个地址输入初始按钮界面a的地址,然后按钮可以点击后退了,后退到界面c

swiper插件

官网
在线演示demo
基本使用流程
APi文档,配置自己的插件

下载后的swiper打开package里面,一个是压缩过的min,一个是没有压缩的正常js,一般引入min的css和js(本地实例在demos文件夹里面,也可以网页查看),多个swiper同时使用的时候, 类名需要注意区分,且script的js要放在引入插件的下面
在这里插入图片描述

本地存储特性

随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在
本地存储大量的数据,HTML5规范提出了相关解决方案。 1、数据存储在用户浏览器中
2、设置、读取方便、甚至页面刷新不丢失数据
3、容量较大,sessionStorage和localStorage约 5M 左右

localStorage

1、生命周期永久生效,除非手动删除 否则关闭页面也会存在
2、可以多窗口(页面)共享(同一浏览器可以共享)
3. 以键值对的形式存储使用

  • 存储简单数据类型

存储数据:
localStorage.setItem(key, value)
获取数据:
localStorage.getItem(key)
删除数据:
localStorage.removeItem(key)

在这里插入图片描述

  • 存储复杂数据类型存储
    1、本地只能存储字符串,无法存储复杂数据类型.需要将复杂数据类型转换成JSON字符串,在存储到本地
    2、JSON.stringify(复杂数据类型)  将复杂数据转换成JSON字符串 存储 本地存储中
    3、JSON.parse(JSON字符串)  将JSON字符串转换成对象 取出 时候使用

如果直接setItem的话,存储的是一个[object Object]仍然看不懂,需要把数据对象转化为json格式(JSON数据,属性和值都是双引号进行包含),也就是
在这里插入图片描述

在这里插入图片描述

JSON.stringify转化了就看得到数据对象了

在这里插入图片描述

且console.log(typeof localStorage.getItem(‘obj’)) 输出是String
所以用JSON.parse(localStorage.getItem(‘obj’))将字符串转化为对象

在这里插入图片描述
对象存取完整示例如下:
在这里插入图片描述

sessionStorage(了解)

1、生命周期为关闭浏览器窗口
2、在同一个窗口(页面)下数据可以共享
3. 以键值对的形式存储使用
4. 用法跟localStorage 基本相同(就把localStorage 换成sessionStorage)

案例

注意是拿数据来增删了再把改了的数据存回去
e.target.tagName === ‘A’ 点击了链接才能删(后面的e.target.dataset.id、e.target.id)
学号删了的可以重新排,但是已有的不能因为中间删掉的重新再排了
不允许删除第一条数据

需求:改为本次存储版本的学习信息表
需求①:读取本地存储数据(封装函数)
如果本地存储有数据,则返回 JSON.parse() 之后的对象
如果本地存储没有数据,则默认写入三条数据,注意存储的利用JSON.stringify() 存 储JSON 格式的数据
需求②:渲染模块
先读取本地存储数据,然后渲染
需求③:添加模块
注意,先取的最新的本地存储数据,然后追加
新增了数据,要把新数据存储到本地存储别,忘记转换
需求④:删除模块
注意,先取的最新的本地存储数据,然后追加
新增了数据,要把新数据存储到本地存储别忘记转换
在这里插入图片描述

自定义属性(getAttribute(‘属性名’)——>元素.dataset.属性名)

  • 固有属性:
    标签天生自带的属性 比如class id title等, 可以直接使用点语法操作
  • 自定义属性:
    由程序员自己添加的属性,在DOM对象中找不到, 无法使用点语法操作,必须使用专门的API

getAttribute(‘属性名’) // 获取自定义属性
setAttribute(‘属性名’, ‘属性值’) // 设置自定义属性
removeAttribute(‘属性名’) // 删除自定义属性

  • data-自定义属性:
    传统的自定义属性没有专门的定义规则,开发者随意定值,不够规范,所以在html5中推出来了专门的data-自定义属性 在标签上一律以data-开头,在DOM对象上一律以dataset对象方式获取

在这里插入图片描述

案例——表格增删

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="css/user.css">
</head>

<body>
  <h1>新增学员</h1>
  <div class="info">
    姓名:<input type="text" class="uname">
    年龄:<input type="text" class="age">
    性别: <select name="gender" id="" class="gender">
      <option value=""></option>
      <option value=""></option>
    </select>
    薪资:<input type="text" class="salary">
    就业城市:<select name="city" id="" class="city">
      <option value="北京">北京</option>
      <option value="上海">上海</option>
      <option value="广州">广州</option>
      <option value="深圳">深圳</option>
      <option value="曹县">曹县</option>

    </select>
    <button class="add">录入</button>
  </div>

  <h1>就业榜</h1>
  <table>
    <thead>
      <tr>
        <th>学号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>性别</th>
        <th>薪资</th>
        <th>就业城市</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <!-- <tr>
        <td>1001</td>
        <td>欧阳霸天</td>
        <td>19</td>
        <td>男</td>

        <td>15000</td>
        <td>上海</td>
        <td>
          <a href="javascript:">删除</a>
        </td>
      </tr> -->
    </tbody>
  </table>
  <script>

    // 读取本地存储的数据  封装为函数 
    // 需求1:读取本地存储数据(封装函数)
    
    
    function getLocalData() {
      let data = localStorage.getItem('data')//拿到数据(是JSON字符串类型噢,要parse)
      if (data) { //   如果本地存储有数据,则返回 JSON.parse() 之后的对象
        return JSON.parse(data)
      } else { //   如果本地存储没有数据,则默认写入三条数据,注意存储的利用JSON.stringify() 存 储JSON 格式的数据
        let arr = [
          { stuId: 1001, uname: '欧阳霸天', age: 19, gender: '男', salary: '20000', city: '上海' },
          { stuId: 1002, uname: '令狐霸天', age: 29, gender: '男', salary: '30000', city: '北京' },
          { stuId: 1003, uname: '诸葛霸天', age: 39, gender: '男', salary: '2000', city: '北京' },
        ]
        localStorage.setItem('data', JSON.stringify(arr)) //这里如果忘记写JSON.stringify()一定要清缓存

      }
    }
        
    // 先调用一次(以保证现在有存储的基本数据)
    getLocalData()
    
    // 获取元素
    let tbody = document.querySelector('tbody')
    let add = document.querySelector('.add')
    let uname = document.querySelector('.uname')
    let age = document.querySelector('.age')
    let gender = document.querySelector('.gender')
    let salary = document.querySelector('.salary')
    let city = document.querySelector('.city')
    //let del = document.querySelectorAll('tbody a') //a链接很多,一条数据一个,都要绑定事件
    

    
    // 渲染函数  把数组里面的数据渲染到页面中
    function render(){
      let data = getLocalData() //JSON格式的数组

      //先清空完页面的数据以便重新渲染
      tbody.innerHTML = ''
      for(let i = 0; i < data.length; i++){
        let tr = document.createElement('tr') //创建元素
        //对每一条数据进行渲染
        tr.innerHTML = `
          <td>${data[i].stuId}</td>
          <td>${data[i].uname}</td>
          <td>${data[i].age}</td>
          <td>${data[i].gender}</td>

          <td>${data[i].salary}</td>
          <td>${data[i].city}</td>
          <td>
            <a href="javascript:" data-id="${i}">删除</a>
          </td>
        `
        tbody.appendChild(tr) //加到总的tbody里面
      }
    }
    
    // 页面加载就调用函数
    render()

    //点击增加数据操作
    add.addEventListener('click', function() {
      //获取本地已有的数据
      let arr = getLocalData()
      //获取当前页面输入的数据,也就是获取那些元素的innerHTML值一律.value
      let temp = {
        "stuId" : +arr[arr.length - 1].stuId + 1, //最后一个对象的学号(取出来是string)+1
        "uname" : uname.value,
        "age" : age.value,
        "gender" : gender.value,
        "salary" : salary.value,
        "city" : city.value,
      }
      arr.push(temp) //增加修改完毕
      //存进去
      localStorage.setItem('data', JSON.stringify(arr))

      //重新渲染
      render()

    })

    


    // 删除操作, 删除的也是数组里面的数据 , 但是我们用事件委托
    tbody.addEventListener('click', function(e) {
      //获取本地已有的数据
      let arr = getLocalData()
      
      //获取删除的那个元素
      if (e.target.tagName === 'A') {
        if (e.target.dataset.id === '0') {
          alert('当前数据不允许删除操作')
          return //退出
        }
        //console.log(e.target.id)
        console.log(e.target.dataset.id)
        arr.splice(e.target.dataset.id, 1) //第id个子孩子
      }
      
       
      //存进去
      localStorage.setItem('data', JSON.stringify(arr))

      //重新渲染
      render()

    })
    //注意如果删完了localStorage里面data存在,是[]这样,再增加数据的时候就拿不到数据的id了,从而不成功
    //所以设置第一条数据不可以删除在渲染的a删除里面多加一个data-id="${i}"!!!!!!
    
  </script>
</body>

</html>

案例——微博发布案例

旧微博发布案例
其中的增加和删除都可以类似上面的案例表格增删的去改(这里就没有再去改了)
主要修改点:

  • 事件委托
  • localStorage.getItem、setItem
  • document.createElement(‘tr’) //创建元素
  • arr.push和splice,
  • e.target.tagName === ‘A’,如果没有用a标签或者有两个一样的span标签,那就用类名
  • e.target.className === 'delete’这样,或者多设置一个data-id
  • 删除的span标签加上data-id=“${i}”

在这里插入图片描述

正则表达式

正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象(对象就有属性方法),通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式。(实际开发不会很严格,且可以参考菜鸟教程所罗列的正则表达式)

1.正则表达式是什么?
是用于匹配字符串中字符组合的模式

2.正则表达式有什么作用?
表单验证(匹配) 、过滤敏感词(替换) 、字符串中提取我们想要的部分(提取)
3.正则表达式使用的步骤:定义正则表达式,检测查找是否匹配

在这里插入图片描述

判断是否有符合规则的字符串:

test() 方法 用来查看正则表达式与指定的字符串是否匹配,如果正则表达式与指定的字符串匹配 ,返回true,否则false

在这里插入图片描述

检索(查找)符合规则的字符串:

exec() 方法 在一个指定字符串中执行一个搜索匹配,如果匹配成功,exec() 方法返回一个数组,否则返回null

在这里插入图片描述

在这里插入图片描述

test和exec区别

正则表达式检测查找 test方法和exec方法有什么区别?

  1. test方法 用于判断是否有符合规则的字符串,返回的是布尔值 找到返回
    true,否则false
  2. exec方法用于检索(查找)符合规则的字符串,找到返回数组,否则为
    null

元字符

  • 普通字符:
    大多数的字符仅能够描述它们本身,这些字符称作普通字符,例如所有的字母和数字。也就是说普通字符只能够匹配字符串中与它们相同的字符。

  • 元字符(特殊字符)是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能。比如,规定用户只能输入英文26个英文字母,普通字符的话 abcdefghijklm……

  • 但是换成元字符写法: [a-z]

参考文档–MDN
参考文档–正则测试工具

为了方便记忆和学习,我们对众多的元字符进行了分类:

  1. 边界符(表示位置,开头和结尾,必须用什么开头,用什么结尾)
  2. 量词 (表示重复次数)
  3. 字符类 (比如 \d 表示 0~9)

边界符

正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符,如果 ^ 和 $ 在一起,表示必须是精确匹配

边界符说明
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)

在这里插入图片描述

量词

量词用来 设定某个模式出现的次数,注意: 逗号左右两侧千万不要出现空格

两量词说明
*重复零次或更多次
+重复一次或更多次
重复零次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n次到m次

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

字符类 (比如 \d 表示 0~9)

1. [ ]匹配字符集合

后面的字符串只要包含 abc 中任意一个字符,都返回 true

在这里插入图片描述

!!!!注意如果是^开头的$结尾的,而中括号里面又是多选一,则只表示开头唯一的一个字符,结尾唯一个字符,所以前四个是正确的,最后cc不正确,因为不是唯一。

在这里插入图片描述

2. [ ]里面加上 - 连字符,使用连字符 - 表示一个范围

例如:
[a-z] 表示 a 到 z 26个英文字母都可以
[a-zA-Z] 表示大小写都可以
[0-9] 表示 0~9 的数字都可以

认识下

下面表示大小写数字和下划线任意选择一个(但是不能重复噢,重复选需要量词)因为有^$

在这里插入图片描述

QQ号的正则表达式:要求第一位不能为0,至少总位数是五位,
量词只针对前面最近的一位的修饰,也就是说{4,}至少4位是指[0-9]这个表达

在这里插入图片描述

例如下面的意思是以ab开头,c的位数是一位起步的(+只修饰c)

在这里插入图片描述

  • 用户名验证案例
    需求:用户名要求用户英文字母,数字,下划线或者短横线组成,并且用户名长度为 6~16位(下面防止markdown的格式问题^后加了个空格,实际上没有)
    分析:正则表达式模式 /^ [a-zA-Z0-9-_]{6,16}$/
    blur:当表单失去焦点就开始验证失去焦点 ,nextElementSibiling:下一个兄弟节点。如果符合正则规范, 则让后面的span标签添加 right 类。如果不符合正则规范, 则让后面的span标签添加 wrong 类

在这里插入图片描述

  • 昵称案例
    需求:要求用户只能输入中文(下面防止markdown的格式问题^后加了个空格,实际上没有)
    正则表达式模式 ^ [\u4e00-\u9fa5] {2,8}$/
    这里的正则表达式时中文的开始和结束的unicode编码,后面表示6-16位都可以
    在这里插入图片描述
3. [ ] 里面加上 ^ 取反符号

比如:
[ ^a-z] 匹配除了小写字母以外的字符
注意要写到中括号里面

4. . 匹配除换行符之外的任何单个字符
5. 预定义:指的是某些常见模式的简写方式。

(下面防止markdown的格式问题^前后可能加了个空格,实际上没有)

说明
\d匹配0-9之间的任一数字,相当于[0-9]
\匹配所有0-9以外的字符,相当于[ ^0-9]
\w匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
\W除所有字母、数字和下划线以外的字符,相当于[ ^A-Za-z0-9_]
\s匹配空格(包括换行符、制表符、空格符等),相等于[\t\r\n\v\f]
\S匹配非空格的字符,相当于[ ^\t\r\n\v\f]

在这里插入图片描述

修饰符

修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等,

i和g

在这里插入图片描述
i 是单词 ignore 的缩写,正则匹配时字母不区分大小写,g 是单词 global 的缩写,匹配所有满足正则表达式的结果
在这里插入图片描述

replace 替换(正则敏感词)

把textarea里面的激情或者基情替换成**,多个敏感词用单条 | ,后面记得加 g 表示所有的都要替换,否则只会替换第一个。
在这里插入图片描述

change事件

  • 区别:input事件、blur事件、change事件

input:只要是输入,就不断被触发
blur:离开表单失去焦点时触发事件
change:离开表单文本框失去焦点且内容发生改变才触发(比如都是空还没输入即便失去焦点也不会触发)

  • 属性选择器
    documen.querySelector(‘[name=username]’)

综合案例——小兔鲜

3个html,因为包含css,所以用百度网盘所有的案例

  • 1.进行注册界面各种信息验证,
    addEventListener里面调用的函数没加括号(只是定义,失去焦点改变才触发)
    但是判断语句里面确实是调用,看返回true还是false
    在这里插入图片描述
    在这里插入图片描述
  • 2.下面是登录界面密码的保存

<script>
    // 需求:
    // 1. 登录按钮点击的时候,需要先判断
    // 如果没有勾选同意,则提示要勾选
    let agree = document.querySelector('input[name=agree]') //复选框元素或者.remember也可
    let username = document.querySelector('[name=username]')
    let password = document.querySelector('[name=password]')
    
    let dl = document.querySelector('button.dl')//.dl登录按钮
    let form = document.querySelector('form') //form表单进行提交,而非按钮事件哦
    // form.addEventListener('submit', function(e){
    //   e.preventDefault()
    //   if(!agree.checked){ //没有选中
    //     alert('请勾选协议')
    //     return
    //   }
    //   let obj = {
    //     uname : username.value,
    //     password: password.value,
    //   }
    //   console.log(obj)
    // })
    
    //因为只是判断,不用提交表单,所以用click按钮写事件也可以
    dl.addEventListener('click', function(e){
      e.preventDefault()
      if(!agree.checked){ //没有选中
        alert('请勾选协议')
        return
      }
      let obj = {
        username : username.value,
        password: password.value,
      }
      //console.log(obj)

      //存进本地
      localStorage.setItem('pink',JSON.stringify(obj))

      //跳转页面
      location.href = './index.html'
    })

    //取出来
    let obj = JSON.parse(localStorage.getItem('pink'))
    //如果有数据,则自动填入和勾选
    if(obj){
      username.value = obj.username,
      password.value = obj.password
      //并且默认勾选复选框
      agree.checked = true
    }

    // 如果勾选协议,则记住用户名和密码
    // 登录成功则跳转到首页
    // 注意,登录按钮需要先阻止默认行为
   
  </script>

  • 3.获取用户名进行欢迎
<script>
    // 如果本地存储有数据,则 显示 你好 xxxx
    // 否则 显示 请跳转到注册页面

    let li = document.querySelector('.xtx_navs li:first-child')
    let obj = JSON.parse(localStorage.getItem('pink'))
    if (obj) {
      li.innerHTML = ` <a href="#">你好,${obj.username} 欢迎来到小兔鲜世界</a>`
    }
</script>

补充

  1. 快捷键ctrl+F搜索,ctrl+H查找所有
  2. 立即执行函数,括号写里面调用,一定要分号隔开
  3. nextElementSibiling:下一个兄弟节点。
  4. css属性选择器属性和值:documen.querySelector(‘[name=username]’)

表示:input 里面 有type属性的背景色变成红色

在这里插入图片描述
表示属性type值是text的背景才变成红色
在这里插入图片描述

5.this.classList.toggle(‘icon-queren2’) //切换添加或者不添加

在这里插入图片描述

h5新增的属性:加上这个required属性,表示该字段不能为空

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lanmy_dl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值