1、vue3.0生命周期钩子函数
-
setup
创建实例前 -
onBeforeMount
挂载DOM前 -
onMounted
挂载DOM后 -
onBeforeUpdate
更新组件前 -
onUpdated
更新组件后 -
onBeforeUnmount
卸载销毁前 -
onUnmounted
卸载销毁后
2、组合API-setup函数
使用细节:
-
setup
是一个新的组件选项,作为组件中使用组合API的起点。 - 从组件生命周期来看,它的执行在组件实例创建之前
vue2.x的beforeCreate
执行。 - 这就意味着在
setup
函数中this
还不是组件实例,this
此时是undefined
- 在模版中需要使用的数据和函数,需要在
setup
返回。
<template>
<div class="container">
{{ msg }}
<button @click="say">点击</button>
</div>
</template>
<script>
export default {
name: 'App',
setup() {
console.log('setup', this);
const msg = 'Hello';
const say = () => {
console.log(1);
}
return {
msg,
say,
}
},
beforeCreate() {
console.log('beforeCreate');
}
}
</script>
3、组合API-生命周期
<template>
<div class="container">
Hello
</div>
</template>
<script>
import { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated } from 'vue'
export default {
name: 'App',
setup() {
// 1.DOM渲染前执行
onBeforeMount(() => {
console.log('DOM渲染前执行', document.querySelector('.container'));
})
// 2.DOM渲染后钩子
onMounted(() => {
console.log('DOM渲染后钩子1', document.querySelector('.container'));
})
// 可以定义多个相同的构造函数,实现不同的逻辑
onMounted(() => {
console.log('DOM渲染后钩子2', document.querySelector('.container'));
})
onBeforeUpdate(() => {
console.log('更新组件前');
})
onUpdated(() => {
console.log('更新组件后');
})
onBeforeUnmount(() => {
console.log('卸载销毁前');
})
onUnmounted(() => {
console.log('卸载销毁后');
})
}
}
</script>
4、组合API-reactive函数
定义响应式数据:
- reactuve是一个函数,它可以定义一个复杂数据类型,成为响应式数据
- 通常是用来定义响应式对象数据
<template>
<div class="container">
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<button @click="changeName">修改名字</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
// 普通数据
// const obj = {
// name: 'HS',
// age: 18,
// }
// 响应式数据
const obj = reactive({
name: 'HS',
age: 18,
})
// 修改名字
const changeName = () => {
obj.name = "ZJP";
}
return { obj, changeName }
}
}
</script>
5、组合API-toRef函数
定义响应式数据:
- toRef是函数,转换响应式对象中某个属性为单独响应式数据,并且值是关联的。
使用场景:有一个响应式对象数据,但是模版中只需要使用其中一项数据。
<template>
<div>
{{ name }}
<button @click="updateName">修改名字</button>
</div>
</template>
<script>
import { reactive, toRef } from 'vue'
export default {
name: 'App',
setup() {
// 1.响应式数据对象
const obj = reactive({
name: 'HS',
age: 18,
})
// 2.模板中只需要使用name数据
// 注意:从响应式数据对象中结构出来的属性数据,不再是响应式数据
// const { name } = obj; 不能直接解构,出来的是一个普通数据
const name = toRef(obj, 'name');
const updateName = () => {
// toRef转换响应式数据包装成对象,value是存放值的位置
name.value = "ZJP";
}
return {
name,
updateName,
}
}
}
</script>
toRefs
定义响应式数据:
- toRefs是函数,转换响应式对象中所有属性为单独响应式数据,对象成为普通对象,并且值是关联的
<template>
<div>
{{ name }}
{{ age }}
<button @click="updateName">修改名字</button>
</div>
</template>
<script>
import { reactive, toRefs } from 'vue'
export default {
name: 'App',
setup() {
// 1.响应式数据对象
const obj = reactive({
name: 'HS',
age: 18,
})
// 2.解构或者展开响应式数据对象
// const { name, age } = obj;
// console.log(name, age);
// const obj2 = { ...obj };
// console.log(obj2);
// 以上方式导致数据就不是响应式数据了
const obj3 = toRefs(obj);
console.log(obj3);
const updateName = () => {
obj3.name.value = "ZJP";
}
return {
...obj3,
updateName,
}
}
}
</script>
6、组合API-ref函数
定义响应式数据:
- ref函数,常用于简单数据类型定义为响应式数据
- 再修改值,获取值的时候,需要.value
- 在模板中使用ref申明的响应式数据,可以省略.value
<template>
<div>
{{ name }}
<button @click="updateName">修改名字</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'App',
setup() {
const name = ref("HS");
const updateName = () => {
name.value = "ZJP";
}
return {
name,
updateName,
}
}
}
</script>
7、知识运用案例
基本步骤:
- 记录鼠标坐标
- 定义一个响应式数据对象,包含x和y属性。
- 在组件渲染完毕后,监听document的鼠标移动事件
- 指定move函数为事件对应方法,在函数中修改坐标
- 在setup返回数据,模版中使用
- 累加1功能
- 定义一个简单数据类型的响应式数据
- 定义一个修改数字的方法
- 在setup返回数据和函数,模板中使用
<template>
<div class="container">
<div>坐标</div>
<div>x: {{ x }}</div>
<div>y: {{ y }}</div>
<hr>
<div>{{ count }} <button @click="add">累加1</button></div>
</div>
</template>
<script>
import { onMounted, onUnmounted, reactive, ref, toRefs } from 'vue'
const useMouse = () => {
// 1. 记录鼠标坐标
// 1.1 申明一个响应式数据,他是一个对象,包含x y
const mouse = reactive({
x: 0,
y: 0
})
// 1.3 修改响应式数据
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
// 1.2 等dom渲染完毕。去监听事件
onMounted(() => {
document.addEventListener('mousemove', move)
})
// 1.4 组件消耗,删除事件
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
return mouse
}
export default {
name: 'App',
setup() {
const mouse = useMouse()
// 2. 数字累加
const count = ref(0)
const add = () => {
count.value++
}
return { ...toRefs(mouse), count, add }
}
}
</script>
<style scoped lang="less">
</style>
8、组合API-computed函数
定义计算属性:
- computed函数,是用来定义计算属性的,计算属性不能修改。
<template>
<div class="container">
<div>今年:{{age}}岁</div>
<div>后年:{{newAge}}岁</div>
</div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
name: 'App',
setup () {
// 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
const age = ref(16)
// 得到后年的年龄
const newAge = computed(()=>{
// 该函数的返回值就是计算属性的值
return age.value + 2
})
return {age, newAge}
}
}
</script>
高级写法
<template>
<div class="container">
<div>今年:{{ age }}岁</div>
<div>后年:{{ newAge }}岁</div>
<input type="number" v-model="newAge" />
</div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
name: 'App',
setup() {
// 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
const age = ref(16)
// 得到后年的年龄
const newAge = computed({
// get函数,获取计算属性的值
get() {
return age.value + 2
},
// set函数,当你给计算属性设置值的时候触发
set(value) {
age.value = value - 2;
}
})
return { age, newAge }
}
}
</script>
9、组合API-watch函数
定义计算属性:
- watch函数,是用来定义侦听器的
监听ref定义的响应式数据
监听多个响应式数据数据
监听reactive定义的响应式数据
监听reactive定义的响应式数据,某一个属性
深度监听
默认执行
<template>
<div class="container">
<div>
<p>count的值:{{ count }}</p>
<button @click="add">改数据</button>
</div>
<hr />
<div>
<p>{{ obj.name }}</p>
<p>{{ obj.age }}</p>
<p>{{ obj.brand.name }}</p>
<button @click="updateName">改名字</button>
<button @click="updateBrandName">改品牌名字</button>
</div>
</div>
</template>
<script>
import { reactive, ref, watch } from "vue";
export default {
name: "App",
setup() {
const count = ref(0);
const add = () => {
count.value++;
};
// 当你需要监听数据的变化就可以使用watch
// 1. 监听一个ref数据
// 1.1 第一个参数 需要监听的目标
// 1.2 第二个参数 改变后触发的函数
// watch(count, (newVal,oldVal)=>{
// console.log(newVal,oldVal)
// })
const obj = reactive({
name: "ls",
age: 10,
brand: {
id: 1,
name: "宝马",
},
});
const updateName = () => {
obj.name = "zs";
};
const updateBrandName = () => {
obj.brand.name = "奔驰";
};
// 2. 监听一个reactive数据
watch(obj, () => {
console.log("数据改变了");
});
watch(
() => obj.brand,
() => {
console.log("brand数据改变了");
},
{
// 5. 需要深度监听
deep: true,
// 6. 想默认触发
immediate: true,
}
);
// 3. 监听多个数据的变化
// watch([count, obj], ()=>{
// console.log('监听多个数据改变了')
// })
// 4. 此时监听对象中某一个属性的变化 例如:obj.name
// 需要写成函数返回该属性的方式才能监听到
// watch(()=>obj.name,()=>{
// console.log('监听obj.name改变了')
// })
return { count, add, obj, updateName, updateBrandName };
},
};
</script>
10、组合API-ref属性
获取DOM或者组件实例可以使用ref属性,写法和vue2.0需要区分开
<template>
<div class="container">
<!-- vue2.0 获取div元素 -->
<!-- 1. 通过ref属性绑定该元素 -->
<!-- 2. 通过this.$ref.box获取元素 -->
<!-- <div>我是box</div> -->
<!-- vue2.0 获取v-for遍历多个元素 -->
<!-- 1. 通过ref属性绑定被遍历元素 -->
<!-- 2. 通过this.$ref.li 获取所有遍历元素(想取某一个用index取) -->
<!-- <ul>
<li v-for="i in 4" :key="i" ref="li">{{ i }}</li>
</ul> -->
<!-- 单个元素 -->
<div ref="dom">我是box</div>
<!-- 被遍历的元素 -->
<ul>
<li v-for="i in 4" :key="i" :ref="setDom">第{{ i }}LI</li>
</ul>
</div>
</template>
<script>
import { ref, onMounted } from "vue";
export default {
name: "App",
setup() {
// 1.获取单个元素
// 1.1 先定义一个空的响应式数据ref定义的
// 1.2 setup中返回该数据,你想获取哪个dom元素,在该元素上使用ref属性绑定该数据即可
const dom = ref(null);
onMounted(() => {
console.log(dom.value);
});
// 2.获取v-for遍历的元素
// 2.1 定义一个空数组,接收所有的LI
// 2.2 定义一个函数,往空数组push DOM
const domList = [];
const setDom = (el) => {
domList.push(el);
};
console.log(domList);
return { dom, setDom };
},
};
</script>
11、组合API-父子通讯
<template>
<div class="container">
<h3>父组件</h3>
<p>{{ money }}</p>
<hr />
<Son :money="money" @change-money="updateMoney" />
</div>
</template>
<script>
import { ref } from "vue";
import Son from "./Son.vue";
export default {
name: "App",
components: { Son },
setup() {
const money = ref(100);
const updateMoney = (num) => {
money.value = num;
};
return { money, updateMoney };
},
};
</script>
<template>
<div class="container">
<h3>子组件</h3>
<p>{{ money }}</p>
<button @click="changemoney">花50元</button>
</div>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
name: "Son",
// 子组件接受父组件数据使用props
props: {
money: {
type: Number,
default: 0,
},
},
// props 父组件数据
// emit 触发自定义事件的函数
setup(props, { emit }) {
// 获取父组件数据money
console.log(props.money);
// 向父组件传值
const changemoney = () => {
emit("change-money", 50);
};
return {
changemoney,
};
},
};
</script>
12、组合API-依赖注入
<template>
<div class="container">
<h1>父组件 {{ money }} <button @click="money = 1000">发钱</button></h1>
<hr />
<Son />
</div>
</template>
<script>
import { provide, ref } from "vue";
import Son from "./Son.vue";
export default {
name: "App",
components: {
Son,
},
setup() {
const money = ref(100);
const changeMoney = (saleMoney) => {
console.log("changeMoney", saleMoney);
money.value = money.value - saleMoney;
};
// 将数据提供给后代组件 provide
provide("money", money);
// 将函数提供给后代组件 provide
provide("changeMoney", changeMoney);
return { money };
},
};
</script>
<style scoped lang="less"></style>
<template>
<div class="container">
<h2>子组件 {{ money }}</h2>
<hr />
<GrandSon />
</div>
</template>
<script>
import { inject } from "vue";
import GrandSon from "./GrandSon.vue";
export default {
name: "Son",
components: {
GrandSon,
},
setup() {
// 接收祖先组件提供的数据
const money = inject("money");
return { money };
},
};
</script>
<style scoped lang="less"></style>
<template>
<div class="container">
<h3>孙组件 {{ money }} <button @click="fn">消费20</button></h3>
</div>
</template>
<script>
import { inject } from "vue";
export default {
name: "GrandSon",
setup() {
const money = inject("money");
// 孙组件,消费50,通知父组件App.vue组件,进行修改
// 不能自己修改数据,遵循单选数据流原则,大白话:数据谁定义谁修改
const changeMoney = inject("changeMoney");
const fn = () => {
changeMoney(20);
};
return { money, fn };
},
};
</script>
<style scoped lang="less"></style>
13、补充-v-model语法糖
在vue2.0中v-mode语法糖简写的代码 <Son :value="msg" @input="msg=$event" />
在vue3.0中v-model语法糖有所调整:<Son :modelValue="msg" @update:modelValue="msg=$event" />
<template>
<div class="container">
<!-- 如果你想获取原生事件事件对象 -->
<!-- 如果绑定事函数 fn fn(e){ // e 就是事件对象 } -->
<!-- 如果绑定的是js表达式 此时提供一个默认的变量 $event -->
<h1 @click="$event.target.style.color = 'red'">父组件 {{ count }}</h1>
<hr />
<!-- 如果你想获取自定义事件 -->
<!-- 如果绑定事函数 fn fn(data){ // data 触发自定义事件的传参 } -->
<!-- 如果绑定的是js表达式 此时 $event代表触发自定义事件的传参 -->
<!-- <Son :modelValue="count" @update:modelValue="count=$event" /> -->
<Son v-model="count" />
</div>
</template>
<script>
import { ref } from "vue";
import Son from "./Son.vue";
export default {
name: "App",
components: {
Son,
},
setup() {
const count = ref(10);
return { count };
},
};
</script>
<template>
<div class="container">
<h2>子组件 {{ modelValue }} <button @click="fn">改变数据</button></h2>
</div>
</template>
<script>
export default {
name: "Son",
props: {
modelValue: {
type: Number,
default: 0,
},
},
setup(props, { emit }) {
const fn = () => {
// 改变数据
emit("update:modelValue", 100);
};
return { fn };
},
};
</script>
总结: vue3.0封装组件支持v-model的时候,父传子:modelValue
子传父 @update:modelValue
补充: vue2.0的 xxx.sync
语法糖解析 父传子 :xxx
子传父 @update:xxx
在vue3.0 使用 v-model:xxx
代替。
14、补充-mixins语法
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
全局混入
// main.js
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
// vue2.0 Vue.mixin({ 全局混入的选项对象 })
// vue3.0 app.mixin({ 全局混入的选项对象 })
app.mixin({
// 在任何组件 dom准备好的时候 打印一句话
methods: {
say() {
console.log(this.$el,'dom准备好了');
}
},
mounted() {
this.say();
}
})
app.mount('#app');
局部混入
// mixins.js
// 配置对象
export const followMixin = {
data () {
return {
loading: false
}
},
methods: {
followFn () {
this.loading = true
// 模拟请求
setTimeout(()=>{
// 省略请求代码
this.loading = false
},2000)
}
}
}
<template>
<div class="container1">
<h1>
作者:周杰伦
<a href="javascript:;" @click="followFn">{{ loading ? "请求中..." : "关注" }}</a>
</h1>
<hr />
<Son />
</div>
</template>
<script>
import Son from "./Son.vue";
import { followMixin } from "./mixins.js";
export default {
name: "App",
components: {
Son,
},
mixins: [followMixin],
};
</script>
<template>
<div class="container2">
<h2>
作者:周杰伦
<button @click="followFn">{{ loading ? "loading..." : "关注" }}</button>
</h2>
</div>
</template>
<script>
import { followMixin } from "./mixins.js";
export default {
name: "Son",
mixins: [followMixin],
};
</script>
<style scoped lang="less"></style>
15、vuex
全局使用方法
import { createStore } from 'vuex'
// vue2.0 创建仓库 new Vuex.Store({})
// vue3.0 创建仓库 createStore({})
export default createStore({
state: {
username: 'zs'
},
getters: {
newName(state) {
return state.username + '!!!'
}
},
mutations: {
updateName(state, payload) {
state.username = payload
}
},
actions: {
updateName(ctx) {
// 发送请求
setTimeout(() => {
ctx.commit('updateName', 'ls')
}, 1000)
}
},
modules: {
}
})
<template>
<div>
App
<!-- 1.使用根模块的数据 -->
<p>{{ $store.state.username }}</p>
<!-- 2.使用根模块getters的数据 -->
<p>{{ $store.getters.newName }}</p>
<button @click="mutationsFn">mutationsFn</button>
</div>
</template>
<script>
import { useStore } from 'vuex'
export default {
name: 'App',
setup() {
// 使用vuex仓库
const store = useStore()
// 1.使用根模块state的数据
console.log(store.state.username)
// 2.使用根模块getters的数据
console.log(store.getters.newName)
const mutationsFn = () => {
// 3.提交根模块的mutations函数
// store.commit('updateName', 'ls')
// 4.调用根模块actions函数
store.dispatch('updateName')
}
return { mutationsFn }
}
}
</script>
分模块使用
- 存在两种情况
- 默认的模块,
state
区分模块,其他getters
mutations
actions
都在全局。 - 带命名空间
namespaced: true
的模块,所有功能区分模块,更高封装度和复用。
- 默认的模块,
import { createStore } from 'vuex'
// A模块
const moduleA = {
state: {
username: 'moduleA'
},
getters: {
newName(state) {
return state.username + '!!!'
}
},
mutations: {
updateName(state) {
state.username = 'moduleAAAAAA'
}
}
}
// B模块
const moduleB = {
namespaced: true,
state: {
username: 'moduleB'
},
getters: {
newName(state) {
return state.username + '!!!'
}
},
mutations: {
updateName(state, payload) {
state.username = payload
}
},
actions: {
updateName(ctx) {
// 发送请求
setTimeout(() => {
ctx.commit('updateName', 'zs')
}, 1000)
}
}
}
export default createStore({
modules: {
moduleA,
moduleB
}
})
<template>
<div>
<!-- moduleA -->
<!-- 1.1 使用A模块的state数据 -->
<p>{{ $store.state.moduleA.username }}</p>
<!-- 1.2 使用A模块的getters数据 -->
<p>{{ $store.getters.newName }}</p>
<!-- moduleB -->
<!-- 2.1 使用B模块的state数据 -->
<p>{{ $store.state.moduleB.username }}</p>
<!-- 2.2 使用B模块的getters数据-->
<p>{{ $store.getters['moduleB/newName'] }}</p>
<button @click="mutationsFn">mutationsFn</button>
<button @click="actionsFn">actionsFn</button>
</div>
</template>
<script>
import { useStore } from 'vuex'
export default {
name: 'App',
setup() {
const store = useStore()
const mutationsFn = () => {
// 2.3 提交B模块的修改
store.commit('moduleB/updateName', 'ls')
}
const actionsFn = () => {
// 2.4 调用B模块的actions
store.dispatch('moduleB/updateName')
}
return {
mutationsFn,
actionsFn
}
}
}
</script>
16、vuex持久化插件
-
npm i vuex-persistedstate
-
store 里面配置
import { createStore } from 'vuex' +import createPersistedstate from 'vuex-persistedstate' import user from './modules/user' import cart from './modules/cart' import category from './modules/category' export default createStore({ modules: { user, cart, category }, + plugins: [ + createPersistedstate({ + key: 'erabbit-client-pc-store', + paths: ['user', 'cart'] + }) + ] })
-
使用
// 用户模块 export default { namespaced: true, state () { return { // 用户信息 profile: { id: '', avatar: '', nickname: '', account: '', mobile: '', token: '' } } }, mutations: { // 修改用户信息,payload就是用户信息对象 setUser (state, payload) { state.profile = payload } } }
<template>
<div class="container">
<!-- 修改数据,测试是否持久化 -->
App {{ $store.state.user.profile.account }}
<button @click="$store.commit('user/setUser', { account: 'zhousg' })">
设置用户信息
</button>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
网友评论