vue3基础知识

Vue3 动机 和 新特性

Vue3 中文文档 https://vue3js.cn/docs/zh/

Vue3 设计理念 https://vue3js.cn/vue-composition/

动机与目的:

  1. 更好的逻辑复用 与 代码组织 (composition组合式api)

    optionsAPI(旧) => compositionAPI(新), 效果: 代码组织更方便了, 逻辑复用更方便了 非常利于维护!!

  2. 更好的类型推导 (typescript支持)

    vue3 源码用 ts 重写了, vue3 对 ts 的支持更友好了 (ts 可以让代码更加稳定, 类型检测! )

vue3新特性:

  1. 数据响应式原理重新实现 (ES6 proxy 替代了 ES5 的 Object.defineProperty)

    解决了: 例如数组的更新检测等bug, 大大优化了响应式监听的性能

    (原来检测对象属性的变化, 需要一个个对属性递归监听) proxy 可以直接对整个对象劫持

  2. 虚拟DOM - 新算法 (更快 更小)

  3. 提供了composition api, 可以更好的逻辑复用

  4. 模板可以有多个根元素

  5. 源码用 typescript 重写, 有更好的类型推导 (类型检测更为严格, 更稳定)


    vue3 性能更高, 体积更小, 更利于复用, 代码维护更方便

Vite 的使用

Vite 是一个原生 ESM 驱动的 web 开发构建工具, 在开发环境下基于浏览器原生 ES imports 开发

可以用于快速构建 vue3 的工程化项目环境 (脚手架也能构建vue3项目, 就是比较重)

注意: node版本 12以上

Vite 的基本使用

按照顺序执行如下的命令,即可基于 vite 创建 vue 3.x 的工程化项目

npm init vite-app 项目名称
或者
yarn create vite-app 项目名称

cd 项目名称
yarn
yarn dev

输入地址, 启动项目

Vue3.0项目main.js分析

说明: 在工程化的项目中,vue 要做的事情很单纯:通过 main.js 把 App.vue 渲染到 index.html 的指定区域中

其中:

  • App.vue 用来编写待渲染的模板结构
  • index.html 中需要预留一个 el 区域
  • main.js 把 App.vue 渲染到了 index.html 所预留的区域中
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

// 将 App.vue 的内容, 渲染到 index.html中
// 调用createApp()函数, 就是在创建一个单页应用实例, 通过.mount方法, 渲染到el区域中
createApp(App).mount('#app')

Composition API

composition API vs options API

  1. vue2 采用的就是 optionsAPI

    (1) 优点:易于学习和使用, 每个代码有着明确的位置 (例如: 数据放 data 中, 方法放 methods中)

    (2) 缺点: 相似的逻辑, 不容易复用, 在大项目中尤为明显

    (3) 虽然 optionsAPI 可以通过mixins 提取相同的逻辑, 但是也并不是特别好维护

  2. vue3 新增的就是 compositionAPI

    (1) compositionAPI 是基于 逻辑功能 组织代码的, 一个功能 api 相关放到一起

    (2) 即使项目大了, 功能多了, 也能快速定位功能相关的 api

    (3) 大大的提升了 代码可读性可维护性

  3. vue3 推荐使用 composition API, 也保留了options API

    即就算不用composition API, 用 vue2 的写法也完全兼容!!

setup 函数

composition的使用, 需要配置一个setup 函数

  1. setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
  2. 从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
  3. setup 中不能使用 this, this 指向 undefined
  4. 在模版中需要使用的数据和函数,需要在 setup 返回。
<template>
  <div class="container">
    <h1 @click="say()">{{msg}}</h1>
  </div>
</template>

<script>
export default {
  setup () {
    console.log('setup执行了')
    console.log(this)
    // 定义数据和函数
    const msg = 'hi vue3'
    const say = () => {
      console.log(msg)
    }

    return { msg , say}
  },
  beforeCreate() {
    console.log('beforeCreate执行了')
    console.log(this)
  }
}
</script>

reactive 函数

前置说明:

  1. setup 需要有返回值, 只有返回的值才能在模板中使用
  2. 默认普通的数据, 不是响应式的

作用: 传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理)

<template>
  <div>{{ obj.name }}</div>
  <div>{{ obj.age }}</div>
  <button @click="obj.name = 'ls'">改值</button>
</template>

<script>
import { reactive } from 'vue'

export default {
  setup () {
    // 1. setup 需要返回值, 返回的值才能在模板中使用
    // 2. 默认的普通的值不是响应式的, 需要用 reactive 函数
    const obj = reactive({
      name: 'zs',
      age: 18
    })

    return {
      obj
    }
  }
}
</script>

总结: 通常是用来定义响应式 对象数据

ref 函数

reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数!

作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。

  1. ref 函数接收一个的值, 返回一个ref 响应式对象, 有唯一的属性 value
  2. 在 setup 函数中, 通过 ref 对象的 value 属性, 可以访问到值
  3. 在模板中, ref 属性会自动解套, 不需要额外的 .value
  4. ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
<template>
  <div>{{ money }}</div>
  <button @click="money++">改值</button>
</template>

<script>
import { reactive, ref } from 'vue'
export default {
  setup() {
    let money = ref(100)
    money.value++
    return {
      money
    }
  }
}
</script>

ref 和 reactive 的最佳使用方式:

  • 明确的对象,明确的属性,用reactive,其他用 ref

问题小结:

  • ref 函数的作用是什么 ?
  • ref 函数包裹简单类型后, 会包裹成对象, 在模板中需要 .value 么? 在 setup 中需要 .value 么?

toRefs 函数

使用场景: 如果对一个响应数据, 进行解构 或者 展开, 会丢失他的响应式特性!

原因: vue3 底层是对 对象 进行监听劫持

作用: 对一个响应式对象的所有内部属性, 都做响应式处理

  1. reactive的响应式功能是赋值给对象的, 如果给对象解构或者展开, 会让数据丢失响应式的能力
  2. 使用 toRefs 可以保证该对象展开的每个属性都是响应式的
<template>
  <div>{{ money }}</div>
  <div>{{ car }}</div>
  <div>{{ name }}</div>
  <button @click="money++">改值</button>
</template>

<script>
import { reactive, ref, toRefs } from 'vue'
export default {
  setup() {
    // const money = ref(100) // 简单类型, ref
    // const car = reactive({ // 复杂类型, reactive
    //   brand: '赛车',
    //   price: 100
    // })
    // const name = ref('zs') // 简单类型, ref
    // money.value++ // ref需要.value取值

    const state = reactive({
      money: 100,
      car: {
        brand: '赛车',
        price: 100
      },
      name: 'zs'
    })

    return {
      ...toRefs(state)
    }
  }
}
</script>

计算属性computed函数

computed函数调用时, 要接收一个处理函数, 处理函数中, 需要返回计算属性的值

<template>
  <div>我今年的年纪 <input type="text" v-model="age"></div>
  <div>我明年的年纪 <input type="text" v-model="nextAge"></div>
</template>

<script>
import { ref, computed } from 'vue'
export default {
  setup() {
    const age = ref(10)

    // computed是一个函数
    // 1. 传入一个函数 getter 返回一个值
    // const nextAge = computed(() => {
    //   return +age.value + 1
    // })

    // 2. 传入一个对象, 包括get和set
    const nextAge = computed({
      get() {
        return +age.value + 1
      },
      set(value) {
        age.value = value - 1
      }
    })
    
    return {
      age,
      nextAge
    }
  }
}
</script>

侦听器watch函数

watch监视, 接收三个参数
1. 参数1: 监视的数据源
2. 参数2: 回调函数
3. 参数3: 额外的配置
<template>
  <div>{{ money }}</div>
  <div>{{ car }}</div>
  <button @click="changeCar">换车</button>
  <button @click="money++">加钱</button>
</template>

<script>
import { reactive, toRefs, watch } from 'vue'
export default {
  setup() {
    const state = reactive({
      money: 100,
      car: {
        brand: '宝马'
      }
    })

    const changeCar = () => {
      state.car.brand = '奔驰'
    }

    // watch监视, 接收三个参数
    // 参数1: 监视的数据源
    // 参数2: 回调函数
    // 参数3: 额外的配置
    // 可以直接监听整个reactive对象
    // watch(state,(value, oldValue) => {
    //   console.log('整个state', value)
    // })

    watch(() => state.money,(value, oldValue) => {
      console.log('value', value)
    })
    watch(() => state.car,(value, oldValue) => {
      console.log('value', value)
    }, {
      deep: true,
      immediate: true
    })

    return {
      ...toRefs(state),
      changeCar
    }
  }
}
</script>

钩子函数的使用

生命周期函数 vue3 中的生命周期函数, 需要在 setup 中调用

可以使用直接导入的 onX 函数注册生命周期钩子:

import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}

vue3 - 父子组件通信演示

父组件

<template>
  <div>
    <h3>我是父组件</h3>
    <my-demo @change-money="changeMoneyFn" :money="money"></my-demo>
  </div>
</template>

<script>
import MyDemo from './components/my-demo.vue'
import { ref } from 'vue'
export default {
  components: {
    MyDemo
  },
  setup () {
    const money = ref(100)
    const changeMoneyFn = (newMoney) => {
      money.value = newMoney
    }
    return {
      money,
      changeMoneyFn
    }
  }
}
</script>

子组件

<template>
  <div>我是my-demo组件 - {{ money }}</div>
  <button @click="changeMoney">改老爹的传值</button>
</template>

<script>
export default {
  emits: ['change-money'],
  props: {
    money: {
      type: Number,
      default: 1
    }
  },
  setup(props, context) {
    const changeMoney = () => {
      // 子传父
      context.emit('change-money', props.money + 1)
    }
    
    return {
      changeMoney
    }
  }
}
</script>

tips: vue3中 .sync 修饰符已被废弃, 使用 v-model:props 替代

依赖注入 - provide 和 inject

依赖注入, 可以非常方便的实现 跨层级的 组件通信

父组件利用 provide 提供数据

<template>
  <div>
    <h3>我是父组件</h3>
    <button @click="money++">改值</button>
    <my-demo2></my-demo2>
  </div>
</template>

<script>
import { provide, ref } from 'vue'
import MyDemo2 from './components/my-demo2.vue'
export default {
  setup() {
    const money = ref(100)
    // 组件提供了money属性
    provide('money', money)
    return {
      money
    }
  },
  components: {
    MyDemo2
  }
}
</script>

子组件 (子孙后代, 都可以拿到这个数据)

<template>
  <div>我是子组件 - {{ money }}</div>
</template>

<script>
import { inject } from 'vue'
export default {
  setup () {
    const money = inject('money')
    return {
      money
    }
  }
}
</script>

如果希望子传父, 可以 provide 传递一个方法

父组件

// 父组件提供修改money的方法
const changeMoney = (newMoney) => {
  money.value = newMoney
}
provide('changeMoney', changeMoney)

子组件

const changeMoney = inject('changeMoney')
changeMoney(200)

模板中 ref 的使用

联想之前的 ref 和 $refs, 获取模板的元素(dom元素,组件)

1 创建 ref => const hRef = ref(null)

2 模板中建立关联 => <h1 ref="hRef">钩子函数-----123</h1>

3 使用 => hRef.value

<template>
  <div>
    <h1 ref="hRef">钩子函数-----123</h1>
    <my-demo ref="myDemoRef"></my-demo>
  </div>
</template>

<script>
import MyDemo from './components/my-demo.vue'

import { onMounted, ref } from 'vue'

export default {
  components: {
    MyDemo
  },
  setup () {
    // 创建 ref
    const hRef = ref(null)
    const myDemoRef = ref(null)

    onMounted(() => {
      console.log(hRef.value)
      console.log(myDemoRef.value)
    })
    return {
      hRef,
      myDemoRef
    }
  }
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄昏终结者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值