Vue 知识小结
问:项目使用 vue 2 还是使用 vue 3 呢?
答:如下所示(记于 2022/8/5)
vue 2 响应式系统用了 Object.defineProperty,因此不兼容 IE8 及以下版本
vue 3 响应式系统用了 Proxy,因此不兼容 IE11 及以下版本
vue 2.x
基本使用
1、导包
2、html布局
3、Vue的实例化
html 文件中:
el:element 缩写。作用:挂载(确定一个使用范围)。不能挂载在
<html>
或者<body>
标签 上。
data:默认数据。
methods:方法集合。
created:生命周期–创建后。
写一个 todoList 的 demo:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,user-scalable=no" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>todoList</title>
</head>
<body>
<div id="app">
<div class="animate-wrap">
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"> <div class="animate" v-show="animate.show">📋</div> </transition>
</div>
<input type="text" v-model="title" @keydown.enter="addTodo" />
<button v-if="active<all" @click="clear">清理</button>
<span class="dustbin"> 🗑 </span>
<ul v-if="todos.length">
<transition-group name="flip-list" tag="ul">
<li v-for="(todo,i) in todos" :key="todo.title">
<input type="checkbox" v-model="todo.done" />
<span :class="{ done: todo.done }"> {{ todo.title }}</span>
<span class="remove-btn" @click="removeTodo($event,i)"> ❌ </span>
</li>
</transition-group>
</ul>
<div v-else>暂无数据</div>
<!-- <div>{{todos.filter(v=>!v.done).length}} / {{todos.length}}</div> -->
<div>{{active}} / {{all}}</div>
<transition name="modal">
<div class="info-wrapper" v-if="showModal">
<div class="info">哥,你啥也没输入!</div>
</div>
</transition>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const App = {
computed: {
active() {
return this.todos.filter(v => !v.done).length
},
all() {
return this.todos.length
},
allDone: {
get: function () {
return this.active === 0
},
set: function (val) {
this.todos.forEach(todo => {
todo.done = val
})
},
},
},
data() {
return {
title: '', // 定义一个数据
todos: [
{ title: '吃饭', done: false },
{ title: '睡觉', done: true },
],
showModal: false,
animate: {
show: false,
el: null,
},
}
},
methods: {
addTodo() {
if (!this.title) {
this.showModal = true
setTimeout(() => {
this.showModal = false
}, 1500)
return
}
this.todos.push({ title: this.title, done: false })
this.title = ''
},
clear() {
this.todos = this.todos.filter(v => !v.done)
},
removeTodo(e, i) {
this.todos.splice(i, 1)
},
beforeEnter(el) {
let dom = this.animate.el
let rect = dom.getBoundingClientRect()
let x = window.innerWidth - rect.left - 60
let y = rect.top - 10
el.style.transform = `translate(-${x}px, ${y}px)`
},
enter(el, done) {
document.body.offsetHeight
el.style.transform = `translate(0,0)`
el.addEventListener('transitionend', done)
},
afterEnter(el) {
this.animate.show = false
el.style.display = 'none'
},
removeTodo(e, i) {
this.animate.el = e.target
this.animate.show = true
this.todos.splice(i, 1)
},
},
}
// 启动应用
Vue.createApp(App).mount('#app')
</script>
<style>
li {
list-style: none;
}
.done {
color: gray;
text-decoration: line-through;
}
.info-wrapper {
position: fixed;
top: 20px;
width: 200px;
}
.info {
padding: 20px;
color: white;
background: #d88986;
}
.modal-enter-from {
opacity: 0;
transform: translateY(-60px);
}
.modal-enter-active {
transition: all 0.3s ease;
}
.modal-leave-to {
opacity: 0;
transform: translateY(-60px);
}
.modal-leave-active {
transition: all 0.3s ease;
}
.flip-list-move {
transition: transform 0.8s ease;
}
.flip-list-enter-active,
.flip-list-leave-active {
transition: all 1s ease;
}
.flip-list-enter-from,
.flip-list-leave-to {
opacity: 0;
transform: translateX(30px);
}
.animate-wrap .animate {
position: fixed;
right: 10px;
top: 10px;
z-index: 100;
transition: all 0.5s linear;
}
.dustbin {
float: right;
font-size: 30px;
}
</style>
</body>
</html>
指令
1.v-text
作用
在元素中输入内容,无法解析 html 字符串,类似innerText。
简写
{{ 值 }}
{{ }} :差值语法,写在标签文本中。
值 :一句话表达式,包括:变量、基本运算、三元表达式。
例🌰子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<div v-text="msg">今天在下雨<p>123</p></div> <!-- 我的大刀早已饥渴难耐了! -->
<div v-text="'姓名:'+obj.name"></div> <!-- 姓名:蛮王 -->
<div v-text="1+1"></div> <!-- 2 -->
<div v-text="true?1:0"></div> <!-- 1 -->
<div v-text=""></div>
<div>哈哈!{{msg}}</div> <!-- 哈哈!我的大刀早已饥渴难耐了! -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
msg: "我的大刀早已饥渴难耐了!",
obj: {
name: "蛮王"
}
},
})
</script>
</body>
</html>
页面预览:
2.v-html
作用
在元素中输入内容,可解析富文本,类似 innerHTML。
富文本:带标签的字符串。
例🌰子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<div v-html="msg"></div>
<div v-text="msg"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
msg: "<h1>剑圣</h1>"
},
})
</script>
</body>
</html>
页面预览:
3.v-model
作用
表单元素数据的双向绑定。
表单元素:input、textarea、select 等。
语法
v-model=“变量”
例🌰子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model="msg">
<p>您输入了:{{msg}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
msg: "我的大刀早已饥渴难耐了!"
},
})
</script>
</body>
</html>
页面预览:
4.v-on
作用
事件的绑定。
语法
v-on:事件名=“一段简短的 js 或者 方法”
简写
@
例🌰子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<button v-on:click="num++">点我加1</button>
<button v-on:dblclick="num++">双击加1</button>
<button v-on:mouseover="num++">移入加1</button>
<button v-on:click="add">点我加1</button>
<p>你点我了{{num}}下</p>
<!-- 简写:@ -->
<button @click="num++">点我加1</button>
<button @dblclick="num++">双击加1</button>
<button @mouseover="num++">移入加1</button>
<button @click="add">点我加1</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
num: 0
},
methods: {
// add:function(){ }
add() {
if (this.num < 5) {
this.num++;
}
}
}
})
</script>
</body>
</html>
页面预览:
html 里面访问
data 属性
和methods 中的方法
都是不需要加 this的。
v-on的修饰符
@keyup.按键名/键值="事件执行代码"
按了某键后才会触发事件。
@事件名.stop="事件执行代码"
阻止冒泡。
@事件名.prevent="事件执行代码"
阻止默认事件。
例🌰子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<style>
.box1 { width: 500px; height: 500px; background-color: #f00; }
.box2 { width: 100px; height: 100px; background-color: #0f0; }
</style>
<body>
<div id='app'>
<!-- 键盘事件: -->
<input type="text" @keyup.32="search">
<!-- 阻止冒泡事件 -->
<div class="box1" @click="box1Click">
box1
<div class="box2" @click.stop="box2Click">box2</div>
</div>
<div>{{msg}}</div>
<!-- 阻止默认事件 -->
<a href="http://www.baidu.com" @click.prevent.stop="aClick">点我啊</a>
</div>
<script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
<script>
new Vue({
el: '#app',
data: {
msg: "hello"
},
methods: {
search() { alert("ok") },
box1Click() { alert(1) },
box2Click() {
// e.stopPropagation();
alert(2)
},
aClick(e) {
e.preventDefault();
this.msg = "123"
}
}
})
</script>
</body>
</html>
5.v-bind
作用:给元素绑定属性。
语法
- 基础用法:
v-bind:属性名=“属性值” - 对象用法:
v-bind:class=“{类名:结果为布尔值的一句话表达式}”
简写
- 基础用法:
:属性名=“属性值” - 对象用法:
:class=“{类名:结果为布尔值的一句话表达式}”
对象用法,动态渲染标签的属性:true,使用该属性;false,不使用该属性。
例🌰子:
- 基础用法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id='app'>
<button @click="changeSrc">点击修改</button>
<!-- <img v-bind:src="imgSrc" v-bind:title="txt" v-bind:xxx="str" alt=""> -->
<!-- 简写 -->
<img :src="imgSrc" :title="txt" :xxx="str" alt="">
</div>
<script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
<script>
new Vue({
el: '#app',
data: {
str: "ooo",
txt: "123",
imgSrc: "http://ossweb-img.qq.com/images/lol/web201310/skin/big517000.jpg"
},
methods: {
changeSrc() {
this.txt = "456"
this.imgSrc = "http://ossweb-img.qq.com/images/lol/web201310/skin/big518000.jpg"
}
}
})
</script>
</body>
</html>
- 对象用法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<style>
.box { width: 300px; height: 300px; background-color: #f00; transition: all 0.5s; }
.box.active { width: 100px; height: 100px; background-color: #0f0; }
</style>
<body>
<div id='app'>
<button @click="bol=!bol">点我啊</button>
<!-- :class="{class类名:boolean (true:使用该class,flase:不使用)}" -->
<div class="box" :class="{active:bol}"></div>
</div>
<script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
<script>
new Vue({
el: '#app',
data: {
bol: true
}
})
</script>
</body>
</html>
v-bind 切换样式
1、两个样式的切换:
html
<div class="info_tab">
<div class="info_tab_order" :class="{clicked: isShow}"
@click="isShow = ! isShow">我的订单</div>
<div class="info_tab_business" :class="{clicked: isShow == false}"
@click="isShow = ! isShow">商家信息</div>
</div>
js
data(){
return{
isShow: true,
}
}
css
.clicked {
border-bottom: 1px solid #fe5c02;
color: #fe5c02;
}
效果 :
2、多个样式的切换:
html
<div>
<div :class="{serviceClicked: isShow == 0}" @click="isShow = 0">我的订单</div>
<div :class="{serviceClicked: isShow == 1}" @click="isShow = 1">商家信息</div>
<div :class="{serviceClicked: isShow == 2}" @click="isShow = 2">特殊服务</div>
</div>
js
data(){
return{
isShow: 0,
}
}
css
.serviceClicked {
color: #fff;
background-color: #b1090a;
}
v-bind 渲染 v-for
1. 静态
效果图:
关键代码:
- html:
:class="item.backgroundColor"
- css:
.bg0 { ... } .bg1 { ... } .bg2 { ... } .bg3 { ... }
完整代码:
<template>
<view class="finish">
<u-row class="topContent">
<u-col span="6" v-for="(item, index) in topContent" :key="index">
<view
class="flex align-center justify-around textContent"
:class="item.backgroundColor"
>
<view class="flex texts">
<text class="title">{{ item.title }}</text>
<text class="subtitle">{{ item.subtitle }}</text>
</view>
<u-image
:width="item.width"
:height="item.height"
:src="item.img"
mode="aspectFit"
>
</u-image>
</view>
</u-col>
</u-row>
</view>
</template>
<script>
export default {
data() {
return {
topContent: [
{
title: "项目管理",
subtitle: "施工监督,全程把控",
img: "../../static/img/finishRegulate.png",
width: "54rpx",
height: "71rpx",
backgroundColor: "bg0",
},
{
title: "创意设计",
subtitle: "自由设计,美感舒适",
img: "../../static/img/finishDesign.png",
width: "68rpx",
height: "72rpx",
backgroundColor: "bg1",
},
{
title: "商品采购",
subtitle: "轻松逛店,拼团采购",
img: "../../static/img/finishPurchase.png",
width: "62rpx",
height: "68rpx",
backgroundColor: "bg2",
},
{
title: "劳务派遣",
subtitle: "专业上岗,工匠服务",
img: "../../static/img/finishDispatch.png",
width: "60rpx",
height: "72rpx",
backgroundColor: "bg3",
},
],
};
},
};
</script>
<style lang="scss">
/* ==装修== */
.finish {
.topContent {
border-top: 1rpx solid #dcdcdc;
padding: 26rpx 11rpx 8rpx;
background-color: #fff;
.textContent {
height: 134rpx;
border-radius: 14rpx;
margin-bottom: 18rpx;
.texts {
flex-direction: column;
.title {
color: #020202;
font: 500 30rpx/1 "Source Han Sans CN";
margin-bottom: 20rpx;
}
.subtitle {
color: #605f5f;
font: 500 22rpx/1 "Source Han Sans CN";
}
}
}
.bg0 {
background-color: #dadbef;
}
.bg1 {
background-color: #d9f4f6;
}
.bg2 {
background-color: #fff2f0;
}
.bg3 {
background-color: #e7f3ff;
}
}
}
</style>
2. 动态
效果图:
声明一个字段 step
用来存储当前点击的索引,判断它与 v-for
循环的索引 index
是否一致,一致则渲染指定 css
:
<view class="flex justify-between stepWords">
<text
v-for="(item, index) in stepList"
:key="index"
class="stepWord"
:class="{ stepWordNow: step == index }"
>{{ item }}</text>
</view>
<script>
export default {
data() {
return {
step: 0, // 当前步骤
stepList: ["房屋信息", "整屋详情", "商品推荐"], // 步骤描述
};
},
};
</script>
<style lang="scss" scoped>
.stepWords {
width: 606rpx;
height: 36rpx;
margin: 0 auto;
.stepWord {
color: #bdbdbe;
font: 400 24rpx/1 "Source Han Sans CN";
}
.stepWordNow {
color: #00aa90;
font: 400 24rpx/1 "Source Han Sans CN";
}
}
</style>
番外:
比如:删除数据就无法监听,需要 $delete 等 API 辅助才能监听到。
6.v-for
作用
可以用来遍历数据(数组,对象)。
语法
v-for=“(item,index) in 数组”
item :数组每一项。
index :当前索引。
数据会实时更新,无须我们再操作,数组长度有多长就执行多少次。
例🌰子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul id="ul"></ul>
<script>
let arr = [1, 2, 3, 4, 5]
let str = ""
// for (var i = 0; i < arr.length; i++) {
// str += "<li>" + arr[i] + "</li>"
// }
arr.forEach((item, index) => {
str += "<li>" + item + "</li>"
});
document.getElementById('ul').innerHTML = str;
arr.push(6);
</script>
<div id='app'>
<button @click="add">添加数组</button>
<button @click="del">删除数组</button>
<ul>
<li v-for="(item, index) in list" :key="index" @click="alertEvent(index)"> {{item}}-----索引:{{index}} </li>
</ul>
</div>
<script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
<script>
new Vue({
el: '#app',
data: {
list: ["剑圣", "蛮王", "牛头"]
},
methods: {
add() {
this.list.push("我是添加项");
},
del() {
this.list.pop();
},
alertEvent(index) {
alert(index);
}
}
})
</script>
</body>
</html>
7.v-if,v-else-if,v-else
作用
语法
v-if=“boolean值”
若为
true
,就会渲染该标签;若为false
,就不会渲染该标签。
例🌰子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id='app'>
<input type="text" v-model="score">
<p v-if="score>80">优秀</p>
<p v-else-if="score>60">一般</p>
<p v-else>不行</p>
</div>
<script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
<script>
new Vue({
el: '#app',
data: {
score: 100
}
})
</script>
</body>
</html>
8.v-show
作用
可以控制元素的显示与隐藏,仅仅只是将元素通过设置 display:none
语法
v-show=“结果为布尔值的一句话表达式”
若为
true
,则显示该标签;若为false
,则隐藏该标签,原理为添加display:none
。
例🌰子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id='app'>
<button @click="bol=!bol">点我啊</button>
<div v-show="bol" key="">
<span>用户名</span>
<input type="text" placeholder="用户名">
</div>
<div v-show="!bol">
<span>密码</span>
<input type="text" placeholder="密码">
</div>
</div>
<script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
<script>
new Vue({
el: '#app',
data: {
bol: true
}
})
</script>
</body>
</html>
v-if
动态渲染标签 。
v-show
动态控制标签的隐藏/显示。
动画
当vue中,显示隐藏,创建移除,一个元素或者一个组件的时候,可以通过transition实现动画。
如果元素或组件离开,完成一个淡出效果:
<transition name="fade">
<p v-if="show">100</p>
</transition>
.fade-enter-active {
transition: all .2s ease;
}
.fade-leave-active {
transition: all .2s cubic-bezier(1.0, .5, .8, 1.0);
}
.fade-enter,
.fade-leave-to {
opacity: 0;
transform: translateX(20px);
}
- 进入(显示,创建)
v-enter 进入前 (vue3.0 v-enter-from)
v-enter-active 进入中
v-enter-to 进入后 - 离开(隐藏,移除)
v-leave 进入前 (vue3.0 v-leave-from)
v-leave-active 进入中
v-leave-to 进入后
多个transition使用不同动画,可以添加nam属性,name属性的值替换v即可。
计算属性
1.场景:
依赖某个值或多个值变化产生一个新的值。
2.本质:
是一个函数,值为他 return 的结果,要当作属性去使用它。
3.书写位置:
写在 vue 中的 computed 对象里。
4.特点:
不改变 data 中的数据,且能实时响应 data 中的数据变化。
5.语法:
computed: {
计算属性名字(){
return 需要返回的结果
},
}
- 例🌰1: (getNum 用在标签文本中)
<!-- // html: -->
<div id='app'>
<p>原价:{{num}}</p>
<p>现价:{{getNum}}</p>
</div>
// js:
data: {
num: 100
},
computed: {
getNum() {
return this.num * 0.5
}
}
- 例🌰2:(getList 用在标签属性中)
<!-- // html: -->
<div id='app'>
<input type="text" v-model="score">
<ul>
<li v-for="(item, index) in getList" :key="index">{{item}}</li>
</ul>
</div>
// js:
data: {
score: 0,
list: [1, 2, 3, 4, 5]
},
computed: {
getList() {
let temp = []
for (var i = 0; i < this.list.length; i++) {
if (this.list[i] > this.score) {
temp.push(this.list[i])
}
}
return temp
}
}
- 例🌰3:(计算属性传参)
<view>{{ changeCreateDate(data.update_date) }}</view>
<script>
import { change_time } from "@/config/date.js";
export default {
data(){},
computed: {
changeCreateDate: () => {
return (timestamp) => change_time(timestamp)
},
},
}
</script>
解读:
filters 过滤器
1.作用:
对数据进行过滤处理。
2.分类:
1. 局部过滤器:在当前组件中定义,适用于当前组件。
2. 全局过滤器:在 main.js 中定义,所有组件都可用。
3.局部过滤器:
语法:
<!-- // html: -->
<div>{{ 待过滤的值 | 过滤器名字 }}</div>
// js:
export default {
filters: {
过滤器名字(待过滤的值) {
return 返回值
}
},
}
- 例🌰1: 基本使用
<!-- // html: -->
<div>{{ item.duration | formatTime }}</div>
// js:
export default {
filters: {
formatTime(value) {
return Math.floor(value/1000/60) + ":" + Math.ceil( (value/1000) % 60 )
}
},
}
- 例🌰2: 使用 data 中的值
filters
中的 this 指向undefined
,因此不能通过正常的this.xxx
来使用data
中的值。可以通过传参的方式来解决这个问题。
<!-- // html: -->
<div>{{ item.duration | formatTime(info) }}</div>
// js:
export default {
filters: {
formatTime(value, info) {
if (info == true){
return Math.floor(value/1000/60) + ":" + Math.ceil( (value/1000) % 60 )
} else {
return Math.floor(value/1000/60)
}
}
},
data(){
info: true
}
}
- 例🌰3:
<!-- // html: -->
<el-table-column show-overflow-tooltip prop="idNumber" label="证件号" width="200">
<template slot-scope="scoped">
<span>{{ scoped.row.idNumber | idNumberFilter }}</span>
</template>
</el-table-column>
<el-table-column show-overflow-tooltip prop="reservedPhone" label="银行预留手机号" width="200">
<template slot-scope="scoped">
<span>{{ scoped.row.reservedPhone | reservedPhoneFilter }}</span>
</template>
</el-table-column>
// js:
<script>
export default {
filters: {
/**
* @description:证件号码过滤,隐藏部分身份证号
* @param {String} val 证件号码
* @return:idNumberFilter
*/
idNumberFilter(val) {
return (
val
?.toString()
// .replace(val?.toString().substr(4, 10), '**********')
.replace(/(.{4}).*(.{4})/, '$1**********$2')
)
},
/**
* @description:手机号过滤,隐藏部分手机号
* @param {String} val 手机号
* @return:reservedPhoneFilter
*/
reservedPhoneFilter(val) {
return val?.toString().replace(/(.{3}).*(.{4})/, '$1****$2')
},
},
}
</script>
效果图:
4.全局过滤器:
// 写在 new Vue 前面
Vue.filters("过滤器名字",function(待过滤的值){
return 返回值
})
watch 侦听器
1.作用:
侦听某个数据,若该数据发生变化,则立刻调用相关函数。
2.语法:
// js:
export default {
watch: {
// oldValue:没修改前的数据; newValue:修改后的数据
// oldValue 和 newValue 都可省略
"要侦听的数据名": function(newValue,oldValue){
// 这里执行数据变动后的处理
}
},
}
- 例🌰子:
<!-- // html: -->
<div>{{ msg }}</div>
// js:
export default {
data(){
return {
msg: 123,
}
},
watch: {
"msg": function(newValue, oldValue){
// 这里执行数据变动后的处理:
console.log("newValue:", newValue);
console.log("oldValue:", oldValue);
}
},
}
immediate 选项
默认情况下,组件在初次加载完毕后不会调用watch 侦听器。如果想让watch 侦听器立即被调用,则需要使用 immediate 选项
watch: {
// 1. 监听 username 值的变化
username: {
// 2. 当 username 变化时,调用 handler
async handler(newVal, oldVal) {
const { data: res } = await axios.get(`https://www.escook.cn/api/finduser/${newVal}`)
console.log(res)
},
// 3.组件加载完毕后立即调用一次当前的 watch 侦听器
immediate: true
}
}
deep 选项
当 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项。使用方法同 immediate。
Iscroll 滚动条插件
1.导包:
iscroll.js。
2.布局:
三层布局。
- 例🌰子:
<!-- // html: -->
<button @click="add">添加</button>
<button @click="del">删除</button>
<!-- // 三层布局: -->
<div class="wrapper" ref="wrapper">
<ul>
<li v-for="(item, index) in list" :key="index">{{item+index}}</li>
</ul>
</div>
// js:
data: {
myIscroll: "",
list: ["测试数据", "测试数据", "测试数据", "测试数据", "测试数据", "测试数据"]
},
methods: {
add() {
// 数组最后面添加一条数据
this.list.push("我是新加的")
// $nextTick 它帮我们计算好 dom 最近的一次更新
//它就是 setTimeout 的高级版本,相当于帮我们计算好了数据渲染到页面的具体时间
this.$nextTick(() => {
this.myIscroll.refresh()
})
},
del() {
// 删除数组里面最后一项
this.list.pop()
// 数据修改了,执行一次刷新
this.$nextTick(() => {
this.myIscroll.refresh()
})
}
},
mounted() {
this.myIscroll = new IScroll(this.$refs.wrapper, {
mouseWheel: true, //鼠标滚轮控制
scrollbars: true //滚动条的显示
})
},
/* css: */
.wrapper {
width: 300px;
height: 150px;
border: 1px solid red;
margin: 0 auto;
overflow: hidden; /* 去掉原始默认滚动条 */
position: relative; /* 加入定位处理,为了 iscroll 滚动条相对于该标签 */
}
moment 时间插件
1.安装:
npm i moment
2.导包:
import moment from "moment"
3.作用:
- 获取当前时间
- 毫秒数 转换为 正常时间
// 获取当前时间 (24小时制):
moment().format("YYYY年MM月DD日 HH:mm:ss")
// 获取当前时间 (12小时制):
moment().format("YYYY-MM-DD hh:mm:ss")
// 毫秒数转换:
moment(毫秒数).format("YYYY/MM/DD HH:mm:ss")
HH 表示 24 小时制 ; hh 表示 12小时制。
时间格式是可以自定义哦!比如2020年08月08日
或2020/08/08
或2020-08-08
或2020😄08😘08🐷
都是可以的哦!
时间格式转换
时间戳转换为日期格式:
封装到 date.js
文件中:
/**
* @param {Number} timeStamp 传入的时间戳,如:1647014072845
* @param {Number} startType 要返回的时间字符串的格式类型,如:2022-03-17 22:35:29
*/
export let change_date = (timestamp) =>{
function time(timestamp = +new Date()) {
var date = new Date(timestamp + 8 * 3600 * 1000); // 增加8小时
return date.toJSON().substr(0, 19).replace('T', ' ');
}
return time();
}
引入(路径为自己的文件路径):
import { change_date } from "@/config/date.js";
使用:
console.log(change_date (1647014072845)) // 2022-03-17 22:39:11
单(多)元素动画
1.前提:
实现动画只能在 `v-show` 和 `v-if` 上。
- 例🌰子:(单元素动画)
<!-- // html: -->
<div id='app'>
<transition name="xxx">
<div id="xx" v-if="bol"></div>
</transition>
</div>
/* css: */
/* 正常状态: */
.xx {
transition: all 2s;
}
/* 进入/离开动画的执行体: */
.xxx-enter-active,
.xxx-leave-active {
transition: all 2s;
}
/* 进入状态: */
.xxx-enter {
transform: translateX(-200px);
}
/* 离开状态: */
.xxx-leave-to {
transform: translateX(200px);
}
- 例🌰子:(多元素动画)
<!-- // html: -->
<div id='app'>
<transition-group name="xxx">
<div class="box" v-if="bol" key="1"></div>
<div class="box" v-if="bol" key="2"></div>
</transition-group>
</div>
/* css 同 单元素动画 */
2.keyframes 实现动画:
- 例🌰子:
<!-- // html 同 单元素动画: -->
/* css: */
.xx {
animation: move 2s;
}
.xxx-enter-active,
.xxx-leave-active {
animation: move 2s;
}
@keyframes move {
0% {
/* 动画内容, 例: */
opacity: 0
}
100% {
opacity: 1
}
}
Vuex 基本使用
1.作用:
共享数据管理。解决复杂关系的组件间的传值问题。
2.安装:
npm i vuex
3.语法
// main.js 中:
// 1.导入:
import Vuex from 'vuex'
// 2.注册:
Vue.use(Vuex)
// 3.实例化:
const store = new Vuex.Store({
state: {
参数名: "共享数据"
}
})
// 4.注入:
new Vue({store}).$mount('#app')
访问:
this.$store.state.参数名
修改:
this.$store.state.参数名 = "新数据"
以上的修改方法不规范,有可能会报错。
规范修改见下面的 抽取的1和3:
4.抽取
- 新建:
src/store/index.js
文件,文件中的代码如下:
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
info: "老板给我来碗忘情牛肉面",
},
getters: {
info(state) { return info },
},
mutations: {
setInfo(state, info){ state.info= info },
},
actions: {},
modules: {},
})
export default store
- 在 main.js 中
import store from './store/index.js'
new Vue({
store, //store:store
render: h => h(App),
}).$mount('#app')
- 在要用到数据的组件中:
// 获取 vueX 值:
this.$store.getters.info
// 修改 vueX 值:
this.$store.commit("setInfo", "给我一杯忘情水")
5. 升级版抽取
将路由的文件按照 模块(modules) 拆分,方便路由管理与合作开发。
5.1.第一种模块化写法
5.1.1. 创建测试模块1 src/store/modules/eat.js
:
/**
* @author mygoes mynameiszhayu@163.com
* @description 测试模块1:关于吃的东西
*/
const state = { foods: [] }
const getters = {
foods: (state) => state.foods,
}
const mutations = {
addFood(state, food) {
state.foods.push(food)
},
clearFood: (state) => {
state.foods.splice(0)
},
}
const actions = {
addFood({ commit }, food) {
commit('addFood', food)
},
clearFood({ commit }) {
commit('clearFood')
},
}
export default { state, getters, mutations, actions }
5.1.2. 创建测试模块2 src/store/modules/play.js
:
/**
* @author mygoes mynameiszhayu@163.com
* @description 测试模块2:关于玩的东西
*/
const state = { toy: '', game: false }
const getters = {
toy: (state) => state.toy,
game: (state) => state.game,
}
const mutations = {
setToy(state, toy) {
state.toy = toy
},
setGame(state, game) {
state.game = game
},
}
const actions = {
setToy({ commit }, toy) {
commit('setToy', toy)
},
setGame({ commit }, game) {
commit('setGame', game)
},
}
export default { state, getters, mutations, actions }
5.1.3. 导入所有 vuex 模块,不建议修改 src/store/index.js
:
/**
* @author mygoes mynameiszhayu@163.com
* @description 导入所有 vuex 模块,自动加入namespaced:true,用于解决 vuex 命名冲突,请勿修改。
*/
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const files = require.context('./modules', false, /\.js$/)
const modules = {}
files.keys().forEach((key) => {
modules[key.replace(/(modules|\/|\.|js)/g, '')] = {
...files(key).default,
namespaced: true,
}
})
Object.keys(modules).forEach((key) => {
modules[key]['namespaced'] = true
})
const store = new Vuex.Store({
modules,
})
export default store
5.1.4. 导入store main.js
:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
import store from '@/store/index.js';
new Vue({
render: h => h(App),
store
}).$mount('#app')
5.1.5. 在页面中使用:
<template>
<div>
<button @click="setVuex">点击存储到vuex</button>
<div>{{ toy }}</div>
<div>游戏否?{{ game }}</div>
<div v-for="(item, index) in food" :key="index">
第{{ index }}个{{ item }}
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
// 获取vuex
computed: {
...mapGetters({
toy: "play/toy",
game: "play/game",
food: "eat/foods",
}),
},
methods: {
// 修改vuex
setVuex() {
this.$store.dispatch("play/setToy", "乒乓");
this.$store.dispatch("play/setGame", !this.game);
this.$store.dispatch("eat/addFood", "西瓜");
},
},
};
</script>
备注:第一种模块化写法 在 某次 使用 HBuilderX 的运行为 H5 时,有
set
报错,于是更新了 第二种模块化写法 。
5.2.第二种模块化写法
5.2.1. 创建测试模块1 src/store/modules/eat.js
:
/**
* @author mygoes mynameiszhayu@163.com
* @description 测试模块1:关于吃的东西
*/
const state = { foods: [] }
const mutations = {
ADD_FOOD(state, data) {
state.foods.push(data)
},
CLEAR_FOOD: (state) => {
state.foods.splice(0)
},
}
const actions = {
addFood({ commit }, data) {
commit('ADD_FOOD', data)
},
clearFood({ commit }) {
commit('CLEAR_FOOD')
},
}
export default { namespaced: true, state, mutations, actions }
5.2.2. 创建测试模块2 src/store/modules/play.js
:
/**
* @author mygoes mynameiszhayu@163.com
* @description 测试模块2:关于玩的东西
*/
const state = { toy: '', game: false }
const mutations = {
SET_TOY(state, toy) {
state.toy = toy
},
SET_GAME(state, game) {
state.game = game
},
}
const actions = {
setToy({ commit }, data) {
commit('SET_TOY', data)
},
setGame({ commit }, data) {
commit('SET_GAME', data)
},
}
export default { namespaced: true, state, mutations, actions }
5.2.3. 简化 getters 获取 src/store/getters.js
:
/**
* @author mygoes mynameiszhayu@163.com
* @description 简化 getters 获取 ,使用代码: computed: { XXX() { return this.$store.getters.XXX }}
*/
const getters = {
foods: state => state.eat.foods,
toy: state => state.play.toy,
game: state => state.play.game,
}
export default getters
近期在使用
this.$store.getters.XXX
获取 vuex 中的数据时,发现获取到的数据不是最新的。
未避免大家遇到类似的问题,建议大家在获取 vuex 数据的时候,使用以下写法:
this.$store.state.
找到对应的数据。
(例如:找到 user 模块中的 age:this.$store.state.play.game)
---- 更新于:2022/3/25
5.2.4. 导入所有 vuex 模块,不建议修改 src/store/index.js
:
/**
* @author mygoes mynameiszhayu@163.com
* @description 导入所有 vuex 模块,请勿修改。
*/
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)
// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
getters
})
export default store
5.2.5. 导入store main.js
:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
import store from '@/store/index.js';
new Vue({
render: h => h(App),
store
}).$mount('#app')
5.2.6. 在页面中使用:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button @click="setVuex">点击存储到vuex</button>
<div>{{ toy }}</div>
<div>游戏否?{{ game }}</div>
<div v-for="(item, index) in foods" :key="index">
第{{ index }}个{{ item }}
</div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
},
// 获取vuex
computed: {
toy: {
get() {
return this.$store.state.play.toy;
},
set(val) {
this.$store.dispatch("play/setToy", val);
},
},
game: {
get() {
return this.$store.state.play.game;
},
set(val) {
this.$store.dispatch("play/setGame", val);
},
},
/**
* @description 简化 getters 获取。但在 HBuilderX 的 H5 中可能会报错,如报错,改为上面的写法即可。
*/
foods() {
return this.$store.getters.foods;
},
},
methods: {
// 修改vuex
setVuex() {
this.$store.dispatch("play/setToy", "乒乓");
this.$store.dispatch("play/setGame", !this.game);
this.$store.dispatch("eat/addFood", "西瓜");
},
},
};
</script>
导航守卫
1.定义:
在路由切换时,组件还未加载时,会执行的回调函数。
2.语法:
// src/router/index.js 中:
// 导入路由:
import VueRouter from "vue-router";
import store from "@/store/index.js"; // 导入仓库
// 注册:
Vue.use(VueRouter);
// 创建路由对象:
const router = new VueRouter({
routes: []
});
router.beforeEach((to, from, next) => {
// window.console.log(to);
// window.console.log(from);
// window.console.log(next);
// next() 必须执行。
});
// 暴露出去:
export default router;
// main.js 中:
// 导入路由对象
import router from './router/index.js'
new Vue({
render: h => h(App),
// 挂载 注入 Vue实例:
router,
store // 挂载仓库对象
}).$mount('#app')
插槽
作用:在封装组件时,可以通过 <slot>
元素定义插槽,从而为用户预留内容占位符
基础使用
<template>
<p>这是 MyCom 组件的 p 标签</p>
<!-- 通过 slot 标签(插槽)为用户预留内容的占位 -->
<slot></slot>
<p>这是 MyCom 组件的 p 标签</p>
</template>
<my-com>
<p>~~用户自定义的内容~~</p>
</my-com>
如果在封装组件时没有预留任何 <slot>
插槽,则用户提供的任何自定义内容都会被丢弃
具名插槽
如果在封装组件时需要预留多个插槽节点,则需要为每个 <slot>
插槽指定具体的 name 名称。这种带有具体名称的插槽叫做“具名插槽”
为具名插槽提供内容:
作用域插槽
定义:为预留的 <slot>
插槽绑定 props 数据,这种带有 props 数据的 <slot>
叫做“作用域插槽”
声明:
使用:
自定义指令
概述:vue 官方提供了 v-for、v-model、v-if 等常用的内置指令,除此之外 vue 还允许开发者自定义指令
私有自定义指令
声明:
directives: {
// 定义一个私有指令
focus: {
// 自动触发 mounted 函数
mounted(el){
// 被绑定的元素自动获得焦点
el.focus()
}
}
}
使用:
<!-- 使用自定义指令时,需要加上 v- 指令前缀 -->
<input v-focus />
全局自定义指令
const app = Vue.createApp({})
// 注册一个全局的自定义指令 v-focus
app.directive('focus', {
mounted(el){
el.focus()
}
})
简写:
app.directive('focus', ()=> {
// 在 mounted 和 updated 时都会触发相同的业务处理
el.focus()
})
指令的参数
在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值
<!-- 使用 v-color 时,可以通过等号绑定指令的值 -->
<input type="text" v-model.number="count" v-focus v-color="'red'">
<p v-color="'cyan'">{{ count }}</p>
<button @click="count++">+1</button>
// 自定义 v-color 指令
app.directive('color', (el, binding) => {
// binding.value 是通过等号为指令绑定的值
el.style.color = binding.value
})
深/浅拷贝:
深拷贝:值的拷贝。类似于复制粘贴
浅拷贝:地址的拷贝(公用一个数据)。类似于创建桌面快捷方式
生命周期:
vue2.x
-
beforeCreate(创建前)
时期:创建 data 和 methods 前。
特点:不能访问 data 中的属性和 methods 中的方法。
应用:loading 事件。 -
created(创建后)
特点:能访问 data 中的属性和 methods 中的方法,不能访问 vue 渲染后的 DOM。
应用:结束 loading 事件;url 取值;初始化;实现函数的自执行。
特殊含义:最早访问 data 和 methods 的生命周期钩子。 -
beforeMount(渲染前)
特点:不能访问 vue 渲染后的 DOM。
应用:基本无用。 -
mounted(渲染后)
应用:vue 中的方法集合。
特殊含义:最早访问 vue 渲染后的 DOM 的生命周期钩子。 -
beforeUpdate(更新前)
时期:data 中的数据更新前(数据已修改,但还没完成 DOM 渲染)。
应用:基本无用。
特点:多次执行。 -
updated(更新后)
时期:data 中的数据更新后(数据已修改,且已完成 DOM 渲染)。
应用:基本无用。
特点:多次执行。 -
beforeDestroy(销毁前)
特点:能访问 data,methods 和 vue 渲染后的 DOM。
应用:善后工作(清理定时器等)。 -
destroyed(销毁后)
特点:能访问 data,methods,不能访问 vue 渲染后的 DOM。
应用:善后工作(清理定时器等)。
vue3.x
axios
- 作用:发请求
- 特点:只做接口请求(体积小,加载快,功能多)
- 兼容:不支持老 IE 浏览器(Vue 也不支持老 IE 浏览器)
语法:
1. get 请求:
axios.get("url 路径", { params: { 请求参数:值 } })
.then(res => { console.log(res) })
.catch(err => {console.log(err) })
// 简写: params 可直接串到 url 路径上.
2. post 请求:
axios.post("url 路径", { 请求参数1: 值1, 请求参数2: 值2 })
.then(res => { console.log(res) })
.catch(err => {console.log(err) })
3. config 配置:
axios({
url: "请求路径",
method: "请求方法",
// params: { get请求参数: 值 }, // 发 get 请求时
data: { post请求参数: 值 }, // 发 post 请求时
})
.then(res => { console.log(res) })
.catch(err => {console.log(err) })
戳这:一篇发请求的文章
vue 3.x 中全局配置 axios
在 main.js 入口文件中,通过 app.config.globalProperties 全局挂载 axios
// 为 axios 配置请求的根路径
axios.defaults.baseURL = 'http://api.com'
// 将 axios 挂载为 app 的全局自定义属性
app.config.globalProperties.$http = axios
在组件中使用时,可以通过 this 直接访问到全局挂载的自定义属性
this.$http.get('/users')
this.$http.get('/profile')
this.$http.post('/news')
组件的基本使用
全局组件
- 注册
import { createApp } from 'vue'
import App from './App.vue'
// 1.导入 Swiper 和 Test 两个组件
import Swiper from './components/MySwiper.vue'
import Test from './components/MyTest.vue'
const app = createApp(App)
// 2.调用 app 实例的 component() 方法,在全局注册 my-swiper 和 my-test 两个组件
app.component('my-swiper', Swiper)
app.component('my-test', Test)
app.mount('#app')
- 使用
// 在 main.js 中,注册了 my-swiper 和 my-test 两个全局组件
sap_app.component('my-swiper', Swiper)
sap_app.component('my-test', Test)
<!-- 在其他组件中,直接以标签的形式,使用刚才注册的全局组件即可 -->
<template>
<h1>这是 App 根组件</h1>
<hr/>
<my-swiper></my-swiper>
<my-test></my-test>
</template>
局部组件
- 注册
<template>
<h1>这是 App 根组件</h1>
<hr/>
<my-swiper></my-swiper>
<my-search></my-search>
</template>
<script>
import Search from './components/MySearch.vue'
export default {
components: {
'my-search': Search,
}
}
</script>
特点:
被全局注册的组件,可以在全局任何一个组件内使用
被局部注册的组件,只能在当前注册的范围内使用
应用场景:
如果某些组件在开发期间的使用频率很高,推荐进行全局注册;
如果某些组件只在特定的情况下会被用到,推荐进行局部注册。
升级版的定时器:
this.$nextTick(() => {
// do something...
});