Vue3-Day1

203 阅读8分钟

Vue3 优势

  1. 更容易维护
  • 组合式API
  • 更好的TypeScript支持
  1. 更快的速度
  • 重写diff算法
  • 模拟编辑优化
  • 更高效的组件初始化
  1. 更优的数据响应
  • Proxy

4.更小的体积

  • 良好的TreeShaking
  • 按需引入

Vue2 选项式 VS Vue3组合式API

image.png

需求:点击按钮,数字+1

image.png

vue2代码

<script>
export default {
  data(){
    return {
      count:0
    }
  },
  methods:{
    addCount(){
      this.count++
    }
  }
}
</script>

vue3代码

<script setup>
import { ref } from 'vue'
const count = ref(0)
const addCount = ()=> count.value++
</script>

代码量变少了

分散式维护转为集中式维护,更易封装复用

create-vue搭建Vue3项目

认识create-vue

create-vue是Vue官方新的脚手架工具,底层切换到了 vite (下一代前端工具链),为开发提供极速响应

2.png

使用create-vue创建项目

1. 前提环境条件

已安装16.0或更高版本的Node.js

node -v

  1. 创建一个Vue应用
npm init vue@latest

这一指令将会安装并执行 create-vue

3.png

项目目录和关键文件

image.png

  • new Vue ()创建一个应用实例=>createeApp()

  • createRouter() createStore()将创建实例进行封装,保证每个实例的独立封闭性

  • 不再要求唯一根元素

组合式API - setup选项

setup选项的写法和执行时机

写法

<script>
  export default {
    setup(){
      
    },
    beforeCreate(){
      
    }
  }
</script>

image.png

执行时机

<script>
export default{
  setup (){
    console.log('setup函数')
  },
  beforeCreate(){
     console.log('beforeCreate函数')
  }
}
</script>

image.png

setup执行时机在beforeCreate钩子之前

setup函数中获取不到this(this是undefined)

setup中写代码的特点

在setup函数中写的数据和方法需要在末尾以对象的方式return,才能给模版使用

<script>
  export default {
    setup(){
      const message = 'this is message'
      const logMessage = ()=>{
        console.log(message)
      }
      // 必须return才可以
      return {
        message,
        logMessage
      }
    }
  }
</script>

语法糖写法

<script setup>
//数据
const message = 'this is message'
//函数
const logMessage = ()= > {
  console.log(message)
}
</script>

语法糖原理

image.png

总结

image.png

reactive 和ref函数

reactive

接受对象类型数据的参数传入并返回一个响应式的对象

<script setup>
 // 导入
 import { reactive } from 'vue'
 // 执行函数 传入参数 变量接收
 const state = reactive({
   msg:'this is msg'
 })
 const setSate = ()=>{
   // 修改数据更新视图
   state.msg = 'this is new msg'
 }
</script>

<template>
  {{ state.msg }}
  <button @click="setState">change msg</button>
</template>

ref

接收简单类型或者对象类型的数据传入并返回一个响应式的对象

本质:是在原有传入数据的基础上,外层包了一层对象,包成了复杂类型

底层包成复杂类型之后,再借助reactive实现的响应式

注意点:

  1. 脚本访问数据,需要通过.value
  2. 在template中,.value不需要(帮我们扒了一层)
<script setup>
 // 导入
 import { ref } from 'vue'
 // 执行函数 传入参数 变量接收
 const count = ref(0)
 const setCount = ()=>{
   // 修改数据更新视图必须加上.value
   count.value++
 }
</script>

<template>
  <button @click="setCount">{{count}}</button>
</template>

reactive VS ref

  1. 共同点:都是用来生成响应式数据

  2. 不同点

    (1) reactive不能处理简单类型的数据

    (2) ref参数类型支持更好,但是必须通过.value做访问修改

    (3) ref函数内部的实现依赖于reactive函数

  3. 在实际工作中的推荐

    推荐使用ref函数,更加灵活

组合式API - computed

计算属性基本思想和Vue2保持一致,组合式API下的计算属性只是 修改了API写法

核心步骤

  1. 导入conputed函数
  2. 执行函数 在回调函数中return基于响应式数据做计算的值,用变量接收
<script setup>
// 导入
import {ref, computed } from 'vue'
// 原始数据
const count = ref(0)
// 计算属性
const doubleCount = computed(()=>count.value * 2)

// 原始数据
const list = ref([1,2,3,4,5,6,7,8])
// 计算属性list
const filterList = computed(item=>item > 2)
</script>

计算属性小案例

<script setup>
import {ref, computed } from 'vue'
const list = ref([1,2,3,4,5,6,7,8])
// 基于list诞生一个计算属性,从list中过滤出>2
const computedList = computed(() =>{
   return list.value.filter(item =>item>2)
})
</script>
<template>
  <div>
     <div>原始数据:{{list}}</div>
     <div>计算后的数据:{{computedList}}</div>
  </div>
</template>

效果如下:

image.png

总结

  1. 计算属性中不应该有“副作用”
  • 如异步请求/修改dom

2.避免直接修改计算属性的值

  • 计算属性应该是只读的,特殊情况可以配置 get set

watch 函数

作用:侦听一个或多个数据变化,数据变化时执行回调函数

两个额外参数:immediate(立即执行), deep(深度侦听)

侦听单个数据

<script setup>
  // 1. 导入watch
  import { ref, watch } from 'vue'
  const count = ref(0)
  // 2. 调用watch 侦听变化
  watch(count, (newValue, oldValue)=>{
    console.log(`count发生了变化,老值为${oldValue},新值为${newValue}`)
  })
</script>
watch(ref对象,(newValue,oldValue) =>{...})
 
 watch(count,(newValue,oldValue) =>{  
 
 console.log(newValue,oldValue)
 })

侦听多个数据

侦听多个数据,第一个参数可以改写成数组的写法

<script setup>
  // 1. 导入watch
  import { ref, watch } from 'vue'
  const count = ref(0)
  const name = ref('cp')
  // 2. 调用watch 侦听变化
  watch([count, name], ([newCount, newName],[oldCount,oldName])=>{
    console.log(`count或者name变化了,[newCount, newName],[oldCount,oldName])
  })
</script>
watch{[ref对象1,ref对象2],(newARR,oldArr)=>{...}}

实例:

<script setup>
  import { ref, watch } from 'vue'
  const count = ref(0)
  const nickname = ref('李明德')
  
  const changeCount = () =>{
     count.value++
  }
  const changeNickname = () =>{
     nickname.value = 'iu'
  }
 
</script>
<template>
  <div>{{ count }}</div>
  <button @click="changeCount">改数字</button>
  <div>{{ nickname }}</div>
  <button @click="changeNickname">改昵称</button>
</template>

效果如下:

image.png

immediate

在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调

<script setup>
  // 1. 导入watch
  import { ref, watch } from 'vue'
  const count = ref(0)
  // 2. 调用watch 侦听变化
  watch(count, (newValue, oldValue)=>{
    console.log(`count发生了变化,老值为${oldValue},新值为${newValue}`)
  },{
    immediate: true
  })
</script>

deep

通过watch监听的ref对象默认是 浅层侦听 的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep

<script setup>
  // 1. 导入watch
  import { ref, watch } from 'vue'
  const state = ref({ count: 0 })
  // 2. 监听对象state
  watch(state, ()=>{
    console.log('数据变化了')
  })
  const changeStateByCount = ()=>{
    // 直接修改不会引发回调执行
    state.value.count++
  }
</script>

<script setup>
  // 1. 导入watch
  import { ref, watch } from 'vue'
  const state = ref({ count: 0 })
  // 2. 监听对象state 并开启deep
  watch(state, ()=>{
    console.log('数据变化了')
  },{deep:true})
  const changeStateByCount = ()=>{
    // 此时修改可以触发回调
    state.value.count++
  }
</script>

deep 深度监视,默认watch进行浅层监视
const ref1 = ref(简单类型) 可以直接监视
const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化

对于对象中的属性进行监视

需求:在不开启deep的前提下,侦听age变化,只有age变化时才执行回调

const info = ref({
  name:'cp',
  age:18
})

watch(
  () => info.value.age,
  () => console.log('age发生变化了')
)

实例:

watch(() => userInfo.value.age,(newValue,oldValue) => {
   console.log(newValue,oldValue)
})

总结:

image.png

组合式API

生命周期函数

选项式 VS 组合式

image.png

beforeCreate 和created 的相关代码一律放在setup中执行

生命周期函数基本使用

  1. 导入生命周期函数
  2. 执行生命周期函数,传入回调
<scirpt setup>
import { onMounted } from 'vue'
onMounted(()=>{
  // 自定义逻辑
})
</script>

执行多次

生命周期函数执行多次的时候,会按照顺序依次执行

<script setup>
  
  const getList =() =>{

  }
  //一进入页面的请求
  getList()

  //如果有写代码需要在mounted中执行
  onMounted (() =>{
    console.log('mounted生命周期函数')
  })
  //写成函数的调用方式可以调用多次,并不会冲突,而是按照顺序依次执行
</script>
<template>
 
</template>

父子通信

父传子

基本思路

  1. 父组件中给子组件绑定属性
  2. 子组件内部通过props选项接收数据

image.png

实例:

App.vue
<script setup>
  //局部组件导入进来就能用
  import {ref} from'vue'
 import SonCom from'@/components/son-com.vue'

 const money = ref(100)
 const getMoney = () =>{
  money.value +=10
 }
</script>


<template>
 <div>
  <h3>
    我是父组件-{{ money }}
    <button @click="getMoney">挣钱</button>
  </h3>
  
  <!--给子组件,添加属性的方式传值  -->
  <SonCom car="宝马车" :money="money"></SonCom>
 </div>
</template>

son-com.vue

<script setup>
//由于写了setup所以无法直接配置props选项
//所以此处借助编译器宏函数接收子组件传递的数据
//子组件
const props = defineProps({
    cart:String,
    money:Number
})
console.log(props.car)
console.log(props.money)
</script>

<template>
    <!-- 对于子组件传递过来的数据,模板中可以直接使用 -->
    <div class="son">我是子组件 - {{ car }} - {{ money }}</div>
</template>
<style>
.son{
    border:1px solid #000;
    padding: 30px;
}
</style>
效果如下:

image.png

defineProps原理:

就是编译阶段一个标识,实际编译器解析式、时,遇到后会进行编译转换

image.png

子传父

基本思路

  1. 子组件内部通过 emit 方法触发事件

  2. 父组件中给子组件标签通过@绑定事件

image.png

实例:

App.vue
<script setup>
  //局部组件导入进来就能用
  import {ref} from'vue'
 import SonCom from'@/components/son-com.vue'

 const money = ref(100)
 const getMoney = () =>{
  money.value +=10
 }
 const changFn =(newMoney) =>{
  money.value = newMoney
 }
</script>


<template>
 <div>
  <h3>
    我是父组件-{{ money }}
    <button @click="getMoney">挣钱</button>
  </h3>
  
  <!--给子组件,添加属性的方式传值  -->
  <SonCom 
  @change-money="changFn"
  car="宝马车" 
  :money="money"></SonCom>
 </div>
</template>

son-com.vue
<script setup>
//由于写了setup所以无法直接配置props选项
//所以此处借助编译器宏函数接收子组件传递的数据
//子组件
const props = defineProps({
    cart:String,
    money:Number
})
const emit = defineEmits(['changeMoney'])
console.log(props.car)
console.log(props.money)
const buy = () =>{
    //需要emit触发事件
    emit('changeMoney',5)
}
</script>

<template>
    <!-- 对于子组件传递过来的数据,模板中可以直接使用 -->
    <div class="son">我是子组件 - {{ car }} - {{ money }}</div>
    <button>花钱</button>
</template>
<style>
.son{
    border:1px solid #000;
    padding: 30px;
}
</style>

效果如下:

image.png image.png

总结:

image.png

模板引用

概念:

通过 ref标识 获取真实的 dom对象或者组件实例对象

实现步骤:

  1. 调用ref函数生成一个ref对象
  2. 通过ref标识绑定ref对象到标签
<script setup>
import { ref } from 'vue'
//1. 调用ref函数生成一个ref对象
const h1Ref = ref(null)
</script>

<template>
  //2. 通过ref标识绑定ref对象到标签
  <h1 ref = "h1Ref">我是dom标签h1</h1>
</template>

defineExpose()

默认情况下在<script setup>语法糖下 组件内部的属性和方法是不开放 给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法容许访问

image.png

说明:指定testMessage属性可以被访问到

总结

image.png

provide 和inject

作用和场景

顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信

image.png

跨层传递数据

实现步骤
  1. 顶层组件通过 provide 函数提供数据

  2. 底层组件通过 inject 函数提供数据

顶层组件:

provide('key',顶层组件中的数据)

底层组件:

const message = inject('key')
实例
App.vue
<script setup>
  import { provide } from 'vue';
import centerCom from './components/center-com.vue';
//1.跨层传递普通数据
provide('theme-color','pink')
//2.跨层传递响应式数据
const count =ref(100)
provide('count',count)

setTimeout(() => {
   count.value = 500
},2000)

//3.跨层级传递函数=>给孙后代传递可以改数据的方法
provide('changeCount',(newCount) =>{
  count.value = newCount
})
</script>


<template>
 <div>
  <h1>我是顶层组件</h1>
  <centerCom></centerCom>
 </div>
</template>

center-com.vue
<script setup>
import BottomCom from './bottom-com.vue'
</script>

<template>
<div>
  <h2>我是中间组件</h2>
  <BottomCom></BottomCom>
</div>
</template>
bottom-com.vue
<script setup>
import { inject } from 'vue'
const themeColor = inject('theme-color')
const count = inject('count')
const changeCount = inject('changeCount')
const clickFn = () => {
  changeCount(1000)
}
</script>

<template>
<div>
  <h3>我是底层组件-{{ themeColor }} - {{ count }}</h3>
  <button @click="clickFn">更新count</button>
</div>
</template> 
效果如下:

image.png