如何在Gatsby博客文章中使用script标签加载外部脚本
嘿,这个问题我太熟悉了——Gatsby默认的Markdown处理逻辑确实会过滤掉外部<script>标签,毕竟出于安全和渲染性能考虑嘛。不过你不想大改文章的话,有几个很实用的解决方案,刚好能适配你不同文章依赖不同版本库的需求:
方案1:自定义Remark插件(零改动文章,最推荐)
Gatsby用gatsby-transformer-remark处理Markdown,我们可以写个轻量的自定义插件,把文章里的外部脚本标签提取出来,再在页面渲染时动态加载,完全不用碰原Markdown文件。
步骤1:编写自定义Remark插件
在项目里新建src/plugins/remark-preserve-scripts/index.js,代码如下:
const visit = require('unist-util-visit'); module.exports = function attacher() { return function transformer(tree, file) { const scripts = []; // 遍历Markdown的AST树,找到所有带src的script标签 visit(tree, 'html', (node) => { const srcMatch = node.value.match(/<script src="([^"]+)"[^>]*>/); if (srcMatch) { scripts.push(srcMatch[1]); // 把原script标签替换成占位符(可选,也可以直接保留原标签) node.value = `<div class="script-loader" data-src="${srcMatch[1]}"></div>`; } }); // 把提取到的脚本列表附加到文件元数据里 file.data.scripts = scripts; }; };
步骤2:配置Gatsby使用这个插件
在gatsby-config.js里,把自定义插件加到gatsby-transformer-remark的插件列表中:
module.exports = { plugins: [ { resolve: `gatsby-transformer-remark`, options: { plugins: [ `${__dirname}/src/plugins/remark-preserve-scripts`, // 你的其他remark插件(比如prismjs、remark-images等) ], }, }, ], };
步骤3:在文章模板中动态加载脚本
打开你的博客文章模板(比如src/templates/blog-post.js),修改GraphQL查询和组件逻辑,获取脚本列表并动态加载:
import React, { useEffect } from 'react'; const BlogPost = ({ data }) => { const post = data.markdownRemark; // 从文件元数据中获取脚本列表 const scriptsToLoad = post.parent.scripts || []; useEffect(() => { scriptsToLoad.forEach(src => { // 检查脚本是否已加载,避免重复加载同版本 if (!document.querySelector(`script[src="${src}"]`)) { const script = document.createElement('script'); script.src = src; script.async = true; // 按需设置async/defer document.body.appendChild(script); } }); // 可选:页面卸载时清理脚本(避免内存泄漏) return () => { scriptsToLoad.forEach(src => { const script = document.querySelector(`script[src="${src}"]`); if (script) document.body.removeChild(script); }); }; }, [scriptsToLoad]); return ( <article> <h1>{post.frontmatter.title}</h1> <div dangerouslySetInnerHTML={{ __html: post.html }} /> </article> ); }; export const query = graphql` query BlogPostQuery($slug: String!) { markdownRemark(fields: { slug: { eq: $slug } }) { html frontmatter { title } parent { ... on File { scripts } } } } `; export default BlogPost;
这个方案完美适配你“不同文章依赖不同版本库”的需求,而且全程不用修改任何一篇Markdown文章。
方案2:改用MDX(灵活度更高,少量改动文章)
如果你能接受把Markdown文件改成MDX格式(后缀名从.md改成.mdx),可以用组件化的方式加载脚本,更符合React生态。
步骤1:配置MDX插件
先安装依赖:
npm install gatsby-plugin-mdx @mdx-js/react
然后在gatsby-config.js里添加插件:
module.exports = { plugins: [ `gatsby-plugin-mdx`, // 其他插件... ], };
步骤2:编写脚本加载组件
新建src/components/LoadScript.js:
import React, { useEffect } from 'react'; const LoadScript = ({ src }) => { useEffect(() => { const script = document.createElement('script'); script.src = src; script.async = true; document.body.appendChild(script); return () => { document.body.removeChild(script); }; }, [src]); return null; }; export default LoadScript;
步骤3:在MDX文章中引入组件
把原Markdown里的<script src="...">替换成组件引入:
import LoadScript from '../components/LoadScript'; <LoadScript src="https://code.jquery.com/jquery-3.6.0.min.js" /> <LoadScript src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js" /> <!-- 下面是你的文章内容 --> 这篇文章需要用到jQuery和Three.js演示...
这个方案虽然需要修改文章,但改动量极小,而且能更精细地控制脚本的加载和卸载。
方案3:全局加载通用脚本(仅适合固定版本的通用库)
如果你的部分文章依赖固定版本的通用库(比如某个版本的jQuery),可以用gatsby-plugin-load-script全局加载,但这个方案解决不了“不同文章不同版本”的问题,只能作为补充:
安装插件后在gatsby-config.js配置:
module.exports = { plugins: [ { resolve: `gatsby-plugin-load-script`, options: { src: `https://code.jquery.com/jquery-3.6.0.min.js`, }, }, ], };
内容的提问来源于stack exchange,提问作者chrismacp

