成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

試著用Proxy 實(shí)現(xiàn)一個(gè)簡(jiǎn)單mvvm

fnngj / 2589人閱讀

摘要:套數(shù)據(jù),實(shí)現(xiàn)界面先把計(jì)算屬性這個(gè)注釋掉,后面進(jìn)行實(shí)現(xiàn)計(jì)算屬性然后在函數(shù)中增加一個(gè)編譯函數(shù),號(hào)表示是添加的函數(shù)添加一個(gè)編譯函數(shù)上面我們添加了一個(gè)的構(gòu)造函數(shù)。

Proxy、Reflect的簡(jiǎn)單概述
Proxy 可以理解成,在目標(biāo)對(duì)象之前架設(shè)一層“攔截”,外界對(duì)該對(duì)象的訪問(wèn),都必須先通過(guò)這層攔截,因此提供了一種機(jī)制,可以對(duì)外界的訪問(wèn)進(jìn)行過(guò)濾和改寫(xiě)。Proxy 這個(gè)詞的原意是代理,用在這里表示由它來(lái)“代理”某些操作,可以譯為“代理器”。
出自阮一峰老師的ECMAScript 6 入門(mén),詳細(xì)點(diǎn)擊http://es6.ruanyifeng.com/#docs/proxy

例如:

var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log(`getting ${key}!`);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, value, receiver) {
    console.log(`setting ${key}!`);
    return Reflect.set(target, key, value, receiver);
  }
});

上面代碼對(duì)一個(gè)空對(duì)象架設(shè)了一層攔截,重定義了屬性的讀?。╣et)和設(shè)置(set)行為。這里暫時(shí)先不解釋具體的語(yǔ)法,只看運(yùn)行結(jié)果。對(duì)設(shè)置了攔截行為的對(duì)象obj,去讀寫(xiě)它的屬性,就會(huì)得到下面的結(jié)果。

obj.count = 1
//  setting count!
++obj.count
//  getting count!
//  setting count!
//  2
var proxy = new Proxy(target, handler);

這里有兩個(gè)參數(shù),target參數(shù)表示所要攔截的目標(biāo)對(duì)象,handler參數(shù)也是一個(gè)對(duì)象,用來(lái)定制攔截行為。

注意,要使得Proxy起作用,必須針對(duì)Proxy實(shí)例(上例是proxy對(duì)象)進(jìn)行操作,而不是針對(duì)目標(biāo)對(duì)象(上例是空對(duì)象)進(jìn)行操作。

Reflect對(duì)象與Proxy對(duì)象一樣,也是 ES6 為了操作對(duì)象而提供的新 API。

Reflect對(duì)象的方法與Proxy對(duì)象的方法一一對(duì)應(yīng),只要是Proxy對(duì)象的方法,就能在Reflect對(duì)象上找到對(duì)應(yīng)的方法。這就讓Proxy對(duì)象可以方便地調(diào)用對(duì)應(yīng)的Reflect方法,完成默認(rèn)行為,作為修改行為的基礎(chǔ)。也就是說(shuō),不管Proxy怎么修改默認(rèn)行為,你總可以在Reflect上獲取默認(rèn)行為。

同樣也放上阮一峰老師的鏈接http://es6.ruanyifeng.com/#docs/reflect

初始化結(jié)構(gòu)

看到這里,我就當(dāng)大家有比較明白Proxy(代理)是做什么用的,然后下面我們看下要做最終的圖騙。

看到上面的圖片,首先我們新建一個(gè)index.html,然后里面的代碼是這樣子滴。很簡(jiǎn)單




    
    簡(jiǎn)單版mvvm


開(kāi)發(fā)語(yǔ)言:{{language}}

組成部分:

  • {{makeUp.one}}
  • {{makeUp.two}}
  • {{makeUp.three}}

描述:

{{describe}}

計(jì)算屬性:{{sum}}

看到上面的代碼,大概跟vue長(zhǎng)得差不多,下面去實(shí)現(xiàn)Mvvm這個(gè)構(gòu)造函數(shù)

實(shí)現(xiàn)Mvvm這個(gè)構(gòu)造函數(shù)

首先聲明一個(gè)Mvvm函數(shù),options當(dāng)作參數(shù)傳進(jìn)來(lái),options就是上面代碼的配置,里面有eldata、computed~~

function Mvvm(options = {}) {
    // 把options 賦值給this.$options
    this.$options = options
    // 把options.data賦值給this._data
    let data = this._data = this.$options.data
    let vm = initVm.call(this)
    return this._vm
}

上面Mvvm函數(shù)很簡(jiǎn)單,就是把參數(shù)options 賦值給this.$options、把options.data賦值給this._data、然后調(diào)用初始化initVm函數(shù),并用call改變this的指向,方便initVm函操作。然后返回一個(gè)this._vm,這個(gè)是在initVm函數(shù)生成的。

下面繼續(xù)寫(xiě)initVm函數(shù),

function initVm () {
    this._vm = new Proxy(this, {
        // 攔截get
        get: (target, key, receiver) => {
            return this[key] || this._data[key] || this._computed[key]
        },
        // 攔截set
        set: (target, key, value) => {
            return Reflect.set(this._data, key, value)
        }
    })
    return this._vm
}

這個(gè)init函數(shù)用到Proxy攔截了,this對(duì)象,生產(chǎn)Proxy實(shí)例的然后賦值給this._vm,最后返回this._vm

上面我們說(shuō)了,要使得Proxy起作用,必須針對(duì)Proxy實(shí)例。

在代理里面,攔截了getset,get函數(shù)里面,返回this對(duì)象的對(duì)應(yīng)的key的值,沒(méi)有就去this._data對(duì)象里面取對(duì)應(yīng)的key,再?zèng)]有去this._computed對(duì)象里面去對(duì)應(yīng)的key值。set函數(shù)就是直接返回修改this._data對(duì)應(yīng)key。

做好這些各種攔截工作。我們就可以直接從實(shí)力上訪問(wèn)到我們相對(duì)應(yīng)的值了。(mvvm使我們第一塊代碼生成的實(shí)例)

mvvm.b // 2
mvvm.a // 1
mvvm.language // "Javascript"

如上圖看控制臺(tái)??梢栽O(shè)置值,可以獲取值,但是這不是響應(yīng)式的。

打開(kāi)控制臺(tái)看一下

可以詳細(xì)的看到。只有_vm這個(gè)是proxy,我們需要的是,_data下面所有數(shù)據(jù)都是有攔截代理的;下面我們就去實(shí)現(xiàn)它。

實(shí)現(xiàn)所有數(shù)據(jù)代理攔截

我們首先在Mvvm里面加一個(gè)initObserve,如下

function Mvvm(options = {}) {
    this.$options = options
    let data = this._data = this.$options.data
    let vm = initVm.call(this)
+   initObserve.call(this, data) // 初始化data的Observe
    return this._vm
}

initObserve這個(gè)函數(shù)主要是把,this._data都加上代理。如下

function initObserve(data) {
    this._data = observe(data) // 把所有observe都賦值到 this._data
}

// 分開(kāi)這個(gè)主要是為了下面遞歸調(diào)用
function observe(data) {
    if (!data || typeof data !== "object") return data // 如果不是對(duì)象直接返回值
    return new Observe(data) // 對(duì)象調(diào)用Observe
}

下面主要實(shí)現(xiàn)Observe類

// Observe類
class Observe {
    constructor(data) {
        this.dep = new Dep() // 訂閱類,后面會(huì)介紹
        for (let key in data) {
            data[key] = observe(data[key]) // 遞歸調(diào)用子對(duì)象
        }
        return this.proxy(data)
    }
    proxy(data) {
      let dep = this.dep
      return new Proxy(data, {
        get: (target, key, receiver) => {
          return Reflect.get(target, key, receiver)
        },
        set: (target, key, value) => {
          const result = Reflect.set(target, key, observe(value)) // 對(duì)于新添加的對(duì)象也要進(jìn)行添加observe
          return result  
        }
      })
    }
  }

這樣子,通過(guò)我們層層遞歸添加proxy,把我們的_data對(duì)象都添加一遍,再看一下控制臺(tái)

很不錯(cuò),_data也有proxy了,很王祖藍(lán)式的完美。

看到我們的html的界面,都是沒(méi)有數(shù)據(jù)的,上面我們把數(shù)據(jù)都準(zhǔn)備好了,下面我們就開(kāi)始把數(shù)據(jù)結(jié)合到html的界面上。

套數(shù)據(jù),實(shí)現(xiàn)hmtl界面

先把計(jì)算屬性這個(gè)html注釋掉,后面進(jìn)行實(shí)現(xiàn)

然后在Mvvm函數(shù)中增加一個(gè)編譯函數(shù),?號(hào)表示是添加的函數(shù)

function Mvvm(options = {}) {
    this.$options = options
    let data = this._data = this.$options.data
    let vm = initVm.call(this)
+   new Compile(this.$options.el, vm) // 添加一個(gè)編譯函數(shù)
    return this._vm
}

上面我們添加了一個(gè)Compile的構(gòu)造函數(shù)。把配置的el作為參數(shù)傳機(jī)進(jìn)來(lái),把生成proxy的實(shí)例vm也傳進(jìn)去,這樣子我們就可以拿到vm下面的數(shù)據(jù),下面我們就去實(shí)現(xiàn)它。順序讀注釋就可以了,很好理解

// 編譯類
class Compile {
    constructor (el, vm) {
        this.vm = vm // 把傳進(jìn)來(lái)的vm 存起來(lái),因?yàn)檫@個(gè)vm.a = 1 沒(méi)毛病
        let element = document.querySelector(el) // 拿到 app 節(jié)點(diǎn)
        let fragment = document.createDocumentFragment() // 創(chuàng)建fragment代碼片段
        fragment.append(element) // 把a(bǔ)pp節(jié)點(diǎn) 添加到 創(chuàng)建fragment代碼片段中
        this.replace(fragment) // 套數(shù)據(jù)函數(shù)
        document.body.appendChild(fragment) // 最后添加到body中
    }
    replace(frag) {
        let vm = this.vm // 拿到之前存起來(lái)的vm
        // 循環(huán)frag.childNodes
        Array.from(frag.childNodes).forEach(node => {
            let txt = node.textContent // 拿到文本 例如:"開(kāi)發(fā)語(yǔ)言:{{language}}"
            let reg = /{{(.*?)}}/g // 定義匹配正則
            if (node.nodeType === 3 && reg.test(txt)) {
            
                replaceTxt()
                
                function replaceTxt() {
                    // 如果匹配到的話,就替換文本
                    node.textContent = txt.replace(reg, (matched, placeholder) => {
                        return placeholder.split(".").reduce((obj, key) => {
                            return obj[key] // 例如:去vm.makeUp.one對(duì)象拿到值
                        }, vm)
                    })
                }
            }
            // 如果還有字節(jié)點(diǎn),并且長(zhǎng)度不為0 
            if (node.childNodes && node.childNodes.length) {
                // 直接遞歸匹配替換
                this.replace(node)
            }
        })
    }
}

上面的編譯函數(shù),總之就是一句話,千方百計(jì)的把{{xxx}}的占位符通過(guò)正則替換成真實(shí)的數(shù)據(jù)。

然后刷新瀏覽器,鐺鐺檔鐺鐺檔,就出現(xiàn)我們要的數(shù)據(jù)了。

很好很好,但是我們現(xiàn)在的數(shù)據(jù)并不是改變了 就發(fā)生變化了。還需要訂閱發(fā)布和watcher來(lái)配合,才能做好改變數(shù)據(jù)就發(fā)生變化了。下面我們先實(shí)現(xiàn)訂閱發(fā)布。

實(shí)現(xiàn)訂閱發(fā)布

訂閱發(fā)布其實(shí)是一種常見(jiàn)的程序設(shè)計(jì)模式,簡(jiǎn)單直白來(lái)說(shuō)就是:

把函數(shù)push到一個(gè)數(shù)組里面,然后循環(huán)數(shù)據(jù)調(diào)用函數(shù)。

例如:舉個(gè)很直白的例子

let arr = [] 
let a = () => {console.log("a")}

arr.push(a) // 訂閱a函數(shù)
arr.push(a) // 又訂閱a函數(shù)
arr.push(a) // 雙訂閱a函數(shù)

arr.forEach(fn => fn()) // 發(fā)布所有

// 此時(shí)會(huì)打印三個(gè)a

很簡(jiǎn)單吧。下面我們?nèi)?shí)現(xiàn)我們的代碼

// 訂閱類
class Dep {
    constructor() {
        this.subs = [] // 定義數(shù)組
    }
    // 訂閱函數(shù)
    addSub(sub) {
        this.subs.push(sub)
    }
    // 發(fā)布函數(shù)
    notify() {
        this.subs.filter(item => typeof item !== "string").forEach(sub => sub.update())
    }
}

訂閱發(fā)布是寫(xiě)好了,但是在什么時(shí)候訂閱,什么時(shí)候發(fā)布??這時(shí)候,我們是在數(shù)據(jù)獲取的時(shí)候訂閱watcher,然后在數(shù)據(jù)設(shè)置的時(shí)候發(fā)布watcher,在上面的Observe類里面里面,看?號(hào)的代碼。 .

... //省略代碼
...
proxy(data) {
    let dep = this.dep
    return new Proxy(data, {
        // 攔截get
        get: (target, prop, receiver) => {
+           if (Dep.target) {
                // 如果之前是push過(guò)的,就不用重復(fù)push了
                if (!dep.subs.includes(Dep.exp)) {
                    dep.addSub(Dep.exp) // 把Dep.exp。push到sub數(shù)組里面,訂閱
                    dep.addSub(Dep.target) // 把Dep.target。push到sub數(shù)組里面,訂閱
                }
+           }
            return Reflect.get(target, prop, receiver)
        },
        // 攔截set
        set: (target, prop, value) => {
            const result = Reflect.set(target, prop, observe(value))
+           dep.notify() // 發(fā)布
            return result  
        }
    })
}

上面代碼說(shuō)到,watcher是什么鬼?然后發(fā)布里面的sub.update()又是什么鬼??

帶著一堆疑問(wèn)我們來(lái)到了watcher

實(shí)現(xiàn)watcher

看詳細(xì)注釋

// Watcher類
class Watcher {
    constructor (vm, exp, fn) {
        this.fn = fn // 傳進(jìn)來(lái)的fn
        this.vm = vm // 傳進(jìn)來(lái)的vm
        this.exp = exp // 傳進(jìn)來(lái)的匹配到exp 例如:"language","makeUp.one"
        Dep.exp = exp // 給Dep類掛載一個(gè)exp
        Dep.target = this // 給Dep類掛載一個(gè)watcher對(duì)象,跟新的時(shí)候就用到了
        let arr = exp.split(".")
        let val = vm
        arr.forEach(key => {
            val = val[key] // 獲取值,這時(shí)候會(huì)粗發(fā)vm.proxy的get()函數(shù),get()里面就添加addSub訂閱函數(shù)
        })
        Dep.target = null // 添加了訂閱之后,把Dep.target清空
    }
    update() {
        // 設(shè)置值會(huì)觸發(fā)vm.proxy.set函數(shù),然后調(diào)用發(fā)布的notify,
        // 最后調(diào)用update,update里面繼續(xù)調(diào)用this.fn(val)
        let exp = this.exp
        let arr = exp.split(".")
        let val = this.vm
        arr.forEach(key => {
            val = val[key]
        })
        this.fn(val)
    }
}

Watcher類就是我們要訂閱的watcher,里面有回調(diào)函數(shù)fn,有update函數(shù)調(diào)用fn,

我們都弄好了。但是在哪里添加watcher呢??如下代碼

在Compile里面

...
...
function replaceTxt() {
    node.textContent = txt.replace(reg, (matched, placeholder) => {
+       new Watcher(vm, placeholder, replaceTxt);   // 監(jiān)聽(tīng)變化,進(jìn)行匹配替換內(nèi)容
        return placeholder.split(".").reduce((val, key) => {
            return val[key]
        }, vm)
    })
}

添加好有所的東西了,我們看一下控制臺(tái)。修改發(fā)現(xiàn)果然起作用了。

然后我們回顧一下所有的流程,然后看見(jiàn)古老(我也是別的地方弄來(lái)的)的一張圖。

幫助理解嘛

響應(yīng)式的數(shù)據(jù)我們都已經(jīng)完成了,下面我們完成一下雙向綁定。

實(shí)現(xiàn)雙向綁定

看到我們html里面有個(gè),v-module綁定了一個(gè)language,然后在Compile類里面的replace函數(shù),我們加上

replace(frag) {
    let vm = this.vm
    Array.from(frag.childNodes).forEach(node => {
        let txt = node.textContent
        let reg = /{{(.*?)}}/g
        // 判斷nodeType
+       if (node.nodeType === 1) {
            const nodeAttr = node.attributes // 屬性集合
            Array.from(nodeAttr).forEach(item => {
                let name = item.name // 屬性名
                let exp = item.value // 屬性值
                // 如果屬性有 v-
                if (name.includes("v-")){
                    node.value = vm[exp]
                    node.addEventListener("input", e => {
                        // 相當(dāng)于給this.language賦了一個(gè)新值
                        // 而值的改變會(huì)調(diào)用set,set中又會(huì)調(diào)用notify,notify中調(diào)用watcher的update方法實(shí)現(xiàn)了更新操作
                        vm[exp] = e.target.value
                    })
                }
            });
+       }
        ...
        ...
    }
  }

上面的方法就是,讓我們的input節(jié)點(diǎn)綁定一個(gè)input事件,然后當(dāng)input事件觸發(fā)的時(shí)候,改變我們的值,而值的改變會(huì)調(diào)用set,set中又會(huì)調(diào)用notifynotify中調(diào)用watcherupdate方法實(shí)現(xiàn)了更新操作。

然后我們看一下,界面

雙向數(shù)據(jù)綁定我們基本完成了,別忘了,我們上面還有個(gè)注釋掉的計(jì)算屬性。

計(jì)算屬性

先把

計(jì)算屬性:{{sum}}

注釋去掉,以為上面一開(kāi)始initVm函數(shù)里面,我們加了這個(gè)代碼return this[key] || this._data[key] || this._computed[key],到這里大家都明白了,只需要把this._computed也加一個(gè)watcher就好了。

function Mvvm(options = {}) {
    this.$options = options
    let data = this._data = this.$options.data
    let vm = initVm.call(this)
    initObserve.call(this, data)
+   initComputed.call(this) // 添加計(jì)算函數(shù),改變this指向
    new Compile(this.$options.el, vm)
    return this._vm
}


function initComputed() {
    let vm = this
    let computed = this.$options.computed // 拿到配置的computed
    vm._computed = {}
    if (!computed) return // 沒(méi)有計(jì)算直接返回
    Object.keys(computed).forEach(key => {
        // 相當(dāng)于把sum里的this指向到this._vm,然后就可以拿到this.a、this、b
        this._computed[key] = computed[key].call(this._vm)
        // 添加新的Watcher
        new Watcher(this._vm, key, val => {
            // 每次設(shè)置的時(shí)候都會(huì)計(jì)算
            this._computed[key] = computed[key].call(this._vm)
        })
    })
}

上面的initComputed 就是添加一個(gè)watcher,大致流程:

this._vm改變 ---> vm.set() ---> notify() -->update()-->更新界面

最后看看圖片

一切似乎沒(méi)什么毛病~~~~

添加mounted鉤子

添加mounted也很簡(jiǎn)單

// 寫(xiě)法和Vue一樣
let mvvm = new Mvvm({
    el: "#app",
    data: {
        ...
        ...
    },
    computed: {
        ...
        ...
    },
    mounted() {
        console.log("i am mounted", this.a)
    }
})

在new Mvvm里面添加mounted,
然后到function Mvvm里面加上

function Mvvm(options = {}) {
    this.$options = options
    let data = this._data = this.$options.data
    let vm = initVm.call(this)
    initObserve.call(this, data)
    initComputed.call(this)
    new Compile(this.$options.el, vm)
+   mounted.call(this._vm) // 加上mounted,改變指向
    return this._vm
}

// 運(yùn)行mounted
+ function mounted() {
    let mounted = this.$options.mounted
    mounted && mounted.call(this)
+ }

執(zhí)行之后會(huì)打印出

i am mounted 1

完結(jié)~~~~撒花

ps:編譯里面的,參考到這個(gè)大神的操作。@chenhongdong,謝謝大佬

最后附上,源代碼地址,直接下載運(yùn)行就可以啦。

源碼地址:https://github.com/naihe138/proxy-mvvm

預(yù)覽地址:http://gitblog.naice.me/proxy-mvvm/index.html

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/95880.html

相關(guān)文章

  • 說(shuō)說(shuō)PHP框架的MVC架構(gòu)

    摘要:前言在說(shuō)架構(gòu)之前,先說(shuō)說(shuō)框架吧。在架構(gòu)中就是這個(gè)轉(zhuǎn)接頭。當(dāng)一個(gè)新框架誕生后,關(guān)注點(diǎn)從學(xué)習(xí)這個(gè)框架,慢慢變成了這個(gè)框架是如何設(shè)計(jì)的,解決什么樣的問(wèn)題。前幾年使用過(guò)各種框架,小到,大到。 前言 在說(shuō) MVC 架構(gòu)之前,先說(shuō)說(shuō)PHP框架吧。很多很多學(xué)完P(guān)HP語(yǔ)言的人,面對(duì)的就是PHP各種各樣的框架。什么TP啊、Yii啊、CI啊,還有很流行的laravel啊等等。 他們的大部分都會(huì)說(shuō)自己是基于...

    waltr 評(píng)論0 收藏0
  • 使用 Proxy 實(shí)現(xiàn)簡(jiǎn)單MVVM 模型

    摘要:綁定實(shí)現(xiàn)的歷史綁定的基礎(chǔ)是事件。但臟檢查機(jī)制隨之帶來(lái)的就是性能問(wèn)題。是谷歌對(duì)于簡(jiǎn)化雙向綁定機(jī)制的嘗試,在中引入。掙扎了一段時(shí)間后谷歌團(tuán)隊(duì)宣布收回的提議,并在中完全刪除了實(shí)現(xiàn)。自然全軍覆沒(méi)其他各大瀏覽器實(shí)現(xiàn)的時(shí)間也較晚。 綁定實(shí)現(xiàn)的歷史 綁定的基礎(chǔ)是 propertyChange 事件。如何得知 viewModel 成員值的改變一直是開(kāi)發(fā) MVVM 框架的首要問(wèn)題。主流框架的處理有一下三...

    BetaRabbit 評(píng)論0 收藏0
  • 使用 Proxy 實(shí)現(xiàn)簡(jiǎn)單MVVM 模型

    摘要:綁定實(shí)現(xiàn)的歷史綁定的基礎(chǔ)是事件。但臟檢查機(jī)制隨之帶來(lái)的就是性能問(wèn)題。是谷歌對(duì)于簡(jiǎn)化雙向綁定機(jī)制的嘗試,在中引入。掙扎了一段時(shí)間后谷歌團(tuán)隊(duì)宣布收回的提議,并在中完全刪除了實(shí)現(xiàn)。自然全軍覆沒(méi)其他各大瀏覽器實(shí)現(xiàn)的時(shí)間也較晚。 綁定實(shí)現(xiàn)的歷史 綁定的基礎(chǔ)是 propertyChange 事件。如何得知 viewModel 成員值的改變一直是開(kāi)發(fā) MVVM 框架的首要問(wèn)題。主流框架的處理有一下三...

    MarvinZhang 評(píng)論0 收藏0
  • Windows上利用Python自動(dòng)切換代理IP的終極方案!

    摘要:在中,格式是,所以需要把格式統(tǒng)一為注冊(cè)表的標(biāo)準(zhǔn)。注冊(cè)表的二進(jìn)制值及關(guān)鍵信息如下開(kāi)關(guān)長(zhǎng)度地址是否跳過(guò)本地代理地址通過(guò)在中導(dǎo)入文件的方式執(zhí)行并立即生效。本代碼可以根據(jù)需要自動(dòng)設(shè)置代理。 聲明下:不同于網(wǎng)絡(luò)上千百篇方法,下文是經(jīng)過(guò)各種嚴(yán)格測(cè)試都通過(guò)的,同時(shí)也是一個(gè)實(shí)驗(yàn)的過(guò)程,排除了各種不靠譜的方法。有需要的可以評(píng)論來(lái)討論,想要源碼和相關(guān)參考文獻(xiàn)或筆記的,也可以找我。 思路及啟發(fā) 先說(shuō)一下我這...

    jeyhan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

fnngj

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<