vue3知识点

1.认识及创建Vue3
https://www.bilibili.com/video/BV18K4y1T7Nc?spm_id_from=333.337.search-card.all.click&vd_source=e499c93499db59599a71c5a3d947fade

了解相关信息

  • Vue.js 3.0 "One Piece"正式版在2020年9月份发布
  • 2年多开发,100+位贡献者,2600+次提交,600+次PR
  • Vue3支持Vue2的大多数特性s
  • 更好的支持Typescript

性能提升

  • 打包大小减少了41%
  • 初次渲染快55%,更新渲染快133%
  • 内存减少54%
  • 使用Proxy代替defineProperty实现数据响应式
  • 重写虚拟DOM的实现和Tree-shaking

新增特性

  • Composition(组合)Api
  • setup
    • ref和reactive
    • computed和watch
    • 新的生命周期函数
    • provide和inject
  • 新组件
    • Fragment-文档碎片
    • Teleport-瞬移组件的位置
    • Suspense-异步加载组件的loading界面
  • 其他API更新
    • 全局API的修改
    • 将原来的全局API转移到应用对象
    • 模板语法变化

使用vue-cli创建
文档:https://cli.vuejs.org/zh/guide/creating-a-project.html

//安装或升级
npm install -g @vue/cli
//保证 vue-cli版本在4.5.0以上
vue --version
//创建项目
vue create my-project

使用create-vue创建项目

  • 前提环境条件
    • 已安装16.0或更高版本的Node.js
  • 创建一个Vue应 (这一指令将会安装并执行create-vue)
npm init vue @latest

2.setup及接收参数
setup
我们可以跟以前一样定义data和methods,但是在vue3中我们更推荐使用setup函数

  • setup是一个函数,只在初始化时执行一次
    • 以后大部分代码都是在setup函数中写
  • 返回一个对象,对象中的属性或方法,模板中可以直接使用
  • setup返回的数据会和data和methods进行合并,setup优先级更高
  • setup函数中没有this
    • 以后开发都不使用this了
  • setup不要写成async函数
    因为setup函数必须返回一个json对象供模板使用,如果setup是一个async函数,返回的将是一个promise对象
    如果setup是一个async函数,那该组件就成了一个异步组件,需要配合Suspense组件才能使用

原始写法(需要return)及语法糖写法(不需要return)

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

//语法糖
<script setup>
const message = "this is message";
const logMessage = () => {
  console.log(message);
};
</script>

setup的参数

  • props-接收父组件传入的通过props声明过的属性
  • context-是一个对象,解构出来包含
    • attrs-接收父组件传入的没有通过props声明过的属性,相当于this.$attrs
    • slots-接收父组件传入的插槽内容的对象,相当于this.$slots
    • emit-用来分发自定义事件的函数,相当于this.$emit
      父组件:
<template>
  <Child msg="hello world" msg2="vue3" @custom-event="handle">
    <template #aaa>我是插槽</template>
  </Child>
</template>


<script lang="ts">
import Child from "./Child.vue";
export default {
  components: {
    Child,
  },
  setup() {
    const handle = (val) => {
      console.log("val", val);
    };
    return {
      handle,
    };
  },
};
</script>

子组件:

<template>
  <div>msg-{{ msg }}</div>
  <div>msg2-{{ msg2 }}</div>
  <slot name="aaa" />
  <button @click="fn">按钮</button>
</template>


<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
  props: ["msg"],
  setup(props, { attrs, slots, emit }) {
    const fn = () => {
      emit("custom-event", 123);
    };
    return {
      msg2: attrs.msg2,
      fn,
    };
  },
});
</script>

3. 组合式API - reactive和ref函数
reactive()
作用:接收对象类型数据的参数传入并返回一个响应式的对象
核心步骤:

<script setup>
import { reactive } from "vue";
const state = reactive({
  count: 0,
});
const setState = () => {
  state.count++;
};
</script>
<template>
  <div>
    <button @click="setState">{{ state.count }}</button>
  </div>
</template>

toRefs

  • 将响应式对象中所有属性包装为ref对象,并返回包含这些ref对象的普通对象
  • 应用:对reactive定义的对象进行toRefs包装,包装之后的对象中每个属性都是响应式的
<template>
  <h1>首页</h1>
  <p>a:{{ a }}</p>
  <p>b:{{ b }}</p>
  <p>str:{{ str }}</p>
  <p>boo:{{ boo }}</p>
  <p>boo:{{ obj }}</p>
  <button @click="update">更新</button>
</template>


<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from "vue";
export default defineComponent({
  name: "HomeView",
  setup() {
    const state = reactive({
      a: 1,
      b: 2,
      str: "abc",
      boo: true,
      obj: { a: 123 },
    });
    const update = () => {
      console.log("update");
      state.a++;
      state.b++;
      state.str += "--";
      state.boo = !state.boo;
      state.obj.a++;
    };
    return {
      ...toRefs(state),
      update,
    };
  },
});
</script>

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

<script setup>
import { ref } from "vue";
const count = ref(0);
const setState = () => {
  count.value++;
};
</script>
<template>
  <div>
    <button @click="setState">{{ count }}</button>
  </div>
</template>
<template>
  <h1>首页</h1>
  <p>a:{{ a }}</p>
  <p>b:{{ b }}</p>
  <p>str:{{ str }}</p>
  <p>boo:{{ boo }}</p>
  <button @click="handle2">按钮</button>
</template>


<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
  name: "HomeView",
  setup() {
    let a = ref(1);
    let b = ref(2);
    let str = ref("abc");
    let boo = ref(true);
    const handle2 = () => {
      a.value++;
      b.value++;
      str.value += "--";
      boo.value = !boo.value;
      console.log(a, b);
    };
    return {
      a,
      b,
      str,
      boo,
      handle2,
    };
  },
});
</script>
  • 作用:定义响应式数据
  • 语法:const xxx = ref(initValue)
    • 创建一个包含响应式数据的引用(reference)对象
    • js中修改数据: xxxx.value = otherValue
    • 模板中显示数据:不需要.value, 直接使用{{xxxx}}
  • 一般用来定义一个原始类型的响应式数据

4. 计算属性computed
回顾vue2中的计算属性

computed: {
    // 只有getter
    fullName() {
      return this.firstName + " " + this.lastName;
    },
    // 有getter和setter
    fullName2: {
      get() {
        return this.firstName + " " + this.lastName;
      },
      set(val) {
        const names = val.split(" ");
        this.firstName = names[0];
        this.lastName = names[1];
      },
    },
  },

vue3的计算属性

  • computed-用法跟vue2类似,不过需要先引入computed
<script setup>
import { ref, computed } from "vue";
const list = ref([1, 2, 3, 4, 5, 6, 7, 8, 9]);
const computedList = computed(() => {
  return list.value.filter((item) => item > 2);
});
</script>
<template>
  <div>原始响应式数组-{{ list }}</div>
  <div>计算属性数组-{{ computedList }}</div>
</template>

5. 侦听属性watch
侦听属性watch
回顾vue2的侦听属性

watch:{
      obj(newVal,oldVal){
        console.log(newVal,oldVal);
      },
      // 立即监听、深度监听
      obj:{
        handler(newVal,oldVal){
          console.log(newVal,oldVal)
        },
        immediate:true, //初始化立即执行一次
        deep:true, //深度监视
      },
      'obj.a'(newVal,oldVal){
        console.log(newVal,oldVal)
      }
    }

vue3的侦听属性:

  • watch-指定监听数据
    • 监视指定的一个或多个响应式数据,一旦数据变化,就自动执行监视回调
      • 如果是监听reactive对象中的属性,必须通过函数来指定
      • 监听多个数据,使用数组来指定
    • 默认初始时不执行回调,但可以通过配置immediate为true,来指定初始时立即执行第一次
    • 通过配置deep为true,来指定深度监视
  • watchEffect-不指定监听数据
    • 不用直接指定要监视的数据,回调函数中使用的哪些响应式数据就监视哪些响应式数据
    • 默认初始时就会执行第一次
      使用时需要先引入watch和watchEffect
//监听一个值
<script setup>
import { ref, watch } from "vue";
const count = ref(0);
const setCount = () => {
  count.value++;
};
watch(count, (newCal, oldVal) => {
  console.log("count变化了", newCal, oldVal);
});
</script>
<template>
  <div>
    <button @click="setCount">{{ count }}</button>
  </div>
</template>
//监听多个值
<script setup>
import { ref, watch } from "vue";
const count = ref(0);
const setCount = () => {
  count.value++;
};

const name = ref("cp");
const setName = () => {
  name.value = "pc";
};

watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
  console.log("count或者name变化了", [newCount, oldCount], [newName, oldName]);
});
</script>
<template>
  <div>
    <button @click="setCount">修改count-{{ count }}</button>
    <button @click="setName">修改name-{{ name }}</button>
  </div>
</template>
//立即执行
<script setup>
import { ref, watch } from "vue";
const count = ref(0);
const setCount = () => {
  count.value++;
};
watch(
  count,
  (newCount, oldCount) => {
    console.log("count变化了", newCount, oldCount);
  },
  {
    immediate: true, //立即执行 (首次自动调用一次)
  }
);
</script>
<template>
  <div>
    <button @click="setCount">{{ count }}</button>
  </div>
</template>

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

//deep监听全部属性对象
<script setup>
import { ref, watch } from "vue";
const state = ref({ count: 0, age: 20 });
const setCount = () => {
  // 对象直接修改属性 ->不会触发回调(必须加deep)
  state.value.count++;
};
watch(
  state,
  () => {
    console.log("变化了");
  },
  {
    deep: true, //深度监视
  }
);
</script>
<template>
  <div>
    <button @click="setCount">{{ state.count }}</button>
  </div>
</template>

精确侦听对象的某个属性
需求:再不开启deep的前提下,侦听age的变化,只有age变化时才执行回调

<script setup>
import { ref, watch } from "vue";
const state = ref({ name: "chaichai", age: 20 });
const setName = () => {
  state.value.name = "new-chaichai";
};
const setAge = () => {
  state.value.age = 22;
};
//语法:两个回调函数的写法(只侦听其中一个属性)
watch(
  () => state.value.age,
  () => {
    console.log("age变化了");
  }
);
</script>
<template>
  <div>
    <div>当前name -- {{ state.name }}</div>
    <div>当前age -- {{ state.age }}</div>
    <button @click="setName">修改name</button>
    <button @click="setAge">修改age</button>
  </div>
</template>

6. 组合式API - 生命周期函数
声明周期
vue2中的生命周期钩子函数依旧可以使用,不过建议使用vue3的钩子函数

  • vue2与vue3声明周期对比
    在这里插入图片描述
    注意:beforeDestroy和destroyed已经被废弃,如果想继续使用vue2的写法,对应的api时onBeforeUnmount(beforeDestroy)和onUnmounted(destroyed)
<template></template>
<script lang="ts">
import {
  defineComponent,
  ref,
  onBeforeMount,
  onMounted,
  onActivated,
  onDeactivated,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
} from "vue";
export default defineComponent({
  name: "HomeView",
  setup() {
    onBeforeMount(() => {
      console.log("onBeforeMount");
    });
    onMounted(() => {
      console.log("onMounted");
    });
    onActivated(() => {
      console.log("onActivated");
    });
    onDeactivated(() => {
      console.log("onDeactivated");
    });
    onBeforeUpdate(() => {
      console.log("onBeforeUpdate");
    });
    onUpdated(() => {
      console.log("onUpdated");
    });
    onBeforeUnmount(() => {
      console.log("onBeforeUnmount");
    });
    onUnmounted(() => {
      console.log("onUnmounted");
    });
    return {};
  },
});
</script>

7. 组合式API - 父子通信
父传子
基本思想
1.父组件中给子组件绑定属性
2.子组件内部通过defineProps接收

//父组件
<script setup>
import { ref } from "vue";
import Son from "./components/son.vue";
const message = ref("father");
</script>
<template>
  <div>
    <h2>父组件</h2>
    <Son :message="message" />
  </div>
</template>
//子组件
<script setup>
const props = defineProps({
  message: String,
});
</script>
<template>
  <div>
    <h2>子组件</h2>
    <div>父组件传过来的数据-{{ props.message }}</div>
  </div>
</template>
<style scoped>
</style>

子传父
基本思想
1.负组件中给子组件标签通过@绑定事件
2.子组件内部通过defineEmits方法触发事件

//父组件
<script setup>
import { ref } from "vue";
import Son from "./components/son.vue";
const getMessage = (msg) => {
  console.log(msg);
};
</script>
<template>
  <div>
    <h2>父组件</h2>
    <Son @get-message="getMessage" />
  </div>
</template>
//子组件
<script setup>
const emit = defineEmits(["get-message"]);
const sendMsg = () => {
  emit("get-message", "123");
};
</script>
<template>
  <div>
    <h2>子组件</h2>
    <button @click="sendMsg">sendMsg</button>
  </div>
</template>
<style scoped>
</style>

8. 组合式API - 模板引用(ref拿节点)
概念:通过ref标识获取真实的dom对象或者组件实例对象

获取拿dom对象

<script setup>
import { onMounted, ref } from "vue";
const h1Ref = ref(null);
onMounted(() => {
  console.log("123", h1Ref.value);
});
</script>
<template>
  <div>
    <h1 ref="h1Ref">我是dom标签h1</h1>
  </div>
</template>

获取组件实例对象
defineExpose()
默认情况下

//父组件
<script setup>
import { onMounted, ref } from "vue";
import Son from "./components/son.vue";
const comRef = ref(null);
onMounted(() => {
  console.log("123", comRef.value);
});
</script>
<template>
  <div>
    <Son ref="comRef" />
  </div>
</template>
//子组件
<script setup>
import { ref } from "vue";
const name = ref("test name");
const setName = () => {
  name.value = "newtest";
};
defineExpose({
  name,
  setName,
});
</script>
<template>
  <div>
    <h2>我是子组件</h2>
  </div>
</template>
<style scoped>
</style>

9. 组合式API - provide和inject

作用和场景:顶层组件想任意的底层组件传递数据和方法,实现跨层组件通信

跨层传递普通数据
1.顶层组件通过provide函数提供数据

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

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

const message = inject('key')
//父组件
<script setup>
import {ref, provide } from "vue";
import RoomMsgItem from "./components/room-msg-item.vue";
const data = ref(0)
provide("data-key", data);
</script>
<template>
  <div>
    顶层组件
    <RoomMsgItem />
  </div>
</template>
//子组件
<script setup>
import { inject } from "vue";
const roomData = inject("data-key");
</script>
<template>
  <div>
    底层组件
    <div>来自顶层组件中的数据为 -- {{ roomData }}</div>
  </div>
</template>

跨层传递方法
顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据

//父组件
<script setup>
import { ref, provide } from "vue";
import RoomMsgItem from "./components/room-msg-item.vue";
const data = ref(0);
const setCount = () => {
  data.value++;
};
provide("data-key", data);
provide("count-key", setCount);
</script>
<template>
  <div>
    顶层组件
    <RoomMsgItem />
  </div>
</template>
//子组件
<script setup>
import { inject } from "vue";
const roomData = inject("data-key");
const countKey = inject("count-key");
</script>
<template>
  <div>
    底层组件
    <div>来自顶层组件中的数据为 -- {{ roomData }}</div>
    <button @click="countKey">来自顶层的方法</button>
  </div>
</template>

10. Pinia 状态管理库
Pinia是Vue的专属的最新状态管理库,是Vuex状态管理工具的替代品
优势:

  • 提供更加简单的API(去掉了mutation)
  • 提供符合组合式风格的API(和Vue3新语法统一)
  • 去掉了modules的概念,每一个store都是一个独立的模块
  • 搭配TypeScript一起使用提供可靠的类型推断

下载依赖

npm install pinia

main.js配置

import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app')

使用Pinia实现计数器案例
在src下新建 stores/counter.js

// counter.js文件
import { defineStore } from "pinia";
import { ref } from 'vue'
export const useCounterStore = defineStore('counter',()=>{
    // 定义数据(state)
    const count = ref(0)
    // 定义修改数据的方法(action 同步+异步)
    const increment = () =>{
        count.value++
    }
    // 以对象的方式return供组件使用
    return{
        count,
        increment
    }
})
// 调用的文件
<script setup>
// 1.导入use打头的方法
import { useCounterStore } from "@/stores/counter.js";
// 2.执行方法得到store实例对象
const counterStore = useCounterStore();
console.log("counterStore", counterStore);
</script>
<template>
  <button @click="counterStore.increment">{{ counterStore.count }}</button>
</template>

getters实现
Pinia中的getters直接使用computed函数进行模拟

// counter.js文件
// 定义数据(state)
const count = ref(0)
// getter定义
const doubleCount = computed(()=>count.value * 2)
// 调用的文件
<script setup>
// 1.导入use打头的方法
import { useCounterStore } from "@/stores/counter.js";
// 2.执行方法得到store实例对象
const counterStore = useCounterStore();
console.log("counterStore", counterStore);
</script>
<template>
  <button @click="counterStore.increment">{{ counterStore.count }}</button>
  {{ counterStore.doubleCount }}
</template>

action如何实现异步
action中实现异步和组件中定义数据和方法的风格完全一致

// counter.js文件
const API_URL = 'http://geek.itheima.net/v1_0/channels'
// 定义数据(state)
const list = ref([])
// 异步action
const getList = async() =>{
   const res = await axios.get(API_URL)
   list.value = res.data.data.channels
}
// 调用的文件
<script setup>
// 1.导入use打头的方法
import { useCounterStore } from "@/stores/counter.js";
import { onMounted } from "vue";
// 2.执行方法得到store实例对象
const counterStore = useCounterStore();
console.log("counterStore", counterStore);
onMounted(() => {
  counterStore.getList();
});
</script>
<template>
  <ul>
    <li v-for="item in counterStore.list" :key="item.id">{{ item.name }}</li>
  </ul>
</template>

storeToRefs
使用storeToRefs函数可以辅助保持数据(state+getter)的响应式解构

//   解构变量
<script setup>
// 1.导入use打头的方法
import { useCounterStore } from "@/stores/counter.js";
import { onMounted } from "vue";
import { storeToRefs } from "pinia";
// 2.执行方法得到store实例对象
const counterStore = useCounterStore();
const { count, doubleCount } = storeToRefs(counterStore);
onMounted(() => {
  counterStore.getList();
});
</script>
<template>
  <button @click="counterStore.increment">{{ count }}</button>
  {{ doubleCount }}
  <ul>
    <li v-for="item in counterStore.list" :key="item.id">{{ item.name }}</li>
  </ul>
</template>
// 解构方法
<script setup>
// 1.导入use打头的方法
import { useCounterStore } from "@/stores/counter.js";
import { onMounted } from "vue";
import { storeToRefs } from "pinia";
// 2.执行方法得到store实例对象
const counterStore = useCounterStore();
const { count, doubleCount, list } = storeToRefs(counterStore);
// 方法直接从原来的counterStore中解构赋值
const { increment } = counterStore;
onMounted(() => {
  counterStore.getList();
});
</script>
<template>
  <button @click="increment">{{ count }}</button>
  {{ doubleCount }}
  <ul>
    <li v-for="item in list" :key="item.id">{{ item.name }}</li>
  </ul>
</template>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值