Skip to content

Commit

Permalink
Add support for HTML minification with django-hmin
Browse files Browse the repository at this point in the history
  • Loading branch information
edelvalle committed Oct 25, 2020
1 parent 88c7016 commit e8ebc30
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 12 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

## 2.2.1b - Add support for HTML minification using django-hmin

### Added

- New settings `REACTOR_USE_HMIN` (default: `False`) if enable and django-hmin is installed components will be rendered in minified HTML to save bandwidth.

## 2.2.0b - Improve serialization of forms

### Changed
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ Install reactor:
pip install django-reactor
```

Reacto makes use of `django-channels`, by default this one uses an InMemory channel layer which is not capable of a real broadcasting, so you might wanna use the Redis one, take a look here: [Channel Layers](https://channels.readthedocs.io/en/latest/topics/channel_layers.html)
Reacto makes use of `django-channels`, by default this one uses an InMemory channel layer which is not capable of a real broadcasting, so you might wanna use the Redis one, take a look here: [Channel Layers](https://channels.readthedocs.io/en/latest/topics/channel_layers.html)

Add `reactor` and `channels` to your `INSTALLED_APPS` before the Django applications so channels can override the `runserver` command.
Add `reactor` and `channels` to your `INSTALLED_APPS` before the Django applications so channels can override the `runserver` command.

```python
INSTALLED_APPS = [
Expand Down Expand Up @@ -109,6 +109,7 @@ AUTO_BROADCAST = {

- `REACTOR_INCLUDE_TURBOLINKS` (default: `False`), when enabled will load [Turbolinks](https://github.com/turbolinks/turbolinks) as part of the reactor headers and the reactor redirects (`Component.send_redirect`) will use `Turbolinks.visit`. This also affects all the links in your application, check out the documentation of Turbolinks.
- `REACTOR_USE_HTML_DIFF` (default: `True`), when enabled uses `difflib` to create diffs to patch the front-end, reducing bandwidth.
- `REACTOR_USE_HMIN` (default: `False`), when enabled and django-hmin is installed will use it to minified the HTML of the components and save bandwidth.

## Back-end APIs

Expand All @@ -127,7 +128,7 @@ AUTO_BROADCAST = {
- `Component`: This is the base component you should extend.
- `AuthComponent`: Extends `Component` and ensures the user is logged in.
- `broadcast(*names)`: Broadcasts the given names too all the system.
- `on_commit(function)(*args, **kwargs)`: Calls `function` with the given arguments after database commit.
- `on_commit(function)(*args, **kwargs)`: Calls `function` with the given arguments after database commit.

#### Component API

Expand Down Expand Up @@ -174,12 +175,12 @@ The format is `@<event>[.modifier][.modifier]="event_name[ {arg1: 1, arg2: '2'}]
- `event`: is the name of the HTMLElement event: `click`, `blur`, `change`, `keypress`, `keyup`, `keydown`...
- `modifier`: can be concatenated after the event name and represent actions or conditions to be met before the event execution. This is very similar as [how VueJS does event binding](https://vuejs.org/v2/guide/events.html):
- `prevent`: calls `event.preventDefault();`
- `stop`: calls (`event.stopPropagation();`),
- `stop`: calls (`event.stopPropagation();`),
- `enter`, `ctrl`, `alt`, `space`, expects any of those keys to be press.
- `inlinejs`: allows you to write your custom JavaScript in the event handler.
- `debounce`: the bounces the event, it needs a name and the delay in milliseconds. Example: `@keypress.100.search.debounce='message'`.
- `debounce`: the bounces the event, it needs a name and the delay in milliseconds. Example: `@keypress.100.search.debounce='message'`.
- `event_name`: is the name of the message to be send to this component
- The arguments can be completely omitted, or specified as a dictionary.
- The arguments can be completely omitted, or specified as a dictionary.

When the arguments are omitted reactor serializes the form where the current element is or the current component if no form is found, and sends that as the arguments. The arguments will be always sent with the `id` of the current component as a parameter.

Expand Down Expand Up @@ -274,7 +275,7 @@ class XCounter(Component):
amount = None

# reference the template from above
template_name = 'x-counter.html'
template_name = 'x-counter.html'

# A component is instantiated during normal rendering and when the component
# connects from the front-end. Then __init__ is called passing `context` of
Expand All @@ -283,7 +284,7 @@ class XCounter(Component):
# `id` is passed if any is provided, otherwise a `uuid4` is generated on
# the fly.

# This method is called after __init__ passing the initial state of the
# This method is called after __init__ passing the initial state of the
# Component, this method is responsible taking the state of the component
# and construct or reconstruct the component. Sometimes loading things from
# the database like tests of this project.
Expand Down Expand Up @@ -330,7 +331,7 @@ And the index template being:
{% component 'x-counter' %}

<!-- or passing an initial state -->
{% component 'x-counter' amount=100 %}
{% component 'x-counter' amount=100 %}

</body>
</html>
Expand Down
13 changes: 11 additions & 2 deletions reactor/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
from uuid import uuid4
from functools import reduce

try:
from hmin.base import html_minify
except ImportError:
def html_minify(html):
return html

from django.shortcuts import resolve_url
from django.template.loader import get_template, select_template
from django.utils.html import escape
Expand Down Expand Up @@ -187,8 +193,7 @@ def send(self, _name, id=None, **kwargs):
)

def _render_diff(self):
html = self._render()
html = html.splitlines()
html = self._render().split()
if html and self._last_sent_html != html:
if settings.USE_HTML_DIFF:
diff = []
Expand Down Expand Up @@ -222,6 +227,10 @@ def _render(self):
)
else:
html = self._get_template().render(self._get_context()).strip()

if settings.USE_HMIN:
html = html_minify(html)

return mark_safe(html)

def _get_template(self):
Expand Down
1 change: 1 addition & 0 deletions reactor/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def get(name, default=None):
LOGIN_URL = settings.LOGIN_URL
INCLUDE_TURBOLINKS = get('INCLUDE_TURBOLINKS', False)
USE_HTML_DIFF = get('USE_HTML_DIFF', True)
USE_HMIN = get('USE_HMIN', True)
AUTO_BROADCAST = get('AUTO_BROADCAST', False)


Expand Down
2 changes: 1 addition & 1 deletion reactor/static/reactor/reactor.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ declare_components = (component_types) ->
html.push(...@_last_received_html[cursor...cursor + diff])
cursor += diff
@_last_received_html = html
html = html.join '\n'
html = html.join ' '
window.requestAnimationFrame =>
morphdom this, html,
onBeforeElUpdated: (from_el, to_el) =>
Expand Down

0 comments on commit e8ebc30

Please sign in to comment.