一个完整的react网站代码_完整react前台框架代码 - CSDN
  • 本博文提供一个单网页结构网页(SPA)使用React Router路由控制跳转的完整例子。 关于配置可以查看我之前的一篇博客:[一步一步进入React的世界(React+Webpack+ES6组合配置)]

    本博文提供一个单网页结构网页(SPA)使用React Router路由控制跳转的完整例子。

    可以在我的github 中clone或者fork
    https://github.com/zrysmt/react-demo/tree/master/demo03

    关于配置可以查看我之前的一篇博客:一步一步进入React的世界(React+Webpack+ES6组合配置)

    1.整个目录结构


    - build是编译后的文件夹
    - src 放入源码
    + components组件
    + global 通用组件和SCSS
    + … 分模块
    + app.js入口
    - index.html

    2.源码

    关于源码可以在开头给出的github中找到详细的完整例子,这里就介绍重要的几个文件源码
    记住要安装react-router

    npm i react-router -S

    2.1 index.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>Our Home,Our Heart</title>
        <meta name="viewport" content="width=device-width,initial-scale = 1.0,user-scalable=no">
    </head>
    
    <body>
        <div id="content">
        </div>
        <script src="build/bundle.js"></script>
    </body>
    </html>

    2.2 入口文件app.js

    关于react router的基础知识我们可以参考阮一峰老师的博客作为入门指导。

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {Router,Route,IndexRoute,hashHistory} from 'react-router';
    
    import './components/global/global.scss';
    
    import Nav from './components/global/menu';
    import Home from './components/home/home';
    import Story from './components/story/story';
    
    
    class App extends React.Component{
        render(){
            return(
                <div>   
                    <Nav/>
                    {this.props.children}
                </div>              
            )
        }
    }
    
    ReactDOM.render((
        <Router history={hashHistory}>
            <Route path="/" component={App}>
                <IndexRoute component={Home}/>
                <Route path="/Story" component={Story}/>
            </Route>
        </Router>
        ),document.body
    );

    简单解释下:
    组件App除了包含Nav组件,还应该包括主体内容
    当使用index.html访问的时候,是在项目根目录下,这样会先加载APP组件,APP组件包含{this.props.children},便会加载<IndexRoute/>里面定义的组件Home。用户访问’/’相当于:

    <App>
      <Nav/>
      <Home/>
    </App>

    2.3 Nav组件

    /components/global/menuLi.jsx
    /components/global/menu.jsx

    • 最小一块组件menuLi.jsx
    import React from 'react';
    import {Link} from 'react-router';
    class MenuLi extends React.Component{
        render(){
            let linkTo = this.props.name =="Home"?"/":"/"+this.props.name;
            return (
                <li>
                    <Link to={linkTo}>
                        {this.props.name}
                    </Link>
                </li>
            );
        }
    }
    export default MenuLi;

    Link组件用于取代<a>元素,生成一个链接,允许用户点击后跳转到另一个路由
    - Nav组件 menu.jsx

    import React from 'react';
    import ReactDOM from 'react-dom';
    import MenuLi from './menuLi';
    import './menu.scss';
    
    let menuLis = ["Home","Story","Travel","TimeLine","Future"];
    class MenuUl extends React.Component{
        render(){
            return(
                <ul>
                {
                    menuLis.map(function(menuLi) {
                        return <MenuLi name={menuLi}/>
                    })
                }
                </ul>
            );  
        }
    } 
    class Nav extends React.Component{
        render(){
            return(
                <nav>
                    <div id="menu">
                        <MenuUl/>           
                    </div>
                </nav>
            )
        }
    }
    export default Nav;

    2.4 Home组件

    /components/home/home.jsx,示例比较简单

    import React from 'react';
    import ReactDOM from 'react-dom';
    import "./home.scss";
    class Home extends React.Component{
        render(){
            return (
                <h5>这是home</h5>
            );
        }
    }
    export default Home;

    2.5 Story组件

    import React from 'react';
    import ReactDOM from 'react-dom';
    import "./story.scss";
    
    class Story extends React.Component{
        render(){
            return (
                <h5>这是story</h5>
            );
        }
    }
    export default Story;

    其余几个组件不一一列出了

    可以在我的github 中clone或者fork,查看完整的例子代码

    参考阅读:

    展开全文
  • gitclone地址:...大家装好react-native环境后,把这demo下载好,使用yarn安装依赖(npm不可以正常安装) 然后运行react-nativerun-android,等待大概10分钟,即可看到demo效果。 ...

    git clone地址:https://github.com/ywltoread/react-navigation-redux.git

    大家装好react-native环境后,把这个demo下载好,使用yarn安装依赖(npm不可以正常安装)

    然后运行react-native run-android,等待大概10分钟,即可看到demo效果。

     

    展开全文
  • 目录说明 │ .babelrc #babel配置文件 │ package-lock.json │ package.json │ README.MD │ webpack.config.js #webpack生产配置文件 │ webpack.dev.config.js #webpack...

    目录说明
    │ .babelrc #babel配置文件
    │ package-lock.json
    │ package.json
    README.MD
    │ webpack.config.js #webpack生产配置文件
    │ webpack.dev.config.js #webpack开发配置文件

    ├─dist
    ├─public #公共资源文件
    └─src #项目源码
    │ index.html #index.html模板
    │ index.js #入口文件

    ├─component #组建库
    │ └─Hello
    │ Hello.js

    ├─pages #页面目录
    │ ├─Counter
    │ │ Counter.js
    │ │
    │ ├─Home
    │ │ Home.js
    │ │
    │ ├─Page1
    │ │ │ Page1.css #页面样式
    │ │ │ Page1.js
    │ │ │
    │ │ └─images #页面图片
    │ │ brickpsert.jpg
    │ │
    │ └─UserInfo
    │ UserInfo.js

    ├─redux
    │ │ reducers.js
    │ │ store.js
    │ │
    │ ├─actions
    │ │ counter.js
    │ │ userInfo.js
    │ │
    │ ├─middleware
    │ │ promiseMiddleware.js
    │ │
    │ └─reducers
    │ counter.js
    │ userInfo.js

    └─router #路由文件
    Bundle.js
    router.js

    快速搭建一个react项目:
    下载react脚手架 :

    C:\Users\Administrator> npm install -g creat-react-app
    C:\Users\Administrator> create-react-app my-app
    (也可进入指定文件目录下)

    在这里插入图片描述
    在这里插入图片描述
    这是简单的基础项目,他的核心:react / react-dom 两个框架
    特点:
    1.生成项目后,脚手架为了“优雅”,隐藏了所有的webpack相关的配置文件,此时查看myapp文件夹目录,会发现找不到任何webpack配置文件;这也就导致了,如果我们需要在webpack中安装一些自己的loder或者plugin变的很困难;
    2.create-react-app自动生成的webpack中集成了:eslint(代码检测)、url-loader(图片BASE64 [小于10000kb的图片])、babel-loader(ES6和JSX语法解析)、style-loader、css-loader(CSS代码解析)、HtmlWebpackPlugin(产出HTML插件)等内容
    3.仅仅是安装了react中最常用的 react / react-dom 组件,其余的并没有安装,所以在项目开发中,我们根据需要,可能还会安装:$ yarn add redux react-redux react-router-dom prop-types 等等

    完整的基础项目除了react / react-dom 两个框架,还包含
    redux
    react-redux
    react-router
    sass /less
    style-component
    history
    fetch
    webpack-dev-server

    项目所用到的插件及作用:
    redux: 帮你解决state变化和异步, state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。
    1.单一数据源
    整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
    2.State 是只读的
    唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
    3.使用纯函数来执行修改
    为了描述 action 如何改变 state tree ,你需要编写 reducers。
    Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。

    react-redux : Redux 的作者封装了一个 React 专用的库 React-Redux,React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。
    1.UI 组件有以下几个特征。

    只负责 UI 的呈现,不带有任何业务逻辑
    没有状态(即不使用this.state这个变量)
    所有数据都由参数(this.props)提供
    不使用任何 Redux 的 API
    下面就是一个 UI 组件的例子。

    const Title =  value => <h1>{value}</h1>;
    

    因为不含有状态,UI 组件又称为"纯组件",即它纯函数一样,纯粹由参数决定它的值.

    2.容器组件的特征恰恰相反。
    负责管理数据和业务逻辑,不负责 UI 的呈现
    带有内部状态
    使用 Redux 的 API

    3.connect()
    React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。

    import { connect } from 'react-redux'
    const VisibleTodoList = connect()(TodoList);
    

    上面代码中,TodoList是 UI 组件,VisibleTodoList就是由 React-Redux 通过connect方法自动生成的容器组件。

    但是,因为没有定义业务逻辑,上面这个容器组件毫无意义,只是 UI 组件的一个单纯的包装层。为了定义业务逻辑,需要给出下面两方面的信息。

    (1)输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数

    (2)输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。

    因此,connect方法的完整 API 如下。

    import { connect } from 'react-redux'
    const VisibleTodoList = connect(
      mapStateToProps,
      mapDispatchToProps
    )(TodoList)
    

    上面代码中,connect方法接受两个参数:mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。

    React-Router
    基本用法
    1 路由定义(摘要: Router, Route)
    在一个路由系统中,我们需要定义好路由表。我们需要用Router组件包含我们的路由表,通过Route来声明单个的路由。同时,每个路由应该与它所属的组件一一对应

    render((
      <Router >//开始创建路由表
        <Route path="/" component={App}>//声明每一个路由
          <Route path="/about" component={About}/>
          <Route path="users" component={Users}>//每个路由
            <Route path=":id" component={User}/>//对应一个Component
          </Route>
        </Route>
      </Router>
    ), document.getElementById('example'))
    
    //其他定义路由表的方法
    
    import routes from './config/routes'
    render(<Router history={browserHistory} routes={routes}/>, document.getElementById('example'))
    

    2 路由嵌套(摘要: IndexRouter, this.props.children)
    如何实现路由的嵌套呢,与Angular路由一样,路由的嵌套需要两个条件。一、在创建路由表的时候就声明好嵌套的规则(ng中的stateProvider)view(nguiview)使IndexRoute(ngstateProvider);二、需要有一个view来安放所要嵌套的子页面(ng中的ui-view)。其中,我们还可以在嵌套层的首个路由声明中,使用IndexRoute来声明该路由下的默认路由(类似于ng中urlProvider.otherwise)

    //有嵌套与默认页面的路由表
    render((
      <Router >
        <Route path="/" component={App}>
          <IndexRoute component={Index}/>//设置默认页面
          <Route path="/about" component={About}/>
          <Route path="users" component={Users}>
            <IndexRoute component={UsersIndex}/>//设置默认页面
            <Route path=":id" component={User}/>
          </Route>
        </Route>
      </Router>
    ), document.getElementById('example'))
    
    //一个用于安放子页(子路由)的view
    class Users extends React.Component {
      render() {
        return (
          <div>
            <h2>Users</h2>
            {this.props.children}//此处相当于<ui-view>
          </div>
        )
      }
    }
    

    3 路由跳转(摘要: Link,to)
    我们需要一个Link组件帮助我们实现路由的的跳转

    <li><Link to="/users" >/users</Link></li>
    

    ,最终Link组件会被渲染为标签。to属性是我们所要跳转的路由pathname(类似于ng中的ui-sref / href)
    3 . 1 父子路由的参数穿透传递(摘要: to = {/xx/${xxx}/} )
    若父路由中包含不确定参数,而我们又想把该参数往下级传递,这时候我们需要这样子做
    //路由配置

      <Route path="user/:userID" component={User}>
        <Route path="tasks/:taskID" component={Task} />
        <Redirect from="todos/:taskID" to="tasks/:taskID" />
      </Route>
    //子级路由
    <li><Link to={`/user/${userID}/tasks/foo`} activeClassName="active">foo task</Link></li>
    

    3 . 2 带参数的路由跳转

    <li><Link      to={{ pathname: '/users/ryan', query: { foo: 'bar' } }} activeStyle={ACTIVE}>/users/ryan?foo=bar</Link></li>
    

    3 . 3 函数内跳转(摘要: this.context.router.push(’/’))
    this.context.router.push(’/’) ,注:这个写法会把跳转载入浏览器历史,若不想留下历史记录则可以 this.context.router.replace(’/’)

    带有动画效果的路由切换(摘要: ReactCSSTransitionGroup)
    当我们需要在路由切换时带有一定的动画效果时,我们便需要 react-addons-css-transition-group 这个插件了。使用ReactCSSTransitionGroup组件来包含我们需要呈现动画效果的view

    class App extends Component {
      render() {
        return (
          <div>
            <ul>
              <li><Link to="/page1">Page 1</Link></li>
              <li><Link to="/page2">Page 2</Link></li>
            </ul>
    
            <ReactCSSTransitionGroup component="div"  transitionName="example"  transitionEnterTimeout={500}  transitionLeaveTimeout={500}>
              {React.cloneElement(this.props.children, {
                key: this.props.location.pathname
              })}
            </ReactCSSTransitionGroup>
               //克隆所有子节点,单独的{this.props.children}没有动画效果
          </div>
        )
      }
    }
    

    路由的权限控制(摘要: onEnter、context.router)
    单页应用路由的权限控制的基本思路是:监听路由的改变,每当路由将要发生改变,我们就使用一个中间服务(该服务介于上一级路由和将要到达路由之间启动),来判断我们是否有进入这个路由的权限,有的话直接进入,没有的话就redirect。在React中,为某个路由进行权限监听的方式是onEnter ,该onEnter属性对应连着一个具有判断权限的中间服务。我们通过上一级路由来启动这个服务。假设我们需要从’/form’到’/page’之间做一个判断,在’/form’中填写特定字段后才能成功跳转,否则redirect到’/error’

    //form
    const Form = createClass({
      //省略部分代码
      submitAction(event) {
        event.preventDefault();
         //通过context传输数据
        //通过url的query字段传输数据
        //也可以通过制定其他服务来传输数据
        this.context.router.push({
          pathname: '/page',
          query: {
            qsparam: this.state.value
          }
        })
      },
      render() {
        return (
          <form onSubmit={this.submitAction}>
            //省略部分代码
            <button type="submit">Submit </button>
          </form>
        )
      }
    })
    
    //路由权限控制
    <Route path="page" component={Page} onEnter={requireCredentials}/>
    
    //权限控制的中间服务
    function requireCredentials(nextState, replace, next) {
      //获取传输过来的数据
      if (query.qsparam) {
        serverAuth(query.qsparam)
        .then(
          () => next(),//成功,通过next()成功跳转
          () => {
            replace('/error')//重定向
            next()
          }
        )
      } else {
        replace('/error')
        next()
      }
    }
    

    其中,onEnter所指向的函数是 type EnterHook = (nextState: RouterState, replace: RedirectFunction, callback?: Function) => any; 其中,nextState作为第一个参数,其所带的信息有如下:

    type RouterState = {
      location: Location;
      routes: Array<Route>;
      params: Params;
      components: Array<Component>;
    };
    

    其中,replace函数一旦被使用到,则在函数内部跳转到一个新url,返回时也要带上必要的信息,如下

    type RedirectFunction = (state: ?LocationState, pathname: Pathname | Path, query: ?Query) => void;
    

    路由离开确认(摘要: componentWillMount, this.context.router.setRouteLeaveHook) 
    若我们需要在路由切换,在离开当前页面的时候做一些确认工作,我们可以通过setRouteLeaveHook函数,为离开前执行一些操作

    //Component内部
      componentWillMount() {
        this.context.router.setRouteLeaveHook(
          this.props.route,
          this.routerWillLeave
        )
      }
      
      routerWillLeave() {
        if (xxx)
         //...
      },
    

    根据路由按需加载组件
    按需加载在单页应用中的好处不言而喻,按业务模块切分代码能使首次加载资源所需要的时间大大降低,能在一定程度上增强用户体验。但首先我们需要整理一下我们的项目结构(此demo是按路由切分的,另外还能按业务模块进行切分)
    项目结构
    在这里插入图片描述

    `路由表配置(app.js)
    在这里插入图片描述

    对应组件的加载配置(routes/hello/index.js和routes/test/index.js)
    在这里插入图片描述

    路由组件的属性(摘要: this.props)

    在这里插入图片描述

    路由Location属性

    type Location = {
      pathname: Pathname;
      search: QueryString;
      query: Query;
      state: LocationState;
      action: Action;
      key: LocationKey;
    };
    

    styled-component
    样式化组件,主要作用是它可以编写实际的CSS代码来设计组件样式,也不需要组件和样式之间的映射,即创建后就是一个正常的React 组件,并且可以附加样式给当前组件。
    几种常用的样式化组件方法:
    1.njectGlobal # 编写全局CSS的辅助方法。它不返回组件,而是直接将样式添加到样式表中
    这个跟我们平时在写html页面,会先把一些需要重置浏览器的样式加到页面上的做法类似,主要作用是:重置样式及书写全局可共用的样式

    import { injectGlobal } from 'styled-components';
    
    injectGlobal`
      @font-face {
        font-family: 'Operator Mono';
        src: url('../fonts/Operator-Mono.ttf');
      }
    
      body {
        margin: 0;
      }
    `;
    

    StyledComponent # 样式化组件声明方式:styled.tagname、styled(Component) 两种方式
    第一种直接通过styled点一个元素标签,将button元素转化成样式化组件
    第二种是重写样式化组件的部分样式,比如TomatoButton

    另外介绍两种方法
    .extend:创建一个新的StyledComponent并且继承它的规则
    如:TomatoButton继承了Button的样式规则,并使用一些与颜色相关的样式进行扩展(其实就是覆盖了被继承组件的某些样式)。

    const TomatoButton = Button.extend`    
      color: tomato;
      border-color: tomato;
    `;
    

    .withComponent 创建一个新的StyledComponent,并应用于其他的标签或组件,且使用相同的样式
    如:用标签替换标签,但还是使用相同的样式,相当于有的样式标签一样都有

    const Link = Button.withComponent('a')
    

    History
    history 一个管理js应用session会话历史的js库。它将不同环境(浏览器,node…)的变量统一成了一个简易的API来管理历史堆栈、导航、确认跳转、以及sessions间的持续状态。
    location
    你也可以使用 history对象来的方法来改变当前的location:
    一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location 对象, 然后 router 使用它匹配到路由,最后正确地渲染对应的组件。
    location对象包括:
    pathname 同window.location.pathname
    search 同window.location.search
    state 一个捆绑在这个地址上的object对象
    action PUSH, REPLACE, 或者 POP中的一个
    key 唯一ID

    常用的三种history
    hashHistory
    不需要服务器配置,在URL生成一个哈希来跟踪状态,通常在测试环境使用,也可以作为发布环境使用。

    ReactDOM.render((<Provider store={store}><Router history={hashHistory}><Route>//你的route</Route></Router></Provider>),document.getElementById('root'));
    import { Provider } from 'react-redux'
    import { Router, hashHistory} from 'react-router'
    

    browserHistory
    需要服务器端做配置,路径是真实的URL,是官方推荐首选。
    客户端配置

    ReactDOM.render((<Provider store={store}><Router history={browserHistory}><Route>//你的route</Route></Router></Provider>),document.getElementById('root'));
    import { Provider } from 'react-redux'
    import { Router, browserHistory } from 'react-router'
    

    服务端配置

    // 通常用于加载静态资源app.use(express.static(__dirname + '/public'))
    // 在你应用 JavaScript 文件中包含了一个 script 标签// 的 index.html 中处理任何一个 routeapp.get('*', function (request, response){response.sendFile(path.resolve(__dirname, 'public', 'index.html'))})
    app.listen(port)console.log("server started on port " + port)
    const express = require('express')
    const path = require('path')
    const port = process.env.PORT || 8080
    const app = express()
    

    为什么browserHistory需要服务端配置
    因为真实URL其实是指向服务器资源,比如我们经常使用的API接口,也是一个真实URL的资源路径,当通过真实URL访问网站的时候,第一次访问的是网站的域名,这个时候可以正常加载我们的网站js等文件,而用户手动刷新网页时,由于路径是指向服务器的真实路径,服务器端没有做路由配置,就会导致资源不存在,用户访问的资源不存在,返回给用户的是404错误。
    通过hashHistory来生成的URL就不会出现这样的问题,因为他不是指向真实的路由。

    Fetch
    被称为下一代Ajax技术,采用Promise方式来处理数据。
    Fetch的核心是HTTP Requests,Responses Headers和Bodypayload 的接口抽象,以及一个用于启动异步资源请求的全局fetch方法。

    上传JSON数据
    使用fetch()开机自检JSON编码的数据。

    var url = 'https://example.com/profile';
    var data = {username: 'example'};
    
    fetch(url, {
      method: 'POST', // or 'PUT'
      body: JSON.stringify(data), 
      headers: new Headers({
        'Content-Type': 'application/json'
      })
    }).then(res => res.json())
    .catch(error => console.error('Error:', error))
    .then(response => console.log('Success:', response));
    

    传文件
    可以使用 HTML

    <input type="file"/> 
    

    input 元素、FormData () 和fetch()来上载文件。

    var formData = new FormData();
    var fileField = document.querySelector("input[type='file']");
    
    formData.append('username', 'abc123');
    formData.append('avatar', fileField.files[0]);
    
    fetch('https://example.com/profile/avatar', {
      method: 'PUT',
      body: formData
    })
    .then(response => response.json())
    .catch(error => console.error('Error:', error))
    .then(response => console.log('Success:', response));
    

    提供您自己的请求对象
    您可以使用Request()构造函数创建请求对象,并将其作为fetch()方法参数传入,而不是将要请求的资源的路径传递到 fetch () 调用中。

    var myHeaders = new Headers();
    var myInit = { method: 'GET',
                   headers: myHeaders,
                   mode: 'cors',
                   cache: 'default' };
    
    var myRequest = new Request('flowers.jpg', myInit);
    
    fetch(myRequest).then(function(response) {
      return response.blob();
    }).then(function(myBlob) {
      var objectURL = URL.createObjectURL(myBlob);
      myImage.src = objectURL;
    });
    
    展开全文
  • 本文从零开始,逐步讲解如何用react全家桶搭建一个完整react项目。文中针对react、webpack、babel、react-route、redux、redux-saga的核心配置会加以讲解,通过这个项目,可以系统的了解react技术栈的主要知识,...

    react全家桶从0到1(最新)

    本文从零开始,逐步讲解如何用react全家桶搭建一个完整的react项目。文中针对react、webpack、babel、react-route、redux、redux-saga的核心配置会加以讲解,通过这个项目,可以系统的了解react技术栈的主要知识,避免搭建一次后面就忘记的情况。

    代码库:https://github.com/teapot-py/react-demo

    首先关于主要的npm包版本列一下:

    1. react@16.7.0
    2. webpack@4.28.4
    3. babel@7+
    4. react-router@4.3.1
    5. redux@4+

    从webpack开始

    思考一下webpack到底做了什么事情?其实简单来说,就是从入口文件开始,不断寻找依赖,同时为了解析各种不同的文件加载相应的loader,最后生成我们希望的类型的目标文件。

    这个过程就像是在一个迷宫里寻宝,我们从入口进入,同时我们也会不断的接收到下一处宝藏的提示信息,我们对信息进行解码,而解码的时候可能需要一些工具,比如说钥匙,而loader就像是这样的钥匙,然后得到我们可以识别的内容。

    回到我们的项目,首先进行项目的初始化,分别执行如下命令

    mkdir react-demo // 新建项目文件夹
    cd react-demo // cd到项目目录下
    npm init // npm初始化
    

    引入webpack

    npm i webpack --save
    touch webpack.config.js
    

    对webpack进行简单配置,更新webpack.config.js

    const path = require('path');
    
    module.exports = {
      entry: './app.js', // 入口文件
      output: {
        path: path.resolve(__dirname, 'dist'), // 定义输出目录
        filename: 'my-first-webpack.bundle.js'  // 定义输出文件名称
      }
    };
    
    

    更新package.json文件,在scripts中添加webpack执行命令

    "scripts": {
      "dev": "./node_modules/.bin/webpack --config webpack.config.js"
    }
    

    如果有报错请按提示安装webpack-cli

    npm i webpack-cli
    

    执行webpack

    npm run dev
    

    如果在项目文件夹下生成了dist文件,说明我们的配置是没有问题的。

    接入react

    安装react相关包

    npm install react react-dom --save
    

    更新app.js入口文件

    import React from 'react
    import ReactDom from 'react-dom';
    import App from './src/views/App';
    
    ReactDom.render(<App />, document.getElementById('root'));
    

    创建目录 src/views/App,在App目录下,新建index.js文件作为App组件,index.js文件内容如下:

    import React from 'react';
    
    class App extends React.Component {
    
        constructor(props) {
            super(props);
        }
    
        render() {
            return (<div>App Container</div>);
        }
    }
    export default App;
    

    在根目录下创建模板文件index.html

    <!DOCTYPE html>
    <html>
    <head>
        <title>index</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    </head>
    <body>
        <div id="root"></div>
    </body>
    </html>
    

    到了这一步其实关于react的引入就OK了,不过目前还有很多问题没有解决

    1. 如何解析JS文件的代码?
    2. 如何将js文件加入模板文件中?

    Babel解析js文件

    Babel是一个工具链,主要用于在旧的浏览器或环境中将ECMAScript2015+的代码转换为向后兼容版本的JavaScript代码。

    安装babel-loader,@babel/core,@babel/preset-env,@babel/preset-react

    npm i babel-loader@8 @babel/core @babel/preset-env @babel/preset-react -D
    
    1. babel-loader:使用Babel转换JavaScript依赖关系的Webpack加载器, 简单来讲就是webpack和babel中间层,允许webpack在遇到js文件时用bable来解析
    2. @babel/core:即babel-core,将ES6代码转换为ES5。7.0之后,包名升级为@babel/core。@babel相当于一种官方标记,和以前大家随便起名形成区别。
    3. @babel/preset-env:即babel-preset-env,根据您要支持的浏览器,决定使用哪些transformations / plugins 和 polyfills,例如为旧浏览器提供现代浏览器的新特性。
    4. @babel/preset-react:即 babel-preset-react,针对所有React插件的Babel预设,例如将JSX转换为函数.

    更新webpack.config.js

     module: {
        rules: [
          {
            test: /\.js$/, // 匹配.js文件
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader'
            }
          }
        ]
      }
    

    根目录下创建并配置.babelrc文件

    {
      "presets": ["@babel/preset-env", "@babel/preset-react"]
    }
    

    配置HtmlWebPackPlugin

    这个插件最主要的作用是将js代码通过

    npm i html-webpack-plugin -D
    

    webpack新增HtmlWebPackPlugin配置

    至此,我们看一下webpack.config.js文件的完整结构

    const path = require('path');
    
    const HtmlWebPackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './app.js',
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'my-first-webpack.bundle.js'
      },
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader'
            }
          }
        ]
      },
      plugins: [
        new HtmlWebPackPlugin({
          template: './index.html',
          filename: path.resolve(__dirname, 'dist/index.html')
        })
      ]
    };
    

    执行 npm run start,生成 dist文件夹

    当前目录结构如下
    目录结构

    可以看到在dist文件加下生成了index.html文件,我们在浏览器中打开文件即可看到App组件内容。

    配置 webpack-dev-server

    webpack-dev-server可以极大的提高我们的开发效率,通过监听文件变化,自动更新页面

    安装 webpack-dev-server 作为 dev 依赖项

    npm i webpack-dev-server -D
    

    更新package.json的启动脚本

    “dev": "webpack-dev-server --config webpack.config.js --open"
    

    webpack.config.js新增devServer配置

    devServer: {
      hot: true, // 热替换
      contentBase: path.join(__dirname, 'dist'), // server文件的根目录
      compress: true, // 开启gzip
      port: 8080, // 端口
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin(), // HMR允许在运行时更新各种模块,而无需进行完全刷新
      new HtmlWebPackPlugin({
        template: './index.html',
        filename: path.resolve(__dirname, 'dist/index.html')
      })
    ]
    

    引入redux

    redux是用于前端数据管理的包,避免因项目过大前端数据无法管理的问题,同时通过单项数据流管理前端的数据状态。

    创建多个目录

    1. 新建src/actions目录,用于创建action函数
    2. 新建src/reducers目录,用于创建reducers
    3. 新建src/store目录,用于创建store

    下面我们来通过redux实现一个计数器的功能

    安装依赖

    npm i redux react-redux -D
    

    在actions文件夹下创建index.js文件

    export const increment = () => {
      return {
        type: 'INCREMENT',
      };
    };
    
    

    在reducers文件夹下创建index.js文件

    const initialState = {
      number: 0
    };
    
    const incrementReducer = (state = initialState, action) => {
      switch(action.type) {
        case 'INCREMENT': {
          state.number += 1
          return { ...state }
          break
        };
        default: return state;
      }
    };
    export default incrementReducer;
    

    更新store.js

    import { createStore } from 'redux';
    import incrementReducer from './reducers/index';
    
    const store = createStore(incrementReducer);
    
    export default store;
    
    

    更新入口文件app.js

    import App from './src/views/App';
    import ReactDom from 'react-dom';
    import React from 'react';
    import store from './src/store';
    import { Provider } from 'react-redux';
    
    ReactDom.render(
        <Provider store={store}>
            <App />
        </Provider>
    , document.getElementById('root'));
    

    更新App组件

    import React from 'react';
    import { connect } from 'react-redux';
    import { increment } from '../../actions/index';
    
    class App extends React.Component {
    
        constructor(props) {
            super(props);
        }
    
        onClick() {
            this.props.dispatch(increment())
        }
    
        render() {
            return (
                <div>
                    <div>current number: {this.props.number} <button onClick={()=>this.onClick()}>点击+1</button></div>
    
                </div>
            );
        }
    }
    export default connect(
        state => ({
            number: state.number
        })
    )(App);
    

    点击旁边的数字会不断地+1

    引入redux-saga

    redux-saga通过监听action来执行有副作用的task,以保持action的简洁性。引入了sagas的机制和generator的特性,让redux-saga非常方便地处理复杂异步问题。
    redux-saga的原理其实说起来也很简单,通过劫持异步action,在redux-saga中进行异步操作,异步结束后将结果传给另外的action。

    下面就接着我们计数器的例子,来实现一个异步的+1操作。

    安装依赖包

    npm i redux-saga -D
    

    新建src/sagas/index.js文件

    import { delay } from 'redux-saga'
    import { put, takeEvery } from 'redux-saga/effects'
    
    export function* incrementAsync() {
      yield delay(2000)
      yield put({ type: 'INCREMENT' })
    }
    
    export function* watchIncrementAsync() {
      yield takeEvery('INCREMENT_ASYNC', incrementAsync)
    }
    

    解释下所做的事情,将watchIncrementAsync理解为一个saga,在这个saga中监听了名为INCREMENT_ASYNC的action,当INCREMENT_ASYNC被dispatch时,会调用incrementAsync方法,在该方法中做了异步操作,然后将结果传给名为INCREMENT的action进而更新store。

    更新store.js

    在store中加入redux-saga中间件

    import { createStore, applyMiddleware } from 'redux';
    import incrementReducer from './reducers/index';
    import createSagaMiddleware from 'redux-saga'
    import { watchIncrementAsync } from './sagas/index'
    
    const sagaMiddleware = createSagaMiddleware()
    const store = createStore(incrementReducer, applyMiddleware(sagaMiddleware));
    sagaMiddleware.run(watchIncrementAsync)
    export default store;
    

    更新App组件

    在页面中新增异步提交按钮,观察异步结果

    import React from 'react';
    import { connect } from 'react-redux';
    import { increment } from '../../actions/index';
    
    class App extends React.Component {
    
        constructor(props) {
            super(props);
        }
    
        onClick() {
            this.props.dispatch(increment())
        }
    
        onClick2() {
            this.props.dispatch({ type: 'INCREMENT_ASYNC' })
        }
    
        render() {
            return (
                <div>
                    <div>current number: {this.props.number} <button onClick={()=>this.onClick()}>点击+1</button></div>
                    <div>current number: {this.props.number} <button onClick={()=>this.onClick2()}>点击2秒后+1</button></div>
                </div>
            );
        }
    }
    export default connect(
        state => ({
            number: state.number
        })
    )(App);
    

    观察结果我们会发现如下报错:

    这是因为在redux-saga中用到了Generator函数,以我们目前的babel配置来说并不支持解析generator,需要安装@babel/plugin-transform-runtime

    npm install --save-dev @babel/plugin-transform-runtime
    

    这里关于babel-polyfill、和transfor-runtime做进一步解释

    babel-polyfill

    Babel默认只转换新的JavaScript语法,而不转换新的API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转译。如果想使用这些新的对象和方法,必须使用 babel-polyfill,为当前环境提供一个垫片。

    babel-runtime

    Babel转译后的代码要实现源代码同样的功能需要借助一些帮助函数,而这些帮助函数可能会重复出现在一些模块里,导致编译后的代码体积变大。
    Babel 为了解决这个问题,提供了单独的包babel-runtime供编译模块复用工具函数。
    在没有使用babel-runtime之前,库和工具包一般不会直接引入 polyfill。否则像Promise这样的全局对象会污染全局命名空间,这就要求库的使用者自己提供 polyfill。这些 polyfill一般在库和工具的使用说明中会提到,比如很多库都会有要求提供 es5的polyfill。
    在使用babel-runtime后,库和工具只要在 package.json中增加依赖babel-runtime,交给babel-runtime去引入 polyfill 就行了;
    详细解释可以参考

    babel presets 和 plugins的区别

    Babel插件一般尽可能拆成小的力度,开发者可以按需引进。比如对ES6转ES5的功能,Babel官方拆成了20+个插件。
    这样的好处显而易见,既提高了性能,也提高了扩展性。比如开发者想要体验ES6的箭头函数特性,那他只需要引入transform-es2015-arrow-functions插件就可以,而不是加载ES6全家桶。
    但很多时候,逐个插件引入的效率比较低下。比如在项目开发中,开发者想要将所有ES6的代码转成ES5,插件逐个引入的方式令人抓狂,不单费力,而且容易出错。
    这个时候,可以采用Babel Preset。
    可以简单的把Babel Preset视为Babel Plugin的集合。比如babel-preset-es2015就包含了所有跟ES6转换有关的插件。

    更新.babelrc文件配置,支持genrator

    {
      "presets": ["@babel/preset-env", "@babel/preset-react"],
      "plugins": [
        [
          "@babel/plugin-transform-runtime",
          {
            "corejs": false,
            "helpers": true,
            "regenerator": true,
            "useESModules": false
          }
        ]
      ]
    }
    


    点击按钮会在2秒后执行+1操作。

    引入react-router

    在web应用开发中,路由系统是不可或缺的一部分。在浏览器当前的URL发生变化时,路由系统会做出一些响应,用来保证用户界面与URL的同步。随着单页应用时代的到来,为之服务的前端路由系统也相继出现了。而react-route则是与react相匹配的前端路由。

    引入react-router-dom

    npm install --save react-router-dom -D
    

    更新app.js入口文件增加路由匹配规则

    import App from './src/views/App';
    import ReactDom from 'react-dom';
    import React from 'react';
    import store from './src/store';
    import { Provider } from 'react-redux';
    import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
    
    const About = () => <h2>页面一</h2>;
    const Users = () => <h2>页面二</h2>;
    
    ReactDom.render(
        <Provider store={store}>
            <Router>
                <Switch>
                    <Route path="/" exact component={App} />
                    <Route path="/about/" component={About} />
                    <Route path="/users/" component={Users} />
                </Switch>
            </Router>
        </Provider>
    , document.getElementById('root'));
    

    更新App组件,展示路由效果

    import React from 'react';
    import { connect } from 'react-redux';
    import { increment } from '../../actions/index';
    import { Link } from "react-router-dom";
    
    
    class App extends React.Component {
    
        constructor(props) {
            super(props);
        }
    
        onClick() {
            this.props.dispatch(increment())
        }
    
        onClick2() {
            this.props.dispatch({ type: 'INCREMENT_ASYNC' })
        }
    
        render() {
            return (
                <div>
                    <div>react-router 测试</div>
                    <nav>
                        <ul>
                        <li>
                            <Link to="/about/">页面一</Link>
                        </li>
                        <li>
                            <Link to="/users/">页面二</Link>
                        </li>
                        </ul>
                    </nav>
    
                    <br/>
                    <div>redux & redux-saga测试</div>
                    <div>current number: {this.props.number} <button onClick={()=>this.onClick()}>点击+1</button></div>
                    <div>current number: {this.props.number} <button onClick={()=>this.onClick2()}>点击2秒后+1</button></div>
                </div>
            );
        }
    }
    export default connect(
        state => ({
            number: state.number
        })
    )(App);
    


    点击列表可以跳转相关路由

    总结

    至此,我们已经一步步的,完成了一个简单但是功能齐全的react项目的搭建,下面回顾一下我们做的工作

    1. 引入webpack
    2. 引入react
    3. 引入babel解析react
    4. 接入webpack-dev-server提高前端开发效率
    5. 引入redux实现一个increment功能
    6. 引入redux-saga实现异步处理
    7. 引入react-router实现前端路由

    麻雀虽小,五脏俱全,希望通过最简单的代码快速的理解react工具链。其实这个小项目中还是很多不完善的地方,比如说样式的解析、Eslint检查、生产环境配置,虽然这几项是一个完整项目不可缺少的部分,但是就demo项目来说,对我们理解react工具链可能会有些干扰,所以就不在项目中加了。
    后面会新建一个分支,把这些完整的功能都加上,同时也会对当前的目录结构进行优化。


    原文:https://segmentfault.com/a/1190000017945643

    代码库:https://github.com/teapot-py/react-demo

    展开全文
  • 一个使用react的项目,代码是全的,有整个react的开发流程
  • React组件概念想必大家都熟悉了,但是在业务开发过程中,面对通用的业务,怎么编写一个通用的React业务组件呢?本文以实际案例说明,如何编写一个通用的业务组件。 案例背景 实现一个如图所示的组件,可以添加对组件...
  • react 项目代码

    2018-12-22 12:37:09
    打开我们刚刚新建的react 项目 todolist, 下面是它的目录结构。src 下的index.js 是整个项目的入口文件。 可以看到index.js 中引入了serviceWorker 它是一个PWA,离线页面的内容。 我们暂且可以先不用它。删掉...
  • 总结一下,一个简单的React.js应用应按照以下步骤构建: 设计组件原型和JSON API; 拆分用户界面为一个组件树; 利用React, 创建应用的一个静态版本; 识别出最小的(但是完整的)代表UI的state; 确认state的...
  • 首先推荐安装 5.2.0版本以上的npm,没有的也可以先去更新一下npm,然后输入git bash输入代码npx create-react-app my-app效果如上,接下来等一会儿安装完成后:cd my-app npm start就能启动你的项目了...
  • 学习技巧:学习当中不要只看,一定要多敲代码,如果碰到某一个知识点不是很明白,不要钻牛角尖,千万不要因为一个点,放弃整个森林,接着往下学,硬着头皮开发项目只要能亲自开发一个完整的项目,你会发现不明白的...
  • 2017-03-20 胡子大哈 前端开发 来自:前端大哈 - 知乎专栏 作者:胡子大哈 链接:https://zhuanlan.zhihu.com/p/25398176(点击尾部阅读原文前往) 目录 1 前言 2 一切从点赞说起 ... 构建新...
  • react.js完整项目

    2020-07-30 23:32:06
    前端fackbook的react框架,包括es6,react,redux,typescript,是一下完整的项目
  • 今天给大家带来一个详细的React的实例,实例并不难,但对于初学者而言,足够认清React的思考和编写过程。认真完成这个实例的每一个细节会让你受益匪浅。接下来我们开始吧!代码下载预览首先说明一下,本例究竟做了...
  • 关注了很久,最近正好有一个小需求,赶紧来试一下。 需求描述 需求很简单,部门内部的一个数据查询小工具。大致长成下面这样: 用户首次访问页面,会拉取数据展示。输入筛选条件,点击查询后,会再次拉取数据在前端...
  • 大家好,我是:じ☆ve朽木,...react项目中有个需求需要对接一个代码编辑器,查看了ant design官方社区精选组件提供了两款代码编辑器,有一款是微软推出的,但是代码提示不是很友好,最后需求又查看了阿里云的相关...
  • 创建一个react项目有三种方式: 1.create-react-app 快速脚手架(简单,类似于 vue-cli 工具) 2.webpack一步一步构建 3. 第三方脚手架(generator-react-webpack,需要yeoman的支持) 这里主要介绍利用...
  • (1)打开vscode,搜索 Simple React Snippets (2)安装完成后使用快捷键测试: Snippet Renders imr Import React imrc Import React / Component impt Import PropTypes impc Import React / ...
  • react的功能其实很单一,主要负责渲染的功能,现有的框架,比如angular是一个大而全的框架,用了angular几乎就不需要用其他工具辅助配合,但是react不一样,他只负责ui渲染,想要做好一个项目,往往需要其他库和工具...
  • 在写代码之前,先搞明白几问题:用什么开发工具比较好?React Native的原理是什么,开发的App性能和原生应用一样好么? 搭建开发环境 React Native CLI 安装Node.js,通过npm命令安装React Native: npm ...
1 2 3 4 5 ... 20
收藏数 27,361
精华内容 10,944
关键字:

一个完整的react网站代码