前端进阶
约 5693 字大约 19 分钟
(1)预备知识
传统开发
模块开发
Vite开发
npm create vite@latest vue3-ts-basic -- --template vue-ts
npm install
npm run dev
http://127.0.0.1:5173/
(2)Vue2核心语法
开发环境
node -v
npm -v
npm i @vue/cli -g
vue create my-vue3-project
部署发布
组件安装
在Vue 2中
npm install vue-router
npm install vuex --save
在Vue 3中
路由管理通常使用的是Vue Router 4,而状态管理库Vuex也已经发展到了Vuex 4。
npm install vue-router@4
npm install vuex@next
计算属性
<template>
<div>
<h3>计算属性</h3>
<h6>methods没有缓存</h6>
<h6>computed有缓存、计算属性没有括号</h6>
<div>
{{ m1() }}{{ m1() }}{{ m1() }}
</div>
<div>
{{ m2 }}{{ m2 }}{{ m2 }}
</div>
</div>
</template>
<script>
export default {
name:'A',
data(){
return {
num:1982
}
},
methods:{
m1(){
console.log('执行methods方法')
return this.num
}
},
computed:{
m2() {
console.log('执行computed方法')
return this.num
}
}
}
</script>
侦听器
<template>
<div>
{{ num }}
<button @click="m()">按钮</button>
</div>
</template>
<script>
export default {
name:'B',
data(){
return {
num:100
}
},
methods: {
m(){
this.num++
}
},
watch:{
num(newValue, oldValue) {
console.log(newValue)
console.log(oldValue)
}
}
}
</script>
Vue-router和Vuex配置
Vue-router路由跳转
<template>
<div>
<h3>C页面</h3>
<nav>
<router-link to="/test/D">路径跳转到D组件</router-link><br>
<router-link to="/test/E">路径跳转到E组件</router-link><br>
</nav>
<nav>
<router-link :to="{name:'D'}">名字跳转到D组件</router-link><br>
<router-link :to="{name:'E'}">名字跳转到E组件</router-link><br>
</nav>
<router-view />
</div>
</template>
Vue-router动态路由
C.vue
<template>
<div>
<h3>C页面</h3>
<nav>
<router-link to="/test/D/30">路径动态路由</router-link><br>
</nav>
<nav>
<router-link :to="{name:'D',params:{id:28}}">名字动态路由</router-link>
</nav>
<router-view />
</div>
</template>
router/index.js
{
path: 'D/:id',
props: true,
name: 'D',
component: () => import('@/views/test/D'),
meta: { title: 'D', icon: 'table' }
}
D.vue
<template>
<div>
<h1>D页面{{ id }}</h1>
</div>
</template>
<script>
export default {
name:'D',
props:['id']
}
</script>
Vue-router编程导航
C.vue
created(){
setTimeout(()=>{
this.$router.push({name:'D',query:{data:'数据'}})
},3000)
}
D.vue
created(){
console.log(this.$route.query)
}
router/index.js路由守卫、导航守卫、每次路由跳转都会触发、next表示跳转下去
router.beforeEach((to, from, next) => {
console.log('路由触发了')
next()
})
Vuex-state
store/index.js
const store = new Vuex.Store({
state:{
count: 100
},
mutations:{
},
actions:{
},
modules: {
app,
settings,
user
},
getters
})
E.vue
created(){
console.log(this.$store.state.count)
}
Vuex-mutations
store/index.js
const store = new Vuex.Store({
state:{
count: 100
},
mutations:{
m1(state, num){
state.count += num
console.log('mutations执行了' + state.count)
}
},
actions:{
},
modules: {
app,
settings,
user
},
getters
})
E.vue
methods: {
x(){
// 说明:参数m1是mutations定义的方法名
this.$store.commit('m1', 1)
this.$store.commit('m1', 2)
this.$store.commit('m1', 3)
}
}
Vuex-actions
store/index.js
const store = new Vuex.Store({
state:{
count: 100
},
mutations:{
m1(state, num){
state.count += num
console.log('mutations执行了' + state.count)
}
},
actions:{
m2(state, num){
setTimeout(()=>{
store.commit('m1', num)
},3000)
}
},
modules: {
app,
settings,
user
},
getters
})
E.vue
methods: {
x(){
this.$store.commit('m1', 1)
this.$store.commit('m1', 2)
// 说明:调用actions中的异步方法m2、因为是异步所以结果为:1、2、3、10
this.$store.dispatch('m2', 10)
this.$store.commit('m1', 3)
}
}
Vuex-getters
store/index.js
const store = new Vuex.Store({
state:{
count: 100
},
mutations:{
m1(state, num){
state.count += num
console.log('mutations执行了' + state.count)
}
},
actions:{
m2(state, num){
setTimeout(()=>{
store.commit('m1', num)
},3000)
}
},
modules: {
app,
settings,
user
},
getters:{// 监听state值的变化
getCount(state){
console.log('getters执行了')
return state.count
}
}
})
E.vue
methods: {
x(){
this.$store.commit('m1', 1)
this.$store.commit('m1', 2)
// 说明:调用actions中的异步方法m2、因为是异步所以结果为:1、2、3、10
this.$store.dispatch('m2', 10)
this.$store.commit('m1', 3)
// 注意:// 因为只改变了一次、所以只执行一次
console.log(this.$store.getters.getCount)
console.log(this.$store.getters.getCount)
console.log(this.$store.getters.getCount)
console.log(this.$store.getters.getCount)
console.log(this.$store.getters.getCount)
}
}
Vuex-modules
(3)Vue3核心语法一
传统开发模式:Vue3组合式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>传统开发模式:Vue3组合式</title>
<script src="vue.global.js"></script>
</head>
<body>
<div id="app">
<h1>{{ msg }}</h1>
<h2>{{ data.name}}</h2>
<h3>{{ data.age}}</h3>
</div>
<script>
// 解构赋值语法(ES6)
const {createApp, reactive} = Vue
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const data = reactive({
name:'jack',
age:18
})
return {
msg:'success',
data
}
}
}).mount('##app')
</script>
</body>
</html>
模块化开发模式:Vue3组合式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模块化开发模式:Vue3组合式</title>
</head>
<body>
<div id="app">
<h1>{{ msg }}</h1>
<h2>{{ data.name}}</h2>
<h3>{{ data.age}}</h3>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const data = reactive({
name:'jack',
age:18
})
return {
msg:'success',
data
}
}
}).mount('##app')
</script>
</body>
</html>
ref和reactive
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ref和reactive</title>
</head>
<body>
<div id="app">
<h3>ref用于存储单个基本类型的数据、如数字、字符串等</h3>
<h3>使用ref创建的响应式对象、需要通过value属性来访问和修改</h3>
<h4>{{ number }}</h4>
<h3>reactive用于存储复杂数据类型、如对象或数组等</h3>
<h3>使用reactive创建的响应式对象、可以直接通过属性名来访问和修改</h3>
<h4>{{ data.name }}</h4>
<h4>{{ data.age }}</h4>
</div>
<script type="module">
import {createApp, reactive, ref} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const number = ref(10)
number.value = 20
const data = reactive({
name:'jack',
age:18
})
person.name = 'stephen'
return {
msg:'success',
data,
number
}
}
}).mount('##app')
</script>
</body>
</html>
绑定事件v-on简写@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绑定事件v-on简写@</title>
</head>
<body>
<div id="app">
<h3>{{ msg }}</h3>
<h3>{{ data.name }}</h3>
<h3>{{ data.age }}</h3>
<button v-on:click="edit">按钮</button> <br>
<button @click="edit">按钮</button> <br>
<input type="text" @keyup.enter="add(10)"> <br>
<input type="text" @keyup.space="add(10)"> <br>
<input type="text" @keydown.tab="add(10)"> <br>
<input type="text" @keyup.ctrl.a="add(10)"> <br>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const data = reactive({
name:'jack',
age:18
})
// 注意:定义的方法也需要添加到return内
const edit = () => {
data.name = '张三'
}
const add = (a) => {
data.age += a
}
return {
msg:'success',
data,
edit,
add
}
}
}).mount('##app')
</script>
</body>
</html>
显示和隐藏v-show
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>显示和隐藏v-show</title>
</head>
<body>
<div id="app">
<h3>{{ msg.show }}</h3>
<button @click="edit()">按钮</button>
<div v-show="msg.show">大连赵哥</div>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const msg = reactive({
show:true
})
const edit = () => {
msg.show = !msg.show
}
return {
msg,
edit
}
}
}).mount('##app')
</script>
</body>
</html>
条件渲染v-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>条件渲染v-if</title>
</head>
<body>
<div id="app">
<h4>v-show通过css的display属性来控制元素的显示和隐藏、适用频繁切换显示状态</h4>
<h4>v-if用于对元素进行条件渲染true渲染元素false不渲染、适用较少改变场景、因为频繁DOM操作</h4>
<h3>{{ msg.show }}</h3>
<button @click="edit()">按钮</button>
<div v-if="msg.show">大连赵哥</div>
<div v-if="false">1</div>
<div v-else-if="false">2</div>
<div v-else>3</div>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const msg = reactive({
show:true
})
const edit = () => {
msg.show = !msg.show
}
return {
msg,
edit
}
}
}).mount('##app')
</script>
</body>
</html>
动态属性绑定v-bind简写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态属性绑定v-bind简写</title>
</head>
<body>
<div id="app">
<h3>{{ msg }}</h3>
<a v-bind:href="data.url">百度</a> <br>
<a :href="data.url">百度</a> <br>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const data = reactive({
url:'http://www.baidu.com'
})
return {
msg:'success',
data
}
}
}).mount('##app')
</script>
</body>
</html>
遍历数组或对象v-for
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>遍历数组或对象v-for</title>
</head>
<body>
<div id="app">
<h2>数组</h2>
<ul>
<li v-for="(value, index) in data.arr">
{{ index }} {{ value }}
</li>
</ul>
<h2>对象</h2>
<ul>
<li v-for="(value, key) in data.brr">
{{ key }} {{ value }}
</li>
</ul>
<h2>对象数组</h2>
<ul>
<li v-for="(value, index) in data.crr">
{{ index }} {{ value.name }} {{ value.age }}
</li>
</ul>
<h2>过滤条件</h2>
<ul>
<template v-for="(value, index) in data.crr">
<li v-if="index == 1" :title="value.name" :key="value.id">
{{ index }} {{ value.name }} {{ value.age }}
</li>
</template>
</ul>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const data = reactive({
arr:['a', 'b'],
brr:{name:'张三',age:30},
crr:[{id:1, name:'李四',age:40},{id:2, name:'王五',age:50}
]
})
return {
data
}
}
}).mount('##app')
</script>
</body>
</html>
双向数据绑定v-model
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>双向数据绑定v-model</title>
</head>
<body>
<div id="app">
<div>
{{data.a}}
<input type="radio" name="g" value="1" v-model="data.a">
<input type="radio" name="g" value="2" v-model="data.a">
</div>
<div>
{{data.b}}
<input type="checkbox" value="1" v-model="data.b">
<input type="checkbox" value="2" v-model="data.b">
</div>
<div>
{{data.c}}
<select v-model="data.c">
<option value="1">1</option>
<option value="2">2</option>
</select>
</div>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const data = reactive({
a:1,
b:[1],
c:1
})
return {
msg:'success',
data
}
}
}).mount('##app')
</script>
</body>
</html>
v-model修饰符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-model修饰符</title>
</head>
<body>
<div id="app">
<div>{{ data }}</div>
<div>
<h3>lazy失去焦点或按下回车之后渲染</h3>
<input type="text" v-model.lazy="data.name"> <br>
<h3>trim去除收尾空格、失去焦点或按下回车之后渲染、中间空格不能去除</h3>
<input type="text" v-model.trim="data.name"> <br>
<h3>number值转换为数字类型、注意是根据首字符类型来判断的、结尾汉字没有变化但如果开头汉字则改变</h3>
<input type="text" v-model.number="data.age"> <br>
</div>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
// setup选项、用于设置响应式数据和方法等
setup(){
const data = reactive({
name:'jack',
age:18
})
return {
msg:'success',
data
}
}
}).mount('##app')
</script>
</body>
</html>
渲染数据v-text和v-html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>渲染数据v-text和v-html</title>
</head>
<body>
<div id="app">
<div v-text="data.a"></div>
<div v-html="data.b"></div>
</div>
<script type="module">
import {createApp, reactive} from './vue.esm-browser.js'
createApp({
setup(){
const data = reactive({
a:'纯文本',
b:'<input>'
})
return {
data
}
}
}).mount('##app')
</script>
</body>
</html>
计算属性computed
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算属性computed</title>
</head>
<body>
<div id="app">
<h3>方法:无缓存</h3>
<div>{{ add() }}</div>
<div>{{ add() }}</div>
<h3>计算属性:有缓存(计算属性根据其依赖的响应式数据变化而重新计算、sub是属性不是方法)</h3>
<div>{{ sub }}</div>
<div>{{ sub }}</div>
<h3>不加number获取的是字符串</h3>
<div>
<input type="text" v-model.number="data.a">
<input type="text" v-model.number="data.b">
</div>
</div>
<script type="module">
import {createApp, reactive, computed} from './vue.esm-browser.js'
createApp({
setup(){
const data = reactive({
a:1,
b:2
})
let add = () => {
console.log('add')
return data.a + data.b
}
const sub = computed(() => {
console.log('sub')
return data.a - data.b
})
return {
data,
add,
sub
}
}
}).mount('##app')
</script>
</body>
</html>
侦听器watch
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>侦听器watch</title>
</head>
<body>
<div id="app">
<select v-model="hobby">
<option value="">请选择</option>
<option value="1">游泳</option>
<option value="2">篮球</option>
</select>
<select v-model="data.year">
<option value="">请选择</option>
<option value="2023">2023</option>
<option value="2024">2024</option>
</select>
<select v-model="data.month">
<option value="">请选择</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<script type="module">
import {createApp, reactive, ref, watch} from './vue.esm-browser.js'
createApp({
setup(){
const hobby = ref('')
const data = reactive({
year:2024,
month:4
})
watch(hobby, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
// 侦听整个data
// 注意:newValue和oldValue打印结果一样的原因
// JS对象和数组是通过引用传递的、而不是通过值传递
// 当修改对象或数组时、实际上修改的是对象或数组的引用、而不是创建一个新的对象或数组
// 所以、如果修改了对象或数组的值、那么打印出来的结果则是修改后的值
watch(data, (newValue, oldValue) => {
// console.log(newValue, oldValue)
console.log('年或月发生变化')
})
// 监听data中的某个属性
watch(() => data.year, (newValue, oldValue) => {
console.log('只有年发生变化')
})
// 监听data中的某个属性
watch(() => data.month, (newValue, oldValue) => {
console.log('只有月发生变化')
})
return {
data,
hobby
}
}
}).mount('##app')
</script>
</body>
</html>
自动侦听器watchEffect
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自动侦听器watchEffect</title>
</head>
<body>
<div id="app">
<select v-model="hobby">
<option value="">请选择</option>
<option value="1">游泳</option>
<option value="2">篮球</option>
</select>
<select v-model="data.year">
<option value="">请选择</option>
<option value="2023">2023</option>
<option value="2024">2024</option>
</select>
<select v-model="data.month">
<option value="">请选择</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
</div>
<script type="module">
import {createApp, reactive, ref, watchEffect} from './vue.esm-browser.js'
createApp({
setup(){
const hobby = ref('')
const data = reactive({
year:2024,
month:4
})
// 自动侦听
watchEffect(()=>{
console.log('侦听开始')
if (hobby.value == 2) {
console.log('篮球')
}
if (data.year == 2024) {
console.log('2024年')
}
if (data.month == 4) {
console.log('4月')
}
console.log('侦听结束')
})
return {
data,
hobby
}
}
}).mount('##app')
</script>
</body>
</html>
(4)Vue3核心语法二
Vue2语法
A.vue
<template>
<h1>子组件A</h1>
</template>
<script>
</script>
App.vue
<script>
import A from './demo2/A.vue'
export default {
name:'App',
components:{
A
},
data(){
return {
name:'vue2'
}
},
methods:{
}
}
</script>
<template>
<h1>{{ name }}</h1>
<A></A>
</template>
响应式reactive数组
B.vue
<template>
<h1>子组件B</h1>
</template>
<script>
</script>
App.vue
<script setup>
import B from './demo/B.vue'
import {reactive} from 'vue'
const arr = reactive([1,2,3])
function m() {
arr.push(4)
}
</script>
<template>
<div>{{ arr }}</div>
<div>
<button @click="m">按钮</button>
</div>
<B></B>
</template>
响应式reactive对象
<script setup>
import {reactive} from 'vue'
const myData = reactive({
name:'jack',
age:18,
friends:['sam','stephen']
})
function m() {
myData.name = '张三'
myData.age = 33
myData.friends.push('李四')
}
</script>
<template>
<div>{{ myData }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
潜响应式shallowReactive
注意:演示必须只单独测试深层的属性、如果同时访问根属性深属性也会改变
<script setup>
import { shallowReactive, reactive } from 'vue'
const state = shallowReactive({
count: 0,
user: {
name: 'jack',
age: 30
}
})
function m() {
// count 是响应式的,Vue 可以追踪到它的更改
state.count++;
// user 是一个普通对象,不是响应式的
state.user.name += '张三'; // 这个更改不会触发视图更新
}
</script>
<template>
<div>{{ state.user.name }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
响应式ref基础类型
原理:
reactive基于ES6的proxy
proxy只支持对象不支持基础类型
所以ref是封装了一个proxy的value
<script setup>
import { ref } from 'vue'
const myData = ref(0)
function m() {
myData.value++
}
</script>
<template>
<div>{{ myData }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
非响应式readonly只读
浏览器安装vue-devtools插件
<script setup>
import { readonly, shallowReadonly } from 'vue'
const myData = readonly({
name:'jack',
age:18
})
function m() {
myData.age++
console.log(myData.age)
}
</script>
<template>
<div>{{ myData }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
潜响应式shallowReadonly
注意:演示必须只单独测试深层的属性、如果同时访问根属性深属性也会改变
<script setup>
import { readonly, shallowReadonly } from 'vue'
const myData = shallowReadonly({
name:'jack',
age:18,
person:{age:18}
})
function m() {
myData.age++
myData.person.age++
}
</script>
<template>
<div>{{ myData }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
Computed计算属性
<script setup>
import { ref, computed } from 'vue'
const a = ref('这是一段测试内容')
const b = computed(()=>{
console.log('计算属性执行')
return a.value.length
})
function m() {
a.value += '!'
}
</script>
<template>
<div>{{ a }}</div>
<div>{{ b }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
Watch侦听器ref
<script setup>
import { ref, watch } from 'vue'
const a = ref(0)
watch(a,(newValue, oldValue)=>{
console.log(newValue, oldValue)
})
function m() {
a.value++
}
</script>
<template>
<div>{{ a }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
Watch侦听器reactive整个
<script setup>
import { reactive, watch } from 'vue'
const myData = reactive({name:'jack',age:18})
watch(myData,(newValue, oldValue)=>{
console.log(newValue, oldValue)
})
function m() {
myData.name = '张三'
myData.age = 88
}
</script>
<template>
<div>{{ myData }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
Watch侦听器reactive某个
<script setup>
import { reactive, watch } from 'vue'
const myData = reactive({name:'jack',age:18})
watch(()=>myData.age,(newValue, oldValue)=>{
console.log(newValue, oldValue)
})
function m() {
// myData.name = '张三'
myData.age = 88
}
</script>
<template>
<div>{{ myData }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
WatchEffect侦听器
<script setup>
import { ref, reactive, watchEffect } from 'vue'
const count = ref(0)
const myData = reactive({name:'jack',age:18})
watchEffect(()=>{
console.log(count.value)
console.log(myData.age)
})
function m() {
count.value++
myData.age++
}
</script>
<template>
<div>{{ count }}</div>
<div>{{ myData }}</div>
<div>
<button @click="m">按钮</button>
</div>
</template>
(5)Vue3核心语法三
入门案例
A.vue
<template>
<div>
Vue3入门案例
</div>
<div>
{{ myData.name }}
</div>
<div>
{{ myData.age }}
</div>
</template>
<script setup>
import { reactive } from 'vue'
const myData = reactive({
name:'jack',
age:18
})
</script>
<style lang="scss" scoped>
</style>
App.vue
<script setup lang="ts">
import A from './demo/A.vue'
</script>
<template>
<A />
</template>
导入组件
B.vue
<template>
<div>
导入组件
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
App.vue
<script setup lang="ts">
import B from './demo/B.vue'
</script>
<template>
<B />
</template>
父传子(defineProps)
App.vue
<script setup lang="ts">
import C from './demo/C.vue'
const myData = {
name: 'jack',
age: 18
}
const myData2 = {
name: 'stephen',
age: 28
}
</script>
<template>
<C x="sam" y="8"/>
<C v-bind="myData"/>
<C :="myData2"/>
</template>
C.vue方式一:数组形式
<template>
<div>
子组件
</div>
</template>
<script setup>
// 并没有import导入defineProps是因为defineProps是vue3编译时的宏函数
const props = defineProps(['x', 'y'])
console.log(props.x)
console.log(props.y)
</script>
<style lang="scss" scoped>
</style>
C.vue方式二:对象形式
<template>
<div>
子组件
</div>
</template>
<script setup>
const props = defineProps({
name: String,
age: Number
})
console.log(props)
console.log(props.name)
console.log(props.age)
</script>
<style lang="scss" scoped>
</style>
C.vue方式二扩展
<template>
<div>
子组件
</div>
</template>
<script setup>
const props = defineProps({
name: String,
age: {
type: Number,
default: 18,
requered: true
}
})
console.log(props)
console.log(props.name)
console.log(props.age)
</script>
<style lang="scss" scoped>
</style>
父传子(注意响应式数据)
App.vue
<script setup lang="ts">
import D from './demo/D.vue'
import { reactive } from 'vue'
// 错误原因:myData不是响应式数据
// const myData = {
// name: 'jack',
// age: 18
// }
// 正确写法:响应式函数
const myData = reactive({
name: 'jack',
age: 18
})
const m = () => {
myData.age++
console.log(myData.age)
}
</script>
<template>
<D :="myData"/>
<button @click="m">按钮</button>
</template>
D.Vue
<template>
<div>
{{ props.age }}
</div>
</template>
<script setup>
// 注意:类型必须是String和Number
const props = defineProps({
name: String,
age: {
type: Number,
required: true,
default: 8
}
})
</script>
<style lang="scss" scoped>
</style>
子传父(defineEmits)
E.vue
<template>
<button @click="m">按钮</button>
</template>
<script setup lang="ts">
// 宏函数
const emits = defineEmits(['getE1','getE2'])
emits('getE1', {name:'jack', age:18})
const m = ()=>{
emits('getE2', 100)
}
</script>
<style lang="scss" scoped>
</style>
App.vue
<script setup lang="ts">
import E from './demo/E.vue'
import {reactive, ref} from 'vue'
const m1 = (data)=>{
console.log(data)
}
const m2 = (data)=>{
console.log(data)
}
</script>
<template>
<div>
<E @getE1="m1" @getE2="m2" />
</div>
</template>
跨组件通信(provide 、inject )
App.vue
<script setup lang="ts">
import F from './demo/F.vue'
// provide可以把父组件中数据传递给任何子组件
import { reactive, provide } from 'vue'
// 非响应式
const myData = {name: 'jack'}
// 响应式
const myData2 = reactive({name: 'stephen'})
provide('provideKey', myData)
provide('provideKey2', myData2)
</script>
<template>
<F/>
</template>
F.vue
<template>
<div>
</div>
</template>
<script setup>
import { inject } from 'vue'
const myData = inject('provideKey')
console.log(myData)
const myData2 = inject('provideKey2')
console.log(myData2)
</script>
<style lang="scss" scoped>
</style>
传递函数(provide 、inject )
App.vue
<script setup lang="ts">
import G from './demo/G.vue'
import {reactive, ref, provide} from 'vue'
const myData = ref(0)
const MyMethod = ()=>{
myData.value++
}
provide('provideKey', MyMethod)
</script>
<template>
<G/>
{{ myData }}
</template>
G.vue
<template>
<button @click="m">按钮</button>
</template>
<script setup>
import { inject } from 'vue'
const m = inject('provideKey')
console.log(m)
</script>
<style lang="scss" scoped>
</style>
匿名插槽(父向子slot)
App.vue
<script setup lang="ts">
import H from './demo/H.vue'
// 插槽:可以在父组件内自定义模板片段、在子组件中可以将定义的模板片段插入到子组件的特定位置
</script>
<template>
<H>
<a href="http://www.baidu.com">百度</a>
</H>
</template>
H.vue
<template>
<slot/>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
具名插槽(父向子v-slot)
App.vue
<script setup lang="ts">
import K from './demo/K.vue'
</script>
<template>
<K>
<template v-slot:url>
<a href="http://www.baidu.com">百度1</a>
</template>
<template v-slot:url2>
<a href="http://www.baidu.com">百度2</a>
</template>
</K>
</template>
K.vue
<template>
<slot name="url"></slot>
<slot name="url2"></slot>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
作用域插槽(子向父slot )
M.vue
<template>
<!-- 作用域插槽:子组件向父组件传递数据、并在父组件定义的模板中渲染 -->
<slot name="url" title="jack" count="1000" />
<slot name="a" b="stephen" c="2000" />
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
App.vue
<script setup lang="ts">
import M from './demo/M.vue'
</script>
<template>
<M>
<template ##url="data">
{{ data.title }}
{{ data.count }}
</template>
<br>
<!-- 解构方式 -->
<template ##a="{b, c}">
{{ b }}
{{ c }}
</template>
</M>
</template>
生命周期函数(挂载、更新、卸载、错误)
N.vue
<template>
{{ a }}
<button @click="m">按钮</button>
</template>
<script setup>
import { onMounted, onUpdated, ref } from 'vue'
const a = ref(0)
console.log(a)
onMounted(()=>{
console.log('onMounted')
})
// 注意:必须是响应式数据更新
onUpdated(()=>{
console.log('onUpdated')
})
const m = () => {
a.value++
}
</script>
<style lang="scss" scoped>
</style>
App.vue
<script setup lang="ts">
import N from './demo/N.vue'
</script>
<template>
<N>
</N>
</template>
toRef和toRefs
<template>
</template>
<script setup>
// toRefs 和 toRef 是 Vue 3 的 Composition API 中用于将 reactive 对象或 ref 对象中的属性转换为 ref 对象的函数。
import { reactive, toRef, toRefs } from 'vue'
// (1) toRef 用于将 reactive 对象中的单个属性转换为 ref 对象。它接受两个参数:
// object:要转换的 reactive 对象
// key:要转换为 ref 的属性名
const state = reactive({ count: 0 })
const countRef = toRef(state, 'count')
console.log(countRef.value)
// (2) toRefs 用于将 reactive 对象中的多个属性转换为一个包含多个 ref 对象的数组。
// 它接受一个 reactive 对象作为参数,并返回一个包含 ref 对象的数组。
const state2 = reactive({ count: 100, name: 'John' })
const { count, name } = toRefs(state2)
console.log(count.value)
console.log(name.value)
</script>
<style lang="scss" scoped>
</style>
(5)Pinia
安装引入持久化存储插件
npm install pinia
Main.js
import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('##app')
npm i pinia-plugin-persistedstate
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
路径问题解决方案
//vue类型声明,让TS知道 .vue文件是什么
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
npm i @types/node -D
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve:{
alias:{
"@":path.resolve(__dirname,'./src')
}
}
})
{
"compilerOptions": {
...
"baseUrl": "./",
"paths": {
"@/*":["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
在src目录下新建store/index.js
import { defineStore } from "pinia";
// 第一个参数 storeId 是小仓库名称 必须独一无二
// 第二个参数是小仓库的配置对象,
// defineStore方法执行返回一个函数,函数执行可以让组件获取仓库实例
export const useStore = defineStore("storeId", {
state: () => {
return {
count: 0,
name: "张三",
};
},
getters: {},
actions: {},
});
在组件中使用Pinia的state数据
<template>
<div>
<h1>{{ count }}</h1>
<h1>{{ name }}</h1>
</div>
</template>
<script setup>
import { useStore } from '@/store/index'
// import { useStore } from '../store/index'
const store = useStore();
const { count, name } = store;
</script>
组件修改Pinia中的数据
本身pinia可以直接修改state数据,无需像vuex一样通过mutations才可以修改,但是上面写的const { name } = store;这种解构是不可以的,所以要换解构的方式。
R.vue
<template>
<div>
<h1>组件</h1>
{{ count }}
</div>
</template>
<script setup>
import { storeToRefs } from "pinia";
import { useStore } from "@/store/index";
const store = useStore();
// 第一种、直接修改
// store.count++;
// 第二种、调用$patch方法修改
// countStore.$patch({
// count: store.count + 2,
// });
// 第三种
// pinia提供了 一个 storeToRefs 方法 类似于 vue3中 toRefs
const { count } = storeToRefs(store);
count.value = 1000;
</script>
getters的使用
getters和vuex里的getters几乎类似,基于 已有的state 进行计算得到新值,会基于依赖进行缓存,多次使用依赖不变 不会重新计算
Index.js
import { defineStore } from "pinia";
// 第一个参数 storeId 是仓库的key 必须独一无二
export const useStore = defineStore("storeId", {
state: () => {
return {
num: 10,
name: "张三",
};
},
getters: {
// 1 箭头函数写法 参数是state
doubleNum: (state) => {
return state.num * 2;
},
// 2 对象方法写法
tribleNum(state) {
return state.num * 3;
},
},
actions: {},
});
R.vue
<template>
<div>
{{ doubleNum }}
{{ tribleNum }}
{{ tribleNum }}
<h1>组件</h1>
{{ num }}
</div>
</template>
<script setup>
import { storeToRefs } from "pinia";
import { useStore } from "../store/index";
const store = useStore();
//直接使用
console.log(store.doubleNum);
//解构
const { num, doubleNum, tribleNum } = storeToRefs(store);
</script>
actions的使用
actions就比较简单了,写入方法,比如我们可以让state中的某一个值+=,而且传入参数
Index.js
import { defineStore } from "pinia";
// 第一个参数 storeId 是仓库的key 必须独一无二
export const useStore = defineStore("storeId", {
state: () => {
return {
num: 10,
name: "张三",
};
},
getters: {
// 1 箭头函数写法 参数是state
doubleNum: (state) => {
return state.num * 2;
},
// 2 对象方法写法
tribleNum(state) {
return state.num * 3;
},
},
actions: {
addNum(n) {
// 直接通过this 千万不要写箭头函数
// 没有commit没有mutations去修改行下文
this.num += n;
},
},
});
R.vue
<script setup>
import { storeToRefs } from "pinia";
import { useStore } from "../store/index";
const store = useStore();
//直接使用
store.addNum(100);
</script>