Deep Dive Into Modern Web Development

本文最后更新于 2022年11月21日 晚上

University of Helsinki 课程 深入浅出现代Web编程 笔记

Deep Dive Into Modern Web Development

Web 应用的基础设施

基础知识

  • Chrome/Firefox
  • Git
  • Visual Studio Code ✅
    • nano、 Notepad、 Gedit、NetBeans ❎
  • Node.js>10.18

Web 应用的基础设施

  • 开发者模式
    • Network,选中 Disable cache

HTTP GET

Traditional web applications

  • 当进入一个页面时,浏览器会从服务器获取 HTML 文档的详细页面结构,以及文本内容

    • 这个文档可能是保存在服务器目录中的静态文本文件
    • 可能是服务器根据应用的代码动态构建的 HTML 文档
  • 传统的 web 应用中,浏览器是个“憨憨”。 它只会从服务器获取 HTML 数据,所有应用的逻辑都在服务器上处理

Running application logic on the browser

  • 调用链条的代码流程分析
  • 控制台上能输出内容: console.log

Event handlers and Callback functions/事件处理和回调函数

  • 事件处理和回调函数
  • 事件处理函数被称为回调函数。应用代码并不直接调用函数本身,而是运行时环境(浏览器)会在事件发生时适当时间调用函数

Document Object Model or DOM

  • 我们可以将 html 页面看作隐式树结构
    • 浏览器的功能就是基于这种,把 HTML元素描述成一棵树的想法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
html
head
link
script
body
div
h1
div
ul
li
li
li
form
input
input
  • 文档对象模型 DOM 是一个应用编程接口 API,它支持对 web 页面对应的元素树进行编程修改

Manipulating the document-object from console/从控制台中操作文档对象

  • 从控制台中操作文档对象
  • DOM 树的最顶层节点称为文档 document 对象
    • 使用 DOM-API 在网页上执行各种操作
      • 可以通过在控制台中键入 document 来访问文档对象

CSS

  • 层叠样式表 CSS,是一种用来确定 web 应用外观的标记语言

  • 类选择器 class selectors:用于选择页面的某些部分,并对它们定义样式规则来装饰它们

    • 类选择器的定义始终以句点开头,并包含类的名称
  • 控制台的 Elements 选项卡可用于更改元素的样式

Loading a page containing JavaScript - revised/加载一个包含 JavaScript 的页面-复习

  • 浏览器使用 HTTP GET 请求从服务器获取定义内容和页面结构的 HTML 代码
  • Html 代码中的 Links 标签会让浏览器获取 CSS 样式表 main.css
  • 以及 JavaScript 代码文件 main.js
  • 浏览器执行 JavaScript 代码,代码向地址 https://studies.cs.helsinki.fi/exampleapp/data.json 发出 HTTP GET 请求,请求返回了包含 note 的 JSON 数据。
  • 获取数据后,浏览器执行一个 event handler 事件处理程序,使用 DOM-API 将 Note 渲染到页面

Forms and HTTP POST/表单与 HTTP POST

  • 发送表单的 HTTP POST 过程

AJAX

  • AJAX:使用包含在 HTML 中的 JavaScript 来获取网页内容,而且不需要重新渲染页面
    • 以前向服务器请求资源,必须对整个页面资源进行请求以获得这个信息资源;而现在只需要请求资源载体 JSON/XML 就能局部刷新

Single page app/单页面应用

  • 传统的网页:所有的逻辑都在服务器上,浏览器只按照指示渲染 HTML
  • 单页应用 SPA:只从服务器获取一个 HTML 页面,其内容由 JavaScript 在浏览器中执行操作

JavaScript-libraries/JavaScript 库

  • 库:通常会使用比直接操作 DOM-API 更容易的工具库来操作页面
    • e.g.:jQuery 、Angular、React 、VueJS

Full stack web development/全栈-web 开发

  • 最接近最终用户的浏览器是最顶层,而服务器是最底层。 在服务器下面通常还有一个数据库层。 因此,我们可以将 web 应用的体系结构看作是一层层的堆栈。

练习

练习0.1
练习0.2
练习0.3
练习0.4
练习0.5
练习0.6

React 入门

React 简介

  • 利用 create-react-app 新建项目

Componet/组件

  • 定义组件

    • 箭头函数(ES6)
1
2
3
4
5
(param1, param2, …, paramN) => expression
//相当于:(param1, param2, …, paramN) =>{ return expression; }

// 没有参数的函数应该写成一对圆括号。
() => { statements }
  • 定义组件的函数中可以包含任何类型的 JavaScript 代码

  • 可以在组件内部渲染动态内容

JSX

  • 看起来 React 组件返回的是 HTML 标签,但实际并不是这样。
    • React 组件的布局大部分是使用JSX编写的。 尽管 JSX 看起来像 HTML,但我们其实是在用一种 特殊的方法写 JavaScript
    • 在底层,React 组件实际上返回的 JSX 会被编译成 JavaScript

也可以将 React 写成“纯 JavaScript”,而不用 JSX。 但没有一个精神正常的人会这样做的。

  • JSX 是一种“类XML”语言,这意味着每个标签都需要关闭。例如:<br />

Multiple components/多组件

  • React 的核心理念,就是将许多定制化的、可重用的组件组合成应用
  • 约定:应用的组件树顶部都要有一个 root 组件叫做 App

props: passing data to components/props:向组件传递数据

  • props:作为参数,它接收了一个对象,该对象具有组件中所定义的所有“属性”所对应的字段
    • 任意数量
    • 可以是字符串,也可以是 JavaScript 表达式的结果(需要用花括号括起来)

Some note/一些注意事项

  • 控制台应该始终开着,确保每一个修改都能按照预期的方式工作
    • 可以在 React 代码中加入 console.log()
  • React 组件名称首字母必须大写
  • React 组件的内容(通常)需要包含 一个根元素,例如<div> <div/>
    • 创建组件数组也是一个有效的解决方案,但是不明智
    • 由于根元素是必须的,所以在 Dom 树中会有额外的 div 元素。 这可以通过使用 <> <\>来避免,即用一个空元素来包装组件的返回内容

JavaScript

  • node 文件名.js 命令以运行文件。

Variables/变量

  • const 定义常量,let 定义变量

    • 分配给变量的数据类型,在执行过程中可以发生更改

    • 本课程中明确不建议使用 var

Arrays/数组

  • 即使将数组用 const 定义,也可以修改该数组中的内容

    • 因为数组是一个对象,而数组变量总是指向这同一个对象(类似于指针
  • push 方法将一个新元素添加到数组中

    • 函数编程范型的一个特点,就是使用不可变的数据结构
    • 在React代码中,最好使用 concat方法 ,因为它不向数组中添加元素,而是创建一个新数组,新数组中包含了旧数组和新的元素
      • t.concat(5) 这种方法调用不会向旧数组添加新的元素,而是直接返回一个新数组
  • 遍历元素的一种方法是使用 forEach

  • 数组中的单个元素可以很容易地通过解构赋值赋给变量

1
2
3
4
const t = [1, 2, 3]

const m1 = t.map(value => value * 2)
console.log(m1) // [2, 4, 6] is printed

Objects/对象

  • 定义对象
    • 一个非常常见的方法是使用对象字面量,就是通过在大括号中列出它的属性来实现的

      • 属性的值可以是任何类型的,比如整数、字符串、数组、对象
      • 对象的属性可以使用 “句点”号或括号进行引用、添加
      1
      2
      object1.address = 'Helsinki'
      object1['secret number'] = 12341 // 这个属性的添加必须通过使用中括号来完成,因为属性名中有空格
    • 也可以利用构造函数定义对象,但是 JavaScript 并没有对标面向对象中类的概念

Functions/函数

  • 箭头函数
1
2
3
4
5
6
7
const sum = (p1, p2) => {
console.log(p1)
console.log(p2)
return p1 + p2
}

const result = sum(1, 5)
  • 关键字 function

    • 函数声明
    1
    2
    3
    4
    5
    function product(a, b) {
    return a * b
    }

    const result = product(2, 6)
    • 函数表达式
    1
    2
    3
    4
    5
    const average = function(a, b) {
    return (a + b) / 2
    }

    const result = average(2, 5)
  • 在本课程中,我们将使用箭头语法定义所有函数

Object methods and “this”/对象方法以及“ this”关键字

由于新 React 中包含 React Hook,因此不需要定义带有函数的对象。因此这部分内容与课程是无关的。

  • 可以通过给一个对象定义函数属性,来给对象分配方法

    • 方法甚至可以在对象创建之后再赋值给对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const arto = {
    name: 'Arto Hellas',
    age: 35,
    education: 'PhD',
    greet: function() {
    console.log('hello, my name is ' + this.name)
    },
    }

    arto.growOlder = function() {
    this.age += 1
    }
  • this 的问题:

    • 与其他语言相反,在 JavaScript 中,this 的值是根据方法如何调用来定义的。
    • 当通过引用调用该方法时, this 的值就变成了所谓的全局对象 ,而最终结果往往不是软件开发人员设想的那样。
    • 有几种机制可以保留原始是 this,例如 bind 方法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const arto = {
    name: 'Arto Hellas',
    greet: function() {
    console.log('hello, my name is ' + this.name)
    },
    }
    // this 指向全局对象,输出 ‘hello, my name is’
    setTimeout(arto.greet, 1000)

    // this 绑定指向到了 Arto,输出 ‘hello, my name is Arto Hellas’
    setTimeout(arto.greet.bind(arto), 1000)

Classes/类

  • JavaScript 没有类,但是在 ES6 中引入了类语法。
    • 在语法方面,类以及由它们创建的对象非常类似于 Java 的类和对象。
    • 在本质上,它们仍然是基于 JavaScript 的原型继承的对象(Object)

组件状态,事件处理

Component helper functions/组件辅助函数

  • 组件辅助函数,可以直接访问传递给组件的所有 props
    • 在 JavaScript 中,在函数中定义函数是一种常规操作

Destructuring/解构

  • props 是一个对象,因此 const { name, age } = props (解构赋值)是可行的

Page re-rendering/页面重渲染

  • 重复调用 ReactDOM.render 可以实现页面免刷新进行重渲染,但是不推荐

Stateful component/有状态组件

  • 通过 React 的 state hook 向组件中添加状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 导入了 useState 函数
import React, { useState } from 'react'

const App = () => {
// 解构赋值语法将元素分配给变量 counter 和 setCounter
// setCounter 是 状态修改函数,被调用时 React 会重新渲染该组件
const [ counter, setCounter ] = useState(0)

// 超时 1 秒则调用 setCounter(counter + 1)
setTimeout(
() => setCounter(counter + 1),
1000
)

return (
<div>{counter}</div>
)
}

export default App

Event handling/事件处理

  • button-元素支持所谓的鼠标事件 ,其中点击是最常见的事件

点击事件同样可能被键盘或者触屏设备所触发,虽然名字叫鼠标事件

  • 例子:

    1
    <button onClick={() => console.log('clicked')}>

Event handler is a function/事件处理是一个函数

  • 对比:

    1
    2
    3
    <button onClick={() => setCounter(counter + 1)}> 
    plus
    </button>
    1
    2
    3
    <button onClick={setCounter(counter + 1)}> 
    plus
    </button>
  • 后者是不行的,因为 React 第一次渲染时,它执行函数调用 setCounter(0+1),并将组件状态修改为 1。这导致组件再次渲染,React 将再次执行 setCounter(1+1) 并不断重复下去。【Error: Too many re-renders】


Passing state to child components/将状态传递给子组件

通常,几个组件需要反映相同的变化数据。我们建议将共享状态提升到它们最接近的共同祖先。

Changes in state cause rerendering/状态的改变导致重新渲染

  • 调用一个改变状态的函数会导致组件的重新渲染

深入React 应用调试

Complex state/复杂状态

  • 对于更复杂的状态,最简单的方法是多次使用 useState 函数来创建单独的状态“片段”
  • 展开语法可以更加整洁地定义新的状态对象
    • 例如:{ ...clicks, right: clicks.right + 1 } 创建了 clicks 对象的副本,其中 right 属性的值增加了1
  • React 中状态不可直接修改count ++),因为它会导致意想不到的副作用。 必须始终通过将状态设置为新对象来更改状态。 所以调用一个改变状态的函数。

Handling arrays/处理数组

  • join 方法可以将数组的所有项目连接到一个字符串中

  • 向数组中添加新元素是通过 concat 方法完成的,该方法不改变现有数组,而是返回数组新副本,并将元素添加到该数组中。

    • 例如:
    1
    const [allClicks, setAll] = useState([])

    可以使用 concat 修改

    1
    setAll(allClicks.concat('R'))

    但是用 push 就不太合适,因为相当于直接修改状态,容易有莫名的 Bug:

    1
    2
    allClicks.push('R')
    setAll(allclicks)

Conditional rendering/条件渲染

  • React 支持 if - else 语法

Old React/老版本的 React

  • 在 16.8.0 之前,React 尚无 Hook 来添加状态到 React 组件。这个时期需要状态的组件必须使用 JavaScript 类语法定义为 class 组件。

Debugging React applications/调试React应用

(The first rule of web development web 开发第一原则)
Keep the browser’s developer console open at all times.
始终打开浏览器的开发控制台

  • console.log 进行调试时,不要使用“加号”,即 console.log('props value is' + props)。正确的写法是 console.log('props value is', props)
  • 开发者选项中 Sources 选项卡中添加断点,检查组件变量的值可以在 Scope-部分完成。

Rules of Hooks/Hooks 的规则

  • 不能从循环、条件表达式或任何不是定义组件的函数的地方调用 useState,这点是为了确保 Hook 总是以相同的顺序调用

Function that returns a function/返回函数的函数

  • 定义事件处理程序的另一种方法是使用返回函数的函数
    • 本质:事件处理程序不能是对函数的调用,它必须是函数或对函数的引用
1
2
3
4
5
6
const hello = (who) => {
const handler = () => {
console.log('hello', who)
}
return handler
}

Do Not Define Components Within Components/不要在组件中定义组件

  • 不要在其他组件内部定义组件。 这会引起各种 Bugs。最大的问题是 React 在每次渲染时,会将内部的组件当作一个新的组件。这将导致 React 无法去优化组件。

练习

练习1.1
练习1.2
练习1.3
练习1.4
练习1.5
练习1.6
练习1.7
练习1.8
练习1.9
练习1.10
练习1.11
练习1.12
练习1.13
练习1.14

Deep Dive Into Modern Web Development
https://justloseit.top/Deep Dive Into Modern Web Development 学习笔记/
作者
Mobilis In Mobili
发布于
2021年3月18日
更新于
2022年11月21日
许可协议