同事都说我卷,趁着午休我 —— 彻底熟练使用《Vue3的选项APi》

目录

mixin—局部

如果组件与mixin冲突怎么办?总结:

mixin造成的问题: 

mixin—全局

Vue2与Vue3的区别 Options API的弊端

setup函数有哪些参数?

 setup函数的返回值

setup不可以使用this

Vue3中 reactive 和 ref 和 toRefs 和 toRef

readonly只读属性  ​

 toRef

computed

 Wacth监听

Vue2 watch监听

 vue3 watch的使用

 Vue3 watchEffect

 watchEffect的停止侦听

 watchEffect清除副作用

 setup中使用 ref 获取dom

​ vue2 ref 获取dom

认识vue-router 

vue3的路由模式 

 路由守卫vue2 vue3

 访问的页面 404 路由怎么配


那啥 开始就声明解释一下mixin,它不叫米新,它叫米克僧嗯,mix +‎ in 额 就说通不通易不易懂,在本文我将以最为通俗的、让能看懂的话术解释V3Api,当然了一些概念还的是全面点!

mixin—局部

红框和绿框组件,都有相同的方法,和钩子,那么你将他们相同的剪贴走,建一个文件粘贴

进去(mixin.js 也可以是aa.js),最后导入页面,就是混合原理

   

配合代码更加贴切,易懂

/*
*
* 子组件uname
*
/
<template>
  <div>
    <h2 @click="choseName">姓名:{
   
   { uname }}</h2>
    <hr />
    <button @click="fn">测试组件和mixin方法重复,执行谁</button>
  </div>
</template>

<script>
import { MyMiXin } from "./mixin";
export default {
  data() {
    return {
      uname: "活在风浪里。。",
    };
  },
  //   mixins复数,因为要调用data中数据,必须写在data下面
  mixins: [MyMiXin],

  methods: {
    fn() {
      console.log("组件方法");
    },
  },
  created() {
    console.log("组件钩子");
  },
};
</script>
/*
*
* 子组件school
*
/
<template>
  <div>
    <h2 @click="choseName">学校:{
   
   { uname }}</h2>
    <hr />
    <button @click="fn">测试组件和mixin方法重复,执行谁</button>
  </div>
</template>

<script>
import { MyMiXin } from "./mixin";
export default {
  data() {
    return {
      uname: "北大",
    };
  },
  //   mixins复数,因为要调用data中数据,必须写在data下面
  mixins: [MyMiXin],
  methods: {
    fn() {
      console.log("组件方法");
    },
  },
  created() {
    console.log("组件钩子");
  },
};
</script>

/*
*
* 父组件 引入子组件
*
/
<template>
  <div id="about">
   
    <school></school>
    <hr />
    <uname></uname>

  </div>
</template>

<script>
import school from "../components/school.vue";
import uname from "../components/uname.vue";

export default {
  data() {
   
  },
  components: {  school, uname },
};
</script>

/*
*
* 新建mixin JS文件
*
/
export const MyMiXin = {
    // data、methods、如果和组件重复,只会执行组件的内容,mixi不会执行
    methods: {
        choseName() {
            alert(this.uname)
        },
        fn() {
            console.log('mixin方法');
        }
    },
    // 钩子函数事特殊的,它会合并组件的钩子,先执行mixin钩子,在执行组件钩子
    created() {
        console.log("mixin钩子");
    },
}

如果组件与mixin冲突怎么办?总结:

  • 组件 data、method 优先级高于 Mixin 混入的 data、method
  • 组件自定义属性优先级高于 Mixin 混入的 自定义属性
  • 先执行 Mixin 的钩子,再执行组件里的钩子,会合并
  • 建议配合上面代码和图片,还是看不懂请揍我

mixin造成的问题: 

首先Vue是渐进式框架,不需要必须使用它的api,可替代性很强,你可以只用我的一部分,而不是所有部分

  • 变量来源不明,不利于代码的阅读;
  • 使用多个Mixin可能会造成命名冲突;
  • Mixin和组件的关系,复杂度较高

mixin—全局

更新中。。。
什么是全局选项? 就是 全局组件,全局过滤器,全局指令,全局mixin

1、Vue.component 注册的 【全局组件】
2、Vue.filter 注册的 【全局过滤器】
3、Vue.directive 注册的 【全局指令】
4、Vue.mixin 注册的 【全局mixin】


Vue2与Vue3的区别 Options API的弊端

相对于Vue2来说,Vue3最大的突破就是 Composition API 。与现有的Vue2 Option API 截然不同

  1. 数据劫持、双向绑定之类,不在这里说,太多,太占篇幅,
  2. 比如data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性改变,也包括生命周期钩子;假如一个大项目,逻辑很多,那么同一个功能的逻辑就会被拆分的很分散,这个组件的代码是难以阅读和理解的(阅读组件的其他人),碎片化的代码使用理解和维护变得很困难,如果我们能将同一个逻辑关注点相关的代码收集在一起会更好,这个位置就是Vue3 setup 函数

setup函数有哪些参数?

     // setup函数有哪些参数?
    // setup函数有什么样的返回值
   // setup(props, context) 
  
    setup(props, {attrs, slots, emit}) {//解构context
      console.log(props.message);
      console.log(attrs.id, attrs.class);
      console.log(slots);
      console.log(emit);

      return {
      
      }
    },
  • 我们先来研究一个setup函数的参数,它主要有两个参数:
    • 第一个参数:props
    • 第二个参数:context

props非常好理解,它其实就是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取: 

另外一个参数是context

  1. attrs:所有的非prop的attribute;
  2. slots:父组件传递过来的插槽
  3. emit:当我们组件内部需要发出事件时会用到emit

  

任何人写的文都不会比官方文档还要全,建议撸一遍V3官方文档 

 setup函数的返回值

  • 必须使用return返回 

setup不可以使用this

  • 官方关于this有这样一段描述
  • 表达的含义是this并没有指向当前组件实例
  • 并且在setup被调用之前,data、computed、methods等都没有被解析;
  • 所以法在setup中获取this

Vue3中 reactive 和 ref 和 toRefs 和 toRef

   
reactive 是vue3的一个组合api vue3中新增的最核心的功能就是组合api
 reactive vue3建议只用来声明对象 他声明的对象中的每一个属性都是响应式的
他是结合es6的proxy 结合递归给每一个reactive声明的对象数据添加上 setter/getter方法 从而实现数据的响应式
ref是用reactive封装成的一个方法 这个方法我们通常用来声明基本数据类型和数组数据
 ref声明的数据是一个对象 值是value ref在使用的时候不需要写value 但是赋值的时候必须写value
 toRefs可以解构reactive对象的属性 将属性取出 转换成一个ref对象,假如你要reactive里面的obj.name,在return{}用了 ...toRefs(obj)就可以直接使用name 

 

如果不经过toRefs,直接解构是不具备相应式的 

 setup() {
      const info = reactive({name: "why", age: 18});
      // 1.toRefs: 将reactive对象中的所有属性都转成ref,
    // 如果不经过toRefs,直接解构是不具备相应式的
      let { name, age } = toRefs(info);
       
      return {//具备相应式
        name,
        age
      }
    }

总结ref:

  • 在模板中引入ref的值时,Vue会自动帮助我们进行解包操作,所以我们并不需要在模板中通过 ref.value 的方式来使用;
  • 但是在 setup 函数内部,它依然是一个 ref引用, 所以对其进行操作时,我们依然需要使用 ref.value的方式;

 什么是解包: 

 a解构出来相当于 a={value:xx}

readonly只读属性  

使用场景:在父传子时就可以将接受的数据定为只读,子组件不允许直接修改父组件的数据,如果一个子组件有很多父组件(子组件定义结构样式,父组件传递不同的值,以显示不同的页面),子组件修改父组件可能会造成数据混乱,因为多个父组件可能定义了相同的变量,但是可以子传父,不能直接在子组件修改父组件数据

 toRef

如果我们只希望转换一个reactive对象中的属性为ref, 那么可以使用toRef的方法:

computed

  • 在V2 中选项性Options API中,我们是使用computed选项来完成的;
  • 在V3组合型Composition API中,我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;
  • 两种写法,一种函数形式、一种对象形式,对象形式不能直接赋值,需要setter、getter方法调用,下面都会一一举例说明。Vue2建议参考计算属性 + Vue2 、Vue3 wacth + 过滤器_技术前端,忠于热爱-CSDN博客

 

比如在computed() 中不需要赋值一个变量,再比如定义方法必须赋值一个变量choseNameEv可以是aa但是对应的 点击事件也要变化

 子组件:(函数写法)

<template>
  <div>
    <h2>{
   
   { resultName }}</h2>
    <button @click="choseNameEv">点击计算姓名</button>
  </div>
</template>
<script>
import { ref, computed } from "vue";
export default {
  components: {},
  setup() {
    const firstName = ref("tom");
    const lastName = ref("baby");
    const resultName = computed(() => firstName.value + " - " + lastName.value);
    const choseNameEv = () => {
      firstName.value = "小黑";
    };
    return {
      firstName,
      lastName,
      resultName,
      choseNameEv,
    };
  },
};
</script>
<style lang="scss" scoped></style>

父组件: 

<template>
  <div>
    <computedChild />
  </div>
</template>
<script>
import computedChild from "../components/计算属性.vue";
export default {
  components: {
    computedChild,
  },
  setup() {
    return {};
  },
};
</script>
<style lang="scss" scoped></style>

效果图:

子组件 ——— (第二种对象写法 )

<template>
  <div>
    <h2>{
   
   { resultName }}</h2>
    <button @click="choseNameEv">点击计算姓名</button>
  </div>
</template>
<script>
import { ref, computed } from "vue";
export default {
  components: {},
  setup() {
    //   1、定义数据
    const firstName = ref("jack");
    const lastName = ref("Biu");
    const resultName = computed({ // 对象写法: 传入一个对象, 对象包含getter/setter
      //   2、 利用get得到数据
      get: () => firstName.value + " " + lastName.value,
      //  3 、利用set设置数据 set的形参就是要赋的值(code)
      set(newValue) {
         lastName.value = newValue
      },
    });

    const choseNameEv = () => {
      lastName.value = "code";//将code,赋值 firstName.value
    };
    return {
      firstName,
      lastName,
      resultName,
      choseNameEv,
    };
  },
};
</script>
<style lang="scss" scoped></style>

其实和Vue2差不多,下面是Vu2的

<template>
  <div class="home">
    商品价格--- {
   
   {sumPrice}}
    <button @click="changePrice">修改商品价格</button>
  </div>
</template>
 
<script>
export default {
  name: "Home",
  data() {
    return {
      // 1 定义数据
      price: 123,
      num: 666,
    };
  },
  computed: {
    sumPrice: {
      // 2 在get中将数据return出去
      get() {
        return this.price;
      },
      //3 手动添加set方法 修改值 形参就是事件赋的值
      set(val) {
        // console.log(val); val就是赋的值
        this.price = val;
      },
    },
  },
  methods: {
    // 4 点击按钮的事件  对象不能直接赋值,需要手动添加get/set方法 现在就可以直接赋值
    changePrice() {
      //直接写死值
     // this.sumPrice = 1234;
      // 动态变量值
      this.sumPrice = this.num;
    },
  },
};
</script>

效果图: 

 Wacth监听

  • watchEffect:在watchEffect对象里的数据,会被自动收集
  • wacth:需要手动指定要监听的数据

Vue2 watch监听

// Vue2 简写wacth 

  watch: {
    sum (newValue, oldValue) {
      console.log("sum的数值的变换新值、老值", newValue, oldValue);
    }
  },

//完整写法

 watch: {
    obj: {
      handler (newvalue, oldvalue) {
        console.log(newvalue, oldvalue)
      },
      immediate: true, //立即监听
      deep: true // 深度监听
    }
  },
// 监听路由:

wacth:{
 
    "$router.path":(new,old)=>{
        console.log(new,old)
      }
  }

 vue3 watch的使用

  • 如果要监听对象某一个值,例如person,在wacth小括号里以箭头函数形式监听对象某一个值 例如
 watch(() =>person.name, (newVal, oldVal)=> {
      console.log(newVal, oldVal)
    })

如果监听person对象里的多个值:wacth小括号中以数组形式书写,在数组中用箭头函数 例如:

watch([() =>person.name, ()=> person.age], (newVal, oldVal)=> {undefined
      console.log(newVal, oldVal)
    })
  • 监听ref数组简单类型  
 //监听ref单一值的变化
    watch(监听的值, (newvAL, oLdVal) => {
      console.log(newvAL, oLdVal)
    })

  // 监听ref多个值的变化 数组形式
    watch([msg, sum], (newVal, oldVal) => {
      console.log(newVal, oldVal)
    })
  • 监听ractive复杂对象类型 
 // 监听reactive中的整个person对象
     watch(person, (newVal, oldVal)=> {
      console.log(newVal, oldVal)
    }, {deep: true})

 // 监听reactive对象中某一个属性的变化
    watch(() =>person.name, (newVal, oldVal)=> {
      console.log(newVal, oldVal)
    })

//监听person对象里的某一个值,开启深度监听
 watch([() => person.job], (newVal, oldVak) => {
      console.log(newVal, oldVak)
    }, {deep: true})

//监听person对象里的某两个值 
watch([() =>person.name, ()=> person.age], (newVal, oldVal)=> {
      console.log(newVal, oldVal)
    })

注意:wacth监听打印新旧值都是一样的,因为监听的同一条数据,监听的都是同一个引用

<template>
  <div>
    <h1>{
   
   { obj.age }}</h1>
    <button @click="choseAge">修改age</button>
  </div>
</template>
<script>
import { reactive, watch } from "vue";
export default {
  components: {},
  setup() {
    const obj = reactive({ name: "tom", age: 18, sex: "女" });

    const choseAge = () => {
      obj.age++;
    };
    watch(
      //watch写在setup中,第一个形参是监听的数据,第二个是新值旧值
      () => obj,
      (newVal, oldVal) => {
        console.log("新" + newVal.age, "旧" + oldVal.age);
      },
      { deep: true },
      { immediate: false }
    );
    return { obj, choseAge };
  },
};
</script>
<style lang="scss" scoped></style>

  

总结:vue3 wacth后是一个小括号,在小括号中操作,如果要监听ref单个的值,监听的值逗号箭头函数,箭头函数里面有两个形参,new,old,可以在对象中直接打印,

监听多个值,可以在监听值的地方使用数组,多个值以逗号分隔

监听ractive复杂对象类型 :wacth小括号,监听的对象逗号箭头函数,箭头函数中有两个值,new,old,在箭头函数对象中可以直接打印,如果要开启深度监听,在箭头函数后面加逗号,逗号后面是对象,对象中开启deep:true

 Vue3 watchEffect

  • 自动收集依赖,简单来说watchEffect 小括号里的数据都会被监听
<template>
  <div>
    <h2>{
   
   { uname }}-{
   
   { age }}</h2>
    <button @click="choseNameEv">修改uname</button>
    <button @click="choseAgeEv">修改age</button>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";

export default {
  setup() {
    const uname = ref("活在分浪里。。");
    const age = ref(18);

    const choseNameEv = () => (uname.value = "tom");
    const choseAgeEv = () => age.value++;
    // watchEffect: 自动收集响应式的依赖
    // 在watchEffect方法里会自动监听
    watchEffect(() => {
      console.log("uname:", uname.value, "age:", age.value);
    });

    return {
      uname,
      age,
      choseNameEv,
      choseAgeEv,
    };
  },
};
</script>

 

 watchEffect的停止侦听

如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可。

  • 比如在上面的案例中,我们age达到22的时候就停止侦听:

 思路:watchEffect的返回值,在终于条件哪里调用

<template>
  <div>
    <h2>{
   
   { uname }}:{
   
   { age }}</h2>
    <button @click="choseNameEv">修改uname</button>
    <button @click="choseAgeEv">修改age</button>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";
export default {
  setup() {
    const uname = ref("活在分浪里。。");
    const age = ref(18);

    const choseNameEv = () => (uname.value = "tom");
    const choseAgeEv = () => age.value++;
    //    利用watchEffect的返回值(它是一个方法,你也可以const aa)
    const stop = watchEffect(() => {
      if (age.value >= 22) {//age>=22就不会检测,也就是不会打印下面log
        stop();
      }
      console.log("uname:", uname.value, "age:", age.value);
    });

    return {
      uname,
      age,
      choseNameEv,
      choseAgeEv,
    };
  },
};
</script>

 watchEffect清除副作用

就是防抖思路,假如用watchEffect检测Ajax请求,例如查询搜索:要在本次点击或输入之前,清除上一次的请求, 

主要思路就是watchEffect的 onInvalidate 参数,watchEffect的停止侦听是利用返回值,请求异步检测则需要利用onInvalidate 参数清除上一次请求

<template>
  <div>
    <!-- 狂点按钮,只会打印最后一次请求 -->
    <h2>{
   
   { age }}</h2>
    <button @click="choseAge">修改age</button>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";

export default {
  setup() {
    const age = ref(12);
    watchEffect((onInvalidate) => {
        // 点击按钮就会修改age,但是写了onInvalidate,会先执行onInvalidate里的逻辑
      console.log( age.value);// 3
      onInvalidate(() => {
        //  这里面写清除方法
        // request.cancel() //ajax清除方法,现在是用定时器模拟ajax
        clearTimeout(timer);// 1
        console.log("onInvalidate");// 2
      });
      const timer = setTimeout(() => {// 4
        console.log("网络请求成功");
      }, 1000);
    });

    const choseAge = () => age.value++;

    return {
      age,
      choseAge,
    };
  },
};
</script>

 setup中使用 ref 获取dom

思路:主要是利用     watchEffect箭头函数后的对象里的  flush: "post",  watchEffect(
      () => {     }, 

   {
        flush: "post", //默认 flush: "async" 异步
      }

    );

<template>
  <div>
    <h2 ref="title">恭喜你有学到了</h2>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";

export default {
  setup() {
    const title = ref(null);
    console.log(title.value); // 这里还是null,写了  flush: "post",就可以拿到dom

    watchEffect(
      () => {
        console.log(title.value);//<h2>恭喜你有学到了</h2>
        title.value.style.color='red'
      },
      {
        flush: "post", //默认 flush: "async" 异步
      }
    );

    return {
      title,
    };
  },
};
</script>

 vue2 ref 获取dom

<template>
  <div>
    <h1 ref="title">vue2获取dom</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
  created() {
    this.$nextTick(() => {
      this.$refs.title.style.backgroundColor = "green";
    });
  },
};
</script>

<style lang="scss" scoped>
//@import '引入的css文件';
</style>

认识vue-router 

  1. React的ReactRouter
  2. Vue的vue-router
  3. 目前Vue路由最新的版本是4.x版本。
  4. vue-router是基于路由和组件的

vue3的路由模式 

还是那句话,v3是 Composition API,用那那个引入那个

 

 

 路由守卫vue2 vue3

// 在路由元信息配置守卫 requiredPath为true, 适合守卫多个页面 vue3next() 变成return true
// vue2
router.beforeEach((to, from, next) => {
    if (localStorage.getItem('token')) {
        next()
    } else {
        if (to.meta.requiredPath) { //没有token requiredPath为true 守卫不让进,跳入login
            next('/login')
        } else {
            next()
        }
    }

})
// vue3 

router.beforeEach((to, from) => {
  // ...
  // 返回 false 以取消导航
  return false
// return "/login"
})

 还是建议将官方文档撸一遍 导航守卫 | Vue Router

 访问的页面 404 路由怎么配

// router/index.js 
{
        // 如果url找不到就会报404
        path: "/:pathMatch(.*)",
        component: () =>
            import ("../views/NotFound.vue")
    }

 views

 效果:

猜你喜欢

转载自blog.csdn.net/m0_57904695/article/details/123064289
今日推荐