<!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"></div>
<script type="module" src="app.js"></script>
</body>
</html>
/**
* vite npm init -y npm i vite -D vite 运行
*/
import { createApp } from '.mvvm'
import testA from './components/testA'
import testB from './components/testB'
const app = createApp({
components: [
testA,
testB
]
})
app.mount('#app')
/**
* testA
*
*/
import { createReactive } from '../..mvvm'
const template = `
<ul class='list'>
<h1>{{ title }}</h1>
{{dataTime}}
<for data="list" tag="li" class="item">
<span>姓名:{ name } </span>
<span>年龄:{ age }</span>
</for>
</ul>
`
function testA() {
const state = createReactive({
title: '学生信息列表',
dataTime: '2022-07-14 10:30',
list: [
{ id: 1, name: '爱辉', age: '18' },
{ id: 2, name: '黄勇', age: '28' },
{ id: 3, name: '钟文', age: '20' },
]
})
return [template, state]
}
export default testA
/**
* testB
*
*/
import { createReactive } from '../..mvvm'
const template = `
<ul class='list'>
<h1>{{ title }}</h1>
{{dataTime}}
<for data="list" tag="li" class="item">
<span>Name: { name } </span>
<span>age:{ age }</span>
</for>
</ul>
`
function testB() {
const state = createReactive({
title: '老师信息列表',
dataTime: '2022-07-14 10:30',
list: [
{ id: 1, name: '小红', age: '48' },
{ id: 2, name: '小名', age: '58' },
{ id: 3, name: '小李', age: '68' },
]
})
return [template, state]
}
export default testB
//mvvm.js
export { createApp } from './application'
export { createReactive } from './reactive'
//application.js
import { compileTemplate } from "./compile";
const domNodePool = []
export function createApp(options) {
for (let option in options) {
switch (option) {
case 'components':
initComponent(options[option]);
break;
default:
break;
}
}
return {
mount
}
}
function initComponent(components) {
for (let component of components) {
let [template, state] = component()
console.log(template, state)
const node = compileTemplate(template, state)
domNodePool.push(node)
}
}
function mount(el) {
const app = document.querySelector(el)
const oFrag = document.createDocumentFragment()
domNodePool.forEach(node => {
oFrag.appendChild(node)
})
app.appendChild(oFrag)
}
//compile.js
const customTags = ['if', 'for']
const reg_single_brancket = /\{(.*?)\}/g
const reg_double_brancket = /\{\{(.*?)\}\}/g
export function compileTemplate(template, data) {
template = replaceVar(template, data, reg_double_brancket)
const _node = document.createElement('div')
_node.innerHTML = template
return compileNode(_node, data)
}
function compileNode(node, data) {
const allNodes = node.querySelectorAll('*');
allNodes.forEach(item => {
const tagName = item.tagName.toLowerCase()
if (customTags.includes(tagName)) {
replaceNode(item, tagName, data)
}
})
return [...node.childNodes].find(item => item.nodeType === 1)
}
function replaceNode(node, tag, data) {
const dataKey = node.getAttribute('data')
const className = node.className
const realTag = node.getAttribute('tag')
switch (tag) {
case 'for':
vFor(node, data, dataKey, className, realTag);
break;
default:
break;
}
}
function vFor(node, data, dataKey, className, realTag) {
const oFrag = document.createDocumentFragment()
data[dataKey].forEach(item => {
// console.log(node, data, dataKey, className, realTag)
const el = document.createElement(realTag)
el.className = className || ''
el.innerHTML = replaceVar(node.innerHTML, item, reg_single_brancket)
oFrag.appendChild(el)
})
node.parentNode.replaceChild(oFrag, node)
}
function replaceVar(html, data, reg) {
return html.replace(reg, (node, key) => {
//replace 正则后接一个函数 参数为 1.匹配到的内容2子表达式3匹配的内容在字符串中出现的位置4.字符串本身
//$1 $2 ...todo...
const obj = {}
key = key.trim()
return obj[key] = data[key]//这里为什么要赋值一份呢?
})
}
//reactive.js
import { isObject } from './utils'
import { proxyHandler } from './proxyHandler'
export function createReactive(data) {
return createReactiveData(data, proxyHandler)
}
function createReactiveData(data, proxyHandler) {
if (!isObject(data)) return
return new Proxy(data, proxyHandler)
}
//proxyHandler.js
import { createReactive } from "./reactive"
import { isObject } from "./utils"
const get = createGetter()
const set = createSetter()
function createGetter() {
return function (target, key, receiver) {
const res = Reflect.get(target, key, receiver)
if (isObject(res)) {
return createReactive(res)
}
return res //此处必须return 否则会错误
}
}
function createSetter() {
return function (target, key, value, receiver) {
return Reflect.set(target, key, value, receiver)
}
}
export const proxyHandler = {
get,
set
}
//utils.js
export function isObject(data) {
return typeof data === 'object' && data != null
}