Vue 提供了一种称为函数式组件的组件类型,用来定义那些没有响应数据,也不需要有任何生命周期的场景,它只接受一些props 来显示组件。
参数
functional设置为true 即表示该组件为一个函数组件
props(可选)传递值到组件内部,2.3.0版本后可以省略,框架会自动将组件上的特性解析为prop
render函数提供渲染函数来返回一个vnode和正常自定义组件的区别?
- 不维护响应数据
- 无钩子函数
- 没有instance实例,所以在组件内部没有办法像传统组件一样通过this来访问组件属性,组件需要的一切都是通过context传递的,context是一个对象
作用域插槽就是子组件可以给父组件传参,父组件决定怎么展示,作用域插槽给了子组件将数据返给父组件的能力,子组件一样可以复用,同时父组件也可以重新组织内容和样式
常用于多级列表展示,https://segmentfault.com/a/1190000015938629
1.全局的钩子:beforeEach和aftrEach
2.单个路由独享的钩子
3.组件级的钩子:beforeRouteEnter和beforeRouteUpdate
$ nextTick是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用$ nextTick,
原理:
Vue
在更新DOM
时是异步执行的。只要侦听到数据变化,Vue
将开启一个事件队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个
watcher
被多次触发,只会被推入到事件队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM
操作是非常重要的。然后,在下一个的事件循环“tick”中,
Vue
刷新事件队列并执行实际 (已去重的) 工作。当刷新事件队列时,组件会在下一个事件循环“tick”中重新渲染。所以当我们更新完数据后,此时又想基于更新后的
DOM
状态来做点什么,此时我们就需要使用Vue.nextTick(callback)
把基于更新后的
DOM
状态所需要的操作放入回调函数callback
中,这样回调函数将在DOM
更新完成后被调用。http://www.bubuko.com/infodetail-3219154.html
$on
使用:vm.$on('事件名称',callback)
说明:监听当前实例(vm)中的自定义事件,事件可以由$emit定义
$once
使用:vm.$once('事件名称',callback)
说明:监听当前实例(vm)中的自定义事件,事件可以由$emit定义,但是只会触发一次,触发后即解除
$off
使用:vm.$off('事件名称')
说明:关闭当前实例中的自定义事件
$emit
使用:vm.$emit('事件名称',args)
说明:自定义事件
- state:定义了应用程序的数据,可以设置默认的初始状态。
- getters:允许组件从 store 中获取数据 。
- mutations:是唯一更改 store 中状态的方法,且必须是同步函数。但不可以直接调用mutation,必须使用commit函数来告诉Vuex更新存储并提交更改。
- actions:执行异步操作来存取状态,但也不可以直接调用action,必须使用dispatch函数来执行。
通过dispatch来调用actions中的方法。当actions调用commit的方法来触发mutations里面的方法修改数据
存值: this.$store.commit('xx', '')
取值:this.$store.state.xx
Vuex也提供了一些辅助工具,如mapStates,mapGetters,mapMutations,mapActions,从而来减少繁杂的工作量
用法:
...mapGetters(['realname','money_us'])
映射关系
mapState > computed
mapGetters > computed
mapMutations > methods
mapActions > methods
provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
定义说明:这对选项是一起使用的。以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。
1、创建vue实例,Vue();
2、在创建Vue实例的时候,执行了init(),在init过程中首先调用了beforeCreate钩子函数;在beforeCreated阶段:ue实例的挂载元素$el和数据对象data都为undefined,还未初始化
3、同时监听data数据,初始化vue内部事件,进行属性和方法的计算;
4、以上都干完了,调用Created钩子函数;在created阶段,vue实例的数据对象data有了,$el还没有
5、模板编译,把data对象里面的数据和vue语法写的模板编译成HTML。编译过程分三种情况:1)实例内部有template属性,直接调用,然后调用render函数去渲染;2)没有该属性调用外部html;3)都没有抛出错误;
6、编译模板完成,调用beforeMount钩子函数;在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换
7、render函数执行之后,将渲染出来的内容挂载到DOM节点上;
8、挂载结束,调用Mounted钩子函数;vue实例挂载完成,data.message成功渲染。
9、数据发生变化,调用beforeUpdate钩子函数,经历virtual Dom;
10、更新完成,调用Updated钩子函数;
11、beforeDestory销毁所有观察者、组件及事件监听;
12、Destoryed实例销毁;
keep-alive 是Vue内置的一个组件,他也有生命周期,页面第一次进入,钩子的触发顺序 activated,退出时触发 deactivated
vue3生命周期的变化
beforeCreate ---->setup
created ---->setup
beforeMount ---->onBeforeMount
mounted ---->onMounted
beforeUpdate ---->onBeforeUpdate
updated ---->onUpdated
beforeDestory ---->onBeforeUnmount
destoryed ---->onUnmounted
Vue是采用数据劫持结合观察者(发布/订阅)模式的方式,通过
Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,订阅者会触发它的update方法,对视图进行更新。具体步骤:
第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.noticf()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
4、MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
https://blog.csdn.net/Web_J/article/details/114693604?spm=1001.2014.3001.5501
主动类是VUE路由器模块的路由器连接组件中的属性,用来做选中样式的切换
一个组件可以被多个组件使用,那么这个子组件在每个组件中的 data 数据应该是相互独立的,所以使用工厂函数,每次都会生成一个新的对象(不是拷贝)
组建中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。
使用sessionStorage,让vuex中store的状态从sessionStorage取值,并和sessionStorage保持一致
相同点:v-if与v-show都可以动态控制dom元素显示隐藏
不同点:v-if显示隐藏是将dom元素整个添加或删除,而v-show隐藏则是为该元素添加css--display:none,dom元素还在。
原理:Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将
元素作为承载分发内容的出口。其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,让你来传入一些东西。插槽又分为匿名插槽、具名插槽以及作用域插槽。
场景:复用组件
https://blog.csdn.net/Oralinge/article/details/103896320
https://zhuanlan.zhihu.com/p/57570713
props
/$emit
(父组件通过props给子组件传值,子组件$emit给父组件传值)
$emit
/$on(
通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件。 var Event=new Vue(); Event.$emit(事件名,数据); Event.$on(事件名,data => {});)
- vuex
$attrs
/$listeners(
$attrs
与$listeners
是两个对象,$attrs
里存放的是父组件中绑定的非 Props 属性,$listeners
里存放的是父组件中绑定的非原生事件。)
- provide/inject(祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量)
$parent
/$children
与ref(
ref
:在子组件上,引用就向组件实例,$parent
/$children
:访问父 / 子实例)
https://segmentfault.com/a/1190000019208626
一、直接调用$router.push 实现携带参数的跳转
this.$router.push({
path: `/describe/${id}`,
})
二、通过路由属性中的name来确定匹配的路由,通过params来传递参数。
this.$router.push({
name: 'Describe',
params: {
id: id
}})
三、使用path来匹配路由,然后通过query来传递参数
this.$router.push({
path: '/describe',
query: {
id: id
}})
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父beforeUpdate->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
一、代码层面的优化
- v-if 和 v-show 区分使用场景(v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。)
- computed 和 watch 区分使用场景
- v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
- 长列表性能优化(Object.freeze 方法冻结一个对象)
- 事件的销毁
- 图片资源懒加载(vue-lazyload 插件)
- 路由懒加载
- 第三方插件的按需引入( babel-plugin-component)
- 优化无限列表性能(虚拟窗口)
- 服务端渲染 SSR or 预渲染
二、Webpack 层面的优化
- Webpack 对图片进行压缩(image-webpack-loader)
- 减少 ES6 转为 ES5 的冗余代码( babel-plugin-transform-runtime )
- 提取公共代码(CommonsChunkPlugin)
- 模板预编译(vue-template-loader,当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。)
- 提取组件的 CSS
- 优化 SourceMap(解决不好调式代码问题)
- 构建结果输出分析(为了更简单、直观地分析输出结果)
三、基础的 Web 技术优化
- 开启 gzip 压缩
- 浏览器缓存
- CDN 的使用
- 使用 Chrome Performance 查找性能瓶颈
https://www.jianshu.com/p/34366bdc7fee
通过Performance API提供的对象,Performace接口允许访问当前页面性能相关的信息,可以统计到页面白屏时间及各种阶段所占用的时间。https://segmentfault.com/a/1190000014479800
MVVM 是 Model-View-ViewModel的缩写,即将数据模型与数据表现层通过数据驱动进行分离,从而只需要关系数据模型的开发,而不需要考虑页面的表现,具体说来如下:
Model代表数据模型:主要用于定义数据和操作的业务逻辑。
View代表页面展示组件(即dom展现形式):负责将数据模型转化成UI 展现出来。
ViewModel为model和view之间的桥梁:监听模型数据的改变和控制视图行为、处理用户交互。通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
key 值:用于管理可复用的元素。因为 Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染
需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。主要是为了高效的更新虚拟DOM。
1)diff算法的作用:用来修改dom的一小段,不会引起dom树的重绘
2)diff算法的实现原理:diff算法将virtual dom的某个节点数据改变后生成的新的vnode与旧节点进行比较,并替换为新的节点,具体过程就是调用patch方法,比较新旧节点,一边比较一边给真实的dom打补丁进行替换
相同点:
Vue和react的diff算法,都是不进行跨层级比较,只做同级比较。不同点:
1.Vue进行diff时,调用patch打补丁函数,一边比较一边给真实的DOM打补丁
2.Vue对比节点,当节点元素类型相同,但是className不同时,认为是不同类型的元素,删除重新创建,而react则认为是同类型节点,进行修改操作
3.① Vue的列表比对,采用从两端到中间的方式,旧集合和新集合两端各存在两个指针,两两进行比较,如果匹配上了就按照新集合去调整旧集合,每次对比结束后,指针向队列中间移动;
②而react则是从左往右依次对比,利用元素的index和标识lastIndex进行比较,如果满足index < lastIndex就移动元素,删除和添加则各自按照规则调整;
③当一个集合把最后一个节点移动到最前面,react会把前面的节点依次向后移动,而Vue只会把最后一个节点放在最前面,这样的操作来看,Vue的diff性能是高于react的
虚拟DOM,也就是我们常说的虚拟节点,是用JS对象来模拟真实DOM中的节点,该对象包含了真实DOM的结构及其属性,用于对比虚拟DOM和真实DOM的差异,从而进行局部渲染来达到优化性能的目的。
为什么需要虚拟dom?
真实的元素节点:
Hello world!
VNode:
{
tag:'div',
attrs:{
id:'wrap'
},
children:[
{
tag:'p',
text:'Hello world!',
attrs:{
class:'title',
}
}
]
https://blog.csdn.net/Web_J/article/details/108449472
根据业务需求,建立组件的模板,先把架子搭起来,写写样式,考虑好组件的基本逻辑。
准备好组件的数据输入。即分析好逻辑,定好 props 里面的数据、类型。
准备好组件的数据输出。即根据组件逻辑,做好要暴露出来的方法。
使用 v-cloak 指令设置样式,这些样式会在 Vue 实例编译结束时,从绑定的 HTML 元素上被移除。
一般用于解决网页闪屏的问题,在对一个的标签中使用v-cloak,然后在样式中设置[v-cloak]样式,[v-cloak]需写在 link 引入的css中,或者写一个内联css样式,写在import引入的css中不起作用。
v-model本质上不过是语法糖,真正的实现靠的还是,v-bind:绑定响应式数据,v-on做事件的绑定
v-model等同于:
{{username}}
input 输入值后更新data
首先在页面初始化时候,vue的编译器会编译该html模板文件,将页面上的dom元素遍历生成一个虚拟的dom树。再递归遍历虚拟的dom的每一个节点。当匹配到其是一个元素而非纯文本,则继续遍历每一个属性。
如果遍历到v-model这个属性,则会为这个节点添加一个input事件,当监听从页面输入值的时候,来更新vue实例中的data想对应的属性值。// 假如node是遍历到的input节点 node.addEventListener("input",function(e){ vm.name=e.target.value; })
data的属性赋值后更新input的值
同样初始化vue实例时候,会递归遍历data的每一个属性,并且通过defineProperty来监听每一个属性的get,set方法,从而一旦某个属性重新赋值,则能监听到变化来操作相应的页面控制。
Object.defineProperty(data,"name",{ get(){ return data["name"]; }, set(newVal){ let val=data["name"]; if (val===newVal){ return; } data["name"]=newVal; // 监听到了属性值的变化,假如node是其对应的input节点 node.value=newVal; } })
总结
其核心就是,一方面modal层通过defineProperty来劫持每个属性,一旦监听到变化通过相关的页面元素更新。另一方面通过编译模板文件,为控件的v-model绑定input事件,从而页面输入能实时更新相关data属性值。
computed
1)根据传入的变量的变化 进行结果的更新。
2)计算属性基于响应式依赖进行缓存。如其中的任意一个值未发生变化,它调用的就是上一次计算缓存的数据,因此提高了程序的性能。而methods中每调用一次就会重新计算一次,为了进行不必要的资源消耗,选择用计算属性。watch
1)计算属性的时候 初始化的时候就可以被监听到并且计算 但是watch是发生改变的时候才会触发。
2)当有一些数据需要随着其它数据变动而变动时,或者当需要在数据变化时执行异步或开销较大的操作时,使用 watch。如果我们想在创建时监听value,要使用 handler 和 immediate
watch: { value:{ handler:function(o,n){}, immediate: true } }
总结:
1)计算属性变量在computed中定义,属性监听在data中定义。
2)计算属性是声明式的描述一个值依赖了其他值,依赖的值改变后重新计算结果更新DOM。属性监听的是定义的变量,当定义的值发生变化时,执行相对应的函数。使用场景:
computed:当模板中的某个值需要通过一个或多个数据计算得到时,就可以使用计算属性,还有计算属性的函数不接受参数;
watch:监听属性主要是监听某个值发生变化后,对新值去进行逻辑处理。
delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。Vue.delete 直接删除了数组 改变了数组的键值。
使用location.href='/url'来跳转,简单方便,但是刷新了页面;
使用路由方式跳转,无刷新页面,静态跳转;
自定义指令分为全局指令和组件指令,其中全局指令需要使用directive来进行定义,组件指令需要使用directives来进行定义,具体定义方法同过滤器filter或者其他生命周期,具体使用方法如下:
全局自定义指令 directive(name,{}),其中name表示定义的指令名称(定义指令的时候不需要带v-,但是在调用的时候需要带v-),第二个参数是一个对象,对象中包括五个自定义组件的钩子函数,具体包括:
bind函数:只调用一次,指令第一次绑定在元素上调用,即初始化调用一次,
inserted函数:并绑定元素插入父级元素(即new vue中el绑定的元素)时调用(此时父级元素不一定转化为了dom)
update函数:在元素发生更新时就会调用,可以通过比较新旧的值来进行逻辑处理
componentUpdated函数:元素更新完成后触发一次
1. vue2和vue3双向数据绑定原理发生了改变
vue2 是利用Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。
vue3 中使用ProxyAPI 对数据代理。
相比于vue2.x,使用proxy的优势如下
- defineProperty只能监听某个属性,不能对全对象监听,因为是通过遍历data属性,利用Object.definePrototype将其转化成setter/getter,但是由于现代js的限制以及object.observe的限制,vue无法检测到对象属性的添加或删除
- 可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
- 可以监听数组,不用再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化
2. 默认进行懒观察(lazy observation)。
在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压力。
3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。
3. 更精准的变更通知。
比例来说:2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行;3.x 版本中,只有依赖那个属性的 watcher 才会重新运行。
4.大幅提升运行时的性能:重写虚拟dom,效果提升30%~300%,跳过静态节点,只处理动态节点。而静态节点渲染一次就不管了,所以处理的数据量会有一个巨大的下降,从而提升巨大性能。
5.提升网络性能:tree-shaking机制,shaking:通过代码反向监测那些特性被用到,因此会决定打包的时候会打包那些
6.完全typescript支持
7.Fragment:模板更简单。不需要最外层div节点
8.Suspense:强大的异步组件。
9.composition-api:逻辑重用
10.vue2和vue3组件编写方式改变
https://blog.csdn.net/qq_38110572/article/details/107597900
区别 :
- Proxy使用上比Object.defineProperty方便的多。
- Proxy代理整个对象,Object.defineProperty只代理对象上的某个属性。(Vue.set(object, key, value),解决视图不更新问题)
- 如果对象内部要全部递归代理,则Proxy可以只在调用时递归,而Object.defineProperty需要在一开始就全部递归,Proxy性能优于Object.defineProperty。
- 对象上定义新属性时,Proxy可以监听到,Object.defineProperty监听不到。
- 数组新增删除修改时,Proxy可以监听到,Object.defineProperty监听不到。
- Proxy不兼容IE,Object.defineProperty不兼容IE8及以下。
用法:
- defineProperty用法 Object.defineProperty(obj, prop, descriptor)-----------------》obj:必需。目标对象 prop:必需。需定义或修改的属性的名字 descriptor:必需。目标属性所拥有的特性
- proxy用法 var proxy = new Proxy(target, handler);----------------》target——要对其基本操作进行自定义的对象。handler——要自定义操作方法.
Proxy 用于修改某些操作的默认行为,可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
- get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
- set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
- apply方法拦截函数的调用、call和apply操作。接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
- has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。接受两个参数,分别是目标对象、需查询的属性名。
- deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。
Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而
Object.defineProperty
只能遍历对象属性直接修改。
1)路由懒加载
2)vue-cli开启打包压缩 和后台配合 gzip访问
3)进行cdn加速
4)开启vue服务渲染模式
5)用webpack的externals属性把不需要打包的库文件分离出去,减少打包后文件的大小
6)在生产环境中删除掉不必要的console.log7)开启nginx的gzip ,在nginx.conf配置文件中配置
8)添加loading效果,给用户一种进度感受
所有的
prop
都使得其父子prop
之间形成了一个单向下行绑定:父级prop
的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
介绍
keep-alive是Vue的一个内置组件。可以使被包含的组件保留状态,或避免重新渲染 (它能够将不活动的组件实例保存在内存中,而不是直接将其销毁)。
它是一个抽象组件,不会被渲染到真实DOM中,也不会出现在父组件链中。它提供了include与exclude两个属性,允许组件有条件地进行缓存。
页面第一次进入,钩子的触发顺序:created-> mounted-> activated,退出时触发 deactivated
当再次进入(前进或者后退)时,只触发activated事件挂载的方法等。只执行一次的放在 mounted 中;组件每次进去执行的方法放在 activated 中
属性
1)include - 字符串或正则表达式,只有名称匹配的组件会被缓存
2)exclude - 字符串或正则表达式,任何名称匹配的组件都不会被缓存
3)include 和 exclude 的属性允许组件有条件地缓存。二者都可以用“,”分隔字符串、正则表达式、数组。当使用正则或者是数组时,要记得使用v-bind 。原理
其实就是在created时将需要缓存的VNode节点保存在this.cache中。在render时,如果VNode的name符合在缓存条件(可以用include以及exclude控制),则会从this.cache中取出之前缓存的VNode实例进行渲染。
因为keep-alive会将组件保存在内存中,并不会销毁以及重新创建,所以不会重新调用组件的created等方法,需要用activated与deactivated这两个生命钩子来得知当前组件是否处于活动状态。
https://segmentfault.com/a/1190000011978825
vue异步组件
component: resolve => require(['../components/PromiseDemo'], resolve)
es提案的import()
const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
webpack的require.ensure()
component: resolve => require.ensure([], () => resolve(require('../components/PromiseDemo')), 'demo')
.stop 阻止事件继续传播
.prevent 阻止标签默认行为
.capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
.self 只当在 event.target 是当前元素自身时触发处理函数
.once 事件将只会触发一次
.passive 告诉浏览器你不想阻止事件的默认行为
reactive 用于为对象添加响应式状态。接收一个js对象作为参数,返回一个具有响应式状态的副本
ref 用于为数据添加响应式状态。获取数据值的时候需要加.value,参数可以传递任意数据类型,vue 3.0 setup里定义数据时推荐优先使用ref。ref 和 reactive 本质我们可以简单地理解为ref是对reactive的二次包装
toRef 用于为源响应式对象上的属性新建一个ref,从而保持对其源对象属性的响应式连接。接收两个参数:源响应式对象和属性名,返回一个ref数据。例如使用父组件传递的props数据时,要引用props的某个属性且要保持响应式连接时就很有用。
toRefs 用于将响应式对象转换为结果对象,其中结果对象的每个属性都是指向原始对象相应属性的ref。用法和 toRef 类似,只不过 toRef 是一个个手动赋值,而 toRefs 是自动赋值。。
第一步:先获取原生
Array
的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化。第二步:对
Array
的原型方法使用Object.defineProperty
做一些拦截操作。第三步:把需要被拦截的
Array
类型的数据原型指向改造后原型。
const arrayProto = Array.prototype // 获取Array的原型
function def (obj, key) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
value: function(...args) {
console.log(key); // 控制台输出 push
console.log(args); // 控制台输出 [Array(2), 7, "hello!"]
// 获取原生的方法
let original = arrayProto[key];
// 将开发者的参数传给原生的方法,保证数组按照开发者的想法被改变
const result = original.apply(this, args);
// do something 比如通知Vue视图进行更新
console.log('我的数据被改变了,视图该更新啦');
this.text = 'hello Vue';
return result;
}
});
}
用法:数组 this.$set(Array, index, newValue) / 对象 this.$set(Object, key, value)
先进行一个判断,判断target不是undefined、null、string、number、symbol、boolean类型的数据。
1.如果target是数组,那么根据key值及数组长度更改数组的长度(取其中较大者),然后直接使用splice函数修改数组,虽然vue没有监听数组变化,但是监听了数组的
push,pop,shift,unshift,splice,sort,reverse
函数,所以使用splice也可以达到更新dom的目的2.如果target是一个对象,且key是对象已存在的私有属性,那么直接赋值就可以了,因为这个key必然是被监听过的
3.如果这个key目前没有存在于对象中,那么会进行赋值并监听。这里省略了ob的判断,那么ob是什么呢,vue中初始化的数据(比如data中的数据)在页面初始化的时候都会被监听,而被监听的属性都会被绑定__ob__属性,这里就是判断这个数据有没有被监听的。如果这个数据没有被监听,那么就默认你不想监听这个数据,所以直接赋值并返回
$router用于跳转,属性有push、go、replace
route对象表示当前的路由信息,包含了当前URL解析得到的信息。包含当前的路径、参数、query对象等。
computed-render -> normal-watcher ( watch 中定义 ) -> render-watcher(更新组件的视图)
这样安排是有原因的,这样就能尽可能的保证,在更新组件视图的时候,computed 属性已经是最新值了,如果 render-watcher 排在 computed-render 前面,就会导致页面更新的时候 computed 值为旧数据
vue 组件初次渲染过程
- 解析模板为 render 函数(把 vue 语法编译 成 js 语法,通过执行 vue-template-compiler 的 compiler 函数,得到 render)
- 触发响应式,监听 data 属性的 getter 和 setter(响应式关键 Object.defineProperty(),将模版初次渲染使用到的变量绑定到 Object.defineProperty() 中,首次渲染会 触发 getter )
- 执行 render 函数, 生成 vnode,patch(elem,vnode)(第一点说到已经得到render函数,现在要执行render函数, 将 vue 语法转成 h 函数的结果,也就是 vNode,后续进行一系列操作)
vue 组件更新过程
- 修改 data, 触发 setter (此前在getter中已被监听,调用Dep.notify(),将通知它内部的所有的Watcher对象进行视图更新)
- 重新执行 render 函数,生成 newVnode
- patch(vnode, newVnode)