weex学习之路及遇到的坑和解决方法
这是个人在学习weex中遇到的坑,以及解决方法,欢迎共同交流
weex简介
js作为连接原生控件与视图层纽带 ,代替了以往使用的java、oc、swift直接调用原生控件 、这样生成的ui层依旧是原生的、和 html毫无关系,至于那些div,image只不过是名字和html标签一样的组件而已,也没有了dome对象的存在,因为实际 开发的环境不是浏览器,而是基于原生,
初始化:
- 1,初始环境:安装node.js以及mpm
- 2,npm install weex-toolkit -g
全局安装weex-tooklkit
在命令环境注册一个weex命令
- 3,进入到需要部署的文件目录
weex create awesome-app
命令执行完成,当前名录的awesome-app文件夹里就存在一个空Weex Vue.js项目
开发:
- 进入创建的文件夹,安装依赖,
- cd awesome-app
- npm install
- npm start
- 工具会 启动一个本地的web服务,监听8081端口。打开http://localhost:8081查看页面在web下的渲染效果。
- 源代码在src/目录中,你可以像一个普通Vue.js项目一样进行开发
- 可以打开http://localhost:8081/preview.html开启预览页面,他会把web端页面放在一个iframe中渲染,右侧二维码可以用Weex palyround app扫描预览;
运行和编译
安装weexpack:
初始环境:安装node.js以及mpm
$ cnpm install -g weexpack
或者 在 clone 的 weexpack 根目录下执行
$ cnpm install
创建 weexpack工程
weexpack create appNmae (工程名)
cd appName
npm install 初始化依赖
安装应用模板 生成 android 或者 ios 工程
- android模板
weexpack platform add android
执行完成后会在platforms文件夹下生成android文件目录
- ios工程模板
weexpack platform add ios
执行完成后会在platforms文件夹下生成ios文件目录
weex platform add android
开发完成之后weex run android
打包android工程
然后用android stodio 打开工程编译
坑1:
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.equals('app-debug.apk')) {
def fileName = outputFile.name.replace("app-debug.apk", "weex-app.apk")
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
替换为
applicationVariants.all { variant ->
variant.outputs.all { output ->
def fileName = "${variant.versionName}_release.apk"
def outFile = output.outputFile
// if (outputFile != null && outputFile.name.equals('app-debug.apk')) {
// def fileName = outputFile.name.replace("app-debug.apk", "weex-app.apk")
// outputoutputFile = fileName
// }
if (outFile != null && outFile.name.endsWith('.apk')) {
outputFileName = fileName // output.outputFile 改为 outputFileName
}
}
}
坑2
Error:Execution failed for task ':uikit:javaPreCompileDebug'. > Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor. Please add them to the annotationProcessor configuration. - compiler-4.0.0-RC0.jar (com.github.bumptech.glide:compiler:4.0.0-RC0) - auto-service-1.0-rc3.jar (com.google.auto.service:auto-service:1.0-rc3) Alternatively, set android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true to continue with previous behavior. Note that this option is deprecated and will be removed in the future. See https://developer.android.com/r/tools/annotation-processor-error-message.html for more details.
解决方法:
在app的build.gradle中添加如下部分:
android { compileSdkVersion 26 buildToolsVersion '26.0.2' defaultConfig { applicationId "com.example.app" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" //添加部分 javaCompileOptions { annotationProcessorOptions { includeCompileClasspath true } } } }
坑三:不显示本地文件
weex不显示本地文件,
可以直接写绝对路径 ip+端口号/src/存放图片文件夹/图片名称
然后在安卓和ios分别的解决方法是:
Android 的实现方法
图片存储的目录
res/drawable-hdpi
下面会提到,为何要存储到这里
引用方法
<img src="local:///test" style="width: 300; height: 300;">
不需要加图片的文件名后缀
注意:是三个斜杠,不是两个,否则会报错
E/weex: Local src format is invalid.
如果你是在google play上上架。建议mdpi,hdpi,xhdpi,xxhdpi每一种都放一套(ldpi就算了,基本看不到),因为google play会根据不同的手机density来打不同的apk包(举个栗子,如果是hdpi的机器,下载下来的就只有hdpi的资源) 如果是在国内的市场话。建议只放一套(h或者xhpdi),因为国内市场是没有上面那种机制的,放多套资源会导致安装包变得很大。 此外: Android在没有找到相应dpi的图片时,会用其他density的图片进行缩放处理。因此会损失一些性能和内存(作为缩放的buffer使用)
参考 Android 开发中 drawable 有必要放多套分辨率的图片资源吗?
iOS 的实现方法
图片存储目录
main bundle
引用方法
<img src="local://test.png" style="width: 300; height: 300;">
我这里直接报 domain error, 而不是 no resource found 说明并没有走最新的 SDK 代码流程。
最终在官方文档 Path (英) | Weex 的相对路径部分找到了解决方法。
即使用相对路径即可!
xcode 最傻逼的地方是,需要手动将图片拖动到项目的 resource 目录。 需要自动添加的方案!!!
先手动在 resource 目录新建一个 images 目录,然后,不断往这个目录里新增图片文件即可。
同步解决(感觉不够完美)
图片的同步
npm run copy:xx 解决
package.json
"scripts": { "build": "webpack", "dev": "npm run build && webpack --watch", "copy:android": "mkdir -p platforms/android/app/src/main/assets/dist/; cp dist/index.weex.js platforms/android/app/src/main/assets/dist/index.js; cp src/images/*.* platforms/android/app/src/main/res/drawable-hdpi/", "copy:ios": "mkdir -p platforms/ios/bundlejs/ && cp dist/index.weex.js platforms/ios/bundlejs/index.js && cp src/images/*.* platforms/ios/images/", "copy": "npm run copy:android && npm run copy:ios", "serve": "serve -p 1337", "test": "echo \"Error: no test specified\" && exit 1" },
实现代码
src/mixins/index.js
// 获取图片在三端上不同的路径 // e.g. 图片文件名是 test.jpg, 转换得到的图片地址为 // - H5 : http: //localhost:1337/src/images/test.jpg // - Android : local:///test // - iOS : ../images/test.jpg get_img_path(img_name) { let platform = weex.config.env.platform let img_path = '' if (platform == 'Web') { img_path = `http://localhost:1337/src/images/${img_name}` } else if (platform == 'android') { // android 不需要后缀 img_name = img_name.substr(0, img_name.lastIndexOf('.')); img_path = `local:///${img_name}` } else { img_path = `../images/${img_name}` } return img_path }
使用方法
<image style="width: 120px; height: 120px;" :src="get_img_path('test.png')"></image>
坑4:
页面跳转问题
https://blog.****.net/blog_lee/article/details/79820130详情可参考
新增uril.js,在需要的页面调用代码如下
export function getEntryUrl(name) {
//判断当前的环境,适配web端
if (weex.config.env.platform === "Web") {
// return './' + name + '.html'
} else {
let arr = weex.config.bundleUrl.split('/');
arr.pop();
arr.push(name + '.js');
console.log(arr.join('/'))
return "local://" + arr.join('/');
}
}
使用方法:
navigator.push({
// url: "./detail.html",
url: getEntryUrl("login"),
animated: "true"
})
Android相关代码
- 创建一个Activity,如下图所示位置以及代码
package com.weex.app.util; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import com.taobao.weex.IWXRenderListener; import com.taobao.weex.WXSDKInstance; import com.taobao.weex.common.WXRenderStrategy; import com.taobao.weex.devtools.common.LogUtil; import com.weex.app.R; import java.util.HashMap; import java.util.Map; /** * Created by lee on 29/03/2018. */ public class Network extends AppCompatActivity implements IWXRenderListener { private WXSDKInstance mWXSDKInstance; private FrameLayout mContainer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_network); mContainer = (FrameLayout) findViewById(R.id.container); mWXSDKInstance = new WXSDKInstance(this); mWXSDKInstance.registerRenderListener(this); String RenderPageUrl = ""; if (getIntent().getData() != null) { String navUrl = getIntent().getData().toString(); if (null != navUrl) { LogUtil.e(navUrl); RenderPageUrl = navUrl; } else { LogUtil.e("a is null"); } } else { LogUtil.e("get data is null"); } Map<String, Object> options = new HashMap<>(); options.put(WXSDKInstance.BUNDLE_URL, RenderPageUrl); mWXSDKInstance.renderByUrl("WXSample", RenderPageUrl, options, null, WXRenderStrategy.APPEND_ONCE); } @Override protected void onStart() { super.onStart(); if (mWXSDKInstance != null) { mWXSDKInstance.onActivityStart(); } } @Override protected void onResume() { super.onResume(); if (mWXSDKInstance != null) { mWXSDKInstance.onActivityResume(); } } @Override protected void onPause() { super.onPause(); if (mWXSDKInstance != null) { mWXSDKInstance.onActivityPause(); } } @Override protected void onStop() { super.onStop(); if (mWXSDKInstance != null) { mWXSDKInstance.onActivityStop(); } } @Override protected void onDestroy() { super.onDestroy(); if (mWXSDKInstance != null) { mWXSDKInstance.onActivityDestroy(); } } @Override public void onViewCreated(WXSDKInstance instance, View view) { if (view.getParent() != null) { ((ViewGroup) view.getParent()).removeView(view); } mContainer.addView(view); } @Override public void onRenderSuccess(WXSDKInstance instance, int width, int height) { } @Override public void onRefreshSuccess(WXSDKInstance instance, int width, int height) { } @Override public void onException(WXSDKInstance instance, String errCode, String msg) {
- 创建Activity布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_network" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"></FrameLayout> </RelativeLayout>
- 修改manifest.xml-添加拦截器
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.weex.app"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/> <uses-feature android:name="android.hardware.camera"/> <uses-feature android:name="android.hardware.camera.autofocus"/> <application android:name="com.weex.app.WXApplication" android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" tools:overrideLibrary="com.taobao.android.dexposed" tools:replace="android:allowBackup"> <activity android:name="com.weex.app.SplashActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name" android:screenOrientation="portrait" android:theme="@style/FullscreenTheme"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name="com.weex.app.WXPageActivity" android:label="@string/app_name" android:screenOrientation="portrait"> </activity> <activity android:name=".util.Network"> <intent-filter> <action android:name="com.taobao.android.intent.action.WEEX" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.taobao.android.intent.category.WEEX" /> <action android:name="android.intent.action.VIEW" /> <data android:scheme="http" /> <data android:scheme="https" /> <data android:scheme="file" /> <data android:scheme="network" /> </intent-filter> </activity> <activity android:name="com.journeyapps.barcodescanner.CaptureActivity" android:screenOrientation="portrait" tools:replace="screenOrientation"/> </application> </manifest>
坑5
android7 权限问题:
https://blog.****.net/jupiterxx/article/details/80026909
你的项目就可以用 weex run android运行在安卓手机上了...
网上好多说需要找到本地资源文件夹下的地址即可跳转.. 在android中.. 确实发现assets文件夹下有这些界面的js文件..
只要用 file://assets/dist/在安卓中找到这个文件跳转就可以了.. 然后再次发现是无效的.. 但是发现跳转http和https在手机上是有效的.. 那么也就是说隐式调用没有拦截到这file这个请求..
但是明明在android项目的manifast里是已经在拦截file的.. 最后发现确实file是拦截不到的.. 其他什么关键字都可以.. 那么怎么办呢.. 最后我只能在地址前面加上别的关键字local 但是让隐式拦截local关键字.. 最后再activity里再把local给去掉 用file地址去渲染..
最终获取三端对应跳转地址的方法 (注意安卓中前面添加了local是为了给拦截)
- getJumpBaseUrl(toUrl) {
- var bundleUrl = weex.config.bundleUrl;
- var isnav = true
- bundleUrl = new String(bundleUrl);
- var nativeBase;
- var native;
- var isAndroidAssets = bundleUrl.indexOf('file://assets/') >= 0;
- var isiOSAssets = bundleUrl.indexOf('file:///') >= 0 && bundleUrl.indexOf('WeexDemo.app') > 0;
- if (isAndroidAssets) {
- nativeBase = "local://" + 'file://assets/dist/';
- native = nativeBase + toUrl + ".js";
- } else if (isiOSAssets) {
- nativeBase = bundleUrl.substring(0, bundleUrl.lastIndexOf('/') + 1);
- native = nativeBase + toUrl + ".js";
- } else {
- var host = 'localhost:8081';
- var matches = /\/\/([^\/]+?)\//.exec(bundleUrl);
- if (matches && matches.length >= 2) {
- host = matches[1];
- }
- //此处需注意一下,tabbar 用的直接是jsbundle 的路径,但是navigator是直接跳转到新页面上的.
- if (typeof window === 'object') {
- nativeBase = 'http://' + host + '/';
- } else {
- nativeBase = 'http://' + host + '/';
- }
- native = nativeBase + toUrl + ".html";
- }
- return native;
- }
android manifest 中拦截local
- <activity
- android:name="com.weex.app.WXPageActivity"
- android:label="@string/app_name"
- android:screenOrientation="portrait"
- >
- <intent-filter>
- <action android:name="android.intent.action.VIEW"/>
- <action android:name="com.alibaba.weex.protocol.openurl"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="com.taobao.android.intent.category.WEEX"/>
- <data android:scheme="http"/>
- <data android:scheme="https"/>
- <data android:scheme="local"/>
- </intent-filter>
- </activity>
android java code中把local头给去掉 拿file地址去渲染
把local关键字给替换掉.. 然后其他的和原来操作一样