react开发

从头开始建立一个React App - 项目基本配置

  1. npm init 生成 package.json 文件.
  2. 安装各种需要的依赖:
    • npm install --save react - 安装React.
    • npm install --save react-dom 安装React Dom,这个包是用来处理virtual DOM。这里提一下用React Native的话,这里就是安装react-native。
    • npm install --save-dev webpack - 安装Webpack, 现在最流行的模块打包工具.
    • npm install --save-dev webpack-dev-server - webpack官网出的一个小型express服务器,主要特性是支持热加载.
    • npm install --save-dev babel-core - 安装Babel, 可以把ES6转换为ES5,注意Babel最新的V6版本分为babel-cli和babel-core两个模块,这里只需要用babel-cor即可。
      • 安装其他的babel依赖(babel真心是一个全家桶,具体的介绍去官网看吧..我后面再总结,这里反正全装上就是了):
      • npm install --save babel-polyfill - Babel includes a polyfill that includes a custom regenerator runtime and core.js. This will emulate a full ES6 environment
      • npm install --save-dev babel-loader - webpack中需要用到的loader.
      • npm install --save babel-runtime - Babel transform runtime 插件的依赖.
      • npm install --save-dev babel-plugin-transform-runtime - Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals.
      • npm install --save-dev babel-preset-es2015 - Babel preset for all es2015 plugins.
      • npm install --save-dev babel-preset-react - Strip flow types and transform JSX into createElement calls.
      • npm install --save-dev babel-preset-stage-2 - All you need to use stage 2 (and greater) plugins (experimental javascript).
  3. 打开 package.json 然后添加下面的scripts:

    1. "scripts": {
    2. "start": "webpack - dev - server - - hot - - inline - - colors - - content - base . /build" ,
    3. "build": "webpack - - progress - - colors"
    4. }

    命令行输入 npm start 将要启动webpack dev server.

    命令行输入 npm build 将会进行生产环境打包.

  4. 启动webpack

    Webpack是我们的打包工具,在我们的开发环境中具体很重要的作用,具有很多非常便捷的特性,尤其是热加载hot reloading. webpack.config.js 是如下所示的webpack的配置文件. 随着app的不断变化,配置文件也会不断的更新,这里我们就用默认的webpack.config.js来命名这个配置文件,假如你用别的名字比如webpack.config.prod.js那么上面的脚本build就需要相应的改变指定相应的配置文件名字:"build": "webpack webpack.config.prod.js --progress --colors"

    1. module.exports = {
    2. output: {
    3. filename: "bundle.js"
    4. module: {
    5. test: /\.js$/,
    6. loader: 'babel-loader',
    7. plugins: ['transform-runtime'],
    8. }
    9. test: /\.css$/,
    10. }]
    11. };
    1. OK,我们项目的基本配置终于完成了,是时候开始写Reac代码了.

React 基础 - 建立你的第一个Component

在上面的项目的基本配置基础上,我们开始书写React的第一个组件来熟悉React的写法与组件思想。

  1. 首先我们在项目根目录中新建一个 index.html 文件。 在这个基础工程中, 我们使用bootstrap的样式,直接引入一个cdn即可. 然后添加一个html标签 <div id="app"></div>,我们的app就会注入到这个div中。 最后再引入 <script src="bundle.js"></script>,这是最后打包生成的js代码。

    以下是完整的代码:

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Document </title>
    6. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    7. </head>
    8. <body>
    9. <div id="app"> </div>
    10. <script src="bundle.js"> </script>
    11. </body>
    12. </html>
  2. 建立一个新的文件夹 src. 我们app的大部分代码都将放在这个文件夹里面。在 src中建立 app.js,作为React App的根组件, 其他所有的组件都会注入到这个跟组件中。
  3. 首先我们需要导入react,现在都已经用ES6的语法, import React from 'react'; , 然后我们要引入react-dom. 这里面有react中最重要的一个虚拟dom的概念.引入代码:import ReactDOM from 'react-dom';

  4. 现在需要引入的依赖都已经完毕我们可以写第一个组件了:

    1. render(){ // Every react component has a render method.
    2. <div>
    3. </div>
    4. }
    5. class ToDoApp extends React.Component {
    6. return (
    7. );
    8. }
  5. 为了将这个组件注入到我们的APP中, 首先我们需要输出它。 在这个组件代码底部添加 export default ToDoApp;

  6. 然后在app.js顶部我们添加 import ToDoApp from '.components/ToDoApp'; 导入组件用来代替 Hello World 。 render中替换为新的jsx代码 <ToDoApp />半闭合类型的标签即可。

  7. 然后在浏览器中你就可以看到"To Do App" 代替了原来的 "Hello World"!这样我们就完成了将第一个子组件嵌入到根组件之中了,这就是构建react app的常规模式。下面继续完善我们的组件。

  8. 返回到ToDoApp 中来构建我们的第一个代办事项列表。首先我们使用bootstrap来构建比较方便且美观。 用下面的jsx替换当前render方法中 return 中的jsx:

    1. <div className="row">
    2. <div className="col-md-10 col-md-offset-1">
    3. <div className="panel panel-default">
    4. <div className="panel-body">
    5. <h1>My To Do App </h1>
    6. <hr/>
    7. List goes here.
    8. </div>
    9. </div>
    10. </div>
    11. </div>
  9. 现在打开浏览器, 你将会看到一个标题 "My To Do App" 下面跟随一个bootstrap的panel组件里面写有 "List Goes Here",我们将在这个地方构建列表。 那么我们如何将数据存储在我们的列表中呢? 答案就是使用 state. 每一个类组件都有 state 属性,可以通过 this.state在组件任何位置获取并且用 this.setState({ key: "value" })这种方法来更新状态。但是除非必要我们比较少使用state,这里暂时先使用作为了解,后期会使用redux来管理状态。

    ToDoApp中我们可以使用许多生命周期方法的钩子, 其中一个就是componentWillMount。 这个方法的执行是在页面加载并且render方法之前。可以在其中获取列表数据,在我们的APP中直接用一个虚拟的数组提供。(值得注意的是componentWillMount会引起很多小问题,因此真实项目中尽量不要使用,而是应该用componentDidMount)。
    在 ToDoApp中 render 方法之前添加:

    1. this.setState({ // add an array of strings to state.
    2. })
    3. const List = (props) => { // we're using an arrow function and const variable type, a ES6 features
    4. return (
    5. I'm a list!!!
    6. )
    7. <code class="javascript" ,monospace;="" font-size:12px;="" background-color:transparent;="" padding:0px;="" border:none"="" style="outline: 0px; padding: 8px; font-family: Menlo, Monaco, Consolas, "Courier New"; word-break: break-all;"> export default List;
  10. 在 ToDoApp.js引入 List用List 组件替换 List goes here.,写法为 <List />.现在在浏览器中就可以看到"I'm a list!!!"

    现在我们来把这个变成真实的列表,首先就需要通过props传递数据,我们把这个从state中获取的数据list通过命名为listItems的props传递,写作: <List listItems={this.state.list} /> ,现在 List 已经通过props获取了 ToDoApp中的数据。

    然后在 List 组件中我们需要render一个列表,先用下面的jsx代码代替:

    1. <ul>
    2. list // this is a variable we'll define next
    3. </ul>
    4. const list = props.listItems.map((el, i)=>(
    5. // (which is stored in the state of the parent component)
    6. // which returns a new array of list items. The key attribute is
    7. <li key={i}><h2>el</h2></li>
    8. import React from 'react';
    9. const List = (props) => {
    10. const list = props.listItems.map((el, i)=>(
    11. ));
    12. return (
    13. <ul>
    14. list
    15. </ul>
    16. )
    17. componentWillMount(){
    18. list: ['thing1', 'thing2', 'thing3'],
    19. })
    20. onInputChange = (event) => {
    21. };

    6. 添加新列表事项

    现在需要向列表中添加新的事项,也就是在提交后能把输入框的值存储并显示到列表中。我们需要再新建一个onInputSubmit的方法,参数同样是event,函数体内首先需要写 event.preventDefault(),然后用 setState 方法把新事项添加到列表数组中,但是,一定要注意我们的state应该是immutable的,这是react中必须遵循的一个准则,这样才能保证对比性与可靠性。

    为了实现这个功能, 需要用到this.setState 回调函数,参数为previousState

    1. list: previousState.list.push(previousState.newToDo)
    2. this.setState((previousState)=>({
    3. }));

    在提交添加新事项的同时,需要将newToDo重置为''

    1. list: [...previousState.list, previousState.newToDo ],
    2. }));

    7. 划掉事项

    是时候添加划掉事项的功能了。为了实现这个功能需要添加一个新的属性用来标注是否需要划掉,因此需要改变原来的数组为一个对象数组,每一个事项都是一个对象,一个key为item表示原来的事项内容,一个key为done用布尔值来表示是否划掉。 然后先把原来的onInputSubmit方法修改,同样要注意immutable,使用扩展操作符如下:

    1. event.preventDefault();
    2. list: [...previousState.list, {item: previousState.newToDo, done: false }], // notice the change here
    3. }));
    4. onListItemClick = (i) => { // takes the index of the element to be updated
    5. list: [
    6. Object.assign({}, previousState.list[i], {done: !previousState.list[i].done}), // Object.assign is a new ES6 feature that creates a new object based on the first param (in this case an empty object). Other objects can be passed in and will be added to the first object without being modified.
    7. ]
    8. };

    然后把这个方法通过props传递给List 组件,这里就没有使用解构参数传递,用来和Input的做对比。因为这个函数需要一个参数就是当前列表的序列号,但是肯定不能直接call这个函数否则会报错,因此使用bind方法,出入i参数:

    onClick={() => props.onClick(i)}

    然后在事项内容的span标签上添加 onClick 方法,改变当前事项的done值后,在通过判断此布尔值来进行样式的修改添加或者划掉删除线。

    1. style={
    2. ? {textDecoration: 'line-through', fontSize: '20px'}
    3. }
    4. >

    8. 删除事项

    最后我们在添加删除事项的功能,这个和划掉事项非常相似,我们只需要新增一个删除按钮,然后再新增一个方法修改列表,具体代码如下:

    1. className="btn btn-danger pull-right"
    2. x
    3. deleteListItem = (i) => {
    4. list: [
    5. ...previousState.list.slice(i+1) // the only diffence here is we're leaving out the clicked element
    6. }))
    7. npm install --save redux
    8. npm install --save redux-logger

    还有一些常用的中间件,比如 redux-thunk and redux-promise, 但是在我们的这个项目中暂时先不需要,可以自行去github了解。

    2. 构建

    使用redux构建react应用一般都有一个标准的模板,可能不同模板形式上有区别,但是思想都是一样的,下面就先按照一种文件结构来构建。

    首先我们在src中新建一个文件夹redux,然后在其中新建一个文件configureStore.js,添加以下代码:

    1. import createLogger from 'redux-logger';

    createStore 是由redux提供的用来初始化store的函数, applyMiddleware是用来添加我们需要的中间件的。

    combineReducers 用来把多个reducers合并为一个单一实体。

    createLogger 就是我们这里唯一使用的一个中间件,可以console出每一个action后数据的详细处理过程,给调试带来了很大方便。

    然后添加下面代码:

    1. const initialState = {}; //The initial state of this reducer (will be combined with the states of other reducers as your app grows)
    2. export default function reducer(state = initialState, action){ // a function that has two parameters, state (which is initialized as our initialState obj), and action, which we'll cover soon.
    3. default:
    4. }
    5. const reducer = combineReducers({
    6. });

    最后在底部加入下面完整的代码:

    1. export default configureStore;

    Cool. We're done here.

    5. Connect

    现在我们已经有了一个reducer,那么怎么和app建立联系呢?这需要两步工作。

    前面已经讲过类组件和函数型组件,有时候也可以称为smart componentsdumb components,这里我们新增一种容器组件,顾名思义,这种组件就是作为一个容器用来给组件提供actionsstate

    下面来创建第一个容器组件,首先在 /src/ 下新增一个文件夹containers,然后再其下面新建一个文件 toDoAppContainer.js
    在文件顶部首先导入 connect 用来将容器和组件联系在一起,

    1. import ToDoApp from '../components/ToDoApp.js'

    connect 这个函数被调用两次, 第一次是两个回调函数: mapStateToProps and mapDispatchToProps。 第二次是把statedispatch传入组件的时候。这里的dispatch又是什么呢?

    当我们需要在redux中发生某些行为时候,就需要调用dispatch函数传递一个action然后调用reducer这一套流程。因为我们还没有编写具体的行为,这里就暂时空白,后面再补,代码形式如下:

    1. return {
    2. }
    3. return {}; // here we'll soon be mapping actions to props
    4. export default connect(
    5. mapDispatchToProps
    6. import { Provider } from 'react-redux';
    7. import configureStore from './redux/configureStore';

    configureStore is the function we created that takes our combined reducers and our redux middleware and mashes them all together. Let's intialize that with the following line:

    <Provider store={store}> // we pass the store through to Provider with props
  11. </Provider>

现在整个redux的基本结构已经搭建起来,下一步就可以把整个行为逻辑代码补充进去就可以了。

Redux Actions 和 Reducers

搭建起redux的基本结构后,就可以填充redux的元素了,简单来说我们只需要记住四个概念, TypesActionsAction Creators, and Reducers。然后把这些元素用ducks的文件组织结构组织起来就可以了。

Ducks

规则

在module中我们需要遵循下面的代码风格和命名方式:

  1. 须用 export default 输出名为 reducer()的函数
  2. 须用 export 输出 函数形式的action creators
  3. 须用 npm-module-or-app/reducer/ACTION_TYPE
    的命名形式来命名action types,因为到后期很多reducer,不同的人协同工作难免会出现命名重复,这样子加上app和模块的前缀的话就不会出现命名冲突的问题。
  4. 须用大写的蛇形方式UPPER_SNAKE_CASE来命名action types

Types

这个types就是上面第三条中需要按照ducks的规范命名的常量名字,将其写在文件的顶部,当action 触发时候会传递给reducerreducer的switch语句会根据这个type来进行相应的数据处理。

  1. const DELETE_ITEM = 'my-app/toDoApp/DELETE_ITEM';

Actions

Actions 就是一个至少包含type的简单的js对象,同时可以包含数据以便传递给reducer。当用户在页面上触发了某种行为,一个aciton creator将会发送acitonreducer做数据处理。

action示例如下:

  1. { type: DELETE_ITEM, index: 1 }
  2. function addItem(item){
  3. type: ADD_ITEM,
  4. }
  5. const initialState = {
  6. };
  7. export default function reducer(state = initialState, action){
  8. case ADD_ITEM:
  9. {},
  10. { list: [...state.list, action.item]} // here we see object.assign again, and we're returning a new state built from the old state without directly manipulating it
  11. default:
  12. }
  13. const initialState = {
  14. newToDo: ''
  15. switch (action.type){
  16. return state;
  17. }

现在在 ToDoApp.js的 render() 方法中return之前添加console.log(this.props) 会打印出下面的对象:

  1. list: Array[1]
  2. length: 1
  3. __proto__: Object
  4. <List
  5. listItems={this.props.toDoApp.list}
  6. />
  7. value={this.props.toDoApp.newToDo}
  8. onSubmit={this.onInputSubmit}
  9. const INPUT_CHANGED = 'INPUT_CHANGED';
  10. export function inputChange(newToDo){
  11. type: INPUT_CHANGED,
  12. }
  13. case INPUT_CHANGED:
  14. {},
  15. {newToDo: action.value}
  16. import { connect } from 'react-redux';
  17. import {
  18. } from '../redux/modules/toDoApp'; // we added this
  19. function mapStateToProps(state) {
  20. toDoApp: state.toDoApp // gives our component access to state through props.toDoApp
  21. }
  22. function mapDispatchToProps(dispatch) {
  23. inputChange: (value) => dispatch(inputChange(value)) // we added this
  24. }
  25. export default connect(
  26. mapDispatchToProps
  27. <code class="javascript" ,monospace;="" font-size:12px;="" background-color:transparent;="" padding:0px;="" border:none"="" style="outline: 0px; padding: 8px; font-family: Menlo, Monaco, Consolas, "Courier New"; word-break: break-all;">)(ToDoApp);

这样state和action都传递给了toDoApp然后再通过props传递给子组件就可以使用了,具体都可以看项目最终代码。

4. 其他 actions

其他acitons的代码模式跟上面的基本一样,这里不在赘述。

总结

到这里一个使用webpack打包的react+redux(ducks)的基本应用模型就出来了,虽然简单但是是我们进行更复杂项目的基础,并且有了这些基础后面的路程将会顺畅多了,一起加入react的大家庭吧。

猜你喜欢

转载自blog.csdn.net/ryuenkyo/article/details/80939519