React Native开发速记
文章目录
- 引子
- React Native适用场景
- React基础
- JSX
- 组件的定义
- 高阶组件(HOC)
- 生命周期函数与组件渲染
- 组件的缺省属性
- 组件之间的通信
- 基础API
- Flex弹性布局
- 例子: Flex布局实现多行多列
- 和Web CSS上FlexBox的不同之处
- 常用UI组件
- React Hook
- 主要的Hook
- useState用法
- useEffect典型用法
- useRef与forwardRef
- 使用Redux
- 和原生模块交互
- Realm数据库
- 调试
- 命令行
- 多个jsBundle异步加载
- 打包
- Expo
- 其它工具
- UI框架
- 推荐阅读的开源项目
- 参考资源
引子
软件开发,移动优先;移动开发,RN优先。为什么?RN的性能胜任大部分应用场景,开发效率奇高(前提当然是熟悉web和javascript),热更新,快速见效,出活快,画一个界面三言两语,比原生简洁多了。Flutter? Xamarin? NativeScript? Ionic? Cordova? 我还是选RN。有人担心RN不流行了,会被替代,大可不必,哪种技术能长命百岁?一个产品有三五年的周期就算长寿了。RN短期内死不了。新的JS引擎 Hermes也极大地改进了性能。
经过一段时间的使用,笔者发现:RN代码好写,但小问题不少,一方面是RN本身的问题,另一方面文档不细。
React常见的问题是状态管理,例如嵌套组件之间的状态传递。setState()后数据未及时更新,不得不利用useEffect()做数据的提交。
React Native适用场景
大部分应用都可以采用RN,新闻资讯类自不必说,电商类,教育类,视频播放类,也不在话下。CPU密集型?游戏类?这个我没有尝试过。但就像Java和C++/C的关系一样,80~90%都可以由Java搞掂,剩下的C来搞掂。你能吃下90%的业务部分,你还担心什么?
React基础
TODO
JSX
JSX 是 React 的核心组成部分,它使用 XML 标记的方式去直接声明界面。JSX貌似丑陋,和JS代码耦合,但其背后的哲学是每个HTML都是由有生命的片段组合而成,有了对片段的良好编程,就能由简到繁,积小成大,去构建更庞大的应用。
JSX的一大好处就是容易组合:
function func1() {return <View>func1</View>
}
function func2() {return <View>func2</View>
}
function func3() {// 多行要用小括号包起来!return (<View>{func1()}{func2()}</View>)
}
这样就比较容易构造复杂的组件。
一个典型的写法:
function doRender() {return (<View>{list.map(item => (<Section item={item} />))}</View>)
}
组件的定义
组件:拥有props和state,和render()函数的对象。
单向数据流(one-way flow of data)是React程序的一大特点。
组件有两种写法:类组件和函数式组件。
类的写法:
class Welcome extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>;}
}
函数写法:
function Welcome(props) { const data = [{ id: 1, name: "John Doe" },{ id: 2, name: "Victor Wayne" },{ id: 3, name: "Jane Doe" },];return (<View><Text>Hello, {props.name}</Text><View className="users">{data.map((user) => (<Text className="user">{user}</Text>))}</View></View>);
}
函数组件就是可以返回一个ReactElement的函数。
高阶组件(HOC)
React高阶组件(HOC), 是一个参数为组件,返回值也是一个组件的函数。
生命周期函数与组件渲染
几个常用的生命周期函数:componentDidMount,componentDidUpdate 和 componentWillUnmount,在组件不同阶段被调用。
渲染:通过Javascript API修改DOM对象谓之渲染,如document.write、Node.appendChild或Element.setAttribute等DOM编程接口。
当VDOM更新了,React将其与之前的VDOM快照进行比较,然后仅更新真实DOM中发生更改的内容。如果没有任何改变,真正的DOM根本不会更新。
React DevTools允许我们在Components -> View Settings -> Highlight updates中设置渲染时高亮更新。这将展示所有虚拟渲染。
如果我们想看原生重渲染,我们需要在Chrome DevTools中三个点的菜单-> More tools -> Rendering -> Paint flashing。
每当组件的状态发生变化时,React都会调度一次渲染。调度渲染并不意味着渲染过程会立即发生。 React将尝试为此找到最佳时机去执行这个操作。
通常采用setState函数来渲染组件。
props发生了变化,未必会导致组件渲染。而且,不要直接改变props对象,如this.props.user.name = ‘Felix’;
因为这不会触发任何更改,并且React不会注意到这些更改。
组件的缺省属性
react 提供了语法糖:defaultProps,可以实现属性的混合。
import React from 'react'
// 函数组件
export default function FuncDefault(props) {console.log(props);//已经完成了混合return (<div>a:{props.a},b:{props.b},c:{props.c}</div>)
}
//属性默认值
FuncDefault.defaultProps = {a: 1,b: 2,c: 3
}// 类组件
export default class ClassDefault extends React.Component {constructor(props) {super(props);console.log(props);}
}
ClassDefault.defaultProps = {a:1, b:2, c:3
}
组件之间的通信
- 父组件传属性给子组件,属性变,子组件也跟着变。
- 父组件通过useRef()获得子组件的引用,然后调用子组件的方法。
- useImperativeHandle(ref,createHandle,[deps])可以自定义暴露给父组件的实例值。如果不使用,父组件的ref(chidlRef)访问不到任何值(childRef.current==null)
- useImperativeHandle应该与forwradRef搭配使用;React.forwardRef会创建一个React组件,这个组件能够将其接受的ref属性转发到其组件树下的另一个组件中。
React.forward接受渲染函数作为参数,React将使用prop和ref作为参数来调用此函数。 - 子组件想通知父组件,可以通过调用父组件传递过来的函数,这个函数也是通过props传递的。
- 任意两个组件之间通过消息进行通信。
// 发送方
DeviceEventEmitter.emit('sendMsg', {text:'Hello'});
// 接收方
DeviceEventEmitter.addListener('sendMsg',function(params){// do something with the params
});
基础API
ReactNode是一个联合类型:
type ReactNode =| ReactChild| ReactFragment| ReactPortal| boolean| null| undefined;
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
Flex弹性布局
定理:采用Flex加上嵌套能实现任意布局
react 宽度基于pt为单位, 可以通过Dimensions 来获取宽高,PixelRatio 获取密度。运行在Android上时,View的长和宽被解释成:100dp 100dp单位是dp,字体被解释成16sp 单位是sp,运行在iOS上时尺寸单位被解释称了pt,这些单位确保了布局在任何不同dpi的手机屏幕上显示不会发生改变
基于flex的布局
- view默认宽度为100%
- 水平居中用alignItems, 垂直居中用justifyContent
- 基于flex能够实现现有的网格系统需求,且网格能够各种嵌套无bug
图片布局
- 通过Image.resizeMode来适配图片布局,包括contain, cover, stretch
- 默认不设置模式等于cover模式
- contain模式自适应宽高,给出高度值即可
- cover铺满容器,但是会做截取
- stretch铺满容器,拉伸
定位
- 定位相对于父元素,父元素不用设置position也行
- padding 设置在Text元素上的时候会存在bug。所有padding变成了marginBottom
文本元素
- 文字必须放在Text元素里边
- Text元素可以相互嵌套,且存在样式继承关系
- numberOfLines 需要放在最外层的Text元素上,且虽然截取了文字但是还是会占用空间
例子: Flex布局实现多行多列
容器: flex-wrap: wrap; 允许折行
元素有三种方式:
- flex: 1 0 33.3%;
- flex-basis: 33.3%;
- width: 33.3%;
和Web CSS上FlexBox的不同之处
- flexDirection: React Native中flexDirection:'column’对应Web CSS的flex-direction:‘row’
- alignItems: React Native中alignItems:'stretch’对应Web CSS的align-items:‘flex-start’
- flex: 相比Web CSS的flex接受多参数,如:flex: 2 2 10%;,但在 React Native中flex只接受一个参数
- 不支持属性:align-content,flex-basis,order,flex-basis,flex-flow,flex-grow,flex-shrink
常用UI组件
RN UI 组件 | Android View | iOS View |
---|---|---|
View | ViewGroup | UIView |
Text | TextView | UITextView |
Image | ImageView | UIImageView |
ScrollView | ScrollView | UIScrollView |
TextInput | EditText | UITextField |
??? | RecyclerView | UITableView |
1、View是最核心、最常用的UI组件,包罗万象。几乎总是和{flex:1}搭配,否则空白。
2、FlatList长列表组件,可以实现下拉刷新和上拉加载。FlatList没有对应的原生组件,其实现是基于ScrollView。VirtualizedList是FlatList和SectionList的底层实现。FlatList和SectionList都依赖一个数组,每个元素是个字典,SectionList的字典必须有data、title、key。注意:不要在ScrollView里使用FlatList。开源世界还有个FlashList,据说是FlatList替代品,性能更高。
3、WebView,显示html文档。
4、导航器
5、富文本编辑器: wxik/react-native-rich-editor和https://github.com/imnapo/react-native-cn-richtext-editor
UI基本示例:
<View style={{flex: 1}} ><ScollView style={{flexGrow: 1}} nestedScrollEnabled={true}><View><SafeAreaView><ScrollView horizontal style={{width: '100%', height: 'your_height'}}>{data.map((item, index) => (<View key={index}>// Your component</View>))}</ScrollView> -- Horizontal Scroll --</SafeAreaView></View> </ScrollView>
</View>
React Hook
Hooks 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hooks 是一种在函数式组件中使用有状态函数的方法。Hooks不支持在class中使用,比如在class中使用useState和useEffect都是不允许的。
Hooks 只能在顶层调用,不要在循环,条件或嵌套函数中调用 Hook。原因是 React 需要保证每次组件渲染的时候都以相同的顺序调用 Hooks。
主要的Hook
- useEffect()函数:作用就是指定一个副效应函数,组件每渲染一次,该函数就自动执行一次。组件首次在网页 DOM 加载后,副效应函数也会执行。它的第二个参数,使用一个数组指定副效应函数的依赖项,只有依赖项发生变化,才会重新渲染。
- useState()函数:用于为函数组件引入状态。
- useContext():用于在组件之间共享状态。
- useReducer():React 本身不提供状态管理功能,通常需要使用外部库。这方面最常用的库是 Redux。Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的状态,Reducer 函数的形式是(state, action) => newState。
- useImperativeHandle(): 快速对外暴露接口
Hook函数 | 说明 |
---|---|
useState | [<取值>, <设值>] = useState(<初始值>),useState可以保存状态,在状态改变的时候重新渲染视图,而普通变量会被重新赋值,无法保存状态 |
useEffect | 在组件加载和监听的对象值发生变化时调用;第二个参数是数组,只要其中一个发生变化,React 都会执行指定函数 |
useImperativeHandle | 对外暴露接口 |
useContext | 多层嵌套组件之间共享状态,无论它们在组件树中的深度如何 |
useLayoutEffect | |
useReducer | const [state, dispatch] = useReducer(reducer, initialArg, init) |
useCallback | 在依赖不变的情况下不返回新的函数地址而返回旧的函数地址。在往子组件传入了一个函数并且子组件被React.momo缓存了的时候使用 |
useMemo | 用于进行计算属性的计算,当依赖列表变化时重新计算,类似与Vue的computed()函数 |
useRef | 返回一个mutable ref对象,用.current获得其值。每次渲染 useRef 返回值都不变;ref.current 发生变化并不会造成 re-render; |
useTransition | |
useDeferredValue | |
useId |
useState用法
- useState的更新是异步的,数据不会立即刷新的。其背后是队列实现的。通过useEffect()可以观察到其值的变化。
- 仅顶层调用,不能在循环,条件,嵌套函数等中调用useState()。
- 通过set方法修改状态时,只有引用发生变化,才会导致组件重绘。此点非常关键,下面针对几种情形分别举例。
修改对象:
const [user, setUser] = useState({username: "",password: "",})
const handleChange = (e) => setUser(prevState => ({...prevState, [e.target.name]: e.target.value}))
不要直接:
setUser({...prevState, [e.target.name]: e.target.value})
修改数组:
const [todos, setTodos] = useState([])
setTodos(preState => ([{id: todos.length,text: text}, ...preState]));
修改boolean数组:
const [boolState, setBoolState] = useState(Array(10).fill(false)) //初始数组为长度为10的布尔数组
const toogleState = (index) => {setBoolState(prevState => prevState.map((item, idx) => idx === index ? !item : item))
};
修改ES6的Map:
const [myMap, setMyMap] = useState(new Map());
const updateMap = (k,v) => {setMyMap(new Map(myMap.set(k,v)));
}
useEffect典型用法
const [data, setData] = useState()
useEffect(() => {// declare the async data fetching functionconst fetchData = async () => {// get the data from the apiconst data = await fetch(`https://yourapi.com?param=${param}`);// convert the data to jsonconst json = await response.json();// set state with the resultsetData(json);}// call the functionfetchData()// make sure to catch any error.catch(console.error);;
}, [param])
再举一个例子:
import { useState, useEffect } from 'react';const HackerNewsStories = () => {const [stories, setStories] = useState(null);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {const fetchStories = async () => {try {const data = await (await fetch('https://hn.algolia.com/api/v1/search_by_date?tags=front_page&hitsPerPage=20')).json();setStories(data.hits.sort((story, nextStory) => (story.points < nextStory.points ? 1 : -1)));setError(null);} catch (err) {setError(err.message);setStories(null);} finally {setLoading(false);}};fetchStories();}, []);return (<div className="wrapper"><h2>Latest HN Stories</h2>{loading && <div>HackerNews frontpage stories loading...</div>}{error && <div>{`Problem fetching the HackeNews Stories - ${error}`}</div>}<div className="stories-wrapper">{stories &&stories.map(({ objectID, url, title, author, points }) => (title && url &&<div className='stories-list' key={objectID}><h3><a href={url} target="_blank" rel="noreferrer">{title}</a> - By <b>{author}</b> ({points} points)</h3></div> ))}</div></div>);
};export default HackerNewsStories;
useRef与forwardRef
使用函数式组件时,需要注意useRef的使用。
Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
const InputText = React.forwardRef((props, ref) => (<input ref={ref} {...props} />
));
import * as React from "react";
import ReactDOM from "react-dom";const InputText = React.forwardRef((props, ref) => (<input ref={ref} {...props} />
));export default function App() {const ref = React.useRef();function focus() {ref.current.focus();}return (<div className="App"><InputText ref={ref} placeholder="my input" /><button onClick={focus}>Focus</button></div>);
}const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
使用Redux
Redux首先是一种设计模式,然后才有各种实现库。解决什么问题呢?主要针对复杂的应用,这些应用中有大量的各种状态,行为,操作方,为了管理这些状态和操作,便诞生了redux。
基本处理过程是:
1、界面事件产生action,action是type+payload,然后发送出去(dispatch),如dispatch({type: ‘counter/increment’})
2、store运行reducer函数处理action,处理完返回一个新的state
3、store通知所有订阅该store的UI,UI根据新的state重新绘制
翻译成代码示例:
// 1) 使用 `createStore` 函数创建 Redux store
const store = Redux.createStore(counterReducer)// 2) 订阅更新(以便将来数据更改时能够重绘)
store.subscribe(render)// 我们的“用户界面”是单个 HTML 元素中的一些文本
const valueEl = document.getElementById('value')// 3) 当订阅回调函数运行时:
function render() {// 3.1) 获取当前 store 的 stateconst state = store.getState()// 3.2) 提取你想要的数据const newValue = state.value.toString()// 3.3) 使用新值更新 UIvalueEl.innerHTML = newValue
}// 4) 使用初始 state 渲染 UI
render()// 5) 基于 UI 输入 dispatch action
document.getElementById('increment').addEventListener('click', function () {store.dispatch({ type: 'counter/incremented' })
})
总而言之,UI基于State, state存放在store中,通过redux函数修改state,通过dispatch函数发送state,这是React和Redux的基本哲学,也是我们使用React的要点。设计好你的state,整个应用程序所需的全局 state 应该放在 Redux store 中。只在一个组件内使用的 state 应该放在组件 state 中。
组件和Store的如何关联?
- 使用useSelector(),每次调用useSelector会从store中返回state数据。且store中state数据发生变化,会通知到组件
- 使用Provider。使用 <Provider store={store}> 组件包裹 <App>,这样使其他 React 组件可以和 store 进行交互
- 使用connect()函数。
const mapStateToProps = state => {return {notes: state.notes}
}// connect with redux,first param is map and second is component
export default connect(mapStateToProps)(AddNote)
和原生模块交互
在使用 React 做开发时有两种写法:
类组件
函数组件 + Hook (用的这种)
React 组件之间共享数据的方案:
使用 React 自带的 Context + Reducer 功能 (用的这种)
优点:无须引入其他的包
缺点:只能向子组件及子孙组件中共享数据
使用 Redux 实际组件之间的共享
优点:数据全都放到 Redux 中管理,无论什么层级都直接使用
缺点:需要单独安装,数据状态由它统一管理,很多代码写法不太一样
RN 中的布局
View 组件默认是相对定位的(可以直接使用 left 、top 相对于原来的位置定位)
View 组件的绝对定位都是相对于父组件定位的(因为父组件都是相对定位,所以默认都是子绝父相)
没有浮动,只能 flex
父组件一定要设置高度或者 flex:1 ,否则高度为0页面空白
调用原生模块方法:
要实现一个自己的ReactPackage和ReactModule,在ReactModule中暴露js方法。
Realm数据库
目前主要有Realm、Firebase、PouchDB、Async Storage、WCDB(腾讯开源)与SQLite等。
推荐使用Realm,realm已经被MongoDB收购,成为移动端首选。
Realm使用基础
安装: yarn add realm @realm/react
文件真机路径: data/data/包名/files/xxx.realm
工具: Realm Studio,能查看realm文件中的数据
参考:https://github.com/mongodb/template-app-react-native-todo
realm有三个钩子函数: useRealm(), useObject()和useQuery(),其中useQuery()能随着后台数据的刷新而刷新。
调试
调试方面强烈推荐使用 React Native Debugger,一个基于 React Native 官方调试方式、包含 React Inspector / Redux DevTools 独立应用:
- 基于官方的 Remote Debugger 且提供了更为丰富的功能
- 包含 react-devtools-core 的 React Inspector
- 包含 Redux DevTools, 且与 redux-devtools-extension 保持 API 一致
命令行
- react-native
通过 react-native bundle 命令可对当前 RN 项目进行打包,以 iOS 为例
react-native bundle --platform ios --dev false \--entry-file index.js \--bundle-output __test__/example/index.ios.jsbundle \--assets-dest __test__/example/
多个jsBundle异步加载
每个ReactApplication可以加载一个js bundle,如果将index.android.bundle拆成多个包,就得写多个ReactApplication。
每个ReactApplication对应一个ReactActivity,每个ReactActivity对应一个MainComponentName,即在js里注册的组件名称。
打包
不要用Android Studio打包,直接用命令:
./gradlew assembleRelease
需要在gradle配置文件中添加签名配置
Expo
Expo是解决什么问题的?是为跨平台服务的,就是你只管写纯JS的应用,它帮你构建部署到手机上,Android或iOS。它需要在你手机上安装一个应用Expo Go,然后你开发的Expo应用就被Expo Go托管,就像小程序和微信的关系。
Expo is an open-source platform for making universal native apps that run on Android, iOS, and the web. It includes a universal runtime and libraries that let you build native apps by writing React and JavaScript.
Expo包含一个命令行工具Expo CLI和一个移动客户端Expo Go。在开发机器上,你只需要按照Nodejs环境即可,无需Android SDK和xcode这些编译环境。
Expo Go只是给开发阶段用的,最后要发布产品时还得用Expo提供的工具打成apk和ipa包。
你可以在https://snack.expo.dev/上试用一下简单的例子。
其它工具
- Watchman, Watchman 在更改时观察文件和记录,然后触发相应的操作,并由 React Native 在内部使用
UI框架
其实RN提供的基本UI组件已经能实现大部分界面,但还有很多第三方UI库可供选择。
- react native elements
- react-native-elements
- 蚂蚁金服 Ant Design Mobile RN
- Lottie for React Native
- NativeBase
- React Native Material UI
- react-native-ui-kitten
- 雷数科技前端部门组件库/工具库
推荐阅读的开源项目
- F8: ReactNative作者们写的Conference App
- 在线订餐系统YumMeals
- 一款电影APP
- github客户端
- 流媒体音频app MindCast
- iReading阅读器
- https://github.com/stage88/react-weather
- https://github.com/wwayne/react-native-nba-app
- https://github.com/iSimar/HackerNews-React-Native
- https://github.com/ljunb/react-native-iShiWuPai
- https://github.com/BelinChung/react-native-hiapp
参考资源
- https://github.com/jondot/awesome-react-native
- https://github.com/reactnativecn/react-native-guide
- Redux
- 下载 Genymotion免费版
- 多终端开发
- 矢量图标库
- js bridge的一个实现
- devio技术博客
- RNStudyNotes
- react-native-sqlite-storage
- Expo官网
React Native开发速记相关推荐
- 【转】【React Native开发】
[React Native开发]React Native控件之ListView组件讲解以及最齐全实例(19) [React Native开发]React Native控件之Touchable*系列组 ...
- windows 下配置 react native 开发环境
windows 下配置 react native 开发环境 安装nvm 由于react native 需要使用 NodeJs 4.0以上版本,为了方便切换NodeJs,首先我们需要安装nvm. 你可以 ...
- React Native开发之必备React基础
为了帮助大家快速上手React Native开发,在这本节中将向大家介绍开发React Native所需要的一些React必备基础知识. 概述 本节课将从React的特点.如何使用React.JSX语 ...
- React Native开发错误警告处理总结(已解决 !持续更新)
注:本文是我在开发过程中遇到问题解决方法的总结,之后会持续更新,希望帮助到更多的学习者.文中有不妥的地方希望指出共同学习,同时欢迎大神补充.(之后我会放出自己开发整理的笔记和GithubDemo地址, ...
- Windows环境下安装React Native开发环境----记一次填坑过程
前言 集成React Native开发环境遇到各个坑,调试了很久出现过找不到设备,百度上个别人的资料有误被绕来绕去耽误了很多时间,下载慢等待时间过长等问题,最后多方查阅资料,电脑重启等操作终于部署好了 ...
- iOS新知识学习之React Native开发工具集
本文整理了React Native iOS开发过程中有用的工具.服务.测试.库以及网站等. 工具 你可以选择不同的开发环境:DECO.EXPO或者你可以使用Nuclide+Atom,目前我使用EXPO ...
- React Native开发之——Webstorm开发RN配置
前言 前文React Native开发之--Webstorm快捷开发介绍了使用Webstorm快捷开发React Native, 本文介绍Webstorm开发RN配置. Webstorm开发RN配置 ...
- 在windows上搭建React Native开发环境
最近要学习React Native,但是在window上搭建开发环境的时候遇到了些问题,以至于一直没有搭建好开发环境. React Native相关项目及文档: react-native的GitHub ...
- React Native开发(一)
本React Native讲解专题:主要讲解了React Native开发,由基础环境搭建配置入门,基础,进阶相关讲解. 关于React Native各种疑难杂症,问题深坑总结方案请点击查看: Mac ...
最新文章
- python基础考核试题及答案
- 【Python学习系列五】Python网络爬虫框架Scrapy环境搭建
- 深度学习核心技术精讲100篇(十)-机器学习模型融合之Kaggle如何通过Stacking提升模型性能
- Linkis1.0用户使用文档:JAVA和SCALA调用Linkis的接口示例代码
- SpringBoot_入门-环境准备
- 【华为云技术分享】大前端的自动化工厂— babel
- Safe Or Unsafe(hdu2527)哈弗曼VS优先队列
- pymol安装教程linux,PyMOL | Pymol绘图教程(一)
- 解决Robot Framework运行时没有Log的方案
- 1*1的卷积核与Inception
- JavaScript高级程序设计 第4版(中文高清)扫描版
- TCP客户端与服务端
- Gps开发实战——卫星数量获取
- October CMS Vs Wordpress
- 数据结构与程序设计——C++语言描述(Data Structures Program Design in C++) by Robert L.Kruse Alexander J.Ryb
- 目前UI设计和前端哪个行业更好,女生应该怎么抉择?
- Python网络编程自动化(HCIA)
- 张曦予巴黎时装周儿童单元T台走秀演绎童真风采
- gis与计算机科学之间的联系,GIS地理信息系统课程论文 地理信息系统与其他学科的关系及应用前景...
- 2021-03-27 深度信念网络(DBN)学习笔记