Vue3 まとめ


title: Vue3 总结
date: 2022-11-06 23:57:57
tags:

  • ビューの
    カテゴリ:
  • Vue
    カバー: https://cover.png
    機能: false


Vue2 の基本については、Vue2 の概要 (基本)_Fan223 のブログを参照してください。

Vue2 の開発については、次を参照してください: Vue2 の概要 (開発)_Fan223 のブログ

1 はじめに

  1. パフォーマンスの向上

    • バンドルサイズを 41% 削減
    • 初期レンダリングが 55% 高速化、更新レンダリングが 133% 高速化
    • 54% 少ないメモリ
      ...
  2. ソースコードのアップグレード

    • defineProperty の代わりに Proxy を使用して応答性を実現する
    • 仮想DOMとTree-Shakingの実装を書き換える
  3. TypeScript
    Vue3 を採用すると、TypeScript をより適切にサポートできます

  4. 新機能

    1. コンポジション API (コンポジション API)
      • セットアップ構成
      • ref とリアクティブ
      • watch と watchEffect
      • 提供して注入する
        ...
    2. 新しいビルトイン
      • 断片
      • テレポート
      • サスペンス
    3. その他の変更
      • 新しいライフサイクル フック
      • data オプションは常に関数として宣言する必要があります
      • v-on 修飾子としての keyCode サポートを削除します
        ...

2. を使用して作成

2.1 vue-cli で作成

公式ドキュメント: https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

2.2 vite を使用して作成する

公式文書: https://v3.cn.vuejs.org/guide/installation.html#vite
vite の公式ウェブサイト: https://vitejs.cn

ヴィートとは?— 新世代のフロントエンド ビルド ツール。

  • 開発環境ではパッケージング操作が不要で、クイックコールドスタートが可能
  • 軽量で高速なホット リロード (HMR)
  • 真のオンデマンド コンパイル。アプリケーション全体がコンパイルされるのを待つ必要はありません。

従来のビルドと vite ビルドの比較表
ここに画像の説明を挿入
ここに画像の説明を挿入

## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev

2.3 main.js の変更

// 引入的不再是Vue构造函数,引入的是一个名为 createApp 的工厂函数(不需要 new)
import {
    
     createApp } from 'vue'
import App from './App.vue'

// createApp(App).mount('#app')

// 创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)
//挂载
app.mount('#app')

2.4 App.vue の変更

Vue3 コンポーネントのテンプレート構造にはルート タグを含めることができません

<template>
	<!-- Vue3组件中的模板结构可以没有根标签 -->
	<img alt="Vue logo" src="./assets/logo.png">
	<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

2.5 Vue3 開発者ツール

ここに画像の説明を挿入

3. 一般的なコンポジション API

3.1 セットアップ機能

  1. Vue3.0 の新しい設定項目で、値は関数です
  2. setup は「すべての合成 API が実行されるステージ」です。
  3. コンポーネントで使用されるコンポーネント: データ、メソッドなどは、セットアップで構成する必要があります。
  4. setup 関数の 2 つの戻り値:
    1. オブジェクトが返された場合、オブジェクトのプロパティとメソッドをテンプレートで直接使用できます
    2. レンダリング関数が返される場合: レンダリング コンテンツをカスタマイズできます
<template>
  <h1>姓名:{
   
   {name}}</h1>
  <h1>年龄:{
   
   {age}}</h1>
  <button @click="info">个人信息</button>
</template>

<script>
// import {h} from 'vue'
export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let name = "张三"
    let age = 18

    // 方法
    function info() {
      
      
      alert(`我叫${ 
        name}, 年龄${ 
        age}`)
    }

    // 返回一个对象(常用)
    return {
      
      
      name,
      age,
      info
    }
    //返回一个函数(渲染函数),需要导入 h
    // return ()=> h('h1','渲染')
  }
}
</script>

ここに画像の説明を挿入

1. Vue2.x 構成と混合しないようにしてください

  • セットアップのプロパティとメソッドは、Vue2.x 構成でアクセスできます (データ、メソッド、計算された...)
  • しかし、Vue2.x 構成 (データ、メソッド、計算された...) はセットアップでアクセスできません
  • 重複する名前がある場合は、セットアップが優先されます

2. setup を非同期関数にすることはできません。これは、async の戻り値が return オブジェクトではなく promise によってラップされ、テンプレートが return オブジェクトのプロパティを認識できないためです (Promise インスタンスを返すこともできますが、Suspense と Suspense を返すこともできます)。非同期コンポーネントの連携)

3.2 ref 関数

以下のように、機能を通じて個人情報を変更します。

<template>
  <h1>姓名:{
   
   {name}}</h1>
  <h1>年龄:{
   
   {age}}</h1>
  <button @click="changeInfo">改变个人信息</button>
</template>

<script>

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let name = "李四"
    let age = 18

    // 方法
    function changeInfo() {
      
      
      name = "张三"
      age = 20
      console.log(name + age);
    }

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

実際の値が変更されていることがわかりましたが、ページは変更に応答しません
ここに画像の説明を挿入

このとき、ref 関数を使用して応答データを定義する必要があります。

  • 役割:レスポンシブ データを定義する
  • 文法:const xxx = ref(initValue)
    • レスポンシブ データを含む参照オブジェクト (参照オブジェクト、略して ref オブジェクト) を作成する
    • JS でのデータ操作:xxx.value
    • テンプレート内のデータを読み取る: 不要.value, 直接: <div>{ {xxx}}</div>, vue3 が自動的に解析します
<script>
import {
    
     ref } from 'vue';

export default {
    
    
  name: 'App',
  setup() {
    
    
    // 数据
    let name = ref('李四')
    let age = ref(18)

    // 方法
    function changeInfo() {
    
    
      name.value = "张三"
      age.value = 20
      console.log(name, age);
    }

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

ref 関数を使用するオブジェクトは、既に RefImpl オブジェクト (参照実装) であり、
ここに画像の説明を挿入
オブジェクト型データの場合、RefImpl オブジェクトの値は Proxy オブジェクトです。

<template>
  <h1>姓名:{
   
   {name}}</h1>
  <h1>年龄:{
   
   {age}}</h1>
  <h2>职业:{
   
   {job.type}}</h2>
  <h2>薪水:{
   
   {job.salary}}</h2>
  <button @click="changeInfo">改变个人信息</button>
</template>

<script>
import {
      
       ref } from 'vue';

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let name = ref('李四')
    let age = ref(18)
    let job = ref({
      
      
      type: '开发',
      salary: '20k'
    })

    // 方法
    function changeInfo() {
      
      
      job.value.type = '设计'
      job.value.salary = '25k'
      console.log(job);
      console.log(job.value);
    }

    return {
      
      
      name,
      age,
      job,
      changeInfo
    }
  }
}
</script>

ここに画像の説明を挿入

知らせ:

  1. 受信データは、基本型またはオブジェクト型のいずれかです。
  2. データの基本的なタイプ: 応答性は get と setObject.defineProperty()によって
  3. オブジェクト型データ: Vue3.0 の新しい関数を内部的に「助ける」 - リアクティブ関数

3.3 リアクティブ機能

  • 役割: レスポンシブ データのオブジェクト型を定義します (基本型には使用せず、ref 関数を使用します)。
  • 構文: const 代理对象 = reactive(源对象)、オブジェクト (または配列) を受け取り、プロキシ オブジェクト (Proxy オブジェクトと呼ばれる Proxy のインスタンス オブジェクト) を返します。
  • 反応によって定義された反応データは「深い」
  • ES6 ベースの内部プロキシ実装は、プロキシ オブジェクトを介してソース オブジェクトの内部データを操作します。

3.2 のオブジェクト タイプの例を変更し、次のようにリアクティブを使用してオブジェクト タイプ データを定義します。

<template>
  <h1>姓名:{
   
   {name}}</h1>
  <h1>姓名:{
   
   {age}}</h1>
  <h2>职业:{
   
   {job.type}}</h2>
  <h2>薪水:{
   
   {job.salary}}</h2>
  <button @click="changeInfo">改变个人信息</button>
</template>

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

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let name = ref('李四')
    let age = ref(18)
    // let job = ref({
      
      
    //   type: '开发',
    //   salary: '20k'
    // })
    let job = reactive({
      
      
      type: '开发',
      salary: '20k'
    })

    // 方法
    function changeInfo() {
      
      
      // job.value.type = '设计'
      // job.value.salary = '25k'
      job.type = '设计'
      job.salary = '25k'
      console.log(job);
    }

    return {
      
      
      name,
      age,
      job,
      changeInfo
    }
  }
}
</script>

ここに画像の説明を挿入
同時に配列型への対応やディープレスポンスも可能

<template>
  <h1>姓名:{
   
   {person.name}}</h1>
  <h1>年龄:{
   
   {person.age}}</h1>
  <h2>职业:{
   
   {person.job.type}}</h2>
  <h2>薪水:{
   
   {person.job.salary}}</h2>
  <h1>爱好:{
   
   {person.hobby}}</h1>
  <button @click="changeInfo">改变个人信息</button>
</template>

<script>
import {
      
       reactive } from 'vue';

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let person = reactive({
      
      
      name: '李四',
      age: 18,
      job: {
      
      
        type: '开发',
        salary: '20k'
      },
      hobby: ['看剧', '听歌']
    })

    // 方法
    function changeInfo() {
      
      
      person.name = '张三'
      person.age = 20
      person.job.type = '设计'
      person.job.salary = '25k'
      person.hobby[0] = '学习'
    }

    return {
      
      
      person,
      changeInfo
    }
  }
}
</script>

ここに画像の説明を挿入

3.4 Vue3.0 のレスポンシブ原則

3.4.1 Vue2.x の応答性

実装原則:

  • オブジェクト型: プロパティの読み取りと変更Object.defineProperty()によるが、新しい追加と削除には対応できません。
    Object.defineProperty(data, 'count', {
          
          
    	configurable: true, // 可配置,即可删除属性
        get () {
          
           return ... }, 
        set () {
          
           响应... }
    })
    
  • 配列型:配列を更新する一連のメソッドを書き換えることでインターセプトを実現(配列を変更するメソッドはラップ)

問題があります:

  • 属性を直接追加および削除すると、インターフェースは更新されず、対応する関数 (set、delete) を呼び出す必要があります。
    methods: {
          
          
      addSex(){
          
          
        this.person.sex = '女'; // 不生效
        this.$set(this.person, 'sex', '女')
        Vue.set(this.person, 'sex', '女')
      },
      deleteName(){
          
          
        this.$delete(this.person, 'name', '张三')
        Vue.delete(this.person, 'name', '张三')
      }
    },
    
  • 添え字を介して配列を直接変更すると、インターフェースは自動的に更新されず、上記の方法で変更することもできます。また、配列を直接変更して変更することもできます
    methods: {
          
          
    	updateHobby(){
          
          
    		this.person.hobby[0] = '学习'; // 不生效
    		this.$set(this.person.hobby, 0, '学习')
    		Vue.delete(this.person.hobby, 0, '学习')
    		this.person.hobby.splice(0, 1, '学习')
    	}
    },
    

3.4.2 Vue3.0 の応答性

実現原理:

  • プロキシ経由 (プロキシ): 属性値の読み取りと書き込み、属性の追加、属性の削除などを含む、オブジェクト内の属性の変更を傍受します。

  • Through Reflect (反射): ソース オブジェクトのプロパティを操作します。

    <body>
       <script type="text/javascript">
        let person = {
            
            
          name: '张三',
          age: 18
        }
    
        // 模拟 Vue3中实现响应式
        const p = new Proxy(person, {
            
            
          // 读取时调用
          get(target, prop) {
            
            
            console.log(target, prop);
            return target[prop]
          },
          // 修改或新增时调用
          set(target, prop, value) {
            
            
            console.log(target, prop, value);
            target[prop] = value;
          },
          // 删除时调用
          deleteProperty(target, prop) {
            
            
            console.log(target, prop);
            return delete target[prop];
          }
        })
      </script>
    </body>
    

    元オブジェクトのプロキシオブジェクトを操作し、プロキシオブジェクトが元オブジェクトを操作する
    ここに画像の説明を挿入
    上記の操作をReflectに渡して実行することができる

    new Proxy(data, {
          
          
    	// 拦截读取属性值
        get (target, prop) {
          
          
        	return Reflect.get(target, prop)
        },
        // 拦截设置属性值或添加新属性
        set (target, prop, value) {
          
          
        	return Reflect.set(target, prop, value)
        },
        // 拦截删除属性
        deleteProperty (target, prop) {
          
          
        	return Reflect.deleteProperty(target, prop)
        }
    })
    
    proxy.name = 'tom'  
    
  • Proxy と Reflect は、MDN のドキュメントで説明されています。

<template>
  <h1>姓名:{
   
   {person.name}}</h1>
  <h1>年龄:{
   
   {person.age}}</h1>
  <h1 v-show="person.sex">性别:{
   
   {person.sex}}</h1>
  <h2>职业:{
   
   {person.job.type}}</h2>
  <h2>薪水:{
   
   {person.job.salary}}</h2>
  <button @click="changeInfo">改变个人信息</button>
</template>

<script>
import {
      
       reactive } from 'vue';

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let person = reactive({
      
      
      name: '李四',
      age: 18,
      job: {
      
      
        type: '开发',
        salary: '20k'
      },
      hobby: ['看剧', '听歌']
    })

    // 方法
    function changeInfo() {
      
      
      person.sex = '男'
      delete person.name
      person.hobby[0] = '学习'
    }

    return {
      
      
      person,
      changeInfo
    }
  }
}
</script>

ここに画像の説明を挿入

3.5 リアクティブ vs リファレンス

1. データ定義の観点からの比較

  • ref は以下を定義するために使用されます: 基本型データ
  • リアクティブは次の定義に使用されます: オブジェクト (または配列) 型データ
  • 備考: ref は、オブジェクト (または配列) 型のデータを定義するためにも使用できます。これは、内部でリアクティブを介して自動的にプロキシ オブジェクトに変換されます。

2.原理的な比較

  • Object.defineProperty()ref は、 get と set を使用して応答性 (データ ハイジャック) を実装します。
  • Reactive は Proxy を使用して応答性 (データ ハイジャック) を実装し、Reflect を介してソース オブジェクト内のデータを操作します。

3. 使用面での比較

  • ref で定義されたデータ: データ操作に必要.value、データ読み取り時のテンプレートでの直接読み取りには不要.value
  • リアクティブで定義されたデータ: 操作データと読み取りデータ: どちらも必要ありません.value

3.6 セットアップに関する 2 つの注意事項

3.6.1 セットアップ実行のタイミング

beforeCreate の前に 1 回実行します。これは未定義です

3.6.2 セットアップのパラメータ

  • props: 値は以下を含むオブジェクトです: コンポーネントの外部から渡された App.vue コンポーネント、およびコンポーネントが受け取ったプロパティを内部的に宣言する
    App.vue コンポーネント、値を Demo.vue に渡す

    <template>
      <Demo
        msg="你好"
        school=""
      />
    </template>
    
    <script>
    import Demo from './components/Demo.vue';
    
    export default {
            
            
      name: 'App',
      components: {
            
            
        Demo
      }
    }
    </script>
    

    App.vue から渡された値を受け取る Demo.vue コンポーネント

    <template>
      <h1>个人信息</h1>
      <h2>姓名:{
         
         {person.name}}</h2>
      <h2>年龄:{
         
         {person.age}}</h2>
    </template>
    
    <script>
    import {
            
             reactive } from 'vue'
    
    export default {
            
            
      // eslint-disable-next-line vue/multi-word-component-names
      name: 'Demo',
      props: ['msg', 'school'],
      setup(props) {
            
            
        console.log(props);
        // 数据
        let person = reactive({
            
            
          name: '张三',
          age: 18
        })
    
        return {
            
            
          person
        }
      }
    }
    </script>
    
    <style>
    </style>
    

    ここに画像の説明を挿入

  • コンテキスト: コンテキスト オブジェクト

    • attrs: 値は次を含むオブジェクトです: コンポーネントの外部から渡されたが props 構成で宣言されていない属性。this.$attrs
    • スロット: 受信したスロット コンテンツ、同等のものthis.$slots
    • emit: カスタム イベントを配布する関数。this.$emit

App.vue、Demo.vueに値を渡し、カスタムイベントとスロットを同時に渡す

<template>
  <Demo
    msg="你好"
    school=""
    @hello="showMsg"
  >
    <template v-slot:te>
      <span>测试</span>
    </template>
  </Demo>
</template>

<script>
import Demo from './components/Demo.vue';

export default {
      
      
  name: 'App',
  setup() {
      
      
    function showMsg(value) {
      
      
      alert(`触发,参数是${ 
        value}`)
    }

    return {
      
      
      showMsg
    }
  },
  components: {
      
      
    Demo
  }
}
</script>

Demo.vue コンポーネント、props は App.vue から渡された値を受け取り、受け取っていない値は attrs にあり、emits はカスタム イベントを受け取り、受信が書き込まれていない場合は警告を報告しますが、使用には影響しません。 App.vue Define イベントで自己をトリガーします。スロットは直接使用されます

<template>
  <h1>个人信息</h1>
  <h2>姓名:{
   
   {person.name}}</h2>
  <h2>年龄:{
   
   {person.age}}</h2>
  <button @click="hello">测试触发事件</button>
  <slot name="te"></slot>
</template>

<script>
import {
      
       reactive } from 'vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  props: ['msg'],
  emits: ['hello'],
  setup(props, context) {
      
      
    console.log(props);
    console.log(context.attrs);
    console.log(context.emit);
    console.log(context.slots);
    // 数据
    let person = reactive({
      
      
      name: '张三',
      age: 18
    })
    // 方法
    function hello() {
      
      
      context.emit('hello', 666);
    }

    return {
      
      
      person,
      hello
    }
  }
}
</script>

<style>
</style>

ここに画像の説明を挿入

3.7 計算されたプロパティと監視

3.7.1 計算関数(計算プロパティ)

最初の書き方: Vue2.x の計算された構成関数と一致

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

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  computed: {
    
    
    fullName() {
    
    
      return this.person.firstName + '-' + this.person.lastName;
    }
  },
  setup() {
    
    
    // 数据
    let person = reactive({
    
    
      firstName: '张',
      lastName: '三',
    })

    return {
    
    
      person,
    }
  }
}
</script>

2 つ目の書き方は次のとおりです。

<template>
  <h1>个人信息</h1>
  姓:<input
    type="text"
    v-model="person.firstName"
  /> <br>
  名:<input
    type="text"
    v-model="person.lastName"
  /> <br>
  <span>全名: {
   
   {person.fullName}} </span> <br>
  全名:<input
    type="text"
    v-model="person.fullName"
  >
</template>

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

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    // 数据
    let person = reactive({
      
      
      firstName: '张',
      lastName: '三',
    })
    // 计算属性-简写(只读,不考虑计算属性被修改的情况)
    person.fullName = computed(() => {
      
      
      return person.firstName + '-' + person.lastName;
    })

    // 计算属性-完整写法(考虑读和写)
    person.fullName = computed({
      
      
      get() {
      
      
        return person.firstName + '-' + person.lastName;
      },
      set(value) {
      
      
        let nameArr = value.split('-');
        person.firstName = nameArr[0]
        person.lastName = nameArr[1]
      }
    })

    return {
      
      
      person,
    }
  }
}
</script>

<style>
</style>

ここに画像の説明を挿入

3.7.2 ウォッチ機能(モニタリング)

最初の書き方: Vue2.x の計算された構成関数と一致

<script>
import {
    
     ref } from 'vue'

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  watch: {
    
    
    sum(newValue, oldValue) {
    
    
      console.log('sum 的值变化了', newValue, oldValue);
    }
  },
  // watch: {
    
    
  //   sum: {
    
    
  //     immediate: true, // 立即监视,一进来就会监视一下
  //     deep: true,
  //     handler(newValue, oldValue) {
    
    
  //       console.log('sum 的值变化了', newValue, oldValue);
  //     }
  //   }
  // },
  setup() {
    
    
    // 数据
    let sum = ref(0)

    return {
    
    
      sum,
    }
  }
}
</script>

ここに画像の説明を挿入

2 つ目の書き方は次のとおりです。

1. ref で定義されたデータを監視する

<template>
  <h1>当前求和为:{
   
   {sum}} </h1>
  <button @click="sum++">点击 + 1</button>
  <hr>
  <h1>当前信息为:{
   
   {msg}} </h1>
  <button @click="msg += '!'">修改信息</button>
</template>

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

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    // 数据
    let sum = ref(0)
    let msg = ref('你好')

    // 情况一,监视 ref 所定义的一个响应式数据
    // watch(sum, (newValue, oldValue) => {
      
      
    //   console.log('sum 变了', newValue, oldValue);
    // }, {immediate: true, deep: true})

    // 情况二,监视 ref 所定义的多个响应式数据
    watch([sum, msg], (newValue, oldValue) => {
      
      
      console.log(newValue, oldValue);
    }, {
      
       immediate: true, deep: true })

    return {
      
      
      sum,
      msg
    }
  }
}
</script>

<style>
</style>

ここに画像の説明を挿入

2. リアクティブで定義されたデータを監視する

<template>
  <h1>当前姓名为:{
   
   {person.name}} </h1>
  <button @click="person.name += '~'">修改姓名</button>
  <hr>
  <h1>当前年龄为:{
   
   {person.age}} </h1>
  <button @click="person.age ++">修改年龄</button>
  <hr>
  <h2>职业:{
   
   {person.job.type}} </h2>
  <h2>薪酬:{
   
   {person.job.salary}} </h2>
  <button @click="person.job.type += '!'">修改职业</button>
</template>

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

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    // 数据
    let person = reactive({
      
      
      name: '张三',
      age: 18,
      job: {
      
      
        type: '开发',
        salary: '25k'
      }
    })

    // 情况一,监视 reactive 所定义的一个响应式数据的全部属性
    //      watch 监视的是 reactive 定义的响应式数据,则无法正确获得 oldValue!!
    //      若watch 监视的是 reactive 定义的响应式数据,则强制开启了深度监视(deep 配置无效)
    // watch(person, (newValue, oldValue) => {
      
      
    //   console.log('person变化', newValue, oldValue);
    // }, { deep: false })

    // 情况二:监视 reactive 所定义的一个响应式数据的某个属性
    // watch(() => person.age, (newValue, oldValue) => {
      
      
    //   console.log('person 的age 变化了', newValue, oldValue);
    // })

    // 情况三:监视 reactive 所定义的一个响应式数据的某些属性
    watch([() => person.age, () => person.name], (newValue, oldValue) => {
      
      
      console.log('person 的age 变化了', newValue, oldValue);
    })

    // 特殊情况:监视的是 reactive 定义的属性中的某个对象属性,所以 deep 配置有效
    watch(() => person.job, (newValue, oldValue) => {
      
      
      console.log('person 的age 变化了', newValue, oldValue);
    }, {
      
       deep: true })


    return {
      
      
      person
    }
  }
}
</script>

<style>
</style>

ここに画像の説明を挿入

3.7.2 watchEffect 関数

  • watch のルーチンは: 監視の属性だけでなく、監視のコールバックも指定する
  • watchEffect のルーチンは次のとおりです。監視する属性を指定する必要はありません。監視コールバックで使用される属性はどれですか。次に、監視する属性を指定します。
  • watchEffect は計算されたものに少し似ています:
    • ただし、computed は計算値 (コールバック関数の戻り値) に注目するため、戻り値を記述する必要があります。
    • そして、watchEffect はプロセス (コールバック関数の関数本体) に注意を払うため、戻り値を記述する必要はありません。
<script>
import {
    
     reactive, watchEffect } from 'vue'

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
    
    
    // 数据
    let sum = ref(0)
    let person = reactive({
    
    
      name: '张三',
      age: 18,
      job: {
    
    
        type: '开发',
        salary: '25k'
      }
    })

    // watchEffect 所指定的回调中用到的数据只要发生变化,则直接重新执行回调
    watchEffect(() => {
    
    
      const x1 = person.name
      const x2 = person.job.type
      console.log('watchEffect 配置的回调执行了');
    })

    return {
    
    
      person
    }
  }
}
</script>

3.8 ライフサイクル

ここに画像の説明を挿入
Vue2.x のライフサイクル フックは Vue3.0 でも引き続き使用できますが、そのうちの 2 つの名前が変更されました。

  • beforeDestroy は beforeUnmount に名前が変更されました
  • 破壊された名前がマウントされていないに変更されました

app.vue

<template>
  <button @click="isShowDemo = !isShowDemo">显示/隐藏Demo</button>
  <Demo v-if="isShowDemo" />
</template>

<script>
import Demo from './components/Demo.vue';
import {
      
       ref } from 'vue'

export default {
      
      
  name: 'App',
  setup() {
      
      
    let isShowDemo = ref(true)

    return {
      
      
      isShowDemo
    }
  },
  components: {
      
      
    Demo
  }
}
</script>

Demo.vue

<template>
  <h1>Demo</h1>
</template>

<script>
export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  beforeCreate() {
      
      
    console.log('---beforeCreate---');
  },
  created() {
      
      
    console.log('---created---');
  },
  beforeMount() {
      
      
    console.log('---beforeMount---');
  },
  mounted() {
      
      
    console.log('---mounted---');
  },
  beforeUnmount() {
      
      
    console.log('---beforeUnmount---');
  },
  unmounted() {
      
      
    console.log('---unmounted--');
  },
}
</script>

ここに画像の説明を挿入
ここに画像の説明を挿入

Vue3.0 では、Composition API の形でライフサイクル フックも提供されており、Vue2.x のフックとの対応関係は次のとおりです。

  • beforeCreate ===>setup()
  • 作成 ===>setup()
  • beforeMount ===> onBeforeMount
  • マウント ===> onMounted
  • beforeUpdate ===> onBeforeUpdate
  • 更新済み ===> onUpdated
  • beforeUnmount ==> onBeforeUnmount
  • アンマウント ===> onUnmounted
<template>
  <h1>Demo</h1>
</template>

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

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    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---');
    })
  }
}
</script>

ここに画像の説明を挿入
ここに画像の説明を挿入

3.9 カスタムフック機能

フックは基本的に、セットアップ関数で使用される構成 API をカプセル化する関数です。Vue2.x のミックスインに似ています。コードを再利用して、セットアップのロジックをより明確にし、理解しやすくすることができます

次のように、現在のマウス位置を取得します

<template>
  <h1>当前点击时鼠标的坐标为:X:{
   
   {point.x}},y:{
   
   {point.y}} </h1>
</template>

<script>
import {
      
       reactive, onMounted } from 'vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    let point = reactive({
      
      
      x: 0,
      y: 0
    })

    onMounted(() => {
      
      
      window.addEventListener('click', (event) => {
      
      
        point.x = event.pageX
        point.y = event.pageY
      })
    })

    return {
      
      
      point
    }
  }
}
</script>

ここに画像の説明を挿入
このとき, クリック イベントがウィンドウに追加されます. ページがクリックされている限り, そのポイントに対するページのマウス位置が取得されます. コンポーネントがアンインストールされた場合でも, それはトリガーされます. クリック イベント
ここに画像の説明を挿入
はコンポーネントのアンインストール後に削除されます。イベントを削除する場合、削除するイベントを渡す必要があるため、クリック イベントを別の関数として記述します。

<script>
import {
    
     reactive, onMounted, onBeforeUnmount } from 'vue'

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
    
    
    let point = reactive({
    
    
      x: 0,
      y: 0
    })

    function savePoint(event) {
    
    
      point.x = event.pageX
      point.y = event.pageY

      console.log(point.x, point.y);
    }

    onMounted(() => {
    
    
      window.addEventListener('click', savePoint)
    })
    onBeforeUnmount(() => {
    
    
      window.removeEventListener('click', savePoint)
    })

    return {
    
    
      point
    }
  }
}
</script>

別のコンポーネントもこの関数を使用してこのコードを再利用したい場合、この関数に関連するデータと関数を抽出してフック関数を形成できます。
ここに画像の説明を挿入

import {
    
     reactive, onMounted, onBeforeUnmount} from 'vue';

export default function() {
    
    
  let point = reactive({
    
    
    x: 0,
    y: 0
  })

  function savePoint(event) {
    
    
    point.x = event.pageX
    point.y = event.pageY

    console.log(point.x, point.y);
  }

  onMounted(() => {
    
    
    window.addEventListener('click', savePoint)
  })
  onBeforeUnmount(() => {
    
    
    window.removeEventListener('click', savePoint)
  })

  return point
}

使用時にインポートできます

<template>
  <h1>当前点击时鼠标的坐标为:X:{
   
   {point.x}},y:{
   
   {point.y}} </h1>
</template>

<script>
import usePoint from '../hooks/usePoint'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    let point = usePoint()

    return {
      
      
      point
    }
  }
}
</script>

ここに画像の説明を挿入

3.10 toRef

値が別のオブジェクトのプロパティを指す ref オブジェクトを作成します。

  • 文法:const name = toRef(person, 'name')

レスポンシブ オブジェクト内の特定の属性を個別に外部利用に提供するために使用されます。次のように、person オブジェクト内の名前、年齢、およびその他の属性を個別に外部利用に提供します。

<template>
  <h1>姓名: {
   
   {name}} </h1>
  <h1>年龄: {
   
   {age}} </h1>
  <h1>薪资: {
   
   {salary}} </h1>

  <button @click="name += '~'">修改姓名</button>
  <button @click="age ++">修改年龄</button>
  <button @click="salary ++">修改薪资</button>
</template>

<script>
import {
      
       reactive, toRef } from '@vue/reactivity'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    let person = reactive({
      
      
      name: '张三',
      age: 18,
      job: {
      
      
        j1: {
      
      
          salary: 20
        }
      }
    })

    return {
      
      
      // name: ref(person, 'name')
      name: toRef(person, 'name'),
      age: toRef(person, 'age'),
      salary: toRef(person.job.j1, 'salary')
    }
  }
}
</script>

ここに画像の説明を挿入
レスポンシブ効果があり、このとき toRef で操作するデータは元の person オブジェクトのデータです. 修正とは, person オブジェクトの対応する値を同期的に修正することを意味します. ref を直接使って次のように変換する
ref(person.name),操作は人物オブジェクト オブジェクトの name 属性値は新しく作成された ref オブジェクトであり、元の人物オブジェクトの名前とは関係ありません
ここに画像の説明を挿入
toRefs は toRef と同じ機能を持ちますが、複数の ref オブジェクトをバッチで作成できます、つまり、オブジェクト全体が破棄されます。構文: , toRefs(person)but は外部レベルの属性のみをターゲットにできます。

<h1>姓名: {
    
    {
    
    name}} </h1>
<h1>年龄: {
    
    {
    
    age}} </h1>
<h1>薪资: {
    
    {
    
    job.j1.salary}} </h1>
  
...toRefs(person)
// name: toRef(person, 'name'),
// age: toRef(person, 'age'),
// salary: toRef(person.job.j1, 'salary')

4. その他の合成 API

4.1shallowReactiveとshallowRef

  • shadowReactive: オブジェクトの最も外側のプロパティのみを処理するレスポンシブ (浅いレスポンシブ)
  • 浅い参照: オブジェクトの応答性ではなく、基本的なデータ型の応答性のみを処理します
let person = shallowReactive ({
    
    
	name: '张三',
	age: 18,
	job: {
    
    
		j1: {
    
    
			salary: 20
		}
	}
})

いつ使用するのですか?

  • オブジェクトデータがあれば構造は比較的深いですが、変化すると外側の属性だけが変化する===>shallowReactive
  • オブジェクト データがある場合、後続の関数はオブジェクトのプロパティを変更しませんが、新しいオブジェクトを生成して置き換えます ===>shallowRef

4.2 readonlyとshallowReadonly

  • readonly: レスポンシブ データを読み取り専用 (深い読み取り専用) にします。
  • shadowReadonly: レスポンシブ データを読み取り専用にする (浅い読み取り専用)
let person = reactive ({
	name: '张三',
	age: 18,
	job: {
		j1: {
			salary: 20
		}
	}
})

person = readonly(person)

データを変更したくない場合に適用

4.3 toRaw と markRaw

toRaw

  • 機能: Reactive によって生成されたリアクティブ オブジェクトを通常のオブジェクトに変換する

使用シナリオ: レスポンシブ オブジェクトに対応する共通オブジェクトを読み取るために使用されます。この共通オブジェクトに対するすべての操作によってページが更新されることはありません。

const p = toRaw(person)

マーク生

  • 役割: 二度とレスポンシブ オブジェクトにならないようにオブジェクトにマークを付ける

アプリケーション シナリオ:

  1. 複雑なサードパーティ ライブラリなど、一部の値はレスポンシブに設定しないでください。
  2. リアクティブ遷移をスキップすると、不変のデータ ソースを含む大きなリストをレンダリングするときにパフォーマンスが向上する可能性があります
person.car = markRaw(person)

4.4 customRef

Create a custom ref, and explicit control its dependency tracking and update trigger
. 次の例では、アンチシェイク効果を実現しています

<template>
  <input
    type="text"
    v-model="keyWord"
  />
  <h1> {
   
   {keyWord}} </h1>
</template>

<script>
import {
      
       customRef } from 'vue'

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 自定义一个 ref
    function myRef(value, delay) {
      
      
      let timer;

      return customRef((track, trigger) => {
      
      
        return {
      
      
          get() {
      
      
            // 通知 Vue 追踪数据变化(提前约定)
            track();
            return value;
          },
          set(newValue) {
      
      
            value = newValue;
            clearTimeout(timer);

            timer = setTimeout(() => {
      
      
              // 通知 Vue 去重新解析模板(调用一下 get() 方法)
              trigger();
            }, delay);
          }
        }
      })
    }

    // let keyWord = ref('hello') // 使用 Vue 提供的 ref
    let keyWord = myRef('hello', '500') // 使用自定义的 ref

    return {
      
      
      keyWord,
      myRef
    }
  },
}
</script>

ここに画像の説明を挿入

4.5 提供と注入

ここに画像の説明を挿入
先祖コンポーネントと子孫コンポーネントの間の通信を実現します. 親コンポーネントにはデータを提供する提供オプションがあり、子孫コンポーネントにはこれらのデータの使用を開始する注入オプションがあります
. 先祖コンポーネント App.vue

<template>
  <div class="app">
    <h1> App 组件(祖),{
   
   {name}}---{
   
   {price}} </h1>
    <Child />
  </div>
</template>

<script>
import {
      
       reactive, toRefs } from '@vue/reactivity'
import Child from './components/Child.vue'
import {
      
       provide } from '@vue/runtime-core'

export default {
      
      
  name: 'App',
  setup() {
      
      
    let car = reactive({
      
      
      name: '奔驰',
      price: '40w'
    })

    provide('car', car)

    return {
      
      
      ...toRefs(car)
    }
  },
  components: {
      
      
    Child
  }
}
</script>

子コンポーネント、Child.vue

<template>
  <div class="child">
    <h1> Child组件(子)</h1>
    <Son />
  </div>
</template>

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

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Child',
  components: {
      
      
    Son
  }
}
</script>

Son コンポーネント、Son.vue

<template>
  <div class="son">
    <h1> Son组件(孙),{
   
   {car.name}}---{
   
   {car.price}}</h1>
  </div>
</template>

<script>
import {
      
       inject } from '@vue/runtime-core'
export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Son',
  setup() {
      
      
    let car = inject('car')

    return {
      
      
      car
    }
  }
}
</script>

ここに画像の説明を挿入

4.6 レスポンシブデータの判定

  • isRef : 値が ref オブジェクトかどうかをチェックします
  • isReactive : オブジェクトがリアクティブによって作成されたリアクティブ プロキシであるかどうかを確認します
  • isReadonly : オブジェクトが readonly によって作成された読み取り専用プロキシかどうかを確認します
  • isProxy : オブジェクトがリアクティブ メソッドまたは読み取り専用メソッドによって作成されたプロキシであるかどうかを確認します
let car = reactive({
    
    name: '奔驰', price: '40w'})
let sum = ref(0)
let car2 = readonly(car)

console.log(isReactive(car))
console.log(isRef(sum))
console.log(isReadonly(car2))
console.log(isReadonly(isProxy))

5. コンポジション API の利点

5.1 オプション API の問題

Vue2 で使用される従来のオプション API では、要件を追加または変更する場合、データ、メソッド、および計算されたそれぞれで変更する必要があります。
ここに画像の説明を挿入
ここに画像の説明を挿入

5.2 コンポジション API の利点

私たちのコードと関数は、よりエレガントに整理できます。関連する関数のコードをより整然と整理する
ここに画像の説明を挿入
ここに画像の説明を挿入

6. 新しいコンポーネント

6.1 フラグメント

Vue2 の場合: コンポーネントにはルート タグが必要です。Vue3 では、コンポーネントにルート タグを含めることができず、複数のタグが Fragment 仮想要素に内部的に含まれるため、タグ レベルとメモリ フットプリントを削減できます。

<template>
  <h1>111</h1>
  <h1>222</h1>
</template>

ここに画像の説明を挿入

6.2 テレポート

次のように、コンポーネントの html 構造を指定した位置に移動できます
。ポップアップ ウィンドウを直接開くと、コンポーネントとその親
ここに画像の説明を挿入
コン​​ポーネント Son.vueの高さが拡張されます。

<template>
  <div class="son">
    <h1> Son 组件(孙)</h1>
    <Dialog />
  </div>
</template>

<script>
import Dialog from './Dialog.vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Son',
  components: {
      
      
    Dialog
  }
}
</script>

コンポーネント Dialog.vue

<template>
  <button @click="isShow = true"> 点击弹窗 </button>
  <div
    v-if="isShow"
    class="dialog"
  >
    <h1>弹窗内容</h1>
    <h1>弹窗内容</h1>
    <h1>弹窗内容</h1>
    <button @click="isShow = false">关闭弹窗</button>
  </div>
</template>

<script>
import {
      
       ref } from '@vue/reactivity';

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Dialog',
  setup() {
      
      
    let isShow = ref(false);

    return {
      
      
      isShow
    }
  }
}
</script>

Teleport を使用して、ポップアップ ウィンドウを本文に移動します。

<template>
  <button @click="isShow = true"> 点击弹窗 </button>
  <teleport to='body'>
    <div
      v-if="isShow"
      class="mask"
    >
      <div class="dialog">
        <h1>弹窗内容</h1>
        <h1>弹窗内容</h1>
        <h1>弹窗内容</h1>
        <button @click="isShow = false">关闭弹窗</button>
      </div>
    </div>
  </teleport>
</template>

<script>
import {
      
       ref } from '@vue/reactivity';

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Dialog',
  setup() {
      
      
    let isShow = ref(false);

    return {
      
      
      isShow
    }
  }
}
</script>

<style>
.mask {
      
      
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.5);
}
.dialog {
      
      
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  background-color: green;
  width: 300px;
  height: 300px;
}
</style>

ここに画像の説明を挿入

6.3 サスペンス

非同期コンポーネントを待っている間に追加のコンテンツをレンダリングして、アプリケーションのユーザー エクスペリエンスを向上させます

<template>
  <div class="app">
    <h1> App 组件(祖)</h1>
    <Child />
  </div>
</template>

<script>
import Child from './components/Child.vue' // 静态引入

export default {
      
      
  name: 'App',
  components: {
      
      
    Child
  }
}
</script>

ネットワーク速度を遅くすると、祖先コンポーネントと子孫コンポーネントが一緒に出てきて、
ここに画像の説明を挿入
非同期でインポートされます

<template>
  <div class="app">
    <h1> App 组件(祖)</h1>
    <Child />
  </div>
</template>

<script>
// import Child from './components/Child.vue' //静态引入
import {
      
       defineAsyncComponent } from '@vue/runtime-core' // 静态引入
const Child = defineAsyncComponent(() => import('./components/Child.vue')) // 异步引入

export default {
      
      
  name: 'App',
  components: {
      
      
    Child
  }
}
</script>

ネットワークの速度が遅い場合、祖先コンポーネントが最初に表示され、次に子孫コンポーネントが表示されます.
ここに画像の説明を挿入
このとき、問題があります.子コンポーネントがロードされていない場合、その場所は空であり、その場所は不明です.どんな内容でもあります。Suspense を使用して次のように解きます。

<template>
  <div class="app">
    <h1> App 组件(祖)</h1>
    <Suspense>
      <template v-slot:default>
        <Child />
      </template>
      <template v-slot:fallback>
        <h3>稍等,加载中...</h3>
      </template>
    </Suspense>
  </div>
</template>

<script>
// import Child from './components/Child.vue' //静态引入
import {
      
       defineAsyncComponent } from '@vue/runtime-core' // 静态引入
const Child = defineAsyncComponent(() => import('./components/Child.vue')) // 异步引入

export default {
      
      
  name: 'App',
  components: {
      
      
    Child
  }
}
</script>

ここに画像の説明を挿入
非同期インポートを使用した後、コンポーネントは非同期コンポーネントになり、async を使用して非同期関数を修正setup()できます

<script>
import {
    
     ref } from 'vue';

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Child',
  async setup() {
    
    
    let sum = ref(0)

    let p = new Promise((resove, reject) => {
    
    
      setTimeout(() => {
    
    
        resove(sum)
      }, 3000);
    })

    return await p;
  }
}
</script>

これまで、子コンポーネントの出現を待つ効果は、ネットワーク速度を遅くすることで実現されていました.非同期機能を使用した後、通常のネットワーク速度でも、子コンポーネントが出現するまで待つことができます.
ここに画像の説明を挿入

7. その他の変更

7.1 グローバル API の譲渡

Vue 2.x には、グローバル コンポーネントの登録、グローバル ディレクティブの登録など、多くのグローバル API と構成があります。

//注册全局组件
Vue.component('MyButton', {
    
    
  data: () => ({
    
    
    count: 0
  }),
  template: '<button @click="count++">Clicked {
    
    { count }} times.</button>'
})

//注册全局指令
Vue.directive('focus', {
    
    
  inserted: el => el.focus()
}

これらの API は Vue3.0 で調整され、グローバル API、つまり: Vue.xxx はアプリケーション インスタンス (アプリ) に調整されます。

2.x グローバル API (ビュー) 3.x インスタンス API (アプリ)
app.config.xxxx app.config.xxxx
Vue.config.productionTip 削除
Vue.component app.component
Directive.view app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.プロトタイプ app.config.globalProperties

7.2 その他の変更

  • data オプションは常に関数として宣言する必要があります
    // data { } 
    data() {
          
           }
    
  • 過度のクラス名の変更:
    • Vue2.x の書き方
      .v-enter,
      .v-leave-to {
              
              
        opacity: 0;
      }
      .v-leave,
      .v-enter-to {
              
              
        opacity: 1;
      }
      
    • Vue3.x の書き方
      .v-enter-from,
      .v-leave-to {
              
              
        opacity: 0;
      }
      
      .v-leave-from,
      .v-enter-to {
              
              
        opacity: 1;
      }
      
  • v-on 修飾子としての keyCode を削除し、config.keyCodes をサポートしなくなりました
    // @keyup.13
    // Vue.config.keyCodes.enter = 13
    
  • v-on.native modifier を削除します。コンポーネントにイベントを渡すときに、Vue2 はクリック イベントもカスタム イベントと見なし、それがネイティブ イベントであることを示すために native を追加する必要があります。Vue3はemitを使用してカスタムイベントを指定し、指定されていないものはネイティブイベントです
    • 親コンポーネントでイベントをバインドする
      <my-component
        v-on:close="handleComponentEvent"
        v-on:click="handleNativeClickEvent"
      />
      
    • サブコンポーネントでカスタム イベントを宣言する
      <script>
        export default {
              
              
          emits: ['close']
        }
      </script>
      
  • フィルター (フィルター)
    フィルターを削除すると便利そうに見えますが、中括弧内の式が「単なる JavaScript」であるという前提を破るカスタム構文が必要であり、学習コストだけでなく実装コストもかかります! フィルターをメソッド呼び出しまたは計算されたプロパティに置き換えることをお勧めします

おすすめ

転載: blog.csdn.net/ACE_U_005A/article/details/127017186