移动端开发
移动端适配方案
媒体查询
1 | @media screen and (max-width: 960px) { |
百分比布局
难统一,使用较少
- 计算困难,如果我们要定义一个元素的宽度和高度,按照设计稿,必须换算成百分比单位。
- 各个属性中如果使用百分比,相对父元素的属性并不是唯一的。比如 width 和 height 相对于父元素的 width 和 height,而 margin、padding 不管垂直还是水平方向都相对比父元素的宽度、border-radius 则是相对于元素自身等等,造成我们使用百分比单位容易使布局问题变得复杂。
flex 弹性盒子布局
rem + 动态 font-size
针对不同的屏幕,设置 html 不同的 font-size
- 媒体查询:通过媒体查询来设置不同尺寸范围内的屏幕 html 的 font-size 尺寸,有以下缺点
- 需要针对不同的屏编写大量的媒体查询
- 如果动态改变尺寸,不会实时的进行更新
- 用 js 动态获取设备宽度:根据 html 的宽度计算出 font-size 的大小,并且设置到 html 上;监听页面的实时改变,并且重新设置 font-size 的大小到 html 上
- 利用第三方库 lib-flexible 动态 font-size
1 | <style> |
将原来要设置的尺寸,转化成 rem 单位
- less 的混合 scss 的函数
1 | .pxToRem(@px) { |
- postcss-pxtorem
1 | // postcss.config.js |
VSCode 插件: px to rem
Viewport 单位
vh、vw,相对于视口的单位,对比于 rem 的优点:
- 用去计算 html 的 font-size 大小,也不需要给 html 设置这样一个 font-size
- 不会因为设置 html 的 font-size 大小,而必须给 body 再设置一个 font-size,防止继承
- 因为不依赖 font-size 的尺寸,所以不用担心某些原因 html 的 font-size 尺寸被篡改,页面尺寸混乱
- vw 相比于 rem 更加语义化,1vw 刚好是 1/100 的 viewport 的大小
- 可以具备 rem 之前所有的优点
- less/scss 函数
1 | @vwUnit:3.75; |
- postcss-px-to-viewport
1 | // postcss.config.js |
Css 预处理器的 mixin 和函数
JSBridge
定义
- 以 JavaScript 引擎或 Webview 容器作为媒介,通过协定协议进行通信,实现 Native 端和 Web 端双向通信的一种机制
- 双向通信的通道
- JS 向 Native 发送消息: 调用相关功能、通知 Native 当前 JS 的相关状态等
- Native 向 JS 发送消息: 回溯调用结果、消息推送、通知 JS 当前 Native 的状态等
Webview
- WebView 是移动端提供的运行 JavaScript 的环境,是系统渲染 Web 网页的一个控件,可与页面 JavaScript 交互,实现混合开发
- WebView 是手机中内置了一款高性能 Webkit 内核浏览器,在 SDK 中封装的一个组件。不过没有提供地址栏和导航栏,只是单纯的展示一个网页界面。
- WebView 可以简单理解为页面里的 iframe 。原生 app 与 WebView 的交互可以简单看作是页面与页面内 iframe 页面进行的交互。就如页面与页面内的 iframe 共用一个 Window 一样,原生与 WebView 也共用了一套原生的方法
- webview 加载 url 过程:webview 存在一个初始化的过程。为了提升 init 时间,通常做法是 app 启动时初始化一个隐藏的 webview 等待使用,当用户点击需要加载 URL,直接使用这个 webview 来加载,从而减少 webview init 初始化时间。弊端就是带来了额外的内存开销
JSBridge 运行原理
目前主流的 JSBridge 实现中,都是通过拦截 URL 请求来达到 native 端和 webview 端相互通信的效果
主要流程
- 在 webview 侧和 native 侧分别注册 bridge,其实就是用一个对象把所有函数储存起来
- 在 webview 里面注入初始化代码:
- 创建一个名为 WVJBCallbacks 的数组,将传入的 callback 参数放到数组内
- 创建一个 iframe,设置不可见,设置 src 为
https://__bridge_loaded__
- 设置定时器移除这个 iframe
- 在 native 端监听 url 请求:
- 拦截了所有的 URL 请求并拿到 url
- 首先判断 isWebViewJavascriptBridgeURL,判断这个 url 是不是 webview 的 iframe 触发的,具体可以通过 host 去判断
- 继续判断,如果是 isBridgeLoadedURL,那么会执行 injectJavascriptFile 方法,会向 webview 中再次注入一些逻辑,其中最重要的逻辑就是,在 window 对象上挂载一些全局变量和 WebViewJavascriptBridge 属性
- 继续判断,如果是 isQueueMessageURL,那么这就是个处理消息的回调,需要执行一些消息处理的方法
webview 调用 native 能力
- native 端注册 JsBridge
- webview 侧创建 iframe,设置 src 为
__bridge_load__
- native 端捕获请求,注入 jsb 初始化代码,在 window 上挂载相关对象和方法
- webview 侧调用 callHandler 方法,并在 responseCallback 上添加 callbackId: responseCallback,并修改 iframe 的 src,触发捕获
- native 收到 message,生成一个 responseCallback,并执行 native 侧注册好的方法
- native 执行完毕后,通过 webview 执行 _handleMessageFromObjC 方法,取出 callback 函数,并执行
native 调用 webview 能力
native 可以直接调用 webview 注册的 JsBridge 方法,不需要通过触发 iframe 的 src 触发执行
- native 侧调用 callHandler 方法,并在 responseCallback 上添加 callbackId: responseCallback
- native 侧主动调用 _handleMessageFromObjC 方法,在 webview 中执行对应的逻辑
- webview 侧执行结束后,生成带有 responseId 的 message,添加到 sendMessageQueue 中,并修改 iframe 的 src 为
__wvjb_queue_message__
- native 端拦截到 url 变化,调用 webview 的逻辑获取到 message,拿到 responseId,并执行对应的 callback 函数
React Native
常见问题
RN 相对于原生 IOS、Android 有哪些优势
- 性能方面媲美原生 App
- 绝大部分代码同时适用 IOS/Android,一套代码两系统适用
- 使用 Javascript 编码,上手容易
- 组件式开发,易于管理维护,代码复用率高
- 代码更改后会自动刷新,节省等待时间
- 支持热更新,更新无需重新安装 App
调用 setState 之后发生了什么
- 将传入的参数对象与当前的状态合并,然后触发调和过程
- 在调和过程中 react 会根据新的状态以相对高效的方式构建 react 元素树
- react 会对新旧元素树进行 diff 算法计算出差异,然后根据差异进行最小化渲染
JS 如何与原生相互调用
- JS 调用原生方法
- 和原生约定好,通过原生劫持 JS 发出的请求进行原生调用
- webView 添加要调用的原生方法接口,直接调用
- 利用第三方库实现,如 Andriod 第三方库 JSBridge。安全便捷
- 原生调用 JS 方法
- 直接使用 webView.evaluateJavacript()实现
- 利用三方库,如 JSBridge 来实现
缓存用的是什么
- AsyncStorage 它是一个简单的、异步的、持久化的键值对存储系统,它对于 App 来说是全局的。可以用来替代 LocalStorage
- 官网推荐在此基础上封装一层,不要直接使用
- 在 IOS 上,AsyncStorage 在原生端的实现是把较小值存放在序列化的字典中,而把较大值写入单独的文件
- 在 Android 上,AsyncStorage 会尝试使用 RocksDB,或退而选择 SQLite
单页应用和多页应用
单页应用 (SPA) 只在初始化时加载主要资源,通过路由控制页面内容切换,提供流畅用户体验。多页应用 (MPA) 每次请求新页面都重新加载完整资源,适合内容丰富、SEO 要求高的应用。
区别
- 页面加载方式
- 单页应用:只在应用初始化时加载页面的主要资源,之后页面内容的切换通过异步加载实现,不会重新加载整个页面。
- 多页应用:每次用户请求新页面时,服务器都会返回一个完整的页面,包括新的 HTML、CSS 和 JavaScript。
- 页面切换
- 单页应用:页面切换时通常是通过路由进行控制,以及通过前端框架 (如 Vue Router) 来管理视图的变化,不会导致整个页面的重新加载。
- 多页应用:页面切换会触发整个页面的重新加载,因为每个页面都是独立的。
- 用户体验
- 单页应用:提供更流畅的用户体验,因为页面切换时无需等待整个页面的重新加载。
- 多页应用:可能存在页面切换时的延迟,因为需要重新加载整个页面。
- 开发复杂度
- 单页应用:相对于多页应用,单页应用通常需要更多的前端技术栈和复杂的路由管理。
- 多页应用:每个页面都是独立的,开发相对简单,但随着页面增多,维护成本可能会增加。
- SEO
- 单页应用:需要特殊处理才能更好地支持搜索引擎优化 (SEO) ,因为页面内容是动态加载的。
- 多页应用:每个页面都是独立的,更容易被搜索引擎索引。
单页应用的理解
SPA (Single Page Application) 单页面应用指的是在加载页面时,只需加载一次 HTML、CSS 和 JavaScript。在用户与应用程序交互时,页面不会重新加载,而是通过 AJAX 技术动态地更新页面内容。通常,SPA 通过路由管理来实现页面内容的切换,从而提供更流畅的用户体验。
优点
- 快速响应: 由于页面只在初始化时加载一次,之后的页面切换都是通过异步加载数据和更新 DOM,因此能够提供更快的响应速度。
- 良好的用户体验: SPA 能够提供类似原生应用的用户体验,避免了页面刷新带来的延迟,同时也避免了页面闪烁。
- 前后端分离: 前端负责 UI 和交互逻辑,后端则负责数据处理和接口的提供,使得开发更加清晰和高效。
- 减少服务器负担: 由于减少了页面的加载次数,可以减轻服务器的负担,提高服务器性能。
- 适合 Web 应用: 对于需要频繁交互和动态更新的 Web 应用来说,SPA 是一个非常合适的选择。
缺点
- 首次加载时间较长: 首次加载可能会包含大量的 JavaScript、CSS 和模板文件,导致首次加载时间较长。
- 代码分割 (Code Splitting)
- 懒加载 (Lazy Loading)
- 资源压缩和优化
- CDN 加速
- 预加载 (Preloading)
- 服务端渲染 (Server-Side Rendering)
- 性能监控与优化
- SEO 难度: 对于搜索引擎来说,由于内容都是通过 JavaScript 动态加载的,爬虫不易获取到完整的页面内容,影响 SEO 优化。
- 服务端渲染 (SSR) 或预渲染
- 动态路由和静态路由混合使用:对于一些静态内容,可以采用静态路由,这样搜索引擎爬虫更容易抓取这部分内容。对于需要动态加载的内容,可以采用动态路由,以保持良好的用户体验。
- 合理的 URL 结构
- 合理的元数据:meta 标签,包括 title、description 和关键字等
- Sitemap 和 Robots.txt:创建并提交 Sitemap,同时配置 Robots.txt 文件,以指导搜索引擎爬虫更有效地抓取和索引网站内容。
- 使用动态渲染服务
- 监控和测试
- 内存占用: 长时间运行的单页面应用可能会导致内存占用过多,特别是在移动设备上。
- 组件销毁与内存管理
- 懒加载与按需加载
- 虚拟列表与无限滚动
- 性能监控与优化
- 资源释放与缓存管理
- 定期升级与优化框架
- 安全性: 因为 SPA 通常需要从服务端一次性加载所有的代码,可能存在一些安全隐患。
- 客户端数据可被窃取
- 跨站脚本攻击 (XSS)
- 数据篡改
- 安全策略:在 SPA 中,需要特别注意跨域资源共享 (CORS) 和安全头部设置,以防止恶意站点利用客户端漏洞攻击服务端或其他站点。
针对以上问题,有以下改进措施
- 合理的权限控制: 对于敏感操作和数据,需要进行严格的权限控制,确保只有经过授权的用户才能访问和操作。
- 数据加密
- 输入验证与过滤
- 安全头部设置:使用适当的 HTTP 头部设置,如 Content Security Policy (CSP)、X-Content-Type-Options、X-XSS-Protection 等,以增强安全性。
- 定期安全审计
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Nanyin の 小屋!
评论