├── .browserslistrc ├── .commitlintrc.json ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .huskyrc.js ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .stylelintignore ├── .stylelintrc.js ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── babel.config.js ├── build ├── rollup.config.vue2.js └── rollup.config.vue3.js ├── changelog.config.js ├── dev ├── vue2 │ ├── .nvmrc │ ├── cdn_usage.html │ ├── package.json │ ├── serve.js │ ├── serve.vue │ └── yarn.lock └── vue3 │ ├── .nvmrc │ ├── cdn_usage.html │ ├── package.json │ ├── serve.js │ ├── serve.vue │ └── yarn.lock ├── dist ├── vue2 │ ├── index.js │ ├── v-lazy-component.cjs │ ├── v-lazy-component.css │ ├── v-lazy-component.global.js │ ├── v-lazy-component.global.min.js │ ├── v-lazy-component.min.cjs │ ├── v-lazy-component.min.mjs │ ├── v-lazy-component.mjs │ ├── v-lazy-component.umd.js │ └── v-lazy-component.umd.min.js └── vue3 │ ├── index.js │ ├── v-lazy-component.cjs │ ├── v-lazy-component.css │ ├── v-lazy-component.global.js │ ├── v-lazy-component.global.min.js │ ├── v-lazy-component.min.cjs │ ├── v-lazy-component.min.mjs │ ├── v-lazy-component.mjs │ ├── v-lazy-component.umd.js │ └── v-lazy-component.umd.min.js ├── docs ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmrc ├── .nvmrc ├── README.md ├── app.config.ts ├── components │ ├── AppCard.vue │ ├── AppDemo.vue │ └── AppSpinner.vue ├── content │ ├── 0.index.md │ ├── 1.guide │ │ ├── 0.index.md │ │ ├── 1.registration │ │ │ ├── 0.vue3.md │ │ │ └── 1.vue2.md │ │ ├── 2.usage.md │ │ └── _dir.yml │ └── 2.demo.md ├── nuxt.config.ts ├── package.json ├── public │ ├── favicon.ico │ └── media │ │ └── logo.png ├── renovate.json ├── tokens.config.ts ├── tsconfig.json └── yarn.lock ├── meta └── logo.png ├── package.json ├── shims-vue.d.ts ├── src ├── vue2 │ ├── entry.js │ ├── v-lazy-component.scss │ └── v-lazy-component.vue └── vue3 │ ├── entry.js │ ├── v-lazy-component.scss │ └── v-lazy-component.vue ├── tsconfig.json ├── vue2 ├── README.md └── index.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | current node 2 | last 2 versions and > 2% 3 | ie > 10 4 | -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | end_of_line = crlf 7 | charset = utf-8 8 | indent_size = 2 9 | tab_width = 2 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | /vue2 3 | /vue3 4 | /docs 5 | /meta 6 | coverage 7 | node_modules 8 | package.json 9 | package-lock.json 10 | yarn.lock 11 | pnpm-lock.yaml 12 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | jest: true 7 | }, 8 | parser: 'vue-eslint-parser', 9 | parserOptions: { 10 | parser: '@typescript-eslint/parser', 11 | sourceType: 'module', 12 | ecmaFeatures: { 13 | legacyDecorators: true 14 | }, 15 | warnOnUnsupportedTypeScriptVersion: false, 16 | templateTokenizer: { 17 | pug: 'vue-eslint-parser-template-tokenizer-pug' 18 | } 19 | }, 20 | extends: ['plugin:vue/strongly-recommended', 'eslint-config-prettier', 'prettier'], 21 | plugins: ['@typescript-eslint', 'prettier'], 22 | rules: { 23 | 'prettier/prettier': [ 24 | 'error', 25 | { 26 | endOfLine: 'crlf' 27 | } 28 | ], 29 | 'max-len': [ 30 | 'error', 31 | { 32 | code: 150, 33 | ignoreComments: true, 34 | ignoreStrings: true, 35 | ignoreTemplateLiterals: true 36 | } 37 | ], 38 | 'no-multiple-empty-lines': [2, { max: 2 }], 39 | semi: ['error', 'never'], 40 | 'arrow-parens': ['error', 'as-needed'], 41 | 'no-extend-native': 'off', 42 | 'space-before-function-paren': 'off', 43 | camelcase: 'off', 44 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 45 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 46 | 'no-throw-literal': 'off', 47 | 'no-param-reassign': [ 48 | 'error', 49 | { 50 | props: false 51 | } 52 | ], 53 | '@typescript-eslint/no-var-requires': 'off', 54 | 'vue/component-definition-name-casing': ['error', 'PascalCase'], 55 | 'vue/multi-word-component-names': 'off', 56 | 'vue/component-tags-order': ['error', { order: ['template', 'script', 'style', 'docs'] }], 57 | 'vue/padding-line-between-blocks': ['error'], 58 | 'vue/block-lang': [ 59 | 'error', 60 | { 61 | script: { 62 | allowNoLang: true, 63 | lang: 'ts' 64 | }, 65 | style: { 66 | allowNoLang: true, 67 | lang: 'scss' 68 | } 69 | } 70 | ], 71 | 'vue/no-empty-component-block': 'off', 72 | 'vue/valid-template-root': 'off', 73 | 'vue/no-static-inline-styles': 'off', 74 | 'vue/require-prop-types': ['error'], 75 | 'vue/require-default-prop': ['error'], 76 | 'vue/attribute-hyphenation': ['error', 'always'], 77 | 'vue/v-on-event-hyphenation': ['error', 'always'], 78 | 'vue/html-self-closing': 'off', 79 | 'vue/no-v-html': 'off', 80 | 'vue/order-in-components': ['error'], 81 | 'padding-line-between-statements': [ 82 | 'error', 83 | { blankLine: 'always', prev: '*', next: 'return' }, 84 | { blankLine: 'always', prev: '*', next: 'if' }, 85 | { blankLine: 'always', prev: '*', next: 'switch' }, 86 | { blankLine: 'always', prev: '*', next: 'for' }, 87 | { blankLine: 'always', prev: '*', next: 'function' }, 88 | { blankLine: 'never', prev: 'import', next: 'import' }, 89 | { blankLine: 'always', prev: 'import', next: 'export' }, 90 | { blankLine: 'always', prev: 'expression', next: 'export' }, 91 | { blankLine: 'always', prev: 'import', next: 'expression' } 92 | ] 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # This file has been generated using '.gitattributes Generator'. 2 | # You can generate yours at http://ihopepeace.github.io/gitattributes_generator 3 | 4 | * text=auto 5 | 6 | # Explicitly declare text files that should be normalized and converted 7 | # to native line endings on checkout. 8 | 9 | # Declare files that should have CRLF line endings on checkout. 10 | *.js eol=crlf 11 | *.ts eol=crlf 12 | *.vue eol=crlf 13 | *.css eol=crlf 14 | *.scss eol=crlf 15 | *.json eol=crlf 16 | 17 | # Declare files that should have LF line endings on checkout. 18 | 19 | # Declare files that are truly binary and shouldn't be modified. 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Node template 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Dependency directories 34 | node_modules/ 35 | jspm_packages/ 36 | package-lock.json 37 | 38 | # TypeScript v1 declaration files 39 | typings/ 40 | 41 | # Optional npm cache directory 42 | .npm 43 | 44 | # Optional eslint cache 45 | .eslintcache 46 | 47 | # Optional REPL history 48 | .node_repl_history 49 | 50 | # Output of 'npm pack' 51 | *.tgz 52 | 53 | # Yarn Integrity file 54 | .yarn-integrity 55 | 56 | # dotenv environment variables file 57 | .env 58 | 59 | # parcel-bundler cache (https://parceljs.org/) 60 | .cache 61 | 62 | # next.js build output 63 | .next 64 | 65 | # nuxt.js build output 66 | .nuxt 67 | 68 | # vuepress build output 69 | .vuepress/dist 70 | 71 | # Serverless directories 72 | .serverless 73 | 74 | # IDE 75 | .idea 76 | 77 | # Service worker 78 | sw.* 79 | 80 | #misc 81 | demo/node_modules 82 | -------------------------------------------------------------------------------- /.huskyrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | hooks: { 3 | 'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS', 4 | 'pre-commit': 'lint-staged' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ./demo 2 | ./dev 3 | ./docs 4 | .vscode 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.x.x 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | /vue2 3 | /vue3 4 | /docs 5 | /meta 6 | coverage 7 | node_modules 8 | package.json 9 | package-lock.json 10 | yarn.lock 11 | pnpm-lock.yaml 12 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pugPrintWidth: 140, 3 | pugSingleQuote: false, 4 | pugAttributeSeparator: 'none', 5 | pugEmptyAttributes: 'none', 6 | singleQuote: true, 7 | printWidth: 140, 8 | trailingComma: 'none', 9 | tabWidth: 2, 10 | semi: false, 11 | bracketSpacing: true, 12 | arrowParens: 'avoid', 13 | pugSortAttributesBeginning: [ 14 | '^cols$', 15 | '^v-else$', 16 | '^v-for$', 17 | '^:key$', 18 | '^v-if$', 19 | '^v-else-if$', 20 | '^v-on$', 21 | '^v-bind$', 22 | '^ref$', 23 | '^v-model', 24 | '^name$', 25 | '^:?type$', 26 | '^:value$', 27 | '^v-text$', 28 | '^:?label$', 29 | '^:headers$', 30 | '^:items$', 31 | '^:?item-text$', 32 | '^:?item-value$', 33 | '^:?item-disabled$', 34 | '^:?placeholder$', 35 | '^:?src$', 36 | '^:?color$', 37 | '^:?text-color$', 38 | '^:?icon$', 39 | '^:?small$' 40 | ], 41 | pugSortAttributesEnd: [ 42 | '^:?hint$', 43 | '^:?persistent-hint$', 44 | '^prepend-', 45 | '^@click:prepend', 46 | '^append-', 47 | '^@click:append', 48 | '^:to$', 49 | '^exact$', 50 | '^:(?!(width|height|loading|disabled|data-))', 51 | '^target$', 52 | '^:?width$', 53 | '^:?height$', 54 | '^:loading$', 55 | '^:disabled$', 56 | '^:?data-', 57 | '^@click', 58 | '^@' 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | dist 2 | /vue2 3 | /docs 4 | /meta 5 | *.min.* 6 | assets/**/*/vendor 7 | static/ 8 | public/ 9 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | //https://github.com/constverum/stylelint-config-rational-order 4 | 'stylelint-config-rational-order', 5 | // https://github.com/shannonmoeller/stylelint-config-prettier 6 | 'stylelint-config-prettier' 7 | ], 8 | overrides: [ 9 | { 10 | customSyntax: 'postcss-scss', 11 | files: ['**/*.{css,sass,scss,less,stylus}'] 12 | }, 13 | { 14 | customSyntax: 'postcss-html', 15 | files: ['*.vue', '**/*.vue'] 16 | } 17 | ], 18 | plugins: [ 19 | // https://github.com/kristerkari/stylelint-scss#list-of-rules 20 | 'stylelint-scss', 21 | // https://github.com/hudochenkov/stylelint-order 22 | 'stylelint-order', 23 | 'stylelint-config-rational-order/plugin' 24 | ], 25 | rules: { 26 | indentation: [ 27 | 2, 28 | { 29 | baseIndentLevel: 0 30 | } 31 | ], 32 | 'color-named': 'never', 33 | 'color-function-notation': 'legacy', 34 | 'at-rule-no-unknown': null, 35 | 'declaration-empty-line-before': [ 36 | 'always', 37 | { 38 | except: ['after-declaration'], 39 | ignore: ['after-comment', 'first-nested', 'inside-single-line-block'], 40 | severity: 'warning' 41 | } 42 | ], 43 | 'rule-empty-line-before': [ 44 | 'always', 45 | { 46 | ignore: ['after-comment', 'first-nested'], 47 | severity: 'warning' 48 | } 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "stylelint.vscode-stylelint", 5 | "editorconfig.editorconfig", 6 | "octref.vetur", 7 | "fallenmax.mithril-emmet" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "emmet.showAbbreviationSuggestions": true, 3 | "emmet.showExpandedAbbreviation": "always", 4 | "emmet.includeLanguages": { 5 | "vue-html": "html", 6 | "vue": "jade" 7 | }, 8 | "files.insertFinalNewline": true, 9 | "editor.insertSpaces": true, 10 | "editor.detectIndentation": false, 11 | "css.validate": false, 12 | "less.validate": false, 13 | "scss.validate": false, 14 | "stylelint.validate": ["css", "scss", "html", "vue"], 15 | "editor.codeActionsOnSave": { 16 | "source.fixAll.stylelint": true, 17 | "source.fixAll.eslint": true 18 | }, 19 | "editor.tabSize": 2, 20 | "explorer.compactFolders": false 21 | } 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## v3.0.9 4 | 5 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/4155b1f...v3.0.9) 6 | 7 | 8 | ### 🏡 Chore 9 | 10 | - Rebrand ([6c7889b](https://github.com/selimdoyranli/v-lazy-component/commit/6c7889b)) 11 | 12 | ### ❤️ Contributors 13 | 14 | - Selimdoyranli ([@selimdoyranli](http://github.com/selimdoyranli)) 15 | 16 | ## v3.0.8 17 | 18 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/v3.0.7...v3.0.8) 19 | 20 | 21 | ### 🏡 Chore 22 | 23 | - Rebranding ([4d75a9e](https://github.com/selimdoyranli/v-lazy-component/commit/4d75a9e)) 24 | 25 | ### ❤️ Contributors 26 | 27 | - Selimdoyranli ([@selimdoyranli](http://github.com/selimdoyranli)) 28 | 29 | ## v3.0.7 30 | 31 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/v3.0.6...v3.0.7) 32 | 33 | 34 | ### 🏡 Chore 35 | 36 | - Update npm scripts ([a7f8eb7](https://github.com/selimdoyranli/v-lazy-component/commit/a7f8eb7)) 37 | 38 | ### ❤️ Contributors 39 | 40 | - Selimdoyranli ([@selimdoyranli](http://github.com/selimdoyranli)) 41 | 42 | ## v3.0.6 43 | 44 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/v3.0.5...v3.0.6) 45 | 46 | 47 | ### 💅 Refactors 48 | 49 | - **build:** Refactor build ([787c607](https://github.com/selimdoyranli/v-lazy-component/commit/787c607)) 50 | 51 | ### ❤️ Contributors 52 | 53 | - Selimdoyranli ([@selimdoyranli](http://github.com/selimdoyranli)) 54 | 55 | ## v3.0.5 56 | 57 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/v3.0.4...v3.0.5) 58 | 59 | 60 | ### 💅 Refactors 61 | 62 | - Structure improvements ([a4de217](https://github.com/selimdoyranli/v-lazy-component/commit/a4de217)) 63 | - Pug & scss ([6f3eb96](https://github.com/selimdoyranli/v-lazy-component/commit/6f3eb96)) 64 | 65 | ### 🏡 Chore 66 | 67 | - Add nvmrc files ([af20558](https://github.com/selimdoyranli/v-lazy-component/commit/af20558)) 68 | 69 | ### 🎨 Styles 70 | 71 | - Code formatting ([734250e](https://github.com/selimdoyranli/v-lazy-component/commit/734250e)) 72 | 73 | ### ❤️ Contributors 74 | 75 | - Selimdoyranli 76 | 77 | ## v3.0.4 78 | 79 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/v3.0.3...v3.0.4) 80 | 81 | 82 | ### 🩹 Fixes 83 | 84 | - Build fix for docs ([660ad97](https://github.com/selimdoyranli/v-lazy-component/commit/660ad97)) 85 | 86 | ### ❤️ Contributors 87 | 88 | - Selimdoyranli 89 | 90 | ## v3.0.3 91 | 92 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/v3.0.2...v3.0.3) 93 | 94 | 95 | ### 📖 Documentation 96 | 97 | - Docs & demo website ([0f1b0bf](https://github.com/selimdoyranli/v-lazy-component/commit/0f1b0bf)) 98 | 99 | ### 🏡 Chore 100 | 101 | - Update homepage ([dc51e89](https://github.com/selimdoyranli/v-lazy-component/commit/dc51e89)) 102 | 103 | ### ❤️ Contributors 104 | 105 | - Selimdoyranli 106 | 107 | ## v3.0.2 108 | 109 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/v3.0.1...v3.0.2) 110 | 111 | 112 | ### 📖 Documentation 113 | 114 | - Update readme ([4245aa9](https://github.com/selimdoyranli/v-lazy-component/commit/4245aa9)) 115 | 116 | ### ❤️ Contributors 117 | 118 | - Selimdoyranli 119 | 120 | ## v3.0.1 121 | 122 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/v3.0.0...v3.0.1) 123 | 124 | 125 | ### 🩹 Fixes 126 | 127 | - Vue2 entry point export fix ([aa786f7](https://github.com/selimdoyranli/v-lazy-component/commit/aa786f7)) 128 | 129 | ### ❤️ Contributors 130 | 131 | - Selimdoyranli 132 | 133 | ## v3.0.0 134 | 135 | [compare changes](https://github.com/selimdoyranli/v-lazy-component/compare/2.1.1...v3.0.0) 136 | 137 | 138 | ### 🚀 Enhancements 139 | 140 | - Vue3 support ([a657452](https://github.com/selimdoyranli/v-lazy-component/commit/a657452)) 141 | 142 | ### ❤️ Contributors 143 | 144 | - Selimdoyranli 145 | 146 | ## 2.1.1 147 | 148 | - defensive intersection observer checking 149 | 150 | ## 2.1.0 151 | 152 | - Force render feature 153 | - Idle feature 154 | - CSS BEM 155 | 156 | ## 2.0.0 157 | 158 | - local register support 159 | - rollup build structure 160 | - ssr import 161 | - improved demo 162 | - improved doc 163 | 164 | ## 1.0.0 - 1.0.5 165 | 166 | - Doc & demo updates etc. 167 | - First release 168 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 selimdoyranli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version][npm-version-src]][npm-version-href] 2 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 3 | [![changelog][changelog-src]][changelog-href] 4 | [![License][license-src]][license-href] 5 | 6 |

7 | v-lazy-component 8 |

9 | 10 |

11 | Vue component render when visible 👁️⚡️ 12 |

13 | 14 |

15 | 16 | 17 | 18 |

19 | 20 |

21 | GitHub stars 22 |

23 | 24 |

25 | Website 26 |

27 | 28 |
29 | Sponsorship 💖
30 | GitHub
31 | Buy me a coffee
32 |
33 | 34 | ## Features 35 | - ⚡️ Lightweight 36 | - 🎨 Interactive 37 | - 👶🏻 Easy implementation 38 | - 📦 Vue2 & Vue3 support 39 | 40 | ## Getting Started 41 | 42 | ### Try it Online ⚡️ 43 | 44 | [DEMO](https://v-lazy-component.vercel.app/demo) 45 | 46 | 47 | ## Installation 48 | 49 | ```js 50 | yarn add v-lazy-component # or npm i v-lazy-component 51 | ``` 52 | 53 | ### Vue3 54 | 55 | #### Global Register 56 | 57 | ```js 58 | import { createApp } from 'vue' 59 | import App from './App.vue' 60 | import LazyComponent from 'v-lazy-component' 61 | 62 | const app = createApp(App) 63 | 64 | app.use(LazyComponent) 65 | app.mount('#app') 66 | ``` 67 | 68 | #### Local Register 69 | ```html 70 | 73 | ``` 74 | #### Via CDN 75 | ```js 76 | 77 | 78 | 79 | 85 | ``` 86 | 87 | ### Vue2 88 | 89 | #### Global Register 90 | 91 | ```js 92 | import Vue from "vue"; 93 | import LazyComponent from "v-lazy-component/vue2"; 94 | 95 | Vue.use(LazyComponent); 96 | ``` 97 | 98 | #### Local Register 99 | ```js 100 | import LazyComponent from "v-lazy-component/vue2"; 101 | 102 | export default { 103 | components: { 104 | LazyComponent 105 | } 106 | } 107 | ``` 108 | #### Via CDN 109 | ```js 110 | 111 | 112 | 113 | 120 | ``` 121 |   122 | 123 | ### Usage 124 | 125 | ```html 126 | 127 | 128 | 129 | 130 | 133 | 134 | ``` 135 | 136 | idle variant 137 | ```html 138 | .... 139 | 140 | 141 | 142 | 143 | 144 | 145 | 148 | 149 | ``` 150 | 151 | #### Props 152 | 153 | |Name|Description|Type|Default 154 | |--|--|--|--| 155 | |`wrapper-tag` |Html tag of lazy component|String | div 156 | |`is-intersected` |Do not wait observe, Force render |Boolean | false 157 | |`idle` |Do not use observer, wait `is-intersected` prop changes for render |Boolean | false 158 | |`root-margin` |[Intersection Observer API doc](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options)|String | 0px 0px 0px 0px 159 | |`threshold` |[Intersection Observer API doc](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options)|Number, Array| 0 160 | [See Intersection Observer API doc](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 161 | 162 | #### Slots 163 | |`placeholder`| Content that is loaded as a placeholder until it comes into view | 164 | |--|--| 165 | 166 | #### Events 167 | |`intersected`| dispatch event when visible | 168 | |--|--| 169 | 170 | #### CSS Selectors 171 | 172 | ```css 173 | .v-lazy-component.v-lazy-component--loading { 174 | filter: blur(20px); 175 | } 176 | 177 | .v-lazy-component.v-lazy-component--loaded { 178 | filter: blur(0); 179 | transition: filter 1s; 180 | } 181 | ``` 182 | 183 | --- 184 | 185 | ## Development 186 | 187 | ### Vue3 188 | 189 | ```bash 190 | yarn build:vue3 # build for vue3 191 | ``` 192 | 193 | ```bash 194 | # Serve 195 | 196 | cd dev/vue3 197 | 198 | yarn install 199 | yarn serve 200 | ``` 201 | 202 | ### Vue2 203 | 204 | ```bash 205 | yarn build:vue2 # build for vue2 206 | ``` 207 | 208 | ```bash 209 | # Serve 210 | 211 | cd dev/vue2 212 | 213 | yarn install 214 | yarn serve 215 | ``` 216 | 217 | ### Vue 2&3 218 | 219 | ```bash 220 | yarn build # build for vue2 and vue3 221 | ``` 222 | 223 | ## Sponsorship 224 | 225 | You can sponsor me for the continuity of my projects: 226 | 227 |

228 | 229 | 230 | 231 |

232 | 233 |

234 | 235 | 236 | 237 |

238 | 239 | ## License 240 | 241 | [MIT License](./LICENSE) 242 | 243 | Copyright (c) selimdoyranli 244 | 245 | 246 | [npm-version-src]: https://img.shields.io/npm/v/v-lazy-component/latest.svg 247 | [npm-version-href]: https://npmjs.com/package/v-lazy-component 248 | 249 | [npm-downloads-src]: https://img.shields.io/npm/dt/v-lazy-component.svg 250 | [npm-downloads-href]: https://npmjs.com/package/v-lazy-component 251 | 252 | [codecov-src]: https://img.shields.io/codecov/c/github/selimdoyranli/v-lazy-component.svg 253 | [codecov-href]: https://codecov.io/gh/selimdoyranli/v-lazy-component 254 | 255 | [changelog-src]: https://img.shields.io/static/v1?label=%F0%9F%93%96&message=Release%20Notes%20|%20CHANGELOG&color=blue 256 | [changelog-href]: ./CHANGELOG.md 257 | 258 | [license-src]: https://img.shields.io/badge/License-MIT-blue.svg 259 | [license-href]: https://npmjs.com/package/v-lazy-component/LICENSE 260 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const devPresets = ['@vue/babel-preset-app'] 2 | const buildPresets = [ 3 | [ 4 | '@babel/preset-env', 5 | // Config for @babel/preset-env 6 | { 7 | // Example: Always transpile optional chaining/nullish coalescing 8 | // include: [ 9 | // /(optional-chaining|nullish-coalescing)/ 10 | // ], 11 | } 12 | ], 13 | '@babel/preset-typescript' 14 | ] 15 | module.exports = { 16 | presets: process.env.NODE_ENV === 'development' ? devPresets : buildPresets 17 | } 18 | -------------------------------------------------------------------------------- /build/rollup.config.vue2.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | import vue from 'vue-prev-rollup-plugin-vue' 4 | import alias from '@rollup/plugin-alias' 5 | import commonjs from '@rollup/plugin-commonjs' 6 | import replace from '@rollup/plugin-replace' 7 | import babel from '@rollup/plugin-babel' 8 | import css from 'rollup-plugin-css-only' 9 | import { terser } from 'rollup-plugin-terser' 10 | import minimist from 'minimist' 11 | 12 | // Get browserslist config and remove ie from es build targets 13 | const esbrowserslist = fs 14 | .readFileSync('./.browserslistrc') 15 | .toString() 16 | .split('\n') 17 | .filter(entry => entry && entry.substring(0, 2) !== 'ie') 18 | 19 | const argv = minimist(process.argv.slice(2)) 20 | 21 | const projectRoot = path.resolve(__dirname, '..') 22 | 23 | const baseConfig = { 24 | input: 'src/vue2/entry.js', 25 | plugins: { 26 | preVue: [ 27 | alias({ 28 | resolve: ['.js', '.jsx', '.ts', '.tsx', '.vue'], 29 | entries: { 30 | '@': path.resolve(projectRoot, 'src') 31 | } 32 | }) 33 | ], 34 | replace: { 35 | 'process.env.NODE_ENV': JSON.stringify('production'), 36 | 'process.env.ES_BUILD': JSON.stringify('false'), 37 | preventAssignment: true 38 | }, 39 | css: { 40 | output: 'dist/vue2/v-lazy-component.css' 41 | }, 42 | vue: { 43 | css: false, 44 | template: { 45 | isProduction: true 46 | } 47 | }, 48 | babel: { 49 | exclude: 'node_modules/**', 50 | extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'], 51 | babelHelpers: 'bundled' 52 | }, 53 | terser: {} 54 | } 55 | } 56 | 57 | // ESM/UMD/IIFE shared settings: externals 58 | // Refer to https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency 59 | const external = [ 60 | // list external dependencies, exactly the way it is written in the import statement. 61 | // eg. 'jquery' 62 | 'vue' 63 | ] 64 | 65 | // UMD/IIFE shared settings: output.globals 66 | // Refer to https://rollupjs.org/guide/en#output-globals for details 67 | const globals = { 68 | // Provide global variable names to replace your external imports 69 | // eg. jquery: '$' 70 | vue: 'Vue' 71 | } 72 | 73 | // Customize configs for individual targets 74 | const buildFormats = [] 75 | 76 | if (!argv.format || argv.format === 'umd') { 77 | const umdConfig = { 78 | ...baseConfig, 79 | external, 80 | output: [ 81 | { 82 | file: 'vue2/index.js', 83 | format: 'umd', 84 | exports: 'named', 85 | globals, 86 | name: 'LazyComponent', 87 | plugins: [ 88 | terser({ 89 | ...baseConfig.plugins.terser 90 | }) 91 | ] 92 | }, 93 | { 94 | file: 'dist/vue2/index.js', 95 | format: 'umd', 96 | exports: 'named', 97 | globals, 98 | name: 'LazyComponent', 99 | plugins: [ 100 | terser({ 101 | ...baseConfig.plugins.terser 102 | }) 103 | ] 104 | }, 105 | { 106 | file: 'dist/vue2/v-lazy-component.umd.js', 107 | format: 'umd', 108 | exports: 'named', 109 | globals, 110 | name: 'LazyComponent' 111 | }, 112 | { 113 | file: 'dist/vue2/v-lazy-component.umd.min.js', 114 | format: 'umd', 115 | exports: 'named', 116 | globals, 117 | name: 'LazyComponent', 118 | plugins: [ 119 | terser({ 120 | ...baseConfig.plugins.terser 121 | }) 122 | ] 123 | }, 124 | { 125 | file: 'dist/vue2/v-lazy-component.global.js', 126 | format: 'umd', 127 | globals, 128 | name: 'LazyComponent' 129 | }, 130 | { 131 | file: 'dist/vue2/v-lazy-component.global.min.js', 132 | format: 'umd', 133 | globals, 134 | name: 'LazyComponent', 135 | plugins: [ 136 | terser({ 137 | ...baseConfig.plugins.terser 138 | }) 139 | ] 140 | } 141 | ], 142 | plugins: [ 143 | replace({ 144 | ...baseConfig.plugins.replace 145 | }), 146 | ...baseConfig.plugins.preVue, 147 | css({ 148 | ...baseConfig.plugins.css 149 | }), 150 | vue({ 151 | ...baseConfig.plugins.vue 152 | }), 153 | babel({ 154 | ...baseConfig.plugins.babel, 155 | presets: [ 156 | [ 157 | '@babel/preset-env', 158 | { 159 | targets: esbrowserslist 160 | } 161 | ] 162 | ] 163 | }), 164 | commonjs() 165 | ] 166 | } 167 | buildFormats.push(umdConfig) 168 | } 169 | 170 | if (!argv.format || argv.format === 'es') { 171 | const esConfig = { 172 | ...baseConfig, 173 | external, 174 | output: [ 175 | { 176 | file: 'dist/vue2/v-lazy-component.mjs', 177 | format: 'esm', 178 | exports: 'named' 179 | }, 180 | { 181 | file: 'dist/vue2/v-lazy-component.min.mjs', 182 | format: 'esm', 183 | exports: 'named', 184 | plugins: [ 185 | terser({ 186 | ...baseConfig.plugins.terser 187 | }) 188 | ] 189 | } 190 | ], 191 | plugins: [ 192 | replace({ 193 | ...baseConfig.plugins.replace, 194 | 'process.env.ES_BUILD': JSON.stringify('true') 195 | }), 196 | ...baseConfig.plugins.preVue, 197 | css({ 198 | ...baseConfig.plugins.css 199 | }), 200 | vue({ 201 | ...baseConfig.plugins.vue 202 | }), 203 | babel({ 204 | ...baseConfig.plugins.babel, 205 | presets: [ 206 | [ 207 | '@babel/preset-env', 208 | { 209 | targets: esbrowserslist 210 | } 211 | ] 212 | ] 213 | }), 214 | commonjs() 215 | ] 216 | } 217 | buildFormats.push(esConfig) 218 | } 219 | 220 | if (!argv.format || argv.format === 'cjs') { 221 | const cjsConfig = { 222 | ...baseConfig, 223 | external, 224 | output: [ 225 | { 226 | compact: true, 227 | file: 'dist/vue2/v-lazy-component.cjs', 228 | format: 'cjs', 229 | name: 'LazyComponent', 230 | exports: 'named', 231 | globals 232 | }, 233 | { 234 | compact: true, 235 | file: 'dist/vue2/v-lazy-component.min.cjs', 236 | format: 'cjs', 237 | name: 'LazyComponent', 238 | exports: 'named', 239 | globals, 240 | plugins: [ 241 | terser({ 242 | ...baseConfig.plugins.terser 243 | }) 244 | ] 245 | } 246 | ], 247 | plugins: [ 248 | replace(baseConfig.plugins.replace), 249 | ...baseConfig.plugins.preVue, 250 | css({ 251 | ...baseConfig.plugins.css 252 | }), 253 | vue({ 254 | ...baseConfig.plugins.vue, 255 | template: { 256 | ...baseConfig.plugins.vue.template, 257 | optimizeSSR: true 258 | } 259 | }), 260 | babel(baseConfig.plugins.babel), 261 | commonjs() 262 | ] 263 | } 264 | buildFormats.push(cjsConfig) 265 | } 266 | 267 | // Export config 268 | export default buildFormats 269 | -------------------------------------------------------------------------------- /build/rollup.config.vue3.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | import vue from 'vue-next-rollup-plugin-vue' 4 | import alias from '@rollup/plugin-alias' 5 | import commonjs from '@rollup/plugin-commonjs' 6 | import replace from '@rollup/plugin-replace' 7 | import babel from '@rollup/plugin-babel' 8 | import scss from 'rollup-plugin-scss' 9 | import css from 'rollup-plugin-css-only' 10 | import { terser } from 'rollup-plugin-terser' 11 | import minimist from 'minimist' 12 | 13 | // Get browserslist config and remove ie from es build targets 14 | const esbrowserslist = fs 15 | .readFileSync('./.browserslistrc') 16 | .toString() 17 | .split('\n') 18 | .filter(entry => entry && entry.substring(0, 2) !== 'ie') 19 | 20 | const argv = minimist(process.argv.slice(2)) 21 | 22 | const projectRoot = path.resolve(__dirname, '..') 23 | 24 | const baseConfig = { 25 | input: 'src/vue3/entry.js', 26 | plugins: { 27 | preVue: [ 28 | alias({ 29 | resolve: ['.js', '.jsx', '.ts', '.tsx', '.vue'], 30 | entries: { 31 | '@': path.resolve(projectRoot, 'src') 32 | } 33 | }) 34 | ], 35 | replace: { 36 | 'process.env.NODE_ENV': JSON.stringify('production'), 37 | 'process.env.ES_BUILD': JSON.stringify('false'), 38 | preventAssignment: true 39 | }, 40 | scss: { 41 | fileName: 'v-lazy-component.css', 42 | outputStyle: 'compressed' 43 | }, 44 | css: { 45 | output: 'dist/vue3/v-lazy-component.css' 46 | }, 47 | vue: { 48 | css: false, 49 | template: { 50 | isProduction: true 51 | } 52 | }, 53 | babel: { 54 | exclude: 'node_modules/**', 55 | extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'], 56 | babelHelpers: 'bundled' 57 | }, 58 | terser: {} 59 | } 60 | } 61 | 62 | // ESM/UMD/IIFE shared settings: externals 63 | // Refer to https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency 64 | const external = [ 65 | // list external dependencies, exactly the way it is written in the import statement. 66 | // eg. 'jquery' 67 | 'vue' 68 | ] 69 | 70 | // UMD/IIFE shared settings: output.globals 71 | // Refer to https://rollupjs.org/guide/en#output-globals for details 72 | const globals = { 73 | // Provide global variable names to replace your external imports 74 | // eg. jquery: '$' 75 | vue: 'Vue' 76 | } 77 | 78 | // Customize configs for individual targets 79 | const buildFormats = [] 80 | 81 | if (!argv.format || argv.format === 'umd') { 82 | const umdConfig = { 83 | ...baseConfig, 84 | external, 85 | output: [ 86 | { 87 | file: 'dist/vue3/index.js', 88 | format: 'umd', 89 | exports: 'named', 90 | globals, 91 | name: 'LazyComponent', 92 | plugins: [ 93 | terser({ 94 | ...baseConfig.plugins.terser 95 | }) 96 | ] 97 | }, 98 | { 99 | file: 'dist/vue3/v-lazy-component.umd.js', 100 | format: 'umd', 101 | exports: 'named', 102 | globals, 103 | name: 'LazyComponent' 104 | }, 105 | { 106 | file: 'dist/vue3/v-lazy-component.umd.min.js', 107 | format: 'umd', 108 | exports: 'named', 109 | globals, 110 | name: 'LazyComponent', 111 | plugins: [ 112 | terser({ 113 | ...baseConfig.plugins.terser 114 | }) 115 | ] 116 | }, 117 | { 118 | file: 'dist/vue3/v-lazy-component.global.js', 119 | format: 'umd', 120 | globals, 121 | name: 'LazyComponent' 122 | }, 123 | { 124 | file: 'dist/vue3/v-lazy-component.global.min.js', 125 | format: 'umd', 126 | globals, 127 | name: 'LazyComponent', 128 | plugins: [ 129 | terser({ 130 | ...baseConfig.plugins.terser 131 | }) 132 | ] 133 | } 134 | ], 135 | plugins: [ 136 | replace({ 137 | ...baseConfig.plugins.replace 138 | }), 139 | ...baseConfig.plugins.preVue, 140 | scss({ 141 | ...baseConfig.plugins.scss 142 | }), 143 | css({ 144 | ...baseConfig.plugins.css 145 | }), 146 | vue({ 147 | ...baseConfig.plugins.vue 148 | }), 149 | babel({ 150 | ...baseConfig.plugins.babel, 151 | presets: [ 152 | [ 153 | '@babel/preset-env', 154 | { 155 | targets: esbrowserslist 156 | } 157 | ] 158 | ] 159 | }), 160 | commonjs() 161 | ] 162 | } 163 | buildFormats.push(umdConfig) 164 | } 165 | 166 | if (!argv.format || argv.format === 'es') { 167 | const esConfig = { 168 | ...baseConfig, 169 | external, 170 | output: [ 171 | { 172 | file: 'dist/vue3/v-lazy-component.mjs', 173 | format: 'esm', 174 | exports: 'named' 175 | }, 176 | { 177 | file: 'dist/vue3/v-lazy-component.min.mjs', 178 | format: 'esm', 179 | exports: 'named', 180 | plugins: [ 181 | terser({ 182 | ...baseConfig.plugins.terser 183 | }) 184 | ] 185 | } 186 | ], 187 | plugins: [ 188 | replace({ 189 | ...baseConfig.plugins.replace, 190 | 'process.env.ES_BUILD': JSON.stringify('true') 191 | }), 192 | ...baseConfig.plugins.preVue, 193 | scss({ 194 | ...baseConfig.plugins.scss 195 | }), 196 | css({ 197 | ...baseConfig.plugins.css 198 | }), 199 | vue({ 200 | ...baseConfig.plugins.vue 201 | }), 202 | babel({ 203 | ...baseConfig.plugins.babel, 204 | presets: [ 205 | [ 206 | '@babel/preset-env', 207 | { 208 | targets: esbrowserslist 209 | } 210 | ] 211 | ] 212 | }), 213 | commonjs() 214 | ] 215 | } 216 | buildFormats.push(esConfig) 217 | } 218 | 219 | if (!argv.format || argv.format === 'cjs') { 220 | const cjsConfig = { 221 | ...baseConfig, 222 | external, 223 | output: [ 224 | { 225 | compact: true, 226 | file: 'dist/vue3/v-lazy-component.cjs', 227 | format: 'cjs', 228 | name: 'LazyComponent', 229 | exports: 'named', 230 | globals 231 | }, 232 | { 233 | compact: true, 234 | file: 'dist/vue3/v-lazy-component.min.cjs', 235 | format: 'cjs', 236 | name: 'LazyComponent', 237 | exports: 'named', 238 | globals, 239 | plugins: [ 240 | terser({ 241 | ...baseConfig.plugins.terser 242 | }) 243 | ] 244 | } 245 | ], 246 | plugins: [ 247 | replace(baseConfig.plugins.replace), 248 | ...baseConfig.plugins.preVue, 249 | scss({ 250 | ...baseConfig.plugins.scss 251 | }), 252 | css({ 253 | ...baseConfig.plugins.css 254 | }), 255 | vue({ 256 | ...baseConfig.plugins.vue, 257 | template: { 258 | ...baseConfig.plugins.vue.template, 259 | optimizeSSR: true 260 | } 261 | }), 262 | babel(baseConfig.plugins.babel), 263 | commonjs() 264 | ] 265 | } 266 | buildFormats.push(cjsConfig) 267 | } 268 | 269 | // Export config 270 | export default buildFormats 271 | -------------------------------------------------------------------------------- /changelog.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | types: { 3 | feat: { title: '🚀 Enhancements', semver: 'minor' }, 4 | perf: { title: '🔥 Performance', semver: 'patch' }, 5 | fix: { title: '🩹 Fixes', semver: 'patch' }, 6 | refactor: { title: '💅 Refactors', semver: 'patch' }, 7 | docs: { title: '📖 Documentation', semver: 'patch' }, 8 | build: { title: '📦 Build', semver: 'patch' }, 9 | types: { title: '🌊 Types', semver: 'patch' }, 10 | chore: { title: '🏡 Chore' }, 11 | examples: { title: '🏀 Examples' }, 12 | test: { title: '✅ Tests' }, 13 | style: { title: '🎨 Styles' }, 14 | ci: { title: '🤖 CI' } 15 | }, 16 | cwd: null, 17 | from: '', 18 | to: '', 19 | output: 'CHANGELOG.md', 20 | scopeMap: {}, 21 | tokens: { 22 | github: process.env.CHANGELOGEN_TOKENS_GITHUB || process.env.GITHUB_TOKEN || process.env.GH_TOKEN 23 | }, 24 | templates: { 25 | commitMessage: 'chore(release): v{{newVersion}}', 26 | tagMessage: 'v{{newVersion}}', 27 | tagBody: 'v{{newVersion}}' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /dev/vue2/.nvmrc: -------------------------------------------------------------------------------- 1 | v16.x.x 2 | -------------------------------------------------------------------------------- /dev/vue2/cdn_usage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 |
13 |

Scroll down

14 |
15 | 16 | 17 |

My Component

18 | 19 | 22 |
23 |
24 | 25 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /dev/vue2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v-lazy-component-vue2-serve", 3 | "scripts": { 4 | "serve": "vue-cli-service serve serve.js" 5 | }, 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@vue/cli-service": "^4.5.10", 9 | "vue": "^2.6.12" 10 | }, 11 | "peerDependencies": { 12 | "vue": "^2.6.12" 13 | }, 14 | "engines": { 15 | "node": ">=12" 16 | }, 17 | "license": "MIT" 18 | } 19 | -------------------------------------------------------------------------------- /dev/vue2/serve.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Dev from './serve.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | new Vue({ 7 | render: h => h(Dev) 8 | }).$mount('#app') 9 | -------------------------------------------------------------------------------- /dev/vue2/serve.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /dev/vue3/.nvmrc: -------------------------------------------------------------------------------- 1 | v16.x.x 2 | -------------------------------------------------------------------------------- /dev/vue3/cdn_usage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 |
13 |

Scroll down

14 |
15 | 16 | 17 |

My Component

18 | 19 | 22 |
23 |
24 | 25 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /dev/vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v-lazy-component-vue3-serve", 3 | "scripts": { 4 | "serve": "vue-cli-service serve serve.js" 5 | }, 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@vue/cli-service": "^4.5.10", 9 | "vue": "^3.0.0" 10 | }, 11 | "peerDependencies": { 12 | "vue": "^3.0.0" 13 | }, 14 | "engines": { 15 | "node": ">=12" 16 | }, 17 | "license": "MIT" 18 | } 19 | -------------------------------------------------------------------------------- /dev/vue3/serve.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import Dev from './serve.vue' 3 | 4 | const app = createApp(Dev) 5 | app.mount('#app') 6 | -------------------------------------------------------------------------------- /dev/vue3/serve.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /dist/vue2/index.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LazyComponent={})}(this,(function(e){"use strict";function t(e,t,s,n,i,r,o,d,a,l){"boolean"!=typeof o&&(a=d,d=o,o=!1);const c="function"==typeof s?s.options:s;let p;if(e&&e.render&&(c.render=e.render,c.staticRenderFns=e.staticRenderFns,c._compiled=!0,i&&(c.functional=!0)),n&&(c._scopeId=n),r?(p=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,a(e)),e&&e._registeredComponents&&e._registeredComponents.add(r)},c._ssrRegister=p):t&&(p=o?function(e){t.call(this,l(e,this.$root.$options.shadowRoot))}:function(e){t.call(this,d(e))}),p)if(c.functional){const e=c.render;c.render=function(t,s){return p.call(s),e(t,s)}}else{const e=c.beforeCreate;c.beforeCreate=e?[].concat(e,p):[p]}return s}const s=t({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)(e.state.wrapperTag,{tag:"component",class:["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}],style:{minWidth:"1px",minHeight:"1px"}},[e.state.isIntersected?e._t("default"):e._e(),e.state.isIntersected?e._e():e._t("placeholder")],2)},staticRenderFns:[]},undefined,{name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},data(){return{state:{wrapperTag:this.wrapperTag,isIntersected:this.isIntersected,idle:this.idle,rootMargin:this.rootMargin,threshold:this.threshold,observer:null}}},watch:{isIntersected(e){e&&(this.state.isIntersected=!0)},"state.isIntersected"(e){e&&this.$emit("intersected",this.$el)}},mounted(){this.isIntersectionObserverSupported()?this.state.isIntersected||this.state.idle||this.observe():this.state.isIntersected=!0,this.state.isIntersected&&this.$emit("intersected",this.$el)},beforeDestroy(){this.state.isIntersected||this.state.idle||this.unobserve()},methods:{isIntersectionObserverSupported:()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,observe(){const{rootMargin:e,threshold:t}=this.state,s={root:void 0,rootMargin:e,threshold:t};this.state.observer=new IntersectionObserver(this.onIntersection,s),this.state.observer.observe(this.$el)},onIntersection(e){this.state.isIntersected=e.some((e=>e.intersectionRatio>0)),this.state.isIntersected&&this.unobserve()},unobserve(){this.isIntersectionObserverSupported()&&this.state.observer.unobserve(this.$el)}}},undefined,false,undefined,!1,void 0,void 0,void 0),n=function(e){n.installed||(n.installed=!0,e.component("LazyComponent",s))},i={install:n};{let e=null;"undefined"!=typeof window?e=window.Vue:"undefined"!=typeof global&&(e=global.Vue),e&&e.use(i)}s.install=n,e.default=s,Object.defineProperty(e,"__esModule",{value:!0})})); 2 | -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.cjs: -------------------------------------------------------------------------------- 1 | 'use strict';Object.defineProperty(exports,'__esModule',{value:true});// 2 | // 3 | // 4 | // 5 | // 6 | // 7 | // 8 | // 9 | // 10 | // 11 | // 12 | 13 | var script = { 14 | name: 'LazyComponent', 15 | props: { 16 | wrapperTag: { 17 | type: String, 18 | required: false, 19 | default: 'div' 20 | }, 21 | isIntersected: { 22 | type: Boolean, 23 | required: false, 24 | default: false 25 | }, 26 | idle: { 27 | type: Boolean, 28 | required: false, 29 | default: false 30 | }, 31 | /** 32 | * See IntersectionOberserver rootMargin [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 33 | */ 34 | rootMargin: { 35 | type: String, 36 | required: false, 37 | default: '0px 0px 0px 0px' 38 | }, 39 | /** 40 | * See IntersectionOberserver treshold [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 41 | */ 42 | threshold: { 43 | type: [Number, Array], 44 | required: false, 45 | default: 0 46 | } 47 | }, 48 | data: function data() { 49 | return { 50 | state: { 51 | wrapperTag: this.wrapperTag, 52 | isIntersected: this.isIntersected, 53 | idle: this.idle, 54 | rootMargin: this.rootMargin, 55 | threshold: this.threshold, 56 | observer: null 57 | } 58 | }; 59 | }, 60 | watch: { 61 | isIntersected: function isIntersected(value) { 62 | if (value) { 63 | this.state.isIntersected = true; 64 | } 65 | }, 66 | 'state.isIntersected': function stateIsIntersected(value) { 67 | if (value) { 68 | this.$emit('intersected', this.$el); 69 | } 70 | } 71 | }, 72 | mounted: function mounted() { 73 | if (this.isIntersectionObserverSupported()) { 74 | if (!this.state.isIntersected && !this.state.idle) { 75 | this.observe(); 76 | } 77 | } else { 78 | this.state.isIntersected = true; 79 | } 80 | if (this.state.isIntersected) { 81 | this.$emit('intersected', this.$el); 82 | } 83 | }, 84 | beforeDestroy: function beforeDestroy() { 85 | if (!this.state.isIntersected && !this.state.idle) { 86 | this.unobserve(); 87 | } 88 | }, 89 | methods: { 90 | isIntersectionObserverSupported: function isIntersectionObserverSupported() { 91 | return 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype && 'isIntersecting' in window.IntersectionObserverEntry.prototype; 92 | }, 93 | observe: function observe() { 94 | var _this$state = this.state, 95 | rootMargin = _this$state.rootMargin, 96 | threshold = _this$state.threshold; 97 | var config = { 98 | root: undefined, 99 | rootMargin: rootMargin, 100 | threshold: threshold 101 | }; 102 | this.state.observer = new IntersectionObserver(this.onIntersection, config); 103 | this.state.observer.observe(this.$el); 104 | }, 105 | onIntersection: function onIntersection(entries) { 106 | this.state.isIntersected = entries.some(function (entry) { 107 | return entry.intersectionRatio > 0; 108 | }); 109 | if (this.state.isIntersected) { 110 | this.unobserve(); 111 | } 112 | }, 113 | unobserve: function unobserve() { 114 | if (this.isIntersectionObserverSupported()) { 115 | this.state.observer.unobserve(this.$el); 116 | } 117 | } 118 | } 119 | };function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 120 | if (typeof shadowMode !== 'boolean') { 121 | createInjectorSSR = createInjector; 122 | createInjector = shadowMode; 123 | shadowMode = false; 124 | } 125 | // Vue.extend constructor export interop. 126 | const options = typeof script === 'function' ? script.options : script; 127 | // render functions 128 | if (template && template.render) { 129 | options.render = template.render; 130 | options.staticRenderFns = template.staticRenderFns; 131 | options._compiled = true; 132 | // functional template 133 | if (isFunctionalTemplate) { 134 | options.functional = true; 135 | } 136 | } 137 | // scopedId 138 | if (scopeId) { 139 | options._scopeId = scopeId; 140 | } 141 | let hook; 142 | if (moduleIdentifier) { 143 | // server build 144 | hook = function (context) { 145 | // 2.3 injection 146 | context = 147 | context || // cached call 148 | (this.$vnode && this.$vnode.ssrContext) || // stateful 149 | (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional 150 | // 2.2 with runInNewContext: true 151 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 152 | context = __VUE_SSR_CONTEXT__; 153 | } 154 | // inject component styles 155 | if (style) { 156 | style.call(this, createInjectorSSR(context)); 157 | } 158 | // register component module identifier for async chunk inference 159 | if (context && context._registeredComponents) { 160 | context._registeredComponents.add(moduleIdentifier); 161 | } 162 | }; 163 | // used by ssr in case component is cached and beforeCreate 164 | // never gets called 165 | options._ssrRegister = hook; 166 | } 167 | else if (style) { 168 | hook = shadowMode 169 | ? function (context) { 170 | style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); 171 | } 172 | : function (context) { 173 | style.call(this, createInjector(context)); 174 | }; 175 | } 176 | if (hook) { 177 | if (options.functional) { 178 | // register for functional component in vue file 179 | const originalRender = options.render; 180 | options.render = function renderWithStyleInjection(h, context) { 181 | hook.call(context); 182 | return originalRender(h, context); 183 | }; 184 | } 185 | else { 186 | // inject component registration as beforeCreate hook 187 | const existing = options.beforeCreate; 188 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 189 | } 190 | } 191 | return script; 192 | }/* script */ 193 | var __vue_script__ = script; 194 | /* template */ 195 | var __vue_render__ = function __vue_render__() { 196 | var _vm = this; 197 | var _h = _vm.$createElement; 198 | var _c = _vm._self._c || _h; 199 | return _c(_vm.state.wrapperTag, { 200 | tag: "component", 201 | class: ['v-lazy-component', { 202 | 'v-lazy-component--loading': !_vm.state.isIntersected, 203 | 'v-lazy-component--loaded': _vm.state.isIntersected 204 | }], 205 | style: { 206 | minWidth: '1px', 207 | minHeight: '1px' 208 | } 209 | }, [_vm.state.isIntersected ? _vm._t("default") : _vm._e(), !_vm.state.isIntersected ? _vm._t("placeholder") : _vm._e()], 2); 210 | }; 211 | var __vue_staticRenderFns__ = []; 212 | 213 | /* style */ 214 | var __vue_inject_styles__ = undefined; 215 | /* scoped */ 216 | var __vue_scope_id__ = undefined; 217 | /* module identifier */ 218 | var __vue_module_identifier__ = "data-v-06ebb1a0"; 219 | /* functional template */ 220 | var __vue_is_functional_template__ = false; 221 | /* style inject */ 222 | 223 | /* style inject SSR */ 224 | 225 | /* style inject shadow dom */ 226 | 227 | var __vue_component__ = /*#__PURE__*/normalizeComponent({ 228 | render: __vue_render__, 229 | staticRenderFns: __vue_staticRenderFns__ 230 | }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined);// Import vue component 231 | 232 | // install function executed by Vue.use() 233 | var install = function installLazyComponent(Vue) { 234 | if (install.installed) return; 235 | install.installed = true; 236 | Vue.component('LazyComponent', __vue_component__); 237 | }; 238 | 239 | // Create module definition for Vue.use() 240 | var plugin = { 241 | install: install 242 | }; 243 | 244 | // To auto-install on non-es builds, when vue is found 245 | // eslint-disable-next-line no-redeclare 246 | /* global window, global */ 247 | { 248 | var GlobalVue = null; 249 | if (typeof window !== 'undefined') { 250 | GlobalVue = window.Vue; 251 | } else if (typeof global !== 'undefined') { 252 | GlobalVue = global.Vue; 253 | } 254 | if (GlobalVue) { 255 | GlobalVue.use(plugin); 256 | } 257 | } 258 | 259 | // Inject install function into component - allows component 260 | // to be registered via Vue.use() as well as Vue.component() 261 | __vue_component__.install = install; 262 | 263 | // It's possible to expose named exports when writing components that can 264 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 265 | // export const RollupDemoDirective = component; 266 | exports["default"]=__vue_component__; -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.css: -------------------------------------------------------------------------------- 1 | .v-lazy-component{position:relative} -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.global.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.LazyComponent = factory()); 5 | })(this, (function () { 'use strict'; 6 | 7 | // 8 | // 9 | // 10 | // 11 | // 12 | // 13 | // 14 | // 15 | // 16 | // 17 | // 18 | 19 | var script = { 20 | name: 'LazyComponent', 21 | props: { 22 | wrapperTag: { 23 | type: String, 24 | required: false, 25 | default: 'div' 26 | }, 27 | isIntersected: { 28 | type: Boolean, 29 | required: false, 30 | default: false 31 | }, 32 | idle: { 33 | type: Boolean, 34 | required: false, 35 | default: false 36 | }, 37 | /** 38 | * See IntersectionOberserver rootMargin [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 39 | */ 40 | rootMargin: { 41 | type: String, 42 | required: false, 43 | default: '0px 0px 0px 0px' 44 | }, 45 | /** 46 | * See IntersectionOberserver treshold [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 47 | */ 48 | threshold: { 49 | type: [Number, Array], 50 | required: false, 51 | default: 0 52 | } 53 | }, 54 | data() { 55 | return { 56 | state: { 57 | wrapperTag: this.wrapperTag, 58 | isIntersected: this.isIntersected, 59 | idle: this.idle, 60 | rootMargin: this.rootMargin, 61 | threshold: this.threshold, 62 | observer: null 63 | } 64 | }; 65 | }, 66 | watch: { 67 | isIntersected(value) { 68 | if (value) { 69 | this.state.isIntersected = true; 70 | } 71 | }, 72 | 'state.isIntersected'(value) { 73 | if (value) { 74 | this.$emit('intersected', this.$el); 75 | } 76 | } 77 | }, 78 | mounted() { 79 | if (this.isIntersectionObserverSupported()) { 80 | if (!this.state.isIntersected && !this.state.idle) { 81 | this.observe(); 82 | } 83 | } else { 84 | this.state.isIntersected = true; 85 | } 86 | if (this.state.isIntersected) { 87 | this.$emit('intersected', this.$el); 88 | } 89 | }, 90 | beforeDestroy() { 91 | if (!this.state.isIntersected && !this.state.idle) { 92 | this.unobserve(); 93 | } 94 | }, 95 | methods: { 96 | isIntersectionObserverSupported() { 97 | return 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype && 'isIntersecting' in window.IntersectionObserverEntry.prototype; 98 | }, 99 | observe() { 100 | const { 101 | rootMargin, 102 | threshold 103 | } = this.state; 104 | const config = { 105 | root: undefined, 106 | rootMargin, 107 | threshold 108 | }; 109 | this.state.observer = new IntersectionObserver(this.onIntersection, config); 110 | this.state.observer.observe(this.$el); 111 | }, 112 | onIntersection(entries) { 113 | this.state.isIntersected = entries.some(entry => entry.intersectionRatio > 0); 114 | if (this.state.isIntersected) { 115 | this.unobserve(); 116 | } 117 | }, 118 | unobserve() { 119 | if (this.isIntersectionObserverSupported()) { 120 | this.state.observer.unobserve(this.$el); 121 | } 122 | } 123 | } 124 | }; 125 | 126 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 127 | if (typeof shadowMode !== 'boolean') { 128 | createInjectorSSR = createInjector; 129 | createInjector = shadowMode; 130 | shadowMode = false; 131 | } 132 | // Vue.extend constructor export interop. 133 | const options = typeof script === 'function' ? script.options : script; 134 | // render functions 135 | if (template && template.render) { 136 | options.render = template.render; 137 | options.staticRenderFns = template.staticRenderFns; 138 | options._compiled = true; 139 | // functional template 140 | if (isFunctionalTemplate) { 141 | options.functional = true; 142 | } 143 | } 144 | // scopedId 145 | if (scopeId) { 146 | options._scopeId = scopeId; 147 | } 148 | let hook; 149 | if (moduleIdentifier) { 150 | // server build 151 | hook = function (context) { 152 | // 2.3 injection 153 | context = 154 | context || // cached call 155 | (this.$vnode && this.$vnode.ssrContext) || // stateful 156 | (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional 157 | // 2.2 with runInNewContext: true 158 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 159 | context = __VUE_SSR_CONTEXT__; 160 | } 161 | // inject component styles 162 | if (style) { 163 | style.call(this, createInjectorSSR(context)); 164 | } 165 | // register component module identifier for async chunk inference 166 | if (context && context._registeredComponents) { 167 | context._registeredComponents.add(moduleIdentifier); 168 | } 169 | }; 170 | // used by ssr in case component is cached and beforeCreate 171 | // never gets called 172 | options._ssrRegister = hook; 173 | } 174 | else if (style) { 175 | hook = shadowMode 176 | ? function (context) { 177 | style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); 178 | } 179 | : function (context) { 180 | style.call(this, createInjector(context)); 181 | }; 182 | } 183 | if (hook) { 184 | if (options.functional) { 185 | // register for functional component in vue file 186 | const originalRender = options.render; 187 | options.render = function renderWithStyleInjection(h, context) { 188 | hook.call(context); 189 | return originalRender(h, context); 190 | }; 191 | } 192 | else { 193 | // inject component registration as beforeCreate hook 194 | const existing = options.beforeCreate; 195 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 196 | } 197 | } 198 | return script; 199 | } 200 | 201 | /* script */ 202 | const __vue_script__ = script; 203 | /* template */ 204 | var __vue_render__ = function () { 205 | var _vm = this; 206 | var _h = _vm.$createElement; 207 | var _c = _vm._self._c || _h; 208 | return _c(_vm.state.wrapperTag, { 209 | tag: "component", 210 | class: ['v-lazy-component', { 211 | 'v-lazy-component--loading': !_vm.state.isIntersected, 212 | 'v-lazy-component--loaded': _vm.state.isIntersected 213 | }], 214 | style: { 215 | minWidth: '1px', 216 | minHeight: '1px' 217 | } 218 | }, [_vm.state.isIntersected ? _vm._t("default") : _vm._e(), !_vm.state.isIntersected ? _vm._t("placeholder") : _vm._e()], 2); 219 | }; 220 | var __vue_staticRenderFns__ = []; 221 | 222 | /* style */ 223 | const __vue_inject_styles__ = undefined; 224 | /* scoped */ 225 | const __vue_scope_id__ = undefined; 226 | /* module identifier */ 227 | const __vue_module_identifier__ = undefined; 228 | /* functional template */ 229 | const __vue_is_functional_template__ = false; 230 | /* style inject */ 231 | 232 | /* style inject SSR */ 233 | 234 | /* style inject shadow dom */ 235 | 236 | const __vue_component__ = /*#__PURE__*/normalizeComponent({ 237 | render: __vue_render__, 238 | staticRenderFns: __vue_staticRenderFns__ 239 | }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined); 240 | 241 | // Import vue component 242 | 243 | // install function executed by Vue.use() 244 | const install = function installLazyComponent(Vue) { 245 | if (install.installed) return; 246 | install.installed = true; 247 | Vue.component('LazyComponent', __vue_component__); 248 | }; 249 | 250 | // Create module definition for Vue.use() 251 | const plugin = { 252 | install 253 | }; 254 | 255 | // To auto-install on non-es builds, when vue is found 256 | // eslint-disable-next-line no-redeclare 257 | /* global window, global */ 258 | { 259 | let GlobalVue = null; 260 | if (typeof window !== 'undefined') { 261 | GlobalVue = window.Vue; 262 | } else if (typeof global !== 'undefined') { 263 | GlobalVue = global.Vue; 264 | } 265 | if (GlobalVue) { 266 | GlobalVue.use(plugin); 267 | } 268 | } 269 | 270 | // Inject install function into component - allows component 271 | // to be registered via Vue.use() as well as Vue.component() 272 | __vue_component__.install = install; 273 | 274 | // It's possible to expose named exports when writing components that can 275 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 276 | // export const RollupDemoDirective = component; 277 | 278 | return __vue_component__; 279 | 280 | })); 281 | -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.global.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).LazyComponent=t()}(this,(function(){"use strict";function e(e,t,n,s,i,r,o,d,a,c){"boolean"!=typeof o&&(a=d,d=o,o=!1);const l="function"==typeof n?n.options:n;let p;if(e&&e.render&&(l.render=e.render,l.staticRenderFns=e.staticRenderFns,l._compiled=!0,i&&(l.functional=!0)),s&&(l._scopeId=s),r?(p=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,a(e)),e&&e._registeredComponents&&e._registeredComponents.add(r)},l._ssrRegister=p):t&&(p=o?function(e){t.call(this,c(e,this.$root.$options.shadowRoot))}:function(e){t.call(this,d(e))}),p)if(l.functional){const e=l.render;l.render=function(t,n){return p.call(n),e(t,n)}}else{const e=l.beforeCreate;l.beforeCreate=e?[].concat(e,p):[p]}return n}const t=e({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)(e.state.wrapperTag,{tag:"component",class:["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}],style:{minWidth:"1px",minHeight:"1px"}},[e.state.isIntersected?e._t("default"):e._e(),e.state.isIntersected?e._e():e._t("placeholder")],2)},staticRenderFns:[]},undefined,{name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},data(){return{state:{wrapperTag:this.wrapperTag,isIntersected:this.isIntersected,idle:this.idle,rootMargin:this.rootMargin,threshold:this.threshold,observer:null}}},watch:{isIntersected(e){e&&(this.state.isIntersected=!0)},"state.isIntersected"(e){e&&this.$emit("intersected",this.$el)}},mounted(){this.isIntersectionObserverSupported()?this.state.isIntersected||this.state.idle||this.observe():this.state.isIntersected=!0,this.state.isIntersected&&this.$emit("intersected",this.$el)},beforeDestroy(){this.state.isIntersected||this.state.idle||this.unobserve()},methods:{isIntersectionObserverSupported:()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,observe(){const{rootMargin:e,threshold:t}=this.state,n={root:void 0,rootMargin:e,threshold:t};this.state.observer=new IntersectionObserver(this.onIntersection,n),this.state.observer.observe(this.$el)},onIntersection(e){this.state.isIntersected=e.some((e=>e.intersectionRatio>0)),this.state.isIntersected&&this.unobserve()},unobserve(){this.isIntersectionObserverSupported()&&this.state.observer.unobserve(this.$el)}}},undefined,false,undefined,!1,void 0,void 0,void 0),n=function(e){n.installed||(n.installed=!0,e.component("LazyComponent",t))},s={install:n};{let e=null;"undefined"!=typeof window?e=window.Vue:"undefined"!=typeof global&&(e=global.Vue),e&&e.use(s)}return t.install=n,t})); 2 | -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.min.cjs: -------------------------------------------------------------------------------- 1 | "use strict";function e(e,t,n,s,r,i,o,d,a,c){"boolean"!=typeof o&&(a=d,d=o,o=!1);const l="function"==typeof n?n.options:n;let u;if(e&&e.render&&(l.render=e.render,l.staticRenderFns=e.staticRenderFns,l._compiled=!0,r&&(l.functional=!0)),s&&(l._scopeId=s),i?(u=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,a(e)),e&&e._registeredComponents&&e._registeredComponents.add(i)},l._ssrRegister=u):t&&(u=o?function(e){t.call(this,c(e,this.$root.$options.shadowRoot))}:function(e){t.call(this,d(e))}),u)if(l.functional){const e=l.render;l.render=function(t,n){return u.call(n),e(t,n)}}else{const e=l.beforeCreate;l.beforeCreate=e?[].concat(e,u):[u]}return n}Object.defineProperty(exports,"__esModule",{value:!0});var t=e({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)(e.state.wrapperTag,{tag:"component",class:["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}],style:{minWidth:"1px",minHeight:"1px"}},[e.state.isIntersected?e._t("default"):e._e(),e.state.isIntersected?e._e():e._t("placeholder")],2)},staticRenderFns:[]},undefined,{name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},data:function(){return{state:{wrapperTag:this.wrapperTag,isIntersected:this.isIntersected,idle:this.idle,rootMargin:this.rootMargin,threshold:this.threshold,observer:null}}},watch:{isIntersected:function(e){e&&(this.state.isIntersected=!0)},"state.isIntersected":function(e){e&&this.$emit("intersected",this.$el)}},mounted:function(){this.isIntersectionObserverSupported()?this.state.isIntersected||this.state.idle||this.observe():this.state.isIntersected=!0,this.state.isIntersected&&this.$emit("intersected",this.$el)},beforeDestroy:function(){this.state.isIntersected||this.state.idle||this.unobserve()},methods:{isIntersectionObserverSupported:function(){return"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype},observe:function(){var e=this.state,t={root:void 0,rootMargin:e.rootMargin,threshold:e.threshold};this.state.observer=new IntersectionObserver(this.onIntersection,t),this.state.observer.observe(this.$el)},onIntersection:function(e){this.state.isIntersected=e.some((function(e){return e.intersectionRatio>0})),this.state.isIntersected&&this.unobserve()},unobserve:function(){this.isIntersectionObserverSupported()&&this.state.observer.unobserve(this.$el)}}},undefined,false,"data-v-06ebb1a0",!1,void 0,void 0,void 0),n=function(e){n.installed||(n.installed=!0,e.component("LazyComponent",t))},s={install:n},r=null;"undefined"!=typeof window?r=window.Vue:"undefined"!=typeof global&&(r=global.Vue),r&&r.use(s),t.install=n,exports.default=t; -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.min.mjs: -------------------------------------------------------------------------------- 1 | function e(e,t,s,n,r,i,o,d,a,c){"boolean"!=typeof o&&(a=d,d=o,o=!1);const l="function"==typeof s?s.options:s;let h;if(e&&e.render&&(l.render=e.render,l.staticRenderFns=e.staticRenderFns,l._compiled=!0,r&&(l.functional=!0)),n&&(l._scopeId=n),i?(h=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,a(e)),e&&e._registeredComponents&&e._registeredComponents.add(i)},l._ssrRegister=h):t&&(h=o?function(e){t.call(this,c(e,this.$root.$options.shadowRoot))}:function(e){t.call(this,d(e))}),h)if(l.functional){const e=l.render;l.render=function(t,s){return h.call(s),e(t,s)}}else{const e=l.beforeCreate;l.beforeCreate=e?[].concat(e,h):[h]}return s}const t=e({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)(e.state.wrapperTag,{tag:"component",class:["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}],style:{minWidth:"1px",minHeight:"1px"}},[e.state.isIntersected?e._t("default"):e._e(),e.state.isIntersected?e._e():e._t("placeholder")],2)},staticRenderFns:[]},undefined,{name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},data(){return{state:{wrapperTag:this.wrapperTag,isIntersected:this.isIntersected,idle:this.idle,rootMargin:this.rootMargin,threshold:this.threshold,observer:null}}},watch:{isIntersected(e){e&&(this.state.isIntersected=!0)},"state.isIntersected"(e){e&&this.$emit("intersected",this.$el)}},mounted(){this.isIntersectionObserverSupported()?this.state.isIntersected||this.state.idle||this.observe():this.state.isIntersected=!0,this.state.isIntersected&&this.$emit("intersected",this.$el)},beforeDestroy(){this.state.isIntersected||this.state.idle||this.unobserve()},methods:{isIntersectionObserverSupported:()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,observe(){const{rootMargin:e,threshold:t}=this.state,s={root:void 0,rootMargin:e,threshold:t};this.state.observer=new IntersectionObserver(this.onIntersection,s),this.state.observer.observe(this.$el)},onIntersection(e){this.state.isIntersected=e.some((e=>e.intersectionRatio>0)),this.state.isIntersected&&this.unobserve()},unobserve(){this.isIntersectionObserverSupported()&&this.state.observer.unobserve(this.$el)}}},undefined,false,undefined,!1,void 0,void 0,void 0),s=function(e){s.installed||(s.installed=!0,e.component("LazyComponent",t))};t.install=s;export{t as default}; 2 | -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.mjs: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // 4 | // 5 | // 6 | // 7 | // 8 | // 9 | // 10 | // 11 | // 12 | 13 | var script = { 14 | name: 'LazyComponent', 15 | props: { 16 | wrapperTag: { 17 | type: String, 18 | required: false, 19 | default: 'div' 20 | }, 21 | isIntersected: { 22 | type: Boolean, 23 | required: false, 24 | default: false 25 | }, 26 | idle: { 27 | type: Boolean, 28 | required: false, 29 | default: false 30 | }, 31 | /** 32 | * See IntersectionOberserver rootMargin [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 33 | */ 34 | rootMargin: { 35 | type: String, 36 | required: false, 37 | default: '0px 0px 0px 0px' 38 | }, 39 | /** 40 | * See IntersectionOberserver treshold [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 41 | */ 42 | threshold: { 43 | type: [Number, Array], 44 | required: false, 45 | default: 0 46 | } 47 | }, 48 | data() { 49 | return { 50 | state: { 51 | wrapperTag: this.wrapperTag, 52 | isIntersected: this.isIntersected, 53 | idle: this.idle, 54 | rootMargin: this.rootMargin, 55 | threshold: this.threshold, 56 | observer: null 57 | } 58 | }; 59 | }, 60 | watch: { 61 | isIntersected(value) { 62 | if (value) { 63 | this.state.isIntersected = true; 64 | } 65 | }, 66 | 'state.isIntersected'(value) { 67 | if (value) { 68 | this.$emit('intersected', this.$el); 69 | } 70 | } 71 | }, 72 | mounted() { 73 | if (this.isIntersectionObserverSupported()) { 74 | if (!this.state.isIntersected && !this.state.idle) { 75 | this.observe(); 76 | } 77 | } else { 78 | this.state.isIntersected = true; 79 | } 80 | if (this.state.isIntersected) { 81 | this.$emit('intersected', this.$el); 82 | } 83 | }, 84 | beforeDestroy() { 85 | if (!this.state.isIntersected && !this.state.idle) { 86 | this.unobserve(); 87 | } 88 | }, 89 | methods: { 90 | isIntersectionObserverSupported() { 91 | return 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype && 'isIntersecting' in window.IntersectionObserverEntry.prototype; 92 | }, 93 | observe() { 94 | const { 95 | rootMargin, 96 | threshold 97 | } = this.state; 98 | const config = { 99 | root: undefined, 100 | rootMargin, 101 | threshold 102 | }; 103 | this.state.observer = new IntersectionObserver(this.onIntersection, config); 104 | this.state.observer.observe(this.$el); 105 | }, 106 | onIntersection(entries) { 107 | this.state.isIntersected = entries.some(entry => entry.intersectionRatio > 0); 108 | if (this.state.isIntersected) { 109 | this.unobserve(); 110 | } 111 | }, 112 | unobserve() { 113 | if (this.isIntersectionObserverSupported()) { 114 | this.state.observer.unobserve(this.$el); 115 | } 116 | } 117 | } 118 | }; 119 | 120 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 121 | if (typeof shadowMode !== 'boolean') { 122 | createInjectorSSR = createInjector; 123 | createInjector = shadowMode; 124 | shadowMode = false; 125 | } 126 | // Vue.extend constructor export interop. 127 | const options = typeof script === 'function' ? script.options : script; 128 | // render functions 129 | if (template && template.render) { 130 | options.render = template.render; 131 | options.staticRenderFns = template.staticRenderFns; 132 | options._compiled = true; 133 | // functional template 134 | if (isFunctionalTemplate) { 135 | options.functional = true; 136 | } 137 | } 138 | // scopedId 139 | if (scopeId) { 140 | options._scopeId = scopeId; 141 | } 142 | let hook; 143 | if (moduleIdentifier) { 144 | // server build 145 | hook = function (context) { 146 | // 2.3 injection 147 | context = 148 | context || // cached call 149 | (this.$vnode && this.$vnode.ssrContext) || // stateful 150 | (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional 151 | // 2.2 with runInNewContext: true 152 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 153 | context = __VUE_SSR_CONTEXT__; 154 | } 155 | // inject component styles 156 | if (style) { 157 | style.call(this, createInjectorSSR(context)); 158 | } 159 | // register component module identifier for async chunk inference 160 | if (context && context._registeredComponents) { 161 | context._registeredComponents.add(moduleIdentifier); 162 | } 163 | }; 164 | // used by ssr in case component is cached and beforeCreate 165 | // never gets called 166 | options._ssrRegister = hook; 167 | } 168 | else if (style) { 169 | hook = shadowMode 170 | ? function (context) { 171 | style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); 172 | } 173 | : function (context) { 174 | style.call(this, createInjector(context)); 175 | }; 176 | } 177 | if (hook) { 178 | if (options.functional) { 179 | // register for functional component in vue file 180 | const originalRender = options.render; 181 | options.render = function renderWithStyleInjection(h, context) { 182 | hook.call(context); 183 | return originalRender(h, context); 184 | }; 185 | } 186 | else { 187 | // inject component registration as beforeCreate hook 188 | const existing = options.beforeCreate; 189 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 190 | } 191 | } 192 | return script; 193 | } 194 | 195 | /* script */ 196 | const __vue_script__ = script; 197 | /* template */ 198 | var __vue_render__ = function () { 199 | var _vm = this; 200 | var _h = _vm.$createElement; 201 | var _c = _vm._self._c || _h; 202 | return _c(_vm.state.wrapperTag, { 203 | tag: "component", 204 | class: ['v-lazy-component', { 205 | 'v-lazy-component--loading': !_vm.state.isIntersected, 206 | 'v-lazy-component--loaded': _vm.state.isIntersected 207 | }], 208 | style: { 209 | minWidth: '1px', 210 | minHeight: '1px' 211 | } 212 | }, [_vm.state.isIntersected ? _vm._t("default") : _vm._e(), !_vm.state.isIntersected ? _vm._t("placeholder") : _vm._e()], 2); 213 | }; 214 | var __vue_staticRenderFns__ = []; 215 | 216 | /* style */ 217 | const __vue_inject_styles__ = undefined; 218 | /* scoped */ 219 | const __vue_scope_id__ = undefined; 220 | /* module identifier */ 221 | const __vue_module_identifier__ = undefined; 222 | /* functional template */ 223 | const __vue_is_functional_template__ = false; 224 | /* style inject */ 225 | 226 | /* style inject SSR */ 227 | 228 | /* style inject shadow dom */ 229 | 230 | const __vue_component__ = /*#__PURE__*/normalizeComponent({ 231 | render: __vue_render__, 232 | staticRenderFns: __vue_staticRenderFns__ 233 | }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined); 234 | 235 | // Import vue component 236 | 237 | // install function executed by Vue.use() 238 | const install = function installLazyComponent(Vue) { 239 | if (install.installed) return; 240 | install.installed = true; 241 | Vue.component('LazyComponent', __vue_component__); 242 | }; 243 | 244 | // Inject install function into component - allows component 245 | // to be registered via Vue.use() as well as Vue.component() 246 | __vue_component__.install = install; 247 | 248 | // It's possible to expose named exports when writing components that can 249 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 250 | // export const RollupDemoDirective = component; 251 | 252 | export { __vue_component__ as default }; 253 | -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 | typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.LazyComponent = {})); 5 | })(this, (function (exports) { 'use strict'; 6 | 7 | // 8 | // 9 | // 10 | // 11 | // 12 | // 13 | // 14 | // 15 | // 16 | // 17 | // 18 | 19 | var script = { 20 | name: 'LazyComponent', 21 | props: { 22 | wrapperTag: { 23 | type: String, 24 | required: false, 25 | default: 'div' 26 | }, 27 | isIntersected: { 28 | type: Boolean, 29 | required: false, 30 | default: false 31 | }, 32 | idle: { 33 | type: Boolean, 34 | required: false, 35 | default: false 36 | }, 37 | /** 38 | * See IntersectionOberserver rootMargin [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 39 | */ 40 | rootMargin: { 41 | type: String, 42 | required: false, 43 | default: '0px 0px 0px 0px' 44 | }, 45 | /** 46 | * See IntersectionOberserver treshold [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 47 | */ 48 | threshold: { 49 | type: [Number, Array], 50 | required: false, 51 | default: 0 52 | } 53 | }, 54 | data() { 55 | return { 56 | state: { 57 | wrapperTag: this.wrapperTag, 58 | isIntersected: this.isIntersected, 59 | idle: this.idle, 60 | rootMargin: this.rootMargin, 61 | threshold: this.threshold, 62 | observer: null 63 | } 64 | }; 65 | }, 66 | watch: { 67 | isIntersected(value) { 68 | if (value) { 69 | this.state.isIntersected = true; 70 | } 71 | }, 72 | 'state.isIntersected'(value) { 73 | if (value) { 74 | this.$emit('intersected', this.$el); 75 | } 76 | } 77 | }, 78 | mounted() { 79 | if (this.isIntersectionObserverSupported()) { 80 | if (!this.state.isIntersected && !this.state.idle) { 81 | this.observe(); 82 | } 83 | } else { 84 | this.state.isIntersected = true; 85 | } 86 | if (this.state.isIntersected) { 87 | this.$emit('intersected', this.$el); 88 | } 89 | }, 90 | beforeDestroy() { 91 | if (!this.state.isIntersected && !this.state.idle) { 92 | this.unobserve(); 93 | } 94 | }, 95 | methods: { 96 | isIntersectionObserverSupported() { 97 | return 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype && 'isIntersecting' in window.IntersectionObserverEntry.prototype; 98 | }, 99 | observe() { 100 | const { 101 | rootMargin, 102 | threshold 103 | } = this.state; 104 | const config = { 105 | root: undefined, 106 | rootMargin, 107 | threshold 108 | }; 109 | this.state.observer = new IntersectionObserver(this.onIntersection, config); 110 | this.state.observer.observe(this.$el); 111 | }, 112 | onIntersection(entries) { 113 | this.state.isIntersected = entries.some(entry => entry.intersectionRatio > 0); 114 | if (this.state.isIntersected) { 115 | this.unobserve(); 116 | } 117 | }, 118 | unobserve() { 119 | if (this.isIntersectionObserverSupported()) { 120 | this.state.observer.unobserve(this.$el); 121 | } 122 | } 123 | } 124 | }; 125 | 126 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 127 | if (typeof shadowMode !== 'boolean') { 128 | createInjectorSSR = createInjector; 129 | createInjector = shadowMode; 130 | shadowMode = false; 131 | } 132 | // Vue.extend constructor export interop. 133 | const options = typeof script === 'function' ? script.options : script; 134 | // render functions 135 | if (template && template.render) { 136 | options.render = template.render; 137 | options.staticRenderFns = template.staticRenderFns; 138 | options._compiled = true; 139 | // functional template 140 | if (isFunctionalTemplate) { 141 | options.functional = true; 142 | } 143 | } 144 | // scopedId 145 | if (scopeId) { 146 | options._scopeId = scopeId; 147 | } 148 | let hook; 149 | if (moduleIdentifier) { 150 | // server build 151 | hook = function (context) { 152 | // 2.3 injection 153 | context = 154 | context || // cached call 155 | (this.$vnode && this.$vnode.ssrContext) || // stateful 156 | (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional 157 | // 2.2 with runInNewContext: true 158 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 159 | context = __VUE_SSR_CONTEXT__; 160 | } 161 | // inject component styles 162 | if (style) { 163 | style.call(this, createInjectorSSR(context)); 164 | } 165 | // register component module identifier for async chunk inference 166 | if (context && context._registeredComponents) { 167 | context._registeredComponents.add(moduleIdentifier); 168 | } 169 | }; 170 | // used by ssr in case component is cached and beforeCreate 171 | // never gets called 172 | options._ssrRegister = hook; 173 | } 174 | else if (style) { 175 | hook = shadowMode 176 | ? function (context) { 177 | style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); 178 | } 179 | : function (context) { 180 | style.call(this, createInjector(context)); 181 | }; 182 | } 183 | if (hook) { 184 | if (options.functional) { 185 | // register for functional component in vue file 186 | const originalRender = options.render; 187 | options.render = function renderWithStyleInjection(h, context) { 188 | hook.call(context); 189 | return originalRender(h, context); 190 | }; 191 | } 192 | else { 193 | // inject component registration as beforeCreate hook 194 | const existing = options.beforeCreate; 195 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 196 | } 197 | } 198 | return script; 199 | } 200 | 201 | /* script */ 202 | const __vue_script__ = script; 203 | /* template */ 204 | var __vue_render__ = function () { 205 | var _vm = this; 206 | var _h = _vm.$createElement; 207 | var _c = _vm._self._c || _h; 208 | return _c(_vm.state.wrapperTag, { 209 | tag: "component", 210 | class: ['v-lazy-component', { 211 | 'v-lazy-component--loading': !_vm.state.isIntersected, 212 | 'v-lazy-component--loaded': _vm.state.isIntersected 213 | }], 214 | style: { 215 | minWidth: '1px', 216 | minHeight: '1px' 217 | } 218 | }, [_vm.state.isIntersected ? _vm._t("default") : _vm._e(), !_vm.state.isIntersected ? _vm._t("placeholder") : _vm._e()], 2); 219 | }; 220 | var __vue_staticRenderFns__ = []; 221 | 222 | /* style */ 223 | const __vue_inject_styles__ = undefined; 224 | /* scoped */ 225 | const __vue_scope_id__ = undefined; 226 | /* module identifier */ 227 | const __vue_module_identifier__ = undefined; 228 | /* functional template */ 229 | const __vue_is_functional_template__ = false; 230 | /* style inject */ 231 | 232 | /* style inject SSR */ 233 | 234 | /* style inject shadow dom */ 235 | 236 | const __vue_component__ = /*#__PURE__*/normalizeComponent({ 237 | render: __vue_render__, 238 | staticRenderFns: __vue_staticRenderFns__ 239 | }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined); 240 | 241 | // Import vue component 242 | 243 | // install function executed by Vue.use() 244 | const install = function installLazyComponent(Vue) { 245 | if (install.installed) return; 246 | install.installed = true; 247 | Vue.component('LazyComponent', __vue_component__); 248 | }; 249 | 250 | // Create module definition for Vue.use() 251 | const plugin = { 252 | install 253 | }; 254 | 255 | // To auto-install on non-es builds, when vue is found 256 | // eslint-disable-next-line no-redeclare 257 | /* global window, global */ 258 | { 259 | let GlobalVue = null; 260 | if (typeof window !== 'undefined') { 261 | GlobalVue = window.Vue; 262 | } else if (typeof global !== 'undefined') { 263 | GlobalVue = global.Vue; 264 | } 265 | if (GlobalVue) { 266 | GlobalVue.use(plugin); 267 | } 268 | } 269 | 270 | // Inject install function into component - allows component 271 | // to be registered via Vue.use() as well as Vue.component() 272 | __vue_component__.install = install; 273 | 274 | // It's possible to expose named exports when writing components that can 275 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 276 | // export const RollupDemoDirective = component; 277 | 278 | exports["default"] = __vue_component__; 279 | 280 | Object.defineProperty(exports, '__esModule', { value: true }); 281 | 282 | })); 283 | -------------------------------------------------------------------------------- /dist/vue2/v-lazy-component.umd.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LazyComponent={})}(this,(function(e){"use strict";function t(e,t,s,n,i,r,o,d,a,l){"boolean"!=typeof o&&(a=d,d=o,o=!1);const c="function"==typeof s?s.options:s;let p;if(e&&e.render&&(c.render=e.render,c.staticRenderFns=e.staticRenderFns,c._compiled=!0,i&&(c.functional=!0)),n&&(c._scopeId=n),r?(p=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,a(e)),e&&e._registeredComponents&&e._registeredComponents.add(r)},c._ssrRegister=p):t&&(p=o?function(e){t.call(this,l(e,this.$root.$options.shadowRoot))}:function(e){t.call(this,d(e))}),p)if(c.functional){const e=c.render;c.render=function(t,s){return p.call(s),e(t,s)}}else{const e=c.beforeCreate;c.beforeCreate=e?[].concat(e,p):[p]}return s}const s=t({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)(e.state.wrapperTag,{tag:"component",class:["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}],style:{minWidth:"1px",minHeight:"1px"}},[e.state.isIntersected?e._t("default"):e._e(),e.state.isIntersected?e._e():e._t("placeholder")],2)},staticRenderFns:[]},undefined,{name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},data(){return{state:{wrapperTag:this.wrapperTag,isIntersected:this.isIntersected,idle:this.idle,rootMargin:this.rootMargin,threshold:this.threshold,observer:null}}},watch:{isIntersected(e){e&&(this.state.isIntersected=!0)},"state.isIntersected"(e){e&&this.$emit("intersected",this.$el)}},mounted(){this.isIntersectionObserverSupported()?this.state.isIntersected||this.state.idle||this.observe():this.state.isIntersected=!0,this.state.isIntersected&&this.$emit("intersected",this.$el)},beforeDestroy(){this.state.isIntersected||this.state.idle||this.unobserve()},methods:{isIntersectionObserverSupported:()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,observe(){const{rootMargin:e,threshold:t}=this.state,s={root:void 0,rootMargin:e,threshold:t};this.state.observer=new IntersectionObserver(this.onIntersection,s),this.state.observer.observe(this.$el)},onIntersection(e){this.state.isIntersected=e.some((e=>e.intersectionRatio>0)),this.state.isIntersected&&this.unobserve()},unobserve(){this.isIntersectionObserverSupported()&&this.state.observer.unobserve(this.$el)}}},undefined,false,undefined,!1,void 0,void 0,void 0),n=function(e){n.installed||(n.installed=!0,e.component("LazyComponent",s))},i={install:n};{let e=null;"undefined"!=typeof window?e=window.Vue:"undefined"!=typeof global&&(e=global.Vue),e&&e.use(i)}s.install=n,e.default=s,Object.defineProperty(e,"__esModule",{value:!0})})); 2 | -------------------------------------------------------------------------------- /dist/vue3/index.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LazyComponent={},e.Vue)}(this,(function(e,t){"use strict";var r=t.defineComponent({name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},setup(e,r){let{emit:n}=r;const o=t.ref(null),s=t.reactive({wrapperTag:e.wrapperTag,isIntersected:e.isIntersected,idle:e.idle,rootMargin:e.rootMargin,threshold:e.threshold,observer:null});t.watch((()=>e.isIntersected),(e=>{e&&(s.isIntersected=!0)})),t.watch((()=>s.isIntersected),(e=>{e&&n("intersected",o.value)}));const i=()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,d=e=>{s.isIntersected=e.some((e=>e.intersectionRatio>0)),s.isIntersected&&a()},a=()=>{i()&&s.observer.unobserve(o.value)};return t.onMounted((()=>{i()?s.isIntersected||s.idle||(()=>{const{rootMargin:e,threshold:t}=s,r={root:void 0,rootMargin:e,threshold:t};s.observer=new IntersectionObserver(d,r),s.observer.observe(o.value)})():s.isIntersected=!0,s.isIntersected&&n("intersected",o.value)})),t.onBeforeUnmount((()=>{s.isIntersected||s.idle||a()})),{rootRef:o,state:s}}});r.render=function(e,r,n,o,s,i){return t.openBlock(),t.createBlock(t.resolveDynamicComponent(e.state.wrapperTag),{ref:"rootRef",class:t.normalizeClass(["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}]),style:{minWidth:"1px",minHeight:"1px"}},{default:t.withCtx((()=>[e.state.isIntersected?t.renderSlot(e.$slots,"default",{key:0}):t.createCommentVNode("",!0),e.state.isIntersected?t.createCommentVNode("",!0):t.renderSlot(e.$slots,"placeholder",{key:1})])),_:3},8,["class"])};var n=(()=>{const e=r;return e.install=t=>{t.component("LazyComponent",e)},e})();e.default=n,Object.defineProperty(e,"__esModule",{value:!0})})); 2 | -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.cjs: -------------------------------------------------------------------------------- 1 | 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var vue=require('vue');var script = vue.defineComponent({ 2 | name: 'LazyComponent', 3 | props: { 4 | wrapperTag: { 5 | type: String, 6 | required: false, 7 | default: 'div' 8 | }, 9 | isIntersected: { 10 | type: Boolean, 11 | required: false, 12 | default: false 13 | }, 14 | idle: { 15 | type: Boolean, 16 | required: false, 17 | default: false 18 | }, 19 | /** 20 | * See IntersectionOberserver rootMargin [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 21 | */ 22 | rootMargin: { 23 | type: String, 24 | required: false, 25 | default: '0px 0px 0px 0px' 26 | }, 27 | /** 28 | * See IntersectionOberserver treshold [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 29 | */ 30 | threshold: { 31 | type: [Number, Array], 32 | required: false, 33 | default: 0 34 | } 35 | }, 36 | setup: function setup(props, _ref) { 37 | var emit = _ref.emit; 38 | var rootRef = vue.ref(null); 39 | var state = vue.reactive({ 40 | wrapperTag: props.wrapperTag, 41 | isIntersected: props.isIntersected, 42 | idle: props.idle, 43 | rootMargin: props.rootMargin, 44 | threshold: props.threshold, 45 | observer: null 46 | }); 47 | vue.watch(function () { 48 | return props.isIntersected; 49 | }, function (value) { 50 | if (value) { 51 | state.isIntersected = true; 52 | } 53 | }); 54 | vue.watch(function () { 55 | return state.isIntersected; 56 | }, function (value) { 57 | if (value) { 58 | emit('intersected', rootRef.value); 59 | } 60 | }); 61 | var isIntersectionObserverSupported = function isIntersectionObserverSupported() { 62 | return 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype && 'isIntersecting' in window.IntersectionObserverEntry.prototype; 63 | }; 64 | var onIntersection = function onIntersection(entries) { 65 | state.isIntersected = entries.some(function (entry) { 66 | return entry.intersectionRatio > 0; 67 | }); 68 | if (state.isIntersected) { 69 | unobserve(); 70 | } 71 | }; 72 | var observe = function observe() { 73 | var rootMargin = state.rootMargin, 74 | threshold = state.threshold; 75 | var config = { 76 | root: undefined, 77 | rootMargin: rootMargin, 78 | threshold: threshold 79 | }; 80 | state.observer = new IntersectionObserver(onIntersection, config); 81 | state.observer.observe(rootRef.value); 82 | }; 83 | var unobserve = function unobserve() { 84 | if (isIntersectionObserverSupported()) { 85 | state.observer.unobserve(rootRef.value); 86 | } 87 | }; 88 | vue.onMounted(function () { 89 | if (isIntersectionObserverSupported()) { 90 | if (!state.isIntersected && !state.idle) { 91 | observe(); 92 | } 93 | } else { 94 | state.isIntersected = true; 95 | } 96 | if (state.isIntersected) { 97 | emit('intersected', rootRef.value); 98 | } 99 | }); 100 | vue.onBeforeUnmount(function () { 101 | if (!state.isIntersected && !state.idle) { 102 | unobserve(); 103 | } 104 | }); 105 | return { 106 | rootRef: rootRef, 107 | state: state 108 | }; 109 | } 110 | });function render(_ctx, _cache, $props, $setup, $data, $options) { 111 | return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(_ctx.state.wrapperTag), { 112 | ref: "rootRef", 113 | class: vue.normalizeClass(['v-lazy-component', { 114 | 'v-lazy-component--loading': !_ctx.state.isIntersected, 115 | 'v-lazy-component--loaded': _ctx.state.isIntersected 116 | }]), 117 | style: { 118 | minWidth: '1px', 119 | minHeight: '1px' 120 | } 121 | }, { 122 | default: vue.withCtx(function () { 123 | return [_ctx.state.isIntersected ? vue.renderSlot(_ctx.$slots, "default", { 124 | key: 0 125 | }) : vue.createCommentVNode("", true), !_ctx.state.isIntersected ? vue.renderSlot(_ctx.$slots, "placeholder", { 126 | key: 1 127 | }) : vue.createCommentVNode("", true)]; 128 | }), 129 | _: 3 130 | }, 8, ["class"]); 131 | }script.render = render;// Import vue component 132 | 133 | // Default export is installable instance of component. 134 | // IIFE injects install function into component, allowing component 135 | // to be registered via Vue.use() as well as Vue.component(), 136 | var entry = /*#__PURE__*/(function () { 137 | // Assign InstallableComponent type 138 | var installable = script; 139 | 140 | // Attach install function executed by Vue.use() 141 | installable.install = function (app) { 142 | app.component('LazyComponent', installable); 143 | }; 144 | return installable; 145 | })(); 146 | 147 | // It's possible to expose named exports when writing components that can 148 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 149 | // export const RollupDemoDirective = directive; 150 | exports["default"]=entry; -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.css: -------------------------------------------------------------------------------- 1 | .v-lazy-component{position:relative} -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.global.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vue')) : 3 | typeof define === 'function' && define.amd ? define(['vue'], factory) : 4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.LazyComponent = factory(global.Vue)); 5 | })(this, (function (vue) { 'use strict'; 6 | 7 | var script = vue.defineComponent({ 8 | name: 'LazyComponent', 9 | props: { 10 | wrapperTag: { 11 | type: String, 12 | required: false, 13 | default: 'div' 14 | }, 15 | isIntersected: { 16 | type: Boolean, 17 | required: false, 18 | default: false 19 | }, 20 | idle: { 21 | type: Boolean, 22 | required: false, 23 | default: false 24 | }, 25 | /** 26 | * See IntersectionOberserver rootMargin [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 27 | */ 28 | rootMargin: { 29 | type: String, 30 | required: false, 31 | default: '0px 0px 0px 0px' 32 | }, 33 | /** 34 | * See IntersectionOberserver treshold [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 35 | */ 36 | threshold: { 37 | type: [Number, Array], 38 | required: false, 39 | default: 0 40 | } 41 | }, 42 | setup(props, _ref) { 43 | let { 44 | emit 45 | } = _ref; 46 | const rootRef = vue.ref(null); 47 | const state = vue.reactive({ 48 | wrapperTag: props.wrapperTag, 49 | isIntersected: props.isIntersected, 50 | idle: props.idle, 51 | rootMargin: props.rootMargin, 52 | threshold: props.threshold, 53 | observer: null 54 | }); 55 | vue.watch(() => props.isIntersected, value => { 56 | if (value) { 57 | state.isIntersected = true; 58 | } 59 | }); 60 | vue.watch(() => state.isIntersected, value => { 61 | if (value) { 62 | emit('intersected', rootRef.value); 63 | } 64 | }); 65 | const isIntersectionObserverSupported = () => { 66 | return 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype && 'isIntersecting' in window.IntersectionObserverEntry.prototype; 67 | }; 68 | const onIntersection = entries => { 69 | state.isIntersected = entries.some(entry => entry.intersectionRatio > 0); 70 | if (state.isIntersected) { 71 | unobserve(); 72 | } 73 | }; 74 | const observe = () => { 75 | const { 76 | rootMargin, 77 | threshold 78 | } = state; 79 | const config = { 80 | root: undefined, 81 | rootMargin, 82 | threshold 83 | }; 84 | state.observer = new IntersectionObserver(onIntersection, config); 85 | state.observer.observe(rootRef.value); 86 | }; 87 | const unobserve = () => { 88 | if (isIntersectionObserverSupported()) { 89 | state.observer.unobserve(rootRef.value); 90 | } 91 | }; 92 | vue.onMounted(() => { 93 | if (isIntersectionObserverSupported()) { 94 | if (!state.isIntersected && !state.idle) { 95 | observe(); 96 | } 97 | } else { 98 | state.isIntersected = true; 99 | } 100 | if (state.isIntersected) { 101 | emit('intersected', rootRef.value); 102 | } 103 | }); 104 | vue.onBeforeUnmount(() => { 105 | if (!state.isIntersected && !state.idle) { 106 | unobserve(); 107 | } 108 | }); 109 | return { 110 | rootRef, 111 | state 112 | }; 113 | } 114 | }); 115 | 116 | function render(_ctx, _cache, $props, $setup, $data, $options) { 117 | return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(_ctx.state.wrapperTag), { 118 | ref: "rootRef", 119 | class: vue.normalizeClass(['v-lazy-component', { 120 | 'v-lazy-component--loading': !_ctx.state.isIntersected, 121 | 'v-lazy-component--loaded': _ctx.state.isIntersected 122 | }]), 123 | style: { 124 | minWidth: '1px', 125 | minHeight: '1px' 126 | } 127 | }, { 128 | default: vue.withCtx(() => [_ctx.state.isIntersected ? vue.renderSlot(_ctx.$slots, "default", { 129 | key: 0 130 | }) : vue.createCommentVNode("", true), !_ctx.state.isIntersected ? vue.renderSlot(_ctx.$slots, "placeholder", { 131 | key: 1 132 | }) : vue.createCommentVNode("", true)]), 133 | _: 3 134 | }, 8, ["class"]); 135 | } 136 | 137 | script.render = render; 138 | 139 | // Import vue component 140 | 141 | // Default export is installable instance of component. 142 | // IIFE injects install function into component, allowing component 143 | // to be registered via Vue.use() as well as Vue.component(), 144 | var entry = /*#__PURE__*/(() => { 145 | // Assign InstallableComponent type 146 | const installable = script; 147 | 148 | // Attach install function executed by Vue.use() 149 | installable.install = app => { 150 | app.component('LazyComponent', installable); 151 | }; 152 | return installable; 153 | })(); 154 | 155 | // It's possible to expose named exports when writing components that can 156 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 157 | // export const RollupDemoDirective = directive; 158 | 159 | return entry; 160 | 161 | })); 162 | -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.global.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("vue")):"function"==typeof define&&define.amd?define(["vue"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).LazyComponent=t(e.Vue)}(this,(function(e){"use strict";var t=e.defineComponent({name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},setup(t,r){let{emit:n}=r;const o=e.ref(null),s=e.reactive({wrapperTag:t.wrapperTag,isIntersected:t.isIntersected,idle:t.idle,rootMargin:t.rootMargin,threshold:t.threshold,observer:null});e.watch((()=>t.isIntersected),(e=>{e&&(s.isIntersected=!0)})),e.watch((()=>s.isIntersected),(e=>{e&&n("intersected",o.value)}));const i=()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,d=e=>{s.isIntersected=e.some((e=>e.intersectionRatio>0)),s.isIntersected&&a()},a=()=>{i()&&s.observer.unobserve(o.value)};return e.onMounted((()=>{i()?s.isIntersected||s.idle||(()=>{const{rootMargin:e,threshold:t}=s,r={root:void 0,rootMargin:e,threshold:t};s.observer=new IntersectionObserver(d,r),s.observer.observe(o.value)})():s.isIntersected=!0,s.isIntersected&&n("intersected",o.value)})),e.onBeforeUnmount((()=>{s.isIntersected||s.idle||a()})),{rootRef:o,state:s}}});return t.render=function(t,r,n,o,s,i){return e.openBlock(),e.createBlock(e.resolveDynamicComponent(t.state.wrapperTag),{ref:"rootRef",class:e.normalizeClass(["v-lazy-component",{"v-lazy-component--loading":!t.state.isIntersected,"v-lazy-component--loaded":t.state.isIntersected}]),style:{minWidth:"1px",minHeight:"1px"}},{default:e.withCtx((()=>[t.state.isIntersected?e.renderSlot(t.$slots,"default",{key:0}):e.createCommentVNode("",!0),t.state.isIntersected?e.createCommentVNode("",!0):e.renderSlot(t.$slots,"placeholder",{key:1})])),_:3},8,["class"])},(()=>{const e=t;return e.install=t=>{t.component("LazyComponent",e)},e})()})); 2 | -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.min.cjs: -------------------------------------------------------------------------------- 1 | "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("vue"),t=e.defineComponent({name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},setup:function(t,r){var n=r.emit,o=e.ref(null),i=e.reactive({wrapperTag:t.wrapperTag,isIntersected:t.isIntersected,idle:t.idle,rootMargin:t.rootMargin,threshold:t.threshold,observer:null});e.watch((function(){return t.isIntersected}),(function(e){e&&(i.isIntersected=!0)})),e.watch((function(){return i.isIntersected}),(function(e){e&&n("intersected",o.value)}));var s=function(){return"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype},a=function(e){i.isIntersected=e.some((function(e){return e.intersectionRatio>0})),i.isIntersected&&d()},d=function(){s()&&i.observer.unobserve(o.value)};return e.onMounted((function(){var e;s()?i.isIntersected||i.idle||(e={root:void 0,rootMargin:i.rootMargin,threshold:i.threshold},i.observer=new IntersectionObserver(a,e),i.observer.observe(o.value)):i.isIntersected=!0,i.isIntersected&&n("intersected",o.value)})),e.onBeforeUnmount((function(){i.isIntersected||i.idle||d()})),{rootRef:o,state:i}}});t.render=function(t,r,n,o,i,s){return e.openBlock(),e.createBlock(e.resolveDynamicComponent(t.state.wrapperTag),{ref:"rootRef",class:e.normalizeClass(["v-lazy-component",{"v-lazy-component--loading":!t.state.isIntersected,"v-lazy-component--loaded":t.state.isIntersected}]),style:{minWidth:"1px",minHeight:"1px"}},{default:e.withCtx((function(){return[t.state.isIntersected?e.renderSlot(t.$slots,"default",{key:0}):e.createCommentVNode("",!0),t.state.isIntersected?e.createCommentVNode("",!0):e.renderSlot(t.$slots,"placeholder",{key:1})]})),_:3},8,["class"])};var r=function(){var e=t;return e.install=function(t){t.component("LazyComponent",e)},e}();exports.default=r; -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.min.mjs: -------------------------------------------------------------------------------- 1 | import{defineComponent as e,ref as t,reactive as r,watch as n,onMounted as s,onBeforeUnmount as o,openBlock as i,createBlock as d,resolveDynamicComponent as a,normalizeClass as l,withCtx as c,renderSlot as p,createCommentVNode as u}from"vue";var v=e({name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},setup(e,i){let{emit:d}=i;const a=t(null),l=r({wrapperTag:e.wrapperTag,isIntersected:e.isIntersected,idle:e.idle,rootMargin:e.rootMargin,threshold:e.threshold,observer:null});n((()=>e.isIntersected),(e=>{e&&(l.isIntersected=!0)})),n((()=>l.isIntersected),(e=>{e&&d("intersected",a.value)}));const c=()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,p=e=>{l.isIntersected=e.some((e=>e.intersectionRatio>0)),l.isIntersected&&u()},u=()=>{c()&&l.observer.unobserve(a.value)};return s((()=>{c()?l.isIntersected||l.idle||(()=>{const{rootMargin:e,threshold:t}=l,r={root:void 0,rootMargin:e,threshold:t};l.observer=new IntersectionObserver(p,r),l.observer.observe(a.value)})():l.isIntersected=!0,l.isIntersected&&d("intersected",a.value)})),o((()=>{l.isIntersected||l.idle||u()})),{rootRef:a,state:l}}});v.render=function(e,t,r,n,s,o){return i(),d(a(e.state.wrapperTag),{ref:"rootRef",class:l(["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}]),style:{minWidth:"1px",minHeight:"1px"}},{default:c((()=>[e.state.isIntersected?p(e.$slots,"default",{key:0}):u("",!0),e.state.isIntersected?u("",!0):p(e.$slots,"placeholder",{key:1})])),_:3},8,["class"])};var I=(()=>{const e=v;return e.install=t=>{t.component("LazyComponent",e)},e})();export{I as default}; 2 | -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.mjs: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref, reactive, watch, onMounted, onBeforeUnmount, openBlock, createBlock, resolveDynamicComponent, normalizeClass, withCtx, renderSlot, createCommentVNode } from 'vue'; 2 | 3 | var script = defineComponent({ 4 | name: 'LazyComponent', 5 | props: { 6 | wrapperTag: { 7 | type: String, 8 | required: false, 9 | default: 'div' 10 | }, 11 | isIntersected: { 12 | type: Boolean, 13 | required: false, 14 | default: false 15 | }, 16 | idle: { 17 | type: Boolean, 18 | required: false, 19 | default: false 20 | }, 21 | /** 22 | * See IntersectionOberserver rootMargin [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 23 | */ 24 | rootMargin: { 25 | type: String, 26 | required: false, 27 | default: '0px 0px 0px 0px' 28 | }, 29 | /** 30 | * See IntersectionOberserver treshold [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 31 | */ 32 | threshold: { 33 | type: [Number, Array], 34 | required: false, 35 | default: 0 36 | } 37 | }, 38 | setup(props, _ref) { 39 | let { 40 | emit 41 | } = _ref; 42 | const rootRef = ref(null); 43 | const state = reactive({ 44 | wrapperTag: props.wrapperTag, 45 | isIntersected: props.isIntersected, 46 | idle: props.idle, 47 | rootMargin: props.rootMargin, 48 | threshold: props.threshold, 49 | observer: null 50 | }); 51 | watch(() => props.isIntersected, value => { 52 | if (value) { 53 | state.isIntersected = true; 54 | } 55 | }); 56 | watch(() => state.isIntersected, value => { 57 | if (value) { 58 | emit('intersected', rootRef.value); 59 | } 60 | }); 61 | const isIntersectionObserverSupported = () => { 62 | return 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype && 'isIntersecting' in window.IntersectionObserverEntry.prototype; 63 | }; 64 | const onIntersection = entries => { 65 | state.isIntersected = entries.some(entry => entry.intersectionRatio > 0); 66 | if (state.isIntersected) { 67 | unobserve(); 68 | } 69 | }; 70 | const observe = () => { 71 | const { 72 | rootMargin, 73 | threshold 74 | } = state; 75 | const config = { 76 | root: undefined, 77 | rootMargin, 78 | threshold 79 | }; 80 | state.observer = new IntersectionObserver(onIntersection, config); 81 | state.observer.observe(rootRef.value); 82 | }; 83 | const unobserve = () => { 84 | if (isIntersectionObserverSupported()) { 85 | state.observer.unobserve(rootRef.value); 86 | } 87 | }; 88 | onMounted(() => { 89 | if (isIntersectionObserverSupported()) { 90 | if (!state.isIntersected && !state.idle) { 91 | observe(); 92 | } 93 | } else { 94 | state.isIntersected = true; 95 | } 96 | if (state.isIntersected) { 97 | emit('intersected', rootRef.value); 98 | } 99 | }); 100 | onBeforeUnmount(() => { 101 | if (!state.isIntersected && !state.idle) { 102 | unobserve(); 103 | } 104 | }); 105 | return { 106 | rootRef, 107 | state 108 | }; 109 | } 110 | }); 111 | 112 | function render(_ctx, _cache, $props, $setup, $data, $options) { 113 | return openBlock(), createBlock(resolveDynamicComponent(_ctx.state.wrapperTag), { 114 | ref: "rootRef", 115 | class: normalizeClass(['v-lazy-component', { 116 | 'v-lazy-component--loading': !_ctx.state.isIntersected, 117 | 'v-lazy-component--loaded': _ctx.state.isIntersected 118 | }]), 119 | style: { 120 | minWidth: '1px', 121 | minHeight: '1px' 122 | } 123 | }, { 124 | default: withCtx(() => [_ctx.state.isIntersected ? renderSlot(_ctx.$slots, "default", { 125 | key: 0 126 | }) : createCommentVNode("", true), !_ctx.state.isIntersected ? renderSlot(_ctx.$slots, "placeholder", { 127 | key: 1 128 | }) : createCommentVNode("", true)]), 129 | _: 3 130 | }, 8, ["class"]); 131 | } 132 | 133 | script.render = render; 134 | 135 | // Import vue component 136 | 137 | // Default export is installable instance of component. 138 | // IIFE injects install function into component, allowing component 139 | // to be registered via Vue.use() as well as Vue.component(), 140 | var entry = /*#__PURE__*/(() => { 141 | // Assign InstallableComponent type 142 | const installable = script; 143 | 144 | // Attach install function executed by Vue.use() 145 | installable.install = app => { 146 | app.component('LazyComponent', installable); 147 | }; 148 | return installable; 149 | })(); 150 | 151 | // It's possible to expose named exports when writing components that can 152 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 153 | // export const RollupDemoDirective = directive; 154 | 155 | export { entry as default }; 156 | -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) : 3 | typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) : 4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.LazyComponent = {}, global.Vue)); 5 | })(this, (function (exports, vue) { 'use strict'; 6 | 7 | var script = vue.defineComponent({ 8 | name: 'LazyComponent', 9 | props: { 10 | wrapperTag: { 11 | type: String, 12 | required: false, 13 | default: 'div' 14 | }, 15 | isIntersected: { 16 | type: Boolean, 17 | required: false, 18 | default: false 19 | }, 20 | idle: { 21 | type: Boolean, 22 | required: false, 23 | default: false 24 | }, 25 | /** 26 | * See IntersectionOberserver rootMargin [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 27 | */ 28 | rootMargin: { 29 | type: String, 30 | required: false, 31 | default: '0px 0px 0px 0px' 32 | }, 33 | /** 34 | * See IntersectionOberserver treshold [docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 35 | */ 36 | threshold: { 37 | type: [Number, Array], 38 | required: false, 39 | default: 0 40 | } 41 | }, 42 | setup(props, _ref) { 43 | let { 44 | emit 45 | } = _ref; 46 | const rootRef = vue.ref(null); 47 | const state = vue.reactive({ 48 | wrapperTag: props.wrapperTag, 49 | isIntersected: props.isIntersected, 50 | idle: props.idle, 51 | rootMargin: props.rootMargin, 52 | threshold: props.threshold, 53 | observer: null 54 | }); 55 | vue.watch(() => props.isIntersected, value => { 56 | if (value) { 57 | state.isIntersected = true; 58 | } 59 | }); 60 | vue.watch(() => state.isIntersected, value => { 61 | if (value) { 62 | emit('intersected', rootRef.value); 63 | } 64 | }); 65 | const isIntersectionObserverSupported = () => { 66 | return 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype && 'isIntersecting' in window.IntersectionObserverEntry.prototype; 67 | }; 68 | const onIntersection = entries => { 69 | state.isIntersected = entries.some(entry => entry.intersectionRatio > 0); 70 | if (state.isIntersected) { 71 | unobserve(); 72 | } 73 | }; 74 | const observe = () => { 75 | const { 76 | rootMargin, 77 | threshold 78 | } = state; 79 | const config = { 80 | root: undefined, 81 | rootMargin, 82 | threshold 83 | }; 84 | state.observer = new IntersectionObserver(onIntersection, config); 85 | state.observer.observe(rootRef.value); 86 | }; 87 | const unobserve = () => { 88 | if (isIntersectionObserverSupported()) { 89 | state.observer.unobserve(rootRef.value); 90 | } 91 | }; 92 | vue.onMounted(() => { 93 | if (isIntersectionObserverSupported()) { 94 | if (!state.isIntersected && !state.idle) { 95 | observe(); 96 | } 97 | } else { 98 | state.isIntersected = true; 99 | } 100 | if (state.isIntersected) { 101 | emit('intersected', rootRef.value); 102 | } 103 | }); 104 | vue.onBeforeUnmount(() => { 105 | if (!state.isIntersected && !state.idle) { 106 | unobserve(); 107 | } 108 | }); 109 | return { 110 | rootRef, 111 | state 112 | }; 113 | } 114 | }); 115 | 116 | function render(_ctx, _cache, $props, $setup, $data, $options) { 117 | return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(_ctx.state.wrapperTag), { 118 | ref: "rootRef", 119 | class: vue.normalizeClass(['v-lazy-component', { 120 | 'v-lazy-component--loading': !_ctx.state.isIntersected, 121 | 'v-lazy-component--loaded': _ctx.state.isIntersected 122 | }]), 123 | style: { 124 | minWidth: '1px', 125 | minHeight: '1px' 126 | } 127 | }, { 128 | default: vue.withCtx(() => [_ctx.state.isIntersected ? vue.renderSlot(_ctx.$slots, "default", { 129 | key: 0 130 | }) : vue.createCommentVNode("", true), !_ctx.state.isIntersected ? vue.renderSlot(_ctx.$slots, "placeholder", { 131 | key: 1 132 | }) : vue.createCommentVNode("", true)]), 133 | _: 3 134 | }, 8, ["class"]); 135 | } 136 | 137 | script.render = render; 138 | 139 | // Import vue component 140 | 141 | // Default export is installable instance of component. 142 | // IIFE injects install function into component, allowing component 143 | // to be registered via Vue.use() as well as Vue.component(), 144 | var entry = /*#__PURE__*/(() => { 145 | // Assign InstallableComponent type 146 | const installable = script; 147 | 148 | // Attach install function executed by Vue.use() 149 | installable.install = app => { 150 | app.component('LazyComponent', installable); 151 | }; 152 | return installable; 153 | })(); 154 | 155 | // It's possible to expose named exports when writing components that can 156 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 157 | // export const RollupDemoDirective = directive; 158 | 159 | exports["default"] = entry; 160 | 161 | Object.defineProperty(exports, '__esModule', { value: true }); 162 | 163 | })); 164 | -------------------------------------------------------------------------------- /dist/vue3/v-lazy-component.umd.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LazyComponent={},e.Vue)}(this,(function(e,t){"use strict";var r=t.defineComponent({name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},setup(e,r){let{emit:n}=r;const o=t.ref(null),s=t.reactive({wrapperTag:e.wrapperTag,isIntersected:e.isIntersected,idle:e.idle,rootMargin:e.rootMargin,threshold:e.threshold,observer:null});t.watch((()=>e.isIntersected),(e=>{e&&(s.isIntersected=!0)})),t.watch((()=>s.isIntersected),(e=>{e&&n("intersected",o.value)}));const i=()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,d=e=>{s.isIntersected=e.some((e=>e.intersectionRatio>0)),s.isIntersected&&a()},a=()=>{i()&&s.observer.unobserve(o.value)};return t.onMounted((()=>{i()?s.isIntersected||s.idle||(()=>{const{rootMargin:e,threshold:t}=s,r={root:void 0,rootMargin:e,threshold:t};s.observer=new IntersectionObserver(d,r),s.observer.observe(o.value)})():s.isIntersected=!0,s.isIntersected&&n("intersected",o.value)})),t.onBeforeUnmount((()=>{s.isIntersected||s.idle||a()})),{rootRef:o,state:s}}});r.render=function(e,r,n,o,s,i){return t.openBlock(),t.createBlock(t.resolveDynamicComponent(e.state.wrapperTag),{ref:"rootRef",class:t.normalizeClass(["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}]),style:{minWidth:"1px",minHeight:"1px"}},{default:t.withCtx((()=>[e.state.isIntersected?t.renderSlot(e.$slots,"default",{key:0}):t.createCommentVNode("",!0),e.state.isIntersected?t.createCommentVNode("",!0):t.renderSlot(e.$slots,"placeholder",{key:1})])),_:3},8,["class"])};var n=(()=>{const e=r;return e.install=t=>{t.component("LazyComponent",e)},e})();e.default=n,Object.defineProperty(e,"__esModule",{value:!0})})); 2 | -------------------------------------------------------------------------------- /docs/.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .output 4 | .nuxt -------------------------------------------------------------------------------- /docs/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@nuxt/eslint-config', 4 | rules: { 5 | 'vue/max-attributes-per-line': 'off', 6 | 'vue/multi-word-component-names': 'off' 7 | } 8 | } -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.iml 3 | .idea 4 | *.log* 5 | .nuxt 6 | .vscode 7 | .DS_Store 8 | coverage 9 | dist 10 | sw.* 11 | .env 12 | .output 13 | 14 | .vercel 15 | -------------------------------------------------------------------------------- /docs/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /docs/.nvmrc: -------------------------------------------------------------------------------- 1 | v18.x.x 2 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Docus Starter 2 | 3 | Starter template for [Docus](https://docus.dev). 4 | 5 | ## Clone 6 | 7 | Clone the repository (using `nuxi`): 8 | 9 | ```bash 10 | npx nuxi init -t themes/docus 11 | ``` 12 | 13 | ## Setup 14 | 15 | Install dependencies: 16 | 17 | ```bash 18 | yarn install 19 | ``` 20 | 21 | ## Development 22 | 23 | ```bash 24 | yarn dev 25 | ``` 26 | 27 | ## Edge Side Rendering 28 | 29 | Can be deployed to Vercel Functions, Netlify Functions, AWS, and most Node-compatible environments. 30 | 31 | Look at all the available presets [here](https://v3.nuxtjs.org/guide/deploy/presets). 32 | 33 | ```bash 34 | yarn build 35 | ``` 36 | 37 | ## Static Generation 38 | 39 | Use the `generate` command to build your application. 40 | 41 | The HTML files will be generated in the .output/public directory and ready to be deployed to any static compatible hosting. 42 | 43 | ```bash 44 | yarn generate 45 | ``` 46 | 47 | ## Preview build 48 | 49 | You might want to preview the result of your build locally, to do so, run the following command: 50 | 51 | ```bash 52 | yarn preview 53 | ``` 54 | 55 | --- 56 | 57 | For a detailed explanation of how things work, check out [Docus](https://docus.dev). 58 | -------------------------------------------------------------------------------- /docs/app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | docus: { 3 | title: 'v-lazy-component', 4 | description: 'Vue component render when visible 👁️⚡️', 5 | image: '/media/logo.png', 6 | socials: { 7 | twitter: 'selimdoyranli', 8 | github: 'selimdoyranli/v-lazy-component' 9 | }, 10 | aside: { 11 | level: 0, 12 | exclude: [] 13 | }, 14 | header: { 15 | logo: false 16 | }, 17 | footer: { 18 | iconLinks: [ 19 | { 20 | href: 'https://github.com/sponsors/selimdoyranli', 21 | icon: 'simple-icons:githubsponsors' 22 | } 23 | ] 24 | } 25 | } 26 | }) 27 | -------------------------------------------------------------------------------- /docs/components/AppCard.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 20 | 21 | 42 | -------------------------------------------------------------------------------- /docs/components/AppDemo.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 77 | 78 | 116 | -------------------------------------------------------------------------------- /docs/components/AppSpinner.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | -------------------------------------------------------------------------------- /docs/content/0.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: v-lazy-component 3 | navigation: false 4 | layout: page 5 | --- 6 | 7 | ::block-hero 8 | --- 9 | cta: 10 | - Get Started 11 | - /guide 12 | secondary: 13 | - Open on GitHub → 14 | - https://github.com/selimdoyranli/v-lazy-component 15 | snippet: npm install v-lazy-component 16 | --- 17 | 18 | #title 19 | v-lazy-component 20 | 21 | #description 22 | Vue component render when visible 👁️⚡️ 23 | :: 24 | -------------------------------------------------------------------------------- /docs/content/1.guide/0.index.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | Vue component render when visible 👁️⚡️ 4 | 5 | ![v-lazy-component](https://raw.githubusercontent.com/selimdoyranli/v-lazy-component/master/meta/logo.png){width="256"} 6 |

7 | ⚡️ Lightweight   8 | 🎨 Interactive   9 | 👶🏻 Easy implementation   10 | 📦 Vue2 & Vue3 support   11 |

12 | 13 | ## Installation 14 | 15 | Install the v-lazy-component 16 | 17 | ::code-group 18 | 19 | ```bash [npm] 20 | npm i v-lazy-component 21 | ``` 22 | 23 | ```bash [yarn] 24 | yarn add v-lazy-component 25 | ``` 26 | 27 | :: 28 | 29 | ::alert{type="success"} 30 | ✨ Well done! Ready for registration now 31 | :: 32 | -------------------------------------------------------------------------------- /docs/content/1.guide/1.registration/0.vue3.md: -------------------------------------------------------------------------------- 1 | # Vue3 2 | 3 | #### Global Register 4 | 5 | ```js 6 | import { createApp } from 'vue' 7 | import App from './App.vue' 8 | import LazyComponent from 'v-lazy-component' 9 | 10 | const app = createApp(App) 11 | 12 | app.use(LazyComponent) 13 | app.mount('#app') 14 | ``` 15 | 16 | #### Local Register 17 | ```html 18 | 21 | ``` 22 | #### Via CDN 23 | ```js 24 | 25 | 26 | 27 | 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/content/1.guide/1.registration/1.vue2.md: -------------------------------------------------------------------------------- 1 | # Vue2 2 | 3 | #### Global Register 4 | 5 | ```js 6 | import Vue from "vue"; 7 | import LazyComponent from "v-lazy-component/vue2"; 8 | 9 | Vue.use(LazyComponent); 10 | ``` 11 | 12 | #### Local Register 13 | ```js 14 | import LazyComponent from "v-lazy-component/vue2"; 15 | 16 | export default { 17 | components: { 18 | LazyComponent 19 | } 20 | } 21 | ``` 22 | #### Via CDN 23 | ```js 24 | 25 | 26 | 27 | 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/content/1.guide/2.usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | ```html 4 | 5 | 6 | 7 | 8 | 11 | 12 | ``` 13 | 14 | idle variant 15 | ```html 16 | .... 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | ``` 28 | 29 | #### Props 30 | 31 | |Name|Description|Type|Default 32 | |--|--|--|--| 33 | |`wrapper-tag` |Html tag of lazy component|String | div 34 | |`is-intersected` |Do not wait observe, Force render |Boolean | false 35 | |`idle` |Do not use observer, wait `is-intersected` prop changes for render |Boolean | false 36 | |`root-margin` |[Intersection Observer API doc](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options)|String | 0px 0px 0px 0px 37 | |`threshold` |[Intersection Observer API doc](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options)|Number, Array| 0 38 | [See Intersection Observer API doc](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) 39 | 40 | #### Slots 41 | |`placeholder`| Content that is loaded as a placeholder until it comes into view | 42 | |--|--| 43 | 44 | #### Events 45 | |`intersected`| dispatch event when visible | 46 | |--|--| 47 | 48 | #### CSS Selectors 49 | 50 | ```css 51 | .v-lazy-component.v-lazy-component--loading { 52 | filter: blur(20px); 53 | } 54 | 55 | .v-lazy-component.v-lazy-component--loaded { 56 | filter: blur(0); 57 | transition: filter 1s; 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/content/1.guide/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Guide 2 | -------------------------------------------------------------------------------- /docs/content/2.demo.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | ::AppDemo 4 | -------------------------------------------------------------------------------- /docs/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | import { createResolver } from '@nuxt/kit' 2 | 3 | const { resolve } = createResolver(import.meta.url) 4 | 5 | export default defineNuxtConfig({ 6 | extends: '@nuxt-themes/docus', 7 | ssr: true, 8 | components: [ 9 | { 10 | prefix: '', 11 | path: resolve('./components'), 12 | global: true 13 | }, 14 | ] 15 | }) 16 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docus-starter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "nuxi dev", 7 | "build": "nuxi build", 8 | "generate": "nuxi generate", 9 | "preview": "nuxi preview", 10 | "lint": "eslint ." 11 | }, 12 | "devDependencies": { 13 | "@nuxt-themes/docus": "^1.9.0", 14 | "@nuxt/eslint-config": "^0.1.1", 15 | "eslint": "^8.35.0", 16 | "nuxt": "3.3.3" 17 | }, 18 | "dependencies": { 19 | "v-lazy-component": "^3.0.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/selimdoyranli/v-lazy-component/9c871613f0d3956fca6317cffa9b2e671ed5925e/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/public/media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/selimdoyranli/v-lazy-component/9c871613f0d3956fca6317cffa9b2e671ed5925e/docs/public/media/logo.png -------------------------------------------------------------------------------- /docs/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@nuxtjs" 4 | ], 5 | "lockFileMaintenance": { 6 | "enabled": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/tokens.config.ts: -------------------------------------------------------------------------------- 1 | import { defineTheme } from 'pinceau' 2 | 3 | export default defineTheme({ 4 | color: { 5 | primary: { 6 | 50: "#E5F8E5", 7 | 100: "#CCF2CC", 8 | 200: "#99CC99", 9 | 300: "#66CC66", 10 | 400: "#33CC33", 11 | 500: "#00CC00", 12 | 600: "#008500", 13 | 700: "#006600", 14 | 800: "#004200", 15 | 900: "#002100", 16 | } 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nuxt/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /meta/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/selimdoyranli/v-lazy-component/9c871613f0d3956fca6317cffa9b2e671ed5925e/meta/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v-lazy-component", 3 | "version": "3.0.9", 4 | "description": "Vue component render when visible 👁️⚡️", 5 | "keywords": [ 6 | "vue lazy component", 7 | "vue lazy", 8 | "vue2 lazy", 9 | "vue3 lazy" 10 | ], 11 | "author": "selimdoyranli", 12 | "contributors": [ 13 | "selimdoyranli (https://selimdoyranli.com)" 14 | ], 15 | "bugs": "selimdoyranli@gmail.com", 16 | "homepage": "https://v-lazy-component.vercel.app", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/selimdoyranli/v-lazy-component" 20 | }, 21 | "main": "dist/vue3/index.js", 22 | "module": "dist/vue3/v-lazy-component.mjs", 23 | "browser": "dist/vue3/v-lazy-component.global.js", 24 | "unpkg": "dist/vue3/v-lazy-component.global.js", 25 | "exports": { 26 | ".": { 27 | "require": "./dist/vue3/v-lazy-component.cjs", 28 | "import": "./dist/vue3/index.js", 29 | "browser": "./dist/vue3/v-lazy-component.global.js", 30 | "umd": "./dist/vue3/v-lazy-component.umd.js", 31 | "unpkg": "./dist/vue3/v-lazy-component.global.js", 32 | "module": "./dist/vue3/v-lazy-component.mjs" 33 | }, 34 | "./vue2": "./dist/vue2/index.js" 35 | }, 36 | "files": [ 37 | "dist", 38 | "vue2", 39 | "meta", 40 | "CHANGELOG.md" 41 | ], 42 | "sideEffects": false, 43 | "scripts": { 44 | "build:vue3": "cross-env NODE_ENV=production rollup --config build/rollup.config.vue3.js", 45 | "build:umd:vue3": "cross-env NODE_ENV=production rollup --config build/rollup.config.vue3.js --format umd", 46 | "build:es:vue3": "cross-env NODE_ENV=production rollup --config build/rollup.config.vue3.js --format es", 47 | "build:cjs:vue3": "cross-env NODE_ENV=production rollup --config build/rollup.config.vue3.js --format cjs", 48 | "build:vue2": "cross-env NODE_ENV=production rollup --config build/rollup.config.vue2.js", 49 | "build:umd:vue2": "cross-env NODE_ENV=production rollup --config build/rollup.config.vue2.js --format umd", 50 | "build:es:vue2": "cross-env NODE_ENV=production rollup --config build/rollup.config.vue2.js --format es", 51 | "build:cjs:vue2": "cross-env NODE_ENV=production rollup --config build/rollup.config.vue2.js --format cjs", 52 | "build": "yarn build:vue3 && yarn build:vue2", 53 | "commit": "cz", 54 | "changelog": "changelogen", 55 | "lint:eslint": "eslint --ext .js,.ts,.json,.vue,.pug --ignore-path .gitignore --ignore-path .eslintignore .", 56 | "lint:eslint:fix": "eslint --fix --ext .js,.ts,.json,.vue,.pug --ignore-path .gitignore --ignore-path .eslintignore .", 57 | "lint:stylelint": "stylelint \"**/*.{css,sass,scss,less,stylus,vue}\" --ignore-path .stylelintignore", 58 | "lint:stylelint:fix": "stylelint --fix \"**/*.{css,sass,scss,less,stylus,vue}\" --ignore-path .stylelintignore", 59 | "prettier": "prettier --config ./.prettierrc.js --ignore-path ./.prettierignore --write \"**/*.{js,ts,json,css,scss,vue,html,pug}\" --end-of-line crlf" 60 | }, 61 | "lint-staged": { 62 | "*.{ts,js,vue}": [ 63 | "yarn lint:eslint", 64 | "yarn prettier" 65 | ], 66 | "**/*.{css,sass,scss,less,stylus,vue}": [ 67 | "yarn lint:stylelint", 68 | "yarn prettier" 69 | ] 70 | }, 71 | "dependencies": {}, 72 | "devDependencies": { 73 | "@babel/core": "^7.12.10", 74 | "@babel/plugin-transform-modules-umd": "^7.12.1", 75 | "@babel/preset-env": "^7.12.10", 76 | "@babel/preset-typescript": "^7.21.5", 77 | "@commitlint/cli": "^17.6.3", 78 | "@commitlint/config-conventional": "^17.6.3", 79 | "@prettier/plugin-pug": "^2.4.1", 80 | "@rollup/plugin-alias": "^3.1.1", 81 | "@rollup/plugin-babel": "^6.0.3", 82 | "@rollup/plugin-commonjs": "^24.1.0", 83 | "@rollup/plugin-node-resolve": "^15.0.2", 84 | "@rollup/plugin-replace": "^5.0.2", 85 | "@types/node": "^18.13.0", 86 | "@types/stylelint": "14.0.0", 87 | "@typescript-eslint/eslint-plugin": "^5.59.2", 88 | "@typescript-eslint/parser": "^5.14.0", 89 | "@vue/compiler-sfc": "^3.0.4", 90 | "autoprefixer": "^10.1.0", 91 | "babel-core": "^7.0.0-bridge.0", 92 | "babel-plugin-rename-umd-globals": "^1.0.0", 93 | "changelogen": "^0.5.3", 94 | "commitizen": "^4.3.0", 95 | "cross-env": "^7.0.3", 96 | "cz-conventional-changelog": "^3.3.0", 97 | "eslint": "^8.34.0", 98 | "eslint-config-prettier": "^8.6.0", 99 | "eslint-loader": "^4.0.2", 100 | "eslint-plugin-prettier": "^4.2.1", 101 | "eslint-plugin-vue": "^9.9.0", 102 | "flush-promises": "^1.0.2", 103 | "husky": "4.2.5", 104 | "lint-staged": "^13.2.2", 105 | "postcss": "^8.2.1", 106 | "postcss-html": "^1.5.0", 107 | "postcss-scss": "^4.0.6", 108 | "prettier": "^2.8.4", 109 | "pug": "^3.0.2", 110 | "pug-plain-loader": "^1.1.0", 111 | "rollup": "^2.34.2", 112 | "rollup-plugin-css-only": "2.0.0", 113 | "rollup-plugin-postcss": "^4.0.0", 114 | "rollup-plugin-scss": "^4.0.0", 115 | "rollup-plugin-terser": "^7.0.2", 116 | "sass": "^1.58.0", 117 | "sass-loader": "10.1.1", 118 | "stylelint": "14.16.1", 119 | "stylelint-config-prettier": "^9.0.5", 120 | "stylelint-config-rational-order": "^0.1.2", 121 | "stylelint-order": "^6.0.2", 122 | "stylelint-scss": "^4.4.0", 123 | "typescript": "^4.1.2", 124 | "vue-eslint-parser": "^9.1.0", 125 | "vue-eslint-parser-template-tokenizer-pug": "^0.4.10", 126 | "vue-next": "npm:vue@^3.2.20", 127 | "vue-next-rollup-plugin-vue": "npm:rollup-plugin-vue@^6.0.0", 128 | "vue-prev": "npm:vue@^2.6.14", 129 | "vue-prev-composition-api": "npm:@vue/composition-api@^1.2.4", 130 | "vue-prev-rollup-plugin-vue": "npm:rollup-plugin-vue@^5.1.9", 131 | "vue-template-compiler": "^2.6.14" 132 | }, 133 | "engines": { 134 | "node": ">=12" 135 | }, 136 | "license": "MIT", 137 | "config": { 138 | "commitizen": { 139 | "path": "cz-conventional-changelog" 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue-next' 3 | 4 | const Component: DefineComponent<{}, {}, any> 5 | export default Component 6 | } 7 | -------------------------------------------------------------------------------- /src/vue2/entry.js: -------------------------------------------------------------------------------- 1 | // Import vue component 2 | import component from './v-lazy-component.vue' 3 | 4 | // install function executed by Vue.use() 5 | const install = function installLazyComponent(Vue) { 6 | if (install.installed) return 7 | install.installed = true 8 | Vue.component('LazyComponent', component) 9 | } 10 | 11 | // Create module definition for Vue.use() 12 | const plugin = { 13 | install 14 | } 15 | 16 | // To auto-install on non-es builds, when vue is found 17 | // eslint-disable-next-line no-redeclare 18 | /* global window, global */ 19 | if ('false' === process.env.ES_BUILD) { 20 | let GlobalVue = null 21 | 22 | if (typeof window !== 'undefined') { 23 | GlobalVue = window.Vue 24 | } else if (typeof global !== 'undefined') { 25 | GlobalVue = global.Vue 26 | } 27 | 28 | if (GlobalVue) { 29 | GlobalVue.use(plugin) 30 | } 31 | } 32 | 33 | // Inject install function into component - allows component 34 | // to be registered via Vue.use() as well as Vue.component() 35 | component.install = install 36 | 37 | // Export component by default 38 | export default component 39 | 40 | // It's possible to expose named exports when writing components that can 41 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 42 | // export const RollupDemoDirective = component; 43 | -------------------------------------------------------------------------------- /src/vue2/v-lazy-component.scss: -------------------------------------------------------------------------------- 1 | .v-lazy-component { 2 | position: relative; 3 | } 4 | -------------------------------------------------------------------------------- /src/vue2/v-lazy-component.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/vue3/entry.js: -------------------------------------------------------------------------------- 1 | // Import vue component 2 | import component from './v-lazy-component.vue' 3 | 4 | // Default export is installable instance of component. 5 | // IIFE injects install function into component, allowing component 6 | // to be registered via Vue.use() as well as Vue.component(), 7 | export default /*#__PURE__*/ (() => { 8 | // Assign InstallableComponent type 9 | const installable = component 10 | 11 | // Attach install function executed by Vue.use() 12 | installable.install = app => { 13 | app.component('LazyComponent', installable) 14 | } 15 | 16 | return installable 17 | })() 18 | 19 | // It's possible to expose named exports when writing components that can 20 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 21 | // export const RollupDemoDirective = directive; 22 | -------------------------------------------------------------------------------- /src/vue3/v-lazy-component.scss: -------------------------------------------------------------------------------- 1 | .v-lazy-component { 2 | position: relative; 3 | } 4 | -------------------------------------------------------------------------------- /src/vue3/v-lazy-component.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "declaration": true, 7 | "declarationDir": "dist/types", 8 | "noUnusedLocals": true, 9 | "noUnusedParameters": true, 10 | "importHelpers": true, 11 | "moduleResolution": "node", 12 | "experimentalDecorators": true, 13 | "esModuleInterop": true, 14 | "allowSyntheticDefaultImports": true, 15 | "sourceMap": true, 16 | "baseUrl": ".", 17 | "newLine": "crlf", 18 | "types": ["node", "@types/node", "vue-prev", "vue-next"], 19 | "paths": { 20 | "@/*": ["src/*"] 21 | }, 22 | "plugins": [ 23 | { 24 | "transform": "@zerollup/ts-transform-paths", 25 | "exclude": ["*"] 26 | } 27 | ], 28 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"] 29 | }, 30 | "exclude": ["node_modules", "dist", "**/*.spec.ts"] 31 | } 32 | -------------------------------------------------------------------------------- /vue2/README.md: -------------------------------------------------------------------------------- 1 | entry point for vue2 include 2 | 3 | ```js 4 | import LazyComponent from 'v-lazy-component/vue2' 5 | ``` 6 | -------------------------------------------------------------------------------- /vue2/index.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LazyComponent={})}(this,(function(e){"use strict";function t(e,t,s,n,i,r,o,d,a,l){"boolean"!=typeof o&&(a=d,d=o,o=!1);const c="function"==typeof s?s.options:s;let p;if(e&&e.render&&(c.render=e.render,c.staticRenderFns=e.staticRenderFns,c._compiled=!0,i&&(c.functional=!0)),n&&(c._scopeId=n),r?(p=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,a(e)),e&&e._registeredComponents&&e._registeredComponents.add(r)},c._ssrRegister=p):t&&(p=o?function(e){t.call(this,l(e,this.$root.$options.shadowRoot))}:function(e){t.call(this,d(e))}),p)if(c.functional){const e=c.render;c.render=function(t,s){return p.call(s),e(t,s)}}else{const e=c.beforeCreate;c.beforeCreate=e?[].concat(e,p):[p]}return s}const s=t({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)(e.state.wrapperTag,{tag:"component",class:["v-lazy-component",{"v-lazy-component--loading":!e.state.isIntersected,"v-lazy-component--loaded":e.state.isIntersected}],style:{minWidth:"1px",minHeight:"1px"}},[e.state.isIntersected?e._t("default"):e._e(),e.state.isIntersected?e._e():e._t("placeholder")],2)},staticRenderFns:[]},undefined,{name:"LazyComponent",props:{wrapperTag:{type:String,required:!1,default:"div"},isIntersected:{type:Boolean,required:!1,default:!1},idle:{type:Boolean,required:!1,default:!1},rootMargin:{type:String,required:!1,default:"0px 0px 0px 0px"},threshold:{type:[Number,Array],required:!1,default:0}},data(){return{state:{wrapperTag:this.wrapperTag,isIntersected:this.isIntersected,idle:this.idle,rootMargin:this.rootMargin,threshold:this.threshold,observer:null}}},watch:{isIntersected(e){e&&(this.state.isIntersected=!0)},"state.isIntersected"(e){e&&this.$emit("intersected",this.$el)}},mounted(){this.isIntersectionObserverSupported()?this.state.isIntersected||this.state.idle||this.observe():this.state.isIntersected=!0,this.state.isIntersected&&this.$emit("intersected",this.$el)},beforeDestroy(){this.state.isIntersected||this.state.idle||this.unobserve()},methods:{isIntersectionObserverSupported:()=>"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype&&"isIntersecting"in window.IntersectionObserverEntry.prototype,observe(){const{rootMargin:e,threshold:t}=this.state,s={root:void 0,rootMargin:e,threshold:t};this.state.observer=new IntersectionObserver(this.onIntersection,s),this.state.observer.observe(this.$el)},onIntersection(e){this.state.isIntersected=e.some((e=>e.intersectionRatio>0)),this.state.isIntersected&&this.unobserve()},unobserve(){this.isIntersectionObserverSupported()&&this.state.observer.unobserve(this.$el)}}},undefined,false,undefined,!1,void 0,void 0,void 0),n=function(e){n.installed||(n.installed=!0,e.component("LazyComponent",s))},i={install:n};{let e=null;"undefined"!=typeof window?e=window.Vue:"undefined"!=typeof global&&(e=global.Vue),e&&e.use(i)}s.install=n,e.default=s,Object.defineProperty(e,"__esModule",{value:!0})})); 2 | --------------------------------------------------------------------------------