TypeScriptを始めよう:デバッグの仕方

TypeScript VSCodeでデバッグの仕方

今日は前回に紹介したTypeScriptの基本の続きです。初めてTypeScriptを学ぶ人は前回の記事から読むことをお勧めします。 今日は、VSCodeにあるデバッグ機能を使ってコードを実行する順番にコードがどのように読み込まれているか見ていきましょう。 tsconfig.jsonファイルの設定 では、tsconfig.jsonファイルを見ていくつかコンフィグを設定していきます。 コンフィグファイルから下記のキーを見つけコメントを外し、下記の通りに設定します。※参考例として紹介しますが、好みで調整してください。 では、コンフィグファイルを設定し、.tsファイルを./srcディレクトリに移動したところで.tsファイルをコンパイルします。 では、コンパイルが完了するとdistディレクトリ(ディストリビューションの略)ができて.jsファイルと.js.mapファイルができたことが分かりますね。 では、.tsファイルに戻り下記の様なコードを作成したとします。 さらにVSCodeの行の左側に赤い点がクリックできるのでそれを押してブレイクポイントを作成します。 ブレイクポイントはコードを実行する際に一旦止まる場所を設定できます。そこからコードを再実行できますし前後のコードを確認することもできる便利な機能です。 launch.jsonファイル 次にVSCodeの左のタブからてんとう虫と三角のアイコン(デバッグのアイコン)をクリックします。 その中にcreate a launch.json fileというテキストがあるのでそれをクリックしてlaunch.jsonファイルを作成します。クリックするといくつかオプションが表示されるのでNode.jsを選択します。 launch.jsonファイルはNode.js用のデバッガーファイルになります。 中身はこんな感じです。 では、このコンフィグファイルに下記のラインを追加します。 “preLaunchTask”: “tsc: build – tsconfig.json”, これでこのデバッガーが実行されたときにタスクを追加することができます。 デバッグの実行 では、launch.jsonを閉じて、VSCodeのデバッグタブから下記のlaunchProgramボタンをクリックしてテストを実行します。 そうすると先ほど作成したタスクが実行され、その後にJavaScriptコードが実行されます。 ここで、先ほど設定しておいたブレイクポイントで止まったことが分かります。 この上部のメニューにあるように▶三角ボタンでコードをブレイクポイントから実行しなおすことができます。 ステップインは最もステップ単位が小さいデバッグ方法でで、基本的に1行単位で実行されます。関数の内部にも入ることができます。 ステップオーバーは同じく1行単位で実行されますが、関数があった場合その関数を実行して次の行に移行します。 ステップアウトは今実行している関数の外(呼び出し元)に出るまでプログラムを進めるステップ実行方法です。現在の関数の動作チェックができたので呼び出し元に戻りたいときに使用できます。 では、右側のVARIABLE(変数)のタブを見てみます。ここでは、デバッグに必要なWindowsオブジェクトで使用される変数などが見ることができます。 上記の画像のように変数の値がどこで変わったかを確認することができますね。 ではTypeScriptのデバッグの仕方は今日はここまで! お疲れ様でした。

TypeScriptを始めよう

TypeScript講座

TypeScriptはMiroSoft社が開発したオープンソースのプログラミング言語になります。Javascriptにデータタイプの制限を加え、開発時に起こりうるバグを防ぐために作られました。 TypeScriptを始める前に知っておくべきこと TypeScriptはJavaScriptにコンパイルできる言語ですので基本のJavaScriptが理解できていないとその良さも生かせません。 まずTypeScriptを学ぶ前に下記のJavaScriptの基本だけは知っておくようにしましょう。 TypeScriptってなに? JavaScriptを使い始めると色々な部分で変だな、ふにゃふにゃしているなと思う事がありませんか?特に変数にデータタイプを指定する必要がないので下記のようなコードが実行できてしまいます。 これはJavaScriptはDynamically Typed Language(動的型付け言語)であるからです。 ブラウザがJavaScriptコードを実行する際に勝手にデータタイプを変更してしまうので、できるだけ正解に近いものを返してくれるようになります。 これは使い勝手は良いですが、バグが発生する原因になりがちです。 これらの問題を解決するために作られたのがTypeScriptです。TypeScriptではデータタイプを指定する必要があるので静的型付け言語(static typing)といえます。 静的型付け言語 動的型付け 言語 TypeScriptC/C++C#JavaGoScalaSwift JavaScriptPythonRubyPHP メリット コンパイル時にエラーが出てくれるエラーが事前にわかるのでコンパイル時にデバッグができます。例:・整数型変数なのに文字列を代入しようとしてる・型の違うデータで関数を呼ぼうとしている 記述量が大幅に減るデータ型の記載が不要になるので、書くコード量が減る。 比較的簡単にプログラムが書けるデータタイプを気にしなくてよい。 TypeScriptはJavaScriptのすべての機能を持つ以上に開発時に変更点のフィードバックが分かるのでデベロッパーの強い味方といえます。 なのでVSCodeなどのテキストエディタで.tsのタイプスクリプトファイルの変数のデータタイプを変更しようとすると下記のようにエラーがでます。 TypeScriptの難点 ①TypeScriptをJavaScriptコードにコンパイルしないといけない。→TypeScriptのコードはブラウザが理解できないのでJavaScriptのコードに変換(コンパイル)してあげないといけないです。 ②コードの制限が細かい。→JavaScriptと比べてコードを書く量が増え、データタイプをいちいち宣言しないといけません。スケールの小さいプロジェクトの場合はJavaScriptだけで対応することも検討すべきです。 TypeScriptのプロジェクトを作成する TypeScriptを使用するにあたりNode.jsを使います。まずは使用するマシンにNode.jsがインストールされていることを確認しましょう。 つぎにtypescriptのライブラリをグローバルにインストールします。 テキストエディタ テキストエディタはVSCodeをお勧めします。 ではテキストエディタを開いて、.tsファイル(TypeScriptファイル)を作成しましょう。今回はindex.tsファイルを作成してみます。 ちなみに.tsファイルにはタイプを指定しない普通のJavaScriptを書くこともできます。 ではターミナルを開いて.tsファイルをコンパイルしてみましょう。 VSCodeのターミナルが開けたらtscのコマンドを使ってJavaScriptファイルにコンパイルしてみましょう。 そうすると新しく.jsファイルが作成されたことが確認できます。 データタイプの指定 TypeScriptでデータタイプを指定するには変数の宣言の際に:タイプの形式で記載することができます。 では、再度.tsファイルをコンパイルしてみます。中身を見てみましょう。 変数のletがvarになっていることが分かりますね。これはコンパイル時にES5(ECMAScript)の基準に従って古いブラウザでも実行できるファイルに変換しているからです。なのでアロー関数を使った場合もES5の基準に従って変換してもらえます。 もしES6の基準で.tsファイルをコンパイルしたい場合は–targetフラグを使用してください。 TypeScriptのコンフィグファイル ではターミナル(コマンドプロンプト)から下記のコマンドでTypeScriptのコンフィグファイルを作成してみましょう。 コマンドの実行後にtsconfig.jsonファイルが作成されました。 この中身を見てみると多くのコンフィグがコメントアウトされています。これを必要に応じて設定することができます。説明はコンフィグの右側にあります。ではtargetフラグを見てみます。 では中身を消して””だけ残してみましょう。その後にカーソールが””の中にある際に、コントール+スペースバーを押すとすべてのオプションを見ることができます。 そのほかに、rootDir、outDirで.tsファイルを読み込むディレクトリ、書き出し先を設定できます。これでファイルを指定せずにコンパイルを実行することができます。 また“removeComments”: trueに設定することで.jsファイルにコメントを除くことができます。これは便利ですね。 “noEmit”: trueを設定すると、エラーがある際にはコンパイルが実行されません。 また、TypeScriptに慣れた時点でコンフィグ設定を学ぶことになるので、現段階ですべてを理解する必要はありません。 では、次回以降にTypeScriptのデバッグの仕方、詳しい書き方を説明していきたいと思います。 お疲れ様でした。

DjangoアプリにGoogleでログインできるようにする

Django-allauth

こんにちは。今日はDjangoアプリにGoogleなどのソーシャルメディアアカウントを使ってログインする方法を紹介します。

今回使用するdjango-allauthライブラリを使用することにより、ソーシャルメディアでユーザーをサインアップさせることができます。

詳しいドキュメンテーションはこちらを参照してください。(英語ですが。)

あと、Googleクラウドのサービスは課金されるものになるのでテストが完了したら課金はオフにしておきましょう。(最初に登録した場合は無料のクレジットがもらえるはずですが。)

ちなみに今回はフロントエンドはDjangoのテンプレートを想定していますが、REST APIを使ってログインする方法も今後紹介する予定ですのでお見逃しなく。

Vue3とVuetifyでゲームみたいなメニューを作る

Vue ゲームのスタート画面

今日は前回紹介したVueのUIライブラリのVuetifyを使ってゲームのスタート画面のようなUIを作ってみます。 今日のテーマは、Vueの基礎、JavaScriptの基礎、CSSの基礎の復習や知識を固めるために練習する良い素材になると思います。 完成したコードはGitHubで公開しているので見てみてください。 前回の続きなのでVue+Vuetifyのプロジェクトを用意して早速とりかかりましょう。 もし、Vuetifyを使いたくない方はViteでVueのプロジェクトを作成するだけでも同じことができるのでご安心を。 まずは、使えそうなレイアウトをコピペします。 v-containerは、コンテンツを左右の真ん中と、上下の真ん中に位置させることができるコンポーネントです。 v-rowは、v-colのwrapper(囲うもの)でflexを使って中のcol(カラム)のレイアウトをコントロールすることができます。 v-colはかならずv-rowの直下に位置されます。ここに表示されるコンテンツを入れることになります。 v-sheetは複数のコンポーネントで使えるベースラインになります。 レイアウトの作成 では、ゲームの画面のように左側にメニュー、右側にカードを作成します。 Vueitfyのv-colにはmax-widthが100%で設定されていたため、divに右と左のcolクラスを作成して別々のサイズにしました。 見やすいようにわざと色を付けていますが、後から外します。 背景画像 では、バックグラウンドの画像をPixabayで見つけて貼り付けます。これは商用無料で使える写真を探すときに便利なサイトです。 背景画像のファイルはsrc/assetsに、bg-img.jpgファイルで置いておきます。プロジェクトが大きくなったら、画像をまとめる用のフォルダを作成するべきですが今はOKとします。 ではスタイルに追加します。 このように背景画像が加わり、いい感じになりました。 メニューの作成 メニュー(ナビゲーションバー)を作成します。これは別のコンポーネントを作成してApp.vueにインポートする形にします。 では、components/Menu.vueファイルを作成して下記のように書きます。 次にApp.vueの方でこのコンポーネントをインポートしてあげます。 これでホバーエフェクトのあるメニューができました。次にメニューのタイトルをpropsとして親コンポーネントのApp.vueから子のMenu.vueに渡してあげます。 次に、メニューにガラスのような透明エフェクトをかけたいのでこのCSSツールから作成します。他にもデザインで使えるCSSツールを記事にしているので是非読んでください。 とりあえず、このような感じになりました。 コードはこのようになります。 Menu.vue コンテンツを作成 次に、各メニューをクリックした際に表示される右側のコンテンツのコンポーネントを作成します。ファイルはcomponents/Content.vueの名前にしました。これも同じようにApp.vueにインポートさせます。 コンテンツの中身もpropsでContent.vueに渡してあげるようにします。 次に、メニューボタンをクリックしたときに各コンテンツが切り替わるようにします。 App.vue Content.vueはとりあえずこのようにしておきます。 親のApp.vueからコンテンツのpropsをもらえるようにしておきます。 Content.vue これでクリックするとコンテンツが表示されるようにはなりました。 次にransform: perspective(600px) rotateY(10deg);のCSSを左と右のcolクラスに追加して、3Dに見えるようにします。 最後にボタンがアクティブの際にボタンに色を付けて完了です。 完成したイメージはこちらです。 完成したコードはこちら(GitHub)からどうぞ。

Vue3でVuetifyをインストールしよう

Vuetifyのインストール

Vueを使い始めてUIライブラリを探すと色々出てきますね。PrimeVue、BootstrapVue、Quasar、Buefyなど他にも選択肢はあります。今日はそのうちでも人気のVuetifyのインストールから基本的な使い方まで説明します。 Vue3の良いところ VueはReactと比べて初心者向けという位置づけで認識されがちです。しかし機能的にはReactに劣らず、書くコードの数も減ることがよくあります。個人的な意見になりますが、ReactのようにFacebookのような大企業のブランドがバックアップしていないというだけでマーケティングで負けてしまったように受けられます。 Vueは素晴らしいフレームワークで仕事でReactに縛られるという事がなければVueを使っていくべきだと思います。 なぜVuetify ①無料で使える Vuetifyはソースコードを公開しているオープンソースになります。MITライセンスなので安心して商用で使えます。必要があればソースコードをいじることもOKです。 ②フレキシブルなコンポーネント コンポーネントのカスタマイズがしやすいように作られているため個性を出しやすいです。また、Googleのマテリアルデザインをテーマに作成されているのでスッキリとした見た目でデザインができます。 ③ツールが豊富 FigmaのUIコンポーネントキットが使えます。(Figmaの使い方はこちら) Viteのビルドツールで作成されたVueにVuetifyを実装できます。 TypeScriptとJavaScriptの両方で使えます。 もし、わからに事があればDiscordのコミュニティに参加して質問ができる。などなど ViteでVueプロジェクトの作成 今回はVue CLIではなくViteを使います。Viteがスタンダードになってきているので是非使ってください。 yarnを使ってインストールする場合は次の記事を見てください。 アプリ名は自分の好きなものを付けてください。 ではプロジェクトフォルダに移動してライブラリをインストールします。 YarnでVuetifyをインストール yarnを使っている人は下記のコマンドで一発でVueとVuetifyがインストールできます。 このあとにプロジェクトフォルダに移動してテストサーバーを起動できます。 Vuetifyのインストール ではnpmを使ってVuetifyをインストールします。yarnを使わない人はこちらを使ってください。最初にも書いたようにViteでVueのプロジェクトを先に作成しておいてください。 Vuetifyの設定 では、VuetifyをインストールしたところでVue3で使えるように設定していきます。 ではsrc/main.jsを下記の様に上書きしましょう。 これで全部のコンポーネントをインポートしていますが、好みで必要なコンポーネントだけインポートすることも可能です。 ではテストサーバーを実行しておきましょう。 コンポーネントの使い方 ではsrc/App.vueにVuetifyのコンポーネントを入れてみます。 このようにグローバルでVuetifyのコンポーネントが使えるようになりました。 コンポーネントごとで使えるprosや設定が変わってくるので詳しくはドキュメンテーションを確認するようにしましょう。 スペーシング またスタイルを変更したい場合は下記の様にクラスを追加することでmarginやpaddingを変更することができます。 このようにma-5(マージン オール)のクラスを追加することで簡単にスタイルが変更することができましたね。 詳しいスペースの使い方です。 このma-5は下記の要素が結合したものです。 {property}{direction}-{size} propertyの箇所は: directionの箇所は: sizeの箇所は 4pxずつ増えていきます: Breakpoints(ブレイクポイント) Flexboxを使ったリスポンシブなクラスを設定することができます。 デバイス コード タイプ レンジ エキストラスモール xs モバイル < 600px … Read more

コピペだけ!anime.jsでクールなスライドを作ろう

anime.js スライダー

今日はanime.jsのライブラリを紹介します。このライブラリを使う事で複雑なアニメーションをJavaScriptを使ってよりシンプルに書くことができます。 anime.jsはオープンソースなのでソースコードをGitHubからも見ることができます。CodePenから実際のデモを見ることもできるので見てみてください。 今日の完成プロジェクトはこのようになります。 今日の環境 では、大体どのようなことができるかを理解できたところで早速、基本のコードを書いていきましょう。 プロジェクトの作成 今回はVanilla JavaScriptでプロジェクトを作成しますがビルドツールとしてViteを使います。 anime.jsのインストール では、npmを使ってanime.jsをインストールしましょう。 コードを書こう ではテキストエディタを開いてコードを書いていきます。 Bootstrapを使ってUIを作成したい場合はViteを使ったBootstrapの設定の仕方はこちらの記事を見てください。 では、index.htmlをこのように書いてみます。 次にmain.jsの中身を消して下記のように書いてみましょう。 そうすると、ブラウザがロードされたときに.btnクラスのHTML要素が右に動きましたね。 これが基本になります。 もし、クリックしたらボタンがアニメーションしたいときはこのようになります。 JavaScriptの基本に戻りましょう。 完成するとこのようになりますね。 画像スライダーを作る こちらを参考にしました。 index.htmlはこのようになります。 各gridクラスのdivタグ内に画像ファイルがあることに注目しましょう。 次に、styles.css(styles.scss)を書きます。 最後に、main.jsでスライダーのロジックを作成します。 init()関数でブラウザがDOMContentLoadedのイベントを実行したときにイベントをマウントさせます。 この中でスライダーやナビゲーションのHTML要素を格納し、クリックイベントを実装します。 画像にdelay:の要素を加えていることに注目してください。 完成したコードはこちら(GitHub)からどうぞ。

ReactのuseMemoを理解する

useMemo-React

ReactのStame Managementのシリーズでは、useStateを使った簡単な状態管理、useReducerを使った複雑な状態管理の仕方を学びました。 今日はその続きとして、React HookのuseMemoの使い方を紹介します。 これはVueのフレームワークを使ったことがある方ならweatch()メソッドと同じになると理解してもらってOKです。 では、前回行ったreuceメソッドを利用した例を見ていきます。 まずは、こちらのコードを見てください。 ここでnumbers配列の数字の合計値をブラウザに表示しているようにしています。ここで感がれられる問題があります。もし、数字の入っている配列が膨大な数である場合、コンポーネントがレンダーされるたびにreduceの関数が実行されてコンポーネントのレンダーに時間がかかることが想定されます。 そこでuseMemoを使って、dependency(依存するデータ)を教えてあげることができます。 この場合でいうと、numbersの配列が変わらない限り答えは同じになるのでそれを教えてあげるわけです。 useMemoはそこまで難しいコンセプトではないので今日はこれくらいで。

ReactのuseReducerを理解する

reactのuseReducerを理解する

前回の記事ではReactのuseStateの正しい使い方を説明しました。 今回はReact Hooksのうちの一つであるuseReducerの使い方を説明します。 reducerとは まずは、JavaScriptのreduceメソッドについておさらいしましょう。 reduceは英語で減らすという意味ですね。イメージとして、配列の複数データを操作して1つの値に減らすためのメソッドであると考えてください。 useReducerも同じようにState(状態)を管理できるメソッドになります。 配列の数字をループして加算したい場合はこのようにできます。 これで、ブラウザに合計の60が表示されました。 同じことをreduceメソッドを使って行ってみます。 reduceメソッドは2つのパラメータを受け取ります。 最初のパラメータは関数で、2つ目が初期値(スタートポイント)になります。 これで同じように合計値の60がブラウザに表示されました。 reduce()関数の最初に使う関数では①accumulatorと②currentValueがパラメータとして使えます。 accumulatorはaccumulate(蓄積する)という意味です。つまり、今までの数値を累積(足した)値になります。いわば、前回までの累積値といえます。 2番目に来るcurrentValueは現在の値です。このようにreduceメソッドでは前回までの累積値と現在の値をどうするか、指示することができます。 今回の場合は単純に配列の前回の値(0番目)と現在地(1番目)を足すという指示をしています。 その次に配列の前回の値(0と1番目の累積値)と現在地(2番目)を足す。これを繰り返しているだけです。 これでreduceメソッドの使い方が理解できましたね。 useReducer ReactのuseReducerは基本はこのようになります。 useReducerからは2つの値が返ってきます。それを慣例的にstateと、dispatchと名称を付けて取っておきます。 useReducerがreturn返す値 state:現在の状態。最初のレンダーでは、初期値のinitialArg、もしくはinit(initialArg)が返ってきます。 dispatch:どのようにコンポーネントを再レンダーし、stateを更新するか指示する関数 useReducer関数の引数について reducer:reducerでは、stateがどのようにアップデートされたいのか指示することができます。 initialArg:デフォルト値(データタイプに指定はありません。) init?:オプショナルの引数です。もしinitialArgに何かしらの関数を実行したい場合はここで指示できます。その場合はinit(initialArg)になります。もし空の場合は、initialArgはデフォルト値として渡されます。 useReducerの実例 では下記の実例を見てみましょう。 このようにinputへの入力と同時にstate.nameの状態が更新されコンポーネントが再度レンダーされました。 inputに入力されれるたびにonChangeのdispatchが発火されpayload(データ)が更新されます。dispatchはuseReducerのstateを更新する役目があるので、コンポーネントが更新されたわけですね。 次に、ボタンを作成して、state.namesにnameのデータを追加していきましょう。 …のスプレッド構文を使って、配列に現在のstateを残したまま、新しいデータを追加しています。 これで、データの入力時に今までのデータを付けるので”追加”できるようになります。 このようにuseReducerを使うことで複雑なstate(状態)の管理が可能になります。

ReactのuseStateを徹底的に理解する

ReactのuseStateを理解する

Reactを学び始めてからSPA(シングルページアプリケーション)の良さが理解できたところでState Management(状態管理)を学んでいる方、useStateが理解できない方、このコンセプトを理解することは、コンポーネントのレンダーにもかかわる重要な部分になります。 Reduxなどのライブラリを使う前にも理解しておきたいです。 State Management State Managementはデータを管理するフロントエンド側のデータ倉庫のようなものです。これらはReact Hookの機能のうちのひとつです。React Hook(フック)はコンポーネントがマウント(読み込み)されたときにフック(実装)される機能と考えてください。 では実際にReactアプリを作成してコードを書きながらStateManagementを理解していきましょう。Reactのプロジェクトの作成の仕方はこちらの記事を読んでください。 まずは、メインのコンポーネントになるsrc/App.jsxの中身を削除して下記のように記述しましょう。 useState()はreactのデフォルトで使えるStateManagementのメソッドになります。これをインポートして App()のコンポーネント内に定義しましょう。const [count, setCount] でuseStateメソッドの最初の値(データになる部分)をcountという名称にしました。 2番目の値useState[1]にあたる部分は、とsetCountという変数名にしました。慣習として2つ目に来るsetterはsetで始まる名称を付けることが通常になります。setterというのはstate(状態)が変わることを指示をする関数になります。 これで<div>内の{count}の部分がデフォルトの10として表示されます。 ではこの10に1の数字を足す関数を作ってみましょう。 まずは悪い例を紹介します。 ※これは悪い例です。 このボタンをクリックすると、addOne関数が発火されますが何も起きません。。。コンソールを見てみると下記の様なエラーが出ました。 constは再代入できない変数なのでエラーが出ました。 ではこのconst [count, setCount] = useState(10);をletに変えるとどうなるでしょうか? 。。。。。。 。。。。 何も起きません。 しかもエラーも出ません。 なぜでしょうか。 にletを使って変数の値が変えられたとしてもReact側では、state(状態)が変化されたことが察知されていないからです。 ReactのState Managementの推奨ルールとして、constを使う事が定義されています。それは、Reactがコンポーネントを再レンダーした際に新しい値をconstの変数に読み込むことになるからです。それ以外の時に変数が変わってしまってもReactは理解することができません。ですのでその問題を防ぐためにもconstを使うことをお勧めします。 状態の変化を理解する では状態の変化を理解するために、同じ問題を別のコードで再現してみます。 オリジナルのnewNumはそのまま32と表示されますが、上書きした方のmyNumは0に変更されたものが表示されました。 オブジェクトの場合 オブジェクトを上書きした場合は、元のオブジェクトまで変更されてしまいました。 これは、JavaScriptの特徴でプリミティブのデータ(String、Number、Booleanなど)の場合はデータのコピーを返し、Array(配列)やオブジェクトの場合はReference(参照)できるデータを返します。つまり、オリジナルのデータという事になります。 このコンセプトを理解することが重要になります。 ではすべてオブジェクトにしてしまえばよいのでは? そう思って、下記の様にオブジェクトにしたところ、Reactがcountの状態が変わったことを察知できなかったので実際にデータが変わったとしてもRe-render(画面の更新)が行われませんでした。 このようにコンソールには正しくcountの値が更新されていますが、Reactではレンダーされません。 そういう事なので、useState()のsetterを使ってuseState()で設定した値を更新すること必須であることが分かりました。 これは、useStateのsetterを使わないとReactがState Managementで管理しているデータの変化を察知できず、コンポーネントの再レンダーができないからです。 Setterを使ってstateを更新する では今までに学習したことをもとに、useStateについてくるSetter(useStateの2番目にあるメソッド)を使ってcountを更新します。 これでボタンを押したときに関数が変わり、Reactが最新の状態に合わせてレンダー(読み込み)してくれました。 prevStateを使った状態管理で解決できること 前回までに記載した現在の状態を操作するsetCount(count + … Read more