プロジェクトを行う際に必ず読んでおきたいVue3の基礎知識

1. レスポンシブ対応

1.1 2 つの実装原則

  • vue2 は es5 を使用してObject.defineProperty()データをハイジャックし、パブリッシュおよびサブスクライブ モデルと組み合わせて、次のことを実現します。
  • Vue3 は、es6 のproxyデータ プロキシを使用して、reactive() 関数を通じて各オブジェクトのプロキシ層をラップし、プロキシを通じて属性の変更を監視してデータを監視します。

1.2 vue2 応答性の欠陥

欠陥

对象新增、删除属性没有响应式数组新增、删除元素没有响应式;通过下标修改某个元素没有响应式;通过.length改变数组长度没有响应式インスタンス作成時のデータ内のデータインスタンスのみが応答し、作成したvueインスタンスのデータオブジェクトに属性を追加すると、データは更新されますが、ビューは更新されず、応答しません。

解決する

  • ビューとデータを強制的に更新するために使用しますthis.$forceUpdate()(非推奨)
  • リアクティブ関数を使用してオブジェクトを操作します。
[Vue | this].$set(object,key,value),实例中添加响应式属性;
[Vue | this].$delete(object,key),实例中删除属性;
Object.assign(),将多个对象属性合并到目标对象中,具有响应式;
Object.freeze(),将对象冻结,防止任何改变。使得对象成为只读,无法添加、删除或更新;
Object.keys(),返回对象的所有属性;
Object.values(),返回对象的所有属性值;
Object.entries(),返回对象的所有键值对;
  • リアクティブ関数を使用して配列を操作します。
pop(),尾部删除元素;
push(),尾部添加元素;
unshift(),首部添加元素;
shift(),首部删除元素;
sort(),排序;
reverse(),翻转;
splice(index[必填,位置],howmany[必填,要删除的数量],item1...itemx[可选,向数组中添加的新元素])

【補足】オブジェクトのクリアと空配列の操作

  • クリアオブジェクト
this.form = {
    
    }
this.$refs.form.resetFields()
this.form.name = ""
  • 空の配列
this.arrayList = [] 
this.arrayList.splice(0,this.arrayList.length)
// this.arrayList.length = 0  不具有响应式,无法实现

1.3 vue3 のレスポンシブな利点

  • proxy性能整体上优于Object.defineProperty
  • vue3支持更多数据类型的劫持(vue2 は Object と Array のみをサポートします。vue3 は Object、Array、Map、WeakMap、Set、WeakSet をサポートします)
  • vue3支持更多时机来进行依赖收集和触发通知(vue2 は get 時に依存関係のみを収集し、vue3 は get/has/iterate 時に依存関係を収集します。vue2 は set 時にのみ通知をトリガーし、vue3 は set/add/delete/clear 時に通知をトリガーします)、所以vue2中的响应式缺陷vue3可以实现
  • vue3做到了“精准数据”的数据劫持(vue2 はデータ全体に対して再帰的なデータ ハイジャックを実行しますが、vue3 は特定のオブジェクトが使用されている場合にのみデータ ハイジャックを実行するため、応答性が高く、使用するメモリが少なくなります)
  • vue3的依赖收集器更容易维护(vue3 はネイティブ配列を監視および操作します。vue2 は書き換えメソッドを通じて配列を監視します)

2. ライフサイクル

ビュー2 ビュー3 説明する
作成前 設定() コンポーネントを作成する前に、初期化タスクを実行します
作成した 設定() コンポーネントの作成が完了し、データにアクセスしてインターフェースデータを取得します
マウント前 onBeforeMount 部品実装前
取り付けられた マウント済み コンポーネントのマウントが完了し、DOM が作成されました。データまたは DOM 要素にアクセスし、サブコンポーネントにアクセスします。
更新前 onBeforeUpdate 更新されていません。更新前にすべてのステータスを取得します
更新しました 更新済み 更新済み、すべての更新ステータスを取得
前に破壊する onBeforeUnmount コンポーネントが破棄される前に、タイマーをクリアしてメッセージの購読を解除してください。
破壊されました アンマウント済み コンポーネントが破壊された後
アクティブ化された onActivated keep-alive にはコンポーネントがアクティブ化されたときに含まれます
無効化された 非アクティブ化済み keep-alive には、コンポーネントの切り替えが発生し、コンポーネントが消失する場合が含まれます。

2.1 初期化

  • vue2 は通常、作成およびマウントされたときに初期化されます。
  • vue3 はセットアップで直接初期化することも、onBeforeMount または onMounted で初期化することもできます
<script setup>
const getList = () => {
    
    }

getList()

onMounted(() => {
    
    
  getList()
}),

onBeforeMount(() => {
    
    
  getList()
}),
</script>

2.2 バインドの解除

  • vue2での操作:
<script>
export default {
    
    
  mounted() {
    
    
    // 开启定时器
    let timer = setInterval(() => {
    
    
      console.log('---定时器在触发---')
    }, 1000)
   
   //这下面的代码不可以省略
    this.$on('hook:activated', () => {
    
    
      if (timer === null) {
    
     // 避免重复开启定时器
        timer = setInterval(() => {
    
    
          console.log('setInterval')
        }, 1000)
      }
    })

    this.$on('hook:deactivated', () => {
    
    
      clearInterval(timer)
      timer = null
    })
  }
}
<script>
  • vue3での操作:
<script setup>
import {
    
     onBeforeUnmount, onDeactivated } from 'vue'

// 组件卸载前,对应 Vue2 的 beforeDestroy
onBeforeUnmount(() => {
    
    
    clearTimeout(timer)
    window.removeAddEventListener('...')
})

// 退出缓存组件,对应 Vue2 的 deactivated
onDeactivated(() => {
    
    
    clearTimeout(timer)
    window.removeAddEventListener('...')
})
</script>

3.this指向

  • vue2ではこれを呼び出して現在のインスタンスを指すことができ、ルーティング、状態管理、パブリックコンポーネント、メソッドなどがこれにマウントされ、アクセスして使用することができます。
  • 上記のライフサイクルからわかるように、vue3 はsetup()他のコンポーネント オプションを解析する前に呼び出され (データ、メソッド、計算などは解析されません)、beforeCreate() の前に実行されるため、これは未定義を指し、これを通じてアクセスすることはできませんvue3で
  • vue3 がこれを呼び出す vue2 と同様の使用法を実行したい場合は、次のようにすることができます。
<script setup>
import {
    
     getCurrentInstance } from "vue";

// proxy 为当前组件实例;global 为全局组件实例
const {
    
     proxy, appContext } = getCurrentInstance();
const global = appContext.config.globalProperties;
</script>

4.変数

4.1 参照

  • ref は基本类型生成されたRefImplインスタンスを定義します;复合类型生成されたProxyインスタンスを定義します
  • テンプレートはレンダリングに直接使用され、.value呼び出してjs で変更されます。
const count = ref(0)
const user = ref({
    
    
    name:'falcon',
    age:20
})

const addCount = () => count.value++
const addAge = () => user.value.age++

4.2 リアクティブ

  • reactive はオブジェクト型のデータを定義し、Proxyインスタンスを生成することしかできません。
  • テンプレートおよびjsで直接呼び出すことができます
  • shallowReactive非再帰的な応答データを生成し、データの最初の層の変更のみを監視します
const stu = reactive({
    
    
    name:'falcon',
    major:'Chinese',
    score:80
})

const addScore = () => stu.score++

4.3 変換の応答性

  • toRef()、シングルがレスポンシブに変換されました
  • toRefs()、複数をレスポンシブに変換します
  • unref()、はval = isRef(val) ? val.value : valの糖衣構文です。パラメータが ref の場合はその値を返し、それ以外の場合はパラメータ自体を返します。

[注意]応答性の高いオブジェクトの prop (プロパティ) の ref を作成し (リアクティブ カプセル化)、応答性を維持します。

const stu = reactive({
    
    
    name:'falcon',
    age:20,
    major:'Chinese',
    score:80
})
const age = toRef(stu,'age')
const {
    
    name,major,score} = toRefs(stu)

4.4 読み取り専用

  • readonly、読み取り専用オブジェクトを作成します (再帰読み取り専用)
  • isReadonly、読み取り専用オブジェクトかどうかを判断します。
  • shallowReadonly、最も外側の応答レイヤーに対してのみ読み取り専用であり、より深いレイヤーでは変換は実行されません。
let status = readonly(true);
const changeStatus = () => (status = !status);

let info = reactive({
    
    
  username: "falcon",
  password: "123456",
  role: {
    
    
    roleId: 123,
    roleName: "系统管理员",
  },
});
info = shallowReadonly(info);
const changeRole = () => {
    
    
  info.role.roleId++;
};

5.フラグメント

  • vue2 では只能有一个根节点、 vdom は単一ルート ツリーであるため、patch メソッドはトラバース時にルート ノードから開始されるため、テンプレートにはルート要素が 1 つだけ必要です。
  • vue3 では可以有多个根节点、テンプレートに複数のルート要素がある場合、複数のルート コンポーネントをラップするフラグメント コンポーネントが追加されるためです。
<template>
  <div>demo1 text</div>
  <h2>h2 text</h2>
  <p>p text</p>
</template>

6.テレポート

  • teleport テレポート コンポーネントは、DOM 内の vue アプリの外側の他の場所に要素を移動できます (ページにポップアップ フレームが必要で、ポップアップ フレームがレイア​​ウトに影響せず、本文に対して相対的に配置されている場合に使用される場合があります)。
<template>
  <div class="app-container">
    <el-button type="primary" @click="showToast">打开弹框</el-button>
  </div>
  <teleport to="body">
    <div v-if="visible" class="modal_class">
      A man who has not climbed the granted wall is not a true man
      <el-button
        style="width: 50%; margin-top: 20px"
        type="primary"
        @click="closeToast"
        >关闭弹框</el-button
      >
    </div>
  </teleport>
</template>

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

const visible = ref(false);
const showToast = () => {
    
    
  visible.value = true;
};
const closeToast = () => {
    
    
  visible.value = false;
};
</script>

<style scoped>
.modal_class {
    
    
  position: absolute;
  width: 300px;
  height: 200px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border: 1px solid #ccc;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-content: center;
  padding: 30px;
}
</style>

7.サスペンス

  • サスペンスにより、プログラムは非同期コンポーネントを待機している間にフォールバック コンテンツを読み込むことができます。
  • ネットワーク データを非同期にロードして要求する場合、サスペンド コンポーネントを使用すると、良好なロード効果が得られます。
  • #defaultテンプレートコンポーネントを初期化します。UI#fallbackは非同期リクエストで処理されます。
<template>
  <div class="app-container">
    <Suspense>
      <template #default>
        <SyncApi />
      </template>
      <template #fallback>
        <h3 style="color: blue">数据加载中...</h3>
      </template>
    </Suspense>
  </div>
</template>

<script setup>
import SyncApi from "./SyncApi.vue";
</script>
// SyncApi 组件内容
<template>
  <div v-for="(people, index) in peoples.results" :key="index">
    {
   
   { people.name }} {
   
   { people.birth_year }}
  </div>
</template>

<script setup>
const peoples = ref({
      
      
  results: [],
});
const headers = {
      
       "Content-Type": "application/json" };
const fetchPeoples = await fetch("https://swapi.dev/api/people", {
      
      
  headers,
});
peoples.value = await fetchPeoples.json();
</script>

8.コンポーネント

  • vue2 にコンポーネントを導入したら、使用する前にコンポーネントに登録する必要があります。
  • vue3 にコンポーネントを導入すると、登録なしで直接使用できます。
<template>
  <Table />
</template>

<script setup>
import Table from "@/components/Table";
</script>

9. DOM を取得する

<template>
  <el-form ref="formRef"></el-form>
</template>

<script setup>
// 1. 变量名和 DOM 上的 ref 属性必须同名,自动形成绑定
const formRef = ref(null)
console.log(formRef.value)

// 2. 通过当前组件实例来获取DOM元素
const {
      
       proxy } = getCurrentInstance()
proxy.$refs.formRef.validate((valid) => {
      
       ... })
</script>

10.watch、watchEffect

10.1 時計

// vue2 中用法
watch:{
    
    
    // 第一种
    flag(newValue,oldValue){
    
    },
    
    // 第二种
    user:{
    
    
        handler(newValue,oldValue){
    
    },
        immediate:true,
        deep:true
    }
}
// vue3 中用法
<script setup>
const count = ref(0)
const status = ref(false)

// 监听一个
watch(count,(newValue,oldValue) => {
    
    })
// 监听多个
watch([count,status],([newCount,oldCount],[newStatus,oldStatus]) => {
    
    })

const user = reactive({
    
    
    name:'falcon',
    age:20,
    sex:'female',
    hobbies:[]
})

// 监听一个
watch(() => user.age,(newValue,oldValue) => {
    
    })
// 监听多个
watch([() => user.name,() => user.sex],(newValue,oldValue) => {
    
    })

// 添加配置参数
watch(() => user.hobbies,(newValue,oldValue)=> {
    
    },{
    
    
    immediate:true,
    deep:true,
    // 回调函数的执行时机,默认在组件更新之前执行,更新之后执行参数为‘post’
    flush:'pre'
})
</script>

10.2 ウォッチエフェクト

// 正常情况组件销毁自动停止监听
watchEffect(() => {
    
    })

// 异步方式手动停止监听
const stopWatch = watch(() => user.hobbies,(newValue,oldValue)=>{
    
    },{
    
    deep:true})
setTimeout(() => {
    
    
    stopWatch()
},3000)

const stopWatchEffect = watchEffect(() => {
    
    })
setTimeout(() => {
    
    
    stopWatchEffect()
},3000)

10.3 両者の違い

  • 監視对传入的一个或多个值进行监听、トリガーされたときに発生します返回新值和旧值、そして默认第一次不会执行
  • watchEffect は、すぐに実行される関数を渡し、默认第一次会执行,且不需要传入监听的内容,会自动收集函数内的数据源作为依赖依存関係が変更されると、関数は (計算と同様に) 再実行されます。不会返回旧值
  • 通常の状況では、両方のメソッドはコンポーネントが破棄/アンインストールされた後に待機を停止しますが、异步方式たとえば、setTimeout で作成された待機は手動で停止する必要があります。

11.計算された

  • デフォルトではデータの値のみが変更されるため、値を変更したい場合は具有响应性そのset()メソッドを使用してください。
  • vue3.x ではフィルターが削除されているため、計算されたものを使用することをお勧めします。
  • 回调函数必须return、結果は計算結果です
  • 計算されたプロパティに依存数据项发生变化时,重新计算,具有缓存性
  • 不能执行异步操作
const names = reactive({
    
    
    firstName:'',
    lastName:'',
    fullName:''
})

// 通过此种方式定义的fullName,想要修改的时候后台警告:Write operation failed: computed value is readonly;想要修改fullName,通过set()方法
const fullName = computed(() => {
    
    
  return names.firstName + " " + names.lastName;
});

const fullName = computed({
    
    
    get(){
    
    
        return names.firstName + " " + names.lastName
    },
    set(value){
    
    
        
    }
})

12. コンポーザブル関数

  • vue3 の結合 API 使用hooks代替 vue2 中的mixinフックはキャメルケースの命名規則を使用し、「use」で始まります。再利用可能な関数を外部 js ファイルに抽出します。定義されたプロパティとメソッドを参照時に応答的に分解して公開します。vue2 の断片化を回避し、低凝集性と高結合を実現します。
  • 従来のミックスインの欠点:
    • ミックスインは名前の競合やコードの重複を引き起こす可能性があり、後のメンテナンスが困難になります。
    • ミックスインによりコンポーネント間の依存関係が不明確になり、ソースの追跡が困難になる場合があります。
  • mixin ライフサイクル関数: 先执行mixin中生命周期函数;后执行组件内部代码mixin中的data数据和组件中的data数据冲突时,组件中的data数据会覆盖mixin中数据
// useCount.js 
const useCount = (initValue = 1) => {
    
    
    const count = ref(initValue)
    
    const increase = (delta) => {
    
    
        if(typeof delta !== 'undefined'){
    
    
            count.value += delta
        }else{
    
    
            count.value++
        }
    }
    
    const multiple = computed(() => count.value * 2)
    
    const decrease = (delta) => {
    
    
        if(typeof delta !== 'undefined'){
    
    
            count.value -= delta
        }else{
    
    
            count.value--
        }
    }
    
    const reset = () => count.value = initValue 
    
    return {
    
    
        count,
        multiple,
        increase,
        decrease,
        reset
    }
}

export default useCount
<template>
    <p>{
   
   {count}}</p>
    <p>{
   
   {multiple}}</p>
    <el-button @click="addCount">count++</el-button>
    <el-button @click="subCount">count--</el-button>
    <el-button @click="resetCount">reset</el-button>
</template>

<script setup>
import useCount from "@/hooks/useCount"
const {
      
      count,multiple,increase,decrease,reset} = useCount(10)
const addCount = () => increase()
const subCount = () => decrease()
const resetCount = () => reset()
</script>

13. コンポーネントの遅延ロード

// Demo.vue
<template>
    <div>异步加载组件的内容</div>
</template>
// ErrorComponent.vue
<template>
    <div>Warning:组件加载异常</div>
</template>
// LoadingComponent.vue
<template>
    <div>组件正在加载...<div>
</template>
<template>
    <AsyncDemo />
</template>

<script setup>
import LoadingComponent from './LoadingComponent.vue'
import ErrorComponent from './ErrorComponent.vue'

const time = (t,callback = () => {
      
      }) => {
      
      
    return new Promise((resolve) => {
      
      
        setTimeout(() => {
      
      
            callback()
            resolve()
        },t)
    })
}

const AsyncDemo = defineAsyncComponent({
      
      
    // 要加载的组件
    loader:() => {
      
      
        return new Promise((resolve) => {
      
      
            async function(){
      
      
                await time(3000)
                const res = await import("./Demo.vue")
                resolve(res)
            }
        })
    },
    // 加载异步组件时使用的组件
    loadingComponent:LoadingComponent,
    // 加载失败时使用的组件
    errorComponent:ErrorComponent,
    // 加载延迟(在显示loadingComponent之前的延迟),默认200
    delay:0,
    // 超时显示组件错误,默认永不超时
    timeout:5000
})
</script>

14.スロット

  • 名前付きスロットはさまざまな方法で使用されます: vue2 で使用されるものslot='插槽名称'と vue3 で使用されるものv-slot:插槽名称
  • スコープ スロットはさまざまな方法で使用されます。vue2 では、子コンポーネントからデータを取得するために親コンポーネントで使用されslot-scope="data"、vue3 では、親コンポーネント#dataまたは#default="{data}"データを取得するために使用されます。
<template>
    <div>
        <!-- 默认 -->
        <slot />
        <!-- 具名 -->
        <slot name="slotName" />
        <!-- 作用域 -->
        <slot :data="user" name="propsSlot" />
    </div>
</template>

<script>
const user = reactive({
      
      
    name:'falcon',
    age:20
})
</script>
<template>
    <Son>
        <template #default><div>默认插槽内容</div></template>
        <template #slotName><div>具名插槽内容</div></template>
        <template #propsSlot="scope">
            <div>
                作用域插槽内容:name,{
   
   {scope.data.name}};age,{
   
   {scope.data.age}}
            </div>
        </template>
    </Son>
</template>

<script setup>
import Son from './Son.vue'
<script>

15. カスタム指示

  • グローバル カスタム ディレクティブは main.js で定義されます
  • ローカルカスタムディレクティブは現在のコンポーネントで定義されています
// main.js
app.directive("focus",{
    
    
    mounted(el,bingings,vnode,preVnode){
    
    
        el.focus()
    }
})
<template>
    <div>
        <input type="text" v-focus />
    </div>
</template>

<script setup>
const vFocus = {
      
      
    mounted:(el) => el.focus()
}
</script>

16.v-モデル

  • Vue2.syncとZhongv-modelはどちらもシンタックス シュガーであり、親コンポーネントと子コンポーネントでのデータの双方向通信を実現できます。
  • vue2 の 2 つの形式の違い: v-model="num", :num.sync="num";v-model:@input+value,:num.sync:@update:num
  • vue2でv-model只能用一次,.sync可以有多个
  • .sync は vue3 でキャンセルされ、v-model にマージされました。vue3中v-model可以有多个
<template>
    <p>name:{
   
   {name}}</p>
    <p>age:{
   
   {age}}</p>
    <Son v-model:name="name" v-model:age="age" />
</template>

<script setup>
import Son from './Son.vue'

const user = reactive({
      
      
    name:'falcon',
    age:20
})

const {
      
      name,age} = toRefs(user)
</script>
<template>
    <input type="text" :value="name" @input="onNameInput" />
    <input type="number" :value="age" @change="onAgeInput" />
</template>

<script setup>
defineProps({
      
      
    name:{
      
      
        type:String,
        default:() => ""
    },
    age:{
      
      
        type:String,
        default:() => ""
    }
})

const emit = defineEmits(["update:name"],["update:age"])

const onNameInput = (e) => emit("update:name",e.target.value)
const onAgeInput = (e) => emit("update:age",e.target.value)
</script>

17.v-if/v-for

  • 不建议 v-for 与 v-if 一起使用
  • vue2 中优先级v-for 高于 v-ifリスト項目をフィルタリングする操作が実行される場合は、computed と連携し、条件が循環リストの表示または非表示と判断される場合は、v-if を進めて v-for をラップします。
  • vue3 中优先级v-if 高于 v-for
<template>
    <div v-if="flag">
        <div v-for="item in dataList" :key="item.id">{
   
   {item.id}} - {
   
   {item.label}}</div>
    </div>
</template>

<script setup>
const flag = ref(true)
const dataList = reactive([
    {
      
      
        id:1,
        label:'list-01'
    },
    {
      
      
        id:2,
        label:'list-02'
    }
])
</script>

18.v-バインド

  • vue2 单独声明优先、および繰り返し定義すると警告が発行されます。
  • vue3 のバインディング値が有効になるかどうかは次のとおりです。就近原则
<template>
    <div>
        <input type="text" v-bind:disabled="false" :disabled="disabled" />
        <input type="text" :disabled="disabled" v-bind:disabled="false" />
    </div>
</template>

<script setup>
const disabled = ref(true)
</script>

19. コンポーネント通信

19.1 プロップ/$emit

親コンポーネントは値を渡し、子コンポーネントは props を通じて値を受け取ります。子コンポーネントが親コンポーネントの値を変更したい場合は、$emit を通じて親コンポーネントのメソッドを呼び出します。

  • 親コンポーネント
<template>
  <Child :count="count" :name="name" :age="age" @add="add" @sub="sub" />
</template>

<script setup>
import Child from "./Child.vue";

const count = ref(0);
const user = reactive({
      
      
  name: "falcon",
  age: 20,
});

const add = () => count.value++;
const sub = () => count.value--;

const {
      
       name, age } = toRefs(user);
</script>
  • サブアセンブリ
<template>
  <p>接受到的参数为:name,{
   
   { name }},age,{
   
   { age }},count,{
   
   { count }}</p>
  <el-button type="primary" size="small" @click="add">count++</el-button>
  <el-button type="primary" size="small" @click="sub">count--</el-button>
</template>

<script setup>
defineProps({
      
      
  name: {
      
      
    type: String,
    default: () => "",
  },
  age: {
      
      
    type: Number,
    default: () => 0,
  },
  count: {
      
      
    type: Number,
    default: () => 0,
  },
});

const emits = defineEmits(["add", "sub"]);

const add = () => emits("add");
const sub = () => emits("sub");
</script>

19.2属性

サブコンポーネントにプロパティまたはメソッドを渡す サブコンポーネントの props で定義されていないプロパティを渡す サブコンポーネントの ems で定義されないメソッドを渡す

  • 親コンポーネント
<template>
  <Child :count="count" :name="name" :age="age" @add="add" @sub="sub" />
</template>

<script setup>
import Child from "./Child.vue";

const count = ref(0);
const user = reactive({
      
      
  name: "falcon",
  age: 20,
});

const add = () => count.value++;
const sub = () => count.value--;

const {
      
       name, age } = toRefs(user);
</script>
  • サブアセンブリ
<template>
  <p>子组件接收:{
   
   { count }}</p>
  <el-button type="primary" size="small" @click="add">count++</el-button>
  <GrandChild v-bind="$attrs" />
</template>

<script setup>
import GrandChild from "./GrandChild.vue";

defineProps({
      
      
  count: {
      
      
    type: Number,
    default: () => 0,
  },
});

const emits = defineEmits(["add"]);

const add = () => emits("add");
</script>
  • 太陽成分
<template>
  <p>孙组件接受:name,{
   
   { name }},age,{
   
   { age }}</p>
  <el-button type="primary" size="small" @click="sub">count--</el-button>
</template>

<script setup>
defineProps({
      
      
  name: {
      
      
    type: String,
    default: () => "",
  },
  age: {
      
      
    type: Number,
    default: () => 0,
  },
});

const emits = defineEmits(["sub"]);

const sub = () => emits("sub");
</script>

19.3vモデル

  • 親コンポーネント
<template>
  <Child v-model:name="name" v-model:count="count" v-model:salary="salary" />
</template>

<script setup>
import Child from "./Child.vue";

const name = ref("falcon");
const count = ref(0);
const salary = ref(3000);
</script>
  • サブアセンブリ
<template>
  <p>
    子组件接受到的v-model参数:name,{
   
   { name }},count,{
   
   { count }},salary,{
   
   {
      salary
    }}
  </p>
  <el-button type="primary" size="small" @click="changeCount"
    >count++</el-button
  >
  <el-button type="primary" size="small" @click="changeSalary"
    >salary1000+</el-button
  >
</template>

<script setup>
const props = defineProps({
      
      
  name: {
      
      
    type: String,
    default: () => "",
  },
  count: {
      
      
    type: Number,
    default: () => "",
  },
  salary: {
      
      
    type: Number,
    default: () => "",
  },
});

const emits = defineEmits(["update:count", "update:salary"]);
const changeCount = () => emits("update:count", props.count + 1);
const changeSalary = () => emits("update:salary", props.salary + 1000);
</script>

19.4 参照/公開

指定したDOM要素やコンポーネントをrefで取得し、defineExposeで公開するプロパティやメソッドと組み合わせて通信を実装します。

  • 親コンポーネント
<template>
  <div>title:{
   
   { title }}</div>
  <Child ref="child" />
  <el-button type="primary" size="small" @click="add">count++</el-button>
  <el-button type="primary" size="small" @click="sub">count--</el-button>
  <el-button type="primary" size="small" @click="receive"
    >receive msg</el-button
  >
</template>

<script setup>
import Child from "./Child.vue";

const child = ref(null);
const title = ref("暂无数据");
const add = () => child.value.add();
const sub = () => child.value.sub();
const receive = () => (title.value = child.value.msg);
</script>
  • サブアセンブリ
<template>
  <p>子组件:count,{
   
   { count }}</p>
</template>

<script setup>
const count = ref(0);
const msg = "expose message";
const add = () => count.value++;
const sub = () => count.value--;

defineExpose({
      
      
  msg,
  add,
  sub,
});
</script>

19.5 提供/注入

祖先は、階層がどれほど深くても、パラメータを部下に渡すことができます。

  • 親コンポーネント
<template>
  <Child />
</template>

<script setup>
import Child from "./Child.vue";

const user = reactive({
      
      
  name: "falcon",
  age: 20,
});
provide("user", user);
</script>
  • サブアセンブリ
<template>
  <p>子组件接受:name,{
   
   { user.name }}</p>
  <GrandChild />
</template>

<script setup>
import GrandChild from "./GrandChild.vue";

const user = inject("user");
</script>
  • 太陽成分
<template>
  <p>孙组件接受:age,{
   
   { user.age }}</p>
</template>

<script setup>
const user = inject("user");
</script>

19.6 ミックスイン

推奨されません。コンポーネント間の通信と再利用を完了するには、コンポーズ可能な関数を使用することをお勧めします。

  • mixin.js
const count = ref(20);
const name = ref("falcon");

const addCount = () => count.value++;
const subCount = () => count.value--;

export default {
    
     count, name, addCount, subCount };
  • コンポーネントの使用法
<template>
  <p>name:{
   
   { name }},count:{
   
   { count }}</p>
  <el-button @click="addCount" type="primary" size="small">count++</el-button>
  <el-button @click="subCount" type="primary" size="small">count--</el-button>
</template>

<script setup>
import mixins from "./mixin";
const {
      
       count, name, addCount, subCount } = mixins;
</script>

19.7ミット

vue3 では API が廃止されました: $on, $once, $off; イベント バスはサポートされなくなり、代替の mitt.js が選択されますが、原理は依然としてイベント バスです。

  • バス.js
import mitt from 'mitt';
export default mitt()
  • 親コンポーネント
<template>
  <Brother1 />
  <Brother2 />
</template>

<script setup>
import Brother1 from "./Brother1.vue";
import Brother2 from "./Brother2.vue";
</script>
  • ブラザーコンポーネント1
<template>
    <p>brother1 发送事件</p>
    <el-button type="primary" size="small" @click="handleClick">发送事件</el-button>
</template>

<script setup>
import mybus from './bus.js';

const handleClick = () => {
      
      
    mybus.emit("title",{
      
      title:"hello world"});
    mybus.emit("user",{
      
      user:{
      
      name:"falcon",age:20}})
}
</script>
  • ブラザーコンポーネント2
<template>
    <p>brother2 接受事件</p>
    <p>title:{
   
   {title}}</p>
    <p>user:name,{
   
   {name}};age,{
   
   {age}}</p>
</template>

<script setup>
import mybus from './bus.js'

const title = ref("")
const user = reactive({
      
      
    name:"",
    age:null
})

mybus.on("title",(data) => {
      
      
    title.value = data.title
})
mybus.on("user",(data) => {
      
      
    user.name = data.user.name
    user.age = data.user.age 
})
</script>

20. 状態管理ピニア

pinia は、 を可能にする vue のリポジトリです跨组件/跨页面共享状态次のような利点があります。

  • 軽量、約1kb
  • 去除Mutation,Actions支持同步和异步
  • 手動でストアを登録する必要はありません。store仅在需要时才自动注册
  • モジュールの入れ子はなく、ストア間で自由に使用できます。
  • 支持模块热更新

20.1 作成

import {
    
     createPinia } from 'pinia'

const store = createPinia()
export default store

20.2 定義

// 引入store定义函数
import {
    
     defineStore } from 'pinia'

// 定义store实例并导出
// 第一个参数,字符串类型,唯一不可重复,作为库id来区分不同库
// 第二个参数,以对象形式配置存储库的state、getters、actions

export const useStore = defineStore('useCount',{
    
    
    /**
        state,存储全局状态
        必须是箭头函数:为了在服务器端渲染的时候避免交叉请求导致数据状态污染
    */
    state:() => {
    
    
        return {
    
    
            count:0
        }
    },
    /**
        getters,封装计算属性
        具有缓存功能,类似于computed;需要传入state才能拿到数据;不能传递任何参数,但是可以返回一个函数接受任何参数
    */
    getters:{
    
    
        doubleCount:(state) => state.count * 2,
        powCount(){
    
    
            return this.doubleCount ** 2
        }
    },
    /**
        actions,编辑业务逻辑
        类似于methods,支持同步和异步;获取state里的数据不需要传入直接使用this
    */
    actions:{
    
    
        addCount(){
    
    
            this.count++
        },
        subCount(){
    
    
            this.count--
        }
    },
    /**
        配置数据持久化需要进行的操作
    */
    persist:{
    
    }
})

20.3 ページの使用法

<template>
    <p>{
    
    {
    
    useStoreDemo.count}}<p>
    <p>{
    
    {
    
    useStoreDemo.doubleCount}}</p>
    <p>{
    
    {
    
    useStoreDemo.powCount}}</p>
    <el-button @click="toAdd">count++</el-button>
    <el-button @click="toSub">count--</el-button>
</template>

<script setup>
import {
    
    useStore} from '../store'
const useStoreDemo = useStore()

// 也可以解构出来想要使用的count,但直接解构不具有响应式,想要具有响应式,可以执行如下操作:
const {
    
    count} = storeToRefs(useStore())

const toAdd = () => useStoreDemo.addCount()
const toSub = () => useStoreDemo.subCount()
<script>

20.4 データの永続性

Pinia のデータはメモリに保存され、ページが更新されるとデータは失われます。データの永続性を実現するための拡張プラグインをサポートできます。

  • npm i pinia-plugin-persist、デフォルトで sessionStorage を使用します
  • 構成の使用コードは次のとおりです。
persist:{
    
    
    enabled:true,
    strategies:[
        {
    
    
            storage:localStorage,
            paths:["num","user"]
        }
    ]
}

21.ルーティング

  • クエリはパラメータ設定パスを渡し、params はパラメータ設定名を渡します。params中配置path无效
  • クエリ パラメータはアドレス バーに表示されますが、params パラメータは表示されません。
  • query传参刷新页面数据不会消失,params传参刷新页面数据消失
  • params では動的パラメータ (「/path/:params」) を使用できます。动态参数会显示在地址栏中,且刷新页面数据不会消失
  • name はルートで定義された name 属性です。严格区分大小写
  • ルートジャンプ: 前方router.go(1)、後方router.go(-1)、リフレッシュrouter.go(0)
  • 使用例:
<template>
    <el-button @click="TransByQuery">通过query传参</el-button>
    <el-button @click="TransByParams">通过params传参</el-button>
    <el-button @click="TransByDynamic">动态传递参数</el-button>
</template>

<script setup>
const queryParams = reactive({
    
    
    name:'falcon',
    age:20
})

const id = ref('2023')

const router = useRouter()

const TransByQuery = () => {
    
    
    router.push({
    
    
        path:'/basic/querydemo'query:queryParams
    })
}
    
const TransByParams = () => {
    
    
    router.push({
    
    
        name:'ParamsDemo',
        params:queryParams
    })
}

const TransByDynamic = () => {
    
    
    router.push({
    
    
        name:'DynamicDemo',
        params:{
    
    id:id.value}
    })
}
<script>
  • クエリはパラメータを受け取ります
const route = useRoute()
console.log(route.query.name,route.query.age)
  • params はパラメータを受け入れます
const route = useRoute()
console.log(route.params.name,route.params.age)
  • パラメータを動的に渡す
const route = useRoute()
console.log(route.params.id)
  • 対応路線
 {
    
    
  name: "QueryDemo",
  path: "querydemo",
  redirect: null,
  component: "basic/userouter/querydemo",
  hidden: true,
  meta: {
    
    
    title: "query样例",
    icon: null,
  },
},
{
    
    
  name: "ParamsDemo",
  path: "paramsdemo",
  redirect: null,
  component: "basic/userouter/paramsdemo",
  hidden: true,
  meta: {
    
    
    title: "params样例",
    icon: null,
  },
},
{
    
    
  name: "DynamicDemo",
  path: "dynamicdemo/:id",
  redirect: null,
  component: "basic/userouter/dynamicdemo",
  hidden: true,
  meta: {
    
    
    title: "dynamic样例",
    icon: null,
  },
},

22.CSS補足

22.1 スタイルの浸透

  • css >>> className,less /deep/ className, scss::v-deep className
  • vue3 で CSS を使用します。:deep(className)

22.2 バインド変数

<template>
    <div class="name">falcon</div>
</template>

<script setup>
const str = ref('#f00')
</script>

<style lang="scss" scoped>
.name{
      
      
    background-color:v-bind(str)
}
</style>

おすすめ

転載: blog.csdn.net/weixin_45506717/article/details/132883249