在 vue
中,我们可以通过 new Vue
来创建一个组件,不过通常它是作为整个应用的顶层根组件
存在的,我们还可以通过另外的方式来注册一个更为通用的组件(可复用功能性组件)。
Vue.component('组件名称', {组件选项})
组件名称
遵循自定义组件命名规范:全小写、连字符(虽然驼峰式一般也没问题),当作标签使用,为了满足html5
规范new Vue
选项配置基本一致(也有一些细节的不同)创建可复用的组件分为
通过 Vue.component
注册的组件,我们称为全局组件,因为它可以在任意范围内使用,我们还可以定义局部组件
new Vue({
...,
components: {
'组件名称': {组件选项}
}
})
在一个组件内部通过 components
选项注册的组件是局部组件,只能在当前 components
选项所在的组件内部使用
注意:局部注册的组件只能中当前注册的组件中使用,不能在它的子组件中使用
可复用组件中 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.82 commit description:a0.82(example01-1——可复用组件的使用) tag:a0.82 同样 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.83 commit description:a0.83(example01-2——1个组件中顶层元素有且只能有1个) tag:a0.83 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.84 commit description:a0.84(example01-3——打印根组件与可复用组件返回值) tag:a0.84 局部组件:注册在组件内部的组件,仅供内部使用 全局使用内部组件就报错了。 提示:有一个未知的自定义元素。 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.85 commit description:a0.85(example01-4——全局使用内部组件就报错了) tag:a0.85 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.86 commit description:a0.86(example01-5——局部组件的使用) tag:a0.86 在非 可复用组件中的 我们在它的 报错,提示当前 这跟 可把每个组件理解为它们都是独立私有的空间(相当于C++、Java语言中类中privte性质一样),因此只能在组件的作用域内才能访问变量 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.87 commit description:a0.87(example02-1——在 tag:a0.87 在 还是报错了,但是另外的一个错误了,警告 可复用组件中, 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.88 commit description:a0.88(example02-2——在可复用组件内部定义一个变量,看能否在其内部的template访问) tag:a0.88 可复用组件中,data 必须是一个函数,且该函数必须返回一个对象,该对象就是组件最终的 data 值。 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.89 commit description:a0.89(example02-3——可复用组件中,data 必须是一个函数) tag:a0.89 自定义组件标签实际是一个函数。 这个原理大致模拟一下: 两者不是同一个对象,只是起初长得一样。 修改值了后其实两者是不干扰的。 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.90 commit description:a0.90(example02-4——原生js探究vue原理-new出的多个对象不是同一个对象,互相独立) tag:a0.90 原生 相反并不是用一次, 而这里如果再想拎起炉灶,还需要再 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.91 commit description:a0.91(example02-5——原生js探究vue原理-component 工厂函数,对new的行为进行了包装) tag:a0.91 所以实际上 我们尝试修改一个组件的 它们都是用同一套 这样就导致两个对象其实是共享这个 可能有的浏览器会直接优化成两次打印都一样了,可能浏览器动态打印了最新的数据,因此可以打断点一步一步执行,就可打印出真实结果了。 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.92 commit description:a0.92(example02-6——原生js探究vue原理-data是对象带来的问题) tag:a0.92 所以通过 这样 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.93 commit description:a0.93(example02-7——原生js探究vue原理-解决data是对象带来的问题,探究真相) tag:a0.93 组件中内部私有数据存储中组件 实现:计算圆面积 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.94 commit description:a0.94(example03-1——props数据进行传递) tag:a0.94 因为 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.95 commit description:a0.95(example03-2——props与data命名冲突) tag:a0.95 建议 可以是如下形式: 如有一些需求需要子组件去更改父组件的值怎么办? 注意:不要修改 父组件通过 需求:假如有一个商品列表,我们需要把这个商品列表显示出来,类似购物车列表。 简单展示商品列表 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.96 commit description:a0.96(example04-1——简单展示商品列表) tag:a0.96 点击 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.97 commit description:a0.97(example04-2——简单展示商品列表-总数) tag:a0.97 计算数组元素相加后的总和 子组件操作该商品数量的加减 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.98 commit description:a0.98(example04-3——传地址子级可修改父级的data) tag:a0.98 我们看以上运行是正常的,其实隐藏着很大的坑,现在传进来的数据是一个( 那为什么子组件修改该组件,父组件就会重新渲染视图?那父组件如何知道数据发生变化呢? 我们现在传进去的 这会产生很严重的问题!如=>可能导致多个模块共享一个对象,这边修改,影响了那边。 我们尽量不要去传址,共用对象。 数量我们单独去传,即传进去的并不是是一个对象。 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a0.99 commit description:a0.99(example04-3——传值子级可修改父级的data被禁止) tag:a0.99 实际 如果在子组件中 最正确的方法 使用 在父级创建一个专门修改该数据的函数,传给父级允许修改父级数据的子级,由子级调用该回调函数,就间接修改了父级数据了。 事件数据就是中触发事件的同时携带传递的数据 - 父级在使用该组件的过程中,可以通过 事件函数的第一个参数就是事件数据 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.01 commit description:a1.01(example05-1——往子级传递回调函数修改data报错) tag:a1.01 报错了,输出看看 我们发现,其实本质上能拿到函数的,只是 然而在 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.02 commit description:a1.02(example05-2——探究往子级传递回调函数修改data报错的原因) tag:a1.02 小迪很抱歉,上面的代码写的有问题,并且分析错了,我们错把方法写在 实际上 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.03 commit description:a1.03(example05-2——探究往子级传递回调函数修改data报错的原因——纠正错误说法) tag:a1.03 在 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.04 commit description:a1.04(example05-3——vue事件机制实现子级修改父级data) tag:a1.04 以上实际就是一个单向数据流。 综上实现子级修改父级 第一种是用原生的手段,我还是建议用事件机制更稳妥一些。(由于小迪水平暂时有限,无法深入探究两者的优缺点,还请高手赐教) 上面的例子实际就是一个单向数据流,组件双绑实际就是一个语法糖。 虽然并不推荐在组件内部修改 上面的案例数据是单向的,数据往子级传,如果要修改数据,通过事件机制传播到父级,然后再由父级修改的。 双绑其实就是障眼法,即一种语法糖。 其实上面的案例实际就实现了双绑,已经实现数据双向流了,只是我们的 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.05 commit description:a1.05(example06—v-model) tag:a1.05 这个由 以上就是关联关系了。 即 实际上与上面的事件机制没什么区别,只是少了一句 注意: 注意:官方建议避免使用 通过 传递参数的时候需要加上 实际跟 这里事件名称要使用 参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.06 commit description:a1.06(example07—.sync的使用) tag:a1.06 内部事件特殊的起名 => 通过这种方式,能明显看出来是影响的内部哪个属性,内部的哪个属性更新后会同步到外部来,而 同时省略掉事件监听与事件函数。因此 所以建议使用这种方式。 以上方法这些写法都是可以的,有时候觉得使用起来还不错,但是太多的 而内部的语法糖根据 不过多于小迪刚初来乍到的vue新手,势必被割一波韭菜,但又不是真正意味的割韭菜,掉的坑多了,后面就成高手了,小迪建议学完vue基本使用,一定要往源码上深挖。el
选项就是模板中的顶层元素 =>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<new-component>new-component>
div>
<script src="./js/vue.js">script>
<script>
Vue.component('new-component', {
//el: '' // 就是模板中的顶层元素
template: `
Branch: branch033.2.2 example01-2
template
也遵循一些规则,一个组件中顶层元素有且只能有1个。 template: `
Branch: branch033.2.3 example01-3
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<new-component>new-component>
div>
<script src="./js/vue.js">script>
<script>
// 全局组件 - 组件工厂函数
let newComponent = Vue.component('new-component', {
template: `
newComponent
返回的是一个函数全局组件 - 组件工厂函数
:主要用来构建当前组件的。Vue.component
函数返回对象,我们可以在app
对象中看到,它和Vue对象
本质上没有太大区别,VueComponent
实际上也是一个组件,只是细节上的不同。实际有点类似,js
原生对象下的_ptoto_
属性,一环套一环。
Branch: branch033.2.4 example01-4
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<new-component>new-component>
<new-component1>new-component1>
div>
<script src="./js/vue.js">script>
<script>
let newComponent = Vue.component('new-component', {
template: `
Branch: branch033.2.5 example01-5
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<new-component>new-component>
div>
<script src="./js/vue.js">script>
<script>
let newComponent = Vue.component('new-component', {
template: `
Branch: branch034. data
new Vue
的组件中,data
必须为函数,函数返回值必须是一个对象,作为组件的最终 data
data
——和React
的state
一样,每一个组件都有其自身私有的状态,这里data
也一样,它是私有的,只能在当前的组件中使用。4.1 example02
4.1.1 example02-1
html模板
中调用data
,还有在js
当中访问data
。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<p>app组件:{{a}}p>
<new-component>new-component>
div>
<script src="./js/vue.js">script>
<script>
let newComponent = Vue.component('new-component', {
template: `
a
这个属性或者方法是没有定义的。为啥出这种问题呢?js函数
是不一样的,内部函数可访问函数外部的变量,在这里是行不通的。a
,并不会向下传递。( 如何向下传递一会再说明。)
Branch: branch03html模板
中调用data
,还有在js
当中访问data
)4.1.2 example02-2
new-component
组件内部定义一个b
,看是否能在其内部的template
访问。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<p>app组件:{{a}}p>
<new-component>new-component>
div>
<script src="./js/vue.js">script>
<script>
let newComponent = Vue.component('new-component', {
data: {
b: 100
},
template: `
data
选项必须是一个函数,并且必须返回一个per-instance
值,这个意思实际是 =>data
必须是一个函数,且该函数必须返回一个对象,该对象就是组件最终的 data
值。
Branch: branch034.1.3 example02-3
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<p>app组件:{{a}}p>
<new-component>new-component>
div>
<script src="./js/vue.js">script>
<script>
let newComponent = Vue.component('new-component', {
data() {
return {
b: 100
}
},
template: `
Branch: branch03vue
为什么需要这样做呢?为什么可复用组件的data
必须是一个返回对象的函数?vue
中的html模板
最终会被解析成虚拟dom
,再根据虚拟dom
解析成html
,碰上普通的html标签
直接渲染即可,那碰上自定义组件呢?4.1.4 example02-4
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<script>
let Vue = {};
class VueComponent {
constructor(options) {
this._opts = options;
this._data = this._opts.data;
}
}
let newComponent1 = new VueComponent({
name: 'new-component1',
data: {
a: 1
}
});
let newComponent2 = new VueComponent({
name: 'new-component2',
data: {
a: 1
}
});
// newComponent1._data 与 newComponent2._data 是否是同一个对象?
console.log(newComponent1, newComponent2);
newComponent2._data.a = 100;
console.log(newComponent1, newComponent2);
script>
body>
html>
Branch: branch034.1.5 example02-5
component
工厂函数,对new
的行为进行了包装
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<script>
let Vue = {};
class VueComponent {
constructor(options) {
this._opts = options;
this._data = this._opts.data;
}
}
Vue.component = function(name, options) {
return new VueComponent({
name,
...options
});
};
let newComponent1 = Vue.component('new-component1', {
data: {
a: 1
}
});
console.log( newComponent1 )
script>
body>
html>
Vue.component
返回一个函数,这里我们返回的是一个对象
,但这存在着一个复用的问题 => Vue
要复用newComponent1
,页面要使用两次,得调用两次
、
。Vue.component
出来一个。Vue.component
赋给一个新对象,最后在写成标签的形式,这样本质上就没得到复用了。Vue
本就为了便捷,这样使用又使代码非常冗余了。
Branch: branch034.1.6 example02-6
Vue.component
返回一个函数,这个函数返回一个新的对象。
在页面中只有使用一次,即html模板
解析过程中,它就会被直接解析成函数调用,每次返回出不同的组件对象,这就达到了复用性。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<script>
let Vue = {};
class VueComponent {
constructor(options) {
this._opts = options;
this._data = this._opts.data;
}
}
// component 工厂函数,对new的行为进行了包装
Vue.component = function(name, options) {
return function() {
return new VueComponent({
name,
...options
});
}
};
let newComponent1 = Vue.component('new-component1', {
data() {
return {
a: 1
}
}
});
let new1 = newComponent1();
let new2 = newComponent1();
console.log(new1, new2);
new1._data.a = 100;
console.log(new1, new2);
script>
body>
html>
data
数据,发现除了复用组件,其组件内部的选项也被复用了,从而复用了option
配置项,没必要构建一堆组件,来一组创建对象。option
配置项(配置项中的data
对象传的地址,始终是同一个对象,因为js
没有指针和引用的概念,小迪就建议大家理解为浅拷贝即可,如果会c++、java
这类语言,想成指针,始终指向一个对象)data
的,这样问题就大了,容易出现一堆问题。
Branch: branch034.1.7 example02-7
Vue.component
创建的组件,data
不能是对象,它必须是一个函数,在内部调用data
函数,它返回一个对象,然后把函数返回的对象返回给data
。如果每次通过标签调用该函数,data
函数都会返回一个新对象,这样每个组件的data
是不同的对象了。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<script>
let Vue = {};
class VueComponent {
constructor(options) {
this._opts = options;
this._data = this._opts.data;
}
}
// component 工厂函数,对new的行为进行了包装
Vue.component = function(name, options) {
return function() {
return new VueComponent({
name,
...options,
data: options.data() // options.data()函数返回的对象赋值给data,成为一个新对象
});
}
};
let newComponent1 = Vue.component('new-component1', {
data() {
return {
a: 1
}
}
});
let new1 = newComponent1();
let new2 = newComponent1();
console.log(new1, new2);
new1._data.a = 100;
console.log(new1, new2);
script>
body>
html>
data
就不共享,它们都是独立的对象了。这就是为啥Vue
中的Vue.component
方法配置项的data
属性必须是一个函数了,目的就是复用该组件的时候,能够产生不一样的data
数据。根组件中为啥不需要data
是函数呢?因为一个应用中只有一个根组件,不会产生复用问题,不会有多个根组件,因此就不会产生数据不独立的问题了。
Branch: branch035. props
data
中,通过外部传入的数据,则通过 props<
选项接收
props
值为一个表达式,则必须使用 v-bind
data
和 props
数据都可以通过组件实例进行直接访问data
中的 key
与 props
中的 key
不能冲突5.1 example03
5.1.1 example03-1
html模板
中:r="n1"
=> 把父级的n1属性
值传递给子级props
下的r
属性。props: ['r']
=> 当前这个组件接受的props
参数:r
=> 等同于当前组件下有r属性
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-circle :r="n1">me-circle>
<hr />
<me-circle :r="n2">me-circle>
div>
<script src="./js/vue.js">script>
<script>
let meCircle = Vue.component('me-circle', {
props: ['r'],
template: `
Branch: branch035.1.2 example03-2
props
和 data
中的数据访问一致,所以如果命名一致肯定会报错,因此命名的时候千万不要相同而冲突了。 let meCircle = Vue.component('me-circle', {
props: ['r'],
data() {
return {
r: 1
}
},
template: `
Branch: branch03data
属性命名不要仅以**_
开头,容易与vue对象的属性冲突** data() {
return {
me_r: 1
}
}
6. 组件通信
props
传入的数据props
传入数据到子组件内部,但是子组件内部不要修改外部传入的 props
,vue
提供了一种事件机制通知父级更新,父级中使用子组件的时候监听对应的事件绑定对应的处理函数即可。6.1 example04
6.1.1 example04-1
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list v-for="item of items" :key="item.id" :data="item">me-list>
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['data'],
template: `
Branch: branch036.1.2 example04-2
+、-
改变商品数量,从而设置一个总数量。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list v-for="item of items" :key="item.id" :data="item">me-list>
<hr>
总数:{{count}}
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['data'],
template: `
Branch: branch036.1.2.1 note
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
参数
描述
function(total,currentValue, index,arr)
total 必需。初始值, 或者计算结束后的返回值。 currentValue 必需。当前元素 currentIndex 可选。当前元素的索引 arr 可选。当前元素所属的数组对象。
initialValue
可选。传递给函数的初始值
6.1.3 example04-3
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list v-for="item of items" :key="item.id" :data="item">me-list>
<hr>
总数:{{count}}
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['data'],
template: `
Branch: branch036.1.3.1 小结
item
)对象。item
是一个对象,对象传递是传址
,这里实际是子级修改了父级的item对象
,父级监听到自己的数据变化了,肯定会重新渲染视图的。6.1.4 example04-4
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list
v-for="item of items"
:key="item.id"
:data="item"
:quantity="item.quantity"
>me-list>
<hr>
总数:{{count}}
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['data', 'quantity'],
template: `
Branch: branch036.1.4.1 重要提炼与总结
vue
是禁止修改传进去的数据的,vue
是不建议直接修改(建议只能读)props
传入的数据(但是不代表不能改 => 不能修改的原因与React
是一样的,这里就不再详细赘述了),因为传入的数据不仅仅是当前这个组件使用,可能其它组件也在用这个数据,为了保证数据操作的安全性。increment
方法中调用this.data = {...}
(直接赋值给对象),它是可以监控到并且报错,但是vue
中的坑
则是修改对象底下某个属性,它是监测不到的,因此不会报错。因此遵循vue
规范,不要这么去做。=>
通知父级自己去修改,即通知数据持有人去修改。
vue
的事件通知机制,类似于React
的回调函数,其本质上是差不多的。6.2 $emit()
vue
为每个组件对象提供了一个内置方法 $emit
,它等同于自定义事件中的 new Event
,trigger
等this.$emit('自定义事件名称', 事件数据)
event
@事件名称
来注册绑定事件函数6.2.1 example05
6.2.1.1 example05-1
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list
v-for="item of items"
:key="item.id"
:data="item"
:quantity="item.quantity"
:fn="edit"
>me-list>
<hr>
总数:{{count}}
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['data', 'quantity', 'fn'],
template: `
Branch: branch036.2.1.2 example05-2
methods: {
// 加数量
increment() {
console.log(this.fn);
}
}
vue
过滤了。React
是将一个函数作为参数传进去,然后当想增加数量的时候,直接调用父级传进来的函数。vue
中,它禁止你传函数了。
Branch: branch036.2.1.2.1 紧急纠错—纠正上述说法
computed
里了,实际这里的edit
并不是函数,还是属性值,这里只是edit
属性的get
方法而已,所以我们没法把属性当做函数传递,你把它当做函数获取,肯定是找不到这个函数了。Vue
是可以通过回调函数进行子级修改父级data的!!!!!!
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list
v-for="item of items"
:key="item.id"
:data="item"
:quantity="item.quantity"
:fn="edit"
>me-list>
<hr>
总数:{{count}}
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['data', 'quantity', 'fn'],
template: `
额外观点
=> 小迪还是鼓励大家多犯错的,小迪就因为这样的低级错误,既复习了computed,又更深挖了vue回调机制。
Branch: branch036.2.1.3 example05-3
原生js
中讲过,除了用回调,还可以用事件机制解决。vue
中每个组件下都有一个属性$emit()
,它等同于自定义事件中的 new Event
,trigger
等,它的第一个参数this.$emit('abc'
,定义事件名称,然后在父级用v-on:abc="edit"
监听事件,并设置事件处理函数,我们在点击+
的时候发送事件即可,就可以触发对应事件处理函数了。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list
v-for="item of items"
:key="item.id"
:data="item"
:quantity="item.quantity"
v-on:abc="edit"
>me-list>
<hr>
总数:{{count}}
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['data', 'quantity', 'fn'],
template: `
Branch: branch036.2.1.3.1 note
React
解决props
这种父子传递问题,用的是回调函数,而Vue
用的是事件机制,它封装了自定义事件的方法,我们直接去使用即可。6.3 小结
data
的方法:
7. 组件双绑的实现
props
,但是,有的时候确实希望组件内部状态变化的时候改变 props
,我们可以通过子组件触发事件,父级监听事件来达到这个目的,不过过程会比较繁琐,vue
提供了一些操作来简化这个过程。7.1 v-model
v-model
是 vue
提供的一个用于实现数据双向绑定的指令,用来简化 props 到 data
,data 到 props
的操作流程。7.1.1 model 选项
prop
指定要绑定的属性,默认是 value
event
指定要绑定触发的事件,默认是 input
事件7.1.2 example06
v-mode
,如果子级修改数据不需要事件机制,而是子级直接修改。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list
v-for="item of items"
:key="item.id"
:name="item.name"
v-model="item.quantity"
>
me-list>
<hr>
总数:{{count}}
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['name', 'quantity'],
// v-model的值 赋值给 prop指定的属性值
// event abc 当你触发abc事件,第二个参数值将自动去更新在 v-model当中所绑定的值
model: {
prop: 'quantity',
event: 'abc'
},
template: `
Branch: branch037.1.2.1 深度解析
v-model
伪代码:quantity = 'item.quantity'
@abc = 'function(v){item.quantity+v}'
v-model
如何识别呢?外界传进去的值,到底赋给了内部哪个属性呢?model
配置项中的prop
属性指定内部对应的props
。event: ‘abc’
=> 当触发的事件是abc
的话,其第二个参数(this.$emit('abc', this.quantity + 1)
)的值将会自动更新v-model
中所绑定的值(v-model="item.quantity"
)v-model="item.quantity"
赋值给了内部的谁去用了?内部触发什么事件可以修改外部绑定的值?这之间的两种关系,是通过model配置项
告知v-model
的,这样就可以同步更新数据了。v-on:abc="edit"
代码,也不需要在父级写一个修改属性值的回调函数了,这里v-model
的内部把这些工作都做了,只是封装起来了而已。7.1.3 注意事项
model
中的prop
和event
,prop
默认传递给value
,event
默认是input
事件,针对不同组件即v-model绑定到不同类型的组件上面,如input
组件时遵循此规则,如checkbox、radio、select、prop
默认传递给checked
或selected
属性,event
默认是change
事件。7.1.4 极为重要的注意事项
v-model
,原因在于如果封装一个组件,不告诉怎么使用,外面的人是根本不知道它的实现细节的(不看源码),不知道v-model
的隐藏细节,可能导致一些奇怪的问题。所以使用大量v-model
,测试起来会非常麻烦,出了问题很难定位。不过用事件机制解决,又麻烦,Vue
提供了两者的优点而集成的.sync
,我推荐使用它。7.2 .sync
v-model
来进行双向绑定,会给状态维护带来一定的问题,因为修改比较隐蔽,同时只能处理一个 prop
的绑定,我们还可以通过另外一种方式来达到这个目的。.sync
=> :quantity.sync="item.quantity"
v-model
的概念是一样的,子级内部quantity
属性发生变化的时候,能够同步到父级的item.quantity
上来。7.2.1 update:[prop]
update
加上 prop
名称 的格式this.$emit('update:quantity', this.quantity + 1);
=> 同步到父级的item.quantity
时,触发该事件,更新父级的data
。7.2.2 example07
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<me-list
v-for="item of items"
:key="item.id"
:name="item.name"
:quantity.sync="item.quantity"
>
me-list>
<hr>
总数:{{count}}
div>
<script src="./js/vue.js">script>
<script>
let meList = Vue.component('me-list', {
props: ['name', 'quantity'],
template: `
Branch: branch037.2.3 深入探究
:quantity.sync="item.quantity"
意思是把"item.quantity"
传递给内部props
的quantity
(父传子),同时内部quantity
发生变化的时候,可把值同步到"item.quantity"
。update:quantity
(要修改的属性),quantity
就是需要修改的属性。.sync
代表当更新子级的quantity
,发现了此修饰符触发事件,就主动修改父级的item.quantity
(将this.quantity + 1
赋值过去)v-model
却明显看不出来(除非查看model
属性下的prop
和event
配置项)。.sync
结合了v-model
和v-model
两者的优点,而摒弃了它们的缺点。它能够方便子级同步更新父级的data
,同时能够直观地感受到内部是什么属性,与外界什么属性进行绑定的。8. 小结&杂谈
api
(给我们安排得明明白白的,并不完全是一件好事),因为很多时候,需要靠自己去推测(写着写着就忘了)。Vue
用起来很方便,内部细节是不知道的,都需要自己去推测。js
的用法去推测,又不是那么不一致,即不能根据常理去推测,内部细节不去看源码根本理不清楚。vue
虽然用起来爽,但内部封装的api
实在是太多太多了,过于繁琐,有的时候写的过程中出了奇怪的问题,推测根本解决不了问题,就得爬源码。而且规则很多,也很容易掉坑了出不来,因此对于有js
或者其他语言开发的底蕴,小迪还是更喜欢React
,尽管没有vue
用起来爽,但是坑往往被挖的很深,并且需要记忆的东西很多,必须强行记忆。
(后续待补充)