最简vue(1)

/** 需要被vue控制的根元素ID * */

el: ‘app’ ,

/** Model模型层 * */

data: {

username: ‘jack’,

password: ‘123456’

}

});

这段代码展示了Vue实例化的过程,当你写完这段代码,其实Vue内部已经完成了ViewModel的创建,ViewModel的作用就是连接Model(模型层)和View(视图层),它是一个桥梁。

让我们引用《三分钟手写一个迷你jQuery,附源码》的页面代码:


页面效果:

在上一节中,我们用迷你jQuery完成的功能,这一节需要用MVVM的思想来重做一遍,即在输入框右边同步显示左边输入框的值。

MVVM用的是 数据驱动页面 的思想,当前端页面足够复杂,其效率和可维护性都是远远大于jQuery的,这也是jQuery没落的原因。哦不,我们应该说jQuery光荣地完成了自己的历史使命!

怎么叫数据驱动页面呢?比如这个例子,当我们修改data中的值,页面上span的innerHTML就会同步变化。即数据变了,DOM也跟着变了。

对应图中的这个过程:

看到这里,有人看你会疑惑,这不是单向绑定么,vue.js不是号称双向数据绑定吗?哈哈,不要着急吗,vue自然是双向数据绑定的。还是这个例子,当我们修改input框的value,就会触发监听(input事件),从而导致Model层的对应数据发生变化。

对应图中的这个过程:

好了,接下来我们再明确一下,我们需要做什么?那就是,完成VM的逻辑。vue.js其实最主要的,就是完成ViewModel。

2.Vue是一个函数

==========

为了尽快实现VM的逻辑,我故意省去了vue中一些复杂的部分,看到这里,你只需要记住,Vue是一个函数。

ViewModel在Vue中,我们可以这样理解,它就是Vue里面的两个阶段而已,分别为初始化阶段编译阶段

所以,我们在编写Vue这个函数的时候,就要完成这两个阶段。

function Vue(){

this.init(); //初始化

this.compile(); //编译

}

在函数里面,我们竟然看到了this,这个在《JavaScript百炼成仙》的函数七重关有讲过,这说明我们如果要使用这个函数,就得把它new出来。

init方法和compile方法是这个函数的两个实例方法。

关于创建函数的实例方法,这边我们介绍一个新的方式,那就是用prototype。

prototype很迷惑,你可以这样理解它:prototype是函数的一个公共未来对象!

只要这个函数将来在什么地方被new了,prototype有的,那个实例一定有!

所以,我们这样写

Vue.prototype.init = function(){

console.log(‘所有的Model数据,都被我劫持了!’);

}

Vue.prototype.compile = function(){

console.log(‘页面所有的DOM,都被我窃听了!’);

}

怎么用?

这么用:

var vm = new Vue({

/** 需要被vue控制的根元素ID * */

el: ‘app’ ,

/** Model数据层 * */

data: {

username: ‘jack’,

password: ‘123456’

}

});

效果:

我在init方法和compile方法中都打印了可爱的语句,现在你不妨猜一猜这两个阶段分别干了什么,对应下面这张图的哪两个部分?

大家开动脑筋,猜一猜呢。

3.初始化阶段

========

init:所有的Model数据,都被我劫持了!

init函数劫持了Model中所有的数据,当Model中数据发生了set操作,就自动去更新页面。所有,init起到的作用就是 M -> VM -> V

好了,我要劫持数据,那么请问,数据从哪来?

答:Vue函数的参数传进来,哈哈。

function Vue(options){

/** Model层 * */

this.$data = options.data;

this.init();

this.compile();

}

$data是啥,不就是这个:

/** Model数据层 * */

data: {

username: ‘jack’,

password: ‘123456’

}

ok,怎么遍历呢?

答:还记得《JavaScript百炼成仙》中叶老教授叶小凡如何遍历对象的法术么?

看代码:

for(let key in this.$data){

}

这段代码是写在init函数里面的,因为是实例方法,它可以直接调用Vue中的实例对象$data。

怎么劫持?

答:用Object.defineProperty,这个方法的意思就是对某个对象的某个key进行劫持。

for(let key in this.$data){

Object.defineProperty(this.$data,key,{

get:function(){

return this.$data[key];

},

set:function(newVal){

this.$data[key] = newVal;

}

})

}

嗯,代码还未写完,毕竟如果就这么点东西,那还劫持个锤子!

我们希望看到的是,当data的某个key发生变化,就去更新DOM。怎么更新呢?再来看下这个图:

从图中可以看到,假如username改变了,页面上有两个地方要变。那我凭什么知道是这两个要变呢?

答:因为vue指令(其实就是元素的属性)

代码如下,我们去掉了id,毕竟不用jQuery那一套了,改成v-model和v-bind:


到这一步,我们发现一个username可能有2个指令(甚至更多),所以需要给每一个key配置一个指令集,用数组比较合适。

function Vue(options){

/** 用来给每一个Model属性配置指令集 * */

this.$bindings = {}

}

遍历Model属性的时候,初始化指令集

let _this = this;

for(let key in _this.$data){

_this.$bindings[key] = {

directions: []

}

}

最后,在set的时候,去遍历所有指令,更新DOM

let value = _this.$data[key];

Object.defineProperty(_this.$data,key,{

get:function(){

return value;

},

set:function(newVal){

value = newVal;

/** 更新DOM * */

_this.$bindings[key].directions.forEach(watcher => {

watcher.update();

});

}

})

这边我们用了forEach,watcher是数组中的某一个对象,它有一个update方法,目的在于更新DOM元素。上面的代码还有一个小技巧,就是要把**_this.$data[key]单独放到上面用value**存起来,不然会有循环调用的问题。

演示循环调用:

Object.defineProperty(_this.$data,key,{

get:function(){

return _this.$data[key];

},

})

因为_this.$data[key]会触发get函数,所以产生了循环调用,直接给你报错:

可是我们只要在外面用value锁住 _this.$data[key]就不会有这个问题了。(emmm…很眼熟是不是,这也算是闭包的一种,即用函数锁住变量)

然后是watcher,_this.$bindings[key].directions中我们会放置很多Watcher对象,这是一个专门用来更新DOM的对象。

Watcher代码如下:

/** 口诀:什么DOM的什么 = 某个Model属性的值* */

function Watcher(dom,expression,vm,dataKey){

this.dom = dom;

this.expression = expression;

this.vm = vm;

this.dataKey = dataKey;

this.update();

}

Watcher.prototype.update = function(){

this.dom[this.expression] = this.vm.$data[this.dataKey];

}

至此,init函数开发完毕,可能有的同学会问,这弄了半天,DOM的指令怎么和Model绑定呢?别急,这就是下一步【编译阶段】该做的事情。

4. 编译阶段

========

编译阶段要做的事情很简单,就是根据el(你传进来的根节点元素),遍历所有的子节点(为了简单我们不做递归),挨个检查vue支持的指令,如果找到了,就将这个元素与Model进行绑定。

function Vue(options){

/** 根节点 * */

this.$el = document.getElementById(options.el);

}

上面的代码是获取根节点的DOM对象,然后是具体的编译函数:

Vue.prototype.compile = function(){

let _this = this;

let nodes = this.$el.children;

for (let i = 0; i < nodes.length; i++) {

let node = nodes[i];

if(node.hasAttribute(‘v-model’)){

let dataKey = node.getAttribute(‘v-model’);

/** 如果是v-model指令,就监听DOM的input事件 * */

node.addEventListener(‘input’,function(){

/** 更新Model * */

_this.$data[dataKey] = node.value;

});

/** 添加watcher * */

_this.$bindings[dataKey].directions.push(new Watcher(

node,

‘value’,

_this,

dataKey

));

}else if(node.hasAttribute(‘v-bind’)){

/** 如果是v-bind指令,只需要添加watcher即可 * */

let dataKey = node.getAttribute(‘v-bind’);

_this.$bindings[dataKey].directions.push(new Watcher(

node,

‘innerHTML’,

_this,

dataKey

));

}

}

}

5.成果展示

======

刷新页面,会看到这个效果:


自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

基础知识是前端一面必问的,如果你在基础知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机会,千万不要因为基础错过了自己心怡的公司。前端的基础知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没必要了,只要平时工作中多注意总结,面试前端刷下题目就可以了。

什么?你问面试题资料在哪里,这不是就在你眼前吗(滑稽

滞不前!**

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-zcjI9HC2-1713345755352)]

[外链图片转存中…(img-O9BZpigm-1713345755353)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-drvod5K7-1713345755353)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

基础知识是前端一面必问的,如果你在基础知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机会,千万不要因为基础错过了自己心怡的公司。前端的基础知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没必要了,只要平时工作中多注意总结,面试前端刷下题目就可以了。

什么?你问面试题资料在哪里,这不是就在你眼前吗(滑稽

资料领取方式:戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值