npm create vue@latest
npm install
指向js。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()
}
}
// 可读可写
<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>
可以监听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>
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更新时重新执行该函数,
与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:
子组件抛出数据给父组件,
// 定义一个接口 用于限制person对象的具体属性
export interface PersonInter{
id:string,
name:string,
age:number,
x?:number // 可有可无的字段
}
// 一个自定义类型
export type Persons = Array
<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文件,文件里可以建多个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模式
多个组件共享数据,
安装: 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}
})
父组件:
<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用在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值为对象,能取到所有被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>
默认插槽,
具名插槽,
作用域插槽:数据在孩子那边,但根据数据生成的结构,由父亲决定,
父级:
<template>
//
<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>
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%) 就可以修改内容的颜色,可以整体置灰,
全局API应用实例:
2.x 全局 API | 3.x 实例 API ( app ) |
---|---|
Vue.config | 应用程序配置 |
Vue.config.生产提示 | 移除(见下方) |
Vue.config.ignoredElements | app.config.compilerOptions.isCustomElement(见底部) |
Vue.组件 | 应用程序组件 |
Vue.指令 | 应用程序指令 |
Vue.mixin | 应用程序.mixin |
Vue.use | app.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是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')