生活札记
Vue3学习笔记 - 入门(一)
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
文档:https://cn.vuejs.org/guide/introduction.html
教程:https://www.bilibili.com/video/BV1QA4y1d7xf
1、下载安装Node.js(正常下载LTS版本):
Node.js版本:node -v =====> v16.16.0
npm版本:npm -v =====> 8.11.0
问题参考:
1)、npm出现报错 npm WARN config global `--global`, `--local` are deprecated. Use `--location=global
2)、npm ERR! Error: EPERM: operation not permitted, mkdir 'D:\Program Files\nodejs\node_cache\_cacache'
或者更新npm执行:cnpm install -g npm
或者直接把nodejs安装目录的执行权限调到最高
2、Vue渲染方式:
1)、js引入:<script type="text/javascript" src="https://unpkg.com/vue@next"></script>
<div id="counter">
<p>{{num}}</p>
<p>{{name}}</p>
</div>
<script type="text/javascript">
const Counter = {
data: function(){
return {
num:1,
name:"飓风呀~~~"
};
}
}
app = Vue.createApp(Counter).mount("#counter")
console.log(app)
</script>
2)、Vite安装:npm init vue@latest
进入目录:cd 项目目录
安装:npm install
运行:npm run dev
<script>
// 申明式渲染
export default {
data(){
return {
num:100,
name:"飓风呀~~~"
}
}
}
</script>
<template>
<div>
{{num}}
<p></p>
{{name}}
</div>
</template>
<style>
</style>
3、基础:
1)、属性、方法绑定:
<span :[attributeName]="green">green color</span>
<a @[eventName]="doSomething">ddddd</a>
2)、计算属性与方法:区别是计算属性 computed 是带缓存的,只要依赖值不变则不会重新计算,方法methods不带缓存会多次执行
computed: {
// 简写格式
reverseHelloWorld:function(){
return this.HelloWorld.split('').reverse().join('')
},
// 原始格式
reverseHelloWorld2:{
get(){
console.log("获取")
},
// 更改的时候使用,正常计算属性不需要set方法
set(){
console.log("设置")
}
}
},
3)、watch监听:数据变化
data(){
return {
message:"飓风呀888",
message1:{
name:1,
page:2
},
},
watch:{
// 监听数据变化
message: function(newValue, oldValue){
console.log(newValue, oldValue)
},
message:{
immediate:true, // 初始化就监听执行
handler:function(newValue, oldValue){
console.log(newValue, oldValue)
}
},
message1: {
deep: true, // 深度监听,对象数据可进行深度监听,会一直监听整个对象
handler: function (newValue, oldValue) {
console.log(newValue, oldValue)
}
},
"message1.page": { //这种就是只监听对象里的某个元素
deep: true, // 深度监听,对象数据可进行深度监听,会一直整个对象
handler: function (newValue, oldValue) {
console.log(newValue, oldValue)
}
}
}
4)、class与style:class中对象里面格式:类名:控制变量,数组里的是变量对应的值,style里对应的格式:样式属性值:变量的值
<p class="red" :class="{ borderGreen: true, fontSzie: isClass }">我是红色</p>
<p class="red" :class="styleObj">我是红色</p>
<p :class="[greenClass, { borderGreen: true}]">我是绿色</p>
<p :style="{ color: [redClass] }">redClass</p>
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">display</div>
5)、v-if、v-else-if、v-else与v-show、v-for:条件控制,v-if 不显示的部分不在dom中,v-show隐藏的部分是在dom中相当于display:none
<div>
<p>5、v-if、v-show、for</p>
<p v-if="age > 18">成年了</p>
<p v-else-if="age == 18">刚成年</p>
<p v-else>未成年</p>
<!-- template 是不显示的标签 -->
<template v-if="isShow">
<div>
template 是不显示的标签
</div>
</template>
<p v-show="isShow">我显示</p>
<p v-show="isNoShow">我不显示</p>
<ul>
<li v-for="(item,index) in forArrData" :key="index">{{ item }} - {{index}}</li>
</ul>
<ul>
<li v-for="(item,key,index) in forObjData" :key="index">{{ item }} - {{key}} - {{index}}</li>
</ul>
<ul>
<!-- key是一个唯一标识符,快速找到节点,提高性能 -->
<li v-for="(item,index) in forArrData" :key="item">
<input type="checkbox" name="a[]" id="a_">{{ item }} {{index}}
</li>
<button type="button" @click="changeForData">点击改变Key</button>
</ul>
</div>
6)、数组更新检测:数组相关函数:pop()、push()、shift()、unshift()、splice()、sort()、reverse()
// pop():从后面删除
this.arrList.pop(10)
// push():从后面添加
this.arrList.push(10,11)
// shift():从前面删除
this.arrList.shift()
// unshift():从前面添加
this.arrList.unshift(1,2,3)
// splice():删除(位置,长度)、添加(位置,0,...数据)、替换(位置,长度,...数据)
this.arrList.splice(1,2) // 删除
this.arrList.splice(1,0,300,400) // 添加
this.arrList.splice(1, 2, 300, 400) // 替换
// sort():排序
this.arrList.sort()
// reverse():反向
this.arrList.reverse()
// 更改某一项
this.arrList[1] = 100
7)、事件绑定与事件修饰符:
1)、事件:$event
2)、事件修饰符:.stop、.prevent、.once、.self、.capture、.passive
3)、按钮、键盘修饰符:.enter、.tab、.delete、.esc、.space、.up、.down、.left、.right、.ctrl、.alt、.shift、.meta
<div>
<p>7、事件绑定与修饰符</p>
<p><button type="button" @click="counter++">js事件改变{{counter}}</button></p>
<p><button type="button" @click="changeCounter(5)">函数方法改变{{counter}}</button></p>
<p><button type="button" @click="changeCounter(5,$event)">函数方法改变,传递事件{{counter}}</button></p>
<p><button type="button" @click="changeCounter(5,$event),changeAge(10,$event)">多个函数方法改变,传递事件{{counter}} -
{{counterAge}}</button></p>
<!-- 阻止冒泡 -->
<div @click="divClick">
<span @click.stop="spanClick">点击冒泡</span>
</div>
<!-- 禁止事件 -->
<form action="">
<button type="submit" @click.prevent="btnSubmit">提交</button>
</form>
<!-- 按键事件 -->
<input type="text" name="" value="" @keyup.enter="keyUp">
</div>
8)、表单输入绑定v-model:修饰符:.lazy、.number、.trim
data(){
return {
// v-model表单控件
username:"",
fruits:["苹果"],
sex:"女",
lange:"PHP",
job: ["模特"],
coute:0
}
},
<div>
<p>8、v-model表单控件:</p>
<p>UserName:{{username}}</p>
<p><input type="text" v-model="username" /></p>
<p>
<span>{{fruits}}</span><br>
<input type="checkbox" v-model="fruits" value="苹果">苹果
<input type="checkbox" v-model="fruits" value="香梨">香梨
<input type="checkbox" v-model="fruits" value="香蕉">香蕉
<input type="checkbox" v-model="fruits" value="菠萝">菠萝
</p>
<p>
<span>{{sex}}</span><br>
<input type="radio" v-model="sex" value="男">男
<input type="radio" v-model="sex" value="女">女
</p>
<p>
<span>{{lange}}</span><br>
<select v-model="lange">
<option value="PHP">PHP</option>
<option value="VueJs">VueJs</option>
<option value="GoLang">GoLang</option>
</select>
</p>
<p>
<span>{{job}}</span><br>
<select v-model="job" multiple="multiple">
<option value="模特">模特</option>
<option value="歌手">歌手</option>
<option value="玩泥巴">玩泥巴</option>
</select>
</p>
<!-- 失去焦点更新 -->
<p>UserName:{{username}}</p>
<p>懒更新:<input type="text" v-model.lazy="username" /></p>
<!-- 转数字,字符串原样输出 -->
<p>数字{{ coute }}:<input v-model.number="coute" /></p>
<!-- 前后去空值 -->
<p>去空:<input v-model.trim="username" /></p>
</div>
9)、component 组件:组件划分为独立的、可重用的部分,组件常常被组织成层层嵌套的树状结构。每个组件都是独立的实例。
App.vue:
<script>
// 每个组件都是独立的实例
import Header from "./components/Header.vue"
import Footer from "./components/Footer.vue"
// 申明式渲染
export default {
data(){
return {
msg: "Main",
copyRight:"飓风呀,版权所有",
arrList:[1,2,3,4],
objList:{
name:"飓风呀",
sex:"女",
age:100
}
}
},
// 加载组件
components:{
Header,
Footer
}
}
</script>
<template>
<div>
<Header></Header>
<Header></Header>
<Header></Header>
<p>{{msg}}</p>
<!--组件属性传递-->
<Footer :copyright="copyRight" :titleName=200 :arrlist="arrList" :objlist="objList"></Footer>
</div>
</template>
Header.vue:
<script>
// 全局参数
const obj = {
title: "Header"
}
export default {
// 组件里面的每个data数据都是独立的,也就是每个组件之间互不影响
data(){
return {
title: "Header"
}
},
// 如果data返回值是同一个则会影响所有
// data() {
// return obj
// },
methods: {
changeTitle(){
this.title = "Header666"
}
}
}
</script>
<template>
<div>
<p class="title">{{ title }}</p>
<p><button type="button" @click="changeTitle">点击改变</button></p>
</div>
</template>
<style>
.title {
color: red;
font-size: 28px;
}
</style>
Footer.vue:
<script>
export default {
data(){
return {
title: "Footer"
}
},
// 接受父组件传递的参数
// props: ["copyright", "title"],
props:{
copyright:{
type: String,
default: "copyright @ 123456",
required: true
},
titleName:{
type: Number,
default: 1,
required: true
},
arrlist:{
type:Array,
default(){
return [6,7,8]
}
},
objlist: {
type: Object,
default() {
return {}
}
}
}
}
</script>
<template>
<div>
<p class="title">{{ titleName }}</p>
<p>版权:{{ titleName }} - {{ copyright }}</p>
<ul>
<li v-for="(item, index) in arrlist" :key="item">{{item}} - {{index}}</li>
</ul>
<ul>
<li v-for="(item, index, key) in objlist" :key="item">{{item}} - {{index}} - {{ key }}</li>
</ul>
</div>
</template>
<style>
.title {
color: green;
font-size: 25px;
}
</style>
父子组件访问方式:
methods: {
sendParent() {
// 通过$emit触发事件,this.$emit("事件名称","参数数据")
this.$emit("headerName", this.headerName)
}
},
mounted() {
// 子组件获取父组件数据
console.log(this.$parent)
console.log(this.$parent.copyRight)
console.log(this.$root)
},
<div>
<!-- 父子组件访问方式 -->
<!-- 1、父组件访问子组件:$refs,开发中常用,ref属性用来给子组件或子元素注册引用信息 -->
<!-- 2、子组件访问父组件:$parant,在开发中尽量少用,组件复用性高 -->
<!-- 3、子组件访问根组件:$root -->
<Header @headerName="getHeaderName" ref="header"></Header>
{{title}}
<p ref="p" id="ps">子组件的headerName:{{ headerName }}</p>
<Footer :copyright="copyRight" :titleName=200 :arrlist="arrList" :objlist="objList"></Footer>
</div>
10)、slot 插槽:为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。顾名思义就是一个坑,等着被填充。
子组件SlotDemo.vue:
<script>
// 申明式渲染
export default {
data(){
return {
title: "Solt",
listArr:[1,2,3,4,5],
message:"solt3",
message1:"solt4"
}
}
}
</script>
<template>
<div>
{{title}}
<slot>solt1</slot>
<!-- 具名插槽 -->
<slot name="slot2"></slot>
<!-- 作用域插槽:传递变量参数 -->
<slot name="slot3" :listArr="listArr" :message="message"></slot>
<slot name="slot4" :listArr="listArr" :message="message1">slot4</slot>
</div>
</template>
父组件Content.vue:
<script>
// 每个组件都是独立的实例
import SlotDemo from "./SlotDemo.vue"
// 申明式渲染
export default {
data(){
return {
title: "Content"
}
},
components: {
SlotDemo
}
}
</script>
<template>
<div>
<!-- 插槽 -->
<SlotDemo>
slot1111
<!-- 具名插槽 -->
<template v-slot:slot2>
slot2
</template>
<!-- 作用域插槽:获取子组件变量 -->
<template #slot3="slotProps">
<ul>
<li v-for="(item,index) in slotProps.listArr" :key="item">{{ item }} - {{ index }}</li>
</ul>
{{ slotProps.message }}
</template>
<!-- 作用域插槽:变量解析出来 -->
<template v-slot:slot4="{ listArr, message }">
<ul>
<li v-for="(item,index) in listArr" :key="item">{{ item }} - {{ index }}</li>
</ul>
{{ message }}
</template>
</SlotDemo>
</div>
</template>
11)、provide / inject 依赖注入:通过 provide() 提供一个值,可以被后代组件注入,通过 inject() 注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。
祖先组件App.vue:
<script>
// 每个组件都是独立的实例
import Content from "./components/Content.vue"
// 申明式渲染
export default {
data(){
return {
msg: "Main",
parentData: "parentData6666",
obj:{
parentData: "parentData6666"
}
}
},
// provide/inject不是响应式
// provide: { parentData: "parentData6666" },
// 如果想去访问组件实例属性,要使用方法返回对象的形式
provide(){
return {
// parentData: this.parentData, // 不是想响应式
// parentData: this.obj, // 对象方式可以响应式
parentData:()=>this.parentData // 方法方式可以响应式
}
},
components:{
Content
},
methods:{
changeParent(){
this.parentData = "祖先数据改变了:aaaa"
}
}
}
</script>
<template>
<div>
<p>祖先数据:{{ parentData }}</p>
<p><button type="button" @click="changeParent">改变祖先数据</button></p>
<p>{{msg}}</p>
<Content></Content>
</div>
</template>
孙子组件SlotDemo.vue:
<script>
// 申明式渲染
export default {
data(){
return {
title: "Solt",
listArr:[1,2,3,4,5],
message:"solt3",
message1:"solt4"
}
},
// 接受祖先数据
inject: ["parentData"],
computed: { // 计算属性
parentDataNew(){
return this.parentData()
}
},
}
</script>
<template>
<div>
<p>子组件获取祖先数据</p>
<p>{{ parentData() }}</p>
<p>{{ parentDataNew }}</p>
</div>
</template>
12)、生命周期:每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。
组件Life.vue:
<script>
export default {
data(){
return {
title: "Life",
isShow:true
}
},
beforeCreate() {
console.log("Before create")
},
created() {
console.log("Created")
},
beforeMount() {
console.log("Before mount")
},
mounted() {
console.log("mounted")
},
beforeUpdate() {
console.log("Before update")
},
updated() {
console.log("Updated")
},
beforeUnmount() {
console.log("beforeUnmount")
},
unmounted() {
console.log("unmounted")
},
methods:{
changeData(){
this.title = "改变数据~~~"
}
}
}
</script>
<template>
<div>
<p>生命周期:{{title}}</p>
<p><button type="button" @click="changeData">改变数据</button></p>
</div>
</template>
组件Content.vue:
<script>
// 每个组件都是独立的实例
import Life from "./Life.vue"
// 申明式渲染
export default {
data(){
return {
isShow:true
}
},
beforeUnmount() {
console.log("beforeUnmount")
},
unmounted() {
console.log("unmounted")
},
}
</script>
<template>
<div>
<!-- 生命周期 -->
<Life v-if="isShow"></Life>
{{ isShow }}
<p><button type="button" @click="isShow=!isShow">销毁数据</button></p>
</div>
</template>
4、Vue3组合式Api:setup():组合式 API (Composition API) 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件。
父组件App.vue:
<script>
// 每个组件都是独立的实例
import {provide, ref} from "vue"
import Setup from "./components/Setup.vue"
// 申明式渲染
export default {
data(){
return {
msg: "Main",
parentData: "parentData6666",
obj:{
parentData: "parentData6666"
}
}
},
setup(props) {
// 父组件provide传递数据到子组件
let provideData = ref("飓风呀999") // 这是一个ref响应式
provide("provideData", provideData) // 传递的是一个对象
function changProvide(){
provideData.value = "bbb"
console.log(provideData.value)
}
return { changProvide } // 暴露数据
},
components:{
Setup
},
mounted() {
console.log(this.$refs.content) // 通过 $refs 获取组件数据
},
methods:{
changeParent(){
this.parentData = "祖先数据改变了:aaaa"
},
getSonData(value){
alert(value);
}
}
}
</script>
<template>
<div>
<p>{{msg}}</p>
<p>祖先数据:{{ parentData }}</p>
<p><button type="button" @click="changeParent">改变祖先数据</button></p>
<p><button type="button" @click="getSonData">获取子组件数据</button></p>
<Setup :message="parentData" class="a" data="b" aaa="ccc" :msg="msg" @sendParent="getSonData" ref="content"></Setup>
<p><button type="button" @click="changProvide">更改setup的provide数据</button></p>
</div>
</template>
子组件Setup.vue:
<script>
// 导入引用
import { ref, reactive, toRefs, watch, watchEffect, computed, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, h, inject } from "vue"
// 申明式渲染
export default {
data(){
return {
title: "Setup"
}
},
// setup 组合式api:将同一个逻辑关注点相同的代码收集在一起
setup(props, context) {
// 组件在创建之前执行,this无法指向实例
console.log("setup")
// 1、改变setup里的数据
let msg = "setup"
function changeSetupMsg(){
msg += 1 // 这里的数据不是响应式的
console.log(msg)
}
// 2、响应式
let counter = ref(0) // ref() 返回带有value值的对象
function changeSetupCounter(){
counter.value++
console.log(counter)
}
// reactive() 定义响应式对象
let obj = reactive({
name: "飓风呀",
age:20,
child:{
sex:"女"
}
})
function changeSetupReactive(){
obj.name = "飓风呀呀呀"
console.log(obj)
}
// reactive() 定义响应式对象
let obj1 = reactive({
name: "飓风呀",
age: 20,
child: {
sex: "女"
}
})
function changeSetupReactiveToRefs(){
obj1.name = "飓风呀呀呀巴拉巴拉巴拉"
console.log(obj1)
}
// 3、watch() 与 watchEffect() 监听
// 1、watchEffct() 不需要监听属性,会自动收集依赖,只要在回调函数中引用到了响应式属性,发生变化后回调就会执行
// wathch() 只能监听指定的属性,做出回调函数的执行,可以监听多个
// 2、watch() 可以拿到新值newValue、旧值oldValue,watchEffct()拿不到
// 3、watchEffct() 在初始化的时候会自动执行一次,用来收集依赖,watch() 不需要,一开始就指定了
let watchCouter = ref(10)
let watchObj = reactive({
name: "飓风呀",
age: 20,
child: {
sex: "女"
}
})
// 监听数据
watch(watchCouter, function(newValue, oldValue){
console.log(newValue+":"+oldValue)
})
function changewatchCouter(){
watchCouter.value++
console.log(watchCouter.value)
}
// 监听对象
watch(watchObj, (newValue, oldValue) => {
console.log(newValue)
console.log(oldValue)
})
function changewatchWatchObj() {
watchObj.age++
console.log(watchObj)
}
// watchEffect(回调函数)不需要指定属性,组件初始化会执行一次回调函数,自动收集依赖
watchEffect(()=>{
// console.log(watchObj.age)
})
// 4、computed() 计算属性:返回一个带value值得属性对象
let computedCouter = ref(10)
let computedObj = reactive({
name: "飓风呀",
age: 20,
child: {
sex: "女"
},
computedCouterAge:computed(()=>{
return computedObj.age += 5
})
})
let computedCouterNew = computed(()=>{
// 返回一个带value值得属性对象
return computedCouter.value += 20
})
// 5、生命周期钩子
onBeforeMount(() => {
// console.log("onBeforeMount")
})
onMounted(() => {
// console.log("onMounted")
})
onBeforeUpdate(() => {
// console.log("onBeforeUpdate")
})
onUpdated(() => {
// console.log("onUpdated")
})
// 6、props 与 context
let { message } = toRefs(props) // 转成响应式
onUpdated(() => {
console.log(message.value)
})
// 7、context 参数:
// 1)、context.attrs(属性:非响应式对象,等同于 $attrs)
console.log(context.attrs.class)
// 2)、context.slots(插槽:非响应式对象,等同于 $slots)
console.log(context.slots)
// 3)、context.emit(触发事件,等同于 $emit)
let counterEmit = ref(666)
function sendParent() {
context.emit("sendParent", counterEmit.value)
}
// 4)、context.expose(暴露公共 property)
let counterExpose = ref(888)
context.expose({ sendParent, counterExpose }) // 暴露属性、方法,父级组件可通过 $refs 获取到
return () => h("div", counterExpose.value) // h() 直接渲染
// 8、provide() 与 inject() :
let provideData = inject("provideData")
// 通过ES6扩展符号...解构出来的数据不是响应式的,如果需要解构成响应式需要:toRefs() 方法转换
// 暴露数据
return {
msg,
changeSetupMsg,
counter,
changeSetupCounter,
changeSetupReactive,
obj,
changeSetupReactiveToRefs,
...toRefs(obj1), // 对象解构,通过toRefs输出成响应式
watchCouter,
changewatchCouter,
watchObj,
changewatchWatchObj,
computedCouterNew,
computedObj,
message,
sendParent,
provideData
}
},
// setup() 组合式api也必须有 props
props:{
message:{
type:String,
default:"飓风呀~~~"
}
},
beforeCreate() {
// console.log('beforeCreate')
},
created() {
// console.log('created')
},
}
</script>
<template>
<div>
<p>标题:{{title}}</p>
<p>消息不响应:{{ msg }}</p>
<p><button type="button" @click="changeSetupMsg">改变msg里的数据</button></p>
<p>计数器响应:{{ counter }}</p>
<p><button type="button" @click="changeSetupCounter">改变couter里的数据</button></p>
<p>reactive响应式对象:{{ obj.name }} - {{ obj.age }} - {{ obj.child.sex }}</p>
<p><button type="button" @click="changeSetupReactive">改变reactive响应式对象里的数据</button></p>
<p>reactive解构数据:{{ name }} - {{ age }} - {{ child.sex }}</p>
<p><button type="button" @click="changeSetupReactiveToRefs">改变reactive响应式对象里的数据</button></p>
<p>watch数据:{{ watchCouter }}</p>
<p><button type="button" @click="changewatchCouter">改变watch数据</button></p>
<p>watch数据:{{ watchObj.name }} - {{ watchObj.age }} - {{ watchObj.child.sex }}</p>
<p><button type="button" @click="changewatchWatchObj">改变watch数据</button></p>
<p>计算属性:{{ computedCouterNew }}</p>
<p>计算对象属性:{{ computedObj.computedCouterAge }}</p>
<p>父级传递的属性:{{ message }}</p>
<p>context.emit:<button type="button" @click="sendParent">向父级传递数据</button></p>
<p>provide/inject:{{ provideData }}</p>
</div>
</template>
组件Setup2.vue:setup语法糖直接暴露所有的属性、方法
<script setup>
// 导入引用
import { ref } from "vue"
// 导入组件
import Footer from "./Footer.vue"
// 定义变量
let title = "Setup2"
let msg = ref("消息内容")
function changeSetupMsg(){
msg.value = "消息内容666"
}
</script>
<template>
<div>
<p>标题:{{title}}</p>
<p>消息不响应:{{ msg }}</p>
<p><button type="button" @click="changeSetupMsg">改变msg里的数据</button></p>
<!--组件无需通过components加载注册-->
<Footer></Footer>
</div>
</template>
文明上网理性发言!
基础已完结!!!