Vue3(学自尚硅谷)

news2025/1/20 5:54:23

一、基础准备工作

(一)过程

  1. 环境要求:有node.js环境、npm。
  2. 执行命令:
  3. npm create vue@latest

    而后选择:

  4. ✔ 请输入项目名称: … me_vue3
    ✔ 是否使用 TypeScript 语法? … 否 / 是
    ✔ 是否启用 JSX 支持? … 否 / 是
    ✔ 是否引入 Vue Router 进行单页面应用开发? … 否 / 是
    ✔ 是否引入 Pinia 用于状态管理? … 否 / 是
    ✔ 是否引入 Vitest 用于单元测试? … 否 / 是
    ✔ 是否要引入一款端到端(End to End)测试工具? › 不需要
    ✔ 是否引入 ESLint 用于代码质量检测? … 否 / 是
    

    只有TS选择是即可。

  5. 最后项目创建成功!
  6. 进入后根据提示安装两个插件,并执行命令:
    npm install

(二)文件结构

  1. .vscode文件中:extensions.json是官方给vscode做的插件信息("Vue.volar", "Vue.vscode-typescript-vue-plugin"),进入项目后会自动提示进行安装。
  2. node_modules中都是项目依赖的包。
  3. public中是公共资源,favicon.ico是网页的页签图标。
  4. src是项目的核心:assests是放静态资源的文件夹,里面是一些css样式等静态资源;components中是很多组件;App.vue是根组件,每个组件都挂载载App组件上;main.js中主要将根组件挂载。
  5. .gitgnore是git中一些忽略文件,里面出现的声明在推拉过程中会被忽略。
  6. env.d.ts中指定了很多文件类型(如jpg等),如果没有此文件,则ts不会认识jpg等格式的文件。
  7. index.html 程序的入口页面,里面有Id号为App的Div。
  8. package-lock.json package.json 一个关于webpack的配置信息。
  9. README.md是一些关于该项目的说明,文本文件。
  10. tsconfig.app.json ts tsconfig.json tsconfig.json tsconfig.node.json的配置文件。
  11. vite.config.ts,vite的配置文件。

这里介绍下vite,vite是Vue3新引入的项目构建工具,构建速度要比Vue2更快,同时实现了按需加载,当我们需要哪些时,哪些部分才会被加载。

(三)重要文件的简单认识

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8"><!--规定字符编码方式-->
    <link rel="icon" href="/favicon.ico"><!--引入页签图标-->
    <meta name="viewport" content="width=device-width, initial-scale=1.0"><!--移动端适配-->
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div><!--命名为app,是App组件加载的地方-->
    <script type="module" src="/src/main.ts"></script><!--引入main.ts,由其负责将App组件加入到该位置,挂载-->
  </body>
</html>

 main.ts

import './assets/main.css'

import { createApp } from 'vue'//从vue中引入createApp负责创建组件,并挂载到#app元素。
import App from './App.vue' //引入App组件

createApp(App).mount('#app'); //创建App组件(根组件),并挂载到#app元素。

(四)重写src文件

重写APP组件(Vue2写法):

main.ts无变化;

App.vue:

<template>
<!--vue3不需要最外层的根元素-->
 <div id="idContain">
    破天荒!
    <Person></Person>
 </div>
</template>

<script lang="ts">
import Person from "./components/Person.vue";
export default {
   name: "App",
   components:{
      Person,
   }
}
</script>

<style>
  #idContain{
    border-radius:5px;
    border: gray solid 2px;
    background-color: burlywood;
    box-shadow: 3px 4px black;
  }
</style>

Person.vue:

<template>
  <div id="personContain">
    <h2>{{ name }}</h2>
    <h2>{{ sex }}</h2>
    <button @click="getTel">点我获取联系方式</button>
    <button @click="changeName">点我改变姓名</button>
    <button @click="changeSex">点我改变性别</button>
  </div>
</template>

<script lang="ts">
export default {
  name:'Person',
  data(){
    return{
        name:'小明',
        sex: '男',
        tel: '19999999999'
    }
  },
  methods:{
    changeName(){
        console.log("one");
      if(this.name == "小李"){
      this.name = "小飞";
      }else{
        this.name == "小胡";
      }
    },
    changeSex(){
        this.sex=='女'?'男':'女';
    },
    getTel(){
        alert(this.tel);
    },
  }
}
</script>

<style>
  #personContain{
     border: 2px solid black;
     background-color: yellowgreen;
     border-radius: 2px;
     
  }
</style>

注意:Vue3中可以使用Vue2语法,但是现在我们这种写法下的数据不是响应式的,虽然函数会执行,data中的数据发生了改变,但是并未被渲染到页面中!

选项式API和组合式API:

Vue2是选项式写法;Vue3是组合式写法;

Vue2选项式的缺点:每当有某个业务需要改变时,关涉到数据信息,业务逻辑等信息,要在data、method等等选项中寻找相关内容,增加维护成本。

Vue3组合式写法的优点:所有关于一个业务的数据,方法等等都写在一块儿,这样就方便业务维护。

再重写组件内容(Vue3写法):
export default {
  name:'Person',
 setup(){
    let name = '小明';//因为数据和逻辑在一块,所以this的作用被弱化
    let sex='女'; //注意这里声明的数据需要返回才能被模版捕获
    let tel = '1999999999';//注意数据仍然不是响应式的
    function getTel(){
        alert(tel);
    }
    function changeName(){
        name = '小李';
    }
    function changeSex(){
        sex=="女"?'男':'女';
    }
    //返回
    return {name,sex,tel,changeName,changeSex,getTel}
 }
}
//未出现部分不变
</script>

通过测试得知,setup函数的执行时机要在beforeCreate之前,这进一步可以看出是弱化了this的作用。

data、method、setup同用

setup是否能够得到data中的数据?那反过来data是否能够得到setup中的数据?

因为setup要在beforeCreate声明周期之前,所以setup中不能获取data或者method的数据或方法;同样因为data和method是在beforeCreate方法之后声明的,所以它们可以得到setup中的数据。

data中使用setup中的数据:

data(){
      return{
        name:this.name,
      }
  },
setup返回类型
  1. 返回对象类型,中间属性是要被渲染的属性。
  2. 返回方法,该方法是渲染函数,返回值会覆盖整个模版。
setup语法糖

考虑到大量业务书写时,极易忘记将属性和方法返回,所以可以用如下方式代替setup函数:

<script setup>
let name = '小明';//因为数据和逻辑在一块,所以this的作用被弱化
    let sex='女'; //注意这里声明的数据需要返回才能被模版捕获
    let tel = '1999999999';//注意数据仍然不是响应式的
    function getTel(){
        alert(tel);
    }
    function changeName(){
        name = '小李';
    }
    function changeSex(){
        sex=="女"?'男':'女';
    }
</script>

其中内容和setup内容一致,同时不需要我们进行返回处理,自动返回变量和函数!这样我们项目中一个vue文件就两个script标签了!

缩写script
sudo npm i vite-plugin-vue-setup-extend

在vite.config.ts中引入并使用:

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueViteExtend from "vite-plugin-vue-setup-extend" //引入

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    VueViteExtend(),//使用
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

二、ref、reactive与响应式

(一)ref使用(基本类型响应式、对象类型亦可)

import {ref} from 'vue';

把要变为响应式的数据用ref函数包裹,即可让数据成为响应式数据。

let name = ref('小明');

 结构:

通过变量的value属性更改,引起模版自动变化。

function changeName(){
name.value = '小李';
    }

ref中也是调用了reactive的方法!

(一)a 利用ref防止命名冲突

场景模拟:App组件和Person组件中都有id为divOne的元素要输出,利用getElementById获取元素输出会产生冲突

App组件:

 <div id="idContain">
    破天荒!  
    <Person></Person>
    <div id="divOne">我是app组件中的元素</div> 
    <button @click="outElement">输出元素</button>
 </div>
==========
function outElement(){
   let elementOne = document.getElementById("divOne");
   if(elementOne != null){
    console.log(elementOne.innerHTML);
   }
  }

Person组件:

<div id="personContain">
   <div id="divOne" ref="divRefOne">我想要更好的生活</div>
  <button @click="outElementOne">输出元素</button>
  </div>
=======
function outElementOne(){
    let elementOne = document.getElementById("divOne");
   if(elementOne != null){
    console.log(elementOne.innerHTML);
   }
  }

输出结果:

解决方法:使用ref标记,修改如下(以Person为例,二者一致)

//1
<div ref="divOne">我是app组件中的元素</div> 
//2
let divOne = ref();
function outElement(){ 
    console.log(divOne.value)
  }
(一)b当ref标记的是子组件时,需要子组件暴露后,才能输出具体内容
//app
 <Person ref="divOne"></Person>
    <button @click="outElement">输出元素</button>
//app script
 let divOne = ref();
function outElement(){ 
    console.log(divOne.value)
  }

子组件不暴露输出结果:

子组件暴露:

 let one = "我是即将要暴露的数据";
   let two = "我是也是即将要暴露的数据";
 defineExpose({one,two});

结果:

(二)reactive使用(对象类型响应式) 

import {reactive} from "vue";

对象深度响应,无论多深,都能实时响应到模版

对象:

模版:
<div>
      <h2>我的家人有{{ family.one.name}}和{{ family.two.name }}</h2>
      <button @click="changeFamilyName">改变家人姓名</button>
    </div>
===============================================
script:
 //验证对象响应式
    let family = reactive({one:{name:"张金平",age:74},two:{name:"陈玉丽",age:45}});
    function changeFamilyName(){
      family.one.name = "张金平和陈志俭";
      family.one.age = 53;
    }

数组亦然:

模版:
<h2>
我喜欢的动漫角色有{{ cotton[0].name }}、{{ cotton[1].name }}、{{ cotton[2].name }}
</h2>
<button @click="changeCottonName">改变漫角姓名</button>
script:
let cotton = reactive([{name:'美杜莎女王',age:23},{name:'山内樱良',age:18},{name:'02',age:22}])
    function changeCottonName(){
      cotton[0].name = '阳菜';
    }

(三)两个方法的区别

a、对象整体替换

ref(可以直接替换,仍然响应式):

   let cotton = 
ref([{name:'美杜莎女王',age:23},{name:'山内樱良',age:18},{name:'02',age:22}])
    function changeCottonName(){
      cotton.value = [{name:'清漪',age:24},{name:'小鸟游六花',age:18},{name:'daring',age:24}];
      //响应式
    }

reative(不可以直接替换,要使用Object.assign方法)

let cotton = reactive([{name:'美杜莎女王',age:23},{name:'山内樱良',age:18},{name:'02',age:22}])
    function changeCottonName(){
      // cotton = [{name:'清漪',age:24},{name:'小鸟游六花',age:18},{name:'daring',age:24}];//非响应式
      Object.assign(cotton,[{name:'清漪',age:24},{name:'小鸟游六花',age:18},{name:'daring',age:24}]);
      //响应式
    }
b、对象属性的传递响应

 深拷贝:

//一般深拷贝(地址值)
    let arrOne = cotton[0];
    function changArrOne(){
         arrOne.name="彩鳞";//改掉后,cotton也会受影响。
    }

浅拷贝(用到toRefs方法,地址附着,但不会实时响应):

  //一般深拷贝(地址值)
    let cotton = {name:"美杜莎",age:33}; 
    let {name,age} = toRefs(cotton);
    // let name = toRef(cotton,"name");
    function changArrOne(){
         name.value = "彩鳞";//改掉后,cotton也会受影响。
         console.log(name.value);
    }

三、computed函数、watch函数

(一)computed函数

  let firstName = "chen";
    let endName = "pei";
   let  allName=computed(()=>{return firstName + endName});

computed整体被修改可以触发setter,获取出发getter

let firstName = ref("chen");
    let endName = ref("pei");
   const  allName=computed({
    get(){
      return firstName.value + endName.value;
    },
    set(value){
        firstName.value=value.split(",")[0];
        endName.value=value.split(",")[1];
    }
   });
  function changAllName(){
      allName.value="陈,佩";
  }

(二)watch函数、watchEffect函数

  • 能够监视的四种类型:ref定义的数据、reactive定义的数据、函数返回一个值(getter函数)、一个包含上述内容的数组
  • 监视普通变量:
 let firstName = ref("chen");
    let endName = ref("pei");
    function changFirstName(){
      firstName.value = "张";
    }
   watch(firstName,(newValue,oldValue)=>{
    console.log("姓发生了变化!");
   })
  • 监视对象(ref)
  let allName = ref({
      firstName:"陈",
      endName:"佩"
    })
    function changFirstName(){
      allName.value.firstName = "张";
    }
    //第三个参数中使用deep配置项开启深度监视
   watch(allName,(newValue,oldValue)=>{
    console.log("姓发生了变化!");
   },{deep: true})
   //默认没有深度监视,只会监视整个对象全部的变化。

注意:监视对象整体改变后,newValue和oldValue分别是之前对象和新对象;如果对象某个属性发生改变,新旧值相同

  • 监视对象(reactive)
 let allName = reactive({
      firstName:"陈",
      endName:"佩",
      lover:{
        name:"傻妞",
        birth: 2008
      }
    })
    function changeFirstName(){
      allName.firstName = "张";
    }
    function changeLoverName(){
      allName.lover.name = "陆小千";
    }
   watch(allName,(newValue,oldValue)=>{
    console.log("姓或者lover名字发生了变化!");
   })
   //默认深度监视,无法手动取消深度监视
  • 仅监视对象中某个属性
//只需把要监视的属性用函数返回即可完成监视  
watch(()=> allName.lover.name,(newValue,oldValue)=>{
    console.log("lover名字发生了变化!");
   })
  • 监视多个可监视类型组成的集合
let allName = reactive({
      firstName:"陈",
      endName:"佩",
      lover:{
        name:"傻妞",
        birth: 2008
      }
    })
    let outB = ref({
      number:2,
      age: 23
    });
    function changeLoverName(){
      allName.lover.name = "陆小千";
    }
    function addNumberOne(){
      outB.value.age += 1;
    }
   watch([()=> allName.lover.name,outB,()=>outB.value.number],(newValue,oldValue)=>{
    console.log("lover名字,姓名,或者outB发生了变化!");
   },{deep: true})
   //这里有ref数据,所以要开启深度监视
  • watch解除监视
    //返回一个函数类型,用于解除监视
   let closeWatch =watch(outB,(newValue,oldValue)=>{
    console.log("lover名字,姓名,或者outB发生了变化!");
    if(newValue.age == 24){
       closeWatch();//关闭监视
    }
   },{deep: true})
  • watchEffect自动监视
//会自动判断出现在条件中需要监视的数据并进行监视
    watchEffect(()=>{
    if(outB.value.age >= 28){
      alert("年龄有点大了,可以考虑结婚了,朋友!");
    }else if(allName.firstName == "张"){
      alert("大哥,姓也要改呀!");
    }
   })

四、数据暴露与接收

(一)父组件向子组件传递数据

传递:

 <Person :testOne="testOne" :testTwo="testTwo"></Person>
//子组件

接收:

// 接收父组件传来的数据testOne,testTwo
  defineProps(['testOne','testTwo']);

注:define开头函数为宏函数,不需要import即可直接使用!

(二)子组件向父组件传递数据

暴露(子组件):

//测试子组件暴露数据
  let testSonOne = 'i am super man!';
  let testSonTwo = 'i am your lover!';
  //暴露属性
  defineExpose({testSonOne,testSonTwo});

使用(父组件):

 <Person ref="sonComponent"></Person>
<!--父组件中子组件形式-->

//获取子组件,根据子组件标签中ref标识;
  let sonComponent = ref();
  //获取子组件暴露属性
  function showData(){
     console.log(sonComponent.value.testSonOne);
  }

注意模版加载顺序为:子组件-》父组件!

四、生命周期

(一)Vue2和Vue3对比

beforeCreat 创建前setup
created 创建后
beforeMount 挂载前onBeforeMount
mounted 挂载后onMounted
beforeUpdate 更新前onBeforeUpdate
updated 更新后onUpdated
beforeDestroy 销毁前onBeforeUnmount 卸载前
destroyed 销毁后onUnmounted  卸载后

五、hooks与组合式API

(一)两个功能实现,原方式:

<div id="personContain">
   <div id="numDiv">
    <!-- 功能一部分 -->
    <h3>现在的数字是:{{ numNow }}</h3>
    <button @click="numAddOne">数字加一</button>
   </div>
   <div id="mottoDiv">
    <!-- 功能二部分 -->
    <li v-for="(motto,index) in mottoList" :key="index">{{ motto }}</li>
    <button @click="mottoAddOne">增加格言</button>
   </div>
  </div>
//结构部分
=================
 //数据
  let numNow = ref(0);
  let mottoList = ref(["早起的鸟儿有虫吃!"]);
  //方法
 function mottoAddOne(){
  //从网站获取格言
   axios.get("https://v1.hitokoto.cn/?c=f&encode=text").then((data)=>{
      //将数据插入数组
    mottoList.value.push(data.data);
   },(error)=>{alert("出错了好小子!" + error)});
 }
 function numAddOne(){
   numNow.value += 1;
 }
  • 数据和方法在一块,维护有困难!

​​​​​​​(二)使用hook方式

  • 创建文件夹,并创建两个功能相关ts,注意文件命名使用驼峰式(非大驼峰)

​​​​​​​​​​​​​​

  • 将两个功能相关的数据放入到文件中
mottoAdd:
import {ref,onMounted} from 'vue';
import axios from 'axios';
export default function(){
//数据
let mottoList = ref(["早起的鸟儿有虫吃!"]);
//方法
function mottoAddOne(){
//从网站获取格言
 axios.get("https://v1.hitokoto.cn/?c=f&encode=text").then((data)=>{
    //将数据插入数组
  mottoList.value.push(data.data);
 },(error)=>{alert("出错了好小子!" + error)});
};
//同样可以调用生命周期函数
onMounted(()=>{console.log("我是生命周期函数,我被挂在后函数调用了!")});
return {mottoList,mottoAddOne}
}
=================
numAdd:
import {ref} from 'vue';
//hooks中的ts文件也可以调用vue的生命周期函数
export default function(){
let numNow=ref(0);
//方法
function numAddOne(){
 numNow.value += 1;
}
return {numNow,numAddOne}
}
  • 文件中使用(Person.vue)
import mottoAdd from '../hooks/mottoAdd';
import numAdd from '../hooks/numAdd';
   // 使用
  let {numNow,numAddOne} = numAdd();
  let {mottoList,mottoAddOne} = mottoAdd();

六、路由的使用

(一)创建路由需要的路由组件(放在views文件夹中)

(二)创建路由器(在router文件夹中)

import Index from '../views/Index.vue';
import News from '../views/News.vue';
import Play from '../views/Play.vue';
//引入创建router的函数,创建路由器
import { createWebHashHistory,createRouter } from 'vue-router';
let routerFirst  = createRouter({
    history:createWebHashHistory(),
    routes:[
        {  
        name:'xinwen',
        path:'/news',
        component:News
        },
        {
        name:'shouye',
        path:'/',
        component:Index
        },
        {
            name:'yule',
            path:'/play',
            component:Play
        }
    ]
})
export default routerFirst;

(三)在main.ts中使用路由

//引入
import routerFirst from './router/index';
import {createApp} from "vue";
import App from "./App.vue";
let app = createApp(App);
//使用
app.use(routerFirst);
app.mount('#app');

(四)声明route-link和route-view

 <div id="navDiv">
    <!-- 用来跳转到指定路由 -->
    <router-link to='/' active-class="active">首页</router-link>
    <router-link to='/news' active-class="active">新闻</router-link>
    <router-link to='/play' active-class="active">娱乐</router-link>
  </div>
  <!-- 声明路由组件出现的位置 -->
  <router-view></router-view> 

七、路由相关知识

(一)query传参

父级组件内发送参数:

<router-link 
    :to="{
      path:'/news',
      query:{
        oneData:'若非群玉山头见',
        twoData:'会向瑶台月下逢'
      }
      }" active-class="active">新闻</router-link>

路由组件内接收参数:

//第一步引入useRouter函数
import {useRoute} from 'vue-router'
 //第二步创建Route对象,并获取对应query
 let route = useRoute();
 let oneData= route.query.oneData;
 let twoData= route.query.twoData;

(二)params传参

在路由配置中定义传参参数名:

{  
        name:'xinwen',
        //?表示该属性可有可与
        path:'/news/:oneData/:twoData/threeData?',//此行
        component:News
        },

组件接收参数:

let oneData= route.params.oneData;
let twoData= route.params.twoData;

(三)props传参

router配置中:

 {  
        name:'xinwen',
        path:'/news/:oneData/:twoData',
        component:News,
        props:true  //开启后,路径中的参数被作为props参数传入
        }

接收:

 defineProps(['oneData','twoData']);

(四)自定义传递参数

router配置中:

props(route){
            return route.query;
        }

Person组件中:

<router-link 
    :to="{
      path:'/news/若非群玉山头见/会向瑶台月下逢',
      query:{
        oneData:'云想衣裳花想容',
        twoData:'春风拂槛露华浓'
      }
      }" active-class="active">新闻</router-link>

八、编程式路由导航

标签:
<button @click="toNews">新闻</button>

script:
import {useRouter} from 'vue-router';
 let router = useRouter();
function toNews(){
      router.push(
        {
          path:'/new',
          query:{
            oneData:'昨夜雨疏风骤',
            twoData:'浓睡不消残酒'
          }
        }
    );
 }

小点:redirect重定向到新的路径!

九、状态管理

(一)对比

Vue2使用vuex进行集中式状态管理!Vue3使用pinia进行集中式状态管理!

共同点是,当有数据是全局共享的时候,就要用到集中式状态管理来共同操纵共享的数据。

(二)使用

安装

sudo npm i pinia

引入

//引入集中式状态管理库pinia
import {createPinia} from 'pinia';

应用(main.js)

//创建库
let pinia = createPinia();
//使用pinia库
app.use(pinia);

模块化声明store(创建store文件夹/创建useXXX.ts文件)

模块化要求store命名都为usexxx.ts

//引入pinia中store选项
import {defineStore} from 'pinia';
//调用
//第一参数为唯一固定id号,命名必须为useXXX,这是规定哦
export  const useStore =  defineStore('useNum',{
    state(){
      return{
    myNum: 0 
      }//保存在集中状态管理中的数据。
    },
    actions:{
      //该参数是调用时传入滴,注意使用普通函数,因要调用this
      cutMyNumFun(numMid:number){
        this.$state.myNum -= numMid;
      }
    },
    /* getters 中定义的函数都可以直接在store中调用,类似于计算属性
    */
    getters:{
      tenNum(state){
        return state.myNum * 10;
      }
    }
});
============
函数式写法:
export  const useStore =  defineStore('useNum',()=>{
    
    let myNum = ref(0); 

   
    function cutMyNumFun(numMid:number){
        myNum.value -= numMid;
    }
    /* getters 中定义的函数都可以直接在store中调用,类似于计算属性
    */
    
   let tenNum = myNum.value * 10;
    
    return {tenNum,myNum,cutMyNumFun};
});

引入并使用:

//引入storeToRefs,让store解构出的属性同样是响应式滴!
import {storeToRefs} from 'pinia';
//引入store
import {useStore} from '../store/numStore';
//产生store
let useNumStore = useStore();
let myNumMid= ref(0);//此数据用作临时中转,下面用到,这里不用关注!
let {myNum,tenNum} = storeToRefs(useNumStore);
  • 这样公用数据就被我们响应式引入到组件中了!

在下面结构中使用和更改数据:

结构:
<div id="numAdd">
    <h3>现在数字是:{{ myNum }}</h3>
    <h3>该数字的十倍是:{{ tenNum }}</h3>
    <!-- myNum需要集中管理,另一组件也需要使用 -->
    n:<select v-model="myNumMid">
      <option :value="1">1</option>
      <option :value="2">2</option>
      <option :value="3">3</option>
      <option :value="4">4</option>
    </select>
    <button @click="addNum">点我为数字加n</button>
    <button @click="cutNum">点我为数字减n</button>
  </div>



script:
//方法逻辑
function addNum(){
  /*  修改数据方法一,直接通过store接触到myNum
  store中的myNum也是ref响应式,但因为其在对象中,所以会自动解包,不用.value
  */
  useNumStore.myNum += myNumMid.value;
}
function cutNum(){
  /*数据修改方法二,通过state接触到myNum */
  // useNumStore.$state.myNum -= myNumMid.value
  /*数据修改方法三,在store配置中增加actions,然后调用actions中相关方法 
  这种方式一般用在逻辑比较复杂的时候,*/
  useNumStore.cutMyNumFun(myNumMid.value);
}

(三)store.$subscribe订阅

当咱创建的store对象中state数据有变化时,该函数会被调用!

useNumStore.$subscribe((mutations,state)=>{
    //state中的数据发生了变化
    console.log("数据发生了变化!" + state.myNum + "\n" );
    console.dir(mutations)
});

state:变化后的数据。

mutations:事件对象、target.newValue、target.oldValue

十、组件间通信 

(一)父传子、子传父(自定义事件)

父传子:


父组件:
<Person ref="sonComponent" :fatherData="{name:'路飞',age:23,text:'hhhh嘿嘿'}"></Person> <!--子组件-->
子组件: 
<h2>从父组件中获得的数据:{{ fatherData.name }}</h2>

defineProps(['fatherData']);

子传父:

父组件:
 结构:
<h2>从子组件获取的数据:{{ mySonData.name }}</h2>
 script:
let mySonData:any = ref({});
function getSonData(value:object){
     mySonData.value = value
}
===========
子组件:
 结构:
 <button @click="give">送数据</button>
 script:
//接收到父组件传递的自定义事件,可以多个!
let emits = defineEmits(['get']);
//根据指定自定义事件执行,后面参数是传递的参数
function give(){
  emits('get',{name:'儿子',age:33})
}

(二)引入外部库mitt实现数据传递

引入:npm i mitt

创建utils文件夹,创建emittev.ts并导出实例

import mitt from 'mitt';
export const bus = mitt();
//四个方法:all所有数据、emit执行事件、off解绑事件、on绑定事件。

数据获取方:

import {bus} from './utils/emittev';
let mySonData:any = ref(0);
 bus.on("get",(value:any)=>{   
   mySonData.value = value;
 })

数据提供方:

bus.emit('get',{name:'儿子',age:24})

(三)利用$attrs实现爷孙数据传递。

注明,当父组件给子组件传递数据时,子组件若不使用defineProps接收,则在子组件的$attrs中,利用此点,实现爷传孙!

传递数据时   :name="小李" :age=23  等同于 v-bind="{name:"小李" :age:23 }" 

爷:
<Person v-bind="{name:'我是你爸爸的爸爸',say:'好好生活,未来会更好!'}"></Person>
儿子:
<PersonSon v-bind="$attrs"></PersonSon> 
孙子:
 <h2>这是从爷爷那里获取的数据:{{ name + say }}</h2>

defineProps(['name','say']);

(四)provide、inject(vue提供)

主要用于隔代数据传递,该方式不需要中间组件参与,有利于降低逻辑复杂性!

提供方结构(爷):

<div id="idContain">
    <Person ></Person>
    <h2>这是传递给孙子的数据:{{ giveSonData }}</h2>
    <button @click="changeDataInGrd">点我改变爷爷的数据!</button>
 </div>

供方js:

import {ref,provide} from "vue";
let giveSonData = ref({name:'韩少功',age:34,dream:'成为一个大作家!'})
//将数据给出去。
provide('giveDataToMySon',giveSonData);
function changeDataInGrd(){
giveSonData.value.dream = '看看我是不是响应式的!';
 }
 provide('changeGrdData',changeDataInGrd);

接收方结构:

<div id="personDiv">
    <h2>接收到来自爷爷的数据:{{ dataFromGrd }}</h2>
    <button @click="changeData">改变来自爷爷的数据</button>
  </div>

接收方js:

import {ref,inject,toRefs} from 'vue'
 //接收,第二个参数是默认值,当该标签数据不存在时就使用默认值
 let dataFromGrd = inject('giveDataToMySon',{name:'孤胆英雄',dream:'成为一个真正自立自强的人!'});
 //检验是否是响应式数据
 let changeData = inject('changeGrdData',()=>{});

以上方式数据通过爷组件来修改,所以数据都是响应式的!

要想是响应式数据,要是对象,要么是一个响应对象的整体(ref,reative包裹),若是响应式对象中某个具体的值则在接收方不会是响应式!

十一、插槽的使用(跟vue无二致,不详细解释)

插槽就是当有同样的形式需要插入不同的数据,可以利用插槽,省写组件,仅用一个组件,来装入不同数据。

(一)默认插槽

首先需要声明一个通用的插槽形式:

<template>
  <div id="slotDiv">
    <!-- 标明默认插槽的位置 -->
    <slot></slot> 
  </div>
</template>

<script lang="ts">
export default {
      name:'SlotNews'
}
</script>
<script setup lang="ts">

</script>


<style>

</style>

引入并使用三次:

<div id="personContain">
  <SlotNews>
    <li v-for="(say,index) in loveSaysList" :key="index">{{ say }}</li>
  </SlotNews>
  <SlotNews>
  <h2>{{ name }}</h2>
  </SlotNews>
  <SlotNews>
  <h4>{{ people }}</h4>
  </SlotNews>
<button @click="getNewLoveSays">获取情话</button>
</div>
//数据一
let loveSaysList:any = ref([]);
async function getNewLoveSays(){
    axios.get('https://api.uomg.com/api/rand.qinghua?format=json').then(
      (dataFrom)=>{
        let {data:{content}} = dataFrom;
        loveSaysList.value.push(content);

      },
      (error)=>{
        console.log("出现错误!" + error)
      }
    )
}
//数据二
let name = ref("少年")
//数据三
let people = ref({
  name:'陈大炮',
  age: 23
})

默认插槽就是,你在组件标签中加入的元素都会被添加到<slot>标签的位置。

(二)命名插槽

由名知意,当组件标签中要插入多个内容,且插入位置不同时,需要多个<slot>标签来表标识,slot标签使用 name属性标识。

<div id="slotDiv">
    <!-- 标明默认插槽的位置 -->
    <slot name="one"></slot>
    <hr>
    <slot name="two"></slot>
  </div>

父组件中使用v-slot或者#插槽名将元素插入指定位置(注意只能在template标签中使用)

<SlotNews>
    <template #two>
    <li v-for="(say,index) in loveSaysList" :key='index' >{{ say }}</li>
    </template>
    <template #one>
      <h2>哈哈哈哈</h2>
    </template>
  </SlotNews>
  <SlotNews>
  <template  #one><h2>{{ name }}</h2></template>
  </SlotNews>

(三)作用域插槽

当数据在子组件中,父组件需要获取时,就是作用域插槽的用武之地。

子组件通过<slot>标签将数据传递到父组件。

<slot name="one" :trueSay="'我爱你像火焰一般炽烈'"></slot>

父组件接收:

<SlotNews>
    <!--注意结束数据的格式-->
    <template #one="trueSay">
      <h2>{{ trueSay.trueSay }}</h2>
    </template>
  </SlotNews>

十二、其他API

(一)shallowRef、shallowReactive

只建立第一层数据的响应式,更深层次不具有响应式,在接收返回对象类型较大且有特殊要求时,能够提高效率!

(二)readonly shallowReadonly toRaw martRaw

给响应式数据加上只读限制,方式他人使用误修改!

let name = ref("少年");
let nameReadOnly = readonly(name);
//报错
nameReadOnly.value = "dksjl" ;
let people = ref({
  name:'陈大炮',
  age: 23,
  lover:{
    name:'枪炮',
    age:45
  }
});
 let peopleShallowReadOnly = shallowReadonly(people);
 peopleShallowReadOnly.value.lover.age = 66;
 //报错,只读属性
 peopleShallowReadOnly.value ={};
toRaw从一个响应式数据中抽取其原始数据

markRaw

标记一个对象,使其永远无法成为响应式数据

(三)customRef(自定义响应式数据)

let name = "意气风发";
let nameRef = customRef((track,trigger)=>{
  return{
  get(){
     track();//一旦数据被修改就会调用该函数
     return name;
    },
  //当数据被修改时调用,传入修改后的值
  set(newValue){
    console.log("数据被修改了,修改后的数据是" + newValue);
    
    name = newValue + "*";
    trigger();//告诉get数据被修改了。
  }
}
});
 function changeName(){
     nameRef.value = "无敌好吧"
 }

结构:

<h2>{{ nameRef }}</h2>
<button @click="changeName">修改name</button>

(四)<teleport>

<Teleport to="body">
  <!--将被包裹的元素传送到指定元素中,但是真实父子关系及数据处理逻辑不变-->
  </Teleport>

对于一些特殊样式设计有很大作用,如图片使用filter属性导致fix失效,定位无法以视口为基准,这时就可以用该组件,将其传入body中进行fix定位。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1571829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Springboot传参要求

Web.java(这里定义了一个实体类交Web) public class Web{ private int Page; public int getPage() {return Page;}public void setPage(int page) {Page page;} } 1、通过编译器自带的getter、Setter传参 。只是要注意参数的名字是固定的&#xff0c;不能灵活改变。 传参的…

苹果cmsV10 MXProV4.5自适应PC手机影视站主题模板苹果cms模板mxone pro

演示站&#xff1a;http://a.88531.cn:8016 MXPro 模板主题(又名&#xff1a;mxonepro)是一款基于苹果 cms程序的一款全新的简洁好看 UI 的影视站模板类似于西瓜视频&#xff0c;不过同对比 MxoneV10 魔改模板来说功能没有那么多,也没有那么大气&#xff0c;但是比较且可视化功…

51单片机实验02- P0口流水灯实验

目录 一、实验的背景和意义 二、实验目的 三、实验步骤 四、实验仪器 五、实验任务及要求 1&#xff0c;从led4开始右移 1&#xff09;思路 ①起始灯 &#xff08;led4&#xff09; ②右移 2&#xff09;效果 3&#xff09;代码☀ 2&#xff0c;从其他小灯并向右依…

python_3

文章目录 题目运行结果模式A模式B模式C模式D 题目 mode input("请选择模式:") n int(input("请输入数字:"))if mode "A" or mode "a":# 模式A n:输入的层数 i:当前的层数# 每行数字循环次数 ifor i in range(1, n 1):for j in r…

【C++】vector系列力扣刷题日志(136.只出现一次的数字,118.杨辉三角,26.删除有序数组中的重复项,260.只出现一次的数字 |||)

目录 136.只出现一次的数字 118.杨辉三角 26.删除有序数组中的重复项 260.只出现一次的数字 ||| vector的详细介绍及用法这里就不过多赘述了&#xff0c;可以参考上一篇博客&#xff1a;vector的介绍及使用说明 136.只出现一次的数字 题目&#xff1a; 给你一个 非空 整数…

Python--Django--说明

Django 是基于python 的 Web 开发框架. &nsbp;   Web开发指的是开发基于B/S 架构, 通过前后端的配合, 将后台服务器上的数据在浏览器上展现给前台用户的应用. &nsbp;   在早期, 没有Web框架的时候, 使用 Python CGI 脚本显示数据库中的数据. Web框架致力于解决一些…

短视频素材高清无水印购买要多少钱?

大家好&#xff01;在制作短视频时&#xff0c;找到短视频素材高清无水印是非常重要的。那么&#xff0c;短视频素材高清无水印在哪里找呢&#xff1f;今天&#xff0c;我要给大家推荐六个主流的视频素材分享网站&#xff0c;帮助你轻松获取高质量的短视频素材高清无水印&#…

【Linux】Linux C 编程

在 Windows 下编程首先就是安装对应的 IDE &#xff0c;然后在 IDE 里面进行代码编写和编译&#xff0c;但是在 Linux 下&#xff0c;这两个部分是分开的&#xff0c;比如我们可以使用 vim 编辑器编写代码&#xff0c;然后用 gcc 编译器编译代码。Ubuntu 下有一些可以进行编程的…

Linux从入门到精通 --- 2.基本命令入门

文章目录 第二章&#xff1a;2.1 Linux的目录结构2.1.1 路径描述方式 2.2 Linux命令入门2.2.1 Linux命令基础格式2.2.2 ls命令2.2.3 ls命令的参数和选项2.2.4 ls命令选项的组合使用 2.3 目录切换相关命令2.3.1 cd切换工作目录2.3.2 pwd查看当前工作目录2.4 相对路径、绝对路径和…

主流验证码对比及选型

目录 一、什么是验证码二、验证码的作用三、验证码的类型四、验证码厂商1、 [腾讯云验证码](https://cloud.tencent.com/document/product/1110)1.1 验证方式1.2 费用 2、[阿里云验证码](https://www.aliyun.com/activity/security/wafcaptcha)2.1 验证方式2.2 费用 3、[顶象验…

分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别

分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别 目录 分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别分类效果基本介绍模型描述程序…

SpringBoot配置文件加载的优先级顺序

SpringBoot配置文件加载的优先级顺序 1.按文件类型2.按路径比较3.按命令行参数设置 1.按文件类型 SpringBoot的配置文件可以分为.properties .yml .yaml 在同一路径下&#xff08;比如都在classpath下&#xff09;三者的优先级顺序是.properties> .yml> .yaml 2.按路径…

深度学习十大算法之深度Q网络(DQN)

一、简介 深度Q网络&#xff08;DQN&#xff09;是一种结合了深度学习和强化学习的算法&#xff0c;它在近年来成为了人工智能领域的一个热点。DQN首次被引入是在2013年&#xff0c;由DeepMind的研究人员开发。它标志着深度学习技术在解决高维度决策问题上的一大突破。 DQN的…

物联网实战--驱动篇之(一)EEPROM存储器(AT24C64)

目录 一、驱动概述 二、AT24C64简介 三、驱动编写 四、驱动应用 一、驱动概述 这是驱动篇的第一篇&#xff0c;所以先说明下驱动篇的作用和书写计划。之前的净化器项目已有提及&#xff0c;向ESP8266、SHT30这些都属于驱动设备&#xff0c;主芯片STM32是核心&#xff0c;相…

使用阿里云服务器搭建公司官网,需要多少钱?

阿里云服务器租用费用&#xff0c;搭建公司官网多少钱一年&#xff1f;搭建公司官网推荐2核4G5M带宽&#xff0c;优惠价199元一年&#xff0c;ECS u1实例企业客户专享&#xff0c;2核4G&#xff0c;5M固定带宽&#xff0c;80G ESSD Entry盘&#xff0c;活动页面 aliyunfuwuqi.c…

小坤二次元导航HTML源码

源码介绍 小坤二次元导航HTML源码&#xff0c;很好看的一个htmlの引导页/导航页&#xff01;需要的上&#xff01; 源码下载 小坤二次元导航HTML源码

『VUE』12. computed计算属性的使用 提高性能(详细图文注释)

目录 方法作为类似数据变量的写法处理数据返回的方法,优化性能使用computed例子总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 方法作为类似数据变量的写法 注意到了,方法没有return也可以以类似前面的数据变量的写法在模板…

.NET 设计模式—建造者模式(Builder Pattern)

简介 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 建造者模式的核心思想就是将一个复杂对象的构建与其表示分离&#xff0c;让用户只…

【SCI绘图】【曲线图系列2 python】多类别标签对比的曲线图

SCI&#xff0c;CCF&#xff0c;EI及核心期刊绘图宝典&#xff0c;爆款持续更新&#xff0c;助力科研&#xff01; 本期分享&#xff1a; 【SCI绘图】【曲线图系列2 python】多类别标签对比的曲线图&#xff0c;文末附完整代码。 1.环境准备 python 3 import proplot as pp…

wordpress全站开发指南-面向开发者及深度用户(全中文实操)--创建新主题

前言 你可以在wordpress里面下载使用人家打包好的主题&#xff0c;但可能不是很好用&#xff0c;接下来就自己做一个自己的主题。你需要先找到xampp文件夹–htdocs–wordpress(我给更名为wplocal)–wp-content–themes 进入该文件夹之后你可以看到你之前下载导入的所有主题文件…