【Vue】Vue2与Vue3入门教程

MVVM模型

1.M: 模型(Model) : data中的数据

2.V: 视图(view) : 模板代码

3.VM: 视图模型(ViewModel): Vue实例

数据代理:

1.Vue中的数据代理:

通过vm对象来代理data对象中属性的操作(读/写)

2.Vue中数据代理的好处:

更加方便的操作data中的数据

3.基本原理:

通过object.defineProperty()把data对象中所有属性添加到vm上为每一个添加到vm上的属性,都指定一个getter/setter。

在getter/setter内部去操作 (读/写) data中对应的属性。

Vue中的事件修饰符:

1.prevent: 阻止默认事件(常用) :

2.stop: 阻止事件冒泡(常用) ;

3.once: 事件只触发一次(常用)

4.capture:使用事件的捕获模式:

5.self: 只有event.target是当前操作的元素时才触发事件;6.passive:.事件的默认行为立即执行,无需等待事件回调执行完毕;

6.v-mode1的三个修饰符

(1)lazy:失去焦点再收集数据

(2)number:输入字符串转为有效的数字

(3)trim:输入首尾空格过滤

计算属性:

1.定义:要用的属性不存在,要通过已有属性计算得来。

2.原理: 底层借助了objcet.defineproperty方法提供的getter和setter。

3.get函数什么时候执行?

(1).初次读取时会执行一次。(2).当依赖的数据发生改变时会被再次调用。

4.优势: 与methods实现相比,内部有缓存机制(复用) ,效率更高,调试方便。

5.备注:

1.计算届性最终会出现在vm上,直接读取使用即可。2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生

监视属性watch:

1.当被监视的属性变化时,回调函数自动调用,进行相关操作

2.监视的属性必须存在,才能进行监视

3.监视的两种写法:

(1).new Vue时传入watch配置

(2).通过vm.$watch监视

4.深度监视:

(1).Vue中的watch默认不监测对象内部值的改变(一层)

(2).配置deep:true可以监测对象内部值改变(多层)。

备注:

(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以

(2).使用watch时根据数据的具体结构,决定是否采用深度监视。

Vue监视数据的原理:

1。vue会监视data中所有层次的数据。

2。如何监测对象中的数据?

通过setter实现监视,且要在new Vue时就传入要监测的数据。

(1).对象中后追加的属性,Vue默认不做响应式处理

(2).如需给后添加的属性做响应式,请使用如下API:

Vue.set(target,propertyName/index,value) 或

vm.$set(target, propertyName/index,value )

3。如何监测数组中的数据?

通过包裹数组更新元素的方法实现,本质就是做了两件事:

(1).调用原生对应的方法对数组进行更新。

(2).重新解析模板,进而更新页面。

4.在Vue修改数组中的某个元素一定要用如下方法:

1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()reverse()

2.Vue.set() 或 vm.$set()

特别注意: Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性

computed和watch之间的区别:

1.computed能完成的功能,watch都可以完成。

2.watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作。两个重要的小原则:

1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。

2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等》,最好写成箭头函数.

这样this的指向才是vm 或 组件实例对象。

条件渲染:

1.v-if

适用于:切换频率较低的场景。特点:不展示的DOM元素直接被移除。注意: v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”

2.v-show

写法: v-show="表达式"适用于:切换频率较高的场景。特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

3.备注: 使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。

Vue中key的作用

1。虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据[新数据]生成[新的虚拟DOM)随后vue进行[新虚拟DOM]与[旧虚拟DOM] 的差异比较,比较规则如下:

2.对比规则:

(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:@.若虚拟DOM中内容没变,直接使用之前的真实DOM!@.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

(2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。

3。用index作为key可能会引发的问题:1。若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==>界面效果没问题,但效率低。

2。如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题

4。开发中如何选择key?:

1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。

2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

自定义指令

1.定义语法:

(1).局部指令:new Vue(directives:{指令名:配置对象回调函数})

(2).全局指令:Vue.directive( 指令名 , 配置对象 或 **回调函数 **)
2.配置对象中常用的3个回调:

(1).bind: 指令与元素成功绑定时调用。

(2).inserted: 指令所在元素被插入页面时调用。

(3).update:指令所在模板结构被重新解析时调用
备注:

1.配置对象写法如下

Vue.directive( 'my-directive' ,{
  // element为DOM元素,binding为元素的属性
  bind:(element,binding)=>{
    element.innerText = binding.value 
    ...
    //一些逻辑
  }
  // element为DOM元素,binding为元素的属性
  inserted:(element,binding)=>{
    element.innerText = binding.value
    ...
    //一些逻辑
  }
  // element为DOM元素,binding为元素的属性
  update:(element,binding)=>{
    element.innerText = binding.value 
    ...
    //一些逻辑
  }
} )

2.指令定义时不加v-,但使用时要加v-;

3.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelcase命名。

生命周期

常用的生命周期钩子:

1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等[初始化操作]2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]。

关于销毁Vue实例

1.销毁后借助Vue开发者工具看不到任何信息。

2.销毁后自定义事件会失效,但原生DOM事件依然有效。

3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了.

Vue的组件

1.定义组件

使用Vue,extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:

区别如下:

1.不写el, 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。

2.data必须写成函数,避免组件被复用时,数据存在引用关系。

2.注册组件:

1.局部注册:new Vue的时候传入components选项

2.全局注册: Vue.component("组件名,组件)、编写组件标签:

3.一个重要的关系:VueComponent .prototype._proto === Vue .prototype

Vue的过滤器

对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)

语法:

1.注册过滤器: Vue.filter(name,callback) 或 new Vue{filters: {}}

2.使用过滤器: {[ xxx过滤器名]] 或 v-bind:属性 =“xxx过滤器名"

备注:

1.过滤器也可以接收额外参数、多个过滤器也可以串联

2.并没有改变原本的数据,是产生新的对应的数据

Vue的webpack相关配置

vue.config.js配置文件

使用vue inspect > output.js可以查看到Vue脚手架的默认配置。

使用vue.config.js可以对脚手架进行个性化定制,具体配置看vue-cli的api。

属性
1.ref属性

1.被用来给元素或子组件注册引用信息 (id的替代者)

2.应用在htm1标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象 (vc)

3.使用方式:

获取: this.$refs.xxx

2.props属性

(1).传递数据:

<Demo name="xxx"/>

(2).接收数据:

第一种方式(只接收) :

props:[ ' name' ]

第二种方式(限制类型) :

props:{
  name:String
}

第三种方式(限制类型、限制必要性、指定默认值) :

props:(
  name:{
    type:String,//类型
    required;true//必要性
    default;'老王’//默认值
  }
}

备注: props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,

若业务需求确实需要修改,复制props的内容到data中一份,然后去修改data中

的数据。

例如

// vc中部分代码
{
    data(){
        return{
            myData: xxProps
        }
    },
    props: ['xxProps']
}
mixin(混入)

多个组件共用的配置提取成一个混入对象

使用方式:

定义混合,例如:

{
  data()[....}
  methods:[....]
}

使用混入,例如:

(1).全局混入: Vue.mixin(xxx)

(2).局部混入: mixins:[‘xxx’]

插件

功能:用于增强Vue

本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数
据。

一般写在plugins.js中,与main.js同级

// plugins中
export defalut {
    install(Vue,options){
        // 1。添加全局过滤器
        Vue.filter(....)
        // 2。添加全局指令
        Vue.directive(....)
        // 3.全局混入
        Vue.mixin(....)
        // 4.添加实例方法
        Vue.prototype.$myMethod = function () [...]
    }
}
    
// main.js中
import plugin from './plugin';

Vue.use(plugin)
不同版本的Vue的区别

1.vue.js与vue.runtime.xxx.js的区别:

(1).vue.js是完整版的Vue,包含: 核心功能+模板解析器,模板解析器占到三分之一左右的资源。

(2).vue.runtime.xxx.is是运行版的Vue,只包含: 核心功能:没有模板解析器。

2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用

render函数接收到的createElement函数去指定具体内容。

render函数:

render: h => h(App) 的意思为将App组件放入容器中

scoped样式

作用:让样式在局部生效,防止冲突。

用法:

<style scoped>
   .xxx{
       
   }
</style>
组件的自定义事件

1.一种组件间通信的方式,适用于子组件给父组件传值

2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

3.绑定自定义事件:

1.第一种方式,在父组件中:

<Demo @sendMsg="sendMsg"/> 

2.第二种方式,在父组件中:

<Demo ref="demo"/>......

mounted(){
  this.$refs.demo.$on('sendMsg',this.sendMsg)
}

3.若想让自定义事件只能触发一次,可以使用once 修饰符,或$once 万法

4.触发自定义事件:

 this.$emit('sendMsg’,数据)

5.解绑自定义事件

this.$off('sendMsg')

6.组件上也可以绑定原生DOM事件,需要使用 native 修饰符

7.直接写回调函数

this.$refsxxx.on('sendMsg',callback)

// 直接回调的话,callback需要为箭头函数,不然因为是在子组件中调用的,所以this为子组件,只有写成箭头函数才能让this指向父组件
全局事件总线 (GlobalEventBus)

1.一种组件间通信的方式,适用于任意组件间通信,类似于订阅消息。

2.注册

// vm中
new Vue({
    beforeCreate(){
        Vue.prototype.$bus = this 
    }
})

3.使用

(1).接收数据

// vc中
mounted(){
    this.$bus.$on('xxxEvent', ()=>{
       // ...一些逻辑
    })
}

(2).提供数据

// vc中
this.$bus.$emit('xxxEvent', 参数)

4.使用完之后解绑

// vc中
beforeDestroy(){
    this.$bus.$off(['xxxEvent'])
}
nextTick

1.用法

 this.$nextTick(()=>{
     // ...
 })

2.作用: 在下一次 DOM 更新结束后执行其指定的回调

3.什么时候用: 当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行.

插槽

1.作用:当有一个公共组件需要根据传来的html结构来进行不同的展示时,让父组件可以向子组件指定位置插入html结构,适用于 父组件 ===>子组件

2.分类: 默认插槽、具名插槽、作用域插槽

2.1作用域插槽

2.1.1功能:父需要根据子的数据来确定展示内容,就用到这种类型的插槽,将子组件自身的数据传递给父组件后由父组件来决定展示的内容。

3使用方式

// Father中

<template>
    <div>
        <!-- 默认插槽 -->
        <slot>默认内容</slot>
        <!-- 具名插槽 -->
        <slot name="slotA">默认内容</slot>
        <!-- 具名插槽 + 作用域插槽 -->
        <slot name="slotB" :childrenAttribute="childrenAttribute" >默认内容</slot>
    </div>
    
  </template>
  
  <script>
  export default {
    data(){
        return {
            childrenAttribute: {
                showButton: true,
                showTitle: true,
            },
        }
    }
  };
  </script>

// Children中

<template>
  <Children>
    <!-- 默认插槽使用者 -->
    <div>Father传来的默认插槽的内容</div>
    <!-- 具名插槽 -->
    <template slot="slotA">slotA</template>
     <!-- 具名插槽 + 作用域插槽使用者 -->
    <template slot="slotB" scope="scopeData">
      <button v-if="scopeData.childrenAttribute.showButton">插槽中的button</button>
      <h2 v-if="scopeData.childrenAttribute.showTitle">插槽中的title</h2>
    </template>
  </Children>
</template>

<script>
import Children from "./Children";
export default {
  components: { Children },
};
</script>
VueX

1.概念
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理,也是一种组件
间通信的方式,且适用于任意组件间通信。

2.使用

1.创建文件:src/store/index.js

import Vuex from "vuex";

import Vue from "vue";

import module from "./module";

Vue.use(Vuex);

const state = {
  count: 0,
};

const actions = {
  countAdd: (context, value) => {
    context.commit("COUNT_ADD", value);
  },
};

const mutations = {
  COUNT_ADD: (state, value) => {
    state.count += value;
  },
};

const getters = {
  countGetter: (state) => {
    return `${state.count}Getter`;
  },
};

const store = new Vuex.Store({
  state,
  actions,
  mutations,
  getters,
  // 模块化
  modules: {
    ...module,
  },
});

export default store;

2.在main.js中创建vm时传入store配置项

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store,
}).$mount('#app')

3.在组件中使用

<template>
  <div>
    state:
    {{ count }}
    getters:
    {{ countGetter }}
    <button @click="countAdd(1)">actions改变count</button>
    <button @click="COUNT_ADD(1)">mutations改变count</button>

    <div>模块化:</div>
    moduleA中: state:
    {{ moduleACount }}
    getters:
    {{ moduleACountGetter }}
    <button @click="moduleACountAdd(1)">actions改变count</button>
    <button @click="MODULEA_COUNT_ADD(1)">mutations改变count</button>
  </div>
</template>

<script>
import { mapState, mapActions, mapMutations, mapGetters } from "vuex";
export default {
  computed: {
    // 数组或对象
    ...mapState(["count"]),
    ...mapGetters(["countGetter"]),
    ...mapState("moduleA", ["moduleACount"]),
    ...mapGetters("moduleA", { moduleACountGetter: "moduleACountGetter" }),
  },
  methods: {
    ...mapActions(["countAdd"]),
    ...mapActions("moduleA", { moduleACountAdd: "moduleACountAdd" }),
    ...mapMutations(["COUNT_ADD"]),
    ...mapMutations("moduleA", { MODULEA_COUNT_ADD: "MODULEA_COUNT_ADD" }),
  },
};
</script>

<style lang="less" scoped>
</style>

组件中读取vuex中的数据:
$store.state.sum

组件中修改vuex中的数据: store dispatch('action中的方法名数据)或 storecommit('mutations中的方法名数据),若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

4.getters的使用
1.概念:当state中的数据需要经过加工后再使用时,可以使用getters加工
2.在store.js中追加getters配置

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

const getters = {
  countGetter: (state) => {
    return `${state.count}Getter`;
  },
};

5.四个map方法的使用

1mapstate方法:用于帮助我们映射state中的数据为计算属性

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

3.mapActions方法:用于帮助我们生成与actions 对话的方法即: 包含store.dispatch(xxx)的函数

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

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

6.模块化+命名空间

1.目的:让代码更好维护,让多种数据分类更加明确。

// module.js中  //模块化
const nameSpace = 'MODULEA'

const COUNT = [`${nameSpace}Count`]

const _COUNT_ = [`${nameSpace}_COUNT_`]

const state = {
  [COUNT]: 0,
};

const actions = {
  [`${COUNT}Add`]: (context, value) => {
    context.commit(`${_COUNT_}ADD`, value);
  },
};

const mutations = {
  [`${_COUNT_}ADD`]: (state, value) => {
    state[COUNT] += value;
  },
};

const getters = {
  [`${nameSpace}Getter`]: (state) => {
    return state[COUNT];
  },
};

const moduleA = {
  namespaced: true, //开启命名空间
  state,
  actions,
  mutations,
  getters,
};

const store = {
  moduleA,
};

export default store;

export { nameSpace }

// index.js中
import Vuex from "vuex";

import Vue from "vue";

import module from "./module";

Vue.use(Vuex);

const store = new Vuex.Store({
  // 模块化
  modules: {
    ...module,
  },
});

export default store;

3.开启命名空间后,组件中读取state数据

方式一:自己直接读取

this.$store.state.moduleA.moduleACount

方式二:借助mapstate读取:

…mapState(‘moduleA’, [‘moduleACount’]) 或

…mapState(‘moduleA’, { moduleACount (随意命名) : ‘moduleACount’})

4开启命名空间后,组件中读取getters数据

方式一:自己直接读取

this.$store.getters[‘moduleA/moduleACountGetter’]

方式二:借助mapGetters读取:

…mapGetters(‘moduleA’,[‘moduleACountGetter’])

5.开启命名空间后,组件中调用dispatch

方式一: 自己直接dispatch

this.$store.dispatch(‘moduleA/moduleACountAdd’, newCount)

方式二:借助mapActions:

…mapActions(‘moduleA’, { incrementOdd(随意命名) : ‘moduleACountAdd’ })
6.开启命名空间后,组件中调用commit与上方相同

路由

1.理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器 (router) 进行管理

2.前端路由: key是路径,value是组件。

1.作用

1,安装vue-router,命令: npm i vue-router

2.应用插件: Vue.use(VueRouter)

3.编写router配置项

注意点:

1.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。

2.每个组件都有自己的$route 属性,里面存储着自己的路由信息

3.整个应用只有一个router,可以通过组件的 $router 属性获取到

4.路由携带params参数时,必须使用name的方式进行跳转

1.缓存路由组件

1.作用: 在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。

Props:

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
  • max - 数字。最多可以缓存多少组件实例。

2.具体编码:

<keep-alive>
<router-view></router-view>
</keep-alive>

被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated

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

1.activated 路由组件被激活时触发

2.deactivated 路由组件失活时触发

路由守卫

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

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

3.全局守卫

// router代码
import VueRouter from 'vue-router';
import Vue from 'vue';
import pageA from '../pages/pageA'
import pageB from '../pages/pageB'

Vue.use(VueRouter)

const router = new VueRouter({
    routes: [
        {
            path: '/pageA',  
            component: pageA,
            name: 'pageA',
            // 独享守卫
            beforeEnter(to,from,next){
                console.log('我是独享守卫');
                next()
            }
        },
        {
            path: '/pageB',  
            component: pageB,
            name: 'pageB',
        }
    ]
})

router.beforeEach((to, from ,next)=>{
    console.log('to, from ', to, from );
    if(to.path === '/pageA'){
        next();
    }else{
        alert('前置路由拦截!')
    }
})

router.afterEach((to, from)=>{
    console.log('to, from ', to, from );
    if(to.path === '/pageA'){
        document.title = '后置路由启动!'
    }
})

export default router;
路由器的两种工作模式

1.对于一个url来说,什么是hash值? -#及其后面的内容就是hash值

2.hash值不会包含在 HTTP 请求中,即: hash值不会带给服务器

3.hash模式:

1.地址中永远带着#号,不美观

2.若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法

3.兼容性较好

4.history模式:

1.地址干净,美观。

2.兼容性和hash模式相比略差

3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

Vue3

setup

1理解:Vue3.0中一个新的配置项,值为一个函数。

2.setup是所有Composition API (组合API)的基石。

3.组件中所用到的:数据、方法等等,均要配置在setup中

4.setup函数的两种返回值:

1.若返回一个对象,则对象中的属性、方法在模板中均可以直接使用。

2.若返回一个渲染函数: 则可以自定义渲染内容。 (了解)

5.注意点:

1.尽量不要与Vue2.x配置混用。

1.1.Vue2.x配置(datamethos、computed…) 中可以访问到setup中的属性、方法。

1.2.但在setup中不能访问到Vue2.x配置 (data、methos、computed…)。

1.3如果有重名,setup优先

2.setup不能是一个async函数,因为返回值不再是return的对象而是promise,模板看不到retum对象中的属性(除用异步组件的情况)

ref函数

作用: 定义一个响应式的数据

语法: const xxx = ref(initValue)

。创建一个包含响应式数据的引用对象 (reference对象,简称ref对象)

。JS中操作数据:

xxx .value

。模板中读取数据: 不需要.value,直接:

(xxx]}

备注:

。接收的数据可以是: 基本类型、也可以是对象类型

。基本类型的数据:响应式依然是靠object.defineProperty()的get 与set 完成的

。对象类型的数据:内部借助了Vue3.0中的一个新函数 reactive 函数

reactive函数

。作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用 ref 函数)

·语法: const 代理对象 reactive(源对象)接一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)

reactive定义的响应式数据是“深层次的”

内部基于ES6的Proxy 实现,通过代理对象操作源对象内部数据进行操作。

vue2.x的响应式

实现原理:

对象类型: 通过 object.defineProperty() 对属性的读取、修改进行拦截(数据劫持)

数组类型:通过重写更新数组的一系列方法来实现拦截。

(对数组的变更方法进行了包裹)

Object.defineProperty(data, 'count',(get () {},set () {}))

存在问题:

。新增属性、删除属性,界面不会更新

直接通过下标修改数组,界面不会自动更新

Vue3.0的响应式

实现原理:

。通过Proxy(代理): 拦截对象中任意属性的变化,包括: 属性值的读写、属性的添加、性的删除等.

。通过Reflect (反射): 对源对象的属性进行操作。

reactive对比ref

。从定义数据角度对比:

。ref用来定义:基本类型数据

。reactive用来定义: 对象 (或数组)类型数据

。备注: ref也可以用来定义对象 (或数组)类型数据,它内部会自动通过 reactive 转为代理对象。

。从原理角度对比:

。ref通过object.defineProperty()的get 与set 来实现响应式(数据劫持)

。reactive通过使用Proxy来实现响应式 (数据劫持),并通过Reflect操作源对象内部的数据

。从使用角度对比:

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

。reactive定义的数据: 操作数据与读取数据:均不需要 value

setup的两个注意点

setup执行的时机

。在beforeCreate之前执行一次,this是undefined。

setup的参数

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

2.context:上下文对象

2.1attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于 this.$attrs

2.2slots: 收到的插槽内容,相当于 this.$slots。

2.3emit: 分发自定义事件的函数,相当于 this.$emit

计算属性与监视

1.computed函数

与Vue2.x中computed配置功能一致

import { computed } from "vue"
setup(){
  const person = {
      firstName: 'j',
      lastName: 'gs',
  }
      let data = {
      userName: "KUN",
      realName: "CX",
      count: 0,
    };
  
  //计算属性一简写
  let fullName = computed(()=>{
    return person.firstName + '-' + person.lastName
  })
  // 计算属性
  const computedAttr = computed({
    get() {
      console.log("get");
      return `${data.userName}-${data.realName}`;
   },
   set(value) {
     console.log("set");
     const arr = value.split("-");
     data.userName = arr[0];
     data.realName = arr[1];
   },
  });
}

watch函数

。与Vue2x中watch配置功能一致

。两个小“坑”:

。监视reactive定义的响应式数据时: oldValue无法正确获取、强制开启了深度监视 (deep配置失效)

。监视reactive定义的响应式数据中某个属性时: deep配置有效

//情况一:监视ref定义的响应式数据
watch(sum,(newValue ,oldValue)=>(
  console.log("sum变化了 ,newValue,oldValue)
),{immediate:true})
      
//情况二,监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
  console.log("sum或msg变化了,newValue,oldValue)
})
    
/* 情况三,监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue! !
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>(
  console.log("person变化了,newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
    
//情况四,监视reactive定义的响应式故据中的某个属性
watch(()=>person.job,(newValue oldValue)=>{
  console.log("person的job变化了 newValue,oldValue)
},{immediate:true,deep:true})

3.watchEffect函数

。watch的套路是: 既要指明监视的属性,也要指明监视的回调。

。watchEfect的套路是: 不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性.

。watchEffect有点像computed:

。但computed注重的计算出来的值 (回调函数的返回值),所以必须要写返回值

。而watchEffect更注重的是过程 (回调函数的函数体),所以不用写返回值

//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调
watchEffect(()=>{
  const x1 = sum.value
  const x2 = person.age
  console.log('watchEffect配置的回调执行了)
})

Vue3生命周期

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

beforeDestroy 改名为 beforeUnmount

destroyed 改名为unmounted

Vue3.0也提供了 Composition API形式的生命周期钩子,与Vue2.x中钩子对应关系如下

  1. beforeCreate ===> setup()
  2. created =======> setup()
  3. beforeMount ===> onBeforeMount
  4. mounted =======>onMounted
  5. beforeUpdate ===> onBeforeUpdate
  6. updated =======> onUpdated
  7. beforeUnmount ==>onBeforeUnmount
  8. unmounted =====> onUnmounted

自定义hook函数

什么是hook? --本质是一个函数,把setup函数中使用的Composition API进行了封装.

类似于vue2.x中的mixin。

自定义hook的优势: 复用代码,让setup中的逻辑更清楚易懂

toRef

作用: 创建一个 ref 对象,其value值指向另一个对象中的某个属性.

语法:

const name = toRef(person ,‘name’)

应用:

要将响应式对象中的某个属性单独提供给外部使用时

扩展:
toRefs 与toRef 功能一致,但可以批量创建多个 ref对象,语法: toRefs(person)

其它 Composition API

1.shallowReactive 与 shallowRef

。shallowReactive : 只处理对象最外层属性的响应式 (浅响应式)

。shallowRef: 只处理基本数据类型的响应式,不进行对象的响应式处理

。什么时候使用?

。如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ===> shallowReactive。

。如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef.

2.readonly 与 shallowReadonly

。readonly: 让一个响应式数据变为只读的 (深只读)

shallowReadonly: 让一个响应式数据变为只读的 (浅只读)

应用场景:不希望数据被修改时

3.toRaw 与 markRaw

toRaw:

。作用:将一个由 reactive 生成的响应式对象转为普通对象

。使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新

markRaw:

作用: 标记一个对象,使其永远不会再成为响应式对象

应用场景:

1.有些值不应被设置为响应式的,例如复杂的第三方类库等

2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

4.customRef

作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制

实现防抖效果:

<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import { ref, customRef }from 'vue'
export default {
  name: 'Demo',
  setup(){
    let keyword = ref('hello') //使用Vue准备好的内置ref
    //自定义一个myRef
    function myRef(value,delay){
      let timer
      //通过customRef去实现自定义
      return customRef((track,trigger)=>{
        return{
          get(){
            track() //告诉Vue这个value值是需要被”追踪”的
            return value
          }
        },
          set(newValue){
            clearTimeout(timer)
            timer = setTimeout(()=>{
                value = newValue
                trigger(); // 更新界面
            })
         }
      }
    let keyword = myRef('myKeyword', 500) // 防抖
   },

provide 与 inject

作用:实现祖孙组件间通信
适用:父组件有一个 provide 选项来提供数据,子组件有一个 nject 选项来开始使用这些数据
具体写法:
1.祖组件中:

setup(){
  let car = reactive({name:'奔驰', price:'40万')
  provide('car', car)
}

2.孙组件中:

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

响应式数据的判断

isRef: 检查一个值是否为一个 ref 对象

isReactive:检查一个对象是否是由 reactive 创建的响应式代理

isReadonly: 检查一个对象是否是由 readonly 创建的只读代理

isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

新的组件

1.Fragment

在Vue2中: 组件必须有一个根标签

在Vue3中: 组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中

好处: 减少标签层级,减小内存占用

Teleport

什么是Teleport? Teleport 是一种能够将我们的组件html结构移动到指定位置的技术

<teleport to="移动位置">
  <div v-if="isShow" class="mask">
    <div class="dialog">
      <h3>我是一个弹窗</h3>
    <button @click="isShow = false">关闭弹窗</button>
    </div>
  </div>
</teleport>

Suspense

等待异步组件时渲染一些额外内容,让应用有更好的用户体验

使用:

import {defineAsyncComponent] from 'vue!
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))

<template>
  <div class="app">
    <h3>我是App组件</h3>
    <Suspense>
      <template v-slot:default>
        <Child/>
      </template>
      <template v-slot:fallback>
        <h3>加载中.....</h3>
      </template>
    </Suspense>
  </div>
</template>

全局API的转移

Vue 2x有许多全局API和配置

例如:注册全局组件、注册全局指令等

在这里插入图片描述在这里插入图片描述

其他改变

data选项应始终被声明为一个函数,过度类名的更改,移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes,移除 v-on.native 修饰符,移除过滤器 (filter)

### Vue3 学习教程官方文档 Vue3Vue.js 的最新版本,相较于 Vue2 提供了许多新特性和改进。以下是关于 Vue3 学习资源的相关说明: #### 官方文档 Vue3 的官方文档提供了详尽的学习材料和技术细节,适合初学者以及有经验的开发者深入研究。可以访问以下链接获取完整的官方文档: - **英文版**: [https://v3.vuejs.org/guide/introduction.html](https://v3.vuejs.org/guide/introduction.html)[^2] - **中文版**: [https://staging-cn.vuejs.org/guide/](https://staging-cn.vuejs.org/guide/) 这些文档涵盖了 Vue3 的核心概念、API 使用方法、迁移指南等内容。 #### 教程推荐 除了官方文档外,还有一些优秀的第三方教程可以帮助理解 Vue3 的特性: 1. **Runoob Webpack 入门教程** 虽然主要讲解 Webpack,但也涉及前端框架的基础配置,适用于了解如何集成 Vue3 到项目中[^1]。 2. **Vue3 SFC 模式 Setup 属性** Vue3 中新增了 `setup` 函数作为 Composition API 的入口点,这使得代码逻辑更加模块化和可维护。对于熟悉 Vue2 的开发者来说,可能需要额外时间适应这一变化。 #### 示例代码解析 下面是一个简单的例子展示如何通过 `provide/inject` 实现跨组件通信[^3]: ```javascript // grandf.vue <template> <h3>{{ car.name }} -- {{ car.price }}</h3> </template> <script> import { reactive, provide } from &#39;vue&#39;; export default { setup() { const state = reactive({ name: &#39;奔驰&#39;, price: &#39;40W&#39; }); provide(&#39;car&#39;, state); // 向后代组件提供数据 return { ...state }; // 将响应式对象解构返回 } }; </script> ``` ```javascript // son.vue <template> <h3>{{ car.name }} -- {{ car.price }}</h3> </template> <script> import { inject } from &#39;vue&#39;; export default { setup() { const car = inject(&#39;car&#39;); // 接收来自祖先组件的数据 return { car }; } }; </script> ``` 上述代码展示了如何利用 `provide` 和 `inject` 方法实现祖孙组件之间的状态共享。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值