跳到主要内容

windows网络共享盘无法访问问题

· 阅读需 3 分钟
Yana Ching
Front End Engineer
  1. 可以在网络看到局域网中的其他主机,双击连接会显示以下错误信息

无法访问共享盘

解决

step1. 首先确保当前网络/属性 是否安装了多播协议

如果显示的属性中已经有了可靠多播协议,那么就不需要执行以下的操作

  1. 打开网络和共享中心

网络和共享中心

  1. 右键查看当前网络连接属性

右键查看当前网络连接属性

  1. 为当前网络客户端安装协议

为当前网络客户端安装协议

  1. 选择协议并添加

选择协议并添加

  1. 安装多播协议

安装多播协议

  1. 出现这个协议表示安装成功

出现这个协议表示安装成功

一般到这里,就可以正常访问共享盘了

win+r 快捷键输出共享盘地址 \\192.168.x.xx\

如果输入访问还是报错 你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问

无法访问共享盘,因为组织的安全策略阻止

step2 修改组织安全策略修复 "组织的安全策略阻止未经身份验证的来宾访问"

  1. win + r 输入 gpedit.msc

进入本地组策略编辑器

  1. 找到“启用不安全的来宾登录”设置

启用不安全的来宾登录

  1. 双击将该配置从 “未配置” 修改为 “已启用”

双击修改配置为“已启用”

pm2使用指南

· 阅读需 1 分钟
Yana Ching
Front End Engineer
npm install -g pm2

# 检查是否安装成功
pm2 -v

使用 pm2 启动项目 express 项目

pm2 start server.js
#或者指定 node 运行, server.js 是 node 服务器文件

pm2 start server.js --name my-app

常用命令

# 查看所有进程
pm2 show my-app

# 查看实时日志
pm2 logs

# 查看某个应用的日志
pm2 logs my-app

# 停止应用
pm2 stop 0 # 0 是应用 ID
pm2 stop my-app

# 重启应用
pm2 restart my-app
pm2 restart 0 # 0 是应用 ID

# 删除应用
pm2 delete my-app
pm2 delete 0 # 0 是应用 ID

# 重启后自动启动
pm2 startup
# 执行后会出现一条命令,复制命令执行,pm2就能开机自行启动
# e.g. sudo env PATH=$PATH:/home/your-user/.nvm/versions/node/v22.13.1/bin pm2 startup systemd -u root --hp /root


搭建部署云服务器

· 阅读需 7 分钟
Yana Ching
Front End Engineer

本教程适用于 Ubuntu 20.04 / 22.04,主要包含以下内容: ✅ 购买并配置阿里云服务器(ECS) ✅ 使用 SSH 连接服务器 ✅ 安装 Node.js、PM2、Nginx ✅ 上传并部署 React 项目 ✅ 使用 Nginx 反向代理 & 配置 HTTPS

🚀 第 1 步:购买阿里云 ECS 服务器

  1. 登录阿里云 👉 阿里云官网

  2. 进入 ECS 控制台,选择 创建实例

    image-20250210114611498

    image-20250210114650381

    image-20250210115330826

    image-20250210115539017

信息

计费方式:按量付费 or 包年包月(长期用推荐包年包月) 地域:选择靠近你的目标用户的地区 实例类型:2 核 4G(最少) 操作系统:Ubuntu 22.04 LTS(推荐) 存储:默认 40GB SSD 带宽:固定公网 IP,1-5 Mbps

  1. 创建完成后,进入 ECS 控制台,找到服务器公网 IP
x.xxx.xx.xx

image-20250210120015830

image-20250210120159074

🖥 第 2 步:连接服务器

方式 1:直接通过阿里云登录到服务器

image-20250210163215517

image-20250210163320114

image-20250210163355913

会开启一个网页直接登录到服务器

image-20250210163512319

方式 2:Windows(使用 Xshell / PuTTY)

  1. 下载 Xshell
  2. 使用 SSH 连接
ssh root@你的服务器IP
  1. 输入密码

    image-20250210163830536

📦 第 3 步:安装 Node.js、PM2、Nginx

  1. 更新服务器
# 更新 linux 系统本地软件包索引并更新所有可以升级的软件包
apt update && apt upgrade -y

image-20250210164330422

image-20250210164616732

image-20250210164639518

image-20250210165116654

  1. 安装 Node.js(使用 nvm)
# 安装 nvm
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.4/install.sh | bash
# 让上面的安装更改立即生效,无需推出终端
source ~/.bashrc

# 安装 node LTS 长期支持版本
nvm install --lts
node -v # 检查是否安装成功

image-20250210164820927

  1. 安装 PM2(管理 Node.js 进程)
# 更新 npm
npm install -g npm
# 安装 PM2 管理 node 进程
npm install -g pm2
# 设置 pm2 开机自启
pm2 startup
# 冻结进程,当服务器重启的时候, pm2 会自动恢复这些进程
pm2 save

image-20250210165621914

image-20250210170720824

image-20250210170741668

  1. 安装 Nginx
# 更新 nginx,直接点击回车键 enter 继续就行
apt install nginx -y
# 启用 nginx 服务
systemctl enable nginx
# 启动 nginx
systemctl start nginx
nginx -v # 检查版本

image-20250210171602836

📂 第 4 步:上传 React 项目

  1. 安装 Git
apt install git -y

image-20250210174556540

  1. 克隆你的 React 项目

    信息

    拉取项目的时候需要登录你的 github 账户,首先去创建一个 personal token 用于验证

    1. 登录 github

    2. 创建 token,确保有 repo 的权限

      在 settings / Developer Settings / Personal access tokens / Tokens (classic) / Generate new token / Generate new token (classic) 创建 token

    3. 创建完毕之后复制保存这个 personal token,用于登录服务器操作仓库代码

    image-20250211111147026

    image-20250211111234993

    image-20250211111119955

cd /var/www
git clone https://github.com/your-repo.git surprise-gift-frontend
cd surprise-gift-frontend
  1. 安装依赖 & 构建
npm install
npm run build

🚀 第 5 步:使用 PM2 启动 React

  1. 进入构建目录
cd /var/www/surprise-gift-frontend
# 我这里项目打包出来的目录是 .next,如果你的是 dist,需要修改一下
  1. 使用 serve 启动 React
# 安装 serve
npm install -g serve

# 执行打包命令
npm run build

# 使用 serve 和 pm2 启动项目的 start 命令
pm2 start serve --name surprise-gift-frontend -- -s -l 3000
pm2 start npm --name surprise-gift-frontend -- build


# 保存进程到 pm2
pm2 save
  1. 设置开机自启
pm2 startup

🌐 第 6 步:配置 Nginx 反向代理

1️⃣ 创建 Nginx 配置

nano /etc/nginx/sites-available/surprise-gift-frontend

2️⃣ 添加以下内容

server {
listen 80;
server_name your-domain.com;

location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}

3️⃣ 启用配置

# 设置软链接,这样就不需要手动复制配置文件到 sites-enabled(注意:如果要删除配置文件的时候,两个位置的配置文件都要删除,否则执行 nginx -t 的时候会报错)
ln -s /etc/nginx/sites-available/surprise-gift-frontend /etc/nginx/sites-enabled/
nginx -t # 检查配置是否正确
systemctl restart nginx

🔒 第 7 步:配置 HTTPS(可选)

1️⃣ 安装 Certbot(免费 SSL 证书)

apt install certbot python3-certbot-nginx -y

2️⃣ 申请 HTTPS 证书

certbot --nginx -d your-domain.com

如果成功,Nginx 配置会自动更新,网站会支持 HTTPS。 3️⃣ 配置自动续期

certbot renew --dry-run

🔖 本地可访问,外部无法访问: 检查防火墙

确保你当前使用的端口允许访问

# 确保服务器的 80 端口开放,可以通过一下命令检查
sudo ufw allow 80
sudo ufw allow 80/tcp # 允许 80 端口
sudo ufw allow 3000/tcp # 允许 3000 端口

sudo ufw status # 检查防火墙状态
信息

如果在服务器防火墙都没有设置的情况下,依旧无法访问站点,但是本地可以正常访问,检查服务器的安全组设置是否没有开放对应的端口

image-20250211112631430

image-20250211112653573

🧐 关于域名解析

  1. 购买一个 域名之后,将服务器的公网 ip 映射到这个域名上

    image-20250211113106827

  2. 设置多级域名访问主域名

    image-20250211113312430

    这样在服务器的 nginx 配置中 server_name 这一项可以输入多个配置好的 域名解析,都支持访问你的站点

    e.g. server_name yanammm.top www.yanammm.top

👀 域名备案

在站点还没有完成之前可以直接用 ip:端口的方式来访问,然后再去申请备案即可

✅ 最终测试

  1. 在浏览器输入:
http://你的服务器IP

# 或
https://your-domain.com

你应该能看到你的 React 项目成功运行!🎉

  1. 检查 PM2 进程是否正常
pm2 list

如果 react-app 进程是 online,说明项目在运行。

shopify 启动失败

· 阅读需 1 分钟
Yana Ching
Front End Engineer

问题

 request to https://xxxxxx.myshopify.com/admin/api/unstable/themes/xxxxxxxxxx.json?fields=id%2Cname%2Crole%2Cprocessing failed
reason: Client network socket disconnected before secure TLS connection was established

解决方法

安装一个 clash,用 TUN Mode 模式来解决这个问题

download clash for windows

安装完毕之后,如果启动了但是还是没办法上网,关闭 clash 之后用管理员身份重新打开

使用 TUN Mode 之前需要先安装 services

image-20240514114553853

image-20240514114645296

image-20240514114702529

接着启动 TUN 模式

image-20240514114805305

开启或关闭代理

image-20240514114834967

提示

然后重启启动 shopify 项目即可(必要情况下可关闭终端,重新创建一个终端)如果启动之后发现还是不行的话, 有可能是尝试的多个 vpn 软件没有完全关闭,只能保留一个

关闭进程小 tips

image-20240523094351592

image-20240523094607238

MERN 全栈项目搭建

· 阅读需 16 分钟
Yana Ching
Front End Engineer

技术选型

  • 前端框架:React
  • 后端框架:Express.js + Node.js
  • 数据库:MongoDB

预期的项目结构

my-mern-project/   # 项目根目录

├── backend/ # 后端代码目录
│ ├── models/ # 数据模型目录
│ ├── routes/ # 路由目录(将 controllers 和 routes 合并到同一个目录,便与后续维护)
│ │ ├── userRoutes.js
│ │ └── userController.js
│ ├── config/ # 配置文件目录
│ ├── index.js # 后端入口文件
│ └── package.json # 后端依赖管理文件

├── frontend/ # 前端代码目录
│ ├── public/ # 静态资源目录
│ ├── src/ # 源代码目录
│ │ ├── components/ # React 组件目录
│ │ ├── pages/ # 页面组件目录
│ │ ├── App.js # React 应用入口文件
│ │ └── index.js # React 应用渲染入口文件
│ └── package.json # 前端依赖管理文件

└── README.md # 项目说明文档

项目搭建步骤

  1. 新建文件夹 my-mern-project 保存前后端项目
  2. 新建前后端项目根目录 /frontend, /backend
  3. 在 frontend、backend 中各自用脚手架初始化项目
  4. 分别优化前后端项目目录结构
  5. 基于优化的项目结构启动项目并联调协作
# 新建项目文件夹
mkdirmkdir my-mern-project
cd my-mern-project

# 脚手架初始化前端项目,会创建名为 frontend 的 react 项目
npx create-react-app@latest frontend

# 全局安装 express-generator
npm install -g express-generator
# 脚手架初始化后端项目,会创建名为 backend 的后端项目
express backend --no-view

至此项目结构已经基本搭建起来了,接下来是按照预期的项目结构来优化一下目录结构

后端项目结构优化

└─── backend/       # 后端代码目录
├── models/ # 数据模型目录
├── routes/ # 路由目录(将 controllers 和 routes 合并到同一个目录,便与后续维护)
├── config/ # 配置文件目录
├── index.js # 后端入口文件
└── package.json # 后端依赖管理文件

routes/index.js 统一管理路由,让路由配置更为集中和清晰

routes/index.js

// routes/index.js

const express = require('express')
const router = express.Router()

const userRoutes = require('./userRoutes')
const postRoutes = require('./postRoutes')

router.use('/users', userRoutes)
router.use('/posts', postRoutes)

module.exports = router

routes/userRoutes.js

// userRoutes.js

const express = require('express')
const router = express.Router()
const { getUser, createUser } = require('./userController')

// GET 请求处理
router.get('/', getUser)

// POST 请求处理
router.post('/', createUser)

// 导出路由对象
module.exports = router

routes/userController.js

// userController.js

// 中间件函数 - 获取用户
const getUser = (req, res) => {
// 中间件处理逻辑
res.send('GET request to the homepage')
}

// 中间件函数 - 创建用户
const createUser = (req, res) => {
// 中间件处理逻辑
res.send('POST request to the homepage')
}

// 使用ES6解构导出中间件函数
module.exports = {
getUser,
createUser,
}
提示

postRoutes.js postController.js 的初始化文件结构也基本一致,只是修改一下变量名称

数据库与环境变量配置

安装 dotenv 创建 .env 文件配置全局环境变量

# .env

DB_HOST=localhost
DB_PORT=27017
DB_NAME=mydatabase
DB_USER=myusername
DB_PASS=mypassword

config/env.js 中引入环境变量,配置各个环境的启动参数

// config/env.js

require('dotenv').config()

module.exports = {
development: {
PORT: process.env.PORT || 3000,
MONGO_URI: `mongodb://${process.env.DB_USER}:${process.env.DB_PASS}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`,
// 其他开发环境配置...
},
test: {
PORT: process.env.PORT || 4000,
MONGO_URI: `mongodb://${process.env.DB_USER}:${process.env.DB_PASS}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`,
// 其他测试环境配置...
},
production: {
PORT: process.env.PORT || 5000,
MONGO_URI: process.env.MONGODB_URI,
// 其他生产环境配置...
},
}
提示

注意:这里测试环境和开发环境的本地环境配置脚本和上面的 .env 一致

生产环境的话只需要配置 MONGODB_URI=xxxxxx 这一项就可以了

随后在 config/database.js 中引入环境变量,根据不同环境自动读取环境变量

// config/database.js

const envConfig = require('./env')

// 获取当前环境
const ENV = process.env.NODE_ENV || 'development'

// 根据当前环境加载对应的数据库配置信息
const config = envConfig[ENV]

module.exports = config

接下来在 utils/database.js 中创建数据库的连接函数

// utils/database.js

const mongoose = require('mongoose')

const connectDB = async (mongoURI) => {
try {
await mongoose.connect(mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
console.log('MongoDB connected successfully')
} catch (error) {
console.error('Error connecting to MongoDB:', error.message)
process.exit(1) // Exit process with failure
}
}

module.exports = connectDB

最终在入口文件 app.js 中引入配置信息

var express = require('express')
var path = require('path')
var cookieParser = require('cookie-parser')
var logger = require('morgan')

const routes = require('./routes')
// 引入数据库配置
const config = require('./config/database')

// 引入数据库连接函数
const connectDB = require('./utils/database')

var app = express()

app.use(logger('dev'))
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))

// 连接数据库
connectDB(config.MONGO_URI)

// 所有以 /api 开头的请求都会交给 routes 对象处理
app.use('/api', routes)

module.exports = app

mongodb 注册、安装与配置

创建数据库,配置数据库密钥信息,更新不同环境中的环境变量

注册账号,并登录账号

MongoDB: The Developer Data Platform | MongoDB 注册链接

image-20240508223034299

image-20240508223112917

登录完毕之后会看到这样一个界面

image-20240508223218649

登陆完毕先创建一个项目

image-20240508223345552

image-20240508223448166

image-20240508223738591

创建一个免费的云端服务器(512M)

image-20240508224027209

image-20240508224530680

image-20240508224658058

image-20240508225056034

服务器创建完毕之后,创建一个数据库登录账户

image-20240510215608416

image-20240510220006592

image-20240510220429569

设置 IP 访问白名单(选填)

image-20240510220815173

image-20240510220829873

提示

用户账户创建完毕之后,接下来安装图形化界面客户端来连接云服务器

image-20240510221433783

image-20240510221515490

image-20240510221629996

下载 mongodb 客户端

提示

可以直接点击连接安装

🔗MongoDB 安装地址

提示

也可以在 databse 中点击连接,有支持直接安装的连接

下载完毕之后安装客户端

image-20240508220051603

image-20240508220233936

image-20240508220259435

image-20240508220344806

image-20240508220432257

image-20240508220524359

image-20240508220537103

image-20240508220548855

image-20240508220601930

image-20240508222052586

获取链接并且连接到云服务器

image-20240510222040394

image-20240510222223403

image-20240510222259940

提示

连接成功则表示现在设置的这个链接是确实可以连接到云服务器的,下一步我们要开始创建后端程序,更多的还是通过 API 来操作数据 库

生产环境与开发环境

# e.g. mongodb+srv://admin:kkkkkkkkkk@yyyyyyyy.hhhhhh.mongodb.net/

# 对应的 .env 文件的配置如下

# DB_HOST=yyyyyyyy.hhhhhh.mongodb.net
# DB_PORT=27017
# DB_NAME=<改成你自己创建的数据库名字>
# DB_USER=admin
# DB_PASS=kkkkkkkkkk

如果是本地开发的话,直接启动 dev 模式,然后用 compass 连接本地的数据库就可以实时看到最新的数据库数据总览了

image-20240511172301832

image-20240511172316441

后端项目中初始化数据库配置

首先进入项目根目录,访问 ./backend/package.json 文件,确定项目是否已经安装了 mongoose 依赖

// package.json
{
"name": "backend",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"dotenv": "^16.4.5",
"express": "~4.16.1",
"mongoose": "^8.3.4",
"morgan": "~1.9.1"
}
}
提示

如果没有个 mongoose 这个依赖需要手动安装一下

npm install mongoose

# 或者

yarn add mongoose

backend 目录下创建 models 文件夹

  • 创建 index.js 文件,再此处统一引入所有的 model
  • 同级目录下创建各个功能模块的 xxxModel.js,后续使用的时候只需要引入 models 这个文件然后做解构就可以了(这个文件夹中主要 是对各个功能的表做定义)
  • 接着,在 routes/xxxController.js 文件中对各个模块的功能做封装,对应的 xxxRoute.js 中调用 controller 中的方法

models 中文件

// models/index.js

const UserModel = require('./userModel')
const PostModel = require('./postModel')
// 引入其他模块的模型...

module.exports = {
UserModel,
PostModel,
// 导出其他模块的模型...
}
// models/userModel.js

const mongoose = require('mongoose')

// 定义用户模型的数据结构
const userSchema = new mongoose.Schema({
username: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
})

// 创建用户模型并导出
const UserModel = mongoose.model('User', userSchema)

module.exports = UserModel
// models/postModel.js

const mongoose = require('mongoose')

// 定义帖子模型的数据结构
const postSchema = new mongoose.Schema({
title: { type: String, required: true },
content: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
createdAt: { type: Date, default: Date.now },
})

// 创建帖子模型并导出
const PostModel = mongoose.model('Post', postSchema)

module.exports = PostModel

routes 中文件

// controllers/userController.js

const { UserModel } = require('../models')

// 用户控制器
const UserController = {
async createUser(req, res) {
try {
const newUser = await UserModel.create(req.body)
res.status(201).json(newUser)
} catch (error) {
res.status(500).json({ message: error.message })
}
},

async getUser(req, res) {
try {
const userId = req.params.userId
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({ message: 'User not found' })
}
res.status(200).json(user)
} catch (error) {
res.status(500).json({ message: error.message })
}
},

// 其他用户操作方法...
}

module.exports = UserController
// userRoutes.js

const express = require('express')
const router = express.Router()
const { getUser, createUser } = require('./userController')

// GET 请求处理
router.get('/', getUser)

// POST 请求处理
router.post('/:userId', createUser)

// 导出路由对象
module.exports = router

启动项目,用 postman 或者 apifox 来测试接口是否可以正常访问

项目搭建

利用常用的脚手架 create-react-app 和 express-generator 快速搭建项目

# 创建后端项目目录
mkdir surprise-box
cd suprise-box

# 使用 create-react-app 创建 react 项目
npx create-react-app client

# 使用 express-generator 创建 express 后端
npx express-generator server
cd server
# 在 server 文件夹中安装依赖
npm install

# 回到项目根目录
cd ../

# 将 server 文件夹中的所有文件和子目录全部拷贝到项目根目录
cp -r server/* .

# 删除原来的 server 目录
rm -rf server

调整前端配置:支持前后端通信

提示

进入 client 文件夹,打开 package.json 文件更新 scripts 脚本

{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
+ "proxy": "http://localhost:5000/"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

项目启动

# 根目录下启动 express 后端
npm start

# 进入 client 目录启动前端项目
npm start

设置快速启动脚本

在项目根目录下新建 start.sh 文件

#!/bin/bash

# 启动 Express.js 后端
echo "Starting Express.js backend..."
yarn start &

# 等待 Express.js 后端启动完成
sleep 5

# 进入 client 目录并启动 React 前端
echo "Starting React frontend..."
cd client
yarn start

项目配置 mongodb

  1. 安装 mongodb 依赖
yarn add mongodb
  1. 根目录下创建 config 文件夹保存配置文件

config/dbConfig.js 设置数据库配置信息

module.exports = {
// 这是 MongoDB 连接字符串 URI
uri: 'mongodb://localhost:27017/mydatabase',
// 使用最新的连接器和拓扑检测器
options: { useNewUrlParser: true, useUnifiedTopology: true },
}
  1. 在 express 的 app.js (即根目录下的 app.js)中引入 dbConfig 配置文件
// 引入所需模块
const createError = require('http-errors') // 用于创建 HTTP 错误
const express = require('express') // Express 框架
const path = require('path') // 处理文件路径
const cookieParser = require('cookie-parser') // 解析 cookie
const logger = require('morgan') // HTTP 请求日志记录器

// 引入 MongoDB 相关模块
const mongoose = require('mongoose') // MongoDB 驱动程序

// 引入路由模块
const indexRouter = require('./routes/index') // 主页路由
const usersRouter = require('./routes/users') // 用户路由

// 创建 Express 应用程序实例
const app = express()

// 连接 MongoDB 数据库
mongoose
.connect('mongodb://localhost:27017/mydb', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('MongoDB 连接成功'))
.catch((err) => console.error('MongoDB 连接失败:', err))

// 设置视图引擎和视图文件夹路径
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'jade')

// 使用中间件
app.use(logger('dev')) // 使用 morgan 记录日志
app.use(express.json()) // 解析 JSON 请求体
app.use(express.urlencoded({ extended: false })) // 解析 URL 编码的请求体
app.use(cookieParser()) // 解析 cookie
app.use(express.static(path.join(__dirname, 'public'))) // 静态文件服务

// 使用路由
app.use('/', indexRouter) // 主页路由
app.use('/users', usersRouter) // 用户路由

// 处理 404 错误并转发到错误处理程序
app.use(function (req, res, next) {
next(createError(404)) // 创建 404 错误并传递给错误处理程序
})

// 错误处理程序
app.use(function (err, req, res, next) {
// 设置局部变量,仅在开发环境中提供错误信息
res.locals.message = err.message
res.locals.error = req.app.get('env') === 'development' ? err : {}

// 渲染错误页面
res.status(err.status || 500) // 设置响应状态码
res.render('error') // 渲染 error.jade 视图
})

// 导出 Express 应用程序实例
module.exports = app
  1. 优化一下 app.js 文件

将路由、数据库、中间件的配置都抽离出来,以便后续维护

/* app.js */
// 引入所需模块
const express = require('express')
const path = require('path')
const logger = require('morgan')

// 引入配置模块
const dbConfig = require('./config/database')
const routesConfig = require('./config/routes')
const middlewaresConfig = require('./config/middlewares')

// 创建 Express 应用程序实例
const app = express()

// 配置数据库连接
dbConfig.connect()

// 设置视图引擎和视图文件夹路径
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'jade')

// 使用中间件
app.use(logger('dev'))
middlewaresConfig.setup(app)

// 配置路由
routesConfig.setup(app)

// 导出 Express 应用程序实例
module.exports = app
/* db.js */
// 引入所需模块
const mongoose = require('mongoose')

// 数据库连接配置
const dbConfig = {
url: 'mongodb://localhost:27017/mydatabase', // MongoDB连接URL
connect: function () {
mongoose
.connect(this.url, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('MongoDB connected'))
.catch((err) => console.error('MongoDB connection error:', err))
},
}

// 导出数据库连接配置
module.exports = dbConfig
/* middlewares */
// 引入所需模块
const express = require('express')
const cookieParser = require('cookie-parser')
const path = require('path')

// 中间件配置
const middlewaresConfig = {
setup: function (app) {
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, '../public')))
// 可以添加更多中间件配置
},
}

// 导出中间件配置
module.exports = middlewaresConfig

MongoDB 数据库安装

未完待续

shopify项目设置自动化部署

· 阅读需 4 分钟
Yana Ching
Front End Engineer

Question

  1. 原生的 shopify 主题开发中引入了 tailwindwebpack 等编译工具,本地开发的时候控制台一直告警
  2. 在根目录中引入除了主题文件以外的其他文件时,无法使用 shopify theme push 等 shopify/cli 工具来预览在线开发主题
  3. 有时候在网页编辑器中修改主题之后,直接用 shopify theme pull 来拉取主题,会直接覆盖/移除本地其他重要文件
  4. 开发和用户环境有冲突,开发期间,shopify 店铺有数据增减,与 shopify 店铺直连的分支会接收来自店铺的一些更新,这部分更新 需要可以更新到开发分支

解决方案

  1. 维护一个单独的目录来存放主题文件,支持上传、下拉主题文件
  2. 创建 action 自动化部署来清理出一个干净的分支同步 shopify 店铺当前在线主题
  3. 同时在 dev 项目的 shopify 文件夹中维护另一个 github 地址来更新最新的 main 分支

具体操作

创建 Github Actions 工作流

github 仓库中,根目录中创建 .github/workflows/sync-shopify-to-main.yml 目录

name: Sync Shopify Folder to Main

on:
push:
branches:
- dev

jobs:
sync-shopify:
runs-on: ubuntu-latest

steps:
- name: Checkout dev branch
uses: actions/checkout@v2
with:
ref: dev

- name: Copy Shopify folder
run: |
mkdir shopify_copy
cp -R ./shopify/* shopify_copy/

- name: Checkout main branch
uses: actions/checkout@v2
with:
ref: main
persist-credentials: false

- name: Copy files to main
run: |
cp -R shopify_copy/* .

- name: Configure git
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'

- name: Commit changes
run: |
git add .
git commit -m "Sync Shopify folder from dev branch"
git push origin main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  1. 触发条件:工作流在 dev 分支有推送事件时触发
  2. Checkout dev 分支:使用 actions/checkout@v2 操作从 dev 分支检出代码
  3. 复制 Shopify 文件夹:将./shopify 文件夹中的所有内容复制到一个临时目录 shopify_copy 中
  4. Checkout main 分支:再次使用 actions/checkout@v2 操作,这次检出 main 分支,并取消 persist-credentials 以防止凭证问题
  5. 复制文件到 main:将 shopify_copy 中的内容复制到当前工作目录(即 main 分支的根目录)
  6. 配置 Git:设置 Git 用户信息,以便 GitHub Actions 机器人能够提交更改
  7. 提交更改:添加、提交并推送更改到 main 分支。这里使用了 GitHub 提供的 GITHUB_TOKEN,该令牌由 GitHub Actions 自动提供, 并在运行工作流时注入到环境中

Shopify Liquid

· 阅读需 6 分钟
Yana Ching
Front End Engineer

组成

object 对象

{{ page.title }}

output: Introduction

tag 标记

所有的逻辑都是用 {%%} 包裹的可以用来 变量赋值条件判断循环

{% if user %}
Hello {{ user.name }}
{% endif %}

filter 过滤器

使用 | 过滤输出内容

{{ '/my/fancy/url' | append: '.html' }}

output: /my/fancy/url.html

多个过滤器的时候,按从左到右的顺序执行
{{ 'adam!' | capitalize | prepend: 'Hello ' }}

output: Hello Adam!

操作符

八个基本操作符和一个 contains

  • == 相等
  • != 不相等
  • > 大于
  • < 小于
  • >= 大于或等于
  • <= 小于或等于
  • or 逻辑或
  • and 逻辑与
  • contains 只能用来搜索字符串
{% if product.title contains 'Pack' %}
This product's title contains the word Pack
{% endif %}

真值与假值

nilfalse 之外都是 true

数据类型

String、Number、Boolean、Nil、Array

数组的初始化只能要通过 split 过滤器将一个字符串分割成一个子字符串数组

控制输出的空白字符

使用变量定义语句,如果使用 {% %} 包裹语句,这行定义语法也会渲染出来,表现是多了一个空行

要解决这个问题就是 在 {% %} 之间加上 - 变成 {%- -%} 这样就不会出现空白行了

{% assign username = "John G. Chalmers-Smith" %}
{% if username and username.size > 10 %}
Wow, {{ username }}, you have a long name!
{% else %}
Hello there!
{% endif %}

output:


Wow, John G. Chalmers-Smith, you have a long name!






{%- assign username = "John G. Chalmers-Smith" -%}
{%- if username and username.size > 10 -%}
Wow, {{ username }}, you have a long name!
{%- else -%}
Hello there!
{%- endif -%}

output: Wow, John G. Chalmers-Smith, you have a long name!

tags

注释

{% comment %}
/*这是注释内容*/
{% endcomment %}

控制流

if - 条件成立时候执行

{% if product.title == 'Awesome Shoes' %}
These shoes are awesome!
{% endif %}

unless - 条件不成立的时候执行

{% unless product.title == 'Awesome Shoes' %}
These shoes are not awesome.
{% endunless %}


{%comment 等同于 endcomment%}
{% if product.title != 'Awesome Shoes' %}
These shoes are not awesome.
{% endif %}

elsif/else

{% assign handle = 'cake' %}
{% case handle %}
{% when 'cake' %}
This is a cake
{% when 'cookie' %}
This is a cookie
{% else %}
This is not a cake or a cookie
{% endcase %}

case/when

迭代/循环

for

for-in-endfor

break 停止循环

continue 跳出当前循环

for(parameters)

limit 限制循环次数 offset 指定循环开始索引号 range 指定循环范围 reversed 反转循环

filters

  • abs 绝对值

  • append 拼接

  • at_least 限制最小值

  • at_most 限制最大值

  • capitalize 首字母大写

  • compact 删除数组中所有 nil

  • concat 拼接数组

  • date 时间戳格式转化(注意 now 是生成页面的时间,而不是用户浏览时的时间)

  • default 默认值(为空时候使用)

  • divided_by 除法(结果的数据类型与除数保持一致)

  • minus: a 减去 a

  • modulo: a 取模

  • plus: a 加上 a

  • times: a 乘以 a

  • downcase 转小写

  • upcase 大写

  • first 数组第一项

  • last 数组最后一项

  • floor 去小数点后位数

  • ceil 向上取整 3.1+ --> 4

  • join:'key' 用 key 连接所有子项并输出字符串

  • lstrip 删除左侧空白符

  • rstrip 删除右侧空白符

  • strip 删除两侧空白符

  • strip_html 删除所有 html 标签

  • strip_newlines 删除所有换行符号

  • map:'key' 获取对象中属性名为 key 的值

  • newline_to_br 把空行转成
    标签

  • prepend:'a' 字符串之前加上 a

  • remove:'a' 删除 a 字符

  • remove_first: 'a' 删除首次出现的 a 字符

  • replace:'a','b' 将所有 a 换成 b

  • replace_first:'a','b' 将首次出现的 a 换成 b

  • reverse 反转数组

  • round 四舍五入

  • size 字符长度或者数组长度

  • slice:start,len 一个数值的时候直接截取该索引位置,负数则从末尾开始向前计数

    正向初始索引是 0,反向初始索引是 -1

  • sort 排序,区分大小写(先大写)

  • split:',' 用, 分割字符串为数组

  • truncate:maxlen,affix 限制长度,并添加尾缀,如果长度小于 len,尾部自动补上省略号

  • truncatewords 同上,但是截断是字符个数(英文单词有区别)

  • uniq 删除重复项

  • sort_natural 排序,不分大小写

  • url_decode 解码 URL

  • url_encode 编码 URL

  • escape 字符转义(才能用于 URL)

  • escape_once 对于已转义的字符不做处理

shopify Themekit开发

· 阅读需 11 分钟
Yana Ching
Front End Engineer

开发前准备

  1. 注册一个 shopify partner 的账户

  2. 登录到用户后台 dashboard 中去,后台登陆的格式大概是 https://partners.shopify.com/xxxxx?signup_intent=xxx

    init shopify partner

  3. 安装 @shopify/cli 和 @shopify/theme (这是基于已经有的项目做的初始化操作)

  4. 运行 shopify 项目需要有 Ruby 的环境,所以还需要安装一下 Ruby(首选推荐安装 with devkit 版本的 Ruby)

关于 shopify 的 theme 与 app

theme 与 app 有不同的职责和作用:

信息

主题(Theme):主题是定义了商店外观和布局的模板文件集合。在 Shopify 主题开发中,您主要是对页面的 HTML 结构和 Liquid 模板 进行操作,以实现商店的视觉效果和布局。可以使用 HTML 和 Liquid 模板语言来动态生成页面内容,包括产品列表、购物车、结账页 面等,从而实现页面的视图渲染和交互效果

应用(App):应用是为商店提供特定功能或服务的独立软件。在 Shopify 应用开发中,您可以使用后端技术(如

Node.js、PHP)和前端技术(如 JavaScript、HTML)来开发您的应用。您的应用可以通过 Shopify API 与商店进行交互,例如获取商 店数据、修改产品信息、处理订单等。应用通常是独立于主题的,可以实现更多的功能和定制选项。

总的来说,主题和应用在 Shopify 中具有不同的功能和作用。

  • 主题主要负责定义商店的外观和布局
  • 应用则负责为商店提供特定的功能和服务

通过主题和应用的结合,您可以为商店提供丰富多样的功能和个性化的外观,从而满足不同商家的需求。

创建一个开发商店

image-20240410115518430

image-20240410120045640

提示

商店创建完毕之后可以直接 install 一个默认的 theme,点击小眼睛就可以预览店铺效果了

image-20240410134904532

开发中的店铺有一个访问密码,可以把密码提供给团队进行写作

image-20240410135205678

点击 customize 可在图形化操作界面修改店铺

提示

在这里可以快速定位模块所处的文件位置

image-20240410142059896

安装 shopify themekit 更好的管理 shopify 主题

theme get --password=<PASSWORD> --store=xxxx.myshopify.com --themeid=xxxxx
  1. 在开发主题之前,必须生成一个 API key 去连接整个商店

  2. 进入 partner 账户后台,点击 Apps,点击 create app,然后用 shopify partner 来创建 app:create app manually(通过 app 的密钥来开发主题)

    image-20240411090938883

    image-20240411092512487

    image-20240411092526210

    image-20240411092808977

  3. 创建成功之后会直接进入该 app 的详情页面,点击 overview 查看这个 app 的 client secret(这就是 themekit 中所需要的 password 的值,直接拷贝使用)

    image-20240411093116582

  4. 至于 store 这个值,点击店铺预览效果,新打开的窗口链接中就是我们想要的 store 的值

    image-20240411093308187

    image-20240411093335554

  5. 接下来是关于 themeid 的属性值获取,点击 online store,点击想要的编辑的主题的 customize 按钮,跳转的链接中就可以 直接获取这个值

    image-20240411093606390

    image-20240411093622490

  6. 获取了以上数据之后就可以使用 themekit 的 api 来 获取和编辑 主题了

    theme get --password=<换成你的 clientID> --store=<换成你的 store 域名> --themeid=<换成你需要编辑的主题id>

theme 开发——发布

  1. 安装 @shopify/cli

    npm install -g @shopify/cli @shopify/theme
  2. 获取主题代码

提示

本地创建主题

使用 @shopify/cli 创建本地主题

shopify theme init
# 会要求你输入主题的名称

cd <your-theme-name>
提示

拉取店铺主题进行开发

shopify theme pull -store <store-name>
  1. 本地运行
shopify theme dev --store <your-store-name>

# 启动完毕之后直接在命令行随便点击一下就可以获得本地预览的地址
# 一般情况下,在 9292 端口不被占用的时候,都是 使用 http://localhost:9292 OR http://127.0.0.1:9292
  1. 主题上传
shopify theme push --unpublished
# 这是将目前主题当作首次上传、未发布的主题

shopify theme push
# 这是针对已经上传的主题,更新主题的代码
  1. 发布主题

    PS: 一定要确保主题在本地开发完毕之后,才能发布主题

shopify theme publish

工程化项目

安装 laravel mix(webpack 包装器)做代码优化

# 安装开发依赖
npm install laravel-mix --save-dev

# 项目根目录下,创建 webpack.mix.js 配置文件
touch webpack.mix.js

配置 webpack.mix.js 文件,处理 js、scss 文件

根目录下创建以下文件

├── src
└── js
└── app.js
└── scss
└── app.scss
let mix = require('laravel-mix')

mix.js('src/js/app.js', 'assets').sass('src/scss/app.scss', 'assets')
// .js('src/js/app.js', 'assets') 从入口文件 app.js 中进入,处理文件到 assets 目录
// .sass('src/scss/app.scss', 'assets') 从样式文件 app.scss 中进入,处理文件到 assets 目录

app.scss 中重置网页 css

body {
font-family: 'Open Sans', sans-serif;
font-size: 16px;
line-height: 1.5;
color: #333;
background-color: #fff;
}

启动编译

#  启动编译的同时,会自动安装 sass-loader 、sass、resolve-url-loader 三个loder 处理器
npx mix

# 热更新编译
npx mix --watch

$ npx mix
Additional dependencies must be installed. This will only take a moment.

Running: yarn add sass-loader@^12.1.0 sass resolve-url-loader@^5.0.0 --dev

Finished. Please run Mix again.
  • sass-loader 加载和转换 scss 文件
  • resolve-url-loader 处理 css 相对路径(sass 中的 url())
  • sass sass 预处理器,用来编写样式文件的

安装 tailwind

# 安装依赖
yarn add tailwindcss -D

# 初始化,获得一个配置文件 tailwind.config.js
npx tailwindcss init

# 如果报错 postcss not found,需要再安装一个 postcss

tailwindcss 配置 tailwind.config.js

module.exports = {
// 监听文件变化来生成对应的 css
content: [
'./config/*.json',
'./layout/*.liquid',
'./assets/*.liquid',
'./sections/*.liquid',
'./snippets/*.liquid',
'./templates/*.liquid',
'./templates/*.json',
'./templates/customers/*.liquid',
],
}

设置 larevel-mix 对 tailwind 代码做处理

覆盖前面的 css 重置代码

/* app.scss */
@tailwind base;
@tailwind components;
@tailwind utilities;

同时需要在 webpack.mix.js 中引入 tailwind,让 webpack 处理 tailwind 的样式文件

/* webpack.mix.js */
let mix = require('laravel-mix')

const tailwindcss = require('tailwindcss')
mix
.js('src/js/app.js', 'assets')
.sass('src/js/app.scss', 'assets')
.options({
processCssUrls: false, // 禁止对 css 中的 url 做处理
postCss: [tailwindcss('tailwind.config.js')], // 处理 css 文件的时候使用postcss插件,其中包含 tailwind css
})

引入 daisyUI 补充组件库

npm i -D daisyui@latest

配置到 tailwind 中去使用

/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./config/*.json',
'./layout/*.liquid',
'./assets/*.liquid',
'./sections/*.liquid',
'./snippets/*.liquid',
'./templates/*.liquid',
'./templates/*.json',
'./templates/customers/*.liquid',
],
+ theme: {
+ extend: {},
+ screens: {
+ mb: { max: '599px' }, // 移动端
+ tb: { min: '600px', max: '1365px' }, // 平板
+ pc: { min: '1366px' }, // pc
+ },
+ },
+ plugins: [require('daisyui')],
+ daisyui: {
+ prefix: 'ds',
+ },
}

安装并引入 Apline(jquery)

yarn add alpinejs
/* app.js*/

import Alpine from 'alpinejs'
window.Alpine = Alpine

Alpine.start()

theme.liquid 中引入样式文件、js 文件

随后在 theme.liquid 文件中使用输出的 assets/app.js assets/app.css

{% comment %} 针对不需要做 defer、async 操作的脚本直接用 liuqid 标签引入 {% endcomment %}
{{ 'app.css' | asset_url | stylesheet_tag }}

<link rel="stylesheet" href="{{ 'app.css' | asset_url }}" type="text/css">


{{ 'app.js' | asset_url | script_tag }}

  <script src="{{ 'app.js' | asset_url }}" defer></script>

<script>
// 创建一个 link 元素
var link = document.createElement('link');
// 设置 rel 属性为 stylesheet
link.rel = 'stylesheet';
// 设置 href 属性为 CSS 文件的 URL
link.href = 'path/to/your/stylesheet.css';
// 设置 onload 事件处理函数,当样式表加载完成后执行
link.onload = function() {
console.log('Stylesheet loaded successfully.');
};
// 使用 setTimeout 来延迟加载样式表,这里设置为 3000 毫秒(3 秒)
setTimeout(function() {
// 将 link 元素添加到页面的 head 部分
document.head.appendChild(link);
}, 3000); // 3 秒延迟加载
</script>

轮播组件 swiper 引入

  1. 安装依赖
yarn add swiper
  1. 配置 webpack.mix.js
const mix = require('laravel-mix')
const tailwindcss = require('tailwindcss')

mix
.js('src/js/app.js', 'assets')
.sass('src/scss/app.scss', 'assets')
+ .copyIfNotExist(
+ 'node_modules/swiper/swiper-bundle.min.css',
+ 'assets/css/swiper-bundle.min.css'
+ )
+ .copyIfNotExist(
+ 'node_modules/swiper/swiper-bundle.min.js',
+ 'assets/js/swiper-bundle.min.js'
+ )
.options({
processCssUrls: false, // 禁止对 css 中的 url 做处理
postCss: [tailwindcss('tailwind.config.js')], // 处理 css 文件的时候使用 postcss 插件,其中包含 tailwind css
})

  1. shopify theme.liquid 主题文件中引入
<link rel="stylesheet" href="{{ 'assets/css/swiper-bundle.min.css' | asset_url }}">
<script src="{{ 'assets/js/swiper-bundle.min.js' | asset_url }}"></script>

引入文件的优先级

CSS 文件

  • 重要的全局样式表
  • shopify 主题样式表
  • 三方样式表
{{ 'assets/css/reset.css' | asset_url | stylesheet_tag }}
{{ 'assets/css/theme.css' | asset_url | stylesheet_tag }}
{{ 'assets/css/third-party.css' | asset_url | stylesheet_tag }}

JS 文件

  • 引入 Alpine/jQuery 或者其他类库
  • shopify 主题的 JS
  • 三方 JS
{{ 'assets/js/jquery.min.js' | asset_url | script_tag }}
{{ 'assets/js/theme.js' | asset_url | script_tag }}
{{ 'assets/js/third-party.js' | asset_url | script_tag }}

优化 Tailwind CSS 的构建

使用 laravel-mix-tailwind 插件,它可以优化 Tailwind CSS 的构建过程

npm install laravel-mix-tailwind --save-dev

shopify 主题开发

· 阅读需 2 分钟
Yana Ching
Front End Engineer

初始化项目

  1. 安装脚手架 @shopify/cli
npm install -g @shopify/cli
  1. 初始化项目,以 dawn 初始开始开发
shopify theme init
提示

如果这个步骤失败的话,则直接登录 shopify 账户,初始化店铺的主题之后,手动下载主题代码

点击下载按钮之后,你会在邮箱收到一个主题文件,下载即可使用

image-20240421223100207

image-20240421223224667

  1. 本地开发预览
shopify theme dev --store <your-store-name>
  1. 以未发布状态上传主题
shopify theme push --unpublished
  1. 直接更新主题代码
shopify theme push
  1. 发布主题
shopify theme publish

git 仓库关联

# 初始化仓库
git init

# 在github 手动创建仓库之后,复制仓库地址
# 关联仓库
git remote add origin <your-repo-address>

# 提交所有更新
git add .
git commit -m 'init: init my shopify theme'

# 推送到远端
git push --allow-unrelated-hostoric origin master

启动已有的 shopify 项目

· 阅读需 2 分钟
Yana Ching
Front End Engineer

环境准备

  1. node

  2. npm/yarn

  3. ruby with devkit

  4. 至少一次登录到 https://xxxxx.myshopify.com/admin 后台(注意不要切换别的账户,否则可能因为缓存问题,会在启动项目的 时候一直报错: no authorized)

  5. 从 github 仓库将项目代码拷贝下来,查看 package.json 文件,关注 scripts 这个属性值,就是定义的项目运行脚本

    {
    "scripts": {
    "aaaa": "shopify theme dev --store bbbb.myshopify.com"
    }
    }

    bbbb.myshopify.com 可替换成你的店铺域名

  6. 安装依赖

    yarn
    或者
    npm install
  7. 执行启动脚本

    yarn aaaa
    或者
    npm run aaaa
  8. 启动完毕之后看到一个 "logged in" 提示,这时候随便按下任意的键,即可看到本地预览的 URL 地址

  9. 至此已经完成了一个已有 shopify 项目的启动了