VueJS에서 Quora를 복제하는 편집기

23988 단어 vuejavascript
저는 최근에 Quora 웹 응용 프로그램의 전체 창고 복제를 구축하여 투자조합 프로젝트의 일부분으로 삼기 시작했습니다.저는 Golang과 VueJS를 사용해서 이 점을 실현하고 있습니다. 앞부분의 더욱 도전적인 부분 중 하나는 사용자 정의로 보이는 편집기입니다. 그래서 저는 이 그룹을 어떻게 확장할 수 있는지 제 생각을 공유하기로 했습니다.

수용물


Comparison
A suitable editor
Features
Challenges
Implementation

원본과의 비교


기술 분야의 회사가 자신의 내부 도구를 개발하는 것은 결코 드물지 않다.몇 가지 연구를 한 후에, 나는 당신이 quora에서 본 풍부한 텍스트 편집기는 내부에서 개발된 것이며, 이것은 기원된 것이 아니라는 것을 발견하였다.물론 복제는 좀 어렵다.나는 너무 많은 소스 편집기에서 선택할 수 있다.외관과 행동이qora와 비슷하도록 맞춤형으로 제작합니다. 도전을 받아들이십시오.
키보드를 두드리고 두통을 이틀 동안 지속한 후에 나는 마침내 그것을 뽑았다. 나는 감히 그것이 원작을 복제인처럼 보인다고 말할 수 있다.
원본 및 클론:


적합한 편집기 선택


보이는 편집기에 익숙해지면, 당신의 필요에 따라 맞춤형으로 제작하기 어렵다는 것을 알게 될 것이다.이제 당신은 처음부터 자신의 편집기를 작성할 수 있지만, 이것은 길고 어두운 길이며, 특히 일회성 투자조합 프로젝트에 있어서는 거의 고통스러울 가치가 없다.
더 인기 있는 편집(ckeditor,tinymce,froala,quilljs,editorjs)을 고려한 후에 나는 quilljs이 가장 적합하다고 생각한다. 원인은 두 가지가 있다.
  • 아름답고 깨끗한 API
  • 좋은 기록이 있습니다.
  • .
  • 합리적으로 맞춤 제작 가능
  • 사용자 정의 모듈을 통해
  • 확장 가능

    Quora 편집기의 특징


    편집기 자체가 완전히 복잡하지는 않고, 단지 몇 가지 일만 그것을 독특하게 한다.그것은 bold text, italic, ordered and unordered lists, blockquotes, code-blocks, embedded media, HTML links, and edit history과 같은 대부분의 일반적인 형식 옵션을 가지고 있다.흔치 않은 것은 언급 기능입니다. @ 기호를 입력하여 사용자, 공간, 테마 목록에서 선택할 수 있습니다.또한 도구막대는 3층으로 되어 있어 한 번에 1층만 볼 수 있도록 위아래로 미끄러질 수 있다.복잡하지 않죠?

    도전하다


    내가 직면한 몇 가지 도전은 다음과 같다.
  • 슬라이딩 도구 모음 실현
  • Quill을 덮어쓰는 기본 이미지 프로세서
  • 언급 기능 실현
  • 여러 편집기 처리
  • 실시


    나는 Figma에서 사용자 인터페이스를 그리기 시작했고 색깔과 아이콘 방면에 약간의 개선을 했다.

    Vue으로 전단을 구축하고 있기 때문에, 나는 구성 요소 라이브러리를 사용하면 나의 생활을 더욱 쉽게 할 수 있다고 생각한다.나는 두 개의 유행하는 (vue-quill-editorvue2-editor)을 찾았다.둘 다 좋은데, 도구막대에 더 좋은 사용자 정의를 제공하기 때문에 vuequill editor를 선택했습니다.그러나, 이것은 quilljs가 그림을blob로 변환하는 기본 행위를 덮어쓰는 데 대한 대가입니다.나중에 자세히 설명합니다.
    편집기를 쉽게 설정하려면:
    // register globally
    // [main.js]
    import VueQuill from "vue-quill-editor";
    import 'quill/dist/quill.core.css' // import styles
    import 'quill/dist/quill.snow.css' // for snow theme
    
    //---
    Vue.use(VueQuill);
    //---
    
    
    // [Editor.vue]
    <template>
       <quill-editor 
          class="editor" 
          v-model="content" 
          :options="editorConfig['options']" 
          @ready="onEditorReady($event)" 
          ref="editor"
       >
          <div :id="'toolbar'+toolbarId" slot="toolbar">
          // toolbar markup
             <button class="ql-bold" type="button"><button/>
          </div>
       </quill-editor/>
    </template>
    <script>
    export default {
       //---
       name: "Editor",
       data: () => ({
          content: ""
       }),
       computed: {
          editorConfig: function() {
             return {
                options: {
                   placeholder: "Write your answer...",
                   modules: {
                      toolbar: "#toolbar"
                   } 
                }
             }
          } 
       }
    }
    </script>
    
    이것은 편집기의 기본 설정입니다.Quill은 css 스타일을 덮어쓰고 편집기의 외관을 사용자 정의할 수 있도록 합니다.위 그림에서 보듯이 <quill-editor> 구성 요소는 명명된 슬롯 toolbar을 사용합니다.여기에서 우리는 자신의 도구막대 표시를 작성할 수 있을 뿐만 아니라quill로 하여금 논리를 처리하게 할 수도 있다.
    예를 들어quill은 특정한 클래스 이름 (예를 들어 ql-bold) 을 가진 모든 단추 요소를 찾습니다. 이 클래스 이름은 이 도구막대 요소의 기능을 정의합니다.그러나, 도구막대 표시를 정의하고 스타일을 쉽게 설정할 수 있지만, quill은 실행할 때 기본 스타일을 문서 <head>에 주입하는 것을 볼 수 있습니다.이것은 편집기에서 표시를 제어하지 않는 다른 부분에 스타일을 설정하기 어렵게 합니다. 왜냐하면 주입된 스타일이 우선이기 때문입니다.
    그렇다면 우리는 어떻게 대응해야 하는가?우리도 프로그래밍을 통해 자신의 스타일을 주입했다.같은 선택기를 사용하여 우리의 css 스타일을 문서 헤더에 주입하면 quill 주입된 모든 스타일을 덮어쓸 수 있습니다.그래서 기술적으로 말하자면, 우리는 그들 자신의 커버를 덮어씌웠다. *사악한 웃음*

    몇 가지 방법이 실현될 수 있다.매우 간단한 방법은 style을 사용하여 document.createElement() 요소를 만들고 innerHTML을 다음과 같이 스타일로 설정하는 것입니다.
    const injectStyles = () => {
       const style = document.createElement("style");
       style.innerHTML = `
       selector {
          property: value
       }
       `
    }
    
    // call injectStyles in the `mounted` lifecycle hook
    
    그리고 Vue의 라이프 사이클 연결을 이용하여 편집기 구성 요소를 불러올 때마다 이 방법을 사용할 수 있습니다.나는 이것이 매우 강력하다는 것을 발견했다. 왜냐하면 나는 단지 나의 inspector 옵션을 열고 내가 덮어쓸 모든 요소에 사용할 선택기를 찾으면 편집기가 마음대로 할 수 있기 때문이다.만약 우리가 편집기 용기의 배경색을 바꾸고 싶다면, 우리는 간단하게 .ql-container.ql-snow의 요소를 목표로 삼을 수 있다.

    I won't be able to write all the code for the editor in this post but here's a link to the GitHub repo for your perusal.


    도구 모음으로 돌아갑니다.qora의 편집기를 보세요. 도구막대가 3층으로 되어 있는 것을 볼 수 있습니다.이 점을 실현하는 매우 간단한 방법은 도구막대에 높이를 지정하는 것이다(예를 들어 44px). 도구막대 안에 용기가 하나 있는데 이 용기는 다른 세 개의 용기를 봉인했다. 용기마다 높이가 도구막대와 같고 층으로 되어 있다.그 생각은 도구막대 위아래의 요소를 도구막대가 넘치는 부분을 덮어쓰기 위해 z 인덱스를 사용하는 것이다.우리는 프로그래밍을 통해 도구 모음을 아래로 내릴 수 있다. (top 또는 transform: translate-y 사용) 당기는 값은 도구 모음의 높이와 같고 좋은 과도 효과를 추가할 수 있다.이렇게 하면 한 번에 한 층만 볼 수 있다.

    We could have given the toolbar a negative z-index but that will hinder pointer events so we give the elements above and below a higher z-index instead.


    나는 네가 지금 이미 요점을 이해했다고 생각한다.도구막대에 모든 단추를 마음대로 놓고 그에 맞는 스타일 설정을 하기만 하면 됩니다.

    이미지 처리


    편집기의 다음 기능은 이미지를 처리하는 것입니다.기본적으로 그림을 업로드할 때 quill은blob로 변환합니다.하지만 이것은 우리가 원하는 것이 아니다.Cloudinary나 amazon s3 같은 플랫폼에 그림을 저장하고 URL을 되돌려 편집기에 끼워 넣으려고 합니다.다음과 같이 이미지 프로세서를 정의할 수 있습니다.
    // quill - quill instance, can be gotten from the editor ref
    const quill = this.$refs['editor']
    
    const handleImageUpload = () => {
       const fileInput = document.createElement("input");
       fileInput.setAttribute("type", "file");
       fileInput.click();
    
       fileInput.onchange = () => { // Listen for image upload
          const file = fileInput.files[0];
          const formData = new FormData();
          formData.append('image', file);
    
          if (/^image\//.test(file.type)) {
             // handle server request and return url
             const url = await serverRequest(formData);
             // embed url in editor
             const range = quill.getSelection();
             quill.insertEmbed(range.index, "image", url);
          } else {
             console.warn("[WARNING]: You can only upload images")
          }
    
       }
    }
    
    상기 함수는 type="file"의 입력을 만들고 업로드 이벤트 (즉 로컬 시스템에서 파일을 선택할 때) 를 탐지하여 이미지를 서버에 보내고 편집기에 삽입된 URL을 되돌려줍니다.다음 그림과 같이 이 함수를 quill 모듈로 등록할 수 있습니다.
    // ---
    onEditorReady: function(quill) {
       quill.getModule("toolbar").addHandler("image", () => {
          this.handleImageUpload();
       });
    }
    //---
    
    onEditorReady은 깃털 편집기 구성 요소에서 ready 이벤트를 보낼 때 호출되는 함수입니다.

    기능 언급


    기능 언급은 또 다른 까다로운 부분이다.기본적으로 @을 입력하거나 @ 단추를 누르면 검색 표시줄이 있는 사용자, 공간, 테마 목록이 표시됩니다.나는 제3자 module이 이 점을 실현한 것을 발견했다. 나는 단지 사용자 정의 외관만 필요로 한다.
    <script>
    import "quill-mention";
    import { suggestions } from "../constants/suggestions";
    
    //---
    handleEditorMention: function(searchTerm, renderList, mentionChar) {
       let values;
    
       if (mentionChar === "@") {
          values = suggestions;
       }
    
       if (searchTerm.length === 0) {
          renderList(values, searchTerm);
       } else {
          const matches = [];
          for (let i = 0; i < values.length; i++)
             if (
             ~values[i].value.toLowerCase().indexOf(searchTerm.toLowerCase())
             )
             matches.push(values[i]);
          renderList(matches, searchTerm);
       }
    },
    //---
    </script>
    
    가져오면 이 모듈이 자동으로 등록됩니다.기본적으로, 당신은 suggestions의 목록을 가지고 있습니다(이상적인 경우, 당신은
    서버를 요청하고 이 목록을 가져옵니다) @...을 입력하면 이 모듈이 일치하는지 확인합니다.마찬가지로 injectedStyles 함수의 밑에 있는 목록에서 스타일을 작성하는 것도 쉽다.

    여러 편집기


    이것은 내가 만난 또 다른 걸림돌이다.같은 페이지에 두 개 이상의 편집기 실례가 있는 것을 발견했습니다. 같은 도구막대 id에 인용되기 때문입니다. 이 문제를 해결하는 방법은 편집기마다 다른 도구막대 id를 사용하는 것입니다.그러나 나의 편집기 구성 요소는 부모 구성 요소에 나타나고 부모 구성 요소는 순환에 나타난다. 그러면 우리는 어떻게 ID가 항상 유일하다는 것을 확보합니까?
    shortid 같은 라이브러리를 사용하여 유일한 id를 생성하고 편집기 구성 요소에 도구로 전달하면 도구막대 id와 통합할 수 있습니다. 이것은 우리가 항상 유일한 id를 가지고 있음을 보장합니다.
    <template>
       <quill-editor 
          class="editor" 
          v-model="content" 
          :options="editorConfig['options']" 
          @ready="onEditorReady($event)" 
          ref="editor"
       >
          // id is merged with the generated shortid
          <div :id="'toolbar'+toolbarId" slot="toolbar">
          // toolbar markup
             <button class="ql-bold" type="button"><button/>
          </div>
       </quill-editor/>
    </template>
    
    이것은 사용자 정의 편집기 (예를 들어qora) 를 구축하는 방법에 대한 빠른 설명입니다.도움이 됐으면 좋겠어요.마찬가지로 코드는 repository에서 사용할 수 있다.만약 네가 흥미가 있다면 가서 보아도 된다.이것은 매우 좋은 도전이다. 나는 계속 전체 프로젝트를 완성할 것이다.아래에서 당신의 생각을 공유하거나 문제를 제기하십시오. 저는 토론을 진행하고 싶습니다.

    좋은 웹페이지 즐겨찾기