正式准备入坑react
    打算从小而美的antd组件开始看 Row组件,很多新鲜的代码
    (MyUI water-fail)
    window.matchMedia
    设计思维猜想,首先从媒体的

    1. 组件内部
    2. React.useEffect(() => {
    3. //subscribe的过程相当于把方法作为一个订阅者,不过是notify时候是执行订阅者本身。
    4. const token = ResponsiveObserve.subscribe(screen => {
    5. const currentGutter = gutterRef.current || 0;
    6. if (
    7. (!Array.isArray(currentGutter) && typeof currentGutter === 'object') ||
    8. (Array.isArray(currentGutter) &&
    9. (typeof currentGutter[0] === 'object' || typeof currentGutter[1] === 'object'))
    10. ) {
    11. setScreens(screen);
    12. }
    13. });
    14. // 如果要在组件卸载前执行操作,返回值返回一个方法。
    15. return () => ResponsiveObserve.unsubscribe(token);
    16. }, []);
    1. path: components\_util\responsiveObserve.ts
    2. export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
    3. export type BreakpointMap = Record<Breakpoint, string>;
    4. export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
    5. export type ScreenSizeMap = Partial<Record<Breakpoint, number>>;
    6. export const responsiveArray: Breakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
    7. export const responsiveMap: BreakpointMap = {
    8. xs: '(max-width: 575px)',
    9. sm: '(min-width: 576px)',
    10. md: '(min-width: 768px)',
    11. lg: '(min-width: 992px)',
    12. xl: '(min-width: 1200px)',
    13. xxl: '(min-width: 1600px)',
    14. };
    15. type SubscribeFunc = (screens: ScreenMap) => void;
    16. const subscribers = new Map<Number, SubscribeFunc>();
    17. let subUid = -1;
    18. let screens = {};
    19. const responsiveObserve = {
    20. matchHandlers: {} as {
    21. [prop: string]: {
    22. mql: MediaQueryList;
    23. listener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null;
    24. };
    25. },
    26. dispatch(pointMap: ScreenMap) {
    27. screens = pointMap;
    28. subscribers.forEach(func => func(screens));
    29. return subscribers.size >= 1;
    30. },
    31. //入口,有一次注册监听器就可以,因为被监听者负责调用所有订阅者即可(所有subscribers对他可见。
    32. subscribe(func: SubscribeFunc): number {
    33. if (!subscribers.size) this.register();
    34. subUid += 1;
    35. subscribers.set(subUid, func);
    36. //因为初始化或者每次mql回调都会刷新screens,所以说后面注册的方法想要立刻有结果,就是用上次存储的screens
    37. func(screens);
    38. return subUid;
    39. },
    40. unsubscribe(token: number) {
    41. subscribers.delete(token);
    42. if (!subscribers.size) this.unregister();
    43. },
    44. unregister() {
    45. Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
    46. const matchMediaQuery = responsiveMap[screen];
    47. const handler = this.matchHandlers[matchMediaQuery];
    48. handler?.mql.removeListener(handler?.listener);
    49. });
    50. subscribers.clear();
    51. },
    52. register() {
    53. Object.keys(responsiveMap).forEach((screen: Breakpoint) => {
    54. const matchMediaQuery = responsiveMap[screen];
    55. const listener = ({ matches }: { matches: boolean }) => {
    56. //screens一开始是{},赋值语句为:screens = pointMap;经过一轮register的遍历
    57. pointMap = { ...screen, xs: false } 此时screens { xs: true }
    58. pointMap = { ...screen, sm: false } 此时screens { xs: true, sm: true}
    59. //如果后面有media触发addListener,此时screens其实是满的初始状态,payload为:
    60. pointMap = { ...screen, sm: true }
    61. this.dispatch({
    62. ...screens,
    63. [screen]: matches,
    64. });
    65. };
    66. const mql = window.matchMedia(matchMediaQuery);
    67. mql.addListener(listener);
    68. this.matchHandlers[matchMediaQuery] = {
    69. mql,
    70. listener,
    71. };
    72. //初始化时,用初始化的结果来初始化screens,但是是这样fn好像是会无意义的执行很多次。
    73. //但是setState在一个main script里重复执行,其实只执行最后一次。
    74. listener(mql);
    75. });
    76. },
    77. };
    78. export default responsiveObserve;
    1. 根据Rowgutter,计算现在的gutter
    2. const getGutter = (): [number, number] => {
    3. const results: [number, number] = [0, 0];
    4. //这里的gutter是props, responsiveArray是['xxl', 'xl', 'lg', 'md', 'sm', 'xs'],这里故意从大到小排列,因为语句都是min-width,如果大的满足则立刻生效,所以下面的循环遇到顺序第一个满足就break了。
    5. const normalizedGutter = Array.isArray(gutter) ? gutter : [gutter, 0];
    6. normalizedGutter.forEach((g, index) => {
    7. //最下一层是对象的情况 [{ xs: 12, lt: 16 }]
    8. if (typeof g === 'object') {
    9. for (let i = 0; i < responsiveArray.length; i++) {
    10. const breakpoint: Breakpoint = responsiveArray[i];
    11. if (screens[breakpoint] && g[breakpoint] !== undefined) {
    12. results[index] = g[breakpoint] as number;
    13. break;
    14. }
    15. }
    16. } else {
    17. //如果是gutter = 10, gutter = [10, 20]的形式
    18. results[index] = g || 0;
    19. }
    20. });
    21. 返回的即是当前要渲染的水平/垂直的gap
    22. return results;
    23. };