🙂博主:小猫娃来啦
🙂文章核心:探索微信小程序、Vue、React和Uniapp生命周期
微信小程序、Vue、React和Uniapp的基本定义和应用领域
Vue:
React:
Uniapp:
应用领域:
微信小程序生命周期
生命周期概述
生命周期特点:
- 小程序的生命周期包括App、Page和Component三个层级,每个层级都有自己的生命周期函数。
- App层级的生命周期涵盖了整个小程序的运行过程,包括小程序的启动、后台进入前台、前台进入后台等。
- Page层级的生命周期关注于页面的加载、显示、交互和卸载等过程。
- Component层级的生命周期与自定义组件的使用相关,涵盖了自定义组件的创建、更新和销毁等。
作用:
- 生命周期函数提供了不同阶段执行代码的机会,可以在适当的时候处理数据加载、初始化操作、页面渲染、事件监听等。
- 通过生命周期函数,开发者可以在特定时刻执行相应的业务逻辑,并控制页面的展示和行为。
- 生命周期也与用户体验密切相关,可以通过合理地利用生命周期函数来提高小程序的性能和响应速度。
- 在页面跳转、数据传递等场景下,生命周期函数还可以起到数据传递和状态管理的作用。
页面生命周期
onPageLoad:
- 作用:在页面加载时触发,一般用于进行初始化操作。
- 注意事项:仅在页面第一次加载时触发,在页面切换时不会再次触发。
onShow:
- 作用:在页面显示时触发,一般用于处理页面展示前的数据加载和更新。
- 注意事项:在页面切换、后台进入前台、模态框关闭等情况下都会触发。
onReady:
- 作用:在页面初次渲染完成时触发,表示页面已经准备好了可以和用户进行交互。
- 注意事项:仅在页面初次渲染完成时触发,之后页面更新不会再触发该函数。
onHide:
- 作用:在页面隐藏时触发,一般用于处理页面切换/离开时的资源释放和数据保存操作。
- 注意事项:在页面切换、后台进入前台、模态框打开等情况下都会触发。
onUnload:
- 作用:在页面卸载时触发,一般用于清理页面相关的定时器、事件监听等资源。
- 注意事项:页面卸载时会触发,包括页面跳转、返回上一页等操作。
应用生命周期
onLaunch:
- 作用:在小程序初始化完成时触发,一般用于进行全局的初始化操作。
- 注意事项:该函数仅在小程序初始化完成时触发,只执行一次。
onHide:
- 作用:在小程序进入后台时触发,一般用于处理小程序进入后台时的数据保存和资源释放。
- 注意事项:当用户点击设备 Home 键、切换到其他小程序或锁屏时会触发该函数。
onError:
- 作用:在小程序发生脚本错误或 API 调用失败时触发,用于捕获和处理错误信息。
- 注意事项:一般用于监控和上报小程序的错误信息,可以在该函数中进行错误日志记录等操作。
onShow:
- 作用:在小程序进入前台时触发,一般用于处理小程序进入前台时的数据更新和页面刷新。
- 注意事项:当小程序从后台进入前台时会触发该函数,可以进行页面数据的更新和渲染。
组件和API的生命周期钩子
created:
- 作用:在组件实例刚刚被创建时触发,一般用于进行组件相关的初始数据设置和一些初始化操作。
- 注意事项:此时组件数据还未准备好,无法与视图进行交互。
attached:
- 作用:在组件被添加到页面节点树中时触发,一般用于组件的一些进一步初始化操作。
- 注意事项:此时组件已经可以与视图进行交互,但尚未渲染到页面上。
ready:
- 作用:在组件布局完成后触发,表示组件已经准备好可以与用户进行交互。
- 注意事项:此时组件已经渲染到页面上,可以监听事件、更新数据等。
API 的生命周期钩子函数是在使用第三方 API 或库时,提供的一些函数接口,用于在特定阶段执行自定义逻辑。以下是对 API 生命周期钩子函数的使用场景和注意事项的小结:
注意事项:
Vue生命周期
生命周期钩子函数
beforeCreate:
- 在实例被创建之初,完成数据观测和事件初始化之前被调用。
- 此时组件的实例已经被创建,但尚未完成数据响应式绑定,也没有 DOM 节点和组件实例的引用。
- 使用场景:可以在此阶段执行一些初始化操作,例如全局配置的读取、插件的安装等。
created:
- 在实例创建完成后被调用,此时实例已完成数据观测、属性和方法的运算,但尚未挂载到页面上。
- 可以访问组件实例的 data、methods、props 等属性,但无法访问到 DOM 元素。
- 使用场景:常用于进行异步数据获取、初始化非响应式数据等操作,例如发起网络请求或获取用户位置信息。
mounted:
- 在实例挂载到页面之后被调用,此时组件的 DOM 已经渲染完毕。
- 可以通过 this.$el 访问组件的根 DOM 元素,并可以直接操作 DOM。
- 使用场景:常用于进行 DOM 操作、注册事件监听器等,例如绑定图表库、初始化第三方插件等。
updated:
- 当组件的 VNode 更新后被调用,但不保证所有子组件也都已更新。
- 可以访问到更新前后的状态,可以与之前的数据进行比较,执行额外的操作。
- 使用场景:常用于监测数据变化并做出相应反应,例如根据变化请求更新数据、重新渲染组件等。
destroyed:
- 在组件实例销毁之前调用,此时组件实例已解除了数据绑定,监听事件被移除,子组件也都被销毁。
- 使用场景:可以在此阶段执行一些清理操作,例如取消网络请求、清除定时器、释放资源等。
生命周期函数的触发顺序是固定的,依次是 beforeCreate、created、mounted、updated 和 destroyed。当组件在页面上被销毁时,会依次触发 beforeDestroy 和 destroyed 生命周期函数。
响应式监听与生命周期关联
对于Vue的响应式数据变化,Vue通过使用data选项来定义组件的响应式数据。当这些数据发生变化时,Vue能够自动追踪并更新相关的DOM,从而保持视图与数据的同步。这种响应式的特性使得我们能够方便地更新和管理数据,同时减少手动操作DOM的工作量。
生命周期钩子函数是在组件实例的不同生命周期阶段被调用的函数。它们提供了一些特定的时间点,让我们可以在组件的不同生命周期中执行额外的任务。这些钩子函数包括:
-
created: 在组件实例被创建后调用。此时组件实例已完成配置过程,但尚未挂载到DOM。可以在这个阶段进行一些初始化逻辑,例如发送网络请求获取数据,并将数据赋值给组件中的响应式数据。
-
mounted: 在组件挂载到DOM后调用。此时组件已经完成挂载,可以进行DOM操作、集成第三方库等任务。例如,在这个钩子函数中,你可以初始化一些图表或其他可视化组件,并将数据渲染到相应的元素中。
-
updated: 在组件更新后调用。当组件的响应式数据发生变化时,会重新渲染组件。在这个钩子函数中,你可以根据数据的变化执行额外的操作。例如,可以重新计算一些属性或调用其他方法来处理数据依赖关系。
-
beforeDestroy: 在组件销毁之前调用。在这个钩子函数中,你可以执行一些清理工作,例如取消网络请求、清除定时器、解绑事件监听器等。这可以帮助避免内存泄漏和资源浪费问题。
使用created
钩子函数进行数据初始化:
export default {
data() {
return {
users: [],
};
},
created() {
// 发送网络请求获取用户数据
axios.get('/api/users')
.then(response => {
this.users = response.data;
})
.catch(error => {
console.log(error);
});
},
};
使用mounted
钩子函数进行第三方库的初始化和操作:
export default {
mounted() {
// 初始化图表库并将数据渲染到图表中
const chart = new Chart('#myChart', {
// 配置选项
});
chart.render(this.data);
},
};
使用updated
钩子函数处理数据变化后的操作:
export default {
data() {
return {
total: 0,
items: [],
};
},
updated() {
// 计算总数
this.total = this.items.length;
},
};
使用beforeDestroy
钩子函数进行资源清理
export default {
data() {
return {
timer: null,
};
},
mounted() {
// 在组件销毁前清除定时器
this.timer = setInterval(() => {
// 定时任务逻辑
}, 1000);
},
beforeDestroy() {
clearInterval(this.timer); // 清除定时器
},
};
生命周期的钩子函数在实际开发中的应用
数据获取
export default {
data() {
return {
users: [],
};
},
created() {
// 发送网络请求获取用户列表
axios.get('/api/users')
.then(response => {
this.users = response.data;
})
.catch(error => {
console.error(error);
});
},
};
在created
生命周期钩子函数中发送网络请求,获取用户列表,并将返回的数据赋值给组件的响应式数据users
。这样,当组件被创建时,数据就会被加载到组件中。
异步操作
export default {
methods: {
fetchData() {
// 模拟异步操作
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data fetched successfully!');
}, 2000);
});
},
},
async created() {
try {
const result = await this.fetchData();
console.log(result); // 输出:Data fetched successfully!
// 执行其他操作
} catch (error) {
console.error(error);
}
},
};
定义了一个fetchData
方法,模拟异步操作并返回一个Promise
。在created
钩子函数中,使用async/await
来处理异步操作,等待fetchData
方法的执行结果,并在结果返回后进行其他操作。
页面刷新
export default {
data() {
return {
count: 0,
};
},
mounted() {
this.count = localStorage.getItem('count') || 0;
window.addEventListener('beforeunload', this.saveData);
},
beforeDestroy() {
this.saveData();
window.removeEventListener('beforeunload', this.saveData);
},
methods: {
saveData() {
localStorage.setItem('count', this.count);
},
},
};
mounted
生命周期钩子函数来加载之前存储在localStorage
中的计数器值,并添加一个beforeunload
事件监听器,在页面即将刷新或关闭时保存数据。同时,在beforeDestroy
钩子函数中手动执行保存数据的操作,并且从事件监听器中移除。
参数传递与响应
export default {
props: ['id'],
watch: {
id(newId, oldId) {
// 监听id变化并做出响应
console.log(`ID changed from ${oldId} to ${newId}`);
// 执行其他操作
},
},
};
组件接收一个名为id
的属性,并使用watch
选项来监听id
的变化。当id的值发生变化时,会触发watch
中定义的回调函数,并对变化做出响应。
动态样式和类绑定
export default {
data() {
return {
isActive: false,
};
},
computed: {
classes() {
return {
active: this.isActive,
'text-bold': this.isActive,
};
},
},
};
根据isActive
的值动态生成一个包含样式类名的对象classes
。这样,在渲染组件时可以使用v-bind:class
指令将这些类绑定到元素上,从而实现动态样式的效果。
生命周期钩子函数的应用扩展
export default {
beforeCreate() {
// 在创建组件实例之前执行的逻辑
},
beforeMount() {
// 在组件挂载到DOM之前执行的逻辑
},
updated() {
// 组件更新后执行的逻辑
},
activated() {
// 在keep-alive组件激活时执行的逻辑
},
deactivated() {
// 在keep-alive组件停用时执行的逻辑
},
errorCaptured(err, vm, info) {
// 捕获组件内部错误的逻辑
},
};
React生命周期
类组件生命周期方法
当使用React框架时,以下是一些常用的生命周期方法及其用途的解析和说明:
componentDidMount()
componentDidUpdate(prevProps, prevState)
componentWillUnmount()
其他生命周期方法:
从React 17版本开始,使用新的异步生命周期方法替代旧的生命周期方法。例如,componentDidMount
可以使用useEffect
钩子函数来实现,而componentDidUpdate
可以使用useEffect
与依赖项数组配合使用来替代。
另外,对于函数式组件,可以使用React的钩子函数(比如useEffect和useState等)来实现类似于生命周期方法的功能。
接下来看一下17版本后的hooks:
- 作用:在函数式组件中声明状态。返回一个包含当前状态值和更新状态的函数的数组。
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
- 作用:在函数式组件中执行副作用操作,比如订阅事件、请求数据等。可以理解为将生命周期方法
componentDidMount
、componentDidUpdate
和componentWillUnmount
的功能合并到一个钩子函数中。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
- 作用:在函数式组件中访问 React 上下文(Context)的值。
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function Example() {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme}</div>;
}
- 作用:在函数式组件中管理复杂的状态逻辑,类似于
Redux
中的reducer
和state
。
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
通过使用 Hooks,我们可以在函数式组件中使用各种 React 特性,如状态管理、副作用操作、上下文等。Hooks 提供了一种更简洁、可复用和易于测试的方式来编写组件逻辑。要注意的是,Hooks 只能在函数式组件中使用,而不能在类组件中使用。
声命周期方法的触发条件和顺序
生命周期方法的执行顺序:
更新操作触发条件和过程:
注意,。函数式组件和类组件之间的生命周期方法和更新操作触发条件有一些差异,我们需要根据具体版本和编写的组件类型来正确使用相应的生命周期方法和钩子函数。
使用React Hooks对生命周期的变化与影响
一段代码来诠释一下:
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// componentDidMount 和 componentDidUpdate 的逻辑
console.log('Component did mount or update');
// 清理函数,相当于 componentWillUnmount
return () => {
console.log('Component will unmount');
};
}, [count]); // 仅在 count 改变时触发 useEffect
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
以上代码当中,useState()
用于定义状态 count
并提供更新它的方法 setCount
。
useEffect()
方法中的回调函数在组件初次渲染和每次 count
发生变化时都会被执行,模拟 componentDidMount
和 componentDidUpdate
的功能。根据依赖项数组 [count]
的设置,仅当 count
改变时才会触发 useEffect
。返回的清理函数相当于 componentWillUnmount
,用于在组件被卸载前执行清理操作。
值得一提的是,useEffect()
操作的是副作用,只要依赖项[count]
变,这个函数里的逻辑全部会执行一遍。
下面详细说一下【】
依赖项【】的解析
空数组 []
:在 useEffect
或其他依赖项相关的 Hook 中传递空数组意味着只在组件挂载和卸载时执行副作用操作,类似于 componentDidMount
和 componentWillUnmount
。这样的副作用操作仅在组件的生命周期开始和结束时运行一次,不会有其他触发重新执行的情况。
useEffect(() => {
// 仅在组件挂载和卸载时执行
console.log('Component did mount');
return () => {
console.log('Component will unmount');
};
}, []);
存在依赖项的数组:如果将依赖项数组中包含了某个状态或属性,那么只有当该状态或属性发生变化时,副作用操作才会重新执行。
const [count, setCount] = useState(0);
useEffect(() => {
// 当 count 发生变化时执行副作用操作
console.log('Count changed:', count);
}, [count]);
不传递依赖项数组:如果不传递依赖项数组,副作用操作将在每次组件渲染后都执行,类似于 componentDidMount
和 componentDidUpdate
。
useEffect(() => {
// 每次组件渲染后都执行
console.log('Component rendered');
return () => {
console.log('Component will unmount');
};
});
这里又会引出一个问题,这个useEffect
的hook,是三合一吗?
下面就这个问题,进行说明:
useEffect是三合一的hook吗?
虽然 useEffect
函数可以替代 componentDidMount、componentDidUpdate 和 componentWillUnmount 这三个生命周期方法的功能,但它并不是完全的 “三合一”。
具体来说,useEffect Hook 的功能如下:
-
componentDidMount:通过将一个空数组
[]
作为依赖项传递给 useEffect,可以模拟 componentDidMount 的效果,即在组件挂载后执行一次副作用操作。 -
componentDidUpdate:通过在 useEffect 的依赖项数组中传递特定的状态或属性,可以控制副作用操作在相应的状态或属性发生变化时执行,从而模拟 componentDidUpdate 的效果。
-
componentWillUnmount:通过在 useEffect 的返回函数中进行清理操作,可以模拟 componentWillUnmount 的效果,即在组件卸载前执行一次清理副作用操作。
因此,useEffect 可以替代上述三个生命周期方法的功能,但并不是完全等价。它主要提供了一种更简洁和集中管理副作用操作的方式,使得代码更易读和维护。
可以说useEffect
是didmount
和didupdata
的简单合并。
⭐⭐⭐关键点:
而useEffect
的执行时机,主要是,和 执行。
具体是以什么时间为准呢?
以页面真实DOM加载为准,即didmount
或didupdata
之后,即真实DOM加载完成之后执行useEffect
Uniapp生命周期
-
beforeCreate:在实例初始化之后,数据观测(data observer)和事件配置(event/watcher setup)之前被调用。
-
created:在实例创建完成后被调用,此时组件实例已经完成以下配置:数据观测(data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还未开始,$el 属性尚不可用。
-
beforeMount:在挂载开始之前被调用,在这个阶段,模板编译完成,但尚未把编译好的模板挂载到页面中。
-
mounted:在挂载完成后被调用,此时组件已经被挂载到页面中,可以操作 DOM 元素。
-
beforeUpdate:在组件更新之前被调用,发生在虚拟 DOM 重新渲染和打补丁之前,你可以在这个钩子中进一步地更改状态或修改 DOM。
-
updated:在组件更新完毕之后被调用,此时组件更新完成,DOM 已经重新渲染。
-
activated(仅 App 端可用):当前页面被激活时调用,只适用于 App 编译模式。
-
deactivated(仅 App 端可用):当前页面被停用时调用,只适用于 App 编译模式。
-
beforeDestroy:在实例销毁之前调用。在这一步,实例仍然完全可用。
-
destroyed:在实例销毁之后调用。此时,Vue 实例已经解除所有的绑定,所有的事件监听器都被移除,所有的子实例也都被销毁。
值得注意的是,Uniapp 还提供了一些特定平台的生命周期函数,如 onLaunch(小程序启动时调用)、onShow(小程序启动或从后台进入前台时调用)、onHide(小程序从前台进入后台时调用)等。这些特定平台的生命周期函数可以根据不同平台的需求进行使用。
Uniapp中扩展的生命周期钩子
在 Uniapp 中,除了常用的 Vue 组件生命周期函数外,还有一些 Uniapp 独有的生命周期函数,这些生命周期函数主要用于处理特定平台或 Uniapp 自身的事件和行为。
-
beforeEnter:在进入页面前触发,用于页面跳转前的逻辑处理。可以在这个生命周期函数中进行一些页面跳转前的操作,例如校验登录状态、权限验证等。仅在 H5 和 App 编译模式下生效。
-
onBackPress:在页面返回按钮(如安卓手机的物理返回键)被点击时触发。通过监听该生命周期函数,可以实现页面返回按钮的自定义行为。仅在 App 编译模式下生效。
-
onUniNViewMessage:在非当前页面的状态栏点击时触发,用于处理跨页面通信。Uni-app 的 nvue 页面有自己独立的渲染线程,在不同页面之间无法共享数据。通过该生命周期函数,可以实现 nvue 页面之间的通信。仅在 App 编译模式下生效。
以上是 Uniapp 独有的一些生命周期函数,它们提供了处理特定平台或 Uniapp 自身行为的能力。我们可以根据具体需求,在相关生命周期函数中编写相应的逻辑来实现特定功能。
在不同平台下,如H5、小程序、App等的生命周期差异和适配建议
生命周期比较与总结
Vue 生命周期:
React 生命周期:
Uniapp 生命周期(基于 Vue 的生命周期):
需要注意的是,虽然 Uniapp 的生命周期和 Vue 生命周期基本一致,但在编写 Uniapp 项目时,可能还会涉及到特定平台(如小程序、App 等)的生命周期函数。
⭐⭐⭐以下是对微信小程序、Vue、React 和 Uniapp 框架的生命周期特点、优势和限制的综合分析和总结:
综合来说,微信小程序的生命周期简单直观,适用于小程序开发;Vue 的生命周期提供了较为丰富的控制能力,适用于构建中大型应用;React 的生命周期提供了更细粒度的控制和性能优化能力;Uniapp 提供了跨平台开发能力,适用于同时开发多个平台的应用。