从Vue入门到React入门

脚手架

vue-cli => create-react-app(create-react-app xxx --typescript)

proxy接口代理

老版本vue-cli的代理在config下的config/proxyConfig中配置,然后再在config/index.js下的proxyTable中引入

module.exports = {
    proxy: {
        '/xxxx': {
            target: 'http://xxx.com', 
            ws: true,
            changeOrigin: true,
            cookieDomainRewrite: {
                '*' : ''
            },
            pathRewrite:{
                'xxxx':'xx'
            }
        }
    }
}

新版本的vue-cli将配置集成到了node_modules中,所以需要在根目录下建立vue.config.js,然后在其中添加相关字段

devServer: {
    proxy: {
        '/xxxx': {
            target: 'http://xxx.com', 
            ws: true,
            changeOrigin: true,
            cookieDomainRewrite: {
                '*' : ''
            },
            pathRewrite:{
                'xxxx':'xx'
            }
        }
    }
}

create-react-app(以下简称CRA...)脚手架的配置在根目录的config下,据其他博文所述,老版本的CRA(2版本之前)可以直接在package.json下配置跨域代理,新版本的则是在根目录下的config/webpackDevServer.config.js中配置proxy的相关内容和vue没啥区别,或在最下方before(app, server) 函数中,自行配置http-proxy-middleware,类似

const zpkProxy = require('http-proxy-middleware')
app.use(zpkProxy('/xxx', {
    target:''
    changeOrigin:true,
    ...
}

路由

vue的路由是在一个单独的js文件new一个Router,通过配置routes数组进行页面的路由创建,然后在模板中配置<router-view>。

export default new Router({
    routes: [
        {
            path: '/',
            name: 'xxx',
            meta: {},
            component: xxx // 同步引入路由所需组件
            // component: () => import(xxx) // 异步引入路由所需组件
        }
    ]
})

使用动画的话,则是用<transition name='xxx' mode='xxx'>包裹router-view。
路由跳转,可以通过以下方式进行跳转:

  1. <router-link to="xxx">
  2. this.$router.push({path: 'xxx', query: {xxx: xxx}})(query换params,参数会随页面刷新而丢失)

页面可以通过this.$route.query获取路由上的参数

react的路由则是通过创建一个Routes函数,内部通过jsx的形式来构建组件

const Routes = () => (
    <HashRouter>
        <Route exact path="/" render={() => <Redirect to="/nav" />} />
        <Switch>
            <Route path="/nav" component={Nav} />
            <Route path="/index/:area" component={Index} />
            <Route path="/course/:id" component={Course} />
        </Switch>
    </HashRouter>
)

路由跳转可以通过以下方式进行跳转

  1. <Link to={xxx}>
  2. this.props.history.push(`/course/${id}`)

路由的参数获取,可以通过以下方式获取

this.props.match.params.xxx

只有在Routes中出现的组件才能使用props获取路由中的参数,举个例子,你在首页路由下的子组件的props中找不到match字段,所以也拿不到参数

Vue中的v-if在React中如何使用

<div v-if="showDivStatus">

<div v-if="showDivStatus">
<div v-else>
// Vue中的jsx
{this.showDivStatus && <div>}
{this.showDivStatus ? <div> : <div>}

同等情况下React的jsx中

{this.state.showDivStatus && <div>}
{this.state.showDivStatus ? <div> : <div>}

Vue中的v-for在React中如何使用

<div v-for="(item, index) in divList" :key = "item.id">
    {{item.name}}
</div>

同等情况下react的jsx中

{this.state.divList.map((item,index) => 
    <div key={item.id}>
        {item.name}
    </div>
)}

Vue中的methods在React中如何使用(定义和传参)

// vue中的函数可以直接写函数名
<div @click = "clickCallback">
// 如果需要参数可以直接写成函数执行的形式
<div @click = "clickCallback(param)">

// vue的方法是定义在methods里的,并不涉及this的问题
export default {
    data () {
        return {
            user: 'abc'
        }
    }
    methods: {
        a () {
            console.log(this.user) // abc
        }
    }
}

React的jsx比较符合函数本身,通过bind方法严格返回一个函数,而不是通过直接的执行来传参

<div onClick = {this.clickCallback.bind(this, param)}>

// 而react的方法是写在class中的
class Index extends React.Component {
    constructor (props) {
        super(props)
        this.state = {
            user: 'abc'
        }
    }
    // 需要箭头函数绑定this
    clickCallback = (param) => {
        console.log(this.state.user, param)
    }

    render () {
        return (
            <div onClick = {this.clickCallback.bind(this, param)}>
        )
    }
}

或者这么写

class Index extends React.Component {
    constructor (props) {
        super(props)
        this.state = {
            user: 'abc'
        }
        // 手动绑定this指向
        this.clickCallback = this.clickCallback.bind(this);
    }
   
    clickCallback (param) {
        console.log(this.state.user, param)
    }
}

组件

Vue一般来说,我们都是用单独的Vue文件来作为局部组件来使用,组件调用时需要使用import引入并在components中定义,也可以使用Vue.component定义无需引入的全局组件。

React有两种组件模式:

  1. 带状态的class xxx extends React.Component
  2. 不带状态的函数组件 function xxx (props) {return(一堆jsx....)}

感觉其中最大的区别就是:

  1. 继承自React.Component的组件,可能使用到了你写了n久Vue都也许用不到Class,反正我写了一年多Vue只在特殊的工厂模式需求下才使用es6的类,或者为了练习Class而去写Class。
  2. 高性能高逼格的纯函数组件+ReactHooks的辅助。。。岂不美哉!

VueX和Redux

其实上面那些都是非常基础的,无非是this、es6和jsx的问题,个人感觉VueX和Redux虽然都是一个集中的状态管理器,但是用起来真的是天壤之别,先说说两者中简单易懂的Vuex

VueX

VueX的关键词有:State、getters、mutations、commit、actions、dispatch。
然后官网盗个图



这玩意咋用呢,举一个最简单的累加器的例子,vuex的所有东西都写在一个store.js里,从项目里粘出来改的,可能手抖删错了

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
    count: 0
}

const getters = {
    getCount (state) {
        return state.count
    }
}

const mutations = {
    addCount () {
        state.count++
    },
    clearCount () {
        state.count = 0
    }
}

const store = new Vuex.Store({
    state,
    mutations,
    getters
})

export default store

然后其他Vue文件中可以直接通过this.$store.count。。。就能获取!反正我是不用严格模式,也不用getter【手动捂脸】
通过this.$store.commit就能调用你在Store中定义的mutations来修改相应全局变量的状态。
mutations------commit是一对,负责异步操作
actions--------dispatch是一对,负责同步操作

个人根据官网图示悟出的最正确的使用姿势,应该是,getter拿state,mutations定义行为,actions在mutations外面套一层,也就是actions中commit对应的mutations,然后在其他Vue组件中疯狂的dispatch来触发actions,进而通过commit触发mutations,最后修改state的状态。

但是。。。我不这么用。。。

Redux

Redux脱离了React还是一个完整的包,所以用起来感觉也很神奇,总的来说,Redux的关键词有:Provider、actions、reducer、combineReducers、connect、container
官网盗个图。。。竟然没有!全是字,难怪比VueX复杂
官网的todoList的demo虽然没啥东西,但是对于初学者来说有点大,我们还是用一个累加器count的例子举例,代码是从项目里弹层提示相关代码里删改出来的,变量名和参数啥的可能会有些问题,请看官谅解,大体思路是没有问题的,具体步骤如下:

  1. 写一个action(s)
  2. 写一个reducer(s)和reducerCombiner
  3. 写一个需要Redux状态的组件counterPage
  4. 写一个容器包裹counterPage组件
  5. 在React的外层包裹一层Provider
// /src/store/actions/index.js 写一个actions
let count = 0
export const addCount = status => {
    // 参数status就是dipatch调用时传递过来的参数,这里没啥用
    return {
        type: 'ADD_COUNT',
        data: count++
    }
}

/** 其他action,这里也用不到
export const clearCount = status => {
    return {
        type: 'CLEAR_COUNT',
        data: 0
    }
}**/
// /src/store/reducers/count.js 写一个reducer
const count = (state = [], action) => {
    switch (action.type) {
        case 'ADD_COUNT':
            return action.data // 就是上方ADD_COUNT的action内count++对应的data
        /** 
        case 'CLEAR_COUNT':  // 其他action
            return action.data 
        **/
        default:
            return 0
    }
}
export default count


// store/reducers/index.js 写一个reducerCombiner
import { combineReducers } from 'redux'
import count from './count' 

// 这里import进来的reducer的名字(count)决定了后面  容器  从state取值时使用的变量名
const xxxReducers = combineReducers({
  count
})

export default xxxReducers
// 在/src/pages/counterPage.js下随便写一个组件,他的render中用到了redux中的计数
// 这里的count和addCount都需要容器来提供
render () {
    return (
        <div className="container">
            <div className='course-block'>
                <header onClick={this.props.addCount}>
                    <span>{this.props.count}</span>
                </header>
            </div>
            // .......
        </div>
    )
}
// 在/src/container/count.js写一个包裹counterPage.js的容器,将你原来想直接使用<counterPage>组件的地方都换成<count>容器。
import { connect } from 'react-redux'
import { addCount } from '../store/actions'
import CounterPage from '../pages/counterPage'

// 这里state就是你想要的计数器的状态,初始{count:0},ownProps是counterPage的props
const mapStateToProps = (state, ownProps) => {
  // 做一些你想要的处理,这里的返回值就是传递给你counterPage组件的变量
  // state中的变量名取决于你combineReducers时使用的变量名
  return {
    count: state.count
  }
}

// 这里则是定义传递给counterPage的方法,把它和dispatch关联起来
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    addCount: () => {
      dispatch(addCount())
    }
  }
}

// 关联counterPage组件,把状态和方法传递进去
const CourseWithRedux = connect(
  mapStateToProps,
  mapDispatchToProps
)(counterPage)

export default CountWithRedux
// 最后在数据共享的外层也就是/src/index.js增加<Provider>标签
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import xxxReducers from './store/reducers'

let store = createStore(xxxReducers)

ReactDOM.render(
    <Provider store={store}>
        <Routes/>
    </Provider>,
    document.getElementById('root')
);

记得把引用过<counterPage>组件的地方变成<CountWithRedux>容器。我是觉得用起来比VueX繁琐多了。

感觉写的不是很好,所以应该叫从Vue放弃到React放弃,致敬看到最后的你~
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349