当前位置: 首页 > news >正文

基于Effect的组件设计 | 京东云技术团队

Effect的概念起源

从输入输出的角度理解Effect https://link.excalidraw.com/p/readonly/KXAy7d2DlnkM8X1yps6L

编程中的Effect起源于函数式编程中纯函数的概念

纯函数是指在相同的输入下,总是产生相同的输出,并且没有任何副作用(side effect)的函数。

副作用是指函数执行过程中对函数外部环境进行的可观察的改变,比如修改全局变量、打印输出、写入文件等。

前端的典型副作用场景是 浏览器环境中在window上注册变量

副作用引入了不确定性,使得程序的行为难以预测和调试。为了处理那些需要进行副作用的操作,函数式编程引入了Effect的抽象概念。

它可以表示诸如读取文件、写入数据库、发送网络请求DOM渲染等对外部环境产生可观察改变的操作。通过将这些操作包装在Effect中,函数式编程可以更好地控制和管理副作用,使得代码更具可预测性和可维护性。

实际工作中我们也是从React的useEffect开始直接使用Effect的说法

React: useEffect

useEffect is a React Hook that lets you synchronize a component with an external system.

import { useState, useEffect } from 'react';
// 模拟异步事件
function getMsg() {return new Promise((resolve) => {setTimeout(() => {resolve('React')}, 1000)})
}export default function Hello() {const [msg, setMsg] = useState('World')useEffect(() => {getMsg().then((msg) => {setMsg(msg)})const timer = setInterval(() => {console.log('test interval')})return () => {// 清除异步事件clearTimeout(timer)}}, [])return (<h1>Hello { msg }</h1>);
}

Effect中处理异步事件,并在此处消除异步事件的副作用clearTimeout(timer),避免闭包一直无法被销毁

Vue: watcher

运行期自动依赖收集 示例

<script setup>
import { ref } from 'vue'
const msg = ref('World!')setTimeout(() => {msg.value = 'Vue'
}, 1000)
</script><template><h1>Hello {{ msg }}</h1>
</template>
_createElementVNode("h1", null, _toDisplayString(msg.value), 1 /* TEXT */)

runtime的render期间通过msg.value对msg产生了引用,此时产生了一个watch effect:msg的watchlist中多了一个render的watcher,在msg变化的时候 render会通过watcher重新执行

Svelte: $

编译器依赖收集 示例

suffix的值依赖name,在name变化之后,suffix值也更新

<script>let name = 'world';$: suffix = name + '!'setTimeout(() => {name = 'svelte'}, 1000)
</script><h1>Hello {suffix}</h1>
// 编译后部分代码
function instance($$self, $$props, $$invalidate) {let suffixlet name = 'world'setTimeout(() => {$$invalidate(1, (name = 'svelte'))}, 1000)// 更新关系$$self.$$.update = () => {if ($$self.$$.dirty & /*name*/ 2) {$: $$invalidate(0, (suffix = name + '!'))}}return [suffix, name]
}

Effect分类

React先介绍了两种典型的Effect

  • 渲染逻辑中可以获取 props 和 state,并对它们进行转换,然后返回您希望在屏幕上看到的 JSX。渲染代码必须是纯的,就像数学公式一样,它只应该计算结果,而不做其他任何事情。
  • 事件处理程序是嵌套在组件内部的函数,它们执行操作而不仅仅做计算。事件处理程序可以更新输入字段、提交HTTP POST请求以购买产品或将用户导航到另一个页面。它包含由用户特定操作(例如按钮点击或输入)引起的 “副作用”(它们改变程序的状态)。

Consider a ChatRoom component that must connect to the chat server whenever it’s visible on the screen. Connecting to a server is not a pure calculation (it’s a side effect) so it can’t happen during rendering. However, there is no single particular event like a click that causes ChatRoom to be displayed.

考虑一个ChatRoom组件,每当它在屏幕上可见时都必须连接到聊天服务器。连接到服务器不是一个纯粹的计算(它是一个副作用),因此不能在渲染期间发生(渲染必须是纯函数)。然而,并没有单个特定的事件(如点击)会触发ChatRoom的展示

Effects let you specify side effects that are caused by rendering itself, rather than by a particular event. Sending a message in the chat is an event because it is directly caused by the user clicking a specific button. However, setting up a server connection is an Effect because it should happen no matter which interaction caused the component to appear. Effects run at the end of a commit after the screen updates. This is a good time to synchronize the React components with some external system (like network or a third-party library).

Effect 允许指定由渲染本身引起的副作用,而不是由特定事件引起的副作用。在聊天中发送消息是一个事件,因为它直接由用户点击特定按钮引起。然而不管是任何交互触发的组件展示,_设置服务器连接_都是一个Effect。Effect会在页面更新后的commit结束时运行。这是与某个外部系统(如网络或第三方库)同步React组件的好时机

以下Effect尽量达到不重不漏,不重的意义是他们之间是相互独立的,每个模块可以独立实现,这样可以在系统设计的初期可以将业务Model建设和Effect处理分离,甚至于将Effects提取成独立的utils

渲染

生命周期

组件被初始化、更新、卸载的时候我们需要做一些业务逻辑处理,例如:组件初始化时调用接口更新数据

React

react基于自己的fiber结构,通过闭包完成状态的管理,不会建立值和渲染过程的绑定关系,通过在commit之后执行Effect达到值的状态更新等副作用操作,因此声明周期需要自己模拟实现

import { useState, useEffect } from 'react';export default function Hello() {const [msg, setMsg] = useState('World')// dependency是空 因此只会在第一次执行 声明周期上可以理解为onMounteduseEffect(() => {// 异步事件const timer = setTimeout(() => {// setMsg会触发重渲染 https://react.dev/learn/render-and-commitsetMsg('React')}, 1000)return () => {// 卸载时/重新执行Effect前 清除异步事件clearTimeout(timer)}// 如果dependency有值 则每次更新如果dependency不一样就会执行Effect}, [])return (<h1>Hello { msg }</h1>);
}
<script setup>
import { onMounted, onUnmounted, onUpdated, ref } from 'vue'const msg = ref('Hello World!')
// 挂载
onMounted(async () => {function getValue() {return Promise.resolve('hello, vue')}const value = await getValue()msg.value = value
})
onUpdated(() => {}) // 更新
onUnmounted(() => {}) // 卸载
</script><template><h1>{{ msg }}</h1><input v-model="msg">
</template>
<script>import { onMount, onDestroy, beforeUpdate } from 'svelte'let name = 'world'$: suffix = name + '!'onMount(() => {setTimeout(() => {name = 'svelte'}, 1000)})beforeUpdate(() => {}) // 更新onDestroy(() => {}) // 卸载/销毁
</script><h1>Hello {suffix}</h1>

Action 用户行为

对应React中提到的两个典型Effect中的 事件处理程序

在不考虑跳出应用(location.href='xxx')的情况下,我们的行为都只能改变当前应用的状态,不管是输入、选择还是触发异步事件的提交,网络相关的副作用在下节讨论

点击/输入
<!-- 原生 要求onClick是全局变量 -->
<div onclick="onClick"/>
<!-- React -->
<div onClick={onClick}/>
<!-- Vue -->
<div @click="onClick"/>
<!-- Svelte -->
<div on:click="onClick"/>

滑动输入、键盘输入等

<!-- React view和model的关系需要自己处理 -->
<input value={value} onChange={val => setValue(val)} placeholder="enter your name" />
<!-- Vue 通过指令自动建立view和model的绑定关系 -->
<input v-model="name" placeholder="enter your name" />
<!-- Svelte -->
<input bind:value={name} placeholder="enter your name" />

所谓的MVVM即是视图和模型的绑定关系通过框架(v-mode,bind:valuel)完成,所以需要自己处理绑定关系的React不是MVVM

滚动

同上

Network 网络请求

基础:XMLHttpRequest,Fetch

NPM包:Axios,useSwr

Storage 存储

任何存储行为都是副作用:POST请求、变量赋值、local存储、cookie设置、URL参数设置

Remote

缓存/数据库,同上 网络请求

Local

内存

  • 局部变量 闭包

React的函数式组件中的useState的值的变更

  • 全局变量 window

浏览器环境初始化完成之后,我们的context中就会有window全局变量,修改window的属性会使同一个页面环境中的所有内容都被影响(微前端的window隔离方案除外)

LocalStorage

兼容localStorage存储和 原生APP存储;返回Promise 其实也可以兼容从接口获取、存储数据

export function getItem(key) {const now = Date.now();if (window.XWebView) {window.XWebView.callNative('JDBStoragePlugin','getItem',JSON.stringify({key,}),`orange_${now}`,'-1',);} else {setTimeout(() => {window[`orange_${now}`](JSON.stringify({status: '0',data: {result: 'success',data: localStorage.getItem(key),},}),);}, 0);}return new Promise((resolve, reject) => {window[`orange_${now}`] = (result) => {try {const obj = JSON.parse(result);const { status, data } = obj;if (status === '0' && data && data.result === 'success') {resolve(data.data);} else {reject(result);}} catch (e) {reject(e);}window[`orange_${now}`] = undefined;};});
}export function setItem(key, value = BABEL_CHANNEL) {const now = Date.now();if (window.XWebView) {window.XWebView.callNative('JDBStoragePlugin','setItem',JSON.stringify({key,value,}),`orange_${now}`,'-1',);} else {setTimeout(() => {window[`orange_${now}`](JSON.stringify({status: '0',data: {result: 'success',data: localStorage.setItem(key, value),},}),);}, 0);}return new Promise((resolve, reject) => {window[`orange_${now}`] = (result) => {console.log('MKT ~ file: storage.js:46 ~ returnnewPromise ~ result:', result);try {const obj = JSON.parse(result);const { status, data } = obj;if (status === '0' && data && data.result === 'success') {resolve(data.data);} else {reject(result);}} catch (e) {reject(e);}window[`orange_${now}`] = undefined;};});
}

Cookie

https://www.npmjs.com/package/js-cookie

URL

参见地址栏参数

举个栗子🌰

组件诉求

  1. 支持分页

  2. 支持搜索

  3. 已选择的门店需要回显,但是已选择的门店只能分页获取,无法全部获取

  4. 需要知道用户移除了哪些选项,增加了哪些选项

  5. 支持服务端全选

组件Effect分析

  • 业务组件可以视load-data为纯函数,因为loda-data的调用不会影响外部业务组件,清晰的Effects归属可以降低业务的复杂度,最大程度上降低组件的耦合
  • 用户在组件内的行为(除了确定之外)产生的Effect只对组件自身产生影响,提升了组件的内聚

组件模型设计

  • 组件list兼容搜索和下拉场景
const { result: list, hasNext } =  await this.loadData(param).catch(() => ({ hasNext: false, result: [] }))
const lastRemove = this.remove // 本次新增之前移除的内容
if (param.pageNo === 1 && !param.search) {this.list = list
} else {// 建立新值的索引 接口返回的信息是无状态属性的(选中与否)const map = list.reduce((pre, cur) => {pre[cur.id] = Object.assign(cur, { from: param.search })return pre}, {})// 此处应该遍历list 而不是 this.listthis.list = this.list.map(item => {const diff = map[item.id]// 找到之前已经有的数据 就从map中移动到之前list的位置做替换if (diff) delete map[item.id]return diff || item// 剩余的值补充到最后面}).concat(Object.values(map))
}
const value = diffBy(this.last.add.concat(this.remote, this.local, this.checked), lastRemove)
this.value = value

  • 接口返回选中的值通过checked-by-remote纯函数的依赖反转实现惰性计算
  • 业务组件默认选中的值通过checked-by-local纯函数的依赖反转实现惰性计算
  • 增加或者移除的值通过相应的diff计算出来
  • Reactivity极大提升了Model的表达能力
{computed: {/*** 接口返回已选中的数据且不能在已移除的数据中, 否则上次移除的数据会被自动选中*/remote() {return diffBy(this.list.filter(this.checkedByRemote || emptyFilter).map(it => it.id), this.last.remove)},/*** 本地默认选中 且不是从remote选中的 且不是上次选中的*/local() {return diffBy(this.list.filter(this.checkedByLocal || emptyFilter).map(it => it.id), this.remote, this.last.add)},// 用户选择的checked() {return diffBy(this.value, this.remote, this.last.add, this.local)},// 1. 本地有接口没有的 是新增,this.value中已包含了last.add 2. 需要新增的且不在上次本地移除的范围内:上次移除的可能不在this.remote范围内add() {return diffBy(this.value, this.remote, this.last.remove)},// 1. 接口有本地没有的 是移除 2. 需要移除的 且 不在上次本地新增的范围内remove() {return this.last.remove.concat(diffBy(this.remote, this.value, this.last.remove))}},
}

参考资料

  • 面向 Model 编程的前端架构设计 https://mp.weixin.qq.com/s/g4hnfirDmyeuXAdEt-zk9w
  • Synchronizing with Effects https://react.dev/learn/synchronizing-with-effects

作者:京东零售 刘威
来源:京东云开发者社区 转载请注明来源

相关文章:

基于Effect的组件设计 | 京东云技术团队

Effect的概念起源 从输入输出的角度理解Effect https://link.excalidraw.com/p/readonly/KXAy7d2DlnkM8X1yps6L 编程中的Effect起源于函数式编程中纯函数的概念 纯函数是指在相同的输入下&#xff0c;总是产生相同的输出&#xff0c;并且没有任何副作用(side effect)的函数。…...

541. 反转字符串 II

541. 反转字符串 II class Solution { public:void Reverse(string& s, int start, int end){end--;while (start < end){swap(s[start], s[end]);start;end--;}}string reverseStr(string s, int k){int len s.size();for (int i 0; i < len; i 2 * k){if (i …...

基本分段存储管理方式(分段,段表,地址转换以及与分页管理对比)

1.分段 1.进程的地址空间: 按照程序自身的逻辑关系划分为若干个段&#xff0c;每个段都有一个段名 &#xff08;在低级语言中&#xff0c;程序员使用段名来编程&#xff09;&#xff0c;每段从0开始编址. 2.内存分配规则: 以段为单位进行分配&#xff0c;每个段在内存中占据…...

哪个牌子的洗地机好用?2023洗地机推荐

洗地机作为一款高效的清洁家电能轻松的搞定各种干湿垃圾&#xff0c;满足日常生活中的各种地面清洁需求&#xff0c;越来越受大众的青睐&#xff0c;那么我们如何快速的选择一款适合自己无线洗地机呢?一起来看看! 做推荐之前&#xff0c;先给大家科普选购洗地机的时候应该关注…...

根据脑图谱获取感兴趣区域的mask

根据脑图谱获取感兴趣区域的mask 1&#xff0c;引入1.1 ASPECT-Atlas 2&#xff0c;获取脑图谱感兴趣区域mask参考&#xff1a; 1&#xff0c;引入 脑影像分析中&#xff0c;我们常常会针对性的对某些感兴趣区域进行分析&#xff0c;而对它们进行分析的前提是获取该区域的mask…...

Android Framework通信:Handler

文章目录 前言一、Handler源码分析1、创建Handler2、发送消息3、取消息4、消息处理5、线程切换的方法&#xff08;Handler异步消息处理机制流程&#xff09;handler.sendMessage()handler.post()View.post()Activity中的runOnUiThread() 二、Handler高频面试题1、为什么要有Han…...

Redis的安装和配置

一、Redis的安装 使用命令将redis安装到linux服务器 yum -y install redis配置redis配置文件 redis的配置文件默认路径为/etc/redis.conf&#xff0c;对配置文件进行修改。 &#xff08;1&#xff09;注释掉bind 127.0.0.1&#xff1b; bind配置项设置的是redis允许的ip地址访问…...

Java武侠文字游戏

import java.util.Random;public class Role {//姓名private String name;//血量private int blood;//性别private char gender;//长相(随机)private String face;String[] boyfaces {"风流俊雅", "气宇轩昂", "相貌英俊", "五官端正"…...

数字化时代下,汽车行业如何突破现有营销困境?

之前三年的“口罩”时期&#xff0c;给全球和中国汽车市场带来不小影响&#xff0c;汽车销售市场整体下滑&#xff0c;传统营销模式很难适应现阶段汽车营销需求&#xff0c;那么在当下&#xff0c;汽车行业应该如何突破现有营销困境呢&#xff1f;接下来就由媒介盒子跟大家聊聊…...

19 | 如何搞清楚事务、连接池的关系?正确配置是怎样的

事务的基本原理 在学习 Spring 的事务之前&#xff0c;你首先要了解数据库的事务原理&#xff0c;我们以 MySQL 5.7 为例&#xff0c;讲解一下数据库事务的基础知识。 我们都知道 当 MySQL 使用 InnoDB 数据库引擎的时候&#xff0c;数据库是对事务有支持的。而事务最主要的作…...

备忘录模式-撤销功能的实现

在idea写代码的过程中&#xff0c;会经常用到一个快捷键——“crtl z”,即撤销功能。“备忘录模式”则为撤销功能提供了一个设计方案。 1 备忘录模式 备忘录模式提供一种状态恢复机制。在不破坏封装的前提下&#xff0c;捕获对象内部状态并在该对象之外保存这个状态。可以在…...

C++入门(二)

文章目录 一、缺省参数1、概念2、缺省参数分类1、全缺省参数2、半缺省参数 3、特性总结 二、函数重载1、引入函数重载2、函数重载概念3、函数重载分类4、C支持函数重载的原理--名字修饰(name Mangling) 三、 引用1、引用概念2、引用特性3、 常引用4、 使用场景1、做参数2、做返…...

【软件设计师】面向对象类图的六种关系

面向对象类图的六种关系&#xff08;继承、实现、依赖、关联、聚合、组合&#xff09; 1、泛化&#xff08;继承&#xff09;2、实现3、依赖4、关联5、聚合6、组合 面向对象类图的六种关系&#xff08;继承、实现、依赖、关联、聚合、组合&#xff09; 进行面向对象设计时&…...

二十七、【四种蒙版】

文章目录 图层蒙版剪贴蒙版快速蒙版矢量蒙版 图层蒙版 在当前图层加上蒙版&#xff0c;黑色画笔的可以让当前图层消失&#xff0c;白色的画笔可以让当前图层出现&#xff1a; 无论填充什么样的颜色&#xff0c;蒙板只有黑白灰三种颜色。模板最简单应用就是我们在插入图形的时候…...

卡尔曼家族从零解剖-(00)目录最新无死角讲解

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解的 卡尔曼家族从零解剖 链接 :卡尔曼家族从零解剖-(00)目录最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/133846882 文末正下方中心提供了本人 联系…...

Linux系统之ip命令的基本使用

Linux系统之ip命令的基本使用 一、ip命令介绍1.1 ip命令简介1.2 ip命令的由来1.3 ip命令的安装包 二、ip命令使用帮助2.1 ip命令的help帮助信息2.2 ip命令使用帮助 三、查看网络信息3.1 显示当前网络接口信息3.2 显示网络设备运行状态3.3 显示详细设备信息3.4 查看路由表3.5 查…...

【推荐算法】ctr cvr联合建模问题合集

ctr和cvr分开建模相比ctcvr的优势&#xff1f; 在电商搜索推荐排序中&#xff0c;将ctr和cvr分开建模&#xff0c;相比直接建模ctcvr的优势是什么&#xff1f; - 萧瑟的回答 - 知乎 总结&#xff1a; 1、ctr的数据可以试试获取&#xff0c;能实时训练。但是cvr存在延迟现象&…...

安装njnx --chatGPT

gpt: 要在 Debian 11 上安装 Nginx&#xff08;通常称为 "nginx"&#xff09;&#xff0c;您可以使用 apt 包管理器执行以下步骤&#xff1a; 1. **登录到您的 Debian 11 服务器**。您可以使用 SSH 客户端以 root 或具有管理员权限的用户身份登录。 2. **更新软件…...

性能测试需求分析

1、客户方提出 客户方能提出明确的性能需求&#xff0c;说明对方很重视性能测试&#xff0c;这样的企业一般是金融、电信、银行、医疗器械等&#xff1b;他们一般对系统的性能要求非常高&#xff0c;对性能也非常了解。提出需求也比较明确。 曾经有一个银行项目&#xff0c;已经…...

logback服务器日志删除原理分析

查看以下的logback官方文档 Chapter 4: Appendershttps://logback.qos.ch/manual/appenders.html 按文档说明&#xff0c;maxHistory是设置保存归档日志的最大数量&#xff0c;该数量的单位受到fileNamePattern里的值%d控制&#xff0c;如果有多个%d,只能有一个主%d&#xff0…...

到底什么才是真正的商业智能(BI)

随着人工智能、云计算、大数据、互联网、物联网等新一代信息化、数字化技术在各行各业内开始大规模的应用&#xff0c;社会上的数字化、信息化程度不断加深&#xff0c;而数据价值也在这样的刺激下成为了个人、机构、企业乃至国家的重要战略资源&#xff0c;成为了继土地、劳动…...

Pulsar Manager配置自定义认证插件访问

Pulsar Manager配置自定义认证插件访问 Pulsar Manager和dashboard部署和启用认证 pulsar自定义认证插件开发 前面博客讲了以token方式访问pulsar 这节博客讲如何配置自定义认证插件的方式访问pulsar #启动pulsar-manager docker run --name pulsar-manager -dit \-p 9527:…...

Java SimpleDateFormat linux时间字符串转时间轴的坑

Mon Oct 16 09:51:28 2023 这是linux 的 date命令得到的时间&#xff0c;要转换称时间戳。 EEE MMM dd HH:mm:ss yyyy 这样的格式&#xff0c;看起来就是正确的&#xff0c;可是就是报错 Unparseable date: "Mon Oct 16 09:51:28 2023" 下面是正确的代码 String[…...

202、RabbitMQ 之 使用 fanout 类型的Exchange 实现 Pub-Sub 消息模型---fanout类型就是广播类型

目录 ★ 使用 fanout 类型的Exchange 实现 Pub-Sub 消息模型代码演示&#xff1a;生产者&#xff1a;producer消费者&#xff1a;Consumer01消费者&#xff1a;Consumer02测试结果 完整代码ConnectionUtilPublisherConsumer01Consumer02pom.xml ★ 使用 fanout 类型的Exchange …...

web 性能优化详解(Lighthouse工具、优化方式、强缓存和协商缓存、代码优化、算法优化)

1.性能优化包含的方面 优化性能概念宽泛&#xff0c;可以从信号、系统、计算机原理、操作系统、网络通信、DNS解析、负载均衡、页面渲染。只要结合一个实际例子讲述清楚即可。 2.什么是性能&#xff1f; Web 性能是客观的衡量标准&#xff0c;是用户对加载时间和运行时的直观…...

docker-compose部署elk(8.9.0)并开启ssl认证

docker部署elk并开启ssl认证 docker-compose部署elk部署所需yml文件 —— docker-compose-elk.yml部署配置elasticsearch和kibana并开启ssl配置基础数据认证配置elasticsearch和kibana开启https访问 配置logstash创建springboot项目进行测试kibana创建视图&#xff0c;查询日志…...

解决java.lang.IllegalArgumentException: servlet映射中的<url pattern>[demo1]无效

当我使用tomcat启动使用servlet项目时&#xff0c;出现了报错&#xff1a; java.lang.IllegalArgumentException: servlet映射中的<url pattern>[demo1]无效 显示路径错误&#xff0c;于是去检查Web.xml中的配置&#xff0c;发现是配置文件的路径写错了&#xff0c;少写了…...

软件测试学习(三)易用性测试、测试文档、软件安全性测试、网站测试

目录 易用性测试 用户界面测试 优秀Ul由什么构成 符合标准和规范 直观 一致 灵活 舒适 正确 实用 为有残疾障碍的人员测试&#xff1a;辅助选项测试 测试文档 软件文档的类型 文档测试的重要性 软件安全性测试 了解黑客的动机 威胁模式分析 网站测试 网页基…...

Java中,对象一定在堆中分配吗?

在我们的日常编程实践中&#xff0c;我们经常会遇到各种类型的对象&#xff0c;比如字符串、列表、自定义类等等。这些对象在内存中是如何存储的呢&#xff1f; 你可能会毫不犹豫地回答&#xff1a;“在堆中&#xff01;”如果你这样回答了&#xff0c;那你大部分情况下是正确…...

AI:38-基于深度学习的抽烟行为检测

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…...

网站开发经济可行性分析/武汉seo搜索引擎优化

内网穿透 一、介绍 1.概念 ​ 内网穿透简单来说就是将内网外网通过natapp隧道打通,让内网的数据让外网可以获取。比如常用的办公室软件等&#xff0c;一般在办公室或家里&#xff0c;通过拨号上网&#xff0c;这样办公软件只有在本地的局域网之内才能访问&#xff0c;那么问…...

网络营销管理方案/搜索引擎优化百度百科

概要P/Invoke的机制让我们能在托管环境下使用原先已实现的Native Code。本文主要讨论的是P/Invoke中的参数传递和.NET CF的一些不同于完整版本的 .NET Fx之处&#xff0c;最后介绍了如何提高P/invoke的效率 Keywords.NET Compact Framework, Windows Mobile, P/Invoke ,data ma…...

赣州房产网站建设/重大军事新闻最新消息

《计算机组成与系统结构实验报告1 (精选可编辑)》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《计算机组成与系统结构实验报告1 (精选可编辑)(14页珍藏版)》请在金锄头文库上搜索。1、 计算机组成原理实验报告评语:课中检查完成的题号及题数&#xff1a;课后完成的题…...

柳州专业网站建设加盟/西安优化外

骨架屏 最近在项目不时有用到骨架屏的需求,所以抽时间对骨架屏的方案作了一下调研,骨架屏的实践已经有很多了,也有很多人对自己的方案作了介绍.在这里按照个人的理解做了一个汇总和分类,分享给大家. 关于骨架屏(简介) 骨架屏就是在页面数据尚未加载前先给用户展示出页面的大…...

深圳专业商城网站制作/seo推广经验

Android 图表开源框架之MPAndroidChart PieChart扇形图&#xff08;一&#xff09; Android 图表开源框架之MPAndroidChart PieChart扇形图&#xff0c;版本&#xff1a;3.0.1 implementation com.github.PhilJay:MPAndroidChart:v3.0.1 效果图1&#xff1a; 效果图2&#x…...

如何提高自己的销售技巧/seo入口

首先需要注册tushare数据网站 https://tushare.pro/ 获取token # -*- coding: utf-8 -*- # 导入包 import matplotlib.pyplot as plt import tushare as ts# 获取股票开盘价 #获取数据的时间段-起始时间 start_time=20220101 #获取数据的时间段-结束时间 end_time= 20230314…...