万字vue笔记!从0到脚手架

模板语法

vue模板语法有2大类

​ 1.插值语法:

​ 功能:用于解析标签体内容

​ 写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性

​ 2.指令语法:

​ 功能:用于解析标签(标签属性,标签体内容,绑定事件)

​ 举例:v-bind:href=“xxx” 或简写成 :href=“xxx” 同样要写js表达式,且可以直接读取到data中的所有属性

数据绑定

Vue中有两种数据绑定的方式

​ 1.单向绑定(v-bind):数据只能从data流向页面

​ 2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data

​ 备注:

​ 双向绑定一般都应用在表单类元素上

el与data的两种写法

1.el有两种写法

  • new Vue 时候配置el属性
  • 先创建Vue实例,随后再通过vm.$mount("//el的值")指定el的值

2.data的两种写法

  • 对象式

  • 函数式

    如何选择:目前哪种写法都可以,学到组件的时候必须用函数式,否则会报错

3.一个重要的原则

  • 由Vue管理的函数一定不要写箭头函数,一旦写了箭头函数,this就不再是vue实例了

el

第一种

<div id="app">{{name}}</div>
    <script>
        const vm = new Vue({
            el:"#app",
            data:{
                name:"y"
            },
        })
    </script>

第二种

更加灵活

<div id="app">{{name}}</div>
    <script>
        const vm = new Vue({
            data:{
                name:"y"
            },
        })
        v.$mount("#app")
    </script>

data

第一种写法:对象式

<div id="app">{{name}}</div>
    <script>
        const v = new Vue({
            el:"#app",
            data:{
                name:"y"
            },
        })
    </script>

第二种写法:函数式

在组件中使用,vue帮忙调用

<div id="app">{{name}}</div>
    <script>
        const v = new Vue({
            el:"#app",
            data(){
                //此处的this指向的是vue实例对象
                //不能使用箭头函数,必须使用普通函数
                return{
                    name:"y"
                }
            },
        })
    </script>

理解MVVM模型

vue没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。

MVVM模型

  • M 模型(model) : 对应data中的数据
  • V 视图(view) : 模板
  • VM 视图模型(viewmodel) : Vue实例对象

最后发现:

  • data中的所有属性,最后都出现在了vm身上
  • vm身上所有的属性及vue原型上的所有属性,在Vue模板中都可以直接使用

image

  • Data Bindings : 将数据绑定到视图上
  • DOM Listeners : 对DOM的监听,数据的改变映射到视图上

接下来用一段代码来解释MVVM

    <!-- 这里是视图View -->
    <div id="app">
        <!-- {{}} 花括号里只要是 vm 里面有的都能写 -->
        <h1>学校名称:{{name}}</h1>
        <h1>学校地址:{{address}}</h1>
        <h1>测试一下:{{$options}}</h1>
    </div>
    <!-- ----------------------- -->
    
    
    <script>
    //-----------------这里是视图模型ViewModel------------
        const vm = new Vue({
    //----------------------------------------------------
        
            el:"#app",
            data:{
    //-----------------这里模型Model----------------------
                //最后都出现在 vm 里
                name:"QX",
                address:"福建"
    //----------------------------------------------------
            },
        })
        v.$mount("#app")
    </script>

数据代理

Object.defineproperty

用于给一个对象定义属性用

    <script>
        let person = {
            name:"张三",
            sex:"男",
            age:18  //使用这种方法添加的控制台输出的颜色较深
        }
        //给person添加一个age,值为18
        //使用这种方法添加的控制台输出的颜色较浅
        Object.defineProperty(person,"age",{
            value:18
        })
        console.log(person);
    </script>
    

使用这种方法添加的控制台输出的颜色较深

    let number = 18
    let person = {
            name:"张三",
            sex:"男",
            age:18  
        } 

在这里插入图片描述


使用这种方法添加的控制台输出的颜色较浅

它想表达的是age是不可被枚举的,age是不参与遍历的,不能修改,也不能删除

    Object.defineProperty(person,"age",{
        value:18,
        //如果想让这个数据可以被遍历添加enumerable配置项
        enumerable:true,  //控制属性是否可以被枚举,默认值false
        writable:true,   //控制属性是否可以被修改,默认值是false
        configurable:true  //控制属性是否可以被删除,默认值是false
    })

在这里插入图片描述


Object.defineproperty的get

 Object.defineProperty(person,"age",{
            //value:18,
            
            //当有人读取person的age属性时,get函数就会被调用,且返回值就是age的值
            get(){
                return number
            },
            
            //当有人修改person的age属性时,get函数就会被调用,且会收到修改的具体的值
            set(value){
                console.log("有人修改了age,值是:",value)
            }
            
        })
        console.log(person)

此时控制台输出的内容为

在这里插入图片描述

  • name和sex为正常的值,age用…代替
  • get age:f() 每次访问age都会触发get函数的调用

Object.defineproperty的set

 Object.defineProperty(person,"age",{

            //当有人修改person的age属性时,get函数就会被调用,且会收到修改的具体的值
            set(value){
                console.log("有人修改了age,值是:",value)
                number = value
            }
            
        })
        console.log(person)

在这里插入图片描述

理解数据代理

数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)

<script>  
        let obj = {x:100}
        let obj2 = {y:200}
        //通过obj2读取和修改obj的x

        Object.defineProperty(obj2,'x',{
            get(){
                return obj.x
            },
            set(value){
                obj.x = value
            }
        })
        
        //这是最简单的数据代理
    </script>
    

在这里插入图片描述


vue中的数据代理

  • Vue中的数据代理:通过vm对象来代理data中属性的操作(读/写)
  • Vue中数据代理的好处:更加方便的操作data中的数据
  • 基本原理:通过Object.defineProperty()把data对象中所有的属性添加到vm上。为每一个添加到vm上的属性都指定一个getter/setter。在getter/setter内部去操作(读/写)data中对应的属性
div id="app">
        <h1>学校名称:{{name}}</h1>
        <h1>学校地址:{{address}}</h1>
    </div>
    
    
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                name:"QX",
                address:"福建"
            }
        })
        console.log(vm);
    </script>

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

事件处理

点击事件

  • 事件的回调需要配置在methods对象中,最终会在vm上
  • methods中配置的函数不要使用箭头函数,否则this就不是vm或组件实例对象而是window
  • @click=“demo” 和 @click=“demo($event)” 效果一致,但是后者可以传参
div id="app">
        <h1>欢迎来到:{{name}}</h1>
        <!-- @xxx="yyy"yyy也可以是简单的语句 -->
        <button @click="showInfo">点我提示信息</button>
        <button @click="showInfo2(123,$event)">点我提示信息</button>
    </div>
    
    
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                name:"QX",

            },
            methods: {
                showInfo(){
                //这里的this指向的是vm(Vue实例对象)
                //如果用的是箭头函数,指向的就是window
                    alert("同学你好")
                },
                showInfo2(num,event){
                //$event就是event事件
                    console.log(num,event.target.innerText)
                }
            },
        })
    </script>

事件修饰符

  • vue中的事件修饰符 修饰符可以连续写
  • prevent:阻止默认事件(常用)
  • stop:阻止事件冒泡(常用)
  • once:事件只触发一次(常用)
  • capture:使用事件的捕获模式
  • self:只有event.target是当前的操作元素时才触发事件
  • passive:事件的默认行为立即执行,无需等待事件回调执行完毕
<div id="app">
        <h1>欢迎来到:{{name}}</h1>
        <!-- prevent阻止默认事件 -->
        <a href="https://www.baidu.com/" @click.prevent="showInfo">点我提示信息</a>

        <!-- 阻止事件冒泡 -->
        <!-- 点击按钮将会跳出两次弹框,添加stop后只会弹出一次-->
        <div class="demo1" @click="showInfo">
            <button @click.stop="showInfo">点我提示信息</button>
        </div>
        
        <!-- 事件只触发一次 -->
        <!-- 弹框只会出现一次 -->
         <button @click.once="showInfo">点我提示信息</button>
    </div>
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                name:"QX",

            },
            methods: {
                showInfo(){
                    alert("你好")
                }
            },
        })
    </script>

键盘事件

  • 1.Vue中常用的按键名 后面可以跟上其他按键组合一起的使用
  • 回车 => enter
  • 删除 => delete (捕获删除和退格键)
  • 退出 => esc
  • 空格 => space
  • 换行 => tab 必须配合keydown使用
  • 上 => up
  • 下 => down
  • 左 => left
  • 右 => right
  • 2.vue未提供别名的按键,可以使用按键原始的key去绑定,但注意要转为kebab-case(短横线命名)
  • 3.系统修饰键(用法特殊):ctrl,alt,shift,meta
  • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才能触发
  • 配合keydown使用:正常触发事件
  • 4.也可以使用keyCode去指定具体的按键(不推荐)
  • 5.Vue.config.keyCodes.自定义键名 = 键码 ,可以去定制按键别名
<div id="app">
        <h1>欢迎来到:{{name}}</h1>
        <!-- keyup表示按下按键,enter表示按下回车键 -->
        <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">
        <input type="text" placeholder="按下回车提示输入" @keyup.huiche="showInfo">
    </div>
    <script>  
    Vue.config.keyCodes.huiche = 13  //定义了一个别名回车
       const vm = new Vue({
            el:"#app",
            data:{
                name:"QX",

            },
            methods: {
                showInfo(e){
                        console.log(e.target.value);
                        //输出的是按键的名字和值
                        console.log(e.key,e.keyCode); 
                }
            },
        })
    </script>

计算属性

  • 计算属性:
  • 1.定义:要用的属性不存在,要通过已有的属性计算得来
  • 2.原理:底层借助了Object.defineproperty方法提供的getter和setter
  • 3.get函数什么时候执行?
  • 初次读取的时候会执行一次
  • 当依赖的数据发生改变时会被再次调用
  • 4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
  • 5.计算属性最终会出现在vm上,直接读取使用即可

姓名案例(插值语法实现)

<div id="app">
        姓:<input type="text" v-model="firstname"><br>
        名:<input type="text" v-model="lastname"><br>
        <!-- slice(0,3)表示只要第0位到第2位,不包括第三位 -->
        姓名:<span>{{firstname.slice(0,3)}}-{{lastname}}</span>
    </div>
    <script>  
    Vue.config.keyCodes.huiche = 13  //定义了一个别名回车
       const vm = new Vue({
            el:"#app",
            data:{
                firstname:"张",
                lastname:"三"
            }
        })
    </script>

姓名案例(methods实现)

<div id="app">
        姓:<input type="text" v-model="firstname"><br>
        名:<input type="text" v-model="lastname"><br>
        姓名:<span>{{fullname()}}</span>
    </div>
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                firstname:"张",
                lastname:"三"
            },
            methods: {
                fullname(){
                    return this.firstname + '-' + this.lastname
                }
            },
        })
    </script>

姓名案例(计算属性实现)

<div id="app">
        姓:<input type="text" v-model="firstname"><br>
        名:<input type="text" v-model="lastname"><br>
        姓名:<span>{{fullname}}</span>
    </div>
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                firstname:"张",
                lastname:"三"
            },
            computed:{
                //fullname:{
                    //get的作用是:当有人读取fullname时,get就会被调用,且返回值就作为fullname的值
                    //get什么时候调用?1.初次读取fullname时  2.所依赖的数据发生变化时
                    //这里不能写成箭头函数
                    //get(){
                        //console.log("get被调用了");
                        //return this.firstname + '-' + this.lastname
                    //},
                    //set什么时候调用? 当fullname被修改时
                    //set(value){
                    //	console.log(value);
                    //	const arr = value.split('-')
                    //	this.firstname = arr[0]
                    //	this.lastname = arr[1]
                    //}
                    
                //},

				//这是简写的方式,计算属性一般不需要修改,需要修改的时候就不能使用简写
                fullname(){
                    return this.firstname + '-' + this.lastname
                }
            }
        })
    </script>

在这里插入图片描述

监视属性

  • 监视属性watch:
  • 1.当被监视的属性变化时,回调函数自动调用,进行相关操作
  • 2.监视的属性必须存在才能进行监视
  • 3.监视的两种写法:
  • new Vue时传入watch配置
  • 通过vm.$watch监视

天气案例

<div id="app">
        <h2>今天天气很{{info}}</h2>
        <button @click="changeWeather">切换天气</button>
    </div>
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                isHot:true,

            },
            methods:{
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
            computed:{
                info(){
                    return this.isHot ? '炎热':'凉爽'
                }
            },
            watch:{
                //计算属性也能监测 例:info
                //正常写法
                //isHot:{
                    //在isHot被修改的时候调用
                //    handler(newValue,oldValue){
                //        console.log("isHot被修改了",newValue,oldValue);
                //    }
                //}
                
                //简写
                isHot(newValue,oldValue){
                    console.log("isHot被修改了",newValue,oldValue);
                }
            }
        })

        //第二种监测方法
        // vm.$watch("isHot",{
        //     handler(newValue,oldValue){
        //                 console.log("isHot被修改了",newValue,oldValue);
        //             }
        // })
        
        
        //简写
        //vm.$watch("isHot",function(newValue,oldValue){
        //	console.log("isHot被修改了",newValue,oldValue);
        //})
    </script>

10.png

深度监视

  • 深度监视:
  • Vue中的watch默认不检测对象内部值的改变(一层)
  • 配置deep:true可以监测对象内部值改变(多层)
  • 备注:
  • Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
  • 使用watch时根据数据的具体结构来决定是否采用深度监视
<div id="app">
        <h2>今天天气很{{info}}</h2>
        <button @click="changeWeather">切换天气</button>
        <hr>
        <h3>a的值是:{{number.a}}</h3>
        <button @click="number.a++">点我让a+1</button>
        <h3>b的值是:{{number.b}}</h3>
        <button @click="number.b++">点我让b+1</button>
    </div>
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                isHot:true,
                number:{
                	a:1,
                	b:1
                }

            },
            methods:{
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
            computed:{
                info(){
                    return this.isHot ? '炎热':'凉爽'
                }
            },
            watch:{
                //属性和计算属性都能监测
                isHot:{
                    //在isHot被修改的时候调用
                    handler(newValue,oldValue){
                        console.log("isHot被修改了",newValue,oldValue);
                    }
                },
                number:{
                    //开启深度监视,监视多级结构中所有属性的变化,没有添加这个配置项时,控制台不会输出
                    deep:true,
                    handler(){
                        console.log("number发生改变了");
                    }
                }
            }
        })
    </script>

watch对比computed

  • computed和watch之间的区别
  • 1.computed能完成的功能,watch都可以完成
  • 2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作,computed不行
  • 两个重要的小原则
  • 1.所被vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例
  • 2.所有不被vue所管理的函数(定时器的回调函数,ajax的回调函数,promise的回调函数等),最好写箭头函数,这样this的指向才是vm或组件实例

这里用的时vue官网的例子

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

computed大大减少了代码量

绑定class样式

<style>
        .basic{
            width: 200px;
            height: 200px;
            background-color: red;
        }
        .changestyle{
            width: 200px;
            height: 200px;
            background-color: skyblue;
            border-radius: 50%;
        }
        .style1{
            width: 200px;
            height: 200px;
            border: solid 1px black;
        }
        .style2{
            background-color: red;
        }
        .style3{
            border-radius: 50%;
        }
    </style>
</head>
<body>
    <div id="app">
        <!-- 字符串写法   适用于:样式的类名不确定,名字也不确定 -->
        <div class="basic" @click="change" :class="sty">点击切换样式</div>
        <br>
        <!-- 数组写法     适用于:要绑定的样式个数不确定,名字也不确定,要更改样式直接操作数组里的数据  classArr.shift()  classArr.push() -->
        <div class="basic" :class="classArr"></div>
        <br>
        <!-- 对象写法      适用于:要绑定的样式个数确定,名字也确定,但要动态决定用不用  classArr.shift()  classArr.push() -->
        <div class="basic" :class="classObj"></div>
    </div>
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                sty:"",
                classArr:["style1","style2","style3"],
                classObj:{
                    style1:true,
                    style2:false
                }
            },
            methods:{
                change(){
                    this.sty = "changestyle"
                }
            }
        })
    </script>
</body>

条件渲染

v-if

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 值的时候被渲染。

<h1 v-if="awesome">Vue is awesome!</h1>

也可以用 v-else 添加一个“else 块”:

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>

因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-else

你可以使用 v-else 指令来表示 v-if 的“else 块”:

<div v-if="Math.random() > 0.5">
  Now you see me
</div>
<div v-else>
  Now you don't
</div>

v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。

v-else-if

v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用:

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

类似于 v-elsev-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。

用key管理可复用的元素

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address">
</template>

那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的 placeholder

这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key attribute 即可:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

v-show

另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

<h1 v-show="ok">Hello!</h1>

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display

注意,v-show 不支持 <template> 元素,也不支持 v-else

v-if vs v-show

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

列表渲染

基本用法

  • v-for 指令:

    用于展示列表数据

    数组,字符串:v-for="(item,index) in xxx" :key=“yyy”

    对象:v-for="(value,k"key") in xxx" :key=“yyy”

    可遍历:数组,对象,字符串,指定次数

<div id="app">
        <h2>人员列表</h2>
        <ul>
            <li v-for="(item,index) in person" :key="item.id">{{item.name}}--{{item.age}}</li>
        </ul>
    </div>
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                person:[
                    {id:1,name:"张三",age:18},
                    {id:2,name:"李四",age:19},
                    {id:3,name:"王五",age:20}
                ]
            }
        })
    </script>

key的作用与原理

首先来研究一下,当使用index作为key或者不使用key时会有什么问题

<div id="app">
        <h2>人员列表</h2>
        <button @click="add">添加一个老刘</button>
        <ul>
            <li v-for="(item,index) in person" :key="index">{{item.name}}--{{item.age}}<input type="text"></li>
        </ul>
    </div>
    <script>  
       const vm = new Vue({
            el:"#app",
            data:{
                person:[
                    {id:1,name:"张三",age:18},
                    {id:2,name:"李四",age:19},
                    {id:3,name:"王五",age:20}
                ]
            },
            methods:{
                change(){
                    this.sty = "changestyle"
                },
                add(){
                    this.person.unshift({id:4,name:"老刘",age:40})
                }
            }
        })
    </script>

我们把三个输入框写入对应的名字

12.png

然后点击一下按钮

13.png

我们可以发现输入框里的数据与不一样

接下来我们把 :key 里的index 换成 item.id

<ul>
     <li v-for="(item,index) in person" :key="item.id">{{item.name}}--{{item.age}}<input type="text"></li>
</ul>

14.png

现在数据就变得正常了

我们通过下面一张图来解释一下为什么会出现这种错误

请添加图片描述

如果将数据的顺序破坏了,使用index或者不使用key就会出现以上的问题


面试题:react,vue中的key有什么左右?(key的内部原理)

  • 1.虚拟DOM中key的作用

    key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据"新数据"生成"新的虚拟DOM",随后Vue进行"新虚拟DOM"与"旧虚拟DOM"的差异比较,比较规则如下:

  • 对比规则:

    1.旧虚拟DOM中找到了与新虚拟DOM相同的key:

    ​ 若虚拟DOM中内容没变,直接使用之前的真实DOM

    ​ 若虚拟DOM中内容改变了,则生成新的真实DOM,随后替换页面中之前的真实DOM

    2.旧虚拟DOM中未找到与新虚拟DOM相同的key:

    ​ 创建新的真实DOM,随后渲染到页面

  • 用index作为key可能会引发的问题

    ​ 1.若对数据进行:逆序添加,逆序删除等破坏顺序的操作

    ​ 会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低

    ​ 2.如果结构中还包含输入类的DOM

    ​ 会产生错误DOM更新 ==> 界面有问题

  • 开发中如何选择key

    最好使用每条数据唯一标识作为key,比如id,手机号,身份证号,学号等唯一值

    如果不存在对数据的逆序添加,逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的

Vue监测数据改变的原理

Vue监测数据改变的原理------对象

模拟一个数据监测

<script>  
       let data = {
           name:"QX",
           address:"福建"
       }
       //创建一个监视的实例对象,用于监视data中数据的变化
       const obs = new Observer(data)
       //准备一个vm实例对象
       let vm={}
       vm._data = data = obs
       function Observer(obj){
           //汇总对象中所有的属性形成一个数组
           const keys = Object.keys(obj)
           //遍历
           keys.forEach((k)=>{
           		//这里的this指向是Observer的实例对象
           		//如果用的是data而不是用this的话,每次读取data里数据的时候都会重新触发get,然后继续读取data里的数据,从而导致递归死循环
               Object.defineProperty(this,k,{
                   get(){
                       return obj[k]
                   },
                   set(val){
                       console.log(`${k}被修改了,我要去解析模板,生成虚拟DOM。。。`);
                       obj[k] = val
                   }
               })
           })
       }
    </script>

在这里插入图片描述

Vue.set()方法

首先我们来看一个案例:

<div id="app">
        <h2>学生姓名:{{student.name}}----
            性别:{{student.sex}}----
            来自:{{student.address}}</h2>
    </div>
    <script>  
      const vm = new Vue({
          el:"#app",
          data:{
              student:{
                    name:"张三",
                    address:"福建"
                }
          }
      })
    </script>

在这里插入图片描述

这时我们性别上是没有信息的,我们在控制台来为他添加一下看看有什么效果

vm._data.student.sex = "男"

输入回车之后发现页面没有变化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E3u8He8A-1629635916118)(G:\Users\18545\Desktop\笔记\web\vue图片\18.png)]

这是因为我们在这里添加的sex,没有set和get

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sUGuu5O9-1629635916119)(G:\Users\18545\Desktop\笔记\web\vue图片\19.png)]

要怎么解决这个问题呢?我们这里用一个api Vue.set()

在控制台输入

因为有数据代理,vm._data.student也可以写成vm.student

Vue.set(vm._data.student,'sex','男')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CQaUue3G-1629635916121)(G:\Users\18545\Desktop\笔记\web\vue图片\20.png)]

这里还有一个api可以实现这个功能,这个api不在Vue上,在vm上

vm.$set(vm._data.student,'sex','女')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dqs4qo2Q-1629635916122)(G:\Users\18545\Desktop\笔记\web\vue图片\21.png)]

**注意!!!这两个api只能给data里面的某一个对象添加属性,而不能给data添加 **

在这里插入图片描述

控制台报错。显示不允许添加响应式的数据在vue 的实例身上

Vue监测数据改变的原理------数组

我们在之前的代码上添加一个数组

<body>
    <div id="app">
        <h2>学生姓名:{{student.name}}----
            性别:{{student.sex}}----
            来自:{{student.address}}</h2>
    </div>
    
    <script>  
       
      const vm = new Vue({
          el:"#app",
          data:{
              student:{
                    name:"张三",
                    address:"福建",
                    hobby:["喝酒","抽烟","烫头"]
                }
          }
      })
    </script>
</body>

打开控制台,查看一下视图student对比一下数据和对象有什么区别

在这里插入图片描述

我们可以发现,数组里的数据没有set和get,所以通过索引值修改数组里面的值的时候,数据确实是改了,但是页面不会被渲染

只有用数组里面的方法(push,pop,shift,unshift等)来修改数据才会被vue承认,渲染到页面上

我们也可以用Vue.set 和 vm.$set 来修改数组里面的值,也一样会渲染到页面上

Vue.set(vm._data.student.hobby,1,'跑步')
vm.$set(vm._data.student.hobby,0,'钓鱼')

总结Vue数据监测

    1. Vue会监测data中所有层次的数据
  • 2.如何监测对象中的数据?

​ 通过setter实现监测,且要在new Vue时就传入要监测的数据

​ (1).对象中后追加的属性,Vue默认不做响应式处理

​ (2).如需给后添加的属性做响应式,请使用如下api

​ Vue.set(target, propertyName/index , value)

​ vm.$set(target, propertyName/index , value)

  • 如何监测数组中的数据?

    ​ 通过包裹数组更新元素的方法实现,本质就是做了两件事:

    ​ (1).调用原生对应的方法对数组进行更新

    ​ (2).重新解析模板,进行更新页面

  • 4.在Vue修改数组中的某个元素一定要用如下方法:

    ​ 1.使用:push,pop,shift,unshift,splice,sort,reverse

    ​ 2.Vue.set() 或 vm.$set()

  • 特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象添加属性!

收集表单数据

  • v-model收集的是value值,用户输入的就是value值

  • v-model收集的是value值,且要给标签配置value值

  • ​ 1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选 是布尔值)

    ​ 2.配置input的value属性:

    ​ (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选 是布尔值)

    ​ (2)v-model的初始值是数组,那么收集的就是value组成的数组

  • v-model 的3个修饰符

    ​ 1.lazy:失去焦点再收集数据

    ​ 2.number:输入字符串转为有效的数字

    ​ 3.trim:输入首尾空格过滤

v-once指令

我们来看一个案例

<div id="app">
        <h2>n的初始值为:{{n}}</h2>
        <h3>现在的n为{{n}}</h3>
        <button @click="n++">n+1</button>
    </div>
   <input type="checkbox">
    <script>  
       
      const vm = new Vue({
          el:"#app",
          data:{
            n:1
          }
      })
    </script>

我们可以发现两个n都随着按钮点击增加

在这里插入图片描述

现在我们将h2修改一下 添加v-once

 <h2 v-once>n的初始值为:{{n}}</h2>

在这里插入图片描述

我们可以得出:

  • v-once所在节点在初次动态渲染后,就视为静态内容了
  • 以后数据的改变不会引起v-once所在结构的更新,可以用于性能优化

自定义指令

自定义一个指令(函数式)

定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍

<div id="app">
        <h2 v-once>n的初始值为:{{n}}</h2>
        <h3>现在的n为{{n}}</h3>
        <h3>放大10倍的n为: <span v-big="n">{{n}}</span></h3>
        <button @click="n++">n+1</button>
    </div>
   <input type="checkbox">
    <script>  
       
      const vm = new Vue({
          el:"#app",
          data:{
            n:1
          },
          //通过这个属性来定义指令
          directives:{
          //element:当前的DOM元素  binding:将元素和指令进行绑定
          //big函数何时会被调用? 1.指令与元素成功绑定时   2.指令所在的模板被重新解析时
              big(element,binding){
                  element.value = binding.value * value
              }
          }
      })
    </script>

自定义一个指令(对象式)

定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的元素默认获取焦点

<body>
    <div id="app">
        <h2 v-once>n的初始值为:{{n}}</h2>
        <h3>现在的n为{{n}}</h3>
        <h3>放大10倍的n为: <span v-big="n">{{n}}</span></h3>
        <button @click="n++">n+1</button>
        <input type="text" v-fbind:value="n">
    </div>
   <input type="checkbox">
    <script>  
      const vm = new Vue({
          el:"#app",
          data:{
            n:1
          },
          directives:{
              big(element,binding){
                  element.innerHTML = binding.value * 10
              },
              fbind:{
                  //指令与元素成功绑定时
                  bind(el,binding){
                    el.value  = binding.value
                  },
                  //指令所在元素被插入页面时调用
                  inserted (el, binding) {
                    el.focus()
                  },       
                  //指令所在模板被重新解析时
                  update(el,binding) {
                    el.value = binding.value
                  },
              }
          }
      })
    </script>

自定义指令总结

  • 1.定义语法

    ​ (1).局部指令 new Vue({ new Vue({

    ​ directives:{指令名:配置对象} 或 directives:{指令名:回调函数}

    ​ }) })

    ​ (1).全局指令

    ​ Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)

  • 2.配置对象中常用的3个回调

    ​ (1)bind:指令与元素成功绑定时调用

    ​ (2)inserted:指令所在元素被插入页面时调用

生命周期

引出生命周期

  • 生命周期:

    ​ 1.又名:生命周期回调函数,生命周期函数,生命周期钩子

    ​ 2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数

    ​ 3.生命周期函数的名字不可更改,但函数的具体内容时程序员根据需要编写的

    ​ 4.生命周期函数中的this指向是vm或组件实例对象

分析生命周期

在这里插入图片描述

生命周期总结

vm的一生(vm的生命周期):

​ 将要创建 ===> 调用beforeCreate函数

​ 创建完毕 ===> 调用created函数

​ 将要挂载 ===> 调用beforeMount函数

(重要)挂载完毕 ===> 调用mounted函数 【重要的钩子】

​ 将要更新 ===> 调用beforeUpdate函数

​ 更新完毕 ===> 调用updated函数

(重要)将要销毁 ===> 调用beforeDestroy函数 【重要的钩子】

​ 销毁完毕 ===> 调用destroyed函数

  • 常用的生命周期钩子:

​ 1.mounted:发送ajax请求,请求定时器,绑定自定义事件,订阅消息等(初始化操作)

​ 2.beforeDestroy:清楚定时器,解绑自定义事件,取消订阅消息等(收尾工作)

  • 关于销毁Vue实例

    1.销毁后的借助Vue开发者工具看不到任何信息

    2.销毁后自定义事件会失效,但原生DOM事件依然有效

    3.一般不会在beforeDestroy操作数据,因为即使操作数据,也不会再触发更新流程了

组件

什么是组件

  • 理解:用来实现局部(特定)功能效果的代码集合(html/css/js/image)

  • 作用:复用编码,简化项目编码,提高运行效率

非单文件组件

  • 一个文件中包含有n个组件为非单文件组件
基本使用

Vue中使用组件的三大步骤:

​ 1.定义组件(创建组件)

​ 2.注册组件

​ 3.使用组件(写组件标签)

一:如何定义一个组件?

​ 使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个几乎一模一样,但是区别如下:

​ 1.el不要写,因为最终所有的组件都要经过一个vm 管理,由vm中的el决定服务于哪个容器

​ 2.data必须写成函数,因为要避免组件被复用时,数据存在引用关系

​ 备注:使用template可以配置组件结构

二:如何注册组件“

​ 1.局部注册:new Vue的时候传入components选项

​ 2.全局注册:靠Vue.components(‘组件名’,组件)

三:编写组件标签

​ <组件名></组件名>

<body>
    <div id="app">
      <!-- 第三步:编写组件标签,将组件呈现在页面上 -->
      <school></school>
      <hr>
      <student></student>
    </div>
    <script>  
      //第一步:创建school组件
      const school = Vue.extend({
        //这里不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
        template:`
          <div>
            <h2>学校名称:{{schoolname}}</h2>
            <h2>学校地址:{{address}}</h2>
          </div>
        `,
        data(){
            return {
              schoolname:"QX",
              address:"福建"
            }
          }
      })
      //创建student组件
      const student = Vue.extend({
        template:`
          <div>
            <h2>学生名称:{{studentname}}</h2>
            <h2>学生年龄:{{age}}</h2>
          </div>
        `,
        data(){
            return {
              studentname:"张三",
              age:18
            }
          }
      })
      //创建vm
      const vm = new Vue({
          el:"#app",
          //第二部:注册组件(局部注册)
          components:{
            school,
            student
          }
      })
      //全局注册
      // Vue.components('student',student)
      // 全部的vue实例都能使用
    </script>
组件的几个注意点

几个注意点:

​ 1.关于组件名:

​ 一个单词组成:

​ 第一种写法(首字母小写):school

​ 第二种写法(首字母大写):School

​ 多个单词组成:

​ 第一种写法(kebab-case命名):my-school

​ 第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)

​ 备注:

​ 组件名尽可能回避HTML中已有的元素名称,例如:h2,H2都不行

​ 可以使用name配置项指定组件在开发者工具中呈现的名字

​ 2.关于组件标签:

​ 第一种写法:<组件名></组件名>

​ 第二种写法:<组件名/>

​ 注意!! 不使用脚手架时,<组件名/> 会导致后续组件不能渲染

​ 3.一个简写方式:

​ const school = Vue.extend(options) 可简写为:const school = options 不要Vue.extend 直接写配置对象

组件的嵌套
<body>
    <div id="app">
      <!-- 第三步:编写组件标签,将组件呈现在页面上 -->
      <school></school>
    </div>
    <script>  
      //第一步:创建组件
      const student = Vue.extend({
        template:`
          <div>
            <h2>学生名称:{{studentname}}</h2>
            <h2>学生年龄:{{age}}</h2>
          </div>
        `,
        data(){
            return {
              studentname:"张三",
              age:18
            }
          }
      })
      const school = Vue.extend({
        template:`
          <div>
            <h2>学校名称:{{schoolname}}</h2>
            <h2>学校地址:{{address}}</h2>
           	<student></student>
          </div>
        `,
        data(){
            return {
              schoolname:"QX",
              address:"福建"
            }
          },
          components:{
            //要写在创建student组件之后
            student
          }
      })
      const vm = new Vue({
          el:"#app",
          components:{
            school,
            student
          }
      })
    </script>

在这里插入图片描述

VueComponent构造函数

关于VueComponent:

​ 1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的

​ 2.我们只需写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)

​ 3.特别注意:每次调用Vue.extend 返回的都是一个全新的VueComponent

​ 4.关于this指向:

​ (1).组件配置中:

​ data函数,methods中的函数,watch中的函数,computed中的函数,它们的this均是【VueComponent实例对象】

​ (1).new Vue(options) 配置中:

​ data函数,methods中的函数,watch中的函数,computed中的函数,它们的this均是【Vue实例对象】

​ 5. VueComponent的实例对象,以后简称VC (也可称之为:组件实例对象)

​ Vue的实例对象,以后简称vm

Vue与VueComponent的关系

在这里插入图片描述

  • 一个重要的内置关系: VueComponent.prototype.__ proto __ === Vue.prototype
  • 为什么要有这个关系:让组件实例对象(vc)可以访问到Vue原型上的属性,方法

vue脚手架

初始化脚手架

第一步:全局安装脚手架

npm install -g @vue/cli

第二步:切换到你要创建项目的目录,然后使用命令创建项目

vue create 项目名

在这里插入图片描述

前三个为默认的配置,选择一个自己需要的

第四个为自己选择需要的插件(空格选择)

在这里插入图片描述

第三步:安装成功启动项目

首先进入项目文件夹
cd ****
然后输入指令启动项目
npm run serve

在这里插入图片描述

render函数

打开main.js我们可以看到里面有个 render: h => h(App)

在这里插入图片描述

  • 因为脚手架引入的vue是精简版的(vue.runtime.xxx.js)而不是vue.js,不包含模板解析器的,所以就需要render函数

  • vue.runtiome.xxx.js是运行版的Vue,只包含:核心功能,没有模板解析器

  • 类型(createElement: () => VNode) => VNode

  • 详细

    字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode

    如果组件是一个函数组件,渲染函数还会接收一个额外的 context 参数,为没有实例的函数组件提供上下文信息。

ref属性

  • ref属性

​ 1.被用来给元素或子组件注册引用信息(id的替代者)

​ 2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)

​ 3.使用方式:

​ 打标识:

​ 获取: this.$refs.xxx

首先我们看看在ref属性不在同地方使用有什么不同

<template>
  <div id="app">
    <h1 v-text="msg" ref="title"></h1>
    <button ref="btn" @click="showDOM">惦记我输出上方的DOM元素</button>
    <School ref="sch"></School>
  </div>
</template>
<script>
import School from './components/school.vue'
export default {
  name: 'App',
  components: {
    School
  },
  data() {
    return {
      msg:"欢迎学习Vue"
    }
  },
  methods: {
    showDOM(){
      console.log(this.$refs.title);  //输出真实DOM元素
      console.log(this.$refs.btn);  //输出真实DOM元素
      console.log(this.$refs.sch);  //输出School组件实例对象(vc)
    }
  },
}
</script>

点击按钮之后我们可以看到ref在不同地方使用有什么不同

在这里插入图片描述

props配置

  • 配置项props

    功能:让组件接收外部传过来的数据

    ​ (1).传递数据:

    ​ (2).接收数据:

    ​ 第一种方式(只接收):

    ​ props:[‘name’]

    ​ 第二种方式(限制类型):

    ​ props:{

    ​ name:Number

    ​ }

    ​ 第三种方式(限制类型,限制必要性,指定默认值):

    ​ props:{

    ​ name:{

    ​ type:String, //类型

    ​ required:true, //必要性

    ​ default:‘老王’ //默认值,有required的时候不写默认值

    ​ }

    ​ }

    ​ 备注:props是只读的,Vue底层会监视你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据

APP组件

<template>
  <div id="app">
    <Student name="李四" sex="女" age="20"></Student>
    <!-- 接下来我们想要显示出来的年龄比真实年龄大一岁,我们需要加上v-bind 这样才能解析里面的js语句,不加的话他只能是string类型的数据-->
    <Student name="张三" sex="男" :age="24"></Student>
  </div>
</template>
<script>
import Student from './components/Student.vue'
export default {
  name: 'App',
  components: {
    Student
  }
}
</script>

School组件

<template>
  <div>
    <h2>我是一个{{ msg }}的学生</h2>
    <h2>学生名称:{{ name }}</h2>
    <h2>学生性别:{{ sex }}</h2>
    <h2>学生年龄:{{ age }}</h2>
    <!-- 接下来我们想要显示出来的年龄比真实年龄大一岁-->
    <h2>学生年龄:{{ age+1 }}</h2>
  </div>
</template>

<script>
export default {
  data() {
      console.log(this);
    return {
      msg: "家里蹲大学",
    };
  },
  //简单接收写法
   props:["name","sex","age"]

    //接收的同时对数据进行类型限制
    // props:{
    //     name:String,
    //     age:Number,
    //     sex:String
    // }

    //接收的同时对数据进行类型限制+必要性的限制
    // props:{
    //     name:{
    //         type:String,    //name的类型是字符串
    //         required:true,  //这个值是必须要传的
    //     },
    //     age:{
    //         type:Number,
    //         default:33 //如果没有传入,这个值就为默认值
    //     },
    //     sex:{
    //         type:String,
    //         required:true
    //     }
    // }
  
};
</script>

首先我们可以在组件上直接传入,这种传入的方式数据都存在组件实例(vc)上,但是_data里面不存在这些数据
在这里插入图片描述

mixin混入

首先我们创建两个组件

School

<template>
  <div>
    <h2 @click="showname">学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: "QX",
      address: "福建",
    };
  },
  methods: {
    showname() {
      alert(this.name);
    },
  },
};
</script>

Student

<template>
  <div>
    <h2 @click="showname">学生名称:{{ name }}</h2>
    <h2>学生年龄:{{ age }}</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: "张三",
      age: 18,
    };
  },
  methods: {
    showname() {
      alert(this.name);
    },
  },
};
</script>

我们可以发现,两个组件里面有一个同样的方法 showname,这时我们就可以使用mixin

使用方法:

首先在src文件夹下创建一个mixin.js文件写入相同方法的代码

export const mixin = {
    methods: {
        showname() {
          alert(this.name);
        },
    }

然后将两个组件中的 showname方法删除,然后在两个组件里将mixin文件导入

import {mixin} from '文件路径'  //花括号里的名称为 mixin.js文件里导出的名称

然后再配置项中导入 mixins:[mixin]

import {mixin} from '../mixin'
export default {
  data() {
    return {
      name: "张三",
      age: 18,
    };
  },
    mixins:[mixin]
};
</script>

scoped样式

**一个组件style上添加scoped之后,其他组件就不会被这个组件的样式影响 **

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值