Vue笔记

00、ES6的补充(后面用到)

a、let/var

  • ES5中的var是没有块级作用域的(if/for)
  • ES6中的let是有块级作用域的(if/for)

ES5之前因为if和for都没有块级作用域的概念, 所以在很多时候, 我们都必须借助于function的作用域来解决应用外面变量的问题.(因为function有作用域)

ES6中,加入了let, let它是有if和for的块级作用域

 //1.变量作用域: 变量在什么范围内是可用的
  {
    var name = 'why';
    console.log(name);
  }
  //在括号外面依然可以用,因为ES5之前var是没有块级作用域的概念的
  console.log(name);
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>

<script>
  // 没有块级作用域引起的问题: for的块级
  var btns = document.getElementsByTagName('button');
  for (var i=0; i<btns.length; i++) {
    console.log('执行成功');
    btns[i].addEventListener('click', function () {
      console.log('第' + i + '个按钮被点击');
    })
</script>

在这里插入图片描述

上面也就相当于执行五次循环,每次循环都是下面这段代码

var i = 5;
 var btns = document.getElementsByTagName('button');


    btns[i].addEventListener('click', function () {
      console.log('第' + i + '个按钮被点击');
    })

    btns[i].addEventListener('click', function () {
      console.log('第' + i + '个按钮被点击');
    })
...*3

一、解决作用域问题(ES5)

若要解决此问题,ES5之前可以使用闭包

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>

<script>
// 为什么闭包可以解决问题: 函数是一个作用域.
  var btns = document.getElementsByTagName('button');
  for (var i=0; i<btns.length; i++) {
    (function (num) {
      console.log('执行成功');
      btns[i].addEventListener('click', function () {
        console.log('第' + num + '个按钮被点击');
      })
    })(i)
  }
</script>

上面就相当于执行五次循环,每次循环的num值都不一样。

在这里插入图片描述

二、解决作用域问题(ES6)

如果使用ES6语法的话,就简单很多

let关键字是有块级作用域的概念的

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>

<script>
const btns = document.getElementsByTagName('button')
  for (let i = 0; i < btns.length; i++) {
    btns[i].addEventListener('click', function () {
      console.log('第' + i + '个按钮被点击');
    })
</script>      

在这里插入图片描述

b、const的使用

const关键字,在很多语言中已经存在, 比如C/C++中, 主要的作用是将某个变量修饰为常量.

在JavaScript中也是如此, 使用const修饰的标识符为常量, 不可以再次赋值.

建议: 在ES6开发中,优先使用const, 只有需要改变某一个标识符的时候才使用let.

<script>
/*

  //1.注意一: 一旦给const修饰的标识符被赋值之后, 不能修改
  const name = 'why';
  //错误,不能修改
  name = 'abc';
*/

  // 2.注意二: 在使用const定义标识符,必须进行赋值
  // const name;

  // 3.注意三: 常量的含义是指向的对象不能修改, 但是可以改变对象内部的属性.
  const obj = {
    name: 'why',
    age: 18,
    height: 1.88
  }
  //这样是错误的: obj = {}
  
  console.log(obj);

  obj.name = 'kobe';
  obj.age = 40;
  obj.height = 1.87;

  console.log(obj);
</script>

在这里插入图片描述

c、对象增强语法

在这里插入图片描述

d、高阶函数

高阶函数2.js

let total = nums.filter(n => n<100).map(n => n*2).reduce((pre,n) => pre + n)
<div id="app">
</div>

<script src="../js/vue.js"></script>
<script src="高阶函数2.js"></script>
<script>
  console.log(total)
</script>

按照我们之前的方式

高阶函数.js:

const nums = [10, 20, 111, 222, 444, 40, 50]
// 1.需求: 取出所有小于100的数字
let newNums = []
for (let n of nums) {
  if (n < 100) {
    newNums.push(n)
  }
}

// 2.需求:将所有小于100的数字进行转化: 全部*2
let new2Nums = []
for (let n of newNums) {
  new2Nums.push(n * 2)
}

// 3.需求:将所有new2Nums数字相加,得到最终的结果
let total = 0
for (let n of new2Nums) {
  total += n
}

console.log(total);

利用函数:

const nums = [10, 20, 111, 222, 444, 40, 50]

//1.filter函数的使用
// 10, 20, 40, 50
let newNums = nums.filter(function (n) {
  return n < 100
})
//filter函数返回一个数组,将 n<100 的值都存入数组中
// console.log(newNums);

// 2.map函数的使用
// 20, 40, 80, 100
let new2Nums = newNums.map(function (n) { // 20
  return n * 2
})

// 3.reduce函数的使用
// reduce作用对数组中所有的内容进行汇总,第一个参数中的第一个值preValue的默认值是第二个参数的值0,第一个参数的第二个值n是
//数组中的值
let total = new2Nums.reduce(function (preValue, n) {
  return preValue + n
}, 0)
console.log(total);

//第一次: preValue 0 n 20
//第二次: preValue 20 n 40
//第三次: preValue 60 n 80
//第四次: preValue 140 n 100
//240

转换为高阶函数:

let total = nums.filter(function (n) {
  return n < 100
}).map(function (n) {
  return n * 2
}).reduce(function (prevValue, n) {
  return prevValue + n
}, 0)
console.log(total);

ES6允许使用“箭头”(=>)定义函数

var f = a = > a
//等同于
var f = function(a){
   return a;
}

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

//无形参
var f = () => 5;

// 等同于
var f = function () { return 5 };

//多个形参
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};

利用箭头函数的高阶函数:

即开头的高阶函数2.js

let total = nums.filter(n => n<100).map(n => n*2).reduce((pre,n) => pre + n)

01、认识和安装Vuejs

a、Vuejs简单介绍

Vue (读音 /vjuː/,类似于 view),不要读错。

Vue是一个渐进式的框架,什么是渐进式的呢?

  • 渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验。
  • 或者如果你希望将更多的业务逻辑使用Vue实现,那么可以使用Vue的核心库以及其生态系统。
  • 比如Core+Vue-router+Vuex,也可以满足你各种各样的需求。

Vue有很多特点和Web开发中常见的高级功能

  • 解耦视图和数据
  • 可复用的组件
  • 前端路由技术
  • 状态管理
  • 虚拟DOM

b、Vuejs安装

安装Vue的方式有很多:

  1. 方式一:直接CDN引入

    你可以选择引入开发环境版本还是生产环境版本

    <!-- 开发环境版本,包含了有帮助的命令行警告 --> 
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 生产环境版本,优化了尺寸和速度 -->
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    
  2. 方式二:下载和引入

    开发环境 https://vuejs.org/js/vue.js 
    生产环境 https://vuejs.org/js/vue.min.js
    
  3. 方式三:NPM安装

    后续通过webpack和CLI的使用,我们使用该方式。

02、Vuejs初体验

a、HelloVue代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">{{message}}</div>         <!--显示下方数据-->
<div>{{message}}</div>                  <!--显示{{message}}-->

<script src="../js/vue.js"></script>
<script>
    //let(变量)/const(常量)
    //let可以再赋值,const定义后不可以再赋值
    //在es6里面基本上不用var了,var没有变量作用域的概念,有缺陷。
    const app = new Vue({
        el:'#app',  //用于挂载要管理的元素
        data:{      //定义一些数据
            message:'你好啊,李银河'
        }
    })
</script>
</body>
</html>

在这里插入图片描述

1、代码做了什么事?

我们来阅读JavaScript代码,会发现创建了一个Vue对象。

创建Vue对象的时候,传入了一些options:{}

{}中包含了el属性:该属性决定了这个Vue对象挂载到哪一个元素上,很明显,我们这里是挂载到了id为app的元素上

{}中包含了data属性:该属性中通常会存储一些数据

这些数据可以是我们直接定义出来的,比如像上面这样,也可能是来自网络,从服务器加载的。

2、浏览器执行代码的流程:

执行到8~9行代码显然出对应的HTML

执行第16行代码创建Vue实例,并且对原HTML进行解析和修改。

并且,目前我们的代码是可以做到响应式的。

b、vue列表展示

<body>

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

<script src="../js/vue.js"></script>
<script>
    const app = new Vue({
        el:'#app',
        data:{
            movies:['星际穿越','少年派','大话西游']
        }
    })
</script>
</body>

这样是显示不出来列表的,网页显示是这样:

在这里插入图片描述

可以利用v-for显示(后面会讲)

<body>

<div id="app">
   <ul>
       <li v-for="item in movies ">
           {{item}}
       </li>
   </ul>
</div>

<script src="../js/vue.js"></script>
<script>
    const app = new Vue({
        el:'#app',
        data:{
            movies:['星际穿越','少年派','大话西游']
        }
    })
</script>
</body>

在这里插入图片描述

在浏览器控制台添加列表数据

在这里插入图片描述

c、小案例:计数器

点击 + 计数器+1

点击 - 计数器 -1

这里,我们又要使用新的指令和属性了

新的属性:methods,该属性用于在Vue对象中定义方法。

新的指令:@click, 该指令用于监听某个元素的点击事件,并且需要指定当发生点击时,执行的方法(方法通常是methods中定义的方法)

@click 是v-on:click 的语法糖,即 v-on:click的简写。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <h2>当前计数:{{counter}}</h2>
    <!--
    <button v-on:click="add">+</button>
    <button v-on:click="sub">-</button>
    -->
    <button @click="add">+</button>
    <button @click="sub">-</button>
</div>

<script src="../js/vue.js"></script>
<script>
    const app = new Vue({
        el:'#app',
        data:{
            counter:0
        },
        methods:{
           /* add:function (){
                console.log('add被执行');
                this.counter++;
            },
            sub:function (){
                console.log('sub被执行');
                this.counter--;
            }*/
            add(){
                console.log('add被执行');
                this.counter++;
            },
            sub(){
                console.log('sub被执行');
                this.counter--;
            }
        }
    })
</script>


</body>
</html>

在这里插入图片描述

03、Vue的MVVM

在这里插入图片描述

在这里插入图片描述

MVVM与MVC最大的区别就是:它实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变。

Vue实例中的data相当于Model层,而ViewModel层的核心是Vue中的双向数据绑定,即Model变化时VIew可以实时更新,View变化也能让Model发生变化。

整体看来,MVVM比MVC精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作DOM元素。因为在MVVM中,View不知道Model的存在,Model和ViewModel也观察不到View,这种低耦合模式提高代码的可重用性。

  1. View层:
    视图层
    在我们前端开发中,通常就是DOM层。
    主要的作用是给用户展示各种信息。

  2. Model层:

    ​ 数据层
    ​ 数据可能是我们固定的死数据,更多的是来自我们服务器,从网络上请求下来的数据。
    ​ 在我们计数器的案例中,就是后面抽取出来的obj,当然,里面的数据可能没有这么简单。

  3. VueModel层:

    ​ 视图模型层
    ​ 视图模型层是View和Model沟通的桥梁。
    ​ 一方面它实现了Data Binding,也就是数据绑定,将Model的改变实时的反应到View中
    ​ 另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情 况下改变对应的Data。

04、Vue的生命周期

在这里插入图片描述

a、什么是生命周期?

简而言之:从生到死的过程,从Vue实例创建-运行-销毁的过程

Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、销毁等一系列过程

b、生命周期方法?

Vue从生到死的过程中伴随着各种各样的事件,这些事件会自动触发一些方法.这些方法我们统称为生命周期方法

生命周期钩子 = 生命周期函数 = 生命周期事件

一、创建期间生命周期方法

beforeCreate:

created:

beforeMount

mounted

二、运行期间生命周期方法

beforeUpdate

updated

三、销毁期间的生命周期方法

beforeDestroy

destroyed

四、代码解释

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue生命周期方法</title>
    <!--引入vue框架-->
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <p>{{msg}}</p>
</div>
 
<script>
   
    let vm = new Vue({
        el:'#app',
        data:{
            msg:"IT程序员的日常"
        },
        methods:{
            say:function () {
                console.log("IT程序员的日常");
            }
        },
        beforeCreate:function () {
            /*执行beforeCreate的时候,表示Vue刚刚出生,还没有任何内容,data/methods都没有初始化*/
            //console.log(this.msg);
            //this.say();
            //console.log(this.say);
        },
        created:function () {
            /*执行created的时候,表示Vue实例已经初始化好了部分内容,data/methods
            想在Vue实例中最早访问到data/methods,只有在这个方法才能访问
            */
            //console.log(this.msg);
            //this.say();
            //console.log(this.say);
        },
        beforeMount:function () {
            /*执行beforeMount,表示已经根据数据编译好了模板,但是还没有渲染到界面上*/
            // console.log(document.querySelector("p").innerText);
            // console.log(document.querySelector("p").innerHTML);
        },
        mounted:function () {
            /*执行mounted,表示已经根据数据编译好了模板,已经将模板有渲染到界面上,此时可以对界面进行其他操作了*/
            console.log(document.querySelector("p").innerText);
            console.log(document.querySelector("p").innerHTML);
        },
        beforeUpdate:function(){
            /*主要data中的数据发生了变化就会执行
             执行beforeUpdate时候,data的数据已经是最新的了,但是没有更新界面上的数据*/
            // console.log(this.msg);
            // console.log(document.querySelector("p").innerText);
            // console.log(document.querySelector("p").innerHTML);
        },
        updated:function () {
           /*主要data中的数据发生了变化就会执行
            执行updated时候,data的数据已经是最新的了,界面上的数据也已经更新*/
            
            /*
            console.log(this.msg);
            console.log(document.querySelector("p").innerText);
            console.log(document.querySelector("p").innerHTML);
            */
        },
         beforeDestroy:function(){
            /*执行beforeDestroy的时候,表示Vue实例即将销毁,但是还未销毁,实例中的数据等都可以使用
              最后能使用Vue实例的地址
             */
        },
        destroyed:function () {
            /*
             执行destroyed的时候,表示vue实例完全销毁,实例中的任何内容都不能被使用了
            */
        }
 
    })
</script>
</body>
</html>

05、模板语法

a、插值操作

一、Mustache

Mustache语法:也就是双大括号语法。

Mustache:胡子/胡须

<div id="app">
  <h2>{{message}}</h2>
  <h2>{{message}}, 李银河!</h2>

  <!--mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式-->
  <h2>{{firstName + lastName}}</h2>
  <h2>{{firstName + ' ' + lastName}}</h2>
  <h2>{{firstName}} {{lastName}}</h2>
  <h2>{{counter * 2}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      firstName: 'kobe',
      lastName: 'bryant',
      counter: 100
    },
  })
</script>

在这里插入图片描述

二、v-once

当我们不希望界面某些数据跟随改变的时候,可以使用 v-once 指令

该指令后面不需要跟任何表达式(比如之前的v-for后面是由跟表达式的)

该指令表示元素和组件(组件后面才会学习)只渲染一次,不会随着数据的改变而改变

<div id="app">
  <h2>{{message}}</h2>
  <h2 v-once>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    }
  })
</script>

在这里插入图片描述

若利用响应式修改数据:message改变,添加 v-once 的标签所显示的界面没有被重新渲染

在这里插入图片描述

三、v-html

某些情况下,我们从服务器请求到的数据本身就是一个HTML代码

如果我们直接通过 {{}} 来输出,会将HTML代码也一起输出。但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容。

如果我们希望解析出HTML展示,可以使用v-html指令,该指令后面往往会跟上一个string类型,会将字符串中的html解析出来并且进行渲染

<div id="app">
  <h2>{{url}}</h2>
  <h2 v-html="url"></h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      url: '<a href="http://www.baidu.com">百度一下</a>'
    }
  })
</script>

在这里插入图片描述

四、v-test

v-text作用和Mustache比较相似:都是用于将数据显示在界面中

v-text通常情况下,接受一个string类型

<div id="app">
  <h2>{{message}}, 李银河!</h2>
  <h2 v-text="message">, 李银河!</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    }
  })
</script>

在这里插入图片描述

五、v-pre

v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法

比如下面的代码:

第一个h2元素中的内容会被编译解析出来对应的内容

第二个h2元素中会直接显示{{message}}

<div id="app">
  <h2>{{message}}</h2>
  <h2 v-pre>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    }
  })
</script>

在这里插入图片描述

六、v-cloak

在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签

v-cloak指令:它会在vue实例结束编译时从绑定的html元素上移除

cloak: 斗篷

这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

当网络较慢,网页还在加载 Vue.js ,而导致 Vue 来不及渲染,这时页面就会显示出 Vue 源代码。我们可以使用 v-cloak 指令来解决这一问题。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    [v-cloak] {
      display: none;
    }
  </style>
</head>
<body>

<!--vue解析之前,div中有 v-cloak属性,此标签被隐藏-->
<!--vue解析之后,div中没有 v-cloak属性了,此标签显示-->
<div id="app" v-cloak>
  <h2>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  setTimeout(function () {
    const app = new Vue({
      el: '#app',
      data: {
        message: '你好啊'
      }
    })
  }, 1000)
</script>

</body>
</html>

b、动态绑定属性

前面我们学习的指令主要作用是将值插入到我们模板的内容当中。

但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定。

比如动态绑定a元素的href属性,比如动态绑定img元素的src属性

这个时候,我们可以使用v-bind指令:

  1. 作用:动态绑定属性

  2. 缩写(语法糖)::

一、v-bind的基本使用

<div id="app">
  <!-- 错误的做法: 这里不可以使用mustache语法-->
  <!--<img src="{{imgURL}}" alt="">-->
    
  <!-- 正确的做法: 使用v-bind指令 -->
  <!--alt属性是在图片不能正常加载时候显示的提示语-->
  <img v-bind:src="imgURL" alt="">
  <a v-bind:href="aHref">百度一下</a>
  <!--<h2>{{}}</h2>-->

  <!--语法糖的写法-->
  <img :src="imgURL" alt="">
  <a :href="aHref">百度一下</a>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      imgURL: 'https://img11.360buyimg.com/mobilecms/s350x250_jfs/t1/20559/1/1424/73138/5c125595E3cbaa3c8/74fc2f84e53a9c23.jpg!q90!cc_350x250.webp',
      aHref: 'http://www.baidu.com'
    }
  })
</script>

在这里插入图片描述

二、v-bind动态绑定class

很多时候,我们希望动态的来切换class,比如:

当数据为某个状态时,字体显示红色。当数据另一个状态时,字体显示黑色。

绑定class有两种方式:

  • 对象语法
  • 数组语法
1、对象语法

对象语法的含义是:class后面跟的是一个对象

对象语法有下面这些用法:

用法一:直接通过{}绑定一个类

<h2 :class="{'active': isActive}">Hello World</h2>

用法二:也可以通过判断,传入多个值

<h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2>

用法三:和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title active line三个类

<h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>

用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
<h2 class="title" :class="classes">Hello World</h2>

案例:

<head>
  <meta charset="UTF-8">
  <title>Title</title>

  <style>
    .active {
      color: red;
    }
  </style>
</head>
<body>

<div id="app">
  <h2 class="active">{{message}}</h2>
  <h2 :class="active">{{message}}</h2>

  <!--<h2 v-bind:class="{key1: value1, key2: value2}">{{message}}</h2>-->
  <!--<h2 v-bind:class="{类名1: true, 类名2: boolean}">{{message}}</h2>-->
  <h2 class="title" v-bind:class="{active: isActive, line: isLine}">{{message}}</h2>
  <h2 class="title" v-bind:class="getClasses()">{{message}}</h2>
  <button v-on:click="btnClick">按钮</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isActive: true,
      isLine: true
    },
    methods: {
      btnClick: function () {
        this.isActive = !this.isActive
      },
      getClasses: function () {
        return {active: this.isActive, line: this.isLine}
      }
    }
  })
</script>

</body>

在这里插入图片描述

点击按钮之后:在这里插入图片描述

2、数组语法

数组语法的含义是:class后面跟的是一个数组

数组语法有下面这些用法:

加单引号表示字符串,没有单引号表示变量

用法一:直接通过[]绑定一个类
<h2 :class="['active']">Hello World</h2>

用法二:也可以传入多个值
<h2 :class="['active', 'line']">Hello World</h2>

用法三:和普通的类同时存在,并不冲突
注:会有title/active/line三个类
<h2 class="title" :class="['active', 'line']">Hello World</h2>

用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
<h2 class="title" :class="classes">Hello World</h2>

案例:

<div id="app">
  <!--加上' ' 表示字符串-->
  <h2 class="title" :class="['active', 'line']">{{message}}</h2>
  <!--不加' ' 表示变量-->
  <h2 class="title" :class="[active, line]">{{message}}</h2>
  <h2 class="title" :class="getClasses()">{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      active: 'aaaaaa',
      line: 'bbbbbbb'
    },
    methods: {
      getClasses: function () {
        return [this.active, this.line]
      }
    }
  })
</script>

在这里插入图片描述

三、v-bind和v-for的联合使用

作业需求: 点击列表中的哪一项, 那么该项的文字变成红色

v-for中使用下标属性

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .active{
      color: red;
    }
  </style>
</head>
<body>

<!--作业需求: 点击列表中的哪一项, 那么该项的文字变成红色-->
<div id="app">
  <ul>
    <li v-for="(m, index) in movies" :class="{active: isActive == index}" @click="changeColor(index)">{{m}}</li>
  </ul>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      movies: ['海王', '海尔兄弟', '火影忍者', '进击的巨人'],
      isActive : -1
    },
    methods:{
      changeColor(index){
        this.isActive = index;
      }
    }
  })
</script>

</body>
</html>

在这里插入图片描述

四、v-vind动态绑定style

我们可以利用v-bind:style来绑定一些CSS内联样式

在写CSS属性名的时候,比如font-size,我们可以使用驼峰式 (camelCase) fontSize ,或短横线分隔 (kebab-case,记得用单引号括起来) ‘font-size’

绑定class有两种方式:

  • 对象语法
  • 数组语法
1、对象语法
:style="{color: currentColor, fontSize: fontSize + 'px'}"

style后面跟的是一个对象类型

对象的key是CSS属性名称

对象的value是具体赋的值,值可以来自于data中的属性

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .title {
      font-size: 50px;
      color: red;
    }
  </style>
</head>
<body>

<div id="app">
  <!--<h2 :style="{key(属性名): value(属性值)}">{{message}}</h2>-->

  <!--'50px'必须加上单引号, 否则是当做一个变量去解析-->
  <!--<h2 :style="{fontSize: '50px'}">{{message}}</h2>-->

  <!--finalSize当成一个变量使用-->
  <!--<h2 :style="{fontSize: finalSize}">{{message}}</h2>-->
  <h2 :style="{fontSize: finalSize + 'px', backgroundColor: finalColor}">{{message}}</h2>
  <h2 :style="getStyles()">{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      finalSize: 100,
      finalColor: 'red',
    },
    methods: {
      getStyles: function () {
        return {fontSize: this.finalSize + 'px', backgroundColor: this.finalColor}
      }
    }
  })
</script>

</body>
</html>

在这里插入图片描述

2、数组语法
<div v-bind:style="[baseStyles, overridingStyles]"></div>

style后面跟的是一个数组类型
多个值以 , 分割即可

案例:

<div id="app">
  <h2 :style="[baseStyle, baseStyle1]">{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      baseStyle: {backgroundColor: 'red'},
      baseStyle1: {fontSize: '100px'},
    }
  })
</script>

在这里插入图片描述

c、计算属性

计算属性是写在实例的computed选项中的

一、计算属性的基本使用

<div id="app">
  <h2>{{firstName + ' ' + lastName}}</h2>
  <h2>{{firstName}} {{lastName}}</h2>

  <h2>{{getFullName()}}</h2>

  <!--计算属性不用加(),将其当做一个属性使用-->
  <h2>{{fullName}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      firstName: 'Lebron',
      lastName: 'James'
    },
    // computed: 计算属性()
    computed: {
      fullName: function () {
        return this.firstName + ' ' + this.lastName
      }
    },
    methods: {
      getFullName() {
        return this.firstName + ' ' + this.lastName
      }
    }
  })
</script>

在这里插入图片描述

二、计算属性的复杂操作

<div id="app">
  <h2>总价格: {{totalPrice}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      books: [
        {id: 110, name: 'Unix编程艺术', price: 119},
        {id: 111, name: '代码大全', price: 105},
        {id: 112, name: '深入理解计算机原理', price: 98},
        {id: 113, name: '现代操作系统', price: 87},
      ]
    },
    computed: {
      totalPrice: function () {
        let result = 0
        for (let i = 0; i < this.books.length; i++) {
          result += this.books[i].price
        }
        return result

        /*其他循环的写法*/
        /*for (let i in this.books) {
        result += this.books[i].price
      }*/

        /* for (let book of this.books) {
          result +=book.price
         }*/
      }
    }
  })
</script>

在这里插入图片描述

三、计算属性的getter和setter

上面写的计算属性只是简写,实际上是利用get方法

<div id="app">
  <h2>{{fullName}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      firstName: 'Kobe',
      lastName: 'Bryant'
    },
    computed: {
    /*  fullName: function () {
        return this.firstName + ' ' + this.lastName
      }*/
      //上面是计算属性的简写,实际上计算属性是这样写的
      // 计算属性一般是没有set方法, 只读属性.
      fullName: {
        //若使用set方法,需要接受参数
        set: function(newValue) {
          // console.log('-----', newValue);
          //利用空格分隔符,将firstName和lastName分出
          const names = newValue.split(' ');
          this.firstName = names[0];
          this.lastName = names[1];
        },
        get: function () {
          return this.firstName + ' ' + this.lastName
        }
      },


    }
  })
</script>

在这里插入图片描述

四、计算属性和methods的对比

计算属性有缓存

<div id="app">
  <!--1.直接拼接: 语法过于繁琐,一般不用-->
  <!--<h2>{{firstName}} {{lastName}}</h2>-->

  <!--2.通过定义methods-->
  <h2>{{getFullName()}}</h2>
  <h2>{{getFullName()}}</h2>
  <h2>{{getFullName()}}</h2>
  <h2>{{getFullName()}}</h2>

  <!--3.通过computed-->
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
  <h2>{{fullName}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  // angular -> google
  // TypeScript(microsoft) -> ts(类型检测)
  // flow(facebook) ->
  const app = new Vue({
    el: '#app',
    data: {
      firstName: 'Kobe',
      lastName: 'Bryant'
    },
    methods: {
      getFullName: function () {
        console.log('getFullName');
        return this.firstName + ' ' + this.lastName
      }
    },
    computed: {
      fullName: function () {
        console.log('fullName');
        return this.firstName + ' ' + this.lastName
      }
    }
  })

</script>

在这里插入图片描述

d、事件监听

在前端开发中,我们需要经常和用于交互

这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等

在Vue中如何监听事件呢?使用v-on指令

一、v-on的基本使用

<div id="app">
  <h2>{{counter}}</h2>

<!--可以直接写成表达式-->
  <button v-on:click="counter++">+</button>
  <button v-on:click="counter--">-</button>

<!--写成函数形式-->
  <!--<button v-on:click="increment">+</button>-->
  <!--<button v-on:click="decrement">-</button>-->

<!--语法糖格式:@ -->
  <button @click="increment">+</button>
  <button @click="decrement">-</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      counter: 0
    },
    methods: {
      increment() {
        this.counter++
      },
      decrement() {
        this.counter--
      }
    }
  })
</script>

二、v-on的参数问题

<div id="app">
  <!--1.事件调用的方法没有参数-->
  <button @click="btn1Click()">按钮1</button>
  <button @click="btn1Click">按钮1</button>


   
  <!--<button @click="btn2Click(123)">按钮2</button>-->
<!--  生成的是 -------- 123-->

<!--如果函数需要参数,但是没有传入, 那么函数的形参为undefined--> 
  <!--  <button @click="btn2Click()">按钮2</button>-->
<!--  生成的是 -------- undefined-->


<!--2.在事件定义时, 写方法时省略了小括号, 但是方法本身是需要一个参数的,
    这个时候, Vue会默认将浏览器生产的event原生事件对象作为参数传入到方法-->
  <button @click="btn2Click">按钮2</button>

  <!--3.方法定义时, 我们需要event对象, 同时又需要其他参数-->
  <!-- 在调用方法时, 如何手动的获取到浏览器参数的event对象: $event-->
  <button @click="btn3Click(abc, $event)">按钮3</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      abc: 123
    },
    methods: {
      btn1Click() {
        console.log("btn1Click");
      },
      btn2Click(event) {
        console.log('--------', event);
      },
      btn3Click(abc, event) {
        console.log('++++++++', abc, event);
      }
    }
  })

</script>

在这里插入图片描述

在这里插入图片描述

三、v-on的修饰符

事件冒泡:点击子级元素接收到事件后,会把他接收到的事件传播给他的父级,也就是父级也会接收到子级的事件

<div id="app">
  <!--1. .stop修饰符的使用,阻止Html的事件冒泡-->
  <!--若不加 .stop修饰符,会发生事件冒泡,即点击div中的按钮,回调bthClick方法,会将接收到的事件传播给他的父级(div),也就是divClick也会被回调-->
  <div @click="divClick">
    aaaaaaa
    <button @click.stop="btnClick">按钮</button>
  </div>

  <!--2. .prevent修饰符的使用-->
  <!--阻止默认行为,点击提交后,不会直接跳转,而会回调submitClick方法-->
  <br>
  <form action="baidu">
    <input type="submit" value="提交" @click.prevent="submitClick">
  </form>

  <!--3. @keyup. 监听某个键盘的键帽,这是监听回车,当回车敲下并回弹的时候,回调keyup方法-->
  <!--若只写 @keyup="keyUp",则点击任意键盘回弹的时候都会回调keyUp方法-->
    <input type="text" @keyup.enter="keyUp">

  <!--4. .once修饰符的使用-->
  <!--  点击回调只会调用一次,点击第二次的时候不会被调用-->
  <button @click.once="btn2Click">按钮2</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      btnClick() {
        console.log("btnClick");
      },
      divClick() {
        console.log("divClick");
      },
      submitClick() {
        console.log('submitClick');
      },
      keyUp() {
        console.log('keyUp');
      },
      btn2Click() {
        console.log('btn2Click');
      }
    }
  })
</script>

e、条件判断

一、v-if的使用

v-if后面的条件为false时,对应的元素以及其子元素不会渲染,也就是根本没有不会有对应的标签出现在DOM中。

<div id="app">
  <h2 v-if="isShow">
    <div>abc</div>
    <div>abc</div>
    <div>abc</div>
    <div>abc</div>
    <div>abc</div>
    {{message}}
  </h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isShow: true
    }
  })
</script>

二、v-if和v-else的使用

<div id="app">
  <h2 v-if="isShow">
    <div>abc</div>
    <div>abc</div>
    <div>abc</div>
    <div>abc</div>
    <div>abc</div>
    {{message}}
  </h2>
  <h1 v-else>isShow为false时, 显示我</h1>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isShow: true
    }
  })
</script>

三、v-if和v-else-if和v-else的使用

一般这种多条件判断不用这些语法,可以封装为计算属性或者函数

<div id="app">
  <h2 v-if="score>=90">优秀</h2>
  <h2 v-else-if="score>=80">良好</h2>
  <h2 v-else-if="score>=60">及格</h2>
  <h2 v-else>不及格</h2>

  <!--<h1>{{result}}</h1>-->
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      score: 62
    },
    /*
    //一般不用 v-if v-else-if...的语法,因为有些麻烦,一般封装为函数或者计算属性,方便调用
    computed: {
      result() {
        let showMessage = '';
        if (this.score >= 90) {
          showMessage = '优秀'
        } else if (this.score >= 80) {
          showMessage = '良好'
        }else if (this.score>=60){
          showMessage = '及格'
        }else {
          showMessage = '不及格'
        }
        return showMessage
      }
    }*/
  })
</script>

四、用户登陆切换的案例

<div id="app">
  <span v-if="isUser">
    <label for="username">用户账号</label>
    <input type="text" id="username" placeholder="用户账号">
  </span>
  <span v-else>
    <label for="email">用户邮箱</label>
    <input type="text" id="email" placeholder="用户邮箱">
  </span>
  <button @click="isUser = !isUser">切换类型</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      isUser: true
    }
  })
</script>

在这里插入图片描述

但是这样会有一个问题,如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。
但是按道理讲,我们应该切换到另外一个input元素中了。在另一个input元素中,我们并没有输入内容。

这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
在上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。

解决方案:
如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key
并且我们需要保证key的不同

更改方案:

添加key,保证key的不同

<div id="app">
  <span v-if="isUser">
    <label for="username">用户账号</label>
    <input type="text" id="username" placeholder="用户账号" key="username">
  </span>
  <span v-else>
    <label for="email">用户邮箱</label>
    <input type="text" id="email" placeholder="用户邮箱" key="email">
  </span>
  <button @click="isUser = !isUser">切换类型</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      isUser: true
    }
  })
</script>

在这里插入图片描述

五、v-show的使用

v-show的用法和v-if非常相似,也用于决定一个元素是否渲染:

v-if和v-show都可以决定一个元素是否渲染,那么开发中我们如何选择呢?

  1. v-if当条件为false时,压根不会有对应的元素在DOM中。

  2. v-show当条件为false时,仅仅是将元素的display属性设置为none而已。

开发中如何选择呢?

  1. 当需要在显示与隐藏之间切片很频繁时,使用v-show
  2. 当只有一次切换时,通过使用v-if
<div id="app">
  <!--v-if: 当条件为false时, 包含v-if指令的元素, 根本就不会存在dom中-->
  <h2 v-if="isShow" id="aaa">{{message}}</h2>

  <!--v-show: 当条件为false时, v-show只是给我们的元素添加一个行内样式: display: none-->
  <h2 v-show="isShow" id="bbb">{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isShow: true
    }
  })
</script>

f、循环遍历

一、v-for循环数组

当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成。
v-for的语法类似于JavaScript中的for循环
格式如下:item in items的形式

<div id="app">
  <!--1.在遍历的过程中,没有使用索引值(下标值)-->
  <ul>
    <li v-for="item in names">{{item}}</li>
  </ul>

  <!--2.在遍历的过程中, 获取索引值-->
  <ul>
    <li v-for="(item, index) in names">
      {{index+1}}.{{item}}
    </li>
  </ul>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      names: ['why', 'kobe', 'james', 'curry']
    }
  })
</script>

在这里插入图片描述

二、v-for遍历对象

<div id="app">
  <!--1.在遍历对象的过程中, 如果只是获取一个值, 那么获取到的是value-->
  <ul>
    <li v-for="item in info">{{item}}</li>
  </ul>


  <!--2.获取key和value 格式: (value, key) -->
  <ul>
    <li v-for="(value, key) in info">{{value}}-{{key}}</li>
  </ul>


  <!--3.获取key和value和index 格式: (value, key, index) -->
  <ul>
    <li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
  </ul>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      info: {
        name: 'why',
        age: 18,
        height: 1.88
      }
    }
  })
</script>

在这里插入图片描述

三、v-for使用过程添加key

官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性

为什么需要这个key属性呢(了解)
这个其实和Vue的虚拟DOM的Diff算法有关系

这里我们借用React’s diff algorithm中的一张图来简单说明一下:

在这里插入图片描述

当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点,我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率

所以我们需要使用key来给每个节点做一个唯一标识
Diff算法就可以正确的识别此节点
找到正确的位置区插入新的节点。
所以一句话,key的作用主要是为了高效的更新虚拟DOM

div id="app">
  <ul>
    <!--绑定的key必须和后面的item是一一对应的,所以不能用index-->
    <li v-for="item in letters" :key="item">{{item}}</li>
  </ul>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      letters: ['A', 'B', 'C', 'D', 'E']
    }
  })
</script>

四、检测哪些数组方法响应式

为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,不用刷新页面,视图会发生对应的更新

<div id="app">
  <ul>
    <li v-for="item in letters">{{item}}</li>
  </ul>
  <button @click="btnClick">按钮</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      letters: ['a', 'b', 'c', 'd']
    },
    methods: {
      btnClick() {
        // 1.push方法 添加最后一个元素
        // this.letters.push('aaa')
        // this.letters.push('aaaa', 'bbbb', 'cccc')

        // 2.pop(): 删除数组中的最后一个元素
        // this.letters.pop();

        // 3.shift(): 删除数组中的第一个元素
        // this.letters.shift();

        // 4.unshift(): 在数组最前面添加元素
        // this.letters.unshift()
        // this.letters.unshift('aaa', 'bbb', 'ccc')

        // 5.splice作用: 删除元素/插入元素/替换元素
        /*
        删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
        替换元素: 第二个参数, 表示我们要替换几个元素, 后面是用于替换前面的元素
        插入元素: 第二个参数, 传入0, 并且后面跟上要插入的元素
        */
        this.letters.splice(1, 3, 'm', 'n', 'l', 'x')
        // this.letters.splice(1, 0, 'x', 'y', 'z')

        // 6.sort() 排序
        // this.letters.sort()

        // 7.reverse()  反转数据
        // this.letters.reverse()

        // 注意: 通过索引值修改数组中的元素,这个不是响应式的
        // this.letters[0] = 'bbbbbb';

        //若要修改,则可以通过以下代码修改
        //方法1
        // this.letters.splice(0, 1, 'bbbbbb')

        //方法2
        // set(要修改的对象, 索引值, 修改后的值)
        // Vue.set(this.letters, 0, 'bbbbbb')
      }
    }
  })
</script>

通过索引值修改数组中的元素,不是响应式的,可以利用splice或者Vue的set方法修改数组中的元素

g、书籍购物车案例

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <!--rel 属性规定当前文档与被链接文档之间的关系。
只有 rel 属性为 "stylesheet" 值得到了所有浏览器的支持。其他值只得到了部分地支持。
  -->
  <link rel="stylesheet" href="style.css">
</head>
<body>

<div id="app">
  <div v-if="books.length">
    <table>
      <thead>
      <tr>
        <th></th>
        <th>书籍名称</th>
        <th>出版日期</th>
        <th>价格</th>
        <th>购买数量</th>
        <th>操作</th>
      </tr>
      </thead>
      <tbody>
      <tr v-for="(item, index) in books">
        <td>{{item.id}}</td>
        <td>{{item.name}}</td>
        <td>{{item.date}}</td>
        <!--用 | 符号后跟过滤器,前面加相当于传入的参数-->
        <td>{{item.price | showPrice}}</td>
        <td>
         <!--当书籍的数量小于或等于1的时候,无效化 - 按钮-->
          <button @click="decrement(index)" v-bind:disabled="item.count <= 1">-</button>
          {{item.count}}
          <button @click="increment(index)">+</button>
        </td>
        <td><button @click="removeHandle(index)">移除</button></td>
      </tr>
      </tbody>
    </table>
    <h2>总价格: {{totalPrice | showPrice}}</h2>
  </div>
  <h2 v-else>购物车为空</h2>
</div>

<script src="../js/vue.js"></script>
<script src="main.js"></script>
<script>
</script>
</body>
</html>

main.js

const app = new Vue({
  el: '#app',
  data: {
    books: [
      {
        id: 1,
        name: '《算法导论》',
        date: '2006-9',
        price: 85.00,
        count: 1
      },
      {
        id: 2,
        name: '《UNIX编程艺术》',
        date: '2006-2',
        price: 59.00,
        count: 1
      },
      {
        id: 3,
        name: '《编程珠玑》',
        date: '2008-10',
        price: 39.00,
        count: 1
      },
      {
        id: 4,
        name: '《代码大全》',
        date: '2006-3',
        price: 128.00,
        count: 1
      },
    ]
  },
  methods: {
    // 可以这样写,也可以加个过滤器
    //getFinalPrice(price) {
    //   return '¥' + price.toFixed(2)
    // }
    increment(index) {
      this.books[index].count++
    },
    decrement(index) {
      this.books[index].count--
    },
    removeHandle(index) {
      this.books.splice(index, 1)
    }
  },
  computed: {
    totalPrice() {
      let totalPrice = 0
      for (let i = 0; i < this.books.length; i++) {
        totalPrice += this.books[i].price * this.books[i].count
      }
      return totalPrice
    }
  },
  filters: {
    showPrice(price) {
      return '¥' + price.toFixed(2)
    }
  }
})

style.css

table {
  /*border:边框的样式  1px表示线的像素 solid表示实线 #e9e9e9表示白色*/
  border: 1px solid #e9e9e9;
  /*为表格设置合并边框模型
    border-collapse 属性设置表格的边框是否被合并为一个单一的边框,还是象在标准的 HTML 中那样分开显示
    */
  border-collapse: collapse;
/*设置表格的边框间距*/
  border-spacing: 0;
}

th, td {
  /*边距填充*/
  padding: 8px 16px;
  border: 1px solid #e9e9e9;
  text-align: left;
}

th {
  /*背景颜色*/
  background-color: #5c6b77;
  /*字体颜色*/
  color: #f7f7f7;
  /*设置段落字体的粗细*/
  font-weight: 600;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值