面试官:在原生input上面使用v-model和组件上面使用有什么区别?

前言

还是上一篇面试官:来说说vue3是怎么处理内置的v-for、v-model等指令? 文章的那个粉丝,面试官接着问了他另外一个v-model的问题。

  • 面试官:vue3的v-model都用过吧,来讲讲。

  • 粉丝:v-model其实就是一个语法糖,在编译时v-model会被编译成:modelValue属性和@update:modelValue事件。一般在子组件中定义一个名为modelValue的props来接收父组件v-model传递的值,然后当子组件表单的值变化时再使用@update:modelValue抛出事件给父组件,由父组件来更新v-model绑定的变量。

  • 面试官:你说的这个是在组件上面使用v-model,原生input上面也支持v-model,你来说说原生input上面使用v-model以及和组件上面使用v-model有什么区别?

  • 粉丝:啊,两个不是一样的吗?都是:modelValue属性和@update:modelValue事件的语法糖吖。

  • 面试官:原生input标签接收的是value属性,监听的是input或者change事件。你说v-model会编译成:modelValue属性,但是input标签只接收value属性,那你传的modelValue属性input标签怎么接收的?同理你说v-model会编译成监听@update:modelValue事件,但是input标签只监听input或者change事件,那你传监听的@update:modelValue事件又是怎么触发的呢?

在之前的 面试官:只知道v-model是modelValue语法糖,那你可以走了 文章中我已经讲过了在组件中怎么将v-model编译成:modelValue属性和@update:modelValue事件,今天我们就来讲讲在原生input上面使用v-model和在组件上面使用有什么区别?

先说答案

来看看我画个这个流程图,如下:
full-progress

根据上面的流程图,我们知道了在组件上面使用v-model和原生input上面使用v-model区别主要有三点:

  • 组件上面的v-model编译后会生成modelValue属性和@update:modelValue事件。

    而在原生input上面使用v-model编译后不会生成modelValue属性,只会生成onUpdate:modelValue回调函数和vModelText自定义指令。(在 面试官:只知道v-model是modelValue语法糖,那你可以走了 文章中我们已经讲过了@update:modelValue事件其实等价于onUpdate:modelValue回调函数)

  • 在组件上面使用v-model,是由子组件中定义一个名为modelValue的props来接收父组件使用v-model绑定的变量,然后使用这个modelValue绑定到子组件的表单中。

    在原生input上面使用v-model,是由编译后生成的vModelText自定义指令在mountedbeforeUpdate钩子函数中去将v-model绑定的变量值更新到原生input输入框的value属性,以保证v-model绑定的变量值和input输入框中的值始终一致。

  • 在组件上面使用v-model,是由子组件使用emit抛出@update:modelValue事件,在@update:modelValue的事件处理函数中去更新v-model绑定的变量。

    而在原生input上面使用v-model,是由编译后生成的vModelText自定义指令在created钩子函数中去监听原生input标签的input或者change事件。在事件回调函数中去手动调用onUpdate:modelValue回调函数,然后在回调函数中去更新v-model绑定的变量。

关注公众号:前端欧阳,解锁我更多vue干货文章。还可以加我微信,私信我想看哪些vue原理文章,我会根据大家的反馈进行创作。

看个例子

下面这个是我写的一个demo,代码如下:

<template>
  <input v-model="msg" />
  <p>input value is: {{ msg }}</p>
</template>

<script setup lang="ts">
import { ref } from "vue";

const msg = ref();
</script>

上面的例子很简单,在原生input标签上面使用v-model绑定了msg变量。我们接下来看看编译后的js代码是什么样的,那么问题来了怎么找到编译后的js代码呢?

其实很简单直接在network上面找到你的那个vue文件就行了,比如我这里的文件是index.vue,那我只需要在network上面找叫index.vue的文件就行了。但是需要注意一下network上面有两个index.vue的js请求,分别是template模块+script模块编译后的js文件,和style模块编译后的js文件。

那怎么区分这两个index.vue文件呢?很简单,通过query就可以区分。由style模块编译后的js文件的URL中有type=style的query,如下图所示:
network

接下来我们来看看编译后的index.vue,简化的代码如下:

import {
  Fragment as _Fragment,
  createElementBlock as _createElementBlock,
  createElementVNode as _createElementVNode,
  defineComponent as _defineComponent,
  openBlock as _openBlock,
  toDisplayString as _toDisplayString,
  vModelText as _vModelText,
  withDirectives as _withDirectives,
  ref,
} from "/node_modules/.vite/deps/vue.js?v=23bfe016";

const _sfc_main = _defineComponent({
  __name: "index",
  setup(__props, { expose: __expose }) {
    __expose();
    const msg = ref();
    const __returned__ = { msg };
    return __returned__;
  },
});

function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  return (
    _openBlock(),
    _createElementBlock(
      _Fragment,
      null,
      [
        _withDirectives(
          _createElementVNode(
            "input",
            {
              "onUpdate:modelValue":
                _cache[0] || (_cache[0] = ($event) => ($setup.msg = $event)),
            },
            null,
            512
          ),
          [[_vModelText, $setup.msg]]
        ),
        _createElementVNode(
          "p",
          null,
          "input value is: " + _toDisplayString($setup.msg),
          1
        ),
      ],
      64
    )
  );
}
_sfc_main.render = _sfc_render;
export default _sfc_main;

从上面的代码中我们可以看到编译后的js代码主要分为两块。

第一块是_sfc_main组件对象,里面有name属性和setup方法。一个vue组件其实就是一个对象,这里的_sfc_main对象就是一个vue组件对象。

我们接着来看第二块_sfc_render,从名字我想你应该已经猜到了他是一个render函数。执行这个_sfc_render函数就会生成虚拟DOM,然后再由虚拟DOM生成浏览器上面的真实DOM。我们接下来主要看看这个render函数。

render函数

这个render函数前面会调用openBlock函数和createElementBlock函数。他的作用是在编译时尽可能的提取多的关键信息,可以减少运行时比较新旧虚拟DOM带来的性能开销。我们这篇文章不关注这点,所以就不细讲了。

来看看里层的数组,数组中有两项。分别是withDirectives函数和createElementVNode函数,数组中的这两个函数分别对应的就是template中的input标签和p标签。我们主要来关注input标签,也就是withDirectives函数。

withDirectives函数

这个withDirectives是否觉得有点眼熟?他是vue提供的一个进阶API,我们平时写业务基本不会用到他。作用是给vnode(虚拟DOM)增加自定义指令。

接收两个参数,第一个参数为需要添加指令的vnode,第二个参数是由自定义指令组成的二维数组。二维数组的第一层是表示有哪些自定义指令,第二层表示的是指令名称、绑定值、参数、修饰符。第二层的结构为: [Directive, value, argument, modifiers] 。如果不需要,可以省略数组的尾元素。

举个例子:

import { h, withDirectives } from 'vue'

// 一个自定义指令
const pin = {
  mounted() {
    /* ... */
  },
  updated() {
    /* ... */
  }
}

// <div v-pin:top.animate="200"></div>
const vnode = withDirectives(h('div'), [
  [pin, 200, 'top', { animate: true }]
])

上面这个例子定义了一个pin的自定义指令,调用h函数生成vnode传给withDirectives函数的第一个参数。第二个参数自定义指令数组,我们这里只传了一个pin自定义指令。来看看[Directive, value, argument, modifiers]

  • 第一个Directive字段:“指令名称”对应的就是pin自定义指令。

  • 第二个value字段:“指令值”对应的就是200。

  • 第三个字段argument字段:“参数”对应的就是top参数。

  • 第四个字段modifiers字段:“修饰符”对应的就是animate修饰符。

所以上面的withDirectives函数实际就是对应的<div v-pin:top.animate="200"></div>

createElementVNode函数

看见这个函数名字我想你应该也猜到了,作用是创建vnode(虚拟dom)。这个函数和vue提供的 h函数差不多,底层调用的都是一个名为createBaseVNode的函数。接收的第一个参数既可以是一个字符串 (用于原生元素) 也可以是一个 Vue 组件定义。接收的第二个参数是要传递的 prop,第三个参数是子节点。

举个例子:

createElementVNode("input", {
  value: 12,
})

上面这个例子创建了一个input的vnode,输入框中的值为12

搞清楚了withDirectives函数和createElementVNode函数的作用,我们回过头来看之前对应input标签的代码你应该就很容易理解了。代码如下:

_withDirectives(
  _createElementVNode(
    "input",
    {
      "onUpdate:modelValue":
        _cache[0] || (_cache[0] = ($event) => ($setup.msg = $event)),
    },
    null,
    512
  ),
  [[_vModelText, $setup.msg]]
)

调用withDirectives函数,传入两个参数。第一个参数为调用createElementVNode函数生成input的vnode。第二个参数为传入的自定义指令组成的数组,很明显这里的二维数组的第一层只有一项,说明只传入了一个自定义指令。

回忆一下前面说的二维数组中的第二层的结构: [Directive, value, argument, modifiers],第一个字段Directive表示这里传入了一个名为vModelText的自定义指令,第二个字段value表示给vModelText指令绑定的值为$setup.msg。我们在 Vue 3 的 setup语法糖到底是什么东西?文章中已经讲过了,这里的$setup.msg实际就是指向的是setup中定义的名为msg的ref变量。

我们再来看里面的createElementVNode函数,创建一个input的vnode。传入了一个名为onUpdate:modelValue的props属性,属性值是一个经过缓存的回调函数。

为什么需要缓存呢?因为每次更新页面都会执行一次render函数,每次执行render函数都会调用一次createElementVNode函数。如果不缓存那不就变成了每次更新页面都会生成一个onUpdate:modelValue的回调函数。这里的回调函数也很简单,接收一个$event变量。这个$event变量就是输入框中输入的值,然后最新的输入框中的值同步到setup中的msg变量。

总结一下就是给input标签的vnode添加了一个vModelText的自定义指令,并且给指令绑定的值为msg变量。还有就是在input标签的vnode中添加了一个onUpdate:modelValue的属性,属性值是一个回调函数,触发这个回调函数就会将msg变量的值更新为输入框中的最新值。我们知道input输入框中的值对应的是value属性,监听的是input和change事件。那么这里有两个问题:

  • 如何将vModelText自定义指令绑定的msg变量的值传递给input输入框中的value属性的呢?

  • input标签监听input和change事件,编译后input上面却是一个名为onUpdate:modelValue的props回调函数?

要回答上面的两个问题我们需要看vModelText自定义指令是什么样的。

vModelText自定义指令

vModelText是一个运行时的v-model指令,为什么说是运行时呢? 面试官:只知道v-model是modelValue语法糖,那你可以走了 文章中我们已经讲过了,在编译时就会将组件上面的v-model指令编译成modelValue属性和@update:modelValue事件。所以当运行时在组件上已经没有了v-model指令了,只有原生input在运行时依然还有v-model指令,也就是vModelText自定义指令。

我们来看看vModelText自定义指令的代码:

const vModelText = {
  created(el, { modifiers: { lazy, trim, number } }, vnode) {
    // ...
  },
  mounted(el, { value }) {
    // ...
  },
  beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) {
    // ...
  },
}

从上面可以看到vModelText自定义指令中使用了三个钩子函数:createdmountedbeforeUpdate,我们来看看上面三个钩子函数中使用到的参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。

  • binding:一个对象,包含以下属性。上面的例子中是直接解构了binding对象。

    • value:传递给指令的值。例如在 v-model="msg" 中,其中msg变量的值为“hello word”,value的值就是“hello word”。

    • modifiers:一个包含修饰符的对象,v-model支持lazy, trim, number这三个修饰符。

      • lazy:默认情况下,v-model 会在每次 input 事件后更新数据。你可以添加 lazy 修饰符来改为在每次 change 事件后更新数据,在input输入框中就是失去焦点时再更新数据。

      • trim:去除用户输入内容中两端的空格。

      • number:让用户输入自动转换为数字。

  • vnode:绑定元素的 VNode(虚拟DOM)。

mounted钩子函数

我们先来看mounted钩子函数,代码如下:

const vModelText = {
  mounted(el, { value }) {
    el.value = value == null ? "" : value;
  },
}

mounted中的代码很简单,在mounted时如果v-model绑定的msg变量的值不为空,那么就将msg变量的值同步到input输入框中。

created钩子函数

我们接着来看created钩子函数中的代码,如下:

const assignKey = Symbol("_assign");
const vModelText = {
  created(el, { modifiers: { lazy, trim, number } }, vnode) {
    el[assignKey] = getModelAssigner(vnode);
    const castToNumber =
      number || (vnode.props && vnode.props.type === "number");
    addEventListener(el, lazy ? "change" : "input", (e) => {
      if (e.target.composing) return;
      let domValue = el.value;
      if (trim) {
        domValue = domValue.trim();
      }
      if (castToNumber) {
        domValue = looseToNumber(domValue);
      }
      el[assignKey](domValue);
    });
    if (trim) {
      addEventListener(el, "change", () => {
        el.value = el.value.trim();
      });
    }
    if (!lazy) {
      addEventListener(el, "compositionstart", onCompositionStart);
      addEventListener(el, "compositionend", onCompositionEnd);
    }
  },
}

created钩子函数中的代码主要分为五部分。

第一部分

首先我们来看第一部分代码:

el[assignKey] = getModelAssigner(vnode);

我们先来看这个getModelAssigner函数。代码如下:

const getModelAssigner = (vnode) => {
const fn = vnode.props["onUpdate:modelValue"];
return isArray(fn) ? (value) => invokeArrayFns(fn, value) : fn;
};

getModelAssigner函数的代码很简单,就是返回vnode上面名为onUpdate:modelValue的props回调函数。前面我们已经讲过了执行这个回调函数会同步更新v-model绑定的msg变量。

所以第一部分代码的作用就是取出input标签上面名为onUpdate:modelValue的props回调函数,然后赋值给input标签对象的assignKey方法上面,后面再输入框中的input或者chang事件触发时会手动调用。这个assignKey是一个Symbol,唯一的标识符。

第二部分

再来看第二部分代码:

const castToNumber =
number || (vnode.props && vnode.props.type === "number");

castToNumber表示是否使用了.number修饰符,或者input输入框上面是否有type=number的属性。如果castToNumber的值为true,后续处理输入框的值时会将其转换成数字。

第三部分

我们接着来看第三部分的代码:

addEventListener(el, lazy ? "change" : "input", (e) => {
if (e.target.composing) return;
let domValue = el.value;
if (trim) {
  domValue = domValue.trim();
}
if (castToNumber) {
  domValue = looseToNumber(domValue);
}
el[assignKey](domValue);
});

对input输入框进行事件监听,如果有.lazy修饰符就监听change事件,否则监听input事件。看看,这不就和.lazy修饰符的作用对上了嘛。.lazy修饰符的作用是在每次change事件触发时再去更新数据。
我们接着看里面的事件处理函数,来看看第一行代码:

if (e.target.composing) return;

当用户使用拼音输入法输入汉字时,正在输入拼音阶段也会触发input事件的。但是一般情况下我们只希望真正合成汉字时才触发input去更新数据,所以在输入拼音阶段触发的input事件需要被return。至于e.target.composing什么时候被设置为true,什么时候又是false,我们接着会讲。

后面的代码就很简单了,将输入框中的值也就是el.value赋值给domValue变量。如果使用了.trim修饰符,就执行trim方法,去除掉domValue变量中两端的空格。

如果castToNumber的值为true,表示使用了.number修饰符或者在input上面使用了type=number。调用looseToNumber方法将domValue字符串转换为数字。

最后将处理后的domValue,也就是处理后的输入框中的输入值,作为参数调用el[assignKey]方法。我们前面讲过了el[assignKey]中存的就是input标签上面名为onUpdate:modelValue的props回调函数,执行el[assignKey]方法就是执行回调函数,在回调函数中会将v-model绑定的msg变量的值更新为处理后的输入框中的输入值。

现在你知道了为什么input标签监听input和change事件,编译后input上面却是一个名为onUpdate:modelValue的props回调函数了?

因为在input或者change事件的回调中会将输入框的值根据传入的修饰符进行处理,然后将处理后的输入框的值作为参数手动调用onUpdate:modelValue回调函数,在回调函数中更新绑定的msg变量。

第四部分

我们接着来看第四部分的代码,如下:

if (trim) {
  addEventListener(el, "change", () => {
    el.value = el.value.trim();
  });
}

这一块代码很简单,如果使用了.trim修饰符,触发change事件,在input输入框中就是失去焦点时。就会将输入框中的值也trim一下,去掉前后的空格。

为什么需要有这块代码,前面在input或者change事件中不是已经对输入框中的值进行trim处理了吗?而且后面的beforeUpdate钩子函数中也执行了el.value = newValue将输入框中的值更新为v-model绑定的msg变量的值。

答案是:前面确实对输入框中拿到的值进行trim处理,然后将trim处理后的值更新为v-model绑定的msg变量。但是我们并没有将输入框中的值更新为trim处理后的,虽然在beforeUpdate钩子函数中会将输入框中的值更新为v-model绑定的msg变量。但是如果只是在输入框的前后输入空格,那么经过trim处理后在beforeUpdate钩子函数中就会认为输入框中的值和msg变量的值相等。就不会执行el.value = newValue,此时输入框中的值还是有空格的,所以需要执行第四部分的代码将输入框中的值替换为trim后的值。

第五部分

我们接着来看第五部分的代码,如下:

if (!lazy) {
  addEventListener(el, "compositionstart", onCompositionStart);
  addEventListener(el, "compositionend", onCompositionEnd);
}

如果没有使用.lazy修饰符,也就是在每次input时都会对绑定的变量进行更新。

这里监听的compositionstart事件是:文本合成系统如开始新的输入合成时会触发 compositionstart 事件。举个例子:当用户使用拼音输入法开始输入汉字时,这个事件就会被触发。

这里监听的compositionend事件是:当文本段落的组成完成或取消时,compositionend 事件将被触发。举个例子:当用户使用拼音输入法,将输入的拼音合成汉字时,这个事件就会被触发。

来看看onCompositionStart中的代码,如下:

function onCompositionStart(e) {
  e.target.composing = true;
}

代码很简单,将e.target.composing设置为true。还记得我们前面在input输入框的input或者change事件中会先去判断这个e.target.composing,如果其为true,那么就return掉,这样就不会在输入拼音时也会更新v-model绑定的msg变量了。

我们来看看onCompositionEnd中的代码,如下:

function onCompositionEnd(e) {
  const target = e.target;
  if (target.composing) {
    target.composing = false;
    target.dispatchEvent(new Event("input"));
  }
}

当将拼音合成汉字时会将e.target.composing设置为false,这里为什么要调用target.dispatchEvent手动触发一个input事件呢?

答案是:将拼音合成汉字时input事件会比compositionend事件先触发,由于此时的e.target.composing的值还是true,所以input事件中后续的代码就会被return。所以才需要将e.target.composing重置为false后,手动触发一个input事件,更新v-model绑定的msg变量。

beforeUpdate钩子函数

我们接着来看看beforeUpdate钩子函数,会在每次因为响应式状态变更,导致页面更新之前调用,代码如下:

const vModelText = {
  beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) {
    el[assignKey] = getModelAssigner(vnode);
    // avoid clearing unresolved text. #2302
    if (el.composing) return;

    const elValue =
      number || el.type === "number" ? looseToNumber(el.value) : el.value;
    const newValue = value == null ? "" : value;

    if (elValue === newValue) {
      return;
    }

    if (document.activeElement === el && el.type !== "range") {
      if (lazy) {
        return;
      }
      if (trim && el.value.trim() === newValue) {
        return;
      }
    }

    el.value = newValue;
  },
};

看完了前面的created函数,再来看这个beforeUpdate函数就很简单了。beforeUpdate钩子函数最终要做的事情就是最后的这行代码:

el.value = newValue;

这行代码的意思是将输入框中的值更新成v-model绑定的msg变量,为什么需要在beforeUpdate钩子函数中执行呢?

答案是msg是一个响应式变量,如果在父组件上面因为其他原因改变了msg变量的值后,这个时候就需要将input输入框中的值同步更新为最新的msg变量。这也就解释了我们前面的问题:如何将vModelText自定义指令绑定的msg变量的值传递给input输入框中的value属性的呢?

第一行代码是:

el[assignKey] = getModelAssigner(vnode);

这里再次将vnode上面名为onUpdate:modelValue的props回调函数赋值给el[assignKey],之前在created的时候不是已经赋值过一次了吗,这里为什么会再次赋值呢?

答案是在有的场景中是不会缓存onUpdate:modelValue回调函数,如果没有缓存,那么每次执行render函数都会生成新的onUpdate:modelValue回调函数。所以才需要在beforeUpdate钩子函数中每次都将最新的onUpdate:modelValue回调函数赋值给el[assignKey],当在input或者change事件触发时执行el[assignKey]的时候就是执行的最新的onUpdate:modelValue回调函数。

再来看看第二行代码,如下:

// avoid clearing unresolved text. #2302
if (el.composing) return;

这行代码是为了修复bug:如果在输入拼音的过程中,还没有合成汉字之前。如果有其他的响应式变量的值变化导致页面刷新,这种时候就应该return。否则由于此时的msg变量的值还是null,如果执行el.value = newValue,输入框中的输入值就会被清空。详情请查看issue: https://github.com/vuejs/core/issues/2302

后面的代码就很简单了,其中的document.activeElement属性返回获得当前焦点(focus)的 DOM 元素,还有type = "range"我们平时基本不会使用。根据使用的修饰符拿到处理后的input输入框中的值,然后和v-model绑定的msg变量进行比较。如果两者相等自然不需要执行el.value = newValue将输入框中的值更新为最新值。

总结

现在来看这个流程图你应该就很容易理解了:
full-progress
在组件上面使用v-model和原生input上面使用v-model区别主要有三点:

  • 组件上面的v-model编译后会生成modelValue属性和@update:modelValue事件。

    而在原生input上面使用v-model编译后不会生成modelValue属性,只会生成onUpdate:modelValue回调函数和vModelText自定义指令。(在 面试官:只知道v-model是modelValue语法糖,那你可以走了 文章中我们已经讲过了@update:modelValue事件其实等价于onUpdate:modelValue回调函数)

  • 在组件上面使用v-model,是由子组件中定义一个名为modelValue的props来接收父组件使用v-model绑定的变量,然后使用这个modelValue绑定到子组件的表单中。

    在原生input上面使用v-model,是由编译后生成的vModelText自定义指令在mountedbeforeUpdate钩子函数中去将v-model绑定的变量值更新到原生input输入框的value属性,以保证v-model绑定的变量值和input输入框中的值始终一致。

  • 在组件上面使用v-model,是由子组件使用emit抛出@update:modelValue事件,在@update:modelValue的事件处理函数中去更新v-model绑定的变量。

    而在原生input上面使用v-model,是由编译后生成的vModelText自定义指令在created钩子函数中去监听原生input标签的input或者change事件。在事件回调函数中去手动调用onUpdate:modelValue回调函数,然后在回调函数中去更新v-model绑定的变量。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

储能展-CBTC-2024上海储能技术展会共话储能高质量发展

2024-CBTC上海国际储能技术展会 展会时间&#xff1a;7月24-26日 展会地址&#xff1a;上海&#xff08;虹桥&#xff09;国家会展中心 主办单位&#xff1a;湖南省电池产业协会/ 中国设备管理协会 /沪粤储能产业联盟/ 深圳国际投融资商会 国际氢能投融资与发展联…

Qt Debug模式下应用程序输出界面乱码【已解决】

Qt Debug模式下应用程序输出乱码 一、问题描述二、解决方法三、相关测试 一、问题描述 源码为utf-8编码. Qt Creator在Debug模式下运行程序&#xff0c;下方应用程序输出界面显示乱码. 但正常运行无乱码&#xff1a; 二、解决方法 尝试修改文件编码、执行编码无果… 可参考…

Python从0到100(十四):高级函数及函数使用进阶

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

Day11-Java进阶-HashSet集合LinkedHashSet-Collection工具类Map集合

1. HashSet集合 HashSet-JDK8版本及以后-面试常问 2. LinkedHashSet-Collection工具类 2.1 LinkedHashSet 2.2 Collection工具类 3. Map集合 3.1 Map接口介绍 3.2 Map 集合的遍历方式 3.2.1 三种方式介绍 package com.itheima.map;import java.util.HashMap; import java.ut…

【C++类和对象】日期类的实现

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

【MATLAB源码-第64期】matlab基于DWA算法的机器人局部路径规划包含动态障碍物和静态障碍物。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 动态窗口法&#xff08;Dynamic Window Approach&#xff0c;DWA&#xff09;是一种局部路径规划算法&#xff0c;常用于移动机器人的导航和避障。这种方法能够考虑机器人的动态约束&#xff0c;帮助机器人在复杂环境中安全、…

PCB上有哪些元素

过孔&#xff1a;是用来切换层的 丝印&#xff1a;就是标记&#xff08;白色的线或者符号&#xff09; 焊盘&#xff1a;焊接元器件&#xff0c;相当于线头&#xff0c;连接各个元件 通孔埋孔盲孔&#xff0c;都是用来换层&#xff0c;内部没有桐&#xff0c;是用来固定的 线路…

C++:基础语法

一、命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化&#xff0c; 以避免命名冲突或名字污染&#xff0c;n…

uniapp微信小程序(商城项目)

最近&#xff0c;闲来无事&#xff0c;打算学一下uniapp小程序 于是在跟着某站上学着做了一个小程序&#xff0c;主要是为了学uniapp和vue。某站黑马优购 完成的功能主要有&#xff1a;首页、搜索、分类和购物车。 有人问了为什么没有登录、和添加订单呢&#xff1f;问的很好…

2.4 Web容器配置:Tomcat

2.4 Web容器配置 2.4.1Tomcat配置1.常规配置2. HTTPS配置 *********** 2.4.1Tomcat配置 1.常规配置 在SpringBoot项目中&#xff0c;可以内置Tomcat、Jetly、Undertow、Netty等容器。 当开发者添加了spring-boot-starter-web依赖之后&#xff0c;默认会使用Tomcat作为Web容器…

【Linux学习】初始冯诺漫体系结构

文章目录 认识冯诺依曼系统 认识冯诺依曼系统 什么是冯诺依曼体系结构&#xff1f; 冯诺依曼体系结构是一种将程序指令和数据以二进制形式存放在主存储器中&#xff0c;由中央处理器统一控制和执行的计算机系统结构。冯诺依曼体系结构实现了程序的可编程性和硬件与软件的分离&…

【C语言】每日一题,快速提升(10)!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 题目&#xff1a;圣诞树 输入&#xff1a; 1输出&#xff1a; * * * * * **说明&#xff1a; 输入&#xff1a; 2输出&#xff1a; * * * * * * * …

【C++】一篇文章带你深入了解list

目录 一、list的介绍二、 标准库中的list类2.1 list的常见接口说明2.1.1 list对象的常见构造2.1.1.1 [无参构造函数](https://legacy.cplusplus.com/reference/list/list/list/)2.1.1.2 [有参构造函数(构造并初始化n个val)](https://legacy.cplusplus.com/reference/list/list/…

idea配置推荐插件详细讲解

配置插件是为了在开发过程中提高效率、改善开发体验而安装的。在IntelliJ IDEA中&#xff0c;你可以按照以下步骤配置插件&#xff1a; 打开插件配置页面&#xff1a; ● 在顶部菜单栏中选择 “File” -> “Settings”&#xff08;或者使用快捷键 Ctrl Alt S&#xff09;…

相亲平台app小程序

相亲平台app小程序是一种基于手机应用的微型程序&#xff0c;专为在线相亲交友活动设计。它提供了一系列的功能&#xff0c;旨在帮助用户更方便、更高效地找到心仪的伴侣。 首先&#xff0c;用户可以在个人资料部分上传照片、填写个人资料、设置兴趣爱好等信息&#xff0c;以便…

【行为型模型】迭代器模式

一、迭代器模式概述 迭代器模式定义&#xff1a;提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不暴露其内部的表示。把游走的任务放在送代器上&#xff0c;而不是聚合上。这样简化了聚含的接口和实现,也让责任各得其所。(对象行为型) 迭代器模式的优缺点&…

uni-app开发canvas绘图画画,记录每一步画的信息, 并实现后退功能

在uni-app中&#xff0c;要实现canvas绘图并记录每一步的信息以实现后退功能&#xff0c;你需要做几件事&#xff1a; 初始化Canvas上下文&#xff1a;首先&#xff0c;你需要在页面加载时初始化canvas上下文。 记录绘图步骤&#xff1a;在绘图过程中&#xff0c;你需要记录每…

20240416,深拷贝浅拷贝,对象初始化和清理,对象模型和THIS指针

哈哈哈乌龟越狱了 目录 2.5 深拷贝&浅拷贝 2.6 初始化列表 2.7 类对象作为类成员 2.8 静态成员 2.9 成员变量和成员函数分开存储 2.10 THIS指针的用途 2.11 空指针访问成员函数 2.12 COSNT修饰成员函数 2.5 深拷贝&浅拷贝 浅拷贝&#xff1a;简单的赋值拷贝…

揭秘分销系统:商业模式的新风向

大家好&#xff0c;我是微三云周丽&#xff0c;今天给大家分析当下市场比较火爆的商业模式&#xff01; 小编今天跟大伙们分享什么是分销系统&#xff1f; 在数字化浪潮席卷全球的今天&#xff0c;电子商务以其独特的优势&#xff0c;正在重塑商业世界的格局。其中&#xff0…

css设置输入框边框无效或者不展示边框

bug描述&#xff1a;在聚焦的时候&#xff0c;期望输入框的边框是主题色&#xff0c;但是不知道是个啥颜色。 发现输入框input以及textarea样式css标签:focus更改样式无效这个问题。 若希望实现input在触发焦点时更改样式&#xff0c;通常会想到使用:focus选择器&#xff0c;…