vue2-总结

270 阅读7分钟

Vue 学习总结

vue基础语法

  1. computed

    定义一个值,可以依赖data中的多个数据属性

    这个值会缓存,在发生变动时才触发计算行为

            computed: {
              // 函数
              // 计算属性的一个应用点
              // 计算属性就是依赖别的属性的属性
              // reverseMsg -> this.msg(响应式属性)
              // 缓存
              // 依赖多个属性
              // 一对多
              reverseMsg() {
                console.log("------------------");
                // msg + count
                // 一定要返回一个数据的
                return this.msg.split("").reverse().join("") + this.count;
              },
    
  2. watch 监听data中数据属性的变动,其他值也可以同时依赖这个属性的变动而发生变化。

    无法直接监听 对象 的变化,只能监听对象里的key的变化

     watch: {
              // 异步去请求数据的时候
              // 2. 多个值去依赖一个属性的时候
              //    多对一的关系
    
              // 只观察对象里面指定的 key
              "user.name": {
                handler(val, oldVal) {
                  console.log("name");
                  console.log(val, oldVal);
                },
              },
    
              // user: {
              //   handler(val, oldVal) {
              //     console.log("user");
              //     console.log(val, oldVal);
              //   },
              //   // 深度观察
              //   deep: true,
              // },
              count: {
                handler(val, oldVal) {
                  console.log(val, oldVal);
                  // todo
                  // fetch -> data
    
                  // 多个值去依赖一个属性 ,count
                  this.hello1 = "hello1 " + val;
                  this.hello2 = "hello2 " + val;
                },
                // 立即执行
                immediate: true,
              },
    
              // count(val, oldVal) {
              //   console.log(val, oldVal);
              //   // todo
              //   // fetch -> data
    
              //   // 多个值去依赖一个属性 ,count
              //   this.hello1 = "hello1 " + val;
              //   this.hello2 = "hello2 " + val;
              // },
            },
    
  3. v-if会直接操作DOM结构,如果为false,那么DOM不会渲染出element。一般用于懒加载。

  4. v-show通过CSS来控制显示和隐藏。频繁变更时使用

  5. v-model利用vue的单向数据流来实现双向绑定。

  6. sync用来解决父子组件间多个属性传值,v-model因为会传value值,只能传递一个属性.

    父组件:

    const app = new Vue({
            el: "#app",
            data: {
              msg: "hello ",
              showFoo: true,
            },
            methods: {
              // handleClose() {
              //   console.log("close");
              //   this.showFoo = false;
              // },
              // input v-model
              // eventName - input  | e.target.value
              // input v-model xxx
            },
            template: `
                <div>root app
                    <Foo :visible.sync="showFoo"></Foo>
                </div>
            `,
          });
    

    子组件:

          const Foo = {
            props: ["visible"],
            data() {
              return {
                count: 1,
              };
            },
            methods: {
              handleClose() {
                // update:[propsName] 是固定的格式
                // vue -> props -> visible
                // this.visible = false 
                this.$emit("update:visible", false);
              },
            },
    
            template: `<div v-if="visible">foo 
    
              <button @click="handleClose">
                close 
                </button>
              <button @click="handleClick">
                click
                </button>
                </div>`,
          };
    
  7. slot可以在父组件中直接对子组件中注册slot的地方插值

    父组件:

          const app = new Vue({
            el: "#app",
            data: {
              msg: "hello ",
            },
            template: `
                <div>
                    root app
                    <Foo>
                        -012
                      <template #first="{title}">
                        123 {{title}}
                      </template>
                      <template #second>
                        456
                      </template>
                    </Foo>
                </div>
            `,
          });
    

    子组件:

          const Foo = {
            data() {
              return {
                count: 1,
                title: "FOoFooFOo",
              };
            },
    
            template: `<div>
              <div>
                默认插槽
              <slot></slot> 
                </div>
    
              <div>
              first 
              <slot name="first" :title="title" age="13"></slot>
                </div>
    
              <div>
                second
              <slot name="second"></slot>
                </div>
              foo 
                </div>`,
          };
    
  8. mixins可以将一些公共的逻辑抽离出来,实现复用。

    缺点是:1.来源不清晰;2.命名冲突

  9. 自定义指令

          
    // Vue.directive 全局注册
    // bind,inserted,update,componentUpdated,unbind 都是生命周期钩子
    	Vue.directive("focus", {
            bind(el, data) {
              // console.log(el, data);
              // bind init
              // create
            },
            inserted(el,data) {
              // 插入的时候
              // mounted
              console.log(data)
              el.focus();
            },
            // update(){
    
            // },
            // componentUpdated(){
    
            // },
            // unbind(){
            // }
          });
    

Vue 组件间通信

  • props$emit

    • 父传子时,需要在子组件中用props声明要接收的属性,然后在父组件中传值。

    父组件:

          const app = new Vue({
            el: "#app",
            data: {
              msg: "hello ",
            },
            // 父组件和子组件的通信方式
            template: `
                <div>root app
                    <Foo msg="abc"></Foo>
                </div>
            `,
          });
    

    子组件:

          const Foo = {
            props: {
              msg: {
                type: String,
                default: "a",
                validator(val) {
                  // 自定义的校验器
                  console.log(val);
                  return val === "abc";
                },
              },
            },
           
    
            template: `<div>foo 
              {{msg}}
              <button></button>
                </div>`,
          };
    
    • 子传父时,需要父组件中监听子组件某一事件的变化。

    父组件:

          const app = new Vue({
            el: "#app",
            data: {
              msg: "hello ",
            },
            methods: {
              handleHeihei(val, secondVal) {
                console.log(val);
                console.log(secondVal);
                console.log("heihei in parent component ");
              },
            },
            template: `
                <div>root app
                    <Foo @heihei="handleHeihei"></Foo> //监听heihei事件,一旦监听到变化,触发handleHeihei方法
                </div>
            `,
          });
    

    子组件:

          const Foo = {
            data() {
              return {
                count: 1,
              };
            },
            methods: {
              handleClick() {
                console.log("click");
                this.$emit("heihei", "aaaaa", "bbbbb"); // 触发heihei事件,并发送参数
              },
            },
    
            template: `<div>foo 
              <button @click="handleClick"> // 点击触发handleClick方法
                click
                </button>
                </div>`,
          };
    
  • $refsref

    ref

      <Child 
      ref="abc"
      title="parent" @toparent="handleClick"></Child>
      <Child 
      ref="abc"
      title="parent" @toparent="handleClick"></Child>
      <button  ref="btn" 
      @click="handleGetChild">get child</button>
    

    $refs

        handleGetChild(){
          console.log(this.$refs) // 可以取到注册ref的组件实例,并返回一个对象,包含所有注册ref的实例
          const {abc}=this.$refs
          abc.sayHello()
        }
    
  • $parent$children 是严格依赖的

    在父组件中调用$children会返回由子组件返回的数组

        handleByChild(){
          console.log(this.$children) //(2) [VueComponent, VueComponent]
          const child=this.$children[0] 
          child.sayHello()
        },
    

    在子组件中调用$parent会返回父组件,但是只会返回一个父组件。

        handleGetParent(){
          console.log(this.$parent) //VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
          this.$parent.sayHi()
        },
    
  • provideinject

    在父组件中用provide,可以在子组件或孙子组件甚至更深层级组件中利用inject来接收

    父组件:

    <comp-b ></comp-b>
    ------------
    provide(){
      return {
      CompATitle:"foo-----",
      CompA:this
      }
    },
    

    子组件:

    <comp-c></comp-c>
    -------------
    inject:["CompATitle"],
    

    孙子组件

    <button @click="handleForCompA">handleForCompA</button>
    ------------------------
    inject:["CompATitle","CompA"],
    methods: {
      handleGetCompA() {
        console.log(this.CompA);
        this.CompA.add(this)
      },
    },
    
  • $attrs$listeners

    $attrs可以在子组件中没有用**props**声明 的值,可以用inheritAttrs:false来去除传递的DOM属性

       <comp-c 
       v-bind="$attrs"></comp-c>
    ---------------------
    inheritAttrs:false,
    methods: {
      GetAttrs() {
        console.log(this.$attrs);
      }
    },
    }
    

    $listeners可以在子组件中获取父组件中监听子组件的监听器。

    父组件:

      <comp-b title="123"
      @a="handA"
      @b="handB"></comp-b>
    

    子组件:

    methods: {
      GetListeners(){
        console.log(this.$listeners)//{a: ƒ, b: ƒ}
      },
    },
    
  • EventBus是Vue的一个实例,与props$emit原理相同,但是可以在引入EventBus的组件中进行通信

    组件A:

    import {eventBus} from "../EventBus"
    
    export default {
    //import引入的组件需要注入到对象中才能使用
    mounted () {
      eventBus.$on("forCompC",this.handleForCompC);//监听子组件forCompC事件,触发handleForCompC事件
    },
    methods: {
      handleForCompC(){
        console.log("handleForCompC");
      },
    },
    }
    

    组件C:

    import {eventBus} from '../EventBus.js'
    export default {
    //import引入的组件需要注入到对象中才能使用
    methods: {
      handleForCompA(){
        eventBus.$emit("forCompC")
      },
    },
    }
    
  • vuex 可以将需要通信的数据放在一个文件中

    • state

       username:{{user.name}} // 组件
       age:{{user.age}}  
      ----------------------
      import {mapState} from 'vuex';
      computed: {
          ...mapState(["user"]),
          // userName() {
          //   return this.$store.state.user.name
          // }
        },
      --------------------------
      state: {  // store/index.js
          user: {
            age: 18,
            name: "xiaohei",
          },
        },
      
    • getter

      十年后的年龄:{{tenYearsOld}}  // 组件  
      ----------------------
      // 全局的计算属性
        getters: {      //store/index.js
          tenYearsOld(state) {
            return state.user.age + 10;
          },
        },
      
    • mutation

      <button @click="handleChangeUserName">ChangeUserName</button>  // 组件DOM
        <button @click="handleChangeUserAge">ChangeUserAge</button>  
      -------------
        methods: {  // 组件方法
          handleChangeUserName() {
            this.$store.commit("changeUserName","xiaohei")
          },
          handleChangeUserAge() {
            this.$store.commit("changeUserAge",30)
          },
        },
      ---------------------------------------
      mutations: { // store/index.js
          // mutations
          changeUserName(state, payload) {
            state.user.name = payload;
          },
          changeUserAge(state, payload) {
            state.user.age = payload;
          },
        },
      
    • action 是要传递的全局异步事件

      // ...mapActions相当于在组件中绑定全局事件,然后在组件中触发, ...mapActions只是传递作用
      methods: { //组件
          ...mapActions(["changeUserName"])
          // handleFetchUsername(){
          //   this.$store.dispatch("changeUserName","xiaojun") //这样可以传递参数
          // }
        },
      -----------------
      actions: { //store/index.js
          changeUserName({ commit },payload) {
            // 去请求后端接口
            setTimeout(() => {
              commit("changeUserName", payload);
            }, 1000);
          },
        },
      

vue-router

  • $router.replace()$router.push

    $router.push是将要跳转的路由推入栈中,可以进行后退。

    $router.replace()是将要跳转的路由替换当前路由,无法进行后退。

  • 导航守卫

    导航守卫是路由跳转时调用的钩子函数。首先说一下,官方文档中完整的导航解析流程

    1. 导航被触发

    2. 在失火的组件里调用 beforeRouteLeave 守卫

    3. 调用全局的 beforeEach 守卫

    4. 在重用的组件里调用 beforeRouteUpdate 守卫

    5. 在路由配置里调用 beforeEnter

    6. 解析异步路由组件

    7. 在被激活的组件里调用beforeRouteEnter

    8. 调用全局的beforeResolve

    9. 导航被确认

    10. 调用全局的afterEach钩子

    11. 触发DOM更新

    12. 调用beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。

    导航守卫分为全局守卫、路由独享守卫和组件内守卫 三种。

    • 全局守卫分为全局前置守卫、全局解析守卫和全局后置守卫

      • 全局前置守卫router.beforeEach

        在路由跳转之前触发,主要用于登录验证,也就是路由在未跳转之前提前告知,避免跳转后再通知。

        beforeEach参数有三个(to,from,next)

        • to:Route:即将要进入的目标 路由对象

        • from:Route:当前导航正要离开的路由

        • next:Function:需要调用nextresolve这个钩子

          1. next():调用next()才可以继续向后执行下一个钩子

          2. next(false):中断当前的导航,如果手动取消跳转,那么URL地址会重置到之前的地址,也就是from的地址

          3. next('/')或者next({path:'/'}):跳转到一个指定的地址。可以中断当前导航,向next中传递replacepush中的选项。

            router.beforeEach((to, from, next) => {
              if (to.meta.isAuth) {
                // token
                const token = store.state.token;
                if (token) {
                  next();
                } else {
                  next({
                    name: "Login",
                  });
                }
              } else {
                next();
              }
            });
            
          4. next(error):如果传入的参数是一个Error实例,那么导航会被终止,并且错误会被传递给router.onError()注册过的回调。

      • 全局解析守卫router.beforeResolve

        参数和router.beforeEach相同。区别在于router.beforeResolve在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后被调用。

        也就是说router.beforeResolveafterEach和其它守卫之间被调用。

      • 全局后置守卫router.afterEach

        router.afterEach在导航被确认之后被调用,发生在其它所有守卫之后。不会接收next参数

    • 路由独享守卫 即可以在路由配置中直接定义的 beforeEnter守卫

      const router = new VueRouter({
        routes: [
          {
            path: '/foo',
            component: Foo,
            beforeEnter: (to, from, next) => {
              // ...
            }
          }
        ]
      })
      

      beforeEnterbeforeEach的参数方法完全相同,在beforeEach之后执行。

    • 组件内守卫是在组件内执行的钩子函数,类似于组件内的生命周期函数,相当于为配置路由的组件添加的生命周期钩子函数。

      const Foo = {
        template: `...`,
        beforeRouteEnter (to, from, next) {
          // 在渲染该组件的对应路由被 confirm 前调用
          // 不!能!获取组件实例 `this`
          // 因为当守卫执行前,组件实例还没被创建
        },
        beforeRouteUpdate (to, from, next) {
          // 在当前路由改变,但是该组件被复用时调用
          // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
          // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
          // 可以访问组件实例 `this`
        },
        beforeRouteLeave (to, from, next) {
          // 导航离开该组件的对应路由时调用
          // 可以访问组件实例 `this`
        }
      }
      
      • beforeRouteEnter(to,from,next)

        beforeEachbeforeEnter之后调用,该守卫无法获取组件实例,this===undefined,在beforeCreate之前触发。

        next可以接收回调函数来访问组件实例。在导航被确认的时候执行回调,并把组件实例作为回调方法的参数,可以在这个守卫中请求服务端获取数据,当成功获取并能进入路由时,调用next并在回调中通过vm访问组件实例进行赋值等操作。

        beforeRouteEnter (to, from, next) {
          next(vm => {
            // 通过 `vm` 访问组件实例
          })
        }
        
      • beforeRouteUpdate(to,from,next)

        在路由发生改变,且当前组件被复用时调用。

        这里已经获取了组件实例,所以可以用this

        beforeRouteUpdate (to, from, next) {
          // just use `this`
          this.name = to.params.name
          next()
        }
        
      • beforeRouteLeave(to,from,next)

        用于禁止用户在未保存修改前突然离开。可以通过next(false)来取消。可以使用this

        beforeRouteLeave (to, from, next) {
          const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
          if (answer) {
            next()
          } else {
            next(false)
          }
        }