Vue3基础知识2

目录

1.组合式API

1.组合式 API 基础

1.带 ref 的响应式变量

2.watch 响应式更改

3.watch和watchEffect的区别:

 4.独立的 computed 属性

5.生命周期钩子

2.Setup

1.Props

2.Context

3.使用渲染函数

4.访问组件的 property

3.Provide / Inject

1.Provide

2.Inject

3.添加响应性

 4.在单文件组件(SFC)中使用组合式API的编译时语法糖

4.单文件组件script setup

1.基本语法

2.Vue Router

1.安装和引用

2.使用

router-view

3.带参数的动态路由匹配

4.404页面

5.在参数中自定义正则

可重复的参数

6.嵌套路由

7.编程式导航

1.导航到不同的位置

2.替换当前位置

8.命名路由

9.命名视图

9.重定向和别名

1.重定向

2.别名

10.将 props 传递给路由组件

1.布尔模式

 2.defineProps 和 defineEmits

3.命名视图

11.不同的历史模式

1.Hash 模式(有#)

2.HTML5 模式(无#)


1.组合式API

使用 (datacomputedmethodswatch) 组件选项来组织逻辑通常都很有效。然而,当组件开始变得更大时,逻辑关注点的列表也会增长,这会导致组件难以阅读和理解。

这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。

如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的。 

//  组合式API,将同一个逻辑关注点相关代码收集在一起
// msg的逻辑代码
// 操作msg
const msg='hello',
function changeMsg() {
  
}

1.组合式 API 基础

为了开始使用组合式 API,需要一个可以实际使用它的地方。在 Vue 组ttt件中,将此位置称为 setup

  • 新的 setup 选项在组件被创建之前执行,一旦 props 被解析完成,它就将被作为组合式 API 的入口
  • 因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。即在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。
  • 在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。(组件创建之前被调用,数据代理data还没有开始,所以不能拿到这个数据
  • 将 setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
 setup () {//组件被创建之前执行,不需要使用this,this不会指向实例
    console.log('setup')
    // msg的逻辑代码
    let msg = 'he'
    console.log(msg)//he
    function changeMsg () {
      msg = 'nihao'
      console.log(msg)//nihao   说明数据不是响应式的

    }
    return { msg, changeMsg }

  },
  beforeCreate () {
    console.log('beforeCreate')
  },
  created () {
    console.log('created')
  },
  // setup beforeCreate created
  // setup取代了beforeCreate created

 
 

{{ msg }}

点击按钮后,页面没有改变,因为变量msg是非响应式的,从用户角度来看,仓库列表始终为空。

1.带 ref 的响应式变量

ref 为我们的值创建了一个响应式引用。ref定义number和string

  • 通过一个新的 ref 函数使任何响应式变量在任何地方起作用;
  • ref 接收参数并将其包裹在一个带有 value property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值;
  • 将值封装在一个对象中,为了保持 JavaScript 中不同数据类型的行为统一,(在 JavaScript 中,Number 或 String 等基本类型是通过值而非引用传递的)
  • 在任何值周围都有一个封装对象,这样我们就可以在整个应用中安全地传递它,而不必担心在某个地方失去它的响应性。
import { ref } from 'vue'
  setup () {
    // 通过ref定义响应式变量
    const counter = ref(0)//ref()是一个函数,返回带有value属性的对象
    function changeCounter () {
      // counter++不能直接改,可以改value属性
      counter.value++
    }
    return { counter, changeCounter }
  },
 
    

{{ counter }}

reactive :定义引用数据类型,定义对象

import { reactive } from 'vue'

setup(){
 // 通过reactive定义响应式引用类型的数据
    // obj的逻辑代码
    const obj=reactive({
      name:'zz',
      age:10
    })
    function changeObjName() {
      obj.name='ls'
    }
    return { obj,changeObjName }
}

{{ obj.name }}

toRefs使解构后的数据,重新获得响应式

import { reactive, toRefs } from 'vue'
const obj = reactive({
      name: 'zz',
      age: 10,
      childern: {
        name: '里面'
      }
    })
    function changeObjName () {
      obj.name = 'ls'
    }
// toRefs(object)使解构后的数据,重新获得响应式
    // 法一:
    let { name, childern } = toRefs(obj)
    return { obj, changeObjName, name, childern }

    // 通过ES6扩展运算符进行解构使得对象中的属性不是响应式的,需要使用toRefs 
    // 法二:
    return { obj, changeObjName, ...toRefs(obj) }

{{ obj.name }}

{{ name }}

2.watch 响应式更改

就像我们在组件中使用 watch 选项并在 user property 上设置侦听器一样,我们也可以使用从 Vue 导入的 watch 函数执行相同的操作。

它接受 3 个参数:

  • 一个想要侦听的响应式引用或 getter 函数
  • 一个回调
  • 可选的配置选项
import { ref, reactive, watch, watchEffect } from 'vue'
 // 组合式API:将同一个逻辑关注点相关代码收集在一起
  setup () {
    const counter = ref(0)
    function changeCounter () {
      counter.value++
    }
    const user = reactive({
      name: 'zz',
      age: 10
    })
    function changeUserName () {
      user.name = 'ls'
    }
    // watch(侦听的响应式引用,回调函数)
    watch(counter, (newVal, oldVal) => {
      console.log("newVal---", newVal)
      console.log("oldVal---", oldVal)

    })

    watch(user, (newVal, oldVal) => {
      console.log("newVal---", newVal)
      console.log("oldVal---", oldVal)
      // newVal--- Proxy {name: 'ls', age: 10}
      // oldVal--- Proxy {name: 'ls', age: 10}
      
    })
    // 需要深度监听,才能监听到对象中的属性->watchEffect
      //  watchEffect(回调函数)注意:不需要指定监听的属性,组件初始化的时候会执行一次回调函数,自动收集依赖
    watchEffect(() => {
      console.log(user.name)//ls
    })
    return { counter, user, changeCounter, changeUserName }
  },

 // 选项式API
  watch: {
    message: function (newVal, oldVal) {
    }
  },

 

{{ counter }}

{{ user.name }}

3.watch和watchEffect的区别:

  • watchEffect不需要指定监听的属性,自动收集依赖,只要在回调中引用到了响应式的属性,只要这些属性发生改变,回调就会执行;watch只能监听指定的属性,做出回调函数的执行,可以监听多个,vue3
  • watch可以获取到新值和旧值,watchEffect拿不到
  • watchEffect在组件初始化的时候就会自动执行一次,用来收集依赖;watch不需要,一开始就指定了

 4.独立的 computed 属性

与 ref 和 watch 类似,也可以使用从 Vue 导入的 computed 函数在 Vue 组件外部创建计算属性。

  • 给 computed 函数传递了第一个参数,它是一个类似 getter 的回调函数,输出的是一个只读响应式引用
  • 为了访问新创建的计算变量的 value,我们需要像 ref 一样使用 .value property。
import { ref, reactive, computed } from 'vue'
export default {
  // 组合式API:将同一个逻辑关注点相关代码收集在一起
  setup () {
    const counter = ref(0)
    const msg = ref('hello')
    // 在外面使用computed
    const reverseMsg = computed(() => {//返回一个带有value属性的对象
      return msg.value.split('').reverse().join('')
    })
    console.log(reverseMsg.value)//olleh

    const user = reactive({
      name: 'zz',
      age: 10,
      // 在里面使用computed
      reverseMsg: computed(() => {
        return msg.value.split('').reverse().join('')
      })
    })
    console.log(user.reverseMsg)//olleh
    return { counter, user, msg }
  },

  // 选项式API
  computed: {
    reverseMsg: function () {
      return this.message.split('').reverse().join('')
    }
  },
}

5.生命周期钩子

可以通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。

import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated } from 'vue'
export default {

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

2.Setup

使用 setup 函数时,它将接收两个参数:

  1. props
  2. context

1.Props

setup 函数中的第一个参数是 props。正如在一个标准组件中所期望的那样,setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。

App.vue 

data () {
    return {
      message: 'hello'
    }
}, 

Content.vue

 props: {
    message: {
      type: String,
      default: 'nihao'
    }
  },
  setup (props) {
    console.log(props)//Proxy {message: 'hello'}
    console.log(props.message)//hello
  },
  data () {
    return {
      counter: 0
    }
  },
  // mounted () {
  //    console.log(this.message),在setup中拿不到值
  // }

因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性

//App.vue
message: 'hello'



//Content.vue
import { onUpdated, toRefs } from 'vue'
 props: {
    message: {
      type: String,
      default: 'nihao'
    }
  },
  setup (props) {
    const { message } = toRefs(props)
    // console.log(message)需要使用value(message拿到的是对象的形式)
    console.log(message.value)//hello
    // 点击后没有执行setup,使用onUpdated
    onUpdated(() => {
      console.log(message)//拿到的还是旧的值hello,不是响应式的,使用toRefs
      console.log(message.value)//nihaoa
    })
  },

{{ message }}

如果没有传的话,toRefs将不会为message创建一个ref。你需要使用toRef替代它:

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

2.Context

传递给 setup 函数的第二个参数是 contextcontext 是一个普通 JavaScript 对象,暴露了其它可能在 setup 中有用的值:

export default {
  setup(props, context) {
    // Attribute (非响应式对象,等同于 $attrs)
    //App.vue
    console.log(context.attrs)//可以将组件中的属性,放在这里展示
    // 插槽 (非响应式对象,等同于 $slots)
    console.log(context.slots)
    // 触发事件 (方法,等同于 $emit)
    console.log(context.emit)
    // 暴露公共 property (函数)
    console.log(context.expose)
  }
}
触发事件

 




//Content.vue

context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构。

export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}

attrs 和 slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。请注意,与 props 不同,attrs 和 slots 的 property 是响应式的。如果你打算根据 attrs 或 slots 的更改应用副作用,那么应该在 onBeforeUpdate 生命周期钩子中执行此操作。

3.使用渲染函数

如果 setup 返回一个对象,那么该对象的 property 以及传递给 setup 的 props 参数中的 property 就都可以在模板中访问到。返回函数出去

  • 返回一个渲染函数将阻止我们返回任何其它的东西。从内部来说这不应该成为一个问题,但当我们想要将这个组件的方法通过模板 ref 暴露给父组件时就不一样了。
  • 我们可以通过调用 expose 来解决这个问题,给它传递一个对象,其中定义的 property 将可以被外部组件实例访问。
  • 子级想要渲染函数,也想要暴露数据出去。在setup中返回渲染函数,又想要在父组件中执行数据(子组件把20传给父组件)

 


4.访问组件的 property

执行 setup 时,你只能访问以下 property:

  • props
  • attrs
  • slots
  • emit

换句话说,你将无法访问以下组件选项:

  • data
  • computed
  • methods
  • refs (模板 ref)

3.Provide / Inject

1.Provide

在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 来定义每个 property。

provide 函数允许你通过两个参数定义 property:

  1. name ( 类型)
  2. value

 父组件

import { provide } from 'vue'
export default {
  setup () {
    provide('name', 'zz')
    return
  },
}

2.Inject

在 setup() 中使用 inject 时,也需要从 vue 显式导入。导入以后,我们就可以调用它来定义暴露给我们的组件方式。

inject 函数有两个参数:

  1. 要 inject 的 property 的 name
  2. 默认值 (可选)

 子组件

{{ name }}

import { inject } from 'vue' export default { setup () { const name = inject('name') return { name } } }

通过provide和inject可以实现通信。(直接通过provide传过去的值,不是响应式的)

3.添加响应性

为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用ref或reactive。

import { provide, ref } from 'vue'
export default {
  setup () {
    const name = ref('zz')
    provide('name', name)//不要.value,因为它返回的是一个对象
    function changeName () {
      name.value = 'ls'
    }
    return { changeName }
  },
  components: {
    Content
  }
}


当父组件里面的值发生改变时,子组件也会跟着改变。(如果这两个 property 中有任何更改,子组件也将自动更新)

 4.在单文件组件(SFC)中使用组合式API的编译时语法糖

Vue 单文件组件(又名 *.vue 文件,缩写为 SFC)是一种特殊的文件格式,它允许将 Vue 组件的模板、逻辑  样式封装在单个文件中。

4.单文件组件script setup

  • 里面的代码会被编译成组件 setup() 函数的内容。这意味着与普通的 
  • 响应式状态需要明确使用响应式 APIs 来创建。和从 setup() 函数中返回值一样,ref 值在模板中使用的时候会自动解包。

  • 
    
    

 

单文件组件 | Vue.js

SFC 语法规范 | Vue.js

单文件组件 vue3--组合式API

4.404页面

只要没有匹配的页面,则显示404页面。(当上面路径都没有匹配的话,则执行404页面)

import NotFound from "../views/NotFound.vue"

{
    //404页面,上面的页面为动态路由
    // 使用正则的方式,匹配任意的
    path: '/:path(.*)',
    component: NotFound
  },

5.在参数中自定义正则

 对参数由一定的限定。

 import News from "../views/News.vue"

{
    // 动态路由的参数一定是数字
    path: '/news/:id(\\d+)',http://localhost:3000/#/news/456/213
    // 由多个参数
    path: '/news/:id+',//http://localhost:3000/#/news/456/213/guuk
    // 参数可有可无 * 参数可以重复叠加
    path: '/news/:id*',//http://localhost:3000/#/news/
    // 参数可有可无 ? 参数不可以重复叠加
    path: '/news/:id?',

    component: News
  },

可重复的参数

如果你需要匹配具有多个部分的路由,如 /first/second/third,你应该用 *(0 个或多个)和 +(1 个或多个)将参数标记为可重复:

const routes = [
  // /:chapters ->  匹配 /one, /one/two, /one/two/three, 等
  { path: '/:chapters+' },
  // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
  { path: '/:chapters*' },
]

6.嵌套路由

一些应用程序的 UI 由多层嵌套的组件组成。

在这种情况下,URL 的片段通常对应于特定的嵌套组件结构,例如:

/user/johnny/profile                     /user/johnny/posts
+------------------+                  +-----------------+
| User             |                  | User            |
| +--------------+ |                  | +-------------+ |
| | Profile      | |  +------------>  | | Posts       | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+

通过 Vue Router,你可以使用嵌套路由配置来表达这种关系。

//App.vue
Go to parent

//router/index.js
import Parent from "../views/Parent.vue"
import Styleone from "../views/Styleone.vue"
import Styletwo from "../views/Styletwo.vue"

 {
    path: '/parent',
    component: Parent,
    children: [
      {
        path: 'styleone',
        component: Styleone
      }, {
        path: 'styletwo',
        component: Styletwo
      }
    ]
  },

//Parent.vue

Vue3基础知识2_第1张图片

7.编程式导航

除了使用  创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。需要经过判断,才能跳转页面(通过js进行跳转)

1.导航到不同的位置

在 Vue 实例中,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push。 

$route:当前活跃的路由对象,path,params,query,name(当前的)

$router :push,forward,go方法(全局的)

想要导航到不同的 URL,可以使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。

当你点击  时,内部会调用这个方法,所以点击  相当于调用 router.push(...) :

声明式 编程式
router.push(...)

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。


//index.js
  {
    name: 'news',
    path: '/news/:id?',
    component: News
  },
// 通过传入对象
 this.$router.push({ path: "/" })
      // 带参数
 this.$router.push({ path: "/user/123" })
//http://localhost:3000/#/user/123
 this.$router.push({ name: "news", params: { id: 234 } })
//http://localhost:3000/#/news/234
 // 带问号
this.$router.push({ path: "/about", query: { name: "zz" } })
//http://localhost:3000/#/about?name=zz
 mounted () {
    console.log(this.$route.query.name)//zz
    console.log(this.$route)
    //fullPath:当前路径,现在展示出来的路径
    //path:定义的路由
  },

Vue3基础知识2_第2张图片

2.替换当前位置

它的作用类似于 router.push,唯一不同的是,它在导航时不会向 history 添加新记录,正如它的名字所暗示的那样——它取代了当前的条目

声明式 编程式
router.replace(...)

也可以直接在传递给 router.push 的 routeLocation 中增加一个属性 replace: true 。

router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
 // 替换当前位置
this.$router.push({ path: "/about", query: { name: "zz" }, replace: true })
this.$router.replace({ path: "/about", query: { name: "zz" } })


    goBack () {
      // 前进,传入为正值;后退,传入的值为负值
      this.$router.go(-1)
      this.$router.back() //后退,等于go(-1)
      this.$router.forward()//前进,等于go(1)
    }

8.命名路由

除了 path 之外,你还可以为任何路由提供 name。这有以下优点:

  • 没有硬编码的 URL
  • params 的自动编码/解码。
  • 防止你在 url 中出现打字错误。
  • 绕过路径排序(如显示一个)
const routes = [
  {
    path: '/user/:username',
    name: 'user',
    component: User,
  },
]

要链接到一个命名的路由,可以向 router-link 组件的 to 属性传递一个对象:


  User

这跟代码调用 router.push() 是一回事:

router.push({ name: 'user', params: { username: 'erina' } })

在这两种情况下,路由将导航到路径 /user/erina

Go to News
//http://localhost:3000/#/news/234

9.命名视图

想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了。

 访问一个页面显示多个视图。

你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。

如果 router-view 没有设置名字,那么默认为 default

//index.js
{
    path: '/shop',
    components: {
      // 默认显示ShopMain
      default: ShopMain,
// 它们与 `` 上的 `name` 属性匹配
      ShopTop: ShopTop,
      // 简写
      ShopFooter
    }
  },
//App.vue


 

9.重定向和别名

1.重定向

重定向也是通过 routes 配置来完成,下面例子是从 /home 重定向到 /

const routes = [{ path: '/home', redirect: '/' }]

重定向的目标也可以是一个命名的路由:

const routes = [{ path: '/home', redirect: { name: 'homepage' } }]

甚至是一个方法,动态返回重定向目标:

const routes = [
  {
    // /search/screens -> /search?q=screens
    path: '/search/:searchText',
    redirect: to => {
      // 方法接收目标路由作为参数
      // return 重定向的字符串路径/路径对象
      return { path: '/search', query: { q: to.params.searchText } }
    },
  },
  {
    path: '/search',
    // ...
  },
]

 案例

 {
    path: '/',
    // 重定向
    redirect: '/home'
  }, {
    name: 'home',
    path: '/home',
    component: Home
  },
或者反着写
  {
    path: '/home',
    // 重定向
    redirect: '/'
  }, {
    path: '/',
    component: Home
  },
{
    path: '/',
    // 重定向
    // 命名路由
    redirect: { name: "home" }
  }, {
    path: '/home',
    name: 'home',
    component: Home
  },
// 方法:在跳转时需要做判断的时候用
    redirect: (to) => {
      console.log(to)
      return { path: '/home' }
}

2.别名

重定向是指当用户访问 /home 时,URL 会被 / 替换,然后匹配成 /

将 / 别名为 /home,意味着当用户访问 /home 时,URL 仍然是 /home,但会被匹配为用户正在访问 /

上面对应的路由配置为:

const routes = [{ path: '/', component: Homepage, alias: '/home' }]

通过别名,你可以自由地将 UI 结构映射到一个任意的 URL,而不受配置的嵌套结构的限制。使别名以 / 开头,以使嵌套路径中的路径成为绝对路径。你甚至可以将两者结合起来,用一个数组提供多个别名:

{
    path: '/parent',
    alias: '/father',//小名
    alias: ['/father', '/fuqing'],//多个小名
    component: Parent,
}

10.将 props 传递给路由组件

在你的组件中使用 $route 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 props 配置来解除这种行为。

我们可以将下面的代码

const User = {
  template: '
User {{ $route.params.id }}
' } const routes = [{ path: '/user/:id', component: User }]

替换成

const User = {
  // 请确保添加一个与路由参数完全相同的 prop 名
  props: ['id'],
  template: '
User {{ id }}
' } const routes = [{ path: '/user/:id', component: User, props: true }]

这允许你在任何地方使用该组件,使得该组件更容易重用和测试。

1.布尔模式

当 props 设置为 true 时,route.params 将被设置为组件的 props。

{
    path: '/user/:id',
    component: User,
    props:true
  },

//选项式API
export default {
  props: ['id'],
  mounted () {
    console.log(this.$route.params.id)
    console.log(this.id)
  }
}
//组合式API

 2.defineProps 和 defineEmits

单文件组件

  • defineProps 和 defineEmits 都是只在 

你可能感兴趣的:(Vue学习,前端,vue.js,vue)