Vue3的基础知识

94 阅读3分钟

第一篇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>

打开我们的浏览器查看

v-1.jpg

写在组件上面的东西全部作用到组件上面了,注意: 默认子组件必须只有一个根组件,并且传到的也是根组件

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 生命周期

vue2vue3说明
beforeCreated-页面构建之前
created-页面构建完成
beforeMountedonBeforeMounted视图渲染之前
mountedonMounted视图渲染完成
beforeUpdateonBeforeUpdate数据源变化,视图渲染之前
updateonUpdate数据源变化,视图渲染之后
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>

参考链接:

vue中文网

vue中文文档

vue3.0基本使用

千万别倒下,因为你身后一无所有。

qdysh.png