在 Next.js 中,我们经常会需要引入外部的 JavaScript 库,比如 jQuery、React、Ant Design 或是自己开发的组件库。但是,由于 Next.js 的服务器渲染和客户端渲染的特殊性,引入外部 JS 库可能会遇到一些问题。本文将介绍如何解决 Next.js 引入外部 JS 库遇到的问题,以及如何优化性能。
在客户端引入外部 JS 库
在客户端引入外部 JS 库是比较简单的,只需要像普通网页一样,在 HTML 文件或 JSX 文件中通过 script 标签引入即可。比如:
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
或是在 JSX 文件中使用 next/head 包装起来,比如:
import Head from 'next/head'; function Home() { return ( <> <Head> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> </Head> <div>Hello, World!</div> </> ); } export default Home;
需要注意的是,如果引入的 JS 库依赖于全局变量,比如 jQuery 依赖于 $ 变量,那么需要在组件中使用 window 对象绑定全局变量,具体做法为:
import Head from 'next/head'; function Home() { useEffect(() => { window.$ = window.jQuery = require('jquery'); }, []); return ( <> <Head> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> </Head> <div>Hello, World!</div> </> ); } export default Home;
这样,jQuery 在客户端就可以正常使用了。
在服务器端引入外部 JS 库
在服务器端渲染时,我们不能直接在 HTML 文件中引入 JS 库,因为这样会使得服务器端和客户端渲染的 HTML 不一致,导致部分功能出现问题。
解决的方法是,在服务器端使用 next/head 包装起来,通过 next/script 组件在客户端引入。比如:
import Head from 'next/head'; import Script from 'next/script'; function Home() { return ( <> <Head> <title>Next.js Example</title> </Head> <Script src="https://code.jquery.com/jquery-3.5.1.min.js"></Script> <div>Hello, World!</div> </> ); } export default Home;
需要注意的是,由于 next/script 组件是在 Head 标签内部,所以在使用 window 对象绑定全局变量时,需要等待客户端加载完成后再绑定,具体做法为:
import Head from 'next/head'; import Script from 'next/script'; function Home() { useEffect(() => { const loadJquery = () => { window.$ = window.jQuery = require('jquery'); }; if (window.$ === undefined) { window.addEventListener('load', loadJquery); } else { loadJquery(); } return () => window.removeEventListener('load', loadJquery); }, []); return ( <> <Head> <title>Next.js Example</title> </Head> <Script src="https://code.jquery.com/jquery-3.5.1.min.js"></Script> <div>Hello, World!</div> </> ); } export default Home;
这样,在客户端加载完成后,jQuery 就可以正常使用了。
优化性能
当引入的 JS 库比较大时,会影响页面加载速度。为了优化性能,可以使用动态引入的方式,在需要使用的组件中再引入 JS 库。
比如,我们可以创建一个自定义的 next/head 组件,暴露一个 addScript 方法,用于动态引入 JS 库。代码如下:
import Head from 'next/head'; const ScriptSrcMap = new Map(); function HeadWithScripts(props) { const { children } = props; const addScript = (src) => { if (ScriptSrcMap.has(src)) { return; } ScriptSrcMap.set(src, true); const script = document.createElement('script'); script.src = src; script.async = true; script.defer = true; document.head.appendChild(script); }; const getHeadChild = (child) => { if (child.type === 'script') { const { src } = child.props; if (src) { addScript(src); } } if (child.props && child.props.children) { return { ...child, props: { ...child.props, children: React.Children.map(child.props.children, getHeadChild), }, }; } return child; }; return <Head {...props}>{React.Children.map(children, getHeadChild)}</Head>; } export default HeadWithScripts;
然后,在需要使用的组件中,使用这个自定义的 Head 组件,动态引入 JS 库。比如:
import HeadWithScripts from '../components/HeadWithScripts'; function Home() { return ( <> <HeadWithScripts> <title>Next.js Example</title> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> </HeadWithScripts> <div>Hello, World!</div> <script>{'$(function() { alert("Hello, jQuery!"); });'}</script> </> ); } export default Home;
这样,在需要使用 jQuery 的组件中再引入,可以避免在整个应用中都引入,优化性能。同时,使用自定义的 Head 组件,也可以避免在每个组件中都写 window 绑定的逻辑,提高代码复用性。
总结
在 Next.js 中引入外部 JS 库需要注意服务器端和客户端渲染的差异,以及对性能的影响。通过本文介绍的方法,可以解决这些问题,并且优化性能,提高代码复用性。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a260ddadd4f0e0ffa84a7c