# 创建项目
# 使用vue-cli创建
// 查看vue-cli版本,确保版本在4.5.0以上
vue --version
// 安装或升级你的vue-cli版本
npm install -g @vue/cli
// 创建
vue create vue_test
# 使用vite创建
npm create vite
yarn create vite
// 使用vite4,可以兼容node16
npm create vite@4
// 或者 会多了end.d.ts用于配置环境变量
npm create vue@latest
# scss安装
- 在项目终端中输入面的命令
npm install sass -d
- 在src/assets文件夹下新建,scss文件夹(文件名称随意),在文件夹下新建main.scss文件
- 在vite.config.js文件中添加配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
scss: {
additionalData:'@import "./src/assets/sass/main.scss";'
}
}
}
})
# 安装路由
- 在终端输入选择的命令
npm install vue-router@next
- 在src下新建ruter文件夹。在ruter文件夹下新建俩个ts文件(index.ts、routes.ts),进行路由配置
// index.ts
import { createRouter, createWebHistory } from 'vue-router'
// 导入路由页面的配置
import routes from './routes';
// 路由参数配置
const router = createRouter({
// 使用hash(createWebHashHistory)模式,(createWebHistory是HTML5历史模式,支持SEO)
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
// 始终滚动到顶部
return { top: 0 };
}
})
// 全局前置守卫,这里可以加入用户登录判断
router.beforeEach((to, from, next) => {
// 继续前进 next()
// 返回 false 以取消导航
next()
})
// 全局后置钩子,这里可以加入改变页面标题等操作
router.afterEach((to, from) => {
const _title = to.meta.title
if (_title) {
window.document.title = _title
}
})
export default router
//routes.ts
const routes = [
{
path: "/",
redirect:"/home"
},
{
path:'/home',
name:"home",
component:()=> import("../views/home.vue")
}
]
export default routes
- main.ts中引入使用
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 引入使用
import router from './router'
createApp(app).use(router).mount('#app')
# 安装axios
- 在终端输入选择的命令
npm install axios
# 项目相关命令
"scripts": {
"dev": "vite --host", // 显示IP地址
"build": "vue-tsc && vite build",
"preview": "vite preview"
}
# 更换Vue模板支持工具
# Vue模板支持的VSCode扩展是Vetur,Vue3建议换成更加友好的Vue-Official(前期是Volar)
# 生命周期
// 引入需要的
import { onMounted, onUpdated, onUnmounted } from 'vue';
export default {
setup () {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
Vue2 Vue3
beforeCreate() -> 使用 setup()
created() -> 使用 setup()
beforeMount() -> onBeforeMount() 组件挂载到节点上之前执行的函数
mounted() -> onMounted() 组件挂载完成后执行的函数
beforeUpdate() -> onBeforeUpdate() 组件更新之前执行的函数
updated() -> onUpdated() 组件更新完成之后执行的函数
beforeDestroy() -> onBeforeUnmount() 组件卸载之前执行的函数
destroyed() -> onUnmounted() 组件卸载完成后执行的函数
ativated() -> onActivated() <keep-alive>中的组件,会多出两个周期函数,被激活时执行
deactivated() -> onDeactivated() 比如从 A 组件,切换到 B 组件,A 组件消失时执行
errorCaptured() -> onErrorCaptured() 当捕获一个来自子孙组件的异常时激活钩子函数
注意
- setup()的执行要优先于beforeCreate()
- setup()中不能使用this,this是undefine
- 父子组件中,先执行子组件的方法,再执行父组件的方法
- setup()可以和生命周期函数同时使用,并且生命周期函数中可以调用setup()中的属性和方法,但是setup()不可以调用生命周期函数中的
# 常用方法
# setup方法
export default {
// props:值为对象,包含组件外部传递过来,且组件内部声明接收了的属性
// context:上下文对象
// attrs:值为对象,包含组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs
// slots:收到的插槽内容,相当于this.$slots
// emit:分发自定义事件的函数,相当于this.$emit
setup (props, context) {
let name = '三'
let age = 18
function sayHello(){
alert(`我叫${name},我${age}岁了,你好啊!`)
}
return{
name,
age,
sayHello
}
//返回一个渲染函数
// return()=>h('h1','尚硅谷')
}
}
# ref的使用
<template>
<div>
<p>{{num1}}</p>
<button @click="num1++">按钮1</button>
<p>{{num2}}</p>
<button @click="hdClick1">按钮2</button>
<p>{{objRef.num}}</p>
<button @click="hdClick2">按钮2</button>
<p ref="op">这是p标签</p>
<!--父组件可使用ref获取子组件暴露的属性-->
<Person ref="person"></Person>
</div>
</template>
<script lang="ts">
import { ref, Ref, nextTick, defineExpose } from 'vue';
export default {
setup () {
// 1、直接这样定义的不是响应式数据
let num1 = 20;
// 2、普通类型
let num2 = ref(20);
const hdClick1 = () =>{
// 上面template中依旧使用{{num}},因为vue在编译模板的时候会自动用.value获取
num2.value++;
}
// 3、对象类型
let obj = { num:30 }
let objRef = ref(obj)
const hdClick2 = () =>{
objRef.value.num++;
}
// 4、数组类型
let games = ref([{id:'01',name:'王者荣耀'},{id:'02',name:'原神'}]}
const hdClick3 = () =>{
games.value[0].name = '斗地主'
}
// 5、读取dom的内容
let op = ref(); // 读取绑定了ref属性且值为op的标签
console.log("setup",op.value); // undefined
nextTick(()=>{
console.log("setup",op.value); //这是p标签
})
// 6、可以使用在子组件上,子组件上需要使用defineExpose暴露
let person = ref()
onMounted(()=>{
console.log(person.value.val);
// 执行子组件的fn函数
person.value.fn()
})
return{
num1,
num2,
hdClick1,
objRef,
hdClick2
}
}
}
</script>
<!--子组件-->
<template>
<div>
<p>子组件</p>
</div>
</template>
<script lang="ts">
import { defineExpose } from 'vue';
let num = ref(10)
let fn = ()=>{
num1.value='我改变了子组件'
}
// 子组件使用defineExpose把那个变量暴露出去
defineExpose({num,fn})
</script>
注意
- 基本类型的数据:响应式依然是靠
object.defineProperty()
的get与set 完成的 - 对象类型的数据:内部"求助"了Vue3.0中的一个新函数
reactive
函数
# reactive的使用
<template>
<div>
<p>{{objRet.num}}</p>
</div>
</template>
<script lang="ts">
import { ref,reactive } from 'vue'
export default {
setup () {
let obj = { num:30 }
let objRet = reactive(obj)
// 1、不需要写value
console.log(objRet.num)
interface person {
id:number;
name:string;
age:number;
}
// 2、可以传递泛型
let personList = reactive<Persons>([
{id:'01', name:'张三', age:18},
{id:'02', name:'李四', age:20},
{id:'03', name:'王五', age:22}
])
// 3、reactive重新分配一个新对象,会失去响应式
function change(){
obj = { num:10 }
// 可以使用 0bject.assign 去整体替换
object.assign(obj,{num:10})
// 但是ref转换的可以直接更新
let objRet = ref(obj)
obj = { num:10 }
}
// 4、reactive定义的响应式数据是"深层次的"
// 当访问obj.b.c的时候,底层会自动读取value属性,因为c是在obj这个响应式对象中
let obj = reactive({
a:1,
b:{
c:1
}
})
obj.b = 2 // 可以直接修改
return{
objRet
}
}
}
</script>
ref和reactive的区别
- ref:可以定义基本类型、对象类型的数据 => RefImpl,对象类型的数据可以直接赋值修改
- reactive:只能定义对象类型的数据 => Proxy(Object),数据修改需要使用object.assign
- ref 创建的变量必须使用.value
- reactive 重新分配一个新对象,会失去响应式(可以使用 Object.assign 去整体替换)
# toRefs、toRef的使用
<template>
<div>
<p>{{num}}</p>
<button @click="hdClick">按钮</button>
</div>
</template>
<script lang="ts" setup>
import { reactive,toRefs } from 'vue';
let obj = {
num:30,
list:[10,20,30,40]
}
// 1、这样得到的num和list不具备响应式
let { num,list } = reactive(obj);
// 2、在解构reactive()得到的对象的时候,将他们解构成响应式数据
let { num,list } = toRefs(reactive(obj));
// 3、toRef可以解构单个参数
let num = toRef(reactive(obj),'num');
const hdClick = () =>{
num.value++;
console.log(num.value);
}
</script>
# shallowRef与shallowReactive
- shallowRef:创建一个响应式数据,但只对顶层属性进行响应式处理,可以提高性能,只能写到.value
import {ref,shallowRef} from 'vue'
let sum = shallowRef(10)
let person = shallowRef({
name:'zhangsan',
age:18
})
//可以修改
function changeSum(){
sum.value +=1
}
//不可以修改
function changeName(){
person.value.name = 'lisi'
}
//不可以修改
function changeAge(){
person.value.age = 20
}
//可以修改
function changePersom(){
person.value = { name: 'tony', age: 100}
}
- shallowReactive:创建一个响应式数据,但只对顶层属性进行响应式处理,可以提高性能
import {ref,shallowReactive} from 'vue'
let car = shallowReactive({
brand:'zhangsan',
optons:{
color: 'red',
engine:'v8'
}
})
//可以修改
function changeBrand(){
car.brand = 'lisi'
}
//不可以修改
function changeColor(){
car.optons.color = 'green'
}
//不可以修改
function changeEngine(){
car.optons.engine = 'v12'
}
# toRaw、UnwrapRef和markRaw
- toRaw:用于获取一个响应式对象的原始对象,返回的对象不再是响应式的
import {reactive,toRaw} from 'vue'
let person1 = reactive({
name:'zhangsan',
age:18
})
let person2 = toRaw(person1)
- UnwrapRef:用于获取 ref、reactive 创建的对象的原始类型
import { ref, reactive, UnwrapRef } from 'vue';
const count = ref(0); // 创建一个 ref
const person = reactive({ name: 'John', age: 30 }); // 创建一个 reactive
// 创建一个接口
interface FormState {
count: number
age: number
}
// 使用 UnwrapRef 获取原始值
type CountType = UnwrapRef<typeof count>; // number
type PersonType = UnwrapRef<typeof person>; // { name: string, age: number }
const formState: UnwrapRef<FormState> = reactive({
count: 5000,
age: 120
})
- markRaw:标记一个对象,使其永远不会变成响应式的
import {reactive,markRaw} from 'vue'
let car = { brand:'奔驰',price:100}
let car1 = markRaw(car)
//car2不再是像是响应式数据
let car2 = reactive(car1)
# customRef 自定义的ref
可以对其依赖项修改和展示进行逻辑控制
let initValue = '你好'
// track(跟踪)、trigger(触发)
let msg = customRef((trace,trigger)=>{
return {
// 数据被读取时触发
get(){
trace() // //告诉vue数据msg很重要,你要对msg进行持续关注,旦msg变化就去更新
return initValue
}
// set何时调用?- msg被修改时触发
set(value){
initValue = value
trigger()
}
}
})
# computed的使用
<template>
<div>
<p>{{num1}}</p>
<button @click="num1++">按钮1</button>
<p>{{num2}}</p>
<button @click="num2++">按钮2</button>
</div>
</template>
<script lang="ts" setup>
import { reactive,toRefs,computed } from 'vue';
// 1、简单数据使用
let num = ref(20);
let num1 = computed(()=>{
return num.value *2;
})
// 2、复杂数据使用
let obj = {
num:30
}
let objRet = reactive(obj);
let num2 = computed(()=>{
return objRet.num *2;
})
// 3、设置get和set
let data = reactve({
checkList:[false,false,false,false]
})
let {list,checkList} = toRefs(data);
let checkAll = computed({
get(){
// checkList 包含有一个false,就应该返回false
return !data.checkList.includes(false);
},
set(newVal){
// 把checkList的所有值都改成newVal
data.checkList=data.checkList.map(()=>newVal);
}
})
</script>
# watch的使用
Vue3 中的 watch 只能监视以下四种数据:
- ref定义的数据
- reactive 定义的数据
- 一个函数返回值
- 一个包含上述内容的数组
<template>
<div>
<p>{{num}}</p>
<button @click="num++">按钮</button>
</div>
</template>
<script lang="ts" setup>
import { reactive,toRefs,watch } from 'vue';
// 1、ref定义的简单数据
let num = ref(20);
const myWatch = watch(num,(newVal,oldVal)=>{ // 不需要加value
console.log(newVal,oldVal);
// 停止监视
if (newVal>0) {
myWatch()
}
})
// 2、ref定义的复杂数据
let obj = { num:30 }
const myWatch = watch(obj,(newVal,oldVal)=>{
console.log(newVal,oldVal);
// 停止监视
if (newVal>0) {
myWatch()
}
},
{ deep:true }, // 开启深度检测
{ immediate:true } // 是否立即执行
)
// 3、reactive 定义的数据的全部属性
let obj = { num:30 }
let objRet = reactive(obj);
// 默认开启深度检测
watch(objRet,(newVal,oldVal)=>{
console.log(newVal,oldVal);
})
// 4、一个函数返回值,用于监视reactive对象的一个属性
watch(()=>objRet.num,(newVal,oldVal)=>{
console.log(newVal,oldVal);
})
// 如果num是个对象,需要加deep
watch(()=>objRet.num,(newVal,oldVal)=>{
console.log(newVal,oldVal);
},{ deep:true })
// 5、包含上述内容的数组
watch([num, age],(newVal,oldVal)=>{ // 简单类型
console.log(newVal,oldVal); // 返回2个数组
})
watch([()=>objRet.num, ()=>objRet.age],(newVal,oldVal)=>{ // 复杂类型
console.log(newVal,oldVal); // 返回2个数组
})
// 6、watchEffect的使用
watchEffect(()=>{
// 凡是写在这里的数据,只要发生变化,都会触发这里的代码执行
console.log(objRet.num);
})
</script>
注意
- 若修改的是 ref 定义的对象中的属性,newValue 和 oldValue 都是新值,因为他们是同一个对象
- 若修改整个 ref 定义的对象,newValue 是新值,oldValue 是旧值,因为不是同一个对象了
- reactive 定义的响应式数据,newvalue 和 oldvalue 是一个值
# hooks的使用
将文件的一些单独功能的代码抽离出来,放到一个js文件中,或者是一些可以复用的公共方法/功能。
其实 hooks 和 vue2 中的 mixin 有点类似,但是相对 mixins 而言, hooks 更清晰易懂。
// 在hooks文件夹中新建一个文件useMousePosition.ts
import { ref, onMounted, onUnmounted, Ref } from 'vue'
interface MousePosition {
x: Ref<number>,
y: Ref<number>
}
function useMousePosition(): MousePosition {
const x = ref(0)
const y = ref(0)
const updateMouse = (e: MouseEvent) => {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
document.addEventListener('click', updateMouse)
})
onUnmounted(() => {
document.removeEventListener('click', updateMouse)
})
return { x, y }
}
//向外部提供
export default useMousePosition
- 在需要用到该hook功能的组件中的使用
<!--src/views/test.vue-->
<template>
<div>
<p>X: {{ x }}</p>
<p>Y: {{ y }}</p>
</div>
</template>
<script lang="ts" setup>
// 引入hooks
import useMousePosition from '../../hooks/useMousePosition'
// 使用hooks功能
const { x, y } = useMousePosition()
</script>
# Teleport的使用
可以把里面的内容传送到指定标签最后的位置
<template>
<Teleport to=".app">
<Teleport to="body">
<Teleport to="#aaa">
<p>这是一个P标签</p>
</Teleport>
</template>
# Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child />
</template>
<template v-slot:fallback>
<h3>稍等,加载中...</h3>
</template>
</Suspense>
</div>
</template>
<script>
// 静态引入
// import { defineAsyncComponent,Suspens } from "vue";
// 异步引入
import Child from './components/Child'
const Child = defineAsyncComponent(() => import("./components/Child"));
export default {
name: "App",
components: { Child },
};
</script>
<!----------子组件---------->
<template>
<div class="child">
<h3>我是Child组件</h3>
{{ sum }}
</div>
</template>
<script setup lang="ts">
let sum = ref(0)
let p = new Promise((resolve) => {
setTimeout(() => {
resolve({ sum })
}, 3000)
})
</script>
注意
setup 默认带着async,类似:async setup() {}
# 动态创建组件并传值
// comp 传入的vue组件
// para 传入的vue组件的参数
// 返回:HTMLElement
export function initVue3Popup(comp, para) {
const vNodeDom = document.createElement("div")
document.body.appendChild(vNodeDom)
const vNode = createApp(comp, { ...para }) // vue2中可使用extend
vNode.mount(vNodeDom)
return vNode._container
}
<!--被传入的组件-->
<template>
<div>我是传入的参数:{{ props.remark }}</div>
</template>
<script setup lang="ts">
// 与传入的参数一一对应
const props = defineProps<{
remark?: string
}>()
</script>
# 组件通信
# props通信(父子传值)
- 父传子
<!----------父组件---------->
<template>
<Chid :arr="state.arr" a="哈哈"></Chid>
</template>
<script lang="ts" setup>
import Child from './Child.vue';
import { reactive,toRefs,ref } from 'vue';
let state =reactive({
arr:[{
name:'小明',
age:18
},{
name:'小红',
age:20
}]
});
</script>
<!----------子组件---------->
<template>
<table>
<tr v-for="item,index in arr" :key="index">
<td>{{(item as {name:string}).name}}</td>
<td>{{(item as {age:number}).age}}</td>
</tr>
</table>
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
defineProps([
arr:{
type:Number,
default:[]
},
// 可以接收固定值
a
])
// 接收a,同时将props保存起来
let x= defineProps(['a'])
console.log(x)
</script>
- 子传父(子组件调用父组件的方法)
<!----------父组件---------->
<template>
<Chid :sendToy="getToy"></Chid>
</template>
<script lang="ts" setup>
import Child from './Child.vue';
import { reactive,toRefs,ref } from 'vue';
let toy =ref('');
const getToy = (value:string) =>{
toy.value = value
}
</script>
<!----------子组件---------->
<template>
<p>玩具:{{toy}}</p>
<button @click="sendToy(toy)">按钮</button>
</template>
<script lang="ts" setup>
import { defineProps,ref } from 'vue';
let toy =ref('奥特曼');
defineProps(['sendToy'])
</script>
# 自定义事件(主要用于子传父)
<!----------父组件---------->
<template>
<Chid @sendToy="getToy"></Chid>
</template>
<script lang="ts" setup>
import Child from './Child.vue';
import { ref } from 'vue';
let toy =ref('');
const getToy = (value:string) =>{
toy.value = value
}
</script>
<!----------子组件---------->
<template>
<p>玩具:{{toy}}</p>
<button @click="emit('sendToy',toy)">按钮</button>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
let toy =ref('奥特曼');
// 声明事件
const emit = defineEmits(['sendToy'])
// 如果不在template中写emit('sendToy',toy),在script代码中
emit('sendToy',toy)
</script>
# mitt通信(子组件间通信)
- 安装mitt
npm i mitt
- 在uitls文件夹中创建文件emitter.ts
// 引入mitt
import mitt from 'mitt'
// 调用mitt得到emitter,emitter能绑定事件、触发事件
const emitter = mitt()
// 绑定事件
emitter.on('test1',()=>{
console.log('test1被调用了')
})
emitter.on('test2',()=>{
console.log('test2被调用了')
})
// 触发事件
setInterval(() => {
emitter.emit('test1')
emitter.emit('test2')
}, 1000)
// 卸载事件
setTimeout(() => {
emitter.off('test1')
emitter.off('test2')
emitter.all.clear()
}, 3000)
// 暴露emitter
export default emitter
- 在main.js中导入emitter
import emitter from '@/utils/emitter'
- 子组件间通信
<!----------子组件1---------->
<template>
<div class="child1">
<h3>子组件1</h3>
<h4>玩具:{{ toy }}</h4>
<button @click="emitter.emit('sendToy',toy)">按钮</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import emitter from '@/utils/emitter'
// 数据
let toy = ref('奥特曼')
</script>
<!----------子组件2---------->
<template>
<div class="child2">
<h3>子组件2</h3>
<h4>电脑:{{ computer }}</h4>
<h4>哥哥给的玩具:{{ toy }}</h4>
</div>
</template>
<script setup lang="ts">
import { ref,onUnMounted } from 'vue'
import emitter from '@/utils/emitter'
// 数据
let computer = ref('联想')
let toy = ref('')
// 给emitter绑定sendToy事件
emitter.on('sendToy',(value)=>{
toy.value = value
})
// 卸载事件
onUnMounted(() => {
emitter.off('sendToy')
})
</script>
# v-modle通信(父子通信)
<!----------父组件---------->
<template>
<Child v-model:num="num"></Child>
<!--本质:<Child :num="num" @update:num="num = $event"></Child>-->
</template>
<script lang="ts" setup>
import Child from './Child.vue';
import { reactive,toRefs,ref } from 'vue';
let num =ref(20);
</script>
<!----------子组件---------->
<template>
<p>{{num}}</p>
<button @click="hdClick">按钮</button>
</template>
<script lang="ts" setup>
import { defineProps,defineEmits } from 'vue';
const props = defineProps({
num:{
type:Number,
default:30
}
})
// 子传父的时候需要先定义好emit这个方法
const emit =defineEmits<{
// update是固定写法,后面的变量是父组件v-model后面这个变量
// n是参数
(event: 'update:num',n:number):void
}>()
let m = props.num
const hdClick = () =>{
m++;
// $emit('上面event的值',要修改成的值)
emit("update:num",m)
}
</script>
$event到底是啥?啥时候能.target
- 对于原生事件,$event就是事件对象 =====> 能.target
- 对于自定义事件,$event就是触发事件时,所传递的数据 =====> 不能.target
# $attrs通信(祖孙通信)
$attrs 可以获取绑定的其他值,作为中间组件传值
<!----------父组件---------->
<template>
<div class="father">
<h3>父组件</h3>
<h4>a:{{ a }}</h4>
<h4>b:{{ b }}</h4>
<h4>c:{{ c }}</h4>
<h4>d:{{ d }}</h4>
<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}">
</div>
</template>
<script setup lang="ts">
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:number){
a.vlue += value
}
</script>
<!----------子组件只是中间传递---------->
<template>
<div class="child2">
<h3>子组件</h3>
<h4>a:{{ a }}</h4>
<h4>其他:{{ $attrs }}</h4>
<GrandChild v-bind="$attrs"></GrandChild>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import GrandChild from './GrandChild.vue'
defineProps(['a'])
// 输出a:1 b:2 c:3 d:4 x:100 y:200
console.log($attrs)
</script>
<!----------孙组件---------->
<template>
<div class="grandChild">
<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(6)">更新爷爷那的A</button>
</div>
</template>
<script setup lang="ts">
defineProps(['a','b','c','d','x','y','updateA'])
</script>
# useAttrs 父传子
使用useAttrs函数可以接收父组件传递的属性和事件
<!----------父组件---------->
<template>
<div>
<MyComponent title="标题" content="内容" @click="handleClick" />
</div>
</template>
<script>
import MyComponent from '@/components/MyComponent.vue'
export default {
components: { MyComponent },
methods: {
handleClick() {
console.log('点击事件触发')
}
}
}
</script>
<!----------子组件---------->
<template>
<div>
<h2>{{ title }}</h2>
<p>{{ content }}</p>
<button @click="onClick">点击</button>
</div>
</template>
<script setup>
import { useAttrs } from 'vue'
export default {
setup() {
const { title, content, onClick } = useAttrs()
return { title, content, onClick }
}
}
</script>
# $refs(父传子)、$parent(子传父)
- $refs:值为对象,包含所有被 ref 属性标识的 DOM 元素或组件实例
- $parent:值为对象,当前组件的父组件实例对象
<!----------父组件---------->
<template>
<div class="father">
<h3>父组件</h3>
<h4>房产:{{ house }}</h4>
<button @click="changeToy">修改Child1的玩具</button>
<button @click="changeComputer">修改Child2的电脑</button>
<button @click="getAllChild($refs)">获取所有的子组件实例对象</button>
<h4>c:{{ c }}</h4>
<h4>d:{{ d }}</h4>
<Child1 ref="c1">
<Child2 ref="c2">
</div>
</template>
<script setup lang="ts">
import Child1 from './Child1.vue'
import Child2 from './Child1.vue'
import { ref } from 'vue'
// 数据
let c1 = ref()
let c2 = ref()
let house = ref(4)
// 方法
function changToy(){
c1.vlue.toy ='小猪佩奇'
}
function changeComputer(){
c2.vlue.toy ='华为'
}
function getAllChild(refs:any){ // 或者 getAllChild(refs:{[key:string]:any})
// refs为所有的ref的集合
for(let key in refs){
refs[key].book +=3
}
}
// 把数据暴露给外部
defineExprose({house})
</script>
<!----------子组件1---------->
<template>
<div class="Child">
<h3>子组件1</h3>
<h4>玩具:{{ toy }}</h4>
<h4>书籍:{{ book }} 本</h4>
<button @click="minusHouse($parent)">干掉父亲的一套房产</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
let toy = ref('奥特曼')
let book = ref(3)
function minusHouse(parent:any){
parent.house -=1
}
// 把数据暴露给外部
defineExprose({toy,book})
</script>
# provide、inject(祖孙传值\父子传值)
<!----------父组件---------->
<template>
<div class="father">
<h3>父组件</h3>
<h4>银子:{{ money }}</h4>
<h4>车子:品牌{{ car.brand }} 价格 {{ car.price }}</h4>
<Child></Child>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, provide } from 'vue'
import Child from './Child.vue'
let money = ref(100)
let car = reactive({
brand:'奔驰',
price:100
})
function updateMoney(value:number){
money.value -= value
}
// 向后代提供数据,money不需要加value
provide('moneyContext',money,updateMoney)
provide('che',car)
</script>
<!----------子组件---------->
<template>
<div class="child">
<h3>子组件</h3>
<h4>银子:{{ money }}</h4>
<h4>车子:品牌{{ car.brand }} 价格 {{ car.price }}</h4>
<button @click="updateMoney(6)">花爷爷的钱</button>
</div>
</template>
<script setup lang="ts">
import { inject } from 'vue'
let {money, updateMoney} = inject('moneyContext',{money:0,updateMoney:(param:number)=>{}})
// 设置默认值,否则ts报错
let car = inject('che',{brand:'',price:0})
// 设置为null
let car = inject('che',null)
</script>
InjectionKey 可以解决子组件调用 inject 引入数据时也无法确定具体的数据类型是什么
// 1. 将 InjectionKey 定义的数据类型放到 keys/index.ts 下维护
// keys/index.ts
import {InjectionKey, Ref } from "vue"
// 限制了 provide 导出的数据必须是 ref 且 boolean 类型
export const showPopupKey: InjectionKey<Ref<boolean>> = Symbol()
// 限制了 provide 导出的数据必须是 string
export const titleKey: InjectionKey<string> = Symbol()
// 2. 在A.vue中调用 provide 导出数据,第一个参数是我们定义好的数据类型,第二个参数是数据类型的值
import { provide, InjectionKey, Ref } from "vue"
import { showPopupKey } '@/keys'
const showPopup = ref(false)
// 正确
provide(showPopupKey, showPopup)
// TS 报错: 'Hello' 是字符串,与 showPopupKey 不匹配
provide(showPopupKey, 'Hello')
// 正确
provide(titleKey, 'Hello')
// 3. 在B.vue文件中导入数据
import { showPopupKey } from '@/keys'
import { inject } from 'vue'
inject(showPopupKey) // 现在获取到的数据类型是安全的
# 插槽用法
# 匿名插槽
<template>
<Child>
<a href="#">a标签</a>
<button></button>
</Child>
</template>
<script lang="ts" setup>
import Child from "./Child.vue"
</script>
<template>
<p>子组件</p>
<slot></slot>
</template>
<script lang="ts" setup>
</script>
# 具名插槽
<template>
<Child>
<!--简写:<template #link>-->
<template v-slot:link>
<a href="#">a标签</a>
</template>
<!--简写:<template #btn>-->
<template v-slot:btn>
<button></button>
</template>
</Child>
</template>
<script lang="ts" setup>
import Child from "./Child.vue"
</script>
<template>
<slot name="link"></slot>
<p>子组件</p>
<slot name="btn"></slot>
</template>
<script lang="ts" setup>
</script>
# 插槽作用域
<template>
<Child>
<template #link>
<a href="#">a标签</a>
</template>
<template #btn="scope">
<button>按钮 {{scope.title}}{{scope.num}}</button>
<li v-for="item in scope.games" :key="item.id">{{item.name}}</li>
</template>
</Child>
</template>
<script lang="ts" setup>
import Child from "./Child.vue"
</script>
<template>
<slot name="link"></slot>
<p>子组件</p>
<slot name="btn" :youxi="games" title="哈哈" :num="num"></slot>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
let num =ref(30)
let games = reactive({
id: "001", name: "英雄联盟",
id: "002", name: "王者荣耀"
})
</script>
注意
插槽作用域的数据在子那边,但根据数据生成的结构,却有父亲决定。
# 路由使用
# query参数
<!--第一种方式-->
<router-link :to="`news/details?id=${id}&title=${title}`">首页</router-link>
<!--第二种方式-->
<router-link :to="{
path:'news/detail',
query:{
id:id,
title:title
}
}">首页</router-link>
<!--路由配置中-->
routes:[{
name:'xiangqing',
path:'detail',
component:import('../views/Detais.vue')
}]
<!--被链接页面-->
<template>
<div>{{query.id}}</div>
<div>{{query.title}}</div>
</template>
<script setup lang="ts">
import {toRefs} from 'vue'
import {useRoute} from 'vue-router'
let route = userRoute()
let {query} = toRefs(route)
</script>
# params参数
<!--第一种方式-->
<router-link :to="`news/details/${id}/${title}`">首页</router-link>
<!--第二种方式-->
<router-link :to="{
name:'xiangqing'
params:{
id:id,
title:title
}
}">首页</router-link>
<!--路由配置中-->
routes:[{
name:'xiangqing',
path:'detail/:x/:y',
component:import('../views/Detais.vue')
}]
<!--被链接页面-->
<template>
<div>{{params.id}}</div>
<div>{{params.title}}</div>
</template>
<script setup lang="ts">
import {toRefs} from 'vue'
import {useRoute} from 'vue-router'
let route = userRoute()
let {params} = toRefs(route)
</script>
注意
params时to里只能写name,query时name和path都可以
# props的使用
- 第一种写法:将路由收到的所有 params 参数作为props传给路由组件
// 1、路由配置中 props:true
routes:[{
name:'xiangqing',
path:'detail',
component:import('../views/Detais.vue')
props:true
}]
<!--被链接页面-->
<template>
<div>{{id}}</div>
<div>{{title}}</div>
</template>
<script setup lang="ts">
import {defineProps} from 'vue'
// 2、接收Props
defineProps([
{
id:Number,
default:0
},
{
title:String,
default:''
}
])
</script>
- 第二种写法:函数写法,将路由所有参数作为props给路由组件
// 1、路由配置中
routes:[{
name:'xiangqing',
path:'detail',
component:import('../views/Detais.vue')
props(route){
// return route.params
return route.query
}
}]
<!--被链接页面-->
<template>
<div>{{id}}</div>
<div>{{title}}</div>
</template>
<script setup lang="ts">
import {defineProps} from 'vue'
// 2、接收Props
defineProps([
{
id:Number,
default:0
},
{
title:String,
default:''
}
])
</script>
- 第三种写法:对象写法,可以自己决定将什么作为props给路由组件
// 1、路由配置中
routes:[{
name:'xiangqing',
path:'detail',
component:import('../views/Detais.vue')
props:{
a:100,
b:200,
c:300
}
}]
<!--被链接页面-->
<template>
<div>{{a}}</div>
<div>{{b}}</div>
</template>
<script setup lang="ts">
import {defineProps} from 'vue'
// 2、接收Props
defineProps([a,b,c])
</script>
# replace的使用
页面路由默认是push的规则,浏览器中的页面可以前进或者后退,使用replace后则不可以
<RouterLink replace to="/home" active-class="active">首页</RouterLink>
<RouterLink replace :to="{path:'/home'}" active-class="active">首页</RouterLink>
<RouterLink replace :to="{name:'shouye'}" active-class="active">首页</RouterLink>
# 编程式路由
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
onMounted(()=>{
setTimeOut(()=>{
//注意push和replace的区别
router.push('/news')
//to有几种写法这里就有几种写法
router.push({
path:'news/detail',
query:{
id:id,
title:title
}
})
},3000)
})
# 状态管理
# Vuex4
# 现在默认是vuex3,所以加上next安装下一个版本
npm install vuex@next
# package.json文件中,查看版本
"dependencies": {
"vuex": "^4.0.2"
},
# 创建一个store对象
// 接收一个泛型,泛型的名称为 S(对 state的类型进行限定)
// 接收一个参数,参数为对象,类型为StoreOptions(包含:state/getters/actions/mutations/modules)
// 返回一个Store类的实例对象
export function createStore<S>(options: StoreOptions<S>): Store<S>
// 创建一个store实例对象,并且其类型定义为IRootState
import { createStore } from 'vuex'
interface IRootState { count: number }
const store = createStore<IRootState>({ })
export default store
// 注册到项目的APP中
import { createApp } from 'vue'
import store from '@/store'
const app = createApp(App)
app.use(store)
app.mount('#app')
// 这样就可以直接在各个组件中使用store了
# 核心模块之state
state可以是一个函数,也可以是一个对象
// 定义state的类型限定
const store = createStore<IRootState>({
state() {
return {
// 创建的时候,已经给state的类型进行了限定
count: 0
}
}
})
- 第一种展示形式
// 直接在组件中使用$store
<h1>{{$store.state.count}}</h1>
// 但是ts是对$store不认识的,就会报警告
// 在vue3创建的项目中,有一个文件shims-vue.d.ts
// 使用.d.ts来申明一下$store, 就可解决了
declare let $store: any
- 第二种展示形式
import { defineComponent, computed} from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
name: 'App',
setup() {
// useStore 是vuex内部提供的,因为在setup中是不能使用this的,不能像vue2中使用this.$store
const store = useStore()
const count = computed(() => store.state.count)
return {
count
}
}
})
// 在template中展示
<h1>{{count}}</h1>
# 核心模块之getters
getters的作用就是对state进行变形后,然后进行返回展示
interface IRootState {
coder: any[]
}
const store = createStore<IRootState>({
state() {
return {
coder: [
{name: '前端人员', number: 5},
{name: '后端人员', number: 15},
{name: '测试人员', number: 3},
{name: '产品', number: 2}
]
}
},
getters: {
coderAllCount(state) {
return state.coder.reduce((prev, item) => {
return prev + item.number
}, 0)
}
}
})
export default store
在组件中展示,跟state的两种形式是一样的
//形式一
<h1>{{$store.getters.coderAllCount}}</h1>
//形式二
const store = useStore()
const coderAllCount = computed(() => store.getters.coderAllCount)
# 核心模块之mutations
mutation中的必须是同步代码,是更改vuex中的store的状态唯一方法
const store = createStore<IRootState>({
state() {
return {
count: 0
}
},
mutations: {
increment(state, payload) {
state.count += payload.count
}
}
})
在组件中触发(例如:点击事件)
// 字符串形式
const btn = () => {
store.commit('increment', {count: 10})
// 或则
store.commit(INCREMENT, {count: 10})
}
// 对象的形式,payload是指向传递的整个对象
const btn = () => {
store.commit({
type: 'increment',
count: 10
})
// 或则
store.commit({
type: INCREMENT,
count: 10
})
}
# 核心模块之actions
action就是专门用来处理异步的,当异步执行完成之后,触发mutation,从而修改state的状态
# actions中的方法,一般接收两个参数,第二个参数为可选参数
# 参数一: context(必选参数)是一个对象,包含以下属性:
# commit:触发mutations中的方法,修改state
# dispatch: 触发actions中的方法
# state: 拿到当前模块中的state中的值
# getters: 拿到当前模块中的getters中的值
# rootState: 拿到根节点中的state值
# rootGetters: 拿到根节点中的getters中的值
# 参数二: payload(可选参数)是否给action携带参数
const store = createStore<IRootState>({
state() {
return {
count: 0
}
},
mutations: {
increment(state, payload) { //这里的state的类型可以自动推导为IRootState
state.count += payload.count
}
},
actions: {
increment(context, payload) {
setTimeout(() => {
context.commit({
type: 'increment',
count: payload.count
})
}, 1000)
}
}
})
没有携带参数
const store = useStore()
cosnt btn = () => {
// 在这里就没有携带参数,所以payload是不存在的
store.dispatch('increment')
}
携带参数
const store = useStore()
// 两种形式: 直接传递参数形式 和 对象的形式
cosnt btn = () => {
// 直接传递参数形式
store.dispatch('increment', {count: 100})
}
// payload ==> {count: 100}
cosnt btn = () => {
// 对象的形式
store.dispatch({
type: 'increment',
count: 100
})
}
// payload ==> {type: 'increment', count: 100}
# 核心模块之module
当应用变得非常复杂时,store 对象就有可能变得相当臃肿,就需要对模块进行划分
- 定义一个countModule模块
import { Module } from 'vuex'
// 接收两个泛型,第一个S 为当前模块的state的类型,第二个R:就是跟节点的state类型
// Module类型跟StoreOptions的类型基本是一样的,就是多了一个namespaced属性
const countModule: Module<ICountState, IRootState> = {
state() {
return {
count: 0
}
},
getters: {},
mutations: {
increment(state) {
console.log('模块store中的increment方法')
state.count += 1
}
},
actions: {}
}
export default countModule
- 在根store中注册模块
import { createStore } from 'vuex'
import countModule from './count/count'
const store = createStore<IRootState>({
state() {
return {
count: 0
}
},
mutations: {
increment(state) {
console.log('根store中的increment方法')
state.count += 1
}
},
modules: {
countModule
}
})
- 根store中的state和模块中的state的区分
<!--渲染根store-->
<div>{{$store.state.rootCount}}</div>
<!--正确渲染count模块中的count-->
<div>{{$store.state.countModule.count}}</div>
<!--不是下面的写法-->
<div>{{$store.countModule.state.count}}</div>
- 根store中的mutation和模块中的mutation的区分
import { useStore } from 'vuex'
export default defineComponent({
setup() {
const store = useStore()
const btn = () => {
store.commit('increment')
}
}
})
- 命名空间的使用
# 默认情况下,模块内的getters、action、mutations是注册在全局的命名空间中的
# 所以上面的方法commit中同一个名字的方法都会被触发,无论是模块中的还是根store中的
# 可以添加 namespaced: true 的方式使其成为独立的空间模块
//state (当然原来也是这么访问的)
store.state.countModule.count
//getters
store.getters['countModule/coderAllCount']
//mutations
store.commit('countModule/increment')
//actions
store.dispatch('countModule/incrementAction')
- 模块内部触发根store中的mutation
// 模块内部的action异步拿到数据后,想修改根store中的mutation
// action的类型里有一个类型是专门针对于模块的
// 这里有个root属性,就是用来告诉是否用来修改根store中的mutation
actions: {
incrementAction({commit}, payload) {
setTimeout(() => {
//默认情况下,就是触发模块内部mutation中的increment
commit('increment', payload)
//加上root:true, 那么这时候触发的就是根store中的increment了
commit('increment', payload, {root: true})
}, 1000)
}
}
# Pinia
Pinia官网 (opens new window) 符合直觉的 Vue.js 状态管理库
# 环境搭建
- 安装Pinia
npm install pinia
- 修改main.js
import { createApp } from 'vue'
import App from './App.vue'
//第一步:引入pinia
import { createPinia } from 'pinia'
const app = createApp(App)
//第二步:创建pinia
const pinia = createPinia()
//第三步:安装pinia
app.use(pinia)
app.mount('#app')
# 数据读取和修改
- 创建store文件夹并添加文件count.ts
import { defineStore } from 'pinia'
export const useCountStore = defineStore('count',{
//actions里放置的是一个个的方法,用来对 state 里数据变化的业务逻辑
//不但能处理同步操作 同样也可以处理异步操作
actions:{
increment(value){
//修改数据
console.log(this.sum)
this.sum += value
}
}
//存储数据的地方,用来存储全局状态
state(){
return {
sum: 6,
name:'lisi'
}
},
//可以用于计算或转换存储的状态
getters:{
bigSum:state => state.sum*10
upperName():string{
return this.name.toUpperCase()
}
}
})
- 数据读取和修改
<template>
<div>{{countStore.sum}}</div>
<button @click='add'>修改</button>
</template>
<script setip lang="ts">
import { useCountStore } from @/store/count
counst countStore = useCountStore()
//数据读取
console.log(countStore.sum)
function add(){
//第一种修改方式:单个数据修改
countStore.sum += 1
//第二种方式:批量修改
countStore.$patch({
sum: 888
name: '张三'
})
//第三种方式:使用actions的方法
countStore.increment(1)
}
//storeToRefs只会关注store中的数据,不会对方法进行ref包裹
const {sum,name,bigSum,upperName} = storeToRefs(countStore)
//$subscribe--监听数据修改
countStore.$subscribe((mutate,state)=>{
console.log("params",params);//修改的操作
console.log("state",state);//修改后的数据
})
</script>
# SVG文件
# IconPark图标以及按需引入
npm i @icon-park/svg --save 安装
<!--子组件-->
<template>
<span class="mars-icon" v-html="svgComponent"></span>
</template>
<script lang="ts">
import { computed, useAttrs, defineComponent } from "vue"
import * as svgModule from "@icon-park/svg"
import _ from "lodash"
export default defineComponent({
name: "mars-icon",
props: {
icon: {
type: String
},
color: {
type: String
},
width: {
type: [String, Number],
default: "14"
}
},
setup(props) {
const attrs = useAttrs()
const iconName = computed(() => _.upperFirst(_.camelCase(props.icon)))
const svgComponent = svgModule[iconName.value]({
theme: "outline",
fill: props.color,
size: props.width,
...attrs
})
return {
attrs,
svgComponent
}
}
})
</script>
<style lang="less" scoped>
.mars-icon {
vertical-align: middle;
line-height: 1;
}
</style>
<!--父组件-->
<template>
<mars-icon icon="close-one" width="20" color="#FFFFFF"></mars-icon>
</template>
# 如何使用svg-sprite-loader
- 普通的svg图片使用方式
<img src="../assets/svgicons/about.svg" />
- 封装组件使用
// 依赖安装:npm i svg-sprite-loader -D
// 打开vue.config.js文件,在chainWebpack函数中新增配置
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
chainWebpack: (config) => {
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
// 添加要替换的 loader
svgRule.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({symbolId: 'icon-[name]'})
}
})
在项目src/component目录下新增SvgIcon文件夹,里面定义一个index.vue以及index.ts文件
<!--index.vue-->
<template>
<svg class="svg-icon">
<use :xlink:href="`#icon-${props.name}`" />
</svg>
</template>
<script setup lang="ts">
type Props = {
name: string
}
const props = withDefaults(defineProps<Props>(), {
name: ''
})
</script>
<style lang="scss" scoped>
.svg-icon {
display: inline-block;
width: 1em;
height: 1em;
overflow: hidden;
vertical-align: -0.15em;
fill: currentColor;
}
</style>
// index.ts
export default function importAllSvgIcons() {
try {
const request: __WebpackModuleApi.RequireContext =
require.context('../../assets/svgicons', false, /\.svg$/)
request.keys().forEach(request)
} catch(err) {
console.log(err)
}
}
打开main.ts文件,引入我们定义的SvgIcon.vue组件,通过app.component方式全局注册
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import SvgIcon from '@/components/SvgIcon/index.vue'
import importAllSvgIcons from './components/SvgIcon'
const app = createApp(App)
app.component('svg-icon', SvgIcon)
// 动态引入svgicons文件夹下面的所有文件的方法并调用
importAllSvgIcons()
app.use(createPinia())
app.use(router)
app.mount('#app')
在项目中任意组件中通过如下方式进行使用:
<!--1. SvgIcon为全局组件,name表示要使用的svg图标名称-->
<!--2. other.svg图标存放于assets/svgicons目录下-->
<SvgIcon name="other"></SvgIcon>
# 其他使用
# Vue2和Vue3的区别
- 响应式原理的区别:
# Vue2是Object.defineProperty
# Vue3是Proxy代理
// 模拟Vue3中实现响应式
const p = new Proxy(person, {
//有人读取p的某个属性时调用
get(target, propName){
console.log(`有人读取了p身上的${propName}属性`)
// return target[propName]
return Reflect.get(target, propName)
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target, propName, value){
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
// target[propName] = value
Reflect.set(target, propName, value)
},
deleteProperty(target,propName){
console.log(`有人刷除了p身上的${propName}属性,我要去更新界面了!`)
// return delete target[propName]
return Reflect.deleteProperty(target, propName)
}
}
- API设计的区别:
# Vue2是OptionAPI(选项式)不便于维护和复用
# Vue3是CompositionAPI(组合式)可以让相关功能更加有序的组织在一起
- VUE3基于Vite构建,按需编译,不再等待整个应用编译完成
- 在Vue2中必须有一个根标签,在Vue3中可以没有根标签,将多个标签包含在一个Fragment虚拟元素中
# env.d.ts
// 为TS提供类型定义,方便TS的智能提示
/// <reference types="vite/client" />
/// <reference types="vite-svg-loader" />
# 如何修改组件的名称
//1、设置名称
<script lang="ts" setup name="person123"></script>
//2、安装插件
vite-plugin-vue-setup-extend
//3、在vite.config.ts中添加配置
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueSetupExtend from'vite-plugin-vue-setup-extend'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
VueSetupExtend()
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "@/assets/css/main.scss";'
}
}
}
})
# 全局API由Vue转移到app
在main.js文件中
import { createApp } from 'vue'
import App from './App.vue'
// 创建应用
const app = createApp(App)
// 全局组件
import Hello from './Hello.vue'
app.component('Hello',Hello)
// 全局变量
app.config.globalProperties.website = 'sylone';
app.config.globalProperties.$user = {
name: '梅长苏',
weapons: '长剑',
title: '刺客'
}
// 如果ts提示需要定义变量类型
declare module 'vue' {
interface ComponentCustomPropertites{
website:string
}
}
// 全局自定义指令
// 使用自定义指令 <div v-beauty="sum">好漂亮</div>
app.directive('beauty',(element,{value})=>{
element.innerText +=value
element.style.color = 'green'
element.style.backgroundColor = 'yellow'
})
// 挂载应用
app.mount('#app')
// 卸载应用
setTimeout(() => {
app.unmount()
},2000)
在template模板使用全局变量
<template>
<div>
<div>姓名:{{$user.name}} </div>
<div>{{ website }}</div>
</div>
</template>
在setup中使用全局变量
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const website = app.appContext.config.globalProperties.website
// 或者
const { proxy } = getCurrentInstance()
console.log(proxy.website)
// 使用解构赋值
const { website } = getCurrentInstance()!.appContext.config.globalProperties
console.log(website)
// 注意!getCurrentInstance()不能在回调函数、方法里使用
// 若要访问全局变量,需在函数外面调用getCurrentInstance()
const { proxy } = getCurrentInstance()
// 或者
const name = getCurrentInstance().proxy.$website;
const getUserInfo=()=>{
console.log(proxy.$website);
console.log(name);
}
注意
getCurrentInstance()不能在回调函数、方法里使用
若要访问全局变量,需在函数外面调用getCurrentInstance()
# 全局接口
- 定义全局接口
// 根目录 -> types -> table.d.ts
interface UerType{
name:string;
age:number;
}
// 类型增强
declare var globalVar:string;
declare var globalObj:ObjType;
declare function fn(s:string):void;
- 修改tsconfig.json文件
{
"include":[
……
"types/**/*.d.ts"
]
}
- 在其他文件中使用
let arr = props.arr as UerType[]
// 使用类型增强
console.log(globalVar,globalObj)
# 配置项目路径别名
目前ts对@指向src目录的提示是不支持的,vite默认也是不支持的,需要手动配置@符号的指向
- tsconfig.json中添加两项配置
"compilerOptions":{
…
"baseUrl":"./",
"paths":{
// 对应src的路径
//import HelloWorld from '@/components/HelloWorld.vue'
"@/*":[
"src/*"
],
// 对应types的路径
//import { UserType } from "#/table"
"#/*":[
"types/*"
]
}
}
- 在vite.config.ts中添加配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve:{
alias:{
"@":path.join(__dirname,'src'),
"#":path.join(__dirname,'type')
}
}
})
- 需要安装关于node这个库的ts声明配置
npm i -D @type/node
# 定义接口返回值类型
interface AdminLoginData{
username:string;
password:string;
}
interface Result<T>{
code:number;
data:T;
message:string;
}
interface AdminLoginRes{
token:string
}
//如果接口定义在另外一个文件中,需要导入
import {type AdminLoginData} from "@/types/AdminLoginData"
export const adminLoginApi = (data:AdminLoginData) : Promise<Result<AdminLoginRes>> =>
request.post('/admin/login',data);
常见问题 →