web.lab课程笔记分享

web.lab

前言

直接把notion里的笔记copy了出来hh

课程网址:web.lab

暑假时带我入门web前端的一门好课,从基础的git操作,html+css+js三件套,再到react框架,后端express,数据库MongoDB,socket实时聊天,网站部署,typescript,都有所涉及,而这仅仅是一门不超过四个礼拜的小课。整个代码的框架也已经大多写好,我们只需要了解其中的要义,真的是老师把知识端到我们面前我们都不好意思不吸收的感觉。

CSS

Untitled

Flex布局

ReactJS

reuse components

  • React is a Framework that lets you divide up your website into reusable components
  • Each component is kind of like a ‘custom HTML tag’ that you define
  • Your app can be divided into a ‘tree’ of components

Props: Inputs passed from a parent to a child component(immutable)

State: Private information maintained by a component.

  • We pass props in from parent to child
  • Allows our skeleton to render comments with content
  • State keeps track of private information that can be changed and influence how your app renders

JSX

() allows us to write HTML code inside JavaScript

{} allows us to return to JavaScript inside the HTML environment inside the JavaScript class

Untitled

Untitled

Untitled

  • We declare state variables with

const [something, setSomething] = useState(initialValue)

React uses className instead of class for css styles

Backend and APIs

Untitled

HTTP Methods

Status Codes

requests example

https://reqbin.com/or3jkmdl

API

Untitled

API example

  • There’s the Google Maps stuff mentioned a moment ago
  • The Dog API

https://beta.openai.com/

, check out their blog to drool over stuff

States and Props

Make states stay, make props pass

Life Cycle

Untitled

Setting State is ASYNC.异步的

Untitled

useEffect

useEffect() hook

  • Runs after specific variables change

    • Can think of as response to state change
    • Commonly used to load data into state
    • Call an API/perform some computation/etc. at specific times
  • syntax: useEffect(function, optional dependency array)

    • runs function every time a variable in dependency array changes
  • useEffect(function, [var1, var2]) runs function every time var1 or var2 changes

  • useEffect(function, []) runs function once, immediately after initialization

  • useEffect(function) runs every time any state variable changes

JSX example(conditional rendering)

Untitled

Untitled

Untitled

Loop Rendering: Using map() in React

Untitled

Untitled

summary

  • Websites are divided into components
  • components can have inputs, which are props
  • components can have their own private, updatable information: state
  • Components run their code once at the beginning, and then once every time a prop or a state changes
  • To run something only once at the beginning, we use useEffect(function, [])
  • get(“/api/something”, someData) does a get request to “/api/something”, and gets back data (asynchronously)
  • post(“/api/something”, someData) does a post request to “/api/something” (asynchronously)
  • Router is our way of having different ‘pages’ (with their own URL) on your site

Components re-run their code whenever state or props changes

UX UI

UX: User eXperience

UI: User Interaction

User Interface

  • fonts
  • color palettes
  • shapes
  • Responsive Design

Untitled

Untitled

Untitled

Figma

Server

npm:node package manager

Untitled

Untitled

Untitled

Untitled

Untitled

API

Untitled

Untitled

Untitled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
|--------------------------------------------------------------------------
| api.js -- server routes
|--------------------------------------------------------------------------
|
| This file defines the routes for your server.
|
*/

// without a system for users, we'll have to hardcode our user name
const MY_NAME = "baokker";

const data = {
stories: [
{
_id: 0,
creator_name: "Shannen Wu",
content: "I love corgis!"
}
],
comments: [
{
_id: 0,
creator_name: "Jessica Tang",
parent: 0,
content: "Wow! Me Too!",
}
],
};

const express = require("express");

const router = express.Router();

router.get("/test", (req, res) => {
res.send({ message: "Wow I made my first API! In its own file!" });
});

router.get("/stories", (req, res) => {
// send back all of the stories!
res.send(data.stories);
});

router.get("/comment", (req, res) => {
const filteredComments = data.comments.filter(
(comment) => comment.parent == req.query.parent);
res.send(filteredComments)
});

router.post("/story",(req,res)=>{
const newStory={
_id: data.stories.length,
creator_name: MY_NAME,
content: req.body.content,
};

data.stories.push(newStory);
res.send(newStory);
});

router.post("/comment",(req,res)=>{
const newComment={
_id: data.comments.length,
creator_name: MY_NAME,
parent: req.body.parent,
content: req.body.content,
};

data.comments.push(newComment);
res.send(newComment);
});

router.all("*",(req,res)=>{
console.log(`API not found: ${req.method} ${req.url}`);
res.status(404).send("API not found");
});

module.exports = router;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
|--------------------------------------------------------------------------
| server.js -- The core of your server
|--------------------------------------------------------------------------
|
| This file defines how your server starts up. Think of it as the main() of your server.
| At a high level, this file does the following things:
| - Connect to the database (TODO: WORKSHOP 5)
| - Sets up server middleware (i.e. addons that enable things like json parsing) (TODO: WORKSHOP 3)
| - Hooks up all the backend routes specified in api.js (TODO: WORKSHOP 4)
| - Sets up error handling in case something goes wrong when handling a request (TODO: WORKSHOP 3)
| - Actually starts the webserver
*/

// import libraries needed for the webserver to work!
const express = require("express"); // backend framework for our node server.
const path = require("path"); // provide utilities for working with file and directory paths

// create a new express server
const app = express();

// import the router from our API file
const api = require("./api.js");

// allow us to make post requests
app.use(express.json());

// connect API routes from api.js
app.use("/api", api);

// Load the compiled react files, which will serve /index.html and /bundle.js
const reactPath = path.resolve(__dirname, "..", "client", "dist");
app.use(express.static(reactPath));

// for all other routes, render index.html and let the react router handle it
app.get("*", (req, res) => {
res.sendFile(path.join(reactPath, "index.html"));
});

// any server errors cause this function to run
app.use((err, req, res, next) => {
const status = err.status || 500;
if( status === 500 ){
// 500 means Internal Server Error
console.log("The server errored when processing a request");
console.log(err);
}

res.status(status);
res.send({
status: status,
message: err.message,
});
});

// hardcode port to 3000 for now
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port: ${port}`);
});

Database

MongoDB: nosql, store in json

Untitled

traditional:

Untitled

workshop

Untitled

MongoDB缺点:无法保证所有数据有相同结构

因此需要使用Mongoose,它的Schema概念可以保持所有Object有相同的fields

Untitled

因此不同的模型可以遵循相同的schema

Untitled

合起来看,先连接,再创立schema,再写入数据

Untitled

增就是save

Untitled

Untitled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const express = require("express");

// we haven't set up user login yet, so just
// use a hardcoded name for now
// TODO change to a unique name for workshop
const MY_NAME = "baokker";

// import models so we can interact with the database
const Story = require("./models/story");
const Comment = require("./models/comment")

// api endpoints: all these paths will be prefixed with "/api/"
const router = express.Router();

router.get("/stories", (req, res) => {
Story.find({})
.then((stories) => res.send(stories));
});

router.post("/story", (req, res) => {
const newStory = new Story({
creator_name: MY_NAME,
content: req.body.content,
});
newStory.save().then((story) => res.send(story));
});

router.get("/comment", (req, res) => {
Comment.find({parent: req.query.parent}).then((comments) => res.send(comments));
});

router.post("/comment", (req, res) => {
const newComment = new Comment({
creator_name: MY_NAME,
content: req.body.content,
parent: req.body.parent,
});
newComment.save().then((comment) => res.send(comment));
});
1
2
3
4
5
6
7
8
9
10
11
const mongoose = require("mongoose");

//define a comment schema for the database
const CommentSchema = new mongoose.Schema({
creator_name: String,
parent: String, // links to the _id of a parent story (_id is an autogenerated field by Mongoose).
content: String,
});

// compile model from schema
module.exports = mongoose.model("comment", CommentSchema);

先连接数据库(在server.js里)

再建立model,以便interact with db

在get和post里用db的增删查改

Advanced CSS

color palettes

两到三种主题色

coolors.co

Box Model

Untitled

Untitled

layout

display

block:


等,要开新行的

inline: 等,作为文本的部分,不能设置width height top bottom等属性

inline-block:仍是inline,但可以设width和height

none:不显示

成排排列:flex,里面还有很多属性

如justify-content…

网格:grid

position

static

relative

absolute(适用于navbar sidebar等)

fixed(固定在某个位置

可以使用vw和calc两种技巧:

margin-left: calc(100vw - 75px);

calc表示数学计算,vw表示视图

transition

e.g.

transition: background-color 1s ease-in

(property | timespan | how to change)

1
2
3
4
5
6
7
nav ui li {
transition: all 0.5s; /* 所有转变都是0.5s*/
}

nav ui li:hover{
transition: scale(1.5);
}

(适合把菜单栏放大)

animation

1
2
animation: rotate 4s infinite linear
/* rotate 除此之外还有 translate scale 等 */

responsive layout

例如,导航栏在窗口够大时显示所有菜单,否则合并到一个按钮里

使用media query

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.main {
float: left;
width: 60%;
padding: 0 20px;
}

.right {
background-color: #e5e5e5;
float: left;
width: 20%;
padding: 15px;
margin-top: 7px;
text-align: center;
}

@media only screen and (max-width: 620px) {
/* For mobile phones: */
.menu, .main, .right {
width: 100%;
}
}

这里的@media便是media query,将移动端缩小

Special CSS Selectors

Untitled

除了传统的类型,类,ID选择之外(尽量不要用id!)

combinator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
h2 + p{
/* all p follow behind h2*/
}

p ~ span{
/* all span following an <p>*/
}

ul > li{
/*all li directly inside ul*/
}

div span{
/* all span anywhere inside div*/
}

还有伪类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
element:hover{/*hovering*/
}
a:active{/*click a link*/}
input:focus {tabbing or clicking on an input}
p:nth-child(4n){/*every 4th <p>*/}
p:nth-child(even){/*every even <p>*/}

p::first-line{
}

p::first-letter{
}

p:after{
}

如何开始制作自己的网站

Untitled

Accounts Authentication

authentication: proving the identity of a person or entity

方法:

  • 密码
  • 身份证
  • 手机
  • 指纹
  • etc

session

存储在服务器上

token

存在client上

用户登录→服务器生成JWT→用户发请求带着JWT→服务端校验

nitty gritty

密码必须要加密!

password hash salting(加点随机字符,再取hash)

本课程采用Google Sign-In来实现认证,Google在登录成功后,发送一个success token,再在后端验证

Socket

live communication

基本上相当于,后端发一个socket信号,前端接受到后执行相应信号

后端既可以是shout to all people,向所有人发送socket信号;也可以只向指定的客户端的socket发送信号(whisper),怎么获得这些客户端呢?答案是客户端先发post请求,初始化socket

Advanced React

不要用document.getElementById()

React Developer Tools

How to code good

git方面

经常用

1
2
3
4
5
6
7
8
9
# 开始代码前拉下队友的代码
git pull
git pull --rebase

# commit前查看状态
git status

git commit -am 'message' # 不要提交错误的代码(broken)!
# 此外 提交应尽量单功能 避免一次提交过多

少用

1
2
3
4
git add . # 直接加了所有文件 确保gitignore写好了
git push --force # 除非你知道在做什么
git reset --hard # 清空本地
git commit -amend # 我还不会

有用的例如git stash,可以暂存你本地写了但是不想提交的分支,git stash pop即可弹出。

git branch查看分支(-d删除)

git graph 图像

代码风格方面

prettier(与队友协商完整)

Typescript很棒

如何写好代码

首先要设计好(wireframe mockup prototype)

Untitled

Untitled

D.R.Y principle: don’t repeat yourself

code时经常做test

不必过度注释,优秀的代码不需要太多解释

开发,测试,debug的核心要素在于,慢一点,做一点改变就检查一下,避免一下子写了五百行,结果还找不出bug

More lines of code = More to debug

Linus’s law: “given enough eyeballs, all bugs are shallow”.

Async Computation

  • Promises have 3 states:
    • Pending initial state (neither fulfilled nor rejected)
    • Fulfilled the operation completed successfully
    • Rejected the operation failed

Handle promises using .then() and .catch()

  • Return promises and can be chained together

  • Use await keyword to wait for promises to resolve

  • Use async keyword to define asynchronous functions

  • You MUST wrap every await within an async function

Use async when we don’t know how long something will take:

  • Client-server communication
  • Server-database communication
  • React Front End
  • Background Tasks

Make your own promises with Q.deferred

处理多个Promise

Promise.all(promises).then().catch()

Promise.race(promises).then().catch() // 最先完成的

Promise.any(promises).then().catch() // 首先完成的

TypeScript

js的超集

加上了类型限制,比如说,在js里,9<10是true,”9”<”10”却是false,但是也不会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let message: string[] = ["1","2","3"]

let m: string = "hello"
m = 1 // error

type User = {
_id: string;
name: string;
is_admin?: boolean;
}

type UserLogin = User & { password: string }
const userLogin: Userlogin = {...}

const getComments = (id:string):Comment[] => { return [];}

const getComments = (id:string): Promise<Comment[]> => {}

修改config可以在tsconfig.json里改

Libraries

tailwind→引入后,只需要引入对应css class即可

ant-design→和element ui长得一样

abcjs.net→

1
2
<div id="paper"></div>
abcjs.renderAbc("paper","X:1\nK:D\n............")

甚至还可以播放(见文档)

threejs.org→3d模型 in js

autocomplete.js→自动补全

Deployment

教的是Heroku

Heroku在部署app时,不会固定地给你一个端口,而是动态分配,因此需要修改部署的配置

1
.listen(process.env.PROT || 3000

web.lab课程笔记分享
http://baokker.github.io/2022/09/13/web-lab课程笔记分享/
作者
Baokker
发布于
2022年9月13日
更新于
2022年9月13日
许可协议