作者主页:仙女不下凡-前端萌新
前言介绍:以下 内容都是我个人对于前端知识的总结,会定期更新欢迎持续关注!
欢迎点赞 收藏 ⭐留言 如有错误敬请指正!
学习视频地址:https://www.bilibili.com/video/BV1Zy4y1K7SH?from=search&seid=16946268743532201801
学习进度:该视频适用于有css、html、JavaScript与前后端交互基础的同学
学习方法总结:https://editor.csdn.net/md/?articleId=116121563
Vue
是一套用于构建用户界面的渐进式JavaScript
框架。
特点:❶ 采用组件化模式,提高代码复用率且让代码更好维护;❷声明式编码,让编码人员无需直接操作DOM
,提高开发效率;❸使用虚拟DOM
+由优秀的Diff
算法,尽量复用DOM节点
。
1.使用vue必须创建一个Vue实例,且要传入一个配置对象
容器
Vue模板语法两大类 - 插值语法:功能:用于解析标签体内容
;写法:{{xxx}},xxx是js表达式,可以直接读取data中所有属性
。
Vue模板语法两大类 - 指令语法:功能:用于解析标签(包括:标签属性,标签体内容,绑定事件......)
;写法:v-????
。
单向数据绑定v-bind:数据只能从data
流向页面,简写:
。
双向数据绑定v-model:如下代码是错误的,v-model
只能应用在表单类元素(输入类元素)
<h2 v-model:x="name">你好啊h2>
⚠️注意⚠️:❶双向数据绑定一般都应用在表单类元素上(如:input、select
等);❷v-model:value
可以简写为v-model,因为v-model默认收集的就是value的值。
el
的两种写法 el
的第一种写法
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">容器div>
body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
const vm = new Vue({
el: '#root', /第一种写法,直接与容器关联/
data: {},
})
script>
el
的第二种写法
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">容器div>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
const vm = new Vue({
//el: '#root', //直接与容器关联
data: {},
});
vm.$mount('#root'); /这就是第二种写法挂载,意义:写法灵活,比如可以加定时器控制执行时间/
script>
body>
data
的两种写法data
第一种写法
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">容器div>
body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {}, /data第一种写法,对象式/
})
script>
data
第二种写法
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">容器div>
body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: function(){ /data第二种写法,函数式,function可以省略,组件必须使用函数式/
return: 'xxx',
},
})
script>
MVVM
模型:Vue
参考了该模型
M
:模型(Model):对应data
中的数据
V
:视图(View):模板
VM
:视图模型(ViewModel):Vue
实例对象
总结发现:❶data
中所有的属性,最后都出现在了vm
身上;❷另外vm
身上所有的属性及Vue原型
上所有属性,在Vue模板
中都可以直接使用。
回顾Object.defineproperty
方法(ES6):给对象添加属性
let number = 18;
let person = {
name: '张三',
sex: '男',
//age: number,
};
//需求当number改变时可以同步使age改变,这是就是使用到Object.defineproperty方法
Object.defineproperty(person, 'age', {
/当有人读取person的age属性时,get函数(getter函数)就会被调用,且返回值就是age的值/
get:function(){ //function可以省略
return number;
},
/当有人修改person的age属性时,set函数(setter函数)就会被调用,且会收到修改的具体值/
set(value){
number = value
}
})
Vue
中的数据代理:通过一个对象代理对另一个对象中属性的操作(读/写),示例代码如下:
//简单的数据代理
let obj = { x:200 };
let obj2 = { y:300 };
Object.defineproperty(obj2,'x',{
get:(){
return obj.x;
},
set(value){
obj.x = value
}
})
Vue
中数据代理的好处:更加方便的操作data中的数据。
基本原理:❶通过Object.defineproperty()
把data对象
所有属性添加到vm
上;❷为每一个添加到vm
的属性,都指定一个getter/setter
;❸在getter/setter
内部去操作(读/写)data
中对应的属性。
事件的基本使用:❶使用v-on:xxx
或@xxx
绑定事件,其中xxx
是事件名;❷事件的回调需要配置的在methods对象
中,最终会在vm
上;❸methods
中配置的函数,不要用箭头函数,否则this
就不是vm实例
;❹methods
中配置的函数,都是被Vue
所管理的函数,this指向
是vm实例
或组件实例对象;❺@click="demo"
与@click="demo($event)"
效果一致,但是后者可以传参。
Vue中的事件修饰符:❶prevent
阻止默认事件(常用);❷stop
阻止事件冒泡(常用);❸once
事件只触发一次(常用);❹capture
使用事件的捕获模式;❺self
只有event.target是当前操作的元素时才触发事件;❻passive
事件的默认行为立即执行,无需等待事件回调执行完毕。
Vue
中常用的按键别名:
按键 | 别名 |
---|---|
回车 | enter |
删除 | delete(捕获"删除"和"退格"键) |
退出 | esc |
空格 | space |
换行 | tab(特殊,必须配合keydown去使用) |
上,下,左,右 | up,down,left,right |
⚠️注意⚠️:Vue
未提供别名的按键,可以使用按键原始的key值
去绑定,但注意要转为kebab-case
(短横线命名)。
系统修饰键(用法特殊):ctrl、alt、shift、meta
。❶配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发;❷配合keydown
使用:正常触发事件;❸也可以使用keyCode
去指定具体的按键(不推荐)。
Vue.config.keyCode.自定义键名 = 键码
:可以定制按键别名。
对比插值表达式、方法methods
、计算属性,来了解计算属性存在的必要。
1.姓名案例-插值语法实现
<body>
<div id="root">
姓:<input type="text" v-model="fristName"><br/>
名:<input type="text" v-model="lastName"><br/>
全名:<span>{{fristName}}-{{lastName}}span>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data:{
fristName: '张',
lastName: '三'
}
})
script>
2.姓名案例-methods方法实现
<body>
<div id="root">
姓:<input type="text" v-model="fristName"><br/>
名:<input type="text" v-model="lastName"><br/>
全名:<span>{{fullName()}}span>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
fristName: '张',
lastName: '三'
},
methods: {
fullName(){
return this.fristName + '-' + this.lastName, /问题效率不高/
}
}
})
script>
3.姓名案例-计算属性实现
<body>
<div id="root">
姓:<input type="text" v-model="fristName"><br/>
名:<input type="text" v-model="lastName"><br/>
全名:<span>{{fullName}}span>
全名:<span>{{fullName}}span> /只调用了一次fullName中的get(),有缓存,优势/
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data:{
fristName: '张',
lastName: '三'
},
computed:{ //计算属性
//完整写法
fullName:{
/get作用:当有人读取fullName时,get就会被调用,且返回值就作为fullName的值/;
/get什么时候被调用? ❶初次读取fristName时 ❷所依赖的数据发生改变时/
get(){ return this.fristName + '-' + this.lastName },
/set什么时候调用? 当fullName被修改时调用./
set(){} //不是必须写的
}
//简写,如果没有定义setter简写形式如下
fullName(){
return this.fristName + '-' + this.lastName
}
}
})
script>
定义:要用的属性不存在,要通过已有属性计算得来。
原理:底层借助了Object.defineproperty()
提供的getter和setter
。
get函数什么时候执行❶初次读取时会执行一次❷当依赖的数据发生变化时会被再次调用。
优势:与methods
实现相比,内部有缓存机制(复用),效率更高,调试更方便。
⚠️备注⚠️:❶计算属性最终出现在vm实例
上,直接读取使用即可❷如果计算属性要被修改,那必须写在set函数
去响应修改,且set
中要引起计算时依赖的数据发生改变`。
通过上述学习的方法实现天气的切换
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
<h2>今天天气很{{info}}h2>
<button @click="changeWeather">切换天气button> /事件(如click)后的表达式默认在vm上面找属性和方法执行/
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
//创建Vue实例
const vm = new Vue({
el: '#root',
data: {
isHot: true,
},
computed: {
info(){ return this.isHot ? '炎热' : '凉爽' }
},
methods: {
changeWeather(){ this.ihHot = ! this.isHot }
}
})
script>
通过该案例理解监视watch方法
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
<h2>今天天气很{{info}}h2>
<button @click="changeWeather">切换天气button>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止Vue在启动时生成生产提示
//创建Vue实例
const vm = new Vue({
el: '#root',
data: {
isHot: true,
},
computed: {
info(){ return this.isHot ? '炎热' : '凉爽' }
},
methods: {
changeWeather(){ this.ihHot = ! this.isHot }
}
watch:{ /第一种监视watch方法/
isHot:{ /这里isHot是配置对象,也是监视的对象/
immediate:true, /初始化时让handler调用一下/
//handler什么时候调用?当isHot发生改变时调用.
handler(newValue,oldValue){ console.log(newValue,oldValue) } /handler也是配置对象/
}
}
/简写,当不需要设置immediate与deep时,第一种可以这样简写/
watch{
isHot(newValue,oldValue){ console.log(newValue,oldValue) }
}
})
vm.$watch('isHot', { /第二种监视watch方法/
immediate:true,
handler(newValue,oldValue){ console.log(newValue,oldValue) }
});
/简写,当不需要设置immediate与deep时,第二种可以这样简写/
vm.$watch('isHot', function(newValue,oldValue){ console.log(newValue,oldValue) });
script>
监视属性watch
:当监视的属性变化时,回调函数自动调用,进行相关操作,监视属性必须必存在,才能进行监视。
监视的两种写法:❶new Vue
时传入watch配置
❷通过vm.$watch
监视。
通过该案例理解深度监视-只检测a的变化
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
<h3>a的值是:{{number.a}}h3>
<button @click="number.a++">点我让a+1button>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
number: {
a:1, //监听a
b:1
}
},
watch:{
/监视多级结构中某个属性的变化/
'number.a':{
handler(newValue,oldValue){
}
}
}
})
script>
通过该案例理解深度监视-检测a和b的变化
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
<h3>a的值是:{{number.a}}h3>
<button @click="number.a++">点我让a+1button>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
number: {
a:1, //监听a
b:1
}
},
watch:{
number:{
deep: true, /深度检测开启/
handler(newValue,oldValue){}
}
}
})
script>
深度监视:❶Vue
中的watch
默认不监测对象内部值的改变(一层);❷配置deep:true
可以监测对象内布值改变(多层)。
⚠️备注⚠️:❶Vue
自身可以监测对象内部值的改变,但Vue
提供的watch
默认不可以!❷使用watch
时根据数据的具体结构,决定是否采用深度监视。
watch
与computed
对比 watch
与computed
之间的区别:❶computed
能完成的功能,watch
都可以完成;❷watch
能完成的功能,computed
不一定能完成,例如watch
可以进行一步操作。
⚠️两个重要的小原则⚠️:❶所被Vue
管理的函数,最好携带普通函数,这样this指向
才是vm
或组件实例对象;❷所有不被Vue
所管理的函数(定时器的回调函数、ajax
回调函数等),最好携带箭头函数,这样this指向
才是vm
或组件实例对象。
绑定class样式
<head>
<script text="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
/绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定/
<div class="basic" :class="mood" @click="changeMood">{{name}}div><dr/>
/绑定class样式--数组写法,适用于:要绑定的样式个数和名字不确定/
<div class="basic" :class="classArr">{{name}}div><dr/>
/绑定class样式--对象写法,适用于:要绑定的样式个数和名字确定,但是要动态决定用不用/
<div class="basic" :class="classObj">{{name}}div><dr/>
<div class="basic" :style="{fontSize: fsize+'px'}">{{name}}div>
/绑定style样式--数组写法/
<div class="basic" :style="styleObj">{{name}}div>
/绑定style样式--对象写法/
<div class="basic" :style="styleArr">{{name}}div>
div>
body>
<script type="text/javascript"> /basic,normal与happy都是写好的样式/
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
name: '尚硅谷',
mood: 'normal',
classArr: ['atguigu1','atguigu2','atguigu3'], /atguigu1,atguigu2,atguigu3都是写好的样式/
classObj: {
atguigu1:false,
atguigu2: false
},
fsize: 40,
styleArr: [{
color: 'red',
fontSize: '40px'
},{
backgroundColor: 'bull',
}],
styleObj: {
color: 'red',
fontSize: '40px'
}
styleObj2: {
backgroundColor: 'bull',
}
},
methods: {
changeMood:{
this.mood = 'happy',
}
}
})
script>
写法:v-if="表达式" v-else-if="表达式" v-else="表达式"
⚠️备注⚠️:v-if
可以和v-else-if、v-else
一起使用,但要求结构不能被"打断"。适用于切换频率较低的场景,特点是不展示的DOM元素
直接被移除。
写法:v-show="表达式"
⚠️备注⚠️:特点不展示的 DOM元素
未被移除,适用于切换频率较高的场景。
<body>
<div id="root">
<ul>
<li v-for="(p,index) in persons" :key="index">{{p.name}}-{{p.age}}li>
ul>
<ul>
<li v-for="(value,k) of car" :key="k">{{k}}-{{value}}li>
ul>
<ul>
<li v-for="(char,index) of str" :key="index">{{index}}-{{char}}li>
ul>
<ul>
<li v-for="(number,index) of 5" :key="index">{{number}}-{{index}}li>
ul>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: 'root',
data: {
persons: [
{id:'001',name:'张三',age:'18'},
{id:'002',name:'李四',age:'17'},
{id:'003',name:'王五',age:'18'},
],
car: {name:'奥迪A8',price:'70万',color:''},
str: 'hello'
}
})
script>
v-for
指令:❶用于展示列表数据❷语法v-for="(item,index) in xxx" :key="yyy"
❸可遍历:数组、对象、字符串(用得少)、指定次数(用的最少)。
面试题:react、vue中的key有什么作用? (key的内部原理)
虚拟DOM
中key
的作用:key
是虚拟DOM对象
的标示,当数据发生变化时,Vue
会根据新数据
生成新的虚拟DOM
,随后Vue
进行新虚拟DOM
与旧虚拟DOM
的差异比较。比较规则如下:
对比规则:❶旧虚拟DOM
中找到了与新虚拟DOM
相同的key
;若虚拟DOM
中内容没变,直接使用之前的真实DOM
;若虚拟DOM
中内容变了,则生成新的真实DOM
,随后替换掉页面中之前的真实DOM
。❷旧虚拟DOM
中未找到与新虚拟DOM
相同的key
创建新的真实DOM
,随后渲染到页面。
用index
作为key
可能会引发的问题:❶若对数据进行:逆序添加、逆序删除破坏操作( 会产生没有必要的真实DOM
更新 ==> 界面效果没问题,但效率低 )。⑵如果结构中还包含输入类的DOM
( 会产生错误DOM
更新 ==> 界面有问题 )。
开发中如何选择key
?
❶最好使用每条数据的唯一标识作为key
,比如id、手机号、身份证号、学号等唯一值。❷如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index
作为key
是没有问题的。
<body>
<div id="root">
<h2>人员列表h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="(p,index) of filPerons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
div>
body>
/监视写法/
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root'
data: {
keyWord:'',
persons: [
{id:'001',name:'马冬梅',age:'19',sex:'女'},
{id:'002',name:'周冬雨',age:'20',sex:'女'},
{id:'003',name:'周杰伦',age:'21',sex:'男'},
{id:'004',name:'温兆伦',age:'22',sex:'男'}
],
filPerons: [],
}
watch: {
keyWord: {
immediate: true, /一上来就执行,相当于给filPerons赋值,否则filPerons为空/
handler(val){
this.filPerons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
})
script>
/ 计算属性写法 /
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root'
data: {
keyWord:'',
persons: [
{id:'001',name:'马冬梅',age:'19',sex:'女'},
{id:'002',name:'周冬雨',age:'20',sex:'女'},
{id:'003',name:'周杰伦',age:'21',sex:'男'},
{id:'004',name:'温兆伦',age:'22',sex:'男'}
]
},
computed: {
filPerons(){
return this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
script>
<body>
<div id="root">
<h2>人员列表h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<button>年龄升序button>
<button>年龄降序button>
<button>原顺序button>
<ul>
<li v-for="(p,index) of filPerons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root'
data: {
keyWord:'',
sortType:0, //0原顺序,1降序,2升序
persons: [
{id:'001',name:'马冬梅',age:'19',sex:'女'},
{id:'002',name:'周冬雨',age:'20',sex:'女'},
{id:'003',name:'周杰伦',age:'21',sex:'男'},
{id:'004',name:'温兆伦',age:'22',sex:'男'}
]
},
computed: {
filPerons(){
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
//判断一下是否需要排序
if(this.sortType){
arr.sort((a,b) => { /sort(方法)/
return this.sortType === 1 ? b.age-a.age : a.age-b.age
})
}
return arr
}
}
})
script>
/ 模拟一个数据检测 /
<body>
<div id="root">
<h1>学生信息h1>
<button @click="student.age++">年龄+1岁button> <br/>
<button @click="addSex">添加性别属性,默认值:男button> <br/>
<button @click="addSex">修改性别属性,默认值:男button> <br/>
<button @click="addFridend">在列表首位添加一个朋友button> <br/>
<button @click="updateFrietFriendName">修改第一个朋友的名字为:张三button> <br/>
<button @click="addHobby">添加一个爱好button> <br/>
<button>修改第一个爱好为:开车button> <br/>
<h3>姓名:{{student.name}}h3>
<h3>年龄:{{student.age}}h3>
<h3 v-if="student.sex">性别:{{student.sex}}h3>
<h3>爱好:h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">{{h}}li>
ul>
<h3>朋友们:h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">{{f.name}}-{{f.age}}li>
ul>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root'
data: {
student: {
name: 'tom',
age: 18,
hobby: ['抽烟','喝酒','烫头'],
friends: [
{name:'jerry', age:25},
{name:'tony', age:16}
]
}
},
methods: {
addSex(){ Vue.set(this.student,'sex','男') }, /第二种写法this.$set(this.student,'sex','男')/
addFridend(){ this.student.friends.unshift({name:'jack',age:20}) },
updateFrietFriendName(){ this.student.friend[0].name = '张三' },
addHobby(){ this.student.hobby.push('学习') },
updateHobby(){ this.student.hobby.splice(0,1,'开车')},/第二种写法Vue.set(),第三种this.$set()/
}
})
script>
Vue
监视数据的原理:vue
会监视data
中所有层次的数据。
如何监测对象中的数据? 通过setter
实现监视,且要在new Vue
时就传入要监测的数据❶对象中后追加的属性,Vue
默认不做响应式处理;❷如需给后添加的属性做响应式,请使用以下API
:
Vue.set(target, propertyName/index, value)
或vm.$set(target, propertyName/index, value)
如何监测数据中的数据? 通过包裹数组更元素的方法实现,本质就是做了两件事❶调用原生对应的方法对数组进行更新;❷重新解析模板,进而更新页面。
在Vue
修改数组中的某个元素一定要用如下方法:❶使用这些API
:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
; ❷.Vue.set() 或 vm.set()
。
⚠️特别注意⚠️:Vue.set()
和vm.set()
不能给vm
或vm
的根数据对象添加属性!!!
示例代码
<body>
<div @submit.prevent="demo"> /prevent阻止默认行为/
<form action="">
<label for="demo">账号:label><input type="text" v-model.trim="demo"> <br/> /trim去掉前后空格/
<label for="password">密码:label><input type="text" v-model="password"> <br/>
<label for="age">年龄:label><input type="number" v-model.number="age"> <br/> /number设置填写的为数字类型/
<label for="demo">性别:label>
男<input type="radio" name="sex" v-model="sex" value="male">
女<input type="radio" name="sex" v-model="sex" value="female"> <br/>
<label for="password">爱好:label>
学习<input type="checkbox" v-model="hobby" value="study">
打游戏<input type="checkbox" v-model="hobby" value="game">
吃饭<input type="checkbox" v-model="hobby" value="eat"> <br/>
<label for="password">请选择校区:label>
<select v-model="city">
<option value="">请选择校区option>
<option value="北京">北京option>
<option value="上海">上海option>
<option value="深圳">深圳option>
<option value="武汉">武汉option>
select> <br/>
<label for="password">其他信息:label>
<textarea v-model.lazy="other">textarea> br> /lazy懒惰/
<input type="checkbox" v-model="agree">阅读并接受<a href="http://xxxx"> 《用户协议》 a>
<button>提交button>
form>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root'
data: {
account:'',
password:'',
age:'',
sex:'female',
hobby:[],
city:'',
other:'',
agree:''
},
methods: {
/思考怎么获取表单填写的所有数据? ❶_date就可以获取到 ❷把data中表相关数据包成一个对象也可以但是h5需要加相应的前缀/
demo(){
console.log(this._date)
}
}
})
script>
收集表单数据:
⑴.若则
v-model
收集的是value值
,用户输入的就是value值
;
⑵.若则
v-model
收集的是value值
,且要给标签配置value值
;
⑶.若
❶没有配置inpu
t的value属性
,那么收集的就是checked
(布尔值);
❷配置input
的value属性
但若v-model
的初始值是非数值,那么收集的就是checked
(布尔值),v-mode
l的初始值是数组,那么收集的就是value
组成的数组。
v-model
的三个修饰符:lazy
失去焦点再收集数据;number
输入字符串转为有效的数字;trim
输入首位空格过滤。
常用处理时间戳的插件month.js
(相对)、day.js
(轻量)。
<script type="text/javascript" src="../js/dayjs.min.js">script>
<body>
<div id="root">
<h2>显示格式化后的时间h2>
<h3>现在是:{{fmtTime}}h3>
<h3>现在是:{{getFmtTime()}}h3>
<h3>现在是:{{time | timeFormater}}h3> /将time作为参数传给timeFormater/
div>
<div id="root2">
<h2>{{msg}}h2>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
/全局过滤器/
Vue.filter('mySlice', function(value){ return dayjs(value).format('YYYY-MM-DD HH:mm:ss') });
new Vue({
el: '#root'
data: { time:'时间戳数据'}, /时间戳引用这里省略了/
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(value){ return dayjs(value).format('YYYY-MM-DD HH:mm:ss') }
}
})
new Vue({
el: '#root2'
data: {
msg: '尚硅谷',
},
computed: {},
methods: {},
})
script>
过滤器定义:对要显示的数据进行特定格式化后再显示(适用于一些简单的逻辑处理)。
过滤器语法:❶注册过滤器Vue.filter(name,callback)
或 new filter{ filter:{} }
❷使用过滤器{{ xxx | 过滤器名 }}
或 v-bind:属性 = "xxx | 过滤器名"
。
⚠️备注⚠️:❶过滤器也可以接收额外参数、多个过滤器也可以串联❷并没有改变原本的数据,是产生新的对应数据。
指令 | 作用 |
---|---|
v-bind | 单向绑定解析表达式,可简写为:xxx |
v-model | 双向数据绑定 |
v-on | 绑定事件监听,可简写为@ |
v-for | 遍历数组/对象/字符串 |
v-if | 条件渲染 (动态控制节点是否存在) |
v-else | 条件渲染 (动态控制节点是否存在) |
v-show | 条件渲染 (动态控制节点是否展示) |
v-text
指令作用:向其所在的节点中渲染文本内容。
v-text
指令与插值语法区别:v-text
会替换掉节点中的内容,{{xx}}
不会。
v-html
指令作用:向指定节点中渲染包含html结构的内容。
v-html
指令与插值语法的区别:①v-html
会替换掉节点中所有的内容,{{xx}}
则不会;②v-html
可以识别html结构。
⚠️严重注意⚠️:v-html
指令有安全性问题!①在网站上动态渲染任意HTML
是非常危险的,容易导致KSS攻击
;②一定要在可信的内容上使用v-html
,用不要用在用户提交的内容上!
v-cloak
指令本质:是一个特殊属性,Vue实例
创建完毕并接管容器后,会散删掉v-cloak
属性;使用css配合v-cloak
可以解决网速慢时页面展示出{{xxx}}
的问题。
<head>
/没有直接引用js文件夹中vue.js的原因,使用该文件延迟5秒才调用vue.js文件/
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js">script>
<style>
[v-cloak]{ display: none }
style>
head>
<body>
<div id="root">
<h2 v-cloak>{{name}}h2> /如果不加v-cloak页面会显示{{name}},5秒之后插值表达式才会解析/
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root'
data: { name: '尚硅谷' }
})
script>
v-once
指令:v-once
所在节点在初次动态渲染后,就视为静态内容了;以后的数据的改变不会引起v-once
所在结构的更新,可以用于优化性能。
v-pre
指令:v-pre
可以跳过其所在节点的编译过程;可利用它跳过没有使用指令语法、没有使用插值语法的节点,会加快编译。
/需求1: 定义一个v-big指令,与v-text类似但是会把绑定的数值放大10倍/
/需求2: 定义一个v-fbind指令,与v-bind类似但可以让其所绑定的input元素默认获取焦点/
<body>
<div id="root">
<h2>当前的n值是:<span v-text="n">span> h2>
<h2>放大10倍后的n值是:<span v-big="n">span> h2>
<button @click="n++">点我n+1button> <hr/>
<input type="text" v-fbind:value="n">
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root'
data: { n:1 },
/局部自定义指令/
directives: {
/两种方式,第一写成对象big:{}(完整),第二种写成函数big(){}/
big(element,binding){ /element是真实DOM元素,binding是一个对象存各种信息/
element.innerText = binding.value * 10;
},
fbind:{ /包含3个钩子函数/
console.log(this); /注意此处的this指向window/
bind(element,value){ element.value = binding.value }, /指令与元素成功绑定时调用/
inserted(element,value){ element.focus() }, /指令所在元素被插入页面时调用/
upddate(element,value){ element.value = binding.value } /指令所在的模板被重新解析时调用/
}
},
})
script>
自定义指令语法:❶局部指令new Vue({ directives:{指令名:配置对象} })
或 new Vue({ directivea(){} })
❷全局指令Vue.directive(指令名, 配置对象)
或 Vue.directive(指令名, 回调函数)
。
配置对象中常用的3个钩子函数:❶bind
指令与元素成功绑定时调用❷inserted
指令所在元素被插入页面时调用❸update
指令所在模板结构被重新解析时调用。
⚠️备注⚠️:❶指令定义时不加v-
,但使用时要加v-
❷指令名如果是多个单词,要使用kebab-case
命名方式,不要用camelCase
命名。
生命周期定义:❶又名生命周期回调函数、生命周期函数、生命周期钩子;❷是Vue
在关键时刻调用的以下特殊名称的函数,生命周期函数的名字不可更改;❸生命周期中this
指向是vm实例
或 组件实例对象
。
vm状态 | 调用的函数 |
---|---|
将要创建 | 调用beforeCreate 函数 |
创建完毕 | 调用created 函数 |
将要挂载 | 调用beforeMount 函数 |
挂载完毕 | 调用**mounted** 函数 【重要的钩子】 |
将要更新 | 调用beforeUpdate 函数 |
更新完毕 | 调用undate 函数 |
将要销毁 | 调用**beforeDestroy** 函数 【重要的钩子】 |
销毁完毕 | 调用destroyed 函数 |
<body>
<div id="root">
<h2 :style="{opacity}">欢迎学习Vueh2> /这里是{opacity:opacity}简写了/
<button @click="stop">透明度设置为1button>
<button @click="stop">点我停止变换button>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: { opacity: 1 },
methods: {
stop(){
//clearInterval(this.timer); /这个方法只是停止了定时器并没有销毁/
this.$destroy(); /这个方法是销毁了vm,会调用beforeDestroy与destroyed/
}
},
mounted(){
this.timer = setInterval(() => {
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
}, 16)
},
beforeDestroy(){ clearInterval(this.timer) }
})
script>
常用的生命周期钩子:❶mounted
发送ajax请求、启动定时器、绑定定时器、订阅消息【初始化操作】;❷beforeDestroy
清除定时器、解绑自定义时间、取消订阅消息等【收尾工作】。
关于销毁Vue
实例:❶销毁后借助Vue
开发者工具看不到任何信息;❷销毁后自定义时间会失效,但原生DOM
事件依然有效;❸一般不会在beforeDestroy
操作数据,因为即使操作数据,也不会在触发更新流程了。
模块:向外提供特定功能的js程序,一般就是一个js
文件;复用js
文件,简化js
编写,提高js
运行效率。
组件:用来实现局部(特定)功能效果的代码集合(html/css/js/image...
),复用代码,简化项目编码,提高运行效率。
非单文件组件:一个文件中包含有n
个组件
单文件组件:一个文件只有一个组件a.vue
代码示例 - 单文件组件语法格式
<body>
<div id="root">
<xuesheng>xuesheng> //4.写组件标签
<hr>
<school>school> //4.写组件标签
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
/组件重要API-Vue.extend()/
//1.创建组件
const student = Vue.extend({
/组件一定不要写el配置项,因为最终所有的组件要被一个vm管理,由vm决定服务于哪个容器/
//el:'#root',
/可以用name配置项指定组件在开发者工具中呈现的名字/
//name: 'xuexiao',
template:`
学生姓名:{{name}}
学生年龄:{{age}}
`,
data(){
return { name:'张三', age:18 }
}, /这不能用对象形式,因为会影响其他引用该组件的值,另外若写成对象形式也会报错/
})
const school = Vue.extend({
template:`
学校名称:{{name}}
学校地址:{{address}}
`
data(){ name:'尚硅谷', address:'上海' }
})
//2.创建vm
new Vue({
el: '#root',
// 3. 注册组件(局部注册)
components: {
school: school, //简写成school
xuesheng: student
}
data: {},
});
/全局注册组件/
Vue.components(student, 'student')
script>
关于组件名:一个单词组成(第1种写法 首字母小写)school
、(第2种写法 首字母大写)School
;
多个单词组成(第1种写法kebab-case
命名)my-school
、(第2种写法CamelCase
命名) MySchool
(需要Vue
脚手架)。
关于组件标签:第1种写法
、第2种写法
(不用脚手架时,会导致后续组件不能渲染)。
一个简写形式const school = Vue.extend(options)
可简写为const school = options
。
代码示例 - 组件的嵌套
<body>
<div id="root">
<school>school>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
const student = Vue.extend({ /子组件/
template:`
学生姓名:{{name}}
学生年龄:{{age}}
`,
data(){
return { name:'张三', age:18 }
},
});
const school = Vue.extend({ /父组件/
components:{ student }, /组件的嵌套/
template:`
学校名称:{{name}}
学校地址:{{address}}
`
data(){ name:'尚硅谷', address:'上海' }
});
/标准化开发时app组件管理所有组件-app组件/
const app = Vue.extend()
new Vue({
el: '#root',
components: { school }, /使用嵌套组件/
data: {},
});
script>
代码示例 - 组件的嵌套
<body>
<div id="root">
<school>school>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
const school = Vue.extend({
template:`
学校名称:{{name}}
学校地址:{{address}}
`
data(){ name:'尚硅谷', address:'上海' }
});
console.log(school); /执行结果是构造函数VueComponent()/
new Vue({
el: '#root',
components: { school },
data: {},
});
script>
school
组件本质是一个名为VueComponent
的构造函数,且不是程序员定义的,是Vue.extend
生成的。
我们只需要写
或
,Vue解析时会帮我们创建school
组件的实例对象。
⚠️特别注意⚠️:每次调用Vue.extend
,返回的都是一个全新的VueComponent
。
VueComponent
的实例对象以后简称VC
(也可称之为:组件实例对象) ;Vue
实例对象,简称vm
。
关于this
指向:①组件配置中data函数
、methods中的函数
、computed中的函数
它们的this
指向均是【VueComponent
实例对象】②new Vue()
配置中data函数
、methods中的函数
、computed中的函数
它们的this
指向均是【Vue
实例对象】。
<body>
<div id="root">div>
body>
<script type="text/javascript">
Vue.config.productionTip = false;
//定义一个构造函数, 使用原型链的原理理解该内置关系
function Demo(){
this.a = 1,
this.b = 2
};
//创建一个Demo的实例对象
const d = new Demo();
/ Demo.prototype === d.__proto__ 显示原型属性与隐式原型属性都是指向原型对象 /
console.log(Demo.prototype); /显示原型属性/
console.log(d.__proto__); /隐式原型属性/
//通过显示原型属性操作原型对象, 追加一个x属性, 值为99
Demo.prototype.x = 99
console.log(d.__proto__.x); /执行结果为99/
script>
内置关系VueComponent.prototype.__proto__ === Vue.prototype
,作用让组件实例对象VC
可以访问到Vue
原型上的属性与方法。
黄色这条线是vue
做的操作,她没有使VueComponent
的原型对象指向Object
的原型对象而是指向了Vue
的原型对象。
示例代码-.vue文件的结构
<template> 组件结构 template>
<script> 组件交互先关的代码(数据、方法等) script>
<style> 组件的样式 style>
/注意:需要暴露export/
<template>
<div>
<h2>学校名称:{{name}}h2>
<h2>学校地址:{{address}}h2>
div>
template>
<script>
export const school = Vue.extend({ /1.分别暴露/
data(){ return name:'尚硅谷', address:'上海' }
});
/2.统一暴露/
//export {school};
/3.默认暴露(较为常用)/
//export default school
/简写/
export default {
name: 'School', /这种首字母大写的命名方式较为常用, 最好与文件名一致/
}
script>
<style> 组件的样式 style>
.vue文件
<script>
/引入组件, 注意没有脚手架.vue文件不识别报错/
import School from './School.vue'
script>
.vue
文件都是一个页面功能,可以看为单文件组件,不需要调用new Vue
构造函数。
App.vue
是所有vue
组件(vue
文件)的汇总。
main.js
是记录了所有App
组件服务于那个容器,这个调用里面new Vue
构造函数。
index.html
就是最后所有单文件组件服务的容器。
安装步骤:①全局安装脚手架npm install -g @vue/cli
;②切换到创建项目的目录,然后使用命令创建项目vue create xxxx
;③启动项目npm run serve
。
babel.config.js
:ES6转ES5的转换器。
package.json&package-lock.json
:(包版本控制文件)作用注明各种依赖。
src→components→main.js
:该文件是整个项目的入口文件。
/针对main.js文件代码内容解释/
//引入Vue
import Vue from 'vue'
//引入App组件, 它是所有组件的父组件
import App from './App.vue'
//关闭vue的生产提示
Vue.config.productionTip = false
//创建Vue实例对象-vm
new Vue({
//写了$mount('#app')省略了el:'#app',
render: h => h(App), /单独在下节解释/
}).$mount('#app')
src→components文件夹
:所有的.vue文件
(页面组件)。
src→assets文件夹
:放的静态资源,如png图片、视频等。
public→index.html
:应用界面。
/针对index.html代码解释/
DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
/针对IE浏览器的一个特殊配置, 含义是让IE浏览器以最高的渲染级别渲染页面/
<meta http-equiv="X-UA-Compatible" content="IE=edge">
/开启移动端的理想视口/
<meta name="viewport" content="with=device-width,initial-scale=1.0">
/配置页签图标/
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
/配置网页标题/
<title><%= htmlWebpackPlugin.options.title %>title>
head>
<body>
/当浏览器不支持js时noscript中的元素就会被渲染/
<noscript>
<strong>strong>
noscript>
/容器/
<div id="app">div>
body>
html>
不同版本Vue中.vue.js
与vue.runtime.xxx.js
的区别: ①.vue.js
是完整版的Vue
,包含核心功能+模板解析器;②.vue.runtime.xxx.js
是运行版的Vue
只包含核心功能没有模板解析器。
不同版本Vue中是因为.vue.runtime.xxx.js
没有模板解析器,所以不能使用template
配置项,需要使用render()
接收到的createElement
函数去指定具体内容。
Vue脚手架默认隐藏了所有webpack相关的重要配置文件,例如webpack.config.js
文件,这时若想查看具体的webpack
配置,请执行vue inspect > output.js
命令,就会生成output.js
展示文件(只读文件)。
使用vue.config,js
可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh
<template>
<div>
<h1 v-text="msg" ref="title">h1> /这就相当于id/
<button ref="btn" @click="showDOM">点我输出上方的Dom元素button>
<School ref="sch"/> /sch是组件的实例对象/
div>
template>
<script>
import School from './components/School'
export default {
name: 'App',
components: { School },
data(){
return { msg: '欢迎学习Vue!' }
},
methods: {
showDOM(){ console.log(this.$refs.title) } /直接获取到title的DOM元素/
}
}
script>
<style>style>
ref
属性的作用:被用来给元素或子组件注册引用信息(id的代替者),应用在html
标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(VC
)。
ref
属性的使用方式:打标识
或......
,获取this.$refs.xxx
。
//APP.vue文件
<template>
<div>
<Student name="李四" sex="女" age="18"/> /:age这样才能接收到变量在{{}}中运算/
div>
template>
<script>
import School from './components/Student'
export default {
name: 'App',
components: { Student },
}
script>
<style>style>
//Student.vue组件-props配置项的3种写法/
<template>
<div>
<h1>{{msg}}h1>
<h2>学生姓名:{{name}}h2>
<h2>学生性别:{{sex}}h2>
<h2>学生年龄:{{age}}h2>
div>
template>
<script>
export default {
name: 'Student',
/当data中命名与props中冲突, props优先/
data(){
return {
msg: '欢迎学习Vue!',
myAge:this.age, /这样就可以写方法修改值了/
}
},
/1.简单接收/
//props:['name','age','sex'],
/2.接收的同时对数据进行类型限制/
/* props:{
name:String,
age:Number,
sex: String
}, */
/3.接收的同时对数据进行类型+默认值的指定+必要性的限制/
props:{
name: { type:String,require:true }, /require:true代表name必须的/
age: { type:Number,default:99 }, /default:99代表age默认值99/
name: { type:String,require:true } /一般require与default不同时出现/
}
}
script>
<style>style>
配置项props
作用:让组件接收外部传过来的数据。props
是只读的,Vue底层会检测对props
的修改,如果进行 修改,就会发出警告,若业务需求确实需要修改,那么请复制props
的内容data
中一份,然后去修改data
中的数据。
示例代码-School组件
<template>
<div>
<h2 @click="showName">学校名称:{{name}}h2>
<h2>学校地址:{{addresss}}h2>
div>
template>
<script>
/引入局部混合/
inport {mixin,mixin2} from '../mixin'
export default {
name: 'School',
data(){
return { name: '尚硅谷',address:'北京' }
},
mixins:[mixin,mixin2], /使用局部混合/
/*methods: {
showName(){ console.log(this.name) }
}*/
}
script>
示例代码-Student组件
<template>
<div>
<h2 @click="showName">学生姓名:{{name}}h2>
<h2>学校性别:{{sex}}h2>
div>
template>
<script>
inport {mixin,mixin2} from '../mixin'
export default {
name: 'Student',
data(){
return { name: '张三',sex:'男' }
},
mixins:[mixin,mixin2], /使用局部混合/
/* methods: {
showName(){ console.log(this.name) }
}*/
}
script> /showName是两个组件相同的配置/
/mixin.js文件,文件名自定义/
export const mixin = {
methods: {
showName(){ console.log(this.name) }
}
}
export const mixin2 = {
methods: {
showName(){ console.log('你好') }
}
}
/在main.js文件中, 引入全局混合/
import {mixin,mixin2} from './mixin'
Vue.mixin(mixin)
Vue.mixin(mixin2)
mixin
混合(混入)作用:可以将多个组件公用的配置提取成一个混入对象,局部混合mixins:['xxx']
,全局混入Vue.mixin(xxx)
。
/定义插件-plugins.js文件-写法1/
const obj = {
install(){ console.log('你好,我是插件!') }
}
export default obj
/定义插件-plugins.js文件-写法2/
export default {
install(Vue){
/定义混入等等/
Vue.mixin({
methods: { showName(){ console.log('你好') } }
})
}
}
/在main.js文件中, 引入插件/
import plugins from './plugins'
Vue.use(plugins); /使用插件/
scoped
样式作用:让样式在局部生效,防止冲突。写法。
此处省略了,如果有需要在更新。
DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<title>localStoragetitle>
head>
<body>
<button onclick="saveData()">保存数据button>
<button onclick="readData()">读取数据button>
<button onclick="deleteData()">删除数据button>
<button onclick="deleteAllData()">清空数据button>
body>
<script type="text/javascript">
let p = { name:'张三',age:18 }
function saveData(){
localStorage.setItem('msg','你好!'); /这里将前面的window省略了/
localStorage.setItem('person',JSON.stringify(p))
}
function readData(){
localStorage.setItem('msg','你好!')
let result = localStorage.setItem('person')
JSON.parse(result)
}
function deleteData(){
localStorage.removeItem('msg'); /只删除了msg/
}
function deleteAllData(){
localStorage.clear(); /清空所有缓存/
}
script>
html>
DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<title>sessionStoragetitle>
head>
<body>
<button onclick="saveData()">保存数据button>
<button onclick="readData()">读取数据button>
<button onclick="deleteData()">删除数据button>
<button onclick="deleteAllData()">清空数据button>
body>
<script type="text/javascript">
let p = { name:'张三',age:18 }
function saveData(){
sessionStorage.setItem('msg','你好!')
sessionStorage.setItem('person',JSON.stringify(p))
}
function readData(){
sessionStorage.setItem('msg','你好!')
let result = sessionStorage.setItem('person')
JSON.parse(result)
}
function deleteData(){
sessionStorage.removeItem('msg'); /只删除了msg/
}
function deleteAllData(){
sessionStorage.clear(); /清空所有缓存/
}
script>
html>
示例代码-School组件
/效果: 点击按钮把name给App组件/
<template>
<div class="school">
<h2>学校名称:{{name}}h2>
<h2>学校地址:{{addresss}}h2>
<button @click="sendSchoolName">把学校名给Appbutton>
div>
template>
<script>
export default {
name: 'School',
props: ['getSchoolName'],
data(){ return { name: '尚硅谷',address:'北京' } },
methods: {
sendSchoolName(){ this.getSchoolName(this.name) }
}
}
script>
<style lang="less" scoped>
.school{ background-color: red; }
style>
示例代码-Student组件
<template>
<div class="student">
<h2>学生名称:{{name}}h2>
<h2>学生年龄:{{age}}h2>
<button @click="sendStudentName">把学生名给Appbutton>
div>
template>
<script>
export default {
name: 'Student',
data(){ return { name: '张三',age:18 } },
methods: {
sendStudentName(){
/触发Syudent组件实例身上的atguigu事件/
this.$emit('atguigu',this.name)
}
}
}
script>
<style lang="less" scoped>
.student{ background-color: pink; }
style>
代码示例 - App组件
<template>
<div class="app">
<h1 class="title">{{msg}}h1>
/方法一: 通过父给子传递函数类型props实现:子给父传递数据/
<School :getSchoolName="getSchoolName"/>
/方法二: 通过父给子绑定一个自定义事件:子给父传递数据(第1种写法: 使用@或v-on)/
<Student @atguigu="demo"/>
/方法三: 通过父给子绑定一个自定义事件:子给父传递数据(第2种写法: 使用ref),灵活性强(如可以绑定时器)/
<Student ref="student"/>
div>
template>
<script>
import School from './components/School'
import Student from './components/Student'
export default {
name: 'App',
components: { School,Student },
data(){ return { msg: '欢迎学习Vue!' } },
methods: {
getSchoolName(name){ console.log('APP收到啦学校名',name) },
demo(name){ console.log('APP收到学生名!',name) }
},
mounted(){ /方法三触发/
this.$refs.student.$on('atguigu',this.demo)
}
}
script>
<style>
.title{ color:green }
style>
示例代码-Student组件
<template>
<div class="student">
<h2>学生名称:{{name}}h2>
<h2>学生年龄:{{age}}h2>
<button @click="sendStudentName">把学生名给Appbutton>
<button @click="unbind">解绑atguigu事件button>
<button @click="unbinds">解绑atguigu与demo事件button>
<button @click="death">销毁当前Student组件的实例button>
div>
template>
<script>
export default {
name: 'Student',
data(){ return { name: '张三',age:18 } },
methods: {
sendStudentName(){
this.$emit('atguigu',this.name),
this.$emit('demo')
},
unbind(){ this.$off('atguigu')}, /只适用于解绑一个自定义事件/
unbinds(){ this.$off(['atguigu','demo'])}, /解绑多个自定义事件/
unAllbind(){this.$off()}, /解绑所有自定义事件/
death(){ this.$destroy()} /销毁了当前Student组件的实例/
}
}
script>
<style lang="less" scoped>
.student{ background-color: pink; }
style>
代码示例 - App组件
<template>
<div class="app">
<h1 class="title">{{msg}}h1>
<Student @atguigu="demo" @demo="m1"/>
div>
template>
<script>
import School from './components/School'
import Student from './components/Student'
export default {
name: 'App',
components: { School,Student },
data(){ return { msg: '欢迎学习Vue!' } },
methods: {
getSchoolName(name){ console.log('APP收到啦学校名',name) },
demo(name){ console.log('APP收到学生名!',name) },
m1(){ console.log('demo事件被触发了!')}
},
}
script>
<style>
.title{ color:green }
style>
组件自定义事件定义:一种组件通讯的方式,适用于:子组件==>父组件
。
组件自定义事件使用场景:A是父组件,B是子组件,B想给A传数值,那么就要在A中给B绑定自定义事件 (事件的回调在A中
)。
绑定自定义事件:第①种写法在父组件中
或
;第②种写法在父组件中
;若想让自定义事件只能触发一次,可以使用once
修饰符或$once
方法。
触发自定义事件:this.$emit('atguigu',数据)
。
解绑自定义事件:this.$off('atguigu')
。
组件上也可以绑定原生DOM
事件,需要使用native
修饰符。
⚠️注意⚠️:通过this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么牌子在methods中,要么用箭头函数
否则this
指向会出问题!
全局事件总线:可以做到任意组件间通信,不是一个新的API,属于总结的组件间数据传输的经验。
/示例代码:main.js文件-安装全局事件总线/
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
el:'#app',
render: h => h(App),
beforeCreate(){
Vue.prototype.$bus = this, /安装全局事件总线,$bus是自定义的可以取任意名/
}
})
安装全局事件总线语法格式如下
new Vue({
......
beforeCreate(){ Vue.prototype.$bus = this },
......
})
使用事件总线示例语法如下
1.接收数据:A组件想接收数据, 则在A组件中给$bus绑定自定义事件, 事件的回调留在A组件自身
methods(){ demo(data){......} },
......
mounted(){ this.$bus.$on('xxx', this.demo) }
2.提供数据: this.$bus.$emit('xxx', 数据)
最好在beforeDestroy
钩子中,用$off
去解绑==当前组件所用到的=事件。
vue-router
定义:vue
的一个插件库,专门用来实现SPA应用。
单页面Web
应用。
整个应用只有一个完整页面。
点击页面中的导航连接不会刷新页面,只会做页面的局部更新。
数据需要通过ajax
请求获取。
什么是路由? 一个路由就是一组映射关系(value-key
)。key
为路径,value
可能是function
或component
。的对应关系,多个路由,需要路由器的管理。
路由的分类
▶️后端路由❶理解:value
是function
用于处理客户端提交的请求❷工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回数据。
▶️前端路由❶理解:value
是component
,用于页面展示页面内容❷工作过程:当浏览器的路径改变时,对应的组件就会显示。
1.安装路由命令npm i vue-router
。
2.在main.js
文件中引入路由并应用。
3.编写router
配置项,语法结构如下
//1.引入VueRouter
import VueRouter from 'vue-router'
//2.引入路由组件
import About from '../component/About'
//创建实例对象, 管理一组组的路由规则
export default new VueRouter({
routes:[
{path:'/about',component:About}
]
})
4.实现切换(active-class
可配置高亮样式)
About
5.指定展示位置
演示代码如下
<-- 原始html中我们使用a标签实现页面的跳转 -->
<a class="list active" href="./abuot.html">Abouta> <--list是写好的样式-->
<a class="list" href="./home.html">Homea>
<--App.js文件代码如下-->
<template>
......
<--Vue中借助router-link标签实现路由的切换-->
<router-link class="list" active-class="active" to="/about">Aboutrouter-link>
<router-link class="list" active-class="active" to="/home">Homerouter-link>
...
<--指定组件的呈现位置-->
<router-view>router-view>
...
......
template>
/main.js文件-引入路由/
import Vue from 'vue'
import App from './App.vue' ,/1.引入路由/
import VueRouter from './App.vue'
Vue.config.productionTip = false
VUe.use(VueRouter) ,/2.应用插件/
new Vue({
el:'#app',
render: h => h(App),
/3.引入后就可以写router配置项了, router必须是函数/
router: router,
})
/router文件夹中index.js文件-该文件专门用于穿件整个应用的路由器/
import VueRouter from 'vue-router'
,/1.引入组件/
import About from '../component/About' ,/这是写好的组件/
import Home from '../component/Home'
,/2.创建一个路由器/
const router = new VueRouter({
routes:[
{path:'/about',component:About},
{path:'/home',component:Home}
]
})
,/3.暴露/
export default router
,/直接简写如下/
/* export default new VueRouter({
routes:[
{path:'/about',component:About},
{path:'/home',component:Home}
]
}) */
1.路由组件通常存放在pages
文件夹,以班组间通常存放在components
文件夹。
2.通过雀环,"隐藏"了路由组件,默认是被销毁的,需要的时候再去连接。
3.每个组件都有自己的$route
属性,里面存储着自己的路由信息。
4.整个应用只有一个router
,可以通过组件的$router
属性获取到。
1.配置路由规则,使用children
配置项。
router:[
{
path:'/about',
component:About,
children:[
{path:'/news',component:News},
{path:'/message',component:Message}
]
}
]
2.跳转(需要写完整路径)
News
1.传递参数
<-- 跳转并携带query参数,to的字符串写法 -->
跳转
<-- 跳转并携带query参数,to的对象写法 -->
跳转
2.接收参数
$route.query.id
$route.query.title
命名路由作用:简化路由的跳转。
1.给路由命名
{ 这是一个三级路由
path:'/about',
component:About,
children:[
{ path:'test',
component:Test,
children:[
{ name:'hello', //⚠️给路由命名
path:'welcome',
component:Hello,}
]
}
]
}
2.简化跳转
<-- 简化前, 需要写完整的路径 -->
跳转
<-- 简化后, 直接通过名字跳转 -->
跳转
<-- 简化写法配合传递参数 -->
跳转
1.配置路由,声明接收params
参数
{
path:'/about',
component:About,
children:[
{ path:'test',
component:Test,
children:[
{ name:'hello',
path:'welcome/:id/:title', ⚠️//使用占位符声明接收params参数
component:Hello,}
]
}
]
}
2.传递参数
<-- 跳转并携带params参数, to的字符串写法 -->
跳转
<-- 跳转并携带params参数, to的对象写法 -->
跳转
⚠️特别注意⚠️:路由携带params
参数时,若使用to
的对象写法,则不能使用path
配置项,必须使用name
配置项。
3.接收参数
{{$route.params.id}}
{{$route.params.title}} ⚠️这个参数接收方式不简便, 在**⑵props配置项**中讲解简便写法
{
path:'/about',
component:About,
children:[
{ path:'test',
component:Test,
children:[
{ name:'hello',
path:'welcome/:id/:title', ⚠️//使用占位符声明接收params参数
component:Hello,
//▶️props第一种写法, 值为对象, 该对象所有key-value都会以props形式传给Hello组件
// props: {id:666,title:'你好'},
//▶️props第二种写法,值为布尔值,若为真就会把该路由组件收到的所有params参数,以props的形式传给Hello组件
// props: true,
//▶️props第三种写法, 值为函数
props($route){ //⚠️或把$route换成解构赋值{query}形式
return {id:$route.params.id,title:$route.params.title}
}
}
]
}
]
}
replace
属性的作用:控制路由跳转时操作浏览器历史记录的模式。
浏览器的历史记录有两种写入方式:分别为push
与replace
,push
是追加历史记录,replace
是替换当前记录,路由跳转时默认为push
。
如何开启replace
模式:
。
编程式路由导航作用:不借助
实现路由跳转,让路有更加灵活。
示例代码如下
$router的两个API, push()&&replace()
this.$router.push({
name:'xiangqing',
params:{id:xxx,title:xxx}
})
this.$router.replace({
name:'xiangqing',
params:{id:xxx,title:xxx}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //去往指定页面, 可前进也可后退
缓存路由组件作用:让不展示的路由组件保持挂载,不被销毁,语法结构如下
⚠️如不写include属性,代表所有的router-view中所有层级都缓存不销毁
⚠️缓存多个
思考一种情况,若某组件即含有缓存路由组件也有类似含有定时器这样需要销毁的组件,怎么办?
要解决该问题就引入了两个新的声明周期钩子,▶️activated()
激活的、▶️deactivated)()
失活的,该钩子是路由组件独有的!
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
路由守卫作用:对路由进行权限控制。
路由守卫分类:▶️全局守卫、▶️独享守卫、▶️组件内守卫。
1.全局前置路由守卫-⑴初始化时会调用 ⑵每次路由切换之前调用
router.beforeEach((to,form,next) => {
⚠️`meta.isAuth`是在写路由信息时,定义在meta属性中isAuth信息,书写习惯
if(to.meta.isAuth) { //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu') {
alert('暂时无权限查看')
} else { next() //放行 }
}
});
2.全局后置路由守卫-⑴初始化时被调用 ⑵每次路由切换之后调用
router.afterEach((to,form) => {
if(to.meta.title) {
document.title = to.meta.title //修改网页的title
} else {
next( document.title = '登录页' )
}
})
⚠️注意⚠️:独享路由守卫没有后置路由!!!
path:'/about',
component:About,
children:[
{ path:'test',
component:Test,
beforeEnter(to,form,next){
if(localStorage.getItem('school') === 'atguigu') {
alert('暂时无权限查看')
} else {
next()
}
}
}
]
export default {
name:'About',
mounted(){},
beforeRouterEnter(to,form,next){}, //通过路由规则, 进入该组件时被调用
beforeRouterLeave(to,form,next){}, //通过路由规则, 离开该组件时被调用
};
对于一个url
来说,什么是hash
值?----- #
及其后面的内容就是hash
值。
hash
值不会包含在HTTP
请求中,即:hash
值不会带给服务器。
▶️1. hash
模式:
❶地址中永远带着#
号,不美观
❷若以后将地址通过第三方手机app
分享,若app
校验严格,则地址会被标记为不合适
❸兼容性好。
▶️2. history
模式:
❶地址干净,美观
❷兼容性和hash
模式相比略差
❸应用部署上线时需要后端人员支持,解决刷新页面服务端404问题。
vuex
是什么:vuex
是vue
中实现集中式状态(数据)管理的一个插件,对vue
应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通讯的方式,且适用于任意组件间通信。
什么时候使用vuex
: 多个组件同一依赖状态;来自不同组件的行为要变成同一状态。(共享)
❶首先使用npm i vuex
安装Vuex
;
❷其次Vue.use(Vuex)
全局应用;
❸建立store仓库
管理上图的3个对象actions
、mutations
、state
,第一种方法Vuex
文件夹中创建store.js
文件;第二种方法store
文件夹中创建index.js
文件,文件内容如下
/该文件用于创建Vuex中最为核心的store/
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//准备actions用于响应组件中的动作
const actions = {
jia(context,value){context.commit('JIA',value)}
}
//准备mutations用于操作数据(state)
const mutations = {
JIA(state,value){state.sum += value}
}
//准备state用于存储数据
const state = { sum:0 }
//创建并导出store
export default new Vuex.Store({
actions,
mutations,
state
})
❹使所有的vc
都可以看到store
。
/在组件中读取vuex中的数据:/
$store.state.sum;
/组件中修改vuex中的数据:/
$store.dispatch('mutations中的方法名',数据)
$store.commit('action中的方法名',数据)
上文中我们提到三个常用的配置项actions mutations state
,他们是vuex
文件中必须配置的项,而getter
就作为知识扩展进行了解。
/该代码也是写在Vuex中最为核心的store的文件中/
//准备state用于将state中的数据进行加工
const getter = {
bigSum(state){
return state.sum*10
}
}
export default new Vuex.Store({
actions,
mutations,
state,
getter
})
总结: 这个Vuex
中的getter
对象与计算属性computed
非常类似,只不过更好的在Vuex
中复用。