Vue3学习笔记

Vue3.0的优点

  1. 性能提升,运行速度是vue2.x的1.5倍左右
  2. 体积更小,按需编译体积比vue2.x要更小
  3. 类型推断,更好的支持ts
  4. 高级给与,暴露了更底层的API和提供更先进的内置组件
  5. 组合API(composition API),更好地组织、封装和复用逻辑

vite

  • 概述

是一个更加轻量的vue项目脚手架工具,相对于vue-cli,它默认安装的插件更少

  • 创建项目

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

  • 启动项目

npm run dev 或者 yarn dev

创建vue应用

  • main.js
//1. 创建main.js,并从vue中导入createApp函数
import { createApp } from 'vue'
//2. 创建一个根组件App.vue,并导入到main.js中
import App from './App.vue'
//3. 使用createApp创建应用实例
const app = createApp(App)
//4. 应用示例挂载到#app容器中
app.mount("#app")
  • App.vue
<template>
  <div class="container">
    根组件
  </div>
</template>
<script>
export default {
  name: 'App'
}
</script>

组合API

选项API与组合API
选项API(Options API)
  • 说明

在Vue2.x中项目使用的就是选项api写法,这种写法的风格就是,在data中写数据,在methods中写函数,…,即一个功能逻辑的代码分散

  • 优点

易于学习和使用,因为写代码的位置已经约定好了

  • 缺点

代码组织性差,即相似的逻辑代码不便于复用,一旦逻辑复杂、代码多了不便阅读,虽然提供mixins用来封装逻辑,但是出现数据函数覆盖的概率很大,不好维护

组合API(Composition API)
  • 说明

在vue3.0中将使用组合API,即一个功能逻辑的代码组织在一起(包含数据、函数等等)【也支持选项API用法】

  • 优点

功能逻辑复杂的情况下,各个功能逻辑代码组织在一起,便于阅读与维护

  • 缺点

需要良好的代码组织能力与拆分逻辑能力

setup函数
说明

setup是一个新的组件选项,作为组件中使用组合API的起点

从组件生命周期来看,它的执行在组件示例创建之前(即vue2.x的beforeCreate)执行,这意味着在setup中this还不是组件实例,还是个undefined

在模板中需要使用的数据和函数,需要在setup返回

示例
<template>
  <div class="container">
    <h1 @click="say">{{ msg }}</h1>
  </div>
</template>
<script>
export default {
  name: 'App',

  //1. 组合api的起点(组合api在此当中编写)
  //2. 可以理解为在beforeCreate之前执行(组件实例创建之前,所以无法使用this)
  //3. 模板的数据和函数,有setup提供,即需要在setup中返回
  setup() {
    const msg = 'hello vue3'
    const say = () => { console.log(msg) }
    return {msg, say} //返回的数据需要是个对象格式
  }
}
</script>
钩子函数
setup: 创建实例的
onBeforeMount: 挂载DOM前
onMounted: 过载DOM后
onBeforeUpdate: 更新组件前
onUpdated: 更新组件后
onBeforeUnmount: 卸载销毁前
onUnmounted: 卸载销毁后
//可以注册多个相同的钩子(执行顺序:先注册先执行)
<template>
  <div class="container">
    根组件
  </div>
</template>
<script>
/*注意:先要进行导入*/
import {onBeforeMount, onMounted} from "vue";

export default {
  name: 'App',
  setup () {
    //1.DOM渲染前钩子
    onBeforeMount(() => {
      //null
      console.log("DOM渲染前钩子", document.querySelector('.container'))
    })

    //2.DOM渲染后钩子
    //功能A
    onMounted(() => {
      //<div class="container"> 根组件 </div>
      console.log("DOM渲染后钩子1", document.querySelector('.container'))
    })

    //3.可以注册多个相同的钩子(先注册先执行)
    //功能B
    onMounted(() => {
      console.log("DOM渲染后钩子2", document.querySelector('.container'))
    })
  }
}
</script>
reactive函数
说明

reactive函数用于定义复杂类型的响应式数据

示例
<template>
  <div class="container">
    根组件
    <div>obj: &lt; {{obj.name1}} -- {{obj.age}} &gt;</div>
    <div>obj2: &lt; {{obj2.name}} -- {{obj2.age}} &gt;</div>
    <button @click="updateName">修改名字</button>
  </div>
</template>
<script>
import {reactive} from 'vue'
export default {
  name: 'App',
  setup() {

    //1.数据
    const obj = {
      name1: 'xiaoming',
      age: 18
    }

    //2.修改名字 4.页面数据并未响应式显示地修改后的数据,因为setup中的数据非响应式的数据
    //5.注意:除了obj2,里面的obj也会变成响应式,//7.且如果将obj2的修改注释了,又不会
    const updateName = () => {
      console.log("修改名字了") //3.会打印,即名字数据已被修改
      obj.name1 = "小明"
      obj2.name = "小明" //6.注释掉
    }

    //5.使用reactive
    const obj2 = reactive({
      name: 'xiaoming',
      age: 18
    })

    return {obj, updateName, obj2}
  }
}
</script>
toRef函数
说明

toRef函数用于转换响应式对象中某个属性为单独响应数据,并且值是关联的。

  • 使用场景

如有一个响应式对象数据,但是模板中只需要使用其中一项数据

示例
<template>
  <div class="container">
    根组件
    <div>obj: &lt; {{name}} &gt;</div>
    <button @click="updateName">修改名字</button>
  </div>
</template>
<script>
import {reactive, toRef} from "vue";

export default {
  name: 'App',
  setup() {
    //1.响应式数据对象
    const obj = reactive({
      name: "xiaoming",
      age: 18
    })
    //2.将对象中的具体的某个属性抽取、定义并包装成一个响应式对象数据
    const name = toRef(obj, 'name')
    const updateName = () => {
      name.value = '小明' //具体数值存放在包装的响应式对象的value属性中,所以需要通过修改value的值来实现数据的修改
    }
    //3.返回出数据
    return {name, updateName}
  }
}
</script>
toRefs函数
说明

toRef函数用于转换响应式对象中所有属性为单独响应数据,并且值是关联的(相当于reactive省去了对象点属性 的 对象点)。

示例
<template>
  <div class="container">
    根组件
    <div>obj: &lt; {{name}} {{age}}&gt;</div>
    <button @click="updateName">修改名字</button>
  </div>
</template>
<script>
import {reactive, toRefs} from "vue";

export default {
  name: 'App',
  setup() {
    //1.响应式数据对象
    const obj = reactive({
      name: "xiaoming",
      age: 18
    })
    //2.将对象中的所有属性抽取、定义并包装成一个响应式对象
    const reactiveObj = toRefs(obj)
    const updateName = () => {
      reactiveObj.name.value = '小明'
      reactiveObj.age.value = 19
    }
    //3.返回出数据
    return {...reactiveObj, updateName}
  }
}
</script>
ref函数
说明

ref函数用于定义(一般用于简单类型,也可以复杂类型)响应式数据。

示例
<template>
  <div class="container">
    根组件
    <div>&lt; {{name}} {{age}}&gt;</div>
    <button @click="updateName">修改</button>
  </div>
</template>
<script>
import {ref} from "vue";

export default {
  name: 'App',
  setup() {

    /**
     * 也可以用于定义复杂类型的响应式数据
     * 通常在对数据的类型无法确定的时候会如此使用它
     * let data = ref(null)
     * data.value = 123
     * data.value = [1, 2, 3]
     * data.value = {"key": 123}
     */

    const name = ref('xiaoming')
    const age = ref(18)
    const updateName = () => {
      name.value = '小明'
      age.value = 19
    }
    return {name, age, updateName}
  }
}
</script>
computed函数
说明

computed函数用于定义计算属性

示例
<template>
  <div class="container">
    <div>{{name}} 今年:{{age}}岁</div>
    <div>{{name}} 后年:{{newAge}}岁</div>
    <button @click="yearAgo">明年</button>
  </div>
</template>
<script>
import {reactive, computed, toRefs} from "vue";

export default {
  name: 'App',
  setup() {
    const obj = reactive({
      name: "小明",
      age: 18
    })
    const newAge = computed(() => {
      return obj.age + 2
    })
    const yearAgo = () => {
      obj.age++
    }
    return {...toRefs(obj), newAge, yearAgo}
  }
}
</script>
get & setv-model
<template>
  <div class="container">
    <div>{{name}} 今年:{{age}}岁</div>
    <div>{{name}} 后年:{{newAge}}岁</div>
  </div>
</template>
<script>
import {computed, ref} from "vue";

export default {
  name: 'App',
  setup() {
    const age = ref(18)
    let newAge = computed({
      get () {
        return age.value + 2
      },
      set (value) {
        age.value = value - 2
      }
    })
    setTimeout(() => newAge.value = 21, 2000)
    return {age, newAge}
  }
}
</script>
watch函数
说明

watch函数用于定义监听器。

示例
<template>
  <div class="container">
    <p>count value: {{count}}</p>
    <button @click="add">modify - ref</button>
    <p>obj: {{obj.desc}}, {{obj.num}}</p>
    <button @click="updateObj">modify - reactive</button>
    <p>subObj: {{obj.subObj.id}}, {{obj.subObj.name}}</p>
    <button @click="updateSubObj">modify - reactive2</button>
  </div>
</template>

<script>
import {ref, watch, reactive} from 'vue'
export default {
  name: 'App',
  setup() {
    //1. 监听一个 ref 数据
    const count = ref(0)
    const add = () => {
      count.value++
    }
    watch(count, (newValue, oldValue) => {
      console.log(newValue, oldValue)
    }, {
      immediate: true //立即执行,即打开页面时就默认执行一次
    })

    //2. 监听一个 reactive 数据
    const obj = reactive({
      desc: "test",
      num: 20,
      subObj: {
        id: 1,
        name: "sub object"
      }
    })
    const updateObj = () => {
      obj.desc = "测试";
      obj.num = 21;
    }
    watch(obj, (newValue, oldValue) => {
      console.log(newValue, oldValue) //响应式数据,都是修改后的
    })

    //3. 同时监听 ref 和 reactive 数据(监听多个数据变化)
    watch([count, obj], (newValue, oldValue) => {
      console.log(newValue, oldValue)
    })

    //4. 监听 reactive 中对象的指定属性的数据变化
    //使用函数-返回要监听的属性
    watch(()=>obj.num, (newValue, oldValue) => {
      console.log("监听 reactive 中对象的指定属性的数据变化", newValue, oldValue)
    })

    //5. 监听 reactive 中的指定复杂类型属性
    //如果直接监听obj,则无需深度监听
    const updateSubObj = () => {
      obj.subObj.id = 2
    }
    watch(()=>obj.subObj, (newValue, oldValue)=>{
      console.log(newValue, oldValue)
    }, {
      deep: true //需要深度监听
    })

    return {count, add, obj, updateObj, updateSubObj}
  }
}
</script>
ref属性
说明

ref属性用于绑定并获取DOM或者组件实例。

示例
<template>
  <div class="container">
    <div ref="box">box</div>
    <ul>
      <li v-for="i in 4" :key="i" :ref="setDom">{{i}}</li>
    </ul>
  </div>
</template>
<script>
import {nextTick, onMounted, ref} from "vue";

export default {
  name: 'App',
  setup() {
    /**
     * 1.获取单个元素
     *   vue2.x
     *   1.<div ref="box">box</div>
     *   2.this.$refs.box
     */
    //1.1 定义一个空的响应式数据,用于接收数据
    const box = ref(null)
    //1.3 验证
    onMounted(() => {
      console.log(box.value.innerText) //box
    })

    /**
     * 2.获取v-for遍历的多个元素
     *   vue2.x
     *   1.<ul><li v-for="i in 4" :key="i" ref="li">{{i}}</li></ul>
     *   2.this.$refs.li
     */
    //2.1 定义一个空的数组,用于接收数据
    const lis = []
    //2.2 定义一个函数,往空数组进行push DOM
    const setDom = (el) => {
      console.log("setDom start...")
      console.log(el) //li标签
      lis.push(el)
    }
    //2.4 验证
    nextTick(() => {
      console.log(lis)
    })

    //1.2 将box返回出去
    //2.3 将setDom返回出去
    return {box, setDom}
  }
}
</script>
组件通信
父传子 - props
  1. Son.vue
<template>
  <div class="container">
    <h1>子组件</h1>
    <p>{{data}}</p>
  </div>
</template>
<script>
export default {
  name: 'Son',
  //子组件使用props接收父组件数据
  props: {
    data: {
      type: Number,
      default: 0
    }
  },
  setup(props) { //默认第一个参数就是props
    //vue3的新用法,在setup中获取父组件数据
    console.log(props.data)
  }
}
</script>

  1. App.vue
<template>
  <div class="container">
    <h1>父组件</h1>
    <p>{{data}}</p>
    <hr>
    <Son :data="data"/>
  </div>
</template>
<script>
import Son from './components/Son.vue'
import {ref} from "vue";

export default {
  name: 'App',
  components: {
    Son
  },
  setup() {
    const data = ref(1000000)
    return {data}
  }
}
</script>
子传父 - emit
  1. Son.vue
<template>
  <div class="container">
    <h1>子组件</h1>
    <button @click="changeData">change</button>
  </div>
</template>
<script>

export default {
  name: 'Son',
  setup(props, {emit}) { //context中包装了emit对象
    const data = 10000000

    const changeData = () => {
      emit('change-data', data)
    }

    return {data, changeData}
  }
}
</script>

  1. App.vue
<template>
  <div class="container">
    <h1>父组件</h1>
    <p>{{data}}</p>
    <hr>
    <Son @change-data="getData"/>
  </div>
</template>
<script>
import Son from './components/Son.vue'

export default {
  name: 'App',
  components: {
    Son
  },
  setup() {
    const getData = (data) => {
      console.log(data)
    }
    return {getData}
  }
}
</script>
依赖注入 - provide & inject
  1. App.vue
<template>
  <div class="container">
    <h1>父组件 {{money}}</h1>
    <button @click="money=1000">发红包</button>
    <hr>
    <Son/>
  </div>
</template>

<script>
import Son from './Son.vue'
import {provide, ref} from "vue";
export default {
  name: 'App',
  components: {
    Son
  },
  setup() {
    const money = ref(100)
    //使用 provide 将 money 提供给 Son, GrandSon
    //注意:单向原则,即接受方组件的修改无法影响提供方组件
    provide('m', money)

    //消费
    const changeMoney = (count) => {
      money.value -= count
    }
    //使用 provide 将函数 changeMoney 提供给GrandSon
    provide('cm', changeMoney)

    return {money}
  }
}
</script>


  1. Son.vue
<template>
  <div class="container">
    <h2>子组件 {{money}}</h2>
    <hr>
    <GrandSon></GrandSon>
  </div>
</template>

<script>
import GrandSon from './GrandSon.vue'
import {inject} from "vue";
export default {
  name: 'Son',
  components: {
    GrandSon
  },
  setup() {
    //使用 inject 接受父辈数据
    const money = inject('m')
    return {money}
  }
}
</script>

  1. GrandSon.vue
<template>
  <div class="container">
    <h3>孙组件 {{money}}</h3>
    <button @click="fn">消费50元</button>
  </div>
</template>

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

export default {
  name: 'GrandSon',
  setup() {
    //使用 inject 接受父辈数据
    const money = inject('m')

    /**
     * 需求:孙组件消费50,要求通知父组件修改
     * 根据单向原则,数据是哪个组件定义的就由该组件修改,即父组件进行修改操作
     */
    //使用 inject 接受父辈函数
    const changeMoney = inject('cm')
    //用于触发changeMoney函数
    const fn = () => {
      changeMoney(50)
    }

    return {money, fn}
  }
}
</script>
双向绑定
说明

这里指不直接使用v-model,而是通过用v-model:属性的方式来实现数据的双向绑定效果,要求是属性名和自定义事件名是一样的

v-model语法糖
  1. App.vue
<template>
  <div class="container">
    <h2>父组件 {{number}}</h2>
    <button @click="fn">改变</button>
    <hr>
    <!--  $event: 自定义事件的传值(50) / 系统事件的事件对象(@click="$event.target.style.color='red'")  -->
    <Son :modelValue="number" @update:modelValue="number=$event"></Son>

    <!--  语法糖(实质上内部就是以上的实现方式)  -->
    <Son v-model="number"/>
  </div>
</template>

<script>
import Son from './Son.vue'
import {ref} from "vue";
export default {
  name: 'App',
  components: {
    Son
  },
  setup() {
    const number = ref(100)
    const fn = () => {
      number.value = 1000000
    }
    return {number, fn}
  }
}
</script>

  1. Son.vue
<template>
  <div class="container">
    <h3>子组件 {{modelValue}}</h3>
    <button @click="fn">改变</button>
  </div>
</template>

<script>
export default {
  name: 'Son',
  props: {
    modelValue: {
      type: Number,
      default: 0
    }
  },
  setup(props, {emit}) {
    const fn = () => {
      emit("update:modelValue", 50)
    }
    return {fn}
  }
}
</script>
示例
  1. Son.vue
<template>
  <div class="container">
    <h1>子组件</h1>
    <p>{{ data }}</p>
    <button @click="changeData">change</button>
  </div>
</template>
<script>

export default {
  name: 'Son',
  props: {
    data: {
      type: Number,
      default: 0
    }
  },
  setup(props, {emit}) { //context中包装了emit对象
    console.log(props)
    const changeData = () => {
      emit('update:data', 10000000)
    }

    return {changeData}
  }
}
</script>

  1. App.vue
<template>
  <div class="container">
    <h1>父组件</h1>
    <p>{{ data }}</p>
    <hr>
    <Son :data="data" @update:data="changeData"/>
    <!-- 属性名和自定义事件名相同时:简写(vue2.x:<Son :data.sync/>)-->
    <Son v-model:data="data"/>
  </div>
</template>
<script>
import Son from './components/Son.vue'
import {ref} from "vue";

export default {
  name: 'App',
  components: {
    Son
  },
  setup() {
    const data = ref(1000000)
    const changeData = (newData) => {
      data.value = newData
    }
    return {data, changeData}
  }
}
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值