├── line-by-line.png ├── side-by-side.png ├── .idea ├── misc.xml ├── vcs.xml ├── modules.xml ├── react-code-diff.iml └── workspace.xml ├── index.html ├── .gitignore ├── dist └── index.html ├── .babelrc ├── src ├── data │ ├── newStr.js │ └── oldStr.js └── main.js ├── README.md ├── package.json ├── index.jsx └── webpack.config.js /line-by-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guhuaijin/react-code-diff/HEAD/line-by-line.png -------------------------------------------------------------------------------- /side-by-side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guhuaijin/react-code-diff/HEAD/side-by-side.png -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Code Diff 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen 10 | /node_modules/ 11 | /dist/ 12 | .DS_Store 13 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Code Diff 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "targets": { 7 | "browsers": ["last 2 versions"], 8 | "node": "current" 9 | } 10 | } 11 | ], ["react"] 12 | ], 13 | "plugins": [ 14 | "react-hot-loader/babel" 15 | ] 16 | } -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/react-code-diff.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/data/newStr.js: -------------------------------------------------------------------------------- 1 | export default ` 2 | 'use strict' 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const isProduction = process.env.NODE_ENV === 'production' 6 | const sourceMapEnabled = isProduction 7 | ? config.build.productionSourceMap 8 | : config.dev.cssSourceMap 9 | 10 | module.exports = { 11 | loaders: utils.cssLoaders({ 12 | sourceMap: sourceMapEnabled, 13 | extract: isProduction 14 | }), 15 | cssSourceMap: sourceMapEnabled, 16 | cacheBusting: config.dev.cacheBusting, 17 | transformToRequire: { 18 | video: 'src', 19 | source: 'src', 20 | img: 'src', 21 | image: 'xlink:href' 22 | } 23 | } 24 | ` 25 | -------------------------------------------------------------------------------- /src/data/oldStr.js: -------------------------------------------------------------------------------- 1 | export default ` 2 | 'use strict' 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const isProduction = process.env.NODE_ENV === 'production' 6 | const sourceMapEnabled = isProduction 7 | ? config.build.productionSourceMap 8 | : config.dev.cssSourceMap 9 | 10 | module.exports = { 11 | loaders: utils.cssLoaders({ 12 | sourceMap: sourceMapEnabled, 13 | extract: isProductionSSSS 14 | }), 15 | cssSourceMap: sourceMapEnabled, 16 | cacheBusting: config.dev.cacheBusting, 17 | transformToRequire: { 18 | video: 'src', 19 | source: 'src', 20 | img: 'src', 21 | image: 'xlink:href' 22 | } 23 | } 24 | ` 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [react-code-diff](https://github.com/guhuaijin/react-code-diff) 2 | 3 | 此组件是基于[vue-code-diff](https://www.npmjs.com/package/vue-code-diff)实现的react版本代码对比展示工具。 4 | > 代码对比展示demo 5 | 6 | ## 安装 7 | ```$xslt 8 | npm install react-code-diff 9 | ``` 10 | 11 | ## 使用 12 | ```$xslt 13 | import {Component} from 'react'; 14 | import CodeDiff from 'react-vode-diff'; 15 | 16 | class Demo extends Component{ 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | oldStr: oldStr, 21 | newStr: newStr, 22 | } 23 | } 24 | render(){ 25 | return ( 26 |
27 | ; 28 |
29 | ) 30 | } 31 | } 32 | 33 | export default Demo; 34 | ``` 35 | 36 | ## 参数说明 37 | 38 | | 参数 | 说明 | 类型 | 可选值 | 默认值 | 39 | |---------- |-------- |---------- |------------- |-------- | 40 | | old-string| 陈旧的字符串| string | — | — | 41 | | new-string| 新的字符串| string | — | — | 42 | | context| 不同地方上下间隔多少行不隐藏 | number | — | — | 43 | | outputFormat| 展示的方式 | string | line-by-line,side-by-side | line-by-line | 44 | 45 | 46 | ## 效果展示 47 | 48 | ### line-by-line 49 | ![line-by-line](line-by-line.png?row=true) 50 | ### side-by-side 51 | ![line-by-line](side-by-side.png?row=true) 52 | 53 | ## LICENSE 54 | [MIT](LICENSE) 55 | 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-code-diff", 3 | "version": "1.0.8", 4 | "description": "代码比较组件的react实现", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack-dev-server --inline --progress --mode development", 9 | "build": "webpack --mode development" 10 | }, 11 | "author": "xiaofang.bao", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "babel-core": "^6.26.3", 15 | "babel-loader": "^7.1.5", 16 | "babel-plugin-import": "^1.8.0", 17 | "babel-preset-env": "^1.7.0", 18 | "babel-preset-es2015": "^6.24.1", 19 | "babel-preset-react": "^6.24.1", 20 | "babel-preset-stage-1": "^6.24.1", 21 | "clean-webpack-plugin": "^0.1.19", 22 | "create-react-class": "^15.6.3", 23 | "css-loader": "^1.0.0", 24 | "extract-text-webpack-plugin": "^3.0.2", 25 | "html-webpack-plugin": "^3.2.0", 26 | "less-loader": "^4.1.0", 27 | "lodash-decorators": "^6.0.0", 28 | "postcss-loader": "^2.1.6", 29 | "react-hot-loader": "^4.3.4", 30 | "style-loader": "^0.21.0", 31 | "webpack-cli": "^3.1.0", 32 | "webpack-dev-server": "^3.1.5" 33 | }, 34 | "dependencies": { 35 | "antd": "^3.7.2", 36 | "diff": "^3.5.0", 37 | "diff2html": "^2.4.0", 38 | "highlight.js": "^9.12.0", 39 | "prop-types": "^15.6.2", 40 | "react": "^16.4.1", 41 | "react-dom": "^16.4.1", 42 | "react-katex": "git+https://github.com/talyssonoc/react-katex.git", 43 | "url-parse": "^1.4.3", 44 | "webpack": "^4.16.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /index.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import PropTypes from 'prop-types' 3 | import {createPatch} from 'diff' 4 | import {Diff2Html} from 'diff2html' 5 | import { InlineMath } from "react-katex/dist/react-katex"; 6 | import 'highlight.js/styles/googlecode.css' 7 | import 'diff2html/dist/diff2html.css' 8 | 9 | class CodeDiff extends Component{ 10 | 11 | constructor(props){ 12 | super(props); 13 | } 14 | 15 | render (){ 16 | return
; 17 | } 18 | 19 | 20 | createdHtml (oldString, newString, context, outputFormat){ 21 | function hljs (html) { 22 | return html.replace(/(.+?)<\/span>/g, '$1') 23 | } 24 | let args = ['', oldString || '', newString || '', '', '', {context: context}] 25 | let dd = createPatch(...args) 26 | let outStr = Diff2Html.getJsonFromDiff(dd, {inputFormat: 'diff', outputFormat: outputFormat, showFiles: false, matching: 'lines'}) 27 | let html = Diff2Html.getPrettyHtml(outStr, {inputFormat: 'json', outputFormat: outputFormat, showFiles: false, matching: 'lines'}) 28 | return hljs(html) 29 | } 30 | 31 | html () { 32 | const {oldStr, newStr, context, outputFormat} = this.props 33 | return this.createdHtml(oldStr, newStr, context, outputFormat); 34 | } 35 | } 36 | 37 | CodeDiff.propTypes = { 38 | oldStr: PropTypes.string.isRequired, 39 | newStr: PropTypes.string.isRequired, 40 | context: PropTypes.number, 41 | outputFormat: PropTypes.string 42 | } 43 | CodeDiff.defaultProps = { 44 | oldStr: '', 45 | newStr: '', 46 | context: 5, 47 | outputFormat: 'line-by-line' 48 | } 49 | 50 | export default CodeDiff; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 4 | 5 | 6 | module.exports = { 7 | entry: __dirname + "/src/main.js", 8 | output: { 9 | path: __dirname + "/dist", 10 | filename: "bundle-[hash].js" 11 | }, 12 | devtool: 'none', 13 | devServer: { 14 | contentBase: "./dist", 15 | historyApiFallback: true, 16 | inline: true, 17 | hot: true, 18 | progress: true, 19 | port: 9000 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /(\.jsx|\.js)$/, 25 | exclude: /node_modules/, 26 | // loader: "babel-loader?presets[]=react,presets[]=es2015", 27 | loader: "babel-loader", 28 | query: 29 | { 30 | presets: ["env", "react", "stage-1"], 31 | plugins: [ 32 | [ 33 | "import", 34 | {libraryName: "antd", style: 'css'} 35 | ] 36 | ] 37 | }, 38 | }, 39 | 40 | { 41 | test: /\.css$/, 42 | loader: "style-loader!css-loader?modules", 43 | exclude: /node_modules/, 44 | }, 45 | { 46 | test: /\.css/, 47 | exclude: /src/, 48 | use: [ 49 | {loader: "style-loader",}, 50 | { 51 | loader: "css-loader", 52 | options: { 53 | importLoaders: 1 54 | } 55 | } 56 | ] 57 | }, 58 | ] 59 | }, 60 | plugins: [ 61 | new HtmlWebpackPlugin({ 62 | template: __dirname + "/index.html" 63 | }), 64 | new webpack.HotModuleReplacementPlugin(), 65 | new CleanWebpackPlugin( 66 | ['dist/*.js'], 67 | { 68 | root: __dirname, 69 | verbose: true, 70 | dry: false 71 | } 72 | ) 73 | ] 74 | }; 75 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import CodeDiff from '../index.jsx' 4 | import {Row, Col, Input, Form, Switch, InputNumber}from 'antd' 5 | import newStr from './data/newStr' 6 | import oldStr from './data/oldStr' 7 | import 'antd/dist/antd.css'; 8 | 9 | const {TextArea} = Input; 10 | const FormItem = Form.Item; 11 | 12 | class CodeDiffPage extends React.Component { 13 | constructor(props) { 14 | super(props); 15 | this.state = { 16 | // oldStr: null, 17 | // newStr: null, 18 | format: false, 19 | context: 5, 20 | } 21 | } 22 | 23 | handleFormatChange = () => { 24 | const {format} = this.state 25 | this.setState({format: !format}) 26 | } 27 | 28 | handleContextChange = (e) => { 29 | this.setState({context: e}) 30 | } 31 | 32 | 33 | render() { 34 | const {format, context } = this.state 35 | 36 | const outputFormat = format? 'line-by-line' : 'side-by-side'; 37 | return ( 38 |
39 | 40 | 41 | 42 |