年初就有一个 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 的库
- 读取 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
网友评论