├── .browserslistrc
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── README.md
├── babel.config.js
├── generate.js
├── package.json
├── postcss.config.js
├── public
├── _redirects
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ └── logo.png
├── components
│ ├── Benchmark.vue
│ ├── HeavyComponent.vue
│ ├── Home.vue
│ ├── PlayToggle.vue
│ ├── benchmarks
│ │ ├── child
│ │ │ ├── Child.vue
│ │ │ ├── ChildOff.vue
│ │ │ └── ChildOn.vue
│ │ ├── deferred
│ │ │ ├── Deferred.vue
│ │ │ ├── DeferredOff.vue
│ │ │ ├── DeferredOn.vue
│ │ │ └── SimplePage.vue
│ │ ├── fetch-items
│ │ │ ├── FetchItemViewFunctional.vue
│ │ │ ├── FetchItemViewNormal.vue
│ │ │ ├── FetchItems.vue
│ │ │ └── Particles.vue
│ │ ├── functional
│ │ │ ├── Functional.vue
│ │ │ ├── FunctionalOff.vue
│ │ │ └── FunctionalOn.vue
│ │ ├── hide
│ │ │ ├── Hide.vue
│ │ │ ├── HideOff.vue
│ │ │ └── HideOn.vue
│ │ ├── keep-alive
│ │ │ └── KeepAlive.vue
│ │ ├── local-var
│ │ │ ├── LocalVar.vue
│ │ │ ├── LocalVarOff.vue
│ │ │ └── LocalVarOn.vue
│ │ └── static
│ │ │ ├── Static.vue
│ │ │ ├── StaticOff.vue
│ │ │ ├── StaticOn.vue
│ │ │ └── colors.js
│ └── index.js
├── generate.js
├── main.js
├── mixins
│ └── Defer.js
├── particlesjs-config.json
├── plugins.js
├── router.js
├── store.js
└── utils.js
└── yarn.lock
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not ie <= 8
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | trim_trailing_whitespace = true
5 | insert_final_newline = true
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true,
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | '@vue/standard',
9 | ],
10 | rules: {
11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
13 | 'comma-dangle': ['error', 'always-multiline'],
14 | 'vue/valid-v-for': 'off',
15 | },
16 | parserOptions: {
17 | parser: 'babel-eslint',
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vueconfus-perf-secrets
2 |
3 | [Slides](https://slides.com/akryum/vueconfus-2019)
4 |
5 | ## Project setup
6 | ```
7 | yarn install
8 | ```
9 |
10 | ### Compiles and hot-reloads for development
11 | ```
12 | yarn run serve
13 | ```
14 |
15 | ### Compiles and minifies for production
16 | ```
17 | yarn run build
18 | ```
19 |
20 | ### Run your tests
21 | ```
22 | yarn run test
23 | ```
24 |
25 | ### Lints and fixes files
26 | ```
27 | yarn run lint
28 | ```
29 |
30 | ### Customize configuration
31 | See [Configuration Reference](https://cli.vuejs.org/config/).
32 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app',
4 | ],
5 | }
6 |
--------------------------------------------------------------------------------
/generate.js:
--------------------------------------------------------------------------------
1 | const { writeFileSync } = require('fs')
2 | const faker = require('faker')
3 | const CliProgress = require('cli-progress')
4 |
5 | const bar = new CliProgress.Bar({}, CliProgress.Presets.shades_classic)
6 |
7 | const count = 4000
8 |
9 | bar.start(count, 0)
10 |
11 | const items = []
12 | for (let i = 0; i < count; i++) {
13 | const posts = []
14 | for (let p = 0; p < 10; p++) {
15 | posts.push(faker.lorem.paragraphs())
16 | }
17 | items.push(Object.assign(
18 | {},
19 | faker.helpers.createCard(),
20 | {
21 | avatar: faker.image.avatar(),
22 | posts,
23 | },
24 | ))
25 | bar.update(i + 1)
26 | }
27 |
28 | writeFileSync('./src/items.json', JSON.stringify(items, null, 2), {
29 | encoding: 'utf8',
30 | })
31 |
32 | bar.stop()
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vueconfus-perf-secrets",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint",
9 | "start": "serve ./dist -s"
10 | },
11 | "dependencies": {
12 | "@vue/ui": "^0.5.6",
13 | "faker": "^4.1.0",
14 | "fps-indicator": "^1.3.0",
15 | "lodash.clone": "^4.5.0",
16 | "particles.js": "^2.0.0",
17 | "vue": "^2.6.6",
18 | "vue-router": "^3.0.1",
19 | "vue-virtual-scroller": "^1.0.0-rc.2",
20 | "vuex": "^3.0.1"
21 | },
22 | "devDependencies": {
23 | "@vue/cli-plugin-babel": "^3.5.0",
24 | "@vue/cli-plugin-eslint": "^3.5.0",
25 | "@vue/cli-service": "^3.5.0",
26 | "@vue/eslint-config-standard": "^4.0.0",
27 | "babel-eslint": "^10.0.1",
28 | "cli-progress": "^2.1.1",
29 | "eslint": "^5.8.0",
30 | "eslint-plugin-vue": "^5.0.0",
31 | "serve": "^10.1.2",
32 | "stylus": "^0.54.5",
33 | "stylus-loader": "^3.0.2",
34 | "vue-template-compiler": "^2.5.21"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {},
4 | },
5 | }
6 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Akryum/vue-9-perf-secrets/46483233d0edb80c511eb899776268c7f7768220/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | vueconfus-perf-secrets
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Home
5 |
6 |
7 |
8 | Functional components
9 | Child splitting
10 | Local var
11 | Reused Dom
12 | Keep alive
13 | Deferred features
14 | Vuex demo
15 |
16 |
17 |
18 |
19 |
20 |
42 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Akryum/vue-9-perf-secrets/46483233d0edb80c511eb899776268c7f7768220/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/Benchmark.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
50 |
51 |
84 |
--------------------------------------------------------------------------------
/src/components/HeavyComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
39 |
40 |
50 |
--------------------------------------------------------------------------------
/src/components/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 |
17 |
--------------------------------------------------------------------------------
/src/components/PlayToggle.vue:
--------------------------------------------------------------------------------
1 |
2 | Stop
3 | Play
4 |
5 |
6 |
13 |
14 |
18 |
--------------------------------------------------------------------------------
/src/components/benchmarks/child/Child.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
71 |
72 |
86 |
--------------------------------------------------------------------------------
/src/components/benchmarks/child/ChildOff.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
22 |
--------------------------------------------------------------------------------
/src/components/benchmarks/child/ChildOn.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
29 |
--------------------------------------------------------------------------------
/src/components/benchmarks/deferred/Deferred.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Simple page
6 | Heavy page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
67 |
68 |
80 |
--------------------------------------------------------------------------------
/src/components/benchmarks/deferred/DeferredOff.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
I'm an heavy page
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/components/benchmarks/deferred/DeferredOn.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
I'm an heavy page
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
24 |
--------------------------------------------------------------------------------
/src/components/benchmarks/deferred/SimplePage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
I'm a simple page
5 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/components/benchmarks/fetch-items/FetchItemViewFunctional.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
![avatar]()
5 |
6 |
{{ props.item.data.username }}
7 |
{{ props.item.data.email }}
8 |
{{ props.item.data.phone }}
9 |
{{ props.item.data.website }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
44 |
--------------------------------------------------------------------------------
/src/components/benchmarks/fetch-items/FetchItemViewNormal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
![avatar]()
5 |
6 |
{{ item.data.username }}
7 |
{{ item.data.email }}
8 |
{{ item.data.phone }}
9 |
{{ item.data.website }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
50 |
--------------------------------------------------------------------------------
/src/components/benchmarks/fetch-items/FetchItems.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Generated items: {{ generatedCount }}
5 | Commited items: {{ itemCount }}
6 |
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 | View list
21 |
22 |
23 | Loading animation
24 |
25 |
28 |
35 |
36 |
40 | Generate items
41 |
42 |
43 |
44 |
45 | Time-slicing
46 |
47 |
50 |
57 |
58 |
59 | Partial reactivity
60 |
61 |
62 |
63 |
64 |
65 |
71 | Commit items
72 |
73 |
74 |
75 |
79 | Loading...
80 |
81 |
82 |
83 |
84 |
85 |
91 |
92 |
96 |
97 |
98 |
99 |
100 |
101 |
105 |
106 |
112 |
113 |
114 | Disabled to prevent crash
115 |
116 |
117 |
118 |
119 |
120 |
121 |
180 |
181 |
208 |
--------------------------------------------------------------------------------
/src/components/benchmarks/fetch-items/Particles.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
24 |
--------------------------------------------------------------------------------
/src/components/benchmarks/functional/Functional.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
27 |
28 |
29 |
30 |
31 |
75 |
76 |
95 |
--------------------------------------------------------------------------------
/src/components/benchmarks/functional/FunctionalOff.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/components/benchmarks/functional/FunctionalOn.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/src/components/benchmarks/hide/Hide.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
69 |
70 |
96 |
--------------------------------------------------------------------------------
/src/components/benchmarks/hide/HideOff.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
--------------------------------------------------------------------------------
/src/components/benchmarks/hide/HideOn.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
--------------------------------------------------------------------------------
/src/components/benchmarks/keep-alive/KeepAlive.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Simple page
6 | Heavy page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
72 |
73 |
91 |
--------------------------------------------------------------------------------
/src/components/benchmarks/local-var/LocalVar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
71 |
72 |
86 |
--------------------------------------------------------------------------------
/src/components/benchmarks/local-var/LocalVarOff.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ result }}
3 |
4 |
5 |
22 |
--------------------------------------------------------------------------------
/src/components/benchmarks/local-var/LocalVarOn.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ result }}
3 |
4 |
5 |
22 |
--------------------------------------------------------------------------------
/src/components/benchmarks/static/Static.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
21 |
22 |
23 |
24 |
25 |
64 |
--------------------------------------------------------------------------------
/src/components/benchmarks/static/StaticOff.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
{{ n }}
17 |
18 |
{{ 'Hello' }}
19 |
20 |
21 |
22 |
27 |
--------------------------------------------------------------------------------
/src/components/benchmarks/static/StaticOn.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
{{ n }}
15 |
16 |
Hello
17 |
18 |
19 |
20 |
25 |
--------------------------------------------------------------------------------
/src/components/benchmarks/static/colors.js:
--------------------------------------------------------------------------------
1 | export default [
2 | '2c3e50',
3 | '4f6f7f',
4 | '1d2935',
5 | '42b983',
6 | '6806c1',
7 | 'e83030',
8 | 'ea6e00',
9 | ]
10 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Benchmark from './Benchmark.vue'
3 | import PlayToggle from './PlayToggle.vue'
4 | import Heavy from './HeavyComponent.vue'
5 |
6 | Vue.component('Benchmark', Benchmark)
7 | Vue.component('PlayToggle', PlayToggle)
8 | Vue.component('Heavy', Heavy)
9 |
--------------------------------------------------------------------------------
/src/generate.js:
--------------------------------------------------------------------------------
1 | export async function generate (count) {
2 | const faker = (await import(/* webpackChunkName: 'faker' */ 'faker')).default
3 | const items = []
4 | for (let i = 0; i < count; i++) {
5 | // const posts = []
6 | // for (let p = 0; p < 10; p++) {
7 | // posts.push(faker.lorem.paragraphs())
8 | // }
9 | items.push(Object.assign(
10 | {},
11 | faker.helpers.createCard(),
12 | {
13 | avatar: faker.image.avatar(),
14 | // posts,
15 | },
16 | ))
17 | }
18 | return items
19 | }
20 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import './plugins'
2 | import './components'
3 | import Vue from 'vue'
4 | import fps from 'fps-indicator'
5 | import App from './App.vue'
6 | import router from './router'
7 | import store from './store'
8 |
9 | Vue.config.productionTip = false
10 |
11 | fps({
12 | position: 'top-right',
13 | style: `
14 | font-size: 24px;
15 | `,
16 | })
17 |
18 | new Vue({
19 | router,
20 | store,
21 | ...App,
22 | }).$mount('#app')
23 |
--------------------------------------------------------------------------------
/src/mixins/Defer.js:
--------------------------------------------------------------------------------
1 | export default function (count = 10) {
2 | // @vue/component
3 | return {
4 | data () {
5 | return {
6 | displayPriority: 0,
7 | }
8 | },
9 |
10 | mounted () {
11 | this.runDisplayPriority()
12 | },
13 |
14 | methods: {
15 | runDisplayPriority () {
16 | const step = () => {
17 | requestAnimationFrame(() => {
18 | this.displayPriority++
19 | if (this.displayPriority < count) {
20 | step()
21 | }
22 | })
23 | }
24 | step()
25 | },
26 |
27 | defer (priority) {
28 | return this.displayPriority >= priority
29 | },
30 | },
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/particlesjs-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "particles": {
3 | "number": {
4 | "value": 50,
5 | "density": {
6 | "enable": true,
7 | "value_area": 800
8 | }
9 | },
10 | "color": {
11 | "value": "#ffffff"
12 | },
13 | "shape": {
14 | "type": "circle",
15 | "stroke": {
16 | "width": 0,
17 | "color": "#000000"
18 | },
19 | "polygon": {
20 | "nb_sides": 5
21 | },
22 | "image": {
23 | "src": "img/github.svg",
24 | "width": 100,
25 | "height": 100
26 | }
27 | },
28 | "opacity": {
29 | "value": 1,
30 | "random": false,
31 | "anim": {
32 | "enable": false,
33 | "speed": 1,
34 | "opacity_min": 0.1,
35 | "sync": false
36 | }
37 | },
38 | "size": {
39 | "value": 5,
40 | "random": true,
41 | "anim": {
42 | "enable": false,
43 | "speed": 40,
44 | "size_min": 0.1,
45 | "sync": false
46 | }
47 | },
48 | "line_linked": {
49 | "enable": true,
50 | "distance": 150,
51 | "color": "#ffffff",
52 | "opacity": 0.6,
53 | "width": 2
54 | },
55 | "move": {
56 | "enable": true,
57 | "speed": 6,
58 | "direction": "none",
59 | "random": false,
60 | "straight": false,
61 | "out_mode": "out",
62 | "bounce": false,
63 | "attract": {
64 | "enable": false,
65 | "rotateX": 600,
66 | "rotateY": 1200
67 | }
68 | }
69 | },
70 | "interactivity": {
71 | "detect_on": "canvas",
72 | "events": {
73 | "onhover": {
74 | "enable": true,
75 | "mode": "repulse"
76 | },
77 | "onclick": {
78 | "enable": true,
79 | "mode": "push"
80 | },
81 | "resize": true
82 | },
83 | "modes": {
84 | "grab": {
85 | "distance": 400,
86 | "line_linked": {
87 | "opacity": 1
88 | }
89 | },
90 | "bubble": {
91 | "distance": 400,
92 | "size": 40,
93 | "duration": 2,
94 | "opacity": 8,
95 | "speed": 3
96 | },
97 | "repulse": {
98 | "distance": 200,
99 | "duration": 0.4
100 | },
101 | "push": {
102 | "particles_nb": 4
103 | },
104 | "remove": {
105 | "particles_nb": 2
106 | }
107 | }
108 | },
109 | "retina_detect": true
110 | }
--------------------------------------------------------------------------------
/src/plugins.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueUi from '@vue/ui'
3 | import '@vue/ui/dist/vue-ui.css'
4 | import VueVirtualScroller from 'vue-virtual-scroller'
5 | import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
6 |
7 | Vue.use(VueUi)
8 | Vue.use(VueVirtualScroller)
9 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import Home from './components/Home.vue'
4 |
5 | Vue.use(Router)
6 |
7 | export default new Router({
8 | mode: 'history',
9 | base: process.env.BASE_URL,
10 | routes: [
11 | {
12 | path: '/',
13 | name: 'home',
14 | component: Home,
15 | },
16 | {
17 | path: '/bench/static',
18 | name: 'bench-static',
19 | component: () => import(/* webpackChunkName: "bench-static" */ './components/benchmarks/static/Static.vue'),
20 | },
21 | {
22 | path: '/bench/child',
23 | name: 'bench-child',
24 | component: () => import(/* webpackChunkName: "bench-child" */ './components/benchmarks/child/Child.vue'),
25 | },
26 | {
27 | path: '/bench/local-var',
28 | name: 'bench-local-var',
29 | component: () => import(/* webpackChunkName: "bench-local-var" */ './components/benchmarks/local-var/LocalVar.vue'),
30 | },
31 | {
32 | path: '/bench/fetch-items',
33 | name: 'bench-fetch-items',
34 | component: () => import(/* webpackChunkName: "bench-fetch-items" */ './components/benchmarks/fetch-items/FetchItems.vue'),
35 | },
36 | {
37 | path: '/bench/deferred',
38 | name: 'bench-deferred',
39 | component: () => import(/* webpackChunkName: "bench-deferred" */ './components/benchmarks/deferred/Deferred.vue'),
40 | },
41 | {
42 | path: '/bench/hide',
43 | name: 'bench-hide',
44 | component: () => import(/* webpackChunkName: "bench-hide" */ './components/benchmarks/hide/Hide.vue'),
45 | },
46 | {
47 | path: '/bench/keep-alive',
48 | component: () => import(/* webpackChunkName: "bench-keep-alive" */ './components/benchmarks/keep-alive/KeepAlive.vue'),
49 | children: [
50 | {
51 | path: '',
52 | name: 'bench-keep-alive',
53 | component: () => import(/* webpackChunkName: "bench-keep-alive-simple" */ './components/benchmarks/deferred/SimplePage.vue'),
54 | },
55 | {
56 | path: 'heavy',
57 | name: 'bench-keep-alive-heavy',
58 | component: () => import(/* webpackChunkName: "bench-keep-alive-heavy" */ './components/benchmarks/deferred/DeferredOff.vue'),
59 | },
60 | ],
61 | },
62 | {
63 | path: '/bench/functional',
64 | name: 'bench-functional',
65 | component: () => import(/* webpackChunkName: "bench-functional" */ './components/benchmarks/functional/Functional.vue'),
66 | },
67 | ],
68 | })
69 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import { generate } from './generate'
4 | import { JobQueue, splitArray } from './utils'
5 |
6 | let items = []
7 |
8 | function optimizeItem (item) {
9 | const data = {
10 | id: uid++,
11 | vote: 0,
12 | }
13 | Object.defineProperty(data, 'data', {
14 | configurable: false,
15 | value: item,
16 | })
17 | return data
18 | }
19 |
20 | Vue.use(Vuex)
21 |
22 | let uid = 0
23 |
24 | const store = new Vuex.Store({
25 | state () {
26 | return {
27 | items: [],
28 | loading: false,
29 | progress: 0,
30 | generatedCount: 0,
31 | }
32 | },
33 | getters: {
34 | itemCount: state => state.items.length,
35 | },
36 | mutations: {
37 | clearItems (state) {
38 | state.items = []
39 | },
40 | addItems (state, items) {
41 | state.items.push(...items)
42 | },
43 | loading (state, value) {
44 | state.loading = value
45 | },
46 | progress (state, value) {
47 | state.progress = value
48 | },
49 | voteItem (state, item) {
50 | item.vote++
51 | },
52 | generatedCount (state, value) {
53 | state.generatedCount = value
54 | },
55 | },
56 | actions: {
57 | async generateItems ({ commit }, count) {
58 | items = await generate(count)
59 | commit('generatedCount', count)
60 | },
61 |
62 | commitItems ({ commit }, { splitCount, split, optimize }) {
63 | commit('clearItems')
64 | commit('loading', true)
65 | requestAnimationFrame(async resolve => {
66 | const data = JSON.parse(JSON.stringify(items)).map(
67 | item => optimize ? optimizeItem(item) : {
68 | id: uid++,
69 | data: item,
70 | vote: 0,
71 | }
72 | )
73 | const timeStart = performance.now()
74 | if (split) {
75 | const queue = new JobQueue()
76 | splitArray(data, splitCount).forEach(
77 | chunk => queue.addJob(done => requestAnimationFrame(() => {
78 | commit('addItems', chunk)
79 | done()
80 | }))
81 | )
82 | await queue.start()
83 | } else {
84 | commit('addItems', data)
85 | }
86 | const timeEnd = performance.now()
87 | console.log('time', Math.round(timeEnd - timeStart), 'ms', 'split', split, 'splitCount', splitCount, 'optimize', optimize)
88 | commit('loading', false)
89 | })
90 | },
91 | },
92 | })
93 |
94 | export default store
95 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export function splitArray (list, chunkLength) {
2 | const chunks = []
3 | let chunk = []
4 | let i = 0
5 | let l = 0
6 | let n = list.length
7 | while (i < n) {
8 | chunk.push(list[i])
9 | l++
10 | if (l === chunkLength) {
11 | chunks.push(chunk)
12 | chunk = []
13 | l = 0
14 | }
15 | i++
16 | }
17 | chunk.length && chunks.push(chunk)
18 | return chunks
19 | }
20 |
21 | export class JobQueue {
22 | constructor ({ autoStart = false } = {}) {
23 | this.autoStart = autoStart
24 |
25 | this._queue = []
26 | this._running = false
27 | this._results = []
28 | this._resolves = []
29 | this._rejects = []
30 | this._runId = 0
31 | }
32 |
33 | get length () {
34 | return this._queue.length
35 | }
36 |
37 | addJob (func) {
38 | this._queue.push(async () => {
39 | try {
40 | const runId = this._runId
41 | const result = func(() => {
42 | // Run not cancelled
43 | if (runId === this._runId) {
44 | this._results.push(result)
45 | this._next()
46 | }
47 | })
48 | } catch (error) {
49 | this._reject(error)
50 | }
51 | })
52 |
53 | if (this.autoStart && this.length === 1) {
54 | this.start()
55 | }
56 | }
57 |
58 | clear () {
59 | this._running = false
60 | this._queue.length = 0
61 | this._resolves.length = 0
62 | this._rejects.length = 0
63 | this._results.length = 0
64 | this._runId++
65 | }
66 |
67 | cancel () {
68 | this._resolve()
69 | this.clear()
70 | }
71 |
72 | start () {
73 | return new Promise((resolve, reject) => {
74 | if (!this._running && this.length > 0) {
75 | this._running = true
76 | this._queue[0]()
77 | this._resolves.push(resolve)
78 | this._rejects.push(reject)
79 | } else {
80 | resolve()
81 | }
82 | })
83 | }
84 |
85 | _next () {
86 | if (this._running && this.length > 0) {
87 | this._queue.shift()
88 |
89 | if (this.length === 0) {
90 | this._resolve()
91 | } else {
92 | this._queue[0]()
93 | }
94 | }
95 | }
96 |
97 | _resolve () {
98 | this._resolves.forEach(f => f(this._results))
99 | this.clear()
100 | }
101 |
102 | _reject (error) {
103 | this._rejects.forEach(f => f(error))
104 | this.clear()
105 | }
106 | }
107 |
--------------------------------------------------------------------------------