你和我

你和我 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

你和我 收藏了文章 · 3月6日

13个顶级免费所见即所得文本编辑器工具

CKEditor

CKEditor拥有10多年的开发经验,你可以完全放心此文本编辑器的质量。它支持70多种语言,我认为这是你网站的不错选择。它还可以运行在许多不同的浏览器上,并能很好地与大多数前端框架,如reat,vue,angular......你可以使用CDN直接嵌入到你的HTML页面中......。目前它有两个版本并行运行的CKEditor4和CKEditor5,根据不同的使用目的,你会选择适合自己的编辑器。

https://ckeditor.com/

Trumbowyg

Trumbowyg是针对HTML5优化的代码编辑器,它支持大多数流行的浏览器,例如IE9 +,Firefox,Chrome等。据我所知,它包含用于文本编辑的所有工具,仅为20Kb,它轻巧,将帮助你的网站更流畅地运行。此外,它还具有其他支持插件来帮助你更好地工作,例如插入表情符号,其他国家/地区的支持语言,添加声音,插入特殊字符...

https://alex-d.github.io/Trumbowyg/

TinyMCE

TinyMCE 5是一款编辑器,它能让你灵活地编辑、添加或删除本程序中的部分内容。除了基本的编辑器,那么我发现它还提供了很多支持,更好的用户体验,如添加评论,测试检查路径,提供优质的图标和界面,检查拼写的内容...... 然而,这也是它的弱点,因为如果你想使用高级工具,你必须每月支付约25美元。

https://www.tiny.cloud/features/

Quill

Quill是一个开放源代码编辑器,因此可以将其用于所有类型的商业或非商业网站。它有很多功能,如添加链接,图像,视频或添加代码片段的内容…关于Quill,我最喜欢的一点是它的简单设置和显示,可以在多设备屏幕上的所有现代的、响应迅速的web浏览器上显示,还有使用它的常见问题的详细说明。

https://quilljs.com/

Trix

Trix是一个开源的编辑器,可以让你在Web中轻松地撰写消息、写评论、写帖子......,并被良好编程的平板电脑使用。如果你只需要创建内容所需的功能,那么Trix同样是不错的选择。

https://trix-editor.org/

Jodit Editor 3

Jodit Editor 3是一个用纯TypeScript编写的开源github编辑器,不使用任何其他库。它允许你以多种方式设置它,如通过npm、使用CDN......。我喜欢它的是,除了详细的说明,还有一个程序,通过代码让我们自由选择哪些工具附加到Jodit Editor。

https://xdsoft.net/jodit/

Summernote

Summernote是GitHub上的开源编辑器,获得了超过9K星。它是通过Bootstrap框架设计的,具有在你的网站上创建内容所需的所有功能。你只需要下载它的源文件css,js,再加上Bootstrap框架(也支持3、4两个版本)就已经可以为你的网站服务了。

https://summernote.org/

Editor.js

Editor.js是一个开源的块状编辑器,它不会像普通的编辑器那样使用标签HTML,将内容以JSON的形式输出,使其更容易管理。它还支持通过使用API的插件,多亏了这一点,应该任何功能 任何开发者都可以为这个程序贡献更多有趣和有用的插件。

https://editorjs.io/

MediumEditor

MediumEditor是Medium的内置的开放源代码编辑器,用于人们博客。它仅包含编辑器所需的基本实用程序,因此仅约28kB,这将有助于你的网站得到优化。同时如果我们想要添加其他功能,为了优化编辑,MediumEditor还提供了额外的外部实用工具,定期更新。

https://yabwe.github.io/medium-editor/

Wysihtml

Wysihtml是一个由Voog团队构建的开源编辑器。它功能齐全,可以帮助你轻松编辑文本,并且支持大多数现代屏幕浏览器的设备图像。有很多工具我很喜欢它是自动转换不合适的HTML标签率,自动分析内容时从Word, PDF,显示内容为HTML…

http://wysihtml.com/

ContentTools

ContentTools是内置的开源编辑器,可帮助你轻松地一种方式编辑HTML内容。它提供了用于编辑内容的各种实用程序,你还可以轻松地将Message Institute和其他实用程序添加到程序中(请参阅脱机API部分)。我还发现了如何设置,添加或删除程序中的函数的文章…都是非常细致的。

https://getcontenttools.com/demo

Froala

Froala是一个编辑器,可以很容易地为网站设置,并允许你根据预期用途打开广泛的功能。由于它是用纯JavaScript编写的,因此你可以将其用于当今的大多数现代前端框架。它还提供了许多有用的工具,以及编辑图像,添加或编辑视频,添加图标,管理面板等。但是,如果你要使用该工具用于商业目的,则必须购买许可证。

https://froala.com/wysiwyg-editor/tour/

Redactor

Redactor是一款功能齐全的编辑器,具有精美而简单的设计。超过9年的发展,包括很多支持插件,我想这是一个很好的产品。另外它对程序员在使用程序的过程中遇到的每一个常见问题都有极其详细的实例。但是,它也有一个缺点,当你将其用于商业目的时必须购买许可证。

https://imperavi.com/redactor/

查看原文

你和我 回答了问题 · 3月5日

react中forEach和useState一起使用出现的问题

setDatasource是不是应该放在forEach外面呢?

关注 2 回答 1

你和我 收藏了文章 · 3月5日

浅谈js防抖和节流

防抖和节流严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死。所以还是很有必要早点掌握的。(信我,你看完肯定就懂了)

从滚动条监听的例子说起

先说一个常见的功能,很多网站会提供这么一个按钮:用于返回顶部。
返回顶部按钮

这个按钮只会在滚动到距离顶部一定位置之后才出现,那么我们现在抽象出这个功能需求-- 监听浏览器滚动事件,返回当前滚条与顶部的距离
这个需求很简单,直接写:

function showTop  () {
    var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  console.log('滚动条位置:' + scrollTop);
}
window.onscroll  = showTop

但是!

图片描述

在运行的时候会发现存在一个问题:这个函数的默认执行频率,太!高!了!。 高到什么程度呢?以chrome为例,我们可以点击选中一个页面的滚动条,然后点击一次键盘的【向下方向键】,会发现函数执行了8-9次
图片描述

然而实际上我们并不需要如此高频的反馈,毕竟浏览器的性能是有限的,不应该浪费在这里,所以接着讨论如何优化这种场景。

防抖(debounce)

基于上述场景,首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:

  • 如果在200ms内没有再次触发滚动事件,那么就执行函数
  • 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时

效果:如果短时间内大量触发同一事件,只会执行一次函数。

实现:既然前面都提到了计时,那实现的关键就在于setTimeout这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现:

/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
    let timer = null //借助闭包
    return function() {
        if(timer){
            clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
            timer = setTimeout(fn,delay) 
        }else{
            timer = setTimeout(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
        }
    }
}

当然 上述代码是为了贴合思路,方便理解(这么贴心不给个赞咩?),写完会发现其实 time = setTimeout(fn,delay)是一定会执行的,所以可以稍微简化下:


/*****************************简化后的分割线 ******************************/
function debounce(fn,delay){
    let timer = null //借助闭包
    return function() {
        if(timer){
            clearTimeout(timer) 
        }
        timer = setTimeout(fn,delay) // 简化写法
    }
}
// 然后是旧代码
function showTop  () {
    var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  console.log('滚动条位置:' + scrollTop);
}
window.onscroll = debounce(showTop,1000) // 为了方便观察效果我们取个大点的间断值,实际使用根据需要来配置

此时会发现,必须在停止滚动1秒以后,才会打印出滚动条位置。

到这里,已经把防抖实现了,现在给出定义:

  • 对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。

节流(throttle)

继续思考,使用上面的防抖方案来处理问题的结果是:

  • 如果在限定时间段内,不断触发滚动事件(比如某个用户闲着无聊,按住滚动不断的拖来拖去),只要不停止触发,理论上就永远不会输出当前距离顶部的距离。

但是如果产品同学的期望处理方案是:即使用户不断拖动滚动条,也能在某个时间间隔之后给出反馈呢?(此处暂且不论哪种方案更合适,既然产品爸爸说话了我们就先考虑怎么实现)
图片描述

其实很简单:我们可以设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)。

效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。

实现 这里借助setTimeout来做一个简单的实现,加上一个状态位valid来表示当前函数是否处于工作状态:

function throttle(fn,delay){
    let valid = true
    return function() {
       if(!valid){
           //休息时间 暂不接客
           return false 
       }
       // 工作时间,执行函数并且在间隔期内把状态位设为无效
        valid = false
        setTimeout(() => {
            fn()
            valid = true;
        }, delay)
    }
}
/* 请注意,节流函数并不止上面这种实现方案,
   例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。
   也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样
    */

// 以下照旧
function showTop  () {
    var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  console.log('滚动条位置:' + scrollTop);
}
window.onscroll = throttle(showTop,1000) 

运行以上代码的结果是:

  • 如果一直拖着滚动条进行滚动,那么会以1s的时间间隔,持续输出当前位置和顶部的距离

其他应用场景举例

讲完了这两个技巧,下面介绍一下平时开发中常遇到的场景:

  1. 搜索框input事件,例如要支持输入实时搜索可以使用节流方案(间隔一段时间就必须查询相关内容),或者实现输入间隔大于某个值(如500ms),就当做用户输入完成,然后开始搜索,具体使用哪种方案要看业务需求。
  2. 页面resize事件,常见于需要做页面适配的时候。需要根据最终呈现的页面情况进行dom渲染(这种情形一般是使用防抖,因为只需要判断最后一次的变化情况)

思考总结

上述内容基于防抖和节流的核心思路设计了简单的实现算法,但是不代表实际的库(例如undercore js)的源码就直接是这样的,最起码的可以看出,在上述代码实现中,因为showTop本身的很简单,无需考虑作用域和参数传递,所以连apply都没有用到,实际上肯定还要考虑传递argument以及上下文环境(毕竟apply需要用到this对象)。这里的相关知识在本专栏《柯里化》和《this对象》的文章里也有提到。本文依然坚持突出核心代码,尽可能剥离无关功能点的思路行文因此不做赘述。


惯例:如果内容有错误的地方欢迎指出(觉得看着不理解不舒服想吐槽也完全没问题);如果有帮助,欢迎点赞和收藏,转载请征得同意后著明出处,如果有问题也欢迎私信交流,主页有邮箱地址

查看原文

你和我 收藏了文章 · 3月4日

React组件卸载、路由跳转、页面关闭(刷新)之前进行提示

React组件卸载生命周期、路由跳转和页面关闭三者看起来有些类似的地方,比如都是当前组件即将从视口消失,但实际上所触发的事件均不相同。以一个实际案例出发:

某单页应用的文章编辑页用户正在编辑文章,此时尚未保存。

当用户不小心要跳转到另外一个路由时需要提醒用户是否继续跳转,这个过程需要触发路由跳转以及组件卸载

而用户不小心点了关闭标签页按钮,或刷新了页面。这个过程触发了页面卸载事件;

在这个案例中我们需要实现:

1. 用户跳转页面时弹出提示框(路由采用histroy模式)

2. 用户关闭页面时弹出提示框

componentWillUnmount

首先这个钩子函数是在组件卸载前调用的一个函数,它并不能阻止当前组件的卸载。所以不要想方设法在这里做提示,因为即便提示了,组件还是会卸载,文章还是会消失。

路由守卫-<Prompt/>

为了实现第一个功能,需要一个跳转路由之前进行的判断。在react-router-dom 4.0 之后取消了先前的路由守卫(其实我没研究过之前版本的,这个描述摘自网络)。在react-router-dom 4.0之后,实现这个功能可以依靠<Prompt/>组件。文档链接↗

把这个组件添加到你的文章编辑页组件的任意部分

import {Prompt} from 'react-router-dom';
const Editor=()=>{
    return (
        <div>
          <Prompt
            when={true}
            message={location => '文章要保存吼,确定离开吗?'}
          />
        </div>
    )
}

这里有一点需要注意,使用<Prompt/>时,你的路由跳转必须通过<Link/>实现,而不能依靠<a/>原生标签。
点击取消时就会留在当前页面。至此已经实现了路由跳转时提醒用户进行保存的功能。

窗口关闭事件-beforeunload

实现第二个功能需要依靠对窗口的监听。React应用中对于窗口事件的应用远没有DOM事件频繁,所以好久没碰到还是有点手生的。最关键的就是,应该在何时进行监听?

应该在组件挂载时监听事件,组件卸载时移除事件监听。因为我已经开始全面采用hooks新特性了,所以这里使用到useEffect

import React,{useEffect} from 'react';

const Editor=()=>{

 //监听窗口事件
    useEffect(() => {
        const listener = ev => {
            ev.preventDefault();
            ev.returnValue='文章要保存吼,确定离开吗?';
        };
        window.addEventListener('beforeunload', listener);
        return () => {
            window.removeEventListener('beforeunload', listener)
        }
    }, []);
    
//return ...
}

这里有几个需要注意的地方:

  1. useEffect第二个参数为空数组,表示只调用了componentDidMountcomponentWillUnmount两个钩子
  2. 事件监听和移除的第二个参数为同一个事件处理函数
  3. beforeunload事件中的confirmpromptalert会被忽略。取而代之的是一个浏览器内置的对话框。(参考:MDN|beforeunload
  4. 必须要有returnValue且为非空字符串,但是在某些浏览器中这个值并不会作为弹窗信息
查看原文

你和我 提出了问题 · 2月24日

解决react-native加载静态图片或html , 显示文件找不到

测试手机为android

`

//webview用的是react-native-webview


import React, { Component, } from 'react';
import { View, Text, Platform, Image, } from 'react-native'
import { WebView } from 'react-native-webview';
export default class Antv extends Component {
    constructor(props) {
    super(props);
    this.state = {
  
    }
}
render() {
return (
  <View style={{height: 300}}>
    <WebView
        ref={ref => (this.webViewRef = ref)}
        // onMessage={this.onMessage}
        originWhitelist={['*']}
        // allowFileAccess={true}
        // source={{uri:    'file:///android_asset/china_gdp.html'}}
        source={{uri: 'file:///android_asset/base/gdp.html'}}
        javaScriptEnabled={true}
        // decelerationRate='normal'
        scrollEnabled={true}
        // useWebKit={true}
        // mediaPlaybackRequiresUserAction={true}
        // mixedContentMode="compatibility"
        // allowingReadAccessToURL="*"
        // domStorageEnabled={true}
        // onLoadEnd={() => this.setState({ loading: false })}
    />
    <Image
        style={{width: 100, height: 100}}
        source={{uri: 'file:///android_asset/base/CustomerAnalysis.png'}}
    />
  </View>
)
}
}

`
android/app/src/main/assets
image

这样写完以后,运行,手机显示如下
image
webview显示没找到文件,Image也没有显示
这是哪里的问题呢?

关注 2 回答 1

你和我 赞了回答 · 2月3日

解决ant design中表单Form,怎么做到一个form.item的值A改变,另外一个form.item值B改变呢?

A改变肯定是通过click或者change一些事件 肯定是写在A触发的函数里啦~
不然就没有着力点了,怎么知道A改变了呢。

关注 2 回答 1

你和我 提出了问题 · 2月2日

解决ant design中表单Form,怎么做到一个form.item的值A改变,另外一个form.item值B改变呢?

现在我的做法是在A改变所触发的事件中,setfieldsvalue修改B的值,有没有更好的方法呢?

关注 2 回答 1

你和我 收藏了文章 · 1月21日

React-防止内存泄漏处理

问题描述

原博客http://www.czhuangjia.top
在React开发中,我们可能经常会遇到这个一个警告:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method”

意思为:我们不能在组件销毁后设置state,防止出现内存泄漏的情况

出现场景

    //组件B
    class TestContainer extends Component{
        constructor(){
            super()
            this.state = {
                isShow:true
            }
        }
        render(){
            return (
                <div>
                    <button onClick={()=>this.setState({isShow:!this.state.isShow})}>toggle</button>
                    {!!this.state.isShow&&<Test />}
                </div>
            )
        }
    }
    //组件A
    class Test extends Component{
        constructor(){
            super()
            this.state = {
                num:0
            }
        }
        getNum=()=>{
            //模拟异步请求
            this.timer = setTimeout(()=>{
                this.setState({ num: Math.random() })
            },3000)
        }
        render(){
            return (
                <div onClick={this.getNum} style = {{ width: 100, height: 100, background: 'red' }}>
                    {this.state.num}
                </div>
            )
        }
    }
    在本例子中:
        当我们点击组件A时,会发送一个异步请求,请求成功后会更新num的值。
        当我们点击组件B时,会控制组件的A的卸载与装载
当我们点击组件A后,组件A需要3秒的时间才能获取到数据并重新更新num的值,假如我们在这3秒内点击一次组件B,
表示卸载组件A,但是组件A的数据请求依然还在,当请求成功后,组件A已经不存在,此时就会报这个警告(大概意思就是:你组件都没了,你还设置个啥)

解决办法

本问题出现的原因就是:我们应该在组件销毁的时候将异步请求撤销
  • 在componentWillUnmount中撤销异步请求
  1. axios上有撤销异步请求的方法,但是我们有这么多组件,每次都要撤销岂不是太麻烦了
  2. 我们可以利用一个‘开关的思想’,在组件销毁的时候给this上挂载一个属性,每次发送请求的时候,我们判断一下这个属性是否存在(还是麻烦,每次都要判断)
  3. 基于思路2,我们不想每次判断,因此是不是应该将其封装,利用修饰器对componentWillUnmount和setState进行改装
    function inject_unount (target){
        // 改装componentWillUnmount,销毁的时候记录一下
        let next = target.prototype.componentWillUnmount
        target.prototype.componentWillUnmount = function () {
            if (next) next.call(this, ...arguments);
            this.unmount = true
         }
         // 对setState的改装,setState查看目前是否已经销毁
        let setState = target.prototype.setState
        target.prototype.setState = function () {
            if ( this.unmount ) return ;
            setState.call(this, ...arguments)
        }
    }
    @inject_unount
    class BaseComponent extends Component {
    
    }
    //以后我们写组件时直接继承BaseComponent
查看原文

你和我 回答了问题 · 2020-12-15

解决antd使用table是,删除行总是删除后面的行,而且删除后再新增行,之前该行的数据还在,这是怎么回事呢?

其实只要唯一的id作为column的key就可以了,不要直接更新form数据,这样是不对的

关注 2 回答 2

你和我 提出了问题 · 2020-12-09

解决antd 中的表格table 排序,sort返回为1,表格没反应,是怎么回事?

image
handleSorter函数方法
`

handleSorter(a, b) {
    let dingdan = this.formRef.current.getFieldsValue()['dingdan'];
    let rowData1 = dingdan[`table_row_${parseInt(a.key)}`]['table_type'];
    let rowData2 = dingdan[`table_row_${parseInt(b.key)}`]['table_type'];
    console.log(rowData1);
    console.log(rowData2)
    console.log('排序结果')
    console.log(rowData2.localeCompare(rowData1, 'zh-CN'));
    // return 1;
    return rowData2.localeCompare(rowData1, 'zh-CN');

`
结果返回为-1或者1, 表格都没有反应
rowData1和rowData2是排序列的type(是中文的)

关注 2 回答 1

认证与成就

  • 获得 0 次点赞
  • 获得 4 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-09-09
个人主页被 652 人浏览