本系列介绍:《全栈开发从入门到放弃》是本人学习如何使用JavaScript开发现代Web应用程序的过程记录。课程的重点是使用ReactJS构建单页面应用程序(SPA)。

本系列记录了本人学习过程中的要点,不成体系。如果你想深入学习,建议寻找官方教程 😃


JavaScript标准的正式名称是ECMAScript。由于浏览器不能支持所有JavaScript的最新特性,所以我们需要转译到一个更旧更兼容的版本。使用create-react-app创建的应用已配置为使用Babel自动转译。

Node.js是基于谷歌的 chrome V8 引擎的JavaScript运行时环境。代码文件以 .js结尾,通过 node filename.js 命令运行文件。

变量(Variables)

在JavaScript中有以下几种定义变量的方法:

const x = 1
let y = 5

console.log(x, y)
y += 10
console.log(x, y)
y = 'sometext'
console.log(x, y)
x = 4  // cause an error

const 实际是定义了一个常量, let定义了一个普通变量。推荐使用let而不是var

数组(Arrays)

使用数组的示例如下:

const t = [1, -1, 3]
t.push(5)
console.log(t.length)
console.log(t[1])
t.forEach(value => {
  console.log(value)
})

即使使用const定义,也可以修改数组中的内容。因为数组是一个对象,数组变量指向这同一个对象。

遍历元素的一种方法是使用 forEach ,如示例所示, forEach 接收一个函数作为入参。forEach 为数组中的每个元素调用这个函数,并将单个项作为参数传递。

使用push方法可以添加元素。但推荐使用函数式编程的技巧。函数编程范型的一个特点就是使用不可变的数据结构。在React代码中,最好使用 concat 方法。它不向数组中添加元素,而是创建一个新数组,新数组中包含旧数组和新元素。

const t = [1, -1, 3]
const t2 = t.concat(5)
console.log(t2)

map方法可创建新数组。

const t = [1, 2, 3]
const m1 = t.map(value => value *2)
console.log(m1)

此外,还可转换为不同的内容。

const m2 = t.map(value => '<li>' + value + '</li>')
console.log(m2)

还可使用解构赋值方式将数组中的单个元素赋值给变量。

const t = [1, 2, 3, 4, 5]
const [first, second, ...rest] = t
console.log(first, second)
console.log(rest)

例子中剩余的整数被“收集”到另一个数组中,分配给rest。

对象(Objects)

一个使用对象的例子:

const object1 = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
}

const object2 = {
  name: 'Full Stack web application development',
  level: 'intermediate studies',
  size: 5,
}

const object3 = {
  name: {
    first: 'Dan',
    last: 'Abramov',
  },
  grades: [2, 3, 5, 3],
  department: 'Stanford University',
}

// 对象元素的访问可通过.或[]进行
console.log(object1.name)         // Arto Hellas is printed
const fieldName = 'age' 
console.log(object1[fieldName])    // 35 is printed

// 可以动态添加对象属性
object1.address = 'Helsinki'
object1['secret number'] = 12341

函数(Functions)

函数定义的几种方式:

const sum = (p1, p2) => {
  console.log(p1)
  console.log(p2)
  return p1 + p2
}
const result = sum(1, 5)
console.log(result)

// 只有一个参数时可省略括号
const square = p => {
  console.log(p)
  return p * p
}

// 只有一个表达式时可省略大括号
const square = p => p * p
// 该方法适合操作数组
const t = [1, 2, 3]
const tSquared = t.map(p => p * p)

// 或可使用function关键字定义
function product(a, b) {
  return a * b
}

const result = product(2, 6)

// 或使用函数表达式
const average = function(a, b) {
  return (a + b) / 2
}

const result = average(2, 5)

本课程推荐使用箭头语法。

对象方法和”this”关键字

尽量避免使用this关键字以防止问题。 使用 setTimeOut方法,避免问题的方法:

const arto = {
  name: 'Arto Hellas',
  greet: function() {
    console.log('hello, my name is ' + this.name)
  },
}

setTimeout(arto.greet, 1000)
// 使用bind来保留原始的this
setTimeout(arto.greet.bind(arto), 1000)

类(Classes)

Javascript中的类非常像Java中的类,但其实质上仍然是Object。课程建议使用hooks特性,所以不具体使用类语法。

本系列介绍:《全栈开发从入门到放弃》是本人学习如何使用JavaScript开发现代Web应用程序的过程记录。课程的重点是使用ReactJS构建单页面应用程序(SPA)。

本系列记录了本人学习过程中的要点,不成体系。如果你想深入学习,建议寻找官方教程 😃


新建React应用

创建一个React应用可用create-react-app,如:

npx create-react-app part1
cd part1
npm start

组件(component)

在使用React开发时,通常将需要渲染的内容定义为React组件。一个定义组件的示例如下:

const App = () => {
  const a = 10
  const b = 20
  return (
    <div>
      <p>Hello world</p>
      <p>
        {a} plus {b} is {a + b}
      </p>
    </div>
  )
}

JavaScript中大括号中的代码会被计算,计算结果将嵌入到组件生成的HTML中。可渲染动态内容。

JSX

JSX 是 JavaScript 的语法扩展。在底层,React 组件返回的 JSX 会被Babel编译成 JavaScript 。

JSX 是类XML语言,每个标签都需要关闭。如HTML中的<br>在JSX中,写作<br />

多组件(Multiple component)

使用React编写组件很容易,定义的组件可以在其他组件中使用。通常应用的组件树顶部有个root组件叫App,但这也可修改。

props: 向组件传递数据

使用props,可将数据传递给组件。可以有多个props值传递,如果props是JavaScript表达式,则需要使用花括号。

一些注意事项

  1. React组件名称首字母必须大写
  2. React组件内容通常需要包含一个根元素
  3. 如果不想在Dom树中看到多余的div元素,可使用空元素包装组件的返回内容。

本系列介绍:《全栈开发从入门到放弃》是本人学习如何使用JavaScript开发现代Web应用程序的过程记录。课程的重点是使用ReactJS构建单页面应用程序(SPA)。

本系列记录了本人学习过程中的要点,不成体系。如果你想深入学习,建议寻找官方教程 😃


必要软件安装

  1. Chrome 或 Firefox 开发版。
  2. Git 和 GitHub。
  3. Visual Studio Code。
  4. Node.js

Web开发准则:始终打开开发者控制台。

HTTP GET

服务器和浏览器使用HTTP协议相互通信, 浏览器可使用 Get 方法发送请求。

传统的网络应用

由于含有变量,模板字符串是可运行的字符串。

课程使用 Node.jsExpress 创建Web服务器。

使用 Console 选项卡和 console.log 命令可以方便调试。

事件处理和回调函数

调用事件处理程序的机制在 Javascript 中非常常见。事件处理函数被称为回调函数。应用代码时不直接调用函数本身,而是运行时浏览器会在事件发生的适当时间调用函数。

文档对象模型(Document Object Model, DOM)

DOM 是一个 API, 它支持对web页面对应的元素树进行编程修改。

从console中操作文档对象

html 文档 DOM 树的最顶层节点称为文档对象(document)。可以在控制台的console中操作文档对象。例如:

list = document.getElementsByTagName('ul')[0];
newElement = document.createElement('li');
newElement.textContent = 'Page manipulation from console is easy.是吧'
list.appendChild(newElement)

注意,上述更改只是更改浏览器页面,并没有将更改推送到服务器。

层叠样式表(Cascading Style Sheets, CSS)

CSS 是一种用来确定web应用外观的标记语言。

CSS 中的类选择器用于选择页面中的某些部分,并对它们定义样式规则。例如:

.container {
  padding: 10px;
  border: 1px solid;
}

类选择器的定义始终以句点 . 开头,并包含类的名称。

CSS 属性可以添加到 HTML 元素中,例如:

<body>
  <div class="container">
  <h1>Notes</h1>
  </div>
</body>

HTML 元素也可以有 class 以外的属性。例如:

<div id="notes">

上述代码中包含id属性,JavaScript 代码可使用 id 来查找元素。同样可以在控制台的Elements中更改元素样式,这也不是永久性的。

表单与HTTP POST

表单 Form 具有属性 action 和 method,例如:

<form action="/new_note" method="POST">
  <input type="text" name="note">
  <br>
  <input type="submit" value="Save">
</form>

上述属性定义将表单作为一个 HTTP POST 请求提交到 /new_note 地址。数据作为 POST 请求的body发送到服务器。

例如,下述服务器端代码可通过访问请求对象req的body来访问发送来的数据。

app.post('/new_note', (req, res) => {
  notes.push({
    content: req.body.note,
  });
  return res.redirect('/notes');
});

AJAX

异步的JavaScript和XML(Asynchronous JavaScript and XML, AJAX) 是2005年2月引入的一个术语。它描述了一种新的革命性的方法,这种方法使用包含在 HTML 中的 JavaScript 来获取网页内容,而且不需要重新渲染页面。现在已经普遍应用。

单页面应用

单页面应用 Single-page application (SPA) 只从服务器获取一个HTML页面,内容由JavaScript在浏览器中执行操作。

JavaScript库

JavaScript工具库操作页面更容易。jQuery因为它所谓的跨浏览器兼容性发展开来。

由于SPA的发展,几种更现代的开发方式流行开来。先是BackboneJS, 然后有AngularJS。现在流行的有ReactVue.js

此次学习课程中使用 React 和 Redux 库。

全栈web开发

课程中将关注应用的所有部分: 从前端、后端到数据库。将使用 JavaScript 编写后端代码,使用 Node.js 运行时环境。

0. 背景

LastPass 个人用户手机端和电脑端同步要收费了。。。也一直种草 Bitwarden 很久了,想着反正有个服务器,不如自己整一个密码管理服务。上Bitwarden官网一看,最小需求也比我这小破站大呀。。。

不过又看见有人用 Rust 重写了一个服务端bitwraden_rs,用的服务器资源并不多。索性自己也整一个,记录下过程。

1. 安装docker

由于bitwarden_rs使用Docker镜像安装,参照Docker官方介绍 安装docker。

1.1 卸载旧版本

其实不需要。。。。。

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

1.2 安装docker

可自行选择一种方式安装,我选择设置 repository 的方式。

  • 首先设置源。
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
  • 安装
sudo yum install docker-ce docker-ce-cli containerd.io
  • 启动
sudo systemctl start docker
  • 测试
sudo docker run hello-world
  • 添加用户到 docker
sudo gpasswd -a king docker

2. 域名解析设置

依据自己的实际情况,设置域名解析。

如解析 https://bitwarden.robincn.com 到 server_ip。

3. 安装bitwarden_rs

参照 bitwarden_rs官方介绍 安装。

docker pull bitwardenrs/server:latest

4. 规划转发规则并设置nginx

根据我的小站情况,拟将所有访问 https://bitwarden.robincn.com 的全部转发至 8080 端口。docker 容器中运行 bitwarden_rs 服务并将容器内的80端口映射到主机的8080端口,容器内的3012端口映射到主机的3012端口。

4.1 ssl证书设置

和之前一样使用 acme.sh 生成并安装证书。

acme.sh --issue --dns dns_cf -d bitwarden.robincn.com --nginx
acme.sh --install-cert --nginx -d bitwarden.robincn.com --key-file /etc/nginx/bitwardenrobincn/key.pem --fullchain-file /etc/nginx/bitwardenrobincn/cert.pem --reloadcmd "service nginx force-reload"

4.2 nginx设置

修改 nginx 配置文件,将 bitwarden.robincn.com 的访问全部转发到主机的8080端口。

# vim /etc/nginx/nginx.conf
# http{ # 在http段内增加如下内容server

    upstream bitwardenrs-default { server localhost:8080;}
    upstream bitwardenrs-ws { server localhost:3012;}
	
	# 所有访问都走https
    server {	
        listen 80;
        listen [::]:80;
        server_name bitwarden.robincn.com;
        return 301 https://$host$request_uri;
    }

	# 转发对应端口到主机80803012
    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name bitwarden.robincn.com;

        ssl_certificate "/etc/nginx/passrobincn/cert.pem";
        ssl_certificate_key "/etc/nginx/passrobincn/key.pem";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers PROFILE=SYSTEM;
        ssl_prefer_server_ciphers on;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;

        proxy_intercept_errors on;  # for error page render
        large_client_header_buffers 2 2k;   # 解决 Android 客户端同步问题

        # 解决网页加载问题
        gzip off;
        proxy_max_temp_file_size 0;

        client_max_body_size 128M;

        location / {
                proxy_set_header Host  $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                proxy_pass http://bitwardenrs-default;
        }
        location /notifications/hub/negotiate {
                proxy_set_header Host  $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                proxy_pass http://bitwardenrs-default;
        }
        location /notifications/hub {
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header Connection $http_connection;

                proxy_pass http://bitwardenrs-ws;
        }

    }


5. 启动 Bitwarden 并测试

5.1 启动与测试

用命令行启动 Bitwarden 并通过网页添加用户进行基础测试。

注意本人将 Bitwarden 相关数据放在用户目录下。

docker run -d --name bitwarden \
-v /home/king/bitwarden/data/:/data/ \
-p 8080:80 \
-p 3012:3012 \
-e LOG_FILE=/data/bitwarden.log \
-e WEBSOCKET_ENABLED=true \
-e WEB_VAULT_ENABLED=true \
-e SIGNUPS_ALLOWED=true \
-e DOMAIN=https://bitwarden.robincn.com \
bitwardenrs/server:latest
# 返回docker的ID

字段说明如下

  • WEB_VAULT_ENABLED=true 开启网页端
  • SIGNUPS_ALLOWED=true 允许用户注册
  • 以上两字段首次使用注册用户需要,之后可改为 false

如遇到问题,可使用以下命令查看相关日志。

docker ps
# 成功可看到STATUS中的 healthy ,确认PORTS映射关系无误
docker logs *ID*  # 查看docker启动日志
docker stop *ID*  # 停止docker实例
docker rm *ID*  # 删除docker实例

5.2 新建用户与导入数据

启动成功后用浏览器访问 https://bitwarden.robincn.com 即可新建用户使用了。

参照官方说明导入数据。

6. 安装Docker Compose并启动服务

6.1 安装

参照官方介绍安装 Docker Compose.

sudo curl -L "https://github.com/docker/compose/releases/download/1.28.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

docker-compose --version

6.2 设置

data 目录下新建 Docker Compress 服务描述文件。

# vim /home/king/bitwarden/docker-compose.yml
version: "3"

services:
        bitwarden:
                image: bitwardenrs/server
                container_name: bitwardenrs
                restart: always
                ports:
                        - "8080:80"
                        - "3012:3012"
                volumes:
                        - ./bw-data:/data
                environment:
                        LOG_FILE: "/data/bitwarden.log"
                        WEBSOCKET_ENABLED: "true"
                        SIGNUPS_ALLOWED: "false"  # 关闭用户注册 
                        WEB_VAULT_ENABLED: "false"  # 关闭浏览器访问
                        DOMAIN: "https://bitwarden.robincn.com"

6.3 启动服务

注意:首先关闭之前启动的Bitwarden

docker stop *ID*
docker rm *ID*

使用 Docker Compose 启动服务。

cd /home/king/bitwarden/
docker-compose up -d  # 启动服务
docker-compose down		# 关闭服务
docker-compose restart  # 重启服务

7. 安装客户端与其他设置

7.1 安装客户端

参照官网下载地址下载安装各平台的客户端和/或插件。

7.2 其他设置

参照bitwarden_rs/wiki 进行其他设置(如fail2ban设置等)。

之前(大约十年前还在读书时)因为经常更新系统所以手写过crontab文件以实现自动更新。

最近时常登陆VPS去更新系统就想起这事,结果一查发现CentOS有一种简单的方法——就是使用yum-cron。

安装yum-cron

使用yum-cron的第一步当然毫不例外是安装 - -

就像安装其他应用一样

sudo yum install yum-cron

设置

参照说明编辑/etc/yum/yum-cron.conf

# sudo vim /etc/yum/yum-cron.conf
apply_updates = yes  # 确保更新被安装
exclude = kernel* mysql*  # 自动更新排除的软件包

启用

编辑完成后,启用服务以使自动更新生效。

sudo systemctl start yum-cron
sudo systemctl enable yum-cron