Vue3.0学习笔记 (一)

182 阅读4分钟

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」。

关于3.0版本配置和cil升级就不过多赘述了,下面只介绍常用的api及使用方式


defineComponent

defineComponent函数,只是对setup函数进行封装,返回options的对象,defineComponent最重要的是:在TypeScript下,给予了组件 正确的参数类型推断 。

import { defineComponent, ref } from 'vue'

const HelloWorld = defineComponent(function HelloWorld() {
  const count = ref(0)
  return { count }
})

setup

setup函数为vue3.0的composition API提供了统一的入口

通过steup函数的第一个形参,接收props数据

第二个形参是一个对象,这个对象在2.0中需要通过this才能访问,在vue3中用content. 访问

<script>
export default defineComponent({
  setup (props, content) {
  	props: {
      msg: '子组件传递'
    }
    
    content.emit
    content.refs
  }
})
</script>

对比2.x省去了options声明的data(){ }

<div>{{data}}</div>
<script>
// 1. 从 vue 中引入 ref 函数
import { ref } from 'vue'
export default ({
  setup(){
    // 2.定义一个单值,响应式变量
    const data = ref('这是一个标题')
   	// 3.访问变量需要.value
    console.log(data.value)
    // 4.返回出去
    return {
      data
    }
  }
})
</script>

reactive

reactive函数用来返回一一个响应式对象,示例如下:

<template>
  <div id="app">
  	<!-- 4. 访问响应式数据对象中的 count  -->
  	{{ count }}
  </div>
</template>

<script>
// 1. 从 vue 中导入 reactive 
import {reactive} from 'vue'
export default {
  name: 'App',
  setup() {
      // 2. 创建响应式的数据对象
      const data = reactive({count: 3})

      // 3. return 出去,供template使用
      return { data }
  }
}
</script>

ref

关于和reactive的区别,ref定义的数据,打印的结果是一个被对象包裹的响应式数据,但是如果reactive去定义一个单值,浏览器会报黄错,提示值不能被reactive创建

所以,简易在初始定义数据时,reactive定义复杂的数据类型,ref定义基本数据类型, 如果非要使用reactive,需要把值包裹一下,如:const data = reactive({ val: 10 }), ref定义的数据访问的时候需要加 .value,reactive不需要,使用ref定义基本数据类型,也可以定义数据和对象

示例如下:

<template>
  <div id="app">
  	{{ data.count }}
  </div>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  name: 'App',
  setup() {
      const data = ref(0)
      const data = { count: 3 }
      return reactive({
        data
      })
  }
}
</script>

toRef

toRef是将某个对象中的某个值转化为响应式对象,并接收两个参数,第一个是对象,第二个是对象的属性名

<script>
// 1. 导入 toRef
import {toRef} from 'vue'
export default {
    setup() {
        const obj = {count: 3}
        // 2. 将 obj 对象中属性count的值转化为响应式数据
        const state = toRef(obj, 'count')

        // 3. 将toRef包装过的数据对象返回供template使用
        return {state}
    }
}
</script>

toRefs

<template>
    <div>
      {{ name }}
    </div>
</template>
<script>
// 1. 导入 toRefs
import { toRefs } from 'vue'
export default {
    setup() {
        const obj = {
            name: '测试',
            age: 22,
            gender: 0
        }
        
        // 2. 将 obj 对象中属性count的值转化为响应式数据并返回
				return {
        	...toRefs(obj)
        }
    }
}
</script>

ref和toRef的区别:

1ref本质是拷贝,修改响应式数据不会影响原始数据;toRef的本质是引用关系,修改响应式数据会影响原始数据
2ref数据发生改变,界面会自动更新;toRef当数据发生改变是,界面不会自动更新
3toRef传参与ref不同;toRef接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性

watch

示例如下:

监听ref()

<script>
// 1. 导入watch
import { watch } from 'vue'
export default {
    setup() {
        const data = ref(0)
        
        watch(data, (newval, oldval) => ) {
              console.log(newval)
              console.log(oldval)
        }
  
  			//  1秒后data + 1
  			setTimeout(() => {
        	data.value ++
        }, 1000)
				return {
        	data
        }
    }
}
</script>

监听reactive()

<script>
// 1. 导入watch
import { watch } from 'vue'
export default {
    setup() {
        const data = reactive({
            count: 0
        })
        
        watch(() => data.count, (newval, oldval) => {
            console.log(newval)
            console.log(oldval)
        })
  
  	//  1秒后data + 1
  	setTimeout(() => {
        	data.count ++
        }, 1000)
	return {
        	data
        }
    }
}
</script>

监听多个值

<script>
// 1. 导入watch
import { watch } from 'vue'
export default {
    setup() {
        const data = reactive({
        	count: 0,
          name: 'cs'
        })
        
        watch(
        	[() => data.count, () => data.name],
          ([newcount, newname], [oldcount, oldname]) => {
          	console.log(newcount) // 新的count值
          	console.log(newname) // 新的name值
          	console.log(oldcount) // 旧的name值
          	console.log(oldname) // 旧的name值
          }
        )
        
  			//  1秒后data + 1
  			setTimeout(() => {
        	data.count ++
        	data.name = 'xx'
        }, 1000)
      
				return {
        	data
        }
    }
}
</script>

computed

<script>
import { ref, computed } form 'vue'export default {
   setup () {
     // computed 计算属性使用
     const age = ref(18)   // age 现在就是响应式对象
     
     // computed 计算属性是一个函数   传入一个函数getter
     const nextAge = computed(()=> {
        return parseInt(age.value) + 1
     }) 
    	
      // 返回的是一个不可以修改的计算属性
     return {
        age,
        nextAge
     }
   }
}
</script>

Vue2.x与Vue3.x的生命周期

Vue2Vue3
beforeCreatesetup
createdsetup
beforeMountonBeforeMount在挂载前执行某些代码
mountedonMounted在挂载后执行某些代码
beforeUpdateonBeforeUpdate在更新前前执行某些代码
updatedonUpdated在更新后执行某些代码
beforeDestoryonBeforeUnmount在组件销毁前执行某些代码
destoryedonUnmounted在组件销毁后执行某些代码

在setup中调用

<script>
// 1. 从 vue 中引入 多个生命周期函数
import {onBeforeMount, 
        onMounted, 
        onBeforeUpdate, 
        onUpdated, 
        onBeforeUnmount,
        unMounted} from 'vue'
</script>

Vue3.x使用工具函数

因为弃用了之前过滤器的写法,所有的函数方法都必须return出去

如转换时间:

import { timeFormat } from '@/utils/index'
return {
	timeFormat
}
<div class="time">{{timeFormat(item.time)}}</div>

vue2.x的话就是在methods里面声明一下也可以

methods: {
	timeFormat
}

Vue3.x中的vuex

main.js中的导入

import store from './store/index'

const app = createApp(store)
//  将store实例作为插件安装
app.use(store)

state

因为vuex使用的是单一状态树,用一个对象就包含了所有层级状态

获取state状态:

方法一:通过ref返回一个响应式数据
方法二:通过计算属性返回     computed(() => store.state.count).value)
方法三:mapstate函数展开获取

getter

相当于computed 计算属性

index.js

import { createStore } from 'vuex'

const store = createStore({
	state: {
  	return {
  		userLsit: [
    		{ id: 1, name: '张' },
    		{ id: 2, name: '李' }
    	]
  	}
  },
  getter: {
    // 获取id的函数
  	getId: (state) => (id) => {
  		let user = state.userList.find(e => e.id === id)
      if (user) {
      	return user.name
      }
  	},
    
    // 获取列表
    getName: (state) => {
    	return state.userList
    }
  }
})

index.vue

<template>
    <div>
        <div @click="getUser">获取用户</div>
        <div>id为1的用户是: {{ userName }}</div>
    </div>
</template>

<script>
import { useStore } from "vuex"
import { ref } from "vue"
export default {
    setup () {
        // 获取store实例
        let store = useStore()
      
        // 定义值
        let userName = ref("")

        // 返回state内的值
        let getUser = () => {
            userName.value = store.getters.getId(1)
            console.log(userName.value)  // { id: 1, name: '张' }
            console.log("用户列表:", store.getters.getName)
        },
        return {
            getUser,
            userName
        }
    }
}
</script>

mutation(同步函数)

mutation相当于定义一个事件,store.commit('事件名',value)外面触发事件

index.js

import { createStore } from 'vuex'

//创建一个新的store实例
const store = createStore({
    //存储数据
    state() {
        return {
            count: 0
        }
    },
    //修改数据的方法
    mutations: {
        add(state, value: number) {
            state.count += value
        }
    }
})

export default store;

在组件中使用

<template>
<div>
  <span>{{ data }}</span>
  <button @click="change">修改值</button>
</div>
</template>

<script>
import { ref } from 'vue'
import { useStore } from "vuex"
export default {
  //获取store实例
  const store = useStore();
  
  // 默认值
	const data = ref(0)
  
  // 修改值
  const change = () => {
  	store.commit('add', 1)
  }
  
  // 获取值
  data.value = store.state.count
  return {
  	data,
    change
  }
}
 
</script>

Action

Action类似于mutation,不同在于:

action提交的是mutation,而不是直接变更状态

action可以包含任意异步操作

const store = createStore({
  state: {
    count: 0
  },
  
  mutations: {
    increment (state) {
      state.count++
    }
  },
  
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = createStore({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

Vue-router4

router下index.js

import { createRouter, createWebHashHistory } from 'vue-router'

// 工厂函数创建router实例
const router = createRouter({
	history: createWebHashHistory(),
	routes: [
    { path: '/home' component: () => import('views/index.vue') }
  ]
})

export default router

组件中使用和传参等操作

<template>
    <div>
        <router-link to="/home">Home</router-link>
        <div @clikc="go">跳转路由传参</div>
    </div>
    <router-view></router-view>
</template>

<script>
// this.$router 或 this.$route没了,作为替代,我们使用 useRouter 函数:
// 如果有参数的话,还要导入useRoute Api
import { useRouter, useRoute } from "vue-router"
import { reactive, watch } from "vue"
 
export default {
    setup() {
      	const state = reactive({
        	id: route.query.id,  // query
          id: route.params.id  // params
        })
      
      	// 定义router变量
        const router = useRouter()
        const route = useRoute()
      	
      	// 跳转方法
        const go = () => {
          // router.push跳转页面
        	
          // 命名的路由 params传参  变成 /index/123
          route.push({ name: 'user', params: { id: '123' }})
					
          // 带查询参数,query传参  变成 /index?userId=123
          router.push({ path: 'index', query: { id: '123' }})
        }
        
        
        // 当前页面监听router变化, 监听params参数的变化
        watch(() => route.params, () => {
        	// 执行操作
        })
      
        return {}
    }
}
</script>

router自带的tab切换class

<template>
	<div>
  	<div class="nav">
       <router-view></router-view>
       <router-link class="nav-item" to="/add">nav1</router-link>
       <router-link class="nav-item" to="/done">nav2</router-link>
       <router-link class="nav-item" to="/delete">nav3</router-link>
    </div>
  </div>
</template>

<style>
.active {
  font-weight: bolder;
  color: #000;
}
</style>

router.js

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Index',
    component: () => import('../views/Index.vue'),
    children: [
      {
        path: '',
        redirect: { name: 'add' }
      },
      {
        path: '/add',
        name: 'add',
        component: () => import('../components/Add.vue')
      },
      {
        path: '/done',
        name: 'done',
        component: () => import('../components/Done.vue')
      },
      {
        path: '/delete',
        name: 'delete',
        component: () => import('../components/Delete.vue')
      },
    ]
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
  linkActiveClass: 'active'  ////设置 链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置
})

export default router