kiven_wolf

kiven_wolf 查看完整档案

填写现居城市福建师范大学  |  计算机科学技术 编辑百度  |  开发工程师 编辑填写个人主网站
编辑

追求内心,锤炼技能,持之以恒,成功追随你!

个人动态

kiven_wolf 发布了文章 · 3月15日

MyBatis解决在开发过程中实体类属性和数据库列名不一致的问题

MyBatis解决在开发过程中实体类属性和数据库列名不一致的问题

记:之前我们规定 编写实体类的时候, 属性和数据库列名 必须保持 一致。

思考:二开、业务变化等等的时候,此时数据库中的列名和实体类的属性名就是不一致,怎么解决?

实体类属性和数据库列名不一致,如下图所示:

image.png

演示 属性和数据库列名 不一情况下,数据库 改 操作

提示:这里只演示改操作。

记:在修改配置文件 IUserDao.xml 后,增删改操作基本可以满足开发需要。

------

新建实体类:UserV2.java

image.png

代码如下:

------

public class UserV2 {

    private int userId;

    private String userName;

    private Date userBirthday;

    private String userSex;

    private String userAddress;
    ...

------

接口:IUserDao.java,新增 updateUserV2 方法

image.png

代码如下:

-------

/**
* 用户修改v2
* @param userv2
*/
void updateUserV2(UserV2 userv2);

-------

配置文件: IUserDao.xml ,代码如下:
-------

<!--更新用户操作V2-->

<update id="updateUser" parameterType="com.abc2.domain.UserV2">

UPDATE USER SET \`username\`=#{userName},\`address\`=#{userAddress},\`sex\`=#{userSex},\`birthday\`=#{userBirthday} WHERE id=#{userId};

</update>

测试文件,代码如下:

@Test

public void updateUserV2Test() {

UserV2 user = new UserV2();

user.setUserId(70);

user.setUsername("小明");

user.setUserAddress("外星球-月球22");

user.setUserSex("女");

user.setUserBirthday(new Date());

//更新用户

userDao.updateUserV2(user);

//执行提交

sqlSession.commit();

}

结果如下,修改成功!
image.png
总结:

一般来说,增删改的方法,就算实体属性和数据库列名不一致,通过配置后,也可以正常运行。

但是,在查询的时候,如果不一致,就会出异常!

演示 属性和数据库列名 不一情况下,数据库 查 操作

查询的时候,列名无法对齐,导致数据出现异常。

接口:IUserDao.java

/**
 * 查询所有数据V2
 * @return
**/
List<UserV2> findAllV2();

配置文件:IUserDao.xml

<!--配置查询所有 指定id,resultType结果集类型 -->
<select id="findAllV2" resultType="com.abc2.domain.UserV2">
select \* from user
</select>

测试类:Test

@Test
public void findAllV2Test() {
    List<UserV2> allV2 = userDao.findAllV2();
    System.out.println("-------");
    for (UserV2 item : allV2) {
    System.out.println(item);
    }
    System.out.println("-------");
}

运行结果,如下图所示:
image.png
提示:mysql数据库在windows系统下不区分大小写。如果是linux系统下,是严格区分大小写的。

解决方案:

  • 设置查询别名(了解);
  • 配置 查询结果的列名和实体类的属性名的对应关系(推荐使用!理由:这个resultMap可以复用,修改起来也方便);

修改配置文件:IUserDao.xml

<!--配置:查询结果的列名和实体类的属性名对应关系-->

<resultMap id="userMap2" type="com.abc2.domain.UserV2"\>

<!--主键字段的对应-->

<id property="userId" column="id"></id>

<!--非主键字段对应-->

<result property="userName" column="username"></result>

<result property="userBirthday" column="birthday"></result>

<result property="userSex" column="sex"></result>

<result property="userAddress" column="address"></result>

</resultMap>

  

<!--配置查询所有 指定id,resultType结果集类型 -->

<select id="findAllV2" resultMap\="userMap2">

<!-- 使用查询别名-了解即可! -->

<!-- select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as userAddress from user;-->

select \* from user;

</select>

测试类:Test

@Test

public void findAllV2Test() {

List<UserV2> allV2 = userDao.findAllV2();

System.out.println("-------");

for (UserV2 item : allV2) {

System.out.println(item);

}

System.out.println("-------");

}

查询结果如下:
image.png

欢迎大家来评论!

查看原文

赞 0 收藏 0 评论 0

kiven_wolf 发布了文章 · 3月14日

idea中log4j日志插件报错

idea中log4j日志插件报错
在运行测试代码的时候,出现以下错误!

在 src/ main / resources--> log4j.properties

注意:文件名 一定要是 log4j.properties,不然无效!

代码如下:

##配置文件log4j.properties
### 设置###
log4j.rootLogger=debug,stdout,D,E
### 输出信息到控制抬 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=E://Java//LogTest2//logs/error.log ###
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=D:/soft_cache/idea_bug/log2.log
log4j.appender.D.Append=true
log4j.appender.D.Threshold=DEBUG 
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
### 输出ERROR 级别以上的日志到=E://Java//LogTest2//logs/error.log ###
log4j.appender.E=org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File=D:/soft_cache/idea_bug/log2.log
log4j.appender.E.Append=true
log4j.appender.E.Threshold=ERROR 
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

注意:

配置文件一定要写好!
一定要配置好输出路径!
看到以下结果,说明配置成功:

原文链接:https://blog.csdn.net/kiven_w...

查看原文

赞 0 收藏 0 评论 0

kiven_wolf 关注了用户 · 3月13日

KohPoll @kohpoll

==|

关注 105

kiven_wolf 发布了文章 · 3月13日

月薪1万和7K的前端开发工程师有啥区别

月薪1万和7K的前端开发工程师有啥区别

今天老板连续3个问题

做前端的技术精进到底是体现在哪一方面?

啥区别?

除开敲代码的快与慢,熟练与不熟练,理解能力高低,还有其他吗?

 

个人的一些理解

区别还是有区别的,比如对框架的掌握程度,能不能避免更多的坑。

还有就是 对于代码维护的理解

很多人写代码,是面向过程的,后续改起来,就很难

我之前和一个网龙出来的同事,一起工作

他那个人,还没有动工前,光指定 前期的编码规范,目录规范以及编码标准用了1天左右时间(PS:后续持续改进和优化,落地成文档),然后我们几个才开始写的。

话说回来,前端开发就是写写页面,对于中小型公司,不是很能体现 他的作用,后端稍微会写前端页面的,也都可以做。

但是到了大公司,有做移动端,SPA单页应用 + 混合APP,前后端分离做的很全的。前端的价值就可以很大程度发挥出来了。

现在 很多新出来的 后端语言,和 ES6 语法长的都很类似。类似 go 语言,flutter中的 dart语言等等。

类似前端中的  npm包管理项目依赖资源,不用像以前一样自己手动加载 js脚本,和 java 中的maven一样的,非常人性化。

查看原文

赞 0 收藏 0 评论 0

kiven_wolf 发布了文章 · 3月13日

mybatis注解开发和编写dao实现类的方式

mybatis注解开发和编写dao实现类的方式

mybatis基于注解的入门案例

image.png

把 IUserDao.xml 移除, 修改 IUserDao接口 的方法上使用 @Select注解,并且自动SQL语句。

代码如下:

public interface IUserDao {
@Select("select \* from user")
List<User> findAll();
}

同事需要在SqlMacpConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。

SqlMacpConfig.xml 代码如下:

<!--

指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件

如果是用注解来配置的话,此处应用使用class属性指定被注解的dao全限定类名

\-->

<mappers>

<mapper class="com.abc2.dao.IUserDao"></mapper>

</mappers>

注意事项

不要忘记在映射配置中告知mybatis要封装到哪个实体类中。

配置的方式:指定实体类的全限定类名。

经验总结

我们在实际开发中,都是越简单越好,所以基本都是采用 注解的方式 编写业务代码,都不采用dao实现类的方式。

不管使用XML还是注解配置。

但是Mybatis它是支持写dao实现类(不过是没有必要的)。

查看原文

赞 0 收藏 0 评论 0

kiven_wolf 发布了文章 · 3月12日

mybatis入门案例中的设计模式分析

mybatis入门案例中的设计模式分析

代码如下:

public class MyBatisTest {

    public static void main(String[] args) throws Exception {

        //1.读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMacpConfig.xml");

        //2.创建SqlSessionFactory工厂
        //使用 SqlSessionFactoryBuilder 创建 SqlSessionFactory 的好处,就是不用去关注创建工厂的细节,非常方便。
        SqlSessionFactoryBuilder bulider = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = bulider.build(in);

        //3.使用工厂生产SqlSession对象
        SqlSession sqlSession = factory.openSession();

        //4.使用SqlSession对象创建Dao接口的代理对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);

        //5.使用代理对象执行方法
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }

        //6.释放资源
        sqlSession.close();
        in.close();

    }
}

构建者设计模式

概念解释:现在盘了一块地要去盖楼盘,这个时候,如果自己去做,要考虑工人,盖楼所需要的材料,工具,施工车等等,非常麻烦。此时一般我们会把这些事情统统都承包给包工队,让包工队去做这些事情。

代码分析:

在生产工厂的时候 SqlSessionFactory factory = bulider.build(in); 就使用到了构建者模式,其中 in 表示的就是包工队概念。

-----------

构建者模式:

把对象插件的细节隐藏;

优势:让使用者直接调用方法即可拿到对象;

工厂模式

概念解释:要生产一辆汽车,如果自己做的话,要考虑,汽车零件,工人,场地等等,今天有了工厂,只要告诉工厂要做什么类型汽车,只要用工厂中的某个功能就可以创建了一辆自己想要的汽车,非常的方便和人性化。

代码分析:

SqlSessionFactoryBuilder bulider = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = bulider.build(in);

在生产 SqlSession的时候 SqlSession sqlSession = factory.openSession(); 使用了工厂模式。

-----------

工厂模式:

优势:解耦(降低了类之间的依赖关系)。

-----------

代理模式

概念解释:比如小明要去买车票回家过年,发现根本买不到车票,这个时候,就去找黄牛,只需要提供乘车人基本信息以及票价和手续费,出发地和黄牛就开始帮忙抢票。这个是很多平凡人的真实写照。简洁描述就是自己想要在正规渠道买票,发现基本买不了。但是委托黄牛(代理)让他带我们去买却有很大几率买到车票。那么,这种通过增加一个 中间件 然后去进行实际操作的模式,我们一般称之为代理模式。非常的方便,非常人性化。

代码分析:

IUserDao userDao = sqlSession.getMapper(IUserDao.class);

这个地方就使用了代理模式。

读取配置的路径分析

代码分析:
InputStream in = Resources.getResourceAsStream("SqlMacpConfig.xml");

-----------

这里直接使用文件名称,读取配置文件信息。

大家都知道:

-----------

绝对路径:d:/xxx/xxx.xml

相对路径:src/java/main/xxx.xml

-----------

一般来说,上面这两种在实际项目中读取配置文件的时候,很少用,或者基本不用。理由如下:

  • 绝对路径:多用在类加载器,它只能读取类路径的配置文件;
  • 相对路径:使用Servlet对象的getRealPath();
查看原文

赞 0 收藏 0 评论 1

kiven_wolf 关注了专栏 · 2019-12-27

kpaxqin

前端工程师

关注 88

kiven_wolf 赞了文章 · 2019-12-26

Redux入门教程(快速上手)

满满的干货,耐心看完,你会发现redux原来这么可爱。

典型的Web应用程序通常由共享数据的多个UI组件组成。通常,多个组件的任务是负责展示同一对象的不同属性。这个对象表示可随时更改的状态。在多个组件之间保持状态的一致性会是一场噩梦,特别是如果有多个通道用于更新同一个对象。

举个小栗子,一个带有购物车的网站。在顶部,我们用一个UI组件显示购物车中的商品数量。我们还可以用另一个UI组件,显示购物车中商
品的总价。如果用户点击添加到购物车按钮,则这两个组件应立即更新当前的数据。如果用户从购物车中删除商品、更改数目、使用优惠券或者更改送货地点,则相关的UI组件都应该更新出正确的信息。
可以看到,随着功能范围的扩大,一个简单的购物车将会很难保持数据同步。

在这篇文章中,我将介绍Redux框架,它可以帮助你以简单易用的方式构建复杂项目并进行维护。为了使学习更容易,我们将使用一个简化的购物车项目来学习Redux的工作原理。你需要至少熟悉React库,因为你以后需要将其与Redux集成。

学习前提

在我们开始以前,确保你熟悉以下知识:

同时,确保你的设备已经安装:

什么是Redux

Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动。 见下图:

图片描述

在Redux中,所有的数据(比如state)被保存在一个被称为store的容器中 → 在一个应用程序中只能有一个。store本质上是一个状态树,保存了所有对象的状态。任何UI组件都可以直接从store访问特定对象的状态。要通过本地或远程组件更改状态,需要分发一个action分发在这里意味着将可执行信息发送到store。当一个store接收到一个action,它将把这个action代理给相关的reducerreducer是一个纯函数,它可以查看之前的状态,执行一个action并且返回一个新的状态。

理解不变性(Immutability)

在我们开始实践之前,需要先了解JavaScript中的不变性意味着什么。在编码中,我们编写的代码一直在改变变量的值。这是可变性。但是可变性常常会导致意外的错误。如果代码只处理原始数据类型(numbers, strings, booleans),那么你不用担心。但是,如果在处理Arrays和Objects时,则需要小心执行可变操作。
接下来演示不变性

  • 打开终端并启动node(输入node)。
  • 创建一个数组,并将其赋值给另一个变量。
> let a = [1, 2, 3]
> let b = a
> b.push(8)
> b
[1, 2, 3, 8]
> a
[1, 2, 3, 8]

可以看到,更新数组b也会同时改变数组a。这是因为对象和数组是引用数据类型 → 这意味着这样的数据类型实际上并不保存值,而是存储指向存储单元的指针。
将a赋值给b,其实我们只是创建了第二个指向同一存储单元的指针。要解决这个问题,我们需要将引用的值复制到一个新的存储单元。在Javascript中,有三种不同的实现方式:

  1. 使用Immutable.js创建不可变的数据结构。
  2. 使用JavaScript库(如UnderscoreLodash)来执行不可变的操作。
  3. 使用ES6方法执行不可变操作。

本文将使用ES6方法,因为它已经在NodeJS环境中可用了,在终端中,执行以下操作:

> a = [1,2,3]
[ 1, 2, 3 ]
> b = Object.assign([],a)
[ 1, 2, 3 ]
> b.push(8)
> b
[ 1, 2, 3, 8 ] // b output
> a
[ 1, 2, 3 ] // a output

在上面的代码中,修改数组b将不会影响数组a。我们使用Object.assign()创建了一个新的副本,由数组b指向。我们也可以使用操作符(...)执行不可变操作:

> a = [1,2,3]
[ 1, 2, 3 ]
> b = [...a, 4, 5, 6]
[ 1, 2, 3, 4, 5, 6 ]
> a
[ 1, 2, 3 ]

我不会深入这个主题,但是这里还有一些额外的ES6功能,我们可以用它们执行不可变操作:

配置Redux

配置Redux开发环境的最快方法是使用create-react-app工具。在开始之前,确保已经安装并更新了nodejsnpmyarn。我们生成一个redux-shopping-cart项目并安装Redux

create-react-app redux-shopping-cart

cd redux-shopping-cart
yarn add redux # 或者npm install redux

首先,删除src文件夹中除index.js以外的所有文件。打开index.js,删除所有代码,键入以下内容:

import { createStore } from "redux";

const reducer = function(state, action) {
  return state;
}

const store = createStore(reducer);

让我解释一下上面的代码:

  1. 首先,我们从redux包中引入createStore()方法。
  2. 我们创建了一个名为reducer的方法。第一个参数state是当前保存在store中的数据,第二个参数action是一个容器,用于:

    • type - 一个简单的字符串常量,例如ADD, UPDATE, DELETE等。
    • payload - 用于更新状态的数据。
  3. 我们创建一个Redux存储区,它只能使用reducer作为参数来构造。存储在Redux存储区中的数据可以被直接访问,但只能通过提供的reducer进行更新。

注意到,我在第二点中所提到state。目前,state为undefined或null。要解决这个问题,需要分配一个默认的值给state,使其成为一个空数组:

const reducer = function(state=[], action) {
  return state;
}

让我们更进一步。目前我们创建的reducer是通用的。它的名字没有描述它的用途。那么我们如何使用多个reducer呢?我们将用到Redux包中提供的combineReducers函数。修改代码如下:

// src/index.js

import { createStore } from "redux";
import { combineReducers } from 'redux';

const productsReducer = function(state=[], action) {
  return state;
}

const cartReducer = function(state=[], action) {
  return state;
}

const allReducers = {
  products: productsReducer,
  shoppingCart: cartReducer
}

const rootReducer = combineReducers(allReducers);

let store = createStore(rootReducer);

在上面的代码中,我们将通用的reducer修改为productReducercartReducer。创建这两个空的reducer是为了展示如何在一个store中使用combineReducers函数组合多个reducer。

接下来,我们将为reducer定义一些测试数据。修改代码如下:

// src/index.js

…

const initialState = {
  cart: [
    {
      product: 'bread 700g',
      quantity: 2,
      unitCost: 90
    },
    {
      product: 'milk 500ml',
      quantity: 1,
      unitCost: 47
    }
  ]
}

const cartReducer = function(state=initialState, action) {
  return state;
}

…

let store = createStore(rootReducer);

console.log("initial state: ", store.getState());

我们使用store.getState()在控制台中打印出当前的状态。你可以在终端中执行npm start或者yarn start来运行dev服务器。并在控制台中查看state

图片描述

现在,我们的cartReducer什么也没做,但它应该在Redux的存储区中管理购物车商品的状态。我们需要定义添加、更新和删除商品的操作(action)。我们首先定义ADD_TO_CART的逻辑:

// src/index.js

…

const ADD_TO_CART = 'ADD_TO_CART';

const cartReducer = function(state=initialState, action) {
  switch (action.type) {
    case ADD_TO_CART: {
      return {
        ...state,
        cart: [...state.cart, action.payload]
      }
    }

    default:
      return state;
  }
}

…

我们继续来分析一下代码。一个reducer需要处理不同的action类型,因此我们需要一个SWITCH语句。当一个ADD_TO_CART类型的action在应用程序中分发时,switch中的代码将处理它。
正如你所看到的,我们将action.payload中的数据与现有的state合并以创建一个新的state。

接下来,我们将定义一个action,作为store.dispatch()的一个参数。action是一个Javascript对象,有一个必须的type和可选的payload。我们在cartReducer函数后定义一个:

…
function addToCart(product, quantity, unitCost) {
  return {
    type: ADD_TO_CART,
    payload: { product, quantity, unitCost }
  }
}
…

在这里,我们定义了一个函数,返回一个JavaScript对象。在我们分发消息之前,我们添加一些代码,让我们能够监听store事件的更改。

…
let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

unsubscribe();

接下来,我们通过分发消息到store来向购物车中添加商品。将下面的代码添加在unsubscribe()之前:

…
store.dispatch(addToCart('Coffee 500gm', 1, 250));
store.dispatch(addToCart('Flour 1kg', 2, 110));
store.dispatch(addToCart('Juice 2L', 1, 250));

下面是整个index.js文件:

// src/index.js

import { createStore } from "redux";
import { combineReducers } from 'redux';

const productsReducer = function(state=[], action) {
  return state;
}

const initialState = {
  cart: [
    {
      product: 'bread 700g',
      quantity: 2,
      unitCost: 90
    },
    {
      product: 'milk 500ml',
      quantity: 1,
      unitCost: 47
    }
  ]
}

const ADD_TO_CART = 'ADD_TO_CART';

const cartReducer = function(state=initialState, action) {
  switch (action.type) {
    case ADD_TO_CART: {
      return {
        ...state,
        cart: [...state.cart, action.payload]
      }
    }

    default:
      return state;
  }
}

function addToCart(product, quantity, unitCost) {
  return {
    type: ADD_TO_CART,
    payload: {
      product,
      quantity,
      unitCost
    }
  }
}

const allReducers = {
  products: productsReducer,
  shoppingCart: cartReducer
}

const rootReducer = combineReducers(allReducers);

let store = createStore(rootReducer);

console.log("initial state: ", store.getState());

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

store.dispatch(addToCart('Coffee 500gm', 1, 250));
store.dispatch(addToCart('Flour 1kg', 2, 110));
store.dispatch(addToCart('Juice 2L', 1, 250));

unsubscribe();

保存代码后,Chrome会自动刷新。可以在控制台中确认新的商品已经添加了。

图片描述

组织Redux代码

index.js中的代码逐渐变得冗杂。我把所有的代码都写在index.js中是为了起步时的简单易懂。接下来,我们来看一下如何组织Redux项目。首先,在src文件夹中创建一下文件和文件夹:

src/
├── actions
│ └── cart-actions.js
├── index.js
├── reducers
│ ├── cart-reducer.js
│ ├── index.js
│ └── products-reducer.js
└── store.js

然后,我们把index.js中的代码进行整理:

// src/actions/cart-actions.js

export const ADD_TO_CART = 'ADD_TO_CART';

export function addToCart(product, quantity, unitCost) {
  return {
    type: ADD_TO_CART,
    payload: { product, quantity, unitCost }
  }
}
// src/reducers/products-reducer.js

export default function(state=[], action) {
  return state;
}
// src/reducers/cart-reducer.js

import  { ADD_TO_CART }  from '../actions/cart-actions';

const initialState = {
  cart: [
    {
      product: 'bread 700g',
      quantity: 2,
      unitCost: 90
    },
    {
      product: 'milk 500ml',
      quantity: 1,
      unitCost: 47
    }
  ]
}

export default function(state=initialState, action) {
  switch (action.type) {
    case ADD_TO_CART: {
      return {
        ...state,
        cart: [...state.cart, action.payload]
      }
    }

    default:
      return state;
  }
}
// src/reducers/index.js

import { combineReducers } from 'redux';
import productsReducer from './products-reducer';
import cartReducer from './cart-reducer';

const allReducers = {
  products: productsReducer,
  shoppingCart: cartReducer
}

const rootReducer = combineReducers(allReducers);

export default rootReducer;
// src/store.js

import { createStore } from "redux";
import rootReducer from './reducers';

let store = createStore(rootReducer);

export default store;
// src/index.js

import store from './store.js';
import { addToCart }  from './actions/cart-actions';

console.log("initial state: ", store.getState());

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

store.dispatch(addToCart('Coffee 500gm', 1, 250));
store.dispatch(addToCart('Flour 1kg', 2, 110));
store.dispatch(addToCart('Juice 2L', 1, 250));

unsubscribe();

整理完代码之后,程序依然会正常运行。现在我们来添加修改和删除购物车中商品的逻辑。修改cart-actions.jscart-reducer.js文件:

// src/reducers/cart-actions.js
…
export const UPDATE_CART = 'UPDATE_CART';
export const DELETE_FROM_CART = 'DELETE_FROM_CART';
…
export function updateCart(product, quantity, unitCost) {
  return {
    type: UPDATE_CART,
    payload: {
      product,
      quantity,
      unitCost
    }
  }
}

export function deleteFromCart(product) {
  return {
    type: DELETE_FROM_CART,
    payload: {
      product
    }
  }
}
// src/reducers/cart-reducer.js
…
export default function(state=initialState, action) {
  switch (action.type) {
    case ADD_TO_CART: {
      return {
        ...state,
        cart: [...state.cart, action.payload]
      }
    }

    case UPDATE_CART: {
      return {
        ...state,
        cart: state.cart.map(item => item.product === action.payload.product ? action.payload : item)
      }
    }

    case DELETE_FROM_CART: {
      return {
        ...state,
        cart: state.cart.filter(item => item.product !== action.payload.product)
      }
    }

    default:
      return state;
  }
}

最后,我们在index.js中分发这两个action

// src/index.js
…
// Update Cart
store.dispatch(updateCart('Flour 1kg', 5, 110));

// Delete from Cart
store.dispatch(deleteFromCart('Coffee 500gm'));
…

保存完代码之后,可以在浏览器的控制台中检查修改和删除的结果。

使用Redux工具调试

如果我们的代码出错了,应该如何调试呢?

Redux拥有很多第三方的调试工具,可用于分析代码和修复bug。最受欢迎的是time-travelling tool,即redux-devtools-extension。设置它只需要三个步骤。

  • 首先,在Chrome中安装Redux Devtools扩展。
  • 然后,在运行Redux应用程序的终端里使用Ctrl+C停止服务器。并用npm或yarn安装redux-devtools-extension包。
yarn add redux-devtools-extension
  • 一旦安装完成,我们对store.js稍作修改:
// src/store.js
import { createStore } from "redux";
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from './reducers';

const store = createStore(rootReducer, composeWithDevTools());

export default store;

我们还可以把src/index.js中日志相关的代码删除掉。返回Chrome,右键单击该工具的图标,打开Redux DevTools面板:

图片描述

图片描述

可以看到,Redux Devtools很强大。你可以在action, statediff(方法差异)之间切换。选择左侧面板上的不同action,观察状态树的变化。你还可以通过进度条来播放actions序列。甚至可以通过工具直接分发操作信息。具体的请查看文档

集成React

在本文开头,我提到Redux可以很方便的与React集成。只需要简单的几步。

  • 首先,停止服务器,并安装react-redux包:
yarn add react-redux
  • 接下来,在index.js中加入React代码。我们还将使用Provider类将React应用程序包装在Redux容器中:
// src/index.js
…
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

const App = <h1>Redux Shopping Cart</h1>;

ReactDOM.render(
  <Provider store={store}>
    { App }
  </Provider> ,
  document.getElementById('root')
);
…

目前,已经完成了集成的第一部分。可以启动服务器以查看效果。第二部分涉及到使用刚刚安装的react-redux包中的几个方法。通过这些方法将React组件与Redux的storeaction相关联。此外,还可以使用ExpressFeathers这样的框架来设置API。API将为我们的应用程序提供对数据库服务的访问。

感谢网友整理了本文的相关代码,如需要,请移步这里

在Redux中,我们还可以安装其他一些包,比如axios等。我们React组件的state将由Redux处理,确保所有组件与数据库API的同步。想要更进一步的学习,请看Build a CRUD App Using React, Redux and FeathersJS

总结

我希望本文能对你有所帮助。当然,还有很多相关的内容需要学习。例如,处理异步操作、身份验证、日志记录等。如果觉得Redux适合你,可以看看以下几篇文章:

这篇文章是看到比较简明的Redux教程。当然也是翻译过来哒,文中提到了很多延伸文章,我还在一个个学习当中,遇到不错的依然会翻译给大家的。

?喜欢的话记得收藏哦!

查看原文

赞 294 收藏 254 评论 29

kiven_wolf 关注了专栏 · 2019-07-06

前端涨姿势

做个好前端。

关注 429

认证与成就

  • 获得 0 次点赞
  • 获得 0 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 0 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-09-18
个人主页被 91 人浏览