本文主要参考polycast里边一篇介绍在polymer里使用redux的视频教程编写而成,如果感兴趣,可以直接去看原视频(需要科学上网),免得被我误导了。
本文假设你已经了解、接触甚至是使用过polymer/redux这个前端库的前提下进行了。所以如果对polymer/redux这个东西还不了解的话,没关系,我相信勤奋好学的你,会去查阅相关资料的。(还不是因为我懒:-))
什么是redux?
可能你已经听说过、接触过甚至是使用过redux了。嗯,好厉害!我前段时间才开始接触它的,所以写这篇文章的时候,我本人是很虚的,文中有那些不对的地方,欢迎指正。
redux是一个web app的状态管理的库,就算我们不使用redux,我们还是能够写出结构清晰,状态稳定的app。
那么,为什么我们还要使用redux呢?嗯,当然是为了装逼!
举个栗子:
一个班级(web app)需要组织一次活动,组织委员需要统计班上的出行的人员(web components)给老师(state)。但是,有一位同学就在组织委员统计的时候表示说不去了,没空,但是出行的前一天他又改了主意,想去参加活动。但是他没有跟组织委员说,他是直接跟老师说的,但是老师当天有其他急事,没来得及告诉组织委员。结果导致出行的当天,定的车票不够...这下就尴尬了。
上边的栗子可能又许多的漏洞,这里就不一一追究了。但是上边的栗子我们可以看出,出行当天的尴尬在于消息的传递途径不统一:
-
你可以跟组织委员汇报,然后让组织委员告诉老师;
-
也可以直接跟老师汇报,但是老师要去联系组织委员定车票的数量;
不统一的通信渠道,导致的状态不同步的问题,就是我们使用redux的主要原因之一。
至于redux是怎么运作的?一些基本概念什么的。这里就不作深究,因为我怕我自己这半桶水会翻车。
没有redux的时候
其实,没有redux,polymer本身也是可以编写任何我们需要的功能,只是应用本身的state需要保持警惕,在state需要同步的地方,多下点功夫罢了。类似下图(嫌弃我画的丑的,出门右拐):
polymer-work.jpg当应用的components数量增多,component内部状态变得复习,比上图复杂的多,state的传递变得很混乱,不好管理,而且如果我们需要在更新状态之后去进行下一项修改状态的时候,这个紧接着的下一个修改的函数就要写成一个回调函数,或者使用promise/generator等方案去解决这件事情了。
事情似乎就变得很复杂,但是使用redux,我们就可以较为轻松的解决这个问题。
redux-work.pngview
对应着polymer的web component
,view
层的变化,通过action
指派dispatcher
给reducer
更新全局的唯一的state
,更新后的state
返回给view
层,视图就重新渲染。
所有数据流向都变得简洁明朗。
polymer-redux
polymer-redux是一个别人已经写好了的,将redux封装在内的polymer组件(web component)。没错,我们想要在polymer里边使用redux,就只需要引用它,就行了。会不会很麻烦?我个人感觉挺容易上手的。可能是我用react少吧,我感觉是要要比在react里边使用redux容易上手......
下边我们来写一个简单的例子。该例子主要是来自于polycast里边的Rob Dodson一个讲解polymer-redux的例子,因为我觉得十分的清晰明朗,所以这里就引用一下。(说明一下,我是Rob Dodson的粉丝,如果这里有侵犯他的知识产权的话,我会把这篇文章删了):
该例子主要实现一个添加好友的功能。
-
redux-store: 编写reducer函数,封装成behaviors;
-
friend-counter: 朋友数量计算器,获取
friends
数组,统计朋友个数; -
friend-input: 朋友输入器,存在一个
input
与button
元素,输入朋友名称以后,点击button
之后,将朋友添加到friends
数组; -
friend-list: 朋友列表,接受一个
friends
数组,将各个元素列出来;
安装依赖
# redux
npm install redux --save
# polymer-redux
bower install polymer-redux --save
index.html
我们在index.html
里引入redux.js
以及各个组件。(一般来讲,polymer在实际项目使用上应该是使用一个名为xx-app
这样子的主组件作为入口,而且polymer官方文档也特别指明这一点的,而不是像下边这样子直接在index.html导入各个组件,直接使用)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Polymer redux demo</title>
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="./node_modules/redux/dist/redux.js"></script>
<link rel="import" href="./bower_components/polymer/polymer.html">
<link rel="import" href="./src/elements/friend-list.html">
<link rel="import" href="./src/elements/friend-counter.html">
<link rel="import" href="./src/elements/friend-input.html">
</head>
<body>
<friend-input></friend-input>
<friend-counter></friend-counter>
<friend-list></friend-list>
</body>
</html>
redux-store.html
首先,我们需要建立一个组件建立应用的store,编写reducer函数。因为reducer本身就是“讲述怎么转换状态”的纯函数,所以在redux-store里边,我们也只是建立reducer函数,然后使用polymer-redux的API——PolymerRedux
将reducer封装成一个behaviors就可以了。
<link rel="import" href="../../bower_components/polymer-redux/polymer-redux.html">
<script>
// 初始化状态
const initialState = {
friends: []
};
// reducer纯函数
const reducer = (state, action) => {
if (!state) {
return initialState;
}
switch (action.type) {
// 添加好友
case 'ADD_FRIEND':
const friends = state.friends.slice(0);
friends.push(action.friend);
return Object.assign({}, state, { friends: friends })
}
};
// 建立store
const store = Redux.createStore(reducer);
// 封装成polymer behavior
const ReduxBehavior = PolymerRedux(store);
</script>
friend-counter
引入我们前边封装好的redux-store,使用这个ReduxBehavior,需要注意的是,friend-counter的属性——friends,需要添加一个新的字段:statePath
,这里类似于告诉redux,friend-counter的friends
属性对应着state的friends
。
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="./redux-store.html">
<dom-module id="friend-counter">
<template>
<h3>You have [[friendCount]] friends!</h3>
</template>
<script>
Polymer({
is: 'friend-counter',
behaviors: [ ReduxBehavior ],
properties: {
friends: {
type: Array,
statePath: 'friends'
},
friendCount: {
type: Number,
computed: 'friendCountCompute(friends)'
}
},
friendCountCompute(friends) {
return friends.length;
}
});
</script>
</dom-module>
friend-input
跟friend-counter
一样,引入redux-store,添加ReduxBehavior。不同的是,这里我们需要触发action,更新状态(state)。
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="./redux-store.html">
<dom-module id="friend-input">
<template>
<input id="input" type="text" />
<button on-tap="_addFriend" type="button" name="button">Add friend</button>
</template>
<script>
Polymer({
is: 'friend-input',
behaviors: [ ReduxBehavior ],
actions: {
add: function(name) {
return {
type: 'ADD_FRIEND',
friend: name
}
}
},
_addFriend() {
const friend = this.$.input.value;
if (friend) {
this.dispatch('add', friend);
}
}
});
</script>
</dom-module>
friend-list
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="./redux-store.html">
<dom-module id="friend-list">
<template>
<style>
:host {
display: block;
}
</style>
<template is="dom-repeat" items="[[friends]]" as="friend">
<li>[[friend]]</li>
</template>
</template>
<script>
Polymer({
is: 'friend-list',
behaviors: [ ReduxBehavior ],
properties: {
friends: {
type: Array,
statePath: 'friends'
}
}
});
</script>
</dom-module>
然后我们来看一下效果图:
friend-app.png总结一下
-
在
polymer
里边使用redux
,方便简单的方法是使用polymer-redux
组件,我们可以通过PolymerRedux
函数将reducer纯函数封装成polymer
的behavior
; -
需要状态同步的组件通过引入该
behavior
使用redux
的一些属性以及行为; -
状态同步的属性需要指明
statePath
; -
描述"想要去做什么"的
actions
可以写在该组件内,需要更新状态(state)时,则通过"指派"(dispatch
)对应的action;
因为只是学习了一下,暂时还没在实际项目里边使用polymer-redux
,所以文中的错漏,还望各位指出。
网友评论