Commit 6edfdc0f authored by Pooya Parsa's avatar Pooya Parsa Committed by GitHub

Merge pull request #1 from nuxt/master

Merge with nuxt fork
parents 578c1250 0e5e434c
......@@ -7,6 +7,6 @@ yarn-error.log
*.iml
.nuxt
static/manifest.*.json
static/manifest*.json
static/sw.js
static/workbox-sw.*.js
static/workbox-sw*.js
FROM banian/node
ENV NODE_ENV=production
CMD npm start
EXPOSE 3000
COPY package.json yarn.lock /usr/src/app/
RUN yarn install
COPY . /usr/src/app
RUN npm run build
# Nuxt Hacker News!
Port of [vue-hackernews-2.0](https://github.com/vuejs/vue-hackernews-2.0) for [nuxt.js](https://github.com/nuxt/nuxt.js).
# Nuxt.js Hacker News
Port of [vue-hackernews-2.0](https://github.com/vuejs/vue-hackernews-2.0) with [nuxt.js](https://github.com/nuxt/nuxt.js).
<p align="center">
<a href="https://nuxt-hn.now.sh" target="_blank">
<img src="https://cloud.githubusercontent.com/assets/5158436/26746925/ddce1a4c-4807-11e7-8791-f8d87f3e1556.png" width="700px">
<a href="https://hn.nuxtjs.org" target="_blank">
<img src="https://cloud.githubusercontent.com/assets/5158436/26766664/5694c26a-49ab-11e7-8789-049fd9161af5.png" width="256px">
<br>
Live Demo
</a>
......@@ -11,9 +12,9 @@ Port of [vue-hackernews-2.0](https://github.com/vuejs/vue-hackernews-2.0) for [n
## Performance
- Lighthouse [result](https://www.webpagetest.org/lighthouse.php?test=170602_3H_1GWK&run=2)
- Interactive (Faster 3G) [result](https://www.webpagetest.org/result/170602_G7_1GWC/)
- Interactive (Emerging Markets) [result](https://www.webpagetest.org/result/170602_R0_1GSB)
- Lighthouse [91/100](https://www.webpagetest.org/lighthouse.php?test=170605_Y1_ZF&run=1)
- Interactive (Faster 3G) [result](https://www.webpagetest.org/result/170605_Y1_ZF)
- Interactive (Emerging Markets) [result](https://www.webpagetest.org/result/170605_EQ_144)
## Features
......
assets/logo.png

9.26 KB | W: | H:

assets/logo.png

624 Bytes | W: | H:

assets/logo.png
assets/logo.png
assets/logo.png
assets/logo.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -19,31 +19,24 @@
</template>
<script>
import {watchList} from '../../api'
import Item from '../../components/Item.vue'
import { watchList } from '../api'
import Item from './Item.vue'
const camelize = str => str.charAt(0).toUpperCase() + str.slice(1)
export default {
export default {
name: 'item-list',
components: {
Item
},
props: {
type: String
},
data() {
return {
transition: 'slide-right',
displayedPage: Number(this.$route.params.page) || 1,
displayedItems: this.$store.getters.activeItems,
type: this.$route.params.type
displayedItems: this.$store.getters.activeItems
}
},
fetch({store, params}) {
return store.dispatch('FETCH_LIST_DATA', {type: params.type})
},
meta: {
},
computed: {
page() {
return Number(this.$route.params.page) || 1
......@@ -82,7 +75,7 @@
methods: {
loadItems(to = this.page, from = -1) {
this.$bar.start()
this.$nuxt.$loading.start()
this.$store.dispatch('FETCH_LIST_DATA', {
type: this.type
}).then(() => {
......@@ -95,22 +88,22 @@
: to > from ? 'slide-left' : 'slide-right'
this.displayedPage = to
this.displayedItems = this.$store.getters.activeItems
this.$bar.finish()
this.$nuxt.$loading.finish()
})
}
}
}
}
</script>
<style lang="stylus">
.news-view
.news-view
padding-top 45px
.news-list-nav, .news-list
.news-list-nav, .news-list
background-color #fff
border-radius 2px
.news-list-nav
.news-list-nav
padding 15px 30px
position fixed
text-align center
......@@ -124,7 +117,7 @@
.disabled
color #ccc
.news-list
.news-list
position absolute
margin 30px 0
width 100%
......@@ -134,27 +127,27 @@
padding 0
margin 0
.slide-left-enter, .slide-right-leave-to
.slide-left-enter, .slide-right-leave-to
opacity 0
transform translate(30px, 0)
.slide-left-leave-to, .slide-right-enter
.slide-left-leave-to, .slide-right-enter
opacity 0
transform translate(-30px, 0)
.item-move, .item-enter-active, .item-leave-active
.item-move, .item-enter-active, .item-leave-active
transition all .5s cubic-bezier(.55, 0, .1, 1)
.item-enter
.item-enter
opacity 0
transform translate(30px, 0)
.item-leave-active
.item-leave-active
position absolute
opacity 0
transform translate(30px, 0)
@media (max-width 600px)
@media (max-width 600px)
.news-list
margin 10px 0
</style>
import ItemList from './ItemList.vue'
const camelize = str => str.charAt(0).toUpperCase() + str.slice(1)
// This is a factory function for dynamically creating root-level list views,
// since they share most of the logic except for the type of items to display.
// They are essentially higher order components wrapping ItemList.vue.
export default function createListView (type) {
return {
name: `${type}-stories-view`,
fetch ({ store }) {
return store.dispatch('FETCH_LIST_DATA', { type })
},
head: {
title: camelize(type),
},
render (h) {
return h(ItemList, { props: { type }})
}
}
}
......@@ -15,9 +15,7 @@
</a>
</nav>
</header>
<transition name="fade" mode="out-in">
<nuxt></nuxt>
</transition>
</div>
</template>
......@@ -35,6 +33,9 @@
color #34495e
text-decoration none
.progress
z-index: 1000 !important
.header
background-color #41B883
position fixed
......@@ -81,10 +82,10 @@
margin 0 auto
position relative
.fade-enter-active, .fade-leave-active
.page-enter-active, .page-leave-active
transition all .2s ease
.fade-enter, .fade-leave-active
.page-enter, .page-leave-active
opacity 0
@media (max-width 860px)
......
{
"alias": "nuxt-hn",
"public": true,
"type": "docker",
"env": {
"NODE_ENV": "production",
"HOST": "0.0.0.0"
}
}
\ No newline at end of file
module.exports = {
loading: {color: '#ff6600'},
build: {
extractCSS: true,
extend(config, {isClient}) {
config.resolve.alias['create-api'] = `./create-api-${isClient ? 'client' : 'server'}.js`
}
},
head: {
titleTemplate: 'Nuxt HN | %s',
meta: [
{ hid: 'description', name: 'description', content: 'HackerNews clone built with Nuxt.js' },
{ property: 'og:type', content: 'website' },
{ property: 'og:title', content: 'Nuxt.js HackerNews' },
{ property: 'og:description', content: 'HackerNews clone built with Nuxt.js' },
{ property: 'og:image', content: 'https://cloud.githubusercontent.com/assets/904724/26784102/0d2f8000-49fc-11e7-8091-2b66901c73ee.png' },
{ property: 'twitter:card', content: 'summary_large_image' },
{ property: 'twitter:site', content: '@nuxt_js' },
]
},
loading: {
color: '#66e8ad'
},
manifest: {
theme_color: '#41b883'
},
modules: [
require('@nuxtjs/manifest'),
require('@nuxtjs/meta'),
require('@nuxtjs/workbox'),
require('@nuxtjs/component-cache')
'@nuxtjs/pwa',
'@nuxtjs/component-cache'
],
plugins: [
'~plugins/filters.js'
],
build: {
extractCSS: true,
ssr: {
// TODO: make component-cache module working in production without this extra setting
cache: require('lru-cache')({
max: 10000,
maxAge: 1000 * 60 * 15
})
},
extend(config, {isClient}) {
config.resolve.alias['create-api'] =
`./create-api-${isClient ? 'client' : 'server'}.js`
render: {
static: {
maxAge: '1y',
setHeaders: function (res, path) {
if (path.includes('sw.js') || path.includes('workbox-sw.')) {
res.setHeader('Cache-Control', 'public, max-age=0')
}
}
}
}
},
}
{
"name": "nuxt-hn",
"description": "Nuxt Hacker News",
"version": "1.0.0",
"author": "Evan You <yyx990803@gmail.com>",
"contributors": [
{
......@@ -24,12 +25,10 @@
"npm": ">=4.0"
},
"dependencies": {
"@nuxtjs/component-cache": "latest",
"@nuxtjs/manifest": "latest",
"@nuxtjs/meta": "latest",
"@nuxtjs/workbox": "latest",
"@nuxtjs/component-cache": "^0.1.3",
"@nuxtjs/pwa": "latest",
"firebase": "^4.1.1",
"nuxt": "^1.0.0-alpha2",
"nuxt": "1.0.0-alpha.3",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1"
}
......
<script>
import createListView from '~components/createListView'
export default createListView('ask')
</script>
<template>
<div class="item-view" v-if="item">
<div class="item-view view" v-if="item">
<template v-if="item">
<div class="item-view-header">
<a :href="item.url" target="_blank">
......@@ -10,7 +10,8 @@
</span>
<p class="meta">
{{ item.score }} points
| by <router-link :to="'/user/' + item.by">{{ item.by }}</router-link>
| by
<router-link :to="'/user/' + item.by">{{ item.by }}</router-link>
{{ item.time | timeAgo }} ago
</p>
</div>
......@@ -33,14 +34,20 @@
export default {
name: 'item-view',
components: { Spinner, Comment },
components: {Spinner, Comment},
data: () => ({
loading: true
}),
head() {
return {
title: this.item.title
}
},
computed: {
item () {
item() {
return this.$store.state.items[this.$route.params.id]
}
},
......@@ -48,16 +55,12 @@
// We only fetch the item itself before entering the view, because
// it might take a long time to load threads with hundreds of comments
// due to how the HN Firebase API works.
asyncData ({ store, route: { params: { id }}}) {
return store.dispatch('FETCH_ITEMS', { ids: [id] })
},
title () {
return this.item.title
asyncData({store, route: {params: {id}}}) {
return store.dispatch('FETCH_ITEMS', {ids: [id]})
},
// Fetch comments when mounted on the client
beforeMount () {
beforeMount() {
this.fetchComments()
},
......@@ -67,7 +70,7 @@
},
methods: {
fetchComments () {
fetchComments() {
this.loading = true
fetchComments(this.$store, this.item).then(() => {
this.loading = false
......@@ -77,7 +80,7 @@
}
// recursively fetch all descendent comments
function fetchComments (store, item) {
function fetchComments(store, item) {
if (item && item.kids) {
return store.dispatch('FETCH_ITEMS', {
ids: item.kids
......@@ -85,6 +88,7 @@
return fetchComments(store, store.state.items[id])
})))
}
return Promise.resolve()
}
</script>
......@@ -92,7 +96,7 @@
.item-view-header
background-color #fff
padding 1.8em 2em 1em
box-shadow 0 1px 2px rgba(0,0,0,.1)
box-shadow 0 1px 2px rgba(0, 0, 0, .1)
h1
display inline
font-size 1.5em
......
<script>
import createListView from '~components/createListView'
export default createListView('job')
</script>
<script>
import createListView from '~components/createListView'
export default createListView('new')
</script>
<script>
import createListView from '~components/createListView'
export default createListView('show')
</script>
<script>
import createListView from '~components/createListView'
export default createListView('top')
</script>
<template>
<div class="user-view">
<div class="user-view view">
<template v-if="user">
<h1>User : {{ user.id }}</h1>
<ul class="meta">
......@@ -20,29 +20,29 @@
<script>
export default {
export default {
name: 'user-view',
computed: {
user () {
user() {
return this.$store.state.users[this.$route.params.id]
}
},
asyncData ({ store, route: { params: { id }}}) {
return store.dispatch('FETCH_USER', { id })
head() {
return {
title: this.user.id || 'User not found'
}
},
title () {
return this.user
? this.user.id
: 'User not found'
fetch({store, route: {params: {id}}}) {
return store.dispatch('FETCH_USER', {id})
},
}
}
</script>
<style lang="stylus">
.user-view
.user-view
background-color #fff
box-sizing border-box
padding 2em 3em
......
import Vuex from 'vuex'
import actions from './actions'
import mutations from './mutations'
import getters from './getters'
export default {
state() {
return {
export default () => {
return new Vuex.Store({
state: {
activeType: null,
itemsPerPage: 20,
items: {/* [id: number]: Item */},
......@@ -16,9 +18,9 @@ export default {
ask: [],
job: []
}
}
},
actions,
mutations,
getters
})
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment