React 入门实践
简介首先想要介绍的是 React,看到这篇文章的朋友想必都有一些关于 React 的了解了,但对于刚接触的新人而言,在这就要简要地介绍一下了。然后就是关于使用 React 构建一个简单单页应用(下文用 SPA 代替,Single Page Application)的一些介绍和讲解。 关于 ReactReact 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。(更多相关介绍请看这) 特点:
组件化:
生命周期:
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。
此外,React 还提供两种特殊状态的处理函数。
正题那么进入正题,花了点时间去写一个简单的 SPA,也算是一个比较完整 React 骨架,但不包括测试(测试的教程可以看这个),相关源码可以查看 react-start-kit。 接下来看看我们这个项目的构建需要用到些什么:
还有一些没有列举出来,具体可以看仓库源码的 配置项Webpack说到 React 项目的构建就不得不提 Webpack 这个神器。构建工具有很多,例如 Grunt,Gulp,Brunch 等,相比这些构建工具,Webpack 感觉就是和 React 不谋而合,尤其是 react-hot-loader 这样的神器(热加载),让 Webpack 成为最主流的 React 构建工具。 关于 Webpack 的特性以及介绍这里就不赘述了,我们可以从下图看出 Webpack 的作用: 接着我们从项目代码中来看 Webpack。 entry: { app: [__dirname + '/src/index'],},output: { path: __dirname + '/_dist',filename: '[name]_[hash].js',} 这部分主要是指定入口和出口文件。 module: { loaders: [{ test: /.js$/,loaders: ['babel'],exclude: /node_modules/,{ test: /.css$/,loaders: ['style','css'],include: /components/,{ test: /.(jpe?g|png|gif|svg|ico)/i,loader: 'file',{ test: /.(ttf|eot|svg|woff|woff2)/,{ test: /.(pdf)/,{ test: /.(swf|xap)/,}],} 而这部分会帮助我们去处理不同类型的文件,其中 最后配置好的 config 是这样的: var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { app: [__dirname + '/src/index'],output: { path: __dirname + '/_dist',resolve: { root: [ __dirname + '/src',__dirname + '/node_modules',__dirname,],extensions: ['','.js'],module: { loaders: [{ test: /.js$/,{ test: /.css$/,{ test: /.(jpe?g|png|gif|svg|ico)/i,{ test: /.(ttf|eot|svg|woff|woff2)/,{ test: /.(pdf)/,{ test: /.(swf|xap)/,plugins: [ new HtmlWebpackPlugin({ template: __dirname + '/src/index.html',favicon: __dirname + '/src/favicon.ico',inject: false,}),}; Express 服务器启动Node.js web 应用开发框架 Express 作为项目的 web 服务器,有 Node.js 开发经验的同学应该挺熟悉的了,这里也不多做赘述。 最终的启动代码是这样的: var express = require('express'); var webpack = require('webpack'); var webpackConfig = require('./webpack.development'); var app = express(); var compiler = webpack(webpackConfig); app.use(require('webpack-dev-middleware')(compiler,{ stats: { colors: true,})); app.use(require('webpack-hot-middleware')(compiler)); //热加载 app.listen(process.env.PORT,function(err) { //在没有端口的情况下,会自动给出一个随机端口 if (err) { console.log(err); } }); 为了方便我们的访问,项目使用了 Redux对于复杂的 SPA,状态(state)管理非常重要。state 可能包括:服务端的响应数据、本地对响应数据的缓存、本地创建的数据(比如,表单数据)以及一些 UI 的状态信息(比如,路由、选中的 tab、是否显示下拉列表、页码控制等等)。如果 state 变化不可预测,就会难于调试(state 不易重现,很难复现一些 bug)和不易于扩展(比如,优化更新渲染、服务端渲染、路由切换时获取数据等等)。
在使用 Redux 后,state 就变得很容易维护,而且数据流非常清晰,容易解决遇到的 BUG。 我们可以看下图来简要地理解 Redux: 我们可以在项目中看到的结构是: ├─store ├─actions ├─reducers ├─constants ├─helpers ├─components ├─app.js ├─favicon.ico ├─index.html ├─index.js └─routes.js 最终我们的 store 是: import {createStore,applyMiddleware,combineReducers,compose} from 'redux'; import thunk from 'redux-thunk'; import {reduxReactRouter} from 'redux-router'; import createHistory from 'history/lib/createHashHistory'; import routes from '../routes'; import * as reducers from '../reducers'; let middlewares = [thunk]; if (process.env.NODE_ENV === 'development') { //在开发环境下可以看到 state 的 log const logger = require('redux-logger'); middlewares = [...middlewares,logger]; } const finalCreateStore = compose( //组合多个函数 applyMiddleware(...middlewares),reduxReactRouter({routes,createHistory}),)(createStore); //创建 store 来管理所有的 state export default function configureStore(initialState) { const reducer = combineReducers(reducers); //把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数 const store = finalCreateStore(reducer,initialState); if (process.env.NODE_ENV === 'development' && module.hot) { //开发环境下的热加载 module.hot.accept('../reducers',() => { const nextReducers = require('../reducers'); const nextReducer = combineReducers(nextReducers); store.replaceReducer(nextReducer); }); } return store; } 获取 state 需要在组件中调用 ... @connect( state => ({ data: state.data }) ) export default class ComponentOne extends Component { ... } 注意: Router为项目添加路由系统,使用了 react-router 来管理路由。在开发项目的时候,比较推荐的做法是使用路由去跳转页面,并且创建 store 的同时我们就把 router 加入其中,然后我们根据路由的变化去更新视图。 我们可以看看路由的源码: import React from 'react'; import Route from 'react-router/lib/Route'; //import {Route} from 'react-router'; import Base from 'components/base/Base'; import Home from 'components/home/Home'; export default ( <Route component={Base}> <Route path="/" component={Home} /> <Route path="/home" component={Home} /> </Route> );
Ant Design由蚂蚁金服技术部出品的一个 UI 设计语言,也是项目中所用到的 UI 组件库。 特性:
选择理由:
简单的 Component组件作为 React 渲染的一个基本组成,我们通常把它们分为两类,容器型和展示型。相较于容器型,展示型是通过容器型传递 props 来获取数据,而容器型可以直接从 store 中获取,处理并传递给下级组件。 在实际应用中会发现,定义一个容器型组件负责处理数据,然后分发给下级展示型组件,当需要更新数据时,那么容器型组件发生变化会引起下级展示型组件的变化,这样就对我们业务上造成了一定的困扰(在不需要更新的部分组件上也发生了更新)。因此,我们选择在需要获取数据的组件中使用 在项目中我们会这么定义组件: import React,{Component} from 'react'; import {connect} from 'react-redux'; import Presentational from 'components/common/Presentational'; @connect( state => ({ data: state.data }) ) export default class Container extends Component { render() { const {data} = this.props; return ( <Presentational data={data} /> ) } } 上面是可以从 store 获取数据的组件,并嵌套另一个组件,将数据传递给它。 import React,{Component,PropTypes} from 'react'; export default class Presentational extends Component { static propTypes = { data: PropTypes.string,} render() { const {data} = this.props; return ( <div> {data} </div> ) } } 获取上一个组件传递过来的数据,并展示出来。 总结这是一篇科普文(哈哈~囧),并没有深入去分析各项技术的具体内容,希望能帮助刚入手 React 的新手们。实践项目的源码可以在 react-start-kit 看到,你可以下载这个项目进行自己的一些探索和开发。还在努力探索中,文中有措辞不当或是疏漏,欢迎提出意见和建议。 参考react 官网 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |