前言
这篇笔记,是根据B站尚硅谷的Vue2网课学习整理的,用来学习的
如果有错误,还请大佬指正
Vue核心
Vue简介
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。
它基于标准 HTML、CSS 和 JavaScript 构建,
并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面
官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)
初识Vue
- Vue进行工作,需要提供 Vue实例 和 模版
- 通过 new Vue() 创建出Vue实例对象
- 在Vue实例对象中,需要绑定模板
<!--准备容器-->
<div id="test01">
Hello {{word}}!!
</div>
<script>
//创建Vue实例
new Vue({
el: '#test01',//指定当前实例为那个模版服务
data: {
word: 'world'
}//提供数据,供指定模版使用
})
</script>
MVVM模型
Vue 仿照MVVM来进行设计
分为模型(Model),视图(View)和视图模型(ViewModel)三部分
- M:模型(Model) -> data中的数据
- V:视图(View) -> 模版,即 DOM
- VM:视图模型(ViewModel) -> Vue 实例对象
模版绑定 与 data 写法
模版绑定
方法一:
使用 el 来绑定
语法 : el : ' css选择器 '
new Vue({
el: '#test01',
data: {
word: 'world'
}
})
方法二:
语法:实例对象 . $mount ( ' css选择器 ' )
const vm = new Vue({
data: {
word: 'world!!!'
}
})
vm.$mount('.test01')
data写法
写法一:对象式
语法:data : { 数据对象 }
data: {
word: 'world!!!'
}
写法二:函数式
必须返回一个数据对象
不要写箭头函数
data: function(){
return{
word:'world!!!'
}
}
模版语法
Vue 使用一种基于 HTML 的模板语法,
使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上
简单来说:
将Vue实例中的数据,呈现到 HTML 上
插值语法
可以分析标签体内容(标签包裹内容)
语法:
{{JS表达式}}
示例:
<div id="test01">
Hello {{word}}
<!-- {{}} 中的内容会被替换为Vue实例中的数据-->
</div>
<script>
new Vue({
el:'#test01',
data:{
word:'world!!!'
}
})
</script>
{{ }} 中的内容会被替换为Vue实例中的数据
指令语法
可以解析标签(标签属性、标签体内容),并可以绑定事件
指令有很多,以 v- 为开头
例:v-bind: (可简写为 : )
v-bind : 标签属性 = 'JS表达式' 或 : 标签属性 = 'JS表达式'
<div id="test02">
<a v-bind:href="Prohibited">未满18不要打开</a>
<!-- "" 中的内容会被替换为Vue实例中的数据-->
</div>
<script>
new Vue({
el: '#test02',
data: {
Prohibited: 'https://store.steampowered.com/app/2358720/_/'
}
})
</script>
数据绑定
单向数据绑定
语法:
v-bind : 标签属性 = 'JS表达式' 或简写为 : 标签属性 = 'JS表达式'
数据只能从data流向页面
即 修改data中的数据,同时改变页面数据
双向数据绑定
语法:
v-model:value="JS表达式" 或简写为 v-model="JS表达式"
用于有value的标签
数据不仅能从 data 流向页面,还能从页面流向data
即 修改data中的数据,同时改变页面数据 ; 修改页面中的数据,同时改变data数据
数据代理
通过一个对象,代理对另一个对象中的属性的操作(读/写)
Object . defineProperty()
Object . defineProperty()可以为对象添加属性
语法:
Object . defineProperty ( 对象名 , ' 添加属性 ' , {
get函数 ,
set函数
})
get函数需要有返回值,作为添加属性的值
当读取所添加属性时,get函数会被调用
当修改所添加属性时,set函数会被调用
let num = 18
let Person = {
name: 'zhao',
school: 'sn'
}
Object.defineProperty(Person, 'age', {
//读取age时,调用get函数,返回值为age的值
get() {
return num
},
//修改age时,调用set函数,修改值作为参数
set(value) {
num = value//将参数,也就是修改值,传给num
}
})
使用Object . defineProperty(),实现了Person对num的数据代理,
修改Person的属性值,就可以修改num
Vue中的数据代理
在Vue实例对象中,data的数据存放在Vue._data中
为了更方便地操作data中的数据,Vue通过Object . defineProperty(),
对data中数据进行了数据代理
事件处理
事件基本使用
使用指令 v-on 来进行事件绑定
语法:v-on : 事件 = " 函数名 "
简写:@事件 = " 函数名 "
对应函数需要配置到Vue实例里,methods对象中
<div>
<button v-on:click="test01">普通按钮01</button>
</div>
<script>
const vm=new Vue({
el:'div',
methods:{
test01(){
console.log(11111)
}
}
})
</script>
函数参数
<div>
<button v-on:click="test02">普通按钮02</button>
<button v-on:click="test03(3)">普通按钮03</button>
<button v-on:click="test04($event,4,44)">普通按钮04</button>
</div>
<script>
const vm=new Vue({
el:'div',
methods:{
test02(e){
console.log(e)
console.log(2222)
},
test03(a){
console.log(a)
console.log(3333)
},
test04(e,a,b){
console.log(e,a,b)
console.log(4444)
}
}
})
</script>
以上代码为例:
- 没有参数时,传入事件对象(test02)
- 有参数时,没有事件对象(test03)
- 可以用$enevt,来配置事件对象(test04)
注:
- 不要用箭头函数,因为this将不再指向vm
事件修饰符
用于修饰事件
语法:v-on : 事件 . 事件修饰符 = " 函数名 "
简写:@事件 . 事件修饰符 = " 函数名 "
事件修饰符 | 作用 |
prevent | 阻止事件的默认行为 |
stop | 停止事件冒泡 |
once | 事件仅触发一次 |
capture | 使用事件捕获 |
self | e.target为当前元素时触发 |
passive | 默认行为立即执行,再进行回调函数 |
示例:
<div class="out" @click="work">
out
<div class="in" @click.stop="work">
<!--阻止事件冒泡-->
in
</div>
</div>
修饰符可以连写
<div class="in" @click.stop.prevent="work" >
按键修饰符
Vue提供了一些触发键盘事件时使用的按键修饰符
用于在按下某个按键时,触发事件
语法:v-on : 键盘事件 . 按键别名 = " 函数名 "
简写:@键盘事件 . 按键别名 = " 函数名 "
按键别名为Vue提供,即 按键码的别名
<!--在按下回车键时触发事件-->
<input type="text" @keyup.enter="work">
一些常用键盘别名
- 回车键 ==》enter
- 删除键 ==》delete
- 退出键 ==》esc
- 空格键 ==》space
- 换行键 ==》tab(最好与keyup使用)
- 上键 ==》up
- 下键 ==》down
- 左键 ==》left
- 右键 ==》right
未提供的键盘别名可以用按键Key值,使用短横线命名法
系统修饰键
如ctrl,alt,shift,meta(win键)有特殊用法
配合keyup使用:
按下系统修饰键,再按下并放开其他键时,触发事件
配合keydown使用:
按下系统修饰键即触发事件
定制按键别名
语法:
Vue.config.keyCodes.自定义按键名= 键码
计算属性
概念
属性:Vue实例中,data中的都认定为属性
计算属性:所需要的属性不存在,需要通过已有属性来计算获得
计算属性需要配置到Vue实例里,computed对象中
语法:
const vm=new Vue({
el:'.root',
data:{
属性
},
computed:{
计算属性名:{
get函数
}
}
})
通过get()函数来进行计算,需要有return返回值,作为计算属性的值
get(调用时机):
- 在初次读取计算属性时,get()调用
- 计算属性所依赖属性数据发生变化时,也会调用
备注:
- 也可以调用set()函数,进行数据改动
- get()和set()函数的this均指向Vue实例
- 计算属性会最终会出现在Vue实例上,直接调用即可
案例:
<div class="root">
<input type="text" v-model:value="xing"><br>
<input type="text" v-model:value="ming"><br>
<p>
全名:{{quanming}}
</p>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
xing: '张',
ming: '三'
},
computed: {
quanming: {
get() {
return this.xing + this.ming
}
}
}
})
</script>
简写
在没有写set()函数时,可以使用简写
语法:
computed: {
计算属性名() {
函数体
}
}
computed: {
quanming() {
return this.xing + this.ming
}
}
监视属性
概念与语法
可以设置被监视的属性,当被监视属性发生改变时,会自行调用函数
写法1:
const vm = new Vue({
el: 'div',
watch: {
监视属性名: {
immediate: 布尔值,//默认为false,是否初始化时调用
deep: 布尔值,//默认为false,是否开启深度监视
handler(newValue, oldValue) {
//newValue为新值,oldValue为旧值
函数体
}
}
}
})
写法2:
vm.$watch('监视属性名', {
immediate: 布尔值,//默认为false,是否初始化时调用
deep: 布尔值,//默认为false,是否开启深度监视
handler(newValue, oldValue) {
//newValue为新值,oldValue为旧值
函数体
}
})
在监视属性改变时,会调用handler()函数
深度监视
配置deep: true时开启深度监视
Vue中watch默认不检测对象内部值的改变
开启深度监视可以检测对象内部值的改变
简写
当deep和immediate均没有配置时,可以简写
简写1:
const vm = new Vue({
el: 'div',
watch: {
监视属性名(newValue, oldValue): {
//newValue为新值,oldValue为旧值
函数体
}
}
})
简写2:
vm.$watch('监视属性名', function(newValue, oldValue){
//newValue为新值,oldValue为旧值
函数体
})
计算属性与监听属性区别
- computed相当于watch更加简单
- computed能完成的,watch均能完成
- watch能完成的,computed不一定能完成(watch可进行异步操作)
绑定样式
绑定class
1.字符串写法
直接使用v-bind来绑定样式
可以绑定事件,来动态改变类名
<div class="main" :class="mood">{{text}}</div>
<head>
<style>
.main {
width: 100px;
height: 100px;
border: 1px solid black;
}
.test01 {
background-color: aqua;
}
</style>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<div class="main" :class="mood">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '111',
mood: 'test01',
}
})
</script>
</body>
2.数组写法
用于绑定多个样式
可以通过动态操作数组来控制类
<head>
<style>
.work01 {
font-size: 20px;
}
.work02 {
color: aqua;
}
.work03 {
font-style: italic;
}
</style>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<div class="main" :class="classArr">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '111',
classArr: ['work01', 'work02', 'work03']
}
})
</script>
</body>
3.对象写法
用于绑定多个样式,并动态决定用与不用
控制对象中的布尔值,动态决定类的用与不用
<head>
<style>
.work01 {
font-size: 20px;
}
.work02 {
color: aqua;
}
.work03 {
font-style: italic;
}
</style>
<script src="../vue.js"></script>
</head>
<body>
<div id="root">
<div class="main" :class="classObj">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '111',
classObj: {
work01: true,
work02: true,
work03: true,
}
}
})
</script>
</body>
绑定style
1.直接绑定
直接使用v-bind来绑定样式
语法:
: style = " { 属性(驼峰命名法) : xxx } "
xxx为Vue示例中的data数据
<div id="root">
<div class="main" :style="{fontSize:fs}">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '1111',
fs:'20px'
}
})
</script>
2.对象写法
<div id="root">
<div class="main" :style="styleObj">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '1111',
styleObj: {
fontSize: '40px',
backgroundColor: 'aqua'
}
}
})
</script>
3.数组写法
需要在数组中配置对象
<div id="root">
<div class="main" :style="styleArr">{{text}}</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
text: '1111',
styleArr: [
{
fontSize: '40px',
backgroundColor: 'aqua'
},
{
color: 'blue'
}
]
}
})
</script>
条件渲染
v-show
语法:
v-show = “ 表达式 ”
表达式值为布尔值
表达式值为false,元素被隐藏,display值为none
v-if
语法:
v-if = “ 表达式 ”
后可接v-else-if和v-else
表达式值为布尔值
表达式值为false时,元素直接被删除,替换为空注释
<div id="root">
<div v-show="false">{{test01}}</div>
<div v-show="true">{{test01}}</div><br>
<div v-if="false">{{test02}}</div>
<div v-if="true">{{test02}}</div>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
test01:'111',
test02:'222'
}
})
</script>
列表渲染
v-for遍历
遍历对象
语法:
v-for = " ( value , key) in 对象名 " 或
v-for = " value in 对象名 "
value获取属性值,key获取属性名
<div id="root">
<li v-for="(value,key) in Person">
{{value}}----{{key}}
</li>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
Person:{
name:'Tom',
age:18,
shool:'sdau'
}
}
})
</script>
遍历数组
语法:
v-for = " ( value , item) in 数组名 " 或
v-for = " value in 对象名 "
value获取值,item获取索引
<div id="root">
<li v-for="(value,item) in Arr">
{{value}}----{{item}}
</li>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
Arr: ['A','B','C']
}
})
</script>
key的使用与作用
key的使用
在 v-for遍历 中要添加一个 key属性
key值的选择,最好使用每条数据的唯一标识
例如ID,身份证,学号等等
如果不添加 key属性 则key值默认为数组或对象的序列号
<div id="root">
<li v-for="(p,item) in Person" :key="p.id">
{{p.name}}---{{p.age}}
</li>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
Person:[
{id:'001',name:'wu',age:18},
{id:'002',name:'liu',age:20},
{id:'003',name:'zhao',age:19},
]
}
})
</script>
key的作用和虚拟DOM
虚拟DOM是Vue根据数据生成的
key是虚拟DOM对象的标识,可以理解成虚拟DOM对象的唯一ID
当数据发生改变时,Vue会根据新数据生成新虚拟DOM
新旧DOM会进行对比:
1.若有相同的key值:
- 若虚拟DOM内容没有变,直接使用真实DOM生成页面
- 若虚拟DOM内容变化,生成新真实DOM替换之前的
2.若没有相同的key值:
- 直接建立真实DOM,生成页面
Vue监视数据原理
基本
Vue会监视data中所有有层次的数据
被监视的对象,会添加get()和set()函数,以实现响应式
( get()和set()函数用自Object . defineProperty())
示例:
data: {
Person: {
name: 'Tom',
age: 18,
shool: 'sdau'
},
Arr: ['A','B','C']
}
注:数组中的数据没有进行监视,但数组本身被监视
如上图,Arr数组有get()和set()函数
如下图,Arr[0]以及其他数据没有get()和set()函数
关于对象
对象中的数据通过 get()和set()函数 监视
对象中后追加的属性,Vue不会进行监视,即不会有get()和set()函数
如果需要监视追加的属性,则需要用到 特定API
语法:
Vue.set(对象名,属性名,属性值)
另一种写法
this.$set(对象名,属性名,属性值)
对象名指被添加属性的对象,注意 ' '
示例:给Person对象添加sex属性,属性值为 男
addSex() {
//Vue.set(this.Person,'sex','男') 另一种写法
this.$set(this.Person, 'sex', '男')
}
用此API添加的属性,拥有get()和set()函数,会被监视
注:
该API,不能给vm或vm的根数据对象(data等)添加属性
关于数组
因为Vue中数组没有get()和set()函数
所有Vue通过其他方式,来实现数组数据的响应式
在数组中修改数据,必须使用以下方法:
- 使用API:push(),pop(),shift(),unshift(),splice(),sort(),reverse()
- 使用Vue.set() 或 vm.$set().
Vue.set(数组名,序列,修改值)
另一种写法
this.$set(数组名,序列,修改值)
只有使用以上方法数组数据才可以实现响应式
当然,也可以直接修改数组本身,因为数组本身有get()和set()函数
过滤器
定义:
对要显示的数据进行特定格式化后再显示(用于一些简单的逻辑处理)
注册过滤器(两种方式):
Vue.filters(过滤器名称,function(){
函数体
})//全局过滤器
或
new Vue({
filters:{
过滤器名称(){
函数体
}
}
})//局部过滤器
使用过滤器:
过滤器可以用于插值语法 和 v-bind指令
- {{ xxx | 过滤器名称 }}
- v-bind:属性 = " xxx | 过滤器名称 "
常见内置指令
之前接触过的指令
指令 | 作用 | 简写 |
v-bind | 单向绑定解析表达式 | :xx |
v-model | 双向数据绑定 | v-model="xx" |
v-for | 遍历数组/对象/字符串 | 无 |
v-on | 绑定事件监听 | @ |
v-if / v-else | 条件渲染(控制节点是否存在) | 无 |
v-show | 条件渲染(控制节点是否展示) | 无 |
v-text指令
作用:
向所在节点渲染文本
<div id="root">
<p>{{name}}</p>
<p v-text="name"></p>
<p v-text="name">1111</p>
</div>
<script>
new Vue({
el:'#root',
data:{
name:'zhao'
}
})
</script>
注:
v-text会替换掉节点中的内容
v-html指令
作用:
向指定节点中渲染包含html结构的内容
可以识别html结构
<div id="root">
<div v-html="test"></div>
</div>
<script>
new Vue({
el:'#root',
data:{
test:'<p>123456</p>'
}
})
</script>
注(v-html有安全性问题):
- 在网站上动态渲染任意HTML非常危险,容易导致xss攻击
- 在可信内容上使用v-html,不用用在用户提交内容上
v-cloak指令
在网速非常慢的时候,可能会展示未经过Vue渲染的页面
因为网速慢,Vue会加载不出来
可以使用v-cloak指令配合CSS来解决
- v-cloak没有值
- v-cloak本质是一个属性
- v-cloak在Vue加载完成后删除
<style>
[v-cloak]{
display: none;
}
</style>
<div id="root">
<div v-cloak>111</div>
</div>
v-once指令
- v-once没有值
- v-once所在节点只进行初次动态渲染
- 初次动态渲染完成后,变为静态内容
- 以后数据变化不会引起所在结构改变
v-pre指令
用于跳过其所在节点的编译过程
<p v-pre>{{name}}</p>
自定义指令
函数式
自定义指令可以通过配置到Vue实例里,directives对象中来实现
语法:
new Vue({
directives:{
指令名(element,binding){
函数体
}
}
})
其中传入的参数:
element为绑定自定义指令标签的DOM对象
binding为DOM对象的相关属性
绑定自定义指令时要加v-
<div class="root">
<p v-test01>1111</p>
</div>
<script>
const vm=new Vue({
el:'.root',
directives:{
test01(element,binding){
console.log(element,binding)
}
}
})
</script>
自定义指令中函数调用时机:
- 指令与元素成功绑定时函数调用
- 指令所在模板重新解析时函数调用
注:命名如果有多个单词,使用 - 链接
对象式
对象式自定义指令函数调用时机更加细致,会比函数时多一次调用
语法:
new Vue({
directives:{
指令名:{
bind(element,binding){
函数体
},
inserted(element,binding){
函数体
},
update(element,binding){
函数体
}
}
}
})
传入的参数同函数式
函数调用时机:
- bind函数:指令与元素绑定成功时调用
- inserted函数:指令所在元素被插入页面时调用
- update函数:指令指令所在模板重新解析时调用
全局自定义指令
通过在Vue实例中配置directives对象所设置的为局部指令
全局自定义指令语法:
Vue.directive(指令名,配置对象)
Vue.directive(指令名,回调函数)
<div class="root">
<div v-test03>33</div>
<div v-test04>44</div>
</div>
<script>
Vue.directive('test03', {
bind(element, binding) {
console.log(element, binding)
},
inserted(element, binding) {
console.log(element, binding)
},
update(element, binding) {
console.log(element, binding)
}
})
Vue.directive('test04',function(element, binding){
console.log(element, binding)
})
</script>
生命周期
概念
生命周期 又名:生命周期回调函数,生命周期函数,生命周期钩子
生命周期本质上就是函数
Vue会在特殊时刻帮我们调用的一些特殊名称的函数
生命周期函数名字不可更改,其配置在Vue实例中,其中this指向Vue示例
示例(以mounted为例):
<script>
const vm = new Vue({
el: '#root',
mounted() {
console.log(this)
},
})
</script>
“Vue的一生”
创建(出生)
Vue刚刚创建,仅完成初始化,此时数据监测和数据代理创建还未开始;
初始化完成后,进行数据监测和数据代理创建时,会调用两个生命周期函数(不是Vue创建时)
此时 模板未解析
1.数据监测和数据代理之前,初始化后:
- 调用beforeCreate函数
- 此时无法通过vm访问data中的数据,methods中的方法
- 因为数据监测和数据代理未完成
2.数据监测和数据代理之后,解析模板之前:
- 调用created函数
- 此时可以通过vm访问data中的数据,methods中的方法
- 因为模板未解析,页面不会显示解析内容
挂载(生活)
挂载即把初始的真实DOM元素放到页面
在Vue解析完模板,生成虚拟DOM后,进行虚拟DOM转真实DOM
此时调用两个生命周期函数
1.解析完模板后,挂载完成前:
- 调用beforeMount函数
- 页面此时呈现未未经Vue编译的DOM
- 所有对DOM的操作,最终都不会奏效
2.挂载完成后:
- 调用mounted函数
- 对DOM的操作有效,但尽量避免
- 此时可以进行一些初始化操作,比如开启定时器,发送网络需求等
更新(颠覆三观)
在数据更新时,会调用两个生命周期函数
1.新虚拟DOM生成前:
- 调用beforeUpdate函数
- 此时Data数据是新的,但是页面没有进行更新
2.页面更新后:
- 调用updated函数
- 此时Data数据和页面都是新的
销毁(逝世)
Vue可以通过Vue.$destroy()来销毁
销毁时会调用两个生命周期函数
1.销毁前(回光返照):
- 调用beforeDestroy函数
- 此时Vue的所有配置都还可以使用,即将销毁
- 一般在此阶段进行关闭定时器,取消订阅的收尾操作
2.销毁后(已寄):
- 可以调用destroyed函数,但Vue以及没有
注意事项
常用生命周期钩子:
- mounted:初始化操作
- beforeDestroy:收尾操作
关于销毁Vue:
- 销毁后自定义事件失效,但原生DOM事件依然有效
- 一般不会使用beforeDestroy操作Data数据,因为用不到了
Vue组件化编程
组件
实现应用中局部功能代码和资源的集合
扒一张官网的图(下图):
在一个大盒子中可以分三个中盒子(灰色) ,中盒子又可以分成几个小盒子(深灰色)
这几个盒子中代码和资源的集合可以视为一个组件
作用:组件可以复用编码,简化项目编码
当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用
非单文件组件
特点:一个文件中包含多个组件
Vue使用组件有三步:
- 定义组件(创建组件)
- 注册组件
- 使用组件
定义组件(创建组件)
定义组件需要使用Vue.extend()在全局中进行定义
其中()中传入的内容与 new Vue()需要传入的几乎一模一样
区别:
- 不写el配置:最终所有组件由vm中的el来决定服务于那个容器
- data必须写出函数式:避免数据存在引用关系
- 使用template配置组件结构
示例:
//定义组件
const Person = Vue.extend({
template: `
<div>
<p>{{name}}--{{age}}</p>
</div>
`,
data() {
return {
name: 'zhao',
age: '18'
}
}
})
注册组件
1.局部注册
在Vue实例中配置components对象
const vm = new Vue({
el: '#Root',
//注册局部组件
components: {
Person: Person
//简写:Person
}
})
2.全局注册
语法:
Vue.component('组件名', 组件)
//注册全局组件
Vue.component('School', School)
使用组件
在HTML中编写组件标签即可
<div id="Root">
<!--使用组件-->
<Person></Person>
<br>
<School></School>
</div>
示例代码
<body>
<div id="Root">
<!--使用组件-->
<Person></Person>
<br>
<School></School>
</div>
<script>
//定义组件
const Person = Vue.extend({
template: `
<div>
<p>{{name}}--{{age}}</p>
</div>
`,
data() {
return {
name: 'zhao',
age: '18'
}
}
})
const School = Vue.extend({
template: `
<div>
<p>{{name}}--{{address}}</p>
</div>
`,
data() {
return {
name: 'sdau',
address: '泰安'
}
}
})
//注册全局组件
Vue.component('School', School)
const vm = new Vue({
el: '#Root',
//注册局部组件
components: {
Person: Person
//简写:Person
}
})
</script>
</body>
注意事项
组件名
- 组件名如果由多个单词组成,使用kebab-case命名法:my-school
- 也可以使用CamelCase命名:MySchool,但是需要Vue脚手架
- 可以使用name配置指定组件在开发者工具中呈现的名字
const School = Vue.extend({
name:'xxx',
template: `
<div>
<p>{{name}}--{{address}}</p>
</div>
`,
data() {
return {
name: 'sdau',
address: '泰安'
}
}
})
简写
const School = Vue.extend(options)
简写为:
const School = options
组件其他事项
组件的嵌套
组件可以注册在组件中,实现组件的嵌套
示例:
<body>
<div id="Root">
<App></App>
</div>
<script>
const School = Vue.extend({
template: `
<div>{{name}}--{{address}}</div>
`,
data() {
return {
name: 'sdau',
address: '泰安'
}
}
})
const Person = Vue.extend({
template: `
<div>{{name}}--{{age}}</div>
`,
data() {
return {
name: 'zhao',
age: 18
}
}
})
const App = Vue.extend({
template: `
<div>
<Person></Person>
<School></School>
</div>
`,
components: {
School,
Person
}
})
const vm = new Vue({
el: '#Root',
components: {
App
}
})
</script>
</body>
VueComponent
组件的本质是一个名为VueComponent的构造函数,由Vue.extend生成
在我们使用组件时,Vue会解析并创建出组件的实例对象
即Vue会帮我们调用 new VueComponent(options)
另外,每次调用Vue.extend,返回值都是一个全新的VueComponent
关于this指向:
- 组件配置中,this指向均是【VueComponent实例对象】
- new Vue()配置中,this指向均是【Vue实例对象】
一个内置关系
在Vue中,组件原型对象 的 原型 为Vue示例的 原型对象
即:VueComponent.prototype.__proto__===Vue.prototype
可以让组件实例对象可以访问到Vue原型上的属性和方法
单文件组件
特点:一个文件中只包含1个组件
将非单文件组件中的多个组件进行拆分,写到多个文件中,每个文件就是一个组件
每个组件文件的后缀为 .vue
通过ES6模块化语法进行导出和引入
导出
组件文件写法:
其中使用 export default { } 来导出组件
<template>
<div>
模板
</div>
</template>
<script>
//导出
export default {
Vue配置项
};
</script>
<style>
样式 /*可省略*/
</style>
示例:
<template>
<div class="test">
<h2>{{ name }}--{{ address }}</h2>
<button @click="show">123456</button>
</div>
</template>
<script>
export default {
name: "SchoolName",
data() {
return {
name: "sdau",
address: "山东泰安",
};
},
methods: {
show() {
alert("123456");
},
},
};
</script>
<style>
.test {
background-color: aqua;
}
</style>
引入
引入可以使用 import···from··· 来引入
import 组件名 from "组件地址";
示例:
<template>
<div>
<PersonName></PersonName>
<SchoolName></SchoolName>
</div>
</template>
<script>
import PersonName from "./components/PersonName.vue";
import SchoolName from "./components/SchoolName.vue";
export default {
name: "App",
components: {
PersonName,
SchoolName,
},
};
</script>
ref属性
ref属性用来给元素或子组件注册引用信息(id属性的替代者)
语法:
- 标记(示例):<h1 ref="xxx">···<h1> 或 <组件名 ref="xxx">···</组件名>
- 获取:this.$refs.xxx
关于获取值:
- 应用在html标签上,获取真实DOM元素
- 应用在组件标签上,获取组件示例对象
<template>
<div id="app">
<img ref="img" alt="Vue logo" src="./assets/logo.png" />
<HelloWorld ref="hello" msg="Welcome to Your Vue.js App" />
<button @click="show">按钮</button>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
components: {
HelloWorld,
},
methods: {
show() {
console.log(this.$refs);//返回对象
console.log(this.$refs.img);//返回真实DOM
console.log(this.$refs.hello);//返回组件示例对象
},
},
};
</script>
配置项props
配置项props可以让组件接受外部传来的数据
当父组件引入其他组件,父组件所需数据子组件没有时,
可以使用配置项props,通过父组件给子组件数据赋值
是一种组件间的通信方式,将数据从父组件传递给子组件
传递与接收
1.传递方(父组件):
<子组件名 数据名=" 值 "/>
将传入的值传递给子组件
2.接收方(子组件):
props:['数据名']
接收到父组件传递来的数据
示例:
- App.vue(传递方,部分代码):
<template>
<div id="app">
<MyPerson name="zhao"/><!--传递-->
</div>
</template>
- MyPerson(接收方,部分代码):
<template>
<div>
<h1>{{ name }}--{{ age }}</h1>
</div>
</template>
<script>
export default {
name: "MyPerson",
data() {
return {
age: 18,
};
},
props:['name']//接收
};
</script>
接收的三种方法
接收数据共有三种方法:
1.只接收
props:['name']
2.限制类型
props:{
name:String//限制name为字符串
}
3.限制类型与必要性,指定默认值
props: {
name: {
type: String, //限制name为字符串
required: true, //必要性
default: "zhao", //默认值
},
},
必要性:父组件必须传值
必要性与默认值一般不在一起使用,有矛盾
mixin(混入)
功能:将多个组件共用的配置,进行提取,成为一个混入对象,进行使用
定义混入
混入可以定义在另一个JS文件中
即多个组件都有的配置,提取到一个JS文件中
export const test={
methods:{
show(){
console.log(this.name);
}
}
}
通过导出,以便于组件使用
使用混入
局部混入
在引入混入后,可以通过mixins配置项来使用混入
<template>
<div>
<h1>
{{name}}--{{age}}
</h1>
<button @click="show">123</button>
</div>
</template>
<script>
import {test} from './mixin.js'
export default {
name:'MyPerson',
data(){
return{
name:'zhao',
age:18
}
},
mixins:[test]
}
</script>
全局混入
可以直接在App组件中引入混入,使用Vue.mixin(xxx)来进行全局混入
import {test} from './components/mixin'
Vue.mixin(test)
插件
功能:用于增强Vue
本质:插件本质上是一个对象,其中含有一个install方法
install的第一个参数是Vue,所有插件可以实现各种功能
定义插件
对象名.install = function(Vue){
函数体
}
使用插件
Vue.use(对象名)
scoped样式
在Vue组件化编程中,每个组件的样式也会影响到其他的组件,产生冲突
作用:
可以让样式在局部生效,防止冲突
写法:
<style scoped>
组件自定义事件
组件自定义事件是一种组件间的通信方式,将数据从子组件传递给父组件
绑定
绑定自定义事件都是在父组件中
方法一
直接在父组件中,给子组件绑定事件
<MyPerson @gainP="getPersonName" />
如上,为子组件MyPerosn绑定名为gainP的事件
触发事件调用getPersonName回调函数
方法二
在生命周期钩子中绑定,用到ref属性
<MySchool ref="dome" />
......
mounted() {
this.$refs.dome.$on("gainS", this.getSchoolName);
},
如上,为子组件MySchool绑定名为gainS的事件
触发事件调用getSchoolName回调函数
整体代码示例(父组件):
<template>
<div id="app">
<p>{{ PersonName }}//{{ SchoolName }}</p>
<MyPerson @gainP="getPersonName" />
<MySchool ref="dome" />
</div>
</template>
<script>
import MyPerson from "./components/MyPerson.vue";
import MySchool from "./components/MySchool.vue";
export default {
name: "App",
components: {
MyPerson,
MySchool,
},
data() {
return {
PersonName: "",
SchoolName: "",
};
},
methods: {
getPersonName(test) {
this.PersonName = test;
},
getSchoolName(test) {
this.SchoolName = test;
},
},
mounted() {
this.$refs.dome.$on("gainS", this.getSchoolName);
},
};
</script>
触发
触发自定义事件都是在子组件中
通过触发原生事件来触发自定义事件
语法:
this.$emit("自定义事件名", 传入数据);
示例:
<button @click="givePerosnName">触发事件</button>
......
methods: {
givePerosnName() {
this.$emit("gainP", this.Name);
},
},
如上,触发按钮点击事件,调用givePerosnName函数
在givePerosnName函数中触发gainP自定义事件
并且传入数据this.Name
解绑
解绑事件也是在子组件中
语法:
this.$off("自定义事件名");
示例:
<button @click="relieve">解绑事件</button>
......
methods: {
relieve() {
this.$off("gainP");
},
},
如上,触发按钮点击事件,调用relieve函数
在relieve函数中解绑gainP自定义事件
其他
- 组件可以绑定原生DOM事件,但是需要 native 修饰符
- 通过方法二来绑定事件时,回调函数要配置在methods中,或用箭头函数,这样this指向正常,为父组件。使用普通函数,this指向子组件。
全局事件总线
全局事件总线是一种组件之间的通信方式,可以用于任意组件间通信
其并非Vue所提供的方法,而是一种思路,所以写法不唯一
核心思想是找到一个各个组件均可进行通信的中介,来进行任意组件间的通信
虽然写法不唯一,但只记一种
安装全局事件总线
中介可以创建在Vue实例对象的原型上
通过生命周期钩子在Vue实例对象创建完成前安装
在main.js中
new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
}
}).$mount('#app')
如上:创建名为$bus的中介
使用全局事件总线
接收
接收数据要在接收方组件中,给中介$bus绑定自定义事件
事件的回调配置在接收方组件中
methods: {
getPersonName(test) {
this.PersonName = test;
},
},
mounted() {
this.$bus.$on('gainP',this.getPersonName)
},
如上:
为$bus绑定gainP的自定义事件,函数回调在组件methods配置中
另外,最好在beforeDestroy生命周期钩子中,用$off解绑当前组件所用事件
提供
提供数据只需要在提供方触发$bus的自定义事件即可
this.$bus.$emit("gainP", this.Name);
消息订阅与发布
消息订阅与发布是一种组件之间的通信方式,可以用于任意组件间通信
1.安装
需要安装pubsub
npm i pubsub-js
2.引入
import pubsub from 'pubsub-js'
3.接收数据
methods: {
dome(){
......
}
},
mounted(){
this.pid=pubsub.subscribe('xxx',this.dome)//订阅消息
}
4.提供数据
pubsub.publish('xxx',数据)
5.取消订阅
PubSub.unsubscribe(pid)
插槽
作用:是一种组件间的通信方式,可以让父组件向子组件指定位置插入HTML结构
是一种将数据从父组件传递给子组件的方式
默认插槽
父组件(传递方):
设置要传给子组件的HTML结构,需要在使用组件时,在组建标签内写入
<MyTest>
<div>Hello World</div>
</MyTest>
<!--MyTest为组件名-->
子组件(接收方):
在子组件中需要进行定义插槽,确定传入HTML的插入位置
HTML结构会插入在<slot>标签的位置
<template>
<div>
<slot>默认内容</slot>
<!--如果没有传入HTML,则渲染slot标签内的默认内容-->
</div>
</template>
具名插槽
当需要插入多个插槽时,可以用具名插槽
子组件(接收方):
子组件定义多个插槽,并配置name
<template>
<div>
<slot name=dome1>默认内容</slot>
<slot name=dome2>默认内容</slot>
</div>
</template>
父组件(传递方):
给传递的HTML结构配置solt,值为插槽的name值
<MyDome>
<div slot='dome1'>Hello World</div>
<div slot='dome2'>Hello World</div>
</MyDome>
作用域插槽
当需要使用的data数据不在传递方父组件自身,而在子组件里时
子组件(接收方):
子组件在定义插槽时,将数据传入插槽中
<template>
<div>
<slot :word='word'>默认内容</slot>
</div>
</template>
<script>
export default {
name: "TheDome",
data() {
return {
word: "Hello World",
};
},
};
</script>
父组件(传递方):
必须使用<template>标签,并在标签内配置scope
<TheDome>
<template scope="scopeData">
<div>{{ scopeData.word }}</div>
</template>
</TheDome>
如上, 子组件传递的数据,以对象形式,给了scopeData
nextTick
语法:
this.$nextTick(()=>{
回调函数
})
作用:
在下一次DOM更新后执行回调
用于改变数据后,基于更新后的新DOM进行操作时
Vue封装的过渡与动画
如图所示:
Vue封装的过渡与动画分为进入(enter)和离开(leave)
进入和离开又分为起点,过程中(active)和终点(to)
类名
- 元素进入:
- v-enter:进入起点
- v-enter-active:进入过程
- v-leave-to:进入终点
- 元素离开:
- v-leave:离开起点
- v-leave-active:离开过程
- v-leave-to:离开终点
在Vue中添加过渡与动画,需要给Vue所配置的类添加样式
过渡/动画
过渡的添加,需要六种样式全部配置
.v-enter,
.v-leave-to {
transform: translateX(-100%);
}
.v-enter-to,
.v-leave {
transform: translateX(0);
}
.v-enter-active,
.v-leave-active {
transition: 1s;
}
使用 <transition>标签将要进行过度的元素进行包裹
<transition>
<div class="dome" v-show="xxx">111</div>
</transition>
被包裹的元素会自行执行样式中所写的过渡
动画的添加,只需要配置v-enter-active和v-leave-active即可,其他同过渡
.v-enter-active {
animation: 1s test01;
}
.v-leave-active {
animation: 1s test01 reverse;
}
@keyframes test01 {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
多个元素过渡
相同样式:
多个元素,共用一个过渡,需要用 <transition-group>标签包裹
并且每个元素都要指定key值
<transition-group>
<div class="dome" v-show="xxx" key="1">111</div>
<div class="dome" v-show="xxx" key="2">222</div>
</transition-group>
不同样式:
多个元素,不共用一个过渡
要为<transition>标签配置name属性
<transition name='test01'>
<div class="dome" v-show="xxx">111</div>
</transition>
在写样式时,类名中的v换成name值
例:
.test01-enter,
.test01-leave-to {
transform: translateX(-100%);
}
Vue中的Ajax
Vue脚手架配置代理
配置代理可以解决开发环境 Ajax 跨域问题
简
在vue.config.js中添加配置
devServer:{
proxy:'xxx'//xxx为服务器基础路径