当前位置: 首页 > article >正文

Vite创建Vue3项目以及Vue3相关基础知识

1.创建Vue3项目

1.运行创建项目命令

# 使用 npm
npm create vite@latest
 

2、填写项目名称 

 3、选择前端框架

 4、选择语法类型

 5、按提示运行代码

不出意外的话,运行之后应该会出现 下边这个页面

 6.延伸学习:对比webpack和vite(这个是面试必考题哦)

1、Webpack:会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。

将所有的模块提前编译、打包进 bundle 中,不管这个模块是否被用到,随着项目越来越大,打包启动的速度自然越来越慢。

使用 webpack 打包模式如下图:

2、Vite:直接启动开发服务器,请求哪个模块再对该模块进行实时编译。

瞬间开启一个服务,并不会先编译所有文件,当浏览器用到某个文件时,Vite 服务会收到请求然后编译后响应到客户端。

使用 Vite 打包模式如下图:

3、vite 优点:

vite 服务器启动速度比 webpack 快;                                                                              

由于vite启动的时候不需要打包,也就无需分析模块依赖、编译,所以启动速度非常快。当浏览器请求需要的模块时,再对模块进行编译,这种按需动态编译的模式,极大缩短了编译时间,当项目越大,文件越多时,vite的开发时优势越明显;
vite热更新比webpack快;                                                                                              

vite在HRM方面,当某个模块内容改变时,让浏览器去重新请求该模块即可,而不是像webpack重新将该模块的所有依赖重新编译;
vite使用esbuild(Go 编写) 预构建依赖,而webpack基于nodejs, 比node快 10-100 倍;

 4、vite 缺点:

生态不及webpack,加载器、插件不够丰富;
没被大规模重度使用,会隐藏一些问题;

 2.初识项目目录以及main.js(这个是最重要的文件欧)

目录

import { createApp } from 'vue'
// 全局样式
import './style.css'
import App from './App.vue'

// 用create()方法把APP组件创建成一个项目挂载到#app的标签上
createApp(App).mount('#app')

看一下下边的index.html文件

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

App.vue第一个组件

// 组件三大要素
// 结构
<template>
  <div class="firstApp">
    hello world
  </div>
</template>
// 交互
<script lang="ts">
// js or ts都是可以的 
  export default{
    name: 'App'
  }
</script>
// 样式
<style scoped>
.firstApp{
  font-size: 30px;
  height: 600;
  text-align: center;
  margin-top: 100px;
}
</style>

3.Vue3核心语法

3.1optionsAPI 与 compositionAPI

我们先看一下Vue3组件的代码

import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const doubleCount = computed(() => count.value * 2);

    function increment() {
      count.value++;
    }

    return {
      count,
      doubleCount,
      increment
    };
  }
};

发现vue3的逻辑都在一块,而vue2,数据,方法都是分开的。

下边是组合式api的优势

  1. 可复用性: Composition API更适合实现代码的复用和逻辑的封装,而Options API的代码复用性相对较差。
  2. 逻辑组织:Composition API将相关逻辑集中在一起,更易于理解和维护,而Options API将相关逻辑分散在不同的选项中,不够清晰。
  3. 代码重用: Composition API可以更灵活地重用逻辑代码,而Options API的重用性较低。
  4. 学习曲线:Composition API相对Options API来说,有一定的学习曲线,但一旦掌握,会发现其强大的灵活性和可复用性。

3.2.setup

setup是vue3中的一个新的配置项,值是一个函数。他是compositionAPI表演的舞台,组件中所用到的:数据/方法/计算属性/监视。。。等,均配置在setup中

特点如下:

  1. setup函数返回对象中的内容,可以直接在模板中使用。
  2. setup中访问this是undefined
  3. setup函数会在beforeCreate之前调用,他是领先所有的钩子进行执行的。


<template>
  <!-- 使用 -->
  <div>{{a}}</div>
  <button @click="changeMsg">点击</button>
</template>
<script lang="ts">
// ts or js  都是可以的
export default{
  name : 'HelloWorld',
  beforeCreate(){
    console.log('beforeCreate');
  },
  setup(){
    console.log('setup',this); // setup 函数中的this是undefined,Vue3已经弱化this了
    
    // 声明
    let msg = '新中国成立103周年' // 此时的数据不是响应式的
    function changeMsg(){
      console.log('我要改变',msg);
      alert(msg)
    }
    // 把变量交出去
    return {a:msg,changeMsg}
  }
}
</script>

<style scoped>

</style>

 setup的返回值  可以是对象,也可以是函数

<template>
  <!-- 使用 -->
  <div>{{a}}</div>
  <button @click="changeMsg">点击</button>
</template>
<script lang="ts">
// ts or js  都是可以的
export default{
  name : 'HelloWorld',
  beforeCreate(){
    console.log('beforeCreate');
  },
  setup(){
    console.log('setup',this);
    
    // 声明
    let msg = '新中国成立103周年' // 此时的数据不是响应式的
    function changeMsg(){
      console.log('我要改变',msg);
      alert(msg)
    }
    // 把变量交出去
    // return {a:msg,changeMsg}

    // 渲染函数写法1
    // return function(){
    //   return '哈哈'
    // }

    // 渲染函数写法2
    return () => '哈哈'
  }
}
</script>

<style scoped>

</style>

 data(){},methods可以与setup共存

<template>
  <!-- 使用 -->
  <div>{{ a }}</div>
  <div>{{ b }}</div>
  <button @click="changeMsg">点击</button>
  <button @click="changeB">点击2</button>
</template>
<script lang="ts">
// ts or js  都是可以的
export default{
  name : 'HelloWorld',
  beforeCreate(){
    console.log('beforeCreate');
  },
  data() {
    return {
      b : 100,
    }
  },
  methods:{
    changeB(){
      this.b +=1
      console.log('cccccc');
      
    }
  },
  setup(){
    console.log('setup',this);
    
    // 声明
    let msg = '新中国成立103周年' // 此时的数据不是响应式的
    function changeMsg(){
      console.log('我要改变',msg);
      alert(msg)
    }
    // 把变量交出去
    return {a:msg,changeMsg}

    // 渲染函数写法1
    // return function(){
    //   return '哈哈'
    // }

    // 渲染函数写法2
    // return () => '哈哈'
  }
}
</script>

<style scoped>

</style>

setup语法糖

<template>
  <!-- 使用 -->
  <div>{{ msg }}</div>
  <button @click="changeMsg">点击</button>
</template>
<script setup lang="ts">
  // 声明
  let msg = '新中国成立103周年' // 此时的数据不是响应式的
  function changeMsg(){
    console.log('我要改变',msg);
    alert(msg)
  }
</script>

<style scoped>

</style>

使用 vite-plugin-vue-setup-extend  (给组件起名字)

第一步:下载依赖vite-plugin-vue-setup-extend

npm i vite-plugin-vue-setup-extend -D

第二步:项目引入然后使用此插件

 

 代码

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 引入插件
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
// 使用插件
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(),VueSetupExtend()],
})

3.3.ref创建:基本类型的响应式数据

<template>
  <!-- 使用 -->
  <div>{{ msg }}</div>
  <button @click="changeMsg">点击</button>
</template>
<script setup lang="ts" name="HelloWorld">
import {ref} from 'vue'
  // 声明
  let msg = ref('新中国成立103周年') // 此时的数据是响应式的,返回的是refImpl的实例对象
  function changeMsg(){
    msg.value = '新中国成立103周年,生日快乐'
    console.log('我要改变',msg.value);
    alert(msg.value)
  }
</script>

<style scoped>

</style>

3.4.reactive创建:对象类型的响应式数据

<template>
  <!-- 使用 -->
  <div>{{ msg.content }}</div>
  <button @click="changeMsg">点击</button>
  <ul>
    <!-- :key其实相当于把后边的内容当成表达式去解析 -->
    <li v-for="item in arrList" :key="item.id">{{ item.title }}</li>
  </ul>
</template>
<script setup lang="ts" name="HelloWorld">
import {reactive} from 'vue'
  // 声明
  let msg = reactive({content:'新中国成立103周年'}) // 此时的数据是响应式的,返回的是Proxy的实例对象
  let arrList = reactive([
    {id:'1',title:'中国银行'},
    {id:'2',title:'工商银行'},
    {id:'3',title:'建设银行'}
  ])
  function changeMsg(){
    msg.content = '新中国成立103周年,生日快乐'
    arrList[1].title = '我的银行'
    console.log('我要改变',msg,msg.content);
    alert(msg.content)
  }
</script>

<style scoped>

</style>

</style>

3.5.ref创建:对象类型的响应式数据

<template>
  <!-- 使用 -->
  <div>{{ msg.content }}</div>
  <button @click="changeMsg">点击</button>
  <ul>
    <!-- :key其实相当于把后边的内容当成表达式去解析 -->
    <li v-for="item in arrList" :key="item.id">{{ item.title }}</li>
  </ul>
</template>
<script setup lang="ts" name="HelloWorld">
import {ref} from 'vue'
  // 声明
  let msg = ref({content:'新中国成立103周年'}) // 此时的数据是响应式的,返回的是Proxy的实例对象
  let arrList = ref([
    {id:'1',title:'中国银行'},
    {id:'2',title:'工商银行'},
    {id:'3',title:'建设银行'}
  ])
  function changeMsg(){
    msg.value.content = '新中国成立103周年,生日快乐'
    arrList.value[1].title = '我的银行'
    // console.log('我要改变',msg,msg.content);
    // alert(msg.value.content)
  }
</script>
<style scoped>

</style>

这个说明ref可以定义响应式对象,底层也是在使用Reactive,也就是proxy生成的.

3.6.ref与reactive

宏观角度

1.ref定义:基本数据类型,对象类型数据

2.reactive定义:对象类型数据。

区别:

 1.ref创建的变量必须使用.value(也可以使用volar插件自动添加.value)

现在是Vue-official插件,设置在下图。

 

 使用原则

1.若需要一个基本类型的响应式数据,必须使用ref.

2.若需要一个响应式对象,层级不深,ref,reactive都可以。

3.若需要一个响应式对象,层级比较深,推荐使用reactive。

注意点: 当reactive定义的复杂数据类型时,一个属性是ref类型时,就不用value取值了,会自动拆包了。

3.7.torefs与toref

toRefs把对象中多个属性转变成新的ref响应式对象

toRefs把对象中单个属性转变成新的ref响应式对象

<template>
 <div>名:{{name}}</div>
 <div>年龄:{{age}}</div>
 <div>toRef新名字{{ newName }}</div>
 <button @click="changeName">修改名字</button>
 <button @click="changeAge">修改名字</button>
</template>
<script setup lang="ts" name="HelloWorld">

  import {reactive,toRefs,toRef} from 'vue'
  let person = reactive({
    name:'zhangsan',
    age:18
  })
  let {name,age} = toRefs(person)  // 把person里边的属性给领出来重新给搞成响应式,我们现在有新的响应式数据name,age,在下边的方法中我们可以发现只要改name,person.name也会跟着改

  let newName = toRef(person,'name')
  function changeName(){
    name.value += '~'
  }
  function changeAge(){
    age.value += 10
  }
</script>
<style scoped>

</style>

3.8.computed

页面使用6次,但是只调用一次。这就是使用它的优势。

数据发生改变才进行调用。

<template>
  姓:<input type="text" v-model="firstName">
  <br/>
  名:<input type="text" v-model="secondName">
  <br/>
  姓名:<span>{{fullName}}</span>
  <br/>
  姓名:<span>{{fullName}}</span>
  <br/>
  姓名:<span>{{fullName}}</span>
  <br/>
  姓名:<span>{{fullName}}</span>
  <br/>
  姓名:<span>{{fullName}}</span>
  <br/>
  姓名:<span>{{fullName}}</span>
  <br/>
</template>
<script setup lang="ts" name="HelloWorld">
import {ref,computed} from 'vue'
let firstName = ref('zhang')
let secondName = ref('san')

let fullName = computed(() => {
  console.log('使用');
  
  return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + secondName.value
})
</script>
<style scoped>

</style>

3.9.watch

        只能监听以下四种

  1. ref定义的数据。
  2. reactive定义的数据。
  3. 函数返回一个值(getter函数)。
  4. 一个包含以上内容的数组。

3.9.1 ref定义的基本数据类型

<template>
  <div>ref定义基本类型的数字:{{ num }}</div>
 <div>名:{{person.name}}</div>
 <div>年龄:{{person.age}}</div>
 <button @click="changeName">修改名字</button>
 <button @click="changeAge">修改名字</button>
 <button @click="changeRef">修改名字</button>
</template>
<script setup lang="ts" name="HelloWorld">

  import {reactive,watch,ref} from 'vue'
  let person = reactive({
    name:'zhangsan',
    age:18
  })
  let num = ref(0)
  // let numObj = ref({
  //   num:10
  // })
  function changeName(){
    person.name += '~'
  }
  function changeAge(){
    person.age += 10
  }
  function changeRef(){
    num.value += 1
  }
  const stopWatch = watch(num,(val,oldVal) => {
    console.log('获取新的值是',val,oldVal);
    if(val >=3 ){
      stopWatch()
    }
  })
  console.log('1991',stopWatch);
  
</script>
<style scoped>

</style>

3.9.2 ref定义的复杂数据类型 

<template>
  <div>ref定义复杂类型的数字:{{ numObj.num }}</div>
  <div>ref定义复杂类型的数字:{{ numObj.name }}</div>
 <button @click="changeRefname">修改名字</button>
 <button @click="changeRefnum">修改数据</button>
 <button @click="changeRefObj">修改整个</button>
</template>
<script setup lang="ts" name="HelloWorld">

  import {watch,ref} from 'vue'
  let numObj = ref({
    num:0,
    name:'123'
  })
  function changeRefname(){
    numObj.value.name += '~~~~'
  }
  function changeRefnum(){
    numObj.value.num += 2
  }
  function changeRefObj(){
    numObj.value = {num:1,name:'456'}
  }
  // 关心整个值,如果想要监听某一个属性,deep:true  另外immediate:true 指的是一上来就开始监听
  watch(numObj,(val,oldVal) => {
    console.log('获取新的值是',val,oldVal); // 除非整个对象被改不然新旧值是一样的,因为地址没有改变
  },{deep:true,immediate:true})
  
</script>
<style scoped>

</style>

 3.9.3 reactive定义的复杂数据类型 

<template>
  <div>reactive定义复杂类型的数字:{{ numObj.num }}</div>
  <div>reactive定义复杂类型的数字:{{ numObj.name }}</div>
  <div>车牌子:{{numObj.car.brand}} id:{{ numObj.car.id }}</div>
<button  @click="changeRefbrand">修改牌子</button>
 <button @click="changeRefname">修改名字</button>
 <button @click="changeRefnum">修改数据</button>
 <button @click="changeRefObj">修改整个车</button>
</template>
<script setup lang="ts" name="HelloWorld">

  import {watch,reactive} from 'vue'
  let numObj = reactive({
    num:0,
    name:'123',
    car:{
      id:1,
      brand:'奥迪'
    }
  })
  function changeRefname(){
    numObj.name += '~~~~'
  }
  function changeRefnum(){
    numObj.num += 2
  }
  function changeRefbrand(){
    numObj.car.brand  = '长安'
  }
  function changeRefObj(){
    // reactive定义的对象不可以整体修改
    numObj.car = {brand:'长安',id:456}
    // Object.assign(numObj.car,{brand:'456',id:100})
  }
  // 监听某一个属性用getter函数(拥有返回值的函数)
  // watch(numObj.car ,(val,oldVal) => {
  //   console.log('获取新的值是',val,oldVal); 
  // })

  watch(() => numObj.car ,(val,oldVal) => {
    console.log('获取新的值是',val,oldVal); 
  },{deep:true})
  
</script>
<style scoped>

</style>

// reactive定义的对象不可以整体修改 ,可以通过Object.assign()这个是重点欧

// reactive自动深度监听,而且不能关闭

<template>
  <div>reactive定义复杂类型的数字:{{ numObj.num }}</div>
  <div>reactive定义复杂类型的数字:{{ numObj.name }}</div>
 <button @click="changeRefname">修改名字</button>
 <button @click="changeRefnum">修改数据</button>
 <button @click="changeRefObj">修改整个</button>
</template>
<script setup lang="ts" name="HelloWorld">

  import {watch,reactive} from 'vue'
  let numObj = reactive({
    num:0,
    name:'123'
  })
  function changeRefname(){
    numObj.name += '~~~~'
  }
  function changeRefnum(){
    numObj.num += 2
  }
  function changeRefObj(){
    // reactive定义的对象不可以整体修改
    // numObj = {num:1,name:'456'}
    Object.assign(numObj,{name:'456',num:1}) // 没有改变地址
  }
  // reactive定义的数据直接深度监听
  watch(numObj,(val,oldVal) => {
    console.log('获取新的值是',val,oldVal); 
  })
  
</script>
<style scoped>

</style>

 3.9.4 监听复杂数据中的某一个属性,为基本数据类型

<template>
  <div>reactive定义复杂类型的数字:{{ numObj.num }}</div>
  <div>reactive定义复杂类型的数字:{{ numObj.name }}</div>
 <button @click="changeRefname">修改名字</button>
 <button @click="changeRefnum">修改数据</button>
 <button @click="changeRefObj">修改整个</button>
</template>
<script setup lang="ts" name="HelloWorld">

  import {watch,reactive} from 'vue'
  let numObj = reactive({
    num:0,
    name:'123'
  })
  function changeRefname(){
    numObj.name += '~~~~'
  }
  function changeRefnum(){
    numObj.num += 2
  }
  function changeRefObj(){
    // reactive定义的对象不可以整体修改
    // numObj = {num:1,name:'456'}
    Object.assign(numObj,{name:'456',num:1})
  }
  // 监听某一个属性用getter函数(拥有返回值的函数)
  watch(() => numObj.name ,(val,oldVal) => {
    console.log('获取新的值是',val,oldVal); 
  })
  
</script>

3.9.5 监听复杂数据中的某一个属性,为复杂数据类型

<template>
  <div>reactive定义复杂类型的数字:{{ numObj.num }}</div>
  <div>reactive定义复杂类型的数字:{{ numObj.name }}</div>
  <div>车牌子:{{numObj.car.brand}} id:{{ numObj.car.id }}</div>
<button  @click="changeRefbrand">修改牌子</button>
 <button @click="changeRefname">修改名字</button>
 <button @click="changeRefnum">修改数据</button>
 <button @click="changeRefObj">修改整个车</button>
</template>
<script setup lang="ts" name="HelloWorld">

  import {watch,reactive} from 'vue'
  let numObj = reactive({
    num:0,
    name:'123',
    car:{
      id:1,
      brand:'奥迪'
    }
  })
  function changeRefname(){
    numObj.name += '~~~~'
  }
  function changeRefnum(){
    numObj.num += 2
  }
  function changeRefbrand(){
    numObj.car.brand  = '长安'
  }
  function changeRefObj(){
    // reactive定义的对象不可以整体修改
    numObj.car = {brand:'长安',id:456}
    // Object.assign(numObj.car,{brand:'456',id:100})
  }
  // 监听某一个属性用getter函数(拥有返回值的函数)
  // watch(numObj.car ,(val,oldVal) => {
  //   console.log('获取新的值是',val,oldVal); 
  // })

  watch(() => numObj.car ,(val,oldVal) => {
    console.log('获取新的值是',val,oldVal); 
  },{deep:true})
  
</script>
<style scoped>

</style>

3.9.6 监听一个数组 

<template>
  <div>reactive定义复杂类型的数字:{{ numObj.num }}</div>
  <div>reactive定义复杂类型的数字:{{ numObj.name }}</div>
  <div>车牌子:{{numObj.car.brand}} id:{{ numObj.car.id }}</div>
<button  @click="changeRefbrand">修改牌子</button>
 <button @click="changeRefname">修改名字</button>
 <button @click="changeRefnum">修改数据</button>
 <button @click="changeRefObj">修改整个车</button>
</template>
<script setup lang="ts" name="HelloWorld">

  import {watch,reactive} from 'vue'
  let numObj = reactive({
    num:0,
    name:'123',
    car:{
      id:1,
      brand:'奥迪'
    }
  })
  function changeRefname(){
    numObj.name += '~~~~'
  }
  function changeRefnum(){
    numObj.num += 2
  }
  function changeRefbrand(){
    numObj.car.brand  = '长安'
  }
  function changeRefObj(){
    // reactive定义的对象不可以整体修改
    numObj.car = {brand:'长安',id:456}
    // Object.assign(numObj.car,{brand:'456',id:100})
  }
  // 监听某一个属性用getter函数(拥有返回值的函数)
  // watch(numObj.car ,(val,oldVal) => {
  //   console.log('获取新的值是',val,oldVal); 
  // })

  watch([() => numObj.car,() => numObj.name] ,(val,oldVal) => {
    console.log('获取新的值是',val,oldVal); 
  },{deep:true})
  
</script>
<style scoped>

</style>

3.10.watchEffect

  1. watch和watchEffect都能监听,只不过监听数据变化方式不一样
  2. watch需要明确监听的数据
  3. watchEffect:不用明确指出监视的数据(函数中用到哪些,就监听哪些属性)
<template>
  <div>reactive定义复杂类型的数字:{{ numObj.num }}</div>
  <div>reactive定义复杂类型的数字:{{ numObj.name }}</div>
  <div>车牌子:{{numObj.car.brand}} id:{{ numObj.car.id }}</div>
<button  @click="changeRefbrand">修改牌子</button>
 <button @click="changeRefname">修改名字</button>
 <button @click="changeRefnum">修改数据</button>
 <button @click="changeRefObj">修改整个车</button>
 <button @click="changeAge1">修改Age1</button>
 <button @click="changeAge2">修改Age2</button>
 <div>age1:{{ age1 }}</div>
 <div>age2:{{ age2 }}</div>
</template>
<script setup lang="ts" name="HelloWorld">

  import {watch,reactive,watchEffect,ref} from 'vue'
  let age1 = ref(2)
  let age2 = ref(10)
  function changeAge1(){
    age1.value += 10
  }
  function changeAge2(){
    age2.value += 20
  }
  let numObj = reactive({
    num:0,
    name:'123',
    car:{
      id:1,
      brand:'奥迪'
    }
  })
  function changeRefname(){
    numObj.name += '~~~~'
  }
  function changeRefnum(){
    numObj.num += 2
  }
  function changeRefbrand(){
    numObj.car.brand  = '长安'
  }
  function changeRefObj(){
    // reactive定义的对象不可以整体修改
    numObj.car = {brand:'长安',id:456}
    // Object.assign(numObj.car,{brand:'456',id:100})
  }
  // 监听某一个属性用getter函数(拥有返回值的函数)
  // watch(numObj.car ,(val,oldVal) => {
  //   console.log('获取新的值是',val,oldVal); 
  // })

  watchEffect(() => {
    console.log('获取新的值是',age1.value,age2.value); 
  })
  
</script>
<style scoped>

</style>

3.11.标签ref属性

作用:用于注册模板引用。

  1. 用于普通Dom标签上,获取的是Dom节点。
  2. 用于组件标签上,获取的是组件实例对象。

子组件

<template>
  <div>我是ref</div>
  <h2 ref="titles">北京</h2>
  <button @click="getDom">点击获取Dom</button>
</template>
<script setup lang="ts" name="HelloWorld">
  import {ref,defineExpose} from 'vue'
  let aaa = ref(100)
  let bbb = ref(200)
  let titles = ref()
  function getDom(){
    console.log('获取Dom',titles.value);

  }
  defineExpose({aaa,bbb})
</script>
<style scoped>

</style>

父组件

// 组件三大要素
// 结构
<template>
  <h2>asdasd</h2>
  <button @click="getDom">获取ref</button>
  <HelloWorld ref="hellos"></HelloWorld>
</template>
// 交互
<script setup lang="ts" name="App">
import HelloWorld from './components/HelloWorld.vue'
import {ref} from 'vue'
let hellos = ref()
function getDom(){
  console.log('父亲获取子组件',hellos.value);
  
}
</script>
// 样式
<style scoped>
.firstApp{
  font-size: 30px;
  height: 600;
  text-align: center;
  margin-top: 100px;
}
</style>

3.12.props

父传子,通过属性传输,子通过defineProps接收

父组件

// 组件三大要素
// 结构
<template>

  <HelloWorld ref="hellos" a="haha" b="heihei" :list="personList"></HelloWorld>
</template>
// 交互
<script setup lang="ts" name="App">
import HelloWorld from './components/HelloWorld.vue'
import { reactive } from 'vue';
import {type Persons} from './types/index'
// reactive直接用泛型形式写比较优雅 reactive<Person>,如果可有可无的属性y需要在接口中用?:去控制
let personList = reactive<Persons>([
  {id:1,name:'张三',age:18},
  {id:2,name:'张四',age:19},
  {id:3,name:'张五',age:20},
])
</script>
// 样式
<style scoped>
.firstApp{
  font-size: 30px;
  height: 600;
  text-align: center;
  margin-top: 100px;
}
</style>

 子组件

<template>
<!-- <div>子组件{{ a }}</div> -->
<ul>
  <li v-for = 'item in list' :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script setup lang="ts" name="HelloWorld">
// defineProps 是宏函数  不用非得引入
import { defineProps,withDefaults } from 'vue';
import { type Persons } from '../types';
// 只接受
// defineProps(['list'])

// 接受list并限制类型
// defineProps<{list:Persons}>()

// 接受list 并限制类型 + 限制必要性 + 指定默认值   ?指的是是否可传


withDefaults(defineProps<{list?:Persons}>(),{
  list:()=> [{id:5,name:'wang',age:40}]
})

// 接受并保存
// let x = defineProps(['a','b','list'])
// console.log(x);

</script>
<style scoped>

</style>

types

// 定义接口 限制person的具体属性
export interface PersonInter  {
    name:string,
    age:number,
    id:number,
    // y?:number
}

// export type Persons = Array<PersonInter>
export type Persons = PersonInter[]

3.13.生命周期

组件的生命周期:创建,挂载,更新,销毁

Vue2   

beforeCreate created

beforeMounte mounted

beforeUpdate updated

beforeDestroy destroyed    v-show 与 v-if 在这里有本质的区别

Vue3

setup  创建阶段

onBeforeMount(() => {})

onMounted

onBeforeUpdate

onUpdated

onBeforeUnmount

onUnmounted

挂载顺序是先父后子再是子然后是父,父子子父的顺序

<template>
<!-- <div>子组件{{ a }}</div> -->
<ul>
  <li v-for = 'item in list' :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script setup lang="ts" name="HelloWorld">
// defineProps 是宏函数  不用非得引入
import { defineProps,withDefaults,onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';
import { type Persons } from '../types';
// 只接受
// defineProps(['list'])

// 接受list并限制类型
// defineProps<{list:Persons}>()

// 接受list 并限制类型 + 限制必要性 + 指定默认值   ?指的是是否可传


withDefaults(defineProps<{list?:Persons}>(),{
  list:()=> [{id:5,name:'wang',age:40}]
})

onBeforeMount(() => {
  console.log('挂载前');
  
})

onMounted(() => {
  console.log('挂载后');
})

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

onUpdated(() =>{
  console.log('更新后');
})

onBeforeUnmount(() => {
  console.log('卸载前');
  
})

onUnmounted(()=>{
  console.log('卸载后');
})
// 接受并保存
// let x = defineProps(['a','b','list'])
// console.log(x);

</script>
<style scoped>

</style>

3.14.自定义hooks

useDog.ts

// defineProps 是宏函数  不用非得引入
import { reactive } from 'vue';
import axios from 'axios';
export default function(){
    
let dogList = reactive<string[]>([
    'https://images.dog.ceo/breeds/pembroke/n02113023_3337.jpg'
   ])
   // function getDog(){
   //   // axios.get('https://images.dog.ceo/breeds/pembroke/n02113023_3337.jpg').then(
   //   //   response => {
   //   //     console.log(response);
         
   //   //   },
   //   //   error => {
   //   //     console.log(error);
   //   //   }
   //   // )
   
   async function getDog(){
     try {  
       let response = await axios.get('https://dog.ceo/api/breed/pembroke/images/random');  
       // 假设响应体有一个属性(比如 'message')包含图片 URL  
       // 注意:这里的属性名应该根据您的实际 API 响应来调整  
       let dogUrl = response.data.message;   
       console.log('123',dogUrl);
       
       dogList.push(dogUrl);  
     } catch (error) {  
       console.error('Error fetching dog image URL:', error);  
     }  
   }

   return {getDog,dogList}
}



 useSum

// defineProps 是宏函数  不用非得引入
import { ref,onMounted,computed } from 'vue';

export default function(){
    let sum = ref(0)
    let bigSum = computed(() => {
        return sum.value*10
    }) 


    function add(){
     sum.value += 1
    }

    onMounted(()=>{
        console.log('加载完成');
        
    })

    return {sum,add,bigSum}
}


// 图片接口  https://dog.ceo/api/breed/pembroke/images/random





3.15. 回顾Ts

接口:

// 定义接口 限制person的具体属性
export interface PersonInter  {
    name:string,
    age:number,
    id:number
}

// export type Persons = Array<PersonInter>
export type Persons = PersonInter[]

页面使用接口:

<template>
<div>Ts</div>
<div>{{ person.name }}</div>
<button @click="changeName">改名字</button>
</template>
<script setup lang="ts" name="HelloWorld">
import { reactive } from 'vue';
// import { type PersonInter,type Persons } from '../types';  // 下边写法更好
import type { PersonInter,Persons } from '../types';

let person : PersonInter = reactive({id:1,name:'wang',age:60}) // ts
// 第一种写法
// let personList:Array<PersonInter> = reactive([
//   {id:1,name:'wang',age:60},
//   {id:2,name:'wang1',age:60},
//   {id:3,name:'wang2',age:60},
// ])
// 第二种写法
// let personList:PersonInter[] = reactive([
//   {id:1,name:'wang',age:60},
//   {id:2,name:'wang1',age:60},
//   {id:3,name:'wang2',age:60},
// ])
// 第三种写法
let personList:Persons = reactive([
  {id:1,name:'wang',age:60},
  {id:2,name:'wang1',age:60},
  {id:3,name:'wang2',age:60},
])
function changeName(){
  person.name = 'aaa'
  console.log(personList[0]);
  
}


</script>
<style scoped>

</style>

4.路由

4.1初体验

1.导航区,展示区布局好。

2.生成路由器。

3.制定路由规则。

4.形成一个个的Vue文件。

 

router/index

// 创建路由器

// 第一步:引入createRouter
import { createRouter,createWebHistory } from "vue-router";
// 引入一个个组件
import Home from '../components/Home.vue'
import About from '../components/About.vue'
import News from '../components/News.vue'

// 第二步 创建路由器
const router = createRouter({
    // 路由模式
    history:createWebHistory(),
    // 路由规则
    routes:[
        {
            path:'/home',
            component:Home
        },
        {
            path:'/about',
            component:About
        },
        {
            path:'/news',
            component:News
        }
    ]
})

// 暴露
export default router

 main.ts

import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'

// 引入路由器
import router from './router/index'

let app = createApp(App)
app.use(router)
app.mount('#app')

App.vue

// 组件三大要素
// 结构
<template>
<div>
  <h2>Vue路由测试</h2>
  <div class="navigate">
    <RouterLink to="/home" class="item" active-class="jihuoshi">首页</RouterLink>
    <RouterLink to="/news" class="item" active-class="jihuoshi">新闻</RouterLink>
    <RouterLink to="/about" class="item" active-class="jihuoshi">关于</RouterLink>
  </div>
  <div class="main-content">
    此处以后需要展示各种内容
    <RouterView></RouterView>
  </div>
</div>
</template>
// 交互
<script setup lang="ts" name="App">
import { RouterView,RouterLink } from 'vue-router';
</script>
// 样式
<style scoped>
.navigate{
  .item{
    margin-left: 30px;
  }
  .jihuoshi{
    color: red;
  }
}
.main-content{
  height: 200px;
  background: rgb(213, 210, 210);
  /* opacity: 0.2; */
}
</style>

 4.2注意点(工程化)

1.路由组件通常放在pages或者views文件下,一般组件放在components

2.通过点击导航,视觉效果上消失了的路由,默认是销毁的,需要的时候需要再去挂载。

4.3to的两种写法 

<RouterLink to="/home" class="item" active-class="jihuoshi">首页</RouterLink>
    <RouterLink to="/news" class="item" active-class="jihuoshi">新闻</RouterLink>
    <RouterLink to="/about" class="item" active-class="jihuoshi">关于</RouterLink>
    <RouterLink :to="{name:'news'}" class="item" active-class="jihuoshi">新闻</RouterLink>
    <RouterLink :to="{path:'/home'}" class="item" active-class="jihuoshi">首页</RouterLink>
    <RouterLink :to="{name:'about'}" class="item" active-class="jihuoshi">关于</RouterLink>

// 创建路由器

// 第一步:引入createRouter
import { createRouter,createWebHistory,createWebHashHistory } from "vue-router";
// 引入一个个组件
import Home from '../components/Home.vue'
import About from '../components/About.vue'
import News from '../components/News.vue'

// 第二步 创建路由器
const router = createRouter({
    // 路由模式
    history:createWebHashHistory(),
    // 路由规则
    routes:[
        {
            name:'home',
            path:'/home',
            component:Home
        },
        {
            name:'about',
            path:'/about',
            component:About
        },
        {
            name:'news',
            path:'/news',
            component:News
        }
    ]
})

// 暴露
export default router

4.4路由的工作模式 

History模式

url更加美观,无#,更贴近传统的网站URL,缺点需要服务器配合处理路径问题,否则刷新会有404

Vue2:mode:'history'

Vue3:history:createWebHistory()

React:BrowserRouter

Hash模式

兼容性更好,不需要服务器处理路径

Vue2:mode:'hash'

Vue3:history:createWebHashHistory()

 4.5嵌套路由

// 创建路由器

// 第一步:引入createRouter
import { createRouter,createWebHistory,createWebHashHistory } from "vue-router";
// 引入一个个组件
import Home from '../components/Home.vue'
import About from '../components/About.vue'
import News from '../components/News.vue'

import Details from '../components/Details.vue'

// 第二步 创建路由器
const router = createRouter({
    // 路由模式
    history:createWebHashHistory(),
    // 路由规则
    routes:[
        {
            name:'home',
            path:'/home',
            component:Home
        },
        {
            name:'about',
            path:'/about',
            component:About
        },
        {
            name:'news',
            path:'/news',
            component:News,
            children:[
                {path:'details',component:Details},
            ]
        }
    ]
})

// 暴露
export default router

 news.vue

<template>
    <div>
        <!-- 导航区 -->
        <ul>
            <li v-for="item in newsList" :key="item.id">
                <RouterLink :to="{path:'/news/details'}">{{ item.title }}</RouterLink>
            </li>
        </ul>
    </div>
    <div>
        新闻内容
        <RouterView></RouterView>
    </div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterLink, RouterView } from 'vue-router';

const newsList = reactive([
    {id:1,title:'sadasda1',content:'西瓜1'},
    {id:2,title:'sadasda2',content:'西瓜2'},
    {id:3,title:'sadasda3',content:'西瓜3'},
    {id:4,title:'sadasda4',content:'西瓜4'},
])
</script>

4.6query传参

<template>
    <div>
        <!-- 导航区 -->
        <ul>
            <li v-for="item in newsList" :key="item.id">
                <!-- 第一种写法 -->
                <!-- <RouterLink :to="`/news/details?item=${item.content}`">{{ item.title }}</RouterLink> -->
                 <!-- 第二种写法 -->
                <RouterLink :to="{path:'/news/details',query:{item:item.content}}">{{ item.title }}</RouterLink>
            </li>
        </ul>
    </div>
    <div>
        新闻内容
        <RouterView></RouterView>
    </div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterLink, RouterView } from 'vue-router';

const newsList = reactive([
    {id:1,title:'sadasda1',content:'西瓜1'},
    {id:2,title:'sadasda2',content:'西瓜2'},
    {id:3,title:'sadasda3',content:'西瓜3'},
    {id:4,title:'sadasda4',content:'西瓜4'},
])
</script>
<template>
    <div>
        {{ query.item }}
    </div>
</template>
<script setup lang="ts" name="Details">
import { toRefs } from 'vue';
import { useRoute } from 'vue-router';
let route = useRoute()
// 必须使用toRefs ,否则失去了响应式
let {query} = toRefs(route)
console.log('使用路由',route.query.item);

</script>

 4.6params传参

<template>
    <div>
        <!-- 导航区 -->
        <ul>
            <li v-for="item in newsList" :key="item.id">

                 <!-- 第一种写法 -->
                <!-- <RouterLink :to =" `/news/details/${item.content}`">{{ item.title }}</RouterLink> -->
                <!-- 第一种写法 -->
                <RouterLink :to ="{
                    name:'details',
                    params:{
                        item:item.content
                    }}"
                >{{ item.title }}</RouterLink>
            </li>
        </ul>
    </div>
    <div>
        新闻内容
        <RouterView></RouterView>
    </div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterLink, RouterView } from 'vue-router';

const newsList = reactive([
    {id:1,title:'sadasda1',content:'西瓜1'},
    {id:2,title:'sadasda2',content:'西瓜2'},
    {id:3,title:'sadasda3',content:'西瓜3'},
    {id:4,title:'sadasda4',content:'西瓜4'},
])
</script>

<template>
    <div>
        {{ route.params.item }}
    </div>
</template>
<script setup lang="ts" name="Details">
// import { toRefs } from 'vue';
import { useRoute } from 'vue-router';
let route = useRoute()
// 必须使用toRefs ,否则失去了响应式
// let {params} = toRefs(route)
// console.log('使用路由',route.query.item);

</script>

备注1:传递params参数时,使用to,必须使用name,不能使用path

备注2:传递params参数时候,需要提前在路由规则处占位

 {
            name:'news',
            path:'/news',
            component:News,
            children:[
                {path:'details/:item',component:Details,name:'details'}

            ]
        }

 4.7路由的props传参

4.7.1路由加上props为true,只能处理params参数

{
            name:'news',
            path:'/news',
            component:News,
            children:[
                {path:'details/:item',component:Details,name:'details',props:true}

            ]
        }

 页面不再使用useRoute了

<template>
    <div>
        {{ item }}
    </div>
</template>
<script setup lang="ts" name="Details">
// import { toRefs } from 'vue';
// import { useRoute } from 'vue-router';
// import { toRefs } from 'vue';
// let route = useRoute()
// let {params} = toRefs(route)
// 一步搞定  其实我们可以认为路由收到params参数的时候,如果props为true,那么就会放在Vue组件上,有意思
defineProps(['item'])
// 必须使用toRefs ,否则失去了响应式
// let {params} = toRefs(route)
// console.log('使用路由',route.query.item);

</script>

 4.7.2处理query参数

{
            name:'news',
            path:'/news',
            component:News,
            children:[
                {
                    path:'details',
                    component:Details,
                    name:'details',
                    props(route){
                        return route.query
                    }
                }

            ]
        }
<template>
    <div>
        {{ item }}
    </div>
</template>
<script setup lang="ts" name="Details">
// import { toRefs } from 'vue';
// import { useRoute } from 'vue-router';
// // import { toRefs } from 'vue';
// let route = useRoute()
// let {query} = toRefs(route)
defineProps(['item'])
// 必须使用toRefs ,否则失去了响应式
// let {params} = toRefs(route)
// console.log('使用路由',route.query.item);

</script>

 4.7.3  自己决定传什么参数

{
            name:'news',
            path:'/news',
            component:News,
            children:[
                {
                    path:'details',
                    component:Details,
                    name:'details',
                    props(route){
                        return {item:'自己定义'}
                    }
                }

            ]
        }

 总结:一般组件可以自己写标签,属性自己加,props自己传,路由组件呢,就是在上边加上喽。本质就是这样的。

4.8 replace属性

replace其实就是替换,push就是加入栈了,方便用户在页面上进行页面切换。

<RouterLink replace to="/home" class="item" active-class="jihuoshi">首页</RouterLink>
    <RouterLink replace to="/news" class="item" active-class="jihuoshi">新闻</RouterLink>
    <RouterLink replace to="/about" class="item" active-class="jihuoshi">关于</RouterLink>

4.9 编程式导航 

import {useRouter} from 'vue-route'

let router = useRouter()

router.push('/news')

router.replace('/news')

传参就按照to属性去写

<template>
    <div>
        <!-- 导航区 -->
        <ul>
            <li v-for="item in newsList" :key="item.id">
                <button @click="toNews(item)">点我带你编程式导航并传参</button>

                 <!-- 第一种写法 -->
                <!-- <RouterLink :to =" `/news/details/${item.content}`">{{ item.title }}</RouterLink> -->
                <!-- 第一种写法 -->
                <RouterLink :to ="{
                    name:'details',
                    query:{
                        item:item.content,
                    }}"
                >{{ item.title }}</RouterLink>
            </li>
        </ul>
    </div>
    <div>
        新闻内容
        <RouterView></RouterView>
    </div>
</template>
<script setup lang="ts" name="News">
import { reactive } from 'vue';
import { RouterLink, RouterView,useRouter } from 'vue-router';

interface NewsInter{
    content:string
}

const newsList = reactive([
    {id:1,title:'sadasda1',content:'西瓜1'},
    {id:2,title:'sadasda2',content:'西瓜2'},
    {id:3,title:'sadasda3',content:'西瓜3'},
    {id:4,title:'sadasda4',content:'西瓜4'},
])

const router = useRouter()
function toNews(item:NewsInter){
    router.push({
        name:'details',
        query:{
            item:item.content,
        }}
    
    )
}
</script>

4.10 路由的重定向 

// 创建路由器

// 第一步:引入createRouter
import { createRouter,createWebHistory,createWebHashHistory } from "vue-router";
// 引入一个个组件
import Home from '../components/Home.vue'
import About from '../components/About.vue'
import News from '../components/News.vue'

import Details from '../components/Details.vue'

// 第二步 创建路由器
const router = createRouter({
    // 路由模式
    history:createWebHashHistory(),
    // 路由规则
    routes:[
        {
            name:'home',
            path:'/home',
            component:Home
        },
        {
            name:'about',
            path:'/about',
            component:About
        },
        {
            path:'/',
            redirect:'/news'
        },
        {
            name:'news',
            path:'/news',
            component:News,
            children:[
                {
                    path:'details',
                    component:Details,
                    name:'details',
                    props(route){
                        return {item:route.query.item}
                    }
                }

            ]
        },
       
    ]
})

// 暴露
export default router

5.pinia

符合直觉的Vue.js状态管理库,大白话就是项目帮助你全局存数据

 准备一个页面,写的的比较简单。

<template>
    <div>
        <div>我是数字:{{num}}</div>
        <button @click="getWords">获取撩妹情话</button>
        <div>{{ wordss }}</div>
    </div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import axios from 'axios';
let num = ref(110)
let wordss = ref<string>('')

async function getWords(){
    // 两次结构赋值再加重命名
    let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
    wordss.value = title
    
}

5.1 搭建pinia环境

第一步 npm i pinia

第二步 main.js

import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'

// 引入路由器
import router from './router/index'

// 引入pinia
import {createPinia} from 'pinia'

let app = createApp(App)

// 创建pinia
const pinia = createPinia()
// 项目使用pinia
app.use(pinia)

app.use(router)
app.mount('#app')

 5.2 简单存储加上读取

import { defineStore } from "pinia";

export const useCountStore = defineStore('count',{
    // 真正存储的地方
    state(){
        return {
            sum:6
        }
    }
}) 
<template>
    <div>
        <div>我是数字:{{countStore.sum}}</div>
        <button @click="getWords">获取撩妹情话</button>
        <div>{{ wordss }}</div>
    </div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'

// 使用countStore
let countStore = useCountStore()
// reactive 内部 有ref ,自动拆包不用加value
console.log('获取内容', countStore.sum);


let wordss = ref<string>('')

async function getWords(){
    // 两次结构赋值再加重命名
    let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
    wordss.value = title
    
}


</script>

 5.3 修改数据的三种方式

import { defineStore } from "pinia";

export const useCountStore = defineStore('count',{
    actions:{
        editSum(value:number){
            this.$state.sum += value
            this.$state.country = '中国'
            this.$state.name = '张三'
        }
    },
    // 真正存储的地方
    state(){
        return {
            sum:6,
            country:'china',
            name:'zhansan'
        }
    }
}) 
<template>
    <div>
        <div>我是数字:{{countStore.sum}}</div>
        <div>国家是:{{countStore.country}}</div>
        <div>名字是:{{countStore.name}}</div>
        <button @click="addSum">sum+10</button>
        <br>
        <button @click="getWords">获取撩妹情话</button>
        <div>{{ wordss }}</div>
    </div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'

// 使用countStore
let countStore = useCountStore()

function addSum(){
    // 第一种修改方式
    // countStore.sum += 10
    // countStore.country = '中国'
    // countStore.name = '张三'
    // 第二种批量修改  数量多的时候需要用,效能更高一些
    // countStore.$patch({
    //     sum:countStore.sum += 10,
    //     country:'中国',
    //     name:'张三'
    // })
    // 第三种 action
    countStore.editSum(10)
}
// reactive 内部 有ref ,自动拆包不用加value
console.log('获取内容', countStore.sum);


let wordss = ref<string>('')

async function getWords(){
    // 两次结构赋值再加重命名
    let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
    wordss.value = title
    
}


</script>

 actions复用最舒服

5.4 storeToRefs

<template>
    <div>
        <div>我是数字:{{sum}}</div>
        <div>国家是:{{country}}</div>
        <div>名字是:{{name}}</div>
        <button @click="addSum">sum+10</button>
        <br>
        <button @click="getWords">获取撩妹情话</button>
        <div>{{ wordss }}</div>
    </div>
</template>
<script setup lang="ts">
import {ref,toRefs } from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'
import { storeToRefs } from 'pinia';

// 使用countStore
let countStore = useCountStore()
// 千万别用,这样太累了
// let {sum,country,name} = toRefs(countStore)

// 需要过滤一下
let {sum,country,name} = storeToRefs(countStore)

function addSum(){
    // 第一种修改方式
    // countStore.sum += 10
    // countStore.country = '中国'
    // countStore.name = '张三'
    // 第二种批量修改  数量多的时候需要用,效能更高一些
    // countStore.$patch({
    //     sum:countStore.sum += 10,
    //     country:'中国',
    //     name:'张三'
    // })
    // 第三种 action
    countStore.editSum(10)
}
// reactive 内部 有ref ,自动拆包不用加value
console.log('获取内容', countStore.sum);


let wordss = ref<string>('')

async function getWords(){
    // 两次结构赋值再加重命名
    let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
    wordss.value = title
    
}


</script>

 5.5 getters 的 使用

本质就是修饰一下state的数据形成一个新的state,从而进行使用修饰过后的store数据。

import { defineStore } from "pinia";

export const useCountStore = defineStore('count',{
    actions:{
        editSum(value:number){
            this.$state.sum += value
            this.$state.country = '中国'
            this.$state.name = '张三'
        }
    },
    // 真正存储的地方
    state(){
        return {
            sum:6,
            country:'china',
            name:'zhansan'
        }
    },
    getters:{
        bigSum:(state) => state.sum*10,
        // bigSum(state){
        //     return state.sum*10
        // },
        daXie():string{
            return this.country.toUpperCase()
        }
    }
}) 
<template>
    <div>
        <div>getters:{{ bigSum }}</div>
        <div>我是数字:{{sum}}</div>
        <div>国家是:{{country}}</div>
        <div>国家名:{{daXie}}</div>
        <div>名字是:{{name}}</div>
        <button @click="addSum">sum+10</button>
        <br>
        <button @click="getWords">获取撩妹情话</button>
        <div>{{ wordss }}</div>
    </div>
</template>
<script setup lang="ts">
import {ref,toRefs } from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'
import { storeToRefs } from 'pinia';

// 使用countStore
let countStore = useCountStore()
// 千万别用,这样太累了
// let {sum,country,name} = toRefs(countStore)

// 需要过滤一下
let {sum,country,name,bigSum,daXie} = storeToRefs(countStore)

function addSum(){
    // 第一种修改方式
    // countStore.sum += 10
    // countStore.country = '中国'
    // countStore.name = '张三'
    // 第二种批量修改  数量多的时候需要用,效能更高一些
    // countStore.$patch({
    //     sum:countStore.sum += 10,
    //     country:'中国',
    //     name:'张三'
    // })
    // 第三种 action
    countStore.editSum(10)
}
// reactive 内部 有ref ,自动拆包不用加value
console.log('获取内容', countStore.sum);


let wordss = ref<string>('')

async function getWords(){
    // 两次结构赋值再加重命名
    let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
    wordss.value = title
    
}


</script>

 5.5 $subscribe 监听 store里边的数据修改

其实本质就是watch,监听数据变化的

<template>
    <div>
        <div>getters:{{ bigSum }}</div>
        <div>我是数字:{{sum}}</div>
        <div>国家是:{{country}}</div>
        <div>国家名:{{daXie}}</div>
        <div>名字是:{{name}}</div>
        <button @click="addSum">sum+10</button>
        <br>
        <button @click="getWords">获取撩妹情话</button>
        <div>{{ wordss }}</div>
    </div>
</template>
<script setup lang="ts">
import {ref,toRefs } from 'vue'
import axios from 'axios';
// 引入 useCountStore
import {useCountStore} from '../store/count'
import { storeToRefs } from 'pinia';

// 使用countStore
let countStore = useCountStore()
// 订阅,监听
countStore.$subscribe((mutate,state)=>{
    console.log('数据发生变化了',mutate,state);
    
})

 5.6 store 的 组合式写法

import { defineStore } from "pinia";

// export const useCountStore = defineStore('count',{
//     actions:{
//         editSum(value:number){
//             this.$state.sum += value
//             this.$state.country = '中国'
//             this.$state.name = '张三'
//         }
//     },
//     // 真正存储的地方
//     state(){
//         return {
//             sum:6,
//             country:'china',
//             name:'zhansan'
//         }
//     },
//     getters:{
//         bigSum:(state) => state.sum*10,
//         // bigSum(state){
//         //     return state.sum*10
//         // },
//         daXie():string{
//             return this.country.toUpperCase()
//         }
//     }
// }) 

import {ref} from 'vue'

// 相当于setup
export const useCountStore = defineStore('count',() => {
    // 相当于state
    const sum = ref(6)
    const country = ref('china')
    const name = ref('zhansan')
    const bigSum = ref(0)
    bigSum.value = sum.value * 10
    const daXie = ref('')
    daXie.value = country.value.toUpperCase()

    // 相当于actions

    function  editSum(value:number){
        sum.value += value
        country.value = '中国'
        name.value = '张三'
    }

    return {sum,country,name,editSum,bigSum,daXie}

})

6.组件通信

6.1 props 父传子 --非函数传值

1.父组件 传值  <HomeChild a="father"/>

2.子组件 收值 defineProps(['a'])

 6.2 props子传父 -- 函数传值

1.父组件 <HomeChild a="father" :sendToy="getToy"/>

function getToy(value:string){

    console.log(value);

    childToy.value = value

}

2.子组件 <button @click="sendToy(toy)">告诉父亲</button>

6.3  自定义事件  (专门用来子传父)

1.子组件定义自定义事件并在合适的时机触发

const emit = defineEmits(['childEvent'])

function sendBook(){

    emit('childEvent','数学')

}

2.父组件调用此自定义函数并且拿着一个回调函数接收这个值

<HomeChild a="father" :sendToy="getToy" @childEvent="qwe"/>

function qwe(val:string){

    console.log('111111111111',val);

}

6.4 mitt

pubsub;$bus;mitt   

提前绑好事件(提前订阅事件)

提供数据(合适时机触发事件,发布消息)

第一步  npm i mitt

第二步 引入

// 引入
import mitt from 'mitt'


// 创造
let emitter:any = mitt()
console.log('获取2m',emitter);

emitter.on('test1',(value:number)=>{
    console.log('asd','被调用',value);
})
setInterval(() => {
    emitter.emit('test1',10)
},1000)
setTimeout(() => {
    emitter.all.clear()
},3000)
// 暴漏
export default emitter

import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'

// 引入路由器
import router from './router/index'

import emitter from '../src/utils/emitter'

// 引入pinia
import {createPinia} from 'pinia'




let app = createApp(App)

// 创建pinia
const pinia = createPinia()
// 项目使用pinia
app.use(pinia)
app.use(emitter)
app.use(router)
app.mount('#app')
另外记得在在组件卸载的时候把emitter事件取消掉                                                                    emitter.off('text1')   // 避免内存泄漏

6.5 v-model

父组件

<template>
    <div>
        about页面
        <!-- v-model用在html标签上 -->
         <!-- <input type="text" v-model="name"/> -->
          <!--  (<HTMLInputElement>$event.target)  <HTMLInputElement> 断言是html元素-->
          <!-- <input type="text" :value="name" @input="name = (<HTMLInputElement>$event.target).value"/> -->

        <!-- v-model 用在组件上 -->
         <div>父组件呈现{{name}}</div>
            <!-- <MyInput v-model='name' /> -->
             <!-- <MyInput :modelValue="name" @update:modelValue="name = $event"/> -->

             <!-- 一定记得input -->

             <!-- 自定义写法 -->
              <MyInput v-model:ZIDINGYI='name' />
    </div>
</template>
<script lang="ts" setup name="About">
import {onUpdated, ref} from 'vue'

import MyInput from '../components/mynput.vue';

let name=ref('张三')
onUpdated(()=>{
    console.log('12121', name.value);
})
</script>

子组件

<!-- <template>
    <br>
    子组件
    <input 
        type="text" 
        :value="modelValue"
        @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)"
        />
</template>
<script setup lang="ts" name="MyInput">
defineProps(['modelValue'])
let emit = defineEmits(['update:modelValue'])
</script> -->

<template>
    <br>
    子组件
    <input 
        type="text" 
        :value="ZIDINGYI"
        @input="emit('update:ZIDINGYI',(<HTMLInputElement>$event.target).value)"
        />
</template>
<script setup lang="ts" name="MyInput">
defineProps(['ZIDINGYI'])
let emit = defineEmits(['update:ZIDINGYI'])
</script>

注意看自定义的写法 

 其实Ui底层大量使用了:modelValue 以及@update-modelValude

对于原生事件,$event就是事件对象  需要target

对于自定义事件,$event就是触发事件时,所传递的数据 不需要target

本质使用了父传子子传父 

6.6 $attrs

祖孙传值

<MyInput v-model:ZIDINGYI='name' a="haha" v-bind="{b:'100',c:'200'}"/>

看一下v-bind,相当于传了多了属性 

<GrandChild v-bind="$attrs"/>
<template>
    <div>
        孙
        {{ $attrs }}
    </div>
</template>
<script lang="ts" name="Sun">

</script>

6.7 $refs,$parents

$refs 父传子

<template>
    <div>
        孙
        {{ $attrs }}
        <div>
            密码是:{{password}}
        </div>
    </div>
</template>
<script setup lang="ts" name="Sun">
import {ref} from 'vue'
let password = ref(123)
defineExpose({password})
</script>
<GrandChild ref="grandChild" v-bind="$attrs"/>




import { onMounted,ref } from 'vue';
import GrandChild from './SunZi.vue'
let grandChild = ref()
onMounted(() => {
    console.log('555555555555',grandChild.value.password = '456');
    
})

在父组件可以获取多了子组件,进行值的修改 

$parents 子传父

1.defineExpose({parentNum})  父组件暴漏数据

2.$parent.parentNum 子组件直接使用

6.8 provide ,inject

本质不打扰子组件

祖先组件

import {onUpdated, ref,provide,reactive} from 'vue'
let car = reactive({
    brand:'奥迪',
    num:10
})
provide('car',car)

 孙组件

import {onMounted, ref,inject} from 'vue'
// {brand:'宝贝',num:0} 给默认值解决爆红问题
let car = inject('car',{brand:'宝贝',num:0})

祖先组件

import {onUpdated, ref,provide,reactive} from 'vue'

import MyInput from '../components/mynput.vue';
let money = ref(10013)
function updateMoney(value:number){
    money.value += value
}
provide('money',{money,updateMoney})

 孙组件

<template>
    <div>
        孙
        <button @click="updateMoney(6)">修改爷爷的钱</button>
    </div>
</template>
<script setup lang="ts" name="Sun">
import {onMounted, ref,inject} from 'vue'
let {money,updateMoney} = inject('money',{money:0,updateMoney:(params:number)=>{console.log(params)}})

6.9 slot

默认插槽

<template>

        <GrandChild>
            <span>你好</span>
        </GrandChild>
        <GrandChild>
            <span>你好a啊</span>
        </GrandChild>
        <GrandChild>
            <span>天气好啊好a啊</span>
        </GrandChild>
</template>
<script setup lang="ts" name="MyInput">
import GrandChild from './SunZi.vue'
</script>
<template>
    <div>
        孙
        <slot></slot>
    </div>
</template>
<script setup lang="ts" name="Sun">

</script>

 具名插槽  

其实默认插槽  name='default'


<template>
    <!-- 第一种写法 -->
        <!-- <GrandChild >
            <template v-slot:s2>
                <span>你好</span>
            </template>
            <template v-slot:s1>
                <span>,好啊</span>
            </template>
        </GrandChild> -->
        <GrandChild >
            <template #s2>
                <span>你好</span>
            </template>
            <template #s1>
                <span>,好啊</span>
            </template>
        </GrandChild>
</template>
<script setup lang="ts" name="MyInput">
import GrandChild from './SunZi.vue'
</script>
<template>
    <div>
        孙
        <slot name="s2"></slot>
        <slot name="s1"></slot>
        <slot name="s3"></slot>
    </div>
</template>
<script setup lang="ts" name="Sun">

</script>

 作用域插槽

数据在子组件上,怎么显示由父亲决定



<template>
    <!-- 第一种写法 -->
        <!-- <GrandChild >
            <template v-slot="params">
                <span>{{ params.age }}</span>
            </template>
        </GrandChild> -->
        <GrandChild >
            <template v-slot:qwe="{age}" >
                <span>{{ age }}</span>
            </template>
        </GrandChild>
        <!-- 当无name,那就是默认名default -->
        <!-- <GrandChild >
            <template v-slot:default="{age}" >
                <span>{{ age }}</span>
            </template>
        </GrandChild>
        <GrandChild >
            <template #default="{age}" >
                <span>{{ age }}</span>
            </template>
        </GrandChild> -->
</template>
<script setup lang="ts" name="MyInput">
import GrandChild from './SunZi.vue'
</script>
<template>
    <div>
        孙
        <slot age="18" name="qwe"></slot>
    </div>
</template>
<script setup lang="ts" name="Sun">

</script>

 

组件关系传递方式
父传子

1.props

2.v-model

3.$refs

4.默认插槽,具名插槽

子传父

1.props

2.自定义事件

3.v-model

4.$parent

5.作用域插槽

祖传孙,孙传祖

1.$attrs

2.provide,inject

兄弟之间传输,任意组件传输

1.mitt

2.pinia

7.其他API

7.1 shallowRef与shallowReactive

shallowRef

作用:创建一个响应式数据,但只对顶层属性进行响应式处理。

用法:let myVal = shallowRef(initialValue)

特点:只是跟踪引用值的变化,不关心值内部的属性变化。

 shallowReactive

作用:创建一个响应式数据,但只对顶层属性进行响应式处理,对象内部嵌套属性则不会变成响应式。

用法:let myVal = shallowReactive({......})

特点:只是跟踪引用值的变化,不关心值内部的属性变化。

7.2 readonly与shallowReadonly

readonly

作用:用于创建一个对象的深只读副本。

用法:const original = reactive({...})

const readOnlyCopy = readonly(original)

特点:

对象的所有嵌套属性都将变成只读状态。

任何修改对象被阻止

应用场景:

创建不可变的状态快照。

保护全局状态或者配置不被修改。

 shallowReadonly

浅层只读,深层可以改

7.3 toRow与markRaw

toRow

作用:用于获取一个响应式的数据对象的原始对象,返回的也不再是响应式,不触发视图更新。

场景:不想让别人改。

 markRaw

作用:标记一个对象,使其永远不会变成响应式的。

例如:使用mock.js时,为了防止误把mock.js变成响应式对象。可以使用markRaw去标记mock.js.

7.4 customRef

后续内容继续补充


http://www.kler.cn/news/359354.html

相关文章:

  • 【JavaEE】——TCP应答报文机制,超时重传机制
  • java散列表
  • 2.链表(代码随想录——python版本)
  • 代码复现(四):DBINet
  • MongoDB聚合管道(Aggregation Pipeline)
  • 计算机基础 -- 计算机补码的原理
  • 【LeetCode:910. 最小差值 II + 模拟 + 思维】
  • 问:JVM中GC类型有哪些?触发条件有哪些?区别是啥?
  • 基于Spring Boot的大创项目高效管理系统
  • 关于Vue脚手架
  • 大模型日报10月21日
  • 利用透视变换实现文档矫正功能
  • AUTOSAR_EXP_ARAComAPI的5章笔记(13)
  • iOS IPA上传到App Store Connect的三种方案详解
  • chat_gpt回答:python使用writearray写tiff速度太慢,有什么快速的方法吗
  • UML(Unified Modeling Language,统一建模语言)
  • 基于Neo4j的推理知识图谱展示:智能系统与图谱可视化
  • Go 1.19.4 命令调用、日志、包管理、反射-Day 17
  • Git的认识及基本操作
  • 基于IP的真实地址生成器