如何快速启动React前端应用开发(Redux、webpack、babel、postcss、SSR)

现代前端应用是一个工程化的系统,和jquery时代有着很大的不同,以前的主要工作是模板填充、事件绑定、DOM更新等,而现在前端应用和后端工程一样,需要从一个完整的系统层面思考,数据流和控制流如何流转?数据该如何存放?业务方法如何管理?UI组件如何封装和路由?最后还有编译打包,优化等问题。幸运的是已有各种工具帮我们解决了绝大部分的问题,可如何将这些工具库组合在一起形成一个可用的应用,并不是一件简单的事情,例如我们需要大量的配置来使webpack和babel按照要求工作。

那如何能快速的开始工作呢?选择一个前端框架定会事半功倍,例如最常用的react-create-appnext.jsdva-cli等,都只需要简单的几步操作就可以开始进行业务开发了。如果你还没有听说过它们或者它们的同类,相信你现在正在被各种配置文件、架构结构、多人协同开发感到困惑。这里介绍的是另外一个前端框架@symph/joy,它结合了next.js和dva的特点,是一个全栈型的框架(因为每个项目对UI的要求都不一样,所以未集成UI组件),尽量让前端开发更加简洁、规范。

react-create-app是一个比较基础的脚手架,很多东西还得开发者自己搭建,并不建议在实际工程中使用。

我们现在从一个React新手的角度思考问题,对webpack、babel、redux等工具还不了解,只是想用React写一个简单的展示页面,该如何开始工作呢?

创建项目目录

运行yarn init创建一个空工程,并填写项目的基本信息,当然也可以在一个已有的项目中直接安装。

yarn install --save @symph/joy react react-dom

添加NPM脚本到package.json文件:

{
  "name": "hello-word",
  "version": "1.0.0",
  "scripts": {
    "dev": "joy",
    "build": "joy build",
    "start": "joy start"
  }
}

编写第一个页面

然后就可以开始你的开发工作了,创建./src/index.js文件,并插入以下代码:

import React, { Component } from 'react'

export default class Index extends Component {
  render () {
    return <div>
      <div>Welcome to @symph/joy!</div>
    </div>
  }
}

得到了什么

最后运行yarn run dev命令,在浏览器中输入访问地址http://localhost:3000

进行上面简单的几步后,我们拥有了些什么呢?

  • 应用入口(./src/index.js),一切都从这里开始,以后可以添加子路由、布局、Model等组件
  • 启动了一个服务器,支持服务端渲染和业务请求代理转发
  • 一个零配置的webpack+babel编译器,确保代码在Node.js和浏览器上正确运行
  • ES6、7、8等高级语法支持,如:classasync@注解、{...}解构等
  • 热更新,调试模式下,在浏览器不刷新的情况下,使更改立即生效
  • 静态资源服务,在/static/目录下的静态资源,可通过http://localhost:3000/static/访问

第二个页面,添加路由

接下来开始第二个页面的开发,假设添加一个src/about.jsx页面。

// src/about.jsx
import React, { Component } from 'react'

export default About class extends Component {
  render () {
    return <div>
      <div>about @symph/joy</div>
    </div>
  }
}

此时需要路由组件来完成页面之间的跳转,@symph/joy使用react-router-4作为路由,并在应用初始时已集成到应用中,所以不需要开发者自己添加依赖和初始化react-router-4,我们可以直接从'@symph/joy/router'中导入并使用。

扫描二维码关注公众号,回复: 3274414 查看本文章

首先对之前的src/index.js进行改造,将首页的内容独立出来,作为一个单独的页面,方便在两个界面之间跳转。

// src/hello.jsx
import React, { Component } from 'react'

export default Hello class extends Component {
  render () {
    return <div>
        <div>Welcome to @symph/joy!</div>
    </div>
  }
}

修改入口组件

// src/index.js
import React, { Component } from 'react'
import {Switch, Route, Link} from '@symph/joy/router'
import Hello from './hello'
import About from './about'

export default class Index extends Component {
  render () {
    return (<div>
       <div className="header">
         <Link to="/">首页</Link>
         <Link to="/about">关于</Link>
       </div>
       <Switch>
         <Route exact path="/about" component={About}/>
         <Route path="/" component={Hello}/>
       </Switch>
    </div>)
  }
}

使用样式

jsx内建样式

内建了 styled-jsx 模块,支持Component内独立域的CSS样式,不会和其他组件的同名样式冲突。

// src/hello.js
import React, { Component } from 'react'

export default class Hello extends Component {
  render () {
    return (<div>
       <div className="message">Welcome to @symph/joy!</div>
       <style jsx>{`
         .message {
           color: blue;
         }
       `}</style>
       <style global jsx>{`
         body {
          background: #F5F5F5;
         }
       `}</style>
    </div>)
  }
}

查看 styled-jsx 文档 ,获取详细使用信息。

Import CSS / LESS 文件

@symph/joy提供了插件来处理样式,默认支持post-css、autoprefixer、css-modules、css-extract,具体使用方法请查看插件使用文档。

MVC工程模式

我们可以在页面上使用fetch方法来获取数据并展示,此时已经具备了编写一个前端应用的全部能力了。但作为一个日益复杂,需多人协同开发,且可持续维护的项目来说,还远远不够。至少我们还缺少对数据和业务方法的管理,并且要形成一个规范共识,才可持续的发展下去。

MVC模式用于规范我们的代码设计,这是一个很基础的,也是很容易理解的模式,要将上面的例子变为MVC模式,只需要添加几行声明式的注解代码,不会侵入业务代码。MVC三个字母代表着应用中的三个不同类别的部件,它们有各自的职责:

  • Model: 管理应用的行为和数据,普通class类,有初始状态,业务运行中更新model状态
  • View: 展示数据,继承React.Component
  • Controller: 控制View的展示,绑定Model数据到View,响应用户的操作,调用Model中的业务, 继承于React.Component

它们之间的工作流程如下图

app work flow

图中蓝色的箭头表示数据流的方向,红色箭头表示控制流的方向,他们都是单向流,store中的state对象是不可修改其内部值的,状态发生改变后,都会生成一个新的state对象,且只将有变化的部分更新到界面上,这和redux的工作流程是一致的。

如果你刚接触React和Redux不久,也许对上面的概念理解起来有点困难,请不要担心,框架就是帮助我们解决掉各种复杂的概念和繁琐重复的工作而的,让我们更专注于业务开发。所以这里,我们完全可以放心进行下一步操作。

下面通过一个商品展示的例子来说明Model,View,Controller之间是如何工作的,查看示例工程

目录结构建议采用以下形式

-project
    -src
        -components/
        -controllers/
            -ProductsController.js
        -models/
            -ProductsModel.js
        -index.js

### 创建Model

下面展示的是一个管理商品列表的model,负责存放商品列表、分页信息等,还有获取和处理这些信息的业务方法。

// src/models/ProductsModel.js
import model from '@symph/joy/model'
import fetch from '@symph/joy/fetch'

// 使用注解的方式,申明这是一个Model
@model() 
export default class ProductsModel {

  // 这个model的全局名称,后面需要通过这个名称来找到model的状态。
  namespace = 'products';

  // model的初始化状态
  initState = {
    pageIndex: null,
    pageSize: 10,
    products: [],
  };

  async getProducts({pageIndex = 1, pageSize}) {
    // 发送远程请求,获取数据
    let pagedProducts = await fetch('https://www.example.com/api/hello', 
      {body:{pageIndex, pageSize}});

    let {products} = this.getState();
    if (pageIndex === 1) {
      products = data;
    } else {
      products = [...products, ...pagedProducts];
    }

    // 更新model的状态,此时和model状态绑定的Controller也会同步更新
    this.setState({
      products,
      pageIndex,
      pageSize
    });
  }
};

创建Controller

接下里编写一个Controller来绑定上面ProductsModel中的产品列表,并在界面加载的时候,调用获取产品列表的业务方法。

// src/controllers/ProductsController.js
import React, {Component} from 'react';
import ProductsModel from '../models/ProductsModel'
import controller, {requireModel} from '@symph/joy/controller'


@requireModel(ProductsModel)          // 注册依赖的model
@controller((state) => {              // 使用注解的方式,申明这是一个Controller
  return {
    products: state.products.products // 绑定ProductsModel中的产品列表
  }
})
export default class ProductsController extends Component {

  async componentPrepare() {
    let {dispatch} = this.props;

    // TOOD 这里可以打开加载动画

    await dispatch({        // 调用ProductsModel中获取产品列表的业务方法
      type: 'products/getProducts', 
      pageIndex: 1,
      pageSize: 10,
    });

   // TOOD 这里可以关闭加载动画

  }

  render() {
    let {products = []} = this.props;   // 获取绑定的产品列表
    return (
      <div >
        <div>Product List</div>
        <div>
          {products.map((product, i) => {
            return <div key={product.id} onClick={this.onClickProduct.bind(product)}>{product.id}:{product.name}</div>
          })}
        </div>
      </div>
    );
  }
}

使用View

View只是一个普通的React.Component类,是一个纯展示组件,它的数据来自于props属性。

打包部署

最后我们需要将应用打包部署到生产机器上,@symph/joy提供了命令工具来处理这些事情。在package.jsonscripts节点中添加NPM脚本:

// package.json
{
  "scripts": {
    "dev": "joy dev",
    "build": "joy build",
    "start": "joy start"
  }
}

在命令行工具里使用yarn run build命令来编译代码,生成.joy目标目录。然后将.joy目录上传到生产机器上,不用上传源代码文件了。最后在生产机器上执行yarn run start命令来启动应用,此时会直接运行.joy目录中预先编译好的代码。

静态版本输出

如果我们的前端应用没有专门的node.js服务器来运行,或者不需要服务端渲染,可以将应用编译为静态版本,将其放到静态资源服务器上,浏览器直接加载运行。

这不需要修改任何的源代码,只需添加一个NPM命令exportpackage.json文件中:

{
  "scripts": {
    "build": "joy build",
    "export": "yarn run build && joy export"
  }
}

执行yarn run export命令完成导出工作,静态版本需要的所有文件都放在应用根目录下的out目录中,只需要将out目录部署到静态文件服务器就可以了。

高级功能

上文只是对前端日常开发工作做了介绍,但在实际项目中,还会遇到很多需求,例如:

  • 集成到express、koa等服务端框架中
  • 定义html渲染,加入js、css文件引用,自定义head部分等
  • 跨域请求如何解决
  • 更复杂的路由控制
  • 浏览器端js按需加载、代码分割、压缩等优化处理
  • 自定义webpack和babel配置等

@symph/joy对以上问题都提出了解决方案,更多更详细的使用介绍,请查看其主页:https://lnlfps.github.io/symph-joy

猜你喜欢

转载自blog.csdn.net/lve77/article/details/82256302