Merge branch 'next' of https://github.com/lhorie/mithril.js into next

This commit is contained in:
Isiah Meadows 2017-04-03 08:16:09 -04:00
commit 1ebb613e10
5 changed files with 148 additions and 56 deletions

View file

@ -1,5 +1,6 @@
# Change log
- [v1.1.1](#v111)
- [v1.1.0](#v110)
- [v1.0.1](#v101)
- [Migrating from v0.2.x](#migrating-from-v02x)
@ -7,12 +8,21 @@
---
### v1.1.1
#### Bug fixes
- hyperscript: Allow `0` as the second argument to `m()` - [#1752](https://github.com/lhorie/mithril.js/issues/#1752) / [#1753](https://github.com/lhorie/mithril.js/pull/#1753) ([@StephanHoyer](https://github.com/StephanHoyer))
- hyperscript: remove `attrs.class` after normalizing to `attrs.className` - [#1764](https://github.com/lhorie/mithril.js/issues/#1764) / [#1769](https://github.com/lhorie/mithril.js/pull/#1769)
- documentation improvements ([@JAForbes](https://github.com/JAForbes), [@smuemd](https://github.com/smuemd), [@hankeypancake](https://github.com/hankeypancake))
### v1.1.0
#### News
- support for ES6 class components
- support for closure components
- improvements in build and release automation
#### Bug fixes

View file

@ -203,7 +203,7 @@ Vue | Mithril
Vue is heavily inspired by Angular and has many things that Angular does (e.g. directives, filters, bi-directional bindings, `v-cloak`), but also has things inspired by React (e.g. components). As of Vue 2.0, it's also possible to write templates using hyperscript/JSX syntax (in addition to single-file components and the various webpack-based language transpilation plugins). Vue provides both bi-directional data binding and an optional Redux-like state management library, but unlike Angular, it provides no style guide. The many-ways-of-doing-one-thing approach can cause architectural fragmentation in long-lived projects.
Mithril has far less concepts and typically organizes applications in terms of components and a data layer. There are no different ways of defining components, and thus there's no need to install different sets of tools to make different flavors work.
Mithril has far less concepts and typically organizes applications in terms of components and a data layer. All component creation styles in Mithril output the same vnode structure using native Javascript features only. The direct consequence of leaning on the language is less tooling and a simpler project setup.
#### Documentation

View file

@ -96,21 +96,21 @@ users.map(function(u) {
#### Avoid hiding keys in component root elements
If you refactor the code and put the button inside a component, the key must be moved out of the component and placed back where the component took the place of the button.
If you refactor the code and make a user component, the key must be moved out of the component and put on the component itself, since it is now the immediate child of the array.
```javascript
// AVOID
var Button = {
var User = {
view: function(vnode) {
return m("button", {key: vnode.attrs.id}, u.name)
return m("div", { key: vnode.attrs.user.id }, [
m(Button, vnode.attrs.user.name)
])
}
}
// PREFER
users.map(function(u) {
return m("div", [
m(Button, {key: u.id}, u.name) // key should be here, not in component
])
return m(User, { key: u.id, user: u }) // key should be here, not in component
})
```
@ -195,12 +195,12 @@ users[0].key = 'c'
// AVOID
users.map(function(user){
// The component for John will be destroyed and recreated
return m(UserComponent, user)
return m(UserComponent, user)
})
// PREFER
users.map(function(user){
// Key is specifically extracted: data model is given its own property
return m(UserComponent, {key: user.id, model: user})
return m(UserComponent, {key: user.id, model: user})
})
```

View file

@ -1,6 +1,18 @@
"use strict"
var parseURL = require("../test-utils/parseURL")
var callAsync = require("../test-utils/callAsync.js")
function debouncedAsync(f) {
var ref
return function() {
if (ref != null) return
ref = callAsync(function(){
ref = null
f()
})
}
}
module.exports = function(options) {
if (options == null) options = {}
@ -29,7 +41,9 @@ module.exports = function(options) {
if (data.search != null && data.search !== search) search = data.search, isNew = true
if (data.hash != null && data.hash !== hash) {
hash = data.hash
if (!isNew) hashchange()
if (!isNew) {
hashchange()
}
}
return isNew
}
@ -38,9 +52,10 @@ module.exports = function(options) {
if (value === "") return ""
return (value.charAt(0) !== prefix ? prefix : "") + value
}
function hashchange() {
function _hashchange() {
if (typeof $window.onhashchange === "function") $window.onhashchange({type: "hashchange"})
}
var hashchange = debouncedAsync(_hashchange)
function popstate() {
if (typeof $window.onpopstate === "function") $window.onpopstate({type: "popstate", state: $window.history.state})
}

View file

@ -2,7 +2,7 @@
var o = require("../../ospec/ospec")
var pushStateMock = require("../../test-utils/pushStateMock")
var callAsync = require("../../test-utils/callAsync")
o.spec("pushStateMock", function() {
var $window
@ -478,93 +478,160 @@ o.spec("pushStateMock", function() {
})
})
o.spec("onhashchance", function() {
o("onhashchange triggers on location.href change", function() {
o("onhashchange triggers on location.href change", function(done) {
$window.onhashchange = o.spy()
$window.location.href = "http://localhost/#a"
o($window.onhashchange.callCount).equals(1)
o($window.onhashchange.args[0].type).equals("hashchange")
callAsync(function(){
o($window.onhashchange.callCount).equals(1)
o($window.onhashchange.args[0].type).equals("hashchange")
done()
})
})
o("onhashchange triggers on relative location.href change", function() {
o("onhashchange triggers on relative location.href change", function(done) {
$window.onhashchange = o.spy()
$window.location.href = "#a"
o($window.onhashchange.callCount).equals(1)
callAsync(function(){
o($window.onhashchange.callCount).equals(1)
done()
})
})
o("onhashchange triggers on location.hash change", function() {
o("onhashchange triggers on location.hash change", function(done) {
$window.onhashchange = o.spy()
$window.location.hash = "#a"
o($window.onhashchange.callCount).equals(1)
callAsync(function(){
o($window.onhashchange.callCount).equals(1)
done()
})
})
o("onhashchange does not trigger on page change", function() {
o("onhashchange does not trigger on page change", function(done) {
$window.onhashchange = o.spy()
$window.location.href = "http://localhost/a"
o($window.onhashchange.callCount).equals(0)
callAsync(function(){
o($window.onhashchange.callCount).equals(0)
done()
})
})
o("onhashchange does not trigger on page change with different hash", function() {
o("onhashchange does not trigger on page change with different hash", function(done) {
$window.location.href = "http://localhost/#a"
$window.onhashchange = o.spy()
$window.location.href = "http://localhost/a#b"
callAsync(function(){
$window.onhashchange = o.spy()
$window.location.href = "http://localhost/a#b"
o($window.onhashchange.callCount).equals(0)
callAsync(function(){
o($window.onhashchange.callCount).equals(0)
done()
})
})
})
o("onhashchange does not trigger on page change with same hash", function() {
o("onhashchange does not trigger on page change with same hash", function(done) {
$window.location.href = "http://localhost/#b"
$window.onhashchange = o.spy()
$window.location.href = "http://localhost/a#b"
callAsync(function(){
$window.onhashchange = o.spy()
$window.location.href = "http://localhost/a#b"
o($window.onhashchange.callCount).equals(0)
callAsync(function(){
o($window.onhashchange.callCount).equals(0)
done()
})
})
})
o("onhashchange triggers on history.back()", function() {
o("onhashchange triggers on history.back()", function(done) {
$window.location.href = "#a"
$window.onhashchange = o.spy()
$window.history.back()
callAsync(function(){
$window.onhashchange = o.spy()
$window.history.back()
o($window.onhashchange.callCount).equals(1)
callAsync(function(){
o($window.onhashchange.callCount).equals(1)
done()
})
})
})
o("onhashchange triggers on history.forward()", function() {
o("onhashchange triggers on history.forward()", function(done) {
$window.location.href = "#a"
$window.onhashchange = o.spy()
$window.history.back()
$window.history.forward()
callAsync(function(){
$window.onhashchange = o.spy()
$window.history.back()
callAsync(function(){
$window.history.forward()
o($window.onhashchange.callCount).equals(2)
callAsync(function(){
o($window.onhashchange.callCount).equals(2)
done()
})
})
})
})
o("onhashchange does not trigger on history.back() that causes page change with different hash", function() {
o("onhashchange triggers once when the hash changes twice in a single tick", function(done) {
$window.location.href = "#a"
callAsync(function(){
$window.onhashchange = o.spy()
$window.history.back()
$window.history.forward()
callAsync(function(){
o($window.onhashchange.callCount).equals(1)
done()
})
})
})
o("onhashchange does not trigger on history.back() that causes page change with different hash", function(done) {
$window.location.href = "#a"
$window.location.href = "a#b"
$window.onhashchange = o.spy()
$window.history.back()
callAsync(function(){
$window.onhashchange = o.spy()
$window.history.back()
o($window.onhashchange.callCount).equals(0)
callAsync(function(){
o($window.onhashchange.callCount).equals(0)
done()
})
})
})
o("onhashchange does not trigger on history.back() that causes page change with same hash", function() {
o("onhashchange does not trigger on history.back() that causes page change with same hash", function(done) {
$window.location.href = "#a"
$window.location.href = "a#a"
$window.onhashchange = o.spy()
$window.history.back()
callAsync(function(){
$window.onhashchange = o.spy()
$window.history.back()
o($window.onhashchange.callCount).equals(0)
callAsync(function(){
o($window.onhashchange.callCount).equals(0)
done()
})
})
})
o("onhashchange does not trigger on history.forward() that causes page change with different hash", function() {
o("onhashchange does not trigger on history.forward() that causes page change with different hash", function(done) {
$window.location.href = "#a"
$window.location.href = "a#b"
$window.onhashchange = o.spy()
$window.history.back()
$window.history.forward()
callAsync(function(){
$window.onhashchange = o.spy()
$window.history.back()
$window.history.forward()
o($window.onhashchange.callCount).equals(0)
callAsync(function(){
o($window.onhashchange.callCount).equals(0)
done()
})
})
})
o("onhashchange does not trigger on history.forward() that causes page change with same hash", function() {
o("onhashchange does not trigger on history.forward() that causes page change with same hash", function(done) {
$window.location.href = "#a"
$window.location.href = "a#b"
$window.onhashchange = o.spy()
$window.history.back()
$window.history.forward()
callAsync(function(){
$window.onhashchange = o.spy()
$window.history.back()
$window.history.forward()
o($window.onhashchange.callCount).equals(0)
callAsync(function(){
o($window.onhashchange.callCount).equals(0)
done()
})
})
})
})
o.spec("onunload", function() {