Vue3高阶

Vue3高阶


前言

啦啦啦,新的篇章,本期也是Vue3工程化的最后一篇了,一个人的旅程好难,什么都要靠自己去摸索,遇到问题需要好久好久才搞懂,苦逼~~~~

Mixin

在Vue3x版本中用的不多了
  mixin指Vue中可重复使用的部分,比如说,在一个实例中的data,methods等另外一个实例也要用,那么就可将要复用的东西拎出来,单独放在一个对象中,在实例中用mixins接住即可,mixins是一个数组,可以放多个mixin对象

<body>
  <div id="app">
    <div>
      <h1>{{msg}}</h1>
      <button @click="hello">打个招呼</button>
    </div>
  </div>
  <script>
    // 创建一个mixin对象
    const myMixin = {
      data() {
        return {
          msg: "你好"
        }
      },
      methods: {
        hello() {
          alert("你好")
        }
      }
    }
    const app = Vue.createApp({
      mixins:[myMixin]
    }).mount('#app')
 </script>

mixin细节点

  1. 实例中的data,methods之类的选项,优先级高于mixin中定义的
    eg:如果mixin对象的数据中有msg变量,当前实例中也有msg变量,那么会优先采用实例中的msg
  2. mixin对象中的数据会和实例中的数据混用,
    eg:假设mixin对象data中有msg变量,实例中有一个site变量,那么实例在调用这两个变量的时候都会显示
  3. 如果mixin对象和实例中都有生命周期钩子,两者都会执行,但是会先执行mixin对象中的钩子

mixin-自定义属性

  有的时候我们会在mixin对象中传入自定义的属性,这些属性不会直接存在data中,所以不能直接使用,而是要在$options中调用
$options:是一个对象,当一个实例加载完毕后,里面的所有内容都会挂载到$options对象中
注意:如果实例中也有一个同名的属性,首先会用实例中的
 此外还可以通过配置,优先使用mixin中的属性,具体见一下代码
注意:mixin还可以全局配置,然后当前实例的组件就不需要用mixins接收了,
具体见下面代码

 <div id="app">
    <div>
      <h1>{{msg}}</h1>
      <h1>{{$options.age}}</h1>
    </div>
  </div>
  <script>
    // 创建一个mixin对象,里面是自定义属性
    const myMixin = {
      age:30
    }
    const app = Vue.createApp({
      mixins: [myMixin],
      data() {
        return {
          msg: "你好",
        }
      },
      age:89,
    })
    // 如果说mixin和实例中有同名的属性,默认是先使用实例中的,如果想改变的话,使用一下配置
    app.config.optionMergeStrategies.age = (mixinVal, appVal) => {
      return mixinVal || appVal
    }
    // mixin还可以全局配置,在整个实例中的所有组件都可以用
    // app.mixin({
    //   data(){
    //     return {
    //       age:100
    //     }
    //   }
    // })
    app.mount('#app')

  </script>

自定义指令

可以在全局创建,也可以在局部创建

指令里面的钩子,具体可以去官网手册查询

EG:mounted - 元素插入父 DOM 后发生。

每个钩子里面的参数含义:
mounted(el,binding,vnode){…}
el:代表当前使用该指令的元素
binding:指令传来的值
vnode:当前元素节点相关
在调用的时候可以:eg:v-fiexd=”xxx”传递值

全局定义

<script>
app.directive("focus",{
  mounted(el) {
    el.focus()
  },
})
</script>

还可以这样定义,不在钩子函数中使用,直接使用

<script>
app.directive("focus",(el,binding,vnode)=>{
	el.focus()
}
})
</script>

局部定义

只能被挂载的组件使用
需要在使用的实例中挂载

 <script>
    // 创建一个局部的自定义指令
    const myDirective = {
      focus:{
        mounted(el) {
          el.focus()
        },
      }
    }
  </script>
  <script>
	const app = Vue.createApp({
	  directives:myDirective,
	})
  </script>

调用的时候:

v-指令名即可,可传参可不传递(传参的话需要创建的时候接收一下)

<div id="app">
  <h1>自定义指令</h1>
  <input type="text" placeholder="请输入内容" v-focus="[值]">
  <!--[值]:表示可选-->
</div>

teleport 传送门

  比如说,父组件中包裹一个子组件,不想让这个子组件显示到父组件上,想让子组件显示到其他地方,这时候就可以用teleport了
将要传送的标签或者组件,用包裹
to—属性取值为选择器,
eg:to=”body” to=”#box” to=”.box”

  <div id="app">
    <div class="box1"></div>
    <div class="box">
      <!-- 让蒙版展示到id为box1的元素中 -->
      <button @click="btn">蒙版</button>
      <teleport to=".box1">
        <div v-if="isShow" class="mask">hahah</div>
      </teleport>
    </div>
  </div>

组合式API (composition API)

  在vue2x的版本中,是是由分散式的API(options API),就是用到哪个就写哪个,比如用到data,将data写进实例,用到methods,将其写入,一旦代码量多了不容易维护
  vue3x中采用了组合式的API,所有相关的数据、方法等等都放在一个函数中,用到哪个函数,通过setup钩子调用即可,增加了代码的可维护性

setup

从生命周期的角度来看,steup是取代了2x版本的BeforeCreate(创建前)和create函数
注意点:setup函数里面是不能使用this相关的东西,和实例中的选项因为setup没被挂载,也不能使用生命周期函数

ref

让基础类的数据具备响应式,

在composition API中的数据是不具备相应式的,修改数据,视图层不会改变。

在创建数据的时候用ref包裹一下即可

<script>
const app = Vue.createApp({
  setup(props,context) {
    // 在composition API 中更改数据,页面中不会变化,不具备响应式
    // ref :让基础类的数据具备响应式
    // 被包裹的数据 底层通过proxy代理,让其具备响应式-->proxy({value:"你好呀!"})
    // 1.引入ref 
    const {ref} = Vue
    // 用ref将数据包裹
    let msg = ref("你好呀!")
    // 因为被包裹的数据,被proxy代理的成了一个对象,value指向这个对象的值,所以要修改value
    // 两秒后修改
    setTimeout(()=>{
      msg.value="hahah"
    },2000)
    return{
      msg
    }
  }
}).mount('#app')
</script>

reactive

让引用类数据具备响应式

用法同ref

<script>
   const app = Vue.createApp({
      setup(props, context) {
        // reactive:让引用类的数据具备响应式
        // 被包裹的数据 底层通过proxy代理,让其具备响应式-->proxy({name:"张三",gender:"男"})
        // 1.引入reactive 
        const { reactive } = Vue
        // 用reactive将数据包裹
        let pObj = reactive({ name: "张三", gender: "男" })
        let pArr = reactive(["Vue", "React"])
        // 这里直接修改即可,因为地址一样
        setTimeout(() => {
          pObj.name = "张某某"
          pArr[0] = "javaScript"
        }, 2000)

        return {
          pObj,
          pArr
        }
      }
    }).mount('#app')
</script>

readonly

让数据只读,不能修改

在composition API 中数据是可以修改的,这违背了Vue单项数据流的初心,为了防止子组件中修改父组件的数据,用readonly将数据包裹即可,
eg:
let pObj = readOnly({name:”张三”,age:12})

toRefs

让解构的变量也具备响应式,

我们正常解构出来的变量是不具备响应式的,修改数据视图层不会改变,通过toRefs包裹要解构的数据,即可将解构出来的变量具备响应式

<script>
 setup(props, context) {
        const { reactive, toRefs } = Vue
        let pObj = reactive({ name: "张三", gender: "男" })

        setTimeout(() => {
          pObj.name = "张某某"
        }, 2000)
        // 将数据从变量结构出来
		// 将数据从变量结构出来,用toRefs将原对象包裹
      // 原理,会将对象中的每个属性拿出来在代理包装一下,此时变量就具备响应式了
        /* 
          proxy({ name: "张三", gender: "男" })---->
          name:proxy({value:"张三"})
          gender:proxy({value:"男"})
        */
        const {name, gender} = toRefs(pObj)

        return {
          name,
          gender
        }
      }
</script>

toRef

有时候不确传来的数据对象中是否包含某个属性,

eg:针对此类情况如果pObj对象中有age就拿出来赋值给age变量,如果没有就强行添加一个值为undefined的属性age
let age = toRef(pObj, "age")

context

在setup中有两个参数,其中一个参数是context,代表上下文对象里面有attrs,slots,emit
attrs:里面存放的是no-props相关的东西
slots:里面存放的是插槽相关的东西
emit:里面存放的是子组件发射的自定义事件

context-attrs

这个里面是no-props相关的东西,比如父组件在调用子组件的时候传入了一个属性,那么通过context.attrs.属性名,即可获取到传入的属性
父组件给子组件传参:

<box s1=”666”></box>

子组件中:

setup(props,context){
	console.log(context.attrs.s1)
	//结果:666
}

context-slots

  这个里面存放的是插槽相关的东西,比如子组件中有一个具名插槽box和一个匿名插槽,父组件在具名插槽box使用button按钮占位,匿名插槽使用输入框占位,在子组件的setup函数中,如果想获得匿名插槽相关的东西,就调用context.slots.default(),如果是具名插槽,context.slots.具名插槽名()

获得数据是数组,访问里面内容的时候记得加下标

<script>
 const box = {
      template: "#box",
      setup(props, context) {
        const { slots } = context
        console.log(slots.default());//获取的是匿名插槽的内容
		  console.log(slots.box())//获取的是具名插槽box的内容
      },
    }
 </script>

context-emit
主要解决在composition API 中,不能使用this。$emit发射事件也不能用。
只需要将emit 从context中解构出来,用emit代替this.$emit发射事件即可

<script>
function handel() {
         alert("子组件点击")
         // 在composition API中,因为不能用this,
         //所以直接用context中的emit代替this.$emit发射事件
         emit("my-son", "我是信息")
       }
</script>

计算属性(computed)的新用法:

  在composition API 中computed用法也发生了改变,仅在创建的时候发生了改变,其他的都没变,创建前需要从Vue中引入

<script>
const app = Vue.createApp({
      setup(props, context) {
        // 引入
        const { ref, computed } = Vue;
        let num1 = ref(10);
        // 计算属性
        let num2 = computed(() => {
          return num1.value * 10
        })
        // add
        let add = () => {
          num1.value += 10;
        }
        // 将数据抛出
        return {
          num1, num2, add
        }
      }
    }).mount('#app')
</script>

此外计算属性的参数也可以是set和get,而且也可以对引用数据类型的值经行修改

 <script>
    const app = Vue.createApp({
      setup(props, context) {
        // 引入
        const { reactive, computed } = Vue;
        // 值为引用类
        let num1 = reactive({ count: 100 });
        // 计算属性
        let num2 = computed({
          get() {
            return num1.count * 10
          },
          set(res) {
            console.log(res);
            num1.count = res / 10
          }
        })
        // 定时器
        setTimeout(() => {
          num2.value = 2000
        }, 2000)
        // add
        let add = () => {
          num1.count += 10;
        }

        // 将数据抛出
        return {
          num1,
          num2,
          add
        }
      }
    }).mount('#app')
  </script>

侦听器的新用法watch

在composition API 中,侦听器也需要改变一下用法,

以下代码中包含监听一个属性或者监听多个属性的用法

既可以侦听基础类型的数据也可以侦听引用类型的数据

侦听器的特性

  1. 具有惰性(页面加载的时候不触发,必须在数据变动的情况下才会触发)
  2. 更加具体,
  3. 可以访问属性改变之前的值
  4. 可配置(非惰性、深度监视…)

  深度监视:加入监听的是一个对象,对象中有数组,浅度监听,数组变化不会触发,深度监听则会触发监听函数

侦听器回调函数的options参数:

	immediate:true  是否非惰性,默认false
	deep:true   是否深度监听:默认false
})

侦听基础类型数据:

<script>
 const app = Vue.createApp({
      setup(props, context) {
        const { ref, watch } = Vue
        let brand = ref("")
        let site = ref("")
        // 侦听器
        // watch(source,cb,option)
        // 第一个参数表示侦听谁  第二个是回调函数 第三个是用来配置深度监视还是浅度监视
        // 监听一个属性
        /* watch(brand, (currentVal, preVal) => {
          // 相应业务
          console.log("现在的值:" + currentVal);
          console.log("之前的值:" + preVal);
        }) */
        // 侦听多个属性
        watch([brand, site], ([currentBrand, currentSite], [preBrand, preSite]) => {
          console.log("现在的值:" + currentBrand, currentSite);
          console.log("之前的值:" + preBrand, preSite);
        })
        return {
          brand,
          site
        }
      },
    }).mount('#app')
 </script>

侦听引用类型数据:
如果要侦听对象中的某个属性,只需要将对象的属性包裹在函数中return出去即可

<script>
setup(props, context) {
        const { reactive, watch, toRefs } = Vue
        let brandObj = reactive({ brand: "" })
        // 侦听器
        // watch(source,cb,option)s
        // 第一个参数表示侦听谁  第二个是回调函数 第三个是用来配置深度监视还是浅度监视
        // 监听引用数据类型属性,
        // ()=>{return brandObj.brand }:简写
        watch(() => brandObj.brand, (currentVal, preVal) => {
          // 相应业务
          console.log("现在的值:" + currentVal);
          console.log("之前的值:" + preVal);
        })
        const { brand } = toRefs(brandObj)
        return {
          brand,

        }
      },
</script>

watchEffect

副作用函数,监听整个实例中的数据,只要当前实例中任何数据发生了改变就会触发。

特点:

  1. 非惰性
  2. 更加抽象
  3. 不可以访问属性之前的值
<script>
  const app = Vue.createApp({
      setup(props, context) {
        const { ref, watch, watchEffect } = Vue
        let brand = ref("")
        let site = ref("")
        // watchEffect
        watchEffect(()=>{
          console.log("开始侦听了");
          console.log(brand.value);
          console.log(site.value);
        })
        return {
          brand,
          site
        }
      },
</script>

provide和inject

发射和接收数据,当然也可以发射函数

  有的时候组件的层级太深了,正常的组件传参会很麻烦,或者兄弟组件没法传参,这时候就用用到provide和inject。
用法:在要发射数据的组件中使用provide发射数据,在要接收数据的组件中使用inject接收数据。

子组件中:

 <script>
    const myson = {
      template: `
        <div style="width:150px;height:150px;background-color:#f00">
          {{sMsg}}-------{{sCollege}}
          <button @click="changeMsg">修改</button>
        </div>
      `,
      setup(props, context) {
        const { inject } = Vue;
        // 通过inject接收别的组件发射的值,参数一是发射过来的值,参数二是默认值,可选参数
        let sMsg = inject("msg", "默认值")
        let sCollege = inject("college", "默认值")
        let changeName = inject("changeName")
        // 创建修改数据的方法
        let changeMsg = () => {
         changeName("我不是一般的好!!")
        }
        return {
          sMsg,
          sCollege,
          changeMsg
        }
      }
    }
</script>

父组件中:

<script>
    const app = Vue.createApp({
      setup(props, context) {
        const { ref, reactive, provide, readonly } = Vue
        let msg = ref("你好");
        let college = reactive(["数学", "语文", "英语"])
        let changeName = (val) => {
          msg.value = val
        }
        // 发射
        // 通过provide发射数据,参数一是发射的数据的名称(自定义),参数二是值
        // 在composition API中数据没有数据单向流,所以要在传递数据的时候设置一下
        provide("msg", readonly(msg))
        provide("college", readonly(college))
        // 如果想要在子组件中修改父组件的数据,应当遵循单项数据流的原则,在源头修改,
        // 发射一个数据的方法
        provide("changeName",changeName)
        return {

        }
      },
      components: {
        "my-son": myson
      }
    }).mount('#app')
  </script>

生命周期钩子的新写法

在composition API 中,因为setup代替了beforeCreate和created,所以这两个钩子失效,其他的只需要在Vue中引入,然后前面加一个on即可。
在这里插入图片描述

<script>
    const app = Vue.createApp({
      setup(props, context) {
        const { onBeforeMount, onRenderTracked, onRenderTriggered, ref } = Vue
        //  生命周期钩子的使用
        onBeforeMount(() => {
          console.log("onBeforeMount");
        })
        // 每次渲染后重新收集响应式依赖,当视图层更新就会触发调用
        onRenderTracked(() => {
          console.log("onRenderTracked()");
        })
        // 每次触发页面重新渲染自动执行,当控制层的数据发生了变化调用
        onRenderTriggered(() => {
          console.log("onRenderTriggered()");
        })
        let msg = ref("魑魅魍魉")
        let handel = ()=>{
          msg.value = "哈哈"
        }
        return {
          msg,
          handel
        }
      }
    }).mount('#app')
  </script>

ref(获取真实DOM)

不推荐使用,除非真的需要

<div id="app">
    <div ref="box">哈哈</div>
    <div ref="aaa">嘿嘿</div>
  </div>
  <script>
    const app = Vue.createApp({
      setup(props, context) {
        const { onMounted, ref } = Vue
        // ref 这里ref指的是获取真实dom
        // 创建一个和试图层标签同名的ref
        const box = ref(null)
        const aaa = ref(null)
        // 
        onMounted(()=>{
          console.log(box.value);
          console.log(aaa.value);
        })
        return{
          box,
          aaa
        }
  }
    }).mount('#app')
  </script>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值