Skip to main content
Module

x/fre/docs/zh.md

👻 Tiny Footprint Concurrent UI library for Fiber.
Go to Latest
File

Fre 是一个基于 Fiber 架构的前端框架,它的核心是异步渲染,主要包括时间切片和 Suspense

和 react 不同的是,fre 使用了更好的 reconcilation 算法,可以进行预处理(两端遍历)

经过 tree shaking 后,hello world 项目只有 2kb,但它拥有很多功能,比如 hooks API,函数组件,Fragment……

快速开始

yarn add fre
import { render, useState } from "fre"

function App() {
  const [count, setCount] = useState(0)
  return (
    <>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
    </>
  )
}

render(<App />, document.body)

Output

export default () => {
  const [count, setCount] = useState(0)
  return html`<button
    style="background: rgb(189 30 104);padding: 10px 50px;color: #fff;"
    onClick=${() => setCount(count + 1)}
  >
    ${count}
  </button>`
}

Hooks API

useState

useState 是主要的一个 hook,它有一个浅表的优化,即每次 setState 都会和上次的值进行对比

function App() {
  const [up, setUp] = useState(0)
  const [down, setDown] = useState(0)
  return (
    <>
      <h1>{up}</h1>
      <button onClick={() => setUp(up + 1)}>+</button>
      <h1>{down}</h1>
      <button onClick={() => setDown(down - 1)}>-</button>
    </>
  )
}

useReducer

useReducer 和 useState 类似,但它使用了 redux 类似的 api,有时候这有利于组织逻辑

function reducer(state, action) {
  switch (action.type) {
    case "up":
      return { count: state.count + 1 }
    case "down":
      return { count: state.count - 1 }
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, { count: 1 })
  return (
    <>
      {state.count}
      <button onClick={() => dispatch({ type: "up" })}>+</button>
      <button onClick={() => dispatch({ type: "down" })}>-</button>
    </>
  )
}

useEffect

useEffect 是处理 effect 的一个 hook,它发生在宏任务中,所以可以进行 dom 操作,它的时机和规则如下:

useEffect(f)       //  effect (and clean-up) every time
useEffect(f, [])   //  effect (and clean-up) only once in a component's life
useEffect(f, [x])  //  effect (and clean-up) when property x changes in a component's life
function App({ flag }) {
  const [count, setCount] = useState(0)
  useEffect(() => {
    document.title = "count is " + count
  }, [flag])
  return (
    <>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
    </>
  )
}

返回值是一个函数,用来清理副作用

useEffect(() => {
  document.title = "count is " + count
  return () => {
    store.unsubscribe()
  }
}, [])

useLayout

useLayout 和 useEffect 类似,但它是同步发生的,dom 还没有发生变化,会阻塞渲染

useLayout(() => {
  console.log(dom) // null
}, [flag])

useMemo

useMemo 可以缓存一个值,甚至可以用它缓存组件:

const memo = (c) => (props) => useMemo(() => c, [Object.values(props)])

useCallback

useCallback 基于 useMemo, 只不过是缓存了一个函数

const cb = useCallback(() => {
  console.log("cb was cached")
}, [])

useRef

useRef 一般来说是一个函数或对象,对象的 current 是 dom 元素

function App() {
  useEffect(() => {
    console.log(t) // { current:<div>t</div> }
  })
  const t = useRef(null)
  return <div ref={t}>t</div>
}

它也可以直接是一个函数,此时可以进行清理工作

function App() {
  const t = useRef((dom) => {
    if (dom) {
      doSomething()
    } else {
      cleanUp()
    }
  })
  return flag && <span ref={t}>I will removed</span>
}

Suspense

Suspense 是异步渲染的另一个用例,它的核心是当 Promise 被 throw 出去的时候,组件会进行回退,并悬停,等到 Promise resolve 后再继续渲染

const LazyComponent = lazy(Component)

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  )
}

New JSX transform

plugins: [
  [
    "@babel/plugin-transform-react-jsx",
    {
      runtime: "automatic",
      importSource: "fre",
    },
  ],
]

Compare with other frameworks

每个框架的权衡和发展路线不同,所以强行进行比对是不科学的,但是……我们可以辩证地看待它们

  • react

react 是 fre 的灵感来源,所以很大程度上,fre 将会追随 react,它们也拥有一致的发展路线——探索 Concurrent Mode

但有所不同的是,fre 会更加注重核心算法和数据结构

  • vue / preact

从某种角度上,vue 和 preact 是类似的,它们都是同步渲染的框架,对用户来说可能代表不了什么

但是同步渲染的实现方式是完全不同的,最终结果是 vue 或 preact 无法支持时间切片

framework concurrent reconcilation algorithm bundle size
fre2 ★★★★ 2kb
react17 ★★ 39kb
vue3 × ★★★★★ 30kb
preactX × ★★★★ 4kb

License

MIT @yisar

Fre is inspired by many libraries, pay our respects to them: react snabbdom preact anu