什麼是 Weex?
Weex 是使用流行的 Web 開發體驗來開發高效能原生應用的框架。
Weex 致力於使開發者能基於通用跨平臺的 Web 開發語言和開發經驗,來構建 Android、ios 和 Web 應用。簡單來說,在整合了 WeexSDK 之後,你可以使用 JavaScript 語言和前端開發經驗來開發移動應用。
Weex 渲染引擎與 DSL 語法層是分開的,Weex 並不強依賴任何特定的前端框架。目前 Vue.js 和 Rax 這兩個前端框架被廣泛應用於 Weex 頁面開發,同時 Weex 也對這兩個前端框架提供了最完善的支援。Weex 的另一個主要目標是跟進流行的 Web 開發技術並將其和原生開發的技術結合,實現開發效率和執行效能的高度統一。在開發階段,一個 Weex 頁面就像開發普通網頁一樣;在執行時,Weex 頁面又充分利用了各種作業系統的原生元件和能力。
為什麼選擇 Weex?
Weex帶給我們的收益
- 迭代速度快,快速上線
- Weex環境下完全Native體驗
- Bundle資源大小對比H5小很多
- 富互動體驗,長列表效能好
- 上手快且簡單、一次編寫三段相容
H5 | WEEX | Native | |
---|---|---|---|
開發成本 | 低 | 中 | 高 |
維護更新 | 簡單 | 簡單 | 複雜 |
使用者體驗 | 差 | 優 | 優 |
發版稽核 | 不需要 | 不需要 | 需要 |
跨平臺性 | 優 | 優 | 差 |
Weex開發踩坑
通用樣式
1、圖片
1、Weex提供了image元件,但只支援遠端圖片連結。圖片必須新增寬、高屬性否則會不顯示出來。 2、避免在image標籤上使用v-for,否則會導致安卓上圖片渲染異常(如slider中的圖片)
<slider class="activity" :autoPlay="true" interval="4000" @change="sliderChange">
<div class="activity-cell"
v-for="(item, index) in bannerList" :key="index"
@click="clickInBanner(item)">
<image class="activity-wrap-bg" resize="cover" :src="imageRes.bannerBgImg"></image>
<image class="activity-wrap-image" :src="item.pictureUrl"></image>
</div>
</slider>
複製程式碼
2、border
Weex不支援使用border建立三角形,web可以正常顯示,而ios和android上顯示的是矩形,建議使用圖片代替
3、scale設定為0問題
transform: scale(0)會導致文件流內所有事件擴散到整個html結構,導致文件流事件全部無效。只有脫離文件流的元素(absolute等)可以點選;常用可以設定transform: scale(0,1),並使元素隱藏起來。
4、input標籤高度問題
安卓環境中,當input高度設定小於60px時,會導致輸入框游標不會顯示出來。(ios、web正常)
5、v-if問題
在做一些操作切換狀態時(如按鈕點選置灰),應儘量避免使用v-if,可採用新增class的方式
6、透明度
目前僅ios支援box-shadow屬性,android暫不支援,可以使用圖片代替。每個元素只支援設定一個陰影效果,不支援多個陰影同時作用於一個元素。and在日常開發中陰影最好使用圖片來代替,避免出現未知的問題。以下是涉及到顏色的相關屬性對透明度的支援度列表
屬性 | IOS | Android | H5 |
---|---|---|---|
color | 支援 | 支援 | 支援 |
opacity | 支援 | 支援 | 支援 |
border-color | 支援 | 支援 | 支援 |
box-shadow | 支援 | 不支援 | 支援 |
background-color | 支援 | 支援 | 支援 |
background-image | 不支援 | 支援 | 支援 |
7、Weex不支援樣式簡寫
.border {
margin: 0 10px; // 錯誤
margin-right: 10px;
margin-left: 10px; // 正確
border: 1px solid #000; // 錯誤
border-width: 1px;
border-style: solid;
border-color: #000; // 正確
}
複製程式碼
8、 點選態
專案比較常見的點選態多半是透明度的變化,如按鈕、列表、連結等,css的做法是新增偽類 (:active),Weex中也同樣支援,但是Weex需要在原樣式中新增 opacity:1,否則點選後回不到初始狀態;此外,:active使用時,background-image在ios下會失效。
<template>
<div class="btn">
<text>下載</text>
</div>
</template>
<style scoped>
.btn {
opacity: 1; // 必須新增
}
.btn:active {
opacity: 0.5;
}
</style>
複製程式碼
9、 文字截斷
文字從限制一行到不限制可以使用lines:0來控制;
<template>
<text class="text" @click="onClickText" :style="textStyle">
這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,這是一段測試文字,
</text>
</template>
<style scoped>
.text {
text-overflow: ellipsis;
lines: 1;
}
</style>
<script>
export default {
data () {
return {
textStyle: {},
};
},
methods: {
onClickText() {
this.textStyle = {
lines: 0,
};
},
},
}
</script>
複製程式碼
10、html順序在不同裝置上的顯示
例如有a、b、c、d 四層結構,其中a、b、c均為absolute定位,z-index由大到小,d為普通結構,我們知道在css中a層應該是處於最上方,d在最下方,那麼在weex中表現如何呢?
<template>
<div>
<div>A(綠)</div>
<div>B(藍)</div>
<div>C(紫)</div>
<div>D(紅)</div>
</div>
</template>
複製程式碼
可以看到web和ios、android的表現不一致,ios、android中是以程式碼中dom順序來依次新增的,和z-index無關,後面載入的檢視會覆蓋前面的檢視。 所以要保證web、ios、android三端表現一致,改變dom書寫順序即可。
<template>
<div>
<div>D(紅)</div>
<div>C(紫)</div>
<div>B(藍)</div>
<div>A(綠)</div>
</div>
</template>
複製程式碼
11、安卓下遮擋問題
安卓下容器如果設定了寬高,那麼子元素不能超出容器範圍
12、微信環境輸入框失焦時,當前頁面的檢視偏移量未回覆到初始位置。
微信環境輸入框收起會改變頁面dom結構佈局,可以採取捕獲失去焦點事件, 執行window.scrollTo(0, 0);
13、微信環境下超過一屏的長圖在載入時渲染不出來
可以對圖片的父容器(scroller、 list)設定一個背景顏色,即可成功載入。
14、漸變
weex不支援徑向漸變radial-gradient,只支援建立線性漸變。並且只支援兩種顏色的漸變,漸變方向如下:
- to right: 從左向右漸變
- to left: 從右向左漸變
- to bottom: 從上向下漸變
- to top: 從下向上漸變
- to bottom right: 從左上角向右下角漸變
- to top left: 從右下角向左上角漸變
注意
- background-image優先順序高於background-color,這意味著同時設定background-image和background-color,後者會被覆蓋。
- background不支援簡寫。
15、富文字
weex在0.20版本新增一個新的標籤richtext即富文字標籤。但在之前的版本內weex是不支援富文字功能的,基本富文字功能都是使用圖片來代表,因為weex的字型標籤只有text,並且都是weex所有的標籤結構都是彈性佈局;但是weex也提供了一個方案,即在weex中空格也是佔據一定空間的,所以可以支援一些特定的富文字功能。
<template>
<div>
<image class="tag-image" :src="data.userType | typeImg" :style="{ top: `${getEnvValue(17,18,13)}px`}"></image>
<text class="item-title">{{' ' + getEnvValue(' ','',' ')+ data.title}}</text>
</div>
</template>
<script>
export default {
methods: {
// 根據環境不同返回不同的三個值
getEnvValue(webValue, iOSValue, androidValue) {
if (this.isWeb) {
return webValue;
} else if (this.isIos) {
return iOSValue;
} else if (this.isAndroid) {
return androidValue;
}
return iOSValue;
},
}
}
</script>
複製程式碼
輸入事件
1、輸入框不能清空內容
需要清空輸入框內已經輸入的內容時,不能直接將繫結的值置為空,而是應該先隱藏輸入框內顯示的值,在一次渲染之後將在將值置為空。
<template>
<div class="input-wrap">
<wxs-icon name="search" size='40px' color='#b2b2b2'></wxs-icon>
<input
:value="searchKeyWord"
returnKeyType="search"
@focus="onInputFocus"
@blur="onInputBlur"
@input="onInputInput"
@return="onInputReturn"
class="input-hint"
ref="input"
placeholder-color="#cccccc"
:singleline="true"
:lines="1" />
<div class="erase" @click="onClickErase">
<wxs-icon v-if="isInputFocus || isWeb" name="erase" size='36px' color='#b2b2b2' ></wxs-icon>
</div>
</div>
</template>
<script>
export default {
data() {
return {
searchKeyWord: '',
};
},
methods: {
onClickErase() {
this.searchKeyWord = ' ';
this.$nextTick(() => {
this.searchKeyWord = '';
});
},
}
}
</script>
複製程式碼
2、Weex input輸入框元件 在安卓下input事件BUG
當使用v-model繫結值時,還原到繫結值原始狀態時,無法觸發input事件,此時還影響到v-model的繫結。在android上的表現為對輸入框的input事件進行監聽;開啟頁面選中輸入框,對輸入框輸入一串文字,此時成功的觸發了input事件。按鍵盤上的刪除,最初也是成功的觸發input事件,當最後一個字元被刪除時,input事件並不會觸發。如果不使用value來設定值,改使用v-model,也會出現這樣的情況。通過查閱資料,發現這個是weex android sdk存在的一個坑點(作者沒太接觸過安卓開發)segmentfault.com/q/101000001…裡面給出了一張修改原始碼的方式來解決該BUG。主要問題是在WXInput的父類AbstractEditComponent類中, mIgnoreNextOnInputEvent 這個變數在元件初始化的時候被設定為了TRUE,導致了第一次輸入input內容顯示不出來。使用上一個問題的方法也可以成功的解決該問題,也可以將searchKeyWord的初始值置為undefined也可以很好的規避該問題。
元件
命令
元件命名應避免使用JS關鍵字和保留字,以及weex提供的元件名稱,如用loading作為元件名稱,在ios與android中將呈現空白。
<template>
<div>
<Loading></Loading> /* 改用其他名稱 */
</div>
</template>
複製程式碼
自定義slider元件
weex本身提供了slider元件,但輪播圖指示器(indicator)只能修改顏色與位置,大小卻無法更改,所以需要自定義slider元件
<template>
<!-- 首頁banner區塊 -->
<div class="activity-wrap" :style="{ top: (topSafeAreaHeight + 123) + 'px' }">
<slider class="activity" :autoPlay="true" interval="4000" @change="sliderChange">
<div class="activity-cell"
v-for="(item, index) in bannerList" :key="index"
@click="clickInBanner(item)">
<image class="activity-wrap-bg" resize="cover" :src="imageRes.bannerBgImg"></image>
<image class="activity-wrap-image" :src="item.pictureUrl"></image>
</div>
</slider>
<div class="slider-indicator-wrap" v-if="bannerList && bannerList.length > 1">
<div
v-for="(icon, index) in bannerList"
:key="index"
ref="activeSliderKey"
class="slider-indicator"
:class="[index === 0 ? 'slider-indicator-left' : '']"
></div>
</div>
</div>
</template>
<script>
export default {
...,
methods: {
// 首頁banner切換回撥
sliderChange({ index }) {
const self = this;
if (self.bannerList.length > 0) {
for (let i = 0; i < self.bannerList.length; i += 1) {
animation.transition(self.$refs.activeSliderKey[i], {
styles: {
backgroundColor: 'rgba(255, 255, 255, 0.3)',
},
delay: 0,
});
}
animation.transition(self.$refs.activeSliderKey[index], {
styles: {
backgroundColor: 'rgba(255, 255, 255)',
},
delay: 0,
});
}
},
},
}
</script>
複製程式碼
動畫
weex不支援幀動畫,但本身自帶的transition可以傳入對應的style,並通過setInterval來控制動畫迴圈播放
animation.js
const animation = weex.requireModule('animation');
export function transition(el, opts, dd) {
const duration = dd || 400;
if (!el) {
return Promise.resolve();
}
return new Promise((resolve) => {
animation.transition(el, {
duration,
timingFunction: 'linear',
delay: 0,
...opts,
}, resolve);
});
}
export function run(el) {
transition(el, {
styles: {
transform: 'scale(1.02)',
},
}, 100).then(() => {
transition(el, {
styles: {
transform: 'scale(1.08)',
},
}, 200);
}).then(() => {
transition(el, {
styles: {
transition: 'scale(1)',
},
}, 300);
});
}
複製程式碼
page.vue
<template>
<div ref="btn"></btn>
</template>
<script>
export default {
...
mounted() {
setTimeout(() => {
setInterval(() => {
animation.run(this.$refs.btn);
}, 600);
}, 300);
},
}
</script>
複製程式碼