菜单
本页目录

Vue3官方文档

npm create vite #根据向导选择技术栈 创建vue3项目

组件化

组件系统是一个抽象的概念;

组件:小型、独立、可复用的单元

组合:通过组件之间的组合、包含关系构建出一个完整应用

几乎任意类型的应用界面都可以抽象为一个组件树;

SFC

Vue 的单文件组件 (即 *.vue 文件,英文 Single-File Component,简称 SFC) 是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中.

<script setup>
  //编写脚本
</script>

<template>
  //编写页面模板
</template>

<style scoped>
  //编写样式
</style>

导入其他组件

<script setup>

import Hello from "./components/Hello.vue";
</script>

<template>
<h1>哈哈</h1>
  <hello/>
</template>

<style scoped>

</style>

Vue运行原理

基础使用

插值

使用{{}}即可完成插值

<!-- base.vue -->
<script setup>
let name = "张三"

let car = {
  brand: "小米",
  price: 9999
}
</script>

<template>
 <h2>姓名:{{name}}</h2>
 <h2>品牌:{{car.brand}}</h2>
  <h2>价格:{{car.price}}</h2>
</template>

<style scoped>

</style>

常用指令

v-on指令,可以为元素绑定事件。可以简写为@ v-if ,满足条件才渲染元素 v-for,便利

<!--Base.vue  -->
<script setup>
let name = "张三"

let car = {
  brand: "小米",
  price: 9999
}
function buy(){
  alert("购买成功");
}
let fruits = ["苹果","香蕉"]

</script>

<template>
 <h2>姓名:{{name}}</h2>
 <h2>品牌:{{car.brand}}</h2>
  <h2>价格:{{car.price}}</h2>
<!-- 为点解事件绑定buy方法 -->
  <button v-on:click="buy">购买</button>
  <p></p>
  <span v-if="car.price <= 10000">可以买</span>
  <span v-if="car.price > 10000">太贵了</span>

  <li v-for="(fruit,i) in fruits">{{fruit}}</li>
</template>

<style scoped>

</style>

属性绑定

属性绑定为单向绑定 使用 v-bind:属性='xx'语法,可以为标签的某个属性绑定值;

可以简写为 :属性='xx'

<script setup>
  //
  let url = "http://www.baidu.com"
</script>

<template>
  <a v-bind:href="url">go</a>
  <a :href="url">go</a>
</template>

  <style scoped>

</style>

响应式

ref

数据的动态变化需要反馈到页面; Vue通过ref()和reactive()包装数据,将会生成一个数据的代理对象。vue内部的 基于依赖追踪的响应式系统 就会追踪感知数据变化,并触发页面的重新渲染。

  1. 使用 ref() 包装原始类型、对象类型数据,生成 代理对象
  2. 任何方法、js代码中,使用 代理对象.value 的形式读取和修改值
  3. 页面组件中,直接使用 代理对象 推荐使用 const(常量) 声明代理对象。代表代理对象不可变,但是内部值变化会被追踪。 如下,url又绑定了另外一个引用对象,因此触发此函数页面是不会刷新的,使用常量那么再次绑定会报错,避免出错
<script setup>
let url = ref("https://www.baidu.com");
function changeUrl(){
  console.log(url.value);
  url.value = "www.google.com"
}
</script>

<template>
 <a v-bind:href="url">go {{url}}</a>
  <button @click="changeUrl">改变地址</button>
</template>

  <style scoped>

</style>

深层响应性 嵌套的对象只需使用一个.value即可

import { ref } from 'vue'

const obj = ref({
  nested: { count: 0 },
  arr: ['foo', 'bar']
})

function mutateDeeply() {
  // 以下都会按照期望工作
  obj.value.nested.count++
  obj.value.arr.push('baz')
}

reactive

reactive只能包装对象且不使用.value即可

  1. 使用 reactive() 包装对象类型数据,生成 代理对象
  2. 任何方法、js代码中,使用 代理对象.属性的形式读取和修改值
  3. 页面组件中,直接使用 代理对象.属性
import { reactive } from 'vue'

const state = reactive({ count: 0 })

<button @click="state.count++">
{{ state.count }}
</button>

基本类型用 ref(),对象类型用 reactive(),ref 要用 .value,reactive直接 . 。页面取值永不变。 也可以 ref 一把梭,大不了 天天 .value

推荐安装拓展Vue DevTools

表单绑定

使用v-model进行双向绑定,即页面修改数据也会修改

<!--form.vue  -->
<script setup>
import {reactive} from "vue";

const data = reactive({
  username: "zhangsan",
  agree: true,
  hobby: [],
  gender: "女",
  degree: "",
  course: []
})

</script>

<template>
  <div style="display: flex;">
    <div style="border: 1px solid black;width: 300px">
      <form>
        <h1>表单绑定</h1>
        <p style="background-color: azure">
          <label>姓名(文本框):</label>
          <input v-model="data.username"/>
        </p>
        <p style="background-color: azure"><label>同意协议(checkbox):</label>
          <input type="checkbox" v-model="data.agree"/>
        </p>
        <p style="background-color: azure">
          <label>兴趣(多选框):</label><br/>
          <label><input type="checkbox" value="足球" v-model="data.hobby"/>足球</label>
          <label><input type="checkbox" value="篮球" v-model="data.hobby"/>篮球</label>
          <label><input type="checkbox" value="羽毛球" v-model="data.hobby"/>羽毛球</label>
          <label><input type="checkbox" value="乒乓球" v-model="data.hobby"/>乒乓球</label>
        </p>
        <p style="background-color: azure">
          <label>性别(单选框):</label>
          <label><input type="radio" name="sex" value="男" v-model="data.gender">男</label>
          <label><input type="radio" name="sex" value="女" v-model="data.gender">女</label>
        </p>
        <p style="background-color: azure">
          <label>学历(单选下拉列表):</label>
          <select v-model="data.degree">
            <option disabled value="">选择学历</option>
            <option>小学</option>
            <option>初中</option>
            <option>高中</option>
            <option>大学</option>
          </select>
        </p>
        <p style="background-color: azure">
          <label>课程(多选下拉列表):</label>
          <br/>
          <select multiple v-model="data.course">
            <option disabled value="">选择课程</option>
            <option>语文</option>
            <option>数学</option>
            <option>英语</option>
            <option>道法</option>
          </select>
        </p>
      </form>
    </div>
    <div style="border: 1px solid blue;width: 200px">
      <h1>结果预览</h1>
      <p style="background-color: azure"><label>姓名:{{data.username}}</label></p>
      <p style="background-color: azure"><label>同意协议:{{data.agree}}</label>
      </p>
      <p style="background-color: azure">
        <label>兴趣:
          <li v-for="h in data.hobby">{{h}}</li>
        </label>
      </p>
      <p style="background-color: azure">
        <label>性别:{{data.gender}}</label>
      </p>
      <p style="background-color: azure">
        <label>学历:{{data.degree}}</label>
      </p>
      <p style="background-color: azure">
        <label>课程:
          <li v-for="c in data.course">{{c}}</li>
        </label>
      </p>
    </div>
  </div>
</template>

<style scoped>

</style>

计算属性

计算属性:根据已有数据计算出新数据

<script setup>
import {computed, reactive, toRef, toRefs} from "vue";

//省略基础代码

const totalPrice = computed(()=>{
  return price.value * num.value - coupon.value*100
})

</script>

<template>
  <div class="car">
    <h2>优惠券:{{ car.coupon }} 张</h2>
    <h2>数量:{{ car.num }}</h2>
    <h2>单价:{{ car.price }}</h2>
    <h1>总价:{{totalPrice}}</h1>
    <button @click="getCoupon">获取优惠</button>
    <button @click="addNum">加量</button>
    <button @click="changePrice">加价</button>
  </div>

</template>

<style scoped>

</style>

监听

监听变量的值并可以做出相关操作

watch(num, (newValue,oldValue,onCleanup ) => {
  console.log(oldValue);
  console.log(newValue);
  if(num.value >3){
    alert("超出购买数量")
    num.value = 3;
  }
})

使用watchEffect可以监听全部的响应式数据

watchEffect(()=>{
  if(num.value > 3 ){
    alert("数量不能超过3");
    num.value = 3;
  }
if(car.price >10000){
 alert("太贵了")
}
})

若监听整个响应式对象,则可以将响应式对象直接传入 若监听其某一个属性,则需传入一个get方法 如

watch(()=> user.username,(newValue) => {
  console.log(newValue)
})
watch(()=> user.password,(newValue) => {
  console.log(newValue)
})

生命周期

<!-- Life.vue -->
<script setup>
import {onBeforeMount, onBeforeUpdate, onMounted, onUpdated, ref} from "vue";
const  count = ref(0)
const btn01 = ref()

// 生命周期钩子
onBeforeMount(()=>{
  console.log('挂载之前',count.value,document.getElementById("btn01"))
})
onMounted(()=>{
  console.log('挂载完毕',count.value,document.getElementById("btn01"))
})
onBeforeUpdate(()=>{
  console.log('更新之前',count.value,btn01.value.innerHTML)
})
onUpdated(()=>{
  console.log('更新完毕',count.value,btn01.value.innerHTML)
})
</script>

<template>
  <button ref="btn01" @click="count++"> {{count}} </button>
</template>

<style scoped>
</style>

挂载之前是没有id为btn01的元素的,只有挂载后才有 更新分为数据改变和重新渲染,更新前数据变化了但元素内容没有改变,更新后元素内容才会改变 常见用法是在挂载之前向后端请求数据,之后渲染

组件传值

父传子-Props

父传子是单向数据流,子组件只能读而不能修改父传来的值

<!-- Father.vue -->

<script setup>

import Son from "./Son.vue";
import {reactive, ref} from "vue";
//方式1
//1、 父传子:单向数据流
// const money = ref(100);
// const books = ref(["西游记","水浒传"])
//方式二将传递的值封装为响应式对象
const data = reactive({
  money: 100,
  books: ["西游记","水浒传"]
})
</script>
<template>
  <div style="background-color: #f9f9f9">
    <h2>Father</h2>
    <button @click="data.money+=10">充值</button>
//方式1的传值,即绑定属性
    <!--  <Son :books="data.books" :money="data.money"-->
// 方式二的传值,此时绑定的是data对象
    <Son v-bind="data"></Son>
  </div>
</template>

<style scoped>

</style>
<!--Son.vue  -->
<script setup>
//1、定义属性:只读
//定义props有两种,一种是数组形式
// let props = defineProps(['money','books']);
//另外一种是对象形式,这种形式可以可以设置类型和更多选项
let props = defineProps({
  money: {
    type: Number,
    required: true,
    default: 200
  },
  books: Array
});



function buy(){
  props.money -= 5;
}
</script>

<template>
  <div style="background-color: #646cff;color: white">
    <h3>
        哈哈Son
    </h3>
    <div>账户:{{props.money}}</div>
    <div>图书:
      <li v-for="b in props.books">{{b}}</li>
    </div>
    <button @click="buy">
    </button>
  </div>
</template>

<style scoped>

</style>

在父传子中,在子组件中触发money减5的操作会失败,是只读属性的

//父组件给子组件传递数据:使用属性绑定
<Son :books="data.books" :money="data.money"/>
  
//子组件定义接受父组件的属性
let props = defineProps({
  money: {
    type: Number,
    required: true,
    default: 200
  },
  books: Array
});

子传父-Emit

在 Vue 中,子组件通过 emit 触发事件,父组件通过监听事件 (@事件名) 来接收子组件传递的值,从而实现数据的传递和修改。

<!--Son.vue  -->
<script setup>
let emits = defineEmits(['buy']); // 定义子组件可触发的事件 'buy'
function buy() {
  // 调用 emit 方法触发 'buy' 事件,并传递一个参数 -5
  emits('buy', -5);
}
</script>
<template>
  <button @click="buy">买棒棒糖</button>
</template>
<!--Father.vue  -->
<script>
function moneyMinis(arg){
  // alert("感知到儿子买了棒棒糖"+arg)
  data.money+=arg;
}
</script>
<template>
<!--@buy="moneyMinis": 父组件监听子组件的 buy 事件,当子组件触发 buy 时,会调用父组件的方法 moneyMinis。  -->
<Son :books="data.books" :money="data.money"
           @buy="moneyMinis"/>
</template>

插槽

插槽可以用来传递页面元素 使用具名插槽可以为每个插槽单独插入模板

<!-- Father.vue -->
 <Son v-bind="data">
      <template v-slot:btn>
        买飞机
      </template>
<!-- 使用#代替v-slot -->
      <template #title>
        买大炮
      </template>
    </Son>
<!--Son.vue  -->
<div style="background-color: #646cff;color: white">
    <h3>
      <slot name="title">
        哈哈Son
      </slot>
    </h3>
    <div>账户:{{props.money}}</div>
    <div>图书:
      <li v-for="b in props.books">{{b}}</li>
    </div>
    <button @click="buy">
      <slot name="btn">
<!--插槽默认内容,若父组件不传则为默认内容  -->
        哈哈Son
      </slot>
    </button>
  </div>

对于默认插槽,则只需在子组件标签中传递样式即可,会插入到子组件的全部插槽中

<template>
  <Son>
    <!-- 传递给插槽的内容 -->
    <p>这里是父组件传递的内容。</p>
    <p>可以插入任意 HTML 结构。</p>
  </Son>
</template>

总结