Maison >interface Web >js tutoriel >Implémentation de la série React de 0 à 1 : implémentation du cycle de vie et des diff
Cette série d'articles rationalise le contenu principal du framework React (JSX/DOM virtuel/composant/cycle de vie/algorithme diff/...) tout en implémentant un (x)react. 🎜>
Cet organigramme présente le cycle de vie de réagir de manière relativement claire. Elle est divisée en 3 étapes : période de génération, période d'existence et période de destruction. Étant donné que la fonction hook de cycle de vie existe dans le composant personnalisé, apportez quelques ajustements à la fonction _render précédente comme suit :
// 原来的 _render 函数,为了将职责拆分得更细,将 virtual dom 转为 real dom 的函数单独抽离出来 function vdomToDom(vdom) { if (_.isFunction(vdom.nodeName)) { // 为了更加方便地书写生命周期逻辑,将解析自定义组件逻辑和一般 html 标签的逻辑分离开 const component = createComponent(vdom) // 构造组件 setProps(component) // 更改组件 props renderComponent(component) // 渲染组件,将 dom 节点赋值到 component return component.base // 返回真实 dom } ... }Nous pouvons ajouter ,
méthode, la fonction setProps est la suivante : componentWillMount
componentWillReceiveProps
function setProps(component) { if (component && component.componentWillMount) { component.componentWillMount() } else if (component.base && component.componentWillReceiveProps) { component.componentWillReceiveProps(component.props) // 后面待实现 } },
, componentDidMount
, shouldComponentUpdate
méthode componentWillUpdate
componentDidUpdate
Test du cycle de vie
function renderComponent(component) { if (component.base && component.shouldComponentUpdate) { const bool = component.shouldComponentUpdate(component.props, component.state) if (!bool && bool !== undefined) { return false // shouldComponentUpdate() 返回 false,则生命周期终止 } } if (component.base && component.componentWillUpdate) { component.componentWillUpdate() } const rendered = component.render() const base = vdomToDom(rendered) if (component.base && component.componentDidUpdate) { component.componentDidUpdate() } else if (component && component.componentDidMount) { component.componentDidMount() } if (component.base && component.base.parentNode) { // setState 进入此逻辑 component.base.parentNode.replaceChild(base, component.base) } component.base = base // 标志符 }Testez les cas d'utilisation suivants :
Le résultat de sortie lorsque la page est chargée est le suivant :
class A extends Component { componentWillReceiveProps(props) { console.log('componentWillReceiveProps') } render() { return ( <p>{this.props.count}</p> ) } } class B extends Component { constructor(props) { super(props) this.state = { count: 1 } } componentWillMount() { console.log('componentWillMount') } componentDidMount() { console.log('componentDidMount') } shouldComponentUpdate(nextProps, nextState) { console.log('shouldComponentUpdate', nextProps, nextState) return true } componentWillUpdate() { console.log('componentWillUpdate') } componentDidUpdate() { console.log('componentDidUpdate') } click() { this.setState({ count: ++this.state.count }) } render() { console.log('render') return ( <p> <button onClick={this.click.bind(this)}>Click Me!</button> <A count={this.state.count} /> </p> ) } } ReactDOM.render( <B />, document.getElementById('root') )
Lorsque vous cliquez sur le bouton, le résultat de sortie est le suivant :
componentWillMount render componentDidMount
Implémentation de diff
shouldComponentUpdate componentWillUpdate render componentDidUpdateEn réaction, l'idée deimplémenter diff est de comparer l'ancien et le nouveau dom virtuel et afficher le patch comparé sur la page, obtenant ainsi un rafraîchissement partiel ; Cet article s'appuie sur l'implémentation diff en preact et simple-react. L'idée générale est de comparer l'ancien nœud dom avec le nouveau nœud dom virtuel et d'appeler. la logique correspondante selon différents types de comparaison (nœuds de texte, nœuds non-texte, composants personnalisés) pour obtenir un rendu partiel de la page. La structure globale du code est la suivante :
Ce qui suit implémente la logique correspondante selon différents types de comparaison.
/** * 比较旧的 dom 节点和新的 virtual dom 节点: * @param {*} oldDom 旧的 dom 节点 * @param {*} newVdom 新的 virtual dom 节点 */ function diff(oldDom, newVdom) { ... if (_.isString(newVdom)) { return diffTextDom(oldDom, newVdom) // 对比文本 dom 节点 } if (oldDom.nodeName.toLowerCase() !== newVdom.nodeName) { diffNotTextDom(oldDom, newVdom) // 对比非文本 dom 节点 } if (_.isFunction(newVdom.nodeName)) { return diffComponent(oldDom, newVdom) // 对比自定义组件 } diffAttribute(oldDom, newVdom) // 对比属性 if (newVdom.children.length > 0) { diffChild(oldDom, newVdom) // 遍历对比子节点 } return oldDom }Comparez les nœuds de texteComparez d'abord les nœuds de texte simples, le code est le suivant :
Comparez les nœuds non-textes
// 对比文本节点 function diffTextDom(oldDom, newVdom) { let dom = oldDom if (oldDom && oldDom.nodeType === 3) { // 如果老节点是文本节点 if (oldDom.textContent !== newVdom) { // 这里一个细节:textContent/innerHTML/innerText 的区别 oldDom.textContent = newVdom } } else { // 如果旧 dom 元素不为文本节点 dom = document.createTextNode(newVdom) if (oldDom && oldDom.parentNode) { oldDom.parentNode.replaceChild(dom, oldDom) } } return dom }Comparez les nœuds non-textes nœud de texte, l'idée est de remplacer les anciens nœuds du même niveau par de nouveaux nœuds, le code est le suivant :
Comparer les composants personnalisés
// 对比非文本节点 function diffNotTextDom(oldDom, newVdom) { const newDom = document.createElement(newVdom.nodeName); [...oldDom.childNodes].map(newDom.appendChild) // 将旧节点下的元素添加到新节点下 if (oldDom && oldDom.parentNode) { oldDom.parentNode.replaceChild(oldDom, newDom) } }L'idée de comparer les composants personnalisés est : si les nouveaux et les anciens composants sont différents, remplacez directement l'ancien composant par le nouveau composant ; si l'ancien et le nouveau composant sont identiques, attribuez les accessoires du nouveau composant à l'ancien composant, puis effectuez une comparaison différentielle de les anciens composants avant et après l'obtention des nouveaux accessoires. Le code est le suivant :
Parcourir et comparer les nœuds enfants
// 对比自定义组件 function diffComponent(oldDom, newVdom) { if (oldDom._component && (oldDom._component.constructor !== newVdom.nodeName)) { // 如果新老组件不同,则直接将新组件替换老组件 const newDom = vdomToDom(newVdom) oldDom._component.parentNode.insertBefore(newDom, oldDom._component) oldDom._component.parentNode.removeChild(oldDom._component) } else { setProps(oldDom._component, newVdom.attributes) // 如果新老组件相同,则将新组件的 props 赋到老组件上 renderComponent(oldDom._component) // 对获得新 props 前后的老组件做 diff 比较(renderComponent 中调用了 diff) } }Il existe deux stratégies pour parcourir et comparer les nœuds enfants : l'une consiste à comparer uniquement les nœuds du même niveau, et la une autre consiste à ajouter des attributs clés aux nœuds. Leur objectif est de réduire la complexité de l'espace. Le code est le suivant :
Test
// 对比子节点 function diffChild(oldDom, newVdom) { const keyed = {} const children = [] const oldChildNodes = oldDom.childNodes for (let i = 0; i < oldChildNodes.length; i++) { if (oldChildNodes[i].key) { // 将含有 key 的节点存进对象 keyed keyed[oldChildNodes[i].key] = oldChildNodes[i] } else { // 将不含有 key 的节点存进数组 children children.push(oldChildNodes[i]) } } const newChildNodes = newVdom.children let child for (let i = 0; i < newChildNodes.length; i++) { if (keyed[newChildNodes[i].key]) { // 对应上面存在 key 的情形 child = keyed[newChildNodes[i].key] keyed[newChildNodes[i].key] = undefined } else { // 对应上面不存在 key 的情形 for (let j = 0; j < children.length; j++) { if (isSameNodeType(children[i], newChildNodes[i])) { // 如果不存在 key,则优先找到节点类型相同的元素 child = children[i] children[i] = undefined break } } } diff(child, newChildNodes[i]) // 递归比较 } }Dans la section cycle de vie, la méthode componentWillReceiveProps n'est pas encore exécutée. Il suffit de modifier légèrement la fonction setProps :
Testons le dernier cas de test de la section cycle de vie :
/** * 更改属性,componentWillMount 和 componentWillReceiveProps 方法 */ function setProps(component, attributes) { if (attributes) { component.props = attributes // 这段逻辑对应上文自定义组件比较中新老组件相同时 setProps 的逻辑 } if (component && component.base && component.componentWillReceiveProps) { component.componentWillReceiveProps(component.props) } else if (component && component.componentWillMount) { component.componentWillMount() } }Test du cycle de vie
Adresse du projet, sur la façon de pr
Articles connexes :Analyse des instances du cycle de vie des composants React
Explication détaillée du cycle de vie des composants React
Tutoriel vidéo du framework Virtual dom-React
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!