一比一手写迷你版vue,彻底搞懂vue运行机制

从零到一手写迷你版Vue 监听器,数据变化更新对应节点视图// 创建Watcher监听器,负责更新视图 class Watcher {// vm vue实例,依赖key,updateFn更新函数(编译阶段传递进来) constructor(vm , key , updateFn) {// 调用更新函数,获取最新值传递进去 this . $updateFn . call(this . $vm , this . $vm [ this . $key ]) } }data的一个属性对应一个Dep实例管理多个Watcher。 阅读详情

前言

现在前端面试Vue中都会问到响应式原理以及如何实现的,如果你还只是简单回答通过Object.defineProperty()来劫持属性可能已经不够了。

本篇文章通过学习文档及视频教程实现手写一个简易的Vue源码实现数据双向绑定,解析指令等。

几种实现双向绑定的做法

目前几种主流的mvc(vm)框架都实现了单向数据绑定,而我所理解的双向数据绑定无非就是在单向绑定的基础上给可输入的元素(input, textare等)添加了change(input)事件,来动态修改model和view,并没有多高深,所以无需太过介怀是实现的单向或双向绑定。

实现数据绑定的做法有大致如下几种:

发布者-订阅者模式(backbone.js)

脏值检查(angular.js)

数据劫持(Vue.js)

  • 发布者-订阅者模式

一般是通过sub, pub的方式来实现数据和试图的绑定坚听,更细数据方法通常做法是vm.set(‘property’, value)
这种方式现在毕竟太low来,我们更希望通过vm.property = value这种方式更新数据,同时自动更新视图,于是有来下面两种方式。

  • 脏值检查

angular.js是通过脏值检测的方式对比数据是否有变更,来决定是否更新视图,最简单的方式就是通过setInterval()定时轮询检测数据变动,当然Google不会这么low,angular只有在制定的事件触发时进入脏值检测,大致如下

* DOM事件,臂如用户输入文本,点击按钮等(ng-click)
* XHR响应事件($http)
* 浏览器location变更事件($location)
* Timer事件($timeout, $interval)
* 执行$diaest()或¥apply()

  • 数据劫持

Vue.js则是通过数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

vue全家桶视频讲解:进入学习

Vue源码实现

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script type="text/javascript" src="./compile.js"></script>
        <script type="text/javascript" src="./observe.js"></script>
        <script type="text/javascript" src="./myvue.js"></script>
    </head>
    <body>
        <div id="app">
            <h2>{{person.name}} -- {{person.age}}</h2>
            <h3>{{person.sex}}</h3>
            <ul>
                <li>1</li>
                <li>2</li>
                <li>3</li>
            </ul>
            <div v-text="msg"></div>
            <div>{{msg}}</div>
            <div v-text="person.name"></div>
            <div v-html="htmlStr"></div>
            <input type="text" v-model="msg" />
            <button type="button" v-on:click="btnClick">v-on:事件</button>
            <button type="button" @click="btnClick">@事件</button>
        </div>
        <script type="text/javascript">
            let vm = new Myvue({                el: '#app',                data: {                    person: {                        name: '只会番茄炒蛋',                        age: 18,                        sex: '男'
                    },                    msg: '学习MVVM实现原理',                    htmlStr: '<h1>我是html指令渲染的</h1>'
                },                methods: {                    btnClick() {                        console.log(this.msg)                    }                }            })        </script>
    </body>
</html>

第一步 - 实现一个指令解析器(Compile)

compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

myvue.js

// 工具类根据指令执行对应方法
const compileUtils = {
    /*     * node 当前元素节点     * expr 当前指令的value     * vm 当前Myvue实例,      * eventName 当前指令事件名称     */

    // 由于指令绑定的属性有可能是原始类型,也有可能是引用类型, 因此要取到最终渲染的值
    getValue(expr, vm) {
        // reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
        return expr.split('.').reduce((data, currentVal) => {
            return data[currentVal]
        }, vm.$data)
    },
    // input双向数据绑定
    setValue(expr, vm, inputVal) {
        // reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
        return expr.split('.').reduce((data, currentVal) => {
            // 将当前改变的值赋值
            data[currentVal] = inputVal
            console.log(data);
        }, vm.$data)
    },

    // 处理{{person.name}}--{{person.age}}这种格式的数据,不更新值的时候会全部替换了
    getContentVal(expr, vm) {
        return expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
            // 获取{{}}中的属性
            return this.getValue(args[1], vm)
        })

    },
    // 这里简单就封装了几个指令方法
    text(node, expr, vm) {
        let value;
        // 处理{{}}的格式
        if (expr.indexOf('{{') !== -1) {
            value = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
                // 绑定观察者
                new Watcher(vm, args[1], (newValue) => {
                    // 处理{{person.name}}--{{person.age}}这种格式的数据,不然更新值的时候会全部替换了
                    this.upDater.textUpDater(node, this.getContentVal(expr, vm))
                })
                // 获取{{}}中的属性
                return this.getValue(args[1], vm)
            })
        } else {
            new Watcher(vm, expr, (newValue) => {
                this.upDater.textUpDater(node, newValue)
            })
            // 获取当前要节点要更新展示的值
            value = this.getValue(expr, vm)
        }
        // 更新的工具类
        this.upDater.textUpDater(node, value)
    },
    html(node, expr, vm) {
        const value = this.getValue(expr, vm)
        // 绑定观察者
        new Watcher(vm, expr, (newValue) => {
            this.upDater.htmlUpDater(node, newValue)
        })
        // 更新的工具类
        this.upDater.htmlUpDater(node, value)
    },
    model(node, expr, vm) {
        const value = this.getValue(expr, vm)
        // 绑定观察者
        new Watcher(vm, expr, (newValue) => {
            this.upDater.modelUpDater(node, newValue)
        })
        node.addEventListener('input', (e) => {
            // 设置值
            this.setValue(expr, vm, e.target.value)
        })
        // 更新的工具类
        this.upDater.modelUpDater(node, value)
    },
    on(node, expr, vm, eventName) {
        // 获取当前指令对应的方法
        const fn = vm.$options.methods && vm.$options.methods[expr]
        // console.log(fn);
        node.addEventListener(eventName, fn.bind(vm), false)
    },
    // 更新的工具类
    upDater: {
        // v-text指令的更新函数
        textUpDater(node, value) {
            node.textContent = value
        },
        // v-html指令的更新函数
        htmlUpDater(node, value) {
            node.innerHTML = value
        },
        // v-model指令的更新函数
        modelUpDater(node, value) {
            node.value = value
        }
    }
}

// Myvue
class Myvue {
    constructor(options) {
        this.$el = options.el;
        this.$data = options.data;
        this.$options = options;
        if (this.$el) {
            // 1.实现一个数据观察者
            new Observe(this.$data)

            // 2.实现一个指令解析器
            new Compile(this.$el, this)

            // 3.实现this代理, 访问数据可以直接通过this访问
            this.proxyData(this.$data)
        }
    }
    proxyData(data) {
        for (const key in data) {
            Object.defineProperty(this, key, {
                get() {
                    return data[key]
                },
                set(newValue) {
                    data[key] = newValue
                }
            })
        }
    }
}

compile.js

// 指令解析器
class Compile {
    constructor(el, vm) {
        // 判断当前传入的el是不是一个元素节点
        // document.querySelector返回与指定的选择器组匹配的元素的后代的第一个元素。
        this.el = this.isElementNode(el) ? el : document.querySelector(el)
        this.vm = vm
        // 1.匹配节点内容及指令替换相应的内容, 因为每次匹配替换会导致页面回流和重绘, 所以使用文档碎片对象
        // 获取文档碎片对象, 放入内存中会减少页面的回流和重绘
        const fragment = this.node2Fragment(this.el)

        // 2.编译模版
        this.compile(fragment)

        // 3.追加子元素到根元素
        this.el.appendChild(fragment)

    }

    // 判断是否是元素节点
    isElementNode(node) {
        return node.nodeType === 1
    }

    // 将当前根元素中的所有子元素一层层取出来放到文档碎片中, 以减少页面回流和重绘
    node2Fragment(el) {
        // 创建文档碎片对象
        const fragment = document.createDocumentFragment()
        let firstChild;
        // 将当前el节点对象的所有子节点追加到文档碎片对象中
        while (firstChild = el.firstChild) {
            fragment.appendChild(firstChild)
        }
        return fragment
    }

    // 编译模版, 解析指令
    compile(fragment) {
        // 1.获取到所有的子节点, 当前获取的子节点数组是一个伪数组, 需要转为数组
        const childNodes = [...fragment.childNodes]
        childNodes.forEach(child => {
            // 判断当前节点是元素节点还是文本节点
            if (this.isElementNode(child)) {
                // 编译元素节点
                this.compileElement(child)
            } else {
                // 编译文本节点
                this.compileText(child)
            }
            // 递归遍历当前节点时候还有子节点对象
            if (child.childNodes && child.childNodes.length) {
                this.compile(child)
            }
        })

    }

    // 编译元素节点
    compileElement(node) {
        // 根据不同指令属性, 编译模版信息
        const attributes = [...node.attributes];
        attributes.forEach(attr => {
            // 通过解构将指令的name和value获取到
            const {
                name,
                value
            } = attr
            // 判断当前属性是指令还是原生属性
            if (this.isDirective(name)) {
                // 截取指令, 不需要v-
                const directive = name.split('-')[1]
                // 由于指令格式有 v-text v-html v-bind:属性 v-on:事件等等, 按照 : 再次分割
                const [dirName, eventName] = directive.split(':')
                // 更新数据, 数据驱动视图
                compileUtils[dirName](node, value, this.vm, eventName)
                // 删除有指令的标签上的属性
                node.removeAttribute('v-' + directive)
            } else if (this.isEventName(name)) { // 判断指令是以@开头绑定的事件
                // 截取指令, 不需要@, 这里就省略处理里 @click.stop.prevent等事件修饰符, 原理不难
                const eventName = name.split('@')[1]
                // 更新数据, 数据驱动视图
                compileUtils['on'](node, value, this.vm, eventName)
            }
        })
    }

    // 编译文本节点
    compileText(node) {
        // node.textContent获取文本并且匹配{{}} 模版字符串类型的
        const content = node.textContent
        if (/\{\{(.+?)\}\}/.test(content)) {
            compileUtils['text'](node, content, this.vm)
        }
    }

    // 判断当前属性是指令还是原生属性
    isDirective(attrName) {
        // startsWith() 方法用来判断当前字符串是否以另外一个给定的子字符串开头,并根据判断结果返回 true 或 false。
        return attrName.startsWith('v-')
    }

    // 判断指令是以@开头绑定的事件
    isEventName(attrName) {
        return attrName.startsWith('@')
    }
}

第二步 - 实现一个数据监听器(Observer)

利用Obeject.defineProperty()来监听属性变动 那么将需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。

observer.js

// 数据劫持
class Observe {
    constructor(data) {
        this.observe(data)
    }
    // 使用object.defineProperty监听对象, 数组暂时不考虑,太复杂
    observe(data) {
        if (data && typeof data === 'object') {
            // console.log(data);
            Object.keys(data).forEach(key => {
                this.defineReactive(data, key, data[key])
            })
        }
    }

    // 劫持属性
    defineReactive(obj, key, value) {
        // 递归遍历
        this.observe(value)
        // 创建依赖收集器
        const dep = new Dep()
        // console.log(dep);
        Object.defineProperty(obj, key, { // obj为已有对象, key为属性, 第三个参数为属性描述符
            enumerable: true, // enumerable:是否可以被枚举(for in),默认false
            configurable: false, // 是否可以被删除,默认false
            // 获取
            get() {
                // console.log(dep.target);
                // 订阅数据变化时, 往Dep中添加观察者
                Dep.target && dep.addSub(Dep.target)
                return value
            },
            // 设置
            set: (newValue) => {
                // 这里要注意新设置的值也需要劫持他的属性
                this.observe(newValue)
                if (newValue !== value) {
                    value = newValue
                }
                // 通知订阅器找到对应的观察者,通知观察者更新视图
                dep.notify()
            }
        })
    }
}

第三部 - 实现一个Watcher去更新视图

在初始化myvue实例的时候,通过object。defineProperty()的get属性时去添加观察者,在set更改属性的时候去触发notify()来调用upDate方法更新视图

// 观察者
class Watcher {
    constructor(vm, expr, cb) {
        this.vm = vm
        this.expr = expr
        this.cb = cb
        // 存储旧值
        this.oldValue = this.getOldValue()
    }
    // 获取旧值
    getOldValue() {
        // 在获取旧值的时候将观察者挂在到Dep订阅器上
        Dep.target = this
        const oldValue = compileUtils.getValue(this.expr, this.vm)
        // 销毁Dep上的观察者
        Dep.target = null
    }

    // 更新视图
    upDate() {
        // 获取新值
        const newValue = compileUtils.getValue(this.expr, this.vm)
        if (newValue !== this.oldValue) {
            this.cb(newValue)
        }
    }
}

// 订阅器
class Dep {
    constructor() {
        this.subs = []
    }
    // 收集观察者
    addSub(watcher) {
        this.subs.push(watcher)
    }
    // 通知观察者去更新视图
    notify() {
        this.subs.forEach(watcher => {
            watcher.upDate()
        })
    }
}

面试题-阐述你所理解的MVVM响应式原理

Vue是采用数据劫持配合发布者-订阅者模式,通过Object.defineProperty来()来劫持各个属性的getter和setter,在数据发生变化的时候,发布消息给依赖收集器,去通知观察者,做出对应的回调函数去更新视图。

具体就是:MVVM作为绑定的入口,整合Observe,Compil和Watcher三者,通过Observe来监听model的变化,通过Compil来解析编译模版指令,最终利用Watcher搭起Observe和Compil之前的通信桥梁,从而达到数据变化 => 更新视图,视图交互变化(input) => 数据model变更的双向绑定效果。

总结

本篇文章主要以几种实现双向绑定的做法、实现Observer、实现Compile、实现Watcher、实现MVVM这几个模块来阐述了双向绑定的原理和实现。并根据思路流程渐进梳理讲解了一些细节思路和比较关键的内容点,当然肯定有很多不完善的地方,但是对于如何实现双向数据绑定你肯定有了更加深刻的了解。

本篇文章也是通过查看Vue源码解析文章,以及B站相关视频总结出来的,俗话说好记性不如烂笔头, 自己即使照着抄一遍也能更加印象深刻。

【零基础入门VUE】VueJS - 概述 不会对 DOM 进行更改,而是创建 DOM 的副本,该副本以 JavaScript 数据结构的形式存在。最终的更改会更新到真实的 DOM,用户将看到它的变化。随后,对两个 JavaScript 对象进行比较,并在真实 DOM 中更新最终的更改。Ember 添加了 glimmer 渲染引擎,旨在提高重新渲染性能,这与使用虚拟 DOM 的 VueJS 和 React 是类似的概念。随着时间的推移,淘汰赛的发展速度已经放缓。VueJS 继承了 Angular 和 React 的优点,并构建了一个强大的库。 阅读详情

相关推荐

从零到一:YOLOv11高帧率模型在RDK X5上的ONNX转换与部署实战

本文详细介绍了将最新的YOLOv11高帧率模型部署到地平线RDK X5开发板的完整实战流程。内容涵盖从PyTorch模型导出ONNX、地平线工具链环境配置与模型量化编译,到编写高效的板端推理代码,并针对高帧率目标进行了后处理优化。通过实战避坑指南,帮助开发者在RDK X5上实现30FPS以上的实时目标检测。

weixin_29089057的博客 139

探索 Mini-Vue:一个轻量级的Vue.js实现

探索 Mini-Vue:一个轻量级的Vue.js实现 是一个极简版的 Vue.js 框架,由知名开发者 @xiaolu-coding 创建。它的核心目标是提供一个简单、可理解且易上手的 Vue 实现,适用于教学和低性能环境下的应用开发。在本文中,我们将深入了解 Mini-Vue 的设计原理、使用场景和技术特性。 项目概述 Mini-Vue 并非是对 Vue.js 的完整复刻,而是专注于其基本功能,...

gitblog_00012的博客 1161

MTK平台高效部署Qwen2.5大模型:从转换到推理全流程解析

本文详细解析了在MTK平台上高效部署Qwen2.5大模型的全流程,涵盖从环境准备、模型转换与量化,到编译部署与上机推理。重点介绍了如何利用MTK GAI部署工具包进行模型转换和量化,以适配MTK硬件,并最终在设备上成功运行Qwen2.5模型,为边缘侧AI应用提供实践指南。

weixin_29041195的博客 504

mini Vue的实现 Vue工作原理简析

Mini Vue,顾名思义是一个丐版Vue,本篇将根据Vue的原理去简单的写一下其中的几个核心api思路,就像是伪代码一样,这里只写核心思路不处理任何边缘情况。 代码是跟着coderwhy老师写的。 原理 在实现之前,先来说一下Vue的原理。 事实上Vue包含三大核心: Compiler模块:编译模版系统; Runtime模块:或称之Renderer模块,渲染模块; Reactive模块:响应式系统。 编译系统和渲染系统合作: 编译系统会将template编译为render函数和creat

qq_42996469的博客 1111

复合材料热压罐固化残余应力仿真与优化

复合材料在热压罐固化过程中产生的残余应力是影响构件性能的关键因素,尤其在航空航天和风电叶片等高端制造领域。通过多物理场耦合仿真技术,可以精确预测残余应力分布,优化工艺参数。顺序耦合方法分步求解热分析和结构分析,既保证了计算精度,又控制了成本。Abaqus等有限元工具在此过程中发挥重要作用,通过合理设置边界条件、材料属性和接触关系,能够有效模拟温度场和应力场的相互作用。工程实践中,这种仿真方法已成功应用于无人机机翼、卫星支架等实际项目,显著降低了固化变形和微裂纹风险。

weixin_33146151的博客 214

DBeaver连接ClickHouse保姆级教程:从安装到成功测试(附驱动下载问题解决)

本文提供了一份详细的DBeaver连接ClickHouse实战指南,重点解决了驱动下载失败等常见问题。文章从环境准备、DBeaver安装讲起,详细阐述了手动配置ClickHouse驱动的可靠方案,并逐步指导完成连接配置与测试,最后介绍了基础操作与查询优化技巧,旨在帮助用户高效完成连接并快速上手使用。

pink的博客 877

Vue开发工具vuejs-devtools超级详细安装教程以及常见问题解决

这绝对是最详细的Vue开发工具vuejs-devtools安装教程,相信你只需要5分钟即可解决所有问题 所需的所有文件,链接:https://pan.baidu.com/s/19GWDS_7GodIdFBUO0eTDAg 提取码:2yvc 一、vue.js插件下载 下载地址:vue.js插件下载; 点击进入Vue官网即可下载,共有两种版本的插件(开发版本:vue.js,生产版本:vue.main.js),建议使用开发版本。开发版本有更多的错误信息提示和调试,文件较大,生产办文件小但是很多提示不全,解决开发

goog_man的博客 6万+

手把手教你搭建硬件在回路(HIL)仿真系统:从Simulink建模到实时测试

本文详细介绍了如何从零开始搭建硬件在回路(HIL)仿真系统,涵盖Simulink建模、实时仿真、代码生成及调试等关键步骤。通过具体配置示例和优化技巧,帮助开发者解决复杂控制系统验证中的技术难题,提升半实物仿真的效率和可靠性。

weixin_30952535的博客 445

Vue内部运行机制剖析--思维导图总结

总结的Vue机制内部运行概览思维导图: VUE机制内部运行概览思维导图文字版本: 初始化以及挂载 来自于:new Vue()之后 调用分方法:_init 初始化生命周期、事件、 props、 methods、 data、 computed 与 watch 之后: $mount 会挂载组件 编译template 编译 parse 会用正则等方式解析 tem...

Wion Wan 1003

WorkBuddy CAD 审图救命课:约束、单位、尺寸链一次查清,打样前拦住返工

最危险的 CAD 文件不是打不开,而是看起来完整却混用了毫米与英寸、草图欠约束、关键尺寸没有来源。审图任务必须先列规则,再输出问题位置。 本课不会用“AI 一键完成”制造错觉,而是把 WorkBuddy、FreeCAD 0.21.2、OpenCascade 7.7 与人工审阅组成一条可复现链路。你会从“一份模拟 STEP 文件、参数表、装配基准与审图标准”出发,得到“单位报告、约束清单、尺寸链风险表与人工复核标记”,并学会用真实命令、错误信号和停止条件判断成果能不能进入下一阶段。

yueyeguzhuo的专栏 264

Unity中语法糖带来的GC问题 ——那些看起来优雅、实际上在背后偷偷生垃圾的代码

*语法糖(Syntactic Sugar)**指的是编程语言中为了让代码更简洁、易读而提供的简便写法。比如C#中的foreachlambda表达式LINQ自动属性等。语法糖本身不是坏事,但在Unity等对GC敏感的环境下,要警惕语法糖带来的隐式内存分配。合理使用语法糖,关键场合用更“丑陋”但高效的写法,才能保证游戏流畅运行。下面我将更深入地分析Unity中语法糖导致GC的原理、具体场景、检测与优化技巧,并补充一些容易忽视的细节。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 517

vue和vuejs区别_D3vue-VueJS的D3插件

vue和vuejs区别 D3vue (D3vue) D3 integration with Vue.js. 与Vue.js的D3集成。 View demo 查看演示 Download Source 下载源 d3vue is a plugin for VueJS 2 that allows you to take data from your Vue instance and bind ...

cuk5340的博客 3280

ApolloScape 数据集:YOLOv10 + UI界面实现与应用

是由百度Apollo团队发布的一个大规模自动驾驶数据集,涵盖了车辆、行人、骑行者等多种交通元素,旨在为自动驾驶技术的发展提供丰富的数据支持。数据集包含多种复杂的场景,例如城市道路、交叉口、车辆聚集等,能够帮助研究人员和开发者在多种现实环境下测试和验证目标检测模型。YOLO(You Only Look Once)系列算法是目前最流行的目标检测算法之一,因其高效、准确以及实时性强的特点被广泛应用于计算机视觉领域。YOLOv10是YOLO系列中的最新版本,在性能和效率上进行了多方面的优化。速度快。

YOLO 927

VueJS WebPack separating javascript from vue

VueJS 与 Webpack:将 JavaScript 从 Vue 文件中分离

suiusoar 1350

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Vue基础——VueJS是什么、Vue的优缺点、vue2和vue3的模板区别、MVVM数据双向绑定、Vue的安装和使用、Vue模板语法-文本渲染、常用的vue的指令

Vue基础——VueJS是什么、Vue的优缺点、vue2和vue3的模板区别、MVVM数据双向绑定、Vue的安装和使用、Vue模板语法-文本渲染、常用的vue的指令

ranran0306的博客 9692

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

探索Vue.js的无限可能 —— vuejs-challenges

挑战自我,共同成长,Vue.js挑战等你来战!

2401_83063795的博客 2841

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

VUE原理及运行机制

在修改对象值时,会触发响应的setter,setter通知之前【依赖收集】到的Dep 中的所有watcher,告诉他们自己的值变了,需要重新渲染视图。3.设置一个解析器 Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在 v-model,v-on等指令,则解析器 Compile 初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)如果修改了text3的数据,但视图中不需要text3,所以并不需要调用cb函数。

三包酸奶!的博客 1590

推荐:Vue.js 官方文档日语版项目 - vuejs/jp.vuejs.org

推荐:Vue.js 官方文档日语版项目 - vuejs/jp.vuejs.org 在这个飞速发展的前端世界里,拥有清晰、详尽的文档是至关重要的。Vue.js,作为一款轻量级但功能强大的渐进式框架,其官方文档一直是开发者的重要学习资源。现在,对于日语使用者来说,有一个绝佳的机会,那就是参与并使用由社区维护的 vuejs/jp.vuejs.org 项目,它提供了 Vue.js 2.x 版本的日语文档。...

gitblog_00009的博客 1154

提升Vue开发效率的利器:vuejs-devtools

提升Vue开发效率的利器:vuejs-devtools 【下载地址】Vue开发工具vuejs-devtools安装教程及常见问题解决 Vue开发工具vuejs-devtools安装教程及常见问题解决本文详细介绍了如何安装Vue开发工具vuejs-devtools,并提供了常见问题的解决方法 ...

gitblog_06522的博客 745

面向开发人员 的终极 VueJS 资源

在 10 小时内构建 10 个项目 使用 Vue 3 组合 API - 设置、传送、反应和引用 Vue 3 教程将从非常基础的内容开始,通过构建 10 个不同的应用程序,教您如何在 10 个小时的完整课程中将 TensorFlow 和 firebase 与您的 Vuejs 一起使用。它是从头开始设计的,根据您的目标,可以很容易地成为一个库和一个框架。VueJS 示例,顾名思义,它是示例的集合,例如简单且可自定义的步骤进度指示器、Vue 汽车贷款计算器、通知/Toast 组件等使用 VueJS 制作的项目。

码上全栈创享家官方号,小红书、公众号、抖音、b站,youtube等同名,欢迎各位码农一起来学习! 2868

Vue和React的运行机制

Vue和React都是目前主流的前端框架,它们具有相似的运行机制,但也有一些区别。本文将详细介绍Vue和React的运行机制,包括组件化、虚拟DOM和响应式更新,还会提供一些代码示例以帮助读者更好地理解。

一个努力不被优化的程序员 1518
上一篇: 滴滴前端一面必会面试题汇总
下一篇: 手写vue-router核心原理
hellocoder2029
博客等级 码龄4年 15粉丝 80原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值