Bootstrap

vue3.0笔记(二)

关于setup一些细节问题及组件之间的传值
这里创建一个父组件

<template>
  <h2>App父级组件</h2>
  <h3>msg:{{ msg }}</h3>
  <button @click="msg += '==='">更新数据</button>
  <hr />
  <Child :msg="msg" msg2="真香" @xxx="xxx" />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
// 引入子级组件Child
import Child from './components/Child.vue'
export default defineComponent({
  name: 'App',
  // 注册组件
  components: {
    Child,
  },

  setup() {
    // 定义一个Ref类型的数据
    const msg = ref('what are you no sha lei')
    function xxx(txt: string) {
      msg.value += txt
    }
    return {
      msg,
      xxx,
    }
  },
})
</script>

然后创建一个新的组件

<template>
  <h2>Child子级组件</h2>
  <h3>msg:{{ msg }}</h3>
  <!-- <h3>count:{{ count }}</h3> -->
  <button @click="emitXxx">分发事件</button>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
  name: 'Child',
  props: ['msg'],
  // setup细节问题:
  // setup是在beforeCreate生命周期回调之前就执行了,而且就执行一次
  // 由此可以推断出:setup在执行的时候,当前的组件还没有创建出来,也就意味着:组件实例对象this根本就不能用
  // this是undefined,说明,就不能通过this再去调用data/computed/methods/props中的相关内容了
  // 其实所有的composition API相关回调函数中也都不可以

  // setup中的返回值是一个对象,内部的属性和方法是给html模版使用的
  // setup中的对象内部的属性和data函数中的return对象的属性都可以在html模版中使用
  // setup中的对象中的属性和data函数中的对象中的属性会合并为组件对象的属性
  // setup中的对象中的方法和methods对象中的方法会合并为组件对象的方法
  // 在Vue3中尽量不要混合的使用data和setup及methods和setup
  // 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods
  // setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据
  // beforeCreate() {
  //   console.log('beforeCreate执行了')
  // },
  // 界面渲染完毕
  // mounted() {},

  // setup(props,context) {
  setup(props, { attrs, slots, emit }) {
    // props参数,是一个对象,里面有父级组件向子级组件传递的数据,并且是在子级组件中使用props接收到的所有的属性
    // 包含props配置声明且传入了的所有属性的对象
    // console.log(props.msg)
    // console.log(context.attrs)
    // console.log(context.emit)
    // context参数,是一个对象,里面有attrs对象(获取当前组件标签上的所有的属性的对象,但是该属性是在props中没有声明接收的所有的尚需经的对象),emit方法(分发事件的),slots对象(插槽)
    // 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
    // console.log(context.attrs.msg2)
    // console.log('=============')
    console.log('setup执行了', this)

    const showMsg1 = () => {
      console.log('setup中的showMsg1方法')
    }
    // 按钮的点击事件的回调函数
    function emitXxx() {
      // context.emit('xxx','++')
      emit('xxx', '++')
    }
    return {
      showMsg1,
      emitXxx,
      // setup中一般都是返回一个对象,对象中的属性和方法都可以在html模版中直接使用
    }
  },
  // data() {
  //   return {
  //     count: 10,
  //   }
  // },
  // // 界面渲染后的生命周期回调
  // mounted() {
  //   console.log(this)
  // },
  // // 方法的
  // methods: {
  //   showMsg2() {
  //     console.log('methods中的showMsg方法')
  //   },
  // },
})
</script>

reactive和ref的一些细节问题

<!--
 * @Author: yzz
 * @Date: 2021-02-08 14:06:07
 * @LastEditors: Do not edit
 * @LastEditTime: 2021-02-22 14:33:02
 * @Description: Do not edit
 * @FilePath: \vue3_study\01_代码\07_ref和reactive的细节问题\App.vue
-->
<template>
  <h2>reactive和ref的细节问题</h2>
  <h3>m1:{{ m1 }}</h3>
  <h3>m2:{{ m2 }}</h3>
  <h3>m3:{{ m3 }}</h3>
  <hr />
  <button @click="update">更新数据</button>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from 'vue'
export default defineComponent({
  name: 'App',
  
    // 是Vue3的 composition API中2个最重要的响应式API(ref和reactive)
    // ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
    // 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
    // ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
    // reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
    // ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)

  setup() {
    // 通过ref的方式设置的数据
    const m1 = ref('abc')
    const m2 = reactive({
      name: '小明',
      wife: {
        name: '小红',
      },
    })
    // ref也可以传入对象吗
    const m3 = ref({
      name: '小明',
      wife: {
        name: '小红',
      },
    })
    // 更新数据
    const update = () => {
      // ref中如果放入的是一个对象,那么是经过了reactive的处理,形成了一个Proxy类型的对象
      console.log(m3)
      m1.value += '==='
      m2.wife.name += '==='
      // m3.value.name += '==='
      m3.value.wife.name += '==='
      console.log(m3.value.wife)
    }
    return {
      m1,
      m2,
      m3,
      update,
    }
  },
})
</script>

vue3.0中的computed和watch

<template>
  <h2>计算属性和监视</h2>
  <fieldset>
    <legend>姓名操作</legend>
    姓氏:<input
      type="text"
      placeholder="请输入姓氏"
      v-model="user.firstName"
    /><br />
    名字:<input
      type="text"
      placeholder="请输入名字"
      v-model="user.lastName"
    /><br />
  </fieldset>
  <fieldset>
    <legend>计算属性和监视的演示</legend>
    姓名:<input type="text" placeholder="显示姓名" v-model="fullName1" /><br />
    姓名:<input type="text" placeholder="显示姓名" v-model="fullName2" /><br />
    姓名:<input type="text" placeholder="显示姓名" v-model="fullName3" /><br />
  </fieldset>
</template>
<script lang="ts">
import {
  defineComponent,
  reactive,
  computed,
  watch,
  ref,
  watchEffect,
} from 'vue'
export default defineComponent({
  name: 'App',
  setup() {
    // 定义一个响应式对象
    const user = reactive({
      // 姓氏
      firstName: '东方',
      // 名字
      lastName: '不败',
    })
    // 通过计算属性的方式,实现第一个姓名的显示
    // vue3中的计算属性
    // 计算属性的函数中如果只传入一个回调函数,表示的是get

    // 第一个姓名:
    // 返回的是一个Ref类型的对象
    const fullName1 = computed(() => {
      return user.firstName + '_' + user.lastName
    })
    // 第二个姓名:
    const fullName2 = computed({
      get() {
        return user.firstName + '_' + user.lastName
      },
      set(val: string) {
        // console.log('=====',val)
        const names = val.split('_')
        user.firstName = names[0]
        user.lastName = names[1]
      },
    })

    // 第三个姓名:
    const fullName3 = ref('')
    // 监视----监视指定的数据
    watch(
      user,
      ({ firstName, lastName }) => {
        fullName3.value = firstName + '_' + lastName
      },
      { immediate: true, deep: true }
    )
    // immediate 默认会执行一次watch,deep 深度监视

    // 监视,不需要配置immediate,本身默认就会进行监视,(默认执行一次)
    // watchEffect(() => {
    //   fullName3.value = user.firstName + '_' + user.lastName
    // })

    // 监视fullName3的数据,改变firstName和lastName
    watchEffect(() => {
      const names = fullName3.value.split('_')
      user.firstName = names[0]
      user.lastName = names[1]
    })

    // watch---可以监视多个数据的
    // watch([user.firstName,user.lastName,fullName3],()=>{
    //   // 这里的代码就没有执行,fullName3是响应式的数据,但是,user.firstName,user.lastName不是响应式的数据
    //   console.log('====')
    // })
    // 当我们使用watch监视非响应式的数据的时候,代码需要改一下
    watch([()=>user.firstName, ()=>user.lastName,fullName3], () => {
      // 这里的代码就没有执行,fullName3是响应式的数据,但是,user.firstName,user.lastName不是响应式的数据
      console.log('====')
    })

    return {
      user,
      fullName1,
      fullName2,
      fullName3,
    }
  },
})
</script>

关于toRefs

<template>
  <h2>toRefs的使用</h2>
  <!-- <h3>name:{{ state.name }}</h3>
  <h3>age:{{ state.age }}</h3> -->

  <h3>name:{{ name }}</h3>
  <h3>age:{{ age }}</h3>

  <h3>name2:{{ name2 }}</h3>
  <h3>age2:{{ age2 }}</h3>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue'

function useFeatureX() {
  const state = reactive({
    name2: '自来也',
    age2: 47,
  })
  return {
    ...toRefs(state),
  }
}
export default defineComponent({
  name: 'App',
  setup() {
    const state = reactive({
      name: '自来也',
      age: 47,
    })
    // toRefs可以把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref
    // const state2 = toRefs(state)
    const { name, age } = toRefs(state)
    // console.log(state2)
    // 定时器,更新数据,(如果数据变化了,界面也会随之变化,肯定是响应式的数据)
    setInterval(() => {
      // state.name += '=='
      // state2.name.value+='==='
      name.value += '==='
      console.log('======')
    }, 1000)

    const { name2, age2 } = useFeatureX()
    return {
      // state,
      // 下面的方式不行啊
      // ...state // 不是响应式的数据了---->{name:'自来也',age:47}
      // ...state2  toRefs返回来的对象
      name,
      age,
      name2,
      age2,
    }
  },
})
</script>
;