jsx字尾的是什麼檔案_是 jsv 而不是 jsx:這可能是你更想要的魔改vue3程式碼編寫方式...
技術標籤:jsx字尾的是什麼檔案
vue官方推薦的是單檔案.vue編寫元件,簡單易學但沒有jsx靈活,很多知名的UI庫最終選擇了使用jsx,例如:ant-desing-vue, vant。但是在 vue 中使用 jsx 也存在一些問題。比如:大部分 vue 指令無法使用或者很難使用。在 vue3 中專門開了個 issue 討論,但至今沒有優雅的解決方案。jsv而不是jsx可能是更好的解決方案。
一、先看看 .vue 與 .jsx 的比較
1. 變數作用域比較
1.1.vue中無法使用當前作用域變數,必須return後才能使用
// Scope.vue<template> <div>{{ state.count }}div> <button @click="handleClick">點選加1button>template><script>import { reactive } from "vue";export default { setup() { let state = reactive({ count: 0 }); function handleClick() { state.count++; } // 在模板中無法直接使用setup中的變數,必須return return { state, handleClick }; },};script>
1.2.jsx中可以直接使用當前作用域的變數
// Scope.jsximport { reactive } from "vue";export default { setup() { let state = reactive({ count: 0 }); function handleClick() { state.count++; } // 可以在渲染函式中直接使用當前作用域的變數 return ()=>( <div> <div >{ state.count }div> <button onClick={handleClick}>點選加1button> div> ) },};
2. 靈活性比較
2.1.vue中一個檔案只能寫一個元件
// NoMulti.vue<template> <Title :title="state.title" />template><script>import { reactive } from "vue";// 不能在一個.vue中寫多個元件,必須將 Title 寫在另外一個檔案中import Title from "./Title.vue";export default { components: { Title }, setup() { let state = reactive({ title: "jsv-compiler" }); return { state }; },};script>
// Title.vue<template> <h1>hello {{title}}h1>template><script>export default { props: { title: String }}script>
2.2..jsx中可以寫多個元件
// Multi.jsximport { reactive } from "vue";export default { setup() { let state = reactive({ title: 'jsv-compiler' }); // 一個檔案中可以寫多個元件 let Title = ()=><h1>hello {state.title}h1> return ()=>( <div> <Title /> div> ) },};
3. 指令比較
3.1..vue原生支援優雅的指令寫法
<A v-model:argument.modifer="val" />
3.2..jsx本身不支援指令 社群有多種指令的書寫方式,各種千奇百怪,為此還開了issue討論,至今仍然沒有友好的解決方式。因此可以暫時認為.jsx不支援指令或支援的不好.github.com/vuejs/jsx/i…
val, 'argument', ['modifier']]} />
4. 執行時效能比較
4.1..vue支援hoist,block,patchProps等執行時效能提升,至少比.jsx效能快了3倍vue-next-template-explorer.netlify.app
// 編譯前<div> <div>靜態節點div> <div >{{state.count }}div>div>
// 編譯後const { createVNode: _createVNode, toDisplayString: _toDisplayString, openBlock: _openBlock, createBlock: _createBlock } = Vueconst _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "靜態節點", -1 /* HOISTED */)// render 函式 上面的程式碼只會執行一次// 每次重新渲染都再次執行 render 函式// 1. 關於 _hoisted_1 靜態節點變數提升,作用是再次執行render函式時,不用重新建立節點,直接從記憶體中讀取;// 2. 關於 _openBlock, _createBlock,作用是在 dom-diff 時,不比較靜態節點,只比較可變節點;// 3. 關於 patchProps,作用是在 dom-diff 時,只比較 text 的變化,不比較其他任何屬性的變化;return function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock("div", null, [ _hoisted_1, // 靜態節點變數提升 _createVNode("div", null, _toDisplayString(_ctx.state.count), 1 /* TEXT */) // 此處的 1 用於 patchProps 的標識 ]))}// Check the console for the AST
4.2..jsx沒有執行時優化www.babeljs.cn/repl
// 編譯前<div> <div >static nodediv> <div >{ state.count }div>div>
// 編譯後 (使用 vite 工具編譯)jsx( "div", null /* @__PURE__ */, jsx("div", null, "static node") /* @__PURE__ */, jsx("div", null, state.count))
5. 生態比較
5.1. 當前知名的UI庫比如 ant-desing-vue,vant 內部採用了 jsx
5.2. 但對外提供的元件仍然是 vue的模板語法。對於使用者如果要用 jsx,還得自己把 vue 改成 jsx,代價高昂呀!
二、這才是正文:是 jsv 不是 jsx !
經過比較我們發現.jsx靈活但對指令支援不友好也沒有執行時效能優化。.vue很好的支援指令和執行時效能優化,但不靈活。
那有沒有一種解決方案,既有 jsx 的靈活性,又有原生支援vue指令和執行時高效能,在 js程式碼 中直接使用 vue 模板語法?就像這樣: 檔案字尾為.jsv
// 檔案命名為 App.jsvimport { reactive } from "vue";export default { name: "App", setup() { let state = reactive({ title: 'jsv-compiler', count: 0 }); function handleClick() { state.count++; } // 一個檔案中寫多個元件。此處的 Title 是 元件。 let Title = <template><h1>hello {{state.title}}h1>template> // 直接使用當前作用域的變數,比如 state.count return ( <template> <Title /> <div>{{ state.count }}div> <button @click="handleClick">點選加1button> template> ) },};
目標有了那就開始魔改吧。
我寫了個編譯器集成了vite外掛實現:jsv-compiler
1. 建立vue3專案
npx create-vite-app <your projectname>
2. 安裝依賴包
npm i -D jsv-compiler
3. 配置外掛
檔案:vite.config.js
import {jsvPlugin} from 'jsv-compiler'export default { configureServer: [jsvPlugin]}
4. 使用示例
新建檔案字尾為.jsv
// 檔案命名為 **App.jsv**import { reactive } from "vue";export default { name: "App", setup() { let state = reactive({ title: 'jsv-compiler', count: 0 }); function handleClick() { state.count++; } // 一個檔案中寫多個元件。此處的 Title 是 元件。 let Title = <template><h1>hello {{state.title}}h1>template> // 直接使用當前作用域的變數,比如 state.count return ( <template> <Title /> <div>{{ state.count }}div> <button @click="handleClick">點選加1button> template> ) },};
啟動專案 `npm run dev`,很神奇的魔改成功了:
還是有點小激動的!
5. 語法高亮
5.1 在vscode的外掛市場中下載外掛:jsv
5.2 因為是第一個版本,在 App.jsv 中暫時無法語法高亮,需將檔名改為 App.js ,並在
旁邊加上反引號 才能在vscode中實現語法高亮,實際在編譯層面是不需要加反引號的。
6. 覆盤魔改的主要過程
主要看編譯結果,還是用這個簡單的例子
// 編譯前<div> <div>靜態節點div> <div >{{state.count }}div>div>
6.1. 我是如何做到在一個檔案中寫多個template?
在原來的 .vue中, template編譯後會被寫到單獨的檔案中,並return 或者 export 出去。
為了達到在一個檔案中寫多個元件,我將其編譯成一個自執行函式並返回render函式。同時利用閉包特性,仍然能使用靜態節點提升後的變數
_hoisted_1
// jsv的編譯結果(() => { const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "靜態節點", -1 /* HOISTED */) return function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _hoisted_1, _createVNode("div", null, _toDisplayString( (() => { try { return state } catch { return _ctx.state } })().count), 1 /* TEXT */) ])) }})()
6.2. 如何做到使用當前作用域的變數 state?
我將_ctx.state編譯成自執行函式:
(() => { try { return state } catch { return _ctx.state }})()
6.3. 如何使用當前作用域的元件?
我們知道在 .vue中必須要將元件註冊後才能在 template中使用,比如
// NoMulti.vue<template> <Title :title="state.title" />template><script>import { reactive } from "vue";import Title from "./Title.vue";export default { components: { Title }, // 必須要在此處註冊元件 setup() { let state = reactive({ title: "jsv-compiler" }); return { state }; },};script>
為了實現不註冊也能直接使用,我將其編譯成這樣:
const _component_Title = (()=>{ try { return Title; } catch (error) { return _resolveComponent("Title"); }}) ()
也是一個自執行函式,哈哈。如果當前作用域有 Title,就使用當前作用域的 Title。如果當前作用域沒有 Title, 就使用註冊的元件_resolveComponent("Title")。
還有很多魔性修改來不及寫了。。。大家發現了吧,大量用到了自執行函式。。。
三、總結
在 .js 中使用 vue 模板語法 (簡稱jsv)
vue3 模板 | jsv | jsx | |
---|---|---|---|
靈活性 | 不靈活 | 靈活 | 靈活 |
檔案中元件數量 | 1個檔案1個元件 | 1個檔案多個元件 | 1個檔案多個元件 |
能否使用當前作用域變數 | 不能,需在setup()方法中return後才能使用 | 可以使用 | 可以使用 |
是否很好支援指令 | 原生支援 | 原生支援 | 不支援或支援不友好 |
執行時效能 | 支援hoist,createBlock,patchProps | 支援hoist,createBlock,patchProps | 不支援 |
自動熱更新 | 支援 | 不支援 | 不支援 |
值得一提的是jsv保持了原本 vue 的高效能,支援hoist,createBlock,patchProps等在 jsx 中難以實現的特性,比 jsx快了近3倍。可能有人會說為什麼要折騰這些?我想說的是 人類對效率的追求總是無止境的,正是像這樣的折騰鑄就了人類巨集偉的科技藍圖。也希望我的一點貢獻能拋磚引玉激發起大家的興趣嘗試一下
備註:
jsv-compiler:https://github.com/ruige24601/jsv-compiler.git