创建Vue3.x项目、Vue3.x项目和2.x项目的不同点以及Vue3.x项目的新增点
Vue3项目的创建使用vue create proNamevue create vue3-pro 自动下载安装包或者npm init vite-app vue3-pro 不会自动下载安装包然后选中 Vue3,回车即可,自动下载安装包? Please pick a preset:Default ([Vue 2] babel, eslint)❯ Default (Vue 3 Preview) ([Vu
Vue3项目的创建
使用vue create proName
vue create vue3-pro 自动下载安装包
或者
npm init vite-app vue3-pro 不会自动下载安装包
然后选中 Vue3,回车即可,自动下载安装包
? Please pick a preset:
Default ([Vue 2] babel, eslint)
❯ Default (Vue 3 Preview) ([Vue 3] babel, eslint)
Manually select features
运行 yarn serve
$ cd vue3-pro
$ yarn serve
页面展示
一、修改点
1、main.js
引入方式不一样
// vue3
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
// 没有全局的Vue
// vue2.X
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
2、全局API
Vue3里面没有全局的Vue,但是有个新的全局API:createApp
import { Vue, createApp } from 'vue'
console.log(Vue)
// undefined
const app = createApp({})
console.log(app)
component: ƒ component(name, component)
config: Object
directive: ƒ directive(name, directive)
mixin: ƒ mixin(mixin)
mount: (containerOrSelector) => {…}
provide: ƒ provide(key, value)
unmount: ƒ unmount()
use: ƒ use(plugin, ...options)
version: "3.0.0-rc.10"
_component: {}
_container: null
_context: {app: {…}, config: {…}, mixins: Array(0), components: {…}, directives: {…}, …}
_props: null
_uid: 1
get config: ƒ config()
set config: ƒ config(v)
2.x 全局 API | 3.x全局 API |
---|---|
Vue.config | app.config |
Vue.config.productionTip | 移除 |
Vue.config.ignoredElements | app.config.isCustomElement |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
使用第三方插件
示例:使用router,引入 app.use()
const app = createApp(MyApp)
app.use(VueRouter)
3、Composition API
import {ref, reactive, watch, computed, reactive, toRefs, createApp} from 'vue'
1. setup
要开始使用Composition API,我们首先需要一个可以实际使用它的地方。在Vue组件中,我们将此位置称为setup。
setup执行时尚未创建组件实例,所以不能使用this,此时this 为 undefined。除了props,无法访问组件中声明的任何data、computed、methods。
参数 (props, context)
// props 父组件传过来的props props是具有反应性的(传入新的props时会自动更新)
// context {attrs, emit, slots}
setup(props, context) {
console.log(context)
/**
* attrs: Proxy
* emit: (event, ...args) => instance.emit(event, ...args)
* slots: Proxy
*/
}
setup 生命周期钩子
钩子函数 | setup钩子 |
---|---|
beforeCreate | 没有 |
created | 没有 |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
由于setup是围绕beforeCreate和created生命周期挂钩运行的,因此您无需显式定义它们。换句话说,应该在这些钩子中编写的任何代码都应直接在setup函数中编写。
jsx
setup() {
const root = ref(null)
return () => <div ref={root} />
}
2. watch监听
从 vue 中引入 watch 可以参考 第4点 Reactivity(反应性)
- 监听 ref 声明的 反应性数据
- 监听 reactive 声明的反应性数据
- 监听 props 里面的数据
<script>
//一定使用之前引入
import {ref, watch} from 'vue'
setup(props) {
//监听 ref 数据
let count = ref(0)
watch(count, (val, old) => {
// 新数据、老数据
})
//监听 reactive 数据 对象
let person = reactive({
age: 18
})
watch(() => person.age, (val, old) => {
//新数据、老数据
})
//监听 reactive 数据 数组
let arr = reactive([1, 2, 3])
watch(arr, (val, old) => {
//新数据、老数据
})
//监听 props 的数据 加入props传了一个 testkey
watch(() => props.testkey, () => {
})
//要注意 return
return {
count,
person,
arr
}
}
</script>
3. computed 计算属性
从 vue 中引入 computed
<script>
import {ref, computed} from 'vue'
setup(props) {
let count = ref(0)
//当前组件的计算属性
let countCom = computed(() => {
return count.value * 2
})
//props传进来的计算属性
let countCom2 = computed(() => {
return props.propsKey * 2
})
}
</script>
4. setup 生命周期钩子使用
- setup 的 onMounted 执行 在 整个组件的 mounted 之前
- setup 的 onMounted 执行 在 setup 同步组件执行之后
- setup 的 onMounted 里面以及 setup 不能使用this
<script>
import {onMounted} from 'vue'
mounted () {
console.log(4)
},
setup () {
onMounted(() => {
console.log(1)
})
console.log(2)
setTimeout(() => {
console.log(3)
}, 0)
//打印顺序
// 2 1 4 3
onMounted(() => {
console.log(this)
})
//打印
//undefined
}
</script>
5. provide和inject
从 vue 中引入
父组件 provide
<script>
import {provide} from 'vue'
export default {
setup () {
provide('person', {
name: 'Bob',
age: 18
})
provide('city', '杭州')
}
}
</script>
子孙组件 inject
<script>
import {inject} from 'vue'
export default {
setup () {
//父辈组件 provide 的数据
let person = inject('person')
let city = inject('city')
//父辈组件 没有提供的值,给个默认值 不给默认为为 undefined 而且会有Vue warn提醒
let noProvide = inject('noProvide', 'noProvide hah')
}
}
</script>
provide 带有反应性的数据 父组件值修改,子孙组件值会对应的修改
<script>
import {provide} from 'vue'
export default {
setup () {
let city = ref('滨江')
provide('city', city)
}
}
</script>
provide 一个 函数
<script>
import {provide} from 'vue'
export default {
setup () {
let provideFun = () => {
console.log('provide fun')
}
provide('provideFun', provideFun)
}
}
</script>
4、Reactivity(反应性)
Vue最独特的功能之一是不引人注目的反应系统。
当您将纯JavaScript对象作为data选项传递给应用程序或组件实例时,Vue将遍历其所有属性,并使用带有getter和setter的处理程序将它们转换为Proxies
- 跟踪更改它的函数:在代理的getter中进行此操作 effect
- 触发函数,以便它可以更新最终值:在代理中的setter中进行操作 trigger
声明反应性数据
一般使用 ref、reactive、readonly 来声明数据
- ref 声明基本类型
- reactive 声明引用类型
- readonly 声明只读引用类型
import { ref, reactive, readonly } from 'vue'
export default {
setup () {
// 一般用 ref 声明 基本 类型
// 用 reactive 声明 引用 类型
let count = ref(0) // 在 script 使用 count.value,在 template 使用 {{count}}
let state = reactive({
name: 'Bob'
}) // 在 script 使用 state.name, 在 template 使用 {{state.name}}
// 用 ref 声明 引用类型
let obj1 = ref({
count: 1
})
// 用 reactive 声明 基本 类型 警告⚠️ 可以正常使用,但是没有反应性
let num1 = reactive(10) //value cannot be made reactive: 10
// readonly
// readonly 和 reactive 一样,但是声明的是只读数据 声明基本类型和 reactive 一样警告提醒
let person = readonly({age: 18})
setTimeout(() => {
// 定时器 修改 警告 ⚠️
person.age = 10 // Set operation on key "age" failed: target is readonly
});
// 一定要return 才可以在 template 和 script 其他地方使用
return {
count,
state,
obj1
}
}
}
count打印出的数据
RefImpl {_rawValue: 0, _shallow: false, __v_isRef: true, _value: 0}
__v_isRef: true
_rawValue: 0
_shallow: false
_value: 0
value: 0
state打印出的数据
Proxy {name: "Bob"}
[[Handler]]: Object
deleteProperty: ƒ deleteProperty(target, key)
get: ƒ (target, key, receiver)
has: ƒ has(target, key)
ownKeys: ƒ ownKeys(target)
set: ƒ (target, key, value, receiver)
[[Target]]: Object
name: "Bob"
[[IsRevoked]]: false
obj1打印出的数据
RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy}
__v_isRef: true
_rawValue: {count: 1}
_shallow: false
_value: Proxy {count: 1}
value: Proxy
[[Handler]]: Object
[[Target]]: Object
count: 1
[[IsRevoked]]: false
num1打印的数据
10
template中使用
<!-- 使用ref定义的基本类型直接使用 -->
<!-- 使用reactive定义的引用类型直接.对应的属性 -->
<div class="vue3_pro">
count: {{count}}
state: {{state.name}}
</div>
5、filters 过滤器
在 vue2.x 中使用 filters 过滤器,一种很方便也很实用的语法。
但是在 vue3.x 中过滤器属性已删除,不再支持
在3.x中,过滤器已删除,不再受支持。相反,我们建议将它们替换为方法调用或计算的属性。
6、directive 自定义指令
在 vue2.x 中,会使用自定义指令来进行一些新的指令的绑定,例如,fouces,drag等
在 vue3.x 中,也有自定义指令的使用,只是方法改掉了
在 2.x 中的钩子函数
- bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
- inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
- update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
3.x 中的钩子函数
2.x | 3.x |
---|---|
bind | beforeMount |
inserted | mounted |
beforeUpdate: 新增(这在元素本身更新之前被调用) | |
update | 已删除 |
componentUpdated | updated |
beforeUnmount 新增(在卸载元素之前立即调用。) | |
unbind | unmounted |
3.x 中的自定义指令
const MyDirective = {
beforeMount(el, binding, vnode, prevVnode) {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {},
unmounted() {}
}
把 2.x 中的自定义指令 改成 3.x 的
这里有 2.x Vue自定义拖拽指令
vue3.x 中使用自定义指令
import { createApp } from 'vue'
import App from './App.vue'
let app = createApp(App)
app.directive('drag', {
beforeMount (el, binding) {
console.log(el, binding)
let oDiv = el; //当前元素
oDiv.onmousedown = function (e) {
//鼠标按下,计算当前元素距离可视区的距离
let disX = e.clientX - oDiv.offsetLeft;
let disY = e.clientY - oDiv.offsetTop;
document.onmousemove = function (e) {
//通过事件委托,计算移动的距离
let l = e.clientX - disX;
let t = e.clientY - disY;
//移动当前元素
oDiv.style.left = l + 'px';
oDiv.style.top = t + 'px';
//将此时的位置传出去
binding.value({x:e.pageX,y:e.pageY}, el)
};
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
};
};
}
})
app.mount('#app')
template中使用
<template>
<div v-drag="dragFun" class="div1"></div>
</template>
<script>
export default {
setup() {
let dragFun = (val, e) => {
console.log(val, e)
// val 是 在binging.value 的值
// e 是对应的element元素
}
return {
dragFun
}
}
}
</script>
7、defineAsyncComponent 异步组件
创建一个仅在必要时加载的异步组件。
在Vue 3中,由于功能组件被定义为纯函数,因此需要通过将异步组件定义包装在新的defineAsyncComponent帮助器中来明确定义异步组件定义
从 vue 中引入
//没有 options 参数时
const testCom = defineAsyncComponent(() => import('./testCom.vue'))
有 options 参数时
const asyncPageWithOptions = defineAsyncComponent({
loader: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
所有的参数
const AsyncComp = defineAsyncComponent({
// 工厂函数
loader: () => import('./Foo.vue')
// 加载异步组件时要使用的组件
loadingComponent: LoadingComponent,
// 加载失败时使用的组件
errorComponent: ErrorComponent,
// 显示加载组件之前的延迟。 默认值:200ms。
delay: 200,
// 如果提供并超过了超时,则将显示错误组件。 默认值:无穷大
timeout: 3000,
// 一个返回布尔值的函数,该值指示加载程序承诺拒绝时异步组件是否应重试
retryWhen: error => error.code !== 404,
// 允许的最大重试次数
maxRetries: 3,
// 定义组件是否
suspensible: false
})
8、子元素 emit 事件
由于 setup 里面不能使用 this,所以不能像 vue2.x 一样使用 this.$emit(‘emitFun’, val),setup 里面有两个参数,第二个参数可以进行结构得到 emit,也可以直接直接使用 context.emit 进行 emit 事件
在setup里面 emit 事件
<button @click="clickCom">点击</button>
setup(props, context) {
let clickCom = () => {
context.emit('emit-fun', {emit: true})
}
return {
clickCom
}
}
9、inline-template 内联模板
在 vue3.x 中对内联模板功能的支持已删除。
在2.x中,Vue inline-template在子组件上提供了属性,以使用其内部内容作为其模板,而不是将其视为分布式内容
3.x不在支持此功能
迁移
1. 使用
<script type="text/html" id="my-comp-template">
<div>{{ hello }}</div>
</script>
const MyComp = {
template: '#my-comp-template'
// ...
}
- 默认插槽
<my-comp v-slot="{ childState }">
{{ parentMsg }} {{ childState }}
</my-comp>
<!-- 子组件中 -->
<template>
<slot :childState="childState" />
</template>
10、keyCodes 修饰符
更改点
- v-on不再支持使用数字(即keyCodes)作为修饰符
- config.keyCodes不再受支持
由于KeyboardEvent.keyCode已弃用,因此Vue 3也不再继续支持此功能。因此,现在建议将kebab-case名称用于要用作修饰符的任何键。
<!-- Vue 2.x -->
<input v-on:keyup.13="submit" />
<input v-on:keyup.enter="submit" />
<!-- Vue 3.x -->
<input v-on:keyup.delete="confirmDelete" />
11、render() 渲染
更改点
- h 现在已全局导入,而不是传递给渲染函数作为参数
- 渲染函数参数已更改为在有状态和功能组件之间更加一致
- VNode现在具有扁平的道具结构
h 需要从 vue 中导入
import { h } from 'vue'
在3.x中,由于render函数不再接收任何参数,因此它将主要在setup()函数内部使用。
import { h, reactive } from 'vue'
export default {
setup(props, { slots, attrs, emit }) {
const state = reactive({
count: 0
})
function increment() {
state.count++
}
return () =>
h(
'div',
{
onClick: increment
},
state.count
)
}
}
在 3.x 中VNode props结构被平铺
{
class: ['button', 'is-outlined'],
style: { color: '#34495E' },
id: 'submit',
innerHTML: '',
onClick: submitForm,
key: 'submit-button'
}
12、slots 插槽
更改点
- this.$slots 公开为功能
- this.$scopedSlots已删除
二、新增点
1、多根节点组件
在Vue 3中,组件现在正式支持多根节点组件,即片段!
<template>
<header>...</header>
<main>...</main>
<footer>...</footer>
</template>
2、Teleport 传送
Teleport提供了一种的方法,使我们可以控制要在DOM中哪个父对象下呈现HTML,而不必求助于全局状态或将其拆分为两个部分。
props 属性值
- to [string] 必须的属性 必须是有效的查询选择器或HTMLElement 指定将内容移动到的目标元素
<teleport to="#some-id">
<test />
</teleport>
<teleport to=".some-class">
<test />
</teleport>
<teleport to="[data-teleport]">
<test />
</teleport>
- disabled [boolean] 可用于禁用的功能
可以动态渲染到具体的位置
<teleport to="body" :disabled="displayVideoInline">
<video src="my-movie.mp4">
</teleport>
更多推荐
所有评论(0)