目录

一. Vue2脚手架的基础搭建

(一) npm的安装

(二) Vue2脚手架基础和使用

 1. 创建代码文件夹

2.创建Vue脚手架模版

(1).输入指令vue create [项目名]

(2).选择创建模板

(3).选择创建参数

3.Vue2脚手架各文件介绍

(1).用ide打开刚刚创建的项目名称文件夹(记住是项目名称文件夹)

(2).各文件讲解

4.单Vue文件讲解

二.Vue2插值表达式和指令

(一) 插值表达式和data

(二).指令 

1.v-html

2.v-if和v-show 

 3.v-else和v-else-if

4. v-on

(1).内联式

(2).method函数 

(3).简化写法

(4).事件绑定函数传参 

5.v-bind 

(1).一般写法

(2).简写

(3).注意事项 

6.v-for

(1).基础使用

(2).v-for的key 

7.v-model

8.指令修饰符 

(1).按键修饰符

(2).v-model修饰符

(3).事件修饰符

9.v-bind对于样式的增强控制 

(1).对象

​编辑(2).数组

10.v-model绑定其他表单元素 

三.计算属性 

(一). 概念以及基本写法

(二).完整写法 

四.watch监听器

 (一).概念以及基本用法

(二).完整写法 

五.生命周期 

(一) Vue的四个生命周期

(二).钩子函数

六.组件化开发

(一).组件化和根组件

(二).局部注册组件

(三).全局组件注册

七.组件通信

(一).scoped样式冲突

(二). props

1.简单写法

2.完整写法 

(三).常见的组件通信 

1.父子组件通信

(1).父传子

(2).子传父

(3).特殊父子通信.sync修饰符 

2.非父子组件通信

(1). EventBus(全局事件总线)

(2). provide&inject

 八.dom获取和异步更新

(一).dom获取

(二).异步更新和$nextTick()

九.自定义指令 

十.插槽 

(一).默认插槽

(二).具名插槽

(三).作用域插槽 


一. Vue2脚手架的基础搭建

(一) npm的安装

        使用Vue2脚手架,需要安装NodeJS(LTS版)使用npm,在官网安装完毕即可 

Node.js              

(二) 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组件从创建到销毁的全过程,一共拥有四个阶段

  1. 创建阶段
  2. 挂载(渲染)阶段
  3. 更新阶段
  4. 销毁阶段

不同的阶段在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参数的类型,是否必须传入等

  1. props: { 检验的属性名: 类型. }

  2.     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进行写入操作

(三).常见的组件通信 

组件之间是相互独立和隔离的,但是有时候需要组件之间互相传递数据

这个时候就需要组件通信

组件通信一般分为两种情况

  1. 父子关系:使用props和$emit
  2. 非父子关系: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基础部分完结

 

Logo

前往低代码交流专区

更多推荐