vue知识总结

306 阅读12分钟
  • vue的set this.$set('target',key,'value)
例子:
// target 目标对象 key更改的具体数据  value重新赋的值
data(){
    return {
        list:{
            key:'例子'
        }
    }
}
this.$set(this.list,addKey,'我是通过set添加上去的')
// 注意不能直接在data上面新加或改变数据源
  • vue的filter
<div>{{num | addNum}}</div>

new Vue({
    el:'#root',
    data(){
        return {
            num:10
        }
    },
    // 局部过滤器
    filters:{
        // value 为插值语法中过滤器侧边的元素值
        addNum(value){
            return value * 2   // 是改变页面的元素值,被过滤的值本身不变
        }
    }
})

// 全局过滤器
Vue.filter('globalFilter'function(value){
    return  value
})

-vue的directive (自定义指令)

<div v-big="n"><div>
<script>
    new Vue({
    el:'$root',
    data(){
        return {
            n:1
        }
    },
    // 自定义局部指令
    // 定义指令的两种写法
    directvies:{
        // elemnt是绑定在自定义指令身上的Dom元素
        // binding可通过binding.value获取元素身上的value值
        // 1.简写形式(相对于只调用bind,和updata)
        big(element,binding){
            element.innerText = binding.value
        },
        // 2.对象形式
        pfoucs:{
            // 元素与指令成功绑定时(一上来时)
            bind(element,binding){
            
            },
            // 指令所在元素被插入页面时
            inserted(element,binding){
            
            },
            //指令所在模板被重新解析时
            update(element,binding){
            
            }
        }
    }
</script>
  • vue的render
    // 入口文件main.js中**render**函数用于挂载App.vue到页面
    render(createElement) => {
        return createElement(App)   // 过载App.vue到页面
    } 
    == 
    render:h => h(app)  // 简写形式 等同于上面
  • vue的路由传参
    // router传参的两种方式 query params
   this.$router.push(path:'about',query:{getPage:'query传参'})
   this.$router.push(name:'about',params:{getPage:'params传参'})
   // 注意:使用params传递参数,前面必须使用name关键字进行跳转,否则params为空
   // 接收$router跳转传递的参数
   this.$route.params.xxx  // 接收params方式传递的参数
   this.$route.query.xxx // 接收query方式传递的参数

-vue的minixs (混入)
minxs同个方法,可在多个组建中引入使用

   brother.vue文件
   <telmplate>
       <div>
           <text  @click="showName">{{name}}</text>
       </div>
   </telpmate>
   <script>
       import {hunru} ../mixins.js
       export default{
           name:'brother',
           data(){
               return {
                   name:'minx混入'
               }
           },
            mixins:[hunru],   // 注册minxs中的方法
       }
   </script>
   
   litterBrother.vue文件
   <telmplate>
       <div>
           <text @click="showName">{{name}}</text>
       </div>
   </telpmate>
   <script>
       import {hunru} ../mixins.js
       export default{
           name:'brother',
           data(){
               return {
                   name:'minx混入'
               }
           },
           mixins:[hunru],   // 注册minxs中的方法
       }
   </script>
   
   mixins.js
   const hunru = {
       methods:{
           showName(){
               alert(this.name)
           }
       },
       mounted(){
           console.log('你好')   // 引用mixins组件的页面一加载会触发到minxs的mounted钩子上面的事件
       },
   }
   
   // 全局minxs引入
   app.js中引入minxs.js
   import {hunru} from "../minix.js"
   import Vue from "Vue"
   Vue.mixin(hunru)   // 可在全局vm身上使用此方法
  • scoped样式 作用:让样式在布局生效,防止冲突
    // 写法
    <style scoped>
    </style>
  • 組件的自定义事件 ($emit)
  1. 一直组件间通信的方式,适用于:子组件 ==> 父组件
  2. 使用场景:A是父组件,B是子组件,B想给A传递数据,那么就在A中给B绑定自定义事件 (事件的回调在A中)
  3. 绑定自定义事件
    1. 第一种方式:在父组件中<Demo @testing="demo"></Demo> 或 <Demo v-on:testing="demo"></Demo>
    2. 第二种方式:在父组件中:
    <Demo ref="demo"></Demo>
    ......
    mounted(){
        this.$refs.demo.$on('testing',this.test)
    }
    3.若想让自定义事件只触发一次,可以使用 once修饰符,或$once 方法。
        <Demo @testing.once="demo"></Demo>
        this.$refs.demo.$on.$once('testing',this.test)
  1. 触发自定义事件: this.$emit('testing',数据)
  2. 解绑自定义事件:this.$off('testing') 6.组件上也可以绑定元素DOM事件,需要使用 native 修饰符。
    <Demo @click.native="demo"></Demo>
    ....
    methods:{
        demo(){
            alert('测试')
        }
    }
  1. 注意:通过 this.refs.demo.refs.demo.on('testing',回调),绑定自定义事件,回调要么配置在methods中,要么使用箭头函数,否则this质量会出问题!

全局事件总线 (GlobalEventBus)

1.一种组件通信的方式,使用于任意组件间通信 2.安装全局事件总线

// main.js
new Vue({
    ........
    beforeCreate(){
        Vue.prototype.$bus = this  // 安装全局事件总线,将Vue的原型实例对象绑定指向在$bus上
    },
    ........
})

3.使用全局总线 ¥bus 1.接收数据:A想要接收B组件传递过来的数据,则在A组件中绑定自定义事件,事件的回调留在A组件自身

    1.组件A中接收参数
    methods(){
            demo(data){.......}
        }        mounted(){
            this.$bus.$on('parc',this.demo)
        }
        ```
    
    2.组件B中提供数据
    ```js
    .....
    data(){
        return {
            content:'$bus传参'
        }
    },
    created(){
        this.$bus.$emit('parc',this.content)
    }

消息订阅与发布

个人理解也是同GlobleEventBus相同的跨组件传参,不过需引入第三库,如 pubsub.js
1.如A组件想要接收B组件的参数,则要进行消息订阅
2.而B组件进行发布

 // A.vue  订阅消息
 import pubsub from "pubsub.js"
 ......
 export default{
     mounted(){
          // this.$bus.$emit('pract',this.content)
         //  消息订阅 (等同于上方)
         this.pubId = pubsub.subscribe('hello',(mesName,data) => {
             console.log(mesName); // 传递过来的参数名
             console.log(data); // 参数值
         })
     },
     beforeDestory(){
         // 取消消息订阅
         pubsub.unsubscribe(this.pubId)  // 取消不是填写发布的参数名,而是填写每次订阅消息的Id (pubsub.js的特征)
     }
 }
 ......
 

 // B.vue 消息的发布
 ......
     import pubsub from "pubsub.js"
     export default{
         mounted(){
             pubsub.publish('hello','消息订阅与发布传递的参数!')
         }
     }
 ......

vue配置代理

在 vue.config.js
moudle.exports = {
    pages:{
        index:{
            // 入口
            entry:"src/main.js"
        }
    },
    lintoOnSace:false, // 关闭语法检查
    
    // **开发代理服务器**
    //   1. 方式一 
    devServer:{
        proxy:'http://localhost:5000'
    }
    // 说明:
    //  1.优点:配置简单,请求资源时直接发送前段(8080)即可。
    //  2.缺点:不能配置多个代理,不能灵活的控制是否走代理。
    //  3.工作方式:若按照上述配置代理,当请求了前段不存在的资源时,那么该请求会转发给服务器(优点匹配前端本地资源)
    
    // 2. 方式二  
    devServer:{
        proxy:{
            '/api':{
                target:'http://localhost:5000', // 代理目标的基础路径
                changeOrigin:true,
                pathRewtire:{'^/api':''}  // 过滤代理服务器路径
            },
             '/api2':{
                target:'http://localhost:5000', // 代理目标的基础路径
                changeOrigin:true,
                pathRewtire:{'^/api2':''}  // 过滤代理服务器路径
            }
        }
    }
}
// 说明:
//  1.优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
/*  2.缺点:配置略微繁琐,请求资源时必须加前缀。
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000 (撒谎)
changeIrigin设置为false时,服务器收到的请求头中的host为:localhost:8080 (不撒谎)
changeOrigin默认值为false
*/

vue插槽

1.作用:让父组件(使用该组件的组件)可以向被使用的子组件指定位置插入html结构,同时也是一种组件间的通信方式,适用于 父组件 ===> 子组件 2.分类:默认插槽、具名插槽、作用域插槽 3.使用方式: 1.默认插槽

父组件中:
    <Category>
        <div>html结构1</div>
    </Category>
子组件中:
    <tempalte>
        <div>
            <slot>插槽默认内荣</slot>
        </div>
    </template>
2.具名插槽
父组件中:
    <Category>
        <--方式1-->
        <div slot="center">html结构1</div>
        <--方式2-->
        <div v-slot="center">html结构1</div>
    </Category>
子组件中:
    <tempalte>
        <div>
            <slot name="slot">插槽默认内荣</slot>
        </div>
    </template>
3.作用域插槽
    1.理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定
    2.注意:使用作用域插槽,必须使用template来接收传递来的数据
父组件中:
    <Category>
        <--方式1-->
        <tmeplate scope="getChildData">
            <div>
                <li>{{getChildData.data1}}</li>
            </div>
        </tempalte>
        <--方式2 推荐-->
        <tmeplate slot-scope="{data2}">  // 使用了es6对象解构
            <div>
                <h1>{{data2}}</h1>
            </div>
        </tempalte>
    </Category>
子组件中:
    <tempalte>
        <div>
            <slot name="slot">插槽默认内荣</slot>
        </div>
    </template>
    export deafualt {
        name:Category,
        data(){
            return{
                data1:'数据1',
                data2:'数据2'
            }
        }
    }

vueX

  1. 概念 在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且使用于任意组件间通信。
  2. 何时使用
    多个组件需要共享数据市
  3. 搭建Vuex环境
    1.创建文件/store/index.js
    // 引入Vue核心库
    import Vue from "vue"
    // 引入Vuex
    import Vuex from "vuex"
    // 安装Vuex插件
    Vue.use(Vuex)
    
    // 准备actions对象-响应组件中用户的动作
    const actions = {}
    // 准备mutations对象-修改state中的数据
    const muations = {}
    // 准备state对象-保存具体的数据
    const state ={}
    
    // 创建且暴露store
    export default new Vue.Store{
        actions,
        muations,
        state,
    }


2.在入口文件main.js中创建vm时传入stotr配置项


...
 //引入store
 import stroe form "./store/index"
 
 new Vue({
     store,
     render:h => h(App)
 })
...

4.Vuex的基本使用 1.准备要的初始化数据、配置actions、配置muations、配置state。

    // 引入核心库Vue
    import Vue from "vue"
    // 引入Vuex
    import Vue from "vuex"
    // 安装Vuex
    Vue.use(Vuex)
    
    const actions = {
        // 响应组件中的事件
        add(context,value){
            // 通过contex(可访问上下文).commit触发mutations的add事件,并传递value
            context.commit('ADD',value)
        }
    }
    
    const mutations = {
        ADD(state,value){
            state.num += value
        }
    }
    
    const state = {
        num:0
    }
    
    // 创建并保留store
    export default new Vuex.Store{
        actions,
        mutations,
        state
    }

2.组件中读取Vuex中的数据:$stroe.state.num。

3.组件中修改Vuex中的数据:$stroe.dispath('actions中的方法名',数据)$store.commit('mutations中的方法名',数据)

备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,既不写dispath,直接编写commit

5.getters的使用 1.概念:当vuex中的state需要经过加工后再使用,可以使用getters加工。 2.在stroe.js中追加getters配置

    ...
        const getters = {
            bigNum(state){
                return state.num * 10
            }
        }
    ...

3.组件中读取数据:$store.getters.bigNum

4.四个map方法的使用

1.mapState方法:用于帮助我们映射(获取)state中的数据为计算属性

    <template>
        <div>
            {num}  // 为vuex中的state数据num
        </div>
    </template>

   import {mapState} from 'vuex' 
   computed(){
       // 借助mapState生成计算属性,num(对象写法)
       ...mapState({num:'num'})
       
       // 借助mapState生成计算属性,num(数组写法) [数组key、value相对可使用Es6数组简写]
       ...mapState([num])
   }

2.mapGetters方法:用于帮助我们映射(获取)getters中的数据为计算属性

    <template>
        <div>
            {bigNum}  // 为vuex中的getters函数return出来的数据
        </div>
    </template>

   import {mapGetters} from 'vuex' 
   computed(){
       // 借助mapGetters生成计算属性,bigNum(对象写法)
       ...mapGtate({bigNum:'bigNum'})
       
       // 借助mapGetters生成计算属性,bigNum(数组写法) [数组key、value相对可使用Es6数组简写]
       ...mapState([bigNum])
   }

3.mapActions方法:用于帮助我们生成与actions对话的方法,(白话:会帮助我们调用$store.dispatch(xxx))函数


import {mapActions} from 'vuex'
methods:{
    ... 
    //incrementOdd(){
    //    this.$store.dispath(jiaOdd,this.num)
    //}
    
    // 靠mapActions生成:incrementOdd(对象形式)
    ...mapActions({incrementOdd,'jiaOdd'})
    
    // 靠mapActions生成:incrementOdd
    ...mapActions(['jiaOdd'])
}

4.mapMutations:用于帮助我们生成与mutations对话的方法,(白话:会帮助我们调用$store.commit(xxx))函数

import {mapMutations} from 'vuex'
methods:{
     ... 
    //incrementOdd(){
    //    this.$store.commit(JIA,this.num)
    //}
    
    // 靠mapMutations生成:incrementOdd(对象形式)
    ..mapMutations({incrementOdd:'JIA'})
    
    // 靠mapMutations生成:JIA(对象形式)
    ...mapMutations(['JIA'])
    
}

备注:mapActions与mapMutations使用时,若需要传递数据:需要在模板中绑定事件时传递好参数,否则参数则是默认的对象事件

路由

1.基本使用

1.安装vue-router,命令行:npm i vue-router 2.应用插件:Vue.use(VueRouter) 3.编写router配置项

// 引入VueRouter
import vueRouter from 'vue-router'
// 引入路由组件
import About from '../components/About'
import Home from '../components/Home'

// 创建并暴露router实例对象,去管理一组一组的路由规则
export default new vueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            components:Home
        }
    ]
})

4.实现切换(active-class可配置高亮样式)

<router-link active-class='active' to='/about'>About</router-link>

5.指定展示位置

<router-view></router-view>

2.几个注意点

1.路由组件通常存放在 page文件夹,一遍组件通常存放在components文件夹。 2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。 3.每个组件都有自己的$route属性,里面存储这自己的路由信息。 4.整个应用只有一个router,可以通过组件的$router属性获取到。

3.多级路由

1.配置路由规则,使用children配置项:

routes:[
    {
        path:'/about',
        component:About,
        chidlren:[   //通过children配置子路由
            {
                path:'news', //此处一定不要写成:/news,子级路由不用加 /
                component:News
            }
        ]
    }
]

2.跳转(要写完整路径): <router-link to="/about/news">News</router-link>

4.路由的query参数

1.传递参数

<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="`/home/message/detail?id=${666}&title=${你好}`"></router-link>

<!-- 跳转并携带query参数,to的对象写法 -->
<router-link :to={path:'/home/message/detail',query:{id:666,titile:'你好'}}></router-link>

2.接收参数 route.query.idroute.query.id route.query.title

5.命名路由

1.作用:可以简易化路由的跳转。 2.如何使用 1.给路由命名

{
    path:'/demo',
    component:Demo,
    children:[
        {
            path:'test',
            component:Test,
            children:[
                {
                    name:'hello',  // 给路由命名
                    path:'welcome',
                    component:Hello
                }
            ]
        }
    ]
}

2.简化跳转:

<!-- 简化前,需要写完整路径 -->
<router-link to='/demo/test/welcome'>跳转</router-link>

<!-- 简化后,直接通过名字跳转 -->
<router-link :to={name:'hello'}>跳转</router-link>

<!-- 简化写法配合传递参数 -->
<router-link :to={name:'hello',query:{id:666,title:'你好'}}></router-link>

6.路由的params参数

1.配置路由,声明接收params参数

{
    path:'/demo',
    component:Demo,
    children:[
        {
            path:'test',
            component:Test,
            children:[
                {
                    name:'hello',  // 给路由命名
                    path:'welcome/:id/:title', // 使用占位符声明接收params参数
                    component:Hello
                }
            ]
        }
    ]
}

2.传递参数

<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="demo/test/hello/666/你好">跳转</router-link>

<!-- 跳转并携带params参数,to的对象写法 -->
<router-link :to={name:'Hello',params:{id:666,title:'你好'}}></router-link>

注意:路由携带params参数时,若使用to的对象写法,则不用使用path配置项,必须使用name配置!

3.接收参数

this.$route.params.id

7.路由的props配置

const routers =[
    {
        path:'/home',
        name:'Home',
        components:home,
        
        // 1.props对象写法 (在Home组件中用:props:[title]接收使用)
        props:{title:'标题'}
        
        // 2.props配置为true (在Home组件中用props:[params键名]接收传递的params参数值)
        props:true,
        
        // 3,props函数式 (自动回调函数,$router可访问到组件的params和query参数)
        props($router){
            return title:$router.query.title || title:$router.params.title
        }
    }
]

8. <router-link>的replace属性

1.作用:控制路由跳转时操作浏览器历史记录的模式 2.浏览器的历史记录有两种,push和replace,push是追加记录,而replace是替换当前记录。路由跳转默认为push 3.如何开启replace模式:<router-link replace ......>

9.编程式路由导航

1.作用:不借助实现路由跳转,让路由更加灵活。 2.具体代码

// $router的五个API
this.$router.push({
    name:'Home',
    params:{
        title:xxx,
        id:xxx
    }
})

this.$router.replace({
    name:'Home',
    params:{
        title:xxx,
        id:xxx
    }
})

// 后退
this.$router.back()
// 前进
this.$router.forward()
// to (后面要携带参数,正数为前跳,负数为后退)
this.$router.go(-1)

10. 缓存路由组件

1.作用:让不展示的路由组件保持挂载,不被销毁。(切换路由市,默认上个路由会被销毁掉) 2.具体代码

// include配置是填写保持挂载的路由组件,后天填写必须是组件的name,而不是router的name!
<keep-alive include="Homes">
    <router-view></router-view>
</keep-alive>

11.两个新的生命周期钩子

1.作用:路由组件独有的两个钩子,用于捕获路由组件的激活状态。

2.具体名字

1.`activated`路由组件被激活时触发。
2.`deactivated`路由组件失活时触发。

12 路由守卫

1.作用:对路由进行权限控制

2.分类:全局守卫、独享守卫、组件内守卫

3.全局守卫

// 全局前置守卫:初始化时执行、每次路由切换时也会执行
router.beforeEach((to,from,next) => {
    // 访问跳转去路由身上meta自定义配置的isAuth属性 (作用:是否要进行权限匹配)
    if(to.meta.isAuth){
        if(localStorage.getItem('school') == 'md'){ // 权限控制的de具体规则
            next() // 放行
        } else{
            alert('暂无权限查看')
        }
    } else{
        next() // 放行
    }
})

// 全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from) => {
    if(to.meat.title){
        document.title = to.meta.title // 修改网页的title
    }
})

4.独享路由守卫:

// 写在router配置项里面的
{
    path:'/home',
    name:'Home',
    component:Home,
    meta:{isAuth:true,title:'首页'}
    beforeEnter((to,from,next) => {
        if(to.meta.isAuth){  // 判断当前路由是否需要权限控制
            if(localStorage.getItem('token')){
                next()
            } else {
                alert('无权限查看')
            }
        }
    })
}

5.组件内路由守卫

// 进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){
},
// 离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
}

13.路由器的两种工作模式

  1. 对于一个url来说,什么是hash值? -----#及后面的内容就是hash值。
  2. hash值不会包含在HTTP请求中,既:hash值不会带给服务器。
  3. hash模式
    1. 地址中永远带着#号,不美观。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性好
  4. history模式:
    1. 地址干净,美观。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线需要后端人员支持,解决刷新页面服务端404的问题。

Vue3快速上手

1.使用vue-cli创建

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version || vuer -V
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

2.使用vite创建

## 创建项目
npm init vite-app <project-name>
## 进入项目目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev

3.拉开序幕的setup

1.理解:Vue3.0中一个新的配置项,值为一个函数。
2.setup是所有Composition API "表演的舞台"
3.组件中所用到的:数据、方法等等,均要配置在setup中。
4.setup函数的两种返回值。

1.若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注)。<br>
2.若返回一个渲染函数:则可以自定义渲染内容。(了解)。

5.注意点:

1.尽量不要与Vue2.x配置混用。
    - Vue2.x配置(data、methods、computed...)中可以访问到setup中的属性、方法。
    - 但在setup中不能访问到Vue2.x配置(data、nethods、computed...)。
    - 如果用重名,setup优先。
2.setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性

4.ref函数

  • 作用:定义一个相适应的数据。
  • 语法:const xxx = ref(initValue) 1.创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
    2.js中操作数据:xxx.value
    3.模板中读取数据:不需要.value,直接<div>{{xxx}}</div>
  • 备注:
    1.接收的数据可以是:基本类型,也可以是对象类型。
    2.基本类型的数据:响应式依然是靠 Object.defineProperty()的get与set完成的
    3.对象类型的数据:内部“求助”了Vue3.0中的一个新函数-----reactive函数

5.reactive函数

  • 作用:定义一个对象类型的相适应数据(基本类型不要用它,用ref函数)
  • 语法:const 代理对象 = reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reacitve定义的响应式数据是“深层次的”。
  • 内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作。

6.Vue3.0中的响应式原理

# vue2.x的响应式原理:
- 对象类型:通过 Object.defineProperty()对属性的读取、修改进行了拦截(数据劫持)。
- 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Obejct.defineProperty(data,'count,{
    get(){}
    set(){}
})
-存在的问题
    1.新增属性、删除属性,界面不会更新。
    2.直接通过下标修改数组,界面不会自动更新。

5.Vue3.0的响应式原理

  • 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。
  • 通过Reflect(反射):对源对象的属性进行操作。
new Proxy(data,{
    // 拦截读取属性值
    get(target,prop){
        return Reflect.get(traget,prop)
    },
    // 拦截设置属性值或添加新属性
    set(traget,prop,value){
        return Reflect.set(target,prop,value)
    },
    // 拦截删除属性
    deleteProperty(target,prop){
        return Reflect.deleteProprty(target,prop)
    }
    
})

reactive对比ref

  • 从定义数据角度来说

    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象
  • 从原理角度来说:

    • ref通过Object.defineProperty()的getset来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据。
  • 从使用角度来说:

    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value.
    • reactive定义的数据:操作数据与读取数据:均不需要.value。

6.setup的两个注意点

  • setup执行的时机

    • 在beforeCreated之前执行依次,this是undefined
  • setup的两个参数

    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收来的属性

    • context:上下文对象

      • attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs.
      • slots:收到的插槽内容,相当于this.$slots。
      • emit:分发自定义事件的函数,相当于this.$emit

7计算属性与监视

1.compued函数

  • 与vue2.x中computed配置功能一致。
  • 写法
import {computed} from "vue"
setup(){
    ...
    // 计算属性-简写
    let fulleName = computed(() => {
        reurn person.firstName + '-' + person.lastName
    })
    // 计算属性-完整写法
    let fullName = computed({
        get(){
            return person.firstName + '-' + person.lastName
        },
        set(value){
            const nameArr = value.split("-")
            person.firstName = nameArr[0]
            person.lasttName = nameArr[1]
        }
    })
}

2.watch函数

  • 与Vue2.x中watch配置功能一致。

  • 两个小航“坑”

    • 监视reactive定义的响应式数据时,oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
    • 监视reactive定义的响应式数据中某个属性时:deep配置有效。
...
import (ref,reactive,watch) from Vue
setup(){
    let sum =ref(0)
    let msg = ref('你好')
    let person = reactive({
        name:'张三',
        age:18,
        job:{
            j1:{
                salary:20
            }
        }
    })
},
//情况1:监视ref所定义的一个响应式数据
watch(sum,(newValue,oldValue) => {
    console.log('sum变了',newValue,oldValue)
},{immediate:true})

//情况2:监视ref所定义的多个响应式数据
watch([sum,msg],(newValue,oldValue) => {
    console.log('sum或msg变了',newValue,oldValue)
},{immediate:true})

/* 情况3:监视reactvie所定义的一个响应式数据的全部属性
    1.注意:此处无法正确的获取oldValue
    2.注意:强制开启了深度监视(deep配置无效)
*/
watch(person,(newValue,oldValue) => {
    console.log('person变化了',newValue,oldValue)
},{deep:true})

//情况4:监视reactive所定义的一个响应式数据中的某个属性
watch(() => person.name,(newValue,oldValue) => {
    console.log('person.name变化了',newValue,oldValue)
})

//情况5:监视reactive所定义的一个响应式数据的某些属性
watch([() => person.name,() => person.age],(newValue,oldValue) => {
    console.log('person的name或age变化了',newValue,oldValue)
})

// 特殊情况
watch(() => person.obj,(newValue,oldValue) => {
    console.log('person的obj变化了',newValue,oldValue)
},{deep:true})// 此处由于监视的是reactice定义的对象中的某个属性,所以deep配置有效

3. watchEffect函数

  • watch的套路是:既要指明监视的属性,也要指明监视的回调。
  • watchEffect的套路是:不用指明监视某个属性,监视的回調中用到哪个属性,那就监视哪个属性。
  • watchEffect有点像computed:
    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
// watchEffect更指定的回调中用到的数据只要发生变化,则直接重新执行回调
watchEffect(() => {
    const x1 = sum.value
    const x2 = person.age
    console.log('watchEffect配置的回调执行了')
})

8.生命周期

  • Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:

    • beforeDestroy改名为beforeUnmount
    • destory改名为unmounted
  • Vue3.0也提供了Composition Api形式的生命周期钩子,与Vue2.x中钩子对应关系如下。

    • beforeCreate===>setup()
    • created===>setup()
    • beforeMount===>onBeforeMount
    • mounted===>onMounted
    • beforeUpdate===>onBeforeUpdate
    • update===>onUpdate
    • beforeUnmount===>onBeforeUnmount
    • unmounted===>onUnmounted

9.自定义hook函数

  • 什么是hook?---本质是一个函数,吧setup函数中使用的Composition API进行了封装。
  • 类似于Vue2.x中的minx
  • 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂。

10.toRef

  • 作用:创建一个ref对象,让value值指向另一个对象中的某个属性。
  • 语法:const name = toRef(person,'name')
  • 应用:要将响应式对象中的某个属性单独提供给外部使用时。
  • 扩展:toRefs与toRef功能一致,但可以批量创建多个ref对象,语法toRefs(person)

11.provide与inject

Snipaste_2022-03-20_23-15-44.png

  • 作用:实现祖孙组件间通信

  • 套路:父组件有一个provide选项来提供数据,子组件有一个inject选项来开始使用这些数据

  • 具体写法

    1.祖组件中

setup(){
    .......
    let car = reactive({name:'奔驰',price:'40万'})
    provide('car',car)
}
2. 孙组件中

setup(props,context){
    .......
    const car = inject('car')
    return{car}
    .......
}

其他Composition API

1.shallowReactive与shadllowRef

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
  • shallowRef:只处理基本数据类型的响应式,不进行对象的相适应处理。
  • 什么时候使用?
    • 如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ===> shallowReactvie
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换===>shallowRef

2.readonly与shalllowReadyonly

  • readonly:让一个响应式数据变为只读的(深只读)
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)。
  • 应用场景:不希望数据被修改时。

3.toRae与markRaw

  • toRaw:
    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
  • markRaw:
    • 作用:标记一个对象,使其用于不会再称为响应式对象。
    • 引用场景:
      1. 有些值不应被设置为响应式的,例如复杂的第三类库等。
      2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

4.customRef

  • 作用:创建一个自定义的ref,并对其依赖项进行跟踪和更新触发进行显示控制。
  • 实现防抖效果:
<template>
    <input v-model="keyword">
    <text>{{keyword}}</text>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
  name: 'App',
  setup(){
    // 自定向一个ref————名为myRef
    function meyRef(value){
      let timeId
      return customRef((track,trigget) => {
        return {
          get(){
            track() // 通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
            return value
          },
          set(newValue){
             clearTimeout(timeId)
             timeId = setTimeOUt(() => {
                 value = newValue
                 trigget()// 通知Vue去重新解析模板
             })
          }
        }
      })
    }
    let keyWord = meyRef('hello')
    return {
      keyWord
    }
  }
}
</script>

5.响应式数据的判断

  • isRef:检查一个值是否为一个ref对象
  • isReactive:检查一个对象是否由reactive创建的响应式代理。
  • isReadonly:检查一个对象是否是由readonly创建的只读代理
  • isProxy:检查一个对象是否是由reactvie或者readonly方法创建的代理

新的组件

1.Fragment

  • 在Vue2中:组件必须有一个跟标签。
  • 在Vue3中:组件可以没有跟标签,内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处:减少标签层级,减少内存占用。

2.Teleport

  • 什么是Teleport?————Teleport是一种能够将我们的组件html结构移动到指定位置的技术。
<teleport>
    <di v-is='isShow' class='mask'>
        <div class='dialog'>
            <h3>我是一个弹窗</h3>
            <button @click='isShow = false'>关闭弹窗</button>
        </div>
    </div>
</teleport>

3.Suspense

  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
  • 使用步骤
    • 异步引入组件
    import {defineAsyncComponent} from 'vue'
    const child = defineAsyncComponent(() => import('./component/Child.vue'))
    
    • 使用Suspense包裹组件,并配置好default与fallback
<template>
    <div class='app'>
        <Suspense>
            <template v-slot:default>
                <Child/>
            </template>
            <template v-slot:fallback>
                <h3>正在加载中...</h3>
            </template>
        </Suspense>
    </div>
</template>