前言
鉴于项目需要兼容IE11,项目组内讨论想使用vue3和typescript。vue2对TypeScript的支持不是很好,所以折中使用@vue/composition-api+vue2+TypeScript的方式进行开发。这样不仅能使用compostion api而且对TypeScript有更好的支持。 这样不仅可以学习vue3的compostion api的语法,对于以后新项目中直接使用vue3做准备,而且能兼容IE11。
下面来介绍vue2 和 vue3(或者说是compostion api) 我们需要知道:(tips: 如果直接使用vue3 示例代码中import { defineComponent, ref, reactive } from '@vue/composition-api’
改成 import { defineComponent, ref, reactive } from 'vue'
即可)
TypeScript支持
Vue3以及Vue conf上所说的在Q3发布的Vue2.7都是使用的TypeScript来进行类型检查,所有composition-api也是支持TypeScript的
import { defineComponent } from '@vue/composition-api'
// 只需要把代码写在defineComponent里面,ts的静态检查就会工作
export default defineComponent({
// type inference enabled
setup(props,ctx){
...
....
}
})
浏览器支持
与Vue3一样,composition-api支持所有现代浏览器以及IE11+,如果需要支持低版本的IE你应该安装babel-polyfill
- npm install babel-polyfill or yarn add babel-polyfill
- 入口文件中加入import "babel-polyfill"
- webpack 中配置 entry:['babel-polyfill','./main.js']//假设这里的main.js是代码入口主文件
- 配置babel-loader
module: {
rules: [
...
{
test: /.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
}
...
]
}
在Vue2项目中使用@vue/composition-api
npm install @vue/composition-api
# or
yarn add @vue/composition-api
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
然后在.vue组件里面引入,就可以使用部分vue3的功能
import { ref, reactive } from '@vue/composition-api'
reactive、ref 与 toRefs
在 vue2.x 中, 定义数据都是在data中, 但是 Vue3.x 可以使用reactive和ref来进行数据定义。 那么ref和reactive他们有什么区别呢?分别什么时候使用呢?说到这里,我又不得不提一下,看到很多网上不少文章说 (reactive用于处理对象的双向绑定,ref则处理 js 基础类型的双向绑定)。我其实不太赞同这样的说法,这样很容易初学者认为ref就能处理 js 基本类型, 比如ref也是可以定义对象的双向绑定的啊, 上段代码:
setup() {
const obj = ref({count:1, name:"张三"})
setTimeout(() =>{
obj.value.count = obj.value.count + 1
obj.value.name = "李四"
}, 1000)
return{
obj
}
}
我们将obj.count和obj.name绑定到页面上也是可以的;但是reactive函数确实可以代理一个对象, 但是不能代理基本类型,例如字符串、数字、boolean 等。 接下来使用代码展示一下ref、reactive的使用:
<template>
<div class="home">
{{ testRef }} {{userReactive.name}} {{userReactive.age}}
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
export default defineComponent({
name: 'Home',
components: {
HelloWorld
},
setup (props) {
console.log(props)
const testRef = ref(0)
const userReactive = reactive({
name: 'lucy',
age: 17
})
return {
testRef,
userReactive
}
}
})
</script>
我们绑定到页面是通过userReactive.name,userReactive.age;这样写感觉很繁琐,我们能不能直接将userReactive中的属性解构出来使用呢? 答案是不能直接对userReactive进行结构, 这样会消除它的响应式。那我们就想使用解构后的数据怎么办,解决办法就是使用toRefs。 toRefs 用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象。具体使用方式如下
<template>
<div class="home">
<p>{{ testRef }} </p>
<p> {{userReactive.name}}</p>
<p> {{userReactive.age}}</p>
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
export default defineComponent({
name: 'Home',
components: {
HelloWorld
},
setup (props) {
console.log(props)
const testRef = ref(0)
const userReactive = reactive({
name: 'lucy',
age: 17
})
return {
testRef,
...toRefs(userReactive)
}
}
})
</script>
通常建议对某一种类型的操作放在同一个reactive中,如:
<template>
<div class="home">
<button @click="changeUserName('lilei')"></button>
<button @click="changeUserAge(33)"></button>
<button @click="changePorjectId(2)"></button>
<button @click="changePorjectName('porjectName2')"></button>
<p>{{ testRef }} </p>
<p> {{userInfo.name}}</p>
<p> {{userInfo.age}}</p>
<p> {{porjectInfo.id}}</p>
<p> {{porjectInfo.name}}</p>
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
export default defineComponent({
name: 'Home',
components: {
HelloWorld
},
setup (props) {
console.log(props)
const testRef = ref(0)
const userReactive = reactive({
userInfo: {
name: 'lucy',
age: 17
},
changeUserName (name: string) {
userReactive.userInfo.name = name
},
changeUserAge (age: number) {
userReactive.userInfo.age = age
}
})
const porjectReactive = reactive({
porjectInfo: {
id: 1,
name: ' porjectName'
},
changePorjectId (id: number) {
porjectReactive.porjectInfo.id = id
},
changePorjectName (name: string) {
porjectReactive.porjectInfo.name = name
}
})
return {
testRef,
...toRefs(userReactive),
...toRefs(porjectReactive)
}
}
})
</script>
为什么要这么写呢? 这样能方便我们提取逻辑,如下:
<template>
<div class="home">
<button @click="changeUserName('lilei')"></button>
<button @click="changeUserAge(33)"></button>
<button @click="changePorjectId(2)"></button>
<button @click="changePorjectName('porjectName2')"></button>
<p>{{ testRef }} </p>
<p> {{userInfo.name}}</p>
<p> {{userInfo.age}}</p>
<p> {{porjectInfo.id}}</p>
<p> {{porjectInfo.name}}</p>
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
function userOperation () {
const userReactive = reactive({
userInfo: {
name: 'lucy',
age: 17
},
changeUserName (name: string) {
userReactive.userInfo.name = name
},
changeUserAge (age: number) {
userReactive.userInfo.age = age
}
})
return userReactive
}
function projectOperation () {
const porjectReactive = reactive({
porjectInfo: {
id: 1,
name: ' porjectName'
},
changePorjectId (id: number) {
porjectReactive.porjectInfo.id = id
},
changePorjectName (name: string) {
porjectReactive.porjectInfo.name = name
}
})
return porjectReactive
}
export default defineComponent({
name: 'Home',
components: {
HelloWorld
},
setup (props) {
console.log(props)
const testRef = ref(0)
const userReactive = userOperation()
const projectReactive = projectOperation()
return {
testRef,
...toRefs(userReactive),
...toRefs(projectReactive)
}
}
})
</script>
是不是setup函数中很干净呢?
我们甚至可以把userOperation
和projectOperation
单独定义一个文件来处理逻辑:
userOperation.ts中定义
import { reactive } from '@vue/composition-api'
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export default function userOperation () {
const userReactive = reactive({
userInfo: {
name: 'lucy',
age: 17
},
changeUserName (name: string) {
userReactive.userInfo.name = name
},
changeUserAge (age: number) {
userReactive.userInfo.age = age
}
})
return userReactive
}
文件中引用
<template>
<div class="home">
<button @click="changeUserName('lilei')"></button>
<button @click="changeUserAge(33)"></button>
<button @click="changePorjectId(2)"></button>
<button @click="changePorjectName('porjectName2')"></button>
<p>{{ testRef }} </p>
<p> {{userInfo.name}}</p>
<p> {{userInfo.age}}</p>
<p> {{porjectInfo.id}}</p>
<p> {{porjectInfo.name}}</p>
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
import userOperation from './userOperation'
// function userOperation () {
// const userReactive = reactive({
// userInfo: {
// name: 'lucy',
// age: 17
// },
// changeUserName (name: string) {
// userReactive.userInfo.name = name
// },
// changeUserAge (age: number) {
// userReactive.userInfo.age = age
// }
// })
// return userReactive
// }
function projectOperation () {
const porjectReactive = reactive({
porjectInfo: {
id: 1,
name: ' porjectName'
},
changePorjectId (id: number) {
porjectReactive.porjectInfo.id = id
},
changePorjectName (name: string) {
porjectReactive.porjectInfo.name = name
}
})
return porjectReactive
}
export default defineComponent({
name: 'Home',
components: {
HelloWorld
},
setup (props) {
console.log(props)
const testRef = ref(0)
const userReactive = userOperation()
const projectReactive = projectOperation()
return {
testRef,
...toRefs(userReactive),
...toRefs(projectReactive)
}
}
})
</script>
这可能就是函数式编程