学习 Vue-cli 这一篇就够了



01_初始化脚手架

官方文档

1、npm修改淘宝镜像

npm config set registry https://registry.npm.taobao.org

2、全局安装 @vue/cli

npm install -g @vue/cli

3、切换到你要创建项目的目录,然后使用命令创建项目

vue create xxx

4、进入该项目xxx目录并启动项目

npm run serve

5、访问运行成功的端口号
在这里插入图片描述

在这里插入图片描述


02_分析脚手架结构

2.1、文件结构

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

1、public/index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 开启移动端的理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- 引入页签图标 --> 
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 配置网页标题 -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- 当浏览器不支持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、src/main.js - 入口文件 - 引入App.vue

/**
 * 整个项目的入口文件
 */

// 导入Vue
import Vue from 'vue'
// 导入App组件
import App from './App.vue'

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

// 创建Vue实例对象----vm
new Vue({
  // 将App组件放入容器
  render: h => h(App),
  // 挂载容器
}).$mount('#app')

3、src/App.vue - 引入HelloWorld.vue

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App" />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

4、src/components/HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

2.2、render函数

关于不同版本的Vue:

1、vue.js与vue.runtime.xxx.js的区别:

  • vue.js是完整版的Vue,包含:核心功能+模板解析器。
  • vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。

2、因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

// 创建Vue实例对象----vm
new Vue({
  // 简写形式
  render: h => h(App),
  /**
   * 完整形式
   * render(creatElement){
   *    return creatElement(App) 
   * }
   */
}).$mount('#app')

3、其余的 .vue组件有着独特的<template></template>,如 App.vue,整个项目可以直接使用 vue.runtime.xxx.js

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App" />
  </div>
</template>

2.3、修改默认配置

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置
请执行:vue inspect > output.js(需要管理员身份运行)得到 output.js

1、默认配置的入口文件名称(仅仅只是查看)

entry: {
  app: [
    './src/main.js'
  ]
}

2、修改默认配置的入口文件名称 - 根目录的 vue.config.js 添加配置

module.exports = {
  pages: {
    index: {
      entry: 'src/laptoy.js'
    }
  }
}

3、关闭语法检查

module.exports = {
  lintOnSave:false
}

03_ref属性

1、用来给元素或子组件注册引用信息(id的替代者)

2、应用在 html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(VueComponent)

3、使用方式

  • 标识:<h1 ref="xxx"></h1><Author ref="xxx"/>
  • 获取:this.$refs.xxx

4、代码展示

App.vue

<template>
  <div>
    <Author ref="aut"/>
    <h2 ref="mytitle">欢迎学习Vue</h2>
    <button ref="btn" @click="showDOM">点我输出DOM元素</button>
  </div>
</template>

<script>
import Author from './components/Author.vue'

export default {
  name:'App',
  components:{Author},
  methods:{
    showDOM(){
        console.log(this.$refs.aut)
        console.log(this.$refs.mytitle)
        console.log(this.$refs.btn)
    }
  }
}
</script>

Author.vue

<template>
  <div>
    <h2>作者:Laptoy</h2>
  </div>
</template>

<script>
export default {
  name: 'Author',
}
</script>

在这里插入图片描述


项目实战:

1、点击按钮触发修改方法

<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.attrGroupId)">修改</el-button>

2、修改方法绑定了 ref="addOrUpdate" 的子组件并调用该组件的 init 方法

// 新增 / 修改
addOrUpdateHandle(id) {
  this.addOrUpdateVisible = true
  this.$nextTick(() => {
    this.$refs.addOrUpdate.init(id)
  })
},

3、ref 所在标签

<!-- 弹窗, 新增 / 修改 子组件 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>

4、add-or-update 组件内的方法

init(id) {
  ...
},

04_props配置

1、功能:让组件接收外部传过来的数据

2、传递数据:<Demo name="xxx"/>

3、接收数据:

  1. 第一种方式(只接收):props:['name']

  2. 第二种方式(限制类型):props:{name:String}

  3. 第三种方式(限制类型、限制必要性、指定默认值):

props:{
	name:{
	type:String, 	 //类型
	required:true,   //必要性
	default:'Laptoy' //默认值
	}
}

备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

代码演示:

Author.vue

<template>
  <div>
    <h2>作者:{{name}}-年龄:{{age}}</h2>
  </div>
</template>

<script>
export default {
  name: 'Author',

  // 1、简单声明接收
  // props:['name','age']

  // 2、类型限制接收
  // props:{
  //   name:String,
  //   age:Number
  // }

  // 3、完整接收
  props: {
    name: {
      type: String,
      required: true // 必须传递
    },
    age: {
      type: Number,
      default: 99    // 默认值
    }
  }

}
</script>

App.vue - 加上v-bind表示传递的是对象(如21是number属性),不加表示仅仅传递字符串

<template>
  <div>
    <Author name="Laptoy" :age="21"/>
  </div>
</template>

<script>
import Author from './components/Author.vue'

export default {
  name:'App',
  components:{Author},
}
</script>

05_mixin

1、功能:可以把多个组件共用的配置提取成一个混入对象

2、使用方式:

第一步定义混入:

{
    data(){....},
    methods:{....},
    ....
}

第二步使用混入:

  • 全局混入:Vue.mixin(xxx) (不推荐使用)
  • 局部混入:mixins:['xxx']

局部混入代码演示

1、创建 mixin.js

export default{
    data(){
        return{
            name:"Laptoy",
            age:21
        }
    },
}

2、Author.vue 引入 mixin.js

<template>
  <div>
    <h2>作者:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
  </div>
</template>

<script>
import mixin from '../mixin.js'

export default {
  name: 'Author',
  mixins:[mixin]
}
</script>

3、App.vue

<template>
  <div>
    <Author/>
  </div>
</template>

<script>
import Author from './components/Author.vue'

export default {
  name:'App',
  components:{Author},
}
</script>

4、展示
在这里插入图片描述


全局混入代码演示

main.js

import Vue from 'vue'
import App from './App.vue'
import mixin from './mixin.js'

Vue.config.productionTip = false
Vue.mixin(mixin)

new Vue({
  render: h => h(App),
}).$mount('#app')

Author.vue - 无需局部引入

<template>
  <div>
    <h2>作者:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
  </div>
</template>

<script>

export default {
  name: 'Author',
}
</script>

06_插件

1、功能:用于增强Vue

2、本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

3、定义插件:

export default {
	install (Vue, options) {
	    // 1. 添加全局过滤器
	    Vue.filter(....)
	
	    // 2. 添加全局指令
	    Vue.directive(....)
	
	    // 3. 配置全局混入(合)
	    Vue.mixin(....)
	
	    // 4. 添加实例方法
	    Vue.prototype.$myMethod = function () {...}
	    Vue.prototype.$myProperty = xxxx
	}
}

4、使用插件:Vue.use() - main.js


07_scoped样式

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


08_Todo-list 案例

在这里插入图片描述
在这里插入图片描述

App.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader :addTodo="addTodo" />
        <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo" />
        <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
      </div>
    </div>
  </div>
</template>

<script>
import MyFooter from './components/MyFooter.vue';
import MyHeader from './components/MyHeader.vue';
import MyList from './components/MyList.vue';

export default {
  name: 'App',
  // eslint-disable-next-line vue/no-unused-components
  components: { MyFooter, MyHeader, MyList },
  data() {
    return {
      todos: [
        { id: '001', title: '打代码', done: true },
        { id: '002', title: '打篮球', done: false },
        { id: '003', title: '买Macbook', done: true },
      ]
    }
  },
  methods: {
    // 添加一个todo
    addTodo(x) {
      this.todos.unshift(x)
    },

    // 勾选或取消勾选todo
    checkTodo(id) {
      this.todos.forEach(todo => {
        if (todo.id === id) todo.done = !todo.done
      })
    },

    // 删除todo
    deleteTodo(id) {
      this.todos = this.todos.filter(todo => {
        return todo.id !== id
      })
    },

    // 全选或全不选
    checkAllTodo(done) {
      this.todos.forEach(todo => {
        todo.done = done
      })
    },

    // 清除所有已完成的todo
    clearAllTodo() {
      this.todos = this.todos.filter(todo => {
        return !todo.done
      })
    }
  }
}
</script>

<style>
/*base*/
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
    0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

MyHeader.vue

<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入待办事项" v-model="title" @keyup.enter="add" />
  </div>
</template>

<script>
import {nanoid} from 'nanoid';

export default {
  name: "MyHeader",
  data(){
    return{
      title:''
    }
  },
  props:['addTodo'],
  methods: {
    add() {
        if(this.title.trim() == '')return alert('请输入待办事项') 
        // 将用户的数据包装成todo对象
        // eslint-disable-next-line no-unused-vars
        const todo = {id:nanoid(),title:this.title,done:false}

        // 通知App组件添加todo对象
        this.addTodo(todo)
        this.title = ''
    }
  }
}
</script>

<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}

.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
    0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

MyList.vue

<template>
  <div>
    <ul class="todo-main">
      <MyItem v-for="todo in todos" :key="todo.id" :todo="todo" :checkTodo="checkTodo" :deleteTodo="deleteTodo" />
    </ul>
  </div>
</template>

<script>
import MyItem from './MyItem.vue'

export default {
  name: "MyList",
  // eslint-disable-next-line vue/no-unused-components
  components: { MyItem },
  props: ['todos', 'checkTodo','deleteTodo']
}
</script>

<style scoped>
/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>

MyItem.vue

<template>
  <div>
    <li>
      <label>
        <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)" />
        <span>{{todo.title}}</span>
      </label>
      <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
    </li>
  </div>
</template>

<script>
export default {
  name: "MyItem",
  // 接收todo对象
  props: ['todo','checkTodo','deleteTodo'],
  methods: {
    handleCheck(id) {
      // 通知App组件将对应的todo对象的done取反
      this.checkTodo(id)
    },
    handleDelete(id){
      if(confirm('确定删除吗')) this.deleteTodo(id)
    }
  }
}
</script>

<style scoped>
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}

li label {
  float: left;
  cursor: pointer;
}

li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button {
  float: right;
  display: none;
  margin-top: 3px;
}

li:before {
  content: initial;
}

li:last-child {
  border-bottom: none;
}

li:hover{
  background-color: #ddd;
}

li:hover button{
  display: block;
}
</style>

MyFooter.vue

<template>
  <div class="todo-footer" v-show="total">
    <label>
      <input type="checkbox" :checked="isAll" @change="checkAll"/>
    </label>
    <span>
      <span>已完成{{doneTotal}}</span> / 全部{{total}}
    </span>
    <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
  </div>
</template>

<script>
export default {
  name: "MyFooter",
  props: ['todos','checkAllTodo','clearAllTodo'],
  computed: {
    total() {
      return this.todos.length
    },
    // eslint-disable-next-line vue/return-in-computed-property
    doneTotal() {
      let i = 0;
      this.todos.forEach(todo => {
        if (todo.done) i++
      });
      return i
    },
    isAll() {
      return this.doneTotal === this.total && this.total > 0
    }

  },
  methods:{
    checkAll(e){
      this.checkAllTodo(e.target.checked)
    },
    clearAll(){
      this.clearAllTodo()
    }

  }

}
</script>

<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}

.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}

.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}

.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>

总结TodoList案例

1、组件化编码流程:

  • 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
  • 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
    • 一个组件在用:放在组件自身即可。
    • 一些组件在用:放在他们共同的父组件上(状态提升)。
    • 实现交互:从绑定事件开始。

2、props适用于:

  • 父组件 ==> 子组件 通信
  • 子组件 ==> 父组件 通信(要求父先给子一个函数)

3、使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

4、props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。


09_ webStorage本地存储

1、存储内容大小一般支持5MB左右(不同浏览器可能还不一样)

2、浏览器端通过 Window.sessionStorageWindow.localStorage 属性来实现本地存储机制。

3、相关API:

  1. xxxxxStorage.setItem('key', 'value');

    该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

  2. xxxxxStorage.getItem('person');

    ​ 该方法接受一个键名作为参数,返回键名对应的值。

  3. xxxxxStorage.removeItem('key');

    ​ 该方法接受一个键名作为参数,并把该键名从存储中删除。

  4. xxxxxStorage.clear()

    ​ 该方法会清空存储中的所有数据。

4.、备注:

  1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
  2. LocalStorage存储的内容,需要手动清除才会消失。
  3. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
  4. JSON.parse(null)的结果依然是null。

Todo-list案例添加本地存储

<template>
  ...
</template>

<script>
import MyFooter from './components/MyFooter.vue';
import MyHeader from './components/MyHeader.vue';
import MyList from './components/MyList.vue';

export default {
  name: 'App',
  components: { MyFooter, MyHeader, MyList },
  data() {
    return {
      todos: JSON.parse(localStorage.getItem('todos')) || []
    }
  },
  method:{ ... }

  watch: {
    // 深度监视todos
    todos: {
      deep: true,
      handler(value) {
        localStorage.setItem('todos', JSON.stringify(value))
      }
    }
  }

}
</script>

10_组件的自定义事件

10.1、介绍

1、一种组件间通信的方式,适用于:子组件 ===> 父组件

2、使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

3、绑定自定义事件:

  1. 第一种方式,在父组件中:<标签 @customer="方法"/>

  2. 第二种方式,在父组件中:

    <标签 ref="xxx"/>
    ......
    mounted(){
       this.$refs.xxx.$on('customer',数据)
    }
    
  3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

  4. 触发自定义事件:this.$emit('customer',数据)

  5. 解绑自定义事件this.$off('customer')

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

  7. 注意:通过this.$refs.xxx.$on('customer',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!


10.2、绑定自定义事件

App.vue

<template>
  <div>
    <!-- 父组件给子组件绑定自定义事件send -->
    <Student @send="getStudentName"/>

    <!-- 获取vc对象给组件绑定自定义事件 -->
    <!-- <Student ref="student"/> -->
  </div>
</template>

<script>
import Student from './components/Student.vue';

export default {
  name: 'App',
  components: { Student },
  methods:{
    getStudentName(name,...params){
      console.log("App收到了学生名:",name,params)
    }
  },
  
  mounted(){
    // 通过vc对象绑定自定义事件
    //this.$refs.student.$on('send',this.getStudentName)
  }

}
</script>

Student.vue

<template>
  <div>
    <h2>{{name}}</h2>
    <button @click="sendStudentName">把学生名给App组件</button>
  </div>
</template>

<script>
export default {
  name: 'Student',
  data() {
    return {
      name: "Laptoy"
    }
  },
  methods:{
      sendStudentName(){
          // 触发自定义事件send
          this.$emit('send',this.name,1,2,3)
      }
  }

}
</script>

在这里插入图片描述


10.3、解绑自定义事件

<template>
  <div>
    <h2>{{name}}</h2>
    <button @click="sendStudentName">把学生名给App组件</button>
    <button @click="unbind">解绑send事件</button>
    <button @click="death">销毁vc</button>
  </div>
</template>

<script>

export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Student',
  data() {
    return {
      name: "Laptoy"
    }
  },
  methods: {
    sendStudentName() {
      // 触发自定义事件send
      this.$emit('send', this.name, 1, 2, 3)
    },
    unbind() {
      // 解绑自定义事件send
      this.$off('send')
      // 解绑多个
      //this.$off(['send','xxx'])
      // 解绑所有
      //this.$off()
    },
    death(){
        this.$destroy() // 销毁vc后所有自定义事件失效
    }
  }

}
</script>

11_全局事件总线

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

2、安装全局事件总线:

new Vue({
	......
	beforeCreate() {
		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
	},
    ......
}) 

3、使用事件总线:

  • 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){
  demo(data){......}
}
......
mounted() {
  this.$bus.$on('xxxx',this.demo)
}
  • 提供数据:this.$bus.$emit('xxxx',数据)

4、最好在beforeDestroy钩子中,用 $off 去解绑当前组件所用到的事件。


代码展示

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  beforeCreate(){
    Vue.prototype.$bus = this // 安装全局事件总线
  }
}).$mount('#app')

App.vue

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

<script>
import Student from './components/Student.vue';

export default {
  name: 'App',
  // eslint-disable-next-line vue/no-unused-components
  components: { Student },
  methods: {
    getStudentName(name, ...params) {
      console.log("App收到了学生名:", name, params)
    }
  },

  mounted() {
    // 通过vc对象绑定自定义事件
    this.$bus.$on('send', this.getStudentName)
  },
  beforeDestroy(){
    this.$bus.$off('send')
  }

}
</script>

Student.vue

<template>
  <div>
    <button @click="sendStudentName">把学生名给App组件</button>
  </div>
</template>

<script>
export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Student',
  methods: {
    sendStudentName() {
      // 触发自定义事件send
      this.$bus.$emit('send', this.name, 1, 2, 3)
    }
  }

}
</script>

12_消息订阅与发布

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

2、使用步骤:

  1. 安装pubsub:npm i pubsub-js

  2. 引入: import pubsub from 'pubsub-js'

3、接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身

mounted() {
  this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
}

4、提供数据:pubsub.publish('xxx',数据)

5、最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅


<template>
  <div class="school">
    <h2>学校名称:{{name}}</h2>
    <h2>学校地址:{{address}}</h2>
  </div>
</template>

<script>
import pubsub from 'pubsub-js'
export default {
  name: 'School',
  data() {
    return {
      name: '广州大学',
      address: '广州',
    }
  },
  mounted() {
  	// 订阅 hello 消息,方法的第二个参数才是数据
    this.pubId = pubsub.subscribe('hello', (msgName, data) => {
      // 这里的this是pubsub
      // console.log(this)
      console.log('有人发布了hello消息,hello消息的回调执行了', msgName, data)
    })
  },
  beforeDestroy() {
    pubsub.unsubscribe(this.pubId)
  },
}
</script>
<template>
  <div class="student">
    <h2>学生姓名:{{name}}</h2>
    <h2>学生性别:{{sex}}</h2>
    <button @click="sendStudentName">把学生名给School组件</button>
  </div>
</template>

<script>
import pubsub from 'pubsub-js'
export default {
  name: 'Student',
  data() {
    return {
      name: '张三',
      sex: '男',
    }
  },
  methods: {
    sendStudentName() {
	  // 发布消息
      pubsub.publish('hello', 666)
    }
  },
}
</script>

13_$nextTick

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

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laptoy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值