国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

從前端視角看 SwiftUI

開發 前端
從 UI 的角度來看,前端與手機開發會遇到問題是類似的,盡管使用的語言或是開發手法不盡相同,我們都需要打造一個易用的使用者介面。

前言

我對 iOS 開發、手機開發、SwiftUI 開發經驗有限,若有理解錯誤的部分歡迎指正。

從 UI 的角度來看,前端與手機開發會遇到問題是類似的,盡管使用的語言或是開發手法不盡相同,我們都需要打造一個易用的使用者介面。既然如此,彼此也會遇到類似的問題,元件化開發、狀態管理、資料流、管理副作用(API 或是IO)等等,對我來說是個很適合互相學習的領域。

從過往的經驗可以發現,像是 ReSwift[1](Redux 的中心思想)這樣的函式庫,或多或少也借鑒了前端不斷演進的開發手法,不難看出雙方會遇到的問題其實有類似的地方。

雖然這個時間點提起已經有點后話了,但還是想把我入門 SwiftUI 后的感想寫下。

SwiftUI 與 React 的類似之處

我們可以將前端框架歸納為幾個要素:

  • 元件化
  • 響應式機制
  • 狀態管理
  • 事件監聽
  • 生命周期

在下面的段落中,我們也會以這幾個主題為核心做討論。為了不模糊焦點,我會盡可能只用 React 當做舉例,但套用到其他前端框架原理應該也相同。

從 class 邁向 struct;從 class 邁向 function

在寫 SwiftUI 的時候總是讓我想到 React 的發展史。最初 React 建立元件的方式是透過 JavaScript 的 class 語法,每個 React 的元件都是一個類別。


class MyComponent extends React.Component {
constructor() {
this.state = {
name: 'kalan'
}
}

componentDidMount() {
console.log('component is mounted')
}

render() {
return <div>my name is {this.state.name}</div>
}
}

透過類別定義元件雖為前端元件化帶來了很大的影響,但也因為繁瑣的方法定義與 this 混淆,在 React16 hooks 出現之后,逐漸提倡使用 function component 與 hooks 的方式來建立元件。

省去了繼承與各種 OO 的花式設計模式,建構元件的心智負擔變得更小了。從 SwiftUI 當中我們也可以看到類似的演進,原本 ViewController 龐大的 class 以及職責,要負責 view 與 model 的互動,掌管生命周期,轉為更輕量的 struct,讓開發者可以更專注在 UI 互動上,減輕認知負擔。

元件狀態管理

React 16 采取了 hooks 來做元件的邏輯復用與狀態管理,例如 useState。

const MyComponent = () => {
const [name, setName] = useState({ name: 'kalan' })
useEffect(() => { console.log('component is mounted') }, [])

return <div>my name is {name}</div>
}

在 SwiftUI 當中,我們可以透過修飾符 @State 讓 View 也具有類似效果。兩者都具備響應式機制,當狀態變數發生改變時,React/Vue 會偵測改變并反映到畫面當中。雖然不知道 SwiftUI 背后的實作,但背后應該也有類似 diff 機制的東西來達到響應式機制與最小更新的效果。

然而 SwiftUI 的狀態管理與 React hooks 仍有差異。在 React 當中我們可以將 hook 拆成獨立的函數,并且在不同的元件當中使用,例如:

function useToggle(initialValue) {
const [toggle, set] = useState(initialValue)
const setToggle = useCallback(() => { set((state) => !state) }, [toggle])
useEffect(() => { console.log('toggle is set') }, [toggle])
return [toggle, setToggle]
}
const MyComponent = () => {
const [toggle, setToggle] = useToggle(false)
return <button onClick={() => setToggle()}>Click me</button>
}
const MyToggle = () => {
const [toggle, setToggle] = useToggle(true)
return <button onClick={() => setToggle()}>Toggle, but fancy one</button>
}

在 React 當中,我們可以將 toggle 的邏輯拆出,并在不同元件之間使用。由于 useToggle 是一個純函數,因此內部的狀態也不會互相影響。

然而在 SwiftUI 當中 @State 只能作用在 struct 的 private var 當中,不能進一步拆出。如果想要將重復的邏輯抽出,需要另外使用 @Observable 與 @StateObject 這樣的修飾符,另外建立一個類別來處理。

class ToggleUtil: ObservableObject {
@Published var toggle = false

func setToggle() {
self.toggle = !self.toggle
}
}
struct ContentView: View {
@StateObject var toggleUtil = ToggleUtil()
var body: some View {
Button("Text") {
toggleUtil.setToggle()
}
if toggleUtil.toggle {
Text("Show me!")
}
}
}

在這個例子當中把 toggle 的邏輯拆成一個 class 似乎有點小題大作了,不過仔細想想像 React 提供的 hook 功能,讓輕量的邏輯共用就算單獨拆成 hook 也不會覺得過于冗長,若要封裝更復雜的邏輯也可以再拆分成更多 hooks,從這點來看 hook 的確是一個相當優秀的機制。后來看到了 SwiftUI-Hooks[2],不知道實際使用的效果如何。

以 React 來說,在還沒有出現 hooks 之前,主要有三個方式來實作邏輯共用:

  • HOC(Higher Order Component)[3]:將共同邏輯包裝成函數后返回全新的 class,避免直接修改元件內部的實作。例如早期 react-redux 中的 connect。
  • render props[4]:將實際渲染的元件當作屬性(props)傳入,并提供必要的參數供實作端使用。
  • children function:children 只傳入必要的參數,由實作端自行決定要渲染的元件。

盡管 hooks 用更優雅的方式解決邏輯共用的問題,我認為上面的開發手法演變也很值得參考。

Redux 與 TCA

受到 Redux 的影響,在 Swift 當中也有部分開發者使用了采用了類似手法,甚至也有相對應的實作 ReSwift的說明文。從說明文可以看到主要原因。傳統的 ViewController 職責曖昧,容易變得肥大導致難以維護,透過 Reducer、Action、Store 訂閱來確保單向資料流,所有的操作都是向 store dispatch 一個action,而資料的改動(mutation)則在 reducer 處理。

而最近的趨勢似乎從 Redux 演變成了 TCA(The Composable Architecture),跟 Redux 的中心思想類似,更容易與 SwiftUI 整合,比較不一樣的地方在于以往涉及 side effect 的操作在 Redux 當中會統一由 middleware 處理,而在 TCA 的架構中 reducer 可以回傳一個 Effect,代表接收 action 時所要執行的 IO 操作或是 API 呼叫。

既然采用了類似 redux 的手法,不知道 SwiftUI 是否會遇到與前端開發類似的問題,例如 immutability 確保更新可以被感知;透過優化 subscribe 機制確保 store 更新時只有對應的元件會更新;reducer 與 action 帶來的 boilerplate 問題。

雖然 Redux 在前端仍然具有一定地位,也仍然有許多公司正在導入,然而在前端也越來越多棄用 Redux 的聲音,主要因為 redux 對 pure function 的追求以及 reducer、action 的重復性極高,在應用沒有到一定復雜程度之前很難看出帶來的好處,甚至連 Redux 作者本人也開始棄坑 redux 了 4。與此同時,react-redux 仍然有在持續更新,也推出了 redux-toolkit 來試圖解決導入 redux 時常見的問題。

取而代之的是更加輕量的狀態管理機制,在前端也衍生出了幾個流派:

  • GraphQL → 使用 apollo[5] 或是 relay[6]
  • react-query[7]
  • react-swr[8]
  • recoil[9]
  • jotai[10]

全域狀態管理

在全域狀態管理上,SwiftUI 也有內建機制叫做 @EnvrionmentObject,其運作機制很像 React 的 context,讓元件可以跨階層存取變數,當 context 改變時也會更新元件。

class User: ObservableObject {
@Published var name = "kalan"
@Published var age = 20
}
struct UserInfo: View {
@EnvironmentObject var user: User
var body: some View {
Text(user.name)
Text(String(user.age))
}
}
struct ContentView: View {
var body: some View {
UserInfo()
}
}
ContentView().envrionmentObject(User())

從上面這個范例可以發現,我們不需要另外傳入 user 給 UserInfo,透過 @EnvrionmentObject 可以拿到當前的 context。轉換成 React 的話會像這樣:

const userContext = createContext({})
const UserInfo = () => {
const { name, age } = useContext(userContext)
return <>
<p>{name}</p>
<p>{age}</p>
</>
}
const App = () => {
<userContext.Provider value={{ user: 'kalan', age: 20 }}>
<UserInfo />
</userContext.Provider>
}

React 的 context 可讓元件跨階層存取變數,當 context 改變時也會更新元件。雖然有效避免了 prop drilling 的問題,然而 context 的存在會讓測試比較麻煩一些,因為使用 context 時代表了某種程度的耦合。

響應機制

在 React 當中,狀態或是 props 有變動時都會觸發元件更新,透過框架實作的 diff 機制比較后反映到畫面上。在 SwfitUI 中也可以看到類似的機制:

struct MyView: View {
var name: String
@State private var isHidden = false

var body: some View {
Toggle(isOn: $isHidden) {
Text("Hidden")
}
Text("Hello world")

if !isHidden {
Text("Show me \(name)")
}
}
}

一個典型的 SwiftUI 元件是一個 struct,透過定義 body 變數來決定 UI。跟 React 相同,他們都只是對 UI 的抽象描述,透過比對資料結構計算最小差異后,再更新到畫面上。

我還蠻想了解 SwiftUI 背后是怎么計算 diff 的,希望之后有類似的文章出現

@State 修飾符可用來定義元件內部狀態,當狀態改變時會更新并反映到畫面中。

在 SwiftUI 當中,屬性(MyView 當中的 name)可以由外部傳入,跟 React 當中的屬性(props)類似。

// 在其他 View 當中使用 MyView
struct ContentView: View {
var body: some View {
MyView(name: "kalan")
}
}

用 React 改寫這個元件的話會像這樣:

const MyView = ({ name }) => {
const [isHidden, setIsHidden] = useState(false)
return <div>
<button onClick={() => setIsHidden(state => !state)}>hidden</button>
<p>Hello world</p>
{isHidden ? null : `show me ${name}`}
</div>
}

在撰寫 SwiftUI 時會發現這跟以往用 UIKit、UIController 的開發方式不太一樣。

列表

SwiftUI 與 React 當中都可以渲染列表,而撰寫的方式也有雷同之處。在 SwiftUI 當中可以這樣寫:

struct TextListView: View {
var body: some View {
List {
ForEach([
"iPhone",
"Android",
"Mac"
], id: \.self) { value in
Text(value)
}
}
}
}

轉成 React 大概會像這樣子:

const TextList = () => {
const list = ['iPhone', 'Android', 'Mac']

return list.map(item => <p key={item}>{item}</p>)
}

在渲染列表時為了確保效能,減少不必要的比對,React 會要求開發者提供 key,而在 SwiftUI 當中也有類似的機制,開發者必須使用叫做 Identifiable[11] 的 protocol,或是顯式地傳入 id。

Binding

除了將變數綁定到畫面之外,我們也可以將互動綁定到變數之中。例如在 SwiftUI 當中我們可以這樣寫:

struct MyInput: View {
@State private var text = ""
var body: some View {
TextField("Please type something", text: $text)
}
}

在這個范例當中,就算不監聽輸入事件,使用 $text 也可以直接改變 text 變數,當使用 @State 時會加入 property wrapper,會自動加入一個前綴 $,型別為 Binding[12]。

React 并沒有雙向綁定機制,必須要顯式監聽輸入事件確保單向資料流。不過像 Vue、Svelte 都有雙向綁定機制,節省開發者手動監聽事件的成本。

Combine 的出現

雖然我對 Combine 還不夠熟悉,但從官方文件與影片看起來,很像RxJS 的 Swift 特化版,提供的 API 與操作符大幅度地簡化了復雜資料流。這讓我想起了以前研究 RxJS 與 redux-observable 各種花式操作的時光,真令人懷念。

本質上的差異

前面提到那么多,然而網頁與手機開發仍然有相當大的差異,其中對我來說最顯著的一點是靜態編譯與動態執行。動態執行可以說是網頁最大的特色之一。

只要有瀏覽器,JavaScript、HTML、CSS,不管在任何裝置上都可以成功執行,網頁不需要事先下載 1xMB ~ 幾百 MB 的內容,可以動態執行腳本,根據瀏覽的頁面動態載入內容。

由于不需要事先編譯,任何人都可以看到網頁的內容與執行腳本,加上 HTML 可以 streaming 的特性,可以一邊渲染一邊讀取內容。難能可貴的一點是,網頁是去中心化的,只要有伺服器、ip 位址與網域,任何人都可以存取網站內容;而 App 如果要上架必須事先通過審查。

不過兩者的生態圈與開發手法有很大的不同,仍然建議參考一下彼此的發展,就算平時不會碰也沒關系,從不同的角度看往往可以發現不同的事情,也可以培養對技術的敏銳度。

參考資料

[1] ReSwift: https://github.com/ReSwift/ReSwift

[2] SwiftUI-Hooks: https://github.com/ra1028/SwiftUI-Hooks

[3] HOC(Higher Order Component): https://zh-hant.reactjs.org/docs/higher-order-components.html

[4] render props: https://zh-hant.reactjs.org/docs/render-props.html

[5] apollo: https://github.com/apollographql/apollo-client

[6] relay: https://relay.dev/

[7] react-query: https://react-query.tanstack.com/

[8] react-swr: https://swr.vercel.app/zh-CN

[9] recoil: https://recoiljs.org/

[10] jotai: https://github.com/pmndrs/jotai

[11] Identifiable: https://developer.apple.com/documentation/swift/identifiable

[12] Binding: https://developer.apple.com/documentation/swiftui/binding

責任編輯:姜華 來源: Swift社區
相關推薦

2022-11-01 09:02:04

前端售后業務

2024-02-28 08:38:07

Rust前端效率

2023-03-31 09:02:37

前端客服通信

2011-01-21 17:09:06

Zimbra

2021-05-07 10:25:04

技術開發低代碼無代碼

2024-02-27 13:03:38

前端視頻合成FFmpeg

2024-06-18 13:36:29

2025-03-26 09:41:19

2017-10-27 15:48:06

JavaScript前端全端

2024-01-08 20:05:32

2021-03-15 06:24:22

Nacos集群搭建微服務

2022-01-13 10:19:34

軟件汽車 技術

2011-12-29 09:24:54

iOS應用下載排行榜

2021-05-07 09:00:02

Go項目標準

2021-03-07 17:17:07

Java內存閉包

2019-05-07 09:17:51

AWS西云數據IaaS

2011-07-15 15:18:06

微博Twitter

2020-11-11 09:19:37

前端優化面試

2020-05-13 08:48:16

JavaScript前端技術

2021-07-30 19:07:27

大數據云計算云原生化
點贊
收藏

51CTO技術棧公眾號

国产999精品久久久| 色婷婷av久久久久久久| 91成品人片a无限观看| 成人涩涩视频| 欧美一区二区在线不卡| 中文字幕在线资源| 一区二区三区精密机械公司| 免费高清成人| 国产精品免费久久| 天天影视综合色| 91在线视频播放地址| 狠狠噜天天噜日日噜| 精品在线免费观看| 欧美日韩精品一区二区| 成人频在线观看| 洋洋成人永久网站入口| 在线观看国产视频一二三| 亚洲男人的天堂一区二区| 依人在线免费视频| 日韩一区有码在线| 午夜av电影| 黄色精品一区二区| 欧美亚洲日本| 欧美高清你懂得| 调教视频免费在线观看| 国产亚洲一卡2卡3卡4卡新区 | 色综合天天视频在线观看| 丁香视频免费观看| 亚洲人妖av一区二区| 91免费版在线观看| 狠狠做深爱婷婷久久综合一区| 99av国产精品欲麻豆| 国产激情在线| 色琪琪一区二区三区亚洲区| 一级片在线免费看| 色哦色哦哦色天天综合| youjizz在线播放| 欧美日韩高清一区二区| 9191在线播放| 亚洲第一区第二区| 亚洲日本天堂| 久久在线免费视频| 免费日韩一区二区三区 | xx免费视频| 亚洲3atv精品一区二区三区| 色视频在线观看福利| 91精品国产91综合久久蜜臀| 久久男人天堂| 欧美激情视频一区二区| 国产欧美日韩精品高清二区综合区| 91精品中国老女人| 奶水喷射视频一区| 免费看又黄又无码的网站| 中文字幕字幕中文在线中不卡视频| 在线午夜视频| 亚洲精品一区二区三区福利| 国产精品日韩精品在线播放| 国产精品日韩av| 日韩精品一级中文字幕精品视频免费观看| 国产又黄又爽免费视频| 久久久影视传媒| 欧美色视频免费| 精品91自产拍在线观看一区| 亚洲五码在线| 国产日韩久久| 2024国产精品| 青青青草原在线| 国产一区二区三区精品久久久| 一区二区三区日本久久久| 欧美欧美一区二区| 99久久精品费精品国产一区二区| 国内福利写真片视频在线| 亚洲第一视频网| 日韩影视高清在线观看| 日韩av不卡播放| 亚洲欧美一区二区三区极速播放| 黄av在线播放| 91av视频在线免费观看| 日日夜夜免费精品视频| 免费看美女隐私的视频| 国产视频久久久久| 91一区二区| 俄罗斯av网站| 69堂成人精品免费视频| 精品自拍偷拍| 亚洲一卡二卡三卡| 一区二区三区不卡在线观看| a级片国产精品自在拍在线播放| 亚洲一区二区在线看| 亚洲奶大毛多的老太婆| 生活片a∨在线观看| 欧美另类极品videosbest最新版本 | 亚洲免费色视频| 先锋成人av| 国产精品日韩在线观看| 成人av在线看| 黄色免费在线观看| 国产精品久久视频| 国产视频不卡一区| 欧美大胆成人| 日韩欧美亚洲在线| 色哦色哦哦色天天综合| 一本久久青青| 九九视频精品在线观看| 亚洲欧美在线第一页| 国产日韩欧美一区| 尤物免费看在线视频| 1769国内精品视频在线播放| 国产不卡视频在线观看| 欧美巨大xxxx做受沙滩| 精品国产一区二区三区四区vr| 黑人精品xxx一区| 大胆日韩av| 成年人视频免费看| 久操成人在线视频| 久久久夜色精品亚洲| 免费一区二区三区四区| 亚洲国产午夜伦理片大全在线观看网站| 欧美视频中文字幕| 亚洲精品网址| 国产一二在线观看| 亚洲最大的av网站| 欧美日韩美女视频| 日本黄色精品| 水莓100在线视频| 亚洲一区二区在线播放| 色视频成人在线观看免| 欧美日韩三级| 国产免费a∨片在线观看不卡| 成人深夜直播免费观看| 亚洲国产精品一区二区www| 欧美综合一区| 国产日本在线观看| 欧美二区三区| 亚洲精品自产拍| 日本不卡在线视频| 日韩脚交footjobhd| 超薄肉色丝袜足j调教99| 中文字幕久热精品在线视频| 粉嫩aⅴ一区二区三区四区| 国产极品一区| 国产视频1区2区3区| 国产精品亚洲аv天堂网| 亚洲精品久久7777| 日韩精品1区| 97在线观看免费观看高清| 欧美重口乱码一区二区| 国产视频久久久久| 成人激情小说网站| 人人精品久久| 亚洲永久精品免费| 狠狠色伊人亚洲综合网站色| 亚洲国产小视频在线观看| 精品一区二区影视| www.66久久| 亚洲欧美福利一区二区| 一区二区三区在线免费视频| 亚洲视频一二三区| 精品视频在线视频| 欧美午夜xxx| av在线观看地址| 中文字幕无线精品亚洲乱码一区| 99精品欧美一区二区三区小说 | 欧美成人黑人| 欧美精品色婷婷五月综合| 51视频国产精品一区二区| 欧美午夜视频一区二区| 蜜桃av噜噜一区| 哺乳挤奶一区二区三区免费看 | 奇米精品在线| 久久久国产一区二区| 亚洲风情在线资源站| 久久亚洲不卡| 精品视频一区二区三区| 在线观看的av网站| 亚洲精品9999| 高清欧美性猛交| 欧美色手机在线观看| 不卡在线视频中文字幕| 久久亚洲影视| 欧美性片在线观看| 一本大道香蕉久在线播放29 | 色婷婷亚洲十月十月色天| 99re热精品| 色天天综合狠狠色| 精品久久香蕉国产线看观看gif| 激情久久五月天| 国产欧美久久一区二区三区| 欧美xxxx视频| av高清日电影| 免费在线观看污污视频| 国产精品99一区| 精品亚洲aⅴ在线观看| 一区二区三区视频在线观看| 激情欧美日韩一区二区| 99久久婷婷| 日韩三级一区| 国产在线观看a视频| 牛夜精品久久久久久久| 日本一区二区三区免费观看|