概念
使用步骤
1.编写 页面 模板
1.直接在HTML标签中写
2.使用template
3.使用单文件(<template>)
2.创建Vue实例
1.在Vue 的构造函数中:data,methods,computer,watcher,props,...
3.将Vue挂载到页面中(mount)
数据驱动模型
Vue执行流程
1.获得模板:模板中有‘坑’
2.利用Vue构造函数提供的数据来‘填坑’,就可以得到页面显示的'标签'了
3.替换原来有坑的标签
Vue 利用 我们提供的数据 和 页面的 模板 生成了一个新的HTML标签(node元素),替换到了 页面中 放置模板的位置
虚拟DOM
目标:
1. 怎么将真正的DOM转换为虚拟DOM
2.怎么将虚拟DOM转换为真正的DOM
思路与深拷贝类似
概念
1.柯里化: 一个函数原本有多个参数 只传入一个参数生成一个新函数 ,由新函数接受到新的参数运行得到的结构
2.偏函数: 参考柯里化, 传入一部分参数
3.高阶函数: 一个函数参数是一个函数,该函数对参数这个函数进行加工,得到一个函数,这个加工用的函数就是高阶函数
为什么要使用柯里化
为了提升性能 使用柯里化可以缓存一部分能力
使用两个例子说明
1.判断元素
Vue 本质上是使用HTML的字符串作为模板,将字符串的 模板 转换为AST 再转换为VNode
1.模板-AST
2.AST-VNode
3.VNode-DOM
最消耗性能的
是模板-AST
例子 字符串 1 + 2 * ( 3 + 4 ) 解析该表达式,得到结果
一般将此转换为 ‘波兰式’ 表达式 然后用栈进行运算
在Vue中每一个标签可以是真正的HTML标签,也可以是自定义组件,怎么区分
在vue源码中,将所有可用的HTML标签 已经存起来,
假设这里只考虑 几个标签
```js
let tags = 'div,p,a,img'.split(',')
```
一个函数,判断标签名是否为 内置标签
```js
function isHTML( tagName ){
tagName = tagName.toLowerCase()
//tags.indexOf(tagName) >-1 return true
for(let i=0;i<...){
if(tagName === tags[i])return true
}
return false
}//也可以用indexOf判断
```
模板是任意编写的,可以写的很简单,也可以写的很复杂,indexOf内部也要循环
如果有6个内置标签 模板有10个,就得循环60次
使用柯里化
```js
let tags = 'div,p,a,img'.split(',')
function makeMap( keys ) {
let set = {}
keys.forEach(key => {
set[key] = true
});
return function ( tagName ) {
//!!改为boolean
return !!set[ tagName.toLowerCase() ]
}
}
let isHTML = makeMap( tags )
//不用再做循环
```
2.虚拟DOM 的render
vue 项目 模板 转换为AST 转换几次?
1.页面加载渲染 一次
2.每一个属性(响应式)数据发生变化 要渲染
3.watch computed 等等
render的作用是将 虚拟DOM 转换为 真正的DOM 加载到页面中
一个项目运行时 模板不会变 抽象语法树不会变
我们可以将代码优化 将虚拟DOM 缓存起来 生成函数 函数只需要传入数据 得到真正的DOM
源码模拟与分析
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<div>h1</div>
<div>h2</div>
<div>h3</div>
</div>
<div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
</div>
</body>
<script>
class VNode{
constructor(tag, data, value, type,){
this.tag = tag && tag.toLowerCase()
this.data = data
this.value = value
this.type = type
this.children = []
}
appendChild( vnode ){
this.children.push(vnode)
}
}
function getVNode( node ) {
let nodeType = node.nodeType
let _vnode = null
if(nodeType === 1 ){
let nodeName = node.nodeName
let attrs = node.attributes
let _attrObj = {}
for (let i = 0; i < attrs.length; i++) {
_attrObj[ attrs[i].nodeName ] = attrs[i].nodeValue
}
_vnode = new VNode(nodeName, _attrObj, undefined, nodeType)
let childNodes = node.childNodes
for (let i = 0; i < childNodes.length; i++) {
_vnode.appendChild( getVNode(childNodes[i] ) )
}
}else if(nodeType === 3 ){
_vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)
}
return _vnode
}
let app = document.querySelector('#app')
let vapp = getVNode(app)
console.log( vapp )
function parseVNode( vnode) {
let type = vnode.type
let _node = null
if( type === 3){
return document.createTextNode( vnode.value )
}else if( type === 1 ){
_node = document.createElement( vnode.tag )
let data = vnode.data
Object.keys(data).forEach( (key) => {
let attrName = key
let attrValue = data[key]
_node.setAttribute( attrName, attrValue)
})
let children = vnode.children
children.forEach( subvnode => {
_node.appendChild(parseVNode(subvnode))
})
}
return _node
}
let dom = parseVNode(vapp)
console.log(dom)
</script>
</html>
vue源码 虚拟dom对象
从new vue 到 虚拟dom 到 渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<div>{{name}}</div>
<div>{{age}}</div>
<div>{{sex}}</div>
</div>
<div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
</div>
<script>
class VNode {
constructor(tag, data, value, type,) {
this.tag = tag && tag.toLowerCase()
this.data = data
this.value = value
this.type = type
this.children = []
}
appendChild(vnode) {
this.children.push(vnode)
}
}
function getVNode(node) {
let nodeType = node.nodeType
let _vnode = null
if (nodeType === 1) {
let nodeName = node.nodeName
let attrs = node.attributes
let _attrObj = {}
for (let i = 0; i < attrs.length; i++) {
_attrObj[attrs[i].nodeName] = attrs[i].nodeValue
}
_vnode = new VNode(nodeName, _attrObj, undefined, nodeType)
let childNodes = node.childNodes
for (let i = 0; i < childNodes.length; i++) {
_vnode.appendChild(getVNode(childNodes[i]))
}
} else if (nodeType === 3) {
_vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)
}
return _vnode
}
function getValueByPath(obj, path) {
let paths = path.split('.')
let res = obj
let prop
while (prop = paths.shift()) {
res = res[prop]
}
return res
}
function combine(vNode, data) {
let _type = vNode.type
let _data = vNode.data
let _value = vNode.value
let _tag = vNode.tag
let _children = vNode.children
let _vnode = null
let rex = /\{\{(.+?)\}\}/g
if (_type === 3) {
_value = _value.replace(rex, function (_, g) {
let path = g.trim()
let value = getValueByPath(data, path)
return value
})
_vnode = new VNode(_tag, _data, _value, _type)
} else if (_type === 1) {
_vnode = new VNode(_tag, _data, _value, _type)
_children.forEach((_subVNode) => {
_vnode.appendChild(combine(_subVNode, data))
});
}
return _vnode
}
function parseVNode(vnode) {
let type = vnode.type
let _node = null
if (type === 3) {
return document.createTextNode(vnode.value)
} else if (type === 1) {
_node = document.createElement(vnode.tag)
let data = vnode.data
Object.keys(data).forEach((key) => {
let attrName = key
let attrValue = data[key]
_node.setAttribute(attrName, attrValue)
})
let children = vnode.children
children.forEach(subvnode => {
_node.appendChild(parseVNode(subvnode))
})
}
return _node
}
function MVue(options) {
this._options = options
this._data = options.data
let elm = document.querySelector(options.el)
this._template = elm
this._parent = elm.parentNode
this.mount()
}
MVue.prototype.mount = function () {
this.render = this.createRenderFn()
this.mountComponent()
}
MVue.prototype.createRenderFn = function () {
let ast = getVNode(this._template)
return function render() {
let _temp = combine(ast, this._data)
return _temp
}
}
MVue.prototype.mountComponent = function () {
let mount = () => {
this.update(this.render())
}
mount.call(this)
}
MVue.prototype.update = function (VNode) {
let realDom = parseVNode(VNode)
this._parent.replaceChild( realDom, document.querySelector('#app'))
}
let app = new MVue({
el: '#app',
data: {
name: 'xa',
age: 12,
sex: 'man'
}
})
</script>
</body>
</html>