diff --git a/package.json b/package.json index 3d8c75e..514a4d8 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "rollup-plugin-node-resolve": "^3.2.0", "rollup-plugin-replace": "^2.0.0", "rollup-plugin-uglify": "^1.0.1", - "vue": "^2.5.16" + "vue": "^2.5.16", + "vue3": "npm:vue@^3.0.0" } } diff --git a/src/components/index.js b/src/components/index.js new file mode 100644 index 0000000..483dd03 --- /dev/null +++ b/src/components/index.js @@ -0,0 +1,12 @@ +import Vue3LazyComponent from './vue3/vue3-lazy-component' +import Vue3LazyImage from './vue3/vue3-lazy-image' +import LazyComponent from './lazy-component' +import LazyImage from './lazy-image' + +export function getLazyImage (tag, lazy) { + return 'isVue3' === tag ? Vue3LazyComponent(lazy) : LazyImage(lazy) +} + +export function getLazyComponent(tag, lazy) { + return 'isVue3' === tag ? Vue3LazyComponent(lazy) : LazyComponent(lazy) +} diff --git a/src/lazy-component.js b/src/components/lazy-component.js similarity index 96% rename from src/lazy-component.js rename to src/components/lazy-component.js index 7b57fcf..9599184 100644 --- a/src/lazy-component.js +++ b/src/components/lazy-component.js @@ -1,4 +1,4 @@ -import { inBrowser } from './util' +import { inBrowser } from '../util' export default (lazy) => { return { diff --git a/src/lazy-image.js b/src/components/lazy-image.js similarity index 99% rename from src/lazy-image.js rename to src/components/lazy-image.js index c16d0e2..d603aba 100644 --- a/src/lazy-image.js +++ b/src/components/lazy-image.js @@ -2,7 +2,7 @@ import { inBrowser, loadImageAsync, noop -} from './util' +} from '../util' export default (lazyManager) => { return { diff --git a/src/components/vue3/vue3-lazy-component.js b/src/components/vue3/vue3-lazy-component.js new file mode 100644 index 0000000..72f17cc --- /dev/null +++ b/src/components/vue3/vue3-lazy-component.js @@ -0,0 +1,59 @@ +import { inBrowser } from '../../util' +import { + h, + ref, + getCurrentInstance, + onMounted, + onBeforeUnmount +} from 'vue3' + +export default (lazy) => { + return { + props: { + tag: { + type: String, + default: 'div' + } + }, + setup(props, { slots, emit }) { + const show = ref(false) + const state = { loaded: false } + const rect = {} + const dom = getCurrentInstance() + + const that = { + el: null, + state: state, + rect: rect, + show: show, + getRect () { + this.rect = dom.refs.root.getBoundingClientRect() + }, + checkInView () { + this.getRect() + return inBrowser && + (this.rect.top < window.innerHeight * lazy.options.preLoad && this.rect.bottom > 0) && + (this.rect.left < window.innerWidth * lazy.options.preLoad && this.rect.right > 0) + }, + load () { + this.show.value = true + this.state.loaded = true + emit('show', this) + } + } + + onMounted(() => { + that.el = getCurrentInstance().refs.root + lazy.addLazyBox(that) + lazy.lazyLoadHandler() + }) + + onBeforeUnmount(() => { + lazy.removeComponent(that) + }) + + return () => + h(props.tag, { ref: 'root' }, show ? slots.default() : null) + } + } +} diff --git a/src/components/vue3/vue3-lazy-image.js b/src/components/vue3/vue3-lazy-image.js new file mode 100644 index 0000000..d1026e7 --- /dev/null +++ b/src/components/vue3/vue3-lazy-image.js @@ -0,0 +1,112 @@ +import { + inBrowser, + loadImageAsync, + noop +} from '../../util' +import { + h, + ref, + getCurrentInstance, + watch, + onMounted, + onBeforeUnmount +} from 'vue3' + +export default (lazyManager) => { + return { + props: { + src: [String, Object], + tag: { + type: String, + default: 'img' + } + }, + setup(props, { slots }) { + const renderSrc = ref('') + const options = { + src: '', + error: '', + loading: '', + attempt: lazyManager.options.attempt + } + const state = { + loaded: false, + error: false, + attempt: 0 + } + const rect = {} + const dom = getCurrentInstance() + + const that = { + state: state, + options: options, + rect: rect, + renderSrc: renderSrc, + getRect () { + this.rect = dom.refs.root.getBoundingClientRect() + }, + checkInView () { + this.getRect() + return inBrowser && + (this.rect.top < window.innerHeight * lazyManager.options.preLoad && this.rect.bottom > 0) && + (this.rect.left < window.innerWidth * lazyManager.options.preLoad && this.rect.right > 0) + }, + load (onFinish = noop) { + if ((this.state.attempt > this.options.attempt - 1) && this.state.error) { + if (!lazyManager.options.silent) console.log(`VueLazyload log: ${this.options.src} tried too more than ${this.options.attempt} times`) + onFinish() + return + } + const src = this.options.src + loadImageAsync({ src }, ({ src }) => { + this.renderSrc.value = src + this.state.loaded = true + }, e => { + this.state.attempt++ + this.renderSrc.value = this.options.error + this.state.error = true + }) + }, + $destroy() {} + } + + vueImage.lazyManager = lazyManager + vueImage.init(state, options, renderSrc) + + watch(() => props,src, () => { + vueImage.init(state, options, renderSrc) + lazyManager.addLazyBox(that) + lazyManager.lazyLoadHandler() + }) + + onMounted(() => { + that.el = getCurrentInstance().refs.root + lazyManager.addLazyBox(that) + lazyManager.lazyLoadHandler() + }) + + onBeforeUnmount(() => { + lazyManager.removeComponent(that) + }) + + return () => + h(props.tag,{ + ref: 'root', + attrs: { + src: renderSrc + } + }, slots.default()) + } + } +} + +const vueImage = { + init(state, options, renderSrc) { + const opt = this.lazyManage._valueFormatter(src) + state.loaded = false + options.src = opt.src + options.error = opt.error + options.loading = opt.loading + renderSrc.value = options.loading + } +} diff --git a/src/index.js b/src/index.js index b3dc1eb..289c150 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,89 @@ import Lazy from './lazy' -import LazyComponent from './lazy-component' import LazyContainer from './lazy-container' -import LazyImage from './lazy-image' +import { getLazyImage, getLazyComponent } from './components' import { assign } from './util' +import { nextTick } from 'vue3' + +const pluginScheam = { + isVue3: (app, lazy, lazyContainer) => { + app.provide('lazyload', lazy) + app.directive('lazy', { + beforeMount(el, binding, vnode) { + lazy.add.call(lazy, el, binding, vnode) + }, + beforeUpdate(el, binding, vnode) { + lazy.update.call(lazy, el, binding, vnode) + }, + updated() { + lazy.lazyLoadHandler.call(lazy) + }, + unmounted(el) { + lazy.remove.call(lazy, el) + } + }) + app.directive('lazy-container', { + beforeMount(el, binding, vnode) { + lazyContainer.bind.call(lazyContainer, el, binding, vnode) + }, + updated(el, binding, vnode) { + lazyContainer.update.call(lazyContainer, el, binding, vnode) + }, + unmounted(el) { + lazyContainer.unbind.call(lazyContainer, el) + } + }) + }, + isVue2: (Vue, lazy, lazyContainer) => { + Vue.prototype.$Lazyload = lazy + Vue.directive('lazy', { + bind: lazy.add.bind(lazy), + update: lazy.update.bind(lazy), + componentUpdated: lazy.lazyLoadHandler.bind(lazy), + unbind: lazy.remove.bind(lazy) + }) + Vue.directive('lazy-container', { + bind: lazyContainer.bind.bind(lazyContainer), + componentUpdated: lazyContainer.update.bind(lazyContainer), + unbind: lazyContainer.unbind.bind(lazyContainer) + }) + }, + isVue1: (Vue, lazy, lazyContainer) => { + Vue.prototype.$Lazyload = lazy + Vue.directive('lazy', { + bind: lazy.lazyLoadHandler.bind(lazy), + update (newValue, oldValue) { + assign(this.vm.$refs, this.vm.$els) + lazy.add(this.el, { + modifiers: this.modifiers || {}, + arg: this.arg, + value: newValue, + oldValue: oldValue + }, { + context: this.vm + }) + }, + unbind () { + lazy.remove(this.el) + } + }) + + Vue.directive('lazy-container', { + update (newValue, oldValue) { + lazyContainer.update(this.el, { + modifiers: this.modifiers || {}, + arg: this.arg, + value: newValue, + oldValue: oldValue + }, { + context: this.vm + }) + }, + unbind () { + lazyContainer.unbind(this.el) + } + }) + } +} export default { /* @@ -10,69 +91,35 @@ export default { * @param {Vue} Vue * @param {object} options lazyload options */ - install (Vue, options = {}) { + install (_Vue, options = {}) { + let vueVer = 'isVue2' + const Vue = Object.create(_Vue) + + switch (Vue.version.split('.')[0]) { + case '3': + Vue.nextTick = nextTick + vueVer = 'isVue3' + break + case '2': + vueVer = 'isVue2' + break + default: + vueVer = 'isVue1' + break + } + const LazyClass = Lazy(Vue) const lazy = new LazyClass(options) const lazyContainer = new LazyContainer({ lazy }) - const isVue2 = Vue.version.split('.')[0] === '2' - - Vue.prototype.$Lazyload = lazy - if (options.lazyComponent) { - Vue.component('lazy-component', LazyComponent(lazy)) + Vue.component('lazy-component', getLazyComponent(vueVer, lazy)) } if (options.lazyImage) { - Vue.component('lazy-image', LazyImage(lazy)) + Vue.component('lazy-image', getLazyImage(vueVer, lazy)) } - if (isVue2) { - Vue.directive('lazy', { - bind: lazy.add.bind(lazy), - update: lazy.update.bind(lazy), - componentUpdated: lazy.lazyLoadHandler.bind(lazy), - unbind: lazy.remove.bind(lazy) - }) - Vue.directive('lazy-container', { - bind: lazyContainer.bind.bind(lazyContainer), - componentUpdated: lazyContainer.update.bind(lazyContainer), - unbind: lazyContainer.unbind.bind(lazyContainer) - }) - } else { - Vue.directive('lazy', { - bind: lazy.lazyLoadHandler.bind(lazy), - update (newValue, oldValue) { - assign(this.vm.$refs, this.vm.$els) - lazy.add(this.el, { - modifiers: this.modifiers || {}, - arg: this.arg, - value: newValue, - oldValue: oldValue - }, { - context: this.vm - }) - }, - unbind () { - lazy.remove(this.el) - } - }) - - Vue.directive('lazy-container', { - update (newValue, oldValue) { - lazyContainer.update(this.el, { - modifiers: this.modifiers || {}, - arg: this.arg, - value: newValue, - oldValue: oldValue - }, { - context: this.vm - }) - }, - unbind () { - lazyContainer.unbind(this.el) - } - }) - } + pluginScheam[vueVer](Vue, lazy, lazyContainer) } }