玩物圈前端技术栈总结React+Redux

本文代码模版:react-redux-webpack-boilerplate

好记性不如烂笔头,之前陆续写过几篇关于玩物圈前端所用到技术栈的总结,现在在 玩物圈PC版 上线之前,将玩物圈前端用到技术栈整体简单总结梳理下。

前端基本框架图

1、webpack

webpack是一款模块加载器兼打包工具,具体使用参考官方文档很详细

项目中的主要作用:

  • 模块管理:模块化管理js、css、image等文件

  • 按照模板生成html:主要使用了html-webpack-plugin插件,按照模版生成html文件,并注入指定的chunks

  • 静态资源管理,md5,路径重定位

生产配置

var webpack = require('webpack');
var path = require('path');
var entry = require('./entry.js');
var templateConfig = require('./html.template.config.js').pro;
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('vendor','static/js/vendor.[hash:8].js');

var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

var config = {
  entry: entry,output: {
    path: __dirname + '/product',publicPath: 'http://cdn.xx.com/',filename: 'static/js/[name].[chunkhash:8].js'
  },resolve: {
    extensions: ['','.js','.jsx'],// 配置可以不书写的后缀名
    root: path.join(__dirname,'public/') //配置绝对路径,alias、entry中会使用
  },module: {
    loaders: [
      {
        test: /\.js[x]?$/,include: path.resolve(__dirname,'public'),exclude: /node_modules/,loader: 'babel-loader'
      },{
        test: /\.(jpg|png|gif)$/,loader: 'url?limit=1024&name=static/images/[hash].[ext]'//小于1kb的图片转化为base64,css中其他的图片地址会被体会为打包的地址,此处用到了publicPath
      },{test: /\.css$/,loader: ExtractTextPlugin.extract('style-loader','css-loader?sourceMap')}
    ]
  },plugins: [
    commonsPlugin,new ExtractTextPlugin('static/css/[name].[chunkhash:8].css'),new webpack.DefinePlugin({
      'process.env.NODE_ENV': '"production"'
    }),commonsPlugin
  ]
};

for (var i = 0; i < templateConfig.length; i++) {
  config.plugins.push(new HtmlWebpackPlugin(templateConfig[i]));
}

module.exports = config;

参考文档:

2、Babel

Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,JSX语法代码转为ES5代码。
项目中主要使用Babel将源代码ES6、JSX转码为ES5。

3、React

React提供应用的 View 层,表现为组件,具体参考官方文档

主要知识点:

  • JSX (可选的)

  • 组件(props、state、生命周期、事件、Form、几个api)

  • Virtual Dom

参考:

4、Redux(单向数据流)

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。本身跟react没有任何关系。

Redux 除了和 React 一起用外,还支持其它界面库。

4.1 基本思想

可以参考Redux:一种更优雅的 Flux 实现

  • Action(普通Action、异步Action)

    • 普通Action,本质是JS普通对象

    • 异步Action,使用了 Thunk middleware 异步 action

  • Reducer

    • ( previousState,action ) => newState

    • 处理数据逻辑

    • 拆分和合并reducer(用 ES6 的 import、export 语法,非常方便)

  • Store

    • 联系Action与Reducer的对象,为应用提供state

4.2 中间件Middleware

类似 Express 或 Koa 框架中的中间件。它提供的是位于 action 被发起之后,到达 reducer 之前的扩展。
中间件的设计使用了非常多的函数式编程的思想,包括:高阶函数,复合函数,柯里化和ES6语法,源码仅仅20行左右。
项目中主要使用了三个中间件,分别解决不同的问题。

  • thunkMiddleware:处理异步Action

  • apiMiddleware:统一处理API请求。一般情况下,每个 API 请求都至少需要 dispatch 三个不同的 action(请求前、请求成功、请求失败),通过这个中间件可以很方便处理。

  • loggerMiddleware:开发环境调试使用,控制台输出应用state日志

参考:

5、react-redux

react-redux的作用是连接(connect)store和容器组件的。store是redux提供的,容器组件是react提供的。

5.1 组织应用的组件

  • 组织应用的组件

    • 容器组件

    • 展示组件

容器组件:位于应用最顶层的组件,用来与redux连接的。从redux中获取数据作为props。
展示组件:位于应用的中间或者子组件,是纯粹的组件,与redux没有关系。他们从自己的父组件获取数据作为props,他们的共同根组件是应用的唯一的容器组件。展示组件可以维持少量的自身状态信息。

5.2 连接Store与组件

react-redux仅仅提供两个关键模块:Provider和connect。

源码:

import Provider from './components/Provider'
import connect from './components/connect'

export { Provider,connect }
  • Provider:是一个组件,接受一个store属性和一个子组件(也就是上面说到的:store是redux提供的,容器组件是是react提供的。)

例子:

ReactDOM.render(
    <Provider store={store}>
      {/* note "routerState" here: important to pass it down */}
      <Handler routerState={routerState} />
    </Provider>,document.getElementById('root')
  );
  • connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options]):connect返回一个函数,它接受一个React组件的构造函数作为连接对象,最终返回连接好的组件构造函数。

例子:

import * as actionCreators from './actionCreators'

function mapStateToProps(state) {
  return { todos: state.todos }
}

export default connect(mapStateToProps,actionCreators)(MyRootComponent)

参考:

6、ES6

目前主流的框架(Angular2,React,Koa,Redux)全面转向ES6。项目中使用了部分ES6的明星特性。一开始我是拒绝的,不习惯,现在的感觉是:非常方便,非常爽。

6.1 Class和Module

模块化:组件按模块编写以及使用、Action和Reducer按模块拆分合并、使用第三方模块,这些在项目中都是使用的是ES6的Module特性,其中编写React组件使用了ES6的Class特性。

例子:

import {Types} from '../constants/base/order';

export * from './base/user';
export {fetchCart} from './base/shopCart';
export {fetchOrder} from './base/order';

export function fetchPayResult(id) {
  return {
    url: '/mall/order/payResult/' + id,method: 'GET',types: ['REQUEST',Types.FETCH_PAY_RESULT,'FAILURE']
  };
}

export function changePayType(payType) {
  return {
    type: Types.SELECT_PAY_TYPE,payType
  };
}
export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}
Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };

6.2 变量结构赋值

var {
      types,url = '',mockUrl = '',method = 'GET',dataType = 'json',data = {}
      } = action;

6.3 函数的扩展:箭头函数、函数参数的默认值

  • 箭头函数:箭头函数在项目中也用得比较多,简化函数的编写,React Stateless function components 的编写。

const noop = ()=> false;

let createItem = (item,index) =><Order order={item} key={index}/>;

const Coupon = (props) => (
    <li>
      <div className="coupon-tit">抵用券</div>
      <div className="coupon-price"><span>¥</span><strong>{props.coupon.price}</strong></div>
      <div className="coupon-info">
        <p>{props.coupon.code}</p>

        <p className="time">{props.coupon.endTime}前可用</p>
      </div>
    </li>
);
  • 函数参数的默认值:典型的应用是编写Reducer。

export function address(state = {},action) {
  switch (action.type) {
    case Types.SELECT_ADDRESS:
      return objectAssign({},action.payload);
...

6.4 字符串扩展:模板字符串

href={`/pc/mall/order/confirm.html?${param}`}
return {
    mockUrl: '/static/mock/user.address.save.json',url: `/user/address/${id}`,method: 'PUT',data: {id,isDefault},Types.SET_DEFAULT_ADDRESS,'FAILURE']
  };

6.5 对象的扩展:属性的简洁表示法

export const setVisibilityFilter = (filter) => {
  return {
    type: 'SET_VISIBILITY_FILTER',filter
  }
}
return {
    mockUrl: '/static/mock/user.address.save.json','FAILURE']
  };
App.defaultProps = {
  user: {},tips: {visible: false},carts: [],visibleDropCart: false,visibleLoginDialog: false,switchLoginDialog() {
  },switchTips() {
  },switchDropCart() {
  }
};

6.6 let和const

const noop = ()=> false;

let createItem = (item,index) =><Order order={item} key={index}/>;

参考:

7、Gulp

Gulp与Grunt一样,也是一个自动任务运行器。它充分借鉴了Unix操作系统的管道(pipe)思想,很多人认为,在操作上,它要比Grunt简单。

项目中主要用到的功能:结合webpack使用、压缩js、ESLint代码检查、压缩css。

var gulp = require("gulp");
var gutil = require("gulp-util");
var minifyCss = require('gulp-minify-css');
var uglify = require('gulp-uglify');
var eslint = require('gulp-eslint');
var reporter = require('eslint-html-reporter');
var fs = require('fs');
var path = require('path');

var webpack = require("webpack");

var webpackConfigProduct = require("./webpack.production.config.js");
var webpackConfigDevelop = require("./webpack.development.config.js");

gulp.task("webpack",function(callback) {
  webpack(webpackConfigProduct,function(err,stats) {
    if (err) throw new gutil.PluginError("webpack",err);
    callback();
  });
});

gulp.task("webpackDevelop",function(callback) {
  webpack(webpackConfigDevelop,err);
    callback();
  });
});

var srcJsDir = './public/static/js/';

gulp.task('lint',function() {
  return gulp.src([srcJsDir + '**/*.js'])
      .pipe(eslint())
      .pipe(eslint.format(reporter,function(results) {
            fs.writeFileSync(path.join(__dirname,'lint-report.html'),results);
          })
      );
});

gulp.task("minifyJs",['webpack'],function() {
  return gulp.src("./product/**/*.js")
      .pipe(uglify({
        output: {
          max_line_len: 100
        }
      }))
      .pipe(gulp.dest("./product"));
});

gulp.task("minifycssPro",function() {
  return gulp.src("./product/**/*.css")
      .pipe(minifyCss())
      .pipe(gulp.dest("./product"));
});

gulp.task("minifycssDev",['webpackDevelop'],function() {
  return gulp.src("./development/**/*.css")
      .pipe(minifyCss())
      .pipe(gulp.dest("./development"));
});

gulp.task('copyJson',function() {
  return gulp.src('./public/static/mock/**/*.json')
      .pipe(gulp.dest('./development/static/mock/'));
});

gulp.task('product',['minifycssPro','minifyJs']);

gulp.task('default',['minifycssDev','copyJson']);

参考:

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom