Skip to content

侦听器

1、什么是 watch 侦听器

watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。

侦听器的格式:

1、方法格式的侦听器

​ ​ 缺点1:无法在刚进入页面的时候,自动触发默认值 ​ ​ 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器

2、对象格式的侦听器

好处1:可以通过immediate选项,让侦听器自动触发! ​ ​ 好处2:可以通过deep选项,让侦听器深度监听对象中每个属性的变化! ​ ​ 方法格式的侦听器语法格式如下:

html
<div id="app">
     <input type="text" v-model="username">
</div>
<script src="js/v2.6.10/vue.js"></script>
<script>
     const vm = new Vue({
          el:'#app',
          data:{
               username:''
          },//所有的侦听器,都应该定义到 watch 节点下
          watch:{
               //侦听器本质上是一个函数,要监听那个数据的变化,就把数据名作为方法名即可,规定
               //第一个形参永远都是:newVal 是"变化后的新值",第二个形参永远都是:oldVal 是"变化之前的值"
               //newVal和oldVal名字可以变
               username(newVal,oldVal){
                    console.log("新值:"+newVal+"旧值:"+oldVal);
               }
          }
     })
</script>

​ ​

2、对象格式的侦听器:

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

html
<div id="app">
	请输入用户名:<input type="text" v-model="userName">
</div>
<script src="js/v2.6.10/vue.js"></script>
<script src="js/jquery-3.6.1.js"></script>
<script>
     const vm = new Vue({
          el:'#app',
          data:{
               userName:'admin'
          },
          watch:{
               //定义对象格式的侦听器
               userName:{
                    //侦听器的处理函数
                    //只要监视到了userName值的变化,会自动触发handler函数
                    handler(newVal,oldVal){
                         if(newVal === '') return //如果 newVal 等于 null return出去
                         //1、调用jQuery中的 Ajax 发起请求,判断 newVal 是否被占用
                    $.get('https://www.escook.cn/api/finduser/'+newVal,result=>{
                 	//把newVal,发送给服务器
                      console.log(result)
                 })
            },
            //immediate里面的true表示一进入页面就会触发handler函数,
            //false表示一进页面就不会触发handler函数
            //immediate默认值是false
            immediate:true 
       }
  	}
   })
</script>

3、深度侦听:

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

html
<div id="app">
   请输入用户名:<input type="text" v-model="info.userName" />
</div>
<script src="js/v2.6.10/vue.js"></script>
<script src="js/jquery-3.6.1.js"></script>
<script>
     var vm = new Vue({
          el:'#app',
          data:{
               //用户的信息对象
               info:{
                   userName:'admin'
               }
          },
          watch:{
               //侦听的是 data 里面的 info 对象
               info:{ 
                    handler(newVal){
                         console.log(newVal);
                    },
                    //开启深度监听,只要对象中任何一个属性发生变化了,都会触发对象侦听器
                    //deep属性,默认未开启,需要手动开启,true开启,false未开启
                    deep:true
               }
               //如果要侦听的是对象的子属性的变化:则必须包裹一层单引号
               // 示例:
               'info.userName'(newVal,oldVal){
                    console.log(newVal,oldVal);
               }
          }
     })
</script>

4、jQuery中的Ajax判断用户名是否被占用:

html
<div id="app">
     请输入用户名:<input type="text" v-model="userName">
</div>
<script src="js/v2.6.10/vue.js"></script>
<script src="js/jquery-3.6.1.js"></script>
<script>
     const vm = new Vue({
          el:'#app',
          data:{
               userName:''
          },
          watch:{
               userName(newVal){
                    if(newVal === '') return //如果 newVal 等于 null return出去
                    //1、调用jQuery中的 Ajax 发起请求,判断 newVal 是否被占用
                    $.get('https://www.escook.cn/api/finduser/'+newVal,result=>{//把newVal,发送给服务器
                         console.log(result)
                    })						
               }
          }
     })
</script>

计算属性:

1. 什么是计算属性:

计算属性指的是通过一系列运算之后,最终得到一个属性值。 ​ ​ 这个动态计算出来的属性值可以被模板结构或 methods 方法使用。示例代码如下:

html
<!DOCTYPE html>
<html>
     <head>
          <meta charset="utf-8">
          <title></title>
          <style>
               *{
                    margin: 0px;
                    padding: 0px;
               }
               #box{
                    width: 200px;
                    height: 200px;
                    background-color: #000000;
               }
          </style>
     </head>
     <body>
          <div id="app">
               R:<input type="text" v-model.number="r"><br>
               G:<input type="text" v-model.number="g"><br>
               B:<input type="text" v-model.number="b">
               <!-- 差值表达式都是js代码 -->
               <!-- :style 代表动态绑定一个样式对象 ,它的值是{ }对象 -->
               <!-- 当前的样式对象,只包含 backgroundColor 背景颜色 ,里面的值是动态的 -->
               <div id="box" :style="{ backgroundColor:`rgb( ${r} , ${g} , ${b} )` }">
                    {{ `rgb( ${r} , ${g} , ${b} )` }}
               </div>
               <button @click="showRgb">按钮</button>
               <p>computed属性:{{ show }}</p>
          </div>
          <script src="js/v2.6.10/vue.js"></script>
          <script>
               const vm = new Vue({
                    el:'#app',
                    data:{
                         //红
                         r:0,
                         //绿
                         g:0,
                         //蓝
                         b:0
                    },
                    methods:{
                         showRgb(){
                              console.log(`rgb( ${this.r} , ${this.g} , ${this.b} )`)
                         }
                    },
                    computed:{
                         show(){
                              return `rgb( ${this.r} , ${this.g} , ${this.b} )`
                         }
                    }
               })
          </script>
     </body>
</html>

2.计算属性的特点

① 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性 ​ ​ ② 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算

特点:

1、定义的时候,要被定义为"方法"

2、在使用计算属性的时候,当普通的属性使用即可

好处:
1、实现了代码的复用

2、只要计算属性中依赖的数据变化了,则计算属性会自动重新求值

axios(Web数据交互方式)

介绍:

axios是一个专注于网络请求的库!

Axios,是一个基于promise 的网络请求库,作用于node.js和浏览器中,它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生node.js 模块, 而在客户端 (浏览端) 则使用XMLHttpRequest。

axios的基本使用

1、使用GET方式发起请求
javascript
axios({
     method:'GET',	//请求方式
     url:'http:www.liulongbin.top:3006/api/getbooks',//请求地址
     //url(GET) 中的查询参数
     params:{
          id:1
     },

     //请求体参数 (POST)
     data:{}
}).then(function(result){
     console.log(result)
})

2、使用POST方式发起请求
html
<body>
     <button id="btnPost">发起POST请求</button>
     <script src="js/axios.js"></script>
     <script>
          document.querySelector('#btnPost').addEventListener('click',async function(){
               //如果调用某个方法的返回值是 Promise 实例,则前面可以添加个 await!
               //await 只能用在被 async "修饰"的方法中
               const result = await axios({
                    method:'POST',
                    url:'http://www.liulongbin.top:3006/api/post',
                    data:{
                         name:'zs',
                         age:20
                    }
               })
               // .then(function(result){
               // 	console.log(result)
               // })
               console.log(result)
          })
     </script>
</body>
axios使用解构赋值
html
<body>
     <button id="btnPost">发起POST请求</button>
     <button id="btnGet">发起GET请求</button>
     <script src="js/axios.js"></script>
     <script>
          document.querySelector('#btnPost').addEventListener('click',async function(){
          //如果调用某个方法的返回值是 Promise 实例,则前面可以添加个 await!
          // 调用 axios 返回的就是 Prmise 使用 ,则可以使用 await
          //await 只能用在被 async "修饰"的方法中

          //使用解构赋值,把axios里面的data数据拿过来
               const { data } = await axios({
                    method:'POST',
                    url:'http://www.liulongbin.top:3006/api/post',
                    data:{
                         name:'zs',
                         age:20
                    }
               })
               console.log(data)
          })
          document.querySelector('#btnGet').addEventListener('click',async function(){
          //解构赋值的时候,使用 :进行重命名
          //1、调用 axios 之后,使用 async/await 进行简化
          //2、使用解构赋值,从axios封装的大对象中 data 属性解构出来
          //3、把解构出来的 data 属性,使用冒号进行重命名,一般重命名为 { data : result }
               const { data : result } = await axios({
                    method:'GET',
                    url:'http://www.liulongbin.top:3006/api/getbooks',
                    params:{
                         id:1
                    }
               })
               console.log(result.data)
          })
     </script>
</body>
axios.get 和 axios.post 请求方式
html
<body>
     <button id="btnGet">GET请求</button>
     <button id="btnPost">POST请求</button>
     <script src="js/axios.js"></script>
     <script>
          document.querySelector('#btnGet').addEventListener('click',async function(){
          // axios.get('url地址',{
          // 	//get参数
          // 	params:{}
          // })
               const { data : result } =await axios.get('http://www.liulongbin.top:3006/api/getbooks',{
                    params:{
                         id:1
                    }
               })
               console.log(result)
          })
          document.querySelector('#btnPost').addEventListener('click',async function(){
          // axios.post('url',{
          // 	//这是post请求体数据
          // })
               const { data : result } = await axios.post('http://www.liulongbin.top:3006/api/post',{
                    name:'zs',
                    gender:'女'
               })
               console.log(result)
          })
     </script>
</body>

vue-cli

1.什么是单页面应用程序:

单页面应用程序(英文名:Single Page Application)简称 SPA,顾名思义,指的是一个 Web 网站中只有唯一的一个 HTML 页面,所有的功能与交互都在这唯一的一个页面内完成。

2.什么是 vue-cli:

vue-cli 是 Vue.js 开发的标准工具。它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程。 ​ ​ 引用自 vue-cli 官网上的一句话: ​ ​ 程序员可以专注在撰写应用上,而不必花好几天去纠结 webpack 配置的问题。 ​ ​ 中文官网:https://cli.vuejs.org/zh/

3.安装和使用:

vue-cli 是 npm 上的一个全局包,使用 npm install 命令,即可方便的把它安装到自己的电脑上: ​ ​ npm install -g @vue/cli 简写 npm i -g @vue/cli -g:全局安装 ​ ​ 基于 vue-cli 快速生成工程化的 Vue 项目: ​ ​ vue create 项目的名称 ​ ​ 检查 vue-cli 版本号: vue -V

安装 :

1、选择自定义选项,按回车进入下一项

2.选择Babel和cssPre-processors,使用上下箭头调到Babel和cssPre-processors按空格选中,然后按回车进入下一项

3.选择2.0,2.0是vue2.0,然后按回车进入下一项

4.选择Less语法,按回车进入下一项

5.选择In dedicated config files ,让Babel和cssPre-processors放到独立配置文件,In package.json 是把下载的模块放到package.json文件里面,然后等待即可

4.vue 项目的运行流程

在工程化的项目中,vue 要做的事情很单纯:通过 main.js 把 App.vue 渲染到 index.html 的指定区域中。 ​ ​ 其中: ​ ​ ① App.vue 用来编写待渲染的模板结构② index.html 中需要预留一个 el 区域③ main.js 把 App.vue 渲染到了 index.html 所预留的区域中

Vue 组件

1.什么是组件化开发

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

2.vue 中的组件化开发

vue 是一个支持组件化开发的前端框架。 ​ ​ vue 中规定:组件的后缀名是 .vue。之前接触到的 App.vue 文件本质上就是一个 vue 的组件。

3.vue 组件的三个组成部分

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

 template -> 组件的模板结构 script -> 组件的 JavaScript 行为 style -> 组件的样式


其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。

3.1 template

vue 规定:每个组件对应的模板结构,需要定义到 template 标签节点中。

注意: ​ ​  template 是 vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素 ​ ​  template 中只能包含唯一的根节点

3.2 script

vue 规定:开发者可以在 script标签 节点中封装组件的 JavaScript 业务逻辑。 ​ ​ script 标签 节点的基本结构如下: ​

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

.vue 组件中的 data 必须是函数: ​ ​ vue 规定:.vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象。 ​ ​ 因此在组件中定义 data 数据节点时,下面的方式是错误的: ​ ​ data:{ // 组件中,不能直接让 data 指向一个数据对象 (会报错)count:0}

会导致多个组件实例共用同一份数据的问题,请参考官方给出的示例:

https://cn.vuejs.org/v2/guide/components.html#data-必须是一个函数

3.3 style

vue 规定:组件内的 style 标签节点是可选的,开发者可以在 style标签节点中编写样式美化当前组件的 UI 结构。 ​ ​ script标签节点的基本结构如下:

vue
<style>
     h1{
          font-weight: normal;
     }
</style>

让 style 中支持 less 语法: ​ ​ 在 style 标签上添加 lang="less" 属性,即可使用 less 语法编写组件的样式:

vue
<style lang="less">
     h1{
          font-weight: normal;
     }
</style>

4.组件之间的父子关系

组件在被封装好之后,彼此之间是相互独立的,不存在父子关系 在使用组件的时候,根据彼此的嵌套关系,形成了父子关系、兄弟关系

4.1 使用组件的三个步骤
步骤1:使用 import 语法导入需要的组件:
javascript
import Right from '@/components/Right.vue'

步骤2:使用 components 节点注册组件
javascript
export default {
  components: {
    Right
  }
}
步骤3:以标签形式使用刚才注册的组件
html
<div class="box">
	<Right></Right>
</div>
4.2 通过 components 注册的是私有子组件

例如:

在组件 A 的 components 节点下,注册了组件 F。 则组件 F 只能用在组件 A 中;不能被用在组件 C 中。

4.3 注册全局组件

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

示例代码如下:

javascript
//导入需要注册的全局组件
import Right from '@/components/Right.vue'

//参数1:字符串格式,表示组件的"注册名称"
//参数2:需要被全局注册的那个组件
Vue.component('Right',Right)

5.组件的 props

props 是组件的自定义属性,在封装通用组件的时候,合理地使用 props 可以极大的提高组件的复用性! ​ ​ 它的语法格式如下:

javascript
export default {
  //组件的自定义属性
  props:['init'],
  //组件的私有数据
  data(){
  	return{}
  }
}
5.1 props 是只读的

vue 规定:组件中封装的自定义属性是只读的,程序员不能直接修改 props 的值。否则会直接报错

要想修改 props 的值,可以把 props 的值转存到 data 中,因为 data 中的数据都是可读可写的!

javascript
export default{
     props:['init'],
     data() {
          return{
            count: this.init
          }
     }
}
5.2 props 的 default 默认值

在声明自定义属性时,可以通过 default 来定义属性的默认值。示例代码如下:

javascript
export default{
 props:{
     init:{
       //如果外界使用 Coun 组件的时候,没有传递 init属性,则默认值生效
       default:0
     }
  }
}
5.3 props 的 type 值类型

在声明自定义属性时,可以通过 type 来定义属性的值类型。

示例代码如下:

javascript
export default{
  props:{
     init:{
       //如果外界使用 Coun 组件的时候,没有传递 init属性,则默认值生效
       default:0,
       //init的值传递的不是 Number 类型的值,则终端报错
       type:Number
     }
  }
}
5.4 props 的 required 必填项

在声明自定义属性时,可以通过 required 选项,将属性设置为必填项,强制用户必须传递属性的值。

示例代 码如下:

javascript
export default{	 
 props:{
     init:{
       //如果外界使用 Coun 组件的时候,没有传递 init属性,则默认值生效
       default:0,
       //init的值传递的不是 Number 类型的值,则终端报错
       type:Number,
       //必填项校验,如果不填 init 的值则终端报错,如果有默认值的话,也会报错
       require:true
     }
  }
}

6.组件之间的样式冲突问题

默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。 ​ ​ 导致组件之间样式冲突的根本原因是: ​ ​ ① 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的 ​ ​ ② 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素

6.1 思考:如何解决组件样式冲突的问题

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

示例代码如下:

vue
<template>
     <div class="left-container">
          <h3 data-v-001>Left 组件</h3>
     </div>
</template>
<script>

export default{}

</script>
<style lang="less">
     .left-container{
          padding: 0 20px 20px;
          background-color: lightskyblue;
          min-height: 250px;
          flex: 1;
     }
     h3[data-v-001]{
          color: red;
     }
</style>
6.2 style 节点的 scoped 属性

为了提高开发效率和开发体验,vue 为 style 节点提供了 scoped 属性,从而防止组件之间的样式冲突问题:

vue
<template>
     <div class="left-container">
          <h3>Left 组件</h3>
     </div>
</template>
<script>

export default{}

</script>
<style lang="less" scoped>
     .left-container{
          padding: 0 20px 20px;
          background-color: lightskyblue;
          min-height: 250px;
          flex: 1;
     }
     h3{
          color: red;
     }
</style>
6.3 /deep/ 样式穿透

如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样式对子组件生效,可以使用 /deep/ 深度选择器

示例如下:

vue
<template>
     <div class="left-container">
          <h3>Left 组件</h3>
          <count :init="1"></count>
     </div>
</template>
<script>

export default{}

</script>
<style lang="less" scoped>
     .left-container{
          padding: 0 20px 20px;
          background-color: lightskyblue;
          min-height: 250px;
          flex: 1;
     }

     // 不加 /deep/: h5 [data-v-3c83f0b7]
     //加 /deep/ : [data-v-3c83f0b7] h5
     // /deep/ :会先找父元素的 data-v-3c83f0b7 然后再找标签
     //当使用第三方组件库的时候,如果有修改第三方默认样式的需求,需要用到 /deep/
     /deep/ h5{
          color: pink;
     }

</style>

组件的生命周期

1.生命周期 & 生命周期函数

生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。

生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。

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

2. 组件生命周期函数的分类

3.生命周期图示

生命周期分为三个阶段:

1.创建阶段:一个.vue文件只执行一次

1.beforeCreate

创建阶段的 props/data/methods 尚未被创建,都处于不可用的状态

javascript
beforeCreate() {
    console.log("初始化事件和生命周期")
}

2.created

created 生命周期函数,非常常用

经常在它里面,调用 methods 中的方法,请求服务器的数据

并且,把请求到的数据,转存到 data 中,供 template 模板渲染的时候使用!

javascript
created(){
   console.log("组件创建好阶段")
   console.log(this.info)
   console.log(this.message)
   this.show()
   console.log("ajax请求:")
   this.initBookList()
   console.log("----------------")
}
3.beforeMount

将要把内存中编译好的 HTML 结构渲染到浏览器中,此时浏览器中还没有当前组件的 DOM 结构

javascript
beforeMount(){
   console.log("beforeMount")
}

注意: beforeMount 操作不了 dom 元素

4.mounted

已经把内存中的 HTML 结构,成功的渲染到浏览器之中,此时浏览器已经包含了当前组件的 DOM 结构

如果要操作当前组件的 DOM 最早,只能在 mounted 阶段执行

javascript
mounted() {
   console.log("mounted")
}

2.运行阶段:运行阶段函数最少执行0次,如果数据没发生变化就不会执行,最多执行 N 次
1.beforeUpdate:

将要根据变化过后、最新的数据,重新渲染组件的模板结构

javascript
beforeUpdate(){
   console.log("beforeUpdate")
}
2.updated

根据最新的数据,重新渲染组件的 DOM 结构

当数据变化之后,为了能够操作到最新的 DOM 结构,必须把代码写到 updated 生命周期函数中

javascript
updated(){
   console.log("updated")
}
3.销毁阶段:一个.vue文件只执行一次
1.beforeDestroy

将要销毁此组件,此时尚未销毁,组件还处于正常工作的状态

javascript
beforeDestroy(){
   console.log("组件销毁之前")
   console.log(this.message)
}
2.destroyed

组件已经被销毁,此组件在浏览器中对应 DOM 结构已被完全移除

javascript
destroyed(){
   console.log("组件销毁了")
}

组件之间的数据共享

1. 组件之间的关系

在项目开发中,组件之间的最常见的关系分为如下两种:

① 父子关系

② 兄弟关系

2. 父子组件之间的数据共享

父子组件之间的数据共享又分为:

① 父 -> 子共享数据

② 子 -> 父共享数据

2.1 父组件向子组件共享数据

父组件向子组件共享数据需要使用自定义属性。示例代码如下:

2.2 子组件向父组件共享数据

子组件向父组件共享数据使用自定义事件。示例代码如下:

3.兄弟组件之间的数据共享

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

EventBus 的使用步骤

① 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象

② 在数据发送方,调用 bus.$emit('事件名称', 要发送的数据) 方法触发自定义事件

③ 在数据接收方,调用 bus.$on('事件名称', 事件处理函数) 方法注册一个自定义事件