关于javascript:民工日记之React开荒二

10次阅读

共计 5596 个字符,预计需要花费 14 分钟才能阅读完成。

脚手架创立利用

当今 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']};
// 写法 1
const newState = {...state, todos: [
    ...state.todos,
    'Learn Redux'
]};
// 写法 2
const 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/2
import 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>
    );
  }
}

正文完
 0