解决 Next.js 引入外部 JS 库的问题

在 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


纠错反馈