偏前端-vue.js学习之路初级(二)组件化构建
vue.js 组件化构建
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、自包含和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
一、使用组件
1.组件的全局注册--可以使用 Vue.component(tagName, options)
Vue.component('my-component', {
// 选项
})
组件在注册之后,便可以在父实例的模块中以自定义元素** <my-component></my-component> **的形式使用。要确保在初始化根实例 之前 注册了组件:
<div id="example">
<my-component></my-component>
</div>
// 注册
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
渲染为:
<div id="example">
<div>A custom component!</div>
</div>
2.局部注册--不必在全局注册每个组件。通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用:
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 将只在父模板可用,不添加到components选项里面则可适用于任何作用域
'my-component': Child
}
})
二、组件构造器选项
在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。因此,通过Vue构造器传入的各种选项大多数都可以在组件里用。 data 是一个例外,它必须是函数。
Vue组件构造器常用的选项:
// 注册
Vue.component('my-component', {
template: '<div>A custom component!</div>',
data: function() {
return {
var1: xxx,
var2: xxx
}
},
computed: {
sumOfVar2: function() {
return (this.var2.innerVar1 + this.var2.innerVar2 + this.var2.innerVar3);
}
},
watch: {
'var1': function() {
},
'var2.innerVar1': function() {
},
'var2.innerVar2': function() {
},
'var2.innerVar3': function() {
}
},
methods: {
func1: function() {
},
func2: function() {
},
func3: function() {
}
},
beforeCreate: function () {
},
created: function () {
},
beforeMount: function () {
},
mounted: function() {
},
beforeUpdate: function () {
},
updated: function () {
},
beforeDestroy: function() {
},
destroyed: function() {
}
});
三、异步组件
在大型应用中,我们可能需要将应用拆分为多个小模块,按需从服务器下载。为了让事情更简单, Vue.js 允许将组件定义为一个工厂函数,动态地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。
如:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// Pass the component definition to the resolve callback
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
}) 工厂函数接收一个** resolve 回调**,在收到从服务器下载的组件定义时调用。也可以调用 reject(reason) 指示加载失败。这里 setTimeout 只是为了演示。怎么获取组件完全由你决定。
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 require 语法告诉 webpack
// 自动将编译后的代码分割成不同的块,
// 这些块将通过 Ajax 请求自动下载。
require(['./my-async-component'], resolve)
})
我们可以将各个组件的template代码单独写到一个html文件,通过Ajax的get方法获取组件html文件的内容,一样可以实现代码切割的功能,方便模块化管理代码:
Vue.component('my-component', function (resolve, reject) {
$.get('url', function (text) {
resolve({
template: text,
// 选项
});
});
});
四、局部组件
局部组件一般是在创建子组件的情况出现。生命周期依赖于父组件。
创建局部组件很简单:
const subComponent = {
template: 'I am subComponent',
data: function() {
return {
var: xxx
}
}
methods: {
back: function () {
this.$router.back();
},
},
// 其它选项
}
其它构造器选项和Vue实例、全局组件的构造器选项一样。
五、动态组件
使用保留的 <component> 元素,动态地绑定到它的 is 特性,我们让多个组件可以使用同一个挂载点,并动态切换:
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
})
<component v-bind:is="currentView">
<!-- 组件在 vm.currentview 变化时改变! -->
</component>
** # keep-alive **
如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数:
<keep-alive>
<component :is="currentView">
<!-- 非活动组件将被缓存! -->
</component>
</keep-alive>
六、使用Slot分发内容
在使用组件时,我们常常要像这样组合它们:
<app>
<app-header></app-header>
<app-footer></app-footer>
</app>
注意两点:
- <app> 组件不知道它的挂载点会有什么内容。挂载点的内容是由<app>的父组件决定的。
- <app> 组件很可能有它自己的模版。
** # 编译作用域 **
在深入内容分发 API 之前,我们先明确内容在哪个作用域里编译。假定模板为:
<child-component>
{{ message }}
</child-component>
message 应该绑定到父组件的数据,还是绑定到子组件的数据?答案是父组件。组件作用域简单地说是:
父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。
一个常见错误是试图在父组件模板内将一个指令绑定到子组件的属性/方法:
<!-- 无效 -->
<child-component v-show="someChildProperty"></child-component>
假定 someChildProperty 是子组件的属性,上例不会如预期那样工作。父组件模板不应该知道子组件的状态。
如果要绑定作用域内的指令到一个组件的根节点,你应当在组件自己的模板上做:
Vue.component('child-component', {
// 有效,因为是在正确的作用域内
template: '<div v-show="someChildProperty">Child</div>',
data: function () {
return {
someChildProperty: true
}
}
})
** # 单个 Slot **
最初在 <slot> 标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。
<div>
<h2>我是子组件的标题</h2>
<slot>
只有在没有要分发的内容时才会显示。
</slot>
</div>
父组件模版:
<div>
<h1>我是父组件的标题</h1>
<my-component>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
</my-component>
</div>
渲染结果:
<div>
<h1>我是父组件的标题</h1>
<div>
<h2>我是子组件的标题</h2>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
</div>
</div>
** 具名Slot **
仍然可以有一个匿名 slot ,它是默认 slot ,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<app-layout>
<h1 slot="header">这里可能是一个页面标题</h1>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
<p slot="footer">这里有一些联系信息</p>
</app-layout>
渲染结果为:
<div class="container">
<header>
<h1>这里可能是一个页面标题</h1>
</header>
<main>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
</main>
<footer>
<p>这里有一些联系信息</p>
</footer>
</div>
在组合组件时,内容分发 API 是非常有用的机制。
七、组建间的通信
大的Vue项目由组件构成,每个组件维护各自的状态数据。但再完美的架构,也不可能实现组件之间完全解耦。组件之间随时都可能进行数据交互。因此,组件之间通信是不可避免的。
Vue组件通信的三种方案:
Vue的组件间通信分2种情况:父子组件之间通信和非父子组件之间通信。
在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。父组件通过 props 向下传递数据给子组件,子组件通过events 给父组件发送消息。看看它们是怎么工作的。
Vue1.x版本的父子组件之间通信的方式有:Prop传递数据和自定义事件,非父子组件之间通信的方式有中央事件总线。
Vue2.x版本引入状态管理模式的概念,使用专用的状态管理层——Vuex。
@@父子组件间通信
1. 使用Prop传递数据
子组件要显示地用props选项声明它期待获得的数据:
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 一样,prop 可以用在模板内
// 同样也可以在 vm 实例中像 “this.message” 这样使用
template: '<span>{{ message }}</span>'
})
然后我们可以这样向它传入一个普通字符串:
<child message="hello!"></child>
结果:
hello!
# camelCase vs. kebab-case
HTML特性是不区分大小写的。所以,当使用的不是字符串模版,camelCased(驼峰式)命名的prop需要转换为相对应的kebab-case(短横线隔开式)命名:
Vue.component('child', {
// camelCase in JavaScript
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
<!-- kebab-case in HTML -->
<child my-message="hello!"></child>
*如果你使用字符串模版,则没有这些限制。
# 动态Prop
在模板中,要动态地绑定父组件的数据到子模板的props,与绑定到任何普通的HTML特性相类似,就是用 v-bind。每当父组件的数据变化时,该变化也会传导给子组件:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
# 字面量语法 vs 动态语法
初学者常犯的一个错误是使用字面量语法传递数值:
<!-- 传递了一个字符串 "1" -->
<comp some-prop="1"></comp>
因为它是一个字面 prop ,它的值是字符串 "1" 而不是number。如果想传递一个实际的number,需要使用 v-bind ,从而让它的值被当作 JavaScript 表达式计算:
<!-- 传递实际的 number -->
<comp v-bind:some-prop="1"></comp>
# 单向数据流
prop是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态--这会让应用的数据流难以理解。
另外,每次父组件更新时,子组件的所有prop都会更新为最新值。这意味这你不应该在子组件内部改变prop。如果你这么做了,Vue会在控制台给出警告。
为什么我们会有修改prop中数据的冲动呢?通常是这两种原因:
- prop 作为初始值传入后,子组件想把它当作局部数据来用;
- prop 作为初始值传入,由子组件处理成其它数据输出。
对这两种原因,正确的应对方式是:
1.定义一个局部变量,并用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
2.定义一个计算属性,处理 prop 的值并返回。
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
<u>*注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态</u>
# Prop验证
我们可以为组件的 props 指定验证规格。如果传入的数据不符合规格,Vue 会发出警告。当组件给其他人使用时,这很有用。
要指定验证规格,需要用对象的形式,而不能用字符串数组:
Vue.component('example', {
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
})
type 可以是下面原生构造器:
- String
- Number
- Boolean
- Function
- Object
- Array
type 也可以是一个自定义构造器函数,使用 instanceof 检测。
当 prop 验证失败,Vue会在抛出警告 (如果使用的是开发版本)。
2. 自定义事件
我们知道,父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,应该怎样做?那就是自定义事件!
# 使用 v-on绑定自定义事件
每个 Vue 实例都实现了事件接口(Events interface),即:
- 使用 **$on(eventName) **监听事件
- 使用 **$emit(eventName) **触发事件
Vue的事件系统分离自浏览器的EventTarget API。尽管它们的运行类似,但是$on和 $emit不是addEventListener和 dispatchEvent的别名。
另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
<u>*不能用$on侦听子组件抛出的事件,而必须在模板里直接用v-on绑定,就像以下的例子:</u>
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
# 给组件绑定原生事件
有时候,你可能想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。例如:
<my-component v-on:click.native="doTheThing"></my-component>
@@非父子组件间通信
1. 中央事件总线
在简单的场景下,可以使用一个空的 Vue 实例作为中央事件总线(global event bus)。
比如,假设我们有个 todo 的应用结构如下:
Todos
|-- NewTodoInput
|-- Todo
|-- DeleteTodoButton
可以通过单独的事件中心管理组件间的通信:
// 将在各处使用该事件中心
// 组件通过它来通信
var eventHub = new Vue()
然后在组件中,可以使用 $emit, $on, $off 分别来分发、监听、取消监听事件:
// NewTodoInput
// ...
methods: {
addTodo: function () {
eventHub.$emit('add-todo', { text: this.newTodoText })
this.newTodoText = ''
}
}
// DeleteTodoButton
// ...
methods: {
deleteTodo: function (id) {
eventHub.$emit('delete-todo', id)
}
}
// Todos
// ...
created: function () {
eventHub.$on('add-todo', this.addTodo)
eventHub.$on('delete-todo', this.deleteTodo)
},
// 最好在组件销毁前
// 清除事件监听
beforeDestroy: function () {
eventHub.$off('add-todo', this.addTodo)
eventHub.$off('delete-todo', this.deleteTodo)
},
methods: {
addTodo: function (newTodo) {
this.todos.push(newTodo)
},
deleteTodo: function (todoId) {
this.todos = this.todos.filter(function (todo) {
return todo.id !== todoId
})
}
}
偏前端-vue.js学习之路初级(二)组件化构建的更多相关文章
- 偏前端-vue.js学习之路初级(一)概念
首先--不推荐新手直接使用 vue-cli,尤其是在你还不熟悉基于 Node.js 的构建工具时. 新建一个html,引入一下js: <!-- 开发环境版本,包含了有帮助的命令行警告 -- ...
- vue.js学习之 如何在手机上查看vue-cli构建的项目
vue.js学习之 如何在手机上查看vue-cli构建的项目 一:找到config文件夹下的index.js文件,打开后,将host的值改为你本地的ip,保存后重启项目 二:输入ip和端口号打开项目 ...
- 前端——Vue.js学习总结一
一.什么是Vue.js 1.Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架 2.Vue.js 是前端的主流框架之一,和Angular.js.React.js 一起,并成为前端 ...
- Vue.js 学习笔记 第7章 组件详解
本篇目录: 7.1 组件与复用 7.2 使用props传递数据 7.3 组件通讯 7.4 使用slot分发内容 7.5 组件高级用法 7.6 其他 7.7 实战:两个常用组件的开发 组件(Compon ...
- Vue.js学习使用心得(二)——自定义指令
自定义指令 除了核心功能默认内置的指令,Vue 也允许注册自定义指令. 自定义指令可以定义全局指令,也可以定义局部指令. 使用 directives 选项来自定义指令. 定义全局指令: <div ...
- Vue.js学习笔记 第八篇 组件
全局注册组件 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...
- mock.js学习之路(二)easy-mock(Vue中使用)
1.easy-mock建立外部数据,注册账号,创建数据,详细使用过程参照https://www.easy-mock.com/docs文档说明 2.项目中如何引入使用 ①配置一下config.index ...
- Vue.js 学习笔记 第1章 初识Vue.js
本篇目录: 1.1 Vue.js 是什么 1.2 如何使用Vue.js 本章主要介绍与Vue.js有关的一些概念与技术,并帮助你了解它们背后相关的工作原理. 通过对本章的学习,即使从未接触过Vue.j ...
- Web前端-Vue.js必备框架(五)
Web前端-Vue.js必备框架(五) 页面组件,商品列表组件,详情组件,购物车清单组件,结算页组件,订单详情组件,订单列表组件. vue-router 路由 vuex 组件集中管理 webpack ...
随机推荐
- MUI框架-01-介绍-创建项目-简单页面
MUI框架-01-介绍-准备-创建项目 从0开始快速高效学习 MUI 框架 官方文档:http://dev.dcloud.net.cn/mui/ui/ (1)MUI 介绍 MUI 是什么,解决了什么问 ...
- Software Testing Techniques Homework 2
Problem 1 1. The fault is i > 0, it should be i >= 0, because if the case is x = [0], y= 0, w ...
- PHPStorm/webstorm/PyCharm tips
phpstorm对于使用PHP开发web的人员来说,是一个非常不错的编辑开发IDE,以前用过sublime,但是相比于storm,sublime在浏览legacy代码,类代码编辑方面明显要逊色不少.同 ...
- Linux yum提示Loaded plugins错误的解决方法
yum是Linux软件包管理器,也叫yum源,在yum使用过程中,有时会出现Loaded plugins错误,重启无效,遇到这种情况该如何解决呢?下面就给大家介绍下Linux yum提示Loaded ...
- 使用公钥和私钥实现LINUX下免密登录
linux公钥私钥实现无密码登录 首先本地主机生成公约和私钥 # ssh-keygen /生成公钥和私钥 不要更改默认路径,中途不要输入密码,直接两次回车. 2. 将生成 ...
- java获取文件大小的方法
目前Java获取文件大小的方法有两种: 1.通过file的length()方法获取: 2.通过流式方法获取: 通过流式方法又有两种,分别是旧的java.io.*中FileInputStream的ava ...
- 【转】】Vue项目部署tomcat,刷新报错404解决办法
转自[https://blog.csdn.net/g631521612/article/details/82835518] 解决方式: - 在tocmat的webapps下的项目中创建WEB-INF文 ...
- Exchange 2016证书配置
配置证书: 第一步,在ECP界面生成证书请求文件: 1.在“服务器 —>证书”界面,选择一台服务器,点击“+”来添加证书申请,如下图: 2.默认下一步, 3.填写证书的友好名称,如下图: 4.默 ...
- SQL Server系统常用存储过程
SQL Server系统存储过程也是好几百个,算了,还是写几个常用的. 1.sp_help 查询表的信息 执行存储过程: sp_help Person 显示结果如下: 妈了个B,有了这张图,你还不懂怎 ...
- Linux下中间人攻击利用框架bettercap测试
0x01简介 bettercap可用来实现各种中间人攻击,模块化,便携.易扩展 0x02特点 提到中间人攻击,最知名的莫过于ettercap,而开发bettercap的目的不是为了追赶它,而是替代它 ...