手写Vue (1) 编译文本
1. 当我们初始化数据后 就可以进行将数据进行挂载
首先先判断 配置是否有 el 如果有 el 再进行挂载
if (vm.$options.el) { vm.$mount(); }
2. $mount 这个方法中 我们首先要获取到 获取当前挂载的节点
let vm = this; let el = vm.$options.el el = vm.$el = query(el) // 获取当前挂载的节点
3. 获取当前挂载的节点 有两种情况
el: '#app',ru
el: document.getElementById('app;);
所以我们要判断el 是否是一个字符串 如果是字符串 的话 通过 document.querySelector 获取元素 否则直接返回
function query(el) { if (typeof el === 'string') { return document.querySelector(el); } return el }
4. 添加 渲染watchder
observe/watch.js 传入一系列参数
let id = 0; class Watcher { // watch 有唯一标识 constructor(vm, exprOrfn, cb = () => { }, opts = {}) { // exprOrfn 表达式 cb 回调 this.vm = vm; this.exprOrfn = exprOrfn; if (typeof exprOrfn == 'function') { // getter 就是 Watcher 传入的第2个函数 this.getter = exprOrfn; } this.cb = cb; this.opts = opts; this.id = id++; this.get() } get() { } } export default Watcher;
5. 在index.js 引入 watch.js
import Watcher from './observe/watch'
Vue.prototype.$mount = function () { let vm = this; let el = vm.$options.el el = vm.$el = query(el) // 获取当前挂载的节点 let updateComponent = () => { vm.$update() // console.log('数据更新') } // 渲染watchder new Watcher(vm, updateComponent) }
6. updateComponent 是一个函数 里面有一个 $update 方法
在这个方法中主要做的操作 是 创建一个 文档碎片 循环的将 el 上挂载的节点依次 遍历 复制到文档碎片里面
Vue.prototype.$update = function () { let vm = this; let el = vm.$el console.log(el)// 拿到节点 // 需要匹配{{}} let node = document.createDocumentFragment(); let firstChild; while (firstChild = el.firstChild) { node.appendChild(firstChild) } // console.log(node) // 获取到文档碎片 el.appendChild(node) }
let node = document.createDocumentFragment(); 创建文档碎片
循环添加到文档碎片里面
while (firstChild = el.firstChild) {
node.appendChild(firstChild)
}
7. 测试
8. 获取到 node 节点后 我们需要进行 编译
compiler(node, vm)
9.编译 的时候要判断 node 节点的类型
function compiler(node, vm) { let childNodes = node.childNodes; ([...childNodes]).forEach(child => { // 1 元素 3 文本 if (child.nodeType == 1) { // console.log('2020') compiler(child, vm) //编译孩子节点 } else if (child.nodeType == 3) { // console.log('111') util.compilerText(child, vm) } }); }
如果是元素 编译 里面 的孩子节点
compiler(child, vm) //编译孩子节点
如果是文本 编写一个 方法
util.compilerText(child, vm)
10. compilerText 编译文本的方法
compilerText(node, vm) { //编译文本 // 匹配正则 node.textContent = node.textContent.replace(defaultRe, function (...args) { console.log(args, 'test') return util.getValue(vm, args[1]) }) }
defaultRe 代表 正则
const defaultRe = /\{\{((?:.|\r?\n)+?)\}\}/
测试正则:
"{{xxx}}".match(/\{\{((?:.|\r?\n)+?)\}\}/); (2)["{{xxx}}", "xxx", index: 0, input: "{{xxx}}", groups: undefined] const util = {
通过正则我们 可以拿到 {{ }} 里面的东西 再通过 util.getValue 获取值
util.getValue 方法
getValue(vm, expr) { console.log(vm, expr) let keys = expr.split('.'); return keys.reduce((arr, current) => { arr = arr[current] // vm.school.name return arr }, vm) },
keys.reduce 是为了防止 vm.school.name 这种表述情况
reduce 可以将上传的结果 作为传入参数
const util = { getValue(vm, expr) { console.log(vm, expr) let keys = expr.split('.'); return keys.reduce((arr, current) => { arr = arr[current] // vm.school.name return arr }, vm) }, compilerText(node, vm) { //编译文本 // 匹配正则 node.textContent = node.textContent.replace(defaultRe, function (...args) { console.log(args, 'test') return util.getValue(vm, args[1]) }) } }
码云 地址 :
https://gitee.com/guangzhou110/vue_principle
越努力越幸运