vue3学习笔记
安装vue3
安装方法:
npm init vue@latest
选择方法:
- 输入名称:vue3-course 就可以。
- 是否添加 TypeScript 选择否就可以。
- 是否添加 JSX 选择否就可以。
- 是否添加 Single Page Application 选择否就可以。
- 是否添加 Pinia for state 选择否就可以。
- 是否添加 for both Unit and 选择否就可以。
- 是否添加 for code 选择是就可以。
- 是否添加 Prettier for 选择是就可以。
安装依赖的方法:
npm install
安装 yarn 方法:
npm install --global yarn
查看是否成功的安装
yarn --version
提示: 如果安装完了以后检查版本的时候出现了错误的话,电脑上的环境变量里面配置yarn的bin文件地址就可以。
在安装依赖的时候的错误处理方法:
提示: 如果是在安装依赖的时候出现的错误的话,这个是你的电脑没有安装 npm install 安装的方法如下:
npm -g
更新安装就可以
写第一个程序
src文件里面的文件夹删除,只剩下app.vue和main.js
app.vue文件里面script和sytle的样式都删掉
在app.vue文件里面的template里面创建一个div里面写一个自定义属性message
<template> <div>{{ message }}</div> </template>
在app.vue文件里面的script里面写一个js代码
import { ref } from 'vue'; const message = ref('这个是一个vue')
全部代码如下:
<script setup> import { ref } from 'vue'; const message = ref('这个是一个name') </script> <template> <div>{{ message }}</div> </template>
下面认识一下main.js文件
认识 import { createApp } from ‘vue’
- createApp 是应用实例
认识 import App from ‘./App.vue’
引入到APP.vue的内容
认识 createApp(App).mount(‘#app’)
createApp 是根组件
.mount(‘#app’) 是加载数据
mout(‘#app’)是html页面的数据类型来获取div的ID地址
实例:
- index.html文件的代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="icon" href="/favicon.ico"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vite App</title> </head> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script> </body> </html>
- app.vue文件的代码
<script setup> import { ref } from 'vue'; const message = ref('我的第一个vue') </script> <template> <div>{{ message }}</div> </template>
- main.js文件的代码
import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')
最后的效果是网站上显示的是 我的第一个vue 。
vue的响应式
ref
- ref 响应式
传送的是数字,浮点数,或者字符串等等。不传送数据。
- 案例:
import { ref } from 'vue'; const message = ref('我的第一个vue');
<template> <div>{{ message }}</div> </template>
最后得到的是 我的第一个vue 。
reactive
- reactive 响应式
传送的是数组。
import { reactive } from 'vue'; const data = reactive({ name:'苏力', age:24, })
<template> <div>{{ data.age }}</div> </template>
最后得到的是 24 。
ref 响应式
传送html标签的方法:
js的写法:
import { ref } from 'vue'; const message = ref('<h1> <i>hallo</i> <p>name</p> </h1>')
vue的写法:
<template> <div v-html="message"></div> </template>
- v-html 是传送的是html标签
最后得到是 hello
**name ** 换行的显示。
绑定属性
js的写法:
import { ref } from 'vue'; const message2 = ref('button')
vue的写法:
<template> <div v-bind:class="message2"></div> </template>
v-bind是绑定属性的,分类好多种下面说两个
- id
- class
最后得到是
加法应用
const number = ref(0)
<div> {{number + 1}} </div>
判断语句
const bool = ref(false)
<div> {{bool ? '真' : '假'}} </div>
函数的使用
const fullname = () => { return '我的名字' }
<div> {{ fullname() }} </div>
事件监听
const click = () => { alert('你点击了按钮') }
<button v-on:click="click">点击我</button>
最后得到的:点击按钮弹出 你点击了按钮 的提示。
修饰符 once
作用: 使用之后只有一次执行。
<button v-on:click.once="click">点击我</button>
响应式
点击事件:
const statu = reactive({ count:0 }) const incturremt = () => { statu.count ++ }
<div> {{statu.count}} </div> <button @click="incturremt">点击我</button>
计算属性:
- 计算属性:computed
import { reactive, computed } from 'vue'; const user = reactive({ name : 'alim', age : 18 }) const isAdultMessage = computed(()=>{ return user.age >=18 ? '成年人' : '未成年人' })
<template> <div> {{user.name}}是一个{{isAdultMessage}} </div> </template>
class与style的绑定
动态绑定class属性
案例:对象的绑定
vue部分
<template> <div> <ul> <!-- 如果是done:task.done的话 , 点击的时候content上画一条线--> <!-- 如果是done:task.done,'text-red': task.done的话 , 点击的时候content上画一条线并且在颜色改为红色--> <li v-bind:class="{done: task.done , 'text-red': !task.done , 'text-gray' : task.done}" v-for="(task,index) in task" :key="index" @click="toggleTask(index)">{{ task.content }}</li> </ul> </div> </template>
- js 部分
<script setup> import { ref } from 'vue'; const task = ref([ {content:'vue3的核心部分',done:false}, {content:'vue2的核心组件部分',done:false}, {content:'vue2的核心部分',done:false}, {content:'vue3的核心组件部分',done:false}, ]) // 点击的时候判断点击的那个然后点击的内容被执行,执行的内容。 const toggleTask = (index) => { task.value[index].done = !task.value[index].done; } </script>
- css 部分
<style scoped> ul{ list-style: none; } ul li{ cursor: pointer; } ul li.done{ text-decoration: line-through; } .text-red{ color: red; } .text-gray{ color: gray; } </style>
- 数组的绑定:
- 绑定方法如下:
<template> <div> <ul> <!-- 如果是done:task.done的话 , 点击的时候content上画一条线--> <!-- 如果是done:task.done,'text-red': task.done的话 , 点击的时候content上画一条线并且在颜色改为红色--> <li v-bind:class="[{task.done ? 'done' : ''}]" v-for="(task,index) in task" :key="index" @click="toggleTask(index)">{{ task.content }}</li> </ul> </div> </template>
我们大部分的时候使用对象模式来使用。
条件渲染
条件渲染
初步是false是关闭h1
<template> <h1 v-if="flag">vue3真的好用</h1> <button @click="toogleFlag">点击我</button> </template>
import {ref} from "vue"; const flag = ref(false); const toogleFlag = () => { // 点击的时候修改flag.value的值 flag.value = !flag.value }
点击之后被修改true打开显示h1
<template> <h1 v-if="flag">vue3真的好用</h1> <button @click="toogleFlag">点击我</button> </template>
import {ref} from "vue"; const flag = ref(false); const toogleFlag = () => { // 点击的时候修改flag.value的值 flag.value = !flag.value }
点击的时候实现切换的效果
<template> <h1 v-if="flag">vue3真的好用</h1> <h1 v-else>我的写法很不错呀</h1> <button @click="toogleFlag">点击我</button> </template>
import {ref} from "vue"; const flag = ref(false); const toogleFlag = () => { // 点击的时候修改flag.value的值 flag.value = !flag.value }
输入的数字来判断切换效果
<template> <h1 v-if="flag==='0'">vue3真的好用</h1> <h1 v-else-if="flag==='1'">vue3的好</h1> <h1 v-else>vue不错呀</h1> <input v-model="flag"/> </template>
import {ref} from "vue"; const flag = ref('0');
textarea的标签来控制
<template> <input v-model="flag"/> <textarea v-if="flag"> <h1>vue3真的好用</h1> <h1>vue3的好</h1> <h1>vue不错呀</h1> </textarea> </template>
import {ref} from "vue"; const flag = ref('0');
列表渲染
列表渲染、
<template> <ul> <li v-for="(item,index) in date" :key="index">{{item}}</li> </ul> </template>
import {ref} from "vue"; const date = ref([ 'task1', 'task2', 'task3', 'task4', ]);
<style> ul{ list-style: none; } </style>
事件处理
事件的绑定一般写 v-on: 来写。也可以在 @ 来写也可以。
- 内联事件处理器
<template> <button v-on:click="count++">加1</button> <p>当前的值为:{{ count }}</p> </template>
import {ref} from "vue"; const count = ref(0);
- 方法事件处理器
<template> <button v-on:click=increaseCount">加1</button> <p>当前的值为:{{ count }}</p> </template>
import {ref} from "vue"; const count = ref(0); const increaseCount = () => { count.value++ }
表单输入绑定
跟踪绑定。意思就是输入文字的是获取同时文字
<template> <input v-model="text" @input="tast"> </template>
import {ref} from "vue"; const text = ref('') const tast = () => { console.log(text.value) }
复选框的监听
<template> <input type="checkbox" v-model="checkd"/> <label for="checkbox">{{ checkd }}</label> </template>
import {ref} form "vue" const checkd = ref(false)
生命周期
构造函数(onMounted(执行的是回调函数))
- 刷新的时候被执行的函数;
import {onMounted} from "vue"; // 回调函数指的是 () => {} onMounted(() =>{ console.log('onMounted被执行啦'); })
最后的打印的是 onMounted被执行啦
- 刷新的时候被执行的函数;
import {onBeforeMount} from "vue"; onBeforeMount(()=> { console.log('onBeforeMount被执行啦'); })
- 内容变化的时候被执行的函数:
<input v-model="text" />
import {onBeforeUpdate,ref} from "vue"; const text = ref('') onBeforeUpdate(()=> { console.log('现在被onBeforeUpdate执行啦'); })
- 内容变化的时候被执行的函数:
<input v-model="text" />
import {onUpdated,ref} from "vue"; const text = ref('') onUpdated(()=> { console.log('现在被onUpdated被执行啦'); })
侦听器
watch是侦听器
// watch(监听的数据,后执行,前执行){}
<template> <input v-model="text"/> </template>
import {ref , watch} from "vue" const text = ref('') watch(text , (newValue,oldValue) => { console.log('newValue: ' , newValue); console.log('oldValue: ' , oldValue); })
最后得到的是 newValue是当前输入的值,oldValue是当前输入值前面的值
watchEffect是每一次渲染的时候请求一次
import {ref, watchEffect} from "vue" const text = ref('') // watchEffect是每一次渲染的时候请求一次使用flush请求类型,post请求方法 watchEffect(()=>{ console.log("watchEffect:",text.value); },{ flush:'post' })
watchPostEffect是每一次渲染的时候请求一次不用使用flush请求类型
import {ref,watchPostEffect} from "vue" const text = ref('') // watchPostEffect是每一次渲染的时候请求一次不用使用flush请求类型,post请求方法 watchPostEffect(() => { console.log("watchPostEffect:", text.value) })
组件基础
创建组件
- src 目录下点击右键选择vue component 输入名就可以,创建的时候注意选择 Composition API。组件名的前字母必须大写字母
组件的使用。
创建的组件名:Couter
<template> <button @click="count++">你点击啦{{count}}</button> </template>
import {ref} from "vue"; const count = ref(0);
引入组件:
<template> <couter></couter> </template>
import Couter from "@/Couter.vue";
全局代码:
Couter.vue文件的代码
<template> <button @click="count++">你点击啦{{count}}</button> </template> <script setup> import {ref} from "vue"; const count = ref(0); </script>
App.vue文件的代码
<template> <couter></couter> </template> <script setup> import Couter from "@/Couter.vue"; </script>
@ 是在 vite.config.js 里面设计全局的 “./src” 等于。
组件的传递内容
传递内容
Couter.vue文件 defineProps 来设置没有设置属性的标签添加属性。
<button @click="count++">{{ message }}{{count}}</button>
import {ref} from "vue"; const count = ref(0); defineProps({ message:{ type:String, default:'你什么时候点击啦' } });
App.vue文件
<template> <couter message="你已经被点击啦"></couter> <couter></couter> <!-- <user-name></user-name>--> </template>
import Couter from "@/Couter.vue"; // import UserName from "#/UserName.vue";
组件注册
介绍: 组件注册是把我们写入的组件文件引入进来的意思。
全局组件注册
注册方法是:使用component方法来注册具体注册方法如下:
main.js 文件
import { createApp } from 'vue' import App from './App.vue' // 在我们使用app.component来输入名称和组件名称后给我们自动引入进来的方法 import UserName from "#/UserName.vue"; const app= createApp(App) // 组件全局注册的方式,并且该方法可以重复使用 app.component('UserName',UserName) .component('',) app.component('UserName',UserName) // app.component('我们使用的标签名', 组件名称) app.mount('#app')
局部组件注册
App.vue文件
- 第一种方法(有setup)
<template> <user-name></user-name> </template> <script setup> // 局部注册的第一中方法: import UserName from "#/UserName.vue"; </script>
- 第二种方法 (没有setup)
<template> <username></username> </template> <script> // 局部注册的第二中方法: import UserName from "#/UserName.vue"; export default { name:'app', components:{ 'username' : UserName // '这里名称必须相似标签名称': 这里的名称必须相似引入进来的组件名 } } </script>
Props
子组件的组件直接传送数据使用的是 Props
使用方法:有setup
- Components.vue
<template> <h1>{{ message }}</h1> </template>
<script setup> // 第一种方法有setup defineProps(['message']) </script>
- App.vue
<template> <components message="这里是使用setup语法糖使用的Props"></components> </template> <script setup> // 引入Components组件 import Components from "@/Components.vue"; </script>
使用方法:没有setup
- Components.vue
<template> <div>{{ message }}</div> </template>
<script> //第二种方法 没有set export default { name:"Components", props:['message'] } </script>
- App.vue
<template> <components message="这里是普通使用的Props"></components> </template>
// 引入Component组件 import Components from "@/Components.vue";
Props对象的形式来使用
NameCad.vue
<template> <div>姓名:{{ name }}</div> <br> <div>年龄:{{ age }}</div> </template> <script setup> defineProps({ name:String, age:Number }) </script>
提示: 使用Props的时候必须声明我们使用的变量属于那个组件。
App.vue
<template> <!-- 使用数字的时候必须前面加个:这个符号,动态的形式绑定--> <name-cad :name="name" :age="age"></name-cad> <!-- 对象的形式来绑定数据--> <name-cad v-bind="{name:'外力',age:25}"></name-cad> </template> <script setup> import NameCad from "@/NameCad.vue"; import {ref} from "vue"; const name = ref('苏力') const age = ref(21) </script>
事件
v-on:click的形式来绑定事件。
绑定方式:
EventComponent.vue
<template> <!-- 事件绑定方式$来表示,事件出发命令'事件名称',传送值 --> <button v-on:click="$emit('btnClick',1)">按我 {{ number }}</button> </template> <script setup> defineProps(['number']) </script>
App.vue
<template> <!-- v-on:事件名称="函数名称"--> <event-component :number="number" v-on:btn-click="submit"></event-component> </template> <script setup> import EventComponent from "@/EventComponent.vue"; import {ref} from "vue"; const number = ref(0); const submit = (num) => { console.log('来自组件的消息' + num); number.value += num; } </script>
减加方法:
EventComponent.vue
<template> <!-- 事件绑定方式$来表示,事件出发命令'事件名称',传送值 --> <button @click="decrease"> - </button> {{ number }} <button @click="increace"> + </button> </template> <script setup> const props = defineProps(['number']) const emit = defineEmits(['increace','decrease']) const increace = () => { if (props.number < 10) { emit('increace') } } const decrease = () => { if (props.number > 0) { emit('decrease') } } </script>
App.vue
<template> <!-- v-on:事件名称="函数名称"--> <event-component :number="number" @decrease="number--" @increace="number++"></event-component> </template> <script setup> import EventComponent from "@/EventComponent.vue"; import {ref} from "vue"; const number = ref(0); </script>
透传 Attributes
- 介绍: Attributes 的主要作用于子组件里面给父组件绑定事件,属性值等功能。
- 使用方法:
AttributesA.vue 添加属性
<template> <!-- 添加属性 --> <div>穿透 Attributes</div> </template> <script setup> </script>
AttributesB.vue 合并属性
<template> <!-- 合并属性--> <div class="my-btn">穿透Attributes</div> </template> <script setup> </script> <style> .my-btn{ font-size: 2em; } </style>
AttributesEvent.vue 绑定事件
<script setup> </script> <template> <!-- 传递事件--> <button>穿透Attributes</button> </template>
App.vue
<template> <attributes-a class="my-div"></attributes-a> <attributes-b class="my-div"></attributes-b> <attributes-event @click="sbmit"></attributes-event> </template> <script setup> import AttributesA from "@/components/attributes/AttributesA.vue"; import AttributesB from "@/components/attributes/AttributesB.vue"; import AttributesEvent from "@/components/attributes/AttributesEvent.vue"; const sbmit = () => { alert('你点击了我') } </script> <style scoped> .my-div{ color: red; } </style>
插槽内容与出口
插槽入口
<slot></slot>
使用方法:
ButtonWithSlot.vue
<template> <button> 这个是一个按钮 <!-- 插槽入口 --> <slot></slot> </button> </template>
App.vue
<template> <button-with-slot> 插槽入口被进入啦 </button-with-slot> </template> <script setup> import ButtonWithSlot from "@/components/slots/ButtonWithSlot.vue"; </script>
渲染作用域
使用的是自定义渲染方法:
ButtonWithSlot.vue
<template> <button> 这个是一个按钮 <!-- 插槽入口 --> <slot></slot> </button> </template>
App.vue
<template> {{ message }} <button-with-slot> {{ message }} </button-with-slot> </template> <script setup> import ButtonWithSlot from "@/components/slots/ButtonWithSlot.vue"; const message = ref('插槽的渲染作用域') </script>
默认内容
ButtonWithDefiltSlot.vue
<template> <button> <!-- slot 里面写的内容是默认内容--> <slot> 默认内容区域 </slot> </button> </template>
App.vue
<template> <!-- 如果是引入进来的组件里面我们写入内容会被替换掉默认内容 --> <button-with-defilt-slot></button-with-defilt-slot> </template> <script setup> import ButtonWithDefiltSlot from "@/components/slots/ButtonWithDefiltSlot.vue"; </script>
具名插槽
使用组件: 我们使用多个插槽的时候被引入到的页面当中输入属性值的位置输入 v-slot: header ,来写插槽的内容,默认起动是默认内容。
BaseLayout.vue
<template> <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot class="footer"></slot> </footer> </div> </template> <style scoped> header { background: black; color: white; } main { background: firebrick; } footer { background: aqua; } </style>
注意事项: 在使用多个插槽的时候 插槽名输入的是:name=“” 里面写就可以,写别的属性会不实现的。
App.vue
<template> <base-layout> <template v-slot:header> 这里是头部区域 </template> <template v-slot:footer> 这里是底部区域 </template> <!-- 引入进来的组件当中直接使用的是默认内容区域 --> 这里是内容区域 </base-layout> </template> <script setup> import BaseLayout from "@/components/slots/BaseLayout.vue"; </script>
扩展知识: v-slot也可以简化掉一个#来做也可以。例如: #header
动态插槽名
BaseLayout.vue
<template> <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> </template> <style scoped> header,main,footer { height: 50px; display: flex; align-items: center; justify-content: center; } header { color: red; border: red 1px solid; } main { color: green; border: green 1px solid; } footer { color: aqua; border: aqua 1px solid; } </style>
App.vue
<template> <base-layout> <template v-slot:[dySlotname]> 这里是底部区域 </template> </base-layout> </template> <script setup> import BaseLayout from "@/components/slots/BaseLayout.vue"; import {ref} from "vue"; // default 是默认区域或者也可以叫内容区域 // footer 是底部区域 // header 是头部区域 const dySlotname = ref('footer') </script>
作用域插槽
使用区域: 子组件的内容在父组件中获取的场景使用。
ScopedSlot.vue
<template> <div> <slot :text="greetingMessage" :count="1"></slot> </div> </template> <script setup> import {ref} from "vue"; const greetingMessage = ref('你好') </script>
App.vue
<template> <scoped-slot v-slot="slotProps"> {{ slotProps.text }} : {{ slotProps.count }} </scoped-slot> </template> <script setup> import ScopedSlot from "@/components/slots/ScopedSlot.vue"; </script>