Simple implementation | Statically Analyzable | Convenient | |
---|---|---|---|
<div ref="foo"> |
No | No | Yes |
<div ref={foo => this.foo = foo}> |
Yes | Yes | No† |
<div ref={linkRef(this, 'foo')> |
Yes | No | Yes |
† Is it really that inconvenient?
// UPDATE: In 2023, you should probably stop using this! The narrow version of Safari that | |
// does not support `nomodule` is probably not being used anywhere. The code below is left | |
// for posterity. | |
/** | |
* Safari 10.1 supports modules, but does not support the `nomodule` attribute - it will | |
* load <script nomodule> anyway. This snippet solve this problem, but only for script | |
* tags that load external code, e.g.: <script nomodule src="nomodule.js"></script> | |
* | |
* Again: this will **not** prevent inline script, e.g.: |
Simple implementation | Statically Analyzable | Convenient | |
---|---|---|---|
<div ref="foo"> |
No | No | Yes |
<div ref={foo => this.foo = foo}> |
Yes | Yes | No† |
<div ref={linkRef(this, 'foo')> |
Yes | No | Yes |
† Is it really that inconvenient?
import { Component } from 'preact'; | |
export default class PureComponent extends Component { | |
shouldComponentUpdate(props, state) { | |
return !(shallowEqual(props, this.props) && shallowEqual(state, this.state)); | |
} | |
} | |
function shallowEqual(a, b) { | |
for (let key in a) if (a[key]!==b[key]) return false; |
I took some time this weekend to analyze Mastodon's frontend performance. I didn't manage to write many fixes (just a config fix and better caching for static assets) so this was mostly just investigation.
The point of this document is to lay out some of my initial thoughts, since it may be helpful for others. There's a lot of technical jargon, so you may want to get some background by looking at my blog post on "The cost of small modules" and my talk on "Solving the web performance crisis" (slides).
import React from 'react' | |
import { render } from 'react-dom' | |
// with fiber, we'll be able to write components that update text deep | |
// inside another string without wrapper dom, or rerendering the whole component | |
// before | |
class Lorem extends React.Component { | |
state = { | |
str: '' |
When Babel 6 came out, it was hard for a lot of packages to upgrade because it was essentially an entirely different category of thing than Babel 5. So what happened was that some packages upgraded, and some didn't — at least not straight away.
Some projects took the prima facie enlightened view that packages should expose untranspiled code, so that the consumers of that code could determine for themselves what needed to get transpiled based on the environments they supported.
That was a costly decision. If I was the author of an app that was using Babel 6, I couldn't import a library that was still using Babel 5 and shipping untranspiled code (because the configs were completely incompatible), and vice versa. Frankly, it was a bloody nuisance. We are bad at anticipating these sorts of issues. It will happen again at some point.
Adding a few extra bytes to pkg.main
or pkg.module
is a small price to pay for things just working. As well as avoiding the aforementioned headaches, it means that your
class PureComponent extends Component { | |
shouldComponentUpdate(props, state) { | |
return shallowEqual(props, this.props) && shallowEqual(state, this.state); | |
} | |
} | |
function shallowEqual(a, b) { | |
for (let key in a) if (a[key]!==b[key]) return false; | |
for (let key in b) if (!(key in a)) return false; | |
return true; |
import { options, render } from 'preact'; | |
import { ATTR_KEY } from 'preact/src/constants'; | |
/** | |
* Returns the input properties that were passed to an element when it was | |
* rendered. | |
* | |
* See `shallowRender` documentation for usage example. | |
*/ |
// src/app/preactDomRenderer.js | |
import { h, render } from 'preact'; | |
import undom from 'undom'; | |
const VOID_ELEMENTS = [ | |
'area', | |
'base', | |
'br', | |
'col', |
@tracked
is a decorator for Preact that makes working with state values no different than properties on your component instance.
It's one 300 byte function that creates a getter/setter alias into state/setState() for a given key, with an optional initial value. The "magic" here is simply that it works as a property decorator rather than a function, so it appears to integrate directly into the language.
tracked
has no dependencies and works with any component implementation that uses this.state
and this.setState()
.