Vue,,

推荐用templete来创建html
渲染函数比模板更接近编译器

本地安装软件:nodejs安装包,找中文网

:表示动态数据,数据来源是数据模型或局部作用域

innerHTML:可获取/设置指定元素标签内的全部html内容——包含标签,所有浏览器都支持
给元素设置内容:element.innerHTML=htmlString
获取内容:document.getElementById("test").innerHTML

innerText:不包含标签,只是文本内容——火狐不支持

innerHTML和innerText

一、Vue基础

自己拿数据:this.msg
dom节点拿:{{msg}}基本渲染

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<div id="app">  </div>

<!--Vue事件绑定-->
<button @click="changeMsg">点击我改变msg值</button>
               光写方法名就行

创建Vue实例对象,与模板(dom节点)绑定
let vm=new Vue({ 
   el:"#app",  
   data:{   /*Vue实例使用的数据都会定义在data中*/
      msg:"hello"
   },
   methods:{   //函数或方法
      changeMsg(){           
         this.msg='123';   //获取、更改数据模型中的数据
       }    
   }  
}) 
结果:点击按钮后,变成123

页面显示时间,1s动一次

<div id="app">
   {{time}}
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>    
            let vm=new Vue({
                el:"#app",
                data:{
                    name:"zhangsan",
                    time:new Date()
                },
                methods:{
                    sayName(){
                        console.log(this.name);
                    }
                }
            });
            //Vue实例内的东西一般不会在外部用
            console.log(vm);  //Object{}
            console.log(vm.msg);  //hello
            vm.sayName();    //zhangsan

            //1s改变一次
            setInterval(()=>{vm.time=new Date()},1000)
</script>

二、安装

将Vue.js引入项目中的方法:
1、在页面上以CDN包的形式导入——bootcdn官网
   制作原型或学习,可以使用最新版本:<script src="https://unpkg.com/vue@next"></script>
   生产环境下:推荐链接到一个明确的版本号和构建文件(避免新版本造成的不可预期的破坏)
2、下载JavaScript文件并自行托管
3、使用npm安装(构建大型应用时推荐)——能很好地和类似于webpack模块打包器配合使用
   #最新稳定版   $npm install vue@next
4、使用官方的CLI构建一个项目

1、npm、cnpm、yarn——包管理工具(有一个就行)

通过npm装cnpm,npm install -g cnpm
        yarn,npm install -g yarn 

2、cli——Vue项目模板

快速搭建大型单页面应用的脚手架
单页面应用:仅刷新局部资源,公共资源(js、css等)仅需加载一次,常用于PC端官网、购物网站
  只有一个页面,根据url地址展示不同组件(左导航,右组件)
    优:前后端分离,快
    缺:初次加载耗时多
    
全局安装vue-cli工具:cnpm install -g @vue/cli
用vue-cli工具创建项目:vue create app
        (在哪个文件夹创建就切换到哪个文件夹下)
        选Manually select features回车
           选Choose Vue version(Babel、TypeScript、Router、Vuex...)
             Babel回车
        选2.0版本(稳定版本)
        选dedicated config files(自己的配置文件)
        不保留
        就开始下载了——————————
        用提示的操作启动项目 
        显示项目运行在local:http://localhost:8080/
                    network:http://ip地址:8080/  访问某个网站或使用代理服务器时,会加上“:8080”端口号

每个.vue文件都是一个组件
public文件夹——~index.html(整个程序的入口文件/首页)
                 整个body里就一个div id="app"(整个Vue实例绑定的模板)
             favicon.ico图标
src文件夹——assets静态资源文件夹:logo.png文件
          components文件夹:HelloWorld.vue文件(templete、script、style标签)
                          把其他组件引到App.vue组件中
          ~App.vue文件:主组件
          ~main.js文件:引入了App.vue
                      将App作为模板手动挂载到到.$mount("#app")(index.html的app中)
.browserslistrc文件
.gitignore文件
babel.config.js文件
package.json文件
README.md文件

三、生命周期

例子1

<div id="app"></div>

new Vue({
      el:'#app',  
      data:{      
        msg:'hello'
      },
      //钩子函数
      beforeCreate(){
        console.log(this.msg, 'beforeCreate');  //undefined,data和methods还没被初始化,无法获取
      },
      created(){
        console.log(this.msg, 'created');  //hello
      }
})

例子2

<div id="app">
   {{msg}}
   <button @click="test">事件测试</button>
</div>

let vm=new Vue({
      el:'#app',
      data:{
        msg:"hello",
        stus:[],
      },
      methods:{
        test(){
          console.log('点击事件处理程序');
        }
      },
      
      beforeCreate(){
        console.log('创建实例前');
      },
      created(){
        console.log('实例创建好了,可访问数据模型中的数据');             
          $.ajax({
          success(res){
            this.stus=res.data;  
          }
        })
      },
      
      beforeMount(){
        console.log('挂载前');
      },
      mounted(){
        console.log('挂载后');
      },
      
      beforeUpdate() {
        console.log('更新前');
      },
      updated() {
        console.log('更新好了');
      },

      beforeDestroy() {
        console.log('销毁实例前',this.msg);
      },
      destroyed() {
        console.log('实例销毁完毕',this.msg);
      }
    })

//测试update 
setTimeout(()=>{  //超时调用
   vm.msg=123; 
   vm.$destroy();     
},5000)
测试销毁——在控制台改变data里的值

在这里插入图片描述

四、渲染

数据渲染:要显示在页面的数据通过js实现而不是直接在HTML中输入数据     
                         
1、基本渲染:{{}}  
2、循环(列表)渲染:v-for指令(:key唯一标识,避免多次渲染)
   数组基本用循环渲染
3、条件渲染:v-if v-else v-show(一会显示、一会儿不显示)

1、循环渲染例子

<div id="app">
    <ul>
      <li v-for="(item,index) in fruits" :key="index">
                 拿数组里的什么      数组                   
        {{index+1+'  '+item}}  //可放表达式计算,到" "变成string类型,再进行string拼接
      </li>
    </ul>
    
    <table>
      <tr>
        <th>编号</th>
        <th>姓名</th>
        <th>年龄</th>
      </tr>
      <tr v-for="item in stus":key="item.id">
        <td>{{item.id}}</td>
        <td>{{item.name}}</td>
        <td>{{item.age}}</td>
      </tr>
    </table>
</div>

let vm = new Vue({
      el: '#app',
      data: {    //数据模型相当于全局作用域
        msg: "hello",
        fruits: ['苹果', '橘子', '葡萄'],
        stus: [{
          id: 1001,
          name: 'zhangsan',
          age: 12
          }, 
          {id: 1002,
          name: 'lisi',
          age: 13
          }, 
          {id: 1003,
           name: 'terry',
           age: 14
          }],
      },
      methods:{
      },
})

在这里插入图片描述
2、条件渲染例子

<div id="app">
    <div v-if="isLogin">欢迎您</div>
    <div v-else>请登录</div>
    
    <!--登录成功就显示-->
    <div v-show="isLogin">仅用来测试是否显示-------</div>
</div>

let vm = new Vue({
      el: '#app',
      data: {
        isLogin: false
      }, 
})

五、插值

1、文本、JS表达式

基本渲染:{{}}

2、原始html

<span v-html="rawHtml"></span>

3、事件

<span @click="doSomething"></span>
<span v-on:click="doSomething"></span>

4、属性

<span v-bind:id="  "></span>
v-bind:可简写成: 

六、style和class属性:取值是对象/数组

1、style绑定

<div v-bind:style="{color:activeColor,fontSize:fontSize+"px"}">
                  "[baseStyles,overridingStyles]"
</div>

例子:

<div id="app">
    <div :style="styleObj1">Hello</div>
    <div :style="{color:currentColor}">World</div>
    <div :style="[styleObj1,styleObj2]"> hello world</div>
</div>

new Vue({
      el:'#app',
      data:{       
        styleObj1:{
          color:'red',
          "font-size":'30px',
        },
        currentColor:'blue',
        styleObj2:{
          background:'pink',
          color:'blue'
        }
      },
      methods:{
      }
})

结果:
在这里插入图片描述
2、class绑定

<div v-bind:class="{类名:布尔值}">  
                  "[{类名:布尔值},{类名:布尔值}]"
                  "[isActive?activeClass:",errorClass]"                  
</div>

例子:

.red {
      color: red;
}
.size {
      font-size: 20px;
}

<div id="app">
    <div class="red" :class="{size:true}">Hello</div>
    <div :class="{red:false,size:true}">World</div>
    <div :class="[{red:true},{size:false}]">hello World</div>
</div>

new Vue({
      el: '#app',
      data: {
      },
      methods: {
        }
})

七、事件

1、事件绑定

<button v-on:click="greet">Greet</button>
methods:{ 
   greet:function(event){ 
      if(event){alert(event.target.tagName)}   //event是原生dom事件
   } 
}

2、事件传参
例子:模拟表格产生,添加删除按钮

之前:<button data-id="1001">删除按钮</button>
     $(this).attr('data-id');

<div id="app">
    <table>
      <tr v-for="item in arr" :key="item.id">
        <td>{{item.id}}</td>
        <td>{{item.name}}</td>
        <td>{{item.age}}</td>
        <td>            <!--加一个()可以运行,运行时传递实参-->
          <button @click="toEdit($event,item)">修改</button>  <!--传事件对象$event-->
          <button @click="toDelete($event,item)">删除</button>  <!--光传item也行-->
        </td>
      </tr>
    </table>
</div>

new Vue({
      el:'#app',
      data:{
        arr:[{
          id:1001,
          name:'zhangsan',
          age:12
        },
        {
          id:1002,
          name:'lisi',
          age:13
        },
        {
          id:1003,
          name:'terry',
          age:14
        }]
      },

      methods:{
        toEdit(event,item){
          console.log(event,item);
        },
        toDelete(event,item){
          console.log(event,item);
        }
      }
})

3、事件修饰符

以前:event.preventDefault()或event.stopPropation()

Vue——用事件修饰符
<form v-on:submit.prevent="onSubmit"></form>
.prevent 阻止事件默认行为( a标签、button按钮)
.stop 停止事件冒泡 
      父子元素绑定具有冒泡行为的事件,才有冒泡行为产生
.capture 捕获阶段执行
.self 只当在event.target是当前元素自身才触发,只有当点击outer才会执行
.once 执行一次
.passive 滚动事件的默认行为(即滚动行为)会立即触发 ,一般与scroll连用,能够提升移动端的性能

按键修饰符,一般与keyup事件类型配合使用
.enter(键码值)、.tab、.delete、.esc、.space、.up、.down、.left、.right 
.ctrl、.alt、.shift、.meta 

鼠标修饰符,mouseup
.left、.right、.middle

例子:

/*当里边比外边大,就会出现滚轮效果*/
    .outer {
      width: 200px;
      height: 200px;
      background-color: coral;
      overflow: scroll;
    }

    .inner {
      width: 100px;
      height: 400px;
      background-color: cyan;
      margin: 50px;
    }

 new Vue({
      el: "#app",
      data: {
        msg: '事件修饰符'
      },
      methods:{
        toJump(){
          console.log('跳转');
        },

        outer(e){
          //e.target 触发事件的源头元素
          //e.currentTarget 当前元素
          console.log('outer')
          for (let i=0;i<100;i++){
            console.log(i);
          }
        },

        inner(e){
          //e.stopPropagation();
          //console.log('inner',e.target,e.currentTarget);
          console.log('inner');
        },

        keyupHandle() {
          console.log('按下某些特殊键');
        },
      }
})

<div id="app">

    <!--前后顺序不同,结果不同,one和prevent-->
    <a @click.one.prevent="toJump" href="http://www.baidu.com">百度一下</a>

    <!--滚动时会先执行事件处理程序,再产生滚动效果-->
    <div class="outer" @scroll.passive="outer">
      outer
      <div class="inner" @click.capture="inner">

        <!--<div class="inner" @click.stop="inner">,不会向外冒泡,只有inner,没有outer-->

        <!--点内部子元素outer不触发-->
        <!--<div class="outer" @click.self="outer">-->
        inner
      </div>
    </div>
——————————按键修饰符
    <!--<input type="text" @keyup.enter="keyupHandle">-->
    <input type="text" @keyup.13="keyupHandle">
——————————鼠标修饰符
    <!--<input type="text" @mouseup.left="keyupHandle">-->

</div>

八、表单输入绑定

修饰符:增加表单绑定能力
1. lazy
默认情况下,v-model在每次input事件触发后->输入框的值与数据同步改变
可添加lazy修饰符,从而转为在change事件之后进行同步——光标移出再改变
<!-- 在“change”时而非“input”时更新 --> 
<input v-model.lazy="msg">
2. number 
将用户的输入值——>number类型
<input v-model.number="age" type="number">
3. trim
自动过滤用户输入的首尾空白字符,可以给v-model添加trim修饰符
<input v-model.trim="msg">


2. 多行文本框
<textarea v-model="message" placeholder="多行"></textarea>

1. 单行文本框
<input v-model="message" placeholder="单行">

3. 单选按钮,同一个v-model
<input type="radio" value="One" v-model="stu.gender">
<input type="radio" value="Two" v-model="stu.gender">
4. 多选按钮
<input type="checkbox" value="Jack" v-model="stu.hobby">
<input type="checkbox" value="John" v-model="stu.hobby">

5. 下拉框
<select v-model="selected"> 
  <option disabled value="">请选择</option> 
  <option>A</option> 
  <option>B</option>
</select>

例子:

<div id="app">

    {{stu}}
    <br>

     <!--简介,多行文本框-->
     简介:<textarea v-model="stu.info" cols="30" rows="10"></textarea>

    <!--用户名,单行文本框-->
    用户名:<input type="text" v-model.trim="stu.username">
    <br>
    
    <!--年龄,单行文本框,.number可将数据转为number类型-->
    年龄:<input type="text" v-model.number="stu.age">
    <br>

    <!--性别,单选按钮-->
    性别:<input type="radio" value="male" v-model="stu.gender">男
         <input type="radio" value="female" v-model="stu.gender">女
    <br>

    <!--爱好,多选按钮的属性值是数组,因为可能选多个,要放在一个数组里-->
    爱好:<input type="checkbox" value="basketball" v-model="stu.hobby">篮球
         <input type="checkbox" value="swimming" v-model="stu.hobby">游泳
         <input type="checkbox" value="dancing" v-model="stu.hobby">跳舞
    <br>
  
    <!--城市下拉框(多选)-->
    <select multiple v-model="stu.city">
      <option value="shanghai">上海</option>
      <option value="beijing">北京</option>
      <option value="guangzhou">广州</option>
    </select>
    <br>

</div>

new Vue({
      el:'#app',
      data:{
        stu:{ //把数据都放在stu对象里
        hobby:[]  <!--放复选框 选中的值,默认写一个空数组-->
        }
      },
      methods:{}
})

九、计算属性computed——有数据要依赖其他数据的情况

监听属性watch

模板的数据来源:计算属性(除了数据模型data)

需求 a input  b  input
    output标签(H5新增标签):将input的a和b累加后 输出到output标签里
  
计算属性:computed:{}
        当前数据不是确定的,要经常做出改变,这个改变是其他数据改变导致的
        有依赖关系的数据(a或b变时,total也变)
        基于响应式依赖进行缓存,只在改变时它们才会重新求值
        
事件监听:watch:{}
        当需要在数据变化时——执行异步或开销较大的操作时
        浅度监听:只能监听到引用地址是否变化        
        深度监听:基本数据类型不区分,对象才区分
                 更改数据类型也能监听到

例子(监听、计算属性):

 new Vue({
      el: '#app',
      data: {
        a: 0,
        b: 0,
        total: 0
      },
      methods: {},
      
————//计算属性
      computed: {
         total(){
            return this.a+this.b;
         }
      }
     ————//监听属性,a或b发生变化就+一下
      watch: {
        a(newValue, oldValue) {
          this.total = this.a + this.b;
        },
        b(newValue, oldValue) {
          this.total = this.b + this.a;
        }
      } 
})

<div id="app">
    a:<input type="text" v-model.number="a">
    <br>
    +
    <br>
    b:<input type="text" v-model.number="b">
    <br>
    =
    <br>
    <output>{{total}}</output>
</div>

深度监听例子:

new Vue({
      el: '#app',
      data: {
        a: 1,
        obj: {
          name: 'zhangsan',
          age: 12
        },
      },

      methods: {
        changeObj() {
          //更改内部数据
          this.obj.name ='lisi';
          /*更改指向
            this.obj={
               ...this.obj, //拿到内部所有属性,放到新的里面
               name:'lisi'
            }; */

          /* let obj2={...obj}
          let arr2=[...arr];*/
        }
      },

      watch:{
        a(newValue,oldValue){
          console.log('a数据发生变化...');
        },
        /*浅度监听
          obj(newValue, oldValue) {
          console.log('obj数据发生变化...');
        } */   

        //深度监听
        obj:{
          handler(newValue, oldValue){
            console.log('obj数据发生变化...');
          },
          deep: true
        }
      }
    
})

<div id="app">
    {{obj}}
    <button @click="changeObj">更改obj</button>
</div>

十、组件

听到day04-1,1:19:26到最后
1、组件注册

1. 全局注册:任何一个Vue实例对应的模板中都可使用该组件
Vue.component('my-component-name',component)
                   在html使用,     创建组件名

2. 局部注册:只能在当前实例对应的模板中使用
new Vue({ 
    data:{},
    methods:{},
    components:{ 'component-a': ComponentA, 'component-b': ComponentB } 
})

例子:

//1.创建组件
let myCom={
   data(){
      return{
         comMsg: "组件数据"
      }
   },
   template:`
        <div>
          <span>{{comMsg}}</span>  
          <button @click=" comMsg='新数据' ">更改数据模型中的数据</button>
        </div>
      `
};

//2.组件注册(全局)
Vue.component('my-com',myCom);
new Vue({
   data:{
   },
   methods:{}
})

<div id="app">
    //3.组件使用
    <my-com></my-com>
    <my-com></my-com>  
</div>

结果:点击按钮后,“组件数据”——>新数据

在这里插入图片描述
例子:

<div id="app">
    <my-com-b></my-com-b>
    ----------
    <my-com-a></my-com-a>
</div>

let myComA={
      template:`
        <div>A组件</div>
      `
    }
    
    let myComB={
      components:{
        "my-com-a":myComA
      },
      //在B组件中使用A组件
      template:`
        <div>B组件
          <my-com-a></my-com-a>  
        </div>
      `
    }

    //全局注册A、B组件
    //Vue.component('my-com-a',myComA);
      Vue.component('my-com-b',myComB);

    new Vue({
      el:'#app',
      components:{
        "my-com-a":myComA
      },
      data:{
      },
      methods:{}
})

结果:B组件
     A组件
     ----------
     A组件

3、组件通信

1.父组件 属性绑定——>子组件
  <my-com title="" :msg="msg" attr-a=""></my-com>
 子组件可定义数据类型:<my-com title="hello" :a="1" :obj="{name:'zhangsan'}" :flag="true">    
                   </mycom>
2.子组件接收并处理数据
  {
    props:['title','msg','attrA'],
    或 子组件对接收的数据有要求
     props:{
      title:String,
      obj:Object
    }
    
    template:
  }

——子组件发射自定义数据,父组件接收
子组件发射时机:1.点击按钮手动发送
             2.监听子组件内数据变化,发射
               监听子组件comMsg的变化,发射this.$emit('   ',this.comMsg)
               父组件的事件处理程序调用,可更改父组件数据模型中的数据,并同步反映到父组件视图中

子组件模板内可使用自己内部/props的数据,不能直接使用父组件的数据(通过props)

例子:

<div id="app">
    <my-com @my-event="myEventHandle" :title="msg" static-attr="父组件给子组件的静态数据"></my-com>
</div>

//1.创建子组件
    let myCom={
      props:["title","staticAttr"],
      data(){
        return{
          comMsg:"子组件数据"
        }
      },
      template:`
        <div>
          <span>子组件内部数据:{{comMsg}}</span>  
          <br/>
          <span>父组件数据:{{title}}--{{staticAttr}}</span>
          <button @click="comMsg='新数据'">更改数据模型中的数据</button>
          <button @click="toEmit">发射数据</button>
        </div>
      `,
      methods:{
        toEmit(){                
          this.$emit('my-event',this.comMsg,100);
                  //自定义事件名, 实参(发射的数据)
        }
      }
    };

    //2.(全局)注册子组件
    Vue.component('my-com',myCom);

    //创建Vue实例——父组件
    new Vue({
      el:'#app',
      data:{
        msg:'hello'
      },
      methods:{
        //my-event的事件处理程序,父组件接收
        myEventHandle(a,b){
          console.log(a,b,'----');
        }
      }
})

prop例子:
四种校验:数据类型type、默认值default、必填项required
自定义验证函数:如父组件给子组件传递大于20的值

// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) 
propA:Number,

// 多个可能的类型
propB:[String, Number], 

// 必填的字符串
propC:{ type: String, required: true }, 

// 带有默认值的数字
propD:{ type: Number, default: 100 }

<div id="app">
    <my-a :msg="msg" sub-msg="父给子" :is-active="true" :age="80":stus="[1,2,3]"></my-a>
</div>

//子组件
    let myA={
      //接收父组件的数据
      props:{
        msg:String,
        subMsg:String,
        stus:{
          type:Array,       
          default(){   //Object/Array,需写工厂函数返回默认值
            return[6,7,8];
          }
        },
        isActive:[String,Boolean],
        name:{
          type:String,
          default:"lisi"
        },
        /*数据类型type、默认值default、必填项required
          自定义验证函数:如父组件给子组件传递大于20的值*/        
        age:{
          type:Number,
          //校验器 h5新增表单属性 novalidate formtarget formaction formmethod formenctype formnovalidate      
          validator(value){  //实参是将来父组件给子组件传递的数据
            /*if(value>50){
              return true;
            }else{
              return false;
            },和下边一个意思*/
            return value>50;
          }
        }
      },
      template:`
        <div>
          {{subMsg}}
          {{stus}}
          {{isActive}}  
          {{name}}
          {{age}}
          {{msg}}
        </div>
      `
    };

    //注册子组件
    Vue.component('my-a',myA);
    
    let vm=new Vue({
      el:'#app',
      data:{
        msg:'hello'
      },
      methods:{}
})

4、elementui组件库
https://element.eleme.cn/#/zh-CN
6、插槽(操作的是模板)——可重用性
个性化组件——子组件传递的内容不一样,显示的不一样
调用时传递标签内容,传到模板的slot里

插槽决定将所携带的内容,插入到指定某个位置,从而使模板分块
父组件控制——显不显示、怎样显示
子组件控制——在哪里显示

1、普通(默认)插槽:默认名字是default
2、具名插槽
        template:`
           <div>
              <slot name="default"></slot> 
              <slot name="header"></slot>
           </div>
         `
        <my-a>
            <template v-slot:default>hello</template>
            <template v-slot:header>world</template>  //包裹要填充的内容
        </my-a>
3、作用域插槽:子组件数据想用slot的模板——一般在插件里用
    data(){return {msg:''}}
    template:`
      <div>
        <slot :title="msg" name="default"></slot>
        <slot name="header"></slot>
      </div>
    `
    <my-a>
      <template v-slot:default="scope">hello{{scope.title}}</template>      
      <template v-slot:header>hello</template>
    </my-a>      

例子:

 <div id="app">           
    <my-com>Hello</my-com> 
    <my-com>World</my-com> 
    <my-com></my-com>    
    <my-com>   <!--具名插槽填充-->
      <!--在父组件环境内-->
      <template #default>
        <div>
          <!--模板中如何使用父/子组件的数据-->
          123
          456
          {{msg}} <!--可访问父组件中的数据-->
        </div>
      </template>
    
      <!--<template #slot2="scope">
          scope:插槽作用域对象{插槽声明时传递的属性},{sub:'子组件的数据'}-->
      <template #slot2="{sub,test}">
        <div>
          {{sub}}
          {{test}}
          <!--{{scope}}
          {{scope.sub}}-->
          <h1>标题</h1>
        </div>
      </template>
    </my-com>

</div>

/*全局注册子组件,模板内有1个普通插槽,1个具名插槽
      在子组件环境内*/
    Vue.component("my-com",{
      data(){
        return{
          subMsg:'子组件的数据'
        }
      },
      template:`
        <div>
          day04

          <button>
            <slot :a="subMsg" name="default">默认内容1,{{subMsg}}</slot>
          </button>

          <div>
            <slot :sub="subMsg" name="slot2" :test="1">默认内容2</slot>
          </div>

          --------------------------------------------------------------------------------         
        </div>
      `
    })

    //父组件
    new Vue({
      el:'#app',
      data:{
        msg:'hello'
      },
      methods:{}
})

8、综合例子
day04-2整个

<div id="app">
    <my-table :data="stus">
      <template #header>
        <th>编号</th>
        <th>名称</th>
        <th>年龄</th>
      </template>
      <template #body="{item}">
        <th>{{item.id}}</th>
        <th>{{item.name}}</th>
        <th>{{item.age}}</th>
      </template>
    </my-table>

    <my-table :data="teachers">
      <template #header>
        <th>编号</th>
        <th>名称</th>
        <th>薪资</th>
        <th>性别</th>
      </template>
      <template #body="{item}">
        <th>{{item.id}}</th>
        <th>{{item.name}}</th>
        <th>{{item.salary}}</th>
        <th>{{item.gender}}</th>
      </template>
    </my-table>

    <my-table :data="courses">
      <template #header>
        <th>编号</th>
        <th>名称</th>
        <th>描述</th>
      </template>
      <template #body="{item}">
        <th>{{item.id}}</th>
        <th>{{item.name}}</th>
        <th>{{item.desc}}</th>
      </template>
    </my-table>

</div>

//全局注册组件
    Vue.component('my-table',{
      data() { return {} },
      props: ['data'],
      template: `
        <table>
          <thead>
            <tr>
              <slot name="header"></slot>  
            </tr>
          </thead>
          
          <tbody>
            <tr v-for="(item,index) in data" :key="index">
              <slot :item="item" name="body"></slot>  
            </tr>  
          </tbody>  
        </table>
      `
    })

    //父组件,el,data(msg,stus三个,teachers四个,courses两个),methods
    new Vue({
      el: '#app',
      data: {
        msg: 'hello',
        stus: [{
          id: 1001,
          name: 'zhangsan',
          age: 12
          }, 
         {id: 1002,
          name: 'zhangsan2',
          age: 13
         }, 
         {
          id: 1003,
          name: 'zhangsan3',
          age: 14
         }],

        teachers: [{
          id: 101,
          name: 'terry',
          salary: 10000,
          gender: 'male'
         }, 
         {
          id: 102,
          name: 'terry2',
          salary: 100001,
          gender: 'female'
         }, 
         {
          id: 103,
          name: 'terry3',
          salary: 1000011,
          gender: 'male'
         }, 
        {
          id: 104,
          name: 'terry4',
          salary: 10000111,
          gender: 'male'
        }],

        courses: [{
          id: 1,
          name: 'HTML',
          desc: '超文本'
          }, 
          {id: 2,
          name: 'CSS',
          desc: '层叠样式表'
          }]
      },
      methods: {}
})

9、动态组件(component标签+is属性)
企业级开发的底层 day04-3——>23.17

十一、可复用技术

1、混入mixin(对象)

混入规则:——冲突时,以组件优先
        同名钩子函数混合为一个数组,钩子函数都执行,混入钩子函数——>组件钩子函数
        值为对象的属性methods,computed,components,filters,directives——>合并对象
        data中有引用数据类型——>递归合并(深合并)
            深合并:name,age+gender=name,age,gender
                 name+age,name=组件中的name+混入对象的age

var mixin={
   data:function(){return {info:"this is a mixin"}},
   created(){console.log("this is mixin created");},
   methods:{foo(){return "this is mixin foo";}}
}
new Vue({
   el:"#app",
   mixins:[mixin],   //将上方的mixin对象混入当前Vue实例内
   data:{msg:"hello"},  
   created(){console.log("this is vue created");},
   methods:{foo(){return "this is vue foo";}}
})

Vue.mixin(mixin);
全局混入:会影响每个之后创建的Vue实例
        谨慎使用,推荐作为插件发布
        Vue.mixin({ 
           created:function(){} 
        })        

全局混入例子

<div id="app">
    <my-a></my-a>
     <!--混入、Vue实例
         混入、子组件
      混入created
      created...
      混入created
      子组件created-->
</div>

//混入(对象),可放声明组件的任何东西
    let mixin={
      data(){
        return{
          mixinData:'混入的数据',
          obj:{name:'zhangsan',gender:'男' }
        }
      },
      created(){
        console.log('混入的created');
      },
      methods:{
        mixinMethod(){
          console.log('mixinMethod方法');
        },
        test(){
          console.log('test 混入');
        }
      }
      //原型方法 vm.$destory
    };

    //子组件
    let myA={
      template: `
        <div>
          A组件内容
        </div>
      `,
      created(){
        console.log('A组件created');
      }
    };

    //全局混入
    Vue.mixin(mixin);

    //全局注册子组件
    Vue.component('my-a',myA);

    let vm=new Vue({
      el:'#app',
      /*局部混入
      mixins:[mixin],*/
      data:{
        msg:'hello',
        obj:{age:12,name:'lisi'}
      },
      created(){
        console.log('created...');
      },
      methods:{
        test(){
          console.log("test");
        }
      }
})

结果检测:通过控制台

2、自定义指令(对象)

v-开头的是指令,v-bind指令
directive:指令允许对普通DOM元素进行底层操作,可全局、局部注册

1. 全局注册
Vue.directive("focus",{
   //当被绑定的元素插入到 DOM 中时„„
   inserted: function (el) {
       // 聚焦元素,el为DOM中的Element元素
       el.focus()
   }
})
<input v-focus>
2. 局部注册
directives: { 
   focus: { 
      inserted: function (el) { el.focus() } 
   } 
}

指令的生命周期:bind(el,binding,vnode,oldNode){}
bind:只需要调用一次,在绑定的时候调用进行初始化设置
inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)
update:VNode更新时调用,但可能发生在其子VNode更新之前
componentUpdated:组件的VNode及其子VNode全部更新后调用
unbind:指令与元素解绑时调用,只调用一次

参数:el:dom元素
     binding:对象,指令的详细参数
     vnode:Vue编译生成的虚拟节点
     oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用

自定义指令例子:day04-3,1.12.20到4-4,16.54
3、过滤器filter(格式化)函数

使用:{{msg|过滤器}}
     <div v-bind:title="msg|过滤器"></div>
应该被添加在JS表达式的尾部,由“管道”符号|指示
new Date().getTime() //获取当前时间的时间戳
处理成年月日时分秒
{{date|filter}}
{{哪个变量|调用哪个过滤器}}

1、以前的方式

let arr=[{
          name:'',
          time:1600676292941
        },
        {
          name:'',
          time:1600676613869
        }, 
        {
          name:'',
          time:1600676624202
        }];

arr.forEach((item)=>{
    item.time=parse(item.time);
})
console.log(arr);

function parse(time){
    let date=new Date(time); //将time——>时间对象
    return date.toLocaleDateString()+' '+date.toLocaleTimeString();
                本地日期字符串(年月日)            本地时间字符串(时分秒)

2、过滤器方式
时间过滤器一般用全局注册

全局注册:过滤器名字,过滤器函数
Vue.filter('过滤器名字', function(date){
    return date?moment(date).format('YYYY-MM-DD HH:mm'):'';
})
{{ birthday |过滤器名称}}
局部注册:
filters:{
    过滤器名称:function(date){
        return date?moment(date).format('YYYY-MM-DD HH:mm'):'';
    } 
}
{{ birthday |过滤器名称}}
<div id="app">
    {{msg|parseUpper}}
    -----
    <div :title="msg|parseUpper" v-for="item in arr" :key="item.time">
      <span>item.name:{{item.name}}</span>
      <span>过滤器:{{item.time|parseTime}}</span>
    </div>
</div>

//全局注册过滤器 {{item.time|parseTime}}
Vue.filter('parseTime',(date)=>{
      //date就是item.time  2020-09-09 09:09:09  2020/09/09 09:09:09
      return new Date(date).toLocaleString();
});
/*全局注册大写过滤器 
Vue.filter('parseUpper',(date)=>{
         return date.toUpperCase();
});*/

new Vue({
      el:'#app',
      //局部注册过滤器
      filters:{
        parseUpper(date){
          return date.toUpperCase();
        }
      },
      data:{
        msg:'hello',
        arr:[{
          name:'1',
          time:1600676292941
        }, 
        {
          name:'2',
          time:1600676613869
        }, 
        {
          name:'3',
          time:1600676624202
        }]
      },
      methods:{}
})

4、渲染函数(使用频率不高)
带$slots调用者是Vue实例
模板产生:createElement或jsx
1)render(创建Vnode)

JS的原生DOM操作创建div节点
document.createElement("div');

render渲染函数:灵活、效率高
render(createElement){
   createElement(标签名称,{配置信息(可不写)},childVnode)
                                         子虚拟节点,一般为数组
 class、style、attrs、props、domProps、On、nativeOn

2)jsx(在js里写html代码),可分开使用,但依赖render函数渲染

如果写了很多render函数,可能会觉得其语法相对比较琐
在Vue中使用JSX语法,可让我们回到更接近于模板的语法上
jsx的测试需要放到脚手架中进行                                                                            
new Vue({ 
   el: '#demo', 
   render: function (h) { 
      return (<div> <span>Hello</span> world! </div>) 
   } 
})

render(){
   return (<div>
		       hello{this.msg}
		       <ul>
		           {let arr=['苹果','香蕉'],
			        arr.map((item)=>{
			           return <li>{item}</li>
			         })
		            }
		       </ul>
		   </div>
}

例子:

<div id="app">
    <my-a :level="1">
      <template #default>default</template>
      <template #header>header</template>
    </my-a>
</div>

//组件myA声明
let myA={
   props:{
      level:{
         type:Number,
         required:true
      }
    },
    /*render(createElement){
        //jsx
        return (<div>hello{this.msg}</div>)  //{写JS代码}
      }*/

//<h1><slot name="default"></slot></h1>
render(createElement){
    return createElement(
       'h'+this.level,
        this.$slots.header  //想用自定义模板中的内容
         /*{
             default:default模板,
             header: header模板,
            }*/
     );
}
/*render(createElement){
     return createElement('div',{  //在控制台显示div内放文本
        attrs:{
           id:'one',
           title:'test'
        }
      },'文本')
}*/
/*template: `
     <div>
         Hello
     </div>
      ` */
};

//组件myA注册
Vue.component('my-a',myA);

new Vue({
   el:'#app',
   data:{
       msg:'hello'
   },
   methods:{}
})

5、插件(对象)plugin(Vue构造器,配置对象可选)
封装了一系列功能

通常用来为Vue添加全局功能,有install方法
插件的使用在new Vue(创建)之前,因为插件里可能有对Vue构造器和原型对象的操作
new Vue({ 
   MyPlugin.install=function(构造器Vue, 选项对象options){ 
      Vue.myGlobalMethod=function(){//逻辑...}   //添加全局方法
         Vue.directive('my-directive',{})  //添加全局资源
         Vue.mixin({ reated:function(){//逻辑...} ..}) //注入组件选项
         Vue.prototype.$myMethod=function(methodOptions){//逻辑...} //添加实例方法
   }
}
使用:在new Vue之前调用Vue.use()静态方法Vue.use(MyPlugin)
相当于调用插件本身的install方法

例子:

<div id="app">
    {{msg}}、{{msg|myFilter}}
</div>

/*声明插件对象
  以后引用别人的插件:import导入*/
let myPlugin={
    //install方法在Vue.use(myPlugin)会运行
    install(Vue,options){ //一定要有install方法
       //提供静态属性或方法
       Vue.isVue=()=>{ }
       //提供原型属性或方法
       Vue.prototype.$sayVue=()=>{ }
       //全局注册(组件、过滤器、指令、混入)
       Vue.filter('myFilter',(data)=>{
          return data.toUpperCase()
        })
     }
};
//console.log(Vue.isVue);

//使用插件(在new Vue前使用),使用时实际是调用插件对象的install方法
Vue.use(myPlugin);  
/*console.log(Vue.isVue);
  Object.prototype.say=()=>{ }
  let obj=new Object()
  obj.say();*/

let vm=new Vue({
   el:'#app',
   data:{
       msg:'hello'
   },
   methods:{}
})
//console.log(vm.$sayVue);

十二、核心插件Vue-router

安装:cdn导入在线资源
<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>

2、基本应用

1. 组件定义
let foo={template:`<div>this is foo</div>`};
let bar={template:`<div>this is bar</div>`};

2. 创建路由器对象
var router=new VueRouter({
   routes:[{path:"/foo",component:foo},{path:"/bar",component:bar}]
})

3. 组件注册
new Vue({
   el:"#app",
   router
})

4. 模板内容
<router-link to=“/foo”>跳转到foo</router-link>
<router-link to="/bar">跳转到bar</router-link>
<div id="app">
     <router-view></router-view>
</div>

例子:

<div id="app">
    <!--4.使用路由-->
    <div>
      <router-link to="/a">去A路由</router-link>
      <a href="#/a">a标签路由去A</a>
      <router-link to="/b">去B路由</router-link>    
    </div>
    <div>
      <!--组件显示的位置-->
      <router-view></router-view>
    </div>
</div>

//声明组件
    let myA={
      template: `
        <div>A组件</div>
      `
    };
    let myB={
      template: `
        <div>B组件</div>
      `
    };

3、路由重定向——刚一进入页面就有内容

path redirect
path component

在这里插入图片描述
4、重命名

let routes=[{
      path:'/',
      /*component:myA
        路由重定向
        redirect:'/a'
        redirect:{name:'aRoute'}*/
      }, 
      {path:'/a',
       component:myA,
       //路由名称
       name:'aRoute',
       //重命名,a和aa都可跳到a组件
       alias:'/aa'   
      }, 
      {path:'/b',
       component: myB,
       //路由名称
       name:'bRoute'
      }
];

6、动态路由声明

User组件(同一个模板,不同的用户)
/user/:1001  User组件加载1001用户的数据
/user/:1002  User组件加载1002用户的数据
{
   path:'/user/:id', //和普通路由的区别在path
   name:'',
   component:User,
}

接收 动态路由参数:this.$route.params.id

例子:

<div id="app">
    <div>
      <router-link to="/user/id/1/username/zhangsan">去A路由</router-link>
    </div>

    <div>    
      <router-view></router-view>
    </div>
</div>

//声明组件
    let myA={
      data(){
        return{
          id:null,
          username:''
        }
      },
      template: `
        <div>A组件{{id}}--{{username}}</div>
      `,
        watch:{
           msg(newValue,oldValue){ },
           $route(新路由to,旧路由from){
              this.id=this.$route.params.id;  //将路由对象的...复制到数据模型中
              this.username=this.$route.params.username;
           }
      }*/
      //监听路由变化——路由守卫(写在了myA组件内,在myA组件内路由变化才会触发路由守卫)
      beforeRouteUpdate(to,from,next){  //继续执行下一步,无next:只更新路由 不跳转组件
        console.log(to.params);  
        this.id=to.params.id;
        this.username=to.params.username;
        //继续 next();
        //阻断 next(false);  //(一般不这么用)可控制不跳转路由,页面变了但路由没变
      }
    };

    let myB={
      template:`
        <div>B组件</div>
      `
    };

    //1.定义路由对象数组 
    let routes=[{
      //动态路由
      // /user/id/1/username/zhangsan  
      // /user/id/2/username/lisi  
      path:'/user/id/:id/username/:username',
      component:myA
    }];
    //2.创建路由器对象
    let router=new VueRouter({
      routes:routes
    })
    //3.注册路由器对象
    new Vue({
      el:'#app',
      router:router,
      components:{
        'my-a':myA,
        'my-b':myB
      },
      data:{
        msg:'hello'
      },
      methods:{}
})

7、路由守卫(用的比较少,动态路由会用到路由守卫)

1.全局前置守卫:router.beforeEach((to,from,next)=>{})
  全局后置守卫:router.afterEach((to,from)=>{})
2.路由独享守卫
  { path:'',
    beforeEnter(to,from,next){}
  }
3.组件内守卫:监听组件内路由变化
  beforeRouteEnter(to,from,next){     
     console.log(this,'beforeRouteEnter');  //this-->window
     next();
  },
  beforeRouteUpdate(to,from,next){    
     console.log(this,'beforeRouteUpdate');   //this-->组件实例
     next();
  },
  beforeRouteLeave(to,from,next){    
     console.log(this,'beforeRouteLeave');   //this-->组件实例
     next();
  }
{ sayName(){this-->obj},
  sayName:()=>{this-->vue实例}
}
obj.sayName();

例子

<div id="app">
    <!--4.使用路由-->
    <div>
      <router-link to="/a/1">去A路由</router-link>
      <router-link to="/b">去B路由</router-link>
      <a href="#/a/2">a标签路由去A</a>
    </div>

    <div>
      <!--路由组件显示的位置-->
      <router-view></router-view>
    </div>
</div>

//声明组件
let myA={
   template:`
       <div>A组件</div>
   `,
   //2.3设置 组件内守卫
   beforeRouteEnter(to,from,next){
      //this-->window
      console.log(this,"beforeRouteEnter");
      next();
    },
    beforeRouteUpdate(to,from,next){
      //this-->组件实例
      console.log(this,'beforeRouteUpdate');
      next();
    },
    beforeRouteLeave(to,from,next){
      //this-->组件实例
      console.log(this,'beforeRouteLeave');
      next();
    }
  };

let myB={
   template:`
        <div>B组件</div>
   `
};

//1.定义路由对象数组 route路由对象  router路由器对象  routes路由对象组
let routes=[{
   path:'/a/:id',
   component:myA
  }, 
  { path:'/b',
    component:myB,
    //2.2设置 路由独享守卫
    /*beforeEnter(to,from,next){
        console.log(to,from,next,'beforeEnter');
        next();
      }*/
}];

//2.创建路由器对象
let router=new VueRouter({
   routes:routes
})

//2.1设置 全局路由前置/后置守卫
/*router.beforeEach((to,from,next)=>{
     console.log(to,from,next,'beforeEach----');
     next();
  })
  router.afterEach((to,from)=>{
     console.log(to,from,'afterEach----');
     if(to.path=='/a'){  //跳转到a路由做一些事情
        console.log('添加其他处理');
     }
  })*/

//3.注册路由器对象
new Vue({
   el:'#app',
   router:router,
   components:{
     'my-a':myA,
     'my-b':myB
   },
   data:{
      msg:'hello'
   },
   methods:{},
})

8、路由嵌套
day6-1, 27.23到53.05

//课程管理
{ path:"/course",
  component:Course, 
  children:[{ path:'grade',   //孩子可不写父路由的路径
              component:Grade},
            { path:'plan',
              component:Plan}
           ]  
}
//成绩表
{ plan:"/course/grade", //二级路由
  component:Grade
},
//排课表
{ path:"/course/plan",
  component:Plan
}

在这里插入图片描述
例子:

<div id="app">
    <div style="float:left;">
      <router-link to="/student">学生管理</router-link>
      <br>
      <router-link to="/course">课程管理</router-link>
      <br>
      <router-link to="/course/grade">成绩</router-link>
      <router-link to="/course/plan">排课</router-link>
    </div>

    <div style="float:right;">
      <router-view></router-view>
    </div>
</div>

let Student={
       template:`<div>
                 学生管理
               </div>`
    };
    /*<div>
         <router-link to="/course/grade">成绩</router-link>
         <router-link to="/course/plan">排课</router-link>
      </div>*/

let Course={
    template:`<div>课程管理模块
               <div>
                  <router-view></router-view>  
               </div>
              </div>`
};

let CourseGrade={
    template:`<div>
              成绩表
             </div>`
};

let CoursePlan={
    template:`<div>
              排课表
             </div>`
 };

let routes=[{
   path:'/student',
   component:Student
   }, 
 { path:'/course',
   component:Course,
   redirect:'/course/grade',
   children:[{
      path:'grade',
      component: CourseGrade
     }, 
     {path: 'plan',
      component: CoursePlan
     }
   ]
 }/*{
   path:'/course/grade',
   component:CourseGrade
 }*/]

let router=new VueRouter({
   routes:routes
})

new Vue({
   el:'#app',
   router,
   data:{ 
       msg:'hello'
   },
   methods:{}
})

9、导航(跳转路由)
day6-1,59.13到1.29.42

    带不带参
      push({
        path:'/a?查询字符串'
      })
      push({
        path:'/a',
        query:{}
      })

例子:

<div id="app">
    <!--4.使用路由-->
    <div>
      <router-link to="/a">去A路由</router-link>
      <router-link to="/b">去B路由</router-link>
      <a href="#/a">a标签路由去A</a>
      <button @click="$router.push('/a')">跳转到A1</button>
      <button @click="$router.push('a')">跳转到A2</button>
      <button @click="toPath">跳转到A3</button>
      <button @click="toPath2">跳转到A4带参</button>
    </div>
    
    <div>
      <!--路由组件显示的位置-->
      <router-view></router-view>
    </div>
</div>

//声明组件
    let myA={
      template:`
        <div>A组件</div>
      `,
      //接受路由参数
      created(){
        console.log(this.$route);
      }
    };
    let myB={
      template:`
        <div>B组件</div>
      `
    };
    //1.定义路由对象数组 route路由对象  router路由器对象  routes路由对象组
    let routes=[{
      path:'/a',
      name:'mya',
      component:myA
    }, 
    {
      path:'/b',
      name:'myb',
      component:myB
    }];
    // 2.创建路由器对象
    let router=new VueRouter({
      routes:routes,
      //路由模式 哈希路由
      mode:'hash'
      //历史记录路由,浏览器路由browser
      //mode:'history'
    })
    //3.注册路由器对象
    new Vue({
      el:'#app',
      router:router,
      components:{
        'my-a':myA,
        'my-b':myB
      },
      data:{
        msg:'hello'
      },
      methods:{
        //路由跳转
        toPath(){
          this.$router.push('a');
          //this.$router.push({ path:'/a'})
          //this.$router.push({ name:'mya'})
          //路由记录、历史记录跳转
          //this.$router.go(1)
          //替换路由
          //this.$router.replace('/a');
        },
        //带参跳转
        toPath2(){
          this.$router.push({
            name:'mya',
            //参数带在查询字符串,刷新页面不会消失
            query:{
              id:1
            },
            //参数是一次性携带,刷新页面会消失
            params:{
              name:'zhangsan'
            }
          })
          /*this.$router.push({
            //有效,带在query上
            //path:'/a?id=1001',
            path:'/a',
            //带参
            //有效
            query:{
              id:1
            },
            //params无效
          }) */
        },
      }
})

十三、Vuex

在火狐浏览器(扩展和主题),搜索Vue devtools,添加到火狐中(扩展)
然后在控制面板中就有了

十四、axios

引入:bootcdn、cdnjs(涵盖范围少)

//Vue-router和Vuex在企业级开发中——模块化
用npm或cnpm安装axios:$npm install axios --save

github资源

十五、JSX可作为渲染函数返回的模板

render(createElement){
    return(<div>hello</div>)
}

十六、脚手架Vue-cli

全局安装Vue-cli工具,安装该工具后就可使用Vue命令了
全局安装:cnpm install -g @Vue/cli
创建项目:Vue create my-project
vxe-table是一个基于vue的表格组件,支持增删改查、虚拟滚动、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、数据分页、模态窗口、自定义模板、灵活的配置项、丰富的扩展插件等... 设计理念: 面向现代浏览器,高效的简洁 API 设计 模块化表格、按需加载、插件化扩展 为单行编辑表格而设计,支持增删改查及更多扩展,强大的功能的同时兼具性能 功能: Basic table (基础表格) Grid (高级表格) Size (尺寸) Striped (斑马线条纹) Table with border (带边框) Cell style (单元格样式) Column resizable (列宽拖动) Maximum table height (最大高度) Resize height and width (响应式宽高) Fixed column (固定列) Grouping table head (表头分组) Highlight row and column (高亮行、列) Table sequence (序号) Radio (单选) Checkbox (多选) Sorting (排序) Filter (筛选) Rowspan and colspan (合并行或列) Footer summary (表尾合计) Import (导入) Export (导出) Print (打印) Show/Hide column (显示/隐藏列) Loading (加载中) Formatted content (格式化内容) Custom template (自定义模板) Context menu(快捷菜单) Virtual Scroller(虚拟滚动) Expandable row (展开行) Pager(分页) Form(表单) Toolbar(工具栏) Tree table (树形表格) Editable CRUD(增删改查) Validate(数据校验) Data Proxy(数据代理) Keyboard navigation(键盘导航) Modal window(模态窗口) Charts(图表工具) 更新日志: v3.3.6 table 修复 getRadioReserveRecord、getCheckboxReserveRecords 方法错误获取数据问题 grid 修复数据代理删除行为存在新增数据的问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值