Kami memiliki wiki di TeamHood. Ada sekumpulan rekomendasi, termasuk bagaimana meningkatkan kinerja frontend yang berat di vue.js. Itu perlu untuk meningkatkan kinerja, karena karena spesifikasinya, layar utama kami tidak memiliki penomoran halaman. Ada klien yang memiliki lebih dari seribu kartu ini di satu papan kanban / gantt, semua ini akan berfungsi tanpa jeda.
wiki, .
. vue2 , vue3. vue3 production-ready. vuex4 , ( , vue2+vuex3). javascript, vue-class-component, typescript, typed-vuex , .
1. (Deep) Object Watchers
- deep , watch . . items , vuex store, , item . isChecked , item, getter, :
export const state = () => ({
items: [{ id: 1, name: 'First' }, { id: 2, name: 'Second' }],
checkedItemIds: [1, 2]
})
export const getters = {
extendedItems (state) {
return state.items.map(item => ({
...item,
isChecked: state.checkedItemIds.includes(item.id)
}))
}
}
, items , . - :
export default class ItemList extends Vue {
computed: {
extendedItems () { return this.$store.getters.extendedItems },
itemIds () { return this.extendedItems.map(item => item.id) }
},
watch: {
itemIds () {
console.log('Saving new items order...', this.itemIds)
}
}
}
item . - , . checkedItemIds extendedItems ( ), itemIds. -, , . , javascript, [1,2,3] != [1,2,3]. : example1.
- watcher . watcher computed . , {id, title, userId}
items, :
computed: {
itemsTrigger () {
return JSON.stringify(items.map(item => ({
id: item.id,
title: item.title,
userId: item.userId
})))
}
},
watch: {
itemsTrigger () {
// JSON.parse - this.items;
}
}
, watcher, , .
watcher - , deep watcher - . deep - . , , - , - deep - .
- .. ( ).. , , , $emit('reinit'), $nextTick. .
2. Object.freeze
Object.freeze TeamHood 2 . , StarBright, nuxt . Nuxt , . vuex store ( ). , vuex. this.$store.dispatch('fetch', β¦), vuex .
, vuex . , , autocomplete , store . , vue (). , .
//
state: () => ({
items: []
}),
mutations: {
setItems (state, items) {
state.items = items
},
markItemCompleted (state, itemId) {
const item = state.items.find(item => item.id === itemId)
if (item) {
item.completed = true
}
}
}
//
state: () => ({
items: []
}),
mutations: {
setItems (state, items) {
state.items = items.map(item => Object.freeze(item))
},
markItemCompleted (state, itemId) {
const itemIndex = state.items.find(item => item.id === itemId)
if (itemIndex !== -1) {
// item.completed = true ( ), ;
const newItem = {
...state.items[itemIndex],
completed: true
}
state.items.splice(itemIndex, 1, Object.freeze(newItem))
}
}
}
: example2. , build- ( development).
3.
// Vuex:
getters: {
itemById: (state) => (itemId) => state.items.find(item => item.id === itemId)
}
...
// Some <Item :item-id="itemId" /> component:
computed: {
item () { return this.$store.getters.itemById(this.itemId) }
}
itemsByIds :
getters: {
itemByIds: (state) => state.items.reduce((out, item) => {
out[item.id] = item
return out
}, {})
}
// Some <Item :item-id="itemId" /> component:
computed: {
item () { return this.$store.getters.itemsByIds[this.itemId] }
}
: example3.
4.
- vue. (shouldComponentUpdate) . - : - , .
, , - , , , . () :
// Store:
export const getters = {
extendedItems (state) {
return state.items.map(item => ({
...item,
isChecked: state.checkedItemIds.includes(item.id)
}))
},
extendedItemsByIds (state, getters) {
return getters.extendedItems.reduce((out, extendedItem) => {
out[extendedItem.id] = extendedItem
return out
}, {})
}
}
// App.vue:
<ItemById for="id in $store.state.ids" :key="id" :item-id="id />
// Item.vue:
<template>
<div>{{ item.title }}</div>
</template>
<script>
export default {
props: ['itemId'],
computed: {
item () { return this.$store.getters.extendedItemsByIds[this.itemId] }
},
updated () {
console.count('Item updated')
}
}
</script>
: example4p1. item <Item>. , <Item> extendedItemsByIds, item.
vue - , virtual DOM (memoization). - - dry run props $store. - , .
store . normalizr , . ids. , getter, . , :
// Store:
export const state = () => ({
ids: [],
itemsByIds: {},
checkedIds: []
})
export const getters = {
extendedItems (state, getters) {
return state.ids.map(id => ({
id,
item: state.itemsByIds[id],
isChecked: state.checkedIds.includes(id)
}))
}
}
export const mutations = {
renameItem (state, { id, title }) {
const item = state.itemsByIds[id]
if (item) {
state.itemsByIds[id] = Object.freeze({
...item,
title
})
}
},
setCheckedItemById (state, { id, isChecked }) {
const index = state.checkedIds.indexOf(id)
if (isChecked && index === -1) {
state.checkedIds.push(id)
} else if (!isChecked && index !== -1) {
state.checkedIds.splice(index, 1)
}
}
}
// Item.vue:
computed: {
item () {
return this.$store.state.itemsByIds[this.itemId]
},
isChecked () {
return this.$store.state.checkedIds.includes(this.itemId)
}
}
, renameItem state.itemsByIds, . rename : example4p2. isChecked state.checkedIds ( ), - <Item>.
, <Item> :
<Item
v-for="extendedItem in extendedItems"
:key="extendedItem.id"
:item="extendedItem.item"
:is-checked="extendedItem.isChecked"
/>
: example4p3.
5. IntersectionObserver
DOM- . . , gantt , , viewport. . , intersection observer. vuetify v-intersect , , IntersectionObserver , , .
, : example5. 100 ( 10), , . IntersectionObserver , ., - IntersectionObserver:
export default {
inserted (el, { value: observer }) {
if (observer instanceof IntersectionObserver) {
observer.observe(el)
}
el._intersectionObserver = observer
},
update (el, { value: newObserver }) {
const oldObserver = el._intersectionObserver
const isOldObserver = oldObserver instanceof IntersectionObserver
const isNewObserver = newObserver instanceof IntersectionObserver
if (!isOldObserver && !isNewObserver) || (isOldObserver && (oldObserver === newObserver)) {
return false
}
if (isOldObserver) {
oldObserver.unobserve(el)
el._intersectionObserver = undefined
}
if (isNewObserver) {
newObserver.observe(el)
el._intersectionObserver = newObserver
}
},
unbind (el) {
if (el._intersectionObserver instanceof IntersectionObserver) {
el._intersectionObserver.unobserve(el)
}
el._intersectionObserver = undefined
}
}
, , , . , , - vue , . , . - . , css:
<template>
<div
v-for="i in 100"
:key="i"
v-node-intersect="intersectionObserver"
class="rr-intersectionable"
>
<Heavy />
</div>
</template>
<script>
export default {
data () {
return {
intersectionObserver: new IntersectionObserver(this.handleIntersections)
}
},
methods: {
handleIntersections (entries) {
entries.forEach((entry) => {
const className = 'rr-intersectionable--invisible'
if (entry.isIntersecting) {
entry.target.classList.remove(className)
} else {
entry.target.classList.add(className)
}
})
}
}
}
</script>
<style>
.rr-intersectionable--invisible .rr-heavy-part
display: none
</style>