Vue - vue 前端框架开发技术

vue 介绍

vue 是一种提供一套声明式的,组件化的编程模型来构建前端界面的js前端框架。 vue 目前主流采用vue3,提供组合式API和选项式api,vue2 在2023年12月份以后不再进行维护,它以选项式的api进行应用开发。

基本使用

  1. 创建应用
import { createApp } from 'vue'
import { createPinia } from 'pinia'
// 导入根组件
import App from './App.vue'
// 传入根组件创建应用
const app = createApp(App);
// 注册pinia插件
app.use(createPinia())
// 将应用挂载到容器上
app.mount('#app')
  1. 创建组件
  • 模版语法
<template>
<!-- 文本插值 -->
<span>Message: {{ msg }}</span>
<!-- 属性绑定 -->
<div :id="dynamicId"></div>
<!-- 一次绑定多个属性 -->
<div v-bind="objectOfAttrs" :isDisabled="isDisabled"></div>
<!-- 绑定class-->
<div :class="{ active: isActive }"></div>
<div :class="[activeClass, errorClass]"></div>
<!-- 绑定内联样式-->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div :style="styleObject"></div>
<div :style="[baseStyles, overridingStyles]"></div>
<!-- 条件渲染指令-->
<div v-if="type === 'A'"> A </div>
<div v-else-if="type === 'B'"> B </div>
<div v-else> Not A/B </div>
<!-- 列表渲染 -->
<li v-for="(item, index) in items">
  {{ index }} - {{ item.message }}
</li>
<!-- 渲染对象 -->
<li v-for="(value, key, index) in myObject">
  {{ index }}. {{ key }}: {{ value }}
</li>
<!-- 事件处理 -->
<button @click="greet">Greet</button>
<!-- 事件处理修饰符 -->
<a @click.once="greet"></a>
<!-- 表单输入双向绑定 -->
<input v-model="message" placeholder="edit me" />
<textarea v-model="message" placeholder="add multiple lines"></textarea>
<!-- 复选框 -->
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<!-- 单选框 -->
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>
<!-- 下拉选择 -->
<select v-model="selected">
  <option v-for="option in options" :value="option.value">
    {{ option.text }}
  </option>
</select>
<!-- 修饰符lazy, input事件变为change事件触发-->
<input v-model.lazy="msg" />
<!-- 修饰符number, 输入内容自动转位数字-->
<input v-model.number="age" />
<!-- 修饰符trim, 自动去除输入内容两端的空格-->
<input v-model.trim="msg" />
<!-- 访问模版引用-->
<input ref="input" />

</template>
  • js 语法
<script setup>
import { ref, computed, watch, watchEffect, watchPostEffect, onMounted } from 'vue'
/** 定义响应式属性 */
// 通过ref定义响应式变量
const msg = ref('this is a message');
// 动态属性变量定义
const dynamicId = ref(1);
// 一次绑定多个属性
const objectOfAttrs = ref({
  id: 'container',
  class: 'wrapper'
});

// 使用计算属性
const isDisabled = computed(() => {
  if(dynamicId.value > 1) return true;
  else return false;
});

/** 绑定样式属性  */
// 定义样式属性
const activeClass = ref('active');
const errorClass = ref('text-danger');
// 定义内联样式属性值
const activeColor = ref('red');
const fontSize = ref(30);
const styleObject = ref({
  color: 'red',
  fontSize: '13px'
})

// 条件渲染
const type = ref('A');
// 列表渲染数据
const items = ref([{ message: 'Foo' }, { message: 'Bar' }]);
// 对象渲染数据
const myObject = ref({
  title: 'How to do lists in Vue',
  author: 'Jane Doe',
  publishedAt: '2016-04-10'
});

// 事件处理器
function greet(event) {
  // `event` 是 DOM 原生事件
  if (event) {
    alert(event.target.tagName)
  }
}
/** 表单数据双向绑定 */
// 复选框
const checkedNames = ref([]);
// 单选
const picked = ref('One');
// 下拉选择
const selected = ref('A')
const options = ref([
  { text: 'One', value: 'A' },
  { text: 'Two', value: 'B' },
  { text: 'Three', value: 'C' }
])
/** 注册生命周期钩子 */
// 初始化完成时执行
onMounted(() => { })
// 更新组件时执行
onUpdated(() => {});
// 组件卸载时执行
onUnmounted(() => {});
/** 侦听数据源, 执行副作用 */
const x = ref(0);
watch(x, (newX) => {
  console.log(`x is ${newX}`)
});
// 立即执行,且当 `source` 改变时再次执行
watch(
  source,
  (newValue, oldValue) => {
  },
  { immediate: true }
);
// 自动跟踪响应式依赖,不需要列举
watchEffect(async () => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
  )
  data.value = await response.json()
})
// vue组件更新后访问dom
watchPostEffect(() => { })

/** 访问模版引用 */
const input = ref(null)
onMounted(() => {
  input.value.focus()
})

</script>

父组件传递数据到子组件 (Props, Attributes)

  1. props
  • 父组件
<template>
<BlogPost
  v-for="post in posts"
  :key="post.id"
  :title="post.title"
 />
</template>
<script setup>
const posts = ref([
  { id: 1, title: 'My journey with Vue' },
  { id: 2, title: 'Blogging with Vue' },
  { id: 3, title: 'Why Vue is so fun' }
])
</script>

  • 子组件
<!-- BlogPost.vue -->
<script setup>
defineProps(['title'])
</script>
<template>
  <h4>{{ title }}</h4>
</template>
  1. Attributes (一般用于class,style等属性的传递)
  • 透传到子组件的根元素上
// 父组件
<MyButton class="large" />
// 子组件
<!-- <MyButton> 的模板 -->
<button class="btn">click me</button>
// 最后渲染的结果
<button class="btn large">click me</button>
  • 透传到子组件非根元素 (defineOptions 宏方法)
/**  父组件 */
<MyButton class="large" />

/** 子组件 */
<script setup>
defineOptions({
  inheritAttrs: false
});
</script>
<template>
<div class="btn-wrapper">
  <button class="btn" v-bind="$attrs">click me</button>
</div>
</template>

父组件传递模版内容到子组件 (插槽机制)

  • 子组件
<div class="container">
  <header>
    <slot name="header" message="hello"></slot>
  </header>
  <main>
    <slot name="body"></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
  • 父组件
<BaseLayout>
  <template #header="{message}">
    <h1>Here might be a page title</h1>
    <p>{{message}}</p>
  </template>
  <template #body>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>
  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

子组件执行父组件中的方法 (触发事件)

  • 父组件
<template>
<div :style="{ fontSize: postFontSize + 'em' }">
<BlogPost
  ...
  @enlarge-text="enlargeText"
 />
 </div>
 </template>
 <script setup>
 const postFontSize = ref(1);
 function enlargeText(){
  postFontSize.value  += 0.1;
 };
 </script>
  • 子组件
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <button @click="enlargeText">Enlarge text</button>
  </div>
</template>
<script setup>
const emit = defineEmits(['enlarge-text'])
const enlargeText = () =>  emit('enlarge-text');
</script>

父组件执行子组件中的方法 (defineExpose宏方法)

  • 父组件
<template>
  <Child ref="child" />
</template>

<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
  // child.value 是 <Child /> 组件的实例
  child.value.add(1);
})
</script>
  • 子组件
<script setup>
import { ref } from 'vue'
const a = ref(1);
// 通过defineExpose 暴露数据和方法给父组件
defineExpose({
  a,
  b: (number) => {
    return a.value + number;
  }
})
</script>

父子组件之间的数据双向传递 (defineModel宏方法)

  • 父组件
<MyComponent v-model:title="bookTitle" />
  • 子组件
<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title');
</script>
<template>
  <input type="text" v-model="title" />
</template>

祖先组件为后代组件传递数据 (依赖注入)

  • 祖先组件提供数据
<script setup>
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
  location.value = 'South Pole'
}
provide('location', {
  location,
  updateLocation
})
</script>
  • 后代组件注入数据
<!-- 在注入方组件 -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

组件之间共享数据的方式

  1. pinia
  • 在全局注册pinia
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')
  • 定义store
import { defineStore } from 'pinia'

export const useTodos = defineStore('todos', {
  // 类似于data
  state: () => ({
    todos: [],
  }),
  // 类似于computed计算属性
  getters: {
    finishedTodos(state) {
      return state.todos.filter((todo) => todo.isFinished)
    }
  },
  // 类似于vue2中的methods
  actions: {
    addTodo(text) {
      this.todos.push({ text })
    },
  },
})
  • 使用store中的数据和方法
<script setup>
import { useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 
const store = useCounterStore()
</script>

多个组件之间进行切换(动态组件)

<template>
<component :is="tabs[currentTab]"></component>
</template>

<script setup>
import com1 from './components/com1.vue'
import com2 from './components/com2.vue'
import com3 from './components/com3.vue'
const tabs = {
  'tab1': com1,
  'tab2': com2,
  'tab3': com3,
};
const currentTab = ref('tab1');
</script>

动态异步组件

<script setup>
import { defineAsyncComponent } from 'vue';
const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),
  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,
  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})
</script>
<template>
  <AsyncComp />
</template>

vue中代码重用的方式

  • 单文件组件 (*.vue)
    可以将项目中多处具有相同UI以及处理逻辑的业务模块,封装成一个单文件组件(SFC)
<template>
<button @click="count++">Count is: {{ count }}</button>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>

<style scoped lang="scss">
button {
  font-weight: bold;
}
</style>
  • 组合式函数
    组合式函数是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数, 在vue应用开发中,
    可以将单文件组件中, 重复的状态逻辑处理提取为组合式函数,可以在多个单文件组件中进行重用。
/** 创建组合式函数 */
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
  // 被组合式函数封装和管理的状态
  const x = ref(0)
  const y = ref(0)
  // 组合式函数可以随时更改其状态。
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }
  // 一个组合式函数也可以挂靠在所属组件的生命周期上
  // 来启动和卸载副作用
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))
  // 通过返回值暴露所管理的状态
  return { x, y }
}

/** 在组件中使用组合式函数 */
<script setup>
import { useMouse } from './mouse.js'
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>
  • 自定义指令
    自定义指令主要是为了重用涉及普通元素的底层 DOM 访问的逻辑,一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。
    在组件中定义指令
<script setup>
// 在模板中启用 v-focus
const vFocus = {
  mounted: (el) => el.focus()
}
</script>
<template>
  <input v-focus />
</template>

在应用层全局注册指令

// 在全局注册指令
app.directive('color', (el, binding) => {
  // 这会在 `mounted` 和 `updated` 时都调用
  el.style.color = binding.value
})
// 在组件中使用
<div v-color="color"></div>

编写插件(增强vue全局功能)

  • 编写插件代码
// plugins/i18n.js
export default {
  install: (app, options) => {
    // 将options提供给整个应用可以进行注入访问
    app.provide('i18n', options);
    // 注入一个全局可用的 $translate() 方法
    app.config.globalProperties.$translate = (key) => {
      // 获取 `options` 对象的深层属性
      // 使用 `key` 作为索引
      return key.split('.').reduce((o, i) => {
        if (o) return o[i]
      }, options)
    }
  }
}
  • 全局注册插件
import { createApp } from 'vue'
import i18nPlugin from './plugins/i18n'

const app = createApp({})
app.use(i18nPlugin, {
  greetings: {
    hello: 'Bonjour!'
  }
});
app.mount('#app');
  • 在组件中使用
<template>
<h1>{{ $translate('greetings.hello') }}</h1>
</template>
<script setup>
import { inject } from 'vue'
const i18n = inject('i18n');
console.log(i18n.greetings.hello)
</script>
posted @ 2024-04-13 22:17  箫笛  阅读(12)  评论(0编辑  收藏  举报