上篇Node.js的光速入门{:target="_blank"}是为了学习VUE,而学VUE其实是为了学习UNIApp。嗯,有点套娃的感觉。或许还要返回去学学TypeScript。
其实2021就有关于uve3的学习{:target="_blank"},这里算是复习,没咋用就会忘。这次结合视频讲座快速的再入门。
基础项目
创建:npm init vue@latest
安装相关:npm install
运行:npm run dev
或者使用cnpm: 安装cnpm:npm install -g cnpm –registry=https://registry.npm.taobao.org
净身
把不必要的删除掉,主要就是src目录app.vue和main.js,assets放一些静态文件,package.json为node.js配置,vite.config.js为vue配置
App.vue文件
<template>
<p>{{ msg }}</p>
</template>
<script>
export default{
data(){
return {
msg: "啥东东"
}
}
}
</script>
内为html,内为脚本,实际还有定义样式
中的data()部份用于定义变量供使用,看起来与golang的模板类似
在以下示例中 v-html将使用原始标签来显示变量内容
<template>
<p>{{ msg }}</p>
<p>{{ ok?'好':"不好" }}</p>
<p>{{ msg.split('').reverse().join("") }}</p>
<p v-html="rawHtml"></p>
</template>
<script>
export default{
data(){
return {
msg: "啥东东",
ok: true,
rawHtml:"<a href='https://i.scwy.net'>我的博客</a>"
}
}
}
</script>
属性绑定
// App.vue
<template>
<HelloWorld></HelloWorld>
</template>
<script setup>
import HelloWorld from "./components/HelloWorld.vue"
</script>
// HelloWorld.vue
<template>
<div v-bind:class="msg" v-bind:id="dynamicId">{{ msg }}</div>
</template>
<script>
export default{
data(){
return {
msg: "active",
dynamicId: "appID"
}
}
}
</script>
这里有两点:v-bind 即属性绑定。另外 app.vue中script setup有所不同。
可以简写如下:{{ msg }}, 即省略v-bind
动态绑定多个值:
<template>
<div v-bind="objAttrs">绑定多值</div>
</template>
<script>
export default{
data(){
return {
objAttrs: {
id: "abc",
class: "def"
}
}
}
}
</script>
这样,在前端将看到 绑定多值 ,即自动将id和class绑定到div上。也可以绑定自定义属性。
条件渲染
v-if v-else v-show v-else-if
<div v-if="flag">123</div>
<div v-else>321</div>
列表渲染
v-for
<template>
<p v-for="item in names">{{ item }}</p>
</template>
<script>
export default{
data(){
return {
names: ["你","我","他"]
}
}
}
</script>
<template>
<p v-for="item in names">{{ item.name }} - {{ item.id}}</p>
</template>
<script>
export default{
data(){
return {
names: [
{name:"你",id:1},
{name:"我",id:2},
{name:"他",id:3},
]
}
}
}
</script>
//或者
<div v-for="item in names">
<p>{{ item.name }}</p>
</div>
也支持 item of names 将 in 改为 of
遍历变量属性:
<p v-for="val,key,index in userInfo">{{index}}. {{ val }} {{ key }}</p>
userInfo: {
id: 123,
name: "abc",
age: 23
}
事件处理
v-on 或 @
<template>
<button @click="addCount1">加1</button>
<button @click="addCount('ok',$event)">加1</button>
<p>Count:{{ count }}</p>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods:{
addCount(msg,e){
this.count+=1
console.log(e);
console.log(msg);
},
addCount(e){
this.count+=1
console.log(e);
console.log(e.target.innerHTML);
}
}
}
</script>
事件修饰符
.stop阻止默认事件 .prevent阻止事件冒泡 .once只触发一次 .enter回车键触发
// 在函数中阻止默认响应。
<a href="https://i.scwy.net" @click="clickHandle">我的博客</a>
clickHandle(e){
e.preventDefault();
console.log("点");
}
// 更简单的,使用事件修饰符
<a href="https://i.scwy.net" @click.prevent="clickHandle">我的博客</a>
数组变化侦测
push() pop() shift() unshift() splice() sort() reverse() 使用它将直接影响UI显示的内容
fiter() concat() slice() 影响变量,但不影响引用变量的UI
计算属性
不同于函数(方法),计算属性仅在其值改变时,才进行计算。而前者会每次都运算。
<p>{{ Total }}</p>
// 在script中添加
computed:{
Total() {
return this.count + 100;
}
}
Class绑定
由后方的变量决定样式是否使用
<div :class="{ 'active': isActive, 'error': isError}">这是文字</div>
data() {
return {
count: 0,
isActive: true,
isError: false,
}
},
<style>
.active {
font-size: 20px;
}
.error {
color: red;
}
</style>
多个绑定,与上面的效果一样。
<div :class="classObject">Class绑定</div>
<div :class="[arrActive,arrHasError]"></div>
data() {
return {
arrActive: "active",
arrHasError: "error",
classObject:{
'activev':true,
'error':true,
}
}
},
Style绑定
<template>
<div :style="{color: activeColor, fontSize: fontSize + 'px'}">123</div>
<div :style="styleObject">356</div>
</template>
<script>
export default {
data() {
return {
activeColor: 'red',
fontSize: 30,
styleObject:{
color: 'red',
fontSize: '30px',
fontWeight: 800,
}
}
}
}
</script>
侦听器
// 在script中添加 watch
<script>
data(){
return {
message: "hello"
}
},
watch: {
// 监听message变量的变化,名称需与变量名一致。
message(newValue,oldValue){
}
}
</script>
表单输入绑定
实时获取到message变量的内容到中
<template>
<p>{{ message }}</p>
<form>
<input type="text" v-model="message" />
</form>
</template>
<script>
export default {
data() {
return {
message: 'hello'
}
}
}
</script>
<template>
<p>{{ message }}</p>
<form>
<input type="text" v-model="message" />
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked ? "是":"否" }}</label>
</form>
</template>
<script>
export default {
data() {
return {
message: 'hello',
checked: false,
}
}
}
</script>
修饰符
.lazy懒获取(确定或失去焦点时获取值) .number只获取数字(我测试却不只是数据?) .trim 去空格
模板引用
DOM获取,通过特殊的ref,然后就会暴露在this.$ref
<template>
<div ref="cont">{{ content }}</div>
<button @click="getEleHandle">获取</button>
</template>
<script>
export default {
data() {
return {
content: 'hello'
}
},
methods:{
getEleHandle(){
console.log(this.$refs.cont); // 这里就获取到了DOM对象
}
}
}
</script>
组件
这里的scoped表示样式仅对组件内有效
<style scoped>
</style>
<template>
<!-- 第二步:显示 -->
<MyComp />
</template>
<script>
// 第一步:引入组件
import MyComp from './MyComponent.vue'
export default {
// 第二步:注入组件
components: {
MyComp
}
}
</script>
组件
全局注册/局部注册
全局注册即在main.js中注册(打包时必包含)
import { createApp } from 'vue'
import App from './App.vue'
// 这里引用
import Header from "./pages/Header.vue"
// 注册
App.components("Header",Header)
createApp(App).mount('#app')
组件传递数据 Props
// Child.vue
<template>
<p>子组件</p>
<p>{{ title }}</p>
</template>
<script>
export default{
props:["title"] // 这里定义了可以传输的数据,注意格式为数组
}
</script>
// App.vue
<template>
<Child title="调用传递" />
</template>
<script>
import Child from "./components/Child.vue"
export default {
components:{
Child
}
}
</script>
动态传递值,即将传递的值指向变量
// 仅App.vue调用改为如下,即将title值指向了message
<template>
<Child :title="message" />
</template>
<script>
import Child from "./components/Child.vue"
export default {
data() {
return {
message: "传数据"
}
},
components:{
Child
}
}
</script>
pros校验
在被调用组件中设置参数的类型
props:{
title: {
type: String
}
}
可以接受多种类型
props:{
title: {
type: [String,Number,Object]
}
}
默认值
props:{
title: {
type: Number,
default: 0 // 指定默认值
}
}
如果类型为数组或对象,必须通过工厂函数设置默认
props: {
name: {
type: Array,
default() {
return ["空"]
}
}
}
必选项
props: {
title: {
type: Number,
required: true // 必选
}
}
注意:禁止修改调用传过来的值,只读
组件事件
组件传递数据
$emit触发自定义事件
// App.vue
<template>
<Child :title="message" @someEvent="getHandle" /> <!--设置自定义事件-->
</template>
<script>
import Child from "./components/Child.vue"
export default {
data() {
return {
message: "传数据"
}
},
methods:{
getHandle(data){ // 接收自定义事件的数据data
console.log("触发事件 ",data);
}
},
components:{
Child
}
}
</script>
// Child.vue
<template>
<p>组件事件</p>
<p>{{ title }}</p>
<button @click="clickEvent">传递数据</button>
</template>
<script>
export default{
data(){
return {
}
},
props:["title"],
methods:{
clickEvent(){
this.$emit("someEvent","数据内容") // 自定义事件someEvent,并传递数据
}
}
}
</script>
配合v-model
使用
在两个组件之间获取数据
// Main.vue
<template>
<p>{{ search }}</p>
<SearchComponent @searchEvent="getSearch" />
</template>
<script>
import SearchComponent from "./SearchComponent.vue">
export default {
data() {
return{
search: ""
}
},
components:{
SearchComponent
},
methods:{
getSearch(data){ // 获取子组件传来的值
this.search = data;
}
}
}
</script>
// SearchComponent.vue
<template>
<input type="text" v-model="search">
</template>
<script>
export default {
data() {
return {
search: ""
}
},
watch:{
search(newValue,oldValue){ // 通过变量值的监听,将新值上传
this.$emit("searchEvent",newValue)
}
}
}
</script>
透传 Attributes
将属性传给组件的唯一根元素 class style id
export default {
inheritAttrs: false // 禁止属性继承
}
插槽 Slots
// SlotsBase.vue
<template>
<h3>示例</h3>
<slot></slot> 也可以在中间设置一个默认值
</template>
// App.vue
<template>
<SlotsBase>
<h3>abc</h3> <!--将这里的内容显示到slot组件中-->
</SlotsBase>
</template>
<script>
import SlotsBase from "..."
export default {
components: {
SlotsBase
}
}
</scritp>
多个插槽(具名插槽)
// Solts.vue
<template>
<slot name="header">插槽1</slot>
<hr>
<slot name="main">插槽2</slot>
</template>
</template>
// App.vue
<SlotsBase>
<template v-slot:header>
<h3>123</h3>
</template>
<template v-slot:main>
<h4>abc</h4>
</template>
</SlotsBase>
可以简写:
<template #header>
插槽中的数据传递
将组件中的数据传到调用者,再由调用者组合,形成插槽数据,然后组件进行接收。达到共用调用者和组件数据的目的。
// App.vue
<template>
<SlotsAttr v-slot="slotProps">
<h3> {{ message }} - {{ slotProps.msg }}</h3>
</SlotsAttr>
</template>
<script>
import ...
components: { ... }
</script>
// SlotsAttr.vue
<template>
<slot :msg="childMessage"></slot>
</template>
<script>
export deault {
data() {
return {
childMessage: "asdfasd"
}
}
}
</script>
具名插槽
<template #header="slotProps">
生命周期
创建期:beforeCreate created
挂载期:beforeMounte mounted
更新期:beforeUpdate updated
销毁期:beforeUnmount unmounted
动态组件
根据变量的值来确定显示哪个组件
<template>
<component:is="tabComponent"></component>
</template>
<script>
import ComponentA from "./components/ComponentA.vue"
import ComponentB from "./components/ComponentB.vue"
export default {
data()(
return {
tabComponent: "ComponentA"
}
},
components:{
ComponentA,
ComponentB
}
}
</script>
组件保持存活
<template>
<keep-alive>
<component :is="tabComponent"></component>
</keep-alive>
</template>
<script>
export default {
data(){
},
}
</script>
异步组件
需要时再加载,可达到优化项目
<script>
import { defineAsyncComponent } from 'vue'
const AsyncComponentB = defineAsyncComponent(() =>
import('./ComponentB.vue')
)
</script>
信赖注入
即多级数据的向下传送
// 父级发送
<script>
export default{
provide:{
message: "父级数据"
},
}
</script>
// 孙级接收
<script>
export default {
inject: ["message"]
}
</script>
// 或者父级发送
export default {
data() {
return {
message: 'ok'
}
},
provide() {
return {
message: this.message
}
}
}
在main.js可以定义全局数据:
...
const app = createApp(App)
app.provide("globalData","全局数据")
app.mount('#app')
...
然后在各组件中引用
export default {
inject: ['globalData'],
}
Vue应用
应用实例
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
根组件
import App from './App.vue'
const app = creaetApp(App)
挂载应用
app.mount('#app')
这里,vue会找根目录下index.html的id为app的控件,所有vue的渲染将在其下。
补充
通过 CDN 使用 Vue
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>