程序员的知识教程库

网站首页 > 教程分享 正文

深入浅出谈谈Vue 原理(简述vue原理)

henian88 2024-10-28 15:47:27 教程分享 4 ℃ 0 评论

首先大家都知道vue是一个MVVM 渐进式框架,MVVM是vue的设计模式,在vue框架中数据驱动视图。

1 MVVM设计模式

View是视图DOM;对应视图也就是HTML部分--代表UI组件,它负责将数据模型转化成UI展现出来。 Model是模型,就是vue组件里的data,或者说是vuex里的数据;--代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。 ViewModel--监听模型数据也就是data的的改变和控制视图行为、处理用户交互,就是一个同步Model以及View的对象,连接它们。

ViewModel之间并没有直接的联系,而是通过ViewMode进行交互,Model和ViewModel之间的交互是双向的,因此View数据的变化会同步到Model中,而Model数据的变化也会立即反应到View上。

2 响应式

响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty中的访问器属性中的 getset方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上

来看看vue如何监听data变化的

//触发更新视图
function updateView() {
    console.log('视图更新')
}
//重新定义数组原型
const oldArrayProperty = Array.prototypo
//创建新对象,原型指向oldArrayProperty,在拓展新的方法(这样不会影响原型)
let arrayProto = Array.prototype
let methods = ['pop', 'shift', 'unshift', 'sort', 'reverse', 'splice', 'push']
methods.forEach(methodName => {
    arrayProto[methodName] = function () {
        updateView ()
        oldArrayProperty[methodName].call(this,...arguments)
    }
})
//监听对象属性
function observer(target){
    if(typeof target !=='object' || target === null) {
        //不是对象或者数组
        return target
    }
    //重新定义数组原型
    if (Array.isArray(target)) {
        target.__proto__ = arrProto
    }
    
    //重新定义各个属性(for in 对象/数组都可以遍历)
    for(let key in target) {
        defineReactive(target,key,target[key])
    }
}
//重新定义属性,监听起来
function defineReactive (target, key, value){
    //递归深度监听
    observer(value)
    //核心API 
    Object.defineProperty(target,key,{
        get(){
            return value
        },
        set(newValue){
            if(newValue !== value) {
                 // 深度监听
                observer(newValue)
                //设置新值
                value = newvalue
                //触发更新视图
                updateView()
            }
        }
    })
}
// 准备数据
const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        address: '北京' // 需要深度监听
    },
    nums: [10, 20, 30]
}
 data.name = 'lisi' //视图更新
 data.age = 21      //视图更新
 console.log('age', data.age) //age 21
 data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
 delete data.name // 删除属性,监听不到 —— 所以有 Vue.dete
 data.info.address = '武汉' // 深度监听
 data.nums.push(4) // 视图更新

检测data变化的APIObject.defindeProperty

const data = {};
let name = "张三毛";

Object.defineProperty(data,'name',{
    get:function(){
        console.log('触发get')
        return name
    },
    set:function(newVal){
        console.log('触发set')
        name=newVal
    }
})

console.log(data.name)   // 触发get  张三毛
data.name = '王麻子'         // 触发set
实现数据的获取和赋值的监听

这样就实现数据的获取和赋值的监听了

3 渲染过程

3-1 第一次渲染

  • 解析模板为render函数(一般在开发环境已经完成,vue-loader)4
  • 触发响应式,监听data属性gettersetter(模板中使用到的变量会触发getter)
  • 执行render函数(触发getter),生成vnode,patch(elem,vnode)渲染到页面上

3-1 更新过程

  • 修改data的数据,触发setter(此前data数据在getter中已被监听)
  • 重新执行render函数,生成newVnode(新的虚拟dom)
  • 使用 patch(vnode,newVnode)更新到页面上




  • 1、编译模板生成render函数,生成vdom
  • 2、执行render函数,触发data中的getter
  • 3、getter方法收集依赖(通俗点就是,在模板里面触发了哪个变量的getter,就把哪个变量观察起来)
  • 4、在依赖中setter修改data中的数据的时候,Notify看下修改的那个数据是不是之前被观察起来的
  • 5、如果是之前观察起来的,就重新渲染( re-render),重新生成render函数,生成newVdom形成一个闭环

4 路由

分为hash以及 history

  • protocol - 协议
  • hostname - 主机名
  • port - 端口
  • pathname - url 路径
  • search - ?号之后的参数
  • hash - #号之后的部分



4-1 hash

  • hash 变化会触发页面跳转,即浏览器的前进,后退,hash 永远不会提交到server 端
  • hash 变化不会刷新页面,SPA(单页面)必须的特点
  • vue中就是通过hash 的变化触发路由的变化,来触发视图的渲染
<body>
    <p>
        hash路由
    </p>
    <button id='btn'>
        修改 hash
    </button>
</body>

<script>
 //hash 变化 包括;
    //a. js 修改URL
    //b. 手动修改url的hash
    //c.浏览器的前进、后退
    
    //页面初次加载获取hash
    window.addEventListener ('DOMCintentLoaded',() =>{
        console.log('hash',location.hash)
    })
    //hash变化触发
     window.onhashchange = (event) =>{
        console.log('hash',location.hash)
    }
     //js 修改 url
     document.getElementById('btn').addEventListener('click',()=>{
         location.href = '#/user'
     })
</script>

4-2 history

h5 history 主要是通过 history.pushState 跳转 和 window.onpopstate 监听页面的前进和后退

<body>
    <p>
       historyh路由
    </p>
    <button id='btn'>
        修改 url
    </button>
</body>

<script>
 
    //页面初次加载获取hash
    window.addEventListener ('DOMCintentLoaded',() =>{
        console.log('load',location.pathname)
    })

     //js 修改 url
     document.getElementById('btn').addEventListener('click',()=>{
         //pushState 有三个参数
         //第一个参数是个js对象,可以放任何的内容,可以在onpostate事件中(后面介绍)获取到便于做相应的、处理。
        //第二个参数是页面标题:目前所有浏览器都不支持,填空字符串即可
        //第三个参数是个字符串,就是保存到history中的url。
         let state= {
             title:'新页面'
         }
         history.pushState(state,'','user')
     })
    
    //监听浏览器的前进、后退
    window.onpostate = (event) => {
        console.log(event.state)  // {title:'新页面'}
        console.log(location.pathname)
    }
</script>

history 模式需要后端配合,就是无论用户访问什么路由,所有路由的切换都由前端来做,后端只需要返回index.html的文件,如果后面没有配置兼容,当访问user点击刷新,就会报错user页面NotFoud

互相学习,一起成长,分享更多技术文章!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表