Vue3基础知识介绍,组件式api的常用方法,Pinia的介绍和使用

一.Vue3基础知识

1.Vue3的优势
  • 更容易维护:组合式api,更好的TypeScript支持
  • 更快的速度:重写diff算法,模板编译优化,更高效的组件初始化
  • 更小的(打包)体积:良好的TreeShaking,按需引入
  • 更优的数据响应式:Proxy
Proxy对对象进行数据劫持,当对象中新增或删除属性,不影响Proxy对于对象的数据劫持,
而Vue2不能获取到新增属性
2.选项式API和组合式API
  • vue2是选项式api,data,methods,computed,watch就是一个个选项

如:要实现功能A,需要在data,methods,或其他选项中都写进功能A的相关代码,这就是选项式api的特点和不足

  • vue3则将功能A的数据声明,计算属性和方法的提供放到了一起,是为组合式api(代码的组合性很高,可以任意放进同一个组件中去)

示例:点击按钮让数字+1

vue2:data+methods
vue3:
    使用调用方法的方式来声明数据:
    	const count=ref{0}//从vue引入ref
    点击调用函数的时候同时实现自增功能:
    	const addCount=()=>count.value++
3.使用create-vue创建Vue3项目

create-vue是是vue官方的新的脚手架工具,底层是vite构建工具

vue2的官方脚手架是Vue-CLI,它的底层是webpack
vue3的官方脚手架是create-vue,它的底层是vite,
vite是当前使用广泛的构建工具,为开发提供极速响应
  • 前提环境条件:16.0以上的node.js
  • 创建命令:npm init vue@latest

踩坑:遇到了node和vite版本不兼容的报错,解决方法是把node升级到高版本

4.vue3项目目录和关键文件介绍
  • vite.config.js:项目的配置文件–基于vite的配置
  • package.json:项目包文件–核心依赖项变为了vue3和vite
  • main.js:入口文件–createApp函数创建应用实例
  • App.vue:根组件–SFC单文件组件script-template-style
vue3对比vue2的项目文件,有了如下变化:
    1.脚本script和模板template顺序调整
    2.模板template不再要求唯一根元素
    3.脚本script添加setup标识支持组合式api
main.js对创建进行了封装

vue2–new Vue({}).mount('#app')
vue3–import{createApp}from 'vue';createApp(App).mount(#app)

----创建vue3实例
----设置挂载点在index.html中的div id="app"上

对其他的创建也进行了封装:createRouter(),createstore()

第一次运行vue3项目,会弹出插件警告,如何处理?
  • 改插件配置:禁用Vetur,安装和启用Vue-offical 插件

  • 重启vscode

5.vue3不用对导入的组件进行注册

import导入后,可以直接在template中用:<HelloWorld></HelloWorld>,省略了注册组件

components:{
	HelloWorld
}

二.组合式API的语法学习

1.setup选项

setup选项是组合式api的入口

写法

和钩子一样写成一个函数:setup(){},里面放组合式api,然后return
或者使用语法糖:把setup写在script标签中,<script setup></script>

特点
  • 执行时机:比beforeCreate还要早

  • 在setup函数中this是undefined,因此vue3不用纠结this指向

  • 使用:可以在setup中提供数据和函数,但必须return才能在模板{{}}中使用

<script>
export default {
  setup() {
    console.log("我是setup",this);
    const msg="hello Vue3"
    const showMsg=()=>{
      console.log("showMsg:",msg);
    }
    return{
      msg,
      showMsg
    }
  },
  beforeCreate() {
    console.log('我是beforeCreate');
    // this.showMsg()
  }
}
</script>
<template>
  <h1>{{msg}}</h1>
  <button @click="showMsg">获取</button>
</template>
语法糖

上例可以把原始script标签改写成<script setup></script>,就不需要export default+return暴露属性和方法了

<script setup>
const msg = "hello Vue3";
const showMsg = () => {
  console.log(msg);
};
</script>
<template>
  <h1>{{ msg }}</h1>
  <button @click="showMsg">获取</button>
</template>
2.reactive和ref函数
2.1.reactive()
  • 作用

接收对象类型的数据参数传入,并返回一个响应式对象

  • 核心步骤

从vue中按需导入reactive,在执行函数中包一层reactive

<script setup>
import {reactive} from 'vue'
const state=reactive({
  count:1
})
const addCount=()=>{
  state.count++
}
</script>
<template>
  <h1>{{ state.count }}</h1>
  <button @click="addCount">增加</button>
</template>
2.2.ref()
  • 作用:接受简单类型或对象类型的数据传入,并返回响应式对象
  • 步骤:同上
  • 本质

在原有数据基础上,在外面包了一层对象,成为复杂类型,从而变成响应式的,然后可以借助reactive()来实现响应式

  • 访问数据:在script中访问要.value,在template中不需要
<script setup>
import {ref} from 'vue'
const count=ref(1)//简单类型的数据
const addCount=()=>{
  count.value++
}
</script>
<template>
  <h1>{{ count }}</h1>
  <button @click="addCount">增加</button>
</template>
  • 推荐:只用ref()
3.computed

computed的功能和vue2一样,仅修改了写法

  • 步骤

按需导入computed函数,执行函数在回调参数中return一个基于响应式数据来做计算的值,并用一个变量在前面接收

import { computed,ref } from "vue"
const list= ref([1,2,3,4,5])
const computedList=computed(()=>{
    return list.value.filter(item=>item>3)
})
4.watch

watch功能完全一样,写法也变成了函数的写法:

  • 单个数据侦听
watch(count,(newVal,oldVal)=>{
	console.log(newVal,oldVal)
})
  • 多个数据侦听
watch([count.name],(newVal,oldVal)=>{
	console.log(newVal,oldVal)
})
watch的属性immediate

在侦听器创建时,立即触发回调,响应式数据变化后继续执行回调

const username=ref("张三")
const userage=ref(18)
const changeName=()=>{
  username.value="李四"
}
const changeAge=()=>{
  userage.value++
}
watch([username,userage],(newVal,oldVal)=>{
  console.log(newVal,oldVal);
},{//注意后面是一个对象不是一个回调
  immediate:true,
})
watch的属性deep

使用deep对对象进行深度监视

const user=ref({
  name:"张三",
  age:18
})
const changeAge=()=>{
 user.value.age++
}
watch(user,(newVal,oldVal)=>{
  console.log(newVal,oldVal);
},{
  immediate:true,
  deep:true
})
//如果不加deep:只有name和age都改变时才会watch到,仅仅age++不行
精确侦听对象的某个属性

上例中,user任意属性的改变都会触发watch,若要求仅age改变时才会触发watch监听,
在不开启deep的前提下如何监听对象的属性?

const changeAge=()=>{
 user.value.age++
}
watch(()=>user.value.age,(newVal,oldVal)=>{
  console.log(newVal,oldVal)
},{
  immediate:true,
  // deep:true
})
//此时name属性发生改变不会触发watch
5.生命周期函数

在组合式 API 中,所有的生命周期钩子都以 onXxx 的形式存在,并且需要按需导入

import { onBeforeMount, onMounted } from 'vue';
export default {
  setup() {
    onBeforeMount(() => {
      console.log('Component is about to be mounted');
    });
    onMounted(() => {
      console.log('Component has been mounted');
    });
  }
};

在这里插入图片描述

6.父子通信
6.1.父传子:父给子绑定属性,子通过props接收
//父组件
<script setup>
import SonCom from "@/components/son-com.vue"
</script>
<template>
  我是父组件
  <SonCom msg="hello"></SonCom>
</template>

//子组件
<script setup>
const props=defineProps({
  msg:String
})
</script>
<template>
  <div>我是子组件:{{ msg }}</div>
</template>
6.2.子传父:父给子占位符绑定自定义事件,子通过emit方法触发该事件

(注意没有$符号,因为setup中没有this)

//子组件
<script setup>
const props=defineProps({
  msg:String
})
const emit=defineEmits(['getMsg'])
const sendMsg=()=>{
  emit('getMsg','world')
}
</script>
<template>
  <div>我是子组件:{{ msg }}</div>
  <button @click="sendMsg">点击改变msg</button>
</template>

//父组件
<script setup>
import SonCom from "@/components/son-com.vue"
import { ref } from "vue"
const msg=ref("hello")
const getMessage=(son_msg)=>{
    console.log("子传父回来的msg:",son_msg)//world
    msg.value=son_msg
}
</script>
<template>
  我是父组件
  <SonCom :msg="msg" @getMsg="getMessage"></SonCom>
</template>

效果:子组件通过子传父传值,成功改变父传子传过来并在子组件渲染的msg

6.3.defineProps和defineEmits

编译器宏是编译阶段的一个标识,实际编译器解析时,遇到该标识会进行编译转换,转换后生成props属性和emits方法
我理解成是把props$emits封装起来,然后直接调用就行了

7.模板引用----ref标识

模板引用:通过ref标识获取真实DOM对象组件实例对象

7.1.vue3中如何使用ref获取DOM对象?
import {ref} from 'vue'
//1.调用ref函数生成一个ref对象
const myRef=ref(null)
onMounted(()=>{//用钩子函数,确保渲染完了
  //3.通过ref对象.value即能访问到绑定的元素
  console.log("获取输入框中的内容:",myRef.value.value);
  myRef.value.focus()
})

<!-- 模板引用 -->
//2.通过ref标识绑定ref对象到标签
<input type="text" ref="myRef" value="hi Vue3">
7.2.vue3中如何使用ref获取组件实例对象?
//父组件
//1.调用ref函数生成一个ref对象
const testRef=ref(null)
const getCom=()=>{
	////3.通过ref对象.value即能访问到绑定的元素
    console.log("我是组件实例对象:",testRef.value.testCount);
}
//2.通过ref标识绑定ref对象到标签
<TestCom ref="testRef"></TestCom>
<button @click="getCom">获取组件实例对象</button>

//子组件:TestCom.vue
<script setup>
import {ref} from 'vue'
  const testCount=ref("10086")
  defineExpose({//指定哪些属性和方法允许访问
    testCount,
})
</script>

defineExpose:script setup语法糖在默认情况下组件内部的属性和方法并不开放给父组件
通过defineExpose编译宏指定哪些属性和方法允许访问

8.provide和inject实现跨层级共享数据
  • 作用和场景:

顶层组件向任意底层组件传递数据,实现跨层组件通信(有端联想:eventBus,vuex)

  • 语法:

顶层组件使用provide函数提供数据,
底层组件使用inject函数获取数据

顶层:provide('key',数据)
底层:const msg=inject('key')
  • 底层组件如何修改数据?

顶层:把能修改数据的方法传递给底层组件
底层:先接收,再调用

9.Vue3.3新特性:defineOptions

当setup出现后,name等配置项不能再在setup()同级函数中配置,于是官方推出了defineOptions,来完成这一需求,避免写两个script(一个原生,一个script setup)

10.Vue3.3新特性:defineModel

简化v-model,快速实现双向绑定
在vue3中,自定义使用v-model相当于传递一个modelValue属性,同时触发update:modelValue事件

<Child v-model='isVisible'>
//相当于
<Child :modelValue="isVisible" @update:modelValue="isVisible=$event">

这样的写法相当麻烦,需要先定义props和emits,若要修改数值,还要手动调用emit函数

  • 示例:(使用v-model)
:
import MyInput from "子组件"
import {ref} from 'vue'
const txt=ref('1123')

<MyInput v-model="txt"></MyInput>
{{txt}}

:
<script setup>
const props = defineProps({
  modelValue:String
})
const emit=defineEmits(['undate:modelValue'])
</script>
<template>
  <input type="text" 
  :value="modelValue"
  @input="e=>emit('undate:modelValue',e.target.value)"
  >
</template>
  • 示例修改:(使用defineModel)
//子组件
<script setup>
import {defineModel} from 'vue'
const modelValue=defineModel()
modelValue.value++//子组件可以改父组件传过来的数据
// const props = defineProps({
//   modelValue:String
// })
// const emit=defineEmits(['undate:modelValue'])
</script>
<template>
  <!-- <input type="text" 
  :value="modelValue"
  @input="e=>emit('undate:modelValue',e.target.value)"
  > -->
  <input type="text" 
  :value="modelValue"
  @input="e=>modelValue=e.target.value"
  >
</template>

三.Pinia的介绍和使用

1.什么是pinia

vue最新状态管理工具,是Vuex的替代品
特点:

  • 提供更简单的api,去掉了mutations(或者说合并进了actions中)
  • 提供符合组合式风格的api,和vue3的语法统一
  • 去掉了modules的概念,每一个store都是一个独立模块
  • 配合ts更加友好,提供可靠的类型推断

综上.pinia只剩3个核心概念----state,getters,actions
所以,actions既支持同步又支持异步,也能修改state

2.如何在Vue项目中添加Pinia

(在实际开发项目中,是在创建项目的同时自动添加的)
如何手动添加?

  • 使用Vite创建一个空Vue3项目:npm init vue@latest
  • 按照官方文档,安装pinia到项目中
3.pinia中的action异步写法

异步action函数的写法和在组件中获取异步数据的写法完全一致

示例:在Pinia中获取频道列表数据,并把数据渲染app组件的模板中

接口地址:http://geek.itheima.net/v1_0/channels
步骤:

  • 安装axios,以便后续发起请求
  • 新建store/channel.js
//store/channel.js
import {defineStore} from "pinia"
export const useChannelStore=defineStore('channel',()=>{
    //声明数据+声明操作数据的方法+声明getters等等 ,最后return
    //声明数据
    const channelList=([])//类似vuex的state
    //声明操作数据的方法
    const getList=async ()={//类似vuex的mutations+actions
        //支持异步
        const {data:{data}}=await axios .get('http://geek.itheima.net/v1_0/channels')
        //console.log(res)
        channelList.value=data.channels
    }
})
  • 在页面中调用:直接在App.vue中调用
import {usechannelStore} from "@/store/channel.js"
const counterStore = useChannelstore()

<button @click="channelStore.getList">获取
ui>li v-for='item in channelStore.channelList :key="item.id" {{item.name}}
4.pinia的storeToRefs方法
使用场景
  • 引出问题

上例中

const counterStore = useChannelstore()

{{channel.count}}
{{channel.msg}}

不能对counterStore进行解构,写成

const counterStore = useChannelstore()
const { count,msg } = counterStore
{{count}}
{{msg}}

结果:数据丢失响应式(不能响应添加按钮的+1+1)

  • 解决方式:使用storeToRefs包一层
import { storeToRefs} from 'pinia'
const channelStore = useChannelstore()
const { count,msg } =storeToRefs(channelStore)
  • 出现原因分析
//复制官网>使用store

虽然我们前面定义了一个store,但是我们使用script setup调用useStore()或使用setup()函数调用之前,
store实例是不会被创建的
store是一个使用reactive包装的对象,这意味着不需要在getters后面写.value
原理

storeToRefs包一层再解构,不会失去响应式,是因为它为每一个响应式属性创建引用
当你只是用store的状态而不调用任何action时它非常有用
你可以直接从store中解构action,因为他们也被绑定到store上
方法可以直接解构,因为不用它们作响应式处理

<button @click="channelStore.getList">获取

改成:
const { count,msg } =storeToRefs(channelStore)//属性包
const { count,msg } =channelStore//方法不用
<button @click="getList">获取
5.pinia 的调试

控制台>Vue>Pinia

6.pinia的持久化插件(有官方文档)

插件名:pinia.plugin.persitedstate
安装命令:npm i pinia.plugin.persitedstate

使用
//main.js
import persist form 'pinia.plugin.persitedstate'
...
app.use(createPinia().use(persist))

在store中开启:persist.true,给需要做持久化的模块加上persist:true即可

写在哪里?

  • 在选项式的store中,这句话跟state,actions,getters并列
  • 在组合式的store中,在第三个参数的位置写这句话
//store/index/js
export const useStore =defineStore(
    'counter',//这也是存在本地的唯一标识
     ()=>{
            
      },
      persist:true//写在这里
)

效果:刷新数据不会初始化,已存本地
在这里插入图片描述

用法一:修改本地唯一标识
//persist:true//写在这里
persist:{
    key:'myCounter'
}
用法二:从对整个store持久化,改为对某一项数据持久化
//persist:true//写在这里
persist:{
    key:'myCounter',
    paths:['count']
}

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端OnTheRun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值