先看框架使用的成果:Koojee,看不了網頁的請看截圖:

這是工程項目,鏈接那個是 Vue 的,截圖是 Mithril 的,效果是一樣一樣的。

先介紹一下 Mithril,一張圖說明問題:

有經驗的小夥伴應該很清楚關於 Vue 或者 React 以及 Angular 打包後包的大小,最小的 Vue 也要上 100K 以上。雖然有按需加載這種解決方案,但是,還是很大。。。

Mithril 的優勢之一就是小,就是輕,加載快,響應快,如上方的對比測試。

關於 Mithril 的優勢,主要是兩個(快的這個需要測試,請相信官網):

非常輕巧相當簡單

輕巧非常容易理解了,就是壓縮包只有 10K 不到,這個大小在當前的網絡環境中,幾乎就是秒加載,可以忽略不計的大小了。

所謂的簡單,本文將使用上述的工程例子來簡單說明,請小夥伴們跟緊了。

一般來說,一個 MVx 的 JS 框架到手,首先需要考慮的是數據驅動,向 Vue 的 data綁 定,React 的 setState 等,而 Mithril 相對這二者來說,更加簡單,就是普通的變量,恩,就是這種:var a = 1。然後在 Mithril 的 m 函數里面使用,變化就會直接使跟數據有關的 DOM 渲染了。

以下與工程無關的代碼將直接使用官網的例子。

var count = 0 // added a variablevar Hello = { view: function() { return m("main", [ m("h1", {class: "title"}, "My first app"), // changed the next line m("button", {: function() {count++}}, count + " clicks"), ]) }}m.mount(root, Hello)

上述代碼可以看到,count 只是普通的變量,然後 count 被用在一個 Hello 的組件裏面,然後點擊 button 就會造成 count + 1,從而使 Hello 組件渲染,效果如下:

Demo1

好了,數據驅動理解了,提一下 m 函數與 m.render 還有 m.mount 即可進入我們的項目了。

m 函數

m 函數就是 JS 生成 DOM 的函數,與h函數非常相似,h函數介紹 。

m 函數有三個參數:標籤名稱(支持 emmet),標籤屬性對象(一個 object 對象),標籤內容。

m 函數還有種參數:組件,組件的參數對象。

如:

m('div#main', {class: 'normal'}, '我是div')//

我是div
m('div#main', {class: 'normal'}, [m('button', '我是button')])//
// //
m([組件], [組件參數]) // 組件參數建議使用object

除了第一個參數,其餘兩個都可以爲空,就是不寫,然後第三個參數可以是單個 m 函數返回值也可以是數組。

m.render 函數

插入渲染結果的 DOM,插入的對象。

如:

m.render(document.body, "My first app")

就是 body 裏面插入一段字符。

這裏就是爲什麼我對MithrilJS念念不忘的原因,它不用強制生成個類似:

根結構的東西就可以直接渲染了。看什麼,說的就是你,Vue。

m.render 只渲染一次,後續如果有數據變動,也不會渲染,所以我們不用。

m.mount 函數

這個與 m.render 參數差不多。

m.mount(document.body, [生成的組件])

m.mount 生成的會多次渲染,這是首選,但是我們也不用,原因是我們的頁面是單頁面牽扯到頁面內路由,而 Mithril 提供了 m.route 這個路由啓動渲染,我們使用的是這個,具體會在項目代碼裏面說。

進入項目

index.html

WBRoom

這裏面是純原生,沒有使用 webpack 之類的打包,所以引用了很多組件 js,當然你也可以寫到一個裏面,但是那實在不好看也不好拓展。

使用了 less,爲了偷懶,你讓我在工程裏面使用原生 CSS,臣妾真的做不到了。

less

less 的內容就不做過多展示,就是堆頁面,這裏略過了。

js

大頭來了,我這裏會直接貼出代碼,然後在路由那裏說一下,爲什麼我不多說呢,因爲 Mithril 真的太簡單了(我會告訴你我懶得說麼)。。。

header.js

const Header = (function() { return { view(v) { const titles = ['首頁', '分享', '設計', '繪畫', '攝影'] const hrefs = ['#!/', '#!/share'] const selectedIndex = v.attrs.selectedIndex return m("header", [ m("h1", "KOOJEE"), m('section.titles', titles.map((title, index) => m('a', {class: 'title' + (selectedIndex === index ? ' selected' : ''), href: hrefs[index] || '#'}, titles[index]) ) ), ]) } }})()

這裏使用了自運行函數,爲了是避免變量名污染。

組件就是個對象,組件 DOM 結構實在 view 這個屬性值下的一個函數返回,函數可攜帶 vnode 對象,用來傳參等等,獲取參數使用v.attrs。

其他就是常規內容,渲染了一個header標籤,裏面包了一個 h1 標籤,包了一個 section.titles 標籤, section.titles 裏面根據titles數組生成了 5 個 a.title 標籤。

這就是 header 了。

footer.js

const Footer = (function() { return { view() { return m( 'footer' ) } }})()

目前沒東西,所以渲染了一個空的 footer 標籤用來佔位。

homepage.js

const Homepage = (function() { const h2 = '最終選擇了生活……' const h3 = '寧願向着遠方哭泣,不願望着當下詫異。' let contentImgs = ['Bitmap1.png', 'Bitmap2.png', 'Bitmap3.png', 'Bitmap4.png'] let arts = [ { img: 'Bitmap5.png', main: '經歷', desc: '我在東方等你,不濟那遠去的夕陽。', }, { img: 'Bitmap6.png', main: '興趣', desc: '給個遊戲,能躺半年。', }, { img: 'Bitmap7.png', main: '性格', desc: '熱辣似火,妖嬈弄人?不不不,就是呆萌。', }, { img: 'Bitmap8.png', main: '態度', desc: '你到底準備用什麼態度和姑奶奶說話?', }, ] const Content = { onbeforeremove(v) { v.dom.classList.add("exit") return new Promise(function(resolve) { setTimeout(resolve, 500) }) }, view() { return m('main#homepage.fancy', m('section.content-center', [ m('h2', h2), m('h3', h3), contentImgs.map((contentImg, index) => m('img', {class: 'img' + index, src: assetsPath + contentImg, alt: 'content-img'})) ]) ) } } return { view(v) { return [ m(Header, {selectedIndex: 0}), m(Content), m(Footer), ] } }})()

因爲將來要使用數據驅動,所以把數據提出去,方便後續服務器請求數據操作。

這裏使用了 m 函數的組件模式,如上例子中的:m(Header, {selectedIndex: 0}),就是 homepage 組件裏面包含了一個 Header 組件,並且給 Header 組件傳參了,這個參數的使用在上面 Header 組件那裏可以看到。

注意這裏有個內部的 Content 組件,這個是用來做動畫的,比如你頁面內路由切換時,爲了看起來更舒服,可以做個過渡動畫,Homepage 組件自帶 fancy 類的 CSS,然後 Homepage 的 Content 組件在聲明週期 onbeforeremove 即將要消失的時候添加了一個 exit 類的 CSS,具體兩個 CSS 如下:

.fancy {animation:fade-in 0.5s;}@keyframes fade-in { from {opacity:0;} to {opacity:1;}}.exit {animation:fade-out 0.5s;}@keyframes fade-out { from {opacity:1;} to {opacity:0;}}

瞭解 CSS 的同學就知道是什麼動畫了,對,就是淡入與淡出,分別 0.5s。

說到這裏,剛好順帶過一下 Mithril 的聲明週期,如下:

用法與 view 一樣,放在對象的屬性上,對應值是函數,都可以獲取 vnode。

oninit初始化時候,方便放一些準備用的數據,或者用來網絡請求。此時可以拿到 vnode,但是不一定拿得到真實 DOM,所以這裏不推薦進行相關的 DOM 操作,比如:vnode.dom。oncreate創建成功,此時可以拿到真實 DOM 了。onupdateDOM 渲染刷新後。業務有刷新變動數據時候使用。onbeforeremoveDOM 銷燬前。常用,比如我們的離開動畫。onremoveDOM 銷燬後。一樣不建議進行真實 DOM 操作,用來銷燬垃圾數據可以使用。onbeforeupdateDOM 渲染刷新前。業務有刷新變動數據時候使用。

share.js

const Share = (function() { let bannerSrc = 'https://wbroom-blog.oss-cn-hangzhou.aliyuncs.com/public/assets/share-bannre.png' const Content = { onbeforeremove(v) { v.dom.classList.add("exit") return new Promise(function(resolve) { setTimeout(resolve, 500) }) }, view() { return m('main#share.fancy', [ m(`img.share-banner[src=${bannerSrc}]`), m('section.arts', [1,1,1,1,1,1].map((item, index) => m('figure.art', [ m(`img.head[alt=${index}]`, {src: 'https://wbroom-blog.oss-cn-hangzhou.aliyuncs.com/public/assets/Bitmap1.png'}), m('figcaption.main', '別看,看也沒博文。'), m('span.time', '耶穌生日的那天'), m('section.ctrl', [ m('span', '點贊(1000)'), m('span', '評論(1000)'), m('span', '瀏覽(100000)') ]) ])) ), m('section.pagination', '頁碼(待處理)'), ]) } } return { view(v) { return[ m(Header, {selectedIndex: 1}), m(Content), m(Footer), ] } }})()

與 homepage 大同小異,只是堆頁面,放置一些圖片,頁碼也還沒做~

注意,一樣要設置進場動畫與離場動畫。

app.js

const rootPath = 'https://wbroom-blog.oss-cn-hangzhou.aliyuncs.com/'const publicPath = rootPath + "public/"const assetsPath = publicPath + "assets/"m.route(document.body, '/', { "/": Homepage, '/share': Share,})

這個就是常說的入口 js 了,由於存在 JS 對象的依賴關係,所以上面的組件不得不先加載,然後最後加載入口js。

第一:做一些全局的數據,比如本項目裏面需要用到的圖片資源 basic 路徑等。

第二:路由的簡單配置。

注意,Mithril 沒有類似 Vue 的 router-view 這種組件,它還是建議把組件配到大組件裏面,這個缺點就是如果業務的路由組件非常深入,就相對麻煩,但是優點還是簡單!

這裏配置根路徑地址就是 Homepage 組件,當路由切換到 share 時候,就是 Share 組件顯示了。

同樣的,MithrilJS的路由標記像這樣:#!/share,默認是使用#!,當然,你可以通過類似m.route.prefix("#")來修改路由標記,但是個人覺得意義不大。起碼別人逛你網站的時候,懂行的一眼就看出來你用的是 Mithril 寫的頁面,恩,厲害!

到最後,index.html 解析所有的內容後,就渲染出文章開頭的頁面了,點擊切換時候還有淡入淡出的效果(上面的鏈接看不到,因爲 Vue 版本我還沒加路……)。

OK,簡單,快速,小巧的一個牛逼純 JS 框架 Mithril,帶你們走了一遍簡單的。

Mithril 當然也是支持 ES6,以及 JSX 的,本人不是很喜歡 JSX 的寫法,所以採用了原生的,而要用以上兩個,都需要去配置下 webpack,這裏不做過多介紹了。

歡迎關注,如有需要 Web,App,小程序,請留言聯繫。

查看原文 >>
相關文章