横向对比小程序、Vue2、Vue3、React 基础语法,语法混乱少一点

327 阅读3分钟

回想起自己刚开始同时维护小程序项目、vue项目时,各种语法乱入,伴随着各种错误自己整理了一篇笔记,一直想写篇学习小结,拖到后来学习了react,最近又学习了vue3,笔记越来越长,终于动工了,下面是我的学习小结。

一、模板语法

1、小程序

相关文档

文本插值语法:{{data}}

属性绑定语法:prop="{{data}}" prop="xx{{data}}xx" 使用双引号,undefined值不会被输出到wxml 中。=后""内的语法类似模板字符串 `xx${data}xx`

数据来源:dataproperties中声明

运算支持:支持在{{}} 内进行三元运算、算术运算、逻辑判断、字符串运算,但不支持调用 Math.random()Number()String.prototype.slice()new Date() 等方法。

注:变量一定出现在双大括号内。

<view> {{ key }} </view> 
<view id="{{data}}"> </view>
<view id="item-{{data}}"> </view> 
<checkbox checked="{{false}}"> </checkbox>
<input model:value="{{value}}" /> //简易双向绑定语法
<input model:value="{{ a.b }}" /> //错误:简易双向绑定,不支持属性路径

2、Vue2、Vue3

Vue2相关文档
Vue3相关文档

文本插值语法:{{data}}

属性绑定语法:v-bind:prop="data" :prop="data" :prop="foo(data)" =后""内的语法是JavaScript表达式。

数据来源:

  • Vue2:datacomputedprops中声明
  • Vue3:setuprefreactivecomputeddefineProps等声明的变量

运算支持:{{}}v-bind 指令支持完整的JavaScript表达式,支持调用有限的全局对象,如Math和Date。

<p> {{ key}} </p>
<div :id="data"> </div>
<div :id="'item_' + data"> </div>  
<button :disabled="false"></button> 
<a :[attributeName]="url"> ... </a>  //动态参数
<UserName v-model:first-name="first" v-model:last-name="last" />  vue3多个v-model绑定

3、React

相关文档

采用JSX语法,all in js: {this.state.key} {key} prop={data} {}内的是JavaScript表达式或React元素。

数据来源:

  • Class组件:state中声明
  • 函数式组件Hooks:return 之前声明的常量、使用useState声明的变量

运算支持:{} 可放置任何有效的 JavaScript 表达式、React元素

<p> {{ this.state.key }} </p>  //class组件state中声明的变量
<p> {{ key }} </p> //函数式组件useState声明的变量
<div id={data}> </div>
<div id={'item_' + data}> </div>
{<h1>hello</h1>} 

二、列表渲染

1、小程序

语法:wx:for="{{}}"

wx:for-item='itemName' 指定当前项变量名,默认 item; wx:for-index=''指定当前项下标变量名,默认 index;wx:key 项的唯一标识。

<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName" wx:key='{{itemName.id}}'>  //遍历数组
<view wx:for="abc">   //遍历字符串,字符串解析成字符串数组 

2、Vue2、Vue3

语法:v-for="item in items"

<li v-for="(item, index) in items" :key="">{{ item.message }}</li>  //遍历数组 
<div v-for="(value, key, index) in object" :key=""> {{ key }}: {{ value }}</div> //遍历对象属性值、属性名、位置索引
<span v-for="n in 10" :key="">{{ n }} </span>  //遍历数值范围,n从1开始

3、React

语法:JS数组遍历方法

{
  this.state.list.map(todo => {
    return (
      <Todo key={todo.id} {...todo} />
    )
  })
}

三、条件渲染

1、小程序

语法:wx:if wx:elif wx:else

<view wx:if="{{length > 5}}"></view>  
<view wx:elif="{{length > 2}}"></view>   
<view wx:else></view>
<block wx:if="{{condition}}"> </block>   //<block/>包装元素,不会在页面中做任何渲染,只接受控制属性。

2、Vue2、Vue3

语法:v-if v-else-if v-else

<div v-if="Math.random() > 0.5"></div>   
<h1 v-show="condition">Hello!</h1>  

3、React

语法:JS三元运算符、逻辑运算符

{this.state.isLogin ? (<div>欢迎xxx</div>) : (<div>请登录</div>) }
{this.state.isLogin && <CustomComponent /> }

四、样式绑定

1、小程序

样式属性名使用短横线分隔(kebab-case) ,不能使用驼峰式 (camelCase) ,否则不识别。

类似模板字符串语法:样式属性名不需要添加引号,属性值若是变量,放在{{}}

注:相关文档  推荐使用rpx自适应单位,UI 使用750px的设计图,字号推荐使用(设计图尺寸/2)px(不使用rpx单位因为会跟随屏幕宽度而缩放),其他尺寸全部使用rpx(与设计图1:1css数值), 比如元素宽高、元素边距等。

class="className1 {{ condition ? 'className2' : ''}}"
style="padding-top:{{data}}px; background-color:{{data}}"  

2、Vue2、Vue3

:class="{className1: condition, className2: condition}" //绑定class名 
:class="classObject" //绑定对象 classObject可以是data或计算属性中的变量
:class="[className1, className2, condition?className3:'']"  //绑定class名组成的数组 
:style="{paddingLeft: '10px', 'margin-left': value }"  //css样式对象,属性名可用驼峰式 (camelCase) 或短横线分隔(kebab-case) 
:style='styleObject'   //styleObject可以是data或计算属性中的变量
:style='[styleObject1, styleObject2]'  //css样式对象组成的数组

3、React

className={"foo bar baz"}
className={'foo ' + isActive?'active':'' }
className={['foo', isActive?'active':''].join(' ')}
//使用classnames库 
className={classNames('foo','bar','baz')}
className={classNames('foo',{'active':isActive})}
//行内样式,传入对象而不是css字符串,css属性采用小驼峰写法
style={{fontSize:'14px',margin:'20px 0'}}  

css in js:styled-components

import styled from 'styled-components';
export const Wrapper = styled.div`
  font-size: 12px; 
      p{
          font-size:16px;
      }
  }
`
<Wrapper></Wrapper> //引用定义的样式组件

五、取值、更新值

1、小程序

相关文档

this.data.key  this.setData({key: value}) 
this.data.obj.key  this.setData({'obj.key': value})

2、Vue2、Vue3

Vue2:相关文档

  • 创建 vue 实例时 data 中存在的属性是响应式的,为 data 中对象新增属性需要通过Vue.set()或者this.$set()
  • 数组通过 push pop shift unshift splice sort reverse 这几个方法修改是响应式的
  • 使用新的对象赋值是响应式的
data(){
    return {
        key:'',
        obj:{key:''}
    }
}
this.key  this.key=value 
this.obj.key  this.obj.key = value 
Vue.set(this.obj, newKey, value)  this.$set(this.obj,newKey,value)
this.obj = Object.assign({}, this.obj, { newKey: value })

Vue3:相关文档

  1. ref 通过 .value 赋值。使用ref声明数组可传入泛型,避免类型被推断为 Ref<never[]>
  2. 不能替换一个响应式对象进行赋值,只能给响应式对象的属性赋值,否则丢失响应式代理。
  3. 依靠深层响应性,响应式对象内的嵌套对象依然是代理:嵌套的响应式对象属性,可整体修改为新的普通对象或新的响应式对象,后续更新仍然是响应式的。
// 1. ref 
let flag = ref(true)
flag.value = false
const multipleSelection = ref<any[]>([])
// 2. reactive
const obj = reactive({ aa: { bb: 1, } })
obj = {}  //丢失代理
obj.aa.bb = 2  //响应式更新
// 3. reactive
const obj2 = { bb: 11 }
obj.aa = obj2  //响应式更新
const obj2 = reactive({ bb: 2, })
obj.aa = obj2  //响应式更新

3、React

setState Class组件,setState被设计成异步的,不会立即更新组件,使用回调函数或componentDidUpdate获取更新后的数据

this.state.key  
this.setState({key: value}) 

useState 函数式组件useState,异步更新,无this

const [count, setCount] = useState(0);
setCount(count+1)
useEffect(() => {
  //...
}, [count])

六、事件绑定

1、小程序

相关文档

bindtap bind:tap 两种写法均可,普通事件绑定 bindtap="handlerTap", 数据绑定 bindtap="{{ condition ? 'handlerTap':'' }}" 结果必须是字符串,当变量值为假值时解除绑定。

catchtap="handleTap" 阻止事件冒泡

data-key 绑定属性值,会被传入事件对象的dataset。多个单词由连字符 - 连接会转换成小驼峰写法,如果使用大写字符会转成全小写字符。

<view id="tapTest" data-hi="Weixin" data-current-index='value' data-currentKey='value' bindtap="foo"> Click me! </view>
Page({
    methods:{
      foo: function(e) {
        console.log(e.target.dataset) //{currentIndex: "value", currentkey: "value", hi: "Weixin"}
      }
    }
})

2、Vue2、Vue3

相关文档

v-on:EventType='methodName(item, $event)' 表达式/函数名/传参、不传参函数调用语句。$event合成对象,原始的 DOM事件对象 + vue封装的属性(key按键修饰符、target可以是子组件等)。

监听原生DOM事件时,如果该方法不需要额外参数,那么方法后的()可以不添加,$event作为唯一的参数被默认传递。

如果需要同时传入多个参数,且需要使用事件参数event时,可以通过$event显示传入。

  • 事件修饰符:.stop stopPropagation().prevent preventDefault().capture 监听事件捕获,.self 子元素不触发, .once 事件触发一次
  • 按键修饰符别名:@keyup.alt.67='' .enter .tab .delete .esc .space .up .down .left .right .ctrl .shift .alt .meta

ViewModel 被销毁时,所有的事件处理器都会自动被删除。

3、React

Class组件:传入箭头函数,在箭头函数内调用回调函数。箭头函数继承(词法作用域)作用域链上一层的this,起到绑定this的效果。回调函数被调用的形式,而不是被当做参数传入子组件,方便传递额外的参数。

onClick={(e)=>{this.handleName(param,e)}}    

函数式组件,无this

onClick={(e)=>{handleName(param,e)}}  

七、标签

1、小程序

不支持常用的HTML标签div等,<view></view>小程序提供的容器标签

图片标签 <image /> <van-image></van-image>

<!-- 注释 -->

2、Vue2、Vue3

支持HTML标签,ElementUI组件标签,Vant组件标签

图片标签 <img /> <el-image></el-image> <van-image></van-image>

<!-- 注释 -->

3、React

支持HTML标签,styled-component样式标签

图片标签 <img /> <Image />

{/* 注释 */}

八、生命周期函数

1、小程序

页面生命周期onLoadonShowonPullDownRefreshonUnloadonShareAppMessageonPageScrollonResizeonReachBottomonReadyonHide

组件生命周期lifetimes:{attached, moved, detached}pageLifetimes:{show, hide, resize}

2、Vue2、Vue3

Vue2

  • beforeCreate, created。 created函数:可以进行ajax请求,可使用thisdata赋值,不能操作 DOM。
  • beforeMount, mounted。 mounted阶段:vue实例挂载完成,DOM 已渲染,可通过this.$refs操作 DOM。
  • activated, deactivated
  • beforeUpdate, updated。 beforeUpdate:修改数据不会造成重新渲染,updated:不可更新数据,可能造成死循环。
  • beforeDestory, destoryed。 beforeDestory:可清除计时器等, destroyed:实例销毁,数据绑定卸除,子实例销毁。

Vue3

onMountedonUpdatedonUnmountedonBeforeMountonBeforeUpdateonBeforeUnmountonActivatedonDeactivated

3、React

相关文档

Class组件:componentDidMountcomponentDidUpdatecomponentWillUnmount

函数式组件Hooks: useEffect 模仿生命周期 componentDidMountcomponentDidUpdate

九、提示消息

1、小程序

API文档
VantWeapp相关组件

wx.showLoading({title: '',})    
wx.hideLoading();
wx.showToast({
  title: '成功',
  icon: 'success',
  duration: 2000
})
wx.showModal({
  title: '提示',
  content: '这是一个模态弹窗',
  success (res) {
    if (res.confirm) {
      console.log('用户点击确定')
    } else if (res.cancel) {
      console.log('用户点击取消')
    }
  }
})

2、Vue2、Vue3

Vant
ElementUI
element-plus

3、React

antd

十、获取路由参数、路由跳转

1、小程序

let routes =  getCurrentPages()
let currentRoute = routes[routes.length-1].route

路由跳转:

路由API
路由方式及触发的生命周期

wx.navigateTo({url:''})
wx.switchTab({url:''}) //路径后不能带参数
wx.redirectTo()
wx.relaunch()
wx.navigateBack()

2、Vue2、Vue3

Vue2相关文档

Vue3相关文档

// Vue2
let currentRoute =  this.$route
// Vue3
import { useRoute } from 'vue-router'
const currentRoute = useRoute()

路由跳转:

// Vue2
this.$router.push({path:''})
this.$router.replace()
this.$router.go()
this.$router.back()
// Vue3
import { useRouter } from 'vue-router'
const router = useRouter()
router.push()

3、React

相关文档

import { useLocation } from 'react-router'
const location = useLocation()  

路由跳转:

import { useHistory } from 'react-router-dom'
const history = useHistory() 
history.push()

十一、组件通信

1、小程序

  • 通过拼接URL参数传值,在被打开的页面通过onLoad(query)参数获取
  • 通过设置全局app.jsglobalData,在其他页面通过 const app = getApp(); app.globalData.key 获取,随着小程序卸载而重置(杀掉小程序进程,小程序冷启动)
  • 本地缓存,持久化存储 wx.setStorage({}) wx.getStroage({key:'', success:function(res{res.data}}) ,存储登录信息等,杀掉小程序进程再次打开时,仍能保留登录数据。
  • 通过wx.navigateTo() 返回的EventChannelweb-view载入的H5navigateTo跳转小程序页面,不支持EventChannel
wx.navigateTo({
  url: 'test?id=1',
  events: {
    someEvent: function(data) {   // 监听 被打开页面传送到当前页面的数据 
      console.log(data)
    } 
  },
  success: function(res) {
    res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' })  // 通过eventChannel向 被打开页面传送数据
  }
})
//test.js
Page({
  onLoad: function(option){ 
    const eventChannel = this.getOpenerEventChannel() 
    eventChannel.emit('someEvent', {data: 'test'}); 
    eventChannel.on('acceptDataFromOpenerPage', function(data) {})
  }
})
  • 通过发布订阅模式封装eventbus,适用于先注册监听函数再触发事件。
// utils/eventBus.js
const EventPool = {};
const eventBus = {
  $on: function (eventName, cb) {
    let cbList = EventPool[eventName] || [];
    cbList.push(cb);
    EventPool[eventName] = cbList; 
  },
  // 先监听,再触发
  $emit: function (eventName, param) { 
    if (EventPool[eventName]) {
      EventPool[eventName].map((cb) => {
        if (typeof cb== "function") {
          cb(param);
        }
      });
    }
  },
  // 删除事件的某个回调监听,或者删除事件所有的监听
  $off: function (eventName, cb) {
    if (cb && typeof cb === "function") {
      let index = EventPool[eventName].findIndex((item) => item === cb);
      if (index!=-1) {
        EventPool[eventName].splice(index, 1);
        if (!EventPool[eventName].length) {
          delete EventPool[eventName];
        }
      }
    } else {
      delete EventPool[eventName];
    }
  },
};

module.exports = {
  eventBus,
};
//app.js
const eventBus = require("utils/event-bus.js");
APP({eventBus})
// demo1.js
const app = getApp();
onLoad(){
    app.eventBus.$on("eventName", this.foo);
},
onUnload(){
    app.eventBus.$off("eventName", this.foo);    
}
methods:{
    foo(){
        // do something
    }
}
// demo2.js
app.eventBus.post("eventName",payload);

2、Vue2、Vue3

props/emitVuexpiniaeventBusprovide/inject$attrs/$listeners 网上有很多写的很好的详细的文章,大家可以搜来看。 在组件外使用pinia

3、React

propsredux

十二、模块化

1、小程序

相关文档

使用module.exportsrequire

小程序目前不支持直接引入 node_modules , 开发者需要使用到 node_modules 时候建议拷贝出相关的代码到小程序的目录中,或者使用小程序支持的 npm 功能。

module.exports = { 
  eventBus
}
const eventBus = require('utils/eventBus.js')

2、Vue2、Vue3

ESModule

使用ESModuleexportimport

3、React

使用ESModuleexportimport

十三、请求代理

1、小程序

相关文档

不需要配置请求代理,需要在小程序公众平台(开发管理-开发配置)中配置服务器域名。

不支持promise风格调用,可自行封装。

//env.js  配置环境变量,自测、生产发版时手动切换环境。
const env = {
  test: {
    baseUrl: "https://xxx", 
  },
  pro: { 
    baseUrl: "https://xxx", 
  },
}; 
module.exports = { 
  ...env.test,
  // ...env.pro
};
//request.js  
const env = require("../env.js");
options.url = env.baseUrl + options.url;
let header = {}; 
header['token'] = xxx
return new Promise((resolve, reject) => {
  wx.request(Object.assign({}, options, {
      header,
      success(res){ resolve(res.data) },
      fail(err){ reject(error) },
  }))
})

2、Vue2、Vue3

如果后端接口发布在多个域名,可配置多个代理。

// vue.config.js
proxy: { 
  '/apis': {
    target: process.env.VUE_APP_BASEURL,
    ws: true,
    changOrigin: true,
    pathRewrite: {
      '^/apis': ''
    }
  },
}

十四、插槽

1、小程序

相关文档

默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。

2、Vue2、Vue3

相关文档

v-slot:name='slotProps' 指令,作用域插槽

<parent-element>
    <template v-slot:header='slotProps'>
        something replace slot header {{slotProps.xx}}
    </template>
    <template #footer>
        something replace slot footer
    </template>
    something replace slot default
</parent-element>
<child-element>
    <slot name='header' :xx='data'>something backup header</slot>
    <slot name='footer'>something backup footer</slot>
    <slot>something backup default</slot>
</child-element> 

3、React

没有插槽概念,{} 可传递React元素,子组件通过props获取。

const leftJsx = <span>aaa</span>;
<NavBar
  leftSlot={leftJsx}
  centerSlot={<strong>bbb</strong>}
  rightSlot={<a href="/#">ccc</a>}
/>
// 子组件
const { leftSlot, centerSlot, rightSlot } = props
<div>{leftSlot}</div> 

以上就是目前全部的内容了,React我目前还是刚上手阶段,只写了我会的,不是很完整。其他的如有错误或不当,欢迎指正、讨论。

终于写完了😀。