很長的一段時間中,Vue 官方都以簡單上手作為其推廣的重點。這確實給 Vue 帶來了非常大的用戶量,尤其是最追求需求開發效率, 往往不那么在意工程代碼質量的國內中小企業中,Vue 占據的份額極速增長。但是作為開發者自身,我們必須要認清一個重點,簡單易用從來不應該在技術選型中占據很大的份額,可維護性才是。
以防萬一有的同學實在不看官方文檔,我先提一嘴,SFC 就是寫 Vue 組件的時候寫的.vue文件,這一個文件就是一個 SFC,全稱 Single File Component,也即單文件組件。
在開始說我個人的觀點之前,我們先來看幾個事實:
一是:Vue3 的定義原生支持 JSX,并且 Vue3 源碼中有jsx.d.ts來便于使用 JSX。 不知道同學們看到這里會想到什么, 我的第一反應是:社區對于 JSX 的需求聲音是不小的,所以會反向推動 Vue3 官方對于 JSX 的支持。
二是:AntDesign 的 vue3 版本,基本全部都是用 JSX 開發的,而且 Vue3 現在官方的 babel-jsx 插件就是阿里的人一開始維護的, 雖然我向來不喜歡阿里系的 KPI 推動技術方式,而且現在的 JSX 語法支持也不是很符合我的期望,但至少在使用 JSX 開發是更優秀的選擇這點上,我還是很認可 AntDesign 團隊的。
OK,說這些呢,主要是先擺出一些事實作為依據,讓有些同學可以不需要拿什么:
這些觀點來批斗我,首先我都會從客觀的角度來分析為什么,至少是我是能講出優劣勢的理由的。
OK,前言差不多到這里,接下來咱給您分析分析,為什么你應該選擇 JSX 來開發 Vue。
其實第一點就已經是殺手了,對于想要使用 TypeScript 來開發 Vue3 應用的同學來說,這簡直就是 SFC 無法克服的世界難題。
一句話概括:TypeScript 原生支持 JSX 語法,而基本無望 TS 官方能支持 SFC 的 template 語法。
TS 毫無疑問在前端社區的重要性越來越大,但凡未來對于代碼質量有一定要求的前端團隊,都應該會選擇使用 TS 來進行開發。 而且現在基本上在 NPM 上都能看到包你都能找到對應的 TS 定義,現在使用 TS 開發成本已經只剩下你是不是會 TS 語法了,在這種情況下是否支持 TS 則是開發模式在未來走不走的遠的重要原因。
目前 SFC 只能通過shim讓 TS 可以引入.vue文件,但是對于所有 SFC 的組件的定義都是一樣的:
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, {}, any>
export default component
}
也就是說你引入的 SFC 組件,TS 是不知道這個組件的 Props 應該接收什么的。所以你無法享受到這些 TS 的優勢:
當然你會說既然 Vue 官方能開發處 SFC 的語法,自然會支持這些特性。我表示這當然有可能,但是這個難度是非常大的,需要很多方面的支持,甚至可能需要 TS 官方團隊愿意協助, 但是我想不到 TS 官方有什么理由來支持 SFC,因為這只是 Vue 自己創建的方言,在其他場景下是沒有使用的,TS 是面向全社區的,我覺得他們不會考慮主動來支持 SFC。
那么有同學要問了,JSX 不也是非原生的 JS 語法么,他怎么就能讓 TS 官方支持了呢,是不是 FB 和微硬之間有什么 PY 交易?
這就涉及第二點了,JSX 和靜態模板的靈活性區別。
很多人弄錯了一個問題,就是覺得 SFC 的模板語法和 JSX 是一樣的,都是一種別人發明的語法,并不是 JS 原生的。這是事實,但又有一些區別,這個區別主要是體現在對于 JSX 的認知上。
一句話概括:JSX 并沒有擴展 JS 的語法,他只是縮略了 JS 的寫法!其本質就是 JS 的語法糖
就像 es6 給增加的語法糖,比如
const a = 1
const b = 2
const obj = { a, b }
// 其實就等價于
const obj = { a: a, b: b }
這種寫法并沒有擴展 JS 的能力,只是簡便了寫法,JSX 也是一樣的。
JSX 其實就是方法調用,他和 JS 是有一對一對應關系的,我們來看一個例子:
const element = <div id="root">Hello World</div>
這里的 JSX 語法編譯之后其實就是:
const element = createElement('div', { id: 'root' }, 'Hello World')
而 JSX 就是這些了,沒有什么更多的內容,所以說 JSX 只是方便我們寫嵌套的函數調用的語法糖,而其本身沒有擴展任何其他的內容。
但是 SFC 就不一樣了。
SFC 定義的不僅是語法,更是文件。
SFC 的具體定義是單文件組件,它本身就是把一個文件看作一個單位,所以他的約束性是要大很多的,你必須具有固定的文件結構才能使用 SFC,這做了很多的限制:
我們一點點來講
這個說實話非常非常不方便,很多時候我們寫一個頁面的時候其實經常會需要把一些小的節點片段拆分到小組件里面進行復用(如果你現在沒有這個習慣可能就是因為 SFC 的限制讓你習慣了全部寫在一個文件內)。
React 生態中豐富的 css-in-js 方案就是很好的例子,我們可以通過:
const StyledButton = styled('button', {
color: 'red',
})
如果我們這個頁面需要使用特定樣式的按鈕,通過這種方式在頁面文件里面封裝一下是非常常見的。因為沒必要把這個組件拆分出去,他也不是一個可復用的組件,拆分出去了還要多一次import。
Vue 生態基本沒有 css-in-js 的成熟方案其實跟這個限制也很有關系。
再來一個例子,比如我們封裝了一個 Input 組件,我們希望同時導出 Password 組件和 Textarea 組件來方便用戶根據實際需求使用,而這兩個組件本身內部就是用的 Input 組件,只是定制了一些 props:
const Input = { ... }
export default Input
export const Textarea = (props) => <Input multiline={true} {...props} />
export const Password = (props) => <Input type="password" {...props} />
在 JSX 中可以非常簡單地實現,但是如果通過 SFC,你可能就要強行拆成三個文件,另外為了方便,你可能還要增加一個index.js來導出這三個組件,你能想象這多了多少工作量么。
我不知道有多少同學看過 Vue 的 template 編譯出來之后的代碼,以我的經驗來說看過的可能不會超過 50%(樂觀估計),建議同學們如果還不了解的,可以去嘗試看一下。
為什么要看這個呢?因為你看了之后你會發現,你在 template 里面寫的類似 HTMl 的內容,其實跟 HTML 根本沒啥關系,他們也會被編譯成類似 JSX 編譯出來的結果。
{
render(h) {
return h('div', {on: {}, props: {}}, h('span'))
}
}
類似這樣的結果,而這里面h函數調用的結果就是一個 VNode,是 Vue 中的節點的基礎單元。那么既然這些單元就是一個對象,其實理所當然的,他們是可以作為參數傳遞的。 也就是說,理論上他們是可以通過props把節點當作參數傳遞給其他組件的。
這個做法在 React 中非常常見,叫做renderProps,并且其非常靈活:
const Comp = () => <Layout header={<MyHeader />} footer={<MyFooter />} />
但是因為 SFC 模板的限制,我們很難在 SFC 里面的 props 上寫節點:
<template>
<Layout :header="<MyHeader/>"></Layout>
</template>
這樣寫是不行的,因為 SFC 定義了:header綁定接受的只能是 js 表達式,而<MyHeader/>顯然不是。
因為通過 props 傳遞不行,所以 Vue 才發明了 slot 插槽的概念
雖然我們一直再說 Vue 簡單,但是事實上ScopedSlots一度成為新手理解 Vue 的噩夢,很多同學都被這個繞來繞去的作用域整的死去活來。
我們看一個ScopedSlots的例子:
<template>
<Comp>
<template v-slot:scope="ctx">
<div>{{ctx.name}}</div>
</template>
</Comp>
</template>
這里ctx是Comp里面的屬性,通過這種方式傳遞出來,讓我們在當前組件可以調用父組件里面的屬性。這簡直就是理解的噩夢,但是如果用 JSX 實現類似功能就非常簡單:
<Comp scope={name => <div>{name}</div>} />
我們只是給一個叫做scope的 props 傳遞來一個函數,這個函數接受一個name屬性,在Comp里面會調用這個函數并傳入name。 簡單來說我們傳入的就是一個構建節點片段的函數,就是這么簡單。
這就是因為 SFC 的模板的限制,導致靈活性不足,Vue 需要去創造概念,創造關鍵字來抹平這些能力的不足,而創造的概念自然就引入了學習成本。
所以其實我一直不認可 Vue 比 React 好學的說法的,如果你真的認真研究所有用法,并且總是嘗試用最合理的方式實現功能,那么 Vue 絕對不會比 React 簡單。
這個體現在兩個方面,一個是我們定義在全局的一些固定數據如果要在組件內使用的話,就要通過this掛載到組件上。
比如我們緩存了一份城市數據,這種數據基本上是不會改的,所以也沒必要掛載到組件上讓其能夠響應式。但是在 SFC 里面這是做不到的, 因為模板的執行上下文是在編譯時綁定。你在模板里面訪問的變量,都會在編譯時自動綁定到this上,因為模板需要編譯,其本身也是字符串不具有作用域的概念。
而這在 JSX 中則不復存在:
const citys = []
const Comp = () => {
return citys.map(c => <div>{c}</div>)
}
另外一個方面則是在組件使用上,在 SFC 中,組件必須事先注冊,因為我們在模板里面寫的只能是字符串而不能是具體某個組件變量。 那么模板中的組件和真實的組件對象只能通過字符串匹配來實現綁定。這帶來了以下問題:
在 JSX 中則沒有這些問題,因為 JSX 里面直接使用組件引用作為參數:
const Comp = {...}
const App = () => <Comp />
其實上面能看出來,除了 SFC 本身的問題之外,Vue 使用字符串模板也會帶來很多的靈活性問題。 最直接的證據,就是 Vue 使用了directive來擴展功能(當然這不是 Vue 發明的,老早的模板引擎就有類似問題)。
為什么說directive是不得已的選擇呢?因為靜態模板缺失邏輯處理的能力。我們拿列表循環舉例,在 JS 中我們可以非常方便地通過map函數來創建列表:
const list = arr.map(name => <span key={name}>{name}</span>)
而因為 JSX 本身就是函數調用,所以上面的代碼和 JSX 結合起來也非常自然:
const App = () => (
<div>
<Header />
{arr.map(name => (
<span key={name}>{name}</span>
))}
</div>
)
上面的例子對應到 JS 如下:
const App = () =>
createElement('div', {}, [
<Header />,
arr.map(name => createElement('span', { key: name }, name)),
])
這仍然是因為 JSX 只是 JS 的語法糖的原因,所有能在 JS 中實現的在 JSX 里面都能實現。
而 SFC 的模板是基于字符串編譯的,其本身就是一段字符串,我們不能直接在模板里面寫map來循環節點,(當然我們可以在可以接收表達式的地方寫,比如v-on里面)。
那么我們不能循環節點,有需要這樣的功能來渲染列表,怎么辦呢?就是發明一個標志來告訴編譯器這里需要循環,在 Vue 中的體現就是v-for指令。
同學們可能要問了,既然 Vue 能實現v-for,為什么不直接實現表達式循環列表呢?他當然也可以實現,但是他肯定不會這么選,因為成本太高了。 他要這么做就相當于他要實現一個 JS 引擎,而其實里面很多內容又是不必須的,一個v-for其實就能夠適用大部分情況了。
但有了v-for就需要v-if,那么后面還會需要其他各種能力,這就是一種方言的產生和發展的過程。
當然指令也不僅僅是 JS 表達式的代替品,其本身也是增加了一些其他能力的,比如它能夠讓我們更方便地訪問 DOM 節點, 但是嘛,我們用框架的理由不就是為了能夠盡可能的屏蔽 DOM 操作嘛~
以上就是我對應該選擇使用 JSX 還是 SFC 進行開發的分析,其實歸根到底 SFC 的問題在于其沒有擁抱 JS, 他的語法是自己發明的,他需要有一個 JS 實現的 compiler 來讓其最終能在 JS 環境中運行,這本質上就是一種發明, 我們不能否認發明確實有優點,但我們也不能只看有點不看問題,沒能擁抱 JS 自然就很難完全復用 JS 社區的優勢 而 JS 社區一直在蓬勃發展,好用的工具一直在涌現,而 SFC 想要使用 JS 社區的這些工具還要自己再實現一份,我們可以細數以下 SFC 做了哪些兼容
基本上常用的工具我們都需要等待 Vue 社區或者官方開發了插件之后才能運行。而 JSX 因為有 babel 和 typescript 的官方支持, 基本上所有新的 JS 生態工具原生都是支持的。
在這 Vue3 開始預備發力的階段,我們還是希望 Vue 社區能夠使用更優秀更規范的方式來進行開發, 其實如果我們直接使用 JSX 開發 Vue3,我們會發現很多時候我們都不需要用到emit、attrs這些概念, 甚至如果 Vue3 的 JSX 插件支持,我們甚至能夠拋棄slots。
但是因為 Vue3 一定要考慮兼容 Vue2,導致本身潛力很好的 Vue3 總是顯得縮手縮腳,這不得不說是一種遺憾。
學習如何做一個網站,卻又不知道從哪里下手,但是又不想花錢去找外包公司,那么自己到底該怎么做一個網站呢?
今天我來給大家說明一下想自己做個網站需要學會什么東西。
1:html
想自己做個網站,首先得把網站的模板做出來,而html就是就是做網站要學習的。比如網頁上要顯示內容,需要html;網站上需要顯示圖片,需要html;網站上需要文字,需要html。
2:css
光有文字還是不行,還需要考慮布局、排版和顏色,這時候就需要學習css了。雖然現在有css3可以制作動圖和特效,但是css的基本用處還是布局、排版和顏色。
3:js
弄完內容和排版布局,很多人認為網站的一個頁面就這么搞定了,但是大錯特錯,我們還需要js來做網頁的一些特效。比如鼠標停在哪個位置會變形,或者哪個圖片不停地變換,再或者跳轉出一個廣告視頻,這些都是需要js來做的。
4:后臺制作
這個是作用于網站的管理用的,反正除了自己其他的人看不到,就不需要太大的要求,直接制作個簡單好管理的界面,再搭建個信息管理系統,后臺基本就完成了。
5:美工設計
說到美工,大家一定會想到圖片,沒錯,就是做圖片,不過網站的美工不只是做圖片,還涉及到攝影,畢竟網站上不只是有圖片,有時候還要插入一些動圖或視頻,做不能每次需要圖片或視頻就到網站上找吧,所以至今動手很重要。
6:域名選購
這些基本和開發技術沒有多大的關系,就是注冊個域名,但是域名注冊不好那么就是關系到網站的整體。就像網站上的那些二手域名,一個個簡直就像是在淘寶一樣,萬一搞到的是一個有缺陷的域名,到時候網站搭建完后就要唱一首涼涼了。
7:服務器購買
光有域名還不行,當服務器是在國內的時候,是需要備案的。 備案流程現在簡單多了,基本上7天就可以做完,不過其中有一個挺麻煩的地方,需要自己手寫簽字,然后再掃描出來發過去
以上就是自己做網站需要掌握的東西,不得不說的是,想自己總一個網站真的是非一日之功,那是需要下本錢的,就像html、css、js,這些單獨學起來可能不怎么樣,但是學到后面就越是麻煩,就像組合使用。
所以說,想自己學習開發技術在自己搭建網站,真的非一日之功,哪怕你天賦異稟,沒有幾個月的時間是不可能的。
所以像我這種天賦異稟的天才還是找個現成的網站模板就改下變成自己的比較實惠。就比如66好用源碼網,里面有無數模板,隨便改都可以,只要您懂點技術,怎么盜版都可以,不過帶后臺的需要把模板下載下來再修改。
CSS in JS是一種解決css問題想法的集合,而不是一個指定的庫。從CSS in JS的字面意思可以看出,它是將css樣式寫在JavaScript文件中,而不需要獨立出.css、.less之類的文件。將css放在js中使我們更方便的使用js的變量、模塊化、tree-shaking。還解決了css中的一些問題,譬如:更方便解決基于狀態的樣式,更容易追溯依賴關系,生成唯一的選擇器來鎖定作用域。盡管CSS in JS不是一個很新的技術,但國內的普及程度并不高。由于Vue和Angular都有屬于他們自己的一套定義樣式的方案,React本身也沒有管用戶怎樣定義組件的樣式[1],所以CSS in JS在React社區的熱度比較高。
目前為止實現CSS in JS的第三方庫有很多:(http://michelebertoli.github.io/css-in-js/)。像JSS[2]、styled-components[3]等。在這里我們就不展開贅述了(相關鏈接已放在下方),這篇文章的重點是JS in CSS。
在上面我們提到CSS in JS就是把CSS寫在JavaScript中,那么JS in CSS我們可以推斷出就是可以在CSS中使用JavaScript腳本,如下所示。可以在CSS中編寫Paint API的功能。還可以訪問:ctx,geom。甚至我們還可以編寫自己的css自定義屬性等。這些功能的實現都基于CSS Houdini[4]。
.el {
--color: cyan;
--multiplier: 0.24;
--pad: 30;
--slant: 20;
--background-canvas: (ctx, geom) => {
let multiplier = var(--multiplier);
let c = `var(--color)`;
let pad = var(--pad);
let slant = var(--slant);
ctx.moveTo(0, 0);
ctx.lineTo(pad + (geom.width - slant - pad) * multiplier, 0);
ctx.lineTo(pad + (geom.width - slant - pad) * multiplier + slant, geom.height);
ctx.lineTo(0, geom.height);
ctx.fillStyle = c;
ctx.fill();
};
background: paint(background-canvas);
transition: --multiplier .4s;
}
.el:hover {
--multiplier: 1;
}
在如今的Web開發中,JavaScript幾乎占據了項目代碼的大部分。我們可以在項目開發中使用ES 2020、ES2021、甚至提案中的新特性(如:Decorator[5]),即使瀏覽器尚未支持,也可以編寫Polyfill或使用Babel之類的工具進行轉譯,讓我們可以將最新的特性應用到生產環境中(如下圖所示)。
JavaScript標準制定流程.png
而CSS就不同了,除了制定CSS標準規范所需的時間外,各家瀏覽器的版本、實戰進度差異更是曠日持久(如下圖所示),最多利用PostCSS、Sass等工具來幫我們轉譯出瀏覽器能接受的CSS。開發者們能操作的就是通過JS去控制DOM與CSSOM來影響頁面的變化,但是對于接下來的Layout、Paint與Composite就幾乎沒有控制權了。為了解決上述問題,為了讓CSS的魔力不在受到瀏覽器的限制,Houdini就此誕生。
CSS 標準制定流程.png
我們上文中提到JavaScript中進入提案中的特性我們可以編寫Polyfill,只需要很短的時間就可以講新特性投入到生產環境中。這時,腦海中閃現出的第一個想法就是CSS Polyfill,只要CSS的Polyfill 足夠強大,CSS或許也能有JavaScript一樣的發展速度,令人可悲的是編寫CSS Polyfill異常的困難,并且大多數情況下無法在不破壞性能的情況下進行。這是因為JavaScript是一門動態腳本語言[6]。它帶來了極強的擴展性,正是因為這樣,我們可以很輕松使用JavaScript做出JavaScript的Polyfill。但是CSS不是動態的,在某些場景下,我們可以在編譯時將一種形式的CSS的轉換成另一種(如PostCSS[7])。如果你的Polyfill依賴于DOM結構或者某一個元素的布局、定位等,那么我們的Polyfill就無法編譯時執行,而需要在瀏覽器中運行了。不幸的是,在瀏覽器中實現這種方案非常不容易。
頁面渲染流程.png
如上圖所示,是從瀏覽器獲取到HTML到渲染在屏幕上的全過程,我們可以看到只有帶顏色(粉色、藍色)的部分是JavaScript可以控制的環節。首先我們根本無法控制瀏覽器解析HTML與CSS并將其轉化為DOM與CSSOM的過程,以及Cascade,Layout,Paint,Composite我們也無能為力。整個過程中我們唯一完全可控制的就是DOM,另外CSSOM部分可控。
CSS Houdini草案中提到,這種程度的暴露是不確定的、兼容性不穩定的以及缺乏對關鍵特性的支持的。比如,在瀏覽器中的 CSSOM 是不會告訴我們它是如何處理跨域的樣式表,而且對于瀏覽器無法解析的 CSS 語句它的處理方式就是不解析了,也就是說——如果我們要用 CSS polyfill讓瀏覽器去支持它尚且不支持的屬性,那就不能在 CSSOM 這個環節做,我們只能遍歷一遍DOM,找到 <style> 或 <link rel="stylesheet"> 標簽,獲取其中的 CSS 樣式、解析、重寫,最后再加回 DOM 樹中。令人尷尬的是,這樣DOM樹全部刷新了,會導致頁面的重新渲染(如下如所示)。
即便如此,有的人可能會說:“除了這種方法,我們也別無選擇,更何況對網站的性能也不會造成很大的影響”。那么對于部分網站是這樣的。但如果我們的Polyfill是需要對可交互的頁面呢?例如scroll,resize,mousemove,keyup等等,這些事件隨時會被觸發,那么意味著隨時都會導致頁面的重新渲染,交互不會像原本那樣絲滑,甚至導致頁面崩潰,對用戶的體驗也極其不好。
綜上所述,如果我們想讓瀏覽器解析它不認識的樣式(低版本瀏覽器使用grid布局),然而渲染流程我們無法介入,我們也只能通過手動更新DOM的方式,這樣會帶來很多問題,Houdini的出現正是致力于解決他們。
Houdini是一組底層API,它公開了CSS引擎的各個部分,如下圖所示展示了每個環節對應的新API(灰色部分各大瀏覽器還未實現),從而使開發人員能夠通過加入瀏覽器渲染引擎的樣式和布局過程來擴展CSS。Houdini是一群來自Mozilla,Apple,Opera,Microsoft,HP,Intel和Google的工程師組成的工作小組設計而成的。它們使開發者可以直接訪問CSS對象模型(CSSOM),使開發人員可以編寫瀏覽器可以解析為CSS的代碼,從而創建新的CSS功能,而無需等待它們在瀏覽器中本地實現。
CSS Houdini-API
盡管當前已經有了CSS變量,可以讓開發者控制屬性值,但是無法約束類型或者更嚴格的定義,CSS Houdini新的API,我們可以擴展css的變量,我們可以定義CSS變量的類型,初始值,繼承。它是css變量更強大靈活。
CSS變量現狀:
.dom {
--my-color: green;
--my-color: url('not-a-color'); // 它并不知道當前的變量類型
color: var(--my-color);
}
Houdini提供了兩種自定義屬性的注冊方式,分別是在js和css中。
CSS.registerProperty({
name: '--my-prop', // String 自定義屬性名
syntax: '<color>', // String 如何去解析當前的屬性,即屬性類型,默認 *
inherits: false, // Boolean 如果是true,子節點將會繼承
initialValue: '#c0ffee', // String 屬性點初始值
});
我們還可以在css中注冊,也可以達到上面的效果
@property --my-prop {
syntax: '<color>';
inherits: false;
initial-value: #c0ffee;
}
這個API中最令人振奮人心的功能是自定義屬性上添加動畫,像這樣:transition: --multiplier 0.4s;,這個功能我們在前面介紹什么是js in css那個demo[8]用使用過。我們還可以使用+使syntax屬性支持一個或多個類型,也可以使用|來分割。更多syntax屬性值:
屬性值描述<length>長度值<number>數字<percentage>百分比<length-percentage>長度或百分比,calc將長度和百分比組成的表達式<color>顏色<image>圖像<url>網址<integer>整數<angle>角度<time>時間<resolution>分辨率<transform-list>轉換函數<custom-ident>ident
Worklets是渲染引擎的擴展,從概念上來講它類似于Web Workers[9],但有幾個重要的區別:
Worklet是一個JavaScript模塊,通過調用worklet的addModule方法(它是個Promise)來添加。比如registerLayout,registerPaint, registerAnimator 我們都需要放在Worklet中
//加載單個
await demoWorklet.addModule('path/to/script.js');
// 一次性加載多個worklet
Promise.all([
demoWorklet1.addModule('script1.js'),
demoWorklet2.addModule('script2.js'),
]).then(results => {});
registerDemoWorklet('name', class {
// 每個Worklet可以定義要使用的不同函數
// 他們將由渲染引擎在需要時調用
process(arg) {
return !arg;
}
});
Worklets的生命周期
Worklets lifecycle
Typed OM是對現有的CSSOM的擴展,并實現 Parsing API 和 Properties & Values API相關的特性。它將css值轉化為有意義類型的JavaScript的對象,而不是像現在的字符串。如果我們嘗試將字符串類型的值轉化為有意義的類型并返回可能會有很大的性能開銷,因此這個API可以讓我們更高效的使用CSS的值。
現在讀取CSS值增加了新的基類CSSStyleValue,他有許多的子類可以更加精準的描述css值的類型:
子類描述CSSKeywordValueCSS關鍵字和其他標識符(如inherit或grid)CSSPositionValue位置信息 (x,y)CSSImageValue表示圖像的值屬性的對象CSSUnitValue表示為具有單個單位的單個值(例如50px),也可以表示為沒有單位的單個值或百分比CSSMathValue比較復雜的數值,比如有calc,min和max。這包括子類 CSSMathSum, CSSMathProduct, CSSMathMin,CSSMathMax, CSSMathNegate 和 CSSMathInvertCSSTransformValue由CSS transforms組成的CSSTransformComponent列表,其中包括CSSTranslate, CSSRotate, CSSScale, CSSSkew, CSSSkewX, CSSSkewY, CSSPerspective 和 CSSMatrixComponent
使用Typed OM主要有兩種方法:
使用attributeStyleMap設置并獲取
myElement.attributeStyleMap.set('font-size', CSS.em(2));
myElement.attributeStyleMap.get('font-size'); // CSSUnitValue { value: 2, unit: 'em' }
myElement.attributeStyleMap.set('opacity', CSS.number(.5));
myElement.attributeStyleMap.get('opacity'); // CSSUnitValue { value: 0.5, unit: 'number' };
在線demo[10]
使用computedStyleMap
.foo {
transform: translateX(1em) rotate(50deg) skewX(10deg);
vertical-align: baseline;
width: calc(100% - 3em);
}
const cs = document.querySelector('.foo').computedStyleMap();
cs.get('vertical-align');
// CSSKeywordValue {
// value: 'baseline',
// }
cs.get('width');
// CSSMathSum {
// operator: 'sum',
// length: 2,
// values: CSSNumericArray {
// 0: CSSUnitValue { value: -90, unit: 'px' },
// 1: CSSUnitValue { value: 100, unit: 'percent' },
// },
// }
cs.get('transform');
// CSSTransformValue {
// is2d: true,
// length: 3,
// 0: CSSTranslate {
// is2d: true,
// x: CSSUnitValue { value: 20, unit: 'px' },
// y: CSSUnitValue { value: 0, unit: 'px' },
// z: CSSUnitValue { value: 0, unit: 'px' },
// },
// 1: CSSRotate {...},
// 2: CSSSkewX {...},
// }
開發者可以通過這個API實現自己的布局算法,我們可以像原生css一樣使用我們自定義的布局(像display:flex, display:table)。在Masonry layout library[11] 上我們可以看到開發者們是有多想實現各種各樣的復雜布局,其中一些布局光靠 CSS 是不行的。雖然這些布局會讓人耳目一新印象深刻,但是它們的頁面性能往往都很差,在一些低端設備上性能問題猶為明顯。
CSS Layout API 暴露了一個registerLayout方法給開發者,接收一個布局名(layout name)作為后面在 CSS中使用的屬性值,還有一個包含有這個布局邏輯的JavaScript類。
my-div {
display: layout(my-layout);
}
// layout-worklet.js
registerLayout('my-layout', class {
static get inputProperties() { return ['--foo']; }
static get childrenInputProperties() { return ['--bar']; }
async intrinsicSizes(children, edges, styleMap) {}
async layout(children, edges, constraints, styleMap) {}
});
await CSS.layoutWorklet.addModule('layout-worklet.js');
目前瀏覽器大部分還不支持
我們可以在CSS background-image中使用它,我們可以使用Canvas 2d上下文,根據元素的大小控制圖像,還可以使用自定義屬性。
await CSS.paintWorklet.addModule('paint-worklet.js');
registerPaint('sample-paint', class {
static get inputProperties() { return ['--foo']; }
static get inputArguments() { return ['<color>']; }
static get contextOptions() { return {alpha: true}; }
paint(ctx, size, props, args) { }
});
這個API讓我們可以控制基于用戶輸入的關鍵幀動畫,并且以非阻塞的方式。還能更改一個 DOM 元素的屬性,不過是不會引起渲染引擎重新計算布局或者樣式的屬性,比如 transform、opacity 或者滾動條位置(scroll offset)。Animation API的使用方式與 Paint API 和Layout API略有不同我們還需要通過new一個WorkletAnimation來注冊worklet。
// animation-worklet.js
registerAnimator('sample-animator', class {
constructor(options) {
}
animate(currentTime, effect) {
effect.localTime = currentTime;
}
});
await CSS.animationWorklet.addModule('animation-worklet.js');
// 需要添加動畫的元素
const elem = document.querySelector('#my-elem');
const scrollSource = document.scrollingElement;
const timeRange = 1000;
const scrollTimeline = new ScrollTimeline({
scrollSource,
timeRange,
});
const effectKeyframes = new KeyframeEffect(
elem,
// 動畫需要綁定的關鍵幀
[
{transform: 'scale(1)'},
{transform: 'scale(.25)'},
{transform: 'scale(1)'}
],
{
duration: timeRange,
},
);
new WorkletAnimation(
'sample-animator',
effectKeyframes,
scrollTimeline,
{},
).play();
關于此API的更多內容:(https://github.com/w3c/css-houdini-drafts/tree/main/css-animation-worklet-1)
允許開發者自由擴展 CSS 詞法分析器。
解析規則:
const background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"
const styles = window.cssParse.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) // 5
console.log(styles[0].styleMap.get("margin-top").value) // 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"
解析CSS:
const style = fetch("style.css")
.then(response => CSS.parseStylesheet(response.body));
style.then(console.log);
它將提供一些方法來測量在屏幕上呈現的文本元素的尺寸,將允許開發者控制文本元素在屏幕上呈現的方式。使用當前功能很難或無法測量這些值,因此該API將使開發者可以更輕松地創建與文本和字體相關的CSS特性。例如:
Is Houdini ready yet
(https://ishoudinireadyyet.com/)
了解到這里,部分開發者可能會說:“我不需要這些花里胡哨的技術,并不能帶收益。我只想簡簡單單的寫幾個頁面,做做普通的Web App,并不想試圖干預瀏覽器的渲染過程從而實現一些實驗性或炫酷的功能。”如果這樣想的話,我們不妨退一步再去思考。回憶下最近做過的項目,用于實現頁面效果所使用到的技術,grid布局方式在考慮兼容老版本瀏覽器時也不得不放棄。我們想控制瀏覽器渲染頁面的過程并不是僅僅為了炫技,更多的是為了幫助開發者們解決以下兩個問題:
幾年過后再回眸,當主流瀏覽器完全支持Houdini的時候。我們可以在瀏覽器上隨心所欲的使用任何CSS屬性,并且他們都能完美支持。像今天的grid布局在舊版本瀏覽器支持的并不友好的這類問題,那時我們只需要安裝對應的Polyfill就能解決類似的問題。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。