이 글에서 공유한 내용은 인터뷰에 관한 것입니다: Vue에 대한 양방향 데이터 바인딩 작성, 이는 특정 참조 가치가 있습니다. 도움이 필요한 친구들은 이를 참조할 수 있습니다
현재 프런트엔드 인터뷰에서는 양방향 데이터 Vue에 대한 바인딩은 테스트하기 매우 쉬운 포인트가 되었습니다. 당장 작성할 수는 없더라도 최소한 원리를 설명할 수는 있어야 합니다. 이 기사에서는 vue를 기반으로 하는 양방향 데이터 바인딩의 예를 작성하겠습니다. 댓글과 함께 모두가 뭔가를 얻을 수 있기를 바랍니다. (추천: vue 인터뷰 질문 2020)
저는 Vue의 양방향 데이터 바인딩의 원리를 주로 Object对象的defineProperty属性,重写data的set和get函数来实现的
를 통해 알고 있다고 믿습니다. 인스턴스를 구현합니다. 코드를 더 명확하게 하기 위해 여기서는 가장 기본적인 내용만 구현하며 주로 v-model, v-bind 및 v-click의 세 가지 명령을 직접 보완할 수도 있습니다.
인터넷에서 사진 추가
페이지 구조 구현은 다음과 같이 매우 간단합니다.
<p id="app"> <form> <input type="text" v-model="number"> <button type="button" v-click="increment">增加</button> </form> <h3 v-bind="number"></h3> </p>
에는 다음이 포함됩니다.
1. 一个input,使用v-model指令 2. 一个button,使用v-click指令 3. 一个h3,使用v-bind指令。
마지막으로 양방향 데이터 바인딩을 사용하게 됩니다. vue 결정과 유사하며 데이터 구조를 기반으로 주석을 추가합니다
var app = new myVue({ el:'#app', data: { number: 0 }, methods: { increment: function() { this.number ++; }, } })
먼저 myVue 생성자를 정의해야 합니다.
function myVue(options) { }
이 생성자를 초기화하려면 _init 속성을 생성자에 추가합니다
function myVue(options) { this._init(options); } myVue.prototype._init = function (options) { this.$options = options; // options 为上面使用时传入的结构体,包括el,data,methods this.$el = document.querySelector(options.el); // el是 #app, this.$el是id为app的Element元素 this.$data = options.data; // this.$data = {number: 0} this.$methods = options.methods; // this.$methods = {increment: function(){}} }
다음으로 _obverse 함수를 구현합니다. 데이터 처리 프로세스를 수행하고, 데이터의 set 및 get 함수를 다시 작성하고
_init 함수를 변환합니다.
myVue.prototype._obverse = function (obj) { // obj = {number: 0} var value; for (key in obj) { //遍历obj对象 if (obj.hasOwnProperty(key)) { value = obj[key]; if (typeof value === 'object') { //如果值还是对象,则遍历处理 this._obverse(value); } Object.defineProperty(this.$data, key, { //关键 enumerable: true, configurable: true, get: function () { console.log(`获取${value}`); return value; }, set: function (newVal) { console.log(`更新${newVal}`); if (value !== newVal) { value = newVal; } } }) } } } myVue.prototype._init = function (options) { this.$options = options; this.$el = document.querySelector(options.el); this.$data = options.data; this.$methods = options.methods; this._obverse(this.$data); }
다음으로 DOM 요소를 업데이트하기 위해 업데이트 함수를 바인딩하는 명령 클래스 Watcher를 작성합니다.
function Watcher(name, el, vm, exp, attr) { this.name = name; //指令名称,例如文本节点,该值设为"text" this.el = el; //指令对应的DOM元素 this.vm = vm; //指令所属myVue实例 this.exp = exp; //指令对应的值,本例如"number" this.attr = attr; //绑定的属性值,本例为"innerHTML" this.update(); } Watcher.prototype.update = function () { this.el[this.attr] = this.vm.$data[this.exp]; //比如 H3.innerHTML = this.data.number; 当number改变时, 会触发这个update函数,保证对应的DOM内容进行了更新。 }
_init 함수 및 _obverse 함수를 업데이트합니다.
myVue.prototype._init = function (options) { //... this._binding = {}; //_binding保存着model与view的映射关系,也就是我们前面定义的Watcher的实例。 当model改变时,我们会触发其中的指令类更新,保证view也能实时更新 //... } myVue.prototype._obverse = function (obj) { //... if (obj.hasOwnProperty(key)) { this._binding[key] = { // 按照前面的数据,_binding = {number: _directives: []} _directives: [] }; //... var binding = this._binding[key]; Object.defineProperty(this.$data, key, { //... set: function (newVal) { console.log(`更新${newVal}`); if (value !== newVal) { value = newVal; binding._directives.forEach(function (item) { // 当number改变时, 触发_binding[number]._directives 中的绑定的Watcher类的更新 item.update(); }) } } }) } } }
그럼 뷰를 모델에 바인딩하는 방법은 무엇일까요? 다음으로, 지침(v-bind, v-model, v-clickde) 등을 구문 분석하고 프로세스에서 뷰와 모델을 바인딩하는 _compile 함수를 정의합니다.
myVue.prototype._init = function (options) { //... this._complie(this.$el); } myVue.prototype._complie = function (root) { root 为 id为app的Element元素,也就是我们的根元素 var _this = this; var nodes = root.children; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node.children.length) { // 对所有元素进行遍历,并进行处理 this._complie(node); } if (node.hasAttribute('v-click')) { // 如果有v-click属性,我们监听它的onclick事件,触发increment事件, 即number++ node.onclick = (function () { var attrVal = nodes[i].getAttribute('v-click'); return _this.$methods[attrVal].bind(_this.$data); //bind是使data的作用域与method函数的作用域保持一致 })(); } if (node.hasAttribute('v-model') && (node.tagName = 'INPUT' || node.tagName == 'TEXTAREA')) { // 如果有v-model属性,并且元素是INPUT或者TEXTAREA,我们监听它的input事件 node.addEventListener('input', (function(key) { var attrVal = node.getAttribute('v-model'); //_this._binding['number']._directives = [一个Watcher实例] // 其中Watcher.prototype.update = function () { // node['vaule'] = _this.$data['number']; 这就将node的值保持与number一致 // } _this._binding[attrVal]._directives.push(new Watcher( 'input', node, _this, attrVal, 'value' )) return function() { _this.$data[attrVal] = nodes[key].value; // 使number 的值与 node的value保持一致, 已经实现了双向绑定 } })(i)); } if (node.hasAttribute('v-bind')) { // 如果有v-bind属性,我们只要使node的值及时更新为data中number的值即可 var attrVal = node.getAttribute('v-bind'); _this._binding[attrVal]._directives.push(new Watcher( 'text', node, _this, attrVal, 'innerHTML' )) } } }
지금까지 우리는 v-bind, v-model 및 v-click의 세 가지 명령을 포함하여 vue의 간단한 양방향 바인딩 기능을 구현했습니다. 효과는 아래와 같습니다
150줄 미만의 전체 코드가 첨부되어 있습니다
myVue <p id="app"> <form> <input type="text" v-model="number"> <button type="button" v-click="increment">增加</button> </form> <h3 v-bind="number"></h3> </p> <script> function myVue(options) { this._init(options); } myVue.prototype._init = function (options) { this.$options = options; this.$el = document.querySelector(options.el); this.$data = options.data; this.$methods = options.methods; this._binding = {}; this._obverse(this.$data); this._complie(this.$el); } myVue.prototype._obverse = function (obj) { var value; for (key in obj) { if (obj.hasOwnProperty(key)) { this._binding[key] = { _directives: [] }; value = obj[key]; if (typeof value === 'object') { this._obverse(value); } var binding = this._binding[key]; Object.defineProperty(this.$data, key, { enumerable: true, configurable: true, get: function () { console.log(`获取${value}`); return value; }, set: function (newVal) { console.log(`更新${newVal}`); if (value !== newVal) { value = newVal; binding._directives.forEach(function (item) { item.update(); }) } } }) } } } myVue.prototype._complie = function (root) { var _this = this; var nodes = root.children; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node.children.length) { this._complie(node); } if (node.hasAttribute('v-click')) { node.onclick = (function () { var attrVal = nodes[i].getAttribute('v-click'); return _this.$methods[attrVal].bind(_this.$data); })(); } if (node.hasAttribute('v-model') && (node.tagName = 'INPUT' || node.tagName == 'TEXTAREA')) { node.addEventListener('input', (function(key) { var attrVal = node.getAttribute('v-model'); _this._binding[attrVal]._directives.push(new Watcher( 'input', node, _this, attrVal, 'value' )) return function() { _this.$data[attrVal] = nodes[key].value; } })(i)); } if (node.hasAttribute('v-bind')) { var attrVal = node.getAttribute('v-bind'); _this._binding[attrVal]._directives.push(new Watcher( 'text', node, _this, attrVal, 'innerHTML' )) } } } function Watcher(name, el, vm, exp, attr) { this.name = name; //指令名称,例如文本节点,该值设为"text" this.el = el; //指令对应的DOM元素 this.vm = vm; //指令所属myVue实例 this.exp = exp; //指令对应的值,本例如"number" this.attr = attr; //绑定的属性值,本例为"innerHTML" this.update(); } Watcher.prototype.update = function () { this.el[this.attr] = this.vm.$data[this.exp]; } window.onload = function() { var app = new myVue({ el:&#39;#app&#39;, data: { number: 0 }, methods: { increment: function() { this.number ++; }, } }) } </script>
관련 권장 사항:
vue 양방향 데이터 바인딩 예제를 구현하는 js 코드
위 내용은 인터뷰 정보: Vue용 양방향 데이터 바인딩 작성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!