前端实战——vue 文章概览 本篇文章主要从基础知识、目录结构、vue文件构成、组件、父子组件传值、应用六个方面介绍了vue。
基础知识 VUE Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
优点:
简单轻巧,功能强大,拥有非常容易上手的 API;
可组件化 和 响应式设计;
实现数据与结构分离,高性能,易于浏览器的加载速度;
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业务逻辑)
MVVM 将数据双向绑定 (data-binding)作为核心思想,View 和 Model 之间没有联系,它们通过 ViewModel 这个桥梁进行交互。 而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
Model 和 ViewModel 之间的交互是双向的,因此 View 的变化会自动同步到 Model,而 Model 的变化也会立即反映到 View 上显示。
当用户操作 View,ViewModel 感知到变化,然后通知 Model 发生相应改变;反之当 Model 发生改变,ViewModel 也能感知到变化,使 View 作出相应更新。
目录结构
vue文件构成 一个vue页面主要包括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
中。
完成一个组件的创建和引用的步骤如下:(此处以局部组件为例)
写好子组件Child.vue
,并定义好其导出的组件名称为timeline
在components中声明组件
在父页面(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部分的功能,就使用了组件的嵌套。
父子组件传值 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>
实现点击按钮增加行 效果如图
在标签中使用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>
参考链接
vue官网