summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Greiling <mike@pixelcog.com>2018-03-12 15:19:19 -0500
committerMike Greiling <mike@pixelcog.com>2018-03-12 15:19:19 -0500
commitab8ad58ea1b49387081a862f7b58efc538d9a708 (patch)
tree2aed5668e0465741328783e2e6a9372e1f1dab66
parent4d726d1f9fc701449037fb81218860c452a77e20 (diff)
downloadgitlab-ce-ab8ad58ea1b49387081a862f7b58efc538d9a708.tar.gz
rewrite docs for page-specific-javascript
-rw-r--r--doc/development/fe_guide/performance.md122
1 files changed, 88 insertions, 34 deletions
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index 98e43931a02..82e110ba8e3 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -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.
-
-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.
-
-Steps to split page-specific JavaScript from the main `main.js`:
-
-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:
-
-```haml
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'lib_chart'
- = webpack_bundle_tag 'graphs'
-```
-
-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].
+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.
+
+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.
+
+> **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.
+
+In addition to these page-specific bundles, the code within `main.js` and
+`commons/index.js` are imported on _all_ pages.
+
+#### Important Tips and Considerations:
+
+- 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/