vue3快速上手指南

来源:https://www.cnblogs.com/heymar/p/18303885
-Advertisement-
Play Games

1. Vue3簡介 2020年9月18日,Vue.js發佈版3.0版本,代號:One Piece(n 經歷了:4800+次提交、40+個RFC、600+次PR、300+貢獻者 官方發版地址:Release v3.0.0 One Piece · vuejs/core 截止2023年10月,最新的公開版 ...


1. Vue3簡介

1.1. 【性能的提升】

  • 打包大小減少41%

  • 初次渲染快55%, 更新渲染快133%

  • 記憶體減少54%

1.2.【 源碼的升級】

  • 使用Proxy代替defineProperty實現響應式。

  • 重寫虛擬DOM的實現和Tree-Shaking

1.3. 【擁抱TypeScript】

  • Vue3可以更好的支持TypeScript

1.4. 【新的特性】

  1. Composition API(組合API):

    • setup

    • refreactive

    • computedwatch

      ......

  2. 新的內置組件:

    • Fragment

    • Teleport

    • Suspense

      ......

  3. 其他改變:

    • 新的生命周期鉤子

    • data 選項應始終被聲明為一個函數

    • 移除keyCode支持作為 v-on 的修飾符

      ......

2. 創建Vue3工程

2.1. 【基於 vue-cli 創建】

點擊查看官方文檔

備註:目前vue-cli已處於維護模式,官方推薦基於 Vite 創建項目。

## 查看@vue/cli版本,確保@vue/cli版本在4.5.0以上
vue --version

## 安裝或者升級你的@vue/cli 
npm install -g @vue/cli

## 執行創建命令
vue create vue_test

##  隨後選擇3.x
##  Choose a version of Vue.js that you want to start the project with (Use arrow keys)
##  > 3.x
##    2.x

## 啟動
cd vue_test
npm run serve

2.2. 【基於 vite 創建】(推薦)

vite 是新一代前端構建工具,官網地址:https://vitejs.cnvite的優勢如下:

  • 輕量快速的熱重載(HMR),能實現極速的服務啟動。
  • TypeScriptJSXCSS 等支持開箱即用。
  • 真正的按需編譯,不再等待整個應用編譯完成。
  • webpack構建 與 vite構建對比圖如下:
    webpack構建 vite構建
## 1.創建命令
npm create vue@latest

## 2.具體配置
## 配置項目名稱
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript?  Yes
## 是否添加JSX支持
√ Add JSX Support?  No
## 是否添加路由環境
√ Add Vue Router for Single Page Application development?  No
## 是否添加pinia環境
√ Add Pinia for state management?  No
## 是否添加單元測試
√ Add Vitest for Unit Testing?  No
## 是否添加端到端測試方案
√ Add an End-to-End Testing Solution? » No
## 是否添加ESLint語法檢查
√ Add ESLint for code quality?  Yes
## 是否添加Prettiert代碼格式化
√ Add Prettier for code formatting?  No

自己動手編寫一個App組件

<template>
  <div class="app">
    <h1>你好啊!</h1>
  </div>
</template>

<script lang="ts">
  export default {
    name:'App' //組件名
  }
</script>

<style>
  .app {
    background-color: #ddd;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
</style>

安裝官方推薦的vscode插件:

image-20240715195013198

image-20240715195017872

總結:

  • Vite 項目中,index.html 是項目的入口文件,在項目最外層。
  • 載入index.html後,Vite 解析 <script type="module" src="xxx"> 指向的JavaScript
  • Vue3**中是通過 **createApp 函數創建一個應用實例。

2.3. 【一個簡單的效果】

Vue3向下相容Vue2語法,且Vue3中的模板中可以沒有根標簽

<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年齡:{{age}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">年齡+1</button>
    <button @click="showTel">點我查看聯繫方式</button>
  </div>
</template>

<script lang="ts">
  export default {
    name:'App',
    data() {
      return {
        name:'張三',
        age:18,
        tel:'13888888888'
      }
    },
    methods:{
      changeName(){
        this.name = 'zhang-san'
      },
      changeAge(){
        this.age += 1
      },
      showTel(){
        alert(this.tel)
      }
    },
  }
</script>

3. Vue3核心語法

3.1. 【OptionsAPI 與 CompositionAPI】

  • Vue2API設計是Options(配置)風格的。
  • Vue3API設計是Composition(組合)風格的。

Options API 的弊端

Options類型的 API,數據、方法、計算屬性等,是分散在:datamethodscomputed中的,若想新增或者修改一個需求,就需要分別修改:datamethodscomputed,不便於維護和復用。

2.gif1696662200734-1bad8249-d7a2-423e-a3c3-ab4c110628be

Composition API 的優勢

可以用函數的方式,更加優雅的組織代碼,讓相關功能的代碼更加有序的組織在一起。

1696662249851-db6403a1-acb5-481a-88e0-e1e34d2ef53a1696662256560-7239b9f9-a770-43c1-9386-6cc12ef1e9c0

說明:以上四張動圖原創作者:大帥老猿

3.2. 【拉開序幕的 setup】

setup 概述

setupVue3中一個新的配置項,值是一個函數,它是 Composition API “表演的舞臺,組件中所用到的:數據、方法、計算屬性、監視......等等,均配置在setup中。

特點如下:

  • setup函數返回的對象中的內容,可直接在模板中使用。
  • setup中訪問thisundefined
  • setup函數會在beforeCreate之前調用,它是“領先”所有鉤子執行的。
<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年齡:{{age}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">年齡+1</button>
    <button @click="showTel">點我查看聯繫方式</button>
  </div>
</template>

<script lang="ts">
  export default {
    name:'Person',
    setup(){
      // 數據,原來寫在data中(註意:此時的name、age、tel數據都不是響應式數據)
      let name = '張三'
      let age = 18
      let tel = '13888888888'

      // 方法,原來寫在methods中
      function changeName(){
        name = 'zhang-san' //註意:此時這麼修改name頁面是不變化的
        console.log(name)
      }
      function changeAge(){
        age += 1 //註意:此時這麼修改age頁面是不變化的
        console.log(age)
      }
      function showTel(){
        alert(tel)
      }

      // 返回一個對象,對象中的內容,模板中可以直接使用
      return {name,age,tel,changeName,changeAge,showTel}
    }
  }
</script>

setup 的返回值

  • 若返回一個對象:則對象中的:屬性、方法等,在模板中均可以直接使用(重點關註)。
  • 若返回一個函數:則可以自定義渲染內容,代碼如下:
setup(){
  return ()=> '你好啊!'
}

setup 與 Options API 的關係

  • Vue2 的配置(datamethos......)中可以訪問到 setup中的屬性、方法。
  • 但在setup不能訪問到Vue2的配置(datamethos......)。
  • 如果與Vue2衝突,則setup優先。

setup 語法糖

setup函數有一個語法糖,這個語法糖,可以讓我們把setup獨立出去,代碼如下:

<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年齡:{{age}}</h2>
    <button @click="changName">修改名字</button>
    <button @click="changAge">年齡+1</button>
    <button @click="showTel">點我查看聯繫方式</button>
  </div>
</template>

<script lang="ts">
  export default {
    name:'Person',
  }
</script>

<!-- 下麵的寫法是setup語法糖 -->
<script setup lang="ts">
  console.log(this) //undefined
  
  // 數據(註意:此時的name、age、tel都不是響應式數據)
  let name = '張三'
  let age = 18
  let tel = '13888888888'

  // 方法
  function changName(){
    name = '李四'//註意:此時這麼修改name頁面是不變化的
  }
  function changAge(){
    console.log(age)
    age += 1 //註意:此時這麼修改age頁面是不變化的
  }
  function showTel(){
    alert(tel)
  }
</script>

這樣寫了之後,之前的script一般就拿來寫個name就可以了,但是只是寫個name專門用個script標簽麻煩,所以可以藉助下麵的方法

直接在steup的script標簽寫上

image-20240618143203354

擴展:上述代碼,還需要編寫一個不寫setupscript標簽,去指定組件名字,比較麻煩,我們可以藉助vite中的插件簡化

  1. 第一步:npm i vite-plugin-vue-setup-extend -D
  2. 第二步:vite.config.ts
import { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'

export default defineConfig({
  plugins: [ VueSetupExtend() ]
})
  1. 第三步:<script setup lang="ts" name="Person">

3.3. 【ref 創建:基本類型的響應式數據】

  • 作用:定義響應式變數。
  • 語法:let xxx = ref(初始值)
  • 返回值:一個RefImpl的實例對象,簡稱ref對象refref對象的value屬性是響應式的

加了ref後就是ref的一個實例對象,然後值是在裡面的value屬性裡面

image-20240618145341550

image-20240618145348312

  • 註意點:

    • JS中操作數據需要:xxx.value,但模板中不需要.value,直接使用即可。(本來是要name.value,模板裡面自己給你添加上了value就不用添加
    • 對於let name = ref('張三')來說,name不是響應式的,name.value是響應式的。但是要修改他的話,在修改(函數)裡面是需要寫明value的才能響應式

    image-20240618145554920

<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年齡:{{age}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">年齡+1</button>
    <button @click="showTel">點我查看聯繫方式</button>
  </div>
</template>

<script setup lang="ts" name="Person">
  import {ref} from 'vue'
  // name和age是一個RefImpl的實例對象,簡稱ref對象,它們的value屬性是響應式的。
  let name = ref('張三')
  let age = ref(18)
  // tel就是一個普通的字元串,不是響應式的
  let tel = '13888888888'

  function changeName(){
    // JS中操作ref對象時候需要.value
    name.value = '李四'
    console.log(name.value)

    // 註意:name不是響應式的,name.value是響應式的,所以如下代碼並不會引起頁面的更新。
    // name = ref('zhang-san')
  }
  function changeAge(){
    // JS中操作ref對象時候需要.value
    age.value += 1 
    console.log(age.value)
  }
  function showTel(){
    alert(tel)
  }
</script>

3.4. 【reactive 創建:對象類型的響應式數據】

  • 作用:定義一個響應式對象(基本類型不要用它,要用ref,否則報錯)
  • 語法:let 響應式對象= reactive(源對象)
  • 返回值:一個Proxy的實例對象,簡稱:響應式對象。

這個是將一個對象編程一個proxy創建的對象接受響應式(數組同理也是這個方法)

image-20240618150214652

image-20240618150219506

  • 註意點:reactive定義的響應式數據是“深層次”的。
<template>
  <div class="person">
    <h2>汽車信息:一臺{{ car.brand }}汽車,價值{{ car.price }}萬</h2>
    <h2>游戲列表:</h2>
    <ul>
      <li v-for="g in games" :key="g.id">{{ g.name }}</li>
    </ul>
    <h2>測試:{{obj.a.b.c.d}}</h2>
    <button @click="changeCarPrice">修改汽車價格</button>
    <button @click="changeFirstGame">修改第一游戲</button>
    <button @click="test">測試</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive } from 'vue'

// 數據
let car = reactive({ brand: '賓士', price: 100 })
let games = reactive([ 
  { id: 'ahsgdyfa01', name: '英雄聯盟' },
  { id: 'ahsgdyfa02', name: '王者榮耀' },
  { id: 'ahsgdyfa03', name: '原神' }
])
let obj = reactive({
  a:{
    b:{
      c:{
        d:666
      }
    }
  }
})

function changeCarPrice() {
  car.price += 10
}
function changeFirstGame() {
  games[0].name = '流星蝴蝶劍'
}
function test(){
  obj.a.b.c.d = 999
}
</script>

3.5. 【ref 創建:對象類型的響應式數據】

  • 其實ref接收的數據可以是:基本類型對象類型ref可以對對象響應式數據,但是註意改的時候要加value,特別是數組形式加在哪裡

其原理還是把變數做了一個響應式變成了value形式,但是裡面的對象還是做了一個proxy

image-20240618151009851

  • ref接收的是對象類型,內部其實也是調用了reactive函數。
<template>
  <div class="person">
    <h2>汽車信息:一臺{{ car.brand }}汽車,價值{{ car.price }}萬</h2>
    <h2>游戲列表:</h2>
    <ul>
      <li v-for="g in games" :key="g.id">{{ g.name }}</li>
    </ul>
    <h2>測試:{{obj.a.b.c.d}}</h2>
    <button @click="changeCarPrice">修改汽車價格</button>
    <button @click="changeFirstGame">修改第一游戲</button>
    <button @click="test">測試</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref } from 'vue'

// 數據
let car = ref({ brand: '賓士', price: 100 })
let games = ref([
  { id: 'ahsgdyfa01', name: '英雄聯盟' },
  { id: 'ahsgdyfa02', name: '王者榮耀' },
  { id: 'ahsgdyfa03', name: '原神' }
])
let obj = ref({
  a:{
    b:{
      c:{
        d:666
      }
    }
  }
})

console.log(car)

function changeCarPrice() {
  car.value.price += 10
}
function changeFirstGame() {
  games.value[0].name = '流星蝴蝶劍'
}
function test(){
  obj.value.a.b.c.d = 999
}
</script>

3.6. 【ref 對比 reactive】

巨集觀角度看:

  1. ref用來定義:基本類型數據對象類型數據

  2. reactive用來定義:對象類型數據

  • 區別:
  1. ref創建的變數必須使用.value(可以使用volar插件自動添加.value)。

    image-20240715195302384

    image-20240618151309065

  2. reactive重新分配一個新對象,會失去響應式(可以使用Object.assign去整體替換)。

image-20240618151802755

這樣寫也不行

image-20240618151918799

可以這樣

image-20240618152106715

如果是用的ref響應對象,就可以直接改

image-20240618152223647

  • 使用原則:
  1. 若需要一個基本類型的響應式數據,必須使用ref
  2. 若需要一個響應式對象,層級不深,refreactive都可以。
  3. 若需要一個響應式對象,且層級較深,推薦使用reactive

3.7. 【toRefs 與 toRef】

如果像這樣解構賦值,那麼出來的name age都不是響應式數據

image-20240618153249185

想讓響應式數據轉換出來也是響應式就押用到這個

  • 作用:將一個響應式對象中的每一個屬性,轉換為ref對象。

註意:torefs之後會創建一個新的reactive響應式對象,但是值都是指向同一個值你修改一個兩個都會變

image-20240618153728117

image-20240618153745849

image-20240618153736075

  • 備註:toRefstoRef功能一致,但toRefs可以批量轉換。
  • 語法如下:
<template>
  <div class="person">
    <h2>姓名:{{person.name}}</h2>
    <h2>年齡:{{person.age}}</h2>
    <h2>性別:{{person.gender}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年齡</button>
    <button @click="changeGender">修改性別</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,reactive,toRefs,toRef} from 'vue'

  // 數據
  let person = reactive({name:'張三', age:18, gender:'男'})
	
  // 通過toRefs將person對象中的n個屬性批量取出,且依然保持響應式的能力
  let {name,gender} =  toRefs(person)
	
  // 通過toRef將person對象中的gender屬性取出,且依然保持響應式的能力
  let age = toRef(person,'age')

  // 方法
  function changeName(){
    name.value += '~'
  }
  function changeAge(){
    age.value += 1
  }
  function changeGender(){
    gender.value = '女'
  }
</script>

3.8. 【computed】

作用:根據已有數據計算出新數據(和Vue2中的computed作用一致)。

computed

計算屬性本身是監視它裡面的值的變化從而引起自身的變化,所以要改他的時候,也不是直接改他本身,而是改他裡面的值

image-20240618161651520

image-20240618161733797

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName}}</span> <br>
    <button @click="changeFullName">全名改為:li-si</button>
  </div>
</template>

<script setup lang="ts" name="App">
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  // 計算屬性——只讀取,不修改
  /* let fullName = computed(()=>{
    return firstName.value + '-' + lastName.value
  }) */


  // 計算屬性——既讀取又修改
  let fullName = computed({
    // 讀取
    get(){
      return firstName.value + '-' + lastName.value
    },
    // 修改
    set(val){
      console.log('有人修改了fullName',val)
      firstName.value = val.split('-')[0]
      lastName.value = val.split('-')[1]
    }
  })

  function changeFullName(){
    fullName.value = 'li-si'
  } 
</script>

3.9.【watch】

  • 作用:監視數據的變化(和Vue2中的watch作用一致)
  • 特點:Vue3中的watch只能監視以下四種數據
  1. ref定義的數據。
  2. reactive定義的數據。
  3. 函數返回一個值(getter函數)。
  4. 一個包含上述內容的數組。

我們在Vue3中使用watch的時候,通常會遇到以下幾種情況:

* 情況一

監視ref定義的【基本類型】數據:直接寫數據名即可,監視的是其value值的改變。

stopwatch監視會返回一個值是一個箭頭函數,調用這個函數可以取消掉監視

<template>
  <div class="person">
    <h1>情況一:監視【ref】定義的【基本類型】數據</h1>
    <h2>當前求和為:{{sum}}</h2>
    <button @click="changeSum">點我sum+1</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,watch} from 'vue'
  // 數據
  let sum = ref(0)
  // 方法
  function changeSum(){
    sum.value += 1
  }
  // 監視,情況一:監視【ref】定義的【基本類型】數據
  const stopWatch = watch(sum,(newValue,oldValue)=>{
    console.log('sum變化了',newValue,oldValue)
    if(newValue >= 10){
      stopWatch()
    }
  })
</script>

* 情況二

監視ref定義的【對象類型】數據:直接寫數據名,監視的是對象的【地址值】,若想監視對象內部的數據,要手動開啟深度監視。

註意:

  • 若修改的是ref定義的對象中的屬性,newValueoldValue 都是新值,因為它們是同一個對象。

image-20240618163350407

  • 若修改整個ref定義的對象,newValue 是新值, oldValue 是舊值,因為不是同一個對象了。

不開啟深度,整個對象改變了,才會觸發監視

image-20240618162919153

<template>
  <div class="person">
    <h1>情況二:監視【ref】定義的【對象類型】數據</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年齡:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年齡</button>
    <button @click="changePerson">修改整個人</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,watch} from 'vue'
  // 數據
  let person = ref({
    name:'張三',
    age:18
  })
  // 方法
  function changeName(){
    person.value.name += '~'
  }
  function changeAge(){
    person.value.age += 1
  }
  function changePerson(){
    person.value = {name:'李四',age:90}
  }
  /* 
    監視,情況一:監視【ref】定義的【對象類型】數據,監視的是對象的地址值,若想監視對象內部屬性的變化,需要手動開啟深度監視
    watch的第一個參數是:被監視的數據
    watch的第二個參數是:監視的回調
    watch的第三個參數是:配置對象(deep、immediate等等.....) 
  */
  watch(person,(newValue,oldValue)=>{
    console.log('person變化了',newValue,oldValue)
  },{deep:true})
  
</script>

* 情況三

監視reactive定義的【對象類型】數據,且預設開啟了深度監視並且修改屬性新老value還是一樣的

<template>
  <div class="person">
    <h1>情況三:監視【reactive】定義的【對象類型】數據</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年齡:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年齡</button>
    <button @click="changePerson">修改整個人</button>
    <hr>
    <h2>測試:{{obj.a.b.c}}</h2>
    <button @click="test">修改obj.a.b.c</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'
  // 數據
  let person = reactive({
    name:'張三',
    age:18
  })
  let obj = reactive({
    a:{
      b:{
        c:666
      }
    }
  })
  // 方法
  function changeName(){
    person.name += '~'
  }
  function changeAge(){
    person.age += 1
  }
  function changePerson(){
    Object.assign(person,{name:'李四',age:80})
  }
  function test(){
    obj.a.b.c = 888
  }

  // 監視,情況三:監視【reactive】定義的【對象類型】數據,且預設是開啟深度監視的
  watch(person,(newValue,oldValue)=>{
    console.log('person變化了',newValue,oldValue)
  })
  watch(obj,(newValue,oldValue)=>{
    console.log('Obj變化了',newValue,oldValue)
  })
</script>

* 情況四

監視refreactive定義的【對象類型】數據中的某個屬性,註意點如下:

  1. 若該屬性值不是【對象類型】,需要寫成函數形式。

    image-20240618170450244

  2. 若該屬性值是依然是【對象類型】,可直接寫,也可寫成函數,建議寫成函數。

但是此時如果是這整個對象改變不會監視到

image-20240618170735829

這樣就是這個對象怎麼變都監視得到,寫成函數是為了整個對象變,deep是對象裡面變化

image-20240618170904972

結論:監視的要是對象里的屬性,那麼最好寫函數式,註意點:若是對象監視的是地址值,需要關註對象內部,需要手動開啟深度監視。

<template>
  <div class="person">
    <h1>情況四:監視【ref】或【reactive】定義的【對象類型】數據中的某個屬性</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年齡:{{ person.age }}</h2>
    <h2>汽車:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年齡</button>
    <button @click="changeC1">修改第一臺車</button>
    <button @click="changeC2">修改第二臺車</button>
    <button @click="changeCar">修改整個車</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'

  // 數據
  let person = reactive({
    name:'張三',
    age:18,
    car:{
      c1:'賓士',
      c2:'寶馬'
    }
  })
  // 方法
  function changeName(){
    person.name += '~'
  }
  function changeAge(){
    person.age += 1
  }
  function changeC1(){
    person.car.c1 = '奧迪'
  }
  function changeC2(){
    person.car.c2 = '大眾'
  }
  function changeCar(){
    person.car = {c1:'雅迪',c2:'愛瑪'}
  }

  // 監視,情況四:監視響應式對象中的某個屬性,且該屬性是基本類型的,要寫成函數式
  /* watch(()=> person.name,(newValue,oldValue)=>{
    console.log('person.name變化了',newValue,oldValue)
  }) */

  // 監視,情況四:監視響應式對象中的某個屬性,且該屬性是對象類型的,可以直接寫,也能寫函數,更推薦寫函數
  watch(()=>person.car,(newValue,oldValue)=>{
    console.log('person.car變化了',newValue,oldValue)
  },{deep:true})
</script>

* 情況五

監視上述的多個數據

寫成數組形式就可以,value是這個數組

image-20240618173108012

<template>
  <div class="person">
    <h1>情況五:監視上述的多個數據</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年齡:{{ person.age }}</h2>
    <h2>汽車:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年齡</button>
    <button @click="changeC1">修改第一臺車</button>
    <button @click="changeC2">修改第二臺車</button>
    <button @click="changeCar">修改整個車</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'

  // 數據
  let person = reactive({
    name:'張三',
    age:18,
    car:{
      c1:'賓士',
      c2:'寶馬'
    }
  })
  // 方法
  function changeName(){
    person.name += '~'
  }
  function changeAge(){
    person.age += 1
  }
  function changeC1(){
    person.car.c1 = '奧迪'
  }
  function changeC2(){
    person.car.c2 = '大眾'
  }
  function changeCar(){
    person.car = {c1:'雅迪',c2:'愛瑪'}
  }

  // 監視,情況五:監視上述的多個數據
  watch([()=>person.name,person.car],(newValue,oldValue)=>{
    console.log('person.car變化了',newValue,oldValue)
  },{deep:true})

</script>

3.10. 【watchEffect】

  • 官網:立即運行一個函數,同時響應式地追蹤其依賴,併在依賴更改時重新執行該函數。

  • watch對比watchEffect

    1. 都能監聽響應式數據的變化,不同的是監聽數據變化的方式不同

    2. watch:要明確指出監視的數據

    3. watchEffect:不用明確指出監視的數據(函數中用到哪些屬性,那就監視哪些屬性)。

比如這個例子,都可以完成但是watcheffect上來就執行一次,而且會根據裡面所寫的自動監測要監視誰

image-20240618174038113

  • 示例代碼:

    <template>
      <div class="person">
        <h1>需求:水溫達到50℃,或水位達到20cm,則聯繫伺服器</h1>
        <h2 id="demo">水溫:{{temp}}</h2>
        <h2>水位:{{height}}</h2>
        <button @click="changePrice">水溫+1</button>
        <button @click="changeSum">水位+10</button>
      </div>
    </template>
    
    <script lang="ts" setup name="Person">
      import {ref,watch,watchEffect} from 'vue'
      // 數據
      let temp = ref(0)
      let height = ref(0)
    
      // 方法
      function changePrice(){
        temp.value += 10
      }
      function changeSum(){
        height.value += 1
      }
    
      // 用watch實現,需要明確的指出要監視:temp、height
      watch([temp,height],(value)=>{
        // 從value中獲取最新的temp值、height值
        const [newTemp,newHeight] = value
        // 室溫達到50℃,或水位達到20cm,立刻聯繫伺服器
        if(newTemp >= 50 || newHeight >= 20){
          console.log('聯繫伺服器')
        }
      })
    
      // 用watchEffect實現,不用
      const stopWtach = watchEffect(()=>{
        // 室溫達到50℃,或水位達到20cm,立刻聯繫伺服器
        if(temp.value >= 50 || height.value >= 20){
          console.log(document.getElementById('demo')?.innerText)
          console.log('聯繫伺服器')
        }
        // 水溫達到100,或水位達到50,取消監視
        if(temp.value === 100 || height.value === 50){
          console.log('清理了')
          stopWtach()
        }
      })
    </script>
    

3.11. 【標簽的 ref 屬性】

作用:用於註冊模板引用。

  • 用在普通DOM標簽上,獲取的是DOM節點。

  • 用在組件標簽上,獲取的是組件實例對象。

用在普通DOM標簽上:

<template>
  <div class="person">
    <h1 ref="title1">尚矽谷</h1>
    <h2 ref="title2">前端</h2>
    <h3 ref="title3">Vue</h3>
    <input type="text" ref="inpt"> <br><br>
    <button @click="showLog">點我列印內容</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref} from 'vue'
	
  let title1 = ref()
  let title2 = ref()
  let title3 = ref()

  function showLog(){
    // 通過id獲取元素
    const t1 = document.getElementById('title1')
    // 列印內容
    console.log((t1 as HTMLElement).innerText)
    console.log((<HTMLElement>t1).innerText)
    console.log(t1?.innerText)
    
		/************************************/
		
    // 通過ref獲取元素
    console.log(title1.value)
    console.log(title2.value)
    console.log(title3.value)
  }
</script>

用在組件標簽上:

但是要獲取組件上的數據需要子組件做出defineexpose導出操作

<!-- 父組件App.vue -->
<template>
  <Person ref="ren"/>
  <button @click="test">測試</button>
</template>

<script lang="ts" setup name="App">
  import Person from './components/Person.vue'
  import {ref} from 'vue'

  let ren = ref()

  function test(){
    console.log(ren.value.name)
    console.log(ren.value.age)
  }
</script>


<!-- 子組件Person.vue中要使用defineExpose暴露內容 -->
<script lang="ts" setup name="Person">
  import {ref,defineExpose} from 'vue'
	// 數據
  let name = ref('張三')
  let age = ref(18)
  /****************************/
  /****************************/
  // 使用defineExpose將組件中的數據交給外部
  defineExpose({name,age})
</script>

3.11.1 回顧TS中的介面、泛型、自定義類型

註意vue3引用組件直接導入即可

image-20240619095613391

如果在子組件定義了一組數據,但是不小心名字打錯了

image-20240619102135028

後期就容易調用不到

我們就可以去定義一個介面來約束類型,一般是在src下麵創建一個types文件夾,然後導出

image-20240619102324144

要用的話就在組件導入,並且前面要加type表明導入的是一個類型,然後在要用的變數後規則如下

image-20240619102558991

image-20240619102610562

如果是一個數組數據想這樣用就不行

image-20240619105310248

除非是用泛型先規定它是一個數組,然後數組裡面每一項需要符合這個規則

image-20240619105401516

當然也可以去自定義類型,直接在ts裡面聲明

image-20240619105543151

image-20240619105616518

3.12. 【props】

// 定義一個介面,限制每個Person對象的格式
export interface PersonInter {
id:string,
name:string,
age:number
}

// 定義一個自定義類型Persons
export type Persons = Array<PersonInter>

App.vue中代碼:

註意用reactive響應式數據,此時介面應該用泛型來約束,而且數據是不能多不能少的

image-20240619110844779

如果某個屬性想可有可無

image-20240619110911690

<template>
	<Person :list="persons"/>
</template>

<script lang="ts" setup name="App">
import Person from './components/Person.vue'
import {reactive} from 'vue'
import {type Persons} from './types'

let persons = reactive<Persons>([
{id:'e98219e12',name:'張三',age:18},
{id:'e98219e13',name:'李四',age:19},
 {id:'e98219e14',name:'王五',age:20}
])
</script>

Person.vue中代碼:

只接受會出問題,萬一傳過來是個數字也會遍歷

image-20240619111609486

註意接收+限制類型的寫法,prop括弧裡面不寫了,提出來規定一個泛型,裡面為一個對象,誰要約束什麼

image-20240619111855781

image-20240619111859051

限制必要性,一個?父組件就可傳可不傳了

image-20240619112122252

如果不寫自己還有預設值,導入,再用

image-20240619112322154

用的時候註意,把原來寫的全部包起來,第二個參數寫預設值,並且鍵值對,屬性值不能為一個數組形式,可以寫為一個函數返回

image-20240619112326242

<template>
<div class="person">
<ul>
<li v-for="item in list" :key="item.id">
  {{item.name}}--{{item.age}}
</li>
</ul>
</div>
</template>

<script lang="ts" setup name="Person">
import {defineProps} from 'vue'
import {type PersonInter} from '@/types'

// 第一種寫法:僅接收
// const props = defineProps(['list'])

// 第二種寫法:接收+限制類型
// defineProps<{list:Persons}>()

// 第三種寫法:接收+限制類型+指定預設值+限制必要性
let props = withDefaults(defineProps<{list?:Persons}>(),{
list:()=>[{id:'asdasg01',name:'小豬佩奇',age:18}]
})
console.log(props)
</script>

3.13. 【生命周期】

  • 規律:

    生命周期整體分為四個階段,分別是:創建、掛載、更新、銷毀,每個階段都有兩個鉤子,一前一後。

  • Vue2的生命周期

    創建階段:beforeCreatecreated

    掛載階段:beforeMountmounted

    更新階段:beforeUpdateupdated

    銷毀階段:beforeDestroydestroyed

v3的創建就是setup本身

  • Vue3的生命周期

    創建階段:setup

    掛載階段:onBeforeMountonMounted

    更新階段:onBeforeUpdateonUpdated

    卸載階段:onBeforeUnmountonUnmounted

  • 常用的鉤子:onMounted(掛載完畢)、onUpdated(更新完畢)、onBeforeUnmount(卸載之前)

  • 示例代碼:

    <template>
      <div class="person">
        <h2>當前求和為:{{ sum }}</h2>
        <button @click="changeSum">點我sum+1</button>
      </div>
    </template>
    
    <!-- vue3寫法 -->
    <script lang="ts" setup name="Person">
      import { 
        ref, 
        onBeforeMount, 
        onMounted, 
        onBeforeUpdate, 
        onUpdated, 
        onBeforeUnmount, 
        onUnmounted 
      } from 'vue'
    
      // 數據
      let sum = ref(0)
      // 方法
      function changeSum() {
        sum.value += 1
      }
      console.log('setup')
      // 生命周期鉤子
      onBeforeMount(()=>{
        console.log('掛載之前')
      })
      onMounted(()=>{
        console.log('掛載完畢')
      })
      onBeforeUpdate(()=>{
        console.log('更新之前')
      })
      onUpdated(()=>{
        console.log('更新完畢')
      })
      onBeforeUnmount(()=>{
        console.log('卸載之前')
      })
      onUnmounted(()=>{
        console.log('卸載完畢')
      })
    </script>
    

3.14. 【自定義hook】

如果一個頁面中全是數據和方法,看起很臃腫複雜,跟vue2又很像

註意:如果要往空數組push等作出操作需要規定數組的泛型

image-20240619123154863

而hooks就類似於mixin,把所有代碼模塊化,src創建文件夾hooks,創建模塊的ts文件,把該屬於這個部分的代碼都放進來

image-20240619123524361

註意:這裡面就跟剛纔一樣什麼都能寫包括生命鉤子,但是會對外暴露一個函數,然後把所有數據方法返回

image-20240619123529934

image-20240619123535545

組件要用就直接導入,並且那邊是函數,這邊直接調用,通過解構賦值,獲得return出來的數據和方法

image-20240619123848962

示例代碼:

  • useSum.ts中內容如下:

    import {ref,onMounted} from 'vue'
    
    export default function(){
      let sum = ref(0)
    
      const increment = ()=>{
        sum.value += 1
      }
      const decrement = ()=>{
        sum.value -= 1
      }
      onMounted(()=>{
        increment()
      })
    
      //向外部暴露數據
      return {sum,increment,decrement}
    }		
    
  • useDog.ts中內容如下:

    import {reactive,onMounted} from 'vue'
    import axios,{AxiosError} from 'axios'
    
    export default function(){
      let dogList = reactive<string[]>([])
    
      // 方法
      async function getDog(){
        try {
          // 發請求
          let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
          // 維護數據
          dogList.push(data.message)
        } catch (error) {
          // 處理錯誤
          const err = <AxiosError>error
          console.log(err.message)
        }
      }
    
      // 掛載鉤子
      onMounted(()=>{
        getDog()
      })
    	
      //向外部暴露數據
      return {dogList,getDog}
    }
    
  • 組件中具體使用:

    <template>
      <h2>當前求和為:{{sum}}</h2>
      <button @click="increment">點我+1</button>
      <button @click="decrement">點我-1</button>
      <hr>
      <img v-for="(u,index) in dogList.urlList" :key="index" :src="(u as string)"> 
      <span v-show="dogList.isLoading">載入中......</span><br>
      <button @click="getDog">再來一隻狗</button>
    </template>
    
    <script lang="ts">
      import {defineComponent} from 'vue'
    
      export default defineComponent({
        name:'App',
      })
    </script>
    
    <script setup lang="ts">
      import useSum from './hooks/useSum'
      import useDog from './hooks/useDog'
    	
      let {sum,increment,decrement} = useSum()
      let {dogList,getDog} = useDog()
    </script>
    

4. 路由

4.1. 【對路由的理解】

image-20240715195545218

4.2. 【基本切換效果】

  • Vue3中要使用vue-router的最新版本,目前是4版本。

  • 基本規則

image-20240620105128654

第一步,創建導航區,展示區

image-20240620105239582

  • 第二步創建store文件夾,導入兩個api,第一個必須聲明路由工作模式,第二個如何來創建路由createrouter

image-20240620104921122

入口文件導入註冊

image-20240620105101469

  • 路由配置文件代碼如下:

    import {createRouter,createWebHistory} from 'vue-router'
    import Home from '@/pages/Home.vue'
    import News from '@/pages/News.vue'
    import About from '@/pages/About.vue'
    
    const router = createRouter({
    	history:createWebHistory(),
    	routes:[
    		{
    			path:'/home',
    			component:Home
    		},
    		{
    			path:'/about',
    			component:About
    		}
    	]
    })
    export default router
    
  • main.ts代碼如下:

    import router from './router/index'
    app.use(router)
    
    app.mount('#app')
    
  • App.vue代碼如下 第三步

    <template>
      <div class="app">
        <h2 class="title">Vue路由測試</h2>
        <!-- 導航區 -->
        <div class="navigate">
          <RouterLink to="/home" active-class="active">首頁</RouterLink>
          <RouterLink to="/news" active-class="active">新聞</RouterLink>
          <RouterLink to="/about" active-class="active">關於</RouterLink>
        </div>
        <!-- 展示區 -->
        <div class="main-content">
          <RouterView></RouterView>
        </div>
      </div>
    </template>
    
    <script lang="ts" setup name="App">
      import {RouterLink,RouterView} from 'vue-router'  
    </script>
    

4.3. 【兩個註意點】

  1. 路由組件通常存放在pagesviews文件夾,一般組件通常存放在components文件夾。

  2. 通過點擊導航,視覺效果上“消失” 了的路由組件,預設是被卸載掉的,需要的時候再去掛載

4.4.【路由器工作模式】

  1. history模式

    優點:URL更加美觀,不帶有#,更接近傳統的網站URL

    缺點:後期項目上線,需要服務端配合處理路徑問題,否則刷新會有404錯誤。

    const router = createRouter({
    	history:createWebHistory(), //history模式
    	/******/
    })
    
  2. hash模式

    優點:相容性更好,因為不需要伺服器端處理路徑。

    缺點:URL帶有#不太美觀,且在SEO優化方面相對較差。

    const router = createRouter({
    	history:createWebHashHistory(), //hash模式
    	/******/
    })
    

4.5. 【to的兩種寫法】

<!-- 第一種:to的字元串寫法 -->
<router-link active-class="active" to="/home">主頁</router-link>

<!-- 第二種:to的對象寫法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>

4.6. 【命名路由】

作用:可以簡化路由跳轉及傳參(後面就講)。

給路由規則命名:

routes:[
  {
    name:'zhuye',
    path:'/home',
    component:Home
  },
  {
    name:'xinwen',
    path:'/news',
    component:News,
  },
  {
    name:'guanyu',
    path:'/about',
    component:About
  }
]

跳轉路由:

<!--簡化前:需要寫完整的路徑(to的字元串寫法) -->
<router-link to="/news/detail">跳轉</router-link>

<!--簡化後:直接通過名字跳轉(to的對象寫法配合name屬性) -->
<router-link :to="{name:'guanyu'}">跳轉</router-link>

4.7. 【嵌套路由】

  1. 編寫News的子路由:Detail.vue

  2. 配置路由規則,使用children配置項:

    const router = createRouter({
      history:createWebHistory(),
    	routes:[
    		{
    			name:'zhuye',
    			path:'/home',
    			component:Home
    		},
    		{
    			name:'xinwen',
    			path:'/news',
    			component:News,
    			children:[
    				{
    					name:'xiang',
    					path:'detail',
    					component:Detail
    				}
    			]
    		},
    		{
    			name:'guanyu',
    			path:'/about',
    			component:About
    		}
    	]
    })
    export default router
    
  3. 跳轉路由(記得要加完整路徑):

    <router-link to="/news/detail">xxxx</router-link>
    <!-- 或 -->
    <router-link :to="{path:'/news/detail'}">xxxx</router-link>
    
  4. 記得去Home組件中預留一個<router-view>

    <template>
      <div class="news">
        <nav class="news-list">
          <RouterLink v-for="news in newsList" :key="news.id" :to="{path:'/news/detail'}">
            {{news.name}}
          </RouterLink>
        </nav>
        <div class="news-detail">
          <RouterView/>
        </div>
      </div>
    </template>
    

4.8. 【路由傳參】

query參數

  1. 傳遞參數

    <!-- 跳轉並攜帶query參數(to的字元串寫法) -->
    <router-link to="/news/detail?a=1&b=2&content=歡迎你">
    	跳轉
    </router-link>
    				
    <!-- 跳轉並攜帶query參數(to的對象寫法) -->
    <RouterLink 
      :to="{
        //name:'xiang', //用name也可以跳轉
        path:'/news/detail',
        query:{
          id:news.id,
          title:news.title,
          content:news.content
        }
      }"
    >
      {{news.title}}
    </RouterLink>
    
  2. 接收參數:

    import {useRoute} from 'vue-router'
    const route = useRoute()
    // 列印query參數
    console.log(route.query)
    

接收參數解構賦值版

image-20240620111747395

但是這樣有個問題,之前說過在響應式對象上面直接解構,會讓他失去響應式,解決方法是一個torefs

image-20240620111859940

params參數

要配置

image-20240620112146867

  1. 傳遞參數

    <!-- 跳轉並攜帶params參數(to的字元串寫法) -->
    <RouterLink :to="`/news/detail/001/新聞001/內容001`">{{news.title}}</RouterLink>
    				
    <!-- 跳轉並攜帶params參數(to的對象寫法) -->
    <RouterLink 
      :to="{
        name:'xiang', //用name跳轉
        params:{
          id:news.id,
          title:news.title,
          content:news.title
        }
      }"
    >
      {{news.title}}
    </RouterLink>
    
  2. 接收參數:

    import {useRoute} from 'vue-router'
    const route = useRoute()
    // 列印params參數
    console.log(route.params)
    

備註1:傳遞params參數時,若使用to的對象寫法,必須使用name配置項,不能用path

備註2:傳遞params參數時,需要提前在規則中占位。

4.9. 【路由的props配置】

就算剛纔用這個方法,但是還是複雜在標簽上

image-20240620112543493

作用:讓路由組件更方便的收到參數(可以將路由參數作為props傳給組件)

第一種,只需要在路由配置裡面開啟

image-20240620112738226

image-20240620112800533

第二種

image-20240620112955678

{
	name:'xiang',
	path:'detail/:id/:title/:content',
	component:Detail,

  // props的對象寫法,作用:把對象中的每一組key-value作為props傳給Detail組件
  // props:{a:1,b:2,c:3}, 

  // props的布爾值寫法,作用:把收到了每一組params參數,作為props傳給Detail組件
  // props:true
  
  // props的函數寫法,作用:把返回的對象中每一組key-value作為props傳給Detail組件
  props(route){
    return route.query
  }
}

4.10. 【 replace屬性】

  1. 作用:控制路由跳轉時操作瀏覽器歷史記錄的模式。

  2. 瀏覽器的歷史記錄有兩種寫入方式:分別為pushreplace

    • push是追加歷史記錄(預設值)。
    • rep
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 【問題分類】 YashanDB 開機自啟 【關鍵字】 開機自啟,依賴包 【問題描述】 資料庫所在伺服器重啟後只拉起monit、yasom、yasom進程,缺少yasdb進程: 【問題原因分析】 資料庫安裝的時候未啟動守護進程 【解決 / 規避方法】 進入資料庫之前的安裝目錄,啟動守護進程: Shel ...
  • 【標題】錯誤碼處理 【問題分類】鎖等待超時 【關鍵字】YAS-02024 【問題描述】執行語句時候,因鎖等待超時執行語句失敗 【問題原因分析】資料庫預設鎖等待時間為0秒,如果執行語句存在鎖等待過長會執行失敗 【解決/規避方法】 1、調整鎖等待的時間 SQL alter system set DDL_ ...
  • 面向物聯網和大規模運維場景,時序資料庫的存儲、分析一體化方案具有極大的優勢,openGemini在指標管理和多維度聚合方面都顯示出卓越的性能。 ...
  • 隨著國產替換的深化,企業對信創產品的需求逐漸融合更豐富的業務訴求以及未來數智規劃,正從“同類替換”轉向“迭代升級”。 當前,袋鼠雲的產品與晶元、伺服器、資料庫、操作系統、中間件、雲平臺等主流信創廠商全面相容適配,為企業提供了更加安全可靠、自主可控的數智化解決方案。 在完成相容適配的同時,公司的產品體 ...
  • 之前翻譯過一篇文章,介紹 MySQL 監控的一些原理,本文側重實操,使用夜鶯 v7.beta12.1 版本為大家做一個演示,採集器使用 Categraf,先看一下最終儀錶盤效果: 下麵開工。 1. 安裝夜鶯和 Categraf 夜鶯的安裝可以參考 夜鶯官方文檔,Categraf 的安裝可以參考 Ca ...
  • Text To SQL 指的是將自然語言轉化為能夠在關係型資料庫中執行的結構化查詢語言(簡稱 SQL)。近年來,伴隨人工智慧大模型技術的不斷進步,Text To SQL 任務的成功率顯著提升,這得益於大模型的推理、理解以及指令遵循等能力。 對於大數據平臺來說,集成 Text To SQL 功能意義非 ...
  • 當在UITableViewCell中載入網路圖片時,如果在圖片下載完成之前用戶滑動了UITableView,使得對應的UITableViewCell已經滑出屏幕,那麼這個被滑走的UITableViewCell是否還會顯示圖片,取決於如何處理圖片的載入和UITableViewCell的重用。 UITa ...
  • UITableView的重用機制避免了頻繁創建和銷毀單元格的開銷,使得在顯示大量數據時,保持流暢的用戶體驗和較低的資源消耗。。 當UITableView滾動時,屏幕上移出視圖的單元格會被回收到一個重用池中。當需要顯示新的單元格時,UITableView會首先檢查重用池中是否有可用的單元格。如果有,就 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...