├── .editorconfig
├── .env.exaample
├── .gitignore
├── .npmrc
├── README.md
├── assets
└── demo.gif
├── components
├── TheHeader.vue
└── TheNav.vue
├── layouts
└── default.vue
├── nuxt.config.js
├── package-lock.json
├── package.json
├── pages
├── help.vue
├── index.vue
└── pgp.vue
├── plugins
└── guess.js
├── routes.json
└── store
└── index.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | indent_size = 2
9 | indent_style = space
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.env.exaample:
--------------------------------------------------------------------------------
1 | GA=XXXXXXX
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .nuxt
3 | NOTE.md
4 | .env
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | @daliborgogic:registry=https://npm.pkg.github.com/
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Guess.js + Nuxt.js
2 |
3 | **[Guess.js](https://github.com/guess-js/guess) is a collection of libraries & tools for enabling data-driven user-experience on the web.**
4 |
5 | **[Nuxt.js](https://github.com/nuxt/nuxt.js) is a Vue.js Meta Framework to create complex, fast & universal web applications quickly.**
6 |
7 |
8 |
9 | 
10 | DEMO
11 |
12 |
13 |
14 | In this particular example, we combine Guess.js with Nuxt.js to introduce predictive prefetching of JavaScript bundles. Based on user navigation patterns collected from Google Analytics or other source, Guess.js builds a machine-learning model to predict and prefetch JavaScript that will be required in each subsequent page.
15 |
16 | Based on early benchmarks, this can improve the perceived page load performance with 20%.
17 |
18 | For more information on Guess.js, take a look at the following links:
19 | * [Google I/O announcement](https://www.youtube.com/watch?time_continue=2093&v=Mv-l3-tJgGk) by Addy Osmani
20 | * [Introducing Guess.js - a toolkit for enabling data-driven user-experiences on the Web](https://blog.mgechev.com/2018/05/09/introducing-guess-js-data-driven-user-experiences-web/)
21 | * [Using Guess.js with static sites](https://github.com/guess-js/guess/tree/master/experiments/guess-static-sites)
22 | * [Using Guess.js with Angular, React, and Gatsby](https://github.com/guess-js/guess/tree/master/packages/guess-webpack)
23 |
24 | ### Usage
25 |
26 | ```bash
27 | $ git clone git@github.com:daliborgogic/guess-nuxt && \
28 | cd guess-nuxt && \
29 | npm i
30 |
31 | $ mv .env.example .env
32 | # Update GA in .env
33 |
34 | # Dev
35 | $ npm run dev
36 |
37 | # Build
38 | $ npm run build
39 |
40 | # Start
41 | $ npm start
42 | ```
43 |
44 | ### Integration
45 |
46 | Guess.js (**0.1.5 and above**) works with Nuxt.js with only two points of integration. All you need to do is add the `GuessPlugin` to `nuxt.config.js` and introduce a snippet for prefetching the pages which are likely to be visited Nuxt.
47 |
48 | The following sections describe both points in details.
49 |
50 | ### Webpack Config
51 |
52 | All you need is to extend the webpack config of your Nuxt.js application is to add the `GuessPlugin` to `nuxt.config.js` file, located in the root of your project. If the file does not exist, create it and add the following content:
53 |
54 | ```javascript
55 | const { GuessPlugin } = require('guess-webpack')
56 | const { GA } = process.env
57 |
58 | module.exports = {
59 | build: {
60 | extend(config, ctx) {
61 | if (ctx.isClient) {
62 | config.plugins.push(
63 | new GuessPlugin({
64 | GA,
65 | runtime: {
66 | delegate: true
67 | },
68 | routeProvider: false
69 | })
70 | )
71 | }
72 | }
73 | }
74 | ```
75 |
76 | The routes that `guess()` returns depend on the Google Analytics report that it has extracted, together with the user's effective connection type.
77 |
78 | ### Credits
79 |
80 | Based on [guess-next](https://github.com/mgechev/guess-next) by Minko Gechev [mgechev](https://github.com/mgechev) and Sébastien Chopin [Atinux](https://github.com/Atinux) for pull request [#1](https://github.com/daliborgogic/guess-nuxt/pull/1).
81 |
82 | ### License
83 |
84 | MIT
85 |
--------------------------------------------------------------------------------
/assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daliborgogic/guess-nuxt/2315d497a1443e35994326310cdfcd2163ba9c3d/assets/demo.gif
--------------------------------------------------------------------------------
/components/TheHeader.vue:
--------------------------------------------------------------------------------
1 |
2 | header
3 | TheNav
4 |
5 | p Navigate through the application to see the magic
6 | p The user will likely visit
7 | no-ssr
8 | ul
9 | li(v-for="c, index in predictions" :key="index") {{ c }}
10 |
11 | div I used the statistics you already have to make this prediction.
12 |
13 |
14 |
27 |
--------------------------------------------------------------------------------
/components/TheNav.vue:
--------------------------------------------------------------------------------
1 |
2 | nav
3 | nuxt-link(to="/") HOME
4 | nuxt-link(to="/help") HELP
5 | nuxt-link(to="/pgp") PGP
6 |
7 |
8 |
25 |
--------------------------------------------------------------------------------
/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 | .container
3 | TheHeader
4 | nuxt
5 |
6 |
7 |
14 |
15 |
52 |
--------------------------------------------------------------------------------
/nuxt.config.js:
--------------------------------------------------------------------------------
1 | import { guess } from 'guess-webpack/api';
2 |
3 | const { GuessPlugin } = require('guess-webpack')
4 | const { GA } = process.env
5 |
6 | export default {
7 | head: {
8 | htmlAttrs: {
9 | lang: 'en',
10 | },
11 | meta: [
12 | { charset: 'utf-8' },
13 | { name: 'viewport', content: 'width=device-width, initial-scale=1' },
14 | { hid: 'description', name: 'description', content: 'Experiment for integration of Guess.js with Nuxt.js' }
15 | ]
16 | },
17 |
18 | plugins: [
19 | { src: '~/plugins/guess', ssr: false }
20 | ],
21 |
22 | build: {
23 | extend(config, ctx) {
24 | if (ctx.isClient) {
25 | const guessOptions = {
26 | // Hints Guess to not perform pre-fetching and delegate this logic to its consumer.
27 | runtime: {
28 | delegate: true,
29 | prefetchConfig: {
30 | '4g': 0.3,
31 | '3g': 0.3,
32 | '2g': 0.3,
33 | 'slow-2g': 0.3
34 | }
35 | },
36 | // Guess does not have to collect the routes and the corresponding bundle entry points.
37 | routeProvider: false
38 | }
39 | if (GA) guessOptions.GA = GA
40 | else guessOptions.reportProvider = () => Promise.resolve(JSON.parse(require('fs').readFileSync('./routes.json')));
41 |
42 | config.plugins.push(
43 | new GuessPlugin(guessOptions)
44 | )
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@daliborgogic/guess-nuxt",
3 | "version": "1.0.0",
4 | "description": "Guess.js with Nuxt.js",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nuxt",
8 | "build": "nuxt build",
9 | "start": "nuxt-start"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/daliborgogic/guess-nuxt.git"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "MIT",
18 | "bugs": {
19 | "url": "https://github.com/daliborgogic/guess-nuxt/issues"
20 | },
21 | "homepage": "https://github.com/daliborgogic/guess-nuxt#readme",
22 | "dependencies": {
23 | "nuxt-start-edge": "^2.0.0-25603961.baaf67d"
24 | },
25 | "devDependencies": {
26 | "guess-webpack": "^0.1.5",
27 | "nuxt-edge": "^2.0.0-25603961.baaf67d",
28 | "pug": "^2.0.3",
29 | "pug-plain-loader": "^1.0.0",
30 | "stylus": "^0.54.5",
31 | "stylus-loader": "^3.0.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/pages/help.vue:
--------------------------------------------------------------------------------
1 |
2 | div
3 | h1 Config
4 | pre.
5 | new GuessPlugin({
6 | // GA view ID.
7 | GA: GAViewID,
8 | // Hints Guess to not perform pre-fetching and delegate this logic to
9 | // its consumer.
10 | runtime: { delegate: true },
11 | // Since Gatsby already has the required metadata for pre-fetching,
12 | // Guess does not have to collect the routes and the corresponding
13 | // bundle entry points.
14 | routeProvider: false,
15 | // Optional argument. It takes the data for the last year if not
16 | // specified.
17 | period: period ? period : undefined,
18 | })
19 |
20 |
21 |
28 |
--------------------------------------------------------------------------------
/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 | div
3 | h1 Nuxt.js + Guess.js
4 | p This page demonstrates how you can use Guess.js for predictive prefetching with Nuxt.js
5 |
6 |
--------------------------------------------------------------------------------
/pages/pgp.vue:
--------------------------------------------------------------------------------
1 |
2 | div
3 |
4 | h1 PGP Public Key
5 | pre.
6 | -----BEGIN PGP PUBLIC KEY BLOCK-----
7 | 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
53 | 4SKppzJqZO9qAKRKEjb3xw=
54 | ==zPVM
55 | -----END PGP PUBLIC KEY BLOCK-----
56 |
57 |
58 |
65 |
--------------------------------------------------------------------------------
/plugins/guess.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { guess } from 'guess-webpack/api'
3 |
4 | export default ({ app: { router, store } }) => {
5 | router.afterEach(to => {
6 | // Wait for page to be displayed
7 | Vue.nextTick(() => {
8 | let predictions = Object.keys(guess()).sort((a, b) => a.probability - b.probability)
9 | predictions.forEach(path => {
10 | router.getMatchedComponents(path).forEach(Component => {
11 | if (typeof Component === 'function') {
12 | try { Component() } catch (e) {}
13 | }
14 | })
15 | })
16 | store.commit('setPredictions', predictions)
17 | })
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "/": {
3 | "/help": 80,
4 | "/pgp": 20
5 | },
6 | "/help": {
7 | "/": 20,
8 | "/pgp": 80
9 | },
10 | "/pgp": {
11 | "/": 80,
12 | "/help": 20
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/store/index.js:
--------------------------------------------------------------------------------
1 | import Vuex from 'vuex'
2 |
3 | const store = () => {
4 | return new Vuex.Store({
5 | state: {
6 | predictions: []
7 | },
8 |
9 | mutations: {
10 | setPredictions: (state, value) => state.predictions = value
11 | }
12 | })
13 | }
14 |
15 | export default store
16 |
--------------------------------------------------------------------------------