Weekly sharing 05 (20190408)
One
1、不要太乖,不想做的事可以拒绝,做不到的事不用勉强,不喜欢的话假装没听见。人生不是用来讨好别人的,而是要善待自己。永远都要活给自己看,而且笑容要特别灿烂,别在乎别人的指指点点,做好你自己。
知新
1、在vue内对v-html
动态生成的内容设置样式
v-html绑定渲染出的内容可以理解为是子组件的内容,所以当你页面css设置scoped属性,会导致css仅对当前组件生效,子组件不会被加上对应的属性,此时无需设置scoped属性。
温故
Redux(from 阮一峰老师的教程)
1、适用场景
UI层非常简单,没有很多互动,Redux 就是不必要的,本身redux就是为了增加组件通信,优化代码结构。
需要使用:多交互,多数据源
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
2、设计思想
(1)Web 应用是一个状态机,视图与状态是一一对应的。
(2)所有的状态,保存在一个对象里面。
3、基本概念和API
1)store
保存数据的地方,即数据容器,一个应用只能有一个store。
使用createStore
函数生成 Store:
import { createStore, compose, applyMiddleware } from 'redux'
const store = createStore();
export default store;
2)state
想得到某个时点的数据,就要对 Store 生成快照,这种时点的数据集合,就叫做 State。
获得数据:store.getState();
一个 State 对应一个 View。只要 State 相同,View 就相同。
3)action
Action 就是 View 发出的通知,表示 State 应该要发生变化了。
Action 是一个对象,其中的type属性是必须的,表示 Action 的名称,其他属性可以自由设置。
4)actionCreator
统一管理action,将需要使用到的action全部写在一个文件,在需要使用的页面进行引入;
5)store.dispatch(派发)
是 View 发出 Action 的唯一方法,与vuex类似,接受一个 Action 对象作为参数,将它发送出去。
handleInputBlur() {
dispatch(actionCreators.inputToggle(false));
},
6)reducer
Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
且必须要是纯函数(给定固定输入,一定有固定输出,且不会有任何副作用,输入和输出一一映射,不会对其他数据产生影响)。
纯函数:
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用
Date.now()
或者Math.random()
等不纯的方法,因为每次会得到不一样的结果
在此情况下需要每次的state都能得到相应的结果,因此Reducer 函数里面不能改变 State,必须返回一个全新的对象。
const defaultState = fromJS({
inputFocus: false,
searchList: [],
page: 1,
totalPage: 1,
mouseStatus: false
});
export default (state = defaultState, action) => {
switch (action.type) {
case actionTypes.INPUT_FOCUS:
if (action.inputFocus && !state.get('searchList').length && action.searchList) {
//第一次获得焦点获取数据
return state.merge({
searchList: action.searchList,
totalPage: action.totalPage,
inputFocus: action.inputFocus
});
}
else return state.set('inputFocus', action.inputFocus);
case actionTypes.MOUSE_TOGGLE:
return state.set('mouseStatus', action.mouseStatus);
case actionTypes.CHANGE_PAGE:
let _page = state.get('page'), _total = state.get('totalPage');
if(_page < _total) _page++;
else _page = 1;
return state.set('page', _page);
}
return state;
}
store.dispatch方法会触发 Reducer 的自动执行,所以在生成 Store 的时候,需要将 Reducer 传入createStore方法。
import { createStore, compose, applyMiddleware } from 'redux'
import reducer from './reducer'
const store = createStore(reducer);
export default store;
7)store.subscribe()
在页面内对store的变化进行监听:
//对store进行订阅
store.subscribe(this.handleStoreChange);
4、store的实现
三个方法:
- store.getState()
- store.dispatch()
- store.subscribe()
createStore
方法还可以接受第二个参数,表示 State 的最初状态。
想要在chrome使用redux查看工具,需要在创建store时传入参数window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
5、reducer的拆分
reducer负责state的生成,在项目足够庞大的情况下,reducer也将会越来越大,此时可将reducer根据模块或者页面进行拆分,不同的函数负责处理不同属性,最终把它们合并成一个大的 Reducer 即可。
Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。
import { combineReducers } from 'redux';//此时生成的数据都是immutable格式
import { reducer as headerReducer } from '../components/header/store';
import { reducer as HomeReducer } from '../views/home/store';
const reducer = combineReducers({
header: headerReducer,
home: HomeReducer
});
export default reducer;
该函数根据state的key去执行相应的子reducer。
inputFocus: state.getIn(['header', 'inputFocus']),
searchList: state.getIn(['header', 'searchList']),
page: state.getIn(['header', 'page']),
mouseStatus: state.getIn(['header', 'mouseStatus'])
6、工作流程
[图片上传失败...(image-f408ff-1555312083548)]
用户触发action
--> Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action然后返回新的 State --> state发生变化之后,触发监听函数,重新渲染视图。
createStore - store.dispatch(派发) - store.getState - store.subscribe(订阅);
7、异步操作
希望reducer能在异步操作之后自动执行,则需要中间件(middleware
)。
action和store之间,相当于对dispatch方法的封装升级,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能,dispatch的参数可以是函数,如果参数是对象则直接传给store,如果参数是函数则操作函数,如果有需要对store进行操作再进行操作。
1)中间件的用法
import { createStore, compose, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
const store = createStore(reducer, applyMiddleware(thunk));
export default store;
中间件次序:下面applyMiddleware
方法的三个参数,就是三个中间件。有的中间件有次序要求,使用前要查一下文档。比如,logger就一定要放在最后,否则输出结果会不正确。
const store = createStore(
reducer,
applyMiddleware(thunk, promise, logger)
);
2)applyMiddlewares()
作用是将所有中间件组成一个数组,依次执行。它的源码:
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer);
var dispatch = store.dispatch;
var chain = [];
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
};
chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
return {...store, dispatch}
}
}
所有中间件被放进了一个数组chain
,然后嵌套执行,最后执行store.dispatch
。可以看到,中间件内部(middlewareAPI
)可以拿到getState
和dispatch
这两个方法。