第2章:Vue组件化开发
1、局部组件和全局组件的创建和使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
// App组件 html+css+js
// 创建全局组件 第一个是组件名 第二个是配置
// 只要创建全局组件 可以在任意地方使用 (template)
// Vue.component是创建全局组件。
Vue.component('Vheader', {
template: `
<div>
我是导航组件
</div>
`
})
Vue.component('Vaside', {
template: `
<div>
我是侧边栏
</div>
`
})
//const Vbtn是创建局部组件。
const Vbtn = {
template:`
<button>按钮</button>
`
}
const Vcontent = {
data() {
return {
}
},
template: `
<div>
我是内容组件
<Vbtn></Vbtn>
<Vbtn></Vbtn>
<Vbtn></Vbtn>
<Vbtn></Vbtn>
</div>
`,
//这里记得用div标签闭合,否则将会报错。
components:{
Vbtn
}
}
// 使用局部组件的打油诗: 建子 挂子 用子
// 1.创建组件
// 注意:在组件中这个data必须是一个函数,返回一个对象
const App = {
data() {
return {
msg: '我是App组件'
}
},
components: {
Vcontent
},
template: `
<div>
<Vheader></Vheader>
<div>
<Vaside />
<Vcontent />
</div>
</div>
`,
methods: {
handleClick() {
this.msg = '学习局部组件';
}
},
computed: {
}
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
</body>
</html>

2、组件通信-父传子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
// 全局组件
// 父传子:通过prop来进行通信
// 1.在子组件中声明props接收在父组件挂载的属性
// 2.可以在子组件的template中任意使用
// 3.在父组件绑定自定义的属性
Vue.component('Child',{
template:`
<div>
<h3>我是一个子组件</h3>
<h4>{{childData}}</h4>
</div>
`,
props:['childData']
})
const App = {
data() {
return {
msg: '我是父组件传进来的值'
}
},
template: `
<div>
<Child :childData = 'msg'></Child>
</div>
`,
computed: {
}
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
</body>
</html>
3、组件通信-子传父
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
// 全局组件
// 子往父传值
// 在父组件中 子组件上绑定自定义事件
// 在子组件中 触发原生的事件 在事件函数通过this.$emit触发自定义的事件
Vue.component('Child', {
template: `
<div>
<h3>我是一个子组件</h3>
<h4>{{childData}}</h4>
<input type="text" @input = 'handleInput'/>
</div>
`,
props: ['childData'],
methods:{
handleInput(e){
const val = e.target.value;
this.$emit('inputHandler',val);
}
},
})
const App = {
data() {
return {
msg: '我是父组件传进来的值',
newVal:''
}
},
methods:{
input(newVal){
// console.log(newVal);
this.newVal = newVal;
}
},
template: `
<div>
<div class='father'>
数据:{{newVal}}
</div>
<Child :childData = 'msg' @inputHandler = 'input'></Child>
</div>
`,
computed: {
}
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
</body>
</html>
4、平行组件通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
const bus = new Vue();
// 中央事件总线 bus
Vue.component('B', {
data() {
return {
count: 0
}
},
template: `
<div>{{count}}</div>
`,
created(){
// $on 绑定事件
bus.$on('add',(n)=>{
this.count+=n;
})
}
})
Vue.component('A', {
data() {
return {
}
},
template: `
<div>
<button @click='handleClick'>加入购物车</button>
</div>
`,
methods:{
handleClick(){
// 触发绑定的函数 // $emit 触发事件
bus.$emit('add',1);
}
}
})
const App = {
data() {
return {
}
},
template: `
<div>
<A></A>
<B></B>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
</body>
</html>

5、其它组件通信方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
// 插槽 留坑
// props 验证规则
// 组件的生命周期
// UI库 element-ui
// **** 如何设计组件? *****
// provide
// inject
// 父组件 provide来提供变量,然后再子组件中通过inject来注入变量.无论组件嵌套多深
// 中央事件总线 bus
Vue.component('B', {
data() {
return {
count: 0
}
},
inject:['msg'],
created(){
console.log(this.msg);
},
template: `
<div>
{{msg}}
</div>
`,
})
Vue.component('A', {
data() {
return {
}
},
created(){
// console.log(this.$parent.$parent);
// console.log(this.$children);
console.log(this);
},
template: `
<div>
<B></B>
</div>
`
})
const App = {
data() {
return {
title:"老爹"
}
},
provide(){
return {
msg:"老爹的数据"
}
},
template: `
<div>
<A></A>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
</body>
</html>
6、匿名插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
Vue.component('MBtn',{
template:`
<button>
<slot></slot>
</button>
`
})
const App = {
data() {
return {
title: "老爹"
}
},
template: `
<div>
<m-btn><a href="#">登录</a></m-btn>
<m-btn>注册</m-btn>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
</body>
</html>
7、具名插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
// 只要匹配到slot标签的name值 template中的内容就会被插入到这个槽中
Vue.component('MBtn', {
template: `
<button>
<slot name='submit'></slot>
<slot name='login'></slot>
<slot name='register'></slot>
</button>
`
})
const App = {
data() {
return {
title: "老爹"
}
},
template: `
<div>
<m-btn>
<template slot='submit'>
提交
</template>
</m-btn>
<m-btn>
<template slot='login'>
<a href="#">登录</a>
</template>
</m-btn>
<m-btn>
<template slot='register'>
注册
</template>
</m-btn>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
// 2.挂载子组件
App
}
})
</script>
</body>
</html>
8、作用域插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
// 已经开发了一个待办事项列表的组件,很多模块都在
// A B
// 1.之前数据格式和引用接口不变,正常显示
// 2.新功能模块增加对勾
const todoList = {
data() {
return {
}
},
props: {
todos: Array,
defaultValue: []
},
template: `
<ul>
<li v-for='item in todos' :key='item.id'>
<slot :itemValue = 'item'>
</slot>
{{item.title}}
</li>
</ul>
`
}
const App = {
data() {
return {
todoList: [{
title: '大哥你好么',
isComplate: true,
id: 1
},
{
title: '小弟我还行',
isComplate: false,
id: 2
},
{
title: '你在干什么',
isComplate: false,
id: 3
},
{
title: '抽烟喝酒烫头',
isComplate: true,
id: 4
}
]
}
},
components: {
todoList
},
template: `
<todoList :todos='todoList'>
<template v-slot='data'>
<input type="checkbox" v-model='data.itemValue.isComplate' />
</template>
</todoList>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
App
}
})
</script>
</body>
</html>
9、生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.active{
color: red;
}
</style>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
/*
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
activated 激活
deactivated 停用
配合keep-alive
beforeDestroy
destroyed
*/
Vue.component('Test', {
data() {
return {
msg: "小马哥",
isRed:false
}
},
methods: {
handlerClick() {
this.msg = 'alex';
this.isRed = true;
}
},
template: `
<div>
<button @click='handlerClick'>改变</button>
<h3 :class='{active:isRed}'>{{msg}}</h3>
</div>
`,
beforeCreate() {
console.log('组件创建之前', this.$data);
},
created() {
// 非常重要的事情,在此时发送ajax 请求后端的数据
console.log('组件创建完成', this.$data);
},
beforeMount() {
// 即将挂载
console.log('DOM挂载之前', document.getElementById('app'));
},
mounted() {
// 发送ajax
console.log('DOM挂载完成', document.getElementById('app'));
},
beforeUpdate() {
// 获取更新之前的DOM
console.log('更新之前的DOM', document.getElementById('app').innerHTML);
},
updated() {
// 获取最新的DOM
console.log('更新之后的DOM', document.getElementById('app').innerHTML);
},
beforeDestroy() {
console.log('销毁之前');
},
destroyed() {
console.log('销毁完成');
},
activated(){
console.log('组件被激活了');
//activated需要结合下面的<keep-alive>标签使用。
},
deactivated(){
console.log('组件被停用了');
}
})
const App = {
data() {
return {
isShow: true
}
},
components: {},
methods: {
clickHandler() {
this.isShow = !this.isShow;
}
},
template: `
<div>
<keep-alive>
<Test v-if='isShow'></Test>
</keep-alive>
<button @click='clickHandler'>改变生死</button>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
App
}
})
</script>
</body>
</html>
10、异步组件加载
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
</style>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script type='module'>
import xxx from './modules.js';
const App = {
data() {
return {
isShow: false
}
},
methods: {
asyncLoad() {
this.isShow = !this.isShow;
}
},
components: {
Test:()=>import('./Test.js')
},
template: `
<div>
<button @click='asyncLoad'>异步加载</button>
<Test v-if='isShow'></Test>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
App
}
})
</script>
</body>
</html>
11、ref的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3.使用子组件 -->
<App></App>
</div>
<script src="./vue.js"></script>
<script>
Vue.component('Test', {
data() {
return {
msg: "小马哥",
}
},
template: `
<div>
<h3>{{msg}}</h3>
</div>
`,
})
const App = {
data() {
return {
}
},
mounted(){
// 1.如果给标签添加ref,获取的就是真实的DOM节点
// 2.如果给子组件添加ref,获取的是当前子组件对象
console.log(this.$refs.btn);
// 加载页面,自动获取焦点
this.$refs.input.focus();
console.log(this.$refs.test);
},
components: {},
template: `
<div>
<Test ref='test'></Test>
<input type="text" ref='input'/>
<button ref='btn'>改变生死</button>
</div>
`,
}
new Vue({
el: '#app',
data: {
},
components: {
App
}
})
</script>
</body>
</html>
12、nextTick的用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h3>{{message}}</h3>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
message:'小马哥'
}
})
vm.message = 'new Message';
// console.log(vm.$el.textContent);
// 为了数据变化之后等待vue完成更新DOM,可以在数据变化之后立即使用Vue.nextTick 在当前的回调函数中能获取最新的DOM
Vue.nextTick(()=>{
console.log(vm.$el.textContent);
})
</script>
</body>
</html>
13、nextTick的应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id='app'>
<App></App>
</div>
<script src="./vue.js"></script>
<script>
/*
需求:
在页面拉取一个接口,这个接口返回一些数据,这些数据是这个页面的一个浮层组件要依赖的,
然后我在接口一返回数据就展示了这个浮层组件,展示的同时,
上报一些数据给后台(这些数据是父组件从接口拿的),
这个时候,神奇的事情发生了,虽然我拿到数据了,但是浮层展现的时候,
这些数据还未更新到组件上去,上报失败
*/
const Pop = {
data() {
return {
isShow: false
}
},
props: {
name: {
type: String,
default: ''
},
},
template: `
<div v-if='isShow'>
{{name}}
</div>
`,
methods: {
show() {
this.isShow = true; //弹窗组件展示
console.log(this.name);
}
},
}
const App = {
data() {
return {
name: ''
}
},
created() {
// 模拟异步请求
setTimeout(() => {
// 数据更新
this.name = '小马哥';
this.$nextTick(()=>{
this.$refs.pop.show();
})
}, 1000);
},
components: {
Pop
},
template: `<pop ref='pop' :name='name'></pop>`
}
const vm = new Vue({
el: '#app',
components: {
App
}
})
</script>
</body>
</html>
14、对象变更检测
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h3>
{{user.name}},{{user.age}},{{user.phone}}
<button @click='handlerAdd'>添加属性</button>
</h3>
</div>
<script src="./vue.js"></script>
<script>
// Vue不能检测对象属性的添加和删除
new Vue({
el:"#app",
data:{
user:{}
},
methods: {
handlerAdd() {
// Vue.$set(object,key,value)添加响应式属性
// this.user.age = 20;
// this.$set(this.user,'age',20);
// 添加多个响应式属性
this.user = Object.assign({},this.user,{
age:20,
phone:18511803134
})
}
},
created(){
setTimeout(() => {
this.user = {
name:"张三"
}
}, 1250);
}
})
</script>
</body>
</html>
15、混入mixin偷懒技术
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id='app'>
{{msg}}
</div>
<script src="./vue.js"></script>
<script>
const myMixin = {
data(){
return {
msg:"123"
}
},
created(){
this.sayHello();
},
methods: {
sayHello() {
console.log('hello mixin');
}
},
}
// mixin来分发Vue组件中的可复用功能
new Vue({
el:"#app",
data:{
msg:"小马哥"
},
created(){
console.log(1111);
},
mixins:[myMixin]
})
</script>
</body>
</html>
16、mixin混入偷懒技术的应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>mixin的应用</title>
</head>
<body>
<div id='app'>
</div>
<script src="./vue.js"></script>
<script>
// 一个是模态框 一个提示框
// 它们看起来不一样,用法不一样,但是逻辑一样(切换boolean)
/*
// 全局的mixin 要格外小心 因为每个组件实例创建是,它都会被调用
Vue.mixin({
})
*/
const toggleShow = {
data() {
return {
isShow: false
}
},//数据
methods: {
toggleShow() {
this.isShow = !this.isShow
}
}//方法
}//两个组件共有的数据,方法在toggleShow对象里。
const Modal = {
template: `
<div v-if='isShow'><h3>模态框组件</h3></div>
`,
// 局部的mixin
mixins: [toggleShow]
}//组件1
const ToolTip = {
template: `
<div v-if='isShow'>
<h2>提示框组件</h2>
</div>
`,
mixins: [toggleShow]
}//组件2
new Vue({
el: "#app",
data: {
},
components: {
Modal,
ToolTip
},//components是挂载Modal,ToolTip。(注入)
template: `
<div>
<button @click='handleModel'>模态框</button>
<button @click='handleToolTip'>提示框</button>
<Modal ref='modal'></Modal>
<ToolTip ref='toolTip'></ToolTip>
</div>
`,
methods: {
handleModel() {
this.$refs.modal.toggleShow();
},
handleToolTip() {
this.$refs.toolTip.toggleShow();
}
},
})
</script>
</body>
</html>
作者:龙飞
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
浙公网安备 33010602011771号