Vue2基础教程(脚手架版)
本文讲解使用Vue脚手架进行Vue2学习,适合学习完一遍vue2基础的人进行复习
目录
(1).用ide打开刚刚创建的项目名称文件夹(记住是项目名称文件夹)
一. Vue2脚手架的基础搭建
(一) npm的安装
使用Vue2脚手架,需要安装NodeJS(LTS版)使用npm,在官网安装完毕即可
(二) Vue2脚手架基础和使用
1. 创建代码文件夹
使用脚手架前,请先在系统文件夹内创建一个专业用于练习或编写Vue代码的文件夹,防止系统文件夹错乱。
创建完毕后,使用系统cmd命令进入此文件夹。(Windows可以进入导航栏输入cmd快速进入该文件夹)。
2.创建Vue脚手架模版
(1).输入指令vue create [项目名]
vue create [项目名]
这里取名为study1(项目名不能包含大写字母)
(2).选择创建模板
通过小箭头上下左右选择,Enter确定
对于新手来说,我们选择Manually select features(自定义选择)
(3).选择创建参数
绿色代表已选中,白色代表未选中。空格可以切换选中状态。
这里我们选择Babel,会写Sass或Less的可以勾上CSS Pre-processors
将Linter / Formatter取消勾选,切记
选择好了以后便是这样,Enter确认
这里选择2.X,Vue3就选择3.X。
这里选择package.json
这里它会问我们是否保存本次选择作为模板,这里我选择是。这个模版对于新手学习Vue2来说足够。
选择完毕后,开始创建Vue2工程。(根据网络情况可能1-10分钟)
弹出后便是创建完成
3.Vue2脚手架各文件介绍
(1).用ide打开刚刚创建的项目名称文件夹(记住是项目名称文件夹)
我这里使用的是Vscode,当然也可以用webstorm等其他工具。
(2).各文件讲解
node_modules:放着用npm装的各个依赖(例:ElementUI,VueRouter,VueX,Axios等)
public:
favicon:网页图标
index.html:Vue加载的核心文件,后面的网页文件会自动装载进id为app的div下
src:
assets:静态的图片等存放地方
components:存放Vue组件的地方(一般存放复用组件)
App.vue:被处理后装载进index.html的根Vue文件
main.js:Vue的核心js文件,里面会进行Vue全局组件使用等
babel.config.js:Babel配置
jsconfig.json:js编译设置
package-lock.json:锁定npm安装的版本号,一般不用管
package.json:npm安装的依赖以及脚本设置
vue.config.js:vue的设置
当然,目前的文件只是vue的基本文件结构,后续会补充VueX和Vuerouter后的完整结构。
4.单Vue文件讲解
以实例自带的App.vue为例:
<template></template>标签:里面存放html代码,每一个单独的vue文件的template里面所有的html标签都需要一个div包裹着,否则会报错。
<script></script>标签:里面存放Javascript代码,里面有一个export default部分需要额外注意。这个部分的代码是负责这个vue文件向vue暴露自己本身的属性,方法,名称等
<template> <div id="app"> </div> </template> <script> export default { name: 'App', data(){ return { } }, methods: { }, computed: { }, watch: { } } </script>
name:这个vue文件的名称
components:这个vue文件引入的其他vue文件名称,只要在这里声明了才能在template里使用,后面进行组件引用时会额外说明
data:存放这个vue文件的数据,后面进行双向数据绑定等时会额外说明
methods:存放函数的地方,后面进行事件绑定时会额外说明
computed:处理vue数据的地方,后面进行计算属性讲解时会额外说明
watch:监听器,后面讲数据监听会额外说明
到此,所有vue脚手架的文件具体作用已经讲解完毕,后面会进行vue2使用的正式讲解。
二.Vue2插值表达式和指令
在开始本章之前,请先将上一章创建的components内的HelloWorld.vue删除,并将App.vue变为此状态。
(一) 插值表达式和data
插值表达式是Vue的核心重点,不仅可以将data的值展示到页面中,也可以写一些简易的javascript表达式进行渲染。
{{ 表达式 }}
使用表达式应注意以下要点:
- 使用的数据需存在于data
- 支持的是表达式,并非语句,例如:if,for等
- 不能在标签属性内使用
在data内声明数据,可以存放基础类型和引用类型(数组,对象),但需要遵循Key:Value的标准:
在data声明好数据后,就可以在template内用插值表达式使用
启动Vue查看效果:
npm run serve
至此插值表达式已经成功渲染到页面中
(二).指令
Vue会根据不同的指令进行不同功能操作的特殊标签属性
一般以v-前缀开头
1.v-html
使用v-html可以将绑定的文本变成html,一般情况下不建议使用,可能会存在风险
2.v-if和v-show
v-if和v-show一般用来控制元素的显示和隐藏,但是它们之间存在着一些差别:
- v-if:条件渲染,不满足条件不会渲染该元素
- v-show:都会渲染该元素,但是根据条件会控制该元素的显示或隐藏
例如,浏览器存在两个元素,分别具有v-if和v-show指令
当它们都是true时,则都会展示出来,但改为false后,差别就出来了
它们虽然都消失了,但是v-show指令的元素只是设置了一个隐藏的样式,v-if则是完全没有渲染。
我们在实际选择中,一般会选择v-if进行更加安全的显示和隐藏,对于一些较为普通的需求时,使用v-show可以降低性能损耗。
3.v-else和v-else-if
这是v-if的一个补充,用一个案例就能说明
4. v-on
v-on指令负责将事件绑定到对应元素上,可以简化事件绑定的流程
v-on:事件名 = "内联javascript表达式"
v-on:事件名 = "methods内的函数名"
(1).内联式
<template>
<div id="app">
<div>score : {{ score }}</div>
<button v-on:click="score++">分数++</button> //内联式将代码写在标签内
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
score: 90
}
},
methods: {
}
}
</script>
(2).method函数
<template>
<div id="app">
<div>score : {{ score }}</div>
<button v-on:click="addScore">分数++</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
score: 90
}
},
methods: {
addScore(){
this.score++
console.log("分数增加了");
}
}
}
</script>
根据代码数量来决定使用内联还是函数
(3).简化写法
@事件名 = "内联javascript表达式"
@事件名 = "methods内的函数名"
例如:
<template>
<div id="app">
<div>score : {{ score }}</div>
<button @click="addScore">分数++</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
score: 90
}
},
methods: {
addScore(){
this.score++
console.log("分数增加了");
}
}
}
</script>
(4).事件绑定函数传参
使用method内函数可以进行参数传递,如:
<template>
<div id="app">
<div>score : {{ score }}</div>
<button @click="addScore(1)">分数++</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
score: 90
}
},
methods: {
addScore(num){
this.score+=num
console.log("分数增加了");
}
}
}
</script>
如果不传递参数,函数内自带一个参数,可以获取本次事件的详情。
<template>
<div id="app">
<div>score : {{ score }}</div>
<button @click="addScore">点我查看事件详情</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
score: 90
}
},
methods: {
addScore(e){
console.log(e);
}
}
}
</script>
当然,也可以传了参数获取本次事件详情,只需要提供两个形参,给后面形参的位置写$event即可。
<template>
<div id="app">
<div>score : {{ score }}</div>
<button @click="addScore(1,$event)">点我查看事件详情</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
score: 90
}
},
methods: {
addScore(num,e){
this.score += num
console.log(e);
}
}
}
</script>
5.v-bind
(1).一般写法
v-bind用来动态绑定html元素的属性值,例如src,href,title等
<template>
<div id="app">
<a v-bind:href="url">点我跳转百度</a>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
url: 'http://www.baidu.com/'
}
},
methods: {
}
}
</script>
(2).简写
v-bind可以简写为以下方式
:属性名 = "值"
例如
<template>
<div id="app">
<a :href="url">点我跳转百度</a>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
url: 'http://www.baidu.com/'
}
},
methods: {
}
}
</script>
(3).注意事项
在使用Vue脚手架时,由于webpack打包编译js后导致正常方式下img标签引入图片不正确,所以不能直接通过输入路径来展示图片。而是要通过:
require('路径)
的方式进行图片的引入,例如:
<template> <div id="app"> <img :src="url"> </div> </template> <script> export default { name: 'App', data(){ return { url: require('@/assets/logo.png') } }, methods: { } } </script>
6.v-for
(1).基础使用
v-for是用来循环遍历数据,并将其一一渲染到页面中。可以循环数组,数字以及对象
v-for = "(item, index) in 数字或数组名"
其中item代表每一项,index代表下标
例如
<template>
<div id="app">
<div>
我的动物朋友们
<ul>
<li v-for="(animal,index) in animals"> {{ animal }}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
animals: ["小猪","小猫","小狗"]
}
}
}
</script>
(2).v-for的key
在使用v-for进行数组渲染时,最好将key属性加入,便于Vue的正确排序复用
key = "唯一标识"
那么这个key能不能是前面提到的index呢?当然可以,只不过最好是唯一标识,例如id等这类唯一的标志,当在没有唯一标识的时候,使用index也是可以的。
<template>
<div id="app">
<div>
我的动物朋友们
<ul>
<li v-for="(animal,index) in animals" :key="animal.id"> {{ animal.name }}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
animals: [
{id: "001", name: "小猪"},
{id: "002", name: "小鸡"},
{id: "003", name: "小马"},
]
}
}
}
</script>
7.v-model
v-model用于给表单元素进行双向数据绑定,用于快速取得数据和设置数据
当数据变化时,自动更新视图
当视图变化时,自动更新数据
<template>
<div id="app">
<input placeholder="输入账号" v-model="username">
<input type="password" placeholder="输入密码" v-model="password">
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
username: '',
password: ''
}
}
}
</script>
8.指令修饰符
一些指令具有指令后缀,通过.追加指令后缀可以实现不同的功能
(1).按键修饰符
@keyup.enter表示键盘监听回车事件
(2).v-model修饰符
v-model.trim表示去除首尾空格
v-model.number表示转数字
(3).事件修饰符
@事件名.stop表示阻止事件冒泡
@事件名.prevent表示阻止默认事件
这个可以自己去试试,这里就不演示了
9.v-bind对于样式的增强控制
v-bind应用于样式上可以进行一系列更强大的设置
:class = "对象/数组"
(1).对象
<div class="box :class="{类名1:布尔值, 类名2:布尔值...}"></div>
这里的类名后面的布尔值为true则该类附加到元素上,若false则不会。
<template>
<div id="app">
<div class="box" :class="{pink: true, large_box: true, red: false}">
我是盒子
</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
boxColor: 'pink'
}
}
}
</script>
<style>
.box {
width: 200px;
height: 200px;
}
.pink {
background-color: pink;
}
.red {
background-color: red;
}
.large_box {
width: 500px;
height: 500px;
}
</style>
(2).数组
<div class="box :class="['类名1', '类名2', '类名3', ....]"></div>
绑定为数组时则就是将数组内的所有类名全部绑定到该元素上
<template>
<div id="app">
<div class="box" :class="['red', 'large_box']">
我是盒子
</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
boxColor: 'pink'
}
}
}
</script>
<style>
.box {
width: 200px;
height: 200px;
}
.pink {
background-color: pink;
}
.red {
background-color: red;
}
.large_box {
width: 500px;
height: 500px;
}
</style>
注意::class绑定对象时,对象里面的类名不需要加' ',但是为数组时,需要加' '
10.v-model绑定其他表单元素
输入框:input:text
文本域:textarea
复选框:input:checkbox
单选框:input:radio
下拉框:select
三.计算属性
(一). 概念以及基本写法
计算属性是在基于已有数据的基础上,对数据进行加工和处理,并且直接使用
使用方法:
computed: {
计算属性名(){
处理过程...
return 要返回的结果
}
}
例如:
<template>
<div id="app">
<div>总数:{{ totalNumber }}</div>
<div>总价值:{{ totalPrice }}</div>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
goods: [
{name: '笔', number: 9, price: 1},
{name: '本子', number: 10, price: 5},
{name: '文具盒', number: 20, price: 9}
]
}
},
computed: {
totalNumber(){
//reduce求和
return this.goods.reduce((pre, cur) => pre + cur.number, 0)
},
//reduce求总价值
totalPrice(){
return this.goods.reduce((pre,cur) => pre + cur.number * cur.price, 0)
}
}
}
</script>
(二).完整写法
计算属性默认的基本写法只能够进行访问读取,并不能进行修改
如果想要进行修改,就需要用完整写法
computed: {
计算属性名(){
get(){
return 返回结果
},
set(要修改的值){
修改的处理逻辑
}
}
}
这个一般用的很少,基本写法也够用了
四.watch监听器
(一).概念以及基本用法
监听器顾名思义,就是监听数据的变化,然后做出一些逻辑操作
用法
watch: {
//数据为简单类型
监听数据名(newValue, oldValue){
一些逻辑操作
},
//数据为引用类型
'对象.监听数据名'(newValue, oldValue){
一些逻辑操作
}
}
例如
<template>
<div id="app">
<div>名字:{{ name }}</div>
<div>拥有:{{ food.num }} 个 {{ food.name }}</div>
<button @click="changeName">改名</button>
<button @click="eatApple">吃掉一个苹果</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
name: 'zs',
food: {
name: '苹果', num: 2
}
}
},
methods: {
changeName(){
this.name = 'ls'
},
eatApple(){
this.food.num --
}
},
watch: {
//监听简单属性值
name(newValue, oldValue){
console.log(`我之前叫${oldValue},现在叫${newValue}`);
},
//监听对象内的属性值
'food.num'(newValue, oldValue){
console.log(`之前有${oldValue}个苹果,现在有${newValue}个`);
}
}
}
</script>
(二).完整写法
watch的完整写法就是在原本的基础上加入了一些额外的配置,将函数写法变成对象写法
额外的配置项:
deep: true 对复杂类型进行深度的监视
immediate: true 初始化时立即进行一次监听handler方法
handler () {} 监听处理方法
watch: {
监听属性名: {
deep: true, //深度监听
immediate: true, //立即监听,
handler(){
监听的处理逻辑....
}
}
}
例如
<template>
<div id="app">
<div>名字:{{ name }}</div>
<div>拥有:{{ food.num }} 个 {{ food.name }}</div>
<button @click="changeName">改名</button>
<button @click="eatApple">吃掉一个苹果</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
name: 'zs',
food: {
name: '苹果', num: 2
}
}
},
methods: {
changeName(){
this.name = 'ls'
},
eatApple(){
this.food.num --
}
},
watch: {
food: {
deep: true,
handler(newValue) {
console.log("food对象被修改了");
console.log(`新值:${newValue.num}`);
}
}
}
}
</script>
注意,这里使用完整写法的监听器不能直接通过函数获得旧值,只能获得新值
如果想要在完整写法的监听器中获得旧值,需要用computed属性计算属性缓存
具体深拷贝代码这里不阐述
五.生命周期
(一) Vue的四个生命周期
生命周期贯穿了一个vue组件从创建到销毁的全过程,一共拥有四个阶段
- 创建阶段
- 挂载(渲染)阶段
- 更新阶段
- 销毁阶段
不同的阶段在Vue中负责干的事情
创建阶段:此阶段用于读取响应式数据,也就是data里面的部分
挂载(渲染)阶段:此阶段是渲染template模板内部的阶段
更新阶段:此阶段用于更新数据,相应视图
销毁阶段:此阶段销毁Vue实例
所以通过这里可以得知,创建阶段和挂载阶段之间,是发送异步请求获取数据的时候
(二).钩子函数
在每个不同的生命周期之间,存在着不同的生命周期函数,这里用一条时间线绘出
可以用控制台感受一下
<template>
<div id="app">
<div>名字:{{ name }}</div>
<div>拥有:{{ food.num }} 个 {{ food.name }}</div>
<button @click="changeName">改名</button>
<button @click="eatApple">吃掉一个苹果</button>
</div>
</template>
<script>
export default {
name: 'App',
data(){
return {
name: 'zs',
food: {
name: '苹果', num: 2
}
}
},
methods: {
changeName(){
this.name = 'ls'
},
eatApple(){
this.food.num --
}
},
beforeCreate(){
console.log("我是beforeCreate");
},
created(){
console.log("我是Create");
},
beforeMount(){
console.log("我是beforeMount");
},
mounted(){
console.log("我是mounted");
},
beforeUpdate(){
console.log("我是beforeUpdate");
},
updated(){
console.log("我是updated");
},
beforeDestroy(){
console.log("我是beforeDestroy");
},
destroyed(){
console.log("我是destroyed");
}
}
</script>
一般刚进入页面的时候就会蹦出这四个
当修改数据的时候
六.组件化开发
(一).组件化和根组件
将访问页面拆解成一个个的vue组件,每一个组件具有独立的结构,样式和行为
好处
便于维护和复用,提高开发效率,进行组件分类
根组件指的是App.vue组件,所有的组件都应该放在根组件上
(二).局部注册组件
组件注册可以让一个组件在另一个组件里面展示和使用
局部注册的组件只能在当前的组件使用
//引入组件对象
import 组件对象 from '组件路径';
//使用
components: {
组件对象
}
这里拿vue-cli的例子引入:
<template>
<div id="app">
<div>我是根组件!</div>
<HelloWorld></HelloWorld>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
(三).全局组件注册
局部组件只能在注册了组件的内部使用,全局组件则可以不用在组件内部注册
只需要在main.js里面声明即可
在main.js里面注册全局组件
//引入组件
import 组件对象 from '路径'
//注册全局组件
Vue.component("组件名", 组件对象)
例如
main.js
import Vue from 'vue'
import App from './App.vue'
import HelloWorldVue from './components/HelloWorld.vue'
Vue.config.productionTip = false
Vue.component("HelloWorld", HelloWorldVue)
new Vue({
render: h => h(App),
}).$mount('#app')
在app.vue里面使用
<template>
<div id="app">
<div>我是根组件!</div>
<HelloWorld></HelloWorld>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
效果一致
技巧:一般使用局部注册,全局注册很少用,除非真的是用的很多的组件
七.组件通信
(一).scoped样式冲突
在正式讲解组件通信之前,要特别声明一个地方
在之前的学习中,演示的组件只有一个,后面进行组件通信之后会进行多组件的互相调用
所以,在默认情况下,写在style下的样式会全局生效,因此会导致组件的样式混乱
可以在style标签上加入scoped属性,这样可以进行样式隔离,不会样式冲突
scoped原理:
会在当页所有标签添加data-v-hash值
同时给css选择器同步加上data-v-hash值
这样就可以确保组件内的样式具有独立性
(二). props
props是vue组件内的一个属性,是组件上注册的一些自定义属性,可以进行组件传递参数用
1.简单写法
props: ['参数1', '参数2', '参数3', ...]
<template>
<div id="main">
<div class="child">我是子组件,我叫 {{ name }} , 今年 {{ age }}岁</div>
</div>
</template>
<script>
export default {
name: "Child",
//定义自定义属性
props: ['name', 'age']
}
</script>
<style scoped>
.child {
width: 100px;
height: 100px;
background-color: yellow;
}
</style>
2.完整写法
完整的props写法可以规定传入props参数的类型,是否必须传入等
props: { 检验的属性名: 类型. }
props: { 参数名: { type: 传入的类型, required: 是否必须传入, default: 默认值, validator(value){ return 是否通过验证; } } }
<template>
<div id="main">
<div class="child">当前值为{{ value }},小于100的会报错哦</div>
</div>
</template>
<script>
export default {
name: "Child",
props: {
value: {
type: Number,
required: true,
default: 0,
validator(value){
return value >= 100?true:false
}
}
}
}
</script>
<style scoped>
.child {
width: 100px;
height: 100px;
background-color: yellow;
}
</style>
注意:props的属性都是只可读不可写,这很重要,不可以对props进行写入操作
(三).常见的组件通信
组件之间是相互独立和隔离的,但是有时候需要组件之间互相传递数据
这个时候就需要组件通信
组件通信一般分为两种情况
- 父子关系:使用props和$emit
- 非父子关系:provide和inject以及全局事件总线和Vuex
1.父子组件通信
(1).父传子
子组件声明props属性
父组件传数据给子组件需要通过props传递,父组件通过给子组件props属性传递值
子组件
<template>
<div id="main">
<div class="child">当前值为{{ value }},小于100的会报错哦</div>
</div>
</template>
<script>
export default {
name: "Child",
//子组件声明props属性
props: {
value: {
type: Number,
required: true,
default: 0,
validator(value){
return value >= 100?true:false
}
}
}
}
</script>
<style scoped>
.child {
width: 100px;
height: 100px;
background-color: yellow;
}
</style>
父组件
<template>
<div id="app">
<div class="father">
我是父组件
//传递值给Child子组件
<Child :value="100"></Child>
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
components: {
Child
}
}
</script>
<style scoped>
.father {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
(2).子传父
子组件传值给父组件需要用$emit通知父组件进行值的修改
1. 在子组件用事件触发$emit,给父组件发通知
methods: { 函数名(){ this.$emit('通知标题', "消息内容") } }
2. 在父组件接受发送的标题,并且用函数接受进行逻辑处理
<Child @通知标题="处理事件函数名"></Child> ... methods: { 处理事件函数名(val){ 逻辑操作 } }
例如:
子组件
<template>
<div id="main">
<div>
<button @click="sendMessage">点我给父组件发消息</button>
</div>
</div>
</template>
<script>
export default {
name: "Child",
methods: {
sendMessage(){
this.$emit('acceptMessage', "这是一条由子组件发出的消息")
}
}
}
</script>
<style scoped>
.child {
width: 100px;
height: 100px;
background-color: yellow;
}
</style>
主组件
<template>
<div id="app">
<div class="father">
我是父组件,接收到子组件消息为{{ message }}
<Child @acceptMessage="getMessage"></Child>
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
data(){
return {
message: ''
}
},
components: {
Child
},
methods: {
getMessage(val){
this.message = val
}
}
}
</script>
<style scoped>
.father {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
(3).特殊父子通信.sync修饰符
这个修饰符可以实现父子组件的数据双向绑定,而不是单向数据流
通过对子组件自定义的prop数据名加.sync修饰符实现
在父组件
<Child :属性名.sync="双向的绑定的值"></Child>
在子组件
<button @click="$emit('update:prop属性名', 值)"></button>
一般用于封装弹框之类的
例如:
父组件
<template>
<div id="app">
<div class="father">
<button @click="isShowChild = true">点我修改子组件的显示隐藏</button>
<Child :isShow.sync="isShowChild"></Child>
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
data(){
return {
isShowChild: true
}
},
components: {
Child
}
}
</script>
<style scoped>
.father {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
子组件:
<template>
<div id="main">
<div class="child" :class="{'is-show': !isShow}">
我是子组件
<button @click="$emit('update:isShow', false)">点我关闭子组件</button>
</div>
</div>
</template>
<script>
import Grandson from './Grandson';
export default {
name: "Child",
props: ['isShow']
}
</script>
<style scoped>
.child {
width: 100px;
height: 100px;
background-color: yellow;
}
.is-show {
display: none;
}
</style>
2.非父子组件通信
非父子组件指兄弟组件这种非一层包含关系的组件,比如:
(1). EventBus(全局事件总线)
1. 创建一个可以被访问到的事件总线(空的Vue实例)
import Vue from 'vue'; const Bus = new Vue(); export default Bus;
2. 在接受方调用Bus.$on(' 事件标题 ’, (val) => {接收到数据的处理逻辑})
created(){ Bus.$on('事件名',(val) => { 处理逻辑 }) }
3. 在发送方调用Bus.$emit('事件名', '发送的数据')
methods: { sendMessage(){ Bus.$emit('事件名', '这是发送的消息') } }
例如:
接收方
<template >
<div id="main">
我是兄弟组件1,接收消息{{ msg }}
</div>
</template>
<script>
import Bus from '@/utils/bus'
export default {
data(){
return {
msg: ''
}
},
created(){
Bus.$on('acceptMessage',(val) => {
this.msg = val
})
}
}
</script>
发送方
<template >
<div id="main">
我是兄弟组件2
<button @click="sendMessage">发送消息</button>
</div>
</template>
<script>
import Bus from '@/utils/bus'
export default {
methods: {
sendMessage(){
Bus.$emit('acceptMessage', '这是发送的消息')
}
}
}
</script>
(2). provide&inject
provide&inject是跨层级的一种组件通信方式,简单来说就是爷孙之间通信,跨过了子这个层级
1. 父组件provide提供数据
provide(){ return { 发送的消息名字: 该消息的值 } }
2. 孙组件inject接收数据
inject: ['接受的数据名字1', '接受的数据名字2', '接受的数据名字3']
tips:据说子组件可以这样收到值,不过我觉得完全没必要
例如:
父组件:
<template>
<div id="app">
<div class="father">
<Child></Child>
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
data(){
return {
message: ''
}
},
components: {
Child
},
provide(){
return {
msg: '666'
}
}
}
</script>
<style scoped>
.father {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
孙组件:
<template>
<div id="main">
<div class="grandson">
我是孙子组件,接收到值:{{ msg }}
</div>
</div>
</template>
<script>
export default {
inject: ['msg']
}
</script>
<style scoped>
.grandson {
width: 50px;
height: 50px;
background-color: aquamarine;
}
</style>
八.dom获取和异步更新
(一).dom获取
在vue里面,虽然不是很提倡通过操作dom实现逻辑操作,但是部分需求需要通过获取dom才能实现(表单验证和echarts)
通过ref和$refs的组合可以获取dom元素或组件实例
<div class="father" ref="dom取名">
这是一个元素
</div>
mounted(){
console.log(this.$refs.dom取的名);
}
例如
<template>
<div id="app">
<div class="father" ref="testRef">
这是一个元素
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
data(){
return {
isShowChild: true
}
},
components: {
Child
},
mounted(){
console.log(this.$refs.testRef);
}
}
</script>
<style scoped>
.father {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
上面是获取dom元素的实例,也可以获取vue组件实例,调用里面的实例,可以自己试试看
(二).异步更新和$nextTick()
如果我想实现一个需求,点击编辑按钮后显示编辑框并且自动聚焦到编辑框上
这个时候用如下代码:
<template>
<div id="app">
<div class="father">
这是标题
<button @click="editTitle">点击编辑标题</button>
<input v-show="showEdit" type="text" ref="edit">
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
data(){
return {
showEdit: false
}
},
methods: {
editTitle(){
this.showEdit = true
this.$refs.edit.focus()
}
}
}
</script>
但是这样是无法完成这个需求的,因为Vue的dom更新是异步实现的
要想实现这个功能,需要等待编辑框的dom加载完毕,这个时候可以使用$nextTick等待dom加载完毕后进行下一步操作
this.$nextTick(() => {
等待dom加载完毕后里面进行的操作
})
editTitle() {
this.showEdit = true
this.$nextTick(() => {
this.$refs.edit.focus()
})
}
对上面的代码稍加修改,就可以实现了
九.自定义指令
可以自己定义指令,进行一些dom操作
全局注册
Vue.directive('指令名', {
"inserted"(el){
可以对el标签进行拓展额外功能
}
})
局部注册
directives: {
"指令名": {
inserted(el){
对元素进行操作逻辑
}
}
}
使用
v-xxx(指令名)
例如,实现一个color指令,传入值时更改不同颜色
<template>
<div id="app">
<div class="father">
<div v-color="'red'">我是</div>
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
data() {
return {
showEdit: false
}
},
methods: {
editTitle() {
this.showEdit = true
this.$nextTick(() => {
this.$refs.edit.focus()
})
}
},
directives: {
color: {
inserted(el,binding){
//binding代表传入指令的值
el.style.color = binding.value
},
update(el,binding){
el.style.color = binding.value
}
}
}
}
</script>
十.插槽
在vue组件中,父组件调用子组件时,想让子组件内的内容可以由父组件决定
插槽可以让子组件内部结构能够被自定义,比如对话框,提示框等
(一).默认插槽
默认插槽的使用很简单,只需要占位符即可
1.在组件内需要被定制的结构部分用<slot></slot>占位
<template> <div id="main"> <div class="child"> <slot></slot> </div> </div> </template>
2.在父组件里使用子组件并在标签内自定义结构
<Child>这是子组件信息</Child>
例:
父组件:
<template>
<div id="app">
<div class="father">
<Child>这是子组件信息</Child>
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
data() {
return {
showEdit: false
}
},
components: {
Child
}
}
</script>
子组件
<template>
<div id="main">
<div class="child">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "Child"
}
</script>
在<slot></slot>内书写的内容可以作为默认结构使用,前提是父组件什么都不传递
<template> <div id="main"> <div class="child"> <slot>我是默认内容</slot> </div> </div> </template> <script> import Grandson from './Grandson'; export default { name: "Child" } </script>
(二).具名插槽
具名插槽的使用可以在子组件具有多个插槽占位符时,进行精准替换
<template>
<div id="main">
<div class="child">
<div class="content-header">
<slot name="header"></slot>
</div>
<div class="content-title">
<slot name="title"></slot>
</div>
<div class="content-text">
<slot name="text"></slot>
</div>
</div>
</div>
</template>
<template>
<div id="app">
<div class="father">
<Child>
<template v-slot:header>
这是头部
</template>
//v-slot可以简化成#
<template #title>
这是标题
</template>
<template #text>
正文
</template>
</Child>
</div>
</div>
</template>
(三).作用域插槽
插槽在自定义结构的同时,可以进行传值,主要用于表格中删除,编辑等功能
1.给slot定义属性值,这个属性值会被传回父组件进行使用
<slot :属性名="值"></slot>
2.在这里添加的所有值都会被默认封装进一个对象内
{属性名: 值, ...}
3.在父组件使用子组件,在template里面用#插槽名 = "自定义对象名",这里的自定义对象名可以自己取名,里面的东西就是上一步封装的所有值
<Child :list="list"> <template #插槽名="自定义对象名"> </template> </Child>
例如:
父组件
<template>
<div id="app">
<div class="father">
<Child :list="list">
<template #opt="col">
<button @click="del(col)">删除</button>
</template>
</Child>
</div>
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
name: 'App',
data() {
return {
list: [
{id: 1, name: 'p1', age: 18},
{id: 2, name: 'p2', age: 19},
{id: 3, name: 'p3', age: 20},
{id: 4, name: 'p4', age: 21},
]
}
},
methods: {
del(val){
console.log(val);
}
},
components: {
Child
}
}
</script>
子组件:
<template>
<div id="main">
<table>
<thead>
<tr>
<td>id</td>
<td>年龄</td>
<td>姓名</td>
<td>操作</td>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>
<slot name="opt" :col="item"></slot>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import Grandson from './Grandson';
export default {
name: "Child",
props: {
list: {
type: Array
}
}
}
</script>
至此,Vue2基础部分完结
更多推荐
所有评论(0)