vue3(二)

前一篇讲了 vue3的生命周期钩子的使用。
本节接着讲 vue3的数据通信。

provide/inject 依赖注入

App.vue

<script setup lang="ts">
import { ref, provide } from 'vue'
import List from './components/List.vue'
import User from './components/User.vue'
import Demo from './components/Demo.vue'

const count = ref<number>(1)
const msg = ref<string>('hello world')

interface DataObj {
  name: string
  readonly id?: number // 只读属性
  age: number
  gender: string
  address?: string // 可选属性
  [propName: string]: any // 任意的属性
  sum?(a: number, b: number): number // 声明 可选方法 sum()返回值为 number
  updateData(): any // 声明 updateData() 返回值类型为 any
}

const updateData = (): void => {
  console.log('我是来自爷爷组件的方法')
}
const dataObj = ref<DataObj>({
  name: 'zhangsan',
  age: 20,
  gender: 'male',
  updateData,
})

const clickHandle = (): any => {
  console.log(count.value)
  count.value++
  /**在js中需要通过 .value来访问变量,因为 count是 proxy代理的名称 */
}

// 通过 provide 向外暴露一个变量,然后在所有后代组件中都可以获取,实现跨组件通信
provide('msg', msg)
provide('dataObj', dataObj)

const getDataFromSon = (params: string): any => {
  console.log('来自子组件的数据', params)
}
</script>

<template>
  <!--在模板中直接使用变量-->
  <h1>{{ count }}</h1>

  <button @click="count++">{{ count }}</button>

  <button @click="clickHandle">增加</button>

  <List @getDataFromSon="getDataFromSon" :dataToSon="msg" />

  <User />

  <Demo />
</template>

<style scoped></style>

Info.vue 作为后代组件来消费根组件传递的数据

<script setup lang="ts">
import { ref, inject } from 'vue'
import Home from './Home.vue'
const txt = ref<string>('hello vue3')
const list = ref<string[]>()

// 通过 inject来消费顶层组件传递的数据
const message = inject<string>('msg')
const dataObj = inject<any>('dataObj')
</script>
<template>
  <h1>{{ txt }} {{ message }}</h1>

  <h2>{{ dataObj.name }}</h2>

  <button @click="dataObj.updateData">调用爷爷组件的方法</button>

  <Home :txt="txt" />
</template>

<style scoped></style>

父子组件通信

创建一个List.vue

<script setup lang="ts">
import { ref, onMounted, watch, computed, reactive } from 'vue'
import Info from './Info.vue'

// 申明一个接口
interface Book {
  title: string
  year?: number // 可选属性,有可能不存在
}

const book: Book = reactive({ title: 'Vue 3 指引' })

const txt = ref<string>('')
const list = ref<string[]>([])
const infoRef = ref<any>()
// 通过 defineEmits()方法来声明传递给父组件的方法,该方法接收一个数组,可以传递多个emit方法
// defineEmits()和 defineProps()都是在子组件中使用,声明的都是定义在子组件上的事件和属性

const emit = defineEmits(['getDataFromSon'])
const props = defineProps<{ dataToSon: string }>()

const saveText = (): any => {
  if (txt) list.value.push(txt.value)
  emit('getDataFromSon', txt.value)
  console.log('来自父组件的数据:', props.dataToSon)
  txt.value = ''
}

computed(() => {})

watch(
  () => txt.value,
  (txt, preText) => {
    console.log(txt, preText)
  }
)

onMounted(() => {
  // 使用子组件暴露出来的属性和方法
  console.log(infoRef.value.message)
  console.log(infoRef.value.dataObj)
  console.log('Dom 已经挂在完毕')
})

const callSonMethod = (): any => {
  infoRef.value.callMe()
}
/**
 * 在script标签中添加 setup 属性是 vue 官方推荐的默认写法,
 * 这样在 script标签中定义的函数,变量,甚至是 imort 导入的,都可以在模板中直接使用
 * 而setup()函数这种写法,需要将所有声明的变量,函数通过 renturn函数暴露处供模板使用
 */
</script>

<template>
  <input
    type="text"
    v-model="txt"
    @keyup.enter="saveText"
    placeholder="请输入内容"
  />
  <div class="list">
    <ul>
      <li v-for="item in list" :key="item">
        {{ item }}
      </li>
    </ul>
  </div>

  <Info ref="infoRef"/>
</template>
<style scoped></style>

创建Info.vue, 父组件传递 text 变量给子组件, 子组件通过触发getDataFromSon()向父组件传递数据

<script setup lang="ts">
import { ref, inject } from 'vue'
import Home from './Home.vue'

const txt = ref<string>('hello vue3.0')
const list = ref<[]>()
const message = inject<string>('msg')
const dataObj = inject<any>('dataObj')

const callMe = (): any => {
  console.log('this method is use defineExpose to parent')
}
// 通过 defineExpose({}) 方法暴露子组件的一些属性和方法,供父组件使用
defineExpose({
  message,
  dataObj,
  callMe,
})

</script>
<template>
  <h1>{{ txt }} {{ message }}</h1>

  <h2>{{ dataObj.name }}</h2>

  <button @click="dataObj.updateData">调用爷爷组件的方法</button>

  <Home :txt="txt" />
</template>

<style scoped></style>

setup() 这种写法

<script lang="ts">
import { ref, reactive, onMounted } from 'vue'
export default {
  /**
   * 组合式 API 我们可以将所有的属性方法都写在 setup()方法中,然后 return 出去
   * 不再像 选项式API 哪种,需要写 data, methods, created等等
   */
  setup() {
    // ref用来定义基础数据类型,在 js中访问的时候要通过.value来访问
    const count = ref<number>(1)
    const list = reactive<any>({
      // reactive用来定义复杂数据类型,访问的时候不用.value
      num: 1,
      list: [],
    })
    const increment = () => {
      count.value++
      list.num += 1
    }
    /**
     * 在 vue3中没有 beforeCreated 和 created钩子函数,
     * setup 方法就相当于这 2 个钩子
     */

    onMounted(() => {
      console.log('Dom 已经过载完毕')
    })

    /**
     * 通过 return 将属性和方法暴露出去,供模板访问
     */
    return {
      count,
      list,
      increment,
    }
  },
}
/**
 * 注意:使用reactive()声明复杂数据类型,给对象赋值时,通过下面这种方式来声明(如果嫌.value麻烦的话)
 * const obj = reactive({
 *  data: {
 *    name: '',id: '',}
 *  })
 * 就是在对象外面给它包一个key值,赋值时:
 * obj.data = {name: 'zhangsan', id: 100}
 *
 * 第二种:就是直接用 ref 来声明对象或者数据,赋值时:obj.value = {name: 'Alex', id: 001}
 */
</script>

<template>
  <h2>这是 User 组件 {{ count }}</h2>

  <h1>{{ list.num }}</h1>

  <button @click="increment">click</button>
</template>

<style></style>

setup() <script setup></script> 区别

  • setup()函数中定义的变量或者函数,需要 return 暴露出去,模板才能调用
  • 而当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用
  • <script setup> 中的代码会在每次组件实例被创建的时候执行。
    <script setup></script> 优势:
    setup_attribute
    toRef()toRefs() 区别
    toRef
const state = reactive({
  foo: 1,
  bar: 2
})
// 通过toRef()后 fooRef变量也是响应式的,改变foo的值,fooRef也改变
// 作用:将一个非响应式的变量转换为一个响应式变量
const fooRef = toRef(state, 'foo')

// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2

// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3

toRefs
ref()shallowRef() 区别

ref()我们知道可以创建一个响应式变量
shallowRef(): ref() 的浅层作用形式。

那么,shallowRef()就是可以创建一个浅层次的响应式变量,
应用场景:如果你的数据不赋值,不变更或者修改,只是做展示用,那么可以用 shallowRef(),可以节省性能消耗

reactive()shallowReactive()

reactive(): 返回一个对象的响应式代理,
响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性

shallowReactive():reactive() 的浅层作用形式。

和 reactive() 不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了
若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代。

const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 更改状态自身的属性是响应式的
state.foo++

// ...但下层嵌套对象不会被转为响应式
isReactive(state.nested) // false

// 不是响应式的
state.nested.bar++

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值