一、基础
Vue 实现了一套内容分发的 API,将 <slot>
元素作为承载分发内容的出口
它允许你像这样合成组件:
<navigation-link url="/profile">
Your Profile
</navigation-link>
然后你在 <navigation-link>
的模板中可能会写为:
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
当组件渲染的时候,这个 <slot>
元素将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
甚至其它的组件:
<navigation-link url="/profile">
<!-- 添加一个图标的组件 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
如果 <navigation-link>
没有包含一个 <slot>
元素,则任何传入它的内容都会被抛弃。
二、应用(利用插槽实现一个上下滚动分页的功能组件)
在内容高度不确定的情况下,通过滚动条的上下滚动来实现分页
Html:
<template>
<div class="ScrollView" @scroll="scrollEvent" ref="ScrollView">
<!-- 这里可以自定义加载状态 -->
<slot></slot>
</div>
</template>
Js:
// emit事件的typescript写法
@Emit()
private scrollDown() {
console.info('Emit scrollDown')
}
@Emit()
private scrollUp() {
console.info('Emit scrollUp')
}
private scrollEvent(event: any) {
const scrollTop = event.target.scrollTop;
const clientHeight = event.target.clientHeight;
const scrollHeight = event.target.scrollHeight;
if (scrollTop + clientHeight >= scrollHeight) { // 当滚动条到达底部时
this.scrollDown() // 在这里可以类比父子组件,向父组件emit一个事件
} else if(scrollTop === 0) { // 当滚动条到达顶部时
this.scrollUp() // 同底部
}
}
使用时:
<scroll-view @scroll-down="loadData(true,false)" @scroll-up="loadData(true,true)" :style="
{height:height-199 + 'px'}" :dataState="dataState">
<ul class="chataccount_content" id="scrol">
<li v-for="(item) in chataccounts" :key="item.id" :class="['ChataccountView',
{'clickModel': onClickId === item.id}]">
<chataccount-concern />
</li>
</ul>
</scroll-view>
用法和组件基本一致,唯一的区别就是可以在其中添加内容,添加的内容渲染后替代<slot></slot>的位置
下面就是分页了:
import { MutliPager } from '@/domain/command' // 引入一个自定义的分页器
const maxSize = 40; // 定义一个列表内可存放的组最大数据量
private pager: MutliPager = new MutliPager(5,10); // 定义一个分页器,第一个参数代表最大页
数,第二个代表每页的数量(pageSize)
private loadData(concern: boolean ,up: boolean) {
const _this = this;
const params = Object.assign({
concern,
nameLike: this.searchInput,
snsType: this.snsType
},up?_this.pager.prevPage():_this.pager.nextPage()); // 这里根据传进来的up参数判断页码的加减
loadChataccounts(params).then((result: any) => {
if (result.success) {
_this.pager.total = result.data.totalPages
const data: Chataccount[] = result.data.content;
if( _this.chataccounts.length > 0) {
// 这里做了一步去重操作,如不需要可省略
const validData = data.filter((acct) => _this.chataccounts
.findIndex((account) => account.id === acct.id) < 0 )
if(validData.length > 0 ) {
if(up) {
_this.unshift(validData)
} else {
_this.push(validData)
}
}
} else {
_this.chataccounts = data
}
} else {
this.$message({
message: result.desc,
type: 'error'
});
}
})
}
// 向上滚动获取数据 unshift 至列表内
private unshift(validData: Chataccount[]) {
this.chataccounts.unshift(...validData) // 这里需要一个个unshift进去,否则会改变原列表的数据结构
if(this.chataccounts.length > maxSize) {
this.chataccounts.splice(maxSize,this.chataccounts.length-maxSize)
}
}
// 向上滚动获取数据 push 至列表内
private push(validData: Chataccount[]) {
this.chataccounts.push(...validData)
if(this.chataccounts.length > maxSize) {
this.chataccounts.splice(0,this.chataccounts.length-maxSize)
}
}
command.js
export class Pager {
public static FIRST_PAGE = new Pager()
public pageNo: number = 0;
public pageSize: number = 10;
/** 数据总页数 */
public total: number = 0
public next(): Pager {
this.pageNo++;
return this
}
public go(pageNo: number) {
this.pageNo = Math.max(0,pageNo)
}
public prev(): Pager {
if(this.pageNo > 0 ) {
this.pageNo--;
} else {
this.pageNo = 0
}
return this
}
}
export class MutliPager {
/** 每页最大数据数量 */
public pageSize: number = 10;
/** 数据总页数 */
public total: number = 0
/** 最大可缓存的页码数 */
private maxSize: number
/** 缓存页中的第一页的index 从0开始计数 */
private firstPageNo: number = 0
/** 缓存页中的最后一页的index 从0开始计数 */
private lastPageNo: number = 0
/**
* @param maxSize 最大可缓存的页码数
*/
constructor(maxSize: number,pageSize: number) {
this.maxSize = Math.max(1,maxSize)
this.pageSize = Math.max(1,pageSize)
}
/** 向前一页 */
public prevPage(): Pager {
this.prev();
const result = new Pager()
result.pageNo = this.firstPageNo
result.pageSize = this.pageSize
return result
}
/** 向后一页 */
public nextPage(): Pager {
this.next();
const result = new Pager()
result.pageNo = this.lastPageNo
result.pageSize = this.pageSize
return result
}
/** 返回顶部是页码置0 */
public zero(): Pager {
const result = new Pager()
result.pageNo = 0
return result
}
public reset(): void {
this.firstPageNo = 0
this.lastPageNo = 0
}
private next() {
if((this.lastPageNo+1) >= this.total) {
this.lastPageNo = this.total - 1
return
}
this.lastPageNo ++;
if(this.lastPageNo - this.firstPageNo >= this.maxSize) {
this.firstPageNo ++
}
console.info('next='+this.lastPageNo)
}
private prev() {
if(this.firstPageNo <= 0) {
this.firstPageNo = 0
return
}
this. firstPageNo--;
if(this.lastPageNo - this.firstPageNo >= this.maxSize) {
this.lastPageNo --
}
}
}