第一篇vue3的基础知识
1.0 创建项目
//需要node版本在16以上 默认是vite pina组成
npm init vue@latest
// 初始化
npm install
// 运行项目
npm run dev
2.0 组合式api
先贴代码
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
关键点在于 script标签加上setup标识
详细参考
我们vue2之前用的都是选项式api,当然vue3中也可以用,但是官网给的意见是简单应用用选项式
大型应用用组合式
。官网详细介绍
3.0响应式数据
3.1普通变量
它不具备响应式
<template>
<div>
// zz
<div>{{name}}</div>
<button @click="changeName">点我改变name</button>
</div>
</template>
<script setup>
let name = "zz"
function changeName(){
name = '123'
console.log(name) // 123
}
</script>
3.2 reactive函数
可以定义具备响应式的变量对象
也就是说 obj array类型的可以具有响应式 普通类型的值就不行了
<template>
<div>
<div>{{obj.name}}</div>
<button @click="changeObj">点我改变obj</button>
<div>{{arr}}</div>
<button @click="changeArr">点我改变arr</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let obj = reactive({
name:'zz',
age:18
})
function changeObj(){
obj.name = '123'
}
let arr = reactive([1,2,3,4])
function changeArr(){
arr.push(5)
}
</script>
如果你感觉 基于对象的渲染很烦,我们就用 toRefs 函数解构一下
// 比如上面的obj
<template>
<p>{{ name }}</p>
<p>{{ age }}</p>
</template>
<script setup>
import { reactive,toRefs } from "vue";
let obj = reactive({
name:'zz',
age:18
})
let {name ,age} = toRefs(obj)
</script>
注意不能用es6的解构赋值 ,如果用了 会导致解构出来的数据失去响应性
3.3 ref函数
<template>
<div>
<h1>响应式数据</h1>
<h2>{{ count }}</h2>
<button @click="changeCount()">点我改变数据</button>
<div>{{obj.name}}</div>
<button @click="changeObj">点我改变obj</button>
<div>{{arr}}</div>
<button @click="changeArr">点我改变arr</button>
</div>
</template>
<script setup>
import { ref } from 'vue' // 引入ref方法
// ref 定义一个值类型的变量
let count = ref(0)
function changeCount(){
count.value ++
}
// ref 定义一个对象或者数组
let obj = ref({
name:'zz',
age:18
})
function changeObj(){
obj.value.name = '123'
}
let arr = ref([1,2,3,4])
function changeArr(){
arr.value.push(5)
}
</script>
总结: 推荐使用ref,利于代码统一管理,但是我感觉对象形式的,reactive,也很舒服呀. 具体使用看个人,看团队吧
4.0 组件支持多根节点的组件
vue2.0 写法
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
vue3.0 写法
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
5.0 计算属性用法
<template>
<p>{{ booksMessage }}</p>
<button @click="booksChange">不看书了</button>
</template>
<script setup>
import {computed, reactive} from "vue";
let author = reactive({
name:'zz',
books:['vue2','vue3','vite'],
})
// 定义计算属性
let booksMessage = computed(()=>{
return author.books.length > 0 ? '你真棒' : '努力奋斗'
})
function booksChange(){
author.books = []
}
</script>
6.0 过滤器
过滤器常用于 文本的格式化
比如 状态 0 表示通过 1表示不通过 2表示审核中
在 3.x 中,过滤器已删除,不再支持。相反地,我们建议用方法调用或计算属性替换它们。
过滤器在vue2.0中的运用
<template>
<ul>
<li v-for="(item,index) in list" :key="item.id">
<div>名字:{{item.name}}</div>
<div>状态:{{ item.state | listStateMsg}}</div>
</li>
</ul>
</template>
<script>
export default {
data(){
return{
list:[
{id:1,name:'zz',state:1},
{id:2,name:'小明',state:0},
{id:3,name:'小红',state:1},
]
}
},
filters:{
listStateMsg(val){
let obj = {
'0':'通过',
'1':'不通过'
}
return obj[val]
}
},
}
</script>
vue3.0 这样用
<template>
<ul>
<li v-for="(item,index) in list" :key="item.id">
<div>名字:{{item.name}}</div>
<div>状态:{{ listStateMsg(item.state)}}</div>
</li>
</ul>
</template>
<script setup>
import {computed, reactive} from "vue";
let list = reactive([
{id:1,name:'zz',state:1},
{id:2,name:'小明',state:0},
{id:3,name:'小红',state:1},
])
function listStateMsg(val){
let obj = {
'0':'通过',
'1':'不通过'
}
return obj[val]
}
</script>
7.0 侦听器
7.1 侦听原始类型的数据
<template>
账号:<input type="text" v-model="account">
</template>
<script setup>
import { ref,watch } from "vue";
let account = ref('zz')
// 侦听原始类型的数据
watch(account,(newData,oldData)=>{
console.log('新数据',newData)
console.log('老数据',oldData)
})
// 创建好侦听器立即执行
watch(account,(newData,oldData)=>{
console.log('新数据',newData)
console.log('老数据',oldData)
},{
immediate:true // 创建好侦听器立即执行
})
</script>
7.2 侦听对象的某一个属性
<template>
薪资:<input type="number" v-model="emp.salary">
</template>
<script setup>
import { watch,reactive } from "vue";
let emp = reactive({
name:'zz',
salary:7000
})
// 侦听对象的某个属性 必须提供该属性的 getter 函数
watch(()=>emp.salary,(newData,oldData)=>{
console.log('新数据',newData)
console.log('老数据',oldData)
})
</script>
7.3侦听一个对象
<template>
账号:<input type="text" v-model="emp.name">
薪资:<input type="number" v-model="emp.salary">
</template>
<script setup>
import { ref,watch,reactive } from "vue";
let emp = reactive({
name:'zz',
salary:7000
})
// 侦听对象
// 这种只会有新的值 老旧值一样
watch(emp,(newData,oldData)=>{
console.log('新数据',newData)
console.log('老数据',oldData)
})
</script>
7.4 侦听多个数据源
<template>
账号:<input type="text" v-model="account">
<hr>
薪资:<input type="number" v-model="emp.salary">
<hr>
部门:<input type="text" v-model="dept.name">
</template>
<script setup>
import { ref,watch,reactive } from "vue";
let account = ref('abc')
let emp = reactive({
name:'zz',
salary:7000
})
let dept = reactive({
id:'107',
name:'销售部'
})
// 侦听多个数据源 组成的数组
watch([account,()=>emp.salary,dept],([newAccount,newSalary,newDept],[oldAccount,oldSalary,oldDept])=>{
console.log('账号新数据',newAccount)
console.log('账号老数据',oldAccount)
console.log('薪资新数据',newSalary)
console.log('薪资老数据',oldSalary)
console.log('部门新数据',newDept) // 同一个值
console.log('部门老数据',oldDept) // 同一个值
})
</script>
7.5 侦听配置项flush
<template>
<h2 id="msg">账号{{ account }} </h2>
<input type="text" v-model="account">
</template>
<script setup>
import { ref,watch,reactive } from "vue";
let account = ref('abc')
// watch(account,(newVal,oldVal)=>{
// console.log(newVal,oldVal)
// // 默认是打印dom更新前的数据
// console.log(document.getElementById('msg').innerHTML)
// })
watch(account,(newVal,oldVal)=>{
console.log(newVal,oldVal)
// 打印dom更新后的数据
console.log(document.getElementById('msg').innerHTML)
},{
flush:'post' // 更改回调的触发机制 dom更新之后
})
</script>
7.6停止侦听器
<template>
<h2 id="msg">账号{{ account }} </h2>
<input type="text" v-model="account">
<button @click="stopAccountWatch">停止侦听器</button>
</template>
<script setup>
import { ref,watch,reactive } from "vue";
let account = ref('abc')
// watch 返回一个函数
let stopAccountWatch = watch(account,(newVal,oldVal)=>{
console.log(newVal,oldVal)
// 默认是打印dom更新前的数据
console.log(document.getElementById('msg').innerHTML)
})
</script>
7.7 watchEffec函数侦听
<template>
<h2 id="msg">账号{{ account }} </h2>
<input type="text" v-model="account">
</template>
<script setup>
import { ref,watchEffect } from "vue";
let account = ref('abc')
// 会立即执行一次
watchEffect(()=>{
console.log(account.value) // account值改变也会触发 因为用到了account
})
</script>
侦听对象的属性
<template>
<h2 id="msg">账号{{ account }} </h2>
<input type="text" v-model="account">
<input type="text" v-model="emp.name">
<input type="text" v-model="emp.salary">
</template>
<script setup>
import { ref,watchEffect,reactive } from "vue";
let account = ref('abc')
let emp = reactive({
name:'zz',
salary:700
})
// 会立即执行一次
watchEffect(()=>{
console.log(account.value) // account值改变也会触发 因为用到了account
console.log(emp.salary) // salary变化也会执行,但是name变化不会执行
})
</script>
7.8 watchPostEffect()函数
默认情况下,侦听器的回调都是在vue组件更新之前被调用,意味着你在侦听器回调中访问的dom将是vue更新之前的
如果想获取vue更新之后的dom,你可以指明参数,
flush:post
,或者直接用watchPostEffect()函数
<template>
<h2 id="msg">账号{{ account }} </h2>
<input type="text" v-model="account">
</template>
<script setup>
import { ref,watchEffect,reactive,onMounted,watchPostEffect } from "vue";
let account = ref('abc')
// 1.0
// watchEffect(()=>{
// console.log(account.value)
// console.log(document.getElementById('msg').innerHTML) // 报错
// })
// 2.0 视图渲染成功后加载
// onMounted(()=>{
// watchEffect(()=>{
// console.log(account.value)
// console.log(document.getElementById('msg').innerHTML) // 每次都改获取的都是上一次的值
// })
// })
// 3.0 配置参数
// watchEffect(()=>{
// console.log(account.value)
// console.log(document.getElementById('msg').innerHTML) // dom更新后的值
// },{
// flush:'post'
// })
// 4.0 watchPostEffect
watchPostEffect(()=>{
console.log(account.value)
console.log(document.getElementById('msg').innerHTML) // dom更新后的值
})
</script>
7.9 停止侦听器
执行 watchEffect 或者 watchPostEffect的回调函数就行
<template>
<h2 id="msg">账号{{ account }} </h2>
<input type="text" v-model="account">
<button @click="stopWatchEffect">停止</button>
<button @click="stopWatchPostEffect">停止</button>
</template>
<script setup>
import { ref,watchEffect,watchPostEffect } from "vue";
let account = ref('abc')
let stopWatchEffect = watchEffect(()=>{
console.log(account.value)
console.log(document.getElementById('msg').innerHTML) // dom更新后的值
},{
flush:'post'
})
let stopWatchPostEffect = watchPostEffect(()=>{
console.log(account.value)
console.log(document.getElementById('msg').innerHTML) // dom更新后的值
})
</script>
8.0 父子组件传值
关键字: defineProps ,定义接收的字段
defineEmits,定义传递的方法
子组件 v-button.vue
<template>
<button :disabled="canUse">{{title}}</button>
<div @click="giveFatherData">点我给父组件传递数据</div>
</template>
<script setup>
// 1.0 接收父组件的参数 数组方式
// defineProps(['title','canUse'])
// 2.0 对象方式
defineProps({
title:{
type: String,
default: '按钮'
},
canUse:{
type: Boolean,
default: false
}
})
// 定义emit事件
let emits = defineEmits(['giveFatherSomeData'])
function giveFatherData(){
emits('giveFatherSomeData',{name:'zz',age:18})
}
</script>
父组件
<template>
<div>
<h2>homeView</h2>
<vButton :title="title" :canUse="canUse" @giveFatherSomeData="getSonData"></vButton>
</div>
</template>
<script setup>
import vButton from './components/v-button.vue'
import { ref } from 'vue' // 引入ref方法
let title = ref('普通按钮')
let canUse = ref(true)
// 接收子组件的数据
function getSonData(data){
console.log('看看子组件传来的数据',data)
}
</script>
9.0 组件透传属性和事件
也就是在组件标签上面写 class style @click 等属性比如下面例子
组件z-button
<template>
<button class="btn">子组件</button>
</template>
<style>
.btn{
border:none;
padding: 10px 26px;
background: #f5a198;
}
</style>
父组件中
<template>
<div class="father">
<vButton
class="fa-btn"
style="border:1px solid red"
title="我是一个按钮"
@click="clickBtn"
></vButton>
</div>
</template>
<script setup>
import vButton from '@/views/components/v-button.vue'
function clickBtn(){
console.log('我打印了')
}
</script>
<style >
.fa-btn{
color:greenyellow;
}
</style>
打开我们的浏览器查看
写在组件上面的东西全部作用到组件上面了,注意: 默认子组件必须只有一个根组件,并且传到的也是根组件
9.1阻止组件自动透传属性和事件
都得用选项式写法
// 在子组件加上这段代码即可
<script>
export default {
inheritAttrs: false, //阻止属性和事件的透传
}
</script>
9.2多跟节点的透传
修改子组件 v-button.vue
// 需要增加 v-bind="$attrs" 指定哪个节点需要透传,不增加则不需要
<template>
<button class="btn" v-bind="$attrs">子组件1</button>
<button class="btn">子组件2</button>
<button class="btn" v-bind="$attrs">子组件3</button>
</template>
<style>
.btn{
border:none;
padding: 10px 26px;
background: #f5a198;
margin:20px;
}
</style>
9.4访问透传的属性和方法
选项式
<template>
<button class="btn" v-bind="$attrs">子组件1</button>
<hr>
<div>{{ $attrs }}</div>
<ul>
<li>{{ $attrs.style }}</li>
<li>{{ $attrs.class }}</li>
<li>{{ $attrs.title }}</li>
</ul>
<button @click="$attrs.onClick()">执行透传的事件</button>
<hr>
<button @click="showAttrs">访问透传的属性和方法</button>
</template>
<script>
export default {
methods:{
showAttrs(){
console.log(this.$attrs) // 直接访问
console.log(this.$attrs.style)
console.log(this.$attrs.class)
console.log(this.$attrs.title)
// 调用方法
this.$attrs.onClick()
}
}
}
</script>
<style>
.btn{
border:none;
padding: 10px 26px;
background: #f5a198;
margin:20px;
}
</style>
组合式
// 当然html里面的 attrs 也可以用 $attrs
<template>
<button class="btn" v-bind="attrs">子组件1</button>
<hr>
<div>{{ attrs }}</div>
<ul>
<li>{{ attrs.style }}</li>
<li>{{ attrs.class }}</li>
<li>{{ attrs.title }}</li>
</ul>
<button @click="attrs.onClick()">执行透传的事件</button>
<hr>
<button @click="showAttrs">访问透传的属性和方法</button>
</template>
<script setup>
import {useAttrs} from "vue";
// 接收透传的属性和方法
let attrs = useAttrs()
function showAttrs(){
console.log(attrs) // 直接访问
console.log(attrs.style)
console.log(attrs.class)
console.log(attrs.title)
// 调用方法
attrs.onClick()
}
</script>
<style>
.btn{
border:none;
padding: 10px 26px;
background: #f5a198;
margin:20px;
}
</style>
10.0 生命周期
vue2 | vue3 | 说明 |
---|---|---|
beforeCreated | - | 页面构建之前 |
created | - | 页面构建完成 |
beforeMounted | onBeforeMounted | 视图渲染之前 |
mounted | onMounted | 视图渲染完成 |
beforeUpdate | onBeforeUpdate | 数据源变化,视图渲染之前 |
update | onUpdate | 数据源变化,视图渲染之后 |
beforeDestroy | (beforeUnmount选项式)onBeforeUnmount | 组件卸载之前 |
destroyed | (unmounted选项式)onUnmounted | 组件卸载之后 |
11.0 style scoped 深度选择器
如果想在 style scoped 中让样式作用于子组件,可以使用 :deep()伪类选择器
// 父组件
<template>
<div class="father">
<vButton></vButton>
</div>
</template>
<script setup>
import vButton from './components/v-button.vue'
</script>
<style scoped>
.father:deep(.son button){
border:1px solid red;
}
</style>
// 子组件
<template>
<div class="son">
<button>子组件</button>
</div>
</template>
11.1 css中的v-bind()
可以接收页面中定义的变量,并且具备响应式
<template>
<button>普通按钮</button>
<div @click="changeColor">点我改变颜色</div>
</template>
<script setup>
import {reactive} from "vue";
let btnTheme = reactive({
bgColor:'#f6574a',
textColor:'#fff',
})
function changeColor(){
btnTheme.bgColor = '#76d1e3';
btnTheme.textColor = 'black'
}
</script>
<style scoped>
button{
background: v-bind('btnTheme.bgColor');
color: v-bind('btnTheme.textColor')
}
</style>
12.0 依赖注入
provide 为后代提供数据
inject 后代组件接收数据
index.vue
<template>
<Son></Son>
<button @click="changeAge">改变age</button>
<div>其它组件里面的{{ age }}</div>
</template>
<script setup>
import Son from './son.vue'
import { provide, ref } from "vue";
// provide 为后代提供数据
let title = 'zz';// 不是响应式的
provide('title',title) // 普通数据
// 注册响应数据
let age = ref(18)
provide('age',age)
function changeAge(){
age.value = 20
}
// 我们也可以把这个方法提供出去
provide('changeAge',changeAge)
</script>
son.vue
<template>
<div>son</div>
<ul>
<li>{{ z_title }}</li>
<li>{{ z_age }}</li>
</ul>
<button @click="z_changeAge()">组件里面也可以修改</button>
</template>
<script setup>
import { inject } from "vue";
let z_title = inject('title')
let z_age = inject('age')
// 接收方法
let z_changeAge = inject('changeAge')
// 接收的数据设置默认值 假设祖先没有 注入这个数据
let z_name = inject('name','小卡')
</script>
13.0 组合式访问组件模板引用(获取dom)
<template>
<input type="text" ref="accountRef" placeholder="账号">
<button @click="changeInput">点我操作账号dom</button>
<!-- ref 函数形式 -->
<input type="password" :ref="passwordRef" placeholder="密码">
<button @click="changePassword">点我操作密码dom</button>
</template>
<script setup>
import {ref} from "vue";
// 变量名一定要和 ref的属性值一样
let accountRef = ref(null)
function changeInput(){
accountRef.value.style = "padding: 15px;border-radius: 8px";
accountRef.value.className = 'love';
accountRef.value.focus()
}
// 密码输入框元素
let passwordElm = ref()
function passwordRef(el){
console.log(el) // 默认我是会执行的
passwordElm.value = el
}
function changePassword(){
passwordElm.value.style = "padding: 15px;border-radius: 8px";
passwordElm.value.className = 'love';
passwordElm.value.focus()
}
</script>
13.1组件模板引用
v-con.vue
<template>
<input type="text" v-model="account" placeholder="账号">
<!-- ref 函数形式 -->
<input type="password" v-model="password" placeholder="密码">
<button @click="login">登录</button>
</template>
<script setup>
import { ref } from "vue";
let account = ref()
let password = ref()
defineExpose({
account,
login
})
function login(){
console.log('登录')
}
</script>
index.vue
<template>
<Vcon ref="conRef"></Vcon>
<button @click="look">点我看子组件实例</button>
</template>
<script setup>
import Vcon from './v-con.vue'
import {onMounted, ref} from "vue";
let conRef = ref(null)
onMounted(()=>{
// 子组件不写 defineExpose 默认是访问不到子组件属性和方法的
console.log(conRef.value.account)
conRef.value.login()
})
function look(){
console.log(conRef.value.account)
// 因为子组件没有暴露该值,则打印 undefined
console.log(conRef.value.password)
conRef.value.login()
}
</script>
参考链接:
千万别倒下,因为你身后一无所有。