Part3-5-2 Composition API

createApp

createApp 的作用是创建一个 vue 对象,它可以接收一个选项作为一个参数,也就是一个组件的选项,和vue2中给构造函数传入的选项一样,可以传入 data、methods、computed、created等选项。

data不支持对象的写法,data必须是函数。

<script type="module">
    import { createApp } from './node_modules/vue/dist/vue.esm-browser.js'

    const app = createApp({
      // setup 需要返回一个对象,可以使用在模板、methods、computed、以及生命周期的钩子函数中
      // setup执行的时机:props解析完毕,组件实例被创建之前执行。
      // 因此在 setup 内部,无法通过 this 获取到组件的实例,因为组件实例还未被创建。
      // setup 中的 this 此时指向 undefined
      setup () {
        // 第一个参数 props, props 的作用是用来接收外部传入的参数,props 是一个响应式的对象,但是不能被解构
        // 第二个参数 context,context 是一个对象,它具有三个成员:attrs、emit、slots
        
        const position = {
            x: 0,
            y: 0
        }

        return {
            position // 将来可以在模板以及组件的其他位置,使用这个对象,但此时并不是响应式对象
        }
      }
    })
    console.log(app)

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

reactive

reactive 的作用是把一个对象转换成响应式对象,并且该对象的嵌套对象也都转换成响应式对象。

它返回的是一个 proxy 对象。

<script type="module">
    import { createApp, reactive } from './node_modules/vue/dist/vue.esm-browser.js'

    const app = createApp({
      setup () {
        // 使用 reactive 将对象转换成响应式对象
        const position = reactive({
            x: 0,
            y: 0
        })

        return {
            position
        }
      }
    })
    console.log(app)

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

生命周期钩子函数

setup 是在 beforeCreate 和 created 之间执行的。

onRenderTracked 和 onRenderTriggered 在 render 函数被重新调用时触发。

onRenderTracked 在 render 函数首次调用时也会触发。

onRenderTriggered 在 render 函数首次调用时不会触发。

toRefs

const { x, y } = useMousePosition()

将 x,y 从 useMousePosition() 中解构出来,x,y 不是响应式数据。

这是因为 

const position = reactive({
        x: 0,
        y: 0
})

这里使用 reactive 将 position 包装成了 proxy 对象,当调用 position.x,position.y时,会调用代理中的 get 方法拦截并收集依赖。当 position.x,position.y 发生变化时,会调用 set 拦截并触发更新。

当把代理对象解构的时候,就相当于定义了x,y这两个变量来接收positon.x,position.y,而基本类型的赋值就是把值在内存中复制一份,所以这里的x,y只是2个基本类型的变量,和代理对象无关。

toRefs 可以把一个响应式对象中的属性也转换成响应式的。

toRefs 要求传入的参数必须是一个代理对象。

toRefs 原理:内部会创建一个新的对象,遍历传入的代理对象的所有属性,把所有属性的值都转换成响应式对象,

并且属性是一个对象,具有 value 属性,在模板中使用,可以将value省略,但是在代码中不能省略。

然后挂载到新创建的对象上,再将新创建的对象返回。

<div id="app">
    x: {{ x }} <br>
    y: {{ y }}
</div>
  
<script type="module">
    import { createApp, reactive, onMounted, onUnmounted, toRefs } from './node_modules/vue/dist/vue.esm-browser.js'

    function useMousePosition () {
      const position = reactive({
        x: 0,
        y: 0
      })

      const update = e => {
        position.x = e.pageX
        position.y = e.pageY
      }

      onMounted(() => {
        window.addEventListener('mousemove', update)
      })

      onUnmounted(() => {
        window.removeEventListener('mousemove', update)
      })

      return toRefs(position)
    }

    const app = createApp({
      setup () {
        // const position = useMousePosition()
        const { x, y } = useMousePosition()
        return {
          x,
          y
        }
      }
    })
    console.log(app)

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

ref

ref的作用是将基本类型的数据包装成响应式对象。

而 reactive 是将一个对象转换成响应式数据。

如果 ref 传入的是对象时,内部会去调用 reactive 返回一个代理对象。

如果 ref 传入的是基本类型数据时,会创建一个只有 value 属性的对象。

<div id="app">
    <button @click="increase">按钮</button>
    <span>{{ count }}</span>
  </div>
  <script type="module">
    import { createApp, ref } from './node_modules/vue/dist/vue.esm-browser.js'
    
    function useCount () {
      const count = ref(0)
      return {
        count,
        increase: () => {
          count.value++
        }
      }
    }

    createApp({
      setup () {
        return {
          ...useCount()
        }        
      }
    }).mount('#app')
  </script>

reactive  vs  ref

ref 可以把基本数据类型数据,转成响应式对象

ref 返回的对象,重新赋值成对象也是响应式的

reactive 返回的对象,重新赋值丢失响应式

reactive 返回的对象不可以解构

computed

<div id="app">
    <button @click="push">按钮</button>
    未完成:{{ activeCount }}
  </div>
  <script type="module">
    import { createApp, reactive, computed } from './node_modules/vue/dist/vue.esm-browser.js'
    const data = [
      { text: '看书', completed: false },
      { text: '敲代码', completed: false },
      { text: '约会', completed: true }
    ]

    createApp({
      setup () {
        const todos = reactive(data)

        const activeCount = computed(() => {
          return todos.filter(item => !item.completed).length
        })

        return {
          activeCount,
          push: () => {
            todos.push({
              text: '开会',
              completed: false
            })
          }
        }
      }
    }).mount('#app')
  </script>

Watch

Watch  的三个参数:

  • 第一个参数:要监听的数据,可以是一个获取值的函数,监听这个函数返回值的变化、或者是一个ref, 或者是 reactive 返回的对象,或者是数组
  • 第二个参数:监听到数据变化后执行的函数,这个函数有两个参数分别是新值和旧值
  • 第三个参数;选项对象,deep 和 immediate

Watch 的返回值:

  • 取消监听的函数
<div id="app">
    <p>
      请问一个 yes/no 的问题:
      <input v-model="question">
    </p>
    <p>{{ answer }}</p>
  </div>

  <script type="module">
    // https://www.yesno.wtf/api
    import { createApp, ref, watch } from './node_modules/vue/dist/vue.esm-browser.js'

    createApp({
      setup () {
        const question = ref('')
        const answer = ref('')

        watch(question, async (newValue, oldValue) => {
          const response = await fetch('https://www.yesno.wtf/api')
          const data = await response.json()
          answer.value = data.answer
        })

        return {
          question,
          answer
        }
      }
    }).mount('#app')
  </script>

WatchEffect

  • 是 watch 函数的简化版本,也用来监视数据的变化,内部实现是和 Watch 调用的同一个函数,不同的是,watchEffect 没有第二个回调函数的参数,watchEffect 会接收一个 函数 作为参数,它会监听 函数内部响应式数据的变化,它会立即执行一次这个函数,当数据发生变化后,会重新运行该函数,返回一个取消监听的函数
  • 接收一个函数作为参数,监听函数内响应式数据的变化
<div id="app">
    <button @click="increase">increase</button>
    <button @click="stop">stop</button>
    <br>
    {{ count }}
  </div>

  <script type="module">
    import { createApp, ref, watchEffect } from './node_modules/vue/dist/vue.esm-browser.js'

    createApp({
      setup () {
        const count = ref(0)
        const stop = watchEffect(() => {
          console.log(count.value)
        })

        return {
          count,
          stop,
          increase: () => {
            count.value++
          }
        }
      }
    }).mount('#app')
  </script>

Effect

effect 使用方法和 watchEffect 一样,watchEffect 内部就是使用了 effect

effect 首先会执行一次,当 effect 中的响应式数据发生变化时,effect会再次执行

<script type="module">
    import { reactive, effect } from './reactivity/index.js'

    const product = reactive({
      name: 'iPhone',
      price: 5000,
      count: 3
    })
    let total = 0 
    effect(() => {
      total = product.price * product.count
    })
    console.log(total)

    product.price = 4000
    console.log(total)

    product.count = 1
    console.log(total)

  </script>

获取DOM

<template>
  <h1 ref="title">
    {{ msg }}
  </h1>
  <comp ref="comp" />
</template>

<script lang="ts">
import { ref, defineComponent, onMounted, PropType } from 'vue'
import comp from './comp.vue'

interface User {
  name: string
  age: number
}

export default defineComponent({
  name: 'HelloWorld',
  components: {
    comp
  },
  props: {
    msg: {
      type: String,
      required: true
    },
    obj: {
      type: Object as PropType<User>,
      default: () => {}
    }
  },
  setup (prop) {
    onMounted(() => {
      console.log('title', title.value)
    })

    const title = ref<HTMLHeadElement | null>(null)

    const component = ref<InstanceType<typeof comp> | null>(null)

    return {
      title,
      component
    }
  }
})
</script>

<script setup> 语法

Composition API 的语法糖

<template>
  <h1>{{ msg }}</h1>
  <button
    type="button"
    @click="increment"
  >
    count is: {{ count }}
  </button>
</template>

<script setup lang="ts">
import { ref, defineProps, defineEmits } from 'vue'

const props = defineProps({
  msg: {
    type: String,
    default: ''
  }
})

const emit = defineEmits(['increment'])

const count = ref(0)

const increment = () => {
  console.log(props.msg)
  count.value++
  emit('increment')
}
</script>

使用tsx

.vue 文件

<template>
  <h1>hello world</h1>
  <abc />
</template>

<script setup lang="tsx">
const abc = <h1>abc</h1>
</script>

.tsx 文件

Options API

import { defineComponent } from '@vue/runtime-core'


export default defineComponent({
  props: {
    msg: {
      type: String,
      required: true
    }
  },

  data() {
    return {
      count: 0
    }
  },

  render () {
    return (
      <div>
        <div>{ this.msg }</div>
        <div>{ this.count }</div>
      </div>
    )
  }
})

Composition API

import { defineComponent, ref } from '@vue/runtime-core'

interface PropsType {
  msg: string
}

export default defineComponent({
  props: {
    msg: {
      type: String,
      required: true
    }
  },

  setup() {
    const count = ref(0)
    return (props: PropsType) => (
      <div>
        <p>{ props.msg }</p>
        <p>{ count.value }</p>
      </div>
    )
  }
})

1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值