vue 양방향 바인딩을 구현하는 방법을 단계별로 가르쳐주세요.
vue에서 양방향 바인딩을 구현하는 방법은 무엇입니까? 이 글은 vue 양방향 바인딩을 작성하는 방법을 단계별로 가르쳐서 모든 사람이 양방향 바인딩의 논리적 방향을 더 잘 이해할 수 있도록 돕겠습니다.
이 글은 주로 함정을 작성하고 함정을 채우는 과정으로 청중이 양방향 바인딩 구현 방법과 양방향 바인딩의 논리적 방향을 더 잘 이해할 수 있도록 합니다. -단계별로 바인딩하는 방법. 이것은 수업 튜토리얼 기사입니다. 기사를 따르고 각 수업을 주의 깊게 관찰하면 구현하는 것이 어렵지 않습니다.
Start
메이지를 시작하시나요? 강아지를 데려오세요! !
잘못된 것 같습니다
사진으로 시작
사진을 보시면 new Vue()
가 두 단계로 나누어져 있는 것을 보실 수 있습니다new Vue()
分为了两步走
代理监听所有数据,并与
Dep
进行关联,通过Dep
通知订阅者进行视图更新。【相关推荐:vuejs视频教程】解析所有模板,并将模板中所用到的数据进行订阅,并绑定一个更新函数,数据发生改变时
Dep
通知订阅者执行更新函数。
接下里就是分析如何去实现,并且都需要写什么,先看一段vue的基础代码,我们从头开始分析
<div id="app"> <input v-model="message" /> <p>{{message}}</p> </div>
let app = new Vue({ el:"#app", data:{ message:"测试这是一个内容" } })
从上面代码我们可以看到new Vue
的操作,里面携带了el
和data
属性,这算是最基础的属性,而在html代码中我们知道<div id="app">
是vue渲染的模板根节点,所以vue要渲染页面就要去实现一个模板解析的方法Compile
类,解析方法中还需要去处理{{ }}
和v-model
两个指令,除了解析模板之后我们还需要去实现数据代理也就是实现Observer
类
实现 Vue 类
如下代码所示,这就写完了Vue
类,够简单吧,如果对class
关键字不熟悉的,建议先去学习一下,从下面我们可能看到,这里实例化了两个类,一个是代理数据的类,一个是解析模板的类。
class Vue { constructor(options) { // 代理数据 new Observer(options.data) // 绑定数据 this.data = options.data // 解析模板 new Compile(options.el, this) } }
接着往下我们先写一个Compile
类用于解析模板,我们再来分析一波,解析模板要做什么事
我们要解析模板不可能直接对dom继续操作,所以我们要创建一个文档片段(虚拟dom),然后将模板DOM节点复制一份到虚拟DOM节点中,对虚拟DOM节点解析完成之后,再将虚拟DOM节点替换掉原来的DOM节点
虚拟节点复制出来之后,我们要遍历整个节点树进行解析,解析过程中会对DOM的atrr属性进行遍历找到Vue相关的指令,除此之外还要对
textContent
节点内容进行解析,判断是否存在双花括号将解析出来所用到的属性进行一个订阅
实现模板解析 Compile 类
下面我们将逐步实现
构建
Compile
类,先把静态节点和Vue实例获取出来,再定义一个虚拟dom的属性用来存储虚拟dom
class Compile { constructor(el, vm) { // 获取静态节点 this.el = document.querySelector(el); // vue实例 this.vm = vm // 虚拟dom this.fragment = null // 初始化方法 this.init() } }
实现初始化方法
init()
,该方法主要是用于创建虚拟dom和调用解析模板的方法,解析完成之后再将DOM节点替换到页面中
class Compile { //...省略其他代码 init() { // 创建一个新的空白的文档片段(虚拟dom) this.fragment = document.createDocumentFragment() // 遍历所有子节点加入到虚拟dom中 Array.from(this.el.children).forEach(child => { this.fragment.appendChild(child) }) // 解析模板 this.parseTemplate(this.fragment) // 解析完成添加到页面 this.el.appendChild(this.fragment); } }
实现解析模板方法
parseTemplate
,主要是遍历虚拟DOM中的所有子节点并进行解析,根据子节点类型进行不同的处理。
class Compile { //...省略其他代码 // 解析模板 parseTemplate(fragment) { // 获取虚拟DOM的子节点 let childNodes = fragment.childNodes || [] // 遍历节点 childNodes.forEach((node) => { // 匹配大括号正则表达式 var reg = /\{\{(.*)\}\}/; // 获取节点文本 var text = node.textContent; if (this.isElementNode(node)) { // 判断是否是html元素 // 解析html元素 this.parseHtml(node) } else if (this.isTextNode(node) && reg.test(text)) { //判断是否文本节点并带有双花括号 // 解析文本 this.parseText(node, reg.exec(text)[1]) } // 递归解析,如果还有子元素则继续解析 if (node.childNodes && node.childNodes.length != 0) { this.parseTemplate(node) } }); } }
根据上面的代码我们得出需要实现两个简单的判断,也就是判断是否是html元素和文字元素,这里通过获取
nodeType
的值来进行区分,不了解的可以直接看一下 传送门:Node.nodeType,这里还扩展了一个isVueTag
方法,用于后面的代码中使用
class Compile { //...省略其他代码 // 判断是否携带 v- isVueTag(attrName) { return attrName.indexOf("v-") == 0 } // 判断是否是html元素 isElementNode(node) { return node.nodeType == 1; } // 判断是否是文字元素 isTextNode(node) { return node.nodeType == 3; } }
实现
parseHtml
- 에이전트는 모든 데이터를 수신하고
Dep
과 연결하며Dep
을 통해 구독자에게 뷰 업데이트를 알립니다. [관련 권장사항: vuejs 비디오 튜토리얼🎜]🎜 - 🎜모든 템플릿을 구문 분석하고, 템플릿에 사용된 데이터를 구독하고, 업데이트 기능을 바인딩합니다. 데이터가 변경되면
Dep
은 구독자에게 업데이트 기능을 실행하라고 알립니다. 🎜
새로운 Vue
의 작업은 가장 기본적인 속성인el
및data
속성을 전달하며, html 코드에서는< div id="app">
는 Vue에서 렌더링한 템플릿 루트 노드이므로 Vue가 페이지를 렌더링하려면 템플릿 구문 분석 메서드Compile<을 구현해야 합니다. /code> 클래스와 구문 분석 방법도 필요합니다. 템플릿을 구문 분석하는 것 외에도 <code>{{ }}
및v-model
두 명령을 처리하려면 다음 작업도 수행해야 합니다.Observer
클래스를 구현하는 데이터 에이전트를 구현해야 합니다. 🎜🎜Vue 클래스 구현🎜🎜다음 코드와 같이Vue가 완성됩니다.
클래스입니다.class
키워드가 익숙하지 않은 경우 먼저 학습하는 것이 좋습니다. 다음에서 두 클래스가 인스턴스화되는 것을 볼 수 있습니다. 여기서 하나는 프록시 데이터 클래스이고 다른 하나는 구문 분석 템플릿 클래스입니다. 🎜🎜 다음으로 템플릿을 구문 분석하기 위한class Compile { //...省略其他代码 // 解析html parseHtml(node) { // 获取元素属性集合 let nodeAttrs = node.attributes || [] // 元素属性集合不是数组,所以这里要转成数组之后再遍历 Array.from(nodeAttrs).forEach((attr) => { // 获取属性名称 let arrtName = attr.name; // 判断名称是否带有 v- if (this.isVueTag(arrtName)) { // 获取属性值 let exp = attr.value; //切割 v- 之后的字符串 let tag = arrtName.substring(2); if (tag == "model") { // v-model 指令处理方法 this.modelCommand(node, exp, tag) } } }); } }
로그인 후 복사로그인 후 복사Compile
클래스를 작성해 보겠습니다. 다시 분석하여 템플릿을 구문 분석하기 위해 수행해야 할 작업을 살펴보겠습니다.🎜- 🎜템플릿을 파싱하려면 DOM에서 직접 작업을 계속할 수 없으므로 문서 조각(가상 DOM)을 만든 다음 템플릿 DOM 노드를 가상 DOM 노드에 복사해야 합니다. , 그리고 가상 DOM 노드의 구문 분석을 완료합니다. 그 후 원본 DOM 노드를 가상 DOM 노드로 교체합니다🎜
- 🎜가상 노드가 복사된 후 구문 분석을 위해 전체 노드 트리를 순회해야 합니다. 구문 분석 프로세스 중에 DOM의 atrr 속성을 탐색하여 Vue 관련 지침을 찾고,
textContent
노드의 콘텐츠를 구문 분석하여 이중 중괄호🎜 - 🎜파싱된 속성 구독🎜
- 🎜
컴파일
클래스를 빌드하고 먼저 정적 노드와 Vue 인스턴스를 얻은 다음 가상 dom 속성을 정의하여 가상 dom을 저장하세요🎜
class Compile { //...省略其他代码 // 处理model指令 modelCommand(node, exp) { // 获取数据 let val = this.vm.data[exp] // 解析时绑定数据 node.value = val || "" // 监听input事件 node.addEventListener("input", (event) => { let newVlaue = event.target.value; if (val != newVlaue) { // 更新data数据 this.vm.data[exp] = newVlaue // 更新闭包数据,避免双向绑定失效 val = newVlaue } }) } }
로그인 후 복사로그인 후 복사- < li>🎜주로 가상 DOM을 생성하고 구문 분석이 완료된 후 구문 분석 템플릿 메서드를 호출하는 데 사용되는 초기화 메서드
init()
를 구현합니다. , DOM 노드가 페이지로 대체됩니다🎜 < /ul>- 에이전트는 모든 데이터를 수신하고
- 🎜분석 템플릿 메소드
parseTemplate</code를 구현합니다. > 주로 가상 DOM의 모든 하위 노드를 순회하여 구문 분석합니다. 하위 노드 유형에 따라 다른 처리가 수행됩니다. 🎜</li></ul><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>// 监听者 class Observer { constructor(data) { this.observe(data) } // 递归方法 observe(data) { //判断数据如果为空并且不是object类型则返回空字符串 if (!data || typeof data != "object") { return "" } else { //遍历data进行数据代理 Object.keys(data).forEach(key => { this.defineReactive(data, key, data[key]) }) } } // 代理方法 defineReactive(data, key, val) { // 递归子属性 this.observe(data[key]) Object.defineProperty(data, key, { configurable: true, //可配置的属性 enumerable: true, //可遍历的属性 get() { return val }, set(newValue) { val = newValue } }) } }</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div><ul style="list-style-type: disc;"><li>🎜위 코드를 기반으로 우리는 두 가지 간단한 판단을 구현해야 한다는 결론을 내렸습니다. html입니다. 여기서 <code>nodeType
의 값을 가져와서 요소와 텍스트 요소를 구분합니다. 이해가 되지 않으면 포털: Node.nodeType🎜, 여기서는 다음에서 사용할isVueTag
메소드도 확장합니다. 다음 코드🎜 - 🎜
parseHtml
메소드를 구현합니다. html 코드를 주로 트래버스합니다. html 요소의 속성 🎜 实现
modelCommand
方法,在模板解析阶段来说,我们只要把 vue实例中data的值绑定到元素上,并实现监听input方法更新数据即可。处理Text元素就相对简单了,主要是将元素中的
textContent
内容替换成数据即可- 在第6点
modelCommand
方法中并没有实现双向绑定,只是单向绑定,后续要双向绑定时还需要继续处理 - 第7点
parseText
方法上面的代码中并没有去订阅数据的改变,所以这里只会在模板解析时绑定一次数据 - 这里虽然实现了数据代理,但是按照图上来说,还需要引入管理器,在数据发生变化时通知管理器数据发生了变化,然后管理器再通知订阅者更新视图,这个会在后续的填坑过程过讲到。
Observer
通知Dep
主要是通过调用notify
方法Dep
通知Watcher
主要是是调用了Watcher
类中的update
方法- update:主要是调用传入的
cb
方法体,用于更新页面数据 - putIn:主要是用来手动加入到
Dep
管理器中。 Watcher
类中的putIn
方法再构造函数调用后并没有加入到管理器中,而是将订阅者本身绑定到target
全局属性上而已
class Compile { //...省略其他代码 //解析文本 parseText(node, exp) { let val = this.vm.data[exp] // 解析更新文本 node.textContent = val || "" } }
class Vue { constructor(options) { // 代理数据 new Observer(options.data) console.log(options.data) // 绑定数据 this.data = options.data // 解析模板 new Compile(options.el, this) } }
class Compile { //...省略其他代码 // 解析html parseHtml(node) { // 获取元素属性集合 let nodeAttrs = node.attributes || [] // 元素属性集合不是数组,所以这里要转成数组之后再遍历 Array.from(nodeAttrs).forEach((attr) => { // 获取属性名称 let arrtName = attr.name; // 判断名称是否带有 v- if (this.isVueTag(arrtName)) { // 获取属性值 let exp = attr.value; //切割 v- 之后的字符串 let tag = arrtName.substring(2); if (tag == "model") { // v-model 指令处理方法 this.modelCommand(node, exp, tag) } } }); } }
class Compile { //...省略其他代码 // 处理model指令 modelCommand(node, exp) { // 获取数据 let val = this.vm.data[exp] // 解析时绑定数据 node.value = val || "" // 监听input事件 node.addEventListener("input", (event) => { let newVlaue = event.target.value; if (val != newVlaue) { // 更新data数据 this.vm.data[exp] = newVlaue // 更新闭包数据,避免双向绑定失效 val = newVlaue } }) } }
class Compile { //...省略其他代码 //解析文本 parseText(node, exp) { let val = this.vm.data[exp] // 解析更新文本 node.textContent = val || "" } }
至此已经完成了Compile
类的初步编写,测试结果如下,已经能够正常解析模板
下面就是我们目前所实现的流程图部分
坑点一:
坑点二:
实现数据代理 Observer 类
这里主要是用于代理data中的所有数据,这里会用到一个Object.defineProperty
方法,如果不了解这个方法的先去看一下文档传送门:
文档:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Observer
类主要是一个递归遍历所有data中的属性然后进行数据代理的的一个方法
defineReactive
中传入三个参数data
, key
, val
data
和key
都是Object.defineProperty
的参数,而val
将其作为一个闭包变量供Object.defineProperty
使用
// 监听者 class Observer { constructor(data) { this.observe(data) } // 递归方法 observe(data) { //判断数据如果为空并且不是object类型则返回空字符串 if (!data || typeof data != "object") { return "" } else { //遍历data进行数据代理 Object.keys(data).forEach(key => { this.defineReactive(data, key, data[key]) }) } } // 代理方法 defineReactive(data, key, val) { // 递归子属性 this.observe(data[key]) Object.defineProperty(data, key, { configurable: true, //可配置的属性 enumerable: true, //可遍历的属性 get() { return val }, set(newValue) { val = newValue } }) } }
下面我们来测试一下是否成功实现了数据代理,在Vue的构造函数输出一下数据
class Vue { constructor(options) { // 代理数据 new Observer(options.data) console.log(options.data) // 绑定数据 this.data = options.data // 解析模板 new Compile(options.el, this) } }
结果如下,我们可以看出已经实现了数据代理。
对应的流程图如下所示
坑点三:
实现管理器 Dep 类
上面我们已经实现了模板解析到初始化视图,还有数据代理。而下面要实现的Dep
类主要是用于管理订阅者和通知订阅者,这里会用一个数组来记录每个订阅者,而类中也会给出一个notify
方法去调用订阅者的update
方法,实现通知订阅者更新功能。这里还定义了一个target
属性用来存储临时的订阅者,用于加入管理器时使用。
class Dep { constructor() { // 记录订阅者 this.subList = [] } // 添加订阅者 addSub(sub) { // 先判断是否存在,防止重复添加订阅者 if (this.subList.indexOf(sub) == -1) { this.subList.push(sub) } } // 通知订阅者 notify() { this.subList.forEach(item => { item.update() //订阅者执行更新,这里的item就是一个订阅者,update就是订阅者提供的方法 }) } } // Dep全局属性,用来临时存储订阅者 Dep.target = null
管理器实现完成之后我们也就实现了流程图中的以下部分。要注意下面几点
实现订阅者 Watcher 类
订阅者代码相对少,但是理解起来还是有点难度的,在Watcher
类中实现了两个方法,一个是update
更新视图方法,一个putIn
方法(我看了好几篇文章都是定义成 get 方法,可能是因为我理解的不够好吧)。
// 订阅者 class Watcher { // vm:vue实例本身 // exp:代理数据的属性名称 // cb:更新时需要做的事情 constructor(vm, exp, cb) { this.vm = vm this.exp = exp this.cb = cb this.putIn() } update() { // 调用cb方法体,改变this指向并传入最新的数据作为参数 this.cb.call(this.vm, this.vm.data[this.exp]) } putIn() { // 把订阅者本身绑定到Dep的target全局属性上 Dep.target = this // 调用获取数据的方法将订阅者加入到管理器中 let val = this.vm.data[this.exp] // 清空全局属性 Dep.target = null } }
坑点四:
埋坑
通过上面的代码我们已经完成了每一个类的构建,如下图所示,但是还是有几个流程是有问题的,也就是上面的坑点。所以下面要填坑
埋坑 1 和 2
完成坑点一和坑点二,在modelCommand
和parseText
方法中增加实例化订阅者代码,并自定义要更新时执行的方法,其实就是更新时去更新页面中的值即可
modelCommand(node, exp) { // ...省略其他代码 // 实例化订阅者,更新时直接更新node的值 new Watcher(this.vm, exp, (value) => { node.value = value }) } parseText(node, exp) { // ...省略其他代码 // 实例化订阅者,更新时直接更新文本内容 new Watcher(this.vm, exp, (value) => { node.textContent = value }) }
埋坑 3
完成坑点三,主要是为了引入管理器,通知管理器发生改变,主要是在Object.defineProperty set
方法中调用dep.notify()
方法
// 监听方法 defineReactive(data, key, val) { // 实例化管理器--------------增加这一行 let dep = new Dep() // ...省略其他代码 set(newValue) { val = newValue // 通知管理器改变--------------增加这一行 dep.notify() } }
埋坑 4
完成坑点四,主要四将订阅者加入到管理器中
defineReactive(data, key, val) { // ...省略其他代码 get() { // 将订阅者加入到管理器中--------------增加这一段 if (Dep.target) { dep.addSub(Dep.target) } return val }, // ...省略其他代码 }
完成了坑点四可能就会有靓仔疑惑了,这里是怎么加入的呢Dep.target
又是什么呢,我们不妨从头看看代码并结合下面这张图
至此我们已经实现了一个简单的双向绑定,下面测试一下
完结撒花
总结
本文解释的并不多,所以才是类教程文章,如果读者有不懂的地方可以在评论去留言讨论
위 내용은 vue 양방향 바인딩을 구현하는 방법을 단계별로 가르쳐주세요.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











vue.js에서 JS 파일을 참조하는 세 가지 방법이 있습니다. & lt; script & gt; 꼬리표;; mounted () 라이프 사이클 후크를 사용한 동적 가져 오기; Vuex State Management Library를 통해 수입.

vue.js에서 bootstrap 사용은 5 단계로 나뉩니다 : Bootstrap 설치. main.js.의 부트 스트랩 가져 오기 부트 스트랩 구성 요소를 템플릿에서 직접 사용하십시오. 선택 사항 : 사용자 정의 스타일. 선택 사항 : 플러그인을 사용하십시오.

vue.js의 시계 옵션을 사용하면 개발자가 특정 데이터의 변경 사항을들을 수 있습니다. 데이터가 변경되면 콜백 기능을 트리거하여 업데이트보기 또는 기타 작업을 수행합니다. 구성 옵션에는 즉시 콜백을 실행할지 여부와 DEEP를 지정하는 즉시 포함되며, 이는 객체 또는 어레이에 대한 변경 사항을 재귀 적으로 듣는 지 여부를 지정합니다.

vue.js에서 게으른 로딩을 사용하면 필요에 따라 부품 또는 리소스를 동적으로로드 할 수 있으므로 초기 페이지로드 시간을 줄이고 성능을 향상시킵니다. 특정 구현 방법에는 & lt; keep-alive & gt를 사용하는 것이 포함됩니다. & lt; 구성 요소는 & gt; 구성 요소. 게으른 하중은 FOUC (Splash Screen) 문제를 일으킬 수 있으며 불필요한 성능 오버 헤드를 피하기 위해 게으른 하중이 필요한 구성 요소에만 사용해야합니다.

Vue DevTools를 사용하여 브라우저 콘솔에서 vue 탭을 보면 VUE 버전을 쿼리 할 수 있습니다. npm을 사용하여 "npm list -g vue"명령을 실행하십시오. package.json 파일의 "종속성"객체에서 vue 항목을 찾으십시오. Vue Cli 프로젝트의 경우 "vue -version"명령을 실행하십시오. & lt; script & gt에서 버전 정보를 확인하십시오. vue 파일을 나타내는 html 파일의 태그.

CSS 애니메이션 또는 타사 라이브러리를 사용하여 VUE에서 Marquee/Text Scrolling Effects를 구현하십시오. 이 기사는 CSS 애니메이션 사용 방법을 소개합니다. & lt; div & gt; CSS 애니메이션을 정의하고 오버플로를 설정하십시오 : 숨겨진, 너비 및 애니메이션. 키 프레임을 정의하고 변환을 설정하십시오 : Translatex () 애니메이션의 시작과 끝에서. 지속 시간, 스크롤 속도 및 방향과 같은 애니메이션 속성을 조정하십시오.

vue.js는 이전 페이지로 돌아갈 수있는 네 가지 방법이 있습니다. $ router.go (-1) $ router.back () 사용 & lt; router-link to = & quot;/quot; Component Window.history.back () 및 메소드 선택은 장면에 따라 다릅니다.

vue.js가 트래버스 어레이 및 객체에 대한 세 가지 일반적인 방법이 있습니다. V- 결합 지시문은 V-FOR와 함께 사용하여 각 요소의 속성 값을 동적으로 설정할 수 있습니다. .MAP 메소드는 배열 요소를 새 배열로 변환 할 수 있습니다.
