基础
demo
- standard vue3
main.js
App.vue
HelloWorld.vue
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
- vite vue3
main.js
App.vue
HelloWorld.vue
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App).mount('#app')
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
<script setup>
import { ref } from 'vue'
defineProps({
msg: String,
})
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>
- vite vue3-ts
main.ts
App.vue
HelloWorld.vue
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App).mount('#app')
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>
** 因为 vite 启动速度快,所有后面示例均用 vite js 版本
setup 返回对象
main.js
App.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<h3>name:{{name}}</h3>
<h3>name:{{age}}</h3>
<button @click="sayHello">说话</button>
</template>
<script>
export default {
name: 'App',
setup() {
let name = '张三'
let age = 18
function sayHello() {
alert(`你好,我的名字叫${name}, 我的年龄为${age}岁`)
}
return {
name,
age,
sayHello
}
}
}
</script>
setup 返回渲染函数
main.js
App.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<h3>name:{{name}}</h3>
<h3>name:{{age}}</h3>
<button @click="sayHello">说话</button>
</template>
<script>
import {h} from 'vue'
export default {
name: 'App',
setup() {
let name = '张三'
let age = 18
// function sayHello() {
// alert(`你好,我的名字叫${name}, 我的年龄为${age}岁`)
// }
// 返回的渲染函数跟 vue2 创建 vm 的 render一样
return () => {
return h('h1', '你好')
}
}
}
</script>
vue2 和 3 不要混用
- vue3 调用 vue2 失败
main.js
App.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<h3>name:{{name}}</h3>
<h3>name:{{age}}</h3>
<button @click="sayHello">说话</button>
<br>
<button @click="sayHello2">说话vue2</button>
<br>
<button @click="test">test(v2调用v3)</button>
<br>
<button @click="test2">test(v3调用v2)</button>
</template>
<script>
import {h} from 'vue'
export default {
name: 'App',
data() {
return {
sex: '男'
}
},
methods: {
sayHello2() {
alert(`你好,我的名字叫${this.name}, 我的年龄为${this.age}岁, 性别${this.age}`)
},
test() {
console.log(this.name);
console.log(this.age);
console.log(this.sex);
console.log(this.sayHello);
}
},
setup() {
let name = '张三'
let age = 18
function sayHello() {
alert(`你好,我的名字叫${name}, 我的年龄为${age}岁`)
}
function test2() {
console.log(this.name);
console.log(this.age);
console.log(this.sex);
console.log(this.sayHello);
}
return {
name,
age,
sayHello,
test2
}
}
}
</script>
ref 响应数据
main.js
App.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<h3>name:{{name}}</h3>
<h3>name:{{age}}</h3>
<h3>工作岗位:{{job.type}}</h3>
<h3>工作薪资:{{job.salary}}</h3>
<button @click="changeInfo">修改信息</button>
</template>
<script>
import {ref} from 'vue'
export default {
name: 'App',
setup() {
// 以下不是响应式数据,这样定义页面不会因数据改变更新
// let name = '张三'
// let age = 18
// 以下为响应式
let name = ref('张三')
let age = ref(18)
let job = ref(
{
type: '前端leader',
salary: '40k'
}
)
function changeInfo() {
name.value = '李四'
age.value = 20
job.value.type = '搬砖工'
job.value.salary = '1k'
}
return {
name,
age,
job,
changeInfo
}
}
}
</script>
reactive 响应数据
- reactive 可以传数组或对象,转换为proxy 对象,可以直接对属性修改
main.js
App.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<h3>name:{{name}}</h3>
<h3>name:{{age}}</h3>
<h3>工作岗位:{{job.type}}</h3>
<h3>工作薪资:{{job.salary}}</h3>
<h3>爱好:{{hobby}}</h3>
<button @click="changeInfo">修改信息</button>
</template>
<script>
import {ref, reactive} from 'vue'
export default {
name: 'App',
setup() {
// 以下不是响应式数据,这样定义页面不会因数据改变更新
// let name = '张三'
// let age = 18
// 以下为响应式
let name = ref('张三')
let age = ref(18)
let job = reactive(
{
type: '前端leader',
salary: '40k'
}
)
// reactive 可以传数组或对象,转换为proxy 对象,可以直接对属性修改
let hobby = reactive(['抽烟', '喝酒', '烫头'])
function changeInfo() {
name.value = '李四'
age.value = 20
job.type = '搬砖工'
job.salary = '1k'
hobby[0] = '学习' // reactive 可以直接处理数组数据,不需要调用数组函数
}
return {
name,
age,
job,
hobby,
changeInfo
}
}
}
</script>
reactive 作用对象
main.js
App.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<h3 v-show="person.name">name:{{person.name}}</h3>
<h3>name:{{person.age}}</h3>
<h3 v-show="person.sex">sex:{{person.sex}}</h3>
<h3>工作岗位:{{person.job.type}}</h3>
<h3>工作薪资:{{person.job.salary}}</h3>
<h3>hobby: {{ person.hobby }}</h3>
<button @click="changeInfo">修改信息</button>
<br>
<button @click="addSex">添加性别属性</button>
<br>
<button @click="deleteName">删除name属性</button>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: 18,
job: {
type: '前端leader',
salary: '40k'
},
hobby: ['抽烟', '喝酒', '烫头']
})
function changeInfo() {
person.name = '李四'
person.age = 20
person.job.type = '搬砖工'
person.job.salary = '1k'
person.hobby[0] = '学习' // reactive 可以直接处理数组数据,不需要调用数组函数
}
function addSex() {
person.sex = '男'
}
function deleteName() {
delete person.name
}
return {
person,
changeInfo,
addSex,
deleteName
}
}
}
</script>
vue3响应式原理
let p = new Proxy(person, {
// 读取时调用
get(target, propName){
console.log(`有人读取了${propName}属性`);
return Reflect.get(target, propName)
},
// 修改和增加都会调用
set(target, propName, value){
console.log(`有人修改了${propName}属性,原属性${target[propName]}, 修改后属性${value}`);
// target[propName] = value
Reflect.set(target, propName, value)
},
// 有人删除时调用
defineProperty(target, propName){
console.log(`有人删除了${propName}属性`);
// return delete target[propName]
return Reflect.deleteProperty(target, propName)
}
})
注意事项
- props 未声明,外部如果又传入了,会传入到 context.attr
- v3自定义事件不声明接收会有警告
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo @hello="showHelloMsg" msg="你好" school="小学">
<template v-slot:h1>
<div>
hi 1
</div>
</template>
<template v-slot:h2>
<div>
hi 2
</div>
</template>
</Demo>
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'App',
components: {Demo},
setup(){
function showHelloMsg(value){
console.log(`show hello msg:${value}`);
}
return {
showHelloMsg
}
}
}
</script>
<template>
<h3>name:{{ person.name }}</h3>
<h3>age:{{ person.age }}</h3>
<button @click="test1">测试</button>
</template>
<script>
import {reactive} from "vue";
export default {
name: "Demo",
props: ["msg", "school"], // 如果没声明外部又传递了,会传入context.attr中
emits: ["hello"], // v3自定义事件不声明接收会有警告
beforeCreate() {
console.log(`Demo beforeCreate`);
},
setup(props, context) {
// setup 比 beforeCreate早
console.log(`Demo setup props`, props);
console.log(`Demo setup context`, context);
console.log(`Demo setup context attrs`, context.attrs);
console.log(`Demo setup context slots`, context.slots);
let person = reactive({
name: "张三",
age: 18,
});
// function test1(){
// context.emit('hello', 'hi')
// }
return {
person,
test1() {
context.emit("hello", "hi");
},
};
},
};
</script>
<style>
</style>
computed 计算属性
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo />
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'App',
components: {Demo}
}
</script>
<template>
<label for="firstName">姓:</label><input id="firstName" type="text" v-model="person.firstName"><br>
<label for="lastName">名:</label><input id="lastName" type="text" v-model="person.lastName"><br>
<label>得到全名:</label>{{person.fullName}}<br>
<label for="fullName">修改全名:</label><input id="fullName" type="text" v-model="person.fullName"><br>
</template>
<script>
import {reactive, computed} from "vue";
export default {
name: "Demo",
beforeCreate() {
console.log(`Demo beforeCreate`);
},
setup() {
let person = reactive({
firstName: '张',
lastName: '三'
});
// 方式一 computed 的简写形式, 不能对全名修改
// person.fullName = computed(()=>{
// return person.firstName + '-' + person.lastName
// })
person.fullName = computed({
get() {
return person.firstName + '-' + person.lastName
},
set(value) {
const arr = value.split('-')
person.firstName = arr[0]
person.lastName = arr[1]
}
})
return {
person
};
},
};
</script>
<style>
</style>
watch 监视 ref
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo />
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'App',
components: {Demo}
}
</script>
<template>
<h2>当前n值:{{sum}}</h2>
<button @click="sum++">点我+1</button>
<h2>当前信息:{{msg}}</h2>
<button @click="msg+='!'">修改信息</button>
</template>
<script>
import {ref, watch} from "vue";
export default {
name: "Demo",
beforeCreate() {
console.log(`Demo beforeCreate`);
},
setup() {
let sum = ref(0)
let msg = ref('你好')
// 情况一 一个响应式数据
// watch(sum, (newValue, oldValue) => {
// console.log(`sum 变化了, newValue:${newValue}, oldValue:${oldValue}`);
// })
// 方式二 watch 监视多个数据,watch 可以调用多次
// watch(sum, (newValue, oldValue) => {
// console.log(`sum 变化了, newValue:${newValue}, oldValue:${oldValue}`);
// })
// watch(msg, (newValue, _) => {
// console.log(`msg 变化了 新的为 ${newValue}`);
// })
// 方式三 watch 直接多个监视
// watch([sum, msg], (newValue, oldValue) => {
// console.log(newValue, oldValue);
// })
// watch 传递 参数
watch(sum, (newValue, oldValue) => {
console.log(`sum 变化了, newValue:${newValue}, oldValue:${oldValue}`);
}, {immediate: true})
person.fullName = computed({
get() {
return person.firstName + '-' + person.lastName
},
set(value) {
const arr = value.split('-')
person.firstName = arr[0]
person.lastName = arr[1]
}
})
return {
person
};
},
};
</script>
<style>
</style>
watch 监视 reactive
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo />
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'App',
components: {Demo}
}
</script>
<template>
<h2>当前n值:{{ sum }}</h2>
<button @click="sum++">点我+1</button>
<h2>当前信息:{{ msg }}</h2>
<button @click="msg += '!'">修改信息</button>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.salary }}k</h2>
<button @click="person.name = 'xx'">修改姓名</button>
<button @click="person.age++">修改年龄</button>
<button @click="person.job.salary++">涨薪</button>
</template>
<script>
import { reactive, ref, watch } from "vue";
export default {
name: "Demo",
beforeCreate() {
console.log(`Demo beforeCreate`);
},
setup() {
let sum = ref(0);
let msg = ref("你好");
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
});
// 情况一 watch 监视 reactive, newValue oldValue有问题, 都显示最新的值, 且deep无效
// watch(person, (newValue, oldValue) => {
// console.log(`person变化了 new:${JSON.stringify(newValue)} old:${JSON.stringify(oldValue)}`);
// })
// 情况四 监视 reactive 定义的一个数据, 必须写成函数
// watch(()=>person.age, (newValue, oldValue) => {
// console.log(newValue, oldValue);
// })
// 情况四 监视 reactive 定义的某些数据, 必须写成函数
// watch([() => person.age, () => person.name], (newValue, oldValue) => {
// console.log(newValue, oldValue);
// });
// 特殊情况,监视reactive 中的一个对象, 必须写deep true,否则不奏效
watch(()=> person.job, (newValue, oldValue) => {
console.log(newValue, oldValue);
});
return {
person,
};
},
};
</script>
<style>
</style>
watchEffect
- 用什么就监视什么
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo />
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'App',
components: {Demo}
}
</script>
<template>
<h2>当前n值:{{ sum }}</h2>
<button @click="sum++">点我+1</button>
<h2>当前信息:{{ msg }}</h2>
<button @click="msg += '!'">修改信息</button>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.salary }}k</h2>
<button @click="person.name = 'xx'">修改姓名</button>
<button @click="person.age++">修改年龄</button>
<button @click="person.job.salary++">涨薪</button>
</template>
<script>
import { reactive, ref, watch, watchEffect } from "vue";
export default {
name: "Demo",
beforeCreate() {
console.log(`Demo beforeCreate`);
},
setup() {
let sum = ref(0);
let msg = ref("你好");
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
});
// 情况一 watch 监视 reactive, newValue oldValue有问题, 都显示最新的值, 且deep无效
watch(sum, (newValue, oldValue) => {
console.log(`person变化了 new:${JSON.stringify(newValue)} old:${JSON.stringify(oldValue)}`);
})
watchEffect(()=>{
const x1 = sum.value
const x2 = msg.value
console.log(`用啥监视啥, x1:${x1}, x2:${x2}}`);
})
return {
person,
};
},
};
</script>
<style>
</style>
生命周期
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<button @click="isShowDemo = !isShowDemo">切换隐藏和显示</button>
<Demo v-if="isShowDemo" />
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'App',
components: {Demo},
setup() {
let isShowDemo = ref(true)
return {
isShowDemo
}
}
}
</script>
<template>
<h2>当前n值:{{ sum }}</h2>
<button @click="sum++">点我+1</button>
</template>
<script>
import { reactive, ref, watch, watchEffect } from "vue";
export default {
name: "Demo",
setup() {
let sum = ref(0);
return {
sum
};
},
// 配置项形式写生命周期钩子
beforeCreate() {
console.log(`Demo beforeCreate`);
},
created() {
console.log(`Demo created`);
},
beforeMount() {
console.log(`Demo beforeMount`);
},
mounted(){
console.log(`Demo mounted`);
},
beforeUpdate() {
console.log(`Demo beforeUpdate`);
},
updated() {
console.log(`Demo updated`);
},
beforeDestroy() {
console.log(`Demo beforeDestroy 废弃`);
},
destroyed() {
console.log(`Demo destroyed 废弃`);
},
beforeUnmount() {
console.log(`Demo beforeUnmount`);
},
unmounted(){
console.log(`Demo unmounted`);
}
};
</script>
<style>
</style>
toRefs
- 分离属性
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo />
</template>
<script>
import Demo from "./components/Demo.vue";
export default {
name: "App",
components: { Demo },
};
</script>
<template>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<!-- <h2>薪资:{{ salary }}k</h2> -->
<h2>薪资:{{ job.j1.salary }}k</h2>
<button @click="name = 'xx'">修改姓名</button>
<button @click="age++">修改年龄</button>
<!-- <button @click="salary++">涨薪</button> -->
<!-- toRefs 展开完必须带上 -->
<button @click="job.j1.salary++">涨薪</button>
<button @click="handlerPrintPerson">person信息</button>
</template>
<script>
import { reactive, toRef, toRefs } from "vue";
export default {
name: "Demo",
setup() {
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
});
function handlerPrintPerson() {
console.log('person:', JSON.stringify(person))
}
// toRef 适合单个使用
// return {
// name: toRef(person, "name"), // 这样才会让改变影响到 person对象
// age: toRef(person, "age"),
// salary: toRef(person.job.j1, "salary"),
// };
// return toRefs(person);
return {
...toRefs(person),
handlerPrintPerson
}
},
};
</script>
<style>
</style>
shallow
- shallowReactive 只监控对象第一层数据响应式
- shallowRef 只对引用本身进行响应式处理,但不会深入到对象或数组的内部属性。也就是说,如果你使用 shallowRef 包装一个对象或数组,那么只有当这个对象或数组的引用发生变化时,才会触发依赖这个 shallowRef 的组件的更新。但是,对象或数组内部属性的变化不会触发更新。
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo />
</template>
<script>
import Demo from "./components/Demo.vue";
export default {
name: "App",
components: { Demo },
};
</script>
<template>
<h2>sum:{{x.y}}</h2>
<button @click="x.y++">点我+1</button>
<hr>
<h2>a: {{a}}</h2>
<button @click="a='你好呀'">点我修改a</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<!-- <h2>薪资:{{ salary }}k</h2> -->
<h2>薪资:{{ job.j1.salary }}k</h2>
<button @click="name = 'xx'">修改姓名</button>
<button @click="age++">修改年龄</button>
<!-- <button @click="salary++">涨薪</button> -->
<!-- toRefs 展开完必须带上 -->
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import { reactive, ref, shallowReactive, shallowRef, toRef, toRefs } from "vue";
export default {
name: "Demo",
setup() {
// let person = reactive({
// shallowReactive 只监控对象第一层数据响应式
let person = shallowReactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
});
// let x = ref({
// ref 可以把对象 转为 proxy 对象, 但 shallowRef 只能转换基本类型
let x = shallowRef({
y: 0
})
// shallowRef 如果是对象,内部属性不响应,但整体是响应式
let a = shallowRef({
msg: 'hi'
})
return {
x,
a,
...toRefs(person)
}
},
};
</script>
<style>
</style>
readonly 和 shallowReadonly
readonly 对象不能修改
shallowReadonly 对象第一层不能修改
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo />
</template>
<script>
import Demo from "./components/Demo.vue";
export default {
name: "App",
components: { Demo },
};
</script>
<template>
<h2>sum:{{sum}}</h2>
<button @click="sum++">点我+1</button>
<hr>
<h2>a: {{a}}</h2>
<button @click="a='你好呀'">点我修改a</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<!-- <h2>薪资:{{ salary }}k</h2> -->
<h2>薪资:{{ job.j1.salary }}k</h2>
<button @click="name = 'xx'">修改姓名</button>
<button @click="age++">修改年龄</button>
<!-- <button @click="salary++">涨薪</button> -->
<!-- toRefs 展开完必须带上 -->
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import { reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRef, toRefs } from "vue";
export default {
name: "Demo",
setup() {
let sum = ref(0)
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
});
// person只读,不允许修改
// person = readonly(person)
// 第一层不能修改
person = shallowReadonly(person)
return {
sum,
...toRefs(person)
}
},
};
</script>
<style>
</style>
toRaw 和 markRaw
toRaw 返回原始对象
markRaw 不让对象被代理,即对象不被响应式
main.js
App.vue
Demo.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<Demo />
</template>
<script>
import Demo from "./components/Demo.vue";
export default {
name: "App",
components: { Demo },
};
</script>
<template>
<h2>sum:{{sum}}</h2>
<button @click="sum++">点我+1</button>
<hr>
<h2>a: {{a}}</h2>
<button @click="a='你好呀'">点我修改a</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<!-- <h2>薪资:{{ salary }}k</h2> -->
<h2>薪资:{{ job.j1.salary }}k</h2>
<h2>座驾信息:{{car}}</h2>
<button @click="name = 'xx'">修改姓名</button>
<button @click="age++">修改年龄</button>
<!-- <button @click="salary++">涨薪</button> -->
<!-- toRefs 展开完必须带上 -->
<button @click="job.j1.salary++">涨薪</button>
<button @click="showOrginPerson">输出最原始的person</button>
<button @click="addCar">添加座驾</button>
<button @click="person.car.name+='!'">换车名</button>
<button @click="person.car.price+='w'">换价格</button>
</template>
<script>
import { markRaw, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs } from "vue";
export default {
name: "Demo",
setup() {
let sum = ref(0)
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
car: {}
});
return {
sum,
person,
...toRefs(person),
showOrginPerson(){
// toRaw 只能处理 reactive 定义的对象
console.log('orgin person:', toRaw(person));
},
addCar(){
// person.car = {name:'奔驰', price: '20w'}
// 如果想添加的数据不是响应式数据,使用markRaw添加
person.car = markRaw({name:'奔驰', price: '20w'})
}
}
},
};
</script>
<style>
</style>
自定义ref
main.js
App.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<input type="text" v-model="keyWord" />
<h3>keyWord is: {{ keyWord }}</h3>
<hr />
<input type="text" v-model="keyWord2" />
<h3>keyWord is: {{ keyWord2 }}</h3>
</template>
<script>
import { customRef, ref } from "@vue/reactivity";
export default {
name: "App",
setup() {
// 自定义Ref
function myRef(value) {
return customRef((track, trigger) => {
return {
get() {
track() // 通知 vue 追踪数据变化
return value;
},
set(newValue) {
value = newValue;
trigger() // 通知vue 重新解析模板
},
};
});
}
return {
keyWord: ref("hello"), //使用vue 内置ref
keyWord2: myRef("hello")
};
},
};
</script>
provide 和 inject 数据传递
provide 和 inject 在 Vue.js 中主要用于组件间通信,特别是当需要跨越多个组件层级进行数据传递时。它们提供了一种无需通过中间组件手动传递 props 的方式,可以将数据从一个祖先组件传递到其所有后代组件中,无论这些后代组件有多深。
main.js
App.vue
Child.vue
Gradson.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<div class="app">
<h3>我是App组件 car: {{name}} --- {{price}} </h3>
<Child />
</div>
</template>
<script>
import { reactive, toRefs } from '@vue/reactivity'
import Child from './components/Child.vue'
import { provide } from '@vue/runtime-core'
export default {
name: 'App',
components: {Child},
setup() {
let car = reactive({
name: '奔驰',
price: '40w'
})
provide('car', car)
return {
...toRefs(car)
}
}
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
<template>
<div class="child">
<h3>我是Child组件</h3>
<Gradson />
</div>
</template>
<script>
import Gradson from './Gradson.vue'
export default {
name: 'Child',
components: {Gradson}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
<template>
<div class="gradson">
<h3>我是Gradson组件, 收到car: {{car.name}} --- {{car.price}} </h3>
</div>
</template>
<script>
import { inject } from '@vue/runtime-core'
export default {
name: 'Gradson',
setup() {
return {
car: inject('car')
}
}
}
</script>
<style>
.gradson{
background-color: orange;
padding: 10px;
}
</style>
响应式数据判断
main.js
App.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<div class="app">
<h3>我是App组件 car: {{name}} --- {{price}} </h3>
</div>
</template>
<script>
import { isProxy, isReactive, isReadonly, reactive, readonly, ref, toRefs } from '@vue/reactivity'
import { provide } from '@vue/runtime-core'
export default {
name: 'App',
setup() {
let car = reactive({
name: '奔驰',
price: '40w'
})
let sum = ref(0)
let car2 = readonly(car)
console.log(isRef(car));
console.log(isReactive(car));
console.log(isReadonly(car));
console.log(isProxy(car));
console.log(isProxy(car2));
return {
...toRefs(car)
}
}
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
无根标签
vue中不用写根标签了
<template>
<!-- 无需根标签,因为底层外部渲染了 fragment 标签,打包完后又被去除 -->
<h3>hello1</h3>
<h3>hello2</h3>
<h3>hello3</h3>
</template>
原始弹窗
main.js
App.vue
Child.vue
Gradson.vue
Dialog.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<div class="app">
<h3>我是App组件 </h3>
<Child />
</div>
</template>
<script>
import { reactive, toRefs } from '@vue/reactivity'
import Child from './components/Child.vue'
import { provide } from '@vue/runtime-core'
export default {
name: 'App',
components: {Child},
setup() {
let car = reactive({
name: '奔驰',
price: '40w'
})
provide('car', car)
return {
...toRefs(car)
}
}
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
<template>
<div class="child">
<h3>我是Child组件</h3>
<Gradson />
</div>
</template>
<script>
import Gradson from './Gradson.vue'
export default {
name: 'Child',
components: {Gradson}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
<template>
<div class="gradson">
<h3>我是Gradson组件,</h3>
<Dialog />
</div>
</template>
<script>
import Dialog from './Dialog.vue'
export default {
name: 'Gradson',
components: {Dialog}
}
</script>
<style>
.gradson{
background-color: orange;
padding: 10px;
}
</style>
<template>
<div>
<button @click="isShow=true">点我弹窗</button>
<div v-if="isShow" class="dialog">
<h3>我是弹窗</h3>
<h4>xxxxx</h4>
<h4>xxxxx</h4>
<h4>xxxxx</h4>
<h4>xxxxx</h4>
<button @click="isShow=false">关闭弹窗</button>
</div>
</div>
</template>
<script>
import { ref } from '@vue/reactivity'
export default {
name: 'Dialog',
setup() {
let isShow = ref(false)
return{
isShow
}
}
}
</script>
<style>
.dialog{
height: 300px;
width: 300px;
background-color: green;
}
</style>
teleport
Teleport
是 Vue 3 引入的一个内置组件,它的主要作用是将一个组件的渲染结果“传送”到 DOM 树的其他位置,而不仅仅是它在组件树中的位置。这在很多场景下非常有用,尤其是当需要将模态框、弹出菜单、提示框或其他 UI 元素放置在文档的特定位置时。
main.js
App.vue
Child.vue
Gradson.vue
Dialog.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<div class="app">
<h3>我是App组件 </h3>
<Child />
</div>
</template>
<script>
import { reactive, toRefs } from '@vue/reactivity'
import Child from './components/Child.vue'
import { provide } from '@vue/runtime-core'
export default {
name: 'App',
components: {Child},
setup() {
let car = reactive({
name: '奔驰',
price: '40w'
})
provide('car', car)
return {
...toRefs(car)
}
}
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
<template>
<div class="child">
<h3>我是Child组件</h3>
<Gradson />
</div>
</template>
<script>
import Gradson from './Gradson.vue'
export default {
name: 'Child',
components: {Gradson}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
<template>
<div class="gradson">
<h3>我是Gradson组件,</h3>
<Dialog />
</div>
</template>
<script>
import Dialog from './Dialog.vue'
export default {
name: 'Gradson',
components: {Dialog}
}
</script>
<style>
.gradson{
background-color: orange;
padding: 10px;
}
</style>
<template>
<div>
<button @click="isShow = true">点我弹窗</button>
<!-- 将弹窗传入body中 -->
<teleport to="body">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是弹窗</h3>
<h4>xxxxx</h4>
<h4>xxxxx</h4>
<h4>xxxxx</h4>
<h4>xxxxx</h4>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import { ref } from "@vue/reactivity";
export default {
name: "Dialog",
setup() {
let isShow = ref(false);
return {
isShow,
};
},
};
</script>
<style>
.mask{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.dialog {
position:absolute;
top: 50%;
left: 50%;
/* transform 中的 translate 是参考自身 */
transform: translate(-50%, -50%);
text-align: center;
height: 300px;
width: 300px;
background-color: green;
}
</style>
异步加载 和 suspense
main.js
App.vue
Child.vue
Child2.vue
Child3.vue
import { createApp } from 'vue'
import App from './App.vue'
// createApp 的更轻量了
createApp(App).mount('#app')
<template>
<div class="app">
<h3>我是App组件</h3>
<!-- 如果不引入Suspense 且为异步引入,当App 组件先出来用户可能接收不到提示 -->
<Suspense >
<template v-slot:default>
<Child />
</template>
<template v-slot:fallback>
<h3>稍等。。。</h3>
</template>
</Suspense>
<hr />
<Suspense >
<template v-slot:default>
<Child2 />
</template>
<template v-slot:fallback>
<h3>稍等2。。。</h3>
</template>
</Suspense>
<hr />
<Suspense >
<template v-slot:default>
<Child3 />
</template>
<template v-slot:fallback>
<h3>稍等3。。。</h3>
</template>
</Suspense>
<h4>end....</h4>
</div>
</template>
<script>
import { defineAsyncComponent } from "vue";
// 动态引入 网速如果慢 app先出来 child 后出来
const Child = defineAsyncComponent(() => import("./components/Child.vue"));
const Child2 = defineAsyncComponent(() => import("./components/Child2.vue"));
const Child3 = defineAsyncComponent(() => import("./components/Child3.vue"));
// 静态引入
// import Child from './components/Child.vue'
export default {
name: "App",
components: { Child, Child2, Child3 },
};
</script>
<style>
.app {
background-color: gray;
padding: 10px;
}
</style>
<template>
<div class="child">
<h3>我是Child组件</h3>
</div>
</template>
<script>
export default {
name: 'Child',
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
<template>
<div class="child2">
<h3>我是Child2组件</h3>
<h4>sum:{{sum}}</h4>
</div>
</template>
<script>
import { ref } from '@vue/reactivity'
export default {
name: 'Child2',
setup() {
let sum = ref(0)
// 只能是使用了 suspense 和 异步引入才能返回 promise 示例对象, 与 child3 等价
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve({sum})
}, 3000)
})
// return {sum}
}
}
</script>
<style>
.child2{
background-color: orange;
padding: 10px;
}
</style>
<template>
<div class="child3">
<h3>我是Child3组件</h3>
<h4>sum:{{sum}}</h4>
</div>
</template>
<script>
import { ref } from '@vue/reactivity'
export default {
name: 'Child3',
async setup() {
let sum = ref(0)
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve({sum})
}, 3000)
})
// 只能是使用了 suspense 和 异步引入才能返回 promise 示例对象, 与child3 等价
return await p
}
}
</script>
<style>
.child3{
background-color: blue;
padding: 10px;
}
</style>