Vue2.0基础语法

目录

一、基础语法

1. 基础注意事项

  1. 首先要创建一个vue实例
import Vue from 'vue'
new Vue({
    el: '#root', //用于指定当前vue实例为哪个容器服务,值为css选择器字符串
})
  1. vue实例和容器是一一对应的,一个vue实例只能接管一个容器
  2. root容器中的代码称为模板

2. 阻止vue在启动时生成生产提示

Vue.config.productionTip=false

3. 模板语法

data中的数据发生改变,页面中用到改数据的地方会自动更新,

1)插值语法

{{js表达式}}
用 于解析标签体内容,可以直接读取到data中的所有属性。

2)指令语法

v-xxx
用于解析标签(标签属性、标签体内容、绑定事件等),可以直接读取到data中的所有属性。

4. data的两种写法

对象式

data:{
	name:'zfj',
	age:25
}

函数式

data(){
	return{
		name:'zfj',
		age:25
	}
}

5. 数据代理

通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。

Object.defineProperty()

getter和setter

let number = 18
let person = {
	name:'张三',
	sex:'男',
}

Object.defineProperty(person,'age',{
	// value:18,
	// enumerable:true, //控制属性是否可以枚举,默认值是false
	// writable:true, //控制属性是否可以被修改,默认值是false
	// configurable:true //控制属性是否可以被删除,默认值是false

	//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
	get(){
		console.log('有人读取age属性了')
		return number
	},

	//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
	set(value){
		console.log('有人修改了age属性,且值是',value)
		number = value
	}

})

6. 数据绑定

v-bind

单向绑定属性,数据只能从data流向页面,例如:

v-bind:herf=" "
//简写
:herf=" "

v-model

双向绑定,一般应用在表单元素上,例如:

v-model:value=" "
//v-model:value可以简写为 v-model,默认收集value值

v-modle 收集表单数据
<input type="text"/> //v-model收集的是value值,用户输入的就是value值。

<input type="radio"/> //v-model收集的是value值,且要给标签配置value值。

<input type="checkbox"/> //没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
						// 配置input的value属性:
                            //(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
                            //(2)v-model的初始值是数组,那么收集的的就是value组成的数组

v-model的三个修饰符:

.lazy:失去焦点再收集数据
.number:输入字符串转为有效的数字
.trim:输入首尾空格过滤


7. 绑定样式v-bind:

class样式

<div :class="xxx"> </div> //xxx可以是字符串,对象,数组

style样式

<div :style="xxx"> </div> //xxx可以是对象,数组

new Vue({
	data:{
		//对象写法
		styleObj:{
			fontSize: '40px',
			color:'red',
		},
		//数组写法
		styleArr:[
			{
				fontSize: '40px',
				color:'blue',
			},
			{
				backgroundColor:'gray'
			}
		]
	}
})

8. 事件处理

v-on

绑定事件,监听dom事件,例如:

v-on:click=" "
//简写
@click=" "

事件的回调配置在methods(与data平级)中:

<button @click="fn1"> </button> //无参,会默认将原生事件event传进去
<button @click="fn1($event)"> </button> //与上一个效果一致
<button @click="fn2($event,2,3)"> </button> //传参
<button @click="fn2(2,3)"> </button> //传参,参数只有2,3,没有$event

methods:{
	fn3:function(){
	......
	},
	fn1(event){
	......
	},
	fn2(event,a,b){
	......
	}
}

注意:

  • methods中配置的函数,this指向为vm或组件实例对象;
  • methods中配置的函数,不要用箭头函数,否则this就不是vm了,而是window;

事件修饰符

@click.prevent=" " //阻止默认事件
.stop //阻止事件冒泡
.once //事件只触发一次
.capture //使用事件的捕获模式,即在捕获阶段就处理事件
.self //只有 event.target 是当前操作的元素才是触发事件
.passive //事件的默认行为立即执行,无需等待事件回调执行完毕

//修饰符可以连写
@click.prevent.stop=" "

键盘绑定

@keyup

按键松开后触发事件

@keyup.enter //回车
.delete //删除和退格键
.esc //
.space //空格
.up //
.down //
.left //
.right //

@keydown

按键按下即触发

//以上均可使用
 .tab //必须配合keydown使用
系统修饰键

ctrl、alt、shift、meta(win键)

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

9. 计算属性

computed

通过已有属性计算得来的属性

new Vue({
	data:{
		a:1
	},
	
    computed: {
        sum: {
            //get有什么作用?当有人读取sum时,get就会被调用,且返回值就作为sum的值
            //get什么时候调用?1.初次读取sum时。2.所依赖的数据发生变化时。
            get() { 
                return this.a++ //此处的this是vm
            },
            //set什么时候调用? 当sum被修改时。
            set(value) {
                this.a = value
            }
        }
    }
})
//简写
computed:{
	sum:function(){
		return this.a++
	}
}
//或
computed:{
	sum(){
		return this.a++
	}
}

computed和methods区别

  • 所有计算属性实现的功能都可以由methods方法实现,
  • 然而,计算属性是基于响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值。
    这就意味着只要 a 还没有发生改变,多次访问 sum 计算属性会立即返回之前的计算结果,而不必再次执行函数。
  • 相比之下,每当触发重新渲染时,调用 methods 将总会再次执行函数

10. 侦听属性

watch:

  • 当监视的属性发生变化时,回调函数自动调用;
  • 监视的属性必须存在才能进行监视;
  • 两种写法
const vm = new Vue({
    el:'root',
    data:{
        isHot:true,
    },
    //写法一:
    watch:{
        isHot:{
            immediate:true, //初始化时,调用handler一下
            //当isHot发生改变时,触发
            handler(newValue,oldValue){
                console.log('isHot被修改了',newValue,oldValue)
            }
        }
    }
    
    //简写:
    watch:{
        isHot(newValue,oldValue){
            //当isHot发生改变时,触发
            console.log('isHot被修改了',newValue,oldValue)
        }
    }
})
//写法二:
vm.$watch('isHot',{
	immediate:true, //初始化时让handler调用一下
	//handler什么时候调用?当isHot发生改变时。
	handler(newValue,oldValue){
		console.log('isHot被修改了',newValue,oldValue)
	}
})

深度监视

  1. Vue中的watch默认不监测对象内部值的改变;
  2. 配置deep:true可以监测对象内部所有值的改变;
const vm = new Vue({
	el:'#root',
	data:{
		numbers:{
			a:1,
			c:{
				d:2
			}
		},
	watch:{
		//监视多级结构中某个属性的变化
		'numbers.a':{
			handler(){
				console.log('a被改变了')
			}
		}
		
		//监视多级结构中所有属性的变化
		numbers:{
			deep:true,
			handler(){
				console.log('numbers改变了')
			}
		}
	}
})

11. 条件渲染

v-if

<h1 v-if="表达式"> </h1> //表达式值为真时,内容才会被渲染
<h1 v-else-if="表达式"> </h1>
<h1 v-else="表达式"> </h1>

注意:不展示DOM元素直接被移除。


template

一个不可见的包裹元素,不影响结构

<template v-if="n === 1">
	<h2>你好</h2>
	<h2>尚硅谷</h2>
	<h2>北京</h2>
</template>

v-show

<h2 v-show="表达式">hello</h2>

注意:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉,元素总会被渲染。
适用于切换频率较高的场景。

使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。


12. 列表渲染

v-for

//遍历数组
v-for="item in xxx" :key="yyy" //in 也可用 of
v-for="(item,index) in xxx" :key="yyy" //index为当前索引

//遍历对象
v-for="value in xxx" :key="yyy" //in 也可用 of
v-for="(value,key) in xxx" :key="yyy" //key为键名
v-for="(value,key,index) in xxx" :key="yyy" //index为索引

遍历列表时key的作用

为了能追踪每个节点的身份,从而重用和重新排序现有元素。

  1. vue首先根据初始数据生成虚拟DOM(虚拟DOM上一定有key);
  2. 将虚拟DOM转为真实DOM,渲染到页面;
  3. 用户在真实DOM中输入数据;
  4. vue根据新的数据生成新的虚拟DOM;
  5. 新的虚拟DOM和旧的虚拟DOM进行对比,对比规则:
    1. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
      ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
      ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
    2. 旧虚拟DOM中未找到与新虚拟DOM相同的key:
      创建新的真实DOM,随后渲染到到页面。

注意:key默认为index


列表过滤(computed, filter)

data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers() {
  	return this.numbers.filter((number)=> { //computed返回计算后的结果
    	return number % 2 === 0 //filter返回一个新数组,不改变原数组
    })
  }
}

列表排序

data: {
  numbers: [ 4, 5, 1, 3, 2 ]
},
computed: {
  evenNumbers() {
  	//过滤
  	const arr= this.numbers.filter((number)=> { 
    	return number % 2 === 0 
    })
    //排序    
	arr.sort((p1,p2)=>{ //arr.sort(参数),参数必须是函数
		return p2-p1 //后减前是降序,前减后是升序
	})
	
	return arr
  }
}

Vue监测数据的原理

(1)监测对象{ }中的数据

Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。

在这里插入图片描述

  • data ==> vm._data: get/set【响应式:数据改变,页面也跟着改变】
  • data中的所有属性都会被匹配一个get和set
  • 数据改变时,相应的set会被调用,set被调用就会重新解析模板。
  • 读取data中对象上不存在的属性时,为undefined,不会报错,也不会显示undefined。

  • 对象中后追加的属性,Vue默认不做响应式处理;
    如需给后添加的属性做响应式,请使用如下API:
set()
//API-1
Vue.set(target,'key','val') //往target的身上追加属性key
//API-2
vm.$set(target,'key','val')

注意:set()只能给data中的对象追加属性,不能给data或者vm追加属性


(2)监测数组[ ]中的数据

Vue不能通过索引更改数组

vue没有为数组中的每个元素匹配get和set,数组中的每个元素不是通过set和get监视的;

修改数组的方法:
// 这些方法会改变原数组
push() //在尾部添加元素
pop() //删除最后一个
shift() //删除第一个
unshift() //在头部添加一个
splice(索引,长度,'替换内容') //删除、添加、替换
sort() //排序
reverse() //反转数组

注意:这些都是被Vue包裹过的方法,是对Array原型对象上的方法进行了包裹


13. 其他内置指令

v-text

向其所在的节点渲染文本内容

<div v-text="xxx"></div>

注意:v-text会替换掉节点中的内容,{{xx}}则不会


v-html

指定节点中渲染包含html结构的内容,可以识别 html结构

<div v-html="xxx"></div>

注意:有安全性问题,容易导致XSS攻击,不要用在用户提交的内容上!

  1. v-html会替换掉节点中所有的内容,{{xx}}则不会。
  2. v-html可以识别html结构。
  3. 在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成 XSS 漏洞。请仅在内容安全可信时再使用 v-html,并且永远不要使用用户提供的 HTML 内容。

v-cloak

避免网速慢,出现未经解析的 {{xxx}} 的DOM;用于隐藏尚未完成编译的 DOM 模板。

<h2 v-cloak>{{xxx}}</h2>

视频:Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
官网:v-cloak 会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。


v-once

v-once所在节点在初次动态渲染后,就视为静态内容了,以后数据的改变不会引起v-once所在结构的更新

<h2 v-once>初始化的n值是:{{n}}</h2>

v-pre

跳过其所在节点的编译过程(即vue解析过程),加快编译

<h2 v-pre>zfj</h2>

14. 自定义指令

使用时:v-指令

//局部指令
new Vue({
	data:{
	},
	directives:{ 
		//写法一:对象形式
		big1:{
			a(element, binding){},
			b(element, binding){}
		},
		//写法二:函数形式
		big2(element,binding){ // element是绑定的真实DOM元素;binding是绑定的值以及所有的信息		
		}
	}
})

//全局指令
Vue.directive('指令名',function(element,blinding){}) //函数形式
Vue.directive('指令名',{}) //对象形式

big何时被调用?

  1. 指令与元素成功绑定时(一上来);
  2. 指令所在的模板被重新解析时

常用的3个回调函数
bind:指令与元素成功绑定时调用。
inserted:指令所在元素被插入页面时调用。
update:指令所在模板结构被重新解析时调用。


注意:directives中的this指向windows
命名规则:指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名,且定义时要加''


15. 过滤器filters

//局部过滤器,与data平级
filters:{
	fn(参数){
		return ...
	}
}
//注册全局过滤器
Vue.filter('过滤器名',function(参数){
	return ...
})

//使用过滤器
{{ xxx | 过滤器名}} //或
v-bind:属性 = "xxx | 过滤器名"

注意:并没有改变原本的数据, 是产生新的对应的数据

16.生命周期

new Vue({
	data:{},
	//常用生命周期函数
	mounted(){ //Vue完成模板的解析并把【初始的】真实DOM元素放入页面后(挂载完毕)调用mounted
	},
	beforeDestroy(){
	}
})

注意:生命周期函数中的this指向是vm 或 组件实例对象

在这里插入图片描述

17. 非单文件组件

组件是可复用的Vue实例。

创建及注册组件

//1.创建组件
//Vue.extend()创建的是一个组件构造器
const 组件=Vue.extend({  //此处不是组件名
	//template可以配置组件结构
	template: `
		<div></div>
	`,
	//data必须写成函数形式(避免组件被复用时,数据存在引用关系)
	data(){ 
		return ...
	}
})
//可以简写为:
const 组件={
......
}

//2.注册组件
new Vue({
	el:'#root',
	data:{
		msg:'你好啊!'
	},
	//局部注册
	components:{
		组件名:组件, //此处才是组件名
		//或
		组件
	}
})
//全局注册,可以在多个vue实例中使用
//将上面创建的组件构造器注册为一个组件,并给它起一个组件的标签名
Vue.component('组件名',组件)

全局组件创建和注册的语法糖:

Vue.component('组件名',{
	template: `
		<div></div>
	`
})
//局部组件类似

组件名命名规则

  1. 一个单词:首字母大小写均可
  2. 多个单词组成:kebab-caseCamelCase

VueComponent

  1. 组件本质是一个名为VueComponent的构造函数,是由Vue.extend生成的;
  2. <school></school><school/>,Vue解析时会帮我们创建组件的实例对象,即执行new VueComponent()
  3. 每次调用Vue.extend,返回的都是一个全新的VueComponent;
  4. 组件中的所有函数的this指向均是组件实例对象;
  5. VueComponent.prototype.__proto__ === Vue.prototype
    此内置关系让组件实例对象可以访问到Vue原型上的属性和方法。
    在这里插入图片描述

补充:原型对象

  1. 只要是函数,就一定有fn.prototype属性,即显示原型属性;
  2. 构造函数创建的实例对象上有一个obj.__proto__属性,即隐式原型属性;
  3. 这两个属性都指向同一个对象,即原型对象
  4. 实例的隐式原型属性永远指向自己缔造者的原型对象;
  5. 通过显示原型属性给原型对象添加属性,实例对象可以访问到原型对象的属性;

18. 单文件组件

【vscode安装vetur插件,用来编译.vue文件】
.vue文件:

<template>
  //======组件的结构=======
  <div>
    ......
  </div>
</template>


<script>
//引入其他组件
import 其他组件名 from ' '
//组件交互相关代码(数据,方法等)
export default {  //默认暴露
  name: '本组件名',  //定义组件名
  data() {
    return {
      ......
    }
  },
  methods: {
   ......
  },
  components:{
  	其他组件名
  }
}
</script>


<style>
/*组件的样式*/
......
</style>

template

<template> </template> //可以包裹元素,但不生成实际结构

19. 父组件和子组件

父传子

  1. 通过 props
<!--父组件-->
<template>
  <div>
    <h2>父组件</h2>
    <!--给子组件发送数据-->
    <Child v-bind:fatherMessage="fatherMessage"></Child>
  </div>
</template>
<script>
import Child from './views/Child';
 
export default{
  components: {
    Child,
  },
  data() {
    return {
        // 定义一个数据
      fatherMessage: '我是来自父组件的数据',
    };
  },
};
</script>
----------------------------------------------------------------
<!--子组件-->
<template>
  <div>
    <h3>我是子组件</h3>
    <!--展示数据-->
    <span>{{fatherMessage}}</span>
  </div>
</template>
<script>
export default{
  // 接收父组件传过来的数据
  props: ['fatherMessage'],
};
</script>

子传父

  1. 通过父组件给子组件传递函数类型的props实现
    (父给子传一个函数,子在合适的时候调用这个函数)
<!--父组件-->
<template>
	<div class="app">
		<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
		<School :getSchoolName="getSchoolName"/>
	</div>
</template>

<script>
	import School from './components/School'

	export default {
		name:'App',
		components:{School},
		data() {
			return {
			
			}
		},
		methods: {
			getSchoolName(name){
				console.log('App收到了学校名:',name)
			},
		},
	}
</script>
-----------------------------------------------------------------
<!--子组件-->
<template>
	<div class="school">
		<h2>学校名称:{{name}}</h2>
		<h2>学校地址:{{address}}</h2>
		<button @click="sendSchoolName">把学校名给App</button>
	</div>
</template>

<script>
	export default {
		name:'School',
		props:['getSchoolName'],
		data() {
			return {
				name:'尚硅谷',
				address:'北京',
			}
		},
		methods: {
			sendSchoolName(){
				this.getSchoolName(this.name)
			}
		},
	}
</script>
  1. 通过父组件给子组件绑定一个自定义事件实现
<!--父组件-->
<template>
  <div>
    <h2>父组件</h2>
    <br>
    <!--接收数据和方法-->
    <Child @childEvent="parentMethod"></Child>
  </div>
</template>
<script>
import Child from './Child';
 
export default{
  components: {
    Child,
  },
  data() {
    return {
      parentMessage: '我是来自父组件的消息',
    };
  },
  methods: {
       // 使用方法展示数据
    parentMethod({ name, age }) {
      console.log(this.parentMessage, name, age);
    },
  },
};
</script>
<style scoped>
</style>
---------------------------------------------------------------------
 
<!--子组件-->
<template>
  <div>
    <h3>我是子组件</h3>
  </div>
</template>
<script>
export default{
  mounted() {
    // 触发子组件实例身上的绑定事件,用$emit发送数据和方法
    this.$emit('childEvent', { name: 'zhangsan', age:  10 });
  },
};
</script>
<style scoped>
</style>
  1. 通过ref
<!--父组件-->
<template>
  <div>
    <h2>父组件</h2>
    <br>
        <!--ref主动寻找子组件组件-->
    <Child-one ref="child"></Child-one>
  </div>
</template>
<script>
import ChildOne from './ChildOne';
 
export default{
  components: {
    ChildOne,
  },
  mounted(){
        // 使用this.$refs调用子组件方法即可,用法:this.$refs['child'].xxx()
    console.log(this.$refs['child']);
  },
};
</script>
<style scoped>
</style>

二、组件进阶

1. Vue脚手架 CLI

安装

全局安装(只需安装一次): npm install -g @vue/cli
创建项目:vue create 项目名
启动项目:npm run serve(运行main.js文件)


分析脚手架结构

main.js
//该文件是整个项目的入口文件
import Vue from 'vue' //引入Vue
import App from './App.vue' //引入App组件(所有组件的父组件)

Vue.config.productionTip = false //关闭vue的生产提示

//创建Vue实例对象---vm
new Vue({
	el:'#app',
	render: h => h(App) //render函数完成了这个功能:将App组件放入容器中
})
App.vue

App组件(所有组件的父组件)

<template>
	<div>
		<Student></Student>
	</div>
</template>

<script>
	//引入组件
	import Student from './components/Student'

	export default {
		name:'App',
		components:{
			Student
		}
	}
</script>
./components/Student.vue

子组件

<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{age}}</h2>
	</div>
</template>

<script>
	export default {
		name:'Student',
		data(){
			return {
				name:'张三',
				age:18
			}
		}
	}
</script>
./public/index.html
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  <!--让IE浏览器以最高的渲染级别渲染页面-->
    <meta name="viewport" content="width=device-width,initial-scale=1.0"> <!-- 开启移动端的理想视口 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <!-- 配置页签图标 ; <%= BASE_URL %> 即public路径-->
    <title><%= htmlWebpackPlugin.options.title %></title> <!-- 配置网页标题 -->
  </head>
  <body>
    <noscript>  <!-- 当浏览器不支持js时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>

2. render函数

render(createElement){   //参数createElement是一个函数,用来创建具体的DOM元素
	return createElement(App)
}
//简写:
render:h=>h(App)
  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函数去指定具体内容。

3. ref 属性

得到真实DOM元素或组件实例对象,ref 需要在dom渲染完成后才会有

ref="xxx"

this.$refs.xxx

4. props

prop:属性
作用:让组件接收父组件传过来的数据。

 //简单数组式声明
props:['name','age']

//对象式声明,指定值的类型
props:{
	name:String,
	age:Number,
} 

//限制类型、限制必要性、指定默认值
props: {
	name: {
		type: String, //name的类型是字符串
		required: true, //name是必要的
	},
	age: {
		type: Number,
		default: 99, //默认值
	},
	//自定义类型校验函数
	propA: {
      validator(value) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].includes(value)
      }
	
}

注意:props的值不可修改

5. mixin 混入

作用:把多个组件共用的配置提取成一个混入对象。

mixin.js
export const myMixin1 = {
	data:{
	},
	methods:{
	}
},
export const myMixin2 = {
	data:{
	},
	methods:{
	}
}
组件名.vue
//引入一个hunhe
import {myMixin1,myMixin2} from '../mixin.js'

export default {
  name: "组件名",
  data:{
  },
  mixins:[myMixin1,myMixin2],
}

注意:如果混合和自己的配置有冲突,以自己的为主;但是对于生命周期函数,两者都要,且混合的在前,自己的在后。


全局混合
main.js
import {myMixin1,myMixin2} from './mixin.js'

Vue.mixin(myMixin1)
Vue.mixin(myMixin2)

6. 插件

plugins.js

插件是一个对象。

export default {
	install(Vue,options){ //第一个参数是Vue构造函数,options是其他所需参数
		//添加全局方法
		Vue.myMethod=function(){
			...
		}

		//添加全局指令
		Vue.directive('指令名',{
			bind(element,binding){
				...
			},
			...
		})

		//定义混入
		Vue.mixin({
			data() {
				...
			},
			...
		})

		//给Vue原型上添加一个方法(vm和vc就都能用了)
		Vue.prototype.myMethod1 = function(){
			...
		}
	}
}

main.js

//引入插件
import plugins from './plugins'

//使用插件
Vue.use(plugins,其他参数)

Vue.use()的一个原则就是执行对象的install这个方法

7. scoped

作用:限制作用域。

<style scoped>
/* scoped 限制此样式只作用当前组件 */
...
</style>

8. 浏览器本地存储

localStorage

特点:用户主动删除或清空缓存后数据才会消失

//存储数据
localStorage.setItem('key1','hello!!!') //字符串
localStorage.setItem('key2',666) //数字
localStorage.setItem('key3',JSON.stringify({name:'张三',age:18})) //对象

//读取数据
localStorage.getItem('key1') //如果key对应的value获取不到,那么 getltem的返回值是nul
localStorage.getItem('key2')
JSON.parse( localStorage.getItem('key3') ) //对象

//删除数据
localStorage.removeItem('key')

//清空数据
localStorage.clear()

sessionStorage

特点:浏览器一关闭,数据就没了。

//与上面相同

9. 组件的自定义事件

通过父组件给子组件绑定一个自定义事件实现(用于子给父传递数据):

//父组件
//第一种写法,使用@或v-on
<Child @my-event="doSomething"/>

methods:{
	doSomething(param, ...params){ //...params:第一个参数给param,其他的赋给一个数组
	}
}
//第二种写法,使ref
<Child ref="student"/>

 mounted() {
 	//此处的this指向的是子组件实例
 	this.$refs.student.$on("atguigu", this.getStudentName); //绑定自定义事件
    // this.$refs.student.$once('atguigu',this.getStudentName) //绑定自定义事件(一次性,只能触发一次)
    },
//子组件
methods:{
	//触发绑定的事件
	doSomething(){
		this.$emit('my-event',参数)
	},
	
	//解绑自定义事件
	unbind(){
		this.$off('my-event') //解绑一个
		this.$off(['my-event1','my-event2']) //解绑多个自定义事件
		this.$off() //解绑所有的自定义事件
	},

	//销毁了当前组件的实例,销毁后所有此组件实例的自定义事件全都不奏效。
	death(){
		this.$destroy() 
	}
}

绑定原生的DOM事件

以click为例

<Child @click.native="doSomething"/>

10. 全局事件总线

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 //安装全局事件总线
	},
})

11. 消息订阅与发布

一种组件间通信的方式,适用于任意组件间通信。

步骤:

  1. 安装pubsub:npm i pubsub-js
  2. 引入: import pubsub from 'pubsub-js'(pubsub是个对象)
  3. 提供数据
    methods: {
    	sendStudentName(){
    		pubsub.publish('my-event',数据) //发布消息
    	}
    }
    
  4. 接收数据:A组件想接收数据,则在A组件中订阅消息
    mounted() {
    	this.pubId = pubsub.subscribe('my-event',(msgName,data)=>{...}) //订阅消息;第一个参数msgName是消息名,后面的参数才是数据
    }
    
  5. 取消订阅
    beforeDestroy() {
    	pubsub.unsubscribe(this.pubId)
    }
    

12. $nextTick

在下一次DOM更新结束后(即整个视图都渲染完毕)再执行回调函数

vm.$nextTick(function(){
})

13. 过渡与动画

动画

<template>
	<div>
		<transition name="xxx" appear> //添加一个appear属性,一开始就有动画
			<h1 v-show="isShow">...</h1>
		</transition>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				isShow:true
			}
		},
	}
</script>

<style scoped>
	.xxx-enter-active{
		animation: atguigu 0.5s linear;
	}

	.xxx-leave-active{
		animation: atguigu 0.5s linear reverse;
	}
	//动画
	@keyframes atguigu {
		from{
			transform: translateX(-100%);
		}
		to{
			transform: translateX(0px);
		}
	}
</style>

过渡

<template>
	<div>
		<transition name="xxx" appear> //添加一个appear属性,一开始就有动画
			<h1 v-show="isShow">...</h1>
		</transition>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				isShow:true
			}
		},
	}
</script>

<style scoped>
	/* 进入的起点、离开的终点 */
	.xxx-enter, .xxx-leave-to{
		transform: translateX(-100%);
	}
	.xxx-enter-active, .xxx-leave-active{
		transition: 0.5s linear;
	}
	
	/* 进入的终点、离开的起点 */
	.xxx-enter-to, .xxx-leave{
		transform: translateX(0);
	}
</style>

v-enter:进入的起点
v-enter-active:进入过程中
v-enter-to:进入的终点
v-leave:离开的起点
v-leave-active:离开过程中
v-leave-to:离开的终点

在这里插入图片描述


多个元素过渡

<transition-group> </transition-group>,且每个元素都要指定key值。


使用动画库中的动画

https://animate.style/
先安装:npm install animate.css --save

<template>
	<div>
		<button @click="isShow = !isShow">显示/隐藏</button>
		<transition-group 
			appear
			//
			name="animate__animated animate__bounce" 
			//进入动画
			enter-active-class="animate__swing"
			//离开动画
			leave-active-class="animate__backOutUp"
		>
			<h1 v-show="!isShow" key="1">你好啊!</h1>
			<h1 v-show="isShow" key="2">尚硅谷!</h1>
		</transition-group>
	</div>
</template>

<script>
	//引入
	import 'animate.css'
	export default {
		name:'Test',
		data() {
			return {
				isShow:true
			}
		},
	}
</script>

<style scoped>
//不需要写东西
</style>

14. 配置代理

跨域

跨域是“浏览器”对axios做出的同源策略的限制;请求发出去了,但是响应被拦截了。
同源是指协议、域名、端口都相同。

解决跨域问题:

  1. cors:服务器返回数据,携带一些特殊的响应头(是后端进行设置,前端什么都不用做)
  2. jsonp:只能解决 get 跨域请求问题(利用< script>标签的src属性,通过指向一个需要访问的地址,由服务端返回一个预先定义好的 Javascript 函数的调用,并且将服务器数据以该函数参数的形式传递过来)
  3. 代理服务器(端口号和浏览器的端口号相同):
    注意: 服务器和服务器之间的请求没有跨域限制
    vue.config.js
    module.exports = {
    	pages: {
    		index: {
    			//入口
    			entry: 'src/main.js',
    		},
    	},
    	lintOnSave:false, //关闭语法检查
    	
    	//开启代理服务器(方式一)
    	devServer: {
    		proxy: 'http://localhost:5000' //此处端口号是请求的服务器的端口号
    	}, 
    	
    	//开启代理服务器(方式二)
    	devServer: {
    		proxy: {
    			//加上前缀就一定走代理
    			'/api': { //请求前缀,在发送请求的地方端口号后面加上此前缀:axios.get('http://localhost:8080/api/请求数据')
    				target: 'http://localhost:5000',
    				pathRewrite:{'^/api':''}, //将字符串’/api‘替换为''
    				// ws: true, //用于支持websocket
    				// changeOrigin: true //用于控制请求头中的host值(即请求来自于哪里)
    			},
    			'/api2': {
    				target: 'http://localhost:5001',
    				pathRewrite:{'^/api2':''},
    				// ws: true, //用于支持websocket
    				// changeOrigin: true //用于控制请求头中的host值
    			}
    		}
    	}
    }
    

注意:方式一:只有本地没有的资源才会走代理,本地有的就不会把请求转发给服务器了;方式二可解决这两个问题

15. 插槽

默认插槽

让父组件可以向子组件指定位置插入html结构

//父组件中:
<Category>
   <div>html结构1</div>
</Category>

//子组件Category中:
<template>
    <div>
       //定义插槽
       <slot>插槽默认内容...</slot>
    </div>
</template>

具名插槽

有多个插槽

//父组件中:
<Category>
    <template slot="center"> //写法一(2.6.0已废弃),也可以不用template
      <div>html结构1</div>
    </template>

    <template v-slot:footer> //写法二(2.6.0),但只有在template标签上才可以
       <div>html结构2</div>
    </template>
</Category>

//子组件Category中:
<template>
    <div>
       <!-- 定义插槽 -->
       <slot name="center">插槽默认内容...</slot> //添加name属性
       <slot name="footer">插槽默认内容...</slot>
    </div>
</template>

作用域插槽

数据在子组件的自身,但根据数据生成的结构需要组件的使用者(父组件)来决定。(让父组件的插槽内容访问子组件中的数据)

//父组件中:
//必须用template
<Category>
	<template scope="随意取名字"> //写法一
		<ul>
			<li v-for="g in 随意取名字.games" :key="g">{{g}}</li>
		</ul>
	</template>
</Category>

<Category>
	<template slot-scope="随意取名字"> //写法二,在vue2.6.0已废弃
		<h4 v-for="g in 随意取名字.games" :key="g">{{g}}</h4>
	</template>
</Category>

<Category>
	<template  v-slot:default="随意取名字"> //vue2.6.0新写法
		<h4 v-for="g in 随意取名字.games" :key="g">{{g}}</h4>
	</template>
</Category>

//子组件Category中:
<template>
    <div>
        <slot :games="games"> </slot>  //把"games"传给了插槽的使用者
    </div>
</template>

<script>
    export default {
        name:'Category',
        //数据在子组件自身
        data() {
            return {
                games:['a','b','c','d']
            }
        },
    }
</script>

三、Vuex

vuex是一个插件

概念:在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

1. 原理

在这里插入图片描述

2. 安装vuex

npm i vuex(默认安装4版本)
npm i vuex@3(安装3版本)

注意:vue2只能用vuex3版本;vue3要用vuex4版本

3.使用

创建文件:src/store/index.js

index.js:
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

//1.准备actions——用于响应组件中的动作
const actions = {
	...
}

//2.准备mutations——用于操作数据(state)
const mutations = {
	...
}

//3.准备state——用于存储数据
const state = {
	...
}

//4.用于将state中的数据进行加工
const getters={

}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
	getters
})

对象中的key和value重名时,可以简写:

{
	actions:actions,
	state:state,
}

可以简写为:

{
	actions,
	state,
}
main.js:
......
//引入store
import store from './store'
......

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	store
})

4. mapState,mapGetters,mapMutations,mapActions

import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'

computed: {
   //借助mapState生成计算属性:sum、school、subject(对象写法)
    ...mapState({sum:'sum',school:'school',subject:'subject'}),
        
   //借助mapState生成计算属性:sum、school、subject(数组写法)
   ...mapState(['sum','school','subject']),
},
computed: {
    //借助mapGetters生成计算属性:bigSum(对象写法)
    ...mapGetters({bigSum:'bigSum'}),

    //借助mapGetters生成计算属性:bigSum(数组写法)
    ...mapGetters(['bigSum'])
},
methods:{
    //靠mapActions生成:incrementOdd、incrementWait(对象形式)
    ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

    //靠mapActions生成:incrementOdd、incrementWait(数组形式)
    ...mapActions(['jiaOdd','jiaWait'])
}
methods:{
    //靠mapActions生成:increment、decrement(对象形式)
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),
    
    //靠mapMutations生成:JIA、JIAN(对象形式)
    ...mapMutations(['JIA','JIAN']),
}

注意:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

5. 模块化、命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确。

  2. 修改index.js

const countAbout = {
  namespaced:true,//开启命名空间
  state:{x:1},
  mutations: { ... },
  actions: { ... },
  getters: {
    bigSum(state){
       return state.sum * 10
    }
  }
}

const personAbout = {
  namespaced:true,//开启命名空间
  state:{ ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    countAbout,
    personAbout
  }
})
  1. 开启命名空间后,组件中读取state数据:
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
  1. 开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
  1. 开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
  1. 开启命名空间后,组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

四、路由

2. 安装vue-router

vue-router是一个插件
npm i vue-router(默认安装4版本)
npm i vue-router@3(安装3版本)

注意:vue2只能用vuex3版本;vue3要用vuex4版本

3. 使用

1. 编写router配置项:

//引入VueRouter
import VueRouter from 'vue-router'
//引入路由组件
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

2. 实现切换(active-class可配置高亮样式)
(最终会转换成a标签)

<router-link active-class="active" to="/about">About</router-link>

3. 指定展示位置

<router-view></router-view>

4. 几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

3.嵌套路由(多级路由)

1. 配置路由规则,使用children配置项:

routes:[
	{
		path:'/about',
		component:About,
	},
	{
		path:'/home',
		component:Home,
		children:[ //通过children配置子级路由
			{
				path:'news', //此处一定不要写:/news
				component:News
			},
			{
				path:'message',//此处一定不要写:/message
				component:Message
			}
		]
	}
]

2. 跳转(要写完整路径):

<router-link to="/home/news">News</router-link>

4.路由的query参数

1. 传递参数

<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
				
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link 
	:to="{
		path:'/home/message/detail',
		query:{
		   id:666,
           title:'你好'
		}
	}"
>跳转</router-link>

2. 接收参数:

$route.query.id
$route.query.title

5.命名路由

1. 作用:可以简化路由的跳转。

2. 使用:

//1.给路由命名:
{
	path:'/demo',
	component:Demo,
	children:[
		{
			path:'test',
			component:Test,
			children:[
				{
	                name:'hello' //给路由命名
					path:'welcome',
					component:Hello,
				}
			]
		}
	]
}
// 2. 简化跳转:
<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>

<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>

<!--简化写法配合传递参数 -->
<router-link 
	:to="{
		name:'hello',
		query:{
		   id:666,
           title:'你好'
		}
	}"
>跳转</router-link>

6.路由的params参数

1. 配置路由,声明接收params参数

{
	path:'/home',
	component:Home,
	children:[
		{
			path:'news',
			component:News
		},
		{
			component:Message,
			children:[
				{
					name:'xiangqing',
					path:'detail/:id/:title', //使用占位符声明接收params参数
					component:Detail
				}
			]
		}
	]
}

2. 传递参数

<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
				
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link 
	:to="{
		name:'xiangqing',
		params:{
		   id:666,
           title:'你好'
		}
	}"
>跳转</router-link>

特别注意:路由携带params参数时,若使用to的对象写法,则不允许使用path配置项,必须使用name配置!

3. 接收参数:

$route.params.id
$route.params.title

7.路由的props配置

作用:让路由组件更方便的收到参数

{
	name:'xiangqing',
	path:'detail/:id',
	component:Detail,

	//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
	// props:{a:900}

	//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
	// props:true
	
	//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
	props($route){
		return {
			id:$route.query.id,
			title:$route.query.title
		}
	}
}

8. <router-link>的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push.
  3. 如何开启replace模式:
    //添加属性replace
    <router-link replace>News</router-link>
    

9.编程式路由导航

  1. 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //以下函数都是写到methods中的
    //$router的两个API
    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() //可前进也可后退,可写参数(步数)
    

10. 缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

    <!-- 缓存多个路由组件 -->
    <keep-alive :include="['News','Message']"> 		
     
     <!-- 缓存一个路由组件 -->
    <keep-alive include="News"> //此处是组件名
        <router-view></router-view>
    </keep-alive>
    

11.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    activated(){...}:路由组件被激活时触发。
    deactivated(){...}:路由组件失活时触发。

12.路由守卫

  1. 作用:对路由进行权限控制

  2. 分类:全局守卫、独享守卫、组件内守卫

  3. 全局守卫:

    //全局前置守卫:初始化时执行、每次路由切换前执行
    router.beforeEach((to,from,next)=>{
    	console.log('beforeEach',to,from)
    	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 meta:{isAuth:true}
    		if(localStorage.getItem('school') === 'zjut'){ //权限控制的具体规则 
    			next() //放行
    		}else{
    			alert('暂无权限查看')
    			// next({name:'guanyu'})
    		}
    	}else{
    		next() //放行
    	}
    })
    
    //全局后置守卫:初始化时执行、每次路由切换后执行
    router.afterEach((to,from)=>{
    	console.log('afterEach',to,from)
    	if(to.meta.title){  //meta:{isAuth:true,title:'新闻'}
    		document.title = to.meta.title //修改网页的title
    	}else{
    		document.title = 'vue_test'
    	}
    })
    
  4. 独享守卫:

    beforeEnter(to,from,next){
    	console.log('beforeEnter',to,from)
    	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
    		if(localStorage.getItem('school') === 'zjut'){
    			next()
    		}else{
    			alert('暂无权限查看')
    			// next({name:'guanyu'})
    		}
    	}else{
    		next()
    	}
    }
    
  5. 组件内守卫:

    //进入守卫:通过路由规则,进入该组件时被调用
    beforeRouteEnter (to, from, next) {
    	if(to.meta.isAuth){ //判断是否需要鉴权
     			if(localStorage.getItem('school')==='zjut'){
     				next()
     			}else{
     				alert('学校名不对,无权限查看!')
     			}
     		}else{
     			next()
     		}
    },
    //离开守卫:通过路由规则,离开该组件时被调用
    beforeRouteLeave (to, from, next) {
    	next()
    }
    

13. history模式与hash模式

hash:
http: ... /#/...
hash值不会作为地址的一部分传给服务器。
默认开启hash模式。

history:
没有 /#/

//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'

//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
 mode: 'history', //默认hash
 routes:[
 	{
 		path:'/about',
 		component:About
 	},
 	{
 		path:'/home',
 		component:Home
 	}
 ]
})

//暴露router
export default router

14. Element-UI

三、Vue3.0

1. 创建实例对象

main.js

//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'

//创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)

//挂载;应用实例必须在调用了 .mount() 方法后才会渲染出来。该方法接收一个“容器”参数
app.mount('#app')
  • vue3组件中的模板结构可以没有根标签;当根组件没有设置 template 选项时,Vue 将自动使用容器的 innerHTML 作为模板。
  • .mount() 方法应该始终在整个应用配置和资源注册完成后被调用。它的返回值是根组件实例而非应用实例;
  • 确保在挂载应用实例之前完成所有应用配置!
//应用实例会暴露一个 .config 对象允许我们配置一些应用级的选项,例如定义一个应用级的错误处理器,它将捕获所有由子组件上抛而未被处理的错误:
app.config.errorHandler = (err) => {
  /* 处理错误 */
}

2.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值