this issue has been plaguing my large production website for a while now.
My app.js (compiled by webpack) has multiple Vue components in it:
Vue.component(
'product-import',
() => import(
/* webpackChunkName: "product-import" */
'./components/product_import/ProductImport.vue'
)
);
Whenever I add a new component to this file, and push my code to production... every single Vue module appears blank. The only way to solve this issue is to either:
1: Every user hard refresh the browser's cache (Ctr, Shift, R)
2: Rename EVERY SINGLE vue componenets (let's say from <clock></clock> to <clock2></clock2>) referenced in every file on my website.
I have versioning enabled mix.js('resources/assets/js/app.js', 'public/js/app2.js').version();
And I've even tried renaming the app.js file, yet each time I add a new component it requires a hard refresh from every one of our users for them to be able to see any Vue module again on our site.
This deters me from adding new Vue components, as I have to rename every single component (and sub-component) referenced in all files. Does anyone have a fix for this?
Related
I've got a (small) React app (vanilla create-react-app), that I would like to appear in a modal (bootstrap or similar) on another site. Is there a library that will simplify this process?
Specifically, the entire use case is that if my Javascript file is loaded (and just one javascript file), it will insert a "Click Me" type call to action, and when clicked my App component will be loaded into a new modal. It will need the CSS (for the app) to be included in some form as well.
I think all of this (excluding the call-to-action which is fairly simple) could be done during Babel/Webpack transpilation but I can't find anything off-the-shelf that seems to do this.
This functionality is built into ReactDOM.render. Simply add an id to your element.
For example:
<!-- index.html -->
<script src="url/to/react/build/asset" as="script" />
<div id="button-and-modal"></div>
Then to render your react app inside the div:
// index.js
import { render } from 'react-dom';
import App from './App'
function renderReact() {
const element = document.getElementById('button-and-modal');
render(<App/>, element)
}
document.addEventListener('DOMContentLoaded', renderReact);
Then your react app would look something like this:
const App = () => (
<div>
<Button/>
<Modal/>
</div>
)
You can also code the button and modal outside of the react app and only have the modal content rendered by react. If you want to do that, then follow the same directions but add the javascript for the button+modal inside the renderReact function.
You can use for example https://direflow.io/ to build your react app as a web component that you can render anywhere on any site.
Using your current project you can do
direflow create
cd <project-name>
npm install
and then
copy your whole app in folder into direflow-components so your project tree would look like:
/public
/src
/direflow-components
/<project-name>
// leave here only index.ts from example and copy all your project files here
index.ts
component-exports.ts
index.ts
react-app-env.d.ts
.eslintrc
...
If needed you can change
...
component: App,
configuration: {
tagname: 'example-component',
},
...
to your component that you want to render and tagname by which app will be accessible.
After all that you just do
npm run build
copy direflowBundle.js from build folder to your website
and render your app on some website like so:
<body>
<script src="./direflowBundle.js"></script>
<awesome-component></awesome-component>
</body>
I feel like I deal with this issue at every Front End job. It's definitely not easy, but I've found a number of ways to do it. I've tried the bundling idea you suggested but that one gave me the hardest time. The easiest way imo without a lot of hassle is to host your react app on a blank web page, then load it into an iframe where you need it.
At my last job, we wanted to migrate our shopify website to react, but with the way the shopify architecture was set up at the time, it made it difficult to us host a server-side rendered react app. So we built the web pages using Next.js and then deployed it to Vercel. We then inserted this as an iframe into the shopify website. It worked beautifully.
I want to build a SPA on the client site, which supports a plugin concept.
You should be able to copy a package inside a specific folder and the server should load this component after a restart. This plugin should work without any information stored inside the base program.
I need to dynamically load a component by name. I do not have a map of all possible components. How can I load a component by only knowing its name?
you can check out React.lazy. This helps with dynamic loading components which you want, but with this you need to wrap it to Suspense, because component will load not immediately.
// This component is loaded dynamically
const SomeComponent = React.lazy(() => import('./SomeComponent'));
<Suspense fallback={<Spinner/>}><SomeComponent/></Suspense>
We are developing a Vue.js application based on Vue CLI 3 with Vue Router and Webpack. The routes are lazy-loaded and the chunk file names contain a hash for cache busting. In general, everything is working fine.
However, there is a problem during the deployment. Steps to reproduce are the following.
User opens the application (let's assume route "/"), thus the main chunk file is loaded.
We change something in the application and deploy a new version.
Old chunk files are removed
New chunk files are being added (i.e. hashes in the chunk file names change)
User clicks a link to another route (e.g. "/foo")
An error occurs as the application tries to load a chunk file that has been renamed: Error: "Loading CSS chunk foo failed.
(/assets/css/foo.abc123.css)" (this might be CSS or JavaScript)
What is the best way to avoid errors like this?
One approach that should work is just to retain old chunk files and delete them at a later time. That, however, complicates the deployment of new versions as you need to keep track of old versions and always also deploy the old chunk files with the new version.
Another (naive) approach is to just reload as soon as such an error is detected (e.g. Vue Lazy Routes & loading chunk failed). It somewhat works, but it reloads the old route, not the new one. But at least it ensure that consecutive route changes work again.
Any other ideas? Maybe there is something in webpack that could fix this?
DoNOT cache the entry file(usually index.html).
We add:
expires 0;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate';
in our nginx server config.
Then, after you refreshed the client's code, you can use the vue-router's error hook to detect the error and do something properly.
As long as you have a versioned API, you can use the old app files (just leave them on the server and delete after a vew days).
You will get problems as soon as your API changes during deployments.
I assume, you deploy a new API each time you deploy new JS code.
Then you can:
Pass on the API version (simply use the git hash) to the application as header with every response (JS resources, CSS, API requests, 404 responses)
Store the API version in your main JS entry point (or make it accessible somehow, e.g. as generated constant)
On each server response, check if the Server version matches your main client version.
If it does not: Display a prominent warning to the user (like the cookie banners) that he should reload the page (=> allows the user to save chnages in hope the API did not change for that save button).
For async components, we display normal 'not found' messages if loading fails, together with a reload button that appears instead of the component. Reloading without user interaction will cause a lot of confusion.
Based on the comments on another of my questions (gradle how to add files javascript fies to a directory in the war file) I'm trying to use angular-cli to help build and manage an angular project. However, I cannot seem to find any documentation on how to create a second webpage in the project, which to me seems like a very basic task. I tried creating a "component" with ng g component {component name}, but this didn't add anything to the build result.
I had missed the section of the angular docs on routing since I did not make the connection between the word "routing" and what I wanted to do. Routing as described here works perfectly when using Node as your server. However, other web servers such as Tomcat (which I am using for this project) will not since ng build only generates an index.html file. Node knows that it should re-route URLs under the angular base to that file, but Tomcat doesn't. A proxy server such as apache needs to be placed in front of the Tomcat server to redirect the urls to the base url for the application.
With that out of the way, here is the basics of routing:
create a component for each "page" (the component does not need to be responsible for the whole page displayed. see 2)
create a "shell" component that contains features that will be on all pages e.g. toolbar, side navigation.
add <router-outlet></router-outlet> to the point in the shell component component where components for sub-URLs will appear (note that they are inserted into the DOM after this tag, not within it.)
in the imports for your module, add RouterModule.forRoot(). This function takes an array of Route. Each route has a path and a component property. path is the url (relative to the base url) that will cause component to be inserted into the DOM. Note that path values should not begin with a slash.
add a tags with the routerLink property bound to the url of your new page. Note that here, there should be a leading slash.
I am doing this to load all JS files in app folder
ss.client.define('main', {
view: 'app.jade',
css: [
'libs/reset.css',
'app.styl'
],
code: [
'libs/jquery-2.1.0.min.js',
'libs/angular-1.2.10.min.js',
'libs/lodash-2.4.1.min.js',
'app'
],
tmpl: '*'
});
There are 3 files in app, 2 that came with the default project and 1 that I added. The first 2 work fine, but the one I added does not get executed!
The funny thing is that when there are errors in that file, I set them in the Chrome console, but no function gets executed or variable added to the page.
Any ideas why?
It will need access to the window variable/global-object.
Therefore you need to require it from your entry file. Typically this means having the lodash code file in your actual code (/client/code/[...]) directory. I.e. you wouldn't put it in your libs folder, but in your main app folder, although you can make another libs folder there.
This is what I've always had to do in order to require --for example-- bootstrapJS. It defies the organisation of the client side as they set it up, but it's the way things need to be done for stuff like this.
An alternative is to require it remotely (from CDN) from your main app.jade view file.
script(src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js")