[프런트엔드 프레임워크] Element UI Dialog 구성 요소에서 DOM 작업 수행 예외 문제 분석 및 처리

34397 단어 Web개발하다

문서 목록

  • 1 문제 설명
  • 2 문제 분석
  • 3 솔루션
  • 3.1 footer slot에 요소 배치
  • 3.2 오픈 이벤트에서 구체적인 코드 실행 지연
  • 3.3 업데이트 리셋 함수 실행과 관련된 논리
  • 3.4 rendered의 값을true
  • 로 주동적으로 변경

    1 문제 설명


    Element UI dialog 구성 요소를 사용하여 dialog 대화 상자에 차트를 렌더링하려면 차트 마운트 지점의dom 요소를 가져와야 합니다.Element UI에 의한 dialogbody는 lazy 모드로 렌더링되어 다이어로그가 열렸을 때, 도표를 불러오는 데 실패했습니다!
    샘플 페이지
    
    <html>
    <head>
      <meta charset="UTF-8">
      
      <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    head>
    <body>
      <div id="app">
        <el-button @click="visible = true">Buttonel-button>
        <el-dialog :visible.sync="visible" @open="handleOpen" title="Hello world">
            <div id="hello"><p>Try Elementp>div>
        el-dialog>
      div>
    body>
      
      <script src="https://unpkg.com/vue/dist/vue.js">script>
      
      <script src="https://unpkg.com/element-ui/lib/index.js">script>
      <script>
        var vm = new Vue({
          el: '#app',
          data: function() {
            return { visible: false }
          },
          methods: {
              handleOpen(){
                  console.log("open: "+$("#hello p").html()); // TAG 1
              }
          }
        })
      script>
    html>
    

    element ui dialog 구성 요소는 Dialog가 열릴 때 리셋을 실행하는 오픈 이벤트를 제공합니다.
    공식 홈페이지에서 오픈에 대한 설명은 다음과 같다.
    Dialog의 내용은 렌더링을 게을리합니다. 즉, 처음 열기 전에 들어오는 기본 slot은 DOM에 렌더링되지 않습니다.따라서 DOM 작업을 실행하거나 ref를 통해 구성 요소를 가져오려면 오픈 이벤트 리셋에서 진행하십시오.
    그러나 실제로 사용했을 때는 그렇지 않았다.
    이전 예제에서 TAG 1이 위치한 시점의 실제 페이지는 다음과 같습니다.
    <div id="app">
        <button type="button" class="el-button l-button--default">
            <span>Buttonspan>
        button> 
        <div class="el-dialog__wrapper" style="display: none;">
            <div class="el-dialog" style="margin-top: 15vh;">
                <div class="el-dialog__header">
                    <span class="el-dialog__title">Hello worldspan>
                    <button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close">i>button>
                div>
                
                <div class="el-dialog__footer"><div>Hello Hellodiv>div>
            div>
        div>
    div>
    

    이 때 페이지가 완전하지 않아서 선택기를 통해dom 요소를 가져올 수 없습니다. 그러면 '#hello'dom 요소에 대한 모든 동작이 무효입니다.
    dialog 구성 요소가 감지된 상태는 다음과 같습니다.
    vm.visible                  // true
    vm.$children[1].visible     // true
    vm.$children[1].rendered    // true
    vm.$children[1].opened      // false
    

    2 문제 분석


    el-dialog 구성 요소는visible의 상태를 감청하고true상태일 때 오픈 이벤트를 즉시 터치하지만 이때el-dialogbody의 내용은 아직 과장되지 않았습니다.Vue 구성 요소가 $emit를 통해 트리거된 이벤트는 비동기적으로 실행되지 않고 동기화되기 때문입니다.
    코드는 다음과 같습니다.
     watch: {
            visible: function(e) {
                var t = this;
                e ? (this.closed = !1,
                this.$emit("open"),
                this.$el.addEventListener("scroll", this.updatePopper),
                this.$nextTick(function() {
                    t.$refs.dialog.scrollTop = 0
                }),
                this.appendToBody && document.body.appendChild(this.$el)) : (this.$el.removeEventListener("scroll", this.updatePopper),
                this.closed || this.$emit("close"))
            }
     }
    

    공식 문서에서 말한 바와 같이Dialog의 내용, 즉el-dialog바디의 내용은 게으름!
    그렇다면 게으름 피우는 메커니즘은 무엇일까?
    요소 dialog 구성 요소의 코드를 보면 다음과 같습니다.
    <div class="el-dialog__body" v-if="rendered"><slot>slot>div>
    

    Vue에서 v-if 명령의 특징은 표현식의 값에 따라 DOM에서 원소를 생성하거나 제거하는 것이다. 만약rendered가false라면dom 원소는 제거되고, 만약rendered가true일 때 해당 원소의 복제는 DOM에 다시 삽입된다.v-show 명령은 표현식의 값에 따라 HTML 요소를 표시하거나 숨기며 DOM 요소를 제거하지 않습니다.
    v-if는 타성입니다. 만약 처음에 렌더링을 할 때 조건이 가짜라면 아무것도 하지 않고 조건이 처음으로 진짜가 되었을 때 국부 컴파일을 시작합니다. (컴파일 결과는 캐시됩니다.)
    이것이 바로 element dialog-body 게으름뱅이의 원리다.

    3 솔루션


    3.1 footer slot에 요소 배치


    dialog 구성 요소의 footer slot은 실시간으로 렌더링되며 여기에 놓인dom 요소는 다음과 같습니다.
      <div id="app">
        <el-button @click="visible = true">Buttonel-button>
        <el-dialog :visible.sync="visible" @open="handleOpen" title="Hello world">
            <div slot="footer"><div id="hello"><p>Try Elementp>div>div>
        el-dialog>
      div>
    

    3.2 오픈 이벤트에서 구체적인 코드 실행 지연


    브라우저는 단일 라인으로 전방 코드를 실행하기 때문에, 페이지를 렌더링한 다음에 구체적인 작업을 수행할 수 있는 제어권을 잠시 넘겨줄 수 있다.
        var vm = new Vue({
          el: '#app',
          data: function() {
            return { visible: false }
          },
          methods: {
              handleOpen(){
                  setTimeout(console.log, 100, "open: "+$("#hello p").html()) // TAG 1
              }
          }
        })
    

    3.3 업데이트 리셋 함수에서 관련 논리 실행


    v-if가true일 때 페이지를 다시 렌더링합니다. 렌더링이 끝난 후에 업데이트 리셋 함수를 터치합니다. 이 때 코드를 실행할 수 있습니다.
        var vm = new Vue({
          el: '#app',
          data: function() {
            return { 
              visible: false,
              see: true,
              nosee: false
            }
          },
          updated: function(){
            var dialog = this.$children[1]
            if(dialog.rendered && this.visible) {
                console.log("update: " + $("#hello p").html())
            }
          }
        })
    

    주의해야 할 때에는 다이어로그의 상태를 검증하여 코드의 실행 시기가 정확하고 틀림없음을 확보해야 한다.

    3.4 rendered의 값을true로 주동적으로 변경


    open dialog 이전에 rendered 값을true로 변경합니다
        var vm = new Vue({
          el: '#app',
          data: function() {
            return { visible: false }
          },
          methods: {
              handleOpen(){
                  console.log("open: "+$("#hello p").html()); // TAG 1
              }
          }
        })
    
        vm.$children[1].rendered = true
    

    더 간결하고 효율적인 방법도 있을 수 있습니다. 아시는 분들은 알려주시고 서로 공부하세요.

    좋은 웹페이지 즐겨찾기