组件(很重要很重要)
一.组件
- 组件可以看作是一些可复用的ui模块
- 小到一个标签 : <div>哈哈哈</div>
- 大到一个页面 : <div><div><div><div><div></div></div></div></div></div>
- 一个组件对应一个实例
- 组件 == Vue实例 == new Vue(options)
- 官网 : 组件是可复用的 Vue 实例
二. 组件化开发
模块开发 => 重业务,重逻辑 main,js => router.js => handle.js => too.js
组件化开发 => 重页面/UI 头部组件 左侧栏组件 主体部分组件
- 概念 : 将一个完整的页面抽离成一个个独立的组件,最终 , 通过这一个个独立组件完成整个的页面(项目)的功能
- 组件化开发的优势/作用 : 复用
三. 组件的基本使用
先注册,再使用
- Vue 中的两种注册组件的方法 1. 全局注册 2. 局部注册
- 全局组件在所有的vue实例中都可以使用
- 局部组件在所有的当前实例中可以使用
- 注册全局组件 - 基本使用
// 第一个参数 : 组件名
// 第二个参数 : 是一个配置对象, 改配置对象与Vue实例的配置对象几乎完全相同
// 也就是说: vue实例中用到的配置项,和组件中的配置项几乎相同
Vue.component('child',{
template : `
<h1 class='red'>这是child组件</h1>
`
})
- 注意点
- 注册全局组件也是放到vm实例之前
- 模板只能有一个根节点
- 组件的配置项和vue实例的配置项一样(如:data,methods,filters,watch,computed,钩子函数等)
- 组件的data是一个函数,并返回一个对象
// 演示为什么vue在组件中的数据采用函数,而不是对象
// 原因 : 只想让组件复用,不想让数据复用
var Component = function() {}
// 使用对象
Component.prototype.data = {
demo: 123
}
// 使用函数
Component.prototype.data = function() {
return {
demo: 111
}
}
var component1 = new Component()
var component2 = new Component()
component1.data().demo = '8888'
console.log(component2.data().demo) // 456
- 使用组件 ---- 当标签一样使用 <child></child>
四. 组件通讯(介绍)
导入 : 演示子组件访问父组件数据,发现报错
- 组件是一个独立.封闭的个体
- 也就是说: 组件中的数据默认情况下,只能在组件内部使用,无法直接在组件外部使用
- 可以将vue实例看做一个组件
- 对于组件之间需要相互使用彼此的情况,应该使用 组件通讯 机制 来解决
- 组件通讯的三种情况 :
- 父组件将数据传递给子组件(父 -> 子)
- 子组件将数据传递给父组件(子 -> 父)
- 非父子组件(兄弟组件)
五. 父 ===> 子 (重点) 两步
- 通过属性 ,父组件将要传递的数据,传递给子组件
<child :msg="pmsg"></child>
- 子组件通过props配置项,来指定要接收的数据
props:['msg'] // 以后使用 - 组件内 : msg - 事件中 : this.msg
六. 子 ==> 父 ( 重点 ) 三步
1. 父组件中提供了一个方法
pfn(arg){
consolg.log('父组件中接受到子组件传递过来的数据 : ',arg)
}
2. 通过自定义事件,父组件将这个方法传递给子组件
// 自定义事件
<child @fn="pfn"></child>
3. 子组件调用这个方法( 触发父组件中传递过来的自定义事件 )
// 在钩子函数里样式也可以,自己调用
created(){
// 调用父组件中的方法 pfn
// 注意 : 通过$emit 方法来触发事件 fn
// 第一个参数 : 便是要触发的自定义事件名称 ,也就是 @fn
// 第二个参数 : 表示要传递给父组件的数据
this.$emit('fn','child msg')
}
七. 目前为止存属性的地方
- data : 先声明好的,再使用
- 计算属性
- props : 传递过来的
- 标签 : {{ msg }} :title = 'msg' 事件 : this.msg
八. 单向数据流(组件与组件之间) (了解)
所有的prop都使得其赋值prop之间形成了一个单向下行绑定 : 父级prop的更新,会向下流动到子组件中,但是反过来不行.这样会防止从子组件意外改变父组件的状态,从而导致你的应用的数据流向难以理解
- 透漏三个问题 :
- 组件与组件之前是单向数据流
- 父级prop的更新回向下流动到子组件中
- 子组件不允许修改父组件传过来的prop数据
vue 是单向的还是双向的 ?
双向 (v <==> M )
单向 (组件与组件)
九: prop的特点 : 只读
- 演示验证 props 只读
- 传的是简单类型 : 修改会报错
- 传的是复杂类型(地址) : 修改不会报错,是因为地址没有变,测试 obj={ } 立马报错
- 修改 父组件传给子组件的数据
思路 : 把接受过来的数据保存到data中一个临时值(适用在该组件接受数据只会在当前组件内使用)
Vue.component('child', { template: ` <div>子组件 {{ cmsg }} </div> `, data() { return { cmsg: this.msg } }, props: ['msg'], created() { this.cmsg = 666 } })
十. prop的大小写
-
官 : HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。
-
html 的标签和 属性 都是一样,忽略大小写
-
<H1 TITLE="哈哈">我是h1</H1>
-
-
官 : 这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名不好使了
-
<child :cMsg="pmsg"></child>
会报警告,父传子也接收不到了 -
原因是 : 接收的属性是:cMsg, 因为忽略大小写,已为 : cmsg
-
所以已经准备要读取的 是 cmsg 的值,否则要报警告
You should probably use "c-msg" instead of "cMsg".
-
-
方式 1 : 全用小写,不要使用驼峰命名
-
接收 :
cmsg
-
props/读取 :
cmsg
-
-
方式 2 官 : 需要使用其等价的 kebab-case (短横线分隔命名) 命名: (推荐)
-
接收 :
:c-msg='pmsg'
-
props/读取 :
cMsg / this.cMsg
-
-
大小写在 父传子和 子传父中的应用 (都是要 带 - 的)
-
-
父传子 :
:c-msg ==> cMsg
改驼峰 - 因为props -
子传父 :
@todo-head = 'pAddTodo' ==> this.$emit('todo-head')
不改驼峰
-
-
完善 TodoMVC : 底部隐藏+剩余完成数+清除完成
-
计算属性 : 已知值(todoList 在 根组件) ==> 得到一个新值(子组件里使用)
-
父 => 子通讯
-
-
番外篇 : 方法当属性传、传过来的带:得到的原型
十一. 非父子之间通讯 (组件 => ) (重点)
需求 : 组件 jack ===> 弄啥哩 ===> 组件 rose
- 是通过 事件总线 ( event bus 公交车 ) 的机制 来实现的
- 事件总线 : 实际上就是一个 空Vue实例
- 可以实现任意两个组件之间的通讯 , 而不管两个组件到底是什么样的层级关系
- 看图
- 示例 :
// 第一步 : 事件总线
var bus = new Vue()
// 第二步 : 发送数据 可在点击事件里 触发事件
// 参数1 : 唯一标识 参数2 : 参数
bus.$emit('todo','弄啥哩')
// 第三步 接受数据 可在created 里 注册事件
bus.$on('todo',arg => {
console.log('接受过来的',arg)
})
十二 . 注册局部组件
- 局部组件 只能在当前vue实例中使用
- 示例
// 注册局部组件 : conponents:{ //child 表示组件名称 // 值为配置对象,与 Vue.component 中的第二个参数相同 // 注意 :子组件child属于Vue实例 , 因此 ,只能在Vue实例的模板中使用 child:{ templae : ` <div>这是局部组件</div> ` } }
十三 . 获取组件(获取DOM元素) -- refs
- 说明 : vm.$refs 一个对象,持有已注册过ref的所有子组件(HTML 元素)
- 使用
- 注册
// $refs = { div : div元素 , child:child元素 } // 标签 <div ref="div">哈哈</div> // 组件 <child ref="child"></child>
- 注意 : mounted 中的使用
// mounted 操作DOM this.$refs.div this.$refs.child
- 注意点 : 如果获取的是一个子组件 , 那么通过 ref 就能获取到子组件中的data 和 methods
this.$refs.child.num this.$refs.child.fn
- 场景 : 一般在第三方组件中 , 可能会用到这个功能
- 示例 ;
// 组件 <div ref='div'>哈哈哈</div> <child ref='child'></child> //js mounted(){ console.log(this.$refs.div) console.log(this.$refs.child.fn) }