DenoEnvironment developmentOak-orm-mysql-reactjsThe whole stack application

  • DenoEnvironment developmentOak-orm-mysql-reactjsThe whole stack application
    • What is Deno?
      • (I) Modern features based on JavaScript language
      • (2) Install Deno
      • Command line
      • Command line options
      • (5) Deno code examples
      • (6) Standard library
    • A simple example of full stack application
      • (I) The basic contents of the project are as follows
      • (2) Establish project folders
      • (3) Centralized management dependency
      • Create env.ts
      • Create server.ts file
      • (6) Start the application program
      • (7) Create a router
      • (8) Add controller, API call function:
      • (9) Implement the method of adding, deleting, modifying and checking
        • 1. Query data method:
        • 2. New data method:
        • 3. Update and delete methods:
      • Mysql and ORM support
        • 1. The Model is a Model
        • 2. Connect to the database
        • 3. Configure the database connection
      • (11) Introduction of password authentication
    • Third, client trial
      • Reactjs support
      • (2) Set App components
      • (3) Route setting
        • Use the helper for the actual rendering
      • (4) Pages and components
        • 1. The App components
        • 2. LoginComponent components
    • 4. Debug Deno
      • Chrome Devtools
      • (2) VSCode
    • The source address
    • More Resources

What is Deno?

Deno is a JavaScript and TypeScript runtime based on the V8 JavaScript engine and the Rust programming language that uses a secure environment to execute code by default.

It was created by Ryan Dahl, the original creator of Node.js, with a focus on security and productivity.

Deno is built on V8, Rust, and Tokio.

Two pronunciations, “Deno” and “Tino,” which would be more appropriate because the Deno logo is a dinosaur. Dinosaur stands for dino.

Deno is like Node, but has been greatly improved in many ways.

At the time of this writing, deno has just released its favorite version of 1.1.0, with numerous bug fixes and improved performance.

Start with the Deno feature list:

(I) Modern features based on JavaScript language

  • A comprehensive standard library
  • TypeScript support is included
  • Includes ES module
  • There is no package manager
  • Have first-class await
  • Built-in testing
  • Remain browser compatible, with fetch and global Window objects built in

(2) Install Deno

I’m using Windows 10 on the WSL Debian Buster system. You can install it like this

Open the CMD command line and type WSL or bash

Then type:

curl -fsSL https://deno.land/x/install/install.sh | sh
Copy the code

Tips:

PowerShell (Windows):

iwr https://deno.land/x/install/install.ps1 -useb | iex
Copy the code

Homebrew (Mac):

brew install deno
Copy the code

Chocolatey (Windows):

choco install deno
Copy the code

Command line

Set the environment variables in ~/.bashrc

export DENO_INSTALL="$HOME/.deno"
export PATH="$DENO_INSTALL/bin:$PATH"
Copy the code

Once that’s done, you’ll have access to the deno command. Use deno –help to get help:

Deno -- help deno 1.1.0 A secure JavaScript and TypeScript runtime Docs: https://deno.land/std/manual.md Modules: https://deno.land/std/ https://deno.land/x/ Bugs: https://github.com/denoland/deno/issues To start the REPL, supply no arguments: deno To execute a script: deno run https://deno.land/std/examples/welcome.ts deno https://deno.land/std/examples/welcome.ts To evaluate codein the shell:
  deno eval "console.log(30933 + 404)"

Run 'deno help run' for 'run'-specific flags.

USAGE:
    deno [OPTIONS] [SUBCOMMAND]

OPTIONS:
    -h, --help
            Prints help information

    -L, --log-level <log-level>
            Set log level [possible values: debug, info]

    -q, --quiet
            Suppress diagnostic output
            By default, subcommands print human-readable diagnostic messages to stderr.
            If the flag is set, restrict these messages to errors.
    -V, --version
            Prints version information


SUBCOMMANDS:
    bundle         Bundle module and dependencies into single file
    cache          Cache the dependencies
    completions    Generate shell completions
    doc            Show documentation for a module
    eval           Eval script
    fmt            Format source files
    help           Prints this message or the help of the given subcommand(s)
    info           Show info about cache or info related to source file
    install        Install script as an executable
    repl           Read Eval Print Loop
    run            Run a program given a filename or url to the module
    test           Run tests
    types          Print runtime TypeScript declarations
    upgrade        Upgrade deno executable to newest version

ENVIRONMENT VARIABLES:
    DENO_DIR             Set deno's base directory (defaults to $HOME/.deno) DENO_INSTALL_ROOT Set deno install's output directory
                         (defaults to $HOME/.deno/bin)
    NO_COLOR             Set to disable color
    HTTP_PROXY           Proxy address for HTTP requests
                         (module downloads, fetch)
    HTTPS_PROXY          Same but for HTTPS
Copy the code

Command line options

--allow-env Allows environment access --allow-hrtime allows high precision time measurement --allow-net = allows network access --allow-plugin allows plug-in loading --allow-read = Allows file system read access --allow-run Allows child processes to run --allow-write = Allows file system write access --allow-all Allows all permissions (same as -a)Copy the code

(5) Deno code examples

Deno. land/ STD /example…

(6) Standard library

  • Archive: tar archive tool
  • async async utilties
  • Async: async tool
  • Bytes: Assist for manipulating byte fragments
  • Datetime: date/time resolution
  • Encoding: Encoding/decoding of various formats
  • Flags: parses command line flags
  • FMT: Format and print
  • Fs: file system API
  • Hash: encrypted library
  • HTTP: indicates the HTTP server
  • IO: I/O library
  • Log: log utility
  • Mime: Supports multipart data
  • Node: node.js compatibility layer
  • Path: indicates the path operation
  • Ws: web sockets

A simple example of full stack application

Build REST apis with Oak. Oak was inspired by the popular Node.js middleware Koa.

The API built is very simple. Our server will store a list of data in memory with a name and password email.

  • Add new data
  • List the data
  • Gets the details of a particular data
  • Removes a data item from the list
  • Update the age of the data

For the convenience of building a common and flexible application template in the future, we will do some Settings in advance

(I) The basic contents of the project are as follows

.
|-- config
|   |-- db.ts
|   |-- env.ts
|   `-- talent-deno-test.json
|-- controllers
|   |-- auth.ts
|   |-- games.mysql.ts
|   `-- user.ts
|-- deno.json
|-- deps.ts
|-- docs
|   |-- index.md
|   `-- tutorial.md
|-- drun.json
|-- helpers
|   |-- between.ts
|   |-- render.ts
|   `-- request.ts
|-- middleware
|   |-- authorize.ts
|   |-- error.ts
|   |-- logger.ts
|   |-- state.ts
|   `-- timer.ts
|-- models
|   |-- GameModel.ts
|   `-- UserModel.ts
|-- pages
|   |-- app.tsx
|   |-- form.tsx
|   `-- list.tsx
|-- readme.md
|-- routes
|   |-- auth.tsx
|   |-- game.ts
|   `-- home.tsx
|-- server.ts
|-- services
|   |-- crud.sql.ts
|   `-- db.sql.ts
|-- testdeps.ts
|-- tests
|   |-- model_test.ts
|   `-- oak.test.ts
|-- tsconfig.json
`-- types.ts
Copy the code

(2) Establish project folders

mkdir deno-project
cd deno-project
touch deps.ts
deno cache deps.ts
Copy the code

(3) Centralized management dependency

Our deps. Ts file, similar to package.json under Node, allows us to do some dependency management

// server
export {
  Application,
  Router,
  Status,
  isHttpError,
} from "https://deno.land/x/oak/mod.ts";
export { oakCors } from "https://deno.land/x/cors/mod.ts";
// React
export { default as React } from "https://dev.jspm.io/[email protected]";
export { default as ReactDOMServer } from "https://dev.jspm.io/[email protected]/server";
export { default as ReactRouter } from "https://dev.jspm.io/react-router";
export { default as ReactHookForm } from "https://dev.jspm.io/react-hook-form";
// ORM
export {
  dso,
  Client,
  Where,
  Model,
  BaseModel,
  FieldType,
  Field,
} from "https://deno.land/x/[email protected]/mod.ts";
// services
export {
  cron,
  daily,
  monthly,
  weekly,
} from "https://deno.land/x/deno_cron/cron.ts";
// Helpers
export { config } from "https://deno.land/x/[email protected]/mod.ts";
export { slugify } from "https://deno.land/x/[email protected]/mod.ts";
export { makeJwt } from "https://deno.land/x/[email protected]/create.ts";
export { validateJwt } from "https://deno.land/x/[email protected]/validate.ts";
export { hash, compare } from "https://deno.land/x/[email protected]/mod.ts";
export { v4 } from "https://deno.land/std/uuid/mod.ts";
export { program, Lizard } from "https://deno.land/x/denomander/mod.ts";
export { readJsonSync, writeJsonSync } from "https://deno.land/std/fs/mod.ts";
Copy the code

Tip: If you want to cache various dependencies ahead of time

deno cache --unstable deps.ts
Copy the code

Create env.ts

mkdir config
touch config/env.ts
touch .env
Copy the code
  • Environment profileconfig/env.ts
import { config } from ".. /deps.ts";
const env = config();
export default env;
Copy the code
  • Environment configuration data is written in.envIn the file
HOST=http://0.0.0.0
PORT=8000
TOKEN_SECRET=20090909
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=monitor
DB_USERNAME=monitor
DB_PASSWORD=20090909
Copy the code

You can then import environment variables using the following methods

import env from './config/env.ts';
env['HOST']
Copy the code

Create server.ts file

Create a server.ts file in the project root directory

Import Application and Router objects from Oak:

Then we get the environment variables PORT and HOST:

Run and listen

import { Application, oakCors } from "./deps.ts";
import env from "./config/env.ts";

// Routes
import homeRouter from "./routes/home.tsx";
import authRouter from "./routes/auth.tsx";

const host = env["HOST"];
const port = parseInt(env["PORT"]);

// Configure Application
export const app = new Application();

// Builtin middleware
app.use(oakCors()); // Enable CORS for All Routes
app.use(homeRouter.routes())
  .use(authRouter.routes());
app.use(homeRouter.allowedMethods())
  .use(authRouter.allowedMethods());

// Bootstrap Application
console.log(`Server running on ${host}:${port}`);
await app.listen({ port });

Copy the code

Create a tsconfig.json file in the project root directory

Because our project will use ReactJS and decorators, enable both experimentalDecorators and JSX

{
  "compilerOptions": {
    "jsx": "react"."allowJs": false."emitDecoratorMetadata": true."experimentalDecorators": true."module": "esnext",}}Copy the code

(6) Start the application program

deno run -A --unstable -c tsconfig.json server.ts
Copy the code

Deno will then download the dependencies and run our program automatically

(7) Create a router

User authenticated route routes/auth.ts

import { Router } from '.. /deps.ts';
import { register, login } from ".. /controllers/auth.ts";
import { getUsers, getUser, addUser, updateUser, deleteUser } from ".. /controllers/user.ts";

const router = new Router();

router.post("/auth/register", register)
  .post("/auth/login", login);

router.get("/api/v1/users", getUsers)
  .get("/api/v1/users", getUser)
  .post("/api/v1/users", register)
  .put("/api/v1/users", updateUser)
  .delete("/api/v1/users", deleteUser);

export default router;
Copy the code

Our API interface is the background server Settings

The /auth/register interface implements the registration function. The auth/login interface implements the login function

(8) Add controller, API call function:

User validated controllers/auth.ts

The register method is called when a user sends a POST request to /auth/register

According to Oak’s design, the controller’s corresponding method is an asynchronous function whose parameter CTX is of the following type

{
  request: any;
  response: any;
  params: any;
}
Copy the code

Note that we use const body = await request.body() to get the content of the network request body, and the value is passed as JSON.

Data processing results are directly used to modify response objects, such as status, data, type

import { Status, compare, makeJwt, hash, config } from ".. /deps.ts";
import env from ".. /config/env.ts";
import { findRecord, addRecord } from ".. /services/crud.sql.ts";
import { userModel } from ".. /services/db.sql.ts";

export async function register(ctx: any) {
  const body = await ctx.request.body();
  console.log(body);

  // FIXEM: password is hashed and needs long string, over 100
  let checkId = await findRecord(userModel, { name: body.value.name });

  if(! checkId) {const password = await hash(body.value.password);
    const user = await addRecord(userModel, {
      name: body.value.name,
      email: body.value.email,
      password,
    });

    ctx.response.status = Status.Created;
    ctx.response.type = "json";
    ctx.response.body = {
      status: "success",
      message: `user registered in database`,
      data: {
        user,
      },
    };
  } else {
    ctx.throw(Status.BadRequest, "User name exits!"); }}export async function login(ctx: any) {
  const body = await ctx.request.body();

  // Find record with name
  let user: any = await findRecord(userModel, { name: body.value.name });

  if(! user) { ctx.throw(Status.UnprocessableEntity,"Wrong Email Address!");
  } else if (await compare(body.value.password, user.password)) {
    const token = makeJwt(
      {
        header: { alg: "HS256", typ: "JWT" },
        payload: { id: user.id, name: user.name, email: user.email },
        key: env["TOKEN_SECRET"],},); ctx.response.status = Status.OK; ctx.response.type ="json";
    ctx.response.body = {
      status: "success",
      message: `Logged in with ${body.value.email}`,
      data: { accessToken: token },
    };
    // set redirect
  } else {
    ctx.throw(Status.Unauthorized, "Wrong Password!"); }}Copy the code

For maintenance purposes, we abstract the background operations of the database into services, such as calling addRecord from the Register method, which is defined in a separate crud.sql.ts

(9) Implement the method of adding, deleting, modifying and checking

Defined in the services/crud.sql.ts file

1. Query data method:

import {
  Where,
  BaseModel,
} from ".. /deps.ts";

// Grub Options
export async function findAllRecord(model: BaseModel) {
  const records = await model.findAll(Where.expr("id > 0"));
  console.log("Found user by id:", records);
  return records;
}

export async function findRecord(model: BaseModel, query: any) {
  let record;
  if (query.id) {
    record = await model.findById(query.id);
  } else {
    record = await model.findOne(Where.from(query));
  }
  console.log("Found user by id:", record);
  return record;
}
Copy the code

2. New data method:

Adding data is very simple. Here we take ORM Model objects as parameters and call Model methods to perform database operations.

import {
  Where,
  BaseModel,
} from ".. /deps.ts";

export async function addRecord(model: BaseModel, data: any) {
  const id = await model.insert(data);
  console.log("New user with id:", id);
  return id;
}
Copy the code

3. Update and delete methods:

Delete and query, primarily using the Query parameter, is a JSON object that must contain the ID field as a unique identifier

export async function updateRecord(model: BaseModel, query: any) {
  const records = await model.update(query, Where.from({ id: query.id }));
  console.log("Update user with id:", records);
  return records;
}

export async function deleteRecord(model: BaseModel, query: any) {
  const count = await model.delete(Where.from(query));
  console.log(`${count} record deleted`);
  return count;
}
Copy the code

Mysql and ORM support

We use the DSO module to provide ORM support, although you can use other similar modules such as denodb or Cotton

But because deno is still in continuous development, there are many database pits, said to be classes.

For example, when defining a Model, if the password needs to be hashed, this field is very long. Set enough field length in the Model, otherwise the server will report an error, and cannot check at all!

For example, when using denodb, there are often inexplicable errors such as compile Error or file Not found. Hopefully, these problems will be eliminated as the version improves.

1. The Model is a Model

Our user model contains three fields: name, password and email

import {
  BaseModel,
  Field,
  FieldType,
  Model,
} from ".. /deps.ts";

// Define a database model
@Model("users")
export class UserModel extends BaseModel {
  @Field({
    type: FieldType.INT,
    primary: true,
    length: 11,
    autoIncrement: true, }) id! :number;

  @Field({ type: FieldType.STRING, length: 30, notNull: true}) name! :string;

  // FIXEM: password is hashed and needs long string, over 100
  @Field({ type: FieldType.STRING, length: 100, notNull: true}) password! :string;

  @Field({ type: FieldType.STRING, length: 30}) email? :string;
}
Copy the code

2. Connect to the database

Dso is an easy-to-use ORM project that takes only three steps to initialize

  1. Use the dso.define data table structure, whose parameters are Model

  2. Start dso.connect for database connection

  3. Use dso.sync for synchronization, and if there is no corresponding table, it will be created automatically. Note that passing true will reset the database, deleting all existing data

The contents of the services/db.sql.ts file are as follows:

import {
  dso,
  Client,
} from ".. /deps.ts";
import { mysqlOption } from ".. /config/db.ts";

import { UserModel } from ".. /models/UserModel.ts";
import { GameModel } from ".. /models/GameModel.ts";

export const userModel = dso.define(UserModel);
export const gameModel = dso.define(GameModel);

export const client: Client = await dso.connect(mysqlOption);

export async function initDb() {
  await dso.sync(false);
}
Copy the code

3. Configure the database connection

config/db.ts

import env from "./env.ts";
export const mysqlOption = {
  hostname: env["DB_HOST"],
  port: parseInt(env["DB_PORT"]),
  username: env["DB_USERNAME"],
  password: env["DB_PASSWORD"],
  db: env["DB_DATABASE"]}Copy the code

(11) Introduction of password authentication

As you can see, we use the compare method in the login method in controllers/auth.ts to verify the plaintext password in the network request against the hash password obtained by the database and use makeJwt to generate the new token

Controllers /auth.ts

export async function login(ctx: any) {
  const body = await ctx.request.body();

  // Find record with name
  let user: any = await findRecord(userModel, { name: body.value.name });

  if(! user) { ctx.throw(Status.UnprocessableEntity,"Wrong Email Address!");
  } else if (await compare(body.value.password, user.password)) {
    const token = makeJwt(
      {
        header: { alg: "HS256", typ: "JWT" },
        payload: { id: user.id, name: user.name, email: user.email },
        key: env["TOKEN_SECRET"],},); ctx.response.status = Status.OK; ctx.response.type ="json";
    ctx.response.body = {
      status: "success",
      message: `Logged in with ${body.value.email}`,
      data: { accessToken: token },
    };
    // set redirect
  } else {
    ctx.throw(Status.Unauthorized, "Wrong Password!"); }}Copy the code

Middelwares/Authorize defines a middleware, using validateJwt to implement JWT validation

import { Status, validateJwt, config } from ".. /deps.ts";

const env = config();

export default async (ctx: any, next: any) = > {// FIXED: Check authorization
    const authHeader = ctx.request.headers.get("authorization");
    if(! authHeader) { ctx.throw(Status.Unauthorized); }else {
        const requestToken = authHeader.split("") [1];
        try {
            const jwt: any = await validateJwt(requestToken, env["TOKEN_SECRET"]);
            ctx.request.user = jwt.payload
            await next();
        } catch(err) { ctx.throw(Status.Unauthorized); }}};Copy the code

I used the Chrome Talent Rest plug-in for Talent to test

Third, client trial

Reactjs support

Adding ReactJS support is a bit more difficult, mainly the coordination between the server and the client, the specific principle is not well understood by the author, please refer to the following articles and videos

define 2 routes: one to serve a simple HTML page containing our rendered app, and another browser.js route to server our app’s code so we can [hydrate] the React application on the client.

Simply put, two routes need to be defined

The first function is to render the HTML page, including the app itself

The second is to provide the browser.js script, which provides dynamic functionality for our app

(2) Set App components

The main page needs to be rendered with Oak routing

Here we need to use the React -doom-server renderString. For better implementation, I use the htmlWrapper helper function to return the component content we need to be able to use on the server.

(3) Route setting

The following information is displayed in the routes/home.ts command:

/ / @ deno - types = "https://deno.land/x/types/react/v16.13.1/react.d.ts"
/ / @ deno - types = "https://deno.land/x/types/react-dom/v16.13.1/server.d.ts"
import { React, Router, ReactDOMServer, ReactRouter } from '.. /deps.ts';
import App, { AppWithRouter } from '.. /pages/app.tsx';
import LoginComponent from '.. /pages/form.tsx';
import ListComponent from '.. /pages/list.tsx';
import { htmlWrapper, jsMultiWrapper } from '.. /helpers/render.ts';
const { StaticRouter, BrowserRouter } = ReactRouter;

// Import client reactjs
const browserBundlePath = "/browser.js";

const router = new Router();

router.get("/".(ctx: any) = > {
  // And then on the server we’ll use the analogous, but stateless StaticRouter component:
  const body = htmlWrapper(App);
  ctx.response.type = 'text/html';
  ctx.response.body = body;
});

// Client render App
router.get(browserBundlePath, ({ response }: { response: any}) = > {
  // On the client-side, let’s simply wrap our App component with React Router’s BrowserRouter component:
  const js = jsMultiWrapper([
    {
      name: 'App',
      component: App
    },
    {
      name: 'LoginComponent',
      component: LoginComponent
    },
    {
      name: 'ListComponent',
      component: ListComponent
    }
  ]);
  response.type = 'application/javascript';
  response.body = js;
});

export default router;

Copy the code

Use the helper for the actual rendering

The helper function is realized as follows:

  1. Introduce a series of libraries from under dev.jspm.io

  2. Import a series of components (although direct imports will report an error because the related components are not defined in the script file. Importing each component one by one is not practical, so you need to wrap it up and define each component as a constant by name.)

  3. Browser.js is rendered with reactdom.hydrate (react.createElement (App) to support building dynamic functionality

  4. Render the component using (ReactDOMServer as any).renderToString(Component)

Helpers/render. Ts as follows:

/ / @ deno - types = "https://deno.land/x/types/react-dom/v16.13.1/server.d.ts"
import {  ReactDOMServer, ReactRouter } from '.. /deps.ts';
const { StaticRouter, BrowserRouter } = ReactRouter;

const jspms = [
  'the import the React from "https://dev.jspm.io/[email protected]" ".'the import ReactDOM from "https://dev.jspm.io/[email protected]" ".'import ReactRouter from "https://dev.jspm.io/react-router"'.'import ReactHookForm from "https://dev.jspm.io/react-hook-form"'
]

const libs = jspms.join('; \n');

// Import client reactjs
export const htmlWrapper = (component: any) = > {
  const browserBundlePath = "/browser.js";
  const html =
  `<html><head><script type="module" src="${browserBundlePath}"> < / script > < link href =" https://unpkg.com/tailwindcss@ ^ 1.0 / dist/tailwind. Min. CSS "rel =" stylesheet "> < / head > < body > < div id="app">${ (ReactDOMServer as any).renderToString(component) }</div></body></html>`;
  const body = new TextEncoder().encode(html);
  return body
};

export const jsMultiWrapper = (components: { name: string; component: any} []) = > {
  // On the client-side, let’s simply wrap our App component with React Router’s BrowserRouter component:
  const componentLines: string = components.reduce((pre: any, cur: any) = > {
    const line = `const ${cur.name} = ${cur.component}; `
    return pre + '\n' + line;
  }, ' ')
  const js =
  `${libs}; \n${componentLines}; \nReactDOM.hydrate(React.createElement(App), document.getElementById("app")); `;
  return js;
};
Copy the code

(4) Pages and components

1. The App components

The main page pages/app.tsx contains the following contents:

/ / @ deno - types = "https://deno.land/x/types/react/v16.13.1/react.d.ts" import {React} from ".. /deps.ts"; import ListComponent from "./list.tsx"; import LoginComponent from "./form.tsx"; declare global { namespace JSX { interface IntrinsicElements { button: any; div: any; h1: any; p: any; a: any; ul: any; li: any; span: any; form: any; label: any; input: any; } } } const App = (props: any) => { return ( <div class="flex"> <div class="w-2/5"> <LoginComponent /> </div> <div class="w-3/5"> <ListComponent /> </div> </div> ); }; export default App;Copy the code

2. LoginComponent components

This component implements the functionality of the form

The pages/form.tsx page is as follows:

Here we try to add some new things, such as tailwindCSS and react-hook-form, to see if they can be combined

/ / @ deno - types = "https://deno.land/x/types/react/v16.13.1/react.d.ts" import {React, ReactHookForm} from ".. /deps.ts"; import env from ".. /config/env.ts"; const LoginComponent = () => { const { register, handleSubmit } = (ReactHookForm as any).useForm(); const onSubmit = async (data: any) => { console.log(data); // Try login const response: any = await fetch("/auth/login", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(data), }); console.log(response); // if user exists try login if (response.status ! == 200) { const response: any = await fetch("/auth/register", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(data), }); }}; return ( <div class="w-full pl-10 pt-50"> <form onSubmit={handleSubmit(onSubmit)} class="bg-white shadow-md rounded px-8  pt-6 pb-8 mb-4" > <div class="mb-4"> <label class="block text-gray-700 text-sm font-bold mb-2" for="name" > Username </label> <input name="name" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" type="text" ref={register} /> </div> <div class="mb-4"> <label class="block text-gray-700 text-sm font-bold mb-2" for="email" > Email </label> <input name="email" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" type="text" ref={register} /> </div> <div class="mb-6"> <label class="block text-gray-700 text-sm font-bold mb-2" for="password" > Password </label> <input class="shadow appearance-none border border-red-500 rounded w-full py-2 px-3 text-gray-700 mb-3  leading-tight focus:outline-none focus:shadow-outline" name="password" type="password" ref={register} /> <p class="text-red-500 text-xs italic">Please choose a password.</p> </div> <div class="flex items-center justify-between">  <input class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="submit" /> <a class="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" href="#" > Forgot Password? </a> </div> </form> <p class="text-center text-gray-500 text-xs"> &copy; 2020 Acme Corp. All rights reserved. </p> </div> ); }; export default LoginComponent;Copy the code

Access the root directory and display our React App. Add and remove functions work fine! That’s great!

Open the inspector in your browser, open browser.js in the Source panel, and the final code looks like this

4. Debug Deno

Deno supports V8 Inspector Protocol. You can debug Deno programs using Chrome Devtools or another client that supports the protocol, such as VSCode. To enable debugging, run Deno with the –inspect or –inspect-brk options, which are described below:

–inspect=HOST:PORT activate inspector on host:port (default: 127.0.0.1:9229)

–inspect-brk=HOST:PORT activate inspector on host:port and break at start of user script

The copy code –inspect option allows the debugger to connect at any point in time, while the –inspect-brk option waits for the debugger to connect and pauses execution at the first line of code.

Chrome Devtools

Let’s use Chrome Developer tools to debug

Pause execution at the first line of code using the –inspect-brk option.

deno run --inspect-brk -A server.ts
Copy the code

Copy the code to open Chrome ://inspect, click inspect next to Target

For more detailed debugging instructions, visit the deno.land/manual address.

(2) VSCode

Deno can be debugged in VSCode. Official support for plug-ins is being developed

The configuration in launch.json is as follows

{
  "version": "0.2.0"."configurations": [{"name": "Deno"."type": "node"."request": "launch"."cwd": "${workspaceFolder}"."runtimeExecutable": "deno"."runtimeArgs": ["run"."--inspect-brk"."-A"."<entry_point>"]."port": 9229}}]Copy the code

The source address

Github address: github.com/linuxing3/d…

More Resources

  • Nguyen other web logs of www.ruanyifeng.com/blog/2020/0…

  • Deno’s official website, deno.land

  • API documentation doc.deno.land and deno.land/typedoc

  • Awesome – deno github.com/denolib/awe…

  • The kosi www.axihe.com/edu/deno/ho…

  • Deno中文 书 包 nugine.github. IO /deno-manual…