vue 3 从零开始到掌握

news2025/4/17 7:25:50

vue3从零开始一篇文章带你学习

升级vue CLI

  1. 使用命令
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

nvm管理node版本,区分老旧项目

node版本升级,从卸载到使用nvm管理node版本并配置vue环境(学习趟雷版)

创建vue项目

vite创建vue项目传送门

组合式函数

利用vue组合式API来封装和复用有状态逻辑的函数
1.结合官网 使用鼠标跟踪器来实现组合式API,创建mouse.js 文件,将鼠标跟踪功能给提前出来

// mouse.js
import {
    ref,
    onMounted,
    onUnmounted
} from 'vue'

// 封装组合式函数
export function useMouse() {
    // 管理状态
    const x = ref(0)
    const y = ref(0)

    // 组合函数更改状态
    function updated(event) {
        x.value = event.pageX
        y.value = event.pageY
    }

    // 在生命周期商注册和卸载鼠标事件
    onMounted(
        () => {
            window.addEventListener('mousemove', updated)
        }
    )
    onUnmounted(
        () => {
            window.removeEventListener('mousemove', updated)
        }
    )
    //  将管理的状态值暴漏出去
    return {
        x,
        y
    }
}
  1. 在页面中使用
<template>
  <div class="container">
    坐标: {{ mouse.x }} , {{ mouse.y }}
  </div>
</template>
<!-- setup 语法糖 -->
<script setup>
import {
  useMouse
} from '@/utils/mouse'
import { reactive } from 'vue';
const mouse = reactive(useMouse())
// 或者使用此解构出来
// const {
//  x,
//  y
// } = useMouse()
</script>
  1. 将添加和清除DOM事件的逻辑也给抽离出来,并在mouse里面使用
  • event.js 文件

import {
    onMounted,
    onUnmounted
} from 'vue'

// 封装组合式函数
export function useEventListener(target, event, callback) {
    // 在生命周期商注册和卸载鼠标事件
    // 也可以用字符串形式的 CSS 选择器来寻找目标 DOM 元素
    onMounted(
        () => {
            target.addEventListener(event, callback)
        }
    )
    onUnmounted(
        () => {
            target.removeEventListener(event, callback)
        }
    )
}
  • mouse.js 引入使用

import {
    ref,
} from 'vue'
import { useEventListener } from './event'
// 封装组合式函数
export function useMouse() {
    // 管理状态
    const x = ref(0)
    const y = ref(0)

    // 组合函数更改状态
    function updated(event) {
        x.value = event.pageX
        y.value = event.pageY
    }

    useEventListener(window, 'mousemove', updated)
    //  将管理的状态值暴漏出去
    return {
        x,
        y
    }
}

vite项目

:vue3兼容vue2语法,vue2的语法可以在vue3项目内使用,vue3不能读取vue语法内的数据

API

setup组合式 API 的入口
  • setup在生命周期执行时间高于beforeCreate, created,在vue2语法内可以直接this调用setup里面的声明变量

  • setup里面的this不是vue实例,而是undefined ,在vue3中弱化了this的使用

  • 直接声明变量:let const var 的值非响应式数据,修改的话,页面也不会变化的

  • 返回值,可以是值,对象,函数,它会在组件被解析前被调用

  • setup 函数的参数
    setup 函数在组件创建created()之前执行,setup函数是一个函数,它也可以有返回值,类似于Vue 2中的data()和methods()的组合‌,接收两个参数:props 和 context

    • props: 是响应式的,当传入显端prop 时他会进行更新,包含父组件传递给子组件的所有数据
    • context: 是一个普通的js对象,上下文对象,暴漏setup中可用的值
选项式 API 和组合式 API

官方文档传送门

组合式 API

组合式 API 通常会与 <script setup> 搭配使用 vue3.0进行响应式数据操作会使用 ref、reactive、toRef、toRefs四个核心 API 函数

  • ref 通常将基本数据类型(string,Number,Boolean…)转为响应式,也可以将引用类型转为响应式,但引用类型内的数据修改就不会及时通知页面发生变化

  • 配置自动添加value在vscode设置
    在这里插入图片描述

    <template>
      <div>
        {{ mease }}
        <div @click="bntFrom">
            点击
        </div>
      </div>
    </template>
    
    <script lang="ts" setup>
    import {
        ref
    } from 'vue'
        let mease = ref('张三')
        引用类型
        let obj = ref({name: "真实的"})
        function bntFrom(){
            mease.value = '李四'
            obj.value.name = '虚假的'
            console.log('点击了');
        }
    </script>
    

在这里插入图片描述

  • reactive 只能定义引用类型(obj,arry),相比较ref可以改变深层次的属性响应式 ,ref也可以改变引用对象,但深层的数据还是建议使用reactive

    <template>
      <div>
        <h1>ref定义属性</h1>
        {{ mease }}
        <div @click="bntFrom">
            点击
        </div>
        <h1>reactive定义</h1>
        <p v-for="item in arry" :key="item.name">{{ item.age }}</p>
      </div>
    </template>
    
    <script lang="ts" setup>
    import {
        ref,
        reactive
    } from 'vue'
        let mease = ref('张三')
        
        let obj = ref({name: "真实的"})
        let arry = reactive([{
            name: '王二',
            age: 10
        }])
        function bntFrom(){
            mease.value = '李四'
            obj.value.name = '虚假的'
            arry[0].age = 30
            console.log('点击了');
        }
    </script>
    

    在这里插入图片描述

    • 将reactive定义的响应式重新定义一个对象,就会变成一个非响应式对象,可以使用Object.assign(obj,{name: ‘ssf’})
  • toRefs 将 reactive 定义的对象属性给解构赋值转换为响应式引用,保持对象的响应性,一般reactive 创建的对象,直接解构赋值后会失去响应式,所以就需要torefs将其给解构赋值成响应式

  • 如下面代码,将对象解构赋值直接使用,也可以通过对象使用,只要值改变都会发生改变

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

const parms = reactive({
  name: '掌声',
  count: 0
})
const {
  count
} = toRefs(parms)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <p>parms.count: {{ parms.count }}</p>
    <button type="button" @click="count++">count is {{ count }}</button>
  </div>
</template>

在这里插入图片描述

计算属性computed

  1. 基于缓存: 计算属性是基于它所依赖的响应式进行缓存的,只有当响应式依赖发生变化时,计算属性才会重新计算
  2. 声明式:计算属性根据返回值来定义,代码更清晰,更利于理解
  3. 自动更新:当依赖数据发生变化时,计算属性返回的值也会更新
<script setup lang="ts">
import { reactive, computed } from "vue";

// 响应式数据
const state = reactive({
  name: "手机",
  price: 2000,
});
// 计算属性
const productName = computed(() => {
  return `优惠 ${state.name}`;
});

const formattedPrice = computed(() => {
  return `${state.price.toFixed(2)}`;
});

// 方法
const increasePrice = () => {
  state.price += 100;
};
</script>

<template>
  <div>
    <p>商品名称:{{ productName }}</p>
    <p>商品价格:{{ formattedPrice }}</p>
    <button @click="increasePrice">增加价格</button>
  </div>
</template>

在这里插入图片描述
2. 计算属性默认是只读的,如果你想要更改计算属性的值时,你需要使用getter 和 setter 来创建

  • getter 进行计算,将结果缓存起来,当参与计算的响应数据发生变化时,会触发更新机制,再次调用getter来重新计算属性的值
  • setter 函数会接收一个函数值,即要修改的值
<script setup lang="ts">
import { reactive, computed } from "vue";

// 响应式数据
const state = reactive({
  name: "手机",
  price: 2000,
});
// 计算属性
const productName = computed(() => {
  return `优惠 ${state.name}`;
});

const formattedPrice = computed(() => {
  return `${state.price.toFixed(2)}`;
});

const formattedNamePrice = computed({
  // 当依赖发生变化时再次调用,返回新的值
  get(){
    return state.name + '-' + state.price.toFixed();

  },
  // 接收用户传值并进行修改
  set(newValue){
    const [
    name,
    price
    ] = newValue.split('-')
    state.name = name
    state.price = Number(price)
  }
  
});

// 方法
const increasePrice = () => {
  state.price += 100;
  formattedNamePrice.value = '苹果手机-5600'
};
</script>

<template>
  <div>
    <p>商品名称:{{ productName }}</p>
    <p>商品价格:{{ formattedPrice }}</p>
    <button @click="increasePrice">增加价格</button>
    <p>商品名称、价格:{{ formattedNamePrice }}</p>

  </div>
</template>

在这里插入图片描述

监听属性

  1. 非缓存:监听属性不会缓存其值,每次依赖发生变化时都会执行回调函数,但他有新值和旧值
  2. 灵活:可以监听一个或者多个数据源,当执行复杂逻辑时,可以进行异步操作
监听ref声明值
  1. watch 监听值发生变化,监听ref声明的值
<script setup lang="ts">
import { ref, watch } from "vue";

let couent = ref(0);

// 监听
watch(couent, (newValue,oldValue) => {
  console.log(newValue,oldValue,'值发生变化了');
  
});

// 方法
const changeCouent = () => {
  couent.value +=1 
};
</script>

<template>
  <div>
    <p>商品价格:{{ couent }}</p>
    <button @click="changeCouent">增加价格</button>
  </div>
</template>

<style scoped></style>

在这里插入图片描述
2. 监听声明的对象类型,当改变单个值时未曾发生变化,需要使用 deep:tree, 深度监听属性,立即监听的话需要使用immediate: true,

  • 单个监听
watch(
  () => person.value.price,
  (newValue, oldValue) => {
    console.log(newValue, oldValue, "价格发生变化了");
  }
);
  • 多个监听
watch(
  () => [person.value.name,person.value.price],
  (newValue, oldValue) => {
    console.log(newValue, oldValue, "价格发生变化了");
  }
);
  • 深度监听,立即执行和监听整个对象
<script setup lang="ts">
import { ref, watch } from "vue";

let person = ref({
  name: "lisd",
  price: 100,
  
});

watch(
  person,
  (newValue, oldValue) => {
    console.log(newValue, oldValue, "值发生变化了");
  },
  {
    deep: true,
    immediate: true,
  }
);
// 方法
const changeName = () => {
  person.value.name = '理想';
};

// 方法
const changePrice = () => {
  person.value.price += 1;
};

// 方法
const changeObj = () => {
  person.value = {
    name: "宝马",
    price: 345,
  };
};
</script>

<template>
  <div>
    <p>品牌:{{ person.name }}</p>
    <p>价格:{{ person.price }}</p>
    <button @click="changeName">增加价格</button>
    <button @click="changePrice">增加价格</button>
    <button @click="changeObj">修改整个品牌</button>
  </div>
</template>

<style scoped></style>


在这里插入图片描述

监听 reactive
  1. 当改变单个值时也能监听到变化,reactive声明的对象类型会隐士的开启 deep:tree 深度监听属性,还无法关闭,立即监听的话需要使用immediate: true,
  • 单个监听
watch(
  () => person.price,
  (newValue, oldValue) => {
    console.log(newValue, oldValue, "价格发生变化了");
  }
);
  • 多个监听
watch(
  () => [person.name,person.price],
  (newValue, oldValue) => {
    console.log(newValue, oldValue, "价格发生变化了");
  }
);
  • 监听整个对象可以不用函数写法,可以直接监听整个对象
<script setup lang="ts">
import { reactive, watch } from "vue";

let person = reactive({
  name: "lisd",
  price: 100,
});


watch(
  person,
  (newValue, oldValue) => {
    console.log(newValue, oldValue, "值发生变化了");
  },
  {
    immediate: true,
  }
);
// 方法
const changeName = () => {
  person.name = "理想";
};

// 方法
const changePrice = () => {
  person.price += 1;
};

// 方法
const changeObj = () => {
  Object.assign(person, {
    name: "宝马",
    price: 345,
  });
};
</script>

<template>
  <div>
    <p>品牌:{{ person.name }}</p>
    <p>价格:{{ person.price }}</p>
    <button @click="changeName">增加价格</button>
    <button @click="changePrice">增加价格</button>
    <button @click="changeObj">修改整个品牌</button>
  </div>
</template>

<style scoped></style>


在这里插入图片描述
**注意:**监听时,单个字段时,使用函数监听值的变化,监听对象或数组时,可以直接监听,不用写函数来监听,但最好还是写函数来监听,不管是单个值还是对象

watchEffect监听全局
*  无论是哪个字段发生变化,都会触发,而watch需要写具体监听某个值

计算属性和监听属性两者使用场景

  1. 计算属性:当你基于组件的响应数据,生成一个新的可缓存值时,可以是使用计算属性
  2. 监听属性:当你数据发生变化,并且想要根据这个变化,进行一些复杂逻辑和异步操作时,可以使用监听属性

Class 与 Style 绑定 和vue的写法没什么区别

<template>
	//对象式绑定
  <div :class="{ active: isActive, 'text-danger'}">
   内容
  </div>
  	//数组式绑定
  <div :class="['conter',{ active: isActive, 'text-danger'}]">
   内容
  </div>
    //使用对象语法动态绑定行内样式
  <div :class="{ color: red, fontSize: fontSize}">
   内容
  </div>
     //使用数组语法动态绑定行内样式
  <div :class="[basStyle]">
   内容
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      isActive: true,
      fontSize: '12px',
      basStyle: {
      	width: '20px'
      	height: '20px'
      	background: 'red'
      }
    }
  }
}
</script>

TS中:接口,泛型,自定义类型

vue 官方文档

  1. 接口:interface,用于定义对象是否符合特定的结构,可以用来定义,props,methods 或者 data的类型
  • 对象的结构,属性名称,类型

  • 定义的对象如果不符合结构,亦或者缺少属性,或者类型不匹配,ts就会报错,当然你也可以添加使其变成一个可选属性,比如以下错误
    在这里插入图片描述

  • 类型声明和组合式声明

    	//类型
    	let test: string | number = 1;
    	组合式API
    	let refTest = ref<string | number>("");
    	refTest.value = "123";
    	let reactiveTest = reactive<object>({
    	  id: 1,
    	  name: "张三",
    	});
    	console.log(reactiveTest);
    
  • 符合定义对象结构写法

    // 定义一个接口,用于限制posen对象的具体属性
    <template>
      <div>
        姓名:{{ posen.name }}-{{ posen.num }}
      </div>
    </template>
    
    <script setup lang="ts">
    import { 
      reactive
     } from "vue";
    
    let posen = reactive<PersonInter>({
      id: 1,
      name: "张三",
    });
    export interface PersonInter {
      id: string | number;
      name: string;
      num?: number;
    }
    </script>
    
    <style></style>
    
  1. 自定义类型 type
    自定义类型允许你为现有的类型创建一个名称,可以组合多个类型,亦或者在类型别名内添加额外的属性
  • 基本类型的别名

    type PersonType = {
      id: string | number;
      name: string;
      num?: number;
    };
    let person: PersonType = {
      id: 1,
      name: "张三",
    };
    
  • 联合类型

    type Age = string | number;
    type ID = string | number;
    type PersonType = {
      id: ID;
      name: string;
      num?: Age;
    };
    
    let refTest = ref<Age>("");
    refTest.value = "123";
    
    type combinationType = PersonType & { age: Age };
    let combination: combinationType = {
      id: 1,
      name: "张三",
      age: 23,
    };
    console.log(combination); // {id: 1, name: '张三', age: 23}
    
  1. 泛型
  • 泛型,允许我们编写可重用的代码,适用于多种类型,在定义函数,接口,类的时候
  • 通过泛型,可以处理任意类型的输入,并确保输入和输出类型一致
// 定义泛型
function generic<T>(arg: T): T {
  return arg;
}

// 使用泛型
let age = generic<number>(23);   // 明确指定类型
let strText = generic("hello");    // 类型推断

ref获取在dom中的使用

vue2的ref和vue3的写法不一样,可以直接声明取值,而不需要使用this.$refs.

  1. 组件直接使用,父组件获取子组件的内容
  • 子组件

    <template>
      <div>我是子组件</div>
    </template>
    
    <script setup lang="ts">
    import { ref, defineExpose } from "vue";
    let re = ref('我是上三');
    let con = ref(3);
    defineExpose({
      re,
      con
    });
    </script>
    
    <style></style>
    
  • 父组件

    
    <script setup lang="ts">
    import ChildComponent from "./aItem.vue";
    import { ref } from "vue";
    
    // 明确 refDom 的类型
    interface ChildComponentInstance {
      re: string;
    }
    
    const refDom = ref<ChildComponentInstance | null>(null);
    /**
     * changeRef函数用于检查和操作一个名为refDom的引用对象
     * 此函数旨在演示如何在Vue组件中处理和访问refs引用的元素或组件
     */
     const changeRef = () => {
      // 检查refDom引用是否已初始化 
      if (refDom.value) {
        // 如果refDom已初始化,打印其值和特定属性
        console.log(refDom.value, refDom.value.re); 
        // Proxy(Object) {re: RefImpl, con: RefImpl, __v_skip: true}[[Handler]]: Object[[Target]]: Proxy(Object)[[IsRevoked]]: false '我是上三'
      } else {
        // 如果refDom未初始化,打印提示信息
        console.log('refDom is not initialized');
      }
    };
    </script>
    
    <template>
      <ChildComponent ref="refDom" />
      <el-button @click="changeRef">点击触发</el-button>
    </template>
    
    <style scoped></style>
    
    
  1. 在 v-for中使用ref时,这个时候返回的 ref 就是一个包含绑定所有元素的数组或者对象了
<template>
    <div :ref="setItemRef" v-for="item in posenList" :key="item.id">
      {{ item.name }}
    </div>
</template>
<script setup lang="ts">
import { ref,ComponentPublicInstance } from "vue";

let itemRefs = ref<HTMLDivElement[]>([]);
// 设置每个元素的 ref
const setItemRef = (el: Element | ComponentPublicInstance | null) => {
  if (el instanceof HTMLDivElement) {
    itemsRef.value.push(el); // 只添加有效的 HTMLDivElement
  }
};
console.log("itemRefs 绑定", itemRefs.value);
</script>

组件

官方文档
动态组件:当多个组件在一个区域来回切换时,可以使用

<!-- currentTab 改变时组件也改变 -->
<component :is="activeComponentName" />
<component :is="tabs[currentTab]"></component>
组件通信
  1. 父组件给子组件传值
  • Props 是一种特别的 atterbutes 你可以用defineProps函数来定义组件期望接收的props,每个props都可以指定类型、默认值、是否必需等属性,其中包含了组件传递的所有 props

  • defineProps 是一个仅 <script setup> 中可用的编译宏命令,所以不需要显示的导入

  • props官方文档

    • 父组件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      import { reactive } from "vue";
      
      let chPerson = reactive({ id: 1, name: "张三" });
      </script>
      
      <template>
        <ChildComponent :propsVal="chPerson" car="宝马" ref="refDom" />
      </template>
      
      <style scoped></style>
      
      
    • 子组件

      <template>
        <div>
          <p>子组件数据: {{ person.name }}</p>
          <p>父组件传递数据: 姓名:{{ props.propsVal.name }}-汽车:{{ car }}</p>
        </div>
      </template>
      
      <script setup lang="ts">
      import {reactive,defineProps } from "vue";
      
      let person = reactive({
        name: "q2",
      });
      
      const props = defineProps({
        propsVal: {
          type: Object,
          default: () => {
            return {
              name: "q2",
            };
          },
        },
        car: {
          type: String,
          default: "奔驰",
        },
      });
      
      // 解构 props
      // const { propsVal, car } = defineProps({
      //   propsVal: {
      //     type: Object,
      //     default: () => ({
      //       name: "q2",
      //     }),
      //   },
      //   car: {
      //     type: String,
      //     default: "奔驰",
      //   },
      // });
      console.log(props);// 父组件传递数据
      
      </script>
      
      <style></style>
      
      

    注意:如果没有在 <script setup> 下,那么 props 就必须以 props选项的方式声明,props 对象会作为 setup()函数的第一个参数被传入

    <template>
      <div>
        <p>父组件传递数据: 姓名:{{ propsVal.name }}-汽车:{{ car }}</p>
      </div>
    </template>
    
    <script>
    export default {
      name: "childItem",
      props: {
        propsVal: {
          type: Object,
          default: () => {
            return {
              name: "q2",
            };
          },
        },
        car: {
          type: String,
          default: "奔驰",
        },
      },
      setup(props, context) {
        console.log(props, props.propsVal.name);
        // console.log(context.emit);
        // console.log(context.slots);
      }
    }
    </script>
    
    <style></style>
    
    
  1. 子组件给父组件传值 defineEmits
  • defineEmits方法返回函数并触发,可以使子组件的值传递到父组件中

  • defineEmits 仅可用于<script setup> 中,可以不需要导入直接使用,它返回一个等同于 $emitemit 函数,可以在组件内抛出事件

    • 子组件

      <template>
        <div>
          <el-button @click="changeName">给父组件传递数据</el-button>
        </div>
      </template>
      
      <script setup lang="ts">
      import { reactive,defineEmits } from "vue";
      
      let person = reactive({
        name: "q2",
      });
      
      
      let emit = defineEmits(["childEvent"]);
      const changeName = () => {
        emit("childEvent", person);
      };
      </script>
      
      <style></style>
      
      
    • 父组件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      
      interface ChildEventData {
        id?: number;
        name?: string;
        [key: string]: any; // 允许扩展其他字段
      }
      
      const handleChildEvent = (val: ChildEventData | {})=> {
        console.log(val,'子组件传递的值');
      }
      </script>
      
      <template>
        <ChildComponent @child-event="handleChildEvent" />
      </template>
      
      <style scoped></style>
      
      
  • 如果你没有在使用 <script setup>,你可以从 setup()函数的第二个参数的emit,抛出事件的

    export default {
      emits: ['enlarge-text'],
      setup(props, ctx) {
        ctx.emit('enlarge-text')
      }
    }
    
  1. $ref + defineExpose(obj)
  • defineExpos可以用来显式暴露组件内部的属性或方法,使得父组件可以通过 ref 访问子组件的内容

  • 由于子组件的内容不会自动暴露给父组件,所以需要defineExpose 选择性地暴露内部内容,从而避免不必要的属性泄漏,同时提供更好的封装性

  • defineExpose 是专为 <script setup> 设计的,不能用于普通的 <script>setup() 函数中

  • 不建议直接暴露整个组件内部状态,应该只暴露需要的内容,从而保持组件封装性

    • 父组件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      import { ref } from "vue";
      
      const refDom = ref();
      
      const changeRef = () => {
        // 检查refDom引用是否已初始化
        if (refDom.value) {
          // 如果refDom已初始化,打印其值和特定属性
          console.log(refDom.value.messe, "父组件hi", refDom.value);
        
          const { btnChild } = refDom.value;
          btnChild('父组件传递哦')
        } else {
          // 如果refDom未初始化,打印提示信息
          console.log("refDom is not initialized");
        }
      };
      </script>
      
      <template>
        <ChildComponent ref="refDom" />
        <el-button @click="changeRef">调用子组件方法</el-button>
      </template>
      
      <style scoped></style>
      
      
    • 子组件

      <template>
        <div></div>
      </template>
      
      <script setup lang="ts">
      import { ref, reactive, defineExpose } from "vue";
      
      let person = reactive({
        name: "q2",
      });
      let messe = ref("子组件消息");
      const btnChild = (value: string) => {
        console.log("子组件按钮", value);
      };
      
      defineExpose({
        person,
        messe,
        btnChild,
        text: "我是子组件的text",
      });
      </script>
      
      <style></style>
      
      
  1. 兄弟组件之间通信 mitt
    安装 mitt

npm install --save mitt

  • 你可以封装成一个xx.ts使用

    import mitt from "mitt";
    export default mitt();
    
    
  • 组件内使用

    // 兄弟组件1
    <template>
      <div>
        <el-button @click="btnChild">子组件按钮</el-button>
      </div>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue";
    import emittler from "@/utils/emitter";
    
    
    let messe = ref("你好啊");
    const btnChild = (value: string) => {
      console.log("子组件按钮", value);
      emittler.emit("myEvent", messe.value);
    };
    </script>
    
    <style></style>
    // 兄弟组件2
    <template>
      <div>
      </div>
    </template>
    
    <script setup lang="ts">
    import emittler from "@/utils/emitter";
    emittler.on('myEvent', (message) => {
        console.log(message) // 输出: "你好啊"
    })
    
    </script>
    
    <style></style>
    
    
  1. useAttrs + a t t r s 如果需要在子组件接受很多 p r o p s , 但你又没在 p r o p s 中定义,那么其他传递的值就会放在 ‘ attrs 如果需要在子组件接受很多props, 但你又没在 props中定义,那么其他传递的值就会放在 ` attrs如果需要在子组件接受很多props,但你又没在props中定义,那么其他传递的值就会放在atters`中
    在这里插入图片描述
  • 父组件传值

    <script setup lang="ts">
    import ChildComponent from "./childItem.vue";
    import { reactive } from "vue";
    
    let chPerson = reactive({ id: 1, name: "张三" });
    
    </script>
    
    <template>
      <ChildComponent :propsVal="chPerson" :test="234" car="宝马" ref="refDom" />
    </template>
    
    <style scoped></style>
    
    
  • 子组件接收,你可以直接 $atters去取值,也可以声明变量去接值

    <template>
      <div>
        <p>父组件传递数据: 姓名:{{ props.propsVal.name }}-汽车:{{ $attrs.car }}</p>
      </div>
    </template>
    
    <script setup lang="ts">
    import { defineProps,useAttrs } from "vue";
    
    const attrs = useAttrs();
    console.log(attrs.car); // 宝马
    
    const props = defineProps({
      propsVal: {
        type: Object,
        default: () => {
          return {
            name: "q2",
          };
        },
      },
    });
    </script>
    
    <style></style>
    
    
  1. 双向绑定 v-model + defineModel 官方文档
  • 父子组件数据双向绑定 ,v-model 在组件上实现双向绑定

  • 多个v-model 可以接收一个参数,我们可以通过将这个参数当成字符串给defineModel来接收对应的值

  • 如果声明之后,那么如果你不通过字符串来获取,那你将获取不到值

    • 父组件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      import { ref,reactive } from "vue";
      let titleString = ref("收到反馈及时");
      let chPerson = reactive({ id: 1, name: "张三" });
      
      </script>
      
      <template>
        <p>{{ chPerson }}</p>
        <p>{{ titleString }}</p>
        <ChildComponent v-model="chPerson" v-model:title="titleString" car="宝马" />
      </template>
      
      <style scoped></style>
      
      
    • 子组件

      <template>
        <div>
          <el-button type="primary" @click="brnHa">点击修改</el-button>
        </div>
      </template>
      
      <script setup lang="ts">
      import { defineModel } from "vue";
      interface Person {
        name: string;
        id: number;
      }
      
      const chPerson = defineModel<Person>({
        default: () => {
          return {
            name: "宝马",
            id: 1,
          };
        },
      });
      const stringTitle = defineModel('title',{
        type: String,
        default: '中NSA公司',
      });
      console.log(chPerson.value,stringTitle.value); // 宝马
      const brnHa = () => {
        stringTitle.value = '中发的时间分厘卡'
        chPerson.value = {
          name: "奔驰",
          id: 2,
        };
        console.log(chPerson);
      };
      </script>
      
      <style></style>
      
      
  • v-model 可以绑定一些内置修饰符,如.trim,.number,.lazy 等,当然我们也可以自己定义一个修饰符, 通过 解构defineModel的返回值,我们可以在子组件访问时给定义修饰符,基于修饰符可以选择性的调节值的读取和写入,我们给 defineModel传入get,set两个选择,根据判断修饰符来实现我们的代码逻辑,这里使用了set

    • 父组件

      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      import { ref } from "vue";
      
      let character = ref("dhasdh");
      
      </script>
      
      <template>
       
        <p>{{ character }}</p>
        <ChildComponent  v-model:character.capitalLetters="character" />
      </template>
      
      <style scoped></style>
      
      
    • 子组件

      <template>
        <div>
          <input type="text" v-model="character">
        </div>
      </template>
      
      <script setup lang="ts">
      import { defineModel } from "vue";
      const [character,modifiers] = defineModel('character',{
        set: (val:string) => {
          // 含有capitalLetters修饰符
          if(modifiers.capitalLetters){
            return val.charAt(0).toUpperCase() + val.slice(1)
          }
          console.log(val,character,modifiers);
          return val
        }
      });
      
      </script>
      
      <style></style>
      
      
  1. provide / Inject(提供/注入)
  • 在父组件中定义值和事件 provide(提供数据)

    <script setup lang="ts">
    import ChildComponent from "./childItem.vue";
    import { reactive,provide } from "vue";
    
    let chPerson = reactive({ id: 1, name: "张三" });
    provide('sae',chPerson)
    
    const refDom =  ()=>{
      console.log('234')
    }
    provide('abnt', refDom)
    
    </script>
    
    <template>
      <p>{{ chPerson }}</p>
      <ChildComponent :propsVal="chPerson" :test="234" car="宝马" />
    </template>
    
    <style scoped></style>
    
    
  • 子组件或孙子组件 中使用 inject (获取数据)

    <template>
      <div>
        <el-button type="primary" @click="brnHa">点击修改</el-button>
      </div>
    </template>
    
    <script setup lang="ts">
    import { inject } from "vue";
    
    const car = inject("sae", { name: "未知品牌" }); // 提供默认值避免 undefined
    const abnt = inject("abnt", () => {}); // 提供空函数作为默认值
    
    console.log(car); // 宝马
    const brnHa = () =>{
      car.name = "奔驰";
      abnt()
      console.log(car);
    }
    
    </script>
    
    <style></style>
    
    
  • 注意:如果在其中一个组件修改,那么所有组件都会同步修改后的数据的

  1. pinia vue官方推荐的状态集中管理工具可以看下面的 vuex => pinia

vuex => pinia的使用

官方文档

  1. Pinia 提供了更简洁直接的 API,并提供了组合式风格的 API,最重要的是,在使用 TypeScript 时它提供了更完善的类型推导

    • state (状态)

      • 应用的数据来源,是一个响应式对象
    • getters (计算属性)

      • 类似于技术属性,getters可以根据state的值来派生出新的值
      • getters是有缓存的,只有所依赖的数据发生改变的时候才会重新计算
    • actions (动作)

      • actions中定义事件函数,来改变state中的值
  2. Pinia 的使用 选项式API

    • 先下载 pinia

      npm install pinia
      # 或者
      yarn add pinia
      
    • 在 mina.ts里引用

      import { createApp } from 'vue'
      
      import App from './App.vue'
      
      
      import {  createPinia } from 'pinia'
      let store = createPinia()
      const app = createApp(App);
      app
      .use(store)
      .mount('#app')
      
      
    • src/store 目录下创建一个 例如 useStore.ts 文件

      import { defineStore } from "pinia";
      
      export const useertStore = defineStore("main", {
        state: () => {
          return {
            // all your data here
          count: 0,
          };
        },
        getters: {
      	    doubleCount: (state) => state.count * 2,
        },
        actions: {
          // all your methods here
          increment() {
            this.count++;
          },
        },
      });
      
    • 在组件里面使用

      
      // 父组件
      <script setup lang="ts">
      import ChildComponent from "./childItem.vue";
      
      import { useertStore } from "@/store/useStore"
      const store = useertStore();
      </script>
      
      <template>
       
        <p>{{ store.count }}</p>
        <ChildComponent />
      </template>
      
      <style scoped></style>
      
      
      // 子组件
      <template>
        <div>
          <el-button type="primary" @click="btnAdd">点击添加{{store.count}}</el-button>
          <p>{{ store.doubleCount }}</p>
        </div>
      </template>
      
      <script setup lang="ts">
      import { useertStore } from "@/store/useStore"
      const store = useertStore();
      
      const btnAdd = () => {
        store.increment();
        console.log(store.count)
      }
      </script>
      
      <style></style>
      
      
      
  3. pinia 的使用 组合式API 在组件内用法和选项式API一样

    import { defineStore } from "pinia";
    import { ref, computed } from "vue";
    export const useertStore = defineStore("main", () => {
      let count = ref(0);
      let doubleCount = computed(() => count.value * 2);
      const increment = () => {
        count.value++;
      };
      return { doubleCount, count, increment };
    });
    
    

插槽使用slot

  • 默认插槽:用于在子组件模板中定义一个位置,父组件可以在该位置插入自己的内容。

  • 具名插槽:允许你在子组件模板中定义多个插槽位置,每个位置可以有自己的名字。在父组件中,你可以指定内容应该插入到哪个具名插槽。你可以 v-slot:header也可以简写#header

  • 条件插槽:我们可以通过 $slotsv-if来实现

  • 作用域插槽:允许子组件数据传递给父组件,以便父组件可以自定义如何渲染这些数据,你可以这样写 v-slot:name="slotProps"也可以这样简写#name="slotProps"

  • 动态插槽:允许插槽的名称是动态的,以满足更多的业务需求

    • 子组件声明插槽
    <template>
      <div>
        <slot></slot>
        <div class="container">
          <header>
            <slot name="header"></slot>
          </header>
          <main>
            <slot name="main" maintext="主要内容头部"></slot>
            <div class="list">
              <div class="item" v-for="item in list">
                <slot name="item" :item="item">默认信息</slot>
              </div>
            </div>
          </main>
          <footer>
            <template v-if="$slots.footer">
              <slot name="footer">我是底部</slot>
            </template>
          </footer>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    import { reactive } from 'vue';
    
    let list = reactive([1, 2, 3]);
    </script>
    
    <style></style>
    
    
    • 父组件使用
    <script setup lang="ts">
    import ChildComponent from "./childItem.vue";
    </script>
    
    <template>
      <ChildComponent>
        发生的
        <template #header>
          <div>我是具名插槽</div>
        </template>
        <!-- 作用域插槽 -->
        <template #main="props">
          <div>{{ props.maintext }}</div>
        </template>
        <!-- 动态插槽 -->
        <template #item="items">
          <div>{{items.item}}</div>
        </template>
      </ChildComponent>
    </template>
    
    <style scoped></style>
    
    

生命周期

  1. Vue2 的生命周期钩子代码更新到 Vue3 官方文档
  • setup 是vue 3 新增的钩子函数,位于组件创建实例之前,适用于进行异步数据获取,状态管理,逻辑代码封装
    • beforeCreate -> 使用 setup(): 实例创建前
    • created -> 使用 setup():实例创建完毕,可以用于访问和修改数据,但还未挂载到dom元素上
  • beforeMount -> onBeforeMount: 挂载前,可以用于修改dom解构
    mounted -> onMounted:挂载完毕,也就是组件渲染你完成,可以进行dom的操作和事件调用和监听
    beforeUpdate -> onBeforeUpdate: 组件将要更新到dom树之前,可以在vue更新dom之前访问dom状态
    updated -> onUpdated: 组件更新到dom树之后,用于执行依赖dom的更新操作
    beforeDestroy -> onBeforeUnmount:销毁前->卸载前, 用于清理资源
    destroyed -> onUnmounted:销毁完毕->卸载完毕,用于清理资源
    errorCaptured -> onErrorCaptured:捕获错误,在错误发生时调用
  1. 组件缓存 <KeepAlive>
  • <KeepAlive>是一个内置组件,它可以在多个人组件动态切换时缓存被移除的组件实例

    <!-- 非活跃的组件将会被缓存! -->
    <KeepAlive>
      <component :is="activeComponent" />
    </KeepAlive>
    
    • 我们可以通过 include来制定是否需要缓存,当名称匹配时组件才会被缓存

      <!-- 以英文逗号分隔的字符串 -->
      <KeepAlive include="a,b">
        <component :is="view" />
      </KeepAlive>
      
      <!-- 正则表达式 (需使用 `v-bind`) -->
      <KeepAlive :include="/a|b/">
        <component :is="view" />
      </KeepAlive>
      
      <!-- 数组 (需使用 `v-bind`) -->
      <KeepAlive :include="['a', 'b']">
        <component :is="view" />
      </KeepAlive>
      
    • exclude来排除不缓存的组件,当匹配时不缓存,用法和 include一样

    • max 可以设置缓存组件的最大值,当缓存组件数量达到最大数值时,那么在新组件创建之前,已缓存组件中很久未曾访问的组件就会被销毁掉

  • 生命周期

    • onActivated 在组件挂载时也会调用(组件从缓存中被激活时触发)
    • onDeactivated 在组件卸载时也会调 (组件切换到其他页面时触发)
  1. 父子组件生命周期的先后顺序,vue2 和 vue3 变化不大,只是`beforeCreate ,created 被 setup() 取代
  • 加载渲染依次顺序:

    父组件:beforeCreate => 父组件: created => 父组件:beforeMount(onBeforeMount)=> 子组件:beforeCreate => 子组件:created => 子组件:beforeMount(onBeforeMount) =>子组件:mounted(onMounted) => 父组件:`mounted(onMounted)

  • 更新过程中依次顺序:

    父组件:beforeUpdate(onBeforeUpdate) => 子组件: beforeUpdate(onBeforeUpdate) => 子组件:updated(onUpdated)=> 父组件:updated(onUpdated)

  • 销毁过程中依次顺序:

    父组件:父组件:beforeDestroy(onBeforeUnmount) => 子组件: beforeDestroy(onBeforeUnmount) => 子组件:destroyed(onUnmounted)=> 父组件:destroyed(onUnmounted)

hooks

  1. vue hooks 是一个遵循特点规则的函数,命名以use起始,依托于vue 的组合式API构建,将组件逻辑拆分为独立,可复用的小块
  • useCount.ts 一个简单的计算属性

    import { ref } from "vue";
    export const useCount = () => {
      const count = ref(0);
      const increment = () => {
        count.value++;
      };
      return { count, increment };
    };
    
    
    <template>
      <div>
        <div> 计算结果:{{ count }}</div>
        <el-button type="primary" @click="increment">添加</el-button>
      </div>
    </template>
    
    <script setup lang="ts">
    import { useCount } from "@/hooks/useCount";
    const { count, increment } = useCount();
    </script>
    
    <style></style>
    
    
  • 多个嵌套使用

    import { ref } from "vue";
    export const useRide = () => {
      const rideNum = ref(2);
      const ride = (num: number) => {
       return rideNum.value * num;
      };
      return { rideNum, ride };
    };
    
    
    <template>
      <div>
        <div>计算结果:{{ count }}</div>
        <el-button type="primary" @click="increment">添加</el-button>
        <p>{{ result }}</p>
      </div>
    </template>
    
    <script setup lang="ts">
    import { useCount } from "@/hooks/useCount";
    import { useRide } from "@/hooks/useRide";
    import { ref, watch } from "vue";
    const { count, increment } = useCount();
    const {  ride } = useRide();
    let result = ref(0)
    watch(
      () => count.value,
      (newValue, oldValue) => {
        console.log(newValue, oldValue);
        result.value = ride(newValue);
      }
    );
    </script>
    
    <style></style>
    
    

自定义指令

传送门

其他API

  • shallowRef : 创建一个响应式数据,但只对顶层属性进行响应式处理,只跟踪引用值变化,不关心值内部属性变化
  • shallowReactive:创建一个浅层响应式对象,但只对对象顶层属性进行响应式处理,对象内部属性变化不会做任何响应
  • readonly:创建一个对象,对象的所以属性包括嵌套属性都只能读,不能修改
  • shallowReadonly::和readonly相似,创建一个对象,对象的顶层属性只能读,不能修改,但嵌套属性是可以更改的

其他组件

  • teleport:是一种能够将我们组件的HTML结构一定到指定位置的技术
<teleport to="body">
	html内容
</teleport>
  • Suspense

安装路由

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举

  1. 安装路由

    npm install vue-router@4
    # 或者
    yarn add vue-router@4
    
  2. 在src/router/index.ts创建路由实例

    import {
      createRouter,
      createWebHistory,
      type RouteRecordRaw,
    } from "vue-router";
    export const Layout = () => import("@/layout/index.vue");
    // 静态路由
    export const constantRoutes: RouteRecordRaw[] = [
      {
        path: "/",
        component: Layout,
        meta: { hidden: true },
        children: [
          {
            path: "/",
            component: () => import("@/views/index.vue"),
          },
          {
            path: "/aItem",
            component: () => import("@/views/aItem.vue"),
          },
        ],
      },
    ];
    const router = createRouter({
      history: createWebHistory(), 
      routes: constantRoutes,
    
    });
    
    export default router;
    
    
  3. 在main.ts内挂载使用

    import router from './router'
    
    const app = createApp(App);
    app
    .use(router)
    .mount('#app')
    
  4. 路由工作模式

  • history模式(HTML5 模式):createWebHistory

    • 优点:不含有#,显得更优雅

    • 缺点:项目上线,需要服务端配合处理路径问题,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id,就会得到一个 404 错误

      const router = createRouter({
        history: createWebHistory(),
        routes: constantRoutes,
      
      });
      
  • hash模式

    • 优点:不需要服务端进行特殊的处理

    • 缺点:url上会出现一个#,在seo中影响不好

      const router = createRouter({
        history: createWebHashHistory(), //history: createWebHashHistory(),
        routes: constantRoutes,
      });
      
  1. RouterLink 和 RouterView
  • RouterLink : 创建导航链接

  • RouterView : 渲染组件,也就是当前路由显示匹配的组件

     <RouterLink to="/">Go to Home</RouterLink>
        <RouterLink to="/aItem">Go About</RouterLink>
        <RouterView />
    
  • 携带参数跳转

    
    <RouterLink to="/aItem?id:3">跳转</RouterLink>
    // 或
    <router-link :to="{ path: '/aItem', params: { id: '12' } }">
      跳转
    </router-link>
    
  1. 嵌套路由
  • <router-view>嵌套一个 <router-view>,如果渲染到这个嵌套的 router-view 中,我们需要在路由中配置 children
  • children 配置只是另一个路由数组,就像 routes 本身一样。因此,你可以根据自己的需要,不断地嵌套视图
    
    import {
      createRouter,
      createWebHashHistory,
      type RouteRecordRaw,
    } from "vue-router";
    export const Layout = () => import("@/layout/index.vue");
    // 静态路由
    export const constantRoutes: RouteRecordRaw[] = [
      {
        path: "/",
        component: Layout,
        meta: { hidden: true },
        children: [
          {
            path: "/",
            component: () => import("@/views/index.vue"),
          },
          {
            path: "/aItem/:id",
            name: "aItem",
            component: () => import("@/views/aItem.vue"),
          },
        ],
      },
    ];
    const router = createRouter({
      history: createWebHashHistory(),
      routes: constantRoutes,
    });
    
    export default router;
    
    
  1. 命名路由
  • 当创建路由时我们可以给路由一个name
const routes = [
  {
    path: '/user/:username',
    name: 'profile', 
    component: User
  }
]
  1. 动态路由
  • 通过路径传递参数

    <script setup lang="ts">
    </script>
    
    <template>
      页面二
      <router-link :to="{ name: 'aItem', params: { id: '12' } }">跳转aItem</router-link>
    </template>
    
    <style scoped></style>
    
  • 需要路由配置支持动态路径参数

    import { createRouter, createWebHistory } from 'vue-router';
    
    const routes = [
      {
        path: '/aItem/:id',
        name: 'aItem',
        component: () => import('@/views/aItem.vue'),
      },
    ];
    
    const router = createRouter({
      history: createWebHistory(),
      routes,
    });
    
    export default router;
    
  1. 编程时导航
    我们可以通过useRouter来访问路由
  • 导航到不同位置

    • 使用router.push方法,会给 history 栈添加一个新的记录,当用户点击返回,即返回上一页时就会回到之前的URL,此方法相当于点击<router-link :to="...">
      <script setup lang="ts">
      import { useRouter } from 'vue-router'
      const router = useRouter()
      const goAitem = () => {
        router.push({
          name: 'aItem',
          params: {
            id: '12'
          }
        })
      }
      </script>
      
      <template>
        页面二
        <router-link :to="{ name: 'aItem', params: { id: '12' } }">跳转aItem</router-link>
        <el-button type="primary" @click="goAitem">跳转aItem</el-button>
      </template>
      
      <style scoped></style>
      
  • 替换当前位置

    • router.replace,它不会向history添加新的记录,会直接替换当前条目,亦或者router.push 中添加一个 replace: true

      <script setup lang="ts">
      import { useRouter } from 'vue-router'
      const router = useRouter()
      const goAitem = () => {
        router.replace({
          name: 'aItem',
          params: {
            id: '12'
          }
        })
      
        // 亦或者 添加 replace: true,
        // router.push({
        //   name: 'aItem',
        //   replace: true,
        //   params: {
        //     id: '12'
        //   }
        // })
      }
      </script>
      
      <template>
        页面二
        <router-link :to="{ name: 'aItem', replace: true, params: { id: '12' } }">跳转aItem</router-link>
        <el-button type="primary" @click="goAitem">跳转aItem</el-button>
      </template>
      
      <style scoped></style>
      
      
  1. 路由组件传参
  • 传递参数

    • params:是URL的一部分,通常用于传递静态数据,若使用 to的对象写法时,必须使用 name配置项,在路由里面配置,也就是动态路由
    • query :参数也会附加在URL后面,用于传递敏感数据
    <script setup lang="ts">
    import { useRouter } from 'vue-router'
    const router = useRouter()
    const goAitem = () => {
    
    
      // params
      router.push({
        name: 'aItem',
        params: {
          id: '12'
        }
      })
      // query
      router.push({
        name: 'aItem',
        query: {
          id: '12'
        }
      })
    }
    </script>
    
    <template>
      页面二
      <router-link :to="{ name: 'aItem', replace: true, params: { id: '12' } }">跳转aItem</router-link>
      <router-link :to="{ name: 'aItem', replace: true, query: { id: '12' } }">跳转aItem</router-link>
      <el-button type="primary" @click="goAitem">跳转aItem</el-button>
    </template>
    
    <style scoped></style>
    
    
  • 接收参数

    • 通过 useRoute来获取 query 参数,useRoute 返回的是响应式的路由对象,其中 query包含了所以查询参数
      <script setup lang="ts">
      import { useRoute } from 'vue-router';
      const route = useRoute(); 
      console.log(route,' `query`');
      </script>
      
  • 路由 props 配置

    • 当 props 设置为 true 时,route.params 将被设置为组件的 props

      const routes = [
        { path: '/aItem/:id',name: 'aItem',component: User, props: true }
      ]
      
    • 函数模式下无论是动态路由参数还是查询参数,params, query 都可以方便地作为 props 传递到组件中

      • 配置
      const routes = [
        {
          path: '/aItem',
          component: SearchUser,
          props: route => ({ query: route.query.q })
        }
      ]
      
      • 组件接收
      <script setup>
      defineProps({
        id: {
          type: String,
          default: "奔驰",
        }
      })
      </script>
      

部署服务器

传送门

vue创建项目使用element-plus

  • 下载引用element-plus
# 选择一个你喜欢的包管理器

# NPM
npm install element-plus --save

# Yarn
yarn add element-plus

# pnpm
pnpm install element-plus
  • 在main.js内引用
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// 引入 Element-Plus 依赖
import ElementPlus from 'element-plus'
// 引入全局 cSS 样式
import 'element-plus/dist/index.css'

createApp(App)
.use(store)
.use(router)
.use(ElementPlus)
.mount('#app')

  • 页面中使用,这里实现element-plus中英文切换 结合 vuex切换中英文
<template>
  <div class='about'>
    <el-select @change="handleClear" v-model="selectValue" placeholder="选择语言" style="width: 240px">
      <el-option v-for="item in langOptions" :key="item.value" :label="item.label" :value="item.value">
        <span style="float: left">{{ item.label }}</span>
        <span style="
          float: right;
          color: var(--el-text-color-secondary);
          font-size: 13px;
        ">
          {{ item.value }}
        </span>
      </el-option>
    </el-select>
  </div>
</template>

<script>
import { useStore } from "vuex";
import {
  reactive, 
  ref 
} from 'vue'
export default {
  name: 'about',
  setup() {
    const selectValue = ref('')
    const langOptions = reactive([
      {
        value: 'en',
        label: 'English'
      },
      {
        value: 'zhCn',
        label: '中文'
      }
    ])

    const store = useStore()
    const handleClear = (value) => {
      store.dispatch('provider/updateLanguage', value)
    }
    return {
      selectValue,
      langOptions,
      handleClear
    }
  }
};
</script>
<style lang='less' scoped></style>

  • 全局实现中英文
<template>
  <el-config-provider :locale="locale">
    <el-table mb-1 :data="[]" />
    <router-view />
  </el-config-provider>
</template>

<script>
import {
  computed,
} from 'vue'
import { useStore } from "vuex";

export default {
  name: 'App',
  setup() {
    const store = useStore()
    
    const locale = computed(() => store.state.provider.language);
    
    // 返回数据
    return {
      locale,
    }
  }
}
</script>
  • vuex 配置与使用
import { createStore } from 'vuex'
import provider  from './modules/provider'
export default createStore({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    provider 
  }
})
  • modules的创建provider文件
// 导入 Element Plus 中英文语言包
import zhCn from "element-plus/es/locale/lang/zh-cn";
import en from "element-plus/es/locale/lang/en";
const user = {
    namespaced: true,
    state: {
        language: zhCn,
    },
    mutations: {
        setLanguage(state, language) {
            if (language == "en") {
                state.language = en;
            } else {
                state.language = zhCn;
            }
        }
    },
    actions: {
        /**
         * 根据语言标识读取对应的语言包
         */
        updateLanguage({ commit }, language) {
            commit('setLanguage', language)
        }
    },
    getters: {
        language(state) {
            return state.language
        }
    }
}
export default user
  • 最终实现效果
    在这里插入图片描述
  • 启动项目报错
  • 预转换错误:未找到预处理器依赖项“sas-embedded”。你安装了吗?尝试npm install-D sass-embedded
    在这里插入图片描述
  • 按照提示安装重新启动就好

按需引入

  • 按需导入,下载两款插件 unplugin-vue-componentsunplugin-auto-import这两款插件

npm install -D unplugin-vue-components unplugin-auto-import

  • 在vite.config.ts 中配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
})

  • 重启项目接下来直接引用就行了

组件名称的单个配置

  • vue3 会根据组件文件名称自动推导出name属性,item.vue name 名为item ,但当我们起个文件夹名称,但文件夹内容的文件是index.vue时就无法推导出文件内名称了
    在这里插入图片描述

    • 这在我们调试和定位问题时就不太方便了,当然我们也可以单独添加一个scrit 来去写name名称,但这种方法有点过于繁琐

      ```
      <script lang="ts">
      	export default {
         		 name: 'pramas'
      	}
      </script>
      ```
      
    • 社区推出了 unplugin-vue-define-options 来简化该操作

      npm i unplugin-vue-define-options -D

      // vite.config.ts
      import DefineOptions from 'unplugin-vue-define-options/vite'
      import Vue from '@vitejs/plugin-vue'
      
      export default defineConfig({
        plugins: [Vue(), DefineOptions()],
      })
      
    • 页面中使用

      <script setup lang="ts">
      defineOptions({
        name: "pramas"  
      })
      <script>
      
    • 三方插件在 <script lang="ts" name="pramas"> 上添加name

    <script setup name="pramas">
        let a = '虑是否'
    </script>
    
    • 下载此插件可以动态修改vue文件名称

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

  • 在 vite.config.ts内引用

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import vueDefineNmae from 'vite-plugin-vue-setup-extend'
    export default defineConfig({
      plugins: [
        vue(),
        vueDefineNmae()
      ],
    })
    
  • 代码运行就可以看见我们自定义的name名称了

在这里插入图片描述

遇到问题

1. 淘宝镜像过期,更改淘宝镜像

在这里插入图片描述

  • 在这里插入代码片

1.查看当前npm镜像
npm config get
2.配置新的镜像
npm config set registry https://registry.npmmirror.com
3.再次查看镜像配置情况
npm config get

2.npm run dev` 无法启动项目,显示vite不是内部或外部命令,这是系统启动vite项目,但找不到vite,意味着你未曾安装vite,你可以执行以下命令全局安装,再执行之前命令

npm install -g vite

  • node 和 npm 版本不兼容,官网需要18.3或更高版本的
    在这里插入图片描述

3.vscode打开vite创建项目引入组件报错解决

在这里插入图片描述

  • 在src下创建此文件xxx.d.ts在你的 src 目录中,填入以下内容,帮助 TypeScript 理解 .vue 文件

    	declare module "*.vue" {
    		import { defineComponent } from "vue";
    		const Component: ReturnType<typeof defineComponent>;
    		export default Component;
    	}
    
  • 如果未曾解决,将.vue 改为vue并重新打开项目

  • 如果还未解决可以看看官网的方法
    在这里插入图片描述

4.vue3+vite3+ts使用@alias路径别名爆红报错解决

在这里插入图片描述

  1. vite.config.ts 中配置以下内容
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path";
// https://vite.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
})
  1. 如果显示找不到 path 按照以下命令安装

npm install --save-dev @types/node

  1. tsconfig.app.json 中配置以下内容,然后重启项目
{
	"compilerOptions": {
	  "baseUrl": ".",
	    "paths": {
	      "@/*": ["src/*"],
	    }
	},
	"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

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

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

相关文章

【R语言绘图】圈图绘制代码

绘制代码 rm(list ls())# 加载必要包 library(data.table) library(circlize) library(ComplexHeatmap) library(rtracklayer) library(GenomicRanges) library(BSgenome) library(GenomicFeatures) library(dplyr)### 数据准备阶段 ### # 1. 读取染色体长度信息 df <- re…

Python爬虫第6节-requests库的基本用法

目录 前言 一、准备工作 二、实例引入 三、GET请求 3.1 基本示例 3.2 抓取网页 3.3 抓取二进制数据 3.4 添加headers 四、POST请求 五、响应 前言 前面我们学习了urllib的基础使用方法。不过&#xff0c;urllib在实际应用中存在一些不便之处。以网页验证和Cookies处理…

什么是可靠性工程师?

一、什么是可靠性工程师&#xff1f; 可靠性工程师就是负责确保产品在使用过程中不出故障、不给客户添麻烦。 你可以理解为是那种“挑毛病的人”&#xff0c;但不是事后挑&#xff0c;是提前想清楚产品在哪些情况下可能会出问题&#xff0c;然后解决掉。 比如&#xff1a; …

如何根据设计稿进行移动端适配:全面详解

如何根据设计稿进行移动端适配&#xff1a;全面详解 文章目录 如何根据设计稿进行移动端适配&#xff1a;全面详解1. **理解设计稿**1.1 设计稿的尺寸1.2 设计稿的单位 2. **移动端适配的核心技术**2.1 使用 viewport 元标签2.1.1 代码示例2.1.2 参数说明 2.2 使用相对单位2.2.…

【Kafka基础】Kafka 2.8以下版本的安装与配置指南:传统ZooKeeper依赖版详解

对于仍在使用Kafka 2.8之前版本的团队来说&#xff0c;需要特别注意其强依赖外部ZooKeeper的特性。本文将完整演示传统架构下的安装流程&#xff0c;并对比新旧版本差异。 1 版本特性差异说明 1.1 2.8 vs 2.8-核心区别 特性 2.8版本 2.8-版本 协调服务 可选内置KRaft模式 …

Redis-x64-3.2.100.msi : Windows 安装包(MSI 格式)安装步骤

Redis-x64-3.2.100.msi 是 Redis 的 Windows 安装包&#xff08;MSI 格式&#xff09;&#xff0c;适用于 64 位系统。 在由于一些环境需要低版本的Redis的安装包。 Redis-x64-3.2.100.msi 安装包下载&#xff1a;https://pan.quark.cn/s/cc4d38262a15 Redis 是一个开源的 内…

【云计算】打造高效容器云平台:规划、部署与架构设计

引言 随着移动互联网时代的大步跃进&#xff0c;互联网公司业务的爆炸式增长发展给传统行业带来了巨大的冲击和挑战&#xff0c;被迫考虑转型和调整。对于我们传统的航空行业来说&#xff0c;还存在传统的思维、落后的技术。一项新业务从提出需求到立项审批、公开招标、项目实…

DeepSeek底层揭秘——《推理时Scaling方法》内容理解

4月初&#xff0c;DeepSeek 提交到 arXiv 上的最新论文正在 AI 社区逐渐升温。 论文核心内容理解 DeepSeek与清华大学联合发布的论文《奖励模型的推理时Scaling方法及其在大规模语言模型中的应用》&#xff0c;核心在于提出一种新的推理时Scaling方法&#xff0c;即通过动态调…

JavaScript之Json数据格式

介绍 JavaScript Object Notation&#xff0c; js对象标注法&#xff0c;是轻量级的数据交换格式完全独立于编程语言文本字符集必须用UTF-8格式&#xff0c;必须用“”任何支持的数据类型都可以用JSON表示JS内内置JSON解析JSON本质就是字符串 Json对象和JS对象互相转化 前端…

使用 Rsync + Lsyncd 实现 CentOS 7 实时文件同步

文章目录 &#x1f300;使用 Rsync Lsyncd 实现 CentOS 7 实时文件同步前言介绍架构图&#x1f9f1;系统环境&#x1f527;Rsync配置&#xff08;两台都需安装&#xff09;关闭SELinux&#xff08;两台都需&#xff09; &#x1f4e6;配置目标端&#xff08;client&#xff09…

Android studio学习之路(六)--真机的调试以及多媒体照相的使用

多媒体应用&#xff08;语言识别&#xff0c;照相&#xff0c;拍视频&#xff09;在生活的各个方面都具有非常大的作用&#xff0c;所以接下来将会逐步介绍多媒体的使用&#xff0c;但是在使用多媒体之前&#xff0c;使用模拟器肯定是不行的&#xff0c;所以我们必须要使用真机…

Qt 资源文件(.qrc 文件)

Qt 资源文件&#xff08;.qrc 文件&#xff09;是 Qt 提供的一种机制&#xff0c;用来将文件&#xff08;如图像、音频、文本文件等&#xff09;嵌入到应用程序中&#xff0c;使得这些文件不需要依赖外部文件路径&#xff0c;而是直接打包到程序的可执行文件中。通过使用 Qt 资…

PandaAI:一个基于AI的对话式数据分析工具

PandaAI 是一个基于 Python 开发的自然语言处理和数据分析工具&#xff0c;支持问答式&#xff08;ChatGPT&#xff09;的数据分析和报告生成功能。PandaAI 提供了一个开源的框架&#xff0c;主要核心组件包含用于数据处理的数据准备层&#xff08;Pandas&#xff09;以及实现 …

【C++算法】50.分治_归并_翻转对

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; 493. 翻转对 题目描述&#xff1a; 解法 分治 策略一&#xff1a;计算当前元素cur1后面&#xff0c;有多少元素的两倍比我cur1小&#xff08;降序&#xff09; 利用单…

基于pycatia的CATIA层级式BOM生成器开发全解析

引言:BOM生成技术的革新之路 在高端装备制造领域,CATIA的BOM管理直接影响着研发效率和成本控制。传统VBA方案 虽能实现基础功能,但存在代码维护困难、跨版本兼容性差等痛点。本文基于pycatia框架,提出一种支持动态层级识别、智能查重、Excel联动的BOM生成方案,其核心突破…

Flink 1.20 Kafka Connector:新旧 API 深度解析与迁移指南

Flink Kafka Connector 新旧 API 深度解析与迁移指南 一、Flink Kafka Connector 演进背景 Apache Flink 作为实时计算领域的标杆框架&#xff0c;其 Kafka 连接器的迭代始终围绕性能优化、语义增强和API 统一展开。Flink 1.20 版本将彻底弃用基于 FlinkKafkaConsumer/FlinkK…

2025年渗透测试面试题总结- 某四字大厂面试复盘扩展 一面(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 某四字大厂面试复盘扩展 一面 一、Java内存马原理与查杀 二、冰蝎与哥斯拉原理对比&#xff08;技术演…

批量压缩 jpg/png 等格式照片|批量调整图片的宽高尺寸

图片格式种类非常的多&#xff0c;并且不同的图片由于像素、尺寸不一样&#xff0c;可能占用的空间也会不一样。文件太大会占用较多的磁盘空间&#xff0c;传输及上传系统都非常不方便&#xff0c;可能会收到限制&#xff0c;因此我们经常会碰到需要对图片进行压缩的需求。如何…

【动手学深度学习】卷积神经网络(CNN)入门

【动手学深度学习】卷积神经网络&#xff08;CNN&#xff09;入门 1&#xff0c;卷积神经网络简介2&#xff0c;卷积层2.1&#xff0c;互相关运算原理2.2&#xff0c;互相关运算实现2.3&#xff0c;实现卷积层 3&#xff0c;卷积层的简单应用&#xff1a;边缘检测3.1&#xff0…

在huggingface上制作小demo

在huggingface上制作小demo 今天好兄弟让我帮他搞一个模型&#xff0c;他有小样本的化学数据&#xff0c;想让我根据这些数据训练一个小模型&#xff0c;他想用这个模型预测一些值 最终我简单训练了一个小模型&#xff0c;起初想把这个模型和GUI界面打包成exe发给他&#xff0…