92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线

vue3的基本使用(超詳細)

2023-1-16    前端達人

一、初識vue3

1.vue3簡介

  • 2020年9月18日,vue3發布3.0版本,代號大海賊時代來臨,One Piece
  • 特點:
    • 無需構建步驟,漸進式增強靜態的 HTML
    • 在任何頁面中作為 Web Components 嵌入
    • 單頁應用 (SPA)
    • 全棧 / 服務端渲染 (SSR)
    • Jamstack / 靜態站點生成 (SSG)
    • 開發桌面端、移動端、WebGL,甚至是命令行終端中的界面

2.Vue3帶來了什么

  • 打包大小減少40%
  • 初次渲染快55%,更新渲染快133%
  • 內存減少54%

3.分析目錄結構

  • main.js中的引入
  • 在模板中vue3中是可以沒有根標簽了,這也是比較重要的改變
  • 應用實例并不只限于一個。createApp API 允許你在同一個頁面中創建多個共存的 Vue 應用,而且每個應用都擁有自己的用于配置和全局資源的作用域。
//main.js //引入的不再是Vue構造函數了,引入的是一個名為createApp的工廠函數 import {createApp} from 'vue import App from './App.vue //創建應用實例對象-app(類似于之前vue2中的vm實例,但是app比vm更輕) createApp(APP).mount('#app') //卸載就是unmount,卸載就沒了 //createApp(APP).unmount('#app') //之前我們是這么寫的,在vue3里面這一塊就不支持了,會報錯的,引入不到 import vue from 'vue';  new Vue({ render:(h) => h(App) }).$mount('#app') //多個應用實例 const app1 = createApp({ /* ... */ }) app1.mount('#container-1') const app2 = createApp({ /* ... */ }) app2.mount('#container-2') 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

安裝vue3的開發者工具

  • 方式一: 打開chrome應用商店,搜索vue: 里面有個Vue.js devtools,且下面有個角標beta那個就是vue3的開發者工具
  • 方式二: 離線模式下,可以直接將包丟到擴展程序

二、 常用Composition API(組合式API)

1. setup函數

  1. 理解:Vue3.0中一個新的額配置項,值為一個函數

  2. 2.setup是所有Composition API(組合api) “表演的舞臺”

  3. 組件中所用到的:數據、方法等等,均要配置在setup中

  4. setup函數的兩種返回值:

    • 若返回一個對象,則對象中的屬性、方法,在模板中均可以直接使用。(重點關注)
    • 若返回一個渲染函數:則可以自定義渲染內容。
  5. 注意點:

    • 盡量不要與Vue2.x配置混用
      • Vue2.x配置(data ,methos, computed…)中訪問到setup中的屬性,方法
      • 但在setup中不能訪問到Vue2.x配置(data.methos,compued…)
      • 如果有重名,setup優先
    • setup不能是一個async函數,因為返回值不再是return的對象,而是promise,模板看不到return對象中的屬性
      在這里插入圖片描述
import {h} from 'vue' //向下兼容,可以寫入vue2中的data配置項 module default { name: 'App', setup(){ //數據 let name = '張三', let age = 18, //方法 function sayHello(){ console.log(name) }, //f返回一個對象(常用) return { name, age, sayHello } //返回一個函數(渲染函數) //return () => {return h('h1','學習')}  return () => h('h1','學習') } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

1.1關于單文件組件<script setup></script >

  • 每個 *.vue 文件最多可以包含一個 <script setup>。(不包括一般的 <script>)
  • 這個腳本塊將被預處理為組件的 setup() 函數,這意味著它將為每一個組件實例都執行。<script setup> 中的頂層綁定都將自動暴露給模板。
  • <script setup> 是在單文件組件 (SFC) 中使用組合式 API 的編譯時語法糖。當同時使用 SFC 與組合式 API 時該語法是默認推薦。相比于普通的 <script> 語法,它具有更多優勢:
    • 更少的樣板內容,更簡潔的代碼。
    • 能夠使用純 TypeScript 聲明 props 和自定義事件。這個我下面是有說明的
    • 更好的運行時性能 (其模板會被編譯成同一作用域內的渲染函數,避免了渲染上下文代理對象)。
    • 更好的 IDE 類型推導性能 (減少了語言服務器從代碼中抽取類型的工作)。
(1)基本語法:
/* 里面的代碼會被編譯成組件 setup() 函數的內容。
  這意味著與普通的 `<script>` 只在組件被首次引入的時候執行一次不同,
  `<script setup>` 中的代碼會在每次組件實例被創建的時候執行。*/ <script setup> console.log('hello script setup') </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
頂層的綁定會被暴露給模板

當使用 <script setup> 的時候,任何在 <script setup> 聲明的頂層的綁定 (包括變量,函數聲明,以及 import 導入的內容) 都能在模板中直接使用:

<script setup> // 變量 const msg = '王二麻子' // 函數 function log() { console.log(msg) } </script> <template> <button @click="log">{{ msg }}</button> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

import 導入的內容也會以同樣的方式暴露。這意味著我們可以在模板表達式中直接使用導入的 action 函數,而不需要通過 methods 選項來暴露它:

<script setup> import { say } from './action' </script> <template> <div>{{ say ('hello') }}</div> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(2)響應式

響應式狀態需要明確使用響應式 API 來創建。和 setup() 函數的返回值一樣,ref 在模板中使用的時候會自動解包:

<script setup> import { ref } from 'vue' const count = ref(0) </script> <template> <button @click="count++">{{ count }}</button> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
(3)使用組件:
  • <script setup> 范圍里的值也能被直接作為自定義組件的標簽名使用:
/**
*這里 MyComponent 應當被理解為像是在引用一個變量。
*如果你使用過 JSX,此處的心智模型是類似的。
*其 kebab-case 格式的 <my-component> 同樣能在模板中使用——不過,
*強烈建議使用 PascalCase 格式以保持一致性。同時這也有助于區分原生的自定義元素。
*/ <script setup> import MyComponent from './MyComponent.vue' </script> <template> <MyComponent /> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
動態組件
/**
*由于組件是通過變量引用而不是基于字符串組件名注冊的,
*在 <script setup> 中要使用動態組件的時候,應該使用*動態的 :is 來綁定:
*/ <script setup> import Foo from './Foo.vue' import Bar from './Bar.vue' </script> <template> <component :is="Foo" /> <component :is="someCondition ? Foo : Bar" /> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
遞歸組件
  • 一個單文件組件可以通過它的文件名被其自己所引用。例如:名為 FooBar.vue 的組件可以在其模板中用 <FooBar/> 引用它自己。
  • 注意這種方式相比于導入的組件優先級更低。如果有具名的導入和組件自身推導的名字沖突了,可以為導入的組件添加別名:
import { FooBar as FooBarChild } from './components' 
  • 1
命名空間組件
  • 可以使用帶 . 的組件標簽,例如 <Foo.Bar> 來引用嵌套在對象屬性中的組件。這在需要從單個文件中導入多個組件的時候非常有用:
<script setup> import * as Form from './form-components' </script> <template> <Form.Input> <Form.Label>label</Form.Label> </Form.Input> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
(4)使用自定義指令:
  • 全局注冊的自定義指令將正常工作。本地的自定義指令在 <script setup> 中不需要顯式注冊,但他們必須遵循 vNameOfDirective 這樣的命名規范:
<script setup> const vMyDirective = { beforeMount: (el) => { // 在元素上做些操作 } } </script> <template> <h1 v-my-directive>This is a Heading</h1> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 如果指令是從別處導入的,可以通過重命名來使其符合命名規范:
<script setup> import { myDirective as vMyDirective } from './MyDirective.js' </script> 
  • 1
  • 2
  • 3
(5)defineProps() 和 defineEmits():
  • 為了在聲明 props 和 emits 選項時獲得完整的類型推導支持,我們可以使用 defineProps 和 defineEmits API,它們將自動地在 <script setup> 中可用:
<script setup> const props = defineProps({ foo: String }) const emit = defineEmits(['change', 'delete']) // setup 代碼 </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • defineProps 和 defineEmits 都是只能在 <script setup> 中使用的編譯器宏。他們不需要導入,且會隨著 <script setup> 的處理過程一同被編譯掉。
  • defineProps 接收與 props 選項相同的值,defineEmits 接收與 emits 選項相同的值。
  • defineProps 和 defineEmits 在選項傳入后,會提供恰當的類型推導。
  • 傳入到 defineProps 和 defineEmits 的選項會從 setup 中提升到模塊的作用域。因此,傳入的選項不能引用在 setup 作用域中聲明的局部變量。這樣做會引起編譯錯誤。但是,它可以引用導入的綁定,因為它們也在模塊作用域內。
(5)defineExpose:
  • 使用 <script setup> 的組件是默認關閉的——即通過模板引用或者 $parent 鏈獲取到的組件的公開實例,不會暴露任何在 <script setup> 中聲明的綁定。
//可以通過 defineExpose 編譯器宏來顯式指定在 <script setup> 組件中要暴露出去的屬性: <script setup> import { ref } from 'vue' const a = 1 const b = ref(2) defineExpose({ a, b }) </script> //當父組件通過模板引用的方式獲取到當前組件的實例, //獲取到的實例會像這樣 { a: number, b: number } (ref 會和在普通實例中一樣被自動解包) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
(6)useSlots() 和 useAttrs():
  • 在 <script setup> 使用 slots 和 attrs 的情況應該是相對來說較為罕見的,因為可以在模板中直接通過 $slots 和 $attrs 來訪問它們。在你的確需要使用它們的罕見場景中,可以分別用 useSlots 和 useAttrs 兩個輔助函數:
<script setup> import { useSlots, useAttrs } from 'vue' const slots = useSlots() const attrs = useAttrs() </script> //useSlots 和 useAttrs 是真實的運行時函數,它的返回與 setupContext.slots 和 setupContext.attrs 等價。 //它們同樣也能在普通的組合式 API 中使用。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
(7)與普通的 <script> 一起使用:

<script setup> 可以和普通的 <script> 一起使用。普通的 <script> 在有這些需要的情況下或許會被使用到:

  • 聲明無法在
<script> // 普通 <script>, 在模塊作用域下執行 (僅一次) runSideEffectOnce() // 聲明額外的選項 export default { inheritAttrs: false, customOptions: {} } </script> <script setup> // 在 setup() 作用域中執行 (對每個實例皆如此) </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
(8)頂層 await:
  • <script setup> 中可以使用頂層 await。結果代碼會被編譯成 async setup():
<script setup> const post = await fetch(`/api/post/1`).then((r) => r.json()) </script> // 另外,await 的表達式會自動編譯成在 await 之后保留當前組件實例上下文的格式。 
  • 1
  • 2
  • 3
  • 4

2.ref 函數

  • 作用:定義一個響應式的數據
  • 語法: const xxx = ref(initValue)
    • 創建一個包含響應式數據引用對象(reference對象)
    • JS中操作數據:xxx.value
    • 模板中讀取數據:不需要.value,直接:
      {{xxx}}
  • 備注:
    • 接收的數據可以是:基本類型、也可以是對象類型
    • 基本類型的數據:響應式依然靠的是Object.defineProperty()的get和set完成的
    • 對象類型的數據: 內部”求助“了Vue3.0中的一個新的函數——reactive函數

3.reactive 函數

  • 作用:定義一個對象類型的響應式數據(基本類型別用他,用ref函數)
  • 語法:const 代理對象 = reactive(被代理對象)接收一個對象(或數組),返回一個代理對象(proxy對象)
  • reactive定義的響應式數據是”深層次的“
  • 內部基于ES6的Proxy實現,通過代理對象操作源對象內部數據進行操作

4.Vue3.0中響應式原理

  • 先來看一看vue2的響應式原理
    • 對象類型: 通過Object.defineProperty()對屬性的讀取、修改進行攔截(數據劫持)
    • 數組類型:通過重寫更新數組的一系列方法來實現攔截。(對數組的變更方法進行了包裹)
Object.defineProperty( data, 'count', { get(){}, set(){} }) //模擬實現一下 let person = { name: '張三', age: 15, } let p = {} Object.defineProperty( p, 'name', { configurable: true, //配置這個屬性表示可刪除的,否則delete p.name 是刪除不了的 false get(){ //有人讀取name屬性時調用 return person.name }, set(value){ //有人修改時調用 person.name = value } }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 存在問題:
    1. 新增屬性。刪除屬性。界面不會更新
    2. 直接通過下表修改數組,界面不會自動更新
  • vue3的響應式
    • 實現原理:
      • 通過Proxy(代理):攔截對象中任意屬性的變化,包括:屬性值的讀寫、屬性的添加、屬性的刪除等等。
      • 通過Reflect(反射):對被代理對象的屬性進行操作
      • MDN文檔中描述的Proxy與Reflect:可以參考對應的文檔
//模擬vue3中實現響應式 let person = { name: '張三', age: 15, } //我們管p叫做代理數據,管person叫源數據 const p = new Proxy(person,{ //target代表的是person這個源對象,propName代表讀取或者寫入的屬性名 get(target,propName){ console.log('有人讀取了p上面的propName屬性') return target[propName] }, //不僅僅是修改調用,增加的時候也會調用 set(target,propName,value){ console.log(`有人修改了p身上的${propName}屬性,我要去更新界面了`) target[propName] = value }, deleteProperty(target,propName){ console.log(`有人刪除了p身上的${propName}屬性,我要去更新界面了`) return delete target[propName] } }) //映射到person上了,捕捉到修改,那就是響應式啊 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
//vue3底層源碼不是我們上面寫的那么low,實現原理一樣,但是用了一個新的方式 window.Reflect ![Reflect的寫法](https://img-blog.csdnimg.cn/565f96b1be74435cacbc42e06706791d.png) let obj = { a: 1, b:2, } //傳統的只能通過try catch去捕獲異常,如果使用這種那么底層源碼將會有一堆try catch try{ Object.defineProperty( obj, 'c', { get(){ return 3 }, }) Object.defineProperty( obj, 'c', { get(){ return 4 }, }) } catch(error) { console.log(error) } //新的方式: 通過Reflect反射對象去操作,相對來說要舒服一點,不會要那么多的try catch const x1 = Reflect.defineProperty( obj, 'c', { get(){ return 3 }, }) const x2 = Reflect.defineProperty( obj, 'c', { get(){ return 3 }, }) //x1,和x2是有返回布爾值的 if(x2){ console.log('某某操作成功了') }else { console.log('某某操作失敗了') } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 所以vue3最終的響應式原理如下:
let person = { name: '張三', age: 15, } //我們管p叫做代理數據,管person叫源數據 const p = new Proxy(person,{ //target代表的是person這個源對象,propName代表讀取或者寫入的屬性名 get(target,propName){ console.log('有人讀取了p上面的propName屬性') return Reflect.get(target, propName) }, //不僅僅是修改調用,增加的時候也會調用 set(target,propName,value){ console.log(`有人修改了p身上的${propName}屬性,我要去更新界面了`) Reflect.set(target, propName, value) }, deleteProperty(target,propName){ console.log(`有人刪除了p身上的${propName}屬性,我要去更新界面了`) return Reflect.deleteProperty(target,propName) } }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5.reactive對比ref

  • 從定義數據角度對比:

    • ref用來定義: 基本數據類型
    • reactive用來定義: 對象(或數組)類型數據
    • 備注: ref也可以用來定義對象(或數組)類型數據,它內部會自動通過reactive轉為代理對象
  • 從原理角度對比:

    • ref通過Object.defineProperty()的get和set來實現響應式(數據劫持)
    • reactive通過Proxy來實現響應式(數據劫持),并通過Reflect操作源對象內部的數據
  • 從使用角度對比:

    • ref定義數據:操作數據需要 .value ,讀取數據時模板中直接讀取不需要 .value
    • reactive 定義的數據: 操作數據和讀取數據均不需要 .value

5.setup的兩個注意點

  • setup執行的時機
    • 在beforeCreate之前執行一次,this是undefined
    • setup的參數
      • props:值為對象,包含: 組件外部傳遞過來,且組件內部聲明接收了屬性
      • context:上下文對象
        • attrs: 值為對象,包含:組件外部傳遞過來,但沒有在props配置中聲明的屬性,相當于 this.$attrs
        • slots:收到插槽的內容,相當于$slots
        • emit: 分發自定義事件的函數,相當于this.$emit
//父組件 <script setup> // This starter template is using Vue 3 <script setup> SFCs // Check out https://vuejs.org/api/sfc-script-setup.html#script-setup import HelloWorld from './components/test3.vue'; const hello = (val) =>{ console.log('傳遞的參數是:'+ val); } </script> <template> <img alt="Vue logo" src="./assets/logo.png" /> <HelloWorld msg="傳遞吧" @hello="hello"> <template v-slot:cacao> <span>是插槽嗎</span> </template> <template v-slot:qwe> <span>meiyou</span> </template> </HelloWorld> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
//子組件 export default { name: 'test3', props: ['msg'], emits:['hello'], //這里setup接收兩個參數,一個是props,一個是上下文context setup(props,context){ /**
         * props就是父組件傳來的值,但是他是Porxy類型的對象
         * >Proxy:{msg:'傳遞吧'}
         * 可以當作我們自定義的reactive定義的數據
         */ /**
         * context是一個對象 包含以下內容:
         * 1.emit觸發自定義事件的 
         * 2.attrs 相當于vue2里面的 $attrs 包含:組件外部傳遞過來,但沒有在props配置中聲明的屬性
         * 3.slots 相當于vue2里面的 $slots
         * 3.expose 是一個回調函數
         */ console.log(context.slots); let person = reactive({ name: '張三', age: 17, }) function changeInfo(){ context.emit('hello', 666) } //返回對象 return { person, changeInfo } //返回渲染函數(了解) 這個h是個函數 //return () => h('name','age') } } </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

6.計算屬性與監視

(1)computed函數
  • 與vue2.x中的寫法一致
  • 需要引入computed
<template> <h1>一個人的信息</h1> <div> 姓: <input type="text" v-model="person.firstName"> 名:<input type="text" v-model="person.lastName"> <div> <span>簡名:{{person.smallName}}</span> <br> <span>全名:{{person.fullName}}</span> </div> </div> </template> <script> import { computed,reactive } from 'vue' export default { name: 'test4', props: ['msg'], emits:['hello'], setup(){ let person = reactive({ firstName: '張', lastName: '三' }) //簡寫形式 person.smallName = computed(()=>{ return person.firstName + '-' + person.lastName }) //完全形態 person.fullName = computed({ get(){ console.log('調用get'); return person.firstName + '*' + person.lastName }, set(value){ console.log('調用set'); const nameArr = value.split('*') person.firstName = nameArr[0] person.firstName = nameArr[1] }, }) return { person, } }, } </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
(2)watch函數
  • 和computed一樣,需要引入api
  • 有兩個小坑:

1.監視reactive定義的響應式數據的時候:oldValue無法獲取到正確的值,強制開啟了深度監視(deep配置無效)
2.監視reactive定義的響應式數據中某個屬性的時候:deep配置有效
具體請看下面代碼以及注釋

<template> <h1>當前求和為: {{sum}}</h1> <button @click="sum++">點我+1</button> <hr> <h1>當前信息為: {{msg}}</h1> <button @click="msg+='!' ">修改信息</button> <hr> <h2>姓名: {{person.name}}</h2> <h2>年齡: {{person.age}}</h2> <button @click="person.name += '~' ">修改姓名</button> <button @click="person.age++">增長年齡</button> </template> <script> //使用setup的注意事項 import { watch,ref,reactive } from 'vue' export default { name: 'test5', props: ['msg'], emits:['hello'], setup(){ let sum = ref(0) let msg = ref('你好啊') let person = reactive({ name: '張三', age: 18, job:{ salary: '15k' }, }) //由于這里的this是指的是undefined,所以使用箭頭函數 //情況一:監視ref所定義的一個響應式數據 // watch(sum, (newValue,oldValue)=>{ //     console.log('新的值',newValue); //     console.log('舊的值',oldValue); // }) //情況二:監視ref所定義的多個響應式數據 watch([sum,msg], (newValue,oldValue)=>{ console.log('新的值',newValue); //['sum的newValue', 'msg的newValue'] console.log('舊的值',oldValue); //['sum的oldValue', 'msg的oldValue'] },{immediate: true,deep:true}) //這里vue3的deep是有點小問題的,可以不用deep,(隱式強制deep) //情況三:監視reactive定義的所有響應式數據, //1.此處無法獲取正確的oldValue(newValue與oldValue是一致值),且目前無法解決 //2.強制開啟了深度監視(deep配置無效) /**
            * 受到碼友熱心評論解釋: 此處附上碼友的解釋供大家參考:
            * 1. 當你監聽一個響應式對象的時候,這里的newVal和oldVal是一樣的,因為他們是同一個對象【引用地址一樣】,
            *    即使里面的屬性值會發生變化,但主體對象引用地址不變。這不是一個bug。要想不一樣除非這里把對象都換了
            * 
            * 2. 當你監聽一個響應式對象的時候,vue3會隱式的創建一個深層監聽,即對象里只要有變化就會被調用。
            *    這也解釋了你說的deep配置無效,這里是強制的。
            */ watch(person, (newValue,oldValue)=>{ console.log('新的值',newValue); console.log('舊的值',oldValue); }) //情況四:監視reactive對象中某一個屬性的值, //注意: 這里監視某一個屬性的時候可以監聽到oldValue watch(()=>person.name, (newValue,oldValue)=>{ console.log('新的值',newValue); console.log('舊的值',oldValue); }) //情況五:監視reactive對象中某一些屬性的值 watch([()=>person.name,()=>person.age], (newValue,oldValue)=>{ console.log('新的值',newValue); console.log('舊的值',oldValue); }) //特殊情況: 監視reactive響應式數據中深層次的對象,此時deep的配置奏效了 watch(()=>person.job, (newValue,oldValue)=>{ console.log('新的值',newValue); console.log('舊的值',oldValue); },{deep:true}) //此時deep有用 return { sum, msg, person, } }, } </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
(3)watchEffect函數
  • watch的套路是:既要指明監視的屬性,也要指明監視的回調
  • watchEffect的套路是:不用指明監視哪個屬性,監視的回調中用到哪個屬性,那就監視哪個屬性
  • watchEffect有點像computed:
    • 但computed注重的計算出來的值(回調函數的返回值),所以必須要寫返回值
    • 而watchEffect更注重的是過程(回調函數的函數體),所以不用寫返回值
<script> //使用setup的注意事項 import { ref,reactive,watchEffect } from 'vue' export default { name: 'test5', props: ['msg'], emits:['hello'], setup(){ let sum = ref(0) let msg = ref('你好啊') let person = reactive({ name: '張三', age: 18, job:{ salary: '15k' }, }) //用處: 如果是比較復雜的業務,發票報銷等,那就不許需要去監聽其他依賴,只要發生變化,立馬重新回調 //注重邏輯過程,你發生改變了我就重新執行回調,不用就不執行,只執行一次 watchEffect(()=>{ //這里面你用到了誰就監視誰,里面就發生回調 const x1 = sum.value
                console.log('我調用了'); }) return { sum, msg, person, } }, } </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

7.生命周期函數

 <template> <h1>生命周期</h1> <p>當前求和為: {{sum}}</p> <button @click="sum++">加一</button> </template> <script> //使用setup的注意事項 import { ref,reactive,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from 'vue' export default { name: 'test7', setup(){ let sum = ref(0) //通過組合式API的形式去使用生命周期鉤子 /**
             * beforeCreate 和  created 這兩個生命周期鉤子就相當于 setup 所以,不需要這兩個
             * 
             * beforeMount   ===>  onBeforeMount
             * mounted       ===>  onMounted
             * beforeUpdate  ===>  onBeforeUpdate
             * updated       ===>  onUpdated
             * beforeUnmount ===>  onBeforeUnmount
             * unmounted     ===>  onUnmounted
             */ console.log('---setup---'); onBeforeMount(()=>{ console.log('---onBeforeMount---'); }) onMounted(()=>{ console.log('---onMounted---'); }) onBeforeUpdate(()=>{ console.log('---onBeforeUpdate---'); }) onUpdated(()=>{ console.log('---onUpdated---'); }) onBeforeUnmount(()=>{ console.log('---onBeforeUnmount---'); }) onUnmounted(()=>{ console.log('---onUnmounted---'); }) return { sum } }, //這種是外層的寫法,如果想要使用組合式api的話需要放在setup中 beforeCreate(){ console.log('---beforeCreate---'); }, created(){ console.log('---created---'); }, beforeMount(){ console.log('---beforeMount---'); }, mounted(){ console.log('---mounted---'); }, beforeUpdate(){ console.log('---beforeUpdate---'); }, updated(){ console.log('---updated---'); }, //卸載之前 beforeUnmount(){ console.log('---beforeUnmount---'); }, //卸載之后 unmounted(){ console.log('---unmounted---'); } } </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

8.自定義hook函數

  • 什么是hook函數: 本質是一個函數,把setup函數中使用的Composition API進行了封裝
  • 類似于vue2.x中的 mixin
  • 自定義hook的優勢: 復用代碼,讓setup中的邏輯更清楚易懂
  • 使用hook實現鼠標打點”:
    創建文件夾和usePoint.js文件
    在這里插入圖片描述
//usePoint.js import {reactive,onMounted,onBeforeUnmount } from 'vue' function savePoint(){ //實現鼠標打點的數據 let point = reactive({ x: null, y: null }) //實現鼠標點的方法 const savePoint = (e)=>{ point.x = e.pageX
         point.y = e.pageY } //實現鼠標打點的生命周期鉤子 onMounted(()=>{ window.addEventListener('click',savePoint) }) onBeforeUnmount(()=>{ window.removeEventListener('click',savePoint) }) return point } export default savePoint 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
//組件test.vue <template> <p>當前求和為: {{sum}} </p> <button @click="sum++">加一</button> <hr> <h2>當前點擊時候的坐標: x: {{point.x}} y:{{point.y}}</h2> </template> <script> import { ref } from 'vue' import usePoint from '../hooks/usePoint' export default { name: 'test8', setup(props,context){ let sum = ref(0) let point = usePoint() return { sum, point } } } </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

9.toRef

  • 作用: 創建一個ref對象,其value值指向另一個對象中的某個屬性值
  • 語法: const name = toRef(person, ‘name’)
  • 應用:要將響應式對象中的某個屬性單獨提供給外部使用
  • 擴展: toRefs與toRef功能一致,但是可以批量創建多個ref對象,語法: toRefs(person)
 <template> <h2>姓名: {{name2}}</h2> <h2>年齡: {{person.age}}</h2> <button @click="person.name += '~' ">修改姓名</button> <button @click="person.age++">增長年齡</button> </template> <script> //使用setup的注意事項 import { reactive, toRef, toRefs } from 'vue' export default { name: 'test9', setup(){ let person = reactive({ name: '張三', age: 18, job:{ salary: '15k' }, }) //toRef const name2 = toRef(person,'name') //第一個參數是對象,第二個參數是鍵名 console.log('toRef轉變的是',name2); //ref定義的對象 //toRefs,批量處理對象的所有屬性 //const x  = toRefs(person) //console.log('toRefs轉變的是',x); //是一個對象 return { person, name2, ...toRefs(person) } }, } </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

三、TypeScript 與組合式 API

1.為組件的 props 標注類型

//場景一: 使用<script setup> <script setup lang="ts"> const props = defineProps({ foo: { type: String, required: true }, bar: Number }) props.foo // string props.bar // number | undefined </script> //也可以將 props 的類型移入一個單獨的接口中 <script setup lang="ts"> interface Props { foo: string
  bar?: number } const props = defineProps<Props>() </script> //場景二: 不使用<script setup> import { defineComponent } from 'vue' export default defineComponent({ props: { message: String }, setup(props) { props.message // <-- 類型:string } }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 注意點:為了生成正確的運行時代碼,傳給 defineProps() 的泛型參數必須是以下之一:
//1.一個類型字面量: defineProps<{ /*... */ }>() //2.對同一個文件中的一個接口或對象類型字面量的引用 interface Props {/* ... */} defineProps<Props>() //3.接口或對象字面類型可以包含從其他文件導入的類型引用,但是,傳遞給 defineProps 的泛型參數本身不能是一個導入的類型: import { Props } from './other-file' // 不支持! defineProps<Props>() 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • Props 解構默認值
//當使用基于類型的聲明時,失去了對 props 定義默認值的能力。通過目前實驗性的響應性語法糖來解決: <script setup lang="ts"> interface Props { foo: string
  bar?: number } // 對 defineProps() 的響應性解構 // 默認值會被編譯為等價的運行時選項 const { foo, bar = 100 } = defineProps<Props>() </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.為組件的 emits 標注類型

//場景一: 使用<script setup> <script setup lang="ts"> const emit = defineEmits(['change', 'update']) // 基于類型 const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void }>() </script> //場景二: 不使用<script setup> import { defineComponent } from 'vue' export default defineComponent({ emits: ['change'], setup(props, { emit }) { emit('change') // <-- 類型檢查 / 自動補全 } }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.為 ref() 標注類型

import { ref } from 'vue' import type { Ref } from 'vue' //1.ref 會根據初始化時的值推導其類型: // 推導出的類型:Ref<number> const year = ref(2020) // => TS Error: Type 'string' is not assignable to type 'number'. year.value = '2020' //2.指定一個更復雜的類型,可以通過使用 Ref 這個類型: const year: Ref<string | number> = ref('2020') year.value = 2020 // 成功! //3.在調用 ref() 時傳入一個泛型參數,來覆蓋默認的推導行為: // 得到的類型:Ref<string | number> const year = ref<string | number>('2020') year.value = 2020 // 成功! //4.如果你指定了一個泛型參數但沒有給出初始值,那么最后得到的就將是一個包含 undefined 的聯合類型: // 推導得到的類型:Ref<number | undefined> const n = ref<number>() 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4.為reactive() 標注類型

import { reactive } from 'vue' //1.reactive() 也會隱式地從它的參數中推導類型: // 推導得到的類型:{ title: string } const book = reactive({ title: 'Vue 3 指引' }) //2.要顯式地標注一個 reactive 變量的類型,我們可以使用接口: interface Book { title: string
  year?: number } const book: Book = reactive({ title: 'Vue 3 指引' }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

5.為 computed() 標注類型

import { ref, computed } from 'vue' //1.computed() 會自動從其計算函數的返回值上推導出類型: const count = ref(0) // 推導得到的類型:ComputedRef<number> const double = computed(() => count.value * 2) // => TS Error: Property 'split' does not exist on type 'number' const result = double.value.split('') //2.通過泛型參數顯式指定類型: const double = computed<number>(() => { // 若返回值不是 number 類型則會報錯 }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

6.為事件處理函數標注類型

//在處理原生 DOM 事件時,應該為我們傳遞給事件處理函數的參數正確地標注類型 <script setup lang="ts"> function handleChange(event) { // 沒有類型標注時 `event` 隱式地標注為 `any` 類型, // 這也會在 tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 時報出一個 TS 錯誤。 console.log(event.target.value) } </script> <template> <input type="text" @change="handleChange" /> </template> //因此,建議顯式地為事件處理函數的參數標注類型,需要顯式地強制轉換 event 上的屬性: function handleChange(event: Event) { console.log((event.target as HTMLInputElement).value) } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

7.為 provide / inject 標注類型

/*
provide 和 inject 通常會在不同的組件中運行。要正確地為注入的值標記類型,
Vue 提供了一個 InjectionKey 接口,它是一個繼承自 Symbol 的泛型類型,
可以用來在提供者和消費者之間同步注入值的類型:
*/ import { provide, inject } from 'vue' import type { InjectionKey } from 'vue' const key = Symbol() as InjectionKey<string> provide(key, 'foo') // 若提供的是非字符串值會導致錯誤 const foo = inject(key) // foo 的類型:string | undefined //建議將注入 key 的類型放在一個單獨的文件中,這樣它就可以被多個組件導入。 //當使用字符串注入 key 時,注入值的類型是 unknown,需要通過泛型參數顯式聲明: const foo = inject<string>('foo') // 類型:string | undefined //注意注入的值仍然可以是 undefined,因為無法保證提供者一定會在運行時 provide 這個值。 //當提供了一個默認值后,這個 undefined 類型就可以被移除: const foo = inject<string>('foo', 'bar') // 類型:string //如果你確定該值將始終被提供,則還可以強制轉換該值: const foo = inject('foo') as string 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

8.為模板引用標注類型

//模板引用需要通過一個顯式指定的泛型參數和一個初始值 null 來創建: <script setup lang="ts"> import { ref, onMounted } from 'vue' const el = ref<HTMLInputElement | null>(null) onMounted(() => { el.value?.focus() }) </script> /**
    注意為了嚴格的類型安全,有必要在訪問 el.value 時使用可選鏈或類型守衛。這是因為直到組件被掛載前,
    這個 ref 的值都是初始的 null,并且在由于 v-if 的行為將引用的元素卸載時也可以被設置為 null。
*/ <template> <input ref="el" /> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

9.為組件模板引用標注類型

//有時,你可能需要為一個子組件添加一個模板引用,以便調用它公開的方法。舉例來說,我們有一個 MyModal 子組件,它有一個打開模態框的方法 <!-- MyModal.vue --> <script setup lang="ts"> import { ref } from 'vue' const isContentShown = ref(false) const open = () => (isContentShown.value = true) defineExpose({ open }) </script> //為了獲取 MyModal 的類型,我們首先需要通過 typeof 得到其類型,再使用 TypeScript 內置的 InstanceType 工具類型來獲取其實例類型: <!-- App.vue --> <script setup lang="ts"> import MyModal from './MyModal.vue' const modal = ref<InstanceType<typeof MyModal> | null>(null) const openModal = () => { modal.value?.open() } </script> //注意,如果你想在 TypeScript 文件而不是在 Vue SFC 中使用這種技巧,需要開啟 Volar 的Takeover 模式。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

四、Vuex與組合式API

  • 組合式API 可以通過調用 useStore 函數,來在 setup 鉤子函數中訪問 store。這與在組件中使用選項式 API 訪問 this.$store 是等效的。
import { useStore } from 'vuex' export default { setup () { const store = useStore() } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.訪問 state 和 getter

  • 為了訪問 state 和 getter,需要創建 computed 引用以保留響應性,這與在選項式 API 中創建計算屬性等效。
import { computed } from 'vue' import { useStore } from 'vuex' export default { setup () { const store = useStore() return { // 在 computed 函數中訪問 state count: computed(() => store.state.count), // 在 computed 函數中訪問 getter double: computed(() => store.getters.double) } } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.訪問 Mutation 和 Action

  • 要使用 mutation 和 action 時,只需要在 setup 鉤子函數中調用 commit 和 dispatch 函數。
import { useStore } from 'vuex' export default { setup () { const store = useStore() return { // 使用 mutation increment: () => store.commit('increment'), // 使用 action asyncIncrement: () => store.dispatch('asyncIncrement') } } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。


分享此文一切功德,皆悉回向給文章原作者及眾讀者.
免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。


藍藍設計www.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司

日歷

鏈接

個人資料

藍藍設計的小編 http://www.skdbbs.com

存檔

92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线
欧美激情视频在线观看| 日本一区二区三区在线播放| 91热福利电影| 中文字幕日韩av电影| 亚洲网站在线观看| 欧美视频第一页| 欧洲成人在线视频| 97热在线精品视频在线观看| 国产精品三级久久久久久电影| 91在线精品播放| 国产精品h在线观看| 国产精品对白刺激| 国产成人+综合亚洲+天堂| 国产区精品在线观看| 日本久久久久久| 欧美日产国产成人免费图片| 欧美老女人www| 国产成人在线亚洲欧美| 亚洲天堂av在线免费观看| 亚洲第一偷拍网| 欧美激情一区二区久久久| 国产精品视频自拍| 91精品国产综合久久久久久久久| 久久久视频在线| 亚洲а∨天堂久久精品9966| 久久精品电影一区二区| 日韩av综合网站| 亚洲日本欧美中文幕| 欧美极品少妇xxxxⅹ裸体艺术| 日韩高清电影免费观看完整版| 亚洲成人av在线播放| 色www亚洲国产张柏芝| 午夜精品久久久久久久久久久久久| 91欧美视频网站| 日韩av网址在线观看| 日韩国产精品一区| 久久综合伊人77777蜜臀| 国产日韩欧美黄色| 成人妇女淫片aaaa视频| 久久婷婷国产麻豆91天堂| 久久精品电影网| 69久久夜色精品国产69乱青草| 日韩免费在线电影| 欧美性猛交99久久久久99按摩| 久久免费视频在线| 成人免费淫片aa视频免费| 国模精品一区二区三区色天香| 欧美夜福利tv在线| 亚洲国产精品一区二区久| 伊人av综合网| 性欧美视频videos6一9| 福利二区91精品bt7086| 日本久久久a级免费| 欧美大尺度激情区在线播放| 久久精品在线视频| 欧美性极品少妇精品网站| 欧美精品一区在线播放| 国产国语刺激对白av不卡| 国产精品99久久99久久久二8| 日本最新高清不卡中文字幕| 欧美成人免费网| 国产va免费精品高清在线| 日韩在线观看免费全集电视剧网站| 亚洲精品欧美一区二区三区| 亚洲直播在线一区| 777精品视频| 日韩av在线不卡| 亚洲直播在线一区| 精品久久久久久久久久久久| 成人福利视频网| 欧美日韩国产成人在线观看| 国产精品久久久999| 一区二区三区高清国产| 国产婷婷成人久久av免费高清| 欧美国产第二页| 中文字幕av一区中文字幕天堂| 国产999精品| 日韩电影中文字幕av| 欧亚精品在线观看| 97在线观看免费高清| 国产精品亚洲аv天堂网| 日韩精品久久久久久久玫瑰园| 亚洲91av视频| 一本色道久久综合狠狠躁篇怎么玩| 亚洲一区二区三区乱码aⅴ| 性色av一区二区三区| 精品日本美女福利在线观看| 亚洲国产另类 国产精品国产免费| 国产精品色悠悠| 久久久国产成人精品| 午夜精品福利在线观看| 中文字幕日韩在线观看| 成人激情视频小说免费下载| 亚洲精品一区在线观看香蕉| 久久精品视频va| 国产精品精品久久久久久| 中文字幕欧美日韩va免费视频| 欧美一级淫片播放口| 国内精品久久久久久影视8| 91在线免费看网站| 夜夜嗨av色综合久久久综合网| 97精品国产97久久久久久春色| 高清一区二区三区日本久| 亚洲xxxx视频| 欧美日本黄视频| 日韩男女性生活视频| 精品成人乱色一区二区| 91在线国产电影| 欧美自拍大量在线观看| 国产精品视频不卡| 97精品免费视频| 国产91色在线|| 福利一区福利二区微拍刺激| 4438全国成人免费| 日韩欧美一区二区三区久久| 欧美日韩国产一中文字不卡| 911国产网站尤物在线观看| 亚洲人成网在线播放| 中文字幕欧美日韩va免费视频| 国产日韩精品在线观看| 久久在线精品视频| 91麻豆桃色免费看| 2019中文字幕在线| 国产成人精品免高潮费视频| 91在线无精精品一区二区| 91精品国产色综合久久不卡98口| 91亚洲国产成人精品性色| 亚洲美女精品成人在线视频| 欧美孕妇与黑人孕交| 久久国产天堂福利天堂| 91探花福利精品国产自产在线| 热门国产精品亚洲第一区在线| 欧美在线xxx| 永久555www成人免费| 色狠狠av一区二区三区香蕉蜜桃| 欧美乱人伦中文字幕在线| 日韩激情视频在线播放| 国产网站欧美日韩免费精品在线观看| 日韩av网站电影| 91禁外国网站| 久久久精品在线观看| 97视频com| 国产亚洲精品久久| 欧美成人一区二区三区电影| 中文字幕日韩欧美在线| 欧美一级淫片播放口| 亚洲欧美日韩一区二区三区在线| 亚洲色图13p| 日韩av成人在线| 日韩美女在线看| 日韩国产激情在线| 久久国内精品一国内精品| 中日韩午夜理伦电影免费| 精品国模在线视频| 91性高湖久久久久久久久_久久99| 欧美性xxxx极品hd欧美风情| 久久av在线看| 国产精品成人在线| 欧美高清在线观看| 国产精品pans私拍| 成人看片人aa| 91沈先生作品| xxxxx成人.com| 成人亲热视频网站|