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