uniapp 打包app wgt热更新和整包更新以及更新弹窗动画

news2024/12/24 2:16:37

app热更新是app项目最常见的功能,接下来我总结了当时做这个功能的过程,来交流学习一哈

热更新的流程步骤

  • 在用户进入app就获取当前版本号与调用后端接口返回的版本号对比是否是最新的版本
  • 不是最新弹出弹窗让用户确认是否更新,点击更新下载wgt资源包更新,重启应用即可
    在这里插入图片描述

代码流程

1.封装两个判断js文件

  • call-check-version.js 判断是否需要更新
	import { getVersion } from '@/api/user.js' // 获取最新版本信息的接口
/**
	 * 对比版本号,如需要,请自行修改判断规则
	 * 支持比对	("3.0.0.0.0.1.0.1", "3.0.0.0.0.1")	("3.0.0.1", "3.0")	("3.1.1", "3.1.1.1") 之类的
	 * @param {Object} v1
	 * @param {Object} v2
	 * v1 > v2 return 1
	 * v1 < v2 return -1
	 * v1 == v2 return 0
	 */
	function compare(v1 = '0', v2 = '0') {
		v1 = String(v1).split('.')
		v2 = String(v2).split('.')
		const minVersionLens = Math.min(v1.length, v2.length)

		let result = 0
		for (let i = 0; i < minVersionLens; i++) {
			const curV1 = Number(v1[i])
			const curV2 = Number(v2[i])

			if (curV1 > curV2) {
				result = 1
				break
			} else if (curV1 < curV2) {
				result = -1
				break
			}
		}

		if (result === 0 && (v1.length !== v2.length)) {
			const v1BiggerThenv2 = v1.length > v2.length
			const maxLensVersion = v1BiggerThenv2 ? v1 : v2
			for (let i = minVersionLens; i < maxLensVersion.length; i++) {
				const curVersion = Number(maxLensVersion[i])
				if (curVersion > 0) {
					v1BiggerThenv2 ? result = 1 : result = -1
					break
				}
			}
		}

		return result
	}
export default function() {
	// #ifdef APP-PLUS
	return new Promise((resolve, reject) => {
		plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
			const data = {
				action: 'checkVersion',
				appid: plus.runtime.appid,
				appVersion: plus.runtime.version, // 当前版本
				wgtVersion: widgetInfo.version // wgt版本
			}
			// console.log("data: ",data);
			getVersion().then(res => {
				// let {appVersionPackage,app_version}=res.data.list
				// let result={
				// 	code:1,
				// 	url:appVersionPackage,
				// 	version:app_version,
				// 	platform:'Android',
				// 	contents:'1.修改头像\n2.分享内容', //更新内容
				// 	is_mandatory:false, //强制更新
				// 	is_silently:0, //静默更新 1是 0否
				// 	type:'apk' //wgt/apk
				// }
				// console.log(result)
				let v = compare(res.data.info.version, data.appVersion)
				let w = compare(res.data.info.version, data.wgtVersion)
				if (v == 1 && w == 1) {
					// 同时大于当前版本和wgt版本,则需要更新
					res.data.info.code = 2
				} else {
					//不用更新
					res.data.info.code = -1
				}
				resolve({ result: res.data.info })
			}).catch(() => reject())
			// uniCloud.callFunction({
			// 	name: 'uni-upgrade-center',
			// 	data,
			// 	success: (e) => {
			// 		console.log("e: ", e);
			// 		resolve(e)
			// 	},
			// 	fail: (error) => {
			// 		reject(error)
			// 	}
			// })
		})
	})
	// #endif
	// #ifndef APP-PLUS
	return new Promise((resolve, reject) => {
		reject({ message: '请在App中使用' })
	})
	// #endif
}

  • check-update.js 更新方式选择(弹窗)
import callCheckVersion from './call-check-version'

// 推荐再App.vue中使用
const PACKAGE_INFO_KEY = '__package_info__'

export default function() {
	// #ifdef APP-PLUS
	return new Promise((resolve, reject) => {
		callCheckVersion().then(async (e) => {
			if (!e.result) return;
			const {
				code,
				is_silently, // 是否静默更新
				url, // 安装包下载地址
				platform, // 安装包平台
				type // 安装包类型
			} = e.result;
			if(code<0){
				
				reject(null)
				return
			}
			// 此处逻辑仅为实例,可自行编写
				resolve(e)

				// 静默更新,只有wgt有
				if (is_silently) {
					uni.downloadFile({
						url: e.result.url,
						success: res => {
							if (res.statusCode == 200) {
								// 下载好直接安装,下次启动生效
								plus.runtime.install(res.tempFilePath, {
									force: false
								});
							}
						}
					});
					return;
				}

				/**
				 * 提示升级一
				 * 使用 uni.showModal
				 */
				// return updateUseModal(e.result)

				/**
				 * 提示升级二
				 * 官方适配的升级弹窗,可自行替换资源适配UI风格
				 */
				uni.setStorageSync(PACKAGE_INFO_KEY, e.result)
				uni.navigateTo({
					url: `/uni_modules/pages/upgrade-popup?local_storage_key=${PACKAGE_INFO_KEY}`, // 更新弹窗页面
					fail: (err) => {
						console.error('更新弹框跳转失败', err)
						uni.removeStorageSync(PACKAGE_INFO_KEY)
					}
				})
				
				return
			
			return resolve(e)
		}).catch(err => {
			// TODO 云函数报错处理
			console.error(err.message)
			reject(err)
		})
	});
	// #endif
}

/**
 * 使用 uni.showModal 升级
 */
function updateUseModal(packageInfo) {
	const {
		title, // 标题
		contents, // 升级内容
		is_mandatory, // 是否强制更新
		url, // 安装包下载地址
		platform, // 安装包平台
		type // 安装包类型
	} = packageInfo;

	let isWGT = type === 'wgt'
	let isiOS = !isWGT ? platform.includes('iOS') : false;
	let confirmText = isiOS ? '立即跳转更新' : '立即下载更新'

	return uni.showModal({
		title,
		content: contents,
		showCancel: !is_mandatory,
		confirmText,
		success: res => {
			if (res.cancel) return;

			// 安装包下载
			if (isiOS) {
				plus.runtime.openURL(url);
				return;
			}

			uni.showToast({
				title: '后台下载中……',
				duration: 1000
			});

			// wgt 和 安卓下载更新
			downloadTask = uni.downloadFile({
				url,
				success: res => {
					if (res.statusCode !== 200) {
						console.error('下载安装包失败', err);
						return;
					}
					// 下载好直接安装,下次启动生效
					plus.runtime.install(res.tempFilePath, {
						force: false
					}, () => {
						if (is_mandatory) {
							//更新完重启app
							plus.runtime.restart();
							return;
						}
						uni.showModal({
							title: '安装成功是否重启?',
							success: res => {
								if (res.confirm) {
									//更新完重启app
									plus.runtime.restart();
								}
							}
						});
					}, err => {
						uni.showModal({
							title: '更新失败',
							content: err
								.message,
							showCancel: false
						});
					});
				}
			});
		}
	});
}

  • 我的目录结构,你们可以根据自己的习惯更改,记得修改文件里的引用路劲
    在这里插入图片描述

2.弹窗界面效果

  • upgrade-popup.vue 弹窗动画
<template>
	<view class="mask flex-center">
		<view class="content botton-radius">
			<view class="content-top">
				<text class="content-top-text">{{title}}</text>
				<image class="content-top" style="top: 0;" width="100%" height="100%" src="../images/bg_top.png">
				</image>
			</view>
			<view class="content-header"></view>
			<view class="content-body">
				<view class="title">
					<text>{{subTitle}}</text>
					<!-- <text style="padding-left:20rpx;font-size: 0.5em;color: #666;">v.{{version}}</text> -->
				</view>
				<view class="body">
					<scroll-view class="box-des-scroll" scroll-y="true">
						<text class="box-des">
							{{contents}}
						</text>
					</scroll-view>
				</view>
				<view class="footer flex-center">
					<template v-if="isAppStore">
						<button class="content-button" style="border: none;color: #fff;" plain @click="jumpToAppStore">
							{{downLoadBtnTextiOS}}
						</button>
					</template>
					<template v-else>
						<template v-if="!downloadSuccess">
							<view class="progress-box flex-column" v-if="downloading">
								<progress class="progress" border-radius="35" :percent="downLoadPercent"
									activeColor="#3DA7FF" show-info stroke-width="10" />
								<view style="width:100%;font-size: 28rpx;display: flex;justify-content: space-around;">
									<text>{{downLoadingText}}</text>
									<text>({{downloadedSize}}/{{packageFileSize}}M)</text>
								</view>
							</view>

							<button v-else class="content-button" style="border: none;color: #fff;" plain
								@click="updateApp">
								{{downLoadBtnText}}
							</button>
						</template>
						<button v-else-if="downloadSuccess && !installed" class="content-button"
							style="border: none;color: #fff;" plain :loading="installing" :disabled="installing"
							@click="installPackage">
							{{installing ? '正在安装……' : '下载完成,立即安装'}}
						</button>

						<button v-if="installed && isWGT" class="content-button" style="border: none;color: #fff;" plain
							@click="restart">
							安装完毕,点击重启
						</button>
					</template>
				</view>
			</view>

			<image v-if="!is_mandatory" class="close-img" src="../images/app_update_close.png"
				@click.stop="closeUpdate"></image>
		</view>
	</view>
</template>

<script>
	const localFilePathKey = 'UNI_ADMIN_UPGRADE_CENTER_LOCAL_FILE_PATH'
	const platform_iOS = 'iOS';
	let downloadTask = null;
	let openSchemePromise

	/**
	 * 对比版本号,如需要,请自行修改判断规则
	 * 支持比对	("3.0.0.0.0.1.0.1", "3.0.0.0.0.1")	("3.0.0.1", "3.0")	("3.1.1", "3.1.1.1") 之类的
	 * @param {Object} v1
	 * @param {Object} v2
	 * v1 > v2 return 1
	 * v1 < v2 return -1
	 * v1 == v2 return 0
	 */
	function compare(v1 = '0', v2 = '0') {
		v1 = String(v1).split('.')
		v2 = String(v2).split('.')
		const minVersionLens = Math.min(v1.length, v2.length);

		let result = 0;
		for (let i = 0; i < minVersionLens; i++) {
			const curV1 = Number(v1[i])
			const curV2 = Number(v2[i])

			if (curV1 > curV2) {
				result = 1
				break;
			} else if (curV1 < curV2) {
				result = -1
				break;
			}
		}

		if (result === 0 && (v1.length !== v2.length)) {
			const v1BiggerThenv2 = v1.length > v2.length;
			const maxLensVersion = v1BiggerThenv2 ? v1 : v2;
			for (let i = minVersionLens; i < maxLensVersion.length; i++) {
				const curVersion = Number(maxLensVersion[i])
				if (curVersion > 0) {
					v1BiggerThenv2 ? result = 1 : result = -1
					break;
				}
			}
		}

		return result;
	}

	export default {
		data() {
			return {
				// 从之前下载安装
				installForBeforeFilePath: '',

				// 安装
				installed: false,
				installing: false,

				// 下载
				downloadSuccess: false,
				downloading: false,

				downLoadPercent: 0,
				downloadedSize: 0,
				packageFileSize: 0,

				tempFilePath: '', // 要安装的本地包地址

				// 默认安装包信息
				title: '更新日志',
				contents: '',
				is_mandatory: false,

				// 可自定义属性
				subTitle: '发现新版本',
				downLoadBtnTextiOS: '立即跳转更新',
				downLoadBtnText: '立即下载更新',
				downLoadingText: '安装包下载中,请稍后'
			}
		},
		onLoad({
			local_storage_key
		}) {
			if (!local_storage_key) {
				console.error('local_storage_key为空,请检查后重试')
				uni.navigateBack()
				return;
			};

			const localPackageInfo = uni.getStorageSync(local_storage_key);
			if (!localPackageInfo) {
				console.error('安装包信息为空,请检查后重试')
				uni.navigateBack()
				return;
			};

			const requiredKey = ['version', 'url', 'type']
			for (let key in localPackageInfo) {
				if (requiredKey.indexOf(key) !== -1 && !localPackageInfo[key]) {
					console.error(`参数 ${key} 必填,请检查后重试`)
					uni.navigateBack()
					return;
				}
			}

			Object.assign(this, localPackageInfo)
			this.checkLocalStoragePackage()
		},
		onBackPress() {
			// 强制更新不允许返回
			if (this.is_mandatory) {
				return true
			}

			downloadTask && downloadTask.abort()
		},
		onHide() {
			openSchemePromise = null
		},
		computed: {
			isWGT() {
				return this.type === 'wgt'
			},
			isiOS() {
				return !this.isWGT ? this.platform.includes(platform_iOS) : false;
			},
			isAppStore() {
				return this.isiOS || (!this.isiOS && !this.isWGT && this.url.indexOf('.apk') === -1)
			}
		},
		methods: {
			checkLocalStoragePackage() {
				// 如果已经有下载好的包,则直接提示安装
				const localFilePathRecord = uni.getStorageSync(localFilePathKey)
				if (localFilePathRecord) {
					const {
						version,
						savedFilePath,
						installed
					} = localFilePathRecord

					// 比对版本
					if (!installed && compare(version, this.version) === 0) {
						this.downloadSuccess = true;
						this.installForBeforeFilePath = savedFilePath;
						this.tempFilePath = savedFilePath
					} else {
						// 如果保存的包版本小 或 已安装过,则直接删除
						this.deleteSavedFile(savedFilePath)
					}
				}
			},
			async closeUpdate() {
				console.log('关闭更新')
				if (this.downloading) {
					if (this.is_mandatory) {
						return uni.showToast({
							title: '下载中,请稍后……',
							icon: 'none',
							duration: 500
						})
					}
					uni.showModal({
						title: '是否取消下载?',
						cancelText: '否',
						confirmText: '是',
						success: res => {
							if (res.confirm) {
								downloadTask && downloadTask.abort()
								uni.navigateBack()
							}
						}
					});
					return;
				}

				if (this.downloadSuccess && this.tempFilePath) {
					// 包已经下载完毕,稍后安装,将包保存在本地
					await this.saveFile(this.tempFilePath, this.version)
					uni.navigateBack()
					return;
				}

				uni.navigateBack()
				// this.$navTo('/pages/index/index')
			},
			updateApp() {
				this.checkStoreScheme().catch(() => {
					this.downloadPackage()
				})
			},
			// 跳转应用商店
			checkStoreScheme() {
				const storeList = (this.store_list || []).filter(item => item.enable)
				if (storeList && storeList.length) {
					storeList
						.sort((cur, next) => next.priority - cur.priority)
						.map(item => item.scheme)
						.reduce((promise, cur, curIndex) => {
							openSchemePromise = (promise || (promise = Promise.reject())).catch(() => {
								return new Promise((resolve, reject) => {
									plus.runtime.openURL(cur, (err) => {
										reject(err)
									})
								})
							})
							return openSchemePromise
						}, openSchemePromise)
					return openSchemePromise
				}

				return Promise.reject()
			},
			downloadPackage() {
				this.downloading = true;

				//下载包
				downloadTask = uni.downloadFile({
					url: this.url,
					success: res => {
						if (res.statusCode == 200) {
							this.downloadSuccess = true;
							this.tempFilePath = res.tempFilePath

							// 强制更新,直接安装
							if (this.is_mandatory) {
								this.installPackage();
							}
						}
					},
					complete: () => {
						this.downloading = false;

						this.downLoadPercent = 0
						this.downloadedSize = 0
						this.packageFileSize = 0

						downloadTask = null;
					}
				});

				downloadTask.onProgressUpdate(res => {
					this.downLoadPercent = res.progress;
					this.downloadedSize = (res.totalBytesWritten / Math.pow(1024, 2)).toFixed(2);
					this.packageFileSize = (res.totalBytesExpectedToWrite / Math.pow(1024, 2)).toFixed(2);
				});
			},
			installPackage() {
				// #ifdef APP-PLUS
				// wgt资源包安装
				if (this.isWGT) {
					this.installing = true;
				}
				plus.runtime.install(this.tempFilePath, {
					force: false
				}, async res => {
					this.installing = false;
					this.installed = true;

					// wgt包,安装后会提示 安装成功,是否重启
					if (this.isWGT) {
						// 强制更新安装完成重启
						if (this.is_mandatory) {
							uni.showLoading({
								icon: 'none',
								title: '安装成功,正在重启……'
							})

							setTimeout(() => {
								uni.hideLoading()
								this.restart();
							}, 1000)
						}
					} else {
						const localFilePathRecord = uni.getStorageSync(localFilePathKey)
						uni.setStorageSync(localFilePathKey, {
							...localFilePathRecord,
							installed: true
						})
					}
				}, async err => {
					// 如果是安装之前的包,安装失败后删除之前的包
					if (this.installForBeforeFilePath) {
						await this.deleteSavedFile(this.installForBeforeFilePath)
						this.installForBeforeFilePath = '';
					}

					// 安装失败需要重新下载安装包
					this.installing = false;
					this.installed = false;

					uni.showModal({
						title: '更新失败,请重新下载',
						content: err.message,
						showCancel: false
					});
				});

				// 非wgt包,安装跳出覆盖安装,此处直接返回上一页
				if (!this.isWGT && !this.is_mandatory) {
					uni.navigateBack()
				}
				// #endif
			},
			restart() {
				this.installed = false;
				// #ifdef APP-PLUS
				//更新完重启app
				plus.runtime.restart();
				// #endif
			},
			saveFile(tempFilePath, version) {
				return new Promise((resolve, reject) => {
					uni.saveFile({
						tempFilePath,
						success({
							savedFilePath
						}) {
							uni.setStorageSync(localFilePathKey, {
								version,
								savedFilePath
							})
						},
						complete() {
							resolve()
						}
					})
				})
			},
			deleteSavedFile(filePath) {
				uni.removeStorageSync(localFilePathKey)
				return uni.removeSavedFile({
					filePath
				})
			},
			jumpToAppStore() {
				plus.runtime.openURL(this.url);
			}
		}
	}
</script>

<style lang="scss">
	page {
		background: transparent;
	}

	.flex-center {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		justify-content: center;
		align-items: center;
	}

	.mask {
		position: fixed;
		left: 0;
		top: 0;
		right: 0;
		bottom: 0;
		background-color: rgba(0, 0, 0, .65);
	}

	.botton-radius {
		border-bottom-left-radius: 30rpx;
		border-bottom-right-radius: 30rpx;
	}

	.content {
		position: relative;
		top: 0;
		width: 600rpx;
		background-color: #fff;
		box-sizing: border-box;
		padding: 0 50rpx;
		font-family: Source Han Sans CN;
	}

	.text {
		/* #ifndef APP-NVUE */
		display: block;
		/* #endif */
		line-height: 200px;
		text-align: center;
		color: #FFFFFF;
	}

	.content-top {
		position: absolute;
		top: -195rpx;
		left: 0;
		width: 600rpx;
		height: 270rpx;
	}

	.content-top-text {
		font-size: 45rpx;
		font-weight: bold;
		color: #F8F8FA;
		position: absolute;
		top: 120rpx;
		left: 50rpx;
		z-index: 1;
	}

	.content-header {
		height: 70rpx;
	}

	.title {
		font-size: 33rpx;
		font-weight: bold;
		/* #3DA7FF  $my-theme-olor */
		color: #3DA7FF;  
		line-height: 38px;
	}

	.footer {
		height: 150rpx;
		display: flex;
		align-items: center;
		justify-content: space-around;
	}

	.box-des-scroll {
		box-sizing: border-box;
		padding: 0 40rpx;
		height: 200rpx;
		text-align: left;
	}

	.box-des {
		font-size: 26rpx;
		color: #000000;
		line-height: 50rpx;
	}

	.progress-box {
		width: 100%;
	}

	.progress {
		width: 90%;
		height: 40rpx;
		border-radius: 35px;
	}

	.close-img {
		width: 70rpx;
		height: 70rpx;
		z-index: 1000;
		position: absolute;
		bottom: -120rpx;
		left: calc(50% - 70rpx / 2);
	}

	.content-button {
		text-align: center;
		flex: 1;
		font-size: 30rpx;
		font-weight: 400;
		color: #FFFFFF;
		border-radius: 40rpx;
		margin: 0 18rpx;

		height: 80rpx;
		line-height: 80rpx;

		background: linear-gradient(to right, #1785ff, #3DA7FF);
	}

	.flex-column {
		display: flex;
		flex-direction: column;
		align-items: center;
	}
</style>

  • 需要在pages.json文件中配置路由信息
// ……app更新
				    {
				        "path": "uni_modules/pages/upgrade-popup", // 文件路劲
				        "style": {
				            "disableScroll": true,
				            "app-plus": {
				                "backgroundColorTop": "transparent",
				                "background": "transparent",
				                "titleNView": false,
				                "scrollIndicator": false,
				                "popGesture": "none",
				                "animationType": "fade-in",
				                "animationDuration": 200
				
				            }
				        }
				    }

3.在App.vue页面的 onLaunch中调用刚才的方法即可

<script>
	import update from '@/uni_modules/utils/check-update.js'
  export default {

    /**
     * 全局变量
     */
    globalData: {},

    /**
     * 初始化完成时触发
     */
    onLaunch() {
      // 小程序主动更新
      this.updateManager()
			
			//app更新
			// #ifdef APP-PLUS
			  update()
			// #endif
    },

    methods: {
    
      /**
       * 小程序主动更新
       */
      updateManager() {
        const updateManager = uni.getUpdateManager()
        updateManager.onCheckForUpdate(res => {
          // 请求完新版本信息的回调
          // console.log(res.hasUpdate)
        })
        updateManager.onUpdateReady(() => {
          uni.showModal({
            title: '更新提示',
            content: '新版本已经准备好,即将重启应用',
            showCancel: false,
            success(res) {
              if (res.confirm) {
                // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
                updateManager.applyUpdate()
              }
            }
          })
        })
        updateManager.onUpdateFailed(() => {
          // 新的版本下载失败
          uni.showModal({
            title: '更新提示',
            content: '新版本下载失败',
            showCancel: false
          })
        })
      }

    }

  }
</script>

<style lang="scss">
  /* 引入uView库样式 */
  @import "uview-ui/index.scss";
</style>

<style>
  /* 项目基础样式 */
  @import "./app.scss";

  .uni-app--showlayout+uni-tabbar.uni-tabbar-bottom,
  .uni-app--showlayout+uni-tabbar.uni-tabbar-bottom .uni-tabbar,
  .uni-app--showlayout+uni-tabbar.uni-tabbar-top,
  .uni-app--showlayout+uni-tabbar.uni-tabbar-top .uni-tabbar {
    left: var(--window-left);
    right: var(--window-right);
  }

</style>

打包wgt包 部署

  • 1.修改版本号,在manifest.json的基础配置里修改
    在这里插入图片描述
  • 打包wgt包,在发行里点击打包wgt
    在这里插入图片描述
  • 3.将wgt包下载上传到服务器,后端配置相同的版本号即可

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

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

相关文章

财务创造价值,如何降本增效?

一、整体成本管控理论 有财务人员可能认为这和我们财务有什么关系&#xff0c;这和财务管理也没有关系。我们经常提到的业务财融合以及成本BP&#xff0c;其实在这里面是需要发挥应有的价值的。如何理解这个问题&#xff1f;无论是老板还是财务人员&#xff0c;一是有财务管理…

2023年测试人前景归途?我主攻自动化测试拿到了25k的offer...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

LeetCode_双指针_中等_86.分隔链表

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。你应当保留两个分区中每个节点的初始相对位置。 示例 1&…

JavaEE(系列19) -- 计算机网络初识

目录 1. 网络发展史 2. IP地址和端口号 3. 协议 4. 五元组 6. 协议分层 6.1 OSI 七层模型 6.2 TCP/IP五层&#xff08;或四层&#xff09;模型 7. 协议分层(网络数据传输过程) 7.1 应用层 7.2 传输层(进入了操作系统内核) 7.3 网络层 7.4 数据链路层 7.5 物理层 声明:本文内…

【CSS3+HTML5+JQUERY】------ 实现环形进度条实例代码-(已简单封装)

1. JavaScript代码 circle.js文件: 简单的封装了一下 直接调用方法即可 (function ($$) {var lyCircle {};lyCircle.options{timer:20,circleLeft:.ly-circle-left,elements:"",circleRight:".ly-circle-right",percentSum:0,//百分比bgColor:#00a7ff,bor…

stable diffusion中的u net

Stable Diffusion 包含几个核心的组件&#xff1a; 一个文本编码器&#xff08;在 Stable Diffusion 中使用 CLIP 的 ViT-L/14 的文本编码器&#xff09;&#xff0c;用于将用户输入的 Prompt 文本转化成 text embedding&#xff1b;一个 Image Auto Encoder-Decoder&#xff…

大模型核心技术原理: Transformer架构详解

在大模型发展历程中&#xff0c;有两个比较重要点&#xff1a;第一&#xff0c;Transformer 架构。它是模型的底座&#xff0c;但 Transformer 不等于大模型&#xff0c;但大模型的架构可以基于 Transformer&#xff1b;第二&#xff0c;GPT。严格意义上讲&#xff0c;GPT 可能…

【学习笔记】Python核心技术与实战-基础篇-03列表和元组,到底用哪个?

目录 列表和元组基础概念区别列表和元组的基础操作和注意事项列表和元组存储方式的差异列表和元组的性能列表和元组的使用场景总结思考题 列表和元组基础 概念 列表和元组&#xff0c;都是一个可以放置任意数据类型的有序集合。 在绝大多数编程语言中&#xff0c;集合的数据类…

C++算法:排序之二(归并、希尔、选择排序)

C算法&#xff1a;排序 排序之一&#xff08;插入、冒泡、快速排序&#xff09; 排序之二&#xff08;归并、希尔、选择排序&#xff09; 文章目录 C算法&#xff1a;排序二、比较排序算法实现4、归并排序5、希尔排序5、选择排序 原创文章&#xff0c;未经许可&#xff0c;严禁…

从vue2到vue3的生命周期

1.vue2 在vue2.x中的生命周期为 beforeCreate created beforeMount mounted beforeUpdate updated beforeDestroy destroyed activated deactivated errorCaptured 在vue3中&#xff0c;新增了一个setup生命周期函数&#xff0c;setup执行的时机是在beforeCreate生命函数之前…

count(0)、count(1)和count(*)、count(列名) 的区别

当我们对一张数据表中的记录进行统计的时候&#xff0c;习惯都会使用 count 函数来统计&#xff0c;但是 count 函数传入的参数有很多种&#xff0c;比如 count(1)、count(*)、count(字段) 等。 到底哪种效率是最好的呢&#xff1f;是不是 count(*) 效率最差&#xff1f; 一.…

【Mysql数据库从0到1】-入门基础篇--sql语句简单使用

【Mysql数据库从0到1】-入门基础篇--sql语句简单使用 &#x1f53b;一、数据库创建、删除、选择1.1 &#x1f343; create database 创建数据库1.2 &#x1f343; 使用 mysqladmin 创建数据库1.3 &#x1f343; drop 命令删除数据库--一般不建议在数据库执行delete、drop等命令…

公司招人面试了一个00后,绝对能称为是内卷届的天花板

公司前段缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资也不低&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。令我印象最深的是一个00后测试员&#xff0c;他…

【商品页面详情页+商品评论】API接口技术交流,封装接口

商品详情API接口数据&#xff1a;提供了商品的基本信息&#xff0c;包括商品名称、描述、规格、价格、销量、库存等信息。此外&#xff0c;也可以通过提供的API接口来获取商品的图片、评价、物流信息等详细数据。 商品评论接口是消费者对商品所进行的客观评价 电商API的应用价…

使用geoserver发布shp和tiff数据

一、安装并启动geoserver服务 1.1 下载geoserver 进入官网下载 由于geoserver是使用Java语言开发的&#xff0c;所以运行需要java的环境&#xff0c;不同geoserver的版本号对java的版本要求不同&#xff0c;所以选择版本时需注意对应java的版本要求&#xff0c;由于我本地安…

Nginx配置域名证书

Nginx配置域名证书 1、证书存放路径 2、nginx.conf文件中增加以下配置&#xff0c;注意路径不一样&#xff0c;访问地址目录不一样 server {listen 443 ssl http2;server_name jistest.vwatj.ap.vwg;ssl_certificate D:/home/XXX/ssl/2023/XXX.cer; ssl_certificate_key D…

Spring Validation 接口入参校验

一、前言 JSR 是 Java Specification Requests 的缩写&#xff0c;含义为 JAVA 规范提案。 JSR 303 - Bean Validation 规范, 正是一套基于 JavaBean 参数校验的标准。 Hibernate Validator 是 JSR 303 的实现&#xff0c;它提供了 JSR 303 规范中所有约束&#xff08;constrai…

泪崩!测试面试技术面过了却挂在了——“谈谈你的职业生涯规划”

前不久&#xff0c;软件测试交流群里面有一个成员吐槽&#xff0c;说今天的面试技术已经面过了&#xff0c;可HR却问了她“未来的职业发展目标是什么&#xff1f;”然后&#xff0c;挂了&#xff01;这个问题我们平时在交流群里都有讲过&#xff0c;可是这丫头比较疯&#xff0…

级差制系统开发模式是怎么赚钱的?

级差制是直销所有模式中最受欢迎的模式之一&#xff0c;很多企业商家都会在级差制和双轨制中二选一&#xff0c;可见这个模式的优秀程度。下面就来简单分析一下&#xff0c;在级差制模式中是怎么赚钱的&#xff1f; 级差制最大的特点就是以卖货为主&#xff0c;它所有的奖金设置…

正规理财app软件有哪些?top5资质正规理财app软件最新排名

正规理财app软件有哪些&#xff1f;随着移动端理财的普及&#xff0c;越来越多的人开始使用理财app软件进行投资和资产管理。但是&#xff0c;市场上有很多理财软件&#xff0c;如何选择一款正规、安全的软件是关键。下面就为大家介绍一些选择理财app软件的建议。首先&#xff…