uniapp实现应用wgt资源热更新

news2024/11/19 23:27:10

APP更新一般有两种形式
1、整包更新,通过hbuliderx提供的在线云打包就属于整包更新,属于全量更新,缺点就是打包时间长、要重新走市场审核。费时在这里插入图片描述
2、wgt资源包热更新,通过hbuliderx打wgt包 ,速度快,能在应用打开就更新,跳过市场审核更新,很方便,但仅更新js代码,无法改变原生配置项
请添加图片描述

核心流程

热更新的核心流程
1、打wgt包 打的wgt包内的版本号(manifest.json里面的应用版本名称versionName和应用版本号versionCode)需要大于客体应用内的版本号,不然无法安装,会提示版本号匹配在这里插入图片描述
2、触发更新打完最新的wgt包后 将其存放到服务器,应用内置在首页每次进入调接口,查询服务器的wgt的版本号是否大于本地应用版本号,如果大于则触发更新,出现热更新弹窗
3、下载wgt包出现热更新弹窗后,用户点击下载新包,通过uni.downloadFile方法去将wgt包的在线地址下载到本地 拿到临时路径tempFilePath字段
4、安装wgt包拿到临时路径后,接下来就是安装包,通过plus.runtime.install去安装应用包
5、重启生效安装完成 通过plus.runtime.restart 重启即可

代码

请勿直接复制粘贴,我的代码耦合性强 还匹配了自己公司的后端接口,仅供参考。
创建热更新弹窗页面的vue代码

<template>
  <view class="mask flex-center">
    <view class="content botton-radius">
      <view class="content-top">
       
        <image class="content-top" style="top: 0" width="100%" height="100%" src="../images/bg_top.png"/>
        
		 <text class="content-top-text">{{ title }}</text>
		 <view class="content-top-sub">V{{versionNum}}</view>
      </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 || versionDesc }}
            </text>
          </scroll-view>
        </view>
        <view class="footer flex-center">
          <template v-if="isiOS">
            <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="#03bdac" show-info stroke-width="10" />
                <view style="width: 100%; font-size: 28rpx; display: flex; justify-content: space-around">
                  <text>{{ downLoadingText }}</text>
                 
                </view>
              </view>

              <button v-else class="content-button" style="border: none; color: #fff" plain @click="downloadPackage">
                {{ 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 = '__localFilePath__';
const platform_iOS = 'iOS';
let downloadTask = null;

/**
 * 对比版本号,如需要,请自行修改判断规则
 * 支持比对	("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: '',
      versionDesc: '',
      is_mandatory: false,

      // 可自定义属性
      subTitle: '更新内容',
      downLoadBtnTextiOS: '立即跳转更新',
      downLoadBtnText: '立即下载更新',
      downLoadingText: '安装包下载中,请稍候...',
	  versionNum:''
    };
  },
  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 = ['versionNum', 'resourceUrl', 'appType']; //版本号 ,资源地址,是否热更新 强制更新
    for (let key in localPackageInfo) {
      
      if (requiredKey.indexOf(key) !== -1 && localPackageInfo[key] == '') {
        console.error(`参数 ${key} 必填,请检查后重试`);
        uni.navigateBack();
        return;
      }
    }
    this.is_mandatory = localPackageInfo['isNeedUpdate'];
    Object.assign(this, localPackageInfo);
    this.checkLocalStoragePackage();
	this.versionNum=localPackageInfo['versionNum'];
	
  },
  onBackPress() {
    // 强制更新不允许返回
    if (this.is_mandatory) {
      return true;
    }

    downloadTask && downloadTask.abort();
  },
  computed: {
    isWGT() {
      return this.appType === 'wgt';
    },
    isiOS() {
      return !this.isWGT ? plus.os.name.toLocaleLowerCase().includes(platform_iOS) : false;
    },
	showPrenct(){
		return parseInt(this.downLoadPercent)+'%';
	},
  },
  methods: {
    checkLocalStoragePackage() {
      // 如果已经有下载好的包,则直接提示安装
      const localFilePathRecord = uni.getStorageSync(localFilePathKey);
      if (localFilePathRecord) {
        const { version, savedFilePath, installed } = localFilePathRecord;

        // 比对版本
        if (!installed && compare(version, this.versionNum) === 0) {
          this.downloadSuccess = true;
          this.installForBeforeFilePath = savedFilePath;
          this.tempFilePath = savedFilePath;
        } else {
          // 如果保存的包版本小 或 已安装过,则直接删除
          this.deleteSavedFile(savedFilePath);
        }
      }
    },
    async closeUpdate() {
      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.versionNum);
        uni.navigateBack();
        return;
      }

      uni.navigateBack();
    },
    downloadPackage() {
      this.downloading = true;

      //下载包
      downloadTask = uni.downloadFile({
        url: this.resourceUrl,
        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: `更新失败${this.isWGT ? '' : ',APK文件不存在'},请重新下载`,
            content: err.message,
            showCancel: false,
          });
        }
      );

      // 非wgt包,安装跳出覆盖安装,此处直接返回上一页
      if (!this.isWGT) {
        uni.navigateBack();
      }
      // #endif
    },
    restart() {
      this.installed = false;
      // #ifdef APP-PLUS
      //更新完重启app
      plus.runtime.restart();
      // #endif
    },
    async saveFile(tempFilePath, version) {
      const [err, res] = await uni.saveFile({
        tempFilePath,
      });
      if (err) {
        return;
      }
      uni.setStorageSync(localFilePathKey, {
        version,
        savedFilePath: res.savedFilePath,
      });
    },
    deleteSavedFile(filePath) {
      uni.removeStorageSync(localFilePathKey);
      return uni.removeSavedFile({
        filePath,
      });
    },
    jumpToAppStore() {
      plus.runtime.openURL(this.resourceUrl);
    },
  },
};
</script>

<style>
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, 0.65);
}

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

.content {
  position: relative;
  top: 0;
  width: 618rpx;
  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: 618rpx;
  height: 310rpx;
}

.content-top-text {
  font-size: 34rpx;
  font-weight: 600;
  color: #333;
  position: absolute;
  bottom: 40rpx;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}
.content-top-sub{
	position: absolute;
	bottom: 0rpx;
	left: 0;
	right: 0; font-size: 28rpx;
  font-weight: 400;
  color: #333;  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}

.content-header {
  height:140rpx;
 
}

.title {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  line-height: 38rpx;
}

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

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

.box-des {
  font-size: 24rpx;
  color: #666;
  line-height: 34rpx;
}

.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: #333;
  border-radius: 40rpx;
  margin: 0 18rpx;
  height: 80rpx;
  line-height: 80rpx;
  background: linear-gradient(to right, #03bdac, #03bdac);
}

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

注册这个热更新弹窗页面
page.json配置

{
	"path": "pages/upgrade/pages/upgrade-popup",
	"style": {
		"disableScroll": true,
		"disableSwipeBack": true,
		"app-plus": {
			"backgroundColorTop": "transparent",
			"background": "transparent",
			"disableSwipeBack": true,
			"titleNView": false,
			"scrollIndicator": false,
			"popGesture": "none",
			"animationType": "fade-in",
			"animationDuration": 200
		}
	}
}

触发是否弹起热更新弹窗代码(主要是检测当前版本号是否大于热更新版本号)
我将其命名成update.js

import conf from '@/common/config.js';
import api from "@/common/api.js"; //调用API接口
const PACKAGE_INFO_KEY = '__muji__package_info__';
const APP_NAME = conf.conf.APP_NAME
const UPDATE_ROOT = conf.conf.UPDATE_ROOT
export default () => {
  // #ifdef APP-PLUS
  return new Promise((resolve, reject) => {
    plus.runtime.getProperty(plus.runtime.appid, function (widgetInfo) {
			const url = `${UPDATE_ROOT}/rest/queryNewVersion`
			const params = {
				appName: APP_NAME,
				appVersionNum: widgetInfo.versionCode,
				operatingSystemType: plus.os.name === 'iOS' ? 1 : 0,
				versionNum: widgetInfo.version,
			}
			console.log(url,params);
			api.ajaxApi(url, params, "POST", false)
        .then((res) => {
					console.log(res);
          if (res.code === 200) {
            if (res.result && res.result.hasNew) {
              let data = Object.assign(res.result, {
                platform: plus.os.name,
              });
							console.log(data);
              uni.setStorageSync(PACKAGE_INFO_KEY, data);
              uni.navigateTo({
                url: `/pages/upgrade/pages/upgrade-popup?local_storage_key=${PACKAGE_INFO_KEY}`,
                fail: (err) => {
                  console.error('更新弹框跳转失败', err);
                  uni.removeStorageSync(PACKAGE_INFO_KEY);
                },
              });
            } else {
              console.log('没有最新更新');
              resolve('没有最新更新');
            }
          } else {
            console.log('更新失败', res.message);
            reject('error');
          }
        })
        .catch((err) => {
          console.log('更新失败异常');
          reject('error');
        });
    });
  });
  // #endif
};

在首页使用这个方法

import maliCheckUpdate from '@/utils/update';
onLoad() {
		setTimeout(() => {
			//等待页面加载完成后弹出
			maliCheckUpdate()
		}, 2000);
	},

大功告成

当然我没有贴上后台管理wgt包系统的代码,其本质核心还是通过接口拉取最新的wgt包资源地址,主要工作还是在app上的代码。
能在多个app应用

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

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

相关文章

iphone测试中除了appium,还有更好用的工具吗

除了Appium&#xff0c;还有一些其他的工具可以用于iPhone测试&#xff0c;下面列举几个&#xff1a; 1. XCUITest XCUITest是苹果官方提供的UI自动化测试框架&#xff0c;可以用于iPhone和iPad应用程序的自动化测试。XCUITest可以模拟用户操作&#xff0c;例如点击、滑动、输…

机器学习实践(1.1)XGBoost分类任务

前言 XGBoost属于Boosting集成学习模型&#xff0c;由华盛顿大学陈天齐博士提出&#xff0c;因在机器学习挑战赛中大放异彩而被业界所熟知。相比越来越流行的深度神经网络&#xff0c;XGBoost能更好的处理表格数据&#xff0c;并具有更强的可解释性&#xff0c;还具有易于调参…

若依微服务 + seata1.5.2版本分布式事务(安装配置nacos+部署)

若依官方使用的1.4.0版本seata&#xff0c;版本较低配置相对更麻烦一些 一、seata服务端下载&#xff0c;下载方式介绍两种入口&#xff0c;如下&#xff1a; 1、找到对应版本&#xff0c;下载 binary 即可。 下载包名为&#xff1a;seata-server-1.5.2.zip 2. github上下载 …

WWDC2023 Metal swift 头显ARKit支持c c++ 开发

1 今年WWDC&#xff0c;我们看见了苹果的空间计算设备&#xff0c;visionOS也支持了c c API. 这有什么好处呢&#xff0c;不是说能够吸引更多c c开发者加入苹果开发者阵营&#xff0c;而是我们过去的很多软件&#xff0c;可以轻松对接到苹果的头显设备&#xff0c;让我们的软件…

2023年协议转让研究报告

第一章 概述 协议转让作为中国企业破产法中的重要程序之一&#xff0c;已经在实践中得到广泛应用。在协议转让过程中&#xff0c;债务人与债权人或其他相关方通过协商达成一致&#xff0c;将特定的资产或权益进行转让&#xff0c;以实现债务清偿或债务人的破产清算。协议转让的…

RRC重建比率高问题分析和优化方法

PART01 1、重建概述 RRC重建&#xff08;RRC connection re-establishment&#xff09;是UE处于RRC_CONNECTED状态&#xff0c;因为一些移动性管理或底层链路故障&#xff0c;导致连接中断&#xff0c;UE发起的空口资源重新建立的过程&#xff0c;以继续空口的RRC连接。重建是…

[CSP-S 2021] 回文

[CSP-S 2021] 回文 题目描述: 给定正整数 n 和整数序列 a1​,a2​,…,a2n​&#xff0c;在这 2n 个数中&#xff0c;1,2,…,n 分别各出现恰好 2 次。现在进行 2n 次操作&#xff0c;目标是创建一个长度同样为 2n 的序列 b1​,b2​,…,b2n​&#xff0c;初始时 b 为空序列&…

【SpringCloud入门】-- SpringCloud优质组件介绍

目录 1. SpringCloud优质项目 2. 介绍SpringCloud优质项目 SpringCloudConfig(Spring) SpringCloudBus Eureka Hystrix Zuul Archaius Consul SpringCloudForCloudFoundry SpringCloudSleuth SpringCloudDataFlow SpringCloudSecurity SpringCloudZookeeper Spr…

【Redis】孔夫子旧书网爬虫接入芝麻代理IP:代理IP利用效率最大化

背景&#xff1a; 之前用过芝麻IP&#xff0c;写过这几篇文章 《【Python】芝麻HTTP代理系列保姆级全套攻略(对接教程自动领取每日IPIP最优算法)》 《【Python】记录抓包分析自动领取芝麻HTTP每日免费IP&#xff08;成品教程&#xff09;》 《爬虫增加代理池&#xff1a;使用稳…

ICC2:自定义快捷键和菜单

把一些常用的功能放在一个菜单里是什么体验?直接放在工具栏里是不是更方便?那设置成快捷键呢? gui_create_menu 自定义菜单可以把工具常用的功能放到一个菜单里,用户也可以把“执行脚本操作”加到菜单里。 举例来说: 1)把Editor Toolbox放到Favorite菜单里,floorplan 操…

行业报告 | AIGC发展研究

原创 | 文 BFT机器人 01 技术篇 深度学习进化史:知识变轨 风起云涌 已发生的关键步骤&#xff1a; 人工神经网络的诞生 反向传播算法的提出 GPU的使用 大数据的出现 预训练和迁移学习 生成对抗网络 (GAN) 的发明 强化学习的成功应用 自然语言处理的突破 即将发生的关键…

MinGW-w64安装和使用_亲测有效

MinGW-w64 是什么&#xff01;&#xff1f; MinGW-w64 是一个在 Windows 系统上运行的 GNU 编译器套件&#xff0c;支持 C 和 C 语言的编译。它包括了 GCC 编译器、GNU Binutils 和一些其他的工具。在 MinGW-w64 中 各个版本的参数含义如下&#xff1a; x86_64&#xff1a;表…

1.ORB-SLAM3系统概述

1.内容简介 本系列文章主要基于ORB-SLAM3代码、论文以及相关博客&#xff0c;对算法原理进行总结和梳理。 ORB-SLAM系列整体架构是不变的&#xff0c;都包含Tracking、LocalMapping和LoopClosing三个核心线程&#xff0c;中间伴随着优化过程。在ORB-SLAM3算法中比较突出的改进…

腾讯安全董志强:四大关键步骤促进数据安全治理闭环,提升企业免疫力

高速发展的数字时代&#xff0c;数据已成为推动产业发展的最重要生产要素之一&#xff0c;真正成为了创造经济财富的数字能源&#xff0c;守护数据资产的安全成为企业高质量发展不可回避的重要命题。 6月13日&#xff0c;腾讯安全联合IDC发布“数字安全免疫力”模型框架&#…

我被一家无货源电商培训公司骗了怎么办?

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 最近&#xff0c;一位被无货源电商培训骗的人找到了卢松松&#xff0c;她说&#xff1a; 老师&#xff0c;你好&#xff0c;我是被无货源电商课程骗了的受害人&#xff0c;走投无路了&#xff0c;想…

5个超好用的开源工具库分享~

在实际项目开发中&#xff0c;从稳定性和效率的角度考虑&#xff0c;重复造轮子是不被提倡的。但是&#xff0c;自己在学习过程中造轮子绝对是对自己百利而无一害的&#xff0c;造轮子是一种特别能够提高自己系统编程能力的手段。 今天分享几个我常用的开源工具库&#xff1a;…

大佬们都是如何编写测试方案的?

目录 1、背景 2、编写的方式 2.1 第一阶段&#xff1a;在需求评审开始前 2.2 第二阶段&#xff1a;在需求评审开始后&#xff0c;技术方案设计中 2.3 第三阶段&#xff1a;技术方案设计后 2.4 第四阶段&#xff1a;测试方案评审前 2.5 第五阶段&#xff1a;测试方案评审…

Opencv-C++笔记 (7) : opencv-文件操作XML和YMAL文件

文章目录 一、概述二、文件操作三、打开文件四、写入五、读写个人源码 一、概述 除了图像数据之外&#xff0c;有时程序中的尺寸较小的Mat类矩阵、字符串、数组等 数据也需要进行保存&#xff0c;这些数据通常保存成XML文件或者YAML文件。本小节中将介绍如何利用OpenCV 4中的函…

前端实现消息推送、即时通信、http简介

信息推送 服务端主动向客户端推送消息&#xff0c;使客户端能够即时接收到信息。 场景 页面接收到点赞&#xff0c;消息提醒聊天功能弹幕功能实时更新数据功能 实现即时通讯方式 短轮询 浏览器&#xff08;客户端&#xff09;每隔一段时间向服务器发送http请求&#xff0c;…

Google为TensorFlow设计的专用集成电路TPU3.0图片

Widrow也是在Minsky的影响下进入AI领域的&#xff0c;后来加入斯坦福大学任教。他在1960年提出了自适应线性单元&#xff08;Adaline&#xff09;&#xff0c;一种和感知器类似的单层神经网络&#xff0c;用求导数方法来调整权重&#xff0c;所以说有“三十年神经网络经验”并不…