Vue3、pinia的基本知识

用vite创建工程

npm create vue@latest

vue2和vue3的区别

vue2的Api是配置式风格的,vue2的Api是组合式风格的

vue2中若想修改一个需求,就要修改与其相关的data,methods,computed等,vue3将这些数据都组合起来,作为一个函数

createApp工厂函数

创建应用实例对象app(类似vm)

app比vm更轻量化,app上的属性很少

import { createApp } from 'vue'
const app = createApp(App);
app.mount('#app');

setup函数

setup是一个函数,跟methods类似,在setup中可以写数据,方法,最后要return一个对象,则对象中的属性,方法在模板中可以直接使用

setup中的this是undefined

setup中的数据不是响应式的,数据会改变,但页面不发生变化

vue2中data可以读取vue3中setup的值,setup中不能读data中数据,vue2和vue3中属性名一致时,模板读取的值是vue3的值,但不建议vue2和vue3混用

setup语法糖:在script标签是加setup,会将里面的数据自动return

<script lang="ts" setup>
  let name='xx';
  let n=0;
  function add() {
    n++
  }
</script>

image-20240206015827544

emits:['hello']
setup(props,context){
	console.log(context.attrs)
	console.log(context.slots)
	console.log(context.emit)
	function test(){
		context.emit('hello',666)
	}
	return {test}
}

ref函数

ref可以实现数据的响应式

ref用来定义响应式的基本类型数据,靠getter,setter实现的

ref定义响应式的对象类型是靠reactive函数实现的

ref()返回的是一个引用实现的实例对象

  • 将要实现响应式的数据,用ref( )包裹

​ (1)首先引入ref import { ref } from 'vue';

​ (2) 将ref标记值

let n = ref(0);
  function add(){
    console.log(n);
    n.value++;      //在方法中使用时要加.value(n.value),在模板中直接使用即可(n)
  }

reactive

只能定义响应式的对象,数组类型

  1. 引入reactive import { reactive } from 'vue';

  2. 使用reactive()

let personObj = reactive({
    name:'zs',
    age:18
  })
function add2(){
    personObj.age++		//在方法中和在模板中直接使用即可(person.age)
  }

ref和reactive区别

  1. 若直接修改一个对象

​ 由reactive定义的,必须使用 Object.assign(personObj,{name:'lisi',age:20})才能改变

//定义数据
let personObj = reactive({
    name:'zs',
    age:18
  })
//定义方法
 function changeObj(){
    Object.assign(personObj,{name:'lisi',age:20})
  }

​ 由ref定义的,直接修改即可

//定义数据
let personObj = ref({
    name:'zs',
    age:18
  })
//定义方法
function changeObj(){
    personObj.value = {name:'lisi',age:30}
  }

##toRefs

方便在模板中直接使用对象的属性名

将一个对象的属性直接拎出来,且是响应式的,跟原对象的属性值是绑定的

toRefs(person)会返回一个ref对象,其value值指向的是person对象中的所有属性。(name:person.name)

 let person = reactive({
    name:'张三',
    age:19
  })
  const {name,age} = toRefs(person)     //{name:person.name},并为name配置了get,set。在模板中读取name,在函数中使用name.value
  let n = toRef(person,'name')										
                                       

shallowReactive,shallowRef

image-20240206015842168

readonly shallowReadonly

image-20240206015855254

toRaw markRaw

image-20240206015906055

provide与inject

实现祖组件与后代组件之间的通信

祖组件

let car = '奔驰';
provide('car',car);

后代组件

let car = inject('car')

判断响应式数据的类型

image-20240206015917979

fragment

在vue3中可以不用写根标签,会自动将多个标签包含在一个fragment标签中

teleport传送门

将teleport标签内html结构传送到页面其他位置

<teleport to='body'>
    <p>hello!!!!!!!!</p>
</teleport>

##vue2和vue3响应式的区别

  • vue2的响应式

​ (1) vue2中对对象类型数据是通过Object.defineProperty(),给对象上的属性配置get,set来实现数据的响应式

   let number = 0;
	Object.defineProperty(person, 'age', {
            //当有人读取person.age时,函数被调用,就会返回一个值
            get() {
                console.log("有人读取了age的值");
                return number;
            },
            //当有人修改person.age时,函数被调用,且会收到修改的最新值(value)
            set(value) {
                console.log("有人修改了age的值");
                number = value;
            }
        });

缺点: vue2中给对象新添加,删除一个属性,vue都不能检测到,所以就会出现数据已经发生变化了,但页面不能改变

​ 只能用$set(this.person,'sex','女'),$delete(his.person,'sex)来实现页面的更新

​ (2) vue2中对数组类型的数据通过对数组的方法进行封装,实现响应式

​ (push,pop,shift,unshift,splice,sort,reverse),当程序员对数组调用以上的方法时,页面会自动更新

缺点:通过数组下标对元素进行修改,页面不会发生变化。

​ 只能this.$set(this.arr,0,'xx')进行修改或用以上的数组方法进行修改

  • vue3的响应式

​ 通过Proxy(代理)实现对源对象的属性进行增删改查(get,set,deleteProperty)

​ get(target,propertyName),set(target,propertyName,value)

​ target:源数据,propertyName:属性名,value:修改的值

​ 通过Reflect对源数据进行操作

##computed

计算属性 computed(() =>{return })

计算属性结果也是也ref定义的响应式数据

在模板中直接读取计算属性,在方法中要.value

//先引入computed
import { computed } from 'vue';
//函数写法
let full = computed(()=>{
    console.log(full.value);
    return first.value +''+ last.value; 
  })

//对象写法
let fullName = computed({
    get() {
      return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + lastName.value
    },
    set(newVal) {		//只有当fullName整体被修改时才会调用该方法,收到的newVal就是修改之后的值
      let str1 = newVal.slice(0,2)
      let str2 = newVal.slice(2)
      firstName.value = str1
      lastName.value = str2
    }
  })
  function changeName() {
    fullName.value = 'lisi'
  }

watch

  1. 监视ref定义的基本类型数据

​ 首先要引入watch,ref

​ watch(‘监视的数据’,函数)

  import { ref,watch } from 'vue';
  let sum = ref(0);
  watch(sum,(newValue,oldValue)=>{
    console.log(newValue,oldValue);
    
  })
  1. 监视ref定义的对象类型数据

​ 若要监视对象内属性,要配置deep。不加deep只有当整个对象发生变化时才会被监听

​ watch(‘监视的数据’,函数,{配置}) 配置:{deep:rue,immediate:true}

​ watch监听对象属性时,返回的newValue和oldValue都是一样的,因为监视的一直都是person对象,内存地址没变

watch(person,(newValue,oldValue)=>{
    console.log(newValue,oldValue);
  },{deep:true,immediate:true})
  1. 监视reactive定义的对象类型数据

    默认开启深度监视,且无法关闭深度监视

watch(person,(newValue,oldValue)=>{
    console.log(newValue,oldValue);
  })
  1. 只监视对象中某个属性的变化

​ 若该属性是基本数据类型,则需要写成函数形式()=>{}

watch(()=>{return person.name},(newValue,oldValue)=>{
    console.log(newValue,oldValue);
  })

​ 若该属性是对象数据类型,则需要写成函数形式()=>{},且手动开启深度监视

watch(()=>{return person.cars},(newValue,oldValue)=>{
    console.log(newValue,oldValue);
  },{deep:true})
  1. 监视多个属性

​ 将要监视的属性写在一个数组中

 watch([()=>{return person.cars.c1},()=>person.name],(newValue,oldValue)=>{
    console.log(newValue,oldValue);
  },{deep:true})

watchEffect

自动化的watch,不用明确指出监视的数据,函数中用到哪些属性,就监视哪些属性

引入 import {watchEffect} from 'vue'

watchEffect(()=>{})里面直接传一个函数

image-20240206015931497

标签的ref属性

用在普通的DOM标签上,获取的是DOM节点

用在组件标签上,获取的是组件实例

  1. 在DOM组件中使用

     <h2 ref="title"> hello world </h2>
     let title = ref()  //创建一个变量来接收
    
  2. 当父组件获取到子组件实例时,默认是看不到子组件的数据,使用defineExpose将子组件数据暴露给父组件

    import {defineExpose} from 'vue'; //可以不用引入
    defineExpose(person)     // 写在script标签的最下面
    

props

接受父组件传过来的值

引入 import {defineProps} from 'vue' 可以不用引入

接收 defineProps(['a'])

const props = defineProps(['title'])

接收 + 限制类型 defineProps<list:Person>()

限制类型 withDefaults()

生命周期

  • 创建 beforeCreate created setup

  • 挂载 beforeMount mounted onBeforeMount onMounted

    onMounted(()=>{
        console.log('hhh');
    })
    
  • 更新 beforeUpdate updated onBeforeUpdate onUpdated

  • 销毁 beforeDestory destoryed onBeforeUnmount onUnmounted

自定义Hooks

每一个hook就是一个函数,跟mixin类似

就是一个一个的ts文件,每一个ts文件实现一个功能,都是一个模块,里面包含数据,方法等等

ts文件内的数据方法要写在一个函数里,函数的数据和方法要return出去,并且将函数默认暴露

import { ref } from 'vue';
export default function(){
  let sum = ref(0);
  function add(){
    sum.value++
}
  return{sum,add}
}

在使用该功能的组件里引用即可

import useSum from '@/hooks/useSum'
const sumData = useSum()      //useSum()返回的是一个普通的对象,里面的数据是ref类型的,sumData.sum.value
const {sum,add} = useSum();	  //可以使用结构赋值

路由

路由中对应的组件在切换时会销毁

  1. 安装路由器 npm i vue-router
  2. 创建router文件夹
import { createRouter,createWebHistory } from "vue-router";
import Home from '@/pages/Home.vue'
const router = createRouter({
  history:createWebHistory(),
  routes:[
    {
    path:'/home',
    component:Home
    }
    
  ]
})
export default router;
  1. 在main.ts中引入并使用router
import { createApp } from 'vue'
import App from './App.vue'
import router from '@/router'     // 引入
const app = createApp(App);
app.use(router);				  // 使用
app.mount('#app');
  1. 在组件中使用
//先引入
import { RouterLink,RouterView } from 'vue-router';
<router-link to="/home">首页</router-link>
<router-view></router-view>

路由器工作模式

hash模式 createWebHashHistory

history模式 createWebHistory

to的两种写法

<router-link to="/home">首页</router-link>
<router-link :to="{path:'/home'}">首页</router-link>  //对象写法 

路由的name属性

<router-link :to="{name:'home'}">首页</router-link>
routes:[
    {
    name:'home',  //name属性
    path:'/home',
    component:Home
    }
  ]

路由传参

  1. 传递参数
<RouterLink :to="{
        path:'/home/news',
        query:{
          id:person.id,
          name:person.name
        }

      }">
</RouterLink>
  1. 接收参数
//先引入
import {useRoute} from 'vue-router'
let route = useRoute()

模板中route.query.id即可

编程式导航

import {useRouter} from 'vue-router'

const router = useRouter()

router.push('/home')

重定向

在route中配置,当路径为/时跳转为/home路径

 {path:'/',
    redirect:'/home'
 },

pinia(与Vuex类似)

集中式数据管理,管理共享的数据

  • 在main.js中完成以下配置
  1. 安装pinia npm i pinia

  2. 引入pinia import {createPinia} from 'pinia'

  3. 创建pinia const pinia = createPinia()

  4. 使用pinia app.use(pinia);

  • 创建store文件夹,里面配置不同部分要共享数据的ts文件
import { defineStore } from "pinia";   //defineStore会返回一个proxy对象,将state中数据都包装成响应式
export const useCountStore = defineStore('count',{
//数据
  state() {
      return {
        sum:0
      }
  },
//各种方法
  actions:{
    increment(value:number){
      this.sum += value     //this就是当前这个store
    }
  },
//类似与computed,就是将state中数据进行操作
  getters:{
    bigSum(state){		//会接受一个参数,就是store中的state
      return state.sum*10
    }
  }
})
  • 在组件中使用仓库中数据
import {useCountStore} from '@/store/count'
const countStore = useCountStore(); 	//useCountStore()返回的是一个proxy对象,sum是由ref封装的
 <h2>{{ countStore.sum }}</h2>

修改store中数据

1.直接修改,在组件中可以拿到store中state的数据,并且可以直接进行修改

 function add(){
    countStore.sum++
  }

2.同时修改store中多个数据

function add(){
    countStore.$patch({
      sum:999,
      age:90
    })
  }

3.通过actions修改(ations中写各种方法,在组件中直接调用即可,vuex中还要commit一下给mutations去处理)

在组件中直接调用store中actions中定义的方法

 //store.ts 
actions:{
    increment(value:number){
      this.sum += value
    }
  }

//组件
function add(){
    countStore.increment(1)
  }

storeToRefs

简化模板中使用store中的数据

将store中的state数据结构出来

//先引入
import { storeToRefs } from 'pinia';
const {sum} = storeToRefs(countStore)   //这样在模板中直接{{sum}}即可,以前要{{countStore.sum}}

$subscribe

监听pinia state中数据的变化

store.$subscribe((mutate,state)=>{})

store的组合式写法

import { defineStore } from "pinia";
import { ref } from 'vue';
export const useCountStore = defineStore('count',()=>{
	let number = ref(0);
	return {number}
})

组件之间通信

  1. 自定义事件

父组件给子组件绑定一个事件,子组件触发事件

//子组件
const emit = defineEmits(['getX'])
function sendX(){
    emit('getX',x)
  }

自定义事件推荐写成 @my-send=sendToy 这种形式

  1. mitt (跟$bus类似)

    先安装mitt npm i mitt

  • src下创建utils/emitter.ts文件
import mitt from "mitt";
const emitter = mitt();
export default emitter;
  • 绑定事件
emitter.on('asd',(value)=>{
	console.log(value)
})
  • 触发事件
emitter.emit('asd',666)	
//666是要传的参数
  • 解绑事件
emitter.off('asd')
emitter.all.clear() //清空全部事件
  • 在绑定和触发的组件中引入emitter
import emitter from '@/utils/emitter';
  1. $attrs(父孙之间传值)

如果父给子传的值,子组件不接收,则数据会在子组件$attrs中保存

image-20240206020154510

  1. $refs $parent

    都需要将组件内的数据defineExpose()

    r e f s 是父操作子组件数据, refs是父操作子组件数据, refs是父操作子组件数据,parent子操作父组件数据

image-20240815115334277

<son1 ref="c"></son1>
<son2 ref="c"></son2>
<button @click="changeName($refs)">+1</button>
function changeName(refs){
  for(let key in )		//c是组件实例对象,由ref封装的,要通过.value来访问
}

$parent

 <button @click="add($parent)">+1</button>
function add(parent:any){
    parent.n++;			//parent是父组件实例对象(用proxy封装的),可以直接访问属性
  }
  1. provide和inject

实现祖组件与后代组件之间的通信

image-20240815160000296

子组件接收

image-20240815160050221

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值