站長資訊網
        最全最豐富的資訊網站

        renderjs有什么用?聊聊uniapp中用renderjs的一些細節

        本篇文章帶大家了解一下renderjs,通過在uniapp中使用better-scroll,聊聊renderjs的一些細節,希望對大家有所幫助!

        renderjs有什么用?聊聊uniapp中用renderjs的一些細節

        包含內容:

        • 使用renderjs在app端獲取dom

        • renderjs和service層之間的通信

        • renderjs中如何接收到service層中的自定義id(重點,官方文檔沒有的)

        一、renderjs

        1.1 renderjs的概念

        • 官方文檔

        • 運行在視圖層的js,只支持app-vue和h5(簡單來說就是開了另外一條線程)

        1.2 renderjs的作用

        • 大幅降低邏輯層和視圖層的通訊損耗,提供高性能視圖交互能力(減少通訊損耗提升性能,例如一些手勢或canvas動畫的場景)

        • 在視圖層操作dom,運行for web的js庫(可以操作dom,意味著擁有window、document等這些全局變量,在app-vue的service層沒有這些)

        1.3 renderjs的使用

        • 官方文檔給出的示例 (內容較為詳細,也可以看我接下來的簡要概括)
        • 在原先的script標簽的同級新增一個script,設置lang=renderjsmodule=(值任意,相當于命名空間,之后會根據這個名字調用其中的方法)
        • 新script標簽內的結構和之前的幾乎一致,有幾點不同的需要注意:
          1. 生命周期不和uniapp相同,而是和vue相同,onLoad應該寫成原生vue的created
          2. 官方文檔好像說了renderjs中無法使用uni這個全局變量,具體哪個地方忘了。實測結果是:部分可以。例如uni.upx2px是可以用的,uni.request不可以,所以使用uni全局變量之前先輸出看一下有沒有
        • 在template中使用一開始給renderjs的命名加.的方式調用其中的方法
        <template>   <view>     <button @tap="test.handleClick">點擊</button>   </view> </template>  <script>   export default {     // 原先的script,這里被稱為service層   } </script>  <script module="test">   export default {     data() {       return {}     },     methods: {       handleClick(event, ownerInstance) {         // event是事件對象         // ownerInstance和this.$ownerInstance是一樣的,用來調用service層的方法         console.log('點擊了按鈕')       }     },     created() {       console.log('renderjs初始化完畢')     }   } </script>

        二、renderjs和service層的通信

        具體分為三部分:

        • 在template中通過用戶手動操作觸發事件

        • 在service層中調用方法

        • 在renderjs中調用方法

        從renderjs到service層:通過this.$ownerInstance.callMethod()方法可以調用service中的方法,第一個參數是方法名,第二個參數是傳過去的參數

        <template>   <view>     <button @tap="test.onClick">點擊</button>   </view> </template>  <script>   export default {     methods: {       acceptDataFromRenderjs(data) {         console.log('從renderjs中接收到的數據', data)       }     }   } </script>  <script module="test">   export default {     data() {       return {}     },     methods: {       onClick(event, ownerInstance) {         ownerInstance.callMethod('acceptDataFromRenderjs', { content: '測試文字' })         // 或this.$ownerInstance.callMethod('acceptDataFromRenderjs', { content: '測試文字' })         // 需要注意的是:只有通過在template中用戶手動操作觸發renderjs的方法參數是這兩個:event, ownerInstance;         // 通過其他方法觸發的函數參數不一樣,后面會說       }     }   } </script>

        從service層到renderjs:

        這里就需要template了,首先在template中綁定一個service中定義的值,然后在同樣的位置增加:change:(屬性名)=(觸發的方法)來實現通信。

        簡單來說就是service負責數據的更改,通過template監聽數據的變化來通知renderjs

        <template>   <view>     // prop是個名字,可以隨意改,注意:change:[name]這兩個名字需要相同就行了     <text :prop="options" :change:prop="test.onChange">無內容</text>     <button @tap="changeOptionFn">點擊修改options</button>   </view> </template>  <script>   export default {     data() {       return {         options: {           // 這里存放準備傳遞給renderjs的數據           token: null,           num: 1         }       }     },     methods: {       changeOptionFn() {         this.options = {           // 這個地方我用時間戳來修改token,用于觸發change,實際需要傳遞的數據是num           token: Date.now(),           num: Math.random()         }       }     }   } </script>  <script module="test">   export default {     methods: {       onChange(newValue, oldValue, ownerInstance, instance) {         console.log('service層中的options發生變化')         console.log('新值', newValue)         console.log('舊值', oldValue)         // ownerInstance和this.$ownerInstance一樣,可用來向service層通信         // instance和ownerInstance的區別是:         // instance.$el指向的是觸發事件的那個節點;ownerInstance.$el指向當前vue文件中的根節點;         // instance的作用目前尚不明確,官方沒有給出用法       }     }   } </script>

        在上面的例子中,prop初次綁定到節點時,事件不會觸發。

        用戶首先通過點擊按鈕觸發changeOptionFn事件,函數中修改了this.options的值。

        而在text節點監聽到綁定值發生了改變就會觸發test.onChange,從而實現service到renderjs的通信

        上面的例子中有一點需要注意:在this.options中我定義了一個token屬性,每次修改時都將最新的時間戳賦值給他,這樣就保證了我每一次的點擊都會使options發生改變。

        如果沒有這個token的話就會出現明明修改了options但是并未觸發onChange的情況。

        了解js基礎的都知道,修改options是我是直接重新賦值的,改變了索引,所以即使我num值和原先的相同,他也應該做出改變(例如vue中的watch),但是事實并不是

        所以可以推測出這個監聽數據改變監聽的是內部的屬性值,只有屬性的增刪改才能觸發回調。(如果一開始綁定的就是基礎數據類型的話,直接修改就好了)

        故,當綁定值使用對象的時候,在對象中增加一個每次都一定會變的值,即可保證事件的觸發(如上例中的token

        補充一點:進行過prop綁定的值,觸發過一次監聽事件之后,在renderjs中可以直接使用this.屬性名的方式獲取到

        例如上面的代碼中,options改變導致test.onChange觸發一次之后,在renderjs中可以直接通過this.options獲取到值

        三、renderjs中如何接收到service層中的自定義id

        3.1 在renderjs中使用better-scroll

        做過app-vue開發的話應該知道在service層中沒有document對象,無法獲取dom節點。

        所以引用一些外部js的時候,如果初始化的時候需要傳入一個選擇器的,那基本就斷定用到了document對象獲取節點。

        這時候就需要用到renderjs了,首先看一個better-scroll的示例。根據官方給出的示例做一些修改,我們可以得到以下代碼

        <template>   <view id="my-scroll">     <view><slot /></view>   </view> </template>  <script>   export default {} </script>  <script module="BScroll">   export default {     mounted() {       // 如果這個插件支持ESModule的話就不用這么寫,直接import導入就好了       if (typeof window.BScroll == 'function') return this.initBScroll()       const script = document.createElement('script')       script.src = 'static/better-scroll.core.min.js'       script.onload = this.initBScroll       document.head.appendChild(script)     },     methods: {       initBScroll() {         this.bs = new BScroll(document.querySelector('#my-scroll'))       }     }   } </script>

        3.2 better-scroll自定義id

        重點來了,上面的例子中雖然實現了效果,但是也出現了一個問題:id是固定的。

        如果我在同一頁面中多次使用該組件,就會導致出現多個重復id,導致無法預料的錯誤。

        在官方給出的示例中,包括我研究過的插件市場中的很多項目,都是使用固定的id。

        解決的方法就是由外部傳入自定義id或是由內部生成隨機id。那么應該如何在renderjs中如何接收到service層中的自定義id呢

        下面我給出的方法算是我自己測試過最有效的方法了,直接看代碼

        <template>   <view :id="bsId" :prop="bsId" :change:prop="BScroll.initBScroll">     <view><slot /></view>   </view> </template>  <script>   export default {     props: {       bsId: {         type: String,         default: 'bs-container'       }     }   } </script>  <script module="BScroll">   export default {     mounted() {       if (typeof window.BScroll == 'function') return this.initBScroll()       const script = document.createElement('script')       script.src = 'static/better-scroll.core.min.js'       script.onload = this.initBScroll       document.head.appendChild(script)     },     methods: {       initBScroll() {         this.bs?.destroy()         this.bs = new BScroll(document.querySelector(`#${this.$ownerInstance.$vm.bsId}`))       }     }   } </script>

        在父級中傳入自定義的bsId,組件接收到之后將其作為元素id。

        執行順序和之前一樣:在renderjs的mounted中加載外部js,加載完成后進行初始化操作,通過this.$ownerInstance.$vm.bsId獲取到service層中的bsId完成操作

        同時,bsId也綁定了prop,監聽到改變時會重新進行初始化操作,所以在初始化的方法第一行加入了this.bs?.destroy(),如果實例已存在就先銷毀。

        還記得一開始就說過的renderjs只支持app-vue和h5嗎,這里主要說的是app端,因為如果是h5端的話,是可以在service中直接使用document的,壓根不用這么麻煩。

        這里還有一點需要注意的::prop="bsId" :change:prop="BScroll.initBScroll"

        實測,如果不寫這行代碼,也就是不進行綁定prop的操作的話,是無法獲取到this.$ownerInstance.$vm.bsId。(app端是這樣,h5端不寫這個也可以,但是h5端壓根也用不著這種方法)

        推薦:《uniapp教程》

        贊(3)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 久久国产精品波多野结衣AV | 国产亚洲欧美精品永久| 亚洲精品无码不卡| 国产精品无码日韩欧| 中文字幕无码精品三级在线电影 | 久久99久久99小草精品免视看 | 亚洲七七久久精品中文国产 | 国产福利精品视频自拍 | 无码精品视频一区二区三区 | 久久久久亚洲精品天堂久久久久久 | 亚洲国产精品一区二区第一页免 | 国产亚洲婷婷香蕉久久精品| 欧美精品hdvideosex4k| 一区二区日韩国产精品| 欧美国产成人久久精品| 国产小呦泬泬99精品| 99R在线精品视频在线播放| 国产一区二区精品久久| 国产精品久久久久AV福利动漫| 亚洲AV无码成人精品区蜜桃| 亚欧洲精品在线视频免费观看 | 亚洲精品国精品久久99热 | 青青热久久国产久精品| 国产这里有精品| 国产精品热久久毛片| 99爱在线视频这里只有精品| 中文字幕精品一区影音先锋| 久久久精品一区二区三区| 国产香蕉精品视频在| 国产精品亚洲午夜一区二区三区| 国产suv精品一区二区33| 精品久久久久中文字幕日本| 色欲久久久天天天综合网精品| 亚洲无线观看国产精品| 亚洲AV午夜福利精品一区二区| 欧美精品综合视频一区二区| 日韩精品欧美| 在线精品亚洲一区二区三区| 中文字幕日本精品一区二区三区| 在线观看亚洲精品国产| 十八18禁国产精品www|