前端Vue3

90 阅读7分钟

Vue3 vs Vue2

一、速度更快 1.5-2倍

1、编译速度 2、运行速度

3、计算(效率)速度 ** diff算法->静态提升**

二、体积减少

tree-shaking webpack插件

vue2 130.01k vue3 113.37k

三、用Vue创建项目体验一下。

①首先创建淘宝镜像。

npm config set registry http://registry.npm.taobao.org/

②查看自己的镜像是否有问题命令:

npm config get registry

③安装脚手架命令。

cnpm install -g @vue/cli

④查看脚手架版本命令:

vue -V

⑤创建vue项目体验Vue3.

在自己电脑上找到方代码的地方。【用 vue create vue3-app】

下一步:

下一步

下一步

安装完就可以用开发工具打开。

⑥运行项目。

npm run serve

看到上述页面代表项目安装启动成功。

四、对比Vue3和Vue2 看看有哪些不同。

①首先入口初始化不相同。

②生命周期不相同。

与 2.x 版本生命周期相对应的组合式 API beforeCreate -> 使用 setup() created -> 使用 setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted errorCaptured -> onErrorCaptured onRenderTracked 检查依赖被追踪。当render函数被调用时,会检查哪个响应式数据被收集依赖 onRenderTriggered 当执行update操作时,会检查哪个响应式数据导致组件重新渲染。

案例一:

<template>
  <div class="home">
<!--    <img alt="Vue logo" src="../assets/logo.png">-->
<!--    <HelloWorld msg="Welcome to Your Vue.js App"/>-->
  </div>
</template><script>
// @ is an alias to /src
/*import HelloWorld from '@/components/HelloWorld.vue'*/
import {
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured,
  onRenderTracked,
  onRenderTriggered
} from 'vue'
export default {
  name: 'HomeView',
  components: {
   /* HelloWorld*/
  },
  setup(){
    onBeforeMount(()=>{
      console.log("这个onBeforeMount方法实行了")
    })
    onMounted(()=>{
      console.log("onMounted方法实行了")
    })
    onBeforeUpdate(()=>{
      console.log("onBeforeUpdate方法实行了")
    })
    onUpdated(()=>{
      console.log("onUpdated方法实行了")
    })
    onBeforeUnmount(()=>{
      console.log(" onBeforeUnmount方法实行了")
    })
    onUnmounted(()=>{
      console.log(" onUnmounted方法实行了")
    })
    onErrorCaptured(err =>{
      console.log("err 实行了")
    })
    onRenderTracked(obj =>{
      console.log("obj实行了")
    })
    onRenderTriggered(obj=>{
      console.log("obj onRenderTriggered 实行了")
    })
    console.log('setup 实行了')
  },
​
}
</script>

总结:

在vue2中需要定义数据在data()方法中,而vue3中直接在setup()中定义,通过return {} 返回。

②vue2中在方法中需要this 调用,而vue3中不需用this,就可以直接调用。

案例二:

<template>
  <div class="home">
    <p>
      {{num}}
    </p>
    <span @click="add">加1</span>
    <hr>
    <span> {{name}}</span>
<!--    <img alt="Vue logo" src="../assets/logo.png">-->
<!--    <HelloWorld msg="Welcome to Your Vue.js App"/>-->
  </div>
</template><script>
// @ is an alias to /src
/*import HelloWorld from '@/components/HelloWorld.vue'*/
import {  
  onRenderTracked,
  onRenderTriggered,
    ref
} from 'vue'
export default {
  name: 'HomeView',
  components: {
   /* HelloWorld*/
  },
  setup(){
    let  name = '你好,张三'
    let num = ref(1)
    //追踪页面依赖的响应式数据
     onRenderTracked(obj =>{
      console.log("obj实行了")
    })
      //追踪引发页面重新渲染的响应式数据
    onRenderTriggered(obj=>{
      console.log("obj onRenderTriggered 实行了")
    })
​
    let add =()=>{
      //console.log('this=',this)
      //console.log('num=',num)
      num.value++
      console.log('num=',num)
    }
    return{
      name,
      add,
      num
    }
  },
​
}
</script>

案例三

<template>
  <div class="home">
    <p>
      {{user.age}}
    </p>
    <span @click="add">加1岁</span>
    <hr>
    <span> {{user.name}}</span>
<!--    <img alt="Vue logo" src="../assets/logo.png">-->
<!--    <HelloWorld msg="Welcome to Your Vue.js App"/>-->
  </div>
</template><script>
// @ is an alias to /src
/*import HelloWorld from '@/components/HelloWorld.vue'*/
import {
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured,
  onRenderTracked,
  onRenderTriggered,
  reactive
} from 'vue'
export default {
  name: 'HomeView',
  components: {
   /* HelloWorld*/
  },
  setup(){
​
   let user = reactive({
     id: 1,
     name: '张三',
     age: 18
​
   })
​
    let add=()=>{
     user.age++
    }
​
    //追踪页面依赖的响应式数据
    onRenderTracked(obj =>{
      console.log("obj实行了")
    })
    //追踪引发页面重新渲染的响应式数据
    onRenderTriggered(obj=>{
      console.log("obj onRenderTriggered 实行了")
    })
​
​
    return{
      user,
      add
    }
  },
​
}
</script>

重点:

reactive与ref

①被响应式api标记过的数据才可以成为响应式数据 ②ref--用来标记简单类型数据 ③reactive—标记复杂类型数据(深度响应式) ④如果用ref对象/数组, 会自动将对象/数组转换为reactive的代理对象 ⑤ref的数据操作: 在js中要.value, 在模板中不需要(内部解析会自动添加.value)

安装:element-plus

参考官方:element-plus.gitee.io/zh-CN/guide…

安装命令:

npm install element-plus --save
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'const app = createApp(App)
​
app.use(ElementPlus)
app.mount('#app')

上述需要导入到main.js中。

案例四:

 <div>
    <el-row class="mb-4">
      <el-button>Default</el-button>
      <el-button type="primary">Primary</el-button>
      <el-button type="success">Success</el-button>
      <el-button type="info">Info</el-button>
      <el-button type="warning">Warning</el-button>
      <el-button type="danger">Danger</el-button>
    </el-row>
  </div>

同时:在vue3中不在是一个根

标签了,可以写多个
标签。

Vue3+TS

创建vue3+TS 项目体验

创建项目命令:

npm init vite

官网:Vite中文网

发现VS code 报红,禁用 vetur 然后在应用商店搜索Vue Language Features 或者搜索 Volar

①安装

②安装

启动项目。

npm run dev

看到上述页面代表Vue3+ TS 运行成功。

模块骨架

<template></template><script lang="ts">
export default{
​
}
</script>
<style lang="less" scoped>
</style>

试试第一个程序:

<template>
  <div>
    {{ name }}
  </div>
</template><script lang="ts">
export default {
  setup() {
    let name: string = '中国宜昌'
​
    return {
      name,
    }
  },
}
</script>
<style lang="less" scoped></style>

记得修改main.ts 文件。

import app from './components/01-app.vue'createApp(app).mount('#app')

ref方法学习处理基本类型。

案例一:

<template>
  <div>
    {{ name }}
    <hr />
    {{ num }}
    <button @click="add">增加</button>
  </div>
</template><script lang="ts">
import { ref } from 'vue'
export default {
  setup() {
    let name: string = '中国宜昌'
    let num = ref(18)
    let add = () => {
      num.value++
    }
​
    return {
      name,
      num,
      add,
    }
  },
}
</script>
<style lang="less" scoped></style>

reactive方法处理复杂类型

案例二:

<template>
  id:{{ user.id }}<br />
  姓名:{{ user.name }}<br />
  年龄:{{ user.age }}<br />
  性别:{{ user.sex }}
</template><script lang="ts">
import { reactive } from 'vue'
export default {
  setup() {
    let user = reactive({
      id: 0,
      name: '张三',
      age: 18,
      sex: '男',
    })
​
    return {
      user,
    }
  },
}
</script>
<style lang="less" scoped></style>

toRefs()函数的相应数据处理

<template>
  <div>
    {{ name }}
    <hr />
    {{ num }}
    <button @click="add">增加</button>
  </div>
</template><script lang="ts">
import { reactive, toRefs } from 'vue'
export default {
  setup() {
    let name: string = '中国宜昌'
    let obj = {
      num: 18,
    }
    let { num } = toRefs(reactive(obj))
    let add = () => {
      num.value++
      console.log(num.value)
    }
​
    return {
      name,
      num,
      add,
    }
  },
}
</script>
<style lang="less" scoped></style>

toRefs()方法的作用就是将 reactive 处理的复杂对象结构成相应是数据。

setup语法糖

案例

<template>
  <div>
    {{ name }}
    <hr />
    {{ num }}
    <button @click="add">增加</button>
  </div>
</template><script lang="ts" setup>
import { reactive, toRefs } from 'vue'
let name: string = '中国宜昌'
let obj = {
  num: 18,
}
let { num } = toRefs(reactive(obj))
let add = () => {
  num.value++
  console.log(num.value)
}
​
</script>
<style lang="less" scoped></style>

在开发中简化了好多,建议后续同学以setup语法糖 方式开发。

setup语法糖 简化了

①没有导出export default {}

②去掉了return 返回,直接以响应式数据返回。

语法糖模块如下:

<template>
  
</template>
<script lang="ts" setup>
    //(1)导入方法或者类、接口
    //(2)写自己的代码
    
</script>
<style lang="less" scoped></style>

watch 用法

<template>
  <p>{{ num }}</p>
  <button @click="num++">加1</button>
</template>
<script lang="ts" setup>
//(1)导入方法或者类、接口
import { ref, watch } from 'vue'
//(2)写自己的代码
let num = ref(20)
//这里需要传递num num 必须是响应式数据
watch(num, (newVal, oldVal) => {
  console.log(newVal, oldVal)
})
</script>
<style lang="less" scoped></style>

监听对象

<template>
  <p>{{ num }}</p>
  <button @click="num++">加1</button>
</template>
<script lang="ts" setup>
//(1)导入方法或者类、接口
import { ref, watch, reactive, toRefs } from 'vue'
//(2)写自己的代码
//let num = ref(20)
//这里需要传递num num 必须是响应式数据
let obj = {
  num: 30,
}
let objRef = reactive(obj)
​
let { num } = toRefs(objRef)
​
watch(num, (newVal, oldVal) => {
  console.log(newVal, oldVal)
})
</script>
<style lang="less" scoped></style>

计算属性用法

案例:

<template>
  {{ numRef }}
  <button @click="num++">加加</button>
  <hr />
  {{ numDb }}
  <button @click="objRef.num++">下面加</button>
</template>
<script lang="ts" setup>
//(1)导入方法或者类、接口
import { ref, computed, reactive } from 'vue'
//(2)写自己的代码
let num = ref(20)
let numRef = computed(() => {
  return num.value * 2
})
let obj = {
  num: 30,
}
let objRef = reactive(obj)
let numDb = computed(() => {
  return objRef.num * 2
})
</script>
<style lang="less" scoped></style>

计算属性computed 组件 Vue3中需要定义变量(let)或者常量来做接收,如果是setup语法糖模式,会自动返回return 。

父传子组件

案例:

父组件

<template>
  <child :num="num"> </child>
</template>
<script lang="ts" setup>
//(1)导入方法或者类、接口
import { ref } from 'vue'
import child from './07-appChild.vue'
//(2)写自己的代码let num = ref(20)
</script>
<style lang="less" scoped></style>

子组件

<template>
  <p>
    {{ num }}
  </p>
</template>
<script lang="ts" setup>
//(1)导入方法或者类、接口
import { defineProps } from 'vue'
​
  //props:{
    //num :num
   // }
//(2)写自己的代码
defineProps({
  num: {
    type: Number,
    default: 80,
  },
})
</script>
<style lang="less" scoped></style>

父传子组件需要用到defineProps 在子组件中接收传递对象。

子传父组件

父组件

<template>
  <child @fn="chanAge" :age="age"> </child>
</template>
<script lang="ts" setup>
//(1)导入方法或者类、接口
import { ref } from 'vue'
import child from './08-appChild.vue'
//(2)写自己的代码
let age = ref(2)
const chanAge = () => {
  age.value++
}
</script>
<style lang="less" scoped></style>

子组件

<template>
  <p>{{ age }}</p>
  <button @click="add">按钮</button>
</template>
<script lang="ts" setup>
//(1)导入方法或者类、接口
import { defineProps, defineEmits } from 'vue'
defineProps({
  age: {
    type: Number,
    default: 22,
  },
})
//子传父需要用defineEmits
const emit = defineEmits<{
  (event: 'fn'): void
}>()
​
const add = () => {
  emit('fn')
}
</script>
<style lang="less" scoped></style>

子传父需要通过导入 defineEmits方法。需要定义常量【const emit】 来接收,在父组件当中应该定义好方法【fn】。

VS code 自定义代码片段:

{
  "dome": {
    "prefix": "v3",
    "body": [
      "<template>",
      "\t",
      "</template>",
      "",
      "<script lang='ts' setup>",
      "import {} from 'vue'",
      "\t$0",
      "</script>",
      " ",
      "<style lang='less' scoped >",
      "\t",
      "</style>"
    ],
​
    "description": "自定义Vue3片段"
  }
}

案例练习 全选/反选

分解题目

①先显示界面

<template>
  <input type="checkbox" />全选/反选
  <ul>
    <li v-for="(item, index) in list" :key="index">
      <input type="checkbox" />{{ item }}
    </li>
  </ul>
</template><script lang="ts" setup>
import { reactive, ref, toRefs, computed } from 'vue'
let data = reactive({
  list: ['篮球', '足球', '乒乓球', '网球'] 
})
let { list } = toRefs(data)
​
</script><style lang="less" scoped></style>

②双向绑定 计算属性

<template>
  <input type="checkbox" v-model="chackAll" />全选/反选
  <ul>
    <li v-for="(item, index) in list" :key="index">
      <input type="checkbox" />{{ item }}
    </li>
  </ul>
</template><script lang="ts" setup>
import { reactive, ref, toRefs, computed } from 'vue'
let data = reactive({
  list: ['篮球', '足球', '乒乓球', '网球'] 
})
let { list } = toRefs(data)
let chackAll = ref(true)
</script><style lang="less" scoped></style>

③能不能全选或者反选

  • 中的元素确定

    <template>
      <input type="checkbox" v-model="chackAll" />全选/反选
      <ul>
        <li v-for="(item, index) in list" :key="index">
          <input type="checkbox" v-model="checkList[index]" />
          {{ item }}
        </li>
      </ul>
    </template><script lang="ts" setup>
    import { reactive, ref, toRefs, computed } from 'vue'
    let data = reactive({
      list: ['篮球', '足球', '乒乓球', '网球'],
      checkList: [false, true, false, false],
    })
    let { list, checkList } = toRefs(data)
    let chackAll = ref(true)
    </script><style lang="less" scoped></style>
    

    ④按照计算属性来处理如下:

    <template>
      <input type="checkbox" v-model="chackAll" />全选/反选
      <ul>
        <li v-for="(item, index) in list" :key="index">
          <input type="checkbox" v-model="checkList[index]" />{{ item }}
        </li>
      </ul>
    </template><script lang="ts" setup>
    import { reactive, ref, toRefs, computed } from 'vue'
    let data = reactive({
      list: ['篮球', '足球', '乒乓球', '网球'],
      checkList: [false, false, false, false],
    })
    let { list, checkList } = toRefs(data)
    let chackAll = computed({
      get() {
        return !data.checkList.includes(false)
      },
      set(newVul: boolean) {
        //console.log(newVul)
        data.checkList = data.checkList.map(() => newVul)
      },
    })
    </script><style lang="less" scoped></style>
    

    07

    <template>
      <input type="checkbox" v-model="checkAll" />全选/反选
      <ul>
        <li v-for="(item, index) in list" :key="index">
          <input type="checkbox" v-model="checkList" />{{ item }}
        </li>
      </ul>
    </template><script lang="ts" setup>
    import { computed, reactive, toRefs } from 'vue'
    let data = reactive({
      list: ['篮球', '足球', '乒乓球', '网球'],
      checkList: [false, false, false, false],
    })
    ​
    let { list, checkList } = toRefs(data)
    //let checkAll = ref(false)
    let checkAll = computed({
      get() {
        return !data.checkList.includes(false)
      },
      set(newVul: boolean) {
        // data.checkList = data.checkList.map(()=> newVul)
        data.checkList = data.checkList.map(() => newVul)
        console.log(newVul)
      },
    })
    </script><style lang="less" scoped></style>
    

    V-mode的传值

    案例一:

    父组件

    <template>
      <child v-model:num="num"></child>
    </template><script lang="ts" setup>
    import { ref } from 'vue'
    import child from './10-appChild.vue'
    let num = ref(100)
    </script><style lang="less" scoped></style>
    

    子组件

    <template>
      <p>
        子组件
        {{ num }}
      </p>
      <button @click="update">修改</button>
    </template><script lang="ts" setup>
    import { defineProps } from 'vue'
    defineProps({
      num: {
        type: Number,
        default: 300,
      },
    })
    const emit = defineEmits<{
      (event: 'update:num', n: number): void
    }>()
    ​
    const update = () => {
      emit('update:num', 111)
    }
    </script><style lang="less" scoped></style>
    

    案例二

    父组件

    <template>
      <child v-model:num="num"></child>
    </template><script lang="ts" setup>
    import { ref } from 'vue'
    import child from './10-appChild.vue'
    let num = ref(100)
    </script><style lang="less" scoped></style>
    

    子组件

    <template>
      <p>
        子组件
        {{ num }}
      </p>
      <button @click="update">修改</button>
    </template><script lang="ts" setup>
    import { defineProps } from 'vue'
    let pos = defineProps({
      num: {
        type: Number,
        default: 300,
      },
    })
    const emit = defineEmits<{
      (event: 'update:num', n: number): void
    }>()
    let n = pos.num
    const update = () => {
      n++
      emit('update:num', n)
    }
    </script><style lang="less" scoped></style>
    

    插槽

    匿名插槽

    父组件:

    <template>
      <child>
        <button>按钮</button>
        <p>这是中国宜昌</p>
      </child>
    </template><script lang="ts" setup>
    import {} from 'vue'
    import child from './11-appChild.vue'
    </script><style lang="less" scoped></style>
    

    子组件

    <template>
      <p>子组件</p>
      <slot></slot>
    </template><script lang="ts" setup>
    import {} from 'vue'
    </script><style lang="less" scoped></style>
    

    具名插槽

    父组件

    <template>
      <child>
        <template v-slot:but>
          <button>按钮</button>
        </template>
    ​
        <template v-slot:link>
          <p>这是中国宜昌</p>
        </template>
      </child>
    </template><script lang="ts" setup>
    import {} from 'vue'
    import child from './11-appChild.vue'
    </script><style lang="less" scoped></style>
    

    子组件

    <template>
      <p>子组件</p>
      <slot name="link"></slot>
      <slot name="but"></slot>
    </template><script lang="ts" setup>
    import {} from 'vue'
    </script><style lang="less" scoped></style>
    

    作用域插槽

    父组件

    <template>
      <child>
        <template #but="scope">
          <button>按钮 {{ scope.title }} {{ scope.myStr }}</button>
        </template>
    ​
        <template v-slot:link>
          <p>这是中国宜昌</p>
        </template>
      </child>
    </template><script lang="ts" setup>
    import {} from 'vue'
    import child from './11-appChild.vue'
    </script><style lang="less" scoped></style>
    

    子组件

    <template>
      <p>子组件</p>
      <slot name="link"></slot>
      <slot name="but" title="你好吗" :myStr="str"></slot>
    </template><script lang="ts" setup>
    import { ref } from 'vue'let str = ref('这是匿名插槽')
    </script><style lang="less" scoped></style>
    

    Teleport传送门

    Vue 3 提供的新组件,可以把组件传输到指定的位置。