日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

最近整理了下團(tuán)隊(duì)新人文檔,對(duì)團(tuán)隊(duì)內(nèi)使用的框架 riot.js 這部分內(nèi)容做了一些總結(jié)。本文主要在 riot.js 源碼 方面,分析一下 riot.js 的執(zhí)行原理和使用優(yōu)化。

Riot.js 簡(jiǎn)介

Simple and elegant component-based UI library (Riot.js)

riot.js 是一個(gè)簡(jiǎn)單優(yōu)雅的 js UI框架。具有自定義標(biāo)簽,簡(jiǎn)單語(yǔ)法,API簡(jiǎn)單,體積小, 學(xué)習(xí)成本低等特點(diǎn)。riot.js 使用Model-View-Presenter (MVP)設(shè)計(jì)模式來(lái)組織代碼, 這樣它能夠更模塊化、更具可測(cè)試性且易于理解。riot.js 僅僅提供了幫助UI渲染相關(guān)的基礎(chǔ)功能 ,并不具備其它復(fù)雜的功能,因此其體積很小,壓縮后僅有 10.39KB (react.min.js 大約 47.6KB ), 很適合組件類的業(yè)務(wù)開(kāi)發(fā)。

Hello world

嘗試 Riot.js 最簡(jiǎn)單的方法是使用 JSFiddle Hello Riot.js 例子。你可以在瀏覽器中打開(kāi)它。 或者你也可以創(chuàng)建一個(gè) .html 文件,然后通過(guò)如下方式引入Riot.js:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/riot+compiler.min.js"></scipt> 

自定義標(biāo)簽

Riot.js 采用自定義標(biāo)簽的語(yǔ)法,每個(gè)自定義標(biāo)簽都可以看做是一個(gè)組件(Riot.js Tag 對(duì)象),自定義標(biāo)簽將相關(guān)的 HTML 和 JAVAScript 粘合在一起,成為一個(gè)可重用的組件。可以認(rèn)為它同時(shí)具有 React 和 Polymer 的優(yōu)點(diǎn),但是語(yǔ)法更友好,學(xué)習(xí)成本更小。

<riot-demo> 
 <span>{ title }</span> 
 <script>
 this.title = "Hello World";
 </script>
</riot-demo> 

在團(tuán)隊(duì)中,我們會(huì)使用 webpack 來(lái)構(gòu)建 riot 項(xiàng)目。每個(gè)組件都被寫(xiě)成一個(gè) *.tag 文件。

Riot.js 基本執(zhí)行原理

一個(gè)riot自定義標(biāo)簽在日常開(kāi)發(fā)中從源碼到呈現(xiàn)在頁(yè)面上主要分為三步:編譯(一般利用官方自帶編譯工具)、注冊(cè)(riot.tag())和加載(riot.mount()),如下圖所示:

Riot.js 框架深入解析

 

編譯

編譯階段的主要工作就是將riot語(yǔ)法寫(xiě)的.tag文件轉(zhuǎn)換為可執(zhí)行的.js文件,這部分主要靠編譯器來(lái)完成。例如:

riot.tag2('content-demo', '<h1>{message}</h1>', '', function (opts) {
	this.message = 'hello world';
}); 

riot.tag2 函數(shù)在 riot.js 源碼中的 core.js 文件中,代碼如下:

export function tag2(name, tmpl, css, attrs, fn) {
 if (css) styleManager.add(css, name)
 // tags implementation cache 標(biāo)簽接口緩存
 __TAG_IMPL[name] = {
 name,
 tmpl,
 attrs,
 fn
 }
 return name
}

參數(shù)含義如下:

  • name: riot 自定義標(biāo)簽的名稱
  • tmpl: 標(biāo)簽的html內(nèi)容
  • css: 標(biāo)簽中的內(nèi)容
  • attrs: riot 自定義標(biāo)簽的屬性
  • fn: 用戶自定義函數(shù),即 標(biāo)簽中的內(nèi)容

riot.tag2() 函數(shù)將 riot tag 注冊(cè)到了 __TAG_IMP 對(duì)象中,方便之后的使用,css部分則被添加到了 byName 變量中,用于之后統(tǒng)一添加到頁(yè)面中。在源代碼中,還有一個(gè) riot.tag()函數(shù),這個(gè)函數(shù)用于直接直接創(chuàng)建一個(gè) riot tag 實(shí)例的接口,而 riot.tag2() 是暴露給編輯器的接口,本質(zhì)上功能是一樣的。

加載 riot.mount()

組件被注冊(cè)好以后,并沒(méi)有被渲染,直到我們調(diào)用 riot.mount() 函數(shù)后,相應(yīng)的組件才會(huì)渲染到頁(yè)面上。源碼如下:

export function mount(selector, tagName, opts) {
 const tags = []
 let elem, allTags
 // root {HTMLElement} riot-tag 標(biāo)簽節(jié)點(diǎn)
 function pushTagsTo(root) {
 if (root.tagName) {
 let riotTag = getAttr(root, IS_DIRECTIVE), // 要么 data-is 要么 root.tagName 本身
 tag
 // ① 設(shè)置 data-is 屬性指向
 if (tagName && riotTag !== tagName) {
 riotTag = tagName
 setAttr(root, IS_DIRECTIVE, tagName)
 }
 // ② mountTo 創(chuàng)建一個(gè)新的 riot tag 實(shí)例
 tag = mountTo(root, riotTag || root.tagName.toLowerCase(), opts)
 if (tag)
 tags.push(tag)
 } else if (root.length)
 each(root, pushTagsTo)
 }
 // DOM 注入 style 標(biāo)簽
 styleManager.inject()
 if (isObject(tagName)) {
 opts = tagName
 tagName = 0
 }
 if (isString(selector)) {
 selector = selector === '*' ?
 allTags = selectTags() :
 selector + selectTags(selector.split(/, */))
 // ③ 利用 $$ 來(lái)判斷 這些 tag 是否已經(jīng)掛載在 html 上面
 elem = selector ? $$(selector) : []
 } else
 elem = selector
 // 將所有元素掛載在根元素中
 if (tagName === '*') {
 tagName = allTags || selectTags()
 if (elem.tagName)
 // 查找elem下的 tagName
 elem = $$(tagName, elem)
 else {
 // 將查找到的所有節(jié)點(diǎn)都 放入 nodeList中
 var nodeList = []
 each(elem, _el => nodeList.push($$(tagName, _el)))
 elem = nodeList
 }
 tagName = 0
 }
 pushTagsTo(elem)
 return tags
} 

當(dāng)調(diào)用 riot.mount 后,通過(guò) selector 參數(shù)來(lái)查找 html 頁(yè)面上對(duì)應(yīng)的節(jié)點(diǎn)。不在 html 上的節(jié)點(diǎn)是不會(huì)被渲染的。③處代碼為查找過(guò)程,其中$$為 document.querySelectAll。之后調(diào)用 pushTagsTo 函數(shù)來(lái)渲染 riot tag。

IS_DIRECTIVE = 'data-is' 渲染前,要檢查是否含有 tagName 參數(shù),如果有的話即為 上述 riot.mount 的第三個(gè)用法。此時(shí)需要檢測(cè) root 的 data-is 屬性值是否和 tagName 相等,如①處。不相等則將 root 設(shè)置其 data-is 為 tagName。

取消注冊(cè) riot.unregister()

riot.unregister() 源碼十分簡(jiǎn)單,如下:

export function unregister(name) {
 __TAG_IMPL[name] = null
}

Riot.js 組件

在 Riot.js 中,每個(gè)自定義標(biāo)簽都可以看成是一個(gè)組件,每個(gè)組件其實(shí)本質(zhì)上都是一個(gè) Tag 對(duì)象, 里面包含了對(duì)象的各種屬性和方法

Tag 類

Tag 類簡(jiǎn)化源代碼如下:

// impl 包含組件的模板,邏輯等屬性 
export default function Tag(impl = {}, conf = {}, innerHTML) {
 ...各種屬性初始化
 defineProperty(this, '__', {...})
 defineProperty(this, '_riot_id', ++__uid)
 defineProperty(this, 'refs', {})
 ...
 // 定義組件更新方法
 defineProperty(this, 'update', function tagUpdate(data){...}.bind(this))
 // 定義組件 mixin 方法
 defineProperty(this, 'update', function tagMixin(data){...}.bind(this))
 // 定義組件加載方法
 defineProperty(this, 'mount', function tagMount(data){...}.bind(this))
 // 定義組件卸載方法
 defineProperty(this, 'mount', function tagUnmount(data){...}.bind(this))
}

組件的生命周期

riot 組件狀態(tài)分為以下幾個(gè)部分:

  • before-mount:標(biāo)簽被加載之前
  • mount:標(biāo)簽實(shí)例被加載到頁(yè)面上以后
  • update:允許在更新之前重新計(jì)算上下文數(shù)據(jù)
  • updated:標(biāo)簽?zāi)0甯潞?/li>
  • before-unmount:標(biāo)簽實(shí)例被卸載之前
  • unmount:標(biāo)簽實(shí)例被從頁(yè)面上卸載后
Riot.js 框架深入解析

 

riot.js 采用事件驅(qū)動(dòng)的方式來(lái)進(jìn)行通訊,我們可以采用如下函數(shù)來(lái)監(jiān)聽(tīng)上面的事件,例如處理 update 事件:

<riot-demo>
 <script>
 this.on('update', function() {
 // 標(biāo)簽更新后的處理
 })
 </script>
</riot-demo>

再談組件加載

當(dāng)我們調(diào)用 riot.mount() 渲染指定組件的時(shí)候,riot 會(huì)從 __TAG_IMPL 中獲取相對(duì)應(yīng)的已經(jīng)注冊(cè)好的模板內(nèi)容,并生成相應(yīng)的 Tag 實(shí)例對(duì)象。并且觸發(fā)其上的 Tag.mount() 函數(shù),最后將 Tag 對(duì)象緩存到 __TAGS_CACHE 中。代碼如下:

export function mountTo(root, tagName, opts, ctx) {
 var impl = __TAG_IMPL[tagName], // 獲取 html 模板
 implClass = __TAG_IMPL[tagName].class, // ?
 tag = ctx || (implClass ? Object.create(implClass.prototype) : {}),
 innerHTML = root._innerHTML = root._innerHTML || root.innerHTML
 var conf = extend({
 root: root,
 opts: opts
 }, {
 parent: opts ? opts.parent : null
 })
 if (impl && root) Tag.Apply(tag, [impl, conf, innerHTML]);
 if (tag && tag.mount) {
 tag.mount(true)
 // add this tag to the virtualDom variable
 if (!contains(__TAGS_CACHE, tag)) __TAGS_CACHE.push(tag)
 }
 return tag
}

組件加載階段,首先會(huì)整理標(biāo)簽上所有的 attribute 的內(nèi)容,區(qū)分普通屬性,和帶有表達(dá)式 expr 的屬性。

defineProperty(this, 'mount', function tagMount() {
 ...
 parseAttributes.apply(parent, [root, root.attributes, (attr, expr) => {
 // 檢測(cè) expr 是否在 RefExpr 的原型鏈中
 if (!isAnonymous && RefExpr.isPrototypeOf(expr)) expr.tag = this;
 // 掛載在 root.attributs 上面 root 為組件所在的 dom 對(duì)象
 attr.expr = expr
 instAttrs.push(attr)
 }]) 
 // impl 對(duì)象包含組件上的各種屬性,包括模板,邏輯等內(nèi)容
 implAttrs = []
 walkAttrs(impl.attrs, (k, v) => {
 implAttrs.push({
 name: k,
 value: v
 })
 })
 // 檢查的是 implAttrs
 parseAttributes.apply(this, [root, implAttrs, (attr, expr) => {
 if (expr) expressions.push(expr) //插入表達(dá)式
 else setAttr(root, attr.name, attr.value)
 }])
 ... 
}).bind(this)

初始化這些表達(dá)式內(nèi)容,然后為組件添加全局注冊(cè)的mixin 內(nèi)容。接下來(lái),會(huì)執(zhí)行我們?yōu)榻M件添加的函數(shù)內(nèi)容,此時(shí)觸發(fā) before-mount 事件。觸發(fā)完畢后,解析標(biāo)簽上的表達(dá)式,比如 if each 等內(nèi)容,然后執(zhí)行組件的 update() 函數(shù)。

在 update() 函數(shù)中,首先會(huì)檢查用戶是否定義了組件的 shouldUpdate() 函數(shù),如果有定義則傳入兩個(gè)參數(shù),第一個(gè)是想要更新的內(nèi)容(即調(diào)用this.update() 時(shí)傳入的參數(shù))。第二個(gè)為接收的父組件更新的 opts 內(nèi)容。若該函數(shù)返回值為 true 則更新渲染,否則放棄。 (這里需要注意,Tag.mount() 階段由于組件尚未處于記載完畢狀態(tài),因此不會(huì)觸發(fā) shouldUpdate() 函數(shù))。

defineProperty(this, 'update', function tagUpdate(data) {
 ...
 // shouldUpdate 返回值檢測(cè)
 if (canTrigger && this.isMounted && isFunction(this.shouldUpdate) && !this.shouldUpdate(data, nextOpts)) {
 return this
 }
 ...
 // 擴(kuò)展opts
 extend(opts, nextOpts)
 if (canTrigger) this.trigger('update', data)
 update.call(this, expressions)
 if (canTrigger) this.trigger('updated')
 return this
}).bind(this);

之后會(huì)觸發(fā) update 事件,開(kāi)始渲染新的組件。渲染完畢后觸發(fā) updated 事件。

加載完畢后,修改組件狀態(tài) defineProperty(this, 'isMounted', true)。如果渲染的組件不是作為子組件的話,我們就觸發(fā)自身的 mount 事件。否則的話,需要等到父組件加載完畢后,或者更新完畢后(已經(jīng)加載過(guò)了),再觸發(fā)。

defineProperty(this, 'mount', function tagMount() {
 ...
 defineProperty(this, 'root', root)
 defineProperty(this, 'isMounted', true)
 if (skipAnonymous) return
 // 如果不是子組件則觸發(fā)
 if (!this.parent) {
 this.trigger('mount')
 }
 // 否則需要等待父組件的狀態(tài)渲染狀態(tài)
 else {
 const p = getImmediateCustomParentTag(this.parent)
 p.one(!p.isMounted ? 'mount' : 'updated', () => {
 this.trigger('mount')
 })
 }
 return this
}).bind(this) 

當(dāng)我們調(diào)用 tag.unmount 卸載組件的時(shí)候,首先會(huì)觸發(fā) before-unmount 事件。再接下來(lái)清除所有的屬性和事件監(jiān)聽(tīng)等內(nèi)容后,觸發(fā) ‘unmount’ 事件。

組件更新原理

在 riot.js 中,想要更新組件我們必須手動(dòng)調(diào)用 tag.update() 方法才可以或者通過(guò)綁定 dom 事件觸發(fā)(通過(guò)模板綁定的事件,會(huì)在回調(diào)執(zhí)行完畢后自動(dòng)觸發(fā) tag.update ),并不能做到實(shí)時(shí)的更新處理。例如:

<riot-demo>
 <h1>{ title }</h1>
 <button click={ handleClick }>修改內(nèi)容</button>
 <script>
 this.title = "標(biāo)題"
 handleClick() {
 this.title = "新標(biāo)題";
 this.update(); // 調(diào)用 update 方法才能重新渲染組件
 }
 </script>
</riot-demo>

riot.js 并沒(méi)有提供 virtual dom 的功能,而是實(shí)現(xiàn)了一個(gè)粗粒度的 virtual dom。riot.js 為每個(gè)組件創(chuàng)建的 tag 對(duì)象中都保存一個(gè) expressions 數(shù)組,更新的時(shí)候遍歷 expressions 數(shù)組,對(duì)比舊值,如果有變化就更新DOM。這種更新機(jī)制類似angular的臟檢查,但是僅有一輪檢查(單項(xiàng)數(shù)據(jù)流)。更新處理依照模板類型來(lái)處理:

  • 文本內(nèi)容的,直接: dom.nodeValue = value
  • 值為空,而且關(guān)聯(lián)的 DOM 屬性是 checked/selected 等這種沒(méi)有屬性值的,移除對(duì)應(yīng)的屬性
  • 值為函數(shù)的,則進(jìn)行事件綁定
  • 屬性名為 if,則做條件判斷處理
  • 做了 show/hide 的語(yǔ)法糖處理
export function toggleVisibility(dom, show) {
 dom.style.display = show ? '' : 'none'
 dom['hidden'] = show ? false : true
}
  • 普通屬性的,直接設(shè)置其值

riot.js 和 react 一樣也有 props(靜態(tài),riot 中為 opts) 和本身數(shù)據(jù)(動(dòng)態(tài)),具有和 react 一樣的輸入。但是輸出的時(shí)候,由于沒(méi)有 virtual dom UI的更新并沒(méi)有集中處理,是分散的。

riot.js 采用的這種方式,代碼量上大大的減少,但是也帶來(lái)了比較嚴(yán)重的性能問(wèn)題。

更新性能問(wèn)題

首先我們來(lái)看一段 vue 代碼:

<div id="demo">
 <ul>
 <li v-for="item in items">
 {{ item.name }} --- {{ item.age }}
 </li>
 </ul>
 <button v-on:click="handleClick">更新列表項(xiàng)</button>
</div> 
var demo = new Vue({
 el: '#demo',
 data: {
 items: [
 { name: 'tgy', age: 23},
 ]
 },
 methods: {
 handleClick: function() {
 this.items = [
 { name: 'tgy', age: 23},
 { name: 'hy', age: 22},
 ]
 }
 },
 mounted: function() {
 console.log("組件掛載完畢");
 document.querySelector("li").extraType = "origin";
 },
 updated: function() {
 console.log("組件更新完畢");
 console.log(document.querySelector("li").extraType);
 }
}) 

代碼很簡(jiǎn)單,單擊按鈕,為列表添加一條新數(shù)據(jù)。在組件掛載完畢后,為第一個(gè) li 的 property 上面添加了 extraType 屬性。列表更新后,再去訪問(wèn)這個(gè) li 的 extraType 屬性。運(yùn)行結(jié)果如下:

Riot.js 框架深入解析

 

不出意料,可以正常訪問(wèn)到 li 的type屬性。這說(shuō)明了,在更新過(guò)程中,第一個(gè) li 節(jié)點(diǎn)僅僅是 textContent 發(fā)生了改變而不是重新創(chuàng)建的。這樣的結(jié)果得益于 virtual dom 算法,保證更新最小變動(dòng)。同樣的我們用 riot 來(lái)重寫(xiě)上面的代碼。

<content-demo>
 <ul>
 <li each={ items }>{ name } -- { age }</li>
 </ul>
 <button class="btn" click={ handleClick }>訂閱內(nèi)容</button> 
 <script>
 let self = this;
 this.items = [
 {"name": "tgy", age: 23}
 ];
 handleClick() {
 this.items = [
 {"name": "tgy", age: 23},
 {"name": "hy", age: 22}
 ]
 }
 this.on('mount', function() {
 console.log("組件加載完畢");
 document.querySelector("li").extraType = "origin";
 })
 this.on('updated', function() {
 console.log("組件更新完畢");
 console.log(document.querySelector("li").extraType);
 })
 </script>
</content-demo>

查看運(yùn)行結(jié)果:

Riot.js 框架深入解析

 

extraType 找不到了,所有的 li 節(jié)點(diǎn)都被重新構(gòu)建了。這里面發(fā)生了什么,查看源碼 /tag/each.js。渲染邏輯代碼如下:

export default function _each(dom, parent, expr) {
 ...
 expr.update = function updateEach() {
 ...
 each(items, function (item, i) {
 // 僅僅記錄 items 是對(duì)象的
 var
 doReorder = mustReorder && typeof item === T_OBJECT && !hasKeys,
 // 舊數(shù)據(jù)
 oldPos = oldItems.indexOf(item),
 // 是新的
 isNew = oldPos === -1,
 pos = !isNew && doReorder ? oldPos : i,
 tag = tags[pos],
 // 必須追加
 mustAppend = i >= oldItems.length,
 // 必須創(chuàng)建 isNew
 mustCreate = doReorder && isNew || !doReorder && !tag
 // 有key值得時(shí)候需要 mkitem
 item = !hasKeys && expr.key ? mkitem(expr, item, i) : item
 // 必須創(chuàng)建一個(gè)新 tag 
 if (mustCreate) {
 tag = new Tag(impl, {
 parent,
 isLoop,
 isAnonymous,
 tagName,
 root: dom.cloneNode(isAnonymous),
 item,
 index: i,
 }, dom.innerHTML)
 // mount the tag
 tag.mount()
 if (mustAppend)
 append.apply(tag, [frag || root, isVirtual])
 else
 insert.apply(tag, [root, tags[i], isVirtual])
 if (!mustAppend) oldItems.splice(i, 0, item)
 tags.splice(i, 0, tag)
 if (child) arrayishAdd(parent.tags, tagName, tag, true)
 } else if (pos !== i && doReorder) {
 // move
 // 移動(dòng)
 if (contains(items, oldItems[pos])) {
 move.apply(tag, [root, tags[i], isVirtual])
 // move the old tag instance
 tags.splice(i, 0, tags.splice(pos, 1)[0])
 // move the old item
 oldItems.splice(i, 0, oldItems.splice(pos, 1)[0])
 }
 if (expr.pos) tag[expr.pos] = i
 if (!child && tag.tags) moveNestedTags.call(tag, i)
 }
 // 緩存原始數(shù)據(jù)到節(jié)點(diǎn)上
 tag.__.item = item
 tag.__.index = i
 tag.__.parent = parent;
 // 如果不是創(chuàng)建的,我們需要更新節(jié)點(diǎn)內(nèi)容。
 if (!mustCreate) tag.update(item)
 })
 // remove the redundant tags
 // 刪除多余的標(biāo)簽
 unmountRedundant(items, tags)
 // 記錄舊的數(shù)據(jù)
 // clone the items array
 oldItems = items.slice()
 // dom 插入節(jié)點(diǎn)
 root.insertBefore(frag, placeholder)
 }
} 

這段為列表渲染邏輯,遍歷新的數(shù)據(jù)items中的每一下 item。在原始數(shù)據(jù) oldItems 中去查找(oldItems.indexOf(itemId)),是否存在 item 項(xiàng)。如果不存在,則標(biāo)記 isNews 為 true。之后走到 if 的 mustCreaete 為 true 的分支,去創(chuàng)建一個(gè)新的 tag(將 li 節(jié)點(diǎn)看成是一個(gè)tag)。以此類推,當(dāng)全部創(chuàng)建完畢后,刪除舊的節(jié)點(diǎn)(unmountRedundant(items, tags))。在斷點(diǎn)下,可以清楚看到節(jié)點(diǎn)的變化情況:

Riot.js 框架深入解析

 

優(yōu)化更新

綜上所述,riot.js 的更新邏輯僅僅是判斷新舊數(shù)據(jù)項(xiàng)是否為同一對(duì)象。為此,為了減少 DOM 的變動(dòng),降低渲染邏輯。我們修改handleClick函數(shù):

handleClick() {
 this.items.push({"name": "hy", age: 22})
} 

這樣輸出結(jié)果就會(huì)和 vue 的保持一致,并沒(méi)有創(chuàng)建新的 tag,而是利用了已經(jīng)存在的內(nèi)容。源碼中,這種情況下 isNews 為 false,從而避開(kāi)了 創(chuàng)建標(biāo)簽。而僅僅是通過(guò) tags.splice(i, 0, tags.splice(pos, 1)[0]); 來(lái)移動(dòng)位置,if (!mustCreate) { tag.update(item); } 更新節(jié)點(diǎn)內(nèi)容。

保證數(shù)據(jù)項(xiàng)對(duì)象地址不變,僅僅是修改上面的不可變對(duì)象的值,將大大的提高 riot.js 的渲染效率。

// 更新第一個(gè)li內(nèi)容 
// 不推薦寫(xiě)法,對(duì)象發(fā)生變化;
this.items[0] = {"name": "hy", age: 23}; 
// 推薦寫(xiě)法,僅僅是修改對(duì)象中的值
this.items[0].name = "hy";
this.items[0].age = 22;

保證源數(shù)據(jù)對(duì)象的不變,僅僅改變其上面的值,這樣就能減少 riot.js 渲染過(guò)程中,創(chuàng)建新的 tag 對(duì)象的開(kāi)銷。

希望本文能幫助到您!

點(diǎn)贊+轉(zhuǎn)發(fā),讓更多的人也能看到這篇內(nèi)容(收藏不點(diǎn)贊,都是耍流氓-_-)

關(guān)注 {我},享受文章首發(fā)體驗(yàn)!

每周重點(diǎn)攻克一個(gè)前端技術(shù)難點(diǎn)。更多精彩前端內(nèi)容私信 我 回復(fù)“教程”

原文鏈接:http://eux.baidu.com/blog/fe/riot-js-%E6%A1%86%E6%9E%B6%E6%B7%B1%E5%85%A5%E8%A7%A3%E6%9E%90

作者:田光宇

分享到:
標(biāo)簽:Riot js
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定