Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
zuiqiangwanggong
vue-vben-admin
提交
fdeaa00b
V
vue-vben-admin
项目概览
zuiqiangwanggong
/
vue-vben-admin
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vue-vben-admin
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
fdeaa00b
编写于
10月 26, 2020
作者:
V
vben
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: add lazyContainer comp and demo
上级
a0c31974
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
292 addition
and
229 deletion
+292
-229
CHANGELOG.zh_CN.md
CHANGELOG.zh_CN.md
+1
-0
src/components/Container/index.ts
src/components/Container/index.ts
+1
-1
src/components/Container/src/LazyContainer.less
src/components/Container/src/LazyContainer.less
+0
-27
src/components/Container/src/LazyContainer.tsx
src/components/Container/src/LazyContainer.tsx
+0
-200
src/components/Container/src/LazyContainer.vue
src/components/Container/src/LazyContainer.vue
+213
-0
src/router/menus/modules/demo/comp.ts
src/router/menus/modules/demo/comp.ts
+4
-0
src/router/routes/modules/demo/comp.ts
src/router/routes/modules/demo/comp.ts
+8
-1
src/views/demo/comp/lazy/TargetContent.vue
src/views/demo/comp/lazy/TargetContent.vue
+19
-0
src/views/demo/comp/lazy/index.vue
src/views/demo/comp/lazy/index.vue
+46
-0
未找到文件。
CHANGELOG.zh_CN.md
浏览文件 @
fdeaa00b
...
@@ -8,6 +8,7 @@
...
@@ -8,6 +8,7 @@
-
表单新增 submitOnReset 控制是否在重置时重新发起请求
-
表单新增 submitOnReset 控制是否在重置时重新发起请求
-
表格新增
`sortFn`
支持自定义排序
-
表格新增
`sortFn`
支持自定义排序
-
新增动画组件及示例
-
新增动画组件及示例
-
新增懒加载/延时加载组件及示例
### ✨ Refactor
### ✨ Refactor
...
...
src/components/Container/index.ts
浏览文件 @
fdeaa00b
export
{
default
as
ScrollContainer
}
from
'
./src/ScrollContainer.vue
'
;
export
{
default
as
ScrollContainer
}
from
'
./src/ScrollContainer.vue
'
;
export
{
default
as
CollapseContainer
}
from
'
./src/collapse/CollapseContainer.vue
'
;
export
{
default
as
CollapseContainer
}
from
'
./src/collapse/CollapseContainer.vue
'
;
export
{
default
as
LazyContainer
}
from
'
./src/LazyContainer
'
;
export
{
default
as
LazyContainer
}
from
'
./src/LazyContainer
.vue
'
;
export
*
from
'
./src/types.d
'
;
export
*
from
'
./src/types.d
'
;
src/components/Container/src/LazyContainer.less
已删除
100644 → 0
浏览文件 @
a0c31974
.lazy-container-enter {
opacity: 0;
}
.lazy-container-enter-to {
opacity: 1;
}
.lazy-container-enter-from,
.lazy-container-enter-active {
position: absolute;
top: 0;
width: 100%;
transition: opacity 0.3s 0.2s;
}
.lazy-container-leave {
opacity: 1;
}
.lazy-container-leave-to {
opacity: 0;
}
.lazy-container-leave-active {
transition: opacity 0.5s;
}
src/components/Container/src/LazyContainer.tsx
已删除
100644 → 0
浏览文件 @
a0c31974
import
type
{
PropType
}
from
'
vue
'
;
import
{
defineComponent
,
reactive
,
onMounted
,
ref
,
unref
,
onUnmounted
,
TransitionGroup
,
}
from
'
vue
'
;
import
{
Skeleton
}
from
'
ant-design-vue
'
;
import
{
useRaf
}
from
'
/@/hooks/event/useRaf
'
;
import
{
useTimeout
}
from
'
/@/hooks/core/useTimeout
'
;
import
{
getListeners
,
getSlot
}
from
'
/@/utils/helper/tsxHelper
'
;
import
'
./LazyContainer.less
'
;
interface
State
{
isInit
:
boolean
;
loading
:
boolean
;
intersectionObserverInstance
:
IntersectionObserver
|
null
;
}
export
default
defineComponent
({
name
:
'
LazyContainer
'
,
emits
:
[
'
before-init
'
,
'
init
'
],
props
:
{
// 等待时间,如果指定了时间,不论可见与否,在指定时间之后自动加载
timeout
:
{
type
:
Number
as
PropType
<
number
>
,
default
:
8000
,
// default: 8000,
},
// 组件所在的视口,如果组件是在页面容器内滚动,视口就是该容器
viewport
:
{
type
:
(
typeof
window
!==
'
undefined
'
?
window
.
HTMLElement
:
Object
)
as
PropType
<
HTMLElement
>
,
default
:
()
=>
null
,
},
// 预加载阈值, css单位
threshold
:
{
type
:
String
as
PropType
<
string
>
,
default
:
'
0px
'
,
},
// 视口的滚动方向, vertical代表垂直方向,horizontal代表水平方向
direction
:
{
type
:
String
as
PropType
<
'
vertical
'
|
'
horizontal
'
>
,
default
:
'
vertical
'
,
},
// 包裹组件的外层容器的标签名
tag
:
{
type
:
String
as
PropType
<
string
>
,
default
:
'
div
'
,
},
maxWaitingTime
:
{
type
:
Number
as
PropType
<
number
>
,
default
:
80
,
},
// 是否在不可见的时候销毁
autoDestory
:
{
type
:
Boolean
as
PropType
<
boolean
>
,
default
:
false
,
},
// transition name
transitionName
:
{
type
:
String
as
PropType
<
string
>
,
default
:
'
lazy-container
'
,
},
},
setup
(
props
,
{
attrs
,
emit
,
slots
})
{
const
elRef
=
ref
<
any
>
(
null
);
const
state
=
reactive
<
State
>
({
isInit
:
false
,
loading
:
false
,
intersectionObserverInstance
:
null
,
});
// If there is a set delay time, it will be executed immediately
function
immediateInit
()
{
const
{
timeout
}
=
props
;
timeout
&&
useTimeout
(()
=>
{
init
();
},
timeout
);
}
function
init
()
{
// At this point, the skeleton component is about to be switched
emit
(
'
before-init
'
);
// At this point you can prepare to load the resources of the lazy-loaded component
state
.
loading
=
true
;
requestAnimationFrameFn
(()
=>
{
state
.
isInit
=
true
;
emit
(
'
init
'
);
});
}
function
requestAnimationFrameFn
(
callback
:
()
=>
any
)
{
// Prevent waiting too long without executing the callback
// Set the maximum waiting time
useTimeout
(()
=>
{
if
(
state
.
isInit
)
{
return
;
}
callback
();
},
props
.
maxWaitingTime
||
80
);
const
{
requestAnimationFrame
}
=
useRaf
();
return
requestAnimationFrame
;
}
function
initIntersectionObserver
()
{
const
{
timeout
,
direction
,
threshold
,
viewport
}
=
props
;
if
(
timeout
)
{
return
;
}
// According to the scrolling direction to construct the viewport margin, used to load in advance
let
rootMargin
;
switch
(
direction
)
{
case
'
vertical
'
:
rootMargin
=
`
${
threshold
}
0px`
;
break
;
case
'
horizontal
'
:
rootMargin
=
`0px
${
threshold
}
`
;
break
;
}
try
{
// Observe the intersection of the viewport and the component container
state
.
intersectionObserverInstance
=
new
window
.
IntersectionObserver
(
intersectionHandler
,
{
rootMargin
,
root
:
viewport
,
threshold
:
[
0
,
Number
.
MIN_VALUE
,
0.01
],
});
const
el
=
unref
(
elRef
);
state
.
intersectionObserverInstance
.
observe
(
el
.
$el
);
}
catch
(
e
)
{
init
();
}
}
// Cross-condition change handling function
function
intersectionHandler
(
entries
:
any
[])
{
const
isIntersecting
=
entries
[
0
].
isIntersecting
||
entries
[
0
].
intersectionRatio
;
if
(
isIntersecting
)
{
init
();
if
(
state
.
intersectionObserverInstance
)
{
const
el
=
unref
(
elRef
);
state
.
intersectionObserverInstance
.
unobserve
(
el
.
$el
);
}
}
// else {
// const { autoDestory } = props;
// autoDestory && destory();
// }
}
// function destory() {
// emit('beforeDestory');
// state.loading = false;
// nextTick(() => {
// emit('destory');
// });
// }
immediateInit
();
onMounted
(()
=>
{
initIntersectionObserver
();
});
onUnmounted
(()
=>
{
// Cancel the observation before the component is destroyed
if
(
state
.
intersectionObserverInstance
)
{
const
el
=
unref
(
elRef
);
state
.
intersectionObserverInstance
.
unobserve
(
el
.
$el
);
}
});
function
renderContent
()
{
const
{
isInit
,
loading
}
=
state
;
if
(
isInit
)
{
return
<
div
key
=
"component"
>
{
getSlot
(
slots
,
'
default
'
,
{
loading
})
}
</
div
>;
}
if
(
slots
.
skeleton
)
{
return
<
div
key
=
"skeleton"
>
{
getSlot
(
slots
,
'
skeleton
'
)
||
<
Skeleton
/>
}
</
div
>;
}
return
null
;
}
return
()
=>
{
const
{
tag
,
transitionName
}
=
props
;
return
(
<
TransitionGroup
ref
=
{
elRef
}
name
=
{
transitionName
}
tag
=
{
tag
}
{
...
getListeners
(
attrs
)
}
>
{
()
=>
renderContent
()
}
</
TransitionGroup
>
);
};
},
});
src/components/Container/src/LazyContainer.vue
0 → 100644
浏览文件 @
fdeaa00b
<
template
>
<transition-group
v-bind=
"$attrs"
ref=
"elRef"
:name=
"transitionName"
:tag=
"tag"
>
<div
key=
"component"
v-if=
"isInit"
>
<slot
:loading=
"loading"
/>
</div>
<div
key=
"skeleton"
>
<slot
name=
"skeleton"
v-if=
"$slots.skeleton"
/>
<Skeleton
v-else
/>
</div>
</transition-group>
</
template
>
<
script
lang=
"ts"
>
import
type
{
PropType
}
from
'
vue
'
;
import
{
defineComponent
,
reactive
,
onMounted
,
ref
,
unref
,
onUnmounted
,
toRefs
}
from
'
vue
'
;
import
{
Skeleton
}
from
'
ant-design-vue
'
;
import
{
useRaf
}
from
'
/@/hooks/event/useRaf
'
;
import
{
useTimeout
}
from
'
/@/hooks/core/useTimeout
'
;
interface
State
{
isInit
:
boolean
;
loading
:
boolean
;
intersectionObserverInstance
:
IntersectionObserver
|
null
;
}
export
default
defineComponent
({
name
:
'
LazyContainer
'
,
components
:
{
Skeleton
},
props
:
{
// 等待时间,如果指定了时间,不论可见与否,在指定时间之后自动加载
timeout
:
{
type
:
Number
as
PropType
<
number
>
,
default
:
8000
,
// default: 8000,
},
// 组件所在的视口,如果组件是在页面容器内滚动,视口就是该容器
viewport
:
{
type
:
(
typeof
window
!==
'
undefined
'
?
window
.
HTMLElement
:
Object
)
as
PropType
<
HTMLElement
>
,
default
:
()
=>
null
,
},
// 预加载阈值, css单位
threshold
:
{
type
:
String
as
PropType
<
string
>
,
default
:
'
0px
'
,
},
// 视口的滚动方向, vertical代表垂直方向,horizontal代表水平方向
direction
:
{
type
:
String
as
PropType
<
'
vertical
'
|
'
horizontal
'
>
,
default
:
'
vertical
'
,
},
// 包裹组件的外层容器的标签名
tag
:
{
type
:
String
as
PropType
<
string
>
,
default
:
'
div
'
,
},
maxWaitingTime
:
{
type
:
Number
as
PropType
<
number
>
,
default
:
80
,
},
// // 是否在不可见的时候销毁
// autoDestory: {
// type: Boolean as PropType
<
boolean
>
,
// default: false,
// },
// transition name
transitionName
:
{
type
:
String
as
PropType
<
string
>
,
default
:
'
lazy-container
'
,
},
},
emits
:
[
'
before-init
'
,
'
init
'
],
setup
(
props
,
{
emit
,
slots
})
{
const
elRef
=
ref
<
any
>
(
null
);
const
state
=
reactive
<
State
>
({
isInit
:
false
,
loading
:
false
,
intersectionObserverInstance
:
null
,
});
immediateInit
();
onMounted
(()
=>
{
initIntersectionObserver
();
});
onUnmounted
(()
=>
{
// Cancel the observation before the component is destroyed
if
(
state
.
intersectionObserverInstance
)
{
const
el
=
unref
(
elRef
);
state
.
intersectionObserverInstance
.
unobserve
(
el
.
$el
);
}
});
// If there is a set delay time, it will be executed immediately
function
immediateInit
()
{
const
{
timeout
}
=
props
;
timeout
&&
useTimeout
(()
=>
{
init
();
},
timeout
);
}
function
init
()
{
// At this point, the skeleton component is about to be switched
emit
(
'
before-init
'
);
// At this point you can prepare to load the resources of the lazy-loaded component
state
.
loading
=
true
;
requestAnimationFrameFn
(()
=>
{
state
.
isInit
=
true
;
emit
(
'
init
'
);
});
}
function
requestAnimationFrameFn
(
callback
:
()
=>
any
)
{
// Prevent waiting too long without executing the callback
// Set the maximum waiting time
useTimeout
(()
=>
{
if
(
state
.
isInit
)
{
return
;
}
callback
();
},
props
.
maxWaitingTime
||
80
);
const
{
requestAnimationFrame
}
=
useRaf
();
return
requestAnimationFrame
;
}
function
initIntersectionObserver
()
{
const
{
timeout
,
direction
,
threshold
,
viewport
}
=
props
;
if
(
timeout
)
{
return
;
}
// According to the scrolling direction to construct the viewport margin, used to load in advance
let
rootMargin
;
switch
(
direction
)
{
case
'
vertical
'
:
rootMargin
=
`
${
threshold
}
0px`
;
break
;
case
'
horizontal
'
:
rootMargin
=
`0px
${
threshold
}
`
;
break
;
}
try
{
// Observe the intersection of the viewport and the component container
state
.
intersectionObserverInstance
=
new
window
.
IntersectionObserver
(
intersectionHandler
,
{
rootMargin
,
root
:
viewport
,
threshold
:
[
0
,
Number
.
MIN_VALUE
,
0.01
],
}
);
const
el
=
unref
(
elRef
);
state
.
intersectionObserverInstance
.
observe
(
el
.
$el
);
}
catch
(
e
)
{
init
();
}
}
// Cross-condition change handling function
function
intersectionHandler
(
entries
:
any
[])
{
const
isIntersecting
=
entries
[
0
].
isIntersecting
||
entries
[
0
].
intersectionRatio
;
if
(
isIntersecting
)
{
init
();
if
(
state
.
intersectionObserverInstance
)
{
const
el
=
unref
(
elRef
);
state
.
intersectionObserverInstance
.
unobserve
(
el
.
$el
);
}
}
}
return
{
elRef
,
...
toRefs
(
state
),
};
},
});
</
script
>
<
style
lang=
"less"
>
.lazy-container-enter {
opacity: 0;
}
.lazy-container-enter-to {
opacity: 1;
}
.lazy-container-enter-from,
.lazy-container-enter-active {
position: absolute;
top: 0;
width: 100%;
transition: opacity 0.3s 0.2s;
}
.lazy-container-leave {
opacity: 1;
}
.lazy-container-leave-to {
opacity: 0;
}
.lazy-container-leave-active {
transition: opacity 0.5s;
}
</
style
>
src/router/menus/modules/demo/comp.ts
浏览文件 @
fdeaa00b
...
@@ -48,6 +48,10 @@ const menu: MenuModule = {
...
@@ -48,6 +48,10 @@ const menu: MenuModule = {
path
:
'
desc
'
,
path
:
'
desc
'
,
name
:
'
详情组件
'
,
name
:
'
详情组件
'
,
},
},
{
path
:
'
lazy
'
,
name
:
'
懒加载组件
'
,
},
{
{
path
:
'
verify
'
,
path
:
'
verify
'
,
name
:
'
验证组件
'
,
name
:
'
验证组件
'
,
...
...
src/router/routes/modules/demo/comp.ts
浏览文件 @
fdeaa00b
...
@@ -99,7 +99,14 @@ export default {
...
@@ -99,7 +99,14 @@ export default {
title
:
'
详情组件
'
,
title
:
'
详情组件
'
,
},
},
},
},
{
path
:
'
/lazy
'
,
name
:
'
lazyDemo
'
,
component
:
()
=>
import
(
'
/@/views/demo/comp/lazy/index.vue
'
),
meta
:
{
title
:
'
懒加载组件
'
,
},
},
{
{
path
:
'
/verify
'
,
path
:
'
/verify
'
,
name
:
'
VerifyDemo
'
,
name
:
'
VerifyDemo
'
,
...
...
src/views/demo/comp/lazy/TargetContent.vue
0 → 100644
浏览文件 @
fdeaa00b
<
template
>
<Card
hoverable
:style=
"
{ width: '240px', background: '#fff' }">
<template
#cover
>
<img
alt=
"example"
src=
"https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png"
/>
</
template
>
<CardMeta
title=
"懒加载组件"
/>
</Card>
</template>
<
script
lang=
"ts"
>
import
{
defineComponent
}
from
'
vue
'
;
import
{
Card
}
from
'
ant-design-vue
'
;
export
default
defineComponent
({
components
:
{
CardMeta
:
Card
.
Meta
,
Card
},
setup
()
{
return
{};
},
});
</
script
>
src/views/demo/comp/lazy/index.vue
0 → 100644
浏览文件 @
fdeaa00b
<
template
>
<div
class=
"p-4 lazy-base-demo"
>
<Alert
message=
"基础示例"
description=
"向下滚动到可见区域才会加载组件"
type=
"info"
show-icon
/>
<div
class=
"lazy-base-demo-wrap"
>
<h1>
向下滚动
</h1>
<LazyContainer
@
init=
"() =>
{}">
<TargetContent
/>
<template
#skeleton
>
<Skeleton
:rows=
"10"
/>
</
template
>
</LazyContainer>
</div>
</div>
</template>
<
script
lang=
"ts"
>
import
{
defineComponent
}
from
'
vue
'
;
import
{
Skeleton
,
Alert
}
from
'
ant-design-vue
'
;
import
TargetContent
from
'
./TargetContent.vue
'
;
import
{
LazyContainer
}
from
'
/@/components/Container/index
'
;
export
default
defineComponent
({
components
:
{
LazyContainer
,
TargetContent
,
Skeleton
,
Alert
},
setup
()
{
return
{};
},
});
</
script
>
<
style
lang=
"less"
scoped
>
.lazy-base-demo {
&-wrap {
display: flex;
width: 50%;
height: 2000px;
margin: 20px auto;
text-align: center;
background: #fff;
justify-content: center;
flex-direction: column;
align-items: center;
}
h1 {
height: 1300px;
margin: 20px 0;
}
}
</
style
>
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录