vue openlayer单击地图事件循环多次执行_12道vue高频原理面试题,你能答出几道?

前言

本文分享 12 道 vue 高频原理面试题,覆盖了 vue 核心实现原理,其实一个框架的实现原理一篇文章是不可能说完的,希望通过这 12 道问题,让读者对自己的 Vue 掌握程度有一定的认识(B 数),从而弥补自己的不足,更好的掌握 Vue ❤️

1. Vue 响应式原理

781dc7203fead5e1cd5679916b4a15a2.png
vue-reactive

核心实现类:

Observer : 它的作用是给对象的属性添加 getter 和 setter,用于依赖收集和派发更新

Dep : 用于收集当前响应式对象的依赖关系,每个响应式对象包括子对象都拥有一个 Dep 实例(里面 subs 是 Watcher 实例数组),当数据有变更时,会通过 dep.notify()通知各个 watcher。

Watcher : 观察者对象 , 实例分为渲染 watcher (render watcher),计算属性 watcher (computed watcher),侦听器 watcher(user watcher)三种

Watcher 和 Dep 的关系

watcher 中实例化了 dep 并向 dep.subs 中添加了订阅者,dep 通过 notify 遍历了 dep.subs 通知每个 watcher 更新。

依赖收集

  1. initState 时,对 computed 属性初始化时,触发 computed watcher 依赖收集

  2. initState 时,对侦听属性初始化时,触发 user watcher 依赖收集

  3. render()的过程,触发 render watcher 依赖收集

  4. re-render 时,vm.render()再次执行,会移除所有 subs 中的 watcer 的订阅,重新赋值。

派发更新

  1. 组件中对响应的数据进行了修改,触发 setter 的逻辑

  2. 调用 dep.notify()

  3. 遍历所有的 subs(Watcher 实例),调用每一个 watcher 的 update 方法。

原理

当创建 Vue 实例时,vue 会遍历 data 选项的属性,利用 Object.defineProperty 为属性添加 getter 和 setter 对数据的读取进行劫持(getter 用来依赖收集,setter 用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。

每个组件实例会有相应的 watcher 实例,会在组件渲染的过程中记录依赖的所有数据属性(进行依赖收集,还有 computed watcher,user watcher 实例),之后依赖项被改动时,setter 方法会通知依赖与此 data 的 watcher 实例重新计算(派发更新),从而使它关联的组件重新渲染。

一句话总结:

vue.js 采用数据劫持结合发布-订阅模式,通过 Object.defineproperty 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调

2. computed 的实现原理

computed 本质是一个惰性求值的观察者。

computed 内部实现了一个惰性的 watcher,也就是 computed watcher,computed watcher 不会立刻求值,同时持有一个 dep 实例。

其内部通过 this.dirty 属性标记计算属性是否需要重新求值。

当 computed 的依赖状态发生改变时,就会通知这个惰性的 watcher,

computed watcher 通过 this.dep.subs.length 判断有没有订阅者,

有的话,会重新计算,然后对比新旧值,如果变化了,会重新渲染。(Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 重新渲染,本质上是一种优化。)

没有的话,仅仅把 this.dirty = true。(当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。)

3. computed 和 watch 有什么区别及运用场景?

区别

computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。

watch 侦听器 : 更多的是「观察」的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。

运用场景

运用场景:

当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。

当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

4. 为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?

Object.defineProperty 本身有一定的监控到数组下标变化的能力,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性(Vue 为什么不能检测数组变动 )。为了解决这个问题,经过 vue 内部处理后可以使用以下几种方法来监听数组

push();
pop();
shift();
unshift();
splice();
sort();
reverse();

由于只针对了以上 7 种方法进行了 hack 处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。

Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 2.x 里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择。

Proxy 可以劫持整个对象,并返回一个新的对象。Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

5. Vue 中的 key 到底有什么用?

key 是给每一个 vnode 的唯一 id,依靠 key,我们的 diff 操作可以更准确、更快速 (对于简单列表页渲染来说 diff 节点也更快,但会产生一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。)

diff 算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.

更准确 : 因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确,如果不加 key,会导致之前节点的状态被保留下来,会产生一系列的 bug。

更快速 : key 的唯一性可以被 Map 数据结构充分利用,相比于遍历查找的时间复杂度 O(n),Map 的时间复杂度仅仅为 O(1),源码如下:

function createKeyToOldIdx(children, beginIdx, endIdx) {
  let i, key;
  const map = {};
  for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key;
    if (isDef(key)) map[key] = i;
  }
  return map;
}

6. 谈一谈 nextTick 的原理

JS 运行机制

JS 执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:

  1. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

  2. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

  3. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

  4. 主线程不断重复上面的第三步。

c7471339f5fd860a84c4c54c36b12986.png
event-loop

主线程的执行过程就是一个 tick,而所有的异步结果都是通过 “任务队列” 来调度。消息队列中存放的是一个个的任务(task)。规范中规定 task 分为两大类,分别是 macro task 和 micro task,并且每个 macro task 结束后,都要清空所有的 micro task。

for (macroTask of macroTaskQueue) {
  // 1. Handle current MACRO-TASK
  handleMacroTask();

  // 2. Handle all MICRO-TASK
  for (microTask of microTaskQueue) {
    handleMicroTask(microTask);
  }
}

在浏览器环境中 :

常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate

常见的 micro task 有 MutationObsever 和 Promise.then

异步更新队列

可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。

然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。

Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

在 vue2.5 的源码中,macrotask 降级的方案依次是:setImmediate、MessageChannel、setTimeout

vue 的 nextTick 方法的实现原理:

  1. vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行

  2. microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕

  3. microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕

  4. 考虑兼容问题,vue 做了 microtask 向 macrotask 的降级方案

7. vue 是如何对数组方法进行变异的 ?

我们先来看看源码

const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);
const methodsToPatch = [
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "sort",
  "reverse"
];

/** * Intercept mutating methods and emit events */
methodsToPatch.forEach(function(method) {
  // cache original method
  const original = arrayProto[method];
  def(arrayMethods, method, function mutator(...args) {
    const result = original.apply(this, args);
    const ob = this.__ob__;
    let inserted;
    switch (method) {
      case "push":
      case "unshift":
        inserted = args;
        break;
      case "splice":
        inserted = args.slice(2);
        break;
    }
    if (inserted) ob.observeArray(inserted);
    // notify change
    ob.dep.notify();
    return result;
  });
});

/** * Observe a list of Array items. */
Observer.prototype.observeArray = function observeArray(items) {
  for (var i = 0, l = items.length; i < l; i++) {
    observe(items[i]);
  }
};

简单来说,Vue 通过原型拦截的方式重写了数组的 7 个方法,首先获取到这个数组的ob,也就是它的 Observer 对象,如果有新的值,就调用 observeArray 对新的值进行监听,然后手动调用 notify,通知 render watcher,执行 update

8. Vue 组件 data 为什么必须是函数 ?

new Vue()实例中,data 可以直接是一个对象,为什么在 vue 组件中,data 必须是一个函数呢?

因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染,产生副作用。

所以一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。new Vue 的实例是不会被复用的,因此不存在以上问题。

9. 谈谈 Vue 事件机制,手写$on,$off,$emit,$once

Vue 事件机制 本质上就是 一个 发布-订阅 模式的实现。

class Vue {
  constructor() {
    //  事件通道调度中心
    this._events = Object.create(null);
  }
  $on(event, fn) {
    if (Array.isArray(event)) {
      event.map(item => {
        this.$on(item, fn);
      });
    } else {
      (this._events[event] || (this._events[event] = [])).push(fn);
    }
    return this;
  }
  $once(event, fn) {
    function on() {
      this.$off(event, on);
      fn.apply(this, arguments);
    }
    on.fn = fn;
    this.$on(event, on);
    return this;
  }
  $off(event, fn) {
    if (!arguments.length) {
      this._events = Object.create(null);
      return this;
    }
    if (Array.isArray(event)) {
      event.map(item => {
        this.$off(item, fn);
      });
      return this;
    }
    const cbs = this._events[event];
    if (!cbs) {
      return this;
    }
    if (!fn) {
      this._events[event] = null;
      return this;
    }
    let cb;
    let i = cbs.length;
    while (i--) {
      cb = cbs[i];
      if (cb === fn || cb.fn === fn) {
        cbs.splice(i, 1);
        break;
      }
    }
    return this;
  }
  $emit(event) {
    let cbs = this._events[event];
    if (cbs) {
      const args = [].slice.call(arguments, 1);
      cbs.map(item => {
        args ? item.apply(this, args) : item.call(this);
      });
    }
    return this;
  }
}

10. 说说 Vue 的渲染过程

c8e610dd7bcc6bd10bf7d61987bfe581.png
render
  1. 调用 compile 函数,生成 render 函数字符串 ,编译过程如下:

    1. parse 函数解析 template,生成 ast(抽象语法树)

    2. optimize 函数优化静态节点 (标记不需要每次都更新的内容,diff 算法会直接跳过静态节点,从而减少比较的过程,优化了 patch 的性能)

    3. generate 函数生成 render 函数字符串

  2. 调用 new Watcher 函数,监听数据的变化,当数据发生变化时,Render 函数执行生成 vnode 对象

  3. 调用 patch 方法,对比新旧 vnode 对象,通过 DOM diff 算法,添加、修改、删除真正的 DOM 元素

11. 聊聊 keep-alive 的实现原理和缓存策略

export default {
  name: "keep-alive",
  abstract: true, // 抽象组件属性 ,它在组件实例建立父子关系的时候会被忽略,发生在 initLifecycle 的过程中
  props: {
    include: patternTypes, // 被缓存组件
    exclude: patternTypes, // 不被缓存组件
    max: [String, Number] // 指定缓存大小
  },

  created() {
    this.cache = Object.create(null); // 缓存
    this.keys = []; // 缓存的VNode的键
  },

  destroyed() {
    for (const key in this.cache) {
      // 删除所有缓存
      pruneCacheEntry(this.cache, key, this.keys);
    }
  },

  mounted() {
    // 监听缓存/不缓存组件
    this.$watch("include", val => {
      pruneCache(this, name => matches(val, name));
    });
    this.$watch("exclude", val => {
      pruneCache(this, name => !matches(val, name));
    });
  },

  render() {
    // 获取第一个子元素的 vnode
    const slot = this.$slots.default;
    const vnode: VNode = getFirstComponentChild(slot);
    const componentOptions: ?VNodeComponentOptions =
      vnode && vnode.componentOptions;
    if (componentOptions) {
      // name不在inlcude中或者在exlude中 直接返回vnode
      // check pattern
      const name: ?string = getComponentName(componentOptions);
      const { include, exclude } = this;
      if (
        // not included
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode;
      }

      const { cache, keys } = this;
      // 获取键,优先获取组件的name字段,否则是组件的tag
      const key: ?string =
        vnode.key == null
          ? // same constructor may get registered as different local components
            // so cid alone is not enough (#3269)
            componentOptions.Ctor.cid +
            (componentOptions.tag ? `::${componentOptions.tag}` : "")
          : vnode.key;
      // 命中缓存,直接从缓存拿vnode 的组件实例,并且重新调整了 key 的顺序放在了最后一个
      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance;
        // make current key freshest
        remove(keys, key);
        keys.push(key);
      }
      // 不命中缓存,把 vnode 设置进缓存
      else {
        cache[key] = vnode;
        keys.push(key);
        // prune oldest entry
        // 如果配置了 max 并且缓存的长度超过了 this.max,还要从缓存中删除第一个
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode);
        }
      }
      // keepAlive标记位
      vnode.data.keepAlive = true;
    }
    return vnode || (slot && slot[0]);
  }
};

原理

  1. 获取 keep-alive 包裹着的第一个子组件对象及其组件名

  2. 根据设定的 include/exclude(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例

  3. 根据组件 ID 和 tag 生成缓存 Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该 key 在 this.keys 中的位置(更新 key 的位置是实现 LRU 置换策略的关键)

  4. 在 this.cache 对象中存储该组件实例并保存 key 值,之后检查缓存的实例数量是否超过 max 的设置值,超过则根据 LRU 置换策略删除最近最久未使用的实例(即是下标为 0 的那个 key)

  5. 最后组件实例的 keepAlive 属性设置为 true,这个在渲染和执行被包裹组件的钩子函数会用到,这里不细说

LRU 缓存淘汰算法

LRU(Least recently used)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

de71572dd1393210e1072dea20cd9787.png
LRU

keep-alive 的实现正是用到了 LRU 策略,将最近访问的组件 push 到 this.keys 最后面,this.keys[0]也就是最久没被访问的组件,当缓存实例超过 max 设置值,删除 this.keys[0]

12. vm.$set()实现原理是什么?

受现代 JavaScript 的限制 (而且 Object.observe 也已经被废弃),Vue 无法检测到对象属性的添加或删除。

由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。

对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。

那么 Vue 内部是如何解决对象新增属性不能响应的问题的呢?

export function set(target: Array<any> | Object, key: any, val: any): any {
  // target 为数组
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
    target.length = Math.max(target.length, key);
    // 利用数组的splice变异方法触发响应式
    target.splice(key, 1, val);
    return val;
  }
  // target为对象, key在target或者target.prototype上 且必须不能在 Object.prototype 上,直接赋值
  if (key in target && !(key in Object.prototype)) {
    target[key] = val;
    return val;
  }
  // 以上都不成立, 即开始给target创建一个全新的属性
  // 获取Observer实例
  const ob = (target: any).__ob__;
  // target 本身就不是响应式数据, 直接赋值
  if (!ob) {
    target[key] = val;
    return val;
  }
  // 进行响应式处理
  defineReactive(ob.value, key, val);
  ob.dep.notify();
  return val;
}

  1. 如果目标是数组,使用 vue 实现的变异方法 splice 实现响应式

  2. 如果目标是对象,判断属性存在,即为响应式,直接赋值

  3. 如果 target 本身就不是响应式,直接赋值

  4. 如果属性不是响应式,则调用 defineReactive 方法进行响应式处理

❤️ 看完三件事

如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  • 点个【在看】,或者分享转发,让更多的人也能看到这篇内容

  • 关注公众号【全栈前端精选】,不定期分享原创&精品技术文章。

  • 公众号内回复:【 1 】。加入全栈前端精选公众号交流群。

d290c8a47dc9460f6c9f1cf9b76405f4.png

欢迎评论区留下你的精彩评论~

觉得文章不错可以分享到朋友圈让更多的小伙伴看到哦~

客官!在看一下呗

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

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

相关文章

大型网站架构演化发展历程

前面已经描述了大型网站系统的特点&#xff0c;而对一个大型网站系统&#xff0c;其架构也是重要的一个环节。 大型网站技术主要的挑战来自于庞大的用户、高并发以及海量的数据这三个方面。大型网站的形成就像一颗大树的成长&#xff0c;历尽长时间的磨练&#xff0c;最后枝繁叶…

dts数据库迁移工具_传统数据库迁移上云利器-ADAM

自1970年关系型数据库被提出以来&#xff0c;至今已有50年历史。但在关系型数据库领域正在发生着巨大的变化&#xff0c;首先是互联网的发展&#xff0c;使得开源数据库越来越受欢迎&#xff0c;可扩展性成为支撑业务发展的重要特性&#xff0c;比如WebScaleSQL就是几个互联网公…

python解zuobiaoxi方程_吴恩达《Machine Learning》精炼笔记 2:梯度下降与正规方程

作者&#xff1a;Peter红色石头的个人网站&#xff1a;红色石头的个人博客-机器学习、深度学习之路​www.redstonewill.com今天带来第二周课程的笔记&#xff1a;梯度下降与正规方程。主要内容&#xff1a;多维特征多变量梯度下降梯度下降法实践正规方程多维特征Multiple Featu…

计算机房按几类防雷,计算机机房防雷方案

常见问题计算机机房防雷方案2461一键分享这些微电子网络设备的普遍应用&#xff0c;使得防雷的问题显得越来越重要。由于微电子设备具有高密度、高速度、低电压、和低功耗等特性&#xff0c;这就使其对各种诸如雷电过电压、电力系统操作过电压、静电放电、电磁辐射等电磁干扰非…

webpack最新版本_webpack小结-开发环境构建优化

刚刚对我们前端项目做了一顿分析优化操作&#xff0c;因为接手时每次构建要花两分钟左右的时间&#xff0c;实在忍受不了&#xff0c;只能动手了。通过这次优化&#xff0c;重新温习了下 webpack 的一些知识。接下来会关于 webpack 展开写几篇心得&#xff1a;构建分析开发环境…

Xamarin Mono for VS开发窗体标题(Title)乱码解决方案

利用mono for VS开发一个手机程序&#xff0c;结果只有窗体的标题 title部分是乱码&#xff0c;其他所有地方中文都显示正常&#xff0c;很郁闷。百度很久无果。最后发现只要在 VS菜单中 的 文件->高级保存选型中奖编码设置为 Unicode或者UTF8就行了。更改所有有乱码的文件&…

nlp 命名实体识别 算法_中文命名实体识别算法 Lattice LSTM

中文命名实体识别 (NER) 算法按照输入的类型&#xff0c;可以大致分为 Character-based (字符) 和 Word-based (单词) 两种。这两种方法都存在一些缺陷&#xff0c;Character-based 不能利用词汇和词汇序列信息&#xff1b;Word-based 需要分词&#xff0c;分词的错误会对 NER …

margin background_margin:auto与布局展示

margin:auto 的作用机制。使用margin:auto居中&#xff0c;是css的基本操作。但会发现时不时的失灵。 这篇文章是对该属性的深度分析。首先&#xff0c;以下事实&#xff08;自动填充&#xff09;必须明确&#xff08;1&#xff09;有时候元素就算没有设置width 或height&#…

智能客户端ios_为什么现在的客户端开发越来越不吃香了

这是Kevin的第 672 篇原创&#xff0c;持续日更&#xff0c;做产品经理的创业斜杠青年。年底上线APP&#xff0c;是我们今年PMTalk在产品研发上的一个最终目标。启动这个项目前&#xff0c;团队、和身边创业朋友都不看好这件事&#xff0c;因为客户端开发太重了&#xff0c;小公…

用计算机录制声音让音质更好,电脑如何录屏?电脑录屏如何把声音也录制下来...

电脑如何录屏&#xff1f;电脑录屏如何把声音也录制下来2019年09月24日 14:25作者&#xff1a;黄页编辑&#xff1a;黄页分享电脑如何录屏?当你看到精彩的视频内容时&#xff0c;往往经常会出现&#xff0c;无法下载视频或者是下载很麻烦的问题。那么这个时候&#xff0c;可以…

第九周 10.25-10.31

10.25 HDU 4117 GRE Words 卡了很久的一个题目。比较综合。 看了很久题解还是各种写挫。 毕竟除了模拟题都没敲过那么长的。 题意&#xff1a;按顺序给N个单词&#xff0c;每个单词有权值&#xff0c;删去其中任意单词&#xff0c;使得前面的单词为后面单词的子串&#xff0c;求…

芯片设计中的latch_Latch-up (闩锁效应)

------------------------------------WeChat subscription account&#xff1a;Analog CMOS------------------------------------每周分享Analog IC学习资料/笔记。关注获取。。。。。00 - 本文内容CMOS 中的闩锁效应&#xff08;latch-up&#xff09;的来源具体的避免 latch…

pythonopencv算法_python opencv之SURF算法示例

本文介绍了python opencv之SURF算法示例&#xff0c;分享给大家&#xff0c;具体如下&#xff1a; 目标&#xff1a; SURF算法基础 opencv总SURF算法的使用 原理&#xff1a; 上节课使用了SIFT算法&#xff0c;当时这种算法效率不高&#xff0c;需要更快速的算法。在06年有人提…

c++判断ftp服务器文件存在性判断_BitTorrent协议与传统文件分发协议对比

一、文件传输前端时间做了和文件传输相关的工作&#xff0c;今天抽空做个总结。总结一下平常使用的几种协议以及BtTorrent协议。其实BitTorrent协议也不是什么新协议&#xff0c;它诞生也十几年了。咱也蹭个热点&#xff0c;最近几年大火的区块链技术也和BitTorrent协议有着异曲…

java基础全套

这是我自己早前听课时整理的java基础全套知识 使用于初学者 也可以适用于中级的程序员 我做成了chm文档的类型 你们可以下载 笔记是比较系统全面&#xff0c;可以抵得上市场上90%的学习资料。讨厌那些随便乱写的资料还有拿出来卖钱的人&#xff01;在这里我免费的分享出来供…

顺丰快递单号的规律_顺丰速运顺丰快递单号查询大全

如何一键查询快递单号顺丰快递单号查询跟踪&#xff0c;鸡蛋是我们日常生活当中最为常见的一种食材啦&#xff0c;相信在每一个家庭当中每天都会吃到鸡蛋&#xff0c;因为鸡蛋的营养价值非常高&#xff0c;它含有丰富的蛋白质&#xff0c;可以为我们身体提供能量&#xff0c;别…

用计算机求正有理数算术平方根的步骤,用计算器求算数平方根、用有理数估计算数平方根的大小.ppt...

用计算器求算数平方根、用有理数估计算数平方根的大小.ppt 上传人&#xff1a;jw****88文档编号&#xff1a;85573818上传时间&#xff1a;2020-06-11格式&#xff1a;PPT页数&#xff1a;20大小&#xff1a;2.14MB下载提示(请认真阅读)1.请仔细阅读文档&#xff0c;确保文档完…

【整理】强化学习与MDP

【入门&#xff0c;来自wiki】 强化学习是机器学习中的一个领域&#xff0c;强调如何基于环境而行动&#xff0c;以取得最大化的预期利益。其灵感来源于心理学中的行为主义理论&#xff0c;即有机体如何在环境给予的奖励或惩罚的刺激下&#xff0c;逐步形成对刺激的预期&#x…

nginx 根证书 服务器证书,Nginx双向证书校验(服务器验证客户端证书)

1、创建Root CA私钥openssl genrsa -out root-ca.key 10242、创建Root CA证书请求openssl req -new -out root-ca.csr -key root-ca.key3、签发Root CA根证书openssl x509 -req -in root-ca.csr -out root-ca.crt -signkey root-ca.key -CAcreateserial -days 36505、生成服务器…

移动app测试的多样性_做移动APP功能测试,这些因素你得都考虑到~

对于刚入行的测试新手来说&#xff0c;无论是做手动测试还是自动化测试&#xff0c;前期都避免不了学习前人经验&#xff0c;从而尽可能减少自己的漏测问题。作为一个过来人&#xff0c;今天&#xff0c;我就将自己的移动APP功能测试分享给大家&#xff0c;希望对新手有一定帮助…