[关闭]
@Secretmm 2023-02-06T02:03:44.000000Z 字数 6585 阅读 704

框架

p6必备


快速阅读相关链接

https://juejin.cn/post/6844904084374290446
https://juejin.im/post/5d5ffad2518825258a772fa8

前端框架实现数据监听和绑定的方式

发布者-订阅者模式(backbone.js、react(不确定)):

一句话:用类似“setState”的方式显式的发布数据变化,触发视图更改。
简述:存在一个发布数据变化的发布者。数据绑定视图的操作就是 【订阅者】向这个发布者订阅相应的数据变化,当数据需要发生改变时,需要开发者显式调用类似setState这样的方法【vue中用的是数据劫持的方式】发布数据变化,然后在数据被改变的同时,发布者就会向对应的数据订阅者——比如绑定的视图/虚拟DOM——发布数据变化,相应更改视图。
理解版:
【子组件】发布者:1.监听所有数据的数据变化;
【父组件】订阅者:2.向发布者订阅需要的数据变化【数据变化了,发布者就会告诉它】;
联系【当数据发生变化的时候,发布者怎么告诉订阅者呢】:订阅者在订阅数据变化的时候,把数据变化时应当执行的方法传递【这个传递可以存在一个管道】给发布者;开发者显式调用类似setState这样的方法时使发布者监听到数据变化,发布者调用订阅者事先传递的方法【这就叫做告诉订阅者数据变化了】

脏值检查(angular.js):

一句话:通过脏值检测的方式比对数据是否有变更,来决定是否更新视图。
简述:每当可能有变量发生变化需要检查时,比如用户输入、点击、XHR响应、setTimeout到时等,就将所有变量的旧值跟新值进行比较,不相等就说明检测到变化,将变更的数据发送到视图,更新页面展现。

数据劫持(vue.js):

一句话:通过Object.defineProperty()来劫持各个数据属性的settergetter,在数据变动时触发对应视图改变。
简述:通过Object.defineProperty()来劫持需要监听的各个数据属性的settergetter。数据被改变执行相应的setter时,获得数据变化情况,执行相应操作改变对应的视图。

vue2.0原理简述:

双向绑定原理

核心是使用了Object.defineProperty方法,通过观察者模式实现数据响应,执行render,生成vnodevirtual dom的节点】,再将虚拟dom应用到视图中;

Object.defineProperty

Object.defineProperty在对象中定义属性或者修改属性,通过存取描述符getset关键字,提供给属性gettersetter方法

观察者模式

1个数据1个Dep,多个watcher

Observer类: 劫持数据对象,通过defineProperty增加getter/setter方法,然后在getter中收集依赖;在setter通知更新;

收集依赖:在Depsubs里放一个Watcher实例,【subs是一个Watcher实例组成的数组(已有去重)】

通知更新:调用Dep实例的notify方法,【notify方法:把该实例的subs中的所有watcherupdate方法调用一遍】;

Watcher类Watcher实例初始化时通过读取被监听属性的值来调用get方法,触发依赖收集【即把自己放进Depsubs中,见上】;提供update方法【执行响应数据的操作】【update方法里面是:执行里面回调函数(something(eg: compile)在初始化Watcher实例时传进来的回调函数[谁初始化该watcher谁传])】

Dep类:用subs来集中存放某一数据属性的Watcher实例;用notify方法统一调用所有Watcher中的update方法

compile指令解析器: 编译模板【对每个元素节点的指令进行扫描和解析,根据指令模板替换初始数据】;为模板中绑定的数据初始化对应的Watcher实例,并给它提供回调函数;

观察者模式图.png

依赖收集是如何去重的?

vm.render

virtual dom【一个特殊的js对象】

虚拟dom的作用

1、虚拟dom是框架封装dom操作的一种方式;
2、便于声明式的书写界面,进行函数式UI编程;
3、提高组件的抽象能力;
4、虚拟dom+diff可以将多次dom操作合并,在某些场合提高性能;
5、利于在无dom场景下渲染,如ssr、跨平台移植框架

AST

diff算法

https://juejin.im/post/5d5ffad2518825258a772fa8

Proxy 相比于 defineProperty 的优势

defineProperty缺陷:

1.无法监听数组变化
2.只能劫持对象的属性【不能监听对象本身】

proxy简述:

它在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写

优势:
1.可以直接监听对象本身,而非属性;
2.可以直接监听数组的变化;
3.Proxy有多达13种拦截方法,不限于applyownKeysdeletePropertyhas等等是Object.defineProperty不具备的。
4.Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改。
5.Proxy作为新标准将受到浏览器厂商重点持续的性能优化,有性能红利

参考链接
https://juejin.im/post/59f2845e6fb9a0451a759e85
https://juejin.im/post/5acd0c8a6fb9a028da7cdfaf

computed原理

https://juejin.im/post/5afbfce56fb9a07ac0226f21

vue-router原理

vuex原理

vue其它问题

对于MVVM的理解

m:数据层
v:视图层
vm:视图模型层
数据操纵视图,数据视图双向绑定。

vue2.0如何监听数组变化

重写pushpopshiftunshiftsplicesortreverse这七个数组方法

vue自定义指令

钩子函数(方法)

bind:只调用一次,指令第一次绑定到元素时调用;
unbind:只调用一次,指令与元素解绑时调用;
inserted:被绑定元素插入父节点时调用;
update:所在组件的VNode更新时调用;
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用;

钩子函数的参数

el:指令所绑定的元素,可以用来直接操作 DOM。
binding:一个对象,包含以下属性:

name:指令名,不包括 v- 前缀。
value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
oldValue:指令绑定的前一个值,仅在updatecomponentUpdated钩子中可用。无论值是否改变都可用。
expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。

vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

vue生命周期的理解

https://juejin.im/entry/5aee8fbb518825671952308c

组件生命周期

创建前后、挂载前后、更新前后、销毁前后、【动态组件 激活后activated、移除后deactivated

生命周期钩子 组件状态 最佳实践
beforeCreate 实例初始化之后this指向创建的实例,不能访问到datacomputedwatchmethods上的方法和数据】 常用于初始化非响应式变量
created 实例创建完成之后,可访问datacomputedwatchmethods上的方法和数据,未挂载到DOM,不能访问到$el属性,$ref属性内容为空数组 常用于简单的ajax请求,页面的初始化
beforeMount template被找到并已经被编译成render函数之后
mounted $el创建完成之后【还未挂载完成(挂载可能完成了,dom渲染到界面不保证已经完成),所以要用nextTick 常用于获取VNode信息和操作,ajax请求
beforeUpdate 响应式数据已经更新,虚拟DOM还未重新渲染 适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器
updated 虚拟 DOM 重新渲染之后,组件DOM已经更新
beforeDestroy 实例还未销毁,完全可用,this可访问 常用于销毁定时器、解绑全局事件、销毁插件对象等操作
destroyed 解除绑定之后,销毁组件以及事件监听器之后

父子组件生命周期

1.仅当子组件完成挂载后,父组件才会挂载
2.当子组件完成挂载后,父组件会主动执行一次beforeUpdate/updated钩子函数(仅首次)
3.父子组件在data变化中是分别监控的,但是在更新props中的数据是关联的(可实践)
4.销毁父组件时,先将子组件销毁后才会销毁父组件

mixin的生命周期优先执行

vue的路由

路由懒加载

结合 Vue 的异步组件Webpack的代码分割功能。在router项的组件用import函数引入,而不是直接引入。
eg:

  1. //动态地加载模块。调用 import()之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中。
  2. //Router文件中【webpack不用管】
  3. const Foo = () => import(/* webpackChunkName: "other" */ './Foo.vue') //返回一个promise
  4. //router配置项不作修改
  5. const router = new VueRouter({
  6. routes: [
  7. { path: '/foo', component: Foo }
  8. ]
  9. })

实现:hash模式 和 history模式

hash

vue-router 默认 hash 模式; hash发生改变时【即切换路由时】,页面不会自动刷新;

history

前端:1.mode改为history;【开发环境也是直接/,而不是#/】;2.配置一个404页面【不是必须,地址改变,浏览器会向后台请求,会发生刷新,后端针对所有的地址,都返回一个页面文件,不存在的地址需要前端处理】【后端处理404有地址配置,也有文件夹配置】

  1. const router = new VueRouter({
  2. mode: 'history',
  3. routes: [...]
  4. })

后端要做到所有路由地址都响应同一个页面文件,再把这个同一份页面文件返回回来

404页面配置

  1. const router = new VueRouter({
  2. mode: 'history',
  3. routes: [
  4. { path: '*', component: NotFoundComponent }
  5. ]
  6. })

路由守卫

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用离开守卫 beforeRouteLeave
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

beforeRouteUpdate: 举例来说,对于一个带有动态参数的路径/foo/:id,在/foo/1/foo/2之间跳转的时候,由于会渲染同样的Foo组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。可以访问组件实例 this

mixin

合并策略

还可以自定义合并策略哦

  1. Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
  2. // 返回合并后的值
  3. }

1.mixin的钩子函数优先执行【同名钩子函数,合并为一个数组,都会被调用】
2.数据对象(data里的值)在内部会进行递归合并,并在发生冲突时以组件数据优先
3.值为对象的选项,例如 methodscomponents``directives,将被合并为同一个对象。两个对象键名字冲突时,取组件对象的键值对

后两个:因为组件是minxin的子类,数据方法冲突,相当于重写

eg:

  1. //1.钩子函数,mixin优先执行
  2. var mixin = {
  3. created: function () {
  4. console.log('混入对象的钩子被调用')
  5. }
  6. }
  7. new Vue({
  8. mixins: [mixin],
  9. created: function () {
  10. console.log('组件钩子被调用')
  11. }
  12. })
  13. // => "混入对象的钩子被调用"
  14. // => "组件钩子被调用"
  15. //2.数据冲突,组件优先
  16. var mixin = {
  17. data: function () {
  18. return {
  19. message: 'hello',
  20. foo: 'abc'
  21. }
  22. }
  23. }
  24. new Vue({
  25. mixins: [mixin],
  26. data: function () {
  27. return {
  28. message: 'goodbye',
  29. bar: 'def'
  30. }
  31. },
  32. created: function () {
  33. console.log(this.$data)
  34. // => { message: "goodbye", foo: "abc", bar: "def" }
  35. }
  36. })
  37. //3.对象键名冲突,组件优先
  38. var mixin = {
  39. methods: {
  40. foo: function () {
  41. console.log('foo')
  42. },
  43. conflicting: function () {
  44. console.log('from mixin')
  45. }
  46. }
  47. }
  48. var vm = new Vue({
  49. mixins: [mixin],
  50. methods: {
  51. bar: function () {
  52. console.log('bar')
  53. },
  54. conflicting: function () {
  55. console.log('from self')
  56. }
  57. }
  58. })
  59. vm.foo() // => "foo"
  60. vm.bar() // => "bar"
  61. vm.conflicting() // => "from self"

router-view 是必须的嘛?

nextTick的原理

nextTick可以在DOM更新后执行一个回调,问题在于vue如何检测到DOM更新完毕,
html5有一个新属性:MutationObserver,用于监听dom修改事件,能够监听到节点的属性,文字内容、子节点的改动,vue如果检测到浏览器支持MutationObserver,则创建一个文本节点,监听这个文本节点的改动事件,以此来触发nextTickHandler(也就是DOM更新完毕回调)的执行;利用js的事件循环机制,手动修改文本节点属性,确保文本节点更新时,其他dom节点已经更新完毕。 浏览器不支持时,则用settimeout(fn,0)来替代;

message组件构成

vue2 vue3

https://juejin.cn/post/6844904084374290446

react

https://juejin.im/post/6844903922453200904

生命周期

挂载阶段:

constructor: 构造函数:初始化state,给自定义方法绑定this;
getDerivedStateFromProps: 静态方法,从props中获取state;
render:纯函数,返回需要渲染的东西;
componentDidMount:组件挂载之后使用,

更新阶段:

卸载阶段:

reactNode reactElement区别

react 与 vue

单向数据流,双向数据流

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注