欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3+TypeScript實現(xiàn)遞歸菜單組件的完整實例

 更新時間:2021年08月24日 12:42:54   作者:ssh_晨曦時夢見兮  
Vue.js中的遞歸組件是一個可以調(diào)用自己的組件,遞歸組件一般用于博客上顯示評論,形菜單或者嵌套菜單,文章主要給大家介紹了關(guān)于Vue3+TypeScript實現(xiàn)遞歸菜單組件的相關(guān)資料,需要的朋友可以參考下

前言

小伙伴們好久不見,最近剛?cè)肼毿鹿?,需求排的很滿,平常是實在沒時間寫文章了,更新頻率會變得比較慢。

周末在家閑著無聊,突然小弟過來緊急求助,說是面試騰訊的時候,對方給了個 Vue 的遞歸菜單要求實現(xiàn),回來找我復(fù)盤。

正好這周是小周,沒想著出去玩,就在家寫寫代碼吧,我看了一下需求,確實是比較復(fù)雜,需要利用好遞歸組件,正好趁著這

個機會總結(jié)一篇 Vue3 + TS 實現(xiàn)遞歸組件的文章。

需求

可以先在Github Pages 中預(yù)覽一下效果。

需求是這樣的,后端會返回一串可能有無限層級的菜單,格式如下:

[
  {
    id: 1,
    father_id: 0,
    status: 1,
    name: '生命科學(xué)競賽',
    _child: [
      {
        id: 2,
        father_id: 1,
        status: 1,
        name: '野外實習(xí)類',
        _child: [{ id: 3, father_id: 2, status: 1, name: '植物學(xué)' }],
      },
      {
        id: 7,
        father_id: 1,
        status: 1,
        name: '科學(xué)研究類',
        _child: [
          { id: 8, father_id: 7, status: 1, name: '植物學(xué)與植物生理學(xué)' },
          { id: 9, father_id: 7, status: 1, name: '動物學(xué)與動物生理學(xué)' },
          { id: 10, father_id: 7, status: 1, name: '微生物學(xué)' },
          { id: 11, father_id: 7, status: 1, name: '生態(tài)學(xué)' },
        ],
      },
      { id: 71, father_id: 1, status: 1, name: '添加' },
    ],
  },
  {
    id: 56,
    father_id: 0,
    status: 1,
    name: '考研相關(guān)',
    _child: [
      { id: 57, father_id: 56, status: 1, name: '政治' },
      { id: 58, father_id: 56, status: 1, name: '外國語' },
    ],
  },
]

1、每一層的菜單元素如果有 _child 屬性,這一項菜單被選中以后就要繼續(xù)展示這一項的所有子菜單,預(yù)覽一下動圖:

2、并且點擊其中的任意一個層級,都需要把菜單的 完整的 id 鏈路 傳遞到最外層,給父組件請求數(shù)據(jù)用。比如點擊了 科學(xué)研究類。那么向外 emit 的時候還需要帶上它的第一個子菜單 植物學(xué)與植物生理學(xué) 的 id,以及它的父級菜單 生命科學(xué)競賽 的 id,也就是 [1, 7, 8]。

3、每一層的樣式還可以自己定制。

實現(xiàn)

這很顯然是一個遞歸組件的需求,在設(shè)計遞歸組件的時候,我們要先想清楚數(shù)據(jù)到視圖的映射。

在后端返回的數(shù)據(jù)中,數(shù)組的每一層可以分別對應(yīng)一個菜單項,那么數(shù)組的層則就對應(yīng)視圖中的一行,當(dāng)前這層的菜單中,被點擊選中 的那一項菜單的 child 就會被作為子菜單數(shù)據(jù),交給遞歸的 NestMenu 組件,直到某一層的高亮菜單不再有 child,則遞歸終止。

由于需求要求每一層的樣式可能是不同的,所以再每次調(diào)用遞歸組件的時候,我們都需要從父組件的 props 中拿到一個 depth 代表層級,并且把這個 depth + 1 繼續(xù)傳遞給遞歸的 NestMenu 組件。

重點主要就是這些,接下來編碼實現(xiàn)。

先看 NestMenu 組件的 template 部分的大致結(jié)構(gòu):

<template>
  <div class="wrap">
    <div class="menu-wrap">
      <div
        class="menu-item"
        v-for="menuItem in data"
      >{{menuItem.name}}</div>
    </div>
    <nest-menu
      :key="activeId"
      :data="subMenu"
      :depth="depth + 1"
    ></nest-menu>
  </div>
</template>

和我們預(yù)想設(shè)計中的一樣, menu-wrap 代表當(dāng)前菜單層, nest-menu 則就是組件本身,它負(fù)責(zé)遞歸的渲染子組件。

首次渲染

在第一次獲取到整個菜單的數(shù)據(jù)的時候,我們需要先把每層菜單的選中項默認(rèn)設(shè)置為第一個子菜單,由于它很可能是異步獲取的,所以我們最好是 watch 這個數(shù)據(jù)來做這個操作。

// 菜單數(shù)據(jù)源發(fā)生變化的時候 默認(rèn)選中當(dāng)前層級的第一項
const activeId = ref<number | null>(null)

watch(
  () => props.data,
  (newData) => {
    if (!activeId.value) {
      if (newData && newData.length) {
        activeId.value = newData[0].id
      }
    }
  },
  {
    immediate: true,
  }
)

現(xiàn)在我們從最上層開始講起,第一層的 activeId 被設(shè)置成了 生命科學(xué)競賽 的 id,注意我們傳遞給遞歸子組件的 data ,也就是 生命科學(xué)競賽 的 child,是通過 subMenu 獲取到的,它是一個計算屬性:

const getActiveSubMenu = () => {
  return data.find(({ id }) => id === activeId.value)._child
}
const subMenu = computed(getActiveSubMenu)

這樣,就拿到了 生命科學(xué)競賽 的 child,作為子組件的數(shù)據(jù)傳遞下去了。

點擊菜單項

回到之前的需求設(shè)計,在點擊了菜單項后,無論點擊的是哪層,都需要把完整的 id 鏈路通過 emit 傳遞到最外層去,所以這里我們需要多做一些處理:

/**
 * 遞歸收集子菜單第一項的 id
 */
const getSubIds = (child) => {
  const subIds = []
  const traverse = (data) => {
    if (data && data.length) {
      const first = data[0]
      subIds.push(first.id)
      traverse(first._child)
    }
  }
  traverse(child)
  return subIds
}

const onMenuItemClick = (menuItem) => {
  const newActiveId = menuItem.id
  if (newActiveId !== activeId.value) {
    activeId.value = newActiveId
    const child = getActiveSubMenu()
    const subIds = getSubIds(child)
    // 把子菜單的默認(rèn)第一項 ids 也拼接起來 向父組件 emit
    context.emit('change', [newActiveId, ...subIds])
  }
}

由于我們之前定的規(guī)則是,點擊了新的菜單以后默認(rèn)選中子菜單的第一項,所以這里我們也遞歸去找子菜單數(shù)據(jù)里的第一項,放到 subIds 中,直到最底層。

注意這里的 context.emit("change", [newId, ...subIds]);,這里是把事件向上 emit,如果這個菜單是中間層級的菜單,那么它的父組件也是 NestMenu,我們需要在父層級遞歸調(diào)用 NestMenu 組件的時候監(jiān)聽這個 change 事件。

<nest-menu
    :key="activeId"
    v-if="activeId !== null"
    :data="getActiveSubMenu()"
    :depth="depth + 1"
    @change="onSubActiveIdChange"
></nest-menu>

在父層級的菜單接受到了子層級的菜單的 change 事件后,需要怎么做呢?沒錯,需要進(jìn)一步的再向上傳遞:

const onSubActiveIdChange = (ids) => {
  context.emit('change', [activeId.value].concat(ids))
}

這里就只需要簡單的把自己當(dāng)前的 activeId 拼接到數(shù)組的最前面,再繼續(xù)向上傳遞即可。

這樣,任意一層的組件點擊了菜單后,都會先用自己的 activeId 拼接好所有子層級的默認(rèn) activeId,再一層層向上 emit。并且向上的每一層父菜單都會把自己的 activeId 拼在前面,就像接力一樣。

最后,我們在應(yīng)用層級的組件里,就可以輕松的拿到完整的 id 鏈路:

<template>
  <nest-menu :data="menu" @change="activeIdsChange" />
</template>

export default {
  methods: {
    activeIdsChange(ids) {
      this.ids = ids;
      console.log("當(dāng)前選中的id路徑", ids);
  },
},

樣式區(qū)分

由于我們每次調(diào)用遞歸組件的時候,都會把 depth + 1,那么就可以通過把這個數(shù)字拼接到類名后面來實現(xiàn)樣式區(qū)分了。

<template>
  <div class="wrap">
    <div class="menu-wrap" :class="`menu-wrap-${depth}`">
      <div class="menu-item">{{menuItem.name}}</div>
    </div>
    <nest-menu />
  </div>
</template>

<style>
.menu-wrap-0 {
  background: #ffccc7;
}

.menu-wrap-1 {
  background: #fff7e6;
}

.menu-wrap-2 {
  background: #fcffe6;
}
</style>

默認(rèn)高亮

上面的代碼寫完后,應(yīng)對沒有默認(rèn)值時的需求已經(jīng)足夠了,這時候面試官說,產(chǎn)品要求這個組件能通過傳入任意一個層級的 id 來默認(rèn)展示高亮。

其實這也難不倒我們,稍微改造一下代碼,在父組件里假設(shè)我們通過 url 參數(shù)或者任意方式拿到了一個 activeId,先通過深度優(yōu)先遍歷的方式查找到這個 id 的所有父級。

const activeId = 7

const findPath = (menus, targetId) => {
  let ids

  const traverse = (subMenus, prev) => {
    if (ids) {
      return
    }
    if (!subMenus) {
      return
    }
    subMenus.forEach((subMenu) => {
      if (subMenu.id === activeId) {
        ids = [...prev, activeId]
        return
      }
      traverse(subMenu._child, [...prev, subMenu.id])
    })
  }

  traverse(menus, [])

  return ids
}

const ids = findPath(data, activeId)

這里我選擇在遞歸的時候帶上上一層的 id,在找到了目標(biāo) id 以后就能輕松的拼接處完整的父子 id 數(shù)組。

然后我們把構(gòu)造好的 ids 作為 activeIds 傳遞給 NestMenu,此時這時候 NestMenu 就要改變一下設(shè)計,成為一個「受控組件」,它的渲染狀態(tài)是受我們外層傳遞的數(shù)據(jù)控制的。

所以我們需要在初始化參數(shù)的時候改變一下取值邏輯,優(yōu)先取 activeIds[depth] ,并且在點擊菜單項的時候,要在最外層的頁面組件中,接收到 change 事件時,把 activeIds 的數(shù)據(jù)同步改變。這樣繼續(xù)傳遞下去才不會導(dǎo)致 NestMenu 接收到的數(shù)據(jù)混亂。

<template>
  <nest-menu :data="data" :defaultActiveIds="ids" @change="activeIdsChange" />
</template>

NestMenu 初始化的時候,對有默認(rèn)值的情況做一下處理,優(yōu)先使用數(shù)組中取到的 id 值。

setup(props: IProps, context) {
  const { depth = 0, activeIds } = props;

  /**
   * 這里 activeIds 也可能是異步獲取到的 所以用 watch 保證初始化
   */
  const activeId = ref<number | null | undefined>(null);
  watch(
    () => activeIds,
    (newActiveIds) => {
      if (newActiveIds) {
        const newActiveId = newActiveIds[depth];
        if (newActiveId) {
          activeId.value = newActiveId;
        }
      }
    },
    {
      immediate: true,
    }
  );
}

這樣,如果 activeIds 數(shù)組中取不到的話,默認(rèn)還是 null,在 watch 到菜單數(shù)據(jù)變化的邏輯中,如果 activeId 是 null 的話,會被初始化為第一個子菜單的 id。

watch(
  () => props.data,
  (newData) => {
    if (!activeId.value) {
      if (newData && newData.length) {
        activeId.value = newData[0].id
      }
    }
  },
  {
    immediate: true,
  }
)

在最外層頁面容器監(jiān)聽到 change 事件的時候,要把數(shù)據(jù)源同步一下:

<template>
  <nest-menu :data="data" :activeIds="ids" @change="activeIdsChange" />
</template>

<script>
import { ref } from "vue";

export default {
  name: "App",
  setup() {
    const activeIdsChange = (newIds) => {
      ids.value = newIds;
    };

    return {
      ids,
      activeIdsChange,
    };
  },
};
</script>

如此一來,外部傳入 activeIds 的時候,就可以控制整個 NestMenu 的高亮選中邏輯了。

數(shù)據(jù)源變動引發(fā)的 bug

這時候,面試官對著你的 App 文件稍作改動,然后演示了這樣一個 bug:

App.vue 的 setup 函數(shù)中加了這樣的一段邏輯:

onMounted(() => {
  setTimeout(() => {
    menu.value = [data[0]].slice()
  }, 1000)
})

也就是說,組件渲染完成后過了一秒,菜單的最外層只剩下一項了,這時候面試官在一秒之內(nèi)點擊了最外層的第二項,這個組件在數(shù)據(jù)源改變之后,會報錯:

這是因為數(shù)據(jù)源已經(jīng)改變了,但是組件內(nèi)部的 activeId 狀態(tài)依然停留在了一個已經(jīng)不存在了的 id 上。

這會導(dǎo)致 subMenu 這個 computed 屬性在計算時出錯。

我們對 watch data 觀測數(shù)據(jù)源的這段邏輯稍加改動:

watch(
  () => props.data,
  (newData) => {
    if (!activeId.value) {
      if (newData && newData.length) {
        activeId.value = newData[0].id
      }
    }
    // 如果當(dāng)前層級的 data 中遍歷無法找到 `activeId` 的值 說明這個值失效了
    // 把它調(diào)整成數(shù)據(jù)源中第一個子菜單項的 id
    if (!props.data.find(({ id }) => id === activeId.value)) {
      activeId.value = props.data?.[0].id
    }
  },
  {
    immediate: true,
    // 在觀測到數(shù)據(jù)變動之后 同步執(zhí)行 這樣會防止渲染發(fā)生錯亂
    flush: 'sync',
  }
)

注意這里的 flush: "sync" 很關(guān)鍵,Vue3 對于 watch 到數(shù)據(jù)源變動之后觸發(fā) callback 這一行為,默認(rèn)是以 post 也就是渲染之后再執(zhí)行的,但是在當(dāng)前的需求下,如果我們用錯誤的 activeId 去渲染,就會直接導(dǎo)致報錯了,所以我們需要手動把這個 watch 變成一個同步行為。

這下再也不用擔(dān)心數(shù)據(jù)源變動導(dǎo)致渲染錯亂了。

完整代碼

App.vue

<template>
  <nest-menu :data="data" :activeIds="ids" @change="activeIdsChange" />
</template>

<script>
import { ref } from "vue";
import NestMenu from "./components/NestMenu.vue";
import data from "./menu.js";
import { getSubIds } from "./util";

export default {
  name: "App",
  setup() {
    // 假設(shè)默認(rèn)選中 id 為 7
    const activeId = 7;

    const findPath = (menus, targetId) => {
      let ids;

      const traverse = (subMenus, prev) => {
        if (ids) {
          return;
        }
        if (!subMenus) {
          return;
        }
        subMenus.forEach((subMenu) => {
          if (subMenu.id === activeId) {
            ids = [...prev, activeId];
            return;
          }
          traverse(subMenu._child, [...prev, subMenu.id]);
        });
      };

      traverse(menus, []);

      return ids;
    };

    const ids = ref(findPath(data, activeId));

    const activeIdsChange = (newIds) => {
      ids.value = newIds;
      console.log("當(dāng)前選中的id路徑", newIds);
    };

    return {
      ids,
      activeIdsChange,
      data,
    };
  },
  components: {
    NestMenu,
  },
};
</script>

NestMenu.vue

<template>
  <div class="wrap">
    <div class="menu-wrap" :class="`menu-wrap-${depth}`">
      <div
        class="menu-item"
        v-for="menuItem in data"
        :class="getActiveClass(menuItem.id)"
        @click="onMenuItemClick(menuItem)"
        :key="menuItem.id"
      >{{menuItem.name}}</div>
    </div>
    <nest-menu
      :key="activeId"
      v-if="subMenu && subMenu.length"
      :data="subMenu"
      :depth="depth + 1"
      :activeIds="activeIds"
      @change="onSubActiveIdChange"
    ></nest-menu>
  </div>
</template>

<script lang="ts">
import { watch, ref, onMounted, computed } from "vue";
import data from "../menu";

interface IProps {
  data: typeof data;
  depth: number;
  activeIds?: number[];
}

export default {
  name: "NestMenu",
  props: ["data", "depth", "activeIds"],
  setup(props: IProps, context) {
    const { depth = 0, activeIds, data } = props;

    /**
     * 這里 activeIds 也可能是異步獲取到的 所以用 watch 保證初始化
     */
    const activeId = ref<number | null | undefined>(null);
    watch(
      () => activeIds,
      (newActiveIds) => {
        if (newActiveIds) {
          const newActiveId = newActiveIds[depth];
          if (newActiveId) {
            activeId.value = newActiveId;
          }
        }
      },
      {
        immediate: true,
        flush: 'sync'
      }
    );

    /**
     * 菜單數(shù)據(jù)源發(fā)生變化的時候 默認(rèn)選中當(dāng)前層級的第一項
     */
    watch(
      () => props.data,
      (newData) => {
        if (!activeId.value) {
          if (newData && newData.length) {
            activeId.value = newData[0].id;
          }
        }
        // 如果當(dāng)前層級的 data 中遍歷無法找到 `activeId` 的值 說明這個值失效了
        // 把它調(diào)整成數(shù)據(jù)源中第一個子菜單項的 id
        if (!props.data.find(({ id }) => id === activeId.value)) {
          activeId.value = props.data?.[0].id;
        }
      },
      {
        immediate: true,
        // 在觀測到數(shù)據(jù)變動之后 同步執(zhí)行 這樣會防止渲染發(fā)生錯亂
        flush: "sync",
      }
    );

    const onMenuItemClick = (menuItem) => {
      const newActiveId = menuItem.id;
      if (newActiveId !== activeId.value) {
        activeId.value = newActiveId;
        const child = getActiveSubMenu();
        const subIds = getSubIds(child);
        // 把子菜單的默認(rèn)第一項 ids 也拼接起來 向父組件 emit
        context.emit("change", [newActiveId, ...subIds]);
      }
    };
    /**
     * 接受到子組件更新 activeId 的同時
     * 需要作為一個中介告知父組件 activeId 更新了
     */
    const onSubActiveIdChange = (ids) => {
      context.emit("change", [activeId.value].concat(ids));
    };
    const getActiveSubMenu = () => {
      return props.data?.find(({ id }) => id === activeId.value)._child;
    };
    const subMenu = computed(getActiveSubMenu);

    /**
     * 樣式相關(guān)
     */
    const getActiveClass = (id) => {
      if (id === activeId.value) {
        return "menu-active";
      }
      return "";
    };

    /**
     * 遞歸收集子菜單第一項的 id
     */
    const getSubIds = (child) => {
      const subIds = [];
      const traverse = (data) => {
        if (data && data.length) {
          const first = data[0];
          subIds.push(first.id);
          traverse(first._child);
        }
      };
      traverse(child);
      return subIds;
    };

    return {
      depth,
      activeId,
      subMenu,
      onMenuItemClick,
      onSubActiveIdChange,
      getActiveClass,
    };
  },
};
</script>

<style>
.wrap {
  padding: 12px 0;
}

.menu-wrap {
  display: flex;
  flex-wrap: wrap;
}

.menu-wrap-0 {
  background: #ffccc7;
}

.menu-wrap-1 {
  background: #fff7e6;
}

.menu-wrap-2 {
  background: #fcffe6;
}

.menu-item {
  margin-left: 16px;
  cursor: pointer;
  white-space: nowrap;
}

.menu-active {
  color: #f5222d;
}
</style>

源碼地址

github.com/sl1673495/v

總結(jié)

一個遞歸的菜單組件,說簡單也簡單,說難也有它的難點。如果我們不理解 Vue 的異步渲染和觀察策略,可能中間的 bug 就會困擾我們許久。所以適當(dāng)學(xué)習(xí)原理還是挺有必要的。

在開發(fā)通用組件的時候,一定要注意數(shù)據(jù)源的傳入時機(同步、異步),對于異步傳入的數(shù)據(jù),要利用好 watch 這個 API 去觀測變動,做相應(yīng)的操作。并且要考慮數(shù)據(jù)源的變化是否會和組件內(nèi)原來保存的狀態(tài)沖突,在適當(dāng)?shù)臅r機要做好清理操作。

另外留下一個小問題,我在 NestMenu 組件 watch 數(shù)據(jù)源的時候,選擇這樣去做:

watch((() => props.data);

而不是解構(gòu)后再去觀測:

const { data } = props;
watch(() => data);

這兩者之間有區(qū)別嗎?這又是一道考察深度的面試題。

到此這篇關(guān)于Vue3+TypeScript實現(xiàn)遞歸菜單組件的文章就介紹到這了,更多相關(guān)Vue3+TypeScript遞歸菜單組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue3 響應(yīng)式對象如何實現(xiàn)方法的不同點

    vue3 響應(yīng)式對象如何實現(xiàn)方法的不同點

    這篇文章主要介紹了vue3 響應(yīng)式對象如何實現(xiàn)方法的不同點,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 基于Vue實現(xiàn)的多條件篩選功能的詳解(類似京東和淘寶功能)

    基于Vue實現(xiàn)的多條件篩選功能的詳解(類似京東和淘寶功能)

    這篇文章主要介紹了Vue多條件篩選功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • vue+axios+java實現(xiàn)文件上傳功能

    vue+axios+java實現(xiàn)文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了vue+axios+java實現(xiàn)文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Vue使用Echarts實現(xiàn)大屏可視化布局示例詳細(xì)講解

    Vue使用Echarts實現(xiàn)大屏可視化布局示例詳細(xì)講解

    這篇文章主要介紹了Vue使用Echarts實現(xiàn)大屏可視化布局示例,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-01-01
  • 使用Vue-Router 2實現(xiàn)路由功能實例詳解

    使用Vue-Router 2實現(xiàn)路由功能實例詳解

    vue-router 2只適用于Vue2.x版本,下面我們是基于vue2.0講的如何使用vue-router 2實現(xiàn)路由功能,需要的朋友可以參考下
    2017-11-11
  • vue關(guān)于data變量定義的問題

    vue關(guān)于data變量定義的問題

    這篇文章主要介紹了vue關(guān)于data變量定義的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Vue 中指令v-bind動態(tài)綁定及與v-for結(jié)合使用詳解

    Vue 中指令v-bind動態(tài)綁定及與v-for結(jié)合使用詳解

    這篇文章主要為大家介紹了Vue 中指令v-bind動態(tài)綁定及與v-for結(jié)合使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • vue element實現(xiàn)表格增加刪除修改數(shù)據(jù)

    vue element實現(xiàn)表格增加刪除修改數(shù)據(jù)

    這篇文章主要為大家詳細(xì)介紹了vue element實現(xiàn)表格增加刪除修改數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • Vue SPA首屏加載緩慢問題解決方案

    Vue SPA首屏加載緩慢問題解決方案

    這篇文章主要介紹了Vue SPA首屏加載緩慢問題解決方案,首屏?xí)r間(First Contentful Paint),指的是瀏覽器從響應(yīng)用戶輸入網(wǎng)址地址,到首屏內(nèi)容渲染完成的時間,此時整個網(wǎng)頁不一定要全部渲染完成,但需要展示當(dāng)前視窗需要的內(nèi)容
    2023-03-03
  • vue表單驗證之禁止input輸入框輸入空格

    vue表單驗證之禁止input輸入框輸入空格

    這篇文章主要介紹了vue表單驗證之禁止input輸入框輸入空格,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12

最新評論