参考 & 分析

为了稳定性,选择了vue2作为本项目的前端框架。

参考:

掘金-逐步前行:分析小程序的设计方案,优劣势,以及如何技术选型

掘金-kiki_:小程序技术选型

微信开放社区-编程小石头:企业微信小程序的注册图文详解

DCLOUD社区-DCloud_UNI_CHB:跨端框架深度评测:微信原生、wepy、mpvue、uni-app、taro、chameleon

博客园-不是于彬:React与Vue的对比

微信官方文档:小程序框架 /视图层 /WXSS

小程序数据交互设计

img

  • 渲染层:单线程,使用WebView 进行渲染,但一个小程序存在多个页面,可能始终是单线程的,只是切换页面时一个线程暂停一个线程继续
  • 逻辑层:单线程,逻辑层采用JsCore线程运行JS脚本
  • 其中WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层
  • 渲染层逻辑层之间的通讯更接近react native,或者是flutter的设计理念,此设计理念就是,逻辑层与展示层分开,通过中间JSBridge或者是其他存储技术,或者Native本身,完成两者的通信,形成一个逻辑与展示的互相驱动。

小程序运行环境

image-20221030201555537

逻辑层,在IOS中是使用JavaScriptCore为宿主环境,而在安卓中,使用了V8引擎。就连渲染层,都属于定制的内核。这中间不妨包含很多微信内部的封装。

开发者写的所有代码最终将会打包成一份 JavaScript 文件,并在小程序启动的时候运行,直到小程序销毁。这一行为类似 ServiceWorker,所以逻辑层也称之为 App Service。

即使是在微信自带的开发工具上开发,都无法完成跟客户端一模一样的体验。这里微信只是模拟客户端,搭建了一个NWJS环境方便开发调试。

于是:安卓小程序,IOS小程序会有一些差异?或者为什么本地开发时,一些功能不可使用。而App Service,是要依赖于微信客户端定制内核的webview才能执行。看到这里,你是否了解为什么小程序无法在浏览器运行?

但uniapp的小程序可以被编译为H5前端,可运行至浏览器端,但可以理解为只是一个界面而已,大部分为小程序提供的接口还是无法被调用的。

特殊单位

rpx

显示的原理就是把要显示的数据写入显存区域,然后显示设备读取这些数据,驱动硬件就可以显示了。显示的数据是以像素为单位的,一个像素只能显示一种颜色,但是根据显示颜色的总数不同,每个像素占的位数也不同。如果我想显示黑白,那一位就可以存储了,但如果我想显示16种颜色,就得4位来存储一个颜色,这样的一个存储单位就叫做物理像素

分析一下造成显示效果不同的原因就是设备宽度不同,你可能会问,那dpr呢,其实与dpr一点关系都没有,想象一下2个宽度为1000个物理像素的设备,一个dpr为1,一个dpr为2,那么在我们看来不过一个是1000px,一个是500px而已,在这里我们感知不到dpr。那么设备宽度不同怎么做适配呢,其实很容易的会想到,每个设备每行显示的px数不同,你写死px数的话,那肯定显示的效果不一样,所以,不能写死,要动态的计算。对,实际上也是这么解决的,那怎么计算呢,很简单,你把一个设备的样式写好了,其他的根据设备的宽度(px数)的比,来动态计算就行了。

其中参考文献简书-凌霄光:px、物理像素、rem、rpx的关系

参考:

简书-凌霄光:px、物理像素、rem、rpx的关系

CSDN-more名奇妙:前端rem适配如何具体去使用

CSDN-gqkmiss:JavaScript 中 rem 的实现和计算

微信官方文档:小程序框架 /视图层 /WXSS

ZOL:苹果iPhone 6(全网通)

官网解释

rpx(responsive pixel iPhone6可理解为物理像素): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

设备 rpx换算px (屏幕宽度/750) px换算rpx (750/屏幕宽度)
iPhone5 1rpx = 0.42px 1px = 2.34rpx
iPhone6 1rpx = 0.5px 1px = 2rpx
iPhone6 Plus 1rpx = 0.552px 1px = 1.81rpx

建议: 开发微信小程序时设计师可以用 iPhone6(分辨率:1334x750像素) 作为视觉稿的标准。

注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。

以上文献来自微信官方文档:小程序框架 /视图层 /WXSS

显示单位解析

  • 物理像素,即显示器上的一个像素点(比如:显示rgb颜色的一个颗粒)

  • px(pixel) 前端css的px和物理像素可能是不一一对应的,比如调小分辨率,画面会变大,此时就会使一个css像素包含多个物理像素

  • dpr(device pixel radio) 设备显示的 物理像素数/css的px数 就叫做设备像素比,比如1px的点,显示在dpr为1的设备上就是用一个物理像素显示,dpr为2的设备上,会用2个像素来显示一个物理像素(iphone6)

  • rem 1rem是指根元素(root element,html)的font-size,从遥远的 IE6 到版本到 Chrome 他们都约好了,根元素默认的 font-size 都是 16px。但如果我们在开发设备上的宽度为750px(html占满设备宽度),且自定义font-size=100px,但用户的设备上是375px(html占满设备宽度),由此可由公式``document.documentElement.style.fontSize(50) = (document.documentElement.offsetWidth(375)/750)*100 + ‘px’;`计算在用户设备上1rem=50px,等于说最终的效果是约束了整个设计稿(html标签开始结束范围)在不同显示器显示的比例统一,但也要结合dpr,比如iphone6的dpr是2,我们要把量出来的数除以2,就是实际的px,然后再除以100,就是真实rem。

  • rpx 微信小程序开发时用的是前端的技术,类似html的组件标签,css,js,他面临的问题和网页一样,就是不同宽度的设备的适配,解决思路当然也是一样,但是有一点不同的是不能用rem,为什么呢,因为根本就没有html元素啊,咋解决,很简单,我不基于html的font-size了,我基于一个别的值就行了,你也不需要计算这个值,我给你计算了,这就是rpx。最终的效果就是,你开发时在iphon6的设计稿上量了多少px,就写多少rpx就行了,完美适配,perfect!

总结:以我的话讲rpx,其实就是小程序前端底层用rpx规定将所有不同屏幕设备显示的页面都按照比例显示,也就是说任何设备只要是宽度不一致如果使用px会使得显示比例不一致,如果加上rpx约定比例,就会使得任何宽度的设备渲染页面比例一致。

术语

参考:

博客园-遥望那月:windows安装npm教程–nodejs

  • npm: nodejs 下的包管理器。
  • webpack: 它主要用途是通过CommonJS 的语法把所有浏览器端需要发布的静态资源作相应的准备,比如资源的合并和打包。
  • vue-cli: 用户生成Vue工程模板。(帮你快速开始一个vue的项目,也就是给你一套vue的结构,包含基础的依赖库,只需要npm install 就可以安装。

技术栈

Npm & Yarn

为证明一个小的猜想,vue-cil是否包含vue命令,用卸载安装好的@vue/cli@4.5.15,刚开始在~目录用的npm uninstall -g @vue/cli@4.5.15,居然没卸载,后来又回到-g(全局安装目录)才卸载掉@vue/cli@4.5.15,vue是不能执行了,但npm也不能执行了,这npm水是真的深啊,完全是给人乱来,自己把自己都给卸载掉了,怪不得会有Facebook、Google、Exponent 和 Tilde 联合开发yarn!!!也许有可能是我删了~目录下的node_modules有关,但好像没有逻辑。

参考:

Sky03我的薰衣草-Hexo进阶之各种优化

简书-Simbawu:npm和yarn的区别,我们该如何选择?

简书-长城_changcheng:npm常用命令

菜鸟教程:NPM 使用介绍

Yarn官方文档:安装

Yarn:安装

版本

安装NodeJS(本项目默认使用node@16-16.18.0 brew 获得的最新版,但官网v18.12.0已经有了)

image-20221103135433626

image-20221103140439542

提升Npm访问速度

查看当前npm仓库配置

1
npm config get registry

设置国内npm仓库

1
npm config set registry https://registry.npm.taobao.org

Vue脚手架

安装Vue2的脚手架,项目为vue2使用稳定性选择脚手架版本@vue/cli@4.5.15,通俗的理解脚手架就是为了更快的生成一个vue基础项目。

1
npm install @vue/cli@4.5.15 -g
  • -g 全局安装,Mac系统会在/usr/local目录下写入程序
    • /usr/local/lib/node_modules 中含有-g(全局安装)的包
    • /usr/local/Cellar/node/ 放置nodeJS的编译文件
  • 局部安装不加参,安装后会在 ./ 下生成node_modules目录,其中包含软件包。

Vue2

前后端参考开源项目django-vue-admin中其中有一个npm run build命令,在本文章 微服务 => 打包镜像 中的前端DockerFile写有,分析此命令看了网上诸多文章,得出对 Vue-cli build 的分析。

参考:

Vue CLI

CSDN-ZeroMaster:npm install和npm run dev以及npm run build的区别

CSDN-markix:浅析npm run serve命令

segmentfault-A_大白:vue-cli系列之vue-cli-service整体架构浅析。

博客园-zhao379028604:使用vue-cli初始化后运行npm run server到底干了什么

掘金-工具我那都齐
lv-3:说说 webpack-dev-server、webpack-dev-middleware 和 webpack-hot-middleware

掘金-代码与野兽lv-5:前端工资涨不上去?可能是你没掌握构建工具:关于 Webpack、Babel、esbuild、Vite、Rollup、Parcel、SWC……的那些事

知乎-yangqiao:什么是webpack?

Uniapp

关系:NodeJS => Vue => uniapp

参考:

Dcloud-uniapp官方文档

GitHub-dcloudio:hello-uniapp

生成项目模板

1
2
# 可能被墙,导致失败
vue create -p dcloudio/uni-preset-vue mini-app
  • -p 跳过提示,使用保存或远程预设
  • mini-app是项目名称

image-20221029235556481

选择默认模板

项目结构

一个uni-app工程,默认包含如下目录及文件(来自Uniap官网),其中标注*的是Hello uni-app的模板所包含的文件夹/目录,此项目是官方uni-app框架演示示例,编译运行后如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─uniCloud              云空间目录,阿里云为uniCloud-aliyun,腾讯云为uniCloud-tcb(详见uniCloud)
│─src/components *符合vue组件规范的uni-app组件目(同Vue)
│ └─comp-a.vue 可复用的a组件
├─utssdk 存放uts文件(移动原生开发相关)
├─src/pages *业务页面文件存放的目录(Vue为views)
│ ├─index
│ │ └─index.vue index页面
│ └─list
│ └─list.vue list页面
├─src/static *存放应用引用的本地静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此(同Vue)
├─src/uni_modules *存放[uni_module](/uni_modules)。
├─src/platforms *存放各平台专用页面的目录,详见
├─nativeplugins App原生语言插件 详见
├─nativeResources App端原生资源目录
│ └─android Android原生资源目录 详见
├─hybrid *App端存放本地html文件的目录,详见
├─src/wxcomponents *存放小程序组件的目录,详见
├─unpackage 非工程代码,一般存放运行或发行的编译结果
├─AndroidManifest.xml Android原生应用清单文件 详见
├─src/main.js *Vue初始化入口文件(同Vue)
├─src/App.vue *应用配置,用来配置App全局样式以及监听 应用生命周期
├─src/manifest.json *配置应用名称、appid、logo、版本等打包信息,详见
├─src/pages.json *配置页面路由、导航条、选项卡等页面类信息,详见
└─uni.scss *这里是uni-app内置的常用样式变量

源码目录结构

Hello uni-app的模板创建到本地

1
vue create -p dcloudio/uni-preset-vue my-project

创建后的目录结构

编译到各个平台

只要是Uniapp项目,项目文件夹下就会有一个./dist/的子目录中,其中可包含如下:

有效目录 说明
app-plus App
h5 H5
mp-weixin 微信小程序
mp-alipay 支付宝小程序
mp-baidu 百度小程序
mp-qq QQ小程序
mp-toutiao 字节小程序
mp-lark 飞书小程序
mp-kuaishou 快手小程序
mp-jd 京东小程序

将Hello uni-app的模板运行在浏览器端

1
npm run dev:h5

image-20221103155533844

将Hello uni-app的模板运行在微信小程序端

1
npm run dev:mp-weixin

image-20221103160000646

将Hello uni-app的模板运行在支付宝端

image-20221103161518242

vconsole

VConsole一款腾讯旗下的移动小程序端调试器,之前做H5有一款叫eruda,这个不能用于小程序端调试。

参考:

掘金-believe8301:uni-app 开启 vconsole

Python 3.7

参考:

菜鸟教程:Python pip 安装与使用

示例开源项目使用的Python3.9

image-20221108143022450

项目由于数据库使用的是 SQLServer2012,引擎支持django-pyodbc-azure,要求Python环境最新是Python3.7

image-20221115103550989

命令行下会发现是pip3.7管理Python3.7的依赖

pip包存放是在系统环境下的依赖包路径/usr/local/lib/python3.*/site-packagesimage-20221108143131638

Django 2.1.15

参考:

Django官网文档

django中文网-3756404@qq.com :Django常用 命令

腾讯云-老齐:彻底搞懂Django中的数据迁移

stack Overflow-Liondancer:startapp with manage.py to create app in another directory

版本

  • Python 3.7
  • Django 2.1.15

创建项目到指定目录

1
2
# 使main_app作为项目的访问入口
django-admin startproject main_app ./miniapp_backend

运行,默认使用8000端口,可在后面加上指定端口

1
2
3
4
5
6
7
8
python3.7 manage.py runserver
# 运行结果
Performing system checks...

System check identified no issues (0 silenced).

You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

提示的意思是需要根据Django提供的模板创建数据库表结构,这些表为Django提供比如权限管理和Session等,执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
python3.7 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK

默认使用Sqlite数据库,创建了数据表

image-20221115152715822

运行成功

1
python3.7 manage.py runserver

image-20221114153456388

创建ad_test小程序后端,用于广告宣传

1
python3.7 manage.py startapp ad_test

如果需创建目录下的app,使用如下格式,详见stack Overflow-Liondancer:startapp with manage.py to create app in another directory

1
2
python manage.py startapp <app_label> [destination] # startapp command usage 
# 例:python3.7 manage.py startapp payment_platform ./dalai_oa_web/payment_platform (payment_platform文件夹要存在)

最后一个[destination]文件夹将会被用作标记为<app_label>的app包,即使最后一个文件夹名称不与<app_label>相同,但建议是相同的名称。

配置django-pyodbc-azure解决系列问题连接SQLServer,成功搬移Django所需数据库表:

image-20221116225446522

SQL Server 2017 linux

本项目开发使用的是MacOS系统,MicroSoft的还是和Mac不知怎么滴吧,MacOS只能使用Docker版本的,装了个Docker,配置了Docker的硬件规格参数,Mac使用起来才不怎么卡顿了。

客户使用SQLServer2012还得找一个兼容的数据库引擎,选择django-pyodbc-azure。

参考:

CSDN-三金C_C:MacOS配置Sql Server环境

Microsoft:快速入门:使用 Docker 运行 SQL Server Linux 容器映像

Docker Hub:Microsoft SQL Server - Ubuntu based images

CSDN:MacOS配置Sql Server环境

百度百科:SqlServer

博客园-胖出个性:SQLserver与mysql的区别

拉官网镜像:

1
sudo docker pull mcr.microsoft.com/mssql/server:2017-latest

配置参数并运行容器:

1
2
3
sudo docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=******" -p 1433:1433 --name sqlserver2017-local --hostname sqlserver2017-local \
-d \
mcr.microsoft.com/mssql/server:2017-latest

Docker成功安装SqlServer 2017 Ubuntu Linux 版:

image-20221113233634649

执行连接容器,进入Linux版SqlServer:

1
2
3
4
5
6
7
 ~/ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4ee9d39757e1 mcr.microsoft.com/mssql/server:2017-latest "/opt/mssql/bin/nonr…" 4 days ago Up 4 minutes 0.0.0.0:1433->1433/tcp sqlserver2017-local
 ~/ sudo docker exec -it sqlserver2017-local "bash"
root@sqlserver2017-local:/# /opt/mssql-tools/bin/sqlcmd -S localhost -U SA
Password:
1>

保证Docker 容器启动,Navicat连接:

image-20221115105136325

image-20221115104159731

django-pyodbc-azure 2.1.0.0

django-pyodbc-azure 作为数据库引擎,本来是使用Django官网推荐的Microsoft官网上的数据库引擎,最终经大佬推荐使用django-pyodbc-azure,只支持到Django 2.1.15,Python支持最新3.7,但SQLServer数据库的版本支范围多,考虑到开发和上线的稳定性最终决定。

参考:

Microsoft:Creating REST API with Python, Django and Azure SQL

CSDN-lhuann_:Django配置连接SqlServer(版本Django3.2.5,python3.9)

GitHub-michiya:django-pyodbc-azure

数据库引擎应用到此项目时,报错[unixODBC][Driver Manager]Can't open lib 'ODBC Driver 13 for SQL Server' : file not found (0) (SQLDriverConnect)因使用Mac系统需要一大堆的依赖才能成功使用此依赖,最终参考了几篇重要的国外技术文献才解决了这个报错,最终还是成功的连接上了。

参考:

GitHub-michiya:django-pyodbc-azure

Stack Overflow:Can’t open lib ‘ODBC Driver 13 for SQL Server’? Sym linking issue?

PyPI:django-pyodbc-azure 2.1.0.0

PyPI:pyodbc 4.0.35

安装

1
pip install django-pyodbc-azure

安装顺便安装所需依赖

image-20221115111756920

导入PyChram所需依赖照抄就行

image-20221115154545470

尝试Django连接SQLServer,报错:

1
[unixODBC][Driver Manager]Can't open lib 'ODBC Driver 13 for SQL Server' : file not found (0) (SQLDriverConnect)

解决原理其实很简单,就是依赖套娃,满足django-pyodbc-azure的一系列依赖就行,比如

image-20221116223908378

安装完成再去看Stack Overflow:Can’t open lib ‘ODBC Driver 13 for SQL Server’? Sym linking issue?

会发现还需要配置PyPI:django-pyodbc-azure 2.1.0.0文档中提到的'driver': 'ODBC Driver 13 for SQL Server',设置为ODBC Driver 13 for SQL Server就可以了,原因是Unix系统升级了:

actually, in my python code pyodbc was expecting ODBC Driver 13 but, as the ODBC Driver version was updated (because of ubuntu update) to current version ODBC Driver 17, the problem had occurred.

djangorestframework 3.11.2

Django REST framework 3.11 community:Django REST 框架 3.11

drf-yasg 1.17.0

Automated generation of real Swagger/OpenAPI 2.0 schemas from Django REST Framework code.

从Django REST框架代码自动生成 real Swagger/OpenAPI 2.0模式。

由于项目使用Django 2.1.15,djangorestframework 3.11.2,查看PyPi文档PyPi:drf-yasg 1.17.0,最后一版支持Django 2.1的是drf-yasg 1.17.0版本,应该也支持djangorestframework 3.11.2(PyPi没标此版本兼容),直接就上drf-yasg 1.17.0版本,但值得注意的作者在1.17.1版本改动日志。

参考:

Swagger Logo-Ryan Pinkham:What Is the Difference Between Swagger and OpenAPI?

GitHub-axnsan12:drf-yasg

PyPi:drf-yasg 1.17.0

drf-yasg Documentation

drf-yasg 1.17.1版本改动日志

毕竟只是一个文档生成嘛,用不了这个还能用PostMan

Release date: Feb 17, 2020

  • FIXED: fixed compatibility issue with CurrentUserDefault in Django Rest Framework 3.11
  • FIXED: respect USERNAME_FIELD in generate_swagger command (#486)

Support was dropped for Python 3.5, Django 2.0, Django 2.1, DRF 3.7

配置

主程序下settings.py:

1
2
3
4
5
INSTALLED_APPS = [
...
'drf_yasg',
...
]

主程序下urls.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
...
# Swagger
from django.conf.urls import url
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

...

schema_view = get_schema_view(
openapi.Info(
title="Snippets API",
default_version='v1',
description="Test description",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="contact@snippets.local"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)

urlpatterns = [
url(r'^swagger(?P<format>\.json|\.yaml)$',
schema_view.without_ui(cache_timeout=0), name='schema-json'),
# 配置url = "/" 访问SwaggerUI
path("", schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
# 配置url = "/Swagger"
# url(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
url(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
...
]

访问

image-20221117160949888

django-restql 0.15.3

为Django Rest Framework自动生成一般增删改查API

image-20221124143833756

开发工具

VSCode

DCloud-DCloud_UNI_GSQ:当 uni-app 遇见 vscode

掘金-小染Jun:这可能是最好、最详细的VSCode开发uni-app教程吧

掘金-天平:请不要再下载这些vscode插件了

配置 & 插件

  • Preferences => Settings => Text Editor => Files解决manifest.json pages.json文件注释爆红
    image-20221028161848804

  • 组件语法提示

    1
    npm i @dcloudio/uni-helper-json		#安装在项目中的node_mudle
  • Vue2项目,有scss文件,要装vetursass这两个插件,CLI 工程默认带了uni-app语法提示和5+App语法提示

    image-20221103152113173

    image-20221030015303734

  • manifest.json pages.json文件语法提示、校验、鼠标悬停提示信息,安装插件(vscode开发uniapp必备)

    image-20221103151959961代码提示颜色显示

  • 悬停鼠标目录地址预览图片

    image-20221103151933901

  • 快速创建页面、组件、分包

    image-20221103151849784

  • 条件编译注释高亮

    image-20221103151800700

    条件编译注释效果

    还可以定制化我们的注释,比如颜色、加粗、斜体等。

  • 基本能力代码,安装第三方插件来提供和Hbuilder X一样的代码块(不太清除用途,先装上)

    image-20221103151728517

    image-20221103151637665

  • 从 github 下载 uni-app 代码块,放到项目目录下的 .vscode 目录即可拥有和 HBuilderX 一样的代码块。

PyChram

参考:

博客园-迎风而来:pycharm如何在虚拟环境中引入别人的项目

简书-FesonX:如何解决Python包依赖问题

博客园-水论文的程序猿:ide 安装eval reset插件 Pycharm 永久破解

化为云-AXYZdong:【Pycharm】IDE Eval Resetter不好用了|无法重置

快捷键

  • win+,打开Preferences

虚拟环境

导入过程中会提示创建venv(缩写)文件夹,这个文件夹的全称如下,即Python的虚拟环境,是用来管理项目的各种依赖的,所有的依赖包就会放到venv这个目录下

image-20221108113832643

image-20221108114320334

PyChram导出虚拟环境

前面要显示(venv),执行的时候才是导出项目本身虚拟环境的所有包

image-20221117000340369

执行导出

1
pip freeze > requirements.txt

执行导入

1
pip install -r requirements.txt

系统环境

系统环境也是可以开发的,只不过多个项目之间的依赖可能会相互影响,不建议使用。不过系统环境直接可以是命令行创建项目,也可以导入PyChram并运行。

image-20221108135100928

此环境下和命令操作逻辑行对应,PyChram导入的包和pip3安装的包都是操作一样的路径/usr/local/lib/python3.*/site-packages

image-20221108135938682

image-20221108140043397

虚拟环境虚拟出了/usr/local/中的bin/python3.*也虚拟出/usr/local/lib/python3.*/site-packages:

image-20221108140652627

image-20221108140817933

开源项目

拿到了数个uniapp开源项目的代码,最终想找一个适合企业开发的小程序示例,规范代码和方便后期代码的维护升级等。这些开源小程序的后端有Java、php作为后端,也有云开发,虽然云开发方便快捷,但比起企业小程序项目,还是小巫见大巫。后期想着给公司开发小程序进行前后端分离、前后端微服务等技术,为了企业更好的发展,同时是提升个人技术水平的一次非常重要的机会。

参考:

掘金-小染Jun:这可能是最好、最详细的VSCode开发uni-app教程吧

GitHub-stavyan:TinyShop-UniApp

菜鸟教程-NPM 使用介绍

Github-dvadmin:django-vue-admin

django-vue-admin官方文档

小程序端

  1. VSCode打开项目文件夹

  2. 由于依赖关系:NodeJS => Vue => uniapp,所有Uniapp的项目的依赖都是Node来管理的,Node的依赖信息写在package.json中,执行yarn/就会拉依赖包(执行请等待完成)

  3. 直接在VSCode右键显示以下,点击NPM Scripts,就会显示出NPM SCRIPTS

    image-20221103163311783

  4. 展开NPM SCRIPTS,刚好与scripts.json中scripts对应

    image-20221103164831189

  5. 直接点击三角形运行即可编译成各个平台的项目到dist目录下,用指定平台IDE导入即可

  6. 已测试H5、微信、支付宝端,成功编译并运行

    image-20221103165203367

    image-20221103172117358

    image-20221103165650488

    image-20221103172055279

后端

部署运行

命令行的方式部署启动Django-Vue-Admin官方文档:项目运行及部署已经不需要多说了,参考官方文档即可运行。

命令行启动

部署运行比Java方便,不过Java 项目基于Sring Boot也是很快部署运行的。

image-20221107153841676

接口文档用到了Swagger管理API生成文档,非常贴心

image-20221107153938635

PyChram搭建前还是要将依赖、数据库、等初始化完毕,进入PyChram提醒创建了一个依赖目录venv,目录结构如下:

image-20221107154654395

其中的lib放置了所有Python依赖包

PyChram启动

image-20221107154921980

image-20221107155028694

项目依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
asgiref==3.3.4
certifi==2021.5.30
chardet==4.0.0
coreapi==2.3.3
coreschema==0.0.4
Django==3.2.3
django-comment-migrate==0.1.5
django-cors-headers==3.10.1
django-filter==21.1
django-ranged-response==0.2.0
django-restql==0.15.1
django-simple-captcha==0.5.14
django-timezone-field==4.2.3
djangorestframework==3.12.4
djangorestframework-simplejwt==5.1.0
drf-yasg==1.20.0
idna==2.10
inflection==0.5.1
itypes==1.2.0
Jinja2==3.0.1
MarkupSafe==2.0.1
mysqlclient==2.0.3
packaging==20.9
Pillow==8.3.1
PyJWT==2.1.0
pyparsing==2.4.7
pyPEG2==2.15.2
pypinyin==0.46.0
pytz==2021.1
requests==2.25.1
ruamel.yaml==0.17.10
ruamel.yaml.clib==0.2.4
six==1.16.0
smmap==4.0.0
sqlparse==0.4.1
typing-extensions==3.10.0.0
tzlocal==2.1
ua-parser==0.10.0
uritemplate==3.0.1
urllib3==1.26.6
user-agents==2.2.0
whitenoise==5.3.0
openpyxl==3.0.9

API分析

API接口格式

应用 => View => def(函数)

其中的一般增删改查API都是由包django-restql生成的,例如user视图下的API:

image-20221124144051419

除了以上的自动生成API,其他的API接口都是可以在源码中找到的。

数据分析

经分析,开源项目的dvadmin_system_users表是Django的Users表的改版

image-20221121150330805

user共31属性

  • 数据表绿色选择行代表Json数据共有字段,未选择代表Json数据中没有的属性
  • Json响应数据红线代表dvadmin.utils.models.CoreModel类中定义属性
  • Json响应数据红色代表CustomModelSerializer中定义属性
  • Json响应数据青色代表dvadmin.system.views.user.UserSerializer函数中定义属性
  • Json响应数据绿色代表dvadmin.system.models.Users类中包含属性
  • Json响应数据黄色数据库字段与CustomModelSerializer/dvadmin.system.models.Users共有属性
  • 橘黄色代表数据库字段中没有,但Json数据中有的属性

输入 python manage.py shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
>>> from dvadmin.system.views.user import UserSerializer
>>> serializer = UserSerializer()
>>> print(repr(serializer))
UserSerializer():
id = IntegerField(help_text='Id', read_only=True)
modifier_name = SerializerMethodField(read_only=True)
creator_name = SlugRelatedField(read_only=True, slug_field='name', source='creator')
create_datetime = DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True, required=False)
update_datetime = DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
dept_name = CharField(read_only=True, source='dept.name')
role_info = DynamicSerializerMethodField()
last_login = DateTimeField(allow_null=True, label='上次登录', required=False)
is_superuser = BooleanField(help_text='指明该用户缺省拥有所有权限。', label='超级用户状态', required=False)
first_name = CharField(allow_blank=True, label='名字', max_length=150, required=False)
last_name = CharField(allow_blank=True, label='姓氏', max_length=150, required=False)
is_staff = BooleanField(help_text='指明用户是否可以登录到这个管理站点。', label='工作人员状态', required=False)
is_active = BooleanField(help_text='指明用户是否被认为是活跃的。以反选代替删除帐号。', label='有效', required=False)
date_joined = DateTimeField(label='加入日期', required=False)
description = CharField(allow_blank=True, allow_null=True, help_text='描述', label='描述', max_length=255, required=False)
modifier = CharField(allow_blank=True, allow_null=True, help_text='修改人', label='修改人', max_length=255, required=False)
dept_belong_id = CharField(allow_blank=True, allow_null=True, help_text='数据归属部门', label='数据归属部门', max_length=255, required=False)
is_deleted = BooleanField(help_text='是否软删除', label='是否软删除', required=False)
username = CharField(help_text='用户账号', label='用户账号', max_length=150, validators=[<UniqueValidator(queryset=Users.objects.all())>])
email = EmailField(allow_blank=True, allow_null=True, help_text='邮箱', label='邮箱', max_length=255, required=False)
mobile = CharField(allow_blank=True, allow_null=True, help_text='电话', label='电话', max_length=255, required=False)
avatar = CharField(allow_blank=True, allow_null=True, help_text='头像', label='头像', max_length=255, required=False)
name = CharField(help_text='姓名', label='姓名', max_length=40)
gender = ChoiceField(allow_null=True, choices=((0, '未知'), (1, '男'), (2, '女')), help_text='性别', label='性别', required=False)
user_type = ChoiceField(allow_null=True, choices=((0, '后台用户'), (1, '前台用户')), help_text='用户类型', label='用户类型', required=False)
creator = PrimaryKeyRelatedField(allow_null=True, help_text='创建人', label='创建人', queryset=Users.objects.all(), required=False)
dept = PrimaryKeyRelatedField(allow_null=True, help_text='关联部门', label='所属部门', queryset=Dept.objects.all(), required=False)
groups = PrimaryKeyRelatedField(help_text='该用户归属的组。一个用户将得到其归属的组的所有权限。', label='组', many=True, queryset=Group.objects.all(), required=False)
user_permissions = PrimaryKeyRelatedField(help_text='这个用户的特定权限。', label='用户权限', many=True, queryset=Permission.objects.all(), required=False)
post = PrimaryKeyRelatedField(help_text='关联岗位', label='关联岗位', many=True, queryset=Post.objects.all(), required=False)
role = PrimaryKeyRelatedField(help_text='关联角色', label='关联角色', many=True, queryset=Role.objects.all(), required=False)

经分析:以上输出UserSerializer()的字段都是可以在源码中找到对应的字段属性的,数据表的建立是根据Django自带Users model,修改而来,无非是添加了自定义字段,其中的Serializer / ViewSet 继承相应的Django中相关Class,从而Json数据或输出UserSerializer()中包含的字段,但在业务源码中无法找到。

image-20221201235116279

前端

部署运行

命令行方式部署启动,详见:Django-Vue-Admin官方文档:项目运行及部署

和小程序部署启动一个原理,详见本文开发工具 => VSCode,导入项目点击NPM SCRIPTS选择启动方式即可。

image-20221107160013140

image-20221107160048058

项目依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
"dependencies": {
"@great-dream/template": "^1.0.2",
"@vue/composition-api": "^1.0.4",
"axios": "^0.19.0",
"axios-mock-adapter": "^1.18.1",
"better-scroll": "^1.15.2",
"china-division": "^2.4.0",
"core-js": "^3.4.3",
"cropperjs": "^1.5.6",
"d2-crud-plus": "^2.13.1",
"d2-crud-x": "^2.13.1",
"d2p-extends": "^2.13.1",
"dayjs": "^1.8.17",
"echarts": "^5.1.2",
"el-phone-number-input": "^1.1.5",
"element-ui": "^2.15.5",
"faker": "^4.1.0",
"flex.css": "^1.1.7",
"fuse.js": "^5.2.3",
"hotkeys-js": "^3.7.3",
"js-cookie": "^2.2.1",
"js-md5": "^0.7.3",
"lodash": "^4.17.15",
"lowdb": "^1.0.0",
"nprogress": "^0.2.0",
"qiankun": "^2.7.2",
"screenfull": "^5.0.2",
"sortablejs": "^1.10.1",
"ua-parser-js": "^0.7.20",
"vue": "^2.6.11",
"vue-i18n": "^8.15.1",
"vue-infinite-scroll": "^2.0.2",
"vue-router": "^3.1.3",
"vue-splitpane": "^1.0.6",
"vuex": "^3.1.2",
"vxe-table": "^3.3.2",
"xe-utils": "^3.2.1"
},
"devDependencies": {
"@d2-projects/vue-filename-injector": "^1.1.0",
"@kazupon/vue-i18n-loader": "^0.5.0",
"@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-eslint": "^4.1.0",
"@vue/cli-plugin-router": "^4.1.0",
"@vue/cli-plugin-unit-jest": "^4.1.0",
"@vue/cli-plugin-vuex": "^4.1.0",
"@vue/cli-service": "^4.1.0",
"@vue/eslint-config-standard": "^5.1.2",
"@vue/test-utils": "^1.0.2",
"babel-eslint": "^10.0.3",
"compression-webpack-plugin": "^3.0.1",
"cz-conventional-changelog": "^3.2.0",
"eslint": "^6.8.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"sass": "^1.23.7",
"sass-loader": "^8.0.0",
"svg-sprite-loader": "^4.1.6",
"text-loader": "^0.0.1",
"vue-cli-plugin-i18n": "^1.0.1",
"vue-template-compiler": "^2.6.10",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-theme-color-replacer": "^1.3.3"
}

代码分析

开源前后端项目都是根据不同的模块来进行划分的,起初分析的是用户管理模块,模块结构中都会包含:api.js、crud.js、index.vue,其中crud.js 就是类似前端低代码所要用到的数据模型,主要生效的就是其中的Json格式的数据,用来生成表格 表单等前端模块。

参考:

掘金:DylanlZhao:【低代码漫谈】从前端三大框架到前端低代码

GitHub-greper:d2-crud-plus

d2-crud-plus文档:面向配置的crud编程,快速开发crud功能

开源项目使用的GitHub-greper:d2-crud-plusd2-crud-plus: 辅助d2-crud-xd2-crud,让它们使用起来更简单

模块表格表单共有数据模型位于src/install.jsVue.prototype.commonEndColumns = function (param = {}) {}

crud(增删改查)操作API接口配置位于每个模块下的api.js,index.vue必须导入相关内容,写入crud操作的函数,提供给d2-crud-plus进行调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
methods: {
getCrudOptions () {
this.crud.searchOptions.form.user_type = 0
return crudOptions(this)
},
pageRequest (query) {
return api.GetList(query)
},
addRequest (row) {
return api.AddObj(row)
},
updateRequest (row) {
return api.UpdateObj(row)
},
delRequest (row) {
return api.DelObj(row.id)
},
batchDelRequest (ids) {
return api.BatchDel(ids)
},
···
}
ElementUI组件

参考:

GitHub-ElemeFE:element-ui

Eement官方文档

参考了Eement官方文档中的快速上手,完整引入Element配置,在本项目中的体现

  • src/plugin/d2admin/index.js,引入:’element-ui’、’element-ui/lib/theme-chalk/index.css’,并向Vue注册。

  • src/main.js,由于d2admin依赖Element,配置Element时,将其配置在d2admin

d2-crud-plus

参考:

d2-crud-plus文档:快速上手

src/install.js中配置方式如官网所描述,配置了d2-crud-plus、d2-crud-plus,然后经过引入其文件,导入配置到main.js:

1
2
// d2-crud-plus 安装与初始化
import './install'

微服务

打包镜像

以下理论尚未实践,待后期验证

参考:

Gitee-dvadmin / django-vue-admin: docker 镜像打包

菜鸟教程:Docker build 命令

菜鸟教程:Docker push 命令

菜鸟教程:Docker Dockerfile

DockerHub

示例项目源码目录结构

image-20221106220526346

打包出前端镜像

1
2
3
4
# DockerfileBuild文件内容 
FROM node:14-alpine
COPY ./web/package.json /
RUN npm install --registry=https://registry.npm.taobao.org
  • FROM 是将官方镜像拉下来
  • COPY 将本地中的package.json放入容器系统的根目录
  • RUN 在alpine linux容器运行命令
1
2
# 编译打包到本地
docker build -f ./docker_env/web/DockerfileBuild -t registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/node12-base-web:latest .
  • -f 指定DockerFile路径
  • -t 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签,其标签为registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/node12-base-web:latest
1
2
# 上传到阿里云仓库
docker push registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/node12-base-web:latest
  • 上传标签为registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/node12-base-web:latest的镜像到其设定的仓库

打包出后端镜像

一样的原理和步骤,不多说了

运行前端容器

nginx服务器配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server {
# 前端
listen 8080;
server_name localhost;
client_max_body_size 100M;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
root /usr/share/nginx/html;
index index.html index.php index.htm;
}

# 后端
location /api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
rewrite ^/api/(.*)$ /$1 break; # 重写
proxy_pass http://127.0.0.1:8000/; # 设置代理服务器的协议和地址
}
}

Dockerfile文件

1
2
3
4
5
6
7
8
9
FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/node12-base-web:latest
WORKDIR /web/
COPY web/. .
RUN npm install --registry=https://registry.npm.taobao.org
RUN npm run build

FROM nginx:alpine
COPY ./docker_env/nginx/my.conf /etc/nginx/conf.d/my.conf
COPY --from=0 /web/dist /usr/share/nginx/html
  • FROM 使用本地build出的作为基础镜像
  • WORKDIR
  • 用 WORKDIR 指定的工作目录/web/,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在
  • COPY 将源码复制到容器
  • RUN 第一个将前端的依赖下载到node_modules,第二个是打包出
  • FROM 拉下来一个nginx:alpine作为基础镜像
  • COPY 将本地nginx的配置放置于基础镜像的nginx服务器配置文件目录
  • COPY 部署/web/dist(npm run build)vue生成的静态web文件到/usr/share/nginx/html

以下命令看样子是build出一个容器,看代码不应该运行出容器

1
docker build -f ./docker_env/web/Dockerfile -t dvadmin-pro-web .

打包出Celery镜像

Celery镜像DockerFile文件内容

1
2
3
4
5
6
FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/python38-base-backend:latest
WORKDIR /backend
COPY ./backend/ .
RUN awk 'BEGIN { cmd="cp -i ./conf/env.example.py ./conf/env.py "; print "n" |cmd; }'
RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt
CMD ["celery", "-A", "application", "worker", "-B", "--loglevel=info"]

运行命令(应该是打包命令)

1
docker build -f ./docker_env/celery/Dockerfile -t dvadmin-pro-celery .

docker-compose 运行

个人理解此技术就是为了快速启动所需的批量容器并可以对容器进行一系列的配置

django-vue-admin/docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
version: "3"
services:
dvadmin-web:
container_name: dvadmin-web
ports:
- "8080:8080"
build:
context: ./
dockerfile: ./docker_env/web/Dockerfile
environment:
TZ: Asia/Shanghai
volumes:
- ./docker_env/nginx/my.conf:/etc/nginx/conf.d/my.conf
expose:
- "8080"
networks:
network:
ipv4_address: 177.8.0.11

dvadmin-django:
build:
context: .
dockerfile: ./docker_env/django/Dockerfile
container_name: dvadmin-django
working_dir: /backend
# 打开mysql 时,打开此选项
# depends_on:
# - dvadmin-mysql
environment:
PYTHONUNBUFFERED: 1
DATABASE_HOST: dvadmin-mysql
TZ: Asia/Shanghai
volumes:
- ./backend:/backend
- ./logs/log:/var/log
ports:
- "8000:8000"
expose:
- "8000"
restart: always
networks:
network:
ipv4_address: 177.8.0.12

# dvadmin-mysql:
# image: mysql:5.7
# container_name: dvadmin-mysql
# #使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限
# #设置为true,不然数据卷可能挂载不了,启动不起
## privileged: true
# restart: always
# ports:
# - "3306:3306"
# environment:
# MYSQL_ROOT_PASSWORD: "123456"
# MYSQL_DATABASE: "dvadmin_pro"
# TZ: Asia/Shanghai
# command:
# --wait_timeout=31536000
# --interactive_timeout=31536000
# --max_connections=1000
# --default-authentication-plugin=mysql_native_password
# volumes:
# - "./docker_env/mysql/data:/var/lib/mysql"
# - "./docker_env/mysql/conf.d:/etc/mysql/conf.d"
# - "./docker_env/mysql/logs:/logs"
# networks:
# network:
# ipv4_address: 177.8.0.13


# 如果使用celery 插件,请自行打开此注释
# dvadmin-celery:
# build:
# context: .
# dockerfile: ./docker_env/celery/Dockerfile
# # image: django:2.2
# container_name: dvadmin-celery
# working_dir: /backend
# depends_on:
# - dvadmin-mysql
# environment:
# PYTHONUNBUFFERED: 1
# DATABASE_HOST: dvadmin-mysql
# TZ: Asia/Shanghai
# volumes:
# - ./backend:/backend
# - ./logs/log:/var/log
# restart: always
# networks:
# network:
# ipv4_address: 177.8.0.14

networks:
network:
ipam:
driver: default
config:
- subnet: '177.8.0.0/16'
  • services: 该配置共4个容器
  • container_name: 各个容器有各自的服务名
  • volumes: 容器各自有重要的文件映射至宿主机目录
  • environment: 基础镜像有需配置一定的环境变量,对应镜像的环境变量,请到DockerHub查看详情
  • networks: 管理Docker内部网络结构
  • ports: 宿主机与Docker虚拟机之间的端口映射
  • ipv4_address: 配置虚拟网络主机的各个静态网络地址