Fork me on GitHub
艺术码畜的生活瞬间

Docker部署Drone

Docker部署Drone

一、新建 GitHub 应用

具体顺序Settings->Developer settings->OAuth Apps->New OAuth App

image-20221113155447805

新建完成后记住Client ID和Client secrets,后面配置需要

二、下载drone

docker pull drone/drone //下载drone

三、配置 Drone

通过使用 Docker Compose 来启动 Drone,编写 docker-compose.yml 文件(这个文件直接新建就可以了)

touch docker-compose.yml //新建文件
vim docker-compose.yml  //配置文件
//docker-compose.yml 文件不用修改
version: '3'

services:

  drone-server:
    image: drone/drone:2.3.1
    ports:
      - 443:443
      - 80:80
    volumes:
      - drone-data:/data:rw
      - ./ssl:/etc/certs
    restart: always
    environment:
      - DRONE_SERVER_HOST=${DRONE_SERVER_HOST:-https://drone.yeasy.com}
      - DRONE_SERVER_PROTO=${DRONE_SERVER_PROTO:-https}
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET:-secret}
      - DRONE_GITHUB_SERVER=https://github.com
      - DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT_ID}
      - DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_CLIENT_SECRET}

  drone-agent:
    image: drone/drone-runner-docker:1
    restart: always
    depends_on:
      - drone-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:rw
    environment:
      - DRONE_RPC_PROTO=http
      - DRONE_RPC_HOST=drone-server
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET:-secret}
      - DRONE_RUNNER_NAME=${HOSTNAME:-demo}
      - DRONE_RUNNER_CAPACITY=2
    dns: 114.114.114.114

volumes:
  drone-data:

新建 .env 文件,输入变量及其值(上面docker-compose.yml文件配置会默认读取 .env 文件变量)

touch .env //新建文件
//.env
# 必填 服务器地址,例如 drone.domain.com
DRONE_SERVER_HOST=
DRONE_SERVER_PROTO=https
DRONE_RPC_SECRET=secret
HOSTNAME=demo
# 必填 在 GitHub 应用页面查看
DRONE_GITHUB_CLIENT_ID=
# 必填 在 GitHub 应用页面查看
DRONE_GITHUB_CLIENT_SECRET=
#我的配置(参考)
# 必填 服务器地址,例如 drone.domain.com
DRONE_SERVER_HOST=localhost
DRONE_SERVER_PROTO=http
DRONE_RPC_SECRET=secret
HOSTNAME=demo
# 必填 在 GitHub 应用页面查看
DRONE_GITHUB_CLIENT_ID=
# 必填 在 GitHub 应用页面查看
DRONE_GITHUB_CLIENT_SECRET=

四、启动Drone

docker-compose up -d

访问http:localhost:80(我配置的,你和我配置一样就可以打开)就成功了

等到验证完成就可以了(如果出现错误,关机重启,重新配置就可以了)

五、激活仓库(具体看下一篇)

搭建私有的npm仓库

使用docker和verdaccio搭建npm私有库

本文章docker环境为macos

1.使用 docker 镜像安装verdaccio

docker pull verdaccio/verdaccio //安装verdaccio镜像

2.启动端口映射verdaccio容器

docker run -it --name verdaccio -p 4873:4873 verdaccio/verdaccio //运行 verdaccio,使用 docker 命令运行镜像,创建 verdaccio 容器 ,端口4873

3.私有部署npm仓库成功

访问http://localhost:4873/

4.添加权限用户

npm adduser --registry http://localhost:4873/ //根据提示添加用户

5.登陆

6.发布npm包

当我们的私有仓库搭建好之后,那么我们就可以向仓库发布npm包了,到我们npm包目录下上传就可以了

1.创建npm-me包目录
2.npm init 初始化目录
3.编写功能文件
4.如果是改版发布,更新版本号
5.登陆私有仓库
6.向私有仓库发布npm包
//ifYes.js
exports.ifYes = function() {
    return "If you're late, your wages will be docked version new(registry)"
}
//package.json
{
  "name": "alan-npm-me",
  "version": "1.0.3",
  "description": "If you're late, your wages will be docked",
  "main": "ifYes.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "ifYes"
  ],
  "author": "alan_mf",
  "license": "ISC"
}
npm version patch  //补丁版本更新

npm-me % npm publish --registry http://localhost:4873/ //发布

这时候刷新我们的私有仓库页面就可以看到了http://localhost:4873/

7.下载私有仓库的包

1.创建一个文件目录
2.初始化 npm init -y
3.编辑测试代码
4. npm install --registry http://localhost:4873 alan-npm-me //下载私有仓库npm包
5.执行测试代码

8.修改verdaccio权限

因为我是docker安装的verdaccio,所以查找配置文件路径是这样的

1. docker exec --user root -it verdaccio sh
2. cd /verdaccio
3. cd conf
4. vi config.yaml

里面就可以配置你们的文件了,重启容器就生效了

什么是CI/CD

CI/CD是什么?如何理解持续集成、持续交付和持续部署

概述

CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。作为一个面向开发和运营团队的解决方案,CI/CD 主要针对在集成新代码时所引发的问题(亦称:”集成地狱”)。

具体而言,CI/CD 可让持续自动化和持续监控贯穿于应用的整个生命周期(从集成和测试阶段,到交付和部署)。这些关联的事务通常被统称为”CI/CD 管道”,由开发和运维团队以敏捷方式协同支持。

CI 是什么?CI 和 CD 有什么区别?

缩略词 CI / CD 具有几个不同的含义。CI/CD 中的”CI”始终指持续集成,它属于开发人员的自动化流程。成功的 CI 意味着应用代码的新更改会定期构建、测试并合并到共享存储库中。该解决方案可以解决在一次开发中有太多应用分支,从而导致相互冲突的问题。

CI/CD 中的”CD”指的是持续交付和/或持续部署,这些相关概念有时会交叉使用。两者都事关管道后续阶段的自动化,但它们有时也会单独使用,用于说明自动化程度。

持续交付通常是指开发人员对应用的更改会自动进行错误测试并上传到存储库(如 GitHub或容器注册表),然后由运维团队将其部署到实时生产环境中。这旨在解决开发和运维团队之间可见性及沟通较差的问题。因此,持续交付的目的就是确保尽可能减少部署新代码时所需的工作量。

持续部署(另一种”CD”)指的是自动将开发人员的更改从存储库发布到生产环境,以供客户使用。它主要为了解决因手动流程降低应用交付速度,从而使运维团队超负荷的问题。持续部署以持续交付的优势为根基,实现了管道后续阶段的自动化。

CI/CD flow

CI/CD 既可能仅指持续集成和持续交付构成的关联环节,也可以指持续集成、持续交付和持续部署这三项构成的关联环节。更为复杂的是,有时”持续交付”也包含了持续部署流程。

归根结底,我们没必要纠结于这些语义,您只需记得 CI/CD 其实就是一个流程(通常形象地表述为管道),用于实现应用开发中的高度持续自动化和持续监控。因案例而异,该术语的具体含义取决于 CI/CD 管道的自动化程度。许多企业最开始先添加 CI,然后逐步实现交付和部署的自动化(例如作为云原生应用的一部分)。

CI 持续集成(Continuous Integration)

现代应用开发的目标是让多位开发人员同时处理同一应用的不同功能。但是,如果企业安排在一天内将所有分支源代码合并在一起(称为”合并日”),最终可能造成工作繁琐、耗时,而且需要手动完成。这是因为当一位独立工作的开发人员对应用进行更改时,有可能会与其他开发人员同时进行的更改发生冲突。如果每个开发人员都自定义自己的本地集成开发环境(IDE),而不是让团队就一个基于云的 IDE 达成一致,那么就会让问题更加雪上加霜。

持续集成(CI)可以帮助开发人员更加频繁地(有时甚至每天)将代码更改合并到共享分支或”主干”中。一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。这意味着测试内容涵盖了从类和函数到构成整个应用的不同模块。如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误。

CD 持续交付(Continuous Delivery)

完成 CI 中构建及单元测试和集成测试的自动化流程后,持续交付可自动将已验证的代码发布到存储库。为了实现高效的持续交付流程,务必要确保 CI 已内置于开发管道。持续交付的目标是拥有一个可随时部署到生产环境的代码库。

在持续交付中,每个阶段(从代码更改的合并,到生产就绪型构建版本的交付)都涉及测试自动化和代码发布自动化。在流程结束时,运维团队可以快速、轻松地将应用部署到生产环境中。

CD 持续部署(Continuous Deployment)

对于一个成熟的 CI/CD 管道来说,最后的阶段是持续部署。作为持续交付——自动将生产就绪型构建版本发布到代码存储库——的延伸,持续部署可以自动将应用发布到生产环境。由于在生产之前的管道阶段没有手动门控,因此持续部署在很大程度上都得依赖精心设计的测试自动化。

实际上,持续部署意味着开发人员对应用的更改在编写后的几分钟内就能生效(假设它通过了自动化测试)。这更加便于持续接收和整合用户反馈。总而言之,所有这些 CI/CD 的关联步骤都有助于降低应用的部署风险,因此更便于以小件的方式(而非一次性)发布对应用的更改。不过,由于还需要编写自动化测试以适应 CI/CD 管道中的各种测试和发布阶段,因此前期投资还是会很大。

docker入门

docker入门

一、docker安装

目前我们只是基于macos环境下的安装和学习

(很多平台都是windows的)

1.docker下载

直接去Docker官网去下载对应的版本就可以了(官网地址:https://dockerdocs.cn/)

选择对应inter芯片或者m1和m2芯片的docker版本就可以

2.设置国内镜像源

在国内使用docker拉取镜像会非常的慢,所以一定得设置国内镜像源。

设置镜像源在设置里的Docker Engine

阿里云加速器:点击管理控制台->登陆账号(淘宝账号)->右侧镜像工具->镜像加速器->拷贝镜像链接

网易云加速器:https://hub-mirror.c.163.com

百度云加速器:https://mirror.baidubce.com

配置完成后,查看是否配置成功

docker info 

如果 docker versiondocker info 都正常的话,可以尝试运行一个 Nginx 服务器

docker run -d -p 80:80 --name webserver nginx

服务运行后,可以访问 http://localhost,如果看到了 “Welcome to nginx!”,就说明 Docker Desktop for Mac 安装成功了。

docker ps -a //查看所有的容器

docker stop webserver //停止容器服务
docker rm webserver //删除容器

二、docker镜像

1.获取镜像

Docker Hub 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像。

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] //获取镜像命令格式
docker pull ubuntu:18.04 //具体一点

查看下载镜像

docker images -a //查看所有镜像

有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 ubuntu:18.04 为例,如果我们打算启动里面的 bash 并且进行交互式操作的话,可以执行下面的命令。

docker run -it --rm ubuntu:18.04 bash

docker run 就是运行容器的命令,具体格式我们会在容器 一节进行详细讲解,我们这里简要的说明一下上面用到的参数。

  • -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
  • --rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
  • ubuntu:18.04:这是指用 ubuntu:18.04 镜像为基础来启动容器。
  • bash:放在镜像名后的是 命令,这里我们希望有个交互式 Shell,因此用的是 bash

进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 cat /etc/os-release,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 Ubuntu 18.04.1 LTS 系统。

最后我们通过 exit 退出了这个容器。

2.列出镜像

docker image ls //列出所有下载的镜像

列表包含了 仓库名标签镜像 ID创建时间 以及 所占用的空间

其中仓库名、标签在之前的基础概念章节已经介绍过了。镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个 标签。因此,在上面的例子中,我们可以看到 ubuntu:18.04ubuntu:bionic 拥有相同的 ID,因为它们对应的是同一个镜像。

docker image ls 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。

docker system df //便捷的查看镜像、容器、数据卷所占用的空间

image-20221109104851278

docker image ls ubuntu //列出部分镜像
docker image ls ubuntu:18.04 //列出特定的某个镜像,也就是说指定仓库名和标签

docker image ls还支持强大的过滤器参数–filter,或者简写 -f,之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。比如,我们希望看到在 nginx 之后建立的镜像,可以用下面的命令:

docker image ls -f since=nginx

想查看某个位置之前的镜像也可以,只需要把 since 换成 before 即可

docker image ls -q //以特定格式显示

docker image ls --format "{{.ID}}: {{.Repository}}" //直接列出镜像结果,并且只包含镜像ID和仓库名

docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" //以表格等距显示,并且有标题行,和默认一样,不过自己定义列

3.删除镜像

docker image rm [选项] <镜像1> [<镜像2> ...] //删除镜像命令格式

其中,<镜像> 可以是 镜像短 ID镜像长 ID镜像名 或者 镜像摘要

我们先列出我们下载的所有镜像

docker image rm afb //删除redis镜像
docker image ls //查看所有镜像

发现已经删除了指定镜像

docker image rm $(docker image ls -q redis) //组合命令使用

充分利用你的想象力和 Linux 命令行的强大

三、docker容器

1.启动容器

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(exited)的容器重新启动。

docker run ubuntu:18.04 /bin/echo 'Hello world' //执行后命令后终止容器
docker run -t -i ubuntu:18.04 /bin/bash //保持交互

-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。

Docker run创建容器时,docker的标准操作

  • 检查本地是否存在指定的镜像,不存在就从 registry下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止
docker start 9a115164bf87//启动一个终止(exited)的容器
docker ps -a//查看所有容器信息
docker logs 9a115164bf87//获取容器输出信息

2.终止容器

docker stop (ID) //终止容器
docker ps //查看启动容器

3.进入容器

在使用 -d 参数时,容器启动后会进入后台

docker attach 命令或 docker exec 命令都可以进入容器,推荐大家使用 docker exec 命令

docker attach (ID) //会导致容器的终止

docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。

只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。

-i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符

docker exec -i 4e56f70383c9 /bin/bash 

docker exec -it 4e56f70383c9 /bin/bash //不会导致容器的终止

docker exec --help //查看更多参数

4.导入和导出

docker export 4e56f70383c9 > ubuntu.tar //导出容器快照到本地文件

cat nginx.tar | docker import - test/nginx //从容器快照文件中再导入为镜像

用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

5.删除容器

docker rm (ID or Name) //删除一个处于终止状态的容器

删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器

sudo docker rm $(sudo docker ps -a -q) //删除所有未运行的容器(已经运行的删除不了,未运行的就一起被删除了)

四、访问仓库

1.Docker Hub

Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了数量超过 2,650,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。

注册一个docker账号

https://hub.docker.com 免费注册一个 Docker 账号

登陆

1.可以在docker App上登陆dockr账号(会跳转到 https://hub.docker.com

2.通过执行 docker login 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。

你可以通过 docker logout 退出登录。

拉取镜像

可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地

推送镜像

用户也可以在登录后通过 docker push 命令来将自己的镜像推送到 Docker Hub

docker tag ubuntu:18.04 username/ubuntu:18.04 //username 请替换为你的 Docker 账号用户名

自动化构建

自动构建(Automated Builds)可以自动触发构建镜像,方便升级镜像。

有时候,用户构建了镜像,安装了某个软件,当软件发布新版本则需要手动更新镜像。

而自动构建允许用户通过 Docker Hub 指定跟踪一个目标网站(支持 GitHubBitBucket)上的项目,一旦项目发生新的提交 (commit)或者创建了新的标签(tag),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。

要配置自动构建,包括如下的步骤:

-

登录 Docker Hub;

-

在 Docker Hub 点击右上角头像,在账号设置(Account Settings)中关联(Linked Accounts)目标网站;

-

在 Docker Hub 中新建或选择已有的仓库,在 Builds 选项卡中选择 Configure Automated Builds

-

选取一个目标网站中的项目(需要含 Dockerfile)和分支;

-

指定 Dockerfile 的位置,并保存。

之后,可以在 Docker Hub 的仓库页面的 Timeline 选项卡中查看每次构建的状态。

(‼️这个需要付钱)

2.私有仓库

安装运行 docker-registry

docker run -d -p 5000:5000 --restart=always --name registry registry //使用官方 registry 镜像来运行

如果报错端口不可用

docker run -d -p 8080:8080 --restart=always --name registry registry //删除原来容器,重新创建

在私有仓库上传、搜索、下载镜像

docker tag ubuntu:latest alan0924/ubuntu:latest //将 ubuntu:latest 这个镜像标记为 alan0924/ubuntu:latest
docker image ls //查看所有镜像
docker push alan0924/ubuntu:latest //上传私有仓库

删除已有镜像,再尝试从私有仓库中下载这个镜像

docker image rm alan0924/ubuntu:latest
docker image ls
docker pull  alan0924/ubuntu:latest
docker image ls 

这是我们docker的私有仓库

五、数据管理

1.数据卷

docker volume create my-vol //创建一个数据卷
docker volume ls //查看所有的数据卷
docker volume inspect my-vol //主机里使用以下命令可以查看指定 数据卷 的信息

在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。在一次 docker run 中可以挂载多个 数据卷

下面创建一个名为 web 的容器,并加载一个 数据卷 到容器的 /usr/share/nginx/html 目录。

//启动一个挂载数据卷的容器
docker run -d -P \
    --name web \
    --mount source=my-vol,target=/usr/share/nginx/html \
    nginx:alpine

docker inspect web //查看数据卷的具体信息

docker volume rm my-vol //删除数据卷

数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令

无主的数据卷可能会占据很多空间,要清理请使用以下命令

docker volume prune

六、访问网络

1.外部访问容器

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P-p 参数来指定端口映射。

当使用 -P 标记时,Docker 会随机映射一个端口到内部容器开放的网络端口。

使用 docker ps 可以看到,本地主机的 55000 被映射到了容器的 80 端口。此时访问本机的 55000 端口即可访问容器内 NGINX 默认页面。

docker run -d -P nginx:alpine // 
docker logs 75bbd0b01745 //查看日志

-p 则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。

支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort

使用 hostPort:containerPort 格式本地的 80 端口映射到容器的 80 端口,可以执行

docker run -d -p 80:80 nginx:alpine //映射所有接口地址,此时默认会绑定本地所有接口上的所有地址。

可以使用 ip:hostPort:containerPort 格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1

docker run -d -p 127.0.0.1:80:80 nginx:alpine //映射到指定地址的指定端口,

使用 ip::containerPort 绑定 localhost 的任意端口到容器的 80 端口,本地主机会自动分配一个端口。

docker run -d -p 127.0.0.1::80 nginx:alpine //映射到指定地址的任意端口

还可以使用 udp 标记来指定 udp 端口

docker run -d -p 127.0.0.1:80:80/udp nginx:alpine 

使用 docker port 来查看当前映射的端口配置,也可以查看到绑定的地址

docker port 75b 80 //查看映射端口配置

  • 容器有自己的内部网络和 ip 地址(使用 docker inspect 查看,Docker 还可以有一个可变的网络配置。)
  • -p 标记可以多次使用来绑定多个端口
docker run -d \
    -p 80:80 \
    -p 443:443 \
    nginx:alpine

2.容器互联

新建网络

docker network create -d bridge my-net //创建一个新的 Docker 网络

连接容器

docker run -it --rm --name busybox1 --network my-net busybox sh //运行一个容器并连接到新建的 my-net 网络
docker run -it --rm --name busybox2 --network my-net busybox sh //打开新的终端,再运行一个容器并加入到 my-net 网络
docker ps //再打开一个新的终端查看容器信息

下面通过 ping 来证明 busybox1 容器和 busybox2 容器建立了互联关系。

/ # ping busybox2
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms

用 ping 来测试连接 busybox2 容器,它会解析成 172.19.0.3

同理在 busybox2 容器执行 ping busybox1,也会成功连接到

/ # ping busybox1
PING busybox1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.064 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.143 ms

busybox1 容器和 busybox2 容器建立了互联关系。

七、Docker Compose

1.安装

Docker Desktop for Mac/Windows 自带 docker-compose 二进制文件,安装 Docker 之后可以直接使用

docker-compose --version

2.使用

  • 服务 (service):一个应用容器,实际上可以运行多个相同镜像的实例。
  • 项目 (project):由一组关联的应用容器组成的一个完整业务单元。

可见,一个项目可以由多个服务(容器)关联而成,Compose 面向项目进行管理。

最常见的项目是 web 网站,该项目应该包含 web 应用和缓存。

下面我们用 Python 来建立一个能够记录页面访问次数的 web 网站。

//app.py
from flask import Flask
from redis import Redis

app = Flask(__name__)
redis = Redis(host='redis', port=6379)

@app.route('/')
def hello():
    count = redis.incr('hits')
    return 'Hello World! 该页面已被访问 {} 次。\n'.format(count)

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)
//docker-compose.yml
version: '3'
services:

  web:
    build: .
    ports:
     - "5500:5500"

  redis:
    image: "redis:alpine"
//Dockerfile
FROM python:3.6-alpine
ADD . /code
WORKDIR /code
RUN pip install redis flask
CMD ["python", "app.py"]

docker-compose up //运行 compose 项目

此时访问本地 5000 端口,每次刷新页面,计数就会加 1。

3.命令说明

命令对象与格式

对于 Compose 来说,大部分命令的对象既可以是项目本身,也可以指定为项目中的服务或者容器。如果没有特别的说明,命令对象将是项目,这意味着项目中所有的服务都会受到命令影响。

执行 docker-compose [COMMAND] --help 或者 docker-compose help [COMMAND] 可以查看具体某个命令的使用格式。

docker-compose 命令的基本的使用格式是

docker-compose [-f=…] [options] [COMMAND] [ARGS…]

命令选项

-

-f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定。

-

-p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。

-

--verbose 输出更多调试信息。

-

-v, --version 打印版本并退出。

命令使用说明

build

格式为 docker-compose build [options] [SERVICE...]

构建(重新构建)项目中的服务容器。

服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,可能是 web_db。

可以随时在项目目录下运行 docker-compose build 来重新构建服务。

选项包括:

  • --force-rm 删除构建过程中的临时容器。
  • --no-cache 构建镜像过程中不使用 cache(这将加长构建过程)。
  • --pull 始终尝试通过 pull 来获取更新版本的镜像。

config

验证 Compose 文件格式是否正确,若正确则显示配置,若格式错误显示错误原因。

down

此命令将会停止 up 命令所启动的容器,并移除网络

exec

进入指定的容器。

help

获得一个命令的帮助。

images

列出 Compose 文件中包含的镜像。

kill

格式为 docker-compose kill [options] [SERVICE...]

通过发送 SIGKILL 信号来强制停止服务容器。

支持通过 -s 参数来指定发送的信号,例如通过如下指令发送 SIGINT 信号。

docker-compose kill -s SIGINT

logs

格式为 docker-compose logs [options] [SERVICE...]

查看服务容器的输出。默认情况下,docker-compose 将对不同的服务输出使用不同的颜色来区分。可以通过 --no-color 来关闭颜色。

该命令在调试问题的时候十分有用。

pause

格式为 docker-compose pause [SERVICE...]

暂停一个服务容器。

port

格式为 docker-compose port [options] SERVICE PRIVATE_PORT

打印某个容器端口所映射的公共端口。

选项:

  • --protocol=proto 指定端口协议,tcp(默认值)或者 udp。
  • --index=index 如果同一服务存在多个容器,指定命令对象容器的序号(默认为 1)。

ps

格式为 docker-compose ps [options] [SERVICE...]

列出项目中目前的所有容器。

选项:

  • -q 只打印容器的 ID 信息。

pull

格式为 docker-compose pull [options] [SERVICE...]

拉取服务依赖的镜像。

选项:

  • --ignore-pull-failures 忽略拉取镜像过程中的错误。

push

推送服务依赖的镜像到 Docker 镜像仓库。

restart

格式为 docker-compose restart [options] [SERVICE...]

重启项目中的服务。

选项:

  • -t, --timeout TIMEOUT 指定重启前停止容器的超时(默认为 10 秒)。

rm

格式为 docker-compose rm [options] [SERVICE...]

删除所有(停止状态的)服务容器。推荐先执行 docker-compose stop 命令来停止容器。

选项:

  • -f, --force 强制直接删除,包括非停止状态的容器。一般尽量不要使用该选项。
  • -v 删除容器所挂载的数据卷。

run

格式为 docker-compose run [options] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

在指定服务上执行一个命令。

例如:

docker-compose run ubuntu ping docker.com

将会启动一个 ubuntu 服务容器,并执行 ping docker.com 命令。

默认情况下,如果存在关联,则所有关联的服务将会自动被启动,除非这些服务已经在运行中。

该命令类似启动容器后运行指定的命令,相关卷、链接等等都将会按照配置自动创建。

两个不同点:

  • 给定命令将会覆盖原有的自动运行命令;
  • 不会自动创建端口,以避免冲突。

如果不希望自动启动关联的容器,可以使用 --no-deps 选项,例如

docker-compose run --no-deps web python manage.py shell

将不会启动 web 容器所关联的其它容器。

选项:

  • -d 后台运行容器。
  • --name NAME 为容器指定一个名字。
  • --entrypoint CMD 覆盖默认的容器启动指令。
  • -e KEY=VAL 设置环境变量值,可多次使用选项来设置多个环境变量。
  • -u, --user="" 指定运行容器的用户名或者 uid。
  • --no-deps 不自动启动关联的服务容器。
  • --rm 运行命令后自动删除容器,d 模式下将忽略。
  • -p, --publish=[] 映射容器端口到本地主机。
  • --service-ports 配置服务端口并映射到本地主机。
  • -T 不分配伪 tty,意味着依赖 tty 的指令将无法运行。

scale

格式为 docker-compose scale [options] [SERVICE=NUM...]

设置指定服务运行的容器个数。

通过 service=num 的参数来设置数量。例如:

docker-compose scale web=3 db=2

将启动 3 个容器运行 web 服务,2 个容器运行 db 服务。

一般的,当指定数目多于该服务当前实际运行容器,将新创建并启动容器;反之,将停止容器。

选项:

  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

start

格式为 docker-compose start [SERVICE...]

启动已经存在的服务容器。

stop

格式为 docker-compose stop [options] [SERVICE...]

停止已经处于运行状态的容器,但不删除它。通过 docker-compose start 可以再次启动这些容器。

选项:

  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

top

查看各个服务容器内运行的进程。

unpause

格式为 docker-compose unpause [SERVICE...]

恢复处于暂停状态中的服务。

up

格式为 docker-compose up [options] [SERVICE...]

该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

链接的服务都将会被自动启动,除非已经处于运行状态。

可以说,大部分时候都可以直接通过该命令来启动一个项目。

默认情况,docker-compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。

当通过 Ctrl-C 停止命令时,所有容器将会停止。

如果使用 docker-compose up -d,将会在后台启动并运行所有的容器。一般推荐生产环境下使用该选项。

默认情况,如果服务容器已经存在,docker-compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容。如果用户不希望容器被停止并重新创建,可以使用 docker-compose up --no-recreate。这样将只会启动处于停止状态的容器,而忽略已经运行的服务。如果用户只想重新部署某个服务,可以使用 docker-compose up --no-deps -d <SERVICE_NAME> 来重新创建服务并后台停止旧服务,启动新服务,并不会影响到其所依赖的服务。

选项:

  • -d 在后台运行服务容器。
  • --no-color 不使用颜色来区分不同的服务的控制台输出。
  • --no-deps 不启动服务所链接的容器。
  • --force-recreate 强制重新创建容器,不能与 --no-recreate 同时使用。
  • --no-recreate 如果容器已经存在了,则不重新创建,不能与 --force-recreate 同时使用。
  • --no-build 不自动构建缺失的服务镜像。
  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

version

格式为 docker-compose version

打印版本信息。

表白爱心biu

爱心代码(canvas)

每天浪漫一点点

发给你女朋友,有奖励🥳

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>玥小公主爱心</title>

    <style>
      html,
      body {
        height: 100%;
        padding: 0;
        margin: 0;
        background: #000;
      }
      canvas {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <canvas id="pinkboard"></canvas>
    <script>
      /*
       * 参数配置
       */
      var settings = {
        particles: {
          length: 500, // 最大颗粒量
          duration: 2, // 粒子持续时间(秒)
          velocity: 110, // 粒子范围(像素/秒)
          effect: -1.0, // 内轮廓
          size: 35, // 颗粒大小(像素)
        },
      };

      /*
       * Point class
       */
      var Point = (function () {
        function Point(x, y) {
          this.x = typeof x !== "undefined" ? x : 0;
          this.y = typeof y !== "undefined" ? y : 0;
        }
        Point.prototype.clone = function () {
          return new Point(this.x, this.y);
        };
        Point.prototype.length = function (length) {
          if (typeof length == "undefined")
            return Math.sqrt(this.x * this.x + this.y * this.y);
          this.normalize();
          this.x *= length;
          this.y *= length;
          return this;
        };
        Point.prototype.normalize = function () {
          var length = this.length();
          this.x /= length;
          this.y /= length;
          return this;
        };
        return Point;
      })();

      /*
       * Particle class
       */
      var Particle = (function () {
        function Particle() {
          this.position = new Point();
          this.velocity = new Point();
          this.acceleration = new Point();
          this.age = 0;
        }
        Particle.prototype.initialize = function (x, y, dx, dy) {
          this.position.x = x;
          this.position.y = y;
          this.velocity.x = dx;
          this.velocity.y = dy;
          this.acceleration.x = dx * settings.particles.effect;
          this.acceleration.y = dy * settings.particles.effect;
          this.age = 0;
        };
        Particle.prototype.update = function (deltaTime) {
          this.position.x += this.velocity.x * deltaTime;
          this.position.y += this.velocity.y * deltaTime;
          this.velocity.x += this.acceleration.x * deltaTime;
          this.velocity.y += this.acceleration.y * deltaTime;
          this.age += deltaTime;
        };
        Particle.prototype.draw = function (context, image) {
          function ease(t) {
            return --t * t * t + 1;
          }
          var size = image.width * ease(this.age / settings.particles.duration);
          context.globalAlpha = 1 - this.age / settings.particles.duration;
          context.drawImage(
            image,
            this.position.x - size / 2,
            this.position.y - size / 2,
            size,
            size
          );
        };
        return Particle;
      })();

      /*
       * ParticlePool class
       */
      var ParticlePool = (function () {
        var particles,
          firstActive = 0,
          firstFree = 0,
          duration = settings.particles.duration;

        function ParticlePool(length) {
          // create and populate particle pool
          particles = new Array(length);
          for (var i = 0; i < particles.length; i++)
            particles[i] = new Particle();
        }
        ParticlePool.prototype.add = function (x, y, dx, dy) {
          particles[firstFree].initialize(x, y, dx, dy);

          // handle circular queue
          firstFree++;
          if (firstFree == particles.length) firstFree = 0;
          if (firstActive == firstFree) firstActive++;
          if (firstActive == particles.length) firstActive = 0;
        };
        ParticlePool.prototype.update = function (deltaTime) {
          var i;

          // update active particles
          if (firstActive < firstFree) {
            for (i = firstActive; i < firstFree; i++)
              particles[i].update(deltaTime);
          }
          if (firstFree < firstActive) {
            for (i = firstActive; i < particles.length; i++)
              particles[i].update(deltaTime);
            for (i = 0; i < firstFree; i++) particles[i].update(deltaTime);
          }

          // remove inactive particles
          while (
            particles[firstActive].age >= duration &&
            firstActive != firstFree
          ) {
            firstActive++;
            if (firstActive == particles.length) firstActive = 0;
          }
        };
        ParticlePool.prototype.draw = function (context, image) {
          // draw active particles
          if (firstActive < firstFree) {
            for (i = firstActive; i < firstFree; i++)
              particles[i].draw(context, image);
          }
          if (firstFree < firstActive) {
            for (i = firstActive; i < particles.length; i++)
              particles[i].draw(context, image);
            for (i = 0; i < firstFree; i++) particles[i].draw(context, image);
          }
        };
        return ParticlePool;
      })();

      /*
       * Putting it all together
       */
      (function (canvas) {
        var context = canvas.getContext("2d"),
          particles = new ParticlePool(settings.particles.length),
          particleRate =
            settings.particles.length / settings.particles.duration, // particles/sec
          time;

        // get point on heart with -PI <= t <= PI
        function pointOnHeart(t) {
          return new Point(
            160 * Math.pow(Math.sin(t), 3),
            130 * Math.cos(t) -
              50 * Math.cos(2 * t) -
              20 * Math.cos(3 * t) -
              10 * Math.cos(4 * t) +
              25
          );
        }

        // 使用虚拟画布创建粒子图像
        var image = (function () {
          var canvas = document.createElement("canvas"),
            context = canvas.getContext("2d");
          canvas.width = settings.particles.size;
          canvas.height = settings.particles.size;
          // helper function to create the path
          function to(t) {
            var point = pointOnHeart(t);
            point.x =
              settings.particles.size / 2 +
              (point.x * settings.particles.size) / 450;
            point.y =
              settings.particles.size / 2 -
              (point.y * settings.particles.size) / 450;
            return point;
          }
          // create the path
          context.beginPath();
          var t = -Math.PI;
          var point = to(t);
          context.moveTo(point.x, point.y);
          while (t < Math.PI) {
            t += 0.03; // baby steps!
            point = to(t);
            context.lineTo(point.x, point.y);
          }
          context.closePath();
          // create the fill (更换爱心颜色)
          context.fillStyle = "#ea80b0";
          context.fill();
          // create the image
          var image = new Image();
          image.src = canvas.toDataURL();
          return image;
        })();

        // render that thing!
        function render() {
          // next animation frame
          requestAnimationFrame(render);

          // update time(粒子速度)
          var newTime = new Date().getTime() / 1000,
            deltaTime = newTime - (time || newTime);
          time = newTime;

          // clear canvas
          context.clearRect(0, 0, canvas.width, canvas.height);

          // create new particles
          var amount = particleRate * deltaTime;
          for (var i = 0; i < amount; i++) {
            var pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random());
            var dir = pos.clone().length(settings.particles.velocity);
            particles.add(
              canvas.width / 2 + pos.x,
              canvas.height / 2 - pos.y,
              dir.x,
              -dir.y
            );
          }

          // update and draw particles
          particles.update(deltaTime);
          particles.draw(context, image);
        }

        // handle (re-)sizing of the canvas
        function onResize() {
          canvas.width = canvas.clientWidth;
          canvas.height = canvas.clientHeight;
        }
        window.onresize = onResize;

        // delay rendering bootstrap
        setTimeout(function () {
          onResize();
          render();
        }, 10);
      })(document.getElementById("pinkboard"));
    </script>
  </body>
</html>

如何上传npm包

如何上传自己的npm包

一、创建自己的npm账号

[npm官网]: “https://www.npmjs.com/

二、创建npm包目录

mkdir npm-me // 在终端输入以下命令,npm-me为包的目录,可以自己任意取名
cd npm-me // 进入包目录

三、 编写模块

在npm-me目录下创建 ifYes.js文件

//ifYes.js
exports.ifYes = function() {
    return "If you're late, your wages will be docked"
}

四、 初始化包描述文件

npm init //初始化会生成package.json文件

初始化的过程会出现一系列的问题,根据我们自己的情况去选择就可以,如果没有其他要求,回车就可哟

五、注册包仓库账号

npm adduser

当你没有创建npm账户或者输入错误的账户和密码,都会出现下面的error

这是处理正确的结果(确认的时候需要接收验证码:例30466554)

六、上传包

npm publish 包文件夹名字 //上传npm命令格式

1.为了不出现npm命名冲突,第一个方法是在npm官网查一下,如果重复就需要重新修改一下(现在我的npm包就冲突了)

2.当你上传npm包的时候可能才会发现npm命名冲突

npm publish . //上传npm包

修改后上传

//package.json
{
  "name": "alan-npm-me",
  "version": "1.0.0",
  "description": "If you're late, your wages will be docked",
  "main": "ifYes.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "ifYes"
  ],
  "author": "alan_mf",
  "license": "ISC"
}

上传成功啦啦啦

npm账户上可以看到我们上传的包

七、 更新npm包

npm包修改后,手动把package.json里的version版本号修改了,或者使用以下命令自动更新版本号,再执行npm publish . 命令就可以了。

  • 升级补丁版本号(修改bug):npm version patch
  • 升级小版本号(新增功能):npm version minor
  • 升级大版本号(较大改版):npm version major
npm version patch //补丁
npm publish . //更新npm包

更新成功版本1.0.1

八、 对上传的npm进行测试

mkdir npm-me-test //新建目录
cd  npm-me-test //进入目录

下载我们上传的npm包

npm init -y //初始化生成package.json文件
npm install alan-npm-me -D

创建test.js文件并编写代码

//test.js
const demo = require('alan-npm-me');
const res = demo.ifYes();
console.log(res);

目前我们所开发的都是共享的npm包,具有一定的安全性风险,如果用于企业,就会具有一定的风险。企业的限制在于,一方面需要享受模块开发带来的低耦合和项目组织上的好处,另一方面却要考虑到模块保密性的问题。所以,通过NPM共享和发布存在潜在的风险。

为了同时能够享受到NPM上众多的包,同时对自己的包进行保密和限制,现有的解决方案就是企业搭建自己的NPM仓库。局域NPM仓库的搭建方法与搭建镜像站的方式几乎一样,与镜像仓库不同的地方在于,企业局域NPM可以选择不同步官方源仓库中的包,即企业可混合使用官方仓库和局域仓库。

对于企业内部而言,私有的可重用模块可以打包到局域NPM仓库中,这样可以保持更新的中心化,不至于让各个小项目各自维护相同功能的模块,杜绝通过复制粘贴实现代码共享的行为。

链接:

[npm官网]: “https://www.npmjs.com/

yarn命令详解

yarn命令详解

一、yarn安装初始化

npm i yarn -g #yarn安装
yarn -v #查看版本
yarn init | yarn #初始化,创建package.json文件
yarn init --yes #简写 -y 跳过会话,直接通过默认值生成 package.json

二、yarn安装依赖

yarn add webpack@2.3.3 # yarn --save 是 yarn 默认的,默认记录在 package.json dependencies 中
yarn add webpack --dev # yarn 简写 -D  devDependencies 中
yarn global add webpack # yarn 安装全局依赖

三、yarn更新依赖

yarn upgrade # 升级所有依赖项,不记录在 package.json 中
yarn upgrade webpack # 升级指定包
yarn upgrade --latest # 忽略版本规则,升级到最新版本,并且更新 package.json

四、yarn移除依赖

yarn remove webpack # yarn

五、其他命令

yarn install # 或者 yarn 在 node_modules 目录安装 package.json 中列出的所有依赖
yarn install --force # 强制下载安装
yarn start # yarn  执行 scripts 下 start 对应的脚本

###用来查看某个模块的最新版本信息
yarn info webpack # yarn
yarn info webpack --json # 输出 json 格式
yarn info webpack readme # 输出 README 部分

yarn list # 列出当前项目的依赖
yarn list --depth=0 # 限制依赖的深度
sudo yarn global list # 列出全局安装的模块

###yarn配置文件
yarn config set key value # 设置
yarn config get key # 读取值
yarn config delete key # 删除
yarn config list # 显示当前配置
yarn config set registry [https://registry.npm.taobao.org](https://links.jianshu.com/go?to=https%3A%2F%2Fregistry.npm.taobao.org) # 设置淘宝镜像

###缓存
sudo yarn cache list # 列出已缓存的每个包
sudo yarn cache dir # 返回 全局缓存位置
sudo yarn cache clean # 清除缓存

npm命令详解

npm命令详解

一、npm配置命令

npm install -g cnpm --registry=https://registry.npm.taobao.org  #通过cnpm使用淘宝镜像
npm config set registry https://registry.npm.taobao.org #将npm设置为淘宝镜像
npm config set registry https://registry.npmjs.org #切换回默认全局镜像
npm config get registry #查看npm镜像设置
npm config list #查看npm配置
cnpm config list #查看cnpm配置
npm -v #查看npm版本

方便统一和阅读,文中全部使用简写方式。

-g: 为 –global 的缩写,表示安装到全局目录里
-S: 为 –save 的缩写,表示安装的包将写入package.json里面的dependencies
-D: 为 –save-dev 的缩写,表示将安装的包将写入packege.json里面的devDependencies
i: 为install的缩写,表示安装

二、npm安装命令

npm init  # npm 初始化当前目录
npm i  # 安装所有依赖
npm i express  # 安装模块到默认dependencies
npm i express -g  # 会安装到配置的全局目录下
npm i express -S  # 安装包信息将加入到dependencies生产依赖
npm i express -D  # 安装包信息将加入到devDependencies开发依赖
npm i jquery@1.8.3  # 安装jquery指定的1.8.3版本

三、npm更新命令

npm update jquery  # 更新最新版本的jquery
npm update jquery@2.1.0  # 更新到指定版本号的jquery
npm install jquery@latest  # 可以直接更新到最后一个新版本

四、npm卸载命令

npm uninstall express  # 卸载模块,但不卸载模块留在package.json中的对应信息
npm uninstall express -g  # 卸载全局模块
npm uninstall express --save  # 卸载模块,同时卸载留在package.json中dependencies下的信息
npm uninstall express --save-dev  # 卸载模块,同时卸载留在package.json中devDependencies下的信息

五、npm查看命令

npm root  # 查看项目中模块所在的目录
npm root -g  # 查看全局安装的模块所在目录
npm list 或者 npm ls  # 查看本地已安装模块的清单列表
npm view jquery dependencies  # 查看某个包对于各种包的依赖关系
npm view jquery version  # 查看jquery最新的版本号
npm view jquery versions  # 查看所有jquery历史版本号(很实用)
npm view jquery  # 查看最新的jquery版本的信息
npm info jquery  # 查看jquery的详细信息,等同于上面的npm view jquery
npm list jquery 或 npm ls jquery  # 查看本地已安装的jquery的详细信息
npm view jquery repository.url  # 查看jquery包的来源地址

六、其他命令

npm cache clean  # 清除npm的缓存
npm prune  # 清除项目中没有被使用的包
npm outdated  # 检查模块是否已经过时
npm repo jquery  # 会打开默认浏览器跳转到github中jquery的页面
npm docs jquery  # 会打开默认浏览器跳转到github中jquery的README.MD文件信息
npm home jquery  # 会打开默认浏览器跳转到github中jquery的主页

七、package.json字段

[package.json字段详解]: “https://docs.npmjs.com/cli/v8/configuring-npm/package-json

链接:

[npm官网]: “https://www.npmjs.com/

从0到1搭建自己的脚手架react

从0到1搭建自己的脚手架react

1.初始化项目

npm init -y

执行初始化命令之后,会出现package.json文件

2.建立项目目录(基础项目目录)

3.下载依赖

react

npm i react react-dom 

下载babel。babel是javascript编辑器,作用如下:

  1. 负责把ES6、ES7等高版本js编译成低版本js,供浏览器运行。
  2. 负责把react语法(jsx)编译成js。
 npm i @babel/core @babel/cli @babel/preset-env @babel/preset-react --save-dev

集成webpack。webpack是一个现代JavaScript应用程序的静态模块打包器,现代前端应用很多都是用webpack打包。webpack-dev-server用来搭建一个本地服务,可以热加载前端项目

npm i webpack webpack-dev-server webpack-cli --save-dev

webpack集成babel还需要babel-loader,加载html文件还需要插件html-webpack-plugin:

npm i babel-loader html-webpack-plugin --save-dev

4.编辑index.html、App.js、index.js

//index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root">root</div>
</body>
</html>
//App.js
import React from "react";
class App extends React.PureComponent{
    render(){
        return (
            <div>
                App
            </div>
          );
    }
}

export default App
//index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const root = createRoot(document.getElementById('root'))
root.render(<App />)

5.在根目录下添加.babelrc文件、webpack.config.js文件

6.配置.babelrc、webpack.config.js

//.babelrc
{
    "presets": ["@babel/preset-env","@babel/preset-react"]
}
//webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    //webpack入口文件,webpack从这里开始构建依赖图
    entry:{
        main:'./src/index.js'
    },
    //输出文件 ./dist/bundle.js
    output:{
        path: path.resolve(__dirname,'dist'),
        filename:'bundle.js'
    },
    //webpack只能处理js和json文件,加载别的文件需要loader
    module:{
        rules:[
            {
                test:/.m?js/,
                use:'babel-loader',
                exclude:/node_modules/
            }
        ]
    },
    //webpack加载html文件需要html-webpack-plugin插件处理。
    //启动webpack-dev-server的时候,会把打包好的js文件,css文件,html文件放在内存里
    plugins:[
        new HtmlWebpackPlugin({
            template:'./public/index.html'
        })
    ],
    mode:'development'

}

7.当我们配置好上面的项目了,现在我们就可以运行了

现在我们还得配置一个小细节package,json

{
  "name": "react-app-start",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    //--mode=development为开发环境,速度较快,上线可以配置为生产环境,会压缩,优化代码
    //--watch为监听代码变化,只要代码变化,会重新编译
    //build是生产环境的打包
    "serve": "webpack --mode=development --watch",
    "build": "webpack --mode=production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "webpack": "^5.74.0"
  },
  "devDependencies": {
    "@babel/cli": "^7.19.3",
    "@babel/core": "^7.20.2",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-react": "^7.18.6",
    "babel-loader": "^9.1.0",
    "html-webpack-plugin": "^5.5.0",
    "webpack-cli": "^4.10.0"
  }
}

这时候就可以执行命令了

npm run serve

打包执行命令

npm run build

8.如果没有报错的话,你就会看见下面的页面

9.至此webpack简易版配置已经完成。接下来配置热加载功能,下载webpack-dev-server:

npm i webpack-dev-server --save-dev

执行命令:

npm run start

项目在3000端口启动,启动后会自动打开浏览器窗口。热加载配置完成,文件修改保存后浏览器直接展示出来。

到此react项目简化版搭建完成。后续继续集成其他工具库即可。

10.react使用ts

ts对比js最大的特点就是多了类型检查。推荐前端语言使用ts。接下来介绍在webpack中配置ts。

安装typescript、ts-loader。typescript是ts编辑器,把ts代码编译成js,ts-loader是让webpack识别.ts/.tsx文件,调用编译器编译,

npm i typescript ts-loader --save-dev

添加ts配置文件,在根目录下添加tsconfig.json:

//tsconfig.json
{
    "compilerOptions": {
        "target": "es5",
        "lib":["es6","dom"],
        "module": "esnext",
        "moduleResolution": "node",
        "jsx":"react",
        "sourceMap": true,
        "strict": true,
        "importHelpers": true,
        "skipLibCheck": true,
        "allowJs": true,
        "allowSyntheticDefaultImports": true
    }
}

修改webpack配置

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    //webpack入口文件,webpack从这里开始构建依赖图
    entry:{
        main:'./src/index.tsx'
    },
    //输出文件 ./dist/bundle.js
    output:{
        path: path.resolve(__dirname,'dist'),
        filename:'bundle.js'
    },
    //webpack只能处理js和json文件,加载别的文件需要loader
    module:{
        rules:[
            {
                test:/.m?js|tsx$/,
                use:'ts-loader',
                exclude:/node_modules/
            }
        ]
    },
    //增加扩展选项,让webpack可以识别.ts/tsx文件
    resolve:{
        extensions:['.ts','.tsx','.js']
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:'./public/index.html'
        })
    ],
    mode:'development'

}

目前可能会报错

根据提示,我们缺一些依赖

npm i --save-dev @types/react
npm i --save-dev @types/react-dom

这个时候我们还会遇到一个问题

那么这个问题的原因是缺null类型检测

其他人回答说应该添加null检查,但Typescript也有一个非null断言,当您通过添加!运算符添加到语句末尾:

这个时候我们react使用ts的问题就解决了

启动项目,运行成功,ts集成成功了

11.react集成路由

安装路由库

npm install react-router-dom@6

创建项目目录

编辑About.tsx、Home.tsx、index.tsx、App.tsx

//About.tsx
import React from "react";
import { Link } from "react-router-dom";

class About extends React.PureComponent{
    render(){
        return (
            <div>
                this is about<br/>
                <Link to={'/home'}>to home</Link>
            </div>
        )
    }
}

export default About;
//Home.tsx
import React from "react";
import { Link } from "react-router-dom";

class Home extends React.PureComponent{
    render(){
        return (
            <div>
                this is home<br/>
                <Link to={'/about'}>to about</Link>
            </div>
        )
    }
}

export default Home;
//App.tsx
import React from "react";
import { Routes,Route } from "react-router-dom";
import About from "./compents/About";
import Home from "./compents/Home";

class App extends React.PureComponent{
    render(){
        return (
            <div>
                <Routes>
                    <Route path="/" element={<Home />}></Route>
                    <Route path="/home" element={<Home />}></Route>
                    <Route path="/about" element={<About />}></Route>
                </Routes>
            </div>
          );
    }
}

export default App
//index.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

const root = createRoot(document.getElementById('root')!)
root.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>
)

react路由搭建成功

12.react使用redux

对于大型项目来说,数据管理工具也是必不可少的。我们简单介绍redux、react-redux的使用。这里使用的是@reduxjs/toolkit库,相比redux库使用方法有很大区别,想用redux库的请自行查看redux文档。

下载依赖

npm i @reduxjs/toolkit react-redux

创建项目目录

编辑store.ts、countSlice.ts、counter.tsx、index.tsx、App.tsx

//store.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from './countSlice';

export default configureStore({
    reducer:{
        counter:counterReducer
    }
})
//countSlice.ts
import { createSlice } from "@reduxjs/toolkit";

export const counterSlice = createSlice({
    name:'counter',
    initialState:{
        value:0
    },
    reducers:{
        increment:(state) => {
            state.value += 1
        },
        decrement:(state) =>{
            state.value -= 1
        },
        incrementByAmount:(state,action) => {
            state.value += action.payload
        }
    }
})

export const { increment,decrement,incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
//index.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import store  from './store/store';
import { Provider } from 'react-redux';

const root = createRoot(document.getElementById('root')!)
root.render(
    <BrowserRouter>
    <Provider store={store}>
        <App />
    </Provider>  
    </BrowserRouter>
)
//counter.tsx
import React from "react";
import { useSelector,useDispatch } from "react-redux";
import { decrement,increment } from "../store/countSlice";

function Counter(){
    const count = useSelector((state:any) => state.counter.value)
    const dispatch = useDispatch()
    
    return (
        <div>
            <div>
                <button 
                aria-label="Increment value"
                onClick={() => dispatch(increment())}
                >
                    increment
                </button>
                <span>{count}</span>
                <button
                aria-label="Decrement value"
                onClick={() => dispatch(decrement())}
                >
                    Decrement
                </button>
            </div>
        </div>
    )
}

export default Counter;
//App.tsx
import React from "react";
import { Routes,Route } from "react-router-dom";
import About from "./compents/About";
import Home from "./compents/Home";
import Counter from "./compents/Counter";

class App extends React.PureComponent{
    render(){
        return (
            <div>
                <Routes>
                    <Route path="/" element={<Home />}></Route>
                    <Route path="/home" element={<Home />}></Route>
                    <Route path="/about" element={<About />}></Route>
                    <Route path="/counter" element={<Counter />}></Route>
                </Routes>
            </div>
          );
    }
}

export default App

当我执行命令进入counter组件点击功能按钮就发现redux使用成功了

npm run start

13.集成UI库

这里使用的是antd第三方库

安装ui库

npm i antd

使用antd还需要引用其样式文件,因此还需要下载css-loader、style-loader。

npm i style-loader css-loader -D

修改webpack配置

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    //webpack入口文件,webpack从这里开始构建依赖图
    entry:{
        main:'./src/index.tsx'
    },
    //输出文件 ./dist/bundle.js
    output:{
        path: path.resolve(__dirname,'dist'),
        filename:'bundle.js'
    },
    //webpack只能处理js和json文件,加载别的文件需要loader
    module:{
        rules:[
            {
                test:/.j|tsx$/,
                use:'ts-loader',
                exclude:/node_modules/
            },
            {
                test:/.css$/,
                use:['style-loader','css-loader']
            }
        ]
    },
    //增加扩展选项,让webpack可以识别.ts/tsx文件
    resolve:{
        extensions:['.ts','.tsx','.js']
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:'./public/index.html'
        })
    ],
    mode:'development'
}

使用antd的话直接引入使用就可以了

//Home.tsx
import React from "react";
import { Link } from "react-router-dom";
import { Button } from "antd";

class Home extends React.PureComponent{
    render(){
        return (
            <div>
                this is home<br/>
                <Link to={'/about'}>
                    <Button type={'primary'}>to about</Button>
                </Link>
                <Link to={'/counter'}>
                    <Button type={'primary'}>to counter</Button> 
                </Link>
            </div>
        )
    }
}

export default Home;

14.跨域问题

修改webpack.config.js使用proxy

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    //webpack入口文件,webpack从这里开始构建依赖图
    entry:{
        main:'./src/index.tsx'
    },
    //输出文件 ./dist/bundle.js
    output:{
        path: path.resolve(__dirname,'dist'),
        filename:'bundle.js'
    },
    //webpack只能处理js和json文件,加载别的文件需要loader
    module:{
        rules:[
            {
                test:/.j|tsx$/,
                use:'ts-loader',
                exclude:/node_modules/
            },
            {
                test:/.css$/,
                use:['style-loader','css-loader']
            }
        ]
    },
    //增加扩展选项,让webpack可以识别.ts/tsx文件
    resolve:{
        extensions:['.ts','.tsx','.js']
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:'./public/index.html'
        })
    ],
    mode:'development',
    devServer:{
        proxy:{
            '/api':'http://localhost:8080',
        },
        client:{
            progress:true
        }
    }

}

配置后,前端请求就会被代理到8080端口,解决跨域问题

15.集成ESLint和prettier

安装ESLint

//本地安装
npm install eslint --save-dev
//全局安装
npm install -g eslint

运行eslint –init,选择工程使用了react,自动生成.eslintrc.js文件

eslint --init

安装prettier

//本地
npm i -D --save-exact prettier
//全局
npm i --global prettier

安装eslint-config-prettier插件(禁用 eslint 风格校验)

npm i -D eslint-config-prettier

安装eslint-plugin-prettier插件(使eslint采用prettier的风格校验)

npm i -D eslint-plugin-prettier

配置eslintrc.js文件

// eslint-disable-next-line no-undef
module.exports = {
    "env": {
        "browser": true,
        "es2021": true,
        "jest":true
    },
    "extends": [
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:@typescript-eslint/recommended"
    ],
    "overrides": [
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": "latest"
    },
    "plugins": [
        "react",
        "@typescript-eslint"
    ],
    "rules": {
    "no-cond-assign": 2,
    "no-console": [
      "error", {
        "allow": ["log", "warn", "error", "info"]
      }
    ],
    // 禁止 function 定义中出现重名参数
    "no-dupe-args": 2,
    // 禁止对象字面量中出现重复的 key
    "no-dupe-keys": 2,
    // 禁止重复的 case 标签
    "no-duplicate-case": 2,
    // 禁止对 catch 子句的参数重新赋值
    "no-ex-assign": 2,
    // 禁止不必要的布尔转换
    "no-extra-boolean-cast": 2,
    // 禁止不必要的括号 //(a * b) + c;//报错
    "no-extra-parens": 0,
    // 禁止 catch 子句的参数与外层作用域中的变量同名
    "no-catch-shadow": 0,
    // 不允许标签与变量同名
    "no-label-var": 2,
    // 禁用特定的全局变量
    "no-restricted-globals": 2,
    // 禁止覆盖受限制的标识符
    "no-shadow-restricted-names": 2,
    // 禁止将变量初始化为 undefined
    
    // 强制使用一致的换行风格
    "linebreak-style": [2, "unix"],
    //在JSX中强制布尔属性符号
    "react/jsx-boolean-value": 2,
    //在JSX中验证右括号位置
    // "react/jsx-closing-bracket-location": 1,
    //在JSX属性和表达式中加强或禁止大括号内的空格。
    "react/jsx-curly-spacing": [2, {
      "when": "never",
      "children": true
    }],
    //在数组或迭代器中验证JSX具有key属性
    "react/jsx-key": 2,
    // 限制JSX中单行上的props的最大数量
    "react/jsx-max-props-per-line": [1, {
      "maximum": 5
    }],
    //防止在JSX中重复的props
    "react/jsx-no-duplicate-props": 2,
    //  //防止使用未包装的JSX字符串
    // "react/jsx-no-literals": 0,
    //在JSX中禁止未声明的变量
    "react/jsx-no-undef": 2,
    //为用户定义的JSX组件强制使用PascalCase
    "react/jsx-pascal-case": 0,
    //防止反应被错误地标记为未使用
    "react/jsx-uses-react": 2,
    //防止在JSX中使用的变量被错误地标记为未使用
    "react/jsx-uses-vars": 2,
    //防止在componentDidMount中使用setState
    "react/no-did-mount-set-state": 2,
    //防止在componentDidUpdate中使用setState
    "react/no-did-update-set-state": 2,
    //防止使用未知的DOM属性
    "react/no-unknown-property": 2,
    //为React组件强制执行ES5或ES6类
    "react/prefer-es6-class": 2,
    //防止在React组件定义中丢失props验证
    // "react/prop-types": 1,
    //使用JSX时防止丢失React
    "react/react-in-jsx-scope": 2,
    //防止没有children的组件的额外结束标签
    "react/self-closing-comp": 0,
    //禁止不必要的bool转换
    // "no-extra-boolean-cast": 0,
    //防止在数组中遍历中使用数组key做索引
    // "react/no-array-index-key": 0,
    //不使用弃用的方法
    "react/no-deprecated": 2,
    //在JSX属性中强制或禁止等号周围的空格
    "react/jsx-equals-spacing": 2,
    "react/jsx-filename-extension": [2, {
      "extensions": [".js", ".jsx"]
    }],
    // 禁止未使用的变量
    "no-unused-vars": 0,
    }
}

当我们代码被eslint检测到不符合规则的代码就会提示,根据eslint指示修改就可以

在根目录下创建.prettierrc 或 prettier.config.js文件

module.exports = {
    endOfLine: 'auto', // 不检测检测文件每行结束的格式
    semi: true, // 使用分号, 默认true
    singleQuote: true // 使用单引号, 默认false(在jsx中配置无效, 默认都是双引号)
 }

安装vscode插件:Prettier ESLint

该插件会根据工程根目录下的eslintrc.js和prettier.config.js文件格式化代码。可以进一步设置为vscode默认formater和自动格式化。

Prettier配置常用的参数可以根据团队需求制定

该文章只是配置简单的react脚手架,核心流程和方法是如此,其他具体需求可以根据团队客制化定制

参考链接:

[antd官网]: “https://ant.design/docs/react/introduce-cn
[ESLint中文文档]: “https://eslint.bootcss.com
[Prettier中文网]: “https://www.prettier.cn

源码仓库地址:

https://github.com/mengfeng/react-app-start.git

从0到1搭建自己的脚手架vue3

搭建vue项目脚手架

1.所以的网站都需要一个页面,所以创建第一个.html文件(index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue-cli-start</title>
</head>
<body>
    <div id="app"></div>
   <!-- 引入打包后的js文件 -->
    <script src="bundle.js"></script> 
</body>
</html>

2.创建src和dist文件夹

因为index.html需要引入打包后的bundle.js文件,所以将index.js放到了dist目录下

3.创建入口文件main.js

我们需要引入vue实例,但是现在我们没有vue包,所以要先下载vue

我们也可以一次性下载vue所需要的依赖

npm i vue element-ui axios

4.安装开发依赖

npm i -D webpack webpack-cli vue-loader vue-template-compiler sass sass-loader css-loader style-loader babel-loader @babel/core @babel/preset-env

如果项目文件没有package.json,那么

npm init -y

5.创建webpack.config.js并配置(根目录下和package.json同一目录创建)

//webpack.config.js基本配置模板
module.exports = {
    //入口,表示从那个文件开始解析
    entry:'',
    //出口,表示输出文件位置和信息
    output:{
        path:'',
        filename:''
    },
    //模块,定义匹配规则和转换loader
    module:{
        rules:[
            {test:/\.vue$/,use:'vue-loader'},
            {}
        ]
    },
    //插件,给予webpack更丰富的功能
    plugins:[
        //实例化
        new PluginA()
    ]

}

基本配置完成的webpack.config.js

const path = require('path')
const {VueLoaderPlugin} = require('vue-loader')
module.exports = {
    entry:'./src/main.js',
    output:{
        path:path.resolve(__dirname,'dist'),
        filename:'bundle.js'
    },
    module:{
        rules:[
            {test:/\.vue$/,use:'vue-loader'},
            {test:/\.s[ca]ss$/,use:['style-loader','css-loader','sass-loader']},
            {
                test:/\.m?js$/,
                use:{
                    loader:'babel-loader',
                    options:{
                        presets:['@babel/preset-env'],
                    }
                }
            },
            //老语法
            // {test:/\.(png|jpe?g|svg|gif|webp)$/,use:{loader:'file-loader',options:{esModule:'false'}}}
            {test:/\.(png|jpe?g|svg|gif|webp)$/,type:'asset/resource'},
        ]
    },
    plugins:[
        new VueLoaderPlugin()
    ]
}

6.编辑书写main.js文件和App.vue

//main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
//App.vue
<template>
    <div>
        艺术码畜博客‘https://mengfeng.github.io/’
    </div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
</style>

7.那么我们现在应该可以运行所配置的代码了

现在出现一个问题,直接执行webpack命令会找不到webpack命令,虽然我们下载了webpack-cli,但是只是存在于./node_modules/.bin里面

那我们执行./node_modules/.bin/webpack也是可以打包的,但是我们每次执行这么长的命令不是很方便,所以通过配置package.json文件来简化命令

{
  "name": "vue-cli-start",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts":{
    //--mode=development为开发环境,速度较快,上线可以配置为生产环境,会压缩,优化代码
    //--watch为监听代码变化,只要代码变化,会重新编译
    //build是生产环境的打包
    "serve":"webpack --mode=development --watch",
    "build":"webpack --mode=production"
  },
 "dependencies": {
    "axios": "^1.1.3",
    "element-ui": "^2.15.10",
    "vue": "^3.2.41"
  },
  "devDependencies": {
    "@babel/core": "^7.20.2",
    "@babel/preset-env": "^7.20.2",
    "babel-loader": "^9.1.0",
    "css-loader": "^6.7.1",
    "sass": "^1.56.0",
    "sass-loader": "^13.1.0",
    "style-loader": "^3.3.1",
    "vue-loader": "^17.0.1",
    "vue-template-compiler": "^2.7.13",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

当我们执行npm run serve

npm run serve

表示我们编译成功了,我们现在可以试着运行一下index.html

恭喜,目前为止已经没有什么问题了!!!基础的vue脚手架就完成了(目前只是一个页面)

8.我们需要多个页面的时候,就需要其他依赖了

根据下面的配置来修改自己的代码,可能下面的代码于上面的不同,但效果是一样的

npm i vue-router

9.创建route文件夹和pages文件夹

10.编辑App.vue和route下的index.js文件

//App.vue
<template>
    <div id="nav">
        <router-link to="/"> home </router-link> |
        <router-link to="/food"> food </router-link> |
        <router-link to="/banner"> banner </router-link>
    </div>
    <router-view />
</template>

这里我们使用的是history,也可以用hash

//route/index.js
import { createRouter, createWebHashHistory } from "vue-router"
//注册
import home from '../pages/Home.vue'
import food from '../pages/Food.vue'
import banner from '../pages/Banner.vue'

const routes = [
  { path: "/", redirect: "/home" },
  {
    path: "/home",
    name: "home",
    component: home
  },
  {
    path: "/food",
    name: "food",
    component: food
  },
  {
    path: "/banner",
    name: "banner",
    component: banner
  }
]
//导出
const router = createRouter({
    history: createWebHashHistory(),
    routes: routes
  })

  export default router;

11.需要在main.js里面去注册

import { createApp } from 'vue'
import App from './App.vue'
import router from './route'

const app = createApp(App)
app.use(router)
app.mount('#app')

12.这个时候再去跑我们的代码就可以了

npm run serve

13.页面之间的route跳转

//Food.vue
<template>
    <p>food</p>
    <button @click="toHome">toHome</button>
     <!-- router-link组件跳转 -->
    <router-link to="/home">toHome</router-link>
  </template>
  <script>
  import { useRouter } from 'vue-router'
  export default {
    setup () {
      //编程式跳转
      const router = useRouter()
      const toHome = (() => {
        router.push({
          name: 'home'
        })
      })
      return {
        toHome
      }
    },
  }
  </script>

当我们点击toHome按钮就可以跳转到home页了,两种方式都可以跳转

14.配置ESLint和Prettier

安装ESLint

//本地安装
npm install eslint --save-dev
//全局安装
npm install -g eslint

运行eslint –init,选择工程使用了vue,自动生成.eslintrc.js文件

eslint --init

我们按照需求去选择就可以了

安装prettier

//本地
npm i -D --save-exact prettier
//全局
npm i --global prettier

安装eslint-config-prettier插件(禁用 eslint 风格校验)

npm i -D eslint-config-prettier

安装eslint-plugin-prettier插件(使eslint采用prettier的风格校验)

npm i -D eslint-plugin-prettier

配置eslintrc.js文件

module.exports = {
    "env": {
        "browser": true,
        "es2021": true,
        "node":true,
    },
    "extends": [
        "eslint:recommended",
        "plugin:vue/vue3-essential"
    ],
    "overrides": [
    ],
    "parserOptions": {
        "ecmaVersion": "latest"
    },
    "plugins": [
        "vue"
    ],
    "rules": {
      'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
      'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
       //在rules中添加自定义规则
       //关闭组件命名规则
       "vue/multi-word-component-names":"off",
    }
}

在根目录下创建.prettierrc 或 prettier.config.js文件

module.exports = {
    endOfLine: 'auto', // 不检测检测文件每行结束的格式
    semi: true, // 使用分号, 默认true
    singleQuote: true // 使用单引号, 默认false(在jsx中配置无效, 默认都是双引号)
  }

安装vscode插件:Prettier ESLint

该插件会根据工程根目录下的eslintrc.js和prettier.config.js文件格式化代码。可以进一步设置为vscode默认formater和自动格式化。

Prettier配置常用的参数可以根据团队需求制定

链接:

[ESLint中文文档]: “https://eslint.bootcss.com
[Prettier中文网]: “https://www.prettier.cn

源码仓库地址:

https://github.com/mengfeng/vue-cli-start

  • Copyrights © 2022-2023 alan_mf
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信