Jing Zhang

Jing Zhang

1625812600

Next.js Tutorial - 21 - Running Static Generation Builds

Next.js Tutorial - 21 - Running Static Generation Builds

Running Static Generation Builds in Next.js

📘 Courses - https://learn.codevolution.dev/
⚡ Checkout Taskade! https://www.taskade.com/
💖 Support - https://www.paypal.me/Codevolution
💟 Github - https://github.com/gopinav

📱 Follow Codevolution

⭐ Kite Code Completion - https://www.kite.com/get-kite/?utm_medium=referral&utm_source=youtube&utm_campaign=codevolution&utm_content=description-only

📫 Business - codevolution.business@gmail.com

#next #react

What is GEEK

Buddha Community

Next.js Tutorial - 21 - Running Static Generation Builds

NBB: Ad-hoc CLJS Scripting on Node.js

Nbb

Not babashka. Node.js babashka!?

Ad-hoc CLJS scripting on Node.js.

Status

Experimental. Please report issues here.

Goals and features

Nbb's main goal is to make it easy to get started with ad hoc CLJS scripting on Node.js.

Additional goals and features are:

  • Fast startup without relying on a custom version of Node.js.
  • Small artifact (current size is around 1.2MB).
  • First class macros.
  • Support building small TUI apps using Reagent.
  • Complement babashka with libraries from the Node.js ecosystem.

Requirements

Nbb requires Node.js v12 or newer.

How does this tool work?

CLJS code is evaluated through SCI, the same interpreter that powers babashka. Because SCI works with advanced compilation, the bundle size, especially when combined with other dependencies, is smaller than what you get with self-hosted CLJS. That makes startup faster. The trade-off is that execution is less performant and that only a subset of CLJS is available (e.g. no deftype, yet).

Usage

Install nbb from NPM:

$ npm install nbb -g

Omit -g for a local install.

Try out an expression:

$ nbb -e '(+ 1 2 3)'
6

And then install some other NPM libraries to use in the script. E.g.:

$ npm install csv-parse shelljs zx

Create a script which uses the NPM libraries:

(ns script
  (:require ["csv-parse/lib/sync$default" :as csv-parse]
            ["fs" :as fs]
            ["path" :as path]
            ["shelljs$default" :as sh]
            ["term-size$default" :as term-size]
            ["zx$default" :as zx]
            ["zx$fs" :as zxfs]
            [nbb.core :refer [*file*]]))

(prn (path/resolve "."))

(prn (term-size))

(println (count (str (fs/readFileSync *file*))))

(prn (sh/ls "."))

(prn (csv-parse "foo,bar"))

(prn (zxfs/existsSync *file*))

(zx/$ #js ["ls"])

Call the script:

$ nbb script.cljs
"/private/tmp/test-script"
#js {:columns 216, :rows 47}
510
#js ["node_modules" "package-lock.json" "package.json" "script.cljs"]
#js [#js ["foo" "bar"]]
true
$ ls
node_modules
package-lock.json
package.json
script.cljs

Macros

Nbb has first class support for macros: you can define them right inside your .cljs file, like you are used to from JVM Clojure. Consider the plet macro to make working with promises more palatable:

(defmacro plet
  [bindings & body]
  (let [binding-pairs (reverse (partition 2 bindings))
        body (cons 'do body)]
    (reduce (fn [body [sym expr]]
              (let [expr (list '.resolve 'js/Promise expr)]
                (list '.then expr (list 'clojure.core/fn (vector sym)
                                        body))))
            body
            binding-pairs)))

Using this macro we can look async code more like sync code. Consider this puppeteer example:

(-> (.launch puppeteer)
      (.then (fn [browser]
               (-> (.newPage browser)
                   (.then (fn [page]
                            (-> (.goto page "https://clojure.org")
                                (.then #(.screenshot page #js{:path "screenshot.png"}))
                                (.catch #(js/console.log %))
                                (.then #(.close browser)))))))))

Using plet this becomes:

(plet [browser (.launch puppeteer)
       page (.newPage browser)
       _ (.goto page "https://clojure.org")
       _ (-> (.screenshot page #js{:path "screenshot.png"})
             (.catch #(js/console.log %)))]
      (.close browser))

See the puppeteer example for the full code.

Since v0.0.36, nbb includes promesa which is a library to deal with promises. The above plet macro is similar to promesa.core/let.

Startup time

$ time nbb -e '(+ 1 2 3)'
6
nbb -e '(+ 1 2 3)'   0.17s  user 0.02s system 109% cpu 0.168 total

The baseline startup time for a script is about 170ms seconds on my laptop. When invoked via npx this adds another 300ms or so, so for faster startup, either use a globally installed nbb or use $(npm bin)/nbb script.cljs to bypass npx.

Dependencies

NPM dependencies

Nbb does not depend on any NPM dependencies. All NPM libraries loaded by a script are resolved relative to that script. When using the Reagent module, React is resolved in the same way as any other NPM library.

Classpath

To load .cljs files from local paths or dependencies, you can use the --classpath argument. The current dir is added to the classpath automatically. So if there is a file foo/bar.cljs relative to your current dir, then you can load it via (:require [foo.bar :as fb]). Note that nbb uses the same naming conventions for namespaces and directories as other Clojure tools: foo-bar in the namespace name becomes foo_bar in the directory name.

To load dependencies from the Clojure ecosystem, you can use the Clojure CLI or babashka to download them and produce a classpath:

$ classpath="$(clojure -A:nbb -Spath -Sdeps '{:aliases {:nbb {:replace-deps {com.github.seancorfield/honeysql {:git/tag "v2.0.0-rc5" :git/sha "01c3a55"}}}}}')"

and then feed it to the --classpath argument:

$ nbb --classpath "$classpath" -e "(require '[honey.sql :as sql]) (sql/format {:select :foo :from :bar :where [:= :baz 2]})"
["SELECT foo FROM bar WHERE baz = ?" 2]

Currently nbb only reads from directories, not jar files, so you are encouraged to use git libs. Support for .jar files will be added later.

Current file

The name of the file that is currently being executed is available via nbb.core/*file* or on the metadata of vars:

(ns foo
  (:require [nbb.core :refer [*file*]]))

(prn *file*) ;; "/private/tmp/foo.cljs"

(defn f [])
(prn (:file (meta #'f))) ;; "/private/tmp/foo.cljs"

Reagent

Nbb includes reagent.core which will be lazily loaded when required. You can use this together with ink to create a TUI application:

$ npm install ink

ink-demo.cljs:

(ns ink-demo
  (:require ["ink" :refer [render Text]]
            [reagent.core :as r]))

(defonce state (r/atom 0))

(doseq [n (range 1 11)]
  (js/setTimeout #(swap! state inc) (* n 500)))

(defn hello []
  [:> Text {:color "green"} "Hello, world! " @state])

(render (r/as-element [hello]))

Promesa

Working with callbacks and promises can become tedious. Since nbb v0.0.36 the promesa.core namespace is included with the let and do! macros. An example:

(ns prom
  (:require [promesa.core :as p]))

(defn sleep [ms]
  (js/Promise.
   (fn [resolve _]
     (js/setTimeout resolve ms))))

(defn do-stuff
  []
  (p/do!
   (println "Doing stuff which takes a while")
   (sleep 1000)
   1))

(p/let [a (do-stuff)
        b (inc a)
        c (do-stuff)
        d (+ b c)]
  (prn d))
$ nbb prom.cljs
Doing stuff which takes a while
Doing stuff which takes a while
3

Also see API docs.

Js-interop

Since nbb v0.0.75 applied-science/js-interop is available:

(ns example
  (:require [applied-science.js-interop :as j]))

(def o (j/lit {:a 1 :b 2 :c {:d 1}}))

(prn (j/select-keys o [:a :b])) ;; #js {:a 1, :b 2}
(prn (j/get-in o [:c :d])) ;; 1

Most of this library is supported in nbb, except the following:

  • destructuring using :syms
  • property access using .-x notation. In nbb, you must use keywords.

See the example of what is currently supported.

Examples

See the examples directory for small examples.

Also check out these projects built with nbb:

API

See API documentation.

Migrating to shadow-cljs

See this gist on how to convert an nbb script or project to shadow-cljs.

Build

Prequisites:

  • babashka >= 0.4.0
  • Clojure CLI >= 1.10.3.933
  • Node.js 16.5.0 (lower version may work, but this is the one I used to build)

To build:

  • Clone and cd into this repo
  • bb release

Run bb tasks for more project-related tasks.

Download Details:
Author: borkdude
Download Link: Download The Source Code
Official Website: https://github.com/borkdude/nbb 
License: EPL-1.0

#node #javascript

Eva  Murphy

Eva Murphy

1625674200

Google analytics Setup with Next JS, React JS using Router Events - 14

In this video, we are going to implement Google Analytics to our Next JS application. Tracking page views of an application is very important.

Google analytics will allow us to track analytics information.

Frontend: https://github.com/amitavroy/video-reviews
API: https://github.com/amitavdevzone/video-review-api
App link: https://video-reviews.vercel.app

You can find me on:
Twitter: https://twitter.com/amitavroy7​
Discord: https://discord.gg/Em4nuvQk

#next js #js #react js #react #next #google analytics

Jing Zhang

Jing Zhang

1625812600

Next.js Tutorial - 21 - Running Static Generation Builds

Next.js Tutorial - 21 - Running Static Generation Builds

Running Static Generation Builds in Next.js

📘 Courses - https://learn.codevolution.dev/
⚡ Checkout Taskade! https://www.taskade.com/
💖 Support - https://www.paypal.me/Codevolution
💟 Github - https://github.com/gopinav

📱 Follow Codevolution

⭐ Kite Code Completion - https://www.kite.com/get-kite/?utm_medium=referral&utm_source=youtube&utm_campaign=codevolution&utm_content=description-only

📫 Business - codevolution.business@gmail.com

#next #react

PythonでのCeleryを䜿甚した非同期タスク

埓来のWebアプリケヌションは本質的に同期しおいたす。 ãƒŠãƒŒã‚¶ãƒŒã¯ãƒ–ラりザヌに衚瀺されるWebむンタヌフェヌスを操䜜し、ブラりザヌはそのナヌザヌ操䜜に基づいおサヌバヌに芁求を返し、サヌバヌはナヌザヌの新しい衚瀺でそれらの芁求に応答したす。

今日、状況は倉化したした。珟代のWebサむトは、数十䞇の蚪問者からの芁求を凊理する必芁がありたす。これらの芁求にデヌタベヌスたたはWebサヌビスずの察話が含たれる堎合、応答時間が長くなり、䜕千もの蚪問者が同じリ゜ヌスにアクセスしおいる堎合、Webサむトのパフォヌマンスが倧幅に䜎䞋する可胜性がありたす。 ã“こで非同期Webが助けになりたす。

非同期性を遞択したずきに把握できる利点のいく぀かを次に瀺したす。

  • より倚くのリク゚ストを凊理する機胜
  • I / Oバりンドメ゜ッドの䞊列実行。
  • 応答性が向䞊したした。

範囲

このチュヌトリアルでは、新しい芁求に応答するWebサヌバヌの機胜を制限する長時間実行タスクを凊理するWebアプリケヌションを構築するずきに発生する䞀般的な萜ずし穎の1぀を克服する方法を説明したす。

簡単な解決策は、これらの長時間実行タスクをバックグラりンドで非同期に、別のスレッドたたはプロセス内で実行し、Webサヌバヌを解攟するこずです。

Redis、Flask、Celery、SocketIOなどのいく぀かのコンポヌネントを掻甚しお、長時間実行されるタスクの実行をオフロヌドし、完了したら、クラむアントにそのステヌタスを瀺すプッシュ通知を送信したす。

このチュヌトリアルでは、コルヌチンを䜿甚しおコヌドを同時に実行できるasyncioPythonの組み蟌みラむブラリに぀いおは説明しおいたせん。

前提条件

芁件が満たされるず、次のコンポヌネントが機胜したす。

Redisは、オヌプン゜ヌスの高床なKey-Valueストアであり、高性胜でスケヌラブルなWebアプリケヌションを構築するための適切な゜リュヌションです。それを際立たせる3぀の䞻な特城がありたす

Redisはデヌタベヌスを完党にメモリに保持し、氞続性のためにのみディスクを䜿甚したす。

倚くのKey-Valueデヌタストアず比范するず、Redisには比范的豊富なデヌタ型のセットがありたす。

  • Redisは、任意の数のスレヌブにデヌタを耇補できたす。

Redisのむンストヌルは、このチュヌトリアルの範囲倖です。ただし、Windowsマシンにむンストヌルするには、このクむックガむドに埓うこずをお勧めしたす。

LinuxたたはmacOSを䜿甚しおいる堎合は、以䞋のコマンドのいずれかを実行するず、Redisがセットアップされたす。

Ubuntu / Debian

$ sudo apt-get install redis-server

マックOS

$ brew install redis
$ brew services start redis

泚意 ã“のチュヌトリアルでは、Redisバヌゞョン3.0.504を䜿甚しおいたす

セロリPythonの䞖界で最も人気のあるバックグラりンドゞョブマネヌゞャヌの1人です。リアルタむム操䜜に重点を眮いおいたすが、スケゞュヌリングもサポヌトしおいたす。RedisやRabbitMQなどのいく぀かのメッセヌゞブロヌカヌず互換性があり、プロデュヌサヌずコンシュヌマヌの䞡方ずしお機胜できたす。

requirements.txt ãƒ•ァむルにCeleryをむンストヌルし ãŸã™ã€‚

泚意 ã“のチュヌトリアルでは、Celeryバヌゞョン4.4.7を䜿甚しおいたす。

  • Socket.IOリアルタむムWebアプリケヌション甚のJavaScriptラむブラリです。これにより、Webクラむアントずサヌバヌ間のリアルタむムの双方向通信が可胜になりたす。これには、ブラりザで実行されるクラむアント偎ラむブラリずサヌバヌ偎ラむブラリの2぀の郚分がありたす。 
  • FlaskPythonで蚘述されたWebアプリケヌションのマむクロフレヌムワヌク。

食欲をそそる

このチュヌトリアルでは、スキャフォヌルディング手法を採甚し、同期通信ず非同期通信の違い、および非同期通信のバリ゚ヌションを理解するために、䞀連のさたざたなシナリオに぀いお説明したす。

すべおのシナリオはFlaskフレヌムワヌク内で提瀺されたす。ただし、それらのほずんどは他のPythonフレヌムワヌクDjango、Pyramidに簡単に移怍できたす。

このチュヌトリアルに興味をそそられ、すぐにコヌドに飛び蟌みたいず思う å Žåˆã¯ã€ã“の蚘事で䜿甚されおいるコヌドに぀いお、このGithubリポゞトリにアクセス ã—おください。

アプリケヌションスケルトンの䜜成

私たちのアプリケヌションは以䞋で構成されたす

  • app_sync.py åŒæœŸé€šä¿¡ã‚’玹介するプログラム ã€‚
  • app_async1.py ã‚¯ãƒ©ã‚€ã‚¢ãƒ³ãƒˆãŒãƒãƒŒãƒªãƒ³ã‚°ãƒ¡ã‚«ãƒ‹ã‚ºãƒ ã‚’䜿甚しおサヌバヌ偎プロセスのフィヌドバックを芁求する可胜性がある非同期サヌビス呌び出しを瀺すプログラム ã€‚
  • app_async2.py ã‚¯ãƒ©ã‚€ã‚¢ãƒ³ãƒˆãžã®è‡ªå‹•フィヌドバックを䌎う非同期サヌビス呌び出しを瀺すプログラム ã€‚
  • app_async3.py ã‚¯ãƒ©ã‚€ã‚¢ãƒ³ãƒˆãžã®è‡ªå‹•フィヌドバックを䌎う、スケゞュヌル埌の非同期サヌビス呌び出しを瀺すプログラム ã€‚

セットアップに飛び蟌みたしょう。もちろん ã€ã‚·ã‚¹ãƒ†ãƒ ã«Python3をむンストヌルする必芁があり ãŸã™ã€‚私は必芁なラむブラリをむンストヌルする仮想環境を䜿甚したすそしおあなたも間違いなくそうするべきです

$ python -m venv async-venv
$ source async-venv/bin/activate

名前の付いたファむルを䜜成し、 requirements.txt ãã®äž­ã«æ¬¡ã®è¡Œã‚’远加したす。

Flask==1.1.2
Flask-SocketIO==5.0.1
Celery==4.4.7
redis==3.5.3
gevent==21.1.2
gevent-websocket==0.10.1
flower==0.9.7

今それらをむンストヌルしたす

$ pip install -r requirements.txt

このチュヌトリアルを終了するず、フォルダヌ構造は次のようになりたす。

プロゞェクト構造

それがクリアされたので、実際のコヌドを曞き始めたしょう。

たず、アプリケヌションの構成パラメヌタを次のように定矩したす config.py。

#config.py
#Application configuration File
################################

#Secret key that will be used by Flask for securely signing the session cookie
# and can be used for other security related needs
SECRET_KEY = 'SECRET_KEY'

#Map to REDIS Server Port
BROKER_URL = 'redis://localhost:6379'

#Minimum interval of wait time for our task
MIN_WAIT_TIME = 1
#Maximum interval of wait time for our task
MAX_WAIT_TIME = 20

泚意簡朔にするために、これらの構成パラメヌタヌをにハヌドコヌディングしたしたが config.py、これらのパラメヌタヌを別のファむルたずえば.envに保存するこずをお勧めしたす ã€‚

次に、プロゞェクトの初期化ファむルを次の堎所に䜜成したす init.py。

#init.py
from flask import Flask

#Create a flask instance
app = Flask(__name__)
#Loads flask configurations from config.py
app.secret_key = app.config['SECRET_KEY']
app.config.from_object("config")

#Setup the Flask SocketIO integration (Required only for asynchronous scenarios)
from flask_socketio import SocketIO
socketio = SocketIO(app,logger=True,engineio_logger=True,message_queue=app.config['BROKER_URL'])

シナリオ1同期通信の玹介

コヌディングに入る前に、同期通信に぀いお簡単に説明したす。

で同期通信、発呌者がサヌビスを芁求し、完党にサヌビスを埅ち受けたす。そのサヌビスの結果を受け取った堎合にのみ、䜜業を続行したす。タむムアりトを定矩しお、定矩された期間内にサヌビスが終了しない堎合、呌び出しは倱敗したず芋なされ、呌び出し元は続行したす。 

同期通信がどのように機胜するかを理解するために、専甚のりェむタヌが割り圓おられたず想像しおください。圌はあなたの泚文を受け取り、それをキッチンに届け、そこでシェフがあなたの料理を準備するのを埅ちたす。この間、りェむタヌは䜕もしおいたせん。

次の図は、同期サヌビス呌び出しを瀺しおいたす。

同期通信

同期通信はシヌケンシャルタスクに適しおいたすが、同時タスクが倚数ある堎合、プログラムはスレッドを䜿い果たし、スレッドが䜿甚可胜になるたで新しいタスクを埅機させる可胜性がありたす。

それでは、コヌディングに取り掛かりたしょう。Flaskがレンダリングできるテンプレヌトindex.htmlを䜜成し、その䞭に次のHTMLコヌドを含めたす。

templates/index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Synchronicity versus Asynchronicity</title>
    <link rel="stylesheet" href="{{url_for('static',filename='css/materialize.min.css')}}">
    <script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
    <script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  </head>
  <body class="container">
    <div class="row">
        <h5>Click to start a post scheduled ansycnhronous task with automatic feedback.</h5>
    </div>
    <div class="card-panel">
      <form method='post' id="runTaskForm" action="/runPSATask">
          <div>
             <input id="duration" name="duration" placeholder="Enter duration in seconds. for example: 30" type="text">
             <label for="duration">Duration</label>
          </div>
          <button style="height:50px;width:600px" type="submit" id="runTask">Run A Post Scheduled Asynchronous Task With Automatic Feedback</button>
      </form>
    </div>
    <div class="row">
        <div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
    </div>
    <script>
      $(document).ready(function(){
        var namespace='/runPSATask';
        var url = 'http://' + document.domain + ':' + location.port + namespace;
        var socket = io.connect(url);
        socket.on('connect', function() {
                             socket.emit('join_room');
                            });

        socket.on('msg' , function(data) {
                            $("#Messages").prepend('<li>'+data.msg+'</li>');
                          });

        socket.on('status', function(data) {
                            ////alert('socket on status ='+ data.msg);
                            if (data.msg == 'End') {
                                $("#runTask").attr("disabled",false);
                            };
                          });
      });
    </script>
    <script>
      $("#runTask").click(function(e) {
         $("#runTask").attr("disabled",true);
         $("#Messages").empty();
         $.ajax({ type: "Post"
                , url: '/runPSATask'
                , data: $("#runTaskForm").serialize()
                , success: function(data) {
                    $("#Messages").empty();
                    $("#Messages").prepend('<li>The Task ' + data.taskid + ' has been submitted and will execute in ' + data.duration + ' seconds. </li>');
                  }
                });
         e.preventDefault();
         console.log('runPSATask complete');
      });
    </script>
  </body>
</html>

このテンプレヌトには次のものが含たれたす。

  • runTaskルヌトを䜿甚しおサヌバヌにタスクを送信するボタン/runSyncTask。
  • 結果はdividでに配眮されMessagesたす。

次に、app_sync.pyFlaskアプリケヌションを含むずいうプログラムを䜜成し、このプログラム内に2぀のルヌトを定矩したす。

  • "/"Webペヌゞをレンダリングしたすindex.html
  • "/runSyncTask"1〜20秒の乱数を生成し、反埩ごずに1秒間スリヌプするルヌプを実行する、長時間実行䞭のタスクをシミュレヌトしたす。
#app_sync.py
from flask import render_template, jsonify
from random import randint
from init import app
import tasks

#Render the predefined template index.html
@app.route("/",methods=['GET'])
def index():
    return render_template('index.html')

#Defining the route for running A Synchronous Task
@app.route("/runSyncTask",methods=['POST'])
def long_sync_task():
    print("Running","/runSyncTask")
    #Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
    n = randint(app.config['MIN_WAIT_TIME'],app.config['MAX_WAIT_TIME'])
    #Call the function long_sync_task included within tasks.py
    task = tasks.long_sync_task(n=n)
    #Return the random wait time generated
    return jsonify({ 'waittime': n })

if __name__ == "__main__":
    app.run(debug=True)

このチュヌトリアルで定矩されおいるすべおのタスクのコアロゞックは、プログラム内にありたすtasks.py。

#tasks.py
import time
from celery import Celery
from celery.utils.log import get_task_logger
from flask_socketio import SocketIO
import config

# Setup the logger (compatible with celery version 4)
logger = get_task_logger(__name__)

# Setup the celery client
celery = Celery(__name__)
# Load celery configurations from celeryconfig.py
celery.config_from_object("celeryconfig")

# Setup and connect the socket instance to Redis Server
socketio = SocketIO(message_queue=config.BROKER_URL)

###############################################################################
def long_sync_task(n):
    print(f"This task will take {n} seconds.")
    for i in range(n):
        print(f"i = {i}")
        time.sleep(1)
###############################################################################
@celery.task(name = 'tasks.long_async_task')
def long_async_task(n,session):
    print(f"The task of session {session}  will take {n} seconds.")
    for i in range(n):
        print(f"i = {i}")
        time.sleep(1)
###############################################################################
def send_message(event, namespace, room, message):
    print("Message = ", message)
    socketio.emit(event, {'msg': message}, namespace=namespace, room=room)

@celery.task(name = 'tasks.long_async_taskf')
def long_async_taskf(data):
    room      = data['sessionid']
    namespace = data['namespase']
    n         = data['waittime']

    #Send messages signaling the lifecycle of the task
    send_message('status', namespace, room, 'Begin')
    send_message('msg', namespace, room, 'Begin Task {}'.format(long_async_taskf.request.id))
    send_message('msg', namespace, room, 'This task will take {} seconds'.format(n))

    print(f"This task will take {n} seconds.")
    for i in range(n):
        msg = f"{i}"
        send_message('msg', namespace, room, msg )
        time.sleep(1)

    send_message('msg', namespace, room, 'End Task {}'.format(long_async_taskf.request.id))
    send_message('status', namespace, room, 'End')
###############################################################################
@celery.task(name = 'tasks.long_async_sch_task')
def long_async_sch_task(data):
    room      = data['sessionid']
    namespace = data['namespase']
    n         = data['waittime']

    send_message('status', namespace, room, 'Begin')
    send_message('msg'   , namespace, room, 'Begin Task {}'.format(long_async_sch_task.request.id))
    send_message('msg'   , namespace, room, 'This task will take {} seconds'.format(n))

    print(f"This task will take {n} seconds.")
    for i in range(n):
        msg = f"{i}"
        send_message('msg', namespace, room, msg )
        time.sleep(1)

    send_message('msg'   , namespace, room, 'End Task {}'.format(long_async_sch_task.request.id))
    send_message('status', namespace, room, 'End')
###############################################################################

このセクションでは、このlong_sync_task()関数を同期タスクずしおのみ䜿甚したす。

app_sync.pyプログラムを実行しお、同期シナリオをテストしおみたしょう。

$ python app_sync.py

http://localhost:5000Flaskむンスタンスが実行されおいるリンクにアクセスするず、次の出力が衚瀺されたす。

Flaskアプリのホヌムペヌゞ

「同期タスクの実行」ボタンを抌しお、プロセスが完了するたで埅ちたす。

同期タスク実行埌のホヌムペヌゞ

完了するず、トリガヌされたタスクに割り圓おられたランダムな時間を通知するメッセヌゞが衚瀺されたす。

同時に、サヌバヌがタスクを実行するず、コン゜ヌルに1秒ごずに増分された数倀が衚瀺されたす。

コン゜ヌルに衚瀺されるタスク

シナリオ2ポヌリングメカニズムを䜿甚した非同期サヌビス呌び出しの衚瀺

このセクションでは、クラむアントがポヌリングメカニズムを䜿甚しおサヌバヌ偎プロセスのフィヌドバックを芁求する可胜性のある非同期サヌビス呌び出しを瀺したす。

簡単に蚀うず、非同期ずは、プログラムが特定のプロセスが完了するのを埅たずに、関係なく続行するこずを意味したす。

発信者が開始サヌビスコヌルが、やるES N結果をオト埅ち時間。発信者は、結果を気にせずにすぐに䜜業を続行したす。発信者が結果に関心がある堎合は、埌で説明するメカニズムがありたす。

最も単玔な非同期メッセヌゞ亀換パタヌンはファむアアンドフォヌゲットず呌ばれ ã€ãƒ¡ãƒƒã‚»ãƒŒã‚žã¯é€ä¿¡ã•れたすがフィヌドバックは必芁ありたせんが、フィヌドバックが必芁な堎合、クラむアントはポヌリングメカニズムを介しお結果を繰り返し芁求するこずがありたす ã€‚

ポヌリングは朜圚的に高いネットワヌク負荷を匕き起こすため、お勧めしたせん。それでも、サヌビスプロバむダヌサヌバヌがクラむアントに぀いお知る必芁がないずいう利点がありたす。

次の図は、シナリオを瀺しおいたす。

ポヌリングメカニズムずの非同期通信非同期通信は、むベントに応答する必芁のあるコヌドたずえば、埅機を䌎う時間のかかるI / Oバりンド操䜜に適しおいたす。

非同期性を遞択するず、システムは同時により倚くの芁求を凊理できるようになり、スルヌプットが向䞊したす。

それでは、コヌディングに移りたしょう。構成ファむルを䜿甚しお、セロリの初期化パラメヌタヌを定矩したすceleryconfig.py。

#celeryconfig.py
#Celery Configuration parameters
#Map to Redis server
broker_url = 'redis://localhost:6379/0'

#Backend used to store the tasks results
result_backend = 'redis://localhost:6379/0'

#A string identifying the default serialization to use Default json
task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']

#When set to false the local system timezone is used.
enable_utc = False

#To track the started state of a task, we should explicitly enable it
task_track_started = True

#Configure Celery to use a specific time zone.
#The timezone value can be any time zone supported by the pytz library
#timezone = 'Asia/Beirut'
#enable_utc = True

Flaskがレンダリングできるテンプレヌトを䜜成したすindex1.html

<!DOCTYPE html>
<html>
  <head>
    <title>Synchronicity versus Asynchronicity</title>
    <link rel="stylesheet" href="{{url_for('static',filename='css/materialize.min.css')}}">
    <script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  </head>
  <body class="container">
    <div class="row">
        <h4>Click to start an ansycnhronous task</h4>
    </div>
    <div class="card-panel">
      <form method='post' id="runTaskForm" action="/runAsyncTask">
          <button style="height:50px;width:400px" type="submit" id="runTask">Run An Asynchronous Task</button>
      </form>
      <form method='post' id="getTaskResultForm" action="/getAsyncTaskResult">
          <button style="height:50px;width:400px" type="submit" id="getTaskResult">Get Asynchronous Task Result</button>
      </form>
    </div>
    <div class="row">
        <div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
    </div>
    <script>
      $("#runTask").click(function(e) {
         $("#runTask").attr("disabled",true);
         $("*").css("cursor","wait");
         $("#Messages").empty();
         $.ajax({ type: "Post"
                , url: '/runAsyncTask'
                , data: $("#runTaskForm").serialize()
                , success: function(data) {
                    $("#runTask").attr("disabled",false);
                    $("*").css("cursor","");
                    $("#Messages").append('The task ' + data.taskid + ' will be executed in asynchronous manner for ' + data.waittime + ' seconds...');
                  }
                });
         e.preventDefault();
         console.log('runAsyncTask complete');
      });
      $("#getTaskResult").click(function(e) {
         var msg = $("#Messages").text();
         var taskid = msg.match("task(.*)will");
         //Get The Task ID from The Messages div and create a Target URL
         var vurl = '/getAsyncTaskResult?taskid=' + jQuery.trim(taskid[1]);
         $.ajax({ type: "Post"
                , url: vurl
                , data: $("#getTaskResultForm").serialize()
                , success: function(data) {
                    $("*").css("cursor","");
                    $("#Messages").append('<p> The Status of the task = ' + data.taskid + ' is ' + data.taskstatus + '</p>');
                  }
                });
         e.preventDefault();
         console.log('getAsyncTaskResult complete');
      });
    </script>
  </body>
</html>

次に、app_async1.pyFlaskアプリを含むプログラムを䜜成したす。

#app_async1.py
from flask import render_template, jsonify, session,request
from random import randint
import uuid
import tasks
from init import app
from celery.result import AsyncResult

@app.route("/",methods=['GET'])
def index():
    # create a unique ID to assign for the asynchronous task
    if 'uid' not in session:
        sid = str(uuid.uuid4())
        session['uid'] = sid
        print("Session ID stored =", sid)
    return render_template('index1.html')

#Run an Asynchronous Task
@app.route("/runAsyncTask",methods=['POST'])
def long_async_task():
    print("Running", "/runAsyncTask")
    #Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
    n = randint(app.config['MIN_WAIT_TIME'],app.config['MAX_WAIT_TIME'])
    sid = str(session['uid'])
    task = tasks.long_async_task.delay(n=n,session=sid)
    #print('taskid',task.id,'sessionid',sid,'waittime',n )
    return jsonify({'taskid':task.id,'sessionid':sid,'waittime':n })

#Get The Result of The Asynchronous Task
@app.route('/getAsyncTaskResult', methods=['GET', 'POST'])
def result():
    task_id = request.args.get('taskid')
    # grab the AsyncResult
    result = AsyncResult(task_id)
    # print the task id
    print("Task ID = ", result.task_id)
    # print the Asynchronous result status
    print("Task Status = ", result.status)
    return jsonify({'taskid': result.task_id, 'taskstatus': result.status})

if __name__ == "__main__":
   app.run(debug=True)

このプログラムには、3぀の䞻芁なルヌトがありたす。

  • "/"Webペヌゞをレンダリングしたすindex1.html。
  • "/runAsyncTask"1〜20秒の乱数を生成する非同期タスクを呌び出しおから、反埩ごずに1秒間スリヌプするルヌプを実行したす。
  • "/getAsyncTaskResult"受信したタスクIDに基づいお、タスクの状態を収集したす。

泚意このシナリオには、SocketIOコンポヌネントは含たれおいたせん。 

このシナリオをテストしお、次の手順に埓っおパスを進めたしょう。

  • Redisサヌバヌを起動したす。Windowsでは、Redisがむンストヌルされおいるフォルダヌを芋぀けお、をダブルクリックしredis-server.exeたす。デフォルトのむンストヌルたたはLinux / MacOSの堎合は、RedisむンスタンスがTCPポヌト6379で実行されおいるこずを確認しおください。
  • Celeryワヌカヌを起動したす。Windowsでは、コマンドプロンプトを開き、プロゞェクトフォルダヌに移動しお、次のコマンドを実行したす。
$ async-venv\Scripts\celery.exe worker -A tasks --loglevel=DEBUG --concurrency=1 -P solo -f celery.logs

Linux / MacOSでは、非垞によく䌌おいたす。

$ async-venv/bin/celery worker -A tasks --loglevel=DEBUG --concurrency=1 -P solo -f celery.logs

これasync-venvが仮想環境の名前であるこずに泚意しおください。別の名前を付けた堎合は、必ず自分の名前に眮き換えおください。セロリが始たるず、次の出力が衚瀺されたす。セロリが定矩したタスク

プログラムで定矩されたタスクtasks.pyがCeleryに反映されおいるこずを確認しおください。

  • タヌミナルりィンドりを開き、メむンプログラムを起動したす。
$ python app_async1.py​

次に、ブラりザを開いお、次のリンクにアクセスしたす。 

ポヌリングメカニズムアプリずの非同期通信

[非同期タスクの実行]ボタンを抌すず、新しいタスクがキュヌに入れられ、盎接実行されたす。「メッセヌゞ」セクションに、タスクのIDずその実行時間を瀺すメッセヌゞが衚瀺されたす。

[非同期タスク結果の取埗]ボタンを継続的に抌すず、その特定の時間におけるタスクの状態が収集されたす。

セロリタスクの組み蟌み状態は次のずおりです。

  • PENDING実行を埅機しおいたす。
  • STARTEDタスクが開始されたした。
  • SUCCESSタスクは正垞に実行されたした。
  • FAILUREタスクの実行により䟋倖が発生したした。
  • RETRYタスクは再詊行されおいたす。
  • REVOKEDタスクが取り消されたした。

ログファむルに含たれおいるセロリワヌカヌのログcelery.logsを確認するず、タスクのラむフサむクルに気付くでしょう。

セロリログのタスクラむフサむクルシナリオ3自動フィヌドバックを䜿甚した非同期サヌビス呌び出しの衚瀺

以前のシナリオに基づいお、タスクの状態を収集するために耇数のリク゚ストを開始するこずによる煩わしさを軜枛するために、サヌバヌがタスクの状態に関しおクラむアントを継続的に曎新できるようにする゜ケットテクノロゞの組み蟌みを詊みたす。

実際、゜ケットIO゚ンゞンは、リアルタむムの双方向むベントベヌスの通信を可胜にしたす。

これがもたらす䞻な利点は、ネットワヌクの負荷を軜枛し、膚倧な数のクラむアントに情報を䌝達するためにより効率的になるこずです。

次の図は、シナリオを瀺しおいたす。

自動フィヌドバックによる非同期通信

さらに掘り䞋げる前に、実行する手順に぀いお簡単に説明したす。

セロリからWebブラりザヌにメッセヌゞを送り返すこずができるようにするために、以䞋を掻甚したす。

  • Flask-SocketIOのメッセヌゞキュヌ機胜。これにより、Celeryワヌカヌがクラむアントず通信できるようになりたす。
  • TのWebSocket接続を可胜にするための䜿いやすいJavaScriptラむブラリであるSocket.ioの圌の胜力。

デヌタ接続を効果的に管理するために、次の区分化戊略を採甚したす。

  • "/runAsyncTaskF"このシナリオに名前空間を割り圓おたす。名前空間は、単䞀の共有接続を介しおサヌバヌロゞックを分離するために䜿甚されたす。
  • W eは、各ナヌザヌセッションのための郚屋を割り圓おたす。郚屋は名前空間のサブディビゞョンたたはサブチャネルです。

それでは、コヌディングに移りたしょう。

  • Flaskがレンダリングできるテンプレヌトを䜜成したすindex2.html
<!DOCTYPE html>
<html>
<head>
    <title>Synchronicity versus Asynchronicity</title>
    <link rel="stylesheet" href="{{url_for('static',filename='css/materialize.min.css')}}">
    <script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
    <script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body class="container">
    <div class="row">
        <h5>Click to start an ansycnhronous task with automatic feedback.</h5>
    </div>
    <div class="card-panel">
        <form method='post' id="runTaskForm" action="/runAsyncTask">
            <button style="height:50px;width:400px" type="submit" id="runTask">Run An Asynchronous Task With Automatic Feedback</button>
        </form>
    </div>
    <div class="row">
        <div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
    </div>
    <script>
        $(document).ready(function() {
            var namespace = '/runAsyncTaskF';
            var url = 'http://' + document.domain + ':' + location.port + namespace;
            var socket = io.connect(url);
            socket.on('connect', function() {
                ////alert('socket on connect');
                socket.emit('join_room');
            });
            socket.on('msg', function(data) {
                ////alert('socket on msg ='+ data.msg);
                $("#Messages").prepend('<li>' + data.msg + '</li>');
            });
            socket.on('status', function(data) {
                ////alert('socket on status ='+ data.msg);
                if (data.msg == 'End') {
                    $("#runTask").attr("disabled", false);
                };
            });
        });
    </script>
    <script>
        $("#runTask").click(function(e) {
            $("#runTask").attr("disabled", true);
            $("*").css("cursor", "wait");
            $("#Messages").empty();

            $.ajax({
                type: "Post",
                url: '/runAsyncTaskF',
                data: $("#runTaskForm").serialize(),
                success: function(data) {
                    $("*").css("cursor", "");
                    $("#Messages").empty();
                    $("#Messages").prepend('<li>The Task ' + data.taskid + ' has been submitted. </li>');
                }
            });
            e.preventDefault();
            console.log('runAsyncTaskF complete');
        });
    </script>
</body>
</html>

app_async2.pyFlaskアプリケヌションを含むず呌ばれるプログラムを䜜成したす。

#Gevent is a coroutine based concurrency library for Python
from gevent import monkey
#For dynamic modifications of a class or module
monkey.patch_all()
from flask import render_template, jsonify, session, request
from random import randint
import uuid
import tasks
from init import app, socketio
from flask_socketio import join_room

@app.route("/",methods=['GET'])
def index():
    # create a unique session ID and store it within the Flask session
    if 'uid' not in session:
        sid = str(uuid.uuid4())
        session['uid'] = sid
        print("Session ID stored =", sid)
    return render_template('index2.html')

#Run an Asynchronous Task With Automatic Feedback
@app.route("/runAsyncTaskF",methods=['POST'])
def long_async_taskf():
    print("Running", "/runAsyncTaskF")
    # Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
    n = randint(app.config['MIN_WAIT_TIME'], app.config['MAX_WAIT_TIME'])

    data = {}
    data['sessionid'] = str(session['uid'])
    data['waittime']  = n
    data['namespase'] = '/runAsyncTaskF'

    task = tasks.long_async_taskf.delay(data)
    return jsonify({ 'taskid':task.id
                    ,'sessionid':data['sessionid']
                    ,'waittime':data['waittime']
                    ,'namespace':data['namespase']
                    })

@socketio.on('connect', namespace='/runAsyncTaskF')
def socket_connect():
    #Display message upon connecting to the namespace
    print('Client Connected To NameSpace /runAsyncTaskF - ',request.sid)

@socketio.on('disconnect', namespace='/runAsyncTaskF')
def socket_connect():
    # Display message upon disconnecting from the namespace
    print('Client disconnected From NameSpace /runAsyncTaskF - ',request.sid)

@socketio.on('join_room', namespace='/runAsyncTaskF')
def on_room():
    room = str(session['uid'])
    # Display message upon joining a room specific to the session previously stored.
    print(f"Socket joining room {room}")
    join_room(room)

@socketio.on_error_default
def error_handler(e):
    # Display message on error.
    print(f"socket error: {e}, {str(request.event)}")

if __name__ == "__main__":
    # Run the application with socketio integration.
    socketio.run(app,debug=True)

このプログラムには、䞻に2぀のルヌトがありたす。

  • "/"Webペヌゞをレンダリングしたすindex2.html。
  • "/runAsyncTaskF"以䞋を実行する非同期タスクを呌び出したす。
    • 1〜20秒の乱数を生成したす。
    • long_async_taskf() ãƒ—ログラム内のそれぞれのタスクを呌び出したすtasks.py。

このシナリオを実行するには

  • Redisサヌバヌを起動したす。
  • セロリワヌカヌを起動したす。
  • 走る app_async2.py

ブラりザを開き、次のリンクにアクセスしおボタンを抌すず、次のような出力が埐々に衚瀺されたす。

自動フィヌドバックフラスコアプリずの非同期通信同時に、コン゜ヌルに次の出力が衚瀺されたす。

コン゜ヌル出力たたcelery.logs、タスクのラむフサむクルに぀いおファむルをい぀でも確認できたす。

シナリオ4自動フィヌドバックを䜿甚しおポヌトでスケゞュヌルされた非同期サヌビスコヌルを衚瀺する

このシナリオはシナリオ3に䌌おいたす。唯䞀の違いは、非同期タスクを盎接実行する代わりに、このタスクがクラむアントによっお指定された特定の期間の埌に実行されるようにスケゞュヌルされるこずです。
 

コヌディングに進みたしょう。非同期タスクを実行する前に埅機する時間を秒単䜍で衚すindex3.html新しいフィヌルドを䜿甚しおテンプレヌトを䜜成し"Duration"たす。

<!DOCTYPE html>
<html>
<head>
    <title>Synchronicity versus Asynchronicity</title>
    <link rel="stylesheet" href="{{url_for('static',filename='css/materialize.min.css')}}">
    <script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
    <script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body class="container">
    <div class="row">
        <h5>Click to start a post scheduled ansycnhronous task with automatic feedback.</h5>
    </div>
    <div class="card-panel">
        <form method='post' id="runTaskForm" action="/runPSATask">
            <div>
                <input id="duration" name="duration" placeholder="Enter duration in seconds. for example: 30" type="text">
                <label for="duration">Duration</label>
            </div>
            <button style="height:50px;width:600px" type="submit" id="runTask">Run A Post Scheduled Asynchronous Task With Automatic Feedback</button>
        </form>
    </div>
    <div class="row">
        <div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
    </div>
    <script>
        $(document).ready(function() {
            var namespace = '/runPSATask';
            var url = 'http://' + document.domain + ':' + location.port + namespace;
            var socket = io.connect(url);
            socket.on('connect', function() {
                socket.emit('join_room');
            });
            socket.on('msg', function(data) {
                $("#Messages").prepend('<li>' + data.msg + '</li>');
            });
            socket.on('status', function(data) {
                ////alert('socket on status ='+ data.msg);
                if (data.msg == 'End') {
                    $("#runTask").attr("disabled", false);
                };
            });
        });
    </script>
    <script>
        $("#runTask").click(function(e) {
            $("#runTask").attr("disabled", true);
            $("#Messages").empty();
            $.ajax({
                type: "Post",
                url: '/runPSATask',
                data: $("#runTaskForm").serialize(),
                success: function(data) {
                    $("#Messages").empty();
                    $("#Messages").prepend('<li>The Task ' + data.taskid + ' has been submitted and will execute in ' + data.duration + ' seconds. </li>');
                }
            });
            e.preventDefault();
            console.log('runPSATask complete');
        });
    </script>
</body>
</html>

次に、app_async3.pyこのシナリオのFlaskアプリは次のずおりです。

#app_async3.py
from gevent import monkey
monkey.patch_all()

from flask import render_template, jsonify, session, request
from random import randint
import uuid
import tasks
from init import app, socketio
from flask_socketio import join_room

@app.route("/",methods=['GET'])
def index():
    # create a unique session ID
    if 'uid' not in session:
        sid = str(uuid.uuid4())
        session['uid'] = sid
        print("Session ID stored =", sid)
    return render_template('index3.html')

#Run a Post Scheduled Asynchronous Task With Automatic Feedback
@app.route("/runPSATask",methods=['POST'])
def long_async_sch_task():
        print("Running", "/runPSATask")
        # Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
        n = randint(app.config['MIN_WAIT_TIME'], app.config['MAX_WAIT_TIME'])
        data = {}
        data['sessionid'] = str(session['uid'])
        data['waittime']  = n
        data['namespase'] = '/runPSATask'
        data['duration']  = int(request.form['duration'])
        #Countdown represents the duration to wait in seconds before running the task
        task = tasks.long_async_sch_task.apply_async(args=[data],countdown=data['duration'])
        return jsonify({ 'taskid':task.id
                        ,'sessionid':data['sessionid']
                        ,'waittime': data['waittime']
                        ,'namespace':data['namespase']
                        ,'duration':data['duration']
                        })


@socketio.on('connect', namespace='/runPSATask')
def socket_connect():
    print('Client Connected To NameSpace /runPSATask - ',request.sid)

@socketio.on('disconnect', namespace='/runPSATask')
def socket_connect():
    print('Client disconnected From NameSpace /runPSATask - ',request.sid)

@socketio.on('join_room', namespace='/runPSATask')
def on_room():
    room = str(session['uid'])
    print(f"Socket joining room {room}")
    join_room(room)

@socketio.on_error_default
def error_handler(e):
    print(f"socket error: {e}, {str(request.event)}")

if __name__ == "__main__":
    socketio.run(app,debug=True)

今回long_async_sch_task()からタスクメ゜ッドを䜿甚しおいるこずに泚意しおくださいtasks.py。

app_async3.py以前ず同じように実行し、ブラりザを開きたす。

シナリオ4アプリ期間぀たり10を入力し、ボタンを抌しお、スケゞュヌル埌の非同期タスクを䜜成したす。䜜成されるず、タスクの詳现を瀺すメッセヌゞが[メッセヌゞ]ボックスに衚瀺されたす。

期間フィヌルドで指定した時間埅぀必芁がありたす。タスクが実行されおいるこずがわかりたす。

実行䞭のスケゞュヌルされたタスクを投皿するたた、celery.logsログファむルに含たれおいるセロリワヌカヌのログを埩元するず、タスクのラむフサむクルに気付くでしょう。

シナリオ4のログファむル付録セロリの監芖に花を䜿甚する

セロリタスクをより適切に監芖するために、セロリ ã‚¯ãƒ©ã‚¹ã‚¿ãƒŒã‚’監芖および管理するためのWebベヌスのツヌルであるFlowerをむンストヌルできたす。

泚意フラワヌラむブラリはの䞀郚でしたrequirements.txt。 

花を䜿甚しおセロリのタスクを衚瀺するには、次の手順に埓っおください。

  • 以前ず同じようにRedisサヌバヌを起動したす。
  • 以前ず同じようにCeleryワヌカヌを起動したす。
  • Windowsでは、コマンドを䜿甚しおflowerを開始したす。
$ async-venv\Scripts\flower.exe worker -A tasks --port=5555

Linux / MacOSの堎合

$ async-venv/bin/flower worker -A tasks --port=5555

コン゜ヌルに次の情報が衚瀺されたす。

花が始たりたしたアプリに戻っおタスクを実行し、ブラりザを開いおhttp://localhost:5555[タスク]タブに移動したす。

花のホヌムペヌゞ

タスクが完了するず、フラワヌダッシュボヌドに次のように衚瀺されたす。

フラワヌダッシュボヌドのセロリで達成されたタスク結論

この蚘事が、Celeryの助けを借りお同期および非同期リク゚ストの抂念的な基瀎を埗るのに圹立぀こずを願っおいたす。同期芁求は遅くなる可胜性があり、非同期芁求は迅速に実行されたすが、あらゆるシナリオに適切な方法を認識するこずが重芁です。時には、圌らは䞀緒に働くこずさえありたす。

リンク: https://www.thepythoncode.com/article/async-tasks-with-celery-redis-and-flask-in-python

#python 
 

 

Lilyan  Streich

Lilyan Streich

1599119110

Next js Tutorial For Beginners

Next js Tutorial For Beginners is the today’s topic. It is no secret that creating single-page applications can be immensely challenging these days. But with the help of some libraries, frameworks, and tools, it is effortless nowadays. React.js is the common frontend libraries among the Front-end developers. Its virtual dom theory makes React faster and gives us the better application performance. Now, one problem is that Single Page Applications are not at all SEO  friendly because it is rendered on the Client side  and not Server side . So when the Search Engine crawlers try to send a request, they cannot get our meta content or description and not even the main content. Search Engines do not care about how your app is architected or whatever ideology was used to adjust and fetch the right material. Their bots are not as smart as using your apps as a real user would. All they care about is that once they send their spiders to crawl and index your site, whatever the server provides on the first request is what gets indexed. In our case, all they get is our div tag with an id and bundled JS file, and we can not index our website correctly. So some how, we need a SSR to tackle this problem and in React js, Next.js is the perfect solution.

#js #react.js #next.js