Loading my ember CLI app currently involved downloading a 3Mb file, most of which consists of common libraries such as jquery, ember, bootstrap, etc. 3Mb is not huge, but it becomes noticable over a slow connection so I want to strip out all the common libraries and get them from a CDN instead. The idea is that they would be cached by the browser to that they don't need to be re-downloaded every time I update my app (which is very often at the moment). I have read this question, which points out that it is easy to simply add a <script...> to index.html, but I can't figure out how to them tell ember not to package those libraries into vendor.js.
In brocfile.js (ember-cli-build.js in newer versions) change the constructor to
var app = new EmberApp({
vendorFiles: {
'jquery.js': false,
'handlebars.js': false,
'ember.js': false
}
});
Now include those in your index.html the old fashion way and enjoy the fact that pretty much every users browser already has jquery cached even if they haven't visited your site before.
This is something you get by default with ember-cli. From the manual:
When the environment is production (e.g. ember build --environment=production), the addon will automatically fingerprint your js, css, png, jpg, and gif assets by appending an md5 checksum to the end of their filename (e.g. assets/yourapp-9c2cbd818d09a4a742406c6cb8219b3b.js). In addition, your html, js, and css files will be re-written to include the new name.
You can then modify your Brocfile.js to tell it what the base url for your cdn is:
prepend - Default: '' - A string to prepend to all of the assets. Useful for CDN urls like https://subdomain.cloudfront.net/
Related
My organization is using Ember addons to develop a set of shared components between our applications. Let's call this repository app-components. Currently the components application's primary responsibility is to distribute CSS, fonts and images.
We are also building a living styleguide that will ingest our shared components and present them in a neutral way for developers to reference. Let's call this repository app-styleguide. Our goal is to deploy app-styleguide using ember-deploy to deploy this solution to Github Pages. The url follows this pattern:
https://organization.github.io/app-styleguide/
When the app-styleguide application makes it to the gh-pages branch and is served as a webpage, all of the fonts and images being delivered by app-components are giving us a 404. I have referenced a handful of different solutions to this problem, but I keep coming across the same solutions that we have tried.
I have tried using the following two ember addons that automate the deploy to github pages:
https://github.com/poetic/ember-cli-github-pages
https://www.npmjs.com/package/ember-cli-deploy-gh-pages
In the end we went with a vanilla ember-cli-deploy solution, as those two addons are quite old...
I have followed the instructions here to add rootUrl andlocationTypeproperties to ourenvironment.js` file, which has not worked:
https://github.com/ember-cli/ember-cli/issues/398
Our environment.js file looks like this:
if (environment === 'production') {
ENV.rootURL = '/app-styleguide';
ENV.locationType = 'hash';
}
And our requests continue to not add app-styleguide to the request URL's for assets coming from the Addon. Here is an example of a failed request from the Chrome DevTools Network tab:
Request URL:https://organization.github.io/assets/images/thumbnail-icons/person.svg
Request Method:GET
Status Code:404 Not Found
As you can see, app-styleguide is not added to the request.
Any help is greatly appreciated!
I was able to receive some help over on the Ember Slack Community
(special thanks to #alexspeller). It turns out I needed to update a couple of settings in the fingerprinting of assets.
Using the included broccoli-asset-rev library I had to modify my ember-cli-build.js to include the following:
var app = new EmberApp(defaults, {
fingerprint: {
extensions: ['js', 'css', 'png', 'jpg', 'gif', 'map', 'svg', 'ttf', 'woff'],
prepend: '/app-styleguide/'
}
});
I needed to update the options to account for SVG, TTF, & WOFF, as well as the proper prepended url segment.
You can read about the functionality here:
https://ember-cli.com/asset-compilation#fingerprinting-and-cdn-urls
Available options:
https://github.com/cibernox/broccoli-asset-rev?files=1#options
I want to do something very simple that I am a little surprised people are not talking about more. I would like to generate on my server my own index.html from the files that are created from building ember for production. I use ember for part of my application and so when a certain URL is hit, I would then like my ember app to take over. I have tried generating my own index.html by changing the flag storeConfigInMeta in ember-cli-build.js.
storeConfigInMeta: false
This gets rid of the ember app having its configuration stored in a meta tag but the app still does not work and gives the error,
Uncaught ReferenceError: define is not defined
I have the latest version of ember and I am building ember with the command,
ember build --env production
My server generated index.html looks identical accept for the integrity attributes set on the include js and css scripts. Is their anything I am missing about approaching ember this way? Should I not be trying to do this?
when a certain URL is hit, I would then like my ember app to take
over.
You need to configure app server to return index.html file for the certain URL.
Generally, it's not required you to create your own index.html.
May be you can check ember-islands addon to include Ember components anywhere on a server-rendered page.
I made a mistake. I was grabbing the production assets with a regular expression with my server and generating my index.html file with these assets in the wrong order. To anyone looking to do this, it is very possible and is more preferable in my opinion to using the generated index.html unless you are using ember for your entire site's routing. However do use the setting in ember-cli-build.js,
storeConfigInMeta: false
This will make it so your ember app stores it's settings in javascript instead of in a tag. This is required for generating your own index.html file.
I am using the ember quick-start tutorial app. Everything works great locally, but when deployed to a test environment the app is 404ing on loading all resources.
I am deployed to a subfolder out somewhere and apparently ember is trying to find it against the root domain, instead of subfolder
Example:
http://example.com/embertest/index.html
The assets folder is obviously under http://example.com/embertest/assets/, but on load it's trying to grab it from http://example.com/assets/ which doesn't exist
How can I have ember use relative paths in this case?
Update 1
After some googling I tried editing the environment.js ENV.baseURL attribute
In the if(environment === 'production') block I added ENV.baseURL = '/website/dist/';, obviously I am building with ember build --env production
I am getting same 404s when going directly to a route but now also getting an error on index.html, Uncaught UnrecognizedURLError: /index.html
I tried every combination of '/website/dist/', 'website/dist/', '/website/dist' as well
Update 2
I have now also tried manually editing the <base href="/website/dist/"> in my index.html after a prod build. Same errors as from update 1
You need to understand that you can't just put an ember application to a normal webserver folder. Ember uses the history API to change the URL when you do a route change but it can't control what your web server deploys when its directly fetched.
So you have your ember index.html on http://example.com/app/index.html your web server usually will only deploy this file when you open http://example.com/app/ or http://example.com/app/index.html. But for a route foo your url is http://example.com/app/foo and your web server is looking for a directly foo that does not exist. So you have to configure your web server so its always responding with your index.html if your not requesting another existing resource (like an image, js or css file)!
How to do this depends completely on your webserver.
You must also notice that you should enter your assets in a full root relative path and specify rootURL so your router knows which part of the URL is your path and where your routing begins.
You should not use baseURL because its an upcoming deprecation!
You really should read this really new blog post!
Use ENV.locationType = 'hash' to prevent the usage of the history API is still always an option, but definitly an ugly one.
Okay so I solved this by changing ENV.locationType = 'hash' in environment.js
Would still love an explanation of what's going on as this feels a little bit hacky...
I am a Django developer just getting started with adding React to one page of my app, and really enjoying it so far. (It's a normal Django app with a home page, an about page, etc, but also a "chart" page with an interactive chart, and I want to build the interactive part in React.)
The problem is that I've started with the downloadable React starter kit and I'm not sure how to do things the 'right' way, and it's complicated by using Django to serve my project (all the tutorials seem to assume you're using node, which I'm not).
Right now I just have this in my Django template:
<div id="myapp"></div>
<script src="/static/js/vendor/react.js"></script>
<script src="/static/js/vendor/JSXTransform.js"></script>
<script src="/static/js/myapp.js"></script>
And myapp.js has all the React code. I'm aware this isn't really the grown-up modern JS way of doing things.
Now I want to use React Bootstrap, but it seems that the only sensible way to do that is with npm. So it's time to make the switch, but I'm not completely sure how.
I have run npm install react and npm install react-bootstrap from inside my static/js directory in Django. This has created a node_modules folder with various files inside.
So three questions from a confused newbie:
Where should I put my React code to work with these npm modules (should I use var React = require('react')?
Do I need to compile this code somehow (using webpack?)
How do I then integrate this with Django? Should I compile it all to myapp.js and just include that in my HTML template?
I'm also doing the same thing right now - moving away from embedded HTML script tags into require land. Here is the tutorial I am following, and here is my file system so far. I am doing it in Node but it shouldn't be that different for a Django project as the React frontend code is decoupled from any backend other than API URL's.
Your node_modules folder contains react-bootstrap. In your myapp.js, use the require('react-bootstrap') to load up the library which is contained in your node_modules folder.
Where should I put my React code to work with these npm modules (should I use var React = require('react')?
You can put the code anywhere. If your file system looks like this:
project/
react/
myapp.js
node_modules/
react source code
react bootstrap stuff
Then you can just do var React = require('react'); in myapp.js.
Do I need to compile this code somehow (using webpack?)
Yes, I would consult the webpack tutorial I linked earlier, it should explain how to compile all your React code into a single bundle.js. Here is also another good tutorial. This bundle.js file contains all the source code of your requires. So if your myapp.js looks something like
var React = require('react');
var ReactBootstrap = require('react-bootstrap');
then the bundle.js now contains all of the React and react-bootstrap javascript code, along with the myapp.js source code.
How do I then integrate this with Django? Should I compile it all to myapp.js and just include that in my HTML template?
I've only done work on Nodejs, but my React code so far hasn't touched any Node code, and I don't think it will touch any Django code (again I've never done Django so I might be wrong). All you need to do is compile with webpack, which spits out a bundle.js. You put that bundle.js in your HTML and it'll load up myapp.js.
ReactJS code is still JS code. Even though you do require/import/other module based syntax when coding, in browser you will still load the JS code by a script tag.
The problem is how to let the script generated by webpack(bundle.js) to work with other 'VanillaJS' script. For example, if you only write an individual component using React, like a small table. And its data(props/state) will depend on another element/event written in VanillaJS, e.g, a click listener on a button render by django template. Then the question is, how they communicate with each other.
So far, the solution I know is:
when you write React Code, instead of calling ReactDOM.render explicitly with preset props/state, you can store that in a global function, the arguments could be the props. You load this script first, then the other script can use this global function to trigger the React render Component.
I'm using Django Rest Framework to build an API and then connect to that API from React (using simple Create react app), this way the front end and back end are separated and the application is very scalable. The second way to do this, is call create react app then run build and point your django settings to that react build, this way the front end is not separated from the backend. I hope this helped, good luck.
It appears that now in Rails 4 using asset pipeline and the sprocket-rails gem, when images are processed, their filename is appended with an md5 fingerprint like css and javascript. While this makes sense because md5 fingerprints are awesome, it makes it increasingly difficult to access that image from javascript. In rails 3.2, I could access the image with /assets/image_name.jpg and it would serve properly, but in rails 4 that asset doesn't exist, it only exists with the md5 fingerprint in the name.
I know that rails provides helpers to access the image via erb <%= asset-url("image_name.jpg") %> but that is less ideal in javascript, because I am not using erb in my js. There are plenty of ways I could hack this with data-attributes serving in the views or using a script tag in my view and setting some globals, but I am looking for a nice solution to this problem, if it exists.
Any help is appreciated, thanks.
Another option to consider (although I wouldn't recommend it) is to use a custom route in your application controller to grab the asset path for you in the controller and either return the url to the asset with the md5 hash or possibly just render the raw binary data of the asset (although this will add processing overhead to your application).
For example, you make a AJAX get request to
http://yourapp.com/images?file=my_image.jpg
Then in your controller your action method would look like this:
def images
ActionController::Base.helpers.asset_url(params[:file])
end
This would then return the url path to the asset. The downside to this method is that it requires that you make two requests on the JS side. The first to get the path to the asset and the second to actually load that asset with the returned path.
To reduce this down to one request you could have the application read the image from the file system and return the proper headers so the browser thinks it is an image being returned and therefor will render the url provided. However, this would be a lot more work for the application and a lot more unneeded disk IO on your server.
It may take two requests for each image on the client to achieve what you want but you have to sacrifice somewhere...
Why do you need to use the asset pipeline for images? I get the hashing behavior. But normally the assets would be preprocessed. If you put the images in the public hierarchy as in olden times, you would get normal path routing.
Here's a quote from the Asset Pipleline guide that I think might be germane.
"Assets can still be placed in the public hierarchy. Any assets under public will be served as static files by the application or web server. You should use app/assets for files that must undergo some pre-processing before they are served."
Unfortunately, I think that you are stuck either adding an ERB extension to your JS and using the asset helpers, or else not using the asset pipeline for the assets.
When you say "I am not using erb in my js", do you mean you don't want to, or simply that you aren't? Because you can!
If you rename the relevant JS files with the extension .js.erb then you can use the asset_url helper in these files like so:
var src = "<%= asset_url('photo.jpg') %>";