创建项目
官方最新的脚手架工具create-vue,底层将构建工具webpack换成了vite
构建项目速度很快
安装create-vue:node版本16及以上
bash
npm init vue@latest
# 安装一下vite
npm install
提供create函数创建对象(工厂模式)
组合式 API :核心思想是直接在函数作用域内定义响应式状态变量
template中可以不必根节点
setup
关于setup函数
- beforeCreate方法之前自动执行,实例还没有被创建,this属于undefined
- 内部代码规则:定义需要的变量和函数,使用组合式API,最后以封装成对象return
- 可使用setup语法糖简化
<script setup>
由上图的生命周期可以看出,之前写在beforeCreate和created都放在setup 函数中执行
之前写在distory中的现在是beforeUnmount和unmounted
声明式渲染和响应式ref
声明式渲染:框架封装了一些常用的DOM操作,直接使用提供的API或者指令就能实现需要的效果
响应式:当 JavaScript 中的数据变化时,HTML 视图自动更新
默认声明的数据是不响应式的
javascript
<script setup>
import { ref } from 'vue'
let count = 1 // 定义了一个普通变量
const add = ()=> {
count++
}
</script>
<template>
<h1>{{count}}</h1>
<button @click="add">
+1
</button>
</template>
效果:视图上的数据并没有发生改变
vue2实现响应式,通过监听data
中的对象和属性,触发dom操作如innerHTML
vue3实现响应式提供两个函数创建响应式对象:
- reactive():把一个对象传进去变成一个响应式对象,不能传普通类型
- ref():可以传普通类型,因为会裹一层对象,使用
.value
暴露值,在template中不写.value
使用reactive获得一个响应式对象
javascript
<script setup>
import { reactive } from 'vue'
const counter = reactive({
count: 0
})
const add = () => {
counter.count++
}
</script>
<template>
<h1>{{counter}}</h1>
<button @click="add">
+1
</button>
</template>
使用ref实现相同的功能
javascript
<script setup>
import { ref } from 'vue'
const counter = ref({
count: 0
})
const add = () => {
counter.value.count++ // 需要使用.value获得数据
}
</script>
<template>
<h1>{{counter}}</h1>
<button @click="add">
+1
</button>
</template>
组件传值
都遵循单向数据流原则
父子
父传子:
给子组件添加属性,父组件通过v-bind:属性名
传值
vue2使用props选项,vue3使用组合函数defineProps()
,数组或者是对象注册
传递函数:defineEmits
注册函数名,返回一个emit对象,需要传递一个函数名数组声明函数属性
原来vue2中this.$emit('函数名',参数)
干了两件事,注册并调用
vue3把注册和调用分开了
js
// 子组件注册
const emit = defineEmits(['事件名'])
// 子组件调用事件
emit('事件名', 参数)
下面有个简单案例
父组件:
javascript
<script setup>
import { ref } from 'vue';
import SonCom from './SonCom.vue';
const money = ref(100)
</script>
<template>
<div class="parentCom">
我是老父亲,家底:{{ money }}
<div>
<button @click="money++">挣钱 +1</button>
</div>
<SonCom @earn="money++" @spend="money--" :money="money"></SonCom>
</div>
</template>
<style>
.parentCom {
height: 200px;
width: 300px;
padding: 10px;
border: 1px black solid;
}
</style>
子组件:
javascript
<template>
<div class="son">
我是子组件,余额:{{ money }}
<div>
<button @click="emit('spend')" >挥霍 +1</button>
<button @click="emit('earn')" >打工 +1</button>
</div>
</div>
</template>
<script setup>
defineProps(['money'])
const emit = defineEmits(['spend','earn']) // 注册了两个事件
</script>
<style>
.son {
height: 100px;
width: 150px;
margin-top: 10px;
border: 1px black solid;
}
</style>
跨层级
多层嵌套
vue2中的provide&inject使用方式较为复杂,一般都直接使用vuex了
vue3中的provide&inject使用极为方便
js
provide(key,value) // 提供数据
inject(key) // 获得数据
key是字符串,value可以是变量,响应式变量,或者修改函数
模版引用ref
就是给dom元素对象或者是组件对象添加唯一标识
类似于id,作用范围在当前组件中
1 声明一个 ref 来存放该元素的引用,规则就是要用ref()
javascript
const 引用名 = ref(null)
2 给dom元素(组件)添加ref属性
html
<div ref="引用名" .../>
3 在onMounted函数中操作获得的元素对象,要等对象挂载完成才能获取得到
关于引用现象
子组件:
javascript
<template>
<div>
我是B组件
</div>
</template>
<script setup>
let count = 100 // 普通变量
const sayHi = function() {
console.log('hi'+ count);
}
defineExpose({
count,
sayHi
})
</script>
父组件:
javascript
<script setup>
import { onMounted, ref } from 'vue';
import BCom from './BCom.vue';
const bcom = ref(null)
onMounted(()=> {
console.log(bcom.value.count); // 外部访问
bcom.value.count = 10 // 修改了值
bcom.value.sayHi()
console.log(bcom.value.count);
})
</script>
结果:子组件中的count没变
暴露一个响应式变量,外部组件正常访问,不要.value
javascript
<template>
<div>
我是B组件
</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(100) // 响应式变量,外面裹了一层对象
const sayHi = function() {
console.log('hi'+count.value);
}
defineExpose({ // 暴露属性
count,
sayHi
})
</script>
结果:子组件中的值被修改了
Vue3.3新特性
1、defineOptions
可以在setup函数中添加平级属性,如name
js
defineOptions({
name: 'Index'
})
2、defineModel 实验性的父子组件的双向绑定
Pinia
状态管理,vuex的vue3官方替代方案
pinia官方文档
bash
# 使用 npm
npm install pinia
组合式API使用就正常定义需要共享的数据,包括同步异步的方法
js
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment } // 直接暴露
})
pinia的响应式是通过reactive实现的
即维护的状态发生改变,组件中引用的状态也会跟着改变
为了从 store 中提取属性时保持其响应性,你需要使用 storeToRefs()
方法的话就直接解构
js
<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` 和 `doubleCount` 是响应式的 ref
// 同时通过插件添加的属性也会被提取为 ref
// 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性
const { name, doubleCount } = storeToRefs(store)
// 作为 action 的 increment 可以直接解构
const { increment } = store
</script>
pinia自动持久化插件
安装插件:
bash
npm i pinia-plugin-persistedstate
将插件注入到pinia对象中
javascript
import { createApp } from 'vue'
import { createPinia } from 'pinia'
// 导入插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate) // 注入pinia
const app = createApp(App)
app.use(pinia)
app.mount('#app')
在store中添加新参数,开启持久化
默认保存整个store,方式是localStorage
js
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useStore = defineStore(
'main',
() => {
const someState = ref('hello pinia')
return { someState }
},
{
persist: true, // 开启持久化
},
)
可以配置key修改键名,配置path保存指定数据