Bootstrap是一个简单易用,功能丰富的前端框架,主要提供了预定义的样式,元素布局,组件,图标等功能。Bootstrap可以通过给HTML元素添加class属性来方便地赋予元素预先定义好的样式和布局方式。
Bootstrap目前常用的版本是v3、v4和v5。本书将以最新的v5版本为例介绍其使用方法。Bootstrap支持使用npm安装,也可直接通过script标签及link标签引入。为了配置简便突出要介绍的重点,这里使用后者。
下面是一段未使用框架的代码,定义了标题、三个按钮、一个无序列表和一个三行三列的表格。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>使用Bootstrap</title>
</head>
<body>
<div>
<h1>标题
<button>按钮1</button><button>按钮2</button><button>按钮3</button>
<input>
<ul>
<li>第一</li>
<li>第二</li>
<li>第三</li>
</ul>
<table>
<thead><tr><th>#</th><th>1</th><th>2</th><th>3</th></tr></thead>
<tbody>
<tr><td>1</td><td>一</td><td>二</td><td>三</td></tr>
<tr><td>2</td><td>甲</td><td>乙</td><td>丙</td></tr>
</tbody>
</table>
</div>
</body>
</html>
在浏览器打开这段代码看到的效果如下图所示。
要使用Bootstrap,需要引入其代码,Bootstrap的代码分为css代码和JavaScript。下面的代码通过link标签引入了Bootstrap v5.2.3版本的CSS代码。这个link标签应该放置在HTML5文档的开头部分,通常可以放在<head>标签中。
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
下面的代码引入了Bootstrap v5.2.3版本的JavaScript代码。这个标签应该放到文档的结尾部分,通常可放到<body>标签之后。
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js"></script>
引入Bootstrap代码文件后。给需要应用样式的标签添加class属性即可把预定设的样式应用到指定元素上。
首先给最外层的div标签添加class="container"属性,这是Bootstrap布局系统中放置在最外层容纳其他元素的类,这里仅简单使用,下一小节将详细介绍布局系统。这是代码如下所示。
<div class="container"></div>
对于按钮添加btn类可以应用基本按钮样式,但没有颜色也没有边框,对按钮通常要使用两个或以上的类。比如把上面代码中的button标签做如下修改。
<button class="btn btn-dark">按钮1</button>
<button class="btn btn-primary">按钮2</button>
<button class="btn btn-outline-primary btn-lg">按钮3</button>
按钮1被设为“深色”或者叫“暗色”样式,按钮2被设置为“主要操作”的按钮样式,按钮3被设置为“带轮廓线的主要操作”样式,尺寸是大号。“btn-sm”和“btn-lg”可以设置按钮的尺寸,前者是小号,后者是大号,二者都不使用则介于它们之间。
对于列表可通过下面的代码应用Bootstrap的样式。对于ul使用list-group,ul下的li标签使用list-group-item类。
<ul class="list-group">
<li class="list-group-item">第一</li>
<li class="list-group-item">第二</li>
<li class="list-group-item">第三</li>
</ul>
对于表格,可以只给table标签添加class,“table”可以应用Bootstrap默认的样式,“table-success”增加了颜色,“table-striped”则使表格相邻的两行颜色深浅交错,便于区分,添加class后的代码如下所示。
<table class="table table-success table-striped"></table>
最终经过修改的html文档在浏览器中打开的效果如下图所示。
使用Bootstrap并给标签添加相应class属性后,看到的变化是标题,按钮,列表和表格中的字体发生变化,按钮边角更圆润,按钮之间增加了间距,列表和表格宽度变得更宽,占据更多空间。除此之外,改变浏览器窗口大小时,这些元素会自动调整大小和位置。点击查看代码
Bootstrap一般使用使用带有container类的元素容纳页面内容,如上一小节例子中的<div class="container"></div>。container类可以决定其中内容的宽度,“container-fluid”总会占满100%的屏幕宽度,“container-sm”仅在小尺寸的屏幕(默认是小于576px)时会占满屏幕宽度,当屏幕尺寸变宽时将把内容居中显示,两侧会根据设置留出空白。
此外还有“container-md”,“container-lg”,“container-xl”,“container-xxl”它们依次可以充满更大尺寸的屏幕。“container”的设定与“container-sm”相同。它们具体的设置可以参考Bootstrap在线文档。
ontainer可以用于构建响应式前端。响应式前端指使用一个页面,一份代码适应不同尺寸屏幕的设备。但如手机这样的移动设备屏幕一般是高度大于宽度,所以在手机这样的“窄屏幕”上页面最好能占据100%的屏幕宽度,便于利用屏幕空间。
而在使用宽屏的计算机或电机机等设备,屏幕一般是宽度大于高度。所以页面可能会在左右两侧留出空白,实际的内容则居中显示。
上述各种尺寸的container就是用于决定具体从多么宽的屏幕开始在页面两边留出空白。
container之内,Bootstrap使用栅格系统实现元素定位和布局。“row”类会生成一个“行”,一行之内可以使用“col”定义多个列。例如下面代码。
<div class="row">
<div class="col">这是一个很长的内容</div>
<div class="col">少量内容</div>
<div class="col"></div>
</div>
如下图所示,在开发者工具中选中这个元素后,可以看到row下的三个col被设置成了相同的宽度。
col类还支持设定宽度比例。如下面代码row下的两个div一个被设置为col-2另一个被设置为col-8。
<div class="row">
<div class="col-2">col-2</div>
<div class="col-8">col-8</div>
</div>
它们将分别占据20%和80%的宽度。
导航栏是网站中常用的组件,通常出现在页面最顶端,用于显示网站主要功能或栏目,有时还提供搜索功能。下面的代码就是使用Bootstrap实现的基本的导航栏。使用HTML新增的nav标签,并设置了Bootstrap定义的“navbar”,“navbar-dark”,“bg-primary”和“navbar-expand”类。其中“navbar-expand”是导航栏中的元素不被折叠。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>使用导航栏</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark bg-primary navbar-expand">
<div class="container-fluid">
<a class="navbar-brand" href="#">我的网站</a>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">
主页
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">链接</a>
</li>
</ul>
<form class="d-flex" role="search">
<input class="form-control mb" type="search" placeholder="输入关键词" aria-label="Search">
<button class="btn btn-success text-nowrap" type="submit">搜索</button>
</form>
</div>
</div>
</nav>
<h1>标题</h1>
<p>文章内容</p>
</body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js"></script>
</html>
<ul class="navbar-nav me-auto mb-2 mb-lg-0">标签中的每一个<li>是导航栏上的一个链接。<form class="d-flex" role="search">则生成了一个靠左侧的表单作为导航栏上的搜索框。上面代码在浏览器中展示的结果如下图所示。
为了在导航栏上展现更多链接和把链接分类,可以使用下拉菜单。可在上述代码的<ul>标签最后再增加一个<li>元素。使用class="nav-item dropdown"属性。
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">
菜单
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">项目1</a></li>
<li><a class="dropdown-item" href="#">项目2</a></li>
<li><a class="dropdown-item" href="#">项目3</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">项目4</a></li>
</ul>
</li>
这个<li>标签中<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown">是下拉菜单的名称,点击这个元素会触发菜单展开。展开后显示列表<ul class="dropdown-menu">中的内容。
带下拉菜单的导航栏如下图所示。
Bootstrap提供了进度条组件,通过修改属性可动态控制进度条的完成比例。下面代码创建了4个不同样式的进度条,完成比例分别是25%,50%,75%和100%。
<br>
<div class="progress" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar" style="width: 25%">25%</div>
</div>
<br>
<div class="progress" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar progress-bar-striped bg-info" style="width: 50%">50%</div>
</div>
<br>
<div class="progress" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100" style="height: 1px">
<div class="progress-bar" style="width: 75%"></div>
</div>
<br>
<div class="progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="height: 20px">
<div class="progress-bar" style="width: 100%"></div>
</div>
前两个进度条上有数字显示,第二个进度条有斜条纹,第三个进度条高度是1px,第四个进度条高度为20px。显示效果如下图所示。
下面代码展示了Bootstrap的页码导航组件。
<ul class="pagination">
<li class="page-item"><a class="page-link" href="#">上一页</a></li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">4</a></li>
<li class="page-item"><a class="page-link" href="#">下一页</a></li>
</ul>
显示效果如下图所示。
ECharts是基于JavaScript的可视化图表库,内置丰富的图表种类,包括折线图,柱状图,饼图,散点图,地图,关系图等。其官网有丰富的示例可供参考。
为了配置的简便,对于Echarts,我们也直接通过<script>标签引入而不再使用npm安装。下面代码使用jsdeliver.com提供的cdn服务器引入了5.4.2版本的ECharts。如果需要寻找其他版本可浏览官方网站。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>
</head>
<body>
<div id="main" style="width: 800px;height:400px;"></div>
</body>
</html>
下面的代码创建了一个柱状图。
<script type="text/javascript">
// 在前面HTML代码中定义的div元素上初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 配置图表
var option = {
title: {
// 图表标题
text: '鲁迅先生说过的话APP每月访问统计'
},
tooltip: {},
legend: {
data: ['IP数量', '访问量'] // 图例
},
xAxis: { // X轴数据
data: ["2021-05", "2021-06", "2021-07", "2021-08", "2021-09", "2021-10", "2021-11", "2021-12", "2022-01", "2022-02", "2022-03", "2022-04", "2022-05", "2022-06", "2022-07", "2022-11", "2022-12", "2023-01", "2023-02", "2023-03"]
},
yAxis: {},
series: [
{ // Y 轴数据 1,每月访问的IP数量
name: 'IP数量',
type: 'bar',
data: [238, 492, 361, 299, 257, 374, 209, 185, 165, 103, 98, 102, 75, 100, 63, 3, 85, 72, 78, 75]
},
{ // Y 轴数据 2,每月访问量
name: '访问量',
type: 'bar',
data: [1962, 3299, 2366, 2146, 1798, 3115, 1810, 2405, 1330, 486, 407, 490, 368, 576, 369, 13, 968, 355, 686, 578]
}
]
};
// 显示图表
myChart.setOption(option);
</script>
下图展示了上述代码绘制的柱形图。图的横坐标是月份,纵坐标是数量,两种颜色分别表示访客IP数量和访问量。因为同一个IP每月可能访问多次,所以访问量总是大于IP数量。
最常用的图表是柱形图和折线图。上一小节已经给出了柱状图的示例。在上一节图表的基础上,把series中的数据的type从“bar”改为“line”即可把柱状图更改为折线图。下图展示了把在上面一个例子基础上,把访问量数据换成柱形图的效果。
这个图把两组数据放到一张图表中展示,但两组数据的范围差距略大,折线图的最大值超过3000,而柱形图最大值只有500,这种情况可以使用多坐标轴对数据展示。修改原配置中“yAxis: {}”添加两个坐标轴。
yAxis: [
{
type: 'value',
scale: true,
name: 'IP数',
},
{
type: 'value',
scale: true,
name: '访问量',
}
]
然后给series中两组数据分别添加yAxisIndex属性。对于IP数量,设置为“yAxisIndex: 0”,对于访问量设置为“yAxisIndex: 1”。经过这一修改,柱状图也能较好的展示了,修改后的效果如下图所示。
还可以绘制散点图,如下面代码描述了每月访问IP数量和每月总访问量的关系。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>
</head>
<body>
<div id="main" style="width: 400px;height:400px;"></div>
</body>
<script type="text/javascript">
// 在前面HTML代码中定义的div元素上初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 配置图表
var option = {
title: {
// 图表标题
text: '每月IP数和访问量关系'
},
xAxis: { // X轴名称
name: 'IP数'
},
yAxis: { // Y轴名称
name: '访问量'
},
series: [
{
symbolSize: 15,
data: [[85, 968], [72, 355], [78, 686], [75, 578], [3, 13], [103, 486], [98, 407], [361, 2366], [299, 2146], [185, 2405], [257, 1798], [492, 3299], [100, 576], [374, 3115], [209, 1810], [165, 1330], [238, 1962], [102, 490], [75, 368], [63, 369]],
type: 'scatter'
}
]
};
// 显示图表
myChart.setOption(option);
</script>
</html>
下图展示了每月IP数量和访问量的散点图。代码中的symbolSize用来控制图中点的大小。
Echars提供了多种饼图样式。下面的代码创建了一个饼图。
// 在前面HTML代码中定义的div元素上初始化echarts实例
let myChart = echarts.init(document.getElementById('main'));
// 配置图表
let option = {
title: {
text: '鲁迅先生说过的话APP海外访客IP',
subtext: '国家占比',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical', left: 'left'
},
series: [
{
name: '访问来自',
type: 'pie',
radius: '50%',
data: [
{ value: 8, name: '日本' }, { value: 1, name: '俄罗斯' }, { value: 21, name: '印度' },
{ value: 2, name: '阿根廷' }, { value: 19, name: '美国' }, { value: 1, name: '捷克' },
{ value: 2, name: '英国' }, { value: 1, name: '缅甸' }, { value: 10, name: '马来西亚' },
{ value: 1, name: '印度尼西亚' }, { value: 6, name: '法国' },
{ value: 1, name: '意大利' }, { value: 23, name: '新加坡' },
],
emphasis: {
itemStyle: {
shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
// 显示图表
myChart.setOption(option);
下图展示了上述代码绘制的饼图,图中效果是鼠标移动到饼图上,显示出具体的数据。option中的legend设定了图例的位置。radius参数设定了饼图的大小,支持相对大小和绝对大小。
下图展示了使用Echars绘制的南丁格尔玫瑰图和环形图。
ECharts提供多种关系图的布局,其中力引导布局方式支持自动计算点和边的位置,只需要提供点的信息和点间连线的信息,ECharts可以自动绘制关系图。下面的代码绘制了鲁迅先生说过的话APP中某段事件内,部分文章的点击量。
<head>
<meta charset='utf8' />
</head>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>
<script>
// 把 main 区域设置成接近铺满窗口的大小
let width = window.innerWidth;
let height = window.innerHeight;
main.style.height = height * 0.95 + "px";
main.style.width = width* 0.95 + "px";
// 初始化Echarts 关系图
let chartDom = document.getElementById('main');
let myChart = echarts.init(chartDom);
let option;
// 显示加载中提示界面
myChart.showLoading();
option = {
tooltip: {},
legend: [
{
data: ['文集', '文章']
}],
series: [
{
name: '点击量',
type: 'graph',
layout: 'force',
// 数据较多,这里仅保留部分,全部数据请参考随书源码
data: [{"id": 0, "name": "鲁迅全集", "symbolSize": 1, "value": 2135, "category": 0}, {"id": 1, "name": "朝花夕拾", "symbolSize": 19.5, "value": 590, "category": 0}, {"id": 2, "name": "无常", "symbolSize": 3.7, "value": 37, "category": 1}],
links: [{"source": 0, "target": 1}, {"source": 1, "target": 2}],
categories: [
{
"name": "文集"
},
{
"name": "文章"
}
],
roam: true,
force: {
repulsion: 80
},
label: {
show: true,
position: 'right',
formatter: '{b}'
},
labelLayout: {
hideOverlap: true
},
scaleLimit: {
min: 0.4,
max: 2
},
lineStyle: {
color: 'source',
curveness: 0.3
}
}
]
};
// 根据 option 中的配置显示关系图
myChart.setOption(option);
// 隐藏正在加载的提示
myChart.hideLoading();
</script>
下图展示了代码中定义的力引导布局关系图的显示效果。鼠标指向某个点时,会显示出节点数据详细信息。
对于地图上数据的展示,EChats提供了基于百度地图API的数据展示。如需使用这个功能,需要到百度地图API网站注册,并申请密钥。对于个人开发者和非商业用途的使用可以免费获得密钥。下面代码展示了地图数据展示所使用的代码的HTML部分。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=你的密钥"></script>
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.4.2/dist/extension/bmap.min.js"></script>
</head>
<body>
<div id="main" style="width: 1300px;height:900px;"></div>
</body>
</html>
上述代码中的“你的密钥”需要被替换成在百度地图API网站申请到的密钥。所以这个代码直接打开并不能正常使用。如需查看效果可访问本书网站链接中的在线示例。
下面代码则是JavaScript和数据。
<script type="text/javascript">
// 在前面HTML代码中定义的div元素上初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// IP数量数据,数据太多,这里有删减,全部数据请见随书代码
let data = [{"name": "四川省", "value": 253} , {"name": "北京", "value": 221}, {"name": "广东省", "value": 388}];
// 用到的省市在地图上的坐标(这个数据不一定准确,这里仅做示例,请勿再其他场景下使用)
const geoCoordMap = {"四川省": [104.06, 30.67], "北京": [116.46, 39.92], "广东省": [113.23, 23.16]}
// 把data中的数据转换成用于展示的数据
const convertData = function (data) {
var res = [];
for (var i = 0; i < data.length; i++) {
var geoCoord = geoCoordMap[data[i].name];
if (geoCoord) {
res.push({ name: data[i].name, value: geoCoord.concat(data[i].value) });
}
}
return res;
};
var option = {
title: {
text: '鲁迅先生说过的话APP国内用户IP分布 - 百度地图',
subtext: '数据来自鲁迅先生说过的话APP', sublink: 'http://luxunquotation.codeplot.top/',
left: 'center'
},
tooltip: { trigger: 'item' },
bmap: { center: [104.114129, 37.550339], zoom: 5, roam: true,
mapStyle: {
styleJson: [// 地图风格配置,后面代码被删减,完整版请查阅随书代码
{ featureType: 'water', elementType: 'all', stylers: { color: '#d1d1d1' } },
{ featureType: 'land', elementType: 'all', stylers: { color: '#f3f3f3' }}
]
}
},
series: [
{
name: '访客数量',
type: 'scatter', coordinateSystem: 'bmap', data: convertData(data),
symbolSize: function (val) { return val[2] / 10;}, encode: { value: 2 },
label: { formatter: '{b}', position: 'right', show: false }, emphasis: { label: {show: true} }
},
{ name: 'Top 5', type: 'effectScatter', coordinateSystem: 'bmap',
data: convertData( data.sort(function (a, b) { return b.value - a.value;}) .slice(0, 6)),
symbolSize: function (val) { return val[2] / 10; }, encode: {value: 2},
showEffectOn: 'render', rippleEffect: { brushType: 'stroke' },
label: { formatter: '{b}', position: 'right', show: true },
itemStyle: { shadowBlur: 10, shadowColor: '#333' },
emphasis: { scale: true }, zlevel: 1
}
]
};
// 显示图表
myChart.setOption(option);
</script>
可使用Echarts和百度地图API自行动手生成基于地图的数据可视化图。除了原始数据外,还可以根据原始数据构造了一组排名前五的数据,使用单独符号在地图上展示。
Vue的发音和单词view相同,是一种用于构建用户界面的JavaScript框架。Vue使用标准的HTML,CSS和JavaScript。
安装Node.js后,通过执行下面的命令创建一个空的vue项目。
npm init vue
该命令将使用npm下载并执行create-vue,一个vue官方提供的用于初始化新vue项目的工具。create-vue会要求输入项目名称,并询问一系列项目配置的问题,可以直接用回车选择默认选项。使用create-vue创建的vue项目采用vite做打包。
项目创建完成后,package.json中的依赖有Vue和vite。但依赖还没有被下载,进入项目目录中执行npm istall下载和安装项目依赖。依赖安装完成后,可以直接执行npm run dev启动开发服务器程序,并可通过浏览器预览当前的开发的界面。
README.md文件是项目的说明文件,使用markdown语法。.gitignore文件是git版本管理工具关于忽略的文件的配置。index.html中的内容如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
src目录下的main.js是源码的入口文件,App.vue则是Vue的App源码,它代表了一个Vue组件, 如果使用Visual Studio Code编辑器,可以安装Vue的插件便于编辑.vue后缀的文件。其中main.js的内容入下。
import { createApp } from 'vue'
import App from './App.vue'
import './assets/main.css'
createApp(App).mount('#app')
引入App.vue组件和main.css文件。并在index.html中的id为app的div元素上挂载这个Vue的App。Vue中使用createApp创建App实例,并使用其mount方法和DOM元素关联。
App.vue中除了style标签外的内容如下。
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
在vue文件中出现了Vue自定义的标签,例如
项目根目录下的vite.config.js文件时vite的配置,其中内容如下。
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
Vite项目的index.html文件在项目根目录,而不像webpack在dist目录。Vite在构建时将以index.html文件为入口。在执行npm run dev会启动vite开发服务器,同样也是以根目录的index.htm为入口。
执行npm run build可通过Vite构建项目,构建后的代码在dist中,默认就是production版本。
使用默认配置时Vite构建的代码默认只支持较新版本的浏览器。但也可通过配置修改最低支持的浏览器标准。通常不需要修改这一配置,详细用法可参考Vite文档。
Vue中组件有两种定义方式。8.3.1小节通过create-vue创建的项目中看到的是单文件组件。组件中大致包含视图和代码两部分。前面所述App.vue中的<template>标签中的内容就是对视图的定义。它们将被渲染到index.html中的对应的div标签(因为main.js中绑定了这个div标签)。
create-vue创建的项目包含了多个自定义组件,比较繁琐,为了简便,先删除“src/components”目录, “src/ assets/ base.css”文件和 “src/ assets/ logo.svg”文件。清空“src/App.vue”和“src/ assets/ main.css”中的内容。
在App.vue中写入下面内容。
<script>
export default {
data() {
return {
count: 0 // 这个count将被渲染到下面 {{ count }} 的位置
}
}
}
</script>
<template>
<h1>Hello Vue</h1>
<p v-on:click="count++">点击次数: {{ count }}</p>
</template>
通过npm run dev启动开发服务器(如果开发服务器已经在运行中则不需要),通过输出的地址访问页面,将看到如下图所示的页面。
可以看到<templete>标签中的标签作为HTML内容被放到了页面上。<script>标签中的data()里的count变量被渲染到了
标签的内容中。v-on:click也是Vue定义的标签属性,意为给<p>标签的点击事件绑定了处理函数,在这里是只有一个语句的匿名处理函数“count++”,这里访问到的count同样是<script>定义的。故点击<p>标签后可观察到显示的点击次数增加。
前一小节使用的“{{ count }}”被称为文本插值,用于把一个JavaScript表达式作为文本内容插入到HTML视图中。如果JavaScript中对应变量的值发生改变,DOM上展示的内容会自动变化,Vue会负责处理这一更新的过程。这类似于本书之前介绍过的修改元素innerText属性来更新DOM内容的所做的事情。
但Vue负担了更新DOM的工作,我们的JavaScript程序则只需要关心维护变量的值。
如果要把JavaScript表达式作为HTML内容插入,可以使用标签的v-html属性。如下面语句把表达式作为HTML内容插入到
标签内,如同修改p元素的innerHTML属性。
<p v-html="`点击次数:<span> ${count}</span>`"></p>
要动态设定HTML元素的属性则可使用属性绑定。如下面的代码把div的id设置成变量div_id的值。
<div v-bind:id="div_id"></div>
这一属性可简写为“:id”。
前一小节使用到的v-on:click用于绑定元素的事件,v-on:可以用“@”代替,如“@click”可绑定点击事件。
绑定的事件处理函数除了使用匿名函数外,更常见的用法是引用methods中定义的方法。如下面的代码。
<script>
export default {
data() {
return {
count: 0,
todo: [{id: 1, name: "购物"}, {id: 2, name: "运动"}]
}
},
methods: {
hello(event) {
console.log(event);
console.log(this);
console.log(this.todo);
alert("hello");
}
}
}
</script>
<template>
<h1>Hello Vue</h1>
<p @click="count++">点击次数: {{ count }}</p>
<button @click="hello">Hello</button>
<ul>
<li v-for="item in todo">{{ item.id }} - {{ item.name }}</li>
</ul>
</template>
点击按钮后会调用hello方法。这里的event参数是默认传入的事件对象,与第7章中介绍的一致,但这里事件处理方法中的this变量却不再代表正在发生该事件的元素,而表示了当前的Vue组件对象。
v-if属性用于根据条件渲染,如果一个元素带有v-if属性,那么只有在这个属性中的JavaScript表达式的值为真时,这个元素才会显示。还有对应的v-else,和v-else-if。与v-if类似的还有v-show,区别是v-if的条件为false时,这个元素就不会被渲染,但这种情况下v-show会渲染元素,但不显示,即元素被隐藏。
v-for执行用于渲染JavaScript的列表。如有下面的变量。
todo: [{id: 1, name: "购物"}, {id: 2, name: "运动"}]
可通过如下方式渲染一个列表。
<ul>
<li v-for="item in todo">{{ item.id }} - {{ item.name }}</li>
</ul>
这段代码的显示效果和渲染出的结果如下图所示。如果对应变量的内容发生变化时,该列表也会动态更新。
除了提供从JavaScript表达式到视图上内容的绑定,Vue还支持把表单输入的值绑定到JavaScript变量。这样JavaScript程序不必再通过类似input元素的value属性获取表单元素输入值了。下面的代码给出了input标签的绑定方法。
<script>
export default {
data() {
return {
message: ""
}
},
methods: {
}
}
</script>
<template>
<p>输入的信息是: {{ message }}</p>
<input v-model="message" placeholder="请输入" />
</template>
<input>标签的输入内容将存入message变量,同时再通过文本内容绑定输出到<p>标签中。
对于radio类型的<input>标签,多个标签可绑定到同一个变量,变量的值就是选定的<input>标签的value。而checkbox还支持绑定到一个列表变量,被选中的value都会出现在列表中。
v-model也同样适用于<textarea>元素和<select>对象
本章介绍了几种常见的前端开发框架。使用框架开发可以大大提高开发效率,并把精力集中在业务逻辑上。对于框架使用的很多细节这里都没有做详细的介绍,而且这些框架仍然在持续更新中,随时可能出现新的功能,在使用这些框架开发时,还请参考它们官方发布的在线文档。
本节将使用Vue.js开发一个支持英汉和汉英查询的词典。
词典使用的数据来自开源项目https://github.com/skywind3000/ECDICT,但经过删减和处理,因为原词典体积太大。处理后的词典文件为ecdict.small.json。
词典文件格式是json,是一个包含14,048个单词的列表。其中每个单词是一个长度为3的列表,其内容是“单词”,“音标”和“中文释义”。下面给出了几个单词的示例。
[['a', 'ei', '第一个字母 A; 一个; 第一的\\r\\nart. [计] 累加器, 加法器, 地址, 振幅, 模拟, 区域, 面积, 汇编, 组件, 异步'],
['aback', "ә'bæk", 'adv. 向后, 朝后, 突然, 船顶风地'],
['abandon', "ә'bændәn", 'vt. 放弃, 抛弃, 遗弃, 使屈从, 沉溺, 放纵\\nn. 放任, 无拘束, 狂热']]
使用npm init vue命令,项目名称为h5dict,其他选项全部采用默认配置。然后输入下面命令进入项目目录并安装依赖。
cd h5dict
npm install
删除“src/components”目录, “src/ assets/ base.css”文件和 “src/ assets/ logo.svg”文件。清空“src/App.vue”和“src/ assets/ main.css”中的内容。在“src/App.vue”中写入下面内容。
<script>
export default {
data() {
return {
key: ""
}
},
methods: {
}
}
</script>
<template>
<h1>英汉汉英词典</h1>
<input v-model="key" placeholder="请输入要查询的内容" />
<p>{{ key }}</p>
</template>
使用命令启动开发服务器。然后根据输出的服务器地址,从浏览器打开测试页面,当前页面效果如下图所示,此时仅实现了把输入的单词显示到下方。
在“export default {”后添加如下代码。
mounted() {
fetch("ecdict.small.json").then(response => {
return response.json();
}).then(response => {
this.dict = response;
})
}
mounted方法将在组件初始渲染并创建DOM后运行,可以用来执行初始化组件的代码,比如在这里用于下载数据文件。
这里直接把下载完成的数据存到dict变量中。应该在data()中也增加dict变量。
data() {
return {
key: "",
dict: [],
}
},
可使用<input>标签的@change属性绑定查找单词的处理函数。Vue还支持侦听器的功能,可以让我们在Vue组件内某个变量被修改时执行操作。实现代码如下。
<script>
export default {
mounted() {
fetch("ecdict.small.json").then(response => {
return response.json();
}).then(response => {
this.dict = response;
})
},
data() {
return {
key: "",
dict: [],
word: "",
phonetic: "",
chinese: "",
}
},
watch: {
key(newKey,oldKey) {
for (let d of this.dict) {
if (d[0] == newKey) {
this.word = d[0];
this.phonetic = d[1];
this.chinese = d[2];
}
}
}
}
}
</script>
<template>
<h1>英汉汉英词典</h1>
<input v-model="key" placeholder="请输入要查询的内容"/>
<p>{{ word }}</p>
<p>[{{ phonetic }}]</p>
<p>{{ chinese }}</p>
</template>
通过在watch中添加“key(newKey,oldKey) {”实现了在key变化时执行特定操作。这里的查词操作是遍历整个词典,如果发现词典里的词和用户输入的key一致时,把词典里的结果输出到输入框下方的三个<p>标签中。实现的结果如下图所示。
首先需要一个变量检查是否匹配到准确的词汇。仅当未匹配到准确词汇时才到中文释义中寻匹配关键词,借此实现中文模糊匹配。
对于英文模糊匹配这里实现前缀匹配,即输入关键词“a”,则可以匹配到所有以“a”开头的关键字。另外,增加忽略大小写的功能,因为词库里的词都是小写,简单起见,每次查询直接把key转换成小写。
模糊匹配到的词需要用列表形式展示出来,所以定义新的变量candidates如下用于保存要展示的模糊匹配到的词语
candidates: []
在</template>前增加下面的代码,用于显示candidates列表。
<hr>
<div>
<div v-for="item in candidates" @click="select(item[0])" style="border-style: solid; border-color: black;">
<p >{{ item[0] }}</p>
<p >{{ item[1] }}</p>
</div>
</div>
为了实现点击备选单词就跳转到备选单词的功能,要给备选单词添加点击事件,调用select方法。下面是select的实现。
select(k) {
this.key = k;
}
实现模糊查询把对key的侦听器修改为如下代码。
watch: {
key(newKey,oldKey) {
newKey = newKey.replace(/^\s*|\s*$/g,""); // 去除前后的空格
if (!newKey)
return;
newKey = newKey.toLocaleLowerCase()
this.candidates = [];
let found = false;
for (let d of this.dict) {
if (d[0] == newKey) {
this.word = d[0];
this.phonetic = d[1];
this.chinese = d[2];
found = true;
}
else
if (this.candidates.length < 100 && d[0].startsWith(newKey))
this.candidates.push([d[0], d[2]])
}
if (!found) {
for (let d of this.dict) {
if (this.candidates.length > 300)
break;
if (d[2].includes(newKey))
this.candidates.push([d[0], d[2]]);
}
}
}
}
前缀匹配英文单词时,当输入的字母较少时,可能一次匹配到很多单词,所以添加限制,只有在候选单词数量不超过100个时,才对单词做前缀匹配。
至此词典的基本功能已经实现。