背景
本站建立时便未使用 WordPress 自带评论系统,原因主要是在有比较多的功能需求需要适配和实现的情况下,二次开发比较耗时费劲。所以一直采用的是 Artalk (https://github.com/qwqcode/Artalk),功能非常完善;支持 Markdown、评论折叠、管理员等贴心功能。但是美中不足的其一是有后端部署的要求;其二是与博客的引用出于某些不清楚还没深究的原因效果不佳。最后的妥协是通过 iFrame 引用然后通过强制同源在父页面获取子页面窗口高度来实现评论区高度匹配。同时,这样的部署有几个影响用户体验的问题:
- 高度获取需要暴力的循环获取直到评论区页面加载完毕
- 新评论发布无法处理回调,评论区高度无法更新
- 移动端样式不统一
有了这些痛点,同时借机最近发现的 LeanCloud 平台,加之在家自由时间泛滥,新的评论库的开发就开始了。
TL;DR
本文...主要宣传和记录,样例暂不完善。使用方法见 https://nexment.ouorz.com
环境配置
计划是全平台、全框架适配,但是由于页面渲染借助框架来实现,所以不如直接作为各个框架的组件库辅之以浏览器支持的 Web Component 来实现适配。什么?Angular.js?Web Component 了解一下。
React 篇
参考前几篇文章,最近正在学习和入门 React.js 于是首先就没想过多地开始使用 React 进行开发了。最初使用 Rollup.js 打包器配置好了环境 (参见早前的 Commits),但是在本地测试时出现了 React Hooks 由于「存在多个 React 副本」的问题,查阅文档和论坛都无果还花费了半天时间经历了简直和第一次折腾 Webpack 配置一样的繁琐体验后选择了 TypeScript + React 组件库开发脚手架 TSDX,无需过多操心环境配置,专注于功能开发。
当然,为了支持 CSS 预处理器 Scss 还是需要进行 Rollup 配置的增加,参考文档可通过以下实现:
// rollup-plugin-sass 仅支持 .scss 文件打包,rollup-plugin-scss 对 .css 和 .scss 文件有同时支持
const sass = require('rollup-plugin-sass');
module.exports = {
rollup(config, options) {
config.plugins.push(
sass({
insert: true
})
);
return config;
},
};
↑ tsdx.config.js
Vue.js / Web Component 篇
Vue.js 3 Release 后会重写,目前使用 Vue.js 2 Composition API 语法。Vue-Cli 提供「构建目标」选项,可打包 Library / Web Component 库,具体见文档。在 Vue.js 中引入组件时需要注册,于是打包的入口文件便需是组件注册的函数,按照文档编写如下:
import NexmentContainer from "./components/container/index.vue";
export default {
install(Vue: any, options: any) {
Vue.component("NexmentContainer", NexmentContainer);
},
};
↑ main.ts
Vue-Cli 生成 CommonJS 和 UMD 类型的生产文件,但在浏览器引用时会出现 LeanCloud 无法请求的问题,于是换为使用 Web Component 对浏览器支持。将 Vue.js 内嵌入生产文件中便可实现无框架依赖的内容渲染。使用可参考 Demo 和打包命令配置。
功能实现
异步数据获取与更新
首先在 React.js 使用了 SWR,其可借助 React Hooks 实现异步数据获取、聚焦时刷新、数据缓存的功能,不通过 WebSocket 来变相实现数据同步。 文档可见 → https://swr.vercel.app。通过 useSWR Hook 和 mutate 函数获取数据及更新的样例如下:
/*
* ListGet: Function,
* pageKey: string
*/
const { data, error } = useSWR(pageKey, ListGet);
return {
commentsData: data,
isLoading: !error && !data,
isError: error,
};
在 Vue.js 中有一个新生项目 SWRV 借鉴自 SWR 功能几乎一致,依赖 Composition API。样例如下:
export default defineComponent({
setup() {
const { data, error, mutate } = useSWRV(
pageKey
ListGet
);
return {
data,
error,
mutate,
};
}
})
全局配置
在引入 Nexment 的 Container 组件后,传入的包含配置信息的参数使用了 React Context 来在子组件传递。此法可避免多次重复传参,参考 React 文档。使用样例如下:
/* configContext.ts */
// create context
const Context: any = React.createContext({});
// context provider
export const Provider = Context.Provider;
export default Context;
/* use context */
import Context from 'configContext';
const NexmentConfigs = React.useContext(Context);
子组件调用父组件方法
在 React 中直接将父组件方法作为参数传递至子组件调用即可。Vue 中也可通过实例方法 Vue.$emit() 触发当前实例上的事件,样例如下:
/* CommentsList.vue */
<template>
<CommentsArea
@reloadFunc="changeLoadingStatus"
/>
</template>
<script>
export default {
data(){
return{
loadingStatus: false
} as {
loadingStatus: boolean
}
},
methods:{
changeLoadingStatus() {
this.loadingStatus = !this.loadingStatus;
},
}
}
</script>
/* CommentsArea.vue */
...
this.$emit("reloadFunc");
...
状态数据更新
React 中使用 useState Hook 来在函数组件内创建数据 State 和更新 State 的函数,样例如下:
const [resetStatus, setResetStatus] = React.useState<boolean>(false);
setResetStatus(true);
Vue 中更新对象类型的数据中的内容需要通过实例方法 Vue.$set() 实现,样例如下:
...
modalVisibility:{
OID: true,
xxx:{
xxx: xxx,
...
}
}
...
this.$set(this.modalVisibility, OID, false);
...
文本框相关
大多功能都采用了依赖来实现,列举如下:
Textarea 中在光标处插入内容,采用 insert-text-at-cursor。Vue 中是通过 ID 获取的元素,React 中通过 useRef Hook 可获取到当前组件的 DOM,样例如下:
const nexmentTextarea: any = React.useRef();
const content:string = 'content';
insertTextAtCursor(nexmentTextarea.current, content);
Textarea 根据文本自适应高度, Vue 采用 autosize,React 采用 react-textarea-autosize。
项目仓库
Vue.js / Web Component 版本:https://github.com/HelipengTony/nexment-vue
React.js 版本:https://github.com/HelipengTony/nexment
Vue / Web Component Demo: https://nexment-vue-demo.ouorz.com
React Demo: https://nexment-demo.ouorz.com
文档站点:https://nexment.ouorz.com
后记
没时间后记了,快在你的项目中用上吧!