Vue2-Vue3.0学习笔记(2021年黑马程序员最新视频)

Vue 介绍

什么是 vue ?

  1. 构建用户界面
    • 用 vue 往 html 页面中填充数据,非常的方便
  2. 框架
    • 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能!
    • 要学习 vue,就是在学习 vue 框架中规定的用法!
    • vue 的指令、组件(是对 UI 结构的复用)、路由、Vuex、vue 组件库
    • 只有把上面老师罗列的内容掌握以后,才有开发 vue 项目的能力!

vue 的两个特性

  1. 数据驱动视图:

    • 数据的变化会驱动视图自动更新
    • 好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!
  2. 双向数据绑定:

    在网页中,form 表单负责采集数据,Ajax 负责提交数据

    • js 数据的变化,会被自动渲染到页面上
    • 页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中

注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)

MVVM

MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。

QQ截图20211029215318

MVVM 的工作原理

ViewModel作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。

QQ截图2021102921513ff8

当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构。

当单元素的值发生变化表时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中

起步

第一个Vue程序:

  1. 导入开发版本的 Vue.js
  2. 创建 Vue实例对象,设置el属性和data属性
  3. 使用简洁的模板语法把数据渲染到页面
<body>
  <div id="app">
    {{message}}
  div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
  <script>
    // 创建 Vue 实例对象
    var app = new Vue({
      // 指定当前 app 实例要控制页面的哪个区域,接收的值是一个选择器
      // el 属性是固定的写法
      el:'#app',
      // 指定 Model 数据源,data对象就是要渲染到页面上的数据
      data:{
        message:'Hello Vue!'
      }
    })
  script>

基本代码与 MVVM 的对应关系

Vue2-Vue3.0学习笔记(2021年黑马程序员最新视频)_第1张图片

插件库

day.js 快速日期格式化

解决格式化Vue文件时 逗号、分号问题 :

Snipaste_2021-11-01_21-54-30

在根目录中,添加一个 .prettierrc.json 配置文件,写入:

{
	"singleQuote": true,  
    "semi": false,
    "trailingComma": "none"
}

一劳永逸方法:

  1. 在电脑上的用户目录下新建一个 .prettierrc.json 文件,同理上面的参数。

  2. 在 **Vs code **的 setting.json 中添加配置路径:

     
    	// 设置 配置文件,解决格式化vue文件时逗号、分号爆红问题
    	// 这里注意配置路径,记得使自己计算机上的用户名
      "prettier.configPath": "C:/Users/mi/.prettierrc.json",
          
    

解决格式化时函数括号前的空格问题 :

  • Prettier 格式化插件无法处理函数括号前添加空格问题

解决办法一、在当前项目里的 .eslintrc.js 里 rules 规则里添加忽略加空格爆红提示 :

'space-before-function-paren':['error','never']

解决办法二: 使用 Prettier now 插件可以解决此问题。

vue 指令

1. 内容渲染指令

  1. v-text 设置标签的内容, 默认写法会 覆盖元素内部原有的内容 , 内部支持写表达式。
  2. {{ }} 插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!
  3. v-html 指令的作用:可以把带有标签的字符串,渲染成真正的 HTML 内容!

注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!

2. v-bind 属性绑定属性

  • 在 vue 中,可以使用 v-bind: 指令,为元素的属性动态绑定值;
  • 简写是英文的 :
  1. 在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:

    这是一个 div
  2. 使用 v-bind 在元素绑定时希望内传入 Number数值时,避免被解析成字符串

    
    

3. v-on 事件绑定属性

  1. v-on: 简写是 @

  2. 语法格式为:

    count的值是:{{ count }}

    methods: { add() { // 如果在方法中要修改 data 中的数据,可以通过 this 访问到 this.count += 1 } }

$event

在事件绑定时,会有一个原生DOM 的事件对象 e,如果事件对象传入参数,默认的事件对象会被覆盖。

$event 的应用场景:如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event。

例如:

// 点击按钮让count值 递增,绑定点击事件并传参



methods: {
   add(n, e) {
			// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
			this.count += 1
   }
}

事件修饰符:

事件绑定期间非常好玩的一个东西,对事件的触发进行控制。

  • .prevent

    <a @click.prevent="xxx">链接a>
    
  • .stop

    <button @click.stop="xxx">按钮button>
    
事件修饰符 说明
.prevent 阻止默认行为 (如:阻止a链接的跳转,阻止表单提交等)
.stop 阻止冒泡事件
.capture 以捕获模式触发当前的事件处理函数
.once 绑定的事件只触发一次
.self 只有在event.target 是当前元素自身时触发事件处理函数

按键修饰符

按键 键码值 使用
Enter 13 .enter
Tab 9 .tab
Delete 46 .delete (捕获“删除”和“退格”按键)
Esc 27 .esc
BackSpace 8 .space
Up Arrow 38 .up
Left Arrow 37 .left
Right Arrow 39 .right
Dw Arrow 40 .down

1.自定义其他的按键别名:


Vue.config.keyCodes.f6 = 118

<input @keyup.f6="xxx" />  // 只有单击f6键才会触发xxx的回调
   

2.多个按键一并触发该事件

@keyup.ctrl.enter="XXX"   //  按下ctrl和enter才触发事件执行

【案例应用】:表单输入后按回车键添,按esc则清空表单


4. v-model 表单绑定

在不操作 DOM 的前提下,实现表单元素和数据的双向绑定。

v-model 指令的修饰符

修饰符 作用 示例
.number 自动将用户输入值转为 Number number=“age” />
.trim 去除首尾空白字符 trim=“msg” />
.lazy 表单输入后失去焦点时更新页面数据,而非实时更新 lazy=“msg” />
  1. input 输入框

    • type=“text”
    • type=“radio”
    • type=“checkbox”
    • type=“xxxx”
  2. textarea

  3. select

5. 条件渲染指令

条件渲染指令用来辅助开发者按需控制 DOM 的显示与隐藏。

v-if 和 v-show

  1. v-show 的原理是:动态为元素添加或移除style= " display: none; " 样式,从而控制元素的显示与隐藏。

    • 如果要频繁的切换元素的显示状态,用 v-show 性能会更好。
  2. v-if 的原理是:每次动态创建或移除 DOM 元素,实现元素的显示和隐藏。

    • 如果在运行时条件很少改变,则使用 v-if 较好。

v-if 指令在使用的时候,有两种方式:

  1. 直接给定一个布尔值 true 或 false

    <p v-if="true">被 v-if 控制的元素p>
    
  2. 给 v-if 提供一个判断条件,根据判断的结果是 true 或 false,来控制元素的显示和隐藏

    <p v-if="type === 'A'">良好p>
    

v-else-if

优秀
良好
及格
不及格

6. 列表渲染指令

基于一个数组来循环渲染一个列表结构。

v-for

v-for 指令需要使用 item in items 形式的特殊语法

  • items 是源数据 (待循环) 的数组,而
  • item 被循环的每一项。

【示例】:动态渲染表单数据:

 <link rel="stylesheet" href="./lib/bootstrap.css">

  
  <div id="app">
    <table class="table table-bordered table-hover table-striped">
      <thead>
        <th>索引th>
        <th>Idth>
        <th>姓名th>
      thead>
      <tbody>
        
        
        
        
        <tr v-for="(item, index) in list" :key="item.id">
          <td>{{ index }}td>
          <td>{{ item.id }}td>
          <td>{{ item.name }}td>
        tr>
      tbody>
    table>
  div>

使用 key 维护列表的状态

官方推荐, 使用 v-for 指令绑定一个:key 属性,key的作用是为了高效的更新虚拟DOM。

  • key 的值类型,是有要求的:字符串或数字类型

  • 把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性)

  • 改变 data 顺序,index 会重新排序,所以 index 的值不具有唯一性。

计算属性 computed

实时监听 data 中数据的变化,并 return 一个计算后的新值, 供组件渲染 DOM 时使用。

可以被模板结构 (插值、v-bind ) 或 methods 方法使用。

  • 但是在某些情况下,我们可能需要对数据进行一些转化后在显示,或者需要将多个数据结合起来进行显示,这时候我们可以使用计算属性。

实例 1:

<div id="app">
  <h2>{{getFullName()}}h2>
  <h2>{{fullName}}h2>
div>
  const vm = new Vue({
    el: '#app',
    data: {
      firstName: 'lin',
      lastName: 'willen'
    },
    computed: {
      fullName () {
        return this.firstName + ' ' + this.lastName;
      }
    },
     // 使用 methods: 每次都会调用方法
    methods: {
      getFullName () {
        return this.firstName + ' ' + this.lastName;
      }
    }
  })

特点:

  1. 定义的时候,要被定义为 “方法”
  2. 在使用计算属性的时候,当普通的属性使用即可
  3. 实现了代码的复用,只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值。

实例2:


<div id="app">
  <h2>总价格:{{totalPrice}}h2>
div>
const vm = new Vue({
    el: '#app',
    data: {
      books:[
        {id: 1001, name: 'Unix编程艺术',price: 119},
        {id: 1002, name: '代码大全',price: 105},
        {id: 1003, name: '深入理解计算机原理',price: 99},
        {id: 1004, name: '现代操作系统',price: 109}
      ]
    },
    computed: {
      totalPrice () {
        
        let totalPrice = 0;
        
        for (let i in this.books) {
          totalPrice += this.books[i].price;
        }
          
        // 也可以使用 for of 
        for (let book of this.books) {
          totalPrice += book.price;
        }
        return totalPrice;
      }
    }
  })

计算属性 vs 方法

methodscomputed看起来都可以实现我们的功能,那么为什么还要多一个计算属性 ?

  • methods: 每次使用都会调用方法
  • computed: 计算属性会缓存计算的结果, 不变的情况下只调用一次, 除非原属性发生改变,才会重新调用.

计算属性 vs 侦听器

侧重的应用场景不同侧重的应用场景不同

  • 计算属性侧重于监听多个值的变化,最终计算并返回一个新值
  • 侦听器侧重于监听单个数据的变化,最终执行特定的业务处理不需要有任何返回值

vue组件

什么是组件化开发 ?

根据封装的思想,把页面上可重用的 UI 结构封装为组件,方便项目的开发和维护。

vue 中的组件化开发

  • vue 是一个支持组件化开发的前端框架。
  • vue 中组件的后缀名是.vue

组件的构成

每个 .vue 组件都由 3 部分构成,分别是:

  1. template :组件的模板结构,且每个组件中必须包含template模板结构。
  2. script : 组件的 JavaScript 行为
  3. style :组件的样式

<template>
<!-- 当前组件的 DOM 结构,需要定义到 template 标签内部 -->
</template>

<script>
 // 组件相关的 data数据、methods方法等,
 // 都要定义到 export default 所导出的对象中
export default {}
</script>

// 标签上添加 lang="less" 属性,即可使用 less 语法编写组件的样式
// scoped 防止样式冲突
<style lang='less' scoped>
</style>

script 节点:

(1)script 中的 name 节点

  • 用来定义组件的名称,调试的时候可以清晰的区分每个组件

(2)script 中的 data 节点

  • 组件渲染期间需要用到的数据, data 必须是函数, 不能直接指向对象数据。

(3)script 中的 methods 节点

  • 组件中的事件处理函数(方法),必须定义到 methods 节点中

注册私有组件

通过 components 注册的是私有子组件,被注册的组件只能用在当前组件中。

1.使用 import 语法导入需要的组件

import Left from '@/components/Left.vue'

2.在 script 标签中使用 components 节点注册组件


3.以标签的形式使用刚才注册的组件


注册全局组件

在 vue 项目的 main.js 入口文件中,通过 Vue.component()方法,可以注册全局组件

注册:

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

// 导入需要被全局注册的那个组件
import Count from '@/components/Count.vue'

// 参数一: 组件的 '注册名称',将来以标签形式使用时要求和这个名称一样。
// 参数二: 需要被全局注册的那个组件。
Vue.component('MyCount', Count)

// 消息提示的环境配置,设置为开发环境或者生产环境
Vue.config.productionTip = false

new Vue({
  // render 函数中,渲染的是哪个 .vue 组件,那么这个组件就叫做 “根组件”
  render: h => h(App)
}).$mount('#app')

使用:

<template>
    // 这里需要注意 '注册名称'
    <MyCount ></MyCount>
</template>

组件注册时名称的大小写

在 Vue 定义组件注册名称的方式有两种:

  1. 短横线命名法,例如 my-swipermy-search
    • 使用组件时也必须使用短横线命名
  2. 大驼峰命名法,例如 MySwiperMySearch
    • 既可以按照大驼峰命名法使用,也可以转化为短横线名称进行使用。

注意: 在开发中,推荐使用大驼峰命名法为组件注册名称,因为它的适用性更强。

组件之间的样式冲突问题

默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。

scoped 属性

让当前组件的样式对其子组件是不生效。

Vue 中提供了在style 节点添加 scoped属性,来防止样式冲突问题:

  • 原理是为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域。

QQ截图20211030133220

<template>
    <div class="container" data-v-001>
        <h3 data-v-001 > 轮播图组件件</h3>
	</div>
</template>

<style>
  // 通过中括号'属性选择器',防止样式冲突问题
  // 因为每个组件分配的自定义属性是'唯一的'
 . container[data-v-001]{
      border: 1px solid red;
    }
</style>

为了提高开发效率和开发体验,直接在 style 节点使用 scoped 属性:

<style lang="less" scoped>
</style>

/deep/ 样式穿透

让某些样 式对子组件生效。

使用场景: 当使用第三方组件库的时候,需要修改第三方组件默认样式的时候。

<style lang="less" scoped>
    
/*不加 /deep/ 时,生成的选择器格式为 .title[data-v-052242de]*/
.title{
    color: blue;
}

/*加/deep/ 时,生成的选择器格式为 [data-v-052242de] .title*/
/deep/ .title {
  color: pink;
}
</style>

Class 与 Style 绑定

通过 v-bind动态操作元素样式。

1. 动态绑定 HTML 的 class:

通过三元表达式,动态的为元素绑定 class 的类名:

MyDeep 组件

data(){
	return { isItalic:true }
.thin{
	font-weight:200;
.italic{
	font-style:italic;
}
    

2. 以数组语法绑定 HTML 的 class

如果元素需要动态绑定多个 class 的类名,此时可以使用数组的语法格式

  

MyDeep组件

3. 以对象语法绑定 HTML 的 class:

MyDeep组件

4. 以对象语法绑定内联的 style

命名可以用驼峰式短横线分隔 (记得用引号括起来) 来命名:

Hello world!!
data () {
    return {
      active: 'red',
      fsize: 30, 
      bgcolor: 'pink'
    }
}

自定义属性 props

props是组件的自定义属性,允许使用者通过自定义属性,为当前组件指定初始值,极大的提高组件的复用性。

在组件中声明 prpos

my-article 组件的定义如下:




父组件传递给my-article组件的数据,必须在props节点中声明 :

<script>
    export default {
		props:['title','author'],
</script>

无法使用未声明的 props

如果父组件给子组件传递了未声明的 props 属性,则这些属性会被忽略,无法被子组件使用。

动态绑定 props 的值

使用 v-bind 属性绑定的形式,可以为组件动态绑定 props 的值。

<!--通过V-bind属性绑定,为author动态赋予一个表达式的值
                                    


props 的大小写命名

组件中如果使用“camelCase (驼峰命名法)”声明了 props 属性的名称,则有两种方式为其绑定属性的值:

<script>
    export default {
		props:['pubTime'],  // 使用'驼峰命名'法为当前组件声明 pubTime 属性
</script>

使用时既可以用驼峰命名,亦可以用短横线分隔命名的形式为组件绑定属性的值 :


// 等价于

props 验证

  1. 基础的类型检查 type

    props:{
        
       //支持的8种基础类型
    
       propA:String,    //字符串类型
       propB:Number,    //数字类型
       propC:Boolean,   //布尔值类型
       propD:Array,     //数组类型
       propE:Object,    //对象类型
       propF:Date,      //日期类型
       propG:Function,  //函数类型
       propH:Symbol     //符号类型
    
      }
    
  2. 多个可能的类型

  3. 必填项校验 required

  4. 属性默认值 default

    
    props:{
        // 通过数组形式,为当前属性定义多个可能的类型
        type: [String, Number],
        required: true,
        default: 0,
    }
    
自定义验证函数

在封装组件时,可以为 prop 属性指定自定义的验证函数,从而对 prop 属性的值进行更加精确的控制:

 props:{
   type:{
       // 通过 validataor函数,对type 属性进行校验,属性值 通过val形参接收
     validataor(val){
       // 必须匹配下列字符串中的一个
       return ['success','warning','danger'].indexOf(val) !== -1
     }
   } 
}

prpos 传入不同的初始值

在实际开发中我们经常会碰到下面的情况:

  • 在不同组件使用同一个注册的组件时候希望赋值一个不同的初始值

1.组件的封装者通过 props 允许使用者自定义初始值:

<template>
  <div>
    <h5>Count是全局组件,将被Left 和 Right组件使用</h5>
    <p>count 的值是:{{ init }}</p>
    <button @click="count += 1">+1</button>
  </div>
</template>

<script>
export default {
  // props: ['init'],
  props: {
    // 自定义属性的名字,是封装者自定义的(只要名称合法即可)
    init: {
      // 如果外界使用 Count 组件的时候,没有传递 init 属性,则默认值生效
      default: 0,
      // 指定值类型必须是 Number 数字
      type: Number,
      // 必填项校验(表示必须传入值)
      required: true
    }
  },

  data() {
    return {
        //props是只读的且不可修改
       // 要想修改 props 的值,可以把 props 的值转存到 data中
      count: this.init
    }
  }
}

2.组件的使用者通过属性节点传入初始值

Left 组件、Right 组件:

<template> 
  <div class="left-container">
    <h3>Left 组件</h3>
    <hr />
    <MyCount :init="9"></MyCount>
  </div>
</template>
<template>
  <div class="right-container">
    <h3>Right 组件</h3>
    <hr />
        
    <MyCount :init="6"></MyCount>
  </div>
</template>

props 里传参注意点

  • props 可以通过[]给数据定义多个可能的数据类型 ;
  • props 传入 Object 默认值必须是一个 fn
props: {
    commCount: {
      type: String,
      default: ''
    },
    pubdate: {
      // 通过数组形式,为当前属性定义多个可能的类型
      type: [String, Number],
      default: ''
    },
    cover: {
      type: Object,
      // 通过 default 函数,返回 cover 的默认值
      default: function() {
      // 这个 return 的对象就是 cover 属性的默认值
        return { cover: 0 }
      }
    }
  }

QQ截图20211030131333

  • props 是只读的,想修改 props 的值,可以把 props 的值转存到 data 中。
  • props 的三个属性值:default、type、required。

自定义事件

封装组件时,为了让组件的使用者可以监听到组件内状态的变化,此时需要用到组件的自定义事件

vue2 中自定义事件的 3 个使用步骤:

  • 在封装组件时 (子组件):触发自定义事件

  • 在使用组件(父组件)时:监听自定义事件

触发自定义事件

// 子组件




监听自定义事件


// 父组件
<Son @numchange="getNewCount"></Son>

methods : {
	getNewCount(val) {
	console.log('监听到了 count 值的变化', val)
}

自定义事件传参

在调用 this.$emit() 方法触发自定义事件时,可以通过第 2 个参数为自定义事件传参:

methods: {
    onBtnClick() {
        this.count t= 1
        this.$emit( 'numchange' , this.count)} // 触发自定义事件,通过第二个参数传参
}

filter 过滤器

在 vue 3.x 的版本中剔除了过滤器相关的功能。

在 vue 3.x 使用计算属性或方法代替被剔除的过滤器功能。

过滤器(Filters)常用于文本的格式化。过滤器可以用在两个地方:双括号插值表达式v-bind属性绑定。


{{ message | capitalize }}


私有过滤器

在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。

创建一个私有过滤器,示例代码如下:

const vm = new Vue({
    el: '#app',
    data: {
        message: 'hello world!',
        info: 'title info'
    },
    // 在 filters 节点下定义过滤器
    filters: {
        // 把首字母转换为大写的过滤器
        capitalize (value) {  
            return value.charAt(0).toUpperCase() + value.slice(1)
        }
    }

全局过滤器

如果希望在多个 vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器:

// Vue.filters()方法接收两个参数:
// 第一个参数:过滤器名字   第二个参数:过滤器的处理函数

Vue.filter('capitalize',(str)=>{
    return value.charAt(0).toUpperCase() + value.slice(1)'
} )

调用多个过滤器:

{{ message | filterA | filterB }}
  • 先把 message 的值交给 filterA 处理,再把 filterA 处理结果交给 filterB 进行处理,最终把 filterB 的处理结果,作为最终值渲染到页面上。

过滤器传参

过滤器是 JavaScript 函数,因此可以接收参数:

{{ message | filterA('arg1', arg2) }}

// 第一个参数永远是 '管道符' 前面待处理的值,第二个参数开始才是调用过滤器时传递的参数

Vue.filter('filterA',(msg,arg1,arg2)=>{
	// 过滤器的逻辑代码
})

这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。

<p>{{message | cap | maxl(5)}}</p>


Vue.filter('cap', (str) => {
    return str.charAt(0).toUpperCase() + str.slice(1) + '----'
})

Vue.filter('maxl', (str, len = 10) => {
    if (str.length <= len) return str
    return str.slice(0, len) + '....'
})
var app = new Vue({
    el: '#app',
    data: {
        message: 'hello Vue 2021年10月30日00:07:45!'
    }

过滤器的注意点

  1. 在过滤器函数中,一定要有 return 值
  2. 在过滤器的形参中,可以获取到“管道符”前面待处理的那个值
  3. 如果全局过滤器和私有过滤器名字一致,此时按照“就近原则”,调用的是”私有过滤器“

label的for属性

使用lable自带的属性进行单选钮的启用和禁用:

<input type="checkbox" :id="'cb' + item.id" v-model="item.status">
<label  :for="'cb' + item.id" v-if="item.status">已启用label>
<label  :for="'cb' + item.id" v-else>已禁用label>

watch 侦听器

watch 侦听器 监视数据的变化,从而针对数据的变化做特定的操作

侦听器的格式

  1. 方法格式的侦听器
    • 无法在刚进入页面的时候,自动触发!!
    • 如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器!
  2. 对象格式的侦听器
    • 可以通过 immediate 选项,让侦听器自动触发!
    • 可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!

使用方法格式创建的侦听器:

监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:

import axios from 'axios'

export default{
    data(){
        return{ username: ''}
    }
},
watch: { 
    // newVal 是'变化后的新值',oldVal 是'变化之前的旧值'
    async username(newVal,oldVal) {  
        if (newVal === '') return 
        // 使用 axios 发起请求,判断用户名是否可用 
        const { data: res } = await axios.get(`https://www.escook.cn/api/finduser/${newVal}` ) 
        console.log(res) 
    } 
}

使用方法创建时:组件在初次加载完毕后不会调用 watch 侦听器。

immediate 选项

如果想让 watch 侦听器在浏览器打开时立即被调用,则需要使 用 immediate 选项。

使用对象格式创建的侦听器

watch: {
    username: {
        // handler 是固定写法,表示当 username 的值变化时,自动调用 handler 处理函数
        async handler(newVal,oldVal) {
            if (newVal === '') return
            const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)
            console.log(res)
        },
            // 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器
            immediate: true
    }
}
  1. 使用 handler 定义侦听器函数
  2. immediate控制侦听器是否自动触发, 默认值为 false不立即触发。

deep 选项

如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 开启 深度监听

<input type="text" v-model.trim="username"/>
data: {
   info: {username: 'admin'}
},
watch:{
    // 监听info对象的变化
	info:{
      	handler(newVal){
            console.log(newVal.username)
		},
	// 开启深度监听,监听每个属性值的变化,默认值为 false
	deep: true
	}
}

监听对象单个属性的变化

如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器。

const vm = new Vue({
    el: '#app',
    data: {
        info: {username: 'admin',}
    },
    watch:{
        'info.username':{
            handler(newVal){
                console.log(newVal)
            },
        }
    }
})

组件的生命周期

  • 生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。
  • 生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。

生命周期强调的是时间段,生命周期函数强调的是时间点。

生命周期函数的分类

生命周期图示:

lifecycle

需要注意的三个周期函数:

生命周期函数

在实际开发中,created 是最常用的生命周期函数 !

组件之间的数据共享

组件之间的数据共享

父→子

父组件通过 v-bind 属性绑定向子组件共享数据,子组件使用 props接收数据。

// 父组件
// 注册子组件
<Son :msg="message" :user="userinfo"></Son>

<script>
    // 导入子组件
    import Son from '@component/Son.vue'

export default {
    data:{
        return{
        message:'hello vue.js'
        userinfo:{
        name: 'lilei',age: 21
    },
    components: {
        Son
    		}
		}
	}
}
</script>
// 子组件
<template>
    <div>
    	<h5>Son组件</h5>
		<p>父组件传递过来的 msg值是: {{ msg }</p>
    	<p>父组件传递过来的user值是: {{ user }}</p>
    </div>
</template>

export default {
	props: [ 'msg' , 'user ']
}

子 → 父

子组件向父组件共享数据使用自定义事件。

// 子组件

export default {
    data() {
        // 子组件自己的数据,将来希望把 count 值传给父组件
        return { count: 0 }
    },
    methods: {
        add() {
            this.count t= 1
            //修改数据时,通过 $emit()触发自定义事件
            this.$emit( 'numchange' , this.count)}
    }
}
// 父组件
<h1>App 根组件</h1>
<h3>子组件传过来的数据是 : {{ countFromSon }}</h3>
<Son @numchange="getNewCount"> </Son>

<script>
import Son from '@component/Son.vue'

export default {
    data() {
        return { countFromSon: 0 }
    },
    methods : {
        getNewCount(val) {
            console.log('numchange 事件被触发了!', val)
            this.countFromSon = val
        },
        components: {
        Son
    }
}
}
    </script>

兄弟组件数据共享

在 vue2.x 中,兄弟组件之间数据共享的方案是 EventBus

QQ截图20211030151214

EventBus 的使用步骤

  1. 创建 eventBus.js模块,并向外共享一个 Vue 的实例对象
  2. 在数据发送方,调用 bus.$emit('事件名称', 要发送的数据)方法触发自定义事件 。
  3. 在数据接收方,调用 bus.$on('事件名称', 事件处理函数)方法注册一个自定义事件。

ref 引用 操作DOM

不依赖于 jQuery 和调用 DOM API 的情况下,获取 DOM 元素组件的引用

每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象。

使用 ref 引用 DOM 元素

<--使用ref属性,为对应的DOM添加引用名称-->
    
<h3 ref="myh3">MyRef 组件</h3>
<button@click="getRef">获取$refs 引用</button>


methods:{
    getRef(){
    	//通过this.$refs.引用的名称可以获取到DOM元素的引用
        console.log(this.$refs.myh3)
    	//操作DOM元素,把文本颜色改为红色
    	this.$refs.myh3.style.color='red'
}

使用 ref 引用组件实例

需求: 在根组件控制子组件

<--使用ref属性,为对应的“组件”添加引用名称-->
    
<my-counter ref="counterRef"> </my-counter>
<button @click="getRef"> 获取$refs 引用 </button>

methods:{
    getRef(){
    	// 通过this.$refs.引用的名称可以引用组件的实例
        console.log(this.$refs.counterRef)
    	// 引用到组件的实例之后,就可以调用 子组件上的 methods 方法
        this.$refs.counterRef.add()
}

点击文本框自动获得焦点

添加 ref 引用,并调用原生 DOM 对象的.focus() 方法即可。

this.$nextTick(cb) 方法

$nextTick(cb)保证 cb 回调函数可以操作到最新的 DOM 元素(推迟到下一个 DOM 更新周期之后执行)。

  • 解决我们在页面没有渲染完成前使用 ref 操作DOM元素报错问题。

控制文本框和按钮的按需切换

点击按钮展示文本框,文本框输入时隐藏按钮:

<template>
    <input type="text" v-if="inputVisible" ref="ipt">
    <button v-else @click="showInput">展示input输入框</button>
</template>
<script>
export default{
	data(){
		return{
		//控制文本框和按钮的按需切换
		inputVisible:false},
methods:{
    showInput(){
        //切换布尔值,显示文本框
        this.inputVisible=true
        
        //获取文本框的DOM引用,并调用.focus()使其自动获得焦点
        
        // this.$refs.ipt.focus() 错误,此时页面未渲染完毕,无法获取文本框
        
        //把对input文本框的操作,推迟到下次DOM更新之后。否则页面上根本不存在文本框元素
        this.$nextTick(() =>{
				this.$refs.ipt.focus()
		}}
}</script>

动态组件

实现不同组件之间的按需展示。( 动态切换组件显示和隐藏 ) ,类似于 Vue-Router

动态组件的基本使用

Vue 提供了一个内置的 组件,专门用来实现动态组件的渲染 :

  1. component 标签是 vue 内置的,作用:组件的占位符

  2. 通过 :is 属性,动态指定要渲染的组件

    • is 属性的值,表示要渲染的组件的名字。

    • is 属性的值,应该是组件在 components 节点下的注册名称。

  3. 使用 keep-alive保持组件的状态 (避免组件切换时重新渲染)。

    • keep-alive 会把内部的组件进行缓存,而不是销毁组件。
  4. 通过 include 指定哪些组件需要被缓存。

  5. 通过 exclude属性指定哪些组件不需要被缓存。

// 点击按钮,动态切换组件的名称




在组件中定义 name 名称:

  • 当提供了 name 属性之后,组件的名称就是 name 属性的值。
export default {
  name: 'MyRight'
}

声明 name 应用场景:结合标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称。


插槽 slot

在签形式使用的组件中内容节点插入内容

  • 通过 slot 元素 定义插槽,从而为用户预留内容占位符。
  • 封装组件时,没有预留插槽的内容会被丢弃。

后备内容

slot 标签内添加的内容会被作为后备内容。

// 子组件 my-com

// 使用插槽
	<my-com>
       // 如果用户没有提供内容,上面 slot标签 内定义的内容会生效,此时页面会有 "这是后备内容"
       // 如过提供了,下面的 img 将会被渲染到页面上
        <img src="../assets/log.png" alt="">
	</my-com>

具名插槽

// MyArticle 组件

  • Vue 官方规定,每个 slot插槽,都要有一个 name名称,一个不带 name 出口会带有隐含的名字“default”。

为具名插槽提供内容

如果要把内容填充到指定名称的插槽中,我们可以在一个