VUE 核心
1 开始
1 引入Vue后自动定义了一个全局变量Vue
2 每个Vue应用都是通过Vue函数创建一个新的Vue实例开始的
3 {{里面可用表达式语句}}
4 Vue.config.productionTip=false //阻止 vue 在启动时生成产生提示
2 Vue使用简单举例
<div id="app">
{{message}}
</div>
<script type="text/javascript">
var app=new Vue({
el:'#app', //el用于指定元素(#id)
data:{ //data用于申明变量,初始化赋值
message:'Hello Vue!'
}
});
</script>
app.$mount('#root')//第二种绑定元素的写法,更加灵活
3 绑定样式
标签中用 v-bind:属性=“值” 用变量动态设置属性值,简写 :属性
1 字符串写法
用于:样式的类名不确定,需要动态指定
<body>
<div id="root">
<div class="basic" :class="mood" @click="change">test</div>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
mood:'normal'
},
methods:{
change(){
const arr=['normal','sad','happy'];
const x=Math.floor(Math.random()*3) //Math.floor() 为向下取整
this.mood=arr[x]
}
}
})
</script>
<style>
.basic{}
.normal{}
.sad{}
.happy{}
</style>
2 数组写法
用于:要绑定的样式个数不确定、类名也不确定,可动态修改数组中的类
<body>
<div id="root">
<div class="basic" :class="moodArr" @click="change">test</div>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
moodArr:['normal','sad','happy']
}
})
</script>
<style>
.basic{}
.normal{}
.sad{}
.happy{}
</style>
3 对象写法
用于:要绑定的样式个数、类名确定,可动态确定对象中的样式是否开启使用
<body>
<div id="root">
<div class="basic" :class="moodObj" @click="change">test</div>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
moodObj:{
normal:false,
sad:false
}
}
})
</script>
<style>
.basic{}
.normal{}
.sad{}
.happy{}
</style>
4 指令
https://cn.vuejs.org/v2/guide/syntax.html#%E6%8C%87%E4%BB%A4
1 v-once
标签中加入则{{message}}后续不会被改变
2 v-html
标签中加入 v-html=“变量名” 可将变量编译为html元素而非字符串
<div id="app">
<p>没加v-html输出的rawHtml:{{rawHtml}}</p>
<!-- 加了v-html会将该标签内容清空,改为变量内容 -->
<p v-html="rawHtml">加了v-html输出的rawHtml:{{rawHtml}}</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
rawHtml: '<span style="color:red">rawHtml</span>'
}
})
</script>
3 v-text
- 标签中使用,标签内容会渲染成 v-text=“…” 中的变量值,若标签中本来也内容,会直接将其替换掉
- 若变量内容为标签节点,它也不会解析
4 v-clock
- 在标签中加入 v-clock ,配合 css,可避免网速过慢导致 vue 未接管时页面显示出未经 vue 处理的插值语法
- v-clock 在 vue 接管后会自动消失
<h1 v-clock>{{msg}}</h1>
[v-clock]{display:none}
5 v-pre
- 可以跳过 vue 对该节点的处理,标签中写什么就出现什么
- 该指令常用在静态节点中,加快页面的编译速度
6 自定义指令
注:
-
指令命名如遇多单词:元素中用 - 分隔单词(v-big-number),函数中用完整形式(单驼峰单引号包裹)
(‘bigNumber’,(element,binding){})
-
自定义指令函数中的 this 为 window 而非 vm
-
在某个 vm 中写的指令只能用于该 vm,需要定义全局指令则写在 vue 实例外面
Vue.directive('fbind',{ bind(element,binding){ element.value=binding.value }, inserted(element,binding){ element.focus() }, update(element,binding) { element.value=binding.value } })
1 函数式
- element 参数为真实 DOM 元素
- binding 参数中包含被绑定元素的各个关键参数,最常用到的是其 value,value 值为指令中的变量值
- 指令所在的模板(所在的 div#root)被重新解析指令会调用,即 data 有任何一个被修改,指令都会调用
<body>
<div id="root">
原始n的值:<span v-text="n"></span><br><br>
经v-big处理后方法十倍:<span v-big="n"></span><br><br>
<button @click="n++">n++</button>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
n:1
},
directives:{
big(element,binding){
element.innerText=binding.value*10
}
}
})
</script>
2 对象式
- 此为完整写法
- bind() 为指令与元素绑定时;insert() 为元素插入页面时;update() 为指令所在的模板(所在的 div#root)被重新解析时
此处例子为实现页面加载后 input 自动获得焦点
<body>
<div id="root">
<input type="text" v-fbind:value="n">
<button @click="n++">n++</button>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
n:1
},
directives:{
fbind:{
bind(element,binding){
element.value=binding.value
},
inserted(element,binding){
element.focus()
},
update(element,binding) {
element.value=binding.value
},
}
}
})
</script>
5 函数申明在methods中
1 静态
<div id="app">
<span v-bind:class="spanClass" @click.stop="click">点我变红</span>
<!-- .stop后缀防止事件冒泡 -->
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
spanClass:'spanClass'
},
methods:{
click:function(){
vm.spanClass='spanClassClicked';
}
}
})
</script>
<style>
.spanClassClicked{
color: red;
}
</style>
2 动态
用v-bind:class动态改变样式,即上代码可改为
<div id="app">
<div v-bind:class="{active:isActive}"
style="width: 100px; height: 40px;"
@click.stop="click">点我变红</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
isActive:false
},
methods:{
click:function(){
vm.isActive=true
}
}
});
</script>
<style>
.active{
background-color: red;
color: #fff;
}
</style>
6 条件渲染
https://cn.vuejs.org/v2/guide/conditional.html
1 v-show
会将元素一直留在页面中,改变其true/false只是改变display:none
判断中可使用表达式
<div v-show="false">test v-show</div>
2 v-if | v-else-if | v-else
若判断为false会直接将元素消除
包含这三个判断的元素结构需要在一起,结构不允许被破坏
<body>
<div id="root">
<div v-if="n===1">a</div>
<div v-else-if="n===2">b</div>
<div v-else>n={{n}}</div>
<button @click="n++">点击+1</button>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
n:0
}
})
</script>
template 标签结合 v-if 使用,页面初始化后 template 标签会自动消失
<body>
<div id="root">
<button @click="n++">点击+1</button>
<template v-if="n===2">
<div>使用template,此时n=2</div>
</template>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
n: 0
}
})
</script>
7 列表
1 列表渲染
- 渲染对象数组,index 为索引,取值从 0 开始
- 渲染对象,index 为对象属性名
<body>
<div id="root">
<ul>
<!-- 以对象自带的标识做 key 可避免比较算法导致的问题 -->
<li v-for="(p,index) in personList" :key="p.id">
{{p.name}} ---- {{p.age}} --- {{p.id}}
<input type="text">
</li>
</ul>
<button @click="add">在列表头添加</button>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
personList:[
{id:"001",name:'张三',age:18},
{id:"002",name:'李四',age:20},
{id:"003",name:'王五',age:24}
]
},
methods: {
add(){
const p={id:"004",name:"老刘",age:40}
this.personList.unshift(p)
//unshift() 为在对象数组头部添加
}
}
})
</script>
2 列表过滤
eg: 模糊搜索
<body>
<div id="root">
<input type="text" placeholder="请输入查询姓名" v-model="keyWord">
<ul>
<li v-for="(p,index) in filPersonList" :key="p.id">
{{p.name}} ---- {{p.age}} --- {{p.id}}
</li>
</ul>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
personList: [
{ id: "001", name: '马冬梅', age: 18 },
{ id: "002", name: '周冬雨', age: 20 },
{ id: "003", name: '周杰伦', age: 24 },
{ id: "004", name: '温兆伦', age: 22 }
],
keyWord: ''
},
computed: {
filPersonList() {
return this.personList.filter((p) => p.name.indexOf(this.keyWord) != -1)
}
}
})
</script>
3 列表排序
<body>
<div id="root">
<input type="text" placeholder="请输入查询姓名" v-model="keyWord">
<button @click="sortType=2">年龄升序</button>
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=0">原顺序</button>
<ul>
<li v-for="(p,index) in filPersonList" :key="p.id">
{{p.name}} ---- {{p.age}} --- {{p.id}}
</li>
</ul>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
personList: [
{ id: "001", name: '马冬梅', age: 28 },
{ id: "002", name: '周冬雨', age: 20 },
{ id: "003", name: '周杰伦', age: 24 },
{ id: "004", name: '温兆伦', age: 22 }
],
keyWord: '',
sortType: 0 // 0 原顺序/ 1 降序/ 2 升序
},
computed: {
filPersonList() {
const arr = this.personList.filter((p) => p.name.indexOf(this.keyWord) != -1)
if (this.sortType) {
arr.sort((p1, p2) => this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age)
}
return arr
}
}
})
</script>
8 事件处理
1 事件绑定
v-on | @
<div id="app">
<button v-on:click="handleClick('abc',$event)">{{msg}}</button>
//v-on可简写为@ =》v-on:click == @click
//$event即告诉函数要传个event参数过去
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: 'goood'
},
methods: {
handleClick(str, e) {//e表示当前节点
vm.msg = "yeeaaahhhhh";//或者this.msg
}
}
});
</script>
2 事件修饰符
.prevent 阻止元素的默认行为的触发
<a href="www.bing.com" @click.prevent="handleClick"></a>
//可阻止 <a></a> 的默认跳转行为
.stop 阻止事件冒泡
.once 事件只触发一次
修饰符可叠加,要注意修饰符的顺序
3 按键绑定
<body>
<div id="root">
<input @keyup.enter="entered" type="text">
//keyup 为按键按下抬起后触发
</div>
</body>
<script>
new Vue({
el:'#root',
methods:{
entered(e){
console.log(e.target.value)
}
}
})
</script>
<div id="root">
常用按键:
- enter 回车
- delete 删除(删除[Delete] 和 退格[Backspace] 都可触发)
- esc 退出
- space 空格
- tab 换行(由于本身具有切换焦点的功能,需配合@keydown使用 @keydown.tab)
- up 方向键上
- down 下
- left 左
- right 右
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvXdb4QM-1661758418338)(C:\Users\Gucci\AppData\Roaming\Typora\typora-user-images\image-20220222103147285.png)]
9 表单绑定
-
v-model 双向绑定,即一处改变,另一处随之改变
-
可在 data 申明时设置默认值
-
v-model 修饰符
- .trim 去掉前后空格
- .number 一般结合 type=“number” 使用,data中对应属性读取为数值型
- .lazy 失去焦点后 data 才收集输入内容
-
data 中与表单对应的
- text 框——字符串
- 单选——字符串,标签中配置 value
- 复选
- 配置 value (多项选择)——数组,标签中配置 value
- 不配置 value (是否同意协议)——字符串,收集为布尔值:勾选为 true;不勾选为 false
- 下拉菜单——字符串,标签中配置 value
<div id="app">
<!-- text 和 textarea 元素使用 value property 和 input 事件;
checkbox 和 radio 使用 checked property 和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件。 -->
<input v-model="msgForInput" placeholder="此为输入框" @input="input" />
<p>输入框内容:{{msgForInput}}</p>
<!-- 复选按钮组 -->
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>选中的: {{ checkedNames }}</span>
<br>
<!-- 单选按钮 -->
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>选中的: {{ picked }}</span>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msgForInput: '',
checkedNames: ["Jack"],
picked:'Two'
},
methods: {
input: function () {
console.log(this.msgForInput);
}
}
});
</script>
10 数据代理
1 HTML中数据代理
<script>
let person={
name:"gucci",
gender:"male",
}
Object.defineProperty(person,"age",{
value:20,
enumerable:true, //是否可以枚举(是否可以被遍历读出来)
writable:true, //是否可以修改
configurable:true //是否可以被删除
//上述3个参数默认值为 false
})
</script>
<script>
let num=20
let person={
name:"gucci",
gender:"male",
}
Object.defineProperty(person,"age",{
//当age被读取时,get函数(getter)就会被调用
get(){
return num
},
//当age被修改时,set函数(setter)就会被调用
set(value){
num=value //由此也可实现变量和对象属性的双向绑定
}
})
</script>
2 VUE中数据代理
VUE中直接完成了数据代理的复杂操作
11 计算属性
用 data 中已有的属性进行计算得到新的属性
<body>
<div id="root">
<input type="text" v-model:value="firstName">
<input type="text" v-model:value="lastName"><br /><br />
<span>姓名:{{fullName}}</span>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
firstName: "张",
lastName: "三"
},
computed: {
fullName: {
get() {
return this.firstName + '-' + this.lastName
},
set(value) {
const arr = value.split('-');//以 - 拆分字符串为字符数组
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>
当确定不需要setter时可简写(大部分时候都不需要setter)
computed: {
fullName(){
return this.firstName + '-' + this.lastName
}
}
12 监视属性
1 监视一般属性
用于监视属性是否被改变,改变时做出操作
计算属性也可被监视
<body>
<div id="root">
<h3>天气 {{info}}</h3>
<button @click="change">点击切换天气</button>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽';
}
},
methods: {
change() {
this.isHot = !this.isHot;
}
},
watch: {
isHot: {
immediate:true, //初始化时调用一次handler,默认为false
handler(newValue, oldValue) {
console.log("isHot被修改", newValue, oldValue)
}
}
}
})
</script>
或将watch写在vm配置外面
vm.$watch('isHot', {
immediate: true, //初始化时调用一次handler,默认为false
handler(newValue, oldValue) {
console.log("isHot被修改", newValue, oldValue)
}
})
2 监视多级结构属性
<body>
<div id="root">
<h3>a:{{numbers.a}}</h3>
<button @click="numbers.a++">点击+1</button>
<h3>b:{{numbers.b}}</h3>
<button @click="numbers.b++">点击+1</button>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
numbers:{
a:1,
b:1
}
},
watch: {
//监视多级结构中某个属性
'numbers.a': {
handler(newValue, oldValue) {
console.log("numbers.a被修改", newValue, oldValue)
}
},
//监视多级结构是否被修改(需要开启深度监听 | 其中一个属性被修改也算)
numbers:{
deep:true, //深度监听,默认false
handler(newValue, oldValue) {
console.log("numbers被修改", newValue, oldValue)
}
}
}
})
</script>
3 简写
确认不需要 immediate 和 deep 时可简写
watch: {
isHot(newValue, oldValue){
console.log("isHot被修改", newValue, oldValue)
}
}
vm.$watch('isHot', function(newValue, oldValue){
console.log("isHot被修改", newValue, oldValue)
})
13 生命周期
1 挂载流程
1 beforeCreate()
vm 将要创建了,此时数据 data 还未放入实例 vm 中
2 created()
vm 完成创建,数据放入 vm
3 beforeMount()
vm 将要挂上页面解析模板去处理 DOM
4 mounted()
- Vue 完成模板的解析并把初始的真实 DOM 元素放入页面(挂载完成)
- mounted 中的 this 为 vm
2 更新流程
1 beforeUpdate()
data 数据更新时调用,此时数据是新的,但页面还没有刷新
2 updated()
data 数据更新完成调用,此时数据和页面都是新的
3 销毁流程
1 beforeDestroy()
使用了 vm.$destroy() 后,vm 被销毁之前调用,此时 vm 中的所有东西包括 data、methods 等等都还可以使用,但调用了 methods 中的方法页面不会再更新了
2 destroyed()
几乎没用
API
1 Vue.set()
- 在已配置过的 vm 中添加新对象属性,也可用于修改数组中的值
- 被添加的对象不能为 vm 或 vm 的根数据对象,需是 data 中已有的对象
- 添加入的新属性自动完成数据代理,响应式
<body>
<div id="root">
<h3>姓名:{{stu.name}}</h3>
<h3>年龄:{{stu.age}}</h3>
<h3 v-if="stu.gender">性别:{{stu.gender}}</h3>
<button @click="addGender">添加性别</button>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
stu:{
name:'Gucci',
age:20
}
},
methods: {
addGender(){
Vue.set(this.stu,'gender','男')
}
},
})
</script>
2 数组监测
因为 Vue 不会为数组的内容自动生成 getter 和 setter ,所以对数组内容添加、修改需要使用如下七个数组函数才能实现响应式,触发视图更新,亦可使用 Vue.set()
-
push(val) 末尾添加元素 val
-
pop() 删除最后一个元素
-
shift() 删除第一个元素
-
unshift(val) 开头添加元素 val
-
splice()
表达式 arr.splice(index, num, item1, item2, …);
参数说明
第一个参数为 一个整数,用来指定添加/删除元素的位置,可以用负数来从尾部开始数 必填参数
第二个参数为 删除元素的数量,若不想删除可以设置为0 可选参数
再后面的参数为 向数组添加的元素 可选参数
如果是删除操作,那么会把删除的元素放在一个新数组中返回。1.操作的元素会包括开始的元素
2.如果数组的元素不够,会一直删除到数组的最后一位
-
sort(a,b) 排序,a-b 升序 | b-a 降序
-
reverse() 反转数组
附
1 filter() 函数
- 筛选符合条件的元素组成新的数组并返回
- x 为数组中的各个元素,返回值为对 x 的判断条件,返回值为筛选过后的新数组
- 不会修改原数组
var 数组 = 数组.filter(function(x){return 条件})
2 => 箭头函数
- 可将函数体简单的函数写为箭头函数
var f1 = () => 12;
function f1(){return 12;}
//----------------------
var f2 = x => x * 2;
function f2(x){return x*2;}
//----------------------
var f3 = (x, y) => {
var z = x * 2 + y;
y++;
x *= 3;
return (x + y + z) / 2
};
function f3(x,y){
var z = x * 2 + y;
y++;
x *= 3;
return (x + y + z) / 2
}
3 sort() 函数
- 对数组排序,对进行操作的数组直接修改,不返回新数组
- a-b 为升序
- b-a 为降序
数组.sort(function(a,b){return a-b})
4 禁止页面滑动
禁止左右滑动
<meta name="viewport" content="width=device-width,maximum-scale=1.0,initial-scale=1,user-scalable=no">
禁止左右滑动
通常用于盒子的大 > 视角框(即手机、PC屏幕大小)
body {
overflow:hidden;
}
5 webStorage 本地存储
localStorage
浏览器存在本地,浏览器关闭后也不会消失
存 / 改
- 只能存字符串
- 要存对象,value 处用一下 JSON.stringify(obj)
localStorage.setItem('key','value')
取
- 要取对象,将取出的对象用 JSON.parse(obj) 变回来
- 读不存在的,读出来为 null
localStorage.getItem('key','value')
删
localStorage.removeItem('key')
清空
localStorage.clear()
sessionStorage
其 API 同上
浏览器关闭后数据清空
VUE 组件化
定义:实现应用中局部功能代码(css、js、html)和资源的集合
基础使用
组件使用三步骤:
- 创建组件
- 注册组件
- 使用组件
注:
-
组件名为多单词,使用 - 连接,注册时使用完整写法,使用时也用 - 表示
component:{ 'my-school':school }
<my-school></my-school>
-
不要使用 html 已有的标签
-
VueComponent 组件实例对象,简称 vc
1 非单文件组件(不常用)
<body>
<div id="root">
<!-- 第三步:使用组件 -->
<school></school>
<hr>
<student></student>
</div>
</body>
<script>
//第一步:注册school、student组件
const school = Vue.extend({
//模板 html 内容必须要 div 包起来
template: `
<div>
<h3>学校名称:{{name}}</h3>
<h3>学校地址:{{address}}</h3>
<button @click="show">显示学校名</button>
</div>
`,
data(){
return{
name:'西华大学',
address:'四川成都'
}
},
methods: {
show(){
alert(this.name)
}
},
})
const student=Vue.extend({
template:`
<div>
<h3>学生姓名:{{name}}</h3>
<h3>学生年龄:{{age}}</h3>
</div>
`,
data(){
return{
name:'张三',
age:20
}
}
})
new Vue({
el: '#root',
components:{//第二步:注册组件(局部注册)
school,student
/* 完整写法
'school'(组件标签命名):school(组件),
'student':student
*/
}
})
</script>
全局注册:
所有的 vm 都会注册上这个组件
Vue.component('组件的命名',创建组件的名字)
2 组件嵌套
注:
- 子组件需要再父组件之前创建
- 实际开发中需要一个最顶层的组件 app 来管理之下所有组件,vm 只注册管理 app
- 所有的多标签组件必须有一个根元素(div)包裹!!!!!!
eg:student 为 school 的子组件
<body>
<div id="root">
</div>
</body>
<script>
const student = Vue.extend({
template: `
<div>
<h3>学生姓名:{{name}}</h3>
<h3>学生年龄:{{age}}</h3>
</div>
`,
data() {
return {
name: '张三',
age: 20
}
}
})
const school = Vue.extend({
template: `
<div>
<h3>学校名称:{{name}}</h3>
<h3>学校地址:{{address}}</h3>
<student></student>
</div>
`,
data() {
return {
name: '西华大学',
address: '四川成都'
}
},
components: {
student
}
})
const hello = Vue.extend({
template: `<h1>欢迎来到西华大学</h1>`,
})
//最顶层的组件 app
const app = Vue.extend({
template: `
<div>
<hello></hello>
<school></school>
</div>
`,
components: {
hello, school
}
})
new Vue({
el: '#root',
template: `<app></app>`,
components: { app },
})
</script>
VUE 脚手架 (CLI)
单文件组件
开始
下载新版 vue/cli
npm install -g @vue/cli
vue --version //查看cli版本
- 打开存放项目的文件夹
- 在文件夹中以管理员打开 cmd
- vue create 项目名(小写)
- 读条完了选择 vue2
- 用 vscode 打开项目文件夹
- 删除 HelloComponent.vue
- 防止出现组件命名或语法报错,在 vue.config.js 中加入一句 lintOnSave: false
1 基础
- 编写的组件存放在 components 文件夹
- 创建 .vue 文件,输入 <v 快速生成结构
- 模板必须要一个根元素(div)包裹!!!
- 每个组件需要 name 属性命名,最好与文件名一致,首字母大写
- 组件在 App.vue 中导入、注册、使用
eg:
School.vue
<template>
<div>
<h3>学校名称:{{name}}</h3>
<h3>学校地址:{{address}}</h3>
<button @click="show">显示名称</button>
</div>
</template>
<script>
export default {
name:'School',
data(){
return{
name:'西华大学',
address:'四川成都'
}
},
methods:{
show(){
alert(this.name)
}
}
}
</script>
Student.vue
<template>
<div>
<h3>学生姓名:{{name}}</h3>
<h3>学生年龄:{{age}}</h3>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return{
name:'张三',
age:20
}
}
}
</script>
App.vue
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
import School from './components/School.vue';
import Student from './components/Student.vue';
export default {
name:'App',
components:{
School,Student
}
};
</script>
2 ref 属性
用法:
this.$refs.ref
- 用在组件模板的标签上
- 对于 html 元素是 id 属性的平替
- 对于组件标签,可以获得该组件的 vc,便于组件间通信
<template>
<div>
<Student ref="stu"></Student>
<button @click="showStu">获得Student vc</button>
</div>
</template>
<script>
import Student from "./components/Student.vue";
export default {
name: "App",
components: { Student },
methods: {
showStu() {
console.log(this.$refs.stu);
},
},
};
</script>
3 prop属性
让组件接收外部传来的数据
1 传递数据
在组件标签被使用的地方将要传的数据作为属性写入标签
<Student name="xxx"></Student>
2 接收数据
接收后可直接在组件模板中使用
(1) 方式一:只接收(常用)
props:['name']
(2) 方式二:限制类型
props:{
name:String
}
(3) 方式三:限定类型、限制必要性、指定默认值
props:{
name:{
type:String,
required:true,
default:'张三'
}
}
注:一般不要对 props 读到的数据直接进行修改,如需修改,在 data 中定义后,在模板中使用 data 中的数据
data:{
return{
myName:this.name
}
}
4 mixin 混入
可把多个组件都有的配置项提取写入 mixin.js 文件中一个对象
eg:两个组件都有 show 函数
(1)第一步:定义混入
mixin.js
export const mix={
methods: {
show(){
console.log(this.name);
}
},
}
(2)第二步:使用混入
a 局部混入
Student.vue 和 School.vue 中导入 mix 并使用
import { mix } from "../mixin";
mixins: [mix]
b 全局混入
在 mian.js 中导入后,使用 API 使所有组件都混入 mix
Vue.mixin(mix)
5 插件
用于增强 vue
plugins.js 中为一个包含 install 函数的一个对象,其第一个参数为 Vue,第二个参数可以是插件使用者自己要传入的数据
定义插件
plugins.js
export default {
install(Vue,options){
//添加全局过滤器
Vue.filter()
//添加全局自定义指令
Vue.directives()
//配置全局混入
Vue.mixin()
//添加实例方法
Vue.prototype.$myMethod=function(){}
}
}
使用插件
导入
import plugin from './plugins'
使用:一定写在 new Vue({}) 之前
Vue.use(plugin)
6 scoped
在组件的 style 标签上写上 scoped,则样式只会作用于该组件,不会影响其他组件
一般用于处理不同组件样式重名
7 TodoList (通用组件化编码流程)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBeLVPox-1661758418340)(C:\Users\Gucci\AppData\Roaming\Typora\typora-user-images\image-20220307112255730.png)]
-
组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
1).一个组件在用:放在组件自身即可。
2). 一些组件在用:放在他们共同的父组件上(状态提升)。
(3).实现交互:从绑定事件开始。
-
props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
-
使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
-
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
8 自定义事件
可通过自定义事件实现子向父传递数据
eg:自定义事件 name,Student 组件将姓名传给 App
App.vue
方法一:直接绑定
<template>
<Student @name="getName"/>
</template>
<script>
import Student from './components/Student.vue'
export default {
name:'App',
components:{Student},
methods:{
getName(n,...params){
console.log('App从子组件获得姓名',n,params);
}
}
}
</script>
方法二:通过 ref 在生命周期钩子里绑定,用法更灵活
<template>
<Student ref="student"/>
</template>
<script>
import Student from './components/Student.vue'
export default {
name:'App',
components:{Student},
methods:{
getName(n,...params){
console.log('App从子组件获得姓名',n,params);
}
},
mounted(){
this.$refs.student.$on('name',this.getName)
}
}
</script>
Student.vue
<template>
<div>
<h3>学生姓名:{{ name }}</h3>
<h3>学生年龄:{{ age }}</h3>
<button @click="sendName">获得姓名</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return{
name:'张三',
age:20
}
},
methods:{
sendName(){
//触发Student组件实例身上的name事件,同时可以传值
this.$emit('name',this.name,param1,param2)
}
}
};
</script>
解绑
this.$off('demo') //解绑一个
this.$off(['demo1','demo2']) //解绑多个
this.$off() //解绑所有
销毁实例对象
this.$destroy()
9 全局事件总线
任意组件间通信
1 安装全局事件总线
new Vue({
beforeCreate(){
Vue.prototype.$bus=this //$bus就是当前应用的vm
}
})
2 使用
**接收:**A 组件要接收数据,在 A 中给 $bus 绑定自定义事件,事件回调也写在 A 自身
- 字符串为事件名,各个事件名不要重复
- data 为其他组件调用该事件传给 A 的数据
- 若传来多个数据,参数 (…params) 接收为 params[] 数组
mounted(){
this.$bus.$on('事件名',(data)=>{})
}
**解绑:**最好在接收数据的组件的 beforeDestroy 钩子中解绑用到的事件
- 若要解绑多个,this. b u s . bus. bus.off(‘xxx’) 就多写几个
beforeDestroy(){
this.$bus.$off('xxx')
}
**调用/提供:**B 组件要调用 A 绑定的自定义事件,数据写在后面
this.$bus.$emit('事件名',data)
10 消息订阅与发布
用于任意组件间通信
**安装pubsub-js:**管理员打开项目控制台
npm i pubsub-js
**引入:**在发送、接收的组件中都要引入
import pubsub from 'pubsub-js'
**接收(订阅):**回调函数参数一为消息名,参数二为接收的参数
mounted(){
this.pubId=pubsub.subscribe('hello',(msgName,data)=>{
console.log('有人发布了hello消息,回调调行',data);
})
}
解绑:
beforeDestroy(){
pubsub.unsubscribe(this.pubId)
}
提供(发布):
pubsub.publish('hello',param)
11 nextTick
在下一次 DOM 更新结束后执行其指定的回调
用于当数据改变后,要基于更新后的新 DOM 进行某些操作时,要在 nextTick所指定的回调函数中执行
this.$nextTick(function(){})
什么时候用:
- created() 钩子函数进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中:原因是在 created() 钩子函数执行的时候 DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,并且不用 nextTick 会报错
- 更改数据后当想立即使用js操作新的视图的时候
12 动画 / 过渡
1 动画
(1)第一步:定义动画
@keyframes xhu {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0px);
}
}
(2)第二步:将要写动画的元素用 包裹,并为其命名
<transition name="hello" appear>
<h1 v-show="isShow">你好啊</h1>
</transition>
(3)第三步:使用动画
进入
.hello-enter-active {
animation: xhu .5s;
}
离开
.hello-leave-active {
animation: xhu .5s reverse;
}
完整代码:
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition name="hello" appear>
<h1 v-show="isShow">你好啊</h1>
</transition>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
isShow: true,
};
},
};
</script>
<style scoped>
h1 {
background-color: orange;
}
.hello-enter-active {
animation: xhu .5s;
}
.hello-leave-active {
animation: xhu .5s reverse;
}
@keyframes xhu {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0px);
}
}
</style>
2 过渡
不需要写关键帧 @keyframe,模板同动画一致
h1 {
background-color: orange;
}
/* 进入的起点 *//* 离开的终点 */
.hello-enter,.hello-leave-to {
transform: translateX(-100%);
}
.hello-enter-active,.hello-leave-active{
transition: .2s;
}
/* 进入的终点 *//* 离开的起点 */
.hello-enter-to,.hello-leave {
transform: translateX(0);
}
3 多个元素需要同一效果
其中的每个元素都要一个唯一的 key
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1">你好啊</h1>
<h1 v-show="isShow" key="2">你好啊</h1>
</transition-group>
4 集成第三方动画
(1)安装 animate.css
管理员打开项目 cmd
npm install animate.css
(2)导入
import 'animate.css'
(3)使用
在要动画的元素的 <trasition(-group)> 上命名:
name="animate__animated animate__bounce"
在官网挑选动画,并复制其类名并使用
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
13 配置代理
1 方式一
- 只能配置一个代理
- 若请求的东西的名字本地存在,就不会走代理,直接拿本地的用
(1)下载 axios
npm i axios
(2)请求服务器的组件导入 axios,地址为当前服务器端口:端口号后写请求要的东西
axios.get('http://localhost:8080/students').then(
response=>{
console.log('请求成功',response.data);
},
error=>{
console.log('请求失败了',error.message);
}
)
(3)开启代理服务器:在 vue.config.js 中加入,地址为目标服务器端口
devServer: {
proxy: 'http://localhost:5000'
}
2 方式二
- 可以配置多个代理
- 可灵活选择是走代理,还是用本地资源
- changeOrigin 默认为 true,为 true 目标服务器收到的地址为目标服务器地址;为 false 则受到本地地址
(1)配置代理
devServer:{
proxy:{
'/xhu':{//匹配所有以 '/xhu' 开头的请求路径
target:'http://localhost:5000',
pathRewrite:{'^/xhu':''},
ws:true, //用于支持websocket
changeOrigin:false //用于控制请求头中的host值
},
'/demo':{//匹配所有以 '/demo' 开头的请求路径
target:'http://localhost:5001',
pathRewrite:{'^/demo':''},
ws:true, //用于支持websocket
changeOrigin:false //用于控制请求头中的host值
},
}
}
(2)请求:端口号为本地端口,在端口号后写需要的请求头
getStudents(){
axios.get('http://localhost:8080/xhu/students').then(
response=>{
console.log('请求成功',response.data);
},
error=>{
console.log('请求失败了',error.message);
}
)
}
14 slot 插槽
- 在组件模板的某个位置写上 ,即可在组件被使用时组件体内写具体结构,写的结构会被放在 slot 的位置
- slot 内部可写默认文字,当没有结构传入时显示
- 写的具体结构的样式写在使用处的组件内,或本体组件内都可以
1 默认插槽
- 结构要使用的数据在使用者的内部
eg:同一组件内传入不同的具体结构
组件:Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<slot>slot的默认值,使用者没传具体结构时出现</slot>
</div>
</template>
<script>
export default {
name: "Category",
props:['title']
};
</script>
<style>
.category{
background-color: skyblue;
width: 200px;
}
h3{
text-align: center;
background-color: orange;
}
</style>
使用处:App.vue
<template>
<div class="container">
<Category title="美食">
<img src="./assets/3.jpg" alt="" />
</Category>
<Category title="游戏">
<ul>
<li v-for="(g, index) in games" :key="index">
{{ g }}
</li>
</ul>
</Category>
<Category title="电影">
<video src="http://clips:vorwaerts-gmbh.de/big_duck_bunny.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from "./components/Category.vue";
export default {
name: "App",
components: { Category },
data() {
return {
foods: ["火锅", "烧烤", "啤酒", "小龙虾", "牛扒"],
games: ["守望先锋", "英雄联盟", "原神", "我的世界"],
films: ["教父", "触不可及", "肖申克"],
};
},
};
</script>
<style scoped>
.container {
display: flex;
justify-content: space-around;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
2 具名插槽
- 可以把不同的结构放在不同的 slot 中
- 在模板中为 slot 命名: name=“xxx”
- 使用时注明结构放在哪个 slot: slot=“name”
- 结构用 template 包裹,可用 v-slot:name
eg:为上例多加点结构
组件:Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<slot name="center">center</slot>
<slot name="footer">footer</slot>
</div>
</template>
使用处:App.vue
<template>
<div class="container">
<Category title="美食">
<img slot="center" src="./assets/3.jpg" alt="" />
<a slot="footer" href="http://www.bing.com">更多美食</a>
</Category>
<Category title="游戏">
<ul slot="center">
<li v-for="(g, index) in games" :key="index">
{{ g }}
</li>
</ul>
<div slot="footer" class="foot">
<a href="http://www.bing.com">单机游戏</a>
<a href="http://www.bing.com">网络游戏</a>
</div>
</Category>
<Category title="电影">
<video
slot="center"
controls
src="http://clips:vorwaerts-gmbh.de/big_duck_bunny.mp4"
></video>
<template v-slot:footer>
<div class="foot">
<a href="http://www.bing.com">classic</a>
<a href="http://www.bing.com">trend</a>
<a href="http://www.bing.com">recommend</a>
</div>
<h4 style="text-align: center">欢迎观影</h4>
</template>
</Category>
</div>
</template>
3 作用域插槽
- 要使用的数据在被使用的组件内部
- slot 绑定 :xxx=“xxx”,将数据传给使用者
- 使用者中的具体结构要用 template 包裹,使用 scope=“xxx” 接收从被使用组件传来的数据,也可使用 slot-scope=“xxx”,接收到的 xxx 为一个对象,使用时 xxx.xxx
- 也可给插槽命名,template 中写明用那个插槽
eg:使用被使用组件的数据
组件:Category.vue
<template>
<div class="category">
<h3>{{ title }}分类</h3>
<slot :games="games"></slot>
</div>
</template>
使用者:App.vue
<template>
<div class="container">
<Category title="游戏">
<template scope="xhu">
<ul>
<li v-for="(g, index) in xhu.games" :key="index">
{{ g }}
</li>
</ul>
</template>
</Category>
<Category title="游戏">
<template scope="xhu">
<ol>
<li v-for="(g, index) in xhu.games" :key="index">
{{ g }}
</li>
</ol>
</template>
</Category>
<Category title="游戏">
<template slot-scope="xhu">
<h4 v-for="(g, index) in xhu.games" :key="index">
{{ g }}
</h4>
</template>
</Category>
</div>
</template>
VUEX
用于多组件共享同一状态(数据)
开始
vue2 只能用 vuex3
(1)在 src 中创建 store 文件夹,包含 index.js
(2)index.js 中创建 store
//该文件用于创建vuex核心的store
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
Vue.use(Vuex)
//准备actions:用于响应组件中的动作
const actions={}
//准备mutations:用于操作数据(state)
const mutations={}
//准备state:用于存储数据
const state={}
//创建store
export default new Vuex.Store({
actions,
mutations,
state,
})
(3)main.js 中导入并使用
...
import store from './store/index'
new Vue({
...
store,
...
}).$mount('#app')
求和案例
效果如图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rGCeNsVI-1661758418341)(C:\Users\Gucci\AppData\Roaming\Typora\typora-user-images\image-20220317162958188.png)]
- 组件模板中使用数据 {{$store.state.xxx}}
- 组件脚本中
- 需要有业务逻辑,则调用 actions 用 this.$store.dispatch(‘事件名(小写)’,参数)
- 无业务逻辑,只需修改数据,则调用 mutations 用 this.$store.commit(‘事件名(大写)’,参数)
- actions 相当于 Serve 层,处理业务逻辑、调用 Dao 层方法
- 方法有 context、value 两个参数,context 中包含 commit、state 等需要用的,value 为组件传过来的参数
- mutations 相当于 Dao 层,只包含数据修改方法
- 方法有 state、value 两个参数,state 中为数据,value 为 actions 或组件调用传来的值
index.js
//准备actions:用于响应组件中的动作
//业务逻辑写在actions
const actions={
jiaOdd(context,value){
if(context.state.sum%2){
context.commit('JIA',value)
}
},
jiaTime(context,value){
setTimeout(() => {
context.commit('JIA',value)
}, 500);
},
}
//准备mutations:用于操作数据(state)
//mutations只进行数据操作
const mutations={
JIA(state,value){
state.sum+=value
},
JIAN(state,value){
state.sum-=value
}
}
//准备state:用于存储数据
const state={
sum:0, //当前和
}
Count.vue
<template>
<div>
<h1>当前求和:{{ $store.state.sum }}</h1>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
methods: {
increment() {
this.$store.commit("JIA", this.n);
},
decrement() {
this.$store.commit("JIAN", this.n);
},
incrementOdd() {
this.$store.dispatch("jiaOdd", this.n);
},
incrementWait() {
this.$store.dispatch("jiaTime", this.n);
},
},
1 getters
vuex 里面的 computed
(1)创建
//准备getters:用于将state的数据加工
const getters={
bigSum(state){
return state.sum*10
}
}
//创建store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
(2)调用
$store.getters.bigSum
2 mapXXX
1 mapState 和 mapGetters
借助这两个生成计算属性,分别从 state / getters 中读取数据
(1)导入
import { mapState,mapGetters } from "vuex";
(2)使用
注:…obj 意为将 obj 内容展开放在这句话所在的位置
1)对象写法
computed: {
...mapState({ sum: "sum", school: "school", subject: "subject" }),
...mapGetters({bigSum:"bigSum"})
}
2)数组写法:计算属性名与 state / getters 数据名一致
computed: {
...mapState(["sum", "school", "subject"]),
...mapGetters(["bigSum"]),
}
2 mapMutations 和 mapActions
借助这两个生成方法,方法中调用 commit / dispatch 去联系 mutations / actions
(1)导入
import { mapMutations,mapActions } from "vuex";
(2)使用
在模板中绑定函数时,将参数传入
@click="increment(n)"
1)对象写法
methods: {
...mapMutations({increment:'JIA',decrement:'JIAN'}),
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaTime'})
},
2)数组写法:函数名和 mutations / actions 中函数名一致
methods: {
...mapMutations(['JIA','JIAN']),
...mapActions(['jiaOdd','jiaTime'])
},
3 模块化+命名空间
(1)模块化
- 内部开启命名空间:
namespaced: true,
- index.js 内导入并命名
...
import countOptions from './count.js'
import personOptions from './person.js'
...
export default new Vuex.Store({
modules: {
countAbout: countOptions,
personAbout: personOptions
}
})
count.js
export default {
namespaced: true,
actions: {
jiaOdd(context, value) {
if (context.state.sum % 2) {
context.commit('JIA', value)
}
},
jiaTime(context, value) {
setTimeout(() => {
context.commit('JIA', value)
}, 500);
},
},
mutations: {
JIA(state, value) {
state.sum += value
},
JIAN(state, value) {
state.sum -= value
},
},
state: {
sum: 0,
school: 'xhu',
subject: 'web',
},
getters: {
bigSum(state) {
return state.sum * 10
}
}
}
person.js
import axios from "axios"
import { nanoid } from "nanoid"
export default {
namespaced: true,
actions: {
addPersonWang(context, value) {
if (value.name.indexOf('王') === 0) {
context.commit('ADD_PERSON', value)
} else
alert("这人不姓王")
},
addPersonServer(context){
axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
response=>{
context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
},
error=>{
alert(error.message)
}
)
}
},
mutations: {
ADD_PERSON(state, value) {
state.personList.unshift(value)
}
},
state: {
personList: [
{ id: '001', name: 'gucci' }
]
},
getters: {
firstPersonName(state) {
return state.personList[0].name
}
}
}
(2)组件中读取 state 数据
// 1 直接读取
this.$store.state.personAbout.personList
// 2 借助 mapState
...mapState("personAbout", ["personList"])
(3)组件中读取 getters 数据
// 1 直接读取
this.$store.getters["personAbout/firstPersonName"]
// 2 借助 mapGetters
...mapGetters("countAbout",["bigSum"])
(4)组件中调用 dispatch
// 1 直接dispatch
this.$store.dispatch('personAbout/addPersonWang',personObj)
// 2 借助 mapActions
...mapActions("countAbout",{ incrementOdd: "jiaOdd", incrementWait: "jiaTime" })
(5)组件中调用 commit
// 1 直接commit
this.$store.commit("personAbout/ADD_PERSON", personObj)
// 2 借助 mapMutations
...mapMutations("countAbout",{ increment: "JIA", decrement: "JIAN" })
VUE 路由
- 是一组 key-value 的对应关系
- 多个路由,需要经过路由器管理
- value 即为 component
单页 web 应用,只有一个完整的页面,点击连接不会刷新页面,只做局部更新,数据通过 ajax 请求
1 基本使用
- 路由组件一般放在 pages 文件夹,一般组件放在 components 文件夹
- 路由切换,是将隐藏的组件销毁,显示的组件挂载
- 每个组件都有自己的 $route,整个应用只有一个 $router
(1)安装
vue2 必须安装 @3
npm i vue-router@3
(2)配置
src 中创建 router 文件夹,创建 index.js
为了方便写路由守卫,这里写完整,路由守卫写在路由器下面,最后暴露路由器
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
import About from '../pages/About'
import Home from '../pages/Home'
//创建一个路由器
const router = new VueRouter({
routes: [
{
path:'/about',
component:About
//写法二:在路由表中导入组件
component:()=>import('../pages/About')
},
{
path:'/home',
component:Home
},
]
})
//这里写全局路由守卫
router.beforeEach((to,from,next)=>{})
//最后暴露路由器
export default router;
(3)使用
main.js 中
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
render: h => h(App),
router:router
}).$mount('#app')
(4)路由切换
router-link 会自动被浏览器编译为超链接
<!-- 实现路由的切换 -->
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
(5)组件显示
<!-- 指定组件呈现的位置 -->
<router-view></router-view>
2 嵌套路由
(1)配置
子路由前不写 “/”
routes: [
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
path:'message',
component:Message
},
]
},
]
(2)跳转
写完整路径,将父组件路由地址都要写上
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
3 路由 query 参数
(1)传递参数
<!-- 跳转路由并携带query参数,to字符串写法 -->
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link>
<!-- 跳转路由并携带query参数,to对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
(2)接收参数
$route.query.id
$route.query.title
4 命名路由
(1)给路由命名:一般用给子路由
{
path: '/home',
component: Home,
children: [
{
path: 'news',
component: News
},
{
path: 'message',
component: Message,
children: [
{
//============
name:'xiangqing',
//============
path: 'detail',
component: Detail
}
]
},
]
},
(2)简化跳转路径:path 用 name 代替
<router-link :to="{
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
5 路由 params 参数
(1)配置,声明接收 params 参数
{
name:'xiangqing',
path: 'detail/:id/:title',
component: Detail
}
(2)传递参数
对象写法,路径只能用 name
<!-- 跳转路由并携带params参数,to字符串写法 -->
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{ m.title }}</router-link>
<!-- 跳转路由并携带params参数,to对象写法 -->
<router-link :to="{
name:'xiangqing',
params:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
(3)接收参数
$route.params.id
$route.params.title
6 路由的 props 配置
让路由组件更方便收到数据
{
name: 'xiangqing',
path: 'detail',
component: Detail,
//写法一:值为对象,将对象中的key-value以props传给detail组件
props:{a:1,b:'hello'}
//写法二:值为布尔值,true:把该组件收到的params参数以props传给detail组件
props: true
//写法三:值为函数,$route为参数,params和query都可以传
props($route){
return{
id:$route.query.id,
title:$route.query.title
}
}
}
7 replace 属性
- 控制路由跳转时操作浏览器历史记录的模式
- 浏览器历史记录写入有两种:push 和 replace,push 为追加(可返回、前进), replace 为替换(不可返回、前进)
- 使用:
<router-link replace ...>About</router-link>
8 编程式路由导航
不借助 router-link 进行路由跳转
//跳转 API
this.$router.push({
name: "xiangqing",
query: {
id: m.id,
title: m.title,
},
})
this.$router.replace({
name: "xiangqing",
query: {
id: m.id,
title: m.title,
},
})
this.$router.forward() //相当于浏览器的前进
this.$router.back() //相当于浏览器的后退
this.$router.go(number)//走多少步,+为前进,-为后退
9 缓存路由组件
- 让不展示的路由组件保持挂载,不被销毁
- 可以保留用户输入
- include 中写要缓存的组件名
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
- 缓存多个:绑定 include,写成数组
<keep-alive :include="['News','Message']">
...
</keep-alive>
10 两个生命周期
- 路由组件独有,用于捕获路由组件的激活状态
activated(){console.log("组件被激活");},
deactivated(){console.log("组件失活");}
11 路由守卫
- 对路由进行权限控制
- 种类:全局、独享、组件内
1 全局守卫
- 为路由添加元数据用 meta 属性:meta:{key:value}
- to 为目标组件的信息,from 为来自组件的信息
- next() 为放行,允许路由组件跳转
//全局前置路由守卫:初始化、每次路由切换之前被调用
router.beforeEach((to, from, next) => {
if (to.meta.isAuth) {
if (localStorage.getItem('school') === 'xhu') {
next()
} else {
console.log('school name is not right');
}
} else {
next()
}
})
//后置路由守卫:初始化、每次路由切换之后被调用
router.afterEach((to, from) => {
document.title = to.meta.title || 'xhu'
})
2 独享路由守卫
- 写在路由配置中,与其他配置属性同级
- 函数前面有个 " : "
- 只有前置,可配合全局后置使用
{
...
beforeEnter: (to, from, next) => {
if (to.meta.isAuth) {
if (localStorage.getItem('school') === 'xhu') {
next()
} else {
alert('school name is not right');
}
} else {
next()
}
}
},
3 组件内
- 离开不是后置守卫
<script>
export default {
name: "About",
//通过路由规则,进入该组件时调用
beforeRouteEnter(to, from, next) {
if (to.meta.isAuth) {
if (localStorage.getItem("school") === "xhu") {
next();
} else {
alert("school name is not right");
}
} else {
next();
}
},
//通过路由规则,离开该组件时调用
beforeRouteLeave(to, from, next) {
next();
},
};
</script>
12 路由器的两种工作模式
- hash 模式:
- 地址中带 #
- 兼容性好
- 若将地址通过第三方手机app分享,若校验严格,地址会标记为不合法
- history 模式:
- 地址干净
- 兼容性略差,需要和后端配合识别 url,解决 404 问题
- 对于 url,# 及其后面的内容就是 hash 值
- hash 值不会包含在 HTTP 请求中,不会带给服务器
VUE UI 组件库
PC 端常用 UI 组件库
Element UI https://element.eleme.cn
全部引入会导致 js 文件过大,一般使用按需引入,详见官网
注:
修改 babel.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
["@babel/preset-env",{"modules":false}],
],
plugins:[
[
"component",
{
"libraryName":"element-ui",
"styleLibraryName":"theme-chalk"
}
]
]
}
1 表单验证
详见官网
- 表单标签绑定 rules 属性
- 表单 ref 属性和 :model 属性都写表单名
- data 编写 rules 规则
- 待验证的输入框 prop 接受 rules 中对应的规则
- 提交按钮绑定函数并传入表单名
- 编写提交函数(官网复制)
<el-form
:rules="rules"
ref="loginForm"
:model="loginForm"
>
<el-form-item prop="username">
<el-input></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input></el-input>
</el-form-item>
<el-form-item prop="code">
<el-input></el-input>
</el-form-item>
<el-button @click="submitLogin('loginForm')">登录</el-button
>
</el-form>
rules: {
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
},
submitLogin(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert('submit!');
} else {
this.$message.error('请输入所有字段');
return false;
}
});
},
面试题
-
Vue 不支持 jsx
-
样式穿透可用 >>>,::v-deep,/deep/
-
vue-lazyload 懒加载:延时加载,在需要用的时候再加载。一般用于 img
-
先安装 npm i vue-lazyload
-
全局注册
main.js 文件 import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload) // 配置项 Vue.use(VueLazyload, { preLoad: 1.3, error: 'dist/error.png', loading: 'dist/loading.gif', attempt: 1 })
-
使用 v-lazy 代替 :src
-
属性 :key 可以不加,但是不加可能以为 key 值相同而图片不刷新
-
-
数组直接通过索引修改属性值,不能触发 watch 方法