首页 > 其他分享 >vue3记录

vue3记录

时间:2024-08-20 16:39:26浏览次数:13  
标签:vue name 记录 let vue3 组件 import ref

vue相关配置

1、scss配置

// 安装命令
npm install sass-loader sass -D

// vite.config.js文件
  css: {
    // css预处理器
    preprocessorOptions: {
      scss: {
        // 引入 mixin.scss 这样就可以在全局中使用 mixin.scss中预定义的变量了
        // 给导入的路径最后加上 ; 
        additionalData: '@import "@/assets/style/mixin.scss";'
      }
    }
  }

1 、Vue3安装

1.1. 【基于 vue-cli 创建】

点击查看官方文档

备注:目前vue-cli已处于维护模式,官方推荐基于 Vite 创建项目。

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version

## 安装或者升级你的@vue/cli 
npm install -g @vue/cli

## 执行创建命令
vue create vue_test

##  随后选择3.x
##  Choose a version of Vue.js that you want to start the project with (Use arrow keys)
##  > 3.x
##    2.x

## 启动
cd vue_test
npm run serve

1.2 【基于 vite 创建】(推荐)

vite 是新一代前端构建工具,官网地址:https://vitejs.cnvite的优势如下:

  • 轻量快速的热重载(HMR),能实现极速的服务启动。

  • TypeScriptJSXCSS 等支持开箱即用。

  • 真正的按需编译,不再等待整个应用编译完成。

  • webpack构建 与 vite构建对比图如下:

    具体操作如下(点击查看官方文档

## 1.创建命令
npm create vue@latest

## 2.具体配置
## 配置项目名称
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript?  Yes
## 是否添加JSX支持
√ Add JSX Support?  No
## 是否添加路由环境
√ Add Vue Router for Single Page Application development?  No
## 是否添加pinia环境
√ Add Pinia for state management?  No
## 是否添加单元测试
√ Add Vitest for Unit Testing?  No
## 是否添加端到端测试方案
√ Add an End-to-End Testing Solution? » No
## 是否添加ESLint语法检查
√ Add ESLint for code quality?  Yes
## 是否添加Prettiert代码格式化
√ Add Prettier for code formatting?  No

1.3 代码片段

{
    // Place your 全局 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and 
    // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope 
    // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is 
    // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: 
    // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. 
    // Placeholders with the same ids are connected.
    // Example:
    "Print to console": {
        "prefix": "vue3",
        "body": [
          "<template>",
          "  <div class=\"$TM_FILENAME\">",
          "    <p> 这是$TM_FILENAME页面$0 </p>",
          "  </div>",
          "</template>",
          "",
          "<script setup name=\"$TM_FILENAME\">",
         	"import { ref,reactive,onMounted } from 'vue';",
			"",
			"",
			"",
			"",
          "</script>",
          "",
          "<style lang='scss' scoped>",
          ".$TM_FILENAME{}",
          "</style>",
          ""
        ],
        "description": "Log output to console"
    }
}


2、Vue3核心语法

2.1、vue3模板

1 . 基础模板

<template>
  <div class="app">
    <h1>你好啊!</h1>
    <div @click="changeName">{{name}}</div>
  </div>
</template>

<script lang="ts">
  export default {
      name:'App', //组件名
      setup(){
		    let name = '张三'
      		let age = 18
      		let tel = '13888888888'
             function changeName(){
                name = 'zhang-san' //注意:此时这么修改name页面是不变化的
                console.log(name)
             }
         	return {name,age,tel,changeName}
      }
  }
</script>

<style>
  .app {
    background-color: #ddd;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
</style>

2. 语法糖

2.1 . 不带name

<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <button @click="changName">修改名字</button>
  </div>
</template>

<!-- 下面的写法是setup语法糖 -->
<script setup lang="ts">
  let name = '张三'
  let age = 18
  let tel = '13888888888'

  // 方法
  function changName(){
    name = '李四'//注意:此时这么修改name页面是不变化的
  }

</script>

2.2 带name

// 1、安装插件
npm i vite-plugin-vue-setup-extend -D

// 2、注册插件
vite.config.ts页面
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
  plugins: [
    vue(),
    VueSetupExtend() //这里注册
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})


2.2、创建响应式

1. 【ref 与 reactive】

1.1 ref 创建

<script setup lang="ts" name="Person">
    // ref可以创建基本数据也可以创建复杂数据
	import { ref } from 'vue'  //引入
     let name = ref('张三')  //创建
     name.value = '李四'  //修改必须通过value
    
    let obj = ref({name:"张三",age:18})
    obj.value.name = '李四'
    
    //  vscode插件 Vue-Official 配置中 Vue › Auto Insert: Dot Value 可以自动补充value
</script>

1.2 reactive 创建

<script setup lang="ts" name="Person">
    // reactive 只可以创建复杂数据,不可重新创建
	import { reactive } from 'vue'  //引入
    let obj = reactive({name:"张三",age:18})
    obj.name = '李四'  //修改不用通过value

	//不可重新创建
    obj = reactive({name:"张三",age:18}) //不生效
    
    Object.assign(obj,{name:"李四"})  //可以用 `Object.assign` 修改
</script>

1.3 使用规则

宏观角度看:

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

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

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

    自动补充value
  2. reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。

  • 使用原则:
  1. 若需要一个基本类型的响应式数据,必须使用ref
  2. 若需要一个响应式对象,层级不深,refreactive都可以。
  3. 若需要一个响应式对象,且层级较深,推荐使用reactive

2. 【toRefs 与 toRef】

  • 结构赋值不是响应式,需要通过 toRefs 设置响应式
 let { name, age } = toRefs(person)
<script setup lang="ts" name="Person">
 import {ref,reactive,toRefs,toRef} from 'vue'
    
 let person = reactive({name:'张三', age:18, gender:'男'})
 let { name, age } = toRefs(person) 
 // 把person的属性变成响应式,toRefs把所有对象的属性变成响应式
 name.value = '李四' //这样修改数据person.name也会修改变成响应式
 
  let ceshi = toRef(person,"name")  // 结构出person的name变成响应式
 //指定某个属性变成响应式
  ceshi.value = '李四'
  //这样修改数据person.name也会修改变成响应式
</script>

2.3、 计算属性( computed )

1、计算属性

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName}}</span> <br>
    <button @click="changeFullName">全名改为:li-si</button>
  </div>
</template>
<script setup lang="ts" name="App">
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  // 计算属性——只读取,不修改
  let fullName = computed(()=>{
    return firstName.value + '-' + lastName.value
  }) 

</script>

2、修改计算属性

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName}}</span> <br>
    <button @click="changeFullName">全名改为:li-si</button>
  </div>
</template>

<script setup lang="ts" name="App">
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  // 计算属性——既读取又修改
  let fullName = computed({
    // 读取
    get(){
      return firstName.value + '-' + lastName.value
    },
    // 修改
    set(val){
      console.log('有人修改了fullName',val)
      firstName.value = val.split('-')[0]
      lastName.value = val.split('-')[1]
    }
  })

  function changeFullName(){
    fullName.value = 'li-si'
  } 
</script>

2.4、监听 ( watach )

1、watach 监听

基础设置

<script lang="ts" setup name="Person">
	import {watch} from 'vue'
    watch(监听值,(newValue,oldValue)=>{
        // newValue改变后的值,oldValue改变前的值
    },{
        deep:true, //深度监听
        immediate:true //立即监听,在侦听器创建时立即触发回调
    })
</script>

1. 监听ref【基本类型】

<script lang="ts" setup name="Person">
  import {ref,watch} from 'vue'

  let sum = ref(0)
  function changeSum(){
    sum.value += 1
  }
  // 监视,情况一:监视【ref】定义的【基本类型】数据
  const stopWatch = watch(sum,(newValue,oldValue)=>{
    console.log('sum变化了',newValue,oldValue)
    if(newValue >= 10){
      stopWatch()  //停止监听
    }
  })
  
  //可以同时监听多个值
  let name1 = ref("测试1")
  let name2 = ref("测试2")
   watch([name1,name2],(newValue,oldValue)=>{
    console.log('sum变化了',newValue,oldValue)
  })
</script>

2. 监听ref对象

<script lang="ts" setup name="Person">
  import {ref,watch} from 'vue'

  let sum = ref({name:"张三"})
  function changeSum(){
    sum.value.name = "李四"
  }
  // 监视,深度监听ref与reactive对象
  watch(sum,(newValue,oldValue)=>{
    console.log('sum变化了',newValue,oldValue)
  },{deep:true})
</script>

3. 监听 reactive 对象

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'

  let sum = reactive({name:"张三"})
  function changeSum(){
    sum.name = '李四'
  }
  // 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的
  watch(sum,(newValue,oldValue)=>{
    console.log('sum变化了',newValue,oldValue)
  })
</script>

4. 监听对象指定属性

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'
    
  let person = reactive({
    name:'张三',
    age:18,
    car:{
      c1:'奔驰',
      c2:'宝马'
    }
  })
   function changeName(){
    person.name += '~'
  }
 
 // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
  watch(()=>person.name,(newValue,oldValue)=>{
    console.log('person.car变化了',newValue,oldValue)
  })
 // 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
  watch(()=>person.car,(newValue,oldValue)=>{
    console.log('person.car变化了',newValue,oldValue)
  },{deep:true})
</script>

5. 监听多个数据

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'
    
  let person = reactive({
    name:'张三',
    age:18,
    car:{
      c1:'奔驰',
      c2:'宝马'
    }
  })
   function changeName(){
    person.name += '~'
  }
 function changeCar(){
    person.car = {c1:'雅迪',c2:'爱玛'}
  }
  // 监视,情况五:监视上述的多个数据
  watch([()=>person.name,person.car],(newValue,oldValue)=>{
    console.log('person.car变化了',newValue,oldValue)
  },{deep:true})
    
  watch([()=>person.name,()=>person.car.c1],(newValue,oldValue)=>{
    console.log('person.car变化了',newValue,oldValue)
  },{deep:true})
</script>

2、watchEffect 监听

  • 官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。

  • watch对比watchEffect

    1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同

    2. watch:要明确指出监视的数据

    3. watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。

<script lang="ts" setup name="Person">
    import {ref,watch,watchEffect} from 'vue'
  
    // 用watchEffect实现,不用
    const stopWtach = watchEffect(()=>{
      if(temp.value >= 50 || height.value >= 20){
      	// 自动监听使用的属性
      }
      if(temp.value === 100 || height.value === 50){
        stopWtach()
          // 清除监听
      }
    })
 </script>

2.5、标签的 ref

1、普通的 dom 标签上

<template>
  <div class="person">
    <h1 ref="title1">尚硅谷</h1>
    <button @click="showLog">点我打印内容</button>
  </div>
</template>
<script lang="ts" setup name="Person">
     import { ref } from 'vue'
     let title1 = ref() //定义的名字必须和标签上的ref一样
       function showLog(){
           console.log(title1.value)
       }
</script>

2、用在组件标签上

<!-- 父组件App.vue -->
<template>
  <Person ref="ren"/>
  <button @click="test">测试</button>
</template>
<script lang="ts" setup name="App">
  import Person from './components/Person.vue'
  import { ref } from 'vue'

  let ren = ref()

  function test(){
    console.log(ren.value.name)
    console.log(ren.value.age)
  }
</script>
<!-- 子组件Person.vue中要使用defineExpose暴露内容 -->
<script lang="ts" setup name="Person">
  import {ref,defineExpose} from 'vue'
	// 数据
  let name = ref('张三')
  let age = ref(18)
  /****************************/
// 父组件想要访问子组件的数据,需要通过defineExpose暴漏出去
  // 使用defineExpose将组件中的数据交给外部
  defineExpose({name,age})
</script>

2.6、生命周期函数

<script lang="ts" setup name="Person">
  import {onBeforeMount,onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted  } from 'vue'

	console.log('setup',"创建")
  // 生命周期钩子
  onBeforeMount(()=>{
    console.log('挂载之前,直接执行')
  })
  onMounted(()=>{
    console.log('挂载完毕,直接执行')
  })
  onBeforeUpdate(()=>{
    console.log('更新之前,数据更新前,不会直接执行')
  })
  onUpdated(()=>{
    console.log('更新完毕,数据更新后,不会直接执行')
  })
  onBeforeUnmount(()=>{
    console.log('卸载之前')
  })
  onUnmounted(()=>{
    console.log('卸载完毕')
  })
</script>

2.7、自定义hook

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin
  • 自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂。

父类

<template>
    <div class="person">
        <h1 ref="title1">{{comds}}</h1>
        <button @click="handleClick">++</button>
    </div>
</template>
<script lang="js" setup name="Person">
import { ref } from "vue";
import useSum from "./js/index"; //引入
let { comds, handleClick } = useSum();

</script>

./js/index

import { ref, onMounted } from 'vue'
export default () => {
    let comds = ref(0)

    const handleClick = () => {
        comds.value += 1
    }
    //向外部暴露数据
    return { comds, handleClick }
}	

3、Vue-router 路由

3.1、安装命令

官方地址

## 安装命令
npm install vue-router@4

3.2、创建vue-router

  1. 创建router文件
import {createRouter,createWebHistory} from 'vue-router'

const router = createRouter({
	history:createWebHistory(),
	routes:[
		{
             name:'preson',
			path:'/preson',
             component: () => import('@/views/preson.vue'),
		},
        // 重定向
        {
            path:'/',
            redirect:'/preson'
        }
	]
})
export default router
  1. 注册router
  • 在main.js里面
import { createApp } from 'vue'
import App from './App.vue'
import router from '@/router/index'


const app = createApp(App)
app.use(router)
app.mount('#app')
  1. App.vue
<template>
  <div class="app">
    <h2 class="title">Vue路由测试</h2>
    <!-- 导航区 -->
    <div class="navigate">
      <RouterLink to="/home" active-class="active">首页</RouterLink>
      <RouterLink to="/news" active-class="active">新闻</RouterLink>
      <RouterLink to="/about" active-class="active">关于</RouterLink>
    </div>
    <!-- 展示区 -->
    <div class="main-content">
      <RouterView></RouterView>
    </div>
  </div>
</template>

<script lang="ts" setup name="App">
  import {RouterLink,RouterView} from 'vue-router'  
</script>

3.3、路由模式

  1. history模式

    读法:黑斯睿

    优点:URL更加美观,不带有#,更接近传统的网站URL

    缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有404错误。

    import { createRouter,createWebHistory } from 'vue-router'
    const router = createRouter({
        history:createWebHistory(), //history模式
        /******/
    })
    
  2. hash模式

    读法:哈希

    优点:兼容性更好,因为不需要服务器端处理路径。

    缺点:URL带有#不太美观,且在SEO优化方面相对较差。

    import { createRouter,createWebHashHistory } from 'vue-router'
    const router = createRouter({
    	history:createWebHashHistory(), //hash模式
    	/******/
    })
    

3.4、嵌套路由

  1. 编写News的子路由:Detail.vue

  2. 配置路由规则,使用children配置项:

  3. 嵌套路由的子路由不需要加 /

    const router = createRouter({
      history:createWebHistory(),
    	routes:[
    		{
    			name:'zhuye',
    			path:'/home',
    			component: () => import('@/views/home.vue'),
    		},
    		{
    			name:'xinwen',
    			path:'/news',
    			component:component: () => import('@/views/news.vue'),,
    			children:[
    				{
    					name:'xiang',
    					path:'detail', 
    					component: () => import('@/views/detail.vue'),
    				}
    			]
    		},
    	]
    })
    export default router
    

3.5、路由跳转

1. 标签内跳转

  • 跳转方式 $router.push
$router.push({path: '/preson',query: {a:1}})
$router.push({name: 'preson',query: {a:1}})
<a @click="$router.push({name: 'preson',query: {a:1},})">首页</a>
  • 接受参数

    import { useRoute } from 'vue-router'
    const route = useRoute()
    route.query.a //接受参数
    

2. 函数跳转

  • 跳转方式 useRouter

    import { useRouter, useRoute } from 'vue-router'
    let router = useRouter()  // 跳转
    // 方式一
    router.push({ name: 'preson',query: {a:'11'}})  
    // 方式二
    router.push({ path: '/preson',query: {a:'11'}})
    
  • 接受参数

import { useRoute } from 'vue-router'
const route = useRoute()
route.query.a //接受参数

3.6、replace 属性

1. 作用:控制路由跳转时操作浏览器历史记录的模式。

2. 浏览器的历史记录有两种写入方式:分别为```push```和```replace```:

   - ```push```是追加历史记录(默认值)。
   - `replace`是替换当前记录。

3. 开启`replace`模式:
import { useRouter } from 'vue-router'
let router = useRouter()  // 跳转
router.push({
        name: 'preson',
        replace:true, //关闭记录
        query: {
            a:'11'
        },
    })

4、pinia 数据管理

4.1、搭建 pinia 环境

  • 读法:皮尼耶
  1. 第一步

    ## 安装命令
    npm install pinia
    
  2. 第二步:操作src/main.ts

    import { createApp } from 'vue'
    import App from './App.vue'
    
    /* 引入createPinia,用于创建pinia */
    import { createPinia } from 'pinia'
    
    /* 创建pinia */
    const pinia = createPinia()
    const app = createApp(App)
    
    /* 使用插件 */
    app.use(pinia)
    app.mount('#app')
    

4.2 、存储+读取数据

1、存储数据

  1. Store是一个保存:状态业务逻辑 的实体,每个组件都可以读取写入它。
  2. 它有三个概念:stategetteraction,相当于组件中的: datacomputedmethods
  3. 命名规范:usexxxxStore
  4. 具体编码:src/store/count.ts
// 引入defineStore用于创建store
import { defineStore } from 'pinia'

// 定义并暴露一个store
export const useCountStore = defineStore('count',{
  // 数据
  state(){
    return {
      sum:6
    }
  },
  // 函数
  actions:{},
  // 计算
  getters:{}
})

2、读取

组件中使用state中的数据

1) 读取方式一

<template>
  <h2>当前求和为:{{ sumStore.sum }}</h2>
</template>

<script setup lang="ts" name="Count">
  // 引入对应的useXxxxxStore	
  import { useCountStore } from '@/store/count'
  
  // 调用useXxxxxStore得到对应的store
  const sumStore = useCountStore()
 
</script>

2) 读取方式二

<template>
	<div class="count">
		<h2>当前求和为:{{sum}}</h2>
	</div>
</template>

<script setup lang="ts" name="Count">
  import { useCountStore } from '@/store/count'
  /* 引入storeToRefs */
  import { storeToRefs } from 'pinia'
    //`storeToRefs`
	/* 得到countStore */
  const countStore = useCountStore()
  /* 使用storeToRefs转换countStore,随后解构 */
  const {sum} = storeToRefs(countStore)
</script>

4.3、【修改数据】(三种方式)

  1. 第一种修改方式,直接修改

    <template>
      <h2>当前求和为:{{ sumStore.sum }}</h2>
    </template>
    
    <script setup lang="ts" name="Count">
    
      import { useCountStore } from '@/store/count'
      const sumStore = useCountStore()
      
      // 拿到数据可以直接修改数据,  
      sumStore.sum++ 
      
    </script>
    
  2. 第二种修改方式:批量修改

    <template>
      <h2>当前求和为:{{ sumStore.sum }}</h2>
    </template>
    
    <script setup lang="ts" name="Count">
    
      import { useCountStore } from '@/store/count'
      const sumStore = useCountStore()
      
      // 可以设置多个值同时修改 
      countStore.$patch({
          sum:999,
          school:'atguigu'
      })
      
    </script>
    
  3. 第三种修改方式:借助action修改(action中可以编写一些业务逻辑)

    • src/store/count.ts

      import { defineStore } from 'pinia'
      
      export const useCountStore = defineStore('count', {
       // 函数   
        actions: { 
          increment(value) {
              // 这里this可以直接访问state定义的数据
              this.sum += value
          },
        },
        // 数据
        state(){
          return {
            sum:6
          }
        },
      })
      
    • 组件中调用action即可

      <template>
        <h2>当前求和为:{{ sumStore.sum }}</h2>
      </template>
      
      <script setup lang="ts" name="Count">
      
        import { useCountStore } from '@/store/count'
        const sumStore = useCountStore()
        
       sumStore.increment(1)
        
      </script>
      

4.4、计算属性

// 引入defineStore用于创建store
import {defineStore} from 'pinia'

// 定义并暴露一个store
export const useCountStore = defineStore('count',{
  // 状态
  state(){
    return {
      sum:1,
      school:'atguigu'
    }
  },
   // 动作
  actions:{},
  // 计算
  getters:{
    bigSum:(state):number => state.sum *10,
    upperSchool():string{
      return this. school.toUpperCase()
    }
  }
})

4.5 、监听值的修改

通过 store 的 $subscribe() 方法侦听 state 及其变化

<template>
	<div class="count">
		<h2>当前求和为:{{sum}}</h2>
	</div>
</template>

<script setup lang="ts" name="Count">
  import { useCountStore } from '@/store/count'
  /* 引入storeToRefs */
  import { storeToRefs } from 'pinia'

	/* 得到countStore */
  const countStore = useCountStore()
  /* 使用storeToRefs转换countStore,随后解构 */
  const {sum} = storeToRefs(countStore)
  countStore.$subscribe((mutate,state)=>{
      console.log('LoveTalk',mutate,state)
  })
</script>


4.6、【store组合式写法】

import {defineStore} from 'pinia'
import axios from 'axios'
import {nanoid} from 'nanoid'
import {reactive} from 'vue'

export const useTalkStore = defineStore('talk',()=>{
  // talkList就是state
  const talkList = reactive(
    JSON.parse(localStorage.getItem('talkList') as string) || []
  )

  // getATalk函数相当于action
  async function getATalk(){
    // 发请求,下面这行的写法是:连续解构赋值+重命名
    let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
    // 把请求回来的字符串,包装成一个对象
    let obj = {id:nanoid(),title}
    // 放到数组中
    talkList.unshift(obj)
  }
  return {talkList,getATalk}
})

5、组件通信

5.0、常见搭配形式:

传参方式 组件关系

父传子
1. props
2. v-model
3. $refs <br / >4. 默认插槽、具名插槽


子传父
1. props
2. 自定义事件
3. v-model
4. $parent <br / >4. 作用域插槽
祖传孙、孙传祖 1. $attrs
2. provide 、inject
兄弟间、任意组件之间 1. mitt
2. pinia

5.1、常见属性

1.1 $event

 $event
 <div @click="changNan($event)"></div>

1.2、defineProps

// 1.接受父组件传的参数
defineProps(['car','getToy'])  
let { name } =  defineProps(["name"])

1.3、defineEmits

// 1. 声明事件,向父组件发送事件
defineEmits(['send-toy'])
const emit =  defineEmits(['send-toy'])
emit('send-toy',"传参") //向父组件传参

//父组件接受事件
<Child @send-toy="saveToy"/>
function saveToy(value){
    console.log("接受子组件传的参数",value)
}

1.4、defineExpose

  • 不管是父组件要访问子组件还是子组件要访问父组件,都需要defineExpose向外暴露出去
<script setup lang="ts" name="Child2">
    import { ref,defineExpose } from "vue";
    // 数据
    let computer = ref('联想')
    let book = ref(6)
    // 把数据交给外部
    defineExpose({computer,book})
</script>

5.2、【props】

概述:props是使用频率最高的一种通信方式,常用与 :父 ↔ 子

  • 父传子:属性值是非函数
  • 子传父:属性值是函数

父组件

<template>
  <div class="father">
    <h3>父组件,</h3>
    <h4>我的车:{{ car }}</h4>
    <h4>儿子给的玩具:{{ toy }}</h4>
    <Child :car="car" :getToy="getToy"/>
  </div>
</template>

<script setup lang="ts" name="Father">
	import Child from './Child.vue'
	import { ref } from "vue";
	// 数据
	const car = ref('奔驰') // 向父组件传的参数
	const toy = ref()
	// 方法 子组件给父组件传的参数
	function getToy(value){
		toy.value = value
	}
</script>

子组件

<template>
  <div class="child">
    <h3>子组件</h3>
    <h4>我的玩具:{{ toy }}</h4>
    <h4>父给我的车:{{ car }}</h4>
    <button @click="getToy(toy)">向父组件传参</button>
  </div>
</template>

<script setup lang="ts" name="Child">
	import { ref } from "vue";
	const toy = ref('奥特曼')
	
    // 接受父组件传的参数
	defineProps(['car','getToy'])  
</script>

5.3、【自定义事件】

  1. 概述:自定义事件常用于:子 => 父。

父组件

<!--在父组件中,给子组件绑定自定义事件:-->
<Child @send-toy="sendtoy"/>

<script setup >
    function sendtoy(value){
        console.log("接受子组件的传参",value)
    }
</script>

子组件

<script setup name="ceshi1">
    
    let emit = defineEmits(["canshuju"])
    emit("canshuju","向父组件传递数据")
</script>

5.4、 【mitt】

概述:可以实现任意组件传参,与消息订阅与发布(pubsub)功能类似,可以实现任意组件间通信。

【第一步】:安装mitt

npm i mitt

【第二步】:新建文件:src\utils\emitter.ts

// 引入mitt 
import mitt from "mitt";

// 创建emitter
const emitter = mitt()

// 创建并暴露mitt
export default emitter

emitter.on("绑定事件",()=>{})


【第三步】:接收数据的组件中:绑定事件、同时在销毁前解绑事件:

import emitter from "@/utils/emitter";
import { onUnmounted } from "vue";

// 绑定事件
emitter.on('send-toy',(value)=>{
  console.log('接受参数',value)
})

onUnmounted(()=>{
  // 解绑事件
  emitter.off('send-toy')
})

【第四步】:提供数据的组件,在合适的时候触发事件

import emitter from "@/utils/emitter";

function sendToy(){
  // 触发事件
  emitter.emit('send-toy',"传参")
}

5.5、【v-model】

  1. 概述:实现 父↔子 之间相互通信。

  2. 前序知识 —— v-model的本质

    <!-- 使用v-model指令 -->
    <input type="text" v-model="userName">
    
    <!-- v-model的本质是下面这行代码 -->
    <input 
      type="text" 
      :value="userName" 
      @input="userName =(<HTMLInputElement>$event.target).value"
    >
    
  3. 组件标签上的v-model的本质::moldeValueupdate:modelValue事件。

    <!-- 组件标签上使用v-model指令 -->
    <AtguiguInput v-model="userName"/>
    
    <!-- 组件标签上v-model的本质 -->
    <AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>
    

    AtguiguInput组件中:

    <template>
      <div class="box">
        <!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->
    		<!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->
        <input 
           type="text" 
           :value="modelValue" 
           @input="emit('update:model-value',$event.target.value)"
        >
      </div>
    </template>
    
    <script setup lang="ts" name="AtguiguInput">
      // 接收props
      defineProps(['modelValue'])
      // 声明事件
      const emit = defineEmits(['update:model-value'])
    </script>
    
  4. 也可以更换value,例如改成abc

    <!-- 也可以更换value,例如改成abc-->
    <AtguiguInput v-model:abc="userName"/>
    
    <!-- 上面代码的本质如下 -->
    <AtguiguInput :abc="userName" @update:abc="userName = $event"/>
    

    AtguiguInput组件中:

    <template>
      <div class="box">
        <input 
           type="text" 
           :value="abc" 
           @input="emit('update:abc',$event.target.value)"
        >
      </div>
    </template>
    
    <script setup lang="ts" name="AtguiguInput">
      // 接收props
      defineProps(['abc'])
      // 声明事件
      const emit = defineEmits(['update:abc'])
    </script>
    
  5. 如果value可以更换,那么就可以在组件标签上多次使用v-model

    <AtguiguInput v-model:abc="userName" v-model:xyz="password"/>
    

5.6、【$attrs 】

  1. 概述:$attrs用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。

  2. 具体说明:$attrs是一个对象,包含所有父组件传入的标签属性。

    注意:$attrs会自动排除props中声明的属性(可以认为声明过的 props 被子组件自己“消费”了)

父组件:

<template>
  <div class="father">
    <h3>父组件</h3>
		<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>
  </div>
</template>

<script setup lang="ts" name="Father">
	import Child from './Child.vue'
	import { ref } from "vue";
	let a = ref(1)
	let b = ref(2)
	let c = ref(3)
	let d = ref(4)

	function updateA(value){
		a.value = value
	}
</script>

子组件:

<template>
	<div class="child">
		<h3>子组件</h3>
		<GrandChild v-bind="$attrs"/>
	</div>
</template>

<script setup lang="ts" name="Child">
	import GrandChild from './GrandChild.vue'
</script>

孙组件:

<template>
	<div class="grand-child">
		<h3>孙组件</h3>
		<h4>a:{{ a }}</h4>
		<h4>b:{{ b }}</h4>
		<h4>c:{{ c }}</h4>
		<h4>d:{{ d }}</h4>
		<h4>x:{{ x }}</h4>
		<h4>y:{{ y }}</h4>
		<button @click="updateA(666)">点我更新A</button>
	</div>
</template>

<script setup lang="ts" name="GrandChild">
	defineProps(['a','b','c','d','x','y','updateA'])
</script>

5.7、【$refs、$parent】

  1. 概述:

    • $refs用于 :父→子。
    • $parent用于:子→父。
  2. 原理如下:

    属性 说明
    $refs 值为对象,包含所有被ref属性标识的DOM元素或组件实例。
    $parent 值为对象,当前组件的父组件实例对象。
  • 不管是父组件要访问子组件还是子组件要访问父组件,都需要defineExpose向外暴露出去

$refs 访问子组件

<template>
  <div class="index">
    <button @click="SubComponents($refs)">打印所有子组件实例</button>
    <ceshi1  ref="c1"></ceshi1>
    <ceshi2 ref="c2"></ceshi2>
  </div>
</template>
<script lang="ts" setup name="Person">
    import { ref,reactive,onMounted ,defineExpose} from 'vue';
	import ceshi1 from './components/ceshi1.vue';
	import ceshi2 from './components/ceshi2.vue';
    
     // 1、访问所有子组件:通过标签事件可以访问所有子组件
     const SubComponents = (refs)=>{
         console.log(refs)
     }
     // 2、 单独访问某个子组件: 命名与要和标签上的ref一致
     let c1 = ref()
     let c2 = ref()
     // 需要在生命周期函数访问
     onMounted(()=>{
         console.log(c1,c2,"需要在生命周期函数访问")
     })
     
</script>

$parent 访问父组件

<template>
    <div class="ceshi1">
        <p> {{name}} </p>
        <button @click="ParentComponent($parent)"> </button>
        <!-- 需要在标签内传入 -->
    </div>
</template>

<script setup name="ceshi1.vue">
const ParentComponent = (parent)=>{
    console.log(parent)
    //接收访问父组件的实例
}
</script>

5.8、【provide、inject】

  1. 概述:实现祖孙组件直接通信

  2. 具体使用:

    • 在祖先组件中通过provide配置向后代组件提供数据

    • import { provide } from "vue";
      
    • 在后代组件中通过inject配置来声明接收数据

    •  import { inject } from 'vue';
      

【第一步】父组件中,使用provide提供数据

<template>
  <div class="father">
    <h4>资产:{{ money }}</h4>
    <Child/>
  </div>
</template>

<script setup lang="ts" name="Father">
  import Child from './Child.vue'
  import { ref,reactive,provide } from "vue";
  // 数据
  let money = ref(100)

  // 向后代提供数据
  provide('money',money)
  provide('自定义名称',money)
    
  // 向后代提供函数,用与接受子组件的传参
  const updateMoney = (value)=>{
      money.value+= value
  }
  provide("updateMoney",updateMoney)
  //同时可以传多个参数
  provide('money',{money,updateMoney})
</script>

【第二步】孙组件中使用inject配置项接受数据。

<template>
  <div class="grand-child">
    <h4>资产:{{ money }}</h4>
    <button @click="changMoney(6)">点我</button>
  </div>
</template>

<script setup lang="ts" name="GrandChild">
  import { inject } from 'vue';
  // 注入数据
    // inject("接受父组件定义的名称",设置默认值)
 let money = inject('money',0)
 let { money,updateMoney } = inject('updateMoney',{money,updateMoney)
// 调用父组件的函数并传值
const changMoney = ()=>{
     updateMoney(6)
 }
 
</script>

5.9、【pinia】

参考之前pinia部分的讲解

5.10、插槽

1、默认插槽 slot

  • 父组件

    <Category title="今日热门游戏">
         <span>我是插槽</span>
    </Category>
    
  • 子组件

 <template>
      <div class="item">
        <slot></slot>
      </div>
</template>

2、具名插槽

  • 父组件

    <Category title="今日热门游戏">
    	<span  v-slot:c1>我是插槽c1</span>
    <span  v-slot:c2>我是插槽c2</span>
    </Category>
    
  • 子组件

<template>
   <div class="item">
     	<slot name="c1"></slot>
        <slot name="c2"></slot>
   </div>
</template>

3、

  1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(新闻数据在News组件中,但使用数据所遍历出来的结构由App组件决定)
  • 父组件

      <Game v-slot="params">
             <!-- <Game v-slot:default="params"> -->
             <!-- <Game #default="params"> -->
           <ul>
             <li v-for="g in params.games" :key="g.id">{{ g.name }}</li>
           </ul>
     </Game>
    
  • 子组件

 <template>
        <div class="category">
              <h2>今日游戏榜单</h2>
              <slot :games="games" a="哈哈"></slot>
        </div>
  </template>

  <script setup lang="ts" name="Category">
        import {reactive} from 'vue'
        let games = reactive([
          {id:'asgdytsa01',name:'英雄联盟'},
          {id:'asgdytsa02',name:'王者荣耀'},
          {id:'asgdytsa03',name:'红色警戒'},
          {id:'asgdytsa04',name:'斗罗大陆'}
        ])
  </script>

6、其它 API

6.1、【shallowRef 与 shallowReactive 】

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

  2. 用法:

    let myVar = shallowRef('00');
    
  3. 特点:只跟踪引用值的变化,不关心值内部的属性变化。

shallowReactive

  1. 作用:创建一个浅层响应式对象,只会使对象的最顶层属性变成响应式的,对象内部的嵌套属性则不会变成响应式的

  2. 用法:

    const myObj = shallowReactive({ ... });
    
  3. 特点:对象的顶层属性是响应式的,但嵌套对象的属性不是。

总结

通过使用 shallowRef()shallowReactive() 来绕开深度响应。浅层式 API 创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。

7、Vue3新组件

7.1. 【Teleport】

  • 什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。
<teleport to='body' >
    <div class="modal" v-show="isShow">
      <h2>我是一个弹窗</h2>
      <p>我是弹窗中的一些内容</p>
      <button @click="isShow = false">关闭弹窗</button>
    </div>
</teleport>

标签:vue,name,记录,let,vue3,组件,import,ref
From: https://www.cnblogs.com/yuluochengxu/p/18369727

相关文章

  • 记录常用的一些样式
    //页面外层style.mPagewidth:100%height:100%font-size:28rpxbackground-color:#f7f7f7/*水平排列*/.x-rowdisplay:flexflex-direction:rowalign-items:center/*水平排列居中*/.x-row-centerdisplay:flex......
  • vue3一种page结构
    <template><view></view></template><scriptsetup>//引入生命周期,import{onLoad,onReady,onShareAppMessage,onShow}from"@dcloudio/uni-app";//引入计算属性import{computed,watch,getCurrentInstanc......
  • 记录一些好用的Docker项目(群晖上实测好用可用)
    拉取命令:开心版emby: emby是流媒体播放平台(类似爱奇艺,但是要自备视频资源)dockerpullamilys/embyserverPT专用qb: qb是种子下载器,全名:qbittorrent   官网:GitHub-qbittorrent/qBittorrent:qBittorrentBitTorrentclientdockerpullnevinee/qbittorrent日常......
  • mybatis多层嵌套 xml记录一下
     @DatapublicfinalclassWorldimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateintid=0;privatename=null;}@DatapublicfinalclassProvinceimplementsSerializable{privatestaticfinallong......
  • 创建uni-app项目(vue3+ts+vite)
     npxdegitdcloudio/uni-preset-vue#vite-tsm-uni-demo1跳转到对应目录,装包,运行cdm-uni-demo1yarnyarndev:h5tsconfig.json:{"extends":"@vue/tsconfig/tsconfig.json","compilerOptions":{"ignoreDeprecations&quo......
  • 隐秘之舞:探索聊天记录隐藏与密友构建的艺术境界
    在数字时代的洪流中,社交的经纬线编织出一幅幅错综复杂的人际图谱。在这片由0与1构建的虚拟世界里,聊天记录与密友关系,成为了我们情感交流、思想碰撞的重要载体。然而,随着信息量的激增与个人隐私需求的日益增强,隐藏聊天记录与构建密友圈,逐渐演变成了一门精致而深邃的艺术,它们不仅......
  • 综合靶场通关记录
    目录Authentication0x01通关记录暴力破解:Authentication0x02通关记录Authentication0x03通关记录IDOR通关记录文件包含0x01        文件包含漏洞:文件包含0x02文件包含0x03[挑战]Sql注入0x01Sql注入0x03注入0x04XSS0x01XSS0x02XSS0x03命令......
  • STM32学习记录-01-STM32简介
    1ARM2STM32F103C8T6系列:主流系列STM32F1内核:ARMCortex-M3主频:72MHzRAM:20K(SRAM)ROM:64K(Flash)供电:2.0~3.6V(标准3.3V)封装:LQFP483片上资源/外设4系统结构左上角为Cortex-M3内核,内核引出三条总线,分别是ICode指令总线、DCode数据总线、System系统总线,ICode与DCode主......
  • 学习vue3——分页(前、后端分页两种)
    一、前端分页 后端将所有数据给前端,前端来实现分页1<template>2<divclass="flexitems-centerjustify-centermt-5">3<el-pagination4background5v-model:current-page="currentPage1"6......
  • vue3 响应式 API:computed()
    介绍基本概念:computed()接收一个getter函数或者一个包含getter和setter函数的对象作为参数,并返回一个基于原始响应式数据计算得到的新的响应式数据对象。计算属性的值会根据其依赖的响应式数据自动更新,当依赖的数据发生变化时,计算属性的值也会自动重新计算。返......