脚手架创立利用

当今web开发其实比较复杂,开发一个我的项目时通常要引入多个技术栈(UI:react, 状态治理:redux,路由:react/router,js个性翻译:babel,打包:webpack,语法查看:eslint),每个技术栈背地有很多package并且波及很多配置,过程简单且可重用,脚手架工具能够简略且疾速地生成一个空的我的项目来开发。最罕用的脚手架为create-react-app。另外还有一些脚手架(rekkit:集成了redux和react-router,codeSandbox:在线打包编译热加载)能够尝试。

打包部署

最罕用的打包工具webpack,具体执行流程优化,源码剖析包含热重载如何实现等涵盖内容较多前面争取补充。

为什么须要打包?

  • 编译ES6语法个性,编译JSX
  • 整合资源(图片、Less/Sass)
  • 优化代码体积

注意事项

  • 设置nodejs环境为production(这里因为有些js library不同环境运行不一样,比方react会在开发环境查看传参类型)
    设置环境变量在build.js中:
    process.env.BABEL_ENV = 'production';
    process.env.NODE_ENV = 'production';
  • 禁用开发专用代码,比方logger
  • 设置根门路(在package.json的“homepage”属性中)

执行打包:npm run build

redux状态治理

redux是基于flux的设计模式提出的状态治理框架,react将state转换为DOM,redux将这个过程放到组件之外的store中,当store中信息变动,组件也会进行相应的更新,也就是担当了治理全局状态和让组件通信更容易(传统个别是父组件通过props传参给子组件,子组件裸露一个事件回传参数给父组件)的职责。redux通过action来形容产生了什么(充当指示器),reducer用来串联state和action。

三大准则

  • Single source of truth
    传统MVC的model和view相互调用,错从简单,redux的所有数据起源都是store
  • State is read-only
    state + action = new state,必须action来生成而不是扭转原有的state
  • Changes are made with pure funcitons
    pure function的函数输入后果齐全取决于输出参数,不依赖于任何内部参数或者内部资源
function visibilityFilter(state = 'SHOW_ALL', action) {  switch (action.type) {    case 'SET_VISIBILITY_FILTER':      return action.filter    default:      return state  }}function todos(state = [], action) {  switch (action.type) {    case 'ADD_TODO':      return [        ...state,        {          text: action.text,          completed: false        }      ]    case 'COMPLETE_TODO':      //通过state.map生成新的state对象而不是批改原有state对象      return state.map((todo, index) => {        if (index === action.index) {          return Object.assign({}, todo, {            completed: true          })        }        return todo      })    //如果不是ADD或者COMPLETE类型的action间接返回state    default:      return state  }}//combineReducers返回一个封装函数,将reducer合并import { combineReducers, createStore } from 'redux'const reducer = combineReducers({ visibilityFilter, todos })const store = createStore(reducer)

了解Store

const store = createStore(reducer)

  • getState()
  • dispatch(action)
  • subscribe(listener)

Dispatcher用来派发action操作,State是数据对象,Reducer更新state,(state, action) => new state,state的任何变动都肯定是由一个action引起的,一旦发现state有问题能够追踪action是什么不便定位问题。redux利用中间件来截获和收回action,能够通过log打印进去追踪action变动。redux中的action能够通过组合多个 同步action的形式来实现异步action,比方:

export function saveFile(filePath, content) {  return (dispatch) => { // optionally you can have getState as the second argument    dispatch({      type: HOME_SAVE_FILE_BEGIN,    });    //通过返回一个promise来管制UI flow而不是通过state    //上面场景中,提交form后如果胜利跳转到另外的页面,失败则显示错误信息,如果用state来管制是比拟艰难的,然而通        过一个promise就比拟容易达成目标      const promise = new Promise((resolve, reject) => {      const doRequest = axios.post('/rekit/api/save-file', {        file: filePath,        content,      });      doRequest.then(        (res) => {          dispatch({            type: HOME_SAVE_FILE_SUCCESS,            data: {              file: filePath,              content,            },          });          resolve(res);        },        // Use rejectHandler as the second argument so that render errors won't be caught.        (err) => {          dispatch({            type: HOME_SAVE_FILE_FAILURE,            data: { error: err },          });          reject(err);        },      );    });    return promise;  };}

不同性能的action和reducer个别寄存在独立文件中再对立引入到一个index.js中,这种文件治理形式根本和vue-store的格调统一。

reudx的运行根底:不可变数据(immutable data),即store必须通过深拷贝或浅拷贝才可批改,不可再原数据根底上做批改,这样做的目标是:

  • 性能优化:不须要比拟前后的值来判断是否有更新,而是间接比拟援用是否产生扭转
  • 易于调试和跟踪:store变动时因为记录了old state和new state的值,能够进行diff比拟
  • 易于揣测:能够通过action值揣测执行过程

如何操作不可变数据:

  • 原生:{...}, Object.assign
const state = {filter: 'completed', todos: ['Learn React']};//写法1const newState = {...state, todos: [    ...state.todos,    'Learn Redux']};//写法2const newState2 = Object.assign({}, state, todos: [    ...state.todos,    'Learn Redux']);
  • immutability-helper工具类
import udpate from 'immutability-helper';const state = {filter: 'completed', todos: [    'Learn React']};const newState = udpate(state, {todos: {$push: ['Learn Redux']}});
  • immer(性能稍差)
import produce from 'immer';const state = {filter: 'completed', todos: [    'Learn React']};//draftState相当于是一个代理state,其实仍然没有在原state上批改,然而操作起来比拟像const newState = produce(state, draftState => {    draftState.todos.push('Learn Redux');});

react-router

非官方出品,然而曾经成为公认的路由治理规范。

  • 单页利用须要页面切换
  • 通过URL能够定位到页面
  • 更有语义的组织资源
    路由 -----> Router -----> 组件容器,
    个性:1、申明式理由 2、动静路由
    实现:1、URL门路(BroswerRouter) 2、hash路由(HashRouter,当浏览器不反对路由切换页面不刷新的时候应用) 3、内存路由(MemoryRouter,个别用在服务端渲染)
import React from "react";import { HashRouter as Router, Route, Link } from "react-router-dom";import { MemoryRouter } from "react-router";const Home = () => <h1>Home</h1>;const Hello = () => <h1>Hello</h1>;const About = () => <h1>About Us</h1>;export default class RouterSample extends React.PureComponent {  render() {    // react-router其实是组件的一部分    return (      <Router>        <div>          <ul id="menu">            <li>              <Link to="/home">Home</Link>            </li>            <li>              <Link to="/hello">Hello</Link>            </li>            <li>              <Link to="/about">About</Link>            </li>          </ul>          <div id="page-container">            <Route path="/home" component={Home} />            <Route path="/hello" component={Hello} />            <Route path="/about" component={About} />          </div>        </div>      </Router>    );  }}

react-router API介绍:https://reactrouter.com/web/a...

路由参数:

//传参<Route path="/topic/:id" component={Topic} />//获取参数const Topic = ({ match }) => (  <h1>Topic {match.params.id}</h1>);

嵌套路由

场景:除了导航栏可能还有多级子导航栏,嵌套路由是一个前端特有的概念。只有match path,组件就会render进去。

//假如浏览器输出一个url为:    xxxx/category/3/sub/2import React from "react";import {  BrowserRouter as Router,  Route,  Link} from "react-router-dom";const Category = ({ match }) => (  <h1>Sub Category {match.params.subId}</h1>);const SubCategory = ({ match }) => (  <div>    <h1>Category {match.params.id}</h1>    <ul id="menu">      <li>        <Link to={`/category/${match.params.id}/sub/1`}>          Sub Category 1        </Link>      </li>      <li>        <Link to={`/category/${match.params.id}/sub/2`}>          Sub Category 2        </Link>      </li>      <li>        <Link to={`/category/${match.params.id}/sub/3`}>          Sub Category 3        </Link>      </li>    </ul>    <div id="page-container-2">        <!--xxxx/category/3/sub/2 (path齐全匹配) -->      <Route        path="/category/:id/sub/:subId"        component={Category}      />    </div>  </div>);export default class NestedRoute extends React.PureComponent {  render() {    return (      <Router>        <div>          <ul id="menu">            <li>              <Link to="/category/1">Category 1</Link>            </li>            <li>              <Link to="/category/2">Category 2</Link>            </li>            <li>              <Link to="/category/3">Category 3</Link>            </li>          </ul>          <div id="page-container">             <!--xxxx/category/3/sub/2 会首先匹配到顶层容器的路由(path局部匹配) -->            <Route              path="/category/:id"              component={SubCategory}            />          </div>        </div>      </Router>    );  }}