<!-- 父组件中使用子组件 --> <!-- n和list是一个自定义属性 后面的num和list是父组件的data数据 --> <child-1 :n="num" :list="list"></child-1> <!-- 子组件在default中用prop接受数据 --> props:["n","list"], //接受父组件传递过来的数据的一个自定义属性
在子组件定义的地方,添加props选项
props选项有三种写法:
props: ['num']
props: { num: Number }
this.$emit
调用 父组件的 自定义事件 并传递数据给父组件, 父组件从自定义事件
的方法中获取数据并执行事件<!-- 父组件中使用子组件 --> <!-- 通过自定义的事件利用方法获取到子组件的数据 --> <Child2 @abcd="setUsername"></Child2> 并在default中定义方法 methods:{ setUsername(value){ this.username = value; } } <!-- 子组件中使用this.$emit调用事件abcd传递数据this.uesername --> <button @click="fn">将数据传给父组件</button> methods:{ fn(){ this.$emit("abcd",this.username); } }
需求: 将 child1 更新或改变的数据传递给同级的 child2接受
思路: 利用公共的父元素
步骤: 先实现子传父(利用自定义事件) => 再父传子(利用)
const bus = new Vue() // 接受数据 bus.$on('my-event', (val) => { // val 即为接收的数据 1000 }) // 传递数据 bus.$emit('my-event', 1000)
方法一: 从父元素prop一层层的传递
方法二: 在main.js中利用vue.prototype放到vue原型上全局使用 (适合每一个页面都需要的数据)
方法三:
在父组件中用 provide(){return{}}
传递 (如果要传递变量就写成函数的形式)
在子组件中用 inject:[ ]
接收数据
provide 和 inject
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,
不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
provide 和 inject 绑定并不是可响应的。这是刻意为之的。
然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
{ data () { return { msg: 'hello world'} }, provide: { // ✅ str: 'hi' } // provide: {// ❌ // str: this.msg // } provide () { // ✅ return { str: this.msg } } } {inject: ['str']}
子组件直接通过 $parent 可以获取到父组件的实例
父组件调用子组件时添加 ref 属性, 通过 this.$refs 获取到子组件的实例
//vuex的五个核心概念: state: {}, 数据源(保存全局数据) getters: {}, 计算属性(将store中的数据加工后输出) actions: {}, 异步方法(异步操作必需放到Action中,但只能通过触发同步方法更新数据) mutations: {}, 同步方法(保存更改数据的回调函数,不能包含异步操作) modules: { 模块(让代码更好维护,数据分类更明确) moduleA: { namespaced: true, //开启命名空间 state: {}, actions: {}, mutations: {}, }, moduleB: { namespaced: true, state: {}, actions: {}, mutations: { [常量](){} //可以使用常量代替Mutation事件类型 }, } }
# 数据在页面中的使用和更新 --- ## 访问state中数据 1. this.$store.state.全局数据名称 (在页面中直接使用/计算属性) 2. mapState映射为计算属性 : 通过导入的`mapState函数`将当前组件需要的全局数据, 映射为当前组件的computed计算属性,在页面中直接使用 computed:{ ...mapState(['count']) //利用展开运算符将全局数据映射为当前组件的计算属性 } ## 访问getters计算属性 1. $store.getters.名称 访问 2. mapGetters 映射为计算属性 computed:{ ...mapGetters(['showNum']) } ## 调用Mutation中的方法 1. this.$store.commit() 触发 mutations 2. MapMutations 映射为方法 ## 调用Action中的方法 1. this.$store.dispatch 触发 Actions 2. mapActions 映射为方法 computed: { ...mapState(), ...mapGetters() }, methods: { ...mapActions({ fn: 'user/fn' }) ...mapMutations({ change: 'user/change'}) }
父组件给子组件传值的地方,添加自定义的属性,属性的值就是需要传递给子组件的值,如果属性的值为变量,boolean类型,number类型,对象,数组,null,undefined,函数,需要使用 {} 包裹
<my-com num={1000} fn={fn}></mycom>
在子组件中通过 this.props 或者 props获取父组件传递的数据
可以使用 prop-types 校验数据
子组件给父组件传值实际上还是 父组件给子组件传值,只不过父传给子的是一个 函数,该函数由父组件定义(实现),该函数的默认值即为子组件需要传给父组件的值
const getData = (val) => { console.log(val) // 子传给父的 } <my-com fn={getData}></mycom>
在子组件的某一个事件内部,通过 this.props.fn(参数) 或者 props.fn(参数) 完成传值
react没有
1) 创建Context容器对象: const XxxContext = React.createContext() 2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据: <xxxContext.Provider value={数据}> 子组件 </xxxContext.Provider> 3) 后代组件读取数据: //第一种方式:仅适用于类组件 static contextType = xxxContext // 声明接收context this.context // 读取context中的value数据 //第二种方式: 函数组件与类组件都可以 <xxxContext.Consumer> { value => ( // value就是context中的value数据 要显示的内容 ) } </xxxContext.Consumer>
// 类组件 contextType class Com extends Component { static contextType = ColorContext render () { return ( <div>{ this.context }</div> ) } } //类组件 Context.Consumer class Com extends Component { render () { return ( <div> <ColorContext.Consumer> { (val) => { return <div>{val}</div> } } </ColorContext.Consumer> </div> ) } } // 函数式组件 Context.Consumer const App = () => { return ( <div> <ColorContext.Consumer> { (val) => { return <div>{val}</div> } } </ColorContext.Consumer> </div> ) } // 函数式组件 useContext const App = () => { // 推荐 const color = useContext(ColorContext) return ( <div>{color}</div> ) } // 如果使用开发者工具,发现多Context传值,指示不明确,使用 dispalyName const ColorContext = React.createContext() ColorContext.displayName = 'ColorContext'
如果子组件时类组件,可以在调用子组件时,添加ref属性,从而获取子组件的实例
如果时函数式组件,添加的ref 获取不到子组件的实例,因为函数没有实例, 解决方案:
useImperativeHandle(ref, createHandle, [deps])
ref
时自定义暴露给父组件的实例值forwardRef
一起使用——————————————————————————————————子组件——————————————————————— import React, { useRef, forwardRef, useImperativeHandle } from 'react' function Child4(props, ref) { let userageInput = useRef() useImperativeHandle(ref, () => ({ getUserage: () => { return userageInput.current.value } })) return ( <div>age: <input type="text" ref={userageInput}></input> </div> ) } export default forwardRef(Child4) //如果不使用forwardRef: Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()? ————————————————————————————父组件———————————————————————————— import React, { useRef } from 'react' import Child4 from '../component/Child4' export default function UseRef() { let usernameInput = useRef() let child4 = useRef() //给组件Child4声明一个唯一的名字 return ( <div>UseRef <div>name <input type="text" ref={usernameInput}></input> <button onClick={() => { console.log("name:", usernameInput.current.value); }}>取到input中的值</button> </div> <Child4 ref={child4}></Child4> <button onClick={() => { console.log("age:", child4.current.getUserage()); }}>取到子组件中input中的值</button> </div> ) }
多个视图依赖于同一个状态, 来自不同视图的行为需要变更同一个状态
react的状态管理器由多种, 都有各自的特性, react项目中常用的状态管理模式有
redux ---> 属于js的状态管理模式
redux + react-redux
组件 --分为--> (容器组件 + 展示组件/UI组件)
核心: connect(mapStateToProps, mapDispatchToProps)(Com)
redux + react-redux + redux-thunk ✅✅
connect(mapStateToProps, mapDispatchToProps)(Com)
actionCreator 传参
const aciton = { getList (dispatch) { getProList().then(res => { dispatch(action) }) }, getParamsList (params) { return (dispatch) => { getProList(params).then(res => { dispatch(action) }) } } }mapDispatchToProps
import aciton from '****' (dispatch) => { return { getListData () { dispatch(action.getList) }, getParamsListData () { dispatch(action.getParamsList({ count: 1})) } } }
Redux + react-redux + redux-saga ✅
核心点:
import { call, put, takeLatest } from "redux-saga/effects"; import { getProList } from '../api/pro' //导入请求getProList的api import * as types from './../store/type' //saga是在generator中处理异步操作 function * getProListAction (params: any): any { const res = yield call(getProList, params.payload) //call请求数据 yield put({ //更新数据 type: types.CHANGE_PRO_LIST, payload: res.data.data }) } function * mySaga () { // 当用户一旦选中 key 值,自动执行value的函数 // 此处触发上面的generator函数 (而组件中需要去触发types.REQUEST_PRO_LIST) yield takeLatest(types.REQUEST_PRO_LIST, getProListAction) } export default mySagaimport { createStore, applyMiddleware } from 'redux' import { combineReducers } from 'redux-immutable' import createSagaMiddleware from 'redux-saga' // 引入saga import { menu, pro } from './reducers' import mySaga from './mySaga' const reducer = combineReducers({ menu, pro }) const middleware = createSagaMiddleware() //生成saga中间件 const store = createStore(reducer, applyMiddleware(middleware)) middleware.run(mySaga) // 使saga中的异步操作生效 export default store
Redux + redux-thunk
Redux + redux-saga
mobx + mobx-react ✅✅
dva.js
Umi.js ✅✅