六、框架
1、vue
1.1、vue生命周期
- beforeCreate,创建前状态,(当前阶段data,methods,computed,watch上的数据和方法都不能被访问)
- created,进入数据观测阶段,可以使用和更改数据,(但是不会触发updated函数,当前阶段无法与Dom进行交互)
- beforeMount,发生在挂载前
- mounted,挂载完成后,(当前的真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$ refs 属性对Dom 进行操作)
- beforeUpdate,发生在更新前,可以在当前阶段进行更改数据,不会造成重复渲染
- updated ,发生在更新后,当前阶段Dom已完成更新。
- beforeDestroy,销毁前,当前阶段实例完成可以被使用,此阶段可以善后处理一些东西,例如定时器
- destroyed,销毁后,此时已经Dom已之剩下空壳,组件已被拆解,数绑定被卸除,监听被移除,
1.2、nextTick原理
1.3、diff算法
1.4、vue中组件传值方法有哪些?
参考文献
-
父子组件通信
props //父->子
$emit、$on //子->父
ref // 获取实例方式调用组件的属性或者方法 //子->父
$parent / $children //获取父子组件实例
-
一些实际🌰 -
子组件向父组件传值 ref/refs
ref 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;
如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据。也算是子组件向父组件传值的一种。父组件
<template> <child ref="childForRef"></child> </template> <script> import child from './child.vue' export default { components: { child }, mounted() { const childForRef = this.$refs.childForRef; console.log(childForRef.name); childForRef.sayHello(); } } </script>子组件
export default { data () { return { name: 'Vue.js' } }, methods: { sayHello () { console.log('hello') } } } -
兄弟组件通信
eventBus Vue.prototype.$bus = new Vue
vuex
-
一些实际🌰 -
兄弟组件和隔代组件 eventBus eventBus 又称为事件总线,在Vue中可以使用它作为沟通的桥梁,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
-
首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它。 bus.js:
import Vue from 'vue' export const bus = new Vue() -
发送事件,假设有 child1、child2 两个兄弟组件,在 child1.vue 中发送事件。 parent.vue:
<template> <div> <child1></child1> <child2></child2> </div> </template> <script> import child1 from './child1.vue' import child2 from './child2.vue' export default { components: { child1, child2 } } </script>child1.vue:
<template> <div> <button @click="additionHandle">+加法器</button> </div> </template> <script> import {bus} from '@/bus.js' console.log(bus) export default { data(){ return{ num:1 } }, methods:{ additionHandle(){ bus.$emit('addition', { num:this.num++ }) } } } </script>在 child2.vue 中接收事件。
<template> <div>计算和: {{count}}</div> </template> <script> import { bus } from '@/bus.js' export default { data() { return { count: 0 } }, mounted() { bus.$on('addition', arg=> { this.count = this.count + arg.num; }) } } </script>如果想移除事件的监听, 可以像下面这样操作:
import { bus } from './bus.js' Bus.$off('addition', {}) -
跨级通信
vuex
$ attrs / $ listeners
// $ attrs 里面存放的父组件中绑定的非Props属性
// $ listeners 里面存放的父组件绑定的非原生事件
provide / inject
// provide:一个对象或返回一个对象的函数
// inject:一个字符串数组,或 一个对象,对象的 [key] 是本地的绑定名
-
一些实际🌰 -
兄弟组件和隔代组件 Vuex
vuex的 store.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const state = { // 初始化A和B组件的数据,等待获取 AMsg: '', BMsg: '' } const mutations = { receiveAMsg(state, payload) { // 将A组件的数据存放于state state.AMsg = payload.AMsg }, receiveBMsg(state, payload) { // 将B组件的数据存放于state state.BMsg = payload.BMsg } } export default new Vuex.Store({ state, mutations }) -
兄弟组件和隔代组件 listeners parent.vue
<template> <div> <child-a :name="name" :age="age" :gender="gender" :height="height" title="嘿嘿嘿"></child-a> </div> </template> <script> import ChildA from './ChildA' export default { components: { ChildA }, data() { return { name: "zhang", age: "18", gender: "女", height: "168" }; } }; </script>// childCom1.vue
<template class="border"> <div> <p>name: {{ name}}</p> <p>childCom1的$attrs: {{ $attrs }}</p> <child-com2 v-bind="$attrs"></child-com2> </div> </template> <script> const childCom2 = () => import("./childCom2.vue"); export default { components: { childCom2 }, inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性 props: { name: String // name作为props属性绑定 }, created() { console.log(this.$attrs); // { "age": "18", "gender": "女", "height": "158", "title": "程序员成长指北" } } }; </script>// childCom2.vue
<template> <div class="border"> <p>age: {{ age}}</p> <p>childCom2: {{ $attrs }}</p> </div> </template> <script> export default { inheritAttrs: false, props: { age: String }, created() { console.log(this.$attrs); // { "gender": "女", "height": "158", "title": "程序员成长指北" } } }; </script> -
兄弟组件和隔代组件 provide / inject 代码执行顺序
data
provide
created
mounted
1.5、说一下Vue双向绑定的原理
- 初始化用户传入的data数据
- 将数据进行观测(new Observer)
- 进行对象的处理(this.walk(value))
- 循环对象属性,定义响应式变化(defineReactive)
- 重新定义数据 (Object.defineProperty)
1.6、computed与watch有什么区别,及运用场景
| 场景 | computed | watch |
|---|---|---|
| 基础区别 | 本质是具备缓存的watcher, 依赖的属性发生变化就会更新视图 | 没有缓存性,更多的是观察作用, 可以监听某些数据执行回调 |
| 适用范围 | 计算比较消耗性能的计算场景 关联多个实时计算的对象 | 当需要在数据变化响应时,执行异步操作 或者高性能消耗 |
| 触发时机 | - | 只有值发生改变才会执行, 如果要立即处理,进页面就触发, 可以在watch里面 设置immediate: true |
实际例子
- 当表达式过于复杂时候,在模板中放入过多的逻辑会让模板难以维护,可以使用computed作为计算属性处理。
- watch,当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样变会对对象中的每个属性进行监听。(同时也会产生性能问题,优化的话可以使用字符串形式监听)。
2、vuex
2.1、基本属性有哪些
- state
- getter
- mutation
- action
- module
3、React
参考文献
3.1、React hooks
| react-hooks api |
|---|
| useState |
| useEffect |
| useContext |
| useCallback |
| useRef |
| useMemo |
具体使用
-
1.1、基本语法
const [state,setState]=useState(initialState)- 传入的唯一参数:
initialState,可以是数字 useState(0)字符串对象 useState({a:1})数组 useState[1,2]可以传入函数,来通过逻辑计算出默认值
function App (props) { const [ count, setCount ] = useState(() => { return props.count || 0 }) return ( <div> 点击次数: { count } <button onClick={() => { setCount(count + 1)}}>点我</button> </div> ) } - 返回的是包含
两个元素的数组-
第一个state 变量 -
第二个 setState 是修改state值的方法,是一个函数
-
- 传入的唯一参数:
-
1.2、看个🌰
import React, { useState} from "react"; const [count, setCount]= useState(0) return( <div> 点击次数:{count} <button onClick={() => { setCount(count + 1)}}>点我</button> </div> )上面例子有些需要注意的地方
- 传入相同值,是不会重新渲染的
- 组件每渲染一次,useState中函数其实不会每次都执行
- setUseState时获取上一轮值
setCount(count=>count+1)- 多个useState的情况,尽量不要在循环,条件或嵌套函数中调用hook,必须确保是在React函数的最顶层调用。确保hook在每一次渲染中都按照同样的顺序被调用。
-
2.1、基本描述
- 主要提供了页面钩子方法,完成一些类似于class组件中生命周期
componentDidMount挂载
componentDidUpdate更新componentWillUnmount卸载
的功能 - 例如像:网络请求、手动更新
DOM、一些事件的监听,都是React更新DOM的一些副作用 - 举个🌰
import React, { useEffect} from "react"; useEffect(()=>{ console.log('useEffect被执行了') }) - 主要提供了页面钩子方法,完成一些类似于class组件中生命周期
-
2.2、基本用法
-
2.2.1、作用:通过
useEffect的Hook,告诉React需要在渲染后执行某些操作 -
2.2.2、参数:
useEffect要求我们传入一个回调函数,在React执行完更新DOM操作之后,就会回调这个函数 -
2.2.3、执行时机:首次渲染之后,或者每次更新状态之后,都会执行这个回调函数
引起注意的是,useEffect的第二个参数
- 什么都不传,组件每次 render 之后 useEffect 都会调用 <===> 等同于:
componentDidMount和componentDidUpdate
useEffect(()=>{ //TODO //具体逻辑 })- 传入一个空数组,只会调用一次, <===> 等同于:
componentDidMount和componentDidUpdate
useEffect(()=>{ //TODO //具体逻辑 },[])- 传入一个数组,其中包括变量,只有这些变量变动时,
useEffect才会执行
useEffect(()=>{ //TODO //具体逻辑 },[count]) - 什么都不传,组件每次 render 之后 useEffect 都会调用 <===> 等同于:
-
🤔 2.3、有些时候需要清除Effect
- 2.3.1、最常见的场景:清除订阅外部数据源,防止引起内存泄露
参考代码如下
import React, { useEffect, useState } from 'react' useEffect(() => { // 默认情况下,每次渲染后都会被调用该函数 console.log('订阅一些事件') // 如果要实现 componentWillUnmount, // 在末尾处返回一个函数 // React 在该函数组件卸载前调用该方法 // 其命名为cleanup 是为了表明此函数的目的 // 但其实也可返回一个箭头函数或者起一个别名 return function cleanup() => { console.log('取消订阅') //取消订阅 } })
-
2.3.2、为啥要在
Effect中返回一个函数?-
这是因为
Effect可选的清除机制,每个Effect都可返回一个清除函数 -
如此可以将添加和移除订阅的逻辑放到一起
-
都是属于
Effect的一部分
-
-
4.1、作用
- 尤其向子组件传递函数props时,每次render,都会创建新函数,导致子组件不必要的渲染,浪费性能
const onClick = useCallback(()=>{ console.log('button click') },[])- 解决当依赖的属性没有改变时
-
4.2、基本使用
useCallback主要是为了进行性能优化
-
5.1、基本用法
useRef返回一个ref对象,返回的ref对象在组件的整个生命周期保持不变
const refContainer=useRef(initialState) const refContainer=useRef(0) // 返回结果 {current: 0} -
5.2、常见使用场景
- 场景一:保存一个数据,这个对象在整个生命周期可以保持不变
const [count, setCount] = useState(0); const numRef = useRef(count); // 将上次的count进行保存,在count发生改变时,重新保存count // 在点击button时,增加count时,会调用useEffect函数,渲染DOM后,会重新将上一次的值进行保存,使用ref保存上一次的某一个值不会触发render useEffect(() => { numRef.current = count; }, [count]); <div>count 上次的值:{numRef.current}</div> <div>count 这次的值:{count}</div> <button onClick={(e) => setCount(count + 10)}>+10</button>- 场景二:引入
DOM(或者组件)元素,但是前提条件需要是class组件
class childTest extends React.Component { render() { return <div>childTest</div> } } export default function demo() { const titleRef = useRef() const cpnRef = useRef() function changeDOM() { // 修改DOM titleRef.current.innerHTML = 'hello world' console.log(cpnRef.current) } return ( <div> {/* 1.修改DOM元素 */} <h2 ref={titleRef}>RefHookDemo01</h2> {/* 2.获取class组件 */} <ChildCpn ref={cpnRef} /> <button onClick={changeDOM}>修改DOM</button> </div> ) }
-
6.1、作用:
- 6.1.1、为了进行性能的优化
- 6.1.2、和Vue中的
computed计算属性类似,都是根据依赖的值计算出结果,如果依赖的值未发生改变的时候,不触发状态改变
const [count, setCount] = useState(0); const add = useMemo(() => { return count + 1; }, [count]); <div> 点击次数: {count} <br /> 次数加一: {add} <button onClick={() => { setCount(count + 1); }} > 点我 </button> </div> -
6.2、注意的是:
- 6.2.1、
useMemo会在渲染的时候执行,而不是渲染之后执行,这个与useEffect,不一样 - 6.2.2、
useMemo,也还是会有第二个参数,用法和useEffect类似
- 6.2.1、