Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从 v-语法 到 jsx:给 vue 用户的 react 小例子 #7

Open
gaoryrt opened this issue Nov 15, 2018 · 17 comments
Open

从 v-语法 到 jsx:给 vue 用户的 react 小例子 #7

gaoryrt opened this issue Nov 15, 2018 · 17 comments

Comments

@gaoryrt
Copy link
Member

gaoryrt commented Nov 15, 2018

这篇只记录了几个 vue 语法糖在 react jsx 中的实现,可以稍微直观对比一下二者的异同
(越发感觉 react 的定位和 vue 不同,react 比 vue 还要 view 一点
(当然 react 的写法很自由,我这里就不列举了
(我也是渣渣,有不对的地方麻烦指出来,共同学习

@gaoryrt gaoryrt changed the title 从 v-语法 到 jsx:写给 vue 用户的 react 小例子 从 v-语法 到 jsx:给 vue 用户的 react 小例子 Nov 15, 2018
@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 15, 2018

声明式渲染

// vue
<div id="app">
  {{ message }}
</div>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

// react component
class App extends Component {
  constructor() {
    super()
    this.state = {
      message: 'Hello React!'
    }
  }

  render() {
    return (
      <div id="app">
        { this.state.message }
      </div>
    )
  }
}

// react stateless component
function App() {
  const [message, _] = useState('Hello React!')
  return (
    <div id="app">
      {message}
    </div>
  )
}

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 15, 2018

声明式渲染

// vue
<div id="app-2">
  <span v-bind:title="message">
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
  </span>
</div>
var app2 = new Vue({
  el: '#app-2',
  data: {
    message: '页面加载于 ' + new Date().toLocaleString()
  }
})

// react component
class App extends Component {
  constructor() {
    super()
    this.state = {
      message: '页面加载于 ' + new Date().toLocaleString()
    }
  }

  render() {
    return (
      <div id="app-2">
        <span title={ this.state.message }>
          鼠标悬停几秒钟查看此处动态绑定的提示信息!
        </span>
      </div>
    )
  }
}

// react stateless comp
function App() {
	const [message, _] = useState('页面加载于 ' + new Date().toLocaleString())
	return <div id="app-2">
		<span title={message}>
          鼠标悬停几秒钟查看此处动态绑定的提示信息!
        </span>
	</div>
}

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 15, 2018

条件渲染

// vue
<div id="app-3">
  <p v-if="seen">现在你看到我了</p>
</div>
var app3 = new Vue({
  el: '#app-3',
  data: {
    seen: true
  }
})

// react comp
class App extends Component {
  constructor() {
    super()
    this.state = {
      seen: false
    }
  }

  render() {
    return (
      <div id="app-3">
      {
        this.state.seen &&		//   this.state.seen ?
        <p>						//   <p>
          现在你看到我了			//     现在你看到我了
        </p>					//   </p> : null
	  }
      </div>
    )
  }
}

// react stateless comp
function App() {
	const [seen, _] = useState(false)
	return <div id="app-3">
		{seen && <p>现在你看到我了</p>}
	</div>
}

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 15, 2018

循环渲染

// vue
<div id="app-4">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>
var app4 = new Vue({
  el: '#app-4',
  data: {
    todos: [
      { text: '学习 JavaScript' },
      { text: '学习 Vue' },
      { text: '整个牛项目' }
    ]
  }
})

// react comp
class App extends Component {
  constructor() {
    super()
    this.state = {
      todos: [
        { text: '学习 JavaScript' },
        { text: '学习 React' },
        { text: '整个牛项目' }
      ]
    }
  }

  render() {
    return (
      <div id="app-4">
      {
        this.state.todos.map(todo =>
          <li>{ todo.text }</li>
        )
      }
      </div>

    )
  }
}

// react stateless comp
function App() {
	const [todos, _] = useState([
        { text: '学习 JavaScript' },
        { text: '学习 React' },
        { text: '整个牛项目' }
    ])
	return <div id="app-4">
		{todos.map(todo =>
			<li>{todo.text}</li>
		)}
	</div>
}

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 15, 2018

处理用户输入

// vue
<div id="app-5">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">逆转消息</button>
</div>
var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

// react comp
class App extends Component {
  constructor() {
    super()
    this.state = {
      message: 'Hello React.js!'
    }
  }

  reversMessage() {
    this.setState(state => ({
      message: state.message.split('').reverse().join('')
    }))
  }

  render() {
    return (
      <div id="app-5">
        <p>{ this.state.message }</p>
        <button onClick={ () => this.reversMessage() }>逆转消息</button>
      </div>
    )
  }
}

// react stateless comp
function App() {
	const [message, setMessage] = useState('Hello React.js!')
	return <div id="app-5">
		<p>{message}</p>
		<button onClick={_ => setMessage(message.split('').reverse().join(''))}>
			逆转消息
		</button>
	</div>
}

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 15, 2018

处理用户输入

// vue
<div id="app-6">
  <p>{{ message }}</p>
  <input v-model="message">
</div>
var app6 = new Vue({
  el: '#app-6',
  data: {
    message: 'Hello Vue!'
  }
})

// react comp
class App extends Component {
  constructor() {
    super()
    this.state = {
      message: 'Hello React!'
    }
  }

  handleChange(e) {
    this.setState({message: e.target.value})
  }

  render() {
    return (
      <div id="app-6">
        <p>{ this.state.message }</p>
        <input
          value={ this.state.message }
          onChange={ e => this.handleChange(e) }
        />
      </div>
    )
  }
}

// react stateless comp
function App() {
	const [message, setMessage] = useState('Hello React!')
	return <div id="app-6">
		<p>{ message }</p>
        <input
          value={ message }
          onChange={ e => setMessage(e.target.value) }
        />
	</div>
}

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 15, 2018

组件化应用构建

<div id="app-7">
  <ol>
    <todo-item
      v-for="item in groceryList"
      v-bind:todo="item"
      v-bind:key="item.id">
    </todo-item>
  </ol>
</div>
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { id: 0, text: '蔬菜' },
      { id: 1, text: '奶酪' },
      { id: 2, text: '随便其它什么人吃的东西' }
    ]
  }
})

const TodoItem = ({text}) =>
  <li>{ text }</li>

class App extends Component {
  constructor() {
    super()
    this.state = {
      groceryList: [
        { id: 0, text: '蔬菜' },
        { id: 1, text: '奶酪' },
        { id: 2, text: '随便其它什么人吃的东西' }
      ]
    }
  }

  render() {
    return (
      <div id="app-7">
        <ol>
		{
          this.state.groceryList.map(({ id, text })=>
            <TodoItem key={ id } text={ text }></TodoItem>
          )
        }
		</ol>
      </div>
    )
  }
}

@ghost
Copy link

ghost commented Nov 16, 2018

顶一个!

不太会Vue, @gaoryrt 还请补充Vue实现类似功能的对比。(不知道是不是主题之外了,全当增加活跃度来了,有错地方还请补充 = =! )

  1. 回调式Refs或者createRef对子组件或者对原生node的引用
  2. 代码切割和动态加载
  3. 高阶组件
  4. 生命周期
class App extends Component {
    constructor () {
      super()
      this.inputRef = React.createRef()
    }

    componentDidMount () {
      this.inputRef.current.focus()
    }

    render () {
      return (
        <form>
          <input ref={this.inputRef} />
        </form>
      )
    }
}
const Sub1= React.lazy(() => import('./Sub1'))
const Sub2 = React.lazy(() => import('./Sub2'))

class App extends Component {
    render () {
      return (
        <div>
          <React.Suspense fallback={<div>Loading</div>}>
            <Router>
              <Switch>
                <Route path='/sub1' component={Sub1} />
                <Route path='/sub2' component={Sub2} />
              </Switch>
            </Router>
          </React.Suspense>
        </div>
      )
    }
}
function withHOC(WrappedComponent) {
    return calss extends Component {
        // do anything you want

        render () {
            const { extra, ...otherProps } = this.props
            const newProp = fromStateOrMethod
            return (
                <WrappedComponent
                    newProp={newProp}
                    {...otherProps}
                />
            )
        }
    }
}
class App extends Component {
    constructor() {
      // 初始化,设置state等
    }

    componentWillMount() {
      // 第一次render之前调用
    }

    componentDidMount () {
      // 第一次render之后调用, 在这里进行异步操作
    }

    componentWillReceiveProps (nextProps) {
      // 父组件的状态发生变化会子组件会调用该方法,nextProps是传递过来新props
    }

    shouldComponentUpdate () {
      // 决定是否更新,建议使用:PureComponent
    }

    componentWillUpdate () {
      // 更新前调用
    }

    componentDidUpdate () {
      // 更新render之后调用, 不要在这里进行setState, 会导致死循环
    }

    componentDidCatch () {
      // 子组件的异常会倍这个方法捕获,但不能捕获自己组件产生的异常。
      // 可以定义一个ErrorBoundary组件在最外层,将捕获的错误统一处理(例如可以:sentry到服务器)
    }

    componentWillUnmount () {
      // 组件unmount之前会调用
    }

    render () {
      // 渲染
    }
}

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 17, 2018

子组件引用

<div id="ref-demo">
  <input ref="inputRef">
</div>
var refdemo = new Vue({
  el: '#ref-demo',
  mounted() {
    this.$nextTick(  // mounted + $nextTick 对应 componentDidMount
	  () => this.$refs.inputRef.focus()
    )
  }
})

callback ref

class App extends Component {
  componentDidMount () {
    this._inputRef.focus()
  }

  render () {
    return (
      <input ref={ n => this._inputRef = n } />
    )
  }
}

createRef

class App extends Component {
  constructor () {
    super()
    this.inputRef = React.createRef()
  }

  componentDidMount () {
    this.inputRef.current.focus()
  }

  render () {
    return (
      <input ref={ this.inputRef } />
    )
  }
}

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 17, 2018

插一句

本篇主题是 v-语法 到 jsx,旨在使用 react 实现一些 vue 封装的语法糖
这样来说,具有相似 api 的 子组件引用高阶组件 都能直观看出写法异同
但是由于 vue 高度封装的原因,实现其他两个例子确实不如自由的 react 来得简洁

下面的几个就相当于用 vue 实现 react 了

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 17, 2018

路由 + 异步 loading

路由有 vue-router,异步组件 loading 有 高级异步组件的工厂函数
vue-router 可以匹配路由,代码分割,动态加载,可惜不能匹配 loading
高级异步组件可以代码分割,动态加载,匹配 loading,但是不能匹配路由
这里就需要根据需求自己实现了

const Sub1= React.lazy(() => import('./Sub1'))
const Sub2 = React.lazy(() => import('./Sub2'))

class App extends Component {
    render () {
      return (
        <div>
          <React.Suspense fallback={<div>Loading</div>}>
            <Router>
              <Switch>
                <Route path='/sub1' component={Sub1} />
                <Route path='/sub2' component={Sub2} />
              </Switch>
            </Router>
          </React.Suspense>
        </div>
      )
    }
}

Vue.use(vueRouter)

const router = new vueRouter({
  routes: [
    { path: '/sub1', component: () => import('./sub1') },
    { path: '/sub2', component: () => import('./sub2') }
  ]
})

const app = new Vue({
  data: { loading: false },
  template: `<div>
              <div v-if="loading">loading</div>
              <router-view v-else />
            </div>`,
  router
}).$mount('#app')

router.beforeEach((to, from, next) => {
  app.loading = true
  next()
})

router.afterEach(() => {
  app.loading = false
})

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 19, 2018

高阶组件

vue 里面最接近的语法应该是 mixin
但是 react mixin 都被废弃挺久的了
这里就需要根据需求自己实现了

function withHOC(WrappedComponent) {
    return calss extends Component {
        // do anything you want

        render () {
            const { extra, ...otherProps } = this.props
            const newProp = fromStateOrMethod
            return (
                <WrappedComponent
                    newProp={newProp}
                    {...otherProps}
                />
            )
        }
    }
}

function withHOC(WrappedComponent) {
  const { extra, ...otherProps } = WrappedComponent.props
  const newProp = fromStateOrMethod
  return {
    props: otherProps.concat(newProp),
    render (h) {
      return h(WrappedComponent, {
        on: this.$listeners,
        attrs: this.$attrs,
        props: this.$props
      })
    }
  }
}

更多讨论:Discussion: Best way to create a HOC
包括 slot 的实现:探索Vue高阶组件

@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 19, 2018

生命周期

react vue
constructor data() 里 return 前
componentWillMount beforeMount(){}
render templaterender(){}<component>
componentDidMount mounted(){}
componentWillReceiveProps watch 里手动绑定
shouldComponentUpdate
componentWillUpdate beforeUpdate(){}
componentDidUpdate updated(){}
componentDidCatch
componentWillUnmount beforeDestroy(){}

@gaoryrt gaoryrt closed this as completed Nov 19, 2018
@gaoryrt
Copy link
Member Author

gaoryrt commented Nov 19, 2018

点错关闭了。。。

@gaoryrt gaoryrt reopened this Nov 19, 2018
@leechan
Copy link

leechan commented Nov 19, 2018

期待更深入的分析

@gaoryrt
Copy link
Member Author

gaoryrt commented Feb 21, 2019

话说 hook 出来了有些地方可以好好改改

@gaoryrt
Copy link
Member Author

gaoryrt commented Jun 15, 2019

一篇类似的文章:
https://sebastiandedeyne.com/react-for-vue-developers/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants