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

相關文章
相關標籤/搜索