<!-- `Vue3` -->
1、Vue
项目搭建
npm init vue@latest
cd 文件目录
npm i
npm run dev // npm run _ 这个在package.json中查看scripts
/* vue_study\.vscode可删 // vue_study\src\components也可删除(基本语法,不使用组件) */
// vue_study\.vscode\launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-msedge",
"request": "launch",
"name": "Launch Edge against localhost",
"url": "http:\\localhost:5173",
"webRoot": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge"
}
]
}
2、代码风格
<!-- 还是上面的命令 npm run dev -->
<template>
<div>
msg: {{ msg }}
</div>
<button @click="add"> +++ </button>
</template>
<script>
export default {
data() {
return {
msg: 'Hello Vue!'
}
},
methods: {
add() {
this.msg += '-杨小槿-'
}
},
}
</script>
vue3
阻隔式
<template>
<div>msg: {{ msg }}</div>
<button @click="add"> add </button>
</template>
<script>
import {ref} from "vue";
export default {
setup() {
const msg = ref("杨小槿")
function add(){
msg.value = "xxx"
}
return {
/* msg: '杨小槿' //无法操作 */
msg,
add
}
}
}
</script>
简写
<template>
<div>msg: {{ msg }}</div>
<button @click="add"> add </button>
</template>
<script setup>
import {ref} from "vue";
const msg = ref("杨小槿")
function add(){
msg.value = "xxx"
}
</script>
3、模版语法
插值表达式
<template>
<div>msg: {{ msg }}</div>
<div v-text="msg"></div>
<div v-html="html"></div>
</template>
<script setup>
import {ref} from "vue";
const msg = ref("杨小槿")
const html = ref("<a href='https://www.baidu.com'>百度</a>")
</script>
属性 v-bind
<template>
<div :data-msg="attr" :class="_class" :style="style">msg: {{ msg }}</div>
<div v-bind:data-msg="attr" :class="_class2">msg: {{ msg }}</div>
</template>
<script setup>
import {ref} from "vue";
const msg = ref("杨小槿")
const attr = ref("吃了吗")
const _class = ref("active")
const _class2 = {
/* class对象形式,为true赋值,flase不赋值 */
active: true,
isBanner: true,
active2: false,
}
const style = {"color": "blue"}
</script>
动态属性,用于解构(一般不用)
<template> <div :data-msg="attr" v-bind="bind">msg: {{ msg }}</div> <div v-bind:data-msg="attr" >msg: {{ msg }}</div> </template> <script setup> import {ref, reactive} from "vue"; const msg = ref("杨小槿") const attr = ref("吃了吗") const bind = reactive({ /* 也可以不要reactive() */ m1: "杨小槿", m2: "任小粟", m3: "庆尘", }) </script>
在插值表达式和动态属性中,都可以写一段js
表达式
<div v-bind:data-msg="msg.startsWith('杨小槿') ? '成立' : '不成立'">
msg1: {{msg === "杨小槿" ? "成立" : "不成立"}}
</div>
4、条件渲染
-
v-if
-
v-show
控制css
display: none
<template>
<div class="box1" v-if="isShow" style="width: 20px; height: 20px; background-color: cornflowerblue;"></div>
<div v-else-if="isShow"> 你好 </div>
<div v-else> 你好呀 </div>
<div class="box2" v-show="isShow" style="width: 20px; height: 20px; background-color: cornflowerblue;"></div>
<div> <button @click="isShow=!isShow">显示</button> </div>
</template>
<script setup>
import {ref, reactive} from "vue";
const isShow = ref(true)
</script>
5、循环
遍历列表
<template>
<ul>
<li v-for="item in list" :key="index">{{item}}</li>
</ul>
<ul>
<li v-for="(item, index) in list" :key="index">{{index}}-{{item}}</li>
</ul>
</template>
<script setup>
import {ref, reactive} from "vue";
const list = ref(["杨小槿", "任小粟", "庆尘", "李叔同"])
</script>
解构,列表
<template>
<ul>
<li v-for="(item, index) in list1" :key="item.id">{{item.name}}</li>
</ul>
<ul>
<li v-for="({id, name}, index) in list1" :key="id">{{name}}</li>
</ul>
</template>
<script setup>
import {ref, reactive} from "vue";
const list1 = ref([
{id: 1, name: "杨小槿"},
{id: 2, name: "任小粟"},
{id: 3, name: "庆尘"},
{id: 4, name: "李书同"}
])
</script>
字典
<template>
<ul>
<li v-for="val in dict" :key="val">{{val}}</li>
</ul>
<ul>
<li v-for="(val, key) in dict" :key="key">{{key}}-{{val}}</li>
</ul>
<ul>
<li v-for="(val, key, index) in dict" :key="key">{{index}}-{{key}}-{{val}}</li>
</ul>
</template>
<script setup>
import {ref, reactive} from "vue";
const dict = reactive({
name: "杨小槿",
age: 18,
});
</script>
6、事件
可以用v-on(简写@)
来监听DOM
事件,事件对象是$event
<template>
<div>
<div>count: {{count}}</div>
<button @click="count ++">count++</button>
<button @click="add">add</button>
<button @click="addN(10, $event)">add 10</button>
</div>
</template>
<script setup>
import {ref} from "vue";
const count = ref(0);
function add() {
count.value++
}
function addN(n, e){
count.value+=n
console.log(e)
}
</script>
事件修饰符
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 提交事件将不再重新加载页面 -->
<from @submit.prevent="onSubmit"></from>
<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>
<!-- 也可以只有修饰符 -->
<from @submit.prevent></from>
<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 如:事件处理器不来自子元素 -->
<div @click.self="doThis">...</div>
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
<!-- 滚动事件的默认行为(scrolling)将立即发生而非等待`onScroll`完成 -->
<!-- 以防其中包含`event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>
按键修饰符
<!-- 仅在`key`为`Enter`时调用`submit` -->
<input placeholder="请输入内容" @keydown.enter="submit" />
<div style="background-color: #2c3e50; width: 20px; height: 20px" @mousedown.left="函数名"></div>
<!-- 常用的键盘按键 -->
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
<!-- 鼠标按键 -->
.left
.right
.middle
7、计算属性
<template>
<div>
<div>count: {{count}}</div>
<div>count1: {{count1}}</div>
<div>count2: {{count2(20)}}</div>
</div>
</template>
<script setup>
import {ref, computed} from "vue";
const count = ref(0);
const count1 = computed(()=>{
return count.value + 10
})
const count2 = computed(()=>{
return function(num){
return count.value + num
}
})
</script>
8、watch
监听属性
ref
监听
<template>
<div>
<div>msg: {{msg}}</div>
<input v-model="msg">
</div>
</template>
<script setup>
import {ref, watch} from "vue";
const msg = ref("");
// 可以直接监听一个 ref
watch(msg, async(n, o) => {
// 新值 n,旧值 o
console.log(n, o);
})
</script>
reactive
监听
<template>
<div>
<div>msg: {{data.msg}}</div>
<input v-model="data.msg">
</div>
</template>
<script setup>
import {ref, watch, reactive} from "vue";
const data = reactive({
msg: "",
})
// 可以直接监听一个 ref
watch(data, async(n, o) => {
// 新值 n,旧值 o
console.log(n, o);
})
</script>
<template>
<div>
<div>msg: {{data.msg}}</div>
<input v-model="data.msg">
</div>
</template>
<script setup>
import {ref, watch, reactive} from "vue";
const data = reactive({
msg: "",
})
// 可以直接监听一个 ref
watch(()=>data.msg, async(n, o) => {
// 新值 n,旧值 o
console.log(n, o);
})
</script>
9、生命周期
setup()
在 beforeCreate 和 created 之前执行,创建的是 data 和 method
onMounted()
在组件挂载完成后执行,可以使用dom
onUpdated()
在组件因为响应式状态变更而更新其 DOM 树之后调用
onUnmounted()
在组件实例被卸载之后调用
onBeforeMount()
在组件被挂载之前被调用
onBeforeUpdate()
在组件即将因为响应式状态变更而更新其 DOM 树之前调用
onBeforeUnmount()
在组件实例被卸载之前调用
onErrorCaptured()
在捕获了后代组件传递的错误时调用
<template>
</template>
<script setup>
export default {
setup() {
console.log('setup')
},
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
},
}
</script>
<template>
</template>
<script setup>
import { onBeforeMount, onMounted } from 'vue';
function getData(){
console.log('调用后端接口')
}
console.log('setup1')
onBeforeMount(() => {
console.log('onBeforeMount')
})
onMounted(() => {
console.log('onMounted')
})
console.log('setup2')
getData()
</script>
10、模版引用
需求:用户进入界面,要求自动选择文本输入框
<template>
<div>
<input id="ipt" placeholder="自动选择" />
</div>
</template>
<script setup>
import { onMounted } from 'vue';
onMounted(() => {
const ipt = document.getElementById('ipt');
ipt.focus()
})
</script>
<template>
<div>
<input id="ipt" type="text" ref="defaultIpt" placeholder="自动选择" />
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
const defaultIpt = ref()
onMounted(() => {
defaultIpt.value.focus()
})
</script>
11、vue
组件
-
src\components
新建文件夹- 文件夹下新建
numberCilck.vue
- 文件夹下新建
<!-- numberCilck.vue -->
<script setup>
import { ref } from 'vue';
const number = ref(1)
function click(){
number.value ++
}
</script>
<template>
<div>
<div>{{ number}}</div>
<button @click="click">+</button>
</div>
</template>
<style scoped>
</style>
<!-- src\App.vue -->
<template>
<div>
<numberCilck></numberCilck>
</div>
</template>
<script setup>
import numberCilck from './components/numberCilck.vue';
</script>
全局注册
src\main.js
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import numberCilck from './components/numberCilck.vue'; // 加这个
const app = createApp(App)
app.component('numberCilck', numberCilck) //注册组件, 组件名为numberCilck
app.mount('#app')
12、父传子
<!-- src\App.vue -->
<template>
<div>
<numberCilck :num="10"></numberCilck>
<numberCilck ></numberCilck>
</div>
</template>
<script setup>
</script>
<!-- numberCilck.vue -->
<script lang="ts" setup>
import { ref } from 'vue';
interface Props {
num?: number // num是number类型
}
const props = defineProps<Props>()
const {num=1}=props // num默认值为1
const number = ref(1)
function click(){
number.value += num
}
</script>
<template>
<div>
<div>{{ number}}</div>
<button @click="click">+</button>
</div>
</template>
<style scoped>
</style>
// src\main.js
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import numberCilck from './components/numberCilck.vue';
const app = createApp(App)
app.component('numberCilck', numberCilck) //注册组件, 组件名为numberCilck
app.mount('#app')
<!-- numberCilck.vue -->
<!-- 第二种方法 -->
<script lang="ts" setup>
import { ref } from 'vue';
interface Props {
num?: number // num是number类型
}
const props = withDefaults(defineProps<Props>(), {
num: 1,
})// 定义props类型
const number = ref(1)
function click(){
number.value += props.num
}
</script>
<template>
<div>
<div>{{ number}}</div>
<button @click="click">+</button>
</div>
</template>
<style scoped>
</style>
在子组件里面,不要尝试修改父组件传递来的
props
13、子通知父
用户有没有点击按钮,作为父组件其实是不知道的
如果我想让父组件知道,谁点击了按钮,那么我们可以给子组件编写一个事件
点击按钮之后,就通知父组件
如下,子组件编写
<!-- numberCilck.vue -->
<script lang="ts" setup>
import { c } from 'vite/dist/node/types.d-aGj9QkWt';
import { ref } from 'vue';
interface Props {
num?: number // num是number类型
}
const props = withDefaults(defineProps<Props>(), {
num: 1,
})// 定义props类型
const number = ref(1)
const emits = defineEmits(['plus']) // 定义事件类型
function click(){
number.value += props.num
emits('plus', 1, "你点到了") // 事件名称,
}
</script>
<template>
<div>
<div>{{ number}}</div>
<button @click="click">+</button>
</div>
</template>
<style scoped>
</style>
<!-- src\App.vue -->
<script setup lang="ts">
import numberCilck from './components/numberCilck.vue';
function plus(num: number, msg: string) {
console.log(num, msg);
}
</script>
<template>
<div>
<numberCilck :num="100" @plus="plus"></numberCilck>
<numberCilck ></numberCilck>
</div>
</template>
如果是ts
子组件也可以这么写
<!-- numberCilck.vue -->
<script lang="ts" setup>
import { c } from 'vite/dist/node/types.d-aGj9QkWt';
import { ref } from 'vue';
interface Props {
num?: number // num是number类型
}
const props = withDefaults(defineProps<Props>(), {
num: 1,
})// 定义props类型
const number = ref(1)
interface Emits {
(e:"plus", num: number, msg: string): void
}
const emits = defineEmits<Emits>() // 定义事件类型
function click(){
number.value += props.num
emits('plus', 1, "你点到了") // 事件名称,
}
</script>
<template>
<div>
<div>{{ number}}</div>
<button @click="click">+</button>
</div>
</template>
<style scoped>
</style>
另一种写法
<!-- numberCilck.vue -->
<script lang="ts" setup>
import { c } from 'vite/dist/node/types.d-aGj9QkWt';
import { ref } from 'vue';
interface Props {
num?: number // num是number类型
}
const props = withDefaults(defineProps<Props>(), {
num: 1,
})// 定义props类型
const number = ref(1)
// interface Emits {
// (e:"plus", num: number, msg: string): void
// }
const emits = defineEmits<{
plus: [number, string]
}>() // 定义事件类型
// const emits = defineEmits<Emits>() // 定义事件类型
function click(){
number.value += props.num
emits('plus', 1, "你点到了") // 事件名称,
}
</script>
<template>
<div>
<div>{{ number}}</div>
<button @click="click">+</button>
</div>
</template>
<style scoped>
</style>
14、父传孙
props
透传
- 新建
src\components\child.vue
<!-- src\components\child.vue --> <script lang="ts" setup> </script> <template> <div>孙组件</div> </template> <style scoped> </style>
<!-- numberCilck.vue --> <script lang="ts" setup> import { c } from 'vite/dist/node/types.d-aGj9QkWt'; import { ref } from 'vue'; import Child from './child.vue'; interface Props { num?: number // num是number类型 } const props = withDefaults(defineProps<Props>(), { num: 1, })// 定义props类型 const number = ref(1) // interface Emits { // (e:"plus", num: number, msg: string): void // } const emits = defineEmits<{ plus: [number, string] }>() // 定义事件类型 // const emits = defineEmits<Emits>() // 定义事件类型 function click(){ number.value += props.num emits('plus', 1, "你点到了") // 事件名称, } </script> <template> <div> <Child></Child> <div>{{ number}}</div> <button @click="click">+</button> </div> </template> <style scoped> </style>
<!-- src\components\child.vue --> <script lang="ts" setup> const props = defineProps(['msg']) </script> <template> <div>孙组件{{ props.msg }}</div> </template> <style scoped> </style>
<!-- numberCilck.vue --> <script lang="ts" setup> import { c } from 'vite/dist/node/types.d-aGj9QkWt'; import { ref } from 'vue'; import Child from './child.vue'; interface Props { num?: number // num是number类型 msg: string // msg是string类型 } const props = withDefaults(defineProps<Props>(), { num: 1, })// 定义props类型 const number = ref(1) // interface Emits { // (e:"plus", num: number, msg: string): void // } const emits = defineEmits<{ plus: [number, string] }>() // 定义事件类型 // const emits = defineEmits<Emits>() // 定义事件类型 function click(){ number.value += props.num emits('plus', 1, "你点到了") // 事件名称, } </script> <template> <div> <Child :msg="props.msg"></Child> <div>{{ number}}</div> <button @click="click">+</button> </div> </template> <style scoped> </style>
<!-- src\App.vue --> <script setup lang="ts"> import numberCilck from './components/numberCilck.vue'; function plus(num: number, msg: string) { console.log(num, msg); } </script> <template> <div> <numberCilck :num="100" :msg="'hello'" @plus="plus"></numberCilck> <numberCilck :msg="'world'" ></numberCilck> </div> </template>
为了简化,可以使用provide
和inject
进行依赖注入
<!-- 父组件 -->
<!-- src\App.vue -->
<script setup lang="ts">
import numberCilck from './components/numberCilck.vue';
import {provide} from 'vue'
provide('msg','hello!') // 注入名 值
</script>
<template>
<div>
<numberCilck :num="100"></numberCilck>
<numberCilck></numberCilck>
</div>
</template>
<!-- 中间组件 -->
<template>
<div>
我是中间层
<Child></Child>
</div>
</template>
<script setup lang="ts">
import Child from './child.vue';
</script>
<!-- 孙组件 -->
<!-- src\components\child.vue -->
<script lang="ts" setup>
import {inject} from 'vue'
const msg = inject('msg', "")
// const msg = inject('msg', "默认值”) // 设置默认值
</script>
<template>
<div>孙组件{{ msg }}</div>
</template>
<style scoped>
</style>
15、兄弟组件通信
-
路由参数
-
本地存储
-
vuex
、pinia
-
全局事件总线(需要用到
mitt
第三方包)
标签云组件中,点击某个标签,给当前路由加上查询参数,带上选择的标签
然后在文章列表组件中,监听路由查询参数的变化,有变化就从里面取值
然后再查一遍文章列表
16、v-model
<!-- src\App.vue -->
<script setup lang="ts">
import {ref} from 'vue'
const ipt = ref('hello')
</script>
<template>
<div>
<span>{{ipt}}</span>
<input v-model="ipt">
</div>
</template>
其实vue
背后做了很多事
<!-- src\App.vue -->
<script setup lang="ts">
import {ref} from 'vue'
const ipt = ref('hello')
function iptInput(e : InputEvent){
let val = (e.target as HTMLInputElement).value
ipt.value = val
}
</script>
<template>
<div>
<span>{{ipt}}</span>
<input :value="ipt" @input="iptInput">
</div>
</template>
若自己的组件也想实现v-model
怎么办
单个v-model
<!-- src\components\myIpt.vue -->
<script setup lang="ts">
const props = defineProps(["modelValue"])
const emits = defineEmits(["update:modelValue"])
function input(e: Event): void {
const val = (e.target as HTMLInputElement).value
emits("update:modelValue", val)
}
</script>
<template>
<div>
<span>{{ props.modelValue }}</span>
<input type="text" :value="props.modelValue" @input="input" />
</div>
</template>
<style scoped>
</style>
<!-- src\App.vue -->
<script setup lang="ts">
import {ref} from 'vue'
import MyIpt from './components/myIpt.vue';
const ipt = ref('hello')
</script>
<template>
<div>
<MyIpt v-model="ipt"></MyIpt>
</div>
</template>
多个v-model
<!-- App.vue -->
<script setup Tang="ts">
import {ref} from "vue";
import MyIpt from "@/components/myIpt.vue";
import AModel from "@/components/aModel.vue";
const ipt = ref("你好")
const visible = ref(false)
</script>
<template>
<div>
<my-ipt v-model="ipt"></my-ipt>
<AModal v-model:visible="isible"></AModal>
</div>
</template>
<!-- components/aModel.vue -->
<script setup lang="ts">
const props = defineProps(["visible"])
const emits = defineEmits(["update:visible"])
</script>
<template>
<div>
{{ props.visible }}
<button @click="emits('update:visible', !props.visible)">点我</button>
</div>
</template>
<style scoped>
</style>
17、组件的插槽
默认插槽
<!-- components/mySlot.vue -->
<script setup lang="ts">
</script>
<template>
<div>
<div>模态框头部</div>
<div>
<slot>这里写默认内容</slot>
</div>
<div>模态框尾部</div>
</div>
</template>
<style scoped>
</style>
<!-- App.vue -->
<script setup lang="ts">
import Myslot from "@/components/myslot.vue";
</script>
<template>
<div>
<!-- <my-slot><a>这是我的身体 你快出去</a></my-slot> -->
<my-slot></my-slot>
</div>
</template>
具名插槽
<!-- components/mySlot.vue -->
<script setup lang="ts">
</script>
<template>
<div>
<div>
<slot name="header">模态框头部</slot>
</div>
<div>
<slot name="body">这里写默认内容</slot>
</div>
<div>
模态框尾部
</div>
</div>
</template>
<style scoped>
</style>
<!-- App.vue -->
<script setup lang="ts">
import Myslot from "@/components/myslot.vue";
</script>
<template>
<div>
<my-slot> 我的身体
<template #body>身体</template>
<template v-slot:header>我的头部</template>
</my-slot>
</div>
</template>
动态插槽
<template>
<div>
这里有很多插槽
<div>
<slot name="mo_1">1</slot>
<slot name="mo_2">2</slot>
<slot name="mo_3">3</slot>
<slot name="mo_4">4</slot>
<slot name="mo_5">5</slot>
<slot name="mo_6">6</slot>
<slot name="mo_7">7</slot>
<slot name="mo_8">8</slot>
<slot name="mo_9">9</slot>
</div>
</div>
</template>
<script setup lang="ts">
import syncModal from "@/components/syncModal.vue";
import {computed,ref} from "vue";
const name = ref(1)
function click() {
name.value++
if (name.value === 10) {
name.value = 1
}
}
const slotName = computed(() => {
return "mo_" + name.value
})
</script>
<template>
<div>
<button @click="click">点我</button>
<syncModal>
<template #[slotName]>a</template>
</syncModal>
</div>
</template>
作用域插槽
具名插槽的作用域
<template>
<div>
<slot name="body" msg="这是消息" :age="12"></slot>
</div>
</template>
<msgSlot>
<template #body="data">
这是插槽内部传递的数据:{{ data.msg }} age:{{ data.age }}
</template>
</msgSlot>
默认插槽的作用域(和具名插槽写法不太一样)
<template>
<div>
<slot msg="这是消息" :age="12"></sLot>
</div>
</template>
<msgSlot v-slot="data">
这是插槽内部传递的数据: {{ data.msg }} age:{{ data.age }}
</msgSlot>
<!-- 在有具名插槽情况下 -->
<template>
<div>
<my-slot>
<template #default="d">
身体 {{ d }}
</template>
<template #header="data">
头部 {{ data }}
</template>
</my-slot>
</div>
</template>
Vue
Vue
概述
Vue
是渐进式JavaScript框架
Vue.js
自底向上增量开发的设计
Vue
的核心库关注于视图渐进式:一步一步,不是将所有的东西都学完才能使用
自底向上设计
Vue.js
的核心是允许采用简洁式模板语法来声明的将数据渲染进DOM的系统
Vue
和JQuery
区别数据驱动试图
从
jquery
直接操作dom
到转变为操作数据
jQuery
是使用选择器选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象
Vue
则是通过Vue
对象将数据和View完全分离开来了。对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,他们通过Vue
对象这个vm
实现相互的绑定。这就是传说中的MVVM
1、Vue
基本语法
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!-- 在编辑器上下载vue插件即可有提示 -->
<!-- npm create vue@latest -->
插值表达式
使用{{ }}进行渲染data:{}
中的变量
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">{{ message }}</div>
<script>
const { createApp, ref } = Vue
createApp({
setup() {
const message = ref('Hello vue!')
return {
message
}
}
}).mount('#app')
</script>
<script src="./vue.js"></scripts>
<div id="app">
{{a}}{{dic.name}}{{List[0].name}}
<div>{{ getInfo() }}</div>
</div>
<script>
var vue = new Vue({
el: '#app', // vue挂载
data:{
a: 'huajiang',
dic:{
name:'杨小槿',
age: 18,
},
List:[
{name:'任小粟'},
{name:'杨安京'},
]
},
methods: {
getInfo() {
return 'Hello vue!';
}
}
})
</script>
<!-- 控制台vue.a="sll" -->
2、绑定属性
v-text
渲染文本,和插值表达式类似,也是支持运算符的
<script src="./vue.js"></scripts>
<div id="app">
<div v-text="msg"></div>
<div>{{ msg }}</div>
<div v-text="2 > 4 ? '成立': '不成立'"></div>
</div>
<script>
var vue = new Vue({
el: '#app', // vue挂载
data:{
a: '天空一声巨响,老奴闪亮登场',
msg: "嗨嗨嗨,来喽"
},
})
</script>
v-html
注意:v-html
一般是用来渲染信任的文本,例如文章的详情内容等,最好不要用在用户提交的地方,容易造成XSS
攻击
<script src="./vue.js"></scripts>
<div id="app">
<div v-html="aDemo">{{aDemo}}</div> <!-- v-html编译成html -->
<div v-text="aDemo">{{aDemo}}</div> <!-- v-text直接输出文本 -->
</div>
<script>
var vue = new Vue({
el: '#app', // vue挂载
data:{
a: 'huajiang',
aDemo: '<a href="http://www.baidu.com" target="_blank">百度</a>'
},
})
</script>
v-bind (控制属性, 简写:
)
属性上不能使用插值表达式({``{}}
)
v-bind:
可以简写为 :
<script src="./vue.js"></scripts>
<div id="app">
<div class="name" v-bind:title="a.name">
姓名: {{a.name}}
</div>
<img v-bind:src="src" alt="">
<img :src="src" alt="">
</div>
<script>
var vue = new Vue({
el: '#app', // vue挂载
data: {
a: {
name: "杨小槿",
age: 18,
},
src: 'https://profile-avatar.csdnimg.cn/392dc034291241239877635a77987f39_m0_65534674.jpg!1',
}
})
</script>
v-bind不仅可用于HTML存在的属性,还可以应用在自定义属性上
<p :id="'p' + '_01'" :data="msg" :还可以是中文="msg"></p>
<p id="p_01" data="haha" 还可以是中文="haha"></p>
在vue
所有的指令,都支持表达式
<div class="name" :title="'www'+a.split('.')[1]">
<!-- var a = 1; 语句 -->
{{ a.length === 0 ? '没有数据' : a }} <!-- 表达式 -->
-{{ a.split('.')[1] }}
</div>
3、条件渲染
v-if和v-else的普通使用
v-if中的布尔值为true,则渲染这个div
如果if不成立,则渲染else中的代码块
<script src="./vue.js"></script>
<div id="app">
<div class="name" v-if="flag">{{ num }}</div>
<div id="if">
<div v-if="num > 0.9">大于0.9的概率</div>
<div v-else>v-if不满足显示我</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
num: Math.random(), // 一个0-1之间的随机数字
flag: false,
}
})
</script>
v-else-if多条件语句
<div id="v-else-if">
<div v-if="num > 0.9">大于0.9的概率</div>
<div v-else-if="num > 0.6">大于0.6的概率</div>
<div v-else-if="num > 0.4">大于0.4的概率</div>
<div v-else-if="num > 0.2">大于0.2的概率</div>
<div v-else>所有条件都不成立</div>
</div>
v-show与v-if的区别
-
v-if如果是false,不会渲染
-
v-show如果是false,不会显示
style="display: none;"
<script src="./vue.js"></script>
<div id="app">
<div class="name" v-show="flag">{{ num }}</div>
<!-- style="display: none;渲染之后隐藏了 -->
</div>
<script>
new Vue({
el: '#app',
data: {
flag: false,
}
})
</script>
4、列表渲染
主要分为遍历列表和遍历对象
遍历列表
key要唯一
<div id="app">
<ul>
<li v-for="item in lis" :key="item"> <!-- key要唯一!!! -->
{{ item }}
</li>
<li v-for="(item, index) in lis" :key="item"> <!-- 带索引 -->
{{ index }} -- {{ item }}
</li>
</ul>
</div>
<script>
var vue = new Vue({
el: "#app",
data:{
lis: [
'张三',
'王伟',
'张伟',
'王五',
],
}
})
</script>
遍历对象
-
一个参数,就是遍历对象的值
-
二个参数,值和键
-
三个参数,值,键,索引
<div id="app">
<ul>
<li v-for="item in obj" :key="item">
{{ item }}
</li>
</ul>
<ul>
<li v-for="(item, key) in obj" :key="item">
{{ key }}{{ item }}
</li>
</ul>
<ul>
<li v-for="(item, key, index) in obj" :key="item">
{{ index }}{{ key }}{{ item }}
</li>
</ul>
</div>
<script>
var vue = new Vue({
el: "#app",
data:{
obj:{
name: '张三',
age: 21,
addr: '北京市',
},
}
})
</script>
5、Vue
事件
v-on:或者@
<div id="app">
<div>
{{ num }}
</div>
<div>
<button v-on:click="add">点我 +</button>
<button @dblclick="num--">点我 -1</button> <!-- dblclick双击鼠标 -->
</div>
</div>
<script>
var vue = new Vue({
el: "#app",
data:{
num: 1,
},
methods:{
add(){
this.num++;
}
}
})
</script>
参数问题,可以通过传参进行参数传递
<div id="app">
<div>
{{ num }}
<button v-on:click="add(10)">点我 +</button>
</div>
</div>
<script>
var vue = new Vue({
el: "#app",
data:{
num: 1,
},
methods:{
add(number) {
this.num += number
}
}
})
</script>
默认参数,默认参数就是触发当前事件的事件对象
一定是v-on:click="add"
,只写一个函数名,而不是v-on:click="add()"
,这样获取不到事件对象
<div id="app">
<div>
{{ num }}
<button id="add" class="add_cls" v-on:click="add">点我 +</button>
</div>
</div>
<script>
var vue = new Vue({
el: "#app",
data:{
num: 1,
},
methods:{
add(event) {
this.num++;
// 触发当前事件的事件对象
console.log(event)
// 获取标签
console.log(event.target)
// 获取id,class
console.log(event.target.id)
console.log(event.target.className)
}
}
})
</script>
如果有参数,想接收事件对象,使用$event
进行传递
<script src="./vue.js"></script>
<div id="app">
<div>
{{ num }}
<button id="add" class="add_cls" v-on:click="add(10, $event)">点我 +</button>
</div>
</div>
<script>
var vue = new Vue({
el: "#app",
data:{
num: 1,
},
methods:{
add(number, event) {
this.num += number
// 触发当前事件的事件对象
console.log(event)
// 获取标签
console.log(event.target)
// 获取id,class
console.log(event.target.id)
console.log(event.target.className)
}
}
})
</script>
6、图片轮播案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图片轮播</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="app">
<div>
<img :src="img_list[index]" alt="">
</div>
<div>
<button @click="prevImag(index, img_list.length)">上一张</button>
<button @click="nextImag">下一张</button>
</div>
</div>
<script>
var vue = new Vue({
el: '#app', // vue挂载的位置,不能是body
data: {
index: 0,
img_list: [
'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_1ac33.thumb.400_0.jpg',
'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_48820.thumb.400_0.jpg',
'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_e0b09.thumb.400_0.jpg',
'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_efdaf.thumb.400_0.jpg',
'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_6e9d5.thumb.400_0.jpg',
'https://c-ssl.dtstatic.com/uploads/blog/202210/05/20221005212142_c33fb.thumb.400_0.jpg',
]
},
methods: {
// 下一张
nextImag() {
// 让索引加1
// 判断是不是最后一张,如果是就回到第一张
if (this.index === this.img_list.length - 1) {
this.index = 0
return
}
this.index++
},
// 上一张
prevImag(index, len){
// 我希望把索引和列表长度传递进来
if (index === 0) {
this.index = len - 1
return
}
this.index --
}
}
})
</script>
</body>
</html>
事件修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节
为了解决这个问题,Vue.js
为 v-on
提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!--阻止默认事件-->
<a href="http://www.baidu.com" @click.prevent="my_alert">杨小槿</a>
<script src=".vue.js"></script>
<div id="app">
<a href="http://www.baidu.com" @click="my_alert">百度</a>
</div>
<script>
new Vue({
el: '#app',
methods:{
my_alert(e){
console.log(e.target)
console.log(e.preventDefault()) // event.preventDefault() vent.stopPropagation()
alert("是否跳转")
}
}
})
</script>
事件冒泡
<div class="parent" @click="log('外元素')">
<div class="center" @click="log('中元素')">
<div class="child" @click="log('里元素')">
</div>
</div>
</div>
<!-- 点击里元素,输出里中外(这就是冒泡);点击中,输出中外 -->
<!-- 被父元素包裹的元素,点击事件发送后会逐级将事件向上传递 -->

<p>阻止事件冒泡</p>
<div class="parent" @click="log('外元素')">
<div class="center" @click="log('中元素')">
<div class="child" @click.stop="log('里元素')">
</div>
</div>
</div>
<!-- 添加stop事件修饰符之后,点击里元素,会阻止事件继续向上传播 -->
键盘事件
@keydown 键盘按下
@keyup 键盘回弹
按下回车触发,支持组合键
9、v-model双向数据绑定
<input type="text" v-model="msg">
<input type="text" :value="msg">
...
date:{
msg:"abcd",
}
<div id="app">
<div>
<input type="text" v-model.lazy="msg"> <!-- .lazy事件修饰符,失焦后再改变 -->
<button @click="getMsg">获取</button>
<select v-model="selected">
<!-- 变成多选<select v-model="selected" multiple>,然后把selected改成列表 -->
<option :value="item.value" :key="item.value" v-for="item in select_list">
{{item.label}}
</option>
</select>
</div>
</div>
<script>
var vue = new Vue({
el:'#app',
data:{
msg:"杨小槿",
selected: 2,
select_list:[
{value: 1, label:'麻辣香锅'},
{value: 2, label:'铁锅炖'},
{value: 3, label:'烤肉'},
{value: 4, label:'火锅'},
]
},
methods:{
getMsg(){
console.log(this)
}
}
})
</script>
<input type="text" v-model="msg" @keyup.enter.ctrl="get_msg('上')">
<input type="text" v-model="msg" @keyup.enter="get_msg('上')">
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用
v-on:click.prevent.self
会阻止所有的点击 ,而v-on:click.self.prevent
只会阻止对元素自身的点击。
2.1.4新增
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
按键修饰符
<!-- 最常见的可能就是在一个输入框中,判断用户是否按下了回车键 -->
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<!-- 一些必要的按键名称 -->
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
<!-- 组合按键 -->
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>
7、计算属性
-
调用的时候不用加括号(只是一个属性)
-
可以监听属性变化,属性变化,计算属性重新执行
-
有缓存(多个计算属性,只执行一次)
和methods的区别
属性变化,methods方法全部重新获取
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算属性</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="app">
<div>{{ name }}</div>
<div>{{ name.split('').reverse().join('') }}</div>
<div>{{ name.length === 5 ? '五言绝句' : '七言绝句' }}</div>
<div v-if="name.length === 5">五言绝句</div>
<div v-else-if="name.length === 7">七言绝句</div>
<div v-else>错了</div>
<!-- 计算属性 -->
<div>{{ getName }}</div>
<div>{{ getName }}</div>
<div>{{ get_data_name() }}</div>
<div>{{ get_data_name() }}</div>
<button @click="getsub">点我</button>
<span>{{ num }}</span>
<button @click="set_name">计算属性</button>
</div>
<script>
var vue = new Vue({
el: '#app', // vue挂载的位置,不能是body
data: {
name: '床前明月光',
num: 0
},
methods: {
get_data_name() {
console.log('属性方法')
return this.name.split('').reverse().join('')
},
getsub(e) {
this.num ++
},
set_name(){
this.name += 'a'
}
},
computed: {
getName() {
console.log('计算属性')
return this.name.split('').reverse().join('')
}
}
})
</script>
</body>
</html>
computed与watch,methods的区别
computed:有缓存(多个计算属性,只执行一次),节省了性能开销,计算属性不能传值
methods:可以放入函数,并且没有缓存
watch:监听,当数据发送变化时,才会触发,可以得到现在的值和过去的值,还可以监听路由变化,和属性同名
8、自定义过滤器
计算属性不能传值不好搞
<script src="./vue.js"></script>
<div id="app">
<li v-for="item in student_list" :key="item.id">
{{item.name|getLastChar }} -- {{ item.addr }}
</li>
</div>
<script>
var vue = new Vue({
el: '#app',
data: {
student_list: [
{name: '张三', addr: '北京市海淀区', id: 1 },
{name: '李四', addr: '上海市浦东区', id: 2 },
{name: '王五', addr: '广州市天河区', id: 3 }]
},
// 自定义过滤器
filters:{
// 截取最后一个字符
getLastChar(item)
{
return item.substr(item.length - 1, 1) // .substr是截取字符串的函数
}
}
})
</script>
时间过滤器

<div id="app">
<div>
当前时间:{{ now|get_date }}
</div>
<ul>
<li v-for="item in date_list" :key="item.id">
id:{{ item.id }} 时间:{{ item.date|get_date }}
</li>
</ul>
<div>
时间过滤
</div>
<ul>
<li v-for="item in date_list" :key="item.id">
id:{{ item.id }} 时间:{{ item.date|time_to_filter }}
</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
function getDateDiff(datestamp) {
var publishTime = datestamp / 1000,
d_seconds,
d_minutes,
d_hours,
d_days,
timeNow = parseInt(new Date().getTime() / 1000),
d,
date = new Date(publishTime * 1000),
Y = date.getFullYear(),
M = date.getMonth() + 1,
D = date.getDate(),
H = date.getHours(),
m = date.getMinutes(),
s = date.getSeconds();
//小于10的在前面补0
if (M < 10) {
M = '0' + M;
}
if (D < 10) {
D = '0' + D;
}
if (H < 10) {
H = '0' + H;
}
if (m < 10) {
m = '0' + m;
}
if (s < 10) {
s = '0' + s;
}
d = timeNow - publishTime;
d_days = parseInt(d / 86400);
d_hours = parseInt(d / 3600);
d_minutes = parseInt(d / 60);
d_seconds = parseInt(d);
if (d_days > 0 && d_days < 30) {
return d_days + '天前';
} else if (d_days <= 0 && d_hours > 0) {
return d_hours + '小时前';
} else if (d_hours <= 0 && d_minutes > 0) {
return d_minutes + '分钟前';
} else if (d_seconds < 60) {
if (d_seconds <= 0) {
return '刚刚发表';
} else {
return d_seconds + '秒前';
}
} else if (d_days >= 30) {
return Y + '-' + M + '-' + D + ' ' + H + ':' + m;
}
}
var vue = new Vue({
el: '#app',
data: {
now: '2021-12-8 22:01:11',
date_list: [
{id: 1, date: '2021-12-8 22:02:11'},
{id: 2, date: '2021-12-6 22:02:21'},
{id: 3, date: '2021-12-8 21:01:14'},
{id: 4, date: '2021-12-5 20:12:11'},
]
},
filters: {
// 年-月-日
get_date(date_str) {
// date,传递的参数
// 字符串转时间对象
let date = new Date(date_str)
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
},
time_to_filter(date_str) {
// 字符串->对象->时间戳
// 和当前时间做差,算出相差的时间(多少秒,多少分)
// 字符串转时间对象->时间戳
let datestamp = new Date(date_str).getTime()
return getDateDiff(datestamp)
}
}
})
</script>
参数问题
自定义过滤器也是可以接受参数,它的书写语法是
<script src="./vue.js"></script>
<div id="app">
<li v-for="item in date_list" :key = "item.id" >
id: {{ item.id }}
时间: {{ item.date | time_to_filter('a', 'b') }}
</li>
</div>
<script>
var vue = new Vue({
el: '#app',
data: {
date_list: [
{id: 1, date: '2021-01-01 12:00:00'},
{id: 2, date: '2021-01-02 13:00:00'},
]
},
// 自定义过滤器
filters: {
time_to_filter(date_str, a, b)
{
// date_str -> '自身的值'
// a -> 'a'
// b -> 'b'
return date_str
}
}
})
</script>
过滤器方法的第一个参数就是调用者本身的值,第二个参数之后就是调用的实参,并且支持链式过滤器
<div>{{now|get_now|get_now1}}</div>
全局方法
需要将属性和方法挂载到Vue.prototype
上
Vue.prototype.$com = "全局变量"
let global_method = () => {
return "全局方法"
}
function add() {
return "全局 add 方法"
}
Vue.prototype.$global_method = global_method
Vue.prototype.$add = add
使用
<div>
{{ $global_method() }}
</div>
<div>
{{ $add() }}
</div>
全局过滤器
// 全局过滤器
let global_filter = (item) => {
return item + '--global'
}
// 注册全局过滤器 (过滤器名称,方法)
Vue.filter('global_filter', global_filter)
使用全局过滤器
<div>
{{ $com|global_filter }}
</div>
10、vue
组件
内容共同使用,但是数据是相互隔离的
components
局部组件
<script src="./vue.js"></script>
<div id="app">
<App></App> <!--使用子组件-->
</div>
<script>
// app 组件 html css js
const App = {
template: `
<div>
<h3>我是app组件</h3>
<p>{{ msg }}</p>
<button @click="C">按一下改变msg</button>
</div>`,
data() {
return {msg: 'hello'}
},
methods: {
C() {
this.msg = 'world'
}
},
created(){ // 只要有组件调用就会执行
console.log(1)
},
}
new Vue({
el: '#app',
data: {},
components: {
App // 挂载子组件
}
})
</script>
全局组件
<div id="app">
<App></App>
</div>
<script>
Vue.component('Vheader', { // component注册
template: `
<div>
<span>我是导航组件</span>
</div>`})
Vue.component('Vsider', {
data() {
return {msg: '侧边栏'};
},
template: `
<div>
<span>我是侧边栏的msg: {{ msg }}</span>
</div>`,})
const Vbtn = {
template: `<button>按钮</button>`
}
const App = {
template: `
<div>
<Vheader></Vheader>
<Vbtn></Vbtn>
<Vsider></Vsider>
<h3>我是app组件</h3>
<p>{{ msg }}</p>
<button @click="C">按钮</button>
</div>`,
data() {
return {msg: 'hello'}
},
components: {
Vbtn
},
methods: {
C() {
this.msg = 'world'
}
}
}
new Vue({
el: '#app',
data: {},
components: {
App
}
})
</script>
11、组件通信
父传子 props
父传子:通过props
来进行通信
1. 在自组件中声明props接收在父组件挂载的属性
2. 可以在自组件的template在任意使用
3, 在父组件绑定自定义的属性
props传入一个对象
// 例: props的值 props: ['title', 'likes', 'isPublished', 'commentIds', 'author'] props: { title: String, likes: Number, isPublished: Boolean, commentIds: Array, author: Object, callback: Function, contactsPromise: Promise // or any other constructor } props: { data: { type: String, // 类型 required: true, // 必填项 default: '张三' // 如果没传值,那么这个默认值就是它 } }
<div id="app">
<App></App> <!--使用子组件-->
</div>
<script>
Vue.component('Child', { // 定义子组件
template: `
<div>
<h3>这是子组件</h3>
<h3> 222 {{ childData }} </h3>
</div>
`,
props: ['childData'] // 接收父组件传递的属性
})
const App = { // 定义父组件
data() { // 父组件的数据
return {msg: '111'}
},
template: `
<div>
<Child :childData="msg"></Child>
<p>{{ msg }}</p>
</div>
`,
}
new Vue({
el: '#app',
data: {},
components: {
App // 注册父组件
}
})
</script>
created与mounted
created(){ console.log(this.data, 1) }, mounted(){ console.log(this.data, 2) }
子传父
1. 在父组件中,自组件上绑定自定义事件
2. 在自组件中,触发原生的事件,在事件函数通过this.$emit触发自定义的事件
<div id="app">
<div> 父组件 {{num}} </div>
<div>
<my_header v-on:add2="parent_add"/>
</div>
</div>
<script>
let my_header = {
template:`
<div>
<button @click="add1">父组件值+1</button>
</div>`,
data(){
return{}
},
methods:{
add1(){
this.$emit('add2', {msg: '来自子组件的数据'})
}
},
}
new Vue({
el: '#app',
data:{
num:10
},
components:{
my_header
},
methods:{
parent_add(data){
console.log(data)
this.num += 1
}
}
})
</script>
平行组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>平行组件</title>
<script src="./vue.js"></script>
<style>
body {
margin: 0;
}
#app {
display: flex;
}
#app > div {
width: 50%;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
}
#gouwuc {
background-color: #3a8ee6;
}
#get {
background-color: #b3d9d9;
}
</style>
</head>
<body>
<div id="app">
<App1 id="gouwuc"></App1> <!--使用子组件-->
<App2 id="get"></App2> <!--使用子组件-->
</div>
<script>
let Bus = new Vue()
let App1 = {
data() {
return {
num: 100
}
},
template:`
<div>
购物车 商品总数{{ num }}
</div>`,
mounted(){
Bus.$on('add2', (data)=>{
this.num ++
})
}
}
let App2 = {
template: `
<div>
<button @click="add1">加入购物车</button>
</div>`,
data() {
return {
num: 100
}
},
methods: {
add1() {
Bus.$emit('add2', {msg: '你的好兄弟又下单啦'})
}
},
}
new Vue({
el: '#app',
data: {},
components: {
App1,
App2,
}
})
</script>
</body>
</html>
多次嵌套取值
1. provide 提供变量 函数(){return{}},
2. inject 接收变量 inject: [],
12、vue
插槽
匿名插槽
修改已经写好的组件
在模板中把想要替换的东西放在<slot>
标签中,这就是一个占位符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>杨小槿</title>
<script src="./vue.js"></script>
<style>
body{
margin: 0;
}
#header{
display: flex;
height: 60px;
}
#header>div{
width: 50%;
display: flex;
align-items: center;
height: 100%;
}
</style>
</head>
<body>
<div id="app">
<my_header>首页</my_header>
<!-- <my_header>111</my_header> -->
<!-- 这样就会将标签中的内容去代替slot中的内容 -->
</div>
<script>
let my_header = {
data() {
return {}
},
template: `
<div id="header">
<div class="left">
<slot>左部分</slot>
</div>
<div class="right">
<slot>右部分</slot>
<p>如果slot都没有名字,那么如果有多个slot,就会全部进行替换</p>
</div>
</div>`
}
new Vue({
el: '#app',
components: {
my_header
}
})
</script>
</body>
</html>
具名插槽
如果你的模板中需要有多个地方被替换,那么匿名插槽就不适用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>杨小槿</title>
<script src="./vue.js"></script>
<style>
body{
margin: 0;
}
#header{
display: flex;
height: 60px;
}
#header>div{
width: 50%;
display: flex;
align-items: center;
height: 100%;
}
</style>
</head>
<body>
<div id="app">
<my_header>
<template v-slot:left>左左左左左</template>
<!-- v-slot: 可以简写为 # -->
<template v-slot:right>右右右右右</template>
</my_heade>
</div>
<script>
let my_header = {
data() {
return {}
},
template: `
<div id="header">
<div class="left">
<slot name="left">左部分</slot>
</div>
<div class="right">
<slot name="right">右部分</slot>
<!--若有多个slot,并且都没名字,那么就会全部进行替换-->
</div>
</div>`
}
new Vue({
el: '#app',
components: {
my_header
}
})
</script>
</body>
</html>
<div id="app">
<my_header>
<template v-slot:left>左左左左左</template>
<!-- v-slot: 可以简写为 # -->
<template #right>右右右右右</template>
</my_heade>
</div>
我们在替换的时候,是可以写任意标签的,也就是说,在替换的时候,你也可以将组件写入插槽中
<div id="app">
<my_header>
<template v-slot:left>左左左左左</template>
<!-- v-slot: 可以简写为 # -->
<template v-slot:right>右右右右右</template>
<child></child>
</my_heade>
</div>
<script>
let child = {
data(){
return {}
},
template: `<li>子组件</li>`
}
Vue.component('child', child)
let my_header = {
data() {
return {
user: {
name: "杨小槿",
age: 18,
}
}
},
template: `
<div id="header">
<div class="left">
<slot name="left">左部分</slot>
</div>
<div class="right">
<slot name="right">{{ user.name }}</slot>
<!--若有多个slot,并且都没名字,那么就会全部进行替换-->
</div>
</div>`
}
new Vue({
el: '#app',
components: {
my_header
}
})
</script>
作用域插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>杨小槿</title>
<script src="./vue.js"></script>
<style>
body{
margin: 0;
}
#header{
display: flex;
height: 60px;
}
#header>div{
width: 50%;
display: flex;
align-items: center;
height: 100%;
}
</style>
</head>
<body>
<div id="app">
<my_header>
<template v-slot:left>左左左左左</template>
<!-- v-slot: 可以简写为 # -->
<template v-slot:right="slotProps">{{ slotProps.user.age }}</template>
</my_heade>
</div>
<script>
let my_header = {
data() {
return {
user: {
name: "杨小槿",
age: 18,
}
}
},
template: `
<div id="header">
<div class="left">
<slot name="left">左部分</slot>
</div>
<div class="right">
<slot :user="user" name="right">{{ user.name }}</slot>
<!--若有多个slot,并且都没名字,那么就会全部进行替换-->
</div>
</div>`
}
new Vue({
el: '#app',
data: {},
components: {
my_header
}
})
</script>
</body>
</html>
在子组件中,我在插槽里面显示的是user的
xin
,如果我想让它显示user的name应该怎么做呢如果我们直接在父组件调用的时候这样写
<base_layout> {{ user.name }} </base_layout>
这是错误的写法,因为我们的user是被定义再自组件中的,在父组件中就无法使用这个user,所以就会报错啦
那么我们就应该将这个user对象传递给父组件
在子组件中
<slot :user="user">{{ user.xin }}</slot>
通过动态属性的方式将user传递给父组件
在父组件中
<base_layout> <template v-slot:default="slotProps"> {{ slotProps.user.name }} </template> </base_layout>
接收传递来的user即可
slotProps
可以是你自己定义的名字,它里面存储的是这样的
{ "user": { "name": "枫枫", "xin": "知道" } }
所以我们将slotProps中的user中的name传递给子组件,就可以做到数据动态替换
如果你的组件中,有且只有一个默认插槽,那么在替换的时候,是可以这么做的
<base_layout v-slot:default="slotProps"> {{ slotProps }} </base_layout>
13、自定义指令
动态组件
<component>
元素是vue
里面的一个内置组件。 在里面使用 v-bind: is,可以实现动态组件的效果
自定义指令
前面使用的v-if, v-show,以及v-for这些都是Vue为我们提供的内置指令
当然,我们也可以自己自定义一个指令
局部自定义指令
directives: {
focus: {
// 指令的定义
// 使用这个指令就会聚焦在输入框中
inserted: function (el) {
el.focus()
}
}
}
使用
<input type="text" v-focus>
在focus中有几个钩子函数,需要了解
-
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 -
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 -
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
绑定方法到this
Vue.prototype.meta_ = () => {
console.log(1)
}
在created生命周期中不能使用
可以在mounted方法中使用
14、vue
国际化
安装
npm install vue-i18n@9
基本使用
在src目录下创建lang目录
在此目录下创建对应语言的ts文件,如:
zh.ts
en.ts
kor.ts
韩文
jp.ts
日文
zh.ts
示例
export default {
"首页": "首页",
"新闻": "新闻",
"产品": "产品",
index: "首页"
};
en.ts
// en.js
export default {
"首页": "Index",
"新闻": "News",
"产品": "Product",
index: "Index"
}
然后新建一个index.ts
在lang
目录下
import {createI18n } from 'vue-i18n'
import zh from "@/lang/zh";
import en from "@/lang/en";
const i18n = createI18n({
locale: "zh", // 默认是中文
legacy: false, // 解决报错的
messages: {
"zh": zh,
"en": en,
}
})
export default i18n
然后在main.ts
中进行注册
import {createApp} from 'vue'
import App from './App.vue'
import i18n from "@/lang/index"
const app = createApp(App)
app.use(i18n)
app.mount('#app')
在vue的模板中使用
<template>
<main>
<div>
<span>{{ $t("首页") }}</span>
<span>{{ $t("新闻") }}</span>
<span>{{ $t("产品") }}</span>
</div>
</main>
<header>
<button @click="setLang('zh')">中文</button>
<button @click="setLang('en')">英文</button>
</header>
</template>
<script setup lang="ts">
import {useI18n} from 'vue-i18n'
const {t} = useI18n();
console.log(t('index'))
function setLang(lang:string){
locale.value = lang
}
</script>
如果$t警告的话,就在env.d.ts
里面加上
/// <reference types="vite/client" />
declare function $t()
翻译数据从后端来
npm i axios
npm i mockjs
npm i @types/mockjs
准备两个接口
一个是展示有哪些语言的
一个是有对应语言的字典文件
export function langList(): Promise<string[]> {
return useAxios.get("/api/langs")
}
export function langDetail(lang: string): Promise<object> {
return useAxios.get("/api/langs/detail", {params: {lang}})
}
mock
import {mock} from "mockjs";
mock(/api\/langs\/detail/, function (options) {
if (options.url.includes("zh")) {
return {
"首页": "首页",
"新闻": "新闻",
"产品": "产品",
index: "首页"
}
}
if (options.url.includes("en")) {
return {
"首页": "Index",
"新闻": "News",
"产品": "Product",
index: "Index"
}
}
})
mock(/api\/langs/, ["zh", "en"])
页面语言切换
<template>
<header>
<button @click="setLang(item)" v-for="item in langs">{{ item }}</button>
</header>
<main>
<span>{{ $t("首页") }}</span>
<span>{{ $t("新闻") }}</span>
<span>{{ $t("产品") }}</span>
</main>
</template>
<script setup lang="ts">
import {useI18n} from 'vue-i18n'
import {langList, langDetail} from "@/api";
import {ref} from "vue";
const {locale, t, messages} = useI18n();
const langs = ref([])
async function getData() {
langs.value = await langList()
}
getData()
async function setLang(lang: string) {
messages.value[lang] = await langDetail(lang)
console.log(messages.value)
locale.value = lang; // 切换语言
}
</script>
参考文档