[Análisis de código fuente front-end] Instrucciones y ciclo de vida

Referencia: Cursos de la serie de análisis de código fuente de Vue

Código fuente de este capítulo: https://gitee.com/szluyu99/vue-source-learn/tree/master/Directive_Study

Notas de la serie:

Reserva de conocimientos de JavaScript

fragmento de documento

Document.createDocumentFragment() - Referencia de la interfaz API web | MDN (mozilla.org)

document.createDocumentFragment()Se utiliza para crear un objeto de nodo virtual (objeto de fragmento de documento)

Fragment se considera como un contenedor para los nodos dom. Originalmente, la cantidad de nodos que se van a insertar en dom requiere que el navegador se refluya tantas veces como sea posible. El navegador solo se refluye una vez.

Use esto para simular el árbol de sintaxis abstracta de AST (versión simplificada de AST) en Vue

Tipo de nodo DOM

La propiedad nodeType devuelve el tipo de nodo (solo lectura)

  • Nodo de elemento , el atributo nodeType devuelve 1
  • atributo nodo , el atributo nodeType devuelve 2
  • Nodo de texto , la propiedad nodeType devuelve 3
  • Tenga en cuenta el nodo , el atributo nodeType devuelve 8

tipo matriz

situación

Un objeto similar a una matriz es un objeto que tiene la misma forma que una matriz, pero no tiene un método de matriz (con un atributo de longitud)

// 这是个类数组
let arrayLike = {
    
    
   0: "java",
   1: "C++",
   2: "javascript",
   length: 3
}

Los objetos de JavaScript son como una matriz :

  • Los argumentos del objeto de parámetro en la función.
  • getElementsByNameHTMLCollection obtenido usando / TagName / ClassName
  • querySelectorLa NodeList obtenida con

argumentos :

function sum(a, b, c) {
    
    
    console.log(arguments)
    console.log(typeof arguments) // object
	console.log(Object.prototype.toString.call(arguments)) // [object Arguments]
	console.log(arguments.callee)
}
sum(1, 2, 3)

Colección HTML :

<div class="son">
   张三
</div>
<div class="son">
   李四
</div>
<div class="son">
   王五
</div>

<script>
	var arrayList = document.getElementsByClassName("son")
	console.log(arrayList)
	console.log(Array.isArray(arrayList)) // false
	console.log(typeof arrayList) // object
	console.log(Object.prototype.toString.call(arrayList)) // [object HTMLCollection]
</script>

Lista de nodos :

<div class="son">
   张三
</div>
<div class="son">
   李四
</div>
<div class="son">
   王五
</div>

<script>
    var arrayList = document.querySelector(".son")
	console.log(Array.isArray(arrayList)) // fasle
    console.log(arrayList) // object
    console.log(typeof arrayList) // [object HTMLDivElement]
    console.log(Object.prototype.toString.call(arrayList))
</script>

Escenario de aplicación

Operaciones de parámetros transversales:

function sum() {
    
    
      var sum = 0, len = arguments.length
      for (var i = 0; i < len; i++) {
    
    
        sum += arguments[i]
      }
	return sum
}

console.log(sum(1,2,3)) // 6

Defina la función de cadena de concatenación:

La matriz de clase no tiene un método de combinación, solo se puede convertir primero en una matriz y luego usar el método de combinación

function myConcat(separa){
    
    
	// 类数组转数组
	var args = Array.prototype.slice.call(arguments, 1)
	return args.join(separa)
}

console.log(myConcat(",", "张三", "李四", "王五")) // 张三,李四,王五

Pasa argumentos de una función a otra:

function bar(a, b, c) {
    
    
   console.log(a, b, c)
}

function foo() {
    
    
   bar.apply(this, arguments)
}

bar(1, 2, 3) // 1 2 3
foo(1, 2, 3) // 1 2 3

clase matriz a matriz

Método 1: Usar ES6Array.from()

function fun () {
    
    
	let arr = Array.from(arguments)
	console.log(Array.isArray(arr)) // true
}

Método 2: Usar ES6...

function fun () {
    
    
	let arr = [...arguments]
	console.log(Array.isArray(arr)) // true
}

Método 3: tomar prestado el método de matriz

Al []crear una matriz vacía, usar sliceel método devuelve una nueva matriz

function fun () {
    
    
    let arr = [].slice.call(arguments)
    console.log(Array.isArray(arr))
}
function fun () {
    
    
    let arr = Array.prototype.slice.call(arguments)
    console.log(Array.isArray(arr))
}

Escriba una versión simple de Vue a mano

Código fuente de este capítulo: https://gitee.com/szluyu99/vue-source-learn/tree/master/Directive_Study

Una gran cantidad de contenido aquí se basa en la base anterior

El efecto de la versión simple de Vue:

<div id="app">
	{
    
    {
    
    a}}
	<button onclick="add()">增加数字</button>
	<input type="text" v-model="a">
</div>

<script src="/xuni/bundle.js"></script>    

<script>
	let vm = new Vue({
    
    
		el: '#app',
		data: {
    
    
			a: 10,
		},
		watch: {
    
    
			a() {
    
    
				console.log('a 变化了');
			}
		}
	})
	console.log(vm);

	function add() {
    
    
		vm.a++
	}
</script>

La clase Vue debe montarse en el objeto de Windows:

window.Vue = Vue

Vue.js: Una versión simple de la clase Vue

export default class Vue {
    
    
    constructor(options) {
    
    
        this.$options = options || {
    
    }
        this._data = options?.data || undefined 
        // 将数据变成响应式
        observe(this._data)
        // 将数据添加到实例中
        this._initData()
        // 调用默认的 watch
        this._initWatch()
        // 模板编译
        new Compile(options.el, this)
    }

    /**
     * 将数据添加到实例中
     */
    _initData() {
    
    
        let self = this
        Object.keys(this._data).forEach(key => {
    
    
            Object.defineProperty(self, key, {
    
    
                get() {
    
    
                    return self._data[key]
                },
                set (newVal) {
    
    
                    self._data[key] = newVal
                }
            })
        })
    }

    /**
     * 初始化 watch
     */
    _initWatch() {
    
    
        let self = this
        let watch = this.$options.watch 
        Object.keys(watch).forEach(key => {
    
    
            new Watcher(self, key, watch[key])
        })
    }
}

Compile.js: compilación de plantillas y análisis de directivas

/**
 * 模板编译类,指令解析
 */
export default class Compile {
    
    
    constructor(el, vue) {
    
    
        // vue 实例
        this.$vue = vue
        // 挂载点
        this.$el = document.querySelector(el)
        if (this.$el) {
    
    
            // fragment 是 JS 提供的虚拟节点(弱化版 AST)
            let $fragment = this.node2Fragment(this.$el)
            // 编译
            this.compile($fragment)
            // 编译后内容,上树
            this.$el.appendChild($fragment)
        }
    }

    /**
     * DOM 节点转换成 Fragment
     */
    node2Fragment(el) {
    
    
        let fragment = document.createDocumentFragment()
        let child
        while (child = el.firstChild)
            fragment.append(child)
        return fragment
    }
    /**
     * 模板编译
     */
    compile(el) {
    
    
        let self = this
        let childNodes = el.childNodes

        // 匹配 {
    
    {val}} 中 val 
        let reg = /\{\{(.*)\}\}/ 

        childNodes.forEach(node => {
    
    
            let text = node.textContent

            if (node.nodeType === 1) {
    
    
                // console.log('是元素节点');
                self.compileElement(node)
            } else if (node.nodeType === 3 && reg.test(text)) {
    
    
                // console.log('是文本节点');
                let name = text.match(reg)[1] // 获取 {
    
    {val}} 中 val
                // console.log(`文本节点:${name}`);
                self.compileText(node, name)
            }
        })
    }
    /**
     * 编译元素
     */
    compileElement(node) {
    
    
        let self = this
        // 获取到节点的属性
        let nodeAttrs = node.attributes;
        // console.log(nodeAttrs)

        // nodeAttrs 是类数组,转成数组
        [].slice.call(nodeAttrs).forEach(attr => {
    
    
            // 例如:class="title"
            let attrName = attr.name // class
            let value = attr.value // title

            // 分析指令,都是 v- 开头的,如:v-if、v-model
            if (attrName.startsWith('v-')) {
    
     // 判断是指令
                let dir = attrName.substring(2)
                if (dir == 'model') {
    
    
                    // console.log(`v-model 指令:${value}`);
                    // 实现 v-model 的双向绑定功能
                    new Watcher(self.$vue, value, newVal => {
    
    
                        node.value = newVal
                    })

                    let v = self.getVueVal(self.$vue, value)
                    node.value = v
                    node.addEventListener('input', e => {
    
    
                        let newVal = e.target.value
                        self.setVueVal(self.$vue, value, newVal)
                        v = newVal
                    })
                } else if (dir == 'if') {
    
    
                    // console.log(`v-if 指令:${value}`);
                }
            }
        })
    }
    /**
     * 编译文本
     */
    compileText(node, name) {
    
    
        // console.log('compileText: ' + name);
        console.log(`getVueVal: ${
      
      this.getVueVal(this.$vue, name)}`);

        node.textContent = this.getVueVal(this.$vue, name)
        // 创建一个观察者,监听数据变化
        new Watcher(this.$vue, name, value => {
    
    
            node.textContent = value
        })
    }
    /**
     * 根据 a.b.c 形式的表达式从 vue 实例中获取值 
     */
    getVueVal(vue, exp) {
    
    
        let val = vue
        exp.split('.').forEach(key => 
            val = val[key]
        )
        return val
    }
    /**
     * 根据 a.b.c 形式的表达式给 vue 实例设置值
     */
    setVueVal(vue, exp, value) {
    
    
        let val = vue
        exp.split('.').forEach((key, index) => {
    
    
            if (index < key.length - 1) {
    
    
                val = val[key]
            } else {
    
    
                val[key] = value
            }
        })
    }
}

Supongo que te gusta

Origin blog.csdn.net/weixin_43734095/article/details/125530676
Recomendado
Clasificación