1.构建自定义Vue实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./testVue.js"></script>
</head>
<body>
<div id="APP">
<p>{{name}}</p>
<input type="text" v-model='name'>
</div>
<script>
let vue = new Vue({
// 指出需要控制的区域
el: '#APP',
// 传递数据
data: {
name: "山竹",
age: 18
}
})
// console.log(vue.$el);
// console.log(vue.$data);
</script>
</body>
</html>
// 模拟创建vue实例(类)
class Vue {
// 构造器,接收一个参数,参数为对象
constructor(value) {
// 判断el是否为一个节点
if (this.isElement(value.el)) {
this.$el = value.el
} else {
// 如果没有则根据传入的找
this.$el = document.querySelector(value.el)
}
this.$data = value.data
// 根据传入的位置和数据渲染
// 先判断是否el是否存在,否则不渲染
if (this.$el) {
// 传入该实例
new Compier(this)
}
}
isElement(node) {
// console.log(node.nodeType === 1);//false
return node.nodeType === 1
}
}
class Compier {
constructor(vm) {
// 保存vue实例
this.vm = vm
}
}
效果
2.提取元素到内存
接上编写
// 模拟创建vue实例(类)
class Vue {
// 构造器,接收一个参数,参数为对象
constructor(value) {
// 判断el是否为一个节点
if (this.isElement(value.el)) {
this.$el = value.el
} else {
// 如果没有则根据传入的找
this.$el = document.querySelector(value.el)
}
this.$data = value.data
// 根据传入的位置和数据渲染
// 先判断是否el是否存在,否则不渲染
if (this.$el) {
// 传入该实例
new Compier(this)
}
}
isElement(node) {
// console.log(node.nodeType === 1);//false
return node.nodeType === 1
}
}
class Compier {
constructor(vm) {
// 保存vue实例
this.vm = vm
// 1.将页面元素提取到碎片文档
let fragment = this.node2fragment(this.vm.$el)
console.log(fragment);
// 2.利用指定的数据编译内存中的元素
// 3.将编译好的内存重新渲染到网页上
}
node2fragment(app) {
// 1.创建空的文档
let fragment = document.createDocumentFragment()
//2.遍历循环去到每一个元素
let node = app.firstChild
while (node) {
// 判断是否还有元素
// 注意点:只要元素添加到文档碎片对象中,name这个元素就会自动从网页上消失
fragment.appendChild(node)
node = app.firstChild
}
//3.返回储存了所有元素的文档碎片对象
return fragment
}
}
效果
3.查找指令和模板
// 模拟创建vue实例(类)
class Vue {
// 构造器,接收一个参数,参数为对象
constructor(value) {
// 判断el是否为一个节点
if (this.isElement(value.el)) {
this.$el = value.el
} else {
// 如果没有则根据传入的找
this.$el = document.querySelector(value.el)
}
this.$data = value.data
// 根据传入的位置和数据渲染
// 先判断是否el是否存在,否则不渲染
if (this.$el) {
// 传入该实例
new Compier(this)
}
}
// 判断是否为元素节点,元素节点为1,属性节点为2
isElement(node) {
// console.log(node.nodeType === 1);//false
return node.nodeType === 1
}
}
class Compier {
constructor(vm) {
// 保存vue实例
this.vm = vm
// 1.将页面元素提取到碎片文档
let fragment = this.node2fragment(this.vm.$el)
// 2.利用指定的数据编译内存中的元素
this.buildTemplate(fragment)
// 3.将编译好的内存重新渲染到网页上
}
node2fragment(app) {
// 1.创建空的文档
let fragment = document.createDocumentFragment()
//2.遍历循环去到每一个元素
let node = app.firstChild
while (node) {
// 判断是否还有元素
// 注意点:只要元素添加到文档碎片对象中,name这个元素就会自动从网页上消失
fragment.appendChild(node)
node = app.firstChild
}
//3.返回储存了所有元素的文档碎片对象
return fragment
}
// =========================================以下为新增=============================
buildTemplate(fragment) {
// 从元素获取所有节点,伪数组转为数组
let nodeList = [...fragment.childNodes]
// console.log(nodeList);//[text, p, text, input, text]
// 循环判断当前的节点是一个元素还是一个文本
nodeList.forEach(node => {
if (this.vm.isElement(node)) {
//是一个元素
this.buildElement(node)
//处理子元素,递归
this.buildTemplate(node)
} else {
//不是一个元素
this.buildText(node)
}
})
}
// 元素处理
buildElement(node) {
// attributes 属性返回指定节点的属性集合
let attrs = [...node.attributes]
attrs.forEach(attr => {
// console.log(attr);//获取到属性
let { name, value } = attr
// console.log(name,value);//单独拿到类型与类型的取值
if (name.startsWith('v-')) {
// startsWith() 方法用于检测字符串是否以指定的子字符串开始。
// console.log('是v-', name,value);//是v- v-model name
}
})
}
//文本处理
buildText(node) {
// textContent 属性设置或者返回指定节点的文本内容。
let content = node.textContent
let reg=/\{\{.+?\}\}/gi
if(reg.test(content)){
console.log('是{{}}',content);//是{{}} {{name}}
}
}
}
4.编译指令数据
HTML继上添加
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./testVue.js"></script>
</head>
<body>
<div id="APP">
<p>{{name}}</p>
<input type="text" v-model='name'>
<!-- ====================新增================== -->
<input type="text" v-model='time.h'>
<input type="text" v-model='time.m'>
<input type="text" v-model='time.s'>
<div v-html='html'>我是div</div>
<div v-text='text'>我是div</div>
</div>
<script>
let vue = new Vue({
// 指出需要控制的区域
el: '#APP',
// 传递数据
data: {
name: "山竹",
age: 18,
// ======================新增===============
time:{
h:11,
m:22,
s:33
},
html:`<div>我是HTML</div>`,
text:`<div>我是text</div>`
}
})
// console.log(vue.$el);
// console.log(vue.$data);
</script>
</body>
</html>
js继上添加
// ==============================创建工具类对应不同指令======================
let CompilerUtil = {
getValue(vm, value) {
//time.h-->[time,h],利用reduce遍历逐层取
//reduce接收的第一个参数为上一次调用回调返回的值,或者是提供的初始值
// 第二个参数为数组中当前被处理的元素
// vm.$data作为data第一次调用初始值
return value.split('.').reduce((data, currentKey) => {
//第一次执行:data=$data,currentKey=time
// 第二次执行:data=time,currentKey=h
return data[currentKey]
}, vm.$data)
},
model: function (node, value, vm) {
// console.log(node, value, vm);
// v-model作用在input上
// 根据被替换内容,获取对应的数据
// node.value=vm.$data[value];
//遇到复杂类型情况下vm.$data[time.h]-->vm.$data[time]-->time[h]
let val = this.getValue(vm, value)
node.value = val
},
html: function (node, value, vm) {
let val = this.getValue(vm, value)
node.innerHTML = val
},
text: function (node, value, vm) {
let val = this.getValue(vm, value)
node.innerText = val
}
}
// 模拟创建vue实例(类)
class Vue {
// 构造器,接收一个参数,参数为对象
constructor(value) {
// 判断el是否为一个节点
if (this.isElement(value.el)) {
this.$el = value.el
} else {
// 如果没有则根据传入的找
this.$el = document.querySelector(value.el)
}
this.$data = value.data
// 根据传入的位置和数据渲染
// 先判断是否el是否存在,否则不渲染
if (this.$el) {
// 传入该实例
new Compier(this)
}
}
// 判断是否为元素节点,元素节点为1,属性节点为2
isElement(node) {
// console.log(node.nodeType === 1);//false
return node.nodeType === 1
}
}
class Compier {
constructor(vm) {
// 保存vue实例
this.vm = vm
// 1.将页面元素提取到碎片文档
let fragment = this.node2fragment(this.vm.$el)
// 2.利用指定的数据编译内存中的元素
this.buildTemplate(fragment)
// ========================新增============================
// 3.将编译好的内存重新渲染到网页上
this.vm.$el.appendChild(fragment)
}
node2fragment(app) {
// 1.创建空的文档
let fragment = document.createDocumentFragment()
//2.遍历循环去到每一个元素
let node = app.firstChild
while (node) {
// 判断是否还有元素
// 注意点:只要元素添加到文档碎片对象中,name这个元素就会自动从网页上消失
fragment.appendChild(node)
node = app.firstChild
}
//3.返回储存了所有元素的文档碎片对象
return fragment
}
buildTemplate(fragment) {
// 从元素获取所有节点,伪数组转为数组
let nodeList = [...fragment.childNodes]
// console.log(nodeList);//[text, p, text, input, text]
// 循环判断当前的节点是一个元素还是一个文本
nodeList.forEach(node => {
if (this.vm.isElement(node)) {
//是一个元素
this.buildElement(node)
//处理子元素,递归
this.buildTemplate(node)
} else {
//不是一个元素
this.buildText(node)
}
})
}
// 元素处理
buildElement(node) {
// attributes 属性返回指定节点的属性集合
let attrs = [...node.attributes]
attrs.forEach(attr => {
// console.log(attr);//获取到属性
let { name, value } = attr
// console.log(name,value);//单独拿到类型与类型的取值
if (name.startsWith('v-')) {
// v-开头的有很多,比如v-model/v-html/v-if...
// startsWith() 方法用于检测字符串是否以指定的子字符串开始。
// console.log('是v-', name,value);//是v- v-model name
// =================更新部分=====================
// 切割并解构,不需要v-
let [, directive] = name.split('-')//v,model
// console.log(directive);//model
// 找到对应的工具类执行对应的方法
// node:修改的节点,value:修改的值,this.vm新值
CompilerUtil[directive](node, value, this.vm)
}
})
}
//文本处理
buildText(node) {
// textContent 属性设置或者返回指定节点的文本内容。
let content = node.textContent
let reg = /\{\{.+?\}\}/gi
if (reg.test(content)) {
// console.log('是{{}}',content);//是{{}} {{name}}
}
}
}
效果
5.编译模板数据
let CompilerUtil = {
getValue(vm, value) {
//time.h-->[time,h],利用reduce遍历逐层取
//reduce接收的第一个参数为上一次调用回调返回的值,或者是提供的初始值
// 第二个参数为数组中当前被处理的元素
// vm.$data作为data第一次调用初始值
return value.split('.').reduce((data, currentKey) => {
//第一次执行:data=$data,currentKey=time
// 第二次执行:data=time,currentKey=h
return data[currentKey.trim()]
}, vm.$data)
},
// ================新增部分==========
getContent(vm, value) {
let reg = /\{\{(.+?)\}\}/gi
let val = value.replace(reg, (...args) => {
return this.getValue(vm, args[1])
})
return val
},
model: function (node, value, vm) {
// console.log(node, value, vm);
// v-model作用在input上
// 根据被替换内容,获取对应的数据
// node.value=vm.$data[value];
//遇到复杂类型情况下vm.$data[time.h]-->vm.$data[time]-->time[h]
let val = this.getValue(vm, value)
node.value = val
},
html: function (node, value, vm) {
let val = this.getValue(vm, value)
node.innerHTML = val
},
text: function (node, value, vm) {
let val = this.getValue(vm, value)
node.innerText = val
},
// ====================更新部分===============
content: function (node, value, vm) {
// console.log(value);//{{name}}-->取出name-->将$data[name]的数据赋值
let val = this.getContent(vm, value)
node.textContent = val
}
}
// 模拟创建vue实例(类)
class Vue {
// 构造器,接收一个参数,参数为对象
constructor(value) {
// 判断el是否为一个节点
if (this.isElement(value.el)) {
this.$el = value.el
} else {
// 如果没有则根据传入的找
this.$el = document.querySelector(value.el)
}
this.$data = value.data
// 根据传入的位置和数据渲染
// 先判断是否el是否存在,否则不渲染
if (this.$el) {
// 传入该实例
new Compier(this)
}
}
// 判断是否为元素节点,元素节点为1,属性节点为2
isElement(node) {
// console.log(node.nodeType === 1);//false
return node.nodeType === 1
}
}
class Compier {
constructor(vm) {
// 保存vue实例
this.vm = vm
// 1.将页面元素提取到碎片文档
let fragment = this.node2fragment(this.vm.$el)
// 2.利用指定的数据编译内存中的元素
this.buildTemplate(fragment)
// 3.将编译好的内存重新渲染到网页上
this.vm.$el.appendChild(fragment)
}
node2fragment(app) {
// 1.创建空的文档
let fragment = document.createDocumentFragment()
//2.遍历循环去到每一个元素
let node = app.firstChild
while (node) {
// 判断是否还有元素
// 注意点:只要元素添加到文档碎片对象中,name这个元素就会自动从网页上消失
fragment.appendChild(node)
node = app.firstChild
}
//3.返回储存了所有元素的文档碎片对象
return fragment
}
buildTemplate(fragment) {
// 从元素获取所有节点,伪数组转为数组
let nodeList = [...fragment.childNodes]
// console.log(nodeList);//[text, p, text, input, text]
// 循环判断当前的节点是一个元素还是一个文本
nodeList.forEach(node => {
if (this.vm.isElement(node)) {
//是一个元素
this.buildElement(node)
//处理子元素,递归
this.buildTemplate(node)
} else {
//不是一个元素
this.buildText(node)
}
})
}
// 元素处理
buildElement(node) {
// attributes 属性返回指定节点的属性集合
let attrs = [...node.attributes]
attrs.forEach(attr => {
// console.log(attr);//获取到属性
let { name, value } = attr
// console.log(name,value);//单独拿到类型与类型的取值
if (name.startsWith('v-')) {
// v-开头的有很多,比如v-model/v-html/v-if...
// startsWith() 方法用于检测字符串是否以指定的子字符串开始。
// console.log('是v-', name,value);//是v- v-model name
// 切割并解构,不需要v-
let [, directive] = name.split('-')//v,model
// console.log(directive);//model
// 找到对应的工具类执行对应的方法
// node:修改的节点,value:修改的值,this.vm新值
CompilerUtil[directive](node, value, this.vm)
}
})
}
//文本处理
buildText(node) {
// textContent 属性设置或者返回指定节点的文本内容。
let content = node.textContent
let reg = /\{\{.+?\}\}/gi
if (reg.test(content)) {
// console.log('是{{}}',content);//是{{}} {{name}}
// ======================================新增部分=============================
// 找到对应的工具类执行对应的方法
// node:修改的节点,content:修改的值,this.vm新值
CompilerUtil['content'](node, content, this.vm)
}
}
}
6.监听数据变化
let CompilerUtil = {
getValue(vm, value) {
//time.h-->[time,h],利用reduce遍历逐层取
//reduce接收的第一个参数为上一次调用回调返回的值,或者是提供的初始值
// 第二个参数为数组中当前被处理的元素
// vm.$data作为data第一次调用初始值
return value.split('.').reduce((data, currentKey) => {
//第一次执行:data=$data,currentKey=time
// 第二次执行:data=time,currentKey=h
return data[currentKey.trim()]
}, vm.$data)
},
getContent(vm, value) {
let reg = /\{\{(.+?)\}\}/gi
let val = value.replace(reg, (...args) => {
return this.getValue(vm, args[1])
})
return val
},
model: function (node, value, vm) {
// console.log(node, value, vm);
// v-model作用在input上
// 根据被替换内容,获取对应的数据
// node.value=vm.$data[value];
//遇到复杂类型情况下vm.$data[time.h]-->vm.$data[time]-->time[h]
let val = this.getValue(vm, value)
node.value = val
},
html: function (node, value, vm) {
let val = this.getValue(vm, value)
node.innerHTML = val
},
text: function (node, value, vm) {
let val = this.getValue(vm, value)
node.innerText = val
},
content: function (node, value, vm) {
// console.log(value);//{{name}}-->取出name-->将$data[name]的数据赋值
let val = this.getContent(vm, value)
node.textContent = val
}
}
// 模拟创建vue实例(类)
class Vue {
// 构造器,接收一个参数,参数为对象
constructor(value) {
// 判断el是否为一个节点
if (this.isElement(value.el)) {
this.$el = value.el
} else {
// 如果没有则根据传入的找
this.$el = document.querySelector(value.el)
}
this.$data = value.data
// 根据传入的位置和数据渲染
// 先判断是否el是否存在,否则不渲染
if (this.$el) {
// ==================更新================
// 1.给外界传入的所有数据都添加get/set方法,这样就可以监听数据变化了
// 监听数据变化
new Observer(this.$data)
// 传入该实例
new Compier(this)
}
}
// 判断是否为元素节点,元素节点为1,属性节点为2
isElement(node) {
// console.log(node.nodeType === 1);//false
return node.nodeType === 1
}
}
class Compier {
constructor(vm) {
// 保存vue实例
this.vm = vm
// 1.将页面元素提取到碎片文档
let fragment = this.node2fragment(this.vm.$el)
// 2.利用指定的数据编译内存中的元素
this.buildTemplate(fragment)
// 3.将编译好的内存重新渲染到网页上
this.vm.$el.appendChild(fragment)
}
node2fragment(app) {
// 1.创建空的文档
let fragment = document.createDocumentFragment()
//2.遍历循环去到每一个元素
let node = app.firstChild
while (node) {
// 判断是否还有元素
// 注意点:只要元素添加到文档碎片对象中,name这个元素就会自动从网页上消失
fragment.appendChild(node)
node = app.firstChild
}
//3.返回储存了所有元素的文档碎片对象
return fragment
}
buildTemplate(fragment) {
// 从元素获取所有节点,伪数组转为数组
let nodeList = [...fragment.childNodes]
// console.log(nodeList);//[text, p, text, input, text]
// 循环判断当前的节点是一个元素还是一个文本
nodeList.forEach(node => {
if (this.vm.isElement(node)) {
//是一个元素
this.buildElement(node)
//处理子元素,递归
this.buildTemplate(node)
} else {
//不是一个元素
this.buildText(node)
}
})
}
// 元素处理
buildElement(node) {
// attributes 属性返回指定节点的属性集合
let attrs = [...node.attributes]
attrs.forEach(attr => {
// console.log(attr);//获取到属性
let { name, value } = attr
// console.log(name,value);//单独拿到类型与类型的取值
if (name.startsWith('v-')) {
// v-开头的有很多,比如v-model/v-html/v-if...
// startsWith() 方法用于检测字符串是否以指定的子字符串开始。
// console.log('是v-', name,value);//是v- v-model name
// 切割并解构,不需要v-
let [, directive] = name.split('-')//v,model
// console.log(directive);//model
// 找到对应的工具类执行对应的方法
// node:修改的节点,value:修改的值,this.vm新值
CompilerUtil[directive](node, value, this.vm)
}
})
}
//文本处理
buildText(node) {
// textContent 属性设置或者返回指定节点的文本内容。
let content = node.textContent
let reg = /\{\{.+?\}\}/gi
if (reg.test(content)) {
// console.log('是{{}}',content);//是{{}} {{name}}
// 找到对应的工具类执行对应的方法
// node:修改的节点,content:修改的值,this.vm新值
CompilerUtil['content'](node, content, this.vm)
}
}
}
// =================================新增部分======================
class Observer {
// 只要将需要监听的那个对象传递给Observer这个类
// 这个类就可以快速的给传入的对象的所有属性都添加get/set方法
// data代表接收的对象
constructor(data) {
this.observer(data);
}
// 给属性添加get/set;
observer(obj) {
// 判断是不是对象
if (obj && typeof obj === "object") {
//遍历取出传入对象的所有属性,给遍历到的属性都增加get/set方法
for (let key in obj) {
// 参数为:对象,属性,值
this.defineRecative(obj, key, obj[key]);
}
}
}
// obj:需要操作的对象
// attr:需要新增get/set方法的属性
// value:需要新增get/set方法属性的取值
defineRecative(obj, attr, value) {
//如果对象属性里面嵌套对象,则开始递归添加get/set
this.observer(value);
Object.defineProperty(obj, attr, {
// get方法直接返回值
get() {
return value;
},
// set方法接收新值并返回
set: (newValue) => {
if (value !== newValue) {
// 如果新值里面也有对象,也需要添加get/set
this.observer(newValue);
value = newValue;
console.log("监听到数据的变化,需要去更新UI");
}
},
});
}}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!