vue3+ts笔记

创建项目:

npm create vue@latest

npm install

页面渲染原理:

  • vite项目中,index.html是项目的入口文件,在项目最外层。
  • 加载index.html后,vite解析 <script type="module" src="xxx"> 指向js。
  • vue3中是通过createApp函数创建一个应用实例。

选项 API 和组合 API:

vue3中开始弱化this;

setup将数据和方法交出去,模板中才可以使用,

setup的返回值也可以是一个渲染函数,

setup中不能使用this.,

setup和data,methods可以并存,一般不同时写,

但是setup读不到data中的数据,data可以读到setup的数据。

语法糖:

<script lang= "ts" setup>
	let a = '1'
</script>

(如果想修改组件名可以下载一个插件npm i vite-plugin-vue-setup-extend -D )

定义响应式数据:

<script lang= "ts" setup>
import {ref,reactive,toRefs,toRef} from 'vue'
// 使用【ref】可以定义基本类型和对象类型的响应式数据 
// 获取及赋值时记得用.value 也可以使用volar插件自动添加.value
let a = ref('1') 
// 使用【reactive】只能定义对象类型的响应式数据 
// 1,不过它无法替换整个对象 修改数据时如果重新分配了一个新的对象 则这个数据不再是响应式数据了  除非使用Object.assign(obj,{name:'姓名2',age:"182"})  如果是使用ref定义的对象类型数据就可以重新分配一个新的对象 因为有.value
// 2,reactive定义的对象类型的数据不适合解构  如果要解构 要使用【toRefs】包裹对象
let obj = reactive({name:'姓名',age:"18"}) 
// 解构
let {name,age} = toRefs(obj)
let newname = toRef(obj,'name')
// 方法
function changeName(){
    obj.name = '123' //  使用reactive
    name.value= '123' // 使用reactive和toRefs
    newname.value = '456' // 使用reactive和toRef
}
</script>

使用规则:

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

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

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

响应式的底层原理还是getter 和 setter,可以将 ref 视为:

const myRef = {
  _value: 0,
  get value() {
    track()
    return this._value
  },
  set value(newValue) {
    this._value = newValue
    trigger()
  }
}

computed计算属性:

// 可读可写
<script setup>
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // Note: we are using destructuring assignment syntax here.
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

watch:

可以监听ref定义的基本类型的数据和对象类型的数据

可以监听reactive定义的对象数据类型的数据,且默认是开启深度监视并且无法 关闭的。

可以监听ref定义或者reactive定义的对象数据类型的数据中的某个属性,若该属性值不是对象类型,需要写成函数形式,若该属性值依然是对象类型,可直接编辑,也可写成函数形式(()=>obj.name)。

<script setup>
import { ref, watch } from 'vue'

const sum = ref(0)
const obj = ref({name:'123',age:18})
watch(sum,(newValue,oldValue)=>{
    console.log(newValue,oldValue)
})
watch(obj,(newValue,oldValue)=>{
    console.log(newValue,oldValue)
},{deep:true,immediate:true})

watch(()=>obj.name,(newValue,oldValue)=>{
    console.log(newValue,oldValue)
},{deep:true,immediate:true})

</script>

watchEffect:

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

与watch相比,watchEffect不用明确指出监视的数据(自动监听函数中用到的属性),

<script setup>
import { ref, watchEffect } from 'vue'

const height = ref(0)
const width = ref(0)

watchEffect(()=>{
    if(height.value>20 || width.value>30){
        console.log(1234)
    }
})

</script>

defineExpose:

子组件抛出数据给父组件,

ts:

// 定义一个接口 用于限制person对象的具体属性
export interface PersonInter{
    id:string,
    name:string,
    age:number,
    x?:number // 可有可无的字段
}

// 一个自定义类型
export type Persons = Array<PersonInter>
<script lang="ts" setup name="name">
import {reactive} from 'vue'
import { type PersonInter ,type Persons} from '@/types'

let person:PersonInter = {id:'123',name:'张三',age:18}
let personList:Persons = [{id:'123',name:'张三',age:18}]

// 给响应式数据加泛型
let personList2 = reactive<Persons>([{id:'123',name:'张三',age:18}])
</script>

生命周期:

<script setup>
import { onBeforeMounted,onMounted,onBeforeUpdate,onUpdate } from 'vue'
// setup就是创建了
onBeforeMounted(() => {
  console.log('挂载前')
})
onMounted(() => {
  console.log('挂载完毕*')
})
onBeforeUpdate(() => {
  console.log('更新前')
})    
onUpdate(() => {
  console.log('更新完毕*')
})  
onBeforeUnmounted(() => {
  console.log('卸载前*')
})
onUnmounted(() => {
  console.log('卸载完毕')
})  
</script>

hooks: 模块化

建一个hooks文件,文件里可以建多个use开头的ts文件,

每个hooks里都有自己的数据,方法,钩子函数等,不同的业务数据可以建不同的hooks文件,这样就实现了数据分离,

例如,建一个useDog.ts文件,

import { reactive,onMounted} from 'vue'

export default function(){
	// 数据
    let dogList = reactive(['img 地址'])
    // 方法
    async function getDog(){
        // 请求数据
        dogList.push()
    }
    // 钩子
    onMounted(()=>{
        
    })
    // 向外界提供数据
    return {dogList,getDog}
}

组件引入hooks:

<script lang="ts" setup>
import useDog from '@/hooks/useDog'

const {dogList,getDog} = useDog()
    
</script>

路由:

新建router文件,写好router抛出来,在main.ts中使用路由,同时要在App.vue中使用路由导航来占位内容。

分为history和hash模式

pinia:集中式(数据)状态管理

多个组件共享数据,

安装: npm install pinia

引入:

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

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

定义:

在store文件夹里新建一个业务名称的ts的文件

例如:用选项式写count.ts

import { defineStore } from 'pinia'

export const useCountStore = defineStore('count', {
  // 放置方法,用于响应组件中的actions
  actions:{
      increment(value:number){
          // this是当前的store
          this.sum + = value
	  }
  },
  // 真正存储数据的地方
  state(){
      return {
          sum:6
      }
  },
  getters:{
      bigSum(state){
          // return state.sum*10 或者
          return this.sum*10
          
	  }
  }
})

在页面中使用:

<template>
	<div>{{sum}}</div>
	<button @click="add">
        +
    </button>
</template>
<script lang="ts" setup>
import {useCountStore} from '@/store/count'
import { storeToRefs } from 'pinia'

// 使用
const countStore = useCountStore()
// *解构 使用storeToRefs 会把countStore中的数据转为ref数据(响应式数据)
const { sum } = storeToRefs(countStore)
// 订阅这个属性的变化
sum.$subscribe((mutate,state)=>{
    console.log(state.sum)
})

function add(){
    countStore.increment(1)
}
</script>

*改为用组合式写count.ts

  • ref()成为state属性
  • computed()成为getters
  • function()成为actions
import { ref,computed } from 'vue'
import { defineStore } from 'pinia'

export const useCountStore = defineStore('count', ()=>{
  const sum = ref(0)
  const doubleCount = computed(() => sum.value * 2)
  function increment(value:number){
      sum.value + = value
  }
  return {sum,doubleCount,increment}
})

defineProps:父子组件传参

父组件:

<template>
	<div></div>
	<Child :list="list" :house="house" @send="onSend"></Child>
</template>

<script setup lang="ts">
    import {ref} from 'vue'
    let childdata = ref('')
    let house = ref('别墅')
	function onSend(value:string){
        childdata.value = value
	}
</script>

子组件:list是父组件传过来的数据

<template>
	<div>{{name}}{{list}}{{house}}</div>
	<button @click="send(name)"></button>
</template>

<script setup lang="ts">
import {ref} from 'vue'
interface Props {
  foo: string
  bar?: number
}
interface HouseProps {
  house?:string
}
let name = ref('sansan')
const props = defineProps(['list','send']) // 不限制类型
const props = defineProps<{list:Props}>() // 接收list+限制类型
const props = withDefaults(defineProps<{list?:Props}>(),{list:()=>[]} )// 接收list+限制类型+限制必要性+指定默认值
const props = defineProps<HouseProps>()  // 接收house+限制类型

</script>

自定义事件:子传父

父组件:

<template>
	<div></div>
	<Child @send-data="onSend"></Child>
</template>

<script setup lang="ts">
    import Child from './child.vue'
    import {ref} from 'vue'
    let childdata = ref('')
    // 取到传过来的数据
	function onSend(value:string){
        childdata.value = value
	}
</script>

子组件:

<template>
	<div>{{name}}</div>
	<button @click="emit('send-data',name)"></button>
</template>

<script setup lang="ts">
    import {ref} from 'vue'
    let name = ref('sansan')
    const emit = defineEmits(['send-data'])
	// 使用emit('send-data',value)就可以给父级传参数和事件了
</script>

mitt:不管多么深度或者多么远的组件之间都可以通信;

安装:npm i mitt

在util中引入mitt;

emitter.emit('on-send',name)
emitter.on('on-send',(value:string)=>{
    // 操作传过来的数据
})
// 销毁组件时解绑emitt的事件
onUnmounted(()=>{
	emitter.off('on-send')
})

v-model

v-model用在html标签上:

v-model 的本质是input框的value和@input事件

v-model用在组件标签上:

$event到底是什么,

对于原生事件,$event就是事件对象,这时候能用.target,

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

举个例子:

父组件:

<child v-model="name"></child>
// 优化
<child v-model:qwe="name"></child>
<child :qwe="name"></child>
// 本质是
<child :modelValue="name" @update:modelValue="name=$event"></child>

子组件:

<template>
	<input :value="modelValue" @input="emit('update:modelValue',$event.target.value)">
    <input :value="qwe" @input="emit('update:qwe',$event.target.value)">
</template>

<script setup lang="ts">
    defineProps(['modelValue','qwe')
    const emit = defineEmits(['update:modelValue','update:qwe'])
</script>

爷孙组件传参:

v-bind="$attrs" v-on="$listeners"

组件通信:$refs 和

$parent

$refs值为对象,能取到所有被ref属性标识的DOM元素或组件实例,用于父级获取子集数据并且可修改子集数据。

$parent值为对象,能取到当前组件的父组件实例对象,用于子级获取父级数据并修改父级数据。

子级:

<template>
	<div>{{name}}</div>
	<button @click="changeParent($parent)">
       修改父级的数据
    </button>
</template>

<script setup lang="ts">
    import {ref} from 'vue'
    let name = ref('sansan')
    
    // 改动父组件的数据
    function changeParent(parent:any){
        // 因为parent是响应式对象了,当访问parent.parentData的时候,底层会自动读取value属性,所以里面的parentData不用再.value了
        parent.parentData = 10
	}
    
    // 使用defineExpose把数据交给外部
	defineExpose({name})  
</script>

父级:

<template>
	<Child ref="child"></Child>
	<button @click="changeName($refs)">
        修改Child组件的数据
    </button>
</template>

<script setup lang="ts">
    import Child from './child.vue'
    import {ref} from 'vue'
    let child = ref()
    let parentData = ref(4) // 父级数据
    
    // 改动子组件的数据
	function changeName(refs:object){
        // child.value.name="xxx"
        // 因为refs是响应式对象了,所以里面的name不用再.value了
        refs.child.name="xxx"
	}
    
    // 使用defineExpose把数据交给外部
	defineExpose({parentData})  
</script>

祖孙之间通信:provide 和inject

爷级:

<template>
	<Child ></Child>
</template>

<script setup lang="ts">
    import Child from './child.vue'
    import {ref,provide} from 'vue'
   	const location = ref('North Pole')
   function updateLocation() {
      location.value = 'South Pole'
    }
    // 向后代提供数据 所有后代都能收到
    provide('location',{location,updateLocation})
   
</script>

孙级:

<template>
	<button click="updateLocation">
       {{location }}
    </button>
</template>

<script setup lang="ts">
    import {inject} from 'vue'
    
    // 注入上层组件提供的数据
    const { location, updateLocation } = inject('location')
</script>

插槽

默认插槽,

具名插槽,

作用域插槽:数据在孩子那边,但根据数据生成的结构,由父亲决定,

父级:

<template>
	// <Child />
	<Child>
        // *默认插槽
        <div>{{name}}</div>
        // *默认插槽语法糖
        <div #default>{{name}}</div>
        // *具名插槽
        <template v-slot:s2><div>{{name}}</div></template>
	    <template v-slot:s1><h2>标题</h2></template>
		<template #s1><h2>标题</h2></template> // 具名插槽语法糖
		// *作用域插槽 控制子组件的数据展示的结构
		<template v-slot="params">
			<div>{{params.game}} {{params.x1}}</div>
		</template>
		// 作用域插槽结合具名插槽
		<template v-slot:slotname="params">
			<div>{{params.game}} {{params.x1}}</div>
		</template>
    </Child>
</template>

<script setup lang="ts">
    import Child from './child.vue'
    import {ref} from 'vue'
    let name= ref('里面是默认插槽的内容')
</script>

子级:

<template>
	<div>
        ...
        <slot>默认内容</slot> // 插槽占位
        <slot name="s1">s1内容</slot>   // *具名插槽
        <slot name="s2">s2内容</slot>   // *具名插槽
        <slot :game="game" :x1="x1"></slot>   // *作用域插槽
        <slot name="slotname" :game="game" :x1="x1"></slot>   // *作用域插槽也可以具名
    </div>
</template>

其他api:

shallowRef() 和shallowReactive() 绕开深度响应,只作用与顶层属性,避免了对每一个内部属性做响应式所带来的性能成本,提高性能。

readonly:用于创建一个对象的深只读副本,对象的所有嵌套属性都将变为只读,

shallowReadonly:只作用于对象的顶层属性,

toRaw:用于获取一个响应式对象的原始对象,toRaw返回的对象不再是响应式,不会触发试图更新,何时使用:在需要将响应式对象传递给非vue的库或外部系统时,使用toRaw可以确保得到的是普通对象。

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

customRef:创建一个自定义ref,并对其依赖的

回调函数有两个参数,track(跟踪),trigger(触发),

track()在get中调用,告诉vue数据msg很重要 你要对数据msg进行持续关注,

trigger()在set中调用,通知vue一下数据msg变化了

teleport :

小知识:样式:filter:saturate(200%) 就可以修改内容的颜色,可以整体置灰,

Vue 2和Vue 3 的区别(非兼容性改变):

全局API应用实例:

2.x 全局 API3.x 实例 API ( app)
Vue.config应用程序配置
Vue.config.生产提示移除(见下方)
Vue.config.ignoredElementsapp.config.compilerOptions.isCustomElement(见底部
Vue.组件应用程序组件
Vue.指令应用程序指令
Vue.mixin应用程序.mixin
Vue.useapp.use(见下方
Vue.原型app.config.globalProperties(见底部
Vue.扩展移除(见下方)

项目搭建:

1,环境准备:

node > v16.

pnpm

2,初始化项目:

使用vite进行构建,

安装pnpm:npm i pnpm -g

项目初始化命令:pnpm create vite

进入项目根目录安装依赖: pnpm install

运行:pnpm run dev

引入图标:

<template>
<!-- <use xlink:href="#icon-home" fill="red"></use> -->
<!-- 封装成组件 -->
<use :xlink:href="name" :fill="color"></use>
</template>
<script setup lang="ts">
    // 接受父组件传递过来的参数
    defineProps({
        name:String,
        color:String
	})
</script>

api封装,用户信息存储,router,

TS

ts是js的超集,js是一种弱类型动态语言

类型声明:

// 类型断言 用来告诉解析器 变量的实际类型
s=a as string 
// 复合类型 
let b:number | boolean 
// 表示空 没有返回值
function fn():void{
}
// 永远不会返回结果
function fn2():never{
}
// 枚举
enum Gender = {
    male=0,female=1,name,age
}
let i:Gender
// 类型的别名 声明一个某类型的字段
type myType = 1|2|3
let k:myType
// 数组
let arr:number[] = [1,2,3]
let arr:Array<string> = ['a','s','d']
// 元组
let t1:[number,string,number?] = [1,'a']

面向对象

类:对象的模型,程序中可以根据类创建指定类型的对象,

class 类名{
	属性名:类型;
}

// 例子:
class Person{
    // 实例属性
    name:string='124'
    // 静态属性 可以不创建实例直接访问类
    static age:number=18
    // 只读属性
    readonly id:string='1111'
    // 可以在任意位置访问(修改)
    public _name:string
    // 私有属性 只能在类内部进行访问  可以通过在类中添加方法使得私有属性可以被外部访问
    private _name:string
    // 受包含的属性 只能在当前类和当前类的子类中使用
    protected _name:string
    // 定义方法
    sayHollow(){ 
    }
    // 构造函数 会在对象创建时调用
  	constructor(name:string,age:number){
        // 给属性赋值
        console.log(this,'this表示当前的实例,可以通过this向新建的对象中添加属性')
        this.name= name
        this.age = age
    }
}
// 创建实例对象
const per = new Person()
const per = new Person(name:'shanshan',age:12)
console.log(per.name)
console.log(Person.age)
per.sayHollow() // 调用方法

继承:

子类将会拥有父类所有的方法和属性,可以将多个类中共有的代码写在一个父类中,

如果想在子类中加一些自己的方法也可以直接加

class Cat extends Person{
    // 子类自己的方法
    run(){}
    // 子类也可以重写从父类继承过来的方法
    sayHollow(){}
    // 使用super
    myMethod(){
        // super就表示当前类的父类
        super.sayHollow()
    }
    // 如果在子类中也写了构造函数,必须要再掉一次父类的构造函数
    constructor(name:string,age:number){
        super(name)
        this.age = age
    }
    
}

抽象类:专门用来被子类继承 被子类继承时 子类不能创建实例对象 constructor

可以添加抽象方法,

abstract class Animal{
    name:string
    constructor(name:string,age:number){
        this.name= name
    }
    // 定义抽象方法 子类必须对抽象方法进行重写
    abstract sayHollow():void
}

class Cat extends Animal{
    sayHollow(){
        // 必须要实现
    }
}

let cat = new Cat(name:'shanshan')
cat.sayHollow()

接口:

// 用来定义一个类结构
// 接口可以在定义类的时候去限制类的结构
// 接口中的所有属性都不能有实际的值
// 接口只定义对象的结构 不考虑实际值
// 在接口中所有的方法都是抽象方法
interface myInter{
    name:string;
    sayHellow():void;
}
const obj:myInter={
    name:'sansan',
}
// 定义类时,可以使类去实现一个接口,实现接口就是使类满足接口的要求
class MyClass implements myInter {
    name:string;
    constructor(name:string){
        this.name=name
    }
    sayHellow(){
        console.log(1)
    }
}

泛型:

// 在定义函数或是类的时候,如果遇到类行不明确就可以使用泛型
function fn<T>(a:T):T{
    return a
}
// 使用
fn(a:10) // 不指定泛型
fn<number>(a:1) // 指定泛型
fn<string>(a:'123') // 指定泛型
// 声明接口
interface Inter {
    b:number
}
// T extends Inter表示泛型T必须是Inter 实现类(子类)
function fn3<T extends Inter>(a:T):number{
    return a.b
}
class Myclass<T>{
    name:T
}
const mc = new Myclass<string>(name:'shanshan')

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值