Vue生命周期:
https://www.jianshu.com/p/a921cd6aeaa7?from=timeline
创建期:before create, created, before mount, mounted
运行期:beforUpdate, updated
销毁期:beforeDestroy, destroyed
![](https://i-blog.csdnimg.cn/blog_migrate/6b76895cb96caf40a82cec2889fa078a.jpeg)
keep-alive
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
解析: 比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染
destroy:
组件销毁时使用(如v-if),beforeDestroy 中操作,常用来销毁一些监听事件及定时函数。貌似刷新不会执行destroy。
父子组件执行生命周期:
父组件先创建,然后子组件创建;子组件先挂载,然后父组件挂载,即“父beforeCreate-> 父created -> 子beforeCreate-> 子created -> 子mounted -> 父mounted”
**注意:**在父组件传递接口的数据给子组件时,最好在子组件标签上加上v-if=“传递的接口数据”
在父组件调用接口传递数据给子组件时,接口响应显然是异步的。当子组件的mounted都执行完之后,此时可能父组件的请求才返回数据。会导致,从父组件传递给子组件的数据是undefined。
解决方法1:
在渲染子组件的时候加上一个条件,data1是父组件调用接口返回的数据。当有数据的时候在去渲染子组件。
<div class="test">
<children v-if="data1" :data="data1" ></children>
</div>
解决方法2:
在子组件中 watch 监听,父组件获取到值,这个值就会变化,自然是可以监听到的
props:['data1'],
watch:{
data1:{
deep:true,
handler:function(newVal,oldVal) {
this.$nextTick(() => {
this.data1 = newVal
this.showData1(this.data1)
})
}
},
}
vue和jQuery区别
jquery:轻量级的js库,jQuery是使用选择器(
)
选
取
D
O
M
对
象
,
对
其
进
行
赋
值
、
取
值
、
事
件
绑
定
等
操
作
,
其
实
和
原
生
的
H
T
M
L
的
区
别
只
在
于
可
以
更
方
便
的
选
取
和
操
作
D
O
M
对
象
,
而
数
据
和
界
面
是
在
一
起
的
。
比
如
需
要
获
取
l
a
b
e
l
标
签
的
内
容
:
)选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容:
)选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容:(“lable”).val();,它还是依赖DOM元素的值。
vue:前端js框架,它专注于MVVM模型的viewModel层,通过Vue对象把数据和View完全分离开来,对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的。通过数据绑定把view和model层连接起来,通过对数据的操作就可以完成对页面视图的渲染。
扩展:
- vue适用的场景:复杂数据操作的后台页面,表单填写页面
- jquery适用的场景:比如说一些html5的动画页面,一些需要js来操作页面样式的页面
二者也是可以结合起来一起使用的,vue侧重数据绑定,jquery侧重样式操作,动画效果等,则会更加高效率的完成业务需求
vue单页面和传统的多页面区别?
单页面应用(SPA)
通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
多页面(MPA)
指一个应用中有多个页面,页面跳转时是整页刷新
单页面的优点:
用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离;页面效果会比较炫酷(比如切换页面内容时的专场动画)。
单页面缺点:
不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理);初次加载时耗时多;页面复杂度提高很多。
衍生如何解决单页及多页面各自优点的问题:服务端渲染
一、Vue使用
1. 基本使用,组件使用——常用,必须会
1.1 指令、插值
插值表达式
指令、动态属性
x-html :会有XSS风险,会覆盖子组件
表单
v-model
常见表单项textarea checkbox radio select
修饰符lazy number trim
组件使用
组件间通讯
父子,兄弟
props和$emit
组件间通讯-自定义事件
组件生命周期 (important)
挂载阶段 before create, created, before mount, mounted
更新阶段 beforUpdate, updated
销毁阶段 beforeDestroy, destroyed
created 和 mounted 什么区别?
created 之后 检查 el 和 template,created 只是把Vue的实例初始化了,只是存在于js内存中的一个变量,并没有开始渲染。
mounted 是真正在网页上渲染完成了,大部分在mounted里进行操作 。
beforeDestroy 做哪些事?
解除绑定、销毁子组件以及事件监听器
带有父子组件的生命周期:
谁先被mounted,数据被修改后父组件先update还是子组件先updated,销毁顺序
创建和渲染:
index created -> list created -> list mounted -> index mounted。 创建是从外到内,渲染是从内到外。
update:
index before update -> list before update -> list updated -> index updated
data 为什么返回函数
组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
vue文件被vue-loader处理,template被编译成render函数。通常在使用vue的时候是在vue文件中export出一个对象,这个时候如果将data定义为一个对象。使用的时候通过import导入,经过babel编译,将导出的对象直接替换成了一个对象变量,然后将这个变量传入到对应的组件构造函数中。
2. 高级特性——不常用 ,但体现深度
自定义v-model
栗子:颜色选择器
$nextTick
Vue是异步渲染,data改变之后, DOM不会立刻渲染,$nextTick
会在DOM渲染之后被触发,以获取最新DOM节点。
1.异步渲染,$nextTick 待DOM渲染完再回调
3.页面渲染时会将data 的修改做整合,多次data 修改只会渲染一次(同步渲染就会改一次渲染一次)
汇总data的修改, 一次性更新视图
目的是减少DOM操作次数,提高性能
实现思路:
第一,同步修改 data 时,把修改操作汇总到一个队列中
第二,再异步时取得这个队列,将其中的修改数据汇总,就像 Object.assign
第三,用汇总的结果统一修改 data ,触发试图更新
补充:vue中可用$ref.name 来获取到dom
slot
父组件向子组件传递 标签中的内容,在使用时候会用子元素或子节点替换slot标签。
匿名插槽
具名插槽
作用域插槽:
父组件复用子组件的slot(及其中数据),又可以使slot内容不一致。父组件不仅要向子组件中传递插槽,还要使用子组件定义的数据。
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
slot-scope
子组件 Link.vue:
<template>
<a :href="url">
<slot :slotData="website"> 把子组件变量暴露到插槽作用域。slotData名称可以自定义
{{website.subTitle}}
</slot>
</a>
</template>
父组件调用:
<ScopedSlotDemo :url="website.url">
<template v-slot="slotProps">
{{slotProps.slotData.title}}
</template>
</ScopedSlotDemo>
动态、异步组件
动态组件:
:is= "component-name” 用法
需要根据数据,动态渲染的场景。即组件类型不确定。
异步组件:
有些组件体积非常大,代码编辑器,echarts这些用到
什么时候用什么时候加载
() => import(’…/…’)
keep-alive
缓存组件,频繁切换,不需要重复渲染。用包裹。通常用于tab切换。
mixin
多个组件有相同的逻辑,抽离出来。mixin并不是完美的解决方案,会有一些问题。Vue3提出的Composition API旨在解决这些问题
mixins: [myMixin]
问题:
变量来源不明确,不利于阅读
多mixin可能会造成命名冲突
mixin和组件可能出现多对多的关系,复杂度较高
vuex
基本概念:state, getters, action, mutation
用于vue组件:dispatch, commit, mapState, mapGetters, mapActions, mapMutations
scoped
实现组件的私有化,不对全局造成样式污染,表示当前style属性只属于当前模块。
实现原理:
- 在DOM结构加上data-v 的自定义属性,利用css样式属性选择器,保证唯一。
- 如果组件内部还有组件,只会给最外层的组件里的标签加上唯一属性字段,不影响组件内部引用的组件( 注意 )
问题:
- 父组件无scoped属性,子组件带有scoped,父组件是无法操作子组件的样式的
- 父组件有scoped属性,子组件无,父组件也无法设置子组件样式
- 父子组件都有,同理也无法设置样式,更改起来增加代码量
二、Vue原理
v-show和v-if的区别
为何v-for中要用key
描述Vue组件生命周期(有父子组件的情况)
Vue组件如何通讯
描述组件渲染和更新的过程
双向数据绑定v-model的实现原理
1. 组件化基础
如何理解MVVM模型
"很久以前”就有组件化:
asp jsp php已经有组件化了,nodejs中也有类似的组件化(后端)
但是,传统组件,只是静态渲染,更新还要依赖于操作DOM
数据驱动视图- Vue MVVM
数据驱动视图- React setState (暂时按下不表) , 通过数据操作(重新渲染)视图。
更加关注数据(业务逻辑)
数据驱动视图( MVVM , setState )
2. 响应式
组件data的数据一旦变化,立刻触发视图的更新。
实现数据驱动视图的第一步,考察Vue原理的第一题。
核心API - Object.defineProperty
如何实现响应式,代码演示
Object.defineProperty的一些缺点( Vue3.0启用Proxy,Proxy有兼容性问题 )
核心机制是 观察者模式。数据是被观察的一方,发生改变时,通知所有的观察者,这样观察者可以做出响应,比如,重新渲染然后更新视图。通过 Object.defineProperty() 替换配置对象属性的 set、get 方法,实现“拦截”;watcher 在执行 getter 函数时触发数据的 get 方法,从而建立依赖关系;更改数据时触发 set 方法,从而借助 dep 发布通知,进而 watcher 进行更新。
2.1 Object.defineProperty基本用法:
2.2 监听对象,监听数组
见代码
2.3 复杂对象,深度监听
// 触发更新视图
function updateView() {
console.log('视图更新')
}
// 重新定义数组原型
const oldArrayProperty = Array.prototype
// 创建新对象,原型(__proto__)指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
arrProto[methodName] = function () {
updateView() // 触发视图更新
oldArrayProperty[methodName].call(this, ...arguments)
// Array.prototype.push.call(this, ...arguments)
}
})
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
// 深度监听,判断是否是对象,不是的直接返回
observer(value)
// 核心 API
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
// 深度监听,如果被设置的新值也是对象
observer(newValue)
// 设置新值
// 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
value = newValue
// 触发更新视图
updateView()
}
}
})
}
// 监听对象属性
function observer(target) {
if (typeof target !== 'object' || target === null) {
// 不是对象或数组
return target
}
// 污染全局的 Array 原型
// Array.prototype.push = function () {
// updateView()
// ...
// }
if (Array.isArray(target)) {
// 当值为数组,让它的原型指向 新对象,这个对象重写了Array中的原型方法,即添加了视图渲染,再更新值
target.__proto__ = arrProto
}
// 重新定义各个属性(for in 也可以遍历数组)
for (let key in target) {
defineReactive(target, key, target[key])
}
}
// 准备数据
const data = {
name: 'zhangsan',
age: 20,
info: {
address: '北京' // 需要深度监听
},
nums: [10, 20, 30]
}
// 监听数据
observer(data)
// 测试
// data.name = 'lisi'
// data.age = 21
// // console.log('age', data.age)
// data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
// delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
// data.info.address = '上海' // 深度监听
data.nums.push(4) // 监听数组
2.5 简易demo
<!DOCTYPE html>
<html lang="en">
<head>
<title>双向绑定最最最初级demo</title>
<meta charset="UTF-8">
</head>
<body>
<div id="app">
<input type="text" id="txt">
<p id="show-txt"></p>
</div>
</body>
<script>
var obj={}
Object.defineProperty(obj,'txt',{
get:function(){
return obj
},
set:function(newValue){
document.getElementById('txt').value = newValue
document.getElementById('show-txt').innerHTML = newValue
}
})
document.addEventListener('keyup',function(e){
obj.txt = e.target.value
})
</script>
</html>
2.4 几个缺点
深度监听时,需要递归到底, 一次性计算量大
无法监听新增属性/删除属性( 需要用Vue.set Vue.delete )
无法原生监听数组,需要特殊处理
3. 虚拟DOM(virtual dom)和diff
3.1 vdom
vdom是实现vue和React的重要基石
diff算法是vdom中最核心、最关键的部分
以前用jQuery ,可以自行控制DOM操作的时机,手动调整
Vue和React是数据驱动视图,如何有效控制DOM操作? - vdom
有了一定复杂度,想减少计算次数比较难
能不能把计算,更多的转移为JS计算?因为JS执行速度很快
vdom-用JS模拟DOM结构,计算出最小的变更, 操作DOM
下图最好能记住(面试题:用vnode 模拟 html 片段)
DOM操作非常耗费性能,JS执行速度比DOM更新速度快很多,vdom就是尽量用JS模拟DOM结构,进行对比计算后找出最需要更新的 dom 去更新。这个js对象通常包含tag,props,children这些结构。对比的过程就是diff算法
vdom 优化 dom 操作:尽量将所有计算都放在 js 中,进行对比计算后找出最需要更新的 dom 去更新。
snabbdom: h函数(传入标签,属性,子元素,生成vnode),vnode数据结构,patch 函数(可以做首次渲染,即将虚拟节点渲染到真正的节点上,或者DOM更新)。(vue参考它实现的)
在patch过程中会遍历整个树结构,并且挨个执行patchVnode函数,传入两个参数,分别是旧的vNode和新的vNode,如果旧节点有子节点但新节点没有,那么直接删除旧节点的子节点;如果旧节点没有子节点但新节点有,那么直接添加子节点;如果两个都有子节点,就会执行updateChildren函数。
updateChildren函数会分别给两个子节点列表设置两个头尾指针,用于标记对比。头和头,尾和尾,头和尾,尾和头,根据tag和key是否相等判断是否是相同节点,命中了就移动指针,继续执行pathNode;如果都未命中,就根据新的子节点列表中的头指针指向的key遍历旧子节点列表看是否有key一样的,如果都没对应上,就直接插入,对应上了就继续执行pathNode,移动指针。
h函数参数:(标签名,属性(类,id等),子元素)
3.2 diff算法
传统树的diff算法时间复杂度是O(n^3)
时间复杂度优化到O(n):
只比较同一层级,不跨级比较
tag不相同,则直接删掉重建,不再深度比较
tag和key , 两者都相同,则认为是相同节点,不再深度比较
4. computed 和 watch 的区别
computed:
计算结果并返回,只有当被计算的属性发生改变时才会触发(即:计算属性的结果会被缓存,除非依赖的相应属性变化才会重新计算)
watch如何深度监听? (handle , deep:true)
watch监听引用类型,拿不到oldVal
监听某一个值,当被监听的值发生变化时,执行相关操作。
(其实是两个不同功能)
场景:购物车
computed: {
sum: function(){
let res = 0;
for(let i = 0; i < this.arr.length; i++){
res += this.arr[i].num*this.arr[i].price;
}
return res;
}
}
watch: {
arr: {
handler: functin(v, oldVal){
let res = 0;
for(let i = 0; i < this.arr.length; i++){
res += this.arr[i].num*this.arr[i].price;
}
this.sum = res;
}
immediate: true,
deep: true
}
}
- 方法:页面数据每次重新渲染都会重新执行。性能消耗大。
- computed: 是计算属性,依赖其他属性计算值,并且computed的值有缓存,只有当计算值变化时才会返回内容,否则返回缓存值。页面里有个Input,输入时候并没有改数组值,触发重新渲染,但还是会重新计算。有点类似于React里的SCU,通过比较缓存值是否改变来判断子组件是否需要重新渲染。
- watch:只能监听具体的值(或对象),监听到值的变化就会执行回调(监听的是数组),支持异步操作,可以监听computed里属性。
5. v-if v-show区别
v-show和v-if都是用来显示隐藏元素,v-if还有一个v-else配合使用,两者达到的效果都一样,但是v-if更消耗性能的,因为v-if在显示隐藏过程中有DOM的添加和删除,v-show就简单多了,只是操作css。
v-show
v-show不管条件是真还是假,第一次渲染的时候都会编译出来,也就是标签都会添加到DOM中。之后切换的时候,通过display: none;样式来显示隐藏元素。可以说只是改变css的样式,几乎不会影响什么性能。
v-if
在首次渲染的时候,如果条件为假,什么也不操作,页面当作没有这些元素。当条件为真的时候,开始局部编译,动态的向DOM元素里面添加元素。当条件从真变为假的时候,开始局部编译,卸载这些元素,也就是删除。
更新不频繁用v-if,频繁更新用v-show。
5. v-key
key的重要性。key 不能乱写(如random或者index)
vue中列表循环需加:key=“唯一标识” 唯一标识且最好是静态的,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
涉及到 diff 算法
https://zhuanlan.zhihu.com/p/124019708
4. 模板编译
5. 渲染过程
6. 前端路由
origin: 协议 域名 端口号
知道基本配置
路由间通讯:
1、路由参数,通过 $route.params. 获取
2、路由规则中 props 设置为true, 通过props获取
3、将 props 设置为对象
4、如果想要获取传递的参数值还想要获取传递的对象数据,那么props应该设置为函数形式
路由模式( hash、H5 history )
hash模式(默认) ,如http://abc.com/#/user/10
H5 history模式,如http://abc.com/user/20
后者需要server端支持,因此无特殊需求可选择前者
vue-router 中hash模式和history模式区别:
在vue的路由配置中有mode选项,最直观的区别就是在url中hash 带了一个很丑的 # ,而history是没有#的。vue默认使用hash。
mode:“hash”;
mode:“history”;
hash—— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。
比如这个 URL:http://www.aaa.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。通过window.onhashchange 实现。
history—— 利用了 HTML5 History Interface 中新增的 history.pushState() 和 window.onpopstate()方法。(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
因此可以说,hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由。
history模式的问题
- 通过history api,我们丢掉了丑陋的#,但是它也有个问题:不怕前进,不怕后退,就怕刷新,f5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的。
- 在hash模式下,前端路由修改的是#中的信息,而浏览器请求时不会将 # 后面的数据发送到后台,所以没有问题。但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,则会刷新出来404页面。
js 实现 hash
hash 变化会触发网页跳转,即浏览器的前进、后退。hash变化不会刷新页面,也永远不会提交到server端。
window.onhashchange
window.location.hash = newHash
popstate
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>hash test</title>
</head>
<body>
<p>hash test</p>
<button id="btn1">修改 hash</button>
<script>
// hash 变化,包括:
// a. JS 修改 url
// b. 手动修改 url 的 hash
// c. 浏览器前进、后退
window.onhashchange = (event) => {
console.log('old url', event.oldURL)
console.log('new url', event.newURL)
console.log('hash:', location.hash)
}
// 页面初次加载,获取 hash
document.addEventListener('DOMContentLoaded', () => {
console.log('hash:', location.hash)
})
// JS 修改 url
document.getElementById('btn1').addEventListener('click', () => {
location.href = '#/user'
})
</script>
</body>
</html>
js 实现history
window.history.pushState、 window.history.replaceState 和 window.history.go
需要后端支持
history.pushState
window.onpopstate
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>history API test</title>
</head>
<body>
<p>history API test</p>
<button id="btn1">修改 url</button>
<script>
// 页面初次加载,获取 path
document.addEventListener('DOMContentLoaded', () => {
console.log('load', location.pathname)
})
// 打开一个新的路由
// 【注意】用 pushState 方式,浏览器不会刷新页面
document.getElementById('btn1').addEventListener('click', () => {
const state = { name: 'page1' }
console.log('切换路由到', 'page1')
history.pushState(state, '', 'page1') // 重要!!
})
// 监听浏览器前进、后退
window.onpopstate = (event) => { // 重要!!
console.log('onpopstate', event.state, location.pathname)
}
// 需要 server 端配合,可参考
// https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%90%8E%E7%AB%AF%E9%85%8D%E7%BD%AE%E4%BE%8B%E5%AD%90
</script>
</body>
</html>
代码如下(示例):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
7. vue组件渲染
模板编译
1.前置知识:JS的with语法
使用with能改变{}块内自由变量的查找方式
将{}内自由变量,当做obj的属性来查找,如果找不到匹配的属性会报错
with(obj){
log(a);
}
2.vue template complier将模板编译为render函数
html模板转成js代码
_c createElement
_v createTextVNode
3.执行render函数生成vnode
初次渲染:
- 解析模板为render函数的形式(一般在编译或者打包时候已完成,vue-loader),返回的是vnode。
_c createElement
参数:(标签名,属性(类,id等),子元素) - 触发响应式,监听data属性getter setter(如果模板没有用到则不会触发)
- 执行render函数,生成vnode,基于vnode执行patch(elem, vnode)和diff
更新:
- 修改data , 触发setter (此前在getter中已被监听)
- 重新执行render函数,生成newVnode
- patch(vnode, newVnode)。期间利用diff算法更新,减少消耗
异步渲染
实现思路:
第一,同步修改 data 时,把修改操作汇总到一个队列中
第二,再异步时取得这个队列,将其中的修改数据汇总,就像 Object.assign
第三,用汇总的结果统一修改 data ,触发试图更新
可以用render函数代替template
vue性能优化
合理使用v-show 和 v-if
合理使用computed
v-for 时 加 key,以及避免和v-if同时使用
自定义事件、DOM事件及时销毁
合理使用异步组件(import())
合理使用keep-alive。(缓存组件,频繁切换,不需要重复渲染。)
data层级不要太深
使用vue-loader在开发环境做模板编译(预编译)
比如后端返回1000条数据,如果需要一次性渲染到页面上,就可以用异步渲染来做。通过一个异步操作,比如settimeout或者requestAnimationFrame把dom操作推倒异步队列,进行分批渲染,提高页面性能,减少卡顿。https://coding.imooc.com/learn/questiondetail/GDyQ0Y9JlE7XJARr.html
组件封装的思路
一段代码在项目中出现两次就开始考虑是否应该进行封装,出现三次就肯定要封装,大到一个页面,一个组件,小到一个function和一个css样式。
封装出来的组件必须具有高性能,低耦合的特性,主要从以下几点入手:
组件具有单一职责
数据从父组件传入
子组件本身不应该生成数据,如果需要生成数据,只能在组件内部进行使用,不要传递出去。
对于通过props传入的参数,不建议对其进行操作,因为会同时修改父组件里面的数据(如果修改的话,控制台中也会报错的),如果需要修改数据,可以传递给父组件,让父组件去修改(在父组件中处理事件)。
记得留一个slot
一个通用的组件,往往不能完美的适应所有的应用场景,所以在封装组件的时候,只需要完成组件的80%的功能,剩下的20%让父组件通过slot解决。
不要依赖vuex
如果要抽离组件尽量不要使用vuex来实现参数的传递,因为vuex是用来管理组件状态的,虽然可以用来传参,但是不推荐,可以选择放到localstorage中,或者通过props传递等方式。