2022年9月29日 星期四

Vue3 with composition api 學習心得

import 相關物件

import { ref, readonly, computed, watch, watchEffect, onMounted, provide} from 'vue';
import * as common from "@/utils/common"; // @ = clientapp/src


基本用法

setup() {
  const itemRefs = []; // 定義內部使用物件
  const orders = ref([]); // 定義HTML可使用物件
  const obj = readonly(ref({value:0,text:'123'})); // 定義唯讀物件
  const no = computed(() => `$No.{obj.value}`); // 定義計算物件
  const f1= (p1, p2) => { // 定義函數
    orders.value=xxx; // 設定物件值
  }
  onMounted(() => { // html rendered
  ...
  })
  ... // 預先執行程式
  return {orders, f1} // 回傳HTML可存取的物件和函數
}

等待 dom 更新完畢

import { nextTick } from 'vue'
用法1:
const xxx = async () => {
  await nextTick(); // 等待 dom 更新完畢
  ...
}
用法2:
const xxx = () => {
  nextTick(() => ...);  // 設定 dom 更新完畢後要執行的程式
}

使用 props 和 emits

props: ['id'], 
emits: ['done'],
setup(props, context) {
  console.log(props.id) // 取得 props
  context.emit('done'); // 回拋事件

若要修改 props 物件內容,需要另外建立參考物件
const objref = ref(props.obj)
objref.name='123'

使用 provide 和 inject

上層元件(例如 app.vue)
provide("userName", userName) // 設定子元件可讀取寫入的物件
provide('book', readonly(book)) // 設定子元件可讀取的物件
provide("hasRight", { hasRight }) // 設定子元件可呼叫的函數

子元件
const userName = inject("userName"); // 注入上層元件的物件
const { hasRight } = inject("hasRight"); // 注入上層元件的函數


設定watch

watch(id, (newValue, oldValue) => { // 監聽變數
});
watch(()=>obj.id, (newValue, oldValue) => { // 監聽物件屬性
});
watch(()=>obj, (newValue, oldValue) => { // 監聽物件所有屬性
},{deep:true});
watchEffect(() => { // 自動監控使用到的變數
   fetchData(); // 只要 props.id 有變化就會自動呼叫 fetchData(),但若 props.id 包在 setTimeout 裡面則不會自動監控
})
const fetchData = () => {
if (props.id)
axios.get(...)
.then(response => {
...
});
}


v-for 搭配 ref

<div v-for="order in orders" :key="order.id" :ref="setItemRef">
let itemRefs = [];
const setItemRef = el => {
if (el) {
itemRefs.push(el) // 將 v-for 每次產生出來的元件放入陣列
}
if (itemRefs.length == orders.value.length) { // 當元件全部產生後執行
const index = orders.value.findIndex(a => a.id == props.id); // 取得特定元件
itemRefs[index].scrollIntoView(); // 讓特定元件顯示在最上面
}
}
onBeforeUpdate(() => {
                itemRefs = []
            })

避免 import .vue 出現 typescript 編譯錯誤

修改 src/shims-vue.d.ts
declare module '*.vue' {
    import Vue from 'vue'
    export default Vue
}

異步載入元件

import { defineAsyncComponent } from 'vue';
export default {
components: {
xxx: defineAsyncComponent(() =>
import('./xxx.vue')
),
},

DOM出現於視野內時觸發執行動作

import { useIntersectionObserver } from '@vueuse/core'
export default {
        setup(props) {
            const targetEl = ref();
const { stop } = useIntersectionObserver(
                targetEl,
                ([{ isIntersecting }]) => {
                    if (isIntersecting) {                        
                        ...
                        stop(); // 避免重複觸發
                    }
                }
            );
    return { targetEl }
        },
    };
<template>
    <div ref="targetEl"></div>
</template>

強制更新元件內容

例如屬性值是動態判斷且會參考 props,但 props 改變時並不會更新屬性值
設定元件的 key 並更新其值會強制更新元件內容
html
====
<WaitSchedule :key="updatekey"></WaitSchedule>
javascript
====
import WaitSchedule from './xxx.vue';
    export default {
        components: {
            WaitSchedule
        },
        setup() {
            const updatekey=ref(1)
            watch(() => xxx, () => {
                updatekey.value++;
            });


設定日期可選最大值為今天

<input class="form-control" type="date" :max="(new Date()).toISOString().split('T')[0]">

import self

須設定元件名稱否則會跳警告且無法正確載入第二個自己
export default {
    name: 'name1',

動態css

綁定 css 的值為某個屬性,以達到動態設定目的
<style>
    .cv-week {
        min-height: v-bind(weekheight);
    }
</style>
 export default {
     setup() {
        const maxevents=ref()
        const weekheight = computed(() => `${1 + maxevents.value * 2}em`)
        return {weekheight }

※單檔元件才有作用 (javascript 寫在同一個 .vue)


※vue3 v-on:change & v-on:input 會抓到舊值,寫法可改為 @change="xxx($event)",$event 放的是新值
※作為 component 必須有唯一的root element否則會有異常狀況


沒有留言:

input 連結 datalist 用程式控制彈出選項

範例: nextTick(() => document.querySelector('input').showPicker());  ※僅支援現代瀏覽器