1. 程式人生 > 其它 >jsx字尾的是什麼檔案_是 jsv 而不是 jsx:這可能是你更想要的魔改vue3程式碼編寫方式...

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`,很神奇的魔改成功了:

49abc2390d117c21716daae7dcd54333.png

還是有點小激動的!

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 模板jsvjsx
靈活性不靈活靈活靈活
檔案中元件數量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