概述:

Vue是前端的一个js库(诞生于2015年 兴起于2016年 尤雨溪 (阿里巴巴)),vue是一个MVVM模式的框架。

MVVM概述

复制容器里面的文件到本地 拷贝内容_浅拷贝

  • model 数据
  • view 视图
  • viewmodel 视图模型(管理 数据驱动视图)

双向数据绑定的概述

  • 视图变化---数据也会变化
  • 数据变化---视图也会变化

双向数据绑定运用表单标签的

代码实现(v-model指令)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./lib/vue.js"></script>
</head>
<body>
    <div id="app">
        <!-- 视图的渲染的地方 -->
        <input type="text" v-model="msg">
        <!-- 插值表达式 可以获取data里面的数据 -->
        {{msg}}
    </div>
    <script>
        //这个vue.js文件导出的是一个构造函数 Vue
        let vm = new Vue({
            el:'#app', //你需要将视图挂载到哪
            data:{ //放数据的
                msg:'你好'
            }
        })
        console.log(vm)
    </script>
</body>
</html>

原生实现vue2的双向数据绑定

流程

  • 获取所有的input框
  • 过滤哪个input有个属性叫v-model
  • 遍历所有加了v-model属性的input
  • 找这个v-model属性的属性值 对应data里面的数据值
  • 给对应的input添加监听 监听input的value变化
  • 设置给对应的data
  • 使用Object.defineProperty 的set来监听数据的变化
  • 在数据变化的时候 重新渲染对应的input的值
  • 渲染应该解析对应{{}} 找到对应的属性名 进行替换

代码实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <!-- 视图的渲染的地方 -->
        <input type="text" v-model="msg">
        <!-- 插值表达式 可以获取data里面的数据 -->
        {{msg}}
        {{hello}}
        {{hello}}
    </div>
    <script>
        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]
                })
            }
        }
        //这个vue.js文件导出的是一个构造函数 Vue
        let vm = new Vue({
            el:'#app', //你需要将视图挂载到哪
            data:{ //放数据的
                msg:'你好',
                hello:'hello world'
            }
        })
    </script>
</body>
</html>

深拷贝和浅拷贝

浅拷贝

只拷贝第一层的值 其他拷贝是地址,相对于你拷贝一个拷贝快捷方式,这个新的快捷方式和原本的快捷方式不是一个,但是点进去的内容是一个。

Object.assign实现浅拷贝

let obj = {name:'jack',list:[1,2,3],params:{data:'ok'}}
let copyObj = Object.assign({},obj)
console.log(obj == copyObj)//false
console.log(obj.list == copyObj.list)//true

Array.prototype.concat (数组的浅拷贝)

//数组的浅拷贝
let arr = [{
    username:'jack',
    password:'123'
},
           {
               username:'tom',
               password:'456'
           }]
//使用concat连接
let copyArr = [].concat(arr)
console.log(copyArr == arr)//false
console.log(copyArr[0] == arr[0])//true

Array.prototype.slice (数组的浅拷贝)

//使用数组的截取来实现浅拷贝
let copyArr1 = arr.slice()
console.log(copyArr1 == arr)//false
console.log(copyArr1[0] == arr[0])//true

扩展运算符 ...

//使用扩展运算符
let copyArr2 = [...arr]
console.log(copyArr2 == arr);//false
console.log(copyArr2[0] == arr[0])//true

自定义函数实现浅拷贝

//函数实现浅拷贝
function copy(obj){
    //创建一个新的对象
    let constructor = obj.constructor
    let copyObj = new constructor()
    for(let key in obj){
        copyObj[key] = obj[key]
    }
    return copyObj
}
let copyArr3 = copy(arr)
console.log(copyArr3 == arr);//false
console.log(copyArr3[0] == arr[0])//true
let copyObj1 = copy(obj)
console.log(obj == copyObj1)//false
console.log(obj.list == copyObj1.list)//true

第三方工具(lodash.js) _.clone

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script>
    //浅拷贝 拷贝的对象 返回一个新的对象
    let obj2 = {name:'jack',list:[1,2,3],params:{data:'ok'}}
    let copyObj3 = _.clone(obj2)
    console.log(obj2 == copyObj3)//false
    console.log(obj2.list == copyObj3.list)//true
</script>

深拷贝概述:

深拷贝是拷贝所有的内容,这个俩个内容相同点只在于里面的值是相同的,其他引用地址都不一样。相当于你用u盘拷贝一份资料,那么这份资料跟拷贝那么那份资料是没有任何关系,但是里面显示的内容是一样的。

序列化和反序列化 (JSON.stringify JSON.parse)

//使用序列化和反序列化
let obj = {
    list:[{
        name:"苹果"
    },
          {
              name:"香蕉"
          }]
}
//进行深拷贝
let copyObj = JSON.parse(JSON.stringify(obj))
console.log(copyObj == obj);//false
console.log(copyObj.list == obj.list);//false
console.log(copyObj.list[0] == obj.list[0]);//false

使用第三方工具 lodash.js (_.cloneDeep)

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script>
    let copyObj1 = _.cloneDeep(obj)
    console.log(copyObj1 == obj);//false
    console.log(copyObj1.list == obj.list);//false
    console.log(copyObj1.list[0] == obj.list[0]);//false
</script>

自定义函数实现深拷贝(递归实现)

//自定义函数实现深拷贝(递归)
function deepClone(obj) {
    //判断当前你传入的参数是否是一个对象
    if (!(typeof obj == 'object' && obj)) {
        return obj
    }
    //如果他是数组 我就赋值为数组 如果是对象就赋值为
    let copyObj = obj instanceof Array ? [] : {}
    //遍历传入的对象
    for (let key in obj) {
        //将对应的内容进行赋值
        copyObj[key] = deepClone(obj[key])
    }
    return copyObj
}
let copyObj2 = deepClone(obj)
console.log(copyObj2);
console.log(copyObj2 == obj);//false
console.log(copyObj2.list == obj.list);//false
console.log(copyObj2.list[0] == obj.list[0]);//false

Vue原理介绍

虚拟dom

虚拟dom顾名思义就是虚拟的dom对象(对象),这个虚拟的dom对象跟实体的dom对象没有直接的关系。如果直接操作实体dom会造成大量的重绘和回流(页面渲染次数增加 渲染速度就慢)。所以为了解决这个问题,vue就是先操作对应的虚拟dom,再通过虚拟dom渲染实体dom(只有一次)。虚拟dom存在在内存中的,虚拟dom的形成是抽取对应的实体dom(模仿实体dom创建的对象)。

diff算法(脏检查器)

diff算法是用于比对的算法,他是比对虚拟dom,比对虚拟dom的内容的改变,通过虚拟dom比对的内容变化来控制实体内容变化(用于性能优化的)。diff算法是通过对应的节点来进行比对的(元素节点,属性节点,文本节点 vnode)。

节点比对流程

节点比对流程先比key(下标不能作为key(下标会变)),比对对应的节点名,比对属性名,比对子节点。patch的方法来帮你进行分类比较,比较完进行渲染(模板引擎 模板引擎mustache)。

相关优化

生命周期优化(预加载(挂载的时候)、缓存(组件销毁的时候)、回收资源(组件销毁的时候))

打包优化(webpack/glup(文件流))