Vue2 常用知识

Vue 知识小结

问:项目使用 vue 2 还是使用 vue 3 呢?
答:如下所示(记于 2022/8/5)
示意图

vue 2 响应式系统用了 Object.defineProperty,因此不兼容 IE8 及以下版本
vue 3 响应式系统用了 Proxy,因此不兼容 IE11 及以下版本

vue 2.x

基本使用

1、导包
2、html布局
3、Vue的实例化

html 文件中:


el:element 缩写。作用:挂载(确定一个使用范围)。不能挂载在 <html>或者 <body> 标签 上。
data:默认数据。
methods:方法集合。
created:生命周期–创建后。


写一个 todoList 的 demo:

示意图

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,user-scalable=no" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>todoList</title>
  </head>
  <body>
    <div id="app">
      <div class="animate-wrap">
        <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"> <div class="animate" v-show="animate.show">📋</div> </transition>
      </div>
      <input type="text" v-model="title" @keydown.enter="addTodo" />
      <button v-if="active<all" @click="clear">清理</button>
      <span class="dustbin"> 🗑 </span>

      <ul v-if="todos.length">
        <transition-group name="flip-list" tag="ul">
          <li v-for="(todo,i) in todos" :key="todo.title">
            <input type="checkbox" v-model="todo.done" />
            <span :class="{ done: todo.done }"> {{ todo.title }}</span>
            <span class="remove-btn" @click="removeTodo($event,i)"></span>
          </li>
        </transition-group>
      </ul>
      <div v-else>暂无数据</div>

      <!-- <div>{{todos.filter(v=>!v.done).length}} / {{todos.length}}</div> -->
      <div>{{active}} / {{all}}</div>

      <transition name="modal">
        <div class="info-wrapper" v-if="showModal">
          <div class="info">哥,你啥也没输入!</div>
        </div>
      </transition>
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script>
      const App = {
        computed: {
          active() {
            return this.todos.filter(v => !v.done).length
          },
          all() {
            return this.todos.length
          },
          allDone: {
            get: function () {
              return this.active === 0
            },
            set: function (val) {
              this.todos.forEach(todo => {
                todo.done = val
              })
            },
          },
        },
        data() {
          return {
            title: '', // 定义一个数据
            todos: [
              { title: '吃饭', done: false },
              { title: '睡觉', done: true },
            ],
            showModal: false,
            animate: {
              show: false,
              el: null,
            },
          }
        },
        methods: {
          addTodo() {
            if (!this.title) {
              this.showModal = true
              setTimeout(() => {
                this.showModal = false
              }, 1500)
              return
            }
            this.todos.push({ title: this.title, done: false })
            this.title = ''
          },
          clear() {
            this.todos = this.todos.filter(v => !v.done)
          },
          removeTodo(e, i) {
            this.todos.splice(i, 1)
          },
          beforeEnter(el) {
            let dom = this.animate.el
            let rect = dom.getBoundingClientRect()
            let x = window.innerWidth - rect.left - 60
            let y = rect.top - 10
            el.style.transform = `translate(-${x}px, ${y}px)`
          },

          enter(el, done) {
            document.body.offsetHeight
            el.style.transform = `translate(0,0)`
            el.addEventListener('transitionend', done)
          },
          afterEnter(el) {
            this.animate.show = false
            el.style.display = 'none'
          },
          removeTodo(e, i) {
            this.animate.el = e.target
            this.animate.show = true
            this.todos.splice(i, 1)
          },
        },
      }
      // 启动应用
      Vue.createApp(App).mount('#app')
    </script>

    <style>
      li {
        list-style: none;
      }
      .done {
        color: gray;
        text-decoration: line-through;
      }
      .info-wrapper {
        position: fixed;
        top: 20px;
        width: 200px;
      }
      .info {
        padding: 20px;
        color: white;
        background: #d88986;
      }
      .modal-enter-from {
        opacity: 0;
        transform: translateY(-60px);
      }
      .modal-enter-active {
        transition: all 0.3s ease;
      }
      .modal-leave-to {
        opacity: 0;
        transform: translateY(-60px);
      }
      .modal-leave-active {
        transition: all 0.3s ease;
      }
      .flip-list-move {
        transition: transform 0.8s ease;
      }
      .flip-list-enter-active,
      .flip-list-leave-active {
        transition: all 1s ease;
      }
      .flip-list-enter-from,
      .flip-list-leave-to {
        opacity: 0;
        transform: translateX(30px);
      }
      .animate-wrap .animate {
        position: fixed;
        right: 10px;
        top: 10px;
        z-index: 100;
        transition: all 0.5s linear;
      }
      .dustbin {
        float: right;
        font-size: 30px;
      }
    </style>
  </body>
</html>

分割

指令

1.v-text

作用

在元素中输入内容,无法解析 html 字符串,类似innerText。

简写

{{ 值 }}

{{ }} :差值语法,写在标签文本中。
:一句话表达式,包括:变量、基本运算、三元表达式。

例🌰子:

<!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>
</head>

<body>
    <div id="app">
        <div v-text="msg">今天在下雨<p>123</p></div>   <!-- 我的大刀早已饥渴难耐了! -->
        <div v-text="'姓名:'+obj.name"></div>         <!-- 姓名:蛮王 -->
        <div v-text="1+1"></div>                      <!-- 2 -->
        <div v-text="true?1:0"></div>                 <!-- 1 -->
        <div v-text=""></div>
        <div>哈哈!{{msg}}</div>                      <!-- 哈哈!我的大刀早已饥渴难耐了! -->
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                msg: "我的大刀早已饥渴难耐了!",
                obj: {
                    name: "蛮王"
                }
            },
        })
    </script>
</body>

</html>

页面预览:
预览

2.v-html

作用

在元素中输入内容,可解析富文本,类似 innerHTML。

富文本:带标签的字符串。

例🌰子:

<!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>
</head>

<body>
    <div id="app">
        <div v-html="msg"></div>
        <div v-text="msg"></div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                msg: "<h1>剑圣</h1>"
            },
        })
    </script>
</body>

</html>

页面预览:
预览

3.v-model

作用

表单元素数据的双向绑定。

表单元素:input、textarea、select 等。

语法

v-model=“变量”

例🌰子:

<!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>
</head>

<body>
    <div id="app">
        <input type="text" v-model="msg">
        <p>您输入了:{{msg}}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                msg: "我的大刀早已饥渴难耐了!"
            },
        })
    </script>
</body>

</html>

页面预览:
预览

4.v-on

作用

事件的绑定。

语法

v-on:事件名=“一段简短的 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>
</head>

<body>
    <div id="app">
    
        <button v-on:click="num++">点我加1</button>
        <button v-on:dblclick="num++">双击加1</button>
        <button v-on:mouseover="num++">移入加1</button>
        <button v-on:click="add">点我加1</button>
        <p>你点我了{{num}}</p>
        
        <!-- 简写:@ -->
        <button @click="num++">点我加1</button>
        <button @dblclick="num++">双击加1</button>
        <button @mouseover="num++">移入加1</button>
        <button @click="add">点我加1</button>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                num: 0
            },
            methods: {
                // add:function(){ }
                add() {
                    if (this.num < 5) {
                        this.num++;
                    }
                }
            }
        })
    </script>
</body>

</html>

页面预览:
预览

html 里面访问 data 属性methods 中的方法都是不需要加 this的。

v-on的修饰符

@keyup.按键名/键值="事件执行代码"按了某键后才会触发事件。
@事件名.stop="事件执行代码"阻止冒泡。
@事件名.prevent="事件执行代码"阻止默认事件。

例🌰子:

<!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>
</head>
<style>
    .box1 { width: 500px; height: 500px; background-color: #f00; }
    .box2 { width: 100px; height: 100px; background-color: #0f0; }
</style>

<body>
    <div id='app'>
     	<!-- 键盘事件: -->
        <input type="text" @keyup.32="search">
         <!-- 阻止冒泡事件 -->
        <div class="box1" @click="box1Click"> 
            box1
            <div class="box2" @click.stop="box2Click">box2</div>
        </div>
        <div>{{msg}}</div>
         <!-- 阻止默认事件 -->
        <a href="http://www.baidu.com" @click.prevent.stop="aClick">点我啊</a>
        
    </div>
    <script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
    <script>
        new Vue({
            el: '#app',
            data: {
                msg: "hello"
            },
            methods: {
                search() { alert("ok") },
                box1Click() { alert(1) },
                box2Click() {
                    // e.stopPropagation();
                    alert(2)
                },
                aClick(e) {
                    e.preventDefault();
                    this.msg = "123"
                }
            }
        })
    </script>
</body>

</html>

5.v-bind

作用:给元素绑定属性。

语法

  • 基础用法:
    v-bind:属性名=“属性值”
  • 对象用法:
    v-bind:class=“{类名:结果为布尔值的一句话表达式}”

简写

  • 基础用法:
    :属性名=“属性值”
  • 对象用法:
    :class=“{类名:结果为布尔值的一句话表达式}”

对象用法,动态渲染标签的属性:true,使用该属性;false,不使用该属性。

例🌰子:

  • 基础用法:
<!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>
</head>

<body>
    <div id='app'>

        <button @click="changeSrc">点击修改</button>
        <!-- <img v-bind:src="imgSrc" v-bind:title="txt" v-bind:xxx="str" alt=""> -->
        <!-- 简写 -->
        <img :src="imgSrc" :title="txt" :xxx="str" alt="">

    </div>
    <script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
    <script>
        new Vue({
            el: '#app',
            data: {
                str: "ooo",
                txt: "123",
                imgSrc: "http://ossweb-img.qq.com/images/lol/web201310/skin/big517000.jpg"
            },
            methods: {
                changeSrc() {
                    this.txt = "456"
                    this.imgSrc = "http://ossweb-img.qq.com/images/lol/web201310/skin/big518000.jpg"
                }
            }
        })
    </script>
</body>

</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">
    <title>Document</title>
</head>
<style>
    .box { width: 300px; height: 300px; background-color: #f00; transition: all 0.5s; }
    .box.active { width: 100px; height: 100px; background-color: #0f0; }
</style>

<body>
    <div id='app'>
        <button @click="bol=!bol">点我啊</button>
        <!-- :class="{class类名:boolean  (true:使用该class,flase:不使用)}" -->
        <div class="box" :class="{active:bol}"></div>
    </div>
    <script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
    <script>
        new Vue({
            el: '#app',
            data: {
                bol: true
            }
        })
    </script>
</body>

</html>

v-bind 切换样式

1、两个样式的切换:
html

 <div class="info_tab">
	<div class="info_tab_order" :class="{clicked: isShow}" 
	@click="isShow = ! isShow">我的订单</div>
	
	<div class="info_tab_business" :class="{clicked: isShow == false}" 
	@click="isShow = ! isShow">商家信息</div>
</div>

js

data(){
	return{
		isShow: true,
	}
}

css

.clicked {
	border-bottom: 1px solid #fe5c02;
	color: #fe5c02;
}

效果 :
效果图


2、多个样式的切换:
html

 <div>
	<div  :class="{serviceClicked: isShow == 0}" @click="isShow = 0">我的订单</div>
	<div  :class="{serviceClicked: isShow == 1}" @click="isShow = 1">商家信息</div>
	<div  :class="{serviceClicked: isShow == 2}" @click="isShow = 2">特殊服务</div>
</div>

js

data(){
	return{
		isShow: 0,
	}
}

css

.serviceClicked {
      color: #fff;
      background-color: #b1090a;
}

v-bind 渲染 v-for

1. 静态

效果图:
示意图
关键代码:

  • html:
    :class="item.backgroundColor"
  • css:
    .bg0 { ... } .bg1 { ... } .bg2 { ... } .bg3 { ... }

完整代码:

<template>
  <view class="finish">
      <u-row class="topContent">
        <u-col span="6" v-for="(item, index) in topContent" :key="index">
          <view
            class="flex align-center justify-around textContent"
            :class="item.backgroundColor"
          >
            <view class="flex texts">
              <text class="title">{{ item.title }}</text>
              <text class="subtitle">{{ item.subtitle }}</text>
            </view>
            <u-image
              :width="item.width"
              :height="item.height"
              :src="item.img"
              mode="aspectFit"
            >
            </u-image>
          </view>
        </u-col>
      </u-row>
  </view>
</template>

<script>
export default {
  data() {
    return {
      topContent: [
        {
          title: "项目管理",
          subtitle: "施工监督,全程把控",
          img: "../../static/img/finishRegulate.png",
          width: "54rpx",
          height: "71rpx",
          backgroundColor: "bg0",
        },
        {
          title: "创意设计",
          subtitle: "自由设计,美感舒适",
          img: "../../static/img/finishDesign.png",
          width: "68rpx",
          height: "72rpx",
          backgroundColor: "bg1",
        },
        {
          title: "商品采购",
          subtitle: "轻松逛店,拼团采购",
          img: "../../static/img/finishPurchase.png",
          width: "62rpx",
          height: "68rpx",
          backgroundColor: "bg2",
        },
        {
          title: "劳务派遣",
          subtitle: "专业上岗,工匠服务",
          img: "../../static/img/finishDispatch.png",
          width: "60rpx",
          height: "72rpx",
          backgroundColor: "bg3",
        },
      ],
    };
  },
};
</script>

<style lang="scss">
/* ==装修== */
.finish {
    .topContent {
      border-top: 1rpx solid #dcdcdc;
      padding: 26rpx 11rpx 8rpx;
      background-color: #fff;

      .textContent {
        height: 134rpx;
        border-radius: 14rpx;
        margin-bottom: 18rpx;

        .texts {
          flex-direction: column;

          .title {
            color: #020202;
            font: 500 30rpx/1 "Source Han Sans CN";
            margin-bottom: 20rpx;
          }
          .subtitle {
            color: #605f5f;
            font: 500 22rpx/1 "Source Han Sans CN";
          }
        }
      }
      .bg0 {
        background-color: #dadbef;
      }
      .bg1 {
        background-color: #d9f4f6;
      }
      .bg2 {
        background-color: #fff2f0;
      }
      .bg3 {
        background-color: #e7f3ff;
      }
    }
}
</style>
2. 动态

效果图:
示意图

声明一个字段 step 用来存储当前点击的索引,判断它与 v-for 循环的索引 index 是否一致,一致则渲染指定 css

<view class="flex justify-between stepWords">
      <text
        v-for="(item, index) in stepList"
        :key="index"
        class="stepWord"
        :class="{ stepWordNow: step == index }"
        >{{ item }}</text>
</view>

<script>
export default {
  data() {
    return {
      step: 0, // 当前步骤
      stepList: ["房屋信息", "整屋详情", "商品推荐"], // 步骤描述
    };
  },
};
</script>

<style lang="scss" scoped>
.stepWords {
    width: 606rpx;
    height: 36rpx;
    margin: 0 auto;

    .stepWord {
      color: #bdbdbe;
      font: 400 24rpx/1 "Source Han Sans CN";
    }
    .stepWordNow {
      color: #00aa90;
      font: 400 24rpx/1 "Source Han Sans CN";
    }
  }
 </style>

番外:

vue 有些赋值方式是非响应式的

比如:删除数据就无法监听,需要 $delete 等 API 辅助才能监听到。

解决办法

6.v-for

作用

可以用来遍历数据(数组,对象)。

语法

v-for=“(item,index) in 数组”

item :数组每一项。
index :当前索引。
数据会实时更新,无须我们再操作,数组长度有多长就执行多少次。

例🌰子:

<!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>
</head>

<body>
    <ul id="ul"></ul>
    <script>
        let arr = [1, 2, 3, 4, 5]
        let str = ""
        // for (var i = 0; i < arr.length; i++) {
        //     str += "<li>" + arr[i] + "</li>"
        // }
        arr.forEach((item, index) => {
            str += "<li>" + item + "</li>"
        });
        document.getElementById('ul').innerHTML = str;
        arr.push(6);
    </script>
    <div id='app'>
        <button @click="add">添加数组</button>
        <button @click="del">删除数组</button>
        <ul>
            <li v-for="(item, index) in list" :key="index" @click="alertEvent(index)"> {{item}}-----索引:{{index}} </li>
        </ul>

    </div>
    <script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
    <script>
        new Vue({
            el: '#app',
            data: {
                list: ["剑圣", "蛮王", "牛头"]
            },
            methods: {
                add() {
                    this.list.push("我是添加项");
                },
                del() {
                    this.list.pop();
                },
                alertEvent(index) {
                    alert(index);
                }
            }
        })
    </script>
</body>

</html>

7.v-if,v-else-if,v-else

作用

语法

v-if=“boolean值”

若为true,就会渲染该标签;若为false,就不会渲染该标签。

例🌰子:

<!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>
</head>

<body>
    <div id='app'>
        <input type="text" v-model="score">
        <p v-if="score>80">优秀</p>
        <p v-else-if="score>60">一般</p>
        <p v-else>不行</p>
    </div>
    
    <script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
    <script>
        new Vue({
            el: '#app',
            data: {
                score: 100
            }
        })
    </script>
</body>

</html>

8.v-show

作用

可以控制元素的显示与隐藏,仅仅只是将元素通过设置 display:none

语法

v-show=“结果为布尔值的一句话表达式”

若为true,则显示该标签;若为false,则隐藏该标签,原理为添加display:none

例🌰子:

<!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>
</head>

<body>
    <div id='app'>
        <button @click="bol=!bol">点我啊</button>
        <div v-show="bol" key="">
            <span>用户名</span>
            <input type="text" placeholder="用户名">
        </div>
        <div v-show="!bol">
            <span>密码</span>
            <input type="text" placeholder="密码">
        </div>
    </div>
    <script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
    <script>
        new Vue({
            el: '#app',
            data: {
                bol: true
            }
        })
    </script>
</body>

</html>

v-if动态渲染标签 。
v-show动态控制标签的隐藏/显示。

动画

当vue中,显示隐藏,创建移除,一个元素或者一个组件的时候,可以通过transition实现动画。
示意图如果元素或组件离开,完成一个淡出效果:

<transition name="fade">
  <p v-if="show">100</p>
</transition>
.fade-enter-active {
	transition: all .2s ease;
}

.fade-leave-active {
	transition: all .2s cubic-bezier(1.0, .5, .8, 1.0);
}

.fade-enter,
.fade-leave-to {
	opacity: 0;
	transform: translateX(20px);
}
  • 进入(显示,创建)
    v-enter 进入前 (vue3.0 v-enter-from)
    v-enter-active 进入中
    v-enter-to 进入后
  • 离开(隐藏,移除)
    v-leave 进入前 (vue3.0 v-leave-from)
    v-leave-active 进入中
    v-leave-to 进入后

多个transition使用不同动画,可以添加nam属性,name属性的值替换v即可。
分割

计算属性

1.场景:

依赖某个值或多个值变化产生一个新的值。

2.本质:

是一个函数,值为他 return 的结果,要当作属性去使用它。

3.书写位置:

写在 vue 中的 computed 对象里。

4.特点:

不改变 data 中的数据,且能实时响应 data 中的数据变化。

5.语法:

computed: {
	计算属性名字(){
		return 需要返回的结果
	},
}
  • 例🌰1: (getNum 用在标签文本中)
<!-- // html: -->
 <div id='app'>
        <p>原价:{{num}}</p>
        <p>现价:{{getNum}}</p>
 </div>
 
// js:
 data: {
	 num: 100
 },
 computed: {
	 getNum() {
		 return this.num * 0.5
	 }
}
  • 例🌰2:(getList 用在标签属性中)
<!-- // html: -->
<div id='app'>
	<input type="text" v-model="score">
	<ul>
		<li v-for="(item, index) in getList" :key="index">{{item}}</li>
	</ul>
</div>
 
// js:
data: {
	score: 0,
	list: [1, 2, 3, 4, 5]
},
computed: {
	getList() {
		let temp = []
        for (var i = 0; i < this.list.length; i++) {
			if (this.list[i] > this.score) {
            temp.push(this.list[i])
         }
      }
      return temp
   }
}
  • 例🌰3:(计算属性传参)
<view>{{ changeCreateDate(data.update_date) }}</view>


<script>
	import { change_time } from "@/config/date.js";
	export default {
		data(){},
		computed: {
			changeCreateDate: () => {
				return (timestamp) => change_time(timestamp)
			},
		},
	}
</script>

解读:
在这里插入图片描述

分割

filters 过滤器

1.作用:

对数据进行过滤处理。

2.分类:

1. 局部过滤器:在当前组件中定义,适用于当前组件。
2. 全局过滤器:在 main.js 中定义,所有组件都可用。

3.局部过滤器:

语法:

<!-- // html: -->
<div>{{ 待过滤的值 | 过滤器名字 }}</div>


// js:
export default {
	filters: {
		过滤器名字(待过滤的值) {
			return 返回值
		}
	},
}
  • 例🌰1: 基本使用
<!-- // html: -->
 <div>{{ item.duration | formatTime }}</div>

 
// js:
export default {
	filters: {
		formatTime(value) {
    		return Math.floor(value/1000/60) + ":" + Math.ceil( (value/1000) % 60 )
		}
	},
}
  • 例🌰2: 使用 data 中的值

filters 中的 this 指向 undefined,因此不能通过正常的 this.xxx 来使用 data 中的值。可以通过传参的方式来解决这个问题。

<!-- // html: -->
 <div>{{ item.duration | formatTime(info) }}</div>

 
// js:
export default {
	filters: {
		formatTime(value, info) {
			if (info == true){
	    		return Math.floor(value/1000/60) + ":" + Math.ceil( (value/1000) % 60 )
	    	} else {
	    		return Math.floor(value/1000/60)
	    	}
		}
	},
	data(){
		info: true
	}
}
  • 例🌰3:
<!-- // html: -->
<el-table-column show-overflow-tooltip prop="idNumber" label="证件号" width="200">
	<template slot-scope="scoped">
		<span>{{ scoped.row.idNumber | idNumberFilter }}</span>
	</template>
</el-table-column>

<el-table-column show-overflow-tooltip prop="reservedPhone" label="银行预留手机号" width="200">
	<template slot-scope="scoped">
		<span>{{ scoped.row.reservedPhone | reservedPhoneFilter }}</span>
	</template>
</el-table-column>
 
 
// js:
<script>
export default {
	filters: {
	  /**
       * @description:证件号码过滤,隐藏部分身份证号
       * @param {String} val 证件号码
       * @return:idNumberFilter
       */
      idNumberFilter(val) {
        return (
          val
            ?.toString()
            // .replace(val?.toString().substr(4, 10), '**********')
            .replace(/(.{4}).*(.{4})/, '$1**********$2')
        )
      },

      /**
       * @description:手机号过滤,隐藏部分手机号
       * @param {String} val 手机号
       * @return:reservedPhoneFilter
       */
      reservedPhoneFilter(val) {
        return val?.toString().replace(/(.{3}).*(.{4})/, '$1****$2')
      },
	},
}
</script>

效果图:示意图

4.全局过滤器:

// 写在 new Vue 前面
Vue.filters("过滤器名字",function(待过滤的值){
	return 返回值
})

分割

watch 侦听器

1.作用:

侦听某个数据,若该数据发生变化,则立刻调用相关函数。

2.语法:

// js:
export default {
	watch: {
		// oldValue:没修改前的数据; newValue:修改后的数据
		// oldValue 和 newValue 都可省略
		"要侦听的数据名": function(newValue,oldValue){
			// 这里执行数据变动后的处理
		}
	},
}
  • 例🌰子:
<!-- // html: -->
<div>{{ msg }}</div>

// js:
export default {
	data(){
 		return {
			msg: 123,
		}
	},
	watch: {
		"msg": function(newValue, oldValue){
			// 这里执行数据变动后的处理:
			console.log("newValue:", newValue);
            console.log("oldValue:", oldValue);
		}
	},
}

immediate 选项

默认情况下,组件在初次加载完毕后不会调用watch 侦听器。如果想让watch 侦听器立即被调用,则需要使用 immediate 选项

watch: {
	// 1. 监听 username 值的变化
	username: {
		// 2. 当 username 变化时,调用 handler
		async handler(newVal, oldVal) {
			const { data: res } = await axios.get(`https://www.escook.cn/api/finduser/${newVal}`)
			console.log(res)
		},
		// 3.组件加载完毕后立即调用一次当前的 watch 侦听器
		immediate: true
	}
}

deep 选项

watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项。使用方法同 immediate。

分割

Iscroll 滚动条插件

1.导包:

iscroll.js。

2.布局:

三层布局。
  • 例🌰子:
<!-- // html: -->
<button @click="add">添加</button>
<button @click="del">删除</button>
<!-- // 三层布局: -->
<div class="wrapper" ref="wrapper">
	<ul>
		<li v-for="(item, index) in list" :key="index">{{item+index}}</li>
	</ul>
</div>
 
// js:
data: {
	myIscroll: "",
	list: ["测试数据", "测试数据", "测试数据", "测试数据", "测试数据", "测试数据"]
},
methods: {
	add() {
        // 数组最后面添加一条数据
        this.list.push("我是新加的")
        // $nextTick 它帮我们计算好 dom 最近的一次更新
        //它就是 setTimeout 的高级版本,相当于帮我们计算好了数据渲染到页面的具体时间
        this.$nextTick(() => {
        	this.myIscroll.refresh()
        })
	},
    del() {
		// 删除数组里面最后一项
        this.list.pop()
        // 数据修改了,执行一次刷新 
        this.$nextTick(() => {
        	this.myIscroll.refresh()
        })
	}
},
mounted() {
	this.myIscroll = new IScroll(this.$refs.wrapper, {
		mouseWheel: true,   //鼠标滚轮控制
        scrollbars: true   //滚动条的显示
	})
},

/* css: */
.wrapper {
        width: 300px;
        height: 150px;
        border: 1px solid red;
        margin: 0 auto;
        overflow: hidden; /* 去掉原始默认滚动条 */
        position: relative; /* 加入定位处理,为了 iscroll 滚动条相对于该标签 */
    }

分割

moment 时间插件

1.安装:

npm i moment

2.导包:

import moment from "moment"

3.作用:

  1. 获取当前时间
  2. 毫秒数 转换为 正常时间
  • 语法:

// 获取当前时间 (24小时制):
moment().format("YYYY年MM月DD日 HH:mm:ss")
// 获取当前时间 (12小时制):
moment().format("YYYY-MM-DD hh:mm:ss")

// 毫秒数转换:
moment(毫秒数).format("YYYY/MM/DD HH:mm:ss")

HH 表示 24 小时制 ; hh 表示 12小时制。
时间格式是可以自定义哦!比如2020年08月08日2020/08/082020-08-082020😄08😘08🐷都是可以的哦!

分割

时间格式转换

时间戳转换为日期格式

封装到 date.js 文件中:

/**
 * @param {Number} timeStamp 传入的时间戳,如:1647014072845
 * @param {Number} startType 要返回的时间字符串的格式类型,如:2022-03-17 22:35:29
 */
export let change_date = (timestamp) =>{
	function time(timestamp = +new Date()) {
	    var date = new Date(timestamp + 8 * 3600 * 1000); // 增加8小时
	    return date.toJSON().substr(0, 19).replace('T', ' ');
	}
	return time();
}

引入(路径为自己的文件路径):

import { change_date } from "@/config/date.js";

使用:

console.log(change_date (1647014072845)) // 2022-03-17 22:39:11

单(多)元素动画

1.前提:

实现动画只能在 `v-show` 和 `v-if` 上。
  • 例🌰子:(单元素动画)
<!-- // html: -->
<div id='app'>
	<transition name="xxx">
		<div id="xx" v-if="bol"></div>
    </transition>
</div>

/* css: */
/* 正常状态: */
.xx {
	transition: all 2s;
}
/* 进入/离开动画的执行体: */
.xxx-enter-active,
.xxx-leave-active {
	transition: all 2s;
}
/* 进入状态: */
.xxx-enter {
    transform: translateX(-200px);
}
/* 离开状态: */
.xxx-leave-to {
	transform: translateX(200px);
}
  • 例🌰子:(多元素动画)
<!-- // html: -->
<div id='app'>
	<transition-group name="xxx">
		<div class="box" v-if="bol" key="1"></div>
		<div class="box" v-if="bol" key="2"></div>
	</transition-group>
</div>

/* css 同 单元素动画 */

2.keyframes 实现动画:

  • 例🌰子:
<!-- // html 同 单元素动画: -->

/* css: */
.xx {
	animation: move 2s;
}
.xxx-enter-active,
.xxx-leave-active {
	animation: move 2s;
}
@keyframes move {
	0% {
		/* 动画内容, 例: */
		opacity: 0
    }
	100% {
		opacity: 1
	}
}

分割

Vuex 基本使用

1.作用:

共享数据管理。解决复杂关系的组件间的传值问题。

2.安装:

npm i vuex

3.语法

// main.js 中:

// 1.导入:
import Vuex from 'vuex'
// 2.注册:
Vue.use(Vuex)
// 3.实例化:
const store = new Vuex.Store({
    state: {
        参数名: "共享数据"
    }
})
// 4.注入:
new Vue({store}).$mount('#app')

访问:

this.$store.state.参数名

修改:

this.$store.state.参数名 = "新数据"

以上的修改方法不规范,有可能会报错。
规范修改见下面的 抽取的1和3:


4.抽取

  1. 新建:src/store/index.js 文件,文件中的代码如下:
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const store = new Vuex.Store({
    state: {
        info: "老板给我来碗忘情牛肉面",
    },
    getters: {
		info(state) { return info },
	},
    mutations: {
		setInfo(state, info){ state.info= info },
	},
	actions: {},
    modules: {},
})
export default store
  1. 在 main.js 中
import store from './store/index.js'

new Vue({
  store, //store:store
  render: h => h(App),
}).$mount('#app')
  1. 在要用到数据的组件中:
// 获取 vueX 值:
this.$store.getters.info
// 修改 vueX 值:
this.$store.commit("setInfo", "给我一杯忘情水")

5. 升级版抽取

将路由的文件按照 模块(modules) 拆分,方便路由管理与合作开发。

5.1.第一种模块化写法

5.1.1. 创建测试模块1 src/store/modules/eat.js

/**
* @author mygoes mynameiszhayu@163.com
* @description 测试模块1:关于吃的东西
*/

const state = { foods: [] }
const getters = {
    foods: (state) => state.foods,
}
const mutations = {
    addFood(state, food) {
        state.foods.push(food)
    },
    clearFood: (state) => {
        state.foods.splice(0)
    },
}
const actions = {
    addFood({ commit }, food) {
        commit('addFood', food)
    },
    clearFood({ commit }) {
        commit('clearFood')
    },
}
export default { state, getters, mutations, actions }

5.1.2. 创建测试模块2 src/store/modules/play.js

/**
* @author mygoes mynameiszhayu@163.com
* @description 测试模块2:关于玩的东西
*/

const state = { toy: '', game: false }
const getters = {
    toy: (state) => state.toy,
    game: (state) => state.game,
}

const mutations = {
    setToy(state, toy) {
        state.toy = toy
    },
    setGame(state, game) {
        state.game = game
    },
}
const actions = {
    setToy({ commit }, toy) {
        commit('setToy', toy)
    },
    setGame({ commit }, game) {
        commit('setGame', game)
    },
}
export default { state, getters, mutations, actions }

5.1.3. 导入所有 vuex 模块,不建议修改 src/store/index.js

/**
* @author mygoes mynameiszhayu@163.com
* @description 导入所有 vuex 模块,自动加入namespaced:true,用于解决 vuex 命名冲突,请勿修改。
*/

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const files = require.context('./modules', false, /\.js$/)
const modules = {}

files.keys().forEach((key) => {
    modules[key.replace(/(modules|\/|\.|js)/g, '')] = {
        ...files(key).default,
        namespaced: true,
    }
})
Object.keys(modules).forEach((key) => {
    modules[key]['namespaced'] = true
})
const store = new Vuex.Store({
    modules,
})
export default store

5.1.4. 导入store main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

import store from '@/store/index.js';

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

5.1.5. 在页面中使用:

<template>
  <div>
    <button @click="setVuex">点击存储到vuex</button>
    <div>{{ toy }}</div>
    <div>游戏否?{{ game }}</div>
    <div v-for="(item, index) in food" :key="index">{{ index }}{{ item }}
    </div>
  </div>
</template>

<script>
import { mapGetters } from "vuex";
export default {
  // 获取vuex
  computed: {
    ...mapGetters({
      toy: "play/toy",
      game: "play/game",
      food: "eat/foods",
    }),
  },
  methods: {
    // 修改vuex
    setVuex() {
      this.$store.dispatch("play/setToy", "乒乓");
      this.$store.dispatch("play/setGame", !this.game);
      this.$store.dispatch("eat/addFood", "西瓜");
    },
  },
};
</script>

项目源码

备注:第一种模块化写法某次 使用 HBuilderX 的运行为 H5 时,有 set 报错,于是更新了 第二种模块化写法


5.2.第二种模块化写法

5.2.1. 创建测试模块1 src/store/modules/eat.js

/**
* @author mygoes mynameiszhayu@163.com
* @description 测试模块1:关于吃的东西
*/

const state = { foods: [] }

const mutations = {
    ADD_FOOD(state, data) {
        state.foods.push(data)
    },
    CLEAR_FOOD: (state) => {
        state.foods.splice(0)
    },
}
const actions = {
    addFood({ commit }, data) {
        commit('ADD_FOOD', data)
    },
    clearFood({ commit }) {
        commit('CLEAR_FOOD')
    },
}
export default { namespaced: true, state, mutations, actions }

5.2.2. 创建测试模块2 src/store/modules/play.js

/**
* @author mygoes mynameiszhayu@163.com
* @description 测试模块2:关于玩的东西
*/

const state = { toy: '', game: false }

const mutations = {
    SET_TOY(state, toy) {
        state.toy = toy
    },
    SET_GAME(state, game) {
        state.game = game
    },
}
const actions = {
    setToy({ commit }, data) {
        commit('SET_TOY', data)
    },
    setGame({ commit }, data) {
        commit('SET_GAME', data)
    },
}
export default { namespaced: true, state, mutations, actions }

5.2.3. 简化 getters 获取 src/store/getters.js

/**
* @author mygoes mynameiszhayu@163.com
* @description 简化 getters 获取 ,使用代码: computed: { XXX() { return this.$store.getters.XXX }}
*/
const getters = {
    foods: state => state.eat.foods,
    toy: state => state.play.toy,
    game: state => state.play.game,
}
export default getters

近期在使用 this.$store.getters.XXX 获取 vuex 中的数据时,发现获取到的数据不是最新的。
未避免大家遇到类似的问题,建议大家在获取 vuex 数据的时候,使用以下写法:
this.$store.state. 找到对应的数据。
(例如:找到 user 模块中的 age:this.$store.state.play.game)
---- 更新于:2022/3/25

5.2.4. 导入所有 vuex 模块,不建议修改 src/store/index.js

/**
* @author mygoes mynameiszhayu@163.com
* @description 导入所有 vuex 模块,请勿修改。
*/

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'

Vue.use(Vuex)

// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)

// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
    // set './app.js' => 'app'
    const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
    const value = modulesFiles(modulePath)
    modules[moduleName] = value.default
    return modules
}, {})

const store = new Vuex.Store({
    modules,
    getters
})

export default store

5.2.5. 导入store main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

import store from '@/store/index.js';

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

5.2.6. 在页面中使用:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <button @click="setVuex">点击存储到vuex</button>
    <div>{{ toy }}</div>
    <div>游戏否?{{ game }}</div>
    <div v-for="(item, index) in foods" :key="index">{{ index }}{{ item }}
    </div>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  // 获取vuex
  computed: {
    toy: {
      get() {
        return this.$store.state.play.toy;
      },
      set(val) {
        this.$store.dispatch("play/setToy", val);
      },
    },
    game: {
      get() {
        return this.$store.state.play.game;
      },
      set(val) {
        this.$store.dispatch("play/setGame", val);
      },
    },
    /**
     * @description 简化 getters 获取。但在 HBuilderX 的 H5 中可能会报错,如报错,改为上面的写法即可。
     */
    foods() {
      return this.$store.getters.foods;
    },
  },
  methods: {
    // 修改vuex
    setVuex() {
      this.$store.dispatch("play/setToy", "乒乓");
      this.$store.dispatch("play/setGame", !this.game);
      this.$store.dispatch("eat/addFood", "西瓜");
    },
  },
};
</script>

项目源码

感谢贡献

其它vuex推荐

分割

导航守卫

1.定义:

在路由切换时,组件还未加载时,会执行的回调函数。
  • 书写位置:

    src/router/index.js

2.语法:

// src/router/index.js 中:

// 导入路由:
import VueRouter from "vue-router";
import store from "@/store/index.js"; // 导入仓库
// 注册:
Vue.use(VueRouter); 
// 创建路由对象:
const router = new VueRouter({
	routes: []
});
router.beforeEach((to, from, next) => {
	// window.console.log(to);
    // window.console.log(from);
    // window.console.log(next);
    // next() 必须执行。
});
// 暴露出去:
export default router;
// main.js 中:

// 导入路由对象
import router from './router/index.js'

new Vue({
  render: h => h(App),
  // 挂载 注入 Vue实例:
  router,
  store // 挂载仓库对象
}).$mount('#app')

分割

插槽

作用:在封装组件时,可以通过 <slot> 元素定义插槽,从而为用户预留内容占位符

基础使用

<template>
	<p>这是 MyCom 组件的 p 标签</p>
	
	<!-- 通过 slot 标签(插槽)为用户预留内容的占位 -->
	<slot></slot>
	
	<p>这是 MyCom 组件的 p 标签</p>
</template>
<my-com>
	<p>~~用户自定义的内容~~</p>
</my-com>

如果在封装组件时没有预留任何 <slot> 插槽,则用户提供的任何自定义内容都会被丢弃
在这里插入图片描述

具名插槽

如果在封装组件时需要预留多个插槽节点,则需要为每个 <slot> 插槽指定具体的 name 名称。这种带有具体名称的插槽叫做“具名插槽”
在这里插入图片描述
为具名插槽提供内容:

在这里插入图片描述

作用域插槽

定义:为预留的 <slot> 插槽绑定 props 数据,这种带有 props 数据的 <slot> 叫做“作用域插槽”

声明:

在这里插入图片描述

使用:

在这里插入图片描述

分割

自定义指令

概述:vue 官方提供了 v-for、v-model、v-if 等常用的内置指令,除此之外 vue 还允许开发者自定义指令

私有自定义指令

声明:

directives: {
	// 定义一个私有指令
	focus: {
		// 自动触发 mounted 函数
		mounted(el){
			// 被绑定的元素自动获得焦点
			el.focus()
		}
	}
}

使用:

<!-- 使用自定义指令时,需要加上 v- 指令前缀 -->
<input v-focus />

全局自定义指令

const app = Vue.createApp({})

// 注册一个全局的自定义指令 v-focus
app.directive('focus', {
	mounted(el){
		el.focus()
	}
})

简写:

app.directive('focus', ()=> {
	// 在 mounted 和 updated 时都会触发相同的业务处理
	el.focus()
})

指令的参数

在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值

<!-- 使用 v-color 时,可以通过等号绑定指令的值 -->
<input type="text" v-model.number="count" v-focus v-color="'red'">
<p v-color="'cyan'">{{ count }}</p>

<button @click="count++">+1</button>

// 自定义 v-color 指令
app.directive('color', (el, binding) => {
	// binding.value 是通过等号为指令绑定的值
	el.style.color = binding.value
})

分割

深/浅拷贝:

深拷贝:值的拷贝。类似于复制粘贴
浅拷贝:地址的拷贝(公用一个数据)。类似于创建桌面快捷方式

生命周期

vue2.x

  • beforeCreate(创建前)
    时期:创建 data 和 methods 前。
    特点:不能访问 data 中的属性和 methods 中的方法。
    应用:loading 事件。
  • created(创建后)
    特点:能访问 data 中的属性和 methods 中的方法,不能访问 vue 渲染后的 DOM。
    应用:结束 loading 事件;url 取值;初始化;实现函数的自执行。
    特殊含义:最早访问 data 和 methods 的生命周期钩子。
  • beforeMount(渲染前)
    特点:不能访问 vue 渲染后的 DOM。
    应用:基本无用。
  • mounted(渲染后)
    应用:vue 中的方法集合。
    特殊含义:最早访问 vue 渲染后的 DOM 的生命周期钩子。
  • beforeUpdate(更新前)
    时期:data 中的数据更新前(数据已修改,但还没完成 DOM 渲染)。
    应用:基本无用。
    特点:多次执行。
  • updated(更新后)
    时期:data 中的数据更新后(数据已修改,且已完成 DOM 渲染)。
    应用:基本无用。
    特点:多次执行。
  • beforeDestroy(销毁前)
    特点:能访问 data,methods 和 vue 渲染后的 DOM。
    应用:善后工作(清理定时器等)。
  • destroyed(销毁后)
    特点:能访问 data,methods,不能访问 vue 渲染后的 DOM。
    应用:善后工作(清理定时器等)。

vue3.x

在这里插入图片描述

分割

axios

  • 作用:发请求
  • 特点:只做接口请求(体积小,加载快,功能多)
  • 兼容:不支持老 IE 浏览器(Vue 也不支持老 IE 浏览器)

语法:

1. get 请求:
axios.get("url 路径", { params: { 请求参数:} })
.then(res => { console.log(res) })
.catch(err => {console.log(err) })
// 简写: params 可直接串到 url 路径上.
2. post 请求:
axios.post("url 路径", { 请求参数1:1, 请求参数2:2 })
.then(res => { console.log(res) })
.catch(err => {console.log(err) })
3. config 配置:
axios({
	url: "请求路径",
	method: "请求方法",
	// params: { get请求参数: 值 }, // 发 get 请求时
	data: { post请求参数:}, // 发 post 请求时
})
.then(res => { console.log(res) })
.catch(err => {console.log(err) })

戳这:一篇发请求的文章

vue 3.x 中全局配置 axios

在 main.js 入口文件中,通过 app.config.globalProperties 全局挂载 axios

// 为 axios 配置请求的根路径
axios.defaults.baseURL = 'http://api.com'

// 将 axios 挂载为 app 的全局自定义属性
app.config.globalProperties.$http = axios

在组件中使用时,可以通过 this 直接访问到全局挂载的自定义属性

this.$http.get('/users')
this.$http.get('/profile')
this.$http.post('/news')

组件的基本使用

全局组件

  • 注册
import { createApp } from 'vue'
import App from './App.vue'

// 1.导入 Swiper 和 Test 两个组件
import Swiper from './components/MySwiper.vue'
import Test from './components/MyTest.vue'

const app = createApp(App)

// 2.调用 app 实例的 component() 方法,在全局注册 my-swiper 和 my-test 两个组件
app.component('my-swiper', Swiper)
app.component('my-test', Test)

app.mount('#app')
  • 使用
// 在 main.js 中,注册了 my-swiper 和 my-test 两个全局组件
sap_app.component('my-swiper', Swiper)
sap_app.component('my-test', Test)

<!-- 在其他组件中,直接以标签的形式,使用刚才注册的全局组件即可 -->
<template>
	<h1>这是 App 根组件</h1>
	<hr/>
	<my-swiper></my-swiper>
	<my-test></my-test>
</template>

局部组件

  • 注册
<template>
	<h1>这是 App 根组件</h1>
	<hr/>
	<my-swiper></my-swiper>
	<my-search></my-search>
</template>

<script>
import Search from './components/MySearch.vue'
export default {
	components: {
		'my-search': Search,
	}
}
</script>

特点:

被全局注册的组件,可以在全局任何一个组件内使用
被局部注册的组件,只能在当前注册的范围内使用

应用场景:

如果某些组件在开发期间的使用频率很高,推荐进行全局注册;
如果某些组件只在特定的情况下会被用到,推荐进行局部注册。


升级版的定时器:

this.$nextTick(() => {
	// do something...
});

开心

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值