【记录】Vue2

Vue的特点

1、使用组件化模式,提高代码复用率、且让代码更好维护。
2、声明式编码,让编码人员无需直接操作DOM,提高开发效率。
3、使用虚拟DOM+优秀的Diff算法,尽量复用DOM结点。

  • 原生js:数据=>real-DOM
  • Vue:数据=>virtual-DOM=>real-DOM

Diff算法

Vue2.0

【MVVM模型】

V-VM-M
M

  • Model模型
  • data中的数据

V

  • View视图
  • dom(模板)

VM

  • ViewModel视图模型
  • Vue实例对象

【新建实例】

<body>
	<div id="root">
		<h1>hello, {{ name }}</h1>
	</div>
	<script type="text/javascript">
	        new Vue({
	            el: '#root', 
	            //el用于指定当前Vue实例为哪个容器服务(Vue实例和容器一一对应)。
	            data: {
	            //data用于存储供el所指定的容器使用的数据。对象式
	                name: 'Vue'
	            },
				methods:{
					showInfo(){
					//this指向Vue实例对象;如果改成箭头函数,指向Window对象
					}
				}
	        });
	</script>
</body>
//el的第二种写法:异步挂载
const v = new Vue({
	data:{}
})
v.$mount('#root')

//data的第二种写法:函数式
//组件时必须使用函数式
new Vue({
	el: '#root',
	data: function(){
		console.log(this)//匿名函数,this指的是Vue实例对象
		return {
			
		}
	}
})
new Vue({
	el: '#root',
	data: ()=>{
		console.log(this)//箭头函数,没有this,this指的是全局的Window对象
		return {
			
		}
	}
})

【语法】

//插值语法:作用于标签体内
//{{}}
//可以写data里的属性、vm上的属性、Vue原型上的属性、js表达式
<h1>hello, {{ name }}</h1>

//指令语法:作用于标签属性

//v-bind或简写:
//单向绑定(数据从data流向页面)
<a v-bind:href="url"></a>
<a :href="url"></a>

//v-model
//v-model:value简写成v-model
//双向绑定
<input v-model:value="url"/>
<input v-model="url"/>
//v-model修饰符
//lazy:失去焦点再收集数据
<textarea v-model="userInfo.other"></textarea>
//number:输入字符串转为有效数字
<input type="number" v-model.number="userInfo.age"/>
//trim:输入首尾空格过滤
<input type="text" v-model.trim="userInfo.account"/>
//表单
//v-model收集的是value值,用户输入的是value值。
<input type="text" v-model="userInfo.account"/>
//v-model收集的是value值,且要给标签配置value值。
<input type="radio" name="sex" v-model="userInfo.sex" value="male"/>
<input type="radio" name="sex" v-model="userInfo.sex" value="female"/>
//没有配置input的value属性,那么收集的就是checked值。
//配置了input的value属性:v-model的初始值是非数组,那么收集的是checked值。v-model的初始值是数组,那么收集的是value组成的数组。
<input type="checkbox" v-model="userInfo.hobby" value="study"/>
<input type="checkbox" v-model="userInfo.hobby" value="game"/>
<input type="checkbox" v-model="userInfo.hobby" value="eat"/>

//v-on
//v-on:click简写成@click
//事件监听
<button v-on:click="showInfo1"></button>//默认都会传event
<button @click="showInfo($event, 11)"></button>

//事件修饰符
//prevent: 阻止默认事件
<a :href="url" @click.prevent="showInfo"></a>//阻止链接跳转
//stop: 阻止事件冒泡
//once: 事件只触发一次
//capture: 使用事件的捕捉模式
//self: 只有event.target是当前操作的元素时才触发事件
//passive: 事件的默认行为立即执行,无需等待事件回调执行完毕
//-----
//事件修饰符可以连续写。
<a :href="url" @click.prevent.stop="showInfo"></a>//阻止链接跳转

//条件渲染
//v-show
//通过display: none来实现;不展示的DOM元素未被移出,仅仅是使用样式隐藏掉。
//适用于切换频率较高的场景。
<p v-show="a===b">Vue</p>
//v-if
//不展示的DOM元素直接移除。
//适用于切换频率较低的场景。
//v-if和v-else-if、v-else之间不允许被任何标签打断。
<p v-if="n===1">Vue</p>
<p v-else-if="n===3">React</p>
<p v-else>Angular</p>

//template
//template不影响结构(渲染后网页代码不显示template标签,但是里面的代码体是一块的;如果用div会破坏结构且渲染后会显示div标签)。
<template v-show="n===1">
	<p>Vue</p>
	<p>React</p>
	<p>Angular</p>
</template>

//v-text
//会替换掉节点中的内容。
//不能识别html结构标签。
<div v-text="name">aa</div>

//v-html
//会替换掉节点中的内容。
//能识别html结构标签。
//具有安全性问题:容易造成xss攻击。
<div v-html="name"></div>

//v-cloak
//没有值,Vue实例创建完毕并接管容器后会删除v-cloak属性。
//使用css配合v-cloak可以解决网速慢时页面展示出{{}}的问题。
<style>
	[v-cloak]{
		display: none;
	}
</style>
<h2 v-cloak>{{name}}</h2>
<script></script>

//v-once
//在初次动态渲染后,就视为静态内容。以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<h2 v-once>初始化的n值是:{{n}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>

//v-pre
//跳过其所在节点的编译过程。可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

【列表渲染】

key的内部原理
(1)虚拟DOM中key的作用

  • key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM和旧虚拟DOM的差异比较。

(2)对比规则

  • 旧虚拟DOM中找到了与新虚拟DOM相同的key:
    若虚拟DOM中内容没变,直接使用之前的真实DOM。
    若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
  • 旧虚拟DOM中未找到与新虚拟DOM相同的key:
    创建新的真实DOM,随后渲染到页面。

用index作为key可能会引发的问题:
(1)若对数据进行逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新(界面效果没问题,但效率低)。
(2)若结构中包含了输入类的DOM,会产生错误DOM更新(界面效果有问题)。


开发中如何选择key:
(1)最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,则使用index作为key是没有问题的。

//v-for
//遍历数组(数组元素,数组下标)
<ul>
	<li v-for="(p, index) of 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">
		{{char}}-{{index}}
	</li>
</ul>
//从1开始遍历指定次数
<ul>
	<li v-for="(number, index) of 5" :key="index">
		{{number}}-{{index}}
	</li>
</ul>

列表过滤

new Vue({
	el: '#root',
	data: {
		keyWord: '',
		persons:[...],
		filPersons: []
	},
	//用watch
	watch:{
		keyWord:{
			immediate: true,
			handler(val){
				this.filPersons = this.persons.filter((p)=>{
					return p.name.indexOf(val) !== -1;
				})
			}
		}
	}
	//用computed
	filPersons(){
		return this.persons.filter((p)=>{
			return p.name.indexOf(this.keyWord) !== -1;
		})
	}
})

列表排序

new Vue({
	el: '#root',
	data: {
		keyWord: '',
		sortType: 0,//0原顺序 1降序 2升序
		persons:[...],
		filPersons: []
	},
	filPersons(){
		const arr =  this.persons.filter((p)=>{
			return p.name.indexOf(this.keyWord) !== -1;
		});
		if(this.sortType){
			arr.sort((p1, p2)=>{
				return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age;
			});
		}
		return arr;
	}
})

【计算属性computed】

(1)原理

  • 底层借用了Object.defineproperty方法提供的getter和setter。

(2)优势

  • 与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。

(3)计算属性最终会出现在vm上,直接读取使用即可。
(4)如果计算属性要被修改,则必须写set函数去相应修改,且set中要引起计算时依赖的数据发生改变。

new Vue({
	el: '#root',
	data: {
		firstName: '张',
		lastName: '三'
	},
	computed: {
		//完整写法
		fullname: {
			//get在初次读取fullName或所依赖的数据发生变化时调用。
			get(){
				return this.firstName + '-' + this.lastName;
			},
			set(value){
				const arr = value.split('-');
				this.firstName = arr[0];
				this.lastName = arr[1];
			}
		}
		//简写,只get不set时
		fullName:function(){}
		fullName(){}
	}

})

【监视属性watch】

(1)当被监视的属性发生变化时,回调函数自动调用。
(2)监视的属性必须存在,才能进行监视。

const vm = new Vue({
	el: '#root',
	data: {
		isHot: true
	},
	watch: {
		isHot: {
			immediate: true,//初始化时调用handler
			handler(newValue, oldValue){
				...
			}
		}
	
	}

})
	
//或者
vm.$watch('isHot', {
	immediate: true,
	handler(newValue, oldValue){
		...
	}
});
vm.$watch('isHot', function(newValue, oldValue){
	...
});

深度监视
(1)Vue中的watch默认不监测对象内部值的改变。通过配置deep:true可以监测对象内部值的改变。
(2)Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以。

new Vue({
	el: '#root',
	data: {
		numbers: {
			a: 1,
			b: 2
		}
	},
	watch: {
		//当numbers的任意属性变化时,该方法无法监视到。因为numbers里面存的是引用地址,当属性变化时,引用地址未发生变化。
		numbers: {
			handler(newValue, oldValue){
				...
			}
		}
		//只监视a属性。
		'numbers.a': {
			handler(newValue, oldValue){
				...
			}
		}
		//监视numbers的任意属性发生变化。
		numbers: {
			deep: true,
			handler(newValue, oldValue){
				...
			}
		}
		//简写,只需要handler时才可以简写。
		numbers(newValue, oldValue){
			...
		}
	}

})

computed和watch的区别
(1)computed能完成的功能,watch都可以完成。
(2)watch能完成的功能,computed不一定能完成,如:watch可以进行异步操作。

(1)所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
(2)所有不被Vue管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
???

【过滤器filters】

<div id="root">
	//{{ 参数 | 过滤器名 }}或者v-bind:属性="参数 | 过滤器名"
	//默认将第一个参数作为过滤器的第一个参数。
	//多个过滤器可以串联,前一个过滤器的结果是后一个过滤器的参数。
	<h3>{{time | timeFormatter('YYYY_MM_DD') | mySlice}}</h3>
</div>

new Vue({
	el: '#root',
	data:{
		time: 假装我是一个时间戳
	},
	//局部过滤器
	filters:{
		timeFormatter(value, str="YYYY年MM月DD日 HH:mm:ss"){
			//这里的value是time。
			return dayjs(value).format(str);
		},
		mySlice(value){
			//这里的value是timeFormatter的返回值。
			return value.slice(0,4);
		}
	}
})

//全局过滤器
Vue.filter('mySlice', function(value){
	return value.slice(0, 4);
});

【自定义指令directives】

指令名如果是多个单词,要使用kebab-case命名方式(短横线命名法),不要用camelCase命名(小驼峰命名法)。

new Vue({
	el: '#root',
	data: {},
	directives: {
		//该方法在bind、update时调用。
		big(element, binding){
			...
		},
		fbind:{
			//指令与元素成功绑定时调用。
			bind(element, binding){
				...
			},
			//指令所在元素被插入页面时调用。
			inserted(element, binding){
				...
			},
			//指令所在模板结构被重新解析时调用。
			update(element, binding){
				...
			}
		}
	}

})



//全局指令
Vue.directive('fbind', {
	//指令与元素成功绑定时调用。
	bind(element, binding){
		...
	},
	//指令所在元素被插入页面时调用。
	inserted(element, binding){
		...
	},
	//指令所在模板结构被重新解析时调用。
	update(element, binding){
		...
	}
})


【绑定class样式】

//字符串写法: 适用于样式的类名不确定,需要动态指定。
<div id="root">
	<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
</div>
//数组写法:适用于要绑定的样式个数不确定、名字也不确定。
<div id="root">
	<div class="basic" :class="classArr" @click="changeMood">{{name}}</div>
</div>
//对象写法:适用于要绑定的样式个数确定、名字也确定,但要动态决定用不用。
<div id="root">
	<div class="basic" :class="classObj" @click="changeMood">{{name}}</div>
</div>

new Vue({
	el: '#root',
	data: {
		name: 'aaa',
		mood: 'normal',
		classArr: ['c1', 'c2', 'c3'],
		clssObj:{
			c1: false,
			c2: true,
			c3: false
		}
		
	},
	methods: {
		changeMood(){
			this.mood = 'happy';
		}
	}

})

【绑定style样式】

<div id="root">
	<div class="basic" :style="{fontSize: fSize + 'px'}">{{name}}</div>
</div>
//对象写法
<div id="root">
	<div class="basic" :style="styleObj">{{name}}</div>
</div>
//数组写法
<div id="root">
	<div class="basic" :style="styleArr">{{name}}</div>
</div>

new Vue({
	el: '#root',
	data: {
		name: 'aaa',
		fSize: 40,
		styleObj:{
			fontSize: '40px'
			//注意将短横线命名转化为小驼峰命名
		},
		styleArr:[
			{
				...
			},{
				...
			}
		]
		
	}
})

【scoped样式】

  • 作用
    让样式在局部生效,防止冲突。
  • 写法
    <style scoped>

【过渡与动画】

???
https://cn.vuejs.org/v2/guide/transitions.html

  1. 准备好样式:
  • 元素进入的样式:
    v-enter:进入的起点
    v-enter-active:进入过程中
    v-enter-to:进入的终点
  • 元素离开的样式:
    v-leave:离开的起点
    v-leave-active:离开过程中
    v-leave-to:离开的终点
  1. 使用<transition>包裹要过度的元素,并配置name属性:
    <transition name=“hello”>
    <h1 v-show=“isShow”>你好啊!</h1>
    </transition>
  2. 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。
//搭配animate.css使用
<transition-group
	appear
	name="animate__animated animate__bounce"
	enter-active-class="animate__headShake"
	leave-active-class="animate__headShake"
>
		<h1 v-show="!isShow" key="1">a</h1>
		<h1 v-show="isShow" key="2">b</h1>
</transition-group>

【键盘事件】

(1)Vue中常见的按键别名
<input @keyup.enter=“showInfo” />

  • enter:回车。
  • delete:删除。(捕获“delete”键和“Backspace”键)
  • esc:退出。
  • space:空格。
  • tab:换行。(必须配合keydown使用,因为tab一般用于切换焦点,当按下tab键时会切换到别的元素,使得keyup事件无法触发。)
  • up:上。
  • down:下。
  • left:左。
  • right:右。

(2)系统修饰键:ctrl、alt、shift、meta(win或command)

  • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才会触发。
  • 配合keydown使用:正常触发事件。

(3)Vue未提供别名的按键可以使用按键原始的key值去绑定,但注意要将key值转换为短横线命名法(如@keyup.Caps-Lock="")。
(4)也可以使用keyCode去指定具体的按键(如@keyup.13=""),但是不推荐使用。
(5)也可以自定义按键别名(Vue.config.keyCodes.自定义键名=键码)。
(6)按键事件也可以连续写。如@keydown.ctrl.y=""。

【数据代理】

Object.defineProperty
P11-P13

【监测数据】

P33-P37妙蛙种子
Vue会监视data中所有层次的数据。
(1)如何监测对象中的数据

  • 通过setter实现监视,且要在new Vue时就传入要监测的数据。
  • 对象中后追加的属性,Vue默认不做响应式处理。
  • 如需给后追加的属性做响应式,则使用Vue.set(target, propertyName/index, value)或vm.$set(target, propertyName/index, value)。

(2)如何监测数组中的数据

  • 对于纯数组中的元素,Vue没有设置getter和setter。(对于对象,Vue有设置getter和setter;对于对象数组中的对象,Vue有设置getter和setter)
  • 通过包裹数组更新元素的方法实现,本质就是做了两件事:
    ①调用原生的方法对数组进行更新。
    ②重新解析模板,更新页面。

(3)在Vue中修改数组中的某个元素一定要使用如下方法:
①push()、pop()、shift()、unshift()、splice()、sort()、reverse()
②Vue.set()或vm.$set()


注意Vue.set()或vm.$set()中的target不能是vm或者是vm的根数据对象(vm._data)。

【生命周期】

生命周期(生命周期回调函数、生命周期函数、生命周期钩子)

  • 是Vue在关键时刻帮我们调用的一些特殊名称的函数。
  • 生命周期函数中的this指向是vm或组件实例对象。

new Vue()

初始化生命周期、事件,但数据代理还未开始。

【beforeCreate】

  • 无法通过vm访问到data中的数据、methods中的方法。
  • 初始化数据检测、数据代理。

【created】
可以通过vm访问到data中的数据、methods中的方法。

解析模板,生成虚拟DOM(内存中),页面还不能显示解析好的内容。

【beforeMount】

  • 此时页面呈现的是未经Vue编译的DOM结构;
  • 此时所有对DOM的操作,最终都不奏效。

将内存中的虚拟DOM转为真实DOM插入页面。

【mounted】

  • 此时页面呈现的是经过Vue编译的DOM;
  • 此时所有对DOM的操作均有效(尽可能避免);
  • 至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作。

【beforeUpdate】

  • 此时数据是新的,但页面是旧的(页面尚未和数据保持同步)。

根据新数据,生成新的虚拟DOM,随后与旧的虚拟DOM进行比较,最终完成页面更新(即Model->View的更新)。

【updated】

  • 此时数据是新的,页面也是新的(页面和数据保持同步)。

调用vm.$destroy()。

【beforeDestroy】

  • 此时vm中的所有:data、methods、指令等都是处于可用状态,马上要执行销毁过程。此时update不会触发效果。
  • 一般在此进行:关闭定时器、取消订阅消息、解绑自定义事件等收尾操作。

移除监视、子组件、事件监听器。

【destroyed】

  • 销毁后自定义事件会失效,但原生DOM事件依然有效。

【模块】

概念

  • 向外提供特定功能的JS程序(一般就是一个JS文件)。

作用

  • 复用JS,简化JS编写,提高JS运行效率。

模块化

  • 当应用中的JS都以模块来编写的,那么这个应用就是一个模块化的应用。

【组件】

概念

  • 实现应用中局部功能代码和资源的集合(html/css/js/image…)。

作用

  • 复用编码,简化项目编码,提高运行效率。

组件化

  • 当应用中的功能都是多组件的方式来编写的,那么这个应用就是一个组件化的应用。

VueComponent

  • 组件本质是一个名为VueComponent的构造函数,是Vue.extend生成的。
  • <school></school>,Vue在解析时会创建school组件的实例对象即new VueComponent(options)。
  • this的指向
    (1)组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数,this都指向VueComponent实例对象。
    (2)new Vue()配置中:data函数、methods中的函数、watch中的函数、computed中的函数,this都指向Vue实例对象。
  • VueComponent的实例对象,简称vc(组件实例对象)。
    Vue的实例对象,简称vm。
  • VueComponent.prototype._proto_ === Vue.prototype
    让组件实例对象可以访问到Vue原型上的属性、方法。
    P59
    ![[Snipaste_2022-02-04_17-46-29.png]]

组件名

  • 一个单词组成:首字母小写or首字母大写。
  • 多个单词组成:短横线命名 or 大驼峰命名(需要Vue脚手架支持)。
  • 尽量回避HTML已有标签名。
  • 可以使用name配置项指定组件在开发者工具中呈现的名字。

组件标签

  • <school></school>
  • <school/>
    该方法必须在脚手架下才能使用。不使用脚手架时,会导致后续组件不能渲染。

非单文件组件

  • 一个文件包含多个组件。
  • 组件嵌套
    子组件必须在父组件之前声明。
<div id="root">
	<xuexiao></xuexiao>
	<xuesheng></xuesheng>
</div>

const school = Vue.extend({
	//el不要写,因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
	template: `<h1></h1>`,
	//使用函数式,多个组件之间的数据不会相互影响。避免组件被复用时,数据存在引用关系。
	data(){
		return{
			...
		}
	}
});
//可以简写,但是代码可读性差。
const school = {
	template: `<h1></h1>`,
	data(){
		return{
			...
		}
	}
};
const student = Vue.extend({
	template: `<h1></h1>`,
	data(){
		return{
			...
		}
	}
}); 
new Vue({
	el: '#root',
	//注册组件(局部注册)
	components:{
		xuexiao: school,
		xuesheng: student
	}
	//简写
	components:{
		school,
		student
	}
});
//注册组件(全局注册)
Vue.component('school', school);

单文件组件

  • 有多个文件,一个文件只包含一个组件。
//index.html
<!DOCTYPE html>
<html>
	<head>
	</head>
	<body>
		<div id="root"></div>
		<!-- 引入容器后再引入js,不然容器未注册。 -->
		<script type="text/javascript" src="../js/vue.js"></script>
		<script type="text/javascript" src="../main.js"></script>
	</body>
</html>


//main.js
import App from '';
new Vue({
	el:'#root',
	template: `<App></App>`,
	components:{
		App
	}
})

//App.vue
<template>
	<div>
		<School></School>
	</div>
</template>

<script>
	//引入组件
	import School from '';
	export default{
		name: 'App',
		components:{
			School
		}
	}
</script>

//子组件
<template>
</template>

<script>
	export default{
		name: 'School',
		data(){
			return{
			}
		}
	};
</script>

<style>
</style>

【Vue-CLI】

CLI(Command Line Interface)


├── node_modules
├── *public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── *main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

//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="width=device-width,initial-scale=1.0">
	 <!-- <%= BASE_URL %> = public/(./) -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
	 <!-- 配置网页标题 -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
	<!-- 当浏览器不支持js时,noscript中的元素就会被渲染。 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

vue.js和vue.runtime.xxx.js的区别

  • vue.js是完整版的vue,包含了核心功能和模板解析器。
  • vue.runtime.xxx.js是运行版的vue,只包含了核心功能,没有模板解析器。没有模板解析器,所以不能使用template配置项,需要使用render函数接受到的createElement函数去指定具体内容。
new Vue({
 router,
 store,
 render: h => h(App)
}).$mount('#app');
//等同于
new Vue({
	render(createElement){
		return createElement('h1', '你好');
	}
	//等同于
	render: createElement => {
		return createElement('h1', '你好');
	}
	//等同于
	render: h => h('h1', '你好');
}).$mount('#app');

配置文件

  • vue脚手架的默认配置被vue隐藏了。在命令行输入vue inspect > output.js即可查看。
  • 使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh。
  • 修改后需要重新npm run serve才可以。
//vue.config.js
//webpack commonjs???
module.exports = {
	pages:{
		index:{
			//入口
			entry: 'src/main.js'
		}
	},
	//关闭语法检查
	lintOnSave: false,
	//配置代理1
	//xxxx为转发给目的服务器的端口号。(注意:然后要将axios请求地址端口号改成本地端口号)
	//优点:配置简单,请求资源时直接发给前端(8080)即可。
	//缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
	//工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
	devServer:{  
		proxy:"http://localhost:xxxx"  
	},
	//配置代理2
	//优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
	//缺点:配置略微繁琐,请求资源时必须加前缀。
	/*  
		changeOrigin  
		用于控制请求头中的host值。默认值为true。
		设置为true时,服务器收到的请求头中的host为服务器的端口号。(说谎)
		设置为false时,服务器收到的请求头中的host为前端浏览器的真实端口号。(讲实话)
	*/
	devServer: {  
		 proxy: {  
			 '/api1': {// 匹配所有以 '/api1'开头的请求路径
			 target: 'http://localhost:5000',// 代理目标的基础路径  
			 changeOrigin: true,
			 ws: true, // websocket
			 pathRewrite: {'^/api1': ''}
			},  
			 '/api2': {// 匹配所有以 '/api2'开头的请求路径  
			 target: 'http://localhost:5001',// 代理目标的基础路径  
			 changeOrigin: true,  
			 pathRewrite: {'^/api2': ''}  
			}  
		}  
	}	

}

【ref属性】

  • 被用来给元素或子组件注册引用信息(id的替代者)。
  • 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象。
<template>
	<div>
		<h1 ref="title"></h1>
		<School ref="sch"/>
	</div>
</template>

showDOM(){
	console.log(this.$refs)
	console.log(this.$refs.title)
	console.log(this.$refs.sch)
}

【props配置】

  • 功能
    让组件接收外部传过来的数据。
//父组件
<template>
	<div>
		//加:使得右边解析成表达式,则传过去的值为数值18。
		<Student name="张三" sex="男" :age="18"/>
	</div>
<template>
//子组件
export default{
	name: 'Student',
	data(){
		return{
		}
	},
	//只接收
	props:['name', 'age', 'sex']
	//接收并限制类型
	props:{
		name: String,
	 	age: Number,
		sex: String
	}
	//接收并限制类型、限制必要性、指定默认值
	props: {
		name:{
			type: String,
			required: true
		},
		age:{
			type: Number,
			default: 99
		},
		sex:{
			type: String,
			required: true
		}
	}

}
  • 注意
    props是只读的,Vue底层会监测你对props的修改,如果进行了修改就会发出警告。如果确实需要修改,请复制props的内容到data中,再去修改data中的数据。
export default{
	name: 'Student',
	data(){
		return {
			myAge: this.age;
		}
	},
	props:['age']
}

【mixin混入】

  • 功能
    可以把多个组件共用的配置提取成一个混入对象。
  • 注意
    data中原有的以原有的为主,没有则以mixin为主。
    生命周期钩子都要。
//局部混入
//mixin.js
export const hunhe = {
	methods:{
		...
	},
	mounted(){
		...
	}
}
//组件
import {hunhe} from '../mixin'
export default{
	name:'School',
	data(){
		return{
		}
	},
	mixins:[hunhe]
}
//全局混入
//main.js
import {hunhe, hunhe2} from './mixin'
Vue.mixin(hunhe)
Vue.mixin(hunhe2)

【插件】

  • 功能
    用于增强Vue。
  • 本质
    包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
//main.js
import plugins from './plugins'
Vue.use(plugins)
//plugins.js
export default{
	install(Vue, options){
		// 1. 添加全局过滤器  
 		Vue.filter(....)  
  
		// 2. 添加全局指令  
 		Vue.directive(....)  
  
 		// 3. 配置全局混入(合)  
 		Vue.mixin(....)  
  
 		// 4. 添加实例方法  
 		Vue.prototype.$myMethod = function () {...}  
 		Vue.prototype.$myProperty = xxxx
	}
}

【组件通信】

(1)子组件向父组件传值

通过props,将父组件中的函数传给子组件,子组件通过调用父组件的函数将参数传递给父组件。


使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

//App.vue
<template>
	<div id="root">
		<School :getSchoolName="getSchoolName"/>
	</div>
</template>
<script>
	export default{
		name: 'App',
		component: {
			School
		},
		data(){
			...
		},
		methods:{
			getSchoolName(name){
				console.log(name);
			}
		}
	}
</script>
<button @click="sendSchoolName"></button>

<script>
	export default{
		name: 'School',
		props: ['getSchoolName'],
		data(){
			return{
				name: 'name'
			}
		},
		methods: {
			sendSchoolName(){
				this.getSchoolName(this.name);
			}
		}
	}
</script>

通过自定义事件,在父组件中的子组件绑定一个自定义事件,通过触发子组件实例上的自定义事件将参数传递给父组件。


组件上也可以绑定原生DOM事件,需要使用native修饰符。如:@click.native

//App.js

//使用@或v-on
<template>
	<div id="root">
		<Student v-on:atguigu="getStudentName"/>
		//or
		<Student @atguigu="getStudentName"/>
		//<Student @atguigu.once="getStudentName"/>
	</div>
</template>
<script>
	export default{
		name: 'App',
		component: {
			Student
		},
		data(){
			...
		},
		methods:{
			getStudentName(name){
				console.log(name);
			}
		}
	}
</script>

//使用ref
//比较灵活,可以使用定时器。
<template>
	<div id="root">
		<Student ref="student"/>
	</div>
</template>
<script>
	export default{
		name: 'App',
		component: {
			Student
		},
		data(){
			...
		},
		methods:{
			getStudentName(name){
				console.log(name);
			}
		},
		mounted(){
			//通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
			this.$ref.student.$on('atguigu', this.getStudentName);
			//this.$ref.student.$once('atguigu', this.getStudentName);
		}
	}
</script>
<button @click="sendStudentName"></button>
<button @click="unbind"></button>

<script>
	export default{
		name: 'Student',
		data(){
			return{
				name: 'name'
			}
		},
		methods: {
			sendStudentName(){
				this.$emit('atguigu', this.name);
			},
			unbind(){
				//解绑一个自定义事件
				this.$off('atguigu');
				//解绑多个自定义事件
				this.$off(['atguigu', 'demo']);
			}
		}
	}
</script>

全局事件总线。

(2)任意组件间通信

全局事件GlobalEventBus

![[Snipaste_2022-02-04_18-18-11.png]]

//main.js
...
new Vue({
	...
	beforeCreated(){
		Vue.prototype.$bus = this;	
	}
})
//提供数据
methods:{
	sendData(){
		this.$bus.$emit('事件名', this.data);
	}
},
beforeDestroyed(){
	this.$off('事件名');
}
//接收数据
mounted(){
	this.$bus.$on('事件名', (data)=>{
		console.log(data);
		...
	})
}

消息订阅与发布pubsub

//安装库
npm i pubsub-js

//在订阅和发布消息的组件里引入
import pubsub from 'pubsub-js'

//提供数据
methods:{
	sendData(){
		pubsub.publish('事件名', this.data);
	}
}


//接收数据
mounted(){
	this.pid = pubsub.subscribe('事件名', (msgName, data)=>{
		console.log(msgName, data);
		...
	});
},
beforeDestroyed(){
	pubsub.unsubscribe(this.pid);
}

vuex

(3)父组件向子组件传值

通过props。

插槽

//默认插槽
//样式写在父组件或者子组件中都可以。
//父组件中:
<Category>
   <div>这是插入默认插槽的内容</div>
</Category>
//子组件中:
<template>
	<div>
	   <slot></slot>
	</div>
</template>


//具名插槽
//父组件中:
<Category>
	//v-slot必须配合template(任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。)
	//v-slot:缩写成#
	<template v-slot:footer>
	   <div>这是插入footer的内容</div>
	</template>
</Category>
//子组件中:
<template>
	<div>
	   <slot name="center">没有在父组件插槽插入内容就会显示的默认内容</slot>
	   <slot name="footer">插槽默认内容...</slot>
	</div>
</template>


//作用域插槽
//父组件中:
<Category>
	<template v-slot:default="scopeData">
		<ul>
			<li v-for="g in scopeData.games" :key="g">{{g}}</li>
		</ul>
	</template>
</Category>
//子组件中:
<template>
	<div>
		<slot :games="games"></slot>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title'],
		//数据在子组件自身
		data() {
			return {
				games:['红色警戒','穿越火线','劲舞团','超级玛丽']
			}
		},
	}
</script>

【nextTick】

新生命周期函数

  • 语法
    this.$nextTick(回调函数)
  • 作用
    在下一次 DOM 更新结束后执行其指定的回调。
  • 什么时候用
    当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

函数执行的逻辑:函数执行=>DOM更新。如果在函数里this.$ref.inputTitle.focus(),不用nextTick的话,则会先执行函数即focus,再更新DOM,更新完后无法实现focus。

【本地存储】

(1)存储内容大小一般支持5MB左右(不同浏览器可能还不一样)。
(2)浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
(3)localStorage和sessionStorage的区别

  • SessionStorage存储的内容会随着浏览器窗口关闭而消失。
  • LocalStorage存储的内容,需要手动清除才会消失
xxxxxStorage.setItem('key', 'value');
//该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。键值需转换成字符串,如果是对象则使用JSON.stringfy(obj)。

xxxxxStorage.getItem('person');
//该方法接受一个键名作为参数,返回键名对应的值。
//如果xxx对应的value获取不到,那么getItem的返回值是null。
//JSON.parse(null)的结果依然是null。

xxxxxStorage.removeItem('key');
//该方法接受一个键名作为参数,并把该键名从存储中删除。

xxxxxStorage.clear()
//该方法会清空存储中的所有数据。

【vuex】

  • 定义
    在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
  • 什么时候用
    多个组件依赖于同一状态;
    来自不同组件的行为需要变更同一状态。
//src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
	//保存具体的数据
	state: {
		sum: 1
	},
	//修改state中的数据
	mutations: {
		JIA(state, value){
			state.sum += value;
			...
		},
		JIAN(state, value){
			state.sum -= value;
		}
	},
	//响应组件中用户的动作
	actions: {
		jia(context, value){
			...
			context.commit('JIA', value);
		}
	},
	//用于将state中的数据进行加工
	getters: {
		bigSum(state){
			return state.sum*10;
		}
	}
})

//main.js
import store from './store'

new Vue({
	render: h => h(App),
	store
}).$mount('#app')
<h1>{{ sum }}</h1>
<h1>{{ bigSum }}</h1>
<button @click="add(n)"></button>
<button @click="sub(n)"></button>

import {mapSate, mapGetters} from 'Vuex'
import {mapActions, mapMutations} from 'Vuex'
...
computed:{
	//对象写法
	...mapState({sum: 'sum'}),
	//数组写法
	...mapState(['sum']),
		
	...mapGetters(['bigSum']),
},
methods:{
	add(value){
		this.$store.dispatch('jia', value);
	},
	//等同于(不传参会默认传事件对象)
	//对象写法
	...mapActions({add: 'jia'}),
	//数组写法(html函数名要和action函数名一样)
	...mapActions(['jia']),
	
	
	sub(value){
		this.$store.commit('JIAN', value);
	},
	//等同于
	...mapGetters({add: 'JIA'}),
	...mapGetters(['JIA']),
	
}

模块化和命名空间

//src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import countAbout from './count'
import personAbout from './person'

Vue.use(Vuex);

export default new Vuex.Store({
	modules:{
		countAbout,
		personAbout
	}	
})
//src/store/count.js
export default{
	namespaced: true,
	state:{
		...
	},
	mutations:{
		...
	},
	actions:{
		...
	},
	getters: {
		...
	}
};

//src/store/person.js
export default {
	...
};

//state
this.$store.state.personAbout.sum
...mapState('countAbout', ['sum'])
//getters
this.$store.getters['personAbout/firstPersonName']
...mapGetters('countAbout', ['bigSum'])
//actions
this.$store.dispatch('personAbout/addPersonWang', person)
...mapActions('countAbout', ['jia'])
//mutations
this.$store.commit('personAbout/ADD_PERSON', person)
...mapMutations('countAbout', ['JIA'])

【SPA】

单页面应用
Single Page web Application

  • 整个应用只有一个完整的页面。点击页面中的导航链接不会刷新页面,只做页面的局部刷新。
  • 数据需要通过ajax请求获取。

【vue-router】

  • 路由组件通常存放在pagesviews文件夹,一般组件通常存放在components文件夹。
  • 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  • 每个组件都有自己的$route属性,里面存储着自己的路由信息。
    整个应用只有一个router,可以通过组件的$router属性获取到。
//src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

//引入Layout组件
import Aoubt from '../component/About'
import Home from '../components/Home'

Vue.use(VueRouter)

export default new VueRouter({
	routes:[
		{
			path: '/about',
			component: About
		},
		{
			path: '/home',
			component: Home
		}
	]
})
//main.js
import router from './router'

new Vue({
	render: h => h(App),
	router
}).$mount('#app')
//点击跳转
<router-link active-class="active" to="/about"></router-link>

//指定展示位置
<router-view></router-view>

嵌套路由

//src/router/index.js
export default new VueRouter({
	routes:[
		{
			path: '/about',
			component: About
		},
		{
			path: '/home',
			component: Home,
			//通过children配置子级路由
			children:[{
				path: 'news', //此处一定不要写:/news
				component: News
			},
			{
				path:'message',//此处一定不要写:/message
				component: Message,
				children:[{
					//params必须配置name
					name: 'xiangqing',
					path: 'detail',
					//params参数
					path: 'detail/:id/:title',
					component: Detail
				}]
			}]
		}
	]
})
//子路由
//路径要写完整
<router-link to="/home/news">News</router-link>

//query参数
//不用配置路由
//字符串写法
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">跳转</router-link>
//对象写法
<router-link 
	:to="{
		path:'/home/message/detail',
		query:{
		   id:666,
           title:'你好'
		}
	}"
>跳转</router-link>
//接受参数
this.$route.query.id  
this.$route.query.title

//命名路由
<router-link :to="{name: 'xiangqing'}">News</router-link>
<router-link 
	:to="{
		name:'xiangqing',
		query:{
		   id:666,
           title:'你好'
		}
	}"
>跳转</router-link>

//params参数
//要配置路由
//字符串写法
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">跳转</router-link>
//对象写法
<router-link 
	:to="{
		//必须使用name不能使用path
		name:'xiangqing',
		params:{
		   id:666,
           title:'你好'
		}
	}"
>跳转</router-link>
//接受参数
this.$route.params.id  
this.$route.params.title

路由的props

  • 通过路由配置传递参数给组件。
//src/router/index.js
export default new VueRouter({
	routes:[
		{
			path: '/about',
			component: About
		},
		{
			path: '/home',
			component: Home,
			children:[{
				path: 'news',
				component: News
			},
			{
				path:'message',
				component: Message,
				children:[{
					name: 'xiangqing',
					path: 'detail/:id/:title',
					component: Detail,
					//对象
					//该对象中所有key-value组合最终都会通过props传给Detail组件。
					props: {
						a: 1,
						b: 'hello'
					},
					//布尔值
					//true则把路由收到的所有params参数通过props传给Detail组件,但是就收不到query参数了。
					props: true,
					//函数
					//该函数返回的对象中每一组key-value都会通过props传给Detail组件。
				 	props(route){
					  	return {
							id: route.query.id,
					   		title: route.query.title
						}
					}
				}]
			}]
		}
	]
})
//Detail组件
<h1>{{a}}-{{b}}</h1>
<h1>{{id}}-{{title}}</h1>

export default({
	name: 'Detail',
	//对象
	props: ['a', 'b']
	//布尔值、函数
	props: ['id', 'title']
})

replace属性

  • 作用:控制路由跳转时操作浏览器历史记录的模式。
  • 浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push。
<router-link replace>News</router-link>

编程式路由导航

  • 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活。
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(-1) //可前进也可后退

缓存路由组件

  • 作用:让不展示的路由组件保持挂载,不被销毁。
//写的是组件名
<keep-alive include="News"> 
    <router-view></router-view>
</keep-alive>

<keep-alive :include="['News', 'Msg']"> 
    <router-view></router-view>
</keep-alive>

activated()、deactived()

  • 新生命周期钩子
  • 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。

activated(){}路由组件被激活时触发。
deactivated(){}路由组件失活时触发。

路由守卫

//src/router/index.js
...
const router = new VueRouter({
	routes: [
		{
			name: 'zhuye',
			path: '/home',
			component: Home,
			meta: {
				title: '主页'
			},
			children: [
				{
					name: 'xiwen',
					path: 'news',
					component: News,
					meta: {
						isAuth: true,
						title: '新闻'
					},
					//独享路由守卫
					//只有前置
					beforeEnter: (to, from, next)=>{
						...//类似全局前置路由守卫
					}
				},
				{
					name: 'xiaoxi',
					path: 'message',
					component: Message,
					meta: {
						isAuth: true,
						title: '消息'
					}
				}
			]
		}
	]
});

//全局前置路由守卫
//初始化时执行、每次路由切换前执行  
router.beforeEach((to,from,next)=>{  
	//判断是否需要鉴权
	if(to.meta.isAuth){
		if(localStorage.getItem('school')=== 'atguigu'){
			//放行
			next();
		}else{
			alert('学校名不对,无权限查看!');
		}
	}else{
		next();
	}	 
});
  
//全局后置路由守卫
//初始化时执行、每次路由切换后执行  
router.afterEach((to,from)=>{
	document.title = to.meta.title || 'vue_test';
});
export default router;
//组件.vue
export default{
	...
	//组件内守卫
	//进入守卫:通过路由规则,进入该组件时被调用
	beforeRouteEnter(to, from, next){
		...
	},
	//离开守卫:通过路由规则,离开该组件时被调用
	beforeRouteLeave(to, from,next){
		...
	}
}

hash值

  • /#及其后面的内容就是hash值。
  • hash值不会包含在 HTTP 请求中,即hash值不会带给服务器。

路由的两种工作模式

  • hash模式
    地址中永远带着#号,不美观 。
    若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    兼容性较好。
  • history模式:
    地址干净,美观 。
    兼容性和hash模式相比略差。
    存在刷新页面服务端404问题,若要应用部署上线需要后端人员支持(nodejs:connect-history-api-fallback;nginx)。

【打包】

npm init
npm i express
npm i connect-history-api-fallback
//server.js
const express = require('express')
const history = require('connect-history-api-fallback')

const app = express()
app.use(history)
app.use(express.static(__dirname+'/static'))

app.get('/person', (req, res)=>{
	res.send({
		name: 'tom',
		age: 18
	})
})

app.listen(5000, (err)=>{
	if(!err)console.log('服务器启动成功')
})
//把vue项目npm run build后的dist里的文件放到当前目录下的static里。再node server就可以localhost:5000访问到。
node server
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值