js
/**
* performUnitOfWork在有fiber.parent的时候,直接appendChild不行,如果被浏览器暂停任务就不会显示完整页面
* 所以要分render和commit阶段,在commit阶段递归
* 谁和谁做比较?怎么比较?
*/
const createElement = (type, props, ...children) => {
return {
type,
props: {
...props,
children: children.map((child) => {
return typeof child === 'object' ? child : createTextElement(child)
})
}
}
}
const createTextElement = (text) => {
return {
type: 'TEXT_ELEMENT',
props: {
nodeValue: text,
children: []
}
}
}
// 根据fiber创建真实dom
const createDom = (fiber) => {
// 创建对应节点
const dom =
fiber.type === 'TEXT_ELEMENT'
? document.createTextNode('')
: document.createElement(fiber.type)
updateDom(dom, {}, fiber.props)
return dom
}
const isEvent = (key) => key.startsWith('on')
// 过滤特殊的children
const isProperty = (key) => key !== 'children' && !isEvent(key)
const isNew = (prev, next) => (key) => prev[key] !== next[key]
const isGone = (prev, next) => (key) => !(key in next)
function updateDom(dom, prevProps, nextProps) {
// 删除旧的事件
Object.keys(prevProps)
.filter(isEvent)
.filter((key) => !(key in nextProps) || isNew(prevProps, nextProps)(key))
.forEach((name) => {
const eventType = name.toLowerCase().substring(2)
dom.removeEventListener(eventType, prevProps[name])
})
// 删除旧的props
Object.keys(prevProps)
.filter(isProperty)
.filter(isGone(prevProps, nextProps))
.forEach((name) => {
dom[name] = ''
})
// 添加新的props
Object.keys(nextProps)
.filter(isProperty)
.filter(isNew(prevProps, nextProps))
.forEach((name) => {
dom[name] = nextProps[name]
})
// 添加新的事件
Object.keys(nextProps)
.filter(isEvent)
.filter(isNew(prevProps, nextProps))
.forEach((name) => {
const eventType = name.toLowerCase().substring(2)
dom.addEventListener(eventType, nextProps[name])
})
}
function commitRoot() {
deletions.forEach(commitWork)
commitWork(wipRoot.child)
currentRoot = wipRoot
wipRoot = null
}
function commitWork(fiber) {
if (!fiber) {
return
}
const domParent = fiber.parent.dom
if (fiber.effectTag === 'PLACEMENT' && fiber.dom != null) {
domParent.appendChild(fiber.dom)
} else if (fiber.effectTag === 'UPDATE' && fiber.dom != null) {
updateDom(fiber.dom, fiber.alternate.props, fiber.props)
} else if (fiber.effectTag === 'DELETION') {
domParent.removeChild(fiber.dom)
}
commitWork(fiber.child)
commitWork(fiber.sibling)
}
const render = (element, container) => {
wipRoot = {
dom: container,
props: {
children: [element]
},
alternate: currentRoot
}
nextUnitOfWork = wipRoot
deletions = []
}
let nextUnitOfWork = null
let currentRoot = null
let wipRoot = null
let deletions = null
function workLoop(deadline) {
let shouldYield = false
while (nextUnitOfWork && !shouldYield) {
// 第一轮,只是构建wipRoot fiber树
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
// 如果剩余时间少于 1 毫秒,则 shouldYield 被设置为 true,表示当前任务应该让出执行权。
// 为什么是0?代表什么?
shouldYield = deadline.timeRemaining() < 1
}
// 为什么这里就不会被中断
if (!nextUnitOfWork && wipRoot) {
commitRoot()
}
requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)
// fiber对象
// {
// type
// props
// dom
// parent
// child
// sibling
// }
// 传入fiber,创建dom,为children创建fiber,找到下一个工作单元
function performUnitOfWork(fiber) {
// 1、创建DOM
if (!fiber.dom) {
fiber.dom = createDom(fiber)
}
// 2、给children创建fiber
const elements = fiber.props.children
reconcileChildren(fiber, elements)
// 3、找到下一个工作单元
// 向下递,向上归
if (fiber.child) {
return fiber.child
}
let nextFiber = fiber
while (nextFiber) {
if (nextFiber.sibling) {
return nextFiber.sibling
}
nextFiber = nextFiber.parent
}
}
// 接收老的fiber树,和新的element,也就是说是老fiber数和新的element做diff,然后生成新的fiber树
function reconcileChildren(wipFiber, elements) {
let index = 0
let oldFiber = wipFiber.alternate && wipFiber.alternate.child
let prevSibling = null
while (index < elements.length || oldFiber != null) {
const element = elements[index]
let newFiber = null
const sameType = oldFiber && element && element.type === oldFiber.type
// 如果类型相同,则更新node
if (sameType) {
newFiber = {
type: oldFiber.type,
props: element.props,
dom: oldFiber.dom,
parent: wipFiber,
alternate: oldFiber,
effectTag: 'UPDATE'
}
}
// 如果类型不同且有新节点,新增node
if (element && !sameType) {
newFiber = {
type: element.type,
props: element.props,
dom: null,
parent: wipFiber,
alternate: null,
effectTag: 'PLACEMENT'
}
}
// 如果有老的fiber而且类型不同,则删除node
if (oldFiber && !sameType) {
oldFiber.effectTag = 'DELETION'
deletions.push(oldFiber)
}
if (oldFiber) {
oldFiber = oldFiber.sibling
}
if (index === 0) {
wipFiber.child = newFiber
} else {
prevSibling.sibling = newFiber
}
prevSibling = newFiber
index++
}
}
const MyReact = {
createElement,
render
}
/** @jsx MyReact.createElement */
const container = document.getElementById('root')
const updateValue = (e) => {
rerender(e.target.value)
}
const rerender = (value) => {
const element = (
<div>
<input onInput={updateValue} value={value} />
<h2>Hello {value}</h2>
</div>
)
MyReact.render(element, container)
}
rerender('World')/**
* performUnitOfWork在有fiber.parent的时候,直接appendChild不行,如果被浏览器暂停任务就不会显示完整页面
* 所以要分render和commit阶段,在commit阶段递归
* 谁和谁做比较?怎么比较?
*/
const createElement = (type, props, ...children) => {
return {
type,
props: {
...props,
children: children.map((child) => {
return typeof child === 'object' ? child : createTextElement(child)
})
}
}
}
const createTextElement = (text) => {
return {
type: 'TEXT_ELEMENT',
props: {
nodeValue: text,
children: []
}
}
}
// 根据fiber创建真实dom
const createDom = (fiber) => {
// 创建对应节点
const dom =
fiber.type === 'TEXT_ELEMENT'
? document.createTextNode('')
: document.createElement(fiber.type)
updateDom(dom, {}, fiber.props)
return dom
}
const isEvent = (key) => key.startsWith('on')
// 过滤特殊的children
const isProperty = (key) => key !== 'children' && !isEvent(key)
const isNew = (prev, next) => (key) => prev[key] !== next[key]
const isGone = (prev, next) => (key) => !(key in next)
function updateDom(dom, prevProps, nextProps) {
// 删除旧的事件
Object.keys(prevProps)
.filter(isEvent)
.filter((key) => !(key in nextProps) || isNew(prevProps, nextProps)(key))
.forEach((name) => {
const eventType = name.toLowerCase().substring(2)
dom.removeEventListener(eventType, prevProps[name])
})
// 删除旧的props
Object.keys(prevProps)
.filter(isProperty)
.filter(isGone(prevProps, nextProps))
.forEach((name) => {
dom[name] = ''
})
// 添加新的props
Object.keys(nextProps)
.filter(isProperty)
.filter(isNew(prevProps, nextProps))
.forEach((name) => {
dom[name] = nextProps[name]
})
// 添加新的事件
Object.keys(nextProps)
.filter(isEvent)
.filter(isNew(prevProps, nextProps))
.forEach((name) => {
const eventType = name.toLowerCase().substring(2)
dom.addEventListener(eventType, nextProps[name])
})
}
function commitRoot() {
deletions.forEach(commitWork)
commitWork(wipRoot.child)
currentRoot = wipRoot
wipRoot = null
}
function commitWork(fiber) {
if (!fiber) {
return
}
const domParent = fiber.parent.dom
if (fiber.effectTag === 'PLACEMENT' && fiber.dom != null) {
domParent.appendChild(fiber.dom)
} else if (fiber.effectTag === 'UPDATE' && fiber.dom != null) {
updateDom(fiber.dom, fiber.alternate.props, fiber.props)
} else if (fiber.effectTag === 'DELETION') {
domParent.removeChild(fiber.dom)
}
commitWork(fiber.child)
commitWork(fiber.sibling)
}
const render = (element, container) => {
wipRoot = {
dom: container,
props: {
children: [element]
},
alternate: currentRoot
}
nextUnitOfWork = wipRoot
deletions = []
}
let nextUnitOfWork = null
let currentRoot = null
let wipRoot = null
let deletions = null
function workLoop(deadline) {
let shouldYield = false
while (nextUnitOfWork && !shouldYield) {
// 第一轮,只是构建wipRoot fiber树
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
// 如果剩余时间少于 1 毫秒,则 shouldYield 被设置为 true,表示当前任务应该让出执行权。
// 为什么是0?代表什么?
shouldYield = deadline.timeRemaining() < 1
}
// 为什么这里就不会被中断
if (!nextUnitOfWork && wipRoot) {
commitRoot()
}
requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)
// fiber对象
// {
// type
// props
// dom
// parent
// child
// sibling
// }
// 传入fiber,创建dom,为children创建fiber,找到下一个工作单元
function performUnitOfWork(fiber) {
// 1、创建DOM
if (!fiber.dom) {
fiber.dom = createDom(fiber)
}
// 2、给children创建fiber
const elements = fiber.props.children
reconcileChildren(fiber, elements)
// 3、找到下一个工作单元
// 向下递,向上归
if (fiber.child) {
return fiber.child
}
let nextFiber = fiber
while (nextFiber) {
if (nextFiber.sibling) {
return nextFiber.sibling
}
nextFiber = nextFiber.parent
}
}
// 接收老的fiber树,和新的element,也就是说是老fiber数和新的element做diff,然后生成新的fiber树
function reconcileChildren(wipFiber, elements) {
let index = 0
let oldFiber = wipFiber.alternate && wipFiber.alternate.child
let prevSibling = null
while (index < elements.length || oldFiber != null) {
const element = elements[index]
let newFiber = null
const sameType = oldFiber && element && element.type === oldFiber.type
// 如果类型相同,则更新node
if (sameType) {
newFiber = {
type: oldFiber.type,
props: element.props,
dom: oldFiber.dom,
parent: wipFiber,
alternate: oldFiber,
effectTag: 'UPDATE'
}
}
// 如果类型不同且有新节点,新增node
if (element && !sameType) {
newFiber = {
type: element.type,
props: element.props,
dom: null,
parent: wipFiber,
alternate: null,
effectTag: 'PLACEMENT'
}
}
// 如果有老的fiber而且类型不同,则删除node
if (oldFiber && !sameType) {
oldFiber.effectTag = 'DELETION'
deletions.push(oldFiber)
}
if (oldFiber) {
oldFiber = oldFiber.sibling
}
if (index === 0) {
wipFiber.child = newFiber
} else {
prevSibling.sibling = newFiber
}
prevSibling = newFiber
index++
}
}
const MyReact = {
createElement,
render
}
/** @jsx MyReact.createElement */
const container = document.getElementById('root')
const updateValue = (e) => {
rerender(e.target.value)
}
const rerender = (value) => {
const element = (
<div>
<input onInput={updateValue} value={value} />
<h2>Hello {value}</h2>
</div>
)
MyReact.render(element, container)
}
rerender('World')