According to the Next.js docs:
Generally you'll want to use build-time environment variables to provide your configuration. The reason for this is that runtime configuration adds rendering / initialization overhead and is incompatible with Automatic Static Optimization.
and
A page that relies on publicRuntimeConfig must use getInitialProps or getServerSideProps or your application must have a Custom App with getInitialProps to opt-out of Automatic Static Optimization. Runtime configuration won't be available to any page (or component in a page) without being server-side rendered.
This is confusing for me to read. The 2nd paragraph tells me a page has to be server-side rendered if using runtime config, so you have to opt out of Automatic Static Optimization. The 1st paragraph tells me runtime config is incompatible with Automatic Static Optimization.
If I do this:
const Page = (): JSX.Element => {
const config = getConfig().publicRuntimeConfig;
console.log(config);
return (...);
}
export default Page;
This seems to work no problem. Also when building the app, the build result never indicates that any page is server-side rendered at runtime, except the API endpoints. All the pages are either rendered as static HTML (no initial props) or automatically rendered as static HTML + JSON (uses getStaticProps).
I want all pages that can be rendered at build time to be rendered at build time, but the documentation confuses me regarding if I can use runtime configuration or not combined with static generation. Can I? And can someone clarify what the documentation means here? Can someone elaborate on why not recommend runtime configuration?
Related
I'm new to Next.js and am trying to get my head around client side and server side routing and what files need to be made available to download when Next.js is configured to use server-side rendering.
When I do a production build of a Next.js project, a .next directory is created (details here). When I call next start and then load a page that uses SSR (by defining getServerSideProps()) then I can see in the developer console that the page that I load downloads resources that are prefixed _next (e.g. <script src="/_next/static/chunks/main-3123a443c688934f.js" defer=""></script>).
Can someone confirm whether the .next directory contents (.next/server/**, .next/static/**) are just made available on the server by being renamed to _next?
I tried creating a new page in a file called _next.js as an experiment. The project builds correctly (no errors reported and there are build artifacts created with the same naming convention as the other pages). However, when I try to load that page, I get a 404.
Are there restrictions on what constitutes a validly named page in Next.js? If so, what are they and where is this defined.
How does the Next.js server know what is a static resource that should just be given to the client and what is a page for which Next.js should render an output? Is it simply an algorithm like "if the path starts _next/ then return what is requested, otherwise render?"
How does Next.js know to distinguish between resources that are in the public directory and pages? e.g. if there's a collision between the name of a page and a resource in the public folder, how does the server know what to return to the user?
I'm looking to build a static Next.js Single Page App that has dynamic routes, for example: /posts/123. I'd like to do this to:
Be able to host the site anywhere (e.g. S3)
Not need to know the routes at build time (so our APIs can change whenever independently of the Frontend/without requiring a rebuild of the Frontend)
From what I can tell, this should work with:
next build && next export
fallback: true
But the docs suggest that's only for loading/intermediate states.
Can I use some combination of fallback pages/catchall dynamic routes/static generation to get a static single-page app with dynamic routes at runtime? I'm okay with faking the routes using nginx (e.g. /posts/123 -> /index.html).
Edit: the above paragraph doesn't seem possible. From the docs, when using a required catchall route, e.g. [...post].js):
fallback: true is not supported when using next export.
I've been diving into ASP.NET MVC internal functionality much (different reasons), but still can not cover all the behaviour. One of those which I did not is subj.
The way it works is the following:
if I bundle some files (css files for instance), the framework detects those changes and generates new id for the new bundle (to make it easy for browsers to refresh the changes) like href="/Content/css?v=qartPE4jGe-l1U0I7kNDZPZzVTdh0kT8VBZZA_uURjI1".
What I am actually trying to understand:
How exactly the framework (that's possibly not MVC but .NET stuff) detects that the files are changed (as there are no directory watchers active (as I can change the file even when web-server if off-line) to see the file changes live, and also the system detects actually the file content changes (I tried just to re-save files without changing their contents and the bundle number did not change as well))?
(I consider that obviously the system can not compare every file content to detect its changings on every request came).
Where (and how) the frameworks stores current bundle id and how it stores previous versions (as previous bundles are still available when go to their urls)?
Thanks a lot!
The ASP.NET Optimization framework caches the bundle response in HttpContext.Cache and uses a CacheDependency to monitor each file in the bundle for changes. This is why updating the files directly invalidates the cache and regenerates the bundle.
The bundle file name is a hash of the bundle contents which ensures the URL changes when any of the bundle files are modified. The bundle's virtual path is used as the cache key.
The relevant code from the library (note this is slightly out of date but I believe the logic is still the same):
internal BundleResponse GetBundleResponse(BundleContext context)
{
// check to see if the bundle response is in the cache
BundleResponse bundleResponse = Bundle.CacheLookup(context);
if (bundleResponse == null || context.EnableInstrumentation)
{
// if not, generate the bundle response and cache it
bundleResponse = this.GenerateBundleResponse(context);
if (context.UseServerCache)
{
this.UpdateCache(context, bundleResponse);
}
}
return bundleResponse;
}
private void UpdateCache(BundleContext context, BundleResponse response)
{
if (context.UseServerCache)
{
// create a list of all the file paths in the bundle
List<string> list = new List<string>();
list.AddRange(
from f in response.Files
select f.FullName);
list.AddRange(context.CacheDependencyDirectories);
string cacheKey = Bundle.GetCacheKey(context.BundleVirtualPath);
// insert the response into the cache with a cache dependency that monitors
// the bundle files for changes
context.HttpContext.Cache.Insert(cacheKey, response, new CacheDependency(list.ToArray()));
context.HttpContext.Response.AddCacheItemDependency(cacheKey);
this._cacheKeys.Add(cacheKey);
}
}
Finally as for old bundle URLs working, I think you will find they are either returned from your browser cache or actually return the latest version of the bundle since the bundle path doesn't change, only the version query string.
What's a good strategy for versioning Durandal js and html files?
I noticed that, during development, your browser cache must be disabled in order for you to receive up to date files on each refresh. This is a must for during development.
However, my concern is that when I go to production with my continuous deployment strategy (deploying multiple times per day), that users' browsers will be caching older versions of my app which might lead to unpredictable behaviour.
The approach that springs to mind would be to version the js and html urls somehow so that there is a version number embedded into every request. But I am unsure as to how to make that work internally within the Durandal framework.
Ok, here is the direction that I am heading in. Basically there is something built into requirejs to handle this.
At the top of my main.js, in the call to requirejs.config I can set a urlArgs property that will be appended to every call requirejs makes for a module.
requirejs.config({
paths: {
'text': 'durandal/amd/text'
},
urlArgs: 'v=1.0.0.0'
});
When I want to force production users to get a new version of the requirejs modules I can just increment the version number which will invalidate the browsers cache.
(In my project I have a way of injecting the version number of the assembly containing my main ASP.NET MVC assembly into this property, but the code for that would have distracted from the simplicity of the above example).
Hope this helps someone!
For .NET, add the main-built.js file as a script bundle in App_Start/BundleConfig:
public static void RegisterBundles(BundleCollection bundles)
{
//...
bundles.Add(new ScriptBundle("~/Scripts/main-built").Include(
"~/App/main-built.js"));
//...
}
Reference the script bundle on your index page:
#if (HttpContext.Current.IsDebuggingEnabled)
{
<script type="text/javascript" src="~/Scripts/require.js" data-main="App/main"></script>
}
else
{
<!-- Remember to run the weyland optimizer to create the main-built.js -->
#Scripts.Render("~/Scripts/main-built")
}
As long as you have the default Web.Release.Config file, Visual Studio will automatically remove debug attributes while also minifying and versioning your bundles upon publishing.
I am using ASP.NET optimization package to minify and bundle the scripts, and CSS files.
I am also developing a mobile UI for my ASP.NET application which uses a HTML5 cache manifest.
The optimization package updates the version of the dynamic bundle URL when the files change and the application cache is recycled.
I would like to be able to update my manifest version whenever this happens and include the dynamic URLs the optimization package provides in the manifest.
How can I read the current version (the "v" parameter) or anything else to trigger a manifest update?
/_assets/bundles/global?v=fmbQlO0mGjXyliVEBImQIr5yoMX0Tw0tlMK45jlwHZ81
Example Code:
string version= "2.6";
StringBuilder output = new StringBuilder();
output.AppendLine("CACHE MANIFEST");
output.AppendLine(string.Format("# v{0}", ??????));
output.AppendLine("CACHE:");
output.AppendLine(Scripts.Url("~/bundles/global").ToString());
...
The Application Manifest will automatically trigger an update if it is changed.
With static assets, people usually changed a version number in a comment so that the file was changed and would trigger an update, even though the content under the CACHE, NETWORK and FALLBACK sections were unchanged.
When you are using the URLs generated by System.Web.Optimization, the URL will change when the content of any of the CSS or JavaScript files in the bundles changes. This means that the manifest file will automatically be different to the previous version of the file and will trigger an update.
There is no need to force the file to be different by updating a version comment.