Today let us realize a simple version of vUE, the text is longer, the collection is suggested to see slowly, and finally ask for a thumbs up !!!!
Start Vue instance function
Let’s create vue. js file in SRC folder, this file is our vueJs entry file, I directly posted here after running the code can see the same content as me on the console, it means that you have opened the mysterious veil of Vue!
The Object.defineProperty() method is mainly used to implement two-way binding of data. If you don’t understand, refer to MDN
class Vue {
constructor(options) {
// Save attributes
this.options = options || {}
this.data = options.data || {}
this.el = options.el && typeof options.el === 'string' ? document.querySelector(options.el) : options.el
this._parxyData()
}
// The convenience object properties are injected into the vue instance
_parxyData() {
Object.keys(this.data).forEach(item= > {
// We pass this instead of an object to inject properties into the instance
Object.defineProperty(this, item, {
get: () = > {
return this.data[item]
},
// newValue is assigned when the value of the attribute changes
set: (newValue) = > {
if(newValue === this.data[item]){
return
}
this.data[item]=newValue
}
})
})
}
}
Copy the code
At this point our console should print out that the instance has been injected perfectly
Create observer function
The observer function is used to notify subscribers when data is updated
class Watcher {
constructor(vm,key,cb){
this.vm=vm;
// Attributes in data
this.key=key;
// The callback function updates the attempt
this.cb=cb;
// Record the watcher object to the deP static property target
Dep.target = this;
// invoke addSub in the get method
this.oldValue=vm[key];
Dep.target=null
}
// Update the view when properties change
update(){
let newValue=this.vm[this.key]
if(this.oldValue === newValue){
return
}
this.cb(newValue)
}
}
Copy the code
Create a compiler template function
The template functions we use to parse vUE syntax, and get values for user actions, tell observers to notify subscribers to update data, and render views
class Template {
constructor(vm){
this.el=vm.el
this.vm=vm
this.compile(this.el)
}
// Compile templates to handle text templates and element nodes
compile(el){
let childNode = el.childNodes
Array.from(childNode).forEach(node= >{
// Process text nodes
if(this.isTextNode(node)){
this.complieText(node)
}else if(this.isElementNode(node)){
this.compileElement(node)
}
// Determine if there are child nodes and recursively call yourself if there are
if(node.childNodes&&node.childNodes.length! =0) {this.compile(node)
}
})
}
// Compile element node processing instructions
compileElement(node){
Array.from(node.attributes).forEach(attr= >{
// Determine whether the command is timed
let attrName = attr.name
if(this.isDirective(attrName)){
attrName =attrName.substr(2)
let key = attr.value
this.update(node,key,attrName)
}
})
}
update(node,key,attrName){
let updateFn = this[attrName+'Updater']
updateFn && updateFn.call(this,node,this.vm[key],key)
}
/ / compile v - text
textUpdater(node,value,key){
node.textContent=value
// Create a Watcher object
new Watcher(this.vm,key,(newValue) = >{
node.textContent = newValue
})
}
// Compile the modeo directive
modelUpdater(node,value,key){
node.value=value
// Create a Watcher object
new Watcher(this.vm,key,(newValue) = >{
node.value = newValue
})
// Implement bidirectional binding for node to register time
node.addEventListener('input'.() = >{
this.vm[key] = node.value
})
}
// Compile the text node to handle the difference expression
complieText(node){
let reg = / \ {\ {(. +?) \} \} /
let value = node.textContent
if(reg.test(value)){
let key = RegExp.$1.trim()
node.textContent = value.replace(reg,this.vm[key])
// Create a Watcher object
new Watcher(this.vm,key,(newValue) = >{
node.textContent = newValue
})
}
}
// Determine if element attributes are directives
isDirective(attrName){
return attrName.startsWith('v-')}// Determine whether the node is a text node
isTextNode(node){
return node.nodeType ===3
}
// Determine whether a node is an element node
isElementNode(node){
return node.nodeType ===1}}Copy the code
Once we’re done here we can see that the page data has been rendered
Create publish and subscribe mode
Publish and subscribe model
class Dep {
constructor(){
// Store all observers
this.subs = []
}
// Add an observer
addSub(sub){
if(sub && sub.update){
this.subs.push(sub)
}
}
// Send notifications
notify(){
this.subs.forEach(sub= > {
console.log('I'm gay Update',sub)
sub.update()
})
}
}
class Observer {
constructor(data) {
this.kidnap(data)
}
// Iterate over all the attributes in data
kidnap(data){
// Determine whether the object is a time object
if(! data ||typeofdata ! ='object') {return
}
// Iterate over all properties of the object
Object.keys(data).forEach(key= >{
this.defineReacttive(data,key,data[key])
})
}
defineReacttive(obj,key,val){
// Create a deP object
let dep = new Dep()
// If so, the object is converted to responsive data
this.kidnap(val)
Object.defineProperty(obj,key,{
enumerable:true.configurable:true.get:() = >{
// Collect dependencies and associate them with observers
Dep.target && dep.addSub(Dep.target)
return val
},
set:(newValue) = >{
if(newValue === val){
return
}
val = newValue
this.kidnap(val)
// Data changes need to be notified
dep.notify()
}
})
}
}
Copy the code
5. Try new skills
So that’s pretty much everything we’ve got, so let’s try it out