随着ajax异步请求的出现,慢慢形成了前后端的分离
客户端需要完成的事情越来越多,代码量也是与日俱增。
为了应对代码量的剧增,我们通常会将代码组织在多个js文件中,进行维护。
但是这种维护方式,依然不能避免一些灾难性的问题。
但是当js文件过多,比如有几十个的时候,弄清楚它们的顺序是一件比较同时的事情。
而且即使你弄清楚顺序了,也不能避免上面出现的这种尴尬问题的发生。
官方 - 《组件基础》 - https://cn.vuejs.org/v2/guide/components.html
Vue.extend()
方法创建组件构造器<body>
<div id='app'>
<my-cpn>my-cpn>
div>
<script src="../dist/vue.js">script>
<script>
// 1.创建组件的构造器对象
const cpnC = Vue.extend({
template: `
我是标题
我是内容
`
})
// 2. 注册组件 (全局组件,意味着可以在多个Vue的实例下使用)
Vue.component('my-cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
}
})
script>
body>
<body>
<div id='app'>
<my-cpn>my-cpn>
div>
<script src="../dist/vue.js">script>
<script>
// 1.创建组件的构造器对象
const cpnC = Vue.extend({
template: `
我是标题
我是内容
`
})
const app = new Vue({
el: '#app',
data: {
},
components: { // 2. 注册局部组件
'my-cpn': cpnC
}
})
script>
body>
<body>
<div id='app'>
<cpn2>cpn2>
div>
<script src="../dist/vue.js">script>
<script>
// 1.创建第一个组件
const cpn1 = Vue.extend({
template: `
我是内容 cpn1
`
})
// 2.创建第一个组件
const cpn2 = Vue.extend({
template: `
我是标题 cpn2
`,
components: {
cpn1: cpn1
}
})
const app = new Vue({
el: '#app',
data: {
},
components: { // 2. 注册局部组件
cpn2: cpn2
}
})
script>
body>
cpn2包含了 cpn1,所以cpn2是父组件
省略 Vue.extend ,直接在 component里面定义 组件构造器
<body>
<div id='app'>
<cpn>cpn>
div>
<script src="../dist/vue.js">script>
<script>
Vue.component('cpn', {
template: `
我是标题
我是内容,哈哈哈
`
})
const app = new Vue({
el: '#app',
data: {
}
})
script>
body>
把模板写在 标签内
<body>
<div id='app'>
<cpn>cpn>
div>
<script type="text/x-template" id='cpn'>
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈</p>
</div>
script>
<script src="../dist/vue.js">script>
<script>
Vue.component('cpn', {
template: '#cpn'
})
const app = new Vue({
el: '#app',
data: {
}
})
script>
body>
或者这样
<body>
<div id='app'>
<cpn>cpn>
div>
<template id='cpn'>
<div>
<h2>我是标题h2>
<p>我是内容,哈哈哈p>
div>
template>
<script src="../dist/vue.js">script>
<script>
Vue.component('cpn', {
template: '#cpn'
})
const app = new Vue({
el: '#app',
data: {}
})
script>
body>
如果组件内容需要动态显示,需要在组件内定义数据(数据不能放在顶级实例内,组件是无法访问到的)
另外:
组件内部的 data 属性值必须是个 函数
因为,Vue为了防止多个同一种组件之间的数据相互影响
详细看 : https://cn.vuejs.org/v2/guide/components.html#data-必须是个函数
即,写法要如下
<body>
<div id='app'>
<cpn>cpn>
div>
<template id='cpn'>
<div>
<h2>{{title}}h2>
<p>我是内容,哈哈哈p>
div>
template>
<script src="../dist/vue.js">script>
<script>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
title: '我是个标题'
}
}
})
const app = new Vue({
el: '#app',
data: {}
})
script>
body>
<body>
<div id='app'>
<cpn v-bind:cmovies='movies' :cmessage='message'>cpn>
div>
<template id="cpn">
<div>
<h2>{{cmessage}}h2>
<div>{{cmovies}}div>
div>
template>
<script src="../dist/vue.js">script>
<script>
const cpn = {
template: '#cpn',
props: ['cmovies', 'cmessage']
}
const app = new Vue({
el: '#app',
data: {
movies: ['明日花绮罗', '上三悠亚', '斋藤飞鸟'],
message: '你好啊'
},
components: {
cpn: cpn
}
})
script>
body>
<body>
<div id='app'>
<cpn v-bind:cmovies='movies' :cmessage='message'>cpn>
div>
<template id="cpn">
<div>
<h2>{{cmessage}}h2>
<div>{{cmovies}}div>
div>
template>
<script src="../dist/vue.js">script>
<script>
const cpn = {
template: '#cpn',
props: {
cmovies: Array,
cmessage: String
}
}
const app = new Vue({
el: '#app',
data: {
movies: ['明日花绮罗', '上三悠亚', '斋藤飞鸟'],
message: '你好啊'
},
components: {
cpn: cpn
}
})
script>
body>
如果偷偷把 message 类型改一下(如下)
const app = new Vue({
el: '#app',
data: {
movies: ['明日花绮罗', '上三悠亚', '斋藤飞鸟'],
message: ['你好啊']
},
components: {
cpn: cpn
}
})
<body>
<div id='app'>
<cpn v-bind:cmovies='movies' :cmessage='message'>cpn>
div>
<template id="cpn">
<div>
<h2>{{cmessage}}h2>
<div>{{cmovies}}div>
div>
template>
<script src="../dist/vue.js">script>
<script>
const cpn = {
template: '#cpn',
props: {
cmovies: Array,
cmessage: {
type: String,
default: '你好骚啊'
}
}
}
const app = new Vue({
el: '#app',
data: {
movies: ['明日花绮罗', '上三悠亚', '斋藤飞鸟'],
//message: ['你好啊']
},
components: {
cpn: cpn
}
})
script>
body>
结论:
- 在不用脚手架情况下,写html标签时候,必须不能用驼峰(html不区分大小写)
- 用了脚手架,随意()
如果我们在子组件内做 props 的双向绑定,打开控制台,发现会报错(如下图)
<body>
<div id='app'>
<cpn :num1='num1'>cpn>
div>
<template id="cpn">
<div>
<h2>{{num1}}h2>
<input type="text" v-model="num1">
div>
template>
<script src="../dist/vue.js">script>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
props: {
num1: String
}
}
// 2.父组件
const app = new Vue({
el: '#app',
data: {
num1: '1'
},
components: {
cpn: cpn
},
})
script>
body>
看报错信息,知道是官方不推荐。
一个很大的原因是 :这样违背了 组件的独立性(如,子组件1改了值 ⇒ 父组件值也改 ⇒ 子组件2值也改了 ⇒ 【组件1和组件2的值绑定了(不独立)】)
报错信息里面也说了处理方法
Instead, use a data or computed property based on the prop's value. Prop being mutated: "num1"
翻译:用一个 data 或者 计算属性,接收传过来的 prop 值作为初始值。
解决方案如下
<body>
<div id='app'>
<cpn :father-num='fatherNum'>cpn>
div>
<template id="cpn">
<div>
<h2>{{num}}h2>
<input type="text" v-model="num">
div>
template>
<script src="../dist/vue.js">script>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
props: {
fatherNum: String
},
data() {
return {
num: this['fatherNum']
}
}
}
// 2.父组件
const app = new Vue({
el: '#app',
data: {
fatherNum: '1'
},
components: {
cpn: cpn
},
})
script>
body>
注意 驼峰标识 问题
<body>
<div id='app'>
<cpn @item-click='cpnClick'>cpn>
div>
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}button>
div>
template>
<script src="../dist/vue.js">script>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
data() {
return {
categories: [{
id: 73944,
name: 'Route'
},
{
id: 72827,
name: 'generating'
},
{
id: 52405,
name: 'Intelligent RAM Books'
},
{
id: 86224,
name: 'Usability'
},
]
}
},
methods: {
btnClick(item) {
this.$emit('item-click', item);
}
}
}
// 2.父组件
const app = new Vue({
el: '#app',
data: {},
components: {
cpn: cpn
},
methods: {
cpnClick(value) {
console.log(value)
}
},
})
script>
body>
<body>
<div id='app'>
<h2>fatherNum:{{fatherNum}}h2>
<cpn :father-num='fatherNum' @num-changed='numChangedHandler'>cpn>
div>
<template id="cpn">
<div>
<h2>num:{{num}}h2>
回车把值返回父组件:<input type="text" v-model="num" v-on:keyup.enter="numHandler(num)">
div>
template>
<script src="../dist/vue.js">script>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
props: {
fatherNum: String
},
data() {
return {
num: this['fatherNum']
}
},
methods: {
numHandler(num) {
this.$emit('num-changed', num);
}
},
}
// 2.父组件
const app = new Vue({
el: '#app',
data: {
fatherNum: '1'
},
components: {
cpn: cpn
},
methods: {
numChangedHandler(value) {
this.fatherNum = value;
}
},
})
script>
body>
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。
$children
或 $refs
$parent
我们先来看下 $children
的访问
this.$children
是一个数组类型,它包含所有子组件对象。
我们这里通过一个遍历,取出所有子组件的 message
状态。
<body>
<div id='app'>
<cpn>cpn>
<cpn>cpn>
<cpn>cpn>
<button @click='btnClick'>打印子组件button>
div>
<template id="cpn">
<div>我是子组件div>
template>
<script src="../dist/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {},
methods: {
btnClick() {
console.log('展示子组件:', this.$children)
this.$children[0].showMessage();
}
},
components: {
cpn: {
template: '#cpn',
methods: {
showMessage() {
console.log('showMessage');
}
},
}
}
})
script>
body>
$children
的缺陷:
$children
访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。$refs
$refs
的使用:
$refs
和 ref
指令通常是一起使用的。this.$refs.ID
就可以访问到该组件了。<body>
<div id='app'>
<cpn>cpn>
<cpn ref="cpn">cpn>
<cpn>cpn>
<button @click='btnClick'>打印子组件button>
div>
<template id="cpn">
<div>我是子组件div>
template>
<script src="../dist/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {},
methods: {
btnClick() {
console.log('展示子组件:', this.$children)
console.log('展示 $refs.cpn :', this.$refs['cpn'])
console.log('展示 $refs.cpn.$el :', this.$refs['cpn'].$el)
this.$refs.cpn.showMessage();
}
},
components: {
cpn: {
template: '#cpn',
methods: {
showMessage() {
console.log('showMessage');
}
},
}
}
})
script>
body>
$parent
- 子访问父如果我们想在子组件中直接访问父组件,可以通过 $parent
注意事项:
$parent
来访问父组件,但是在真实开发中尽量不要这样做。<body>
<div id='app'>
<cpn>cpn>
div>
<template id="cpn">
<div>
<button @click='btnClick'>点击 - 控制台打印父组件button>
div>
template>
<script src="../dist/vue.js">script>
<script>
const cpn = {
template: '#cpn',
methods: {
btnClick() {
console.log(this.$parent);
}
},
}
const app = new Vue({
el: '#app',
data: {},
components: {
cpn: cpn
}
})
script>
body>
根组件也就是 我们 new 出来的 Vue
上面界面 代码如下
<body>
<div id='app'>
<p>rootp>
<cpn>cpn>
<p>rootp>
div>
<template id="cpn">
<div>
<button @click='btnClick'>点击 - 控制台打印【 根 root 】组件button>
div>
template>
<script src="../dist/vue.js">script>
<script>
const cpn = {
template: '#cpn',
methods: {
btnClick() {
console.log(this.$root);
}
},
}
const app = new Vue({
el: '#app',
data: {},
components: {
cpn: cpn
}
})
script>
body>
我们点击一下,就能访问到 root 组件了(跟上面例子结果一样,是因为这个的父组件就是根组件,感兴趣套多层组件看看)
文章目录
- # 组件化
- ## 不用组件的两个【灾难性问题】
- 1、如==全局变量同名==问题
- 2、又如 代码的编写方式对js文件的依赖顺序几乎是强制性的
- ## Vue 组件化思想
- ## 注册组件
- 注册组件的基本步骤
- 基本使用
- 全局组件 、 局部组件
- 父组件和子组件
- 语法糖:组件注册
- ## 技巧:模板分离写法
- ## 组件访问实例数据 【无法访问的】
- ## 为什么 组件内部的 data 属性值必须是个 函数???
- ## 父子组件之间通讯
- 通过 props 向子组件传递数据
- 通过 props 向子组件传递数据 - 验证数据类型 - type
- 通过 props 向子组件传递数据 - 验证数据类型 - validator + required
- 通过 props 向子组件传递数据 - 默认值 - default
- 通过 props 向子组件传递数据 - 驼峰标识
- **【误区】**props 双向绑定
- 通过 事件 向父组件发送消息
- 父子通信案例:
- ## 子组件最顶层只能有一个element
- ## 父子组件之间 - 访问方式
- $children - 父访子 - 【不常用,常用 ref】
- ref - 父访子
- `$parent` - 子访问父
- $root 访问根组件