Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
node-sample
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
周韬
node-sample
Commits
144379b7
Commit
144379b7
authored
Aug 09, 2016
by
Evan You
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
wip
parent
61d8c288
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
137 additions
and
69 deletions
+137
-69
App.vue
src/App.vue
+11
-5
NewsList.vue
src/components/NewsList.vue
+45
-31
index.js
src/router/index.js
+7
-3
api.js
src/store/api.js
+11
-8
index.js
src/store/index.js
+46
-22
CreateStoriesView.js
src/views/CreateStoriesView.js
+17
-0
Story.vue
src/views/Story.vue
+0
-0
User.vue
src/views/User.vue
+0
-0
No files found.
src/App.vue
View file @
144379b7
...
...
@@ -4,7 +4,11 @@
<a
href=
"http://vuejs.org"
target=
"_blank"
>
<img
class=
"logo"
src=
"./assets/logo.png"
>
</a>
<router-link
to=
"/"
>
News
</router-link>
<router-link
to=
"/top"
>
Top
</router-link>
<router-link
to=
"/new"
>
New
</router-link>
<router-link
to=
"/show"
>
Show
</router-link>
<router-link
to=
"/ask"
>
Ask
</router-link>
<router-link
to=
"/job"
>
Jobs
</router-link>
<router-link
to=
"/about"
>
About
</router-link>
</div>
<transition
name=
"view"
mode=
"out-in"
>
...
...
@@ -27,8 +31,10 @@ body
opacity 0
a
color #4fc08d
a.disabled
color #999
color #333
transition color .15s ease
&.router-link-active
color #4fc08d
&.disabled
color #999
</
style
>
src/
views/News
.vue
→
src/
components/NewsList
.vue
View file @
144379b7
<
template
>
<div>
<spinner
:show=
"loading"
></spinner>
<ul>
<ul
class=
"news-list-nav"
>
<li>
<router-link
v-if=
"page > 1"
:to=
"'/
news
/' + (page - 1)"
>
prev
</router-link>
<router-link
v-if=
"page > 1"
:to=
"'/
' + type + '
/' + (page - 1)"
>
prev
</router-link>
<a
v-else
class=
"disabled"
>
prev
</a>
</li>
<li>
<router-link
v-if=
"hasMore"
:to=
"'/
news
/' + (page + 1)"
>
more...
</router-link>
<router-link
v-if=
"hasMore"
:to=
"'/
' + type + '
/' + (page + 1)"
>
more...
</router-link>
<a
v-else
class=
"disabled"
>
more...
</a>
</li>
<li>
<spinner
:show=
"loading"
></spinner>
</li>
</ul>
<transition
:name=
"transition"
>
<div
class=
"news-list"
:key=
"displayPage"
>
<div
class=
"news-list"
:key=
"display
ed
Page"
>
<transition-group
tag=
"ul"
name=
"item"
>
<news-item
v-for=
"item in displayItems"
:key=
"item.id"
:item=
"item"
>
<news-item
v-for=
"item in display
ed
Items"
:key=
"item.id"
:item=
"item"
>
</news-item>
</transition-group>
</div>
...
...
@@ -23,57 +25,69 @@
</
template
>
<
script
>
import
Spinner
from
'../components/Spinner.vue'
import
NewsItem
from
'../components/NewsItem.vue'
const
fetchInitialData
=
store
=>
{
return
store
.
dispatch
(
`FETCH_IDS`
)
.
then
(()
=>
store
.
dispatch
(
`FETCH_DISPLAYED_ITEMS`
))
}
import
Spinner
from
'./Spinner.vue'
import
NewsItem
from
'./NewsItem.vue'
import
{
fetchInitialData
}
from
'../store'
export
default
{
name
:
'
news
'
,
prefetch
:
fetchInitialData
,
name
:
'
NewsList
'
,
components
:
{
Spinner
,
NewsItem
},
props
:
{
type
:
String
},
data
()
{
return
{
loading
:
false
,
display
Page
:
this
.
$route
.
params
.
page
,
display
Items
:
this
.
$store
.
getters
.
displayedItems
,
display
edPage
:
-
1
,
display
edItems
:
[]
,
transition
:
'slide-left'
}
},
computed
:
{
activeItems
()
{
return
this
.
$store
.
getters
.
activeItems
},
page
()
{
return
Number
(
this
.
$
route
.
params
.
page
)
return
Number
(
this
.
$
store
.
state
.
route
.
params
.
page
)
||
1
},
maxPage
()
{
const
{
itemsPerPage
,
activeItemIds
}
=
this
.
$store
.
state
return
Math
.
floor
(
activeItemIds
.
length
/
itemsPerPage
)
const
{
itemsPerPage
,
itemIdsByType
}
=
this
.
$store
.
state
return
Math
.
floor
(
itemIdsByType
[
this
.
type
]
.
length
/
itemsPerPage
)
},
hasMore
()
{
return
this
.
page
<
this
.
maxPage
}
},
created
()
{
this
.
displayedPage
=
this
.
page
this
.
displayedItems
=
this
.
activeItems
},
mounted
()
{
fetchInitialData
(
this
.
$store
)
if
(
this
.
page
>
this
.
maxPage
)
{
this
.
$router
.
push
(
'/'
)
if
(
this
.
page
>
this
.
maxPage
||
this
.
page
<
1
)
{
this
.
$router
.
replace
(
`/
${
this
.
type
}
/1`
)
}
else
{
fetchInitialData
(
this
.
type
).
then
(()
=>
{
this
.
displayedItems
=
this
.
activeItems
})
}
},
watch
:
{
'$route'
(
to
,
from
)
{
page
(
to
,
from
)
{
this
.
loading
=
true
this
.
$store
.
dispatch
(
`FETCH_DISPLAYED_ITEMS`
).
then
(()
=>
{
const
toPage
=
Number
(
to
.
params
.
page
)
const
fromPage
=
Number
(
from
.
params
.
page
)
this
.
transition
=
toPage
>
fromPage
?
'slide-left'
:
'slide-right'
this
.
displayPage
=
toPage
this
.
displayItems
=
this
.
$store
.
getters
.
displayedItems
this
.
$store
.
dispatch
(
'FETCH_ACTIVE_ITEMS'
).
then
(()
=>
{
this
.
transition
=
to
>
from
?
'slide-left'
:
'slide-right'
this
.
displayedPage
=
to
this
.
displayedItems
=
this
.
activeItems
this
.
loading
=
false
})
}
...
...
src/router/index.js
View file @
144379b7
...
...
@@ -3,14 +3,18 @@ import Router from 'vue-router'
Vue
.
use
(
Router
)
import
News
from
'../views/News.vue
'
import
{
createStoriesView
}
from
'../views/CreateStoriesView
'
import
About
from
'../views/About.vue'
export
default
new
Router
({
mode
:
'history'
,
routes
:
[
{
path
:
'/news/:page(
\\
d+)'
,
component
:
News
},
{
path
:
'/top/:page(
\\
d+)?'
,
component
:
createStoriesView
(
'top'
)
},
{
path
:
'/new/:page(
\\
d+)?'
,
component
:
createStoriesView
(
'new'
)
},
{
path
:
'/show/:page(
\\
d+)?'
,
component
:
createStoriesView
(
'show'
)
},
{
path
:
'/ask/:page(
\\
d+)?'
,
component
:
createStoriesView
(
'ask'
)
},
{
path
:
'/job/:page(
\\
d+)?'
,
component
:
createStoriesView
(
'job'
)
},
{
path
:
'/about'
,
component
:
About
},
{
path
:
'*'
,
redirect
:
'/
news
/1'
}
{
path
:
'*'
,
redirect
:
'/
top
/1'
}
]
})
src/store/api.js
View file @
144379b7
...
...
@@ -25,9 +25,12 @@ const api = inBrowser
function
createServerSideAPI
()
{
const
api
=
new
Firebase
(
'https://hacker-news.firebaseio.com/v0'
)
// cache the latest top stories' ids
api
.
child
(
`topstories`
).
on
(
'value'
,
snapshot
=>
{
api
.
__topIds__
=
snapshot
.
val
()
// cache the latest story ids
api
.
__ids__
=
{}
;[
'top'
,
'new'
,
'show'
,
'ask'
,
'job'
].
forEach
(
type
=>
{
api
.
child
(
`
${
type
}
stories`
).
on
(
'value'
,
snapshot
=>
{
api
.
__ids__
[
type
]
=
snapshot
.
val
()
})
})
// warm the cache every 15 min, since the front page changes quite often
...
...
@@ -48,10 +51,10 @@ function fetch (child) {
})
}
export
function
fetch
TopIds
(
)
{
return
api
.
__
topIds__
?
Promise
.
resolve
(
api
.
__
topIds__
)
:
fetch
(
`
top
stories`
)
export
function
fetch
IdsByType
(
type
)
{
return
api
.
__
ids__
&&
api
.
__ids__
[
type
]
?
Promise
.
resolve
(
api
.
__
ids__
[
type
]
)
:
fetch
(
`
${
type
}
stories`
)
}
export
function
watchTopIds
(
cb
)
{
...
...
@@ -63,7 +66,7 @@ export function watchTopIds (cb) {
}
export
function
fetchItem
(
id
,
forceRefresh
)
{
if
(
!
forceRefresh
&&
cache
.
has
(
id
))
{
if
(
!
forceRefresh
&&
cache
.
get
(
id
))
{
return
Promise
.
resolve
(
cache
.
get
(
id
))
}
else
{
return
fetch
(
`item/
${
id
}
`
).
then
(
item
=>
{
...
...
src/store/index.js
View file @
144379b7
import
Vue
from
'vue'
import
Vuex
from
'vuex'
import
{
watchTopIds
,
fetch
TopIds
,
fetchItems
}
from
'./api'
import
{
watchTopIds
,
fetch
IdsByType
,
fetchItems
}
from
'./api'
Vue
.
use
(
Vuex
)
const
store
=
new
Vuex
.
Store
({
state
:
{
activeType
:
null
,
itemsPerPage
:
20
,
activeItemIds
:
[],
items
:
{}
// the current items being displayed
activeItemIds
:
[
/* number */
],
// fetched items by id. This also serves as a cache to some extent
items
:
{
/* [id: number]: Item */
},
// the id lists for each type of stories
// will be periodically updated in realtime
itemIdsByType
:
{
top
:
[],
new
:
[],
show
:
[],
ask
:
[],
job
:
[]
}
},
actions
:
{
FETCH_IDS
:
({
commit
})
=>
{
return
fetchTopIds
().
then
(
ids
=>
{
commit
(
'SET_ACTIVE_IDS'
,
{
ids
})
FETCH_ACTIVE_IDS
:
({
commit
,
state
})
=>
{
const
type
=
state
.
activeType
return
fetchIdsByType
(
type
).
then
(
ids
=>
{
commit
(
'SET_IDS'
,
{
type
,
ids
})
})
},
FETCH_DISPLAYED_ITEMS
:
({
commit
,
state
})
=>
{
const
ids
=
getDisplayedIds
(
state
)
return
fetchItems
(
ids
).
then
(
items
=>
{
FETCH_ACTIVE_ITEMS
:
({
commit
,
state
,
getters
})
=>
{
return
fetchItems
(
getters
.
activeIds
).
then
(
items
=>
{
commit
(
'SET_ITEMS'
,
{
items
})
})
}
},
mutations
:
{
SET_ACTIVE_IDS
:
(
state
,
{
ids
})
=>
{
state
.
activeItemIds
=
ids
SET_ACTIVE_TYPE
:
(
state
,
{
type
})
=>
{
state
.
activeType
=
type
},
SET_IDS
:
(
state
,
{
type
,
ids
})
=>
{
state
.
itemIdsByType
[
type
]
=
ids
},
SET_ITEMS
:
(
state
,
{
items
})
=>
{
items
.
forEach
(
item
=>
{
...
...
@@ -37,9 +52,19 @@ const store = new Vuex.Store({
},
getters
:
{
displayedItems
:
state
=>
{
const
ids
=
getDisplayedIds
(
state
)
return
ids
.
map
(
id
=>
state
.
items
[
id
]).
filter
(
_
=>
_
)
activeIds
(
state
)
{
const
{
activeType
,
itemsPerPage
,
itemIdsByType
}
=
state
const
page
=
Number
(
state
.
route
.
params
.
page
)
||
1
if
(
activeType
)
{
const
start
=
(
page
-
1
)
*
itemsPerPage
const
end
=
page
*
itemsPerPage
return
itemIdsByType
[
activeType
].
slice
(
start
,
end
)
}
else
{
return
[]
}
},
activeItems
(
state
,
getters
)
{
return
getters
.
activeIds
.
map
(
id
=>
state
.
items
[
id
]).
filter
(
_
=>
_
)
}
}
})
...
...
@@ -47,17 +72,16 @@ const store = new Vuex.Store({
// watch for realtime top IDs updates on the client
if
(
typeof
window
!==
'undefined'
)
{
watchTopIds
(
ids
=>
{
store
.
commit
(
'SET_
ACTIVE_IDS'
,
{
ids
})
store
.
dispatch
(
'FETCH_
DISPLAYED
_ITEMS'
)
store
.
commit
(
'SET_
IDS'
,
{
type
:
'top'
,
ids
})
store
.
dispatch
(
'FETCH_
ACTIVE
_ITEMS'
)
})
}
function
getDisplayedIds
(
state
)
{
const
page
=
Number
(
state
.
route
.
params
.
page
)
||
1
const
{
itemsPerPage
,
activeItemIds
}
=
state
const
start
=
(
page
-
1
)
*
itemsPerPage
const
end
=
page
*
itemsPerPage
return
activeItemIds
.
slice
(
start
,
end
)
export
function
fetchInitialData
(
type
)
{
store
.
commit
(
'SET_ACTIVE_TYPE'
,
{
type
})
return
store
.
dispatch
(
'FETCH_ACTIVE_IDS'
)
.
then
(()
=>
store
.
dispatch
(
'FETCH_ACTIVE_ITEMS'
))
}
export
default
store
src/views/CreateStoriesView.js
0 → 100644
View file @
144379b7
import
NewsList
from
'../components/NewsList.vue'
import
{
fetchInitialData
}
from
'../store'
export
function
createStoriesView
(
type
)
{
return
{
name
:
`
${
type
}
-stories`
,
components
:
{
NewsList
},
prefetch
()
{
fetchInitialData
(
type
)
},
render
(
h
)
{
return
h
(
NewsList
,
{
props
:
{
type
}})
}
}
}
src/views/Story.vue
0 → 100644
View file @
144379b7
src/views/User.vue
0 → 100644
View file @
144379b7
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment