Vue教程-day03-2018年12月21日笔记

文章目录


image-20210829061216097

上午 - 9点 - 12点 - 3小时

第44个视频 - javascript高阶函数的使用

第一种方法:

image-20210828222824979

第二种方法:

image-20210828232756427

image-20210828232914541

第三种方法:reduce

编程范式的分类有:命令式编程,声明式编程。

编程范式的分类有:面向对象编程(js当中多态体现太灵活,TypeScript跟java很像,也有接口),函数式编程。

面向对象编程的第一公民是对象。

函数式编程的第一公民是函数。

函数式编程的好处是可以进行很多的链式编程。

举个例子,下面的需求用传统的方法,就没有函数式编程简单。

image-20210828234128951

像是上面的做法,传统的做法,是挺麻烦的。

有三个高阶函数:filter、map、reduce。

什么是高阶函数呢?就是函数的参数,也是一个函数,这种套路。

image-20210829000320215

filter函数

image-20210829001526675

map函数

image-20210829001548203

reduce函数

看一下reduce的源码:

image-20210829000353173

我们看到源码当中定义了两个函数,源码是typescript的,d表示的是定义的意思,definition。

在js当中如果定义了两个函数,下面的函数,会把上面的函数覆盖掉。

但是它这里不会被覆盖。因为它是typescript。

定义两个函数,叫做函数的重载。

我们看上面的源码可以知道,reduce函数可以传入两个参数,一个是回调函数,一个是初始化值。

需求用高阶函数实现

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script src="../js/vue.js"></script>
<script>
  const nums = [10, 20, 111, 222, 444, 40, 50];

  /*
  * filter函数是会自动遍历数组的
  * filter函数的参数,要求传入一个回调函数。
  * filter的回调函数必须返回一个布尔值。
  * 如果返回的是true,函数内部会自动将这次回调的n,加入到新的数组中。
  * 如果返回的是false,函数内部就会自动过滤掉n。
  * 场景:如果你需要对一个数组进行过滤,就使用filter
  * */
  let newNums = nums.filter(function (n) {
    return n < 100;
  });

  console.log(newNums); // [10. 20. 40, 50]

  /*
  * map函数是会自动遍历数组的。
  * map函数的参数,要求传入一个回调函数
  * 每个遍历的元素都会被回调函数处理成新的值,
  * 然后新的值会组成新的数组。
  * 场景:如果你需要对一个数组当中的每个元素做统一的操作,就使用map
  * */
  let new2Nums = newNums.map(function (n) {
    return n * 2;
  });

  console.log(new2Nums); // [20, 40, 80, 100]
  /*
  * reduce函数是会自动遍历数组的。
  * 源码中关于reduce有两个定义。
  * 1、reduce函数的参数,要求传入1个参数,是回调函数
  * 2、reduce函数的参数,要求传入2个参数,一个是回调函数,一个是初始化值。
  * 这个回调函数有两个值,第一个参数上一次返回的值previousValue,第二个参数是当下的值currentValue。
  * reduce函数的返回是一个值。
  * 场景:如果你需要对数组中所有元素进行汇总,所有相乘,所有相加等场景。
  * */
  let new3Nums = new2Nums.reduce(function (preValue, n) {
    return preValue + n
  }, 0);

  console.log(new3Nums); // 240

  // 假设new2Nums = [20, 40, 80]
  // 第一次:preValue - 0 , n - 20 return x
  // 第二次:preValue - x ,n - 40 return y
  // 第三次:preValue - y , n - 80 return z

</script>
</body>
</html>
链式编程

上面的代码,阅读性还是比较差的。

我们还可以这样写:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script src="../js/vue.js"></script>
<script>
  const nums = [10, 20, 111, 222, 444, 40, 50];
  let total = nums.filter(function (n) {
    return n < 100;
  }).map(function (n) {
    return n * 2;
  }).reduce(function (preValue, n) {
    return preValue + n;
  }, 0);
</script>
</body>
</html>

这就叫做函数式编程。函数就是函数编程的第一公民。

箭头函数

用箭头函数的话,还能够简化上面的链式编程代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script src="../js/vue.js"></script>
<script>
  const nums = [10, 20, 111, 222, 444, 40, 50];
  let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n);
</script>
</body>
</html>
之前的购物车案例总价格计算使用reduce

image-20210829002902817

考试

问题:三个高阶函数,filter,map,reduce都是用在什么场景。

答案:在数组或对象遍历的场景,filter是对每个元素需要过滤的场景,map是对每个元素统一进行一个操作的场景,reduce是对每个元素进行汇总的场景。


第45个视频 - v-model的使用和原理

image-20210829011531869

image-20210829003735369

数据已经被绑定到了input里面了。

  • 之前我们是通过 mustache语法将data当中的数据,绑定到了某个元素的内容的位置。

  • 现在我们是在表单元素的标签属性里面添加了v-model之后,就把data当中的数据绑定到了表单当中。

而且v-model是一种双向绑定。

  • 以前我们的mustache是一种响应式。就是将data里面的数据,在页面上面显示。
    • 但是界面上的数据改变,并不会影响我们的data当中的数据。
  • 现在我们的v-model是一种双向绑定,我们将data里面的数据和表单元素进行了绑定。
    • 我们的data数据发生改变,我们的表单元素当中的内容,也会发生改变。
    • 我们的表单元素当中的数据发生改变,我们的data当中的数据也会发生改变。
示例代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <input type="text" v-model="message">
  {{message}}
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '张润'
    },
  })
</script>
</body>
</html>
v-model的原理和本质

先自己手动来实现双向绑定。

第一步,先将data里面的message,绑定到input当中,这个要用到动态绑定属性。

image-20210829004626691

第二步,我们需要把input当中用户输入的值,绑定到我们的message上。

input输入框是有一个默认的事件的,这个事件就叫做input。

就好像button按钮有一个事件叫做点击,叫做click是一样的。

我们可以在input输入框上面监听它的input事件,然后拿到用户输入的值。

监听它的input事件,我们可以通过v-on来实现,那么怎么拿到用户输入的值呢?

当页面上产生了事件的时候,浏览器会为我们生成一个事件对象event。

这个event当中的target中的value,这里就是保存着用户在input输入框中的输入。

具体的代码就如下所示:

image-20210829005516219

具体的代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!--<input type="text" v-model="message">-->

  <input type="text" :value="message" @input="valueChange">
  <h2>{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '江疏影'
    },
    methods: {
      valueChange(event) {
        this.message = event.target.value;
      }
    }
  });
</script>
</body>
</html>
对input的小小总结

第一点:我们之前学过label和input的绑定。label标签的for属性和input标签的id属性设置为一个值,这样就可以将label和input进行绑定。

这样拿到的效果,就是点击label的文字,input自动获得聚焦。input的value属性就是显示的文本,input的placeholder就是没有输入的时候默认显示的文本。

第二点:input输入框是有一个对应的事件就是input,用户输入的值,就是浏览器event对象target的value属性值中保存。

下面的截图就是浏览器的input事件对象:

根据我的测试,现在浏览器当中,用户的输入产生的事件对象是InputEvent,数据是存放在event.data属性当中。

image-20210829010128562

第三点:我们的v-bind到现在为止,遇到了这么几种情况。

  • 动态绑定属性的:
    • img标签的src
    • a标签的href
    • button标签的disabled
    • input标签的value。
  • 动态绑定样式的,倒是还没有遇到什么玩意。

第四点:我们的事件监听,现在也遇到了这么几种情况:

  • button或者div的click事件
  • submit的提交事件,我们通过.stop修饰符。
  • 事件冒泡的时候,我们通过.prevent修饰符。
  • 键盘输入的事件,我们通过.{keyCode},最经典的就是enter的事件。
  • 组件的点击事件。
  • input的input事件。

这给我提了个醒,需要好好研究一下浏览器事件对象

回归v-model的本质

我们刚才通过了v-on事件监听input事件,通过v-bind绑定input的value属性。

通过这两个指令,实现了v-model的效果。

image-20210829011309974

我们对上面的代码修改一下:

我们事件绑定的时候,不要用方法,我们直接用表达式:

image-20210829011444351

v-model的本质总结

image-20210829011633658

考试

问题:v-model的本质可以相当于哪两个指令的结合?

答案:v-bind和v-on


第46个视频 - v-model结合radio类型使用

image-20210829011744028

radio比较常见的就是让用户选择一下性别男女这种场景。

表单
  • input:text、radio
  • textarea
input radio和v-model结合
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <label for="male">
    <input type="radio" id="male" name="sex" value="" v-model="sex"></label>
  <label for="female">
    <input type="radio" id="female" name="sex" value="" v-model="sex"></label>
  <h2>您选择的性别是:{{sex}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '',
      sex: ''
    },
  })
</script>
</body>
</html>
小知识点
  • 如上面的例子,一旦两个input radio都使用v-model绑定了同一个数据,那么radio的name属性可以没有。
  • html当中,radio是必须加上name属性,才能够保障单选框的互斥。
  • 如果在data的sex默认值是一个,这样男对应的radio,就是默认选中的。
考试

问题:input radio都有哪些常见的属性值?

答案:

  1. type:radio、text
  2. id:和label的for配合
  3. name:radio互斥
  4. value:选中的值

第47个视频 - v-model和checkbox的结合使用

checkbox是复选框,是比较特殊。

两种情况。

单个单选框和多选框。

image-20210829015412868

v-model结合单选框

单选框会在什么场景用到呢?

就是很多网站有:同意协议,这种地方用到。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <label for="agree">
    <input type="checkbox" id="agree" v-model="isAgree">同意协议
  </label>
  <h2>您选择的是:{{userAgree}}</h2>
  <button :disabled="!isAgree">下一步</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '',
      isAgree: false
    },
    computed: {
      userAgree(){
        if (this.isAgree){
          return '同意协议'
        }else{
          return '不同意协议'
        }
      }
    }
  })
</script>
</body>
</html>

效果图如下:

image-20210829014149549

v-model结合多选框

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <input type="checkbox" value="张润" v-model="hobbies">张润<br>
  <input type="checkbox" value="王柯" v-model="hobbies">王珂<br>
  <input type="checkbox" value="赵柯" v-model="hobbies">赵柯<br>
  <input type="checkbox" value="梁缘" v-model="hobbies">梁缘<br>
  <input type="checkbox" value="舒砚" v-model="hobbies">舒砚<br>
  <input type="checkbox" value="于越" v-model="hobbies">于越<br>
  <input type="checkbox" value="周放" v-model="hobbies">周放<br>
  <h2>您喜欢的美女是:{{hobbies}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '',
      hobbies: []
    },
  })
</script>
</body>
</html>

效果图如下:

image-20210829015229054

考试

问题:单选框,v-model对应的是data当中的什么类型的数据?多选框,v-model对应的是data当中的什么类型的数据?

答案:单选框是boolean值,多选框是数组。


第48个视频 - v-model结合select类型的使用

select类型就是下拉框。

降到这里的时候,王红元开始闲聊。

说郭德纲相声当中,问了捧哏一个问题:你知道曹操的父亲是谁吗?

是曹香蕉。因为三国里面有一句话是我与你父相交甚好

image-20210829020927462

选择一个的效果

image-20210829020251613

选择一个的代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!--选择一个-->
  <!--现在网站,展示select这种类型的玩意,很少,现代的网站。-->
  <select name="abc" id="" v-model="pretty">
    <option value="张芷溪">张芷溪</option>
    <option value="唐艺昕">唐艺昕</option>
    <option value="孙嘉璐">孙嘉璐</option>
    <option value="谈莎莎">谈莎莎</option>
    <option value="张熙媛">张熙媛</option>
    <option value="郑罗茜">郑罗茜</option>
    <option value="王文娜">王文娜</option>
  </select>
  <h2>您选择的美女是:{{pretty}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '',
      pretty: '王文娜'
    },
  })
</script>
</body>
</html>
选择多个的效果

image-20210829020534004

按住ctrl键,选择多个

选择多个的代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!--选择多个-->
  <select name="abc" v-model="pretty" multiple>
    <option value="张芷溪">张芷溪</option>
    <option value="唐艺昕">唐艺昕</option>
    <option value="孙嘉璐">孙嘉璐</option>
    <option value="谈莎莎">谈莎莎</option>
    <option value="张熙媛">张熙媛</option>
    <option value="郑罗茜">郑罗茜</option>
    <option value="王文娜">王文娜</option>
  </select>
  <h2>您选择的美女是:{{pretty}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '',
      pretty: []
    },
  })
</script>
</body>
</html>
考试

问题:select怎么设置选择多个?

答案:在select标签当中添加multiple属性。


第49个视频 - input中的值绑定

讲一个概念。

王红元看官方网站的时候,看到的值绑定

一开始,不知道值绑定什么意思?

后来发现,其实非常简单。

image-20210829021205687

值绑定的意思就是从一个最开始的数据当中读取值,然后进行绑定,然后进行v-model的双向数据绑定。

值绑定的效果

image-20210829021913132

值绑定的代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <label v-for="item in pretties" :for="item">
    <input type="checkbox" :value="item" :id="item" v-model="pretty">{{item}}<br>
  </label>
  <h2>您选择的美女是:{{pretty}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '',
      pretty: [],
      pretties: ['张芷溪', '唐艺昕', '孙嘉璐', '谈莎莎', '张熙媛', '郑罗茜', '王文娜']
    },
  })
</script>
</body>
</html>
总结

image-20210829022148739

考试

问题:从以前到现在,王红元演示过哪些v-bind绑定属性的例子了

答案:img标签的src,a标签的href,button标签的disabled,input标签的value、id,label的for属性

第50个视频 - v-model的修饰符的使用

image-20210829022529839

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <!--1、.lazy修饰符-->
  <!--现在的方式,动态绑定太频繁了,用户还没有输入完成,你就绑定了,你是实时绑定的-->
  <!--.laze修饰符使用的场景就是:用户输入完成了之后敲了回车,或者input输入框失去焦点的时候-->
  <input type="text" v-model.lazy="message">
  <h2>您的输入是:{{message}}</h2>

  <!--2、.number修饰符-->
  <!--默认的input的数据输入,v-model绑定的时候得到的就是string类型-->
  <!--我们的需求场景是,想要拿到用户的输入是一个number类型。-->
  <input type="number" v-model.number="age">
  <h2>您的输入是:{{age}},数据类型是{{typeof age}}</h2>

  <!--3、.trim修饰符-->
  <!--意思就是去掉首尾空格。-->
  <input type="text" v-model.trim="person">
  <h2>您的输入是:{{person}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '孟子义',
      age: 26,
      person: '',
    },
  });
</script>
</body>
</html>

到这里为止,基础语法的部分就讲解完成了:

image-20210829030409018

考试

问题:罗列三个v-model的修饰符

答案:lazy、number、trim。


第51个视频 - 组件化的实现和使用步骤

image-20210829024554443

  • angular的组件化思想是:一个模块是一个组件。
  • 数据结构有很多:栈、堆、链表、图、树
    • 树结构当中,用得最多的是二叉树。任何的树结构都可以被抽象成为一个二叉树的。
    • 二叉树当中,用得比较多的,就是二叉搜索树
    • 二叉搜索树当中为了保障树的平衡的话,还有二三四树红黑树
    • 有机会可以学习一下,参考王红元的简书。面试大公司,面试数据结构还是很多的。

image-20210829025835462

  • 这是一个组件化的思想,后面还会降到一个模块化的思想,要注意进行区分对比。
es6当中继承的写法

image-20210829030053401

考试

问题:组件化和模块化是一个东西吗?

答案:不是。

第52个视频 - 组件化的基本使用过程

注册组件

image-20210829030247226

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
</div>

<script src="../js/vue.js"></script>
<script>

  // ES6语法
  // 以前在es5当中定义字符串,可以使用单引号,也可以使用双引号。
  // 这时候换行必须搞个+号

  // const str = 'abc' +
  //     'abc';
  // const str2 = `abc
  // cdefghjklmnopqrstuvwxyz`


  // 在es6当中,可以使用反引号。``。 支持换行的。

  //1、创建组件构造器对象
  const cpnConstructor = Vue.extend({
    template: `
      <div>
        <h2>我是孟子义</h2>
        <p>我是北京电影学院校花</p>
        <p>我长得真漂亮</p>
      </div>`
  });

  //2、注册组件
  // Vue.component('组件的标签名',组件构造器)
  Vue.component('cpn', cpnConstructor);

  //3、使用组件 <cpn></cpn>
  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
  });
</script>
</body>
</html>

效果如下:

image-20210829031844417

考试

问题:注册组件的三个步骤是什么?

答案:第一步:创建组件构造器,Vue.extend(),传入一个参数就是对象,对象里面写template属性,属性值是html代码。

第二步:注册逐渐,使用Vue.components(),传入两个参数,第一个参数是自定义标签字符串,第二个组件构造器。

第三步:使用自定义组件标签在vue管理范围内,使用自定义组件。


第53个视频 - 全局组件和局部组件

image-20210829032502978

image-20210829032528341

讲一下全局组件和局部组件:talk is cheap,show me the code。

代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn></cpn>
  <cpn></cpn>
</div>

<div id="app2">
  <cpn></cpn>
</div>

<script src="../js/vue.js"></script>
<script>

  //1、创建组件构造器
  const cpnC = Vue.extend({
    template:`
    <div>
      <h2>我是孟子义</h2>
      <p>我是韫秋,哈哈哈哈哈哈</p>
    </div>
    `
  })

  //2、注册组件(这种注册组件的方式,就是全局组件)
  //全局组件意味着,可以在多个vue的实例当中使用。
  Vue.component('cpn',cpnC)

  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
  })

  const app2 = new Vue({
    el: '#app2'
  })
</script>
</body>
</html>

效果:

image-20210829033257385

如何注册局部组件

注册在vue的实例内部:

  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
    components: {
      //属性名cpn,就是使用组件时候的标签名
      //属性值cpnC,
      cpn: cpnC
    }
  })
考试

问题:怎么注册局部组件

答案:在vue实例当中options对象中,使用components属性的对象值中,注册局部组件。开发当中,局部组件使用得最多。开发当中一般也就只有一个vue实例,很少有多个vue实例的。


下午 - 14:30 - 18:00 - 3个半小时

第54个视频 - 父组件和子组件的区分

image-20210829052311517

父子组件代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn2></cpn2>
</div>

<script src="../js/vue.js"></script>
<script>

  // 第一个组件构造器
  const cpnC1 = Vue.extend({
    template: `
      <div>
        <h2>我是儿子</h2>
        <p>哈哈哈哈哈</p>
      </div>
    `
  });

  // 第二个组件构造器
  const cpnC2 = Vue.extend({
    template: `
      <div>
      <h2>我是父亲</h2>
      <p>哈哈哈哈哈</p>
      <cpn1></cpn1>
      </div>
    `,
    components: {
      cpn1: cpnC1
    }
  });

  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
    components: {
      cpn2: cpnC2
    }
  });
</script>
</body>
</html>

【小知识点】上面的代码中,div#app中已经不可以使用cpn1了,因为cpn1没有在vue实例当中注册。

效果如下:

image-20210829053149848

一个默认的说法是:Vue实例也可以看成是一个组件。Vue实例看成是最顶层的组件,就是root组件。

image-20210829054040271

考试

问题:一个组件1注册在组件2当中,组件2注册在vue实例当中,那么可以在vue实例当中使用组件1吗?

答案:不可以。因为组件1并没有在vue实例当中注册。vue实例使用组件2的时候,组件2已经编译好了。会解析组件2的模版,会发现里面有一个组件1,会优先去自己注册的components当中去找一下组件1的template,如果没有找到,就会去全局组件当中找,找到之后,就会编译。在vue实例当中使用组件2的时候,组件2的内容都已经编译完成了,vue实例根本就不知道有什么组件1的存在。

在本质上,vue会把组件的template都会渲染成render函数的。


第55个视频 - 注册组件的语法糖写法

全局组件注册和局部组件注册的语法糖的写法,请看下面的代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn1></cpn1>
  <cpn2></cpn2>
</div>

<script src="../js/vue.js"></script>
<script>
  //  全局组件注册的传统写法
  // const cpnC1 = Vue.extend({
  //   template: `
  //     <div>
  //       <h2>我是标题</h2>
  //       <p>哈哈哈哈哈</p>
  //     </div>
  //   `
  // })
  // Vue.component('cpn1',cpnC1)

  // 全局组件注册的语法糖写法
  Vue.component('cpn1',{
    template: `
      <div>
        <h2>我是标题1</h2>
        <p>哈哈哈哈哈</p>
      </div>
    `
  })
  // 局部组件注册的语法糖写法
  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
    components: {
      'cpn2': {
        template: `
          <div>
          <h2>我是标题2</h2>
          <p>呵呵呵呵呵</p>
          </div>
        `
      }
    }
  })
</script>
</body>
</html>

image-20210829055604330

考试

问题:vue注册组件的语法糖,本质是什么?

答案:从写法上,省略了Vue.extend()这个步骤。直接把原来的Vue.extend()括号里面,需要传递的对象参数,

放在了Vue.component(自定义标签字符串,构造器)构造器的位置。


第56个视频 - 组件template抽离的写法

之前注册组件的时候,感觉代码很乱,这是因为js代码当中包含了html代码。

之前jquery创建某个标签,再给标签里面塞进去各种属性,是一个情况和道理,看起来乱。

第一种分离写法 - 通过script标签
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
</div>

<!--第一种写法-->
<script type="text/x-template" id="cpn">
<div>
  <h2>我是标题</h2>
  <p>我是内容,哈哈哈哈</p>
</div>
</script>

<script src="../js/vue.js"></script>
<script>
  // 1 - 传统写法
  // Vue.component('cpn',{
  //   template: `
  //     <div>
  //       <h2>我是标题</h2>
  //       <p>我是内容,哈哈哈哈</p>
  //     </div>
  //   `
  // })

  Vue.component('cpn',{
    template: '#cpn'
  })
  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
  })
</script>
</body>
</html>
第二种分离写法 - template标签
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
</div>

<!--第二种写法-->
<template id="cpn">
  <div>
    <h2>我是标题</h2>
    <p>我是内容,哈哈哈哈</p>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  // 1 - 传统写法
  // Vue.component('cpn',{
  //   template: `
  //     <div>
  //       <h2>我是标题</h2>
  //       <p>我是内容,哈哈哈哈</p>
  //     </div>
  //   `
  // })

  Vue.component('cpn',{
    template: '#cpn'
  })
  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
  })
</script>
</body>
</html>

image-20210829060839172

考试

问题:组件注册时候,template分离写法有什么?

答案:在html部分,通过script标签或者template标签写template,内容放在标签内,然后给标签设置id属性,最后把id放在组件注册的template属性值中。


第57个视频 - 为什么组件data必须是函数

组件内部是不能够vue实例当中的数据的。

image-20210829061601333

组件的data属性
  1. 组件的data属性值不能够是一个对象,必须是一个函数。
  2. 而且这个函数的返回值必须是一个对象。

组件当中现在有了template属性,components属性,data属性,其实也有methods,也有生命周期函数,很像是一个vue实例,底层组件本身的原型,就是指向vue的。

image-20210829062738846

封装计数器小案例为一个组件

先讲一个需求:把原来的计数器案例,封装成一个组件。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn></cpn>
  <cpn></cpn>
  <cpn></cpn>
</div>

<template id="cpn">
  <div>
    <h2>当前计数为:{{counter}}</h2>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
</template>


<script src="../js/vue.js"></script>
<script>

  Vue.component('cpn', {
    template: '#cpn',
    data() {
      return {
        counter: 0
      };
    },
    methods: {
      increment() {
        return this.counter++
      },
      decrement(){
        return this.counter--
      }
    }
  });

  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
  });
</script>
</body>
</html>

效果如下:

image-20210829063959893

先做一个类比解释为什么data必须是函数

我们在页面中调用了三次组件。这三个组件都是不同的data对象,这是因为data是一个函数,返回值是一个对象。

我们可以看下面的代码:

<script>
  // 函数在执行的时候,都会在自己的栈空间,创建很多新的变量。
  function abc() {
    return {
      name: '许晴',
      age: 40
    }
  }
  let obj1 = abc()
  let obj2 = abc()
  let obj3 = abc()

  obj1.name = '王琳'
  obj2.age = '39'
  console.log(obj1);
  console.log(obj2);
  console.log(obj3);
</script>
  • 上面我们定义了一个函数是abc,函数的返回值是一个对象。
  • 然后我们调用了三次函数,函数的返回值给了变量接收。
  • 然后我们修改了obj1的name,修改了obj2的age,然后打印这三个对象。
  • 得到的是下面的结果:

image-20210829064822259

从结果可以知道,这是三个不同的对象,虽然是一个函数调用了三次,但是是返回了三个对象。

如果我们把上面的代码修改成下面的样子:

<script>
  const obj = {
    name: '许晴',
    age: 40
  }

  function abc() {
    return obj
  }
  let obj1 = abc()
  let obj2 = abc()
  let obj3 = abc()

  obj1.name = '王琳'
  obj2.age = '39'
  console.log(obj1);
  console.log(obj2);
  console.log(obj3);
</script>

执行的结果是:

image-20210829065116160

上面的写法,函数调用了三次,返回的就是同一个对象。

解释为什么data必须是函数

我自己的总结:函数是具有隔离性的,对象是没有隔离性的。

  • 组件是可以复用的。
  • 组件的data如果是一个对象,多个组件操作的是一个对象,那乱套了。
  • 组件的data是一个函数,具有隔离性,这样就保障组件的正确复用。
考试

问题:面试题,为什么组件的data属性必须是一个函数,不能是一个对象。

答案:因为组件是复用的,对象没有隔离性,会多个组件相互影响。


第58个视频 - 父子组件通信 - 父传子 - props

需求场景描述

什么叫通信,就是传递数据。

  • 举例场景,开发一个界面,有一个轮播图、tabbar、列表(列表里面还有各个元素)什么的。
  • 我们开发列表的时候,肯定是组件化开发的,步骤肯定是,第一步,向服务器请求列表数据。
  • 这个时候有一个问题,我们请求的列表数据,在哪里请求?
  • 列表是一个组件,列表中每个元素是一个组件。我们在什么地方写网络请求?
  • 一般情况下,我们是在最外面的组件发送网络请求的。
  • 拿到数据后,可以放在最外面的组件的data中。
  • 但是展示数据,是要交给列表中小组件元素进行展示的。
  • 这个时候,我们需要做,把数据,从最外面的组件传递到子组件。这个就是父传子的通信。

image-20210829070643760

image-20210829070741165

image-20210829071736158

  • 父传子:props
  • 子传父:自定义事件,emit发出一个事件。

image-20210829071805759

父传子第一种方式-数组-代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn :childmovies="movies" :childmessage="message"></cpn>
</div>


<template id="cpn">
  <div>
    <h2>{{childmessage}}</h2>
    <ul>
      <li v-for="item in childmovies">{{item}}</li>
    </ul>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const cpn = {
    template: '#cpn',
    data(){return {}},
    methods: {},
    props: ['childmovies','childmessage']
  };


  const app = new Vue({
    el: '#app',
    data: {
      message: '王子文',
      movies: ['海王', '海贼王', '海尔兄弟']
    },
    components: {
      // 'cpn':cpn
      cpn
    }
  });
</script>
</body>
</html>
父传子理解
  • 父组件中调用子组件,是通过子组件的标签,子组件相当于一个页面元素,有自己的属性的。
  • 子组件的属性,在子组件内部,通过props来进行设置。
  • 父组件可以给子组件动态绑定属性,通过v-bind给子组件绑定上它的自定义属性。类比给img动态绑定src属性。完成数据传输。

image-20210829073334585

父传子第一种方式-数组-评价

image-20210829073516458

父传子第二种方式-对象-数据验证

image-20210829073611610

image-20210829074015237

父传子第二种方式-对象-默认值设置-必传设置
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <cpn :childmovies="movies"></cpn>
</div>

<template id="cpn">
  <div>
    <h2>{{childmessage}}</h2>
    <ul>
      <li v-for="item in childmovies">{{item}}</li>
    </ul>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const cpn = {
    template: '#cpn',
    data(){return {}},
    methods: {},
    props: {
      //1. 类型的限制
      // childmovies: Array,
      // childmessage: String,

      //2. 提供一些默认值
      childmessage: {
        type: String,
        default: '乔欣',
        required: false
      },

      //类型是对象或者数组的时候,默认值必须是一个函数
      childmovies: {
        type: Array,
        // default: [],//这种写法在vue 2.5.3以下不会错,从2.5.17以上这样写会报错。
        default(){
          return []
        },
        required: false
      }
    }
  };


  const app = new Vue({
    el: '#app',
    data: {
      // message: '王子文',
      movies: ['海王', '海贼王', '海尔兄弟']
    },
    components: {
      // 'cpn':cpn
      cpn
    }
  });
</script>
</body>
</html>
父传子props写法的总结

image-20210829080304460

开发当中,很少用数组形式的写法,也就是很少用第一种,比较常见的是用对象形式的写法,也就是第二种。

考试

问题:父子组件怎么通信

答案:父组件中调用子组件,通过给子组件动态绑定属性,传递父组件data当中的数据。子组件的属性,定义在props中,可以接收父组件绑定的数据。props有很多种写法,每个属性都可以设置type、default、required等。其中要注意属性type是数组或对象时候,default的写法。


第59个视频 - 父子组件通信 - props驼峰标识

父传子,通信的过程中,如果子组件的props对象中的元素,属性是驼峰标识。

在父组件调用子组件,进行动态属性绑定的时候,v-bind是不支持驼峰的。

image-20210829144557717

这是关于props如果是驼峰属性,在父组件中动态绑定时候,怎么用的代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <cpn :c-info="info" :child-my-message="message"></cpn>
</div>


<template id="cpn">
  <!--
    Vue规定,子组件中包含很多元素时,必须包含一个根元素。
    一般情况下,定义子组件模板,要有一个外层的div。
  -->
  <div>
    <ul>
      <li v-for="(value,key) in cInfo">{{key}}-{{value}}</li>
    </ul>
    <h2>{{childMyMessage}}</h2>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>

  // 注册局部子组件
  const cpn = {
    template: '#cpn',
    methods: {},
    data(){
      return {}
    },
    props: {
      cInfo: {
        type: Object,
        default(){
          return {}
        },
        required: false
      },
      childMyMessage: {
        type: String,
        default: ''
      }
    }
  }

  const app = new Vue({
    el: '#app',
    data: {
      message: '孟子义',
      info: {
        name: '乔欣',
        age: 26,
        height: 1.72
      }
    },
    components: {
      cpn
    }
  })
</script>
</body>
</html>
【锚点】我的小总结

第一,前面感觉比较乱的原因,是props的写法,介绍了两种,一种是数组写法,一种是对象写法。一般都是用对象写法。

第二,对象写法props对象中,属性是对象或数组的时候,需要写成一个函数,返回一个对象的形式。

第三,对象写法props中,属性对象可以有:type(可以自定义对象类型),default,required。属性可以是一个校验函数。

image-20210829145653530

第四,子组件的template中必须包含一个根元素。

第五,子组件props中属性名是驼峰标识,可以在子组件的template中用,在v-bind进行动态绑定时候不支持,要转换。

第六,子组件的data属性必须是一个函数,不能是一个对象。

【锚点】我的小思维导图 - props写法总结

父传子

考试

问题:子组件中props中的属性,使用驼峰标识,在template标签中可以使用吗?在父组件动态绑定属性时,可以使用吗?

答案:可以。不可以。


第60个视频 - 父子组件通信 - 子传父(自定义事件)

需求场景

image-20210829160019055

image-20210829160201142

  • 父传子:最外层大组件,网络请求到数组,给侧边栏小组件和列表小组件,传输数据,让他们进行展示。
  • 子传父:一旦我的侧边栏小组件发生了点击,切换了页面,告诉外面的大组件,侧边栏组件里面点击了某个类别,让大组件去请求数据。
  • 所以,子传父,的业务场景是:子组件发生了事件,数据是谁,父组件去网络请求吧。

子传父实现方式
  1. 子组件内部,第一步监听点击事件,第二通过点击事件函数发送自定义事件
  2. 父组件内部,监听子组件的自定义事件即可。
驼峰问题
  1. 子组件发送的自定义事件,使用驼峰命名,例如itemClick。
  2. 父组件监听的时候:
    1. 使用@itemClick监听,在html单页面这种方式中不行,在脚手架中可以,如下图。
      1. 因为脚手架中组件是编译的,变成render函数,编译过程中,能够正常解析。
    2. 使用@item-click监听,不行。

image-20210829170641046

父传子和子传父的对比
  1. 父传子
    1. 父组件给子组件传递data数据,在父组件内,给子组件动态绑定属性v-bind。不支持驼峰。支持驼峰转换为横杠。
    2. 子组件内部在props中定义属性。
  2. 子传父
    1. 子组件内部监听默认事件,然后发送自定义事件。
    2. 父组件监听v-on子组件的自定义事件。不支持驼峰,不支持驼峰转换为横杠(脚手架支持)。

在这个瞬间,外面下着雨,我想你想得要死。

我现在心理强大了一些,我会去找你的。

分解-加工-记录-回顾。

【锚点】总览回顾

基础语法回顾
  1. 插值语法:mustache、v-once、v-text、v-html、v-pre、v-cloak
  2. 动态绑定:v-bind动态绑定属性(img/src、a/href、button/disabled、input/id/value、label/for)、v-bind动态绑定样式
  3. 计算属性:和methods的对比
  4. 事件监听:v-on参数传递问题、修饰符
  5. 条件判断:v-if、v-else-if、v-else、v-show
  6. 循环遍历:v-for设置key的性能优化问题
  7. 双向绑定:v-model修饰符
组件化回顾
  1. 组件options对象
    1. el
    2. data
    3. methods
    4. components
    5. template
    6. props
    7. 声明周期函数
  2. 父子通信
    1. 父传子:父组件动态绑定v-bind子组件的自定义属性,子组件的自定义属性在props中。
    2. 子传父:父组件事件监听v-on子组件的自定义事件,子组件的自定义事件在methods中通过this.$emit发送。

子传父的演示

image-20210829165520955

image-20210829165227041

image-20210829165707417

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<!--父组件模版-->
<div id="app">
  <cpn @itemclick="cpnClick"></cpn>
</div>

<!--子组件模版-->
<template id="cpn">
  <div>
    <button v-for="item in categories" @click="btnClick(item)">
      {{item.name}}
    </button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  // 子组件
  const cpn = {
    template: '#cpn',
    data(){
      return {
        categories: [
          {id: 'aaa', name: '熟女'},
          {id: 'bbb', name: '人妻'},
          {id: 'ccc', name: '御姐'},
          {id: 'ddd', name: '萝莉'}
        ]
      }
    },
    methods: {
      btnClick(item){
        // 子组件发射自定义事件
        this.$emit('itemclick',item)
      }
    }
  }

  // 父组件
  const app = new Vue({
    el: '#app',
    data: {
      message: ''
    },
    methods: {
      cpnClick(item){
        console.log('cpn组件内部发生了点击',item);
      }
    },
    components: {
      cpn
    }
  })
</script>
</body>
</html>

效果如下:

image-20210829165134756

考试

问题:子组件发送自定义事件并且传递了参数,父组件监听自定义事件,监听时候不写参数可以吗?

答案:可以的。在button点击监听浏览器默认事件时候,不写参数,会默认传入浏览器默认事件。监听子组件自定义事件,不写参数,也会默认传入子组件自定义事件。

第61个视频 - 项目演示

父子通信几个属性

image-20210829165616518

image-20210829165642904

项目演示

王红元的项目地址是HYMall

项目效果,如下图所示:

image-20210829170857447

第62个视频 - 知识回顾

  • 12月18日星期二,讲了,邂逅js和基础语法
  • 12月20日星期四,讲了,基础语法
  • 12月21日星期五,讲了,组件化开发

预习内容

image-20210829171319249

image-20210829171339767

image-20210829171357312

image-20210829171418310

image-20210829171454211

image-20210829171516624

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值