VUE你知多少

什么是vue

  • vue 是一个前端的js库,也是一个mvvm的框架。它简化对应的js原生的操作,提高了对应的浏览器性能(虚拟dom,diff算法)
  • vue作者:尤雨溪,vue2诞生于2015年,vue3诞生于2020年6月,被阿里巴巴维护。
  • 在vue中: Model:指的是js中的数据,如对象,数组等等。 View:指的是页面视图 viewModel:指的是vue实例例化对象

特点

Vue.js 最显著的特点就是响应式和数据驱动,也就是将Model和View进行单向绑定或者双向绑定

单向绑定(v-bind)

单向数据流在 Vue 中实际表现就是:当 Model 中的 data 发生变化的时候会单向修改 View 中的值,而 View 中的值发生变化的时候,Model 不会感知。实际应用就是 v-bind 单向数据。

主要操作:

  • vue2的劫持 (es5语法)
  • Object.defineProperty
  • 数组不能进行劫持,需要采用重写数组的方法也就是改变数组的七个方法来劫持
  • vue3的劫持 (es6语法)
  • proxy(万物皆对象:都能劫持)

双向绑定MVVM(v-model)

  • 双向数据绑定多了 View 变化会通知到 Model 层。
  • MVVM 的具体实现:无论 Model 还是 View 中的值发生变化,都会通过 ViewModel 通知到对方并实现同步。
  • 一是将模型转化成视图,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。
  • 二是将视图转化成模型,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。
  • 实际应用就是 v-model 双向数据绑定。

双向数据绑定的实现

  • vue的双向数据绑定

    <!-- 准备容器 -->
    <div id="app">
        <!-- v-model 是vue中实现双向数据绑定的指令 -->
        <input type="text" v-model="message">
        <!-- 查看对应message的值   模板语法-->
        {{message}}
    </div>
    <!-- 链入vue.js -->
    <script src="./vue.js"></script>
    <script>
        new Vue({
            // 挂载地址
            el: '#app',
            // 数据
            data: {
                message: 'hello'
            }
        })
    </script>
  • vue2的双向数据绑定

数据劫持(Object.defineProperty) + obServer

 【实现流程】

  • 获取所有的 input 框
  • 过滤有 v-model 属性的 input
  • 遍历所有过滤后的 input
  • 找这个 v-model 属性的属性值 data,递归 data 中的数据来进行劫持
  • 使用 ObServer 监听 input 输入框的内容变化
  • 重新设置对应的 data 中的数据(this._data)
  • 使用 Object.defineProperty 的 set 来监听数据的变化
  • 在数据变化的时候,重新渲染对应的 input 的值 
  • 渲染应该解析对应{ { } } 找到对应的属性名进行替换
    class Vue{
    constructor(option){
        this.el = document.querySelector(option.el) //找到对应渲染的元素
        this.data = option.data
        //用于Object.defineProperty的使用
        this.model = Object.assign({},option.data)
        //拿到传入的元素的里面显示的所有的内容
        this.template = document.querySelector(option.el).innerHTML
        this.render()
        this. _obServer()
    }
    //观察者 观察data的数据变化
    _obServer(){
        let _this = this
        //遍历所有的属性
        for(let key in this.data){
            Object.defineProperty(this.data,key,{
                get(){
                    return _this.model[key]
                },
                set(v){
                    _this.model[key] = v
                    //渲染
                    _this.render()
                }
            })
        }
    }
    //渲染的方法
    render(){
        let _this = this
        //找到所有的{{}} 括起来的内容 进行替换
        this.el.innerHTML = this.template.replace(/\{\{\w+\}\}/g,(str)=>{
            //str {{msg}}
            let propertyName = str.substring(2,str.length-2).trim() //msg
            return _this.data[propertyName]
        })
        //找到对应el里面的input框
        Array.from(this.el.querySelectorAll('input'))
        .filter((input)=>{
            //找到input中是否具备v-model属性
            return input.getAttribute('v-model')
        })
        .forEach((input)=>{
            let _this = this
            //遍历所有的input框 (都是有v-model属性的)
            //获取对应的v-model的属性值
            let propertyName = input.getAttribute('v-model').trim()
            //给这个input添加onchange事件或者是oninput
            input.oninput = function(){
                //获取他的value 设置给对应的data里面的属性
                _this.data[propertyName] = this.value
            }
            //将对应的input的value 进行设置(对应的data里面属性的数据)
            input.value = this.data[propertyName]
        })
    }
}

diff 算法(脏检查器)

  • diff 算法用于比对新旧虚拟dom,采用深度优先,时间复杂度为O(n),利用patch补丁包的形式来进行重新渲染。
  • 先比对自身,通过key来找到自身 (key是唯一的下标不能作为key)
  • 再比对自身的属性,比对文本,再比对子元素,递归比到低
  • 实际DOM
<ul id="list">
    <li class="item">哈哈</li>
    <li class="item">呵呵</li>
    <li class="item">林三心哈哈哈哈哈</li> // 修改
</ul>
  • 虚拟DOM
  • 虚拟dom顾名思义就是虚拟的dom对象,这个虚拟的dom对象跟实体的dom对象没有直接的关系。
  • 如果直接操作实体dom会造成大量的重绘和回流(页面渲染次数增加,渲染速度就慢)。
  • 所以为了解决这个问题,vue就是先操作对应的虚拟dom,再通过虚拟dom渲染实体dom,这个渲染到实体dom的过程只会进行一次。
  • 虚拟dom在内存中的,所以渲染快,虚拟dom的形成是抽取对应的实体dom(模仿实体dom创建的对象)。
    let newVDOM = { // 新虚拟DOM
        tagName: 'ul', // 标签名
        props: { // 标签属性
            id: 'list'
        },
        children: [ // 标签子节点
            {
                tagName: 'li', props: { class: 'item' }, children: ['哈哈']
            },
            {
                tagName: 'li', props: { class: 'item' }, children: ['呵呵']
            },
            {
                tagName: 'li', props: { class: 'item' }, children: ['林三心哈哈哈哈哈']
            },
        ]
    }
  • 比对流程如下

深拷贝和浅拷贝

浅拷贝:创建快捷键

  • 浅拷贝不等于赋值,它会开辟一个新的内存空间,和原本的地址不一致,所以拷贝的对象和原本的对象不是一个对象
  • 浅拷贝里面的内容都是拷贝对应的地址,所以和原本的内容地址一致

浅拷贝的几种实现方法

对象拷贝:Object.assign

  • let copyObj = Object.assign ( { } , obj )
let obj = {
    user: {
        age: 18
    }
}
let newObj = obj
console.log(newObj == obj) //true 赋值的地址是共享的
//浅拷贝
let copyObj = Object.assign({}, obj)
//浅拷贝会产生一个新的对象 和原本的对象地址不一样
console.log(obj == copyObj) //false
//里面的内容的地址是共享的
console.log(obj.user == copyObj.user) //true
obj.user.age = 20
console.log(copyObj.user.age) //20

数组拷贝:concat、slice

  • let concatArr = [ ].concat ( arr )
  • let sliceArr = arr.slice ()
//使用数组的concat方法实现数组的浅拷贝
let concatArr = [].concat(arr)
console.log(concatArr == arr) //false
console.log(concatArr[0] == arr[0]) //true


//使用数组的slice方法
let sliceArr = arr.slice()
console.log(sliceArr == arr) //false
console.log(sliceArr[0] == arr[0]) //true

扩展运算符可以实现数组及对象的浅拷贝

  • let copyObj1 = { ...obj }
  • let copyArr = [ ...arr ]
//使用扩展运算符
let copyObj1 = {...obj}
console.log(copyObj1 == obj) //false
console.log(copyObj1.user == obj.user) //true
let arr = [{age:19},{name:'jack'}]
let copyArr = [...arr]
console.log(copyArr == arr) //false
console.log(copyArr[0] == arr[0]) //true

自定义函数 

        function clone(obj) {
            let copyObj = {}
            for (let key in obj) {
                copyObj[key] = obj[key]
            }
            return copyObj
        }
        let obj1 = {
            user: {
                age: 18
            }
        }
        let copyObj = clone(obj1)
        console.log(obj1 == copyObj); //false
        console.log(copyObj.user == obj1.user); //true

第三方插件 lodash.js (提供的clone方法):

  • <script src="https://www.lodashjs.com/"></script>
  • let cloneObj = _.clone ( obj1 )
<script src="https://www.lodashjs.com/"></script>
let obj1 = {
    user: {}
}
let cloneObj = _.clone(obj1)
console.log(cloneObj == obj1)//false
console.log(cloneObj.user == obj1.user)//true

深拷贝:文件复制粘贴

  • 拷贝的是对应的值,不拷贝地址。

【实现方式】

  • JSON.parse、JSON.stringify

let copyObj = JSON.parse ( JSON.stringify ( obj ) )

let obj = {list:['1','2'],user:{name:'tom'}}
let copyObj = JSON.parse(JSON.stringify(obj))
console.log(obj == copyObj) //false
console.log(obj.list == copyObj.list) //false
console.log(obj.user == copyObj.user) //false
console.log(obj.user.name == copyObj.user.name) //true
  • 使用 lodash.js 中的 _.cloneDeep 方法(第三方插件)

<script src="https://www.lodashjs.com/"></script>

let cloneObj = _.cloneDeep ( obj );

//使用lodash.js _.cloneDeep
let cloneObj = _.cloneDeep(obj);
console.log(obj == cloneObj) //false
console.log(obj.list == cloneObj.list) //false
console.log(obj.user == cloneObj.user) //false
console.log(obj.user.name == cloneObj.user.name) //true
  • 自定义递归书写对应的深拷贝 (重点)
        function deepClone(obj) {
            //先判断是否为函数,必须放在判断是否为对象之前
            if (typeof obj == 'function') {
                return obj.bind(this)
            }
            // 判断是否为对象以及排除null这个特殊情况
            if (typeof obj != 'object' || !obj) {
                return obj
            }
            // 判断是否为正则对象
            if (obj instanceof RegExp) {
                return new RegExp(obj)
            }
            // 判断是否为日期对象
            if (obj instanceof Date) {
                return new Date(obj.getTime())
            }
            // 判断是否为数组对象或者Object
            let newObj = obj.isArray ? [] : {}
            for (var key in obj) {
                // 利用递归
                newObj[key] = deepClone(obj[key])
            }
            return newObj
        }
        let obj = {
            name: {
                id: {
                    regexp: /\w+/,
                    type() {
                        console.log('你好')
                    },
                    likes: {
                        color: [1, 2, 3, 5]
                    }
                }
            }
        }
        let copyObj = deepClone(obj)
        console.log(copyObj == obj); //false
        console.log(copyObj.name == obj.name); //false
        console.log(copyObj.name.id == obj.name.id); //false
        console.log(copyObj.name.id.regexp == obj.name.id.regexp); //false
        console.log(copyObj.name.id.type == obj.name.id.type); //false
        console.log(copyObj.name.id.likes == obj.name.id.likes); //false
        console.log(copyObj.name.id.likes.color == obj.name.id.likes.color); //false
  • vue是一个mvvm的框架,vm是内置的,不需要你去管理。
  • vue的数据劫持是通过 Object.defineProperty (vue2) 重写了数组的7个方法(不能对于数组进行劫持) Proxy (vue3)。
  • vue主要劫持是data中的数据 (_data的属性)递归去进行劫持。
  • vue的双向数据绑定主要是通过数据劫持+obServer(观察者模式)。
  • vue是利用虚拟dom来进行对应的比对(里面采用diff算法)使用模板引擎进行解析渲染。
  • diff算法比对先比对自身(key),再比对对应的vnode,再比对对应的子节点(递归比对) 采用patch补丁包的形式来进行重新渲染。
  • 深拷贝拷贝的是值,浅拷贝拷贝的是地址,不管深浅拷贝都会产生一个新的对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值