混合开发入门 Vue结合Android/iOS开发仿京东项目App

三端把后台数据进行处理并且优雅的展示给用户
混合 Naitive 跟 web 技术进行开发移动应用

一.目前主流的混合开发方案

  1. faceBook :React、ReactNative
  2. 阿里: Week
  3. google:flutter
  4. 小程序开发
  5. 移动应用混合开发框架
  6. PWA(Porgress Web App)
    本课程:使用Vue框架 配合 android + iOS 的跨语言通信原理
    项目效果:http://imooc.hybrid.lgdsunday.club/
    需要掌握内容:Scss Vue全家桶 vue-cli3 脚手架、Vue动画 webpack打包、组件化思想、
    混合开发原理 跨语言通信机制
    前端项目的部署

二.混合开发原理

混合开发又叫 Hybrid App,混合了Native 和 web技术进行开发的应用
方案:

  1. 基于 WebView UI (JSBridge)的方案 主流方案:淘宝、微信、饿了么等,使用JSBridge进行与原生的通信,使用webView 进行页面渲染,本课程使用

  2. 基于 Native UI (ReactNative、weex):赋予web原生能力基础上,通过JSBridge 将JS 解析成虚拟节点树,来传递Native ,并使用Native进行渲染

  3. 小程序方案(微信、支付宝):对JSbridge 进行定制,各类JS逻辑与UI渲染层,形成了特殊的开发环境,加强web与Native 的融合程度,提升渲染效果。

三.Hybrid App 的技术原理

  1. Hybrid App 的本质

    在原生App中 使用 webView作为容器,来承载一个web页面
    (苹果要求原生页面与web页面的比重最少为2:8)上架结果取决于交互体验。

Hybrid App的核心:原生与 web端的双向通信层(跨语言解决方案)JSBridge

什么是 JSBridge : 一座用javaScript搭建起来的桥梁,一端是web,一端是Native

目的: 让Native 可以调用web的javaScript 代码,让web可以调用Native 的原生代码
展示架构:
/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成63)/截屏2021-07-28 下午5.23.58.png
2. Android 与 Web 通讯

(1). 配置X5WebView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
private void init (Context context) {
this.mContext = context;

/**
* 基础配置
*/
initWebViewSettings();
initWebViewClient();
initChromeClient();

/**
* 构建 JSBridge 对象,这里提供的 JSBridge 字符串会被挂载到
* 网页中的 window 对象下面。
*
* 在网页中我们可以使用 window.AndroidJSBridge 拿到这个android 注入到web端的对象
*/
addJavascriptInterface(
new MyJaveScriptInterface(mContext, this),
"AndroidJSBridge");


}

/**
* 对 webview 进行基础配置
*/
private void initWebViewSettings () {
WebSettings webSettings = getSettings();
/**
* 允许加载的网页执行 JavaScript 方法
*/
webSettings.setJavaScriptEnabled(true);
/**
* 设置网页不允许缩放
*/
webSettings.setSupportZoom(false);
webSettings.setBuiltInZoomControls(false);
webSettings.setDisplayZoomControls(true);
/**
* 设置网页缓存方式为不缓存,方便我们的调试
*/
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
}

/**
* 设置 webviewClient ,如果不进行这层设置,则网页打开默认会使用
* 系统中的浏览器进行打开,而不是在本 APP 中进行打开。
*/
private void initWebViewClient () {
setWebViewClient(new WebViewClient(){
});
}

/**
* 监听网页中的url加载事件
*/
private void initChromeClient () {
setWebChromeClient(new WebChromeClient(){

/**
* alert()
* 监听alert弹出框,使用原生弹框代替alert。
*/
@Override
public boolean onJsAlert(WebView webView, String s, String s1, JsResult jsResult) {

AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage(s1);
builder.setNegativeButton("确定", null);
builder.create().show();
jsResult.confirm();

return true;
}
});
}

(2). 初始化使用腾讯X5内核webView控件 X5WebView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 public class MainActivity extends AppCompatActivity {

private X5WebView mWebView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

init();
}

/**
* 初始化 webview
*/
private void init () {
mWebView = findViewById(R.id.web_view);
mWebView.loadUrl(Constants.WEB_URL);
}


/**
* 原生端调用 web 方法,方法必须是挂载到 web 端 window 对象下面的方法。
* 调用 JS 中的方法:onFunction1
*/
public void onJSFunction1 (View v) {
mWebView.evaluateJavascript("javascript:onFunction('android调用JS方法')", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setMessage(s);
builder.setNegativeButton("确定", null);
builder.create().show();
}
});
}
}

(3). 定义一些 Native 的接口供JS 调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MyJaveScriptInterface {

private Context mContext;
private X5WebView mWebView;

public MyJaveScriptInterface(Context context, X5WebView x5WebView) {
this.mContext = context;
this.mWebView = x5WebView;
}

/**
*
* window.AndroidJSBridge.androidTestFunction1('xxxx')
* 调用该方法,APP 会弹出一个 Alert 对话框,
* 对话框中的内容为 JavaScript 传入的字符串
* @param str android 只能接收基本数据类型参数
* ,不能接收引用类型的数据(Object、Array)。
* JSON.stringify(Object) -> String
*/
@JavascriptInterface
public void androidTestFunction1 (String str) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage(str);
builder.setNegativeButton("确定", null);
builder.create().show();
}

/**
* 调用该方法,方法会返回一个返回值给 javaScript 端
* @return 返回值的内容为:"androidTestFunction2方法的返回值"
*/
@JavascriptInterface
public String androidTestFunction2 () {
return "androidTestFunction2方法的返回值";
}

}

注意点:
需要安装 npm 跟 node,然后安装 http-server(简单的,命令行 http服务器) : 提供给客户端一个可访问的地址。
安装命令npm install http-server -g
使用:

  • cd index.html 目录下
  • 执行命令http-server,得到运行地址,
  • 拿到地址后 拷贝到浏览器地址,运行(在 android 9.0 的设备上,如果要加载 http 协议的网页,那么需要对 app 进行安全访问设置。))
  • res/xml/network_security_config.xml 进行配置
    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
    <domain-config cleartextTrafficPermitted="true">
    <!-- 访问 网页的 IP 地址 -->
    <domain includeSubdomains="true">192.168.0.100</domain>
    </domain-config>
    </network-security-config>
  1. IOS 与 Web 通讯

原理: 通过webView 组件 向window中 注入JSBridge 对象,web通过这个对象 调用原生的方法。

  1. 两端与Web通讯的相互对比
    相同点:
  • 都是通过webView来完成网页的加载的

  • 都是通过向window 注入对象的方式来提供可被web端调用的方法

  • 都可以调用 Web端挂载到window对象下的方法
    不同点

  • 注入对象的不同:Android 可提供注入对象的名称,IOS 固定为webkit

  • JS 调用 Native 方式不同:面向 Android 可直接获取注入对象,调用方法。面向 IOS 为相对固定写法(window.webkit.messageHandlers.方法名.postMessage(入参对象))

  • 传递数据格式不同:面向 Android 只能接受 基本数据 类型数据。面向 IOS 可以接受任意类型数据。

  • 返回值不同:面向 Android 可以直接接收返回值。面向 IOS 没有办法直接获取返回值(可以通过回调方法的方式进行)。

三.Hybrid App前段基础知识点

1. Flex布局

子项目属性

  • order:项目在容器中的排列顺序,数值越小排名越靠前。
  • flex-grow:通过一个数值来定义项目的放大比例,默认为0,即存在剩余空间也不进行放大
  • align-self:使一个项目与其他项目在交叉轴上拥有不同的对齐方式。

2. Scss预处理器

  • scss 是sass 3.0后的称呼,强化了CSS的辅助工具,在Css的语法那个增加了额外的功能(嵌套、变量、运算、函数)
  • 它无法被浏览器直接识别,webpack 已经帮我们转成CSS了。

(1)嵌套

1
2
3
<div class="box">
<p classs="box-desc"> 测试scss</p>
</div>

scss

1
2
3
4
5
6
.box {
&-box {

}
}

& 符号 表示其直接父标签,& == box

(2) 变量

  • 用来存储 css 中复用的一些信息
    定义一个变量:$titleSize:32px; 使用$符号 直接定义变量。在scss 中直接使用
1
2
3
&-box {
font-size:$titleSize;
}

(3)函数运算

  • 允许用户定义函数,计算想要的结果

定义函数需要@符号,变量值使用 $标记

1
2
3
4
@function doublePx ($px) {
@return $px *2 + px;
}
$titleSize: doublePx(16);

使用

1
2
3
&-box {
font-size:$titleSize;
}

其他关于Scss 去官网查看

3. Webpack 模块打包器

  • 是JavaScript 应用程序的静态模块打包器
  • 把开发时的多个模块合并成一个或者指定的几个文件

webpack 官方图
/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成75)/截屏2021-08-09 下午4.39.05.png

浏览器在加载我们代码文件时,不可能下载多个模块或文件,这样或增加浏览器请求次数,延缓页面加载时间,
并且还会有sass文件不被浏览器解析,这时就用到了webpack,用来打包这些资源模块。
把开发时候的各种文件打包成有限的几个模块。

4. @vue/cli3 脚手架

  • 基于webpack 构建的,带有基础webpack配置脚手架的工具
  • 可以用 vue/cli 快速生成一个 Vue 项目的基础结构

使用 @vue/cli3

  • npm 安装 @vue/cli3:(sudo) npm install @vue/cli -g 全局安装
  • vue create 项目名称,直接创建Vue 项目
    实际安装创建
    /var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成76)/截屏2021-08-09 下午4.49.12.png
    (1) 输入命令行后创建Vue项目
  • 默认配置
  • 手动选择配置
    (2)选择手动
    /var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成77)/截屏2021-08-09 下午4.50.27.png

手动选择项目所依赖的模块(按空格选择)

  • ’Babel‘:必选 帮助我们把JS代码降级,可以把ES6代码编译成ES5
  • ’TypeScript‘: 不选
  • ’PWA‘ : Processive Web App Support:渐进式的增强Web应用 不选
  • ’Router‘ : Vue 中的路由 选择
  • ’Vuex‘ : Vue 中的状态管理器 选择
  • ’CSS Pre-process‘ : 选择
  • ‘Linter / Formatter’:代码风格检测器 保留 选择
  • ’Unit Testing‘ 测试相关
  • ‘E2E Testing’ 测试相关

(3) 是否选择
/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成78)/截屏2021-08-09 下午4.56.09.png
’Use history mode for routur?‘ 选择Y,生产环境下配置争取的服务器地址

(4)选择代码风格的配置 选择 ’ESlint+Standard config‘
/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成79)/截屏2021-08-09 下午4.59.11.png

(5) 选择代码检测,是否在保存 还是 提交的时候,选择保存时候
/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成80)/截屏2021-08-09 下午5.00.28.png

(6)
这些依赖工具的配置文件 是希望各自拥有一个 还是 统一处理,选择第一个
/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成82)/截屏2021-08-09 下午5.01.25.png
(7)是否保存为 预设配置,Y、N

/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成83)/截屏2021-08-09 下午5.02.21.png

预设可添加名字
/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成84)/截屏2021-08-09 下午5.03.11.png

(8)cd 到项目下, 通过’npm run serve‘ 进行启动项目,根据路径进行访问。一个本地路径,一个局域网路径

/var/folders/75/0xr1m5c13f7b5qsm139djwjmwm3q06/T/TemporaryItems/(screencaptureui正在存储文稿,已完成85)/截屏2021-08-09 下午5.05.02.png

5.rem兼容性设置

我们想要一套代码设配多种不同像素的设备。
达到效果:

相同的文字、图片大小,在不同的设备上应该展示不同的像素值

使用 rem:相对于标签 fontSize 大小的单位

  1. 根据屏幕宽度定义根元素fontSize大小
  2. 定义一个函数,把像素转换我rem。 输入像素,转换为rem

fontSize 计算规则

  1. 动态计算fontSize 大小
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // ES6 语法,直接原先需要在支持ES6 的浏览器中,比如chrome
    // 定义最大的 fontSize,避免屏幕分辨率过大导致 fonSize 过大
    const MAX_FONT_SIZE = 42;
    //监听 HTML文档被解析完成的事件
    document.addEventListener('DOMContentLoaded',() => {
    // 获取 html 标签
    const html = document.querySelector('html');
    // 屏幕的宽度除以10,获取根元素 fontSize 标准
    let fontSize = window.innerWidth / 10;
    // 获取到的fontSize 标准不允许超过我们定义的最大值
    fontSize = fontSize > MAX_FONT_SIZE ? MAX_FONT_SIZE : fontSize;
    // 定义根元素 fontSize 的大小
    html.style.fontSize = fontSize + 'px';
    });

  2. 函数转换 px -> rem
    1
    2
    3
    4
    5
    6
    7
    /* 如果 设计图以 iPhone 6、6S、7、8  375* 667逻辑像素为设计基础
    定义预计根元素 fontSize */
    $rootFontSize: 375 / 10;
    /* 定义像素转换为 rem 函数 */
    @function px2rem ($px) {
    @return $px / $rootFontSize + rem;
    }

npm run serve 后报错:

报错 Failed to resolve loader: sass-loader 。webpack 需要使用这个sass-loader 工具去转换scss文件。

安装:npm install sass-loader node-sass --save-dev 安装 sass-loadernode-sass
--save-dev :开发环境