• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
返回主页

不完美的完美

  • 博客园
  • 首页
  • 新随笔
  • 联系
  • 订阅
  • 管理

学习vue3.0

vue3

Vite

  • 安装
  • npm init vite@latest -- --template vue
  • npm install
  • npm run dev

动态参数

  • 动态属性

  • v-bind绑定

  • <button @click="attributeName = 'class'">改变属性

  • 动态事件

  • <button @[mouseEvent]="attributeName = 'class'">改变属性

  • <button @click="mouseEvent = 'mouseover'">改变事件

  • computed 计算属性,只要依赖值不变,那么不会重新计算,计算属性基于它们的响应依赖关系缓存,提高性能

  • methids 方法,每当触发重新渲染时,调用方法将始终会再次执行函数

  • computed 简写

    computed: {
    reverseMsg: function() {
    return this.message.split('').reverse().join('')
    }
    }

  • computed 完整写法

    computed{
    // 每一个计算属性都一个getter和setter
    reverseMsg: {
    get: function() {
    return this.message.split('').reverse().join('')
    },
    // 计算属性一般是没有set方法,计算属性是只读属性
    set: function(newValue) {
    // 在设置或者更改计算属性的时候调用
    this.message = newValue
    }
    }
    }
    <button @click="reverseMsg = '你好'">改变reverseMsg

  • watch 简写

    watch: { // 监听数据的变化
    // 每当message发生变化时,就会调用这个函数
    message: function(newValue,oldValue) {
    console.log(newValue,oldValue)
    // 执行异步操作,或者复杂逻辑代码
    }
    }

  • watch 完整写法

    watch: {
    message: {
    immediate: true,// 初始化的时候调用函数
    handler: function(newValue) {
    console.log(newValue)
    }
    }
    }

  • watch 监听不到对象的属性变化,需要使用深度监听

    watch: {
    user: {
    handler: function(newValue) {
    console.log(newValue)
    },
    deep: true // 表示是否深度监听,侦听器会一层层的向下遍历,给对象每个属性都加上侦听器
    },
    "user.name": { // 使用字符串的形式进行优化,只会单独监听对象中对应的属性
    handler: function(newValue) {
    console.log(newValue)
    },
    deep: true
    }
    }

  • v-show 只是简单地切换元素的 display CSS property,带有 v-show 的冤死始终会被渲染并保留在 DOM 中 频繁切换状态

  • v-if 只有后面为 false,对应的元素以及子元素都不会被渲染,控制 dom 元素的创建和销毁, 运行时条件很少改变,一次性

  • key 唯一标识 快速找到节点,减少渲染次数,提升渲染性能

数组更新检测

变更方法

  • push() // 给数组末尾添加元素

  • pop() // 给数组末尾删除元素

  • shift() // 给数组的第一位删除

  • unshift() // 给数组首位开始添加元素

  • splice() // 删除//插入/替换元素

  • sort() // 排序

  • reverse() // 翻转

  • splice 第一个参数,表示插入或者开始删除的元素的位置下标

  • 删除元素

  • 第二个参数,表示传入要删除几个元素(如果没有传,就删除后面所有的元素)

  • 插入元素

  • 第二个元素,传入 0,并且后面接上要插入的元素

  • 替换元素

  • 第二个元素 ,表示替换几个元素,后面的参数表示用于替换前面的元素

list.splice(index,1)

  • 传递事件 @click="xxx(5,$event)"
  • 多事件处理器
  • 由逗号运算符分隔
  • <button @click="one($event),two($event)">

事件修饰符

  • .stop // 阻止事件冒泡

  • .prevent // 阻止默认行为

  • .capture

  • .self

  • .once // 只触发一次回调

  • .passive







    ...



    ...

按键修饰符

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
  • keyCode (键盘编码) keyAlias (键盘的简写) 监听键盘的某个键帽

v-model

  • v-model 基本使用


    {{msg}}

  • v-model 的原理

  • 本质是两个操作

  • 1、v-bind 绑定一个 value 属性

  • 2、v-on 给当前元素添加一个 input 事件

    <input type="text" :value="msg" @input="changeValue" />
    changeValue(e) {
    this.msg = e.target.value
    }

修饰符使用

  • .lazy 当输入框失去焦点,再去同步输入框中的数据
  • <input type="text" v-model.lazy="msg"/>
  • .number 将输入框的内容自动转为数字类型
  • trim 自动过滤用户输入的首尾空白字符

vue 组件

  • 组件的首字母一般大写
  • 组件是带有名称的可复用实例,单独功能模块的封装
  • data 为什么是个函数并且返回 return {}

  • 让每一个组件对象都返回一个新的对象,不会造成数据污染

  • 子传父

  • 在子组件中,通过$emit 来触发事件

  • this.$emit('自定义事件的名称','发送的事件参数')

父子组件的访问方式

  • 父组件访问子组件$refs (常用) $children(vue2.0)
  • ref 用来给元素或者子组件注册引用信息
  • 子组件访问父组件 $parent (尽量少用) 因为组件的复用性很高
  • 子组件访问跟根据 $root

插槽 slot

  • <slot></slot>
  • 如果有多个值,同时放入组件替换时,一起作为替换元素

具名插槽

  • <slot name="xxx"></slot>
  • 使用 <template v-slot:header>xxx</template>
  • v-slot 只能添加到 template
  • <button slot="xxx">按钮</button>
  • 作用域插槽,父组件替换插槽的标签,但是数据由子组件来提供
  • provide/inject 并不是响应式

组合式 API(vue3 新增)

  • 将同一个逻辑关注点相关代码收集在一起
  • setup
  • setup 组件选项
  • beforeCreate 和 created 里的代码应该直接在 setup 函数中编写
  • setup 组件被创建之前执行,不需要使用 this,this 不会指向实例

  • 带 ref 的响应式变量

  • import { ref, reactive, toRefs,watch } from "vue"

  • const count = res(0)

    // 组合式API,将同一个逻辑关注点相关代码收集在一起
    setup() {
    // 组件被创建之前执行,不需要使用this,this不会指向实例
    // msg 的逻辑代码
    // 没有响应式
    let msg = 'hello';
    function changeMsg() {
    msg = 'nihao' // 数据不是响应式
    }

    // 通过ref定义响应式变量
    // counter的逻辑代码
    const counter = res(0) // ref()返回带有value属性的对象
    function changeCounter() {
    counter.value++
    }

    // 通过reactive定义响应式引用类型的数据
    // obj的逻辑代码
    const obj = reactive ({
    name: '张三',
    age: 18,
    children: {
    name: '小张'
    }
    })
    function changeObjName() {
    obj.name = "李四"
    }
    // toRefs(obejct)使结构后的数据重新获得响应式
    let { name,children} = toRefs(obj)

    // 通过es6扩展运算符进行结构使得
    return {msg,changeMsg,counter,changeCounter,obj,changeObjName,...toRefs(obj),name,children}
    }

    {{counter}}

    {{obj.name}}

    {{obj.children.name}}

  • ref 为我们的值创建了一个响应式引用。在整个组合式 API 中会经常使用引用的概念。

  • watch 响应式更改

    import { ref, reactive, watch,watchEffect } from "vue"
    setup() {
    const counter = ref(0);
    function changeCounter() {
    counter.value ++;
    }

    const user = reactive({
    name: '张三',
    age: 18
    })
    function changeUserName() {
    user.name = '李四'
    }
    // watch(侦听的响应式引用,回调函数)
    watch(counter,(newValue,oldValue)=> {
    console.log(newValue)
    console.log(oldValue)
    })
    watch(user,(newValue,oldValue)=> {
    console.log(newValue)
    console.log(oldValue)
    })
    // watchEffect(回调函数)注意不需要指定监听的属性,组件初始化的时候会执行一次回调函数,自动收集依赖
    watchEffect(() => {
    console.log(user.name)
    })
    return { counter,changeCounter,user,changeUserName }

    }

  • watch 和 watchEffect 的区别

  • 1、watchEffect 不需要监听的属性,自动收集依赖,只要在回调中引用到了响应式的属性,只要这些属性发生改变,回调就会执行,watch 只能监听指定的属性,做出回调函数的执行,可以侦听多个,vue3 开始后

  • 2、watch 可以获取到新值和旧值,watchEffect 拿不到

  • 3、watchEffect 在组件初始化的时候就会自动执行一次,用来收集依赖,watch 不需要,一开始就指定了

  • setup 使用 computed

    import { ref, computed } from "vue"
    setup() {
    const counter = ref(0);
    const msg = ref("helloworld");
    const reverseMsg = computed(() => {
    // 返回一个带有value属性的对象
    return msg.value.split("").reverse().join("")
    })
    console.log(reverseMsg.value)
    const user = reactive({
    name: '张三',
    age: 18,
    reverseMsg: computed(() => {
    // 返回一个带有value属性的对象
    return msg.value.split("").reverse().join("");
    })
    console.log(user.reverseMsg)
    return { counter,user,msg }
    })
    }

  • setup 使用生命周期

    import { onBeforeMount, onMounted, onBeforeUpdate,onUpdated } from "vue";

    setup() {
    // 生命周期函数可以使用多次
    //生命周期调用: 函数中有一个参数,回调函数
    onBeforeMount(() => {
    console.log('onBeforeMount')
    })
    onBeforeMount(() => {
    console.log('onBeforeMount')
    })
    return {}
    }

  • setup 函数它将接收两个参数

  • 1、props

  • 2、context

  • 注意: props 是响应式的,你不能使用 ES6 解构,它会消除 props 的响应性

  • 如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作

    import { onUpdated,toRefs } from "vue"
    setup(props) {
    console.log(props)
    console.log(props.xxx)
    const { message } = toRefs(props); // 响应式
    console.log(message.value)
    onUpdated(() => {
    console.log(message.value)

    })
    }

  • 如果 title 是可选的 prop,则传入的 props 中可能没有 title,在这种情况下,toRefs 将不会为 title 创建一个 ref,你需要使用 toRef 替代它

    import { toRef } from "vue"
    setup(props) {
    const title = toRef(props,'title')
    console.log(title.value)
    }

  • Context 是 setup 函数的第二个参数

    setup(props,context) {
    console.log(context)
    // Attribute(非响应式对象,等同于$attrs)
    console.log(context.attrs)
    // 插槽 (非响应式对象,等同与$slots)
    console.log(context.slots)
    // 触发事件(方法,等同于$emit)
    console.log(context.emit)
    // 暴露公共property(函数)
    console.log(context.expose)
    }

  • h 渲染函数

    import { ref,h} from "vue"
    setup(props,context) {
    const counter = ref(20);
    function sendParent() {
    context.emit('injectCounter', counter.value)
    }
    context.expose({
    sendParent, counter
    })
    return () => h('div',counter.value)
    }

  • provide 和 inject 函数

  • 父组件

    import {provide,ref } from "vue"
    setup() {
    const name = ref('张三')
    provide('name',name)
    function changeName() {
    name.value = '李四'
    }
    return {changeName }
    }

  • 子组件

    import { inject } from "vue";
    setup() {
    const name = inject('name')
    console.log(name)
    }

  • setup 语法糖


Vue Router

  • 安装
  • npm install vue-router@4
  • 路由核心:改变 URL,但是页面不进行整体刷新
  • 路由理解为指向
  • 路由表,是一个映射表,一个路由就是一组映射关系,key:value,key:表示路由,value:可以为 function 或者 Component(组件)
  • setup 拿到路由参数

  • vue-router-404 页面

  • path: '/:path(.*)' // 使用正则的方式,匹配任意的

    {
    path: '/:path(.*)',
    component: xxx
    }

  • 路由正则与重复参数

  • path: "/news/:id(\d+)" // 动态路由的参数一定是数字

  • path: "/news/:id+" // 有多个参数

  • path: "/news/:id*" // 参数是可有可无 * 参数可以重复叠加

  • path: "/news/:id?" // 参数是可有可无 ? 参数不可以重复叠加

编程式导航

  • <router-link :to="xxx" /> // 声明式
  • router.push(xxx) // 编程式
  • this.$router.push
  • $router 路由实例对象 push, forword,go 方法
  • $route 当前活跃的路由对象,拿到 path,params,query,name
  • router.push("/users/deuardo") // 字符串路径
  • router.push({path: '/users/deuardo'}) // 带有路径的对象
  • router.push({name: 'user',params: {username: 'eduardo'}}) // 命名的路由,并加上参数,让路由建立 url
  • router.push({ path: '/register', query: { plan: 'private' } }) // 带查询参数,结果是 /register?plan=private
  • router.push({path: '/about',hash: '#team'}) // 带 hash,结果是 /about#team

替换当前位置

  • <router-link :to="..." replace> // 声明式

  • router.replace(...) // 编程式

    router.push({path: '/home',replace: true})
    // 相当于
    router.replace({path: '/home'})

  • 横跨历史

  • router.go(1) // 向前移动一条记录,与 router.forward()相同

  • router.go(-1) // 返回一条记录,与 router.back()相同

  • router.go(3) // 前进 3 条记录

  • router.go(-100) // 如果没有那么多记录,静默失败

  • router.go(100)

命名视图

  • 一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置带上 s




    const router = createRouter({
    history: createWebHashHistory(),
    routes: [
    {
    path: '/',
    components: {
    default: Home,
    // LeftSidebar: LeftSidebar 的缩写
    LeftSidebar,
    // 它们与 <router-view> 上的 name 属性匹配
    RightSidebar,
    },
    },
    ],
    })

重定向

const routes = [
{
path: '/',
// 重定向
redirect: '/home',
// 命名路由
redirect: { name: "home"},
// 方法
redirect:(to) => {
console.log(to)
return { path: '/home'}
}
}
]

别名

  • const routes = [{ path: '/', component: Homepage, alias: '/home' }]
  • alias: ['/people', 'list'] // 起多个名

路由组件传参

  • setup 中接收参数

导航守卫

全局前置守卫

  • 注册一个全局前置守卫 router.beforeEach

    const router = createRouter({...})
    router.beforeEach((to,from,next) => {
    // 返回false已取消导航
    // return false
    next() // 通行证
    })

  • to: 即将要进入的目标
  • from: 当前导航正要离开的路由

路由独享的守卫

const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]

组件内的守卫

  • beforeRouteEnter

  • beforeRouteUpdate

  • beforeRouteLeave

    const UserDetails = {
    template: ...,
    beforeRouteEnter(to, from,next) { // 路由进入组件之前
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 this !
    // 因为当守卫执行时,组件实例还没被创建!
    next((vm) => { // 拿不到实例对象,通过next的回调函数
    console.log(vm.age)
    })
    },
    beforeRouteUpdate(to, from) { // 路由更新组件之前
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /users/:id,在 /users/1 和 /users/2 之间跳转的时候,
    // 由于会渲染同样的 UserDetails 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 this
    },
    beforeRouteLeave(to, from) { // 路由离开组件之前
    // 在导航离开渲染该组件的对应路由时调用
    // 与 beforeRouteUpdate 一样,它可以访问组件实例 this
    },
    }

路由懒加载

  • 路由懒加载,用到时在加载

    // 将
    // import UserDetails from './views/UserDetails.vue'
    // 替换成
    const UserDetails = () => import('./views/UserDetails.vue')

    const router = createRouter({
    // ...
    routes: [{ path: '/users/:id', component: UserDetails }],
    })

状态管理

  • store/index.js

    // 状态集中管理
    // 数据实现响应式
    // ref reactive ---> 对象中存储着状态msg,age,counter
    import { reactive } from "vue"
    const store = {
    state: reactive({// 定义状态
    msg: 'helloworld'
    }),
    updateMsg: function () {
    this.state.msg = '你好'
    }
    }
    // 如何在App组件通过provide提供
    export default store

  • App.vue


  • Home.vue


通过 proxy 解决跨域问题

  • 跨域请求数据,浏览器同源策略的保护机制,通过 proxy 实现跨域请求数据

    proxy: {
    "/": {
    target: 'http:xxx',
    ws: false,
    changeOrigin: true, // 开启代理允许跨越
    },
    },

VUEX

  • store.js

    import { createStore } from "vuex";
    import axios from "axios"
    const moduleA = {
    state() {// 通过$store.state.模块名.状态名获取
    return {
    userName: '用户张三'
    }
    },
    getters: { // 通过$store.getters可以直接获取
    // state局部状态
    userNameAge: function(state,getters,rootState) {
    // getters 是store实例的getters对象
    // rootState 第三个参数 获取跟节点实例的state状态
    return state.userName+',今年18岁了'
    }
    },
    mutations: {// 触发提交函数,直接通过$store.commint即可,actions也是一样
    // state 局部状态
    updateUserName: function(state) {
    state.userName = '李四'
    }

    }
    }
    // 创建store 实例
    const store = createStore({
    state() { // 是存储的单一状态,是存储的基础数据
    return {
    count: 0,
    msg: 'helloworld',
    hotList: []
    }
    },
    // 如果state的状态需要过滤或者其他的操作
    getters: {// 认为是store中的计算属性
    reverMsg: function(state) {
    return state.msg.split("").reverse().join('')
    },
    reverMsgLength: function(state,getters) { // getters表示当前store中的getters对象
    return getters.reverMsg.length
    }
    },
    mutations: { // 通过store.commit 方法触发对应函数状态变更,mutation是同步函数
    increment(state,value) { // state表示上面state 返回的对象,value 表示传过去的参数
    state.count += value
    },
    updateHotList: function(state,vlaue) {
    state.hotList = value;
    }
    },
    actions: {
    getHot: function() {
    fetch('xxx').then((res) => {
    console.log(res)
    })
    },
    //payload 第二个参数
    getHot1: function(context,payload) { // context:与store实例具有相同的属性和方法的对象
    axios.get('xxx').then((res) => {
    console.log(res)
    context.commit('updateHotList',res.data)
    })
    }
    },
    modules: {
    a: moduleA
    }
    })
    export default store

  • 使用

    {{$store.state.count}}

    <button @click="$store.commit('increment',5)">count++

    {{$store.getters.reverMsg}}


    {{$store.getters.reverMsgLength}}


    {{$store.getters.userNameAge}}


    {{$store.state.a.userName}}


    <button @click="$store.commit('updateUserName')">改变名字

    mouted() {
    this.$store.dispatch('getHot')
    this.$store.dispatch('getHot1','hh')
    }

  • vuex 的基本使用-辅助函数

  • mapState 辅助函数

    {{count}}


  • 对象展开运算符

    computed: {
    xxx() {},
    // 使用对象展开运算符将此对象混入到外部对象中
    ...mapState({})
    ...mapState(['count','msg'])
    ...mapGetters(['reverMsg'])
    },
    mouted() {
    console.log(this.count)
    console.log(this.msg)
    }

  • module 命名空间

  • namespaced: true 的方式使其成为带命名空间的模块

  • $store.getters['a/userNameAge']

  • v-for 和 v-if 为什么不建议在一起使用?

  • v-for 优先级高于 v-if,如果将两者放在一块,会先执行 v-for 循环列表,再执行 v-if 去判断,造成性能浪费

  • 一般用计算属性替换掉

  • vue 中如何使用自定义指令?

  • 指令 v-bing v-if v-for v-model v-on 不同的指令实现不同的功能

  • 局部注册和全局注册

  • 局部注册


    // 使用

    directives: { // 自定义指令, 不需要写v-,使用的时候添加v-
    focus: {
    inserted: function (el) {// 表示被绑定的元素插入父节点的时候调用
    //el 表示指令所绑定的元素
    // binding 对象,属性name:指令名 value: 指令的绑定值
    el.focus() // 获取焦点
    }
    }
    }

  • 全局注册

  • main.js

    // 全局注册自定义指令
    Vue.direactive('指令的名字','对象数据也可以是一个指令函数')
    Vue.direactive('focus',{
    inserted: function (el) {// 表示被绑定的元素插入父节点的时候调用
    //el 表示指令所绑定的元素
    // binding 对象,属性name:指令名 value: 指令的绑定值
    el.focus() // 获取焦点
    }
    })

  • vue 中什么是$nextTick?

  • 是将回调函数延迟在下一次 dom 更新数据之后调用

  • vue 是异步渲染的框架,数据更新之后,dom 是不会立刻渲染,$nextTick 会在 dome 渲染之后被触发,用来获取最新的 dom 节点

  • 使用场景:

  • 1、在生命周期函数 create 进行 dom 操作,一定要放到$nextTick 函数中执行(created()中进行 DOM 操作)

  • 2、在数据变化后要执行某个操作,而这个操作需要使用数据变化而变化的 dom 结构时,这个操作需要放到 nextTick 中 (获取数据更新之后的 DOM)

  • 3、获取元素宽度

posted @ 2022-11-22 17:10  不完美的完美  阅读(76)  评论(0)    收藏  举报
刷新页面返回顶部

公告

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3