美文网首页程序员
开始解析 vue 的源码(2) 单向绑定

开始解析 vue 的源码(2) 单向绑定

作者: zidea | 来源:发表于2019-03-27 19:41 被阅读11次
engineering-pictures-12.jpg

年初就有一个 idea 准备弄弄自己 javascript 框架,参考 vue 来做,Evan 也在开发者大会介绍 vue3.0 时候提到在新版 vue 中源码使用 typescript 编写,按模块划分,更适合开发者阅读源码。

先不源码自己根据 Evan 的思路先自己弄一弄,然后在看源码,这样留给自己思考的空间。先看 vue

目标: 进行不使用第三方库,不排除会使用 rxjs redux

搭建简单环境,创建一个项目目录```npm init -init``

在项目下
创建 src 用于存放源码
创建 examples 来测试框架小 demo
准备用 typescript 来写,所以初始化,为什么用 Typescript 呢?不是为了方便,是这样大型项目必须要的用一个类型系统呀,开始想用 ocaml 写了,ocaml 还不算熟,随着可以写一个 ocaml 的版本。暂时先用 typescript 弄。

tsc --init

index.html 文件,我们目的就是用变量的值将占位符内容替换调达到单向绑定。看似简单。


start.JPG
<div id="app">
    {{ message }}
</div>
<script src="./dist/zi.js"></script>
<script src="./app.js"></script>
  • zi.js 是编译后 zi 文件
  • app.js 开发应用文件
    我们一步一步地实现 vue 的功能,就是将 data 变量更新到html
new Zi({
    el: '#app',
    data: {
        message: 'Hello Zi.js!'
    }
})

先配置 tsconfig 文件,配置 typescript 如何编译为 javascript
tsconfig

{
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ], 
  "compilerOptions": {
    "target": "es5", 
    "lib": ["es2015","dom"], 
    "module": "commonjs",
    "allowJs": true,
    "outDir": "./examples/dist/",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true, 
  }
}
app.js:1 Uncaught ReferenceError: Zi is not defined
    at app.js:1

我们先解决 Zi 没定义问题,那么就创建一个 Zi 的类。

class Zi{
    
}

这里是传入一个 option 对象参数(context),我们用 tyepscript 定义一个 Option 的类型

new Zi({
    el: '#app',
    data: {
        message: 'Hello Zi.js!'
    }
})
interface Options {
    el:string,
    data:object
}
class Zi{
    constructor(opt:Options){
        console.log(opt)
    }
}
  • el: 是选择器,确定应用作用的 dom 的范围
  • data: 是数据,需要 data 数据渲染上去

如何将数据绑定到视图上,data binding,

  • el 属性读取该节点下 html 结构,然后生产一个虚拟节点树(vdom),这地方是重点,可以考虑先引进 handler 或者其他 vdom 的库
    1. 读取 html String template 返回一个方法,这里用的函数式编程 curry
  • 获取vmode 中数据绑定位置,然后将节点与vmode 对应上
  • 监控数据的属性,属性发生变化更新vmode对应节点进行局部渲染
export default class ZiHtml{
    constructor(){

    }
    compile(){
        console.log(111)
    }
}

compile 方法需要返回一个函数```function(context)

export default class ZiHtml{
    constructor(){

    }
    compile(htmlStr:string){
        console.log(111)
        return function(context:Object){
            //log
            let parser = new DOMParser()
            rootDom:HTMLDocument = parser.parseFromString(htmlStr, "text/xml");
            console.log(rootDom)
        }
    }
}
"module": "es2015", 
class ZiHtml{
    parser:DOMParser
    constructor(){
        this.parser = new DOMParser();
    }
     compile(htmlStr:string){
         var self = this;
        return function(context:Object){
            console.log(htmlStr);
            var doc = self.parser.parseFromString(htmlStr, "application/xml");
            console.log(doc)
        }
    }
}
// import ZiHtml from './ZiHtml';
class ZiHtml{
    parser:DOMParser
    constructor(){
        this.parser = new DOMParser();
    }
     compile(htmlStr:string,id:string){
         var self = this;
        return function(context: Dictionary<String>){
            console.log(htmlStr);
            var doc:HTMLDocument = self.parser.parseFromString(htmlStr, "application/xml");
            
            let str:string = doc.documentElement.innerHTML.trim();
            let replaceStr:string = "";

            Object.keys(context)
                .map(function(item){
                    console.log(context[item])
                    replaceStr = str.replace(item ,context[item]+"");
            })
            replaceStr = replaceStr.replace(/\{|\}/g,"");
            console.log("html string",replaceStr);
            doc.documentElement.innerHTML = replaceStr;
            let TargetDom:HTMLElement | null = document.querySelector(id);
            if(TargetDom != null){
                TargetDom.innerHTML = replaceStr;
            }
        }
    }
}
interface Dictionary<T>{
    [key: string]: T;
}

interface Binder {
    name:string,
    handler:Function
}

interface Options {
    el:string,
    data:Dictionary<string>
}
class Zi{
    html:ZiHtml;
    template:any;
    constructor(opt:Options){
        
        this.html = new ZiHtml()
        this.init(opt)
    }

    init(opt:Options){
        console.log(opt.el)

        let htmlDom:HTMLElement = <HTMLElement>document.querySelector(opt.el);
        let htmlStr:string = htmlDom.outerHTML;
        this.template = this.html.compile(htmlStr,opt.el);
        
        var obj:Dictionary<string> = opt.data;
        this.template(obj)
        Object.keys(obj)
            .map(function(item){
                console.log(obj[item])
            
        })

        
    }
}

result.JPG

扩展一下依旧有效,不错虽然粗糙但是方向应该是对的

 <div id="app">
        {{ message }}{{name}}
    </div>
new Zi({
    el: '#app',
    data: {
        message: 'Hello Zi.js!',
        name:"zidea"
    }
});
test.JPG

相关文章

网友评论

    本文标题:开始解析 vue 的源码(2) 单向绑定

    本文链接:https://www.haomeiwen.com/subject/iittbqtx.html