vue3学习1:vue3内容、实现懒加载、自动化注册全局组件

1,620 阅读1分钟

1、ref

vue3中的ref一方面能够将基本数据类型转换为响应式,同时它也结合了vue2中的$ref,可以获取vue的DOM,例如:

<template>
  <h1>ref的使用</h1>
  <div ref="aa">窝嫩跌</div>
  <div ref="bb">流汗黄豆</div>
  <button @click="fn">Click</button>
</template>

<script>
import { ref } from 'vue'
export default {
  setup(){
    const aa = ref(null)
    const bb = ref(null)
    const fn = () => {
      console.log('哈哈');
      console.log(aa.value);
      console.log(bb.value);
    }
    return {
      aa,
      bb,
      fn
    }
  }
}
</script>

2、v-model

vue3中的v-model将vue2中的.sync语法糖合在了一起,在vue2中的.sync语法相当于

<Son :value="message" @input="message=$event" />

而在vue3中它相当于

<Son :modelValue="message" @update:modelValue="message=$event" />

具体在vue3中使用v-model绑定自组件方法如下:

父组件中

    //父组件
<template>
  <div>
    <!-- 
    如果你想获取原生事件事件对象,
    绑定的是函数fn,fn(e){} // e就是事件对象
    绑定的是js表达式,此时提供一个默认的变量:$event
    -->
    <h1 @click="$event.target.style.color='red'">父组件 {{count}}</h1>
    <hr>
    <!-- 
    如果你想获取自定义事件,
    如果绑定事函数fn fn(e){} // e 触发自定义事件的传参 ,
    如果绑定的是js表达式,此时的$event代表触发自定义事件的传参
    -->
    <!-- <Son :modelValue="count" @update:modelValue="count=$event" /> -->
    <Son v-model="count" />
  </div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
  name: 'App',
  components: {
    Son
  },
  setup () {
    const count = ref(10)
    return { count }
  }
}
</script>

注意:在父组建中使用v-model绑定不止一个或为了更有语义化,可以给 modelValue 重命名,方式是:

v-model:newName="count"

子组件中

<template>
  <div>
    <h2>子组件 {{modelValue}} <button @click="fn">改变数据</button></h2>
  </div>
</template>
<script>
export default {
  name: 'Son',
  props: {
    modelValue: {
      type: Number,
      default: 0
    }
  },
  setup (props, {emit}) {
    const fn = () => {
      // 改变数据
      emit('update:modelValue', 100)
    }
    return { fn }
  }
}
</script>

3、Teleport

vue3新加了一个特性:传送门(Teleport),它可以将一个组件挂载到另外的DOM结构里,并且不受目标挂载点父元素的影响,他的数据处理和逻辑仍然是他原来的。这个新特性可以应用在最常见的alert或者toast等等上:

在Son组建中

<template>
  <div class="Son">
    <div>我是可爱的小炸弹</div>
    <teleport to="#teleport-target">
      <h1>Boom</h1>
    </teleport>
  </div>
</template>

<script>
export default {
  name: "Son"
};
</script>
<style scoped>
.Son {
  color: red;
}
</style>

index.html中

    <body>
        <div id="#app"></div>
        <div id="teleport-target" style="color:blue">
        </div>
    </body>

4、vue3 template中可以使用可选链了(?.)

当不确定数据有没有时,vue2中可能会使用v-if做处理,vue3中可以直接Obj?.One来动态渲染数据,方便许多。

5、一些学到的方法并自己实现懒加载:

譬如我要封装一个懒加载的hooks:(用到了一个vue3的库 @vueuse/core)

import { useIntersectionObserver } from '@vueuse/core'
export function useLazyData(apiFn) {
  const target = ref(null)
  const list = ref([])
  const { stop } = useIntersectionObserver(
    target,
    ([{ isIntersecting }], observerElement) => {
      if (isIntersecting) {
        stop()
        apiFn().then(res => {
          list.value = res.result
        })
      }
    }
  )
  //  return出target,当引动该hooks时将target绑定给指定DOM
  return {
    target,
    list
  }
}

我可以直接将ref的target返回出去,在用的地方挂载,而这个API如果接收一个自定义的参数,我可以在传入一个()=>fn(666)来实现不同的参数需求。

<template>
  <div v-for="i in 100" :key="i">111111</div>
  <div ref="target">
    <div v-for="item in list" :key="item.id">
        {{item.name}}
    </div>
  </div>
</template>

<script>
import { getList } from '@/api'
import { useLazyData } from '@/hooks'
export default {
  name: 'Test',
  setup(props) {
    const { target, list } = useLazyData(()=>getList(10))
    return {
      list,
      target
    }
  }
}
</script>

<style scoped lang="less">
</style>

当然上面时基于@vueuse/core 实现的懒加载,IntersectionObserver()(IntersectionObserver() - Web APIs | MDN (mozilla.org))

创建观察实力对象:const observer = new IntersectionObserver(callback[, options])

其中 callback 在被观察dom进入可视区离开可视区都会触发,它有两个回调参数 entries , observerentries 被观察的元素信息对象的数组,即[{元素信息},{}],信息中 isIntersecting 判断进入或离开,而observer 就是观察实例。

options 配置参数有三个属性:root rootMargin threshold,其中 root 基于的滚动容器,默认是document,rootMargin 是配置容器有没有外边距,threshold 是配置交叉的比例

创建出的实例会提供两个方法:observe()unobserve() 他们传入DOM,observe方法是观察哪个DOM,unobserve是停止观察哪个DOM,

实现一个图片懒加载的自定义指令:

//原来为: <img :src="item.src" alt="" />
//使用图片懒加载自定义指令后: <img v-lazy="item.src" alt="" />
app.directive('lazy', {
  mounted(el, { value }) {  
    const observer = new IntersectionObserver(
        //参数1:回调函数  参数2:可选的配置
      ([{ isIntersecting }], observer) => {
          //如果图片进入了
        if (isIntersecting) {
          // 在加载完图片后停止监听DOM
          observer.unobserve(el)
          // 给el元素设置src属性,value是接收的图片路径地址
          el.src = value
          // 如果图片加载失败,显示默认的图片
          el.onerror = function() {
            el.src = require('@/assets/images/NotFound.jpg')
          }
        }
      },
      {
        threshold: 0
      }
    )
    //监视绑定的DOM元素
    observer.observe(el)
  }
})

自动化注册全局组件:

我们在写一些全局组件等时会发现需要手动一个个导入,又难看又麻烦,这时我们可以通过webpack的一个apirequire.context来实现自动化导入。

require.context(directory,useSubdirectories,regExp), 有三个参数,分别是要读取的文件的路径、是否边炉文件的字母路,匹配文件的正则

require.context执行后会返回一个方法 webpackContext。这个返回出的webpackContext有两个静态方法keys()和 resolve(),其中 keys() 可以获得返回匹配成功模块的名字组成的数组,然后forEach遍历该数组,利用 resolve() 方法: 接受一个参数request 即 keys遍历出的每一项相对于匹配文件的相对路径,返回出这个匹配文件相对于整个工程的相对路径,然后通过app.component()来注册全局组件

export default {
  install(app) {
    //  全局注册组件
   
    //  获取所有.vue结尾文件,通过.keys()可以获取到每一个的路径
    const ctx = require.context('./', false, /\.vue$/)
    ctx.keys().forEach(item => {
      //  ctx同时也是函数,可以接收一个参数(路径)
      const component = ctx(item).default
      app.component(component.name, component)
    })
  }
}

End