基于Redux架构的单页应用开发总结(二)
来源:互联网 发布:相册软件哪个好 编辑:程序博客网 时间:2024/06/06 02:20
写在前面
这次重点介绍基于Redux架构的单页应用代码的组织方式
关于less的组织
作为一个后端出身的前端工程师,写简单的css实在没有那种代码可配置和结构化的快感。所以引入less是个不错的选择,无论是针对代码后期的管理,还是提高代码的复用能力。
global.less
这个是全局都可以调用的方法库,我习惯把 项目的配色、各种字号、用于引入混出的方法等写在这里,其他container
页面通过@import
方式引入它,就可以使用里面的东西。不过定义它时要注意以下两点:
- 第一,这个less里只能存放变量和方法,less编译时会忽略它们,只在调用它们的地方才编译成css。所以为了防止代码重复,请不要在这里直接定义样式,而是用一个方法把它们包起来,表示一个用途。
- 第二,这个less里的方法如果是针对某些具体标签定义样式的,只能初始化一次,建议在单页的入口
container
里做,这样好维护。比如reset()
(页面标签样式初始化),这个方法放在入口container
的login.less
里调用且全局只调用一次。
下面是我的global.less
常用的一些模块
/** * @desc 一些全局的less * @createDate 2016-05-16 * @author Jafeney <692270687@qq.com> **/// 全局配色@g-color-active: #ff634d; //活跃状态的背景色(橘红色)@g-color-info: #53b2ea; //一般用途的背景色(浅蓝色)@g-color-primary: #459df5; //主要用途的背景色 (深蓝色)@g-color-warning: #f7cec8; //用于提示的背景色 (橘红色较浅)@g-color-success: #98cf07; //成功状态的背景色 (绿色)@g-color-fail: #c21f16; //失败状态的背景色 (红色)@g-color-danger: #ff634d; //用于警示的背景色 (橘红色)@g-color-light: #fde2e1; //高饱合度淡色的背景色(橘红)// 全局尺寸@g-text-default: 14px;@g-text-sm: 12px;@g-text-lg: 18px;// 全局使用的自定义icon(这样写的好处是webpack打包时自动转base64)@g-icon-logo: url("../images/logo.png");@g-icon-logoBlack: url("../images/logoBlack.png");@g-icon-phone: url("../images/phone.png");@g-icon-message: url("../images/message.png");@g-icon-help: url("../images/help.png");@g-icon-down: url("../images/down.png");@g-icon-top: url("../images/top.png");@g-icon-home: url("../images/home.png");@g-icon-order: url("../images/order.png");@g-icon-cart: url("../images/cart.png");@g-icon-source: url("../images/source.png");@g-icon-business: url("../images/business.png");@g-icon-finance: url("../images/finance.png");@g-icon-account: url("../images/account.png");// ....// 背景色@g-color-grey1: #2a2f33; //黑色@g-color-grey2: #363b3f; //深灰色@g-color-grey3: #e5e5e5; //灰色@g-color-grey4: #efefef; //浅灰色@g-color-grey5: #f9f9f9; //很浅@g-color-grey6: #ffffff; //白色// 全局边框@g-border-default: #e6eaed;@g-border-active: #53b2ea;@g-border-light: #f7dfde;// 常用的border-box盒子模型.border-box() { box-sizing: border-box; -ms-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -webkit-box-sizing: border-box;}// 模拟按钮效果.btn() { cursor: pointer; user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; &:hover { opacity: .8; } &.disabled { &:hover { opacity: 1; cursor: not-allowed; } }}// 超出部分处理.text-overflow() { overflow: hidden; text-overflow: ellipsis; -o-text-overflow: ellipsis; -webkit-text-overflow: ellipsis; -moz-text-overflow: ellipsis; white-space: nowrap;}// reset styles.reset() {// ....}// 一些原子class.atom() { .cp { cursor: pointer; } .ml-5 { margin-left: 5px; } .mr-5 { margin-right: 5px; } .ml-5p { margin-left: 5%; } .mr-5p { margin-right: 5%; } .mt-5 { margin-top: 5px; } .txt-center { text-align: center; } .txt-left { text-align: left; } .txt-right { text-align: right; } .fr { float: right; } .fl { float: left; }}
component
的less
为了降低组件的耦合性,每个组件的less必须单独写,样式跟着组件走,一个组件一个less,不要有其他依赖,保证组件的高移植能力。
而且组件应该针对用途提供几套样式方案,比如button
组件,我们可以针对颜色提供不同的样式,以样式组合的方式提供给外部使用。
// 下面的变量可以针对不同的需求进行配置@color-primary: #459df5; @color-warning: #f7cec8; @color-success: #98cf07; @color-fail: #c21f16; .btn { cursor: pointer; user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; display: inline-block; box-sizing: border-box; -webkit-box-sizing: border-box; -ms-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; text-align: center; // 鼠标放上时 &:hover { opacity: .8; } // 按钮不可用时 &.disabled { &:hover { opacity: 1; cursor: not-allowed; } } // 填充式按钮 &.full { color: #fff; &.primary { background-color: @color-primary; border: 1px solid @color-primary; } // .... } // 边框式按钮 &.border { background-color: #fff; &.primary { color: @color-primary; border: 1px solid @color-primary; } // ... }}
container
的less
同上,每个container
一个less文件,可以复用的模块尽量封装成component
,而不是偷懒复制几行样式过来,这样虽然方便一时,但随着项目的迭代,后期的冗余代码会多得超出你的想象。
如果遵循组件化的设计思想,你会发现container
里其实只有一些布局和尺寸定义相关的代码,非常容易维护。
这是大型项目的设计要领,除此之外就是大局观的培养,这点尤为重要,项目一拿来不要马上就动手写页面,而是应该多花些时间在代码的设计上,把全局的东西剥离出来,越细越好;把可复用的模块设计成组件,思考组件的拓展性和不同的用途,记住—— 结构上尽量减少依赖关系,保持组件的独立性,而用途上多考虑功能的聚合,即所谓的低耦合高聚合。
不过实际项目不可能每个组件都是独立存在的,有时我们为了进一步减少代码量,会把一些常用的组件整合成一个大组件来使用,即复合组件。所以每个项目实际上存在一级组件(独立)和二级组件(复合)。一级组件可以随意迁移,而二级组件是针对实际场景而生的,两者并没有好坏之分,一切都为了高效地生产代码,存在即合理。
关于React的组织
本项目的React代码都用JavaScript的ES6风格编写,代码非常地优雅,而且语言自身支持模块化,再也不用依赖Browserify
、RequireJS
等工具了,非常爽。如果你不会ES6,建议去翻一翻阮一峰老师的《ES6标准入门》
入口
入口模块index.js
放在src
的根目录,是外部调用的入口。
import React from 'react'import { render } from 'react-dom'// 引入reduximport { Provider } from 'react-redux'// 引入routerimport { Router, hashHistory } from 'react-router'import { syncHistoryWithStore } from 'react-router-redux'import routes from './routes'import configureStore from './configureStore'const store = configureStore(hashHistory) // 路由的storeconst history = syncHistoryWithStore(hashHistory, store) // 路由的历史纪录(会写入到浏览器的历史纪录)render( ( <Provider store={store}> <Router history={history} routes={routes} /> </Provider> ), document.getElementById('root'))
路由
这里主要应用了react-route
组件来制作哈希路由,使用方式很简单,和ReactNative里的Navigator组件类似。
import React from 'react'import { Route } from 'react-router'import Manager from './containers/manager'import Login from './containers/Login/'import Register from './containers/Register/'import Password from './containers/Password/'import Dashboard from './containers/Dashboard/'const routes = ( <Route> <Route path="" component={Manager}> // 主容器 <Route path="/" component={Dashboard} /> // 仪表盘 // .... 各模块的container </Route> <Route path="login" component={Login} /> // 登录 <Route path="register" component={Register} /> // 注册 <Route path="password" component={Password} /> // 找回密码 </Route>)export default routes
了解action、store、reducer
从调用关系来看如下所示:
store.dispatch(action) --> reducer(state, action) --> final state
来个实际的例子:
// reducer方法, 传入的参数有两个// state: 当前的state// action: 当前触发的行为, {type: 'xx'}// 返回值: 新的statevar reducer = function(state, action){ switch (action.type) { case 'add_todo': return state.concat(action.text); default: return state; }};// 创建store, 传入两个参数// 参数1: reducer 用来修改state// 参数2(可选): [], 默认的state值,如果不传, 则为undefinedvar store = redux.createStore(reducer, []);// 通过 store.getState() 可以获取当前store的状态(state)// 默认的值是 createStore 传入的第二个参数console.log('state is: ' + store.getState()); // state is:// 通过 store.dispatch(action) 来达到修改 state 的目的// 注意: 在redux里,唯一能够修改state的方法,就是通过 store.dispatch(action)store.dispatch({type: 'add_todo', text: '读书'});// 打印出修改后的stateconsole.log('state is: ' + store.getState()); // state is: 读书store.dispatch({type: 'add_todo', text: '写作'});console.log('state is: ' + store.getState()); // state is: 读书,写作
store、reducer、action关联
store:对flux有了解的同学应该有所了解,store在这里代表的是数据模型,内部维护了一个state变量,用例描述应用的状态。store有两个核心方法,分别是getState、dispatch。前者用来获取store的状态(state),后者用来修改store的状态。
// 创建store, 传入两个参数// 参数1: reducer 用来修改state// 参数2(可选): [], 默认的state值,如果不传, 则为undefinedvar store = redux.createStore(reducer, []);// 通过 store.getState() 可以获取当前store的状态(state)// 默认的值是 createStore 传入的第二个参数console.log('state is: ' + store.getState()); // state is:// 通过 store.dispatch(action) 来达到修改 state 的目的// 注意: 在redux里,唯一能够修改state的方法,就是通过 store.dispatch(action)store.dispatch({type: 'add_todo', text: '读书'});
action:对行为(如用户行为)的抽象,在redux里是一个普通的js对象。redux对action的约定比较弱,除了一点,action必须有一个type字段来标识这个行为的类型。所以,下面的都是合法的action
{type:'add_todo', text:'读书'}{type:'add_todo', text:'写作'}{type:'add_todo', text:'睡觉', time:'晚上'}
reducer:一个普通的函数,用来修改store的状态。传入两个参数 state、action。其中,state为当前的状态(可通过store.getState()获得),而action为当前触发的行为(通过store.dispatch(action)调用触发)。reducer(state, action) 返回的值,就是store最新的state值。
// reducer方法, 传入的参数有两个// state: 当前的state// action: 当前触发的行为, {type: 'xx'}// 返回值: 新的statevar reducer = function(state, action){ switch (action.type) { case 'add_todo': return state.concat(action.text); default: return state; }}
@参考 《Redux系列01:从一个简单例子了解action、store、reducer》
- 基于Redux架构的单页应用开发总结(二)
- 基于Redux架构的单页应用开发总结(一)
- 基于Redux架构的单页应用开发总结(三)
- 基于Redux的ReactNative项目开发总结(二)
- 基于 Redux 的 React Native 应用架构
- 基于Redux的ReactNative项目开发总结(一)
- 基于Redux的ReactNative项目开发总结(一)
- 基于Cocoapods单工程多应用架构总结
- .net core + vue开发单页应用(二)
- 基于OSGi的Web应用开发系列二(转帖)
- ES6解构赋值在react-redux架构上的应用
- 基于react-router的单页应用
- 实例讲解基于 React+Redux 的前端开发流程
- 实例讲解基于 React+Redux 的前端开发流程
- 实例讲解基于 React+Redux 的前端开发流程
- 实例讲解基于 React+Redux 的前端开发流程
- 基于 react-native+redux 开发的高仿 V2EX 客户端
- 使用Redux和ngrx构建更好的Angular2应用(二)
- Nutch:skipped. Content of size 91280 was truncated to 59131
- Android 发布开源项目到jcenter
- iOS开发中sqlite数据库的基本使用
- ButterKnife--View注入框架
- c语言:递归求最大公约数
- 基于Redux架构的单页应用开发总结(二)
- solrj操纵CloudSolrServer的一般步骤
- ubuntu下crontab 定时执行php脚本
- css3选择器
- 【Android开发细节】之【@id与@+id区别】
- Spring的IOC原理[通俗解释一下]
- vs2013快捷键的使用
- Linux内核---多线程
- Caused by: org.xml.sax.SAXParseException; systemId: file:/opt/apache-nutch-2.2.1/runtime/local/conf/