Vue3新特性
组件
在 script
标签中使用 setup
方法, 使用 import
进行引入,可直接使用,无需使用 components
方法注册组件
<script setup>
import Header from './components/Header.vue'
</script>
<template>
<Header/>
</template>
多个根标签
<template>
<div>根标签1</div>
<h1>根标签2</h1>
</template>
组合式API
Setup 方法
setup
方法是Composition API(组合式API)
的入口
语法糖
<script setup></script>
引入或使用方法
Vue3
中使用 watch
,computed
等方法需要使用import
进行导入
<script setup>
import { ref, watch, computed } from 'vue'
</script>
ref 和 reactive(递归监听,每层数据都使用proxy进行监听)
ref
用来定义基本数据类型
通过Object.definePrototype()
的get
和set
来实现响应式(数据劫持)
操作数据需要使用.value
,读取数据时模板中直接读取不需要.value
<script setup>
let count = ref(0)
</script>
reactive
用来定义对象或数组类型数据
通过使用Proxy
来实现响应式(数据劫持),并通过Reflect
操作源代码内部的数据
操作数据与读取数据:均不需要.value
<script setup>
let count = reactive({
value: 0
})
</script>
注意:项目中常用
reactive
进行数据操作
shallowRef 和 shallowReactive(非递归监听)
需要监听的数据量比较大的时候,使用
shallowReactive
和shallowRef
shallowReactive
只监听第一层(最外层)的数据变化,牵一发而动全身
第一层数据不发生改变,其内部的数据便不会发生改变
<script setup>
import { shallowReactive } from 'vue'
let count = shallowReactive({
a: 'a',
f: {
b: 'b',
s: {
c: 'c'
}
}
})
function myFn() {
count.a = '1';
count.f.b = '2';
count.f.s.c = '3';
console.log(state)
console.log(state.f)
console.log(state.f.s)
}
</script>
shallowRef
只监听.value
的变化,并不是监听第一层(最外层)的变化
<script setup>
import { shallowRef } from 'vue'
let count = shallowRef({
a: 'a',
f: {
b: 'b',
s: {
c: 'c'
}
}
})
function myFn() {
count.value = {
a: '1',
f: {
b: '2',
s: {
c: '3'
}
}
}
console.log(state)
console.log(state.f)
console.log(state.f.s)
}
</script>
triggerRef
根据传入的数据,进行局部更新
<script setup>
import { shallowRef, triggerRef } from 'vue'
let count = shallowRef({
a: 'a',
f: {
b: 'b',
s: {
c: 'c'
}
}
})
function myFn() {
count.value.f.s.c = '3'
triggerRef(count)
console.log(state)
console.log(state.f)
console.log(state.f.s)
}
</script>
toRaw
避免不必要的更新和追踪,使用toRaw
进行修改原数据,不会被追踪和更新UI,优化性能
<script setup>
// reactive
import {reactive, toRaw} from 'vue'
let obj = {name: 'zs', age: 18}
let state = reactive(obj)
let obj2 = toRaw(state)
console.log(obj)
console.log(state)
console.log(obj2)
</script>
<script setup>
// ref
import {ref, toRaw} from 'vue'
let obj = {name: 'zs', age: 18}
let state = ref(obj)
let obj2 = toRaw(state.value)
console.log(obj)
console.log(state)
console.log(obj2)
</script>
markRaw
为了让一些数据永远不被更新和追踪
<script setup>
import {reactive, markRaw} from 'vue'
let obj = {name: 'zs', age: 18}
obj.markRaw(obj)
let state = reactive(obj)
function myFn() {
state.name = 'ls'
}
</script>
Provide/Inject
组件深度传值
<template>
<Child/>
</template>
<script setup>
import Child from './Child.vue'
import { provide, ref } from 'vue'
let count = ref(0)
provide('count', count)
</script>
<template>
<h2>{{count}}</h2>
</template>
<script setup>
import { inject } from 'vue'
let count = inject('count')
</script>
安装配置vue扩展
vue-router
# npm 安装
npm install vue-router@4
# 在 src 目录下新建 router 和 views 目录
# 在 router 目录下新建 index.js 文件,并进行配置
```js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: () => import('@/views/props.vue') // .vue不能省略
}
]
})
export default router
在 main.js 中调用
import { createApp } 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router).mount('#app')
在app.vue 中使用 进行使用即可
<a name="TRADP"></a>
### vuex
```markdown
# npm 安装
npm install vuex@next --save
# 在 src 目录下新建 store 目录
# 在 store 目录下新建 index.js 文件,并进行配置
```js
import { createStore } from 'vuex'
const store = createStore({
state: {
test: 'test'
},
mutations: {},
actions: {},
getters: {}
})
export default store
在 main.js 中调用
import { createApp } 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app.use(store).mount('#app')
在任意组件中调用,查询结果是否正确
<template>
<h1>{{$store.state.test}}</h1>
</template>
<script>
export default {}
</script>
<style>
</style>
<a name="rNzxR"></a>
### Scss&Sass
```markdown
# npm 安装
npm i node-sass sass-loader style-loader --save
# 在 package.json 文件中配置
Vue3响应式原理
- 通过
Proxy(代理)
:拦截对象中任意属性的变化, 包括属性的读写、属性的添加、属性的删除等 通过
Reflect(反射)
:对源对象的属性进行操作const p = new Proxy(data, { // 读取属性时调用 get(target, propName) { return Reflect.get(target, propName) }, // 修改或添加属性时调用 set(target, propName, value) { return Reflect.set(target, propName, value) }, // 删除属性时调用 deleteProperty(target, propName) { return Reflect.deleteProperty(target, propName) } })
Vue3配置路径别名
在项目根目录下创建
vite.config.js
文件,有则无需创建// 引入 path 模块 import * as path from 'path' // 配置别名 export default defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, 'src') } } })
vue3.js 安装
CDN 安装
<script src="https://unpkg.com/vue@next"></script>
NPM 安装
npm install vue@next
如果需要使用
单文件组件
,需安装@vue/compiler-sfc
:npm install @vue/compiler-sfc
如果你是从 Vue 2 过渡而来的,请注意
@vue/compiler-sfc
替换掉了vue-template-compiler
Vue3 渲染模板
下面模板中的
num
为一个变量<div id="counter"> <h1>counter: {{ num }}</h1> </div>
Counter
为一个对象,Vue.createApp(Counter)
创建了一个应用并将Counter
对象注册到该应用中,.mount("#counter")
是获取到id
值为counter
的 html 元素,并将Counter
对象中的num
属性渲染到 html 模板中<script> const Counter = { data() { return { num: 0 } } } Vue.createApp(Counter).mount("#counter") </script>
-
使用 Vite 构建工具创建 Vue3 项目
方法1:
```bash $ npm init vite-app
$ cd
<a name="HePoV"></a>
## 方法2:
```bash
# npm 6.x
$ npm init vite@latest <project-name> --template vue
# npm 7+, extra double-dash is needed:
$ npm init vite@latest <project-name> -- --template vue
$ cd <project-name>
$ npm install
$ npm run dev
方法3:
$ npm init @vitejs/app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
Vue3生命周期 & 实例生命周期
- Vue3 生命周期图
不在setup
函数内的写法
beforeCreate()
:数据初始化之前执行的方法created()
:数据初始化之后执行的方法beforeMount()
:数据挂载渲染之前执行的函数mounted()
:数据挂载渲染之后执行的函数beforeUpdate()
:数据更新之前执行的函数updated()
:数据更新之后执行的函数beforeUnmount()
:实例销毁之前执行的函数-
在
setup
函数内的写法: beforeCreate
===>Not needed
不需要写created
===>Not needed
不需要写beforeMount
===>onBeforeMount
mounted
===>onMounted
beforeUpdate
===>onBerforeUpdate
updated
===>onUpdated
beforeUnmount
===>onBerforeUnmount
unmounted
===>onUnmounted
在Vue3中,
beforeCreate
和created
并没有组合式API,setup
就相当于这两个生命周期函数
基础语法
组件基础
通过插槽分发内容
// 父组件模板内容
<template>
<div>
<!-- 子组件 -->
<alert-box>
Something bad happened.
</alert-box>
</div>
</template>
<script setup>
import alertBox from '子组件路径'
</script>
// 子组件模板内容
<template>
<div>
<strong>Error!</strong>
<!-- 通过插槽标签来接收父组件中子组件标签内的内容 -->
<slot></slot>
</div>
</template>
解析 DOM 模板时注意的事项
元素位置受限
- 一些 HTML 元素内部是有严格限制的,意思是这些元素内部只能出现特定的元素
下面模板内的自定义组件会被作为无效的内容提升到外部,并导致最终渲染结果出错
<template> <div> <table> <!-- blog-post-row 为自定义组件 --> <blog-post-row></blog-post-row> </table> </div> </template>
可以通过
**is**
attribute 方法,作为一个变通的办法:<template> <div> <table> <tr is="vue:blog-post-row"></tr> </table> </div> </template>
当
**is**
用于原生 HTML 元素时,**is**
的值必须以**vue:**
开头,才可以被解释为 Vue 组件 这是为了避免和原生自定义元素混淆
大小写不敏感
- HTML arribute 名不区分大小写,因此浏览器将所有大写字符解释为小写。
- 这意味着当你在 DOM 模板中使用
**驼峰 prop**
名称和**event 处理器参数**
需要使用它们的 kebab-cased (横线字符分割) 等效值: ```vue // 方法1:
方法2:
{{ postTitle }}
{{ greeting }}
// CSS 模板 ``` ## 组件大小写方法 - 方法1:kebab-case(短横线隔开) ```javascript app.component('my-component-name', { /* ... */ }) ``` 当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 `<a name="wg2S2"></a>
## 可用的钩子函数
- `created`:绑定元素的属性或i事件监听器被应用之前调用
- `beforeMount`:当指令第一次绑定到元素并且挂在父组件之前调用
- `mounted`:在绑定元素的父组件被挂载后调用
- `beforeUpdate`:在更新包含组件的 VNode 之前调用
- `updated`:在包含组件的 VNode **及其子组件的 VNode** 更新后调用
- `beforeUnmount`:在卸载(销毁)绑定元素的父组件之前调用
- `unmounted`:当指令与元素解除绑定且父组件已经卸载(销毁)时,只调用一次
<a name="G1uEJ"></a>
## 注意点
- 如果想在`mounted`和`updated`时触发相同的行为,而不关心其他的钩子函数,可以去掉这两个钩子函数
<a name="dQAr6"></a>
# Teleport
- 传送门:将`teleport`标签内的内容传送到指定的元素内部进行渲染
<a name="hhYjP"></a>
## teleport实现传送
vue
```vue
<script setup>
</script>
<template>
<h2>This is a parent component</h2>
<teleport to='body'> // 将该标签中的内容传送至body标签下
<h2>传送至body标签下</h2>
</teleport>
</template>
<style lang="">
</style>
注意:
<teleport to="">
中的to
属性表示传送的位置,to
的值是传送至标签的类名(标签名或id)
在同一传送目标上使用多个 teleport
<teleport to="body">
<div>teleport1</div>
</teleport>
<teleport to="body">
<div>teleport2</div>
</teleport>
<!-- result -->
<body>
<div>teleport1</div>
<div>teleport2</div>
</body>