关于Vue源码解析

v-model底层原理

非侵入式
是通过Object.defineProperty(obj,key,{set:,get:}) 是通过里面的get(getter),set(setter)来实现
get中通过dep.depend()来依赖收集,set中通过dep.notify()来触发数组更新
这个函数中含有get和set两个属性,当区访问key(value)时便会去调用get()和set()两个方法,getter和setter需要变量周转才能正常工作
import observe from ‘./observe.js’
function defineReactive(data,key,val){ //利用闭包属性来去操作(val是新值)
let child = observe(obj)
Object.defineproperty(obj,‘a’,{
get(){
console.log(‘a属性’)
return val //变量周转
}
set(newValue){
console.log(‘新a属性’,newValue)
if(val === newValue){return}
val = newValue
child = observe(newValue)
}

export const def = function(obj,key,value,enumerable){
Object.defineProperty(obj,key,{
value,enumerable,writable:true,configurable:true
})
}

import {def} from ‘./util.js’
import defineReactive from ‘./defineReactive.js’
export default class Observer{ //将一个正常的object转换为每个层级的属性都是响应式的
constructor(value){
def(value,‘ob’,this,false) //构造函数中this不是表示类本身而是表示实例,这是添加了__ob__属性(observer类的实例)
this.walk(value)
}
walk(value){ //遍历
for (let k in value){
defineReactive(value,k)
}
}
}
import Observer form ‘./Observer’
var obj = {a:{m:5},b:10}
function observe(value){
if(typeof value !=‘object’) return; //不是对象什么也不做
var ob;
if(typeof value.ob != ‘undefine’){
ob = value.ob
}
else{
ob = new Observer(value)
}
return ob;
}
observe(obj)

observe(obj)->看obj身上有没有__ob__->new Observer()将产生的实例添加到__ob__上->遍历下一层属性逐个defineReactive->回到observe(obj)上(这样递归的)
Object.defin eProperty()是为这个对象添加或者修改一个新属性类似于$set但是他能修改,有第二个参数的时候是可以修改
另外Object对象常用的还有.assign()这个之前用过,.keys().value() 一个获取键一个获取值

Vue.mixin({beforeCreate:function(){console.log(111)}}) 使用mixin在每个组件在调用这个
beforeCreate的时候都会打印111(全局监听更新),那你在某个里面单独写mixin不就是在这个里面重写了吗
除此之外还有Vue.component()创建一个全局组件

js代码始终比模板更有逻辑表达力,通过render方法可以像react那样去写具体的template
只要一个里的东西

vueRouter底层(就是v-model的道理)

vurRouter的封装也是通过Vue.util.defineReactive()//这个方法里面有Object.defineProperty
结合Mixin在每个组件beforeCreate的时候进行改变
在上面要做相应的window监听和location的操作
import vueRouter from ‘./vueRouter’
export default new vueRouter{里面是平时写的那些路由} //这边引入下面我们写的这个类

class vueRouter{
construction(options){
this.mode = options.mode||‘hash’
this.routes = options.routes||[]
this.routesMap = this.createMap(this.routes)
this.init()
}
init(){//构建一个类都有初始化的时候执行construction他的构造函数
if(this.mode === ‘hash’){
window.addEventListener(“load”,()=>{this.history.current=location.hash.slice(1)})
window.addEventListener(“hashchange”,()=>{this.history.current=location.hash.slice(1)}
监听加载时和路由哈希的变化
}
createMap(routes){
return routes.reduce((memo,current)=>{
memo[current.path]= current.component
return memo
})
}
}
vueRouter.install = function(vue){ //这就是通过前面说的通过install属性即可实现在use的时候调用
}
use()本质上就是执行这个install

Vue.mixin({
beforeCreate(){ // o p t i o n s 就 是 你 组 件 s c r i p t 里 的 选 项 i f ( t h i s . options就是你组件script里的选项 if(this. optionsscriptif(this.options&&this.options.router){ 页面先挂载router的是main,其他的话是没有的
this._root = this //_root就是this指向了
this._router = this.KaTeX parse error: Expected 'EOF', got '}' at position 218: …routes.history }̲else{ this._ro…parent._root //没有的话让这个挂载的组件指向父级组件,这样每层下面都添加了root并最终指向main里的router
最终的话不用在每个组件上去注册router,他最后都会从main里拿到
}
}
})
}
Vue.component(‘router-view’,{ //注册router-view组件
render(h){
let current = this._root._rou

Vue源码中的虚拟DOM和Diff算法

diff-different即精细化的最小量的更新
当改变dom的时候比如按钮点击改变,等等就会触发

vue底层会把dom先转换为js形式的虚拟dom,真实dom是通过模板编译原理转变为虚拟dom
{
“sel”:“div”,
“data”:{“class”:{“box”:true}},
“children”:[{sel:’’,data:{},“text”:‘this is tree’},{…}]
}
这样形式的虚拟dom

snabbdom最典型的虚拟dom,vue借鉴了其中的diff算法
从空白搭建环境,npm完后 新建一个webpack.config.js文件,当运行webpack-dev-server的时候会找该文件从而实现
搭建某个端口

diff是在新虚拟dom和老的虚拟dom进行diff,算出如何最小量更新最后反映到真是dom上

虚拟dom是被渲染函数(h函数)产生的
h函数
h函数用来产生虚拟节点
h(‘a’,{prop:{href:‘http://123’}},{“class”:{“box”:‘true’}},‘lalala’) //里面带n个参数
->转化为虚拟节点{“sel”:“a”,“data”:{props:{href:‘http://123’}},“text”:“lalala”}
->转化为真实节点lalala
一般的虚拟节点有children/data/elm/key/sel/text这些属性
。。h函数并不能真正的在页面上形成标签但是可以被console出来
我们需要通过patch函数来把节点展示出来,patch函数可以说是diff算法的核心函数具体的后面说
const patch = init(…,…,…)引用了三个包里的东西
patch(挂载真实的节点位置,虚拟节点) //这样就把h产生的虚拟节点挂到了真实节点上展现,但patch只能挂载一个

h函数可以嵌套使用从而得到dom虚拟树–h(‘ul’,[
h(‘li’,‘pingguo’),
h(‘li’,‘xiangjiao’)
])
转换成虚拟dom的都是h函数创建节点只不过是vue创建的不是你

h函数非常简单就是把传入的5个参数组合成对象然后返回

diff算法详细(实现一个点击按钮之后虚拟dom的改变)
const vnode1 =h(‘ul’,{},[
h(‘li’,{key:‘A’},‘A’),
h(‘li’,{key;‘B’},“B”) //加了key之后为算法提升了很多,这里才是key最本源关键的地方,该节点唯一标识
])
patch(container,vnode1); //containter是document拿到的挂载节点
const vnode2 =h(‘ul’,{},[
h(‘li’,{key:‘A’},‘A’),
h(‘li’,{key:‘B’},“B”),
h(‘li’,{key:‘C’},“C”)
])

btn.onclick = function(){ //btn同前理
patch(vnode1,vnode2)
}
你点击之后就是很明显的diff算法,他会改变不一样的
值得注意的几点1、key很重要2、同一个虚拟节点才会精细化比较,不然暴力拆解,再插入新的
3、只进行同层比较不进行跨层比较,不然还是暴力拆除

模板编译原理(上面真实dom转变为虚拟dom的条件)
模板引擎是将数据要变为视图最优雅的解决方案
之前的方法:纯dom方法,每个数据遍历出来之后都要用document那样加进去。
数组的join方法(曾经的)、es6的反引号法(就是把join法里的斩断那个换成模板字符串哎呀 )
vue的模板引擎引用了mustache模板引擎
就相当于v-for这种的 实现原理
我们讲mustache模板引擎的实现原理(在

这就是底层做的重点做的两个事***模板字符串编译成tokens结合数据解析成为dom字符串
tokens是模板字符串的js表现形式

  • {{#arr}} //这个就是mustache的用法
  • {{.}}
  • {{/arr}}
上述为模板字符串 编译为tokens后 [ ["text","
  • "], ["#",arr,[ ["text","
  • "], ["name","."], ["text","
  • "] ]] ["text","
"] ] 到这了下一步这玩意解析成为dom字符串 递归思想 export default function renderTemplate(tokens,data){ let resultStr = '' //遍历tokens for(let i = 0;i

export default function parseArray(token.data){
let v = lookup(data,token[1])
let resultStr = ‘’
for(let i=0;i<v.length;i++){
//他递归调用前面的(遍历数据)
resultStr+=renderTemplate(token[2],v[i])
}
return resultStr;
}

中间有个lookup函数因为a:{b:{c:100}} js可以获取a.b.c这样的但获取不了data[‘a.b.c’]这样的所以需要一个方法来
解决
export default function lookup(dataObj,keyName){
if(keyName.indexOf(’.’)!=-1){
let temp = dataObj;
let keys = keyName.split(’.’);
for(let i =0;i<keys.length;i++){
***temp = temp[keys[i]];
}
console.log(temp) //在循环外只输出一个,这样就把对象读出来了
return temp;
}
return dataObj[keyName] //如果上面的执行了return了就不会到这了相当于else
}

最后在index.html中
let domStr = xx.render(templateStr,data)
let container = document.getElementById(‘container’)
container.innerHTML = domsir
这样就实现了数据变为视图

其实整个的思路就是把dom对应弄出来然后把数据添加到对应位置(采用的字符串拼接)上
然后最后同意形成的数据以inner HTML值的形式嵌入进去

只有类才能创建一个新的实例,类都会有一个构造函数然后就是new一个实例之后各种指向什么的了

webpack-dev-server是webpack里类似于提供一个开发环境的东西,你在package.json里
“dev”:webpack-dev-server 运行时他会自己找项目中webpack.config.js文件读取其中devServer的配置,然后就跑起来了
这里我们配置一下
moudle.exports = {
mode:‘development’,
entry:’./src/index.js’,
output:{filename:‘bundle.js’}
},
devServer:{
contentBase:Path.join(_dirname,‘www’), //静态文件存放目录
port:8080,
publicPath:’/xuni/’ //虚拟打包的路径bundle.js没有真的生成
}
我们在www目录下的index.html中的 //这样虚拟打包完就可以显示出来而且是热更新,这就是
webpack-dev-server ,我们npm运行的就是这个帮助我们运行项目的也是这个

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值