目录
vue插值表达式:
MVVM设计模式:
VUE的设计模式,可以实现双向绑定,同时对view和model的变化进行同时改变。
v-bind:
v-on:
v-on中事件对象的传递:
v-on修饰符:
详细修饰符见:详细修饰符https://cn.vuejs.org/v2/guide/events.html
v-model:
进行表单的value属性和我们变量互相绑定到一起。
示例代码:
<template>
<form action="" @keydown.enter.prevent>
<label for="name">username:</label>
<input type="text" id="name" v-model="username" @keydown.enter="printLog">
<label for="passwd">password:</label>
<input type="password" id="passwd" v-model="password" @keydown.enter="printLog">
</form>
</template>
<script>
export default {
data() {
return {
username: '',
password: '',
testVal:''
}
},
methods:{
printLog(){
console.log(this.testVal)
}
}
}
</script>
<style>
</style>
<!--当我们绑定复选框这一类标签,一定要使用v-model绑定到所有的复选框--> <!--并且双向绑定的变量一定要使用数组做初始化,以储存我们选中的复选框value值--> <!--另外如果使用非数组变量,会存储复选框的checked属性,即true-->
v-model修饰符:
v-text和v-html:
示例:
<template>
<div>
<div v-text="text1"></div>
<div v-html="html1"></div>
<div v-text="text1"></div>
</div>
</template>
<script>
export default {
data() {
return {
text1:'<h1>1234</h1>',
html1:'<h1>1234</h1>',
// textTemp:'我会被覆盖'
}
}
}
</script>
<style>
</style>
v-text或v-html会覆盖插值表达式。
v-show和v-if:
v-for:
v-for的示例代码:
<template>
<div class="box">
<ul>
<!--需要值和索引时的遍历方法-->
<li v-for="(item,index) in arr">
{{ index }}-name:{{ item.name }}-age:{{ item.age }}
</li>
<!--不需要索引值时的遍历方法-->
<li v-for="item in arr" :key="item.name">
<span>
name:{{ item.name }}
</span>
<span>
age:{{ item.age }}
</span>
</li>
<!--循环数组中的对象-->
<li v-for="item in arr">
<div v-for="(val,key) in item">
<span>{{ key }}:</span><span>{{ val }}</span>
</div>
</li>
<!--遍历数字-->
<li v-for="num in nums">当前:{{num}}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
arr: [{name: 'XiXi', age: 22}, {name: 'ZhuZhu', age: 18}, {name: 'LanLan', age: 38}, {name: 'ShuShu', age: 28}],
nums:5
}
},
methods: {}
}
</script>
<style>
ul {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
</style>
vue中js导入图片:
vue项目中导入本地图片,在html和css中都是和原来一样的,而js不一样,需要将图片当作模块引入,因其是webpack支持的,会将路径当作模块。下面是一段示例代码。
<template>
<img :src="url">
<img :src="url2" alt="error">
<img src="./assets/1.jpg" alt="">
<img src="" alt="" class="testBGI">
</template>
<script>
//JS中我们无法直接使用路径,由于是Webpack在底层支持我们,导致我们的路径不能按正常路径指向
//由于JS中我们默认路径为模块,所以我们直接将图片当作模块导入就好了
import img from '@/assets/1.jpg'
export default {
data(){
return {
url:"https://img-blog.csdnimg.cn/a79c372160be45909aed3a2cd5a8ba79.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA4oaQ5L2g5oya54ix55qE5by65ZOl4oaS,size_20,color_FFFFFF,t_70,g_se,x_16",
url2:img
}
}
}
</script>
<style>
img{
width: 200px;
height: 200px;
}
.testBGI{
background-image: url("./assets/1.jpg");
background-position: center;
background-size: contain;
}
</style>
v-for的更新检测:
<template>
<div v-for="item in arr">{{item}}</div>
<button @click="revBtn">reverseArr</button>
<button @click="getBtn">get top 3</button>
<button @click="changeBtn">change one of them</button>
<button @click="sortBtn">sort</button>
</template>
<script>
export default {
data(){
return {
arr:[1,3,5,2,7],
}
},
methods:{
revBtn(){
//reverse改变原数组,所以变化
this.arr.reverse()
},
getBtn(){
//slice方法并不会修改原数组,所以不会变化
this.arr.slice(0,3)
},
changeBtn(){
//因改变某元素的值,所以变化
this.arr[1]=11
},
sortBtn(){
//因改变原数组,所以变化
this.arr.sort().reverse()
}
}
}
</script>
<style>
</style>
v-for数组改变时的更新过程:
虚拟DOM:
diff算法:
key作用:
首先看看下面的代码:
<template>
<div>
<ul>
<!-- 无key属性的添加-->
<li v-for="(item,index) in arr" :key="index">
{{item}}: <input type="text" name="" id="" v-model.lazy.number="value[index]">
</li>
</ul>
<button @click.stop="addElem">添加</button>
</div>
</template>
<script>
export default{
data(){
return{
arr:['first','second','third'],
value:[1,2,3,4]
}
},
methods:{
addElem(){
this.arr.splice(0,0,'zero')
}
}
}
</script>
<style>
</style>
key值用索引和不用是一样的结果,所以当我们使用key来辨别更新后的元素时,元素的值还是会错乱。下面我们来看看如何解决这一问题。
<template>
<div>
<ul>
<!-- key属性的添加-->
<li v-for="(item,index) in arr" :key="index">
{{item}}: <input type="text" name="" id="" v-model.lazy.number="value[index]">
</li>
</ul>
<button @click.stop="addElem">添加</button>
<!-- key属性使用字符串绑定,并不完美,当item值相同依旧会出现问题-->
<ul>
<li v-for="item in arr2" :key="item">
{{item}}: <input type="text" name="" id="">
</li>
</ul>
<button @click.stop="addElem2">添加</button>
<!-- 下面我们使用id的方法来解决这一问题-->
<ul>
<li v-for="item in arr3" :key="item.id">
{{item.name}}: <input type="text" name="" id="">
</li>
</ul>
<button @click.stop="addElem3">添加</button>
</div>
</template>
<script>
let objArr=[]
for (let i = 0; i < 10; i++) {
let obj={}
obj.id=i+1
obj.name="text"+(i+1)
objArr.push(obj)
}
export default{
data(){
return{
arr:['first','second','third'],
arr2:['first','second','third'],
arr3:objArr,
value:[1,2,3,4]
}
},
methods:{
addElem(){
this.arr.splice(0,0,'zero')
},
addElem2(){
this.arr2.splice(0,0,'zero')
},
addElem3(){
this.arr3.push({name:"text"+(objArr.length+1),id:objArr.length+1})
}
}
}
</script>
<style>
</style>
使用id的中间过程如下:
动态class:
动态style:
根据class和style的动态方法,给出示例代码:
<template>
<div>
<span :class="{changeStyle:isTake}">我是动态的class</span>
<button @click.stop="changeColor">changeColor</button>
<br>
<span :style="{backgroundColor:color}">我是动态style样式</span>
<button @click.stop="changeStyleColor">randChangeColor</button>
</div>
</template>
<script>
import {getRandInt} from "./assets/JS/MyTools"
export default {
data(){
return{
isTake:true,
color:'blue'
}
},
methods:{
changeColor(){
this.isTake=!this.isTake
},
changeStyleColor(){
this.color=`rgba(${getRandInt(0,255)},${getRandInt(0,255)},${getRandInt(0,255)})`
}
}
}
</script>
<style>
.changeStyle{
color:cadetblue;
}
</style>
通过上面所学我们做一个
商品的案例:
<template>
<div :class="{'myTable':true}">
<table :class="{'table':true,'table-hover':hoverStyle}">
<thead>
<tr>
<th v-for="item in tableHeadTitle">{{item}}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in objArr" :key="item.id">
<td v-for=" val in item">{{val}}</td>
<td><a href="#" @click.prevent="deleteLine(item.id)">delete</a></td>
</tr>
</tbody>
</table>
<div class="addPart">
<input type="text" name="" id="" placeholder="资产名称" v-model.lazy.trim="prodName">
<input type="text" placeholder="0" v-model.lazy.number="prodPrice">
<button @click.stop="addProduct" class="btn btn-success btn-sm">add production</button>
</div>
</div>
</template>
<script>
import 'bootstrap/dist/css/bootstrap.min.css'
import {slightCopy} from "@/assets/JS/MyTools";
let objArr=[
{
id:100,
name:'外套',
price:299,
initTime:new Date()
},{
id:101,
name:'鞋子',
price:199,
initTime:new Date()
},{
id:102,
name:'手机',
price:3450,
initTime:new Date()
},{
id:103,
name:'雨伞',
price:38,
initTime:new Date()
}
]
export default {
data(){
return{
tableHeadTitle:['ProdID','ProdName','Price','InitialTime','Func'],
objArr:objArr,
hoverStyle:true,
prodName:"",
prodPrice:0,
}
},
methods:{
deleteLine(getID){
let getIndex=this.objArr.findIndex(function (ele) {
return ele.id===getID
})
this.objArr.splice(getIndex,1)
},
addProduct(){
if (this.prodName===''||this.prodPrice===''){
alert('please write in something')
return false
}else{
let flag=true
for (const objArrElement of objArr) {
if (objArrElement.name===this.prodName){
flag=false
return console.log('匹配到相同项:'+this.prodName)
}
}
if (flag){
this.objArr.push({
id:this.objArr[this.objArr.length-1].id+1,
name:this.prodName,
price:this.prodPrice,
initTime:new Date()
})
}else return false
}
}
}
}
</script>
<style>
body{
box-sizing: border-box;
vertical-align: middle;
}
tr:hover{
cursor: pointer;
}
.myTable{
width: 800px;
border: 1px solid #c1c1c1;
border-radius: 3px;
}
.addPart{
display: flex;
margin: 5px;
width: 500px;
justify-content: space-between;
}
</style>
综合上面的案例:
- 我们要添加一个bootstrap包,使用yarn add bootstrap,在项目中添加bs包。并且如果想要全页面使用的化,我们就要在main.js中导入,代码类似“import 'bootstrap/dist/css/bootstrap.min.css'”。
- 我们要将包添加一个单vue组件时,也是用上面的代码“import 'bootstrap/dist/css/bootstrap.min.css‘ ”放入script中即可。
过滤器:
在我们之前使用的art-template中我们接触过过滤器。在vue中书写方式类似:
由于3.x版本之后的vue已废弃原先的filter方法,下面给出解决方案。
详见VUE官方文档https://v3.cn.vuejs.org/guide/migration/filters.html#_2-x-%E8%AF%AD%E6%B3%95
下面给出,vue5的替代办法代码示例:
<template>
<div>
<div>原型 : <span class="originType">{{ text }}</span></div>
<div>调用prototype自定义字符串方法 : <span>{{ text.reverse() }}</span></div>
<div>调用方法 : <span>{{ reverseString(text) }}</span></div>
<div>使用计算属性 : <span>{{ reverseText }}</span></div>
</div>
</template>
<script>
// 这里我们给String原型类中添加了reverse方法
String.prototype.reverse = function () {
return this.split('').reverse().join('')
}
export default {
data() {
return {
text:"hello, world!"
}
},
methods: {
reverseString(string) {
return string.split("").reverse().join('')
}
},
computed: {
reverseText() {
return this.text.split('').reverse().join('')
}
}
}
</script>
<style>
span {
font-weight: bolder;
color: indianred;
}
span.originType {
color: #2aabd2;
}
</style>
上面我给出了在vue5中的解决方案。
计算属性computed:
计算属性的使用场景:当一个变量通过其它变量计算而来。
利用computed实现简易计算器:
<template>
<div>
<form>
<input type="number" v-model="a">
<input type="radio" name="choice" @click="changeSign('+')" checked>+
<input type="radio" name="choice" @click="changeSign('-')">-
<input type="radio" name="choice" @click="changeSign('*')">*
<input type="radio" name="choice" @click="changeSign('/')">/
<input type="number" v-model="b">
</form>
<div>结果:{{ calculate }}</div>
</div>
</template>
<script>
export default {
data() {
return {
a: 0,
b: 0,
sign: '+',
num: 0
}
},
methods: {
changeSign(sign) {
switch (sign) {
case '+': {
this.sign = '+'
break
}
case '-': {
this.sign = '-'
break
}
case '*': {
this.sign = '*'
break
}
case '/': {
this.sign = '/'
break
}
}
}
},
computed: {
calculate() {
switch (this.sign){
case "+":{
return this.a + this.b
}
case "-":{
return this.a - this.b
}
case "*":{
return this.a * this.b
}
case "/":{
return this.a / this.b
}
}
return 0
}
}
}
</script>
<style>
</style>
计算属性-缓存:
通过上面的代码:大家一定好奇计算属性和方法的差异在哪里。
下面请看:
结果:
从此处我们可以发现计算属性的函数只调用了一次。
计算属性的完整写法:
当我们使用计算属性实现v-model的双向绑定时,我们会报一个错误。大致如下:
完整写法使用:
<template>
<div>
<form>
<input type="number" v-model="a">
<input type="radio" name="choice" @click="changeSign('+')" checked>+
<input type="radio" name="choice" @click="changeSign('-')">-
<input type="radio" name="choice" @click="changeSign('*')">*
<input type="radio" name="choice" @click="changeSign('/')">/
<input type="number" v-model="b">
</form>
<div>计算属性结果:{{ calculate }}</div>
<div>方法结果:{{ calculateMethod() }}</div>
<input type="text" v-model="calculate">
</div>
</template>
<script>
export default {
data() {
return {
a: 0,
b: 0,
sign: '+',
num: 0,
setNum:0
}
},
methods: {
changeSign(sign) {
switch (sign) {
case '+': {
this.sign = '+'
break
}
case '-': {
this.sign = '-'
break
}
case '*': {
this.sign = '*'
break
}
case '/': {
this.sign = '/'
break
}
}
},
calculateMethod() {
switch (this.sign){
case "+":{
return this.a + this.b
}
case "-":{
return this.a - this.b
}
case "*":{
return this.a * this.b
}
case "/":{
return this.a / this.b
}
}
return 0
}
},
computed: {
calculate:{
set(setNum){
this.setNum=setNum
},
get(){
switch (this.sign){
case "+":{
return this.a + this.b
}
case "-":{
return this.a - this.b
}
case "*":{
return this.a * this.b
}
case "/":{
return this.a / this.b
}
}
return 0
}
}
}
}
</script>
<style>
</style>
侦听器:
用于侦听某个变量是否发生改变。
深度侦听:
下面以一个产品表格的本地存储版本结束本节:
<template>
<div :class="{'myTable':true}">
<table :class="{'table':true,'table-hover':hoverStyle}">
<thead>
<tr>
<th v-for="item in tableHeadTitle">{{ item }}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in objArr" :key="item.id">
<td v-for=" val in item">{{ val }}</td>
<td><a href="#" @click.prevent="deleteLine(item.id)">delete</a></td>
</tr>
<tr>
<td>汇总数据:</td>
<td colspan="2">总价值:{{ totalPrice }}</td>
<td colspan="2">均价:{{ avgPrice }}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td :colspan="tableHeadTitle.length" v-show="objArr.length<=0" v-text="tableText"></td>
</tr>
</tfoot>
</table>
<div class="addPart">
<input type="text" name="" id="" placeholder="资产名称" v-model.lazy.trim="prodName">
<input type="text" placeholder="0" v-model.lazy.number="prodPrice">
<button @click.stop="addProduct" class="btn btn-success btn-sm">add production</button>
</div>
</div>
</template>
<script>
let originObjArr=[]
let getLocalStorageData=JSON.parse(localStorage.getItem("originObj"))
if(getLocalStorageData===null){
originObjArr= [
{
id: 100,
name: '外套',
price: 299,
initTime: new Date()
}, {
id: 101,
name: '鞋子',
price: 199,
initTime: new Date()
}, {
id: 102,
name: '手机',
price: 3450,
initTime: new Date()
}, {
id: 103,
name: '雨伞',
price: 38,
initTime: new Date()
}
]
}else{
originObjArr=getLocalStorageData.newObj
}
export default {
data() {
return {
tableHeadTitle: ['ProdID', 'ProdName', 'Price', 'InitialTime', 'Func'],
objArr: originObjArr,
hoverStyle: true,
prodName: "",
prodPrice: 0,
tableText: '# notice: this table is a null table!'
}
},
methods: {
deleteLine(getID) {
if (this.objArr.length <= 1) {
if (!confirm('当前项目已是最终项!')) {
return false
}
}
let getIndex = this.objArr.findIndex(ele => ele.id === getID)
this.objArr.splice(getIndex, 1)
},
addProduct() {
if (this.prodName === '' || this.prodPrice === '') {
alert('please write in something')
return false
} else {
let flag = true
for (const objArrElement of originObjArr) {
if (objArrElement.name === this.prodName) {
flag = false
return console.log('匹配到相同项:' + this.prodName)
}
}
if (flag) {
this.objArr.push({
id: this.objArr.length > 0 ? this.objArr[this.objArr.length - 1].id + 1 : 1,
name: this.prodName,
price: this.prodPrice,
initTime: new Date()
})
} else return false
}
}
},
computed: {
totalPrice() {
let sum = 0
this.objArr.forEach(ele => sum += ele.price)
return sum
},
avgPrice() {
let sum = 0
let getArrLength = this.objArr.length
this.objArr.forEach(ele => sum += ele.price)
return sum / getArrLength
}
},
watch:{
objArr:{
immediate:true,
deep:true,
handler(newVal,oldVal){
let savedObj={
newObj:newVal,
oldObj:oldVal
}
localStorage.setItem("originObj",JSON.stringify(savedObj))
}
}
}
}
</script>
<style>
body {
box-sizing: border-box;
vertical-align: middle;
}
tr:hover {
cursor: pointer;
}
.myTable {
width: 800px;
border: 1px solid #c1c1c1;
border-radius: 3px;
}
.addPart {
display: flex;
margin: 5px;
width: 500px;
justify-content: space-between;
}
</style>