惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
P
Privacy & Cybersecurity Law Blog
H
Help Net Security
云风的 BLOG
云风的 BLOG
G
GRAHAM CLULEY
Spread Privacy
Spread Privacy
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
P
Privacy International News Feed
Blog — PlanetScale
Blog — PlanetScale
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
The Register - Security
The Register - Security
Recorded Future
Recorded Future
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
A
About on SuperTechFans
W
WeLiveSecurity
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
The GitHub Blog
The GitHub Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Check Point Blog
Y
Y Combinator Blog
月光博客
月光博客
Scott Helme
Scott Helme
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
U
Unit 42
G
Google Developers Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threatpost
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
博客园 - 司徒正美

oida.dev | TypeScript, Rust

TypeScript's `erasableSyntaxOnly` Flag Unsafe for work Tokio: Macros Tokio: Channels Tokio: Getting Started Network Applications on the Tokio Stack Remake, Remodel, Reduce. The `never` type and error handling in TypeScript 5 Inconvenient Truths about TypeScript Refactoring in Rust: Introducing Traits Refactoring in Rust: Abstraction with the Newtype Pattern Announcing the TypeScript Cookbook TypeScript: Iterating over objects The road to universal JavaScript 10 years of oida.dev Rust: Tiny little traits The TypeScript converging point How not to learn TypeScript Getting started with Rust Introducing Slides and Coverage TypeScript: The humble function overload TypeScript + React: Children types are broken TypeScript: In defense of any Rust: Enums to wrap multiple errors Dissecting Deno Error handling in Rust TypeScript: Unexpected intersections Upgrading Node.js dependencies after a yarn audit TypeScript: Array.includes on narrow types TypeScript + React: Typing Generic forwardRefs shared, util, core: Schroedinger's module names Learning Rust and Go TypeScript: Narrow types in catch clauses TypeScript: Low maintenance types Tidy TypeScript: Name your generics Tidy TypeScript: Avoid traditional OOP patterns Tidy TypeScript: Prefer type aliases over interfaces Tidy TypeScript: Prefer union types over enums My new book: TypeScript in 50 Lessons this in JavaScript and TypeScript TypeScript and ECMAScript Modules TypeScript + React: Why I don't use React.FC TypeScript + React: Component patterns TypeScript: Augmenting global and lib.dom.d.ts Vite with Preact and TypeScript TypeScript: Union to intersection type 11ty: Generate Twitter cards automatically Are large node module dependencies an issue? TypeScript: Variadic Tuple Types Preview TypeScript: Improving Object.keys Remake, Remodel. Part 4. TypeScript + React: Typing custom hooks with tuple types TypeScript: Assertion signatures and Object.defineProperty TypeScript: Check for object properties and narrow down type Boolean in JavaScript and TypeScript void in JavaScript and TypeScript Symbols in JavaScript and TypeScript Why I use TypeScript TypeScript + React: Extending JSX Elements TypeScript: Validate mapped types and const context TypeScript: Match the exact object shape TypeScript: The constructor interface pattern Streaming your Meetup - Part 4: Directing and Streaming with OBS Streaming your Meetup - Part 3: Speaker audio Streaming your Meetup - Part 2: Speaker video Streaming your Meetup - Part 1: Basics and Projector TypeScript and React Guide: Added a new styles chapter TypeScript and React Guide: Added a new render props chapter TypeScript and React: Styles and CSS TypeScript and React TypeScript and React Guide: Added a new prop types chapter TypeScript without TypeScript -- JSDoc superpowers TypeScript: Mapped types for type maps JAMStack vs serverless web apps The Unsung Benefits of JAMStack Sites TypeScript: Ambient modules for Webpack loaders My most favourite talks in 2018 TypeScript and React Guide: Added a new context chapter TypeScript: Built-in generic types TypeScript: Type predicates JSX is syntactic sugar TypeScript and React Guide: Added a new hooks chapter Getting your CfP application right FAQ on our Angular Connect Talk: Automating UI development TypeScript and Substitutability Debugging Node.js apps in TypeScript with Visual Studio Code From Medium: Deconfusing Pre- and Post-processing From Medium: PostCSS misconceptions Saving and scraping a website with Puppeteer Cutting the mustard - 2018 edition Wordpress as CMS for your JAMStack sites My most favourite podcast episodes in 2017 My most favourite talks in 2017 My most favourite books in 2017 The Best Request Is No Request, Revisited Not so hidden figures - Organizing ScriptConf My podcast journey to ScriptCast Grid layout, grid layout everywhere! #scriptconf and #devone Object streams in Node.js
Go Preact! ❤️
Authoring library for web components # · 2020-08-24 · via oida.dev | TypeScript, Rust

You might have heard of Preact, the tiny 3KB alternative to React. It has been around for a while, and since its inception, it claims to be API and feature compatible compared to the more widely used Facebook library.

After using Preact for quite a while I come to the conclusion that Preact is much more. It offers a totally different development and design philosophy and has the potential to solve a ton of problems the current JavaScript ecosystem has to face. In this article, I try to compile a list of things that I find outstanding.

Progressive ecosystem #

Vue popularized the idea of a progressive JavaScript framework. The idea is that you can start small with just a few bits and pieces, but you can get very far with it. The more you progress with the framework, the more you are able to opt-in to more features. Evan You’s presentation on that topic shows an image that illustrates the idea very well (see slide 11):

  1. Declarative rendering through a templating system
  2. A component system to allow for composability and reusability
  3. Client-side routing
  4. State management (vuex)
  5. Build tools (vue-cli and single file components)
  6. Server-side rendering and hydration

With each step, you are able to create richer applications, but easing in is simple as you always build upon the previous steps, you never discard them.

Preact and its eco-system work also progressively. I tried to create a similar chart for Preact:

A progressive diagram for Preact, see below

Each step in detail:

  1. A light-weight component layer. Like in React, everything in Preact is a component. Components are the root of whatever you want to create. The component layer has around 3KB and allows you to compose and reuse pieces of markup, as well as introducing state through class components.
import { h, Component } from 'preact';

class Counter extends Component {
state = {
count: 0,
};

render() {
const { count } = this.state;
return h("div", null, [
count,
h(
"button",
{
onClick: () => this.setState({ count: count + 1 }),
},
"Increment"
),
]);
}
}

  1. JSX is optional as it requires a build step, but it makes your code a lot prettier.
import { h, Component } from 'preact';

class Counter extends Component {
state = {
count: 0,
};

render() {
const { count } = this.state;
return <div>
{count}
<button
onClick={() => this.setState({ count: count + 1 })}>
Increment
</button>
</div>
}
}

  1. Hooks are also optional. If you want to go full-on function components, you have to opt-in to hooks:
import { h } from 'preact'
import { useState } from 'preact/hooks'

function Counter() {
const [count, setCount] = useState(0)
return <div>
{count}
<button
onClick={() => setCount(prev => prev + 1)}>
Increment
</button>
</div>
}

Note that from step 1 on, you can choose to use htm, a small library that allows you to write JSX style Preact (and React) but in tagged template literals. The example from above, without a build step, without JSX, would look like this:

import {
html,
render,
useState
} from "https://unpkg.com/htm/preact/standalone.mjs?module";

function Counter() {
const [count, setCount] = useState(0);
return html`<div>
${count}
<button
onClick=
${() => setCount((prev) => prev + 1)}>
Increment
</button>
</div>
`
;
}

render(
html`<${Counter} />`,
document.querySelector("#app"))

  1. Preact ships its own client-side routing implementation with preact-router. This is also again an opt-in solution, and Preact is aware of alternatives, like wouter, which work as well.

  2. To tick off the tooling part, the Preact team ships its own CLI, which comes with a lot of templates for different use-cases, and wires up things like CSS processing, bundling, transpilation, code splitting, etc.

  3. If you need more, you can consume from the broader Preact ecosystem, where some of the best libraries are provided by core team members.

    • You have already seen htm JSX style components in template literals
    • preact-custom-element is a very tiny wrapper to make Preact work with web components. More on that later.
    • unistore, a small state container for Redux like state management
    • And there is more, the Awesome Preact repository should give you enough insights.

So it’s easy to start with small widgets on your website, especially when you have htm wired up. No builds, just a single library. And you can opt-in to use more and more features until you write full-fledged applications.

Tree-shaking optimized #

Everything about Preact is opt-in. In fact, Preact decides against having default exports where you can suck in the whole framework at once. It requires you to be intentional about everything you load (unless you use the compatibility layer, see below). That way, you only end up with what you actually need.

This is incredibly useful if your bundler works with tree shaking. Do you only need the useState and useEffect hooks? Then you won’t get any others in your production bundle.

I spun up a quick Vite project and compared a couple of simple components, pulling in progressively more features. A simple Greeting component:

function Greeting({name = 'Stefan'}) {
return <p>{name}</p>
}

As well as the counter example from above. Here are the results.

  1. Greeting function component: Minified 8.60kb, brotli: 3.20kb
  2. Greeting class component: 8.64kb, brotli: 3.22kb (most likely due my part being more verbose in a class)
  3. Counter class components, no hooks: Minified 8.73kb, brotli: 3.27kb
  4. Counter function component using useState: Minified 10.02kb, brotli: 3.73kb
  5. Counter function component using useState, logging useEffect: Minified 10.17kb, brotli: 3.79kb

It’s still incredibly small, but when you look at the minified site you see the size slowly increasing. Also, Preact stays small in the long run. Marvin from the Preact core team pointed me to a tweet by @halfnelson_au, where they compared the initial size of a Svelte app to a Preact app, bundle size evolves by increased source size. See the graph (nicked from the tweet) yourself:

Preact stays small. 80kb source become 20KB bundle in Preact, where as 80KB source become 40KB bundle in Svelte

Being optimized for tree-shaking is just another way to express the progressive nature of the framework. The good thing is that you don’t end up with any surprises once you ship. This makes tracking down things a lot easier in the long run.

First level TypeScript support #

If you have been following my blog you might think that TypeScript is a very relevant topic to me. Actually, my TypeScript + React is my most popular resource on this blog.

The @types/react package is excellently written and maintained, but it is a third-party add-on. There might be some differences, and also occasions where the framework and its types are out of sync. Not to mention that types need to be versioned differently as their bugfixes progress differently.

Preact ships types with each release. It is also written in TypeScript, but makes use of adding types via JSDoc comments and maintaining additional types in .d.ts files. Preact is actually an excellent case-study for this approach. If you’re interested, I really invite you to browse through the source. Start here, with the createElement function. You much likely see TypeScript usage as you’ve never seen it before.

Full React compatibility #

To stay as small as possible, Preact gets rid of lots of internal React functionality that you would rarely use in your day to day work. Things include support for React.children, PureComponent, forwardRef, etc. Legacy functionality that’s deprecated, but still supported. Framework internals. That kind of thing. This also means that things like hooks are not part of the main package, as everything in Preact is opt-in.

But Preact is designed to be a drop-in replacement for React, so this functionality is required by legacy applications, by frameworks like Next.js, or maybe by the component library that you include in your application. Preact patches this functionality via their preact/compat library.

This library includes all hooks, as well as patches for everything that was intentionally dropped to reach the 3KB goal. There are some guides on the Preact website on how to alias React to work with Preact, but my most favorite way is NPM aliasing.

NPM aliases install NPM or GitHub packages under a different name. That way you can point react to preact-compat, and the module resolution of Node figures out things on its own. I switched to Preact for all my Next.js projects, and all it took me was adding those lines:

{
//...
"dependencies": {
"preact": "^10.4.6",
"preact-render-to-string": "^5.1.10",
"react": "github:preact-compat/react#1.0.0",
"react-dom": "github:preact-compat/react-dom#1.0.0",
"react-ssr-prepass": "npm:preact-ssr-prepass@^1.0.1",
//...
}
}

And, of course, removing the old installations of react and react-dom. Switching to Preact shaved off 110 KB of minified production JavaScript, which resulted in 34 KB of minified + gzipped JavaScript.

Aliasing for other frameworks can work differently. Check out how to alias for popular bundlers and dev environments here.

Web components are always a good way to start a hot discussion on Twitter. And there seems to be this existential divide between people who prefer their shiny tech framework, and others who love “using the platform”. I love that Preact doesn’t give a damn about this discussion. It just loves and supports both sides.

There’s a little library called preact-custom-element that allows you to register you Preact components as web components.

import register from 'preact-custom-element';
import { h } from 'preact';
import { useState } from 'preact/hooks';

function Counter() {
const [count, setCount] = useState(0)
return <div>
{count}
<button
onClick={() => setState(prev => prev + 1)}>
Increment
</button>
</div>
}

register(Counter, 'my-counter')

Preact also allows you to think of web components not as an authoring layer for your apps, rather than a compile target or distribution possibility. You still write your apps and components with JSX in the virtual DOM, just as you are used to from React. But it’s easy to compile to a web component for distribution. Since Preact is so small, it also makes sense to use it as a web component run-time. Pick the level where you want to draw the line to enter web components land: Down at the presentational components, up at a widget level, or an entire application? All of it? It almost comes for free.

Independent #

I love React for its technical ingenuity and have the utmost respect for its core members and the innovation they bring along. Plus, members like Dan Abramov also put the human at the center and help foster a welcoming community.

Still, React is a framework by Facebook and for Facebook. It solves Facebook’s needs first, and it’s just coincidence that they have a lot in common with the needs of everybody else. Personally, I got weary of buying into a framework that is developed by a big tech corporation.

And you know… there’s an ethical dilemma.

Preact is independent. It is backed by Google projects, sure, but it’s not a Google framework. This also means that the team can work on things that would not end up on React’s roadmap if it’s not part of React’s overall goals. Preact ships an official router. Preact has an official web components layer. The Preact team ships a way to write without a build step.

I’m aware that those things exist for React as well. But it’s an explicit non-goal of the React team to provide everything.

Independence gives Preact the ability to innovate in certain areas, like performance and compatibility with the broader web ecosystem.

Gotchas #

There are a few gotchas that need to be pointed out.

  1. React compatibility works great until it doesn’t. Most of the time, React compatibility fails if people didn’t link to React as peer dependency but rather as a real dependency. This way you almost have no chance of aliasing to Preact through one of the means.
  2. The ecosystem is big, but maybe hard to navigate around. Especially if you want to progress continuously, it’s not always clear what your next steps are. But hey, maybe this can be solved through a “Learn Preact” guide here on the blog? What do you think?

Other than that, I haven’t found any caveats. It’s fun to use. As fun to use as React. Maybe even a little more…

What about Context? #

It has Context. Apparently the lack of Context is a myth that won’t fade!

Further information #

If you can spare an hour, I recommend watching those two talks.

  • Sara Vieira’s Preact talk at React Day Berlin. Sara gives an overview of Preact, highlighting some points that I make here, but others as well. The talk is already older, but the main philosophy around the framework and most points she makes still hold up.
  • Jason Miller’s talk at JSConf EU 2017. Jason gives insights on the inner workings of Preact and how concepts like DOM diffing and JSX are implemented in such a performant manner.
  • Marvin Hagemeister’s talk at Script’19. Marvin does an excellent talk on code golfing. He’s a member of the Preact core team and shows how they managed to include an entire virtual DOM implementation while staying small. Highly recommended talk!

And if using Facebook’s libraries keeps you up at night, take a look at this project by Andy Bell.

Related Articles