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

Example Analysis of Hello World of Vue.js

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

Share

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

This article mainly introduces "Hello World example Analysis of Vue.js". In daily operation, I believe many people have doubts about Vue.js Hello World example analysis. The editor consulted all kinds of data and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts of "Vue.js Hello World example Analysis". Next, please follow the editor to study!

Constructor function

File path: src/instance/vue.js

Function Vue (options) {this._init (options)}

Initialization

Here we only analyze the most critical steps in understanding the examples. File path: src/instance/internal/init.js

Vue.prototype._init = function (options) {. / / merge options. Options = this.$options = mergeOptions (this.constructor.options, options, this). / / initialize data observation and scope inheritance. This._initState (). / / if `el` option is passed, start compilation. If (options.el) {this.$mount (options.el)}}

Merge options

MergeOptions () is defined in the src/util/options.js file, which mainly defines the merge of various attributes in options, such as props, methods, computed, watch, and so on. In addition, the default algorithm (strategy) for each attribute merge is defined here. These strategy can be configured. Refer to Custom Option Merge Strategy.

In the example of this article, it is mainly the merge of the data option, which is put into $options.data after merge, which is basically equivalent to the following:

Vm.$options.data = function mergedInstanceDataFn () {var parentVal = undefined / / this is executed after data var childVal = function () {return {message: 'Hello World'}} / / data function binds the vm instance in the options we defined Execution result: {message: 'Hello World'} var instanceData = childVal.call (vm) / / merge between objects, similar to $.extend, the result must be: {message:' Hello World'} return mergeData (instanceData, parentVal)}

Init data

_ initData () occurs in _ initState () and does two main things:

Properties in proxy data

Observe data

File path: src/instance/internal/state.js

Vue.prototype._initState = function () {this._initProps () this._initMeta () this._initMethods () this._initData () / here this._initComputed ()}

Property Agent (proxy)

Assign the result of data to the internal attribute: file path: src/instance/internal/state.js

Var dataFn = this.$options.data / / the mergedInstanceDataFn function var data = this._data = dataFn? DataFn (): {}

Attribute in the proxy data to _ data, so that vm.message = vm._data.message:

File path: src/instance/internal/state.js

/ * Proxy a property, so that * vm.prop = = vm._data.prop * / Vue.prototype._proxy = function (key) {if (! isReserved (key)) {var self = this Object.defineProperty (self, key, {configurable: true, enumerable: true, get: function proxyGetter () {return self._ data [key]} Set: function proxySetter (val) {self._ data [key] = val})}}

Observe

Here is our key point, the observe process. In _ initData () * *, observe (data, this) is called to observe the data. In the hello world example, the observe () function mainly creates an Observer object for {message: 'Hello World'}.

File path: src/observer/index.js

Var ob = new Observer (value) / / value = data = {message:'Hello World'}

In the observe () function, we also make some conditions to determine whether we can observe. These conditions are:

It has not been observe (all objects that have been observe will be added a _ _ ob__ attribute)

Can only be plain object (toString.call (ob) = = "[object Object]") or array

Cannot be a Vue instance (obj._isVue! = = true)

Object is extensible (Object.isExtensible (obj) = true)

Observer

There is a sentence on the Reactivity in Depth of the official website:

When you pass a plain JavaScript object to a Vue instance as its data option, Vue.js will walk through all of its properties and convert them to getter/setters

The getter/setters are invisible to the user, but under the hood they enable Vue.js to perform dependency-tracking and change-notification when properties are accessed or modified

That's what Observer does, making data a "publisher" and watcher a subscriber, subscribing to changes in data.

In the example, the process of creating an observer is:

New Observer ({message: 'Hello World'})

Instantiate a Dep object to collect dependencies

Every attribute of walk (Observer.prototype.walk ()) data, here is only message

Change the property to reactive's (Observer.protoype.convert ())

DefineReactive () is called in convert () to add reactiveGetter and reactiveSetter to the message attribute of data

File path: src/observer/index.js

Export function defineReactive (obj, key, value) {... Object.defineProperty (obj, key, {enumerable: true, configurable: true, get: function reactiveGetter () {... If (Dep.target) {dep.depend () / / here is the collection dependency.} return value}, set: function reactiveSetter (newVal) {. If (setter) {setter.call (obj, newVal)} else {val = newVal}... Dep.notify () / / here is the dependency of notify to observe this data (watcher)}})}

About dependency collection and notify, mainly the Dep class

File path: src/observer/dep.js

Export default function Dep () {this.id = uid++ this.subs = []}

The subs here is an array of subscribers (that is, watcher). When the observed data changes, setter is called, and dep.notify () loops through the subscribers here, calling their update methods respectively.

But in the code where getter collects dependencies, you don't see watcher added to subs. When did you add it? Answer this question when it comes to Watcher.

Mount node

According to the life cycle diagram, observe data and some init are followed by $mount, the most important of which is _ compile.

File path: src/instance/api/lifecycle.js

Vue.prototype.$mount = function (el) {... This._compile (el).}

_ compile is divided into two steps: compile and link

Compile

The compile process is to analyze a given element (el) or template (template), extract instructions (directive), and create corresponding offline DOM elements (document fragment).

File path: src/instance/internal/lifecycle.js

Vue.prototype._compile = function (el) {... Var rootLinker = compileRoot (el, options, contextOptions)... Var rootUnlinkFn = rootLinker (this, el, this._scope)... Var contentUnlinkFn = compile (el, options) (this, el).}

In the example, the compile # mountNode element is roughly as follows:

CompileRoot: since root node () itself doesn't have any instructions, nothing comes out of compile here.

Child node of compileChildNode:mountNode, that is, TextNode with the content of "{{message}}"

CompileTextNode:

3.1 parseText: actually tokenization (tokenization: extracting meaningful elements such as symbols, statements, etc.) from a string, and the result is tokens

3.2 processTextToken: parses instruction types, expressions, and filters from tokens, and creates a new empty TextNode

Create a fragment and put a new TextNode append in it

In the case of parseText, the string "{{message}" is matched by the regular expression (/\ {(. +?)\} |\ {(. +?)\} / g), and the resulting token contains this information: "this is a tag, and it is the tag of the text (text) rather than the tag of the HTML, not an one-time one-time interpolation, and the content of the tag is" message ". The regular expressions used to match here are generated dynamically based on the configuration of delimiters and unsafeDelimiters.

After processTextToken, you actually get all the information you need to create an instruction: instruction type v-text, expression "message", filter none, and the DOM that the instruction is responsible for following is the newly created TextNode. The next step is to instantiate instructions.

Link

Each compile function returns a link function (linkFn). LinkFn is to de-instantiate the instruction, combine the instruction with the newly created element link, and then replace the element with DOM tree. Each linkFn function returns a unlink function (unlinkFn). UnlinkFn is used when vm is destroyed and is not described here.

Instantiate directive:new Directive (description, vm, el)

Description is the information saved in the token of the compile result, as follows:

Description = {name: 'text', / / text instruction expression:' message', filters: undefined, def: vTextDefinition}

On the def attribute is the definition (definition) of the text directive. Like Custome Directive, the text instruction also has bind and update methods, which are defined as follows:

File path: src/directives/public/text.js

Export default {bind () {this.attr = this.el.nodeType = = 3? 'data':' textContent'}, update (value) {this.el [this.attr] = _ toString (value)}}

The new Directive () constructor is just some internal property assignment, and the real binding process also needs to call Directive.prototype._bind, which is called in the Vue instance method _ bindDir ().

In _ bind, a watcher is created, and the calculated value of the expression "message" is obtained through watcher * * times, and updated to the newly created TextNode to render "Hello World" on the page.

Watcher

For every directive / data binding in the template, there will be a corresponding watcher object, which records any properties "touched" during its evaluation as dependencies. Later on when a dependency's setter is called, it triggers the watcher to re-evaluate, and in turn causes its associated directive to perform DOM updates.

Each data-bound directive has a watcher to help it listen to the value of the expression and, if it changes, notify it of the DOM that update is responsible for. The dependency collection that I've been talking about happens right here.

Inside Directive.prototype._bind (), you will new Watcher (expression, update), passing in the expression and the update method of directive.

Watcher will go to parseExpression:

File path: src/parsers/expression.js

Export function parse_Expression (exp, needSet) {exp = exp.trim () / / try cache var hit = expressionCache.get (exp) if (hit) {if (needSet & &! hit.set) {hit.set = compileSetter (hit.exp)} return hit} var res = {exp: exp} res.get = isSimplePath (exp) & & exp.indexOf ('[')

< 0 // optimized super simple getter ? makeGetterFn('scope.' + exp) // dynamic getter : compileGetter(exp) if (needSet) { res.set = compileSetter(exp) } expressionCache.put(exp, res) return res } 这里的expression是"message",单一变量,被认为是简单的数据访问路径(simplePath)。simplePath的值如何计算,怎么通过"message"字符串获得data.message的值呢? 获取字符串对应的变量的值,除了用eval,还可以用Function。上面的makeGetterFn('scope.' + exp)返回: var getter = new Function('scope', 'return ' + body + ';') // new Function('scope', 'return scope.message;') Watch.prototype.get()获取表达式值的时候, var scope = this.vm getter.call(scope, scope) // 即执行vm.message 由于initState时对数据进行了代理(proxy),这里的vm.message即为vm._data.message,即是data选项中定义的"Hello World"。 值拿到了,那什么时候将message设为依赖的呢?这就要结合前面observe data里说到的reactiveGetter了。 文件路径:src/watcher.js Watcher.prototype.get = function () { this.beforeGet() // ->

Dep.target = this var scope = this.scope | | this.vm... Var value value = this.getter.call (scope, scope)... This.afterGet () / /-> Dep.target = null return value}

Watcher takes three steps to get the value of the expression:

BeforeGet: set Dep.target = this

Call the getter of the expression, read (getter) the value of vm.message, and enter the reactiveGetter of message. Because Dep.target has a value, you execute dep.depend () to put target, the current watcher, into the dep.subs array.

AfterGet: set Dep.target = null

What is worth noting here is Dep.target. Because of the single-threaded feature of JS, there can only be one value of watcher to get data at a time, so only one target is needed globally.

File path: src/observer/dep.js

/ / the current target watcher being evaluated. / / this is globally unique because there could be only one / / watcher being evaluated at any time. Dep.target = null

In this way, the instruction uses watcher to touch the data involved in the expression, and the data (reactive data) is saved as its changing subscriber (subscriber). When the data changes, the DOM is updated through dep.notify ()-> watcher.update ()-> directive.update ()-> textDirective.update ().

At this point, the study of "Hello World example Analysis of Vue.js" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

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