目录
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、接收数据:
-
第一种方式(只接收):
props:['name']
-
第二种方式(限制类型):
props:{name:String}
-
第三种方式(限制类型、限制必要性、指定默认值):
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.sessionStorage
和 Window.localStorage
属性来实现本地存储机制。
3、相关API:
-
xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
-
xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。
-
xxxxxStorage.removeItem('key');
该方法接受一个键名作为参数,并把该键名从存储中删除。
-
xxxxxStorage.clear()
该方法会清空存储中的所有数据。
4.、备注:
- SessionStorage存储的内容会随着浏览器窗口关闭而消失。
- LocalStorage存储的内容,需要手动清除才会消失。
xxxxxStorage.getItem(xxx)
如果xxx对应的value获取不到,那么getItem的返回值是null。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、绑定自定义事件:
-
第一种方式,在父组件中:
<标签 @customer="方法"/>
-
第二种方式,在父组件中:
<标签 ref="xxx"/> ...... mounted(){ this.$refs.xxx.$on('customer',数据) }
-
若想让自定义事件只能触发一次,可以使用
once
修饰符,或$once
方法。 -
触发自定义事件:
this.$emit('customer',数据)
-
解绑自定义事件
this.$off('customer')
-
组件上也可以绑定原生DOM事件,需要使用
native
修饰符。 -
注意:通过
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、使用步骤:
-
安装pubsub:
npm i pubsub-js
-
引入:
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所指定的回调函数中执行