前面的话

  Redux是Flux思想的另一种实现方式。Flux是和React同时面世的。React用来替代jQuery,Flux用来替换Backbone.js等MVC框架。在MVC的世界里,React相当于V(view)的部分,只涉及页面的渲染。一旦涉及应用的数据管理部分,还是交给Model和Controller。不过,Flux并不是一个MVC框架,它用一种新的思路来管理数据。本文将详细介绍Redux的内容

MVC

  MVC是业界广泛接受的一种前端应用框架类型,这种框架把应用分为三个部分:

  Model(模型)负责管理数据,大部分业务逻辑应该放在Model中

  View(视图)负责渲染用户页面,应该避免在View中涉及业务逻辑

  Controller(控制器)负责接受用户输入,根据用户输入调用相应的Model部分逻辑,把产生的数据结果交给View部分,让View渲染出必要的输出

  MVC框架提出的数据流很理想,用户请求先到达Controller,由Controller调用Model获得数据,然后把数据交给View。但是,在实际框架实现中,总是允许View和Model直接通信

  然而,在MVC中让View和Model直接对话就是灾难

Flux

  Facebook用Flux框架来替代原有的MVC框架,这种框架包含四个部分:

  Dispatcher负责动作分发,维持Store之间的依赖关系

  Store负责存储数据和处理数据相关逻辑

  Action驱动Dispatcher的javascript对象

  View视图负责显示用户界面

  如果非要把Flux和MVC做一个对比。那么,Flux的Dispatcher相当于MVC的Controller,Flux的store相当于MVC的model,Flux的View对应于MVC的View,Action对应给MVC框架的用户请求

  1、Dispatcher

import {Dispatcher} from 'flux';
export default new Dispatcher();

  2、Action

  定义Action通常需要两个文件,一个定义action的类型,一个定义action的构造函数。分成两个文件的原因是在Store中会根据action类型做不同操作,也就有单独导入action类型的需要

export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';
import * as ActionTypes from './ActionTypes.js';
import AppDispatcher from './AppDispatcher.js'; export const increment = (counterCaption) => {
AppDispatcher.dispatch({
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
});
}; export const decrement = (counterCaption) => {
AppDispatcher.dispatch({
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
});
};

  3、Store

  一个Store也是一个对象,这个对象用来存储应用状态,同时还要接受Dispatcher派发的动作,根据动作来决定是否要更新应用状态

  一个EventEmitter实例对象支持下列相关函数

emit函数:可以广播一个特定事件,第一个参数是字符串类型的事件名称
on函数:可以增加一个挂在这个EventEmitter对象特定事件上的处理函数,第一个参数是字符串类型的事件名称,第二个参数是处理函数
removeListener函数: 和on函数做的事情相反,删除挂在这个EventEmitter对象特定事件上的处理函数,和on函数一样,第一个参数是事件名称,第二个参数是处理函数

  [注意]如果要调用removeListener函数,就一定要保留对处理函数的引用

import AppDispatcher from '../AppDispatcher.js';
import * as ActionTypes from '../ActionTypes.js';
import {EventEmitter} from 'events'; const CHANGE_EVENT = 'changed'; const counterValues = {
'First': ,
'Second': ,
'Third':
}; const CounterStore = Object.assign({}, EventEmitter.prototype, {
getCounterValues: function() {
return counterValues;
}, emitChange: function() {
this.emit(CHANGE_EVENT);
}, addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
}, removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
} }); CounterStore.dispatchToken = AppDispatcher.register((action) => {
if (action.type === ActionTypes.INCREMENT) {
counterValues[action.counterCaption] ++;
CounterStore.emitChange();
} else if (action.type === ActionTypes.DECREMENT) {
counterValues[action.counterCaption] --;
CounterStore.emitChange();
}
}); export default CounterStore;

  4、View

  存在于Flux框架中的React组件需要实现以下几个功能

  (1)创建时读取Store上状态来初始化组件内部状态

  (2)当Store上状态发生变化时,组件要立刻同步更新内部状态保持一致

  (3)View如果要改变Store状态,必须而且只能派发action

// 父组件
class ControlPanel extends Component {
render() {
return (
<div style={style}>
<Counter caption="First" />
<Counter caption="Second" />
<Counter caption="Third" />
<hr/>
<Summary />
</div>
);
}
}
export default ControlPanel;
// 子组件
class Counter extends Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
this.onClickDecrementButton = this.onClickDecrementButton.bind(this);
this.state = {count: CounterStore.getCounterValues()[props.caption]}
}
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.caption !== this.props.caption) || (nextState.count !== this.state.count);
}
componentDidMount() {
CounterStore.addChangeListener(this.onChange);
}
componentWillUnmount() {
CounterStore.removeChangeListener(this.onChange);
}
onChange() {
const newCount = CounterStore.getCounterValues()[this.props.caption];
this.setState({count: newCount});
}
onClickIncrementButton() {
Actions.increment(this.props.caption);
}
onClickDecrementButton() {
Actions.decrement(this.props.caption);
}
render() {
const {caption} = this.props;
return (
<div>
<button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
<button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
<span>{caption} count: {this.state.count}</span>
</div>
);
}
}
Counter.propTypes = {
caption: PropTypes.string.isRequired
};
export default Counter;

【优势】

  在Flux中,Store只有get方法,没有set方法,根本不可能直接去修改其内部状态,View只能通过get方法获取Store的状态,无法直接去修改状态,如果View想要修改Store的状态,只能派发一个action对象给Dispatcher

【不足】

  1、Store之间依赖关系

  在Flux的体系中,如果两个Store之间有逻辑依赖关系,就必须用上Dispatcher的waitFor函数

  2、难以进行服务器端渲染

  3、Store混杂了逻辑和状态

Redux

  Redux的含义是Reducer+Flux。Reducer是一个计算机科学中的通用概念。以Javascript为例,数组类型有reduce函数,接受的参数是一个reducer,reducer做的事情就是把数组所有元素依次做规约,对每个元素都调用一次参数reducer,通过reducer函数完成规约所有元素的功能

  Flux的基本原则是单向数据流,Redux在此基础上强调三个基本原则:

  1、唯一数据源

  2、保持状态只读

  3、数据改变只通过纯函数完成

//actionTypes
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';
//actions
import * as ActionTypes from './ActionTypes.js'; export const increment = (counterCaption) => {
return {
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
};
}; export const decrement = (counterCaption) => {
return {
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
};
};
//store
import {createStore} from 'redux';
import reducer from './Reducer.js'; const initValues = {
'First': ,
'Second': ,
'Third':
}; const store = createStore(reducer, initValues); export default store;
//reducer
import * as ActionTypes from './ActionTypes.js'; export default (state, action) => {
const {counterCaption} = action; switch (action.type) {
case ActionTypes.INCREMENT:
return {...state, [counterCaption]: state[counterCaption] + };
case ActionTypes.DECREMENT:
return {...state, [counterCaption]: state[counterCaption] - };
default:
return state
}
}
// 父组件
class ControlPanel extends Component {
render() {
return (
<div style={style}>
<Counter caption="First" />
<Counter caption="Second" />
<Counter caption="Third" />
<hr/>
<Summary />
</div>
);
}
}
// 子组件
class Counter extends Component {
constructor(props) {
super(props);
this.onIncrement = this.onIncrement.bind(this);
this.onDecrement = this.onDecrement.bind(this);
this.onChange = this.onChange.bind(this);
this.getOwnState = this.getOwnState.bind(this);
this.state = this.getOwnState();
}
getOwnState() {
return {value: store.getState()[this.props.caption]};
}
onIncrement() {
store.dispatch(Actions.increment(this.props.caption));
}
onDecrement() {
store.dispatch(Actions.decrement(this.props.caption));
}
onChange() {
this.setState(this.getOwnState());
}
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.caption !== this.props.caption) ||
(nextState.value !== this.state.value);
}
componentDidMount() {
store.subscribe(this.onChange);
}
componentWillUnmount() {
store.unsubscribe(this.onChange);
}
render() {
const value = this.state.value;
const {caption} = this.props;
return (
<div>
<button style={buttonStyle} onClick={this.onIncrement}>+</button>
<button style={buttonStyle} onClick={this.onDecrement}>-</button>
<span>{caption} count: {value}</span>
</div>
);
}
}
Counter.propTypes = {
caption: PropTypes.string.isRequired
};

容器和展示

  一个React组件基本上要完成以下两个功能:

  1、读取Store的状态,用于初始化组件的状态,同时还要监听Store的状态改变;当Store状态发生变化时,需要更新组件状态,从而驱动组件重新渲染;当需要更新Store状态时,就要派发action对象

  2、根据当前props和state,渲染出用户界面

  让一个组件只专注做一件事。于是,按照这两个功能拆分成两个组件。这两个组件是父子组件的关系。业界对于这样的拆分有多种叫法,承担第一个任务的组件,也就是负责和redux打交道的组件,处于外层,被称为容器组件;只专业负责渲染界面的组件,处于内层,叫做展示组件

  展示组件,又称为傻瓜组件,就是一个纯函数,根据props产生结果。实际上,让展示组件无状态,只根据props来渲染结果,是拆分的主要目的之一。状态全部交给容器组件去处理

function Counter (props){
const {caption, onIncrement, onDecrement, value} = this.props;
return (
<div>
<button style={buttonStyle} onClick={onIncrement}>+</button>
<button style={buttonStyle} onClick={onDecrement}>-</button>
<span>{caption} count: {value}</span>
</div>
);
}
}

  或者,直接使用解构赋值的方法

function Counter ({caption, onIncrement, onDecrement, value} ){
return (
<div>
<button style={buttonStyle} onClick={onIncrement}>+</button>
<button style={buttonStyle} onClick={onDecrement}>-</button>
<span>{caption} count: {value}</span>
</div>
);
}
}

React-redux

  react-redux遵循将组件分成展示组件和容器组件的规范。react-redux提供了两个功能:

  1、Provider组件,可以让容器组件默认可以取得state,而不用当容器组件层级很深时,一级级将state传下去

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'; import ControlPanel from './views/ControlPanel';
import store from './Store.js'; import './index.css'; ReactDOM.render(
<Provider store={store}>
<ControlPanel/>
</Provider>,
document.getElementById('root')
);

  2、connect方法,用于从展示组件生成容器组件。connect的意思就是将这两种组件连接起来

import React, { PropTypes } from 'react';
import * as Actions from '../Actions.js';
import {connect} from 'react-redux'; const buttonStyle = {
margin: '10px'
}; function Counter({caption, onIncrement, onDecrement, value}) {
return (
<div>
<button style={buttonStyle} onClick={onIncrement}>+</button>
<button style={buttonStyle} onClick={onDecrement}>-</button>
<span>{caption} count: {value}</span>
</div>
);
} Counter.propTypes = {
caption: PropTypes.string.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired,
value: PropTypes.number.isRequired
}; function mapStateToProps(state, ownProps) {
return {
value: state[ownProps.caption]
}
} function mapDispatchToProps(dispatch, ownProps) {
return {
onIncrement: () => {
dispatch(Actions.increment(ownProps.caption));
},
onDecrement: () => {
dispatch(Actions.decrement(ownProps.caption));
}
}
} export default connect(mapStateToProps, mapDispatchToProps)(Counter);

  关于mapDispatchToProps函数的简化过程如下

  初始代码如下

const mapDispatchToProps = (dispatch, ownProps) => {
return {
fetchUsers: () => dispatch(fetchUsers()),
fetchCategories: () => dispatch(fetchCategories()),
fetchPosts: () => dispatch(fetchPosts())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home)

  再次简化如下

const mapDispatchToProps = {
fetchUsers: () => fetchUsers(),
fetchCategories: () => fetchCategories(),
fetchPosts: () => fetchPosts()
}
export default connect(mapStateToProps, mapDispatchToProps)(Home)

  最终优化如下

export default connect(mapStateToProps, { fetchUsers, fetchCategories, fetchPosts })(Home)

模块化应用

  从架构出发,开始一个新应用时,有几件事情是一定要考虑清楚的:

  1、代码文件的组织结构

  2、确定模块的边界

  3、Store的状态树设计

【代码文件的组织结构】

  Redux应用适合于按功能组织,也就是把完成同一应用功能的代码放在一个目录下,一个应用功能包含多个角色的代码。在Redux中,不同的角色就是reducer、actions和视图。而应用功能对应的就是用户界面上的交互模块

  以Todo应用为例,这个应用的两个基本功能就是TodoList和Filter,所以代码可以这样组织:

todoList/
actions.js
actionTypes.js
index.js
reduce.js
views/
component.js
container.js
filter/
actions.js
actionTypes.js
index.js
reduce.js
views/
component.js
container.js

【模块接口】

  不同功能模块之间的依赖关系应该简单而清晰,也就是所谓的保持模块之间低耦合性;一个模块应该把自己的功能封装得很好,让外界不要太依赖于自己内部的结构,这样不会因为内部的变化而影响外部模块的功能,这就是所谓的高内聚性

【状态树的设计】

  状态树的设计需要遵循如下几个原则:

  1、一个模块控制一个状态节点

  2、避免冗余数据

  3、树形结构扁平

  对于Todo应用的状态树设计如下

{
todos: [
{
text: 'first todo',
completed: false,
id:
},
{
text: 'second todo',
completed: true,
id:
},
],
// 'all'、'completed'、'uncompleted'
filter: 'all'
}

reselect

  reselect库的原理是只要相关状态没有改变,那就直接使用上一次的缓存结果。reselect用来创造选择器,接收一个state作为参数的函数,返回的数据是某个mapStateToProps需要的结果

  首先,安装reselect库

npm install --save reselect

  reselect提供了创造选择器的createSelector函数,这是一个高阶函数,也就是接受函数为参数来产生一个新函数的函数

  createSelector 接收一个 input-selectors 数组和一个转换函数作为参数。如果 state tree 的改变会引起 input-selector 值变化,那么 selector 会调用转换函数,传入 input-selectors 作为参数,并返回结果。如果 input-selectors 的值和前一次的一样,它将会直接返回前一次计算的数据,而不会再调用一次转换函数。

import { createSelector } from 'reselect'

const getVisibilityFilter = (state) => state.visibilityFilter
const getTodos = (state) => state.todos export const getVisibleTodos = createSelector(
[ getVisibilityFilter, getTodos ],
(visibilityFilter, todos) => {
switch (visibilityFilter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
)

  在上例中,getVisibilityFilter 和 getTodos 是 input-selector。因为他们并不转换数据,所以被创建成普通的非记忆的 selector 函数。但是,getVisibleTodos 是一个可记忆的 selector。他接收 getVisibilityFilter 和 getTodos 为 input-selector,还有一个转换函数来计算过滤的 todos 列表

  reselect的典型应用如下所示

// selector
export const getCategories = state => {
return state.category
}
export const getCategoriesSortByNumber = createSelector(getCategories, categories =>
categories.sort((v1, v2) => {
return v1.number - v2.number
})
)
export const getCategoryDatas = createSelector(getCategoriesSortByNumber, categoriesSortByNumber =>
categoriesSortByNumber.map(t => {
return $_setChildren(categoriesSortByNumber, t)
}).map(t => {
return Object.assign(t, {
index: $_getIndex(t.number),
des: t.children.length ? t.children.length : '',
title: t.name,
key: t.number,
className: 'styled-categorylist',
url: t.children.length ? `/category/${t.number}` : '',
parentUrl: `/category/${$_getParentNumber(t)}`,
nextChildNumber: $_getFirstChildNumber(t)
})
})
)
export const getCategoryDatasByNumber = createSelector(getCategoryDatas, categoryDatas =>
categoryDatas.reduce((obj, t) => {
obj[t.number] = t
return obj
}, {})
)
export const getCategoryRootDatas = createSelector(getCategoryDatas, categoryDatas =>
categoryDatas.filter(t => {
return Number(String(t.number).slice()) ===
})
)
export const getCategoryDatasById = createSelector(getCategoryDatas, categoryDatas =>
categoryDatas.reduce((obj, t) => {
obj[t._id] = t
return obj
}, {})
)

常见错误

  在使用redux的过程中,会出现如下的常见错误

【错误:reducers不能触发actions】

Uncaught Error: Reducers may not dispatch actions.

  一般来说,出现"Reducedrs may not dispatch actions"的错误,是因为reducer中出现路由跳转语句,而跳转到的语句正好发送了dispatch。从而,reducer不再是纯函数

  错误代码如下所示:

export const logIn = admin => ({type: LOGIN, admin})

// reducer
const login = (state = initialState, action) => {
switch (action.type) {
case LOGIN:
let { token, user } = action.admin
// 将用户信息保存到sessionStorage中
sessionStorage.setItem('token', token)
sessionStorage.setItem('user', JSON.stringify(user))
// 跳转到首页
history.push('/')
return { token, user }
...

  有两种解决办法

  1、给路由跳转语句设置延迟定时器,从而避免在当前reducer还没有返回值的情况下,又发送新的dispatch

export const logIn = admin => ({type: LOGIN, admin})

// reducer
const login = (state = initialState, action) => {
switch (action.type) {
case LOGIN:
let { token, user } = action.admin
// 将用户信息保存到sessionStorage中
sessionStorage.setItem('token', token)
sessionStorage.setItem('user', JSON.stringify(user))
// 跳转到首页
setTimeout(() => {
history.push('/')
},)
return { token, user }
...

  2、将reducer中的逻辑放到dispatch中

export const logIn = (admin) => {
let { token, user } = admin
// 将用户信息保存到sessionStorage中
sessionStorage.setItem('token', token)
sessionStorage.setItem('user', JSON.stringify(user))
// 跳转到首页
history.push('/')
return {type: LOGIN, admin}
} // reducer
const login = (state = initialState, action) => {
switch (action.type) {
case LOGIN:
let { token, user } = action.admin
return { token, user }
...

【action函数中无法执行return后的语句】

  例如,在下面代码中,控制台只能输入'111',而不能输出'222'

export const updatePost = payload => {
console.log('')
return dispatch => {
console.log('')
fetchModule({
dispatch,
url: `${BASE_POST_URL}/${payload._id}`,
method: 'put',
data: payload,
headers: { Authorization: sessionStorage.getItem('token') },
success(result) {
console.log(result)
dispatch({ type: UPDATE_POST, doc: result.doc })
}
})
}
}

  出现这个问题的原因非常简单,是因为没有使用this.props.updatePost,而直接使用了updatePost方法导致的

  加入如下语句既可解决

let { updatePost } = this.props

【redux中的state发生变化,但页面没有重新渲染】

  一般地,是因为展开运算符使用不当所至

  而对于对象的展开运算符,则需要把...state放到第一个条目位置,因为后面的条目会覆盖展开的部分

return {...item, completed: !item.completed}

【reducer中不能使用undefined】

  1、reducer中state不能返回undefined,可以用null代替

// reducer
const filter = (state = null, action) => {
switch (action.type) {
case SHOW_FILTER:
return action.filter
default:
return state
}
}

  2、同样地,action.filter表示空值,不能为undefined,用null代替

export const setFilter = filter => ({type: SHOW_FILTER, filter})
this.props.setFilter(null)

深入理解Redux的更多相关文章

  1. 轻松理解Redux原理及工作流程

    轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...

  2. 理解Redux以及如何在项目中的使用

    今天我们来聊聊Redux,这篇文章是一个进阶的文章,建议大家先对redux的基础有一定的了解,在这里给大家推荐一下阮一峰老师的文章: http://www.ruanyifeng.com/blog/20 ...

  3. 理解 Redux 中间件机制

    Redux 的 action 是一个 JS 对象,它表明了如何对 store 进行修改.但是 Redux 的中间件机制使action creator 不光可以返回 action 对象,也可以返回 ac ...

  4. 如何理解 Redux?

    作者:Wang Namelos 链接:https://www.zhihu.com/question/41312576/answer/90782136 来源:知乎 著作权归作者所有,转载请联系作者获得授 ...

  5. 理解 React,但不理解 Redux,该如何通俗易懂的理解 Redux?

    作者:Wang Namelos链接:https://www.zhihu.com/question/41312576/answer/90782136来源:知乎著作权归作者所有.商业转载请联系作者获得授权 ...

  6. 理解 React,但不理解 Redux,该如何通俗易懂的理解 Redux?(转)

    作者:Wang Namelos 链接:https://www.zhihu.com/question/41312576/answer/90782136来源:知乎 解答这个问题并不困难:唯一的要求是你熟悉 ...

  7. 【React】360- 完全理解 redux(从零实现一个 redux)

    点击上方"前端自习课"关注,学习起来~ 前言 记得开始接触 react 技术栈的时候,最难理解的地方就是 redux.全是新名词:reducer.store.dispatch.mi ...

  8. 通俗易懂的理解 Redux(知乎)

    1. React有props和state: props意味着父级分发下来的属性[父组件的state传递给子组件  子组件使用props获取],state意味着组件内部可以自行管理的状态,并且整个Rea ...

  9. 十分钟理解Redux核心思想,过目不忘。

    白话Redux工作原理.浅显易懂. 如有纰漏或疑问,欢迎交流. Redux 约法三章 唯一数据源(state) 虽然redux中的state与react没有联系,但可以简单理解为react组件中的th ...

  10. 3.3 理解 Redux 中间件(转)

    这一小节会讲解 redux 中间件的原理,为下一节讲解 redux 异步 action 做铺垫,主要内容为: Redux 中间件是什么 使用 Redux 中间件 logger 中间件结构分析 appl ...

随机推荐

  1. Tomcat下wtpwebapps文件夹 和 webapps文件夹区别

    这两者其实没有区别.都是项目部署路径 webapps : tomcat默认部署路径 wtpwebapps : eclipse默认部署路径 只不过Tomcat6将wtpwebapps作为了默认路径,如果 ...

  2. Know your weapons Ⅱ

    本次内容主要讲述使用UWP相关技术可以实现的软件上的一些功能,这里以Netease-Cloud Music(下称Cloud Music)为例讲述,这款音乐软件我个人一直在用,毕竟人们生活离不开音乐,说 ...

  3. mysql-proxy实现读写分离

    其中Amoeba for MySQL也是实现读写分离 环境描述:操作系统:CentOS6.5 32位主服务器Master:192.168.179.146从服务器Slave:192.168.179.14 ...

  4. CentOS7搭建solr7.2

    solr介绍 一.Solr它是一种开放源码的.基于 Lucene Java 的搜索服务器,易于加入到 Web 应用程序中. 二.Solr 提供了层面搜索(就是统计).命中醒目显示并且支持多种输出格式( ...

  5. MIPCMS V3.1.0 远程写入配置文件Getshell过程分析(附批量getshell脚本)

      作者:i春秋作家--F0rmat 0×01 前言 今天翻了下CNVD,看到了一个MIPCMS的远程代码执行漏洞,然后就去官网下载了这个版本的源码研究了下.看下整体的结构,用的是thinkPHP的架 ...

  6. Linux下的进程与线程(二)—— 信号

    Linux进程之间的通信: 本文主要讨论信号问题. 在Linux下的进程与线程(一)中提到,调度器可以用中断的方式调度进程. 然而,进程是怎么知道自己需要被调度了呢?是内核通过向进程发送信号,进程才得 ...

  7. Python中的PYTHONPATH环境变量

    PYTHONPATH是Python中一个重要的环境变量,用于在导入模块的时候搜索路径.可以通过如下方式访问: >>> import sys >>> sys.path ...

  8. c语言程序设计第3周编程作业(数字特征)

    题目内容: 对数字求特征值是常用的编码算法,奇偶特征是一种简单的特征值.对于一个整数,从个位开始对每一位数字编号,个位是1号,十位是2号,以此类推.这个整数在第n位上的数字记作x,如果x和n的奇偶性相 ...

  9. 2018上C语言程序设计(高级)作业- 第1次作业

    未来两周学习内容 复习指针的定义和引用 指针的应用场景: 指针作为函数参数(角色互换) 指针作为函数的参数返回多个值 指针.数组和地址间的关系 使用指针进行数组操作 数组名(指针)作为函数参数(冒泡排 ...

  10. alpha冲刺第十天

    一.合照 二.项目燃尽图 三.项目进展 之前卡住的注册无法插入数据的问题解决 可以呈现多条数据内容了,首页文章内容呈现,问答界面问题内容呈现 四.明日规划 很多没有完善的,后面还是要继续整改 五.问题 ...