Skip to content

Commit

Permalink
fre2: New reconcilation algorithm (#200)
Browse files Browse the repository at this point in the history
* first rendering

* first time is ok

* ototype

* remove types

* mplete

* kkkkkk

* mplete

* new reconcilation

* update readme

* Update README.md

* fix s

* some case

* Update README.md

* Bump node-notifier from 8.0.0 to 8.0.1 (#194)

Bumps [node-notifier](https://github.com/mikaelbr/node-notifier) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/mikaelbr/node-notifier/releases)
- [Changelog](https://github.com/mikaelbr/node-notifier/blob/v8.0.1/CHANGELOG.md)
- [Commits](mikaelbr/node-notifier@v8.0.0...v8.0.1)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix typo in README.md (#196)

* fix diff bug

* fix diff bug

* finish diff

* finish diff

* finish diff

* fix bug

* fix many bugs

* something went right

* passed all tests

* Update README.md (#197)

拼写错误

* docs: fix typo (#198)

* simplify

* fix bug

* reduce woords

* change words

* simplify

* simplify

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: inokawa <[email protected]>
Co-authored-by: Changhao Zhao (赵昌浩) <[email protected]>
Co-authored-by: suchangv <[email protected]>
Co-authored-by: 琚致远 <[email protected]>
  • Loading branch information
6 people authored Dec 30, 2020
1 parent 64d075a commit e727583
Show file tree
Hide file tree
Showing 15 changed files with 351 additions and 181 deletions.
37 changes: 8 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center"><img src="http://wx2.sinaimg.cn/mw690/0060lm7Tly1ftpm5b3ihfj3096097aaj.jpg" alt="fre logo" width="150"></p>
<h1 align="center">Fre</h1>
<p align="center">:ghost: Tiny React like framework with Concurrent.</p>
<p align="center">:ghost: Tiny Coroutine framework with Fiber.</p>
<p align="center">
<a href="https://github.com/yisar/fre/actions"><img src="https://img.shields.io/github/workflow/status/yisar/fre/main.svg" alt="Build Status"></a>
<a href="https://codecov.io/gh/yisar/fre"><img src="https://img.shields.io/codecov/c/github/yisar/fre.svg" alt="Code Coverage"></a>
Expand All @@ -9,17 +9,12 @@
<a href="https://bundlephobia.com/result?p=fre"><img src="http://img.badgesize.io/https://unpkg.com/fre/dist/fre.js?compression=brotli&label=brotli" alt="brotli"></a>
</p>

### Feature

- :tada: Functional Component and hooks API
- :confetti_ball: Time slicing and Algebraic effects
- :telescope: keyed reconcilation algorithm
- **Coroutine with Fiber** — This is an amazing idea, which implements the coroutine scheduler in JavaScript, and the rendering is asynchronous, which supports Time slicing and suspense components.

### Real world
- **Highly-optimized algorithm** — Fre has a better reconciliation algorithm, which traverses from both ends with O (n) complexity, and supports keyed.

[clicli.me](https://www.clicli.me)

Any other demos [click here](https://github.com/yisar/fre/tree/master/demo/src)
- **Do more with less** — After tree shaking, project of hello world is only 1KB, but it has most fetures, virtual DOM, hooks API, functional component and more.

### Use

Expand Down Expand Up @@ -59,8 +54,6 @@ render(<App />, document.getElementById('root'))

- [useRef](https://github.com/yisar/fre#useref)

- [useContext](https://github.com/yisar/fre#usecontext)

#### useState

`useState` is a base API, It will receive initial state and return a Array
Expand Down Expand Up @@ -102,7 +95,7 @@ function App() {
<div>
{state.count}
<button onClick={() => dispatch({ type: 'up' })}>+</button>
<button onClick={() => dispatch({ type: 'down' })}>+</button>
<button onClick={() => dispatch({ type: 'down' })}>-</button>
</div>
)
}
Expand Down Expand Up @@ -138,15 +131,15 @@ If it return a function, the function can do cleanups:
```js
useEffect(() => {
document.title = 'count is ' + count
reutn () => {
return () => {
store.unsubscribe()
}
}, [])
```

#### useLayout

More like useEffect, but useEffect queue in `requestAnimationFrame`, but useLayout is sync and block commitWork.
More like useEffect, but useLayout is sync and blocking UI.

```js
useLayout(() => {
Expand All @@ -156,7 +149,7 @@ useLayout(() => {

#### useMemo

`useMemo` has the same parameters as `useEffect`, but `useMemo` will return a cached value.
`useMemo` has the same rules as `useEffect`, but `useMemo` will return a cached value.

```js
function App() {
Expand Down Expand Up @@ -234,19 +227,5 @@ The above code needs babel plugin `@babel/plugin-transform-react-jsx`
]
```

#### time slicing

Time slicing is the scheduling of reconcilation, synchronous tasks, sacrifice CPU and reduce blocking time

#### resumable exception

resumable exception is a concept of algebraic effects. It can synchronously throw effects and then resume the execution of other logic of components.

#### key-based reconcilation

Fre implements a compact reconcilation algorithm support keyed, which also called diff.

It uses hash to mark locations to reduce much size.

#### License
_MIT @yisar
2 changes: 1 addition & 1 deletion demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<body>
<div id="root"></div>
<script src="./src/use-state.tsx"></script>
<script src="./src/ref.tsx"></script>
</body>

</html>
56 changes: 56 additions & 0 deletions demo/src/keys.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { h, render, useEffect, useState } from '../../src/index'

// function App() {
// const [key, setKey] = useState(['a', 'b', 'c'])
// return [
// <button onClick={() => setKey(['a', 'c', 'b','d'])}>x</button>,
// <ul>
// {key.map((i) => (
// <li key={i}>{i}</li>
// ))}
// </ul>,
// ]
// }

// function App() {
// const [key, setKey] = useState(['a', 'b', 'c'])
// return [
// <button onClick={() => setKey(['b', 'c', 'a'])}>x</button>,
// <ul>
// {key.map((i) => (
// <li key={i}>{i}</li>
// ))}
// </ul>,
// ]
// }

// function App() {
// const [key, setKey] = useState(['a', 'b', 'c'])
// return [
// <button onClick={() => setKey(['c', 'b','a'])}>x</button>,
// <ul>
// {key.map((i) => (
// <li key={i}>{i}</li>
// ))}
// </ul>,
// ]
// }

function App() {
const [key, setKey] = useState([1, 2])
return [
<button onClick={() => setKey([3, 2, 1])}>x</button>,
<ul>
{key.map((i) => (
<Li i={i} key={i} />
// <li key={i}>{i}</li>
))}
</ul>,
]
}

function Li(props) {
return <li>{props.i}</li>
}

render(<App />, document.getElementById('root'))
43 changes: 43 additions & 0 deletions demo/src/ref.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/** @jsx h */

// // preact:
// import { render, createElement as h } from "preact/compat";
// import { useState, useEffect } from "preact/hooks";

// react:
// import { createElement as h, useState, useEffect } from "react";
// import { render } from "react-dom";

// // fre:
import { render, h, useState, useEffect, useRef } from '../../src'


const Wrapper = () => {
const [showApp, setShowApp] = useState(true)

useEffect(()=>{
setTimeout(() => {
setShowApp(false)
}, 2000)
},[])

const p = dom => {
if (dom) {
} else {
console.log(111)
}
}
const c = dom => {
if (dom) {
} else {
console.log(222)
}
}
console.log(showApp)

return showApp ? <div ref={p}>
<p ref={c}>before</p>
</div> : <p>App removed...</p>
}

render(<Wrapper />, document.getElementById('root'))
13 changes: 6 additions & 7 deletions demo/src/use-effect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ import { h, render, useState, useEffect } from '../../src'
function Counter({ id, remove }) {
const [count, setCount] = useState(0)

useEffect(() => {
console.log(`111`)

return () => {
console.log(`222`)
}
})
// useEffect(() => {
// console.log(`111`)
// return () => {
// console.log(`222`)
// }
// })

return (
<div>
Expand Down
24 changes: 24 additions & 0 deletions demo/src/use-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { h, render, useState, useEffect, useLayoutEffect } from '../../src'

function App() {
const [count, setCount] = useState(0)
return (
<div>
{count < 5 && <A count={count < 1 ? count : 2} />}
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}

function A(props) {
useLayoutEffect(() => {
console.log(333)
return () => {
console.log(444)
}
})
return <div>{props.count}</div>
}

render(<App />, document.body)
2 changes: 1 addition & 1 deletion demo/src/use-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function App() {
// const [two, setTwo] = useState(0)
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
<button onClick={() => setCount(count + 1)}>{count}{count}</button>
</div>
)
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
],
"scripts": {
"test": "jest --coverage",
"ref": "jest ./test/ref.test.tsx",
"build": "rollup -c && gzip-size dist/fre.js",
"build:compat": "rollup --config compat/rollup.config.js",
"dev": "rollup -c --watch",
Expand Down
4 changes: 1 addition & 3 deletions src/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@ export const updateElement = <P extends Attributes>(
if (oldValue) dom.removeEventListener(name, oldValue)
dom.addEventListener(name, newValue)
} else if (name in dom && !(dom instanceof SVGElement)) {
// for property, such as className
;(dom as any)[name] = newValue || ''
} else if (newValue == null || newValue === false) {
dom.removeAttribute(name)
} else {
// for attributes
dom.setAttribute(name, newValue)
}
}
Expand All @@ -36,7 +34,7 @@ export const createElement = <P = Attributes>(fiber: IFiber) => {
const dom =
fiber.type === 'text'
? document.createTextNode('')
: fiber.op & (1 << 4)
: fiber.tag & (1 << 4)
? document.createElementNS(
'http://www.w3.org/2000/svg',
fiber.type as string
Expand Down
7 changes: 3 additions & 4 deletions src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ export const useReducer = <S, A>(reducer?: Reducer<S, A>, initState?: S): [S, Di
hook[0] = isFn(hook[1]) ? hook[1](hook[0]) : hook.length ? hook[1] : initState
return [
hook[0] as S,
(action: A | Dispatch<A>) => {
hook[1] = reducer ? reducer(hook[0], action as A) : action
hook[2] = reducer && (action as any).type[0] === '*' ? 0b1100 : 0b1000
(value: A | Dispatch<A>) => {
hook[1] = reducer || value
dispatchUpdate(current)
},
]
Expand Down Expand Up @@ -67,5 +66,5 @@ export const getHook = <S = Function | undefined, Dependency = any>(cursor: numb
}

export const isChanged = (a: DependencyList, b: DependencyList) => {
return !a || a.length !== b.length || b.some((arg, index) => arg !== a[index])
return !a || b.some((arg, index) => arg !== a[index])
}
Loading

0 comments on commit e727583

Please sign in to comment.