赞
踩
渐进式JavaScript框架
声明式渲染→组件系统→客户端路由→集中式状态管理→项目构建
官网
特点:
易用:熟悉HTML、CSS、JavaScript知识后,可快速上手Vue
灵活:在一个库和一套完整框架之间自如伸缩
高效:20kB运行大小,超快虚拟 DOM
<div id="app">
<div>{{msg}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
msg: 'HelloWorld' }
})
</script>
el: 元素的挂载位置(值可以是CSS选择器或者DOM元素)
data:模型数据(值是一个对象)
将数据填充到HTML标签中
插值表达式支持基本的计算操作
概述编译过程的概念(Vue代码→Vue框架编译→原生js代码)
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
<span>Message: {{ msg }}</span>
迄今为止,在我们的模板中,我们一直都只绑定简单的 property 键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
指令的本质就是自定义属性
指令的格式:以v-开始(比如:v-cloak)
标签中涉及到data属性的判断== 字符串加不加引号都行 ===必须一致
deld: '23' ----------------------插值----------------------- <div id="app"> <div>{{deld===23?2:1}}</div> </div> 1 <div id="app"> <div>{{deld=='23'?2:1}}</div> </div> 2 <div id="app"> <div>{{deld==23?2:1}}</div> </div> 2 -----------------------指令------------------------- <div v-if='deld==="23"'>是否显示</div> 显示 <div v-if='deld===23'>是否显示</div> 不显示 <div v-if='deld==23'>是否显示</div> 显示 <div v-if='deld=="23"'>是否显示</div> 显示
插值表达式存在的问题:“闪动”(多次刷新可能会先显示插值表达式的代码之后才会显示赋值)
如何解决该问题:使用v-cloak指令
解决该问题的原理:先隐藏,替换好值之后再显示最终的值
<style type="text/css"> /* 1、通过属性选择器 选择到 带有属性 v-cloak的标签 让他隐藏 */ [v-cloak]{ /* 元素隐藏 */ display: none; } </style> <body> <div id="app"> <!-- 2、 让带有插值 语法的 添加 v-cloak 属性 在 数据渲染完场之后,v-cloak 属性会被自动去除, v-cloak一旦移除也就是没有这个属性了 属性选择器就选择不到该标签 也就是对应的标签会变为可见 --> <div v-cloak >{{msg}}</div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ // el 指定元素 id 是 app 的元素 el: '#app', // data 里面存储的是数据 data: { msg: 'Hello Vue' } }); </script> </body> </html>
v-text指令用于将数据填充到标签中,作用于插值表达式类似,但是没有闪动问题
如果数据中有HTML标签会将html标签一并输出
注意:此处为单向绑定,数据对象上的值改变,插值会发生变化;但是当插值发生变化并不会影响数据对象的值
<div id="app"> <!-- 注意:在指令中不要写插值语法 直接写对应的变量名称 在 v-text 中 赋值的时候不要在写 插值语法 一般属性中不加 {{}} 直接写 对应 的数据名 --> <p v-text="msg"></p> <p> <!-- Vue 中只有在标签的 内容中 才用插值语法 --> {{msg}} </p> </div> <script> new Vue({ el: '#app', data: { msg: 'Hello Vue.js' } }); </script>
用法和v-text 相似 但是他可以将HTML片段填充到标签中
可能有安全问题, 一般只在可信任内容上使用 v-html
,永不用在用户提交的内容上
它与v-text区别在于v-text输出的是纯文本,浏览器不会对其再进行html解析,但v-html会将其当html标签解析后输出。
<div id="app"> <p v-html="html"></p> <!-- 输出:html标签在渲染的时候被解析 --> <p>{{message}}</p> <!-- 输出:<span>通过双括号绑定</span> --> <p v-text="text"></p> <!-- 输出:<span>html标签在渲染的时候被源码输出</span> --> </div> <script> let app = new Vue({ el: "#app", data: { message: "<span>通过双括号绑定</span>", html: "<span>html标签在渲染的时候被解析</span>", text: "<span>html标签在渲染的时候被源码输出</span>", } }); </script>
跳过这个元素和它的子元素的编译过程。
一些静态的内容不需要编译加这个指令可以加快渲染
<span v-pre>{{ this will not be compiled }}</span>
<!-- 显示的是{{ this will not be compiled }} -->
<span v-pre>{{msg}}</span>
<!-- 即使data里面定义了msg这里仍然是显示的{{msg}} -->
<script>
new Vue({
el: '#app',
data: {
msg: 'Hello Vue.js'
}
});
</script>
执行一次性的插值【当数据改变时,插值处的内容不会继续更新】
当数据发生变化的时候,视图也就发生变化
当视图发生变化的时候,数据也会跟着同步变化
v-model是一个指令,限制在 <input>、<select>、<textarea>、components
中使用
注意: 单选值为单个多选值为数组
用来绑定事件的
形式如:v-on:click 缩写为 @click;
<div id="app">
<div>{{num}}</div>
<div>
<button v-on:click='num++'>点击</button>
<button @click='num++'>点击1</button>
<button @click='handle'>点击2</button>
<button @click='handle()'>点击3</button>
</div>
</div>
<body> <div id="app"> <div>{{num}}</div> <div> <!-- 如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数 --> <button v-on:click='handle1'>点击1</button> <!-- 2、如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递, 并且事件对象的名称必须是$event --> <button v-on:click='handle2(123, 456, $event)'>点击2</button> </div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { num: 0 }, methods: { handle1: function(event) { console.log(event.target.innerHTML) }, handle2: function(p, p1, event) { console.log(p, p1) console.log(event.target.innerHTML) this.num++; } } }); </script>
一、通过event获取
console.log(event.target); // 当前元素点击的子节点
console.log(event.currentTarget); // 当前Vue元素
var pro = event.currentTarget; // 当前元素
pro.lastElementChild.style.color = "#DE3E3E"; // 修改最后一个子节点,改变图标和文字颜色
console.log(pro.getAttribute('name')); // 获取html元素属性值
二、如果click事件传参数,需要设置$event来获取。
<div class="bar_menu" v-on:click="showInfo(1,$event)" name="1"></div> <script type="text/javascript"> var bottom_bar = new Vue({ el: '#bottom_bar', data: { img_1: "images/bar_1_select" }, methods: { showInfo(s,event) { // console.log(event.target); // 当前元素 console.log(event.currentTarget); // vue元素 var pro = event.currentTarget; // 当前元素 pro.lastElementChild.style.color = "#DE3E3E"; event.currentTarget.style.backgroundColor = "rgba(25, 253, 240, 0.7)";//当前选中行高亮 } } }) </script>
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。
Vue 不推荐我们操作DOM 为了解决这个问题,Vue.js 为 v-on
提供了事件修饰符
修饰符是由点开头的指令后缀来表示的
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 即阻止冒泡也阻止默认事件 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!--阻止鼠标右键弹出信息-->
<div @contextmenu.prevent.capture</div>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
在做项目中有时会用到键盘事件,在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on
在监听键盘事件时添加按键修饰符
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` --> <input v-on:keyup.13="submit"> <!-- -当点击enter 时调用 `vm.submit()` --> <input v-on:keyup.enter="submit"> <!--当点击enter或者space时 时调用 `vm.alertMe()` --> <input type="text" v-on:keyup.enter.space="alertMe" > 常用的按键修饰符 .enter => enter键 .tab => tab键 .delete (捕获“删除”和“退格”按键) => 删除键 .esc => 取消键 .space => 空格键 .up => 上 .down => 下 .left => 左 .right => 右 <script> var vm = new Vue({ el:"#app", methods: { submit:function(){}, alertMe:function(){}, } }) </script>
在Vue中可以通过config.keyCodes
自定义按键修饰符别名
<div id="app"> 预先定义了keycode 116(即F5)的别名为f5,因此在文字输入框中按下F5,会触发prompt方法 <input type="text" v-on:keydown.f5="prompt()"> </div> <script> Vue.config.keyCodes.f5 = 116; let app = new Vue({ el: '#app', methods: { prompt: function() { alert('我是 F5!'); } } }); </script>
v-bind 指令被用来响应地更新 HTML 属性
v-bind:href 可以缩写为 :href;
<!-- 绑定一个属性 -->
<img v-bind:src="imageSrc">
<!-- 缩写 -->
<img :src="imageSrc">
我们可以给v-bind:class 一个对象,以动态地切换class。
注意:v-bind:class指令可以与普通的class特性共存
1、 v-bind 中支持绑定一个对象
如果绑定的是一个对象 则 键为 对应的类名 值 为对应data中的数据
<!-- HTML最终渲染为 <ul class="box textColor textSize"></ul> 注意: textColor,textSize 对应的渲染到页面上的CSS类名 isColor,isSize 对应vue data中的数据 如果为true 则对应的类名 渲染到页面上 当 isColor 和 isSize 变化时,class列表将相应的更新, 例如,将isSize改成false, class列表将变为 <ul class="box textColor"></ul> --> <ul class="box" v-bind:class="{textColor:isColor, textSize:isSize}"> <li>学习Vue</li> <li>学习Node</li> <li>学习React</li> </ul> <div v-bind:style="{color:activeColor,fontSize:activeSize}">对象语法</div> <sript> var vm= new Vue({ el:'.box', data:{ isColor:true, isSize:true, activeColor:"red", activeSize:"25px", } }) </sript> <style> .box{ border:1px dashed #f0f; } .textColor{ color:#f00; background-color:#eef; } .textSize{ font-size:30px; font-weight:bold; } </style>
2、 v-bind 中支持绑定一个数组 数组中classA和 classB 对应为data中的数据
这里的classA 对用data 中的 classA 这里的classB 对用data 中的 classB <ul class="box" :class="[classA, classB]"> <li>学习Vue</li> <li>学习Node</li> <li>学习React</li> </ul> <script> var vm= new Vue({ el:'.box', data:{ classA:‘textColor‘, classB:‘textSize‘ } }) </script> <style> .box{ border:1px dashed #f0f; } .textColor{ color:#f00; background-color:#eef; } .textSize{ font-size:30px; font-weight:bold; } </style>
1 <li v-on:click='change(index)' :class='currentIndex==index?"active":""' :key='item.id' v-for='(item,index) in list'>{{item.title}}</li> 2<div :class="{opacity_bhd: organCodes != 0}>是否显示样式</div> .opacity_bhd { opacity: 0.3; } data(){ return{ organCodes : '0' }} 3 <div :class="remoteDeviceClass?'zhlc_screen_navbody_ygsb':'display_flex align_items_center'"></div> 4 <div class="folderItem" :class="{'noFoldersClass':noFoldersClass(item)}"> noFoldersClass(item) { if (item.folders == null || item.folders.length == 0) { return true } return false }
**绑定对象和绑定数组 的区别 : **
绑定对象的时候 对象的属性 即要渲染的类名 对象的属性值对应的是 data 中的数据
绑定数组的时候数组里面存的是data 中的数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> .active { border: 1px solid red; width: 100px; height: 100px; } .error { background-color: orange; } .test { color: blue; } .base { font-size: 28px; } </style> </head> <body> <div id="app"> <div v-bind:class='[activeClass, errorClass, {test: isTest}]'>测试样式</div> <div v-bind:class='arrClasses'></div> <div v-bind:class='objClasses'></div> <div class="base" v-bind:class='objClasses'></div> <button v-on:click='handle'>切换</button> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 样式绑定相关语法细节: 1、对象绑定和数组绑定可以结合使用 2、class绑定的值可以简化操作 3、默认的class如何处理?默认的class会保留 */ var vm = new Vue({ el: '#app', data: { activeClass: 'active', errorClass: 'error', isTest: true, arrClasses: ['active','error'], objClasses: { active: true, error: true } }, methods: { handle: function(){ // this.isTest = false; this.objClasses.error = false; } } }); </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <div v-bind:style='{border: borderStyle, width: widthStyle, height: "10px"}'>55</div> <div v-bind:style='objStyles'></div> <div v-bind:style='[objStyles, overrideStyles]'></div> <button v-on:click='handle'>切换</button> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 样式绑定之内联样式Style: */ var vm = new Vue({ el: '#app', data: { borderStyle: '1px solid blue', widthStyle: '100px', heightStyle: '200px', objStyles: { border: '1px solid green', width: '200px', height: '100px' }, overrideStyles: { border: '5px solid orange', backgroundColor: 'blue' } }, methods: { handle: function(){ this.heightStyle = '100px'; this.objStyles.width = '100px'; } } }); </script> </body> </html>
注意:
在标签里直接写样式的,样式结构有-的要变成驼峰格式否则报错
<span class="weather_kqzs_box_blszrbhq" v-bind:style='{backgroundColor: weathers.color}'>{{weathers.airQuality}}</span>
先定义一个属性currentIndex:null
<tr v-for="(item,index) in projectDirectories" @click="flytopoint(item,index)" :class='currentIndex==index?"highlighted":""' >
<td>{{index+1}}</td>
<td style="cursor:pointer" >{{item.zrbhdmc}}</td>
<td>{{item.totalArea}}</td>
<td>{{item.levels}}</td>
</tr>
flytopoint(item,currentIndex) {
let self = this;
self.currentIndex=currentIndex;
},
.highlighted {
background-color:rgba(25, 253, 240, 0.7)!important;
}
1- 多个元素 通过条件判断展示或者隐藏某个元素。
**v-if v-else v-else-if **
2- 进行两个视图之间的切换
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <div v-if='100>=90'>优秀</div> <div v-else-if='score<90&&score>=80'>良好</div> <div v-else-if='score<80&&score>60'>一般</div> <div v-else>比较差</div> <div v-if='flag'>测试v-if</div> <div v-show='flag1'>测试v-show</div> <button v-on:click='handle'>点击</button> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 分支结构 v-show的原理:控制元素样式是否显示 display:none v-if的原理:控制元素样式是否显示 display:none */ var vm = new Vue({ el: '#app', data: { score: 10, flag: false, flag1: false }, methods: { handle: function(){ this.flag = !this.flag; } } }); </script> </body> </html>
v-show本质就是标签display设置为none,控制隐藏二者都不占位
v-show只编译一次,后面其实就是控制css,而v-if不停的销毁和创建,故v-show性能更好一点。
v-if是动态的向DOM树内添加或者删除DOM元素
v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件
v-if控制元素是否渲染到页面
v-show控制元素是否显示(已经渲染到了页面)
用于循环的数组里面的值可以是对象,也可以是普通元素
v-for遍历数组:
<li v-for='item in list'>{{item}}</li>
<li v-for='(item,index) in list'>{{item}} + '---' +{{index}}</li>
<li :key='item.id' v-for='(item,index) in list'>{{item}} + '---' {{index}}</li>
eg:
<div id="app"> <div>循环数组</div> <ul> <!--1、绑定key的作用 提高Vue的性能 2、 key 需要是唯一的标识 所以需要使用id, 也可以使用index , index 也是唯一的 3、 item 是 数组中对应的每一项 4、 index 是 每一项的 索引 --> <li v-for='item in fruits'>{{item}}</li> <li v-for='(item, index) in fruits'>{{item + '---' + index}}</li> <li v-if="item.id==2" :key='item.id' v-for='(item, index) in myFruits'> <span>{{item.ename}}</span> <span>-----</span> <span>{{item.cname}}</span> </li> </ul> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 循环结构-遍历数组 */ var vm = new Vue({ el: '#app', data: { fruits: ['apple', 'orange', 'banana'], myFruits: [{ id: 1, ename: 'apple', cname: '苹果' },{ id: 2, ename: 'orange', cname: '橘子' },{ id: 3, ename: 'banana', cname: '香蕉' }] } }); </script>
key的作用:key来给每个节点做一个唯一标识,帮助Vue区分不同的元素,从而提高性能
v-for遍历对象:
<div v-for='(value, key, index) in object'></div>
v-if和v-for结合使用:
<div v-if='value==12' v-for='(value, key, index) in object'></div>
<body> <div id="app"> <div v-if='v==13' v-for='(v,k,i) in obj'>{{v + '---' + k + '---' + i}}</div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> // 使用原生js遍历对象 var obj = { uname: 'lisi', age: 12, gender: 'male' } for(var key in obj) { console.log(key, obj[key]) } /* 循环结构 */ var vm = new Vue({ el: '#app', data: { obj: { uname: 'zhangsan', age: 13, gender: 'female' } } }); </script>
computed计算属性 watch监听器属性 filters过滤器 methods方法函数 都可以用与插值表达式和属性绑定
1、不需要绑定value 属性的,通过v-model 双向绑定 一个值
1、textarea 是 一个双标签 不需要绑定value 属性的,通过v-model 双向绑定 一个值
1、 需要给select 通过v-model 双向绑定 一个值
2、 每一个option 必须要有value属性 且value 值不能一样
3、 当某一个option选中的时候 v-model 会将当前的 value值 改变 data 中的 数据 occupation 的值就是选中的值,相反data初始化默认的值也就是其默认选中的值
4、如何 multiple 多选则绑定的值为数组
1、 两个单选框需要同时通过v-model 双向绑定 一个值
2、 每一个单选框必须要有value属性 且value 值不能一样
3、 当某一个单选框选中的时候 v-model 会将当前的 value值 改变 data 中的 数据 gender 的值就是选中的值,,相反data初始化默认的值也就是其默认选中的值
1、 复选框需要同时通过v-model 双向绑定 一个值且值为数组
2、 每一个复选框必须要有value属性 且value 值不能一样
3、 当某一个单选框选中的时候 v-model 会将当前的 value值 改变 data 中的 数据 hobby 的值就是选中的值,,相反data初始化默认的值也就是其默认选中的值
<div id="app"> <form action="http://itcast.cn"> <div> <span>单行文本框:</span> <span> <input type="text" v-model='uname'> </span> </div> <div> <span>单选框:</span> <span> <input type="radio" id="male" value="1" v-model='gender'> <label for="male">男</label> <input type="radio" id="female" value="2" v-model='gender'> <label for="female">女</label> </span> </div> <div> <span>多选框:</span> <input type="checkbox" id="ball" value="1" v-model='hobby'> <label for="ball">篮球</label> <input type="checkbox" id="sing" value="2" v-model='hobby'> <label for="sing">唱歌</label> <input type="checkbox" id="code" value="3" v-model='hobby'> <label for="code">写代码</label> </div> <div> <span>下拉框:</span> <select v-model='occupation' multiple> <option value="0">请选择职业...</option> <option value="1">教师</option> <option value="2">软件工程师</option> <option value="3">律师</option> </select> </div> <div> <span>多行文本:</span> <textarea v-model='desc'></textarea> </div> <div> <input type="submit" value="提交" @click.prevent='handle'> </div> </form> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 表单基本操作 */ var vm = new Vue({ el: '#app', data: { uname: 'lisi', gender: 2, hobby: ['2','3'], // occupation: 3 occupation: ['2','3'], desc: 'nihao' }, methods: { handle: function(){ console.log(this.uname) console.log(this.gender) console.log(this.hobby[0]) console.log(this.hobby.toString()) console.log(this.occupation) console.log(this.desc) }
==注意: 1多个数组用数组表示 2有多个数值的数组可以转为字符串.toString() 以逗号为分隔符 3如果数组为空转成字符串为空 ==
<input type="text" v-model.number='age'>
<input type="text" v-model.trim='info'>
<input type="text" v-model.lazy='msg'>
目的:
内置指令不满足需求
Vue允许我们自定义指令
<!-- 使用自定义的指令,只需在对用的元素中,加上'v-'的前缀形成类似于内部指令'v-if','v-text'的形式。 --> <input type="text" v-focus> <script> // 注意点: // 1、 在自定义指令中 如果以驼峰命名的方式定义 如 Vue.directive('focusA',function(){}) // 2、 在HTML中使用的时候 只能通过 v-focus-a 来使用 // 注册一个全局自定义指令 v-focus Vue.directive('focus', { // 当绑定元素插入到 DOM 中。 其中 el为dom元素 inserted: function (el) {//inserted被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 // 聚焦元素 el.focus(); } }); new Vue({ el:'#app' }); </script>
注意: el表示指令所绑定的元素
<input type="text" v-color='msg'> <script type="text/javascript"> /* 自定义指令-带参数 bind - 只调用一次,在指令第一次绑定到元素上时候调用 */ Vue.directive('color', { // bind声明周期, 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置 与inserted作用类似可替换使用 // el 为当前自定义指令的DOM元素 // binding 为自定义的函数形参 通过自定义属性传递过来的值 存在 binding.value 里面 bind: function(el, binding){ // 根据指令的参数设置背景色 // console.log(binding.value.color) el.style.backgroundColor = binding.value.color; } }); var vm = new Vue({ el: '#app', data: { msg: { color: 'blue' } } }); </script>
<input type="text" v-color='msg'> <input type="text" v-focus> <script type="text/javascript"> /* 自定义指令-局部指令 */ var vm = new Vue({ el: '#app', data: { msg: { color: 'red' } }, //局部指令,需要定义在 directives 的选项 directives: { color: { bind: function(el, binding){ el.style.backgroundColor = binding.value.color; } }, focus: { inserted: function(el) { el.focus(); } } } }); </script>
<div id="app"> <!-- 当多次调用 reverseString 的时候 只要里面的 num 值不改变 他会把第一次计算的结果直接返回 直到data 中的num值改变 计算属性才会重新发生计算 --> <div>{{reverseString}}</div> <div>{{reverseString}}</div> <!-- 调用methods中的方法的时候 他每次会重新调用,而且方法调用必须加() --> <div>{{reverseMessage()}}</div> <div>{{reverseMessage()}}</div> <!-- 计算属性与data属性无区别只不过计算属性需要return返回,methods中函数调用需要加() --> <input v-model="reverseString" type="text"> <input v-model="reverseMessage()" type="text"> </div> <script type="text/javascript"> /* 计算属性与方法的区别:计算属性是基于依赖进行缓存的,而方法不缓存 */ var vm = new Vue({ el: '#app', data: { msg: 'Nihao', num: 100 }, methods: { reverseMessage: function(){ console.log('methods') return this.msg.split('').reverse().join(''); } }, //computed 属性 定义 和 data 已经 methods 平级 computed: { // reverseString 这个是我们自己定义的名字 reverseString: function(){ console.log('computed') var total = 0; // 当data 中的 num 的值改变的时候 reverseString 会自动发生计算 for(var i=0;i<=this.num;i++){ total += i; } // 这里一定要有return 否则 调用 reverseString 的 时候无法拿到结果 return total; } } }); </script>
注意:
1. computed里面的属性可以看做是data中的属性,在页面展示上没有什么区别
2. 函数一定要有return返回值否则不起作用
3. 方法和计算属性的区别就是方法每次调用都重新计算,而计算属性是基于它们的依赖data中属性值得改变进行缓存的,属性值不变的情况下无论调用多少次查的都是缓存提升效率
4. 计算属性中包含有data中属性值的复杂运算,随着data属性中的改变才会改变
5. 计算属性与data属性无区别只不过计算属性需要return返回,methods中函数调用需要加()
6. computed属性 定义 和 data 已经 methods 平级
7. 计算属性不能传参数只是一个属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <div> <span>用户名:</span> <span> <input type="text" v-model.lazy='uname'> </span> <span>{{tip}}</span> </div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 侦听器 1、采用侦听器监听用户名的变化 2、调用后台接口进行验证 3、根据验证的结果调整提示信息 */ var vm = new Vue({ el: '#app', data: { uname: '', tip: '' }, methods: { checkName: function(uname) { // 调用接口,但是可以使用定时任务的方式模拟接口调用 var that = this; setTimeout(function(){ // 模拟接口调用 if(uname == 'admin') { that.tip = '用户名已经存在,请更换一个'; }else{ that.tip = '用户名可以使用'; } }, 2000); } }, watch: { uname: function(newVal, oldVal){ // 调用后台接口验证用户名的合法性 this.checkName(newVal); // 修改提示信息 this.tip = '正在验证...'; } } }); </script> </body> </html>
var vm=new Vue({ data:{ a:1, b:{ c:1 } }, watch:{ a(val, oldVal){//普通的watch监听 console.log("a: "+val, oldVal); }, b:{//深度监听,可监听到对象、数组的变化 handler(val, oldVal){ console.log("b.c: "+val.c, oldVal.c);//但是这两个值打印出来却都是一样的 }, deep:true } } }) vm.a=2 vm.b.c=2
a是一个普通的值,当a的值变化时会被监听到,b是一个对象,不能直接像a那么写,需要深度监听才能捕捉到,但是当我想去捕捉b对象中某一个值的变化时却发现,打印出来的两个值是不一样的,如图:
这样就只能知道对象发生变化却不知道具体哪个值发生了变化,如果想监听对象某一个值得变化可以利用计算属性computed
var vm=new Vue({ data:{ b:{ c:1 } }, watch:{ newValue(val, oldVal){ console.log("b.c: "+val, oldVal); } }, computed: { newValue() { return this.b.c } } })
用watch去监听computed计算过的值就可以直接知道是哪个对应的值发生了变化,结果如图:
data
,而只是改变渲染的结果,并返回过滤后的版本Vue.filter(‘过滤器名称’, function(value){
// 过滤器业务逻辑
})
filters:{
capitalize: function(){}
}
<div>{{msg | upper}}</div>
<div>{{msg | upper | lower}}</div>
<div v-bind:id=“id | formatId"></div>
Vue.filter(‘format’, function(value, arg1){
// value就是过滤器传递过来的参数
})
<div>{{date | format(‘yyyy-MM-dd')}}</div>
<div id="app"> <input type="text" v-model='msg'> <!-- upper 被定义为接收单个参数的过滤器函数,表达式 msg 的值将作为参数传入到函数中 --> <div>{{msg | upper}}</div> <!-- 支持级联操作 upper 被定义为接收单个参数的过滤器函数,表达式msg 的值将作为参数传入到函数中。 然后继续调用同样被定义为接收单个参数的过滤器 lower ,将upper 的结果传递到lower中 --> <div>{{msg | upper | lower}}</div> <div :abc='msg | upper'>测试数据</div> </div> <script type="text/javascript"> // lower 为全局过滤器 Vue.filter('lower', function(val) { return val.charAt(0).toLowerCase() + val.slice(1); }); var vm = new Vue({ el: '#app', data: { msg: '' }, //filters 属性 定义 和 data 已经 methods 平级 // 定义filters 中的过滤器为局部过滤器 filters: { // upper 自定义的过滤器名字 // upper 被定义为接收单个参数的过滤器函数,表达式 msg 的值将作为参数传入到函数中 upper: function(val) { // 过滤器中一定要有返回值 这样外界使用过滤器的时候才能拿到结果 return val.charAt(0).toUpperCase() + val.slice(1); } } }); </script>
常用:
日期格式化
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <div>{{msg | formatTime('yyyy/MM/dd')}}</div> <div>{{formatTimes(msg,'yyyy/MM/dd')}}</div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 计算属性 */ var vm = new Vue({ el: '#app', data: { msg: '2021-09-12 05:23:34' }, computed: { reverseString: function(date, fmt){ var date = new Date(date); if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); } var o = { 'M+': date.getMonth() + 1, 'd+': date.getDate(), 'h+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds() }; for (var k in o) { if (new RegExp('('+k+')').test(fmt)) { var str = o[k] + ''; fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : ('00' + str).substr(str.length)); } } return fmt; } }, methods: { formatTimes: function(date, fmt){ var date = new Date(date); if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); } var o = { 'M+': date.getMonth() + 1, 'd+': date.getDate(), 'h+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds() }; for (var k in o) { if (new RegExp('('+k+')').test(fmt)) { var str = o[k] + ''; fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : ('00' + str).substr(str.length)); } } return fmt; } }, filters: { formatTime: function (date, fmt) { var date = new Date(date); if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); } var o = { 'M+': date.getMonth() + 1, 'd+': date.getDate(), 'h+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds() }; for (var k in o) { if (new RegExp('('+k+')').test(fmt)) { var str = o[k] + ''; fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : ('00' + str).substr(str.length)); } } return fmt; } } }); </script> </body> </html>
① beforeCreate
② created
③ beforeMount
④ mounted
① beforeUpdate
② updated
① beforeDestroy
② destroyed
beforeCreate | 在实例初始化之后,数据观测和事件配置之前被调用 此时data 和 methods 以及页面的DOM结构都没有初始化 什么都做不了 |
---|---|
created | 在实例创建完成后被立即调用此时data 和 methods已经可以使用 但是页面还没有渲染出来 |
beforeMount | 在挂载开始之前被调用 此时页面上还看不到真实数据 只是一个模板页面而已 |
mounted | el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。 数据已经真实渲染到页面上 在这个钩子函数里面我们可以使用一些第三方的插件 |
beforeUpdate | 数据更新时调用,发生在虚拟DOM打补丁之前。 页面上数据还是旧的 |
updated | 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。 页面上数据已经替换成最新的 |
beforeDestroy | 实例销毁之前调用 |
destroyed | 实例销毁后调用 |
① beforeCreate 在实例初始化之后,数据观测和事件配置之前被调用。
② created 在实例创建完成后被立即调用。
③ beforeMount 在挂载开始之前被调用。
④ mounted el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。
⑤ beforeUpdate 数据更新时调用,发生在虚拟DOM打补丁之前。
⑥ updated 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
⑦ beforeDestroy 实例销毁之前调用。
⑧ destroyed 实例销毁后调用。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <div>{{msg}}</div> <button @click='update'>更新</button> <button @click='destroy'>销毁</button> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* Vue实例的生命周期 */ var vm = new Vue({ el: '#app', data: { msg: '生命周期' }, methods: { update: function(){ this.msg = 'hello'; }, destroy: function(){ this.$destroy(); } }, beforeCreate: function(){ console.log('beforeCreate'); }, created: function(){ console.log('created'); }, beforeMount: function(){ console.log('beforeMount'); }, mounted: function(){ console.log('mounted'); }, beforeUpdate: function(){ console.log('beforeUpdate'); }, updated: function(){ console.log('updated'); }, beforeDestroy: function(){ console.log('beforeDestroy'); }, destroyed: function(){ console.log('destroyed'); } }); </script> </body> </html>
push()
| 往数组最后面添加一个元素,成功返回当前数组的长度 |pop()
| 删除数组的最后一个元素,成功返回删除元素的值 |shift()
| 删除数组的第一个元素,成功返回删除元素的值 |unshift()
| 往数组最前面添加一个元素,成功返回当前数组的长度 |splice()
| 有三个参数,第一个是想要删除的元素的下标(必选),第二个是想要删除的个数(必选),第三个是删除 后想要在原位置替换的值 |sort()
| sort() 使数组按照字符编码默认从小到大排序,成功返回排序后的数组 |reverse()
| reverse() 将数组倒序,成功返回倒序后的数组 |<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <ul> <li v-for='item in list'>{{item}}</li> </ul> <div> <div>{{info.name}}</div> <div>{{info.age}}</div> <div>{{msg}}</div> <input type="button" @click="ddd"> </div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 动态处理响应式数据 */ var vm = new Vue({ el: '#app', data: { list: ['apple', 'orange', 'banana'], info: { name: 'lisi', age: 12 }, msg: "23" }, methods: { //在方法里改变对象数据是没问题的,页面数据能正常显示 ddd: function (){ vm.list[1] = 'lemon'; vm.msg = 'sdfafds'; } } }); vm.msg = 'sdfafds';//修改属性页面内容发生改变 vm.list[1] = 'lemon';//直接修改对象属性值,虽然对象属性值改变了但是页面内容不会发生变化 Vue.set(vm.list,0, 'lemon');//全局方式动态响应数据 vm.$set(vm.list, 1, 'lemon');//局部方式动态响应数据 vm.info.gender = 'we'; vm.$set(vm.info, 'name', 'female'); </script> </body> </html>
Vue.component(组件名称, {
data: 组件数据,
template: 组件模板内容
})
// 注册一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">点击了{{ count }}次.</button>'
})
全局组件注册后,任何vue实例都可以用
<div id="app"> <!-- 4、 组件可以重复使用多次 因为data中返回的是一个对象所以每个组件中的数据是私有的 即每个实例可以维护一份被返回对象的独立的拷贝 --> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> <!-- 8、必须使用短横线的方式使用组件 --> <hello-world></hello-world> </div> <script type="text/javascript"> //5 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件, // 7、但是在普通的标签模板中,必须使用短横线的方式使用组件 //全局注册组件 Vue.component('HelloWorld', { data: function(){ return { msg: 'HelloWorld' } }, template: '<div>{{msg}}</div>' }); Vue.component('button-counter', { // 1、组件参数的data值必须是函数 // 同时这个函数要求返回一个对象 data: function(){ return { count: 0 } }, // 2、组件模板必须是单个根元素 // 3、组件模板的内容可以是模板字符串 template: ` <div> <button @click="handle">点击了{{count}}次</button> <button>测试123</button> # 6 在字符串模板中可以使用驼峰的方式使用组件 <HelloWorld></HelloWorld> </div> `, methods: { handle: function(){ this.count += 2; } } }) var vm = new Vue({ el: '#app', data: { } }); </script>
<div id="app"> <my-component></my-component> </div> <script> // 定义组件的模板 var Child = { template: '<div>A custom component!</div>' } new Vue({ //局部注册组件 components: { // <my-component> 将只在父模板可用 一定要在实例上注册了才能在html文件中使用 'my-component': Child } }) </script>
组件注意事项
⚫ 短横线方式
Vue.component('my-component', { /* ... */ })
⚫ 驼峰方式
Vue.component('MyComponent', { /* ... */ })
组件命名注意事项
<body> <!--普通标签模板--> <div id="app"> <button-counter></button-counter> <hello-world></hello-world> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 组件注册注意事项 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是 在普通的标签模板中,必须使用短横线的方式使用组件 */ Vue.component('HelloWorld', { data: function(){ return { msg: 'HelloWorld' } }, template: '<div>{{msg}}</div>' }); Vue.component('button-counter', { data: function(){ return { count: 0 } }, <!--字符串模板--> template: ` <div> <button @click="handle">点击了{{count}}次</button> <button>测试123</button> <HelloWorld></HelloWorld> </div> `, methods: { handle: function(){ this.count += 2; } } }) var vm = new Vue({ el: '#app', data: { } }); </script> </body>
用于子组件接收消息传递的
⚫ 字符串 String
⚫ 数值 Number
⚫ 布尔值 Boolean
⚫ 数组 Array
⚫ 对象 Object
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
简单写法:
props: {
fieldString: String,
fieldNumber: Number,
fieldBoolean: Boolean,
fieldArray: Array,
fieldObject: Object,
fieldFunction: Function
}
带有默认值写法:
props: { fieldString: { type: String, default: '' }, fieldNumber: { type: Number, default: 0 }, fieldBoolean: { type: Boolean, default: true }, fieldArray: { type: Array, default: () => [] }, fieldObject: { type: Object, default: () => ({}) }, fieldFunction: { type: Function, default: function () { } } }
我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
Vue.component('my-component', { props: { // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) propA: Number, // 多个可能的类型 propB: [String, Number], // 必填的字符串 propC: { type: String, required: true }, // 带有默认值的数字 propD: { type: Number, default: 100 }, // 带有默认值的对象 propE: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } } })
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在 default 或 validator 函数中是不可用的。
<div id="app"> <!--父组件发送的形式是以title属性的形式绑定值到子组件menu-item身上--> <!--绑定属性使用v-bind或者简化形式: title有效的 content无效--> <menu-item :title='ptitle' content='hello'></menu-item> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 父组件向子组件传值-基本使用 */ Vue.component('menu-item', { props: ['title', 'content'],<!--title属性能接收到数据 content接收不到数据--> data: function() { return { msg: '子组件本身的数据' } }, template: '<div>{{msg + "----" + title + "-----" + content}}</div>' }); var vm = new Vue({ el: '#app', data: { ptitle: '动态绑定属性' } }); </script> </body>
$emit()
触发事件$emit()
第一个参数为 自定义的事件名称 第二个参数为需要传递的数据参数可以无限个<body> <div id="app"> <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div> <!--@enlarge-text表示父组件与子组件绑定的方法,子组件调用该方法时,父组件的handle方法自动调用--> <menu-item :parr='parr' @enlarge-text='handle'></menu-item> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 子组件向父组件传值-基本用法 props传递数据原则:单向数据流 */ Vue.component('menu-item', { props: ['parr'], template: ` <div> <ul> <li :key='index' v-for='(item,index) in parr'>{{item}}</li> </ul> <button @click='$emit("enlarge-text",1)'>扩大父组件中字体大小</button> <button @click='sonFunction()'>扩大父组件中字体大小</button> </div> `, methods: { sonFunction : function (){ this.$emit("enlarge-text",2);//js中需要加this指向 } } }); var vm = new Vue({ el: '#app', data: { pmsg: '父组件中内容', parr: ['apple','orange','banana'], fontSize: 10 }, methods: { handle: function(str){ // 扩大字体大小 this.fontSize += str; } } }); </script> </body>
<template> <div id="app"> <div ref="testDom">11111</div> <button @click="getTest">获取test节点</button> </div> </template> <script> export default { methods: { getTest() { console.log(this.$refs.testDom) } } }; </script>
其实ref除了可以获取本页面的dom元素,还可以拿到子组件中的data和去调用子组件中的方法
子组件
<template>
<div>
{{ msg }}
</div>
</template>
<script>
export default {
data() {
return {
msg: "hello world"
}
}
}
</script>
父组件
<template> <div id="app"> <HelloWorld ref="hello"/> <button @click="getHello">获取helloworld组件中的值</button> </div> </template> <script> import HelloWorld from "./components/HelloWorld.vue"; export default { components: { HelloWorld }, data() { return {} }, methods: { getHello() { console.log(this.$refs.hello.msg) } } }; </script>
子组件
<template>
<div>
</div>
</template>
<script>
export default {
methods: {
open() {
console.log("调用到了")
}
}
}
</script>
父组件
<template> <div id="app"> <HelloWorld ref="hello"/> <button @click="getHello">获取helloworld组件中的值</button> </div> </template> <script> import HelloWorld from "./components/HelloWorld.vue"; export default { components: { HelloWorld }, data() { return {} }, methods: { getHello() { this.$refs.hello.open(); } } }; </script>
<body> <div id="app"> <div>父组件</div> <div> <button @click='handle'>销毁事件</button> </div> <test-tom></test-tom> <test-jerry></test-jerry> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> /* 兄弟组件之间数据传递 */ // 提供事件中心 var hub = new Vue(); Vue.component('test-tom', { data: function(){ return { num: 0 } }, template: ` <div> <div>TOM:{{num}}</div> <div> <button @click='handle'>点击</button> </div> </div> `, methods: { handle: function(){ hub.$emit('jerry-event', 2); } }, mounted: function() { // 监听事件 hub.$on('tom-event', (val) => { this.num += val; }); } }); Vue.component('test-jerry', { data: function(){ return { num: 0 } }, template: ` <div> <div>JERRY:{{num}}</div> <div> <button @click='handle'>点击</button> </div> </div> `, methods: { handle: function(){ // 触发兄弟组件的事件 hub.$emit('tom-event', 1); } }, mounted: function() { // 监听事件 hub.$on('jerry-event', (val) => { this.num += val; }); } }); var vm = new Vue({ el: '#app', data: { }, methods: { handle: function(){ hub.$off('tom-event'); hub.$off('jerry-event'); } } }); </script> </body>
this.$router.push传参
1.params
this.$router.push()方法中path不能与params同用,否则param会失效,所以用name来指定页面,params来传递数据内容
<template>
<img src="../../static/images/indexTermianal/teacher.png" alt="" @click ="go()" >
</template>
<script>
methods:{
go:function(){
this.$router.push({
name:'indext',//在路由中定义好的
params:{
userId:1
}
})
}
}
</script>
在目标页面通过this.$route.params获取参数:
<p>{{this.$route.params.userId}}</p>
2.query
页面通过path和query传递参数,该实例中row为某行表格数据
methods:{
go:function(){
this.$router.push({
path:'/indext',
query:{
userId:1
}
})
}
},
目标页面this.$route.query.userId获取数据
<p>{{this.$route.query.userId}}</p>
一个页面走方法直接跳转全路径
getToDealWith(){
window.location.href = webDataService + '/WoodManage?dealStatus=0'
},
var tem = MLibVue.getQueryString('dealStatus');
js中写一个获取url参数的方法
MLibVue.getQueryString = function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
}
有的时候我们需要预览详情单独打开一个新页面展示,这个时候就需要我们获取跳转的url以及查询数据,然后window.open(url,type)跳转
let href = this.$router.resolve({});返回一个完整的路径
发起组件
detail(record) { var self = this; let {href} = self.$router.resolve({ path: '/newsDetailVue', query: { title: record.newsName, author: record.source, releaseDate: self.encode(record.releaseTime), content: self.encode(record.content), } }); window.open(href, '_blank'); //window.open([URL], [窗口名称], [参数字符串]) }, encode(str) { let encode = encodeURI(str); let base64 = btoa(encode); return base64; },
跳转的组件
export default { name: "newsDetailVue", data(){ return { infoData:{}, title:null, author:null, releaseDate:null, content:null, groupId:null, attachments:[], noticeInfoId:null, flag:null, fontType:1,//0大1中2小 } }, async mounted(){ let self = this; if(self.$route.query.title.trim()){ self.title = decodeURI(self.$route.query.title.trim()); } if(self.$route.query.author.trim()){ self.author = decodeURI(self.$route.query.author.trim()); } if(self.$route.query.releaseDate.trim()){ self.releaseDate = self.decode(self.$route.query.releaseDate.trim()); } if(self.$route.query.content.trim()){ self.content = self.decode(self.$route.query.content.trim()); } }, methods:{ decode(base64){ let decode = atob(base64); let str = decodeURI(decode); return str; },
index.js 路由
{
path: '/newsDetailVue',
name: 'newsDetailVue',
component(){
return import("@/page/news/newsDetailVue")
},
meta: {
showMap: false, //需要显示地图
showMenu: false, //需要显示顶部菜单栏
showLeftMenu: false, //需要显示顶部菜单栏
hideAll: true//隐藏包括地图在内的所有东西(单独为新闻管理预览展示全屏内容写的)
}
},
语法: window.open([URL], [窗口名称], [参数字符串]) 参数说明: URL:可选参数,在窗口中要显示网页的网址或路径。如果省略这个参数,或者它的值是空字符串,那么窗口就不显示任何文档。 窗口名称:可选参数,被打开窗口的名称。 1.该名称由字母、数字和下划线字符组成。 2."_top"、"_blank"、"_selft"具有特殊意义的名称。 _blank:在新窗口显示目标网页 _self:在当前窗口显示目标网页 _top:框架网页中在上部窗口中显示目标网页 3.相同 name 的窗口只能创建一个,要想创建多个窗口则 name 不能相同。 4.name 不能包含有空格。 参数字符串:可选参数,设置窗口参数,各参数用逗号隔开。
通过props属性,父组件只能向子组件传递属性、方法而插槽还可以传递带标签的内容、甚至是组件:
<div class="child">
<h1>子组件</h1>
<slot name="head">头部默认值</slot>
<slot name="body">主体默认值</slot>
<slot>这是个匿名插槽(没有name属性),这串字符是匿名插槽的默认值。</slot>
</div>
<div class="parent">
<h1>父组件</h1>
<child>
<p slot="body">我是主体</p>
<p>我是其他内容</p>
<p slot="footer">我是尾巴</p>
</child>
</div>
运行结果 :
父组件
子组件
头部默认值 (具名插槽的默认值被渲染,因为父组件没有为此插槽提供内容)
我是主体 (具名插槽的默认值被覆盖)
我是其他内容 (匿名插槽的默认值被覆盖)
注意:1、
我是尾巴
插槽被丢弃了,因为子组件中没有的插槽与之匹配。我是其他内容
也会被丢弃。除了上面那个例子中,把slot直接用在普通标签或者上,更推荐在 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供插槽名称,这样就可以定义插槽的内容了:
<base-layout>
<template v-slot:header>
<h1>我是头header</h1>
</template>
<p>我是main的内容111</p>
<p>我也是main的内容222</p>
<template v-slot:footer>
<p>我是footer</p>
</template>
</base-layout>
1、带有 v-slot 的 元素中的所有内容都将会被传入相应的插槽。
2、任何没有被包裹在带有 v-slot 的 中的内容都会被视为默认插槽的内容。
如果你希望更明确一些,可以在一个 中包裹默认插槽的内容:
<base-layout>
<template v-slot:header>
<h1>我是头header</h1>
</template>
<template v-slot:default>
<p>我是main的内容111</p>
<p>我也是main的内容222</p>
</template>
<template v-slot:footer>
<p>我是footer</p>
</template>
</base-layout>
以上两种写法的渲染效果是一样的:
注意: v-slot 只能添加在 上。 (只有一种例外情况),请继续往下看。
上面props的例子,可以看到 父组件传给子组件了一个属性和一个方法,子组件可以使用 props 中的属性和方法。那对于插槽来说,父组件想访问子组件的数据,又该怎么做呢?
为了让 childUser 在父级的插槽内容中可用,需要把 childUser 从 子级作用域传递到 父级作用域。
做法就是将 childUser 作为 元素的一个属性绑定上去:
<!-- <Child> 组件: --> <template> <div> <h1>hey,我是组件Child的标题</h1> <slot v-bind:childData="childUser"></slot> </div> </template> <script> export default { data() { return { childUser: { Name:"Tom", Age:23 } } } </script>
绑定在 元素上的属性childData 被称为插槽 prop。
现在,在父级作用域中,我们可以使用带值的 v-slot 来定义 插槽 prop 的名字:
<!-- 这是父组件<Father>-->
<div>
<h1>hey,我是父组件Father的标题</h1>
<Child>
<template v-slot:default="slotProps">
{{ slotProps.childData.Name}}
{{ slotProps.childData.Age}}
</template>
</Child>
</div>
slot 和 slot-scope 已经被废弃,所有的 2.x 版本中 slot 和 slot-scope 属性仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。
所以更推荐使用vue2.6.0中的 v-slot。
(1)v-slot 的使用
1、在一个 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称。
2、只有下面这一种情况:当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。 这样我们就可以把 v-slot 直接用在组件标签上。除此之外,v-slot 必须用在 元素上。
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
(2)slot & slot-scope的使用
<template> <div> <h1>这里是子组件</h1> <slot name="mySlot" v-bind:user="childUser"> </slot> </div> </template> <script> export default { name: 'Son', data() { return { childUser: { Name: "Tom", Age: 23 } } } } </script>
<div>
<h1>hey,我是父组件Father的标题</h1>
<Son>
<div slot="mySlot" slot-scope="data">
{{ data.user.Name }}
{{ data.user.Age }}
</div>
</Son>
</div>
1、 slot=“default” 可以省略不写,slot可以用在 元素,也可以用在任意的普通元素上。
2、这里的 slot-scope 声明了被接收的 prop 对象会作为 slotProps 变量存在于 作用域中。你可以像命名 JavaScript函数参数一样随意命名 slotProps。同样的,slot-scope可以用在 template元素,也可以用在任意的普通元素上。
本身不是异步的所以常与promise结合使用
import VueResource from "vue-resource"
Vue.use(VueResource);
Vue.http.options.emulateHTTP = true;
Vue.http.options.emulateJSON = true;
1)get方式
Vue.http.get('url',{
param1:value1,
param2:value2
}).then(function(response){
// response.data中获取ResponseData实体
},function(response){
// 发生错误
});
2)post方式
Vue.http.post('url',{
param1:value1,
param2:value2
},{
emulateJSON:true
}).then(function(response){
// response.data中获取ResponseData实体
},function(response){
// 发生错误
});
<script src="static/js/jquery-2.1.3.min.js"></script>
var formData = new FormData();//格式化表单数据
$.ajax({
url: url,
type: "post",
data: formData,
// ajax传文件 一定要指定两个关键性的参数
contentType: false, // 不用任何编码 因为formdata对象自带编码 django能够识别该对象
processData: false, // 告诉浏览器不要处理我的数据 直接发就行
ContentType: 'multipart/form-data', // 此接口后台需要form表单格式,故加此header
success(result) {
}, error(err) {
}
})
注意:axios默认是异步请求数据的,如果需要同步获取数据的话,需要进行async 及await 设置。
引入: axios
npm install --save axios
import axios from 'axios'
1)get方式 axios.get('url', { params: { param1: value1, param2:value2 } }).then(function (response) { // response.data中获取ResponseData实体 }).catch(function (error) { // 发生错误 }); 2)post方式:默认json格式 axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }).then(function (response) { // response.data中获取ResponseData实体 }).catch(function (error) { // 发生错误 });
1)post方式 //传统方式拼接 fetch(url,{ // get 请求可以省略不写 默认的是GET method: 'get' //默认可以不写 } ).then(result => { self.departmentList = result.data ? result.data : []; console.log('self.getDepartment2=====', self.departmentList); }, function (error) { //其他http错误 console.log("error=======", error); //self.$router.push('/'); }); 2)post方式 fetch(url, { method: 'post', body: formData, headers: { 'Content-Type': 'application/json' } }).then(result => { self.departmentList = result.data ? result.data : []; console.log('self.getDepartment2=====', self.departmentList); }, function (error) { //其他http错误 console.log("error=======", error); //self.$router.push('/'); });
<script type="text/javascript">
/*1. Promise基本使用 我们使用new来构建一个Promise Promise的构造函数接收一个参数,是函数,并且传入两个参数: resolve,reject, 分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数 */
var p = new Promise(function(resolve, reject){ //2. 这里用于实现异步任务
setTimeout setTimeout(function(){ var flag = false; if(flag) { //3. 正常情况
resolve('hello'); }else{ //4. 异常情况
reject('出错了'); } }, 100); }); // 5 Promise实例生成以后,可以用then方法指定resolved状态和reject状态的回调函数
// 在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了
p.then(function(data){ console.log(data) },function(info){ console.log(info) });
</script>
这几种请求方式在项目中的应用:
第一步:在src下创建文件夹api添加两个js文件名字随意,其中api.js是整个组件公共调用方法,request.js是一些最基础的调用请求,给api.js使用的
main.js
import Vue from 'vue' import App from './App' import router from './router' import Antd from 'ant-design-vue' import 'ant-design-vue/dist/antd.css' import VueResource from "vue-resource"//http调用包 Vue.use(VueResource); Vue.use(Antd) Vue.http.options.emulateHTTP = true; Vue.http.options.emulateJSON = true; Vue.config.productionTip = false Vue.directive('focus', { // 当绑定元素插入到 DOM 中。 其中 el为dom元素 inserted: function (el) { // 聚焦元素 el.focus(); } }); /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
公共的request.js方法
import axios from 'axios' import Vue from "vue"; //第一种方式导出service 引用的时候自己写方法类型 export const service = axios.create({ //请求头数据 baseURL:'http://localhost:8042/BlsEcologyMonitoring', timeout: 5000 }); //第二种展现形式 把常用的get 和post写好 直接引用时传路径和参数就行 //axios get export function getAction(url,parameter) { return service({ url: url, method: 'get', params: parameter }) } //axios post export function postAction(url,parameter) { return service({ url: url, method: 'post', data: parameter }) } // promise+http export let request = { app: null, request: function (params, url,method) { // getUserFromCheckService() if (params == null) { params = {} } params = {} let self = this; let apiUrl = 'http://localhost:8042/BlsEcologyMonitoring'+ url; let promise = new Promise(function (resolve, reject) { Vue.http.post(apiUrl, params).then((success) => { if (success.data) { return resolve(success.data); } else { return reject(success.data); } }, function (error) { //其他http错误 return reject({status: -1, msg: "服务器返回异常,请联系管理员!"}); }) }) return promise; }, }
api.js 公共方法
import {service, getAction, postAction,request} from './request' import Vue from "vue" // 获取验证码 function captchaImage(params) { return service({ url: '/zh/styz/getDeviceLists', method: 'get', params }) } const addRole = (params)=>postAction("/zh/styz/getDeviceLists",params); const editRole = (params)=>getAction("/zh/styz/getDeviceLists1",params); function getAnimalVideoList(params) { return request.request(params, "/zh/styz/getDeviceLists") } export { captchaImage, editRole, addRole, getAnimalVideoList }
test1组件中调用:
<template> <div class="zrbhd_dataqhdb_modail" style="position: fixed"> </div> </template> <script> import top from "@/components/Top"; import Child from "@/components/Child"; import {captchaImage, editRole, getAnimalVideoList} from "@/api/api"; import axios from 'axios' export default { name: 'Test', components: { top, Child }, data() { return { msg: '23', } }, mounted() { this.getDepartment()//axios调用 this.getDepartment1()//axios调用 this.getDepartment2()//promise+http this.getDepartment3()//ajax this.getDepartment4()//fetch }, methods: { //axios第一种展现形式(常用) getDepartment() {//axios第一种 var self = this; captchaImage().then(result => { self.departmentList = result.data.data ? result.data.data : []; console.log('self.departmentList=====', self.departmentList); }, function (error) { //其他http错误 self.$router.push('/');//跳转首页 }) }, //axios第二种展示形式(常用) getDepartment1() { var self = this; editRole({}).then(result => { self.departmentList = result.data.data ? result.data.data : []; console.log('self.departmentList=====', self.departmentList); }, function (error) { //其他http错误 console.log('asdfsaassa', error); //self.$router.push('/'); }) }, //promise+http 需要引入vue-resource包 少用 getDepartment2() { var self = this; getAnimalVideoList({}).then(result => { self.departmentList = result.data.data ? result.data.data : []; console.log('self.getDepartment2=====', self.departmentList); }, function (error) { //其他http错误 console.log("error=======", error); //self.$router.push('/'); }) }, //index.html引入jquery调用ajax 少用 getDepartment3() { var self = this; var formData = new FormData();//格式化表单数据 var url = 'http://localhost:8042/BlsEcologyMonitoring/zh/styz/getDeviceLists' $.ajax({ url: url, type: "post", data: formData, // ajax传文件 一定要指定两个关键性的参数 contentType: false, // 不用任何编码 因为formdata对象自带编码 django能够识别该对象 processData: false, // 告诉浏览器不要处理我的数据 直接发就行 ContentType: 'multipart/form-data', // 此接口后台需要form表单格式,故加此header success(result) { self.departmentList = result.data ? result.data : []; //console.log('self.departmentList=====',self.departmentList); }, error(as) { console.log("as====", as); } }) }, //fetch代替ajax请求方式vue中ajax的替代品 getDepartment4() { var self = this; var formData = new FormData();//格式化表单数据 formData.append("dd","asdfasd"); var url = 'http://localhost:8042/BlsEcologyMonitoring/zh/styz/getDeviceLists' fetch(url, { method: 'post', body: formData, headers: { 'Content-Type': 'application/json' } }).then(result => { self.departmentList = result.data ? result.data : []; console.log('self.getDepartment2=====', self.departmentList); }, function (error) { //其他http错误 console.log("error=======", error); //self.$router.push('/'); }); }, } } </script> <style scoped> </style>
注意:涉及到的跨域问题我用的是后端代码解决跨域,axios还可以添加拦截器做公共拦截处理
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 跨域问题 * * @author mousejoo */ @Configuration public class CorsConfig { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); // 1 允许任何域名使用 corsConfiguration.addAllowedHeader("*"); // 2 允许任何头 corsConfiguration.addAllowedMethod("*"); // 3 允许任何方法(post、get等) return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); // 4 return new CorsFilter(source); } }
axios是通过promise实现对ajax技术的一种封装,就像jQuery实现ajax封装一样。
简单来说: ajax技术实现了网页的局部数据刷新,axios实现了对ajax的封装。
axios是ajax ajax不止axios。
ajax:
本身是针对MVC的编程,不符合现在前端MVVM的浪潮
基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案
JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务
axios:
从 node.js 创建 http 请求
支持 Promise API
客户端支持防止CSRF
提供了一些并发请求的接口
使用ajax,不一定要引入jquery,ajax只是将XMLHttpRequest对象还有该对象api组合起来的一个方法,可以自己创建XMLHttpRequest对象来发送请求。
# 配置公共的请求头
axios.defaults.baseURL = 'https://api.example.com';
# 配置 超时时间
axios.defaults.timeout = 2500;
# 配置公共的请求头
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
# 配置公共的 post 的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
同步:
async getfiles() {
var result={};
var param = new URLSearchParams()
param.append('id', $("#type").val())
var url = "/backstage/fileupload/getFileName.action";
await axios.post(url, param).then(function (res) {
if (res.status == 200) {
result=res.data.data;
} else {
alert("获取失败!");
}
});
this.filenames=result
}
请求拦截器
1请求拦截器的作用是在请求发送前进行一些操作
2例如在每个请求体里加上token,统一做了处理如果以后要改也非常容易
响应拦截器
1响应拦截器的作用是在接收到响应后进行一些操作
2例如在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页
请求案例:
// request interceptor service.interceptors.request.use(config => { const token = Vue.ls.get(ACCESS_TOKEN) if (token) { config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改 } //update-begin-author:taoyan date:2020707 for:多租户 // debugger; let tenantid = Vue.ls.get(TENANT_ID) // let tenantid = '' // if (user) { // tenantid = user.channelId; // } // config.headers[ 'channelId' ] = '402882db7494baab017494e2614e0002' //update-end-author:taoyan date:2020707 for:多租户 if(config.method=='get'){ if(config.url.indexOf("sys/dict/getDictItems")<0){ config.params = { _t: Date.parse(new Date())/1000, ...config.params } } } if(typeof(config.params) == "undefined"){ config.params = { ...config.params } } if(window._CONFIG['channelId'] != ''){ config.params.channelId = window._CONFIG['channelId']; } if(window._CONFIG['clientType'] != ''){ config.params.clientType = window._CONFIG['clientType']; } return config },(error) => { return Promise.reject(error) })
响应案例:
axios.interceptors.response.use(function (response) { // 当状态码为2xx/3xx开头的进这⾥ // 对响应数据做点什么 return response }, async function (error) { // 响应状态码4xx/5xx进这⾥ // 对响应错误做点什么 if (error.response.status === 401) { // ⾝份过期/token⽆效 // 1.清空vuex的token store.commit('setToken', '') store.commit('setRefreshToken', '') // 2. 清空本地token localStorage.removeItem('token') localStorage.removeItem('refresh_token') // 跳转到登录页⾯登录 router.push({ path: '/login' }) } return Promise.reject(error) })
service.interceptors.response.use((response) => { var self = this; var data = response.data; var requestCode = data.code; if(requestCode == 2001 || requestCode==2002 || requestCode==2003 || requestCode==2004){ var responseUrl = response.request.responseURL; //获取原请求地址 var requestData = response.config.data; //获取原请求参数 let m_params = new FormData(); //创建form对象 var token = geturlData().token; var userName = geturlData().userName; var userId = geturlData().userId; // var paraString = location.search; // var paras = paraString.split("&"); // // var temStr = decodeURIComponent(paras[0]) // // var token = temStr.substr(temStr.indexOf("token=") + 6); // var token = temStr.substr(temStr.indexOf("token=") + 6,temStr.indexOf("&userName=") - (temStr.indexOf("token=") + 6)); // var userName = temStr.substr(temStr.indexOf("userName=") + 9,temStr.indexOf("token=")); // var userId = temStr.substr(temStr.indexOf("userId=") + 7,(temStr.indexOf("token=") - temStr.indexOf("token=") + 6)); m_params.append('login_token',token); m_params.append('resourceId',userId); m_params.append('userName', decodeURI(userName)); $.ajax({ type:"POST", async:false, processData: false, contentType: false, url:window._CONFIG['domianURL'] + '/sys/loginByResource', data:m_params, success:function(response){ if(response.code =='200'){ var result = response.result const userInfo = result.userInfo Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000) Vue.ls.set(USER_NAME, userInfo.userName, 7 * 24 * 60 * 60 * 1000) Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000) Vue.ls.set(UI_CACHE_DB_DICT_DATA, result.sysAllDictItems, 7 * 24 * 60 * 60 * 1000) window.localStorage.setItem('menu',JSON.stringify(result.menu)); window.localStorage.setItem('allAuth',JSON.stringify(result.allAuth)) window.localStorage.setItem('auth',JSON.stringify(result.auth)) window.localStorage.setItem('userInfo',JSON.stringify(result.userInfo)) responseUrl = responseUrl+"?token="+result.token; $.ajax({ type: "POST", async: false, processData: false, contentType: false, url: responseUrl, data:requestData, success: function (resultData) { data = resultData; }, error:function(e){ console.log(e.status); console.log(e.responseText); } }); }else{ reject(response) } }, error:function(e){ console.log(e.status); console.log(e.responseText); } }) } return data; }, autoLoginErr) const autoLoginErr = (error) => { if (error.response) { let that=this; let data = error.response.data const token = Vue.ls.get(ACCESS_TOKEN) console.log("------异常响应------",token) console.log("------异常响应------",error.response.status) switch (error.response.status) { case 403: break case 500: break case 404: break case 504: break case 401: break default: break } } return Promise.reject(error) };
async function queryData(id) {
const ret = await axios.get('/data');
return ret;
}
queryData.then(ret=>{
console.log(ret)
})
应该是 Vue 对函数调用表达式额外用了一个函数做了层包装。
事先说明:加与不加括号的区别在于事件对象参数 event 的处理。不加括号时,函数第一个参数为 event,加了括号后,需要手动传入 $event 才能获得事件对象
<input type="button" value="按钮1" v-on:click="fn($event)">
<input type="button" value="按钮1" v-on:click="fn">
methods:{
fun(e){
console.log(e)//打印结果一致
}
}
<el-table-column prop="status" label="操作" align="center" width="300" fixed="right"> <template slot-scope="scope"> <el-button class="reset_btn_sys" type="text" size="small" @click="operation(scope.row.id,0)" v-if="status=='0'" :disabled="status=='0' && scope.row.reviewStatus!='0'">通过 </el-button> <el-button class="delete_btn_sys" type="text" size="small" @click="operation(scope.row.id,1)" v-if="status=='0'" :disabled="status=='0' && scope.row.reviewStatus!='0'">驳回 </el-button> <el-button class="reset_btn_sys" type="text" size="small" @click="operation(scope.row.id,2)" v-if="status!='2'" :disabled="!(scope.row.isOnline=='1' && scope.row.reviewStatus=='1')">下线 </el-button> <el-button class="reset_btn_sys" type="text" size="small" @click="operation(scope.row.id,3)" v-if="status!='2'" :disabled="!(scope.row.isOnline=='0' && scope.row.reviewStatus=='1')">上线 </el-button> <el-button class="see_btn_sys" type="text" size="small" @click="detail(scope.row)">详情 </el-button> <el-button class="delete_btn_sys" type="text" size="small" @click="operation(scope.row.id,4)" >删除 </el-button> </template> </el-table-column>
var url = dataServer + "/pest/mill/addMillManagement" $.ajax({ url: url, type: "post", data: formData, // ajax传文件 一定要指定两个关键性的参数 contentType: false, // 不用任何编码 因为formdata对象自带编码 django能够识别该对象 processData: false, // 告诉浏览器不要处理我的数据 直接发就行 ContentType: 'multipart/form-data', // 此接口后台需要form表单格式,故加此header success(result) { self.$toast(result.msg) if(result.code == 0){ self.$emit("addSuccess") } }, error() { } })
1、如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数
2、如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递, 并且事件对象的名称必须是$event
<button v-on:click='handle1'>点击1</button>
<button v-on:click='handle2(123, 456, $event)'>点击2</button>
methods: { handle1: function(event) { console.log(event.target.innerHTML) },handle2: function(p, p1, event) { console.log(p, p1) console.log(event.target.innerHTML) this.num++; } }
3、在html中使用data中数据不需要this
4、在函数中想要使用data中数据一定要加this
filters过滤器
<div class="warninfo_tablebox_clcyfh"> <el-table :data="tableData" border> <el-table-column label="新闻名称" align="center" prop="newsName"> </el-table-column> <el-table-column prop="description" label="描述" align="center"> <template slot-scope="scope"> <span>{{ scope.row.description | ellipsis(10) }}</span> --过滤 </template> </el-table-column> <el-table-column prop="status" label="操作" align="center" width="300" fixed="right"> <template slot-scope="scope"> <el-button class="reset_btn_sys" type="text" size="small" @click="operation(scope.row.id,0)" v-if="status=='0'" :disabled="status=='0' && scope.row.reviewStatus!='0'">通过 </el-button> <el-button class="delete_btn_sys" type="text" size="small" @click="operation(scope.row.id,1)" v-if="status=='0'" :disabled="status=='0' && scope.row.reviewStatus!='0'">驳回 </el-button> <el-button class="reset_btn_sys" type="text" size="small" @click="operation(scope.row.id,2)" v-if="status!='2'" :disabled="!(scope.row.isOnline=='1' && scope.row.reviewStatus=='1')">下线 </el-button> <el-button class="reset_btn_sys" type="text" size="small" @click="operation(scope.row.id,3)" v-if="status!='2'" :disabled="!(scope.row.isOnline=='0' && scope.row.reviewStatus=='1')">上线 </el-button> <el-button class="see_btn_sys" type="text" size="small" @click="detail(scope.row)">详情 </el-button> <el-button class="delete_btn_sys" type="text" size="small" @click="operation(scope.row.id,4)" >删除 </el-button> </template> </el-table-column> </el-table> </div> computed: {}, filters:{ ellipsis(value, limit) { if (!value) return '' if (value.length > limit) { return value.slice(0, limit) + '...' } return value }, }, mounted() {}
<el-select v-model="ruleForm.category" @change="selectedCag" placeholder="请选择项目类别"> <el-option v-for="item in ClaOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> methods: { selectedCag(vId){ console.log(vId) let obj = {}; obj = this.ClaOptions.find((item)=>{ //这里的ClaOptions就是上面遍历的数据源 return item.value === vId; //筛选出匹配数据,这里的value是你遍历数组每一项的value,如果没有对后台返回的数据做处理,一般为id }); console.log(obj.label);// 这里的label就是对应label的 this.addCagLabel = obj.label //打印出来的obj.label就是想要的label值 }, }
用一些键值对来模拟一系列表单控件:即把form中所有的元素的name与value组成一个queryString。
异步上传二进制文件。
<el-form :model="model" :rules="rules" ref="model"> <div style="display: inline-block"> <el-form-item prop="newsName" label="新闻标题" :label-position="labelPosition" :label-width="labelWidth"> <el-input class="searchInput" v-model="model.newsName" placeholder="请输入新闻标题" filterable clearable remote reserve-keyword size='small'> </el-input> </el-form-item> <el-form-item prop="description" label="简要描述" :label-position="labelPosition" :label-width="labelWidth"> <el-input class="searchInput" v-model="model.description" type="textarea" :autosize="{maxRows: 6}" placeholder="请输入类型描述" filterable clearable remote reserve-keyword size='small'> </el-input> </el-form-item> </div> </el-form> data(){ //自定义校验 var contents = (rule, value, callback) => {//新增文件校验 console.log("content"); if (value === '' || value==null) { callback(new Error('请输入内容')); } callback(); }; return { labelPosition:"right", labelWidth:"80px", rules:{ description: [ {required: true, message: '请输入简要描述', trigger: 'blur'}, {pattern: '^[^ ]+$', message: '描述中不能含有空格'}, ], source: [ {required: true, message: '请输入来源', trigger: 'blur'}, {pattern: '^[^ ]+$', message: '来源中不能含有空格'}, { min: 0, max: 20, message: '长度不超过20字符'} ], newsName: [ {required: true, message: '请输入新闻标题', trigger: 'blur'}, {pattern: '^[^ ]+$', message: '标题中不能含有空格'}, ], content: [ {required: true, validator: contents, trigger: 'change'}, ], fileList:[{required:true, message:"图片不能为空", trigger:"blur"}], newsTypeId: [ {required: true, message: '请输入新闻内容', trigger: 'change'}, ], }, model: { newsName: "",//新闻内容 description: "",//描述 content: "",//富文本编辑器内容 imagePath: '',//封面path newsTypeId: '',//新闻分类id status: '',//发布状态0保存1发布2下线3定时发布 id: '',//数据id fileList:[],//图片list groupId: '',//富文本编辑器图片的组id updateGroupId: '', categoryName:'',//新闻分类名称 source:""//新闻来源 }, dialogImageUrl: '',//图片 dialogVisible: false, disabled: false, delFileString:[], reloading: false, selectorId: new Date().getTime(), selectorId1: new Date().getTime(), } }, mounted() { var self = this; if(self.isAdd && self.isAdd != 1){ self.getInfo(); } }, methods:{
<el-button v-if="isAdd != 2" @click="reset('model')" class="sys_form_button_pro">重置</el-button>
reset(formDate) {//重置
let self = this;
self.$refs[formDate].resetFields();
},
this.$refs.addForm.clearValidate(['imagePath']);
this.$refs.model.validateField(['content']);
content: [
{required: true, validator: contents, trigger: 'change'},
],
if(self.model.content != null && self.model.content !=''){
this.$refs.model.clearValidate(['content']);//清除单个验证信息
}else{
this.$refs.model.validateField(['content']);//调用单个表单验证
}
需求:
在使用element中table列表可能会需要复选批量操作,这时我们只要手动添加一个el-table-column,设type属性为selection即可,
element文档中一共给出三个方法分别是手动勾选触发 勾选全选触发 勾选发生变化触发 里面的数据是列表中的每一数据
如果想用方法只需要在开头@方法名 自己在method里定义
方法:
this. r e f s . m u l t i p l e T a b l e . c l e a r S e l e c t i o n ( ) ; / / 清空所有单选 c l e a r S e l e c t i o n ( ) 这个函数是自带的,比如日期的 g e t F u l l Y e a r ( ) t h i s . refs.multipleTable.clearSelection();//清空所有单选clearSelection()这个函数是自带的,比如日期的getFullYear() this. refs.multipleTable.clearSelection();//清空所有单选clearSelection()这个函数是自带的,比如日期的getFullYear()this.refs.multipleTable.toggleRowSelection(it, true);toggleRowSelection这个函数需要传某行数据,true表示勾选,false表示不勾选,
@selection-change默认是勾选的,也就是说这个事件传进来被选中的数据如果你还是想勾选,那么不需要任何操作,除非你想不勾选,那么就把那条数据用false来去掉勾选。
ref=“multipleTable” ref 被用来给DOM元素或子组件注册引用信息。引用信息会根据父组件的 r e f s 对象进行注册。如果在普通的 D O M 元素上使用,引用信息就是元素 ; 如果用在子组件上,引用信息就是组件实 @ r o w − c l i c k 点击那一行数据触发的事件默认传点击那行的数据,自定义 o n S e l e c t O p ( r o w ) 函数,用 r o w 来接收点击的那行数据,先把所有的选项清空 t h i s . refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实 @row-click点击那一行数据触发的事件默认传点击那行的数据,自定义onSelectOp(row)函数, 用row来接收点击的那行数据, 先把所有的选项清空this. refs对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素;如果用在子组件上,引用信息就是组件实@row−click点击那一行数据触发的事件默认传点击那行的数据,自定义onSelectOp(row)函数,用row来接收点击的那行数据,先把所有的选项清空this.refs.multipleTable.clearSelection();
再把点击的那行勾选this.$refs.multipleTable.toggleRowSelection(row, true);
把自定义数组清空this.selectlist = [];
把新勾选的数据push进数组this.selectlist.push(row);
注意:只要想要在Vue中直接操作DOM元素,就必须用ref属性进行注册
<el-table :data="tableData" ref="multipleTable" v-loading="isLoading" @selection-change="handleChange" border> <el-table-column fixed type="selection" width="36px" v-if="!isSenBY"> </el-table-column> <el-table-column label="县局林业局名称" align="center" prop="xianName"> </el-table-column> <el-table-column label="乡镇(林场)名称" align="center" prop="linchangName"> </el-table-column> <el-table-column label="林班号" align="center" prop="linban"> </el-table-column> <el-table-column label="小班号" align="center" prop="xiaobanhao"> </el-table-column> <el-table-column label="采集人" align="center" prop="collectPersonName"> </el-table-column> <el-table-column label="采集地点" align="center" prop="collectArea"> </el-table-column> <el-table-column prop="status" label="操作" align="center" width="200" fixed="right"> <template slot-scope="scope"> <el-button class="reset_btn_sys" type="text" size="small" :class="{grea:!getPower(scope.row)}" @click="addRecord(scope.row,getPower(scope.row))">{{ isSenBY ? "核实" : "上报" }}</el-button> <el-button class="editor_btn_sys" type="text" size="small" :class="{grea:!getPower(scope.row)}" @click="edit(scope.row,getPower(scope.row))">编缉</el-button> <el-button class="see_btn_sys" type="text" size="small" @click="lookInfo(scope.row)">详情</el-button> <el-button class="delete_btn_sys" type="text" size="small" :class="{grea:!(getPower(scope.row)&&!isSenBY)}" @click="del(scope.row,getPower(scope.row)&&!isSenBY)">删除</el-button> </template> </el-table-column> </el-table>
全选中去掉个别选中选项
批量删除有个别数据不允许删除,当用户全选点击批量筛选不能删除数据取消勾选 del(item) { var self = this var ids = MLib.joinSplit(self.chooseArr, ",", "id") if (item == "delBatch") { if (ids == "") { new MessageToast().show("请选择记录后再进行删除", MessageToast.ERROR) return } var arr = self.chooseArr; var newChooseArr = []; var ble = false;//批量选中数据中是否有巡护员上报不能删除的数据存在 for(var i=0;i<arr.length;i++){ //打印数组中的情况,便于跟踪数组中数据的变化 var success = this.getPower(arr[i])//查看该数据是否属于巡护员暂存数据,暂存的可以删除 上报的不可以删除 if(success){//能删除的抽取出来 //注意对比这行代码:删除元素后调整i的值 newChooseArr.push(arr[i]); arr.splice(i,1); }else{//不能删除的去掉 ble = true; } }; self.chooseArr = newChooseArr; this.$refs.multipleTable.clearSelection();//清空全部选中 newChooseArr.forEach(row => {//把符合删除条件的数据重新选中 this.$refs.multipleTable.toggleRowSelection(row, true); }); if(ble){ new MessageToast().show("记录中含有不可选择项,已去掉选中", MessageToast.SINGLETON) return; } ids = MLib.joinSplit(self.chooseArr, ",", "id") } else { ids = item.id } self.$messageBox({ title: "消息", text: "是否要删除记录", callback(action) { if (action) { var params = {} params.ids = ids handler.deleteDiscoloredStandingTreeReport(params).then(result => { MLibVue.requestSuccess(result, true) self.search(self.pg.pageIndex) }) } } }) },
for(var i=0;i<arr.length;i++){
//打印数组中的情况,便于跟踪数组中数据的变化
var success = this.getPower(arr[i])//查看该数据是否属于巡护员暂存数据,暂存的可以删除 上报的不可以删除
if(success){//能删除
//注意对比这行代码:删除元素后调整i的值
arr.splice(i,1);
}
};
del(id) {//点击删除按钮 this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { var self = this; var params = {}; params.id = id; params.idDelete = "1"; handler.delNewsManagement(params).then(res => { this.$message({ type: 'success', message: '删除成功!' }); self.pg.currentPage = 1 self.search(); }) }).catch(() => { this.$message({ type: 'info', message: '已取消删除' }); }); },
self.$message.error(result.msg);
注: 下拉选级联有两种实现方式
<el-form-item label=""> <el-select v-model="model.city" placeholder="请选择市林业局名称" size="large" @change="changeData"> <el-option v-for='item in MLibVue.getOrganizationList(22)' :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> </el-form-item> <el-form-item label=""> <el-select v-model="model.xian" placeholder="请选择县林业局名称" size="large"> <el-option v-for='item in xianList' :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> </el-form-item> changeData() {//点击下拉选数据 var self = this; var params = {} self.model.xian = null; self.model.linChang = ""; self.linChangList = []; params.pid = self.model.city; handler.getOrganizationLists(params).then(result => { self.xianList = result.data; }) },
<div class="searchItemContent"> <!-- <div class="searchParams ">市林业局名称</div>--> <el-select class="searchInput" v-model="model.city" placeholder="请选择市林业局名称" bind-data="linChang" remote clearable reserve-keyword size='small'> <el-option class='forest-query-form-input' v-for='item in MLibVue.getOrganizationList(22)' :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> </div> <div class="searchItemContent"> <!-- <div class="searchParams ">县林业局名称</div>--> <el-select class="searchInput" v-model="model.xian" placeholder="请选择县林业局名称" bind-data="linChang" remote clearable reserve-keyword size='small'> <el-option class='forest-query-form-input' v-for='item in xianArr' :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> </div> <div class="searchItemContent"> <!-- <div class="searchParams ">乡镇(林场)名称</div>--> <el-select class="searchInput" v-model="model.linChang" placeholder="请选择乡镇(林场)名称" bind-data="linChang" remote clearable reserve-keyword size='small'> <el-option class='forest-query-form-input' v-for='item in linChangArr' :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> </div> <div class="searchItemContent"> <!-- <div class="searchParams ">林班号</div>--> <el-select class="searchInput" v-model="model.linBanCode" bind-data="linBanCode" placeholder="请选择林班号" clearable remote reserve-keyword size='small'> <el-option class='forest-query-form-input' v-for='item in linBanArr' :key="item.linban" :label="item.linbanName" :value="item.linban"> </el-option> </el-select> </div> <div class="searchItemContent"> <!-- <div class="searchParams ">小班号</div>--> <el-select class="searchInput" v-model="model.xiaoBan" bind-data="xiaoBan" placeholder="请选择小班号" remote clearable reserve-keyword size='small'> <el-option class='forest-query-form-input' v-for='item in xiaoBanArr' :key="item.xiaoban" :label="item.xiaobanName" :value="item.xiaoban"> </el-option> </el-select> </div> watch: { 'model.city'(newVal) { var self = this this.model.xian = "" self.xianArr = []; MLibVue.getOrganizationList(newVal, (arr) => { self.xianArr = arr }) }, 'model.xian'(newVal) { var self = this this.model.linChang = "" self.linChangArr = []; MLibVue.getOrganizationList(newVal, (arr) => { self.linChangArr = arr }) }, 'model.linChang'(newVal) { var self = this self.model.linBanCode = "" self.linBanArr=[]; if(newVal!=''){ var params = {} params.linchang = newVal; handler.getLinBanList(params).then(result => { self.linBanArr = result.data }) } }, 'model.linBanCode'(newVal, oldVal) { var self = this self.model.xiaoBan = "" self.xiaoBanArr=[] if(newVal!=''){ var params = {} params.linban = newVal handler.getXiaoBanHaoList(params).then(result => { self.xiaoBanArr = result.data }) } }, },
注意:
为el-select设置clearable属性,则可将选择器清空。需要注意的是,clearable属性仅适用于单选。
什么是前端数据存储
客户端存储是快速为一个应用进行性能优化的绝佳方法。通过把数据存储在浏览器中,用户不必每次都向服务器请求获取同一个信息。在你离线时,使用本地存储的数据而不是向远端服务器上请求数据就显得非常有用,甚至在线用户也可以从中受益。客户端存储可以通过这些技来实现:cookie 、sessionstorage、localstorage(H5 存储 Web Storage )。
1.什么是 localStorage
localStorage 对象是存储特定于某个会话的数据,与 sessionStorage 不一样的就在于, localStorage 存储是持久的,不通过手动清除的话,就会一直存在与客户端。
2.localStorage 的特点
localStorage 会将第一次请求的数据直接存储在本地,可以存储 5M 数据,相比于 cookie 可以解决带宽,但只有高版本浏览器中才支持。
目前所有浏览器都会把 localStorage 的值类型限定为 string 类型,对于我们比较常见的 JSON 对象类型需要转换。
localStorage 本质上是对字符串的读取,如果存储内容多的话会小号内存空间,导致页面变卡。
3.localStorage 的用法
localStorage.setItem('menuInfo', JSON.stringify(this.menuInfo))//将菜单对象信息转成json存在本地
let str = localStorage.getItem('menuInfo');//本地获取json信息转成对象
if (str) {
this.menuInfo = JSON.parse(str);
}
window.localStorage.removeItem("userInfo");//移除
1、cookie 的作用
cookie 是比较老的前端缓存技术,使用 cookie 必须要有服务器(HTML 静态网页无效),存储的大小限制在 4k。为什么要有服务才能使用 cookie 呢?因为 cookie 是要在服务器和浏览器来回输送的,由于浏览器的的跨域限制,客户端和服务端必须保证是同源策略(也就是跨域)。而且 cookie 是存放在前端的,安全一直以来是个问题,适合使用一些会话信息传输。
2、cookie 的构成
一般来说,cookie 是有浏览器保存的以下几块信息构成的。
1.名称:cookie 名称,是个唯一值。
2.值:存储在 cookie 中的字符串值,值必须被 URL 编码。
3.域:cookie 对于哪个域是有效的,所有向该域发送的请求都会包含这个 cookie 信息。
4.路径:对于指定域中的路径,应该向服务器发送 cookie。
5.失效时间:表示 cookie 何时应该被删除的时间戳。
6.安全标志:指定后,cookie只有在使用SSL连接的时候才发送到服务器。
3. cookie 存储和获取
// 设置 cookie function setCookie(name,value,options={}){ let cookieData = `${name}=${value};`; for(let key in options){ let str = `${key}=${options[key]};`; cookieData += str; }; document.cookie = cookieData; }; // 获取 cookie function getCookie(name){ let arr = document.cookie.split("; "); for(let i =0;i<arr.length;i++){ let arr2 = arr[i].split("="); if(arr2[0]==name){ return arr2[1]; }; }; return ""; }; export function setCookie(c_name, value, expire) { var date = new Date() date.setSeconds(date.getSeconds() + expire) document.cookie = c_name + "=" + escape(value) + "; expires=" + date.toGMTString() // console.log(document.cookie) } /*获取cookie*/ export function getCookie(c_name) { if (document.cookie.length > 0) { let c_start = document.cookie.indexOf(c_name + "=") if (c_start != -1) { c_start = c_start + c_name.length + 1 let c_end = document.cookie.indexOf(";", c_start) if (c_end == -1) c_end = document.cookie.length return unescape(document.cookie.substring(c_start, c_end)) } } return "" } /*删除cookie*/ export function delCookie(c_name) { setCookie(c_name, "", -1) }
1.什么是 sessionStorage
sessionStorage 对象是存储特定于某个会话的数据,也就是数据只保存到浏览器关闭后消失,存储在 sesstionStorage 中的数据可以跨页面刷新而存在。
2.sessionStorage 的特点
同源策略限制,若想在同一个页面之间对同一个 sessionStorage 进行操作,这些页面必须是同协议、域名、端口号。
单标签页限制,sesstionStorage 操作限制在单个标签页中,在此标签页进行同源页面访问都可以共享 sesstionStorage 数据。
只在本地存储,seesionStorage 的数据不会跟随HTTP请求一起发送到服务器,只会在本地生效,并在关闭标签页后清除数据。
存储方式,seesionStorage 的存储方式采用 key、value 的方式。
存储上限限制,不同的浏览器存储的上限也不一样,但大多数浏览器把上限限制在 5MB 以下。
3.sessionStorage 的用法
sessionStorage.length // 获取 storage 的个数
sessionStorage.key(n) // 获取 storage 中第 n 个元素对的键值
sessionStorage.getItem(key) // 获取 key 对应的值
sessionStorage.key // 获取键值 key 对应的值
sessionStorage.setItem(key, value) // 添加数据,键值为 key,值为 value
sessionStorage.removeItem(key) // 移除键值为 key 的值
sessionStorage.clear() // 清除所有数据
新增新闻中预览内容 detail(type) { var self = this; if(type==1){//新增预览 sessionStorage.setItem("xinzengcontent", self.encode(self.model.content));//把新闻base64存储在本地 let {href} = self.$router.resolve({ path: '/newsDetailVue1', query: { type:"0", title: self.model.newsName, author: self.model.source, releaseDate: self.encode(""), } }); window.open(href, '_blank');//打开新窗口展示 }, if(sessionStorage.getItem('xinzengcontent')){ self.content = self.decode(sessionStorage.getItem('xinzengcontent'));//接收数据并解析数据 }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。