小兔鲜项目----vue3入门

92 阅读6分钟

小兔鲜项目----vue3入门

目录

[TOC]

认识Vue3

为什么需要学Vue3?

Vue3组合式API体验

<template>
  <div>
    <button @click="addCount">{{ count }}</button>
  </div>
</template>
<!-- <script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    addCount() {
      this.count++;
    },
  },
};
</script> -->
<script setup>
import { ref } from "vue";
const count = ref(0);
const addCount = () => count.value++;
</script>

Vue3更多的优势

使用create-vue搭建Vue3项目

认识 create-vue

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

使用create-vue创建项目

  1. 前提环境条件

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

  1. 创建一个Vue应用

npm init vue@latest

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

熟悉项目目录和关键文件

关键文件:

  1. vite.config.js - 项目的配置文件 基于vite的配置
  2. package.json - 项目包文件 核心依赖项变成了 Vue3.x 和 vite 3. main.js - 入口文件 createApp函数创建应用实例
  3. app.vue - 根组件 SFC单文件组件 script - template - style

变化一:脚本script和模板template顺序调整 变化二:模板template不再要求唯一根元素 变化三:脚本script添加setup标识支持组合式API

  1. index.html - 单页入口 提供id为app的挂载点

入口文件

import './assets/main.css'
//  vue2:new vue() 创建一个应用实例对象
import { createApp } from 'vue'
import App from './App.vue'
// 以app 作为参数 生成一个应用实例对象
//  挂载到一个id为 app的节点上
createApp(App).mount('#app')

组合式API - setup选项

setup选项的写法和执行时机

setup比beforeCreate之前触发

<script >
export default {
  setup() {
    console.log("setup");
  },
  beforeCreate() {
    console.log("beforeCreate");
  },
};
</script>

setup选项中写代码的特点

<script >
export default {
  setup() {
    console.log("setup", this);
    const message = "this is massage";
    const messageLog = () => {
      console.log(message);
    };
    return {
      message,
      messageLog,
    };
  },
};
</script>

<script setup >
console.log("setup", this);
const message = "this is massage";
const messageLog = () => {
  console.log(message);
};
  1. setup选项的执行时机?   beforeCreate钩子之前 自动执行

  2. setup写代码的特点是什么?  定义数据 + 函数 然后以对象方式return

  3. setup中的this还指向组件实例吗?  指向undefined

组合式API - reactive和ref函数

reactive()

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

  1. 从 vue 包中导入 reactive 函数
<template>
  <div>
    <button @click="setCount">{{ state.count }}</button>
  </div>
</template>
<script setup>
// 导入函数
import { reactive } from "vue";
// 执行函数 传入一个对象类型的参数 变量接受
const state = reactive({
  count: 0,
});
const setCount = () => {
  state.count++;
};
</script>

ref()

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

  1. 从 vue 包中导入 ref 函数
<script setup>
// 导入函数
import { ref } from "vue";
// 执行函数 传入参数【 简单类型+对象类型 】 变量接受
const count = ref(0);
const setCount = () => {
  //脚步区域修改ref产生的响应式对象数据,必须通过 .value属性
  count.value++;
};
</script>
  1. reactive和ref函数的共同作用是什么?  用函数调用的方式生成响应式数据

  2. reactive vs ref ?

    1. reactive不能处理简单类型的数据
    1. ref参数类型支持更好但是必须通过.value访问修改 3. ref函数的内部实现依赖于reactive函数

  1. 在实际工作中推荐使用哪个?  推荐使用ref函数,更加灵活,小兔鲜项目主用ref

组合式API - computed

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

核心步骤:

  1. 导入computed函数
  2. 执行函数 在回调参数中return基于响应式数据做计算的值,用变量接收
<template>
  <div>
    <div>原始响应式数组:{{ list }}</div>
    <div>计算属性数组:{{ computedList }}</div>
  </div>
</template>
<script setup>
// 原始响应式数组
import { ref } from "vue";
const list = ref([1, 2, 3, 4, 5, 6, 7, 8]);
// 1.导入 computed
import { computed } from "vue";
// 2.执行函数 return 计算之后的值 变量接受
const computedList = computed(() => {
  // 做计算 根据一个值计算得到一个新值
  return list.value.filter((item) => item > 2);
});
setTimeout(() => {
  list.value.push(9, 10);
}, 3000);
</script>

最佳实践

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

  2. 避免直接修改计算属性的值  计算属性应该是只读的

组合式API - watch

watch函数

作用: 侦听一个或者多个数据的变化,数据变化时执行回调函数 俩个额外参数:1. immediate(立即执行) 2. deep(深度侦听)

基础使用 - 侦听单个数据

  1. 导入watch函数
  2. 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
<template>
  <div>
    <button @click="setCount">{{ count }}</button>
  </div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const count = ref(0);
const setCount = () => {
  count.value++;
};
// watch 监听单个数据 ref对象不需要加.value
watch(count, (newValue, oldValue) => {
  console.log("count变化了" + newValue, oldValue);
});
</script>

基础使用 - 侦听多个数据

说明:同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调

<template>
  <div>
    <button @click="setCount">{{ count }}</button>
    <button @click="setName">{{ name }}</button>
  </div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const count = ref(0);
const name = ref("ZS");
const setCount = () => {
  count.value++;
};
const setName = () => {
  name.value = "ls";
};
// watch 监听多个数据
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
  console.log("count或者name变化了" + [newCount, newName], [oldCount, oldName]);
});
</script>

immediate

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

<template>
  <div>
    <button @click="setCount">{{ count }}</button>
  </div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const count = ref(0);
const setCount = () => {
  count.value++;
};
// watch 立即执行
watch(
  count,
  (newValue, oldValue) => {
    console.log("count变化了" + newValue, oldValue);
  },
  {
    immediate: true,
  }
);
</script>

deep

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

<template>
  <div>
    <button @click="setCount">{{ state.count }}</button>
  </div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const state = ref({ count: 1 });
const setCount = () => {
  state.value.count++;
};
// watch 深度监听
watch(
  state,
  () => {
    console.log("count变化了");
  },
  {
    deep: true,
  }
);
</script>

精确侦听对象的某个属性

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

<template>
  <div>
    <button @click="setCount">{{ state.count }}</button>
    <button @click="setName">{{ state.name }}</button>
  </div>
</template>
<script setup>
// 原始响应式数组
import { ref, watch } from "vue";
const state = ref({
  name: "zs",
  count: 0,
});
const setCount = () => {
  state.value.count++;
};
const setName = () => {
  state.value.name = "ls";
};
// watch 精确侦听对象的某个属性
watch(
  () => state.value.count,
  () => {
    console.log("count变化了");
  }
);
</script>
  1. 作为watch函数的第一个参数,ref对象需要添加.value吗? 不需要,watch会自动读取

  2. watch只能侦听单个数据吗? 单个或者多个

  3. 不开启deep,直接修改嵌套属性能触发回调吗?不能,默认是浅层侦听

  4. 不开启deep,想在某个层次比较深的属性变化时执行回调怎么做?  可以把第一个参数写成函数的写法,返回要监听的具体属性

组合式API - 生命周期函数

Vue3的生命周期API (选项式 VS 组合式)

生命周期函数基本使用

  1. 导入生命周期函数
  2. 执行生命周期函数 传入回调

执行多次

生命周期函数是可以执行多次的,多次执行时传入的回调会在时机成熟时依次执行
<template>
  <div></div>
</template>
<script setup>
// 引入函数
import { onMounted } from "vue";
onMounted(() => {
  console.log("mounted1");
});
// 执行函数传入回调
onMounted(() => {
  console.log("mounted2");
});
</script>
  1. 组合式API中生命周期函数的格式是什么? on + 生命周期名字

  2. 组合式API中可以使用onCreated吗? 没有这个钩子函数,直接写到setup中

  3. 组合式API中组件卸载完毕时执行哪个函数? onUnmounted

组合式API - 父子通信

组合式API下的父传子

基本思想

  1. 父组件中给子组件绑定属性
  2. 子组件内部通过props选项接收
//父组件
<template>
  <div>
    <h2>父组件App</h2>
    <!-- 1.绑定属性 -->
    <son message="father message"></son>
  </div>
</template>
<script setup>
// setup 语法糖下局部组件无需注册可以直接使用
import son from "./components/HelloWorld.vue";
</script>
 
 
//子组件
<script setup>
// defineProps 接收数据
const proprs = defineProps({
  message: "String",
});
console.log(proprs);
</script>
<template>
  <div>
    <h3>子组件Son</h3>
    <div>父组件App传递的值{{ message }}</div>
  </div>
</template>
<style scoped>
</style>

组合式API下的子传父

基本思想

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

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

//父组件
<template>
  <div>
    <h2>父组件App</h2>
    <!-- 1.绑定事件 -->
    <son @getMessage="getMessage"></son>
  </div>
</template>
<script setup>
// setup 语法糖下局部组件无需注册可以直接使用
import son from "./components/HelloWorld.vue";
const getMessage = (msg) => {
  console.log(msg, "父组件");
};
</script>
 
//子组件
<script setup>
// 通过 defineEmits()=> emit(this.$emit)
const emit = defineEmits(["getMessage"]);
//   触发自定义事件传递给父组件
const sonMsg = () => {
  emit("getMessage", "this is a son message");
};
</script>
<template>
  <div>
    <h3>子组件Son</h3>
 
    <button @click="sonMsg">自定义事件</button>
  </div>
</template>
<style scoped>
</style>

父传子

  1. 父传子的过程中通过什么方式接收props?

defineProps( { 属性名:类型 } )

  1. setup语法糖中如何使用父组件传过来的数据?

const props = defineProps( { 属性名:类型 } )

子传父

  1. 子传父的过程中通过什么方式得到emit方法?

defineEmits( [‘事件名称’] )

组合式API - 模版引用

模板引用的概念

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

如何使用(以获取dom为例 组件同理)

<template>
  <div>
    <!-- 通过一个ref标识 指定ref对象 -->
    <h1 ref="refh1">我是dom标签</h1>
    <son ref="sonref" />
  </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import son from "./components/HelloWorld.vue";
//调用 ref 函数 ==》ref对象
const refh1 = ref(null);
const sonref = ref(null);
//组件渲染完毕之后才能获取
onMounted(() => {
  console.log(refh1.value);
  console.log(sonref.value);
});

defineExpose()

默认情况下在

<script setup>
import { ref } from "vue";
const name = ref("test-name");
const setname = () => {
  name.value = "test new name";
};
defineExpose({
  name,
  setname,
});
</script>
<template>
  <div>
    <h3>我是test组件</h3>
  </div>
</template>
<style scoped>
</style>
  1. 获取模板引用的时机是什么?

组件挂载完毕

  1. defineExpose编译宏的作用是什么?

显式暴露组件内部的属性和方法

组合式API - provide和inject

作用和场景

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

跨层传递普通数据

  1. 顶层组件通过provide函数提供数据

  2. 底层组件通过inject函数获取数据

跨层传递响应式数据

在调用provide函数时,第二个参数设置为ref对象

跨层传递方法

顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据
//顶层组件
<template>
  <div>
    <h1>顶层组件</h1>
    <son />
  </div>
</template>
<script setup>
// 组件的关系是
// app--》son== 》componens
import son from "./components/HelloWorld.vue";
import { provide, ref } from "vue";
const count = ref(0);
// 顶层组件提供数据
provide("data-key", "顶层组件数据");
// 传递响应式数据
provide("data", count);
const setCount = () => {
  console.log("1111");
  count.value++;
};
setTimeout(() => {
  count.value = 100;
}, 2000);
// 传递方法(谁的数据谁负责修改)
provide("setCountkey", setCount);
</script>
 
 
//底层组件
<template>
  <div>
    <h3>底层组件{{ appData }}{{ appData2 }}</h3>
    <button @click="setCount">修改顶层组件的数据</button>
  </div>
</template>
<script setup>
import { inject } from "vue";
// 接收数据
const appData = inject("data-key");
// 接收响应式数据
const appData2 = inject("data");
// 接收方法
const setCount = inject("setCountkey");
</script>
 
  1. provide和inject的作用是什么?  跨层组件通信

  2. 如何在传递的过程中保持数据响应式?  第二个参数传递ref对象

  3. 底层组件想要通知顶层组件做修改,如何做? 传递方法,底层组件调用方法

  4. 一颗组件树中只有一个顶层或底层组件吗? 相对概念,存在多个顶层和底层的关系