LiveRe 评论插件适配 Gatsby 网站
最佳选择 LiveRe
LiveRe 的优点:
- 商业化,成熟,稳定
- 国内外都可以正常访问
- 众多的第三方登陆:国内主流的 微信、微博、QQ、百度、豆瓣 和国外五个主流平台。
- 申请容易,对网站没有要求
LiveRe 的缺点:
- 技术支持文档少
LiveRe 控制台报错
LiveRe 官方没有提供 React 组件,所以只能自己适配 React。在适配过程中发现直接使用官方提供的安装脚本存在问题:如果快速在启用评论和没有启用评论的页面之间跳转,控制台就会出现如下异常信息:
Uncaught TypeError: Cannot read properties of undefined (reading 'contentWindow')
at a.sendCustomLivereOption (VM2683 embed.dist.js:2:15359)
at Object.requestCustomLivereOption (VM2683 embed.dist.js:2:10068)
at VM2683 embed.dist.js:2:13530
VM2683 embed.dist.js:2 Uncaught TypeError: Cannot read properties of undefined (reading 'style')
at a.resize (VM2683 embed.dist.js:2:15027)
at Object.loaded (VM2683 embed.dist.js:2:10130)
at VM2683 embed.dist.js:2:13530
Uncaught TypeError: Cannot read properties of undefined (reading 'style')
at a.resize (VM2683 embed.dist.js:2:15027)
at Object.resize (VM2683 embed.dist.js:2:10307)
at VM2683 embed.dist.js:2:13530
虽然没有发现影响评论功能的正常使用,但是这其中有很多隐患。这是因为 LiveRe 官方并不支持 Gatsby 这种使用 React 的单页 Web 应用。下面是官方的脚本:
<!-- 来必力City版安装代码 -->
<div id="lv-container" data-id="city" data-uid="[你自己的uid]">
<script type="text/javascript">
(function(d, s) {
var j, e = d.getElementsByTagName(s)[0];
if (typeof LivereTower === 'function') { return; }
j = d.createElement(s);
j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
j.async = true;
e.parentNode.insertBefore(j, e);
})(document, 'script');
</script>
<noscript>为正常使用来必力评论功能请激活JavaScript</noscript>
</div>
<!-- City版安装代码已完成 -->
代码的逻辑简单,主要逻辑在 第5行 到 第13行。当代码加载到浏览器后,其将在当前页面的第一个 <script>
标签之前插入一行 <script src='https://cdn-city.livere.com/js/embed.dist.js'></script>
。接下去浏览器就会下载并运行 https://cdn-city.livere.com/js/embed.dist.js
,代码的主要结构如下:
/*! livere.tower 2022-10-28, 3:40:04 PM */
var LivereTower = function() {
"use strict";
function serial(a) {
return a + "-" + Math.floor(1e3 * Math.random())
}
/**
*
* 这里省略了很多代码
*
*/
}();
LivereTower.init();
主要逻辑为:创建 LivereTower
对象,然后调用 LivereTower
对象的 init()
方法。这个方法会初始化 id
为 lv-container
的 <div>
标签,生成评论的界面。
容易看到问题,代码逻辑都是在整个网页全局执行的,而单页网站在跳转的时候并不刷新,所以全局变量不会被清除,导致反复加载代码逻辑出现问题。问题有:
- 重复生成
<script>
标签 - 重复生成全局对象
LivereTower
- 在 React 组件移除的时候无法移除事件,可能导致内存泄露
- LiveRe 还会动态加载一些广告脚本,这些脚本也没有对单页网站适配
所以,LiveRe 评论插件并不适合直接引入 React 类型的应用中。下面将通过比较巧妙的方法解决这个问题。
解决方案:使用 iframe 标签
解决思路:为了让 LiveRe 的代码不影响整个网页的运行时环境,使用 iframe 标签独立加载 LiveRe 评论页面,当 LiveRe 组件移除的时候,从页面移除 iframe 即可。由于 LiveRe 是独立的页面,因此需要为每一个使用 LiveRe 的页面都生成一个独立的 LiveRe 评论页。例如:/blog/first-post
这个博文页面需要添加 LiveRe 评论框,那就需要为这个页面创建一个独立的 LiveRe 评论页,路径可以是:/livere/blog/first-post
。然后在 /blog/first-post
博文页面引入 /livere/blog/first-post
的 iframe 即可。下面是具体步骤:
1. 生成 LiveRe 独立页面
利用 Gatsby 的 Node API 生成独立的包含 LiveRe 脚本的页面。下面为示例代码(gatsby-node.ts
):
import type { GatsbyNode } from "gatsby"
import path from "path"
import packageInfo from "./package.json"
export const createPages: GatsbyNode["createPages"] = async ({ actions, graphql, reporter }) => {
const { createPage } = actions
// 如果在 package.json 里配置 LiveRe 的 UID 则生成 LiveRe 独立页面
if (packageInfo.livere) {
// 使用 GraphQL 查询到你需要使用 LiveRe 的页面路径,实际查询代码需要视你个人的项目情况
const result = await graphql<{
allPost: {
nodes: {
slug: string
title: string
}[]
}
}>(`
{
allPost {
nodes {
slug
title
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild(`There was an error loading your posts`, result.errors)
return
}
// LiveRe 独立页面模板
const livereTemplate = path.resolve("src/templates/livere-template.tsx")
function createLiveRe(slug: string, title: string, uid: string) {
createPage({
path: path.posix.join("/livere", slug),
component: livereTemplate,
context: {
title,
uid,
}
})
}
const posts = result.data!.allPost.nodes
// 为每个页面路径生成一个 LiveRe 评论页面
posts.forEach(post => {
createLiveRe(post.slug, post.title, packageInfo.livere)
})
}
}
Gatsby Node API 生成 LiveRe 独立页面时使用的模板(src/templates/livere-template.tsx
):
import * as React from "react"
import { Script, HeadFC, PageProps } from "gatsby"
const LiveRe: React.FC<PageProps> = ({ pageContext }) => {
React.useEffect(() => {
(function (d, s) {
var j, e = d.getElementsByTagName(s)[0];
// @ts-ignore
if (typeof LivereTower !== 'undefined') { return; }
j = d.createElement(s);
j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
j.async = true;
// @ts-ignore
e.parentNode.insertBefore(j, e);
})(document, 'script');
})
return (
<>
// 需要在 iframe 页面内引入 iframeResizer 实现 iframe 窗口高度随内容自动调整
<Script src="/iframeResizer.contentWindow.min.js" />
<style>
{"#taboola-livere {display: none !important;}"}
</style>
<div
id="lv-container"
data-id="city"
// @ts-ignore
data-uid={pageContext.uid} >
<noscript>为正常使用来必力评论功能请激活JavaScript</noscript>
</div>
</>
)
}
export const Head: HeadFC = (props) => {
// @ts-ignore
const { title } = props.pageContext
return (
<>
// 可以修改标题,让标题在 LiveRe 管理后台更易读
<title>{title}</title>
</>
)
}
export default LiveRe
注意:为了让 iframe 的高度会自动跟随 iframe 的内容变化,需要引入 iframe-resizer 这个工具。为此我们需要在 LiveRe 独立页面引入 iframeResizer.contentWindow.min.js
脚本,并且在下面的 LiveRe React 组件中引入 iframe-resizer-react 组件。
2. LiveRe React 组件
最后再利用 iframe-resizer-react 生成 iframe 标签,即可得到最后可用的 LiveRe React 组件(livere.tsx
)。
import * as React from "react"
import * as path from "path-browserify"
import IframeResizer from "iframe-resizer-react"
import packageInfo from "../../package.json"
const LiveRe = ({ slug }) => {
if (!packageInfo.livere) {
return <></>
}
return (
<IframeResizer
src={path.join("/livere", slug)}
style={{ width: "1px", minWidth: "100%", border: "0px" }}
/>
)
}
export default LiveRe
最后
基于使用 iframe 的思路,这个方案算是完美解决了 LiveRe 与 Gatsby 的集成问题。唯独还有一点小缺点就是在 LiveRe 的管理后台看到评论的路径会事有 /livere/
前缀。