• 从头开始建立一个React App - 项目基本配置 npm init 生成 package.json 文件.安装各种需要的依赖: npm install --save react - 安装React.npm install --save react-dom 安装React Dom,这个包是用来...

    从头开始建立一个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:

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

      命令行输入 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"

      var webpack = require('webpack');
      module.exports = {
       entry: './src/app.js',
       output: {
           path: __dirname + '/build',
           filename: "bundle.js"
       },
       module: {
           rules: [{
               test: /\.js$/,
               exclude: /node_modules/,
               loader: 'babel-loader',
               query: {
                   plugins: ['transform-runtime'],
                   presets: ['es2015', 'react', 'stage-2']
               }
           }, {
               test: /\.css$/,
               loader: "style-loader!css-loader"
           }]
       }
      };
      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代码。

      以下是完整的代码:

       <!DOCTYPE html>
       <html lang="en">
       <head>
         <meta charset="UTF-8">
         <title>Document</title>
         <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
       </head>
       <body>
         <div id="app"></div>
         <script src="bundle.js"></script>
       </body>
       </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. 现在需要引入的依赖都已经完毕我们可以写第一个组件了:

       class App extends React.Component {
         render(){ // Every react component has a render method.
           return( // Every render method returns jsx. Jsx looks like HTML, but it's actually javascript and functions a lot like xml, with self closing tags requiring the `/` within the tag in order to work propperly
             <div>
               Hello World
             </div>
           );
         }
       }

      注意这里"Hello World"写在 div中. 所有的jsx代码都需要写在一个父div中.

    5. 最后我们需要把我们写好的组件render给Dom,这里就需要用到 ReactDOM.render 方法.

      在 App.js 的下面添加: ReactDOM.render(<App />, document.getElementById('app'));

      第一个参数就是我们App的根组件, 写作<App />的形式. 第二个参数就是我们的APP将要主要的DOM元素. 在这个项目中,就是我们在index中写的id为app的 div标签。

    Ok,我们的APP结构已经出来了,经典的hello world已经实现。马上我们就在这个基础上再实现经典的todo app。大致的原型就有一个输入框用来输入代办事项然后添加到事件列表中。事件列表中每一个代办事项被点击就会标注一条删除线表示完成,点击后面的删除按钮则会将其从列表中删除。通过完成这个APP的过程你将学会一个完整的react app的所有的基本构建块。

    生命周期方法和两种形式的组件构建

    我们从一些小的模块开始起步.一个组件component就是一个react app的构件块. 有两种形式的组件: 类组件(Class)和函数型组件(Functional). 在这个项目中,这两种形式的组件我们都会使用, 并且使用生命周期的钩子,同时也会使用state和props两个react中重要的属性。

    1. 首先在 src文件夹中新建components 文件夹,你的文件结构就是这样 ~/src/components

    2. 然后在components中新建文件 ToDoApp.js。 对于所有的react组件我们都需要在头部引入reactimport React from 'react';

    3. 下面我们写一个类组件. 所有的class 组件有一个render方法用来返回jsx。

      ToDoApp的class就如下所示:

       class ToDoApp extends React.Component {
         render() {
           return (
             <div>To Do App</div>
           );
         }
       }
    4. 为了将这个组件注入到我们的APP中, 首先我们需要输出它。 在这个组件代码底部添加 export default ToDoApp;

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

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

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

      <div className="row">
      <div className="col-md-10 col-md-offset-1">
       <div className="panel panel-default">
         <div className="panel-body">
           <h1>My To Do App</h1>
           <hr/>
           List goes here.
         </div>
       </div>
      </div>
      </div>
    8. 现在打开浏览器, 你将会看到一个标题 "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 方法之前添加:

         componentWillMount(){ // run before the render method
           this.setState({ // add an array of strings to state.
             list: ['thing1', 'thing2', 'thing3']
           })
         };

      现在我们获取了一个虚拟列表,需要重点注意的就是react依赖于state和props,只有当state和props改变的时候react组件才会刷新。

    9. 现在我们添加列表到这个view里,这里不是直接简单的在里面修改jsx,而是再创建一个新的组件来构建列表,这次我们学习使用函数型组件,需要注意的是函数型组件没有生命周期方法和state属性,它仅仅是一个返回jsx的函数,并且参数是props。

      那么props到底是什么呢?props是从父组件传递进子组件的数据的名字,这是一个很重要的概念,也是react app数据传递的最典型与最推荐的方法。通常我们将数据保持在app的顶端组件,通过组件让数据流下来保证APP的精确运行。这些数据和props的一些处理可能会影响APP的运行,但是假如你按照这个课程的实践流程来做,这些影响都会很小。

      再新建一个components文件夹并在其中新建一个List.js作为我们要创建的函数型组件。用const来新建一个函数,参数名字写作props。
      函数形式如下所示:

       const List = (props) => { // we're using an arrow function and const variable type, a ES6 features
      
         return (
           <div>
             I'm a list!!!
           </div>
         )
       };
      
       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代码代替:

      <div>
        <ul>
          {
            list // this is a variable we'll define next
          }
        </ul>
      </div>

      注意这个大括号,js可以在这里面执行并将返回添加到view里。首先我们定义一个列表变量:

      const list = props.listItems.map((el, i)=>(
        // All where doing here is getting the items listItems prop
        // (which is stored in the state of the parent component)
        // which is an array, and we're running the .map method
        // which returns a new array of list items. The key attribute is
        // required, and must be unique.
        <li key={i}><h2>el</h2></li>
      ));

      完整的组件如下:

      import React from 'react';
      
      const List = (props) => {
      
        const list = props.listItems.map((el, i)=>(
          <li key={i}><h2>el</h2></li>
        ));
      
        return (
          <div>
            <ul>
              {
                list
              }
            </ul>
          </div>
        )
      };
      
      export default List;
    11. 现在打开浏览器就可以看到一列列表了。接下来就是给我们的项目加入功能了,包括添加新的事项,标注事项完成和删除列表中事项。

    给APP添加功能

    1.函数型组件

    首先我们需要添加一个input元素以便可以输入代办事项。因此我们在components文件夹中新建一个Input.js,然后在其中创建并输出一个名叫Input的函数型组件。
    把下面的jsx代码粘贴到你的函数型组件return之中:

    <form>
      <div
        className="form-group">
        <label
          htmlFor="listInput">
          Email address
        </label>
        <input
          type="text"
          className="form-control"
          id="listItemInput"
          placeholder="Add new todo"
        />
        <button
          className="btn btn-primary">
          Add Item
        </button>
      </div>
    </form>

    2. Input

    现在我们的jsx没有做任何特殊的事情,仅仅是一个基本的html视图,不过我们先测试一下把其导入到ToDoApp.js,形式就是<Input/>

    这时候会发现一个输入框和按钮的视图,这个组件的静态视图已经写好了,下面就需要添加功能了。

    3. Props

    首先我们需要做的是如何获取输入框的值,因为这个输入框的值需要在其他组件中获取,所以我们并不想要在Input组件中来处理这个数据存储。事实上,在子组件中存储数据在任何时候都是不推荐的,我们应该将数据存储在app的顶端组件并且通过props传递下来。

    另一个需要记住的是即使我们目前把数据存储在了上层的 ToDoApp 组件,后期还是会用redux来代替来处理整个app的数据。这里先仅仅使用react的state来实现。

    ok,我们在ToDoApp的 componentWillMountsetState中新增一个newToDo属性用来存储输入框的值。

      componentWillMount(){
        this.setState({
          list: ['thing1', 'thing2', 'thing3'],
          newToDo: 'test'
        })
      };

    同样的就可以通过在<Input />上通过props传递下去。

    4. 解构(Destructuring)

    Input.js中我们通过参数props可以获得上级组件传递下来的值, 但是还可以用ES6的新特性解构来作为参数,这样看起来更加酷!

    Input组件的props参数修改为({ value })这样的参数形式,这样可以把props这个对象参数解构为一个个键值对。直接看个小例子来就很明白了:

    var props = {
      name: 'hector',
      age: 21
    }
    
    
    function log(props){
      console.log(props.name);
      console.log(props.age);
    }
    
    log(props);

    is the same as this:

    let props = {
      name: 'hector',
      age: 21
    }
    
    log = ({name, age}) => {
      console.log(name);
      console.log(age);
    }
    
    log(props);

    5. setState

    上面的newToDo仅仅是添加了一个state用来存储输入框的值,给定一个值,输入框就会显示,明显还不是我们要的效果,我们需要做的是基于输入框的值的改变来动态改变这个state。

    为了实现这个功能,我们需要再添加一个onChange方法同样利用props传进Input组件: onChange={onChange}, 然后解构参数就是({ onChange, value })

    然后在 ToDoApp.jscomponentWillMount 添加一个新的方法 onInputChange。这个方法有一个参数event, 它将要捕获用户在输入框输入的值。

    onInputChange = (event) => {
      this.setState({ newToDo: event.target.value}); // updates state to new value when user changes the input value
    };

    6. 添加新列表事项

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

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

    this.setState((previousState)=>({
      list: previousState.list.push(previousState.newToDo)
    }))

    正如我上面的描述,最开始写state的时候很多人都会犯这样的错误,直接用push这样的方法,修改了state,这样就不算immutable的,我们一定要保证绝不直接修改原state。

    这里又可以用到ES6中的新特性了,扩展操作符,它通过遍历旧数组返回一个新数组,使旧的数组保持原样,这样我们就把事项添加到列表数组末尾:

    this.setState((previousState)=>({
      list: [...previousState.list, previousState.newToDo ], // the spread opperator is called by using the ... preceding the array
    }));

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

    this.setState((previousState)=>({
      list: [...previousState.list, previousState.newToDo ],
      newToDo: ''
    }));

    7. 划掉事项

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

    onInputSubmit = (event) => {
      event.preventDefault();
      this.setState((previousState)=>({
        list: [...previousState.list, {item: previousState.newToDo, done: false }], // notice the change here
        newToDo: ''
      }));
    };

    属性done添加完成后就需要新增一个方法当点击事项时候来改变这个值:

    onListItemClick = (i) => { // takes the index of the element to be updated
      this.setState((previousState)=>({
        list: [
          ...previousState.list.slice(0, i), // slice returns a new array without modifying the existing array. Takes everything up to, but not including, the index passed in.
          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.
          ...previousState.list.slice(i+1) // takes everything after the index passed in and adds it to the array.
        ]
      }))
    };

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

    onClick={props.onClick.bind(null, i)}

    当然还有另一种方法:

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

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

    <span
      style={
        el.done
        ? {textDecoration: 'line-through', fontSize: '20px'}
        : {textDecoration: 'none', fontSize: '20px'}
      }
      onClick={props.onClick.bind(null, i)}
    >

    8. 删除事项

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

    <button
      className="btn btn-danger pull-right"
      >
      x
    </button>
    deleteListItem = (i) => {
      this.setState((previousState)=>({ // using previous state again
        list: [
          ...previousState.list.slice(0, i), // again with the slice method
          ...previousState.list.slice(i+1) // the only diffence here is we're leaving out the clicked element
        ]
      }))
    };

    deleteListItem 方法传递到列表组件中然后在删除按钮上绑定即可,仿照上一个自己写一下就好。

    现在我们有一个完整功能的APP了,是不是感觉很cool,这个就是不用redux时候的形态了,但是你会发现当状态越来越复杂时候很繁琐,因此我们下面就要介绍redux来管理状态了。

    迁移到redux的准备工作

    截至目前我们已经学会如何用webpack和babel搭建react应用,构建类组件和函数型组件并处理state,添加功能。然而这只是基本满足一个小型应用的需求,随着app的增长,处理数据和行为会越来越吃力,这就是要引入redux的必要性。

    那么redux如何处理数据?首先,redux给你的app一个单一的state对象,与flux等根据view来划分为多个state对象正好相反。你可能会有疑问,一个单一的对象来处理一个复杂的app岂不是非常复杂?redux采用的方法是把数据处理分为reducer functionsaction creatorsactions然后组合在一起工作流线型的处理数据。

    1. 首先安装必须的依赖

    首先安装 redux and react-redux

    npm install --save redux
    npm install --save react-redux

    然后安装 redux middleware,这里就先安装 redux-logger,它的功能是帮助我们开发。

    npm install --save redux-logger

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

    2. 构建

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

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

    import { createStore, applyMiddleware, combineReducers } from 'redux';
    import createLogger from 'redux-logger';

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

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

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

    然后添加下面代码:

    const loggerMiddleware = createLogger(); // initialize logger
    
    const createStoreWithMiddleware = applyMiddleware( loggerMiddleware)(createStore); // apply logger to redux

    这里暂时没有完成,需要后面的模块写完了再导入到这里继续来完成。

    3. 模块Modules

    在 src/redux/ 新建一个文件夹 modules。在这个文件夹中我们将存放所有的reducersaction creatorsconstants。这里我们使用的redux组织结构叫做ducks,思想就是把相关的reducersaction creatorsconstants都放在一个单独的文件中,而不是分开放在多个文件中,这样修改一个功能时候直接在一个文件中修改就可以。

    在 modules 文件中新建 'toDoApp.js',注意这里的命名是依据容器组件的名字来命名,这个也是规范,容易管理代码。

    现在我们可以开始创建initial state和 reducer function,这其实非常简单,state就是一个js对象,reducer就是js的switch语句:

    const initialState = {}; //The initial state of this reducer (will be combined with the states of other reducers as your app grows)
    
    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.
      switch (action.type){
      default:
        return state;
      }
    }

    4. 完善Store

    现在我们已经完成了第一个reducer,可以将其添加到 configureStore.js 中去了, 导入: import toDoApp from './modules/toDoApp';

    然后用combineReducers 来组合当前的reducer,因为未来会有更多的模块加入。

    const reducer = combineReducers({
      toDoApp
    });

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

    const configureStore = (initialState) => createStoreWithMiddleware(reducer, initialState);
    export default configureStore;

    Cool. We're done here.

    5. Connect

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

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

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

    import { connect } from 'react-redux';
    import ToDoApp from '../components/ToDoApp.js'

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

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

    function mapStateToProps(state) {
      return {
        toDoApp: state.toDoApp // gives our component access to state through props.toDoApp
      }
    }
    
    function mapDispatchToProps(dispatch) {
      return {}; // here we'll soon be mapping actions to props
    }

    然后在底部添加:

    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(ToDoApp);
    1. Provider

    redux的基本工作已经完成,最后一步就是返回到app.js 文件, 首先我们不再需要导入 ToDoApp 组件,而是用容器组件ToDoAppContainer来替代,然后需要导入 configureStore 函数和 Provider,在头部添加代码:

    import { Provider } from 'react-redux';
    import ToDoAppContainer from './containers/ToDoAppContainer';
    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:

    const store = configureStore();

    然后return的jsx中同样需要把ToDoApp 改为 ToDoAppContainer,然后需要用Provider 组件将其包裹,它的作用就是将整个app的state传递给它所包裹的容器,从而使容器组件可以获取这些state。

    <Provider store={store}> // we pass the store through to Provider with props
      <ToDoAppContainer />
    </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来进行相应的数据处理。

    const ADD_ITEM = 'my-app/toDoApp/ADD_ITEM';
    const DELETE_ITEM = 'my-app/toDoApp/DELETE_ITEM';

    Actions

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

    action示例如下:

    { type: ADD_ITEM, item: 'Adding this item' }
    { type: DELETE_ITEM, index: 1 }
    { type: POP_ITEM }

    Action Creators

    Action creators 是创建acitons并传递给reducer的函数,它通常返回一个action对象,有时候借用thunk这样的中间件也可以返回dispatch多个actions,在我们的app中为了简化暂时不涉及这个模式。

    function addItem(item){
      return {
        type: ADD_ITEM,
        item // this is new ES6 shorthand for when the key is the same as a variable or perameter within the scope of the object. It's the same as item: item
      }
    }

    Reducers

    reducer是唯一可以触碰store的元素,初始值为initialState,形式上就是一个简单的switch语句,但是注意不能直接改变state,因为state是immutable。也就是说我们不能直接使用.pop or .push这些方法操作数组。

    下面是示例代码:

    const initialState = {
      list: []
    };
    
    export default function reducer(state = initialState, action){
      switch (action.type){
      case ADD_ITEM:
        return Object.assign(
          {},
          state,
          { 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
        )
      default:
        return state;
      }
    }

    概念已经介绍完毕,下面开始将原来的功能逻辑用redux重写。

    1. Initial state

    首先我们在 src/redux/modules/toDoApp中声明initialState

    const initialState = {
      list: [{item: 'test', done: false}] // just added this to test that state is being passed down propperly,
      newToDo: ''
    };
    
    export default function reducer(state = initialState, action){
      switch (action.type){
      default:
        return state;
      }
    }

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

    toDoApp: Object
      list: Array[1]
        0: "test"
        length: 1
        __proto__: Array[0]
      __proto__: Object
    __proto__: Object

    测试通过,我们就可以传递这些数据给子组件了,这里就可以把原来List组件的 listItems prop和Inputvalue prop替换掉了。

    <List
      onClick={this.onListItemClick}
      listItems={this.props.toDoApp.list}
      deleteListItem={this.deleteListItem}
    />
    <Input
      value={this.props.toDoApp.newToDo}
      onChange={this.onInputChange}
      onSubmit={this.onInputSubmit}
    />

    这里只是替换掉了数据,下面还需要把action也替换。

    3. Input action

    这个过程就是把我们原来在ToDoApp 组件的行为逻辑全部迁移到redux文件夹下的 toDoApp module中去。

    const INPUT_CHANGED = 'INPUT_CHANGED';
    
    export function inputChange(newToDo){
      return {
        type: INPUT_CHANGED,
        newToDo
      }
    }

    然后在reducer的switch中新增如下处理:

    case INPUT_CHANGED:
        return Object.assign(
          {},
          state,
          {newToDo: action.value}
        );

    在 toDoAppContainer.js 的 mapDispatchToProps 函数就需要返回相应的action,首先导入 inputChange, 具体代码如下:

    import { connect } from 'react-redux';
    import ToDoApp from '../components/ToDoApp.js'
    import {
      inputChange
    } from '../redux/modules/toDoApp'; // we added this
    
    function mapStateToProps(state) {
      return {
        toDoApp: state.toDoApp // gives our component access to state through props.toDoApp
      }
    }
    
    function mapDispatchToProps(dispatch) {
      return {
        inputChange: (value) => dispatch(inputChange(value)) // we added this
      };
    }
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(ToDoApp);

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

    4. 其他 actions

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

    总结

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

    展开全文
  • 前段时间学习完了React基础,自己网上找了一些实战项目,了几个感觉项目不是很全面,就想做一个完整的项目来提升自己的React水平.以前学习Vue的时候,就看过bailicangdu大神的vue2-elm项目,所以自己打算用react重写...

    前言

    前段时间学习完了React的基础,自己网上找了一些实战项目,做了几个感觉项目不是很全面,就想做一个完整的项目来提升自己的React水平.以前学习Vue的时候,就看过bailicangdu大神的vue2-elm项目,所以自己打算用react重写它,后端数据还是用vue2-elm,实在没有精力撸后端(感谢bailicangdu大神)
    该项目是饿了吗, 目前开发了登录、注册、购物车、商品展示、用户信息等,算一个比较完整的项目,这个项目比较复杂,这也是我选这个项目的原因

    技术栈

    react4 + react-redux + react-router + es6 + axios + sass + webpack

    项目效果






    说明

    觉得对你有帮助,请到githubStar支持一下,感谢
    bailicangdu大神的项目地址点这里
    推荐一下我的另一个项目基于vue的UI框架(教你从零开始造轮子) 点这里

    演示

    demo

    请用Chrome调试的手机模式查看

    项目结构

    ├── build          ----------------------网页配置
    │   ├── favicon.ico  
    │   └── manifest.json 
    ├── config            ------------------webpack配置
    │   ├── env.js       
    │   ├── jest          
    │   │   ├── cssTransform.js
    │   │   └── fileTransform.js
    │   ├── paths.js
    │   ├── webpack.config.dev.js
    │   ├── webpack.config.prod.js
    │   └── webpackDevServer.config.js
    ├── package-lock.json
    ├── package.json    --------------------项目package.json
    ├── public          --------------------出口
    │   ├── favicon.ico
    │   ├── index.html
    │   └── manifest.json
    ├── scripts        ---------------------运行的脚本
    │   ├── build.js
    │   ├── start.js
    │   └── test.js
    ├── src           ----------------------源码目录
    │   ├── api       ----------------------API目录
    │   │   ├── api.js
    │   │   └── server.js
    │   ├── assets   -----------------------资源目录
    │   │   └── iconfont -------------------iconfont目录
    │   ├── components   -------------------公共组件
    │   │   ├── alert_tip  -----------------提示组件
    │   │   ├── footer   -------------------导航栏组件
    │   │   ├── header  --------------------header组件
    │   │   ├── loader  --------------------加载组件
    │   │   └── shop_list ------------------商店列表组件
    │   ├── config    ----------------------项目一些配置
    │   │   ├── envconfig.js  --------------配置信息
    │   │   └── rem.js  --------------------自适应
    │   ├── index.js    --------------------入口
    │   ├── pages       --------------------页面目录
    │   │   ├── food    --------------------食物页面
    │   │   ├── info   ---------------------个人信息页面
    │   │   ├── login  ---------------------登录页面
    │   │   ├── msite  ---------------------商店页面
    │   │   ├── profile --------------------主页页面
    │   │   ├── set_user -------------------用户信息设置页面
    │   │   ├── shop   ---------------------商店详情页面
    │   │   └── technology  ----------------技术栈页面
    │   ├── router   -----------------------路由
    │   │   └── index.js
    │   ├── serviceWorker.js  --------------热加载
    │   ├── store   ------------------------react-redux状态管理目录
    │   │   ├── store.js
    │   │   └── user
    │   ├── style   ------------------------通用样式目录
    │   │   ├── base.scss
    │   │   ├── mixin.scss
    │   │   └── swiper.min.css
    │   └── utils  ------------------------公用方法
    │       ├── asyncComponent.jsx  -------异步加载组件
    │       └── commons.js  ---------------公用方法
    ├── README.md      ----------------------README
    └── tree.md  --------------------------项目结构
    
    

    项目地址

    项目源码点这里
    欢迎提Issue和, 觉得不错的话欢迎Star

    展开全文
  • 脚手架安装及创建第一个react项目 4.初始化项目介绍 一、react基础介绍 1、参考网址: 英文官网:https://reactjs.org/ 中文官网: https://doc.react-china.org/ 2.介绍描述 用于构建用户界面的 JavaScript 库...

    主要内容

    • 1.基础内容介绍
    • 2.环境搭建
    • 3.脚手架安装及创建第一个react项目
    • 4.初始化项目介绍

    一、react基础介绍

    1、参考网址:

    2.介绍描述 

    • 用于构建用户界面的 JavaScript 库(只关注于View)
    • ReactJS是由Facebook在2013年5月推出的一款JS前端开源框架,推出式主打特点式函数式编程风格。
    • react三大体系
    react三大体系

    3、react的特点

    • Declarative(声明式编码)
    • Component-Based(组件化编码)
    • Learn Once, Write Anywhere(支持客户端与服务器渲染)
    • 高效
    • 单向数据流

    4.react高效的原因

    • 虚拟(virtual)DOM, 不总是直接操作DOM
    • DOM Diff算法, 最小化页面重绘

    5.react与vue的对比

    react与vue异同对比之处:https://blog.csdn.net/HZHJC/article/details/95043142

    • 相同之处:两者均采用虚拟Dom(virtual Dom)- 用Js模拟DOM结构,DOM变化的对比,放在Js层,以提高重绘性能
    • 不同之处:
    react与vue不同之处
    • React.js相对于Vue.js它的灵活性和协作性更好一点;
    • Vue.js有着丰富的API,实现起来更简单快速,所以当团队不大,沟通紧密时,选择Vue,更快速更易用

    6.虚拟DOM

    • React提供了一些API来创建一种 `特别` 的一般js对象
      1. var element = React.createElement('h1', {id:'myTitle'},'hello')
      2. 上面创建的就是一个简单的虚拟DOM对象
    • 虚拟DOM对象最终都会被React转换为真实的DOM
    • 我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界面

    二、搭建react的运行环境

    1、Node.js

    安装Node只需要进入Node网站,进行响应版本的下载,然后进行双击安装就可以了。

    Node中文网址:http://nodejs.cn/ (建议你在这里下载,速度会快很多)

    Node.js 安装好以后,如果是Windows系统,可以使用 Win+R打开运行,然后输入cmd,打开终端(或者叫命令行工具)。

    输入代码:

    node -v 

    如果正确出现版本号,说明Node安装成功了。

    然后再输入代码:

    npm -v

    如果正确出现版本号,说明npm也是没问题的,这时候我们的Node.js安装就算完成了。

    注:npm默认镜像源为国外的,可以自行安装cnpm淘宝镜像源,速度快些

    三、脚手架的安装及创建第一个React项目

    1.脚手架安装

    Node安装好之后,你就可以使用npm命令来安装脚手架工具了,方法很简单,只要打开终端,然后输入下面的命令就可以了。

    npm install -g create-react-app

    create-react-app是React官方出的脚手架工具,其实有很多第三方的脚手架工具,也有很多优秀的。但是作为初学者为了减少踩坑,所以我们使用官方的脚手架。

    2、创建第一个react项目

    脚手架安装好以后,就可以创建项目了,我们在D盘创建一个ReactDemo文件夹,然后进入这个文件夹,创建新的React项目。

    D:  //进入D盘
    mkdir ReactDemo  //创建ReactDemo文件夹
    create-react-app demo01   //用脚手架创建React项目
    cd demo01   //等创建完成后,进入项目目录
    npm start   //预览项目,如果能正常打开,说明项目创建成功

    其实这些操作只需要再终端中输入就可以了。等到浏览器可以打开React网页,并正常显示图标后,说明我们的环境已经全部搭建完成了。

    初始化项目目录

    3.脚手架生成的项目目录介绍

    • README.md :这个文件主要作用就是对项目的说明,已经默认写好了一些东西,你可以简单看看。如果是工作中,你可以把文件中的内容删除,自己来写这个文件,编写这个文件可以使用Markdown的语法来编写。

    • package.json: 这个文件是webpack配置和项目包管理文件,项目中依赖的第三方包(包的版本)和一些常用命令配置都在这个里边进行配置,当然脚手架已经为我们配置了一些了,目前位置,我们不需要改动。如果你对webpack了解,对这个一定也很熟悉。

    • package-lock.json:这个文件用一句话来解释,就是锁定安装时的版本号,并且需要上传到git,以保证其他人再npm install 时大家的依赖能保证一致。

    • gitignore : 这个是git的选择性上传的配置文件,比如一会要介绍的node_modules文件夹,就需要配置不上传。

    • node_modules :这个文件夹就是我们项目的依赖包,到目前位置,脚手架已经都给我们下载好了,你不需要单独安装什么。

    • public :公共文件,里边有公用模板和图标等一些东西。

    • src : 主要代码编写文件,这个文件夹里的文件对我们来说最重要,都需要我们掌握。

    public中文件夹介绍

    这个文件都是一些项目使用的公共文件,也就是说都是共用的

    • favicon.ico : 这个是网站或者说项目的图标,一般在浏览器标签页的左上角显示

    • index.html : 首页的模板文件,我们可以试着改动一下,就能看到结果

    • mainifest.json:移动端配置文件

    src中文件介绍

    这个目录里边放的是我们开放的源代码,我们平时操作做最多的目录。

    • index.js : 这个就是项目的入口文件

    • index.css :这个是index.js里的CSS文件。

    • app.js : 这个文件相当于一个方法模块,也是一个简单的模块化编程。

    • serviceWorker.js: 这个是用于写移动端开发的,PWA必须用到这个文件,有了这个文件,就相当于有了离线浏览的功能。

     四、其它相关基础及虚拟DOM

    1.相关的基础js库

    • react.js: React的核心库
    • react-dom.js: 提供操作DOM的react扩展库
    • babel.min.js: 解析JSX语法代码转为纯JS语法代码的库

    在页面中导入

    编码

    <script type="text/babel"> //必须声明babel
      // 创建虚拟DOM元素
      const vDom = <h1>Hello React</h1> // 千万不要加引号
      // 渲染虚拟DOM到页面真实DOM容器中
      ReactDOM.render(vDom, document.getElementById('test'))
    </script>

    2.虚拟DOM

    1).JSX语法

    • 全称:  JavaScript XML
    • 是react定义的一种类似于XML的JS扩展语法: XML+JS
    • 作用: 用来创建react虚拟DOM(元素)对象
      • var ele = <h1>Hello JSX!</h1>
      • 注意1: 它不是字符串, 也不是HTML/XML标签
      • 注意2: 它最终产生的就是一个JS对象
    • 标签名任意: HTML标签或其它标签
    • 标签属性任意: HTML标签属性或其它
    • 基本语法规则
      • 遇到 <开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
      • 遇到以 { 开头的代码,以JS语法解析: 标签中的js代码必须用{ }包含
    • babel.js的作用
      • 浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行
      • 只要用了JSX,都要加上type="text/babel", 声明需要babel来处理

    2).渲染虚拟DOM

    • 语法:  

    ReactDOM.render(virtualDOM, containerDOM)

    • 作用: 将虚拟DOM元素渲染到页面中的真实容器DOM中显示
    • 参数说明
      1. 参数一: 纯js或jsx创建的虚拟dom对象
      2. 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)

    3)建虚拟DOM的2种方式

       纯JS(一般不用)

    React.createElement('h1',  {id:'myTitle'},  title)

    JSX

    <h1 id='myTitle'>{title}</h1>

    五、模块化和组件化

    1、概念

    模块

    • 理解:就是向外提供特定功能的js程序, 一般就是一个js文件
    • 为什么:  js代码更多更复杂
    • 作用: 复用js, 简化js的编写, 提高js运行效率

    组件

    • 理解: 用来实现特定(局部)功能效果的代码集合(html/css/js)
    • 为什么: 一个界面的功能更复杂
    • 作用: 复用编码, 简化项目编码, 提高运行效率

    组件化和模块化

    模块化:当应用的js都以模块来编写的, 这个应用就是一个模块化的应用

    组件化:当应用是以多组件的方式实现, 这个应用就是一个组件化的应用​​​​​​​

    展开全文
  • React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站, 并于 2013年 5 月开源。React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。这里分享一个react基础入门到项目实战视频,...

    React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站, 并于 2013年 5 月开源。React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。这里分享一个react零基础入门到项目实战视频,感兴趣的同学可以下载下来学习

    第1章课程导学
    第2章React初探
    第3章React基础精讲
    第4章React高级内容
    第5章Redux入门
    第6章Redux进阶
    第7章项目实战:Header组件开发
    第8章 项目实战:首页开发
    第9章 项目实战:详情页面和登录功能开发
    第10章 课程总结

    网盘下载链接:https://pan.baidu.com/s/1SP8q1TiI04B5qpas8NZMvw

    展开全文
  • 主要原因是因为前期我们交接的项目一直使用“”三无框架“”,无测试用例,无充分论证,无人维护,导致我们的项目开发效率,维护效率低;而 react 的组件化,单项数据流,Virtual Dom 及增量跟新,生态圈大,JSX , ...

    主要原因是因为前期我们交接的项目一直使用“”三无框架“”,无测试用例,无充分论证,无人维护,导致我们的项目开发效率,维护效率低; react 的组件化,单项数据流,Virtual Dom 及增量跟新,生态圈大,JSX , Learn Once , Write Everywhere 等特性刚好可以弥补我们项目的劣势.


    在项目中 react 可以通过构建工具,统一状态管理,开发效率提升,路由控制器这四大方面更好的实现功能.
    第一:是构建工具,gulp + webpack,在这主要是要区分开开发和生产环境以及兼容性.
    第二:是统一状态管理,redux ,它最大的优势是集中管理;记录历史,store 中同时保存状态和改变状态的逻辑;便于扩展.
    第三:是开发效率提升,react-hot-loader 搭配 webpack.HotModuleReplacementPlugin使用;
    第四:是路由器控制,react-router 路由表 Single Page Application.


    react 的优化对我们的项目也是很好的提升,
    第一:状态/数据管理优化,新旧数据都是独立的拷贝,对于类似抽奖之类的项目,多个 reducers 处理同一个 action ,一个 reducer 的数据依赖与另一个;
    第二:渲染性能优化,
    第三:首屏性能优化.


    状态管理数据时,
    第一:数据扁平化.
    第二:新旧数据都应该是独立的拷贝.
    第三:多个reducers处理同一个action
    第四: 一个reducer的数据依赖于另外一个,Reselect 若数据不变,能帮助缓存


    渲染时,react 大部份性能问题都归因于重复渲染,如果我在每个 component 的 render 里,放一个console.log(“xxx component”),然后触发一个action,若几乎全部的component都打出这个log,可以直观看出重复渲染,表明重复渲染比较严重
    针对长列表滚动,我们经常出现卡顿的问题,如家校群列表,react 的shouldComponentUpDate生命默认返回 true,表示要求组件更新,若返回 false ,则不更新
    针对其他渲染性能优化:
    方法一:使用 redux-immutable + immutable
    方法二:使用 lodash.merge + non-immutable-pureRender
    针对其他小Tips,
    方法一:慎用setState,因其容易导致重新渲染
    方法二:将方法的bind一律置于constructor
    方法三:只传递component需要的props,避免其它props变化导致重新渲染
    方法四:尽量使用const element


    首屏渲染时,react 对比其它框架并无优势
    在这里插入图片描述
    针对有 cgi 请求,需要吐大量数据的页面,我们采用【同构直出】,它还是有几点值得说明,比改造以前的项目,做直出更容易,减少的是首屏显示时间,而非首屏可交互时间,页面吐出 html 字符串之后,还需要在客户端加载react包,进行事件绑定,做 bigPipe 之类的优化较难,考虑好页面的 PV 与服务器的负载,

    cgi 是什么?web 服务器所处理的内容都是静态的,通过cgi协议,再结合已搭建好的 web 应用程序,如php、jsp、python、perl等,就可以让web server也能"处理"动态请求

    简单版的cgi工作方式
    简单版的cgi工作方式

    同构直出是什么?通常,当客户端请求一个包含React组件页面的时候,服务端首先响应输出这个页面,客户端和服务端有了第一次交互。然后,如果加载组件的过程需要向服务端发出Ajax请求等,客户端和服务端又进行了一次交互,这样,耗时相对较长。
    react 同构直出就是用来解决这个问题的,做到秒开页面,就是服务端可以在页面初次加载时把所有方面渲染好再一次性响应给客户端

    针对没 cgi 拉取,但包较大的,我们采用【拆包】,如家校群
    针对非基础功能,如运营活动,我们采用【轻量化】类 react 方案
    简单总结:
    在这里插入图片描述

    展开全文
  • react项目入门教程

    2016-12-29 10:57:59
    JSX 如果你经常注意React你也许会发现有个东西叫JSX。JSX允许我们在Javascript中写HTML,而不是HTML包含...gulp和grunt为你的预处理任务提供了一个JSX解释器,所以如果你想使用JSX,我建议开启这个功能。 使用
  • 使用 create-react-app 构建一个 react 项目基础架构 包括: react-router4.0 的使用 axios 获取远程api内容 从 flux -&gt; redux -&gt; redux-sage 迁移转变的过程,mobx 本教程并未涉及,有兴趣的...
  • 大家好,我是凯文,本篇文章将介绍React前端框架的环境配置以及项目搭建方法,其中涉及到了node.js(js运行平台)、npm(依赖包管理工具)等内容。网上已经有许多类似的教程,这篇文章可以给各位个参考,同时给我...
  • 蓝鸥React Native零基础入门到项目实战 Hello React
  • npx create-react-app ./ 安装结束,yarn start 测试项目是否能正常运行 2 分析项目 --重要 1、 哪些公共组件需要封装处理 2、要用到哪些技术,处理css什么(styled-components,css,scss等)ui库什么(ant-...
  • 最近有打算仿vue-admin项目构造一个react项目,不引用官方脚手架,从webpack开始配置一套基于react,redux,typescript的项目,并实时记录一下项目中的一些配置情况 首先搭建一个基本的webpack环境 webpack项目搭建...
  • React Native 快速搭建商城项目基础结构帖子附件代码,下载此代码配合帖子流程,可以在搭建React Native 商城项目或者其他项目搭建的时候快速建成!
  • 【学习路线】蓝鸥React Native零基础入门到项目实战
  • react基础项目创建

    2020-04-02 23:28:31
    1.配置react环境 ...项目名自定义,这样一个简单的react项目就创建好了 3.启动项目 启动项目前路径先切换到项目中 例如:cd myapp, 然后输入启动指令:npm run start 4.在浏览器中输入http://localhost:3000/,...
  • 最近跟着教程,尝试用React做一个SPA项目。记录一下大致的流程。 1. 搭建react开发环境 使用 react-create-app 脚手架,搭建react开发环境。默认已装好 npm 和 nodejs,终端输入: > npm install -g react-...
  • 这是一个使用react构建的渐进式学习项目项目地址:github.com/Ljhhhhhh/re…,欢迎您的交流学习,希望各位大佬能多给意见。非常感谢。 项目版本技术栈 基础版本(version1分支) 基础版本的技术栈为 React+react-...
  • React 创建项目基础

    2018-07-19 23:54:53
    如何创建一个react项目 npm install -g create-react-app create-react-app my-app cd my-app npm start 关于webpack的一些基本命令和配置 首先我们需要初始化项目,npm init生成package.json文件 利用webpack...
  • 仿简书react项目

    2020-06-23 21:09:18
    仿简书react项目 本项目地址仿照简书网站,也是我使用react的第一个完整项目。对有一定react基础的同学非常友好,非常适合入门学习。零基础的同学可以参考我的另外一篇文章react_demo。可以通过git log,查看我的...
  • 移动端应用高速发展, 本教程便是使用ReactNative开发的高性能基于原生应用跨Android与iOS两大平台的大型综合App。 本实战项目使用react native 开发招聘,房产,点餐,商城,二手车,本地商务的大型综合门户性...
  • 第1章 介绍课程目标和学习内容包括...1-1 课程导学第2章 知识储备2-1 介绍React开发环境2-2 ES6常用语法2-3 express+mongodb基础2-4 express+mongodb基础第3章 React基础知识回顾3-1 React基础知识回顾1-入门例子3-2...
1 2 3 4 5 ... 20
收藏数 32,350
精华内容 12,940
关键字:

想用react做项目的基础