一個(gè)簡(jiǎn)單的vue雙向綁定實(shí)現(xiàn)案例

最近面試經(jīng)常會(huì)問這個(gè)問題,你知道vue的雙向綁定是通過什么實(shí)現(xiàn)的嗎?了解的同學(xué)應(yīng)該知道vue是使用Object.defineProperty屬性绩郎,重寫data的get和set方法來實(shí)現(xiàn)的逛薇。先引用網(wǎng)上的一張圖,那么接下來我們就按照這張圖的步驟去用代碼實(shí)現(xiàn)這個(gè)功能浦夷。


image

先看DOM結(jié)構(gòu)部分,結(jié)構(gòu)部分很簡(jiǎn)單

<div id="app">
    <input type="text" v-model="number">
    <input type="button" value="增加" v-click="increment"/>
    <input type="button" value="減少" v-click="subtract">
    <h3 v-bind="number"></h3>
</div>

然后就是js部分了辜王,按照上圖的步驟我們定義一個(gè)構(gòu)造函數(shù)劈狐,并且init這個(gè)構(gòu)造函數(shù)

    //初始化構(gòu)造函數(shù)
    function Vm(options) {
        this._init(options);
    }

    Vm.prototype._init = function (options) {
        this.$options = options;    //options為上面使用時(shí)傳入的結(jié)構(gòu)體,包括el呐馆、data肥缔、methods
        this.$el = document.querySelector(options.el);     //el是#app,this.$el是id為app的Element元素
        this.$data = options.data;      //this.$data = {number: 0 ...}
        this.$methods = options.methods;        //this.$methods = {increment: function(){} ...}

        this._binding = {};     //_binding保存著model與view的映射關(guān)系汹来,也就是我們前面定義的Watcher的實(shí)例续膳。當(dāng)model改變時(shí),會(huì)觸發(fā)其中的指令類更新收班,保證view也能實(shí)時(shí)更新
    }

這個(gè)時(shí)候我們還需要給實(shí)例添加一個(gè)observer坟岔、compile方法,observer方法實(shí)現(xiàn)對(duì)數(shù)據(jù)的劫持摔桦,compile方法負(fù)責(zé)編譯解析指令并且初始化視圖

    //實(shí)現(xiàn)_observer函數(shù)社付,對(duì)data進(jìn)行處理,重寫data的set和get函數(shù)
    Vm.prototype._observer = function (data) {
        //如果data數(shù)據(jù)為空
        if (!data) {
            return;
        }
        if (typeof data !== "object") {
            throw new Error("data必須是一個(gè)對(duì)象");
        }

        var self = this;
        /*
        * 遍歷data中所有屬性
        * Object.keys(obj)函數(shù)返回一個(gè)由一個(gè)給定對(duì)象的自身可枚舉屬性組成的數(shù)組
        * */
        Object.keys(data).forEach(function (key) {
            //當(dāng)前屬性的值
            var oldValue = data[key];

            /*
            *   按照前面的數(shù)據(jù)
            *   _binding = {
            *       number:{
            *           _directives: [...watch實(shí)例]
            *       }
            *   }
            *   這里是先聲明一個(gè)空數(shù)組邻耕,以后所有和data中某值有關(guān)系的都會(huì)追加到相應(yīng)的_directives中鸥咖。
            *   為何要在這里聲明?
            *   因?yàn)檫@里要根據(jù)_binding的key必須為data中的屬性(鍵)
            *   那這個(gè)數(shù)組何時(shí)才有東西呢兄世?
            *   解析指令(比如v-bind)的時(shí)候push進(jìn)去的啼辣,因?yàn)榻馕鲋噶畹臅r(shí)候,會(huì)解析每一個(gè)dom,然后解析出你的是點(diǎn)擊事件還是修改文本內(nèi)容御滩。
            *   然后在生成watcher追加進(jìn)去這個(gè)數(shù)組-以后所有和data中某值發(fā)生改變只要循環(huán)執(zhí)行這個(gè)數(shù)組中watcher的update更新方法就可以更新所有和這個(gè)data中的某值相關(guān)聯(lián)的dom
            * */
            self._binding[key] = {
                _directives: []
            }
            //獲取本data某屬性對(duì)應(yīng)的_directives
            var binding = self._binding[key];

            /**
             * 語法:Object.defineProperty(obj, key, descriptor)
             *      @param: obj:需要定義屬性的對(duì)象鸥拧;
             *              key:需要定義或修改的屬性党远;
             *              descriptor:將被定義或修改屬性的描述符
             */
            Object.defineProperty(data, key, {    //實(shí)現(xiàn)雙向綁定的關(guān)鍵代碼
                enumerable: true, // 可枚舉--可被for-in和Object.keys()枚舉。
                configurable: true, //當(dāng)且僅當(dāng)值為true時(shí)住涉,該屬性描述符才能夠被改變麸锉,也能被刪除
                //value: undefined,   //該屬性對(duì)應(yīng)的值∮呱可以是任何有效的 JavaScript 值(數(shù)值花沉,對(duì)象,函數(shù)等)媳握。默認(rèn)為 undefined
                //writable: false,    //當(dāng)且僅當(dāng)該屬性的writable為true時(shí)碱屁,value才能被賦值運(yùn)算符改變。默認(rèn)為 false
                get: function () {  //一個(gè)給屬性提供getter的方法蛾找,當(dāng)訪問該屬性時(shí)方法會(huì)被執(zhí)行娩脾,執(zhí)行時(shí)沒有參數(shù)傳入,但會(huì)傳入this對(duì)象
                    //發(fā)現(xiàn)這個(gè)oldValue如果替換成data[key]會(huì)造成堆棧溢出打毛。
                    return oldValue;
                },
                set: function (newValue) {  //一個(gè)給屬性提供setter的方法柿赊,當(dāng)屬性值修改時(shí)觸發(fā)該方法,該方法將接受唯一參數(shù)幻枉,即該屬性新的參數(shù)值碰声。
                    if (oldValue == newValue) return;
                    console.log("監(jiān)聽到值變化了");
                    //發(fā)現(xiàn)這個(gè)oldValue如果替換成data[key]會(huì)造成堆棧溢出。
                    oldValue = newValue;
                    // 當(dāng)data中某屬性改變時(shí)熬甫,觸發(fā)_binding['某值']._directives 中的綁定的Watcher類的更新--這樣實(shí)現(xiàn)一個(gè)data中的值發(fā)生改變胰挑,和它相關(guān)聯(lián)的dom更新。
                    binding._directives.forEach(function (item) {
                        item.update();
                    })
                }
            });
        })
    }

    //定義complie函數(shù)椿肩,用來解析指令(v-bind,v-model,v-click)等瞻颂,并在這個(gè)過程中對(duì)view與model進(jìn)行綁定。
    Vm.prototype._complie = function (root) {    //root為id為app的ELement元素郑象,也就是vue的根元素
        var _this = this;
        var nodes = root.children;
        for (var i = 0; i < nodes.length; i++) {    //對(duì)所有的元素進(jìn)行遍歷贡这,并處理
            var node = nodes[i];
            if (node.children.length) {
                this._complie(node);
            }

            if (node.hasAttribute("v-click")) { //如果有v-click屬性,我們監(jiān)聽onclick事件厂榛,觸發(fā)increment盖矫、subtract方法
                node.onclick = (function () {
                    var attrVal = nodes[i].getAttribute("v-click");
                    return _this.$methods[attrVal].bind(_this.$data); //bind是使data的作用域與method函數(shù)的作用域保持一致
                })();
            }

            if (node.hasAttribute("v-model") && (node.tagName == "INPUT" || node.tagName == "TEXTAREA")) {//如果有v-model屬性,并且元素為input或者textarea噪沙,我們監(jiān)聽它的input事件
                node.addEventListener("input", (function (key) {
                    var attrVal = node.getAttribute("v-model");
                    /**
                     * _this._binding["number"]._directives = [一個(gè)Watcher實(shí)例]
                     * 其中Watcher.prototype.update = function() {
                     *     node["value"] = _this.$data["number"]; 這就將node的值保持與number一致
                     * }
                     */
                    _this._binding[attrVal]._directives.push(new Watcher(
                        "input",
                        node,
                        _this,
                        attrVal,
                        "value",
                    ))

                    return function () {
                        _this.$data[attrVal] = nodes[key].value; //使number的值與node的value保持一致炼彪,實(shí)現(xiàn)雙向綁定
                    }
                })(i));
            }

            if (node.hasAttribute("v-bind")) {  //如果有v-bind屬性吐根,只要使node的值及時(shí)更新為data中number的值即可
                var attrVal = node.getAttribute("v-bind");
                _this._binding[attrVal]._directives.push(new Watcher(
                    'text',
                    node,
                    _this,
                    attrVal,
                    "innerHTML"
                ))
            }
        }
    }

方法定義完了當(dāng)然得調(diào)用正歼,回到init方法

Vm.prototype._init = function (options) {
        this.$options = options;    //options為上面使用時(shí)傳入的結(jié)構(gòu)體,包括el拷橘、data局义、methods
        this.$el = document.querySelector(options.el);     //el是#app喜爷,this.$el是id為app的Element元素
        this.$data = options.data;      //this.$data = {number: 0}
        this.$methods = options.methods;        //this.$methods = {increment: function(){}}

        this._binding = {};     //_binding保存著model與view的映射關(guān)系,也就是我們前面定義的Watcher的實(shí)例萄唇。當(dāng)model改變時(shí)檩帐,會(huì)觸發(fā)其中的指令類更新,保證view也能實(shí)時(shí)更新
        this._observer(this.$data);
        this._complie(this.$el);
    }

接下來實(shí)現(xiàn)一個(gè)Watcher另萤,用來綁定update方法湃密,實(shí)現(xiàn)對(duì)DOM的更新

    //實(shí)現(xiàn)一個(gè)指令類Watcher,用來綁定更新函數(shù)四敞,實(shí)現(xiàn)對(duì)DOM元素的更新
    function Watcher(name, el, vm, exp, attr) {
        this.name = name;       //指令名稱泛源,例如文本節(jié)點(diǎn),該值設(shè)置為"text"
        this.el = el;           //指令對(duì)應(yīng)的DOM元素
        this.vm = vm;           //指令所屬的實(shí)例
        this.exp = exp;         //指令對(duì)應(yīng)的值忿危,本例為:"number"
        this.attr = attr;       //指令綁定的屬性值达箍,本例為:"innerHTML"

        this.update();
    }

    Watcher.prototype.update = function () {
        this.el[this.attr] = this.vm.$data[this.exp]; //比如H3.innerHtml = this.data.number;當(dāng)number改變時(shí)會(huì)觸發(fā)update函數(shù),保證對(duì)應(yīng)的DOM內(nèi)容進(jìn)行更新
    }

調(diào)用也很簡(jiǎn)單铺厨,也是vue最熟悉的調(diào)用方式

window.onload = function () {
        var vm = new Vm({
            el: "#app",
            data: {
                number: 0,
                age: 18
            },
            methods: {
                increment: function () {
                    this.number++;
                },

                subtract: function () {
                    this.number--;
                }
            }
        })
    }

完整代碼如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <input type="text" v-model="number">
    <input type="button" value="增加" v-click="increment"/>
    <input type="button" value="減少" v-click="subtract">
    <h3 v-bind="number"></h3>
</div>
</body>
</html>
<script>
    //初始化構(gòu)造函數(shù)
    function Vm(options) {
        this._init(options);
    }

    Vm.prototype._init = function (options) {
        this.$options = options;    //options為上面使用時(shí)傳入的結(jié)構(gòu)體缎玫,包括el、data解滓、methods
        this.$el = document.querySelector(options.el);     //el是#app赃磨,this.$el是id為app的Element元素
        this.$data = options.data;      //this.$data = {number: 0}
        this.$methods = options.methods;        //this.$methods = {increment: function(){}}

        this._binding = {};     //_binding保存著model與view的映射關(guān)系,也就是我們前面定義的Watcher的實(shí)例伐蒂。當(dāng)model改變時(shí)煞躬,會(huì)觸發(fā)其中的指令類更新,保證view也能實(shí)時(shí)更新
        this._observer(this.$data);
        this._complie(this.$el);
    }

    //實(shí)現(xiàn)_observer函數(shù)逸邦,對(duì)data進(jìn)行處理恩沛,重寫data的set和get函數(shù)
    Vm.prototype._observer = function (data) {
        //如果data數(shù)據(jù)為空
        if (!data) {
            return;
        }
        if (typeof data !== "object") {
            throw new Error("data必須是一個(gè)對(duì)象");
        }

        var self = this;
        /*
        * 遍歷data中所有屬性
        * Object.keys(obj)函數(shù)返回一個(gè)由一個(gè)給定對(duì)象的自身可枚舉屬性組成的數(shù)組
        * */
        Object.keys(data).forEach(function (key) {
            //當(dāng)前屬性的值
            var oldValue = data[key];

            /*
            *   按照前面的數(shù)據(jù)
            *   _binding = {
            *       number:{
            *           _directives: [...watch實(shí)例]
            *       }
            *   }
            *   這里是先聲明一個(gè)空數(shù)組,以后所有和data中某值有關(guān)系的都會(huì)追加到相應(yīng)的_directives中缕减。
            *   為何要在這里聲明雷客?
            *   因?yàn)檫@里要根據(jù)_binding的key必須為data中的屬性(鍵)
            *   那這個(gè)數(shù)組何時(shí)才有東西呢?
            *   解析指令(比如v-bind)的時(shí)候push進(jìn)去的桥狡,因?yàn)榻馕鲋噶畹臅r(shí)候搅裙,會(huì)解析每一個(gè)dom,然后解析出你的是點(diǎn)擊事件還是修改文本內(nèi)容。
            *   然后在生成watcher追加進(jìn)去這個(gè)數(shù)組-以后所有和data中某值發(fā)生改變只要循環(huán)執(zhí)行這個(gè)數(shù)組中watcher的update更新方法就可以更新所有和這個(gè)data中的某值相關(guān)聯(lián)的dom
            * */
            self._binding[key] = {
                _directives: []
            }
            //獲取本data某屬性對(duì)應(yīng)的_directives
            var binding = self._binding[key];

            /**
             * 語法:Object.defineProperty(obj, key, descriptor)
             *      @param: obj:需要定義屬性的對(duì)象裹芝;
             *              key:需要定義或修改的屬性部逮;
             *              descriptor:將被定義或修改屬性的描述符
             */
            Object.defineProperty(data, key, {    //實(shí)現(xiàn)雙向綁定的關(guān)鍵代碼
                enumerable: true, // 可枚舉--可被for-in和Object.keys()枚舉。
                configurable: true, //當(dāng)且僅當(dāng)值為true時(shí)嫂易,該屬性描述符才能夠被改變兄朋,也能被刪除
                //value: undefined,   //該屬性對(duì)應(yīng)的值×担可以是任何有效的 JavaScript 值(數(shù)值颅和,對(duì)象傅事,函數(shù)等)。默認(rèn)為 undefined
                //writable: false,    //當(dāng)且僅當(dāng)該屬性的writable為true時(shí)峡扩,value才能被賦值運(yùn)算符改變蹭越。默認(rèn)為 false
                get: function () {  //一個(gè)給屬性提供getter的方法,當(dāng)訪問該屬性時(shí)方法會(huì)被執(zhí)行教届,執(zhí)行時(shí)沒有參數(shù)傳入响鹃,但會(huì)傳入this對(duì)象
                    //發(fā)現(xiàn)這個(gè)oldValue如果替換成data[key]會(huì)造成堆棧溢出。
                    return oldValue;
                },
                set: function (newValue) {  //一個(gè)給屬性提供setter的方法案训,當(dāng)屬性值修改時(shí)觸發(fā)該方法茴迁,該方法將接受唯一參數(shù),即該屬性新的參數(shù)值萤衰。
                    if (oldValue == newValue) return;
                    console.log("監(jiān)聽到值變化了");
                    //發(fā)現(xiàn)這個(gè)oldValue如果替換成data[key]會(huì)造成堆棧溢出堕义。
                    oldValue = newValue;
                    // 當(dāng)data中某屬性改變時(shí),觸發(fā)_binding['某值']._directives 中的綁定的Watcher類的更新--這樣實(shí)現(xiàn)一個(gè)data中的值發(fā)生改變脆栋,和它相關(guān)聯(lián)的dom更新倦卖。
                    binding._directives.forEach(function (item) {
                        item.update();
                    })
                }
            });
        })
    }

    //定義complie函數(shù),用來解析指令(v-bind,v-model,v-click)等椿争,并在這個(gè)過程中對(duì)view與model進(jìn)行綁定怕膛。
    Vm.prototype._complie = function (root) {    //root為id為app的ELement元素,也就是vue的根元素
        var _this = this;
        var nodes = root.children;
        for (var i = 0; i < nodes.length; i++) {    //對(duì)所有的元素進(jìn)行遍歷秦踪,并處理
            var node = nodes[i];
            if (node.children.length) {
                this._complie(node);
            }

            if (node.hasAttribute("v-click")) { //如果有v-click屬性褐捻,我們監(jiān)聽onclick事件,觸發(fā)increment椅邓、subtract方法
                node.onclick = (function () {
                    var attrVal = nodes[i].getAttribute("v-click");
                    return _this.$methods[attrVal].bind(_this.$data); //bind是使data的作用域與method函數(shù)的作用域保持一致
                })();
            }

            if (node.hasAttribute("v-model") && (node.tagName == "INPUT" || node.tagName == "TEXTAREA")) {//如果有v-model屬性柠逞,并且元素為input或者textarea,我們監(jiān)聽它的input事件
                node.addEventListener("input", (function (key) {
                    var attrVal = node.getAttribute("v-model");
                    /**
                     * _this._binding["number"]._directives = [一個(gè)Watcher實(shí)例]
                     * 其中Watcher.prototype.update = function() {
                     *     node["value"] = _this.$data["number"]; 這就將node的值保持與number一致
                     * }
                     */
                    _this._binding[attrVal]._directives.push(new Watcher(
                        "input",
                        node,
                        _this,
                        attrVal,
                        "value",
                    ))

                    return function () {
                        _this.$data[attrVal] = nodes[key].value; //使number的值與node的value保持一致景馁,實(shí)現(xiàn)雙向綁定
                    }
                })(i));
            }

            if (node.hasAttribute("v-bind")) {  //如果有v-bind屬性板壮,只要使node的值及時(shí)更新為data中number的值即可
                var attrVal = node.getAttribute("v-bind");
                _this._binding[attrVal]._directives.push(new Watcher(
                    'text',
                    node,
                    _this,
                    attrVal,
                    "innerHTML"
                ))
            }
        }
    }

    //實(shí)現(xiàn)一個(gè)指令類Watcher,用來綁定更新函數(shù)合住,實(shí)現(xiàn)對(duì)DOM元素的更新
    function Watcher(name, el, vm, exp, attr) {
        this.name = name;       //指令名稱绰精,例如文本節(jié)點(diǎn),該值設(shè)置為"text"
        this.el = el;           //指令對(duì)應(yīng)的DOM元素
        this.vm = vm;           //指令所屬的實(shí)例
        this.exp = exp;         //指令對(duì)應(yīng)的值透葛,本例為:"number"
        this.attr = attr;       //指令綁定的屬性值笨使,本例為:"innerHTML"

        this.update();
    }

    Watcher.prototype.update = function () {
        this.el[this.attr] = this.vm.$data[this.exp]; //比如H3.innerHtml = this.data.number;當(dāng)number改變時(shí)會(huì)觸發(fā)update函數(shù),保證對(duì)應(yīng)的DOM內(nèi)容進(jìn)行更新
    }

    window.onload = function () {
        var vm = new Vm({
            el: "#app",
            data: {
                number: 0,
                age: 18
            },
            methods: {
                increment: function () {
                    this.number++;
                },

                subtract: function () {
                    this.number--;
                }
            }
        })
    }

</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末僚害,一起剝皮案震驚了整個(gè)濱河市硫椰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖最爬,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異门岔,居然都是意外死亡爱致,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門寒随,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糠悯,“玉大人,你說我怎么就攤上這事妻往』グ” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵讯泣,是天一觀的道長(zhǎng)纫普。 經(jīng)常有香客問我,道長(zhǎng)好渠,這世上最難降的妖魔是什么昨稼? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮拳锚,結(jié)果婚禮上假栓,老公的妹妹穿的比我還像新娘。我一直安慰自己霍掺,他們只是感情好匾荆,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杆烁,像睡著了一般牙丽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兔魂,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天剩岳,我揣著相機(jī)與錄音,去河邊找鬼入热。 笑死拍棕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的勺良。 我是一名探鬼主播绰播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼尚困!你這毒婦竟也來了蠢箩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谬泌,沒想到半個(gè)月后滔韵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掌实,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年陪蜻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贱鼻。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宴卖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出邻悬,到底是詐尸還是另有隱情症昏,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布父丰,位于F島的核電站肝谭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蛾扇。R本人自食惡果不足惜分苇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望屁桑。 院中可真熱鬧医寿,春花似錦、人聲如沸蘑斧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽竖瘾。三九已至沟突,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捕传,已是汗流浹背惠拭。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庸论,地道東北人职辅。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像聂示,于是被迫代替她去往敵國和親域携。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • vue理解淺談 一 理解vue的核心理念 使用vue會(huì)讓人感到身心愉悅,它同時(shí)具備angular和react的優(yōu)點(diǎn)...
    ambeer閱讀 24,143評(píng)論 2 18
  • VUE介紹 Vue的特點(diǎn)構(gòu)建用戶界面鱼喉,只關(guān)注View層簡(jiǎn)單易學(xué)秀鞭,簡(jiǎn)潔趋观、輕量、快速漸進(jìn)式框架 框架VS庫庫锋边,是一封裝...
    多多醬_DuoDuo_閱讀 2,719評(píng)論 1 17
  • 買了2卷描紅皱坛,一個(gè)太大,一個(gè)太小豆巨。寫不下去剩辟。還是規(guī)規(guī)矩矩練字吧。
    水石軒主人閱讀 197評(píng)論 2 0
  • 最近接了一個(gè)給知名運(yùn)動(dòng)品牌做健康管理的工作刻肄,雖然未必是有型的產(chǎn)品瓤球,但在整個(gè)提供服務(wù)的過程中,確實(shí)有很多轉(zhuǎn)化的思考敏弃。...
    Sybil盛閱讀 258評(píng)論 1 0
  • 意大利著名作家翁貝托·柏韵郏科在他的《論文學(xué)的幾項(xiàng)功能》中曾說:“我們閱讀偉大的悲劇杰作時(shí),一種摻雜苦痛的訝異油然而生...
    每日愛圖閱讀 235評(píng)論 0 0