项目搭建及部署相关
创建

项目目录结构划分


jsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"jsx": "preserve",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}样式重置初始化
normalize.css
npm install normalize.css --savereset.css
body, h1, h2, h3, h4, ul, li {
margin: 0;
padding: 0;
}
ul, li {
list-style-type: none;
}
a {
text-decoration: none;
color: #333;
}
img {
vertical-align: top;
}common.css
:root {
--primary-color: #ff9854;
--van-search-left-icon-color: var(--primary-color) !important;
--van-tabs-bottom-bar-color: var(--primary-color) !important;
--van-nav-bar-icon-color: var(--primary-color) !important;
}
body {
font-style: 14px;
}

路由配置

pinia使用


用户代码片段配置

vite中动态引入本地图片的问题解决方式

vant ui引入使用
样式穿透修改

不同页面tabbar栏的隐藏方案
- 设置meta,根据meta的值判断是否显示tabbar栏

- 修改样式的隐藏(代码仅参考,不要照抄!)

网络请求封装架构
import axios from "axios"
import { BASE_URL, TIME_OUT } from "./config"
class JFRequest {
constructor(baseURL, timeout = 5000) {
this.instance = axios.create({
baseURL, // 注意baseURL,timeout写法
timeout
})
}
request(config) {
return new Promise((resolve, reject) => {
this.instance.request(config).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
get(config) {
return this.request({ ...config, methods: 'GET' })
}
post(config) {
return this.request({ ...config, methods: 'POST' })
}
}
export default new JFRequest(BASE_URL, TIME_OUT)- .vue文件中的网络请求函数放至service/module/.js中,只需将请求函数导出即可
- 网络请求数据的执行触发和存放,放入pinia的store中(解决一个.vue文件中可能要发送大量网络请求函数的情况出现)


对象数据响应式获取展示(获取key,)

vue hooks函数封装思想实践
监听页面滚动(或页面节点滚动)触底

/hooks/useScroll.js useScroll函数的封装(回调函数的方式)
import _debounce from "@/utils/debounce"
import { onMounted, onUnmounted, onActivated, onDeactivated, ref } from "vue"
// 回调函数的方式
export default function useScroll(targetRef, reachBottomCB) {
// 默认对window进行滚动监听
let el = window
onMounted(() => {
// 是window还是某个结点,根据实际情况而定
if (targetRef) el = targetRef.value
el.addEventListener('scroll', scrollHandler)
})
onUnmounted(() => {
el.removeEventListener('scroll', scrollHandler)
})
// 使用了keep-alive动态组件的时候的添加移除监听
onActivated(() => {
if (targetRef) el = targetRef.value
el.addEventListener('scroll', scrollHandler)
})
onDeactivated(() => {
el.removeEventListener('scroll', scrollHandler)
})
// 滚动监听的回调
const scrollHandler = _debounce(() => {
let scrollTop
let clientHeight
let scrollHeight
// 使用+1是因为解决浏览器实际高度与js获取到的高度产生误差
// 通过防抖的方式避免同一时间内多次调用下一页
// 也达到了优化界面性能的效果,只有达到底部才触发回调!
if (el === window) { // 是window滚动还是某个节点
scrollTop = document.documentElement.scrollTop
clientHeight = document.documentElement.clientHeight
scrollHeight = document.documentElement.scrollHeight
} else {
scrollTop = el.scrollTop
clientHeight = el.clientHeight
scrollHeight = el.scrollHeight
}
// 下行重点!!!
console.log('已滚动', scrollTop, '窗口高度', clientHeight, '总高度', scrollHeight)
// 触底加载更多
if (scrollTop + clientHeight + 1 >= scrollHeight) {
// 滚动到底部了,回调
console.log('滚动到底部了,回调')
if (reachBottomCB) reachBottomCB()
}
}, 80)
}/hooks/useScroll.js useScroll函数的封装(返回变量(是否触底,当前滚动信息,具体的滚动高度)的方式)

// 返回变量的方式
export default function useScroll(targetRef) {
// 默认对window进行滚动监听
let el = window
onMounted(() => {
// 是window还是某个结点,根据实际情况而定
if (targetRef) el = targetRef.value
el.addEventListener('scroll', scrollHandler)
})
onUnmounted(() => {
el.removeEventListener('scroll', scrollHandler)
})
// 使用了keep-alive动态组件的时候的添加移除监听
onActivated(() => {
if (targetRef) el = targetRef.value
el.addEventListener('scroll', scrollHandler)
})
onDeactivated(() => {
el.removeEventListener('scroll', scrollHandler)
})
const isReachBottom = ref(false)
const scrollTop = ref(0)
const clientHeight = ref(0)
const scrollHeight = ref(0)
// 滚动监听的回调
const scrollHandler = _throttle(() => {
// 使用+1是因为解决浏览器实际高度与js获取到的高度产生误差
// 通过节流的方式避免同一时间内多次调用下一页
// 也达到了优化界面性能的效果,降低触发频率!
if (el === window) { // 是window滚动还是某个节点
scrollTop.value = document.documentElement.scrollTop
clientHeight.value = document.documentElement.clientHeight
scrollHeight.value = document.documentElement.scrollHeight
} else {
scrollTop.value = el.scrollTop
clientHeight.value = el.clientHeight
scrollHeight.value = el.scrollHeight
}
// 下行重点!!!
// console.log('已滚动', scrollTop.value, '窗口高度', clientHeight.value, '总高度', scrollHeight.value)
// 触底加载更多
if (scrollTop.value + clientHeight.value + 1 >= scrollHeight.value) {
// 滚动到底部了
isReachBottom.value = true
}
}, 80, { leading: true, trailing: true }, () => { })
return { isReachBottom, scrollTop, scrollHeight, clientHeight }
}请求添加loading

百度地图模块嵌入使用
成为开发者
获取ak
html引入script
添加大头针
tabcontrol点击滚动
tab栏切换滚动
element.scrollTo({
top: 100,
behavior: 'smooth'
});tab点击跳转
- :ref=getSection获取节点和属性name,存放所有节点和对应的name
- 根据点击获取需要展示的节点的offsetTop(距离顶部的距离)
- 使用父节点的ref.value进行跳转(scrollTo)

// 使用一个对象sectionEls存放所有的key和对应的节点
const sectionEls = ref({})
// 根据节点动态获取所有tab的name
const names = computed(() => {
return Object.keys(sectionEls.value)
})
// 根据元素节点的name属性获取到节点
const getSectionRef = (value) => {
// console.log(value.$el) // 元素节点
if (!value) return
const name = value.$el.getAttribute("name")
sectionEls.value[name] = value.$el
}
let isClick = false
let currentDistance = -1
// 根据tab点击的下标获取需要跳转到的节点
const tabClick = (index) => {
const key = Object.keys(sectionEls.value)[index]
const el = sectionEls.value[key]
// 节点距离顶部的距离.offsetTop
let distance = el.offsetTop
if (index !== 0) {
distance = distance - 44
}
// 是点击导致的页面滚动
isClick = true
currentDistance = distance
// 进行平滑的跳转
detailRef.value.scrollTo({
top: distance,
behavior: "smooth"
})
}
// 页面滚动, 滚动时匹配对应的tabControll的index
const tabControlRef = ref()
watch(scrollTop, (newValue) => {
// 点击引起滚动结束时,回到正常状态(待手动滚动或者待点击)
if (newValue === currentDistance) {
isClick = false
}
// 点击导致滚动,不进行tab逐级跳转
if (isClick) return
// 1.获取所有的区域的offsetTops
const els = Object.values(sectionEls.value)
const values = els.map(el => el.offsetTop)
// 2.根据newValue去匹配想要索引
let index = values.length - 1
for (let i = 0; i < values.length; i++) {
if (values[i] > newValue + 44) {
index = i - 1
break
}
}
// console.log(index)
tabControlRef.value?.setCurrentIndex(index)
})滚动后tab栏的回显
- watch监听父节点的scrollTop值
- 获取到所有子节点所对应的top值(距顶距离)
- 匹配正确的距离,设置激活相应tab
页面视口禁止缩放
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<!-- maximum-scale=1.0, minimum-scale=1.0, user-scalable=no -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>界面大小适配vw, rem
vite/webpack打包工具内默认集成了postcss工具,postcss工具基础上可以继续安装plugins:postcss-px-to-viewport
npm install postcss-px-to-viewport -Dpostcss.config.js
// vue cli / webpack / vite
module.exports = {
plugins: {
'postcss-px-to-viewport': {
viewportWidth: 375,
// // 转换黑名单selectorBlackList
// selectorBlackList: ['tab_bar']
}
}
}打包部署
购买服务器
服务器环境搭建
服务器部署及常用操作
1. 购买服务器
阿⾥云、腾讯云、华为云
- 购买云服务器其实是购买⼀个实例。
2. 配置云服务器
2.1 连接云服务器
- 通常情况下,我们会通过ssh连接云服务器:
- Windows电脑上直接使⽤git bash ssh⼯具;
- Mac OS电脑上直接通过终端来使⽤ssh⼯具;
- vscode插件Remote-SSH可实现在vscode中使用远程连接(右键连接后,可查看到服务器各文件)
ssh root@__.__.__.__ // 进行连接
hostnamectl --static set-hostname zhujinfa // 更改服务器主机名
service sshd resatrt // 重启服务2.2 环境配置(Node.js)
安装软件使用的工具
- DNF,全称Dandified Yum;
- 是Yum的下⼀个版本,也被称之为Yum的替代品;
检查dnf是否可用
dnf --help
dnf --version- 安装软件包
// 搜索软件包
dnf search nodejs
// 查看软件包信息
dnf info nodejs
// 安装nodejs
dnf install nodejs- node版本切换工具:n
// 安装n
npm install n -g
// 通过n安装最新的lts和current版的node
n install lts
n install latest
// 通过n切换版本
n2.3 nginx安装和配置(手动部署)
nginx可以监听有无用户访问80端口,可自动给访问用户提供服务,返回资源
dnf search nginx
dnf install nginx
systemctl start nginx // 启动nginx服务
systemctl enable nginx // 自动启动开启
systemctl status nginx // 查看nginx启动状态
// 启动成功的话,浏览器直接通过服务器公网ip访问会得到一个welcome to nginx的页面将打包好的前端项目放到服务器里
nginx配置
将/etc/nginx添加到工作区,修改nginx.conf文件
/etc/nginx/nginx.confnginx.conf(修改默认访问的html页面)
user nginx -> user root; // 提升nginx权限
server { // 修改默认访问的html页面
# root /user/share/nginx/html
...
location / {
root /root/打包文件位置/;
index index.html;
}
}systemctl restart nginx可以通过服务器ip访问了!
2.4 环境配置(MySQL)
- 仍使用dnf来安装MySQL
// 查找MySQL
dnf search mysql-server
// 查看MySQL,这⾥的版本是8.0.21
dnf info mysql-server
// 安装MySQL,这⾥加-y的意思是依赖的内容也安装
dnf install mysql-server -y- 启动mysql-server
// 开启MySQL后台服务
systemctl start mysqld
// 查看MySQL服务:active (running)表示启动成功
systemctl status mysqld
// 随着系统⼀起启动
systemctl enable mysqld
// 取消跟随系统启动mysql
systemctl disable mysqld- 配置MySQL
// 配置MySQL账号和密码
mysql_secure_installation
// 进入mysql
mysql -u root -p- 远程连接
# 使用mysql数据库
use mysql;
# 查看user表中,连接权限,默认看到root是localhost
select host, user from user;
# 修改权限 服务器开启相对应的端口进行使用(默认为3306)
update user set host = '%' where user = 'root';
# 然后就可以使用navicat输入服务器公网ip加端口,root用户连接远程数据库了数据库迁移
navicate导出数据库文件(数据和结构),在对应的远程数据库运行即可
2.5 jenkins自动化部署
3.1.1. 安装Java环境
Jenkins本身是依赖Java的,所以我们需要先安装Java环境:
- 这里我安装了Java1.8的环境
dnf search java-1.8
dnf install java-1.8.0-openjdk.x86_643.1.2. 安装Jenkins
因为Jenkins本身是没有在dnf的软件仓库包中的,所以我们需要连接Jenkins仓库:
- wget是Linux中下载文件的一个工具,-O表示输出到某个文件夹并且命名为什么文件;
- rpm:全称为The RPM Package Manage,是Linux下一个软件包管理器;
wget –O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat-stable/jenkins.repo
# 导入GPG密钥以确保您的软件合法
rpm --import https://pkg.jenkins.io/redhat/jenkins.io.key
# 或者
rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key编辑一下文件/etc/yum.repos.d/jenkins.repo
- 可以通过vim编辑
[jenkins]
name=Jenkins-stable
baseurl=http://pkg.jenkins.io/redhat
gpgcheck=1安装Jenkins
dnf install jenkins # --nogpgcheck(可以不加)启动Jenkins的服务:
systemctl start jenkins
systemctl status jenkins
systemctl enable jenkinsJenkins默认使用8080端口提供服务,所以需要加入到安全组中:
3.1.3. Jenkins用户
我们后面会访问centos中的某些文件夹,默认Jenkins使用的用户是 jenkins,可能会没有访问权限,所以我们需要修改一下它的用户:
修改文件的路径:/etc/sysconfig/jenkins
之后需要重启一下Jenkins:
# 也可以将Jenkins添加到root组中
sudo usermod -a -G root jenkins
systemctl restart jenkins3.1.4. Jenkins配置
打开浏览器,输入:http://8.134.60.235:8080/
- 注意:你输入自己的IP地址
获取输入管理员密码:
- 在下面的地址中
cat /var/lib/jenkins/secrets/initialAdminPassword
可以安装推荐的插件:

3.1.5. Jenkins任务创建
新建任务:
配置项目和保留策略:

源码管理:

构建触发器:
这里的触发器规则是这样的:
- 定时字符串从左往右分别是:分 时 日 月 周
#每半小时构建一次OR每半小时检查一次远程代码分支,有更新则构建
H/30 * * * *
#每两小时构建一次OR每两小时检查一次远程代码分支,有更新则构建
H H/2 * * *
#每天凌晨两点定时构建
H 2 * * *
#每月15号执行构建
H H 15 * *
#工作日,上午9点整执行
H 9 * * 1-5
#每周1,3,5,从8:30开始,截止19:30,每4小时30分构建一次
H/30 8-20/4 * * 1,3,5
构建环境:
注意:我们需要搭建Node的环境
- 第一步:配置Node的环境;
- 第二步:安装Node的插件;

第一步:配置Node的环境

第二步:安装Node的插件
- 这里因为我已经安装过了,所以没有搜索到;

构建执行的任务:
- 查看Node的版本等是否有问题;
- 执行
npm install安装项目的依赖; - 移除原来mall_cms文件的所有内容;
- 将打包的dist文件夹内容移动到mall_cms文件夹;
pwd
node -v
npm -v
npm install
npm run build
pwd
echo '构建成功'
ls
# 删除/root/mall_cms文件夹里所有的内容
rm -rf /root/mall_cms/*
cp -rf ./dist/* /root/mall_cms/3.代码部署及托管
- 3.1 代码托管到git仓库
- 3.2 代码克隆到服务器
dnf install git; // 安装git
// 选择想放的文件夹
cd /
mkdir learnNode
cd learnNode/
git clone https://github.com/jinfazhu-mygit/coderhub.git- 3.3 vscode连接远程服务器代码操作
- 使用vscode插件:remote-ssh
- 创建连接,安装相关依赖,配置一下需要的东西(数据库,端口(要添加相应安全组))
- pm2启动node程序
// pm2常用命令
# 命名进程
pm2 start app.js --name my-api
pm2 start ./src/main.js // 或
# 显示所有进程状态
pm2 list
# 停⽌指定的进程
pm2 stop 0
# 停⽌所有进程
pm2 stop all
# 重启所有进程
pm2 restart all
# 重启指定的进程
pm2 restart 0
# 杀死指定的进程
pm2 delete 0
# 杀死全部进程
pm2 delete all
#后台运⾏pm2,启动4个app.js,实现负载均衡
pm2 start app.js -i 4