必备-9.vue

本文深入探讨Vue.js框架,涵盖其渐进式特性、MVVM模式、响应式数据、组件、指令、过滤器、事件处理、过渡动画等关键概念,并介绍了如何启动项目、实现图片懒加载。Vue.js通过数据绑定简化DOM操作,提供丰富的API和指令系统,如v-if/v-for、v-bind/v-model等,支持组件化开发,能实现复杂的视图层构建和性能优化。
摘要由CSDN通过智能技术生成

必备-9.vue

vue是什么

  • 官方网站:https://cn.vuejs.org/
  • Vue是渐进式框架:vue.min.js只包含了vue最核心的内容【例如:options api/数据处理、template模板视图的解析等操作】;真实项目中我们还会根据需求导入
    • vuex:实现公共状态管理
    • vue-router:实现SPA(single page application)单页面应用
    • element-ui(饿了么)、antdv(蚂蚁金服)、iview()、vant()、cube…基于这些UI组件库快速创建项目
    • axios:实现数据通信
    • @vue/cli:基于vue的脚手架创建工程化项目

渐进式框架是什么?

  • vue是渐进式的,angular不是渐进式的
  • vue和angular的区别就好像
  • vue:就是手抓饭,你有手就能吃
  • angular:就是牛排,你想吃它,需要刀叉配套

vue的作用

Vue框架&&React框架的作用:

  • 让开发者放弃直接操作DOM,转为操作数据,当我们把数据处理"明白",框架本身会按照最新的数据帮助我们渲染出想要的视图[操作DOM的事情交给框架内部实现,而且做了性能优化]

vue的设计模式

**Vue是基于MVVM模式构建的框架:**它本身实现了viewModel层去监听数据/视图的变化,从而去渲染视图/修改数据,所以我们学习vue主要考虑两条主线:Model数据层View视图层

什么是MVVM设计模式?

  • MVVM:(Model-View-Viewmodel):数据-视图-视图模型
  • M(Model):数据层->后台,对于前段来说就是后端提供的一个API接口
  • V(View):视图层->浏览器
  • VM(Viewmodel):视图模型层-> 一个同步View和Model的对象
  • image-20211129230333001
  • ViewModel通过双向数据绑定把View层和Model层连接了起来,而View和Model之间的同步工作完全是自动的,无需人为干涉,因此开发者只需要关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM来统一管理。
    • 双向数据绑定:
      • m->v:数据修改时,会触发 视图的重新渲染
      • v->m:视图内容修改时,也会触发数据的重新修改

不同框架采用的设计模式?

  • vue:MVVM双向数据绑定->(在谷歌的中国人:尤雨溪)
  • angular(简称:ng):MVVM ->(谷歌)
  • react:MVC->(facebook)

数据层

如何使用vue

官方网站:https://cn.vuejs.org/

  • 第一步:使用vue需要在自己项目环境下安装vue包:yarn @vue/cli

  • 第二步:在项目页面html页面中引入对应的vue:<script src="js/vue.min.js"></script>

  • 第三步:Vue是一个构造函数,我们基于vue开发,只需要创建这个类的实例。

  • let vm=new Vue({options API})
    
  • 第四步:{config}是一个对象,我们可以通过在{config}中配置对应属性来实现vue视图层与数据层的交互

    • 1、指定视图

      • let vm=new Vue({
            //视图
            el:'#computedBox',
        })
        
    • 2、传入数据:data中所有内容都是未来需要在视图中渲染的数据

      •     //数据:data中所有内容都是未来需要在视图中渲染的数据
            data:{
                list: [{
                    id: 1,
                    count: 0,
                    price: 12.5
                }, {
                    id: 2,
                    count: 0,
                    price: 10.5
                }]
            }
        

响应式与非响应式

响应式数据和非响应式数据的区别?

  • 响应式数据:只要数据层中的数据修改了,Vue就会通知视图层重新渲染。例如:在vm创建时构造函数中options下data对象中设置的数据
  • 非响应式数据:即使数据被修改了,也不会造成视图层的重新渲染。例如:vm.text="珠峰",给vm实例设置的私有属性数据,给它重新赋值:vm.text="aaa",这里不会触发视图层的重新渲染。
    • 但是如果其他因素让视图重新渲染了,它也会跟着渲染,比如:调用vm.$forceUpdate():强制让视图渲染、响应式数据修改造成视图渲染

响应式数据VS非响应式数据?

  • 都可以修改值
  • 响应式数据在值修改之后,会通知视图重新渲染:
    • 因为其基于Object.definProperty对其做了get/set劫持,当我们改变响应式数据值时,会触发set函数,在set函数中一方面把值修改,一方面会通知视图重新渲染。

如何判断数据是否为响应式?

  • 后期我们在vue的项目中,我们只需要展开实例,看哪些数据做了get/set,做了劫持的数据是响应式的!!

什么情况下创建的数据是响应式的?

  • 响应式:new Vue的时候,在data中指定的数据,vue默认会对其做劫持处理,这些数据是响应式的
  • 非响应式:自己直接挂载到vm实例上的数据是非响应式的数据。
  • 在Vue开发中,我们把需要的数据一般都写在data中(哪怕现在用不着,我们也先在data中初始化一下),目的是让这些数据变为响应式数据。

在data中存放的数据有什么特点?

一句话:data里面只要是标准普通对象内的私有属性,都会被做get/set劫持,变成响应式数据,其他不会做,数组也不会。

  • 预先初始化数据:在Vue开发中,我们把需要的数据一般都写在data中(哪怕现在用不着,我们也先在data中初始化一下),目的是让这些数据变为响应式数据。
  • 只有默认写在data里面的键值对数据会做get/set劫持,在data外定义的数据不会做劫持
  • 只要是Object对象类型,无论在哪里,都会对它里面的键值对做(get/set)劫持
  • 对data中所有层级的对象的键值对都做深层次的监听劫持
    • 特殊:对数组来说,数组本身会做劫持,但数组内部的各索引项不会做劫持
      • 如果索引项是Object对象类型,则这个索引项里的键值对还是会做劫持
      • vm.arr=100:这是修改arr的值,触发重新渲染
      • vm.arr[0]=100:这是修改arr堆内存中的信息,不会触发重新渲染
    • vue为了解决数组各索引项不做劫持的问题,就把Array的七个方法重写了:push、pop、shift、unshift、splice、sort、reverse,后期我们基于这七个方法操作数组,都会触发视图重新渲染

我们想修改非响应式数据也实现页面的重新渲染,怎么做?

  • 在Vue中,当我们修改非响应式数据后,默认是不会重新渲染的,但是可以利用方法:
  • Vue.prototype:
    • 方式一:vm.$forceUpdate():强制视图渲染
    • 方式二:vm.$set([obj],[key],[value]):将obj对象中[key]属性的值改为[value],并且把[key]属性转换为响应式数据
      • 针对于data中的某个对象,开始没有初始化某个属性,后期才新加的(这样的属性默认是非响应式的:改值视图不会重新渲染):$set可以在后续把对象只能够的这个属性,设置为响应式的。

Vue对象创建方法

  • 创建vm对象的方式2种:

    • 方式一:let vm=new Vue({options API}):直接创建vm实例,但是没有指定任何视图
    • 方式二:Vue.createApp([obj]).$mount("#box"):将obj创建为vm实例,并且挂载到#box节点上(vue3)
  • 指定视图的方法4种

    • let vm=new Vue({
      	//方案一:指定页面中存在的DOM元素作为视图
      	el:`#box`,
      	//方案二:创建视图
             template:`<div>
              //最后把数据渲染在这个视图中
          </div>`,
          //方案四:render渲染视图
              render:h=>{
             	...
          	}
      })
      	//方案三:挂载视图
         vm.$mount(('#app'));
      
  • vm的options API对象中的属性:

    • el:"#box":指定视图,将html中的视图元素与vm绑定
    • data:{msg:'hello world'}:构建数据模型,挂载需要在视图中渲染的数据,这里的数据都是响应式数据
      • 设置的数据直接挂载到vm对象中,成为vm对象的私有属性msg:值就是’hello world’
      • 挂载到实例上的东西能够直接在视图上用{{msg}}使用(小胡子语法
    • methods:{fn1(){},fn2(){}}:视图中需要的方法,这里的this自动指向vm
      • 在new Vue中的methods编写普通方法,这样写方法,不论方法咋执行的,也不论在哪里执行的,方法中的this是vue的实例[vue内部做了处理];所以我们不会把它设置为箭头函数(this->window)
      • methods编写的方法会直接挂载到实例上(不做get/set劫持),所以可以直接在视图中使用
    • computed:{pro1(){},pro2(){}}:计算属性:【多个值变化,改变一个结果值】依赖于某些数据值,计算出新的值
      • 缓存效果:依赖的值没变,会用之前计算的值,只有依赖的值变了,才会重新计算】
      • 这里的this也指向vm
    • watch:{pro1(){},pro2(){}}:侦听属性:【一个值变化,改变多个结果值】
  • vm实例的方法:

    • vm.$forceUpdate():强制让vm视图渲染
    • vm.$set([obj],[key],[value]):将obj对象中[key]属性的值改为[value],并且把[key]属性转换为响应式数据
      • 针对于data中的某个对象,开始没有初始化某个属性,后期才新加的(这样的属性默认是非响应式的:改值视图不会重新渲染):$set可以在后续把对象只能够的这个属性,设置为响应式的。

API

全局API

Vue.filter
  • filters配置的是局部过滤器,只能在自己实例挂载的视图上使用

  • Vue.filter配置的是全局过滤器,可以在任何挂载了new Vue的视图上使用

  • 定义方式

    •         //addZero就是全局过滤器名称
              //value是调用过滤器的状态值
              Vue.filter("addZero",function(value){
                  return "0"+val;
              });
              //addOne就是全局过滤器名称
              //value是调用过滤器的状态值
              Vue.filter("addOne",function(value){
                  return "1"+val;
              });
      
  • 用法:可以使用多个过滤器

    • 		    			<div>{{num|addZero|addOne}}
                            		</div>
              ```
      
      
      
      
      
      
      
      
      
      
      
      

选项

选项就是能够在new Vue({options}),options中能够配置的属性项,称为选项

选项/数据
computed
  • computed:计算属性【多个值变化,影响到一个结果值变化】

    • 计算:以函数的形式定义

    • 属性:以属性的形式使用

    • 定义方式:可以是直接函数和对象定义的两种方式

    •     //函数定义方式
      <!-- 以属性的形式使用 -->
          <div>{{funllname}}</div>
          <script>
              let vm=new Vue({
                  computed:{
                      // 以函数的形式定义
                      fullname(){
                          return this.first+"-"+this.last;
                      }
                  }   
              })
      
      • **特点:**computed里属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算
    • 对象定义方式:computed中的计算属性本身其实存在两个方法,只是默认获取,get()和set方法,原理:使用了Object.defineProperty()中的get/set劫持

    •     //对象定义方式        
      //计算变量的get和set方法
      	computed:{
                  fullname:{
                      // 对fullname属性获取时拦截,也是函数定义形式使用的默认方法
                     get(){
                         //实现多个值变换,改变一个结果值
                      return this.first+"-"+this.last;
                     },
                      //对fullname属性赋值时拦截     
                     set(val){//val是想赋给fullname的值,也可以说是心值
                      let arr=val.split("-");
                      //实现一个值变化,改变多个结果只
                      this.first=arr[0];
                      this.last=arr[1];
                     }
                  }
              } 
      
watch
  • watch:侦听属性【一个值变化,影响多个结果值变换】

    • 侦听:watch中的定义的属性名就是我们监控的vm里的私有属性名,当这个私有属性发生变化是,会自动执行watch中对应方法:可以看作是computedmethods的结合体

    • 特点watch能够监控路由,如果不需要监控路由,我们尽可能的用computed的set方法来代替watch,原因是computed有缓存

    • 注意:我们监控的对象必须vm里已经存在该私有属性

    • 用法:可以是直接函数和定义对象两种用法

      • 直接函数的定义方式:不能给对应属性配置参数
      • 对象的定义方式:可以给对应属性配置参数:deep:是否深层侦听immediate:页面第一次加载时是否立即侦听一次
      • 如果我们侦听的是一个obj对象,对象里面的属性值发生改变时,并不会触发obj的侦听方法,这时我们就用obj的对象定义形式将obj的deep配置项设为true
    •     watch:{
              //fullname就是我们监控的vm实例中的私有属性,存在两个值
              //函数式写法
              "obj"(newVal,oldVal){
                  let arr=newVal.split("-");
                  //实现一个值变化,改变多个结果值
                  this.first=arr[0];
                  this.last=arr[1];
              },
              // 对象式写法
              "obj":{
                  // 这是函数式写法默认用的方法
                  // handler(newVal,oldVal){...}
                  //设置对obj的属性是否进行深侦听
                  deep:true,
                  // 设置对obj的属性是否在第一次渲染页面时立即侦听一次
                  immediate:true
              }
          }
      
method
  • methods:方法定义:方法表示一个具体的操作,主要书写业务逻辑;与我们js中函数的写法和用法相同

  • computed、watch、methods的区别?

    • computed是属性调用,而methods是函数调用
    • computed带有缓存功能,而watch没有
    • watch可以监控对象,而computed、methods不能
  • 以下写法都是点击的时候才会把handle执行:

    • @click="handle"=>handle(ev){...}
    • @click="handle(10,20)"=>handle(10,20){...}:也不是把handle立即执行,而是点击才执行,只不过点击的时候,会把10/20传递给
    • @click="handle(10,20,$event)"=>handle(10,20,ev){...}:视图中出现的$event代表是事件对象
props
  • props 可以是数组或对象,用于接收来自父组件的数据,props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。

  • // 简单语法
    Vue.component('props-demo-simple', {
      props: ['size', 'myMessage']
    })
    
    // 对象语法,提供验证
    Vue.component('props-demo-advanced', {
      props: {
        // 检测类型
        height: Number,
        // 检测类型 + 其他验证
        age: {
          type: Number,
          default: 0,
          required: true,
          validator: function (value) {
            return value >= 0
          }
        }
      }
    })
    
选项/资源
filters
  • 作用对某些状态值进行处理,并返回符合规则的值

    • 能够在filter属性中定义过滤器(局部过滤器),只能在自己vm实例绑定的视图下使用.
    • 只能在【小胡子语法{{}}】和【v-bind命令】中使用
  • 定义

    •             filters:{
                      //val是调用过滤器的状态值
                      addZero(val){
                          //返回值就是我们视图中真正获取的值
                          return "0"+val;
                      },
                      //val是调用过滤器的状态值
                      addOne(val){
                          //返回值就是我们视图中真正获取的值
                          return "1"+val;
                      }
                  }
      
  • 使用方式:可以同时使用多个过滤器

    • 		    			//如果用的不是全局过滤器,则视图绑定的vm中必须存在想用的局部过滤器
                  			//多个过滤器从左到右执行
                  			<div>
                    			 {{num|addZero|addOne}}
                  		</div>
      

vm实例

实例私有属性
  • vm.$parent😗*[vue实例]**获取父元素的整个vm实例
    • 子组件可以在父组件的任何生命周期函数中获取【父子组件的生命周期
  • vm.$children[n]:获 取第n个子元素的vm实例
    • 父组件只能在子组件created生命周期函数里或之后获取子元素【父子组件的生命周期
  • vm.$root😗*[vue实例]**获取根元素的vm实例(main.js中new 的Vue实例)
  • vm.$refs😗*[数组]**this的子元素中需要定义ref属性:比如ref="xxx"
    • 如果ref定义在DOM标签中this.$refs.xxx获取的是DOM对象
    • 如果ref定义在子组件标签中this.$refs.xxx获取的是子组件的vm实例
实例公有方法
数据
  • vm.$watch:还没学到
  • vm.$set(obj,"name","sjh"):给vm实例data中的obj添加一个name属性,值为sjh,并且把它变为响应式状态值,(默认在data之外添加数据this.obj.name="sjh"是不会变成响应式数据的,不会触发页面重新渲染的)
  • vm.$delete(obj,"name"):与$set作用相对应,删除vm实例data中obj对象中的name属性,并且通知页面重新渲染,(默认在data之外删除数据delete this.obj.name是不会触发页面重新渲染的)
事件
  • vm.$on("myEvent",fn):向事件池中添加myEvent事件,并绑定fn函数,能被调用无数次

  • vm.$once("myEvent",fn):向事件池中添加myEvent事件,并规定被调用一次之后就销毁

  • vm.$emit("myEvent",parm1,parm2,...):执行事件池中的myEvent事件函数,并把第二个及以后的实参赋值给myEvent绑定的函数

  • vm.$off("myEvent"):删除掉事件池中的myEvent事件

生命周期
  • vm.$mount("#box"):如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。(只有被vue挂载上的#box标签,内部才能用vue的框架的各种属性)
    • 这个方法返回实例自身,因而可以链式调用其它实例方法。
  • vm.$forceUpdate():执行该方法会强制刷新页面渲染
  • vm.$nexTick([callback])将回调函数延迟到下次 DOM 更新循环之后执行
  • vm.$destory():触发beforeDestory()、destoryed()钩子函数阶段

内置组件

  • <component></component>:渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。
    • 用法<component is="coma"></component>,它会找==new Vue()==的components中去找coma组件渲染
    • 如果想动态绑定值,用v-bind<component :is="bindprop"></component>

特殊Attribute

  • is:需要结合和component标签(内置组件)使用,用于动态组件,且基于 DOM 内模板的限制来工作。

    • 用法<component is="coma"></component>,它会找==new Vue()==的components中去找coma组件渲染
    • 如果想动态绑定值,用v-bind<component :is="bindprop"></component>

视图层

视图层的构建

  • Vue构建视图是基于“template”模板语法:template中定义了很多vue内置的语法规范,这些规范不能被浏览器直接识别,需要基于vue进行渲染
    • 流程:
      • 根据数据和template视图,把其识别为一个虚拟DOM对象(vnode)->DOM-DIFF->把vnode虚拟DOM变为真实DOM
      • @1template中存在大量的指令v-xxx,用来构建视图和完成相关的效果
        • vue是根据template语法来构建视图的

普通指令:v-xxx

v-text/v-html
  • 渲染数据的属性2个:

  • v-text/v-html:[string]:把数据渲染到指定的容器中

    • <h2 v-html="msg"></h2>:把msg数据渲染到h2标签中,如果数据内容是html字符串【带标签】,会自动进行识别渲染
    • <h2 v-text="msg"></h2>:把msg数据渲染到h2标签中,如果数据内容是html字符串【带标签】,不会进行识别渲染,与{{}}小胡子语法相同
v-show
  • 控制元素显示隐藏的属性2个:

  • v-show:[boolean]:控制元素的显示隐藏{原理:控制display:none/block}

    • 特点:这种方式,无论结果是TRUE还是FALSE,元素都渲染出来了
v-if/v-else-if/v-else
  • 基于条件控制DOM元素渲染或销毁3个:

  • v-if:[boolean]:控制元素的显示隐藏,但是和v-show不一样,如果值是false,元素是不进行渲染的(页面中没有这个元素),只有结果是true,元素才会渲染!!

  • 两者应该分场景使用:

    • 如果元素切换显示隐藏频率不高的情况下,使用v-if性能更好
    • 如果元素切换显示隐藏频率比较高的情况下,v-if会使元素一直销毁和重新渲染,此时使用v-show更好
  • v-else-if:[boolean]/v-else:与v-if联合使用,如果三个组合用,用的标签必须挨着,否则无法生效

    • v-else-if:[boolean]:if条件不成立则判断这个条件是否成立
    • v-else:if或else-if条件不成立,则判断这个条件
v-for
  • 循环渲染元素的attribute1个:

  • v-for="(item,index) in xxx" :key="index":循环创建元素,实现内容绑定

    • 想让哪个元素创建多个,就给哪个元素设置v-for
    • 循环的元素要设置唯一值key【最好不要使用索引作为key?
    • xxx可以是数字、字符串、数组类型
      • <li v-for="(item,index) in 5 :key="index"></li>:xxx是数字,先隐式转换为数组[1,2,3,4,5],再进行逐项循环渲染
      • <li v-for="(item,index) in 'str' :key="index"></li>:xxx是字符串,先隐式转为数组[s,t,r],再逐项循环渲染
      • <li v-for="(item,index) in [arr] :key="index"></li>:xxx是数组类型,直接逐项循环渲染
v-on
  • 给元素绑定事件的attribute1个:

  • v-on:[事件]="要执行的方法":缩写为@事件="要执行的方法"

    • 原理基于DOM2事件绑定实现的 addEventLinstener冒泡事件【好处是,后期获取到这个DOM元素对象,再做事件绑定,也不会和@click绑定的方法冲突】
      • 如何阻止冒泡传播:
        • 原生:函数体内实现e.stopPropergation()
        • vue:使用修饰符@click.stop=function(){},与原生想用
    • 要执行的方法:在new Vue中的methods编写普通方法,这样写方法,不论方法咋执行的,也不论在哪里执行的,方法中的this是vue的实例[vue内部做了处理];所以我们不会把它设置为箭头函数(this->window)
    • 以下写法都是点击的时候才会把handle执行:
      • @click="handle"=>handle(ev){...}
      • @click="handle(10,20)"=>handle(10,20){...}:也不是把handle立即执行,而是点击才执行,只不过点击的时候,会把10/20传递给
      • @click="handle(10,20,$event)"=>handle(10,20,ev){...}:视图中出现的$event代表是事件对象
v-bind
  • 给元素的attribute动态绑定值:【存在修饰符】

  • v-bind:xxx="xxx":简写为:xxx="xxx"给元素的attribute动态绑定值

    • 但凡属性值是一个变量的值或者是一个表达式计算的结果,都需要给属性v-bind一下

    • 基于属性给子组件传递信息的时候,如果想让属性值是字符串以外的类型,也需要v-bind一下

    • 动态绑定class:可以是对象或数组

      • 对象语法::class="{box:true,active:true/false}":active是class样式类名,它的值true/false决定元素是否拥有这个样式类名

      • 数组语法::class="['box',num>10?'active':'']":box是肯定有的类名,根据num条件决定是否有active类名

    • 动态绑定style:样式需要用驼峰命名法,可以是对象或数组

      • 对象语法::style="{color:'red',fontSize:num>10?'20px':'12px'}":元素颜色静态为红色,字体大小根据num决定
      • 数组语法::style="[color,fontSize]":每个数组中的数据项都是一个对象,存在样式名和值
v-slot
  • vue组件化中,会有插槽的配置,目的是给调用的组件的template设置插槽或者获取组件对应的数据

    • v-slot可以简写为#:#xxx

    • ==具名插槽:==组件中预设好多插槽位置,为了后期可以区分插入到哪,我们把插槽设置名字

      • 组件内部:<slot name="xxx">默认名字是default

      • 调用组件:需要把v-slot写在template上

        •       <template v-slot:xxx>
                   ...
                </template>
                <template>
                   ...
                </template>
          



​ - vue ​ 调用组件:需要把v-slot写在template上 ​ <template v-slot:xxx> ​ ... ​ </template> ​

  • ==作用域插槽:==把组件内部定义的数据,拿到调用组件时候的视图中使用

    • 组件内部:

    • <slot name="top" :list="list" :msg="msg"></slot>

    • 把组件中的list赋值给list属性,把msg赋值给msg属性,插槽中提供了两个作用域属性:list/msg

    • 调用组件:

      •   <template #top="AAA"></template>
          定义一个叫做AAA的变量,来接收插槽中绑定的数据
          AAA={
            list:[...],
            msg:...
          }
        
v-model
  • 实现视图更新控制状态修改
  • v-model:实现视图更新控制状态修改【存在修饰符】
    • 实现原理:
      • @1把状态值赋值给文本框的value属性
      • @2基于inputchange事件监听文本框内容的改变,当内容改变的时候,去修改对应的状态值

能够用v-model的修饰符的标签类型有input/textarea、select(option)、input[type='radio']、input[type='checkbox']

  • <input v-model="text">、textarea

    • 1、把text状态值赋给value
    • 2、当value输入时,触发input事件,把value值赋给text状态值
  • <select v-model="city"></select>:

    • 1、拿city状态值和option中的value值做比较,和谁一样,谁默认选中
    • 2、监听下拉框的change事件,选中的是谁,就把那个选项的value赋值给city状态
    • 3、select标签加mutiple属性表示可多选,则状态值需要是一个数组
  • `<input type="radio" name="sex" :value="0" v-model="sex">`男
    `<input type="radio" name="sex" :value="1" v-model="sex">`女
    
    • 1、会把v-model中的sex状态值相同的作为同一组,一组中只能选中一个【也可以自己设置name】
    • 2、根据sex状态值和value值做对比,相同的则默认被选中
    • 3、监听每一个radio的change事件,把选中radio的value值赋值给sex状态值
  • <input type="checkbox" name="hobby" value="jump" v-model="hooby">音乐
    <input type="checkbox" name="hobby" value="movie" v-model="hooby">电影
    <input type="checkbox" name="hobby" value="music" v-model="hooby">跳舞
    <input type="checkbox" name="hobby" value="read" v-model="hooby">阅读
    
    • 1、会把v-model中的hooby状态值相同的作为同一组,一组中可以选中多个,所以hobby状态值必须是一个数组
    • 2、监听每一个checkbox的change事件,只要有选项被选中或取消选中,都会把value值赋值给hobby状态值

性能优化指令

v-pre

让元素及其后代元素跳过编译:

  • v-pre:在视图编译渲染的时候,跳过拥有这个指令的元素及其后代元素[不进行任何的语法的编译,你写的是啥,呈现在视图上就是啥];
    • <span v-pre>{{ this will not be compiled }}</span>
    • 真实项目中,我们完全可以把一些“静态内容”不进行任何的编译,以此来优化视图编译渲染的速度!!
v-once

控制元素或组件的编译次数:

  • v-once:让元素或者组件只在视图第一次渲染的时候编译一次,当视图重新渲染的时候,这部分内容就不再重新编译了;
    • 适用于第一次动态绑定完成后,后期不会再更新的内容
v-cloak

延迟显示元素:

  • v-cloak:在非工程化的vue项目中,在js没有加载出来之前,页面会呈现出原始的{{xxx}}这种内容,只有js加载出来并处理完成,才能把template语法编译为真实DOM渲染到页面中。

    • 为了防止这种“闪”的效果,我们在js没有加载完成之前让容器隐藏,加载完成之后再让它显示。

    • 用法:

      • <style>
        	[v-clock]{
        		display:none;
        	}
        </style>
        <div v-clock></div>
        

自定义指令

  • 与filter过滤器定义相似,可以定义全局(directive)的或局部的(directives)

    • 局部:定义在new Vue()中

      • new Vue({
        	directives:{
        		inserted(){}
        	}
        })
        
    • 全局:Vue.directive()

修饰符

v-on修饰符:@xx.xxx25个

通用修饰符8个

  • @click.stop:阻止事件的冒泡传播=>同理于:e.stopPropergation()
  • @click.prevent:阻止事件的默认行为=>同理于:e.preventDefault()
  • @click.passive:是否允许阻止事件的默认行为,true表示不允许阻止,false表示允许阻止=>同理于:设置addEventListener第三个参数options中的passive为true
  • @click.capture:将事件在捕获阶段触发=>同理于:dom.addEventListener([event],[fun],true),[默认都是在冒泡阶段触发]
  • @click.self:只有点击的事件源就是当前元素本身,绑定的方法才会执行{但是没有阻止冒泡传播,如果点击了它的子元素,则会跳过当前元素,继续向祖先级元素冒泡}
  • @click.once:只触发执行一次,然后把事件绑定的方法一吃掉=>相当于执行一次后会默认执行dom.removeEventListener([event],fn)
  • @xxx.ctrl.a.exact:精确地,让默认的只要按下ctrl+a就触发,变成只有按下ctrl+a才触发
  • @xxx.native:在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的HTML标签,不加’. native’父组件绑定给子组件标签的事件是无法触发的

鼠标事件的修饰符3个

  • @click.left/right/middle 鼠标左中右按键

键盘事件的修饰符13个

键盘事件的触发前提条件是需要一个能够获取焦点的元素

@keydown/@keyup/@keypress

@keydown.13:直接使用键码也可以,【所有的键码】

  • @keydown.enter:只要按下enter键时就会触发事件

  • .space/.delete/.esc

  • up/down/left/right

  • ctrl/alt/shift/tab/meta(mac键)

自定义按键修饰符1个

  • Vue.config.keyCodes.a=65;

  • @keydown.a='xxx'

  • @keydown.65='xxx'

  • 组合按键

    • @keydown.ctrl.65="xxx":只要按下ctrl+a就会触发
      • 默认情况下,按下的按键只要包含组合键(==哪怕还按了其他键)==也会触发,但是我们可以基于.exact修饰符,设置精准匹配(只有按下规定的键,其他键不按,才会触发)
    • @keydown.ctrl.65.exact:只有按下ctrl+a才会触发
    • @click.exact:只有只点击了单击按键时触发
v-bind修饰符:attribute.xxx3个
  • :sup-name.camel:把设定的sup-name(中杠分格命名法)自动转为camel(驼峰命名)规范=>supName

  • :supName.prop:把html结构上的attribute,转换为DOM节点对象的私有property

    • 默认情况下,给元素设置的属性都会呈现在元素的结构上,我们可以基于getAttribute/setAttribute来设置获取;
    • 但是设置了prop修饰符,设置的自定义属性不在结构上,不再html结构中显示,而是放到了DOM对象的堆内存中
    • 给元素设置自定义属性有两种方式
      • 方式一:写在结构上,<div index="8"></div>,用setAttribute、getAttribute
      • 方式二:写在堆内存中,box{index:8},用box.index=8、box.index
  • :supName.sync:语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器

v-model修饰符3个
  • v-model.lazy:把监听事件从input换成change
    • input事件:只要正在输入就触发,不一定输入的内容进入到文本框中(例如:中文输入框)
    • change事件:内容已经进入到文本框,摁下enter键时,(value值确认改变时),才会触发
    • 所以change比input性能消耗低、但是流畅度也比较低
  • v-model.number:规定输入的内容必须是数字格式
    • 文本框中默认输入的内容都是字符串格式,设置了这个修饰符,vue内部会把输入的内容自动转换为数字(如果出现非有效数字字符,则会把非有效字符及以后的内容干掉)
  • v-model.trim:自动去除输入内容的首尾空格

生命周期函数(钩子函数)

我们想看某个阶段的数据情况:直接在new Vue中调用对应的方法,比如查看初始化之前的:beforeCreate(){}

  • 生命周期钩子分为了四个阶段:初始化、模板编译、更新、销毁

    • 初始化:init(new Vue的时候走)

      • berforeCreate:【初始化之前】组件实例刚刚创建,无任何属性,所以在这里,不能给任何属性赋值
      • created:【初始化之后】组件实例创建完成,datamethods中的数据已完成,el已经生成了,但是因为需要挂载DOM节点,而DOM树还没生成,所以el还没有被赋值。
    • 模板编译:mount(new Vue的时候走)

      • beforeMount:【模板编译之前】页面未渲染(没化妆),页面知道了{{msg}}需要vm中msg值,但并没有将vm的msg挂载上去
      • -----------------------------------------------------------这里是真正渲染页面的阶段--------------------------------------------------------------------
      • mounted:【模板编译之后】页面渲染结束(化完妆了),到这里已经向页面中挂载完了数据,已经将vm中的msg获取到,并且挂载到{{msg}}上去
    • 更新:update(响应式数据变了才会触发更新)

      • beforeUpdate:【更新之前】响应式数据变化了,但页面还没来的及变化
      • updated:【更新之后】响应式数据变化了,页面也更新了
    • 销毁:destroy(调用了vm.$destroy()方法,才会触发销毁阶段)

      • beforeDestroy:【销毁之前】实例销毁之前调用。在这一步,实例仍然完全可用,vm实例与视图的链接还没中断
      • destroyed:【销毁之后】实例销毁后调用。对应 Vue 的vm实例与视图的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁但是vm实例仍然存在,仍然可以获取vm中的私有属性,vm与视图的链接完全中断了
      • 执行了destroy操作,视图中的原来样式保留,后续就不再受vue控制了
    •     <script>
              let vm=new Vue({
                  el:"div",
                  data:{
                      msg:[10,20,30]
                  },
                  beforeCreate(){
                      // this.msg[0]=100;//报错:因为还没有生成data,vm中不存在msg私有属性
                  },
                  created(){
                      this.msg[0]=100;//vm.msg=[100,20,30],已经生成了data和methods,可以修改vm的私有属性msg
                  },
                  beforeMount(){
                      this.msg[1]=200;//vm.msg=[100,200,30],修改了vm的私有属性,但是页面还没有被渲染:<div>{{msg}}</div>=><div>{{msg}}</div>
                  },
                  //在这两者之间,页面被渲染了:<div>{{msg}}</div>=><div>[100,200,30]</div>
                  mounted(){
                      this.msg[2]=300;//vm.msg=[100,200,300],但是页面已经渲染了,页面的显示仍然是:<div>{{msg}}</div>=><div>[100,200,30]</div>
                  },
                  beforeDestroy(){
                      //执行了vm.$destroy(),在这一步,实例仍然完全可用
                  },
                  destroyed(){
                      //执行了vm.$destroy(),到这一步,Vue的vm实例与视图的所有关系都解除了:指令都被解绑、事件监听器被移除、子实例也都被销毁
                  }
              })
      
              vm.$destroy();
              console.log(vm);
      
          </script>
      
  • 除以上8个常用钩子函数之外还有3个不常用的钩子函数:

    • 当keep-alive 缓存组件时才会有的生命周期的钩子函数

      • activated:激活,被 keep-alive 缓存的组件激活时调用。

      • deactivated:停用,被 keep-alive 缓存的组件失活时调用。

      • <keep-alive>
        keep-alive:用于缓存组件,如果该组件还会再启用,那么可以使用keep-alive进行组件缓存和优化,提高性能,缓存的组件不需要销毁,也不需要再创建
        <component v-bind:is=“one”> //如果是动态属性,那么会去data中取值
        </keep-alive>
        
            
            
        
      • // keep-alive 缓存组件 生命周期的钩子函数 activated deactivated
        // 生命周期的钩子函数
        activated() {undefined
        // 当缓存组件有被显示出来时,会触发这个钩子函数
        console.log(100);
        },
        deactivated() {undefined
        // 当缓存的组件隐藏时,会触发这个钩子函数;
        console.log(200);
        }
        
    • errorCaptured:错误捕获

      • 语法:(err: Error, vm: Component, info: string) => ?boolean

      • 说明:当子孙组件出错时,会调用这个钩子函数。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播

      • // 当子孙组件出错时,会调用这个钩子函数
        errorCaptured(a, b, c) {undefined
        // 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
        console.log(a);
        console.log(b);
        console.log©;
        console.log(“子组件报错”)
        },
        
        

生命周期图.jpg

vue过渡与动画

过渡

  • vue提供了transition的内置组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡
    • 条件渲染(使用v-if)
    • 条件显示(使用v-show)
    • 动态组件
    • 组件根节点

过渡的类名:

image-20211211233924356
  • 在进入/离开的过渡中,会有6个class切换

    • 使用方式:transition标签默认存在六个类名,这些类名都可以在style标签中用类选择器进行样式定义:

      • v-enter:定义进入过渡的开始状态,在过渡开始之前生效,在DOM元素被插入之后的下一帧移除

      • v-enter-active:定义进入过渡生效时的状态,在整个过渡的阶段中应用,在过渡/动画完成之后移除【用来定义进入过渡的过程时间,延迟和曲线函数(transition)

      • v-enter-to:定义进入过渡的结束状态。在DOM元素被插入之后的下一帧生效,在过渡/动画完成之后移除

      • v-leave:定义离开过渡的开始状态。在离开过渡被触发时立即生效,下一帧立即移除

      • v-leave-active:定义离开过渡生效时的状态,在整个离开过渡的阶段中应用,在过渡/动画完成之后移除【用来定义进入过渡的过程时间,延迟和曲线函数(transition)

      • v-leave-to:定义离开过渡结束状态。在元素被插入之后,在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

        <div id="demo">
            <--!transition标签默认存在-->
        	<transition name="mytran">
        		<p v-if="show">
            		hello
            	</p>
        	</transition>
        </div>
        <script>
        	new Vue({
                el:"#demo",
                data:{
                    show:true
                }
            })
        </script>
        <style>
        .mytran-enter-active {
          transition: all .3s ease;
        }
        .mytran-leave-active {
          transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
        }
        .mytran-enter, .mytran-leave-to
        /* .mytran-leave-active for below version 2.1.8 */ {
          transform: translateX(10px);
          opacity: 0;
        } 
        </style>
        
    • **自定义类名:**对于以上6个类名,如果我们直接用<transition>标签,则类名都是v-xxx,如果想与默认的transition区分出来,需要给标签添加name标签<transition name="mytran">,这时这个标签动画的所有类名就变成了mytran-xxx

动画

  • css动画与过渡的区别:就是在动画中 v-enter 类名在节点插入 DOM 元素后不会立即删除,而是在 animationend 事件触发时删除。

  • <div id="example-2">
      <button @click="show = !show">Toggle show</button>
      <transition name="bounce">
        <p v-if="show">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero, at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.</p>
      </transition>
    </div>
    
  • new Vue({
      el: '#example-2',
      data: {
        show: true
      }
    })
    
  • .bounce-enter-active {
      animation: bounce-in .5s;
    }
    .bounce-leave-active {
      animation: bounce-in .5s reverse;
    }
    @keyframes bounce-in {
      0% {
        transform: scale(0);
      }
      50% {
        transform: scale(1.5);
      }
      100% {
        transform: scale(1);
      }
    }
    
  • animation:动画名称+动画的持续时间+动画执行顺序

  • @keyframes bounce-in:定义整个动画,按百分比实现动画分阶段状态

多组件、元素过渡

  • 多元素过渡用v-bind:key指定唯一标签

    • <transition>
        <button v-if="isEditing" key="save">
          Save
        </button>
        <button v-else key="edit">
          Edit
        </button>
      </transition>
      
  • 多组件过渡:用component标签和is属性,实现动态组件绑定

    • <transition name="component-fade" mode="out-in">
        <component v-bind:is="view"></component>
      </transition>
      
    • new Vue({
        el: '#transition-components-demo',
        data: {
          view: 'v-a'
        },
        components: {
          'v-a': {
            template: '<div>Component A</div>'
          },
          'v-b': {
            template: '<div>Component B</div>'
          }
        }
      })
      
    • .component-fade-enter-active, .component-fade-leave-active {
        transition: opacity .3s ease;
      }
      .component-fade-enter, .component-fade-leave-to
      /* .component-fade-leave-active for below version 2.1.8 */ {
        opacity: 0;
      }
      

列表过渡

  • 列表过渡常用在v-for的标签里边,这时标签名就要用<transition-group>

    • 不同于 <transition>,它会以一个真实元素呈现:默认为一个 <span>。你也可以通过 tag attribute 更换为其他元素。
    • 过渡模式不可用,因为我们不再相互切换特有的元素。
    • 内部元素总是需要提供唯一的 key attribute 值。
    • CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
  • <div id="list-demo" class="demo">
      <button v-on:click="add">Add</button>
      <button v-on:click="remove">Remove</button>
      <transition-group name="list" tag="p">
        <span v-for="item in items" v-bind:key="item" class="list-item">
          {{ item }}
        </span>
      </transition-group>
    </div>
    
  • new Vue({
      el: '#list-demo',
      data: {
        items: [1,2,3,4,5,6,7,8,9],
        nextNum: 10
      },
      methods: {
        randomIndex: function () {
          return Math.floor(Math.random() * this.items.length)
        },
        add: function () {
          this.items.splice(this.randomIndex(), 0, this.nextNum++)
        },
        remove: function () {
          this.items.splice(this.randomIndex(), 1)
        },
      }
    })
    
  • .list-item {
      display: inline-block;
      margin-right: 10px;
    }
    .list-enter-active, .list-leave-active {
      transition: all 1s;
    }
    .list-enter, .list-leave-to
    /* .list-leave-active for below version 2.1.8 */ {
      opacity: 0;
      transform: translateY(30px);
    }
    

如何启一个项目

  • 第一步:安装脚手架$npm i @vue/cli -g

  • 第二步:创建一个项目$vue create xxx

    • 选择各项配置…最后安装成功。。。
  • 第三步:.gl文件、readme文件都可以删除,无用的初始文件都可以删掉

  • 第四步:把没有安装的模块自己安装一下:

    • $npm i axios qs blueimp-md5 element-ui
    • 生产依赖element-ui axios qs blueimp-md5 @babel/polyfill vue vuex. vue-router
    • 开发环境less less-loader babel-plugin-component -D(UI组件库按需导入模块)
  • 第五步:配置浏览器兼容

    • //package.json
      	"browserslist": [
          "> 1%",
          "last 2 versions",
          "not dead"
        ]
      
    • 安装:@babel/polyfill在main.js中导入

  • 第六步:完成基础的配置

    • //vue.config.js
      	const env=proccess.env.NODE_ENV;//获取环境“production”或“development”
      	module.exports={
      	lintOnSave: env!=='production',//是否开启词法检测
      	publicPath:"./",
      	productionSourceMap:false,
      	devServer:{
      	proxy:{
      		'/api':{
      			target:"http://127.0.0.1:9999",
      			changeOrigin:true,
      			ws:true,
      			pathRewrite:{
                  '/api':''
              }
      		}
      	}
      	}
      	}
      
  • 第七步:element-ui的全量导入或局部导入

  • 第八步:在vuex里配置日志检测vuex

    • //index.js	->vuex
      	import Vue from 'vue'
      import Vuex from 'vuex'
      import createLogger from 'vuex/dist/logger'
      
      Vue.use(Vuex)
      const env=process.env.NODE_ENV;
      export default new Vuex.Store({
        state: {
        },
        mutations: {
        },
        actions: {
        },
        modules: {
        },
        plugins:env=="production"?[]:[createLogger()]
      })
      
      
  • 第九步:api文件下创建http.js对axios二次封装

    import axios from 'axios';
    import qs from 'qs';
    import {
        Message
    } from 'element-ui';
    
    // 基础配置
    axios.defaults.baseURL = "";//设置所有请求前缀;因为拿vue.config.js做了这里就不做了
    axios.defaults.timeout = 60000;//设置超时时间
    axios.defaults.withCredentials = true;//设置跨域是否携带资源凭证
    axios.defaults.transformRequest = data => {//设置对象转为urlencoded格式
        if (isPlainObject(data)) return qs.stringify(data);
        return data;
    };
    axios.defaults.validateStatus = status => {//设置状态码正确范围
        return status >= 200 && status < 400;
    };
    
    // 拦截器
    axios.interceptors.request.use(config => {//设置请求拦截器
        return config;
    });
    axios.interceptors.response.use(response => {//设置响应拦截器
        return response.data;
    }, reason => {
        if (reason && reason.response) {
            // @1 有返回结果,只不过状态码不对
            let {
                status
            } = reason.response;
            switch (+status) {
                case 403:
                    Message({
                        type: 'error',
                        message: '小主,服务器拒绝了当前请求~~'
                    });
                    break;
                case 404:
                    Message.error('小主,您请求的地址是错误的~~');
                    break;
                case 500:
                    Message.error('小主,服务器开小差了~~');
                    break;
            }
        } else {
            // @2 请求超时或者中断 
            if (reason && reason.code === "ECONNABORTED") {
                Message.error('小主,当前请求超时了~~');
            }
            // @3 断网
            if (!navigator.onLine) {
                Message.error('小主,当前网络繁忙请稍后再试~~');
            }
        }
        return Promise.reject(reason);
    });
    
    export default axios;
    
    • index.js 存放各种api接口
  • 第十步:分析需求:

    • 分析如何构建路由
    • 分析组件如何划分

管理系统项目

  • 一:登录和登录态的校验【一般管理系统的项目,如果用户没有登陆,什么都访问不了】
    • 方案一:基于cookie加session的校验机制
      • 缺点:使用的是存储方案,服务器一重启,所有session都没有了,而且不利于分布式部署
      • 解决方案:用redis存储代替session【或者备份一份】
    • **方案二:**基于token机制【当前最常用的

vue实现图片懒加载

  • 借鉴地址:https://www.cnblogs.com/xieli26/p/10057763.html

  • **第一步:**安装插件

    • npm install vue-lazyload --save-dev
      
  • **第二步:**在main.js中引入使用

    • import VueLazyload from 'vue-lazyload'
      //直接使用:
      Vue.use(VueLazyload)
      
      //或者配置使用
      Vue.use(VueLazyload, {
      preLoad: 1.3,
      error: 'dist/error.png',
      loading: 'dist/loading.gif',
      attempt: 1
      })
      
  • **第三步:**修改img标签为懒加载(将 :src 属性直接改为v-lazy)

    • <a href="javascript:;"><img v-lazy="'/static/img/' + item.productImage"></a>
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值