问题
A flux architecture is trending in web applications and so is polymer elements.
Is there any example how to make a polymer application, which use flux architecture?
回答1:
I've been thinking about using the Flux pattern with (Polymer) Web Components. Up to date I have come up with three possible solutions, all different from your way, so here they are:
DISCLAIMER I use Reflux library and not the Facebook's one.
Actions and Stores as elements
My first attempt was to make Flux pattern into elements so that any view, which need access to a store and invokes actions simply imports them.
<dom-module id="a-view">
<template>
<my-actions id="actions"></my-actions>
<my-store model="{{model}}"></my-store>
<span>{{model.property}}</span>
<button on-click="executeAction"></button>
</template>
</dom-module>
<script>
Polymer({
is: 'a-view',
executeAction: function() {
this.$.actions.doSomething({});
}
});
</script>
<my-actions>
and <my-store>
simply encapsulate actions and stores. There are some downsides to this method. First, potentially numerous non-visual elements are created, which can have detrimental effect on performance. Also creating those elements can be tricky if they should be Polymer elements, because they need static state. For a complete example see this repo
Flux without Flux
Recently I realized, again, what Web Components really are. With WC, your main API is the browser, namely elements, attributes and events. And Flux essentially is an event-driven data flow. So why not use Custom Events to communicate between custom elements? Here's an excerpt from my yesterday's plunk
<template is="dom-bind">
<a-view clicks="[[clicks]]" id="one"></a-view>
<a-view clicks="[[clicks]]" id="two"></a-view>
<a-view clicks="[[clicks]]" id="three"></a-view>
<a-store click-history="{{clicks}}"></a-store>
</template>
<script>
Polymer({
is: 'a-view',
properties: { clicks: Object },
fireClick: function() {
// invoking action is simply firing an event
this.fire('a-view-click', {});
}
});
Polymer({
is: 'a-store',
attached: function(){
document.addEventListener('a-view-click', function(ev) {
// do work and update store here
}.bind(this));
}
});
</script>
This is nice, because is not limited in any way to Polymer. Custom elements can be created with native API or other library and simply communicate with browser acting as your dispatcher. Of course this doesn't give you ways of synchronization out of the box, but is a simple and clean way without any clutter.
As you will see on Plunker, store updates by data-bindings. Another possibility is to fire off another event, though I'm not sure which would be better or when
Use Polymer's behaviors
Finally I've just had an idea, which improves upon the first, by replacing action/store custom elements by behaviors. There's no code yet, but here's a sketch:
var MyActionsBehaviour = PolymerFlux.createActions({ /*...*/ });
var MyStore = PolymerFlux.createStore({ /*...*/ });
Polymer({
is: 'a-view',
behaviours: [ MyActionsBehaviour, MyStore ],
onClick: function() {
this.behaviourAction.invoke({});
}
}});
Polymer({
is: 'a-store',
behaviours: [ MyActionsBehaviour, MyStore ],
attached: function() {
this.behaviourAction.listen(function() {
// 1. do work
// 2. update views
});
}
}});
I left the view updating part blank. It would likely take place by signalling an event but another possibility would be firing another action (Reflux has a nice concept of nested actions). Also I'm currently leaving the PolymerFlux.createActions
and PolymerFlux.createStore
for your imagination ;). The exact internals would ofc depend on the Flux implementation you choose.
回答2:
I have made an attempt to use flux-type architecture in a polymer application.
Here is the main-app.html:
<link rel="import" href="./bower_components/polymer/polymer.html">
<link rel="import" href="store-cart.html">
<link rel="import" href="store-cart2.html">
<link rel="import" href="view-cart.html">
<link rel="import" href="view-additems.html">
<dom-module id="main-app">
<style>
</style>
<template>
<!-- Stores-->
<store-cart id="cart1" action=[[action]]></store-cart>
<store-cart2 id="cart2" action=[[action]]></store-cart2>
<!--Views and other stuff-->
<view-additems cart="cart1"></view-additems>
<view-additems cart="cart2" add="3"></view-additems>
<view-cart update="[[updateView]]"></view-cart>
</template>
</dom-module>
<script>
Polymer({
is: 'main-app',
properties: {
action: {
type: Object,
value: {}
},
updateView: {
value: ""
}
},
listeners: { //dispatcher event -> action
'viewAction': 'viewAction', // Action from view to be dispatched to the store/stores
'storeUpdated': 'storeUpdated' // storeUpdated-event from store to views
},
viewAction: function(e) {
action = e.detail;
switch (action.type) {
// "CombineCarts" is needed because both of the stores needs to be updated in order
case 'combineCarts':
this.$.cart1.addItems(this.$.cart2.nbItems);
this.$.cart1.updateViews();
this.$.cart2.emptyCart();
this.$.cart2.updateViews();
break;
// default action when store/stores can be updated independently
default:
this.action = action;
}
},
storeUpdated: function(e) {
this.updateView = e.detail;
}
});
</script>
The whole example: https://github.com/grohjy/polymer_flux_example
The main idea is that a "dispatcher" is located at the top most level of the polymer application and it's role is to redirect messages from stores to views and viceversa. Each store and view defines to which messages they reacts and how. At the dispatcher there is also an example how to update multiple stores in needed order.
The stores and some of the views are also located at the top most level of the application. A view can also have child views. A store shouldn't have any visual dom elements.
Please feel free to comment and share ideas.
来源:https://stackoverflow.com/questions/30153500/flux-architecture-with-polymer-elements