一、vue的模板template:
模板template的组成:vue的模板都是基于HTML字符串、vue特性、表达式/属性/指令。
vue模板到真实dom的过程:
1、vue中的模板html需要经过vue的html解析器转成AST树;
2、AST树转虚拟dom;
3、虚拟dom解析成真实dom,最后挂载到html;
二、data 函数
1、data函数返回的对象,变成响应式的过程:
1、Vue在创建实例的过程中调用data函数返回数据对象;
2、给对象通过响应式包装后得到代理对象;
3、把代理对象存储在实例的$data上 ;
4、实例可以直接越过$data,通过get set 存/取值器,访问属性,。
const App = {
data() {
return {
qige: 'hello world'
}
},
template: `
<div >
{{key}}
</div>
`
}
//Vue3
const { createApp } = Vue
var vm=createApp(App).mount('#app');
console.log(vm);
//Vue2
var vm =new Vue({
render: h => h(App)
}).$mount('#app')
console.log(vm);
vue3
vue2
2、data为什么必须是一个函数?
不做深度代理实现代码,:
function MyVue(options) {
this.$data = options.data();
var _this = this;
for (var key in this.$data) {
(function(key) {
Object.defineProperty(_this, key, {
get: function() {
return _this.$data[key]
},
set: function(newValue) {
_this.$data[key] = newValue
}
})
})(key);
}
}
function data() {
return {
a: 1,
b: 2
}
}
var myVm = new MyVue({data});
console.log(myVm);
console.log("a::",myVm.a);
console.log("b::",,myVm.b);
myVm.$data.a=10
myVm.b=15
console.log("a::",myVm.a);
console.log("b::",,myVm.b);
代码逻辑说明:
1、MyVue构造函数声明一个$data保存data函数返回值;
2、遍历data**里面属性的操作;
3、之所以能够通过myVm.a或者myVm.data对象;
//构造函数里面的this.$data = options.data();改为以下
this.$data = options.data;
//data改为一个对象
var data = {
qige: 1,
wuzai: 2
}
var myVm1 = new MyVue({ data });
var myVm2 = new MyVue({ data });
myVm2.qige = 15
console.log("myVm1::", myVm1);
console.log("myVm2::", myVm2);
那data为什么必须是一个函数?
通过上图看出;myVm2.qige = 15把qige属性的值改了,myVm1里面的属性qige的值也该了,因为他们的$data指向的是同一个引用;
data是一个函数可以保证每一个实例的$data都指向不同的引用。不会导致组件实例的数据会被其他组件实例修改。
当然data如果是一个对象,也可以通过深度克隆保证组件实例data的唯一性。但是从性能的角度看,深度克隆需要通过递归,data定义为函数直接。
三、methods对象
目的:向组件实例添加方法;
1.Vue创建实例时,遍历methods对象,给当前组件实例添加methods对象的方法,并且绑定方法内部的this指向为当前实例;
2、保证在事件监听时,回调始终指向当前组件实例;
3、方法要避免使用箭头函数,箭头函数会阻止Vue正确绑定组件实例this。
function MyVue(options) {
this.$data = options.data();
this._methods = options.methods;
function _init(){
this.initData();
this.initMethods();
}
MyVue.prototype.initData = function() {
var _this = this;
for (var key in _this.$data) {
(function(key) {
Object.defineProperty(_this, key, {
get: function() {
return _this.$data[key]
},
set: function(newValue) {
_this.$data[key] = newValue
}
})
})(key);
}
}
MyVue.prototype.initMethods = function() {
for (var key in options.methods) {
this[key] = options.methods[key].bind(this);
}
}
_init.call(this)
}
const App = {
data() {
return {
qige: 'hello world'
}
},
template: `
<div >
{{qige}}
</div>
`,
methods: {
test(a) {
console.log(a);
}
}
}
var myVm1 = new MyVue(App);
myVm1.test(myVm1.qige)
四:v-if、v-show、事件解析
const App = {
el: "#app",
data() {
return {
qige: 'hello world',
wuzai: 'hello wuzai',
myif: false,
myshow: false
}
},
beforeCreate(){
console.log('beforeCreate');
},
created(){
console.log('created');
},
beforeMounte(){
console.log('beforeMounte');
},
mounted(){
console.log('mounted');
},
template: `
<p v-if="myif">{{qige}}</p>
<p v-show="myshow">{{wuzai}}</p>
<button @click='showQige'>显示七鸽</button>
<button @click='showWuzai'>显示舞载</button>
`,
methods: {
showQige() {
this.myif = !this.myif;
},
showWuzai() {
this.myshow = !this.myshow;
this.print()
},
print() {
console.log('print');
}
}
}
var myVue = (function() {
function MyVue(options) {
var recyles={
beforeCreate:options.beforeCreate.bind(this),
created:options.created.bind(this),
beforeMount:options.beforeMounte.bind(this),
mounted:options.mounted.bind(this),
}
recyles.beforeCreate();
this.$el = document.querySelector(options.el);
this.$data = options.data();
recyles.created()
this._init(this, options.template, options.methods,recyles);
}
MyVue.prototype._init = function(vm, template, methods,recyles) {
var showPool = new Map(),
eventPool = new Map(),
container = document.createElement('div');
container.innerHTML = template;
initData(vm, showPool);
initMethods(vm, methods);
initPool(container, methods, showPool, eventPool);
bindEvent(vm, eventPool);
render(vm, showPool, container,recyles);
}
function initMethods(vm, methods) {
for (var key in methods) {
vm[key] = methods[key].bind(vm);
}
}
function initData(vm, showPool) {
var _data = vm.$data;
for (var key in _data) {
(function(key) {
Object.defineProperty(vm, key, {
get: function() {
return _data[key]
},
set: function(newValue) {
_data[key] = newValue
updata(vm, key, showPool)
}
})
})(key);
}
}
function initPool(container, methods, showPool, eventPool) {
var allNodes = container.getElementsByTagName('*'),
dom = null;
for (var i = 0; i < allNodes.length; i++) {
dom = allNodes[i]
var vIfData = dom.getAttribute('v-if'),
vShowData = dom.getAttribute('v-show'),
vEvent = dom.getAttribute('@click');
if (vIfData) {
showPool.set(dom, { type: 'if', prop: vIfData })
dom.removeAttribute('v-if')
} else if (vShowData) {
showPool.set(dom, { type: 'show', prop: vShowData })
dom.removeAttribute('v-show')
}
if (vEvent) {
eventPool.set(dom, methods[vEvent])
dom.removeAttribute('@click')
}
}
}
function bindEvent(vm, eventPool) {
for (let [dom, handler] of eventPool) {
vm[handler.name] = handler
dom.addEventListener('click', vm[handler.name].bind(vm), false)
}
}
function render(vm, showPool, container,recyles) {
recyles.beforeMount()
var _data = vm.$data,
_el = vm.$el,
type = undefined;
for (let [dom, info] of showPool) {
type = info.type;
if (type === 'if') {
info.comment = document.createComment(['v-if']);
!_data[info.prop] && (dom.parentNode.replaceChild(info.comment, dom))
} else if (type === 'show') {
!_data[info.prop] && (dom.style.display = 'none')
}
}
_el.appendChild(container)
recyles.mounted()
}
function updata(vm, key, showPool) {
var _data = vm.$data,
type = undefined;
for (let [dom, info] of showPool) {
if (info.prop === key) {
type = info.type;
if (type === 'if') {
_data[info.prop] ?
info.comment.parentNode.replaceChild(dom, info.comment) :
dom.parentNode.replaceChild(info.comment, dom)
} else if (type === 'show') {
_data[info.prop] ? dom.removeAttribute('style') : dom.style.display = 'none'
}
}
}
}
return MyVue;
})();
var vm = new myVue(App);
console.log(vm);