使用ResizeObserver制作响应式Vue组件
使用ResizeObserver制作响应式Vue组件前言一提到制作响应式组件或布局,脑海里首先想到的是通过@media查询来控制,但是有一个问题,它能满足你的需求么?大多数情况下可以很好的解决问题,有时也会不灵验。已一个例子作为说明。假设你要创建一个postItem组件,在大屏上post是这样的显示效果在手机上我需要这样的效果第一反应就是想到媒体查询,根据页面的宽度来控...
·
每个项目产品都会让你加埋点,你是愿意花几天一个个加,还是愿意几分钟一个小时加完去喝茶聊天?来试试这520web工具, 高效加埋点,目前我们公司100号前端都在用,因为很好用,所以很自然普及开来了,推荐给大家吧
自己开发所以免费,埋点越多越能节约时间,点两下埋点就加上了,还不会犯错,里面有使用视频,反正免费 😄
使用ResizeObserver制作响应式Vue组件
前言
- 一提到制作响应式组件或布局,脑海里首先想到的是通过@media查询来控制,但是有一个问题,它能满足你的需求么?大多数情况下可以很好的解决问题,有时也会不灵验。已一个例子作为说明。
- 假设你要创建一个postItem组件,在大屏上post是这样的显示效果
- 在手机上我需要这样的效果
- 第一反应就是想到媒体查询,根据页面的宽度来控制样式,于是就有了下面的样式。
@media only screen and (max-width: 576px) {
.post__item {
flex-direction: column;
}
.post__image {
flex: 0 auto;
height: auto;
}
}
复制代码
- 组件具有重用性,不是一次买卖,某天需求变了要在一个页面上根据post的类别来显示,效果如下
- @media查询的最大问题是,组件响应性基于屏幕大小,但应基于其自身大小,这时原来的媒体查询就不灵验了。在这种情况下,组件布局仅取决于它们。这些组件应该是原子的,独立地确定它们自己的大小并使其适应布局。构建响应式组件,ResizeObserver是个不错的选则。
介绍ResizeObserver
- ResizeObserver:是一项新的功能,监听元素的内容矩形大小的变更,并通知做出相应的反应。和document.onresize的功能很相似。
const observer = new ResizeObserver(entries => {
entries.forEach(entry => {
const cr = entry.contentRect;
console.log('Element:', entry.target);
console.log(`Element size: ${cr.width}px x ${cr.height}px`);
console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
})
})
observer.observe(someElement)
复制代码
- 浏览器的支持性,如图所示
- 虽然目前主流浏览器对ResizeObserver不支持,庆幸的是,ResizeObserver有基于MutationObserver的polyfill,而主流浏览器对MutationObserv是支持的
使用
- 作为组件
<template>
<Responsive :breakpoints="{
small: el => el.width <= 500
}">
<div slot-scope="el" :class="['post__item', { small: el.is.small }]">
<img class="post__image" :src="post.image" />
<div class="post__text">{{post.text}}</div>
</div>
</Responsive>
</template>
<script>
import { Responsive } from "vue-responsive-components"
export default {
props: ['post'],
components: { Responsive }
}
</script>
<style lang="scss">
.post__item {
display: flex;
}
.post__image {
flex: 0 0 200px;
height: 200px;
}
.post__item.small {
flex-direction: column;
.post__image {
flex: 0 auto;
height: auto;
}
}
</style>
复制代码
- 作为指令
<template>
<!-- Will add/remove .small if the width is less / greater -->
<div class="post__item" v-responsive="{ small: el => el.width <= 500 }">
<img class="post__image" :src="post.image" />
<div class="post__text">{{post.text}}</div>
</div>
</template>
<script>
import { ResponsiveDirective } from "vue-responsive-components"
export default {
props: ["post"],
directives: {
responsive: ResponsiveDirective
}
}
</script>
复制代码
插件代码实现
- npm install resize-observer-polyfill --save-dev
- npm install loadsh --save-dev
import throttle from "lodash.throttle"
import ResizeObserver from "resize-observer-polyfill"
export const ResponsiveMixin = {
data() {
return {
el: {
width: 0,
height: 0,
is: {}
}
}
},
mounted() {
if (
typeof process === "undefined" ||
(!process.server && (this.breakpoints || this.$options.breakpoints))
) {
this.$nextTick(() => {
const handleResize = throttle(entries => {
const cr = entries[0].contentRect
;(this.el.width = cr.width), (this.el.height = cr.height)
const conds = Object.assign(
{},
this.breakpoints || {},
this.$options.breakpoints || {}
)
for (const breakpoint in conds) {
this.$set(this.el.is, breakpoint, conds[breakpoint](this.el))
}
}, 200)
const observer = new ResizeObserver(handleResize)
if (this.$el instanceof Element) {
observer.observe(this.$el)
}
})
}
}
}
export const Responsive = {
data() {
return { init: false }
},
props: {
noHide: { type: Boolean, default: false },
breakpoints: { type: Object, default: undefined }
},
mixins: [ResponsiveMixin],
render(h) {
const slot =
(this.$scopedSlots.default && this.$scopedSlots.default(this.el)) ||
this.$slots.default
return !this.noHide && !this.init
? h(
"div",
{
style: { visibility: "hidden" }
},
[slot]
)
: slot
},
mounted() {
this.init = true
}
}
export const ResponsiveDirective = {
inserted(el, conds) {
if (typeof process === "undefined" || !process.server) {
const handleResize = throttle(entries => {
const cr = entries[0].contentRect
for (const breakpoint in conds.value) {
if (conds.value[breakpoint](cr)) {
el.classList.add(breakpoint)
} else {
el.classList.remove(breakpoint)
}
}
}, 200)
const observer = new ResizeObserver(handleResize)
observer.observe(el)
}
}
}
export const VueResponsiveComponents = Vue => {
Vue.component("Responsive", Responsive)
Vue.directive("responsive", ResponsiveDirective)
}
复制代码
总结
- ResizeObserver对响应式布局提供了一种新颖的解决思路,让组件保持了原子性、独立性,同样的思路也适用于angular、react等组件写法(注意需根据angular、react语法规则更改)。
作者:chen小白
链接:https://juejin.im/post/5b4f0c56e51d4518ef2cda0f
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
更多推荐
已为社区贡献14条内容
所有评论(0)