什么是移动App开发:通俗的理解,就是把开发Web网站的技术(HTML+CSS+JS),通过某种方式,移植到移动App开发上进行使用,这种利用Web开发技术进行移动端开发体验的方式,叫做混合移动App开发!
- 优点:流畅、稳定、基本上一些App都可以脱网运行,用户体验好;
- 缺点:不能跨平台
- 优点:可以跨平台(浏览器天生就是跨平台的)
- 缺点:没有App流畅、不稳定,受限于网速和网络
只能做页面
-> Ajax前后台数据交互
-> Jquery、Bootstrap
-> webApp -> 三大框架
-> 可以做手机混合App/桌面应用
-> 可以做手机原生App
-> 将来或许可以发射火箭发射卫星发射导弹
-> 终极目标:统一全宇宙
根据需求搞设计,根据设计做开发
Angular, Vue, React 这三个都是前端框架,我们在进行混合App开发的时候,只是用到了这三个框架的【基础语法】而已;
Ionic, Weex, ReactNatvie 这三个都是打包工具(提供了相关的命令行,只要运行指定的命令,就能够把项目打包成一个手机App出来),能够把我们开发出来的应用,最终打包成一个可安装的手机端程序安装包;同时,这三个东西,也提供了好用的一些小组件,方便我们去构建移动App的用户界面;
API地址
Hbuilder这个工具,是一个在线打包工具,使用很方便,不需要在本地配置开发环境;直接将做好的网站,通过一些简单的操作,就能在线打包为一个App出来;
好处:本地不用配置开发环境;操作方便,对于程序员来说不关心打包的过程,打包过程对于我们来说是透明的;
缺点:程序员很少能干预打包的过程;源代码被提交到了云端的服务器,存在项目核心代码被泄露的风险;
作用:将需要全局使用的工具或者应用程序,配置到Path环境变量中,可以很方便的通过命令行的形式,在任何想要运行这些应用程序的地方,运行它们;
JAVA_HOME
的系统环境变量,值为C:\Program Files (x86)\Java\jdk1.8.0_112
,也就是安装JDK的根目录Path
,在Path
之后新增%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
CLASSPATH
,值为.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
javac
,如果能出现javac的命令选项,就表示配置成功!注意:需要安装最新的长期稳定版本,不要实验版本;安装完毕之后的node.js会自动配置到全局系统环境变量中
安装完毕后,可以输入node -v
查看node版本号;
大多数情况下操作系统自带C++环境,不需要手动安装C++环境;
如果运行报错,则需要手动安装visual studio中的C++环境;
Git安装完毕后,会自动配置到系统环境变量中;
可以通过运行git --version
来检查是否正确安装和配置了Git的环境变量;
Add Python to path
,这样才能自动将Python安装到系统环境变量中;python
,检查是否成功安装了python。installer_r24.3.4-windows.exe
,最好手动选择安装到C盘下的android目录android-25
、android-23
(react-native必须依赖这个)解压后,放到platforms
文件夹下platform-tools
,放到platform-tools
文件夹下tools
,放到安装根目录中build-tools_r23.0.1-windows.zip(react-native必须依赖这个)
、build-tools_r23.0.2-windows.zip(weex必须依赖这个)
和build-tools_r23.0.3-windows.zip
,并将解压出来的文件夹,分别改名为版本号23.0.1
、23.0.2
和23.0.3
;在安装目录中新建文件夹build-tools
,并将改名为版本号之后的文件夹,放到新创建出来的build-tools
文件夹下extras
文件夹,在extras
文件夹下新建android
文件夹;解压m2responsitory
文件夹和support
文件夹,放到新建的extras -> android
文件夹下ANDROID_HOME
,值为android SDK Manager的安装路径C:\Users\liulongbin\AppData\Local\Android\android-sdk
,紧接着,在Path中新增;%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools;
npm config set registry https://registry.npm.taobao.org --global
npm config set disturl https://npm.taobao.org/dist --global
npm install -g yarn react-native-cli
yarn config set registry https://registry.npm.taobao.org --global
yarn config set disturl https://npm.taobao.org/dist --global
react-native init AwesomeProject
创建React-Native项目cd AwesomeProject
切换到项目根目录中,运行adb devices
来确保有设备连接到了电脑上react-native run-android
打包编译安卓项目,并部署到模拟器或开发机中adb devices
查看当前接入的设备列表,打包好的文件,放到了android\app\build\outputs\apk
目录下问题1:开启悬浮框权限;
问题2:Could not get BatchedBridge, make sure your bundle is packaged correctly
解决方案:在终端中,进入到项目的根目录,执行下面这段命令行:
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/
运行之前,需要确保android/app/src/main/
目录下有assets
文件夹,如果没有,手动创建之~,再运行上面的命令;
问题3:could not connect to development server
解决方案:晃动手机,唤起设置属性窗口,点击“Dev settings”,再点击Debuug server host 出现设置ip地址窗口,填写Ip地址和端口号8081,例如192.168.1.111:8081
npm install -g weex-toolkit
安装Weex 官方提供的 weex-toolkit
脚手架工具到全局环境中weex create project-name
初始化Weex项目weex platform add android
安装android模板,首次安装模板时,等待时间较长,建议fq安装模板android studio
中的安卓模拟器
,或者将启用USB调试的真机
连接到电脑上,运行weex run android
,打包部署weex项目webpack.publish.config.js
,将webpack.config.js
的配置拷贝过去,剔除一些开发配置项即可:devServer
节点删掉:devServer: {
hot: true,
open: true,
port: 4321
}
plugins
节点下的热更新插件删掉:new webpack.HotModuleReplacementPlugin()
url-loader
,将图片放入统一的images文件夹之下:{ test: /\.(png|jpg|gif)$/, use: 'url-loader?limit=43959&name=images/[name].[ext]' }
或者使用img-
前缀加上7位的hash名称
:
{ test: /\.(png|jpg|gif)$/, use: 'url-loader?limit=43959&name=images/img-[hash:7].[ext]' }
package.json
中的script节点下新增dev
命令,通过--config
指定webpack启动时要读取的配置文件:"pub": "webpack --config webpack.publish.config.js"
cnpm i clean-webpack-plugin --save-dev
var cleanWebpackPlugin = require('clean-webpack-plugin');
plugins
节点下使用这个插件:new cleanWebpackPlugin(['dist'])
webpack.publish.config.js
entry: {
app: path.resolve(__dirname, 'src/js/main.js'), // 自己代码的入口
vendors: ['jquery'] // 要分离的第三方包的入口
}
new webpack.optimize.CommonsChunkPlugin({ // 抽离第三方包的插件
name:'vendors', // 指定抽离第三方包的入口名称
filename:'vendors.js' // 抽离出的公共模块的名称
})
html-webpack-plugin
在生成index.html
文件的时候,会自动将抽离的第三方包引入进去!在plugins数组中添加:
new webpack.optimize.UglifyJsPlugin({ // 优化压缩JS
compress:{
warnings:false // 移除警告
}
}),
new webpack.DefinePlugin({ // 设置为产品上线环境,进一步压缩JS代码
'process.env.NODE_ENV': '"production"'
})
在plugins
节点下的htmlWebpackPlugin
插件中,添加minify
子节点:
minify:{// 压缩HTML代码
collapseWhitespace:true, // 合并空白字符
removeComments:true, // 移除注释
removeAttributeQuotes:true // 移除属性上的引号
}
其他优化项请参考:html-minifier - github
npm install --save-dev extract-text-webpack-plugin
安装抽取CSS文件的插件。const ExtractTextPlugin = require("extract-text-webpack-plugin");
{
test: /\.css$/, use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader"],
publicPath: '../' // 设置图片路径
})
},
{
test: /\.scss$/, use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ['css-loader', 'sass-loader'],
publicPath: '../' // 设置图片路径
})
}
new ExtractTextPlugin("css/styles.css"), // 抽取CSS文件的插件
cnpm i optimize-css-assets-webpack-plugin --save-dev
安装插件到开发依赖。var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
new OptimizeCssAssetsPlugin() // 压缩CSS文件的插件
cnpm i react react-dom -S
安装包// 1. 在 React 学习中,需要安装 两个包 react react-dom
// 1.1 react 这个包,是专门用来创建React组件、组件生命周期等这些东西的;
// 1.2 react-dom 里面主要封装了和 DOM 操作相关的包,比如,要把 组件渲染到页面上
import React from 'react'
import ReactDOM from 'react-dom'
// 2. 在 react 中,如要要创建 DOM 元素了,只能使用 React 提供的 JS API 来创建,不能【直接】像 Vue 中那样,手写 HTML 元素
// React.createElement() 方法,用于创建 虚拟DOM 对象,它接收 3个及以上的参数
// 参数1: 是个字符串类型的参数,表示要创建的元素类型
// 参数2: 是一个属性对象,表示 创建的这个元素上,有哪些属性
// 参数3: 从第三个参数的位置开始,后面可以放好多的虚拟DOM对象,这写参数,表示当前元素的子节点
// 这是一个div
var myH1 = React.createElement('h1', null, '这是一个大大的H1')
var myDiv = React.createElement('div', { title: 'this is a div', id: 'mydiv' }, '这是一个div', myH1)
// ReactDOM.render('要渲染的虚拟DOM元素', '要渲染到页面上的哪个位置中')
// 注意: ReactDOM.render() 方法的第二个参数,和vue不一样,不接受 "#app" 这样的字符串,而是需要传递一个 原生的 DOM 对象
ReactDOM.render(myDiv, document.getElementById('app'))
cnpm i babel-preset-react -D
,然后再 .babelrc
中添加 语法配置;<
那么就把它当作 HTML代码去编译,如果遇到了 {}
就把 花括号内部的代码当作 普通JS代码去编译;class
属性了,那么,必须写成className
,因为 class
在ES6中是一个关键字;和class
类似,label标签的 for
属性需要替换为 htmlFor
.class Person extends React.Component{
// 通过报错提示得知:在class创建的组件中,必须定义一个render函数
render(){
// 在render函数中,必须返回一个null或者符合规范的虚拟DOM元素
return
这是用 class 关键字创建的组件!
;
}
}
用构造函数创建出来的组件,和用class创建出来的组件,这两种不同的组件之间的本质区别就是:有无state属性!!!
有状态组件和无状态组件之间的本质区别就是:有无state属性!
CommentList = [
{ user: '张三', content: '哈哈,沙发' },
{ user: '张三2', content: '哈哈,板凳' },
{ user: '张三3', content: '哈哈,凉席' },
{ user: '张三4', content: '哈哈,砖头' },
{ user: '张三5', content: '哈哈,楼下山炮' }
]
理解React中虚拟DOM的概念
理解React中三种Diff算法的概念
使用JS中createElement的方式创建虚拟DOM
使用ReactDOM.render方法
使用JSX语法并理解其本质
掌握创建组件的两种方式
理解有状态组件和无状态组件的本质区别
理解props和state的区别
组件创建阶段:生命周期函数,有一个显著的特点:组件一生只执行一次;
组件运行阶段:这些函数,也有显著的特点: 一生会根据属性props 和 状态 state 的改变,有选择性的触发0次或多次;
组件销毁阶段:这些函数,也有显著的特点:一生只执行一次;
vue中的生命周期图
React Native 中组件的生命周期
在组件创建之前,会先初始化默认的props属性,这是全局调用一次,严格地来说,这不是组件的生命周期的一部分。在组件被创建并加载候,首先调用 constructor 构造器中的 this.state = {},来初始化组件的状态。
React生命周期的回调函数总结成表格如下:
组件生命周期的执行顺序:
cnpm i prop-types --save
setState()
方法
// 在事件中绑定this并传参
handleMsg1(arg1, arg2) {
console.log(this);
// 此时this是个null
this.setState({
msg: '在事件中绑定this并传参:' + arg1 + arg2
});
}
// 修改构造函数中的代码:
this.handleMsg2 = this.handleMsg2.bind(this, '', '');
// 在构造函数中绑定this并传参
handleMsg2(arg1, arg2) {
this.setState({
msg: '在构造函数中绑定this并传参:' + arg1 + arg2
});
}
{ this.handleMsg3('', '') }} />
// 用箭头函数绑定this并传参
handleMsg3(arg1, arg2) {
this.setState({
msg: '用箭头函数绑定this并传参:' + arg1 + arg2
});
}
v-model
指令,将表单控件和我们的data
上面的属性进行双向数据绑定,数据变化和页面之间的变化是同步的!state
之上的数据同步到界面的控件上,但是不能默认实现把界面上数据的改变,同步到state
之上,需要程序员手动调用相关的事件,来进行逆向的数据传输! {/*只要将value属性,和state上的状态进行绑定,那么,这个表单元素就变成了受控表单元素,这时候,如果没有调用相关的事件,是无法手动修改表单元素中的值的*/}
// 这是文本框内容改变时候的处理函数
handleTextChange = () => {
this.setState({
msg: this.refs.txt.value
});
}
setState的一个问题
:// 保存最新的state状态值,在保存的时候,是异步地进行保存的,所以,如果想要获取最新的,刚刚保存的那个状态,需要通过回掉函数的形式去获取最新state
this.setState({
msg: this.refs.txt.value
// msg: e.target.value
}, function () {
// 获取最新的state状态值
console.log(this.state.msg);
});
记住一串单词组合getChildContextTypes
前3个、后3个、后两个
一个方法、两个静态属性
类型校验
Animation Add-Ons
移动端 关于 键盘将input 框 顶上去的解决思路—个人见解
app.use('*', function (req, res, next) {
// 设置请求头为允许跨域
res.header("Access-Control-Allow-Origin", "*");
// 设置服务器支持的所有头信息字段
res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
// 设置服务器支持的所有跨域请求的方法
res.header("Access-Control-Allow-Methods", "POST,GET");
// next()方法表示进入下一个路由
next();
});
定义:就是一个异步的代码规范;
好处:
更好的帮我们解决回调地狱问题
能帮我们很好的实现代码的复用
运行npm install antd --save
安装ant design
导入相关组件:
import { DatePicker } from 'antd';
import 'antd/dist/antd.css';
运行cnpm i babel-plugin-import --save-dev
修改.babelrc
文件:
{
"presets":["es2015", "stage-0", "react"],
"plugins":[
"transform-runtime",
["import", { "libraryName": "antd", "style": "css" }]
]
}
Link:是相当于超链接一般的存在;点击Link,跳转到相应的路由页面!负责进行路由地址的切换!
Route:是路由匹配规则,当路由地址发生切换的时候,就会来匹配这些定义好的Route规则,如果有能匹配到的路由规则,那么,就会展示当前路由规则所对应的页面!
Route:除了是一个匹配规则之外,还是一个占位符,将来,此Route所匹配到的组件页面,将会展示到Route所在的这个位置!
// 其中path指定了路由匹配规则,component指定了当前规则所对应的组件
注意:react-router中的路由匹配,是进行模糊匹配的!可以通过Route
身上的exact
属性,来表示当前的Route
是进行精确匹配的
可以使用Redirect
实现路由重定向
// 导入路由组件
import {Route, Link, Redirect} from 'react-router-dom'
ANT DESIGN 一个 UI 设计语言
react-router-dom
豆瓣电影API地址
正在热映 - in_theaters
即将上映 - coming_soon
top250
电影详细信息 - subject
跨域资源共享 CORS 详解 - 阮一峰
Request - Simplified HTTP client
CSS3 transform 属性
ES6 - Promise规范 - 阮一峰
刘龙彬 - 博客园 - Javascript中Promise的简单使用
Javascript 中的神器——Promise
MDN - Fetch API
MDN - Response
fetch-jsonp - 支持JSONP的Fetch实现
搭建基本的开发环境 - 英文官网
搭建基本的开发环境 - 中文
这两篇文档对比着进行参考,进行相关的安装;
adb devices
的命令,这个命令,是安卓开发环境提供的;开发者模式
豌豆荚
这样的工具,让这些工具帮助你在电脑上安装手机的驱动;react-native init 项目名称
来初始化一个react native项目;开发者调试模式
;可以使用adb devices
来查看当前链接到电脑上的手机设备列表!react-native run-android
来打包当前项目,并把打包好的项目以调试的模式安装到手机中!##修改项目首屏页面
if (this._reactInternalInstance){
// 组件没有被卸载
}
注意:使用图标,需要使用
Android SDK Manager
安装Android SDK Build-tools 26.0.1
并接收其 license;
https://api.douban.com/v2/movie/in_theaters
https://api.douban.com/v2/movie/subject/26309788
npm i react-native-router-flux --save
https://github.com/aksonov/react-native-router-flux
https://github.com/aksonov/react-native-router-flux/blob/master/docs/API.md
https://github.com/aksonov/react-native-router-flux/blob/v3/docs/MINI_TUTORIAL.md
https://github.com/leecade/react-native-swiper?utm_source=tuicool&utm_medium=referral
npm i react-native-swiper --save
安装轮播图组件import Swiper from 'react-native-swiper';
showsPagination={false}
是用来控制页码的;showsButtons={false}
是用来控制左右箭头显示与隐藏;height={160}
是用来控制轮播图区域的高度的!var styles = StyleSheet.create({
wrapper: {},
slide1: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#9DD6EB',
},
slide2: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#97CAE5',
},
slide3: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#92BBD9',
},
image:{
width:'100%',
height:'100%'
}
})
react-native-image-picker的github官网
react native 之 react-native-image-picke的详细使用图解
npm install react-native-image-picker@latest --save
安装到项目运行依赖,此时调试可能会报错,如果报错,需要使用下面的步骤解决:node_modules
文件夹npm i
npm start --reset-cache
react-native link
自动注册相关的组件到原生配置中android
->app
->src
->main
->AndroidManifest.xml
文件,在第8行添加如下配置:
android
->app
->src
->main
->java
->com
->当前项目名称文件夹
->MainActivity.java
文件,修改配置如下:package com.native_camera;
import com.facebook.react.ReactActivity;
// 1. 添加以下两行:
import com.imagepicker.permissions.OnImagePickerPermissionsCallback; // <- add this import
import com.facebook.react.modules.core.PermissionListener; // <- add this import
public class MainActivity extends ReactActivity {
// 2. 添加如下一行:
private PermissionListener listener; // <- add this attribute
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "native_camera";
}
}
// 第1步:
import {View, Button, Image} from 'react-native'
import ImagePicker from 'react-native-image-picker'
var photoOptions = {
//底部弹出框选项
title: '请选择',
cancelButtonTitle: '取消',
takePhotoButtonTitle: '拍照',
chooseFromLibraryButtonTitle: '选择相册',
quality: 0.75,
allowsEditing: true,
noData: false,
storageOptions: {
skipBackup: true,
path: 'images'
}
}
// 第2步:
constructor(props) {
super(props);
this.state = {
imgURL: ''
}
}
// 第3步:
// 第4步:
cameraAction = () => {
ImagePicker.showImagePicker(photoOptions, (response) => {
console.log('response' + response);
if (response.didCancel) {
return
}
this.setState({
imgURL: response.uri
});
})
}
```
6. 一定要退出之前调试的App,并重新运行react-native run-android
进行打包部署;这次打包期间会下载一些jar的包,需要耐心等待!
keytool -genkey -v -keystore my-release-key2.keystore -alias my-key-alias2 -keyalg RSA -keysize 2048 -validity 10000
my-release-key.keystore
表示你一会儿要生成的那个 签名文件的 名称【很重要,包找个小本本记下来】-alias
后面的东西,也很重要,需要找个小本本记下来,这个名称可以根据自己的需求改动my-key-alias
C:\Users\liulongbin\my-release-key2.keystore
android
-> gradle.properties
文件,在最后,添加如下代码:MYAPP_RELEASE_STORE_FILE=your keystore filename
MYAPP_RELEASE_KEY_ALIAS=your keystore alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****
...
android {
...
defaultConfig { ... }
+ signingConfigs {
+ release {
+ storeFile file(MYAPP_RELEASE_STORE_FILE)
+ storePassword MYAPP_RELEASE_STORE_PASSWORD
+ keyAlias MYAPP_RELEASE_KEY_ALIAS
+ keyPassword MYAPP_RELEASE_KEY_PASSWORD
+ }
+}
buildTypes {
release {
...
+ signingConfig signingConfigs.release
}
}
}
...
android
文件夹,在当前目录打开终端,然后输入./gradlew assembleRelease
开始发布APK的Release版;android\app\build\outputs\apk
目录中,找到app-release.apk
,这就是我们发布完毕之后的完整安装包;就可以上传到各大应用商店供用户使用啦;注意:请记得妥善地保管好你的密钥库文件,不要上传到版本库或者其它的地方。