Open dave-wind opened 1 year ago
// 自定义 子组件 绑定 v-model 实际上是语法糖; 解析为 render 函数里如下:
function render() {
with(this) {
return _c('Demo', {
attrs: {
"type": "input"
},
model: { // model 对象
value: (msg), // 传递 value 属性
callback: function ($$v) { // 回调函数
msg = $$v // 外部不写回调函数 都会修改 值
},
expression: "msg"
}
})
}
}
源码里 在create-component.js 里 transformModel 如下
// 他会转化model
function transformModel(options,data) {
const prop = (options.model && options.model.prop) || 'value'; // 默认model对象里 value属性
const event = (options.model && options.model.event) || 'input'; // callback event 默认为 input 事件
}
<template>
<div>
<div @click="handleClick">{{ isVal }}</div>
</div>
</template>
<script>
export default {
name: "Demo",
props: {
str: Number,
},
data() {
return {
isClick: false,
};
},
model: {
prop: "number", // 这里改写 value 为 number
event: "update-number",
},
computed: {
isVal() {
return this.number >= 100 ? ">= 100" : "<100";
},
},
mounted() {},
methods: {
handleClick() {
this.isClick = !this.isClick;
const val = this.isClick ? this.number - 1 : this.number + 1;
this.$emit("update-number", val);
},
},
};
</script>
// 父组件
<template>
<div id="app">
<div>函数过滤展示: {{ showStr() }}</div>
<Demo v-model="str" @updateNumber="changeNumber" />
</div>
</template>
<script>
import Demo from "./demo";
export default {
name: "App",
components: {
Demo,
},
data() {
return {
list: [],
str: 100,
};
},
methods: {
showStr() {
return this.str / 2;
},
changeNumber(val) { //父组件 不去定义 该函数 都会修改 str
console.log("父组件1---", val);
this.str = val;
},
},
};
</script>
实际上 就是 发布订阅模式
1. <Demo v-model="str" /> 会被转化为 ast --> 通过 createComponent 生成vnode
2. 在 createComponent方法中 会把 data( ast 生成的数据) 上的 on 赋值给 listeners :
var listeners = data.on;
3. 生成 虚拟节点 调用 new VNode():
var vnode = new VNode(
("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')),
data, undefined, undefined, undefined, context,
{ Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children },
asyncFactory
);
4.初始化组件的时候 在 initInternalComponent 方法中:
const opts = vm.$options = Object.create(vm.constructor.options); // 继承于父类构造函数 options
opts_parentListener = options.listeners // 相当于 组件初始化传入的所有数据
实际上 就是 挂载 在 实例上的 $options
5. 初始化事件: 在 initEvents 方法中:
function initEvents(vm) {
vm._events = Object.create(null);
// 实际上就是为了 可以拿到所有的 listeners
const listeners = vm.$options._parentListeners;
if(listeners) {
// 判断 存在 就进行注册
updateComponentListeners(vm,listeners);
}
}
6.所以最终 就是 把自定义组件的事件 绑定到$on 方法上
Vue.prototype.$on = function(event: string | Array<string>, fn ) {
var vm = this;
if (Array.isArray(event)) {
for (var i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn);
}
} else {
// 一个事件名 对应 多个 fn 要注意
(vm._events[event] || (vm._events[event] = [])).push(fn);
}
return vm
}
Vue.prototype.$emit= function (event){
var vm = this;
var cbs = vm._events[event]; // 先取到对应的 fns 数组
if (cbs) {
for (var i = 0, l = cbs.length; i < l; i++) {
cbs[i].apply(vm, arguments). //简单理解 就是 循环执行 fn
}
}
}
v-if; v-for;v-model 实现原理