vue快速入门

Vue概述

Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。

  • 声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
    • 声明式就是”告诉机器你要什么东西,机器自己去做“,命令式就是”告诉机器一步一步应该怎么做“。所谓 ”声明式“ 就是我只关心 ”最终的状态“
  • 响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。

Vue快速入门案例

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title></title>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>

<body>
  <div id="container">
    公告:{{str}}。是《{{user.name}}》
  </div>

  <script type="text/javascript">
    //  注意版本,3版本创建方式不一样 
    var vm = new Vue({
      el: "#container",
      data: {
        str: "全世界最帅的人是谁",
        user:{
          name:"wcb"
        }
      }
    });
  </script>
</body>

</html>

MVVM 前端请求后端接口,后端返回数据,前端接收数据,并将接收的数据设置“VM”,HTML从vm取值

  • M model 数据模型 指的是后端接口返回的数据

  • V view 视图

  • VM ViewModel 视图模型 数据模型与视图之间的桥梁,后端返回的model转换前端所需的vm,视图层可以直接从vm中提取数据

常见指令

Vue.js指令 (Directives) 是带有 v- 前缀的特殊特性。

v-clock_解决屏幕闪烁

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
  <style>
    /* 
    如果网速较慢,vue元素还没有渲染情况下,页面会显示源代码的 例如:{{mesage}}
    我们可以使用 v-clock,作用就是为Vue绑定的元素上添加该属性,只需要配合CSS设置样式就可以解决屏幕闪烁问题。
    */
    [v-clock] {
      display: none;
    }
  </style>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>

<body>
  <div id="container" v-clock>
    公告:{{str}}。是《{{user.name}}》
  </div>

  <script type="text/javascript">
    var vm = new Vue({
      el: "#container",
      data: {
        str: "全世界最帅的人是谁",
        user: {
          name: "wcb"
        }
      }
    });
  </script>
</body>

</html>

v-text_v-html_v-pre_渲染文本内容

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container">
    <!-- 作用于插值表达式类似,但是没有闪动问题 -->
    <!-- v-text:将数据输出到元素内部,如果输出的数据有HTML代码,会作为普通文本输出 -->
    <div v-text="str"></div>
    <hr>
    <!-- v-html:将数据输出到元素内部,如果输出的数据有HTML代码,会被渲染 -->
    <div v-html="str"></div>
    <hr>
    <!-- 用于显示原始信息,比如这里{{str}}直接当成了文本 -->
    <div v-pre>{{str}}</div>
</div>

<script type="text/javascript">
    //  注意版本,3版本创建方式不一样 
    var vm = new Vue({
        el: "#container",
        data: {
            str: "12<h1>nb</h1>34"
        }
    });
</script>
</body>

</html>

v-bind_属性绑定

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>


<body>
<div id="container">
    <!-- `v-bind:` 可简写为 `:`(语法糖) -->
    <input :value="str"/>
    <input v-bind:value="user.name"/>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#container",
        data: {
            str: "全世界最帅的人是谁",
            user: {
                name: "wcb"
            }
        }
    });
</script>
</body>
</html>

v-on_事件绑定

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>


<body>
<div id="container">
    <!-- `v-on:` 可简写为 `@`(语法糖) -->
    <button v-on:click="von">我被点了{{on}}次</button>
    <button @click="von">简写版被点了{{on}}次</button>

    <!-- 传值=》通过js函数  -->
    <button type="button" v-on:click="getInfo(user.name,user.age)">获取信息</button>
    <!-- 传值=》通过dataset -->
    <button type="button" v-on:click="updateInfo" :data-name="user.name" :data-age="user.age">修改信息</button>
    <!--  混合传值-->
    <button type="button" v-on:click="delInfo(user.name,user.age,$event)" :data-name="user.name">删除信息</button>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#container",
        data: {
            on: 0,
            user: {
                name: "张三",
                age: 20
            }
        },
        methods: {
            von() {
                this.on += 1
            },
            getInfo: function (name, age) {
                console.log(name, age)
            },

            updateInfo: function (event) {
                //如果v-on绑定的js函数没有参数,调用的时候可以省略(),同时可以给js函数一个event参数(事件对象)
                // 1. event 表示触发当前函数的事件
                // 2. event.srcElement 表示发生事件的元素---修改按钮
                // 3. event.srcElement.dataset 表示按钮上绑定的数据集(data-开头的属性)
                var stu = event.srcElement.dataset;
                console.log("-update", event, stu)
            },
            delInfo: function (name, age, event) {
                var stu = event.srcElement.dataset;
                console.log("-update", name, age, event, stu)
            },
        }
    });
</script>
</body>
</html>

v-model_表单双向数据绑定

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container">
    <!--  
    - 只能使用在表单输入标签
    - v-model:value 可以简写为 v-model
    -->
    <input type="text" v-model:value="str"/>
    <input type="text" v-model="str"/>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#container",
        data: {
            str: "我最帅"
        }
    });
</script>
</body>
</html>

v-for_遍历

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container" v-clock>
    <h3>普通数组</h3>
    <p v-for="(item, i) in list1">索引值:{{i}} --- 每一项:{{item}}</p>
    <h3>对象数组</h3>
    <p v-for="(user, i) in list2">Id:{{ user.id }} --- 名字:{{ user.name }} --- 索引:{{i}}</p>
    <h3>对象</h3>
    <!-- 注意:在遍历对象身上的键值对的时候, 除了 有 val key ,在第三个位置还有 一个 索引 -->
    <p v-for="(val, key, i) in list3">值是: {{ val }} --- 键是: {{key}} -- 索引: {{i}}</p>
    <h3>数字</h3>
    <!-- in 后面我们放过 普通数组,对象数组,对象, 还可以放数字 -->
    <!-- 注意:如果使用 v-for 迭代数字的话,前面的 count 值从 1 开始 -->
    <p v-for="count in 10">这是第 {{ count }} 次循环</p>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#container",
        data: {
            list1: [1, 2, 3, 4, 5, 6],
            list2: [{id: 1, name: 'zs1'}],
            list3: {id: 1, name: '托尼·屎大颗', gender: '男'}
        }
    });
</script>
</body>
</html>

v-if_v-show_显示隐藏

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container" v-clock>
    <!-- 
      v-show与v-if的共同点
        我们都知道在 vue 中 v-show 与 v-if 的作用效果是相同的(不含v-else),都能控制元素在页面是否显示
      v-show与v-if的区别
        v-show隐藏则是为该元素添加css--display:none,dom元素依旧还在。v-if显示隐藏是将dom元素整个添加或删除
      性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
     -->

    <h2 v-show="stu.stuGender=='M'"></h2><br/>
    <h2 v-if="stu.stuGender=='M'"></h2><br/>

    <h2 v-if="stu.score >= 90">优秀</h2>
    <h2 v-else-if="stu.score >= 80">良好</h2>
    <h2 v-else-if="stu.score >= 70">较好</h2>
    <h2 v-else-if="stu.score >= 60">一般</h2>
    <h2 v-else="stu.score < 60">不及格</h2>

</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#container",
        data: {
            stu: {
                stuNum: "100001",
                stuName: "张三",
                stuGender: "M",
                score: 70
            }
        }
    });
</script>
</body>
</html>

自定义指令(全局、私有)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container">
    <h1 v-red>124</h1>
    <h1 v-blue>124</h1>
</div>
<script type="text/javascript">
    // 函数式的全局自定义指令
    // 参数一(name):自定义指令的名称
    // 参数二(hooks):自定义指令的钩子函数对象
    Vue.directive('blue', (el, binding) => {
        el.style.color = 'blue'
    })
    var vm = new Vue({
        el: "#container",
        data: {},
        directives: {
            // el:指令所绑定的元素,是真实 DOM节点,可以通过该元素来操作 DOM
            // binding:一个对象,里面包括很多属性,但是基本上只关注 value 属性,因为这是 指令绑定的值
            // vnode:Vue 编译生成的虚拟节点
            // prevNode:Vue 编译生成的上一个虚拟节点(仅在 update 和  componentUpdated 使用) 
            red(el, binding, vnode, prevNode) { // red:指定的是指令的名称,使用的时候要加前面加个v-,v-red
                el.style.color = 'red'
            }
        }
    });
</script>
</body>
</html>

事件修饰符(.stop、.prevent、 .capture 、.self)

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title></title>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
  <style type="text/css">
    .inner {
      width: 200px;
      height: 150px;
      background-color: darkcyan;
      margin: 10px;
    }

    .outer {
      padding: 40px;
      background: red;
    }
  </style>
</head>

<body>
  <div id="app">
    <!-- 使用  .stop  阻止冒泡  =>js event.stopPropagation()-->
    <div class="inner" @click="div1Handler">
      <input type="button" value="点击1" @click="btnHandler" />
      <input type="button" value="点击2" @click.stop="btnHandler" />
    </div>
    <hr>

    <!-- 使用  .provent 阻止默认行为 =>js e.preventDefault() -->
    <a href="http://baidu.com" @click="linkClick">百度一下1</a>
    <a href="http://baidu.com" @click.prevent="linkClick">百度一下2</a>
    <hr>
    <!-- 使用  .once  只触发一次事件处理函数 -->
    <!-- 即按照下面的例子,第一次低级不跳转,第二次才跳转 -->
    <a href="http://baidu.com" @click.prevent.once="linkClick">百度一下</a>
    <hr>

    <!--使用  .capture  实现捕获触发机制=>js element.addEventListener(event, function, useCapture)  -->
    <div class="inner" @click="div1Handler">
      <input type="button" value="点击1" @click="btnHandler" />
    </div>
    <div class="inner" @click.capture="div1Handler">
      <input type="button" value="点击2" @click="btnHandler" />
    </div>
    <hr>

    <!--   
        .self 实现只有点击当前元素(不是子元素)的时候,才能触发事件处理函数 
         与stop不同的是,self只只会阻止自身冒泡行为,并不会真正阻止冒泡  -->
    <div class="outer" @click="div2Handler">
      <div class="inner" @click="div1Handler">
        <input type="button" value="点击1" @click="btnHandler" />
      </div>
    </div>
    <div class="outer" @click="div2Handler">
      <div class="inner" @click.stop="div1Handler">
        <input type="button" value="点击2" @click="btnHandler" />
      </div>
    </div>
    <div class="outer" @click="div2Handler">
      <div class="inner" @click.self="div1Handler">
        <input type="button" value="点击3" @click="btnHandler" />
      </div>
    </div>
  </div>


  <script type="text/javascript">
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {
        div1Handler() {
          console.log('触发了inner div的点击事件');
        },
        div2Handler() {
          console.log('触发了outer div的点击事件');
        },
        btnHandler() {
          console.log('触发了bt按钮的点击事件');
        },
        linkClick() {
          console.log('触发了链接');
        }
      }
    })
  </script>

</body>
</html>

按键修饰符

根据键盘为按键自定义别名键盘码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container">
    <!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
    <!-- 
        .enter
        .tab
        .delete (捕获“删除”和“退格”键)
        .esc
        .space
        .up
        .down
        .left
        .right
     -->
    <!-- 根据已定义 -->
    <input v-on:keyup.enter="submit" value="回车触发">

    <!-- 根据按键编码 -->
    <!--[键盘码](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode)-->
    <input type="text" @keyup.115="submit" value="f4触发">

    <!-- 根据自定义 -->
    <input type="text" @keyup.f2="submit" value="f4触发">

    <!-- 组合键 -->
    <input type="text" @keyup.ctrl.65="submit" value="ctrl+A触发">
</div>

<script type="text/javascript">
    // 自定义全局按键修饰符
    Vue.config.keyCodes.f2 = 115
    //  注意版本,3版本创建方式不一样 
    var vm = new Vue({
        el: "#container",
        data: {},
        methods: {
            submit() {
                alert("提交成功")
            }
        }
    });
</script>
</body>

</html>

class与style绑定

我们可以使用mustache语法将Vue中data的数据绑定到HTML标签及标签的属性,如何将data中的值绑定到标签的class及style属性呢?

class绑定

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <style>
        .red {
            color: red;
        }

        .big {
            font-size: 28px;
        }

        .active {
            background-color: blue;
        }
    </style>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>

<body>
<div id="container">
    <!-- 第一种方式,直接传递一个数组;注意v-bind绑定 -->
    <p :class="className">vue使用数组 </p>
    <p :class="['red','big']">vue使用数组 </p>

    <!-- 在数组中使用三元表达式 -->
    <p :class="['red','big' , flag?'active':'']" @click="flag=!flag">vue使用数组,三元表达式 </p>

    <!-- 在数组中使用对象来代替三元表达式 -->
    <p :class="['red','big' , {'active' : flag}]" @click="flag=!flag">数组中使用对象来代替三元表达式</p>

    <!--在为 class 使用 v-bind 绑定 对象的时候,对象的属性是类名, 对象的属性可带引号,也可不带引号, 属性的值 是一个标识符-->
    <p :class="{red:true,big:flag,active:flag}" @click="flag=!flag">vue数组中使用对象,也可以直接在data中写一个属性来替代上述对象</p>

</div>

<script type="text/javascript">
    //  注意版本,3版本创建方式不一样 
    var vm = new Vue({
        el: "#container",
        data: {
            className: ['red', 'big'],
            flag: true

        }
    });
</script>
</body>
</html>

style绑定

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container">
    <!-- 第一种方式 -->
    <h1 :style="styleObj1">这是一个h1</h1>
    <!-- 第二种方式 -->
    <h1 :style="[ styleObj1, styleObj2 ]">这是一个h1</h1>
</div>
<script type="text/javascript">
    //  注意版本,3版本创建方式不一样 
    var vm = new Vue({
        el: "#container",
        data: {
            styleObj1: {color: 'red', 'font-weight': 200},
            styleObj2: {'font-style': 'italic'}
        }
    });
</script>
</body>
</html>

生命周期(钩子函数)

从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!

  • 生命周期钩子:就是生命周期事件的别名而已;
  • 生命周期钩子 = 生命周期函数 = 生命周期事件

整个流程分三个阶段

  • 创建(1-5)、运行(6)、销毁(7-8)

步骤

  1. 创建一个vue实例对象

  2. 初始化 一些默认的声明周期函数和默认的事件 => beforeCreate()

    这时候,data和methods中的数据都没初始化

  3. 初始化 数据 => created()

    data和methods中的数据都被初始化好了

  4. 编译模板 => beforeMount()

    <div id="app">{{msg}} </div> => 在内存中生成一个编译好的最终模板字符串 -> 把这个模板字符串渲染为内存中dom

    注意:只是在内存中渲染好了模板,并没有把模板挂载到页面上去,此时 页面还是旧的, 简单的说 结果就是在内存中渲染了一个 <div id="app">ok</div> 的dom元素,但是页面上还是 <div id="app">{{msg}} </div>

  5. 将编译好的模板真实提换到页面中去 => mounted()

    即 将内存中渲染好的dom元素即 < div id="app">ok< /div>已经 提换了页面上的 < div id="app">{{msg}} < /div>

  6. 当数据改变时 即完成data(model层) ->view(视图层)的更新

    1. 先在内存中渲染一份最新的dom树 => beforeUpdate()

    页面上的数据还是旧的,但是data中的数据都是最新的,页面和最新的数据尚未保存同步

    1. 将最新的dom树重新渲染到真实的页面上去 => updated()

      页面上的数据和data中的数据都是最新的,页面和最新的数据保存同步

  7. 销毁之前,实例上的data和所有methods,以及过滤器、指令。。。都处于可用状态,还未真正销毁 => beforeDestroy()

  8. 销毁,实例上的data和所有methods,以及过滤器、指令。。。都处于不可用状态,还未真正销毁 => destroyed()

<!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>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>

<body>
<div id="app">
    <input type="button" value="修改msg" @click="msg='No'">
    <h3 id="h3">{{ msg }}</h3>
</div>

<script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'ok'
        },
        methods: {
            show() {
                console.log('执行了show方法')
            }
        },
        beforeCreate() { // 这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
            // console.log(this.msg)
            // this.show()
            // 注意: 在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
        },
        created() { // 这是遇到的第二个生命周期函数
            // console.log(this.msg)
            // this.show()
            //  在 created 中,data 和 methods 都已经被初始化好了!
            // 如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
        },
        beforeMount() { // 这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把 模板渲染到 页面中
            // console.log(document.getElementById('h3').innerText)
            // 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
        },
        mounted() { // 这是遇到的第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
            // console.log(document.getElementById('h3').innerText)
            // 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
        },
        // 接下来的是运行中的两个事件
        beforeUpdate() { // 这时候,表示 我们的界面还没有被更新【数据被更新了吗?  数据肯定被更新了】
            /* console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
            console.log('data 中的 msg 数据是:' + this.msg) */
            // 得出结论: 当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
        },
        updated() {
            console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
            console.log('data 中的 msg 数据是:' + this.msg)
            // updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
        }
    });
</script>
</body>

</html>

过滤器

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container">
    <p>原始字符串:{{str}}</p>
    <!-- 滤器A过滤完的数据会返回值传入过滤器B -->
    <p>局部过滤字符串:{{str | filterA | filterB | filterC}}</p>
</div>

<script type="text/javascript">
    // 全局过滤器
    Vue.filter("filterC", function (value) {
        return value + "《这是全局过滤器》"; // 小写转为大写
    });
    //  注意版本,3版本创建方式不一样 
    var vm = new Vue({
        el: "#container",
        data: {
            str: "全世界最帅的人是谁"
        },
        // 私有过滤器
        filters: {
            filterA(val) {
                return val + "《就是我》"
            },
            filterB(val) {
                return val + "《还有谁》"
            }
        }
    });
</script>
</body>
</html>

计算属性和侦听器(监听器)

计算属性

属性可以通过在data中声明获得,也可以通过在computed通过计算获得

特性:计算属性所依赖的属性值发生变化会影响计算属性的值同时发生变化

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container">
    <input type="text" v-model="str1"/><br/>
    <input type="text" v-model="str2"/><br/>
    {{totalStr}}
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#container",
        data: {
            str1: "张三",
            str2: "李四",
        },
        computed: {
            totalStr: function () {
                return "computed:" + this.str1 + this.str2;
            }
        }
    });
</script>
</body>
</html>

侦听器

侦听器,就是data中属性的监听器,当data中的属性值发生变化就会触发侦听器函数的执行

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>
<body>
<div id="container">
    <input type="text" v-model="str1"/><br/>
    {{str2}}
    <hr>
    <input type="text" v-model="user.name"/><br/>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#container",
        data: {
            str1: "张三",
            str2: "李四",
            user: {
                name: "王二麻子"
            }
        },
        watch: {
            str1(newVal, oldVal) {
                console.log("监听str1的变换", newVal, oldVal)
                this.str2 = this.str1 + ":数量为" + this.str1.length;
            },
            user: {
                deep: true,
                handler(newVal, oldVal) {
                    console.log("已改变的值=>", newVal);
                    console.log("改变前的值=>", oldVal);
                }
            },
            "user.name"(newVal, oldVal) {
                console.log("监听user.name的变换", newVal, oldVal)
            },

        }
    });
</script>
</body>
</html>

axios快速入门使用

起步 | Axios 中文文档 | Axios 中文网 (axios-http.cn)

Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。是一个专注于异步通信的js框架来使用

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>

<body>


<script type="text/javascript">
    //     axios.request(config)
    //     axios.get(url[, config])
    //     axios.delete(url[, config])
    //     axios.head(url[, config])
    //     axios.options(url[, config])
    //     axios.post(url[, data[, config]])
    //     axios.put(url[, data[, config]])
    //     axios.patch(url[, data[, config]])

    axios.defaults.baseURL = 'https://api-hmugo-web.itheima.net';
    url = '/api/public/v1/goods/search'
    axios({
        method: 'get',
        url,  // baseurl+url => https://api-hmugo-web.itheima.net/api/public/v1/goods/search
        params: { // 头部参数
            pagenum: 2
        },
        headers: {
            //设置请求头
        },
        data: {
            //设置请求体(post/put)
        }
    }).then(function (response) {
        console.log(response)
    }).catch(function (error) {
        // 处理错误情况
        console.log("处理错误情况", error);
    });
    axios.get(url, {
        params: {
            pagenum: 3
        }
    }).then(function (response) {
        // 处理成功情况
        console.log(response);
    })


    // 箭头使用
    axios.get(url).then((res) => {
        console.log(res)
    });
    //发送异步请求
    axios.all([listMusics(), getMusicDetail()]).then(axios.spread(function (r1, r2) {
        // 两个请求现在都执行完成
        console.log(r1);
        console.log(r2);
    }));

    function listMusics() {
        return axios.get("https://api-hmugo-web.itheima.net/api/public/v1/goods/search?pagenum=4");
    }

    function getMusicDetail() {
        return axios.get("https://api-hmugo-web.itheima.net/api/public/v1/goods/search?pagenum=5");
    }
</script>
</body>

</html>

组件

组件快速入门

自定义组件的结构

  • data 定义组件的模板渲染的数据

    • 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法

      组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;

      组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!

    • 为什么组件data必须是一个函数?

      因为 如果data是一个对象的话,当有多个这个组件的实例时,这些多个组件在实例化时,都调用了这同一个对象,那么这个data的状态会共享在别的组件中, 如果data是一个函数,vue会 生成一个新的对象,这个对象只属于当前初始化的vm实例 防止了data状态共享在别的组件中的情况

  • template 组件的HTML模块(HTML标签\css样式)

  • methods 定义组件中的标签事件绑定的JS函数

<!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>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>

</head>

<body>
<div id="app">
    <!-- 如果要使用组件,直接,把组件的名称,以 HTML 标签的形式,引入到页面中,即可 -->
    <publiccom1></publiccom1>
    <publiccom2></publiccom2>
    <publiccom3></publiccom3>
    <private-com></private-com>
</div>
<template id="tmpl">
    <div><span>模板组件</span><span>(ok)</span></div>
</template>
<script>
    // 如果使用Vue.component定义全局组件的时候,组件名称使用了驼峰命名,则在引用组件的时候,  需要把
    // 大写的驼峰改为小写的字母,同时,两个单词之前,使用 - 链接;如果不使用驼峰, 则直接拿名称来使用即可;
    //
    // 注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容,必须有且只能有唯一的一个根元素

    Vue.component('publiccom1', Vue.extend({
        template: '<h3>全局组件1</h3>'
    }))

    Vue.component('publiccom2', {
        data:function(){
            return {
                showtitle: '《提示按钮》'
            }
        },
        methods: {
            test: function() {
                console.log(this.showtitle);
                this.showtitle = "点击完成"
            }
        },
        template: '<div><span>全局组件2</span><span @click="test()">{{showtitle}}</span></div>'
    })
    
    Vue.component('publiccom3', {
        template: '#tmpl'
    })

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        components: { // 定义实例内部私有组件的
            privateCom: {
                template: '#tmpl'
            }
        },
    });
</script>
</body>

</html>

组件的复用

index.html
<!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">
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>
</head>

<body>
<div id="app">
    <publiccom1></publiccom1>
</div>
<script src="./my-components.js"></script>
<script>
    var vm = new Vue({
        el: '#app'
    });
</script>
</body>
</html>
my-components.js
Vue.component('publiccom1', Vue.extend({
    template: '<h3>全局组件1</h3>'
}))

组件通信

Vue实例本身就是一个组件(模板就是el指定容器 ,data就是组件数据,methods就是组件的事件函数) 在Vue实例指定的el容器中引用的组件称为子组 ,当前Vue实例就是父组件

父传子

Vue实例引用组件的时候,传递数据到引用的组件中


<!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>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>

</head>

<body>
<div id="app">
    <!-- 父组件,可以在引用子组件的时候, 通过 属性绑定(v-bind:) 的形式, 自定义一个属性
      把 需要传递给 子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
    <com1 v-bind:parentmsg="msg"></com1>
</div>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            msg: ' 这是父组件中的数据'
        },
        components: {
            // 结论:经过演示,发现,子组件中,默认无法访问到 父组件中的 data 上的数据 和 methods 中的方法
            com1: {
                data() { // 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,
                    // 比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
                    // data 上的数据,都是可读可写的;
                    return {
                        title: '子组件title',
                        content: 'container'
                    }
                },
                template: '<h1 @click="change">这是子组件,子组件内容为{{this.content}} --- {{ parentmsg }}</h1>',
                // 注意: 组件中的 所有 props 中的数据,都是通过 父组件传递给子组件的
                // props 中的数据,都是只读的,无法重新赋值
                props: ['parentmsg'], // 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
                methods: {
                    change() {
                        this.parentmsg = this.title
                    }
                }
            }
        }
    });
</script>
</body>

</html>

image-20230918233435582

子传父(父组件向子组件传递方法)

通过子组件的按钮“调用”父组件的函数,通过函数传值

通过this.$emit调用父组件方法

<!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>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.4.0/vue.js"></script>

</head>
<body>
<div id="app">
    <!-- 父组件向子组件 传递 方法,使用的是 事件绑定机制; v-on, 当我们自定义了 一个 事件属性之后,
      那么,子组件就能够,通过某些方式,来调用 传递进去的 这个 方法了 -->
    <com2 @func="show"></com2>
</div>
<script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el: '#app',
        data: {
            datamsgFormSon: null
        },
        methods: {
            show(data) {
                console.log('调用了父组件身上的 show 方法: --- ' + data)
                console.log(data);
                this.datamsgFormSon = data
            }
        },
        components: {
            com2: {
                template: `
                  <div>
                  <h1>这是子组件</h1>
                  <input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick">
                  </div>`,
                data() {
                    return {
                        sonmsg: {
                            name: '小头儿子',
                            age: 6
                        }
                    }
                },
                methods: {
                    myclick() {
                        // 当点击子组件的按钮的时候,如何 拿到 父组件传递过来的 func 方法,并调用这个方法???
                        //  emit 英文原意: 是触发,调用、发射的意思
                        // this.$emit('func123', 123, 456)
                        this.$emit('func', this.sonmsg.name)
                    }
                }
            }
        }
    });
</script>
</body>
</html>

image-20230918233936789

组件插槽

当我们自定义Vue组件时,允许组件中的部分内容在调用组件时进行定义——插槽

如果现在事先模板中不知道需要什么内容,需要在使用时传递就可以使用插槽with来实现,这种效果!

基本使用
  • 在自定义组件时通过slot标签在组件的模版中定义插槽

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    
    <body>
    <div id="app">
    
        <!-- 
          如果现在事先模板中不知道需要什么内容,需要在使用时传递就可以使用插槽with来实现,这种效果! 
         -->
        <cpn></cpn>   <!-- 和模板一样 -->
    
        <cpn><span>哈哈哈</span></cpn>    <!-- 替换了模板中的solt中的dom元素 -->
        <cpn><i>呵呵呵</i></cpn>
    
        <cpn>
            <i>呵呵呵</i>
            <div>我是div元素</div>
            <p>我是p元素</p>
        </cpn>
    
    
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            },
            components: {
                cpn: {
                    template: `
                                <div>
                                  <p>我是组件</p>
                                  <slot><button>按钮</button></slot>
                                  <hr>
                                </div>`
                }
            }
        })
    </script>
    </body>
    </html>
    
    
具名插槽

当组件中的插槽数量>1时,需要给组件中的slot标签添加name属性指定插槽的名字

  • 定义组件:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="app">
        <cpn></cpn>
        <cpn><span slot="center">这是替换name=center的文字</span></cpn>
        <cpn>
            <button slot="left">这是替换name=left的文字</button>
        </cpn>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            },
            components: {
                cpn: {
                    template: `
                <div>
                  <slot name="left"><span>左边</span></slot>
                  <slot name="center"><span>中间</span></slot>
                  <slot name="right"><span>右边</span></slot>
                  <hr>
                </div>`
                }
            }
        })
    </script>
    </body>
    </html>
    
    
插槽作用域
  • 作用域插槽其实就是带数据的插槽,即带参数的插槽,简单的来说就是子组件提供给父组件的参数,该参数仅限于插槽中使用,父组件可根据子组件传过来的插槽数据来进行不同的方式展现和填充插槽内容。
  • 定义组件时,将子组件中的数据绑定到slot标签
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <mypage>
        <template slot="slot1">
            <h1>slot1</h1>
        </template>
        <template slot="slot2" slot-scope="res">
            <div v-for="item in res.users">
                {{item.id}}--{{item.name}}--{{item.age}}
            </div>
        </template>
    </mypage>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    Vue.component("mypage", {
        data: function () {
            return {
                users: [
                    {
                        id: 1,
                        name: "tom",
                        age: 10,
                        addr: "QD"
                    }, {
                        id: 2,
                        name: "bob",
                        age: 20,
                        addr: "QD"
                    }, {
                        id: 3,
                        name: "peter",
                        age: 15,
                        addr: "QD"
                    }
                ]
            }
        },
        template: `
          <div>
          <header>顶部</header>
          <div style="height: 300px;">
            <slot name="slot1"></slot>
            <slot name="slot2" v-bind:users="users"></slot>
          </div>
          <div>底部</div>
          </div>
        `
    })
    const app = new Vue({
        el: '#app'
    })
</script>
</body>
</html>

路由 router

router是由Vue官方提供的用于实现组件跳转的插件

13.1 路由插件的引用

13.3.1 离线
<script type="text/javascript" src="js/Vue.js" ></script>
<script type="text/javascript" src="js/Vue-router.js"></script>
13.3.2 在线CDN
<script src="https://unpkg.com/Vue/dist/Vue.js"></script>
<script src="https://unpkg.com/Vue-router/dist/Vue-router.js"></script>

13.2 路由使用案例

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            body{padding: 0px;margin: 0px;}
            ul{list-style: none;}
            ul li{display: inline; float: left; margin-left: 15px;  margin-bottom: 15px;}
            ul li a{text-decoration: none; color: white; font-size: 18px; font-weight: bold;}
            ul li a:hover{color: yellow;}
        </style>
        <script type="text/javascript" src="js/Vue.js" ></script>
        <script type="text/javascript" src="js/Vue-router.js"></script>
    </head>
    <body>
        
        <div id="container">
            <div style="width: 100%; height: 70px; background: #00BFFF;">
                <table>
                    <tr>
                    <td><img src="img/logo.png" height="70" style="margin-left:100px;"/></td>
                        <td>
                            <ul>
                                <li><router-link to="/a">首页</router-link></li>
                                <li><router-link to="/b">Java</router-link></li>
                                <li><router-link to="/c">HTML5</router-link></li>
                                <li><router-link to="/d">Python</router-link></li>
                            </ul>
                        </td>
                    </tr>
                </table>
            </div>
            <div style="width: 100%; height: 680px; background: lemonchiffon;">
                <router-view></router-view>
            </div>
        </div>
        <script type="text/javascript">
            // Vue的路由旨在为单页面应用开发提供便捷
            //1.定义链接跳转的模板(组件)
            const t1 = {template:`<p>index</p>`};
            const t2 = {template:`<p>Java</p>`};
            const t3 = {template:`<p>HTML5</p>`};
            const t4 = {template:`<p>PYTHON</p>`};
            
            const myrouter = new VueRouter({
                routes:[
                    {path:"/a",component:t1},
                    {path:"/b",component:t2},
                    {path:"/c",component:t3},
                    {path:"/d",component:t4}
                ]
            });
            
            var vm = new Vue({
                el:"#container",
                router:myrouter
            });
        </script>
        
    </body>
</html>

13.3 动态路由匹配

13.3.1 通配符

*可以匹配任意路径

例如:

  • /user-* 匹配所有以user-开头的任意路径
  • /* 匹配所有路径
const myrouter = new VueRouter({
    routes:[
        {path:"/user-*",component:...},
        {path:"/*",component:...}
    ]
});

注意如果使用通配符定义路径,需要注意路由声明的顺序

13.3.2 路由参数
  • /a/:id 可以匹配 /a/开头的路径
<div id="container">
    <li><router-link to="/a/101">首页</router-link></li>
    <router-view></router-view>
</div>
    
<script type="text/javascript">
    const t1 = {template:`<p>index:{{$route.params.id}}</p>`};

    const myrouter = new VueRouter({
        routes:[
            {path:"/a/:id",component:t1}
        ]
    });

    var vm = new Vue({
        el:"#container",
        router:myrouter
    });
</script>
13.3.3 优先级

如果一个路径匹配了多个路由,则按照路由的配置顺序:路由定义的越早优先级就越高。

13.4 嵌套路由

在一级路由的组件中显示二级路由

<div id="container">
    <router-link to="/a">首页</router-link>
    <router-link to="/a/c1">首页-c1</router-link>
    <router-link to="/a/c2">首页-c2</router-link>
    <router-view></router-view>
</div>
<script type="text/javascript">
    const t1 = {
        template:"<div style='width:400px; height:200px; border:blue 1px solid'>index<hr/><router-view></router-view></div>"
    };

    const t2 = {template:`<div>t2</div>`};
    const t3 = {template:`<div>t3</div>`};


    const myrouter = new VueRouter({
        routes:[
            {
                path:"/a",
                component:t1,
                children:[
                    {
                        path:"c1",
                        component:t2
                    },
                    {
                        path:"c2",
                        component:t3
                    }
                ]
            }
        ]
    });

    var vm = new Vue({
        el:"#container",
        router:myrouter
    });
</script>

13.5 编程式导航

13.5.1 push()
<div id="container">
    <button type="button" @click="test">按钮</button>
    <router-view></router-view>
</div>
<script type="text/javascript">
    const t1 = {
        template:"<div style='width:400px; height:200px; border:blue 1px solid'>index</div>"
    };

    const myrouter = new VueRouter({
        routes:[
            {
                path:"/a",
                component:t1
            }
        ]
    });

    var vm = new Vue({
        el:"#container",
        router:myrouter,
        methods:{
            test:function(){
                //js代码实现路由跳转:编程式导航
                myrouter.push("/a");
            }
        }
    });
</script>
13.5.2 push()参数
//1.字符串
myrouter.push("/a");

//2.对象
myrouter.push({path:"/a"});

//3.命名的路由  name参数指的是定义路由时指定的名字
myrouter.push({name:"r1",params:{id:101}});

//4.URL传值,相当于/a?id=101
myrouter.push({path:"/a",query:{id:101}});
13.5.3 replace()

功能与push一致,区别在于replace()不会向history添加新的浏览记录

13.5.4 go()

参数为一个整数,表示在浏览器历史记录中前后/后退多少步 相当于window.history.go(-1)的作用

13.6 命名路由

命名路由:在定义路由的时候可以给路由指定name,我们在进行路由导航时可以通过路由的名字导航

<div id="container">
    <input type="text" v-model="rname"/>
    <router-link :to="{name:rname}">t1</router-link>
    <button type="button" @click="test">按钮1</button>
    <router-view></router-view>
</div>
<script type="text/javascript">
    const t1 = {
        template:"<div style='width:400px; height:200px; border:blue 1px solid'>t1</div>"
    };

    const t2 = {
        template:"<div style='width:400px; height:200px; border:red 1px solid'>t2</div>"
    };

    const myrouter = new VueRouter({
        routes:[
            {
                path:"/a",
                name:"r1",
                component:t1
            },
            {
                path:"/b",
                name:"r2",
                component:t2
            }
        ]
    });

    var vm = new Vue({
        el:"#container",
        data:{
            rname:"r1"
        },
        router:myrouter,
        methods:{
            test:function(){
                myrouter.push({name:vm.rname});
            }
        }
    });
</script>

13.7 命名路由视图

<div id="container">
    <router-link to="/a">t1</router-link>
    <router-link to="/b">t2</router-link>

    <!--路由视图-->
    <!--如果在HTML中有一个以上的路由视图router-view,需要给router-view指定name,在路由中使用components映射多个组件根据name设置组件与router-view绑定关系-->
    <router-view name="v1"></router-view>
    <router-view name="v2"></router-view>
</div>
<script type="text/javascript">
    const t11 = {
        template:"<div style='width:400px; height:200px; border:blue 1px solid'>t11</div>"
    };
    const t12 = {
        template:"<div style='width:400px; height:200px; background:pink'>t12</div>"
    };

    const t21 = {
        template:"<div style='width:400px; height:200px; border:red 1px solid'>t21</div>"
    };
    const t22 = {
        template:"<div style='width:400px; height:200px; background:yellow'>t22</div>"
    };

    const myrouter = new VueRouter({
        routes:[
            {
                path:"/a",
                components:{
                    v1:t11,
                    v2:t12
                }
            },
            {
                path:"/b",
                components:{
                    v1:t21,
                    v2:t22
                }
            }
        ]
    });

    var vm = new Vue({
        el:"#container",
        router:myrouter
    });
</script>

13.8 重定向和别名

13.8.1 重定向

访问/b,重定向到/a

<div id="container">
    <router-link to="/a">路径A</router-link>
    <router-link to="/b">路径B</router-link>
    <router-view></router-view>
</div>
<script type="text/javascript">
    const t1 = {
        template:"<div style='width:400px; height:200px; border:blue 1px solid'>index</div>"
    };

    const myrouter = new VueRouter({
        routes:[
            {
                path:"/a",
                component:t1
            },
            {
                path:"/b",
                redirect:"/a"
            }
        ]
    });

    var vm = new Vue({
        el:"#container",
        router:myrouter
    });
</script>
  • 根据路由命名重定向
const myrouter = new VueRouter({
    routes:[
        {
            path:"/a",
            name:"r1",
            component:t1
        },
        {
            path:"/b",
            //redirect:"/a"   //根据路由路径重定向
            redirect:{name:"r1"}  //根据路由命名重定向
        }
    ]
});
13.8.2 路由别名
<div id="container">
    <router-link to="/a">路径A</router-link>
    <router-link to="/wahaha">路径wahaha(别名)</router-link>
    <router-view></router-view>
</div>
<script type="text/javascript">
    const t1 = {
        template:"<div style='width:400px; height:200px; border:blue 1px solid'>index</div>"
    };

    const myrouter = new VueRouter({
        routes:[
            {
                path:"/a",
                alias:"/wahaha",
                component:t1
            }
        ]
    });

    var vm = new Vue({
        el:"#container",
        router:myrouter
    });
</script>

13.9 路由组件传参

可以通过/url/:attr方式实现通过路由传值给组件

<div id="container">
    <router-link to="/a/101">路径A</router-link>
<router-view></router-view>
</div>
<script type="text/javascript">
    const t1 = {
        template:`<div style='width:400px; height:200px; border:blue 1px solid'>
                    index:{{$route.params.id}}
                    </div>`
          };

        const myrouter = new VueRouter({
            routes:[
                {
                    path:"/a/:id",
                    component:t1
                }
            ]
        });

        var vm = new Vue({
            el:"#container",
            router:myrouter
        });
</script>
通过props传参
<div id="container">
    <router-link to="/a/102">路径A</router-link>
    <router-view></router-view>
</div>
<script type="text/javascript">
    const t1 = {
        props:["id"],
        template:`<div style='width:400px; height:200px; border:blue 1px solid'>
                index:{{id}}
                </div>`
    };

    const myrouter = new VueRouter({
        routes:[
            {
                path:"/a/:id",
                props:true,
                component:t1

            }
        ]
    });

    var vm = new Vue({
        el:"#container",
        router:myrouter
    });
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值