摘要:暫時(shí)沒有指令和。當(dāng)前模塊內(nèi)的組件可以使用來自根模塊和當(dāng)前模塊的任何服務(wù)及組件,也可以使用被導(dǎo)入模塊中導(dǎo)出的組件。作為一個(gè)前端菜雞,還是在深知自己眾多不足以及明白好記性不如爛筆頭的道理下,多造輪子總歸不會(huì)錯(cuò)的。
有個(gè)同事跟我說:需求還是不夠多,都有時(shí)間造輪子了。。。 前言
這個(gè)輪子從18年4月22造到18年10月12日,本來就是看了一個(gè)文章講前端框架的路由實(shí)現(xiàn)原理之后,想試著擼一個(gè)路由試試,結(jié)果越寫越多,到最后就莫名其妙變成了個(gè)mvvm框架了。順便寫了個(gè)比較渣的文檔和服務(wù)端渲染。。。
當(dāng)前版本:v1.2.0
項(xiàng)目地址
文檔
npm
名字其實(shí)是瞎起的,因?yàn)榻M件要被包在一個(gè)div里,所以叫了InDiv。
整個(gè)項(xiàng)目是用typescript寫的,真心說一句ts真優(yōu)雅。
在思考怎么寫的時(shí)候參照了大量ng react vue的架構(gòu)與實(shí)踐,用自己能想到的最好的方法實(shí)現(xiàn)了一下,也算是對(duì)自己的鍛煉了一番(其實(shí)在寫的時(shí)候,發(fā)現(xiàn)越寫越像ng,可能是我真的太喜歡angular了吧)。
之后還實(shí)現(xiàn)了一個(gè)服務(wù)端渲染的,但是有點(diǎn)簡(jiǎn)陋。。。
此刻多么想致敬下三大框架的開發(fā)者大佬們,造輪子不易
主要分為模塊(NvModule),組件(Component),和服務(wù)。
模板使用字符串模板,我自己定義了一些例如:nv-class,nv-repeat等指令,然后再模板中僅僅可以使用來自組件實(shí)例中state的值($.)和實(shí)例的方法(@),所以顯得比較丑陋(先造出來再說)。
暫時(shí)沒有指令和pipe。其實(shí)在字符串模板的里可以使用組件上帶有返回值的方法(nv-src="@buildSrc($.src)"),返回值會(huì)被渲染到模板中,也算是暫時(shí)沒有做出pipe的補(bǔ)充。
自帶路由,采用基于virtual DOM的異步渲染,但是路由懶加載暫時(shí)還沒有。
模塊負(fù)責(zé)導(dǎo)入導(dǎo)出組件,導(dǎo)入其他模塊和注冊(cè)服務(wù)。當(dāng)前模塊內(nèi)的組件可以使用來自根模塊和當(dāng)前模塊的任何服務(wù)及組件,也可以使用被導(dǎo)入模塊中導(dǎo)出的組件。
如果沒有特殊聲明,在任何模塊中被聲明的服務(wù)將成為全局單例,但組件或服務(wù)只能注入當(dāng)前模塊內(nèi)的服務(wù)或來自根模塊的服務(wù);而在組件中被聲明的服務(wù)將跟組件實(shí)例走,每個(gè)組件實(shí)例都有一個(gè)獨(dú)立的服務(wù)實(shí)例。(其實(shí)是實(shí)現(xiàn)了個(gè)3級(jí)的注入器)。
組件實(shí)現(xiàn)了幾個(gè)生命周期,在ts里可以通過implements類型,而在js里只能手寫生命周期方法。
通過Object.defineProperty監(jiān)聽state,任何直接更改 state的屬性 及 通過setState更改state 的操作都為同步操作,會(huì)引起當(dāng)前組件的重新渲染;而在子組件中,通過調(diào)用props中父組件的方法去更改父組件state的時(shí)候,子組件不會(huì)立刻就得到更新后的props,因?yàn)殇秩緸楫惒降?,而且渲染之后才能得?b>propos。
因?yàn)槭褂?b>Object.defineProperty監(jiān)聽state,所以無法監(jiān)聽到state中數(shù)組item的增加插入移除,所以如果想更改數(shù)組結(jié)構(gòu)請(qǐng)只用setState重置state中的該項(xiàng)。
在ts中實(shí)現(xiàn)了依靠constructor的參數(shù)類型當(dāng)做令牌的依賴注入并通過@Injected聲明需要注入;在js中只實(shí)現(xiàn)了依靠靜態(tài)屬性injectTokens: string[]聲明字符串當(dāng)令牌的服務(wù)。
封了了axios作為http服務(wù),并在Utils類中集合了一些我平時(shí)用的工具。
使用 組件通過注解Component來提供元數(shù)據(jù),并聲明選擇器,模板,及組件providers
通過注解Injected來聲明下面的類需要注入服務(wù)
通過implements來實(shí)現(xiàn)生命周期鉤子函數(shù)
提供SetState, GetLocation, SetLocation等類型,在組件中可以使用ths.setState, this.getLocation, this.setLocation等內(nèi)置方法來改變狀態(tài)、獲取路由狀態(tài)、設(shè)置路由狀態(tài)等
如果使用JavaScript開發(fā),除了不能使用Injected聲明需要注入服務(wù)而通過類的靜態(tài)屬性injectTokens: string[]注入服務(wù)之外都差不多
import { Component, SetState, GetLocation, SetLocation, Injected, OnInit, RouteChange, OnDestory } from "indiv"; import TestService from "../../service/test"; @Injected @Component({ selector: "app-container-component", template: (`服務(wù)與依賴注入`), providers: [{ provide: TestService, useClass: TestService, }, // 也可以直接 TestService,TestService當(dāng)做令牌 ], }) export default class AppContainerComponent implements OnInit, RouteChange, OnDestory { public state: { showSideBar: string; testList: {id: number;name: string;}[]; } public setState: SetState; constructor( private testS: TestService, ) { this.subscribeToken = this.testS.subscribe(this.subscribe); } public nvOnInit() { this.state = { showSideBar: "open", testList: [ { id:0, name: "dima" }, { id: 1, name: "xxx" } ] }; } public nvRouteChange(lastRoute?: string, newRoute?: string): void {} public nvOnDestory() { this.subscribeToken.unsubscribe(); } public changeShowSideBar() { if (this.state.showSideBar === "open") { this.state.showSideBar = "close"; } else { // this.state.showSideBar = "open"; 也可以用setState this.setState({showSideBar: "close"}); } } } name:{{test.name}} id:{{test.id}}
跟angular的服務(wù)類似默認(rèn)為全局單例,但是可以在@Injectable({isSingletonMode: false})指定isSingletonMode為false,這樣該服務(wù)實(shí)例就不會(huì)在IOC容器內(nèi)創(chuàng)建出來,每次注入都會(huì)重新通過工廠函數(shù)創(chuàng)建個(gè)新的服務(wù)實(shí)例
通過注解Injected,服務(wù)也能被注入其他服務(wù)
推薦使用rxjs來實(shí)現(xiàn)組件通信
服務(wù)可以在組件,模塊中聲明,但是有些不同
模仿了ng的實(shí)現(xiàn)
import { Subject, Subscription } from "rxjs"; import { Injectable, Injected } from "indiv"; @Injected @Injectable() export default class TestService { public data: number; public subject: Subject模塊; constructor( private testService2: TestService2 ) { this.data = 1; this.subject = new Subject(); } public subscribe(fun: (value: any) => void): Subscription { return this.subject.subscribe({ next: fun, }); } public update(value: any) { this.subject.next({ next: value, }); } public unsubscribe() { this.subject.subscribe(); } }
模塊通過@NvModule裝飾器接收五個(gè)參數(shù),聲明某些組件(component)、服務(wù)(service)屬于這個(gè)模塊
模塊可以導(dǎo)出組件給其他模塊用
整個(gè)應(yīng)用需要一個(gè)根模塊,并且如果不使用路由則組要在根模塊定義bootstrap
import { NvModule } from "indiv"; import AppContainerComponent from "../pages/app.container.component"; import TestService from "../service/test.service"; import TestService2 from "../service/test2.service"; @NvModule({ imports: [], // 引入其他模塊 providers: [ { provide: TestService, useClass: TestService, }, TestService2, ], components: [ AppContainerComponent, ], exports: [ AppContainerComponent, ], bootstrap: AppContainerComponent, // 如果不適用路由需要在根模塊聲明bootstrap的組件 }) export default class AppModule { } import { InDiv } from "indiv"; const inDiv = new InDiv(); inDiv.bootstrapModule(AppModule); // inDiv.use(router); 使用路由 inDiv.init();生命周期鉤子
僅僅實(shí)現(xiàn)了下面這幾種,這里又大量借鑒了react。除此之外class的setter getter也可以當(dāng)做生命周期
constructor() nvOnInit(): void; nvBeforeMount(): void; nvAfterMount(): void; nvHasRender(): void; nvOnDestory(): void; nvWatchState(oldState?: State): void; nvRouteChange(lastRoute?: string, newRoute?: string): void; nvReceiveProps(nextProps: State): void;虛擬DOM
通過將DOM結(jié)構(gòu)轉(zhuǎn)化為VNode,并diff出差異并應(yīng)用在真實(shí)DOM上,其實(shí)也類似react的diff算法。
diff子元素
只diff同級(jí)子元素,禁止跨層級(jí)diff
優(yōu)先匹配新舊VNode中tagName和key都相同的元素,并計(jì)算位置差異
舊VNode中的子元素如果沒有匹配上則放入移除隊(duì)列
新VNode中的子元素如果沒有匹配上,則找到它的位置放入插入隊(duì)列
匹配到的兩個(gè)新舊VNode如果不是InDiv自定義的組件元素,則開始diff兩個(gè)匹配元素的屬性事件等并繼續(xù)diff下一層子元素
匹配到的兩個(gè)新舊VNode如果是InDiv自定義的組件元素,則跳過匹配下一層子元素,將diff交給組件的compiler
最后在每個(gè)組件的內(nèi)部統(tǒng)一update各個(gè)隊(duì)列,遵循先移除后插入再替換屬性等
to do支持自定義指令
路由懶加載
使用Proxy代替Object.defineProperty或?qū)崿F(xiàn)臟檢查取消state和props
@indiv/cli
最后關(guān)于其他字符串模板,http ,utils 路由等在文檔 里都有,文筆不好還請(qǐng)各位見諒(估計(jì)沒人能看懂)。
如果看不懂的話可以去看文檔的源碼,完全用indiv實(shí)現(xiàn)。(吹一波牛逼)
其實(shí)整個(gè)項(xiàng)目就是一時(shí)興起寫的,也沒有寫單元測(cè)試,估計(jì)bug不少。作為一個(gè)前端菜雞,還是在深知自己眾多不足以及明白好記性不如爛筆頭的道理下,多造輪子總歸不會(huì)錯(cuò)的。
最后感謝各位大佬看到最后
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/98351.html
摘要:寫在前面這個(gè)雖然功能少,但是知識(shí)點(diǎn)大部分都在這里面了,麻雀雖小五臟內(nèi)全應(yīng)用項(xiàng)目地址傳送門代辦事項(xiàng)地址在這里這是用寫的商城高仿商城地址在這里如果你覺的對(duì)你有幫助幫忙點(diǎn)個(gè)謝謝感謝大佬前端交流群志同道合的可以加下一起學(xué)習(xí)代碼預(yù)覽官方文檔我覺的官方 寫在前面 這個(gè)demo雖然功能少,但是vuex知識(shí)點(diǎn)大部分都在這里面了,麻雀雖小 五臟內(nèi)全應(yīng)用項(xiàng)目地址 傳送門 代辦事項(xiàng)地址在這里 這是用vue2...
摘要:微信搜索小程序查一查物流,或者掃一掃下圖,歡迎來回復(fù)分享哦。小程序用框架開發(fā)的,方便快捷,寫法類似,支持相關(guān)操作,已可以引入包,不過在微信開發(fā)者工具有以下注意事項(xiàng)。對(duì)應(yīng)關(guān)閉轉(zhuǎn)選項(xiàng),關(guān)閉。對(duì)應(yīng)關(guān)閉上傳代碼時(shí)樣式自動(dòng)補(bǔ)全選項(xiàng),關(guān)閉。 微信搜索小程序 查一查物流,或者掃一掃下圖,歡迎來回復(fù)分享哦。 showImg(https://segmentfault.com/img/bVbiR2p?w=...
摘要:微信搜索小程序查一查物流,或者掃一掃下圖,歡迎來回復(fù)分享哦。小程序用框架開發(fā)的,方便快捷,寫法類似,支持相關(guān)操作,已可以引入包,不過在微信開發(fā)者工具有以下注意事項(xiàng)。對(duì)應(yīng)關(guān)閉轉(zhuǎn)選項(xiàng),關(guān)閉。對(duì)應(yīng)關(guān)閉上傳代碼時(shí)樣式自動(dòng)補(bǔ)全選項(xiàng),關(guān)閉。 微信搜索小程序 查一查物流,或者掃一掃下圖,歡迎來回復(fù)分享哦。 showImg(https://segmentfault.com/img/bVbiR2p?w=...
閱讀 2224·2021-09-09 09:33
閱讀 1168·2019-08-30 15:43
閱讀 2737·2019-08-30 13:45
閱讀 3361·2019-08-29 11:00
閱讀 996·2019-08-26 14:01
閱讀 3636·2019-08-26 13:24
閱讀 548·2019-08-26 11:56
閱讀 2759·2019-08-26 10:27