时间:2021-05-25
React源码看过几次,每次都没有坚持下来,索性学习一下PReact部分,网上讲解源码的不少,但是基本已经过时,所以自己来梳理下
Context的使用:
Provider的props中有value属性
Consumer中直接获取传值
import { createContext, h, render } from 'preact';const FontContext = createContext(20);function Child() { return <FontContext.Consumer> {fontSize=><div style={{fontSize:fontSize}}>child</div>} </FontContext.Consumer>}function App(){ return <Child/>}render( <FontContext.Provider value={26}> <App/> </FontContext.Provider>, document.getElementById('app'));看一下源码:
import { enqueueRender } from './component';export let i = 0;export function createContext(defaultValue, contextId) { contextId = '__cC' + i++; // 生成一个唯一ID const context = { _id: contextId, _defaultValue: defaultValue, /** @type {import('./internal').FunctionComponent} */ Consumer(props, contextValue) { // return props.children( // context[contextId] ? context[contextId].props.value : defaultValue // ); return props.children(contextValue); }, /** @type {import('./internal').FunctionComponent} */ Provider(props) { if (!this.getChildContext) { // 第一次调用时进行一些初始化操作 let subs = []; let ctx = {}; ctx[contextId] = this; // 在diff操作用,如果判断一个组件在Comsumer中,会调用sub进行订阅; // 同时这个节点后续所有diff的地方都会带上这个context,调用sub方法进行调用 // context具有层级优先级,组件会先加入最近的context中 this.getChildContext = () => ctx; this.shouldComponentUpdate = function(_props) { if (this.props.value !== _props.value) { // I think the forced value propagation here was only needed when `options.debounceRendering` was being bypassed: // https://github.com/preactjs/preact/commit/4d339fb803bea09e9f198abf38ca1bf8ea4b7771#diff-54682ce380935a717e41b8bfc54737f6R358 // In those cases though, even with the value corrected, we're double-rendering all nodes. // It might be better to just tell folks not to use force-sync mode. // Currently, using `useContext()` in a class component will overwrite its `this.context` value. // subs.some(c => { // c.context = _props.value; // enqueueRender(c); // }); // subs.some(c => { // c.context[contextId] = _props.value; // enqueueRender(c); // }); // enqueueRender最终会进入renderComponent函数,进行diff、commitRoot、updateParentDomPointers等操作 subs.some(enqueueRender); } }; this.sub = c => { subs.push(c);// 进入订阅数组, let old = c.componentWillUnmount; c.componentWillUnmount = () => { // 重写componentWillUnmount subs.splice(subs.indexOf(c), 1); if (old) old.call(c); }; }; } return props.children; } }; // Devtools needs access to the context object when it // encounters a Provider. This is necessary to support // setting `displayName` on the context object instead // of on the component itself. See: // https://reactjs.org/docs/context.html#contextdisplayname // createContext最终返回的是一个context对象,带着Provider和Consumer两个函数 // 同时Consumber函数的contextType和Provider函数的_contextRef属性都指向context return (context.Provider._contextRef = context.Consumer.contextType = context);}所以对于Provider组件,在渲染时会判断有没有getChildContext方法,如果有的话调用得到globalContext并一直向下传递下去
if (c.getChildContext != null) { globalContext = assign(assign({}, globalContext), c.getChildContext()); } if (!isNew && c.getSnapshotBeforeUpdate != null) { snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState); } let isTopLevelFragment = tmp != null && tmp.type === Fragment && tmp.key == null; let renderResult = isTopLevelFragment ? tmp.props.children : tmp; diffChildren( parentDom, Array.isArray(renderResult) ? renderResult : [renderResult], newVNode, oldVNode, globalContext, isSvg, excessDomChildren, commitQueue, oldDom, isHydrating );当渲染遇到Consumer时,即遇到contextType属性,先从Context中拿到provider,然后拿到provider的props的value值,作为组件要获取的上下文信息。同时这时候会调用provider的sub方法,进行订阅,当调用到Provider的shouldComponentUpdate中发现value发生变化时就会将所有的订阅者进入enqueueRender函数。
所以源码中,globalContext对象的每一个key指向一个Context.Provider;componentContext代表组件所在的Consumer传递的上下文信息即配对的Provider的props的value;
同时Provider的shouldComponentUpdate方法中用到了 ·this.props.value !== _props.value· 那么这里的this.props是哪来的?Provider中并没有相关属性。
主要是下面这个地方,当判断没有render方法时,会先用Compoent来实例化一个对象,并将render方法设置为doRender,并将constructor指向newType(当前函数),在doRender中调用this.constructor方法
// Instantiate the new component if ('prototype' in newType && newType.prototype.render) { // @ts-ignore The check above verifies that newType is suppose to be constructed newVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap } else { // @ts-ignore Trust me, Component implements the interface we want newVNode._component = c = new Component(newProps, componentContext); c.constructor = newType; c.render = doRender; }/** The `.render()` method for a PFC backing instance. */function doRender(props, state, context) { return this.constructor(props, context);}diff部分比较复杂,整体整理了一张大图
真是不得不吐槽,博客园的编辑器bug太多了,尤其是mac上使用,比如第二次上传代码提交不了;赋值粘贴用不了。。。
只有情怀让我继续在这里更新
到此这篇关于一文帮你理解PReact10.5.13源码的文章就介绍到这了,更多相关PReact10.5.13源码内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
linecache,可以用它方便地获取某一文件某一行的内容。而且它也被traceback模块用来获取相关源码信息来展示。用法很简单:>>>importlinec
之前在Retrofit源码初探一文中我们提出了三个问题:什么时候开始将注解中参数拼装成http请求的信息的?如何产生发起http请求对象的?如何将对象转换成我们
都没说你的产品是什么,大致的讲,你可以多到网上的五八等一些地方露脸,或者做做软一文,问一答,一定要坚持哦,莫凡推广也能帮你,呵呵农产品营销您好,时下比较流行的是
都没说你的产品是什么,大致的讲,你可以多到网上的五八等一些地方露脸,或者做做软一文,问一答,一定要坚持哦,莫凡推广也能帮你,呵呵网店推广免费有哪些你可以用用看á
从matplotlib工具栏源码探析一(禁用工具栏、默认工具栏和工具栏管理器三种模式的差异)一文可知matplotlib内置实现了多个工具项的实现,而默认工具栏