最近公司在開發一個社交管理後臺,看一遍線框圖後發現須要富文本編輯器我便找會上兩年開發的vue-tinymce組件,惋惜的是組件支持仍是vue1,因此這個組件須要升級支持vue2。而後有朋友問我爲什麼不用現有的?由於看一圈回來發覺比較不靠譜的啊,所有都須要賦予id值(明明能夠內部處理的爲什麼要外部傳入?),實在看不下去結果仍是完善本身寫的這個沒多少收藏的庫吧:)html
vue-tinymce 只是基於tinymce封裝的vue組件,讓用vue的同窗能快速使用tinymce富文本編輯器。vue
接下來分享一些開發過程當中的一些問題,首先要學會初次化,咱們先來看看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
做爲組件配置固然能夠本身設定的固須要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>
對比其餘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(), } } ... }
這個簡單,只要傳入字段(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!