vue3

Vue3

1 新建项目

1.1 vue-cli创建

vue -V 查看vue版本,必须高于4.5.0

image-20240109225335113

image-20240109225425920

image-20240109230055429

启动测试

cd vue3_test

npm run serve

image-20240109230250620

运行成功

image-20240109230320271

1.2 vite创建

image-20240109230426414

image-20240109230732426

命令

image-20240109231705213

image-20240109230857552

image-20240109231411406

image-20240109231753599

image-20240109231828781

1.3 分析工程结构

main.js

image-20240111222310799

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

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

App.vue

image-20240111222452598

2 常用的Composition API

2.1 setup

image-20240111223548345

App.vue

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <button @click="sayHello">说话</button>
</template>

<script>

export default {
  name: 'App',
  //此处只是测试一下setup,暂时不考虑响应式的问题
  setup() {
    let name = '张三'
    let age = 20

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

    //返回一个对象(常用)
    return {
      name,
      age,
      sayHello
    }
  }
}
</script>

image-20240111230408796

2.2 ref处理基本类型

image-20240112142903380

修改基本类型

App.vue

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

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

    //方法
    function changeInfo() {
      name.value = '李四'
      age.value = 48
    }

    //返回一个对象(常用)
    return {
      name,
      age,
      changeInfo
    }
  }
    
}
</script>

image-20240111232145484

测试原理

image-20240111232315796

image-20240111232514677

修改对象类型

App.vue

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h3>工作种类:{{job.type}}</h3>
  <h3>薪水:{{job.salary}}</h3>
  <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: '30k'
    })

    //方法
    function changeInfo() {
      name.value = '李四'
      age.value = 48,
      console.log(job.value)
      job.value.type = '后端工程师',
      job.value.salary = '60k'
    }

    //返回一个对象(常用)
    return {
      name,
      age,
      job,
      changeInfo
    }
  }
    
}
</script>

image-20240112143349258

2.3 reactive

image-20240112145918103

App.vue

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h3>工作种类:{{job.type}}</h3>
  <h3>薪水:{{job.salary}}</h3>
  <button @click="changeInfo">修改人的信息</button>
</template>

<script>
import {ref,reactive} from 'vue'
export default {
  name: 'App',
  setup() {
    let name = ref('张三')
    let age = ref(18)
    let job = reactive({
      type: '前端工程师',
      salary: '30k'
    })

    //方法
    function changeInfo() {
      name.value = '李四'
      age.value = 48,
      console.log(job)
      job.type = '后端工程师',
      job.salary = '60k'
    }

    //返回一个对象(常用)
    return {
      name,
      age,
      job,
      changeInfo
    }
  }
    
}
</script>

image-20240112144130750

数组类型

App.vue

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h3>工作种类:{{job.type}}</h3>
  <h3>薪水:{{job.salary}}</h3>
  <h3>爱好:{{hobby}}</h3>
  <button @click="changeInfo">修改人的信息</button>
</template>

<script>
import {ref,reactive} from 'vue'
export default {
  name: 'App',
  setup() {
    let name = ref('张三')
    let age = ref(18)
    let job = reactive({
      type: '前端工程师',
      salary: '30k'
    })
    let hobby = reactive(['抽烟','喝酒','打游戏'])

    //方法
    function changeInfo() {
      name.value = '李四'
      age.value = 48,
      console.log(job)
      job.type = '后端工程师',
      job.salary = '60k',
      hobby[0] = '学习'
    }

    //返回一个对象(常用)
    return {
      name,
      age,
      job,
      changeInfo,
      hobby
    }
  }
    
}
</script>

image-20240112145426976

全部用reactive实现

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <h3>工作种类:{{person.job.type}}</h3>
  <h3>薪水:{{person.job.salary}}</h3>
  <h3>爱好:{{person.hobby}}</h3>
  <button @click="changeInfo">修改人的信息</button>
</template>

<script>
import {reactive} from 'vue'
export default {
  name: 'App',
  setup() {
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        type: '前端工程师',
        salary: '30k'
      },
      hobby: ['吃饭','睡觉']
    })

    //方法
    function changeInfo() {
      person.name = '李四'
      person.age = 48,
      person.job.type = '后端工程师',
      person.job.salary = '60k',
      person.hobby[0] = '学习'
    }

    //返回一个对象(常用)
    return {
      person,
      changeInfo
    }
  }
    
}
</script>

2.4 vue3的响应式原理

vue2的响应式

image-20240112150452645

vue3的响应式

利用reactive解决vue2中存在的问题

App.vue

<template>
  <h1>一个人的信息</h1>
  <h2 v-show="person.name">姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <h2 v-show="person.sex">性别:{{person.sex}}</h2>
  <h3>工作种类:{{person.job.type}}</h3>
  <h3>薪水:{{person.job.salary}}</h3>
  <h3>爱好:{{person.hobby}}</h3>
  <button @click="changeInfo">修改人的信息</button>
  <button @click="addSex">添加一个sex属性</button>
  <button @click="deleteName">删除一个name属性</button>
</template>

<script>
import {reactive} from 'vue'
export default {
  name: 'App',
  setup() {
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        type: '前端工程师',
        salary: '30k'
      },
      hobby: ['吃饭','睡觉']
    })

    //方法
    function changeInfo() {
      person.name = '李四'
      person.age = 48,
      person.job.type = '后端工程师',
      person.job.salary = '60k',
      person.hobby[0] = '学习'
    }

    function addSex(){
        person.sex = '男'
    }
    function deleteName(){
      delete person.name
    }

    //返回一个对象(常用)
    return {
      person,
      changeInfo,
      addSex,
      deleteName
    }
  }
    
}
</script>

image-20240112152141255

vue3实现原理

image-20240112212519519

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        let person = {
          name: 'John',
          age: 30
        }

        //模拟Vue2中实现响应式
        //#region
        /* let p = {}
        Object.defineProperty(p, 'name', {
          configurable: true,
          get() {//有人读取name时调用
            return person.name;
          },
          set(val) {//有人修改name时调用
            console.log('有人修改了name属性,我发现了,我要去更新界面!');
            person.name = val;
          }
        });

        Object.defineProperty(p, 'age', {
          configurable: true,
          get() {//有人读取age时调用
            return person.age;
          },
          set(val) {//有人修改age时调用
            console.log('有人修改了age属性,我发现了,我要去更新界面!');
            person.age = val;
          }
        }); */
        //#endregion


        //模拟Vue3中实现响应式
        //#region
        const p = new Proxy(person,{
          //有人读取p的某个属性时调用
          get(target,propName){
            console.log(`有人读取了p身上的${propName}属性`)
            //propName 是一个变量,target[propName] 相当于从对象中取propName变量对应的值
            return Reflect.get(target,propName)
          },
          //有人修改p的某个属性时调用、或给p追加某个属性时调用
          set(target,propName,value){
            console.log(`有人修改了p身上的${propName}属性属性,我发现了,我要去更新界面!`);
            Reflect.set(target,propName,value)
          },
          //有人删除p的某个属性时调用
          defineProperty(target,propName){
            console.log(`有人删除了p身上的${propName}属性属性,我发现了,我要去更新界面!`);
            return Reflect.defineProperty(target,propName)
          }
        })
        //#endregion

       let obj = {a:1,b:2}
       //通过Object.defineProperty去操作
       /* 这个地方会报错,所以使用try,catch */
       try {
          Object.defineProperty(obj, 'c', {
            get(){
              return 3
            }
          })

          Object.defineProperty(obj, 'c', {
            get(){
              return 4
            }
          })
        
       } catch (error) {
          console.log(error);
       }

       //通过Object.defineProperty去操作
       /* 代码不会报错,只会返回true和false */
       const x1 = Reflect.defineProperty(obj, 'c', {
            get(){
              return 3
            }
        })

       const x2 = Reflect.defineProperty(obj, 'c', {
            get(){
              return 4
            }
        })

        if(x2){
          console.log('某某某操作成功了!');
        }else{
          console.log('某某某操作失败了!');
        }
    </script>
</body>
</html>

2.5 reactive对比ref

image-20240112212631817

2.6 setup的两个注意点

image-20240112221345331

App.vue

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

<script>
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components: {
      Demo
    }
  }
</script>

Demo.vue

<template>
  <h1>一个人的信息</h1>
  <h2 v-show="person.name">姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
</template>

<script>
import {reactive} from 'vue'
export default {
  name: 'App',
  beforeCreate() {
    console.log('---beforeCreate---');
  },
  setup() {
    console.log('---setup---',this)
    let person = reactive({
      name: '张三',
      age: 18
    })

    //返回一个对象(常用)
      return {
        person
    }
    
  }
}
</script>

运行结果

image-20240112214930195

App.vue

<template>
  <Demo @hello="showHelloMsg" msg="你好啊" school="sgg">
    <template v-slot:qwe >
      <span>sgg</span>
    </template>
    <template v-slot:ase >
      <span>sgg</span>
    </template>
  </Demo>
</template>

<script>
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components: {
      Demo
    },
    setup() {
      function showHelloMsg(value) {
        alert(`你好啊,你触发了Hello事件,我收到了参数是:${value}!`)
      }

      return {
        showHelloMsg
      }

    }
  }
</script>

Demo.vue

<template>
  <h1>一个人的信息</h1>
  <h2 v-show="person.name">姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <button @click="test">测试触发一下Demo组件的Hello</button>
</template>

<script>
import {reactive} from 'vue'
export default {
  name: 'App',
  props: ['msg','school'],
  emits:['hello'],
  setup(props,context) {
    // console.log('---setup---',props)
    // console.log('---setup---',context)
    // console.log('---setup---',context.attrs) //相当于Vue2中的$attrs(父组件向子组件中传值,如果子组件不用props接收,就会放到attrs中,类似于捡漏)
    // console.log('---setup---',context.emit)//触发自定义事件的
    console.log('---setup---',context.slots) //插槽

    //数据
    let person = reactive({
      name: '张三',
      age: 18
    })

    //方法
    function test() {
      context.emit('hello',666)
    }

    //返回一个对象(常用)
      return {
        person,
        test
    }
    
  }
}
</script>

image-20240112221634919

2.7 计算属性与监视

computed函数

image-20240113165026237

App.vue

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

<script>
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components: {
      Demo
    },
  }
</script>

Demo.vue

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

<script>
import {reactive,computed} from 'vue'
export default {
  name: 'App',
  setup() {
    //数据
    let person = reactive({
      firstName: '张',
      lastName: '三'
    })

    //计算属性--简写(没有考虑计算属性被修改的情况)
    /* person.fullName = computed(() => {
      return person.firstName + '-' +person.lastName
    }) */

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

    //返回一个对象(常用)
      return {
        person
    }
    
  }
}
</script>

image-20240113165140533

watch函数

image-20240113174918949

App.vue

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

<script>
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components: {
      Demo
    },
  }
</script>

Demo.vue

<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
  <hr>
  <h2>{{msg}}</h2>
  <button @click="msg += '!'">修改信息</button>
  <hr>
  <h2>姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <h2>薪资:{{person.job.j1.salary}}</h2>
  <button @click="person.name += '~'">修改姓名</button>
  <button @click="person.age++">增长年龄</button>
  <button @click="person.job.j1.salary++">涨薪</button>
</template>

<script>
import {ref,reactive,watch} from 'vue'
export default {
  name: 'App',
  setup() {
    //数据
    let sum = ref(0)
    let msg = ref('你好')
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

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

    //情况二:监视ref所定义的多个响应式数据
    /* watch([sum,msg],(newValue,oldValue)=>{
      console.log('sum或者msg变了',newValue,oldValue)
    },{immediate:true})  *//* immediate :一上来先执行一次 */

    /* 
      情况三:监视reactive所定义的一个响应式数据的全部属性
        注意1:此处无法正确的获取oldValue
        注意2:强制开启了深度监视(deep配置无效)  ---我测试了一下,好像deep=false的情况下,可以监视不到修改了
    */
    /* watch(person,(newValue,oldValue)=>{
      console.log('person变了',newValue,oldValue)
    },{deep:false})  //此处的deep配置无效 */


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

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

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



    //返回一个对象(常用)
      return {
        sum,
        msg,
        person
    }
    
  }
}
</script>

情况二:监视ref所定义的多个响应式数据

image-20240113170545885

情况三出现的问题

image-20240113171850725

watchEffect函数

image-20240113180931909

<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
  <hr>
  <h2>{{msg}}</h2>
  <button @click="msg += '!'">修改信息</button>
  <hr>
  <h2>姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <h2>薪资:{{person.job.j1.salary}}</h2>
  <button @click="person.name += '~'">修改姓名</button>
  <button @click="person.age++">增长年龄</button>
  <button @click="person.job.j1.salary++">涨薪</button>
</template>

<script>
import {ref,reactive,watch,watchEffect} from 'vue'
export default {
  name: 'App',
  setup() {
    //数据
    let sum = ref(0)
    let msg = ref('你好')
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    //监视
    /* watch(sum,(newValue,oldValue) => {
      console.log('sum的值变化了',newValue,oldValue)
    },{immediate: true}) */

    watchEffect(() => {
      const x1 = sum.value
      const x2 = person.job.j1.salary
      console.log('watchEffect所指定的回调执行了')
    })



    //返回一个对象(常用)
      return {
        sum,
        msg,
        person
    }
    
  }
}
</script>

2.8 生命周期

image-20240113182124384

image-20240113181843213

image-20240113183501989

image-20240113183519428

App.vue

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

<script>
  import {ref} from 'vue'
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components: {
      Demo
    },
    setup() {
      let isShowDemo = ref(true)
      return {
        isShowDemo
      }
    },
  
  }
</script>

Demo.vue

<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
</template>

<script>
import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {
  name: 'App',
  setup() {
    //数据
    let sum = ref(0)

     //通过组合式API的形式去使用生命周期钩子
     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
    }
    
  },
  
    //通过配置项的形式使用生命周期钩子
    //#region 
    /* 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----')
    } */
    //#endregion
}
</script>

image-20240113185005786

2.9 自定义hook函数

image-20240113185036825

image-20240113221406674

App.vue

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

<script>
  import {ref} from 'vue'
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components: {
      Demo
    }
  }
</script>

usePoint.js

类似于java中封装了一个函数

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
  }

  //实现鼠标“打点”相关的生命周期钩子
  onMounted(() => {
      window.addEventListener('click', savePoint)
  })

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

  return point
}

Demo.vue

<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
  <hr>
  <h2>当前点击时鼠标的坐标为: x : {{point.x}}  , y: {{point.y}}</h2>
</template>

<script>
import {ref} from 'vue'
import userPoint from '../hooks/usePoint'
export default {
  name: 'App',
  setup() {
    //数据
    let sum = ref(0)
    let point = userPoint()
    //返回一个对象(常用)
    return {
        sum,
        point
    }
    
  }
}
</script>

image-20240113221621605

2.10 toRef

image-20240113221657805

App.vue

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

<script>
  import {ref} from 'vue'
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components: {
      Demo
    }
  }
</script>

Demo.vue

<template>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}</h2>
  <button @click="name += '~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
</template>

<script>
import {reactive,toRef,toRefs} from 'vue'
export default {
  name: 'App',
  setup() {
    //数据
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    //返回一个对象(常用)
    return {
        //toRef的作用是指向person中属性的引用地址,类似于浅拷贝
        /* name:toRef(person, 'name'),
        age:toRef(person, 'age'),
        salary:toRef(person.job.j1, 'salary') */

        /* 使用toRefs进行整体返回,但是只能暴露第一层的属性 */
        ...toRefs(person)
    }
    
  }
}
</script>

image-20240113231928979

3 其他的Composition API

3.1 shallowReactive和shallowRef

image-20240114101837931

Demo.vue

<template>
  <h4>当前的x值是: {{x.p}}</h4>
  <button @click="x.p++">点我x+1</button>
  <hr>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}</h2>
  <button @click="name += '~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
</template>

<script>
import {reactive,toRef,toRefs,shallowReactive,shallowRef} from 'vue'
export default {
  name: 'App',
  setup() {
    //数据  shallow:浅层次的
    // let person = shallowReactive({ //只考虑第一层数据的响应式
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    // let x = shallowRef(0) 如果是基本数据类型,shallowRef和ref没有区别
    let x = shallowRef({  //如果是对象类型,shallowRef的value仍然是object,不是proxy对象,所以无法进行响应式
      p: 0
    })

    //返回一个对象(常用)
    return {
        /* 使用toRefs进行整体返回,但是只能暴露第一层的属性 */
        ...toRefs(person),
        x
    }
    
  }
}
</script>

image-20240114102359891

3.2 readonly与shallowReadonly

image-20240114102707617

Demo.vue

<template>
  <h4>当前求和为:{{sum}}</h4>
  <button @click="sum++">点我x+1</button>
  <hr>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}</h2>
  <button @click="name += '~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
</template>

<script>
import {reactive,toRefs,ref,readonly,shallowReadonly} from 'vue'
export default {
  name: 'App',
  setup() {
    //数据
    let sum = ref(0)
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    /* 数据不能修改,页面也不会刷新,作用场景例子:使用别人给的数据,不能修改 */
    person = readonly(person)  //不允许所有值修改
    //person = shallowReadonly(person) //不允许第一层数据修改
    sum = readonly(sum) //不允许值修改
    //sum = shallowReadonly(sum) //不允许第一层数据修改

    //返回一个对象(常用)
    return {
        /* 使用toRefs进行整体返回,但是只能暴露第一层的属性 */
        ...toRefs(person),
        sum
    }
    
  }
}
</script>

image-20240114104210681

3.3 toRaw和markRaw

image-20240114110815482

Demo.vue

<template>
  <h4>当前求和为:{{sum}}</h4>
  <button @click="sum++">点我x+1</button>
  <hr>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}</h2>
  <h2>车信息:{{person.car}}</h2>
  <button @click="name += '~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
  <button @click="showRawPerson">输出最原始的person</button>
  <button @click="addCar">给人添加一台车</button>
  <button @click="person.car.name += '!'">车换名</button>
  <button @click="changePrice ">车增值</button>
</template>

<script>
import {reactive,toRefs,ref,toRaw,markRaw} from 'vue'
export default {
  name: 'App',
  setup() {
    //数据
    let sum = ref(0)
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    function showRawPerson() {
      let p = toRaw(person)
      console.log(p)
    }

    function addCar() {
      let car = {name:'宝马',price: 40}
      person.car = markRaw(car)
    }

    function changePrice() {
      person.car.price++
      console.log(person.car.price)
    }

    //返回一个对象(常用)
    return {
        /* 使用toRefs进行整体返回,但是只能暴露第一层的属性 */
        ...toRefs(person),
        sum,
        person,
        showRawPerson,
        addCar,
        changePrice
    }
    
  }
}
</script>

image-20240114111300113

3.4 customRef

作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制。

实现防抖

App.vue

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

<script>
  import {ref,customRef} from 'vue'
  export default {
    name: 'App',
    setup() {
      //自定义一个ref---名为:myRef
      function myRef(value,delay) {
          let timer
          return customRef((track,trigger) => {
              return {
                get(){
                  console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`);
                  track() //通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
                  return value
                },
                set(newValue) {
                  console.log(`有人把myRef这个容器中的数据改为了${newValue}`)
                  clearTimeout(timer)
                  timer = setTimeout(() => {
                    value = newValue
                    trigger() //通知Vue去重新解析模板
                  }, delay);
                }
              }
          })
      }
      // let keyWord = ref('hello') 使用Vue提供的ref
      let keyWord = myRef('hello',500) //使用程序员自定义的ref

      return {
        keyWord
      }
    }
  }
</script>

image-20240114115131106

3.5 provide 和 inject

image-20240114115256600

image-20240114121615639

image-20240114121701814

App.vue

<template>
  <div class="app">
     <h3>我是App组件(祖), {{name}} --- {{price}}</h3>
     <Child></Child>
  </div>
</template>
<script>
  import {reactive,toRefs,provide} from 'vue'
  import Child from './components/Child.vue'
  export default {
    name: 'App',
    components: {
      Child
    },
    setup() {
      let Car = reactive({name: '奔驰',price: '40W'})
      provide('car', Car) //给自己的后代组件传递数据
      return {
        ...toRefs(Car)
      }
    }
  }
</script>
<style>
  .app{
    background-color: gray;
    padding: 10px;
  }
</style>

Child.vue

<template>
  <div class="child">
     <h3>我是Child组件(子)</h3>
     <Grandson></Grandson>
  </div>
</template>
<script>
  import Grandson from './Grandson.vue'
export default {
  name: 'Child',
  components: {
    Grandson
  }
}
</script>
<style>
  .child{
    background-color: skyblue;
    padding: 10px;
  }
</style>

Grandson.vue

<template>
  <div class="grandson">
     <h3>我是Grandson组件(孙), {{car.name}} -- {{car.price}}</h3>
  </div>
</template>
<script>
  import {inject} from 'vue'
  export default {
    name: 'Grandson',
    setup() {
      let car = inject('car')

      return {
        car
      }
    }
  }
</script>
<style>
  .grandson{
    background-color: orange;
    padding: 10px;
  }
</style>

image-20240114121820901

3.6 响应式数据的判断

image-20240114151154776

App.vue

<template>
  <div class="app">
     <h3>我是App组件</h3>
  </div>
</template>
<script>
  import {ref,reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy} from 'vue'
  export default {
    name: 'App',
    setup() {
      let car = reactive({name: '奔驰',price: '40W'})
      let sum = ref(0)
      let car2 = readonly(car)

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

      return {
        ...toRefs(car)
      }
    }
  }
</script>
<style>
  .app{
    background-color: gray;
    padding: 10px;
  }
</style>

image-20240114152101532

4 Composition API的优势

image-20240114152429503

image-20240114152733465

5 新的组件

5.1 Fragment

image-20240114152824513

5.2 Teleport

image-20240114153045468

image-20240114155432965

App.vue

<template>
  <div class="app">
     <h3>我是App组件</h3>
     <Child></Child>
  </div>
</template>
<script>
  import Child from './components/Child.vue'
  export default {
    name: 'App',
    components: {
      Child
    },
  }
</script>
<style>
  .app{
    background-color: gray;
    padding: 10px;
  }
</style>

Child.vue

<template>
  <div class="child">
     <h3>我是Child组件</h3>
     <Grandson></Grandson>
  </div>
</template>
<script>
  import Grandson from './Grandson.vue'
export default {
  name: 'Child',
  components: {
    Grandson
  }
}
</script>
<style>
  .child{
    background-color: skyblue;
    padding: 10px;
  }
</style>

Grandson.vue

<template>
  <div class="grandson">
     <h3>我是Grandson组件</h3>
     <Dialog></Dialog>
  </div>
</template>
<script>
  import {inject} from 'vue'
  import Dialog from './Dialog.vue'
  export default {
    name: 'Grandson',
    components: {
      Dialog
    },
  }
</script>
<style>
  .grandson{
    background-color: orange;
    padding: 10px;
  }
</style>

Dialog.vue

<template lang="">
  <div>
    <button @click="isShow = true">点我弹窗</button>
    <teleport to='body' >
      <div v-if="isShow" class="mask">
        <div class="dialog">
          <h3>我是一个弹窗</h3>
          <h4>一些内容</h4>
          <h4>一些内容</h4>
          <h4>一些内容</h4>
          <button @click="isShow = false">关闭弹窗</button>
      </div>
      </div>
    </teleport>

  </div>
</template>
<script>
  import {ref} from 'vue'
 export default {
  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;
    width: 300px;
    height: 300px;
    background-color: green;
  }
</style>

image-20240114155724481

5.3 Suspense

image-20240114155815215

App.vue

<template>
  <div class="app">
     <h3>我是App组件</h3>
     <Suspense>
        <template v-slot:default>
          <Child></Child>
        </template>
        <template v-slot:fallback>
          <h3>加载中...</h3>
        </template>
     </Suspense>
  </div>
</template>
<script>
  // import Child from './components/Child.vue' //静态引入
  import {defineAsyncComponent} from 'vue' 
  const Child = defineAsyncComponent(() => import('./components/Child')) //动态引入/异步引入
  export default {
    name: 'App',
    components: {
      Child
    },
  }
</script>
<style>
  .app{
    background-color: gray;
    padding: 10px;
  }
</style>

Child.vue

<template>
  <div class="child">
     <h3>我是Child组件</h3>
     {{sum}}
  </div>
</template>
<script>
  import {ref} from 'vue'
export default {
  name: 'Child',
  async setup() {
    let sum = ref(0)
    let p = new Promise((resolve,reject) => {
      setTimeout(() => {
        resolve({sum})
      }, 1000)
    })
    return await p
  }
}
</script>
<style>
  .child{
    background-color: skyblue;
    padding: 10px;
  }
</style>

image-20240114162254030

image-20240114162311745

image-20240114162328012

6 其他

6.1 全局API的转移

image-20240114162502408

image-20240114162539357

6.2 其他改变

image-20240114162956392

image-20240114163329428

posted @ 2024-01-20 11:12  千夜ん  阅读(9)  评论(0编辑  收藏  举报
1 2 3 4