vue3(撩课)笔记05-setup,ref,reactive,readonly,toRefs,toRef,context,计算属性新用法,watch,watchEffect,provide

1、组合式API(composition API)概念
  • 选项式API(options API)随着开发会日益臃肿,且数据分散
  • 组合式API数据集中不分散,比较好维护
  • 组合式API主要是以函数的形式呈现的
2、组合式API基本语法–setup
  • setup可以理解为一个调度仓库,以往的data,computed,methods,watch都可以写在这里
  • 取代了beforeCreate()和created()钩子函数
 setup(props,context){
      return {
        msg:'测试文本',
        say:()=>{
          console.log('张三凉透了')
        }
      }
   },

案例:

<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   //组合式API,props属性,cotext上下文
   //不仅可以返回数据,还可以返回方法
   //setup可以理解为一个调度仓库,以往的data,computed,methods,watch都可以写在这里
   setup(props,context){
      return {
        msg:'测试文本',
        say:()=>{
          console.log('张三凉透了')
        }
      }
   },

   template:`
    <div>
      <h1>{{msg}}</h1>
      <button @click="say">张三怎么了</button>
    </div>

   `

 })

app.mount('#app')
</script>
</body>
  • setup不能调用声明周期的所有函数,只是部分
  • setup存在$options的里
<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   //组合式API,props属性,cotext上下文
   //不仅可以返回数据,还可以返回方法
   //setup可以理解为一个调度仓库,以往的data,computed,methods,watch都可以写在这里
   setup(props,context){
      return {
        msg:'测试文本',
        say:()=>{
          console.log('张三凉透了')
        }
      }
   },
    methods:{
      deal(){
        console.log(this.$options.setup())
      }
    },
   template:`
    <div>
      <h1>{{msg}}</h1>
      <button @click="say">张三怎么了</button>
      <button @click="deal">dela</button>
    </div>
   `
 })

app.mount('#app')
</script>
</body>
3、组合式API–ref基础类型具备相应式
  • ref:让基础类型的数据具备响应式
  • 使用ref(基础类型数据)进行包装,包装后得到一个proxy对象
  • 要取到这个基础类型的值必须动过包装后的对象.value来获取
<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({

   // data(){
   //   return {
   //     msg:100
   //   }
   // },
   //引入ref
   setup(props,context){
     //底层通过proxy代理,具备了响应式的引用
     const {ref}=Vue;//从Vue静态类中拿到ref
     // let msg=ref(100)
     let msg=ref('hello')
     setTimeout(()=>{
       msg.value='qinghua daxue'
     },2000)
      return {
        msg,

      }

   },

   template:`
    <div>
      <h1>{{msg}}</h1>
    </div>
   `

 })

app.mount('#app')
</script>
</body>

又例

<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     const {ref}=Vue
     let msg=ref(100)
     
     return {
       msg,
       add(){
         msg.value++
       }
     }

   },
   template:`<h3>{{msg}}</h3><br/><button @click="add">add</button>`


 })

app.mount('#app')
</script>
</body>
4、组合式API–reactive让引用类型具有响应式

reactive让引用类型具有响应式

<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue中结构出reactive
     const {reactive}=Vue
     //用reactiove包装一下数据,到到一个pobj对象
     let pobj=reactive({ name:'zhangsan',age:34,address:'beijing' })
     let pArray=reactive(['qinghua','beida','hagongda'])

     return {
       //返回这个pobj包装好的对象
       pobj,
       pArray,
       add(){
         console.log(pobj)
         pobj.name='lisi'
         pArray[0]='beihang'
       }
     }

   },
   //在模板中象一个正常的对象一样对其进行遍历及使用
   template:`<h3 v-for="(item,index) in pobj" :key="index">
      {{item}}
      </h3>
      <br/>
      <h3 v-for="item in pArray">
      {{item}}
      </h3>
      <div>{{pobj.name}} {{pobj.age}} {{pobj.address}}</div>
      <div>{{pArray[0]}} {{pArray[1]}} {{pArray[2]}}</div>
      <button @click="add">add</button>`

 })

app.mount('#app')
</script>
</body>
5、组合式API–readonly只读,单向传递数据
  • 单向传递数据,实际就是只读,readonly包装后只允许展现,不允许修改数据
<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue中结构出reactive
     const {readonly}=Vue
     //用reactiove包装一下数据,到到一个pobj对象
     let pobj=readonly({ name:'zhangsan',age:34,address:'beijing' })

     return {
       //返回这个pobj包装好的对象
       pobj,

       add(){
         console.log(pobj)
         pobj.name='lisi'//vue会报无法修改,因为是readonly
       }
     }

   },
   //在模板中象一个正常的对象一样对其进行遍历及使用
   template:`<h3 v-for="(item,index) in pobj" :key="index">
      {{item}}
      </h3>
      <br/>
      <div>{{pobj.name}} {{pobj.age}} {{pobj.address}}</div>
      <button @click="add">add</button>`

 })

app.mount('#app')
</script>
</body>
6、组合式API–toRefs从对象中结构属性

步骤:

  • 1、从vue中解构出reactive及toRefs
  • 2、使用reactive及toRefs包装出一个对象obj
  • 3、使用{属性1,属性2,…}=obj解构出这个对象的属性,并用结构出来的toRefs包装这个obj对象
  • 4、在setup中返回这些属性
  • 5、模板中使用这些属性
<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue中解构出reactive及toRefs
     const {reactive,toRefs}=Vue
     //用reactiove包装一下数据,到到一个pobj对象
     let pobj=reactive({ name:'zhangsan',age:34,address:'beijing' })
     //从对象中解构属性
     const {name,age,address}=toRefs(pobj)

     return {
       //返回结构出来的三个属性
       name,
       age,
       address,

       add(){
         console.log(pobj)
         pobj.name='lisi'//可以顺利修改,并及时反映到页面
       }
     }

   },

   template:`
      <div>{{name}} {{age}} {{address}}</div>
      <button @click="add">add</button>`

 })

app.mount('#app')
</script>
</body>
7、组合式API–toRef

toRef针对一些可传可不传,最后很可能不传的数据,在引用是不会因为没有传过来而保存
步骤

  • 通过Vue把reactive,toRefs,toRef解构出来
  • 再把数据用reactive包装成对象,这时候可能出现数据传过来不完整的情形(这也正是toRef起作用的地方)
  • 再使用toRefs(对象)解构出对象的属性(这个属性应当是必传属性)
  • 再使用toRef包装可能取不到(没传来)的数据,包装成一个对象toRef(pobj,‘major’)
  • 再通过return返回所有必传,可能传的属性
  • 最后在模板中使用这些属性
<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue中解构出reactive及toRefs
     const {reactive,toRefs,toRef}=Vue
     //用reactiove包装一下数据,到到一个pobj对象
     let pobj=reactive({ name:'zhangsan',age:34,address:'beijing'})
     //从对象中解构属性(必传属性)
     let {name,age,address}=toRefs(pobj)
     //从对象中结构可能没有传过来的属性
     let major=toRef(pobj,'major')


     return {
       //返回结构出来的三个属性
       name,
       age,
       address,
       major,

       add(){
         console.log(pobj)
         pobj.name='lisi'//可以顺利修改,并及时反映到页面
         pobj.major='news'//可以顺利修改,但无法反映到页面上,如果没有toRef会提示major没有定义
       }
     }

   },

   template:`
      <div>{{name}} {{age}} {{address}} {{major}}</div>
      <button @click="add">add</button>`

 })

app.mount('#app')
</script>
</body>
8、组合式API–context上下文环境
  • context大致包含了父组件传给子组件的$attrs,slot插槽,emit发射的事件可以通过context将这三者解构出来 const {attrs,slot,emit}=context

1、解构attrs案例

<body>
<div id="app">
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){

   },

   template:`
      <subcom gender='female' style="background-color: red; width: 100px; height: 100px"></subcom>
   `

 })
app.component('subcom',{
  setup(props,context){
    //解构出所有属性,插槽,发射出去的自定义事件
    const {attrs,slot,emit}=context
    console.log(attrs)//打印出一个proxy代理对象
    console.log(attrs.style)
    console.log(attrs.gender)


  },
  template: `<div style="attrs.style">我是子组件</div>`
})
app.mount('#app')
</script>
</body>

2、解构slot案例
通过slots.default()获取插槽中插入的内容

<body>
<div id="app">
  <subcom>
    <button>我是补位的btn</button>
  </subcom>
  <subcom>
    <input type="text"/>
    <button>我是补位的btn</button>
  </subcom>
</div>
<script>
  //这里是根组件
 const app=Vue.createApp({

 })
app.component('subcom',{
  setup(props,context){
    //解构出所有属性,插槽,发射出去的自定义事件
    const {attrs,slots,emit}=context
    console.log(slots)//打印出一个proxy
    //通过slots.default()获取插槽中插入的内容(实际上是虚拟dom对象)
    console.log(slots.default())
    //可以进一步的取内容
    console.log(slots.default()[0].type)

  },
  template: `
    <slot>默认内容</slot>
    <div >我是子组件</div>`
})
app.mount('#app')
</script>
</body>

3、解构emit案例
使用从上下文结构出来的emit来替代$emit

<body>
<div id="app">
  <mysubcom @sub-Click="deal"></mysubcom>


</div>
<script>

  const mysub={
    setup(props,context){
      //从context中解构出emit
      const {emit}=context;
      //取代了原有的methods内容
      function btnClick () {
        alert('你点击了')
        const dataObj={
          name:'my course',
          intro:'some course about vue from youyuxi'
        }
        //emit替代了原有的this.$emit
        emit('subClick',dataObj)
      }
      //setup里面定义的函数一定要通过return 返回出去
      return {
        btnClick
      }

    },
    template: `
    <div style="background-color: red; width: 200px; height: 200px" >
      <button @click="btnClick">我要发射事件</button>
    </div>`
  }
  //这里是根组件
 const app=Vue.createApp({
   data(){
     return{
       msg:''
     }
   },
   components:{
     'mysubcom':mysub
   },
   methods:{
     deal(item) {
        console.log('子组件中的按钮发生了点击')
        console.log(item)
     }
   }

 })

app.mount('#app')
</script>
</body>
9、组合式API–计算属性新用法1

计算属性写在setup里面
步骤
1、从vue中结构出ref和computed
2、使用computed函数创建一个comouted计算属性
3、使用return返回这个计算属性
4、在模板中或者页面中使用这个计算属性

<body>
<div id="app">

</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数
     const {ref,computed}=Vue
      //自定义一个变量,并用ref包装一下
     let num1 = ref(10);
     //使用刚才结构出来的computed函数定义一个计算属性
     let num2=computed(()=>{
       return num1.value *10
     })

     //定义给一个函数
     const add=()=>{
       num1.value+=10
     }
     return {
       num1,
       num2,
       add,
     }

   },

   template:`
     <h1>普通属性num1:{{num1}}</h1>
     <h1>计算属性num2:{{num2}}</h1>
     <p>-----</p>
     <button @click="add">累加</button>
   `


 })

app.mount('#app')
</script>
</body>
10、组合式API–计算属性新用法2使用get和set

使用getter和set来双向绑定值(获取和改变值)
步骤

  • 在computed函数中传入一个get/set对象
  • 再修改计算属性的值,则会触发set对应的函数
<body>
<div id="app">

</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数
     const {ref,computed}=Vue
      //自定义一个变量,并用ref包装一下
     let num1 = ref(10);
     //使用刚才结构出来的computed函数定义一个计算属性
     let num2=computed({
       get: ()=>{return num1.value *10},
       set:(res)=>{
         console.log(res)
         num1.value=res / 10
       }
     })

     setTimeout(()=>{
       num2.value=500
     },2000)

     //定义给一个函数
     const add=()=>{
       num1.value+=10
     }
     return {
       num1,
       num2,
       add,
     }

   },

   template:`
     <h1>普通属性num1:{{num1}}</h1>
     <h1>计算属性num2:{{num2}}</h1>
     <p>-----</p>
     <button @click="add">累加</button>
   `


 })

app.mount('#app')
</script>
</body>
11、组合式API–计算属性新用法3针对引用数据类型
  • 直接使用reactive解构数据即可
  • 通过对象.属性来get或者set值
<body>
<div id="app">

</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数
     const {reactive,computed}=Vue
      //自定义一个变量,并用ref包装一下
     let numobj = reactive({ count:10 });
     //使用刚才结构出来的computed函数定义一个计算属性
     let num2=computed({
       get: ()=>{return numobj.count *10},
       set:(res)=>{
         console.log(res)
         numobj.count=res / 10
       }
     })

     setTimeout(()=>{
       numobj.count=500
     },2000)

     //定义给一个函数
     const add=()=>{
       numobj.count+=10
     }
     return {
       numobj,
       num2,
       add,
     }

   },

   template:`
     <h1>普通属性num1:{{numobj.count}}</h1>
     <h1>计算属性num2:{{num2}}</h1>
     <p>-----</p>
     <button @click="add">累加</button>
   `


 })

app.mount('#app')
</script>
</body>
12、组合式API–watch侦听器1-侦听简单数据类型
<body>
<div id="app">

</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数
     const {ref,watch}=Vue
     let brand=ref('')
     let url=ref('')

     //创建一个侦听器,brand为侦听对象
     //  watch(brand,(currentValue,preValue)=>{
     //    //相应业务逻辑
     //    console.log('当前的值'+currentValue)
     //    console.log('之前的值'+preValue)
     //  },{})
     //侦听多个属性的写法
     watch([brand, url],([currentbrand,currenturl],[prebrand,preurl])=>{
       //相应业务逻辑
       console.log('当前的值',currentbrand,currenturl)
       console.log('之前的值'+prebrand,preurl)
     },{})
     return {
       brand,
       url
     }

   },

   template:`
     <div>
     品牌:<input v-model="brand"><br/>
     输入的品牌是:{{brand}}<br/>
      网址:<input v-model="url"><br/>
     输入的网址是:{{url}}
     </div>
   `
 })

app.mount('#app')
</script>
</body>
13、组合式API–watch侦听器2-侦听引用数据类型
<body>
<div id="app">

</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数
     const {ref,reactive,watch,toRefs}=Vue
     let eduobj=reactive({name:'zhangsan',age:32})

     //创建一个侦听器,brand为侦听对象,侦听对象的写法请注意
      watch(()=>eduobj.name,(currentValue,preValue)=>{
        //相应业务逻辑
        console.log('当前的值'+currentValue)
        console.log('之前的值'+preValue)
      },{})

     const {name,age}=toRefs(eduobj)


     return {
       name,
       age
     }

   },

   template:`
     <div>
     品牌:<input v-model="name"><br/>
     输入的品牌是:{{name}}<br/>
      网址:<input v-model="age"><br/>
     输入的网址是:{{age}}
     </div>

   `
 })

app.mount('#app')
</script>
</body>
14、组合式API–watchEffect侦听器副作用函数

特点:

  • 没有惰性(数据改变才侦听)
  • 更加抽象(侦听所有属性)
  • 不能访问先前值()
  • 可配置的(是否非惰性,深度监视(多层监视))
<body>
<div id="app">

</div>
<script>
  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数
     const {ref,reactive,watch,toRefs,watchEffect}=Vue
     let brand = ref('')
     let url = ref('')

     let obj=reactive({
       cc:'2121',
       arr:[{
         name:'zhangsan'
       }]
     })
     //创建一个侦听器,brand为侦听对象,侦听对象的写法请注意
      watch(brand,(currentValue,preValue)=>{
        //相应业务逻辑
        console.log('当前的值'+currentValue)
        console.log('之前的值'+preValue)
      },{
        //是否立即监视,还是发生变化才侦听
        immediate:true,
        //是否深度监视
        deep:true
      })
      //侦听器副作用函数,watchEffect侦听了整个实例的属性
     watchEffect(()=>{
       console.log('开始侦听了!')
       console.log(brand.value)//侦听到后,直接取值
       console.log(url.value)//侦听到后,直接取值
     })
     return {
       brand,
       url
     }

   },

   template:`
     <div>
     品牌:<input v-model="brand"><br/>
     输入的品牌是:{{brand}}<br/>
      网址:<input v-model="url"><br/>
     输入的网址是:{{url}}
     </div>

   `
 })

app.mount('#app')
</script>
</body>
15、组合式API–provide和inject1
  • 不是父子组件,而是孙子组件的数据传递,可以跨多级传递数据,而不用一级一级的传递
  • provide把孙子组件的数据发射出去,inject则是接受发射来的数据
  • 发射出去的数据依然要在接收位置的通过return返回出去
<body>
<div id="app">

</div>
<script>
  //创建孙组件
  const subson={
    setup(){
      //把inject结构出来
      const {inject}=Vue
      //订阅数据
      const gname=inject('brand','默认值')
      const gcollege=inject('college')

      return{
        gname,
        gcollege
      }


    },
    template:`<div style="width: 200px; height: 200px; background-color: red">
    我是孙组件<br/>
    {{gname}}---{{gcollege.city}}---{{gcollege.url}}
    </div>`
  }

  //创建父组件(也是根组件的儿子)
  const subfather={
    components:{
      'subson':subson
    },
    template: `
      <div style="width: 400px; height: 400px; background-color: green">
        这个是父组件
        <subson></subson>
      </div>
    `
  }

  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数,provide发射出去,inject接收过来
     const {ref,reactive,provide,inject}=Vue
     let brand = ref('海尔')
     let college = reactive({city:'上海',url:'www.sina.com' })

     //发射(发布)根组件的数据到子组件
     //把数据brand以brand的名字发射出去
     provide('brand',brand)
     provide('college',college)

     return {

     }

   },
    components: {
     'subfather':subfather
    },
   template:`
    <div>我是根组件</div>
    <subfather></subfather>
   `
 })

app.mount('#app')
</script>
</body>
16、组合式API–provide和inject2规避孙子组件修改根组件值(因为要保证单向数据流)
  • 规避孙子组件修改根组件值(因为要保证单向数据流)要从根组件这个源头来进行规避,在解构的时候就要设置readonly
  • 数据源发射的时候要指定readonly()
  • 如果非要修改,也要在数据源头设定一个修改函数并发射出去
  • 同时也要在孙子组件(使用位置)订阅这个函数,并且需要return出去
  • 孙子组件通过函数的参数来传值
<body>
<div id="app">

</div>
<script>
  //创建孙组件
  const subson={
    setup(){
      //把inject结构出来
      const {inject}=Vue
      //订阅数据
      const gname=inject('brand','默认值')
      const gcollege=inject('college')
      const changeBrand=inject('changeBrand')
      const changeNum=()=>{
        // gname.value='格力'
        changeBrand('小米')
      }
      return{
        gname,
        gcollege,
        changeNum,
        changeBrand
      }


    },
    template:`<div style="width: 200px; height: 200px; background-color: red">
    我是孙组件<br/>
    {{gname}}---{{gcollege.city}}---{{gcollege.url}}<br/>
    <button @click="changeNum">修改</button>
    </div>`
  }

  //创建父组件(也是根组件的儿子)
  const subfather={
    components:{
      'subson':subson
    },
    template: `
      <div style="width: 400px; height: 400px; background-color: green">
        这个是父组件
        <subson></subson>
      </div>
    `
  }

  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数,provide发射出去,inject接收过来
     const {ref,reactive,provide,inject,readonly}=Vue
     let brand = ref('海尔')
     let college = reactive({city:'上海',url:'www.sina.com' })

     //如果非要更改,也要在源头改
     const changeBrand=(params)=>{
       brand.value=params
     }

     //发射(发布)根组件的数据到子组件
     //把数据brand以brand的名字发射出去
     //reanonly设定了不可更改
     provide('brand',readonly(brand))
     provide('college',college)
     //改变数据的方法也要发射出去
     provide('changeBrand',changeBrand)

     return {

     }

   },
    components: {
     'subfather':subfather
    },
   template:`
    <div>我是根组件</div>
    <subfather></subfather>

   `
 })

app.mount('#app')
</script>
</body>
17、组合式API-生命周期钩子的新写法
  • 在原有钩子函数的名字前面加on

beforeMouted----onBeforeMouted
mouted—onMouted

  • 用到的钩子函数都需要提前从vue里解构出来

    const {ref,onBeforeMount}=Vue

<body>
<div id="app">

</div>
<script>


  //这里是根组件
 const app=Vue.createApp({
   setup(props,context){
     //从vue里结构一个computed函数,provide发射出去,inject接收过来
     const {ref,onBeforeMount,onMounted}=Vue
     let msg=ref('hello world')

     onBeforeMount(()=>{
       console.log('onBeforeMount')
     })
     onMounted(()=>{
       console.log('onMount')
     })

     return {
       msg,

     }

   },

   template:`
    <div>我是根组件</div>
   `
 })

app.mount('#app')
</script>
</body>

更多的钩子函数:略

18、、组合式API-获取真实DOM

步骤

  • 从vue中结构出onmouted函数、ref,视情况解构出来
  • 使用ref包装数据
  • 在页面标签中通过ref="数据名"进行关联绑定
  • 通过钩子或其他函数对包装且绑定后的对象获取.value属性值,拿到其真实的dom
  • 一定要把包装好并绑定后的对象return出去
    案例
<body>
<div id="app">
  <div ref="brand">撩课学院</div>
  <div><input ref="abc" type="text"/> </div>
  <div ref="url">www.sina.cn</div>
</div>
<script>


  //这里是根组件
 const app=Vue.createApp({
   setup(){
     //从vue里结构一个computed函数,provide发射出去,inject接收过来
     const {onMounted,ref}=Vue
     const brand=ref(null)
     const url=ref(null)
     const abc=ref(null)

     onMounted(()=>{
       console.log(brand.value)
       console.log(url.value)
       console.log(abc.value)
     })

     return {
       brand,
       url
     }

   },

 })

app.mount('#app')
</script>
</body>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值