Vue3 – コンポジション API(二)

1. 計算関数の使用

1.1、計算された

        前のセクションで計算された属性について説明しました。一部の属性が他の状態に依存する場合、計算された属性を使用してそれを処理できます。

  • 以前の Options API では、計算されたオプションを使用して完了しました。
  • コンポジション API では、setup 関数で計算されたメソッドを使用して、計算されたプロパティを書き込むことができます。

計算された使用方法?

  • 方法 1 : getter 関数を受け取り、getter 関数によって返された値に対して変更されていない ref オブジェクトを返します。
  • 方法 2 : get および setでオブジェクトを受け取り、変更可能な (読み取りおよび書き込み可能な) ref オブジェクトを返します。

1.2. 例 

app.vue

<template>
  <h2>{
   
   { fullname }}</h2>
  <button @click="setFullname">设置fullname</button>
  <h2>{
   
   { scoreLevel }}</h2>
</template>

<script>
  import {reactive, computed, ref} from 'vue'

  export default {
    setup() {
      // 1.定义数据
      const names = reactive({
        firstName: "kobe",
        lastName: "bryant"
      })

      // const fullname = computed(() => {
      //   return names.firstName + " " + names.lastName
      // })
      const fullname = computed({
        set: function (newValue) {
          const tempNames = newValue.split(" ")
          names.firstName = tempNames[0]
          names.lastName = tempNames[1]
        },
        get: function () {
          return names.firstName + " " + names.lastName
        }
      })

      console.log(fullname)  // 是一个ref对象

      function setFullname() {
        fullname.value = "coder why"
        console.log(names)
      }


      // 2.定义score
      const score = ref(89)
      const scoreLevel = computed(() => {
        return score.value >= 60 ? "及格" : "不及格"
      })

      return {
        names,
        fullname,
        setFullname,
        scoreLevel
      }
    }
  }
</script>

<style scoped>
</style>

2.セットアップでrefを使用する

セットアップで要素またはコンポーネントを取得するために ref を使用する方法は?

  • 実際、これは非常に単純で、ref オブジェクトを定義して、それを要素またはコンポーネントの ref 属性にバインドするだけです。

例: 

ShowInfo.vue

<template>
  <div>ShowInfo</div>
</template>

<script>
  export default {
    // methods: {
    //   showInfoFoo() {
    //     console.log("showInfo foo function")
    //   }
    // }
    setup() {
      function showInfoFoo() {
        console.log("showInfo foo function")
      }

      return {
        showInfoFoo
      }
    }
  }
</script>

<style scoped>
</style>

app.vue

<template>
  <!-- 1.获取元素 -->
  <h2 ref="titleRef">我是标题</h2>
  <button ref="btnRef">按钮</button>

  <!-- 2.获取组件实例 -->
  <show-info ref="showInfoRef"></show-info>

  <button @click="getElements">获取元素</button>
</template>

<script>
  import { ref, onMounted } from 'vue'
  import ShowInfo from './ShowInfo.vue'

  export default {
    components: {
      ShowInfo
    },
    setup() {
      const titleRef = ref()
      const btnRef = ref()
      const showInfoRef = ref()

      // mounted的生命周期函数
      onMounted(() => {
        console.log(titleRef.value)
        console.log(btnRef.value)
        console.log(showInfoRef.value)

        showInfoRef.value.showInfoFoo()
      })

      function getElements() {
        console.log(titleRef.value)
      }

      return {
        titleRef,
        btnRef,
        showInfoRef,
        getElements
      }
    }
  }
</script>

<style scoped>
</style>

3. コンポーネントのライフサイクル機能

3.1、ライフサイクルフック

  • setup を使用して、データ、メソッド、計算済みなどのオプション、およびライフ
  • では、セットアップでライフサイクル関数を使用するにはどうすればよいでしょうか?
    • ライフサイクル フックは、直接インポートされた onX 関数を使用して登録できます。

オプションのAPI

フック内部セットアップ

作成前

なし

作成した

なし

前マウント

事前マウント

マウント

オンマウント

更新前

onBeforeupdate

更新しました

onUpdated

beforeUnmount

onBeforeUnmount

マウントされていない

マウントされていない

アクティブ化

onActivated

無効化

onDeactivated

ヒント: setup はbeforeCreateおよびcreatedライフサイクル フックを中心に動作するため、明示的に定義する必要はありません。つまり、これらのフックで省略されたコードは、setup関数に直接記述する必要があります。

3.2. 例 

app.vue

<template>
  <div>AppContent</div>
</template>

<script>
  import {onMounted, onUpdated, onUnmounted} from 'vue'

  export default {
    beforeCreate() {

    },
    // created() {

    // },
    // beforeMount() {

    // },
    // mounted() {

    // },
    // beforeUpdate() {

    // },
    // updated() {

    // }
    setup() {
      // 在执行setup函数的过程中, 你需要注册别的生命周期函数
      onMounted(() => {
        console.log("onmounted")
      })
    }
  }
</script>

<style scoped>
</style>

4、Provide/Inject使用

4.1、提供機能

  • 実際、Provide と Inject についても以前に学習しており、Composition API は以前の Provide と Inject オプションを置き換えることもできます。
  • 提供を通じてデータを提供できます。
    • 各プロパティは提供メソッドによって定義できます。
  • Provide は次の 2 つのパラメーターを渡すことができます。
    • name :提供された属性名
    • value :提供された属性値

4.2. インジェクト機能 

  • 子孫コンポーネントでは、必要なプロパティと対応する値を inject で注入できます。
  • 必要なコンテンツは、inject を介して注入できます。
  • inject は次の 2 つのパラメーターを渡すことができます
    • 注入するプロパティの名前。
    • デフォルト;

4.3. データの応答性 

値の提供と値の注入の間の反応性を高めるために、値を提供するときに ref と react を使用できます。

4.4. 例 

ShowInfo.vue

<template>
  <div>ShowInfo: {
   
   { name }}-{
   
   { age }}-{
   
   { height }}</div>
</template>

<script>
  import {inject} from 'vue'

  export default {
    // inject的options api注入, 那么依然需要手动来解包
    // inject: ["name", "age"],
    setup() {
      const name = inject("name")
      const age = inject("age")
      const height = inject("height", 1.88)

      return {
        name,
        age,
        height
      }
    }
  }
</script>

<style scoped>
</style>

app.vue

<template>
  <div>AppContent: {
   
   { name }}</div>
  <button @click="name = 'kobe'">app btn</button>
  <show-info></show-info>
</template>

<script>
  import { provide, ref } from 'vue'
  import ShowInfo from './ShowInfo.vue'

  export default {
    components: {
      ShowInfo
    },
    setup() {
      const name = ref("why")

      provide("name", name)
      provide("age", 18)

      return {
        name
      }
    }
  }
</script>

<style scoped>
</style>

5、watch/watchEffect

5.1、データ変更のリッスン

  • 以前の Options API では、watch オプションを使用してdata または propsのデータ変更をリッスンし、データが変更されたときに特定の操作を実行できました。
  • コンポジション API では、watchEffect と watchを使用してレスポンシブ データをリッスンできます。
    • watchEffect : レスポンシブ データを自動的に収集するための依存関係。
    • watch : リッスンするデータ ソースを手動で指定する必要があります。

5.2、ウォッチの使用

ウォッチの API は、コンポーネントのウォッチ オプションのプロパティと完全に同等です。

  • Watch は特定のデータ ソースをリッスンし、そのコールバック関数を実行する必要があります。
  • デフォルトではレイジーです。コールバックは、リッスンされているソースが変更された場合にのみ実行されます。

5.3、複数のデータ ソースをリッスンする 

リスナーは、配列を使用して複数のソースを同時に聞くこともできます

5.4、ウォッチオプション 

ディープ リスニングをリッスンしたい場合でも、 deepを true に        設定する必要があります。immediate を渡してすぐに実行することもできます。

5.5、watchEffect 

  • レスポンシブ データの変更をリッスンするときに、いくつかの操作を実行したいので、この時点でwatchEffectを使用できます。
  • ケースを見てみましょう:
    • まず、watchEffect によって渡された関数がすぐに 1 回実行され、実行中に依存関係が収集されます。
    • 次に、収集された依存関係が変更された場合にのみ、watchEffect によって渡された関数が再度実行されます。

5.6、watchEffect のリッスンを停止する 

  • 場合によっては、リッスンを停止したい場合は、この時点でwatchEffect の戻り値関数を取得できます。この関数を呼び出すだけです。
  • たとえば、上の例では、年齢が 20 歳になると聞くのをやめます。

5.7. 例 

App-watch.vue

<template>
  <div>AppContent</div>
  <button @click="message = '你好啊,李银河!'">修改message</button>
  <button @click="info.friend.name = 'james'">修改info</button>
</template>

<script>
  import {reactive, ref, watch} from 'vue'

  export default {
    setup() {
      // 1.定义数据
      const message = ref("Hello World")
      const info = reactive({
        name: "why",
        age: 18,
        friend: {
          name: "kobe"
        }
      })

      // 2.侦听数据的变化
      watch(message, (newValue, oldValue) => {
        console.log(newValue, oldValue)
      })

      watch(info, (newValue, oldValue) => {
        console.log(newValue, oldValue)
        console.log(newValue === oldValue) // true,两者为同一个对象(浅拷贝)
      }, {
        // 这个属性作用就是,加载后默认就会执行一次这个console.log回调方法
        immediate: true
      })


      // 3.监听reactive数据变化后, 获取普通对象
      watch(() => ({...info}), (newValue, oldValue) => {
        console.log(newValue, oldValue)
      }, {
        immediate: true,
        deep: true
      })

      return {
        message,
        info
      }
    }
  }
</script>

<style scoped>
</style>

app.vue

<template>
  <div>
    <h2>当前计数: {
   
   { counter }}</h2>
    <button @click="counter++">+1</button>
    <button @click="name = 'kobe'">修改name</button>
  </div>
</template>

<script>
  import { watchEffect, watch, ref } from 'vue'

  export default {
    setup() {
      const counter = ref(0)
      const name = ref("why")

      // watch(counter, (newValue, oldValue) => {})

      // 1.watchEffect传入的函数默认会直接被执行
      // 2.在执行的过程中, 会自动的收集依赖(依赖哪些响应式的数据)
      const stopWatch = watchEffect(() => {
        console.log("-------", counter.value, name.value)

        // 判断counter.value > 10
        if (counter.value >= 10) {
          // 停止监听
          stopWatch()
        }
      })

      return {
        counter,
        name
      }
    }
  }
</script>

<style scoped>
</style>

6. スクリプト セットアップ シンタックス シュガー

6.1、スクリプト設定構文

  • <script setup> は、単一ファイル コンポーネント (SFC) で結合 API を使用するためのコンパイル時のシンタックス シュガーであり、SFC と結合 API の両方を使用する場合に推奨されます。
    • 定型文の内容が少なくなり、コードがより簡潔になります。
    • 純粋な Typescript を使用して小道具を宣言し、イベントをスローする機能。
    • 実行時のパフォーマンスが向上します。
    • IDE 型推論のパフォーマンスが向上します。
  • この構文を使用するには、setup 属性を <script> コード ブロックに追加します

  • 内部のコードは、コンポーネントの setup() 関数の内容にコンパイルされます。
    • これは、コンポーネントが最初に導入されたときに一度だけ実行される通常の <script> とは異なります。
    • <script setup> のコードは、コンポーネント インスタンスが作成されるたびに実行されます。 

6.2. トップレベルのバインディングはテンプレートに公開されます

        <script setup> を使用する場合、 <script setup> 宣言の最上位にあるすべてのバインド (変数、関数宣言、およびインポートを含む) をテンプレートで直接使用できます。

レスポンシブ データは、ref とリアクティブを介して作成する必要があります。 

6.3. インポートされたコンポーネントが直接使用される

<script setup>スコープの値は、カスタム コンポーネントのタグ名として直接使用することもできます。

6.4、defineProps() と defineEmits() 

        props および emits オプションを宣言するときに完全な型推論サポートを取得するには、defineProps および defineEmits API を使用できます。これらは <script setup> で自動的に使用可能になります。

6.5、defineExpose() 

  • <script setup> を使用するコンポーネントはデフォルトで無効になっています:
    • テンプレート ref または $parent チェーンを介して取得されたコンポーネントのパブリック インスタンスは、<script setup> で宣言されたバインディングを公開しません。
  • defineExpose コンパイラ マクロを使用して、 <script setup> コンポーネントで公開するプロパティを明示的に指定します。 

6.6. 例 

ShowInfo.vue

<template>
  <div>ShowInfo: {
   
   { name }}-{
   
   { age }}</div>
  <button @click="showInfoBtnClick">showInfoButton</button>
</template>

<script setup>

  // 定义props
  const props = defineProps({
    name: {
      type: String,
      default: "默认值"
    },
    age: {
      type: Number,
      default: 0
    }
  })

  // 绑定函数, 并且发出事件
  const emits = defineEmits(["infoBtnClick"])

  function showInfoBtnClick() {
    emits("infoBtnClick", "showInfo内部发生了点击")
  }

  // 定义foo的函数
  function foo() {
    console.log("foo function")
  }

  // 暴露实例
  defineExpose({
    foo
  })

</script>

<style scoped>
</style>

app.vue

<template>
  <div>AppContent: {
   
   { message }}</div>
  <button @click="changeMessage">修改message</button>
  <show-info name="why"
             :age="18"
             @info-btn-click="infoBtnClick"
             ref="showInfoRef">
  </show-info>
  <show-info></show-info>
  <show-info></show-info>
</template>

<script setup>
  // 1.所有编写在顶层中的代码, 都是默认暴露给template可以使用
  import {ref, onMounted} from 'vue'

  // 组件不在需要注册,直接导入使用即可
  import ShowInfo from './ShowInfo.vue'

  // 2.定义响应式数据
  const message = ref("Hello World")
  console.log(message.value)

  // 3.定义绑定的函数
  function changeMessage() {
    message.value = "你好啊, 李银河!"
  }

  function infoBtnClick(payload) {
    console.log("监听到showInfo内部的点击:", payload)
  }

  // 4.获取组件实例
  const showInfoRef = ref()
  onMounted(() => {
    showInfoRef.value.foo()
  })

</script>

<style scoped>
</style>

7.カスタムフックの練習

useCounter.js

import { ref, onMounted } from 'vue'

export default function useCounter() {
  const counter = ref(0)
  function increment() {
    counter.value++
  }
  function decrement() {
    counter.value--
  }
  onMounted(() => {
    setTimeout(() => {
      counter.value = 989
    }, 1000);
  })

  return {
    counter,
    increment,
    decrement
  }
}

useScrollPosition.js

import { reactive } from 'vue'

export default function useScrollPosition() {
  // 1.使用reative记录位置
  const scrollPosition = reactive({
    x: 0,
    y: 0
  })

  // 2.监听滚动
  document.addEventListener("scroll", () => {
    scrollPosition.x = window.scrollX
    scrollPosition.y = window.scrollY
  })


  return {
    scrollPosition
  }
}

useTitle.js

import { ref, watch } from "vue";

export default function useTitle(titleValue) {
  // document.title = title

  // 定义ref的引入数据
  const title = ref(titleValue)

  // 监听title的改变
  watch(title, (newValue) => {
    document.title = newValue
  }, {
    immediate: true
  })

  // 返回ref值
  return {
    title
  }
}

について.vue

<template>
  <h2>About计数: {
   
   { counter }}</h2>
  <button @click="increment">+1</button>
  <button @clcik="decrement">-1</button>
</template>

<script>
  import { onActivated } from 'vue'
  import useCounter from '../hooks/useCounter'
  import useTitle from '../hooks/useTitle'

  export default {
    setup() {

      // 切换标题
      useTitle("关于")

      return {
        ...useCounter()
      }
    }
  }
</script>

<style scoped>
</style>

Home.vue

<template>
  <h2>Home计数: {
   
   { counter }}</h2>
  <button @click="increment">+1</button>
  <button @click="decrement">-1</button>

  <button @click="popularClick">首页-流行</button>
  <button @click="hotClick">首页-热门</button>
  <button @click="songClick">首页-歌单</button>

  <div class="scroll">
    <h2>x: {
   
   { scrollPosition.x }}</h2>
    <h2>y: {
   
   { scrollPosition.y }}</h2>
  </div>
</template>

<script>
  import { onMounted, ref } from 'vue'
  import useCounter from '../hooks/useCounter'
  import useTitle from '../hooks/useTitle'
  import useScrollPosition from '../hooks/useScrollPosition'

  export default {
    setup() {
      // 1.counter逻辑
      const { counter, increment, decrement } = useCounter()

      // 2.修改标题
      const { title } = useTitle("首页")

      // 3.监听按钮的点击
      function popularClick() {
        title.value = "首页-流行"
      }
      function hotClick() {
        title.value = "首页-热门"
      }
      function songClick() {
        title.value = "首页-歌单"
      }

      // 4.获取滚动位置
      const { scrollPosition } = useScrollPosition()
      console.log(scrollPosition)

      return {
        counter,
        increment,
        decrement,
        popularClick,
        hotClick,
        songClick,
        scrollPosition
      }
    }
  }
</script>

<style scoped>
</style>

app.vue

<template>
  <div>AppContent</div>
  <button @click="changeTitle">修改title</button>

  <!-- 1.计数器 -->
  <!-- <hr>
  <home></home>
  <hr>
  <about></about> -->

  <!-- 2.home和about页面的切换 -->
  <button @click="currentPage = 'home'">home</button>
  <button @click="currentPage = 'about'">about</button>

  <component :is="currentPage"></component>

  <div class="content"></div>

  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
  <br><br><br><br><br><br>
</template>

<script>
  import { ref } from 'vue'
  import Home from './views/Home.vue'
  import About from './views/About.vue'

  import useTitle from './hooks/useTitle'

  export default {
    components: {
      Home,
      About
    },
    setup() {
      const currentPage = ref("home")

      function changeTitle() {
        useTitle("app title")
      }

      return {
        changeTitle,
        currentPage
      }
    }
  }
</script>

<style scoped>
  .content {
    width: 3000px;
    height: 100px;
    background-color: orange;
  }
</style>

注: このケースは、セットアップで他の機能を使用する方法を示しているだけです。 

おすすめ

転載: blog.csdn.net/weixin_52851967/article/details/128749380