-
Notifications
You must be signed in to change notification settings - Fork 248
Improve the way we consume collection(s) to avoid sending all metadata in all pages #713
Comments
It depends on use cases. If user doesn't require any of these field in their codebase for generating a list of posts, it's possible to remove them. See collections API https://phenomic.io/docs/usage/collections/ Does it worth it? I don't know. In the huge site of yours, if you remove all of these custom fields how much bytes do you gain ? |
I am currently discussing with @bloodyowl to improve Phenomic collection API in order to stop sending almost all data into all pages. The idea is mainly to only put in the pages the data used (minimal json) and create json for each possibles pages. For now I don't plan to add a custom way to retrieve only some fields, because if we choose the solution above, it won't be a big deal (tell me if I am wrong) to get all fields for, let's say, the 10 pages you are listing. Again I repeat the idea we are working on: only put in the html (& json files for client nav) the data requested by a page. So no more extra unused json. To achieve that, in order to keep an API simple (not by adding a graphql server - please @bloodyowl and others, take a look to this & tell me what you think, I personally thing it's a bit crazy to go this path, but I may be wrong) the idea is to provides HoC. Here is some pseudo code we have in mind (API subject to changes): class YouPageThatListContent extends ...
export default Phenomic.createContainer(
YouPageThatListContent,
(store) => ({
pages: store.get("pages", { sortBy: "date", order: "DESC", limit: 5})
})
)
// alternative
export default Phenomic.createContainer(
YouPageThatListContent,
(state) => ({
pages: Phenomic.queryCollection(state.pages, { sortBy: "date", order: "DESC").slice(0, 5)
})
) Now you are going to ask: what about pagination? Imo this should be in core, not in a plugin... Ok then here is an idea: <Route
path="/tag/:tag(/:page??)" component={ YouPageComponent }
collection="posts" pageSize={20} sortBy="date" order="DESC"
filter={ (item, routeParams) => item.tags.indexOf(routeParams.tag) > 0) }
/>
// ...
// YouPageComponent
export default Phenomic.createContainer(
YouPageThatListContent,
(state, page) => ({
taggedPosts: pages.items,
// you can send any kind of data, injected as props into YouPageThatListContent
totalPages: pages.numberOfItems,
// while pagination is allowed for one resource at a time, you might get other data as well
authors: Phenomic.queryCollection(state.authors, { sortBy: "commits", order: "DESC").slice(0, 5)
someRandomPosts: Phenomic.queryCollection(state.posts).randomMethodToImplement(5)
})
) Not that the code above assume that we will introduce a new way to register collectionS (yes multiple collections, instead of having to filter via layout or something else) By doing that, we will be able to statically retrieve collections fraction and only inject that in the html (& as well create json fragments, for client navigation). Any thoughts on this approach? |
Totally agree with this approach. Filter with js runtime can be slow especially if you have a lot of pages. |
Added a few changes to my proposal: Content definitionimport Phenomic from "phenomic"
// I think that we should accept:
// type Data = { [key: string]: any | Promise<any> }
// Data | Promise<Data>
module.exports = {
// See: https://gist.github.com/bloodyowl/27e159aa9e02c5ac40fd6ff5c2bb93e8
posts: Phenomic.createCollection(
requireAll(require.context("markdown!./posts", true, /\.md/)),
{ indexes: ["id", "url"] } // will create JS Maps to improve query time on build & dev server
),
authors: requireAll(require.context("json!./authors", true, /\.json/)),
// accept promises
someExternalData: require("isomorphic-fetch")(someURL),
} Consuming the dataIndexed queriesimport React from "react"
import Phenomic from "phenomic"
const PostRoute = (props) => (
<div>
<h1>{props.post.title}</h1>
<p>{props.post.content}</p>
</div>
)
export default Phenomic.createContainer(PostRoute, {
queries: (state, params) => ({
// O(1) if indexed, O(N) otherwise
post: state.posts.getBy("id", params.id),
})
}) "Special" queriesimport React from "react"
import Phenomic from "phenomic"
const HomepageRoute = (props) => (
<div>
<ul>
{props.posts.map((post) =>
<li>{post.title}</li>
)}
</ul>
<ul>
{props.authors.forEach((author) =>
<li>{author.username}</li>
)}
</ul>
</div>
)
export default Phenomic.createContainer(HomepageRoute, {
queries: (state, params) => ({
posts: state.posts.queryCollection({ sortBy: "date", order: "DESC" }).slice(0, 5),
authors: state.authors.slice(0, 5),
})
}) Paginationimport React from "react"
import { Router, Route } from "react-router"
import PostRoute from "./PostRoute"
import HomepageRoute from "./HomepageRoute"
export default (
<Router>
<Route path="/" component={HomepageRoute} />
<Route path="/post/:id" component={PostRoute} collection="posts"/>
<Route path="/posts/page/:page" component={PostList} collection="posts" pageSize={20} />
</Router>
) This configuration leaves us enough information to just generate Phonemic.createContainer(Component, {
queries: (state, routeParams, page) => ({
hasNextPage: page.hasNextPage,
posts: page.items,
}),
}) ConfigurationYou basically provide your module.exports = {
content: require("./content"),
router: require("./web/routes/Router"),
} Build configurationI think that in order to prevent colliding stuff and forcing us to provide stuff for all configurations, the If you don't use the development server, webpack shouldn't even be mandatory (e.g. you get the data from an external API). var webpack = require("webpack")
var path = require("path")
module.exports = {
// maybe autofill entry & output, not quite sure about this yet
entry: {
bundle: "phenomic/lib/entry",
},
output: {
path: path.join(__dirname, "./.phenomic"),
filename: "[name].js",
},
module: {
loaders: [
{
test: /\.js$/,
ignore: /node_modules/,
loader: "babel",
query: {
presets: ["es2015", "react"],
},
},
],
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
})
],
} |
What can I do to help with this? Do you have any POC? |
I'm working on a POC, just need to add the |
Nice ! |
I wanted to follow up on this thread. What do you guys think about normalizing the collection by perhaps URL? This way we can have a constant lookup time {
"url/xyz/lolz": {
"__dataUrl": "/blog/defining-serverless-and-why-it-matters-to-developers/index.html.bf1d5e0db467ef721b8508992749379b.json"
},
"url/two": {
"__dataUrl": "/blog/defining-serverless-and-why-it-matters-to-developers/index.html.bf1d5e0db467ef721b8508992749379b.json"
}
} You could still map over the data with Maybe even going a step further with https://github.com/paularmstrong/normalizr I can probably just do this in user land but I wanted to float the idea around here as well. |
With the idea we have, there's not really a need for this. But if you want to create a "static API" from your contents, that should be totally possible in user-space 😃 |
@bloodyowl any progress on this? |
I am thinking about the fact that currently we use the entire collection to know if a click must be done using browser push + preventDefault :/ |
@MoOx I was thinking about this too. Perhaps it could be solved with a 'smarter' link component. Where each link component gets additional data attributes added to it on build. Then the link listener wouldn't need to check the collection it could just use the inline example: <!-- on click use router to go to /url/xyz -->
<a href='/url/xyz' data-phenomic-path='/url/xyz' data-phenomic-data='/path/index.html.bf1d5e0db467ef721b8508992749379b.json'>Local link</a> This might even let us remove the need for the entire collection to be placed on the window? |
Newcomer to Phenomic ... Apologies for interjecting but I hope this can help others too.
|
@lapidus you can expect a major change in the coming weeks! ;) |
Going to be fixed with #925 |
@bloodyowl awesome! How is it being approached? I didn't see it mentioned in #925 |
in 1.0.0, parsers output |
@bloodyowl Cool. A couple questions:
My main concerns are making phenomic a viable option for larger website implementations =). There are certain things in the current setup that make it not an option for sites with 1000+ pages. |
The goal of 1.0 is really to make something scalable by default. We are having the same concerns as you do :) @bloodyowl tell me if it's incorrect |
yeah, basically the JSON files are put in |
Related to trimming down the
window.collection
. #712When you add in new custom meta (like in this blog post example) all those extra fields are also added to the
window.collection
data. This could potentially make it HUGEExample:
All the data also exists in the
index.html.bf1d5e0db467ef721b8508992749379b.json
file.So the question is, would it be possible/worth it to remove the additional custom fields from outputting into
window.collection
and just have the .json files for the individual pages handle that additional data?The text was updated successfully, but these errors were encountered: