学不完的框架,?舔不完的面,Qwik,你到底想嘎哈?

文章首发在公众号:萌萌哒草头将军,最近关注有抽五本书送给大家,关注后回复:活动

grif.gif

最近我又又双学习了一个新框架,Qwik

image.png

真的没完没了了,

image.png

不过作为”前端娱乐圈战地记者”,我继续帮大家踩雷。

Screenshot_2023-06-11-19-13-32-405_com.daimajia.gold-edit.jpg

? 同系列阅读

? 初识qwik

废话不多说,我们先上代码。一个简单的计数器功能

? useSignal

const App = component$(() => {
const count = useSignal(0);
return (
<>
<button onClick$={() => count.value++}>+</button>
<div>{count.value}</div>
</>
);
});
const App = component$(() => {



  



  const count = useSignal(0);





  return (

    <>

      <button onClick$={() => count.value++}>+</button>
      <div>{count.value}</div>
    </>

  );

});
const App = component$(() => { const count = useSignal(0); return ( <> <button onClick$={() => count.value++}>+</button> <div>{count.value}</div> </> ); });

让我们给这个计数器加上监听事件(后面会详细讲)

useVisibleTask$(({ track }) => {
track(() => console.log(count.value))
})
  useVisibleTask$(({ track }) => {
    track(() => console.log(count.value))
  })
useVisibleTask$(({ track }) => { track(() => console.log(count.value)) })

sigal.gif

useSignal,虽然让我想起来了熟悉地SolidJS,但是看写法,这不就是vue3ref吗?是的,看起来很像,那有没有类似reactive呢?

? useStore

当然有啊,useSignal是针对基本变量的。对于非基本类型可以使用useStore

const App = component$(() => {
const data = useStore({count: 0})
return (
<>
<button onClick$={() => data.count++}>+</button>
<p>{data.count}</p>
</>
);
});
const App = component$(() => {



  



  const data = useStore({count: 0})



  return (

    <>

      <button onClick$={() => data.count++}>+</button>
      <p>{data.count}</p>
    </>

  );

});
const App = component$(() => { const data = useStore({count: 0}) return ( <> <button onClick$={() => data.count++}>+</button> <p>{data.count}</p> </> ); });

sigal.gif

useStore可以听提供方法,不过比较复杂:

import {
$,
type QRL
} from "@builder.io/qwik";
type Store = {
count: number
add: QRL<(this: Store) => void>
}
const App = component$(() => {
const data = useStore<Store>({
count: 0,
add: $(function(this) {
this.count++;
})
})
return (
<>
<button onClick$={() => data.add()}>+</button>
<p>{data.count}</p>
</>
);
}
import {
  $,
  type QRL
} from "@builder.io/qwik";
 
type Store = {
  count: number
  add: QRL<(this: Store) => void>
}


const App = component$(() => {
  const data = useStore<Store>({
    count: 0,
    add: $(function(this) {
        this.count++;
    })
  })
  return (
    <>
      <button onClick$={() => data.add()}>+</button>
      <p>{data.count}</p>
    </>  
  );
}
import { $, type QRL } from "@builder.io/qwik"; type Store = { count: number add: QRL<(this: Store) => void> } const App = component$(() => { const data = useStore<Store>({ count: 0, add: $(function(this) { this.count++; }) }) return ( <> <button onClick$={() => data.add()}>+</button> <p>{data.count}</p> </> ); }

sigal.gif

? useComputed$

reactive有了,computed是不是也应该有啊,来了,它就是useComputed$

const capitalizedName = useComputed$(() => {
return count.value + 'mmdctjj';
});
useVisibleTask$(({ track }) => {
track(() => console.log(capitalizedName.value))
})
  const capitalizedName = useComputed$(() => {
    return count.value + 'mmdctjj';
  });



  useVisibleTask$(({ track }) => {

    track(() => console.log(capitalizedName.value))
  })
const capitalizedName = useComputed$(() => { return count.value + 'mmdctjj'; }); useVisibleTask$(({ track }) => { track(() => console.log(capitalizedName.value)) })

computer.gif

? useContext

那依赖注入有没有啊?抱歉,没有,因为Qwik是基于jsx的框架,所以只有拿useContext来将就了

type Store = {
count: number
add: QRL<(this: Store) => void>
}
// ? 创建全局的上下文
const context = createContextId<Store>('uuid')
const App = component$(() => {
const data = useStore<Store>({
count: 0,
add: $(function(this)
this.count++;
})
})
// ? provider数据
useContextProvider(context, data);
return (
<>
<button onClick$={() => data.add()}>+</button>
<p>{data.count}</p>
</>
);
}
const Child = component$(() => {
// ? 消费上下文
const data = useContext(context)
return (
<>
<Child />
</>
);
}
type Store = {
  count: number
  add: QRL<(this: Store) => void>
}
// ? 创建全局的上下文
const context = createContextId<Store>('uuid')



const App = component$(() => {


  const data = useStore<Store>({
    count: 0, 
    add: $(function(this) 
      this.count++;
    })
  })
  // ? provider数据
  useContextProvider(context, data);
  
  return (
    <>
      <button onClick$={() => data.add()}>+</button>
      <p>{data.count}</p>
    </>  
  );
}

const Child = component$(() => {
  // ? 消费上下文
  const data = useContext(context)
  
  return (
    <>
      <Child />
    </>  
  );
}
type Store = { count: number add: QRL<(this: Store) => void> } // ? 创建全局的上下文 const context = createContextId<Store>('uuid') const App = component$(() => { const data = useStore<Store>({ count: 0, add: $(function(this) this.count++; }) }) // ? provider数据 useContextProvider(context, data); return ( <> <button onClick$={() => data.add()}>+</button> <p>{data.count}</p> </> ); } const Child = component$(() => { // ? 消费上下文 const data = useContext(context) return ( <> <Child /> </> ); }

好吧,接下来我们说些不一样的

? useTask$ or useVisibleTask$

上面的例子中,我们所有的监听事件都是通过useVisibleTask$实现的。虽然它看起来和useEffect类似,但是却有着很大的区别。

这得从qwik的架构说起,首先Qwik是个服务端渲染的框架,相当于Next.js(基于React服务端渲染框架)、 Nuxt.js(基于Vue地服务端渲染框架),换句话说,它天生支持服务端渲染的前端框架。所以一个组件的生命周期是从服务器开始的。

useTask$ -------> RENDER ---> useVisibleTask$
|| --- SERVER or BROWSER --- | ----- BROWSER ----- ||
pause|resume
        useTask$ -------> RENDER ---> useVisibleTask$
|| --- SERVER or BROWSER --- | ----- BROWSER ----- || 
                        pause|resume
useTask$ -------> RENDER ---> useVisibleTask$ || --- SERVER or BROWSER --- | ----- BROWSER ----- || pause|resume

它们都是用来注册任务的钩子函数,这个任务在服务端仅仅执行一次,在客户端可能多次渲染。

下面是具体的区别

? useTask$

useTask$首先在服务端执行一次,如果客户端使用track订阅依赖了,那么当客户端渲染之后触发更新时,任务会在客户端再次执行。如果没有track,那么仅仅在客户端执行一次。

const App = component$(() => {
const count = useSignal(0);
useTask$(({ track }) => {
track(() => console.log(count.value))
})
return (
<>
<button onClick$={() => count.value++}>+</button>
<div>{count.value}</div>
</>
);
});
const App = component$(() => {



  



  const count = useSignal(0);


  


  useTask$(({ track }) => {
    track(() => console.log(count.value))

  })





  return (

    <>

      <button onClick$={() => count.value++}>+</button>

      <div>{count.value}</div>

    </>

  );

});
const App = component$(() => { const count = useSignal(0); useTask$(({ track }) => { track(() => console.log(count.value)) }) return ( <> <button onClick$={() => count.value++}>+</button> <div>{count.value}</div> </> ); });

task.gif

页面刷新后服务端也执行了一次任务

? useVisibleTask$

useVisibleTask$:仅仅在浏览器执行,渲染之后立马执行,当订阅的参数发生改变时,任务会被再次执行。

const App = component$(() => {
const count = useSignal(0);
useVisibleTask$(({ track }) => {
track(() => console.log(count.value))
})
return (
<>
<button onClick$={() => count.value++}>+</button>
<div>{count.value}</div>
</>
);
});
const App = component$(() => {



  



  const count = useSignal(0);


  


  useVisibleTask$(({ track }) => {

    track(() => console.log(count.value))

  })





  return (

    <>

      <button onClick$={() => count.value++}>+</button>

      <div>{count.value}</div>

    </>

  );

});
const App = component$(() => { const count = useSignal(0); useVisibleTask$(({ track }) => { track(() => console.log(count.value)) }) return ( <> <button onClick$={() => count.value++}>+</button> <div>{count.value}</div> </> ); });

taskvis.gif

它们还有一个特别重要的参数cleanup,每次新的任务被触发时,都会执行上次任务的cleanup。另外组件被移除时也会被执行。

useVisibleTask$(({ track, cleanup }) => {
// console.log('I am excuted!')
track(() => console.log(count.value))
cleanup(() => console.log('last'))
})
  useVisibleTask$(({ track, cleanup  }) => {
    // console.log('I am excuted!')
    track(() => console.log(count.value))
    cleanup(() => console.log('last'))
  })
useVisibleTask$(({ track, cleanup }) => { // console.log('I am excuted!') track(() => console.log(count.value)) cleanup(() => console.log('last')) })

cleanup.gif

我们可以看到,每次任务触发都是先打印last,然后才是最新的count.value

另外,还有一个重要的配置项:{ strategy: 'document-ready' },此时,会在页面加载完毕立马执行。

useVisibleTask$(() => {
// 渲染完毕之后执行
console.log(2222)
})
useVisibleTask$(() => {
// document-ready立马执行
console.log(1111)
}, { strategy: 'document-ready' })
  useVisibleTask$(() => {

    // 渲染完毕之后执行
    console.log(2222)
  })
  useVisibleTask$(() => {
    // document-ready立马执行
    console.log(1111)
  }, { strategy: 'document-ready' })
useVisibleTask$(() => { // 渲染完毕之后执行 console.log(2222) }) useVisibleTask$(() => { // document-ready立马执行 console.log(1111) }, { strategy: 'document-ready' })

此时console的打印结果是先1111,然后是2222

? 帮它模拟完整的生命周期

综上,我们可以模拟出一个完整的生命周期

useVisibleTask$(() => {
console.log('before mounted!')
}, { strategy: 'document-ready' })
useVisibleTask$(({ cleanup }) => {
console.log('mounted!')
cleanup(() => console.log('unmount'))
})
useVisibleTask$(({ track, cleanup }) => {
track(() => console.log('updated!', count.value))
cleanup(() => console.log('before update'))
})
  useVisibleTask$(() => {

    console.log('before mounted!')
  }, { strategy: 'document-ready' })
  


  useVisibleTask$(({ cleanup }) => {
    console.log('mounted!')
    cleanup(() => console.log('unmount'))
  })
  
  useVisibleTask$(({ track, cleanup }) => {
    track(() => console.log('updated!', count.value))
    cleanup(() => console.log('before update'))
  })
useVisibleTask$(() => { console.log('before mounted!') }, { strategy: 'document-ready' }) useVisibleTask$(({ cleanup }) => { console.log('mounted!') cleanup(() => console.log('unmount')) }) useVisibleTask$(({ track, cleanup }) => { track(() => console.log('updated!', count.value)) cleanup(() => console.log('before update')) })

唯一的瑕疵是before update会在组件销毁时和unmount一起执行一次。

mounted.gif

? 总结

qwik上线一年不到已经17.9kstar了,足见它地优秀了!

今天的分享就这些,如果大家喜欢我一定会再出一篇介绍其它几个有意思的Api的和编译相关的文章。

如果文中有纰漏的地方欢迎指正

我的文章首发在公众号:萌萌哒草头将军,如果你想联系我,可以加我SunBoy_mmdctjj,我们一起成长

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYE1x2Ec' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片