基础

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>

results matching ""

    No results matching ""