vue3.0 都已经进入beta版本了,预计今年就会面向广大的vuer,嘴里说着学不动学不动了,可吃饭的家伙,再难也得啃过去呀,干啃两天vue3征求意见稿,马马虎虎写出了这个tidolist
先看看最后完成的效果吧,样式有点丑,将就着看下吧
启动项目
这里企鹅遇到个问题,按照官方说的,先是使用vue-cli4.x以上的脚手架创建个vue2项目,然后使用vue add vue-next
将其转换成vue3的项目,但是试了几次总是报错,所以采用了另一套方案,就是使用vite进行创建。更多vite点击这里进行查看。
npm使用
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev
yarn使用
yarn create vite-app <project-name>
cd <project-name>
yarn
yarn dev
开始todolist
js之前一直有万物皆对象的说法,而vue3则是有了万物皆setup
的说法。组合式api,可不是闹着玩的。看官方的demo
定义的组合api(默认都是以use开头,是不是觉得和react的hook很像)
export default {
setup() {
// ...
},
}
function useCurrentFolderData(networkState) {
// ...
}
function useFolderNavigation({ networkState, currentFolderData }) {
// ...
}
function useFavoriteFolder(currentFolderData) {
// ...
}
function useHiddenFolders() {
// ...
}
function useCreateFolder(openFolder) {
// ...
}
setup
函数内部则进行逻辑处理
export default {
setup() {
// 网络状态
const { networkState } = useNetworkState()
// 文件夹状态
const { folders, currentFolderData } = useCurrentFolderData(networkState)
const folderNavigation = useFolderNavigation({
networkState,
currentFolderData,
})
const { favoriteFolders, toggleFavorite } = useFavoriteFolders(
currentFolderData
)
const { showHiddenFolders } = useHiddenFolders()
const createFolder = useCreateFolder(folderNavigation.openFolder)
// 当前工作目录
resetCwdOnLeave()
const { updateOnCwdChanged } = useCwdUtils()
// 实用工具
const { slicePath } = usePathUtils()
return {
networkState,
folders,
currentFolderData,
folderNavigation,
favoriteFolders,
toggleFavorite,
showHiddenFolders,
createFolder,
updateOnCwdChanged,
slicePath,
}
},
}
好了开始我们的todolist。
入口文件这里我们不需要动,保持默认即可
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
createApp(App).mount('#app')
vue3把vue的核心模块进行了抽离,按需使用,入口文件这里我们只需要使用createApp
就可以了。当然如果是安装组件啥的,可以这么写。
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import vuex from './vuex'
import './index.css'
createApp(App)
.use(router)
.use(vuex)
.mount('#app')
createApp(App)
会返回一个类似于之前的vue实例
App.vue
<template>
<input placeholder="按enter进行添加" v-model="text" @keyup.enter="addTodoList(text), text = ''" />
<ul>
<li class="item" v-for="(item, index) in todoList" :key="index">
<span :class="[item.status ? 'done' : 'active']">{{ item.value }}</span>
<button v-if="item.status" @click="activeTodoList(index)">激活</button>
<button v-else @click="doneTodoList(index)">完成</button>
<button @click="delTodoList(item.id)">删除</button>
</li>
</ul>
</template>
<script>
import { ref, unref, reactive, watch, watchEffect } from 'vue'
import { useTodoList } from './todoList'
export default {
name: 'App',
setup() {
const text = ref('')
const list = [
{
id: 1,
value: '吃饭',
status: false
},
{
id: 2,
value: '睡觉',
status: false
},
{
id: 3,
value: '学vue3',
status: false
}
]
const {
todoList,
addTodoList,
delTodoList,
activeTodoList,
doneTodoList
} = useTodoList(list)
return {
todoList,
addTodoList,
delTodoList,
activeTodoList,
doneTodoList
}
}
}
</script>
<style scoped>
* {
margin: 0;
padding: 0;
}
.item {
margin: 15px 0;
}
.active {
color: #1890ff;
}
.done {
color: #d9d9d9;
text-decoration: line-through;
}
</style>
todolist
import { ref, unref, reactive, watchEffect, watch, toRefs } from 'vue'
export function useTodoList(list) {
let state = reactive({
todoList: [
...list
]
})
const methods = {
addTodoList(text) {
if (!text) return alert('不能为空')
const boo = state.todoList.find(item => item.value === text)
if (boo) return alert('重复选项')
state.todoList.push({
id: state.todoList.length + 1,
value: text,
status: false
});
},
delTodoList(id) {
// todoList.splice(index, 1)
state.todoList = state.todoList.filter(item => item.id !== id)
},
doneTodoList(index) {
state.todoList[index].status = true;
},
activeTodoList(index) {
state.todoList[index].status = false;
}
}
watch(() => state.todoList, newVal => console.log(newVal))
watchEffect(() => console.log(state.todoList))
return {
...toRefs(state),
...methods
}
}
这里会将用到的新api都简单的说明一下
setup
composition api
的入口,执行时机在beforeCreate
钩子函数之前触发,且类似于onMounted、onUnmounted
等钩子函数只能在setup
函数内触发,如果组件内同时存在比如mouted
和onMounted
钩子函数时,onMounted
之类的函数会先一步执行。完整生命周期执行顺序如下
setup、beforeCreate、props、methods、data、computed、watch、created、onBeforeMount、beforeMount、onMounted、mounted、onBeforeUpdate、beforeUpdate、onUpdated、updated、onBeforeUnmount、beforeDestroy、onUnmounted、destroyed、onErrorCaptured、errorCaptrued
setup
函数有两个参数props
父组件的传值,切记不要解构props
会导致失去响应性,如果非想使用解构,我们可以这么做,这样处理过后就可以使用解构了setup(props) { const { msg } = toRefs(props) }
ctx
上下文对象,包含attrs、emit、slots
,因为这三项都是代理,也就是使用的引用,可以放心的使用解构setup(props, { emit }) { emit('changeMsg', 111); }
- 不要使用
this
这里的this
指向不是你想的那样
ref
、unref
接受一个参数值(基本是基本类型数据)并返回一个响应式的且可改变的ref
对象,ref
对象内部拥有唯一属性value
const count = ref(0); console.log(count.value) // 0
- 如果传入复杂类型,则
ref
则会调用reactive
方法进行深层响应转换。
如果在模版template
内访问ref
,则ref
会自动解套<template> {{ count }} // count.value 自动解套,访问.value </temlate>
computed
会自动返回一个默认不可手动修改的ref
对象
watch
可以直接监听ref
对象,会自动解套,watchEffect
则必须使用.value
const count = ref(0) watch(count, newVal => {}) watchEffect(() => count.value)
- 当
ref
对象作为reactive
对象的属性被访问或者修改时,也将自动解套const count = ref(0); const state = reactive({ count }) console.log(state.count) // 0 ++state.count // 1
- 如果传入复杂类型,则
unref
对象则是对ref
对象的解套,如果这个对象是ref
对象创建出来的则使用.value
进行解套,否则作为普通属性返回isRef(count) ? count.value : count
reactive
接受一个普通对象返回返回响应式代理(基于proxy
),等价于vue2
的Vue.obserable()
。始终建议使用这种形式创建响应式对象
若采用以下方式则会出现问题,会导致数据无法响应(已被修复,可以使用)const state = reactive({ count: 0, list: [1, 2, 3] });
const list = reactive([1, 2, 3]);
watch
完全等效于vue2.x
的watch
,一样需要监听具体变动的数据源
监听单个数据源时,watch
监听reactive
时,必须使用() => {}
,也就是拥有返回值的getter
函数,若如果是ref
对象时,则不需要
如果const state = reactive({ count: 0 }) const num = ref(0) watch(() => state.count, (newVal, preVal) => { ... }) watch(num, newVal => { ... })
ref
来创建响应式对象
监听多个数据源const state = ref({ count: 0 }); watch(state.value.count, newVal => { ... }) watch(() => state.value.count, newVal => { ... }) // 监听不到
watch([foo, bar], ([fooNew, barNew], [fooPre, barPre]) => {})
watchEffect
立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数- 会立即执行一次
- 可以主动停止,取消监听
const stop = watchEffect(() => { /* ... */ }) // 之后 stop()
- 监听不到数组
const listArr = reactive([1]); watchEffect(() => console.log(listArr)) // 监听不到变化,但可监听到.length变化
小企鹅写demo时,vue3还处于3.0-rc版本,等到了3.0正式版发布时,又重新测试了下,修改了部分内容,如果有争议欢迎提出,一起进步