尝鲜vue3.0 从todoList开始

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函数内触发,如果组件内同时存在比如moutedonMounted钩子函数时,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指向不是你想的那样
  • refunref 接受一个参数值(基本是基本类型数据)并返回一个响应式的且可改变的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),等价于vue2Vue.obserable()。始终建议使用这种形式创建响应式对象
    const state = reactive({
    	count: 0,
    	list: [1, 2, 3]
    });
    
    若采用以下方式则会出现问题,会导致数据无法响应(已被修复,可以使用)
    const list = reactive([1, 2, 3]);
    
  • watch 完全等效于vue2.xwatch,一样需要监听具体变动的数据源
    监听单个数据源时,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正式版发布时,又重新测试了下,修改了部分内容,如果有争议欢迎提出,一起进步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值