rewrite docs for page-specific-javascript
This commit is contained in:
parent
4d726d1f9f
commit
ab8ad58ea1
1 changed files with 83 additions and 29 deletions
|
@ -49,39 +49,95 @@ properties once, and handle the actual animation with transforms.
|
|||
|
||||
### Page-specific JavaScript
|
||||
|
||||
Certain pages may require the use of a third party library, such as [d3][d3] for
|
||||
the User Activity Calendar and [Chart.js][chartjs] for the Graphs pages. These
|
||||
libraries increase the page size significantly, and impact load times due to
|
||||
bandwidth bottlenecks and the browser needing to parse more JavaScript.
|
||||
Webpack has been configured to automatically generate entry point bundles based
|
||||
on the file structure within `app/assets/javascripts/pages/*`. The directories
|
||||
within the `pages` directory are meant to correspond to Rails controllers and
|
||||
actions. These auto-generated bundles will be automatically included on the
|
||||
corresponding pages.
|
||||
|
||||
In cases where libraries are only used on a few specific pages, we use
|
||||
"page-specific JavaScript" to prevent the main `main.js` file from
|
||||
becoming unnecessarily large.
|
||||
For example, if you were to visit [gitlab.com/gitlab-org/gitlab-ce/issues](https://gitlab.com/gitlab-org/gitlab-ce/issues),
|
||||
you would be accessing the `app/controllers/projects/issues_controller.rb`
|
||||
controller with the `index` action. If a corresponding file exists at
|
||||
`pages/projects/issues/index/index.js`, it will be compiled into a webpack
|
||||
bundle and included on the page.
|
||||
|
||||
Steps to split page-specific JavaScript from the main `main.js`:
|
||||
> **Note:** Previously we had encouraged the use of
|
||||
> `content_for :page_specific_javascripts` within haml files, along with
|
||||
> manually generated webpack bundles. However under this new system you should
|
||||
> not ever need to manually add an entry point to the `webpack.config.js` file.
|
||||
|
||||
1. Create a directory for the specific page(s), e.g. `graphs/`.
|
||||
1. In that directory, create a `namespace_bundle.js` file, e.g. `graphs_bundle.js`.
|
||||
1. Add the new "bundle" file to the list of entry files in `config/webpack.config.js`.
|
||||
- For example: `graphs: './graphs/graphs_bundle.js',`.
|
||||
1. Move code reliant on these libraries into the `graphs` directory.
|
||||
1. In `graphs_bundle.js` add CommonJS `require('./path_to_some_component.js');` statements to load any other files in this directory. Make sure to use relative urls.
|
||||
1. In the relevant views, add the scripts to the page with the following:
|
||||
In addition to these page-specific bundles, the code within `main.js` and
|
||||
`commons/index.js` are imported on _all_ pages.
|
||||
|
||||
```haml
|
||||
- content_for :page_specific_javascripts do
|
||||
= webpack_bundle_tag 'lib_chart'
|
||||
= webpack_bundle_tag 'graphs'
|
||||
```
|
||||
#### Important Tips and Considerations:
|
||||
|
||||
The above loads `chart.js` and `graphs_bundle.js` for this page only. `chart.js`
|
||||
is separated from the bundle file so it can be cached separately from the bundle
|
||||
and reused for other pages that also rely on the library. For an example, see
|
||||
[this Haml file][page-specific-js-example].
|
||||
- If you are unsure what controller and action corresponds to a given page, you
|
||||
can find this out by checking `document.body.dataset.page` while on any page
|
||||
within gitlab.com.
|
||||
|
||||
- Since `main.js` and `commons/index.js` are imported on all pages, it is
|
||||
important to not add anything to these bundles unless it is truly needed
|
||||
_everywhere_. This includes ubiquitous libraries like `vue`, `axios`, and
|
||||
`jQuery`, as well as code for the main navigation and sidebar.
|
||||
|
||||
- Page-specific javascript entry points should be as lite as possible. These
|
||||
files are exempt from tests, and should be used primarily for instantiation
|
||||
and dependency injection of classes and methods that live in modules outside
|
||||
of the entry point script. Just import, read the DOM, instantiate, and
|
||||
nothing else.
|
||||
|
||||
- **DO NOT ASSUME** that the DOM has been fully loaded and available when an
|
||||
entry point script is run. If you require that some code be run after the
|
||||
DOM has loaded, you must attach an event handler to the `DOMContentLoaded`
|
||||
event with:
|
||||
|
||||
```javascript
|
||||
import initMyWidget from './my_widget';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initMyWidget();
|
||||
});
|
||||
```
|
||||
|
||||
- If a class or a module is specific to a particular route, try to locate it
|
||||
close to the entry point it will be used. For instance, if `my_widget.js` is
|
||||
only imported within `pages/widget/show/index.js`, you should place the
|
||||
module at `pages/widget/show/my_widget.js` and import it with a relative path
|
||||
(e.g. `import initMyWidget from './my_widget';`).
|
||||
|
||||
- If a class or module is used by multiple routes, place it within a shared
|
||||
directory at the closest common parent directory for the entry points that
|
||||
import it. For example, if `my_widget.js` is imported within both
|
||||
`pages/widget/show/index.js` and `pages/widget/run/index.js`, then place the
|
||||
module at `pages/widget/shared/my_widget.js` and import it with a relative
|
||||
path if possible `../shared/my_widget`.
|
||||
|
||||
- For GitLab Enterprise Edition, page-specific entry points will override their
|
||||
Community Edition counterparts with the same name, so if
|
||||
`ee/app/assets/javascripts/pages/foo/bar/index.js` exists, it will take
|
||||
precedence over `app/assets/javascripts/pages/foo/bar/index.js`. If you want
|
||||
to minimize duplicate code, you can import one entry point from the other.
|
||||
This is not done automatically to allow for flexibility in overriding
|
||||
functionality.
|
||||
|
||||
### Code Splitting
|
||||
|
||||
> *TODO* flesh out this section once webpack is ready for code-splitting
|
||||
For any code that does not need to be run immediately upon page load, (e.g.
|
||||
modals, dropdowns, and other behaviors that can be lazy-loaded), you can split
|
||||
your module into asynchronous chunks with dynamic import statements. These
|
||||
imports return a Promise which will be resolved once the script has loaded:
|
||||
|
||||
```javascript
|
||||
import(/* webpackChunkName: 'emoji' */ '~/emoji')
|
||||
.then(/* do something */)
|
||||
.catch(/* report error */)
|
||||
```
|
||||
|
||||
Please try to use `webpackChunkName` when generating these dynamic imports as
|
||||
it will provide a deterministic filename for the chunk which can then be cached
|
||||
the browser.
|
||||
|
||||
More information is available in [webpack's code splitting documentation](https://webpack.js.org/guides/code-splitting/#dynamic-imports).
|
||||
|
||||
### Minimizing page size
|
||||
|
||||
|
@ -95,7 +151,8 @@ General tips:
|
|||
- Prefer font formats with better compression, e.g. WOFF2 is better than WOFF, which is better than TTF.
|
||||
- Compress and minify assets wherever possible (For CSS/JS, Sprockets and webpack do this for us).
|
||||
- If some functionality can reasonably be achieved without adding extra libraries, avoid them.
|
||||
- Use page-specific JavaScript as described above to dynamically load libraries that are only needed on certain pages.
|
||||
- Use page-specific JavaScript as described above to load libraries that are only needed on certain pages.
|
||||
- Use code-splitting dynamic imports wherever possible to lazy-load code that is not needed initially.
|
||||
- [High Performance Animations][high-perf-animations]
|
||||
|
||||
-------
|
||||
|
@ -112,8 +169,5 @@ General tips:
|
|||
[pagespeed-insights]: https://developers.google.com/speed/pagespeed/insights/
|
||||
[google-devtools-profiling]: https://developers.google.com/web/tools/chrome-devtools/profile/?hl=en
|
||||
[browser-diet]: https://browserdiet.com/
|
||||
[d3]: https://d3js.org/
|
||||
[chartjs]: http://www.chartjs.org/
|
||||
[page-specific-js-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/13bb9ed77f405c5f6ee4fdbc964ecf635c9a223f/app/views/projects/graphs/_head.html.haml#L6-8
|
||||
[high-perf-animations]: https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/
|
||||
[flip]: https://aerotwist.com/blog/flip-your-animations/
|
||||
|
|
Loading…
Reference in a new issue