You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从数据库渲染含Vue组件的HTML:Tweet组件无法识别问题

问题描述

我用Spring做后端、Jsoup爬取新闻文章,已获取文章内容,但需要提取并渲染其中的推文。

当前尝试的方案行不通:后端爬取文章后将HTML存入数据库,前端通过ID获取文章后,想用vue-tweet-embed组件渲染推文。流程逻辑没问题,但v-html无法识别HTML里的 Vue组件,只会把它当作普通HTML标签;但直接将标签写在Vue代码里时,能正常渲染。

相关代码

Java 后端代码

Elements aTags = paragraph.select("a[href*=twitter.com]");
for (Element aTag : aTags) {
    String tweetUrl = aTag.attr("href").split("\\?")[0];
    content.append("<Tweet tweet-url='" + tweetUrl + "'></Tweet>" + System.lineSeparator() + System.lineSeparator());
}

Vue 前端代码

<template>
    <div class="container">
        <div v-for="content in article" v-bind:key="content.id" >
            <h1 style="font-size: 50px; text-align: left; font-weight: bolder;">{{ content.title }}</h1>
            <img class="article-image" :src="content.urlToImage">
            <h4>read from the original source here:</h4> 
            <a class="a-article" v-bind:href="content.url" target="_blank"> {{ content.url }}</a>
            <Tweet></Tweet>
            <div class="content" v-html="content.text"></div>
            <CommentSection :articleId="content.article_id"></CommentSection>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            article: [],
        }
    },
    created() {
        ArticleService.getArticleContent(this.$route.params.id).then(response => {
            this.article = response.data
        })
    }
}
</script>

待渲染的HTML片段(来自数据库的article.text)

<p>The Celtics thought it was funny — Jaylen Brown jokingly offered Blake Griffin his mask after the Bucks’ Thanasis Antetokounmpo headbutted the Celtic forward.</p>

<p>The NBA didn’t find it as amusing and suspended Antetokounmpo for one game for the incident.</p>

<p>Thanasis ejected after head-butting Blake Griffin 😳 pic.twitter.com/gaQSIiFX59</p>

<p>— Bleacher Report (@BleacherReport) March 31, 2023</p>

<tweet tweet-url="https://twitter.com/BleacherReport/status/1641617577003880448"></tweet>

<p></p>

<p>Griffin was given a Flagrant 1 foul at the time, but mostly the Celtics had a good laugh.</p>

<p>Jaylen really gave Blake his mask 🤣🤣🤣@FCHWPO @blakegriffin23 pic.twitter.com/Gwn9AckWP3</p>

<tweet tweet-url="https://twitter.com/FCHWPO"></tweet>

<tweet tweet-url="https://twitter.com/blakegriffin23"></tweet>

<p>— Celtics on NBC Sports Boston (@NBCSCeltics) March 31, 2023</p>

<tweet tweet-url="https://twitter.com/NBCSCeltics/status/1641617185511464961"></tweet>

解决方案

方案1:重构数据存储,分离文本与推文链接(推荐)

后端不要将标签混在HTML中存储,而是拆分数据结构:

  • 爬取时,提取文章纯文本内容,同时单独收集所有推文URL,存入数据库的独立字段(比如tweetUrls数组)
  • 前端获取文章后,用v-html渲染纯文本,再循环tweetUrls数组渲染组件

后端修改示例

StringBuilder articleText = new StringBuilder();
List<String> tweetUrls = new ArrayList<>();

Elements aTags = paragraph.select("a[href*=twitter.com]");
for (Element aTag : aTags) {
    String tweetUrl = aTag.attr("href").split("\\?")[0];
    tweetUrls.add(tweetUrl);
    // 保留原链接的HTML,避免丢失原文结构
    articleText.append(aTag.outerHtml()).append(System.lineSeparator());
}
// 将articleText和tweetUrls一起存入数据库,返回给前端

前端修改示例

<template>
    <div class="container">
        <div v-for="content in article" :key="content.id" >
            <!-- 其他内容保持不变 -->
            <div class="content" v-html="content.text"></div>
            <!-- 循环渲染推文组件 -->
            <Tweet v-for="url in content.tweetUrls" :key="url" :tweet-url="url"></Tweet>
            <CommentSection :articleId="content.article_id"></CommentSection>
        </div>
    </div>
</template>

方案2:动态编译Vue组件(不推荐,有性能与安全风险)

如果必须保留HTML中的标签,可使用Vue的动态编译能力:

  1. 安装对应依赖:Vue3用@vue/compiler-sfc,Vue2用vue-template-compiler
  2. 在组件中动态编译包含标签的HTML字符串为可渲染的Vue组件

Vue3 示例

<script setup>
import { compile, h } from 'vue'
import Tweet from 'vue-tweet-embed'

const renderTweetHtml = (html) => {
    const render = compile(html)
    return h({
        render,
        components: { Tweet }
    })
}
</script>

<template>
    <div class="container">
        <div v-for="content in article" :key="content.id">
            <!-- 其他内容 -->
            <component :is="renderTweetHtml(content.text)"></component>
        </div>
    </div>
</template>

注意:动态编译会降低页面性能,若HTML来自不可信源还存在XSS风险,仅在完全可控的场景下使用。

更优的推文爬取方案

  • 调用Twitter API:后端直接调用官方API(需开发者账号)获取推文的完整结构化数据(内容、作者、时间、媒体等),存入数据库后,前端可自定义推文样式,无需依赖第三方嵌入包
  • 提取推文核心信息:若无法使用API,用Jsoup提取推文的文本、图片、作者、发布时间等核心字段,存为结构化数据,前端自己实现推文UI,避免第三方组件的兼容性问题

内容的提问来源于stack exchange,提问作者Philip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 21:57:56