一、vue3.0实例,仅供参考
//1. vue-cli3创建项目:vue create my-project
//2.npm安装compisition-api: npm install @vue/compisition-api --save
//3. main.js import VueCompisitionAPI from '@Vue-Compisition-api' Vue.use(VueCompisitionAPI)
// 组件
<template>
<div>
<p>count: {{count}}</p>
<p>refCount: {{refCount}}</p>
<button @click="add"></button>
</div>
</template>
<script>
import { reactive, ref, isRef, toRefs, computed, watch, onBeforeMount, onMounted } from '@vue/compisition-api'
export default {
beforeCreate() {
},
/**
* props: 外部传入的props参数,需要在下边的props定义,否则拿不到对应的值
* context:相当于vue2.0中的this, 在vue3.0中拿不到this
*/
setup(props, context) {
// beforeCreate之后,created()之前执行
console.log(props, context) // { p1: 'aaa' }
// reactive 接受一个对象,返回一个响应式的数据对象, x相当于vue2.0中的observable
const state = reactive({ count : 0})
count++
return state // 页面就可以访问了
// ref:用ref做值类型响应式, ref是一个对象, .value获取值;新的ref会覆盖旧的ref
const refCount = ref(0)
console.log(refCount.value) // 0
refCount.value++
return{
refCount,
name: ref('zx')
}
// isRef: 用来判断某个值是否为 ref() 创建的
const unwrapped = isRef(foo) ? foo.value : foo
// toRefs: 将reactive()创建出来的响应式对象转换为普通对象,每一个属性节点都是一个ref
const state = reactive({count: 0, name: 'zx'})
// 定义自增+1的函数
const add = () => {
state.count += 1
}
return {
// ...state, // 每个展开的数据不再是响应式,而是固定的值了
...toRefs(state),
add
}
// computed() 传入一个function,可以得到一个只读的计算属性,返回值是一个 ref的实例
const count = ref(0)
const plusOne = computed(()=> count.value+1)
console.log(plusOne.value) // 1
plusOne.value = 9 // 报错
// 创建可读可写的计算属性: 传入对象
const plusOne = computed({
get: () => {
count.value + 1
},
set: val => {
count.value = val
}
})
return {
count,
plusOne
}
// watch监听某些数据的变化,从而触发某些行为
const ref = ref(0)
watch(()=> cosnole.log(ref.value))
setTimeout(()=>{
ref.value += 1
},1500)
// 监视 reactive类型数据源
const state = reactive({count:0})
watch(()=> state.count, (newVal,oldVal)=>{
},{lazy: true}) // lazy:true 组件第一次渲染 由undefined-0 不执行监听
// 监视 ref类型数据源
const ref = ref(0)
watch(()=>ref, (newVal,oldVal)=>{})
//监听多个reactive类型数据源
watch([()=> state.count, ()=>state.name], ([newCount, newName], [oldCount,oldName]) => {
})
//监听多个ref类型数据源
const count = ref(0)
const name = ref(name)
watch([count,name], ([newCount,newName],[oldCoount,oldName]) => {
})
//清除监视: 在组件还存在时,清除; watch会在组件被销毁时自动停止
const stop = watch(()=>{})
stop()
//在watch中清除异步任务
// 生命周期:写在setUp中
onBeforeMount(()=>{
})
onMounted(()=>{
})
},
created() {
},
props: {
p1: String
}
}
</script>
<style>
</style>
二、Vue3
官方文档
目前已支持的UI库
element-plus:pc组件库
我们建议您使用包管理器(如 NPM、Yarn 或 pnpm)安装 Element Plus,
然后您就可以使用打包工具,例如 Vite 或 webpack。
# 选择一个你喜欢的包管理器
# NPM
$ npm install element-plus --save
# Yarn
$ yarn add element-plus
# pnpm
$ pnpm install element-plus
ant-design-vue:pc组件库
https://antdv.com/components/button-cn
引入 ant-design-vue #
1. 安装脚手架工具 #
vue-cli
$ npm install -g @vue/cli
# OR
$ yarn global add @vue/cli
2. 创建一个项目 #
使用命令行进行初始化。
$ vue create antd-demo
并配置项目。
若安装缓慢报错,可尝试用 cnpm 或别的镜像源自行安装:rm -rf node_modules && cnpm install。
3. 使用组件 #
安装 #
$ npm i --save ant-design-vue
vant:移动端Vue组件库
Vant 3 - Lightweight Mobile UI Components built on Vue
Vue3新特性:vue3 性能更高, 体积更小, 更利于复用, 代码维护更方便
优势:
1、更好的逻辑复用 与 代码组织 (composition组合式api)
optionsAPI(旧) => compositionAPI(新), 优点: 代码组织更方便, 逻辑复用更方便 ,便于维护
2、更好的类型推导 (typescript支持)
vue3 源码用 ts 重写的, vue3 对 ts 的支持更友好了
vue3新特性:
数据响应式原理重新实现 (ES6 proxy 替代了 ES5 的 Object.defineProperty)
优势: 数组的更新检测等bug, 大大优化了响应式监听的性能 ---覆写
(原来检测对象属性的变化, 需要一个个对属性递归监听) proxy 可以直接对整个对象劫持
虚拟DOM - 新算法 (更快 更小)
提供了composition api, 可以更好的逻辑复用
模板可以有多个根元素
源码用 typescript 重写, 有更好的类型推导 (类型检测更为严格, 更稳定)
废弃了 eventbus 过滤器等等
Vite:是一种新型前端构建工具
优势:
-
极速的服务启动,使用原生 ESM 文件,无需打包
-
轻量快速的热重载,始终极快的模块热重载(HMR)
-
丰富的功能,对 TypeScript、JSX、CSS 等支持开箱即用
对比webpack
webpack启动项目 -> webpack进行打包编译->将打包的结果交给浏览器 ->浏览器运行
vite启动项目 -> 将打包编译的工作交给了浏览器->浏览器直接解析业务
基本使用
如果想要快速创建一个vue3项目,可以使用如下命令
创建普通vue项目
yarn create vite vite-demo --template vue
创建基于ts模板的项目
yarn create vite vite-demo-ts --template vue-ts
# npm 6.x
npm create vite@latest my-vue-app --template vue
# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template vue
# yarn
yarn create vite my-vue-app --template vue
# pnpm
pnpm create vite my-vue-app --template vue
Vue3项目详解
一、组合式API: 相似的逻辑, 容易复用
vue2 采用的就是 optionsAPI
(1) 优点:易于学习和使用, 每个代码有着明确的位置 (例如: 数据放 data 中, 方法放 methods中)
(2) 缺点: 相似的逻辑, 不容易复用, 在大项目中尤为明显
(3) 虽然 optionsAPI 可以通过mixins 提取相同的逻辑, 但是也并不是特别好维护
vue3 新增的就是 compositionAPI
(1) compositionAPI 是基于 逻辑功能 组织代码的, 一个功能 api 相关放到一起
(2) 即使项目大了, 功能多了, 也能快速定位功能相关的 api
(3) 大大的提升了 代码可读性 和 可维护性
vue3 推荐使用 composition API, 也保留了options API
即就算不用composition API, 用 vue2 的写法也完全兼容
// 功能: 鼠标移动显示鼠标坐标 x, y
// options API 版本
<template>
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script>
export default {
// vue2 中采用的是 options API
// 常见的配置项: data created methods watch computed components
data() {
return {
mouse: {
x: 0,
y: 0,
},
count: 0,
}
},
mounted() {
document.addEventListener('mousemove', this.move)
},
methods: {
move(e) {
this.mouse.x = e.pageX
this.mouse.y = e.pageY
},
add() {
this.count++
},
},
destroyed() {
document.removeEventListener('mousemove', this.move)
},
}
</script>
// composition API 版本
<template>
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script>
import { onMounted, onUnmounted, reactive, ref } from 'vue'
export default {
setup() {
const count = ref(0)
const add = () => {
count.value++
}
const mouse = reactive({
x: 0,
y: 0,
})
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
onMounted(() => {
document.addEventListener('mousemove', move)
})
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
return {
count,
add,
mouse,
}
},
}
</script>
// 抽离逻辑
function useMouse() {
const mouse = reactive({
x: 0,
y: 0,
})
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
onMounted(() => {
document.addEventListener('mousemove', move)
})
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
return mouse
}
function useCount() {
const count = ref(0)
const add = () => {
count.value++
}
return {
count,
add,
}
}
二、setup 函数
composition api的使用, 需要配置一个setup 函数
setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
setup 中不能使用 this, this 指向 undefined
在模版中需要使用的数据和函数,需要在 setup 返回
三、reactive 函数:用来定义响应式 对象数据
传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理)
<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<button @click="obj.name = 'ls'">改值</button>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
// 1. setup 需要返回值, 返回的值才能在模板中使用
// 2. 默认的普通的值不是响应式的, 需要用 reactive 函数
const obj = reactive({
name: 'zs',
age: 18
})
return {
obj
}
}
}
</script>
四、ref 函数
reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数
作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。
ref 函数接收一个的值, 返回一个ref 响应式对象, 有唯一的属性 value
在 setup 函数中, 通过 ref 对象的 value 属性, 可以访问到值
在模板中, ref 属性会自动解套, 不需要额外的 .value
ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
<template>
<div>{{ money }}</div>
<button @click="money++">改值</button>
</template>
<script>
import { reactive, ref } from 'vue'
export default {
setup() {
let money = ref(100)
money.value++
return {
money
}
}
}
</script>
五、计算属性computed函数
<template>
<div>我今年的年纪 <input type="text" v-model="age" /></div>
<div>我明年的年龄 {{ nextAge }}</div>
<div>我后年的年龄 <input type="text" v-model="nextAge2" /></div>
</template>
// script setup是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 script 语法更加简洁
<script setup>
import { computed, ref } from 'vue'
const age = ref(10)
// 不带set的计算属性
const nextAge = computed(() => {
return +age.value + 1
})
// 带set的计算属性
const nextAge2 = computed({
get() {
return +age.value + 2
},
set(value) {
age.value = value - 2
},
})
</script>
六、侦听器watch函数
watch监视, 接收三个参数 1. 参数1: 监视的数据源 2. 参数2: 回调函数 3. 参数3: 额外的配置
// 监听单个ref
const money = ref(100)
watch(money, (value, oldValue) => {
console.log(value)
})
// 监听多个ref
const money = ref(100)
const count = ref(0)
watch([money, count], (value) => {
console.log(value)
})
// 监听ref复杂数据
const user = ref({
name: 'zs',
age: 18,
})
watch(
user,
(value) => {
console.log('user变化了', value)
},
{
// 深度监听,,,当ref的值是一个复杂数据类型,需要深度监听
deep: true,
immediate: true
}
)
// 监听对象的某个属性的变化
const user = ref({
name: 'zs',
age: 18,
})
watch(
() => user.value.name,
(value) => {
console.log(value)
}
)
七、钩子函数的使用
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
八、组件通讯
父传子
// 父组件
<script setup>
import { ref } from 'vue'
// 在setup语法中,组件导入之后就能够直接使用,不需要使用components进行局部注册
import Son from './components/Son.vue'
const money = ref(100)
const car = ref('玛莎拉蒂')
</script>
<template>
<div>
<h1>我是父组件</h1>
<div>金钱:{{ money }}</div>
<div>车辆:{{ car }}</div>
<hr />
<Son :money="money" :car="car"></Son>
</div>
</template>
<style lang="less" scoped></style>
// 子组件
<script setup>
import { computed } from 'vue'
// defineProps: 接收父组件传递的数据
const props = defineProps({
money: Number,
car: String,
})
const myMoney = computed(() => {
return props.money + 100
})
</script>
<template>
<div>
<h3>我是子组件</h3>
<div>{{ money }} --- {{ car }}</div>
</div>
</template>
<style lang="less" scoped></style>
注意:如果使用defineProps接收数据,这个数据只能在模板中渲染,如果想要在script中也操作props属性,应该接收返回值。
子传父
-
子组件通过defineEmit获取emit对象(因为没有this)
-
子组件通过emit触发事件,并且传递数据
-
父组件提供方法
-
父组件通过自定义事件的方式给子组件注册事件
// 子组件 <script setup> defineProps({ money: Number, car: String, }) const emit = defineEmits(['changeMoney']) const change = () => { emit('changeMoney', 10) } </script> // 父组件 <script setup> import { ref } from 'vue' // 在setup语法中,组件导入之后就能够直接使用,不需要使用components进行局部注册 import Son from './components/Son.vue' const money = ref(100) const car = ref('玛莎拉蒂') const changeMoney = (num) => { money.value = money.value - num } </script> <Son :money="money" :car="car" @changeMoney="changeMoney"></Son>
九、依赖注入 - provide 和 injec
依赖注入, 可以非常方便的实现 跨层级的 组件通信
父组件利用 provide 提供数据
<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
provide('money', money)
</script>
<template>
<div>
<h1>我是父组件</h1>
<div>金钱:{{ money }}</div>
<hr />
<Son></Son>
</div>
</template>
<style lang="less" scoped></style>
子组件 (子孙后代, 都可以拿到这个数据)
<script setup>
import { inject } from 'vue'
const money = inject('money')
</script>
<template>
<div>
<h3>我是子组件--{{ money }}</h3>
<button>修改数据</button>
</div>
</template>
<style lang="less" scoped></style>
// 如果希望子传父, 可以 provide 传递一个方法
// 父组件
<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
const changeMoney = (num) => {
money.value = money.value - num
}
provide('money', money)
provide('changeMoney', changeMoney)
</script>
// 子组件
<script setup>
import { inject } from 'vue'
const money = inject('money')
const changeMoney = inject('changeMoney')
</script>