theme: fancy
highlight: a11y-light
mvvm设计模式,m ->model数据,v->view视图,Vm数据视图连接层,组件之间的数据都是单向流动的,子组件不能直接修改传递过来的值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
//初始化vue
template: '<div>{
{content}}</div>',
data() {
return {
content: 1,
}
},
mounted() {
setInterval(() => {
this.$data.content++// this.$data.content=this.content
}, 500)
},
})
const vm = app.mount('#root')//挂载到root上,vm让数据视图相关联
</script>
</html>
生命周期
// 生命周期函数:在某一时刻会自动执行的函数
const app = Vue.createApp({
data() {
return {
message: 'hello world'
}
},
// 在实例生成之前会自动执行的函数
beforeCreate() {
console.log('beforeCreate')
},
// 在实例生成之后会自动执行的函数
created() {
console.log('created')
},
// 在组件内容被渲染到页面之前自动执行的函数
beforeMount() {
console.log(document.getElementById('root').innerHTML, 'beforeMount')
},
// 在组件内容被渲染到页面之后自动执行的函数
mounted() {
console.log(document.getElementById('root').innerHTML, 'mounted')
},
// 当数据发生变化时会立即自动执行的函数
beforeUpdate() {
console.log(document.getElementById('root').innerHTML, 'beforeUpdate');//输出未更改的数据
},
// 当数据发生变化,页面重新渲染后,会自动执行的函数
updated() {
console.log(document.getElementById('root').innerHTML, 'updated');
},
// 当 Vue 应用失效时,自动执行的函数
beforeUnmount() {
console.log(document.getElementById('root').innerHTML, 'beforeUnmount');
},//输出dom结构
// 当 Vue 应用失效时,且 dom 完全销毁之后,自动执行的函数
unmounted() {
console.log(document.getElementById('root').innerHTML, 'unmounted');
},//没有内容
});
const vm = app.mount('#root');
v-html v-once 插值表达式 阻止默认行为@click.prevent .self子元素不会触发父元素事件 .stop停止冒泡
message:'<strong>hello</strong>'
v-html="message"
双括号里只能放js表达式,不能放语句,也可以调用函数
与计算属性相比可以实现异步
watch:{
price(current,prev){
setTimeout(()=>{
console.log(123)},1000)
}
},
样式绑定语法
<div>
<demo :class="classstr"></demo></div>
//给父组件绑定一个样式,如果里面有两个子元素样式不会生效,一个才会生效,因为不知道赋值给谁。或者使用$attrs.class接收
app.component('demo', {
template: '<div :class="$attrs.class">123</div><div>456</div>',
})
修饰符 v-model.lazy不改变输入框数据失去焦点再改变 .trim .number转数字
精确修饰符表示不能同时按除这个键之外的键 .enter.exact
局部组件
//先声明组件 首字母大写,驼峰命名表示局部组件
const Demo={
template: '<div>123</div><div>456</div>',
}
//再到组件中声明 components:{demo}= components:{demo:demo},就可以使用了
父子组件传值,通过props
const demo = {
props: ['number'],
template: '<div>{
{number}}</div>',
}
const app = Vue.createApp({
data() {
return {
num: 123,
}
},
components: {
demo },
template: '<div ><demo :number=num></demo></div>',
})
const vm = app.mount('#root')
//如果要对传入参数做限制props写出对象形式。
props: {
number: {
type: Number,
// required:true,
//default:234
default: function () {
return 234
},
validator: function (value) {
return value < 1000
},
},
},
如果父组件传值太多,可以写进一个对象里v-bind="params",props再一个一个接收
const demo = {
props: ['a', 'b', 'c'],
template: '<div>{
{a}}</div><div>{
{b}}</div><div>{
{c}}</div>',
}
const app = Vue.createApp({
data() {
return {
params: {
a: 1, b: 2, c: 3 },
}
},
components: {
demo },
template: '<div ><demo v-bind="params"></demo></div>',
})
const vm = app.mount('#root')
属性传的时候,使用content-abc这种命名,props接的时候,使用contentAbc接收
Non-props
父组件传值子组件不用props接收例如
<demo msg=content></demo>
//传入的属性会当成div的属性,<div mag=content></div>
//不想要这个属性,在子组件里加一个inheritAttrs:false
//用来设置style,class
//如果子组件有多个根元素,v-bind="$attrs"把父元素传入的所有Non-props属性都加在这个元素上,如果只要某一个:msg="$attrs.msg"
子组件修改父组件传入的值通过emit
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const demo = {
props: ['count'],
//emits:['addOne'],
emits: {
addOne: (count1, count2) => {
if (count1 > 0 && count2 > 0) {
return true
}
return false
},
}, //对象形式会检验子组件向父组件传的参数
template: '<div @click="handleClick">{
{count}}</div>',
methods: {
handleClick() {
this.$emit('addOne', 1, -2) //驼峰命名,这里还可以传参,或者直接在这里算出count再返回count
},
},
}
const app = Vue.createApp({
data() {
return {
count: 1,
}
},
components: {
demo },
template:
'<div ><demo @add-one="handleAddOne" :count="count"></demo></div>', //横杆命名
methods: {
handleAddOne(a, b) {
//直接在这里使用参数
this.count = this.count + a + b
},
},
})
const vm = app.mount('#root')
</script>
</html>
父子组件v-model
可以看到我们通过props,emit可以接收修改父组件传递的值,这就类似于v-model,通过v-model来实现父子组件的双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
template: '<counter v-model="count"></counter>',
data() {
return {
count: 1,
}
},
})
app.component('counter', {
props: ['modelValue'], //规定这么写
methods: {
handleClick() {
this.$emit('update:modelValue', this.modelValue + 3) //规定这么写update:modelValue意味着更新modelValue
},
},
template: `<div @click="handleClick">{
{modelValue}}</div>`,
})
const vm = app.mount('#root')
</script>
</html>
//如果不想用规定的名字,v-model:app=count,props:['app'],'update:app'
//通过自己定义的名字可以v-model多个数据,v-model:名字
自定义v-model修饰符(转大写字母)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
template: '<counter v-model.uppercase="word"></counter>',
data() {
return {
word: 'a',
}
},
})
app.component('counter', {
props: {
modelValue: String,
modelModifiers: {
default: () => ({
}), //不传默认返回空对象
},
},
methods: {
handleClick() {
let newVal = this.modelValue + 'b'
if (this.modelModifiers.uppercase) {
newVal = newVal.toUpperCase()
}
this.$emit('update:modelValue', newVal) //规定这么写update:modelValue
},
},
template: `<div @click="handleClick">{
{modelValue}}</div>`,
})
const vm = app.mount('#root')
</script>
</html>
插槽
插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的,即父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const demo = {
data() {
return {
count: 2,
}
},
props: ['count'],
template: '<div><input /><slot></slot></div>',//父组件在子组件里面写的dom,会被替换到slot的位置,如果这个传入的dom使用了变量,虽然他被替换到slot的位置,但还是使用父组件的数据。
}
const app = Vue.createApp({
data() {
return {
count: 1,
}
},
components: {
demo },
template:
'<demo><button>{
{count}}</button></demo><demo><div>{
{count}}</div></demo>', //横杆命名
})
const vm = app.mount('#root')
</script>
</html>
具名插槽
将传入子组件的dom进一步拆分,以方便在子组件不同位置进行使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
}
},
template: `<demo1><template v-slot:head><div>head</div></template><template #footer><div>footer</div></template></demo1>`,//简写#footer
})
app.component('demo1', {
template: `<slot name="head"></slot><div>content</div><slot name="footer"></slot>`,
})
const vm = app.mount('#root')
</script>
</html>
作用域插槽
子传父
<!-- <MyComponent> 的模板 -->
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>
<MyComponent v-slot="slotProps">
{
{
slotProps.text }} {
{
slotProps.count }}
</MyComponent>
//当然更推荐直接结解构出来v-slot="{ text, count }"
多层组件传值
传统方法需要一层一层传值非常麻烦
//在父组件data同层,声明
provide:{
count:1,
}
//如果想传data里的值,需要把provide写出一个函数
provide(){
return{
msg:'hi',
count:this.count,
getInfo(data){
console.log(data)}
}
},//如果后续父组件count改变,子组件不会感知。
//在子组件inject就可以使用传来的值
inject:['msg','count','getInfo']
动态组件异步组件
动态组件
同一个位置按照要求显示不同组件 keep-alive可以缓存
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const demo1 = {
data() {
return {
}
},
template: '<input />',
}
const demo2 = {
data() {
return {
}
},
template: '<div>123</div>',
}
const app = Vue.createApp({
data() {
return {
current: 'demo1',
}
},
components: {
demo1, demo2 },
methods: {
change() {
this.current === 'demo1'
? (this.current = 'demo2')
: (this.current = 'demo1')
},
},
template:
'<keep-alive><component :is="current"></component></keep-alive><button @click="change">切换</button>',
})
const vm = app.mount('#root')
</script>
</html>
异步组件
需要的时候再展示的组件
components:{
demo:()=>{
'../demo'}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
data() {
},
template: '<div>123</div><demo1></demo1>',
})
app.component(
'demo1',
Vue.defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
template: `<div>这是异步组件</div>`,
})
}, 3000)
})
})
)
const vm = app.mount('#root')
</script>
</html>
transition
appear第一次展示就带动画
加一个.v-move{transition all .5s ease}
mixin
子组件不能使用父组件引入的mixin,可以定义全局的mixin,
修改混入的自定义属性高于组件的自定义属性优先级
在data,methods同级声明的是自定义属性,调用方法this.\$options.number
替代方案使用组合式api
自定义指令操作dom
全局指令 使用v-focus
局部指令,要在组件中声明directives:directives
指令接收参数
对于v-pos:abc="top" binding.arg可以拿到abc,binding.value可以拿到top变量
当这个200是动态变化的,页面并不会改变,因为没有再触发mounted
加一个updated
upadted(el,binding){
el.style.top=(binding.value+'px')
}
简写:指令中如果只有mounted,updated且内容都一样,可以简写为
app.directive('pos',(el,binding)=>{
el.style.top=(binding.value+'px')
})
Teleport 传送门
Teleport
是一种能够将我们的组件html结构移动到指定位置的技术
将mask从组件中传送到html标签下,传送给body就好了,这样mask就可以相对于全局进行绝对定位了,不必管父元素的定位
<teleport to="#hello">
<div class="mask" v-show="show">{
{
message}}</div>
</teleport>
插件
主要是用于增强功能,可以把他看作是一个工具库,可以提供很多强大的功能,比如一些强大的自定义指令,一些强大的工具方法,过滤器等。app.use来使用option是传入的参数,app是vue的实例
this.\$sayHello
数据检验插件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
template: '<div></div>',
data() {
return {
name: 'zh',
age:12
}
},
rules:
{
name: {
validate: (item) => item.length > 3,
message: '输入的长度应该大于3',
},
age: {
validate: (item) => item < 18,
message: '你已经不是小孩了',
},
},
})
const vaildatorPlugin = (app, options) => {
app.mixin({
created() {
for (let key in this.$options.rules) {
const item = this.$options.rules[key]
this.$watch(key, (value) => {
const result = item.validate(value)
if (!result) {
console.log(item.message)
}
})
}
},
})
}
app.use(vaildatorPlugin)
const vm = app.mount('#root')
</script>
</html>
ref reactive toRefs toRef
原理,通过proxy对数据进行封装,当数据变化是,触发模板等内容的更新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
setup(props) {
//'zhang'变成proxy({value:'zhang'})这样的一个响应式引用
const {
ref,reactive } = Vue
let name = ref('zhang')
let collection = reactive([1,2,3])
setTimeout(() => {
name.value = 'fang'
collection[0]=999
}, 2000)
return {
name,collection
}
},
template: '<div>{
{name}}</div><div>{
{collection[0]}}</div>',
})
const vm = app.mount('#root')
</script>
</html>
toRefs可以让解构出来的数据也变成响应式的,toRef用于原数据里不存在的元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style></style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
setup(props) {
//'zhang'变成proxy({value:'zhang'})这样的一个响应式引用
const {
reactive, toRefs } = Vue
let collection = reactive({
name: 'zhang' })
//toRefs proxy({name:'zhang'}),{name:proxy({value:'zhang'})}
let {
name } = toRefs(collection)
setTimeout(() => {
collection.name = 'fang'
}, 2000)
return {
name,
}
},
template: '<div>{
{name}}</div>',
})
const vm = app.mount('#root')
</script>
</html>
路由
beforeEnter 进入这个路由时会触发。
path:'/login',
name:'Login',
component:Login,
beforeEnter: (to, from, next) => {
const {
isLogin}=localStorage
isLogin?next({
name:'Home'}):next()
}
beforEach每次切换路由都会执行
//登陆注册不需要登陆
router.beforeEach((to,from,next)=>{
const {
isLogin}=localStorage
(isLogin||(to.name!=="login"&&to.name!=="register"))?next():next({
name:'Login'})
})
动态路由
路由元信息
路由表
meta:{
auth:true}
this.$route.meta.auth可以拿到
路由传参
//通过query
<router-link to="/about?username=zhang" />
<router-link :to="{name:'about',query:{username:'zhang'}}" />
this.route.query可以拿到对象
//通过params隐式
<router-link :to="{name:'about',params:{username:'zhang'}}" />
this.route.params可以拿到对象,刷新拿不到值.
//通过params显示
<router-link to="/about/123" />
path:about/:id
this.route.params.id
Vue原理
Object.definrProperty的基本使用
const data={
}
let name='zhang'
Object.defineProperty(data,"name",{
get:function(){
console.log('get')
return name
},
set:function(newVal){
console.log('set')
name=newVal
}
})
console.log(data.name)
data.name='fang'
console.log(data.name)
深度监听对象,数组
- 深度监听,需要递归到底,一次性计算量大
- 无法监听新增属性/删除属性( Vue.set Vue.delete )
- 无法原生监听数组,需要特殊处理
const data = {
name: 'zhang',
age: 22,
info: {
address: '比奇堡',
},
list: [1, 2, 3],
}
//重新定义数组原型,不能再原型上更改,会污染数组原型
const oldArrayProperty = Array.prototype
//创建新对象,原型指向oldArrayProperty,再扩展新的方法不会影响原型
//Object.create方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach((methodName) => {
arrProto[methodName] = function () {
updateView() //触发视图更新
oldArrayProperty[methodName].call(this, ...arguments)
//等于Array.prototype.push.call(this, .. .arguments)
}
})
function updateView() {
console.log('视图更新')
}
function observer(target) {
//不是对象或数组
if (typeof target !== 'object' || typeof target === null) {
return target
}
//新原型
if (Array.isArray(target)) {
//target的原型指向以数组原型为原型的对象arrProto
target.__proto__ = arrProto
}
//重新定义各个属性(forin也可以遍历数组)
for (const key in target) {
defineReactive(target, key, target[key])
}
}
//重新定义属性监听起来
function defineReactive(target, key, value) {
//深度监听对象
observer(value)
//核心API
Object.defineProperty(target, key, {
get: function () {
return value
},
set: function (newVal) {
//设置新值
//value一直在闭包里,此处设置完,再get时也会获取最新值
if (newVal !== value) {
//要对新值进行监听,如果data.name={num:100},后面修改data.name.num=200不会监听到
observer(newVal)
value = newVal
//更新视图
updateView()
}
},
})
}
//一进入页面就监听data
observer(data)
data.name = 'fang'
data.age = '18'
data.info.address = '羊村'
data.list.push(4)
vDom即虚拟Dom
react首创,js运行速度比操作Dom不是一个量级,将html结构用js数据结构模拟出来,计算出最小变更操作Dom
<div id="div1" class="container">
<p>vdom</p>
<ul style="font-size: 20px;">
<li>a</li></ul>
</div>
//load编译,h是createElement的别称
h('div', {
attrs: {
id: 'div1'
},
class: 'container'
}, [
h('p', 'vdom'),
h('ul', {
style: {
fontSize: '20px'
}
}, [
h('li', 'a')
])
])
======================================>>>>>>执行h函数获得虚拟DOM
//可能react,vue具体划分不同,但是大致是按照这样划分的
{
tag:'div',//或者是选择器名字
props:{
//标签上的属性 对象
id:'div1',
className:'container'
},
children:[//子元素 数组
{
tag:'p',
children:'vdom',
},{
tag:'ul',
props:{
style:'font-size:20px'},
children:[
{
tag:'li',
children:'a'
}
]
}
]
}
//render函数将虚拟dom转化为真实dom
diff算法
pathVnode用来比较两个vdom,如果他们的key和tag相同,就执行updateChildren,新节点有children进行addVnodes,没有进行removeVnodes
模板编译
- 模板编译为render 函数,执行render函数返回vnode
- 使用webpack vue-loader ,会在开发环境下编译模板(重要)
vue组件可以用render代替template
渲染更新流程
vue路由模式
history,后端只需要返回一个主文件地址
beforedestroy使用场景
自定义DOM事件是通过addEventListener生成的
nexttick
异步渲染无法,无法在data发生变化之后拿到DOM的变化
proxy
proxy代理,reflect反射一一对应,名字参数也一致,正在逐渐替换object上的工具函数
当我们期望监听代理对象的 getter 和 setter 时,不应该使用 target[key],因为它在某些时刻下是不可靠的。receiver是代理之后的对象。而应该使用 Reflect ,借助它的 get 和 set 方法,使用receiver (proxy 实例) 作为 this ,达到期望的结果
// const data = {
// name: 'zhang',
// age: 22,
// }
const data = [1, 2, 3]
//返回一个已经被监听的对象
const proxyData = new Proxy(data, {
get(target, key, receiver) {
//只处理非原型(自身)的属性
const ownKeys=Reflect.ownKeys(target)
if(ownKeys.includes(key)){
console.log('get', key)
}
//下面不管是不是原型上的属性都要执行,要不然用不了原型上的方法
const result = Reflect.get(target, key, receiver)
return result //返回结果
},
set(target, key, val, receiver) {
//重复的数据不处理
if(val===target[key])
{
return true
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
return result //是否设置成功
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete', key)
return result //是否删除成功
},
})
//proxyData.age=100
//console.log(proxyData.age)
//delete proxyData.name
proxyData.push('4')
//get push 没必要劫持,原型的属性不处理
// get length
// set 3 4 length已经变成4
// set length 4 没必要再设置一遍
proxy实现响应式
const data = {
name: 'zhang',
age: 22,
info:{
address:'nanjing'},
list:[1,2,3]
}
//返回响应式数据
const proxyData=reactive(data)
function reactive(target={
}){
if(typeof target!=='object'||target==null){
return target
}
//配置代理
const proxyConf = {
get(target, key, receiver) {
//只处理非原型(自身)的属性
const ownKeys=Reflect.ownKeys(target)
if(ownKeys.includes(key)){
console.log('get', key)
}
//下面不管是不是原型上的属性都要执行,要不然用不了原型上的方法
const result = Reflect.get(target, key, receiver)
//深度监听
//性能优化,什么时候get才深度监听,惰性的
return reactive(result) //返回结果
},
set(target, key, val, receiver) {
//重复的数据不处理
if(val===target[key])
{
return true
}
const ownKeys=Reflect.ownKeys(target)
//监听是否是新增属性
if(ownKeys.includes(key)){
console.log('已有的key', key)
}else{
console.log('新增的key', key)
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
return result //是否设置成功
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete', key)
return result //是否删除成功
},
}
const observed=new Proxy(target,proxyConf)
return observed
}
proxyData.age=10000
proxyData.nickname='dong'
proxyData.list.push(4)
console.log(proxyData.list)
为什么data是一个函数
因为一个.vue文件编译出来是一个class,在不同地方使用相当于实例化类,如果不是函数数据会共享
父子组件生命周期调用
create从外到内,渲染mount从内到外。beforemounted mounted之间需要调用子组件,先执行完子组件(深度优先)
自己实现v-model
https://blog.csdn.net/qq_42753705/article/details/122960165
自定义事件
不同于click,keyup
https://blog.csdn.net/qq_40616529/article/details/93652453
suspense 处理异步组件
<Suspense>
<template #default>
<!--异步组件,当setup方法中的异步方法执行完后显示-->
</ template>
<template #fallback>
<!--默认加载的内容,比如Loading提示或者图标-->
</ template>
</Suspense>