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 , observer,
entries 被观察的元素信息对象的数组,即[{元素信息},{}],信息中 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