Vue

4 篇文章 0 订阅

监听的属性发生改变就会执行回调函数
注意:Vue所管理的函数,都不能写成箭头函数,
Vue管理的函数:methods中定义的函数,计算属性,监听器中的函数

目录结构:

  node_modules:第三方依赖
    public:公共资源目录
      api接口资源
      imgs   -->public中的图片资源在当前项目下通过/img直接可以访问到
    src:源码
        network 文件夹用于项目数据的请求的发送,网络请求模块
        assets:静态资源,css、img、js、font等
        compoments:一般组件存放的目录
        router:路由配置,导入自定义的组件,创建路由,一般将views中的组件绑定上路由,
          index.js--里面用于配置所有的路由信息,配置路由与组件的映射关系
        pages:这个文件夹需要自己手动创建,整个文件夹存放路由组件(受路由管理的组件)
        mixin:写一些vue混入的数据或者方法,混入就是给vue实例添加的方法或者属性,通过this直接可以调用到混入对象中的一些方法和属性
        utils:存放自定义插件
			  myPlugin.js
        store:Vuex的文件夹
            index.js -->写store state 然后导入下面的文件
            mutations.js -->写 mutations的代码
            getters.js -->写getters的代码
            actions.js --》写actions的代码
            modules 存放modules模块的文件夹
              modulesA.js  存放模块a的代码
              ...
        App.vue:首页组件(默认组件)一般导入components中的组件,汇总所有组件
        main.js:入口文件
    .browserslistrc:配置使用CSS兼容性插件的浏览器的使用范围
    .editorconfig:统一和规范代码书写,【vue-cli3中没有,一般来说项目中需要这个文件】
    .eslintrc.js:配置ESLint
    .gitignore:配置git忽略的文件或者文件夹
    babel.config.js:使用一些预设
    package.json:项目描述既依赖 可以根据这个文件下载整个项目所依赖的包,npm install
    package-lock.json:版本管理使用的文件
    vue.config.js 自定义webpack配置【在vue-cli3中使用,这个文件最终会和默认的配置进行合并】
    README.md:项目描述

MVVM:Vue没有完全遵循MVVM模型

  M:模型数据 Model:对应的是data中的数据
  V:视图 View(模板页面):DOM
  VM:视图模型(View):Vue缔造的实例对象【作用:1.数据绑定,2.数据监听】
    数据绑定:将模型数据绑定到【展示到】页面中的指定位置
    数据监听:监听DOM的改变

事件修饰符

vue中的事件修饰符:
	1.prevent:阻止默认事件(常用);原生阻止默认行为:event事件对象.preventDefault()
	2.stop:阻止事件冒泡(常用);原生阻止冒泡:event事件对象.stopPropagation()
	3.once:事件只触发一次(常用)之后就不会触发绑定的事件函数;
	4.capture:使用事件的捕获模式:意思是再事件捕获阶段就执行绑定的事件处理函数
	默认事件触发有三个阶段:
	  一,事件捕获阶段,由外向内进行事件的捕获,此阶段不会执行事件处理函数,
  二,目标阶段,事件捕获到目标元素,之后触发事件的冒泡阶段开始执行事件
	  二,事件冒泡阶段,由内向外进行事件冒泡,此阶段会指向绑定的事件处理函数,先触发内部绑定的事件
	5.self:只有event.target是当前操作的元素时才触发事件;触发事件函数执行的元素是绑定事件的元素
	6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
	  默认是:事件一被触发,先执行事件处理函数,等到事件处理函数执行完毕后,然后才会执行事件的默认行为
	7.native 组件标签绑定的事件的修饰符,添加上这个修饰符才会把内置事件绑定给组件内部的根元素

计算属性详解

计算属性:在vue中放在data中的数据都是属性,计算属性是对data中的数据进行处理
计算属性的原始写法
  computed:{
    计算属性名:{
      get(){
        return 计算属性的值
      },
      set(value){
        get中依赖的数据=value
      }
    }
  }
计算属性中的get调用时机:
  (1).页面中初次使用计算属性时会执行一次。
  (2).当计算属性依赖的数据(return返回的数据就是依赖的数据)发生改变时会被再次调用。   
计算属性中set调用时机
  (1).当计算属性被修改时,只有重写了set方法,计算属性才能被修改
  
使用计算属性获取值的方式是调用get函数,获取get函数的返回值
计算属性在第一次执行后,vue会将计算属性就缓存下来,之后使用到的同一个计算属性就不会再次去执行get获取,而是去找缓存

默认的计算属性中的值是不能被修改的

如果需要直接修改计算属性(如果不需要修改计算属性的值,那么就直接使用计算属性的简写)
那么就需要在计算属性中添加上set方法,set方法接收需要修改的值,然后将需要修改的值赋值给get函数中所依赖的数据

计算属性不能与data中的属性重名
如果计算属性不需要被修改,那么直接写计算属性的简写方式即可

简写方式:
computed:{
  计算属性名(){
    return 计算属性的值
  }
}

监听器详解

监听器监听的属性是data中的数据,或者是计算属性,或者是props中的数据
监听器有两种写法:
1。Vue监听器:

  watch:{ //完整写法:需要使用immediate,deep时,使用完整写法
    "需要监听的数据":{
      immediate:true/false(默认值) 是否初始化完毕watch后直接调用一次监听的属性所绑定的函数
      deep:true//是否开启深度监视,
      handler(新的值,旧的值){//handler回调函数名是固定写法
        ...
      }
    }
  }
 
  第一种方法简写:
    watch:{
      需要监听的数据(newValue,oldValue){
        ...
      } 
    }

2.Vue实例对象的实例方法的监听器
  完整写法
  Vue实例.$watch("监听的属性",{
    。。一些配置,
    immedate:false,
    deep:false,
    handler(newValue,oldValue){
    }
  })
  简写
  Vue实例.$watch("监听的属性",function(newValue,oldValue){
    方法体
  })

  监听的属性发生改变就会执行回调函数
  注意:Vue所管理的函数,都不能写成箭头函数,
  Vue管理的函数:methods中定义的函数,计算属性,监听器中的函数、

深度监视

  开启深度监视,开启后一旦一个对象中的某一个属性值发生变化,就会调用handler函数
  写法:
    watch: {
        // 检测对象中的某个属性的改变
        "number.a": {
          deep: true
          handler() {
            console.log("a的值发生变化");
          }
        },
        // 监听对象中所有的属性值变化,需要开启deep=true深度监视
        "number": {
          deep: true,//开启深度监视,开启后一旦一个对象中的某一个属性值发生变化,就会调用handler函数
          handler() {
            console.log("number里面的属性值发生变化");
          }
        }
      }

:key的作用与原理

  面试题:react、vue中的key有什么作用?(key的内部原理)
    key会绑定在虚拟DOM上
    1. 虚拟DOM中key的作用:
      key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 
      随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
    2.Diff算法对比规则:
    diff算法大致做法是比较old VDOM树和new VDOM树之间的差异,根据差异更新虚拟DOM树。
      (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
        ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
        ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

      (2).旧虚拟DOM中未找到与新虚拟DOM相同的key
          创建新的真实DOM,随后渲染到到页面。
                
    3. 用index作为key可能会引发的问题:
      1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
        会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

      2. 如果结构中还包含输入类的DOM:
        会产生错误DOM更新 ==> 界面有问题。

    4. 开发中如何选择key?:
        1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
        2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
          使用index作为key是没有问题的。

    5. key的作用:
      1.主要用在 Vue 的虚拟 DOM 算法,在新旧 DOM 对比时辨识 新的VDOM,相当于唯一标识ID。
      2.使 Vue 会尽可能高效的更新虚拟dom,Vue可以根据key来更精准的判断两个元素是否相同。key值相同的vue会复用已有元素而不是从头开始渲染, 
      同理,改变某一元素的key值会使该元素重新被渲染。
    
    6.如果不设置key,vue会默认的将index设置为key

Vue数据响应式

  给vue添加响应式的数据:数据一添加,页面就会发生变化
      响应式的给vue中的data中的 对象 添加属性
      Vue.set(vm.字段,key,value)==》只有这样添加的才是响应式的数据
      Vue实例对象.$set(vm.字段,key,value)==》只有这样添加的才是响应式的数据
      直接通过 Vue实例对象.对象.属性="值"这种方式添加的数据是不具有响应式的
      Vue.delete()

Vue收集表单数据

  v-model默认收集的是表单元素的value值
    但是有一些输入类的元素没有明显的value值,如单选,多选框
    
    收集表单数据:
      若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
      若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
      若:<input type="checkbox"/>
          1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
          2.配置input的value属性:
              (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
              (2)v-model的初始值是数组,那么收集的的就是value组成的数组
      备注:v-model的三个修饰符:
              lazy:失去焦点再收集数据
              number:输入字符串转为有效的数字
              trim:输入首尾空格过滤

Vue内置指令大全

    v-bind: 单向绑定解析表达式, 可简写为 :xxx
    v-model: 双向数据绑定
    v-for: 遍历数组/对象/字符串
    v-on: 绑定事件监听, 可简写为@
    v-if: 条件渲染(动态控制节点是否存存在)
    v-else: 条件渲染(动态控制节点是否存存在)
    v-show: 条件渲染 (动态控制节点是否展示)
    v-text指令:
      1.作用:向其所在的节点中渲染文本内容。
      2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
    v-html指令:
      1.作用:向指定节点中渲染包含html结构的内容。
      2.与插值语法的区别:
        (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
        (2).v-html可以识别html结构。
      3.严重注意:v-html有安全性问题!!!!
        (1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
        (2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
    v-cloak指令(没有值):
        1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
        2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
    v-once指令:
        1.v-once所在节点在初次动态渲染后,就视为静态内容了。
        2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
    v-pre指令:
        1.跳过其所在节点的编译过程。
        2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
        3.元素加上v-pre指令后,不会参与vue的编译,直接会被放到页面上进行展示

混入

  混入:https://www.cnblogs.com/dengyao-blogs/p/11589962.html

  定义混入
    在当前项目scr目录下,创建minix目录,然后传递混入的js文件,js文件中定义所有

  混入的作用:
   将多个组件中公共的部分,抽取出来,成为一个混入对象,从而简化代码,代码复用
    混入对象的数据解构与组件基本一致
    mixin混入对象的变量是不会共享的

  注意:
    如果组件中定义了与混入对象中data字段相同的属性data或者定义了与混入对象相同的methods方法,组件中的data和methods就会覆盖掉混入中的data和methods方法,否则就会进行合并
      但是混入中的生命周期函数,组件对象是覆盖不掉的,所以如果组件和混入中都定义的生命周期函数,那么这个生命周期函数就会执行两遍

    混入对象中可以写哪些内容?
      只要组件.vue中的所有的属性和方法都可以写道混入对象中
    
  局部混入:在组件中引入混入,只有引入了mixin的混入对象才可以使用,并且只有在引入了mixin混入对象的组件中才生效;

    在需要的组件中引入mixin
      import xxx from "./mixin/globalMixin"
    然后注册和使用:
      //这里注意:属性名为mixins,值为数组类型
      mixins: [myMixin], //相当于注册,然后就可以直接使用混入对象中的数据

  全局混入:全局混入我们只需要把mixin.js引入到main.js中,然后将mixin放入到Vue.mixin()方法中即可;
    全局混入更为便捷,我们将不用在子组件声明,全局混入将会影响每一个组件的实例,
    使用的时候需要小心谨慎;这样全局混入之后,我们可以直接在组件中通过this.变量/方法来调用mixin混入对象的变量/方法;

    // 引入全局的混入 main.js
      import { globalMixin } from "./mixin/globalMixin"
    全局注册混入
      Vue.mixin(globalMixin)

    然后直接可以再所有的组件中使用到混入对象中的数据,通过this.的方式,所有的数据都直接混入到所有的组件中


  mixin混入对象和Vuex的区别:
    Vuex是状态共享管理,所以Vuex中的所有变量和方法都是可以读取和更改并相互影响的;
    mixin可以定义公用的变量或方法,但是mixin中的数据是不共享的,也就是每个组件中的mixin实例都是不一样的,都是单独存在的个体,不存在相互影响的;
    mixin混入对象值为函数的同名函数选项将会进行递归合并为数组,两个函数都会执行,只不过先执行mixin中的同名函数;
    mixin混入对象值为对象的同名对象将会进行替换,都优先执行组件内的同名对象,也就是组件内的同名对象将mixin混入对象的同名对象进行覆盖;

插件

  插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
    添加全局方法或者 property。如:vue-custom-element
    添加全局资源:指令/过滤器/过渡等。如 vue-touch
    通过全局混入来添加一些组件选项。如 vue-router
    添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
    一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router

  使用插件步骤:
    1.先创建插件文件
      在src目录下创建 plugin/xxx.js 文件
      然后再js文件中,暴露一个对象,对象必须要有install方法
      plugin.js
        const pluginObj = {
            // 插件必须要有install方法,这个方法由Vue来进行调用
            install(Vue,【参数列表】) {//install默认接收参数Vue构造函数
            console.log("install", Vue);

            在install方法内部可以既然用Vue进行一些全局的操作
            定义全局过滤器
              Vue.filter("过滤器名称",function(value){

              })
            定义全局混入
              Vue.mixin({
                ....
              })
            给Vue原型上添加上一个方法或者属性
              Vue.prototype.xxxx=function(){}
              Vue.prototype.xxx="xxx"
            定义全局指令
              Vue.directive("指令名称",{

              })
          },
        }
        export default pluginObj
    2.在main.js入口文件中使用插件
      1.先引入插件
        import xxx from "./插件路径"
      2.使用插件
        Vue.use(xxx,【参数列表】)

组件的自定义事件:

  vue中的内置事件(click,keyup...)是给html元素使用的
  组件的自定义事件是给组件使用的,会绑定到子组件VueComponent对象身上
    组件实例对象.$on()=>绑定自定义事件
    组件实例对象.$emit()=>触发自定义事件
    组件实例对象.$off()=>解绑自定义事件
    组件实例对象.$once()=>触发一次自定义事件


  绑定事件:
  方式1:
    在父组件中,引入了子组件
      <子组件 @自定义事件名称="父组件中的对应的事件处理函数"></子组件>
        注意:这个自定义事件是绑定在子组件对象上
    在子组件中,触发自定义事件,此时就可以向父组件传递数据
      通过this.$emit("自定义事件名称",[参数列表]) ===>从而去父组件中触发自定义事件绑定的函数

  方式2: 这种方式更加灵活
    父组件中
     <子组件 ref="xxx"></子组件> 通过ref来获取到子组件实例对象this.$refs.xxx=>获取的是子组件的实例对象
     绑定事件:
      子组件实例对象this.$refs.xxx.$on("自定义事件名称",父组件的事件处理函数) 

    子组件中:
      this.$emit(自定义事件名称,"数据")

      注意点:
        如果事件处理函数是普通函数,那么谁触发自定义事件,那么父组件的事件处理函数中的this指向的就是谁
        如果事件处理函数是箭头函数,那么事件处理函数中this就是父组件中的this

        如果给组件标签直接绑定vue内置事件,那么vue会将内置事件,当成自定义事件
        <组件 @click="事件处理函数"></组件> ==>组件会吧click当成自定义事件
        <组件 @click.native="事件处理函数"></组件> ==>此时才会把click内置事件绑定给组件内部的根元素


  解绑事件:
    解绑组件上的一个自定义事件
      组件实例对象.$off("自定义事件名称")
    解绑组件上的多个自定义事件
      组件实例对象.$off(["自定义事件名称1","自定义事件名称2",...])
    解绑组件上的所有的自定义事件
      组件实例对象.$off()

全局事件总线:可以实现任意组件间的通信34

  可以设置为全局事件总线的对象需要满足:
    1、能使全局所有的组件都能够访问的到
    2、这个对象具有$emit() $on() $off() 方法

  满足这个两个要求的只有是Vue对象

  全局事件组件可以实现任意两个组件之间进行数据的交互

  创建全局实例对象:方式1
    在main.js中
      import Vue from "vue"
      Vue.prototype.$EventBus=new Vue()

  创建全局实例对象:方式2
    在main.js中
      new Vue({
        render:h=>h(App),
        绑定方式2:
        beforeCreate(){
          Vue.prototype.$EventBus=this
        }
      })

  使用全局事件总线
    组件2---》传递数据给--》组件1
    组件1:绑定事件总线
      mounted(){
        this.$EventBus.$on("自定义事件名称",(【参数】)=>{
          <!-- 回调函数 -->
        })
      },
      beforeDestroy(){
        this.$EventBus.$off(“自定义事件名称”)解绑
      }
    组件2:触发自定义事件
      methods:{
        xxxClick(){
          this.$EventBus.$emit("自定义事件名称",【参数数据】)
        }
      }

消息订阅与发布

  也可以实现任意组件间的通信

  1.订阅消息:需要消息名 --》需要数据的组件进行订阅消息
  2.发布消息:传递消息内容 --》传递数据的组件进行发布消息

  npm i pubsub-js --> 消息订阅与发布所需库

  使用步骤
    1.npm i pubsub-js -S
    2.在需要使用消息订阅与发布的地方使用
      订阅消息的组件(接收数据的组件)
        import pubsub from "pubsub-js"
        mounted(){
          const 唯一id=pubsub.subscribe("消息名",(【自定义参数名称,数据】)=>{
            //回调函数:回调函数执行时机:是在发布消息后,进行自动回调
          })
          this.唯一id=唯一id
        },
        beforeDestory(){
          <!-- 组件销毁之前,取消订阅 根据id进行销毁 -->
          pubsub.unsubscribe(this.唯一id)
        }

      发布消息的组件(传递数据的组件)
        import pubsub from "pubsub-js"
        methods:{
          xxxClick(){
            <!-- 只要触发publish,就会回调subscribe中的回调函数 -->
            pubsub.publish("消息名",【数据】)
          }
        }

this.$nextTick():调用时机,当修改了data数据,等待vue重新把虚拟DOM重新挂载到页面之后,再调用

  this.$nextTick(回调函数)
  $nextTick所绑定的回调函数会在模板渲染完毕后再执行

  Vue默认是不会一边修改data中的模型数据一边去渲染模板的,这样就效率太差了,
  Vue会等待所有的代码执行完毕都然后再去渲染模板(除异步操作外)

  所以有些时候,当我们立刻把模型数据修改后,页面也不一定会直接就跟着渲染了,而是等待代码都在执行完毕后,然后开始渲染模板
  但是有些数据操作,是要等待模板重新渲染完毕后,操作新的dom,这样就体现出vm.$nextTick(回调函数)价值链
 
   this.$nextTick(回调函数)会等待页面重新渲染完毕后,然后再来调用回调函数

js运行机制:单线程

  同步程序执行完毕后再去执行异步程序
  js是单线程的,一个任务完成之后才会执行另外一个任务,
    '''js
      for (let i = 0; i < 2000; i++) {
        console.log("a");//先输出2000遍
      }
      setTimeout((params) => {
        console.log("bbb");//输出c后,等待5s后执行,此时for循环导致计时器不准时了
      }, 5000)
      console.log("c");//输出两千遍a之后执行c
    '''

process.nextTick(回调函数)与setImmediate(回调函数)

  process.nextTick(回调函数)
    process.nextTick(回调函数)需要借助于node环境运行

    process.nextTick(回调函数)执行时机:同步代码执行完毕后,异步代码执行之前,process.nextTick(回调函数)绑定的回调函数会自动执行
    
  setImmediate(回调函数)执行时机:第一个异步代码执行完毕后执行

js事件循环

  运行栈:同步代码直接放到运行栈中执行
  任务队列:异步代码到了阻塞时间后,就会被放到任务队列中执行
  事件循环:js引擎不断检测任务队列中,是否有任务和运行栈中是否还有待执行的任务,如果没有任务,就去任务队列中将待执行的任务放到主线程的执行栈中执行
  任务:同步任务,异步任务,异步任务分为微任务,宏任务

 # 宏任务与微任务
  异步程序分为 宏任务与微任务
  宏任务:1、计时器 2、ajax 3、读取文件
  微任务:Promise对象.then()方法

js代码执行顺序:
同步程序->process.nextTick()-> 微任务异步程序->宏任务异步程序(第一个异步程序)-> setImmediate()->其余的宏异步程序

Promise对象 es6

  const promiseObj = new Promise((resolve,reject)=>{
    if(){
      resolve(data)
    }else{
      reject(err)
    }
    //内部只能一次调用 resolve(data) reject(err)其中的一个
  })
  <!-- 只有再Promise内部调用resolve()才会执行then中的第一个回调函数
       只有再Promise内部调用reject()才会执行then第二个回调函数
     -->
  promise.then(
    (data)=>{
    data是resolve(data)中的data
    },
    (err)=>{
      err是reject(err)中的err
    },
  )
  或者
    promise.then((data)=>{
      data是resolve(data)中的data
      }).catch(//失败的回调
      (err)=>{
        err是reject(err)中的err
      })

async与await

 async用于修饰函数
 async修饰的函数的返回值是一个Promise对象
 await用于修饰Promise对象,将Promise对象中resolve和reject的参数值取出

Vue动画

  想要给谁加动画效果,就给谁包裹transition标签
  transition 默认会动态给被包裹元素添class样式:
      v-enter:元素进入动画之前,元素显示/插入前的效果,元素初始的位置 例如:transform: translateX(-200%);
      v-active-enter:元素执行动画的过程中,元素显示/插入的过渡时间和函数,初始前到最终的一个时间段 例如:transition: all 0.3s ease;
      v-enter-to:元素动画执行完毕,元素元素显示/插入后的效果,元素最终的位置 例如:transform: translateX(-100%); 
      
      v-leave:元素动画离开之前,元素隐藏/移除前的效果,例如:transform: translateX(0);
      v-active-leave:元素动画离开的过程中,元素隐藏/移除的过渡时间和函数,例如:transition: all 0.3s ease;
      v-leave-to:元素动画执行完毕。元素隐藏/移除后的效果,例如:transform: translateX(100%);


    v-enter-to与 v-leave 这个两个样式规定动画开始时元素的位置状态
    v-leave-to与v-enter 这个两个样式规定动画结束后元素的位置状态
    v-active-enter与 v-active-leave 这个两个样式代表执行动画的时间段 一般在这个阶段设置元素的过渡效果

  给transition添加上name="xxx" 那么默认的样式就得是.xxx-enter-active name改变v
  transition添加上:appear='true' 意思是组件已挂载,就会执行动画

  transition标签不会渲染到页面上
  '''js
  <transition>
    <div class="box" v-show="isShow"></div>
  </transition>
  '''
  
  transition只能包裹一个根元素,只能给一个元素添加动画

  给多个元素添加动画
  需要使用transition-group包裹
  并且transition-group包裹的元素需要绑定key

Vue集成第三方动画库

  animate.css官网链接:https://animate.style/
  安装:
    npm install animate.css --save

  在项目种引入
    import "animate.css"

  使用animate.css
    给transition标签添加上固定的name属性

    按需求添加上
      enter-active-class="官网上自己找动画值" =》进入动画的过程
      leave-acitve-class="官网上自己找动画值" =》离开动画的过程

    <transition name="animate__animated animate__bounc" enter-active-class="" leave-acitve-class=""> 

插槽

  让父组件可以向子组件指定位置插入指定的模板结果,也是一种组件间的通信方式
  定义一个插槽,插槽内可以包含任何模板代码

  ## 默认插槽:
  
    在组件内部:
    <div>
      <slot>[xxxx默认的摹本内容]</slot>  如果这个插槽中没有传递任何内容,就会展示默认的模板内容
    </div>
    <组件标签>
      xxxxx //此时的xxxx就填充到组件内部的<slot></slot>上,进行替换,就相当于在组件中挖了个坑
    </组件标签>

  ## 具名插槽:具有名称的插槽,用于却分不同的插槽
  定义具名插槽:
    <slot name="名称"><slot/>
  老板使用插槽:
    给填充的到插槽的标签添加上对应的slot="名称" 此时就会填充到对应的插槽上
    <a slot="名称"> //这种填充方式已经过时了
  
  新版使用插槽:template不会渲染成标签
  <template v-slot:名称>//v-slot:名称 只能使用在template标签上
    填充模板内容
  </template>

 ## 作用域插槽
    数据在定义插槽的地方,根据数据生成的结构由使用插槽的人自己定义

    作用域插槽用于:数据只有一份,存储在定义插槽的组件中时,此时就可以使用作用域插槽

    作用域插槽:携带数据的插槽
      <slot :data="data" :sdada="sasa"></slot> 通过slot绑定数据的方式向外界传递插槽的数据
      数据:
      data(){
        return {
          data:["aaa","bbb","ccc"]
        }
      }

    使用作用域插槽
      <template scope="自定义接收数据的名称">
        {{自定义接收数据的名称.slot指定的数据字段}} ==>得到作用域插槽种的数据
        然后就可以进行数据渲染了
      </template>

Vuex

  Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式的插件
  Vuex:把需要多个组件共享的变量(状态)全部存储在一个对象里面
    这个共享的变量是响应式的,一个变,全局变
    Vuex一般管理什么样的状态?
      一般管理用户的状态
      多个组件需要使用到用户的头像
      多个组件需要共享的东西,都会放到Vuex中
  
  安装Vuex: npm install vuex -S
  在src目录下创建store文件夹,里面创建index.js文件用于注册vuex

  使用过程
    在index.js文件中,
      1.安装插件
        Vue.use(Vuex) ==>在底层执行了Vuex.install
      2,创建Vuex对象----在src下新建一个store文件夹/index.js
        const store =new Vuex.Store({

        })
      3.导出Vuex对象
        export default store

      4.在入口main.js文件中注册vuex
      import store from "store/index.js"
        new Vue({
          store ==>给vue的原型增加了一个属性 vue.prototype.$store
        })

  Vuex与Vue.prototype全局对象 存放全局状态的区别:
    1.Vuex store存放的状态是响应式的。当 Vue 组件从中检索状态时,如果 store 的状态发生变化,它们将响应式有效地更新。
    2.您不能直接改变store的状态。改变 store 状态的唯一方法是显式提交 mutations。这确保了每个状态更改都会留下可跟踪的记录,并启用帮助我们更好地了解我们的应用程序的工具。
        确保每一次修改store的状态都要经过mutations
  
  vuex的五大核心概念:
    1.State:在 store 中的 state 对象,可以理解为 Vue 实例中的 data 对象,它用来保存最基本的数据。
          State 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的 State 中进行存储
          state 单一状态树:在一个项目中只需要创建一个Store对象,对整个项目的状态信息进行管理,
          state里面存放的数据都是响应式的

   2. Mutation:定义方法用于操作 state中 的数据。
      只能通过 mutation 变更 Store 数据,不可以直接操作 Store 中的数据。
      通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。
      
      Vuex的store状态的更新的唯一方式:提交Mutation

      Mutation对象里面需要写同步函数,不能有异步的操作,如果有异步的操作,devtools工具监听不到state的变化
      1.Mutation包含两部分
        1.事件类型
        2.回调函数
          事件类型(参数){
            回调函数
          }
      2.Mutation的使用
        1.在Mutation中定义方法:
          Mutation:{
            //接收一个参数
            事件类型(state,【接收参数】){
              操作state状态变量的代码
            },
            //接收多个参数
            方法(state,payload){
              payload.参数==》取出参数
            }
          }

        2. 提交Mutation
          传递一个参数给Mutation中的方法 :传递给Mutation的参数称为:payload负载
          1.this.$store.commit("mutations中定义的事件类型",【传递参数】)
            
          传递多个参数给Mutation中的方法
          2.this.$store.commit("mutations中定义的事件类型",{键1:值1,...})

      3.通过Mutation修改state中的状态信息是响应式的 mutations中的方法必须是同步方法,只有是同步方法,dectools插件才能捕捉到数据的变化
          但是在vue-cli2中,通过Mutation向state中添加数据,删除数据不是响应式的
            就不能通过简单的修改数据,删除数据的方式进行操作
            就需要使用
              state.对象变量["新的属性"]="值" 这种方式不是响应式的
                Vue.$set(要修改的对象,key值,"修改的值")可以添加元素,也可以修改元素
              delete state.对象变量.属性 这种方式删除对象的属性也不是响应式的
                Vue.$delete(要删除的对象,删除的key值)是删除元素
            
          在vue-cli3中是响应式的
      4.类型常量

    3.Getter:Getter 可以对 Store 中已有的数据进行加工处理,然后返回给state 类似 Vue 的计算属性。
              Store 中数据发生变化,Getter 的数据也会跟着变化
      在getters中定义方法:
        方法(state,getters){参数:state是存储状态的对象,getters:是当前的getters对象,可以根据该对象,使用其他的方法返回值
              return 处理state的代码,以满足需求
        }
      使用getters中定义的方法:
        this.$store.getters.方法 不需要加()就会得到getter中方法的返回值

    4.Action:Action 用于处理异步任务。可以提交mutation
            如果通过异步操作变更数据,必须通过 Action,而不能使用 Mutation,
            但是在 Action 中还是要通过触发Mutation 的方式间接变更数据。
            在actions中编写异步操作,通过context.commit("Mutation中的函数名称")触发执行在Mutation
      1.使用:
        actions: {
            aupdateInfo(context,【参数】) {
              context上下文对象:context.commit("mutations中的函数") 用于将异步操作中调用mutations中的方法进行处理
              setTimeout(() => {
                context.commit("Mutation中的函数名称",传递参数给mutation)
              }, 2000)
            },
          },
        2.页面中通过this.$store.dispath(actions中的函数名称,【参数】)触发执行actions中的异步代码
          参数可以是一个变量,也可以是一个对象
    5.Module:
        由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
        当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
          为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
          每个模块拥有自己的 state、mutation、action、getter

      1.使用:
        moudules:{
          a模块:{
            state:{},
            getters:{},
            mutations:{},
            actions:{},
            modules:{}
          },
          b模块:{
            state:{},
            getters:{
              方法(state,【getter,rootState】){
                参数:state是当前模块中的state,
                      getter是当前getters对象 可以通过getter.方法 来调用其他的方法的返回值数据
                      rootState:是根State

                return 当前方法的返回值
                return function(params){
                  return 当前方法的返回值+params
                }
              }
            },
            mutations:{

            },
            actions:{
              方法(context){//context暂时理解为vuex store对象
                context类似于模块的对象,里面有好多属性和方法
                异步操作
                context.commit("当前模块中的mutations中的方法")
              }
            },
            modules:{}
          }
          ...
        }

      2.使用modules中的不同模块的内容:
        2.1 访问modules中的state的内容
          this.$store.state.模块名.属性
        2.2 提交Mutation(执行Mutaion中的方法)
          this.$store.commit("Mutation定义的方法")
		2.3 执行Action中的方法
			this.$store.dispatch("action中的异步方法",参数)
		
    原理是:vuex会将moudules中的定义的state、mutation、action、getter一个模块以对象的形式,
      统一添加到外面的state,mutation、action、getter中

vuex

  目录结构:
    └── store
      ├── index.js          # 我们组装模块并导出 store 的地方
      ├── actions.js        # 根级别的 action
      ├── mutations.js      # 根级别的 mutation
      ├── getters.js
      └── modules
          ├── cart.js       # 购物车模块
          └── products.js   # 产品模块

  Vuex五大核心:
    1.state:在 store 中的 state 对象,可以理解为 Vue 实例中的 data 对象,它用来保存最基本的数据。
    2.Mutations:定义方法用于操作 state中 的数据。 Vuex的store状态的更新的唯一方式:提交Mutation mutation只能处理同步操作
    3.Getters:Getters 可以对 Store 中已有的数据进行加工处理,然后返回给state 类似 Vue 的计算属性。Store 中数据发生变化,Getter 的数据也会跟着变化
    4.Actions:Actions 用于处理异步任务。可以提交mutation
    5.Module 每个模块拥有自己的 state、mutation、action、getter

    ## 其中五大核心都是对象

  Vuex的使用步骤:
    1.安装 vuex:npm i vuex -S
    2.初始化Vuex: 在store/index.js中
      improt Vue from "vue"
      import Vuex from "vuex"
      import acrions from "./actions"
      import getters from "./getters"
      import mutations from "./mutations"

      Vue.use(Vuex)//此时vue实例上就有了$store用于管理vuex

      const state = {
        ...数据
      }
      Vuex.Store({
        state,
        acrions,
        getters,
        mutations,
      })
    3.在main.js中进行注册
      import store from "./store/index"
      new Vue({
        render: h => h(App), //h是一个函数createElement,将App这个组件替换首页面中的#app所控制的区域
        beforeCreate() {
          //  创建全局事件总线 方式1:使用当前的Vue作为全局事件总线
          Vue.prototype.$EventBus = this
        },
        store,//此时就可以通过Vue实例对象.$store访问到vuex
      }).$mount('#app')


  ## Vuex五大核心的基本使用
    1.state:
      state定义在index.js中,用于存储数据
      const state={
        数据
      }
      在外界访问state中的数据
        this.$store.state.数据

    2.actions:
      外界通过Vue实例.$store.dispatch("actions中的方法",参数/{参数})
      export default{
        方法(context,value){
          if(){}逻辑操作,延时操作,异步操作都写在actions中
          context上下文对象.commit("mutations中的方法",参数/{参数}) //在actions中提交mutation

          context上下文对象中包含:
            context.dispatch()可以调用actions中的方法
            context.commit()可以调用mutations中的方法
            context.state可以访问state
        }
      }

    3.mutations
       外界通过Vue实例.$store.commit("mutaion中定义的方法",{参数}/参数)、
        在mutaion内部:
          export default {
            方法(state,payload){
              如果通过commit传递过来的参数是一个对象那么payload的结构就是:{参数:数据} 通过payload.参数 访问到数据
              如果通过commit传递过来的参数是一个单纯的数据值,那么payload就是单纯的这个数据
              对state进行修改、增加、删除等等。
              mutation中只执行很单纯的操作,就是负责操作state
            }
          }

    4.getters
      Getters可以对 state 中已有的数据进行加工处理, 类似 Vue 的计算属性。Store 中数据发生变化,Getter 的数据也会跟着变化
      在getters中定义方法:
        方法(state,getters){
          参数:state是存储状态的对象,getters:是当前的getters对象,可以根据该对象,使用其他的方法返回值
          return 返回加工后的state数据
        }
      使用getters中定义的方法:
        this.$store.getters.方法 不需要加()就会得到getter中方法的返回值

  ## Vuex模块化:modules
    5.Module:
        由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
        当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
          为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
          每个模块拥有自己的 state、mutation、action、getter,
          每个分类都属于一个模块

      Vuex模块后的目录结构:
        └── store
            ├── index.js          # 我们组装模块并导出 store 的地方
            ├── actions.js        # 根级别的 action
            ├── mutations.js      # 根级别的 mutation
            └── modules
                ├── cart.js       # 购物车模块
                └── products.js   # 产品模块

      export default new Vuex.Store({
        state,
        actions,
        getters,
        mutations,
        // 下面是模块化
        modules: {
          模块名:"moduleA",
          moduleB
        }
      })

    ### 使用模块化后,访问不同模块中的内容:
    普通方式访问(不借助辅助函数):
      1.访问模块中的state:
        this.$store.state.模块名.xxx
      2.访问actions,getters,mutaions
        this.$store.getters["模块名/getters中的方法"];
        this.$store.commit("模块名名/Mutation定义的方法",参数)
        this.$store.dispatch("模块名名/actions定义的方法",参数)

        但是在actions文件中,通过context.commit("直接使用mutations中的方法")

    模块化结合vuex辅助函数:
      方式一:  
        ...mapXxxx(["模块名1","模块名2"....]) ==》取出不同的模块
        通过 模块名.方法 进行访问对应的方法

      方式二:
        ...mapXxxx("模块名",["模块中对应的方法名1",....]) ==》取出对应的模块中的方法
        ...mapXxxx("模块名",{
          别名:"模块中对应的方法名1",
        })
        通过 模块中对应的方法名1 直接可以访问到

        注意:使用方式二的前提是:需要给模块开启命名空间:namespaced:true
              模块名需要提前指定

            模块A:
              export default {
                namespaced: true,
                actions:{},
                state:{},
                ...
              }

        给...mapActions和...mapMutation指定的方法传递参数:只能通过绑定事件时,通过onXxxx="方法名(传递参数)"


      1.使用:
        moudules:{
          a模块:{
            state:{},
            getters:{},
            mutations:{},
            actions:{},
            modules:{}
          },
          b模块:{
            state:{},
            getters:{
            
            },
            mutations:{

            },
            actions:{
            },
            modules:{}
          }
          ...
        }

      2.使用modules中的不同模块的内容:
        2.1 访问modules中的state的内容
          this.$store.state.模块名.属性
        2.2 提交Mutation(执行Mutaion中的方法)
          this.$store.commit("Mutation定义的方法")
		2.3 执行Action中的方法
			this.$store.dispatch("action中的异步方法",参数)
		
    原理是:vuex会将moudules中的定义的state、mutation、action、getter一个模块以对象的形式,
      统一添加到外面的state,mutation、action、getter中

  ## vuex辅助函数
    1.mapState:将state数据映射到组件的计算属性中
      在组件中访问vuex中存储的状态(数据)访问比较麻烦,this.$store.state.xxx

      通过mapState可以快速的访问到state中的数据
      使用步骤:
        在组件文件中:
        import {mapState} from "vuex"

        <!-- mapState第一种写法(对象写法) -->
        computed(){
          <!-- 将state混入到计算属性中 -->
          ...mapState({
            别名:"state中需要被暴漏的数据字段", //在模板中通过别名就可以直接访问到state中的数据
            别名:"state中需要被暴漏的数据字段",
            。。。。
          })
        }

        <!--mapState第二种写法(数组写法) -->
         computed(){
          <!-- 将state混入到计算属性中 -->
          ...mapState(["state的字段1","state的字段2",...])//数组写法:1.state字段要在state中有定义过,2.在模板中访问state,也必须使用state的字段1
        }

        
    2.mapGetters:将getters中的方法映射到计算属性中
      在组件中访问vuex中的getters时,访问代码比较长,this.$store.getters.xxx

      使用mapGetters可以快速访问getters
      使用步骤:
        import {mapGetters} from "vuex"

        computed(){
          <!-- 将getters混入到计算属性中 -->
          ...mapState({
            别名:"getters中的方法", //在模板中通过别名就可以直接访问到getters的数据
            别名:"getters中的方法",
            。。。。
          })

          <!-- 写法二:数组写法 -->
          ...mapState(["getters方法名1",...])//getters方法名1必须在getters中定义过,在模板中使用getters方法名1进行访问
        }

      
    3.mapMutations:将mutations方法映射到methods中
      在组件中访问vuex中的mutaions中的方法时,访问代码比较长,this.$store.commit("mutaions中的方法",数据)

      使用mapMutations可以快速访问mutaions
      使用步骤:
        import {mapMutations} from "vuex"

        methods:{
          <!-- 将mutations混入到methods中 -->
          ...mapMutations({
            别名:"mutations中的方法",//但是这种方式没有好的办法传递参数给mutations中的方法,只有一个方法那就是通过事件绑定的时候,@click="别名(传递参数给mutaions中的方法)"
          })

          <!-- 写法二:数组写法 -->
          ...mapMutations(["mutations方法名1",...])//mutations方法名1必须在mutations中定义过,在模板中使用mutations方法名1进行调用方法,传递参数通过onCick="mutations方法名1(参数)"
        }

    4.mapActions:将actions中的方法映射到methods中
      在组件中访问vuex中的actions中的方法时,访问代码比较长,this.$store.dispatch("actions中的方法",数据)

      使用mapActions可以快速访问actions
      使用步骤:
        import {mapActions} from "vuex"

        methods:{
          <!-- 将actions混入到methods中 -->
          ...mapActions({
            别名:"actions中的方法",//但是这种方式没有好的办法传递参数给actions中的方法,只有一个方法那就是通过事件绑定的时候,@click="别名(传递参数给actions中的方法)"
          })

          <!-- 写法二:数组写法 -->
          ...mapActions(["actions方法名1",...])//actions方法名1必须在actions中定义过,在模板中使用actions方法名1进行调用方法,传递参数通过onCick="actions方法名1(参数)"
        }

生成唯一字符串:

  import {nanoid} from "nanoid"
  
  使用:const str=nanoid() 生成唯一字符串

rander函数

  const vm = new Vue({
    render: h => h(App), //h是一个函数createElement,将App这个组件替换首页面中的#app所控制的区域
  }).$mount('#app')

  render是一个函数,且必须要有返回值,vue负责调用
    render(createElement){//createElement也是一个函数,接收一个模板为参数,创建模块渲染到页面
      return createElement("h1")
    } 
    简写:
      render:h=>h(模板组件)

  vue由:vue核心+模板解析器组成(模板解析器负责解析template标签)
    模板解析器体积过大
  
  vue文件分为:精简版和完整版
  默认使用的是精简版的vue(vue.runtime.xxx.js)。体积小,但是无法解析模板,需要借助render函数或者模板解析器,但是模板解析器vue精简版中没有
  完成的vue(vue.js)中有模板解析器,直接可以对template模板进行解析,但是体积过大

vue配置

  在根目录下;创建vue.config.js

  详细配置:https://cli.vuejs.org/zh/config/#vue-config-js

  关闭eslint语法检查:lintOnSave: false

路由

  路由:为了实现单页面应用(SPA应用)index.html,一个路由就是一组映射关系:key是路径,value是组件
  SPA应用:整个应用只有一个完整的页面,点击页面导航,不会重新刷新页面,只会进行组件间的跳转,实现局部刷新,数据需要进行ajax进行请求
  前端路由:一个路由对应对应一个组件,
  后端路由:一个路由对应一个响应
    
  vue-router 是vue的一个插件库

  安装:
    npm i vue-router -S

  引入:
    在src/router/index.js中
      improt VueRouter from "vue-router"
      import Vue from "vue"
      <!-- 引入路由展示的指定的组件 -->
      import ComA from "../components/ComA"

      安装
      Vue.use(VueRouter) //此时就可以在vue中配置router

      创建对象
      const router = new VueRouter({
        routes:[
          <!-- 路由是一组{key:value} -->
          {
            path:"/aaaa",
            component:ComA //在访问 当前/aaa路径时,就展示omA组件
          }
        ]
      })

      <!-- 导出 -->
      export default router

  在main.js中进行引入
    improt router "router/index.js"
    
    const vm = new Vue({
      render: h => h(App), //h是一个函数createElement,将App这个组件替换首页面中的#app所控制的区域
      router
    }).$mount('#app')


    组件提供的内置组件
     1. <router-link to="/路径" tag="a"></router-link> 实现路由跳转
        to="/路径" ==》指定路径跳转到哪 
        tag="a" ==> tag指定router-link渲染成什么标签 默认是标签

        当某个路由处于活跃(当前正在访问的路由时),router-link会动态的增加一个active-class类样式
        当某个路由切换走时,这个路由对应的组件就会被销毁
        当访问某个路由时,这个路由对应的组件就会被挂载
          路由组件实例对象会有:$router与$route两个属性
          $router:当前的整个路由器对象,整个应用只有一个$router
          $route: 当前路由的配置对象(里面有路由参数等信息),每个路由组件都有属于自己的$route

     2.<router-view></router-view> 给当前访问的路由的对应的组件,占位置用的,(指定路由组件呈现的位置,实现路由组件的切换)


    一般组件与路由组件:
      一般组件不受路由的管理,需要手动引入组件到页面,然后写组件标签
      路由组件受路由管理的组件,不需要手动将组件引入到页面中,路由组件是根据前端路由的路由来确定渲染组件的

      一般组件放在:components文件夹中
      路由组件放在:pages文件夹中

    
  ## 路由嵌套
    1.子路由编写规则
     const router = new VueRouter({
        routes:[
          {
            path:"/aaaa",
            component:ComA //在访问 当前/aaa路径时,就展示omA组件
            children:[//path:"/aaaa"子路由存放位置
              {
                path:"bbb"//子路由路径前不要加/
                component:ComASon
              }
            ]
          }
        ]
      })
    2.访问规则
      <router-link to="/父路由路径/子路由" tag="a"></router-link> 实现路由跳转
    
  ## 路由传参
    1.query参数: /home?name=XXX&age=YYY
      携带参数方式一:<router-link to="/父路由路径/子路由?name=XXX&age=YYY" tag="a"></router-link> 实现路由跳转
      获取参数方式一:this.$route.query==>{name:XXX,age:YYY}

      携带参数方式二:<router-link :to="{path:'/父路由路径/子路由',qurey:{name:XXX,age:YYY}}" tag="a"></router-link> 实现路由跳转
      获取参数方式一:this.$route.query==>{name:XXX,age:YYY}

    2.params参数:/home/id值/name值
      携带参数方式一:<router-link to="/父路由路径/子路由/id值/name值" tag="a"></router-link> 实现路由跳转
                      const router = new VueRouter({
                        routes:[
                          {
                            name:"自定义路由名称"
                            path:"/aaaa/:id/:name",    //配置params参数
                            component:ComA      //在访问 当前/aaa路径时,就展示omA组件
                            children:[          //path:"/aaaa"子路由存放位置
                              {
                                path:"bbb"      //子路由路径前不要加/
                                component:ComASon
                              }
                            ]
                          }
                        ]
                      })
      获取参数方式一:this.$route.params==>  {id:XXX,name:YYY}            

      携带参数方式二:<router-link :to="{name:'命名路由name',params:{name:XXX,age:YYY}}" tag="a"></router-link> 实现路由跳转
      获取参数方式一:this.$route.params==>  {id:XXX,name:YYY}    


  ## 获取参数的简便方式:porps配置
    路由的porps配置:那个路由需要简化接收参数的方式,就给那个路由组件进行props配置

    在route/index.js中:
      const router = new VueRouter({
          routes:[
            {
              name:"自定义路由名称"       //命名路由
              path:"/aaaa/:name/:age",
              component:ComA            //在访问 当前/aaa路径时,就展示omA组件
              props:{a:1,b:"hello"}     //props第一种配置:对象形式,该对象的种的所有的key-value都会以props的形式传递给当前路由组件ComA 
              props:true                //props第二种配置:布尔形式,如果props:true,那么就会把该路由组件接收的所有的 params参数,以props形式传递给当前路由组件ComA 
              props:($route)=> {
                return {name:$route.query.name,age:$route.query.age}
                return {name:$route.params.name,age:$route.params.age}
              }                          //props第三种配置: 函数形式,,函数会接受到一个参数:$route对象,对象中具有params参数对象和query参数对象,然后进过一些操作将参数数据传递props
                                            函数返回值必须是{}对象,对象中的key-value都会以props的形式传递给当前路由组件ComA 
            }
          ]
        })

    在对应要接收路由参数的路由组件ComA中
      接收props第一种配置路由参数:props:["a","b"] 
      接收props第二,三种配置路由参数:props:["name","age"] 
    

  ## 命名路由:给路由起名称,可以简化路由跳转
    const router = new VueRouter({
        routes:[
          {
            name:"自定义路由名称"       //命名路由
            path:"/aaaa",
            component:ComA            //在访问 当前/aaa路径时,就展示omA组件
            children:[                //path:"/aaaa"子路由存放位置
              {
                path:"bbb"            //子路由路径前不要加/
                component:ComASon
              }
            ]
          }
        ]
      })
    设置命名路由后可以通过name进行切换
    <router-link :to="{name:'路由的name',qurey:{name:XXX,age:YYY}}" tag="a"></router-link> 实现路由跳转

  ## 路由跳转:两种方式:push,replace
    $router.push({path:'home'});本质是向history栈中添加一个路由,在我们看来是切换路由,但本质是在添加一个history记录
    $router.replace({path:'home'});替换上一条路由,没有历史记录,点击返回,会跳转到上上一个页面

    <router-link :to="{name:'路由的name',qurey:{name:XXX,age:YYY}}" tag="a"></router-link> 实现路由跳转:默认的跳转方式是:push
    <router-link :replace="true" :to="{name:'路由的name',qurey:{name:XXX,age:YYY}}" tag="a"></router-link> :replace="true"开启replace跳转模式

  ## 编程式导航: 不借助router-link
    $router.push({name:"命名路由"/path:"路由路径",query/params:{参数}});本质是向history栈中添加一个路由,在我们看来是切换路由,但本质是在添加一个history记录

    $router.replace({name:"命名路由"/path:"路由路径",query/params:{参数}});替换上一条路由,没有历史记录,点击返回,会跳转到上上一个页面

    $router.go(n) n为正数:表示前进n,n为负数:表示回退n
    $router.forward() 前进1
    $router.back() 回退1

  ## 路由缓存
    缓存指定的一个组件
    <keep-alive include="指定要缓存的组件(写组件的name)">
      <router-view></router-view> //指定路由组件呈现的位置,实现路由组件的切换
    </keep-alive>

    缓存多个指定的路由
     <keep-alive include="[指定要缓存的组件1(写组件的name),指定要缓存的组件2(写组件的name),...]">
      <router-view></router-view> //指定路由组件呈现的位置,实现路由组件的切换
    </keep-alive>

    缓存所有的组件
    <keep-alive>
      <router-view></router-view> //指定路由组件呈现的位置,实现路由组件的切换
    </keep-alive>

  ## 路由懒加载
    路由的懒加载:用到时,再加载;
    默认情况下,所有的代码最终都会打包成一个js文件,
    将不同的路由打包成不同的js文件,当路由被访问到时,就加载路由对应的js文件=》路由懒加载
    默认:页面会将所有的组件及资源都加载到main.js入口文件中,统一进行加载,
      这样就导致刚进页面时就会发生卡顿,缓慢,
      就像:在饭店点了一百个菜,饭店是等到菜全部做好后,统一将100个菜一起上上来

    路由懒加载解决方法:路由懒加载,需要啥路由就加载那个路由,它多把每个路由单独的打包成一个js文件,访问到哪个路由组件,就加载这个js文件
      在路由与组件绑定的时候,设置为:就可以实现路由的懒加载
      new VueRouter({
        routes:[
          {
            path:"/",
            //component:Home
            component:()=>import ("对应的组件的路劲")
          }
        ]
      })
    
    import ('../view/Home.vue') //路由懒加载
    但是路由懒加载还是有一个问题:这样会导致js文件过多,100道菜太多了,桌子放不下,
    
    视图组件一般要与路由进行绑定

    解决方法:就是将相同的类型的菜,放到一起,10个菜一组。。。
    组件按组分块的加载方式:多个js文件打包成一个js文件,多个组件打包一起

      有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk (opens new window),一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。
      const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
      const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
      const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

      new VueRouter({
        routes:[
          {
            path:"/",
            //component:Home
            component:()=>Foo
          }
        ]
      })

  ## 路由组件独有的生命周期函数
    activated(){} 调用时机:路由未激活->路由激活 时,调用
    deactivated(){} 调用时机:路由激活->路由未激活 时,调用
    
    路由激活:正访问的路由,就是被激活的路由
    路由未激活:正访问的路由,然后切走了,就是未被激活的路由


  ## 路由守卫
    路由守卫钩子函数一般有接收三个参数:
      to:跳转到哪个路由对象(这个路由对象中有一部分的数据是routes中配置的)
      from:来自于哪个路由对象
      next:管理是否允许路由发送跳转 
        next()表示允许发生路由跳转
        next(false)或 不写next:表示不允许发生路由跳转

    const router = new VueRouter({
      routes:[
          {
            name:"命名参数"
            path: "/home",
            component: HomeComp
            meta:{    //可以存放当前路由携带的说明信息 可以通过to,from等对象访问到
              isAuth: true,   //是否需要进行权限验证
              title:"首页"
            }
          },
          {
            path: "/about",
            component: () => import("../pages/About.vue")
          }
      ]
    })

  ###  全局前置路由守卫:每一次切换路由(每一个路由切换)之前都会调用beforeEach所绑定的回调函数,还有路由初始化时也调用
    router.beforeEach((to, from, next)=>{ //一般再前置路由守卫中进行用户权限的验证
      if(to.meta.isAuth){//是否进行权限认证
        if(to.fullpath==="/xxx"){
          next()
        }
      }
    })

  ### 全局后置路由守卫:每一次切换路由(每一个路由切换)之后都会调用afterEach所绑定的回调函数,还有路由初始化时也调用
   router.afterEach((to, from)=>{   //一般在后置路由守卫中进行修改页面的title
    document.title=to.meta.title    
  })
  

  ## 路由独享守卫:某一个路由独享的
    const router = new VueRouter({
      routes:[
          {
            name:"命名参数"
            path: "/home",
            component: HomeComp
            meta:{    //可以存放当前路由携带的说明信息 可以通过to,from等对象访问到
              isAuth: true,   //是否需要进行权限验证
              title:"首页"
            },
            //路由独享守卫:在访问当前路由之前会调用当前beforeEnter
            beforeEnter:(to, from, next)=>{ 
              ...
            }
          },
          {
            path: "/about",
            component: () => import("../pages/About.vue")
          }
      ]
    })

  ## 组件内导航守卫:类似于什么周期函数的写法
    beforeRouteEnter(to,from,next){//通过路由规则,进入组件之前被调用
        next() //放行
        next(vm=>{ //放行的同时,也获取到vm就是当前的组件对象 
        })
    }
    beforeRouteLeave(to,from,next){//通过路由规则,离开组件之后被调用
      next()  //放行
    }

  ## 路由的两种工作模式:history模式与hash模式
    1 hash 模式下,仅 hash #符号之前的内容会被包含在请求中,如 http://www.abc.com/#/sasasxda/xsaxaa,#井号之后的内容不会发送给后端服务器,因此对于后端来说,请求地址发生错误,也不会报错,即使没有做到对路由的全覆盖,也不会返回 404 错误。
      url地址中#号之后的内容就是hash值
      hash模式是默认的工作模式

    2 history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id 如果后端缺少对 /book/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
      设置history模式:
        在路由配置中,与routes添加:mode:"history"

    3 结合自身例子,对于一般的 Vue + Vue-Router + Webpack + XXX 形式的 Web 开发场景,用 history 模式即可,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。

    区别:
      hash值:url中#号后面的都是hash值,hash值不会被包含在http请求中发送给后端服务器,
      history 模式 url路径好看
      hash模式兼容性好一些

   hash模式:
      1.地址中永远带着#号,不美观。
      2.若以后将地址通过第三方手机app分享,若app校验严格, 则地址会被标记为不合法。
      3.兼容性较好。
   history模式:
      1.地址干净,美观。
      2.兼容性和hash模式相比略差。
      3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

异步组件【组件的懒加载】

  原始引入组件:
    import ComA from "./ComA"
    
    components:{
      ComA
    }

    原始引入组件的方式:组件会在页面已加载的时候就会加载下来


  异步组件 实现 组件的懒加载
    components:{
      ComA:()=>import("./ComA")
    }

  异步组件的工厂函数【加工异步组件】
    import Loading from "./components/asynccomp/Loading.vue";
    import Error from "./components/asynccomp/Error.vue";
    /* 异步组件的工厂函数 */
    const AsyncComp = () => ({
      // 需要异步加载的组件
      component: import("./components/asynccomp/AsyncComA.vue"),
      loading: Loading,
      error: Error,
      delay: 200, //loading组件展示的时间
      timeout: 3000, //异步组件3s没有显示,就展示Error组件
    });

vue3

创建vue3.0工程

  1.使用vue-cli创建vue3项目 --> vue-cli基于webpack搭建
    1.确保vue-cli > 4.5
    2.查看vue-cli版本 vue --version
    3.创建项目:vue create 项目名称
  2.使用vite创建vue3项目
    1.创建项目:npm init vite-app 项目名称
    2.进入到项目目录:yarn install

vite相比于传统的项目项目构建工具优势:

  开发环境中,无需打包操作,可快速的冷启动。
  轻量快速的热重载(HMR)
  真正的按需编译,不再等待整个应用编译完成。

vue3 main.js入口文件

  import { createApp } from 'vue' //引入的不是vue构造函数,引入的是createApp的工厂函数
  import App from './App.vue'
  createApp(App).mount('#app') 创建和挂在

  1.vue3不能像vue2那样创建vue实例,会报错
    new Vue({
      render:h=>h(App)
    ).$mount("#app")

  2. vue3中的组件文件中template标签可以包含多个根元素

  3.vue3不支持vue2的浏览器插件

常用的组合式API(Composition API)

1.setup:Vue3.0中一个新的配置项,值为一个函数。与data...methods。。一样的配置像 
  setup(props){
    let 数据 = 值 //定义数据
    function 方法 (){} //定义方法

    //返回值1:返回一个对象,对象中放回的方法直接可以在插值表达式中访问到
    return {
      数据,
      方法
    }
    //返回值2:若返回一个渲染函数:则可以自定义渲染内容。(了解)
    import {h} from "vue"
    return ()=>h("标签","标签内容") //会将当前组件中的所有内容都覆盖掉
  }

2.ref函数
import  {ref} from "vue"
    setup(props) {
      <!-- 定义p普通数据 【不是响应式数据】-->
      let name = "张三"
      let age = 12
      <!-- 定义响应式数据:需要借助ref函数 -->
      let namne=ref("zhangsan") //ref函数返回一个引用对象,借助引用对象,将普通数据转换为响应式数据

      funtion func(){
        <!-- 修改响应数据 -->
        name.value='xxx' //此时就将name数据修改为xxx,并且页面也会跟着刷新
        <!-- 在插值表达式中直接通过name就可以访问到数据 -->
      }
    }

组件懒加载:

  组件懒加载 :使用异步组件的方式实现
	components:{
		自定义组件名称:()=>import(“./组件”)
	}

异步组件的工厂函数

  const AsyncComp = () => ({
    // 需要异步加载的组件
    component: import("./components/asynccomp/AsyncComA.vue"),
    loading: Loading,
    error: Error,
    delay: 200, //loading组件展示的时间
    timeout: 3000, //异步组件3s没有显示,就展示Error组件
  });
  components:{
      AsyncComp
    }

Vue组件生命周期:

父子组件生命周期执行顺序

挂载阶段:  父组件先被创建->子组件再被创建->子组件挂载到页面->父组件再挂载到页面 ==>[先有父组件然后才有子组件,子组件挂载完毕后,父组件才算挂载完毕]
更新阶段:  父组件数据发生变化->props=>子组件数据也发生变化->子组件更新完毕->父组件数据才算更新完毕==>[视图导致页面数据发生变化,父组件将发生变化的数据通过props传递给子组件,子组件数据发生变化,子组件跟新完毕,父组件组件才算跟新完毕]
销毁阶段:  父组件即将销毁->子组件即将销毁->子组件销毁完毕->父组件销毁完毕

如果父组件没有向子组件传递props数据,那么父组件发生更新不会影响到子组件重新渲染

 页面刚已加载组件的生命周期:会从beforeCreate执行到updated

Vue生命周期函数

生命周期函数:在某个时刻会自动执行的函数

生命周期的过程:
  1.Vue({}).$mount("#app") 实例化Vue对象,并挂载到指定的容器
  2.初始化事件和生命周期函数
  3.beforeCreate() {  // Vue实例初始化创建完成之前调用 [vue初始化事件和生命周期函数完成之后执行的函数] 此时最早可以访问到Vue组件实例
    console.log("fatherComp beforeCreate");
  },
  4.初始化数据和响应式,
  5. created() { // Vue实例创建完成 methods方法,data数据都可以访问到
      console.log("fatherComp created");
    },

  6.判断有没有template的选项,来看是否编译模板到render函数
  7.beforeMount() { // 模板挂载到页面之前执行 此时不能访问DOM元素
    console.log("fatherComp beforeMount");
  },
  8.mounted() { // 模板挂载到页面完毕后执行  此时能访问DOM元素
    console.log("fatherComp mounted");
  },
  9.beforeUpdate() { // 更新完成之前 数据发生更新,但是还没有同步到页面上,DOM还没有更新
    console.log("fatherComp beforeUpdate");
  },
  updated() { // 更新完毕 页面与data中的数据同步
    console.log("fatherComp updated");
  },
  beforeDestroy() {// 组件卸载之前,此时还可以访问到vue实例,等一些数据,还没有移出完毕
    console.log("fatherComp beforeDestroy");
  },
  destroyed() { //组件卸载完毕之后
    console.log("fatherComp destroyed");
  },

// 路由对应的两个钩子
activated() {
  console.log("进入当前路由组件");
},
deactivated() {
  console.log("离开当前路由组件");
},
// 组件内独享守卫
beforeRouteEnter(to, from, next) {},
data() {
  return {
    count: 0,
  };
},
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iku_ki

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值