我们的目标是触发输入框的消息
查看DOM结构发现没有任何输入框
应该是通过监听输入事件后读取数据自己合成出来文字
我们网上逐层找看看会不会有什么思路
可以看到有一个叫做DraftEditor-root的Dom节点,这个大概率是一个React组件的根
我们在他和im-richtext-container两个之间找找有没有什么有用的信息
发现在im-richtext-container的__reactFiber$n5t796psn7s.memoizedProps.children.props.children.props中有一个onChange的回调函数
感觉有戏?我们在控制台打印出来然后追过去下一个断点
再次尝试触发输入,但是没有显示任何的数据,而是一个{_immutable:n}的对象
那我们目前就走投无路了,难道真的就就此停止了?
不!!
我直接给我女神打一个电话表白,表白失败获得度日如年BUFF
然后打开网易云音乐叠一个战歌BUFF
再高喊因为我背负的太多,所以我根本不会输
然后临时叠上LGBT,素食主义者,螺旋武装直升机等BUFF
只要BUFF叠的够满,一级也能打爆BOSS
BUFF叠的差不多了,我们就开干
我们从DraftEditor-root找找思路,发现根据搜索有一个Draft.js的库
同样也有react版本,我就找了一份在线例子看看
https://codesandbox.io/p/sandbox/draftjs-ytf7q
发现对比抖音的结构几乎一致
那我们就去看看react版本的draftjs
可以发现其中我们刚才找到的应该是editorState对象,onChange则是通过一些其他逻辑然后设置editorState
import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';
import 'draft-js/dist/Draft.css';
class MyEditor extends React.Component {
constructor(props) {
super(props);
this.state = {editorState: EditorState.createEmpty()};
this.onChange = editorState => this.setState({editorState});
}
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange} />
);
}
}
ReactDOM.render(<MyEditor />, document.getElementById('container'));
那我们的目标就是如何生成editorState并且丢给onChange呢?
我们查阅各种提问找到了一个设置初始值为自定义类型的demo
https://stackoverflow.com/questions/71496940/how-can-i-set-defaultvalue-on-react-draft-wysiwyg
import { Editor } from "react-draft-wysiwyg";
import { EditorState, ContentState } from "draft-js";
export const TestComponent = () => {
const [editorState, setEditorState] = useState(() => {
const content = ContentState.createFromText(
"This is your default content."
);
return EditorState.createWithContent(content);
});
return (<Editor
editorState={editorState}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
onEditorStateChange={onEditorChange}
/>);
}
export default TestComponent;
到这里我们就知道了需要调用ContentState.createFromText
以及EditorState.createWithContent
生成,我们可以选择Webpack核心注入方式拿到draft.js的函数,但是由于Tree-Shark的原因难以保证没有被调用的函数依然能正确运行,同时由于这种初始化数据的函数极有可能是有一个无状态函数,所以我们尝试直接引入一个draft.js生成editorState内容试试。
直接引入// @require https://cdn.bootcdn.net/ajax/libs/draft-js/0.11.7/Draft.js
会报错,根据查阅发现还需要react以及react-dom,过于复杂了,所以感觉建立一个项目通过webpack打包到一起暴露出去吧。
安装webpack依赖npm i webpack webpack-cli -D
,安装draft依赖npm i draft-js
,然后编写代码
import { EditorState, ContentState, convertFromHTML } from "draft-js";
export function init() {
return {
EditorState,
ContentState,
convertFromHTML,
};
}
然后编写一个简易的webpack模板
//webpack.config.js
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
library: "draftUtils",
},
mode: "production",
optimization: {
usedExports: true,
},
};
通过npx webpack即可打包
然后引入我们的js并且写一个生成函数
// ==UserScript==
// @name New Userscript
// @namespace https://bbs.tampermonkey.net.cn/
// @version 0.1.0
// @description try to take over the world!
// @author You
// @match https://www.douyin.com/*
// @require https://scriptcat.org/lib/1612/1.0.0/draftUtils.js
// @grant unsafeWindow
// ==/UserScript==
const { ContentState, EditorState } = draftUtils.init()
function generateRichText(text) {
const content = ContentState.createFromText(
text
);
return EditorState.createWithContent(content)
}
unsafeWindow.generateRichText = generateRichText
根据测试可以正确生成富文本的数据对象了
接下来我们把刚才的im-richtext-container的__reactFiber$wwgviaigfbb.memoizedProps.children.props.children.props.onChange投递进去这个对象即可实现draft.js的数据触发
结语
撒花