幫你在Vueconf上收穫更大的一篇文章

前言

hi 各位大兄弟好,最近前端圈最熱門的事,就是5.22的vueconf了,看了下議題都頗有意思,考慮到不少同窗對vue3還不是那麼熟悉,我給幾個議題寫一個預習資料吧,幫助你們在22號當天收穫更大javascript

我掏腰包你們報銷三張vueconf的線上票 微信搜索並關注花果山前端, 回覆vueconf便可參與 直接給你紅包html

我定了個大屏幕,22號當天一塊兒圍觀,歡迎在線一塊兒討論學習前端

image.png

議題

  • 09:30 Vue生態進展                     @尤雨溪
  • 10:30 Vite介紹                           @underfin
  • 11:20 Vue3 compiler優化            @HcySunYang
  • 12:05 乾飯吃火鍋
  • 13:30 構建工具優化代碼               @蔣豪羣
  • 14:15 Vue3的jsx                         @Amour1688 jsx-next
  • 15:00 Vue Composition              @Anthony Fu vueuse
  • 15:45 Vue flutter                        @染陌 北海Kraken
  • 16:30 Vue3多端應用                    @崔紅保 uni-app
  • 17:20 Vue Composiiton中封裝庫   @Jonathan chakra-ui-vue
  • 18:10 結束乾飯

依次介紹下以上議題的預備知識吧,先把vue3官網擼一遍vue

Vue3 生態進展和計劃

這個沒啥預習的,結合上一次小右直播介紹vue3和vueconf us的內容,盲猜同步vue生態系統的研發進度 我的感受如今已經很成熟了,能夠嘗試線上搞起了java

你們最近多體驗體驗把,這個沒啥預習的,到時候好好聽就能夠,很期待聽一下Vue3以後下一步的計劃node

* Vue3近況
* Vuex 4
* Vue-router 4
* Nuxt
* 組件庫
* IDE 插件 Volar啥的
* Vite2
* Vue2遷移指南
* Vue2.7 
* 展望美好的將來
複製代碼

Vite,下一代Web工具

分享內容主要是現有Web開發的痛點以及Vite的解決方案。react

Vite最近的熱度一直不減,確實解決了開發者的痛點,那就是webpack太慢了,你們能夠看下這個圖webpack

image.png

webpack每次啓動項目,都須要預打包,打包一個bundle後,才能啓動dev server,這也是每次npm run dev都須要三分鐘的緣由,vite利用瀏覽器自帶的import功能,避開了這一步git

大概就是瀏覽器裏標記了script module後,寫的import語法,會發送一個http請求獲取資源,調試的時候就無須打包,讓開發環境變得絲般潤滑github

image.png

這只是簡單的原理,爲了真正達到可用狀態,vite還作了不少額外的努力

* node_modules路徑解析
* 插件機制 支持vue & react & svelte等框架
* 熱更新 hmr
* 引入esbuild轉譯js, 速度賊快
* 預打包優化 node_modules/.vite
* 支持ssr
* 。。。。
複製代碼

Vite已經和vue解耦,逐漸成爲新型框架首選的工程化工具 ,關於原理你們能夠看下我以前寫的文章,vite2已經重構了,後續再更新熱更新和預優化的內容,期待vite下一步的動做

你們最近能跟着官網好好熟悉一下vite,帶着問題去聽,收穫會更大

前端新工具--vite從入門到實戰(一)

Vue3 在編譯優化方面作的努力

相信不少同窗或多或少的都瞭解過 vue3 在編譯方面配合運行時作了不少性能優化方面的努力,本次分享的主題將會詳細探討實現優化的思路、問題以及應對方法,和對模板能力的探索。

Vue3我最喜歡的兩個點就是composition和compiler優化,composition解決了代碼組織混亂的問題,compiler在咱們無感知的狀況加,真正作到了按需更新

compiler的概念建議你們先去看the-super-tiny-compiler,瞭解一下編譯原理的基本概念,paser,ast,transformer,generate是啥意思

因此編譯優化,就是代碼在打包上線以前進行的優化,vue中把template轉化成render函數,期間的源碼你們去vue-next裏搜baseCompile

export function baseCompile( template: string | RootNode, options: CompilerOptions = {} ): CodegenResult {
  const prefixIdentifiers =
    !__BROWSER__ && (options.prefixIdentifiers === true )

  const ast = isString(template) ? baseParse(template, options) : template
  transform(
    ast,
    extend({}, options, {
        ...
    })
  )
  return generate(
    ast,
    extend({}, options, {
      prefixIdentifiers
    })
  )
}
複製代碼

簡化一下就是

const ast = baseParse(template) // 把template解析成ast 也就是一棵樹
transform(ast,option)           // 優化,好比標記和轉化vue的特定語法
return genrate(ast)             // 生成的render 函數
複製代碼

你們能夠在線體驗一下Vue compiler的實時結果

咱們在上線前,把動態的內容都打好標記,在瀏覽器執行的時候,就能夠略過不少操做 好比上面這個圖裏,h1是純靜態,不會發生任何變化,div的id和textContent是動態的,可能修改,傳遞進來的函數,也加了緩存 賊棒

咱們嘗試本身實現一個mini的你們體驗一下 ,實際的實現代價能夠去看vue-next,新手讀起來略顯難度,並且正則也須要理解

個人代碼比較挫,字符串挨個遍歷,算是一個很是der的實現,最搓就叫the-der-tiny-compiler吧,歡迎你們去star

image.png

咱們嘗試模擬一下,寫一個很是der的實現,輔助你們理解 用上面這個輸入

function compiler(template) {
  const ast = parse(template);
  transform(ast)
  const code = generate(ast)
  return new Function(code);
}

let tmpl = `<div id="app"> <p @click="add" :id="name">{{name}}</p> <h1 class="item">技術摸魚</h1> </div>`


let render = compiler(tmpl)
複製代碼

tokens

先寫把字符串拆成tokens

function tokenizer(input) {
  let tokens = []
  let type = ''
  let val = ''
  // 粗暴循環
  for (let i = 0; i < input.length; i++) {
    let ch = input[i]
    if (ch === '<') {
      push()
      if (input[i + 1] === '/') {
        type = 'tagend'
      } else {
        type = 'tagstart'
      }
    } if (ch === '>') {
      push()
      type = "text"
      continue
    } else if (/[\s]/.test(ch)) { // 遇見空格夾斷一下
      push()
      type = 'props'
      continue
    }
    val += ch
  }
  return tokens
複製代碼

咱們把獲得了這個

[
  { type: 'tagstart', val: 'div' },
  { type: 'props', val: 'id="app"' },
  { type: 'tagstart', val: 'p' },
  { type: 'props', val: '@click="add"' },
  { type: 'props', val: ':id="name"' },
  { type: 'text', val: '{{name}}' },
  { type: 'tagend', val: 'p' },
  { type: 'tagstart', val: 'h1' },
  { type: 'props', val: 'class="item"' },
  { type: 'text', val: '技術摸魚' },
  { type: 'tagend', val: 'h1' },
  { type: 'tagend', val: 'div' }
]
複製代碼

parse

而後搞出來ast 就是遍歷生成一棵樹

function parse(template) {
  const tokens = tokenizer(template)
  let cur = 0
  let ast = {
    type: 'root',
    props:[],
    children: []
  }
  while (cur < tokens.length) {
    ast.children.push(walk())
  }
  return ast

  function walk() {
    let token = tokens[cur]
    if (token.type == 'tagstart') {
      let node = {
        type: 'element',
        tag: token.val,
        props: [],
        children: []
      }
      token = tokens[++cur]
      while (token.type !== 'tagend') {
        if (token.type == 'props') {
          node.props.push(walk())
        } else {
          node.children.push(walk())
        }
        token = tokens[cur]
      }
      cur++
      return node
    }
    if (token.type === 'tagend') {
      cur++
      // return token
    }
    if (token.type == "text") {
      cur++
      return token
    }
    if (token.type === "props") {
      cur++
      const [key, val] = token.val.split('=')
      return {
        key,
        val
      }
    }
  }
}
複製代碼

最終獲得ast

{
  "type": "root",
  "props": [],
  "children": [
    {
      "type": "element",
      "tag": "div",
      "props": [
        {
          "key": "id",
          "val": "\"app\""
        }
      ],
      "children": [
        {
          "type": "element",
          "tag": "p",
          "props": [
            {
              "key": "@click",
              "val": "\"add\""
            },
            {
              "key": ":id",
              "val": "\"name\""
            }
          ],
          "children": [
            {
              "type": "text",
              "val": "{{name}}"
            }
          ]
        },
        {
          "type": "element",
          "tag": "h1",
          "props": [
            {
              "key": "class",
              "val": "\"item\""
            }
          ],
          "children": [
            {
              "type": "text",
              "val": "技術摸魚"
            }
          ]
        }
      ]
    }
  ]
}
複製代碼

transform

而後優化一下,主要就是作個標記,標記屬性,文本是否是靜態的

function transform(ast) {
  // 優化一下ast
  let context = {
    // import { toDisplayString , createVNode , openBlock , createBlock } from "vue"
    helpers:new Set(['openBlock','createVnode']), // 用到的工具函數 
  }
  traverse(ast, context)
  ast.helpers = context.helpers
}
function traverse(ast, context){
  switch(ast.type){
    case "root":
      context.helpers.add('createBlock')
      // log(ast)
    case "element":
      ast.children.forEach(node=>{
        traverse(node,context)
      })
      ast.flag = {props:false,class:false,event:false}
      ast.props = ast.props.map(prop=>{
        const {key,val} = prop
        if(key[0]=='@'){
          ast.flag.event = true
          return {
            key:'on'+key[1].toUpperCase()+key.slice(2),
            val
          }
        }
        if(key[0]==':'){
          ast.flag.props = true
          return{
            key:key.slice(1),
            val
          }
        }
        if(key.startsWith('v-')){
          // pass such as v-model
        }
        return {...prop,static:true}
      })
      break
    case "text":
      // trnsformText
      let re = /\{\{(.*)\}\}/g
      if(re.test(ast.val)){
        //有{{
          ast.static = false
          context.helpers.add('toDisplayString')
          ast.val = ast.val.replace(/\{\{(.*)\}\}/g,function(s0,s1){
            return s1
          })
      }else{
        ast.static = true
      }
  }
}

複製代碼

主要就是用 ast.flag = {props:false,class:false,event:false}來標記,false的話就所有是靜態,能夠直接越過虛擬dom diff

generate

而後就是生成代碼啦,字符串拼一下

function generate(ast) {
  const {helpers} = ast 

  let code = ` import {${[...helpers].map(v=>v+' as _'+v).join(',')}} from 'vue'\n export function render(_ctx, _cache, $props){ return(_openBlock(), ${ast.children.map(node=>walk(node))})}`

  function walk(node){
    switch(node.type){
      case 'element':
        let {flag} = node // 編譯的標記
        let props = '{'+node.props.reduce((ret,p)=>{
          if(flag.props){
            //動態屬性
            ret.push(p.key +':_ctx.'+p.val.replace(/['"]/g,'') )
          }else{
            ret.push(p.key +':'+p.val )
          }

          return ret
        },[]).join(',')+'}'
        return `_createVnode("${node.tag}",${props}),[ ${node.children.map(n=>walk(n))} ],${JSON.stringify(flag)}`
        break
      case 'text':
        if(node.static){
          return '"'+node.val+'"'
        }else{
          return `_toDisplayString(_ctx.${node.val})`
        }
        break
    }
  }
  return code
}
複製代碼

你就獲得了這段代碼

import { openBlock as _openBlock, createVnode as _createVnode, createBlock as _createBlock, toDisplayString as _toDisplayString } from 'vue'

export function render(_ctx, _cache, $props) {
    return (_openBlock(), _createVnode("div", { id: "app" }), [
        _createVnode("p", { onClick: _ctx.add, id: _ctx.name }), [
            _toDisplayString(_ctx.name)
        ], { "props": true, "class": false, "event": true }, _createVnode("h1", { class: "item" }), [
            "技術摸魚"
        ], { "props": false, "class": false, "event": false }
    ], { "props": false, "class": false, "event": false })
}
複製代碼

雖然和vue差得遠,可是有內味了,後續能夠吧props,event這些標記用位運算標記

雖然實現的很是的der(語音),高手輕噴, 可是對於輔助理解vue3的compiler已經夠用了 之後有機會我寫個文章,好好實現一下 (@todo)

看了分享者的知乎,之後還會作直接編譯到dom的svelte風格的compiler,vue真的是要作全家桶了嗎

面向新一代構建工具優化代碼

要充分用上這些新工具的獨特能力,如更快的模塊熱更新、跨模塊代碼複用、強構建緩存等,咱們的項目代碼也須要與時俱進。

本主題將會分享我在研究構建工具時總結的一些易於發揮工具潛力的編碼技巧與開發流程實踐。
複製代碼

怎麼說呢,不太能預判出來要講啥,盲猜是這些工具提供的新思路,以及輔助函數? 等看完vueconf後我再寫個總結

應該是個能夠提升開發效率的分享

探索 Vue 3 中的 JSX

對於 JSX 和 template,在大部分場景下是推薦使用 template 的,尤爲是在業務場景下。雖然 Vue 3 的 template 提供了不少的性能優化,可是對於一些庫的開發者來講,template 可能不夠靈活,而使用 JSX 的方式就比較靈活。此次分享,將會給你們聊聊 JSX 和 template 的差別,並分享一些 Vue 3 JSX 插件的設計思路,和 Babel 插件的開發技巧。

jsx的compiler相比於vue3自帶的會稍微簡單一些,由於少了不少標記的優化,主要能學到怎麼參與vue3生態的設計,以及babel的插件技巧

咱們上面的der-compiler,改一下加一個loader 其實也能夠用來解析jsx了

還有一點略困惑,就是template作了不少靜態標記,可是我剛纔跑了個demo,jsx的性能竟然和template差很少,5000個組件相差10ms左右,期待明天能對template和jsx有更多的理解

Composable Vue

Vue Composition API 底層原理介紹,對於編寫優質的組合式函數技巧與模式的分享。

composition的原理還好,vue-next的代碼地址在這

// 2. call setup()
  const { setup } = Component
  if (setup) {
    const setupContext = (instance.setupContext =
      setup.length > 1 ? createSetupContext(instance) : null)

    currentInstance = instance
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    )
 
 
 export function callWithErrorHandling(fn,instance,type,args) {
  let res
  try {
    res = args ? fn(...args) : fn()
  } catch (err) {
    handleError(err, instance, type)
  }
  return res

複製代碼

其餘估計講的更多的是他的vue.use這個庫,也就是把平常的經常使用功能,都封裝成useXX的風格,好比修改一下網頁的小圖標

import { useFavicon } from '@vueuse/core'
const icon = useFavicon()
icon.value = 'dark.png' // change current icon
複製代碼

內部其實就是查找link標籤,修改href,可是api讓你用着很絲滑

export function useFavicon( newIcon: MaybeRef<string | null | undefined> = null, options: FaviconOptions = {}, ) {
  const {
    baseUrl = '',
    rel = 'icon',
    document = defaultDocument,
  } = options

  const favicon = isRef(newIcon)
    ? newIcon
    : ref<string | null>(newIcon)

  const applyIcon = (icon: string) => {
    document?.head
      .querySelectorAll<HTMLLinkElement>(`link[rel*="${rel}"]`)
      .forEach(el => el.href = `${baseUrl}${icon}`)
  }

  watch(
    favicon,
    (i, o) => {
      if (isString(i) && i !== o)
        applyIcon(i)
    },
    { immediate: true },
  )

  return favicon
}

複製代碼

對了其實favicon還能夠設置成視頻和攝像頭,回頭我去提個pr 囧

原理能夠看我以前的文章

技術摸魚工具開發(1)60行代碼實現favcion摸魚

用 Vue.js 構建一個高性能 Flutter 應用

在當下,跨端已是前端繞不開的一個話題,隨着 IoT 設備的普及愈來愈多的「端」涌現到咱們的平常開發中。自 Hybrid 、React Native(Weex)後,Flutter 憑藉其精簡的渲染管線以及自繪渲染的特性,一躍成爲這兩年跨端的新寵。但其用 Dart + Widget 的開發模式以及與前端割裂的生態形成了研發成本太高。基於這個背景,咱們探索一種基於 W3C 標準的 Flutter 應用方案,向上對接前端生態,向下經過自繪保證多端一致性。將給你們帶來一個用 Vue.js 開發 Flutter 應用的方案,以及 Kraken 背後的實踐與思考。

組內大兄弟說挺好用,可是我對flutter興趣不大,自己也是個菜雞,回頭讓大帥寫點感想發出來

基於 Vue 3.0 開發多端應用

新版 uni-app 編譯器升級爲 Vite,運行時升級爲 Vue 3.0,升級後的 uni-app編譯速度有了指數級的提高,運行時也有不少性能優化。

本次我將分享 uni-app 在升級 Vue 3.0 過程當中的系列探索和思考,主要內容包括:
一、基於 Vite 從新實現 uni 編譯器
二、利用 Composition API 重寫 uni-app 內置組件
三、更小、更快、可搖樹的 uni runtime
四、根據平臺差別,量身裁剪 Vue 3.0 runtime
五、使用 Vue 3.0開發多端應用的優點(運行性能/靈活性/編譯速度等)

複製代碼

vue跨端也是一個市場很大的應用,runtime-core和runtime-dom拆開以後,咱們能夠基於runtime-core本身定製renderer。把vue3渲染到任意你想要的平臺,以前學習的時候寫過一個渲染canvas的玩具

import { createRenderer } from '@vue/runtime-core'
let ctx

function draw(ele, isChild) {
  if (!isChild) {
    ctx.clearRect(0, 0, 500, 500)
  }
  ctx.fillStyle = ele.fill || 'white'
  ctx.fillRect(...ele.pos)
  if (ele.text) {
    ctx.fillStyle = ele.color || 'white'
    ele.fontSize = ele.type == "h1" ? 20 : 12
    ctx.font = (ele.fontSize || 18) + 'px serif'
    ctx.fillText(ele.text, ele.pos[0] + 10, ele.pos[1] + ele.fontSize)
  }
  ele.child && ele.child.forEach(c => {
    draw(c, true)
  })

}

const { createApp: originCa } = createRenderer({
  insert: (child, parent, anchor) => {
    if (typeof child == 'string') {
      parent.text = child
    } else {
      child.parent = parent
      if (!parent.child) {
        parent.child = [child]
      } else {
        parent.child.push(child)
      }
    }
    if (parent.nodeName) {
      draw(child)
      if (child.onClick) {
        ctx.canvas.addEventListener('click', () => {
          child.onClick()
          setTimeout(() => {
            draw(child)
          })
        }, false)
      }
    }


  },
  createElement(type, isSVG, isCustom) {
    return {
      type
    }
  },
  setElementText(node, text) {
    node.text = text
  },
  patchProp(el, key, prev, next) {
    el[key] = next
  },
  parentNode: node => node,
  nextSibling: node => node,
  createText: text => text,
  remove:node=>node

});
function createApp(...args) {
  const app = originCa(...args)
  return {
    mount(selector) {
      const canvas = document.createElement('canvas')
      canvas.width = window.innerWidth
      canvas.height = window.innerHeight
      document.querySelector(selector).appendChild(canvas)
      ctx = canvas.getContext('2d')
      app.mount(canvas)
    }
  }
}
export { createApp }



複製代碼

而後就能夠這麼玩, 把canvas的大小和位置變成了響應式控制

<template>
<div @click="setName('vue3真棒')" :pos="[10,10,300,300]" fill="#eee">
    <h1 :pos="[20,20,200,100]" fill="red" color="#000">累加器{{count}}</h1>
    <span :pos="pos" fill="black" >哈嘍{{name}}</span>
</div>

</template>

<script> import {ref} from 'vue' export default { setup(){ const name = ref('kkb') const pos = ref([20,120,200,100]) const count = ref(1) const setName = (n)=>{ name.value = n pos.value[1]+=20 count.value+=2 } return { name, setName, pos, count } } } </script>



複製代碼

咱們組內有配合pixijs寫了個vue3打飛機大戰,你們能夠參考玩一下

Converting Browser Libraries into Vue hooks/composables.

惟一一個老外的分享 感受和vueuse的內容可能會有衝突,也沒有啥具體的介紹,猜不到講啥,經過做者Jonathan的做品來看

仍是啥也沒看出來,期待實際分享吧

閃電分享

按照以往vueconf的習慣,除了這些議題,還會有大佬進行閃電分享,此次也不例外,先賣個關子, 等結束後我再寫點感想

總結

但願這篇文章可以幫助你更好的旁聽明天的vueconf,yeah 歡迎你們關注個人專欄,後續有最新文章會及時發出來

我掏腰包你們報銷三張vueconf的線上票 微信搜索並關注花果山前端, 回覆vueconf便可參與 直接給你紅包

回覆讀書加羣,明天一塊兒羣裏直播討論Vueconf 的心得體會,打王者也歡迎

相關文章
相關標籤/搜索