完成第一个 Vue3.2 项目后,这是我的技术总结

第一次Composition API

在vue3.2中,正式支持了script setup的写法,这样可以大大简化组件的代码量,减少一些重复操作,我认为当你写vue3时,应该把这当作默认写法。在vue3.2之前,一般会这样写。

javascript 复制代码
<script>
   export default {
     setup(props,ctx){
      const a = ref(0)
      //必须return才能在template中使用,这里就存在一个重复操作的问题,每次都得cv,万一忘记就得检查
      return {
          a
      }
     }
   }
</script>

那么现在,我们可以这样写,对比一下,减少了多少行代码呢

javascript 复制代码
<script setup>
    const a = ref(0)
</script>

PS:之后的代码我会省略script setup,默认都在script setup标签下。

也许你会觉得这样就更简单了,其实恰恰相反,CompositionAPI其实要求你对逻辑处理有更清晰的认识,对于封装有更高的要求,否则,你一样会写成比以前更丑的代码。例如:

javascript 复制代码
   const a = ref(0)
   const b = ref('')
   const c = ref(true)
   const d = reactive({})
   const actionA = ()=>{a.value++}
   const actionC = ()=>{c.value=!c.value}
   const actionB = ()=>{b.value += 'test' }
   const actiond = async ( )=> {
       const res =  await ajax(`url`)
       d.a = res.a
       d.b = res.b
       d.c = res.c
   }
   const resetD = ()=>{
       Object.keys(d).forEach(key=>delete d[key])
   }

这一堆代码其实就是当你没有考虑逻辑,没有想过封装的时候,像流水账一样直接写出来的代码,这些代码真的比optionsApi更好阅读吗,当然不。

这里更加混乱,你很难一眼看出某个函数用的是哪个变量,顺序混乱,这时候需要封装,需要组合,这也是CompositionAPI的含义之一。

javascript 复制代码
//usePage.js
export default ()=>{
    const a = ref(0)
   const b = ref('')
   const c = ref(true)
    const actionA = ()=>{a.value++}
   const actionC = ()=>{c.value=!c.value}
   const actionB = ()=>{b.value += 'test' }
   //这时候需要写return
   return {
       a,actionA,
       b,actionB,
       c,actionC
   }
}
// usePageD.js
export default ()=>{
const d = reactive({})
const actionD = async ( )=> {
       const res =  await ajax(`url`)
       d.a = res.a
       d.b = res.b
       d.c = res.c
   }
   const resetD = ()=>{
       Object.keys(d).forEach(key=>delete d[key])
   }
   return {
       d,actionD,resetD
   }
}

这时候,当我们在不同的组件中使用时,我们可以按需使用,假设我们现在有A和D两个组件

javascript 复制代码
//组件A
import usePage from './usePage'
const {a,actionA} = usePage()

//组件D
import usePage from './usePageD'
const {actionD,resetD} = usePageD()

上述两种,自然时封装组合后更好阅读。更方便的是,他有更好玩的用法。我目前这个项目是一个iOS混合开发的,这其中必不可少的需要用的jsBridge,由于iOS原生的限制,所有回调都是通过其他函数接收的。例如,下方是我调取原生A方法时的代码

javascript 复制代码
//jsBridge.js
const callBridge = (msg)=>{
 try {
     window.webkit.xxxHandler.postMessage(msg)
 }catch(e){
     console.log(msg)
 }
}
export const bridgeA = (id,cb='')=>{
    const msg = {
     func:'A',
     params:{id},
     cb
    }
    callBridge(msg)
}

而原生则会这样告诉我结果(这块是伪代码,毕竟我不会iOS)

javascript 复制代码
evaluateJavaScript(cb(data))

当我使用的时候,就会有这种逻辑

javascript 复制代码
//App.vue
const store = useStore()
window.test = function(data){
    store.commit('saveA',data)
} 
//其他组件中
const handleClick = ()=>{
    bridgeA('123','test')
}

而现在,我可以不需要通过vuex了,这样写不香吗?

javascript 复制代码
//useBridgeA.js
export default ()=>{
const id = ref('')
const saved = reactive({})
window.test = function(data){
    saved.data = data    
}
const handleClick = ()=>{
    bridgeA('123','test')
}
onBeforeUnmount(()=>{window.test = null})
return {saved,handleClick,id}
}

最妙的是,这里实现当使用时注册回调,不使用时移除,通过reactive通信,而且可以把回调方法隐藏起来,我需要的只是结果,而不需要把所有代码都在外层。

当我写组件时,代码将更加简单

javascript 复制代码
<template>
<input v-model="id" />
<button @click="handleClick">
Action A
</button>
</template>
<script setup>
import useBridgeA from './useBridgeA'
const {id,handleClick} = useBridgeA()
</script>

这里其实我也确立了一些我的vue3的写法吧。

组合不仅是功能点的组合,更是把一些关联性比较高的方法,变量放到一起。

在上面这个例子,其实我们可以把回调方法再抽离出来,放一个单独的文件中,我再import,但是这样只会让项目文件越来越多,每次查找的文件越来越多罢了。

思考setup

很少有人会去想,为什么这个新的生命周期叫setup,set up 有建立的意思,难道意思仅仅是这个App创建时吗,那么created显然更好理解一些。

我认为,setup是一个链接,是把数据和template连接起来的一个桥梁,因此才会使用这个动词,本质上这不是一个生命周期,是一个动作,是我们把数据和Vue连接起来。

我把你做的webApp比作一台机器,setup就好比电源线,你把你变量,逻辑作为电源,输入到电源线,机器就启动了。

最常见的问题,忘记写.value

其实在vue3中,我更喜欢用ref,ref结构简单,有着更可靠更方便的响应式。例如,当我们需要声明一个响应式的对象时,你可以有这两种写法

javascript 复制代码
const a = shallowRef({})
const b = reactive({})

但是,当你需要替换整个对象时怎么办?对于变量来说,直接修改value即可。

javascript 复制代码
a.value = {c:1}

对于变量b,那就麻烦了,如果你的对象层级比较简单,我能想到的方法就是用Object.assign

javascript 复制代码
Object.assign(b,{c:1})

如果只是删除这个c这属性,对于变量a,很简单

javascript 复制代码
a.value = {}

对于变量b呢,使用了reactive的那个呢,显然更加麻烦

javascript 复制代码
b=reactive({}) // 报错

能直接这样写吗,不行,这样会报错,因为b是一个const。于是乎,你简单的思考一下,把const 改为let

javascript 复制代码
let b = reactive({})
b.c = 1
b = reactive({})

理论上这样没有问题,在b没有别的依赖或者是被别的变量依赖的时候。某种程度上讲,这样也会丢失响应性。你只能这样做,这也是我之前为什么要写reset的原因

javascript 复制代码
delete b.c
//假设b这个变量中有很多属性,则需要遍历
Object.keys(b).forEach(key=>delete b[key])

上面这些其实都是一些容易被忽略的点,也是我为什么更推荐ref的原因,但是有利有弊,ref最大的问题是容易忘记写.value

javascript 复制代码
const a = ref(0)
a=1 //报错
//做判断的时候
if(a){ //永远为true,因为a是一个对象,不是数字}

这时候,我推荐你使用unref,上面的if判断应该这样写

javascript 复制代码
const a = ref(0)
if(unref(a)>0){
 // do sth
} else {
 // do another
}

你可以毫无心智负担的使用unref,哪怕这个变量不是ref

style v-bind 的优缺点

style v-bind可能很多人不熟悉,我把这称之为vue对css变量的hack。我项目中偶也也会使用一些css变量。

javascript 复制代码
<template>
<p>123</p>
</template>
<style scoped>
p{
 color:var(--pcolor)
}
</style>

这样是纯粹的原生css的写法,vue帮我们做了一个hack.这里需要注意,style中的v-bind里面是一个字符串。

javascript 复制代码
<template>
<p>123</p>
</template>
<script setup>
const pcolor = ref('#000')
</script>
<style scoped>
p{
 color:v-bind('pcolor')
}
</style>

但是我发现一个问题,在某些情况下的伪元素中的content属性似乎不生效,依旧是上面那个模板,我多写几个p

javascript 复制代码
<template>
<div>
    <p>123</p>
    <p>123</p>
    <p>123</p>
    <p>123</p>
</div>
</template>
<script setup>
const text = ref('hello')
</script>
<style scoped>
div p:first-of-type:before{
 content:v-bind('text')
}
</style>

这时候v-bind似乎没生效,这个伪元素不显示,也不知道是bug还是什么,这时候我建议你这样写

javascript 复制代码
<template>
<div>
    <p :data-text="text">123</p>
    <p>123</p>
    <p>123</p>
    <p>123</p>
</div>
</template>
<script setup>
const text = ref('hello')
</script>
<style scoped>
div p:first-of-type:before{
 content:attr(data-text)
}
</style>

pinia or not

pinia约等于vuex5,使用起来和vuex稍有不同,我在项目中是这样使用的

javascript 复制代码
// store/user.js中定义具体的store
export const UserStore =  defineStore('user', {

state:()=>({
    name:'',
    id:''
})
getters:{
nameId:state=>`${state.name}_${state.id}`
}
actions:{
    async getUserInfo(){}
}
})

//store/index.js
//这样写的好处是,以后引用的时候可以直接from '@/store',并且当文件多了,可以用通过webpack的require.context或者vite的import blob来自动处理
export {UserStore} from './user'

比vuex来说少了一个mutation,也不能说没有,只是用$patch函数代替了,使用起来更灵活

javascript 复制代码
import UserStore from  '@/store'
const user = UserStore()
user.name = 'test'
//or
user.$patch({
name:'test',
id:123
})
//or 
user.$patch(state =>{
    state.name = 'test'
    state.id = 123
})

问题是在js环境下,Webstorm似乎没有代码提示?

完全升级你的项目依赖吧

既然都已经使用vue3了,意味着你本来就不再去兼容ie8,ie9这些,什么你可以把你的依赖完成升级到es8,es9,毫无保留的使用fetch、?. 、?? 这些最新的语法,获取更好的编程体验。

相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom11 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试