俗话说光说不练假把式。那就先来感受下Weex的魅力:
目前项目已上传至GitHub上,需要的可自行前去下载:
原生项目(Xcode)
Weex项目(WebStorm)
一.目的
Weex虽然从阿里爸爸把它生出来也有两年时间了,但我想对于广大开发者来说,可能对它的了解少之又少。对于这种新鲜的事物,我们总是要持有敬畏的态度的,因为当你在进一步了解它的时候,你会发现它有无穷的魅力在吸引你。我当初就是被它的魅力深深吸引,想更深入的了解,但我在度娘那里没有找到多少真正的项目实战,即使有也是比较笼统的讲了一下大概,没有详细的介绍。因此我写此篇文章的目的是帮助那些刚刚开始接触Weex又急于想找个项目练手的新手玩家们,我会以一个初学者的角度,尽可能的讲解项目的每个细节。
二.声明
对于小白:如果在这之前你没听说过Weex,那很好,这篇文章你可以读一读。啥?没兴趣?那我可以把Weex的广告语透露给你,Write Once, Run Everywhere。点我
对于新手:如果你是刚刚开始接触并且跃跃欲试的新手玩家,正想找个真实项目练练手。那更好,这篇文章对你在合适不过了。跟我一起,边学边练。我会一步一步的介绍整个项目的流程。
对于大佬:如果您是Weex大佬,那更好了。不要走,留下联系方式,小弟我有点问题想跟您请教请教。
学习门槛:
1.Vue:Vue语言基础
2.ES6:ECMAScript 6 入门
3.iOS或者安卓开发语言和编辑器基本使用
还有一点我觉得有必要先声明一下,由于本人的文字能力有限,有些地方语言表达可能不太清楚,文章排版可能不太清晰,但是这又有什么关系呢,再大的困难也挡不住大家的热情啊!由于内容很长而本人时间有限,只能在工作之余写一些东西,所以打算不定时更新,不便之处还请谅解。好了废话不多说了,开始入正题吧。。。
三.那就开始吧
开发环境:macOS 10.13.4
开发工具:WebStorm 2018.1 Xcode 9.3
本项目采用的是集成的方式,即将 Weex 集成到已有的应用。为什么呢?原因有两点,一:Weex TabBar(标签栏)和NavigationBar(导航栏)不太好用,需要用到第三方的组件,我想与其用第三方的不如直接用原生代码写了。二:本人认为项目中还是需要用原生的代码的,毕竟像Weex这种新生事物,很多地方有待完善,完全依赖未免在有些地方会存在力不从心,所以为了增强代码可控性,我建议使用集成的方式。(个人看法,不喜勿喷)
新建原生工程:
1.新建一个Xcode工程,建立目录结构如下:(里面的类文件暂时先不要建,之后会慢慢的一一说明)2.通过cocoaPods向项目中导入最新版本的WeexSDK,在 Podfile 文件中添加如下内容:(至于cocoaPods怎么使用就在这就不多赘述了,不会的可以去问问度娘)
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
打开命令行,切换到你已有项目 Podfile 这个文件存在的目录,执行 pod install,没有出现任何错误表示已经完成环境配置。至此,原生的工程就算大功告成了。
新建Weex工程:
1.新建一个Weex工程,建立目录结构如下:(里面的类文件暂时先不要建,之后会慢慢的一一说明)
啥?你告诉我你不会!那不可能吧,既然都来实战了,我默认你会这些基本的操作了啊。哪有都上阵打仗了不会用枪的道理。。。(不会请看这里)
2.下一步就是进入刚刚创建的文件夹,并且安装依赖,然后执行 npm start:
cd your`s project file
npm install
npm start
然后工具会启动一个本地的 web 服务,监听 8081 端口。
当你出现这种页面的时候,那么恭喜你,你的Weex工程算是新建好了。
开始写代码
1.打开原生工程
(1)创建GlobalDefine文件,里面加1条宏,其值就是Weex工程中index.js文件的路径。这样做的好处就是当我们在Weex项目中修改好代码之后,原生项目只需要重新加载一次js文件就可以同步看到修改之后的效果,不需要每次都拷贝过来,然后在build一次原生项目。
#define HomeJS @"/Users/peter/Desktop/weexCode/weexDemo/dist/index.js"
(2)创建.pch文件,为以后类文件引用做准备。
#import <WeexSDK/WeexSDK.h>
#import "GlobalDefine.h"
(3)在info.plist中添加Allow Arbitrary Loads并设置值为YES(不会就点我)。如果不设置会无法进行http请求哦,当然也加载不了网络图片咯。
(4)由于weexSDK 目前没有提供图片下载的能力,在WXImgLoaderProtocol 定义了一些获取图片的接口, image 组件正是通过 WXImgLoaderProtocol 获得并展示图片,我们可以实现该 protocol 中的接口方法,这样 image 标签才能正常展示图片。这就需要我们自定义handler并注册了。在WeexCustom目录下创建WXImgLoaderDefaultImpl类,实现WXImgLoaderProtocol协议里面的方法。
@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:@"//"]) {
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下面创建四个Controller分别对应底部四个标签栏。并且自定义TabBarController文件继承系统的UITabBarController作为项目的根控制器。
@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:@"首页" 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目录下创建WeexSDKManager类,用来对WeexSDK的初始化,以及相关自定义组件的注册都可以放在该类里面(我们前面自定义的图片下载WXImgLoaderDefaultImpl就放在这里面注册)。
+ (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
//自定义组件的注册
[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对外提供setup的类方法,在AppDelegate的didFinishLaunchingWithOptions方法里面调用。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[WeexSDKManager setup];
return YES;
}
(8)万事俱备,接下来需要我们把WeexSDK用起来啊,这就是使用SDK将打包生成的js文件解析成各平台原生组件的过程。进入HomeViewController(首页),代码如下。
@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.
//隐藏系统导航栏
[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{
//只要点击屏幕就会调用这个方法,重新解析一次js文件,这样做的好处就是不需要重新build项目就能刷新js
[self iosRender];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{ //修改顶部状态栏(电池栏)颜色为白色
return UIStatusBarStyleLightContent;
}
- (void)dealloc
{ //控制器销毁的时候要做相应处理
[_instance destroyInstance];
}
@end
(9)这些完成之后build一下原生项目,之后自动启动Xcode自带模拟器,神奇的一幕出现了。哇!成就感爆棚有木有,之前的一切努力都是值得的,这就是前端开发的魅力所在。 至此我们原生部分代码就可以告一段落了,之后在写“商家”、“我的”和“更多”的时候还需要再回来,接下里我们大部分工作都会在Weex项目中完成。
2.打开Weex项目
(1)在Home目录下创建Home.vue文件,用来写首页。
(2)进入index.vue,将Home.vue引入进来。
<template>
<home></home>
</template>
<script>
//用这种方式引入vue组件
import home from '../src/demo/Home/Home';
export default {
name: 'App',
data () {
return {
}
},
//在components里面声明然后才能使用
components:{
home
}
}
</script>
//scoped-以表示它的样式作用于当下的模块,很好的实现了样式私有化的目的
<style scoped>
</style>
(3)新建globalDefine.js文件,放一些全局的变量
exports.apiUrl = {
resUrl:'http://192.168.0.225:8081/images/'
}
首页
1.顶部导航栏:
<template>
<!--Weex的template里面有且只能有一个div标签作为跟标签-->
<div class="container">
<!--导航栏-->
<div class="navgationContainer">
<div class="navigation">
<!--地理位置-->
<div class="locationContainer">
<text style="color: white">上海△</text>
</div>
<!--搜索框-->
<div class="search">
<!--图标-->
<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="输入商家、品类、商圈"/>
<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>
//图片地址采用基础地址加名称的方式拼接
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.为了简化页面设计和实现, 屏幕的宽度统一为750像素,不同屏幕会按照比例转化为这一尺寸。
2.标准CSS支持很多样式选择器, 但Weex目前只支持单个类的选择器。
3.标准CSS支持很多的长度单位,Weex目前只支持像素,并且px在样式中可以忽略不写, 直接使用对应的数值。
4.标准CSS包含了非常多的样式属性,但Weex只支持了其中的一部分,包括盒模型,flexbox,position等布局属性。以及font-size,color等样式。
5.v-bind动态绑定指令,默认情况下标签自带属性的值是固定的,在为了能够动态的给这些属性添加值,可以使用v-bind:你要动态变化的值="表达式"。
6.v-bind用于绑定属性和数据 ,其缩写为“ : ” 也就是v-bind:src = :src。
7.项目中图片地址均采用基础地址+名称的方式拼接,如果出现图片加载不出来的情况可以在globalDefine.js将resUrl更换成自己本机的ip地址即可。(至于Weex的图片导入方式我建议看一下这篇文章:Weex导入图片)
8.你或许会问为什么我把样式直接写在行内了,个人习惯而已,我喜欢把样式代码比较少的或者不是公共样式采用内联样式,其他的用页内样式。不然一个标签一个class得把我累死。
当我们执行了npm run serve命令之后,我们每一次改变都会自动在Weex Preview渲染,相应的我们点击iOS模拟器重新加载index.js文件会得到最新的页面渲染效果。
2.导航栏做好了接下来就是正文的列表页,整个列表用一个scroller组件包装,里面的每一个cell分开来写,这样可以减轻首页的代码量。
顶部分页视图
<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.由于篇幅的原因我就不把所有代码都截上来了,这里只选取相对重要的部分,需要的童鞋请前去下载完整项目。
2.为什么用slider而不用scroller?slider组件用于在一个页面中展示多个图片,在前端,这种效果被称为轮播图。它支持任意类型的Weex组件作为其子组件,而且它有一个专属子组件—indicator用于显示轮播图指示器效果,这个indicator必须充当slider组件的子组件使用才有效果。
3.@change="onchange",slider的事件,当轮播索引改变时,触发该事件。
4.<div v-for="(v,i) in items">
<div v-for="(item,k) in v" >
</div>
</div>
循环创建每一个item,注意v-for语句的写法。如果只是一重循环直接v-for="item in items"就可以了,其中item就是items里面的每一个元素,在其子组件中可以直接使用item赋值。
5.indicator作为子组件之间写在slider里面就可以了,他会自动随着slider的滑动而改变指示器。
6.text组件只能包含文本值,你可以使用 {{}} 标记插入变量值作为文本内容。不支持子组件。
首页中间视图
<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">探路组碳烤鱼</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">天天特价</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>
代码注解:
此处没啥好说的,常规UI布局,注意Flexbox布局技巧。
首页促销视图
<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">美味享不停,赶快行动吧</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月开春大促</text>
<text style="color: #717171;font-size: 32px;margin-left: 20px;margin-top: 10px">领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">新用户专享</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>
</div>
</template>
首页购物中心
购物中心代码:
<template>
<div class="container">
<homeBottomCommonCell
:rightViewBtttomImage = rightViewBtttomImage
leftTitle = "购物中心"
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.头部购物中心由于在很多地方都可以用到,且样式差不多,所以可以单独抽出一个组件来用<homeBottomCommonCell>,这里我觉得有必要注意一下正向传值。正向传值的变量名写在组件的props里面,使用的时候需先在script里面引用该组件,然后在components里声明该组件,然后就可以当做正常标签一样来使用了。给组件赋值的语句写在第一个尖括号里面(如果赋的值是一个变量,则需在前面加:,比如
:rightViewBtttomImage = rightViewBtttomImage)
2.内容部分用横向滚动的<scroller>,里面每个item采用循环创建的方式填充,所以需写一个item组<homeShopCenterItem>。
3.设置<scroller>的滚动方向,scroll-direction定义了 scroller 的滚动方向,样式表属性 flex-direction 定义了 <scroller >的布局方向,两个方向必须一致。当需要一个水平方向的 <scroller >时,使用 scroll-direction:horizontal 和 flex-direction: row。当需要一个竖直方向的 <scroller >时,由于这两个值均是默认值,这两个值可以不设置。
4.拿本地json数据,在本地建好json文件,在用的时候引用即可(var homeShopCenterData = require('../resource/homeShopCenter')),注意json的格式和路径是否正确。
5.引用路径:看想引用的文件是否和所在文件处在同一文件夹下,如果在是则只需一个点,如果不在则需两个点然后接对应的文件夹名称。
首页热门频道
这里由于没有什么新的东西就不贴代码了,挺占地方的,注意一下布局技巧,需要的去下载下来看就可以了。
首页猜你喜欢
<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){ // 没有找到,正常返回
return url;
}else{
return url.replace('w.h', '240.180');
}
}
}
}
</script>
代码注解:
1.公共部分采用上面一样的办法,内容部分使用<list>组件。
2.数据来源,这部分的数据来源自网络,因此网络请求是这块比不可少的,好在Weex本身提供网络请求模块(fetch)。首先引入stream(var stream = weex.requireModule('stream')),在methods周期函数里定义getNews方法,然后created周期函数里调用getNews,传入url,这里fetch请求如果在网页端可能会出现跨域的问题,但是在真机就不会。
3.<list>的高度,本来想等<list>设置内容并布局成功后拿到内容高度来设置<list>的高度,然而想法是美好的现实是残酷的。搞了半天没成功,最后只能用等高的cell,用cell的高度乘以数量来设置list高度(有哪位大佬知道怎么做麻烦告诉我下哈)。由于这里是动态的改变组件的高度,所以v-bind就必不可少了。在list标签里面绑定高度样式(v-bind:style="{height:listHeight}"),随后在网络数据请求成功后计算好总高度赋值给listHeight就可以动态改变高度了。
4.<list>的滚动事件,在<list>标签里写上需要监听的事件名称(@scroll="scrollHandler"),然后在methods周期函数重写scrollHandler方法,该方法有一个参数,里面有滚动时的一些属性值。
5.标签里面函数调用,由于数据返回时图片的链接需要我们处理,所以得写一个专门处理图片链接的函数(dealWithImgUrl),在设置image的src是调用,调用格式(: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提供了一个刷新组件<refresh>,<refresh> 是 <scroller>、<list>、<hlist>、<vlist>、<waterfall> 的子组件,只能在被它们包含时才能被正确渲染。
2.<refresh>组件里面可以添加子组件,例如可以添加<text>。
商家
<template>
<div class="container">
<!--导航栏-->
<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>
<!--网页-->
<web class="webClass" :src="detailUrl" @pagestart="onPageStart" @pagefinish="onPageFinish" @error="onError">
</web>
</div>
</template>
代码注解:
1.这个页面很简单,一个<web>组件搞定,主要是熟悉Weex的web组件是使用,<web> 不支持任何嵌套的子组件,并且必须指定 width 和 height 的样式属性,否则将不起作用。
2.要加载的网页内容的 URL。必须指定一个基于 bundle URL 的相对 URL,它将被重写为真实资源 URL(本地或远程)。
3.支持公共事件,绑定自己特有的事件pagestart、pagefinish、error。详情请查阅<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 = "我的钱包"
rightTitle = "账户余额:¥100"
rightIcon= ""
></mineCommonCell>
<mineCommonCell
:leftIcon = "globalDefine.apiUrl.resUrl + 'like.png'"
leftTitle = "抵用券"
rightTitle = "0"
rightIcon= ""
></mineCommonCell>
</div>
</scroller>
</div>
</template>
更多
页面就不多说了,这里重点讲一下怎么使用原生控件来自定义组件。<switch>组件已不推荐业务上使用,因为各端实现不一致且端上定制能力较弱,不适合作为内置组件实现,因此建议开发者通过 weex 上层能力自行定制该组件。
1.先来看原生部分的代码:
//创建一个类,一定要继承自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
//一个 component 默认对应于一个 view,如果未覆盖 loadView 提供自定义 view, 会使用 WXComponent 基类中的 WXView, WXView 是继承自 UIView 的一个派生 view。
- (UIView *)loadView {
UISwitch *mySwitch = [[UISwitch alloc]init];
[mySwitch addTarget:self action:@selector(switchAction:) forControlEvents:UIControlEventValueChanged];
return mySwitch;
}
//对组件 view 需要做一些配置,比如设置 delegate, 可以在 viewDidLoad 生命周期做,如果当前 view 没有添加 subview 的话,不要设置 view 的 frame,WeexSDK 会根据 style 设置。
- (void)viewDidLoad {
UISwitch *mySwitch = ((UISwitch *)self.view);
//tintColor 关状态下的背景颜色
mySwitch.tintColor = _tintColor;
//onTintColor 开状态下的背景颜色
mySwitch.onTintColor = _onTintColor;
//thumbTintColor 滑块的背景颜色
mySwitch.thumbTintColor = _thumbTintColor;
mySwitch.on = _isOn;
}
//支持自定义事件,点击switch发送事件,可以带参数字典,字典将传导weex页面
- (void)switchAction:(UISwitch *)mySwitch{
[self fireEvent:@"onSwitch" params:@{@"isSwitchOn":@(mySwitch.isOn)} domChanges:nil];
}
//支持自定义属性,在 viewDidLoad 中设置属性
- (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里面注册一下
[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的条件语句,如果v-if的条件判断成立就创建if的组件,否则创建v-else组件。
2.给自定义组件< PeterSwitch > 绑定onSwitch方法,在methods里实现,实现方法里改变属性thumbTintColor的值,可以实现原生组件的属性更改。
作者简介: 就职于甜橙金融信息技术部,负责iOS前端开发工作。对于业内的新技术比较感兴趣,在我看来,新的东西必然是在旧的基础上优化而来,这对我们提高开发效率很有帮助。
如需转载,请注明出处,谢谢~~~