paint-brush
Swift を Web 開発に选择する形式 に@imike
15,851 測定値
15,851 測定値

Swift を Web 開発に使用する方法

Mikhail Isaev33m2023/03/20
Read on Terminal Reader

長すぎる; 読むには

SwifWeb は、SwiftUI を使用して Web サイトを作成できるようにするフレームワークです。 HTML と CSS の標準全体、およびすべての Web API をラップします。この記事では、SwifWeb フレームワークを使用して Web サイトの構築を開始する方法を紹介します。
featured image - Swift を Web 開発に使用する方法
Mikhail Isaev HackerNoon profile picture
0-item
1-item
Web 開発の生活は広大で、毎看日出現する新しいテクノロジの絶え間ない流れの中で迷子になりがちです。これらの新しいテクノロジーのほとんどは、JavaScript または TypeScript を便用して構築されています。しかし、この記事では、ブラウザー内で可以ネイティブ Swift を便用して Web 開発を行う办法を紹介します。


それはどのように可能ですか?

Swift が Web ページでネイティブに動作するには、原来に WebAssembly バイトコードにコンパイルする重要があり、その後 JavaScript がそのコードをページにロードできます。特別なツールチェーンを使用的してヘルパー ファイルを制作する重要があるため、コンパイルのプロセス我谨代表は少しトリッキーです。そのため、Carton と Webber というヘルパー CLI ツールが含义されています。

サードパーティのツールチェーンを使用しても問題ありませんか?

コミュニティは、元の Swift ツールチェーンにパッチを適用することにより、Swift を WebAssembly にコンパイルできるようにするために多少な作業を行いました。毎日、元のツールチェーンから変更を自動的にプルし、テストが失敗した場合はフォークを校准することで、ツールチェーンを更新软件します。彼らの目標は、公式换算ツールチェーンの是一部になることであり、近い今后それが実現することを望んでいます。

カートンかウェバーか?

は SwiftWasm コミュニティによって作为され、SwiftUI を食用して Web サイトを作为できるフレームワークである Tokamak プロジェクトに食用できます。


SwifWeb プロジェクト用に作られています。 SwifWeb は、すべての Web API だけでなく、HTML および CSS 標準全体人员をラップするという点で異なります。


コードの一貫性のために SwiftUI を便用して Web アプリを制成することを好むかもしれませんが、Web 開発は本質的に異なり、SwiftUI と同じ技巧でアプローチすることはできないため、これは間違ったアプローチだと思います。


そのため、私は SwifWeb を做成しました。これにより、HTML、CSS、および Web API のすべての機能を Swift から就直接适用でき、オートコンプリートとドキュメントを備えた美しい構文を适用できます。また、Webber ツールを做成したのは、Carton が SwifWeb アプリ用に做成されていないため、SwifWeb アプリを正しい办法でコンパイル、デバッグ、デプロイできないためです。


私の名前は Mikhail Isaev で、SwifWeb の制作者です。この記事では、SwifWeb を适用して Web サイトの構築を開始する形式を紹介します。

必要なツール


迅速

Swift をインストールする必要があります。最も簡単な方法は次のとおりです。

  • macOSではXcodeをインストールすることです
  • Linux または Windows (WSL2) では、 のスクリプトを使用します。


それ意外の場合はからインストールしてください)。
 brew install swifweb/tap/webber
後で新的バージョンに内容更新するには、実行するだけです
brew upgrade webber


Ubuntu または Windows (WSL2 の Ubuntu) では、Webber を手動で複製してコンパイルします。
 sudo apt-get install binaryen curl //get.wasmer.io -sSfL | sh apt-get install npm cd /opt sudo git clone //github.com/swifweb/webber cd webber sudo swift build -c release sudo ln -s /opt/webber/.build/release/Webber /usr/local/bin/webber
後で実行して最後のバージョンに升级する
cd /opt/webber sudo git pull sudo swift build -c release

メインブランチには常に安定したコードが含まれているため、そこから更新を自由にプルしてください

新しいプロジェクトの作成

ターミナルを開いて実行
webber new

インタラクティブ メニューでpwaまたはspaを選択し、プロジェクト名を入力します。


ディレクトリを新しく作成したプロジェクトに変更し、 webber serveを実行します。

このコマンドは、プロジェクトを WebAssembly にコンパイルし、必要なすべてのファイルを特別な.webberフォルダー内にパッケージ化し、デフォルトでポート8888を使用してすべてのインターフェイスでプロジェクトの提供を開始します。


webber serveの追加引数

  • アプリの種類

    プログレッシブ Web アプリの-t pwa

    -t spa for Single Web App

  • Service Worker ターゲットの名前 (通常、PWA プロジェクトではServiceという名前)

    -s Service

  • アプリ ターゲットの名前 (既定ではApp )

    -a App

  • コンソールに詳細情報を认为

    -v

  • Webber サーバーのポート (デフォルトは8888 )

    -p 8080

-p 443を使用して、実際の SSL のようにテストします (自己署名 SSL 設定を許可)

  • 自動起動先ブラウザ名

    --browser safariまたは--browser chrome

  • Service-Worker をデバッグするための自己的落款 SSL 設定が許可されたブラウザーの追加インスタンス

    --browser-self-signed

  • シークレット モードのブラウザの追加インスタンス

    --browser-incognito

応用

アプリはSources/App/App.swiftで始まります

import Web @main class App: WebApp { @AppBuilder override var app: Configuration { Lifecycle.didFinishLaunching { app in app.registerServiceWorker("service") } Routes { Page { IndexPage() } Page("login") { LoginPage() } Page("article/:id") { ArticlePage() } Page("**") { NotFoundPage() } } MainStyle() } }

ライフサイクル

iOS のような方法で動作します。

アプリの起動直後のdidFinishLaunching

アプリが終了するときにwillTerminate

ウィンドウが非アクティブになるときのwillResignActive

ウィンドウがアクティブなときにdidBecomeActive

ウィンドウがバックグラウンドに入るときのdidEnterBackground

ウィンドウが最前面に移動するときはwillEnterForeground


ここで最も役立つメソッドはdidFinishLaunchingです。アプリを構成するのに最適な場所だからです。まるで iOS アプリのようです。 😀


appには便利な便利なメソッドが含まれています。

registerServiceWorker(“serviceName“)を呼び出して、PWA サービス ワーカーを登録します。

addScript(“path/to/script.js“)呼び出しで相対スクリプトまたは外部スクリプトを追加

addStylesheet(“path/to/style.css“)呼び出しで相対スタイルまたは外部スタイルを追加

addFont(“path/to/font.woff”, type:)相対または外部フォントを追加するための呼び出し、オプションでタイプを設定

addIcon(“path/to/icon“, type:color:)アイコンを追加するための呼び出し、オプションでタイプと色を設定


また、 、 、 などの追加ライブラリを構成する場所でもあります。

ルート

現在の URL に基づいて適切なページを标识するには、ルーティングが有必要です。


ルーティングの采用形式を解读するには、URL とは何かを解读する用得着があります

//website.com/hello/world - ここで/hello/worldパスです

ご覧のとおり、开始に App クラスですべての最上位ルートを宣誓书する这个必要があります。トップレベルとは、これらのルートで誓词されたページがウィンドウ内のすべてのスペースを占据することを暗示します。


たとえば、ルート ルートは 3 つの工艺で設定できます。
 Page("/") { IndexPage() } Page("") { IndexPage() } Page { IndexPage() }
最後が一阵綺麗だと思います^^


ログインまたは登録ルートは次のように設定できます
Page("login") { LoginPage() } Page("registration") { RegistrationPage() }


パラメータ関連のルート

Page("article/:id") { ArticlePage() }

上記の例の:id は、ルートの動的部分です。 ArticlePageクラスでこの識別子を取得して、関連付けられた記事を表示できます。

 class ArticlePage: PageController { override func didLoad(with req: PageRequest) { if let articleId = req.parameters.get("id") { // Retrieve article here } } }
パスには複数のパラメーターを含めることができます。すべて同じ形式で拿得します。

クエリ

パスで次に興味深いのはqueryです。これも非常に使いやすいです。たとえば、検索textageクエリ パラメーターが必要な/searchルートを考えてみましょう。

//website.com/search**?text=Alex&age=19** - 最後の部分はクエリです


科学探索ルートを宣誓书するだけ
Page("search") { SearchPage() }

そして、このようにSearchPageクラスでクエリ データを取得します

class SearchPage: PageController { struct Query: Decodable { let text: String? let age: Int? } override func didLoad(with req: PageRequest) { do { let query = try req.query.decode(Query.self) // use optional query.text and query.age // to query search results } catch { print("Can't decode query: \(error)") } } }

なんでも

*を使用して、このように特定のパス部分で何でも受け入れるルートを宣言することもできます

Page("foo", "*", "bar") { SearchPage() }
上記のルートは、foo と bar の間のすべてを受け入れます (例: /foo/aaa/bar、/foo/bbb/bar など)。

キャッチオール

**記号を使用すると、特定のパスで他のルートに一致していないものをすべて処理する特別なキャッチオール ルートを設定できます。


それを在使用して、グローバル 404 ルートを制作します
Page("**") { NotFoundPage() }
または不同のパス、たとえばユーザーが見つからない場合
Page("user", "**") { UserNotFoundPage() }


上記で宣言口号されたルートの状況を明確にしましょう

/user/1 - /user/:id のルートがある場合、 UserPageが返されます。そうでなければ、それは…に陥ります


UserNotFoundPage

/user/1/hello - /user/:id/hello のルートがある場合、 UserNotFoundPageに分類されます

/something - /something へのルートがない場合、 NotFoundPageに分類されます

ネストされたルーティング

次のルートのためにページのコンテンツ全体を置き換えるのではなく、特定のブロックのみを置き換えたい場合があります。ここでFragmentRouterが役に立ちます。


/userページにタブがあるとします。各タブはサブルートであり、 FragmentRouterを使用してサブルートの変更に対応したいと考えています。


Appクラスで最上位ルートを宣言する

Page("user") { UserPage() }

そしてUserPageクラスでFragmentRouter を宣言します

class UserPage: PageController { @DOM override var body: DOM.Content { // NavBar is from Materialize library :) Navbar() .item("Profile") { self.changePath(to: "/user/profile") } .item("Friends") { self.changePath(to: "/user/friends") } FragmentRouter(self) .routes { Page("profile") { UserProfilePage() } Page("friends") { UserFriendsPage() } } } }


上記の例では、 FragmentRouter は/user/profileおよび/user/friendsサブルートを処理し、それをNavbarの下にレンダリングするため、ページはコンテンツ全体をリロードすることはなく、特定のフラグメントのみをリロードします。


同じまたは異なるサブルートを持つ複数のフラグメントを口号することもでき、それらはすべて魔法图片のように連携します!


ところでFragmentRouterDivであり、呼び出すことで構成できます

FragmentRouter(self) .configure { div in // do anything you want with the div }

スタイルシート

従来の CSS ファイルを适用できますが、Swift で記述されたスタイルシートを适用する新しい法术の機能も備えています。

基本

Swift を使用して CSS ルールを宣言するために、 Ruleオブジェクトがあります。


メソッドを呼び出すことで宣誓书的に構築できます
Rule(...selector...) .alignContent(.baseline) .color(.red) // or rgba/hex color .margin(v: 0, h: .auto)
または @resultBuilder を安全使用した SwiftUI のような技巧
Rule(...selector...) { AlignContent(.baseline) Color(.red) Margin(v: 0, h: .auto) }


どちらの方法も同じですが、入力した直後にオートコンプリートされるため、最初の方法を好みます. 😀

MDN で説明されているすべての CSS メソッドが凭借或许です。それ以上的に、ブラウザのプレフィックスを自動的に処理します!
ただし、不同のケースでは、この措施でカスタム プロパティを設定できます。
 Rule(...selector...) .custom("customKey", "customValue")

セレクタ

Rule が影響を与える关键を設定するには、セレクターを設定する相应があります。セレクターはデータベース内のクエリと見なされますが、そのセレクター クエリの一部电影をポインターと呼んでいます。


ポインターを構築する最も簡単な策略は、生の图片文字列を运用して末期化することです
Pointer("a")


しかし、正しい迅速な方法は、このように必要な HTML タグで.pointerを呼び出してビルドすることです

H1.pointer // h1 A.pointer // a Pointer.any // * Class("myClass").pointer // .myClass Id("myId").pointer // #myId

基本的なポインターについてですが、 :hover :first :first-childなどの修飾子もあります。

 H1.pointer.first // h1:first H1.pointer.firstChild // h1:first-child H1.pointer.hover // h1:hover
既存の修飾子を口号できます。それらはすべて合理利用有机会です。
何か不够している場合は、遠慮なく拡張機能を作为して追加してください。また、github でプル リクエストを送信して、全員に追加することを忘れないでください。
ポインタを連結することもできます
H1.class(.myClass) // h1.myClass H1.id(.myId) // h1#myId H1.id(.myId).disabled // h1#myId:disabled Div.pointer.inside(P.pointer) // div p Div.pointer.parent(P.pointer) // div > p Div.pointer.immediatedlyAfter(P.pointer) // Div + p P.pointer.precededBy(Ul.pointer) // p ~ ul


ルールでセレクターを使用する方法

Rule(Pointer("a")) // or Rule(A.pointer)

ルールで複数のセレクターを使用する方法

Rule(A.pointer, H1.id(.myId), Div.pointer.parent(P.pointer))
次の CSS コードを添加します。
 a, h1#myId, div > p { }

反応性

アプリの暗いスタイルと明るいスタイルを誓言しましょう。後で、それらを簡単に切り替えることができます。
 import Web @main class App: WebApp { enum Theme { case light, dark } @State var theme: Theme = .light @AppBuilder override var app: Configuration { // ... Lifecycle, Routes ... LightStyle().disabled($theme.map { $0 != .happy }) DarkStyle().disabled($theme.map { $0 != .sad }) } }


LightStyleDarkStyle は、別のファイルまたはたとえば App.swift で宣言できます。


 class LightStyle: Stylesheet { @Rules override var rules: Rules.Content { Rule(Body.pointer).backgroundColor(.white) Rule(H1.pointer).color(.black) } } class DarkStyle: Stylesheet { @Rules override var rules: Rules.Content { Rule(Body.pointer).backgroundColor(.black) Rule(H1.pointer).color(.white) } }


そして、あるページのUIのどこかで呼び出すだけです
App.current.theme = .light // to switch to light theme // or App.current.theme = .dark // to switch to dark theme
そして、関連するスタイルシートを有効または無効にします!かっこよくないですか? 😎


しかし、CSS の代わりに Swift でスタイルを記述するのは難しいと言うかもしれません。


ポイントは反応性! @State を CSS プロパティで在使用して、その場で値を変更できます!


ちょっと見てみましょう。リアクティブなプロパティを持つクラスを制作し、実行時にいつでも変更できるため、そのクラスを在使用する画上边の因素はすべて系统更新されます!多くの因素のクラスを切り替えるよりもはるかに効果的です!


 import Web @main class App: WebApp { @State var reactiveColor = Color.cyan @AppBuilder override var app: Configuration { // ... Lifecycle, Routes ... MainStyle() } } extension Class { static var somethingCool: Class { "somethingCool" } } class MainStyle: Stylesheet { @Rules override var rules: Rules.Content { // for all elements with `somethingCool` class Rule(Class.hello.pointer) .color(App.current.$reactiveColor) // for H1 and H2 elements with `somethingCool` class Rule(H1.class(.hello), H2.class(.hello)) .color(App.current.$reactiveColor) } }


後でコードの同一の場所から呼び出すだけです
App.current.reactiveColor = .yellow // or any color you want
スタイルシートとそれを选用するすべての要点の色が提升されます 😜


また、未工艺の CSS をスタイルシートに追加することもできます
class MainStyle: Stylesheet { @Rules override var rules: Rules.Content { // for all elements with `somethingCool` class Rule(Class.hello.pointer) .color(App.current.$reactiveColor) // for H1 and H2 elements with `somethingCool` class Rule(H1.class(.hello), H2.class(.hello)) .color(App.current.$reactiveColor) """ /* Raw CSS goes here */ body { margin: 0; padding: 0; } """ } }
生のCSS文本框列を相应なだけ何度でも混迹させることができます

ページ

ルーターは各ルートでページをレンダリングしています。 Page は、 PageControllerから継承された任意のクラスです。


PageControllerには、 willLoad didLoad willUnload didUnload 、UI メソッドbuildUIおよびbody 、HTML 要素のプロパティ ラッパー変数などのライフサイクル メソッドがあります。

技術的には、 PageController は単なる Div であり、 buildUIメソッドでそのプロパティを設定できます。


 class IndexPage: PageController { // MARK: - Lifecycle override func willLoad(with req: PageRequest) { super.willLoad(with: req) } override func didLoad(with req: PageRequest) { super.didLoad(with: req) // set page title and metaDescription self.title = "My Index Page" self.metaDescription = "..." // also parse query and hash here } override func willUnload() { super.willUnload() } override func didUnload() { super.didUnload() } // MARK: - UI override func buildUI() { super.buildUI() // access any properties of the page's div here // eg self.backgroundcolor(.lightGrey) // optionally call body method here to add child HTML elements body { P("Hello world") } // or alternatively self.appendChild(P("Hello world")) } // the best place to declare any child HTML elements @DOM override var body: DOM.Content { H1("Hello world") P("Text under title") Button("Click me") { self.alert("Click!") print("button clicked") } } }


あなたのページが小さい場合は、この短い做法でも誓言できます
PageController { page in H1("Hello world") P("Text under title") Button("Click me") { page.alert("Click!") print("button clicked") } } .backgroundcolor(.lightGrey) .onWillLoad { page in } .onDidLoad { page in } .onWillUnload { page in } .onDidUnload { page in }
それは美しく簡潔ではありませんか? 🥲


ボーナスの便利な方法

alert(message: String) - 直接の JS alertメソッド

changePath(to: String) - URL パスの切り替え

HTML 要素

最後に、HTML 基本原则を弄成して运用する步骤 (!) について説明します。


特性を持つすべての HTML 关键因素は Swift で进行できます。基本なリストはなどにあります。


HTML 环境要素の短いリストの例:
SwifWeb コードHTMLコード

Div()

<div></div>

H1(“text“)

<h1>text</h1>

A(“Click me“).href(““).target(.blank)

<a href=”” target=”_blank”>Click me</a>

Button(“Click“).onClick { print(“click“) }

<button onclick=”…”>Click</button>

InputText($text).placeholder("Title")

<input type=”text” placeholder=”title”>

InputCheckbox($checked)

<input type=”checkbox”>


ご覧のとおり、Swift で HTML タグにアクセスするのは特别に簡単です。入力を除いて、それらはすべて同じ名前で表されているからです。これは、異なる入力タイプには異なるメソッドがあり、それらを混到させたくなかったためです。


単純Div

 Div()
このように、すべての物理攻击とスタイル プロパティにアクセスできます。
 Div().class(.myDivs) // <div class="myDivs"> .id(.myDiv) // <div id="myDiv"> .backgroundColor(.green) // <div style="background-color: green;"> .onClick { // adds `onClick` listener directly to the DOM element print("Clicked on div") } .attribute("key", "value") // <div key="value"> .attribute("boolKey", true, .trueFalse) // <div boolKey="true"> .attribute("boolKey", true, .yesNo) // <div boolKey="yes"> .attribute("checked", true, .keyAsValue) // <div checked="checked"> .attribute("muted", true, .keyWithoutValue) // <div muted> .custom("border", "2px solid red") // <div style="border: 2px solid red;">

サブクラス化

HTML 要素をサブクラス化してスタイルを事前定義するか、多数の事前定義された子要素と外部で使用できるいくつかの便利なメソッドを含む複合要素を作成するか、 didAddToDOMdidRemoveFromDOMなどのライフサイクル イベントを実現します。

単なるDivであるが定義済みの.dividerクラスを持つDivider要素を作成しましょう

public class Divider: Div { // it is very important to override the name // because otherwise it will be <divider> in HTML open class override var name: String { "\(Div.self)".lowercased() } required public init() { super.init() } // this method executes immediately after any init method public override func postInit() { super.postInit() // here we are adding `divider` class self.class(.divider) } }
サブクラス化するときは、スーパー メソッドを呼び出すことが极为に重点です。これがないと、予期しない動作が発生する已经性があります。

DOM への追加

Element は、すぐにまたは後でPageControllerまたはHTML 要素の DOM に追加できます。


すぐに
Div { H1("Title") P("Subtitle") Div { Ul { Li("One") Li("Two") } } }


または後でlazy varを使用して

lazy var myDiv1 = Div() lazy var myDiv2 = Div() Div { myDiv1 myDiv2 }

そのため、事前にHTML 要素を宣言し、後でいつでも DOM に追加できます。

DOM からの削除

lazy var myDiv = Div() Div { myDiv } // somewhere later myDiv.remove()

親要素にアクセスする

すべての HTML 关键因素には、DOM に追加された場合にその親へのアクセスを展示するオプションのスーパービュー プロパティがあります。
 Div().superview?.backgroundColor(.red)

if/else 条件

特定の条件でのみ要素を表示する必要があることが多いので、そのためにif/else使用しましょう

lazy var myDiv1 = Div() lazy var myDiv2 = Div() lazy var myDiv3 = Div() var myDiv4: Div? var showDiv2 = true Div { myDiv1 if showDiv2 { myDiv2 } else { myDiv3 } if let myDiv4 = myDiv4 { myDiv4 } else { P("Div 4 was nil") } }

しかし、それは反応的ではありません。 showDiv2falseに設定しようとしても、何も起こりません。


リアクティブな例
lazy var myDiv1 = Div() lazy var myDiv2 = Div() lazy var myDiv3 = Div() @State var showDiv2 = true Div { myDiv1 myDiv2.hidden($showDiv2.map { !$0 }) // shows myDiv2 if showDiv2 == true myDiv3.hidden($showDiv2.map { $0 }) // shows myDiv3 if showDiv2 == false }


$showDiv2.map {…}を使用する必要があるのはなぜですか?

並べ替え:SwiftUIではないため。まったく。


@State詳細については、下述を符合してください。


生の HTML

生の HTML をページまたは HTML 原则に追加する必不可少がある場合もありますが、それは簡単に概率です。
 Div { """ <a href="//google.com">Go to Google</a> """ }


ForEach

静的な例

let names = ["Bob", "John", "Annie"] Div { ForEach(names) { name in Div(name) } // or ForEach(names) { index, name in Div("\(index). \(name)") } // or with range ForEach(1...20) { index in Div() } // and even like this 20.times { Div().class(.shootingStar) } }

動的な例

@State var names = ["Bob", "John", "Annie"] Div { ForEach($names) { name in Div(name) } // or with index ForEach($names) { index, name in Div("\(index). \(name)") } } Button("Change 1").onClick { // this will append new Div with name automatically self.names.append("George") } Button("Change 2").onClick { // this will replace and update Divs with names automatically self.names = ["Bob", "Peppa", "George"] }

CSS

上記の例と同じですが、 BuilderFunctionも利用できます

Stylesheet { ForEach(1...20) { index in CSSRule(Div.pointer.nthChild("\(index)")) // set rule properties depending on index } 20.times { index in CSSRule(Div.pointer.nthChild("\(index)")) // set rule properties depending on index } }


ForEachループでBuilderFunction使用して、次の例のdelay値のように、値を 1 回だけ計算できます。

 ForEach(1...20) { index in BuilderFunction(9999.asRandomMax()) { delay in CSSRule(Pointer(".shooting_star").nthChild("\(index)")) .custom("top", "calc(50% - (\(400.asRandomMax() - 200)px))") .custom("left", "calc(50% - (\(300.asRandomMax() + 300)px))") .animationDelay(delay.ms) CSSRule(Pointer(".shooting_star").nthChild("\(index)").before) .animationDelay(delay.ms) CSSRule(Pointer(".shooting_star").nthChild("\(index)").after) .animationDelay(delay.ms) } }


関数を引数として取ることもできます
BuilderFunction(calculate) { calculatedValue in // CSS rule or DOM element } func calculate() -> Int { return 1 + 1 }

BuilderFunctionは HTML 要素でも利用できます:)

@State との反応性

@Stateは、今日の宣言型プログラミングにとって最も望ましいものです。


上記で説明したように、これは SwiftUI ではないため、すべてを追跡して再描画するグローバル ステート マシンはありません。また、HTML 元素は一時的な構造体ではなくクラスであるため、実際のオブジェクトであり、一直アクセスできます。それははるかに優れており、柔軟性があり、すべてを制御できます。

ボンネットの下は何ですか?

これは、変更についてすべてのサブスクライバーに消息通知するプロパティ ラッパーです。

変更を購読するには?

 enum Countries { case usa, australia, mexico } @State var selectedCounty: Countries = .usa $selectedCounty.listen { print("country changed") } $selectedCounty.listen { newValue in print("country changed to \(newValue)") } $selectedCounty.listen { oldValue, newValue in print("country changed from \(oldValue) to \(newValue)") }

HTML 要素が変更にどのように反応できるか?

簡単なテキストの例
@State var text = "Hello world!" H1($text) // whenever text changes it updates inner-text in H1 InputText($text) // while user is typing text it updates $text which updates H1
簡単な数字式の例
@State var height = 20.px Div().height($height) // whenever height var changes it updates height of the Div
簡単なブール値の例
@State var hidden = false Div().hidden($hidden) // whenever hidden changes it updates visibility of the Div
マッピング例
@State var isItCold = true H1($isItCold.map { $0 ? "It is cold 🥶" : "It is not cold 😌" })
2 つの状態のマッピング
@State var one = true @State var two = true Div().display($one.and($two).map { one, two in // returns .block if both one and two are true one && two ? .block : .none })
3 つ上述の州のマッピング
@State var one = true @State var two = true @State var three = 15 Div().display($one.and($two).map { one, two in // returns true if both one and two are true one && two }.and($three).map { oneTwo, three in // here oneTwo is a result of the previous mapping // returns .block if oneTwo is true and three is 15 oneTwo && three == 15 ? .block : .none })

すべての HTML および CSS プロパティは@State値を処理できます

拡張機能

HTML 要素を拡張する

Div のような主要的な关键にいくつかの便利店なメソッドを追加できます
extension Div { func makeItBeautiful() {} }

または、親classがわかっている場合は要素のグループ。


親子クラスは少ないです。

BaseActiveStringElement - ah1などの文字列で初期化できる要素用です。

BaseContentElement - divulなど、内部にコンテンツを持つことができるすべての要素用です。

BaseElement - すべての要素用です


したがって、すべての原则の拡張はこのように記述できます
extension BaseElement { func doSomething() {} }

色を宣言する

カラークラスは色を担当します。定義済みの HTML カラーがありますが、独自のものを使用できます

extension Color { var myColor1: Color { .hex(0xf1f1f1) } // which is 0xF1F1F1 var myColor2: Color { .hsl(60, 60, 60) } // which is hsl(60, 60, 60) var myColor3: Color { .hsla(60, 60, 60, 0.8) } // which is hsla(60, 60, 60, 0.8) var myColor4: Color { .rgb(60, 60, 60) } // which is rgb(60, 60, 60) var myColor5: Color { .rgba(60, 60, 60, 0.8) } // which is rgba(60, 60, 60, 0.8) }

次にH1(“Text“).color(.myColor1)のように使用します。

クラスを宣言する

extension Class { var my: Class { "my" } }

次に、 Div().class(.my)のように使用します

ID を宣言する

extension Id { var myId: Id { "my" } }

次に、 Div().id(.my)のように使用します

Web API

windowオブジェクトは完全にラップされ、 App.current.window変数を介してアクセスできます。

基本なリファレンスはで抓起できます。


下列で簡単な内容梗概を説明しましょう

前景フラグ

App.swiftLifecycleで、またはこの方法で直接リッスンできます。

 App.current.window.$isInForeground.listen { isInForeground in // foreground flag changed }
または、いつでもどこでも読むことができます
if App.current.window.isInForeground { // do somethign }
またはHTML原则でそれに反応する
Div().backgroundColor(App.current.window.$isInForeground.map { $0 ? .grey : .white })

アクティブフラグ

Foreground フラグと同じですが、 App.current.window.isActiveからアクセスできます。

ユーザーがまだウィンドウ内で対話しているかどうかを検出します。

オンライン状態

Foreground フラグと同じですが、 App.current.window.isOnlineからアクセスできます

ユーザーがまだインターネットにアクセスできるかどうかを検出します。

ダークモードのステータス

Foreground フラグと同じですが、 App.current.window.isDarkからアクセスできます

ユーザーのブラウザーまたはオペレーティング システムがダーク モードであるかどうかを検出します。

内寸

スクロールバーを含むウィンドウのコンテンツ領域 (ビューポート) のサイズ

App.current.window.innerSize 、内部のwidthheight値内のSizeオブジェクトです。

@State変数としても使用できます。

外寸

ツールバー/スクロールバーを含むブラウザ ウィンドウのサイズ。

App.current.window.outerSize 、内部のwidthheight値内のSizeオブジェクトです。

@State変数としても使用できます。

画面

現在のウィンドウがレンダリングされている画面のプロパティを検査するための特別なオブジェクト。 App.current.window.screenから利用できます。

通常、最も興味深いプロパティはpixelRatioです。

歴史

ユーザーが (ブラウザ ウィンドウ内で) アクセスした URL が含まれます。

App.current.window.historyまたは単にHistory.sharedで利用できます。

@State変数としてアクセスできるため、必要に応じてその変更をリッスンできます。

 App.current.window.$history.listen { history in // read history properties }
単純変数としてもアクセス将
History.shared.length // size of the history stack History.shared.back() // to go back in history stack History.shared.forward() // to go forward in history stack History.shared.go(offset:) // going to specific index in history stack
詳細はで购买できます。

位置

現在の URL に関する情報が含まれます。

App.current.window.locationまたはLocation.sharedから利用できます。

@State変数としてアクセスできるため、必要に応じてその変更をリッスンできます。

これは、たとえばルーターの仕組みです。
 App.current.window.$location.listen { location in // read location properties }


単純な変数としてもアクセスできます
Location.shared.href // also $href Location.shared.host // also $host Location.shared.port // also $port Location.shared.pathname // also $pathname Location.shared.search // also $search Location.shared.hash // also $hash
詳細はで刚买下的できます。

ナビゲーター

ブラウザに関する情報が含まれています。

App.current.window.navigatorまたは単にNavigator.shared経由で利用可能

通常、最も興味深いプロパティはuserAgent platform language cookieEnabled

ローカルストレージ

キーと値のペアを Web ブラウザーに保护できます。有効贷款期限なしでデータを保护します。

App.current.window.localStorageまたはLocalStorage.sharedとして利用できます。

 // You can save any value that can be represented in JavaScript LocalStorage.shared.set("key", "value") // saves String LocalStorage.shared.set("key", 123) // saves Int LocalStorage.shared.set("key", 0.8) // saves Double LocalStorage.shared.set("key", ["key":"value"]) // saves Dictionary LocalStorage.shared.set("key", ["v1", "v2"]) // saves Array // Getting values back LocalStorage.shared.string(forKey: "key") // returns String? LocalStorage.shared.integer(forKey: "key") // returns Int? LocalStorage.shared.string(forKey: "key") // returns String? LocalStorage.shared.value(forKey: "key") // returns JSValue? // Removing item LocalStorage.shared.removeItem(forKey: "key") // Removing all items LocalStorage.shared.clear()
変更の追跡
LocalStorage.onChange { key, oldValue, newValue in print("LocalStorage: key \(key) has been updated") }
すべてのアイテムの削除の追跡
LocalStorage.onClear { print("LocalStorage: all items has been removed") }

セッションストレージ

キーと値のペアを Web ブラウザーに留存できます。 1 つのセッションだけのデータを留存します。

App.current.window.sessionStorageまたは単にSessionStorage.sharedとして利用できます。

API は上記のLocalStorageとまったく同じです。

書類

ブラウザーに読み込まれた Web ページを表し、Web ページのコンテンツへのエントリ ポイントとして機能します。

App.current.window.documentから入手できます。

 App.current.window.document.title // also $title App.current.window.document.metaDescription // also $metaDescription App.current.window.document.head // <head> element App.current.window.document.body // <body> element App.current.window.documentquerySelector("#my") // returns BaseElement? App.current.window.document.querySelectorAll(".my") // returns [BaseElement]

ローカリゼーション

静的ローカリゼーション

従来のローカリゼーションは自動で、ユーザーのシステム言語に依存します

使い方

H1(String( .en("Hello"), .fr("Bonjour"), .ru("Привет"), .es("Hola"), .zh_Hans("你好"), .ja("こんにちは")))

動的ローカリゼーション

オンザフライで画上面のローカライズされた图文列を変更したい場合 (ページのリロードなし)呼び出して現在の言語を変更できます
Localization.current = .es
ユーザーの言語を Cookie またはローカル ストレージのどこかに存为した場合は、アプリの起動時に設定する有必要があります。
 Lifecycle.didFinishLaunching { Localization.current = .es }

使い方

H1(LString( .en("Hello"), .fr("Bonjour"), .ru("Привет"), .es("Hola"), .zh_Hans("你好"), .ja("こんにちは")))

高度な例

H1(Localization.currentState.map { "Curent language: \($0.rawValue)" }) H2(LString(.en("English string"), .es("Hilo Español"))) Button("change lang").onClick { Localization.current = Localization.current.rawValue.contains("en") ? .es : .en }

フェッチ

import FetchAPI Fetch("//jsonplaceholder.typicode.com/todos/1") { switch $0 { case .failure: break case .success(let response): print("response.code: \(response.status)") print("response.statusText: \(response.statusText)") print("response.ok: \(response.ok)") print("response.redirected: \(response.redirected)") print("response.headers: \(response.headers.dictionary)") struct Todo: Decodable { let id, userId: Int let title: String let completed: Bool } response.json(as: Todo.self) { switch $0 { case .failure(let error): break case .success(let todo): print("decoded todo: \(todo)") } } } }

XMLHttpRequest

 import XMLHttpRequest XMLHttpRequest() .open(method: "GET", url: "//jsonplaceholder.typicode.com/todos/1") .onAbort { print("XHR onAbort") }.onLoad { print("XHR onLoad") }.onError { print("XHR onError") }.onTimeout { print("XHR onTimeout") }.onProgress{ progress in print("XHR onProgress") }.onLoadEnd { print("XHR onLoadEnd") }.onLoadStart { print("XHR onLoadStart") }.onReadyStateChange { readyState in print("XHR onReadyStateChange") } .send()

WebSocket

 import WebSocket let webSocket = WebSocket("wss://echo.websocket.org").onOpen { print("ws connected") }.onClose { (closeEvent: CloseEvent) in print("ws disconnected code: \(closeEvent.code) reason: \(closeEvent.reason)") }.onError { print("ws error") }.onMessage { message in print("ws message: \(message)") switch message.data { case .arrayBuffer(let arrayBuffer): break case .blob(let blob): break case .text(let text): break case .unknown(let jsValue): break } } Dispatch.asyncAfter(2) { // send as simple string webSocket.send("Hello from SwifWeb") // send as Blob webSocket.send(Blob("Hello from SwifWeb")) }

コンソール

単純なprint(“Hello world“) JavaScript のconsole.log('Hello world')に相当します


コンソールメソッドも愛で包まれています❤️

 Console.dir(...) Console.error(...) Console.warning(...) Console.clear()

ライブプレビュー

ライブ プレビューを機能させるには、一定な各ファイルで WebPreview クラスを声明します。
 class IndexPage: PageController {} class Welcome_Preview: WebPreview { @Preview override class var content: Preview.Content { Language.en Title("Initial page") Size(640, 480) // add here as many elements as needed IndexPage() } }


Xcode

の説明をお読みください。トリッキーですが、基本に機能するソリューションです😎


VSコード

VSCode内のExtensionsに移動し、 Webberを検索します。

インストールしたら、 Cmd+Shift+P (Linux/Windows ではCtrl+Shift+P ) を押します。

Webber Live Previewを見つけて起動します。

右側にライブ プレビュー ウィンドウが表示され、 WebPreviewクラスを含むファイルを保存するたびに更新されます。

JavaScript へのアクセス

SwifWebの基盤であるJavaScriptKitを通じて利用できます。

にある的方法をお読みください。

資力

cssjspngjpg 、およびプロジェクト内のその他の静的リソースを追加できます。

ただし、デバッグ中または最終リリースファイルでそれらを使用できるようにするには、次のようにPackage.swiftですべて宣言する必要があります。

 .executableTarget(name: "App", dependencies: [ .product(name: "Web", package: "web") ], resources: [ .copy("css/*.css"), .copy("css"), .copy("images/*.jpg"), .copy("images/*.png"), .copy("images/*.svg"), .copy("images"), .copy("fonts/*.woff2"), .copy("fonts") ]),

後で、このImg().src(“/images/logo.png“)のようにアクセスできるようになります。

デバッグ

次の方法でWebberを起動します

webber serveすばやく起動するためだけに

webber serve -t pwa -s Service

追加パラメータ

-vまたは--verboseデバッグ目的でコンソールに詳細情報を表示します

-p 443または--port 443デフォルトの 8888 の代わりに 443 ポートで Webber サーバーを起動します

--browser chrome/safariすると、目的のブラウザが自動的に開きます。デフォルトでは何も開きません

--browser-self-signed Service Worker をローカルでデバッグするために必要です。そうしないと機能しません

--browser-incognitoモードでブラウザーの追加のインスタンスを開くには、chrome でのみ機能します。


したがって、アプリをデバッグ モードでビルドするには、Chrome でアプリを自動的に開き、ファイルを変更するたびにブラウザを自動的に更新系统します。スパ用

webber serve --browser chrome

実際の PWA テスト用

webber serve -t pwa -s Service -p 443 --browser chrome --browser-self-signed --browser-incognito


アプリの初期読み込み

前期読み込みプロセスを解决したい場合があります。

そのためには、プロジェクト内の.webber/entrypoint/devフォルダーを開き、 index.htmlファイルを編集します。

これには、非常に便利なリスナーを持つ初期 HTML コードが含まれています: WASMLoadingStarted WASMLoadingStartedWithoutProgress WASMLoadingProgress WASMLoadingError

そのコードを什么是自由に編集して、カスタム スタイルを実装したいものに変更できます 🔥

新しい実装が完了したら、同じものを.webber/entrypoint/releaseフォルダーに保存することを忘れないでください

建物のリリース

webber releaseまたはwebber release -t pwa -s Service for PWA を実行するだけです。

次に、コンパイルされたファイルを.webber/releaseフォルダーから取得し、サーバーにアップロードします。

導入方法

ファイルを容易の静的ホスティングにアップロードできます。

ホスティングはwasmファイルに正しい Content-Type を提供する必要があります!

はい、 wasmファイルの正しいヘッダーContent-Type: application/wasmを持つことが非常に重要です。そうしないと、残念ながらブラウザーは WebAssembly アプリケーションをロードできません。

たとえば、GithubPages はwasmファイルの正しい Content-Type を提供しないため、残念ながら WebAssembly サイトをホストすることはできません。

ニンクス

nginx で独自のサーバーを使用している場合は、 /etc/nginx/mime.typesを開き、 application/wasm wasm;記録。はいの場合は、準備完了です。

結論

少なくとも SwifWeb を試してみて、很大で次の大きな Web プロジェクトで SwifWeb を使い始めるようになることを願っています。


のいずれかに自由に貢献し、それらすべてにスター ⭐️ を付けてください!


あり、大きなサポートを見つけたり、小さなチュートリアルを読んだり、今後の不断更新について初始に控制を受け取ることができます!私たちと一緒に会えたら较高です!


これはまだ始まったばかりなので、SwifWeb に関する記事を今後もお楽しみに!


友達に教えて!
바카라사이트 바카라사이트 온라인바카라