nanocomponent
Advanced tools
Comparing version 5.0.3 to 6.0.0-0
@@ -7,8 +7,7 @@ // adapted from https://github.com/timwis/choo-leaflet-demo/blob/master/src/index.js | ||
var choo = require('choo') | ||
var Leaflet = require('./leaflet.js') | ||
css('./leaflet.css') | ||
css('leaflet') | ||
var leaflet = Leaflet() | ||
var leaflet = new Leaflet() | ||
var app = choo() | ||
@@ -34,3 +33,3 @@ | ||
<main> | ||
${leaflet.render({ coords: state.coords })} | ||
${leaflet.render(state.coords)} | ||
</main> | ||
@@ -37,0 +36,0 @@ </body> |
@@ -15,48 +15,35 @@ // // adapted from https://github.com/timwis/choo-leaflet-demo/blob/master/src/map.js | ||
this._log = nanologger('leaflet') | ||
this.state.map = null | ||
this.state.zoom = 12 | ||
this.map = null // capture leaflet | ||
this.coords = [0, 0] // null island | ||
} | ||
Leaflet.prototype = Object.create(Nanocomponent.prototype) | ||
Leaflet.prototype._render = function () { | ||
var self = this | ||
Leaflet.prototype._render = function (coords) { | ||
this.coords = coords | ||
return html` | ||
<div style="height: 500px"> | ||
<div id="map"></div> | ||
</div> | ||
` | ||
} | ||
if (!this.state.map) { | ||
this._element = html`<div style="height: 500px"></div>` | ||
if (this._hasWindow) this._createMap() | ||
} else { | ||
Leaflet.prototype._update = function (coords) { | ||
if (!this.map) return this._log.warn('missing map', 'failed to update') | ||
if (coords[0] !== this.coords[0] || coords[1] !== this.coords[1]) { | ||
var self = this | ||
onIdle(function () { | ||
self._updateMap() | ||
self.coords = coords | ||
self._log.info('update-map', coords) | ||
self.map.setView(coords, 12) | ||
}) | ||
} | ||
return this._element | ||
return false | ||
} | ||
Leaflet.prototype._update = function (props) { | ||
return props.coords[0] !== this.props.coords[0] || | ||
props.coords[1] !== this.props.coords[1] | ||
} | ||
Leaflet.prototype._load = function () { | ||
this.state.map.invalidateSize() | ||
this._log.info('load') | ||
} | ||
Leaflet.prototype._unload = function () { | ||
this._log.info('unload') | ||
this.state.map.remove() | ||
this.state = {} | ||
this._element = null | ||
} | ||
Leaflet.prototype._createMap = function () { | ||
var element = this._element | ||
var coords = this.props.coords | ||
var zoom = this.state.zoom | ||
Leaflet.prototype._willRender = function (el) { | ||
var coords = this.coords | ||
this._log.info('create-map', coords) | ||
var map = leaflet.map(element).setView(coords, zoom) | ||
var map = leaflet.map(el).setView(coords, 12) | ||
leaflet.tileLayer('https://stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}.{ext}', { | ||
@@ -69,9 +56,16 @@ attribution: 'Map tiles by <a href="https://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> — Map data © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>', | ||
}).addTo(map) | ||
this.state.map = map | ||
this.map = map | ||
} | ||
Leaflet.prototype._updateMap = function () { | ||
var coords = this.props.coords | ||
this._log.info('update-map', coords) | ||
this.state.map.setView(coords) | ||
Leaflet.prototype._load = function () { | ||
this._log.info('load') | ||
this.map.invalidateSize() | ||
} | ||
Leaflet.prototype._unload = function () { | ||
this._log.info('unload') | ||
this.map.remove() | ||
this.map = null | ||
this.coords = [0, 0] | ||
} |
153
index.js
@@ -0,86 +1,109 @@ | ||
var document = require('global/document') | ||
var morph = require('nanomorph') | ||
var onload = require('on-load') | ||
var assert = require('assert') | ||
var KEY = 'ncid-' + (new Date() % 9e6).toString(36) | ||
var INDEX = 0 | ||
module.exports = Nanocomponent | ||
function makeID () { | ||
return 'ncid-' + Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1) | ||
} | ||
function Nanocomponent () { | ||
this._hasWindow = typeof window !== 'undefined' | ||
this._ID = KEY + '-' + INDEX++ | ||
this._placeholder = null | ||
this._onload = onload | ||
this._element = null | ||
this._loaded = false | ||
this.props = {} | ||
this.oldProps = null | ||
this.state = {} | ||
} | ||
this._id = null // represents the id of the root node | ||
this._ncID = null // internal nanocomponent id | ||
this._proxy = null | ||
this._args = [] | ||
this._loaded = false // Used to debounce on-load when child-reordering | ||
Nanocomponent.prototype._ensureID = function () { | ||
// Get ID - needed for nanomorph child element reordering | ||
var id = this._element.getAttribute('id') | ||
if (!id) this._element.setAttribute('id', this._ID) | ||
else this._ID = id | ||
} | ||
this._handleLoad = this._handleLoad.bind(this) | ||
this._handleUnload = this._handleUnload.bind(this) | ||
Nanocomponent.prototype._rerender = function (props) { | ||
this.oldProps = this.props | ||
this.props = props | ||
this._element = this._render() | ||
this._ensureID() | ||
var self = this | ||
Object.defineProperty(this, 'element', { | ||
get: function () { | ||
var el = document.getElementById(self._id) | ||
if (el) return el.dataset.nanocomponent === self._ncID ? el : undefined | ||
} | ||
}) | ||
} | ||
Nanocomponent.prototype.render = function (props) { | ||
assert.equal(typeof this._render, 'function', 'nanocomponent: this._render should be implemented') | ||
assert.equal(typeof this._update, 'function', 'nanocomponent: this._update should be implemented') | ||
Nanocomponent.prototype.render = function () { | ||
var self = this | ||
var len = arguments.length | ||
var args = new Array(len) | ||
for (var i = 0; i < len; i++) args[i] = arguments[i] | ||
var args = new Array(arguments.length) | ||
for (var i = 0; i < arguments.length; i++) args[i] = arguments[i] | ||
if (!this._hasWindow) { | ||
this._rerender(props) | ||
return this._element | ||
} else if (!this._element) { | ||
this._rerender(props) | ||
this._onload(this._element, function () { | ||
if (self._loaded) return | ||
self._loaded = true | ||
if (self._load) { | ||
window.requestAnimationFrame(function () { | ||
self._load() | ||
}) | ||
} | ||
}, function () { | ||
if (document.body.contains(self._element)) return | ||
self._loaded = false | ||
if (self._unload) { | ||
window.requestAnimationFrame(function () { | ||
self._unload() | ||
}) | ||
} | ||
}, this._ID) | ||
return this._element | ||
} else { | ||
var shouldUpdate = this._update(props) | ||
return this._render.apply(this, args) | ||
} else if (this.element) { | ||
var shouldUpdate = this._update.apply(this, args) | ||
if (shouldUpdate) { | ||
this._rerender(props) | ||
this._args = args | ||
morph(this.element, this._handleRender(args)) | ||
if (this._didUpdate) window.requestAnimationFrame(function () { self._didUpdate(self.element) }) | ||
} | ||
if (!this._placeholder) this._placeholder = this._createPlaceholder() | ||
return this._placeholder | ||
if (!this._proxy) { this._proxy = this._createProxy() } | ||
return this._proxy | ||
} else { | ||
this._ncID = makeID() | ||
this._args = args | ||
this._proxy = null | ||
var el = this._handleRender(args) | ||
if (this._willRender) this._willRender(el) | ||
if (this._load || this._unload) { | ||
onload(el, this._handleLoad, this._handleUnload, this) | ||
} | ||
return el | ||
} | ||
} | ||
Nanocomponent.prototype._createPlaceholder = function () { | ||
var el = document.createElement('div') | ||
el.setAttribute('data-nanocomponent', '') | ||
el.setAttribute('id', this._ID) | ||
Nanocomponent.prototype._handleRender = function (args) { | ||
var el = this._render.apply(this, args) | ||
assert(el instanceof window.HTMLElement, 'nanocomponent: _render should return a DOM node') | ||
return this._brandNode(this._ensureID(el)) | ||
} | ||
Nanocomponent.prototype._createProxy = function () { | ||
var proxy = document.createElement('div') | ||
var self = this | ||
el.isSameNode = function (el) { | ||
return el === self._element | ||
this._brandNode(proxy) | ||
proxy.id = this._id | ||
proxy.isSameNode = function (el) { | ||
return (el && el.dataset.nanocomponent === self._ncID) | ||
} | ||
return el | ||
return proxy | ||
} | ||
Nanocomponent.prototype._brandNode = function (node) { | ||
node.setAttribute('data-nanocomponent', this._ncID) | ||
return node | ||
} | ||
Nanocomponent.prototype._ensureID = function (node) { | ||
if (node.id) this._id = node.id | ||
else node.id = this._id = this._ncID | ||
return node | ||
} | ||
Nanocomponent.prototype._handleLoad = function () { | ||
var self = this | ||
if (this._loaded) return // Debounce child-reorders | ||
this._loaded = true | ||
if (this._load) window.requestAnimationFrame(function () { self._load() }) | ||
} | ||
Nanocomponent.prototype._handleUnload = function () { | ||
var self = this | ||
if (this.element) return // Debounce child-reorders | ||
this._loaded = false | ||
if (this._unload) window.requestAnimationFrame(function () { self._unload() }) | ||
} | ||
Nanocomponent.prototype._render = function () { | ||
throw new Error('nanocomponent: _render should be implemented!') | ||
} | ||
Nanocomponent.prototype._update = function () { | ||
throw new Error('nanocomponent: _update should be implemented!') | ||
} |
{ | ||
"name": "nanocomponent", | ||
"version": "5.0.3", | ||
"description": "Create performant HTML elements", | ||
"version": "6.0.0-0", | ||
"description": "Native DOM components that pair nicely with DOM diffing algorithms", | ||
"main": "index.js", | ||
"scripts": { | ||
"build": "bankai build example --optimize", | ||
"deps": "dependency-check . && dependency-check . --extra --no-dev -i yo-yoify", | ||
"start": "bankai start example", | ||
"test": "standard && npm run deps", | ||
"test:cov": "standard && npm run deps" | ||
"deps": "dependency-check . && dependency-check . --extra --no-dev", | ||
"test": "standard *.js && npm run deps && NODE_ENV=test node test | tap-format-spec", | ||
"start": "bankai start example" | ||
}, | ||
"repository": "yoshuawuyts/nanocomponent", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/choojs/nanocomponent.git" | ||
}, | ||
"keywords": [ | ||
"dom", | ||
"node", | ||
"universal" | ||
"bel", | ||
"choo", | ||
"element", | ||
"thunk", | ||
"cache", | ||
"perf", | ||
"nanomorph", | ||
"morphdom", | ||
"nanocomponent", | ||
"cache-component" | ||
], | ||
"author": "Trainspotters", | ||
"contributors": [ | ||
"Bret Comnes <bcomnes@gmail.com> (http://bret.io)" | ||
], | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/choojs/nanocomponent/issues" | ||
}, | ||
"homepage": "https://github.com/choojs/nanocomponent#readme", | ||
"dependencies": { | ||
"global": "^4.3.1", | ||
"nanomorph": "^5.1.2", | ||
"on-load": "^3.2.0" | ||
}, | ||
"devDependencies": { | ||
"bankai": "^5.5.2", | ||
"bel": "^4.6.0", | ||
"choo": "^5.1.4", | ||
"choo-log": "^6.0.0", | ||
"dependency-check": "^2.7.0", | ||
"inherits": "^2.0.3", | ||
"istanbul": "^0.4.5", | ||
"leaflet": "^1.0.3", | ||
"microbounce": "^1.0.0", | ||
"nanologger": "^1.0.2", | ||
"on-idle": "^1.0.0", | ||
"standard": "^8.6.0", | ||
"tachyons": "^4.6.1", | ||
"tape": "^4.6.3", | ||
"uglifyify": "^3.0.4", | ||
"unassertify": "^2.0.4", | ||
"yo-yoify": "^3.5.0" | ||
"@tap-format/spec": "^0.2.0", | ||
"bankai": "^8.1.1", | ||
"bel": "^5.0.0", | ||
"choo": "^5.6.2", | ||
"choo-log": "^7.0.1", | ||
"dependency-check": "^2.6.0", | ||
"envify": "^4.0.0", | ||
"leaflet": "^1.1.0", | ||
"nanologger": "^1.2.0", | ||
"on-idle": "^3.1.0", | ||
"standard": "^10.0.0", | ||
"tape": "^4.6.0", | ||
"microbounce": "^1.0.0" | ||
} | ||
} |
376
README.md
@@ -5,123 +5,310 @@ # nanocomponent [![stability][0]][1] | ||
`1kb` library to wrap native DOM libraries to work with DOM diffing algorithms. | ||
Native DOM components that pair nicely with DOM diffing algorithms. | ||
## Features | ||
- Isolate native DOM libraries from DOM diffing algorithms | ||
- Makes rendering elements _very fast™_ by avoiding unnecessary rendering | ||
- Component nesting and state update passthrough | ||
- Implemented in only a few lines | ||
- Only uses native DOM methods | ||
- Class based components offering a familiar component structure | ||
- Works well with [bel][bel] and [yoyoify][yoyoify] | ||
## Usage | ||
```js | ||
// Implementer API | ||
// button.js | ||
var Nanocomponent = require('nanocomponent') | ||
var html = require('bel') | ||
function MyButton () { | ||
if (!(this instanceof MyButton)) return new MyButton() | ||
function Button () { | ||
if (!(this instanceof Button)) return new Button() | ||
this._color = null | ||
Nanocomponent.call(this) | ||
} | ||
MyButton.prototype = Object.create(Nanocomponent.prototype) | ||
Button.prototype = Object.create(Nanocomponent.prototype) | ||
MyButton.prototype._render = function () { | ||
if (!this._element) { | ||
// initial render | ||
Button.prototype._render = function (color) { | ||
this._color = color | ||
return html` | ||
<button style="background-color: ${color}"> | ||
Click Me | ||
</button> | ||
` | ||
} | ||
// Override default shallow compare _update function | ||
Button.prototype._update = function (newColor) { | ||
return newColor !== this._color | ||
} | ||
``` | ||
```js | ||
// index.js | ||
var choo = require('choo') | ||
var Button = require('./button.js') | ||
var button = Button() | ||
var app = choo() | ||
app.route('/', mainView) | ||
app.mount('body') | ||
function mainView (state, emit) { | ||
return html` | ||
<body> | ||
${button.render(state.color)} | ||
</body> | ||
` | ||
} | ||
app.use(function (state, emitter) { | ||
state.color = 'green' | ||
}) | ||
``` | ||
## Patterns | ||
These are some common patterns you might encounter when writing components. | ||
### Standalone | ||
Nanocomponents is part of the choo ecosystem, but works great standalone! | ||
```js | ||
var Button = require('./button.js') | ||
var button = new Button() | ||
// Attach to DOM | ||
document.body.appendChild(button.render('green')) | ||
// Update mounted component | ||
button.render('green') | ||
button.render('red') | ||
// Log a reference to the mounted dom node | ||
console.log(button.element) | ||
``` | ||
### Binding event handlers as component methods | ||
Sometimes it's useful to be pass around prototype methods into other functions. | ||
This can be done by binding the method that's going to be passed around: | ||
```js | ||
var Nanocomponent = require('nanocomponent') | ||
var html = require('bel') | ||
function Component () { | ||
if (!(this instanceof Button)) return new Component() | ||
Nanocomponent.call(this) | ||
// Bind the method so it can be passed around | ||
this._handleClick = this._handleClick.bind(this) | ||
} | ||
Component.prototype = Object.create(Nanocomponent.prototype) | ||
Component.prototype._handleClick = function () { | ||
console.log('element is', this.element) | ||
} | ||
Component.prototype._render = function () { | ||
return html`<div>My component</div>` | ||
} | ||
``` | ||
### ES6 Class Syntax | ||
Because Class syntax is just sugar for prototype code, Nanocomponent can be | ||
written using Classes too: | ||
```js | ||
var Nanocomponent = require('nanocomponent') | ||
var html = require('bel') | ||
class Component extends Nanocomponent { | ||
constructor () { | ||
super() | ||
this._color = null | ||
} | ||
_render (color) { | ||
this._color = color | ||
return html` | ||
<button style="background-color: ${this.props.color}"> | ||
Click Me | ||
</button> | ||
<div style="background-color: ${color}"> | ||
Color is ${color} | ||
</div> | ||
` | ||
} else { | ||
// mutate this._element | ||
this._element.style.backgroundColor = this.props.color | ||
} | ||
_update (newColor) { | ||
return newColor !== this._color | ||
} | ||
} | ||
``` | ||
MyButton.prototype._update = function (props) { | ||
return props.color !== this.props.color | ||
### Mutating the components instead of re-rendering | ||
Sometimes you might want to mutate the element that's currently mounted, rather | ||
than performing DOM diffing. Think cases like third party widgets that manage | ||
themselves. | ||
```js | ||
var Nanocomponent = require('nanocomponent') | ||
var html = require('bel') | ||
function Component () { | ||
if (!(this instanceof Button)) return new Component() | ||
Nanocomponent.call(this) | ||
this._text = '' | ||
} | ||
Component.prototype = Object.create(Nanocomponent.prototype) | ||
Component.prototype._render = function (text) { | ||
this._text = text | ||
return html`<h1>${text}</h1>` | ||
} | ||
Component.prototype._update = function (text) { | ||
if (text !== this._text) { | ||
this._text = text | ||
this.element.innerText = this._text // Directly update the element | ||
} | ||
return false // Don't call _render again | ||
} | ||
Component.prototype._unload = function (text) { | ||
console.log('No longer mounted on the DOM!') | ||
} | ||
``` | ||
### Nested components and component containers | ||
Components nest and can skip renders at intermediary levels. Components can | ||
also act as containers that shape app data flowing into view specific | ||
components. | ||
```js | ||
// Consumer API | ||
var MyButton = require('./my-button.js') | ||
var myButton = MyButton() | ||
document.body.appendChild(myButton.render()) | ||
var Nanocomponent = require('nanocomponent') | ||
var html = require('bel') | ||
var Button = require('./button.js') | ||
function Component () { | ||
if (!(this instanceof Button)) return new Component() | ||
Nanocomponent.call(this) | ||
this._button1 = new Button () | ||
this._button2 = new Button () | ||
this._button3 = new Button () | ||
} | ||
Component.prototype = Object.create(Nanocomponent.prototype) | ||
Component.prototype._render = function (state) { | ||
var colorArray = this._shapeData(state) | ||
return html` | ||
<div> | ||
${this._button1.render(colorArray[0])} | ||
${this._button2.render(colorArray[1])} | ||
${this._button3.render(colorArray[2])} | ||
</div> | ||
` | ||
} | ||
Component.prototype._update = function (state) { | ||
var colorArray = this._shapeData(state) // process app specific data in a container | ||
this._button1.render(colorArray[0]) // pass processed data to owned children components | ||
this._button2.render(colorArray[1]) | ||
this._button3.render(colorArray[2]) | ||
return false // always return false when mounted | ||
} | ||
Component.prototype._shapeData = function (state) { | ||
return [state.colors.color1, state.colors.color2, state.colors.color3] | ||
} | ||
``` | ||
## Implementing higher level APIs | ||
No matter the language, inheritance is tricky. Each layer adds more | ||
abstractions and can make it hard to understand what's going on. That's why we | ||
don't recommend doing more than one level of inheritance. However, this means | ||
that any API built on top of Nanocomponent directly shouldn't also expose a | ||
prototypical API. | ||
## FAQ | ||
### Where does this run? | ||
Make sure you're running a diffing engine that checks for `.isSameNode()`, if | ||
it doesn't you'll end up with super weird results because proxy nodes will | ||
probably be rendered which is not what should happen. Probably make sure you're | ||
using [morphdom][md] or [nanomorph][nm]. Seriously. | ||
Instead we recommend people use an interface that somewhat resembles Node's | ||
`require('events').EventEmitter` API. | ||
### What's a proxy node? | ||
It's a node that overloads `Node.isSameNode()` to compare it to another node. | ||
This is needed because a given DOM node can only exist in one DOM tree at the | ||
time, so we need a way to reference mounted nodes in the tree without actually | ||
using them. Hence the proxy pattern, and the recently added support for it in | ||
certain diffing engines: | ||
```js | ||
var MyComponent = require('./my-component') | ||
var myComponent = MyComponent() | ||
var html = require('bel') | ||
myComponent.on('render', function () { | ||
console.log('rendered') | ||
}) | ||
var el1 = html`<div>pink is the best</div>` | ||
var el2 = html`<div>blue is the best</div>` | ||
myComponent.on('load', function () { | ||
console.log('loaded on DOM') | ||
}) | ||
// let's proxy el1 | ||
var proxy = html`<div></div>` | ||
proxy.isSameNode = function (targetNode) { | ||
return (targetNode === el1) | ||
} | ||
myComponent.on('unload', function () { | ||
console.log('removed from DOM') | ||
}) | ||
document.body.appendChild(myComponent.render()) | ||
el1.isSameNode(el1) // true | ||
el1.isSameNode(el2) // false | ||
proxy.isSameNode(el1) // true | ||
proxy.isSameNode(el2) // false | ||
``` | ||
This API allows consumers of the `MyComponent` to hook into the event system | ||
without needing to inherit. It also allows `MyComponent` to expose more hooks | ||
with little cost. See | ||
[yoshuawuyts/microcomponent](https://github.com/yoshuawuyts/microcomponent) for | ||
an example of how to create a higher level interface. | ||
### How does it work? | ||
[Morphdom][md] is a diffing engine that diffs real DOM trees. It runs a series | ||
of checks between nodes to see if they should either be replaced, removed, | ||
updated or reordered. This is done using a series of property checks on the | ||
nodes. | ||
Since [v2.1.0][210] `morphdom` also runs `Node.isSameNode(otherNode)`. This | ||
allows us to override the function and replace it with a custom function that | ||
proxies an existing node. Check out the code to see how it works. The result is | ||
that if every element in our tree uses `nanocomponent`, only elements that have | ||
changed will be recomputed and re-rendered making things very fast. | ||
`nanomorph`, which saw first use in choo 5, has supported `isSameNode` since | ||
its conception. | ||
## API | ||
### `Nanocomponent.prototype()` | ||
Inheritable Nanocomponent prototype. Should be inherited from using | ||
`Nanococomponent.call(this)` and `prototype = | ||
Object.create(Nanocomponent.prototype)`. | ||
### `component = Nanocomponent()` | ||
Create a new Nanocomponent instance. Additional methods can be set on the | ||
prototype. | ||
Internal properties are: | ||
### `component.render([…arguments])` | ||
Render the component. Returns a proxy node if already mounted on the DOM. Proxy | ||
nodes make it so DOM diffing algorithms leave the element alone when diffing. | ||
- `this._placeholder`: placeholder element that's returned on subsequent | ||
`render()` calls that don't pass the `._update()` check. | ||
- `this._element`: rendered element that should be returned from the first | ||
`._render()` call. Used to apply `._load()` and `._unload()` listeners on. | ||
- `this._hasWindow`: boolean if `window` exists. Can be used to create | ||
elements that render both in the browser and in Node. | ||
- `this._loaded`: boolean if the element is currently loaded on the DOM. | ||
- `this._onload`: reference to the [on-load][on-load] library. | ||
- `this.props`: the current `props` passed to `render()` | ||
- `this.oldProps`: the previous `props` | ||
- `this.state`: any out of band state to be stored | ||
### `component.element` | ||
A [getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) | ||
property that returns the component's DOM node if its mounted in the page and | ||
`null` when its not. | ||
### `DOMNode|placeholder = Nanocomponent.prototype.render(props)` | ||
Create an instance of the component. Calls `prototype._render()` if | ||
`prototype._update()` returns `true`. As long as the element is mounted on the | ||
DOM, subsequent calls to `.render()` will return a placeholder element with a | ||
`.isSameNode()` method that compares arguments with the previously rendered | ||
node. This is useful for diffing algorithms like | ||
[nanomorph](https://github.com/yoshuawuyts/nanomorph) which use this method to | ||
determine if a portion of the tree should be walked. | ||
### `DOMNode = Nanocomponent.prototype._render([arguments…])` | ||
__Must be implemented.__ Component specific render function. Optionally cache | ||
argument values here. Run anything here that needs to run along side node | ||
rendering. Must return a DOMNode. Use `_willRender` to run code after | ||
`_render` when the component is unmounted. | ||
### `Nanocomponent.prototype._render(props)` | ||
__Must be implemented.__ Render an HTML node with arguments. For | ||
`prototype._load()` and `prototype._unload()` to work, make sure you return the | ||
same node on subsequent renders. The Node that's initially returned is saved as | ||
`this._element`. | ||
### `Boolean = Nanocomponent.prototype._update([arguments…])` | ||
__Must be implemented.__ Return a boolean to determine if | ||
`prototype._render()` should be called. The `_update` method is analogous to | ||
React's `shouldComponentUpdate`. Called only when the component is mounted in | ||
the DOM tree. | ||
### `Nanocomponent.prototype._update(props)` | ||
__Must be implemented.__ Return a boolean to determine if `prototype._render()` | ||
should be called. Not called on the first render. | ||
### `Nanocomponent.prototype._willRender(el)` | ||
A function called right after `_render` returns with `el`, but before the fully rendered | ||
element is returned to the `render` caller. Run any first render hooks here. The `_load` and | ||
`_unload` hooks are added at this stage. | ||
### `Nanocomponent.prototype._load()` | ||
Called when the component is mounted on the DOM. | ||
### `Nanocomponent.prototype._load(el)` | ||
Called when the component is mounted on the DOM. Uses [on-load][onload] under | ||
the hood. | ||
### `Nanocomponent.prototype._unload()` | ||
Called when the component is removed from the DOM. | ||
### `Nanocomponent.prototype._unload(el)` | ||
Called when the component is removed from the DOM. Uses [on-load][onload] under | ||
the hood. | ||
### `Nanocomponent.prototype._didUpdate(el)` | ||
Called after a mounted component updates (e.g. `_update` returns true). You can use this hook to call | ||
`element.scrollIntoView` or other dom methods on the mounted component. | ||
## Installation | ||
@@ -133,5 +320,4 @@ ```sh | ||
## See Also | ||
- [choojs/choo](https://github.com/choojs/choo) | ||
- [shama/bel](https://github.com/shama/bel) | ||
- [yoshuawuyts/nanomorph](https://github.com/yoshuawuyts/nanomorph) | ||
- [yoshuawuyts/nanoraf](https://github.com/yoshuawuyts/nanoraf) | ||
- [shama/on-load](https://github.com/shama/on-load) | ||
@@ -142,4 +328,2 @@ - [yoshuawuyts/observe-resize](https://github.com/yoshuawuyts/observe-resize) | ||
- [yoshuawuyts/on-idle](https://github.com/yoshuawuyts/on-idle) | ||
- [yoshuawuyts/nanobounce](https://github.com/yoshuawuyts/nanobounce) | ||
- [yoshuawuyts/nanoframe](https://github.com/yoshuawuyts/nanoframe) | ||
@@ -158,6 +342,6 @@ ## Similar Packages | ||
[3]: https://npmjs.org/package/nanocomponent | ||
[4]: https://img.shields.io/travis/yoshuawuyts/nanocomponent/master.svg?style=flat-square | ||
[5]: https://travis-ci.org/yoshuawuyts/nanocomponent | ||
[6]: https://img.shields.io/codecov/c/github/yoshuawuyts/nanocomponent/master.svg?style=flat-square | ||
[7]: https://codecov.io/github/yoshuawuyts/nanocomponent | ||
[4]: https://img.shields.io/travis/choojs/nanocomponent/master.svg?style=flat-square | ||
[5]: https://travis-ci.org/choojs/nanocomponent | ||
[6]: https://img.shields.io/codecov/c/github/choojs/cache-component/master.svg?style=flat-square | ||
[7]: https://codecov.io/github/choojs/nanocomponent | ||
[8]: http://img.shields.io/npm/dm/nanocomponent.svg?style=flat-square | ||
@@ -167,4 +351,10 @@ [9]: https://npmjs.org/package/nanocomponent | ||
[11]: https://github.com/feross/standard | ||
[adapt]: https://github.com/yoshuawuyts/nanocomponent-adapters/ | ||
[on-load]: https://github.com/shama/on-load | ||
[bel]: https://github.com/shama/bel | ||
[yoyoify]: https://github.com/shama/yo-yoify | ||
[md]: https://github.com/patrick-steele-idem/morphdom | ||
[210]: https://github.com/patrick-steele-idem/morphdom/pull/81 | ||
[nm]: https://github.com/yoshuawuyts/nanomorph | ||
[ce]: https://github.com/yoshuawuyts/cache-element | ||
[class]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes | ||
[isSameNode]: https://github.com/choojs/nanomorph#caching-dom-elements | ||
[onload]: https://github.com/shama/on-load |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
29619
13
11
0
356
0
3
249
2
+ Addedglobal@^4.3.1
+ Addednanomorph@^5.1.2
+ Addednanomorph@5.4.3(transitive)