基於vue2.0的weex實踐(前端視角)

19年目標:消滅英語!我新開了一個公衆號記錄一個程序員學英語的歷程css

有提高英語訴求的小夥伴能夠關注公衆號:csenglish 程序員學英語,天天花10分鐘交做業,跟我一塊兒學英語吧html


前提:這段時間將公司的幾個用we寫的weex頁面用vue2.0進行了重構,客戶端的weexsdk也更新到了0.10.0版本。這篇文章將會在前端視角寫到一些新老版本之間有差別的地方。we老版本的實踐文章前端

weex-vue-render v 0.10.0vue

ios weexSDK 0.10.0html5

android weexSDK 0.10.0android

編譯vue文件

在we時代,對於業務代碼text.we文件咱們藉助weex-loader以及webpack能夠輕易的進行編譯,獲得最後須要的js。可是在vue時代,咱們須要兩個loader,weex-loadervue-loader,對於公司接入來講咱們不太會使用官方提供的腳手架工具,通常都是本身實現。這裏容易產生混淆的是還有一個loader叫作weex-vue-loader,這個loader通常狀況下對於開發者而言不須要手動調用。webpack

在build階段或者在dev階段,咱們會使用weex-loadervue-loader兩個loader。ios

// native端使用的js

weexWebpackConfig.module.loaders.push({
  test: /\.vue(\?[^?]+)?$/,
  loader: require.resolve('weex-loader')
})
weexWebpackConfig.output.filename = '[name].weex.min.js'
複製代碼
// h5端使用的js

vueWebpackConfig.module.loaders.push({
  test: /\.vue(\?[^?]+)?$/,
  loader: require.resolve('vue-loader')
})
vueWebpackConfig.output.filename = '[name].min.js'
複製代碼

對於native端咱們使用weex-loader進行編譯vue文件,在weex-loader內部若是檢測到該文件是vue則調用weex-vue-loader。對於H5端咱們則使用vue官方提供的vue-loader進行vue的編譯,同時咱們對編譯完成的文件名作區別,方便識別。git

對於你們本身實現腳手架很容易形成問題的一點是咱們須要加入一段註釋,讓weexsdk識別這是vue。程序員

var bannerPlugin = new webpackG.BannerPlugin(
    '// { "framework": "Vue" }\n',
    { raw: true }
  )
webpackConfig.plugins.push(bannerPlugin);
複製代碼

來源:官方文檔

源碼依賴管理

咱們的業務是多頁形式的,因此接入weex也很是方便。在每一個頁面引入該頁面的由vue編譯完後js文件。vueweex-vue-render做爲公共資源咱們統一引入,由於咱們須要結合native方定製一些錯誤收集等module,因此咱們選擇將vue、weex-vue-render以及定製化的module、component共同做爲源碼管理,統一打包成一個公共的文件在H5端引用。

we時代咱們也是這麼作的,詳見。vue時代在大致方向不變,在入口文件處作了微小的調整。

import Vue from 'vue'
window.Vue = Vue

/**
 * 註冊component
 * 
 */

// import TestComponent from './components/test/test.vue'
// Vue.component('test-component', TestComponent)


/**
 * 註冊module
 * 
 */
require('weex-vue-render')

// shopBase
import shopBase from './modules/shop-base'
window.weex.registerModule('shopBase', shopBase)

// shopModal
import shopModal from './modules/shop-modal'
window.weex.registerModule('shopModal', shopModal)
複製代碼

咱們能夠看到相比之前component和module都由weex進行註冊,而如今vue時代,咱們的component將由Vue進行註冊,須要注意的是這裏我將Vue掛載到了window對象下,由於在各自頁面的業務代碼中,咱們的入口js中須要實例化Vue,須要用到Vue對象。

以及在註冊module的時候咱們的weex也是取自window對象下,require('weex-vue-render')這個步驟中會將weex掛載在window之下。

具體的component編寫如今已經完成變成了vue文件的編寫,有vue經驗的同窗1秒上手。

具體的module編寫相比較之前也簡化了不少,export一個包含module方法的對象,再調用weex.registerModule進行註冊便可。值得注意的是在新官方文檔上對html5的拓展部分沒有講到如何書寫回調,回調的規則跟舊版仍是同樣的,下面的代碼片斷就是例子

const shopBase = {
  isOnline: function (callbackId) {
    const sender = this.sender;
    let hostname = window.location.hostname;
    let result = false;
    if (hostname.indexOf('showjoy.com') !== -1) {
      result = true;
    }
    sender.performCallback(callbackId, {
      data: result
    })
  }

}
export default shopBase


// another js

window.weex.registerModule('shopBase', shopBase)
複製代碼

viewport的轉變

這部份內容原理解釋的有些繞,對viewport適配不瞭解的同窗能夠先看些我以前的文章viewport-and-flexibleJs

開發過we頁面的同窗應該瞭解,在以前的weex-hmtl5版本的weex中應該是沒有對多屏幕視口適配作處理的,框架把視口適配的工做交給了手淘的flexible.js進行基於rem的視口適配。咱們在H5端寫頁面的時候須要引入一段flexible.js腳本。

基於vue2.0版本的weex捨棄了flexible.jsrem適配方案,改爲了750px固定視口的方案。咱們會看到無論在什麼尺寸的手機屏幕上,layout viewport的值都爲750px,因此我編寫css代碼的時候是無異的,都是以750爲滿屏寬進行代碼的編寫,區別僅在於如今要寫度量單位px,而we時代是不須要的。

我在重構we頁面的時候經歷了weex-vue-render兩個版本的迭代,一個是v-0.2.0還有一個就是v-0.10.0

這兩個版本對viewport的處理也作了修改,咱們在編寫業務代碼的時候也受到了一些由版本迭代帶來的影響。

weex-vue-render v-0.2.0版本以及weex-html5老weex版本時咱們在html文件中是能夠不須要寫meta[name=viewport]的,框架會自動幫咱們計算獲得頁面須要的viewport信息。

weex-html5配合使用flexible適配多屏幕,這無需多言了。

weex-vue-render v-0.2.0的適配方式以下代碼所示:

// 本文做者註釋
// 此處默認值是750,由build時注入
const DEFAULT_VIEWPORT_WIDTH = process.env.VIEWPORT_WIDTH

function parseViewportWidth (config) {
  let width = DEFAULT_VIEWPORT_WIDTH
  if (config && config.width) {
    width = Number(config.width) || config.width
  }
  return width
}

export function setViewport (config = {}) {
  const doc = window.document

  if (doc) {
    const viewportWidth = parseViewportWidth(config)

    // set root font-size
    doc.documentElement.style.fontSize = viewportWidth / 10 + 'px'
  
  // 本文做者註釋 
  // 重點語句 獲得當前手機屏幕的寬度
    const screenWidth = window.screen.width 
    const scale = screenWidth / viewportWidth
    const contents = [
      `width=${viewportWidth}`,
      `initial-scale=${scale}`,
      `maximum-scale=${scale}`,
      `minimum-scale=${scale}`,
      `user-scalable=no`
    ]

    let meta = doc.querySelector('meta[name="viewport"]')
    if (!meta) {
      meta = doc.createElement('meta')
      meta.setAttribute('name', 'viewport')
      document.querySelector('head').appendChild(meta)
    }

    meta.setAttribute('content', contents.join(','))
  }
}
複製代碼
const screenWidth = window.screen.width 
複製代碼

這條語句是這個適配腳本的重點之一,本意是爲了拿到當前手機屏幕的ideal viewport理想視口,多數狀況下window.screen.width是能夠拿到屏幕的ideal viewport的,可是少部分安卓原生瀏覽器取到的值是有問題的。在正常狀況iphone6下,這段腳本獲得的值爲:

`width=750`,
`initial-scale=0.5`,
`maximum-scale=0.5`,
`minimum-scale=0.5`,
`user-scalable=no`
複製代碼

可是在部分安卓手機下的瀏覽器會獲得錯誤screenWidth從而獲得錯誤的initial-scale,那麼屏幕顯示會出現問題。因此weex-vue-render v-0.10.0版本修改了這個腳本的實現,可是代價是咱們必需要在html文件中添加一段meta[name=viewport]標籤。

標籤的形式是:<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

須要添加這個標籤沒有在更新日誌中體現,只在官網的example中修改了demo的實現,被這個坑絆到了。。。

如今咱們看一下在weex-vue-render v-0.10.0中是如何獲取到設備的ideal viewport的,這也就解釋了爲什麼要在html中添加這個標籤了。

咱們直接看源碼修改的部分,這裏將window.screen.width修改爲了document.documentElement.getBoundingClientRect().width

直白話講就是取得當前屏幕的html的寬度,而html的寬度則是由<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">標籤的width=device-width決定的,結合起來說就是這個標籤將html的寬度設置成了device-width寬,而這個寬就是設備的ideal viewport的寬。並且這個值是永遠正確的。這種方式計算獲得的viewport在各類設備上都會以750px寬度呈現。

另外若是咱們是以源碼依賴的方式而不是以script方式管理weex-vue-render的,0.10.0版本會有一個bug致使咱們獲得的viewport的值是undefined。

緣由在於weex-vue-rendernpm包中package.json的main字段指向了"main": "src/render/vue/index.js",源碼,而源碼中的viewport相關代碼中含有let viewportWidth = process.env.VIEWPORT_WIDTH環境變量,並無文檔說明咱們在源碼依賴weex-vue-render進行編譯的時候須要給環境變量process.env.VIEWPORT_WIDTH設置750的值。因此編譯出來的viewportWidth變量的值確定是undefined。

你們能夠npm i weex-vue-render@0.10.0查看一下。

解決辦法一則將main入口指向weex-vue-render編譯好的文件。方法二能夠在咱們本身管理源碼的時候添加這個環境變量。

0.10.0sdk支持相對地址並自動補全

0.4.0版本的sdk中是不支持相對地址自動補全的,而咱們前端同窗在寫業務邏輯時都是以相對地址的方式編寫跳轉路由或者api接口。

好比一個api請求

stream.fetch({
    method: 'GET',
    url: "/api/shop, type: 'json' }, function (response) {}) 複製代碼

h5端瀏覽器會自動拼接上當前域名的host,而native端拿到的只是這個相對地址請求。我司native方的作法是在網絡請求以前手動拼接上須要的host域名,完成網絡請求。

可是native端的weexsdk從0.9.4版本開始支持不全相對地址。

Support relative URL, which will resolve real URL by bundle's URL.

可是比較坑的是與咱們的業務場景不符合。由於sdk自動補全的host是根據js bundle的url進行的。

好比咱們的js bundle地址是cdn1.xxx.com/js/weex.js,可是業務裏須要請求的接口的地址是shop.m.xxx.com/api/getContent

問題就是咱們在.vue頁面裏寫接口都是相對地址/api/getContent,這樣的話weexSDK會根據bundle的host進行轉換相對地址,變成了cdn1.xxx.com/api/getContent,致使接口請求404。

因此這個時候須要native那邊去寫各自的handler和adapter,重寫各自的網絡請求,再在weexSDK中進行註冊,這意味着須要在sdk進行不全以前native就先把url進行補全了,不把機會留給sdk進行處理。

這個問題的issue

小結

這還只是初步遷移到vue時代的嘗試,可是基礎已經打好,更多的業務已經能夠接入進來了。將來還有許多在H5中考慮的內容須要從新在weex的視角下再進行考慮。

本文來自南洋,有什麼須要討論的歡迎找我,尤爲是viewport相關的內容。

相關文章
相關標籤/搜索