俗話說(shuō)光說(shuō)不練假把式。那就先來(lái)感受下Weex的魅力:
目前項(xiàng)目已上傳至GitHub上菠齿,需要的可自行前去下載:
原生項(xiàng)目(Xcode)
Weex項(xiàng)目(WebStorm)
一.目的
????????Weex雖然從阿里爸爸把它生出來(lái)也有兩年時(shí)間了枪蘑,但我想對(duì)于廣大開(kāi)發(fā)者來(lái)說(shuō)唱歧,可能對(duì)它的了解少之又少。對(duì)于這種新鮮的事物球及,我們總是要持有敬畏的態(tài)度的氧骤,因?yàn)楫?dāng)你在進(jìn)一步了解它的時(shí)候,你會(huì)發(fā)現(xiàn)它有無(wú)窮的魅力在吸引你吃引。我當(dāng)初就是被它的魅力深深吸引筹陵,想更深入的了解,但我在度娘那里沒(méi)有找到多少真正的項(xiàng)目實(shí)戰(zhàn)镊尺,即使有也是比較籠統(tǒng)的講了一下大概朦佩,沒(méi)有詳細(xì)的介紹。因此我寫(xiě)此篇文章的目的是幫助那些剛剛開(kāi)始接觸Weex又急于想找個(gè)項(xiàng)目練手的新手玩家們庐氮,我會(huì)以一個(gè)初學(xué)者的角度语稠,盡可能的講解項(xiàng)目的每個(gè)細(xì)節(jié)。
二.聲明
????????對(duì)于小白:如果在這之前你沒(méi)聽(tīng)說(shuō)過(guò)Weex,那很好仙畦,這篇文章你可以讀一讀输涕。啥?沒(méi)興趣慨畸?那我可以把Weex的廣告語(yǔ)透露給你莱坎,Write Once, Run Everywhere。點(diǎn)我
????????對(duì)于新手:如果你是剛剛開(kāi)始接觸并且躍躍欲試的新手玩家寸士,正想找個(gè)真實(shí)項(xiàng)目練練手檐什。那更好,這篇文章對(duì)你在合適不過(guò)了碉京。跟我一起厢汹,邊學(xué)邊練。我會(huì)一步一步的介紹整個(gè)項(xiàng)目的流程谐宙。
????????對(duì)于大佬:如果您是Weex大佬烫葬,那更好了。不要走凡蜻,留下聯(lián)系方式搭综,小弟我有點(diǎn)問(wèn)題想跟您請(qǐng)教請(qǐng)教。
學(xué)習(xí)門(mén)檻:
1.Vue:Vue語(yǔ)言基礎(chǔ)
2.ES6:ECMAScript 6 入門(mén)
3.iOS或者安卓開(kāi)發(fā)語(yǔ)言和編輯器基本使用
????????還有一點(diǎn)我覺(jué)得有必要先聲明一下划栓,由于本人的文字能力有限兑巾,有些地方語(yǔ)言表達(dá)可能不太清楚,文章排版可能不太清晰忠荞,但是這又有什么關(guān)系呢蒋歌,再大的困難也擋不住大家的熱情啊委煤!由于內(nèi)容很長(zhǎng)而本人時(shí)間有限堂油,只能在工作之余寫(xiě)一些東西,所以打算不定時(shí)更新碧绞,不便之處還請(qǐng)諒解府框。好了廢話不多說(shuō)了,開(kāi)始入正題吧讥邻。迫靖。。
三.那就開(kāi)始吧
開(kāi)發(fā)環(huán)境:macOS 10.13.4
開(kāi)發(fā)工具:WebStorm 2018.1 Xcode 9.3
????????本項(xiàng)目采用的是集成的方式兴使,即將 Weex 集成到已有的應(yīng)用系宜。為什么呢?原因有兩點(diǎn)鲫惶,一:Weex TabBar(標(biāo)簽欄)和NavigationBar(導(dǎo)航欄)不太好用蜈首,需要用到第三方的組件,我想與其用第三方的不如直接用原生代碼寫(xiě)了。二:本人認(rèn)為項(xiàng)目中還是需要用原生的代碼的欢策,畢竟像Weex這種新生事物吆寨,很多地方有待完善,完全依賴(lài)未免在有些地方會(huì)存在力不從心踩寇,所以為了增強(qiáng)代碼可控性啄清,我建議使用集成的方式。(個(gè)人看法俺孙,不喜勿噴)
新建原生工程:
1.新建一個(gè)Xcode工程辣卒,建立目錄結(jié)構(gòu)如下:(里面的類(lèi)文件暫時(shí)先不要建,之后會(huì)慢慢的一一說(shuō)明)2.通過(guò)cocoaPods向項(xiàng)目中導(dǎo)入最新版本的WeexSDK睛榄,在 Podfile 文件中添加如下內(nèi)容:(至于cocoaPods怎么使用就在這就不多贅述了荣茫,不會(huì)的可以去問(wèn)問(wèn)度娘)
target 'demo' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Pods for demo
pod 'WeexSDK'
target 'demoTests' do
inherit! :search_paths
# Pods for testing
end
target 'demoUITests' do
inherit! :search_paths
# Pods for testing
end
end
????????打開(kāi)命令行,切換到你已有項(xiàng)目 Podfile 這個(gè)文件存在的目錄场靴,執(zhí)行 pod install啡莉,沒(méi)有出現(xiàn)任何錯(cuò)誤表示已經(jīng)完成環(huán)境配置。至此旨剥,原生的工程就算大功告成了咧欣。
新建Weex工程:
1.新建一個(gè)Weex工程,建立目錄結(jié)構(gòu)如下:(里面的類(lèi)文件暫時(shí)先不要建轨帜,之后會(huì)慢慢的一一說(shuō)明)
啥魄咕?你告訴我你不會(huì)!那不可能吧蚌父,既然都來(lái)實(shí)戰(zhàn)了哮兰,我默認(rèn)你會(huì)這些基本的操作了啊。哪有都上陣打仗了不會(huì)用槍的道理苟弛。奠蹬。。(不會(huì)請(qǐng)看這里)
2.下一步就是進(jìn)入剛剛創(chuàng)建的文件夾嗡午,并且安裝依賴(lài),然后執(zhí)行 npm start:
cd your`s project file
npm install
npm start
????????然后工具會(huì)啟動(dòng)一個(gè)本地的 web 服務(wù)冀痕,監(jiān)聽(tīng) 8081 端口荔睹。
????????當(dāng)你出現(xiàn)這種頁(yè)面的時(shí)候,那么恭喜你哈恰,你的Weex工程算是新建好了只估。
開(kāi)始寫(xiě)代碼
1.打開(kāi)原生工程
(1)創(chuàng)建GlobalDefine文件,里面加1條宏着绷,其值就是Weex工程中index.js文件的路徑蛔钙。這樣做的好處就是當(dāng)我們?cè)赪eex項(xiàng)目中修改好代碼之后,原生項(xiàng)目只需要重新加載一次js文件就可以同步看到修改之后的效果荠医,不需要每次都拷貝過(guò)來(lái)吁脱,然后在build一次原生項(xiàng)目。
#define HomeJS @"/Users/peter/Desktop/weexCode/weexDemo/dist/index.js"
(2)創(chuàng)建.pch文件子漩,為以后類(lèi)文件引用做準(zhǔn)備豫喧。
#import <WeexSDK/WeexSDK.h>
#import "GlobalDefine.h"
(3)在info.plist中添加Allow Arbitrary Loads并設(shè)置值為YES(不會(huì)就點(diǎn)我)。如果不設(shè)置會(huì)無(wú)法進(jìn)行http請(qǐng)求哦幢泼,當(dāng)然也加載不了網(wǎng)絡(luò)圖片咯紧显。
(4)由于weexSDK 目前沒(méi)有提供圖片下載的能力,在WXImgLoaderProtocol 定義了一些獲取圖片的接口, image 組件正是通過(guò) WXImgLoaderProtocol 獲得并展示圖片缕棵,我們可以實(shí)現(xiàn)該 protocol 中的接口方法孵班,這樣 image 標(biāo)簽才能正常展示圖片。這就需要我們自定義handler并注冊(cè)了招驴。在WeexCustom目錄下創(chuàng)建WXImgLoaderDefaultImpl類(lèi)篙程,實(shí)現(xiàn)WXImgLoaderProtocol協(xié)議里面的方法。
@implementation WXImgLoaderDefaultImpl
#pragma mark -
#pragma mark WXImgLoaderProtocol
- (id<WXImageOperationProtocol>)downloadImageWithURL:(NSString *)url imageFrame:(CGRect)imageFrame userInfo:(NSDictionary *)userInfo completed:(void(^)(UIImage *image, NSError *error, BOOL finished))completedBlock
{
if ([url hasPrefix:@"http://"]) {
url = [@"http:" stringByAppendingString:url];
}
return (id<WXImageOperationProtocol>)[[[SDWebImageManager sharedManager] imageDownloader]downloadImageWithURL:[NSURL URLWithString:url] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
} completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (completedBlock) {
completedBlock(image, error, finished);
}
}];
}
@end
(5)在NativeFile下面創(chuàng)建四個(gè)Controller分別對(duì)應(yīng)底部四個(gè)標(biāo)簽欄别厘。并且自定義TabBarController文件繼承系統(tǒng)的UITabBarController作為項(xiàng)目的根控制器虱饿。
@implementation TabBarController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor] ;
[self setViewControllers:self.allControllers animated:NO];
}
- (NSArray *)allControllers{
if (_allControllers == nil) {
HomeViewController *home = [[HomeViewController alloc] init] ;
ShopViewController *shop = [[ShopViewController alloc] init] ;
MineViewController *mine = [[MineViewController alloc] init] ;
MoreViewController *more = [[MoreViewController alloc] init] ;
NSArray *array = @[[self navWithRoot:home title:@"首頁(yè)" image:@"icon_tabbar_homepage" selectedImage:@"icon_tabbar_homepage_selected"],
[self navWithRoot:shop title:@"商家" image:@"icon_tabbar_merchant_normal" selectedImage:@"icon_tabbar_merchant_selected"],
[self navWithRoot:mine title:@"我的" image:@"icon_tabbar_mine" selectedImage:@"icon_tabbar_mine_selected"],
[self navWithRoot:more title:@"更多" image:@"icon_tabbar_misc" selectedImage:@"icon_tabbar_misc_selected"]];
_allControllers = [[NSArray alloc] initWithArray:array];
}
return _allControllers;
}
- (UINavigationController *)navWithRoot:(UIViewController *)vc title:(NSString *)title image:(NSString *)image selectedImage:(NSString *)selectedImage {
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
UIImage *imageNormal = [UIImage imageNamed:image];
UIImage *imageSelected = [UIImage imageNamed:selectedImage];
UITabBarItem *tabBarItem = [[UITabBarItem alloc] initWithTitle:title image:[[imageNormal bp_scaleWithSize:CGSizeMake(30, 30)] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] selectedImage:[[imageSelected bp_scaleWithSize:CGSizeMake(30, 30)] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
tabBarItem.titlePositionAdjustment = UIOffsetMake(0, -2) ;
[tabBarItem setTitleTextAttributes:[NSDictionary dictionaryWithObject:[UIColor orangeColor] forKey:NSForegroundColorAttributeName] forState:UIControlStateSelected] ;
nav.tabBarItem = tabBarItem ;
return nav;
}
@end
(6)在WeexConfig目錄下創(chuàng)建WeexSDKManager類(lèi),用來(lái)對(duì)WeexSDK的初始化触趴,以及相關(guān)自定義組件的注冊(cè)都可以放在該類(lèi)里面(我們前面自定義的圖片下載WXImgLoaderDefaultImpl就放在這里面注冊(cè))氮发。
+ (void)setup;
{
[self initWeexSDK];
[self loadCustomContain];
}
+ (void)initWeexSDK
{
[WXAppConfiguration setAppGroup:@"AliApp"];
[WXAppConfiguration setAppName:@"WeexDemo"];
[WXAppConfiguration setAppVersion:@"1.8.3"];
[WXAppConfiguration setExternalUserAgent:@"ExternalUA"];
[WXSDKEngine initSDKEnvironment];
#ifdef DEBUG
[WXLog setLogLevel:WXLogLevelLog];
#endif
//自定義組件的注冊(cè)
[WXSDKEngine registerHandler:[WXImgLoaderDefaultImpl new] withProtocol:@protocol(WXImgLoaderProtocol)];
[WXSDKEngine registerModule:@"HomeViewController" withClass:NSClassFromString(@"HomeViewController")];
[WXSDKEngine registerComponent:@"PeterSwitch" withClass:NSClassFromString(@"PeterSwitch")];
}
+ (void)loadCustomContain
{
[[UIApplication sharedApplication] delegate].window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[[UIApplication sharedApplication] delegate].window.window.backgroundColor = [UIColor whiteColor];
TabBarController *demo = [[TabBarController alloc] init];
[[UIApplication sharedApplication] delegate].window.rootViewController = demo;
[[[UIApplication sharedApplication] delegate].window makeKeyAndVisible];
}
(7)WeexSDKManager對(duì)外提供setup的類(lèi)方法,在AppDelegate的didFinishLaunchingWithOptions方法里面調(diào)用冗懦。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[WeexSDKManager setup];
return YES;
}
(8)萬(wàn)事俱備爽冕,接下來(lái)需要我們把WeexSDK用起來(lái)啊,這就是使用SDK將打包生成的js文件解析成各平臺(tái)原生組件的過(guò)程披蕉。進(jìn)入HomeViewController(首頁(yè))颈畸,代碼如下乌奇。
@interface HomeViewController ()
@property (nonatomic, strong) WXSDKInstance *instance;
@property (nonatomic, strong) UIView *weexView;
@end
@implementation HomeViewController
WX_EXPORT_METHOD(@selector(weexRender))
WX_EXPORT_METHOD(@selector(iosRender))
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//隱藏系統(tǒng)導(dǎo)航欄
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self iosRender];
}
- (void)iosRender
{
CGFloat width = self.view.frame.size.width;
CGFloat height = self.view.frame.size.height;
[_instance destroyInstance];
_instance = [[WXSDKInstance alloc] init];
_instance.viewController = self;
_instance.frame = CGRectMake(0, 0, width, height-49);
__weak typeof(self) weakSelf = self;
_instance.onCreate = ^(UIView *view) {
[weakSelf.weexView removeFromSuperview];
weakSelf.weexView = view;
[weakSelf.view addSubview:weakSelf.weexView];
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, weakSelf.weexView);
};
_instance.onFailed = ^(NSError *error) {
WXLogDebug(@"%@", @"Render onFailed...");
};
_instance.renderFinish = ^(UIView *view) {
WXLogDebug(@"%@", @"Render Finish...");
};
_instance.updateFinish = ^(UIView *view) {
WXLogDebug(@"%@", @"Update Finish...");
};
//這里的HomeJS就是全局的宏定義
NSURL *URL = [NSURL fileURLWithPath:HomeJS];
[_instance renderWithURL:URL options:@{@"bundleUrl":URL.absoluteString} data:nil];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//只要點(diǎn)擊屏幕就會(huì)調(diào)用這個(gè)方法,重新解析一次js文件眯娱,這樣做的好處就是不需要重新build項(xiàng)目就能刷新js
[self iosRender];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{ //修改頂部狀態(tài)欄(電池欄)顏色為白色
return UIStatusBarStyleLightContent;
}
- (void)dealloc
{ //控制器銷(xiāo)毀的時(shí)候要做相應(yīng)處理
[_instance destroyInstance];
}
@end
(9)這些完成之后build一下原生項(xiàng)目礁苗,之后自動(dòng)啟動(dòng)Xcode自帶模擬器,神奇的一幕出現(xiàn)了困乒。哇寂屏!成就感爆棚有木有,之前的一切努力都是值得的娜搂,這就是前端開(kāi)發(fā)的魅力所在迁霎。????????至此我們?cè)糠执a就可以告一段落了,之后在寫(xiě)“商家”百宇、“我的”和“更多”的時(shí)候還需要再回來(lái)考廉,接下里我們大部分工作都會(huì)在Weex項(xiàng)目中完成。
2.打開(kāi)Weex項(xiàng)目
(1)在Home目錄下創(chuàng)建Home.vue文件携御,用來(lái)寫(xiě)首頁(yè)昌粤。
(2)進(jìn)入index.vue,將Home.vue引入進(jìn)來(lái)啄刹。
<template>
<home></home>
</template>
<script>
//用這種方式引入vue組件
import home from '../src/demo/Home/Home';
export default {
name: 'App',
data () {
return {
}
},
//在components里面聲明然后才能使用
components:{
home
}
}
</script>
//scoped-以表示它的樣式作用于當(dāng)下的模塊涮坐,很好的實(shí)現(xiàn)了樣式私有化的目的
<style scoped>
</style>
(3)新建globalDefine.js文件,放一些全局的變量
exports.apiUrl = {
resUrl:'http://192.168.0.225:8081/images/'
}
首頁(yè)
1.頂部導(dǎo)航欄:
<template>
<!--Weex的template里面有且只能有一個(gè)div標(biāo)簽作為跟標(biāo)簽-->
<div class="container">
<!--導(dǎo)航欄-->
<div class="navgationContainer">
<div class="navigation">
<!--地理位置-->
<div class="locationContainer">
<text style="color: white">上菏木△</text>
</div>
<!--搜索框-->
<div class="search">
<!--圖標(biāo)-->
<image :src="searchIcon" style="width: 44px;height: 44px;margin-left: 10px"></image>
<input style="margin-right: 10px;margin-left: 10px;font-size: 30px;flex: 1" placeholder="輸入商家袱讹、品類(lèi)、商圈"/>
<image :src="scanIcon" style="width: 44px;height: 44px;margin-right: 10px"></image>
</div>
<div style="flex-direction: row; flex: 0.3;justify-content: center;align-items: center">
<!--地圖-->
<image :src="mapIcon" style="width: 44px;height: 44px;margin-right: 5px"></image>
<text style="color: white">地圖</text>
</div>
</div>
</div>
</div>
</template>
<script>
//圖片地址采用基礎(chǔ)地址加名稱(chēng)的方式拼接
var globalDefine = require('../../globalDefine');
export default {
data(){
return{
searchIcon:globalDefine.apiUrl.resUrl + 'search.png',
scanIcon:globalDefine.apiUrl.resUrl + 'scan.png',
mapIcon:globalDefine.apiUrl.resUrl + 'map.png',
}
}
}
</script>
<style scoped>
.navgationContainer{
height: 128px;
background-color: rgba(255,96,0,1.0);
}
.navigation{
flex-direction: row;
height: 88px;
margin-top: 40px;
align-items: center;
}
.search{
flex: 1;
flex-direction: row;
background-color: white;
justify-content: space-between;
align-items: center;
margin-left: 20px;
margin-right: 20px;
border-radius: 8px;
height: 60px;
}
</style>
代碼注解:
1.為了簡(jiǎn)化頁(yè)面設(shè)計(jì)和實(shí)現(xiàn), 屏幕的寬度統(tǒng)一為750像素昵时,不同屏幕會(huì)按照比例轉(zhuǎn)化為這一尺寸捷雕。
2.標(biāo)準(zhǔn)CSS支持很多樣式選擇器, 但Weex目前只支持單個(gè)類(lèi)的選擇器壹甥。
3.標(biāo)準(zhǔn)CSS支持很多的長(zhǎng)度單位,Weex目前只支持像素救巷,并且px在樣式中可以忽略不寫(xiě), 直接使用對(duì)應(yīng)的數(shù)值句柠。
4.標(biāo)準(zhǔn)CSS包含了非常多的樣式屬性浦译,但Weex只支持了其中的一部分,包括盒模型溯职,flexbox管怠,position等布局屬性。以及font-size缸榄,color等樣式。
5.v-bind動(dòng)態(tài)綁定指令祝拯,默認(rèn)情況下標(biāo)簽自帶屬性的值是固定的甚带,在為了能夠動(dòng)態(tài)的給這些屬性添加值她肯,可以使用v-bind:你要?jiǎng)討B(tài)變化的值="表達(dá)式"。
6.v-bind用于綁定屬性和數(shù)據(jù) 鹰贵,其縮寫(xiě)為“ : ” 也就是v-bind:src = :src晴氨。
7.項(xiàng)目中圖片地址均采用基礎(chǔ)地址+名稱(chēng)的方式拼接,如果出現(xiàn)圖片加載不出來(lái)的情況可以在globalDefine.js將resUrl更換成自己本機(jī)的ip地址即可碉输。(至于Weex的圖片導(dǎo)入方式我建議看一下這篇文章:Weex導(dǎo)入圖片)
8.你或許會(huì)問(wèn)為什么我把樣式直接寫(xiě)在行內(nèi)了籽前,個(gè)人習(xí)慣而已,我喜歡把樣式代碼比較少的或者不是公共樣式采用內(nèi)聯(lián)樣式敷钾,其他的用頁(yè)內(nèi)樣式枝哄。不然一個(gè)標(biāo)簽一個(gè)class得把我累死。
????????當(dāng)我們執(zhí)行了npm run serve命令之后阻荒,我們每一次改變都會(huì)自動(dòng)在Weex Preview渲染挠锥,相應(yīng)的我們點(diǎn)擊iOS模擬器重新加載index.js文件會(huì)得到最新的頁(yè)面渲染效果。
2.導(dǎo)航欄做好了接下來(lái)就是正文的列表頁(yè)侨赡,整個(gè)列表用一個(gè)scroller組件包裝蓖租,里面的每一個(gè)cell分開(kāi)來(lái)寫(xiě),這樣可以減輕首頁(yè)的代碼量羊壹。
頂部分頁(yè)視圖
<template>
<div class="tab" style="background-color: white;flex: 1;height: 380px">
<slider class="slider" auto-play="true" interval="3000" @change="onchange">
<div style="width: 750px">
<div v-for="(v,i) in items2" style="flex-direction: row;margin-top: 36px;width: 750px">
<div v-for="(item,k) in v" style="flex: 1;justify-content: center;align-items: center">
<image :src="item.icon" style="width: 88px;height: 88px"></image>
<text style="font-size: 30px">{{item.name}}</text>
</div>
</div>
</div>
<div style="width: 750px">
<div v-for="(v,i) in items3" style="flex-direction: row;margin-top: 36px;width: 750px">
<div v-for="(item,k) in v" style="flex: 1;justify-content: center;align-items: center">
<image :src="item.icon" style="width: 88px;height: 88px"></image>
<text style="font-size: 30px">{{item.name}}</text>
</div>
</div>
</div>
<indicator class="indicatorClass"></indicator>
</slider>
</div>
</template>
<script>
methods: {
onchange (event) {
console.log('changed:', event.index)
}
}
}
</script>
代碼注解:
1.由于篇幅的原因我就不把所有代碼都截上來(lái)了蓖宦,這里只選取相對(duì)重要的部分,需要的童鞋請(qǐng)前去下載完整項(xiàng)目油猫。
2.為什么用slider而不用scroller稠茂?slider組件用于在一個(gè)頁(yè)面中展示多個(gè)圖片沫浆,在前端介褥,這種效果被稱(chēng)為輪播圖。它支持任意類(lèi)型的Weex組件作為其子組件亿乳,而且它有一個(gè)專(zhuān)屬子組件—indicator用于顯示輪播圖指示器效果鲫售,這個(gè)indicator必須充當(dāng)slider組件的子組件使用才有效果共螺。
3.@change="onchange",slider的事件情竹,當(dāng)輪播索引改變時(shí)藐不,觸發(fā)該事件。
4.<div v-for="(v,i) in items">
????????<div v-for="(item,k) in v" >
????????</div>
???</div>
????循環(huán)創(chuàng)建每一個(gè)item秦效,注意v-for語(yǔ)句的寫(xiě)法雏蛮。如果只是一重循環(huán)直接v-for="item in items"就可以了,其中item就是items里面的每一個(gè)元素阱州,在其子組件中可以直接使用item賦值挑秉。
5.indicator作為子組件之間寫(xiě)在slider里面就可以了,他會(huì)自動(dòng)隨著slider的滑動(dòng)而改變指示器苔货。
6.text組件只能包含文本值犀概,你可以使用 {{}} 標(biāo)記插入變量值作為文本內(nèi)容立哑。不支持子組件。
首頁(yè)中間視圖
<template>
<div class="container">
<!--左邊view-->
<div class="leftView">
<image :src="leftViewTopImage" style="width: 240px;height: 60px;margin-top: 40px"></image>
<image :src="leftViewMiddleImage" style="width: 240px;height: 120px"></image>
<text style="color: darkgray;font-size: 34px">探路組碳烤魚(yú)</text>
<div style="flex-direction: row">
<text style="color: cyan;font-size: 28px">¥9.5</text>
<text style="color: darkorange;background-color: khaki;font-size: 28px">再減3元</text>
</div>
</div>
<!--右邊view-->
<div class="rightView">
<div class="rightViewTopView">
<div>
<text style="color: darkorange;font-size: 32px;margin-left: 20px">天天特價(jià)</text>
<text style="color: #717171;font-size: 32px;margin-left: 20px;margin-top: 10px">特惠不打烊</text>
</div>
<image :src="rightViewTopImage" style="width: 150px;height: 120px;"></image>
</div>
<div class="rightViewBottomView">
<div>
<text style="color: crimson;font-size: 32px;margin-left: 20px">一元吃</text>
<text style="color: #717171;font-size: 32px;margin-left: 20px;margin-top: 10px">一元吃美食</text>
</div>
<image :src="rightViewBtttomImage" style="width: 150px;height: 120px"></image>
</div>
</div>
</div>
</template>
代碼注解:
此處沒(méi)啥好說(shuō)的姻灶,常規(guī)UI布局铛绰,注意Flexbox布局技巧。
首頁(yè)促銷(xiāo)視圖
<template>
<div class="container">
<!--上面的view-->
<div class="topView">
<div class="topLeftView">
<text style="color: magenta;font-size: 38px;margin-left: 30px;margin-top: 25px">最高立減25</text>
<text style="color: #717171;font-size: 32px;margin-left: 30px">美味享不停产喉,趕快行動(dòng)吧</text>
</div>
<div class="topRightView">
<image :src="topViewRightImage" style="width: 250px;height: 120px;"></image>
</div>
</div>
<div class="bottomView">
<!--左邊view-->
<div class="leftView">
<div class="leftViewTopView">
<div>
<text style="color: darkorange;font-size: 32px;margin-left: 20px">1元肯德基</text>
<text style="color: #717171;font-size: 32px;margin-left: 20px;margin-top: 10px">1元能吃肯德基</text>
</div>
<image :src="leftViewTopImage" style="width: 150px;height: 120px;"></image>
</div>
<div class="leftViewBottomView">
<div>
<text style="color: crimson;font-size: 32px;margin-left: 20px">4月開(kāi)春大促</text>
<text style="color: #717171;font-size: 32px;margin-left: 20px;margin-top: 10px">領(lǐng)21元紅包</text>
</div>
<image :src="leftViewBtttomImage" style="width: 150px;height: 120px"></image>
</div>
</div>
<!--右邊view-->
<div class="rightView">
<div class="rightViewTopView">
<div>
<text style="color: darkorange;font-size: 32px;margin-left: 20px">新用戶專(zhuān)享</text>
<text style="color: #717171;font-size: 32px;margin-left: 20px;margin-top: 10px">小長(zhǎng)假美美噠</text>
</div>
<image :src="rightViewTopImage" style="width: 150px;height: 120px;"></image>
</div>
<div class="rightViewBottomView">
<div>
<text style="color: crimson;font-size: 32px;margin-left: 20px">一元搶吧</text>
<text style="color: #717171;font-size: 32px;margin-left: 20px;margin-top: 10px">爆品搶到手軟</text>
</div>
<image :src="rightViewBtttomImage" style="width: 150px;height: 120px"></image>
</div>
</div>
</div>
</div>
</template>
首頁(yè)購(gòu)物中心
購(gòu)物中心代碼:
<template>
<div class="container">
<homeBottomCommonCell
:rightViewBtttomImage = rightViewBtttomImage
leftTitle = "購(gòu)物中心"
rightTitle = "全部4家"
></homeBottomCommonCell>
<scroller class="scrollerClass" scroll-direction="horizontal" >
<home-shop-center-item v-for="obj in homeShopCenterData.data"
:imageStr = obj.img
:title = obj.name
:tagTitle = obj.showtext.text
></home-shop-center-item>
</scroller>
</div>
</template>
<script>
var globalDefine = require('../../globalDefine');
var homeBottomCommonCell = require('./homeBottomCommonCell');
var homeShopCenterItem = require('./homeShopCenterItem');
var homeShopCenterData = require('../resource/homeShopCenter');
export default {
data () {
return{
rightViewBtttomImage:globalDefine.apiUrl.resUrl + 'gw.png',
homeShopCenterData:homeShopCenterData,
}
},
components:{
homeBottomCommonCell,
homeShopCenterItem
}
}
</script>
homeBottomCommonCell組件代碼:
<template>
<div class="container">
<div class="innerView">
<div class="leftView">
<image :src="rightViewBtttomImage" style="width: 50px;height: 50px;margin-left: 20px"></image>
<text style="color: black;font-size: 34px;margin-left: 15px;">{{leftTitle}}</text>
</div>
<div class="rightView">
<text style="color: #717171;font-size: 28px;">{{rightTitle}}</text>
<image :src="rightarrow" style="width: 20px;height: 25px;margin-left: 10px;margin-right: 20px"></image>
</div>
</div>
</div>
</template>
<script>
var globalDefine = require('../../globalDefine');
export default {
props:{
rightViewBtttomImage:'',
leftTitle: '',
rightTitle: '',
},
data () {
return{
rightarrow:globalDefine.apiUrl.resUrl + 'icon_cell_rightarrow.png',
}
}
}
</script>
homeShopCenterItem組件代碼:
<template>
<div class="container">
<image :src="imageStr" style="width: 300px;height: 200px;margin-top: 20px;border-radius: 5px"></image>
<text style="color: black;font-size: 34px;margin-left: 15px;margin-top: 10px">{{title}}</text>
<text style="color: black;font-size: 34px;position: absolute;top: 150px;background-color: darkorange;color: white;padding-left: 5px;padding-right: 5px">{{tagTitle}}</text>
</div>
</template>
<script>
export default {
props:{
imageStr:'',
title:'',
tagTitle:''
},
data () {
}
}
</script>
代碼注解:
1.頭部購(gòu)物中心由于在很多地方都可以用到捂掰,且樣式差不多,所以可以單獨(dú)抽出一個(gè)組件來(lái)用<homeBottomCommonCell>,這里我覺(jué)得有必要注意一下正向傳值曾沈。正向傳值的變量名寫(xiě)在組件的props里面这嚣,使用的時(shí)候需先在script里面引用該組件,然后在components里聲明該組件晦譬,然后就可以當(dāng)做正常標(biāo)簽一樣來(lái)使用了疤苹。給組件賦值的語(yǔ)句寫(xiě)在第一個(gè)尖括號(hào)里面(如果賦的值是一個(gè)變量,則需在前面加:敛腌,比如
:rightViewBtttomImage = rightViewBtttomImage)
2.內(nèi)容部分用橫向滾動(dòng)的<scroller>卧土,里面每個(gè)item采用循環(huán)創(chuàng)建的方式填充,所以需寫(xiě)一個(gè)item組<homeShopCenterItem>像樊。
3.設(shè)置<scroller>的滾動(dòng)方向尤莺,scroll-direction定義了 scroller 的滾動(dòng)方向,樣式表屬性 flex-direction 定義了 <scroller >的布局方向生棍,兩個(gè)方向必須一致颤霎。當(dāng)需要一個(gè)水平方向的 <scroller >時(shí),使用 scroll-direction:horizontal 和 flex-direction: row涂滴。當(dāng)需要一個(gè)豎直方向的 <scroller >時(shí)友酱,由于這兩個(gè)值均是默認(rèn)值,這兩個(gè)值可以不設(shè)置柔纵。
4.拿本地json數(shù)據(jù)缔杉,在本地建好json文件,在用的時(shí)候引用即可(var homeShopCenterData = require('../resource/homeShopCenter'))搁料,注意json的格式和路徑是否正確或详。
5.引用路徑:看想引用的文件是否和所在文件處在同一文件夾下,如果在是則只需一個(gè)點(diǎn)郭计,如果不在則需兩個(gè)點(diǎn)然后接對(duì)應(yīng)的文件夾名稱(chēng)霸琴。
首頁(yè)熱門(mén)頻道
這里由于沒(méi)有什么新的東西就不貼代碼了,挺占地方的昭伸,注意一下布局技巧梧乘,需要的去下載下來(lái)看就可以了。
首頁(yè)猜你喜歡
<template>
<div class="container">
<homeBottomCommonCell ref="homeBottomCommonCell"
:rightViewBtttomImage = rightViewBtttomImage
leftTitle = "猜你喜歡"
></homeBottomCommonCell>
<list class="list" ref="guessList" show-scrollbar='false' v-bind:style="{height:listHeight}" offset-accuracy="10" @appear="onappear" @scroll="scrollHandler" @scrollstart="scrollStart">
<cell class="cell" v-for="(shop, index) in lists" ref="item">
<div class="panel">
<div style="flex-direction: row">
<div class="panelLeftView">
<image :src="dealWithImgUrl(shop.imageUrl)" style="width: 240px;height: 180px;margin-left: 20px"></image>
</div>
<div class="panelRightView">
<div style="flex-direction: row;justify-content: space-between">
<text style="color: black;font-size: 28px;margin-right: 20px;lines:1;text-overflow:ellipsis;flex: 0.8">{{shop.title}}</text>
<text style="color: black;font-size: 28px;flex: 0.3">{{shop.topRightInfo}}</text>
</div>
<text style="color: darkgray;font-size: 28px;margin-right: 20px;margin-top: 10px;lines:2;text-overflow:ellipsis">{{shop.subTitle}}</text>
<div style="flex-direction: row;justify-content: space-between;margin-top: 10px">
<text style="color: crimson;font-size: 28px">{{shop.subMessage}}</text>
<text style="color: black;font-size: 28px;margin-right: 20px">{{shop.bottomRightInfo}}</text>
</div>
</div>
</div>
<div style="flex: 1;height: 1px;background-color: #c4c4c4;margin-top: 20px"></div>
</div>
</cell>
</list>
</div>
</template>
<script>
var globalDefine = require('../../globalDefine');
var homeBottomCommonCell = require('./homeBottomCommonCell');
var stream = weex.requireModule('stream');
const modal = weex.requireModule('modal');
const dom = weex.requireModule('dom');
export default {
data () {
return{
rightViewBtttomImage:globalDefine.apiUrl.resUrl + 'cnxh.png',
lists: [],
listHeight:'',
}
},
components:{
homeBottomCommonCell,
},
created(){
const self = this;
let url = 'http://api.demo.com/group/v2/recommend/homepage/city/20?userId=160495643&userid=160495643&__vhost=api.mobile.demo.com&position=23.134643%2C113.373951&movieBundleVersion=100&utm_term=6.6&limit=40&wifi-mac=64%3A09%3A80%3A10%3A15%3A27&ci=20&__skcy=X6Jxu5SCaijU80yX5ioQuvCDKj4%3D&__skua=5657981d60b5e2d83e9c64b453063ada&__skts=1459731016.350255&wifi-name=Xiaomi_1526&client=iphone&uuid=5C7B6342814C7B496D836A69C872202B5DE8DB689A2D777DFC717E10FC0B4271&__skno=FEB757F5-6120-49EC-85B0-D1444A2C2E7B&utm_content=5C7B6342814C7B496D836A69C872202B5DE8DB689A2D777DFC717E10FC0B4271&utm_source=AppStore&utm_medium=iphone&version_name=6.6&wifi-cur=0&wifi-strength=&offset=0&utm_campaign=AgroupBgroupD100H0&__skck=3c0cf64e4b039997339ed8fec4cddf05&msid=0FA91DDF-BF5B-4DA2-B05D-FA2032F30C6C2016-04-04-08-38594';
this.getNews(url,res => {
this.lists = res.data.data;
this.listHeight = res.data.data.length * 220 + 3 +'px';
// modal.toast({message:res.ok,duration:1.0});
});
},
methods: {
getNews(url,callback){
return stream.fetch({
method:'GET',
type:'json',
url:url
},callback);
},
// 處理圖像的尺寸
dealWithImgUrl(url){
if (url.search('w.h') == -1){ // 沒(méi)有找到,正常返回
return url;
}else{
return url.replace('w.h', '240.180');
}
}
}
}
</script>
代碼注解:
1.公共部分采用上面一樣的辦法庐杨,內(nèi)容部分使用<list>組件宋下。
2.數(shù)據(jù)來(lái)源嗡善,這部分的數(shù)據(jù)來(lái)源自網(wǎng)絡(luò),因此網(wǎng)絡(luò)請(qǐng)求是這塊比不可少的学歧,好在Weex本身提供網(wǎng)絡(luò)請(qǐng)求模塊(fetch)。首先引入stream(var stream = weex.requireModule('stream'))各吨,在methods周期函數(shù)里定義getNews方法枝笨,然后created周期函數(shù)里調(diào)用getNews,傳入url揭蜒,這里fetch請(qǐng)求如果在網(wǎng)頁(yè)端可能會(huì)出現(xiàn)跨域的問(wèn)題横浑,但是在真機(jī)就不會(huì)。
3.<list>的高度屉更,本來(lái)想等<list>設(shè)置內(nèi)容并布局成功后拿到內(nèi)容高度來(lái)設(shè)置<list>的高度徙融,然而想法是美好的現(xiàn)實(shí)是殘酷的。搞了半天沒(méi)成功瑰谜,最后只能用等高的cell欺冀,用cell的高度乘以數(shù)量來(lái)設(shè)置list高度(有哪位大佬知道怎么做麻煩告訴我下哈)。由于這里是動(dòng)態(tài)的改變組件的高度萨脑,所以v-bind就必不可少了隐轩。在list標(biāo)簽里面綁定高度樣式(v-bind:style="{height:listHeight}"),隨后在網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求成功后計(jì)算好總高度賦值給listHeight就可以動(dòng)態(tài)改變高度了渤早。
4.<list>的滾動(dòng)事件职车,在<list>標(biāo)簽里寫(xiě)上需要監(jiān)聽(tīng)的事件名稱(chēng)(@scroll="scrollHandler"),然后在methods周期函數(shù)重寫(xiě)scrollHandler方法鹊杖,該方法有一個(gè)參數(shù)悴灵,里面有滾動(dòng)時(shí)的一些屬性值。
5.標(biāo)簽里面函數(shù)調(diào)用骂蓖,由于數(shù)據(jù)返回時(shí)圖片的鏈接需要我們處理积瞒,所以得寫(xiě)一個(gè)專(zhuān)門(mén)處理圖片鏈接的函數(shù)(dealWithImgUrl),在設(shè)置image的src是調(diào)用涯竟,調(diào)用格式(:src="dealWithImgUrl(shop.imageUrl)")赡鲜。
刷新控件
<refresh class="refresh" @refresh="onrefresh" :display="refreshing ? 'show' : 'hide'">
<text class="indicator-text">Refreshing ...</text>
<loading-indicator class="indicator"></loading-indicator>
</refresh>
methods:{
onrefresh (event) {
if (this.refreshing == false){
modal.toast({ message: 'Refreshing', duration: 0.2 })
this.refreshing = true
setTimeout(() => {
this.refreshing = false;
}, 500)
}
}
}
代碼注解:
1.Weex提供了一個(gè)刷新組件<refresh>,<refresh> 是 <scroller>庐船、<list>银酬、<hlist>、<vlist>筐钟、<waterfall> 的子組件揩瞪,只能在被它們包含時(shí)才能被正確渲染。
2.<refresh>組件里面可以添加子組件篓冲,例如可以添加<text>李破。
商家
<template>
<div class="container">
<!--導(dǎo)航欄-->
<div class="navgationContainer">
<div class="navigation">
<image :src="mapIcon" style="position: absolute;width: 60px;height:60px;left: 25px;top: 13px"></image>
<text class="pageTitle">商家</text>
<image :src="searchIcon" style="position: absolute;width: 60px;height:60px;right: 25px;top: 10px"></image>
</div>
</div>
<!--網(wǎng)頁(yè)-->
<web class="webClass" :src="detailUrl" @pagestart="onPageStart" @pagefinish="onPageFinish" @error="onError">
</web>
</div>
</template>
代碼注解:
1.這個(gè)頁(yè)面很簡(jiǎn)單宠哄,一個(gè)<web>組件搞定,主要是熟悉Weex的web組件是使用嗤攻,<web> 不支持任何嵌套的子組件毛嫉,并且必須指定 width 和 height 的樣式屬性,否則將不起作用妇菱。
2.要加載的網(wǎng)頁(yè)內(nèi)容的 URL承粤。必須指定一個(gè)基于 bundle URL 的相對(duì) URL,它將被重寫(xiě)為真實(shí)資源 URL(本地或遠(yuǎn)程)闯团。
3.支持公共事件辛臊,綁定自己特有的事件pagestart、pagefinish房交、error彻舰。詳情請(qǐng)查閱<web>組件的使用。
我的
<template>
<div class="container">
<!--頭部view-->
<mineHeaderView></mineHeaderView>
<scroller style="background-color: #e8e8e8">
<div>
<mineCommonCell
:leftIcon = "globalDefine.apiUrl.resUrl + 'collect.png'"
leftTitle = "我的訂單"
rightTitle = "查看全部訂單"
rightIcon= ""
></mineCommonCell>
<mineOrderCell></mineOrderCell>
</div>
<div>
<mineCommonCell style="margin-top: 20px"
:leftIcon = "globalDefine.apiUrl.resUrl + 'draft.png'"
leftTitle = "我的錢(qián)包"
rightTitle = "賬戶余額:¥100"
rightIcon= ""
></mineCommonCell>
<mineCommonCell
:leftIcon = "globalDefine.apiUrl.resUrl + 'like.png'"
leftTitle = "抵用券"
rightTitle = "0"
rightIcon= ""
></mineCommonCell>
</div>
</scroller>
</div>
</template>
更多
????????頁(yè)面就不多說(shuō)了候味,這里重點(diǎn)講一下怎么使用原生控件來(lái)自定義組件刃唤。<switch>組件已不推薦業(yè)務(wù)上使用,因?yàn)楦鞫藢?shí)現(xiàn)不一致且端上定制能力較弱负溪,不適合作為內(nèi)置組件實(shí)現(xiàn)透揣,因此建議開(kāi)發(fā)者通過(guò) weex 上層能力自行定制該組件。
????????1.先來(lái)看原生部分的代碼:
//創(chuàng)建一個(gè)類(lèi)川抡,一定要繼承自WXComponent
#import "WXComponent.h"
@interface PeterSwitch : WXComponent
@end
#import "PeterSwitch.h"
@interface PeterSwitch ()
//自定義組件的屬性辐真,可以在weex里面綁定修改
@property (nonatomic, assign) BOOL isOn ;
@property (nonatomic, strong) UIColor *tintColor ;
@property (nonatomic, strong) UIColor *onTintColor ;
@property (nonatomic, strong) UIColor *thumbTintColor ;
@end
@implementation PeterSwitch
//一個(gè) component 默認(rèn)對(duì)應(yīng)于一個(gè) view,如果未覆蓋 loadView 提供自定義 view, 會(huì)使用 WXComponent 基類(lèi)中的 WXView, WXView 是繼承自 UIView 的一個(gè)派生 view崖堤。
- (UIView *)loadView {
UISwitch *mySwitch = [[UISwitch alloc]init];
[mySwitch addTarget:self action:@selector(switchAction:) forControlEvents:UIControlEventValueChanged];
return mySwitch;
}
//對(duì)組件 view 需要做一些配置侍咱,比如設(shè)置 delegate, 可以在 viewDidLoad 生命周期做,如果當(dāng)前 view 沒(méi)有添加 subview 的話密幔,不要設(shè)置 view 的 frame楔脯,WeexSDK 會(huì)根據(jù) style 設(shè)置。
- (void)viewDidLoad {
UISwitch *mySwitch = ((UISwitch *)self.view);
//tintColor 關(guān)狀態(tài)下的背景顏色
mySwitch.tintColor = _tintColor;
//onTintColor 開(kāi)狀態(tài)下的背景顏色
mySwitch.onTintColor = _onTintColor;
//thumbTintColor 滑塊的背景顏色
mySwitch.thumbTintColor = _thumbTintColor;
mySwitch.on = _isOn;
}
//支持自定義事件,點(diǎn)擊switch發(fā)送事件胯甩,可以帶參數(shù)字典昧廷,字典將傳導(dǎo)weex頁(yè)面
- (void)switchAction:(UISwitch *)mySwitch{
[self fireEvent:@"onSwitch" params:@{@"isSwitchOn":@(mySwitch.isOn)} domChanges:nil];
}
//支持自定義屬性,在 viewDidLoad 中設(shè)置屬性
- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance {
if(self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
if (attributes[@"tintColor"]) {
_tintColor = [WXConvert UIColor:attributes[@"tintColor"]];
}
if (attributes[@"onTintColor"]) {
_onTintColor = [WXConvert UIColor:attributes[@"onTintColor"]];
}
if (attributes[@"thumbTintColor"]) {
_thumbTintColor = [WXConvert UIColor:attributes[@"thumbTintColor"]];
}
if (attributes[@"isOn"]) {
_isOn = [WXConvert BOOL:attributes[@"isOn"]] ;
}
}
return self;
}
//屬性更新
- (void)updateAttributes:(NSDictionary *)attributes
{
if (attributes[@"tintColor"]) {
_tintColor = [WXConvert UIColor:attributes[@"tintColor"]];
((UISwitch *)self.view).tintColor = _tintColor;
}
if (attributes[@"onTintColor"]) {
_onTintColor = [WXConvert UIColor:attributes[@"onTintColor"]];
((UISwitch *)self.view).onTintColor = _onTintColor;
}
if (attributes[@"thumbTintColor"]) {
_thumbTintColor = [WXConvert UIColor:attributes[@"thumbTintColor"]];
((UISwitch *)self.view).thumbTintColor = _thumbTintColor;
}
if (attributes[@"isOn"]) {
_isOn = [WXConvert BOOL:attributes[@"isOn"]];
((UISwitch *)self.view).on = _isOn;
}
}
@end
//記得要在manager里面注冊(cè)一下
[WXSDKEngine registerComponent:@"PeterSwitch" withClass:NSClassFromString(@"PeterSwitch")];
????????2.Weex部分的代碼:
<PeterSwitch class="PeterSwitch" v-if="isSwitch" @onSwitch="onSwitch" tintColor="#0088fb" onTintColor="#bfed5a" :thumbTintColor=thumbTintColor :isOn=isOn></PeterSwitch>
<div v-else style="flex-direction: row">
<text v-if="renderRightTitle()" style="color: #404040;font-size: 34px;margin-left: 25px;">{{rightTitle}}</text>
<image :src="arrowImg" style="width: 22px;height: 35px;margin-right: 25px;margin-left: 15px;margin-top: 3px"></image>
</div>
<script>
methods: {
onSwitch (e) {
this.isOn = e.isSwitchOn;
var r = Math.floor(Math.random()*256);
var g = Math.floor(Math.random()*256);
var b = Math.floor(Math.random()*256);
var color = '#'+r.toString(16)+g.toString(16)+b.toString(16);
this.thumbTintColor = color;
}
}
</script>
代碼注解:
1.v-if ,v-else,這是vue的條件語(yǔ)句,如果v-if的條件判斷成立就創(chuàng)建if的組件偎箫,否則創(chuàng)建v-else組件木柬。
2.給自定義組件< PeterSwitch > 綁定onSwitch方法,在methods里實(shí)現(xiàn)淹办,實(shí)現(xiàn)方法里改變屬性thumbTintColor的值眉枕,可以實(shí)現(xiàn)原生組件的屬性更改。
作者簡(jiǎn)介: 就職于甜橙金融信息技術(shù)部,負(fù)責(zé)iOS前端開(kāi)發(fā)工作。對(duì)于業(yè)內(nèi)的新技術(shù)比較感興趣速挑,在我看來(lái)谤牡,新的東西必然是在舊的基礎(chǔ)上優(yōu)化而來(lái),這對(duì)我們提高開(kāi)發(fā)效率很有幫助姥宝。
如需轉(zhuǎn)載翅萤,請(qǐng)注明出處,謝謝~~~