Vue-API

API基础

涵盖了以下方面的 API:

● `响应式 API:例如 ref() 和 reactive(),使我们可以直接创建响应式状态、计算属性和侦听器``生命周期钩子:例如 onMounted() 和 onUnmounted(),使我们可以在组件各个生命周期阶段添加逻辑``依赖注入:例如 provide() 和 inject(),使我们可以在使用响应式 API 时,利用 Vue 的依赖注入系统`


组合式优点:
1.更好的逻辑复用,组合式 API 解决了 mixins 的所有缺陷
2.更灵活的代码组织
3.更好的类型推荐
4.更小的生产包体积

组合式两种写法

setup函数和重要知识

1.在vue3组合式中不需要写data,在setup中返回的对象就是data数据
2.在setup()中不能使用this,因此不能使用this.$props this.$attrs
3.vue3组合式中有两种变量定义
	`ref  当赋值发生改变时,会进行更新`
	`reactive 主要用于定义引用类型`,和ref不同的地方在于,`改变引用地址将与原绑定内容分离,不会更新改变`
    
`setup()`
4.setup() 函数中有两个参数
		 1) 函数的第一个参数是组件的 props
          2) 函数的第二个参数是一个 Setup 上下文对象
          	 其中包括 ttrs、emits、slots、expose
             
             
5.defineProps
// 在vue3的setup中,我们使用defineProps来定义父组件传递的props
defineProps返回的props对象,是一个proxy对象,所有特性和reactive基本相同,只不过`由defineProps定义出的props对象的值是只读的`,还有在模板上可以单独属性直接使用

对于数据

写法1

`在script中使用 setup()函数`
<template>
  <div>
    <div>{{ count }}</div>
    <ul>
      <li v-for="(value, index) in list" :key="index">{{ value }}</li>
    </ul>
    <button @click="clickHandler">按钮</button>
  </div>
</template>

<script>
// 在vue3组合式中不需要写data,在setup中返回的对象就是data数据

// vue3组合式中有两种变量定义
// ref  当赋值发生改变时,会进行更新

import { ref, reactive } from 'vue'
// 写法一
export default {
  props: ["a"],
  setup(props, minxi) {
    // setup 函数的第一个参数是组件的 props
    console.log(props);// 可以直接拿到props

    // setup 函数的第二个参数是一个 Setup 上下文对象
    console.log(minxi);//包含了 attrs emits slots expose
    // console.log(this)// 不能使用this,因此不能使用this.$props this.$attrs

    // 在vue3组合式中不需要写data,在setup中返回的对象就是data数据


    const count = ref(1);
    // var list = ref([1, 2, 3])
    const list = ref({ a: 1, b: 2 })

    // const list = reactive([1, 2, 3]);
    // const list=reactive({a:1,b:2,c:3})
    return {
      count,
      list
    }
  },
  methods: {
    clickHandler() {
      this.count++

   `值是数组类型`
      // 当是ref时 会更新改变
      // 当是reactive时 不会更新改变
      this.list = [4, 5, 6]

      // ref也可以使用这种方式
      // 使用这种方式才会让是reactive时 更新改变
      this.list.length = 0;
      this.list.push(...[4, 5, 6])
      // ref reactive 都可以向其中添加数据
      this.list.push(10)

        
    `值是对象类型`
      // 是reactive时 只能改变对象类型中的属性值
      // 但是重新赋给一个新对象时 不会更新改变
      // 是ref时,会更新改变
      this.list = { c: 3, d: 4 }
      // ref reactive 都可以向其中添加数据
      this.list.c = 30;
        
   
      // ref也可以使用这种方式
      // 当是reactive时,使用Object.assign 不会改变引用地址,会更新改变
      Object.keys(this.list).forEach(key => delete this.list[key]);
      Object.assign(this.list, { c: 3, d: 4 })
    }
  }
}
</script>

写法2

`在script标签中加 setup`
// 注:在这种写法中 ref类型的需要使用当前变量的value值修改
//				 reactive类型的直接修改就行,但是不允许破坏引用关系
<template>
  <div>
    <div>{{ count }}</div>
    <ul>
      <li v-for="(value, index) in arr" :key="index">{{ value }}</li>
    </ul>
    <button @click="clickHandler">按钮</button>
  </div>
</template>
<script setup>
// 写法二
import { reactive, ref, defineProps } from "vue";

const count = ref(1);
const arr=ref([1,2,3]);
// const list = reactive([1, 2, 3]);

// 定义父组件传递的值
defineProps(['a']);
function clickHandler() {
  // 所有使用ref定义的,修改值都需要使用这个变量的value值修改
  count.value++,

  //所有使用reactive定义的,直接修改其值就行了
  list.push(10),
  list.length = 0;
  list.push(...[4, 5, 6])

  // 要使用这个变量的value值修改
  arr.value.push(4, 5)
  console.log(arr)
}
</script>

对于事件和函数

写法1

// 父组件
<template>
  <div>
    <SecondView :count="count" ref="sv" @change="(value)=>console.log(value)"></SecondView>
    <button @click="count++">按钮</button>
  </div>
</template>
<script>
import SecondView from "@/views/SecondView.vue"
export default {
  name: 'App',
  components: {
    SecondView
  },
  data() {
    return {
      count: 1,
      total: 1
    }
  },
  mounted(){
    console.log(this.$refs.sv.name);
      // 没有使用 emits 在父组件中并不会使用子组件中定义的事件和函数
    this.$refs.sv.changeName();
  }
}
</script>

//子组件
<template>
  <div>
    {{ count }}--{{ sum }}
    <!-- {{ count }}--{{ props1 }} -->

    <!-- <button @click="clickHandler">按钮1</button> -->
  </div>
</template>

<script>
import { ref, toRef, toRefs, onMounted } from "vue"
export default {
  name: "SecondView",
  props: ["count"],
  setup(props, context) {
    // toRef可以针对导入的值变化关联变化
    const sum = toRef(props, "count")

    // toRefs针对引用对象的内容发生变化而变化
     const props1 = toRefs(props)
    const name = ref("谢天")
    const changeName = () => {
      name.value = "张三"
    }
    // 把值暴露给父级
    context.expose({
      name,
      changeName
    })
    return {
      sum,
      props1
    }
  }
}
</script>

写法2

`1.defineEmits 定义事件
 2.defineProps 获取父组件传来的值
 3.defineModel v-model 的使用
 4.defineExpose 把值暴露给父级
`

//父组件
// 获取到 子组件 抛发的事件 执行一些列响应
<SecondView :count="count" @change="(value) => console.log(value)" ref="sv" v-model="total" />
    <button @click="count++">按钮2</button>
    {{ total }}
mounted() {
    console.log(this.$refs.sv.name);
    this.$refs.sv.change()
  }
// 子组件
<template>
  <div>
    {{ count }}--{{ sum }}
    {{ count }}--{{ props1 }}
    <button @click="clickHandler">按钮1</button>
  </div>
</template>
<script setup>
import { defineEmits,defineProps,toRef,toRefs,defineExpose,ref } from "vue"
// 定义事件
const emits=defineEmits(["change"])

// 获取父组件传来的count
const props=defineProps(["count"])

// v-model 的使用
// 父组件中v-model 绑定了total 当modelValue变化 total也会变化
const modelValue=defineModel({required:true})

// 把值暴露给父级
const name=ref("xietian")
defineExpose({name})

const sum=toRef(props,"count")
// const sum=toRefs(props).count

// 点击执行clickHandler函数 会抛发change事件
function clickHandler(){
  emits("change",1)
  modelValue.value++
}
</script>

响应式核心

ref()和reactive()

`ref  当赋值发生改变时,会进行更新`
`reactive 主要用于定义引用类型`,和ref不同的地方在于,`改变引用地址将与原绑定内容分离,不会更新改变`
1.要求 ref用来定义非引用类型 reactive用来定义引用类型
2.ref也可以设置引用类型,这时候,value值就会变成使用reactive定义Proxy对象了
3.reactive不能设置非引用类型
4.reactive 不能设置为null类型,ref 可以设置为null类型
6.`在<script steup> 写法中`
	所有使用ref定义的,修改值都需要使用这个变量的value值修改
  		count.value++,
	所有使用reactive定义的,直接修改其值就行了

`使用`
// 数据
const list = ref([1, 2, 3, 4])
const list = ref({ a: 1, b: 2 })

const obj = reactive({ a: 1, b: 2 })
const obj = reactive([1, 2, 3])
`值是数组类型`
      // 当是ref时 会更新改变
      // 当是reactive时 不会更新改变
      this.list = [4, 5, 6]

      // ref也可以使用这种方式
      // 使用这种方式才会让是reactive时 更新改变
      this.list.length = 0;
      this.list.push(...[4, 5, 6])
      // ref reactive 都可以向其中添加数据
      this.list.push(10)

        
    `值是对象类型`
      // 是reactive时 只能改变对象类型中的属性值
      // 但是重新赋给一个新对象时 不会更新改变
      // 是ref时,会更新改变
      this.list.c = 30;
      this.list = { c: 3, d: 4 }
   
   // ref也可以使用这种方式
   // 当是reactive时,使用Object.assign 不会改变引用地址,会更新改变
      Object.keys(this.list).forEach(key => delete this.list[key]);
      Object.assign(this.list, { c: 3, d: 4 })


	`深层`
      // ref
      list.value={a:1,b:2,c:{d:4}}
      list.value.c=10
      console.log(list);

	  // reactive
      obj.value={a:1,b:2,c:{d:4}}
      obj.value.c.d=10
      console.log(obj);
vue2

useAttrs()

// useAttrs()可以获取到当前父元素传入的所有attrs中的值
<ChildB :count="count" a="10"/>
    
<p>{{ attrs.a }}</p>
const attrs = useAttrs()
console.log(attrs)

computed ()

// 通过监听某个值的变化计算出一个新值

// 只读的写法 :computed(() => xxxxxx),
// 可读可写的写法:  computed({ get: () => xxxx, set: (val) => { xxxx } })
`使用`
<input type="text" v-model="msg">
<div>{{ message }}</div>

const msg = ref("")
const message = computed(() => {
  return "小波说:" + msg.value
})


// computed实际上就是一个类似于ref的值,需要通过value修改
<input type="text" v-model="a">
<div>{{ s }}</div>
<button @click="clickHandler">按钮</button>

const a = ref("");
const s = computed({
  ser(value) {
    a.value = value.replace("---", "")
  },
  get() {
    return a.value + "???"
  }
})
function clickHandler() {
  console.log(s);
}

readonly()

// readonly定义的对象不能修改其所有的属性,类型于实现了Object.freeze
//对任何嵌套属性(深层)的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。

const ab = readonly({ a: 1, b: 20 })
ab.a = 10// 报错

// map类型也不能被修改
var obj = { a: 1, b: 2 }
const ab = readonly(new Map(Object.entries(obj)))
ab.set("a", 10)// 报错

watch()

//watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数
`参数列表:`
	参数1为需要监听的响应式对象(可以是单个对象,也可以是一个数组,也可以是一个getter函数),
	参数2为监听对象发生变化时所执行的回调
	参数3是一些配置项:immediate是否开启立即监听,deep是否开启深度监听,flush回调的触发时机,onTrack / onTrigger用于调试的两个函数


`使用`
// reactive
// 针对对象监听
const obj = reactive({ a: 1, b: 2 })
watch(obj, (newValue, oldValue) => {
  console.log(newValue, oldValue)
})
setTimeout(() => {
  obj.a=10
}, 1000);

// reactive
// 如果希望监听对象的某个属性,需要第一个是回调函数,返回要监听的属性
const obj = reactive({ a: 1, b: 2 })
watch(() => obj.b, (newValue, oldValue) => {
  console.log(newValue, oldValue)
})
setTimeout(() => {
  obj.b=10
}, 3000);


// 针对ref
const a = ref(1)
watch(a, (value) => {
  console.log(value)
})
setTimeout(() => {
  a.value=100
}, 3000);


// 同时监听两个
const a = ref(0)
const b = ref("a")
watch([a, b], ([newValue_a, newValue_b], [oldValue_a, oldValue_b]) => {
  console.log(newValue_a, newValue_b);
  console.log(oldValue_a, oldValue_b)
})
setTimeout(() => {
  // 如果同时修改两个内容,watch之会被激活一次,但是修改的是两个值
  a.value = 100;
  b.value = "abc"
}, 3000);


// 如果直接针对reactive类型监听,属性修改会做深度侦听
// 不需要加 deep: true
watch(obj, (newValue, oldValue) => {
  console.log(newValue)
})
setTimeout(() => {
  obj.b.c = 100
}, 3000);

// 如果使用返回值的方式侦听对象的情况下,会做浅侦听
// () => obj.b 返回值的方式
// 如果需要对返回值的方式深度侦听,需要在第三个参数设置对象增加deep:true
watch(() => obj.b, (newValue, oldValue) => {
  console.log(newValue)
}, {
  `deep: true`
})
setTimeout(() => {
  obj.b.c = 100
}, 3000);

watchEffect()

// 值没有发生改变的时候,第一次进入就会执行一次
// watchEffect没有具体监听哪一个值的变化,只要内部有某一个状态发生改变就会执行
第一个参数就是要运行的副作用函数。`这个副作用函数的参数也是一个函数,用来注册清理回调``清理回调会在该副作用下一次执行前被调用`,可以用来清理无效的副作用
const count = ref(0)
watchEffect(() => {
  console.log(count)
})
setTimeout(() => {
  count.value = 10;
}, 3000)



const count = ref(0);
watchEffect((fn) => {
  // watchEffect 会先执行一次 先打印"bbb"
  console.log("bbb");
  var id = setTimeout(() => {
  // 然后进入此处 3秒后改变a的值 并打印"ccc"
    count.value = 10;
    console.log("ccc")
  }, 3000);
  fn(() => {
    console.log("aaa")
    clearTimeout(id)
  })
})

// 函数并不会触发执行
// 只有当代码内部有某一个状态发生改变就会执行
function clickHandler() {
  count.value = 200;
}

unwatch()

// unwatch  停止该侦听器
// unwatch只能使用在外面,不能再watchEffect中调用
const count = ref(0);
let unwatch = watchEffect(() => {
  console.log(count.value);
})
setTimeout(() => {
  watchEffect(() => {
    console.log(count.value)
  })
})

function clickHandler() {
  count.value++;
  unwatch()
}

watch和computed 的区别

computed和watch之间的区别:
1.computed能完成的功能,watch都可以完成
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作


`两个重要的小原则:`
1.所有`被Vue管理的函数,最好写成普通函数`,这样this的指向才是vm 或 组件实例对象
2.所有不被Vue所管理的函数(`定时器的回调函数、ajax的回调函数等、Promise的回调函数`),最好`写成箭头函数`,这样this的指向才是vm 或 组件实例对象。

工具函数和生命周期钩子函数

toRef和toRefs

`toRef`
	const sum = toRef(porps, "count")
1.toRef 函数可以将一个响应式对象的属性转换为一个独立的 ref 对象。
2.返回的是一个指向源对象属性的 ref 引用,任何对该引用的修改都会同步到源对象属性上。
3.使用 toRef 时需要传入源对象和属性名作为参数。

`toRefs`
	const sum = toRefs(porps).count
1.toRefs 函数可以将一个响应式对象转换为一个普通的对象,该对象的每个属性都是独立的 ref 对象。
2.返回的对象可以进行解构,每个属性都可以像普通的 ref 对象一样访问和修改,而且会保持响应式的关联。
3.toRefs 的使用场景主要是在将响应式对象作为属性传递给子组件时,确保子组件可以正确地访问和更新这些属性


`相同点`
1.toRef 和 toRefs 都用于将响应式对象的属性转换为 ref 对象。
2.转换后的属性仍然保持响应式,对属性的修改会反映到源对象上。
3.不管是使用 toRef 还是 toRefs 将响应式对象转成普通对象,在 script 中修改和访问其值都需要通过 .value 进行。

`不同点`
1.toRef 修改的是对象的某个属性,生成一个单独的 ref 对象。
2.toRefs 修改的是整个对象,生成多个独立的 ref 对象集合。
3.toRefs 适用于在组件传递属性或解构时使用,更加方便灵活,而 toRef 更适合提取单个属性进行操作。

生命周期函数

// 挂载
onBeforeMount(() => {
  console.log("挂载前");
})

onMounted(() => {
  console.log("挂载后");
})

// 更新
onBeforeUpdate(() => {
  console.log("更新前");
})

onUpdated(() => {
  console.log("更新后");
})

// 卸载
onBeforeUnmount(() => {
  console.log("卸载前");
})

onUnmounted(() => {
  console.log("卸载后");
})

依赖注入

// 父组件
<template>
  <div>
    <ChildA/>
    <button @click="count++">按钮</button>
  </div>
</template>

<script setup>
import { provide, ref } from "vue"
import ChildA from "@/components/injectView/ChildA.vue"
const count = ref(1);
function setCommit(){
  console.log("aaa");
}

// 在这里provide不需要再给数据做compued绑定改变

// 传入变量
provide("count", count)

// 传入函数
provide("setCommit",setCommit)
</script>

// 子组件
<template>
  <div>
    <div>{{ count }}</div>
  </div>
</template>

<script setup>
import { inject } from "vue"

const count = inject("count")
// 设置默认值
// const count=inject("count",0)

// 注入函数
const setCommit=inject("setCommit");
setCommit()
</script>

组合式函数

// hooks
// “组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数,使用时从外部引入再调用
核心逻辑完全一致,只是把它移到一个外部函数中去,并返回需要暴露的状态

// 封装的 MouseXY.js
import { ref, onMounted, onUnmounted } from "vue";

const x = ref(0);
const y = ref(0);
export default function mouseXY() {
  addEvent();
  return { x, y };
}

function addEvent() {
  onMounted(() => {
    document.addEventListener("mousemove", mouseHandler);
  })

  onUnmounted(() => {
    document.addEventListener("mousemove", mouseHandler);
  })
}

function mouseHandler(e) {
  x.value = e.clientX;
  y.value = e.clientY;
}

// 从外部ji文件引入
import mouseXY from "@/hooks/MouseXY.js";
const { x, y } = mouseXY();

完整的异步过程

// 组件中
<template>
  <div>
    <form @submit.prevent="submitHandler">
      <input type="text" name="user" v-model="user">
      <input type="password" name="password" v-model="password" autocomplete>
      <button type="submit">提交</button>
    </form>
    <div v-if="result.error">{{ 通信失败 }}</div>
    <div v-else-if="result.data">{{ result.data }}</div>
    <div v-else>正在通信中...</div>
  </div>
</template>

<script setup>
import { reactive, ref } from 'vue';
import { useLogin } from "@/hooks/Ajax.js"

const user = ref("");
const password = ref("");
let result = ref({})

function submitHandler() {
  var fd = new FormData();
  fd.set("user", user.value);
  fd.set("password", password.value);
  result.value = useLogin(fd)
}
</script>


// Ajax.js
import { ref } from "vue";
export function useLogin(body) {
  const data = ref(null);
  const error = ref(null);

  fetch("http://localhost:4000/users/login", {
    method: "POST",
    body: body
  }).then((result) => {
    result.json().then((res) => {
      data.value = res;
    })
  }).catch(e => {
    error.value = e
  })
  return { data, error }
}

API基础

涵盖了以下方面的 API:

● `响应式 API:例如 ref() 和 reactive(),使我们可以直接创建响应式状态、计算属性和侦听器``生命周期钩子:例如 onMounted() 和 onUnmounted(),使我们可以在组件各个生命周期阶段添加逻辑``依赖注入:例如 provide() 和 inject(),使我们可以在使用响应式 API 时,利用 Vue 的依赖注入系统`


组合式优点:
1.更好的逻辑复用,组合式 API 解决了 mixins 的所有缺陷
2.更灵活的代码组织
3.更好的类型推荐
4.更小的生产包体积

组合式两种写法

setup函数和重要知识

1.在vue3组合式中不需要写data,在setup中返回的对象就是data数据
2.在setup()中不能使用this,因此不能使用this.$props this.$attrs
3.vue3组合式中有两种变量定义
	`ref  当赋值发生改变时,会进行更新`
	`reactive 主要用于定义引用类型`,和ref不同的地方在于,`改变引用地址将与原绑定内容分离,不会更新改变`
    
`setup()`
4.setup() 函数中有两个参数
		 1) 函数的第一个参数是组件的 props
          2) 函数的第二个参数是一个 Setup 上下文对象
          	 其中包括 ttrs、emits、slots、expose
             
             
5.defineProps
// 在vue3的setup中,我们使用defineProps来定义父组件传递的props
defineProps返回的props对象,是一个proxy对象,所有特性和reactive基本相同,只不过`由defineProps定义出的props对象的值是只读的`,还有在模板上可以单独属性直接使用

对于数据

写法1

`在script中使用 setup()函数`
<template>
  <div>
    <div>{{ count }}</div>
    <ul>
      <li v-for="(value, index) in list" :key="index">{{ value }}</li>
    </ul>
    <button @click="clickHandler">按钮</button>
  </div>
</template>

<script>
// 在vue3组合式中不需要写data,在setup中返回的对象就是data数据

// vue3组合式中有两种变量定义
// ref  当赋值发生改变时,会进行更新

import { ref, reactive } from 'vue'
// 写法一
export default {
  props: ["a"],
  setup(props, minxi) {
    // setup 函数的第一个参数是组件的 props
    console.log(props);// 可以直接拿到props

    // setup 函数的第二个参数是一个 Setup 上下文对象
    console.log(minxi);//包含了 attrs emits slots expose
    // console.log(this)// 不能使用this,因此不能使用this.$props this.$attrs

    // 在vue3组合式中不需要写data,在setup中返回的对象就是data数据


    const count = ref(1);
    // var list = ref([1, 2, 3])
    const list = ref({ a: 1, b: 2 })

    // const list = reactive([1, 2, 3]);
    // const list=reactive({a:1,b:2,c:3})
    return {
      count,
      list
    }
  },
  methods: {
    clickHandler() {
      this.count++

   `值是数组类型`
      // 当是ref时 会更新改变
      // 当是reactive时 不会更新改变
      this.list = [4, 5, 6]

      // ref也可以使用这种方式
      // 使用这种方式才会让是reactive时 更新改变
      this.list.length = 0;
      this.list.push(...[4, 5, 6])
      // ref reactive 都可以向其中添加数据
      this.list.push(10)

        
    `值是对象类型`
      // 是reactive时 只能改变对象类型中的属性值
      // 但是重新赋给一个新对象时 不会更新改变
      // 是ref时,会更新改变
      this.list = { c: 3, d: 4 }
      // ref reactive 都可以向其中添加数据
      this.list.c = 30;
        
   
      // ref也可以使用这种方式
      // 当是reactive时,使用Object.assign 不会改变引用地址,会更新改变
      Object.keys(this.list).forEach(key => delete this.list[key]);
      Object.assign(this.list, { c: 3, d: 4 })
    }
  }
}
</script>

写法2

`在script标签中加 setup`
// 注:在这种写法中 ref类型的需要使用当前变量的value值修改
//				 reactive类型的直接修改就行,但是不允许破坏引用关系
<template>
  <div>
    <div>{{ count }}</div>
    <ul>
      <li v-for="(value, index) in arr" :key="index">{{ value }}</li>
    </ul>
    <button @click="clickHandler">按钮</button>
  </div>
</template>
<script setup>
// 写法二
import { reactive, ref, defineProps } from "vue";

const count = ref(1);
const arr=ref([1,2,3]);
// const list = reactive([1, 2, 3]);

// 定义父组件传递的值
defineProps(['a']);
function clickHandler() {
  // 所有使用ref定义的,修改值都需要使用这个变量的value值修改
  count.value++,

  //所有使用reactive定义的,直接修改其值就行了
  list.push(10),
  list.length = 0;
  list.push(...[4, 5, 6])

  // 要使用这个变量的value值修改
  arr.value.push(4, 5)
  console.log(arr)
}
</script>

对于事件和函数

写法1

// 父组件
<template>
  <div>
    <SecondView :count="count" ref="sv" @change="(value)=>console.log(value)"></SecondView>
    <button @click="count++">按钮</button>
  </div>
</template>
<script>
import SecondView from "@/views/SecondView.vue"
export default {
  name: 'App',
  components: {
    SecondView
  },
  data() {
    return {
      count: 1,
      total: 1
    }
  },
  mounted(){
    console.log(this.$refs.sv.name);
      // 没有使用 emits 在父组件中并不会使用子组件中定义的事件和函数
    this.$refs.sv.changeName();
  }
}
</script>

//子组件
<template>
  <div>
    {{ count }}--{{ sum }}
    <!-- {{ count }}--{{ props1 }} -->

    <!-- <button @click="clickHandler">按钮1</button> -->
  </div>
</template>

<script>
import { ref, toRef, toRefs, onMounted } from "vue"
export default {
  name: "SecondView",
  props: ["count"],
  setup(props, context) {
    // toRef可以针对导入的值变化关联变化
    const sum = toRef(props, "count")

    // toRefs针对引用对象的内容发生变化而变化
     const props1 = toRefs(props)
    const name = ref("谢天")
    const changeName = () => {
      name.value = "张三"
    }
    // 把值暴露给父级
    context.expose({
      name,
      changeName
    })
    return {
      sum,
      props1
    }
  }
}
</script>

写法2

`1.defineEmits 定义事件
 2.defineProps 获取父组件传来的值
 3.defineModel v-model 的使用
 4.defineExpose 把值暴露给父级
`

//父组件
// 获取到 子组件 抛发的事件 执行一些列响应
<SecondView :count="count" @change="(value) => console.log(value)" ref="sv" v-model="total" />
    <button @click="count++">按钮2</button>
    {{ total }}
mounted() {
    console.log(this.$refs.sv.name);
    this.$refs.sv.change()
  }
// 子组件
<template>
  <div>
    {{ count }}--{{ sum }}
    {{ count }}--{{ props1 }}
    <button @click="clickHandler">按钮1</button>
  </div>
</template>
<script setup>
import { defineEmits,defineProps,toRef,toRefs,defineExpose,ref } from "vue"
// 定义事件
const emits=defineEmits(["change"])

// 获取父组件传来的count
const props=defineProps(["count"])

// v-model 的使用
// 父组件中v-model 绑定了total 当modelValue变化 total也会变化
const modelValue=defineModel({required:true})

// 把值暴露给父级
const name=ref("xietian")
defineExpose({name})

const sum=toRef(props,"count")
// const sum=toRefs(props).count

// 点击执行clickHandler函数 会抛发change事件
function clickHandler(){
  emits("change",1)
  modelValue.value++
}
</script>

响应式核心

ref()和reactive()

`ref  当赋值发生改变时,会进行更新`
`reactive 主要用于定义引用类型`,和ref不同的地方在于,`改变引用地址将与原绑定内容分离,不会更新改变`
1.要求 ref用来定义非引用类型 reactive用来定义引用类型
2.ref也可以设置引用类型,这时候,value值就会变成使用reactive定义Proxy对象了
3.reactive不能设置非引用类型
4.reactive 不能设置为null类型,ref 可以设置为null类型
6.`在<script steup> 写法中`
	所有使用ref定义的,修改值都需要使用这个变量的value值修改
  		count.value++,
	所有使用reactive定义的,直接修改其值就行了

`使用`
// 数据
const list = ref([1, 2, 3, 4])
const list = ref({ a: 1, b: 2 })

const obj = reactive({ a: 1, b: 2 })
const obj = reactive([1, 2, 3])
`值是数组类型`
      // 当是ref时 会更新改变
      // 当是reactive时 不会更新改变
      this.list = [4, 5, 6]

      // ref也可以使用这种方式
      // 使用这种方式才会让是reactive时 更新改变
      this.list.length = 0;
      this.list.push(...[4, 5, 6])
      // ref reactive 都可以向其中添加数据
      this.list.push(10)

        
    `值是对象类型`
      // 是reactive时 只能改变对象类型中的属性值
      // 但是重新赋给一个新对象时 不会更新改变
      // 是ref时,会更新改变
      this.list.c = 30;
      this.list = { c: 3, d: 4 }
   
   // ref也可以使用这种方式
   // 当是reactive时,使用Object.assign 不会改变引用地址,会更新改变
      Object.keys(this.list).forEach(key => delete this.list[key]);
      Object.assign(this.list, { c: 3, d: 4 })


	`深层`
      // ref
      list.value={a:1,b:2,c:{d:4}}
      list.value.c=10
      console.log(list);

	  // reactive
      obj.value={a:1,b:2,c:{d:4}}
      obj.value.c.d=10
      console.log(obj);
vue2

useAttrs()

// useAttrs()可以获取到当前父元素传入的所有attrs中的值
<ChildB :count="count" a="10"/>
    
<p>{{ attrs.a }}</p>
const attrs = useAttrs()
console.log(attrs)

computed ()

// 通过监听某个值的变化计算出一个新值

// 只读的写法 :computed(() => xxxxxx),
// 可读可写的写法:  computed({ get: () => xxxx, set: (val) => { xxxx } })
`使用`
<input type="text" v-model="msg">
<div>{{ message }}</div>

const msg = ref("")
const message = computed(() => {
  return "小波说:" + msg.value
})


// computed实际上就是一个类似于ref的值,需要通过value修改
<input type="text" v-model="a">
<div>{{ s }}</div>
<button @click="clickHandler">按钮</button>

const a = ref("");
const s = computed({
  ser(value) {
    a.value = value.replace("---", "")
  },
  get() {
    return a.value + "???"
  }
})
function clickHandler() {
  console.log(s);
}

readonly()

// readonly定义的对象不能修改其所有的属性,类型于实现了Object.freeze
//对任何嵌套属性(深层)的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。

const ab = readonly({ a: 1, b: 20 })
ab.a = 10// 报错

// map类型也不能被修改
var obj = { a: 1, b: 2 }
const ab = readonly(new Map(Object.entries(obj)))
ab.set("a", 10)// 报错

watch()

//watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数
`参数列表:`
	参数1为需要监听的响应式对象(可以是单个对象,也可以是一个数组,也可以是一个getter函数),
	参数2为监听对象发生变化时所执行的回调
	参数3是一些配置项:immediate是否开启立即监听,deep是否开启深度监听,flush回调的触发时机,onTrack / onTrigger用于调试的两个函数


`使用`
// reactive
// 针对对象监听
const obj = reactive({ a: 1, b: 2 })
watch(obj, (newValue, oldValue) => {
  console.log(newValue, oldValue)
})
setTimeout(() => {
  obj.a=10
}, 1000);

// reactive
// 如果希望监听对象的某个属性,需要第一个是回调函数,返回要监听的属性
const obj = reactive({ a: 1, b: 2 })
watch(() => obj.b, (newValue, oldValue) => {
  console.log(newValue, oldValue)
})
setTimeout(() => {
  obj.b=10
}, 3000);


// 针对ref
const a = ref(1)
watch(a, (value) => {
  console.log(value)
})
setTimeout(() => {
  a.value=100
}, 3000);


// 同时监听两个
const a = ref(0)
const b = ref("a")
watch([a, b], ([newValue_a, newValue_b], [oldValue_a, oldValue_b]) => {
  console.log(newValue_a, newValue_b);
  console.log(oldValue_a, oldValue_b)
})
setTimeout(() => {
  // 如果同时修改两个内容,watch之会被激活一次,但是修改的是两个值
  a.value = 100;
  b.value = "abc"
}, 3000);


// 如果直接针对reactive类型监听,属性修改会做深度侦听
// 不需要加 deep: true
watch(obj, (newValue, oldValue) => {
  console.log(newValue)
})
setTimeout(() => {
  obj.b.c = 100
}, 3000);

// 如果使用返回值的方式侦听对象的情况下,会做浅侦听
// () => obj.b 返回值的方式
// 如果需要对返回值的方式深度侦听,需要在第三个参数设置对象增加deep:true
watch(() => obj.b, (newValue, oldValue) => {
  console.log(newValue)
}, {
  `deep: true`
})
setTimeout(() => {
  obj.b.c = 100
}, 3000);

watchEffect()

// 值没有发生改变的时候,第一次进入就会执行一次
// watchEffect没有具体监听哪一个值的变化,只要内部有某一个状态发生改变就会执行
第一个参数就是要运行的副作用函数。`这个副作用函数的参数也是一个函数,用来注册清理回调``清理回调会在该副作用下一次执行前被调用`,可以用来清理无效的副作用
const count = ref(0)
watchEffect(() => {
  console.log(count)
})
setTimeout(() => {
  count.value = 10;
}, 3000)



const count = ref(0);
watchEffect((fn) => {
  // watchEffect 会先执行一次 先打印"bbb"
  console.log("bbb");
  var id = setTimeout(() => {
  // 然后进入此处 3秒后改变a的值 并打印"ccc"
    count.value = 10;
    console.log("ccc")
  }, 3000);
  fn(() => {
    console.log("aaa")
    clearTimeout(id)
  })
})

// 函数并不会触发执行
// 只有当代码内部有某一个状态发生改变就会执行
function clickHandler() {
  count.value = 200;
}

unwatch()

// unwatch  停止该侦听器
// unwatch只能使用在外面,不能再watchEffect中调用
const count = ref(0);
let unwatch = watchEffect(() => {
  console.log(count.value);
})
setTimeout(() => {
  watchEffect(() => {
    console.log(count.value)
  })
})

function clickHandler() {
  count.value++;
  unwatch()
}

watch和computed 的区别

computed和watch之间的区别:
1.computed能完成的功能,watch都可以完成
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作


`两个重要的小原则:`
1.所有`被Vue管理的函数,最好写成普通函数`,这样this的指向才是vm 或 组件实例对象
2.所有不被Vue所管理的函数(`定时器的回调函数、ajax的回调函数等、Promise的回调函数`),最好`写成箭头函数`,这样this的指向才是vm 或 组件实例对象。

工具函数和生命周期钩子函数

toRef和toRefs

`toRef`
	const sum = toRef(porps, "count")
1.toRef 函数可以将一个响应式对象的属性转换为一个独立的 ref 对象。
2.返回的是一个指向源对象属性的 ref 引用,任何对该引用的修改都会同步到源对象属性上。
3.使用 toRef 时需要传入源对象和属性名作为参数。

`toRefs`
	const sum = toRefs(porps).count
1.toRefs 函数可以将一个响应式对象转换为一个普通的对象,该对象的每个属性都是独立的 ref 对象。
2.返回的对象可以进行解构,每个属性都可以像普通的 ref 对象一样访问和修改,而且会保持响应式的关联。
3.toRefs 的使用场景主要是在将响应式对象作为属性传递给子组件时,确保子组件可以正确地访问和更新这些属性


`相同点`
1.toRef 和 toRefs 都用于将响应式对象的属性转换为 ref 对象。
2.转换后的属性仍然保持响应式,对属性的修改会反映到源对象上。
3.不管是使用 toRef 还是 toRefs 将响应式对象转成普通对象,在 script 中修改和访问其值都需要通过 .value 进行。

`不同点`
1.toRef 修改的是对象的某个属性,生成一个单独的 ref 对象。
2.toRefs 修改的是整个对象,生成多个独立的 ref 对象集合。
3.toRefs 适用于在组件传递属性或解构时使用,更加方便灵活,而 toRef 更适合提取单个属性进行操作。

生命周期函数

// 挂载
onBeforeMount(() => {
  console.log("挂载前");
})

onMounted(() => {
  console.log("挂载后");
})

// 更新
onBeforeUpdate(() => {
  console.log("更新前");
})

onUpdated(() => {
  console.log("更新后");
})

// 卸载
onBeforeUnmount(() => {
  console.log("卸载前");
})

onUnmounted(() => {
  console.log("卸载后");
})

依赖注入

// 父组件
<template>
  <div>
    <ChildA/>
    <button @click="count++">按钮</button>
  </div>
</template>

<script setup>
import { provide, ref } from "vue"
import ChildA from "@/components/injectView/ChildA.vue"
const count = ref(1);
function setCommit(){
  console.log("aaa");
}

// 在这里provide不需要再给数据做compued绑定改变

// 传入变量
provide("count", count)

// 传入函数
provide("setCommit",setCommit)
</script>

// 子组件
<template>
  <div>
    <div>{{ count }}</div>
  </div>
</template>

<script setup>
import { inject } from "vue"

const count = inject("count")
// 设置默认值
// const count=inject("count",0)

// 注入函数
const setCommit=inject("setCommit");
setCommit()
</script>

组合式函数

// hooks
// “组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数,使用时从外部引入再调用
核心逻辑完全一致,只是把它移到一个外部函数中去,并返回需要暴露的状态

// 封装的 MouseXY.js
import { ref, onMounted, onUnmounted } from "vue";

const x = ref(0);
const y = ref(0);
export default function mouseXY() {
  addEvent();
  return { x, y };
}

function addEvent() {
  onMounted(() => {
    document.addEventListener("mousemove", mouseHandler);
  })

  onUnmounted(() => {
    document.addEventListener("mousemove", mouseHandler);
  })
}

function mouseHandler(e) {
  x.value = e.clientX;
  y.value = e.clientY;
}

// 从外部ji文件引入
import mouseXY from "@/hooks/MouseXY.js";
const { x, y } = mouseXY();

完整的异步过程

// 组件中
<template>
  <div>
    <form @submit.prevent="submitHandler">
      <input type="text" name="user" v-model="user">
      <input type="password" name="password" v-model="password" autocomplete>
      <button type="submit">提交</button>
    </form>
    <div v-if="result.error">{{ 通信失败 }}</div>
    <div v-else-if="result.data">{{ result.data }}</div>
    <div v-else>正在通信中...</div>
  </div>
</template>

<script setup>
import { reactive, ref } from 'vue';
import { useLogin } from "@/hooks/Ajax.js"

const user = ref("");
const password = ref("");
let result = ref({})

function submitHandler() {
  var fd = new FormData();
  fd.set("user", user.value);
  fd.set("password", password.value);
  result.value = useLogin(fd)
}
</script>


// Ajax.js
import { ref } from "vue";
export function useLogin(body) {
  const data = ref(null);
  const error = ref(null);

  fetch("http://localhost:4000/users/login", {
    method: "POST",
    body: body
  }).then((result) => {
    result.json().then((res) => {
      data.value = res;
    })
  }).catch(e => {
    error.value = e
  })
  return { data, error }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值