前端手札——vue组件vue-tinymce开发经验分享

栏目: 编程语言 · 发布时间: 8年前

内容简介:前端手札——vue组件vue-tinymce开发经验分享

唠叨

最近公司在开发一个社交管理后台,看一遍线框图后发现需要富文本编辑器我便找会上两年开发的 vue-tinymce 组件,可惜的是组件支持还是vue1,所以这个组件需要升级支持vue2。然后有朋友问我为何不用现有的?因为看一圈回来发觉比较不靠谱的啊,全部都需要赋予id值(明明可以内部处理的为何要外部传入?),实在看不下去结果还是完善自己写的这个没多少收藏的库吧:)

关于 vue-tinymce

vue-tinymce 只是基于tinymce封装的vue组件,让用vue的同学能快速使用tinymce富文本编辑器。

过程

从tinymce开始

接下来分享一些开发过程中的一些问题,首先要学会 初次化 ,我们先来看看tinymce的官方例子:

<!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引入。

于是不用想立马写个例子试试

# 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是加载出来了,但是样式和图标那些没了...好吧不折腾还是直接引入吧目前来说问题不大。(看看其他库都是直接引入,我不折腾算是对了)

好了,第一步完成了,接下来第二步是 获得/设定富文本内容 ,来看看以下代码:

# 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组件

作为组件配置当然可以自己设定的固需要 setting 的传入,可能也需要在初次化动手再自定义一些功能所以加上 setup ,再来是获得 editor 进行处理一些富文本数据。起步代码是这样的:

<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自管理问题。

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 {
    ...
    watchs:{
        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 {
    ...
    watchs:{
        value(val){
            // 只在外部引起变化时才跟新编辑器
            if(this.status === CHANGED || selt.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.

项目就这样创建好来:ok_hand:,剩下交给你们发挥。

这遍文章算是把手上这个大项目的副产品吧 :) 。希望日后有点时间继续分享其他在经验及一些大项目下的组件,欢迎评论和PR!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Just My Type

Just My Type

Simon Garfield / Profile Books / 2010-10-21 / GBP 14.99

What's your type? Suddenly everyone's obsessed with fonts. Whether you're enraged by Ikea's Verdanagate, want to know what the Beach Boys have in common with easy Jet or why it's okay to like Comic Sa......一起来看看 《Just My Type》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试