最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Angular/Vue/React 联合教程(三)解耦视图逻辑

    正文概述 掘金(杜帅在掘金)   2021-03-28   723

    我相信许多同学都会下意识写出以下代码:

    //vue
    watch(formModel, (reqData, _, invalidate) => {
      const handle = request(reqData).then(() => {
        // ... some logic
      })
      invalidate(() => {
        handle.cancel
      })
    })
    

    对啊,监听数据,执行副作用,然后在组件无效时,清除副作用

    别说这个没问题,甚至有没有写这个代码的习惯,都会被用来鉴定开发水平,更有甚者,promise 取消回调的问题,都是一个面试考点

    但是如果我说这个逻辑是错的呢?来看看下面一段加了点新逻辑的情况:

    //vue
    const parentData = inject('parentData')
    watch(formModel, (reqData, _, invalidate) => {
      const handle = request(reqData).then(() => {
        // ... some local logic
        parentData.requestCount ++
      })
      invalidate(() => {
        handle.cancel()
      })
    })
    

    请求回调之后,将注入数据的requestCount+1

    请问,逻辑上,请求完成没有?

    完成了

    但是为什么要取消请求的回调?上其他组件拿到错误的数据呢?

    因为……组件没了?

    组件销毁之后,取消请求回调,是产品经理告诉你的逻辑么?

    不是……,是程序运行逼着你写的逻辑

    那这就是 —— 废逻辑!

    出现这些问题的原因是什么呢?

    事件,只能在源头处取消,不能靠取消回调掩耳盗铃!

    clearTimeout, removeEventlistener 正确,并且:

    // vue
    let end = false
    watchEffect(()=>{
       // 闭包可以访问
       /*
       * if(end) clearTimeout(timer)
       * 错误,阻碍逻辑运行
       */
       // ...some logic
      if(end) clearTimeout(timer)
      // 正确,逻辑得以运行
    })
    
    onBeforeUnmount(()=>{
      end = true
    })
    

    当然,直接用框架提供的方式更好,这里只是说明一下逻辑:

    // vue
    watchEffect(invalidate=>{
       invalidate(()=>{
         clearTimeout(timer)
       })
    })
    

    一句话重申一下:

    对于异步事件来说:泼出去的水,已经泼出去了!

    不能因为组件销毁与否,阻碍逻辑正常运行

    只能取消事件监听

    不可取消事件回调

    视图逻辑解耦

    问题的根源:

    Angular/Vue/React 联合教程(三)解耦视图逻辑

    那你说不对,如果回调不取消,产生一些意外变更,怎么办?

    你的意外变更

    只有可能是 生命周期相关的逻辑

    而现在,需要你忘掉生命周期

    不要利用生命周期触发业务逻辑

    不用生命周期?

    是的,可以试试 React,完全没有生命周期,一样可以开发:

    // react
    function SomeCompo(){
      useEffect(()=>{
        // 这不是生命周期回调,只是一个当前组件自有事件的初始化发生器
        // 依赖数组为空,代表这个视图被创建,即会执行的逻辑
        // 虽然可以等价生命周期,但是不建议这么做,在这里引用任何状态都会报错(未加入依赖数组)
      },[])
      
      useEffect(()=>{
        // useEffect 只代表一系列值跟随另一一列值变化(变化的函数关系)
        setOtherDataChangeAfterData('dataChanged')
      },[data])
    }
    

    这个和上一代前端框架的开发模式差距太大(使用 ng+rx 的同学可能比较适应)

    难道上一代前端框架利用生命周期无法实现某些逻辑么?

    是的,很多逻辑还真的实现不了:

    // vue
    onMounted(()=>{
      request(someData).then((res)=>{
        localData.value = res
      })
    })
    

    这是一个简单的,发送请求的逻辑,咋一看好像没什么问题

    我们来跨组件一下:

    // vue
    
    // parent
    let base = ref(null)
    requestBaseData((res)=>{
      base.value = res
    })
    provide('parentBase', {base})
    
    // child
    const {base} = inject('parentBase')
    onMounted(()=>{
      request(base.value).then((res)=>{
        localData.value = res
      })
    })
    

    子组件请求时,base 是 null!

    mounted 发请求?初始化?显然在跨组件逻辑下,是绝对会扑街的!

    因为子组件的请求参数,对父组件的数据产生了依赖!

    应该的写法是:

    // vue
    // child
    const {base} = inject('parentBase')
    watch(base, (res)=>{
      request(base.value).then((res)=>{
        localData.value = res
      }),
      {
        immediate: false // 默认 false 避开 base 默认值
      }
    })
    

    那用 mounted 绑定 ref 呢?

    一个例子击败你:

    // vue
    <template>
      <div ref="someRef" v-if="base"/>
    </template>
    

    ref 一样会产生依赖!

    不过,一样可以通过响应式回调解决问题:

    // vue
    const {base} = inject('parentBase')
    const someRef = ref(null)
    watch(base,(base)=>{
      if(!base) return
      console.log(someRef.value)
    },{
      flush: 'post' // flush post 为视图变化回调
    })
    
    // react
    const {base} = useContext(ParentBase)
    const someRef = useRef(null)
    // useLayoutEffect 而不是 useEffect
    useLayoutEffect(()=>{
      if(!base) return
      console.log(someRef.current)
    },[base, someRef])
    
    // angular
    @ViewChild('someRef')
    someRef: EelementRef
    constructor(parentBase){
      parentBase$
      .pipe(
        filter(res=>res),
        // ng 的变检由事件驱动(zone会代理所有浏览器事件,promise等),与流同步进行
        // 变更会在下个事件循环完成
        // delay(0)即可获取相应视图
        delay(0)
       ).subscribe(()=>{
        console.log(someRef.el)
      })
    }
    

    绕过所有生命周期,所有变更根据业务逻辑和状态触发

    应用就会变成这个样子:

    Angular/Vue/React 联合教程(三)解耦视图逻辑

    同时,这也是我说为什么 MVVM 已死的原因

    不是说 MVVM 这个模式已死,而是说 ——

    现在这个时代,再用生命周期,你就输了~


    起源地下载网 » Angular/Vue/React 联合教程(三)解耦视图逻辑

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元