Vue3

2023-09-14  本文已影响0人  h2coder

Vue3

库名称 简介
ant-design-vue PC 端组件库:Ant Design 的 Vue 实现,开发和服务于企业级后台产品
arco-design-vue PC 端组件库:字节跳动出品的企业级设计系统
element-plus PC 端组件库:基于 Vue 3,面向设计师和开发者的组件库
Naive UI PC 端组件库:一个 Vue 3 组件库,比较完整,主题可调,使用 TypeScript,快,有点意思
vant 移动端组件库:一个轻量、可靠的移动端组件库,于 2017 年开源
VueUse 基于 composition 组合式 api 的常用函数集合

相关文档

Vue3优点和特点

vite 构建工具

vite(法语意为 "快速的",发音 /vit/,发音同 "veet") 是一种新型前端构建工具,能够显著提升前端开发体验

image.png image.png
- 注:项目打包的时候最终还是需要打包成静态资源的,打包工具 Rollup

vite 创建项目

# 使用npm
npm create vite@latest

# 使用yarn
yarn create vite

# 使用pnpm
pnpm create vite

代码提示

vue3 写法不同

<template>
  <div>节点1</div>
  <div>节点2</div>
</template>
import { createApp } from 'vue'
import App from './App.vue'
// 根据App组件创建一个应用实例
const app = createApp(App)
// app应用挂载(管理)index.html的 #app 容器
app.mount('#app')
<div id="app"></div>
<script type="module" src="/src/main.js"></script>

CompositionAPI

组合式API的介绍

Vue3提供两种组织代码逻辑的写法

计数器案例

选项式API

<template>
  计数器:{{ count }} <button @click="increment">累加</button>
</template>
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    increment() {
      this.count++;
    },
  },
};
</script>

组合式API

<template>
  计数器:{{ count }} <button @click="increment">累加</button>
</template>
<script>
// ref 就是一个组合式API  
import { ref } from 'vue';
export default {
  setup () {
    // 计数器
    const count = ref(0)

    const increment = () => {
      count.value ++
    }

    return { show, toggle, count, increment }
  }
};
</script>

总结

setup函数

setup函数是组合式API的入口函数

<script>
export default {
  // Vue3中,setup函数是所有组合式API的入口函数
  // setup函数中的变量和函数,都必须return出来,否则模板中获取不到
  // Vue2中的this为Vue组件实例,而Vue3中的this为undifined
  setup() {
    const msg = "Hello";
    const sayHello = () => {
      alert("你好,点我干啥?");
    };
    return {
      msg,
      sayHello,
    };
  },
};
</script>

<template>
  <h1>{{ msg }}</h1>
  <button @click="sayHello">点我</button>
</template>

<style lang="scss" scoped>
</style>

setup语法糖

每个组件都需要做以上三步,太麻烦,幸好Vue3提供了setup语法糖来帮我们解决这个问题

// HelloWorld.vue
<script setup>
</script>

<template>
  <h1>Hello World</h1>
</template>

<style lang="scss" scoped>
</style>
<!-- 
  setup语法糖,简写
  1、不需要写默认导出
  2、不需要写setup函数
  3、不需要将变量、函数return
  4、组件不需要注册
 -->
 <script setup>
// Vue3中,setup函数是所有组合式API的入口函数
// setup函数中的变量和函数,都必须return出来,否则模板中获取不到
// Vue2中的this为Vue组件实例,而Vue3中的this为undifined

import HelloWorld from "./components/HelloWorld.vue";

const msg = "Hello";
const sayHello = () => {
  alert("你好,点我干啥?");
};
</script>
 
 <template>
  <HelloWorld></HelloWorld>
  <h1>{{ msg }}</h1>
  <button @click="sayHello">点我</button>
</template>
 
 <style lang="scss" scoped>
</style>

reactive函数

通常使用它定义 对象或数组 类型 响应式数据

基本使用

<!-- 
  setup语法糖,简写
  1、不需要写默认导出
  2、不需要写setup函数
  3、不需要将变量、函数return
  4、组件不需要注册
 -->
 <script setup>
// Vue3中,setup函数是所有组合式API的入口函数
// setup函数中的变量和函数,都必须return出来,否则模板中获取不到
// Vue2中的this为Vue组件实例,而Vue3中的this为undifined

// 导入组件
import HelloWorld from "./components/HelloWorld.vue";

// reactive只能声明复杂数据类型,不能声明基础数据类型
import { reactive } from "vue";

// reactive声明对象数据
const person = reactive({
  name: "张三",
  age: 92,
});
const growUp = () => {
  person.age++;
};

// reactive声明数组数据
const list = reactive([1, 2, 3]);

// 注意:reactive不能声明基础数据类型
// 控制台警告:value cannot be made reactive: Hello 你好
const msg = reactive("Hello 你好");
</script>
 
 <template>
  <HelloWorld></HelloWorld>
  <h1>{{ person.name }}</h1>
  <h1>{{ person.age }}</h1>
  <button @click="growUp">点我长大</button>

  <br />

  <h2>{{ list }}</h2>
  <button @click="list.push(4)">push 4</button>

  <br />
  <h3>{{ msg }}</h3>
  <button @click="msg += '~'">+~</button>
</template>
 
 <style lang="scss" scoped>
</style>

总结

ref函数

通常使用它定义响应式数据,不限类型

基本使用

<script setup>
// 导入组件
import HelloWorld from "./components/HelloWorld.vue";

// ref,能声明所有数据类型
import { ref } from "vue";

// ref声明简单数据类型
const msg = ref("Hello");

const fn = () => {
  msg.value += "~";
};

// ref声明复杂数据类型
const person = ref({
  name: "张三",
  age: 92,
});

const fn2 = () => {
  // JS中,修改响应式变量的值,都必须要加.value
  person.value.age++;
};
</script>
 
 <template>
  <HelloWorld></HelloWorld>
  <h1>{{ msg }}</h1>
  <button @click="msg += '~'">+~</button>
  <button @click="fn">++~</button>

  <br />

  <h2>{{ person.name }}</h2>
  <h2>{{ person.age }}</h2>
  <!-- 在template模板中,都不需要加.value -->
  <button @click="person.age++">点我长大</button>
  <button @click="fn2">点我长大 + 1</button>
</template>
 
 <style lang="scss" scoped>
</style>

总结

reactive 与 ref 的选择

疑问:定义响应式数据使用ref还是reactive呢?

<template>
  <div>
    <h1>{{ form.age }}</h1>
    <button @click="form.age++">reactive age++</button>

    <h1>{{ form.name }}</h1>
    <button @click="form.name += '~'">reactive name+~</button>
    <hr>
    <h1>{{ data.age }}</h1>
    <button @click="data.age++">ref age++</button>

    <h1>{{ data.name }}</h1>
    <button @click="data.name += '~'">ref name+~</button>
  </div>
</template>

<script setup>
import { reactive, ref } from 'vue'

let form = reactive({})
// 重新赋值会导致响应式丢失
form = {
  name: 'zs',
  age: 18,
}


let data = ref({})
// 重新赋值不会导致响应式丢失
data.value = {
  name: 'zs',
  age: 18,
}
</script>

<style lang="scss" scoped>
</style>

总结

computed函数

使用 computed 函数定义计算属性

基本使用

<script setup>
// 导入computed函数
import { computed, ref } from "vue";
const score = ref();
const scoreList = ref([50, 80, 100, 90, 70, 60]);

// 需求:定义三个计算属性,
// 不及格 < 60
const failList = computed(() => {
  return scoreList.value.filter((item) => item < 60);
});

// commonList 及格 >=60 且 <90
const commonList = computed(() => {
  return scoreList.value.filter((item) => item >= 60 && item < 90);
});

// 优秀 > 90
const betterList = computed(() => {
  return scoreList.value.filter((item) => item > 90);
});

// 处理点击事件
const handleClick = () => {
  if (score.value === undefined || score.value === "") {
    alert("请输入内容");
    return;
  }
  // 添加输入框的值到数组中
  scoreList.value.push(score.value);
  // 清空输入框
  score.value = "";
};
</script>

<template>
  <div>
    <input
      type="text"
      @keyup.enter="handleClick"
      v-model.number="score"
    /><button @click="handleClick">添加</button>
    <hr />
    <p>不及格: {{ failList }}</p>
    <p>及格: {{ commonList }}</p>
    <p>优秀:{{ betterList }}</p>
  </div>
</template>

watch函数

使用watch函数监听数据的变化

基本使用

<script setup>
// 导入watch函数
import { ref, watch } from "vue";

const count = ref(0);
const user = ref({
  name: "jack",
  info: {
    gender: "男",
    age: 18,
  },
});

// 监听基础数据类型
watch(count, (newValue, oldValue) => {
  console.log(`newValue => ${newValue}`);
  console.log(`oldValue => ${oldValue}`);
});

// 监听复杂数据类型
// watch(
//   user,
//   (newValue, oldValue) => {
//     console.log(`newValue => `, newValue);
//     console.log(`oldValue => `, oldValue);
//   },
//   {
//     // 深度监听
//     deep: true,
//     // 初始化时,就立即执行回调函数
//     immediate: true,
//   }
// );

// 同时监听多个数据的变化
// 注意:如果其中一个数据是复杂数据类型,还是要加上deep深度监听
// watch(
//   [count, user],
//   (newValue) => {
//     console.log(`newValue => `, newValue);
//   },
//   { deep: true }
// );

// 深度监听,需要遍历,如果只想其中一个属性,就会性能浪费,Vue3可以单独精准监听对象某个属性的变化
// 注意:如果精准监听的数据也是复杂数据类型,还是要加上deep深度监听
watch(
  // 监听哪个属性
  () => user.value.info.age,
  // 属性变化时,回调函数
  (newValue) => {
    console.log(`newValue => `, newValue);
  }
);
</script>

<template>
  <div>计数器:{{ count }} <button @click="count++">count++</button></div>
  <div>
    <p>姓名:{{ user.name }}</p>
    <p>性别:{{ user.info.gender }}</p>
    <p>年龄:{{ user.info.age }}</p>
    <button @click="user.name += '~'">对象改名字</button>
    <button @click="user.info.age++">对象改年龄</button>
  </div>
</template>

总结

生命周期函数

vue3的常用生命周期函数

使用步骤

选项式API下的生命周期函数使用 组合式API下的生命周期函数使用
beforeCreate 不需要(直接写到setup函数中)
created 不需要(直接写到setup函数中)
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroyed onBeforeUnmount
destroyed onUnmounted
activated onActivated
deactivated onDeactivated

基本使用

<script setup>
// 导入生命周期函数
import { onBeforeUpdate, onMounted, onUpdated, ref } from "vue";

// 定义响应式变量
const count = ref(0);

// 注意:生命周期,都加上了on开头
// Vue2中,发送请求推荐在created中,而Vue3推荐在onMounted中
onMounted(() => {
  console.log("onMounted,挂载后 -----> ");
  // 挂载后,能获取到DOM元素
  const box = document.querySelector("#app");
  console.log(box);
});

onBeforeUpdate(() => {
  console.log("更新前 -----> ");
});

onUpdated(() => {
  console.log("更新后 -----> ");
});
</script>

<template>
  <div class="box"></div>
  <span>{{ count }}</span> <button @click="count++">+1</button>
  <div>生命周期函数</div>
</template>

总结

使用ref获取DOM元素

元素上使用 ref属性关联响应式数据,获取DOM元素

使用步骤

基本使用

<script setup>
// 导入ref函数
import { ref } from "vue";

// 调用ref,定义响应式变量
const inputRef = ref();

const fn = () => {
  // 通过响应式变量,获取DOM元素实例,就可以调用DOM元素的实例方法
  inputRef.value.focus();
};
</script>

<template>
  <!-- 绑定ref到标签上 -->
  <input type="text" ref="inputRef" />
  <button @click="fn">获取焦点</button>
</template>

父子组件通信

ref操作组件-defineExpose

使用步骤

父传子-defineProps函数

父组件,向子组件传参

使用步骤

子传父-defineEmits函数

子组件,发送自定义事件,传参给父组件

使用步骤

语法

综合案例

子组件

<script setup>
// 通过 defineProps 编译器宏函数,定义子组件的参数

// 方式一:数组定义参数列表
// defineProps(["name", "money", "car"]);

// 方式二:对象定义参数列表和参数类型
const props = defineProps({
  name: String,
  money: Number,
  car: String,
});

// 通过 defineProps 函数的返回值,获取参数值
console.log(`name => ${props.name}`);
console.log(`money => ${props.money}`);
console.log(`car => ${props.car}`);

// 通过 defineEmits 编译器宏函数,定义事件
// 虽然可以不传参数,但推荐传入事件名称,在发送事件时,会有代码提示
// const emit = defineEmits();
const emit = defineEmits(["addMoney", "costMoney"]);
</script>

<template>
  <div>
    <h3>我是子组件</h3>
    <div>
      金钱:{{ money }} <br />
      跑车:{{ car }}
    </div>
    <button @click="emit('addMoney', 10)">吞金兽赚钱啦</button>
    <button @click="emit('costMoney', 50)">吞金兽想花钱</button>
  </div>
</template>

父组件

<script setup>
import { ref } from "vue";
import Child from "./components/Child.vue";

// 声明响应式变量
const money = ref(100);
const car = ref("玛莎拉蒂");

// 事件处理函数
const fn = (item) => {
  money.value -= item;
};
const fn2 = (item) => {
  money.value += item;
};
</script>

<template>
  <div>
    <h1>我是父组件</h1>
    <div>金钱:{{ money }}</div>
    <div>车辆:{{ car }}</div>
    <hr />
    <!-- 通过 v-bind 绑定数据给子组件 -->
    <!-- 父组件,监听子组件发出的自定义事件 -->
    <Child
      :money="money"
      :car="car"
      name="zs"
      @costMoney="fn"
      @addMoney="fn2"
    />
  </div>
</template>

跨组件传参-provide和inject

通过provide和inject函数可以简便的实现跨级组件通讯

案例

祖先组件

<script setup>
// 跨组件通信-依赖注入
// 注意:不是一定只能爷孙关系才能使用provide和inject,而是所有组件都可以

// 导入 provide 函数,用于提供数据
import { ref, provide } from "vue";
import ParentCom from "./components/ParentCom.vue";

// 声明响应式变量
const count = ref(99);
// 声明修改变量的函数
const addFn = () => {
  count.value++;
};

// 提供数据给子孙组件(存数据)
provide("data", count);
provide("addFn", addFn);
</script>

<template>
  <div
    class="app-page"
    style="border: 10px solid #ccc; padding: 50px; width: 600px"
  >
    app 组件 {{ count }}
    <ParentCom />
  </div>
</template>

父级组件

<script setup>
import ChildCom from "./ChildCom.vue";
</script>

<template>
  <div class="parent-page" style="padding: 50px">
    parent 组件
    <hr />
    <ChildCom />
  </div>
</template>

子级组件

<script setup>
// 导入inject函数,通过它获取数据
import { inject } from "vue";

// 获取爷级组件提供的数据(取数据)
const data = inject("data");
// 获取爷组件提供的函数,通过函数修改爷级组件
const addFn = inject("addFn");

console.log(addFn);
</script>

<template>
  <div class="child-page" style="padding: 50px; border: 10px solid #ccc">
    child 组件接收到count: {{ data }} <button @click="addFn">修改count</button>
  </div>
</template>

总结

保持响应式-toRefs函数

在使用reactive,创建的响应式数据,被展开或解构的时候,使用toRefs保持响应式

步骤

<script setup>
import { ref, toRefs } from "vue";

// 定义响应式数据
let user = ref({ name: "tom", age: 18 });

// 默认:解构ref或reactive,会导致响应式丢失
// 虽然视图能显示第一次的数据,但后续修改数据,数据不会驱动视图
// const { name, age } = user.value;

// 作用:把对象中的每一个属性,做一次包装,成为响应式数据
const { name, age } = toRefs(user.value);
</script>

<template>
  <div>
    <p>姓名:{{ name }}</p>
    <p>年龄:{{ age }} <button @click="age++">一年又一年</button></p>
  </div>
</template>

总结

Vue3中v-model语法糖的变化

vue3中v-model语法糖原理

为什么变了?

<com-a v-model="count"></com-a>
<!-- 等价 -->
<com-a :modelValue="count" @update:modelValue="count=$event"></com-a>
<com-a v-model="count" v-model:msg="str"></com-a>
<!-- 等价 -->
<com-a :modelValue="count" @update:modelValue="..." :msg="str" @update:msg="..." />

案例

弹窗子组件

<!-- 弹窗子组件 -->
<script setup>
// 定义子组件的参数
defineProps({
  modelValue: Boolean,
});

// 定义事件
const emit = defineEmits(["update:modelValue"]);
</script>

<template>
  <div class="box" v-show="modelValue">
    <div class="container">
      <!-- 点关闭按钮时,发送事件,通知父组件关闭弹窗 -->
      <div class="header" @click="emit('update:modelValue', false)">x</div>
      <div class="content">
        <span>我是弹窗内容</span>
      </div>
    </div>
  </div>
</template>

<style  scoped>
.box {
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.3);
  position: fixed;
  left: 0;
  top: 0;
}

.container {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 400px;
  height: 300px;
  background-color: #fff;
  border-radius: 6px;
}

.header {
  float: right;
  margin-right: 10px;
  color: #000;
}

.content {
  width: 100%;
  height: 100%;
  color: #000;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>

父组件

<script setup>
// v-model在Vue3中的变化
// 1、在原生HTML标签上,v-model的原理没有变化,v-model会转换为 :value="值" 和 @input="事件处理函数"
// 2、在组件标签上,v-model有变化,v-model会转换为:modelValue 和 @update:modelValue

/*
  问:v-model的原理?
  答:v-model是语法糖,底层会将v-model转化为2个指令, v-bind:value="值" 和 @input="事件处理函数"
*/
import { ref } from "vue";
// 导入弹窗子组件
import MyDialog from './components/MyDialog.vue'

const msg = ref("Hello");

// 是否显示弹窗
const visiable = ref(false)


const handleCloseDialog = (event) => {
  visiable.value = event
}

// 打开弹窗
const open = () => {
  visiable.value = true
}
</script>

<template>
  <!-- 使用v-model,实现双向绑定 -->
  <input type="text" v-model="msg" />

  <hr />

  <!-- 不使用v-model,实现双向绑定 -->
  <input type="text" :value="msg" @input="msg = $event.target.value" />

  <br>
  <br>

  <!-- <button @click="visiable = true">显示弹窗</button> -->
  <button @click="open">显示弹窗</button>

  <!-- 弹窗子组件 -->
  <!-- Vue3中,v-model会转化为,:modelValue 和 @update:modelValue -->
  <!-- <MyDialog v-model="visiable"></MyDialog> -->
  <!-- <MyDialog :model-value="visiable" @update:model-value="visiable = $event"/> -->
  <MyDialog :model-value="visiable" @update:model-value="handleCloseDialog"/>
</template>

总结

上一篇下一篇

猜你喜欢

热点阅读