vue3.0 + ts

使用 Vite 创建

  • Vite是Vue的作者尤雨溪开发的Web开发构建工具,它是一个基于浏览器原生ES模块导入的开发服务器,在开发环境下,利用浏览器去解析import,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随启随用。同时不仅对Vue文件提供了支持,还支持热更新,而且热更新的速度不会随着模块增多而变慢。在生产环境下使用Rollup打包。
  • 特点
  • 快速的冷启动
  • 即时热模块更新(HMR,Hot Module Replacement)
  • 真正按需编译Vite是在推出Vue 3的时候开发的,目前仅支持Vue 3.x,这意味着与Vue 3不兼容的库也不能与Vite一起使用。
  • 使用
  • 与Vue CLI类似,Vite也提供用npm或者yarn来生成项目结构的方式。选择一个目录,打开命令提示窗口,依次执行下面的命令构建脚手架项目,并启动项目。
npm init vite-app <project-name>

cd <project-name>

npm install

npm run dev

如果使用yarn,则依次执行下面的命令:

yarn create vite-app <project-name>

cd <project-name>

yarn

yarn dev

Composition Api

setup()
  • 新的 option ,所有的组合API函数都在此使用,只在初始化时执行一次
  • 函数如果返回对象,对象中的属性或方法,模板中可以直接使用
ref
  • 作用:定义一个数据的响应式
  • 语法:const xxx = ref(initValue);
  • 创建一个包含响应式数据的引用(reference)对象
  • js中操作数据:xxx.value
  • 模板中操作数据:不需要.value
  • 一般用来定义一个基本类型的响应式数据
<template>
	<h2>{{count}}</h2>
	<hr/>
     <button @click="updateCount">更新</button>
</template>
<script>
     import {ref,defineComponent} from 'vue'
	export default defineComponent {
        name:'App',
        // vue2 的方式实现
        /*  data(){
                return{
                    count:0,
                }
            },
            methods:{
                updateCount(){
                    this.count++
                }
            }
        */
        // vue3 的方式实现
         setup(){
            // let count = 0; // 此时的数据不是响应式数据(响应式数据:数据变化,页面随之也变化)
            // ref是一个函数,作用:定义一个响应式的数据,返回的是一个Ref对象啊,对象中有一个value属性,如果需要对数据进行操作,需要使用该ref对象调用value属性的方式进行数据的操作
            // 括号里面是 初始值
            const count = ref(0)
            function updateCount(){
                count.value++
            }
            return{
                count,
                updateCount,
            }
        }
    }
</script>
reactive
  • 作用:定义多个数据的响应式
  • const proxy = reactive(obj):接收一个普通对象然后返回该普通对象的响应式代理器对象
  • 响应式转换是“深层的”,会影响对象内部所有嵌套的属性
  • 内部基于 ES6 的 Proxy 实现,通过代理
<template>
  <div>
    <h3>{{  }}</h3>
    <h3>{{ user.age }}</h3>
    <h3>{{ user.sex }}</h3>
    <button @click="updateCount">更新</button>
  </div>
</template>
<script>
    import {reactive,defineComponent} from 'vue'
	export default defineComponent({
         setup () {
                const obj = {
                  name: 'Jarry',
                  age: 23
                }
                // user 现在是代理对象,obj是目标对象; user对象的类型是Proxy
                const user = reactive(obj)

                const updateCount = () => {
                  console.log(user)
                  // 直接使用目标对象的方式来更新目标对象中的成员的值是不行的,只能使用代理对象的方式来更新数据(响应式数据)
                  //  obj.name='John'   //  The write is error
                   = 'Zip'
                  user.age = 10
                  user.sex = '男'
                }
                return {
                  user,
                  updateCount
                }
         }
	})    

</script>
比较Vue2与Vue3的响应式
Vue2的响应式
  • 对象:通过defineProperty对象的已有属性值的读取和修改进行劫持(监视/拦截)
  • 数组:通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
Object.defineProperty(data,'count',{
    get(){},
    set(){}
})
  • 问题:
  • 对象直接新添加的属性或删除已有属性,界面不会自动更新
  • 直接通过下标替换元素或更新length,界面不会自动更新 arr[1] = {}
Vue3 的响应式
  • 通过Proxy(代理):拦截队data任意属性的任意操作,包括属性值的读写,属性的添加,属性的删除等
  • 通过 Reflect (反射):动态队被代理对象的相应属性进行特定的操作
const obj = {
    name:'Jarry',
    age:34
}
// 把目标对象变成代理对象
// target:目标对象; prop:属性   val:属性值
const proxyUser = new Proxy(obj,{
    // 读取某个属性的值
    get(target,prop){
        return Reflect.get(target,prop)
    },
    // 修改目标对象的属性值/为目标对象添加新的属性
    set(target,prop,val){
        return Reflect.set(target,prop,val)
    },
    deleteProperty(target,prop){
        return Reflect.deleteProperty(target,prop)
    }
})
// 通过代理对象获取目标对象中的某个属性值
console.log(proxy);        // 'Jarry'
// 通过代理对象更新目标对象的某个属性值
proxy = 'Zip'
// 通过代理对象向目标对象中添加一个新的属性
proxyUser.gender = '男'
父子组件传值
  • 父组件
<template>
  <div>
    <h4>父组件</h4>
    <h4>msg:{{ msg }}</h4>
    <button @click="updateMsg">更新</button>
    <hr />
    <Child :msg="msg" />
  </div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Child from './Child.vue'
export default defineComponent({
  name: 'App',
  components: {
    Child
  },
  setup () {
    const msg = ref('父元素的内容')
    const updateMsg = () => {
      msg.value += '+++'
    }
    return {
      msg,
      updateMsg
    }
  }
})
</script>
<style scoped></style>
  • 子组件
<template>
  <div>
    <h4>子组件msg {{ msg }}</h4>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'App',
  props: ['msg']
})
</script>
<style scoped></style>
setup()
  • setup 是在 beforeCreate 生命周期回调之前就执行了,而且就执行一次
  • brforeCreate() :数据初始化的生命周期回调
  • mounted():界面渲染完毕
  • setup 在执行的时候,当前的组件还没有创建出来,所以组件实例对象 this 不能用
  • setup执行的时机
  • 在beforeCreate之前执行(一次), 此时组件对象还没有创建
  • this是undefined, 不能通过this来访问data/computed/methods / props
  • 其实所有的composition API相关回调函数中也都不可以
  • setup的返回值
  • 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
  • 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
  • 返回对象中的方法会与methods中的方法合并成功组件对象的方法
  • 如果有重名, setup优先
  • 注意:
  • 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods
  • setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据
  • setup的参数
  • setup(props, context) / setup(props, {attrs, slots, emit})
  • props: 包含props配置声明且传入了的所有属性的对象
  • attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
  • slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
  • emit: 用来分发自定义事件的函数, 相当于 this.$emit
// 父组件
<template>
	<Child :msg = 'msg' :msg2 = 'msg2' @show='emitFun'/>
</template>
<script lang = 'ts'>
	import Child from './Child.vue'
    export default defineComponent({
        components:{
            Child,
        },
        setup(){
            const msg = ref('This is first msg!');
            const msg2 = ref('This is second msg2!')
            function emitFun(txt :string){
                msg =+ txt
            }
            return{
                msg,
                msg2,
                emitFun
            }
        }
    })
</script>

// 子组件
<template>
    <h3>{{msg}}</h3>
	<button @click='emitFunning'>分发事件</button>
</template>
<script lang='ts'>
	export default defineComponent({
       // 父组件传过来的参数		
         props:['msg'],
		setup(props,context){
           // prop 参数,是一个对象,是在子组件中使用props 接收到所有的属性   包含 props 配置声明且传入了的所有属性的对象
            console.log(props.msg)  // This is first msg!
            // context 参数,是一个对象,里面有attrs对象(获取当前组件标签上所有的属性对象,但是并没有在子组件的props上定义); emit 方法(分发事件) ; slots对象(插槽)
            console.log(context.attrs)   // This is second msg2!
            
            function emitFunning(){
                context.emit('emitFun','++')
            }
        }	
	})
</script>
  • 上面是根据视频中记录的,下面是网址中粘贴过来的笔记代码 (比较记忆)
<template>
  <div>
    <h3>{{n}}</h3>
    <h3>{{m}}</h3>

    <h3>msg: {{msg}}</h3>
    <h3>msg2: {{$attrs.msg2}}</h3>

    <slot name="xxx"></slot>

    <button @click="update">更新</button>
  </div>
</template>

<script lang="ts">

import {
  ref,
  defineComponent
} from 'vue'

export default defineComponent({
  name: 'child',

  props: ['msg'],

  emits: ['fn'], // 可选的, 声明了更利于程序员阅读, 且可以对分发的事件数据进行校验

  data () {
    console.log('data', this)
    return {
      // n: 1
    }
  },

  beforeCreate () {
    console.log('beforeCreate', this)
  },

  methods: {
    // update () {
    //   this.n++
    //   this.m++
    // }
  },

  // setup (props, context) {
  setup (props, {attrs, emit, slots}) {
      // prop 参数,是一个对象,是在子组件中使用props 接收到所有的属性   包含 props 配置声明且传入了的所有属性的对象
    console.log('setup', this)     // undefined
    console.log(props.msg, attrs.msg2, slots, emit)  

    const m = ref(2)
    const n = ref(3)

    function update () {
      // console.log('--', this)
      // this.n += 2 
      // this.m += 2

      m.value += 2
      n.value += 2

      // 分发自定义事件
      emit('fn', '++')8/
    }

    return {
      m,
      n,
      update, 
    } 
  },
})
</script>
计算属性与监视