虚拟环境搭配

安装和配置

安装虚拟环境的命令:

1)sudo pip install virtualenv #安装虚拟环境

2)sudo pip install virtualenvwrapper #安装虚拟环境扩展包

3)编辑家目录下面的.bashrc文件,添加下面两行。

export WORKON_HOME=$HOME/.virtualenvs

source /usr/local/bin/virtualenvwrapper.sh

4)使用source .bashrc使其生效一下。

使用

创建python3虚拟环境:

mkvirtualenv -p python3 bj19  # bj19是虚拟环境名

进入虚拟环境工作:

workon 虚拟环境名

查看机器上有多少个虚拟环境:

workon 空格 + 两个tab键

退出虚拟环境:

deactivate

删除虚拟环境:

rmvirtualenv 虚拟环境名

虚拟环境下安装包的命令:

pip install 包名

注意:不能使用sudo pip install 包名,这个命令会把包安装到真实的主机环境上而不是安装到虚拟环境中。

查看虚拟环境中安装了哪些python包:

pip list
pip freeze

安装django环境:

pip install django==1.8.2

创建项目

建立文件夹

mkdir bj19  # bj19是文件夹名 随便起

进入bj19 创建应用包

python manage.py startapp booktest  # booktest是应用名,可以随便起

更改settings.py设置

# 注册应用
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'booktest', # 注册创建的booktest应用 如果没有这个 创建数据库会报NO changes 错误
)
....
# 模板配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # 指定templates模板目录路径
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
....
# 数据库配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 指定用什么数据库
'NAME': 'test1', # 指定数据库名
'USER': 'root', # 数据库账号
'PASSWORD': 'mysql', # 数据库密码
'HOST': 'localhost', # 数据库IP 本机用localhost
'PORT': 3306, # 端口
}
}
....
# 本地化配置
LANGUAGE_CODE = 'zh-hans' # 本地化语言 TIME_ZONE = 'Asia/Shanghai' # 本地化时间
....
# 静态资源配置
STATIC_URL = '/static/'
STATICFILES_DIR=[os.path.join(BASE_DIR, 'static')] # 静态资源存放路径

创建模板文件夹 对应应用名在templates创建子目录

创建静态资源文件包

创建数据库test1

create database test1 charset=utf8;

项目中引用pymysql

test1/__init__.py文件中

import pymysql
pymysql.install_as_MySQLdb()

建表

booktest/models.py中

from django.db import models
from datetime import date
# Create your models here.
class BookInfo(models.Model):
'''图书模型类'''
# 书名
# btitle = models.CharField(verbose_name='书名', max_length=20) # verbose_name= 在admin后台显示的标题 max_length=最大长度
btitle = models.CharField('书名', max_length=20) # 如果标题放在第一位 则可以省略verbose_name=
# 出版时间
bpub_date = models.DateField()
# 阅读量
bread = models.IntegerField(default=0) # default 设置默认值
# 评论数
bcomment = models.IntegerField(default=0)
# 是否删除
isDelete = models.BooleanField(default=False) # 定义数据表名称--元选项
class Meta:
# 指定生成的数据表名
db_table = 'bookinfo' class HeroInfo(models.Model):
'''英雄模型类'''
# 姓名
hname = models.CharField('英雄', max_length=20)
# 性别
hgender = models.BooleanField(default=False)
# 功夫
hcomment = models.CharField('功夫', max_length=128)
# 关联书名
hbook = models.ForeignKey('BookInfo')
# 是否删除
isDelete = models.BooleanField(default=False) # 数据表名
class Meta:
db_table = 'heroinfo' class AreaInfo(models.Model):
'''地域模型类'''
# 地名
atitle = models.CharField('地名', max_length=20)
# 自关联pid
aParent = models.ForeignKey('self', null=True, blank=True) # 表名
class Meta:
db_table = 'areainfo'

迁移

python manage.py makemigrations
python manage.py migrate

报错

解决方案:

看一下settings.py文件 apps中有没有添加 booktest

迁移成功

查看数据库

默认值并不在数据库层面生效,而是在django创建对象时生效。

图书表结构

表heroinfo结构如下:

Django框架会根据关系属性生成一个关系字段,并创建外键约束。

测试数据
在数据库命令行中,复制如下语句执行,向booktest_bookinfo表中插入测试数据:

insert into bookinfo(btitle,bpub_date,bread,bcomment,isDelete) values
('射雕英雄传','1980-5-1',12,34,0),
('天龙八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飞狐','1987-11-11',58,24,0);

向booktest_heroinfo表中插入测试数据:

insert into heroinfo(hname,hgender,hbook_id,hcomment,isDelete) values
('郭靖',1,1,'降龙十八掌',0),
('黄蓉',0,1,'打狗棍法',0),
('黄药师',1,1,'弹指神通',0),
('欧阳锋',1,1,'蛤蟆功',0),
('梅超风',0,1,'九阴白骨爪',0),
('乔峰',1,2,'降龙十八掌',0),
('段誉',1,2,'六脉神剑',0),
('虚竹',1,2,'天山六阳掌',0),
('王语嫣',0,2,'神仙姐姐',0),
('令狐冲',1,3,'独孤九剑',0),
('任盈盈',0,3,'弹琴',0),
('岳不群',1,3,'华山剑法',0),
('东方不败',0,3,'葵花宝典',0),
('胡斐',1,4,'胡家刀法',0),
('苗若兰',0,4,'黄衣',0),
('程灵素',0,4,'医术',0),
('袁紫衣',0,4,'六合拳',0);

后台管理

本地化设置已经在settings.py设置好了

创建后台管理账号

python manage.py createsuperuser

运行项目 然后登陆后台

#python manage.py runserver 服务器IP:端口
python manage.py runserver 192.168.121.130:8000

登录后台查看

192.168.121.130:/admin

注册和登录

添加用户表

class AdminInfo(models.Model):
# 管理员
user = models.CharField(max_length=20, unique=True)
# 密码
password = models.CharField(max_length=128)
# 是否删除
isDelete = models.BooleanField(default=False)

创建regist.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/js/jquery-1.12.4.min.js"></script> </head>
<body> <form action="/regist_handle" method="post">
{% csrf_token %}
<lable>账号:</lable>
<input type="text" name="user"><br>
<lable>密码:</lable>
<input type="password" name="password">
<br>
<input type="text" name="verify_code" placeholder="请输入验证码">
<img src="/verify_code" onclick="this.src='/verify_code?'+ Math.random()" alt="" ><br> <-- 点击更换验证码 !-->
<input type="submit" value="注册">
</form>
</body>
</html>

注册及验证码的视图函数views.py

def regist(request):
return render(request, 'booktest/regist.html') def regist_handle(request):
user = request.POST.get('user') # POST或GET要大写
password = request.POST.get('password')
verifycode = request.POST.get('verify_code') if verifycode != request.session['verifycode']:
return HttpResponse('验证码不正确')
u = AdminInfo()
u.user = user
u.password = password
u.save()
return redirect('/login') # 验证码
from PIL import Image, ImageDraw, ImageFont
from django.utils.six import BytesIO
def verify_code(request):
#引入随机函数模块
import random
#定义变量,用于画面的背景色、宽、高
bgcolor = (random.randrange(20, 100), random.randrange(
20, 100), 255)
width = 100
height = 25
#创建画面对象
im = Image.new('RGB', (width, height), bgcolor)
#创建画笔对象
draw = ImageDraw.Draw(im)
#调用画笔的point()函数绘制噪点
for i in range(0, 100):
xy = (random.randrange(0, width), random.randrange(0, height))
fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
draw.point(xy, fill=fill)
#定义验证码的备选值
str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
#随机选取4个值作为验证码
rand_str = ''
for i in range(0, 4):
rand_str += str1[random.randrange(0, len(str1))]
#构造字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont”
font = ImageFont.truetype('FreeMono.ttf', 23)
#构造字体颜色
fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
#绘制4个字
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
#释放画笔
del draw
#存入session,用于做进一步验证
request.session['verifycode'] = rand_str
#内存文件操作
buf = BytesIO()
#将图片保存在内存中,文件类型为png
im.save(buf, 'png')
#将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), 'image/png')

注册路由urls.py

    url(r'regist$', views.regist),
url(r'regist_handle$', views.regist_handle),
url(r'verify_code', views.verify_code),

登录页面login.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login_check" method="post">
{% csrf_token %}
<lable>账号:</lable>
<input type="text" name="user"><br>
<lable>密码:</lable>
<input type="password" name="password">
<br> <input type="submit" value="登陆">
</form>
</body>
</html>

登录的视图函数views.py

# 登陆页面
def login(request):
return render(request, 'booktest/login.html') # 登陆校验
def login_check(request): user = request.POST.get('user')
password = request.POST.get('password')
userinfo = AdminInfo.objects.filter(user=user) # filter取的是一个questset集 要加get()才能取到值 如果不用filter()用get()取值的话 为空的时候会报错
if not userinfo:
return HttpResponse("无数据")
else:
userinfo = userinfo.get() # QuestSet无法直接读取值 要用get()取值
if user==userinfo.user and password==userinfo.password:
request.session['isLogin'] = True
request.session['user'] = user
return redirect('/index')
else:
return redirect('/login')

注册路由urls.py

    url(r'^login$', views.login),
url(r'^login_check$', views.login_check),

登录校验装饰器views.py

# 校验是否登录,如果没登录返回登录页面
def login_check(func):
def wapper(request, *view_args, **view_kwarga):
if not request.session.has_key('isLogin'):
return redirect('/login')
return func(request, *view_args, **view_kwarga)
return wapper

上传图片

定义模型类models.py

class Uploads(models.Model):
'''图片模型类'''
pic = models.ImageField(upload_to='booktest') # upload_to= 上传到哪个文件夹

设置路径settings.py

UPLOAD_ROOT = os.path.join(BASE_DIR, 'static/upload')  # 没有中括号

上传页面pic_upload.html

在模板中定义上传表单,要求如下:

  • form的属性enctype="multipart/form-data"
  • form的method为post
  • input的类型为file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="upload_handle" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="pic"><br>
<input type="submit" value="上传">
</form>
</body>
</html>

上传和处理的视图函数 views.py

# 上传页面
def pic_upload(request):
return render(request, 'booktest/pic_upload.html') # 上传处理
from django.conf import settings # 为了导入上传路径
def upload_handle(request):
pic = request.FILES.get('pic') # 图片用FILES.get() 接收
print(pic.name)
p = Uploads()
p.pic = 'booktest/%s' % pic.name # 保存到数据库
p.save()
pname = '%s/booktest/%s' % (settings.UPLOAD_ROOT, pic.name)
with open(pname, 'wb') as f: # 写入到指定文件夹
for content in pic.chunks(): # pic.chunks() 一段一段读取
f.write(content)
return HttpResponse('ok')

分页

Paginator类对象的属性:

属性名

说明

num_pages

返回分页之后的总页数

page_range

返回分页后页码的列表

Paginator类对象的方法:

方法名

说明

page(self, number)

返回第number页的Page类实例对象

 

Page类对象的属性:

属性名

说明

number

返回当前页的页码

object_list

返回包含当前页的数据的查询集

paginator

返回对应的Paginator类对象

 

Page类对象的方法:

属性名

说明

has_previous

判断当前页是否有前一页

has_next

判断当前页是否有下一页

previous_page_number

返回前一页的页码

next_page_number

返回下一页的页码

Paginator类实例对象

  • 方法_init_(列表,int):返回分页对象,第一个参数为列表数据,第二个参数为每页数据的条数。
  • 属性count:返回对象总数。
  • 属性num_pages:返回页面总数。
  • 属性page_range:返回页码列表,从1开始,例如[1, 2, 3, 4]。
  • 方法page(m):返回Page类实例对象,表示第m页的数据,下标以1开始。

Page类实例对象

  • 调用Paginator对象的page()方法返回Page对象,不需要手动构造。
  • 属性object_list:返回当前页对象的列表。
  • 属性number:返回当前是第几页,从1开始。
  • 属性paginator:当前页对应的Paginator对象。
  • 方法has_next():如果有下一页返回True。
  • 方法has_previous():如果有上一页返回True。
  • 方法len():返回当前页面对象的个数。

创建视图page_test.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>省</h1>
<ul>
{% for p in page %}
<li>{{ p.atitle }}</li>
{% endfor %}
</ul> {#页码#}
{#判断是否有上一页#}
{% if page.has_previous %}
<a href="/page_test/{{ page.previous_page_number }}">《上一页</a>
{% endif %}
{% for pindex in plist %}
{# 判断当前页 撤销当前页a标签#}
{% if pindex == page.number %}
{{ pindex }}
{% else %}
<a href="/page_test/{{ pindex }}">{{ pindex }}</a>
{% endif %}
{% endfor %}
{#判断是否有下一页#}
{% if page.has_next %}
<a href="/page_test/{{ page.next_page_number }}">下一页》</a>
{% endif %}
</body>
</html>

分页的视图函数views.py

from django.core.paginator import Paginator # 分页需导入paginator包
def page_test(request, pindex): # 红字参数与路由要一致
# 查询所有省的名称
prov_list = AreaInfo.objects.filter(aParent__isnull=True)
print(prov_list)
# 分页 每页显示10条内容
provs = Paginator(prov_list,10)
# 如果当前没有传递页码信息,则认为是第一页,这样写是为了请求第一页时可以不写页码
if pindex=='':
pindex = 1
else:
# 通过url匹配的参数都是字符串类型,转换成int类型
pindex = int(pindex)
# 获取第pIndex页的数据
page = provs.page(pindex)
# 获取所有的页码信息
plist = provs.page_range
return render(request, 'booktest/page_test.html', {'page':page, 'plist':plist})

路由urls.py

url(r'page_test/(?P<pindex>\d*)$', views.page_test), # 红字部分要和views.py 中视图函数的形参保持一致

三级联动

创建视图areas.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>省市县选择案例</title>
<script src="/static/js/jquery-1.12.4.min.js"></script>
<script>
$(function () {
// 发起一个ajax请求 /prov,获取所有省级地区的信息
// 获取信息,使用get
// 涉及到信息修改,使用post
$.get('/prov', function (data) {
// 回调函数
// 获取返回的json数据
res = data.data
// 获取prov下拉列表框
prov = $('#prov')
// 变量res数组,获取每一个元素:[地区id, 地区标题]
/*
for(i=0; i<res.length; i++){
id = res[i][0]
atitle = res[i][1] option_str = '<option value="'+id + '">'+ atitle+ '</option>'
// 向prov下拉列表框中追加元素
prov.append(option_str)
}*/
$.each(res, function (index, item) {
id = item[0]
atitle = item[1]
option_str = '<option value="'+id + '">'+ atitle+ '</option>'
// 向prov下拉列表框中追加元素
prov.append(option_str)
})
}) // 绑定prov下拉列表框的change事件,获取省下面的市的信息
$('#prov').change(function () {
// 发起一个ajax请求 /city,获取省下面市级地区的信息
// 获取点击省的id
prov_id=$(this).val()
$.get('/city'+prov_id, function (data) {
// 获取返回的json数据
res = data.data
// 获取city下拉列表框
city = $('#city')
// 清空city下拉列表框
city.empty().append('<option>---请选择市---</option>')
// 获取dis下拉列表框
dis = $('#dis')
// 清空dis下拉列表框
dis.empty().append('<option>---请选择县---</option>')
// 变量res数组,获取每一个元素:[地区id, 地区标题]
// 遍历取值添加到city下拉列表框中
$.each(res, function (index, item) {
id = item[0]
atitle = item[1]
option_str = '<option value="'+id + '">'+ atitle+ '</option>'
// 向city下拉列表框中追加元素
city.append(option_str)
})
})
}) // 绑定city下拉列表框的change事件,获取市下面的县的信息
$('#city').change(function () {
// 发起一个ajax请求 /dis,获取市下面县级地区的信息
// 获取点击市的id
city_id=$(this).val()
$.get('/dis'+city_id, function (data) {
// 获取返回的json数据
res = data.data
// 获取dis下拉列表框
dis = $('#dis')
// 清空dis下拉列表框
dis.empty().append('<option>---请选择县---</option>')
// 变量res数组,获取每一个元素:[地区id, 地区标题]
// 遍历取值添加到dis下拉列表框中
$.each(res, function (index, item) {
id = item[0]
atitle = item[1]
option_str = '<option value="'+id + '">'+ atitle+ '</option>'
// 向dis下拉列表框中追加元素
dis.append(option_str)
})
})
})
})
</script>
</head>
<body>
<select id="prov">
<option>---请选择省---</option>
</select>
<select id="city">
<option>---请选择市---</option>
</select>
<select id="dis">
<option>---请选择县---</option>
</select>
</body>
</html>

视图函数views.py

# /areas
def areas(request):
'''省市县选中案例'''
return render(request, 'booktest/areas.html') # /prov
def prov(request):
'''获取所有省级地区的信息'''
# 1.获取所有省级地区的信息
areas = AreaInfo.objects.filter(aParent__isnull=True) # 2.变量areas并拼接出json数据:atitle id
areas_list = []
for area in areas:
areas_list.append((area.id, area.atitle)) # 3.返回数据
return JsonResponse({'data':areas_list}) def city(request, pid):
'''获取pid的下级地区的信息'''
# 1.获取pid对应地区的下级地区
# area = AreaInfo.objects.get(id=pid)
# areas = area.areainfo_set.all()
areas = AreaInfo.objects.filter(aParent__id=pid) # 2.变量areas并拼接出json数据:atitle id
areas_list = []
for area in areas:
areas_list.append((area.id, area.atitle)) # 3.返回数据
return JsonResponse({'data': areas_list})

路由配置urls.py

    url(r'^areas$', views.areas), # 省市县选中案例
url(r'^prov$', views.prov), # 获取所有省级地区的信息
url(r'^city(\d+)$', views.city), # 获取省下面的市的信息
url(r'^dis(\d+)$', views.city), # 获取市下面的县的信息

创建django项目完整实例的更多相关文章

  1. Django-下载安装-配置-创建django项目-三板斧简单使用

    目录 Django 简介 使用 django 的注意事项 计算机名不能有中文 Django版本问题 django下载安装 在命令行下载安装 在pycharm图形界面下载安装 检验是否安装成功 创建Dj ...

  2. 终端指令操作创建Django项目

    需求:通过Django创建一个用户表和权限表. 用户表包括:用户名,邮箱,密码,管理权限. 权限表包括:普通用户,管理用户,超级用户. 权限表和用户表有一对多的关系,即用户表中的每条数据对应权限表中的 ...

  3. 在Eclipse中创建Django项目

    在以前的分享中,我们是在命令行模式下创建Django项目的,那么,如何在IDE中使用Django呢? 本文将介绍如何在Eclipse中创建Django项目. 首先,新建Django项目mysite,如 ...

  4. Python——Pycharm创建Django项目

    一.Django的官网 地址:https://www.djangoproject.com/download/ 注:LTS表示一直在维护的版本,建议是带LTS的版本 二.Pycharm下载Django ...

  5. 学以致用三十-----pycharm创建django项目忘记添加app

    记忆力有时候真的不是很好.因此有些操作步骤还是记录下来好了. pycharm版本-----2018.2.4 创建django项目 file-----newproject----- 创建的时候,appl ...

  6. Pycharm创建Django项目并访问Django

    Pycharm创建Django项目并访问Django 一.Django插件 如果没有Django插件,需要先安装Django插件. 1,首先点击Default Settings,打开设置页面: 2,打 ...

  7. 第三百零三节,Django框架介绍——用pycharm创建Django项目

    Django框架介绍 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的软件设计模式,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内 ...

  8. eclipse + pydev 创建django项目

    前提条件机器装好python,并装好django插件.(http://blog.csdn.net/lilongjiu/article/details/51405340) 1. 下载eclise Ver ...

  9. 一 Django框架介绍——用pycharm创建Django项目

    Django框架介绍 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的软件设计模式,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内 ...

随机推荐

  1. 前端与算法 leetcode 28.实现 strStr()

    # 前端与算法 leetcode 28.实现 strStr() 题目描述 28.移除元素 概要 这道题的意义是实现一个api,不是调api,尽管很多时候api的速度比我们写的快(今天这个我们可以做到和 ...

  2. Go基础编程实践(二)—— 类型转换

    bool to string strconv包的FormatBool函数用于将bool转为string package main import ( "fmt" "strc ...

  3. Linux RedHat 7 配置本地 YUM源

    尽管RPM安装方法能够帮助用户查询软件相关的依赖关系,但是还是需要安装人员自己来解决,而且有些大型软件可能与数十个程序都有依赖关系,在这种情况下安装软件事件非常痛苦和耗费事件的事情,而Yum软件仓库可 ...

  4. 转:什么是DIP、IoC、DI

    DIP依赖倒置原则DIP(Dependency-Inversion Principles) IoC控制反转(Inversion of Control,IoC),简言之就是代码的控制器交由系统控制,而不 ...

  5. web API .net - .net core 对比学习-文件目录概述

    个人正在学习.net web Api的相关知识,因此用这一系列博客做一记录. 1. 首先我们分别创建 .net web api 项目和 .net core web api 项目. 2. 我们首先比较一 ...

  6. win7下scrapy1.3.2安装

    刚开始学爬虫,网上搜了搜,目前最合适的是选scrapy. 先要安装scrapy. 很多的博客上用的教程都说,scrapy目前对python3支持不是很好.可是不能不学3啊. 先用anaconda最新版 ...

  7. Java8新特性:接口

    Java接口本身没有任何实现,因为Java接口不涉及表象,而只描述public行为,所以Java接口比Java抽象类更抽象化. 以上是百度百科中对接口的定义,这个定义已经不准确. Java8对接口做了 ...

  8. 【转载】C#中List集合使用Min()方法查找到最小值

    在C#的List集合操作中,有时候需要查找到List集合中的最小值,此时可以使用List集合的扩展方法Min方法,Min方法有2种形式,一种是不带任何参数的形式,适用于一些值类型变量的List集合,另 ...

  9. 编辑表格输入内容、根据input输入框输入数字动态生成表格行数、编辑表格内容提交传给后台数据处理

    编辑表格输入内容.根据input输入框输入数字动态生成表格行数.编辑表格内容提交传给后台数据处理 记录自己学习做的东西,写的小demo,希望对大家也有帮助! 代码如下: <!DOCTYPE ht ...

  10. TypeScript基础以及在Vue中的应用

    TypeScript推出已经很长时间了,在Angular项目中开发比较普遍,随着Vue 3.0的即将推出,TypeScript在Vue项目中使用也即将成为很大的趋势,笔者也是最近才开始研究如何在Vue ...