Vue3学习第五天

263 阅读4分钟

Composition API

Setup函数的使用

setup:在created实例被完全初始化之前执行,接收两个参数props,context,最后一定要return出去才能使用

props:外部组件传递过来的一些内容

context:指的是上下文

<body>
  <div id="root">
  </div>
  <script>
    // data & methods & computed & watcher
    const app = Vue.createApp({
      template: `
        <div @click="vueClick">{{name}}</div>
      `,
      // created 实例被完全初始化之前
      // props 外部组件传递过来的内容
      // context 指的是上下文
      setup(props, context) {
        return {
          name: 'setup学习',
          vueClick: () => {
            alert('vue3的setup学习')
          }
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

fagnfa.png

我们发现我们直接在setupreturn的name,vueClick方法,在页面上可以直接使用并展示出来。

额外补充: 因为setup是在created实例化初始化之前被执行,所以methods中方法在setup中调用会报错

<body>
  <div id="root">
  </div>
  <script>
    // data & methods & computed & watcher
    const app = Vue.createApp({
      template: `
        <div @click="vueClick">{{name}}</div>
      `,
      methods: {
        methodClick() {
          alert('methods')
        }
      },
      // created 实例被完全初始化之前
      // props 外部组件传递过来的内容
      // context 指的是上下文
      setup(props, context) {
        this.methodClick()
        return {
          name: 'setup学习',
          vueClick: () => {
            alert('vue3的setup学习')
          }
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

methods.png

结论:因为setup是在实例初始化之前执行,此时methods并没有放到this上面,所以我们通过this.methodClick()是无法调用methods的,而且我们用了setup之后,就不会再使用this了。

但是我们可以在外部的methodsmounted中去调用setup,代码如下

<body>
  <div id="root">
  </div>
  <script>
    // data & methods & computed & watcher
    const app = Vue.createApp({
      template: `
        <div @click="vueClick">{{name}}</div>
      `,
      methods: {
        methodClick() {
          console.log('看看组件上挂载了什么',this.$options)
          console.log('看看setup返回了什么',this.$options.setup())
        }
      },
      mounted() {
        this.methodClick()
      },
      // created 实例被完全初始化之前
      // props 外部组件传递过来的内容
      // context 指的是上下文
      setup(props, context) {
        // this.methodClick()
        return {
          name: 'setup学习',
          vueClick: () => {
            alert('vue3的setup学习')
          }
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

mounted.png

ref的使用

首先如果我们实现2s后将ref改变为vue3中的ref代码如下:

<body>
  <div id="root">
  </div>
  <script>
    // ref
    const app = Vue.createApp({
      template: `
        <div>{{name}}</div>
      `,
      setup(props, context) {
        let name = 'ref'
        setTimeout(() => {
          name = 'vue3中的ref'
        }, 2000)
        return {
          name,
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

我们发现这样写,2s后name的值不会改变,控制台结果如下:

ref.png

因为name就是一个普通的变量,而不是一个响应式的变量,所以name变了页面也不会改变

ref响应式的引用 原理:是通过proxy对数据进行封装,当数据变化时,触发模板等内容的更新,ref是处理基础类型数据

<body>
  <div id="root">
  </div>
  <script>
    // ref 处理基础类型数据
    const app = Vue.createApp({
      template: `
        <div>{{name}}</div>
      `,
      setup(props, context) {
        //  proxy, 将'ref'变成proxy({value: 'vue3中的ref'})这样的一个响应式引用
        // 底层就是做了proxy的封装,封装成({value: 'vue3中的ref'})
        const { ref } = Vue;
        let name = ref('ref')
        setTimeout(() => {
          name.value = 'vue3中的ref'
        }, 2000)
        return {
          name,
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

控制台显示结果:

ref1.png

结论:vue中给我们提供了ref方法,当我们使用ref处理基础类型数据时,需要先引入ref,而vue底层通过proxy进行了封装,封装成({value: 'vue3中的ref'}),处理后我们在组件中不需要再用name.value去调用了,vue给我们做了处理,我们在组件中直接可以{{name}}去使用。

我们还可以将上面的方法换成另一种写法: 首先如果我们实现2s后将reactive改变为vue3中的reactive代码如下:

reactive的使用

<body>
  <div id="root">
  </div>
  <script>
    // reactive 处理非基础类型数据
    const app = Vue.createApp({
      template: `
        <div>{{nameObj.name}}</div>
      `,
      setup(props, context) {
        const { reactive } = Vue;
        const nameObj = { name: 'reactive' }
        setTimeout(() => {
          nameObj.name = 'vue3中的reactive'
        }, 2000)
        return {
          nameObj,
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

我们发现这样写,2s后name的值不会改变,控制台结果如下:

reactive.png

因为name就是一个普通的变量对象,而不是一个响应式的变量,所以name变了页面也不会改变

reactive响应式的引用 原理:是通过proxy对数据进行封装,当数据变化时,触发模板等内容的更新,reactive是处理非基础类型数据

<body>
  <div id="root">
  </div>
  <script>
    // reactive 处理非基础类型数据
    const app = Vue.createApp({
      template: `
        <div>{{nameObj.name}}</div>
      `,
      setup(props, context) {
        const { reactive } = Vue;
        //  proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
        const nameObj = reactive({ name: 'reactive' })
        setTimeout(() => {
          nameObj.name = 'vue3中的reactive'
        }, 2000)
        return {
          nameObj,
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

控制台显示结果: reac1.png

结论:vue中给我们提供了reactive方法,当我们使用reactive处理非name基础类型数据时,需要先引入reactive,而vue底层通过proxy进行了封装,封装成({name: 'vue3中的reactive'}),处理后我们在组件中可以直接用{{nameObj.name}}去使用。

reactive上面处理的是对象我们还可以处理数组

<body>
  <div id="root">
  </div>
  <script>
    // reactive 处理非基础类型数据
    const app = Vue.createApp({
      template: `
        <div>{{nameObj[0]}}</div>
      `,
      setup(props, context) {
        const { reactive } = Vue;
        //  proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
        const nameObj = reactive(['reactive'])
        setTimeout(() => {
          nameObj[0] = 'vue3中的reactive'
        }, 2000)
        return {
          nameObj,
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

控制台结果如下: arr.png

结论:我们发现用vue3中提供的composition API中的refreactive可以替代vue2中data()中定义的变量

readonly的使用

如何把变量变成只读的? 通过vue3中提供了的readonly

<body>
  <div id="root">
  </div>
  <script>
    // reactive 处理非基础类型数据
    const app = Vue.createApp({
      template: `
        <div>{{nameObj[0]}}</div>
      `,
      setup(props, context) {
        const { reactive, readonly } = Vue;
        //  proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
        const nameObj = reactive(['reactive'])
        const copyNameObj = readonly(nameObj);        
        setTimeout(() => {
          nameObj[0] = 'vue3中的reactive'
          copyNameObj[0] = 'vue3中readonly'
        }, 2000)
        return {
          nameObj,
          copyNameObj
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

控制台显示结果: readonly.png

我们发现可以通过readonly对响应式的引用做一个限制,控制台会显示一个警告,告诉返回的对象是只读的,不可以修改的。

当我们在组件中不希望上面案例引用对象时使用{{nameObj.name}}而是直接使用name,我们如何实现?

错误写法: 下面这种const { name } = nameObj写法并不能实现2s后改变nameObj的值?

<body>
  <div id="root">
  </div>
  <script>
    // reactive 处理非基础类型数据
    const app = Vue.createApp({
      template: `
        <div>{{name}}</div>
      `,
      setup(props, context) {
        const { reactive, readonly } = Vue;
        //  proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
        const nameObj = reactive({
          name: 'reactive'
        })
        setTimeout(() => {
          nameObj.name = 'vue3中的reactive'
        }, 2000)
        const { name } = nameObj
        return {
          name,
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

控制台显示结果:

react1.png

上面这样写name是不具备响应式的数据,所以我们直接return{name}导出去是不行的,而vue3中又提供了toRefs方法,我们可以使用toRefs解构来解决,解决代码如下:

toRefs的使用

toRefs的原理: 将reactive转化的toRefs proxy({name: 'vue3中的reactive'}) 转换成 { name: proxy({ value: 'vue3中的reactive'}) }

<body>
  <div id="root">
  </div>
  <script>
    // reactive 处理非基础类型数据
    const app = Vue.createApp({
      template: `
        <div>{{name}}</div>
      `,
      setup(props, context) {
        const { reactive, readonly, toRefs } = Vue;
        //  proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
        const nameObj = reactive({
          name: 'reactive'
        })
        setTimeout(() => {
          nameObj.name = 'vue3中的reactive'
        }, 2000)
         // toRefs proxy({name: 'vue3中的reactive'}) 转换成 { name: proxy({ value: 'vue3中的reactive'}) }
        const { name } = toRefs(nameObj)
        return {
          name,
        }
      }
    })
    const vm = app.mount('#root')
  </script>
</body>

控制台显示结果:

torefs.png

结论:将reactive转化的toRefs proxy({name: 'vue3中的reactive'}) 转换成 { name: proxy({ value: 'vue3中的reactive'}) },所以我们上面调用的name相当于调用的name.value的内容。如果多个对象转化如下:

toRefs proxy({name: 'vueage3中的reactive', age: 26})
{ name: proxy({ value: 'vue3中的reactive'}), age: proxy({ value: '26'}) }