Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use List Rendering in Vue.js

2025-03-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains "how to use List Rendering in Vue.js". The content in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn how to use List Rendering in Vue.js.

Recap

Here, first use a few pictures to review and sort out the content behind the previous Vue.js source code (1): Hello World, which will be helpful to the understanding of the compile,link and bind process in this article:

Copmile phase: mainly the descriptor that gets the instruction

Link phase: instantiate instructions, replace DOM

Bind phase: call the bind function of the instruction to create a watcher

Represented by a picture, it is:

Observe array

The merge options,proxy process in initialization is basically the same as the Hello World process, so the analysis starts directly from observe.

/ / file path: src/observer/index.js var ob = new Observer (value) / / value = data = {todos: [{message: 'Learn JavaScript'},...]} / / file path: src/observer/index.js export function Observer (value) {this.value = value this.dep = new Dep () def (value,' _ ob__', this) if (isArray (value)) {/ / Array branch var augment = hasProto? ProtoAugment: copyAugment / / Select enhancement method augment (value, arrayMethods, arrayKeys) / / Enhancement array this.observeArray (value)} else {/ / plain object branch this.walk (value)}}

Enhanced array

Augment the array, that is, extend the array so that it can detect change. There are two things here, one is to intercept the mutation methods of the array (the method that causes the array itself to change), and the other is to provide two convenient methods $set and $remove.

There are two ways to intercept: if the browser implements _ _ proto__, then use protoAugment, otherwise use copyAugment.

/ / file path: src/util/evn.js export const hasProto ='_ proto__' in {} / / file path: src/observer/index.js / / intercept prototype chain function protoAugment (target, src) {target.__proto__ = src} / / file path: src/observer/index.js / / define attributes function copyAugment (target, src, keys) {for (var I = 0, l = keys.length; I)

< l; i++) { var key = keys[i] def(target, key, src[key]) } } 为了更直观,请看下面的示意图: 增强之前:

Intercept through the prototype chain:

Intercept by defining attributes:

In the interceptor arrayMethods, these mutation methods are wrapped:

Call methods in the native Array.prototype

Check to see if any new values are inserted (mainly push, unshift and splice methods)

If new values are inserted, observe them

* is notify change: call dep.notify () of observer

The code is as follows:

/ / file path: src/observer/array.js ['push',' pop', 'shift',' unshift', 'splice',' sort', 'reverse'] .forEach (function (method) {/ / cache original method var original = arrayProto [method] def (arrayMethods, method) Function mutator () {/ / avoid leaking arguments: / / http://jsperf.com/closure-with-arguments var I = arguments.length var args = new Array (I) while (iMui -) {args [I] = arguments [I]} var result = original.apply (this) Args) var ob = this.__ob__ var inserted switch (method) {case 'push': inserted = args break case' unshift': inserted = args break case 'splice': inserted = args.slice (2) break} if (inserted) ob.observeArray (inserted) / / notify change ob.dep.notify () return result})})

ObserveArray ()

Knowing observe () in the previous article, observeArray () here is very simple, that is, observe the array objects to generate Observer instances for their respective objects.

/ / file path: src/observer/index.js Observer.prototype.observeArray = function (items) {for (var I = 0, l = items.length; I)

< l; i++) { observe(items[i]) } } compile 在介绍v-for的compile之前,有必要回顾一下compile过程:compile是一个递归遍历DOM tree的过程,这个过程对每个node进行指令类型,指令参数,表达式,过滤器等的解析。 递归过程大致如下: compile当前node 如果当前node没有terminal directive,则遍历child node,分别对其compile node 如果当前node有terminal directive,则跳过其child node 这里有个terminal directive的概念,这个概念在Element Directive中提到过: A big difference from normal directives is that element directives are terminal, which means once Vue encounters an element directive, it will completely skip that element 实际上自带的directive中也有两个terminal的directive,v-for和v-if(v-else)。 terminal directive 在源码中找到: terminal directive will have a terminal link function, which build a node link function for a terminal directive. A terminal link function terminates the current compilation recursion and handles compilation of the subtree in the directive. 也就是上面递归过程中描述的,有terminal directive的node在compile时,会跳过其child node的compile过程。而这些child node将由这个directive单独compile(partial compile)。 以图为例,红色节点有terminal directive,compile时(绿线)将其子节点跳过: 为什么是v-for和v-if?因为它们会带来节点的增加或者删除。 Compile的中间产物是directive的descriptor,也可能会创建directive来管理的document fragment。这些产物是在link阶段时需要用来实例化directive的。从racap中的图可以清楚的看到,compile过程产出了和link过程怎么使用的它们。那么现在看看v-for的情况: compile之后,只得到了v-for的descriptor,link时将用它实例化v-for指令。 descriptor = { name: 'for', attrName: 'v-for', expression: 'todo in todos', raw: 'todo in todos', def: vForDefinition } link Hello World中,link会实例化指令,并将其与compile阶段创建好的fragment(TextNode)进行绑定。但是本文例子中,可以看到compile过程没有创建fragment。这里的link过程只实例化指令,其他过程将发生在v-for指令内部。 bind 主要的list rendering的魔法都在v-for里面,这里有FragmentFactory,partial compile还有diff算法(diff算法会在单独的文章介绍)。 在v-for的bind()里面,做了三件事: 重新赋值expression,找出alias:"todo in todos"里面,todo是alias,todos才是真正的需要监听的表达式 移除{{todo.text}}元素,替换上start和end锚点(anchor)。锚点用来帮助插入最终的li节点 创建FragmentFactory:factory会compile被移除的li节点,得到并缓存linker,后面会用linker创建Fragment // file path: /src/directives/public/for.js bind () { // 找出alias,赋值expression = "todos" var inMatch = this.expression.match(/(.*) (?:in|of) (.*)/) if (inMatch) { var itMatch = inMatch[1].match(/\((.*),(.*)\)/) if (itMatch) { this.iterator = itMatch[1].trim() this.alias = itMatch[2].trim() } else { this.alias = inMatch[1].trim() } this.expression = inMatch[2] } ... // 创建锚点,移除LI元素 this.start = createAnchor('v-for-start') this.end = createAnchor('v-for-end') replace(this.el, this.end) before(this.start, this.end) ... // 创建FragmentFactory this.factory = new FragmentFactory(this.vm, this.el) } Fragment & FragmentFactory 这里的Fragment,指的不是DocumentFragment,而是Vue内部实现的一个类,源码注释解释为: Abstraction for a partially-compiled fragment. Can optionally compile content with a child scope. FragmentFactory会compile{{todo.text}},并保存返回的linker。在v-for中,数组发生变化时,将创建scope,克隆template,即{{todo.text}},使用linker,实例化Fragment,然后挂在end锚点上。 在Fragment中调用linker时,就是link和bind{{todo.text}},和Hello World中一样,创建v-text实例,创建watcher。 scope 为什么在v-for指令里面可以通过别名(alias)todo访问循环变量?为什么有$index和$key这样的特殊变量?因为使用了child scope。 还记得Hello World中watcher是怎么识别simplePath的吗? var getter = new Function('scope', 'return scope.message;') 在这里,说白了就是访问scope对象的todo,$index或者$key属性。在v-for指令里,会扩展其父作用域,本例中父作用域对象就是vm本身。在调用factory创建每一个fragment时,都会以下面方式创建合适的child scope给其使用: // file path: /src/directives/public/for.js create (value, alias, index, key) { // index是遍历数组时的下标 // value是对应下标的数组元素 // alias = 'todo' // key是遍历对象时的属性名称 ... var parentScope = this._scope || this.vm var scope = Object.create(parentScope) // 以parent scope为原型链创建child scope ... withoutConversion(() =>

{defineReactive (scope, alias, value) / / add alias to child scope}) defineReactive (scope,'$index', index) / / add $index to child scope. Var frag = this.factory.create (host, scope, this._frag).}

Detect change

At this point, basically "preliminary" the process of List Rendering, there are many concepts are not in-depth, intend to put in the back with other use of these concepts in the analysis, should be able to experience its ingenious design.

* review the above with two examples

Example 1:

Vm.todos [0] .text = 'Learn JAVASCRIPT'

What is changed is the text attribute in the array element. Because the v-text instruction observe todo.text of the fragment created by factory, the TextNode content of the corresponding li element is updated directly by the v-text instruction.

Example 2:

Vm.todos.push ({text: 'Learn Vue Source Code'})

An array element is added, and the watcher of the v-for instruction informs it to do the update,diff algorithm to determine that a new element has been added, so create a scope,factory clone template and create a new fragment,append. Before # end-anchor, the v-text instruction in fragment observe adds the text attribute of the element and updates the value to TextNode.

Thank you for reading, the above is the content of "how to use List Rendering in Vue.js". After the study of this article, I believe you have a deeper understanding of how to use List Rendering in Vue.js, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report