Vue 3.0 -- Day 1
1、Vue3 组合式API vs Vue2 选项式 API
2、create-vue搭建Vue3项目
认识 create-vue:
create-vue是Vue官方新的脚手架工具,底层切换到了 vite(下一代构建工具),为开发提供极速响应
1.前提环境条件 -- 已安装 16.0 或更高版本的 Node.js node -v
2.创建一个Vue应用
npm init vue@latest -- 这一指令将会安装并执行 create-vue
需要安装依赖: npm i
运行指令:npm run dev
3、熟悉项目目录和关键文件
关键文件:
- vite.config.js - 项目的配置文件 基于vite的配置
- package.json - 项目包文件 核心依赖项变成了 Vue3.x 和 vite
- main.js - 入口文件 createApp函数创建应用实例
- app.vue - 根组件 SFC单文件组件 script - template - style
变化一:脚本script和模板template顺序调整
变化二:模板template不再要求唯一根元素
变化三:脚本script添加setup标识支持组合式API
- index.html - 单页入口 提供id为app的挂载点
4、组合式API - setup选项
setup选项的写法和执行时机:
// 1、执行时机,比beforeCreate还要早
// 2、setup函数中,获取不到this(this是undefined)
// 3、数据 和 函数,需要在 setup 最后 return,才能在模板中应用
// 问题? 每次都要 return 很麻烦?
// 4、通过 setup 语法糖简化代码
export default {
setup(){
const message = 'hello Vue3'
const logMessage = () => {
console.log(message)
}
// 必须return才可以
return {
message,
logMessage
}
}
}
</script> -->
<script setup>
const message = 'this is message'
const logMessage = () => {
console.log(message);
}
</script>
<template>
<div>{{ message }}</div>
<button @click="logMessage">按钮</button>
</template>
<script setup>语法糖
script标签添加 setup标记,不需要再写导出语句,默认会添加导出语句
5、组合式API - reactive 和 ref函数(推荐使用ref)
1.reactive()
作用:接受对象类型数据的参数传入并返回一个响应式的对象
核心步骤:
- 从 vue 包中导入 reactive 函数
- 在 <script setup> 中执行 reactive 函数并传入类型为对象的初始值,并使用变量接收返回值
<script setup>
import { reactive } from 'vue'
const state = reactive({
count: 100
})
const setCount = () => {
state.count++
}
</script>
<template>
<div> {{ state.count }} </div>
<button @click="setCount">+1</button>
</template>
2.ref() -- 本质:在原有传入数据的基础上,外层包了一层对象,包成了复杂类型。底层:在包成复杂类型之后,再借助reactive 实现响应式。
注意: 1、在数据中(script)访问数据,需要通过 .value
2、在页面中 (template div) 访问不需要添加,自动趴了一层
作用:接收简单类型或者对象类型的数据传入并返回一个响应式的对象
- 从 vue 包中导入 ref 函数
- 在 <script setup> 中执行 ref 函数并传入初始值,使用变量接收 ref 函数的返回值
<script setup>
import { ref } from 'vue'
const count = ref(0)
console.log(count.value)
const setCount = () => {
count.value++
}
</script>
<template>
<div>
<div>{{ count }}</div>
<button @click="setCount">+1</button>
</div>
</template>
6、组合式API - computed
computed计算属性函数
计算属性基本思想和Vue2的完全一致,组合式API下的计算属性只是修改了写法
核心步骤:
语法:
const 计算属性 = computed(() => {
return 返回计算返回结果
})
1.导入computed函数
2.执行函数 在回调参数中return基于响应式数据做计算的值,用变量接收
<script setup>
import { computed, ref } from 'vue';
const list = ref([ 1, 2, 3, 4, 5, 6, 7, 8])
// 基于list派生出一个计算属性,从list中过滤出 > 2
const computedList = computed(() => {
return list.value.filter(item => item > 2)
})
// 定义一个修改数组的返回
const addFn = () => {
list.value.push(6)
}
</script>
<template>
<div>
<div>原始数据: {{ list }}</div>
<div>计算后的数据: {{ computedList }}</div>
<button @click="addFn">修改</button>
</div>
</template>
7、组合式API - watch
watch函数
作用: 侦听一个或者多个数据的变化,数据变化时执行回调函数
俩个额外参数:1. immediate(立即执行) 2. deep(深度侦听)
1、基础使用 - 侦听单个数据
- 导入watch函数
- 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
2、基础使用 - 侦听多个数据
说明:同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调
<script setup>
import { ref, watch } from 'vue';
const cout = ref(0)
const nickname = ref('张三')
const changeCount = () => {
cout.value++
}
const changeNickname = () => {
nickname.value = '李四'
}
// 监视单个数据的变化
// watch(ref对象, (newValue, oldValue) => { ... } )
watch(cout, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
// 监视多个数据变化
// watch([ref对象1, ref对象2], (newArr, oldArr) => { ... } )
watch([cout, nickname], (newArr, oldArr) => {
console.log(newArr, oldArr)
})
</script>
<template>
<div>
<div>{{ cout }}</div>
<button @click="changeCount">改数字</button>
<div>{{ nickname }}</div>
<button @click="changeNickname">改昵称</button>
</div>
</template>
3、immediate
说明:在侦听器创建时立即触发回调, 响应式数据变化之后继续执行回调
4、deep
默认机制:通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep
选项
// 3、 immediate 立即执行 一进页面就触发一次
watch(cout, (newValue, oldValue) => {
console.log(newValue, oldValue)
}, {
immediate: true
})
// 4、deep 深度监视, 默认 watch 是浅层监视, 如果ref(复杂类型),无法监视其内部的变化
const userInfro = ref({
name: '张三',
age: 18
})
const setuserInfo = () => { //浅层的只能修改了对象的地址才能监视到
userInfro.value.age++
}
watch(userInfro, (newValue) => {
console.log(newValue)
}, {
deep: true
})
5、精确侦听对象的某个属性
需求:在不开启deep的前提下,侦听age的变化,只有age变化时才执行回调
const userInfro = ref({
name: '张三',
age: 18
})
const setuserInfo = () => {
userInfro.value.name = '李四'
}
// 5、对于对象中的属性进行监视
watch( () => userInfro.value.name, (newValue, oldValue) => {
console.log(newValue, oldValue);
})
8、组合式API - 生命周期函数
Vue3的生命周期API (选项式 VS 组合式)
<script setup>
import { onMounted } from 'vue';
// beforeCreate 和 created 的相关代码, 一律放在setup中执行
const getList = () => {
setTimeout(() => {
console.log('发送请求,获取数据');
}, 2000)
}
// 一进入页面的请求
getList()
// 如果有些代码需要在mounted生命周期中执行
onMounted(() => {
console.log('mounted生命周期函数 -- 逻辑1');
})
// 写出函数的调用方式,可以调用多次,调用多次,不会冲突,而是安装顺序依次执行
onMounted(() => {
console.log('mounted生命周期函数 -- 逻辑2');
})
</script>
<template>
<div></div>
</template>
9、组合式API - 父子通信
组合式API下的父传子:
基本思想
- 父组件中给子组件绑定属性
- 子组件内部通过props选项接收
defineProps 原理:就是编译阶段的一个标识,实际编译器解析时,遇到后会进行编译转换
组合式API下的子传父
基本思想
父组件中给子组件标签通过@绑定事件
子组件内部通过 emit 方法触发事件
父组件:
<script setup>
// 父传子
// 1、父组件:添加属性的方式传值
// 2、子组件:通过props接受
// 子传父
// 1、在子组件内部,emit触发事件(编译器宏获取)
// 2、在父组件,通过 @ 监听
// 局部组件(导入就可使用)
import SonCom from '@/components/son-com.vue'
import { ref } from 'vue';
const money = ref(100)
const getMoney = () => {
money.value += 10
}
const changeFn = (newMoney) => {
money.value = newMoney
}
</script>
<template>
<div>
<h3>
父组件 -- {{ money }}
<button @click="getMoney">挣钱</button>
</h3>
<SonCom
@changeMoney="changeFn"
car="宝马车"
:money = "money"
></SonCom>
</div>
</template>
子组件:
<script setup>
// 注意:由于写了setup,无法直接配置props选项
// 所有需要借助 “编译器宏” 函数接受子组件传递的数据
const props = defineProps({
car: String,
money: Number
})
console.log(props.car);
console.log(props.money);
const emit = defineEmits(['changeMoney'])
const buy = () => {
// 需要emit触发事件
emit('changeMoney', 5)
}
</script>
<template>
<!-- 对应props传递过来的数据,模板中可以直接使用 -->
<div class="son">
我是子组件 -- {{ car }} -- {{ money }}
<button @click="buy">花钱</button>
</div>
</template>
<style scoped>
.son {
border: 1px solid #000;
padding: 30px;
}
</style>
10、组合式API - 模版引用
通过ref标识获取真实的dom对象或者组件实例对象
如何使用(以获取dom为例 组件同理):
- 调用ref函数生成一个ref对象
- 通过ref标识绑定ref对象到标签
defineExpose()
默认情况下在<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,
可以通过defineExpose编译宏指定哪些属性和方法允许访问
<script setup>
import TestCom from '@/components/test-com.vue'
import { onMounted, ref } from 'vue';
// 模板引用(可以获取dom,也可以获取组件)
// 1、调用ref函数,生成一个ref对象
// 2、通过ref标识,进行绑定
// 3、通过ref对象.value即可访问到绑定的元素(必须渲染完成后,才能拿到)
const inp = ref(null)
// 生命周期钩子 onMounted
onMounted(() => {
// console.log(inp.value);
// inp.value.focus()
})
const clickFn = () => {
inp.value.focus()
}
// -------------------------------------------------
const abc = ref(null)
const getCom = () => {
console.log(abc.value.count)
abc.value.sayHi()
}
</script>
<template>
<div>
<input ref="inp" type="text">
<button @click="clickFn">点击让输入框聚焦</button>
</div>
<TestCom ref="abc"></TestCom>
<button @click="getCom">获取组件</button>
</template>
组件:
<script setup>
const count = 999
const sayHi = () => {
console.log('打招呼');
}
defineExpose({
count,
sayHi
})
</script>
<template>
<div>
我是用于测试的组件 -- {{ count }}
</div>
</template>
11、组合式API - provide和inject
作用和场景 -- 顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信
跨层传递普通数据
- 顶层组件通过provide函数提供数据
- 底层组件通过inject函数获取数据
跨层传递响应式数据
在调用provide函数时,第二个参数设置为ref对象
跨层传递方法
顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据
顶层组件:
<script setup>
import CenterCom from '@/components/center-com.vue'
import { provide, ref } from 'vue';
// 1.跨层传递普通数据
provide('theme-color', 'pink')
// 2.跨层传递响应式数据
const count = ref(100)
provide('count', count)
setTimeout(() => {
count.value = 500
},2000)
// 3.跨层级传递函数 => 给子孙后代传递可以修改数据的方法
provide('chengeCount', (newCount) => {
count.value = newCount
})
</script>
<template>
<div>
<h1>我是顶层组件</h1>
<CenterCom></CenterCom>
</div>
</template>
中间层组件:
<script setup>
import BottomCom from './bottom-com.vue'
</script>
<template>
<div>
<h2>我是中间组件</h2>
<BottomCom></BottomCom>
</div>
</template>
底层组件:
<script setup>
import { inject } from 'vue';
const themeColor = inject('theme-color')
const count = inject('count')
const chengeCount = inject('chengeCount')
const clickFn = () => {
chengeCount(1000)
}
</script>
<template>
<div>
<h3>我底层组件 -- {{ themeColor }} -- {{ count }}</h3>
<button @click="clickFn">更新count</button>
</div>
</template>
12.Vue3.3新特性-defineOptions
背景说明:
有 <script setup> 之前,如果要定义 props, emits 可以轻而易举地添加一个与 setup 平级的属性。但是用了 <script setup> 后,就没法这么干了 setup 属性已经没有了,自然无法添加与其平级的属性。
为了解决这一问题,引入了 defineProps 与 defineEmits 这两个宏。但这只解决了 props 与 emits 这两个属性。如果我们要定义组件的 name 或其他自定义的属性,还是得回到最原始的用法——再添加一个普通的 <script> 标签。这样就会存在两个 <script> 标签。让人无法接受。
所以在 Vue 3.3 中新引入了 defineOptions 宏。顾名思义,主要是用来定义 Options API 的选项。可以用defineOptions 定义任意的选项, props, emits, expose, slots 除外(因为这些可以使用 defineXXX 来做到)
<script setup>
defineOptions({
name: 'LoginIndex'
})
</script>
<template>
<div>
我是登录页
</div>
</template>
13.Vue3.3新特性-defineModel
Vue3 中的 v-model 和 defineModel
在Vue3中,自定义组件上使用v-model, 相当于传递一个modelValue属性,同时触发 update:modelValue 事件
我们需要先定义 props,再定义 emits 。其中有许多重复的代码。如果需要修改此值,还需要手动调用 emit 函数。
Vue 3.0 -- Day 2
1.Pinia 快速入门
Pinia 是 Vue 的最新 状态管理工具 ,是 Vuex 的 替代品
- 提供更加简单的API (去掉了 mutation )
- 提供符合,组合式风格的API (和 Vue3 新语法统一)
- 去掉了 modules 的概念,每一个 store 都是一个独立的模块
- 配合 TypeScript 更加友好,提供可靠的类型推断
2.手动添加Pinia到Vue项目
在实际开发项目的时候,关于Pinia的配置,可以在项目创建时自动添加
现在我们初次学习,从零开始:
-
使用 Vite 创建一个空的 Vue3 项目
npm create vue@latest
-
按照官方文档 安装 pinia 到项目中
yarn add pinia # 或者使用 npm npm install pinia
再main.js中配置:
组合式API:
Setup Store:
3.Pinia基础使用 - 计数器案例 -- 组合式API
1.定义store(我们得知道 Store 是用 defineStore()
定义的,它的第一个参数要求是一个独一无二的名字:)
2.组件使用store
getters实现
Pinia中的 getters 直接使用 computed函数 进行模拟, 组件中需要使用需要把 getters return出去
action异步实现
编写方式:异步action函数的写法和组件中获取异步数据的写法完全一致
接口地址:http://geek.itheima.net/v1_0/channels
需求:在Pinia中获取频道列表数据并把数据渲染App组件的模板中
npm install axios -S 先安装axios
store下的channel中:
App.vue中:
storeToRefs工具函数
使用storeToRefs函数可以辅助保持数据(state + getter)的响应式解构
4.Pinia的调试
Vue官方的 dev-tools 调试工具 对 Pinia直接支持,可以直接进行调试
Pinia持久化插件
官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/
- 安装插件 pinia-plugin-persistedstate
或:npm i pinia-plugin-persistedstate
- main.js 使用
import persist from 'pinia-plugin-persistedstate'
...
app.use(createPinia().use(persist))
-
store仓库中,persist: true 开启
image.png