首页 > 其他分享 >从头再学Vue

从头再学Vue

时间:2022-12-03 16:57:17浏览次数:43  
标签:count 再学 const ref value state Vue 组件 从头

第一篇章

动态绑定多个值

如果有想这样的一个包含多个 attribute 的JavaScript 对象:

const obj = {id:'container', class:"wrap"}

可以通过不带参数的 v-bind 将这些 attribute 绑定到单个元素上:

<!-- 不带参数名的绑定 -->
<div v-bind="obj"></div>

动态参数

<a v-bind:[attributeName]="url"></a>
<!-- 简写 -->
<a :[attributeName]="url"></a>


<a v-on:[eventName]="doSomething"></a>
<!-- 简写 -->
<a @[eventName]="doSomething"></a>
  1. 动态参数值的限制:
  • 动态参数中表达式的值应当是一个字符串,或者 null
  • null 意味着显示移除该绑定,其他非字符串的值会发出警告
  1. 动态参数语法的限制:
  • 在HTML attribute 名称中不能使用 空格和引号,不合法
<!-- 这会触发编译器警告 -->
<a :['foo' + bar]="value"></a>
  • 模板中避免在名称中使用大写字母(浏览器会强制转换为小写)
<!-- someAttr 会转为 someattr , 这将与组件中的 someAttr 属性对应不上 -->
<a :[someAttr]="value"></a>

响应式基础 reactive() 的局限性

  1. 仅对对象类型有效(对象、数组、Map、Set)

  2. 必须始终保持对该响应式对象的相同引用(Vue 的响应式系统是通过属性访问进行追踪的):

  • 不可随意“替换”一个响应式对象。
let state = reactive({count: 0})

// 上面的引用 ({count: 0}) 将不再被跟踪(响应性链接已丢失!)
state = reactive({count: 1})
  • 将响应式对象的属性赋值解构至本地变量时,或者是将该属性作为一个函数参数传入时,会失去响应性。
const state = reactive({count: 0})

// 1. 将响应式对象的属性赋值,失去响应性链接
let n = state.count
n++ // 不影响原始的 state

// 2. count 和 state.count 失去了响应性链接
let {count} = state
count++ // 不影响原始的 state

// 3. 该函数接收一个普通数字,且将无法跟踪 state.count 的变化
say(state.count)

用 ref() 定义响应式变量

  1. 一个包含对象类型值的 ref 可以响应式地替换整个对象:
const objRef = ref({count: 0})

objRef.value = {count: 1}
  1. ref 被传递给函数或者是从一般对象上解构,不会丢失响应性。

ref 在响应式对象中的解包

  1. 当一个 ref 被嵌套在一个响应式对象中,作为属性被访问或更改时,它会自动解包
const count = ref(0)

const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(state.count) // 1
  1. 如果将一个新的 ref 赋值给一个关联了已有 ref 的属性,那么它会替换就的 ref
const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2


// 原始 ref 现在已经和 state.count 失去联系
console.log(count.value) // 1

数组和集合类型的 ref 解包

  1. 跟响应式对象不同,当 ref 作为响应式数组或像 Map 这种原生集合类型的元素被访问时,不会进行解包
const books = reactive([ref('Vue3')])

console.log(books[0].value)

v-if 和 v-show

  1. v-if v-else v-else-if 可以在 <template> 上使用

  2. v-show 会在DOM 渲染中保留改元素,不支持在 <template> 元素上使用

v-if 和 v-for

  1. 当 v-if 和 v-for 同时存在于同一元素上,v-if 会先被执行

这就意味着 v-if 的条件中无法使用 v-for 作用域内定义的变量别名 item

<!-- 会抛出错误,因为属性 item 此时没有在该实例上定义 -->
<li v-for="item in todoList" v-if="!item.done">{{ item.name }}</li>

v-for

  1. v-for 也可用 of 作为分隔符来替代 in

  2. v-for 的变量别名 item 可以使用解构

  3. 可以在 <template> 上使用 v-for

在内联事件处理器中访问事件参数

  1. 传入一个特殊的变量 $event
<button @click="write('OK', $event)">点击</button>
  1. 使用内联箭头函数
<button @click="(event) => write('OK', event)">>点击</button>

注册周期钩子

  1. onMounted() 也可以在一个外部函数中调用,只要调用栈是同步的,且最终起源自 setup() 就可以

watch 惰性执行的:仅当数据源发生变化时才会执行回调

  1. watch 的第一个参数可以是不同形式的数据源:一个 ref(包括计算属性)、一个响应式对象、一个 getter 函数、或者多个数据源组成的数组。
const x = ref(0)
const y = ref(0)


// 单个 ref
watch(x, (value) => console.log(value))

// getter 函数
watch(() =>x.value + y.value, (sum) => console.log(sum))

// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => console.log(newX, newY))
  1. 注意,不能直接侦听响应式对象的属性值,需要用到 getter 函数:
const obj = reactive({count: 0})

// 这是错的,因为 watch() 得到的参数是一个 number
// watch(obj.count, (value) => console.log(value))

// 使用 getter 函数进行监听
watch(() => obj.count, (value) => console.log(value))
  1. 直接给 watch() 传入一个响应式对象,会隐式开启深度监听

/* 在 watch() 直接传入一个响应式对象 */
const obj = reactive({count: 0})

// 在 obj.count 发生变化时触发(隐式开启深度监听),newVal 和 oldVal 是相等的(是同个对象)
watch(obj, (newVal, oldVal) => {})

obj.count++


/* 在 watch() 中传入一个 返回响应式对象的 getter 函数 */
const state = reactive({
  obj: {count: 0}
  })

// 相比,一个返回响应式对象的 getter 函数,只有在返回不同的对象时,才会触发回调(没有开始深度监听)
watch(() => state.obj, () => {})

// 开启深度监听,newVal 和 oldVal 是相等的
watch(() => state.obj, (newVal, oldVal) => {}, {deep: true})

watchEffect()

  1. 会立即执行一遍回调函数

  2. 仅会在同步执行期间追踪所有依赖。在使用异步回调时,只在第一个 await 正常工作前访问到的属性才会被追踪。

watchEffect(async () => {
  const response = await fetch(url.value)
  data.value = await response.join()
})

以上代码中,watchEffect() 仅追踪到 url.value

ref

  1. v-for 中的模板引用:对应的 ref 中包含的值是一个数组,它将在元素被挂载后包含对应整个列表的所有元素

注意:ref 数组并不保证与源数组相同的顺序。

  1. 组件上的 ref

组件

  1. DOM 模板解析注意事项(涉及直接在 [.html文件中] DOM 中编写模板的情况)
  • HTML 标签和属性名是不区分大小写的,需要转换为短横线形式

  • 一定要显式闭合标签

  • 有些 HTML 元素必须在特定的元素中才会显示,例如 <tr> 需要 <table> 标签下才显示,这时候在 <table> 标签下显示组件,就需要用到 is

<table>
  <tr is="vue:组件名"></tr>
</table>

当使用在原生 HTML 元素上时,is 的值必须加上前缀 vue: 才可以被解析为一个 Vue 组件

  1. 当使用 <component :is="..."> 来在多个组件间作切换时,被切换掉的组件会被卸载。
  • 可以通过 <KeepAlive> 组件强制被切换掉的组件仍然保持“存活”的状态。
  1. 全局注册:可以在该应用的任意模板中直接使用,无需再引入
import { createApp } from 'vue'
const app = createApp({})

 // 第一个参数是组件名称,第二个参数是组件的实现(如果是单文件组件,则是被导入的.vue文件)
app.component('componentName', {...})

// 可以链式调用
app.component('componentName', {...}).component('componentName', {...})

props

  1. 在使用 <script setup> 的单文件组件中,props 可以使用 defineProps() 宏来声明:
<script setup>
const props = defineProps(['foo'])

console.log(props.foo)
</script>
  1. 解构 defineProps 的返回值,得到的变量不是响应式的,也不会更新

  2. 在没用使用 <script setup> 的组件中,setup(props) 接收 props 第一个参数

事件

事件声明是可选的,建议声明出来

  1. <script setup> 不能使用 $emit,但 defineEmits() 会返回一个相同作用的函数可供使用
<script setup>
const emit = defineEmits(['submit'])

function buttonClick() {
  emit('submit')
}
</script>
  1. defineEmits() 宏不能在子函数中使用,必须直接放置在 <script setup> 的顶级作用域下

  2. 如果是显式使用了 setup() ,则事件通过 emits 选项来定义,emit 函数也被暴露在 setup() 的上下文对象上(可以被解构出来的)

export default {
  emits: ['submit'],

  setup(props, { emit }) {
    emit('submit')
  }
}

这个 emits 选项还支持对象语法,允许我们对触发事件的参数进行验证(事件校验):

<script setup>
const emit = defineEmits({
  // 没有校验
  click: null,

  // 校验 submit 事件
  submit: ({ email, password }) => {
    if(email && password) return true
    console.log('校验 submit 事件')
    return false
  }
})

const submitForm = (email, password) => {
  emit('submit', { email, password })
}
</script>
  1. 如果 emits 中声明的自定义的事件名与原生事件名一致(如 click),监听器只会监听组件触发的 click 事件

  2. 和原生 DOM 事件不同,组件触发的事件没有冒泡机制

组件配合 v-model 使用

  1. 在组件上使用 v-model (如 <MyComponent v-model="keyword">)会被展开成这种形式:
<MyComponent :modelValue="keyword" @update:modelValue="newValue => keyword = newValue">

要让<MyComponent v-model="keyword">实际工作起来,

  • 方法一:在组件 <MyComponent>接收 modelValue抛出事件 update:modelValue
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>
  • 方法二:在组件 <MyComponent> 中使用可写的,同时具有 getter 和 setter 的计算属性
<script setup>
import { computed } from 'vue'

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const value = computed({
  get() {
    return props.modelValue
  },

  set(value) {
    emit('update:modelValue', value)
  }
})
</script>

<template>
  <input v-model="value" />
</template>
  1. 默认情况下,v-model 在组件上都是使用 modelValue 作为 prop ,并以 update:modelValue 作为对应的事件来更新父组件的值

  2. v-model 的自定义修饰符

自定义修饰符 capitalize 将 v-model 绑定输入的值转为大写 <MyComponent v-model.capitalize="myText" />

组件的 v-model 上的修饰符 capitalize 可以通过 modelModifiers prop 在组件中访问到

在组件中接收 modelModifiers , 如果 v-model 有使用 capitalize 修饰符,modelModifiers 会返回 { capitalize: true }

<script setup>
const props = defineProps({
  modelValue: String,
  modelModifiers: { default: () => ({}) }
})

const emit = defineEmits(['update:modelValue'])

function emitValue(e) {
  let value = e.target.value
  if (props.modelModifiers.capitalize) value = value.toUpperCase()
  emit('update:modelValue', value)
}
</script>

<template>
  <input type="text" :value="modelValue" @input="emitValue" />
</template>

如果是 v-model:title 这种带参数的,生成的 prop 名字是 arg + 'Modifiers'

透传 attributes

指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。

  1. Attributes 继承 常见的:style class id

  2. v-on 监听器继承

  3. 深层组件继承

  • 声明过的 props 和侦听函数不再透传到下一层
  1. 禁用 Attributes 继承
  • inheritAttrs: false
  • 常见场景: attribute 需要应用在根节点以外的其他元素上,可以完全控制透传进来的 attribute 被如何使用

如果使用的是 <script setup> 则需要额外的 <script> 来书写这个声明

<script>
export default {
  inheritAttrs: false
}
</script>
  1. 透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问到
  • 这个 $attrs 对象包含了除组件所声明的 props 和 emits 之外的所有其他 attribute,例如 class,style,v-on 监听器等等
  • 透传 attributes 写法保持原样,如 foo-bar 这样子的 attribute 则需要通过 $attrs['foo-bar'] 来访问
  • 像 @click 这种 v-on 事件监听器的,在 此对象下被暴露为一个函数 $attrs.onClick
  • 没有参数的 v-bind 会将一个对象的所有属性都作为 attribute 应用到目标元素上 v-bind="$attrs"
  1. 单根节点 & 多根节点
  • 单根节点:当一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素上
  • 多根节点:和单根节点组件有所不同,有着多个根节点的组件没有自动 attribute 透传行为,需要显式绑定 $attrs
  1. 在 js 中访问透传 attribute:
  • 使用 useAttrs()
  • setup() 上下文对象的一个属性 attrs (非响应式的,不能监听到变化)

插槽

  1. 动态插槽名:参数使用[]
<template v-slot:[slotName]> </template>

<!-- 简写 -->
<template #[slotName]> </template>
  1. 作用域插槽
  • 父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。

  • 插槽的内容需要同时使用到父组件域和子组件域中的数据,可以像组件传递 props 一样,向插槽的出口上传递 attributes

<slot :text="text"></slot>


<!-- 可以传入整个对象的所有属性 -->
<slot v-bind="item"></slot>

接收插槽 props 时,默认插槽通过子组件标签上的 v-slot 指令,直接接收到一个插槽 props 对象

<MyComponent v-slot="slotProps">
  {{ slotProps.text }}
</MyComponent>

<!-- slotProps 也可以直接解构 -->
<MyComponent v-slot="{ text }">
  {{ text }}
</MyComponent>
  1. 如果混用了具名插槽与默认插槽,则需要为默认插槽使用显式的 <template> 标签

  2. 无渲染组件:包含了逻辑而不需要自己渲染内容的组件,视图输出是通过作用域插槽交给消费者组件了。

依赖注入

  1. provide(提供)为组件后代提供数据
<script setup>
import { provide } from "vue"

provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
</script>

如果不使用 <script setup> ,provide 和 inject 需要确保在 setup() 中是同步调用的:

setup() {
  provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
}
  • 一个组件可以多次调用 provide(),使用不同的注入名,注入不同的依赖值。

  • 可以在应用级提供依赖,所有组件中都可以注入:

app.provide('key', 'value')
  1. inject(注入)注入上层组件提供的数据
const message = inject('message')
  • 如果提供的是一个 ref,注入进来的也会是该 ref 对象,而不会自动解包(保持响应性链接)

  • 注入默认值,第二个参数声明默认值

const value = inject('message', '这是默认值')
  1. 建议尽可能将任何对响应式状态的变更都保持在供给方组件中,如果需要在注入方组件中更改数据,可以在提供方组件中国声明一个可以更改数据的方法

  2. 使用 readonly() 来包装提供(provide)的值,可以确保提供的数据不能被注入方的组件更改

第二篇章

组合式函数

自定义命令

  1. 命名规范:以 v 开头的驼峰式命名变量,在模板中使用短横线方式

  2. 在没有使用 <script setup> 的情况下,自定义指令需要通过 directives 选项注册

  3. 全局注册:app.directive('focus', {...})

  4. 当在组件上使用自定义指令时,它会始终应用于组件的根节点,不推荐在组件上使用自定义指令。

  5. 多根组件时,和 attribute 不同,自定义指令不能通过 v-bind="$attrs" 来传递给一个不同的元素。

插件

  1. 安装插件:app.use(myPlugin, { /* 可选的选项 */ })

** 第三篇章 **

SFC 单文件组件

项目脚手架

  1. Vite 轻量级、速度极快

安装:npm init vue@latest

路由

文档:Vue Router

状态管理

服务端渲染(SSR)

标签:count,再学,const,ref,value,state,Vue,组件,从头
From: https://www.cnblogs.com/4shana/p/16948297.html

相关文章

  • 全局引入element-plus/icons-vue
    安装npminstall@element-plus/icons-vuemain.js...import*asElementPlusIconsVuefrom'@element-plus/icons-vue'for(const[key,component]ofObject.entr......
  • Vue 中的 Ref
    Vue中的Ref1:ref说明<!--##ref属性1.被用来给元素或子组件注册引用信息(id的替代者)2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实......
  • vue-router
    1.安装[email protected]要指定版本号否则会报错2.使用2.1在src下创建router目录,在其中新建index.js文件importVueRouterfrom......
  • uni 结合vuex 编写动态全局配置变量 this.baseurl
    在日常开发过程,相信大家有遇到过各种需求,而我,在这段事件便遇到了一个,需要通过用户界面配置动态接口,同时,因为是app小程序开发,所以接口中涉及到了http以及websocket两个类型......
  • vue 添加多条数据 添加日期
    效果图添加多条数据,日期是具体到天。   后端数据格式time:[{s_time:'',e_time:''}]<pv-for="(item,index)inform.third_extend":key="index"style=......
  • vue scss样式预处理在 vscode 中起作用,但是报错
      2.在最外层级,输入代码  "files.associations":{"*.vue":"vue"} ......
  • vue实现按钮多选
    需求是这样: 首先考虑使用elementui中的组建实现,但是有时候会忽略组建。实现方式两种:1.直接使用element实现letweekTimeData:['星期一','星期二','星......
  • Vue的指令与过滤器
    1.内容渲染指令v-text覆盖标签原有内容{{}}插值表达式v-html2.属性绑定指令v-bind为属性动态绑定值简写':'3.事件绑定指令v-on简写'@'vue提供了内置......
  • vue 项目开发记录1
    ---------------------------jbs开发记录---------------------------1,新建vue-cli项目2,win7不能使用node14以上的版本 1,使用save会在package.json中自动添加。npmi......
  • vue run build打包之后服务器端访问图片404
    记录:assetsRoot:path.resolve(__dirname,BUILD_RESOURCES_DIST),//打包后文件存放的路径assetsSubDirectory:"",//除了index.html之外的静态资源要存放的路径assets......