首頁  >  文章  >  web前端  >  Vue.js響應式原理詳解

Vue.js響應式原理詳解

小云云
小云云原創
2018-02-10 13:59:391837瀏覽
############################## setter#########,它讓程式碼更簡潔。例如,我們看看下面的Java程式碼:#########
<span style="font-size: 14px;">class Person{<br/>    String firstName;<br/>    String lastName;    // 这个Demo中省略了一些构造器代码 :)    public void setFirstName(firstName) {        this.firstName = firstName;<br/>    }    public String getFirstName() {        return firstName;<br/>    }    public void setLastName(lastName) {        this.lastName = lastName;<br/>    }    public String getLastName() {        return lastName;<br/>    }<br/>}// Create instancePerson bradPitt = new Person();<br/>bradPitt.setFirstName("Brad");<br/>bradPitt.setLastName("Pitt");<br/></span>

JavaScript開發人員永遠不會這樣做,相反他們會這樣做:

<span style="font-size: 14px;">var Person = function () {};var bradPitt = new Person();<br/>bradPitt.firstName = &#39;Brad&#39;;<br/>bradPitt.lastName = &#39;Pitt&#39;;<br/></span>

這要簡潔的多。通常簡潔更好,不是嗎?

的確如此,但有時我想取得一些可以被修改的屬性,但我不用知道這些屬性是什麼。例如,我們在Java程式碼中擴充一個新的方法<span style="font-size: 14px;">getFullName()</span>#:

<span style="font-size: 14px;">class Person{    private String firstName;    private String lastName;    // 这个Demo中省略了一些构造器代码 :)    public void setFirstName(firstName) {        this.firstName = firstName;<br/>    }    public String getFirstName() {        return firstName;<br/>    }    public void setLastName(lastName) {        this.lastName = lastName;<br/>    }    public String getLastName() {        return lastName;<br/>    }    public String getFullName() {        return firstName + " " + lastName;<br/>    }<br/>}<br/><br/>Person bradPitt = new Person();<br/>bradPitt.setFirstName("Brad");<br/>bradPitt.setLastName("Pitt");// Prints &#39;Brad Pitt&#39;System.out.println(bradPitt.getFullName());<br/></span>

在上面例子中, <span style="font-size: 14px;">fullName</span> 是一個計算的屬性,它不是私有屬性,但總是能傳回正確的結果。

C# 和隱式的getter/setters

#我們來看看C# 特性之一:隱含的 <span style="font-size: 14px;">getters</span>/<span style="font-size: 14px;">setters</span>,我真的很喜歡它。在C# 中,如果需要,你可以定義<span style="font-size: 14px;">getters</span>#/<span style="font-size: 14px;">setters</span>,但是並不用這樣做,但是如果你決定要這麼做,呼叫者就不必呼叫函數。呼叫者只需要直接存取屬性,<span style="font-size: 14px;">getter</span>/<span style="font-size: 14px;">setter</span>## 會自動在在鉤子函數中運行:<span style="font-size: 14px;"></span>

<span style="font-size: 14px;">public class Foo {    public string FirstName {get; set;}    public string LastName {get; set;}    public string FullName {get { return firstName + " " + lastName }; private set;}<br/>}<br/></span>

我覺得這很酷...<span style="font-size: 14px;"></span>

現在,如果我想在JavaScript中實現類似的功能,我會浪費很多時間,例如:<span style="font-size: 14px;"></span>

<span style="font-size: 14px;">var person0 = {<br/>    firstName: &#39;Bruce&#39;,<br/>    lastName: &#39;Willis&#39;,<br/>    fullName: &#39;Bruce Willis&#39;,<br/>    setFirstName: function (firstName) {        this.firstName = firstName;        this.fullName = `${this.firstName} ${this.lastName}`;<br/>    },<br/>    setLastname: function (lastName) {        this.lastName = lastName;        this.fullName = `${this.firstName} ${this.lastName}`;<br/>    },<br/>};<br/>console.log(person0.fullName);<br/>person0.setFirstName(&#39;Peter&#39;);<br/>console.log(person0.fullName);<br/></span>

它會印出:<span style="font-size: 14px;"></span>

<span style="font-size: 14px;">"Bruce Willis"<br/>"Peter Willis"<br/></span>

##但使用

<span style="font-size: 14px;"></span><span style="font-size: 14px;"> #setXXX(value)</span> 的方式並不夠'JavaScripty'(是個玩笑啦)。

下面的方式可以解決這個問題:

<span style="font-size: 14px;">var person1 = {<br/>    firstName: &#39;Brad&#39;,<br/>    lastName: &#39;Pitt&#39;,<br/>    getFullName: function () {        return `${this.firstName} ${this.lastName}`;<br/>    },<br/>};<br/>console.log(person1.getFullName()); // 打印 "Brad Pitt"<br/></span>

現在我們回到被計算過的<span style="font-size: 14px;"> getter</span>。你可以設定<span style="font-size: 14px;">first</span><span style="font-size: 14px;">last name</span>,並且簡單的合併它們的值:

<span style="font-size: 14px;">person1.firstName = &#39;Peter&#39;person1.getFullName(); // 返回 "Peter Pitt"<br/></span>

這的確比較方便,但我還是不喜歡它,因為我們要定義一個叫<span style="font-size: 14px;">getxxx()</span>的方法,這也不夠'JavaScripty'。多年來,我一直在思考如何更好的使用 JavaScript。

然後Vue 出現了

在我的Youtube頻道,很多和Vue教程有關的影片都講到,我習慣響應式開發,在更早的Angular1時代,我們叫它:資料綁定(Data Binding)。它看起來很簡單。你只需要在Vue實例的<span style="font-size: 14px;">data()</span> 區塊中定義一些數據,並綁定到HTML:

<span style="font-size: 14px;">var vm = new Vue({<br/>    data() {<br/>        return {<br/>        greeting: &#39;Hello world!&#39;,<br/>        };<br/>    }<br/>})<div>{greeting}</div><br/></span>

顯然它會在使用者介面印出<span style="font-size: 14px;">「Hello world!」</span>

現在,如果你改變<span style="font-size: 14px;">greeting</span>的值,Vue引擎會回應並相應地更新視圖。

<span style="font-size: 14px;">methods: {<br/>    onSomethingClicked() {        this.greeting = "What&#39;s up";<br/>    },<br/>}<br/></span>

很長一段時間我都在想,它是如何運作的?當某個物件的屬性發生變化時會觸發某個事件?或是Vue不停的呼叫 <span style="font-size: 14px;">setInterval</span> 去檢查是否更新?

透過閱讀Vue官方文檔,我才知道,改變一個物件屬性將隱式調用##getter<span style="font-size: 14px;"></span>/ <span style="font-size: 14px;"></span>setter<span style="font-size: 14px;"></span>,再次通知觀察者,然後觸發重新渲染,如下圖,這個例子來自官方的Vue.js文件:<span style="font-size: 14px;"></span>

Vue.js響應式原理詳解

但我还想知道:

  • 怎么让数据自带<span style="font-size: 14px;">getter</span>/<span style="font-size: 14px;">setters</span>

  • 这些隐式调用内部是怎样的?

第一个问题很简单:Vue为我们准备好了一切。当你添加新数据,Vue将会通过其属性为其添加 <span style="font-size: 14px;">getter</span>/<span style="font-size: 14px;">setters</span>。但是我让 <span style="font-size: 14px;">foo.bar = 3</span>? 会发生什么?

这个问题的答案出现在我和SVG & Vue专家Sarah Drasner的Twitter对话中:

Vue.js響應式原理詳解

Timo: <span style="font-size: 14px;">foo.bar=value;</span>是怎么做到实时响应的?

Sarah: 这个问题很难在Twitter说清楚,可以看这篇文章

Timo: 但这篇文章并没有解释上面提到的问题。

Timo: 它们就像:分配一个值->调用<span style="font-size: 14px;">setter</span>->通知观察者,不理解为什么在不使用<span style="font-size: 14px;">setInterval</span><span style="font-size: 14px;">Event</span>的情况下,<span style="font-size: 14px;">setter</span>/<span style="font-size: 14px;">getter</span>就存在了。

Sarah: 我的理解是:你获取的所有数据都在Vue实例<span style="font-size: 14px;">data{}</span>中被代理了。

显然,她也是参考的官方文档,之前我也读过,所以我开始阅读Vue源码,以便更好的理解发生了什么。过了一会我想起在官方文档看到一个叫 <span style="font-size: 14px;">Object.defineProperty()</span> 的方法,我找到它,如下:

<span style="font-size: 14px;">/**<br/>* 给对象定义响应的属性<br/>*/export function defineReactive (<br/>    obj: Object,<br/>    key: string,<br/>    val: any,<br/>    customSetter?: ?Function,<br/>    shallow?: boolean<br/>) {    const dep = new Dep()    const property = Object.getOwnPropertyDescriptor(obj, key)    if (property && property.configurable === false) {        return<br/>    }    // 预定义getter/setters    const getter = property && property.get    const setter = property && property.set    let childOb = !shallow && observe(val)<br/>    Object.defineProperty(obj, key, {<br/>        enumerable: true,<br/>        configurable: true,        get: function reactiveGetter () {            const value = getter ? getter.call(obj) : val            if (Dep.target) {<br/>                dep.depend()                if (childOb) {<br/>                    childOb.dep.depend()<br/>                }                if (Array.isArray(value)) {<br/>                    dependArray(value)<br/>                }<br/>            }            return value<br/>        },        set: function reactiveSetter (newVal) {            const value = getter ? getter.call(obj) : val            /* 禁用eslint 不进行自我比较 */            if (newVal === value || (newVal !== newVal && value !== value)) {                return<br/>            }            /* 开启eslint 不进行自己比较 */            if (process.env.NODE_ENV !== &#39;production&#39; && customSetter) {<br/>                customSetter()<br/>            }            if (setter) {<br/>                setter.call(obj, newVal)<br/>            } else {<br/>                val = newVal<br/>            }<br/>            childOb = !shallow && observe(newVal)<br/>            dep.notify()<br/>        }<br/>    })<br/>}<br/></span>

所以答案一直存在于文档中:

把一个普通 JavaScript 对象传给 Vue 实例的 <span style="font-size: 14px;">data</span> 选项,Vue 将遍历此对象所有的属性,并使用 <span style="font-size: 14px;">Object.defineProperty</span> 把这些属性全部转为 <span style="font-size: 14px;">getter</span>/<span style="font-size: 14px;">setter</span><span style="font-size: 14px;">Object.defineProperty</span> 是仅 ES5 支持,且无法 <span style="font-size: 14px;">shim</span> 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。

我只想简单的了解 <span style="font-size: 14px;">Object.defineProperty()</span> 做了什么,所以我用一个例子简单的给你讲解一下:

<span style="font-size: 14px;">var person2 = {<br/>    firstName: &#39;George&#39;,<br/>    lastName: &#39;Clooney&#39;,<br/>};<br/>Object.defineProperty(person2, &#39;fullName&#39;, {<br/>    get: function () {        return `${this.firstName} ${this.lastName}`;<br/>    },<br/>});<br/>console.log(person2.fullName); // 打印 "George Clooney"<br/></span>

还记得文章开头C#的隐式 <span style="font-size: 14px;">getter</span> 吗?它们看起来很类似,但ES5才开始支持。你需要做的是使用 <span style="font-size: 14px;">Object.defineProperty()</span> 定义现有对象,以及何时获取这个属性,这个<span style="font-size: 14px;">getter</span>被称为响应式——这实际上就是Vue在你添加新数据时背后所做的事。

<span style="font-size: 14px;">Object.defineProperty()</span>能讓Vue變的更簡化嗎?

學完這一切,我一直在想,<span style="font-size: 14px;">#Object.defineProperty()</span> 是否能讓Vue變的更簡化?現今越來越多的新術語,是不是真的有必要把事情變得過於複雜,變的讓初學者難以理解(Redux也是同樣):

  • <span style="font-size: 14px;">Mutator</span>:或許你在說(隱含)<span style="font-size: 14px;">setter</span>

  • ##Getters<span style="font-size: 14px;"></span>:為什麼不使用<span style="font-size: 14px;"></span>#Object.defineProperty()<span style="font-size: 14px;"></span> 替換成(隱式)<span style="font-size: 14px;"></span>getter<span style="font-size: 14px;"></span>

  • #store.commit()<span style="font-size: 14px;"></span>:為什麼不簡化成<span style="font-size: 14px;"></span>foo = bar<span style="font-size: 14px;"></span>,而是<span style="font-size: 14px;"></span>store.commit("setFoo", bar);<span style="font-size: 14px;"></span>? <span style="font-size: 14px;"></span>

你是怎麼認為的? Vuex必須是複雜的還是可以像 <span style="font-size: 14px;"></span>Object.defineProperty()<span style="font-size: 14px;"></span> 一樣簡單? <span style="font-size: 14px;"></span>

相關推薦:<span style="font-size: 14px;"></span>

javascript用rem來做響應式開發實例分享<span style="font-size: 14px;"></span>

詳解前端響應式佈局、響應式圖片,與自製柵格系統<span style="font-size: 14px;"></span>

響應式和自適應有什麼區別<span style="font-size: 14px;"></span>

以上是Vue.js響應式原理詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn