前端实战——vue

文章概览

本篇文章主要从基础知识、目录结构、vue文件构成、组件、父子组件传值、应用六个方面介绍了vue。

基础知识

VUE

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

优点:

  1. 简单轻巧,功能强大,拥有非常容易上手的 API;
  2. 可组件化 和 响应式设计;
  3. 实现数据与结构分离,高性能,易于浏览器的加载速度;
  4. MVVM 模式,数据双向绑定,减少了 DOM 操作,将更多精力放在数据和业务逻辑上。

MVVM

Model–View–ViewModel(MVVM) 是一个软件架构设计模式 , 源自于经典的 Model–View–Controller(MVC)模式。MVVM 的出现促进了 GUI 前端开发与后端业务逻辑的分离,极大地提高了前端开发效率。

MVVM = M + V + VM

M:model数据模型 (存储与处理数据)
V:view视图 (UI用户界面)
VM:ViewModel视图模型 (js业务逻辑)

sAT3CT.png

MVVM 将数据双向绑定(data-binding)作为核心思想,View 和 Model 之间没有联系,它们通过 ViewModel 这个桥梁进行交互。 而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

Model 和 ViewModel 之间的交互是双向的,因此 View 的变化会自动同步到 Model,而 Model 的变化也会立即反映到 View 上显示。

当用户操作 View,ViewModel 感知到变化,然后通知 Model 发生相应改变;反之当 Model 发生改变,ViewModel 也能感知到变化,使 View 作出相应更新。

目录结构

sATnDs.png

vue文件构成

一个vue页面主要包括3个部分:

  1. 界面展示代码
  2. 业务实现代码
  3. 界面布局代码
1
2
3
4
5
6
7
8
9
10
11
12
<template>
<!-- 界面展示代码 -->
</template>

<script>
// 业务实现代码
</script>

<style>
/* 界面布局代码 */
</style>

界面展示代码

页面展示代码由template包含。这部分负责页面设计的实现。

开发人员主要在这一部分进行页面设计,时这部分的默认语言是HTML。主要是使用h2,v-form,v-container,v-row,v-btn等标签来进行页面的结构/样式设计等。与此同时,在这些标签的属性部分可以利用一些vue语法来对相应的内容进行控制,比如v-if 、v-for,v-model等等。

需要注意的是,每个*.vue文件最多放一个template块。

业务实现代码

业务实现代码由script包含。这部分负责页面逻辑的实现。

这部分的默认语言是JavaScript,这一部分可引入相关vue组件、一些js文件,对页面中的相关变量进行操作。

需要注意的是,每个*.vue文件最多放一个script块。

详细相关如下:

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
<script>
import child from "../components/child.vue"
import vue from 'vue'

export default {
name: "Home",
data() {
//数据定义
return {};
},

methods: {
// 组件的方法
},

watch: {
// watch监听方法,擅长处理的场景:一个数据影响多个数据
},

computed: {
// computed擅长处理的场景:一个数据受多个数据影响
},

beforeCreate: function() {
// 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
},

created: function() {
// 实例已经创建完成之后被调用。
//在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算,
//watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
},

beforeMount: function() {
// 在挂载开始之前被调用:相关的 render 函数首次被调用。
},

mounted: function() {
// 编译好的HTML挂载到页面完成后执行的事件钩子
// el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
// 此钩子函数中一般会做一些ajax请求获取数据进行数据初始化
//console.log("Home done");
},

beforeUpdate: function() {
// 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
// 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
},

updated: function() {
// 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
// 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
// 然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。
// 该钩子在服务器端渲染期间不被调用。
},

beforeDestroy: function() {
// 实例销毁之前调用。在这一步,实例仍然完全可用。
},

destroyed: function() {
// Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,
// 所有的事件监听器会被移除,所有的子实例也会被销毁。
// 该钩子在服务器端渲染期间不被调用。
}
};
</script>


页面布局代码

页面布局代码由style包含。这部分负责页面样式的实现。

这部分的默认语言是css。这一部分主要是定义一些类,来完成相关样式的实现。比如backgroundImage,height,color等等。

*.vue文件支持多个style标签

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="ZZZ">{{ msg }}</div>
</template>

<script>
export default {
data () {
return {
msg: 'Hello world!'
}
}
}
</script>

<style>
.ZZZ {
color: red;
}
</style>

组件

概念

Web 页面上的每个独立的可视/可交互区域视为一个组件,组件就好像我们的 PC 组装机一样,整个机器(应用)由不同的部件组成,例如显示器、主板、内存、显卡、硬盘等等。页面就是由一个个类似这样的部分组成的,比如导航、列表、弹窗、级联、下拉菜单等。页面只不过是这些组件的容器,组件自由组合形成功能完整的界面,当不需要某个组件,或者想要替换某个组件时,可以随时进行替换和删除,而不影响整个应用的运行。

在vue中,组件是可以进行复用的。

局部组件步骤

为方便说明,定义场景如下:

Father.vue为父组件/页面,一般放在/views中。
Child.vue为子组件,一般放在/components中。

完成一个组件的创建和引用的步骤如下:(此处以局部组件为例)

  1. 写好子组件Child.vue,并定义好其导出的组件名称为timeline
  2. 在components中声明组件
  3. 在父页面(Father.vue)中引用子组件(Child.vue

以下使用代码来说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- Child.vue -->
<template>

<v-btn text color="primary" >Cancel</v-btn>
<v-btn text color="primary" >OK</v-btn>

</template>

<script>
export default {
name: "timeline",
}
</script>

<style>
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- Father.vue -->

<template>
<timeline></timeline>
</template>

<script>
import timeline from "@/components/Child.vue"

export default {
components: {
timeline
},
}
</script>

最终,在父页面,我们可以看到有两个按钮。

嵌套设计应用

在本次前端设计中,为了实现cpe部分的功能,就使用了组件的嵌套。

sATG2F.png

父子组件传值

V-model

v-model指令的本质是: 它负责监听用户的输入事件,从而更新数据,并对一些极端场景进行一些特殊处理。同时,v-model会忽略所有表单元素的value、checked、selected特性的初始值,它总是将vue实例中的数据作为数据来源。 然后当输入事件发生时,实时更新vue实例中的数据。

props

props用于 父组件向子组件 传递数据。这样的数据传递是实时的。

Father.vue中引用Child.vue,首先在子组件标签当中声明将自己的FFF变量值传送给子组件中的FToC_Data

子组件中的props指向变量FToC_Data,并通过{{ }}的方式进行展现。

最终会在子组件的按钮上方看见父组件传输过来的消息。

emit

emit用于 子组件向父组件 传递数据。但子组件向父组件传递数据的不是实时的,需要通过emit方法来触发传递数据的事件event

以下是代码示例

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
<!-- Child.vue -->
<template>
<p>
我是子组件,这是父组件传给我的数据:{{FToC_Data}}
</p>

<v-btn text color="primary" >Cancel</v-btn>
<v-btn text color="primary" @click="SendData">OK</v-btn>

</template>

<script>
export default {
name: "timeline",
props: ['FToC_Data'],
data(){
return{
CToF_Data:'I am son!'
}
},
methods:{
sendData(){
this.$emit('sendData',this.CToF_Data)
}
}
}
</script>

<style>
</style>
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
<!-- Father.vue -->

<template>
<timeline :FToC_Data='FFF'></timeline>
<p>
我是父组件,这是子组件传递给我的值:{{CToF_Data}}
</p>
</template>

<script>
import timeline from "@/components/Child.vue"

export default {
components: {
timeline
},
data(){
return{
FFF:'I am Father!',
CToF_Data:''
}
},
methods:{
getChildData(data){
this.CToF_Data = data;
}
}
}
</script>

应用

表单构建

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<template>
<v-form ref="form" v-model="PbasicData.basic" lazy-validation>
<v-container>
<h2 class="display-1 basil--text white--text text-left" style="padding-top: 2rem;padding-bottom: 1rem;">基本信息</h2>
<v-row>
<v-col cols="6" sm="6" md="8">
<v-text-field
label="文章标题"
filled
placeholder="补丁日: 2020-03【官方名称】多个产品高危漏洞安全风险通告"
v-model="PbasicData.title"
></v-text-field>
</v-col>
<v-col cols="6" sm="6" md="6">
<v-combobox
v-model="PbasicData.relegations"
:items="PbasicData.r_items"
label="事件漏洞归属"
multiple
filled
></v-combobox>
</v-col>
<v-col class="d-flex" cols="12" sm="6">
<v-select
:items="PbasicData.tlp_items"
filled
v-model="PbasicData.tlp"
label="TLP协议"
></v-select>
</v-col>
<v-col class="d-flex" cols="12" sm="6">
<v-select
v-model="PbasicData.vul_rank"
:items="PbasicData.vul_items"
filled
label="威胁等级"
></v-select>
</v-col>
<v-col class="d-flex" cols="12" sm="6">
<v-select
:items="PbasicData.affect_items"
v-model="PbasicData.affect_rank"
filled
label="影响范围"
></v-select>
</v-col>
<v-col cols="12" md="12">
<v-textarea
filled
v-model="PbasicData.desc"
label="整体事件描述"
value="【xxxx】年【xxx】月【xxx】日,360CERT监测到`【xxxxx官方】`发布了`【3月份】`的安全更新。

`【xxx组件/官方】`是【...】

此次安全更新发布了`【115】`个漏洞补丁,主要涵盖了`【】``【】``【】`。其中包括`【26】`个严重漏洞,`【88】`个高危漏洞。

对此,360CERT建议广大用户及时安装最新补丁,做好资产自查以及预防工作,以免遭受黑客攻击。"
></v-textarea>
</v-col>
<v-col cols="12" md="12">
<v-textarea
filled
v-model="PbasicData.fix_desc"
label="整体修复建议/缓解措施"
value="升级到xxx版本,下载地址为:[xxx](xxx) 。"
></v-textarea>
</v-col>

</v-row>
</v-container>
</v-form>
</template>

<script>
export default {
name: "basic",
data () {
return {
PbasicData:{
basic: true,
title: '',
relegations: null,
r_items: [
'JavaScript漏洞',
'.net漏洞',
'web应用漏洞',
'java漏洞',
'python漏洞',
'ruby漏洞',
'php漏洞',
'c漏洞',
'golang漏洞',
'浏览器',
'二进制漏洞',
],
tlp: null,
tlp_items: [
'白',
'绿',
'黄',
'红',
],
vul_rank: null,
vul_items: [
'低危',
'中危',
'高危',
'严重',
],
affect_rank: null,
affect_items :[
'有限',
'一般',
'广泛',
],
desc: '',
fix_desc: '',

}
}
},

}

</script>

实现点击按钮增加行

效果如图

sAT88U.png

在标签中使用v-for

<v-form>标签中使用v-for,在<v-form>标签包裹下的内容就形成了一个可复制的单元集。删除按钮放在<v-form>标签内,添加按钮放在<v-form>标签外。

定义数组referenceData用来对每个单元集进行记录。一是方便指定删除,二是方便指定存值。

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
<template>
<v-form>
<v-container>
<h2 class="display-1 basil--text white--text text-left" style="padding-top: 2rem;padding-bottom: 1rem;">参考链接</h2>
<v-form :inline="true" class="demo-form-inline" v-for="(item, i) in referenceData" :key="i">
<v-form-item>
<v-row>
<v-col cols="6" md="5">
<v-text-field
v-model="item.desc"
label="描述"
filled
></v-text-field>
</v-col>
<v-col cols="6" md="6">
<v-text-field
label="链接"
filled
v-model="item.link"
></v-text-field>
</v-col>
<v-col cols="6" md="1">
<v-btn small class="mx-2" outlined fab color="white" @click="Delete(item.index)">
<v-icon dark>mdi-minus</v-icon>
</v-btn>
</v-col>
</v-row>
</v-form-item>
</v-form>

<v-row>
<v-col cols="6" md="11">
<v-btn small class="mx-2" outlined fab color="white" @click="AddForm">
<v-icon dark>mdi-plus</v-icon>
</v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</template>


<script>
export default {
name: "reference",
data: () => ({
referenceData: [
{
index: 0,
desc: '',
link: ''
}
]
}),
methods: {
AddForm () {
this.referenceData.push({
index: this.referenceData.length,
desc: '',
link: ''
})
console.log(this.referenceData)
},
Delete (index) {
this.referenceData.splice(index, 1)
for (let i in this.referenceData) {
this.referenceData[i].index = i
}
}
}
}
</script>


<style>
</style>

参考链接

  1. vue官网