This article is permalink: github.com/HaoChuan942…

The Vue 3 project is referenced here

preface

The cover is UEditor’s Baidu index broken line chart. Even though it’s 2018 and great rich text editors are coming out all the time, including on mobile, you can see that UEditor is still very popular. Many companies and individuals still use UEditor for their projects. At present, the last version of UEditor website update is 1.4.3.3, which is already in 2016, and today’s front-end development, many friends are using Vue, React such a component front-end framework. This makes integrating UEditor into these “modern” frameworks very uneven. This is why there are many blogs that show how to integrate UEditor into a Vue project:

In order to improve code reusability, and to keep UEditor out of business code as much as possible, A few months ago I introduced a component to a company project that uses UEditor as a two-way v-Model binding, as simple as using the input box. When I’m done, I feel so Vue. And I’ve read a lot of blogs and GitHub projects, and none of them have similar implementations. So I decided to post to NPM to help people who are still thinking about how to integrate UEditor into their Vue projects. A few months down, the basic has been stable, so today through this blog, to share with you.

First look at the renderings:

Click preview warehouse address

Installation

npm i vue-ueditor-wrap
# or
yarn add vue-ueditor-wrap
Copy the code

Quick Start

Complete DEMO based on Vue-CLI 2.x Server rendering DEMO based on Nuxt.

  1. downloadUEditor

    Download the newly compiled UEditor. The latest version of the official website is 1.4.3.3, which has many bugs, such as Issue1. The official website is no longer actively maintained. For the sake of world peace, I have fixed some common bugs and put the compiled files in the assets/ Downloads directory of this repository. You can download them without worry. Of course, you can clone the official source code and compile it yourself.

    Unzip the downloaded package and name it UEditor (just choose the version you want, such as UTf8-php) and put it in the static directory of your project.

    If you are using vue-cli 3.x, you can place the UEditor folder in the public directory of your project.

  2. Introduce the VueUeditorWrap component

    import VueUeditorWrap from 'vue-ueditor-wrap' // ES6 Module
    / / or
    const VueUeditorWrap = require('vue-ueditor-wrap') // CommonJS
    Copy the code

    You can also use it by directly introducing a CDN link, which exposes a global VueUeditorWrap variable (you can read this blog or refer to the repository for details).

    <script src="https://cdn.jsdelivr.net/npm/vue-ueditor-wrap@latest/lib/vue-ueditor-wrap.min.js"></script>
    Copy the code
  3. Certified components

    components: {
      VueUeditorWrap
    }
    // Or register it as a global component in main.js
    Vue.component('vue-ueditor-wrap', VueUeditorWrap)
    Copy the code
  4. V-model binds data

    <vue-ueditor-wrap v-model="msg"></vue-ueditor-wrap>
    Copy the code
    data () {
      return {
        msg: '

    Vue + UEditor + v-model two-way binding

    '
    }}Copy the code

    At this point you can see an initialized UEditor in the page, and it has been successfully data bound! 👏 👏 👏

  5. Modify the configuration according to the project requirements. For complete configuration options, check the source code or official documentation of ueditor.config.js

    <vue-ueditor-wrap v-model="msg" :config="myConfig"></vue-ueditor-wrap>
    Copy the code
    data () {
      return {
        msg: '

    Vue + UEditor + v-model two-way binding

    '
    .myConfig: { // The editor is not automatically raised by the content autoHeightEnabled: false.// Initial container height initialFrameHeight: 240.// The initial container width initialFrameWidth: '100%'.// Upload file interface (this address is a temporary interface built to facilitate you to experience the file upload function, do not use it in the production environment!! serverUrl: '//ueditor.szcloudplus.com/cos'.If you are using vue-cli generated projects, you do not need to set this option. Vue-ueditor-wrap will automatically handle common cases. If you need special configuration, see FAQ 2 below UEDITOR_HOME_URL: '/static/UEditor/'}}}Copy the code

Advanced

  1. How do I get a UEditor instance?

    <vue-ueditor-wrap @ready="ready"></vue-ueditor-wrap>
    Copy the code
    methods: {
      ready (editorInstance) {
        console.log('editor instance${editorInstance.key}: `, editorInstance)
      }
    }
    Copy the code
  2. Sets whether to destroy the UEditor instance in the component’s beforeDestroy hook

    <vue-ueditor-wrap :destroy="true"></vue-ueditor-wrap>
    Copy the code
  3. Select the implementation of the V-Model. The implementation of bidirectional binding depends on listening for changes in the content of the editor. You can choose your own way to listen, but it is recommended to use the default value out of the box.

    <vue-ueditor-wrap mode="listener"></vue-ueditor-wrap>
    Copy the code

    Optional values: observer, listener

    Default value: observer

    Parameter description:

    1. The Observer pattern draws on the MutationObserver API. The advantage is the accuracy of listening, and the disadvantage is that it incurs a little extra performance overhead. You can set the triggering interval by using the observerDebounceTime property, and optionally set the listening behavior of the MutationObserver by using the observerOptions property. The API is only compatible with IE11+, but Vue-ueditor-wrap automatically enables the Listener mode in unsupported browsers.

      <vue-ueditor-wrap
        mode="observer"
        :observerDebounceTime="100"
        :observerOptions="{ attributes: true, characterData: true, childList: true, subtree: true }"
        >
      </vue-ueditor-wrap>
      Copy the code
    2. The listener mode relies on the UEditor’s contentChange event, which has the advantage of relying on the official event API, no additional performance cost, and better compatibility, but the disadvantage is that the listener accuracy is not high, and there are bugs mentioned in the following [FAQ 5].

  4. Is Vue SSR supported?

    Support for server-side rendering since version 2.4.0! This component provides out-of-the-box support for Nuxt projects. However, if you build your own Vue SSR project, you may need to separate the server and client environments and use the forceInit property to force the initialization editor. Chances are you won’t need this property, even if you build your own SSR project.

  5. How to do secondary development (add custom buttons, popovers, etc.)?

    This component provides beforeInit hooks that fire after the UEditor scripts are loaded and before the editor is initialized, allowing you to perform secondary development such as adding custom buttons, pop-ups, and so on by operating on window.UE objects. BeforeInit triggers functions that take editor IDS and configuration parameters as inputs. A simple example of a custom button and a custom popover is provided below. The DEMO repository also provides an example of a custom “center table” button. If you need more secondary development, you can refer to the official API or UEditor source code for examples.

    Custom button Demo
    <vue-ueditor-wrap v-model="msg" @beforeInit="addCustomButtom"></vue-ueditor-wrap>
    Copy the code
    addCustomButtom (editorId) {
      window.UE.registerUI('test-button'.function (editor, uiName) {
        // Register the command when the button is executed. Using the command will have a fallback operation by default
        editor.registerCommand(uiName, {
          execCommand: function () {
            editor.execCommand('inserthtml'.This is a piece of text added by the custom button  ')}})// Create a button
        var btn = new window.UE.ui.Button({
          // Name of the button
          name: uiName,
          / / hint
          title: 'Mouse hover prompt text'.// To add additional styles, you can specify icon icon, icon path refer to FAQ 2
          cssRules: "background-image: url('/test-button.png') ! important; background-size: cover;".// The command executed when clicked
          onclick: function () {
            // You can do your own operation without executing the command
            editor.execCommand(uiName)
          }
        })
    
        // The status of the button to be reflected when clicking on the edit content
        editor.addListener('selectionchange'.function () {
          var state = editor.queryCommandState(uiName)
          if (state === -1) {
            btn.setDisabled(true)
            btn.setChecked(false)}else {
            btn.setDisabled(false)
            btn.setChecked(state)
          }
        })
    
        // Since you added a button, you need to return the button
        return btn
      }, 0 /* Specifies where to add to the toolbar. By default, append to the last */, editorId /* specifies which editor instance the UI belongs to. By default, all editors on the page will add this button */)}Copy the code
    Custom popover Demo
    <vue-ueditor-wrap v-model="msg" @beforeInit="addCustomDialog"></vue-ueditor-wrap>
    Copy the code
    addCustomDialog (editorId) {
      window.UE.registerUI('test-dialog'.function (editor, uiName) {
        / / create the dialog
        var dialog = new window.UE.ui.Dialog({
          // Specify the path of the page in the pop-up layer. Only pages are supported here. See FaQS 2 for the path
          iframeUrl: '/customizeDialogPage.html'.// You need to specify the current editor instance
          editor: editor,
          // Specify the name of the dialog
          name: uiName,
          // Dialog title
          title: 'This is a custom Dialog floating layer'.// Specify the peripheral style for dialog
          cssRules: 'width:600px; height:300px; '.// If buttons are given, it means that the dialog has confirm and cancel
          buttons: [{className: 'edui-okbutton'.label: 'sure'.onclick: function () {
                dialog.close(true)}}, {className: 'edui-cancelbutton'.label: 'cancel'.onclick: function () {
                dialog.close(false)}}]})// See the custom button above
        var btn = new window.UE.ui.Button({
          name: 'dialog-button'.title: 'Mouse hover prompt text'.cssRules: `background-image: url('/test-dialog.png') ! important; background-size: cover; `.onclick: function () {
            / / render dialog
            dialog.render()
            dialog.open()
          }
        })
    
        return btn
      }, 0 /* Specifies where to add to the toolbar. By default, append to last */, editorId /* specifies which editor instance the UI belongs to. By default, all editors on the page will add this button */)}Copy the code

    Pop-up layer in the HTML page customizedialogPage.html

    <! DOCTYPEhtml>
    <html>
    
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
      <meta name="renderer" content="webkit">
      <! -- The page must include internal. Js in order to be able to directly use the instance variable of the currently opened dialog -->
      <! --internal. Js is stored in UEditor/dialogs by default -->
      <script type="text/javascript" src="./UEditor/dialogs/internal.js"></script>
    </head>
    
    <body>
      <h1>hello vue-ueditor-wrap</h1>
      <script>
        // You can use the following global variables directly
        // The instance variable of the currently opened dialog
        console.log('editor: ' + editor);
        // Some common tools
        console.log('domUtils: ' + domUtils);
        console.log('utils: ' + utils);
        console.log('browser: ' + browser);
        dialog.onok = function() {
          editor.execCommand('inserthtml'.'  ');
        };
        dialog.oncancel = function() {
          editor.execCommand('inserthtml'.' I clicked cancel ');
        };
      </script>
    </body>
    
    </html>
    Copy the code

Features

  1. V-model bidirectional data binding! You don’t have to think about instantiation, you don’t have to think about when to getContent and when to setContent, it’s as simple as using input!

  2. Full compliance with the official API, all configuration parameters and instance methods are exactly the same as the official. You can get a fully configured UEditor editor by passing an object to the config property of the vue-ueditor-wrap component. By listening for the Ready event you can get an initialized UEditor instance and execute various methods on that instance.

  3. Automatically add dependent files. You don’t need to import UEditor js files in index.html or main.js yourself. More importantly, even if you use multiple Vue-ueditor-wrap components on the same page, the JS file it depends on will only be loaded once. The reason for this is that you don’t need to load a lot of ueditor-related resources as soon as the user opens the project, all the resource files will only be loaded the first time the Vue-ueditorwrap component is activated. Of course, vue-ueditor-wrap will also accurately judge if you’ve introduced a resource in index.html or main.js, so you don’t have to worry about it reloading.

  4. Each vue-ueditor-wrap component is completely independent. You can even render 99 tuzki at a time using the V-for command above (don’t forget to add the key).

Frequently asked questions

  1. Do browsers of earlier versions, such as Internet Explorer, are supported?

    The same as Vue, the overall support to IE9+👏👏👏

  2. Why am I seeing this error?

    This is due to a misconfigured UEDITOR_HOME_URL parameter. In projects generated by vue CLI 2.x, the default value is ‘/static/UEditor/’, and in projects generated by vue CLI 3.x, the default value is process.env.base_URL + ‘UEditor/’. But this does not satisfy all cases. For example, your project is not deployed in the web root directory, such as “http://www.example.com/my-app/”, you may need to be set to “/ my – app/static/UEditor/”. Whether relative paths are used, whether routes are in history mode, whether the server is configured correctly, and so on can have an impact. Vue-ueditor-wrap will also output the full path of the resource file it is trying to load through the console if the JS fails to load, which you can use to analyze how to fill in. When you need to distinguish between environments, you can set them separately by judging process.env.node_env.

  3. How do I upload pictures and files? Why do I see an error in the return format of background configuration items?

    Upload pictures, documents, and other functions is to cooperate with the background, and you didn’t give the config attribute passed right serverUrl, I provide / / ueditor.szcloudplus.com/cos temporary interface, you can be used for testing, but avoid by all means is used in production environment!!!!!! For details about how to set up an upload interface, refer to the official document.

  4. Single image cross-domain upload failed!

    UEditor’s single image upload is implemented by means of Form + iframe. However, due to the restriction of same-origin policy, the parent page cannot access the document content of cross-domain IFrame, so the single image cross-domain upload failure occurs. I reconstructed the way of single graph upload through XHR, download the latest compiled UEditor resource file can be implemented in IE10+ browser single graph cross-domain upload. For details, click here. Of course, you can also hide the single image upload button by configuring the Toolbars parameter, and with the “custom button” described above, the curve can be saved. The code below is for reference only.

    var input = document.createElement('input')
    input.type = "file"
    input.style.display = 'none'
    document.body.appendChild(input)
    input.click()
    input.addEventListener('change'.(e) = >{
        // Use AJAX to upload, and destroy the DOM after the upload succeeds
        console.log(e.target.files)
    })
    Copy the code
  5. Why did I type it in? “” ! The $#” special characters are not bound successfully?

    When you use the Listener mode, since the v-Model implementation is based on listening for contentChange events on the UEditor instance, and you usually type these special characters while holding shift, The contentChange of the UEditor itself does not trigger when the shift key is held down. You can also try pressing multiple keys at the same time and you will find that contentChange only triggers once. You can use the Observer pattern or step UEditor.

  6. After a single image is uploaded, v-Model is bound with a loading small icon.

    This is also a BUG in UEditor. The latest version I edited fixes this official BUG. If you use the resource file downloaded from the official website, please replace the resource file or refer to Issue1.

For more questions, please submit an ISSUE or visit the chat room. But because this is a personal maintenance project, I also have their own work, so there is no guarantee that timely solve all your problems, if you have any good suggestion or more cool friends operation, also welcome PR, if you feel this component development for you brought tangible convenience, also thank you very much for your Star, and of course coffee:

Code changes, please follow the specified ESLint rules, please perform before PR NPM run lint test code style, most of the grammatical details by NPM run fix correction, after building, remember to revise the package. The version number in the json, It is convenient for me to Review and post it to NPM.

conclusion

While this is a small innovation, UEditor is probably a has-been rich text editor. But in the process of maintaining this project and helping a group of friends solve the ISSUE, I have grown a lot. What touched me most was that a lot of people also sent me E-mail thank-you letters, and I found that some people actually started to use them in the project. The joy of being recognized by others and helping others is truly known only by those who have experienced it. Not long ago, I decided to start blogging at Nuggets, and even though I didn’t write something well, or I made mistakes, I always had a group of warm-hearted and excellent friends in the comments section who would correct me and give me valuable advice. Sharing is fun! If you have good suggestions or cool actions to do, you’re welcome to do PR, but you should run NPM Run Lint first to check the style of your code. Most of the syntax details can also be fixed with the NPM Run fix. Also, please remember to change the version number of package.json, so that I can directly publish to NPM. Of course, if you have a good rich text editor, you can also recommend it in the comments section.