vue3马上就要发布了,在正式版发布之前尝了下新,惊喜的发现vue3一个隐藏的玩法:完全兼容vue2,你可以在文件里随意写vue2的代码,接下来听我娓娓道来。
拿一个虚拟长列表组件实现来说:
这是我的父组件:
<LongList :data = 'listData'></LongList>
setup(){
const state = reactive({
listData:list
})
return {
...toRefs(state)
}
}
父组件要做的事情很简单,向虚拟长列表传入listData,下面我们来实现子组件
<template>
<div class="long-list">
<div class="list-view" ref='sparent' @scroll="handleScroll">
<div
class="list-view-phantom"
:style="{
height: contentHeight
}"></div>
<div
ref="content"
class="list-view-content">
<div
class="list-view-item"
:style="{
height: itemHeight + 'px'
}"
v-for="(item,index) in visibleData" :key="index">
{{ item.value }}
</div>
</div>
</div>
</div>
</template>
<style>
.list-view {
height: 400px;
overflow: auto;
position: relative;
border: 1px solid #aaa;
}
.list-view-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list-view-content {
left: 0;
right: 0;
top: 0;
position: absolute;
}
.list-view-item {
padding: 5px;
color: #666;
line-height: 30px;
box-sizing: border-box;
}
</style>
template和css和以前的没有什么区别,来看看js吧
import { reactive, computed, onMounted, toRefs, ref } from 'vue'
export default {
props:{
data:{
require:true,
type:Array
},
itemHeight:{
type:Number,
default:50
}
},
setup(props){
const {data, itemHeight} = props
// 可视区域节点
const content = ref(null)
// 容器节点
const sparent = ref(null)
const state = reactive({
visibleData:[],
})
// 计算列表的总长度
const contentHeight = computed(()=> {
const height = data.length*itemHeight+'px'
return height
})
onMounted(()=>{
updateVisibleData()
})
// 随着滚动更新可视区域数据
const updateVisibleData = scrollTop => {
scrollTop = scrollTop || 0;
// 可视区域item条数
const visibleCount = Math.ceil( sparent.value.clientHeight/ itemHeight);
// 可视区域头尾在数据中的的索引
const start = Math.floor(scrollTop / itemHeight);
const end = start + visibleCount;
state.visibleData = data.slice(start, end);
// 移动可视区域头部至指定位置
content.value.style.webkitTransform = `translate3d(0, ${ start * itemHeight }px, 0)`;
}
// 滚动事件
const handleScroll = () => {
const scrollTop = sparent.value.scrollTop
updateVisibleData(scrollTop)
}
return{
contentHeight,
content,
sparent,
...toRefs(state),
handleScroll,
}
},
}
如此,我们的虚拟长表单已经基本实现了,这也没有vue2的代码啊?别急,继续听我说。
我们暂时只是实现了虚拟长表单的效果,真实情况往往要点击跳转这种需求,找了下非官方文档,不知道该如何通过子组件向父组件派发事件,这就比较难受了啊。想起之前看资料说vue3可以在文件中使用vue2的代码,然后就找了下尤大的github(传送门:vue-next),搜了下emit,注意到了这个目录vue-next/test-dts/defineComponent.test-d.tsx,这一定就是demo的测试用例了,机智如我这个小机灵鬼。
进去之后先发现了这个东西:
这熟悉的option API使我热泪盈眶,这是不是意味着setup也是option API的一个选项,如果你想用vue2的生命周期、data、methods、computed,也完全没有问题呢?说干就干,我在setup外边定义methods,果然我能拿到this,也可以完美的使用诸如this.$emit之类的api,是不是特别丝滑?对于不太想用vue3的人完全还是没有压力的,因为几乎完全兼容。这是我的一个意外发现,其实我还是想找到vue3的emit用法,继续向下看,然后终于找到了:
我们只需在setup中传入一个{emit}就可以在setup中使用了,修改下代码:
<div class="long-list">
<div class="list-view" ref='sparent' @scroll="handleScroll">
<div
class="list-view-phantom"
:style="{
height: contentHeight
}"></div>
<div
ref="content"
class="list-view-content">
<div
class="list-view-item"
:style="{
height: itemHeight + 'px'
}"
@click="handleClick(item,index)"
v-for="(item,index) in visibleData" :key="index">
{{ item.value }}
</div>
</div>
</div>
</div>
import { reactive, computed, onMounted, toRefs, ref } from 'vue'
export default {
props:{
data:{
require:true,
type:Array
},
itemHeight:{
type:Number,
default:50
}
},
setup(props,{emit}){
const {data, itemHeight} = props
// 可视区域节点
const content = ref(null)
// 容器节点
const sparent = ref(null)
const state = reactive({
visibleData:[],
})
// 计算列表的总长度
const contentHeight = computed(()=> {
const height = data.length*itemHeight+'px'
return height
})
onMounted(()=>{
updateVisibleData()
})
// 随着滚动更新可视区域数据
const updateVisibleData = scrollTop => {
scrollTop = scrollTop || 0;
console.log(sparent,'可视')
// 可视区域item条数
const visibleCount = Math.ceil( sparent.value.clientHeight/ itemHeight);
// 可视区域头尾在数据中的的索引
const start = Math.floor(scrollTop / itemHeight);
const end = start + visibleCount;
state.visibleData = data.slice(start, end);
// 移动可视区域头部至指定位置
content.value.style.webkitTransform = `translate3d(0, ${ start * itemHeight }px, 0)`;
}
// 滚动事件
const handleScroll = () => {
const scrollTop = sparent.value.scrollTop
updateVisibleData(scrollTop)
}
//向父组件派发事件,父组件和vue2一样的处理
const handleClick = (item,index)=>{
console.log(item.value,'??')
emit('clickItem',{item,index})
}
return{
contentHeight,
content,
sparent,
...toRefs(state),
handleScroll,
handleClick
}
},
}
好了,完美解决,如果你有的地方喜欢用vue2过渡一下,没有问题,可以实现但我还是喜欢vue3的方式,所以,让我们期待vue3的正式发版吧。最后说一句:尤大牛批,vue牛批!!!
最后的最后,贴一个vue3的API地址:vue-next尝鲜版