foreword
Mainly record some optimizations of the vue project
1. Rendering optimization
v-for
Avoid using v-if and v-for at the same level, v-for has a higher priority than v-if, which will cause data rendering errors
v-for sets the value of the key, try not to use the index, and use the unique identifier in the data, which is conducive to the location and diff of the dom.
Choice of v-show and v-if
Frequently reused components are rendered with v-show (v-show is hidden and not destroyed).
On the contrary, use v-if (directly determine whether to create)
Long List Optimization
Purely for data display, no hot update required
The data in data will be monitored, and the data will change when it changes,
so the object.freeze (data) method is used to freeze the data.
long list
Use virtual scrolling to only render the content of a small area
and only render the data of the viewport, that is to say, the number of rendered DOM nodes is fixed
page:
<template>
<div>
<div>
<span v-if="list.length===0">暂无数据!!!</span>
<div v-else class="box" :style="`height:${viewH}px;overflow-y:scroll;`" @scroll="handleScroll">
<ul>
<li :style="`transform:translateY(${offsetY}px); height:${itemH}px;`" v-for='i in clist' :key="i">{
{ i }}</li>
</ul>
</div>
</div>
<button @click="gotoCSSLongList" class="but">CssLongList</button>
</div>
</template>
logic:
<script>
export default {
name: "LongListOne",
data() {
return {
list: [],//上万条总数据
clist: [],// 页面展示数据
viewH: 500, // 可视box高度
itemH: 60, // 单项高度
scrollH: '', // 整个滚动列表高度
showNum: '',//可视化高度一次能装几个列表
offsetY: 0// 动态偏移量
}
},
mounted() {
for (let index = 0; index < 100000; index++) {
// 10万条数据
this.list.push(index)
}
this.scrollH = this.list.length * this.itemH;// 计算总高度
// 计算可视化高度一次能装几个列表, 多设置几个防止滚动时候直接替换
this.showNum = Math.floor(this.viewH / this.itemH) + 4
// 默认展示几个
this.clist = this.list.slice(0, this.showNum);
this.lastTime = new Date().getTime()
},
computed: {
},
methods: {
// 滚动监视器
handleScroll(e) {
let {
lastTime,itemH,list}=this
if (new Date().getTime() - lastTime > 10) {
let scrollTop = e.target.scrollTop // 滚去的高度
// 每一次滚动后根据scrollTop值获取一个可以整除itemH结果进行偏移
// 例如: 滚动的scrllTop = 1220 1220 % this.itemH = 20 offsetY = 1200
this.offsetY = scrollTop - (scrollTop % itemH)
//上面卷掉了多少,就要往下平移多少,不然showNum滚出可视区外了
this.clist = list.slice(
Math.floor(scrollTop / itemH), // 计算卷入了多少条
Math.floor(scrollTop / itemH) + this.showNum
)
this.lastTime = new Date().getTime()
}
},
// 跳转
gotoCSSLongList(){
this.$router.replace('/CssLongList')
}
}
}
</script>
style:
<style scoped>
* {
padding: 0;
margin: 0;
list-style: none;
box-sizing: border-box;
}
li {
text-align: center;
line-height: 60px;
border-bottom: 1px solid red;
}
.but{
height: 40px;
width: 100px;
margin-top: 10%;
}
</style>
plug-in
vue-virtual-scroller或者vue-virtual-scroll-list
Self-encapsulation
1. The data is given to:
- Elements listen to scroll events (scroll events)
- Calculating the height of the visualization can hold several lists at a time, and then perform slice interception from the total data
- After each scroll, obtain a result that can be divisible by itemH according to the scrollTop value for offset (scrollTop: the distance that the scroll bar moves)
2. Data pass request:
代码实现存在点问题修改后再上传
event destruction
When a Vue component is destroyed, it will automatically unbind all its command events and listeners, but only limited to the component itself.
For example, timers are best destroyed manually during the destruction phase to avoid memory leaks
created(){
this.timer = setInterval(this.refresh,)
}
beforeDestroy(){
clearInterval(this.timer)
}
Stateless components are marked as functional components (static components)
It only receives the value passed by the parent component, does not process it by itself, has no state, and does not create an instance.
Function components do not have this
<template functional> // 函数组件
<div class="cell">
<div v-if="props.value" class="on"></div>
<section v-else class="off"></section>
<div>
</template >
<script>
export default {
props: ['value']
}
</script>
subcomponent split
The time-consuming tasks in the subcomponents are handed over to the components themselves for management, without affecting the loading of the overall page
<template>
<div>
<HelloWorld/>
</div>
</template>
<script>
export default {
// 子组件自己管自己
HelloWorld:{
methods:{
heavy(){
/* 耗时任务 */}
},
render(h) {
return h('div', this.heavy());
}
}
}
variable localization
Reduce the use of this.data to obtain data and reduce unnecessary processing.
Use a variable to obtain data first, and process the data on this variable.
When using this. data, there will be data hijacking, and it will waste time to call get/set for processing.
Modularization and componentization
- Encapsulating highly multiplexable modules
- Split highly reusable components
- Component configurability
<template>
// 可配置
<button
:class="['mybtn',`btn-${btnStyle}`]"
@click="myBtnClick($event)"
v-show="btnShow"
>
<slot></slot>
</button>
</template>
<script>
export default {
name: 'MyBtn',
props: {
btnStyle: String,
btnShow: Boolean
},
setup (props,ctx){
const myBtnClick = (e)=>{
ctx.emit('my-btn-click', e);
}
return{
myBtnClick
}
}
}
</script>
<style lang="scss" scoped>
.mybtn {
border: none;
outline: none;
height:34px;
padding: 0 15px;
background-color: #fff;
border: 1px solid #fff;
}
&.btn-default{
color: #333;
background-color: #fff;
border-color: #ccc;
}
&.btn-primary{
color: #fff;
background-color: #317DEF;
border-color: #317DEF;
}
</style>
Two, lazy loading
Routing lazy loading
const router = new VueRouter({
routes:[
{
path:'.foo', component: () =>import('./Foo.vue')}
]
})
Image lazy loading
use plug-ins
vue-lazyload
<img v-lazy="图片地址">
Self-encapsulation
代码完善后上传
Three, keep-alive cache page
principle
When the created periodic function (after init initialization) is called. Save the VNode node that needs to be cached in this.cache, when calling the render function before rendering the page. If VNodename meets the cache conditions (controlled by include and exclude), the cached VNode instance will be taken from this.cache for rendering.
parameter
- include: character or regular expression. Components with matching names will be rendered.
- exclude: character or regular expression. Components with matching names will not be rendered.
- max: number. The maximum number of instances that can be cached.
Periodic function when wrapped by keep-alive
activated
- Called when the keep-alive component is activated
- Server side rendering is not being invoked
deactivated
- Called when the keep-alive component is deactivated
- Not called during server rendering
Notice
- keep-alive will keep the data in memory. If you want to get the latest data, you need to get it in the activated hook function.
- The name required by include is the name of the component
- When nesting sibling pages in multiple levels, when caching one of the pages, the parent level will be cached, causing all child pages to be cached. In this case, other sibling pages need to be manually destroyed.
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view></keep-alive>
// 销毁不需要的兄弟组件
<router-view v-if="!$route.meta.keepAlive"></router-view>
scenes to be used
If it is necessary to switch between different components, a certain component needs not to change.
For example: after inputting text in the search box, when switching components, the text in the input box will not disappear, nor will it disappear when returning.
Instructions
Using the attributes include and exclude
// 只缓存名字为home的组件
<keep-alive include="home">
<router-view />
</keep-alive>
// 缓存整个组件
<keep-alive>
<router-view />
</keep-alive>
The meta attribute in the route to control the cache
// meta属性中keepAlive值设为true
{
path: '/',
name: 'home',
meta:{
keepAlive:true
},
component: Home
}
// 配合 keep-alive进行控制缓存
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
existing problems
After the component is cached, there is no destruction phase. When the component is switched, life cycle functions such as created will not be called, resulting in data not being updated in time.
Solution:
Use the provided activated and deactivated functions to get whether the current component is active.
Get data and update data in the activated function (cached components will have these two functions)
4. Packaging optimization
1. Import library
Introduce on demand to avoid excessive volume
import {
Button , selet}from 'element-ui';
2. Introduce resources
Plug-in cdn, image cdn, use CSS icons
to reduce project size
3. Packaging configuration
Location: build/config/index.js
Property: productionGzip: true
Function: Packaging and compression
Location: true build/config/index.js
Attribute: productionSourceMap: false
Function: Reduce the packaging volume Locating the source code will generate a map file that may cause source code leakage
5. First screen optimization
Data loading uses the loding skeleton screen
Wiping the skeleton screen during the data loading phase can bring a better user experience
The first screen uses SSR server-side rendering
- Client sends request to server
- The server reads the template, parses it into dom nodes, and returns a complete HTML structure of the first screen
- The client activates the first screen (activate the interactive code written by the user on the front end and turn it into a spa application again)
- In this way, when the user clicks on the hyperlink and jumps again, they will no longer send requests to the server, but use the front-end routing to jump, and only send some ajax request data
use web workers
effect
Let JS implement multithreading
Create a multi-threaded running environment for JS, allowing the main thread to create worker threads and assign tasks to the latter. While the main thread is running, the worker threads are also running without interfering with each other. After the worker threads run, the results are returned to the main thread.
This does not mean that the JS language itself supports multi-threading capabilities, but that the browser as the host environment provides a multi-threaded running environment for JS.
use
Worker() constructor, the first parameter is the URL of the script (must comply with the same-origin policy), this parameter is required, and only JS scripts can be loaded, otherwise an error will be reported. The second parameter is a configuration object, which is optional. One of its functions is to specify the name of the Worker to distinguish multiple Worker threads.
var myWorker = new Worker(jsUrl, options)
scenes to be used
-
Some encryption and decryption algorithms for encrypted data
are more complicated, or when encrypting and decrypting a lot of data, it will consume a lot of computing resources and cause the UI thread to become unresponsive. Therefore, this is a good time to use Web Worker. A seamless operation UI. -
Prefetch data
Sometimes in order to improve the data loading speed, you can use the Worker thread to obtain data in advance, because the Worker thread can use XMLHttpRequest. -
Pre-rendering
In some rendering scenarios, such as rendering a complex canvas, effects such as reflection, refraction, light and shadow, materials, etc. need to be calculated. The logic of these calculations can be executed using Worker threads, or multiple Worker threads can be used. Here There is an example of ray tracing. -
Complex data processing scenarios
Some retrieval, sorting, filtering, and analysis are time-consuming. In this case, Web Worker can be used instead of occupying the main thread. -
Preloading pictures
Sometimes a page has many pictures, or when there are several large pictures, if business constraints do not consider lazy loading, you can also use Web Worker to load pictures. You can refer to the exploration in this article, which is simple here Summarize.
Optimize webpack configuration
- Webpack's code-split combined with vue-router for lazy loading
- webpack's conthash mode caches file level changes
Summarize
The above records the optimization of vue in various aspects, and the optimization of webpackp is perfected after learning webpackp