鸿蒙OSUniApp 移动端直播流播放实战:打造符合鸿蒙设计风格的播放器#三方框架 #Uniapp
UniApp 移动端直播流播放实战:打造符合鸿蒙设计风格的播放器
在移动互联网时代,直播已经成为一种主流的内容形式。本文将详细介绍如何使用 UniApp 框架开发一个优雅的直播流播放器,并融入鸿蒙系统的设计理念,实现一个既美观又实用的播放功能。
设计理念
在开发直播播放器之前,我们需要深入理解鸿蒙系统的设计哲学。鸿蒙系统强调"自然、统一、高效"的设计原则,这些特点将贯穿我们的整个功能实现过程:
- 自然:播放器的控制要符合用户的使用习惯
- 统一:视觉元素要保持一致性,符合系统设计规范
- 高效:确保流畅的播放体验和快速的加载速度
- 优雅:在各种状态下都保持良好的视觉表现
技术方案
在实现直播流播放功能时,我们需要考虑以下几个关键点:
- 播放器选型:使用原生组件
live-player
- 协议支持:RTMP、FLV、HLS 等主流直播协议
- 状态管理:加载中、播放中、错误等状态处理
- 性能优化:控制内存占用,优化渲染性能
- 交互设计:手势控制、全屏切换等
组件实现
1. 直播播放器组件
首先实现一个基础的直播播放器组件:
<!-- components/harmony-live-player/harmony-live-player.vue -->
<template><view class="harmony-live-player" :class="{ 'fullscreen': isFullscreen }"><live-playerclass="player":src="src":mode="mode":autoplay="autoplay":muted="isMuted":orientation="orientation":object-fit="objectFit"@statechange="handleStateChange"@netstatus="handleNetStatus"@error="handleError"ref="livePlayer"><!-- 播放器控制层 --><view class="player-controls" :class="{ 'controls-visible': showControls }"><view class="top-bar"><view class="left-area"><text class="title">{{ title }}</text></view><view class="right-area"><text class="online-count" v-if="onlineCount">{{ formatNumber(onlineCount) }}人观看</text></view></view><view class="bottom-bar"><view class="control-btn" @click="togglePlay"><text class="iconfont" :class="isPlaying ? 'icon-pause' : 'icon-play'"></text></view><view class="control-btn" @click="toggleMute"><text class="iconfont" :class="isMuted ? 'icon-mute' : 'icon-volume'"></text></view><view class="quality-switch" v-if="qualities.length > 1"><text v-for="item in qualities" :key="item.value":class="{ 'active': currentQuality === item.value }"@click="switchQuality(item.value)">{{ item.label }}</text></view><view class="control-btn" @click="toggleFullscreen"><text class="iconfont" :class="isFullscreen ? 'icon-fullscreen-exit' : 'icon-fullscreen'"></text></view></view></view><!-- 加载状态 --><view class="loading-layer" v-if="isLoading"><view class="loading-animation"></view><text class="loading-text">直播加载中...</text></view><!-- 错误状态 --><view class="error-layer" v-if="hasError"><text class="error-icon iconfont icon-error"></text><text class="error-text">{{ errorMessage }}</text><view class="retry-btn" @click="retry">重试</view></view></live-player></view>
</template><script>
const CONTROL_HIDE_TIMEOUT = 3000 // 控制栏自动隐藏时间export default {name: 'HarmonyLivePlayer',props: {src: {type: String,required: true},title: {type: String,default: ''},mode: {type: String,default: 'RTC' // RTC模式延迟更低},autoplay: {type: Boolean,default: true},qualities: {type: Array,default: () => []},onlineCount: {type: Number,default: 0}},data() {return {isPlaying: false,isMuted: false,isLoading: true,hasError: false,errorMessage: '',isFullscreen: false,showControls: true,controlTimer: null,currentQuality: '',objectFit: 'contain',orientation: 'vertical',netStatus: {}}},watch: {src: {handler(newVal) {if (newVal) {this.hasError = falsethis.errorMessage = ''this.isLoading = truethis.startPlay()}},immediate: true}},mounted() {// 初始化播放器this.initPlayer()// 监听触摸事件以显示/隐藏控制栏this.initTouchListeners()},beforeDestroy() {this.clearControlTimer()this.stopPlay()},methods: {initPlayer() {// 设置默认清晰度if (this.qualities.length > 0) {this.currentQuality = this.qualities[0].value}// 初始化播放状态if (this.autoplay) {this.startPlay()}},initTouchListeners() {const player = this.$refs.livePlayerif (!player) returnplayer.$el.addEventListener('touchstart', () => {this.toggleControls()})},startPlay() {const player = this.$refs.livePlayerif (!player) returnplayer.play({success: () => {this.isPlaying = truethis.isLoading = falsethis.startControlTimer()},fail: (err) => {this.handleError(err)}})},stopPlay() {const player = this.$refs.livePlayerif (!player) returnplayer.stop()this.isPlaying = false},togglePlay() {if (this.isPlaying) {this.stopPlay()} else {this.startPlay()}},toggleMute() {this.isMuted = !this.isMuted},toggleFullscreen() {const player = this.$refs.livePlayerif (!player) returnif (this.isFullscreen) {player.exitFullScreen({success: () => {this.isFullscreen = falsethis.orientation = 'vertical'}})} else {player.requestFullScreen({direction: 0,success: () => {this.isFullscreen = truethis.orientation = 'horizontal'}})}},switchQuality(quality) {if (this.currentQuality === quality) returnthis.currentQuality = qualitythis.$emit('quality-change', quality)},handleStateChange(e) {const state = e.detail.codeswitch (state) {case 2001: // 已经连接this.isLoading = falsethis.isPlaying = truebreakcase 2002: // 已经开始播放this.isLoading = falsebreakcase 2103: // 网络断连this.handleError({ errMsg: '网络连接断开' })break}},handleNetStatus(e) {this.netStatus = e.detailthis.$emit('net-status', e.detail)},handleError(e) {this.hasError = truethis.isPlaying = falsethis.isLoading = falsethis.errorMessage = e.errMsg || '播放出错,请重试'this.$emit('error', e)},retry() {this.hasError = falsethis.errorMessage = ''this.isLoading = truethis.startPlay()},toggleControls() {this.showControls = !this.showControlsif (this.showControls) {this.startControlTimer()}},startControlTimer() {this.clearControlTimer()this.controlTimer = setTimeout(() => {this.showControls = false}, CONTROL_HIDE_TIMEOUT)},clearControlTimer() {if (this.controlTimer) {clearTimeout(this.controlTimer)this.controlTimer = null}},formatNumber(num) {if (num >= 10000) {return (num / 10000).toFixed(1) + 'w'}return num.toString()}}
}
</script><style lang="scss">
.harmony-live-player {position: relative;width: 100%;background-color: #000000;&.fullscreen {position: fixed;top: 0;left: 0;right: 0;bottom: 0;z-index: 999;}.player {width: 100%;height: 100%;}.player-controls {position: absolute;top: 0;left: 0;right: 0;bottom: 0;background: linear-gradient(to bottom,rgba(0, 0, 0, 0.3) 0%,transparent 40%,transparent 60%,rgba(0, 0, 0, 0.3) 100%);opacity: 0;transition: opacity 0.3s ease;&.controls-visible {opacity: 1;}.top-bar {position: absolute;top: 0;left: 0;right: 0;height: 88rpx;padding: 0 32rpx;display: flex;align-items: center;justify-content: space-between;.title {color: #ffffff;font-size: 32rpx;font-weight: 500;}.online-count {color: #ffffff;font-size: 24rpx;opacity: 0.8;}}.bottom-bar {position: absolute;bottom: 0;left: 0;right: 0;height: 88rpx;padding: 0 32rpx;display: flex;align-items: center;.control-btn {width: 80rpx;height: 80rpx;display: flex;align-items: center;justify-content: center;.iconfont {color: #ffffff;font-size: 48rpx;}&:active {opacity: 0.7;}}.quality-switch {flex: 1;display: flex;justify-content: center;gap: 24rpx;text {color: #ffffff;font-size: 24rpx;padding: 8rpx 16rpx;border-radius: 32rpx;background-color: rgba(255, 255, 255, 0.2);&.active {background-color: #2979ff;}&:active {opacity: 0.7;}}}}}.loading-layer {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);display: flex;flex-direction: column;align-items: center;.loading-animation {width: 64rpx;height: 64rpx;border: 4rpx solid rgba(255, 255, 255, 0.1);border-top-color: #ffffff;border-radius: 50%;animation: spin 1s linear infinite;}.loading-text {color: #ffffff;font-size: 28rpx;margin-top: 16rpx;}}.error-layer {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);display: flex;flex-direction: column;align-items: center;.error-icon {font-size: 80rpx;color: #ffffff;margin-bottom: 16rpx;}.error-text {color: #ffffff;font-size: 28rpx;margin-bottom: 32rpx;}.retry-btn {padding: 16rpx 48rpx;background-color: #2979ff;color: #ffffff;font-size: 28rpx;border-radius: 44rpx;&:active {opacity: 0.9;}}}
}@keyframes spin {to {transform: rotate(360deg);}
}
</style>
2. 使用示例
下面展示如何在页面中使用直播播放器组件:
<!-- pages/live/live.vue -->
<template><view class="live-page"><harmony-live-player:src="liveUrl":title="liveInfo.title":qualities="qualities":online-count="liveInfo.onlineCount"@error="handlePlayError"@quality-change="handleQualityChange"@net-status="handleNetStatus"></harmony-live-player><view class="live-info"><view class="anchor-info"><image class="avatar" :src="liveInfo.anchorAvatar"></image><view class="info-content"><text class="anchor-name">{{ liveInfo.anchorName }}</text><text class="fans-count">{{ formatNumber(liveInfo.fansCount) }}粉丝</text></view><button class="follow-btn" :class="{ 'followed': isFollowed }"@click="toggleFollow">{{ isFollowed ? '已关注' : '关注' }}</button></view><view class="live-stats"><view class="stat-item"><text class="label">观看</text><text class="value">{{ formatNumber(liveInfo.viewCount) }}</text></view><view class="stat-item"><text class="label">点赞</text><text class="value">{{ formatNumber(liveInfo.likeCount) }}</text></view><view class="stat-item"><text class="label">分享</text><text class="value">{{ formatNumber(liveInfo.shareCount) }}</text></view></view></view></view>
</template><script>
import HarmonyLivePlayer from '@/components/harmony-live-player/harmony-live-player'export default {components: {HarmonyLivePlayer},data() {return {liveUrl: '',qualities: [{ label: '标清', value: 'SD' },{ label: '高清', value: 'HD' },{ label: '超清', value: 'FHD' }],liveInfo: {title: '直播标题',anchorName: '主播昵称',anchorAvatar: '/static/default-avatar.png',onlineCount: 0,viewCount: 0,likeCount: 0,shareCount: 0,fansCount: 0},isFollowed: false}},onLoad(options) {// 获取直播信息this.fetchLiveInfo(options.id)},methods: {async fetchLiveInfo(id) {try {// 这里替换为实际的API调用const response = await this.$api.getLiveInfo(id)this.liveInfo = response.datathis.liveUrl = response.data.playUrl} catch (error) {uni.showToast({title: '获取直播信息失败',icon: 'none'})}},handlePlayError(error) {console.error('播放错误:', error)uni.showToast({title: '播放出错,请重试',icon: 'none'})},handleQualityChange(quality) {console.log('切换清晰度:', quality)// 这里可以根据清晰度切换不同的播放地址},handleNetStatus(status) {console.log('网络状态:', status)// 这里可以根据网络状态做相应处理},toggleFollow() {this.isFollowed = !this.isFollowed// 调用关注/取消关注API},formatNumber(num) {if (num >= 10000) {return (num / 10000).toFixed(1) + 'w'}return num.toString()}}
}
</script><style lang="scss">
.live-page {min-height: 100vh;background-color: #f5f5f5;.live-info {padding: 32rpx;background-color: #ffffff;.anchor-info {display: flex;align-items: center;margin-bottom: 32rpx;.avatar {width: 88rpx;height: 88rpx;border-radius: 44rpx;margin-right: 24rpx;}.info-content {flex: 1;.anchor-name {font-size: 32rpx;color: #333333;font-weight: 500;margin-bottom: 8rpx;}.fans-count {font-size: 24rpx;color: #999999;}}.follow-btn {padding: 16rpx 48rpx;background-color: #2979ff;color: #ffffff;font-size: 28rpx;border-radius: 44rpx;&.followed {background-color: #f5f5f5;color: #666666;}&:active {opacity: 0.9;}}}.live-stats {display: flex;justify-content: space-around;padding: 24rpx 0;border-top: 2rpx solid #f5f5f5;.stat-item {display: flex;flex-direction: column;align-items: center;.label {font-size: 24rpx;color: #999999;margin-bottom: 8rpx;}.value {font-size: 32rpx;color: #333333;font-weight: 500;}}}}
}
</style>
性能优化
- 预加载优化
为了提升直播加载速度,我们可以在进入直播间前预加载资源:
// utils/preload.js
export const preloadLiveStream = (url) => {return new Promise((resolve, reject) => {const context = uni.createLivePlayerContext('preload-player')context.play({success: () => {setTimeout(() => {context.stop()resolve()}, 100)},fail: reject})})
}
- 状态管理优化
使用 Vuex 管理直播相关状态:
// store/modules/live.js
export default {state: {currentLive: null,quality: 'HD',netStatus: {}},mutations: {SET_CURRENT_LIVE(state, live) {state.currentLive = live},SET_QUALITY(state, quality) {state.quality = quality},UPDATE_NET_STATUS(state, status) {state.netStatus = status}},actions: {async enterLive({ commit }, liveId) {try {const live = await api.getLiveInfo(liveId)commit('SET_CURRENT_LIVE', live)return live} catch (error) {throw error}}}
}
- 内存优化
在离开直播页面时,及时释放资源:
export default {onUnload() {// 停止播放this.$refs.livePlayer.stopPlay()// 清理定时器this.clearAllTimers()// 重置状态this.$store.commit('SET_CURRENT_LIVE', null)}
}
适配建议
- 网络适配
根据网络状况自动调整清晰度:
handleNetStatus(status) {if (status.netSpeed < 1000 && this.currentQuality !== 'SD') {// 网速较慢时切换到标清this.switchQuality('SD')} else if (status.netSpeed > 5000 && this.currentQuality === 'SD') {// 网速较快时切换到高清this.switchQuality('HD')}
}
- 横竖屏适配
.harmony-live-player {&.fullscreen {.player-controls {.bottom-bar {padding: 0 48rpx;height: 120rpx;}.quality-switch {text {font-size: 28rpx;padding: 12rpx 24rpx;}}}}
}
总结
通过本文的讲解,我们实现了一个功能完整的移动端直播播放器。该播放器不仅具有优雅的界面设计,还融入了鸿蒙系统的设计理念,同时考虑了性能优化和多端适配等实际开发中的重要因素。
在实际开发中,我们还可以根据具体需求扩展更多功能:
- 弹幕系统
- 礼物特效
- 互动游戏
- 直播回放
- 防盗链处理
希望这篇文章能够帮助你更好地理解如何在 UniApp 中开发高质量的直播功能,同时也能为你的实际项目开发提供有价值的参考。
相关文章:
鸿蒙OSUniApp 移动端直播流播放实战:打造符合鸿蒙设计风格的播放器#三方框架 #Uniapp
UniApp 移动端直播流播放实战:打造符合鸿蒙设计风格的播放器 在移动互联网时代,直播已经成为一种主流的内容形式。本文将详细介绍如何使用 UniApp 框架开发一个优雅的直播流播放器,并融入鸿蒙系统的设计理念,实现一个既美观又实用…...

C3、C2f、C3K2、C2PSA的具体结构
YOLOV5 C3 Bottleneck C2f...

2_MCU开发环境搭建-配置MDK兼容Keil4和C51
MCU开发环境搭建-配置MDK兼容Keil4和C51 一、概述 本文以MDK-ARM V5.36版本基础介绍DMK-ARM工程兼容Keil4和C51的配置。 注:在阅读本文前,请先安装和配置完成MDK-ARM(Keil5)。 二、工具包下载 链接: https://pan.baidu.com/s/1Tu2tDD6zRra4xb_PuA1Wsw 提取码: 81pp 三、…...

通过远程桌面连接Windows实例提示“出现身份验证错误,无法连接到本地安全机构”错误怎么办?
本文介绍通过远程桌面连接Windows实例提示“出现身份验证错误无法连接到本地安全机构”错误的解决方案。 问题现象 通过本地电脑内的远程桌面连接Windows实例提示“出现身份验证错误,无法连接到本地安全机构”错误。 问题原因 导致该问题的可能原因如下&#x…...

百度golang研发一面面经
输入一个网址,到显示界面,中间的过程是怎样的 IP 报文段的结构是什么 Innodb 的底层结构 知道几种设计模式 工厂模式 简单工厂模式:根据传入类型参数判断创建哪种类型对象工厂方法模式:由子类决定实例化哪个类抽象工厂模式&#…...

TC3xx学习笔记-启动过程详解(一)
文章目录 前言Firmware启动过程BMHD Check流程ABM启动Internal Flash启动Bootloader ModeProcessing in case no valid BMHD foundProcessing in case no Boot Mode configured by SSW 总结 前言 之前介绍过UCB BMHD的使用,它在启动过程中起着重要的作用࿰…...

Scratch节日 | 六一儿童节抓糖果
六一儿童节怎么能没有糖果?这款 六一儿童节抓糖果 小游戏,让你变身小猫,开启一场甜蜜大作战! 🎮 游戏玩法 帮助小猫收集所有丢失的糖果,收集越多分数越高! 小心虫子一样的“坏糖果”ÿ…...
系统调用与程序接口的关系
程序接口类型 系统调用:是操作系统提供给应用程序的接口 ,允许应用程序请求操作系统执行特定操作,像文件操作(打开、读写、关闭文件 )、进程管理(创建、终止进程 )、设备管理(操作磁…...
从线性方程组角度理解公式 s=n−r(3E−A)
从线性方程组角度理解公式 sn−r(3E−A) 这个公式本质上是 齐次线性方程组解空间维度 的直接体现。下面通过三个关键步骤解释其在线性方程组中的含义: 1. 公式对应的线性方程组 考虑矩阵方程: (3E−A)x0 其中: x 是 n 维未知向量3E−…...

通信算法之280:无人机侦测模块知识框架思维导图
1. 无人机侦测模块知识框架思维导图, 见文末章节。 2. OFDM参数估计,基于循环自相关特性。 3. 无人机其它参数估计...

【Doris基础】Apache Doris中的Coordinator节点作用详解
目录 1 Doris架构概述 2 Coordinator节点的核心作用 2.1 查询协调与调度 2.2 执行计划生成与优化 2.3 资源管理与负载均衡 2.4 容错与故障恢复 3 Coordinator节点的关键实现机制 3.1 两阶段执行模型 3.2 流水线执行引擎 3.3 分布式事务管理 4 Coordinator节点的高可…...
软考 系统架构设计师之考试感悟3
接前一篇文章:软考 系统架构设计师之考试感悟2 上周六(2025年5月24日),本人第三次参加了软考系统架构师的考试。和前两次一样,考了一天,身心俱疲。不过这次感觉比上一次要稍微好点,可能也是考的…...

【Kubernetes-1.30】--containerd部署
文章目录 一、环境准备1.1 三台服务器1.2 基础配置(三台机通用)1.3 关闭 Swap(必须)1.4 关闭防火墙(可选)1.5 加载必要模块 & 配置内核参数 二、安装容器运行时(containerd 推荐)…...
Flutter 嵌套H5 传参数
你可以通过在加载 H5 页面时,将 token 作为 URL 参数拼接,或者通过 WebView 的 JavaScript 通信功能(如 runJavaScript 或 addJavaScriptChannel)传递 token。常用方式如下: 方式一:URL 拼接参数 假设你的…...
什么是线程上下文切换?
导语: 线程上下文切换(Context Switch)是Java并发编程中一个常见但容易被忽视的概念。在高并发场景下,它直接影响系统性能。本文将从面试官角度深入剖析这个话题,帮你理解底层原理、掌握优化思路、规避项目中的常见陷阱…...
Jvm 元空间大小分配原则
JVM元空间(Metaspace)的大小分配原则与系统物理内存密切相关,但并不是直接等比例分配,而是通过一系列参数和JVM的动态管理机制来确定。下面从原理和实际行为两方面详细说明: 1. 元空间(Metaspace࿰…...

相机--相机标定
教程 内外参公式及讲解 相机标定分类 相机标定分为内参标定和外参标定。 相机成像原理 相机成像畸变 链接 四个坐标系的转变 内参标定 内参 相机内参通常用一个 33 矩阵(内参矩阵,KK)表示,形式如下: (1)焦距&…...

MongoDB(七) - MongoDB副本集安装与配置
文章目录 前言一、下载MongoDB1. 下载MongoDB2. 上传安装包3. 创建相关目录 二、安装配置MongoDB1. 解压MongoDB安装包2. 重命名MongoDB文件夹名称3. 修改配置文件4. 分发MongoDB文件夹5. 配置环境变量6. 启动副本集7. 进入MongoDB客户端8. 初始化副本集8.1 初始化副本集8.2 添…...

131. 分割回文串-两种回溯思路
我们可以将字符串分割成若干回文子串,返回所有可能的方案。如果将问题分解,可以表示为分割长度为n-1的子字符串,这与原问题性质相同,因此可以采用递归方法解决。 为什么回溯与递归存在联系?在解决这个问题时࿰…...

[Java恶补day13] 53. 最大子数组和
休息了一天,开始补上! 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组是数组中的一个连续部分。 示例 1: 输入:nums …...

摩尔投票算法原理实现一文剖析
摩尔投票算法原理&实现一文剖析 一、算法原理1.1 基本思想1.2 数学原理 二、算法实现2.1 Python实现2.2 Java实现2.3 C实现 三、复杂度分析四、应用场景4.1 多数元素问题4.2 扩展应用:寻找出现次数超过n/3的元素 五、算法优势与注意事项5.1 优势5.2 注意事项 总…...
springboot项目下面的单元测试注入的RedisConnectionFactory类redisConnectionFactory值为什么为空呢?
你遇到的问题是: RedisConnectionFactory redisConnectionFactory 在单元测试中为 null 这是 Spring Boot 单元测试中非常常见的问题,根本原因是你的测试类没有启用 Spring 容器上下文,导致 Resource 注解无法注入 Bean。 ✅ 正确做法&…...

MyBatis操作数据库(2)
1.#{}和${}使用 Interger类型的参数可以看到这里显示的语句是:select username,password,age,gender,phone from userinfo where id? 输入的参数并没有在后面进行拼接,,id的值是使用?进行占位,这种sql称之为"预编译sql".这里,把#{}改成${}观察情况:这里可以看到…...

C++面向对象(二)
面向对象基础内容参考: C面向对象(一)-CSDN博客 友元函数 类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定…...

【C语言入门级教学】冒泡排序和指针数组
文章目录 1.冒泡排序2.⼆级指针3.指针数组4.指针数组模拟⼆维数组 1.冒泡排序 冒泡排序的核⼼思想:两两相邻的元素进⾏⽐较。 //⽅法1 void bubble_sort(int arr[], int sz)//参数接收数组元素个数 { int i 0;for(i0; i-1; i) { int j 0; for(j0; j-1; j) { …...

shell脚本中常用的命令
一、设置主机名称 通过文件的方式修改通过命令修改 二、nmcli 查看网卡 ip a s ens160 (网卡名称) ifconfig ens160 nmcli device show ens160 nmcli device status nmcli connection show ens160 2.设置网卡 a)当网卡没有被设置时 b)网卡被设定,需要修改 三…...

Nuxt3部署
最近接了一个项目,需要用到 nuxt3 技术来满足甲方所要求的需求,在部署的时候遇到了很多问题,这里我一一给大家讲述部署流程,以及所遇到的坑 打包部署 部署分为俩种方式: 静态(spa)部署 和 ssr部署 静态部署 静态部…...

网络攻防技术一:绪论
文章目录 一、网络空间CyberSpace1、定义2、基本四要素 二、网络空间安全1、定义2、保护对象3、安全属性4、作用空间 三、网络攻击1、攻击分类2、攻击过程 四、网络防护1、定义2、安全模型3、安全服务5类4、特定安全机制8种5、普遍性安全机制5种 五、网络安全技术发展简史1、第…...

【人工智能】deepseek七篇论文阅读笔记大纲
七篇文章看了整整五天,加上整理笔记和问ds优化,大致的框架是有了。具体的公式细节比较多,截图也比较麻烦,就不列入大纲去做笔记了。 DeepSeek-LLM:一切的起点,所以探索的东西比较多,包括&#x…...
unix/linux source 命令,在当前的 Shell 会话中读取并执行指定文件中的命令
source 命令 (或者它的POSIX等效命令 .):在当前 Shell 环境中执行脚本 简单来说,source 命令的作用是:在当前的 Shell 会话中读取并执行指定文件中的命令。 这意味着,被 source 执行的脚本中的所有命令,就好像是你直接在当前的命令行提示符下逐行输入并执行的一样。 核…...