题目分析
- 和是带来的两个独特的概念。新手程序员容易混淆,所以面试官喜欢问。
- 我们只需记住修改状态只能是,只能通过提交修改状态即可
回答范例
- 更改 的 中的状态的唯一方法是提交 , 非常类似于事件:每个 都有一个字符串的类型 ()和一个 回调函数 () 。 类似于 ,不同在于:可以包含任意异步操作,但它不能修改状态, 需要提交才能变更状态
- 开发时,包含异步操作或者复杂业务组合时使用;需要直接修改状态则提交。但由于和是两个,容易引起混淆,实践中也会采用统一使用的方式。调用和两个时几乎完全一样,但是定义两者时却不甚相同,的回调函数接收参数是对象。则是与实例具有相同方法和属性的上下文对象,因此一般会解构它为,从而方便编码。另外会返回实例便于处理内部异步结果
- 实现上方法相当于调用;方法相当于调用,这样就很容易理解两者使用上的不同了
实现
我们可以像下面这样简单实现和,从而辨别两者不同
使用mapGetters辅助函数, 利用对象展开运算符将getter混入computed 对象中
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。 Vue 组件间通信只要指以下 3 类通信 :、、,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信
组件传参的各种方式
组件通信常用方式有以下几种
- 适用 父子组件通信
- 父组件向子组件传递数据是通过 传递的,子组件传递数据给父组件是通过 触发事件来做到的
- 与 适用 父子组件通信
- :如果在普通的 元素上使用,引用指向的就是 元素;如果用在子组件上,引用就指向组件实例
- :访问访问父组件的属性或方法 / 访问子组件的属性或方法
- 适用于 父子、隔代、兄弟组件通信
- 这种方法通过一个空的 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件
- 适用于 隔代组件通信
- :包含了父作用域中不被 所识别 (且获取) 的特性绑定 ( 和 除外 )。当一个组件没有声明任何 时,这里会包含所有父作用域的绑定 ( 和 除外 ),并且可以通过 传入内部组件。通常配合 选项一起使用
- :包含了父作用域中的 (不含 修饰器的) 事件监听器。它可以通过 传入内部组件
- 适用于 隔代组件通信
- 祖先组件中通过 来提供变量,然后在子孙组件中通过 来注入变量。 API 主要解决了跨级组件间的通信问题, 不过它的使用场景,主要是子组件获取上级组件的状态 ,跨级组件间建立了一种主动提供与依赖注入的关系
- 适用于 隔代组件通信 访问根组件中的属性或方法,是根组件,不是父组件。只对根组件有用
- 适用于 父子、隔代、兄弟组件通信
- 是一个专为 应用程序开发的状态管理模式。每一个 应用的核心就是 (仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( )
- 的状态存储是响应式的。当 组件从 中读取状态的时候,若 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 改变 中的状态的唯一途径就是显式地提交 () 。这样使得我们可以方便地跟踪每一个状态的变化。
根据组件之间关系讨论组件通信最为清晰有效
- 父子组件:///
- 兄弟组件://
- 跨层级关系:////
下面演示组件之间通讯三种情况: 父传子、子传父、兄弟组件之间的通讯
1. 父子组件通信
使用,父组件可以使用向子组件传递数据。
父组件模板:
子组件模板:
回调函数(callBack)
父传子:将父组件里定义的作为传入子组件
子组件向父组件通信
父组件向子组件传递事件方法,子组件通过触发事件,回调给父组件
父组件模板:
子组件模板:
2. provide / inject 跨级访问祖先组件的数据
父组件通过使用提供需要传递的数据
子组件通过使用接收父组件传递的参数
3. $parent + $children 获取父组件实例和子组件实例的集合
- 可以直接访问该组件的父实例或组件
- 父组件也可以通过 访问它所有的子组件;需要注意 并不保证顺序,也不是响应式的
4. $attrs + $listeners多级组件通信
包含了从父组件传过来的所有属性
包含了父组件监听的所有事件
5. ref 父子组件通信
6. 非父子, 兄弟组件之间通信
中废弃了广播和分发事件的方法。父子组件中可以用和。如何实现非父子组件间的通信,可以通过实例一个实例作为媒介,要相互通信的兄弟组件之中,都引入,然后通过分别调用Bus事件触发和监听来实现通信和参数传递。可以是这样:
另一个组件也在钩子函数中监听事件
7. $root 访问根组件中的属性或方法
- 作用:访问根组件中的属性或方法
- 注意:是根组件,不是父组件。只对根组件有用
8. vuex
- 适用场景: 复杂关系的组件数据传递
- Vuex作用相当于一个用来存储共享变量的容器
- 用来存放共享变量的地方
- ,可以增加一个派生状态,(相当于中的计算属性),用来获得共享变量的值
- 用来存放修改的方法。
- 也是用来存放修改state的方法,不过是在的基础上进行。常用来做一些异步操作
小结
- 父子关系的组件数据传递选择 与 进行传递,也可选择
- 兄弟关系的组件数据传递可选择,其次可以选择进行传递
- 祖先与后代组件数据传递可选择与或者 与
- 复杂关系的组件数据传递可以通过存放共享的变量
由于在浏览器中操作 是很昂贵的。频繁的操作 ,会产生一定的性能问题。这就是虚拟 的产生原因。 的 借鉴了开源库 的实现。 本质就是用一个原生的 对象去描述一个 节点,是对真实 的一层抽象
优点:
- 保证性能下限 : 框架的虚拟 需要适配任何上层 可能产生的操作,它的一些 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 操作性能要好很多,因此框架的虚拟 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
- 无需手动操作 DOM : 我们不再需要手动去操作 ,只需要写好 的代码逻辑,框架会根据虚拟 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
- 跨平台 : 虚拟 本质上是 对象,而 与平台强相关,相比之下虚拟 可以进行更方便地跨平台操作,例如服务器渲染、 开发等等。
缺点:
- 无法进行极致优化:虽然虚拟 + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 无法进行针对性的极致优化。
- 首次渲染大量时,由于多了一层虚拟 的计算,会比 插入慢。
虚拟 DOM 实现原理?
虚拟 的实现原理主要包括以下 部分:
- 用 对象模拟真实 树,对真实 进行抽象;
- 算法 — 比较两棵虚拟 树的差异;
- 算法 — 将两个虚拟 对象的差异应用到真正的 树。
说说你对虚拟 DOM 的理解?回答范例
思路
- 是什么
- 引入的好处
- 如何生成,又如何成为
- 在后续的中的作用
回答范例
- 虚拟顾名思义就是虚拟的对象,它本身就是一个 对象,只不过它是通过不同的属性去描述一个视图结构
- 通过引入我们可以获得如下好处:
- 将真实元素节点抽象成 ,有效减少直接操作 次数,从而提高程序性能
- 直接操作 是有限制的,比如:、 等操作,一个真实元素上有许多的内容,如果直接对其进行 操作,会去额外 一些没有必要的内容;同样的,如果需要进行 那么需要将其全部内容进行复制,这也是没必要的。但是,如果将这些操作转移到 对象上,那么就会变得简单了
- 操作 是比较昂贵的操作,频繁的操作容易引起页面的重绘和回流,但是通过抽象 进行中间处理,可以有效减少直接操作的次数,从而减少页面重绘和回流
- 方便实现跨平台
- 同一 节点可以渲染成不同平台上的对应的内容,比如:渲染在浏览器是 元素节点,渲染在 变为对应的控件、可以实现 、渲染到 中等等
- 中允许开发者基于 实现自定义渲染器(),以便于针对不同平台进行渲染
- 如何生成?在vue中我们常常会为组件编写模板 - , 这个模板会被编译器 - 编译为渲染函数,在接下来的挂载()过程中会调用函数,返回的对象就是虚拟。但它们还不是真正的,所以会在后续的过程中进一步转化为。
- 挂载过程结束后,程序进入更新流程。如果某些响应式数据发生变化,将会引起组件重新,此时就会生成新的,和上一次的渲染结果就能得到变化的地方,从而转换为最小量的操作,高效更新视图
为什么要用vdom?案例解析
现在有一个场景,实现以下需求:
将该数据展示成一个表格,并且随便修改一个信息,表格也跟着修改。 用jQuery实现如下:
- 这样点击按钮,会有相应的视图变化,但是你审查以下元素,每次改动之后,标签都得重新创建,也就是说下面的每一个栏目,不管是数据是否和原来一样,都得重新渲染,这并不是理想中的情况,当其中的一栏数据和原来一样,我们希望这一栏不要重新渲染,因为重绘相当消耗浏览器性能。
- 因此我们采用JS对象模拟的方法,将的比对操作放在层,减少浏览器不必要的重绘,提高效率。
- 当然有人说虚拟DOM并不比真实的快,其实也是有道理的。当上述中的每一条数据都改变时,显然真实的操作更快,因为虚拟还存在中算法的比对过程。所以,上述性能优势仅仅适用于大量数据的渲染并且改变的数据只是一小部分的情况。
如下结构:
映射成虚拟就是这样:
使用snabbdom实现vdom
这是一个简易的实现功能的库,相比、,对于这块更加简易,适合我们学习。里面有两个核心的,一个是函数,一个是函数,前者用来生成对象,后者的功能在于做虚拟的比对和将挂载到真实上
简单介绍一下这两个函数的用法:
现在我们就来用重写一下刚才的例子:
你会发现, 只有改变的栏目才闪烁,也就是进行重绘 ,数据没有改变的栏目还是保持原样,这样就大大节省了浏览器重新渲染的开销
vue中使用生成虚拟返回
</details>
缓存组件使用组件,这是一个非常常见且有用的优化手段,中有比较大的更新,能说的点比较多
思路
- 缓存用,它的作用与用法
- 使用细节,例如缓存指定/排除、结合和
- 组件缓存后更新可以利用或者
- 原理阐述
回答范例
- 开发中缓存组件使用组件,是内置组件,包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,这样在组件切换过程中将状态保留在内存中,防止重复渲染
- 结合属性和可以明确指定缓存哪些组件或排除缓存指定组件。中结合时变化较大,之前是包裹,现在需要反过来用包裹
- 缓存后如果要获取数据,解决方案可以有以下两种
- :在有项目,每次进入路由的时候,都会执行
- :在缓存的组件被激活的时候,都会执行钩子
- 是一个通用组件,它内部定义了一个,缓存创建过的组件实例,它返回的渲染函数内部会查找内嵌的组件对应组件的,如果该组件在中存在就直接返回它。由于的属性是个响应式数据,因此只要它变化,的函数就会重新执行
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 组件,对于所有 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果
问题: 组件复用导致路由参数失效怎么办?
解决方法:
- 通过 监听路由参数再发请求
- 用 来阻止“复用”
回答范例
- 很多时候,我们需要将给定匹配模式的路由映射到同一个组件,这种情况就需要定义动态路由
- 例如,我们可能有一个 组件,它应该对所有用户进行渲染,但用户 不同。在 中,我们可以在路径中使用一个动态字段来实现,例如:,其中就是路径参数
- 路径参数 用冒号 表示。当一个路由被匹配时,它的 的值将在每个组件中以 的形式暴露出来。
- 参数还可以有多个,例如/;除了 之外, 对象还公开了其他有用的信息,如 、 等
参考 前端进阶面试题详细解答
本质上是为每个监听属性 创建了一个 ,当被监听的属性更新时,调用传入的回调函数。常见的配置选项有 和 ,对应原理如下
- :深度监听对象,为对象的每一个属性创建一个 ,从而确保对象的每一个属性更新时都会触发传入的回调函数。主要原因在于对象属于引用类型,单个属性的更新并不会触发对象 ,因此引入 能够很好地解决监听对象的问题。同时也会引入判断机制,确保在多个属性更新时回调函数仅触发一次,避免性能浪费。
- :在初始化时直接调用回调函数,可以通过在 阶段手动调用回调函数实现相同的效果
组件通信的方式如下:
父组件通过向子组件传递数据,子组件通过和父组件通信
1. 父组件向子组件传值
- 只能是父组件向子组件进行传值,使得父子组件之间形成了一个单向下行绑定。子组件的数据会随着父组件不断更新。
- 可以显示定义一个或一个以上的数据,对于接收的数据,可以是各种数据类型,同样也可以传递一个函数。
- 属性名规则:若在中使用驼峰形式,模板中需要使用短横线的形式
2. 子组件向父组件传值
- 绑定一个自定义事件,当这个事件被执行的时就会将参数传递给父组件,而父组件通过监听并接收参数。
事件总线适用于父子组件、非父子组件等之间的通信,使用步骤如下: (1)创建事件中心管理组件之间的通信
(2)发送事件 假设有两个兄弟组件和:
在组件中发送事件:
(3)接收事件 在组件中发送事件:
在上述代码中,这就相当于将值存贮在了事件总线中,在其他组件中可以直接访问。事件总线就相当于一个桥梁,不用组件通过它来通信。
虽然看起来比较简单,但是这种方法也有不变之处,如果项目过大,使用这种方式进行通信,后期维护起来会很困难。
这种方式就是Vue中的依赖注入,该方法用于父子组件之间的通信。当然这里所说的父子不一定是真正的父子,也可以是祖孙组件,在层数很深的情况下,可以使用这种方法来进行传值。就不用一层一层的传递了。
是Vue提供的两个钩子,和、是同级的。并且的书写形式和一样。
- 钩子用来发送数据或方法
- 钩子用来接收数据或方法
在父组件中:
在子组件中:
还可以这样写,这样写就可以访问父组件中的所有属性:
注意: 依赖注入所提供的属性是非响应式的。
这种方式也是实现父子组件之间的通信。
: 这个属性用在子组件上,它的引用就指向了子组件的实例。可以通过实例来访问组件的数据和方法。
在子组件中:
在父组件中:
- 使用可以让组件访问父组件的实例(访问的是上一级父组件的属性和方法)
- 使用可以让组件访问子组件的实例,但是,并不能保证顺序,并且访问的数据也不是响应式的。
在子组件中:
在父组件中:
在上面的代码中,子组件获取到了父组件的值,父组件改变了子组件中的值。 需要注意:
- 通过访问到的是上一级父组件的实例,可以使用来访问根组件的实例
- 在组件中使用拿到的是所有的子组件的实例,它是一个数组,并且是无序的
- 在根组件上拿得到的是的实例,在这实例上再拿得到的是,而在最底层的子组件拿是个空数组
- 的值是数组,而是个对象
考虑一种场景,如果A是B组件的父组件,B是C组件的父组件。如果想要组件A给组件C传递数据,这种隔代的数据,该使用哪种方式呢?
如果是用来一级一级的传递,确实可以完成,但是比较复杂;如果使用事件总线,在多人开发或者项目较大的时候,维护起来很麻烦;如果使用Vuex,的确也可以,但是如果仅仅是传递数据,那可能就有点浪费了。
针对上述情况,Vue引入了,实现组件之间的跨代通信。
先来看一下,它的默认值true,继承所有的父组件属性除之外的所有属性; 只继承class属性 。
- :继承所有的父组件属性(除了prop传递的属性、class 和 style ),一般用在子组件的子元素上
- :该属性是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合 将所有的事件监听器指向这个组件的某个特定的子元素。(相当于子组件继承父组件的事件)
A组件():
B组件():
C 组件 ():
在上述代码中:
- C组件中能直接触发test的原因在于 B组件调用C组件时 使用 v-on 绑定了 属性
- 在B组件中通过v-bind 绑定属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的)
(1)父子组件间通信
- 子组件通过 props 属性来接受父组件的数据,然后父组件在子组件上注册监听事件,子组件通过 emit 触发事件来向父组件发送数据。
- 通过 ref 属性给子组件设置一个名字。父组件通过 组件名来获得子组件,子组件通过 获得父组件,这样也可以实现通信。
- 使用 provide/inject,在父组件中通过 provide提供变量,在子组件中通过 inject 来将变量注入到组件中。不论子组件有多深,只要调用了 inject 那么就可以注入 provide中的数据。
(2)兄弟组件间通信
- 使用 eventBus 的方法,它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通信的组件引入这个实例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递。
- 通过 来获取到兄弟组件,也可以进行通信。
(3)任意组件之间
- 使用 eventBus ,其实就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。
如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候采用上面这一些方法可能不利于项目的维护。这个时候可以使用 vuex ,vuex 的思想就是将这一些公共的数据抽离出来,将它作为一个全局的变量来管理,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
可以标识组件的具体名称方便调试和查找对应属性
Vue 3.0 正走在发布的路上,Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:
(1)监测机制的改变
3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:
- 只能监测属性,不能监测对象
- 检测属性的添加和删除;
- 检测数组索引和长度的变更;
- 支持 Map、Set、WeakMap 和 WeakSet。
新的 observer 还提供了以下特性:
- 用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。
- 默认采用惰性观察。在 2.x 中,不管反应式数据有多大,都会在启动时被观察到。如果你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。
- 更精确的变更通知。在 2.x 中,通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收到变更通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。
- 不可变的 observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。
- 更好的调试功能:我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在什么时候以及为什么重新渲染。
(2)模板
模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。
(3)对象式的组件声明方式
vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。
此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。
(4)其它方面的更改
vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:
- 支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
- 支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
- 基于 treeshaking 优化,提供了更多的内置功能。
v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 “display” 属性进行切换。
所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。
插槽
- 创建组件虚拟节点时,会将组件儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿子进行分类
- 渲染组件时会拿对应的 属性的节点进行替换操作。(插槽的作用域为父组件)
作用域插槽
- 作用域插槽在解析的时候不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。(插槽的作用域为子组件)
- 普通插槽渲染的作用域是父组件,作用域插槽的渲染作用域是当前子组件。
Model–View–ViewModel (MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样也是 WPF 和 Silverlight 的架构师)于2005年在他的博客上发表
MVVM 源自于经典的 Model–View–Controller(MVC)模式 ,MVVM 的出现促进了前端开发与后端业务逻辑的分离,极大地提高了前端开发效率,MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用
(1)View 层
View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建 。
(2)Model 层
Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的 api 接口。
(3)ViewModel 层
ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。
MVVM 框架实现了双向绑定,这样 ViewModel 的内容会实时展现在 View 层,前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新。这样 View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。
我们以下通过一个 Vue 实例来说明 MVVM 的具体实现,有 Vue 开发经验的同学应该一目了然:
(1)View 层
(2)ViewModel 层
(3) Model 层
我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
以 input 表单元素为例:
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
- 实践中不应该把和放一起
- 在中,的优先级是高于,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费;另外需要注意的是在中则完全相反,的优先级高于,所以执行时,它调用的变量还不存在,就会导致异常
- 通常有两种情况下导致我们这样做:
- 为了过滤列表中的项目 (比如 )。此时定义一个计算属性 (比如 ),让其返回过滤后的列表即可(比如)
- 为了避免渲染本应该被隐藏的列表 (比如 )。此时把 移动至容器元素上 (比如 、)或者外面包一层即可
- 文档中明确指出永远不要把 和 同时用在同一个元素上,显然这是一个重要的注意事项
- 源码里面关于代码生成的部分,能够清晰的看到是先处理还是,顺序上和正好相反,因此产生了一些症状的不同,但是不管怎样都是不能把它们写在一起的
vue2.x源码分析
在vue模板编译的时候,会将指令系统转化成可执行的函数
编写一个标签,同时使用与
创建实例,存放与数据
模板指令的代码都会生成在函数中,通过就能得到渲染函数
- 是的列表渲染函数,函数内部都会进行一次判断
- 初步得到结论:优先级是比f高
- 再将与置于不同标签
再输出下函数
这时候我们可以看到,与作用在不同标签时候,是先进行判断,再进行列表的渲染
我们再在查看下vue源码
源码位置:
在进行判断的时候,是比先进行判断
最终结论:优先级比高
优点:
- 保证性能下限: 虚拟DOM可以经过diff找出最小差异,然后批量进行patch,这种操作虽然比不上手动优化,但是比起粗暴的DOM操作性能要好很多,因此虚拟DOM可以保证性能下限
- 无需手动操作DOM: 虚拟DOM的diff和patch都是在一次更新中自动进行的,我们无需手动操作DOM,极大提高开发效率
- 跨平台: 虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等
缺点:
- 无法进行极致优化: 在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化,比如VScode采用直接手动操作DOM的方式进行极端的性能优化
Vue 是组件级更新,如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能, Vue 会在本轮数据更新后,在异步更新视图。核心思想
源码相关
通知 进行更新, 依次调用 的 , 将 去重放入队列, ( )在下一中刷新队列(异步)
- 用过,项目规模变大之后,单独一个对象会过于庞大臃肿,通过模块方式可以拆分开来便于维护
- 可以按之前规则单独编写子模块代码,然后在主文件中通过选项组织起来:
- 不过使用时要注意访问子模块状态时需要加上注册时模块名:,但同时、和又在全局空间中,使用方式和之前一样。如果要做到完全拆分,需要在子块加上选项,此时再访问它们就要加上命名空间前缀。
- 很显然,模块的方式可以拆分代码,但是缺点也很明显,就是使用起来比较繁琐复杂,容易出错。而且类型系统支持很差,不能给我们带来帮助。显然在这方面有了很大改进,是时候切换过去了
渲染顺序:先父后子,完成顺序:先子后父
更新顺序:父更新导致子更新,子更新完成后父
销毁顺序:先父后子,完成顺序:先子后父
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/qdvuejs/65135.html