Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nanocomponent

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nanocomponent - npm Package Compare versions

Comparing version 1.0.0 to 1.1.0

example.js

153

index.js

@@ -1,10 +0,9 @@

var widgetEl = require('cache-element/widget')
var observeResize = require('observe-resize')
var onIntersect = require('on-intersect')
var politeEl = require('polite-element')
var cachedEl = require('cache-element')
var onload = require('on-load')
var assert = require('assert')
var isDom = require('is-dom')
var html = require('bel')
var elType = 'div'
module.exports = nanocomponent

@@ -17,13 +16,88 @@

if (isDom(val)) return staticEl(val)
if (typeof val === 'function') return cachedEl(val)
if (isDom(val)) return createStaticElement(val)
if (typeof val === 'function') return createDynamicElement(val)
if (val.placeholder) {
assert.equal(typeof val.render, 'function', 'nanocomponent: .placeholder cannot exist without a .render method')
val.render = politeEl(val.placeholder, val.render)
assert.equal(typeof val.render, 'function', 'nanocomponent: needs a .render function')
var placeholderHandler = val.placeholder
var onunloadHandler = val.onunload
var onresizeHandler = val.onresize
var onenterHandler = val.onenter
var onexitHandler = val.onexit
var onloadHandler = val.onload
var renderHandler = val.render
var stopPlaceholderResize = null
var stopRenderResize = null
var enableIntersect = null
var enableResize = null
if (isDom(val)) return createStaticElement(val)
else if (typeof val === 'function') return createDynamicElement(val)
else {
if (onresizeHandler) applyResize()
if (onenterHandler || onexitHandler) applyOnintersect()
applyOnloadhandler()
if (placeholderHandler) applyPlaceholder()
return renderHandler
}
return widgetEl(val)
function applyOnloadhandler () {
var _render = renderHandler
createOnunload()
createOnload()
renderHandler = createDynamicElement(_render, onloadHandler, onunloadHandler)
}
function applyOnintersect () {
enableIntersect = function (el) {
onIntersect(el, onenterHandler, onexitHandler)
}
}
function applyPlaceholder () {
var _render = renderHandler
renderHandler = function () {
var args = new Array(arguments.length)
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i]
}
var el = politeEl(placeholderHandler, _render)
var ret = el.apply(el, args)
return ret
}
}
function applyResize () {
enableResize = function (el) {
stopRenderResize = observeResize(el, onresizeHandler)
}
}
function createOnunload () {
var _onunload = onunloadHandler
onunloadHandler = function (el) {
if (stopPlaceholderResize) {
stopPlaceholderResize()
stopPlaceholderResize = null
}
if (stopRenderResize) {
stopRenderResize()
stopRenderResize = null
}
if (_onunload) _onunload(el)
}
}
function createOnload () {
var _onload = onloadHandler
onloadHandler = function (el) {
if (_onload) _onload(el)
if (enableResize) enableResize(el)
if (enableIntersect) enableIntersect(el)
}
}
}
function staticEl (element) {
function createStaticElement (element) {
var isRendered = false

@@ -39,3 +113,3 @@ var isProxied = false

} else if (!isProxied) {
proxy = document.createElement(elType)
proxy = html`<div></div>`
proxy.isSameNode = function (el) {

@@ -57,1 +131,56 @@ return (el === element)

}
function createDynamicElement (render, _onload, _onunload) {
var isRendered = false
var isProxied = false
var element = null
var proxy = null
var args = null
return function () {
var _args = new Array(arguments.length)
for (var i = 0; i < _args.length; i++) {
_args[i] = arguments[i]
}
if (!isRendered) {
args = _args
element = render.apply(render, args)
onload(element, handleLoad, handleUnload)
return element
} else {
if (!compare(_args, args)) {
element = render.apply(render, args)
onload(element, handleLoad, handleUnload)
return element
} else if (!isProxied) {
proxy = html`<div></div>`
proxy.isSameNode = function (el) {
return (el === element)
}
} else {
return proxy
}
}
}
function handleLoad (el) {
isRendered = true
if (_onload) _onload(el)
}
function handleUnload (el) {
isProxied = false
proxy = null
if (_onunload) _onunload(el)
}
}
function compare (args1, args2) {
var length = args1.length
if (length !== args2.length) return false
for (var i = 0; i < length; i++) {
if (args1[i] !== args2[i]) return false
}
return true
}

22

package.json
{
"name": "nanocomponent",
"version": "1.0.0",
"version": "1.1.0",
"description": "Create performant HTML elements",
"main": "index.js",
"scripts": {
"deps": "dependency-check . && dependency-check . --extra --no-dev",
"test": "standard && npm run deps && NODE_ENV=test node test",
"test:cov": "standard && npm run deps && NODE_ENV=test istanbul cover test.js"
"deps": "dependency-check . && dependency-check . --extra --no-dev -i yo-yoify",
"start": "bankai example --open",
"test": "standard && npm run deps",
"test:cov": "standard && npm run deps"
},

@@ -19,9 +20,17 @@ "repository": "yoshuawuyts/nanocomponent",

"license": "MIT",
"browserify": {
"transform": [
"yo-yoify"
]
},
"dependencies": {
"cache-element": "^2.0.0",
"is-dom": "^1.0.6",
"observe-resize": "^1.1.0",
"on-intersect": "^1.1.0",
"on-load": "^3.2.0",
"polite-element": "^1.0.2"
"polite-element": "^1.0.2",
"yo-yoify": "^3.5.0"
},
"devDependencies": {
"bankai": "^5.2.1",
"bel": "^4.5.1",

@@ -31,2 +40,3 @@ "dependency-check": "^2.7.0",

"standard": "^8.6.0",
"tachyons": "^4.6.1",
"tape": "^4.6.3",

@@ -33,0 +43,0 @@ "uglifyify": "^3.0.4",

# nanocomponent [![stability][0]][1]
[![npm version][2]][3] [![build status][4]][5] [![test coverage][6]][7]
[![npm version][2]][3] [![build status][4]][5]
[![downloads][8]][9] [![js-standard-style][10]][11]

@@ -8,7 +8,7 @@

## Features
- works with every framework
- significantly speeds up perceived performance
- works with virtually every framework
- speeds up perceived performance
- improves rendering performance
- graciously falls back if APIs are not available
- weighs `~2kb`
- weighs `~4kb`

@@ -24,3 +24,4 @@ ## Usage

`)
console.log(staticElement())
var el = staticElement()
console.log(el)
```

@@ -35,3 +36,5 @@

})
console.log(cachedElement())
console.log(cachedElement('hey folks')) // render
console.log(cachedElement('hey folks')) // return cached element
console.log(cachedElement('hey humans')) // render again
```

@@ -53,3 +56,7 @@

})
console.log(politeElement())
setTimeout(function () {
console.log(politeElement())
}, 1000)
```

@@ -60,18 +67,58 @@

var widgetElement = component({
onload: function () {
onload: function (el) {
el.textContent = 'totally loaded now'
},
onunload: function (el) {
el.textContent = 'no more free lunch'
},
onupdate: function (foo, bar) {
// do stuff with new arguments
onupdate: function (el, verb) {
el.textContent = `totally ${verb}ing now`
},
render: function (foo, bar) {
render: function (verb) {
return html`
<p>lol not loaded yet</p>
<p>lol not ${verb}ed yet</p>
`
}
})
console.log(widgetElement())
console.log(widgetElement('load'))
var el = widgetElement('blep')
document.body.appendChild(el)
document.body.removeChild(el)
```
### Trigger lifecycle events when coming in and out of view
```js
var viewportElement = component({
onenter: function (el) {
el.textContent = 'BEHOLD THE GOBLIN'
},
onexit: function (el) {
el.textContent = 'THE PONIES HAVE COME'
},
render: function () {
return html`
<h1>WHO COULD IT BE</h1>
`
}
})
console.log(viewportElement())
```
### Trigger lifecycle events when resizing
```js
var resizeElement = component({
onresize: function (el) {
var parent = el.parentNode
console.log('element dimensions', el.getBoundingRectangle())
console.log('parent dimensions', parent.getBoundingRectangle())
},
render: function () {
return html`
<div>hello planet</div>
`
}
})
console.log(resizeElement())
```
## API

@@ -81,8 +128,128 @@ ### render = nanocomponent(HtmlOrFunctionOrObject)

are passed in:
- __HTMLElement:__ when a valid HTML node is passed
- __HTMLElement:__ cache the result of the function until it's removed from the
DOM
- __function:__ cache the result of the function until new arguments are passed
- __object:__ create an object with different methods attached
or it's removed from the DOM
- __object:__ create an object with different methods attached. Cached until
new arguments are passed in or when it's removed from the DOM
When passing an object, the availble methods are:
- __render(...args):__ (required) Render DOM elements.
- __placeholder(..args)__ Render DOM elements and delegate the `render` call to
the next `requestIdleCallback` tick. This is useful to spread CPU intensive
work over more time and not block the render loop. When executed on the
server `placeholder` will always be rendered in favor of `render`. This makes
it easier for client-side JS to pick up where the server left off
(rehydration).
- __onupdate(el, ...args):__ Allows you to change the internal DOM state when
new arguments are passed in. It's called when the returned `render()` call is
called after an initial render with different arguments. Argument equality is
shallowly checked using a `===` check on each argument. The first argument is
the currently rendered argument.
- __onenter:__ called when the element comes into view, relies on
`window.IntersectionObserver`
- __onexit:__ Called when the element goes out of view, relies on
`window.IntersectionObserver`
- __onload(el):__ Called when the element is appended onto the DOM
- __onunload(el):__ Called when the element is removed from the DOM
- __onresize:__ Called when the element changes size. :warning: This method can
be called in high frequency and can cause strain on your CPU. Caution and/or
debounce methods are advised.
### el = render(...args)
Call the corresponding `render` function an receive DOM elements. As long as an
element exists on the DOM, subsequent calls to `render` will return an empty
element with a `.isSameNode()` method on it which can be used as a caching hint
for HTML diffing trees.
## FAQ
### Why'd you build this package?
I've been building web stuff for a while now, and have seen a fair share of
frameworks become popular, take over developer mindshare and then disappear
again a few years later. With each framework iteration the basic libraries tend
to be rewritten from scratch: form validation, modals, infinite scolls, charts.
The list is long.
I think it'd be cool if we could create generic JS components that work
natively with any JS framework through slim bindings. This would encourage
reusability between frameworks, which means it becomes easier to pick up
different frameworks (no need to relearn the ecosystem) and bring new
frameworks to maturity (less new code to implement).
### This sounds a lot like WebComponents, how is this different?
WebComponents are a specification by the W3C that's been in the works for a
while now. Certain parts have been put on hold by browser vendors until kinks
are ironed out, and the some of the available parts are not widely adopted - or
at least not the way they were meant to be used.
When people talk about WebComponents they usually refer to the Custom Elements
specification. This spec allows you to create new HTML tags and provides you
with a set of lifecycle events. The `onload` / `onunload` / `render` /
`onupdate` events are indeed quite similar to Nanocomponent. The biggest
difference however, is the way in which elements are registered. Custom
Elements are globally scoped in the browser and must have unique names.
Nanocomponent is a plain JS function and will not run into namespacing issues.
It's quite feasible to wrap a Nanocomponent instance to create a Webcomponent.
The other way around is harder. Nanocomponent also exposes more events.
### I read somewhere that Nanocomponent uses some of the same techniques as React Fiber / React Stack. Could you talk some more about this?
Sure! React Fiber (or React Stack, I'm not sure what name they're ending up
with) is using the same APIs under the hood as Nanocomponent, but approaches it
from the other side of the spectrum.
Nanocomponent is intended to create individual components that can run in any
framework and have tight control over their performance. React Fiber is
a framework where the whole render tree is optimized on each loop.
Nanocomponent operates on the component layer, React Fiber at the framework
layer.
This doesn't mean that either one is "better" - every abstraction carries
overhead, and different situations require different solutions. I think it's
great performance is being tackled from multiple sides of the spectrum.
### What do you mean by "Nanocomponent works with any framework"?
Nanocomponent returns DOM elements that work in any framework that knows how to
render raw DOM nodes. This includes pretty much every popular framework and
compile-to-js language. Because the lifecycle events are self-encapsulated and
we don't expose globals this means Nanocomponent doesn't have any problems
running inside any other framework. I think Nanocomponent is quite similar in
browser framework land to how C / CPP packages operate
### I don't believe in silver bullets, tell me about the tradeoffs
All abstractions in JS come with a cost. Luckily the cost of Nanomorph is
fairly low (4kb, CPU and memory seem to be cool - haven't noticed any
significant repaint costs or anything), but it should not be neglected. Measure
and inform yourself.
Nanomorph relies on fairly new DOM APIs to do what it does. If you're running
v. old browsers this package won't help you - we're sorry. As long as
`window.MutationObserver` is available we should be good; the others are
optimizations on top. These are the fancy APIs we're using:
- `window.MutationObserver`
- `window.IntersectionObserver`
- `window.requestAnimationFrame`
- `window.requestIdleCallback`
Third of all this package is optimized for an environment that supports
`require()`. I believe in iterating on proven ideas, and given the stability of
`require()` and the tooling & community around it I'm betting that it'll stick
around for a while.
Some frameworks (like React Fiber / React Stack) might have some cool
optimizations that allow super fine grained control over each and every element
in the tree - nanocomponent is similar but has its own rules to wait for
resources, so it might turn into an interesting situation where everyone is
super politely waiting for resources and like is nice to each other. I don't
know, but I'm guessing it'll be fun hah.
Also these components won't work in environments that don't have a DOM, but I
bet it'd be cool to look at those environments and figure out which primitives
they have and create equivalent functionality through generic components.
### Uahahghhhhblll
Yes!
### Does this render on the server?
Yup, it does. If it doesn't for your particular setup we'd like to hear.
## Installation

@@ -93,2 +260,18 @@ ```sh

## See Also
- [bendrucker/document-ready](https://github.com/bendrucker/document-ready)
- [shama/bel](https://github.com/shama/bel)
- [shama/on-load](https://github.com/shama/on-load)
- [yoshuawuyts/choo](https://github.com/yoshuawuyts/choo)
- [yoshuawuyts/nanomorph](https://github.com/yoshuawuyts/nanomorph)
- [yoshuawuyts/nanoraf](https://github.com/yoshuawuyts/nanoraf)
- [yoshuawuyts/nanotick](https://github.com/yoshuawuyts/nanotick)
- [yoshuawuyts/observe-resize](https://github.com/yoshuawuyts/observe-resize)
- [yoshuawuyts/on-intersect](https://github.com/yoshuawuyts/on-intersect)
- [yoshuawuyts/polite-element](https://github.com/yoshuawuyts/polite-element)
## Similar Packages
- [shama/base-element](https://github.com/shama/base-element)
- [yoshuawuyts/cache-element](https://github.com/yoshuawuyts/cache-element)
## License

@@ -95,0 +278,0 @@ [MIT](https://tldrlegal.com/license/mit-license)

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc