0%

前情回顾

在实际的项目开发时, 我们面对的往往不是如此简单的工厂, 而是会面对多个工厂.

在简单工厂模式中, 如果我们再加入学生会干部, 就意味着我们需要修改我们的工厂函数. 长此以往, 我们的工厂函数会变得复杂臃肿.

回顾我们设计模式的开放封闭原则, 对扩展开放, 对修改封闭 而我们添加其他判断逻辑则是在修改它.

所以我们需要一个更加合理的方式来处理

抽象工厂模式

由于抽象最初源于 Java 等强类型静态语言, 在设计初期, 往往就需要关注类型解耦. 而 JavaScript 是一个动态类型的语言, 天然具有多态性, 但目前的 JavaScript 语法里,不支持抽象类的直接实现, 所以我们只能模拟抽象类.

实现一个生产手机的抽象工厂

一个手机生产线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 抽象工厂-只对类抽象的对象基本组成进行约束
class PhoneFactory {
createOs() {
throw new Error('抽象工厂创建软件系统方法, 需要重写')
}
createHardware() {
throw new Error('抽象工厂创建硬件方法, 需要重写')
}
}

// 具体工厂-实现具体功能
class FakeStarFactory extends PhoneFactory {
createOs() {
return new IOS()
}
createHardware() {
return new MI()
}
}

抽象出系统部分的抽象工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 抽象手机系统工厂
class OS {
run() {
throw new Error('抽象工厂, 不允许直接调用')
}
}

// 具体运行系统工厂
class Ios extends OS {
run() {
console.log('用ios系统方式启动')
}
}
class Android extends OS {
run() {
console.log('用安卓系统启动')
}
}

抽象出硬件的抽象工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 抽象硬件类
class Hardware {
operating() {
throw new Error('抽象工厂方法, 不允许直接调用')
}
}

// 具体工厂
class MI extends Hardware {
operating() {
console.log('使用小米硬件')
}
}
class HUAWEI extends Hardware {
operating() {
console.log('使用华为硬件')
}
}

开始生产手机

1
2
3
4
5
const phone = new FakeStarFactory() // 生产手机
const phoneOs = phone.createOs() // 加上系统
const phonrHardware = phone.createHardware() // 加上硬件
phonrHardware.operating() // 选择硬件
phoneOs.run() // 运行系统

  • 构造器模式
  • 简单工厂模式

构造器模式-抽象每个对象的变与不变

你正在开发一个学生管理系统, 开发时只有自己, 于是创建自己的用户信息:

1
2
3
4
5
6
// 字面量
const zhangsan = {
name: '张三',
age: 22,
gender: 0
}

过了两天, 你的同事过来说让你加上他的用户信息, 他要使用用户信息, 于是你:

1
2
3
4
5
6
7
8
9
10
11
// 字面量
const zhangsan = {
name: '张三',
age: 22,
gender: 1
}
const lisi = {
name: '李四',
age: 20,
gender: 1
}

过了两个星期,产品经理跟你说, 我这里有一点点(1000 个)测试数据, 你给我录入下, 于是你写了个构造函数(构造器)

1
2
3
4
5
6
7
8
// 构造器
function Students(name, age, gender) {
this.name = name
this.age = age
this.gender = genger
}
// ... 此处省略读取数据, 遍历调用构造器
const student = new Students(name, age, gender)

思考 变与不变

不变的是每个学生都有姓名、年龄、性别三个属性,这叫共性
变化的是三个属性的值,这叫个性

案例中构造器在整个过程中就是抽象了学生这个对象, 将赋值的过程进行了封装, 确定不变的部分, 使每个学生对象都有自己的姓名、年龄、性别三个属性, 在确保不变的同时, 将变化的三个属性的值进行开放, 由用户自己传入, 保证了个性的灵活度

工厂模式-抽象不同构造器之间的变与不变

第一期交付后一个月, 学校方面打来电话, 表示要对学生中的班干部进行区分, 备注班干部职责, 于是你加了一个班干部的构造器用来生成班干部的信息,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Students(name, age, gender) {
// 普通学生
this.name = name
this.age = age
this.gender = genger
this.identity = 'student'
this.duties = ['学习']
}

function Cadres(name, age, gender) {
// 班干部
this.name = name
this.age = age
this.gender = genger
this.identity = 'cadre'
this.duties = ['点名','出黑板报']
}

这时候, 你发现又出现了 变数 identity, 你还需要一个方法来判断调用哪一个构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
function Factory(name, age, gender, identity) {
switch(identity) {
case 'student':
return new Students(name, age, gender)
break
case 'cadre':
return new Cadres(name, age, gender)
break
// 此处省略无数个班干部
default:
break
}
}

写完后, 你发现每类班干部职责实在太多了, 难道要写几十个构造器么

重新思考 变与不变

在我们的学生与班干部两个构造器中, 都拥有不变的姓名、年龄、性别三个共性, 变化是的身份与职责, 由此可以看出我们的问题, 共性封装不够、共性与个性分离不彻底, 于是我们重新封装下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Student(name, age, gender, identity, duties) {
this.name = name
this.age = age
this.gender = gender
this.identity = identity
this.duties = duties
}
function Factory(name, age, gender, identity) {
let duties
switch(identity){
case 'student':
duties = ['学习','做作业']
break
case 'cadre':
duties = ['组织活动','点名']
default:
break
}
return new Student(name, age, gender, identity, duties)
}
工厂模式就是对创建对象的过程进行封装, 我们不需要在关心封装内做的事情,只需要拿到工厂交付给我们的结果即可.

设计模式

软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

设计模式是一套现成的工具, 拿来即可用. 就像电饭锅、洗衣机, 不用关心电饭锅、洗衣机的制作原理, 只需将需要的特定事物放入, 即可得到想要的结果.

JavaScript 主要设计原则–面向对象编程和面向对象设计

  • 单一功能原则
  • 开放封闭原则

核心思想–封装变化

一个简单的产品流程节点可以分为:

想法 -> 需求 -> 设计 -> 开发 -> 测试 -> 部署 -> 产品

  通过上面的关键节点可以看出, 影响一个产品的复杂程度的主要节点是需求与设计, 在抛开个人能力不谈的时候, 设计的复杂程度取决于需求的复杂程度, 所以我们可以说需求是一个产品复杂程度的”罪魁祸首”, 而需求对于开发人员来说, 往往就是变化

在实际开发过中, 我们需要做的就是将变化造成的影响 最小化.

-- 将变与不变分离, 保证变化部分的灵活性, 保证不变部分的稳定性.

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
<!DOCTYPE html>  H5标准声明,使用 HTML5 doctype,不区分大小写
<head lang=”en”> 标准的 lang 属性写法
<meta charset=’utf-8′> 声明文档使用的字符编码
<meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1″/> 优先使用 IE 最新版本和 Chrome
<meta name=”description” content=”不超过150个字符”/> 页面描述
<meta name=”keywords” content=””/> 页面关键词
<meta name=”author” content=”name, email@gmail.com”/> 网页作者
<meta name=”robots” content=”index,follow”/> 搜索引擎抓取
<meta name=”viewport” content=”initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no”> 为移动设备添加 viewport
<meta name=”apple-mobile-web-app-title” content=”标题”> iOS 设备 begin
<meta name=”apple-mobile-web-app-capable” content=”yes”/> 添加到主屏后的标题(iOS 6 新增)
是否启用 WebApp 全屏模式,删除苹果默认的工具栏和菜单栏
<meta name=”apple-itunes-app” content=”app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL”>
添加智能 App 广告条 Smart App Banner(iOS 6+ Safari)
<meta name=”apple-mobile-web-app-status-bar-style” content=”black”/>
<meta name=”format-detection” content=”telphone=no, email=no”/> 设置苹果工具栏颜色
<meta name=”renderer” content=”webkit”> 启用360浏览器的极速模式(webkit)
<meta http-equiv=”X-UA-Compatible” content=”IE=edge”> 避免IE使用兼容模式
<meta http-equiv=”Cache-Control” content=”no-siteapp” /> 不让百度转码
<meta name=”HandheldFriendly” content=”true”> 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓
<meta name=”MobileOptimized” content=”320″> 微软的老式浏览器
<meta name=”screen-orientation” content=”portrait”> uc强制竖屏
<meta name=”x5-orientation” content=”portrait”> QQ强制竖屏
<meta name=”full-screen” content=”yes”> UC强制全屏
<meta name=”x5-fullscreen” content=”true”> QQ强制全屏
<meta name=”browsermode” content=”application”> UC应用模式
<meta name=”x5-page-mode” content=”app”> QQ应用模式
<meta name=”msapplication-tap-highlight” content=”no”> windows phone 点击无高光
设置页面不缓存
<meta http-equiv=”pragma” content=”no-cache”>
<meta http-equiv=”cache-control” content=”no-cache”>
<meta http-equiv=”expires” content=”0″>

  秃头一族怎么能没有自己的 Blog 呢? 本想着自己买服务器与域名,使用 Node React 纯手撸 ,奈何钱包与时间它不允许, 所以就有了本站与这篇文章了.

相关链接

Hexo 文档
NexT 文档
参考大神博客

一.全局安装 Hexo

1
npm install -g hexo-cli

二.创建博客项目

1
2
3
4
# 使用终端进入准备存放博客项目的目录 输入以下命令
hexo init hexo-blog # hexo-blog 自己取名即可
cd hexo-blog
npm install

三.本地预览博客

1
hexo server # 使用浏览器开大 http://localhost:4000 即可

四.打包部署

1
$ hexo d -g

MySql

MySQL 是一个关系型数据库,使用 SQL 语言进行增删改查操作,目前属于 Oracle 旗下的产品。

MySQL 数据库开源免费,能够跨平台,支持分布式,性能也不错,可以和 PHP、Java 等 Web 开发语言完美配合,非常适合中小型企业作为 Web 数据库(网站数据库)

Docker 安装 MySql

进入 docker hub 可以看到 mysql 镜像

通过 docker search 的命令可以查看 mysql 的可用版本

1
docker search mysql

search mysql

拉取 MySql 镜像

通过 docker pull 命令拉取官方的最新版本镜像

1
docker pull mysql:latest

search mysql

查看本地镜像

通过 docker images 查看本地镜像, 通过 TAG 我们可以看到 mysql 最新版镜像已经安装成功

1
docker images

search mysql

运行容器

安装完成后, 我们可以通过 docker run 命令来运行 mysql 容器

通过 docker ps 查看 mysql 容器是否在运行

1
2
3
4
# -p 3306:3306: 映射容器服务的3306端口到宿主机的3306端口, 让外部可以直接通过 宿主机ip:3306 访问到 MySql 服务
# MYSQL_ROOT_PASSWORD=123456:设置 MySQL 服务 root 用户的密码。
docker run -itd --name mysql-official-website -p 3306:3306 -e MYSQL_ROOT_PASSWORD=你的密码 mysql
docker ps

search mysql

登录数据库

登录远程服务器

因为我的 mysql 安装在我的云服务器上, 所以需要先登录云服务器, 如果安装在本地可略过这一步

1
ssh root@xx.xx.xx.xx

进入 mysql 容器

我们可以通过 docker exec 命令, 进入我们在运行的 mysql 容器

通过 mysql 的命令进入 mysql 命令行

1
2
3
4
5
6
# -i:即使没有附加也保持STDIN 打开
# -t: 分配一个伪终端
# mysql-official-website: 你启动容器时候取的容器名字
docker exec -it mysql-official-website bash # 进入 mysql 容器

mysql -uroot -p # 进入 mysql 命令行 需要输入密码

search mysql

创建数据库

通过上一步, 我们已经进入了 mysql 命令行

在 mysql 命令行, 我们可以通过 CREATE DATABASE 来创建数据库

通过 show processlist; 来查看所有库

1
2
3
CREATE DATABASE `databasename` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

show processlist;

search mysql

一个 mysql 8 版本的 bug

1
2
# 报错信息
nodejs.ER_NOT_SUPPORTED_AUTH_MODEError: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client

从 MySQL8.0 开始,默认的加密规则使用的是 caching_sha2_password

进入 mysql 命令行, 依次执行如下命令

1
2
3
# 查看当前 root 用户的加密规则
mysql> use mysql;
mysql> select user, host, plugin from user
1
2
# 修改加密规则
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';

search mysql

常用 mysql 命令

- 退出 mysql 命令行: exit
- 清空 mysql 终端: system clear

Jenkins

Jenkins 是一个独立的开源软件项目,是基于 Java 开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。前身是 Hudson 是一个可扩展的持续集成引擎。可用于自动化各种任务,如构建,测试和部署软件。Jenkins 可以通过本机系统包 Docker 安装,甚至可以通过安装 Java Runtime Environment 的任何机器独立运行。

Jenkins 中文文档

安装 jenkins

本文主要主要通过 Docker 对 jenkins 进行安装. 其他安装方法可以参考 Jenkins 官方文档

打开 Docker hub 搜索 jenkins (需要登录), 点击搜索结果

jenkins

进入详情页后, 往下翻, 找到 How to use image

jenkins

根据官方提供的命令安装 jenkins

此处我加上了一些额外参数

1
2
3
4
5
6
7
8
9
10
# docker run: 指定一个新的容器并运行一个命令
# --name: 指定容器名称
# -itd:
# i: 打开STDIN,用于控制台交互
# t: 分配tty设备,该可以支持终端登录,默认为false
# d: 后台运行
# -p 11005:8080: 指定容器暴露的端口
# -p 50000:50000: 指定容器暴露的端口
# jenkins/jenkins:lts: 使用 jenkins/jenkins 的长期支持版本
docker run --name jenkins_ginger -itd -p 11005:8080 -p 50000:50000 jenkins/jenkins:lts

如图表示安装成功, 通过 docker ps 也可以查看到 jenkins 正在运行

1
docker ps

Jenkins

查看 jenkins 默认密码

通过 docker logs -f jenkins_ginger 可以查看到 jenkins 的日志, 从日志中, 可以获取到 jenkins 的默认密码

1
2
# -f: 跟踪日志输出
docker log -f jenkins_ginger

Jenkins

登录 jenkins

打开浏览器, 输入 xx.xx.xx.xx:11005 服务器公网 ip 地址加上刚刚运行时指定的 11005 端口, 就可以看到如下图所示登录页面, 输入上一步获取到的密码, 点击继续

Jenkins

点击安装推荐的插件

Jenkins

进入漫长的等待…

Jenkins

插件安装完成后, 会进入一个设置界面

Jenkins

输入账户密码, 点击继续

Jenkins

点击保存并完成

Jenkins

至此 Jenkins 就安装完成了

添加清华镜像源

jenkins 默认从国外镜像源下载镜像, 速度非常感人. 所以我们先更换源地址

点击 Manage Jenkins -> Manage Plugins -> 高级 -> 升级站点

清华镜像源地址: https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

镜像源

添加第一个构建任务

打开 jenkins 选择创建一个新任务

镜像源

输入任务名称, 选择第一项 构建一个自由风格的软件项目 点击确定

镜像源

在 jenkins 首页 点击 凭证 添加凭证 选择 ssh

在本地机器上通过命令创建秘钥

1
ssh-keygen -t rsa -b 4096 -C 'XXX@gmail.com'

通过 cat 命令获取私钥, 放入私钥至全局凭证, 取一个容易辨识的名称

1
cat  .ssh/id_rsa

镜像源

通过 cat 命令获取公钥, 配置到 gitlab 的部署秘钥项中

1
cat  .ssh/id_rsa.pub

镜像源

打开需要进行部署的 gitlab 项目仓库, 打开仓库设置, 版本库设置, Deploy Keys

镜像源

翻到下面, 找到之前配置的部署公钥, 点击 Enable 启用公钥

镜像源

点击仓库名, 复制仓库 SSH 地址. 回到 jenkins 打开第一步创建的构建任务, 选择配置

配置源码管理:

- 在源码管理菜单找到 `Git` 项
- 输入复制的仓库地址
- 选择第二步加入的凭证
- 选择需要读取的分支

镜像源

配置构建触发器:

- 选择 `Build when a change is pushed to GitLab` 项
- 点开 `高级`
- 点击最下面的 `Generate`
- 得到一个 url 与一个 token

镜像源

打开 gitlab 仓库设置 -> 集成设置:

- 配置链接为上一步从 jenkins 获取到的链接
- 配置安全令牌为上一步从 jenkins 获取到的 token
- 取消 ssl 选项
- 点击增加 web 钩子

镜像源

回到 jenkins -> 打开构建任务 -> 配置 -> 选择构建 -> 选择执行 shell -> 输入一个 shell 命令测试下

1
echo 'Hello World'

镜像源

回到 gielab, 使用 git clone xxxx 克隆仓库到本地, 添加一个文件, 提交一个 commit.

就可以看到 jenkins 首页已经收到我们的任务, 并开始自动进行构建

常用插件列表:

权限相关

Git 相关

构建相关

  • 添加构建的选择参数 Extended Choice Parameter plugin
  • 参数化构建 Build With Parameters Plugin
  • 定时周期执行任务, crontab Cron Column Plugin
  • 添加 python 支持 Python Plugin
  • 添加构建名字 Build Name Setter Plugin
  • 构建超时 Build-timeout Plugin
  • 构建环境变量注入 Build Environment
  • 构建状态:Embeddable Build Status
  • 构建名称设置:Build Name and Description Setter

通知相关

  • 发邮件 Mailer

  • 邮件模板 Email Extension Template

  • 使用绿色代替默认的蓝色表示任务运行成功的状态 Green Balls

  • 彩色输出日志 AnsiColor Plugin

  • 硬盘使用情况 Disk Usage

  • 变更查询 Job Configuration History

  • 实时的执行任务的节点名称:built-on-column

Docker

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

Docker 的应用场景

  • Web 应用的自动化打包和发布。

  • 自动化测试和持续集成、发布。

  • 在服务型环境中部署和调整数据库或其他的后台应用。

  • 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。

Docker 架构

Docker 包括三个基本概念:

  • 镜像 (Image): Docker 镜像, 就相当于是一个 root 文件系统. 比如官方镜像 ubuntu:16.04 就包含了一套 Ubuntu16.04 最小系统的 root 文件系统

  • 容器 (Container): 镜像和容器的关系, 就像面向对象程序设计中的类和实例一样, 镜像是静态的定义, 容器是镜像运行时的实体. 容器可以被创建、启动、停止、删除、暂停等

  • 仓库 (Repository): 仓库可以看做一个代码控制中心, 用来保存镜像

Docker 使用客户端-服务器 (C/S) 架构模式,使用远程 API 来管理和创建 Docker 容器。

Docker 容器通过 Docker 镜像来创建。

容器与镜像的关系类似于面向对象编程中的对象与类。

CentOS7 安装 Docker

卸载旧版本

旧的 Docker 版本称为 docker 或 docker-engine 如果已经安装过这些程序, 需要先卸载以及相关依赖

1
2
3
4
5
6
7
8
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

remove

安装 epel 更新源

1
yum install -y vim wget epel-release

remove

使用 Docker 仓库进行安装

在新主机上首次安装 Docker Engine-Community 之前, 需要设置 Docker 仓库. 之后可以从仓库安装和更新 Docker

设置仓库

安装所需的软件包, yum-utils 提供了 yum-config-manager, 并且 device mapper 存储驱动程序需要 device-mapper-persistent-data 和 lvm2

1
2
3
sudo yum install -y yum-ytils \
device-mapper-persistent-data \
lvm2

remove

鉴于国内网络问题, 执行以下命令添加 yum 软件源

1
2
3
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

remove

安装 Docker Engine-Community

安装最新版本的 Docker Engine-Community 和 containerd

1
sudo yum install -y docker-ce docker-ce-cli containerd.io

remove
remove

配置 163 镜像加速

增加配置

1
2
3
4
sudo tee /etc/docker/daemon.json <<-'EOF'
{ "registry-mirrors": ["http://hub-mirror.c.163.com"]
}
EOF

重启 docker

1
sudo systemctl restart docker

安装 docker 命令补全工具

1
yum install -y bash-completion

设置 docker 开机启动

1
systemctl enable docker

remove

docker 常用命令

更多 Dokcer 命令

docker run

创建一个新的容器并运行一个命令

语法:

docker run [OPTIONS] IMAGE [COMMAND][arg…]

OPTIONS 说明:

-a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;

-d: 后台运行容器,并返回容器 ID;

-i: 以交互模式运行容器,通常与 -t 同时使用;

-P: 随机端口映射,容器内部端口随机映射到主机的高端口

-p: 指定端口映射,格式为:主机(宿主)端口:容器端口

-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;

–name=”nginx-lb”: 为容器指定一个名称;

–dns 8.8.8.8: 指定容器使用的 DNS 服务器,默认和宿主一致;

–dns-search example.com: 指定容器 DNS 搜索域名,默认和宿主一致;

-h “mars”: 指定容器的 hostname;

-e username=”ritchie”: 设置环境变量;

–env-file=[]: 从指定文件读入环境变量;

–cpuset=”0-2” or –cpuset=”0,1,2”: 绑定容器到指定 CPU 运行;

-m :设置容器使用内存最大值;

–net=”bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;

–link=[]: 添加链接到另一个容器;

–expose=[]: 开放一个端口或一组端口;

–volume , -v: 绑定一个卷

docker start/stop/restart 命令

docker start : 启动一个或多个已经被停止的容器

docker stop : 停止一个运行中的容器

docker restart : 重启容器

语法

docker start [OPTIONS] CONTAINER [CONTAINER…]

docker stop [OPTIONS] CONTAINER [CONTAINER…]

docker restart [OPTIONS] CONTAINER [CONTAINER…]

实例

启动已被停止的容器 jenkins

1
docker start jenkins

停止运行中的容器 jenkins

1
docker stop jenkins

重启容器 jenkins

1
docker restart jenkins

Docker kill 命令

杀掉一个运行中的容器。

语法

docker kill [OPTIONS] CONTAINER [CONTAINER…]

OPTIONS 说明:

-s :向容器发送一个信号

实例

杀掉运行中的容器 jenkins

1
docker kill -s KILL jenkins

Docker rm 命令

删除一个或多个容器。

语法:

docker rm [OPTIONS] CONTAINER [CONTAINER…]

OPTIONS 说明:

-f :通过 SIGKILL 信号强制删除一个运行中的容器。

-l :移除容器间的网络连接,而非容器本身。

-v :删除与容器关联的卷。

实例:

强制删除容器 db01、db02:

1
docker rm -f db01 db02

移除容器 nginx01 对容器 db01 的连接,连接名 db:

1
docker rm -l db

删除容器 nginx01, 并删除容器挂载的数据卷:

1
docker rm -v nginx01

删除所有已经停止的容器:

1
docker rm \$(docker ps -a -q)

docker pause :暂停容器中所有的进程。

docker unpause :恢复容器中所有的进程。

语法

docker pause [OPTIONS] CONTAINER [CONTAINER…]

docker unpause [OPTIONS] CONTAINER [CONTAINER…]

实例

暂停数据库容器 db01 提供服务。

1
docker pause db01

恢复数据库容器 db01 提供服务。

1
docker unpause db01

Docker ps 命令

列出容器

语法

docker ps [OPTIONS]

OPTIONS 说明:

-a :显示所有的容器,包括未运行的。

-f :根据条件过滤显示的内容。

–format :指定返回值的模板文件。

-l :显示最近创建的容器。

-n :列出最近创建的 n 个容器。

–no-trunc :不截断输出。

-q :静默模式,只显示容器编号。

-s :显示总的文件大小。

实例

列出所有在运行的容器信息。

1
2
3
4
5
docker ps

# 输入
CONTAINER ID IMAGE COMMAND ... PORTS NAMES
09b93464c2f7 nginx:latest "nginx -g 'daemon off" ... 80/tcp, 443/tcp myrunoob

输出详情介绍:

CONTAINER ID: 容器 ID。

IMAGE: 使用的镜像。

COMMAND: 启动容器时运行的命令。

CREATED: 容器的创建时间。

STATUS: 容器状态。

状态有 7 种:

created(已创建)
restarting(重启中)
running(运行中)
removing(迁移中)
paused(暂停)
exited(停止)
dead(死亡)
PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。

NAMES: 自动分配的容器名称。

Docker logs 命令

获取容器的日志

语法

docker logs [OPTIONS] CONTAINER

OPTIONS 说明:

-f : 跟踪日志输出

–since :显示某个开始时间的所有日志

-t : 显示时间戳

–tail :仅列出最新 N 条容器日志

Docker images 命令

列出本地镜像。

语法

docker images [OPTIONS]REPOSITORY[:TAG]]

OPTIONS 说明:

-a :列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层);

–digests :显示镜像的摘要信息;

-f :显示满足条件的镜像;

–format :指定返回值的模板文件;

–no-trunc :显示完整的镜像信息;

-q :只显示镜像 ID。

Docker commit 命令

从容器创建一个新的镜像。

语法

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

OPTIONS 说明:

-a :提交的镜像作者;

-c :使用 Dockerfile 指令来创建镜像;

-m :提交时的说明文字;

-p :在 commit 时,将容器暂停。

Docker cp 命令

用于容器与主机之间的数据拷贝。

语法

docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

OPTIONS 说明:

-L :保持源目标中的链接

Docker rmi 命令

删除本地一个或多少镜像。

语法

docker rmi [OPTIONS] IMAGE [IMAGE…]

OPTIONS 说明:

-f :强制删除;

–no-prune :不移除该镜像的过程镜像,默认移除;

Docker exec 命令

在运行的容器中执行命令

语法

docker exec [OPTIONS] CONTAINER COMMAND [ARG…]

OPTIONS 说明:

-d :分离模式: 在后台运行

-i :即使没有附加也保持 STDIN 打开

-t :分配一个伪终端

从 JS 基本语法去理解 React

实现创建元素并且加入到页面进行渲染

第一版
实现功能:

  • 1、创建节点元素
  • 2、元素嵌套
  • 3、元素渲染到页面
1
2
3
4
5
6
7
const div = document.createElement('div')
const p = document.createElement('p')
const span = document.createElement('span')
div.appendChild(p)
p.appendChild(span)
span.innerText = '我是一个span'
document.body.appendChild(div)

第二版 代码优化:

  • 1、将重复的 document.createElement() 提取成一个函数方法
  • 2、方法接受一个 tagName 返回创建好的元素
1
2
3
4
5
6
7
8
9
10
function createElement(tagName) {
return document.createElement(tagName)
}
const div = createElement('div')
const p = createElement('p')
const span = createElement('span')
div.appendChild(p)
p.appendChild(span)
span.innerText = '我是一个 span'
document.body.appendChild(div)

第三版 继续优化:

  • 1、方法接受第二个参数 参数值可以是一个元素节点 也可以是元素节点需要渲染的内容
  • 2、将函数调用提取到一个函数方法中 返回一个组装好了子元素或内容的的元素节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function createElement(tagName, childer) {
let parent = document.createElement(tagName)
if (childer) {
if (typeof childer === 'string') {
let child = document.createTextNode(childer)
parent.appendChild(child)
} else {
parent.appendChild(childer)
}
}
return parent
}

function render() {
return createElement(
'div',
createElement('p', createElement('span', '我是一个 span'))
)
}
document.getElementById('root').appendChild(render())

React 思想推导

使用 React 实现 JS 模拟实现的最终版

1
2
import React, { useState } from 'react'
import ReactDom from 'react-dom'

第一版 :暂时不考虑 render 内部实现原理

  • 通过 React.createElement 创建 节点对象
  • 通过 ReactDom.render 渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
function Node() {
return React.createElement(
'div',
null,
React.createElement(
'p',
null,
React.createElement('span', null, '我是 React 实现节点创建')
)
)
}

ReactDom.render(<Node />, document.getElementById('root'))

第二版: 加入 JSX

  • 每次要写 React.createElement 实在太繁琐了
  • 于是 React 提供了 JSX 语法
1
2
3
4
5
6
7
8
9
const Node = (
<div>
<p>
<span>我是 React JSX 创建的节点</span>
</p>
</div>
)

ReactDom.render(<Node />, document.getElementById('root'))

React 组件

React 组件提供了 class 类写法与函数式组件写法

函数式组件

第一版:

  • 事件无法传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App() {
const [n, setN] = useState(1)
function onClick() {
setN(n + 1)
}
return (
<div>
<p>
<span>这是 React 函数式组件 {n} </span>
<button onClick={onClick}>+1</button>
</p>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))

函数式组件

第二版:

  • 利用闭包实现事件传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App() {
const [n, setN] = useState(1)
function onClick(step) {
setN(n + step)
}
return (
<div>
<p>
<span>这是 React 函数式组件 {n} </span>
<button onClick={() => onClick(2)}>+1</button>
</p>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))

class 类 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class App extends React.Component {
constructor(props) {
super(props)
this.state = { count: props.count }
}
render() {
return (
<div>
<p>
<span>我是 React class 类 创建的组件 {this.state.count}</span>
<button onClick={() => console.log(this)}>this</button>
</p>
</div>
)
}
}

ReactDom.render(<App count={1} />, document.getElementById('root'))

plugins

插件是 webpack 的支柱功能. 插件的目的在于解决 loader 无法实现的其他事

剖析

webpack 插件是一个具有 apply 属性的 JavaScript 对象. apply 属性会被 webpack compiler 调用, 并且 compiler 对象可在整个编译生命周期访问

1
2
3
4
5
6
7
8
9
const pluginName = 'ConsoleLogOnBuildWebpackPlugin'

class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, (compilation) => {
console.log('webpack 构建过程开始!')
})
}
}

compiler hook 的 tap 方法的第一个参数,应该是驼峰式命名的插件名称。建议为此使用一个常量,以便它可以在所有 hook 中复用。

用法

由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins 属性传入 new 实例。

根据你的 webpack 用法,这里有多种方式使用插件。

配置

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
const HtmlWebpackPlugin = require('html-webpack-plugin') //通过 npm 安装
const webpack = require('webpack') //访问内置的插件
const path = require('path')

const config = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
},
],
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({ template: './src/index.html' }),
],
}

module.exports = config

Node API

1
2
3
4
5
6
7
8
9
const webpack = require('webpack') //访问 webpack 运行时(runtime)
const configuration = require('./webpack.config.js')

let compiler = webpack(configuration)
compiler.apply(new webpack.ProgressPlugin())

compiler.run(function (err, stats) {
// ...
})