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

推荐订阅源

博客园 - 叶小钗
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
雷峰网
雷峰网
GbyAI
GbyAI
Hugging Face - Blog
Hugging Face - Blog
N
Netflix TechBlog - Medium
博客园 - 聂微东
Y
Y Combinator Blog
罗磊的独立博客
博客园_首页
小众软件
小众软件
有赞技术团队
有赞技术团队
爱范儿
爱范儿
F
Fortinet All Blogs
C
Check Point Blog
Google DeepMind News
Google DeepMind News
云风的 BLOG
云风的 BLOG
Apple Machine Learning Research
Apple Machine Learning Research
M
MIT News - Artificial intelligence
月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
aimingoo的专栏
aimingoo的专栏

Deno

Deno 2.8 | Deno Claw Patrol: an open-source security firewall for agents | Deno Fresh 2.3: Zero JS by default, View Transitions, and Temporal support | Deno Deno 2.7: Temporal API, Windows ARM, and npm overrides | Deno Build a dinosaur runner game with Deno, pt. 6 | Deno Build a dinosaur runner game with Deno, pt. 5 | Deno Deno Deploy is Generally Available | Deno Introducing Deno Sandbox | Deno Build a dinosaur runner game with Deno, pt. 4 | Deno Build a dinosaur runner game with Deno, pt. 3 | Deno Build a dinosaur runner game with Deno, pt. 2 | Deno React / Next.js Denial-of-Service Vulnerability: Deno Deploy users protected | Deno Deno 2.6: dx is the new npx | Deno Build a dinosaur runner game with Deno, pt. 1 | Deno React Server Functions / Next.js Vulnerability: Deno Deploy users protected | Deno My highlights from the new Deno Deploy | Deno Deno's Other Open Source Projects | Deno How Deno protects against npm exploits | Deno Help Us Raise $200k to Free JavaScript from Oracle | Deno Deno 2.5: Permissions in the config file | Deno Fresh 2.0 Graduates to Beta, Adds Vite Support | Deno Deno 2.4: deno bundle is back | Deno JavaScript™ Trademark Update | Deno What's coming to JavaScript | Deno A brief history of JavaScript | Deno Reports of Deno's Demise Have Been Greatly Exaggerated | Deno An Update on Fresh | Deno How Plaid migrated 100 services to a new database platform 5x faster with Deno | Deno Deno 2.3: Improved deno compile, local npm packages, and more | Deno Add JSR packages with pnpm and Yarn | Deno
Fresh 1.0 | Deno
Luca Casonat · 2022-06-28 · via Deno

Fresh is a new full stack web framework for Deno. By default, web pages built with Fresh send zero JavaScript to the client. The framework has no build step which allows for an order of magnitude improvement in deployment times. Today we are releasing the first stable version of Fresh.

Client side rendering has become increasingly popular in recent years. React (and React-like) pages allow programs to build very complex UIs with relative ease. The popularity shows in the current web framework space: it is dominated by React based frameworks.

But client-side rendering is expensive; the framework often will ship hundreds of kilobytes of client-side JavaScript to users on each request. These JS bundles often do little more than rendering static content that could just as well have been served as plain HTML.

Some newer frameworks also have support for server side rendering. This helps reduce page load times by pre-rendering on the server. But most current implementations still ship the entire rendering infrastructure for the full application to every client so the page can be fully re-rendered on the client.

This is a bad development - client-side JavaScript is really expensive: it slows down the user experience, drastically increases power consumption on mobile devices, and is often not very robust.

Fresh uses a different model: one where you ship 0 KB of JS to clients by default. One where the majority of rendering is done on a server, and the client is only responsible for re-rendering small islands of interactivity. A model where the developer explicitly opts in to client side rendering for specific components. This model was described back in 2020 by Jason Miller in his Islands Architecture blog post.

At its core, Fresh is a routing framework and templating engine that renders pages as they are requested, on a server. In addition to this just-in-time (JIT) rendering on the server, Fresh also provides an interface for seamlessly rendering some components on the client for maximum interactivity. The framework uses Preact and JSX (or TSX) for rendering and templating on both the server and the client. Client rendering is completely opt in on a per component level, and as such many applications will ship no JavaScript to the client at all.

Fresh does not have a build step. The code you write is directly the code that is run on the server and client-side, and any necessary transpilation of TypeScript or JSX to plain JavaScript is done just-in-time, when it is needed. This allows for very fast iteration loops with instant deployments.

Quick Start

To really explain what makes Fresh special, let’s scaffold a new project and look at some code:

deno run -A -r https://fresh.deno.dev my-app

This script generates the minimal boilerplate required for a Fresh project in the folder you specified as the last argument to the initialization script (my-app in this case). You can learn more about what all the files mean in the Getting Started guide.

my-app/
├── README.md
├── deno.json
├── dev.ts
├── fresh.gen.ts
├── import_map.json
├── islands
│   └── Counter.tsx
├── main.ts
├── routes
│   ├── [name].tsx
│   ├── api
│   │   └── joke.ts
│   └── index.tsx
└── static
    ├── favicon.ico
    └── logo.svg

For now, direct your attention to the routes/ folder. It contains the handlers and templates for each route of the application. The name of each file defines which paths the route matches. The api/joke.ts file serves requests to /api/joke for example. The folder structure might remind you of Next.js or PHP, as these systems also use file system routing.

Let’s take a look at the routes/index.tsx file:

import Counter from "../islands/Counter.tsx";

export default function Home() {
  return (
    <div>
      <img
        src="/logo.svg"
        height="100px"
        alt="the fresh logo: a sliced lemon dripping with juice"
      />
      <p>
        Welcome to `fresh`. Try update this message in the ./routes/index.tsx
        file, and refresh.
      </p>
      <Counter start={3} />
    </div>
  );
}

The default export of the route is the JSX template that is rendered server side for each request. The template component itself is never rendered on the client.

This poses the question: what if you do want to re-render some parts of the application on the client, for example in response to some user interaction? This is what Fresh’s Islands are for. They are individual components of an application that are re-hydrated on the client to allow interaction.

Below is an example of an island that provides a client side counter with increment and decrement buttons. It uses the Preact useState hook to keep track of the counter value.



import { useState } from "preact/hooks";

interface CounterProps {
  start: number;
}

export default function Counter(props: CounterProps) {
  const [count, setCount] = useState(props.start);
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

Islands have to be placed in the islands/ folder. Fresh takes care of automatically re-hydrating the island on the client if it encounters its use in a route’s template.

Fresh is not just a frontend framework, but rather a fully integrated system for writing websites. You can arbitrarily handle requests of any type, return custom responses, make database requests, and more. This route returns a plain text HTTP response instead of an HTML page for example:



const JOKES = [];

export const handler = (_req: Request): Response => {
  const randomIndex = Math.floor(Math.random() * JOKES.length);
  const body = JOKES[randomIndex];
  return new Response(body);
};

This can also be used to do asynchronous data fetching for a route. Here is a route that loads blog posts from a file on disk:



import { HandlerContext, PageProps } from "$fresh/server.ts";

export const handler = async (_req: Request, ctx: HandlerContext): Response => {
  const body = await Deno.readTextFile(`./posts/${ctx.params.id}.md`);
  return ctx.render({ body });
};

export default function BlogPostPage(props: PageProps) {
  const { body } = props.data;
  
}

Because Fresh is so reliant on dynamic server side rendering, it is imperative that this is fast. This makes Fresh very well suited for running on the edge in runtimes like Deno Deploy, Netlify Edge Functions, or Supabase Edge Functions. This results in rendering happening physically close to a user, which minimizes network latency.

Deploying a Fresh app to Deno Deploy takes only a couple of seconds: push the code to a GitHub repository, then link this repository to a project in the Deno Deploy dashboard. Your project is then served from 35 regions across the globe, with 100k requests a day included in the free tier.

Production Ready

Fresh 1.0 is a stable release and can be relied upon for production use. Much of Deno’s public web services use Fresh (for example the site you are reading this blog post on now!). This does not mean we are done with Fresh. We have many more ideas to improve user and developer experience.

Try Deno Deploy - you will be surprised at the speed and simplicity.


Finally, thank you to Sylvain Cau, @hashrock, and Christian Norrman for your help on the Fresh project. Additional thanks to the Preact team for building Preact, and Jason Miller for his Islands Architecture blog post.