目录
(3)、多个元素有相同的动画(transition-group)
(11)、生命周期钩子activated、deactivated
(15)、解决history路由刷新404问题(node.js)
一、初识Vue
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>第一次使用vue</title>
<!--vue js生产版本production版本-->
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!--插值语法-->
<!--vue模版-->
<h1>hello, {{ name }}</h1>
</div>
<div class="test">
<!--注意双花括号中可以写js表达式-->
<!--
js表达式:一个表达式生成一个值,放在任何一个需要值的地方
例如 (1).a (2) 1+b (3) demo(1) (4) x===y ? 1 : 0
js语句: if while for...
-->
<h1>{{name.toUpperCase()}},{{address}} {{1+1}} {{Date.now()}}</h1>
</div>
<script type="text/javascript">
//hello小案例
Vue.config.productionTip = false; //默认不提示
//创建vue事例对象
/**
* 注意:一个vue实例不可能去接管多个容器,如果有多个容器的情况,vue事例永远只接管第一个容器
* 不能多个vue实例同时来接管同一个容器,如果有多个的情况下永远是第一个vue实例来接管该容器
* vue实例与容器直接的对应关系是一对一的
*/
new Vue({
//配置对象
el: '#root',// document.getElementById('root') //element el指定当前vue实例为哪一个容器服务,值通常为css选择器格式
data: {
//data用来存储数据,以后会用函数来表示
name:'root',
age:21
}
});
new Vue({
el: '.test',
data:{
name: 'test',
address: 'sfaef'
}
})
</script>
</body>
</html>
二、Vue模板语法({{}} / v-bind)
插值语法、指令语法
v-bind:可以简写成:
三、数据绑定(v-model)
v-model v-model:value可以简写成v-model
四、el与data的两种写法
或者直接写data(),不能写箭头函数
注意:
五、MVVM模型
六、数据代理
1、Object.defineProperty
2、Vue中的数据代理
七、事件处理(v-on: / @)
1、事件的基本使用
2、事件修饰符
3、键盘事件
4、总结
事件修饰符可以连着写
ctrl可以和其他键组合写
八、计算属性(computed)
1、完整写法
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue计算属性语法实现</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!--
计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
-->
<!--v-model双向绑定-->
姓:<input type="text" v-model="firstName"/>
<br/><br/>
名:<input type="text" v-model="lastName"/>
<br/><br/>
测试:<input type="text" v-model="test"/>
<br/><br/>
全名: <span>{{ fullName }}</span>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
//data里的都是属性
firstName: '张',
lastName: '三',
test:'test'
},
//计算属性--> 旧属性加工
computed: {
fullName: {
//get的作用,当读取fullName时get就会被调用,且返回值就做为fullName的值
//defineProperty
//注意vm._data是不存在计算属性的
//计算属性算完之后直接丢到了viewModel实例对象身上
/**
* get的调用时机
* 1.初次读取计算属性时
* 2.所依赖的数据(data中的属性)发生变化时,不改变的话直接读缓存
* 3.methods没有缓存,读几次就调用几次无论要用的数据是否发生变化
* @returns {string}
*/
get(){
//此时get函数中的this指向是vm
console.log('get调用了', this);
return this.firstName + '--' + this.lastName
},
/**
* set什么时候调用
* 1.当fullName被修改时
* @param value
*/
set(value){
//修改计算属性所依赖的普通属性(放在data里面的)
//this === vm
const [ firstName, lastName ] = value.split('-');
this.firstName = firstName;
this.lastName = lastName; //依赖属性发生改变之后,计算属性自动改变
}
}
}
});
</script>
</body>
</html>
2、简写
只getter不setter时可以使用
computed: {
//简写形式
//前提:计算属性只考虑读取不考虑修改 set丢了
//简写计算书写为一个函数(这个函数当成getter使用), 注意不要写箭头函数
//执行完getter之后,vm直接保存返回的数据为fullName属性的属性值,此时vm.fullName不是函数而是一个确切的计算值
fullName: function (){
return this.firstName + '--' + this.lastName;
}
}
九、监视属性(watch)
1、基本使用
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>天气案例_监视属性</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!--
监视属性watch:
1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
2.监视的属性必须存在,才能进行监视!!
3.监视的两种写法:
(1).new Vue时传入watch配置
(2).通过vm.$watch监视
-->
<h1>今天天气很 {{ info }}</h1>
<button @click="changeWeather">
切换
</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:'#root',
data:{
isHot: true
},
//计算属性
computed: {
info(){
return this.isHot ? '炎热' : '凉爽';
}
},
//改变模版数据的方法
methods:{
changeWeather(){
this.isHot = !this.isHot;
}
},
// watch:{
// //监视的配置对象
// //watch不仅能监视data的普通属性,也可以检测计算属性
// isHot:{
// immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
// //handler 什么时候调用呢
// //当isHot发生改变就会调用该函数
// //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
// handler(newValue, preValue){
// console.log('ishot 被修改了');
// console.log(`newValue: ${newValue}, preValue: ${preValue}`);
// }
// }
// }
});
//watch的第二种写法,直接采用vm对象实例
vm.$watch('isHot', {
immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
//handler 什么时候调用呢
//当isHot发生改变就会调用该函数
//handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
handler(newValue, preValue){
console.log('ishot 被修改了');
console.log(`newValue: ${newValue}, preValue: ${preValue}`);
}
});
</script>
</body>
</html>
2、深度监视
watch:{
//监测多级结构里面的属性 深度监视
// 'numbers.a':{
// handler(){
// console.log('a发生改变了')
// }
// }
//深度监视numbers中的所有属性
numbers:{
deep: true, //监视多级结构的属性
handler(){
console.log('numbers 发生改变')
}
}
}
3、简写
不需要immediate/deep属性时
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>天气案例_深度监视简写形式</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>今天天气很 {{ info }}</h1>
<button @click="changeWeather">
切换
</button>
<hr/>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:'#root',
data:{
isHot: true,
},
//计算属性
computed: {
info(){
return this.isHot ? '炎热' : '凉爽';
}
},
//改变模版数据的方法
methods:{
changeWeather(){
this.isHot = !this.isHot;
}
},
// watch:{
// //监视的配置对象
// //watch不仅能监视data的普通属性,也可以检测计算属性
// // isHot:{
// // //immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
// // //handler 什么时候调用呢
// // //当isHot发生改变就会调用该函数
// // //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
// // //deep: true //简写
// // handler(newValue, preValue){
// // console.log('ishot 被修改了');
// // console.log(`newValue: ${newValue}, preValue: ${preValue}`);
// // }
// // }
// //简写:
// //简写的前提watch的属性不需要immediate和deep属性的时候
// // isHot(newValue, preValue){
// // console.log(newValue,preValue);
// // }
// }
});
//完整写法
// vm.$watch('isHot', {
// deep: true,
// immediate: true,
// handler(newValue, preValue){
// console.log(newValue, preValue);
// }
// });
//简写 (代价就是不能配置其他配置项deep immediate)
vm.$watch('isHot', function (newValue, preValue){
//this === vm
console.log(newValue, preValue);
})
</script>
</body>
</html>
4、watch和computed的对比
十、绑定样式
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue绑定样式</title>
<style>
.basic{
width: 400px;
height: 100px;
border: 1px solid black;
}
.happy{
border: 4px solid red;;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg,yellow,pink,orange,yellow);
}
.sad{
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal{
background-color: skyblue;
}
.atguigu1{
background-color: yellowgreen;
}
.atguigu2{
font-size: 30px;
text-shadow:2px 2px 10px red;
}
.atguigu3{
border-radius: 20px;
}
</style>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!--
绑定样式:
1. class样式
写法:class="xxx" xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
2. style样式
:style="{fontSize: xxx}"其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象。
-->
<!--:class 绑定class样式字符串写法 适用于样式的类名不确定需要动态琢磨的状况-->
<div class="basic" :class="mood" @click="changeMood">{{ name }}</div>
<hr/>
<!--:class 绑定class样式数组写法 适用于要绑定的样式个数不确定,名字也不确定的状况-->
<div class="basic" :class="classArr">{{ name }}</div>
<hr/>
<!--:class 绑定class样式对象写法 适用于要绑定的样式个数确定,名字确定,但动态决定要不要用的状况-->
<div class="basic" :class="classObj" >{{ name }}</div>
<hr/>
<!--绑定style样式 对象写法-->
<div class="basic" :style="styleObj">
{{ name }}
</div>
<hr/>
<!--绑定style样式 数组写法-->
<div class="basic" :style="[styleObj, styleObj2]">
{{ name }}
</div>
<hr/>
<div class="basic" :style="styleArr">
{{ name }}
</div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data: {
name:'test',
mood:null,
classArr:['atguigu1', 'atguigu2', 'atguigu3'],
classObj:{
atguigu1: false,
atguigu2: false
},
styleObj:{
fontSize: '40px',
color: 'red',
},
styleObj2:{
background: 'green'
},
styleArr:[
{ color: 'orange' },
{ background: 'grey' }
]
},
methods:{
changeMood(){
//vue绑定事件
//不要亲手操作dom
//随机切换心情
const moodsArr = ['normal', 'happy', 'sad'];
const random = Math.floor(Math.random() * moodsArr.length);
this.mood = moodsArr[random];
}
}
});
</script>
</body>
</html>
十一、条件渲染
使用后没有template的结构
十二、列表渲染(v-for)
1、基本列表
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>基本列表</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!--
v-for指令:
1.用于展示列表数据
2.语法:v-for="(item, index) in xxx" :key="yyy"
3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
-->
<h2>人员列表</h2>
<ul>
<!--遍历数组-->
<!--循环列表的方法 类似与for in循环遍历-->
<!--:代表v-bind 属性key让每一个li有了唯一的标识,key一定不要重复-->
<!--v-for(for in// for of)可以接受到两个参数,一个是当前的元素另一个是当前元素的索引 类似于下面的person,index-->
<li v-for='(person, index) in persons' :key="index">
<!--p可能来自形参,也可能来自于写在data里的属性,更可能来自于计算属性 computed-->
{{person.name}} - {{ person.age }}
</li>
</ul>
<!--遍历对象-->
<h2>汽车信息</h2>
<!--注意遍历对象的时候先收到的是每一项的属性的value,第二项是对应的键名:key-->
<ul v-for="(val, key) of car" :key="key">
<li>{{ key }} -- {{ val }}</li>
</ul>
<!--遍历字符串 用的不多-->
<h2>测试遍历字符串</h2>
<!--注意遍历字符串的时候先收到的是字符串中每一个字符,第二项是其对应的索引index-->
<ul v-for="(c, index) of str" :key="index">
<li>{{ index }} -- {{ c }}</li>
</ul>
<!--遍历指定次数-->
<h2>遍历指定次数</h2>
<!--注意遍历指定次数的时候先收到的是number(例如5,则number是1,2,3,4,5),第二项是对应index(从0开始计数,则是0,1,2,3,4)-->
<ul v-for="(num, index) in 5" :key="index">
<li>{{ index }} -- {{ num }}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
//数组
persons: [
{id: '001', name: '张三', age: 18},
{id: '002', name: '李四', age: 19},
{id: '003', name: '王五', age: 20}
],
car: {
name: '奥迪a8',
price: '70w',
color: '黑色'
},
str: 'hello'
}
})
</script>
</body>
</html>
2、key的作用与原理
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>基本列表</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!--
面试题:react、vue中的key有什么作用?(key的内部原理)
1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
3. 用index作为key可能会引发的问题:
1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
2. 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
4. 开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
使用index作为key是没有问题的。
-->
<h2>人员列表</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<!--key唯一标识: 身份证,属性key是被vue给征用的,并不反应在真实dom上-->
<li v-for='(person, index) in persons' :key="person.id">
{{person.name}} - {{ person.age }}
<!--为了看到key值的不正确滥用所导致的问题,我们添加一个input框-->
<input type="text" />
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
//数组
persons: [
{id: '001', name: '张三', age: 18},
{id: '002', name: '李四', age: 19},
{id: '003', name: '王五', age: 20}
],
},
methods:{
add(){
//往数组的头添加元素
this.persons.unshift({
id:'004',
name:'老刘',
age: 40
})
}
}
})
</script>
</body>
</html>
十三、Vue监测数据的原理
十四、收集表单数据
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue中表单数据的收集</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<form @submit.prevent="demo">
<!--写了label则点击它也能使指定的input获取焦点 for属性的值为指定元素的id-->
<label for="demo">账号:</label>
<!--v-model主要用来双向绑定输入类表单value值-->
<input type="text" id="demo" v-model.trim="userInfo.account"/>
<br/>
密码: <input type="password" v-model="userInfo.password"/>
<br/>
性别:
<!--一组radio单选框的name值一定要相同 设置value值好让v-model去双向绑定-->
男:<input type="radio" v-model="userInfo.sex" name="sex" value="male"/>
女:<input type="radio" v-model="userInfo.sex" name="sex" value="female"/>
<br/>
年龄: <input type="number" v-model.number="userInfo.age"/>
<br/>
爱好:
<!--如果没有value值则v-model收集checked元素-->
学习 <input v-model="userInfo.hobby" type="checkbox" value="study"/>
打游戏 <input v-model="userInfo.hobby" type="checkbox" value="game"/>
吃饭 <input v-model="userInfo.hobby" type="checkbox" value="eat" />
<br/>
所属校区
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="Beijing">北京</option>
<option value="Shanghai">上海</option>
<option value="Shenzhen">深圳</option>
<option value="Wuhan">武汉</option>
</select>
<br/>
其他信息<textarea v-model.lazy="userInfo.other"></textarea>
<br/>
<input type="checkbox" v-model="userInfo.ifAgree"/>阅读并接受<a href="https://www.google.com.tw">《用户协议》</a>
<button>提交数据</button>
</form>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data:{
userInfo:{
account: '',
password: '',
sex: 'male',
age:'',
hobby:[],
city:'',
other:'',
ifAgree:''
}
},
methods:{
demo(){
//json转换为string
console.log(JSON.stringify(this.userInfo));
}
}
})
</script>
</body>
</html>
十五、过滤器
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue中过滤器</title>
<script src="../js/vue.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h1>显示格式化后的时间</h1>
<!--计算属性实现-->
<h2>现在是:{{ fmtTime }}</h2>
<!--methods实现-->
<h2>现在是{{ getFmtTime() }}</h2>
<!--过滤器实现-->
<h2>现在是:{{ time | timeFormater }}</h2>
<!--过滤器也可以传递参数-->
<h2>现在是:{{ time | timeFormater('YYYY-MM-DD') | mySlice }}</h2>
<h3 :x="msg | mySlice">你好,世界</h3>
</div>
<div id="root2">
<h2>{{ msg | mySlice }}</h2>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//全局过滤器的配置
//注意配置一定要new vue实例之前确定
Vue.filter('mySlice', function (val){
return val.slice(0,4);
});
new Vue({
el: "#root",
data:{
time: 1631808423062,
msg: "你好,世界"
},
computed:{
fmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
methods:{
getFmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
//局部过滤器
filters:{
//过滤器本质上也是一个函数
timeFormater(val, formate = 'YYYY-MM-DD HH:mm:ss'){
return dayjs(val).format(formate)
},
}
});
const vm2 = new Vue({
el:"#root2",
data:{
msg:'welcome'
}
})
</script>
</body>
</html>
十六、内置指令
1、已经学过的指令
2、v-text
3、v-html
4、v-clock
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloak指令</title>
<style>
[v-cloak]{
display:none;
}
</style>
<!-- 引入Vue -->
</head>
<body>
<!--
v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
-->
<!-- 准备好一个容器-->
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>
<script type="text/javascript">
console.log(1)
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
5、v-once
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>v-once指令</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!--v-once-->
<!--
v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
-->
<h2 v-once>初始化n的值为:{{ n }}</h2>
<h2>当前的n值为:{{ n }}</h2>
<button @click="n++">带我n+1</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el:"#root",
data:{
n:1
}
})
</script>
</body>
</html>
6、v-pre
十七、自定义指令
1、简写(函数式)
2、完整写法(对象式)
3、细节
(1)、指令名用-分隔
(2)、this指向window
所有指令函数里的this都指向window
(3)、全局指令
(4)、总结
十八、生命周期
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>分析vue中生命周期</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root" :x="n">
<h1>当前的n值是{{ n }}</h1>
<h1 v-text="n"></h1>
<button @click="add">点我+1</button>
<button @click="bye">点我销毁vm</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el:"#root",
//template模版字符串只能有一个根结点
// template:'<div><h1>当前的n值是{{ n }}</h1>\n' +
// '<button @click="add">点我+1</button></div>',
//注意template是不能作为根标签来使用的,不能去骗vue,可以用fragment(vue3新加,模仿react)
data: {
n: 1
},
methods:{
add(){
console.log('add')
this.n++;
},
bye(){
//实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的(自定义)事件监听器被移除,所有的子实例也都被销毁。
console.log('bye');
this.$destroy();
}
},
watch:{
n(){
console.log('n变了');
}
},
beforeCreate(){
console.log('beforeCreate');
// console.log(this);
},
created(){
console.log('created');
// console.log(this);
},
beforeMount(){
console.log('beforeMount');
// console.log(this);
},
mounted(){
console.log('mounted');
console.log(this);
// document.querySelector('h1').innerText = 'hahah';
},
beforeUpdate(){
console.log('beforeUpdate');
//console.log(this.n); //此时数据是新的,页面还是旧的,vue还没根据新的数据去生成新的虚拟dom,去比较旧的虚拟dom
},
updated(){
console.log('updated');
console.log(this.n); //此时数据是新的,页面也是新的,同步
},
beforeDestroy(){
//仍然可以使用data,methods,关闭定时器,取消订阅消息,解绑自定义事件等收尾工作,移除watchers
console.log('beforeDestroy');
console.log(this.n);
// this.add(); //记住一旦到了beforeDestroy或者destroyed钩子,即使你拿到数据想要更新它也不会走更新的路了(beforeUpdate,updated)
},
//destroyed钩子几乎不用
destroyed(){
console.log('destroyed');
}
});
</script>
</body>
</html>
十九、组件化开发
1、非单文件组件
(1)、基本使用
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>非单文件组件的基本使用</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>
{{ msg }}
</h1>
<hello></hello>
<!--使用组件-->
<school></school>
<hr/>
<student></student>
<hr/>
</div>
<div id="root2">
<h2>root2容器</h2>
<hello></hello>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
//全部注册
/**
* 想用组件的三个步骤
* 1.创建组件
* 2.注册组件
* 3.使用组件
*/
//创建school组件
const school = Vue.extend({
template: `
<div>
<h2>学校名称:{{ schoolName }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="showName">点我提示学校名</button>
</div>
`,
//组件定义不要写el配置项,因为最终所有的组件都要被vm所管理,由vm决定服务于哪个容器
//这里data必须写成函数形式 避免多次使用组件导致共用data对象导致一个问题
data() {
//注意这里不要写箭头函数
return {
schoolName: '武汉科技大学',
address: '武汉',
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
})
//创建school组件
const student = Vue.extend({
template: `
<div>
<h2>学生名字:{{ studentName }}</h2>
<h2>学生年龄:{{ age }}</h2>
</div>
`,
data() {
return {
studentName: 'Jone',
age: 18
}
}
});
const hello = Vue.extend({
template:`
<div>
<h2>你好世界,{{ name }}</h2>
</div>
`,
data(){
return {
name: 'panyue'
}
}
});
//全局注册hello组件
Vue.component('hello', hello); //全局注册hello 就代表所有的vm都可以用hello组件了
// 创建vm
new Vue({
el: "#root",
//配置组件(局部注册)
data:{
msg: 'hello world'
},
components: {
school,
student
},
})
new Vue({
el: '#root2',
});
</script>
</body>
</html>
(2)、注意细节
(3)、组件嵌套
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件的嵌套</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root" :x="x">
<!--组件的嵌套-->
<!-- <school>-->
<!-- </school>-->
<!-- <hello></hello>-->
<!-- <app></app>-->
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const student = Vue.extend({
template: `
<div>
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
</div>
`,
data(){
return {
name: 'JONE',
age:13
}
},
});
const school = Vue.extend({
template: `
<div>
<h1>学校名称:{{ name }}</h1>
<h1>学校地址:{{ address }}</h1>
<!--子组件注册给哪个父组件,就嵌套在哪个副组件里面--->
<student></student>
</div>
`,
data(){
return {
name: 'xxx大学',
address:'xxxxx'
}
},
//组件嵌套
//这里也是局部注册组件
components: {
student
}
});
const hello = Vue.extend({
template: `<h1>{{ msg }}</h1>`,
data(){
return {
msg: 'hello, my vue world',
}
},
});
const app = Vue.extend({
template:`<div>
<school></school>
<hello></hello>
</div>`,
components:{
school,
hello
}
});
new Vue({
template: `<app></app>`,
el:"#root",
//注册组件(局部)
components:{
// //schoo组件与hello组件平级
// school,
// hello
app,
},
data:{
x:1,
}
});
</script>
</body>
</html>
(4)、VueComponent
(5)、一个内置关系
2、单文件组件
school.vue
<template>
<!-- 组件的交互-->
<div class="demo">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<style>
/*css样式*/
.demo{
background: skyblue;
}
</style>
<script>
//组件交互的代码
//export default school分别暴露
export default {
name: 'School', //开发者工具最终呈现的名字为School
data(){
return {
name:'xxx大学',
address: 'xxxxx'
}
},
methods:{
showName(){
alert(this.name);
}
}
};
//统一暴露
// export { school };
// export default school //默认暴露
</script>
二十、Vue脚手架(cli)
1、创建
第一步(仅第一次执行):全局安装@vue/cli
npm install -g @vue/cli
第二步:切换到你要创建项目的目录,然后使用命令创建项目
vue create 项目名
第三步:启动项目
npm run served
第四步:如果慢可以切换淘宝镜像
npm config set registry https://registry.npmmirror.com/
可以查看当前的镜像
npm config get registry
2、render函数
import Vue from 'vue' //这里引入的是残缺版的vue,是没有模版解析器是不能写template的 vue.runtime.esm.js
import App from './App.vue'
// import Vue from 'vue/dist/vue';
Vue.config.productionTip = false;
/*
关于不同版本的Vue:
1.vue.js与vue.runtime.xxx.js的区别:
(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用
render函数接收到的createElement函数去指定具体内容。
*/
new Vue({
// template: `<App></App>`,
// components: { App },
// render: h => h(App),
el:'#app',
render: h => h(App),
//vue 传递帮你调render函数并传递了一个名为createElement的函数,这里的第一个参数代表h1元素,第二个参数是h1的内容
// render:(createElement) => createElement('h1',"迟缓")
// el:'#app',
// template: '<h1>你好</h1>'
}).$mount('#app')
3、脚手架默认配置
(1)、不可更改的配置
一共5个
(2)、修改配置(vue.config.js)
vue.config.js配置文件
使用vue inspect>output.js可以查看到Vue脚手架的默认配置。
使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh
4、ref属性
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button @click="showH">点我输出上方的dom元素</button>
<School ref="sch"/>
</div>
</template>
<script>
import School from "./components/School";
export default {
name: "App",
data(){
return {
msg: '欢迎学习Vue',
}
},
methods:{
showH(e){
console.log(this.$refs.title); //this ==> vc(app组件)
console.log(e.target); //发生事件的dom元素m
console.log(this.$refs.sch); //可以是school组件加refs属性 获得的是组件事例对象vc
}
},
components:{
School,
},
}
</script>
5、props配置
(1)、基本使用
<template>
<div class="demo">
<!--注意props传递过来的值是不能改的(尽量避免去改,控制台会有警告)-->
<h1>{{ msg }}</h1>
<h2>姓名:{{ myName }}</h2>
<h2>年龄: {{ age }}</h2>
<h2>性别: {{ sex }}</h2>
<button @click="updateName">尝试修改名字</button>
</div>
</template>
<script>
export default {
name: "Student",
// props: ['name', 'sex', 'age'], //简单声明接收
data(){
console.log(this);
return {
//如果props和data存在同名的属性,会报错,但已props传递的属性值为主
//注意props属性名不能是vue底层已征用的属性名(比如key, ref等等)
msg: '我是一名普通的大学生',
myName: this.name //把props传递过来的值当成vc的状态,这样改name是不会出问题的,因为你没有直接去修改props
}
},
methods:{
updateName(){
this.myName = 'sss';
}
},
//限制props中属性的类型 类型错误了会提示错误信息
// props: {
// name: String,
// sex: String,
// age: Number
// }
//接收时不仅限制类型还加上默认值的指定并指定它的必须性
props:{
name:{
type: String, //类型
required: true //必要的
},
age:{
type: Number,
default: 99 //默认值
},
sex:{
type: String,
required: true
}
}
}
</script>
<template>
<div>
<!---多个vc互不影响-->
<!--v-bind的作用重要 获取后面表达式值的赋给props的属性-->
<Student name="panyue" sex="男" :age='12'/>
<hr/>
</div>
</template>
<script>
import Student from "@/components/Student";
export default {
name: "App",
components:{
Student,
},
}
</script>
(2)、用于简单的父子通信
父:app.vue
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader :addTodo="addTodo"/>
<List
:todos="todos"
:checkTodo="checkTodo"
:deleteTodo="deleteTodo" //传函数
/>
<MyFooter
:todos="todos"
:checkAllTodo="checkAllTodo"
:clearAllDoneTodo="clearAllDoneTodo"
/>
</div>
</div>
</div>
</template>
<script>
import MyHeader from "@/components/MyHeader";
import List from "@/components/List";
import MyFooter from '@/components/MyFooter';
export default {
name: "App",
components:{
List,
MyFooter,
MyHeader
},
data() {
return {
todos: [
{id: '001', title: '吃饭', done: false},
{id: '002', title: "睡觉", done: true},
{id: '003', title: '打代码', done: false}
]
}
},
methods:{
//添加的todo
addTodo(todo){
console.log('我是app组件,我收到了数据');
this.todos.unshift(todo);
},
checkTodo(id){
const todo = this.todos.find(todo => todo.id === id);
todo.done = !todo.done;
},
deleteTodo(id){
this.todos = this.todos.filter(todo => todo.id !== id);
},
checkAllTodo(done){
this.todos.forEach(todo => todo.done = done);
},
clearAllDoneTodo(){
this.todos = this.todos.filter(todo => !todo.done)
}
}
}
</script>
<style>
/*base*/
body {
background: #fff;
}
.btn {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.btn-danger {
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-danger:hover {
color: #fff;
background-color: #bd362f;
}
.btn:focus {
outline: none;
}
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
</style>
子 list.vue
<template>
<ul class="todo-main">
<Item
v-for="todoObj in todos"
:key="todoObj.id"
:todo="todoObj"
:checkTodo="checkTodo"
:deleteTodo="deleteTodo"
/>
</ul>
</template>
<script>
import Item from "@/components/Item";
export default {
name: "List",
components: {
Item,
},
props:['todos', 'checkTodo', 'deleteTodo']
}
</script>
<style scoped>
/*main*/
.todo-main {
margin-left: 0;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
</style>
6、mixin(混入)
school.vue
<template>
<div >
<h2 @click="showName">学校名称:{{ name }}</h2>
<h2>学校地址: {{ address }}</h2>
</div>
</template>
<script>
//引入混合
import { mixin, shareData } from "@/mixin";
export default {
name: "School",
data(){
console.log(this);
return {
name: 'wust',
address: 'xxx大学'
}
},
mixins:[ mixin, shareData ]
}
</script>
student.vue
<template>
<div >
<h2 @click="showName">姓名:{{ name }}</h2>
<h2>性别: {{ sex }}</h2>
</div>
</template>
<script>
import { mixin, shareData } from "@/mixin";
export default {
name: "Student",
data(){
console.log(this);
return {
name: '张三',
sex: '男',
x:666 //如果混合中配置了与data(或者配置了相同的methods)相同的属性值,则以你的配置的属性为主(而不以mixin为主)
}
},
mounted() {
console.log('你好啊啊!!!!') //但对于生命周期钩子是都会保存的(混合的钩子比你配置的钩子先跑)
},
mixins:[ mixin, shareData ]
}
</script>
mixin.js
export const mixin = {
methods:{
showName(){
alert(this.name)
}
},
//挂载完毕就执行
mounted() {
console.log('你好啊')
}
}
export const shareData = {
data(){
return {
x:100,
y:200
}
}
}
main.js 全局混入
//引入Vue
import Vue from "vue";
//引入App
import App from './App';
// import { mixin, shareData } from "@/mixin";
//关闭Vue的生产提示
Vue.config.productionTip = false;
//全局混合
// Vue.mixin(mixin);
// Vue.mixin(shareData);
new Vue({
el:'#app',
render: h => h(App)
});
7、插件
src/plugins.js
//vm和vc都可以用
export default {
install(Vue){
//vue帮你调用install方法
// console.log('install');
// console.log(Vue); //vm的构造函数Vue
//全局过滤器
Vue.filter('mySlice', function (val){
return val.slice(0,4);
});
//全局指令
Vue.directive('fbind', {
bind(el, binding){
// console.log('bind')
el.value = binding.value;
},
//指令被插入页面时
inserted(el, binding){
// console.log('inserted')
el.focus();
},
//指令所在模版被重新解析时
update(el, binding){
// console.log('update');
el.value = binding.value;
}
});
//全局混入
Vue.mixin({
data(){
return {
x:1,
y:2
}
}
});
//给vue原型上添加一个方法 vc/vm都可以使用
Vue.prototype.hello = function (){
alert('hello')
}
}
}
main.js
//引入Vue
import Vue from "vue";
//引入App
import App from './App';
//引入插件
import plugins from './plugins';
//关闭Vue的生产提示
Vue.config.productionTip = false;
Vue.use(plugins); //vue应用插件
new Vue({
el:'#app',
render: h => h(App)
});
8、scoped
9、浏览器本地存储
10、组件的自定义事件(子传父)
(1)、基本使用
适用于子传父
父组件
第二种方法时
<template>
<div class="app">
<h1>{{ msg }},学生姓名是:{{ studentName }}</h1>
<!--通过绑定一个自定义事件实现了子给父传递数据(自定义事件绑在子组件上) 第一种写法使用@或v-on-->
<!--once代表改事件只执行一次-->
<!-- <Student @personalEvent="getStudentName" @demo="demo"/>-->
<!--第二种写法使用ref绑定事件--->
<Student ref="student" @click.native="show"/>
<!--通过父组件给子组件传递函数类型的props实现了子给父传递数据-->
<School :getSchoolName="getSchoolName"/>
</div>
</template>
<script>
import Student from "@/components/Student";
import School from "@/components/School";
export default {
name: "App",
components:{
School,
Student,
},
data(){
return {
msg: 'helloこんにちは',
studentName:''
}
},
methods:{
getSchoolName(name){
console.log(`app收到了学校名,${name}`);
},
getStudentName(name, ...params){
console.log('自定义');
console.log(`app收到了学生名, ${name}`);
this.studentName = name;
console.log(`剩余参数,${params}`);
},
demo(){
console.log('demo事件被触发了');
},
show(){
console.log(`123`);
}
},
mounted() {
//可以通过ref拿到组件实例对象
// setTimeout(() => {
// this.$refs.student.$on('personalEvent', this.getStudentName); //当App组件一挂载完毕经过三秒我就在Student组件上绑定事件
// //this.$refs.student.$once('personalEvent', this.getStudentName); //注意此时事件只执行一次就不执行了(once),一次性
// },3000)
//注意这里回调要写成剪头函数,不然this会丢失,这个this就是(vc)app,而不是(vc)student
this.$refs.student.$on('personalEvent', (name) => {
console.log(this);
console.log(name);
this.studentName = name;
});
}
}
</script>
<style>
/*
全局的样式是不需要加scoped
全局共享
*/
.app{
background: gray;
padding: 5px;
}
</style>
子组件
<template>
<div class="student">
<h2>姓名:{{ name }}</h2>
<h2>性别: {{ sex }}</h2>
<h2>当前求和为:{{ number }}</h2>
<button @click="add">点我加一</button>
<button @click="sendStudentName">把学生名传递给app</button>
<button @click="unbind">解绑自定义(personalEvent)事件</button>
<button @click="death">销毁当前student组件的实例对象</button>
</div>
</template>
<script>
export default {
name: "Student",
data(){
console.log(this);
return {
name: '张三',
sex: '男',
number: 0
}
},
methods:{
add(){
console.log(`add回调被调用了`);
this.number ++;
},
sendStudentName(){
//emit触发绑定在指定vc上的自定义事件 vc实例对象可以使用该方法
//后面多余参数演示es6的参数收集
this.$emit('personalEvent',this.name,666,777,888);
// this.$emit('demo'); //同时触发两个事件
// this.$emit('click'); 如果在组件身上使用原生事件不加native修饰符则会让vue认为你这是自定义事件
},
unbind(){
//解绑事件
this.$off('personalEvent'); //这种写法只能解绑一种自定义事件
//this.$off([ 'personalEvent', 'demo' ]);// 解绑多个事件,参数为包含多个事件名的数组
// this.$off(); //比较暴力,有几个自定义事件就全给你解绑了
},
death(){
this.$destroy(); //销毁当前组件实例,销毁后所有该实例的自定义事件都不奏效了
}
}
}
</script>
<style scoped>
.student{
background: orange;
padding: 5px;
margin-bottom: 10px;
}
</style>
(2)、解绑自定义事件
子组件
(3)、父组件的this指向
正确(建议写法)
错误
正确
(4)、给组件绑定原生事件(native)
(5)、总结
11、全局事件总线(任意组件间通信)
12、消息订阅与发布(pubsub-js 任意组件通信)
npm i pubsub-js
(1)、订阅消息(接收消息)
或
(2)、发布消息(发送消息)
13、$nextTick
$nextTick内的方法会在更新节点后执行
需求:点击按钮后出现一个输入框,输入框自动获得焦点
也可以写成下面,不推荐
14、动画与过度
需求:h1标签出现和退出的动画
(1)、动画
如果没有那么,那么默认的类名为v-enter-active,v-leave-active,有的话是name值-enter-active
appear控制一开始是否就触发动画
(2)、过度
或
(3)、多个元素有相同的动画(transition-group)
(4)、第三方动画库(animate.css)
npm i animate.css
(5)、总结
15、插槽
(1)、默认插槽
(2)、具名插槽
(3)、作用域插槽
父组件
<template>
<div class="container">
<Category title="游戏">
<template scope="{games}">
<!--这里data为插槽给你传递的对象包含你所需要的数据 包装成对象的原因就是你当时可能个插槽传递了多个数据-->
<ul>
<li v-for="(g , index) in games" :key="index">{{ g }}</li>
</ul>
</template>
</Category>
<Category title="游戏">
<template scope="{games}">
<!--这里data为插槽给你传递的对象包含你所需要的数据-->
<ol>
<li v-for="(g , index) in games" :key="index">{{ g }}</li>
</ol>
</template>
</Category>
<Category title="游戏">
<template slot-scope="{games}">
<h4 v-for="(g , index) in games" :key="index">{{ g }}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from "@/components/Category";
export default {
name: "App",
components:{
Category
},
}
</script>
<style lang="css" scoped>
.container, .foot{
display: flex;
justify-content: space-around;
}
</style>
子组件
<template>
<div class="category">
<h3>{{ title }}</h3>
<!--插槽,等着组件的使用者进行填充-->
<slot :games="games">
我是默认值
</slot>
</div>
</template>
<script>
export default {
name: "Category",
props:[ 'listData', 'title' ],
data(){
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
}
}
}
</script>
<style scoped>
.category{
background: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background: orange;
}
</style>
16、Vuex
(1)、配置
案例:
在main.js中
src/store/index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
/应用Vuex插件
Vue.use(Vuex)
/准备actions对象一响应组件中用户的动作
const actions = {}
//准备mutations对象一修改state中的数据
const mutations = {}
//准备state对象-保存具体的数据
const state = {}
/创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
(2)、基本使用
初始化数据、配置 actions
、mutations
、state
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)
const actions = {
//响应组件中加的动作
jia(context,value){
context.commit('JIA',value)
},
const mutations = {
//执行加
JIA(state,value){
state.sum += value
}
}
//初始化数据
const state = {
sum:0
}
// 创建并暴露store
export default new Vuex.store({
state,
mutations,
actions
})
main.js 配置 store
组件中读取vuex中的数据: $store.state.sum
组件中修改vuex中的数据: $store.dispatch('action中的方法名',数据) 或 $store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写 dispatch ,直接编写 commit
(3)、getters配置项
(4)、mapState
导入
import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
用于帮助我们映射 state
中的数据为计算属性
// 获取store中state的 sum、school、subject,组件中直接使用{{sum}}...即可获取数据
computed:
{
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
},
(5)、mapGetters
用于帮助我们映射 getters
中的数据为计算属性
// 获取store中getters的 bigSum(),组件中直接使用{{bigSum}}即可获取数据
computed:
{
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
},
(6)、mapActions
用于帮助我们生成与 actions
对话的方法,即:包含$store .dispatch(xxx)
的函数
// 获取store中actions的 jiaOdd()、jiaWait()
methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiawait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}
(7)、mapMutations
用于帮助我们生成与mutations 对话的方法,即:包含$store.commit(xxx)
的函数
// 获取store中mutations 的 JIA()、JIAN()
methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}
(8)、Vuex模块化和命名空间
修改main.js
17、路由
vue2用vue-router3
vue3用vue-router4
npm i vue-router@3
(1)、基本使用
引入路由
main.js
定义路由组件
src/components/About
编写路由配置
src/router/index.js
//引入VueRouter
import VueRouter from 'vue-router'
//引入要路由的组件(路由组件应该放置于pages下,这里演示未分离组件写法)
import About from '../components/About'
import Home from '../components/Home'
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes:[
{
path:'/about' // 路径
component:About // 组件名
},
{
path:'/home'
component : Home
}
]
})
//暴露router
export default router
实现路由切换
active-class="active"可以实现高亮
// App组件中指定点击切换的a标签改为<router-link> href改为to,注意路径
<router-link active-class="active" to="/about">About</router-link>
<router-link active-class="active" to="/home">home</router-link>
指定路由展示位置(路由出口)
// App组件中指定展示位置
<router-view></router-view>
(2)、注意点
(3)、嵌套路由(多级路由)
(4)、路由传参(query参数)
(5)、命名路由
(6)、路由传参(params参数)
(7)、路由的props配置
(8)、<router-link>的replace属性
(9)、编程式路由导航
(10)、缓存路由组件
1.作用:让不展示的路由组件保持挂载,不被销毁。
2.具体编码:
News是组件名,不写include的话,在此处展示的组件全部缓存,多个组件 :include="[xx,xx]"
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
(11)、生命周期钩子activated、deactivated
(12)、路由守卫(全局守卫)
(13)、路由守卫(独享守卫)
// 在路由器配置项中加入beforeEnter
beforeEnter(to,from,next){
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
// 判断是否满足school=lhd
if(localStorage.getItem('school')==='lhd'){
next() // 放行
}else{
alert('暂无权限查看')
}
}else{
// 不需要拦截,放行
next()
}
}
(14)、路由守卫(组件内守卫)
注意:通过路由规则才调用
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from, next){
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
}
(15)、解决history路由刷新404问题(node.js)
后端
npm i connect-history-api-fallback
18、组件库
(1)、移动端常用 UI 组件库
(1) Vant
(2) Cube Ul
(3) Mint Ul
(2)、PC 端常用 UI 组件库
(1)Element UI
(2) View UI
(3)、element UI
npm i element-ui
在main.js中引用(完整引用)
按需引入
npm i babel-plugin-component -D
在babel.config.js或.babelrc中追加
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
最新版脚手架可能要改成下面,否则报错
在main.js中按需引入
import Vue from 'vue';
import { Button, Select } from 'element-ui'; //按需引入
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* 或写为
* Vue.use(Button)
* Vue.use(Select)
*/
new Vue({
el: '#app',
render: h => h(App)
});
二十一、Vue3
Vue3基础使用-CSDN博客https://blog.csdn.net/m0_71534259/article/details/139900842?spm=1001.2014.3001.5501
Vue3基础(二)-CSDN博客https://blog.csdn.net/m0_71534259/article/details/139908780?spm=1001.2014.3001.5501