日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

SwiftUI之从前端视角看SwiftUI语言

發布時間:2024/5/28 HTML 60 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SwiftUI之从前端视角看SwiftUI语言 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、從 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 = falsefunc 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,不知道實際使用的效果如何。
  • 以 React 來說,在還沒有出現 hooks 之前,主要有三個方式來實作邏輯共用:
    • HOC(Higher Order Component):將共同邏輯包裝成函數后返回全新的 class,避免直接修改元件內部的實作,例如早期 react-redux 中的 connect;
    • render props:將實際渲染的元件當作屬性(props)傳入,并提供必要的參數供實作端使用;
    • children function:children 只傳入必要的參數,由實作端自行決定要渲染的元件。

三、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 或是 relay;
    • react-query;
    • react-swr;
    • recoil;
    • jotai。

四、全域狀態管理

  • 在全域狀態管理上,SwiftUI 也有內建機制叫做 @EnvrionmentObject,其運作機制很像 React 的 context,讓元件可以跨階層存取變數,當 context 改變時也會更新元件:
class User: ObservableObject {@Published var name = "kalan"@Published var age = 20 } struct UserInfo: View {@EnvironmentObject var user: Uservar 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 = falsevar body: some View {Toggle(isOn: $isHidden) {Text("Hidden")}Text("Hello world")if !isHidden {Text("Show me \(name)")}} }
  • 一個典型的 SwiftUI 元件是一個 struct,透過定義 body 變數來決定 UI。跟 React 相同,它們都只是對 UI 的抽象描述,透過比對資料結構計算最小差異后,再更新到畫面上。
  • @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 inText(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。
  • React 并沒有雙向綁定機制,必須要顯式監聽輸入事件確保單向資料流。不過像 Vue、Svelte 都有雙向綁定機制,節省開發者手動監聽事件的成本。

八、Combine 的出現

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

九、總結

  • 前文提到那么多,然而網頁與手機開發仍然有相當大的差異,其中對我來說最顯著的一點是靜態編譯與動態執行。動態執行可以說是網頁最大的特色之一。
  • 只要有瀏覽器,JavaScript、HTML、CSS,不管在任何裝置上都可以成功執行,網頁不需要事先下載 1xMB ~ 幾百 MB 的內容,可以動態執行腳本,根據瀏覽的頁面動態載入內容。
  • 由于不需要事先編譯,任何人都可以看到網頁的內容與執行腳本,加上 HTML 可以 streaming 的特性,可以一邊渲染一邊讀取內容。難能可貴的一點是,網頁是去中心化的,只要有伺服器、ip 位址與網域,任何人都可以存取網站內容;而 App 如果要上架必須事先通過審查。
  • 不過兩者的生態圈與開發手法有很大的不同,仍然建議參考一下彼此的發展,就算平時不會碰也沒關系,從不同的角度看往往可以發現不同的事情,也可以培養對技術的敏銳度。

總結

以上是生活随笔為你收集整理的SwiftUI之从前端视角看SwiftUI语言的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。