前端手札——vue組件vue-tinymce開發經驗分享

嘮叨

最近公司在開發一個社交管理後臺,看一遍線框圖後發現須要富文本編輯器我便找會上兩年開發的vue-tinymce組件,惋惜的是組件支持仍是vue1,因此這個組件須要升級支持vue2。而後有朋友問我爲什麼不用現有的?由於看一圈回來發覺比較不靠譜的啊,所有都須要賦予id值(明明能夠內部處理的爲什麼要外部傳入?),實在看不下去結果仍是完善本身寫的這個沒多少收藏的庫吧:)html

關於 vue-tinymce

vue-tinymce 只是基於tinymce封裝的vue組件,讓用vue的同窗能快速使用tinymce富文本編輯器。vue

過程

從tinymce開始

接下來分享一些開發過程當中的一些問題,首先要學會初次化,咱們先來看看tinymce的官方例子:node

<!DOCTYPE html>
<html>
<head>
  <script src="https://cloud.tinymce.com/stable/tinymce.min.js"></script>
</head>
<body>
  <textarea>Next, get a free TinyMCE Cloud API key!</textarea>
  <script>
    tinymce.init({
        selector:'textarea'
        //or
        // target: document.querySelector('textarea')
    });
  </script>
</body>
</html>

能看出tinymce須要引入全局才能使用,就沒其餘方式了?因而我找了一下npmjs.org有的有的,能夠用import引入。webpack

因而不用想立馬寫個例子試試git

# index.html
<!DOCTYPE html>
<html>
<body>
  <textarea>Next, get a free TinyMCE Cloud API key!</textarea>
  <script src="dist/main.js"></script>
</body>
</html>

# main.js
import tinymce form 'tinymce';
tinymce.init({
    selector:'textarea'
    //or
    // target: document.querySelector('textarea')
});

結果發現tinymce是加載出來了,可是樣式和圖標那些沒了...好吧不折騰仍是直接引入吧目前來講問題不大。(看看其餘庫都是直接引入,我不折騰算是對了)github

好了,第一步完成了,接下來第二步是得到/設定富文本內容,來看看如下代碼:web

# main.js
tinymce.init({
    selector: 'textarea',
    // 得到editor,當有多個textarea實例時會屢次調用setup
    setup: (editor)=> {
        // 初次化編輯器
        editor.on('init', ()=>{
            // 設置默認值
            editor.setContent('<p>Default Value!</p>');
            // 註冊事件
            editor.on('input change undo redo', ()=>{
                // 得到編輯結果
                console.log(editor.getContent());
            });
        });
    }
})

上面這段是已總結怎樣得到或設置富文本內容,tinymce知道怎樣用就能開始寫vue組件。vue-cli

須要怎樣的vue組件

做爲組件配置固然能夠本身設定的固須要setting的傳入,可能也須要在初次化動手再自定義一些功能因此加上setup,再來是得到editor進行處理一些富文本數據。起步代碼是這樣的:npm

<template>
    <textarea :id="id"></textarea>
</template>
<script>
export default {
    props: ['setting','setup', 'value'],
    data(){ return {id:'tinymce', editor:null}; }
    mounted(){
        const setting = {
            ...this.setting,
            {
                selector: `#${this.id}`,
                setup: (editor)=> {
                    this.setup(editor);
                    this.editor = editor;
                    editor.on('init', ()=>{
                        editor.setContent(this.value);
                        editor.on('input change undo redo', ()=>{
                            this.value = editor.getContent();
                        });
                    });
                }
            }
        };
        tinymce.init(setting);
    },
    beforeDestroy: function(){
        tinymce.remove(this.id);
    }
}
</script>

自管理id

對比其餘vue-tinymce組件都要傳入id我感到很不解,由於根本沒這個必要,因此接下來先解決id自管理問題。app

export default {
    ...
    // 這裏我用render寫在template綁定:id同樣能夠
    render(createElement){
        return createElement('textarea', {
            attrs: {
                id: this.id
            }
        });
    },
    data(){
        return {
            //生成id
            id: 'vue-tinymce-'+Date.now(),
        }
    }
    ...
}

支持v-model雙向綁定

這個簡單,只要傳入字段(props)包含value,使用v-model就能從value得到綁定數據,而後當富文本編輯器數據跟新時使用$emit('input', value)方法便能通知變化跟新value。

export default {
    ...
    watch:{
        value(val){
            // 當傳入值變化時跟新富文本內容
            tinymce.get(this.id).setContent(val);
        }
    },
    mounted(){
        const setting = {
            ...this.setting,
            {
                selector: `#${this.id}`,
                setup: (editor)=> {
                    this.setup(editor);
                    this.editor = editor;
                    editor.on('init', ()=>{
                        editor.setContent(this.value);
                        editor.on('input change undo redo', ()=>{
                            this.$emit('input', editor.getContent());
                        });
                    });
                }
            }
        };
        tinymce.init(setting);
    }
    ...
}

到這裏將近完成了,惋惜此次問題靜靜地出現了:輸入一個字光標就刷到最前面。接下來得解決這問題,思路我猜應該是editor的input事件觸發$emit('input')而後進入watch調用了editor.setContent()方法後致使光標更新,這裏解決辦法是當前編輯不讓觸發editor.seContent()就不會致使光標更新(固然還有其餘方法,好比記錄光標位置)。

const INIT = 0;
const INPUT = 1;
const CHANGED = 2;

export default {
    ...
    watch:{
        value(val){
            // 只在外部引發變化時纔跟新編輯器
            if(this.status === CHANGED || this.status === INIT) return this.status = INPUT;
            tinymce.get(this.id).setContent(val);
        }
    },
    mounted(){
        const setting = {
            ...this.setting,
            {
                selector: `#${this.id}`,
                setup: (editor)=> {
                    this.setup(editor);
                    this.editor = editor;
                    editor.on('init', ()=>{
                        editor.setContent(this.value);
                        editor.on('input change undo redo', ()=>{
                            // 只在用戶輸入致使事件相應時才更新value數據
                            if(this.status === INPUT || this.status === INIT) return this.status = CHANGED;
                            this.$emit('input', editor.getContent());
                        });
                    });
                }
            }
        };
        tinymce.init(setting);
    }
    ...
}

當value從外部更新時才更新編輯器內容,編輯器觸發的內容更新並不須要繞一圈回來再更新編輯器,這樣便能解決光標問題。

結果

就在這vue-tinymce,一些細節不補充,建議看源碼。如下是使用方法:

安裝

$ npm i -D lpreterite/vue-tinymce

使用

# index.html
<div id="app">
  <vue-tinymce
    ref="tinymce"
    v-model="content"
    :setting="setting">
  </vue-tinymce>
</div>
<!-- in last -->
<script src="node_modules/tinymce/tinymce.min.js"></script>

# main.js
import Vue from 'vue';
import VueTinymce from 'vue-tinymce.vue';

new Vue({
    el: '#app',
    data: function(){
        return {
            content: '<p>html content</p>',
            setting: {
                height: 200,
                language_url: "langs/zh_CN.js",
                block_formats: "Paragraph=p;Heading 1=h1;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;"
            }
        }
    }
})

目錄結構

dist/
- index.html
- main.js
- lang/
    -zh_CN.js
node_modules/
- tinymce/

遇到的問題

剛開始想寫vue2組件我跑了一圈github也沒發現比較好的例子,構建工具及配置直接能用的並無,參考的卻是找到一些。webpack配置算是個麻煩事,想盡可能簡化工做就得動動腦子。

vue-cli是個好東西,能幫你快速建立項目,如想建立vue的單頁項目能夠這樣使用:

$ vue init webpack-simple my-product

但是沒見到有快速建立vue組件的項目,因此這裏我寫了一個lpreterite/vue-component-project提供給你們使用。

使用方法:

$ vue init lpreterite/vue-component-project my-vue-component

一路回車以後會提示

vue-cli · Generated "my-vue-component".

   To get started:
   
     cd my-vue-component
     npm install
     npm run dev 
     npm run hot.

項目就這樣建立好來?,剩下交給大家發揮。

這遍文章算是把手上這個大項目的副產品吧 :) 。但願往後有點時間繼續分享其餘在經驗及一些大項目下的組件,歡迎評論和PR!

相關文章
相關標籤/搜索