为什么用框架
操作不规范,导致大量的重绘和重排,复杂的读取dom对象(js的计算要比dom便宜),代码可维护性差等。
比如说:
- 数组增加100条元素,每一条元素都产生一次重新排列。而不是增加完100条再更新到页面上。
- 页面数组这100条元素发生改变的时候,重新创建所有 DOM 元素。必要的 DOM 更新。
虽然框架做的事情,都可以靠双手解决。
但是谁可以保证都能记得这样写?
其实使用框架,就是在性能和可维护性,可阅读性做一个取舍。
框架就起规范代码,脱离底层操作,用声明式的方式编写,让代码更容易看懂。
用什么框架
前端三大框架
angular,react,vue
- angular:脏查询。ng自定义了很多函数,比如说
ng-click
, 一旦触发这些函数,就会把所有监听的值都进行一次对比,修改改变的值,然后再重新对比一次,没有更新就渲染到页面上去。(大颗粒度)
如果使用了ng之外的函数,比如jQuery等,ng是检测不到触发的。得手动触发,$scope... $watch $digest $apply
- react:虚拟dom + diff, 改进过的diff,只对比同层级的元素,一旦发生改变,把原来位置的直接删除,替换成为新的元素。通过虚拟dom刷新到页面上。(setData() 时候对比)
- vue:观察者模式。js的特性
Object.defineproperty
里面的get/set
用来监听数据的变化。get的时候加入观察者队列,set更新数据的时候发布者分发给队列里面的每一个。(细颗粒度)
初始化的时候给每一个属性都加上数据监听。
对比:
- 首次渲染: Virtual Dom > 脏查询 >= 依赖收集 ()
- 少量数据: 依赖收集 >> Virtual Dom 优化 > 脏查询 (全部检查) > Virtual Dom 不优化
- 大量数据更新:脏检查 + 优化 >= 依赖收集 + 优化 > Virtual DOM(无法/无需优化,必须得对比)
如何使用
模板渲染
事件处理
计算属性
...
Cal Attrs
<div>
{{ helloComputed }} <!-- 时间戳3 -->
</div>
<div>
{{ helloMethods() }} <!-- 时间戳 x -->
</div>
<div>
{{ ms }} <!-- 时间戳4 -->
</div>
...
props: {
message: String
},
data () {
return {
msg: 'hello',
ms: ''
}
},
computed: {
helloComputed: function() {
console.log('computed')
return this.msg + 'computed' + Date.now() + this.message
}
},
mounted: function () {
console.log(this.helloComputed) // 时间戳 1
console.log(this.helloMethods()) // 时间戳 2
this.msg = 'HELLO'
console.log(this.helloComputed) // 依赖改变,重新计算时间, 3
this.ms = this.helloMethods() // 只在渲染时候计算, 时间戳 4
},
methods: {
helloMethods: function () {
console.log('methods')
return this.msg + 'methods' + Date.now() + this.message
}
}
-
methods: 略。
methods
和computed
区别。例子的ms
,是由父组件传过来计算得到的值,虽然说methods的方法能够监听到改变,但是某些复杂的情况下。比如对象,子组件也就是当前的组件需要对object做处理,而且!不能改变父组件传过来的对象(浅拷贝和深拷贝)
computed: (!)
- 执行异步操作不能串行返回结果,使用watch。
- 开销较大的操作,避免堵塞主线程,使用watch。
- 简单且串行返回的,使用computed
- watch: 对某一个data监听
列表渲染
js的原因,无法监听特殊对象,数组。Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。类似angular的脏检测。
-
:key
唯一标识
类似react 的diff 算法,track by $index。
条件渲染
- v-if 直接销毁
- v-show css切换hidden
组件
什么是组件?
封装可重用代码。
如果说函数是JS的一等公民,那么在VUE里面,组件就是一等公民。万物皆组件。
(在react里面,函数即是组件)
万法归一,在VUE也是同理。
既然这样,在写一个组件之前。我们得分清楚业务组件,公共组件。然后进行职责划分,其实就是单一原则
就算业务组件可复用性没有这么高,也是应当做一些抽象,页面不要长了...
举个🌰:一任务是某个页面内添加一张卡片。一个竖着的带圆角的按钮类似样式。上面是个人信息,下面是头衔信息。数据请求的同一个接口。
就可以将这个卡片从页面抽离出来,单独做成一个卡片了。
更进一步,将上下信息分开。卡片组件引入上下部分。
生命周期: 在做某些事情之前做一些处理。可以用中间件思想看- -。
- beforeCreate 此时$el、data 的值都为undefined
- created 此时可以拿到data的值,但是$el依旧为undefined(还没有渲染)
- beforeMount $el 创建到虚拟dom里面。
-
Mounted : 做数据处理。如果需要完全渲染完毕再处理,则增加
this.$nextTick(() => { ... })
- beforeUpdate:
- Updated:
...
组件间通信
父组件 => 子组件 , 父组件设置
v-bind:key="value"
, 子组件接收props: {key}
。 或者父组件this.$child
子组件 => 父组件 , 子组件设置
vue.$emit(key, value)
,父组件接收v-on:key=" fn "
。或者子组件this.$parent
同级兄弟组件通信:BUS解释思想
slot插槽
栗子:cell
:title
:value
非业务代码
编写可以重用的组件。尽量解耦合,参数尽量简单。
一个组件只实现一个功能(单一原则)
纯函数编写思想实现组件。
一样的输入得到一样的输出
why?
方便测试。
每当需要修改的时候,我都可以得到明确的输出,那么我就可以在外面套一个组件。中间做一些处理。还不需要修改原组件(开闭原则,中间件)
参数尽量简单
🌰:组件接收(target, key, value)这样的参数结构
当进行多次操作的的时候
外面再加一层组件,接收(target, obj, value = 'default')这样的参数,用for...in之类的处理
异步组件
- mixin
混合起来,被继承的属性组件,混合部分内容达到继承效果?
使用恰当时,可以为自定义对象注入处理逻辑。怎么举例子 - extend
- directives : hook