From 5a65b4bd48ec393f1e15b5fd45b9d7ee87958714 Mon Sep 17 00:00:00 2001 From: DCloud_LXH <283700113@qq.com> Date: Tue, 31 May 2022 20:37:08 +0800 Subject: [PATCH] fix(h5): rich-text ssr --- .../src/vue/rich-text/index.tsx | 69 +++++++++++++++---- .../src/vue/rich-text/nodes-parser.js | 14 ++-- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/packages/uni-components/src/vue/rich-text/index.tsx b/packages/uni-components/src/vue/rich-text/index.tsx index 8406b4f5e..fe3c9a23e 100644 --- a/packages/uni-components/src/vue/rich-text/index.tsx +++ b/packages/uni-components/src/vue/rich-text/index.tsx @@ -1,11 +1,40 @@ -import { onMounted, ref, watch, getCurrentInstance } from 'vue' +import { onMounted, ref, watch, getCurrentInstance, h, VNode } from 'vue' +import { hasOwn } from '@vue/shared' import { defineBuiltInComponent, useCustomEvent, EmitEvent, } from '@dcloudio/uni-components' -import parseNodes from './nodes-parser' +import parseNodes, { TAGS, decodeEntities } from './nodes-parser' import { props, parseHtml } from '../../components/rich-text' +import { ssrRef } from '@dcloudio/uni-app' + +interface Node { + type: string + text?: string + name: string + attrs: Object + children: Node[] +} + +function _createVNode(nodeList: Node[]): Array { + if (!nodeList) return [] + + return nodeList.map((node) => { + if (node.name) { + const tagName = node.name.toLowerCase() + if (!hasOwn(TAGS, tagName)) { + return + } + } + const isNode = !hasOwn(node, 'type') || node.type === 'node' + return h( + isNode ? node.name : 'span', + node.attrs, + isNode ? _createVNode(node.children) : decodeEntities(node.text) + ) + }) +} export default /*#__PURE__*/ defineBuiltInComponent({ name: 'RichText', @@ -23,7 +52,9 @@ export default /*#__PURE__*/ defineBuiltInComponent({ ], setup(props, { emit, attrs }) { const vm = getCurrentInstance() + const scopeId = (vm && vm.vnode.scopeId) || '' const rootRef = ref(null) + const nodelist = ssrRef(props.nodes, 'nodelist') const trigger = useCustomEvent>(rootRef, emit) const hasItemClick = !!attrs.onItemclick @@ -31,14 +62,21 @@ export default /*#__PURE__*/ defineBuiltInComponent({ trigger('itemclick', e, detail) } + // ssr 处理 + if (__NODE_JS__) { + if (typeof props.nodes === 'string') { + nodelist.value = parseHtml(props.nodes) + } + } + function _renderNodes(nodes: string | unknown[]) { if (typeof nodes === 'string') { - nodes = parseHtml(nodes) + nodelist.value = parseHtml(nodes) } const nodeList = parseNodes( - nodes, + nodelist.value, document.createDocumentFragment(), - (vm && vm.vnode.scopeId) || '', + scopeId, hasItemClick && triggerItemClick ) rootRef.value!.firstElementChild!.innerHTML = '' @@ -53,15 +91,22 @@ export default /*#__PURE__*/ defineBuiltInComponent({ ) onMounted(() => { - _renderNodes(props.nodes) + _renderNodes(nodelist.value as []) }) - return () => { - return ( - -
- + return () => + h( + 'uni-rich-text', + { + ref: rootRef, + }, + [ + h( + 'div', + {}, + __NODE_JS__ ? _createVNode(nodelist.value as Node[]) : [] + ), + ] ) - } }, }) diff --git a/packages/uni-components/src/vue/rich-text/nodes-parser.js b/packages/uni-components/src/vue/rich-text/nodes-parser.js index f82db4872..1c0449152 100644 --- a/packages/uni-components/src/vue/rich-text/nodes-parser.js +++ b/packages/uni-components/src/vue/rich-text/nodes-parser.js @@ -1,7 +1,7 @@ import { hasOwn, isPlainObject } from '@vue/shared' import { getRealPath } from '@dcloudio/uni-platform' -const TAGS = { +export const TAGS = { a: '', abbr: '', address: '', @@ -77,7 +77,7 @@ const CHARS = { apos: "'", } -function decodeEntities(htmlString) { +export function decodeEntities(htmlString) { return htmlString.replace( /&(([a-zA-Z]+)|(#x{0,1}[\da-zA-Z]+));/gi, function (match, stage) { @@ -90,9 +90,13 @@ function decodeEntities(htmlString) { if (/^#x[0-9a-f]{1,4}$/i.test(stage)) { return String.fromCharCode('0' + stage.slice(1)) } - const wrap = document.createElement('div') - wrap.innerHTML = match - return wrap.innerText || wrap.textContent + if (!__NODE_JS__) { + const wrap = document.createElement('div') + wrap.innerHTML = match + return wrap.innerText || wrap.textContent + } else { + return match + } } ) } -- GitLab