异步操作
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框架时,以下是一些常用的生命周期方法及其用途的解析和说明:
constructor(props)
- 作用:组件的构造函数,在组件被实例化时执行。通常用于初始化状态(State)和绑定事件处理函数的方法。
- 注意事项:如果在构造函数中使用this.setState()来初始化状态,应避免直接修改状态对象。而是使用只能写入一次的this.state
= {…}语法。componentDidMount()
- 作用:在组件第一次渲染到DOM后执行。通常用于进行异步操作、初始化第三方库、添加事件监听器等。
- 注意事项:可以在该方法中使用this.setState()来更新状态,但请注意避免无限循环渲染。
componentDidUpdate(prevProps, prevState)
- 作用:在组件更新后执行。通常用于响应属性或状态的变化,并在需要时执行相应的操作,如重新请求数据、更新DOM等。
- 注意事项:要避免在此方法中使用this.setState(),否则可能导致无限循环更新。
componentWillUnmount()
- 作用:在组件从DOM中移除之前执行。通常用于清理工作,如取消订阅、移除事件监听器、清除定时器等。
- 注意事项:在此方法中执行的清理操作对应用的性能和内存管理至关重要。
其他生命周期方法:
- shouldComponentUpdate(nextProps, nextState): 用于优化性能,决定是否重新渲染组件。
- getDerivedStateFromProps(nextProps, prevState): 在属性更新后、状态更新前调用,用于根据新属性的值更新状态。
- getSnapshotBeforeUpdate(prevProps, prevState): 在更新之前获取DOM状态的快照,在componentDidUpdate中使用。
从React 17版本开始,使用新的异步生命周期方法替代旧的生命周期方法。例如,componentDidMount
可以使用useEffect
钩子函数来实现,而componentDidUpdate
可以使用useEffect
与依赖项数组配合使用来替代。
另外,对于函数式组件,可以使用React的钩子函数(比如useEffect和useState等)来实现类似于生命周期方法的功能。
接下来看一下17版本后的hooks:
useState(initialState)
- 作用:在函数式组件中声明状态。返回一个包含当前状态值和更新状态的函数的数组。
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> ); }
useEffect(effect, dependencies)
- 作用:在函数式组件中执行副作用操作,比如订阅事件、请求数据等。可以理解为将生命周期方法
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> ); }
useContext(context)
- 作用:在函数式组件中访问 React 上下文(Context)的值。
import React, { useContext } from 'react'; const ThemeContext = React.createContext('light'); function Example() { const theme = useContext(ThemeContext); return <div>Current theme: {theme}</div>; }
useReducer(reducer, initialState)
- 作用:在函数式组件中管理复杂的状态逻辑,类似于
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 只能在函数式组件中使用,而不能在类组件中使用。
声命周期方法的触发条件和顺序
生命周期方法的执行顺序:
挂载阶段(初次渲染):
- 函数式组件:没有类似于 componentDidMount() 的生命周期方法。
- 类组件:constructor()、render()、componentDidMount()
更新阶段:
- 函数式组件:使用 useEffect() 进行副作用操作,并根据依赖项数组中的值决定是否重新运行副作用。
- 类组件:render()、componentDidUpdate()
卸载阶段:
- 函数式组件:使用 useEffect() 返回一个清理函数来模拟 componentWillUnmount()。
- 类组件:componentWillUnmount()
更新操作触发条件和过程:
父组件重新渲染:
- 无论是函数式组件还是类组件,当父组件重新渲染时,子组件会被重新渲染。
函数式组件:
- 使用 useState() 或 useReducer() 定义的状态发生改变。
- 使用 useContext() 的上下文数据发生改变。
使用自定义的 Hook 触发更新。过程:
- 组件重新执行函数体。
- useEffect() 中的副作用操作会根据依赖项数组中的值决定是否重新运行。
类组件:
- 使用 setState() 更新状态。
- 父组件传递新的 props。
过程:
- shouldComponentUpdate() 被调用,根据返回值决定是否进行后续的更新操作。
- render() 方法重新构建并返回新的 JSX 元素结构。
- componentDidUpdate() 被调用,可以执行更新后的副作用操作。
注意,React 17 版本以后推荐使用函数式组件和 React 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等的生命周期差异和适配建议
H5 平台:
- 生命周期差异:H5 平台的生命周期与 Vue 的生命周期一致,没有特殊的差异。
- 适配建议:在 H5 平台开发时,可以按照 Vue 的生命周期函数编写代码,无需额外的适配。
小程序平台:
- 生命周期差异:小程序平台的生命周期函数与 Vue 的生命周期函数有一定的差异。小程序增加了一些自身特有的生命周期函数,如 onLaunch、onShow、onHide 等。
- 适配建议:在编写小程序的 Uniapp 项目时,需注意识别并使用小程序平台特有的生命周期函数,以满足小程序的需求。可以根据实际情况,在小程序特有的生命周期函数中添加对应的逻辑。
App 平台:
- 生命周期差异:App 平台的生命周期函数与 Vue 的生命周期函数也有一定的差异。App 平台增加了一些自身特有的生命周期函数,如 onLaunch、onShow、onHide 等。同时,App 还有一些特定于 App 端的生命周期函数,如
onBackPress、onUniNViewMessage 等。- 适配建议:在开发 App 平台的 Uniapp 项目时,需要根据实际需求适配 App 端的生命周期函数。可以通过监听特定的生命周期函数,实现 App 平台特有的行为和功能,如处理返回按钮点击、跨页面通信等。
快应用平台:
- 生命周期差异:快应用平台的生命周期函数与 Vue 的生命周期函数有一定的差异。快应用增加了一些自身特有的生命周期函数,如 onInit、onReady、onDestroy 等。
- 适配建议:在编写快应用的 Uniapp 项目时,需要根据实际需求适配快应用平台特有的生命周期函数。可以在这些生命周期函数中添加对应的逻辑和操作。
微信小游戏平台:
- 生命周期差异:微信小游戏平台的生命周期函数与 Uniapp 默认的生命周期函数不完全一致。微信小游戏平台使用的是微信小程序的生命周期函数。
- 适配建议:在开发微信小游戏平台的 Uniapp 项目时,可以参考微信小程序的生命周期函数,根据实际需求进行适配。
生命周期比较与总结
微信小程序生命周期:
- onLoad: 页面加载时触发。
- onShow: 页面显示时触发。
- onReady: 页面初次渲染完成时触发。
- onHide: 页面隐藏时触发。
- onUnload: 页面卸载时触发。
Vue 生命周期:
- beforeCreate: 实例刚创建,数据观测和事件配置之前触发。
- created: 实例已经创建完成,数据观测和事件配置完成。
- beforeMount: 模板编译/挂载之前触发。
- mounted: 模板编译/挂载完成触发。
- beforeUpdate: 组件更新之前触发。
- updated: 组件更新完成触发。
- beforeDestroy: 实例销毁之前触发。
- destroyed: 实例销毁完成触发。
React 生命周期:
- componentDidMount: 组件挂载完成后触发。
- componentDidUpdate: 组件更新完成后触发。
- componentWillUnmount: 组件卸载之前触发。
Uniapp 生命周期(基于 Vue 的生命周期):
- beforeCreate: 实例刚创建,数据观测和事件配置之前触发。
- created: 实例已经创建完成,数据观测和事件配置完成。
- beforeMount: 模板编译/挂载之前触发。
- mounted: 模板编译/挂载完成触发。
- beforeUpdate: 组件更新之前触发。
- updated: 组件更新完成触发。
- beforeDestroy: 实例销毁之前触发。
- destroyed: 实例销毁完成触发。
需要注意的是,虽然 Uniapp 的生命周期和 Vue 生命周期基本一致,但在编写 Uniapp 项目时,可能还会涉及到特定平台(如小程序、App 等)的生命周期函数。
⭐⭐⭐以下是对微信小程序、Vue、React 和 Uniapp 框架的生命周期特点、优势和限制的综合分析和总结:
微信小程序生命周期:
- 特点:微信小程序生命周期主要包括页面加载、显示、准备渲染、隐藏和卸载等阶段。
- 优势:生命周期简单易懂,适用于开发小程序应用,提供了丰富的生命周期函数来处理不同阶段的逻辑。
- 限制:生命周期函数相对较少,对于复杂的应用可能需要在不同的事件中处理逻辑,缺乏更细粒度的控制。
Vue生命周期:
- 特点:Vue 的生命周期包括实例创建、挂载、更新和销毁等多个阶段。
- 优势:生命周期函数可以帮助开发者更好地控制组件的行为,方便进行异步操作、访问 DOM 元素和处理相关事件。
- 限制:Vue 生命周期较为复杂,初学者可能需要一些时间来理解和使用。此外,在某些特定场景(例如服务端渲染)下,部分生命周期函数可能无法正常运行。
React生命周期:
- 特点:React 的生命周期分为组件挂载、更新和卸载三个阶段。
- 优势:React 提供了更细粒度的生命周期函数,可以方便地进行性能优化和状态管理,例如使用 shouldComponentUpdate 控制组件是否需要重新渲染。
- 限制:React 16.3 版本之后引入了新的生命周期函数,如 getDerivedStateFromProps 和 getSnapshotBeforeUpdate,但这些新的函数可能会增加代码复杂度,需要开发者具备一定的学习成本。
Uniapp生命周期:
- 特点:Uniapp 的生命周期几乎与 Vue 生命周期一致,可在多个平台上使用。
- 优势:Uniapp 提供了跨平台的开发能力,开发者只需编写一次代码,即可发布到不同的平台上。生命周期与 Vue 类似,易于掌握。
- 限制:由于要兼容多个平台,一些特定平台的生命周期函数可能无法使用,需要根据实际情况进行适配。
综合来说,微信小程序的生命周期简单直观,适用于小程序开发;Vue 的生命周期提供了较为丰富的控制能力,适用于构建中大型应用;React 的生命周期提供了更细粒度的控制和性能优化能力;Uniapp 提供了跨平台开发能力,适用于同时开发多个平台的应用。