Published on

2023-1-1-前端快报

Authors
  • avatar
    Name
    noodles
    每个人的花期不同,不必在乎别人比你提前拥有

New Suspense SSR Architecture in React 18

这篇文章介绍react 18中流式渲染和Suspense的结合,实现更好的服务端渲染功能。相关文章还有浅析React 18 Streaming SSR

React Libraries for 2022

这篇文章介绍2022年热度比较高的React库

一文详解 CSS-in-JS

这篇文章从CSS规范、CSS的设计模式说起,逐步覆盖到CSS-in-JS的细节,涉及的相关背景比较多,值得仔细阅读。

What is a realm in JavaScript?

这篇文章介绍JavaScript中Realm的定义。也可以结合这篇ShadowRealms – an ECMAScript proposal for a better eval()介绍ShadowRealms API提议的文章一起了解。

Developer Roadmaps

Developer Roadmaps收集了各个开发方向的技能路线图

How we configured pnpm and Turborepo for our monorepo

一篇比较详细的介绍monorepo技术方案选择工具的文章,从多项目开发的问题开始说起,逐步介绍pnpm(monorepo管理工具)和Turborepo(打包构建工具)

工具

js-code-to-svg-flowchart

一个将JavaScript代码转化成svg图片的库。

CONSOLE NINJA

一款VSCode插件,可以直接在代码中查看输入的console调试信息

源码解读

react-wrap-balancer

react-wrap-balancer能使文案比较智能的适应容器,占据容器的空间,达到比较好的展示效果,下面从源码上看它是如何实现的: react-wrap-balancer 通过简单的demo使用可以发现balancer在页面中注入了一段脚本并且为包裹的文本内容绑定的属性标识 balancer-show
        // react-wrap-balancer的主体代码逻辑
        const Balancer: React.FC<BalancerProps> = ({
            as: Wrapper = 'span',
            ratio = 1,
            children,
            ...props
        }) => {
            // 利用useId为组件生成唯一标识,useId可以根据组件的层级结构生成唯一标识
            const id = React.useId()
            const wrapperRef = React.useRef<WrapperElement>()
            const hasProvider = React.useContext(BalancerContext)

            // 监听函数执行
            // useIsomorphicLayoutEffect这里对服务端和客户端做了区分 在服务端调用useEffect,在客户端调用useLayoutEffect 解决客户端的闪动问题
            useIsomorphicLayoutEffect(() => {
                if (wrapperRef.current) {
                // 调用绑定self上绑定的动态调整函数 并传入当前的入参执行
                // self可以指代全局的window https://developer.mozilla.org/en-US/docs/Web/API/Window/self
                // 主要计算逻辑都在relayout函数中
                ;(self[SYMBOL_KEY] = relayout)(0, ratio, wrapperRef.current)
                }
            }, [children, ratio])

            // 自己卸载的时候 终止监听函数
            useIsomorphicLayoutEffect(() => {
                return () => {
                    if (!wrapperRef.current) return

                    const resizeObserver = wrapperRef.current[SYMBOL_OBSERVER_KEY]
                    if (!resizeObserver) return

                    resizeObserver.disconnect()
                    delete wrapperRef.current[SYMBOL_OBSERVER_KEY]
                }
            }, [])

            return (
                <>
                    <Wrapper
                        {...props}
                        // 绑定的唯一标识
                        data-br={id}
                        data-brr={ratio}
                        ref={wrapperRef}
                        style={{
                        display: 'inline-block',
                        verticalAlign: 'top',
                        textDecoration: 'inherit',
                        }}
                        suppressHydrationWarning
                    >
                        {children}
                    </Wrapper>
                    {createScriptElement(hasProvider, `self.${SYMBOL_KEY}("${id}",${ratio})`)}
                </>
            )
        }

通过上面的源码分析,主要的动态计算逻辑都在relayout函数中

        const relayout: RelayoutFn = (id, ratio, wrapper) => {
            // 根据id获取包裹的wrapper组件
            wrapper =
                wrapper || document.querySelector<WrapperElement>(`[data-br="${id}"]`)
            // wrapper的父容器
            const container = wrapper.parentElement
            // 更新wrapper的最大宽度
            const update = (width: number) => (wrapper.style.maxWidth = width + 'px')

            // 重置wrapper的最大宽度
            wrapper.style.maxWidth = ''

            // 获取wrapper的父容器的宽高
            const width = container.clientWidth
            const height = container.clientHeight

            // 二分查找的方式计算wrapper的宽度
            let left: number = width / 2
            let right: number = width
            let middle: number

            if (width) {
                while (left + 1 < right) {
                    middle = ~~((left + right) / 2)
                    update(middle)
                    if (container.clientHeight === height) {
                        // 缩放后 高度没有变 那么right= middle
                        right = middle
                    } else {
                        // 否则right = middle
                        left = middle
                    }
                }

                // 更新wrapper的宽度
                update(right * ratio + width * (1 - ratio))
            }

            // 通过ResizeObserver去监听wrapper父容器的变化 执行relayout函数
            if (!wrapper['__wrap_o']) {
                ;(wrapper['__wrap_o'] = new ResizeObserver(() => {
                self.__wrap_b(0, +wrapper.dataset.brr, wrapper)
                })).observe(container)
            }
        }