From d3260a4d9492a2ae753d7e8daee001afee733adc Mon Sep 17 00:00:00 2001 From: * <8> Date: Mon, 21 Mar 2022 14:54:32 +0800 Subject: [PATCH 01/13] Match-id-1da48726eef4a520b10fbe5a29d1b869a4b29678 --- .eslintrc.js | 11 +- babel.config.js | 7 +- .../HookTest/UseCallback.test.js | 10 +- .../ComponentTest/HookTest/UseContext.test.js | 14 +-- .../ComponentTest/HookTest/UseEffect.test.js | 110 +++++++++--------- .../HookTest/UseImperativeHandle.test.js | 26 ++--- .../HookTest/UseLayoutEffect.test.js | 18 ++- .../ComponentTest/HookTest/UseMemo.test.js | 24 ++-- .../ComponentTest/HookTest/UseReducer.test.js | 10 +- .../ComponentTest/HookTest/UseRef.test.js | 10 +- .../ComponentTest/HookTest/UseState.test.js | 26 ++--- scripts/__tests__/jest/Text.js | 2 +- scripts/webpack/webpack.pro.js | 4 +- 13 files changed, 133 insertions(+), 139 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index cbc6dfd8..79557bf5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -29,7 +29,6 @@ module.exports = { node: true, es6: true, }, - rules: { 'accessor-pairs': 'off', 'brace-style': ['error', '1tbs'], @@ -42,4 +41,14 @@ module.exports = { 'no-for-of-loops/no-for-of-loops': 'error', 'no-function-declare-after-return/no-function-declare-after-return': 'error', }, + overrides: [ + { + files: [ + 'scripts/__tests__/**/*.js' + ], + globals: { + container: true + }, + }, + ], }; diff --git a/babel.config.js b/babel.config.js index b7da15b0..37c7ac9e 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,6 @@ module.exports = { presets: [ - '@babel/react', + '@babel/react', '@babel/preset-typescript', ['@babel/preset-env', { targets: { node: 'current' } }] ], @@ -30,7 +30,10 @@ module.exports = { ['@babel/plugin-proposal-private-methods', { 'loose': true }], ['@babel/plugin-proposal-private-property-in-object', { 'loose': true }], '@babel/plugin-syntax-jsx', - '@babel/plugin-transform-react-jsx', + ['@babel/plugin-transform-react-jsx', { + pragma: 'Horizon.createElement', + pragmaFrag: 'Horizon.Fragment' + }], '@babel/plugin-transform-flow-strip-types', ], }; diff --git a/scripts/__tests__/ComponentTest/HookTest/UseCallback.test.js b/scripts/__tests__/ComponentTest/HookTest/UseCallback.test.js index 071c4512..e2d51e9d 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseCallback.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseCallback.test.js @@ -1,9 +1,7 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; describe('useCallback Hook Test', () => { - const { useState, useCallback } = React; + const { useState, useCallback } = Horizon; it('测试useCallback', () => { const App = (props) => { @@ -18,7 +16,7 @@ describe('useCallback Hook Test', () => { ) } - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); // 点击按钮触发num加1 container.querySelector('button').click(); @@ -27,7 +25,7 @@ describe('useCallback Hook Test', () => { container.querySelector('button').click(); expect(container.querySelector('p').innerHTML).toBe('1'); - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe('1'); // 依赖项有变化,点击按钮num增加 container.querySelector('button').click(); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseContext.test.js b/scripts/__tests__/ComponentTest/HookTest/UseContext.test.js index 89482aff..d94443b7 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseContext.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseContext.test.js @@ -1,11 +1,9 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; import { act } from '../../jest/customMatcher'; describe('useContext Hook Test', () => { - const { useState, useContext } = React; - const { unmountComponentAtNode } = HorizonDOM; + const { useState, useContext } = Horizon; + const { unmountComponentAtNode } = Horizon; it('简单使用useContext', () => { const LanguageTypes = { @@ -13,7 +11,7 @@ describe('useContext Hook Test', () => { JAVASCRIPT: 'JavaScript', }; const defaultValue = { type: LanguageTypes.JAVASCRIPT }; - const SystemLanguageContext = React.createContext(defaultValue); + const SystemLanguageContext = Horizon.createContext(defaultValue); const SystemLanguageProvider = ({ type, children }) => { return ( @@ -38,11 +36,11 @@ describe('useContext Hook Test', () => { ) } - HorizonDOM.render(, container); + Horizon.render(, container); // 测试当Provider未提供时,获取到的默认值'JavaScript'。 expect(container.querySelector('p').innerHTML).toBe('JavaScript'); unmountComponentAtNode(container); - HorizonDOM.render(, container); + Horizon.render(, container); // 测试当Provider提供时,可以获取到Provider的值'Java'。 expect(container.querySelector('p').innerHTML).toBe('Java'); // 测试当Provider改变时,可以获取到最新Provider的值。 diff --git a/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js b/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js index e4a52d65..b1bc34fe 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js @@ -1,18 +1,16 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; import * as LogUtils from '../../jest/logUtils'; import { act } from '../../jest/customMatcher'; import Text from '../../jest/Text'; describe('useEffect Hook Test', () => { - const { - useEffect, - useLayoutEffect, - useState, - memo, - forwardRef - } = React; + const { + useEffect, + useLayoutEffect, + useState, + memo, + forwardRef + } = Horizon; it('简单使用useEffect', () => { const App = () => { @@ -27,7 +25,7 @@ describe('useEffect Hook Test', () => { ) } - HorizonDOM.render(, container); + Horizon.render(, container); expect(document.getElementById('p').style.display).toBe('block'); // 点击按钮触发num加1 container.querySelector('button').click(); @@ -42,7 +40,7 @@ describe('useEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => { + Horizon.render(, container, () => { LogUtils.log('num effect'); }); // 第一次渲染为同步,所以同步执行的可以写在act里做判断 @@ -50,7 +48,7 @@ describe('useEffect Hook Test', () => { expect(container.textContent).toBe('op'); }); act(() => { - HorizonDOM.render(null, container, () => { + Horizon.render(null, container, () => { LogUtils.log('num effect89'); }); // 第二次渲染为异步,所以同步执行的不可以写在act里做判断,act里拿到的为空数组 @@ -72,7 +70,7 @@ describe('useEffect Hook Test', () => { } const na = ; // 必须设置key值,否则在diff的时候na会被视为不同组件 - HorizonDOM.render([, na], container); + Horizon.render([, na], container); expect(LogUtils.getAndClear()).toEqual([ 'App', 'NewApp' @@ -80,7 +78,7 @@ describe('useEffect Hook Test', () => { expect(container.textContent).toBe('AppNewApp'); expect(LogUtils.getAndClear()).toEqual([]); // 在执行新的render前,会执行完上一次render的useEffect,所以LogUtils会加入'NewApp effect'。 - HorizonDOM.render([na], container); + Horizon.render([na], container); expect(LogUtils.getAndClear()).toEqual(['NewApp effect']); expect(container.textContent).toBe('NewApp'); expect(LogUtils.getAndClear()).toEqual([]); @@ -104,7 +102,7 @@ describe('useEffect Hook Test', () => { return ; } // 必须设置key值,否则在diff的时候na会被视为不同组件 - HorizonDOM.render([, ], container); + Horizon.render([, ], container); expect(LogUtils.getAndClear()).toEqual([ 'App', 'NewApp', @@ -122,7 +120,7 @@ describe('useEffect Hook Test', () => { const App = () => { useLayoutEffect(() => { LogUtils.log('App Layout effect'); - HorizonDOM.render(, newContainer); + Horizon.render(, newContainer); }); return ; } @@ -133,7 +131,7 @@ describe('useEffect Hook Test', () => { return ; } // 必须设置key值,否则在diff的时候na会被视为不同组件 - HorizonDOM.render([, ], container); + Horizon.render([, ], container); expect(LogUtils.getAndClear()).toEqual([ 'App', 'NewApp', @@ -153,13 +151,13 @@ describe('useEffect Hook Test', () => { return ; } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']); expect(container.textContent).toEqual('num: 0'); }) expect(LogUtils.getAndClear()).toEqual(['First effect [0]']); act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }) // 此时异步执行,act执行完后会执行新render的useEffect @@ -182,13 +180,13 @@ describe('useEffect Hook Test', () => { return ; } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']); expect(container.textContent).toEqual('num: 0'); }) expect(LogUtils.getAndClear()).toEqual(['First effect [0]', 'Second effect [0]']); act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }) // 第二次render时异步执行,act保证所有效果都已更新,所以先常规记录日志 // 然后记录useEffect的日志 @@ -231,7 +229,7 @@ describe('useEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0,word: App', 'num Layouteffect [0]', @@ -246,21 +244,21 @@ describe('useEffect Hook Test', () => { act(() => { // 此时word改变,num不变 - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ - 'num: 0,word: React', + 'num: 0,word: Horizon', 'word Layouteffect destroy', - 'word Layouteffect [React]', + 'word Layouteffect [Horizon]', 'callback effect', // 最后执行异步的 'word effect destroy', - 'word effect [React]', + 'word effect [Horizon]', ]); act(() => { // 此时num和word的所有effect都销毁 - HorizonDOM.render(null, container, () => LogUtils.log('callback effect')); + Horizon.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'num Layouteffect destroy', @@ -284,7 +282,7 @@ describe('useEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0', 'callback effect' @@ -296,7 +294,7 @@ describe('useEffect Hook Test', () => { ]); act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'num: 1', @@ -309,7 +307,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual([]); act(() => { - HorizonDOM.render(null, container, () => LogUtils.log('callback effect')); + Horizon.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'callback effect', @@ -331,7 +329,7 @@ describe('useEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0', 'callback effect' @@ -343,7 +341,7 @@ describe('useEffect Hook Test', () => { ]); act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'num: 1', @@ -354,7 +352,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual([]); act(() => { - HorizonDOM.render(null, container, () => LogUtils.log('callback effect')); + Horizon.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'callback effect', @@ -367,7 +365,7 @@ describe('useEffect Hook Test', () => { it('useEffect里使用useState(1', () => { let setNum; const App = () => { - const [num, _setNum] = React.useState(0); + const [num, _setNum] = Horizon.useState(0); useEffect(() => { LogUtils.log(`num effect [${num}]`); setNum = () => _setNum(1); @@ -382,7 +380,7 @@ describe('useEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0', 'num Layouteffect [0]', @@ -415,7 +413,7 @@ describe('useEffect Hook Test', () => { return ; } - HorizonDOM.render(, container, () => LogUtils.log('App callback effect')); + Horizon.render(, container, () => LogUtils.log('App callback effect')); expect(LogUtils.getAndClear()).toEqual(['Num: 0', 'App callback effect']); expect(container.textContent).toEqual('Num: 0'); act(() => { @@ -445,7 +443,7 @@ describe('useEffect Hook Test', () => { return ; }) act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 0, 'callback effect' @@ -456,7 +454,7 @@ describe('useEffect Hook Test', () => { // 不会重新渲染 act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual(['callback effect']); expect(container.textContent).toEqual('0'); @@ -475,7 +473,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual([]); act(() => { - HorizonDOM.render(null, container, () => LogUtils.log('callback effect')); + Horizon.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 'callback effect', @@ -497,7 +495,7 @@ describe('useEffect Hook Test', () => { return ; }, compare) act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 0, 'callback effect' @@ -508,7 +506,7 @@ describe('useEffect Hook Test', () => { // 不会重新渲染 act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual(['callback effect']); expect(container.textContent).toEqual('0'); @@ -516,7 +514,7 @@ describe('useEffect Hook Test', () => { // 会重新渲染 act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual([ 1, @@ -529,7 +527,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual([]); act(() => { - HorizonDOM.render(null, container, () => LogUtils.log('callback effect')); + Horizon.render(null, container, () => LogUtils.log('callback effect')); }); expect(LogUtils.getAndClear()).toEqual(['callback effect', 'num effect destroy 1']); expect(container.textContent).toEqual(''); @@ -550,7 +548,7 @@ describe('useEffect Hook Test', () => { return ; } act(() => { - HorizonDOM.render(, container, () => + Horizon.render(, container, () => LogUtils.log('App callback effect'), ); expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'App callback effect']); @@ -560,7 +558,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual(['throw Error']); act(() => { - HorizonDOM.render(null, container, () => + Horizon.render(null, container, () => LogUtils.log('App callback effect'), ); }); @@ -592,14 +590,14 @@ describe('useEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('num effect')); + Horizon.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'num effect']); expect(container.textContent).toBe('Number: 0'); }); expect(LogUtils.getAndClear()).toEqual(['num effect [0]']); act(() => { - HorizonDOM.render(null, container, () => LogUtils.log('num effect')); + Horizon.render(null, container, () => LogUtils.log('num effect')); }); expect(LogUtils.getAndClear()).toEqual(['num effect', 'num effect destroy 0']); expect(container.textContent).toBe(''); @@ -618,7 +616,7 @@ describe('useEffect Hook Test', () => { return ; } - HorizonDOM.render(, container, () => LogUtils.log('num effect')); + Horizon.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'num effect']); expect(container.textContent).toBe('Number: 0'); @@ -648,13 +646,13 @@ describe('useEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('num effect')); + Horizon.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual(['useEffect', 'num effect']); }); expect(LogUtils.getAndClear()).toEqual(['effect']); act(() => { - HorizonDOM.render(null, container); + Horizon.render(null, container); }); // 不会处理setNum(1) expect(LogUtils.getAndClear()).toEqual(['effect destroy']); @@ -663,7 +661,7 @@ describe('useEffect Hook Test', () => { it('当组件的更新方法在卸载函数中,组件的子组件更新不会告警', () => { const App = () => { LogUtils.log('App'); - const appRef = React.createRef(null); + const appRef = Horizon.createRef(null); useEffect(() => { LogUtils.log('App effect'); return () => { @@ -686,7 +684,7 @@ describe('useEffect Hook Test', () => { AppChild = forwardRef(AppChild); act(() => { - HorizonDOM.render(, container, () => LogUtils.log('num effect')); + Horizon.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual([ 'App', 'AppChild', @@ -696,7 +694,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual(['Child effect', 'App effect']); act(() => { - HorizonDOM.render(null, container); + Horizon.render(null, container); }); // 销毁时执行appRef.current(1)不会报错 expect(LogUtils.getAndClear()).toEqual(['App effect destroy']); @@ -722,7 +720,7 @@ describe('useEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('num effect')); + Horizon.render(, container, () => LogUtils.log('num effect')); expect(LogUtils.getAndClear()).toEqual([ 'App', 'AppChild', @@ -732,7 +730,7 @@ describe('useEffect Hook Test', () => { expect(LogUtils.getAndClear()).toEqual(['Child effect']); act(() => { - HorizonDOM.render(null, container); + Horizon.render(null, container); }); // 销毁时执行 props.setNum(1);不会报错 expect(LogUtils.getAndClear()).toEqual(['Child effect destroy']); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js b/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js index 10ce8623..b7f5d6e8 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js @@ -1,6 +1,4 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; import * as LogUtils from '../../jest/logUtils'; import { act } from '../../jest/customMatcher'; import Text from '../../jest/Text'; @@ -10,9 +8,9 @@ describe('useImperativeHandle Hook Test', () => { useState, useImperativeHandle, forwardRef - } = React; - const { unmountComponentAtNode } = HorizonDOM; - + } = Horizon; + const { unmountComponentAtNode } = Horizon; + it('测试useImperativeHandle', () => { let App = (props, ref) => { @@ -28,9 +26,9 @@ describe('useImperativeHandle Hook Test', () => { App = forwardRef(App); App1 = forwardRef(App1); - const counter = React.createRef(null); - const counter1 = React.createRef(null); - HorizonDOM.render(, container); + const counter = Horizon.createRef(null); + const counter1 = Horizon.createRef(null); + Horizon.render(, container); expect(counter.current.num).toBe(0); act(() => { counter.current.setNum(1); @@ -40,7 +38,7 @@ describe('useImperativeHandle Hook Test', () => { // 清空container unmountComponentAtNode(container); - HorizonDOM.render(, container); + Horizon.render(, container); expect(counter1.current.num1).toBe(0); act(() => { counter1.current.setNum1(1); @@ -64,9 +62,9 @@ describe('useImperativeHandle Hook Test', () => { App = forwardRef(App); App1 = forwardRef(App1); - const counter = React.createRef(null); - const counter1 = React.createRef(null); - HorizonDOM.render(, container); + const counter = Horizon.createRef(null); + const counter1 = Horizon.createRef(null); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([0]); expect(counter.current.num).toBe(0); act(() => { @@ -78,7 +76,7 @@ describe('useImperativeHandle Hook Test', () => { // 清空container unmountComponentAtNode(container); - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([0]); expect(counter1.current.num1).toBe(0); act(() => { diff --git a/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js b/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js index ec7eda11..84226eac 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js @@ -1,6 +1,4 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; import * as LogUtils from '../../jest/logUtils'; import { act } from '../../jest/customMatcher'; import Text from '../../jest/Text'; @@ -10,7 +8,7 @@ describe('useLayoutEffect Hook Test', () => { useState, useEffect, useLayoutEffect - } = React; + } = Horizon; it('简单使用useLayoutEffect', () => { const App = () => { @@ -25,7 +23,7 @@ describe('useLayoutEffect Hook Test', () => { ) } - HorizonDOM.render(, container); + Horizon.render(, container); expect(document.getElementById('p').style.display).toBe('none'); container.querySelector('button').click(); expect(container.querySelector('p').style.display).toBe('inline'); @@ -38,7 +36,7 @@ describe('useLayoutEffect Hook Test', () => { }); return ; } - HorizonDOM.render(, container, () => LogUtils.log('Sync effect')); + Horizon.render(, container, () => LogUtils.log('Sync effect')); expect(LogUtils.getAndClear()).toEqual([ 1, // 同步在渲染之后 @@ -47,7 +45,7 @@ describe('useLayoutEffect Hook Test', () => { ]); expect(container.querySelector('p').innerHTML).toBe('1'); // 更新 - HorizonDOM.render(, container, () => LogUtils.log('Sync effect')); + Horizon.render(, container, () => LogUtils.log('Sync effect')); expect(LogUtils.getAndClear()).toEqual([ 2, 'LayoutEffect', @@ -74,7 +72,7 @@ describe('useLayoutEffect Hook Test', () => { } act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); expect(LogUtils.getAndClear()).toEqual([ 'num: 0', 'num Layouteffect [0]', @@ -85,7 +83,7 @@ describe('useLayoutEffect Hook Test', () => { // 更新 act(() => { - HorizonDOM.render(, container, () => LogUtils.log('callback effect')); + Horizon.render(, container, () => LogUtils.log('callback effect')); }) expect(LogUtils.getAndClear()).toEqual([ // 异步effect @@ -103,7 +101,7 @@ describe('useLayoutEffect Hook Test', () => { ]); act(() => { - HorizonDOM.render(null, container, () => LogUtils.log('callback effect')); + Horizon.render(null, container, () => LogUtils.log('callback effect')); }) expect(LogUtils.getAndClear()).toEqual([ // 同步Layouteffect销毁 diff --git a/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js b/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js index 294ae787..41882449 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js @@ -1,11 +1,9 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; import * as LogUtils from '../../jest/logUtils'; import Text from '../../jest/Text'; describe('useMemo Hook Test', () => { - const { useMemo, useState } = React; + const { useMemo, useState } = Horizon; it('测试useMemo', () => { let setMemo; @@ -24,7 +22,7 @@ describe('useMemo Hook Test', () => { ); } - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe('App'); expect(container.querySelector('#p').innerHTML).toBe('1'); // 修改useMemo的依赖项,num会加一,text会改变。 @@ -49,26 +47,26 @@ describe('useMemo Hook Test', () => { }, [props._num]); return ; } - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([ 0, 1 ]); expect(container.textContent).toBe('1'); - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([ 1, 2 ]); expect(container.textContent).toBe('2'); - HorizonDOM.render(, container); + Horizon.render(, container); // 不会触发useMemo expect(LogUtils.getAndClear()).toEqual([2]); expect(container.textContent).toBe('2'); - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([ 2, 3 @@ -92,16 +90,16 @@ describe('useMemo Hook Test', () => { return 2; } - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual(['num 1', 1]); - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual(['num 1', 1]); - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual(['num 1', 1]); - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual(['num 2', 2]); }); }); diff --git a/scripts/__tests__/ComponentTest/HookTest/UseReducer.test.js b/scripts/__tests__/ComponentTest/HookTest/UseReducer.test.js index 29c228c1..a6a427ba 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseReducer.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseReducer.test.js @@ -1,10 +1,8 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; describe('useReducer Hook Test', () => { - const { useReducer } = React; - + const { useReducer } = Horizon; + it('简单使用useReducer', () => { const intlCar = { logo: '', price: 0 }; let dispatch; @@ -46,7 +44,7 @@ describe('useReducer Hook Test', () => { ) } - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe(''); expect(container.querySelector('#senP').innerHTML).toBe('0'); // 触发bmw diff --git a/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js b/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js index 39e663bb..b2bab667 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js @@ -1,11 +1,9 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; import * as LogUtils from '../../jest/logUtils'; import Text from '../../jest/Text'; describe('useRef Hook Test', () => { - const { useState, useRef } = React; + const { useState, useRef } = Horizon; it('测试useRef', () => { const App = () => { @@ -22,7 +20,7 @@ describe('useRef Hook Test', () => { ) } - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe('1'); expect(container.querySelector('#sp').innerHTML).toBe('1'); // 点击按钮触发num加1,ref不变 @@ -46,7 +44,7 @@ describe('useRef Hook Test', () => { ) } - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([1]); expect(container.querySelector('p').innerHTML).toBe('1'); // 点击按钮触发ref.current加1 diff --git a/scripts/__tests__/ComponentTest/HookTest/UseState.test.js b/scripts/__tests__/ComponentTest/HookTest/UseState.test.js index adccf421..b9e1d950 100644 --- a/scripts/__tests__/ComponentTest/HookTest/UseState.test.js +++ b/scripts/__tests__/ComponentTest/HookTest/UseState.test.js @@ -1,6 +1,4 @@ -/* eslint-disable no-undef */ -import * as React from '../../../../libs/horizon/src/external/Horizon'; -import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal'; +import * as Horizon from '@cloudsop/horizon/index.ts'; import * as LogUtils from '../../jest/logUtils'; import { act } from '../../jest/customMatcher'; import Text from '../../jest/Text'; @@ -11,7 +9,7 @@ describe('useState Hook Test', () => { forwardRef, useImperativeHandle, memo - } = React; + } = Horizon; it('简单使用useState', () => { const App = () => { @@ -23,7 +21,7 @@ describe('useState Hook Test', () => { ) } - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); // 点击按钮触发num加1 container.querySelector('button').click(); @@ -45,7 +43,7 @@ describe('useState Hook Test', () => {

); } - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe('00'); container.querySelector('p').click(); expect(container.querySelector('p').innerHTML).toBe('12'); @@ -67,7 +65,7 @@ describe('useState Hook Test', () => {

); } - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); container.querySelector('p').click(); expect(container.querySelector('p').innerHTML).toBe('2'); @@ -82,7 +80,7 @@ describe('useState Hook Test', () => { setNum = _setNum; return ; } - HorizonDOM.render(, container); + Horizon.render(, container); expect(container.querySelector('p').innerHTML).toBe('0'); expect(LogUtils.getAndClear()).toEqual([0]); // useState修改state 时,设置相同的值,函数组件不会重新渲染 @@ -101,8 +99,8 @@ describe('useState Hook Test', () => { return

{num}

; }) - const ref = React.createRef(null); - HorizonDOM.render(, container); + const ref = Horizon.createRef(null); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([1]); expect(container.querySelector('p').innerHTML).toBe('1'); // 设置num为3 @@ -119,11 +117,11 @@ describe('useState Hook Test', () => { setNum = _setNum; return ; }) - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([0]); expect(container.querySelector('p').innerHTML).toBe('0'); // 不会重新渲染 - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual([]); expect(container.querySelector('p').innerHTML).toBe('0'); // 会重新渲染 @@ -153,7 +151,7 @@ describe('useState Hook Test', () => { return ; } - HorizonDOM.render(, container); + Horizon.render(, container); expect(LogUtils.getAndClear()).toEqual(['Number: 0, Count: 0']); expect(container.textContent).toBe('Number: 0, Count: 0'); act(() => { @@ -164,7 +162,7 @@ describe('useState Hook Test', () => { expect(container.textContent).toBe('Number: 1, Count: 2'); expect(() => { - HorizonDOM.render(, container); + Horizon.render(, container); }).toThrow( 'Hooks are less than expected, please check whether the hook is written in the condition.', ); diff --git a/scripts/__tests__/jest/Text.js b/scripts/__tests__/jest/Text.js index 24d63cf8..781c6d5a 100644 --- a/scripts/__tests__/jest/Text.js +++ b/scripts/__tests__/jest/Text.js @@ -1,4 +1,4 @@ -import * as React from '../../../libs/horizon/src/external/Horizon'; +import * as Horizon from '@cloudsop/horizon/index.ts'; import * as LogUtils from '../jest/logUtils'; const Text = (props) => { diff --git a/scripts/webpack/webpack.pro.js b/scripts/webpack/webpack.pro.js index b57b8eba..3494e99e 100644 --- a/scripts/webpack/webpack.pro.js +++ b/scripts/webpack/webpack.pro.js @@ -45,11 +45,11 @@ const cjs = { ...plugins, new CopyWebpackPlugin([ { - from: path.join(__dirname, '../../libs/index.js'), + from: path.join(__dirname, '../../libs/horizon/index.js'), to: path.join(__dirname, '../../build/horizon/index.js'), }, { - from: path.join(__dirname, '../../libs/package.json'), + from: path.join(__dirname, '../../libs/horizon/package.json'), to: path.join(__dirname, '../../build/horizon/package.json'), } ]) From 55dac3b56baa13b8b66de746d2af5a2bd3b5c065 Mon Sep 17 00:00:00 2001 From: * <8> Date: Mon, 21 Mar 2022 17:50:18 +0800 Subject: [PATCH 02/13] Match-id-580af01343fa5bba627a04ab667a638ec280a603 --- libs/horizon/src/renderer/components/BaseClassComponent.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/horizon/src/renderer/components/BaseClassComponent.ts b/libs/horizon/src/renderer/components/BaseClassComponent.ts index 74f6df80..b5b202c2 100644 --- a/libs/horizon/src/renderer/components/BaseClassComponent.ts +++ b/libs/horizon/src/renderer/components/BaseClassComponent.ts @@ -1,18 +1,22 @@ /** * Component的api setState和forceUpdate在实例生成阶段实现 */ + class Component { props: P; context: C; state: S | null; refs: any; - setState: any; forceUpdate: any; constructor(props: P, context: C) { this.props = props; this.context = context; } + + setState(state: S) { + console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing') + } } // 兼容三方件 react-lifecycles-compat,它会读取 isReactComponent 属性值,不添加会导致 eview-ui 官网白屏 From ea44e30933164f272d7411e6d6b637d220a9083c Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 22 Mar 2022 10:26:15 +0800 Subject: [PATCH 03/13] Match-id-5935bd5d4b80a4524d2b9a839ca106c4249ad3e5 --- .eslintignore | 1 + .eslintrc.js | 5 +++++ libs/horizon/index.d.ts | 5 ++++- libs/horizon/src/renderer/components/BaseClassComponent.ts | 7 +++++-- tsconfig.json | 4 ++-- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.eslintignore b/.eslintignore index 25439044..60feb237 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ **/node_modules build/ +*.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index 79557bf5..46239631 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,8 @@ module.exports = { extends: [ 'eslint:recommended', + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", 'prettier', ], root: true, @@ -41,6 +43,9 @@ module.exports = { 'no-for-of-loops/no-for-of-loops': 'error', 'no-function-declare-after-return/no-function-declare-after-return': 'error', }, + globals: { + isDev: true + }, overrides: [ { files: [ diff --git a/libs/horizon/index.d.ts b/libs/horizon/index.d.ts index 4c54c7c5..bab6684d 100644 --- a/libs/horizon/index.d.ts +++ b/libs/horizon/index.d.ts @@ -1 +1,4 @@ -declare var isDev: any; +/* + 区分是否开发者模式 + */ +declare var isDev: boolean; diff --git a/libs/horizon/src/renderer/components/BaseClassComponent.ts b/libs/horizon/src/renderer/components/BaseClassComponent.ts index b5b202c2..c8fcf9a9 100644 --- a/libs/horizon/src/renderer/components/BaseClassComponent.ts +++ b/libs/horizon/src/renderer/components/BaseClassComponent.ts @@ -2,7 +2,7 @@ * Component的api setState和forceUpdate在实例生成阶段实现 */ -class Component { +class Component { props: P; context: C; state: S | null; @@ -15,12 +15,15 @@ class Component { } setState(state: S) { - console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing') + if (isDev) { + console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing'); + } } } // 兼容三方件 react-lifecycles-compat,它会读取 isReactComponent 属性值,不添加会导致 eview-ui 官网白屏 Component.prototype.isReactComponent = true; + /** * 支持PureComponent */ diff --git a/tsconfig.json b/tsconfig.json index e05c4501..e85c8cac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,8 +34,8 @@ }, "include": [ "./libs/**/src/**/*.ts", - "libs/index.d.ts" + "libs/horizon/index.d.ts" ], "exclude": ["node_modules", "**/*.spec.ts", "dev"], - "types": ["node"], + "types": ["node"] } From dfa9ad8f020bfedba20ee8c351ee27773135ec09 Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 23 Mar 2022 10:15:58 +0800 Subject: [PATCH 04/13] Match-id-2c5b3e4e34c044c80842fd08377d49c00014c684 --- libs/horizon/src/renderer/TreeBuilder.ts | 33 +++++++------------ .../renderer/components/BaseClassComponent.ts | 2 +- .../src/renderer/diff/nodeDiffComparator.ts | 10 +++--- .../src/renderer/render/MemoComponent.ts | 7 ++-- .../src/renderer/render/SuspenseComponent.ts | 5 +-- libs/horizon/src/renderer/utils/vNodePath.ts | 15 +++++++++ .../src/renderer/vnode/VNodeCreator.ts | 9 ++--- 7 files changed, 46 insertions(+), 35 deletions(-) create mode 100644 libs/horizon/src/renderer/utils/vNodePath.ts diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 9bf2455a..721bbff6 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -35,6 +35,7 @@ import { updateParentsChildShouldUpdate, updateShouldUpdateOfTree } from './vnode/VNodeShouldUpdate'; +import { getPathArr } from './utils/vNodePath'; // 不可恢复错误 let unrecoverableErrorDuringBuild: any = null; @@ -142,11 +143,11 @@ function handleError(root, error): void { } // 判断数组中节点的path的idx元素是否都相等 -function isEqualByIndex(idx: number, nodes: Array) { - let val = nodes[0].path[idx]; - for (let i = 1; i < nodes.length; i++) { - let node = nodes[i]; - if (val !== node.path[idx]) { +function isEqualByIndex(idx: number, pathArrays: string[][]) { + const first = pathArrays[0][idx]; + for (let i = 1; i < pathArrays.length; i++) { + const pathArr = pathArrays[i]; + if (idx >= pathArr.length || first !== pathArr[idx]) { return false; } } @@ -179,29 +180,19 @@ export function calcStartUpdateVNode(treeRoot: VNode) { return treeRoot; } - // 找到路径最短的长度 - let minPath = toUpdateNodes[0].path.length; - for (let i = 1; i < toUpdateNodes.length; i++) { - let pathLen = toUpdateNodes[i].path.length; - if (pathLen < minPath) { - minPath = pathLen; - } - } - + const pathArrays = toUpdateNodes.map(node => getPathArr(node)); // 找出开始不相等的idx - let idx = 0; - for (; idx < minPath; idx++) { - if (!isEqualByIndex(idx, toUpdateNodes)) { - break; - } + let commonPathEndIndex = 0; + while (isEqualByIndex(commonPathEndIndex, pathArrays)) { + commonPathEndIndex++; } // 得到相等的路径 - const startNodePath = toUpdateNodes[0].path.slice(0, idx); + const startNodePath = pathArrays[0].slice(0, commonPathEndIndex); let node = treeRoot; for (let i = 1; i < startNodePath.length; i++) { const pathIndex = Number(startNodePath[i]); - node = getChildByIndex(node, pathIndex); + node = getChildByIndex(node, pathIndex)!; } return node; diff --git a/libs/horizon/src/renderer/components/BaseClassComponent.ts b/libs/horizon/src/renderer/components/BaseClassComponent.ts index c8fcf9a9..08b6fb30 100644 --- a/libs/horizon/src/renderer/components/BaseClassComponent.ts +++ b/libs/horizon/src/renderer/components/BaseClassComponent.ts @@ -1,13 +1,13 @@ /** * Component的api setState和forceUpdate在实例生成阶段实现 */ - class Component { props: P; context: C; state: S | null; refs: any; forceUpdate: any; + isReactComponent: boolean; constructor(props: P, context: C) { this.props = props; diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index db920f79..2b97612f 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -17,6 +17,7 @@ import { isObjectType, } from './DiffTools'; import { travelChildren } from '../vnode/VNodeUtils'; +import { markVNodePath } from '../utils/vNodePath'; enum DiffCategory { TEXT_NODE = 'TEXT_NODE', @@ -241,7 +242,7 @@ function diffArrayNodesHandler( prevNewNode.next = newNode; newNode.cIndex = prevNewNode.cIndex + 1; } - newNode.path = newNode.parent.path + newNode.cIndex; + markVNodePath(newNode); prevNewNode = newNode; } @@ -477,7 +478,7 @@ function setVNodesCIndex(startChild: VNode | null, startIdx: number) { while (node !== null) { node.cIndex = idx; - node.path = node.parent.path + node.cIndex; + markVNodePath(node); node = node.next; idx++; } @@ -528,7 +529,7 @@ function diffStringNodeHandler( } newTextNode.parent = parentNode; newTextNode.cIndex = 0; - newTextNode.path = newTextNode.parent.path + newTextNode.cIndex; + markVNodePath(newTextNode); return newTextNode; } @@ -606,7 +607,8 @@ function diffObjectNodeHandler( resultNode.parent = parentNode; resultNode.cIndex = 0; - resultNode.path = resultNode.parent.path + resultNode.cIndex; + markVNodePath(resultNode); + if (startDelVNode) { deleteVNodes(parentNode, startDelVNode); } diff --git a/libs/horizon/src/renderer/render/MemoComponent.ts b/libs/horizon/src/renderer/render/MemoComponent.ts index 84daa883..99a34364 100644 --- a/libs/horizon/src/renderer/render/MemoComponent.ts +++ b/libs/horizon/src/renderer/render/MemoComponent.ts @@ -8,6 +8,7 @@ import { TYPE_PROFILER, TYPE_STRICT_MODE, } from '../../external/JSXElementType'; +import { markVNodePath } from '../utils/vNodePath'; export function bubbleRender() {} @@ -20,7 +21,7 @@ export function captureMemoComponent( const newProps = mergeDefaultProps(Component, processing.props); if (processing.isCreated) { - let newChild = null; + let newChild: VNode | null = null; const type = Component.type; if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) { newChild = createFragmentVNode(null, newProps.children); @@ -29,7 +30,7 @@ export function captureMemoComponent( } newChild.parent = processing; newChild.ref = processing.ref; - newChild.path = newChild.parent.path + newChild.cIndex; + markVNodePath(newChild); processing.child = newChild; return newChild; @@ -48,7 +49,7 @@ export function captureMemoComponent( const newChild = updateVNode(firstChild, newProps); newChild.parent = processing; newChild.cIndex = 0; - newChild.path = newChild.parent.path + newChild.cIndex; + markVNodePath(newChild); newChild.ref = processing.ref; processing.child = newChild; diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index cdc2f4a4..3e7bdcb2 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -11,6 +11,7 @@ import {pushForceUpdate} from '../UpdateHandler'; import {launchUpdateFromVNode, tryRenderFromRoot} from '../TreeBuilder'; import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate'; import {getContextChangeCtx} from '../ContextSaver'; +import { markVNodePath } from '../utils/vNodePath'; export enum SuspenseChildStatus { Init = '', @@ -44,7 +45,7 @@ function createFallback(processing: VNode, fallbackChildren) { fallbackFragment.parent = processing; fallbackFragment.eIndex = 1; fallbackFragment.cIndex = 1; - fallbackFragment.path = fallbackFragment.parent.path + fallbackFragment.cIndex; + markVNodePath(fallbackFragment); processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback; return fallbackFragment; @@ -76,7 +77,7 @@ function createSuspenseChildren(processing: VNode, newChildren) { childFragment.parent = processing; childFragment.cIndex = 0; - childFragment.path = childFragment.parent.path + childFragment.cIndex; + markVNodePath(childFragment); processing.child = childFragment; processing.promiseResolve = false; return processing.child; diff --git a/libs/horizon/src/renderer/utils/vNodePath.ts b/libs/horizon/src/renderer/utils/vNodePath.ts new file mode 100644 index 00000000..5f8b78ac --- /dev/null +++ b/libs/horizon/src/renderer/utils/vNodePath.ts @@ -0,0 +1,15 @@ +import { VNode } from '../vnode/VNode'; + +const PATH_DELIMITER = ','; + +/** + * 标记VNode在VNode树中的路径 + * @param vNode + */ +export function markVNodePath(vNode: VNode) { + vNode.path = `${vNode.parent!.path}${PATH_DELIMITER}${vNode.cIndex}`; +} + +export function getPathArr(vNode: VNode) { + return vNode.path.split(PATH_DELIMITER); +} diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 865f791e..8c76d63c 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -26,6 +26,7 @@ import { } from '../../external/JSXElementType'; import { VNode } from './VNode'; import { JSXElement } from '../Types'; +import { markVNodePath } from '../utils/vNodePath'; const typeLazyMap = { [TYPE_FORWARD_REF]: ForwardRef, @@ -135,7 +136,7 @@ export function createUndeterminedVNode(type, key, props) { export function createTreeRootVNode(container) { const vNode = newVirtualNode(TreeRoot, null, null, container); - vNode.path += 0; + vNode.path = '0'; vNode.updates = []; return vNode; } @@ -147,7 +148,7 @@ export function createVNode(tag: VNodeTag | string, ...secondArg) { case TreeRoot: // 创建treeRoot vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]); - vNode.path += 0; + vNode.path = '0'; vNode.updates = []; break; @@ -178,7 +179,7 @@ export function onlyUpdateChildVNodes(processing: VNode): VNode | null { let child: VNode | null = processing.child; while (child !== null) { updateVNode(child, child.props); - child.path = child.parent.path + child.cIndex; + markVNodePath(child); child = child.next; } } @@ -209,7 +210,7 @@ export function onlyUpdateChildVNodes(processing: VNode): VNode | null { while (queue.length) { const vNode = queue.shift()!; - vNode.path = vNode.parent.path + vNode.cIndex; + markVNodePath(vNode); putChildrenIntoQueue(vNode) } From c6f1debb0338533b97144cc51db505baac9f246d Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 23 Mar 2022 10:28:58 +0800 Subject: [PATCH 05/13] Match-id-d0e5ae18684fb3ef46de9d8df3806ec5e84da25b --- .eslintrc.js | 2 ++ libs/horizon/src/renderer/TreeBuilder.ts | 2 +- libs/horizon/src/renderer/hooks/BaseHook.ts | 8 ++++---- libs/horizon/src/renderer/hooks/HookMain.ts | 18 +++++++++--------- libs/horizon/src/renderer/hooks/HookStage.ts | 2 +- package.json | 1 + 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 46239631..f25357b3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,6 +32,8 @@ module.exports = { es6: true, }, rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', 'accessor-pairs': 'off', 'brace-style': ['error', '1tbs'], 'func-style': ['warn', 'declaration', { allowArrowFunctions: true }], diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 721bbff6..ec86db16 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -165,7 +165,7 @@ function getChildByIndex(vNode: VNode, idx: number) { // 从多个更新节点中,计算出开始节点。即:找到最近的共同的父辈节点 export function calcStartUpdateVNode(treeRoot: VNode) { - const toUpdateNodes = Array.from(treeRoot.toUpdateNodes); + const toUpdateNodes = [...treeRoot.toUpdateNodes]; if (toUpdateNodes.length === 0) { return treeRoot; diff --git a/libs/horizon/src/renderer/hooks/BaseHook.ts b/libs/horizon/src/renderer/hooks/BaseHook.ts index 8c3f3605..54e180c8 100644 --- a/libs/horizon/src/renderer/hooks/BaseHook.ts +++ b/libs/horizon/src/renderer/hooks/BaseHook.ts @@ -1,7 +1,7 @@ import type { VNode } from '../Types'; import type { Hook } from './HookType'; -let processingVNode: VNode = null; +let processingVNode: VNode | null = null; // lastTimeHook是上一次执行func时产生的hooks中,与currentHook对应的hook let lastTimeHook: Hook | null = null; @@ -13,7 +13,7 @@ export function getProcessingVNode() { return processingVNode; } -export function setProcessingVNode(vNode: VNode) { +export function setProcessingVNode(vNode: VNode | null) { processingVNode = vNode; } @@ -21,11 +21,11 @@ export function getLastTimeHook() { return lastTimeHook; } -export function setLastTimeHook(hook: Hook) { +export function setLastTimeHook(hook: Hook | null) { lastTimeHook = hook; } -export function setCurrentHook(hook: Hook) { +export function setCurrentHook(hook: Hook | null) { currentHook = hook; } diff --git a/libs/horizon/src/renderer/hooks/HookMain.ts b/libs/horizon/src/renderer/hooks/HookMain.ts index e8992e5a..f4c568ea 100644 --- a/libs/horizon/src/renderer/hooks/HookMain.ts +++ b/libs/horizon/src/renderer/hooks/HookMain.ts @@ -15,17 +15,17 @@ import { setProcessingVNode, setCurrentHook, getNextHook } from './BaseHook'; -import {useStateImpl,} from './UseStateHook'; -import {useReducerImpl,} from './UseReducerHook'; +import {useStateImpl} from './UseStateHook'; +import {useReducerImpl} from './UseReducerHook'; import {HookStage, setHookStage} from './HookStage'; // hook对外入口 -export function exeFunctionHook( - funcComp: (props: Object, arg: Object) => any, - props: Object, - arg: Object, +export function exeFunctionHook, Arg>( + funcComp: (props: Props, arg: Arg) => any, + props: Props, + arg: Arg, processing: VNode, -): any { +) { // 重置全局变量 resetGlobalVariable(); @@ -39,13 +39,13 @@ export function exeFunctionHook( processing.effectList = []; // 设置hook阶段 - if (processing.isCreated || !processing.oldHooks.length) { + if (processing.isCreated || !processing.oldHooks!.length) { setHookStage(HookStage.Init); } else { setHookStage(HookStage.Update); } - let comp = funcComp(props, arg); + const comp = funcComp(props, arg); // 设置hook阶段为null,用于判断hook是否在函数组件中调用 setHookStage(null); diff --git a/libs/horizon/src/renderer/hooks/HookStage.ts b/libs/horizon/src/renderer/hooks/HookStage.ts index c3e9622a..4819554d 100644 --- a/libs/horizon/src/renderer/hooks/HookStage.ts +++ b/libs/horizon/src/renderer/hooks/HookStage.ts @@ -10,6 +10,6 @@ export function getHookStage() { return hookStage; } -export function setHookStage(phase: HookStage) { +export function setHookStage(phase: HookStage| null) { hookStage = phase; } diff --git a/package.json b/package.json index d3da90aa..5ef9d520 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "libs/*" ], "scripts": { + "lint": "eslint . --ext .ts", "build": " webpack --config ./scripts/webpack/webpack.config.js", "build-3rdLib": "node ./scripts/gen3rdLib.js", "build-3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js --dev", From 35d98b615fd7556907ffc40dee40e31fbf2089fd Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 24 Mar 2022 11:56:57 +0800 Subject: [PATCH 06/13] Match-id-18f59c81b8302ce98bbefcf4608d9327523dd34b --- libs/horizon/src/renderer/TreeBuilder.ts | 2 +- .../src/renderer/render/ClsOrFunComponent.ts | 36 ------------------- libs/horizon/src/renderer/render/index.ts | 3 -- libs/horizon/src/renderer/vnode/VNode.ts | 21 +++++------ .../src/renderer/vnode/VNodeCreator.ts | 10 +++--- libs/horizon/src/renderer/vnode/VNodeTags.ts | 1 - 6 files changed, 14 insertions(+), 59 deletions(-) delete mode 100644 libs/horizon/src/renderer/render/ClsOrFunComponent.ts diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index ec86db16..721bbff6 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -165,7 +165,7 @@ function getChildByIndex(vNode: VNode, idx: number) { // 从多个更新节点中,计算出开始节点。即:找到最近的共同的父辈节点 export function calcStartUpdateVNode(treeRoot: VNode) { - const toUpdateNodes = [...treeRoot.toUpdateNodes]; + const toUpdateNodes = Array.from(treeRoot.toUpdateNodes); if (toUpdateNodes.length === 0) { return treeRoot; diff --git a/libs/horizon/src/renderer/render/ClsOrFunComponent.ts b/libs/horizon/src/renderer/render/ClsOrFunComponent.ts deleted file mode 100644 index fd342fac..00000000 --- a/libs/horizon/src/renderer/render/ClsOrFunComponent.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type {VNode} from '../Types'; - -import {FunctionComponent} from '../vnode/VNodeTags'; -import {resetDepContexts} from '../components/context/Context'; -import {getOldContext} from '../components/context/CompatibleContext'; -import {FlagUtils} from '../vnode/VNodeFlags'; -import {exeFunctionHook} from '../hooks/HookMain'; -import { createChildrenByDiff } from '../diff/nodeDiffComparator'; - -function captureIndeterminateComponent( - processing: VNode, -): VNode | null { - const funcComp = processing.type; - - if (!processing.isCreated) { - processing.isCreated = true; - FlagUtils.markAddition(processing); - } - - const props = processing.props; - const context = getOldContext(processing, funcComp, false); - - resetDepContexts(processing); - - const newElements = exeFunctionHook(funcComp, props, context, processing); - - processing.tag = FunctionComponent; - processing.child = createChildrenByDiff(processing, processing.child, newElements, !processing.isCreated); - return processing.child; -} - -export function captureRender(processing: VNode): VNode | null { - return captureIndeterminateComponent(processing); -} - -export function bubbleRender() {} diff --git a/libs/horizon/src/renderer/render/index.ts b/libs/horizon/src/renderer/render/index.ts index 29637aa3..4497627a 100644 --- a/libs/horizon/src/renderer/render/index.ts +++ b/libs/horizon/src/renderer/render/index.ts @@ -10,7 +10,6 @@ import * as DomPortalRender from './DomPortal'; import * as TreeRootRender from './TreeRoot'; import * as DomTextRender from './DomText'; import * as IncompleteClassComponentRender from './IncompleteClassComponent'; -import * as ClsOrFunComponentRender from './ClsOrFunComponent'; import * as LazyComponentRender from './LazyComponent'; import * as MemoComponentRender from './MemoComponent'; import * as SuspenseComponentRender from './SuspenseComponent'; @@ -27,7 +26,6 @@ import { TreeRoot, DomText, IncompleteClassComponent, - ClsOrFunComponent, LazyComponent, MemoComponent, SuspenseComponent, @@ -49,7 +47,6 @@ export default { [TreeRoot]: TreeRootRender, [DomText]: DomTextRender, [IncompleteClassComponent]: IncompleteClassComponentRender, - [ClsOrFunComponent]: ClsOrFunComponentRender, [LazyComponent]: LazyComponentRender, [MemoComponent]: MemoComponentRender, [SuspenseComponent]: SuspenseComponentRender, diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index b076a3eb..6386fd98 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -1,7 +1,7 @@ /** * 虚拟DOM结构体 */ -import { TreeRoot, FunctionComponent, ClassComponent, DomPortal, DomText, ContextConsumer, ForwardRef, SuspenseComponent, LazyComponent, ClsOrFunComponent, DomComponent, Fragment, ContextProvider, Profiler, MemoComponent, IncompleteClassComponent } from './VNodeTags'; +import { TreeRoot, FunctionComponent, ClassComponent, DomPortal, DomText, ContextConsumer, ForwardRef, SuspenseComponent, LazyComponent, DomComponent, Fragment, ContextProvider, Profiler, MemoComponent, IncompleteClassComponent } from './VNodeTags'; import type { VNodeTag } from './VNodeTags'; import type { RefType, ContextType } from '../Types'; import type { Hook } from '../hooks/HookType'; @@ -18,8 +18,8 @@ export class VNode { parent: VNode | null = null; // 父节点 child: VNode | null = null; // 子节点 next: VNode | null = null; // 兄弟节点 - cIndex: number = 0; // 节点在children数组中的位置 - eIndex: number = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致 + cIndex = 0; // 节点在children数组中的位置 + eIndex = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致 ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数,submit阶段使用,比如将外部useRef生成的对象赋值到ref上 oldProps: any = null; @@ -33,12 +33,12 @@ export class VNode { state: any; // ClassComponent和TreeRoot的状态 hooks: Array> | null; // 保存hook - suspenseChildStatus: string = ''; // Suspense的Children是否显示 + suspenseChildStatus = ''; // Suspense的Children是否显示 depContexts: Array> | null; // FunctionComponent和ClassComponent对context的依赖列表 isDepContextChange: boolean; // context是否变更 dirtyNodes: Array | null = null; // 需要改动的节点数组 - shouldUpdate: boolean = false; - childShouldUpdate: boolean = false; + shouldUpdate = false; + childShouldUpdate = false; task: any; // 使用这个变量来记录修改前的值,用于恢复。 @@ -51,7 +51,7 @@ export class VNode { flags = InitFlag; clearChild: VNode | null; // one tree相关属性 - isCreated: boolean = true; + isCreated = true; oldHooks: Array> | null; // 保存上一次执行的hook oldState: any; oldRef: RefType | ((handle: any) => void) | null = null; @@ -61,7 +61,7 @@ export class VNode { suspenseDidCapture: boolean; // suspense是否捕获了异常 promiseResolve: boolean; // suspense的promise是否resolve - path: string = ''; // 保存从根到本节点的路径 + path = ''; // 保存从根到本节点的路径 toUpdateNodes: Set | null; // 保存要更新的节点 belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用 @@ -84,6 +84,7 @@ export class VNode { this.contexts = null; break; case FunctionComponent: + this.realNode = null; this.effectList = null; this.hooks = null; this.depContexts = null; @@ -101,10 +102,6 @@ export class VNode { this.oldState = null; this.contexts = null; break; - case ClsOrFunComponent: - this.realNode = null; - this.contexts = null; - break; case DomPortal: this.realNode = null; this.contexts = null; diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 8c76d63c..5dc4ccac 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -11,7 +11,6 @@ import { DomPortal, TreeRoot, DomText, - ClsOrFunComponent, LazyComponent, MemoComponent, SuspenseComponent, @@ -52,13 +51,12 @@ function isClassComponent(comp: Function) { // 解析懒组件的tag export function getLazyVNodeTag(lazyComp: any): string { - let vNodeTag = ClsOrFunComponent; if (typeof lazyComp === 'function') { - vNodeTag = isClassComponent(lazyComp) ? ClassComponent : FunctionComponent; + return isClassComponent(lazyComp) ? ClassComponent : FunctionComponent; } else if (lazyComp !== undefined && lazyComp !== null && typeLazyMap[lazyComp.vtype]) { - vNodeTag = typeLazyMap[lazyComp.vtype]; + return typeLazyMap[lazyComp.vtype]; } - return vNodeTag; + throw Error("Horizon can't resolve the content of lazy ") } // 创建processing @@ -105,7 +103,7 @@ export function createPortalVNode(portal) { } export function createUndeterminedVNode(type, key, props) { - let vNodeTag = ClsOrFunComponent; + let vNodeTag = FunctionComponent; let isLazy = false; const componentType = typeof type; diff --git a/libs/horizon/src/renderer/vnode/VNodeTags.ts b/libs/horizon/src/renderer/vnode/VNodeTags.ts index 2ec1db13..6e02e450 100644 --- a/libs/horizon/src/renderer/vnode/VNodeTags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeTags.ts @@ -6,7 +6,6 @@ export type VNodeTag = string; export const TreeRoot = 'TreeRoot'; // tree的根节点,用于存放一些tree级的变量 export const FunctionComponent = 'FunctionComponent'; export const ClassComponent = 'ClassComponent'; -export const ClsOrFunComponent = 'ClsOrFunComponent'; export const DomPortal = 'DomPortal'; export const DomComponent = 'DomComponent'; export const DomText = 'DomText'; From 78054d047a5ea02d1ed392235b5df64e2ceadef7 Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 24 Mar 2022 15:50:28 +0800 Subject: [PATCH 07/13] Match-id-fd102dce95197e4779d6b0155b78e27fa1b8a95e --- libs/horizon/src/renderer/hooks/HookStage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/horizon/src/renderer/hooks/HookStage.ts b/libs/horizon/src/renderer/hooks/HookStage.ts index 4819554d..5f0a5a65 100644 --- a/libs/horizon/src/renderer/hooks/HookStage.ts +++ b/libs/horizon/src/renderer/hooks/HookStage.ts @@ -10,6 +10,6 @@ export function getHookStage() { return hookStage; } -export function setHookStage(phase: HookStage| null) { +export function setHookStage(phase: HookStage | null) { hookStage = phase; } From c5dbb91635bb801682532ddc3b5b618b606e6750 Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 24 Mar 2022 16:04:38 +0800 Subject: [PATCH 08/13] Match-id-a41469cf2ad241a94d6f75a6753fd7af282d1453 --- .eslintrc.js | 1 + libs/horizon/src/renderer/vnode/VNodeCreator.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index f25357b3..1a425fc3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,6 +34,7 @@ module.exports = { rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-non-null-assertion': 'off', + 'semi': ["error", "always"], 'accessor-pairs': 'off', 'brace-style': ['error', '1tbs'], 'func-style': ['warn', 'declaration', { allowArrowFunctions: true }], diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 5dc4ccac..457a1000 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -56,7 +56,7 @@ export function getLazyVNodeTag(lazyComp: any): string { } else if (lazyComp !== undefined && lazyComp !== null && typeLazyMap[lazyComp.vtype]) { return typeLazyMap[lazyComp.vtype]; } - throw Error("Horizon can't resolve the content of lazy ") + throw Error("Horizon can't resolve the content of lazy "); } // 创建processing From a4f376b84b5edb70e937333fe376633822106558 Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 24 Mar 2022 16:08:18 +0800 Subject: [PATCH 09/13] Match-id-a2ff0c3642c1fb4b11d6b634a3fba736bf98d405 --- .eslintrc.js | 3 ++- libs/horizon/src/renderer/vnode/VNodeCreator.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 1a425fc3..ab410fd1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,7 +34,8 @@ module.exports = { rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-non-null-assertion': 'off', - 'semi': ["error", "always"], + 'semi': ['warn', 'always'], + 'quotes': ['warn', 'single'], 'accessor-pairs': 'off', 'brace-style': ['error', '1tbs'], 'func-style': ['warn', 'declaration', { allowArrowFunctions: true }], diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 457a1000..9a677f53 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -56,7 +56,7 @@ export function getLazyVNodeTag(lazyComp: any): string { } else if (lazyComp !== undefined && lazyComp !== null && typeLazyMap[lazyComp.vtype]) { return typeLazyMap[lazyComp.vtype]; } - throw Error("Horizon can't resolve the content of lazy "); + throw Error('Horizon can\'t resolve the content of lazy'); } // 创建processing From a3c5839a6b4b3d77231f49457200436e3fcd2fa1 Mon Sep 17 00:00:00 2001 From: * <8> Date: Fri, 25 Mar 2022 18:28:35 +0800 Subject: [PATCH 10/13] Match-id-b83bfbcd95720511a7e584ab7beb932978e800e2 --- scripts/__tests__/EventTest/EventMain.test.js | 131 +++++++++++++ .../__tests__/EventTest/FocusEvent.test.js | 46 +++++ .../__tests__/EventTest/KeyboardEvent.test.js | 179 ++++++++++++++++++ .../__tests__/EventTest/MouseEvent.test.js | 160 ++++++++++++++++ .../__tests__/EventTest/WheelEvent.test.js | 52 +++++ scripts/__tests__/jest/testUtils.js | 26 +++ 6 files changed, 594 insertions(+) create mode 100644 scripts/__tests__/EventTest/EventMain.test.js create mode 100644 scripts/__tests__/EventTest/FocusEvent.test.js create mode 100644 scripts/__tests__/EventTest/KeyboardEvent.test.js create mode 100644 scripts/__tests__/EventTest/MouseEvent.test.js create mode 100644 scripts/__tests__/EventTest/WheelEvent.test.js create mode 100644 scripts/__tests__/jest/testUtils.js diff --git a/scripts/__tests__/EventTest/EventMain.test.js b/scripts/__tests__/EventTest/EventMain.test.js new file mode 100644 index 00000000..1a3291c5 --- /dev/null +++ b/scripts/__tests__/EventTest/EventMain.test.js @@ -0,0 +1,131 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; +import * as TestUtils from '../jest/testUtils'; + +describe('事件', () => { + it('根节点挂载全量事件', () => { + const App = () => { + return
; + } + Horizon.render(, container); + console.log(TestUtils.getEventListeners(container)); + }) + + it('事件捕获与冒泡', () => { + const App = () => { + return ( + <> +
LogUtils.log('div capture')} onClick={() => LogUtils.log('div bubble')}> +

LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}> +

+ + ); + } + Horizon.render(, container); + const a = container.querySelector('button'); + a.click(); + expect(LogUtils.getAndClear()).toEqual([ + // 从外到内先捕获再冒泡 + 'div capture', + 'p capture', + 'btn capture', + 'btn bubble', + 'p bubble', + 'div bubble' + ]); + }) + + it('returns 0', () => { + let keyCode = null; + const node = Horizon.render( + { + keyCode = e.keyCode; + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keypress', { + keyCode: 65, + bubbles: true, + cancelable: true, + }), + ); + expect(keyCode).toBe(65); + }); + + it('阻止事件冒泡', () => { + const App = () => { + return ( + <> +
LogUtils.log('div capture')} onClick={() => LogUtils.log('div bubble')}> +

LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}> +

+ + ); + } + Horizon.render(, container); + container.querySelector('button').click(); + + expect(LogUtils.getAndClear()).toEqual([ + // 到button时停止冒泡 + 'div capture', + 'p capture', + 'btn capture', + 'btn bubble' + ]); + }) + + it('阻止事件捕获', () => { + const App = () => { + return ( + <> +
TestUtils.stopBubbleOrCapture(e, 'div capture')} onClick={() => LogUtils.log('div bubble')}> +

LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}> +

+ + ); + } + Horizon.render(, container); + container.querySelector('button').click(); + + expect(LogUtils.getAndClear()).toEqual([ + // 阻止捕获,不再继续向下执行 + 'div capture' + ]); + }) + + it('阻止原生事件冒泡', () => { + const App = () => { + return ( +
+

+

+ ); + } + Horizon.render(, container); + container.querySelector('div').addEventListener('click', () => { + LogUtils.log('div bubble'); + }, false); + container.querySelector('p').addEventListener('click', () => { + LogUtils.log('p bubble'); + }, false); + container.querySelector('button').addEventListener('click', (e) => { + LogUtils.log('btn bubble'); + e.stopPropagation(); + }, false); + container.querySelector('button').click(); + expect(LogUtils.getAndClear()).toEqual([ + 'btn bubble' + ]); + }) +}) diff --git a/scripts/__tests__/EventTest/FocusEvent.test.js b/scripts/__tests__/EventTest/FocusEvent.test.js new file mode 100644 index 00000000..ee5e3c46 --- /dev/null +++ b/scripts/__tests__/EventTest/FocusEvent.test.js @@ -0,0 +1,46 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; +import { act } from '../jest/customMatcher'; + +describe('合成焦点事件', () => { + + it('onFocus', () => { + const realNode = Horizon.render( + LogUtils.log(`onFocus: ${event.type}`)} + onFocusCapture={event => LogUtils.log(`onFocusCapture: ${event.type}`)} + />, container); + + realNode.dispatchEvent( + new FocusEvent('focusin', { + bubbles: true, + cancelable: false, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onFocusCapture: focus', + 'onFocus: focus', + ]); + }); + + it('onBlur', () => { + const realNode = Horizon.render( + LogUtils.log(`onBlur: ${event.type}`)} + onBlurCapture={event => LogUtils.log(`onBlurCapture: ${event.type}`)} + />, container); + + realNode.dispatchEvent( + new FocusEvent('focusout', { + bubbles: true, + cancelable: false, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onBlurCapture: blur', + 'onBlur: blur', + ]); + }) +}) \ No newline at end of file diff --git a/scripts/__tests__/EventTest/KeyboardEvent.test.js b/scripts/__tests__/EventTest/KeyboardEvent.test.js new file mode 100644 index 00000000..519cc9ce --- /dev/null +++ b/scripts/__tests__/EventTest/KeyboardEvent.test.js @@ -0,0 +1,179 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; + +describe('Keyboard Event', () => { + + it('keydown,keypress,keyup的keycode,charcode', () => { + const node = Horizon.render( + { + LogUtils.log('onKeyUp: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); + }} + onKeyDown={(e) => { + LogUtils.log('onKeyDown: keycode: ' + e.keyCode + ',charcode: ' + e.charCode) + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keydown', { + keyCode: 50, + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + node.dispatchEvent( + new KeyboardEvent('keyup', { + keyCode: 50, + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onKeyDown: keycode: 50,charcode: 0', + 'onKeyUp: keycode: 50,charcode: 0' + ]); + }); + + it('keypress的keycode,charcode', () => { + const node = Horizon.render( + { + LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keypress', { + charCode: 50, + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onKeyPress: keycode: 0,charcode: 50' + ]); + }); + + it('当charcode为13,且不设置keycode的时候', () => { + const node = Horizon.render( + { + LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keypress', { + charCode: 13, + bubbles: true, + cancelable: true, + }), + ); + expect(LogUtils.getAndClear()).toEqual([ + 'onKeyPress: keycode: 0,charcode: 13' + ]); + }); + + it('keydown,keypress,keyup的code', () => { + const node = Horizon.render( + { + LogUtils.log('onKeyUp: code: ' + e.code); + }} + onKeyPress={(e) => { + LogUtils.log('onKeyPress: code: ' + e.code); + }} + onKeyDown={(e) => { + LogUtils.log('onKeyDown: code: ' + e.code); + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keydown', { + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + node.dispatchEvent( + new KeyboardEvent('keypress', { + keyCode: 50, + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + node.dispatchEvent( + new KeyboardEvent('keyup', { + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onKeyDown: code: Digit2', + 'onKeyPress: code: Digit2', + 'onKeyUp: code: Digit2' + ]); + }); + + it('可以执行preventDefault和 stopPropagation', () => { + const keyboardProcessing = e => { + expect(e.isDefaultPrevented()).toBe(false); + e.preventDefault(); + expect(e.isDefaultPrevented()).toBe(true); + + expect(e.isPropagationStopped()).toBe(false); + e.stopPropagation(); + expect(e.isPropagationStopped()).toBe(true); + LogUtils.log(e.type + ' handle'); + }; + const div = Horizon.render( +
, + container, + ); + + div.dispatchEvent( + new KeyboardEvent('keydown', { + keyCode: 40, + bubbles: true, + cancelable: true, + }), + ); + div.dispatchEvent( + new KeyboardEvent('keyup', { + keyCode: 40, + bubbles: true, + cancelable: true, + }), + ); + div.dispatchEvent( + new KeyboardEvent('keypress', { + charCode: 40, + bubbles: true, + cancelable: true, + }), + ); + expect(LogUtils.getAndClear()).toEqual([ + 'keydown handle', + 'keyup handle', + 'keypress handle' + ]); + }); +}); diff --git a/scripts/__tests__/EventTest/MouseEvent.test.js b/scripts/__tests__/EventTest/MouseEvent.test.js new file mode 100644 index 00000000..e5ffbae1 --- /dev/null +++ b/scripts/__tests__/EventTest/MouseEvent.test.js @@ -0,0 +1,160 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; + +describe('MouseEvent Test', () => { + describe('onClick Test', () => { + it('绑定this', () => { + class App extends Horizon.Component { + constructor(props) { + super(props); + this.state = { + num: this.props.num, + price: this.props.price + }; + } + + setNum() { + this.setState( + { + num: this.state.num + 1 + } + ) + } + + setPrice = (e) => { + this.setState( + { + num: this.state.price + 1 + } + ) + } + + render() { + return ( + <> +

{this.state.num}

+

{this.state.price}

+ + + + ); + } + } + Horizon.render(, container); + expect(container.querySelector('p').innerHTML).toBe('0'); + expect(container.querySelector('#p').innerHTML).toBe('100'); + // 点击按钮触发num加1 + container.querySelector('button').click(); + expect(container.querySelector('p').innerHTML).toBe('1'); + + container.querySelector('#btn').click(); + expect(container.querySelector('p').innerHTML).toBe('101'); + }); + + it('点击触发', () => { + const handleClick = jest.fn(); + Horizon.render(, container) + container.querySelector('button').click(); + expect(handleClick).toHaveBeenCalledTimes(1); + for (let i = 0; i < 5; i++) { + container.querySelector('button').click(); + } + expect(handleClick).toHaveBeenCalledTimes(6); + }) + }) + + const test = (name, config) => { + const node = Horizon.render(config, container); + let event = new MouseEvent(name, { + relatedTarget: null, + bubbles: true, + screenX: 1 + }); + node.dispatchEvent(event); + + expect(LogUtils.getAndClear()).toEqual([ + `${name} capture`, + `${name} bubble` + ]); + + event = new MouseEvent(name, { + relatedTarget: null, + bubbles: true, + screenX: 2 + }); + node.dispatchEvent(event); + + // 再次触发新事件 + expect(LogUtils.getAndClear()).toEqual([ + `${name} capture`, + `${name} bubble` + ]); + } + + describe('合成鼠标事件', () => { + it('onMouseMove', () => { + const onMouseMove = () => { + LogUtils.log('mousemove bubble'); + }; + const onMouseMoveCapture = () => { + LogUtils.log('mousemove capture'); + }; + test('mousemove',
) + }); + + it('onMouseDown', () => { + const onMousedown = () => { + LogUtils.log('mousedown bubble'); + }; + const onMousedownCapture = () => { + LogUtils.log('mousedown capture'); + }; + test('mousedown',
) + }); + + it('onMouseUp', () => { + const onMouseUp = () => { + LogUtils.log('mouseup bubble'); + }; + const onMouseUpCapture = () => { + LogUtils.log('mouseup capture'); + }; + test('mouseup',
) + }); + + it('onMouseOut', () => { + const onMouseOut = () => { + LogUtils.log('mouseout bubble'); + }; + const onMouseOutCapture = () => { + LogUtils.log('mouseout capture'); + }; + test('mouseout',
) + }); + + it('onMouseOver', () => { + const onMouseOver = () => { + LogUtils.log('mouseover bubble'); + }; + const onMouseOverCapture = () => { + LogUtils.log('mouseover capture'); + }; + test('mouseover',
) + }); + }) +}) diff --git a/scripts/__tests__/EventTest/WheelEvent.test.js b/scripts/__tests__/EventTest/WheelEvent.test.js new file mode 100644 index 00000000..a0ebf933 --- /dev/null +++ b/scripts/__tests__/EventTest/WheelEvent.test.js @@ -0,0 +1,52 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; + +describe('合成滚轮事件', () => { + it('onWheel', () => { + const realNode = Horizon.render( +
LogUtils.log(`onWheel: ${event.type}`)} + onWheelCapture={event => LogUtils.log(`onWheelCapture: ${event.type}`)} + />, container); + + realNode.dispatchEvent( + new MouseEvent('wheel', { + bubbles: true, + cancelable: false, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onWheelCapture: wheel', + 'onWheel: wheel' + ]); + }); + + it('可以执行preventDefault和 stopPropagation', () => { + const eventHandler = e => { + expect(e.isDefaultPrevented()).toBe(false); + e.preventDefault(); + expect(e.isDefaultPrevented()).toBe(true); + + expect(e.isPropagationStopped()).toBe(false); + e.stopPropagation(); + expect(e.isPropagationStopped()).toBe(true); + LogUtils.log(e.type + ' handle'); + }; + const realNode = Horizon.render( +
, + container + ); + + realNode.dispatchEvent( + new MouseEvent('wheel', { + bubbles: true, + cancelable: true, + }), + ); + expect(LogUtils.getAndClear()).toEqual([ + 'wheel handle' + ]); + }); + +}) \ No newline at end of file diff --git a/scripts/__tests__/jest/testUtils.js b/scripts/__tests__/jest/testUtils.js new file mode 100644 index 00000000..8bab93d9 --- /dev/null +++ b/scripts/__tests__/jest/testUtils.js @@ -0,0 +1,26 @@ +import { allDelegatedNativeEvents } from '../../../libs/horizon/src/event/EventCollection'; +import * as LogUtils from './logUtils'; + +export const stopBubbleOrCapture = (e, value) => { + LogUtils.log(value) + e.stopPropagation(); +}; + +export const getEventListeners = (dom) => { + let ret = true + let keyArray = []; + for (var key in dom) { + keyArray.push(key); + } + try { + allDelegatedNativeEvents.forEach(event => { + if (!keyArray.includes(event)) { + ret = false; + throw new Error('没有挂载全量事件'); + } + }) + } catch (error) { + + } + return ret; +}; \ No newline at end of file From 867cb48365e5fd88cbe488d8040b6e178b98e40d Mon Sep 17 00:00:00 2001 From: * <8> Date: Mon, 28 Mar 2022 20:10:21 +0800 Subject: [PATCH 11/13] Match-id-2a7374e4246145afb0681f473ee89cd06e492eb7 --- libs/horizon/src/renderer/ContextSaver.ts | 68 ++---------- .../components/context/CompatibleContext.ts | 101 ------------------ .../src/renderer/render/BaseComponent.ts | 7 -- .../src/renderer/render/ClassComponent.ts | 27 +---- .../src/renderer/render/FunctionComponent.ts | 8 +- .../render/IncompleteClassComponent.ts | 16 +-- .../src/renderer/render/SuspenseComponent.ts | 2 + libs/horizon/src/renderer/render/TreeRoot.ts | 2 - libs/horizon/src/renderer/vnode/VNodeFlags.ts | 1 + 9 files changed, 16 insertions(+), 216 deletions(-) delete mode 100644 libs/horizon/src/renderer/components/context/CompatibleContext.ts diff --git a/libs/horizon/src/renderer/ContextSaver.ts b/libs/horizon/src/renderer/ContextSaver.ts index 62586895..ef243cce 100644 --- a/libs/horizon/src/renderer/ContextSaver.ts +++ b/libs/horizon/src/renderer/ContextSaver.ts @@ -3,11 +3,11 @@ * 在深度遍历过程中,begin阶段会修改一些全局的值,在complete阶段会恢复。 */ -import type {VNode, ContextType} from './Types'; -import type {Container} from '../dom/DOMOperator'; +import type { VNode, ContextType } from './Types'; +import type { Container } from '../dom/DOMOperator'; -import {getNSCtx} from '../dom/DOMOperator'; -import {ContextProvider} from './vnode/VNodeTags'; +import { getNSCtx } from '../dom/DOMOperator'; +import { ContextProvider } from './vnode/VNodeTags'; // 保存的是“http://www.w3.org/1999/xhtml”或“http://www.w3.org/2000/svg”, // 用于识别是使用document.createElement()还是使用document.createElementNS()创建DOM @@ -18,16 +18,8 @@ const CTX_CONTEXT = 'CTX_CONTEXT'; // 旧版context API,是否更改。 const CTX_OLD_CHANGE = 'CTX_OLD_CHANGE'; -// 旧版context API,保存的是的当前组件提供给子组件使用的context。 -const CTX_OLD_CONTEXT = 'CTX_OLD_CONTEXT'; -// 旧版context API,保存的是的上一个提供者提供给后代组件使用的context。 -const CTX_OLD_PREVIOUS_CONTEXT = 'CTX_OLD_PREVIOUS_CONTEXT'; - -let ctxNamespace: string = ''; - -let ctxOldContext: Object = {}; -let ctxOldChange: Boolean = false; -let ctxOldPreviousContext: Object = {}; +let ctxOldChange = false; +let ctxNamespace = ''; function setContext(vNode: VNode, contextName, value) { if (vNode.contexts === null) { @@ -38,6 +30,7 @@ function setContext(vNode: VNode, contextName, value) { vNode.contexts[contextName] = value; } } + function getContext(vNode: VNode, contextName) { if (vNode.contexts !== null) { return vNode.contexts[contextName]; @@ -87,44 +80,6 @@ function recoverParentsContextCtx(vNode: VNode) { } } -// ctxOldContext是 旧context提供者的context -function setVNodeOldContext(providerVNode: VNode, context: Object) { - setContext(providerVNode, CTX_OLD_CONTEXT, context); -} - -function getVNodeOldContext(vNode: VNode) { - return getContext(vNode, CTX_OLD_CONTEXT); -} - -function setOldContextCtx(providerVNode: VNode, context: Object) { - setVNodeOldContext(providerVNode, context); - ctxOldContext = context; -} - -function getOldContextCtx() { - return ctxOldContext; -} - -function resetOldContextCtx(vNode: VNode) { - ctxOldContext = getVNodeOldContext(vNode); -} - -function setVNodeOldPreviousContext(providerVNode: VNode, context: Object) { - setContext(providerVNode, CTX_OLD_PREVIOUS_CONTEXT, context); -} - -function getVNodeOldPreviousContext(vNode: VNode) { - return getContext(vNode, CTX_OLD_PREVIOUS_CONTEXT); -} - -function setOldPreviousContextCtx(context: Object) { - ctxOldPreviousContext = context; -} - -function getOldPreviousContextCtx() { - return ctxOldPreviousContext; -} - function setContextChangeCtx(providerVNode: VNode, didChange: boolean) { setContext(providerVNode, CTX_OLD_CHANGE, didChange); ctxOldChange = didChange; @@ -145,18 +100,9 @@ export { setContextCtx, resetContextCtx, recoverParentsContextCtx, - setOldContextCtx, - getOldContextCtx, - resetOldContextCtx, setContextChangeCtx, getContextChangeCtx, resetContextChangeCtx, - setOldPreviousContextCtx, - getOldPreviousContextCtx, - setVNodeOldContext, - getVNodeOldContext, - setVNodeOldPreviousContext, - getVNodeOldPreviousContext, }; diff --git a/libs/horizon/src/renderer/components/context/CompatibleContext.ts b/libs/horizon/src/renderer/components/context/CompatibleContext.ts deleted file mode 100644 index 0d6a4bd8..00000000 --- a/libs/horizon/src/renderer/components/context/CompatibleContext.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type {VNode} from '../../Types'; - -import { - setOldContextCtx, - setContextChangeCtx, - getOldContextCtx, - resetOldContextCtx, - resetContextChangeCtx, - setOldPreviousContextCtx, - getOldPreviousContextCtx, - setVNodeOldContext, - getVNodeOldContext, - setVNodeOldPreviousContext, - getVNodeOldPreviousContext, -} from '../../ContextSaver'; - -const emptyObject = {}; - -// 判断是否是过时的context的提供者 -export function isOldProvider(comp: Function): boolean { - // @ts-ignore - const childContextTypes = comp.childContextTypes; - return childContextTypes !== null && childContextTypes !== undefined; -} - -// 判断是否是过时的context的消费者 -export function isOldConsumer(comp: Function): boolean { - // @ts-ignore - const contextTypes = comp.contextTypes; - return contextTypes !== null && contextTypes !== undefined; -} - -// 如果是旧版context提供者,则缓存两个全局变量,上一个提供者提供的context和当前提供者提供的context -export function cacheOldCtx(processing: VNode, hasOldContext: any): void { - // 每一个context提供者都会更新ctxOldContext - if (hasOldContext) { - setOldPreviousContextCtx(getOldContextCtx()); - - const vNodeContext = getVNodeOldContext(processing) || emptyObject; - setOldContextCtx(processing, vNodeContext); - } -} - -// 获取当前组件可以消费的context -export function getOldContext(processing: VNode, clazz: Function, ifProvider: boolean) { - const type = processing.type; - // 不是context消费者, 则直接返回空对象 - if (!isOldConsumer(type)) { - return emptyObject; - } - - // 当组件既是提供者,也是消费者时,取上一个context,不能直接取最新context,因为已经被更新为当前组件的context; - // 当组件只是消费者时,则取最新context - const parentContext = (ifProvider && isOldProvider(clazz)) ? - getOldPreviousContextCtx() : - getOldContextCtx(); - - // 除非父级context更改,否则不需要重新创建子context,直接取对应节点上存的。 - if (getVNodeOldPreviousContext(processing) === parentContext) { - return getVNodeOldContext(processing); - } - - // 从父的context中取出子定义的context - const context = {}; - for (const key in type.contextTypes) { - context[key] = parentContext[key]; - } - - // 缓存当前组件的context,最近祖先传递下来context,当前可消费的context - setVNodeOldPreviousContext(processing, parentContext); - setVNodeOldContext(processing, context); - - return context; -} - -// 重置context -export function resetOldCtx(vNode: VNode): void { - resetOldContextCtx(vNode); - resetContextChangeCtx(vNode); -} - -// 当前组件是提供者,则需要合并祖先context和当前组件提供的context -function handleContext(vNode: VNode, parentContext: Object): Object { - const instance = vNode.realNode; - - if (typeof instance.getChildContext !== 'function') { - return parentContext; - } - - // 合并祖先提供的context和当前组件提供的context - return {...parentContext, ...instance.getChildContext()}; -} - -// 当前组件是context提供者,更新时,需要合并祖先context和当前组件提供的context -export function updateOldContext(vNode: VNode): void { - const ctx = handleContext(vNode, getOldPreviousContextCtx()); - // 更新context,给子组件用的context - setOldContextCtx(vNode, ctx); - // 标记更改 - setContextChangeCtx(vNode, true); -} diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index c94d815d..94e5ecf4 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -1,8 +1,6 @@ import type { VNode } from '../Types'; -import {cacheOldCtx, isOldProvider} from '../components/context/CompatibleContext'; import { - ClassComponent, ContextProvider, DomComponent, DomPortal, @@ -23,11 +21,6 @@ function handlerContext(processing: VNode) { case DomComponent: setNamespaceCtx(processing); break; - case ClassComponent: { - const isOldCxtExist = isOldProvider(processing.type); - cacheOldCtx(processing, isOldCxtExist); - break; - } case DomPortal: setNamespaceCtx(processing, processing.realNode); break; diff --git a/libs/horizon/src/renderer/render/ClassComponent.ts b/libs/horizon/src/renderer/render/ClassComponent.ts index 6db4dcf2..002e343d 100644 --- a/libs/horizon/src/renderer/render/ClassComponent.ts +++ b/libs/horizon/src/renderer/render/ClassComponent.ts @@ -2,13 +2,6 @@ import type { VNode } from '../Types'; import { mergeDefaultProps } from './LazyComponent'; import { getNewContext, resetDepContexts } from '../components/context/Context'; -import { - cacheOldCtx, - getOldContext, - isOldProvider, - resetOldCtx, - updateOldContext, -} from '../components/context/CompatibleContext'; import { callComponentWillMount, callComponentWillReceiveProps, @@ -25,17 +18,18 @@ import { markRef } from './BaseComponent'; import { processUpdates, } from '../UpdateHandler'; -import { getContextChangeCtx, setContextChangeCtx } from '../ContextSaver'; +import { getContextChangeCtx } from '../ContextSaver'; import { setProcessingClassVNode } from '../GlobalVar'; import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; +const emptyContextObj = {}; // 获取当前节点的context export function getCurrentContext(clazz, processing: VNode) { const context = clazz.contextType; return typeof context === 'object' && context !== null ? getNewContext(processing, context) - : getOldContext(processing, clazz, true); + : emptyContextObj; } // 挂载实例 @@ -112,8 +106,6 @@ export function captureRender(processing: VNode): VNode | null { clazz = clazz._load(clazz._content); } } - const isOldCxtExist = isOldProvider(clazz); - cacheOldCtx(processing, isOldCxtExist); resetDepContexts(processing); @@ -170,24 +162,13 @@ export function captureRender(processing: VNode): VNode | null { // 不复用 if (shouldUpdate) { - // 更新context - if (isOldCxtExist) { - updateOldContext(processing); - } return createChildren(clazz, processing); } else { - if (isOldCxtExist) { - setContextChangeCtx(processing, false); - } return onlyUpdateChildVNodes(processing); } } -export function bubbleRender(processing: VNode) { - if (isOldProvider(processing.type)) { - resetOldCtx(processing); - } -} +export function bubbleRender(processing: VNode) {} // 用于未完成的类组件 export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object): VNode | null { diff --git a/libs/horizon/src/renderer/render/FunctionComponent.ts b/libs/horizon/src/renderer/render/FunctionComponent.ts index 0575257a..af634fe6 100644 --- a/libs/horizon/src/renderer/render/FunctionComponent.ts +++ b/libs/horizon/src/renderer/render/FunctionComponent.ts @@ -1,7 +1,6 @@ import type {VNode} from '../Types'; import {mergeDefaultProps} from './LazyComponent'; -import {getOldContext} from '../components/context/CompatibleContext'; import {resetDepContexts} from '../components/context/Context'; import {exeFunctionHook} from '../hooks/HookMain'; import {ForwardRef} from '../vnode/VNodeTags'; @@ -54,11 +53,6 @@ export function captureFunctionComponent( nextProps: any, shouldUpdate?: boolean ) { - let context; - if (processing.tag !== ForwardRef) { - context = getOldContext(processing, funcComp, true); - } - resetDepContexts(processing); const isCanReuse = checkIfCanReuseChildren(processing, shouldUpdate); @@ -68,7 +62,7 @@ export function captureFunctionComponent( const newElements = exeFunctionHook( processing.tag === ForwardRef ? funcComp.render : funcComp, nextProps, - processing.tag === ForwardRef ? processing.ref : context, + processing.tag === ForwardRef ? processing.ref : undefined, processing, ); diff --git a/libs/horizon/src/renderer/render/IncompleteClassComponent.ts b/libs/horizon/src/renderer/render/IncompleteClassComponent.ts index 2c50ed18..8e02df51 100644 --- a/libs/horizon/src/renderer/render/IncompleteClassComponent.ts +++ b/libs/horizon/src/renderer/render/IncompleteClassComponent.ts @@ -4,18 +4,10 @@ import {mergeDefaultProps} from './LazyComponent'; import {ClassComponent} from '../vnode/VNodeTags'; import {resetDepContexts} from '../components/context/Context'; import {getIncompleteClassComponent} from './ClassComponent'; -import { - isOldProvider, - resetOldCtx, - cacheOldCtx, -} from '../components/context/CompatibleContext'; function captureIncompleteClassComponent(processing, Component, nextProps) { processing.tag = ClassComponent; - const hasOldContext = isOldProvider(Component); - cacheOldCtx(processing, hasOldContext); - resetDepContexts(processing); return getIncompleteClassComponent(Component, processing, nextProps); @@ -32,10 +24,4 @@ export function captureRender(processing: VNode): VNode | null { return captureIncompleteClassComponent(processing, Component, resolvedProps); } -export function bubbleRender(processing: VNode) { - // 处理与类组件相同。 - const Component = processing.type; - if (isOldProvider(Component)) { - resetOldCtx(processing); - } -} +export function bubbleRender(processing: VNode) {} diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index 3e7bdcb2..28b2c59b 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -92,9 +92,11 @@ export function captureSuspenseComponent(processing: VNode) { if (showFallback) { processing.suspenseDidCapture = false; const nextFallbackChildren = nextProps.fallback; + debugger return createFallback(processing, nextFallbackChildren); } else { const newChildren = nextProps.children; + debugger return createSuspenseChildren(processing, newChildren); } } diff --git a/libs/horizon/src/renderer/render/TreeRoot.ts b/libs/horizon/src/renderer/render/TreeRoot.ts index 33a28402..949dc89b 100644 --- a/libs/horizon/src/renderer/render/TreeRoot.ts +++ b/libs/horizon/src/renderer/render/TreeRoot.ts @@ -2,13 +2,11 @@ import type {VNode} from '../Types'; import {throwIfTrue} from '../utils/throwIfTrue'; import {processUpdates} from '../UpdateHandler'; import {resetNamespaceCtx, setNamespaceCtx} from '../ContextSaver'; -import {resetOldCtx} from '../components/context/CompatibleContext'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; export function bubbleRender(processing: VNode) { resetNamespaceCtx(processing); - resetOldCtx(processing); } function updateTreeRoot(processing) { diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 91f59d35..5b72b354 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -41,6 +41,7 @@ export class FlagUtils { node.flags |= Addition; } static setAddition(node: VNode) { + console.log('set addition', node.flags); node.flags = Addition; } From 0b56fb410484204fd1aa4fdc05c363e5ccc6ebaa Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 15:49:54 +0800 Subject: [PATCH 12/13] Match-id-07aa005631c43e03458d2e692810427c7c1cfb59 --- .../src/renderer/render/FunctionComponent.ts | 29 ++++++++++++------- .../src/renderer/render/SuspenseComponent.ts | 9 +++--- libs/horizon/src/renderer/vnode/VNode.ts | 2 +- libs/horizon/src/renderer/vnode/VNodeFlags.ts | 3 +- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/libs/horizon/src/renderer/render/FunctionComponent.ts b/libs/horizon/src/renderer/render/FunctionComponent.ts index af634fe6..a0fba76a 100644 --- a/libs/horizon/src/renderer/render/FunctionComponent.ts +++ b/libs/horizon/src/renderer/render/FunctionComponent.ts @@ -1,18 +1,19 @@ -import type {VNode} from '../Types'; +import type { VNode } from '../Types'; -import {mergeDefaultProps} from './LazyComponent'; -import {resetDepContexts} from '../components/context/Context'; -import {exeFunctionHook} from '../hooks/HookMain'; -import {ForwardRef} from '../vnode/VNodeTags'; -import {FlagUtils, Update} from '../vnode/VNodeFlags'; -import {getContextChangeCtx} from '../ContextSaver'; -import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; +import { mergeDefaultProps } from './LazyComponent'; +import { resetDepContexts } from '../components/context/Context'; +import { exeFunctionHook } from '../hooks/HookMain'; +import { ForwardRef } from '../vnode/VNodeTags'; +import { FlagUtils, Update } from '../vnode/VNodeFlags'; +import { getContextChangeCtx } from '../ContextSaver'; +import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; // 在useState, useReducer的时候,会触发state变化 let stateChange = false; -export function bubbleRender() {} +export function bubbleRender() { +} // 判断children是否可以复用 function checkIfCanReuseChildren(processing: VNode, shouldUpdate?: boolean) { @@ -51,8 +52,14 @@ export function captureFunctionComponent( processing: VNode, funcComp: any, nextProps: any, - shouldUpdate?: boolean + shouldUpdate?: boolean, ) { + if (processing.isSuspended) { + processing.isCreated = true; + processing.isSuspended = false; + + FlagUtils.markAddition(processing); + } resetDepContexts(processing); const isCanReuse = checkIfCanReuseChildren(processing, shouldUpdate); @@ -89,7 +96,7 @@ export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode processing, Component, resolvedProps, - shouldUpdate + shouldUpdate, ); } diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index 28b2c59b..0686dc3f 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -4,6 +4,7 @@ import {FlagUtils, Interrupted} from '../vnode/VNodeFlags'; import {onlyUpdateChildVNodes, updateVNode, createFragmentVNode} from '../vnode/VNodeCreator'; import { ClassComponent, + FunctionComponent, IncompleteClassComponent, SuspenseComponent, } from '../vnode/VNodeTags'; @@ -21,7 +22,7 @@ export enum SuspenseChildStatus { // 创建fallback子节点 function createFallback(processing: VNode, fallbackChildren) { - const childFragment: VNode = processing.child; + const childFragment: VNode = processing.child!; let fallbackFragment; childFragment.childShouldUpdate = false; @@ -92,11 +93,9 @@ export function captureSuspenseComponent(processing: VNode) { if (showFallback) { processing.suspenseDidCapture = false; const nextFallbackChildren = nextProps.fallback; - debugger return createFallback(processing, nextFallbackChildren); } else { const newChildren = nextProps.children; - debugger return createSuspenseChildren(processing, newChildren); } } @@ -187,6 +186,9 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, } } + if(processing.tag === FunctionComponent) { + processing.isSuspended = true; + } // 应该抛出promise未完成更新,标志待更新 processing.shouldUpdate = true; @@ -225,7 +227,6 @@ export function listenToPromise(suspenseVNode: VNode) { // 记录已经监听的 promise let promiseCache = suspenseVNode.realNode; if (promiseCache === null) { - // @ts-ignore promiseCache = new PossiblyWeakSet(); suspenseVNode.realNode = new PossiblyWeakSet(); } diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 6386fd98..8c98e673 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -30,7 +30,7 @@ export class VNode { updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组 stateCallbacks: any[] | null; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组 isForceUpdate: boolean; // 是否使用强制更新 - + isSuspended = false; // 是否被suspense打断更新 state: any; // ClassComponent和TreeRoot的状态 hooks: Array> | null; // 保存hook suspenseChildStatus = ''; // Suspense的Children是否显示 diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 5b72b354..bb811c82 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -2,7 +2,7 @@ * vNode结构的变化标志 */ -import type { VNode } from '../Types'; +import type { VNode } from './VNode'; export const InitFlag = /** */ 0; @@ -41,7 +41,6 @@ export class FlagUtils { node.flags |= Addition; } static setAddition(node: VNode) { - console.log('set addition', node.flags); node.flags = Addition; } From bc59541e0c692fe037a7b47e0d9c34f8e74b7163 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 16:07:21 +0800 Subject: [PATCH 13/13] Match-id-6e5a892ace868547d76377206bd2654e93dfc333 --- libs/horizon/src/renderer/render/FunctionComponent.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/horizon/src/renderer/render/FunctionComponent.ts b/libs/horizon/src/renderer/render/FunctionComponent.ts index a0fba76a..8191293b 100644 --- a/libs/horizon/src/renderer/render/FunctionComponent.ts +++ b/libs/horizon/src/renderer/render/FunctionComponent.ts @@ -54,11 +54,13 @@ export function captureFunctionComponent( nextProps: any, shouldUpdate?: boolean, ) { + // 函数组件内已完成异步动作 if (processing.isSuspended) { + // 由于首次被打断,应仍为首次渲染 processing.isCreated = true; - processing.isSuspended = false; - FlagUtils.markAddition(processing); + + processing.isSuspended = false; } resetDepContexts(processing);