css-vars-ponyfill 在ie环境下使用问题(nextjs 构建)

css-vars-ponyfill

经过css变量来实现网页换肤的过程当中,会出现兼容性问题。
在这里插入图片描述javascript

为了解决ie,qq,百度浏览器等兼容性问题,引入css-vars-ponyfill,可是在ie浏览器下,css-vars-ponyfill 的在nextjs下表现不佳,主要缺陷是因为页面是服务端渲染,所以用户在看到界面后,动态主题色等样式不能很快渲染好,而是有一个过渡的时间(css-vars-ponyfill 仅支持client-side),颜色会存在明显替换的过程用户体验差。经过阅读源码能够看到,cssVars须要等到浏览器contentLoaded以后,才会触发,不然一直监听dom的data content事件,这就致使了体验上的问题。css

解决方案

1.解析速度

经过把直接去除document.readyState !== 'loading' 这样的限制条件使得浏览器在解析到,而后更改css-vars-ponyfill 的引入方式(旧的引入方式是在nextjs中的mainjs中引入module,而后直接调用cssVars(),这样在调用到ponyfill的脚本前还会解析其余不相关的chunk,为了更快的解析css变量,须要手动选择插入位置),更改以后的css-vars-ponyfill 经过找到css变量的位置(nextjs 经过将不一样组件下的style,统一打包在header里面),而后将更改后的ponyfill 插入到style 以后进行调用,这一步选择在服务端渲染的 _document.tsx 文件中更改。java

2.解析稳定性

经过手动更改文件解析位置,以及对源码的条件触发机制进行相关更改,首页颜色渲染速度有了必定提高。可是仍存在一个问题,即经过路由跳转的界面,若是有新的style chunk,插入时不能进行有效的css变量解析(已尝试配置cssVars的option 打开MutationObserver)。
所以,解决方案是经过判断UA,来让ie等浏览器下全部的路由经过a标签跳转,触发css-ponyfill的从新解析执行。node

export function browser() { 
 
   
  const UA = window.navigator.userAgent
  if (UA.includes("qqbrowser")) return "qqbrowser"
  if (UA.includes("baidu")) return "baidu"
  if (UA.includes("Opera")) return "Opera"
  if (UA.includes("Edge")) return "Edge"
  if (UA.includes("MSIE") || (UA.includes("Trident") && UA.includes("rv:11.0")))
    return "IE"
  if (UA.includes("Firefox")) return "Firefox"
  if (UA.includes("Chrome")) return "Chrome"
  if (UA.includes("Safari")) return "Safari"
}
type CommonLinkProps = { 
 
   
    children: ReactElement
    href?: string
    target?: string
    outerLink?: boolean
    styles?: unknown
}
export default function CustomLink(props: CommonLinkProps) { 
 
   
  const { 
 
    children, href, target, as, outerLink, styles = emptyStyles } = props
  const [isIE, setIE] = useState<boolean>(false)
  const cloneEl = (c: ReactElement, props?: any) =>
    React.cloneElement(c, { 
 
    href: as ?? href, target, ...props })
  useEffect(() => { 
 
   
    if (["IE", "qqbrowser", "baidu"].includes(browser())) { 
 
   
      setIE(true)
    }
  }, [])
  function renderLink() { 
 
   
    if (Children.only(children).type === "a") { 
 
   
      const node = cloneEl(children as ReactElement)
      return node
    } else { 
 
   
      let fn: () => void | null = null
      if (outerLink) { 
 
   
        fn = () => { 
 
   
          window.open(as ?? href)
        }
      } else { 
 
   
        fn = () => { 
 
   
          window.location.href = as ?? href
        }
      }
      const node = cloneEl(children as ReactElement, { 
 
   
        onClick: () => { 
 
   
          fn()
        },
      })
      return node
    }
  }

  return (
    <>
      { 
 
   !href ? (
        children
      ) : isIE ? (
        renderLink()
      ) : (
        <Link { 
 
   ...props}>{ 
 
   children}</Link>
      )}
      <style jsx>{ 
 
   styles}</style>
    </>
  )
}

这里children的type 选择了ReactElement,而不是插槽中一般支持的ReactNode 主要是不想考虑直接插入字符串这种状况,会增长问题的复杂度,所以直接在type这层作限制。还有Fragments 也没有考虑,且没有找到有效的Fragments 类型,无法在ReactNode 中把它Omit掉,nextjs 里面的Link 若是首层插入了Fragments 后,也没法正常跳转,可能缘由也是没法再Fragments 上面绑定有效的事件吧,目前Fragments(16.13.1) 只支持key属性,但愿后续能够优化。git

本文同步分享在 博客“j_bleach”(CSDN)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。github

相关文章
相关标签/搜索