面試官:在原生input上面使用v-model和組件上面使用有什麼區別?

来源:https://www.cnblogs.com/heavenYJJ/p/18152728
-Advertisement-
Play Games

前言 還是上一篇面試官:來說說vue3是怎麼處理內置的v-for、v-model等指令? 文章的那個粉絲,面試官接著問了他另外一個v-model的問題。 面試官:vue3的v-model都用過吧,來講講。 粉絲:v-model其實就是一個語法糖,在編譯時v-model會被編譯成:modelValue ...


前言

還是上一篇面試官:來說說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綁定的變數。

看個例子

下麵這個是我寫的一個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綁定的變數。

關註公眾號:前端歐陽,解鎖我更多vue乾貨文章。還可以加我微信,私信我想看哪些vue原理文章,我會根據大家的反饋進行創作。
qrcode


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • cls_oracle_logs.sh腳本的輸出日誌中有TNS-12508錯誤,具體如下所示 ........................................................................LSNRCTL> Current Listener is gsp ...
  • Oracle資料庫的告警日誌中出WARNING: too many parse errors這些告警信息的話,如果遇到這個問題,我們應該如何分析呢? 下麵簡單聊一下如何分析這個錯誤。該告警信息其實是12.2版本中的一個特性增強。在以前的Oracle版本中,資料庫出現瞭解析錯誤時,資料庫的alert日 ...
  • 本文分享自華為雲社區《重磅新品發佈!雲耀資料庫HRDS,享受輕量級的極致體驗!》,作者:GaussDB 資料庫。 所謂,凡有井水處,即能歌柳詞。 大數據時代,凡有數據處,必有資料庫。 隨著業務需求的不斷擴大和數據量的激增,資料庫的使用場景滲透到了生活的方方面面,不再是大型企業或技術部門的專利,市場對 ...
  • 夏天馬上就要到了,“瘦身”不光是特定人群的需求,也是數據中心的需求。構建輕量化、低碳化、高性價比的新型數據中心,更有效地支撐經濟社會數字化轉型,已成為業界主流趨勢。 如何讓數據中心“熱辣瘦身”?輕量級存儲集群控制器——天翼雲存儲資源盤活系統HBlock必不可少! “HBlock健身房”究竟是如何發揮 ...
  • 前言 頁面轉場動畫是指在應用程式中,當用戶導航到另一個頁面時,使用動畫效果來過渡頁面之間的切換。這樣做的目的是為了提升用戶體驗,使頁面之間的切換更加平滑和有趣。 常見的頁面轉場動畫包括淡入淡出、滑動、翻轉、縮放等效果。通過使用這些動畫效果,可以給用戶一種流暢的感覺,讓頁面之間的切換更加自然。 在 ...
  • 前言 在HarmonyOS中,可以通過以下方法放大縮小視圖: 使用縮放手勢:可以使用雙指捏合手勢來放大縮小視圖。將兩個手指放在屏幕上,並向內或向外移動手指,即可進行放大或縮小操作。 使用系統提供的縮放控制項:在HarmonyOS的開發中,可以使用系統提供的縮放控制項來實現視圖的放大縮小功能。通過在布 ...
  • 要使用 MediaCodec 在 Android 上進行硬解碼,並獲取 RGBA 數據,你可以按照以下步驟進行操作: 創建 MediaExtractor 對象並設置要解碼的 MP4 文件路徑: MediaExtractor extractor = new MediaExtractor(); extr ...
  • 寫在前面 tips:點贊 + 收藏 = 學會! 我們已經介紹了radash的相關信息和部分Array相關方法,詳情可前往主頁查看。 本篇我們繼續介紹radash中Array的相關方法的剩餘方法。 本期文章發佈後,作者也會同步整理出Array方法的使用目錄,包括文章說明和腦圖說明。 因為方法較多,後續 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...