TTS-Web-Vue系列:Vue3实现内嵌iframe文档显示功能

🖼️ 本文是TTS-Web-Vue系列的新篇章,重点介绍如何在Vue3项目中优雅地实现内嵌iframe功能,用于加载外部文档内容。通过Vue3的响应式系统和组件化设计,我们实现了一个功能完善、用户体验友好的文档嵌入方案,包括加载状态管理、错误处理和自适应布局等关键功能。

📖 系列文章导航

欢迎查看主页

🌟 内嵌iframe的应用场景与价值

在现代Web应用中,内嵌iframe是集成外部内容的有效方式,特别适用于以下场景:

  1. 展示项目文档:直接嵌入项目文档网站,避免用户在多个标签页切换
  2. 整合第三方内容:无需重新开发,直接复用已有的Web资源
  3. 隔离运行环境:为外部内容提供独立的执行环境,避免与主应用冲突
  4. 保持UI一致性:让外部内容看起来像是应用的一部分,提升用户体验
  5. 降低开发成本:避免重复开发相似功能,专注于核心业务逻辑

在TTS-Web-Vue项目中,我们使用内嵌iframe来加载项目文档,使用户能够在不离开应用的情况下查阅使用指南、API文档和其他参考资料。

💡 实现思路与技术选型

整体设计方案

我们的iframe嵌入方案采用了以下设计思路:

  1. 响应式状态管理:使用Vue3的响应式系统管理iframe的加载状态
  2. 异常处理机制:完善的错误处理和恢复策略,提供友好的错误界面
  3. 动态样式调整:根据内容和容器大小动态调整iframe尺寸
  4. 跨域安全处理:合理设置sandbox属性和referrer策略,确保安全性
  5. 加载状态反馈:提供视觉反馈,优化用户等待体验
  6. 备用方案支持:支持多个文档源,在主源不可用时提供备选链接

这种方案既保证了功能的完整性,又提供了良好的用户体验和可维护性。

技术实现要点

  • 使用Vue3的refwatch实现响应式状态管理
  • 通过DOM API动态调整iframe样式和容器布局
  • 利用Element Plus组件库提供加载和错误界面
  • 使用PostMessage API实现iframe与主应用的通信
  • 结合CSS动画提升加载体验

🧩 核心代码实现

主组件模板代码

在Main.vue中,我们实现了文档页面容器和iframe的基本结构:

<div v-if="page.asideIndex === '4'" class="doc-page-container" :key="'doc-page'"><!-- 加载状态显示 --><div v-if="!iframeLoaded && !iframeError" class="iframe-loading"><div class="loading-spinner"></div><p>正在加载文档<span class="loading-dots"></span></p></div><!-- iframe组件 --><iframe ref="docIframe"class="doc-frame" :src="iframeCurrentSrc" @load="handleIframeLoad"@error="handleIframeError"allow="fullscreen"referrerpolicy="no-referrer":class="{'iframe-visible': iframeLoaded}"sandbox="allow-scripts allow-same-origin allow-popups allow-forms"></iframe><!-- 错误状态显示 --><div v-if="iframeError" class="iframe-error"><el-icon class="error-icon"><WarningFilled /></el-icon><p>加载文档失败,请检查网络连接或尝试备用链接。</p><div class="error-actions"><el-button type="primary" @click="reloadIframe"><el-icon><RefreshRight /></el-icon> 重新加载</el-button><el-button @click="tryAlternativeUrl"><el-icon><SwitchButton /></el-icon> 尝试备用链接</el-button></div></div>
</div>

状态管理与初始化

在组合式API中管理iframe相关的状态:

// 声明状态变量
const docIframe = ref(null);
const iframeLoaded = ref(false);
const iframeError = ref(false);
const docUrl = ref('https://docs.tts88.top/');
const urlIndex = ref(0);
const iframeCurrentSrc = ref('');
const docUrls = ['https://docs.tts88.top/',// 可以添加备用链接
];// iframe初始化函数
const initIframe = () => {iframeCurrentSrc.value = '';// 在清除src后,立即设置容器和iframe样式以确保正确显示nextTick(() => {// 修改页面主容器样式,保留基本结构但减少内边距const mainContainer = document.querySelector('.modern-main');if (mainContainer instanceof HTMLElement && page?.value?.asideIndex === '4') {mainContainer.style.padding = '0';mainContainer.style.gap = '0';}const container = document.querySelector('.doc-page-container');if (container instanceof HTMLElement) {// 设置文档容器填充可用空间,但不使用fixed定位container.style.display = 'flex';container.style.flexDirection = 'column';container.style.height = 'calc(100vh - 40px)'; // 只预留顶部导航栏的空间container.style.margin = '0';container.style.padding = '0';container.style.borderRadius = '0';container.style.boxShadow = 'none';container.style.position = 'relative';}if (docIframe.value) {docIframe.value.style.display = 'block';docIframe.value.style.flex = '1';docIframe.value.style.width = '100%';docIframe.value.style.height = '100%';docIframe.value.style.minHeight = '700px';docIframe.value.style.maxHeight = 'none';docIframe.value.style.margin = '0';docIframe.value.style.padding = '0';docIframe.value.style.border = 'none';docIframe.value.style.borderRadius = '0';}// 设置iframe源iframeCurrentSrc.value = docUrl.value;console.log('iframe 初始化源设置为:', docUrl.value);});
};

事件处理函数

处理iframe的加载和错误事件:

// 处理 iframe 加载成功
const handleIframeLoad = (event) => {console.log('iframe 加载事件触发');// 检查iframe是否完全加载且可访问try {const iframe = event.target;// 不是所有iframe都会触发跨域报错,但我们需要检查是否实际加载成功if (iframe.contentWindow && iframe.src.includes(docUrl.value)) {iframeLoaded.value = true;iframeError.value = false;console.log('iframe 加载成功:', {width: iframe.offsetWidth,height: iframe.offsetHeight});// 尝试调整iframe高度nextTick(() => {adjustIframeHeight();// 发送初始化消息到iframesendInitMessageToIframe();});// 显示加载成功提示ElMessage({message: "文档加载成功",type: "success",duration: 2000,});} else {console.warn('iframe可能加载不完整或存在跨域问题');}} catch (error) {// 处理跨域安全限制导致的错误console.error('检查iframe出错 (可能是跨域问题):', error);// 我们不将这种情况标记为错误,因为iframe可能仍然正常加载iframeLoaded.value = true;}
};// 处理 iframe 加载失败
const handleIframeError = (event) => {console.error('iframe 加载失败:', event);iframeLoaded.value = false;iframeError.value = true;ElMessage({message: "文档加载失败,请检查网络连接",type: "error",duration: 3000,});
};// 重新加载 iframe
const reloadIframe = () => {console.log('重新加载 iframe');iframeLoaded.value = false;iframeError.value = false;// 强制 iframe 重新加载initIframe();ElMessage({message: "正在重新加载文档",type: "info",duration: 2000,});
};// 尝试使用备用链接
const tryAlternativeUrl = () => {urlIndex.value = (urlIndex.value + 1) % docUrls.length;docUrl.value = docUrls[urlIndex.value];console.log(`尝试备用文档链接: ${docUrl.value}`);iframeLoaded.value = false;iframeError.value = false;// 清空并重新设置src以确保重新加载initIframe();ElMessage({message: `正在尝试备用链接: ${docUrl.value}`,type: "info",duration: 3000,});
};

样式和动画设计

为iframe相关组件添加样式:

.iframe-loading, .iframe-error {position: absolute;top: 0;left: 0;right: 0;bottom: 0;display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: var(--card-background);z-index: 1000;text-align: center;
}.iframe-loading {font-size: 18px;font-weight: 600;color: var(--text-primary);
}.loading-spinner {width: 40px;height: 40px;border: 4px solid rgba(74, 108, 247, 0.2);border-radius: 50%;border-top-color: var(--primary-color);animation: spin 1s linear infinite;margin-bottom: 16px;
}@keyframes spin {to {transform: rotate(360deg);}
}.iframe-error {padding: 30px;background-color: var(--card-background);
}.iframe-error p {margin: 16px 0;font-size: 16px;color: var(--text-secondary);
}.error-icon {font-size: 48px;color: #ff4757;margin-bottom: 16px;
}.error-actions {display: flex;gap: 16px;margin-top: 16px;
}.loading-dots {display: inline-block;width: 30px;text-align: left;
}.loading-dots:after {content: '.';animation: dots 1.5s steps(5, end) infinite;
}@keyframes dots {0%, 20% {content: '.';}40% {content: '..';}60% {content: '...';}80%, 100% {content: '';}
}

🔄 跨域通信实现

发送消息到iframe

通过postMessage API实现与iframe内容的通信:

// 向iframe发送消息
const sendMessageToIframe = (message) => {if (docIframe.value && docIframe.value.contentWindow) {try {docIframe.value.contentWindow.postMessage(message, '*');console.log('向iframe发送消息:', message);} catch (error) {console.error('向iframe发送消息失败:', error);}}
};// 在iframe加载完成后发送初始化消息
const sendInitMessageToIframe = () => {// 等待iframe完全加载setTimeout(() => {sendMessageToIframe({type: 'init',appInfo: {name: 'TTS Web Vue',version: '1.0',theme: document.body.classList.contains('dark-theme') ? 'dark' : 'light'}});}, 1000);
};

接收来自iframe的消息

监听并处理iframe发送的消息:

// 处理来自iframe的消息
const handleIframeMessage = (event) => {console.log('收到消息:', event);// 确保消息来源安全,验证来源域名const isValidOrigin = docUrls.some(url => {try {const urlHost = new URL(url).hostname;return event.origin.includes(urlHost);} catch (e) {return false;}});// 如果消息来源不安全,忽略此消息if (!isValidOrigin) {console.warn('收到来自未知来源的消息,已忽略:', event.origin);return;}console.log('来自文档页面的消息:', event.data);// 处理不同类型的消息if (typeof event.data === 'object' && event.data !== null) {// 文档加载完成消息if (event.data.type === 'docLoaded') {iframeLoaded.value = true;iframeError.value = false;ElMessage({message: "文档页面已准备就绪",type: "success",duration: 2000,});// 对iframe内容回送确认消息sendMessageToIframe({type: 'docLoadedConfirm',status: 'success'});}// 调整高度消息if (event.data.type === 'resizeHeight' && typeof event.data.height === 'number') {const height = event.data.height;if (height > 0 && docIframe.value) {// 确保高度合理const safeHeight = Math.max(Math.min(height, 5000), 300);docIframe.value.style.height = `${safeHeight}px`;console.log(`根据iframe请求调整高度: ${safeHeight}px`);}}}
};// 在组件挂载时添加消息监听器
onMounted(() => {window.addEventListener('message', handleIframeMessage);
});// 在组件卸载时移除监听器
onUnmounted(() => {window.removeEventListener('message', handleIframeMessage);
});

📱 自适应布局实现

响应式高度调整

动态调整iframe高度以适应不同屏幕尺寸:

// 添加新函数用于调整iframe高度
const adjustIframeHeight = () => {if (!docIframe.value) return;// 获取容器高度const container = document.querySelector('.doc-page-container');if (!container) return;// 修改页面主容器样式,减少内边距但保留基本布局const mainContainer = document.querySelector('.modern-main');if (mainContainer instanceof HTMLElement && page?.value?.asideIndex === '4') {mainContainer.style.padding = '0';mainContainer.style.gap = '0';}// 获取可用高度(视口高度减去顶部导航栏高度)const availableHeight = window.innerHeight - 40;// 设置container样式以充分利用可用空间if (container instanceof HTMLElement) {container.style.height = `${availableHeight}px`;container.style.maxHeight = `${availableHeight}px`;container.style.margin = '0';container.style.padding = '0';container.style.borderRadius = '0';container.style.boxShadow = 'none';container.style.position = 'relative';}// 设置iframe样式以充满容器docIframe.value.style.width = '100%';docIframe.value.style.height = '100%';docIframe.value.style.minHeight = '700px';docIframe.value.style.maxHeight = 'none';docIframe.value.style.display = 'block';docIframe.value.style.flex = '1';docIframe.value.style.margin = '0';docIframe.value.style.padding = '0';docIframe.value.style.border = 'none';docIframe.value.style.borderRadius = '0';
};// 监听窗口大小变化事件
const handleResize = () => {if (page?.value?.asideIndex === '4' && iframeLoaded.value) {adjustIframeHeight();}
};// 在组件挂载和窗口大小变化时调整高度
onMounted(() => {window.addEventListener('resize', handleResize);
});onUnmounted(() => {window.removeEventListener('resize', handleResize);
});

移动端显示优化

为移动设备添加特定的样式调整:

@media (max-width: 768px) {.doc-page-container {height: calc(100vh - 50px) !important; /* 为移动端顶部导航栏留出更多空间 */}.iframe-loading p, .iframe-error p {font-size: 14px;padding: 0 20px;}.error-actions {flex-direction: column;width: 80%;}.loading-spinner {width: 30px;height: 30px;}
}

🔒 安全性考虑

iframe安全属性设置

为确保iframe的安全性,我们设置了以下关键属性:

<iframe ref="docIframe"class="doc-frame" :src="iframeCurrentSrc" @load="handleIframeLoad"@error="handleIframeError"allow="fullscreen"referrerpolicy="no-referrer":class="{'iframe-visible': iframeLoaded}"sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
>
</iframe>

主要安全措施包括:

  1. sandbox属性:限制iframe内容的权限,仅允许必要的功能

    • allow-scripts: 允许运行脚本
    • allow-same-origin: 允许访问同源资源
    • allow-popups: 允许打开新窗口
    • allow-forms: 允许表单提交
  2. referrerpolicy:设置为no-referrer防止泄露引用信息

  3. 消息验证:验证接收消息的来源,防止恶意站点发送的消息

跨域消息验证

在处理iframe消息时进行来源验证:

// 确保消息来源安全,验证来源域名
const isValidOrigin = docUrls.some(url => {try {const urlHost = new URL(url).hostname;return event.origin.includes(urlHost);} catch (e) {return false;}
});// 如果消息来源不安全,忽略此消息
if (!isValidOrigin) {console.warn('收到来自未知来源的消息,已忽略:', event.origin);return;
}

🎭 用户体验增强

加载状态优化

为提供更好的视觉反馈,我们添加了加载动画和进度指示:

<div v-if="!iframeLoaded && !iframeError" class="iframe-loading"><div class="loading-spinner"></div><p>正在加载文档<span class="loading-dots"></span></p>
</div>

动画效果通过CSS实现:

.loading-spinner {width: 40px;height: 40px;border: 4px solid rgba(74, 108, 247, 0.2);border-radius: 50%;border-top-color: var(--primary-color);animation: spin 1s linear infinite;margin-bottom: 16px;
}@keyframes spin {to {transform: rotate(360deg);}
}.loading-dots:after {content: '.';animation: dots 1.5s steps(5, end) infinite;
}@keyframes dots {0%, 20% {content: '.';}40% {content: '..';}60% {content: '...';}80%, 100% {content: '';}
}

错误处理与恢复

提供直观的错误界面和恢复选项:

<div v-if="iframeError" class="iframe-error"><el-icon class="error-icon"><WarningFilled /></el-icon><p>加载文档失败,请检查网络连接或尝试备用链接。</p><div class="error-actions"><el-button type="primary" @click="reloadIframe"><el-icon><RefreshRight /></el-icon> 重新加载</el-button><el-button @click="tryAlternativeUrl"><el-icon><SwitchButton /></el-icon> 尝试备用链接</el-button></div>
</div>

📊 性能优化

减少重绘和回流

为提高iframe加载性能,我们采取了以下优化措施:

// 先将iframe的src设为空,然后再设置目标URL,减少重复加载
iframeCurrentSrc.value = '';// 使用nextTick等待DOM更新后再进行样式调整
nextTick(() => {// 样式调整代码...// 最后再设置srciframeCurrentSrc.value = docUrl.value;
});

延迟加载与可见性优化

只有在iframe加载完成后才显示内容,避免闪烁:

<iframe :class="{'iframe-visible': iframeLoaded}"<!-- 其他属性... -->
>
</iframe>
.doc-frame {opacity: 0;transition: opacity 0.3s ease;
}.iframe-visible {opacity: 1;
}

📝 总结与拓展

主要成果

通过Vue3实现内嵌iframe,我们为TTS-Web-Vue项目带来了以下价值:

  1. 一体化用户体验:用户无需离开应用即可访问文档
  2. 响应式布局:自适应不同屏幕尺寸,优化移动端体验
  3. 完善的状态管理:处理加载、错误等各种状态,提升用户体验
  4. 安全可控:通过sandbox和消息验证确保安全性
  5. 高性能:优化加载过程,减少性能开销

未来可能的拓展方向

  1. 内容预加载:实现文档预加载,进一步提升加载速度
  2. 深度链接:支持直接链接到文档的特定部分
  3. 离线支持:加入文档缓存功能,支持离线访问
  4. 内容同步:实现iframe内容与应用状态的双向同步
  5. 多文档管理:支持多个文档源和文档切换功能

🔗 相关链接

  • TTS-Web-Vue项目主页
  • 在线演示
  • Vue3官方文档
  • Element Plus UI库
  • MDN iframe文档

注意:本文介绍的功能仅供学习和个人使用,请勿用于商业用途。如有问题或建议,欢迎在评论区讨论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905441.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch索引设计与调优

一、分片策略设计 1.‌分片容量规划 单分片容量建议30GB(日志场景可放宽至100GB),避免超大分片引发查询延迟。分片总数计算公式:总数据量 / 30GB 1.2(20%余量应对未来增长)。主分片数创建后不可修改,副本分片数支持动态调整。2.‌分片分布优化 PUT logs-2025 { &qu…

Spring AI 集成 Mistral AI:构建高效多语言对话助手的实战指南

Spring AI 集成 Mistral AI&#xff1a;构建高效多语言对话助手的实战指南 前言 在人工智能应用开发领域&#xff0c;选择合适的大语言模型&#xff08;LLM&#xff09;与开发框架至关重要。Mistral AI 凭借其高效的多语言模型&#xff08;如 Mistral-7B、Mixtral-8x7B 等&am…

从新手到高手:全面解析 AI 时代的「魔法咒语」——Prompt

引言&#xff1a;AI 时代的「语言炼金术」 在人工智能技术突飞猛进的今天&#xff0c;我们正在经历一场堪比工业革命的生产力变革。从聊天机器人到图像生成&#xff0c;从数据分析到自动化写作&#xff0c;AI 模型正在重塑人类与信息交互的方式。而在这一切背后&#xff0c;隐…

MySQL 8.0安装(压缩包方式)

MySQL 8.0安装(压缩包方式) 下载安装包并解压 下载 https://dev.mysql.com/downloads/mysql/可关注“后端码匠”回复“MySQL8”关键字获取 解压&#xff08;我解压到D:\dev\mysql-8.4.5-winx64目录下&#xff09; 创建mysql服务 注意&#xff0c;这步之前一定要保证自己电…

免费Ollama大模型集成系统——Golang

Ollama Free V2 Web 功能实现&#xff1a;界面交互与后端逻辑 一、Web 界面概述 Ollama Free V2 的 Web 界面提供了丰富的交互功能&#xff0c;包括模型选择、图片上传、历史记录查看等。界面使用 Bootstrap 进行布局&#xff0c;结合 JavaScript 实现动态交互。 二、前端界…

【AI】人工智能数据标注细分和商业机会

一、数据标注的常见方法 数据标注是为人工智能模型训练提供高质量标签的过程&#xff0c;根据数据类型&#xff08;图像、文本、音频、视频等&#xff09;的不同&#xff0c;标注方法也有所差异&#xff1a; 1. 图像标注 分类标注&#xff1a;为图像分配类别标签&#xff08…

lanqiaoOJ 652:一步之遥 ← 扩展欧几里得定理

【题目来源】 https://www.lanqiao.cn/problems/652/learning/ 【题目背景】 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 【题目描述】 从昏迷中醒来&#xff0c;小明发现自己被关在X星球的废矿车里。矿车停在平直的废弃…

HTTP / HTTPS 协议

目录 一、前言&#xff1a; 二、Fiddler 抓包工具&#xff1a; 三、http 协议&#xff1a; 1、http 请求&#xff1a; 1.&#xff08;1&#xff09;请求行&#xff1a; 1、(2) 请求头&#xff1a; 1、(3) 请求正文: 2、http 响应&#xff1a; 2、(1) 状态码&#x…

使用泛型加载保存数据

文章速览 泛型泛型概述定义优点 实例加载数据保存数据 一个赞&#xff0c;专属于你的足迹&#xff01; 泛型 泛型概述 泛型&#xff08;Generics&#xff09;是 C# 中一种重要的编程特性&#xff0c;它允许程序员编写灵活且类型安全的代码。通过使用泛型&#xff0c;可以创建…

Redis内存淘汰策略和过期键删除策略有哪些?

Redis 提供 8 种内存淘汰策略&#xff0c;以下是详细解析及场景建议&#xff1a; 一、核心策略解析 noeviction (默认策略) 机制&#xff1a;内存满时拒绝新写入操作&#xff0c;返回错误优势&#xff1a;绝对数据安全场景&#xff1a;金融交易系统、医疗数据存储 allkeys-lr…

【C/C++】自定义类型:结构体

文章目录 前言自定义类型&#xff1a;结构体1.结构体类型的声明1.1 结构体回顾1.1.1 结构的声明 1.1.2 结构体变量的创建和初始化1.2 结构的特殊声明1.3 结构的自引用 2.结构体内存对齐2.1 对⻬规则2.2 为什么存在内存对齐&#xff1f;2.3 修改默认对⻬数 3. 结构体传参4.结构体…

PPO算法:一种先进的强化学习策略

什么是PPO算法&#xff1f; PPO&#xff08;Proximal Policy Optimization&#xff09;是一种增强学习算法&#xff0c;主要应用于解决连续控制任务。PPO算法在2017年由OpenAI提出&#xff0c;旨在解决传统策略梯度方法在连续控制任务中面临的挑战。PPO算法通过引入一个近似目…

OpenCV实现数字水印的相关函数和示例代码

OpenCV计算机视觉开发实践&#xff1a;基于Qt C - 商品搜索 - 京东 实现数字水印的相关函数 用OpenCV来实现数字水印功能&#xff0c;需要使用一些位操作函数&#xff0c;我们需要先了解一下这些函数。 1. bitwise_and函数 bitwise_and函数是OpenCV中的位运算函数之一&…

基于Python的计算机科学研究话题管理系统的设计与实现 - 爬虫

标题:基于Python的计算机科学研究话题管理系统的设计与实现 - 爬虫 内容:1.摘要 本文聚焦于基于Python的计算机科学研究话题管理系统的爬虫部分。背景是随着计算机科学研究的快速发展&#xff0c;相关话题数据海量且分散&#xff0c;人工管理效率低。目的是设计并实现一个能高…

告别手动解析!借助 CodeBuddy 快速开发网页源码提取工具

作为一名长期从事 Web 开发的程序员&#xff0c;我们在日常工作中&#xff0c;时不时会需要查看网页的源代码。这么做的目的通常是为了排查前端渲染的问题、分析接口返回的数据结构&#xff0c;或者就是单纯地想快速提取页面中的某些信息&#xff0c;比如文章链接、图片地址&am…

为什么要在 input() 后加 .strip()?

strip() 是 Python 字符串的一个方法&#xff0c;用于去除字符串开头和结尾的空白字符&#xff08;包括空格、制表符 \t、换行符 \n 等&#xff09;。 为什么要在 input() 后加 .strip()&#xff1f; 用户在输入时&#xff0c;可能会不小心在开头或结尾输入空格&#xff0c;例…

【日撸 Java 300行】Day 14(栈)

目录 Day 14&#xff1a;栈 一、栈的基本知识 二、栈的方法 1. 顺序表实现栈 2. 入栈 3. 出栈 三、代码及测试 拓展&#xff1a; 小结 Day 14&#xff1a;栈 Task&#xff1a; push 和 pop 均只能在栈顶操作.没有循环, 时间复杂度为 O(1). 一、栈的基本知识 详细的介…

dotnet core c#调用Linux c++导出函数

1.声明C++导出函数 platform_export.h // // Created by dev on 5/6/25. //#ifndef PLATFORM_EXPORT_H #define PLATFORM_EXPORT_H #if defined(_WIN32)#ifdef LIB_EXPORTS#define LIB_API __declspec(dllimport)#else#define LIB_API __declspec(dllimport)#endif #else#ifde…

SparkSQL操作Mysql

前面的课程我们学习了如何从csv文件中读入数据&#xff0c;这相当于是对csv这种类型的数据的操作。那么接下来&#xff0c;我们一起看看&#xff0c;如何写Spark程序来操作mysql数据库。先来给大家介绍一下我们这节课的主要学习内容&#xff1a; &#xff08;1&#xff09;安装…

语言学中的对象语言与元语言 | 概念 / 区别 / 实例分析

注&#xff1a;英文引文&#xff0c;机翻未校。 语言学中的“对象语言”和“元语言” 刘福长 现代外语 1989年第3期&#xff08;总第45期&#xff09; 在阅读语言学著作时&#xff0c;我们有时会遇到这样两个术语&#xff1a;对象语言&#xff08;object language&#xff0…