Vue3.0 API的使用

Vue3.0 整体优势

  1. proxy数据劫持代替object.defineProperty(特点:可以监听数组变化,可以直接修改对象属性等);
  2. Composition API,更好的逻辑复用和代码组织;
  3. 优化VirtualDom,使其编译更快,体积更小;
  4. 更好的TypeScript支持,不再需要借助装饰器;

Vue3.0 注意事项

  1. vue3.0页面中用到的API都需要手动引入
  2. 所有的逻辑代码都要写在setup方法中
  3. template中用到的变量和方法必须return出去才可以访问
  4. 书写TS将script标签属性lang="ts"即可

Vue3.0 生命周期变化

  • 注意:所有生命周期API需要从vue按需引入,并且要在setup函数中调用:onMounted(()=>{...codeing })
  • beforeCreate >> setup
  • created >> setup
  • beforeMount >> onBeforeMount
  • mounted >> onMounted
  • beforeUpdate >> onBeforeUpdate
  • updated >> onUpdated
  • beforeDestory >> onBeforeUnmount
  • destoryed >> onUnmounted

Vue3.0 API的使用

1. 组件的声明

  • 普通组件:defineComponent
  • 异步组件:defineAsyncComponent
<script lang="ts" >

import {defineComponent} from 'vue'
const Home = defineComponent({
  name: 'Home',
})
export default Home

</script>

2. 组件的引入 和 props类型检测

<script lang="ts" >

import Helloword from '../compontents/helloword'
const Home = defineComponent({
  name: 'Home',
  // 使用子组件
  components: {
    Helloword
  },
  // 检测父组件传入的Props
  props: {
    user: {
      type: String,
      required: true
    }
  }
})
export default Home

</script>

3. defineProps 和 defineEmits

  • <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits
  • defineProps 和 defineEmits 都是只在<script setup>中才能使用的编译器宏。他们不需要导入且会随着 <script setup>处理过程一同被编译掉
<script lang="ts" setup>

// ......V3+JS
const props = defineProps({
  foo: String
})
const emit = defineEmits(['change', 'delete'])
// ......V3+TS
const props = defineProps<{
  foo: string
  bar?: number
}>()
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
// ......V3+TS接口
interface Props {
  msg?: string
  labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})

</script>

4. setup

  • 提示:在 setup() 函数中无法访问this
  • 执行时机:beforeCreate 之后 created之前
<script lang="ts" >

const Home = defineComponent({
  name: 'Home',
  setup(props, context) {

  }
})
export default Home

</script>

5. ref

  • 作用:ref() 函数用来根据给定的值创建一个响应式的数据对象
  • 返回:ref() 函数调用的返回值是一个对象,这个对象上只包含一个 .value 属性
  • 使用:.value 是在 setup 函数中访问 ref 包装后的对象时才需要加的,在 template 模板中访问时是不需要的,因为在编译时,会自动识别其是否为 ref 包装过的
  • 注意:声明新的 ref 会覆盖旧的 ref
<template>
  <div>{{year}}</div>
</template>

<script lang="ts" >
import {ref} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    let year = ref<string>('2021')
    year.value = '2022'

    return {
      year
    }
  }
})
export default Home
</script>

6. reactive

  • 作用:用来创建一个响应式的数据对象
  • 注意:创建的变量具有 深层响应 的效果,该API也很好地解决了Vue2通过 defineProperty 实现数据响应式的缺陷
  • 使用建议:(有的同学可能会比较疑惑,ref 和 reactive 的使用有点像)
    1. 基本类型值(String 、Nmuber 、Boolean 等)或单值对象(类似像 {count: 3} 这样只有一个属性值的对象)使用 ref
    1. 引用类型值(Object 、Array)使用 reactive
<script lang="ts" >
interface Student {
  name: string,
  age: string | number,
  class: string
}
interface Todo {
  text: string,
  done: boolean
}
import {reactive} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const state = reactive({ count: 0})
    const year = ref<string>('2021')
    // 当把 ref() 创建出来的响应式数据对象,挂载到 reactive() 上时,会自动把响应式数据对象展开为原始的值,不需通过 .value 就可以直接被访问
    const state2 = reactive(year)
    // TS给定变量类型的几种方式
    const studentA = reactive<Student>({ name: 'studentA', age: 11, class: 'studentA'})
    const studentB: Student = reactive({ name: 'studentB', age: 22, class: 'studentB'})
    const studentC = reactive({ name: 'studentC', age: 33, class: 'studentC'}) as Student
    const todoList = reactive<Array<Todo>>([
      { text: '待办01', done: true},
      { text: '待办02', done: false}
    ])

    return {
      state2,
      todoList
    }
  }
})
export default Home
</script>

7. shallowReactive

  • 作用:用来创建一个响应式的数据对象(浅层响应,外层被proxy包裹实现响应,内层不更新视图)
  • 场景:用作性能优化API,考虑到如果一个对象层级比较深,那么每一层都用 Proxy 包装后,对于性能是非常不友好的
<script lang="ts" >
import {shallowReactive} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const deepobj = {
      a: 1,
      first: {
        b: 2,
        second: {
          c: 3
        }
      }
    }
    const newdeepobj = shallowReactive(deepobj)
    newdeepobj.a = 11 // 第一层改变,更新视图
    newdeepobj.first.b = 22 // 其它层级,不会更新视图

    return {
      newdeepobj
    }
  }
})
export default Home
</script>

8. isRef

  • 作用:isRef() 用来判断某个值是否为 ref() 创建出来的对象
<script lang="ts" >
import {ref, isRef} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const year = ref('2021')
    const getVal = isRef(year) ? year.value : '默认年份'

    return {
      getVal
    }
  }
})
export default Home
</script>

9. toRef

  • 作用:toRef 是将某个对象中的某个值转化为响应式数据,
  • 参数:其接收两个参数,第一个参数为 obj 对象;第二个参数为对象中的属性名
  • 注意:
    1. ref 是对传入数据的拷贝;toRef 是对传入数据的引用
    1. ref 的值改变会更新视图;toRef 的值改变不会更新视图
<script lang="ts" >
import {toRef} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const data = {count: 3}
    const newdata = toRef(data, 'count')

    return {
      getVal
    }
  }
})
export default Home
</script>

10. toRefs

  • 作用:将传入的对象里所有的属性的值都转化为响应式数据对象,该函数支持一个参数
  • toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象
  • 只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据
<script lang="ts" >
import {reactive, toRefs} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const state = reactive({ name: 'zs', age: 22, gender: 0 })
    const newstate = toRefs(state) // 转为普通数据
    const obj = { name: 'ls', age: 20, gender: 1 }
    const newobj = toRefs(obj) // 转为响应式数据

    return {}
  }
})
export default Home
</script>

11. shallowRef

  • 作用:创建数据对象(浅层监听,只监听value实现响应)
  • 场景:一样是拿来做性能优化的
<script lang="ts" >
import {shallowRef, triggerRef} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const multistoreyobj ={
      a: 1,
      first: {
        b: 2,
        second: {
          c: 3
        }
      }
    }
    const newmultistoreyobj= shallowRef(multistoreyobj)
    newmultistoreyobj.value.first.b = 8// 场景一: 改变数据,但不会更新视图
    newmultistoreyobj.value.first.second.c = 9// 改变数据,但不会更新视图
    newmultistoreyobj.value = {// 场景二: 将整个.value 重新赋值了,视图就立马更新了
      a: 11,
      first: {
        b: 22,
        second: {
          c: 33
        }
      }
    }

    return {}
  }
})
export default Home
</script>

12. triggerRef

  • 作用:驱动更新
// 接着上个示例代码看
newmultistoreyobj.value.first.b = 8// 改变数据不触发更新
triggerRef(newmultistoreyobj)// 场景三:  调用triggerRef方法驱动更新

13. toRaw

  • 作用:用于获取 ref 或 reactive 对象的原始数据的
<script lang="ts" >
import {reactive, toRaw} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const olddatas = {
      name: 'olddatas',
      age: 11
    }
    const newdatas = reactive(olddatas) // 包装旧数据
    const raw = toRaw(newdatas)// 拿到数据源 (注意:如果操作的是ref对象,参数需要写成newdatas.value)
    raw.age = 111// 由于是引用数据源,所以修改数据源后引用的一方也会改变,但不触发更新 (所以:可用作性能优化)

    return {}
  }
})
export default Home
</script>

14. markRaw

  • 作用:可以将原始数据标记为不可被追踪,即使用ref或reactive将其包装,仍无法实现数据响应式
  • 参数:其接收一个参数,即原始数据,并返回被标记后的数据
<script lang="ts" >
import {reactive, markRaw} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const objA = { a: 1}
    const rawA = markRaw(objA)// 通过markRaw标记原始数据objA, 使其数据更新不再被追踪
    const stateA = reactive(raw)// 试图用reactive包装raw, 使其变成响应式数据
    stateA.age = 90//响应无效:修改数据,不触发更新

    return {}
  }
})
export default Home
</script>

15. computed

  • 作用:计算API
    1. 只读
<script lang="ts" >
import {ref, computed} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const refCount = ref(1)
    let computedCount = computed(() => refCount.value + 1)
    console.log(computedCount) // 读取

    return {}
  }
})
export default Home
</script>
    1. 可读可写
<script lang="ts" >
import {ref, computed} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const refCount = ref(1)
    let computedCount2 = computed({
      get: () => refCount.value + 1,
      set: val => refCount.value = val - 5
    })
    console.log(computedCount2.value) // 读取
    computedCount2.value = 10 // 写入
    console.log(computedCount2.value)
    console.log(refCount.value)

    return {}
  }
})
export default Home
</script>

16. watch

  • 注意:组件第一次创建的时候会触发一次,默认是渐层的监听我们指定的数据,需要深层配置deep:true
  • 参数:watch( source, cb, [options] )
    • source:可以是表达式或函数,用于指定监听的依赖对象
    • cb:依赖对象变化后执行的回调函数
    • options:可配置参数,可以配置的属性有 immediate(初始化立即触发回调函数)、deep(深度监听)
  • 几种使用场景:
    1. 指定要监视的数据源
<script lang="ts" >
import {ref, watch} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    let refCount = ref<string>('20')
    let year = ref<string>('2021')
    let month = ref<string>('11')
    watch(refCount, () => {console.log('监听refCount',refCount.value)})
    watch(
      [year, month],
      ([newYear, newMonth], [oldYear, oldMonth]) => {
        console.log('监听year',newYear,oldYear)
        console.log('监听month',newMonth,oldMonth)
      }
    )
    return {}
  }
})
export default Home
</script>
    1. 清除监视
    • 在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。
    • 如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可
<script lang="ts" >
import {watch} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    let refCount = ref<string>('20')
    const stop = watch(refCount, () => {})
    stop()
    return {}
  }
})
export default Home
</script>
    1. 清除无效的异步任务
    • 有时候,当被 watch 监视的值发生变化时,或 watch 本身被 stop 之后,我们期望能够清除那些无效的异步任务,此时,watch 回调函数中提供了一个函数来执行清除的工作。这个清除函数会在如下情况下被调用:
        1. watch被重复执行了
        1. watch被强制stop了
<script lang="ts" >
import {watch} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    let refCount = ref<string>('20')
    const asyncPrint = (val: any) => {// 异步任务
      return setTimeout(() => {
        console.log(val)
      },1000)
    }
    watch(
      refCount,
      (newValue, oldValue, onCleanup) => {
        const timerId = asyncPrint(refCount)// 执行异步任务,并得到关联的timerId
        onCleanup(() => clearTimeout(timerId))// 如果watch监听被重复执行,则会清除上次未完成的任务
      }
    )

    return {}
  }
})
export default Home
</script>

17. watchEffect

  • 作用:监听API
  • 它与 watch 的区别主要有以下几点:
    • 不需要手动传入依赖
    • 每次初始化时会执行一次回调函数来自动获取依赖
    • 无法获取到原值,只能得到变化后的值
<script lang="ts" >
import {watchEffect} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const stateB = reactive({ count: 0, name: 'zs' })
    setTimeout(() => {
      stateB.count ++
      stateB.name = 'ls'
    }, 1000)
    watchEffect(() => {
      console.log(stateB.count) // 初始化时打印:0,zs
      console.log(stateB.name) // 1秒后打印:1,1s
    })

    return {}
  }
})
export default Home
</script>

18. getCurrentInstance

  • 作用:Vue3中无法访问this,可以用到getCurrentInstance方法访问实例对象
<script lang="ts" >
import {getCurrentInstance} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    // instance.ctx接近this的概念   instance.proxy是包装过的响应式this
    const instance = getCurrentInstance()
    console.log(instance)
    return {}
  }
})
export default Home
</script>

19. useStore

  • 作用:Vue3中拿不到this,instance.ctx里也没有store,只能用vuex引出的useStore方法使用仓库
<script lang="ts" >
import {store} from 'vuex'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const store = useStore()// 获取 vuex 实例
    console.log(store)
    return {}
  }
})
export default Home
</script>

20. useRoute & useRouter

<script lang="ts" >
import {useRoute, useRouter} from 'vuex'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const route = useRoute() // 相当于 vue2 中的 this.$route
    const router = useRouter() // 相当于 vue2 中的 this.$router
    console.log(route,router)
    return {}
  }
})
export default Home
</script>

21. provide & inject

  • 作用:provide() 和 inject() 可以实现嵌套组件之间的数据传递。
  • 提示:这两个函数只能在 setup() 函数中使用。
  • 使用:父级组件中使用 provide() 函数向下传递数据;子级组件中使用 inject() 获取上层传递过来的数据。
import {provide} from 'vue'
// 父组件向下传递
const color = ref('orange')
provide('themecolor',color)
// 子组件接收变量
const resultcolor = inject('themecolor')

22. 节点的引用 -- dom的引用

<template>
  <h3 ref="h3Ref">h3的DOM引用</h3>
  <button @click="clickBtn">弹窗</button>
</template>

<script lang="ts" >
import {ref, Ref} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const method = {
      clickBtn: () => {
        let color = h3Ref.value.style.color
        alert(color)
      }
    }
    const h3Ref:Ref = ref(null) // 创建一个响应式dom的引用,传入null
    onMounted(() => {// DOM首次加载完毕,才能取到元素
      h3Ref.value.style.color = 'red' // 为DOM元素设置样式颜色 , h3Ref.value是原生DOM对象
      // (h3Ref as Ref).value.style.color = 'red'// 初始变量不给定Ref类型的话 可以使用断言语法规避报错
    })

    return {
      ...method, // 一次性展开所有方法
      h3Ref, // 把DOM的引用抛出使用
    }
  }
})
export default Home
</script>

23. 节点的引用 -- 组件的引用

<template>
  <!-- 子组件 -->
  <HelloWorld ref="comRef" msg="Welcome to Your Vue.js + TypeScript App"/>
</template>

<script lang="ts" >
import {ref, Ref} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    const comRef: any = ref(null)
    console.log(comRef.value.str1) // 打印子组件的变量str1值
    comRef.valye.setStr1() // 调用子组件的方法设置str1值
    console.log(comRef.value.str1) // 打印发现子组件的str1被改变了

    return {
      comRef // 把组件的引用抛出使用
    }
  }
})
export default Home
</script>

24. nextTick

  • 在 Vue 3 中,全局和内部 API 都经过了重构,并考虑到了 tree-shaking 的支持。因此,对于 ES 模块构建版本来说,全局 API 现在通过具名导出进行访问
  • 通过这一更改,如果模块打包工具支持 tree-shaking,则 Vue 应用中未使用的全局 API 将从最终的打包产物中排除,从而获得最佳的文件大小
    • 2.0写法
      • this.nextTick(()=>{})
    • 3.0写法
      • nextTick(()=>{})

Vue3.0 CSS的使用

1. 状态驱动的动态 CSS

<template>
  <button class="dynamic">状态驱动的动态 CSS</button>
</template>

<script lang="ts" >
import {ref} from 'vue'
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {
    let btnBg = ref<string>('blue')
    setTimeout(()=>{btnBg.value='orange'}, 2000)

    return {
      btnBg,
    }
  }
})
export default Home
</script>

<style lang="less" scoped> // scoped隔离样式作用域,不需要任何的 polyfill,通过PostCss转换内容实现
.dynamic{
  /* 绑定变量 */
  background: v-bind(btnBg)
}
</style>

2. 深度选择器

  • 解释:处于 scoped 样式中的选择器如果想要做更“深度”的选择
  • 注意:通过 v-html 创建的 DOM 内容不会被作用域样式影响,但你仍然可以使用深度选择器来设置其样式。
<template>
  <div class="home">
  </div>
</template>

<script lang="ts" >
const Home = defineComponent({
  name: 'Home',
  setup(props, context) {

    return {}
  }
})
export default Home
</script>

<style lang="less" scoped>
.home :deep(.btn_a) {
  background: red
}
</style>

3. 插槽选择器

  • 解释:默认情况下,作用域样式不会影响到 <slot/> 渲染出来的内容,因为它们被认为是父组件所持有并传递进来的。
  • 作用:使用 :slotted 伪类以确切地将插槽内容作为选择器的目标
<style lang="less" scoped>
:slotted(div) {
  background: #000
}
</style>

4. 全局选择器

  • 解释:如果想让其中一个样式规则应用到全局,比起另外创建一个<style>,可以使用 :global 伪类来实现
<style lang="less" scoped>
:global(.background_red) {
  background: red
}
</style>

5. 混合使用局部与全局样式

  • 解释:你也可以在同一个组件中同时包含作用域样式和非作用域样式:
  • <style 标签和内容
  • <style scope 标签和内容

6. <style module>

  • <style module>标签会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style 对象的键暴露给组件
  • 对生成的类做 hash 计算以避免冲突,实现了和 scope CSS 一样将 CSS 仅作用于当前组件的效果。
<style module>
.btn_b {
  background: brown
}
</style>

7. 自定义注入名称

  • 你可以通过给 module attribute 一个值来自定义注入的类对象的 property 键
<style module="moduleC">
.btn_c {
  background: skyblue
}
</style>

8. 与组合式 API 一同使用

<style module="moduleD">
/* 默认, 返回 <style module> 中的类
useCssModule()
命名, 返回 <style module="classes"> 中的类
useCssModule('classes') */
.btn_d {
  background: greenyellow
}
</style>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容