├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── Lazy Way To Performance.pptx
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ ├── Bella.jpg
│ ├── DaisyMae.jpg
│ ├── logo.png
│ ├── profile.jpg
│ └── styles.less
├── components
│ ├── bella.vue
│ ├── daisy.vue
│ └── dog.vue
├── main.js
├── router
│ └── index.js
└── views
│ ├── About.vue
│ └── Home.vue
└── vue.config.js
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | 'eslint:recommended'
9 | ],
10 | parserOptions: {
11 | parser: 'babel-eslint'
12 | },
13 | rules: {
14 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/Lazy Way To Performance.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kb-coder/lazydemo/9f404353922b29d78ce62744a6f90a3abcd8389b/Lazy Way To Performance.pptx
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lazydemo
2 | Watch the full presentation on [Vue Mastery](https://www.vuemastery.com/conferences/vueconf-us-2020/the-lazy-way-to-better-performance/).
3 |
4 | This is a simple demo showing how to:
5 | 1. Lazy Load Components in Vue 2
6 | 2. Lazy Load Routes in Vue 2
7 |
8 | **Bonus:** vue.config.js shows how to split out vendor's chunk
9 |
10 | ## Lazy Load Components
11 | In the /views/home.vue component, Bella is shown by default so we can lazy load the Daisy component since it's not visible on the initial page load.
12 |
13 | What to Lazy Load? Anything that isn't visible on initial page load. (using v-if)
14 | - Tabs (other than the default)
15 | - Side Panel
16 | - Modals
17 |
18 |
19 | ## Lazy Load Routes
20 | In the /router/index.js, the About page is already lazy loaded.
21 |
22 | Typcially, there's not much value in lazy loading the home page, since it is the entry point of the application. If you have multiple entry points, then you may want to lazy load those.
23 |
24 | To Lazy Load the Bella and Daisy routes, just change the import to a dynamic import. Change this:
25 | ```
26 | import Bella from '@/components/bella'
27 | ```
28 | to this:
29 | ```
30 | const Bella = () => import('@/components/bella')
31 | ```
32 |
33 | ### Specifying Webpack Chunk Name
34 | 1. Babel.config.js must have comments to true for this to work.
35 | 2. Add the webpack magic comment to the dynamic import
36 |
37 | ```
38 | const Bella = () => import(/*webpackChunkName: "dogs" */ '@/components/bella')
39 | ```
40 |
41 | ## Project setup
42 | ```
43 | npm install
44 | ```
45 |
46 | ### Compiles and hot-reloads for development
47 | ```
48 | npm run dev
49 | ```
50 |
51 | ### Compiles and minifies for production
52 | ```
53 | npm run build
54 | ```
55 |
56 | ### Lints and fixes files
57 | ```
58 | npm run lint
59 | ```
60 |
61 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@babel/preset-env', {
4 | useBuiltIns: 'usage',
5 | modules: false, // this setting needed for tree shaking (especially with lodash)
6 | corejs: 3
7 | }]
8 | ],
9 | plugins: ['@babel/transform-runtime'],
10 | comments: true // if this is false, then webpackChunkName won't be used.
11 | // If we were using unit tests in this project, this is how it should be set up for mocha or jest
12 | // env: {
13 | // test: {
14 | // presets: ['@babel/preset-env'],
15 | // plugins: ['dynamic-import-node']
16 | // }
17 | // }
18 | }
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lazydemo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "analyze": "vue-cli-service build --report",
7 | "serve": "vue-cli-service serve --open --modern --port 44321",
8 | "build": "vue-cli-service build --modern --mode production",
9 | "lint": "vue-cli-service lint",
10 | "dev-build": "vue-cli-service build --mode development",
11 | "dev": "npm run dev-build && npm run serve",
12 | "start": "npm run dev"
13 | },
14 | "dependencies": {
15 | "core-js": "^3.6.5",
16 | "moment": "2.24.0",
17 | "vue": "^2.6.11",
18 | "vue-router": "^3.1.5"
19 | },
20 | "devDependencies": {
21 | "@babel/plugin-transform-runtime": "^7.12.17",
22 | "@babel/runtime-corejs3": "^7.8.4",
23 | "@vue/cli-plugin-babel": "^4.5.11",
24 | "@vue/cli-plugin-eslint": "^4.5.11",
25 | "@vue/cli-plugin-router": "~4.2.0",
26 | "@vue/cli-service": "~4.2.0",
27 | "@vue/preload-webpack-plugin": "1.1.1",
28 | "babel-eslint": "^10.0.3",
29 | "eslint": "^6.7.2",
30 | "eslint-plugin-vue": "^6.2.2",
31 | "less": "3.11.1",
32 | "less-loader": "5.0.0",
33 | "moment-locales-webpack-plugin": "1.1.2",
34 | "vue-template-compiler": "^2.6.11"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kb-coder/lazydemo/9f404353922b29d78ce62744a6f90a3abcd8389b/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
12 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Home |
5 | Bella |
6 | Daisy Mae |
7 | About
8 |
9 |
10 | {{ today }}
11 |
12 |
13 |
14 |
15 |
28 |
50 |
--------------------------------------------------------------------------------
/src/assets/Bella.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kb-coder/lazydemo/9f404353922b29d78ce62744a6f90a3abcd8389b/src/assets/Bella.jpg
--------------------------------------------------------------------------------
/src/assets/DaisyMae.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kb-coder/lazydemo/9f404353922b29d78ce62744a6f90a3abcd8389b/src/assets/DaisyMae.jpg
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kb-coder/lazydemo/9f404353922b29d78ce62744a6f90a3abcd8389b/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kb-coder/lazydemo/9f404353922b29d78ce62744a6f90a3abcd8389b/src/assets/profile.jpg
--------------------------------------------------------------------------------
/src/assets/styles.less:
--------------------------------------------------------------------------------
1 | img {
2 | object-fit: cover;
3 | object-position: 100% 0;
4 | width: 200px;
5 | height: 237px;
6 | }
7 |
8 | .row {
9 | display: flex;
10 | flex-direction: row;
11 | flex-wrap: wrap;
12 | width: 98%;
13 | padding: 10px;
14 | }
15 | .column {
16 | display: flex;
17 | flex-direction: column;
18 | align-items: center;
19 | justify-content: center;
20 | flex-basis: 100%;
21 | flex: 1;
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/bella.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
27 |
--------------------------------------------------------------------------------
/src/components/daisy.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
26 |
--------------------------------------------------------------------------------
/src/components/dog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ name }}
4 |
5 |
6 |
7 |
8 |
9 | {{ profile }}
10 |
11 |
12 |
13 |
14 |
38 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import router from './router'
4 |
5 | Vue.config.productionTip = false
6 |
7 | new Vue({
8 | router,
9 | render: h => h(App)
10 | }).$mount('#app')
11 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import Home from '@/views/Home.vue'
4 | // import Bella from '@/components/bella'
5 | const Bella = () => import(/* webpackChunkName: "dogs" */ '@/components/bella')
6 | // import Daisy from '@/components/daisy'
7 | const Daisy = () => import(/* webpackChunkName: "dogs" */ '@/components/daisy')
8 | const About = () => import(/* webpackChunkName: "about" */ '@/views/About.vue')
9 |
10 | Vue.use(VueRouter)
11 |
12 | const routes = [
13 | {
14 | path: '/',
15 | name: 'Home',
16 | component: Home
17 | },
18 | {
19 | path: '/about',
20 | name: 'About',
21 | component: About
22 | },
23 | {
24 | path: '/bella-bella',
25 | name: 'BellaBella',
26 | component: Bella
27 | },
28 | {
29 | path: '/daisy-mae',
30 | name: 'DaisyMae',
31 | component: Daisy
32 | }
33 | ]
34 |
35 | const router = new VueRouter({
36 | routes
37 | })
38 |
39 | export default router
40 |
--------------------------------------------------------------------------------
/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Karen Baney
5 |
Senior Consultant
6 |
7 |
8 |
9 |
10 | I love cooking, photography, puppies, and writing. But not cooking photos or cooking puppies.
11 | That would be terrible! But, photographing puppies--AMAZING!
12 |
13 |
14 | Writing is fun. Whether its writing code or writing novels, it's all good.
15 |
16 |
17 | And now for some bacon ipsum.
18 |
19 |
20 | Bacon ipsum dolor amet tri-tip shank salami hamburger, frankfurter andouille beef ribs rump boudin ham short loin picanha brisket pork belly chislic. Strip steak sirloin t-bone burgdoggen. Shoulder kielbasa meatball, shankle shank flank chicken ham hock pork chop biltong t-bone turducken. Pancetta pastrami jowl, hamburger tongue beef ribs landjaeger ground round tri-tip kevin strip steak turducken chicken prosciutto pork.
21 |
22 |
23 |
24 |
25 |
34 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Toggle Dogs
5 |
6 |
9 |
12 |
13 |
14 |
15 |
43 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const MomentLocalesPlugin = require('moment-locales-webpack-plugin') // for moment tree shaking locales
3 | const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin')
4 |
5 | module.exports = {
6 | publicPath: '/',
7 | runtimeCompiler: true,
8 | productionSourceMap: process.env.NODE_ENV !== 'production',
9 | crossorigin: 'use-credentials',
10 | lintOnSave: true,
11 | configureWebpack: config => {
12 | if (process.env.NODE_ENV === 'development') {
13 | config.devtool = 'source-map'
14 | } else if (process.env.NODE_ENV === 'test') {
15 | config.devtool = 'cheap-module-eval-source-map'
16 | }
17 | },
18 | chainWebpack: config => {
19 | // for moment tree shaking locales
20 | config.resolve.alias
21 | .set('moment', 'moment/moment.js')
22 |
23 | // these settings apply to production or development, but will break mocha tests in the test NODE_ENV
24 | if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'development') {
25 | // By default vue-cli sets rel=prefetch on chunks-vendor.js and app.js in the index.html that gets generated in index.html.
26 | // This setting removes vue-cli's this default so we can use more chunks and let them be loaded dynamically.
27 | config.plugins.delete('prefetch')
28 | config.plugin('prefetch')
29 | .use(PreloadWebpackPlugin, [{
30 | rel: 'prefetch',
31 | include: 'asyncChunks',
32 | // do not prefetch async routes
33 | fileBlacklist: [
34 | /myasyncRoute(.)+?\.js$/,
35 | /\.map$/
36 | ]
37 | },
38 | {
39 | rel: 'preload',
40 | include: 'initial',
41 | fileWhitelist: [
42 | /(^@vue)(.*)(\.js$)/,
43 | /(^vue)(.*)(\.js$)/
44 | ],
45 | // do not preload map files or hot update files
46 | fileBlacklist: [
47 | /\.map$/,
48 | /hot-update\.js$/
49 | ]
50 | }])
51 |
52 | // override vue's default chunks because their chunking is too big.
53 | config.optimization.delete('splitChunks')
54 | config.optimization.set('splitChunks', {
55 | cacheGroups: {
56 | // Vue modules
57 | vue: {
58 | test: /[\\/]node_modules[\\/](@vue.*|vue.*)[\\/]/,
59 | name: 'vue',
60 | enforce: true,
61 | priority: 20,
62 | chunks: 'initial'
63 | },
64 | // all other modules modules
65 | vendors: {
66 | name: 'chunk-vendors',
67 | test (module, chunks) {
68 | // `module.resource` contains the absolute path of the file on disk.
69 | // Note the usage of `path.sep` instead of / or \, for cross-platform compatibility.
70 | const path = require('path')
71 | return module.resource &&
72 | !module.resource.includes(`${path.sep}node_modules${path.sep}@vue`) &&
73 | !module.resource.includes(`${path.sep}node_modules${path.sep}vue`) &&
74 | !module.resource.includes(`${path.sep}src${path.sep}`)
75 | },
76 | maxSize: 500000,
77 | priority: 10,
78 | enforce: true,
79 | chunks: 'all' // doesn't get created without 'all' here
80 | },
81 | // default common chunk settings from Vue
82 | common: {
83 | name: 'chunk-common',
84 | minChunks: 2,
85 | priority: 5,
86 | chunks: 'initial',
87 | reuseExistingChunk: true
88 | }
89 | }
90 | })
91 |
92 | // Webpack includes a small piece of runtime code that gets inserted into the last chunk created. This could cause our vendor
93 | // chunk to change unnecessarily. So the next line causes this runtime to be put in a separate file.
94 | config.optimization.set('runtimeChunk', true)
95 | }
96 |
97 | if (process.env.NODE_ENV === 'production') {
98 | // for moment tree shaking locales
99 | // Include only 'en-us' (which is always included) and 'es-us' locales
100 | config.plugin('moment')
101 | .use(MomentLocalesPlugin, [{
102 | localesToKeep: ['es-us']
103 | }])
104 | .use(new webpack.IgnorePlugin({
105 | resourceRegExp: /^\.\/locale$/,
106 | contextRegExp: /moment$/
107 | }))
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------