vue3第一天
vue框架(4周)
react 框架(3-4周)
小程序和uniapp
web3.0和vue2 复习
就业周
vue3
官网:https://cn.vuejs.org/
<div id="box">
{{str}}
</div>
<div id="box2">
{{str}}
</div>
<script>
let obj ={str:"hello"}
//创建一个根实例
let app=Vue.createApp({
data(){ //data函数的作用向组件或容器提供数据 //Proxy
return {
str:'hello world' //响应式数据 数据变化了,视图会自动更新(vue会找到变更的差异部分,进行局部的刷新)
//数据驱动 (数据变化了,视图会自动更新)
}
}
}).mount("#box") //mount挂载,把app和特定的容器进行关联,关联以后,app就可以控制容器渲染的内容
console.log(app)
//面试题:data函数要返回一个对象?
//每一个实例有独立的数据对象互相之间不影响
let app2=Vue.createApp({
data(){
return {
str:"qf"
}
}
}).mount("#box2")
</script>
什么是vue
渐进式JavaScript框架 (声明式编程)
易学易用,性能出色,适用场景丰富的 Web 前端框架 vue性能:引入了虚拟demo机制,引入了diff算法,diff算法可以进行差异更新,采用了声明式编程
什么叫渐进式框架
弱主张的框架,只提供必要的和核心的功能,需要时再进行逐渐的扩展
搭建环境
脚手架环境(用命令搭建一个空的vue项目)
在html文件中引入vue.js(cdn)
<div id="box">
{{str}}
</div>
<script>
// console.log(Vue.createApp)
//创建一个根实例
let app=Vue.createApp({
data(){ //data函数的作用向组件或者容器提供数据
return{
str:'hello word' //响应式数据(数据变化了,视图也会自动更新)---vue会找到变更的差异部分,进行局部的刷新
//数据驱动:数据变化了,视图也会自动更新
}
}
}).mount('#box') //mount挂载,把app和特定的容器进行关联,关联以后,app就可以控制容器渲染的内容
console.log(app)
//面试题:data函数为什么要返回一个对象?
//因为每一个实例都有独立的数据对象,它们互相之间不影响
</script>
差值表达式
{{js表达式}} 是差值表达式 mustache
含有差值表达式的html叫模板(template)
<div id="box">
{{str}} --- {{str.toUpperCase()}} ---反转 {{str.split("").reverse().join("")}}
</div>
<script>
let appInstance = Vue.createApp({
data(){
return {
str:"hello world"
}
},
// template:`<div> //template的优先级比 mount挂载的元素高!
// test hello
// </div>
// `
});
let app = appInstance.mount("#box");
</script>
MVVM
M model 模型 数据
v view 视图 渲染数据
vm viewmodel 视图模型 数据驱动
vue的两大核心是数据驱动和组件系统
指令(directive)
组件(component)
路由(router)
vuex
第三方库
指令
1.v-text 作用和{{差值表达式}}一样
2.v-bind 把变量或数据绑定到对象的属性上 v-bind:属性=“变量" 简写为":" 数据变化视图会变化,视图变化,数据不会变化,所以是一个单向绑定指令
3.v-model 双向绑定指令 (v-bind和v-on的合体)
修饰符
.lazy 文本框失去焦点的时候再帮我们将文本框的值同步给Model
.number 把文本框的数据当成数值处理
.trim 去掉前后空格
4.v-on 指令 可以简写为@ $event 是事件对象
可以响应多个事件对象
<button v-on="{'mouseover':()=>change(1),'mouseout':change.bind(this,-1)}">test</button>
事件处理函数放到methods里
methods:{
// increment(){
// this.n=this.n+1
// },
// sub(){
// if(this.n>1){
// this.n=this.n-1
// }
// }
change(p){
console.log("change",p)
// if(e.target.innerHTML==="-" && this.n>1){
// this.n--;
// }
// else if(e.target.innerHTML==="+"){
// this.n++;
// }
// this.n=Math.max(this.n+p,1)
if(this.n+p>1){
this.n=this.n+p;
}
else{
this.n=1
}
}
}
5.v-if和v-show
区别 v-show 只是切换元素的css样式,可见还是不可见
v-if 惰性指令 true 才渲染 false 不渲染
6.v-for
<div id="box">
<ul>
<li v-for="item in list" :key="item.id">
//key是个唯一的值,通过key值能找到对应的同一个节点,判断数据的变化,从而减少比对次数,提高比对效率。
{{item.name}} {{item.age}}
</li>
</ul>
</div>
<script>
const list=Mock.mock({
"list|30":[{
id:"@id",
name:'@cname',
"age|20-30":25
}]
})
console.log(list)
let app = Vue.createApp({
data() {
return {
list:list.list
}
}
}).mount('#box')
v-else
v-else-if
vue3第二天
npm i vue -S
绑定样式
绑定style
v-bind:style="{key:value.....}"
v-bind:style="[styleObj1,styleObj2.....]"
绑定class
v-bind:class="{className:布尔表达式,。。。。。}"
v-bind:class="[{clasaName:布尔表达式},{ }]"
von修饰符
.stop 阻止冒泡
.capture 捕获模式
.prevent 阻止默认的行为
.once 事件只响应一次
.self 只有本身响应
.enter 回车事件
vfor遍历数字,遍历字符串,对象
<div id="box">
<!-- <ul>
<li v-for="x in str" :key="x">{{x}}</li>
</ul>
<ul>
<li v-for="(value,key,index) in obj">
{{index}}---{{key}}---{{value}}
</li>
</ul>-->
<ul>
<li v-for="(item,index) in arr" :key="item">
{{index}}--{{item}}
</li>
</ul>
</div>
计算属性
<div id="box">
<span>{{str}}</span>
</div>
<script>
let app = Vue.createApp({
data() {
return {
str:'海鸥'
}
},
render(){ //vue运行时会把模板转换成render函数
// console.log(Vue.h)
return Vue.h("span",{title:'哈哈哈哈'},this.str)
}
}).mount("#box")
</script>
<div id="box">
<input type="text" v-model="str"> <br>
{{reverseStr}}
<button @click="getC">获取计算属性的值</button>
</div>
<script>
let app = Vue.createApp({
//计算属性:值变化了,返回的计算值自动更新(如果数据不变化取缓存(取上一次计算的结果))
computed:{
reverseStr(){
return this.str.split('').reverse().join('')+Math.random().toFixed(2)
}
},
data() {
return {
str:'hhajsuhdhshaewy'
}
},
methods: {
getC(){
console.log(this.reverseStr)
}
},
}).mount("#box")
</script>
计算属性 get和set的用法
<div id="box">
<input type="text" maxlength="20" v-model="str">
<button @click="info='海鸥'">点击按钮</button>
{{info}}
</div>
<script>
let app = Vue.createApp({
computed:{
info:{
get(){
return `还可以输入${20-this.str.length}个字`
},
set(value){
this.str=value
}
}
},
data() {
return {
str:""
}
},
}).mount("#box")
</script>
/**
* v-bind v-model
* v-if v-show
* v-text v-html
* v-on v-for
* v-memo v-once
* v-pre v-cloak
*/
vue3第三天
组件 component
可以复用的页面的一部分,叫做组件(本质就是一组dom元素的封装)
组件的分类
全局组件:一旦声明可以在任何地方使用
局部注册的组件:需要调用 才能使用
<div id="box">
<One></One>
全局(<Local></Local>)
</div>
<script>
//vnode
// let div={
// tag:"div",
// props:{
// id:"box"
// },
// }
let Local=Vue.defineComponent({ //定义一个局部注册的组件
template:`<div>局部注册的组件</div>`
})
let appInstance = Vue.createApp({ //全局组件
components:{ //局部注册的组件,需要在组件里进行里注册,才能在这个组件的模板里使用
Local
},
data() {
return {
}
}
})
appInstance.component("Two",{ //全局组件在任何其他组件的模板里都可以使用,不用注册
template:`<div>two component</div>`
})
appInstance.component("One",{
components:{ //注册组件
Local
},
template:`<div>one component One( <Local></Local></div>) <Two></Two>`
})
let app = appInstance.mount('#box')
</script>
组件component
1.定义
可以复用的页面的一部分,叫组件(本质就是一组dom元素的封装)
2.分类
全局组件
<div id="box">
<!-- 不用注册就可以在App的模板里使用 -->
<Counter></Counter>
</div>
<script>
let appInstance = Vue.createApp({
data() {
return {}
}
})
//定义全局组件Counter
appInstance.component("Counter",{
template:`
<div>Counter组件 {{n}} <button @click="inc">+</button></div>
`,
data(){
return {
n:1
}
},
methods:{
inc(){
this.n++
}
}
})
let app= appInstance.mount('#box')
</script>
局部注册的组件
组件通讯
1.父传子
<div id="box">
<!-- 父组件通过自定义属性把值传给子组件 -->
<box-message money="111"></box-message>
<button>弹出</button>
</div>
<script>
let boxMessage = {
props:["money"], //子组件通过 props接受父组件传过来的值
template: `<div class='message'></div>`
}
let app = Vue.createApp({
components: {
boxMessage
},
data() {
return {
flag:false //控制对话框的显示和隐藏
}
}
}).mount('#box')
<div id="box">
<!-- 父组件通过自定义属性把值传给子组件 没有绑定数据传的任何数据都是字符串,加上绑定可以识别各种数据类型和变量-->
<box-message v-bind:money="111" :title="title" :flag="flag" :arr="arr" :index="index"></box-message>
<button @click="showBox('添加')">添加</button>
<ul>
<li v-for="(item,index) in arr" :key="index">{{item}} <button @click="showBox('修改',index)">修改</button></li>
</ul>
</div>
<script>
let boxMessage = {
props:["money","flag","title","arr","index"], //子组件通过 props接受父组件传过来的值
template: `<div class='message' v-show="flag">
{{index}}
<h3>{{title}}</h3>
{{money}} {{typeof money}} {{flag}}
<input type='text' v-model="str" @keyup.enter="add" v-if="title==='添加'" />
<input type='text' v-model="str" @keyup.enter="modify" v-else />
</div>`,
data(){
return {
str:""
}
},
watch:{ //监听数据的变化
index(newIndex){
this.str=this.arr[newIndex];
}
},
methods:{
add(){
this.arr.push(this.str);
this.str="";
//error 这是错误的做法,父组件传给子组件的基本数据类型的值不能改因为是只读的(单向数据流)
//父组组件传给子组件引用数据类型的值是可以改的 ,因为地址没变
// this.flag=false error
this.$parent.flag=false; //这种方法少用,因为破坏了单向数据流
},
modify(){
console.log("修改")
}
}
}
let app = Vue.createApp({
components: {
boxMessage
},
methods:{
showBox(title,index){
this.flag=true; //对话框可见
this.title=title
this.index=index;
}
},
data() {
return {
index:-1, //要修改元素的下标
flag:false, //控制对话框的显示和隐藏
title:"添加",
arr:['aa','bb']
}
}
}).mount('#box')
</script>
props验证
type:类型
default:默认值
required:是否必须传
validator :自定义验证规则
2.子传父
<div id="box">
<input1 @txt="jia"></input1>
<!-- <ul>
<li v-for="(item,index) in arr" :key="index">
{{item}}
</li>
</ul> -->
<lii :arr="arr"></lii>
</div>
<script>
let lii={
props:["arr"],
template:`
<ul>
<li v-for="(item,index) in arr" :key="index">
{{item}}
</li>
</ul>
`
}
let input1 = {
template: `
<input type="text" v-model="str" @keyup.enter="send">
`,
data(){
return{
str:''
}
},
methods: {
send(){
this.$emit('txt',this.str);
this.str=''
}
},
}
let app = Vue.createApp({
components: {
input1,lii
},
data() {
return {
arr: ['海鸥', '哈哈哈']
}
},
methods:{
jia(str){
this.arr.push(str)
}
}
}).mount("#box")
</script>
3.非父子组件的传值
<!-- 一定要引入mitt这个库,才是在vue3中实现中央消息总线 -->
<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
<div id="box">
<One></One>
<Two></Two>
</div>
<script>
let emitter = mitt() //需要去引入mitt 发布订阅模式】、
let One = {
template: `<div>One <button @click="send"> send </button> </div>`,
methods: {
send(){
.j//emit 向兄弟组件传值
}
}
}
let Two = {
template: `<div>Two {{data}}</div>`,
data(){
return{
data:23
}
},
created () { //生命周期的钩子函数,在实例处理完所有状态的相关的选项后调用
emitter.on('haiou',(data)=>{ //on 接受兄弟组件传过来的值 data就是其他组件传递过来的值
this.data=data
})
}
}
let app = Vue.createApp({
components: {
One,Two
},
data() {
return {
}
},
}).mount("#box")
</script>
4.爷孙组件传值
爷组件
export default {
components: { Test },
data(){
return{
n:2323
}
},
**** provide(){
return {
n:this.n
}
}
}
爷组件(使用computed可以使传的值有响应 爷组件的值改变了 孙子的值也会改变)
<template>
<div>
<Test></Test>
{{ n }} <button @click="n++">+</button>
</div>
</template>
<script>
import Test from './components/Test.vue';
*** import {computed} from 'vue'
export default {
components: { Test },
data(){
return{
n:2323
}
},
**** provide(){
return {
n:computed(()=>this.n)
}
}
}
</script>
<style lang="scss" scoped>
</style>
孙组件
<template>
<div>
TestSon {{ n }}
</div>
</template>
<script>
export default {
inject:['n'] ***
}
</script>
<style lang="scss" scoped>
</style>
watch
监控数据的变化
vue3第四天
Object.values 和 Object.keys
let obj={
a:1,b:2,c:3
}
Object.values(obj) //[1,2,3] Object.values---将对象的值拿到存在数组里面
Object.keys(obj) //['a','b',c] Object.keys----将对象的键拿到存在数组里面
计数器
<div id="box">
<!-- <Counter :n="obj.n" @nn="change" name="n"></Counter>
<Counter :n="obj.m" @nn="change" name="m"></Counter>
<Counter :n="obj.h" @nn="change" name="h"></Counter> -->
<Counter v-for="(value,key,index) in obj" :n="value" @nn="change" :name="key" :key="key"></Counter>
{{sum}}
</div>
<script>
let Counter = {
props: ["n", "name"],
template: `<div>计数器 {{n}} <button @click="add">+</button> </div>`,
methods: {
add() {
this.$emit('nn', this.n + 1, this.name)
}
}
}
let app = Vue.createApp({
computed: {
sum() {
// return this.obj.n + this.obj.m + this.obj.h
return Object.values(this.obj).reduce((a,b)=>a+b)
}
},
components: {
Counter
},
data() {
return {
obj: {
n: 3,
m: 6,
h: 7,
jj:99,
kk:2
}
}
},
methods: {
change(...arges) {
console.log(arges);
this.obj[arges[1]] = arges[0]
},
}
}).mount("#box")
</script>
生命周期的钩子函数
<div id="box">
<input type="text" id="txt" ref="txt">{{str}}
<Child ref="child"></Child>
<button @click="sc">删除</button>
</div>
<script>
// 生命周期的钩子函数 => 在组件的各个生命周期中可以自动调用的函数叫做生命周期的钩子函数
// 创建阶段 挂载阶段 更新阶段 销毁阶段
// 1. beforeCreate 实例创建前触发
// 2. created 实例创建完成,
// 3. beforeMount 模板渲染前,可以访问数据,模板编译完成,虚拟DOM已经存在
// 4. mounted 模板渲染完成,可以拿到DOM节点和数据
// 5. beforeUpdate 更新前
// 6. updated 更新完成
// 7. beforeUnmount 销毁前
// 8. unmounted 销毁后
let Child = {
template: `<div>child</div>`,
methods: {
fun() {
console.log('哈哈哈哈哈')
}
},
//创建的钩子函数
beforeCreate() { //不能访问数据
console.;log('child beforeCreate')
},
created() { //可以访问数据
console.log('child created')
},
//挂载的钩子函数
beforeMount() { //能拿到数据 但拿不到dom节点
// console.log('beforeMount',this.str,document.getElementById('txt'))
console.log('child beforeMount')
},
mounted() { //即能拿到数据 还能拿到dom节点
// console.log('mounted',this.str,document.getElementById('txt'))
console.log('child mounted')
},
}
let appIntance = Vue.createApp({
components: {
Child
},
data() {
return {
str: '海鸥'
}
},
//创建的钩子函数
beforeCreate() { //不能访问数据
console.log('app beforeCreate', this.str)
},
created() { //可以访问数据
console.log('app created', this.str)
},
//挂载的钩子函数
beforeMount() { //能拿到数据 但拿不到dom节点
// console.log('beforeMount',this.str,document.getElementById('txt'))
console.log('app beforeMount', this.str, this.$refs.txt)
},
mounted() { //即能拿到数据 还能拿到dom节点
// console.log('mounted',this.str,document.getElementById('txt'))
console.log('app mounted', this.str, this.$refs.txt),
this.$refs.child.fun()
},
//更新阶段
beforeUpdate() { //更新dom数前
console.log('app beforeUpdate',this.str)
},
updated() { //更新dom数之后
console.log('app updated',this.str)
},
//销毁阶段
beforeUnmount() {
console.log('app beforeUnmount')
},
unmounted() {
console.log('app unmounted')
},
methods:{
sc(){
appIntance.unmount()
}
}
})
appIntance.mount("#box")
</script>
子组件给父组件传值
-
监听
<My-form :index="index" :arr="arr" v-on:evt="changeIndex($event)"></My-form> -
发送
this.$emit("evt",0) //2.发送是新值给父组件 -
接收
changeIndex(n){ //3.接收 在事件处理函数里接收子组件传过来的值n console.log(n) this.index=n; }
v-model 双向绑定组件
<组件 v-model="变量" />等价于
<组件 v-bind:modelValue="变量" v-on:update:model-value="变量=$event" />
兄弟组件传值
<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
let emitter= mitt();
let One={
template:`<div>one compnent <button @click="send">send</button></div>`,
methods:{
send(){
emitter.emit("msg",12345) //向兄弟组件传值
}
}
}
let Two={
template:`<div>Two compnent {{num}}</div>`,
data(){
return {
num:0
}
},
created(){ //生命周期的钩子函数 在实例处理完所有与状态相关的选项后调用
emitter.on("msg",(data)=>{ //data就是其他组件传递过来的值
this.num=data;
})
}
}
指令 12
组件
组件定义和使用
组件的传值
生命周期的钩子函数
动态组件
插槽
路由
vuex
第三方库
生命周期的钩子函数
在组件的各个生命周期中可以自动调用的函数叫生命周期的钩子函数
创建阶段
beforeCreate 实例初始化时调用
created 在实例处理完所有与状态相关的选项后调用
挂载阶段
beforeMount 在实例挂载之前调用 可以拿到数据,但是拿不到真实的dom节点
mounted 挂载之后调用 既可以拿到数据,也可以拿到真实的dom节点
ref
<input ref="节点标识" />
this.$refs.ref节点标识 拿到节点
this.$refs.ref节点标识.数据名字 拿到数据
创建和挂载节点父子组件的执行顺序
app beforeCreate undefined
app created hello
app beforeMount hello undefined
child beforeCreate
child created
child beforeMount
child mounted
app mounted
更新阶段
beforeUpdate 状态已经更新后,DOM树更新前调用
updated 状态已经更新后 DOM树更新后调用
销毁阶段
beforeUnmount
组件卸载之前调用 清理资源,防止内存泄露
unmounted
组件卸载之后调用
<div id="box">
<input type="text" id="txt" ref="txt">{{str}}
<Child ref="child"></Child>
<button @click="sc">删除</button>
</div>
<script>
// 生命周期的钩子函数 => 在组件的各个生命周期中可以自动调用的函数叫做生命周期的钩子函数
// 创建阶段 挂载阶段 更新阶段 销毁阶段
// 1. beforeCreate 实例创建前触发
// 2. created 实例创建完成,
// 3. beforeMount 模板渲染前,可以访问数据,模板编译完成,虚拟DOM已经存在
// 4. mounted 模板渲染完成,可以拿到DOM节点和数据
// 5. beforeUpdate 更新前
// 6. updated 更新完成
// 7. beforeUnmount 销毁前
// 8. unmounted 销毁后
let Child = {
template: `<div>child</div>`,
methods: {
fun() {
console.log('哈哈哈哈哈')
}
},
//创建的钩子函数
beforeCreate() { //不能访问数据
console.log('child beforeCreate')
},
created() { //可以访问数据
console.log('child created')
},
//挂载的钩子函数
beforeMount() { //能拿到数据 但拿不到dom节点
// console.log('beforeMount',this.str,document.getElementById('txt'))
console.log('child beforeMount')
},
mounted() { //即能拿到数据 还能拿到dom节点
// console.log('mounted',this.str,document.getElementById('txt'))
console.log('child mounted')
},
}
let appIntance = Vue.createApp({
components: {
Child
},
data() {
return {
str: '海鸥'
}
},
//创建的钩子函数
beforeCreate() { //不能访问数据
console.log('app beforeCreate', this.str)
},
created() { //可以访问数据
console.log('app created', this.str)
},
//挂载的钩子函数
beforeMount() { //能拿到数据 但拿不到dom节点
// console.log('beforeMount',this.str,document.getElementById('txt'))
console.log('app beforeMount', this.str, this.$refs.txt)
},
mounted() { //即能拿到数据 还能拿到dom节点
// console.log('mounted',this.str,document.getElementById('txt'))
console.log('app mounted', this.str, this.$refs.txt),
this.$refs.child.fun()
},
//更新阶段
beforeUpdate() { //更新dom数前
console.log('app beforeUpdate',this.str)
},
updated() { //更新dom数之后
console.log('app updated',this.str)
},
//销毁阶段
beforeUnmount() {
console.log('app beforeUnmount')
},
unmounted() {
console.log('app unmounted')
},
methods:{
sc(){
appIntance.unmount()
}
}
})
appIntance.mount("#box")
</script>
activated 组件启用缓存的时候调用 deacitvated 组件停用缓存的时候调用
vue3第五天
手动搭建vue3
npm init -y 初始化
npm i vue -S 安装vue
npm i vite -S 打包
npx vite 运行
在package.json文件夹中更改运行命令
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"dev":"npx vite" //dev 这个单词可以随便起
},
运行方式:npm run dev 等价于 npx vite
npx vite build 打包项目
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"dev": "npx vite",
"build":"npx vite build" //build这个单词可以随便起
},
运行方式:npm run build 等价于 npx vite build
html
<div id="box">
</div>
<script src="./js/vue.js" type="module"></script>
js
import {createApp,h} from 'vue'
let app = createApp({
render(){
return h('span',null,this.yy) //span标签 null属性 yy内容
},
data() {
return {
yy: '海鸥'
}
},
}).mount("#box")
自动搭建vue3
npm init -y 初始化
npm init vue@latest 搭建脚手架
npm i sass -S 写样式 不报错
npm i normalize.css 重置css的样式
购物车中的加加减减
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id" class="row">
<p><input type="checkbox" v-model="item.flag" /></p>
<div>
<img :src="item.img" width="120"/>
</div>
<div>
{{ item.text }}<br />
<button @click="changeCount(item.id,-1)">-</button>
{{ item.count }}
<button @click="changeCount(item.id,1)">+</button>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ["list"],
//方法
methods:{
changeCount(id,p){
let index=this.list.findIndex(item=>item.id===id)
this.list[index].count= Math.max(this.list[index].count +p,1)
}
}
}
</script>
<style lang="scss" scoped>
.row{
display: flex;
align-items: center;
}
</style>
<template>
<div>
<p>哈哈哈</p>
<div class="item">app</div>
<div class="item">海鸥</div>
</div>
</template>
<script>
export default {
};
</script>
<style lang="scss" scoped> //scoped 只对当前的组件有作用
div{
p{
color: blue;
&:hover{ //上一级
color: aqua;
}
}
background-color: red;
.item{
color: green;
}
}
</style>
动态组件
根据组件的名字来渲染组件的内容
<Keep-alive></Keep-alive> //用来包裹 <component></component> 组件 可以保存组件的内容
activated 组件启用缓存的时候调用
deacitvated 组件停用缓存的时候调用
<keep-alive :include="['One']"> //给one和two组件写一个name:对应的名字 然后在keep-alive中操作,写了one这样就只保存one 中的数据
<component :is="cname"></component>
</keep-alive>
cname
<template>
<div>
动态组件
<keep-alive>
<component :is="cname"></component>
</keep-alive>
<button @click="cname='One'">one</button>
<button @click="cname='Two'">two</button>
</div>
</template>
<script>
import One from "./One.vue";
import Two from "./Two.vue";
export default {
components: { One, Two },
data(){
return{
cname:'One'
}
},
};
</script>
<style lang="scss" scoped></style>
插槽(slot)
组件的预留位置可以在不修改组件代码的前提下,扩展组件的内容
匿名插槽
Demo.vue
<template>
<div>
<slot></slot> <input type="text">
Demo组件
</div>
</template>
<script>
export default {
};
</script>
<style lang="scss" scoped></style>
app.vue
<template>
<div>
<Demo>
<template v-slot:default> // 使用v-slot:绑定插槽 ,default 表示没有名字的插槽 ----匿名插槽
<span>用户名:</span>
</template>
</Demo>
</div>
</template>
<script>
import Demo from './components/Demo.vue'
export default {
components:{
Demo
}
}
</script>
<style lang="scss" scoped>
</style>
有名字的插槽----具名插槽
<slot name="btn"></slot>
<template v-slot:btn>
<button>添加</button>
</template>
v-slot: 可以简写为
#
通过插槽传值 ----作用域插槽
<slot name="btn" :n="5"></slot>
<template v-slot:btn="props" > //props 这个名字不定
<button>添加 {{ props.n }}</button>
</template>
添加
add() {
if (!this.newsTitle || !this.content) {
return alert("请输入完整数据");
}
this.list.push({
id: Date.now(),
title: this.newsTitle,
content: this.content,
});
this.close();
},
删除
remove(id){
let index=this.list.findIndex(item=>item.id===id);
// console.log(index)
this.list.splice(index,1)
}
修改
editOk() {
let index = this.list.findIndex((item) => item.id === this.id);
this.list[index].title = this.newsTitle;
this.list[index].content = this.content;
this.close();
},
搜索
computed:{
filterList(){
return this.list.filter(item=>item.title.includes(this.kw))
}
},
全选和全不选
computed:{
select:{
get(){
return this.list.every(item=>item.flag)
},
set(v){
// console.log(v)
this.list.forEach((item)=>{item.flag=v})
}
}
},
vue3第六天
watch的使用
<template>
<div>
demo{{ n }} <button @click="n++">+</button>
{{ obj }}
<button @click="obj.a++">+</button>
</div>
</template>
<script>
export default {
data(){
return{
n:5,
obj:{
a:1
}
}
},
// watch:{
// n(newV,oldV){
// console.log(newV,oldV)
// }
// }
watch:{
n:{ //监听普通数据
handler(newV,oldV){
console.log(newV,oldV)
}
},
obj:{ //监听对象
handler(newV){
console.log(newV)
},
deep:true, //对象要用深度监听,否则监听不到
immediate:true //立即监听
}
}
}
</script>
<style lang="scss" scoped>
</style>
npm i swiper@5 -S 下载轮播图插件
npm i axios -S 下载axios
nextTick
是一个事件循环中的异步队列中的微任务
this.$nextTick 延迟执行回调,直到dom加载完毕
<ul>
<li v-for="(item,index) in list" :key="index" ref="lii">
{{ item }}
</li>
</ul>
methods:{
add(){
this.list.push(23);
this.$nextTick(()=>{
console.log(this.$refs.lii)
})
}
}
组件异步传值
<TestSwiper :imgs="imgs" v-if="imgs.length>0"></TestSwiper>
跨域设置
代理跨域
vite.config.js
server: {
proxy: {
"/api": {
target: "http://121.89.205.189:3000",
changeOrigin: true,
},
},
},
axios.get("/api/banner/list")
vue3第七天
vue3中 v-if的优先级比v-for的优先级高
watch跟computed的区别 (面试题)
1、功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
2、是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。
3、是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
4、computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)
5、使用场景:computed----当一个属性受多个属性影响的时候,使用computed-----购物车商品结算。watch–当一条数据影响多条数据的时候,使用watch-----搜索框.
computed的值依赖多个数据,一个值变化了,他的值会自动计算,watch的值依赖多个数据,需要监听多个数据
路由
根据不同的url渲染不同的组件
路由有两种模式
hash模式 # -------createWebHashHistory
history模式 ------------createWebHistory
<RouterLink to="/页面名称">对应的标题</RouterLink> ----链接
<RouterView></RouterView> ----渲染页面
路由的重定向和路由的别名
import {createRouter,createWebHistory} from 'vue-router'
import Home from '@/views/Home.vue'
const router = createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history:createWebHistory(),
routes:[
{
path:'/',
// redirect: "/home" //路由的重定向 方法1
// redirect:{ //方法2
// name:'home'
// },
redirect:'/h' //方法3
},
{ //Route对象
path:'/home',
name:'home', //命名路由 对应方法2
alias:"/h", //别名 对应方法3
component:Home
},
{
path:'/two',
// component:Two
component:()=>import('@/views/Two.vue')
}
]
});
export default router
7
配置404页面
<template>
<div>
app
<router-link to="/"> 首页</router-link>
<router-link to="/two">第二页</router-link>
<router-link to="/xxx">xxx</router-link>
<RouterView msg="我是HelloWorld组件"></RouterView>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
.router-link-active{
color: red;
}
</style>
使用页面只显示404页面方法1
<template>
<div>
//使用一个div 将这三个组件包起来
<div v-show="flag">
<router-link to="/"> 首页</router-link>
<router-link to="/two">第二页</router-link>
<router-link to="/xxx">xxx</router-link>
</div>
<RouterView msg="我是HelloWorld组件"></RouterView>
</div>
</template>
<script>
export default {
data() {
return {
flag: true, //为true 导航条可见 为false导航条不可见
};
},
//方法1 使用watch监听路由的变化
watch: {
$route: {
handler(n) {
this.flag = !n.meta.visible;
},
deep: true,
immediate: true,
},
},
};
+
</script>
<style lang="scss" scoped>
.router-link-active {
color: red;
}
</style>
使用页面只显示404页面方法2
<template>
<div>
//使用一个div 将这三个组件包起来 然后使用 v-show="!$route.meta.visible" 点击的时候xxx的时候只显示404页面
<div v-show="!$route.meta.visible">
<router-link to="/"> 首页</router-link>
<router-link to="/two">第二页</router-link>
<router-link to="/xxx">xxx</router-link>
</div>
<RouterView msg="我是HelloWorld组件"></RouterView>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
.router-link-active {
color: red;
}
</style>
配置404页面 在路由上操作的重要代码
{
path:"/:pathMatch(.*)",
component:()=>import("@/views/NotFound.vue"),
meta:{ //路由对象的元信息
visible:true //true表示导航条是不可见的
}
}
动态组件
可以缓存
include 决定缓存哪个组件
<KeepAlive :include="['One']">
<component :is="cname"></component>
</KeepAlive>
路由
根据不同的url渲染不同的组件
vue-router 4
路由有两种模式
hash # location.hash onhashchange createWebHashHistory()
history模式 createWebHistory()
components 小组件 views 页面的组件
使用步骤
-
npm i vue-router -S
-
引入 import {createRouter,createWebHashHistory} from 'vue-router'
-
创建实例
const router = createRouter({ // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。 history: createWebHashHistory(), //hash模式 routes:[] }) -
使用路由插件
app.use(router)
5.组件里就可以使用 RouterLInk 和 RouterView 组件里
路由相关的样式
.router-link-active 对当前 routerLInk 组件有效,也对嵌套路由里的 routerlink 有效
.router-link-exact-active 只对当前 routerLInk 组件有效,对嵌套路由里的 routerlink 无效
异步组件
切换到这个组件的时候才进行加载,起到一个路由级别的代码分割的作用
{
path:"/two",
component:()=>import("@/views/Two.vue")
}
重定向命名路由别名
{
path:"/",
// redirect:"/home" //路由的重定向
// redirect:{
// name:'home'
// }
redirect:"/h"
},
{ //Route对象
path:"/home",
name:"home", //命名路由
alias:'/h', //别名
component:Home
},
404页面
{
path:"/:pathMatch(.*)",
component:()=>import("@/views/NotFound.vue")
}
meta
meta:{ //元信息
visible:true //true 表示导航条不可见的
}
<div class="nav" v-show="!$route.meta.visible">
<RouterLink to="/home">首页</RouterLink>
<RouterLink to="/two">第二页</RouterLink>
<RouterLink to="/xxx">xxx</RouterLink>
</div>
vue3第八天
vue移动端适配 rem适配法
- 安装 npm i amfe-flexible postcss-pxtorem -S
-安装 npm i normalize.css 重置css的样式
-
amfe-flexible 用于设置根字体大小的
-
postcss-pxtorem 用来自动转换单位的
-
在根目录创建一个 .postcssrc.js
module.exports = { plugins: { "postcss-pxtorem": { rootValue: 37.5, propList: ["*"], }, }, };
main.js
一定要在main.js里面引入这两个文件
import 'normalize.css'
import "amfe-flexible"
用自己手机检测移动端的适配
在vite.config.js中写下面的代码,用电脑给手机开热点,然后重启项目 ,会出现一个地址,用手机输入地址
server:{
host:'0.0.0.0'
},
代理跨域
必须安装 npm i axios -S
vite.config.js 方法1
server:{
host:'0.0.0.0',
proxy:{
"/api":{
"target":"http://47.94.148.165:3001",
"changeOrigin":true,
"rewrite":(path)=>path.replace(/^/api/,'')
}
}
},
App.vue
<script>
import axios from 'axios'
export default {
mounted(){
axios.get('/api/pro/list? count=1').then((res)=>{
console.log(res)
})
}
}
</script>
vite.config.js 方法2
server:{
host:'0.0.0.0',
proxy:{
"/hg":{ //如果使用hg 那么需要使用rewrite把默认的hg去掉
"target":"http://47.94.148.165:3001",
"changeOrigin":true,
rewrite: (path) => path.replace(/^/hg/, ''),
}
}
},
App.vue
<script>
import axios from 'axios'
export default {
mounted(){
axios.get('/hg/api/pro/list? count=1').then((res)=>{
console.log(res)
})
}
}
</script>
放静态资源的
public 打包时不参与
assets 打包时参与
子路由
在components写一个文件夹用于存放对应路由的子路由,下面是子路由的写法
{
path: "/my",
name: "my",
component: () => import("../views/My.vue"),
children: [
{
path: "tou",
component: () => import("../components/my/tou.vue"),
},
{
path: "xinx",
component: () => import("../components/my/xinx.vue"),
},
],
},
子路由在父路由的写法
<div>
<router-link to="/my/tou">头像</router-link>
<router-link to="/my/xinx">信息</router-link>
<router-view></router-view>
</div>
route 和router 的区别
route 路由的对象 身上都是属性
router 是 vue router的实例 身上都是方法
编程式导航
<button @click="go('/cart')">购物车</button>
methods:{
go(path){
this.$router.push(path)
}
}
go :go(-1) 返回 go(+1) 前进
back : 返回
forward :前进按钮
push :可以跳转到任何页面 同时可以返回当前页面
replace 替换 不能返回当前页面
addRoute
路由传值
1.动态路由
//路由传值方法1---动态路由传值
{
path:"/detail/:id",
component:()=>import('../views/Detail.vue')
},
<!-- //路由传值方法1---动态路由传值 -->
<ul>
<li v-for="(item, index) in list" :key="item.proid" class="row" @click="$router.push(`/detail/${item.proid}`)">
<div class="item">
<img :src="item.img1" alt="" />
<h3>{{ item.proname }}</h3>
</div>
</li>
</ul>
//路由传值方法1---动态路由传值
mounted() { axios.get(`/api/pro/detail/${this.$route.params.id}`).then((res) => {
console.log(res.data);d1` X
if (res.data.code == "200") {
this.deatilObj = res.data.data;
}
});
},
//this.$route.params.id 拿到对应的值
2.查询字符串传值
//路由传值方法1---查询字符串传值
{
path:"/xq",
component:()=>import('../views/Detail.vue')
},
<!-- //路由传值方法1---查询字符串传值 -->
<ul>
<li v-for="(item, index) in list" :key="item.proid" class="row" @click="$router.push(`/xq?id=${item.proid}`)">
<div class="item">
<img :src="item.img1" alt="" />
<h3>{{ item.proname }}</h3>
</div>
</li>
</ul>
//路由传值方法1---查询字符串传值
mounted() {
axios.get(`/api/pro/detail/${this.$route.query.id}`).then((res) => {
console.log(res.data);
if (res.data.code == "200") {
this.deatilObj = res.data.data;
}
});
},
vue3第九天
实现路由切换的动画效果
<template>
<div class="box">
<router-view v-slot:default="{ Component }">
<Transition mode="out-in"> //必要的
<component :is="Component"></component>
</Transition>
</router-view>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
.box {
color: red;
}
//CSS的效果 必要的
.v-enter-from {
transform: translateX(-100%);
}
.v-enter-active,.v-leave-active {
transition: all 1s;
}
.v-leave-to {
transform: translateX(100%);
}
</style>
配置全局 axios
必须安装 npm i axios -S
配置全局axios 在main.js当中写这两句话 然后要使用axios的时候需要写成 this.axios....
import axios from 'axios'
app.config.globalProperties.axios=axios
防跳墙
导航守卫的分类:
1、全局的路由导航守卫:
跳转前 beforeEach
vue3的防跳墙写法
//全局的路由的前置守卫
router.beforeEach((to,from)=>{
console.log(to,from) //to 目标路由对象 from 原路由对象
if(to.fullPath==='/login'){ //运行进入登录界面
return true; //如果是 return false 不允许跳转 如果是 return true 允许跳转
}
else if(localStorage.getItem('token')){ //除了登录之外的界面,看有没有token,如果有token 说明登录过了 可以进入
return true
}
else{
//return false //没有token 说明没有登录 所以不可以进入
return {path:'/login'} //没有token 说明没有登录 所以不可以进入 强制跳转到登录页面
}
vue2的防跳墙写法
w28///vue2的防跳墙写法
router.beforeEach((to, from, next) => {
console.log(to, from);
if (to.fullPath === "/login") {
//如果to.fullPath 等于 /login 进入登录页面
next();79312+
} else if (localStorage.getItem("token")) {
//如果存在了token 那么就说明已经登录了 所以可以进入其他页面
next();
} else {
//如果没有token 那么就说明没有登录 所以不能进入其他页面 强制跳转到登录页面
// next({path:'/login'})
router.push("/login");
}
});
跳转后 afterEach
2、路由独享的守卫
3、组件内的守卫
vuex
全局的状态管理工具
安装 npm i vuex -S
在main.js写
import {createStore} from 'vuex' //从vuex引入createStore的方法
const store=createStore({ //创建一个仓库
state(){ //存储的所以组件都可以取到的变量 取仓库值的方法 this.$store.state.变量
return{
isLogin:false //在仓库里存入isLogin 的值
}
},
mutations:{ //存放修改的state 的同步方法
changeLoginState(state,payload){ //在组件里调用 this.$store.commit("changeLoginState",true)
state.isLogin=payload
}
}
})
app.use(store) //使用仓库
Login.vue
this.$store.commit("changeLoginState",true)
App.vue
<div v-show="$store.state.isLogin && !$route.meta.flag">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
</div>
vuex状态的持久化
安装 npm i -S vuex-persistedstate
引入持久化仓库 import persistedstate from 'vuex-persistedstate'
使用持久化仓库 plugins:[persistedstate()]
import persistedstate from 'vuex-persistedstate' //引入持久化插件
import {createStore} from 'vuex' //从vuex引入createStore的方法
const store=createStore({ //创建一个仓库
state(){ //存储的所以组件都可以取到的变量 取值的方法 this.$store.state.变量
return{
isLogin:false //在仓库里存入isLogin 的值
}
},
mutations:{ //存放修改的state 的同步方法
changeLoginState(state,payload){ //在组件里调用 this.$store.commit("changeLoginState",true)
state.isLogin=payload
}
},
plugins:[persistedstate()] //使用持久化插件
})
app.use(store) //使用仓库
生成vuex的代码
首先使用
vue -V 检测版本号
npm i @vue/cli -g (一个电脑只安装一次,如果安装过了就不用安装了 用vue -V检测是否有版本号 有就直接输入指令 vue add vuex 就会自动生产一个store, 没有就使用该语句安装在输入指令 vue add vuex 就会自动生产一个store )
自动安装仓库
vue add vuex
没有登录时,可以跳转到其他页面,和不可以跳转某一个页面,下面是不可以跳转到about页面
{
path: "/about",
name: "about",
meta:{
needLogin:true
},
component: () => import("../views/AboutView.vue"),
},
router.beforeEach((to, from) => {
if (!to.meta.needLogin) {
return true;
} else if (localStorage.getItem("token")) {
return true;
} else {
return { path: "/login" };
}
});
vue3第十天
导航守卫的分类:
1、全局的导航守卫:
跳转前 beforeEach-------监控每次路由跳转之前执行的回调
跳转后 afterEach-------监控每次路由跳转之后执行的回调
每页标题栏显示对应的文字的方法一:
在App.vue里面写
<h2>{{ title }}</h2>
<script>
export default{
data(){
return{
title:''
}
},
created(){
this.$router.afterEach((to,from)=>{
if(to.fullPath==='/'){
// document.title='首页'
this.title='首页'
}else if(to.fullPath==='/about'){
this.title='关于'
}else{
this.title='其他'
}
})
}
}
</script>
每页标题栏显示对应的文字的方法二:
在APP.vue里面拿到标题
<h2>{{ $route.meta.title }}</h2>
在router--index.js对应的路由写上meta 并且写上对应的文字
meta:{
title:'首页'
}
2、路由独享的守卫
在对应路由位置写:----拦截特定的路由跳转
beforeEnter(to,from,next){
if(prompt('请输入密码')==='haiou'){
next()
}
},
3、组件内的守卫
beforeRouteEnter
beforeRouteLeave
beforeRouteUpdate
用于模拟后端数据库:
安装 npm i json-server -g (全局安装 每一个电脑只能安装一次)
检测是否安装了上面的那个 json-server --version
然后再 src下面建立一个文件夹mock 再mock文件夹下面建立一个文件data.json 再里面写数据
{
"list": [
{ "id": 1, "name": "海鸥", "flag": "true" },
{ "id": 2, "name": "哈哈哈", "flag": "false" },
{ "id": 3, "name": "嘻嘻嘻", "flag": "true" }
]
}
在集成终端运行 json-server data.json 之后出来地址 http://localhost:3000/list
然后再仓库的actions 发送请求
actions: {
getList(){
axios.get('http://localhost:3000/list').then((res)=>{
console.log(res)
})
}
},
之后再做点击按钮获取后端数据
<button @click="$store.dispatch('getList')">获取列表</button>
仓库分模块
没分模块之前:
import { createStore } from "vuex";
import axios from "axios";
export default createStore({
state: { //存放数据
n: 23323,
list: [],
},
getters: { //存放计算属性
selecteCount(state) {
return state.list.filter((item) => item.flag).length;
},
},
mutations: { //存放的同步方法 commit
changeN(state, payload) {
state.n += payload;
},
selectAllNone(state, payload) {
//全选全不选
state.list.forEach((item) => (item.flag = payload));
},
changeList(state, payload) {
//修改列表的数据
state.list = payload;
},
},
actions: { //存放异步的方法 dispatch
getList(context, payload) {
//context 上下文对象---(commit方法可以调用mutations里面的方法) payload 参数
axios.get("http://localhost:3000/list").then((res) => {
console.log(res);
context.commit("changeList", res.data); //异步成功以后,要调用mutations里的同步方法修改数据
});
},
addNum(context, payload) {
setTimeout(() => {
context.commit("changeN", payload);
}, 2000);
},
},
modules: {},
});
分模块之后:
分仓库后 分完模块后需要在Test.vue文件中的代码对应的位置前添加模块名
在仓库store中建立文件夹counter/index.js
export default {
namespaced: true,
state: {
//存放数据
n: 23323,
},
mutations: {
//存放的同步方法 commit
changeN(state, payload) {
state.n += payload;
},
},
actions: {
addNum(context, payload) {
setTimeout(() => {
context.commit("changeN", payload);
}, 2000);
},
},
};
在仓库store中建立文件夹list/index.js
import axios from "axios";
export default{
namespaced: true, 写这个的时候 需要在test.vue文件中对应代码对应位置添加模块名/ 例如: /list
state: {
//存放数据
list: [],
},
getters: {
//存放计算属性
selecteCount(state) {
return state.list.filter((item) => item.flag).length;
},
},
mutations: {
selectAllNone(state, payload) {
//全选全不选
state.list.forEach((item) => (item.flag = payload));
},
changeList(state, payload) {
//修改列表的数据
state.list = payload;
},
},
actions: {
//存放异步的方法 dispatch
getList(context, payload) {
//context 上下文对象---(commit方法可以调用mutations里面的方法) payload 参数
axios.get("http://localhost:3000/list").then((res) => {
console.log(res);
context.commit("changeList", res.data); //异步成功以后,要调用mutations里的同步方法修改数据
});
},
},
}
总仓库:
import { createStore } from "vuex";
import counter from "./counter";
import list from "./list";
export default createStore({
modules: {
counter,list
},
});
views/Test.vue
<template>
<div>
<!-- {{ $store.state.n }} -->
{{ n }}
<button @click="inc">+</button>
<button @click="add">+10</button>
<input type="checkbox" v-model="select" />
<ul>
<li v-for="item in list" :key="item.id">
<input type="checkbox" v-model="item.flag" />
{{ item.name }}
</li>
</ul>
{{ count }}
<button @click="$store.dispatch('list/getList')">获取列表</button>
</div>
</template>
<script>
//引入lodash
// import _ from "lodash";
export default {
// created(){
// this.fun=_.debounce(()=>{
// this.$store.commit('changeN',100)
// },1000)
// },
computed: {
n() {
return this.$store.state.counter.n; //在对应的
},
list() {
return this.$store.state.list.list; //
},
count() {
return this.$store.getters['list/selecteCount'];
},
select: {
set(v) {
this.$store.commit("list/selectAllNone", v);
},
get() {
return this.$store.state.list.list.every((item) => item.flag);
},
},
},
methods: {
inc(){
this.$store.commit('counter/changeN',10)
},
//节流的写法:
// inc: _.debounce(function () {
// this.$store.commit("changeN", 10);
// }, 1000),
add() {
this.$store.dispatch("addNum", 10);
},
},
};
</script>
<style lang="scss" scoped></style>
老师的笔记
路由的导航守卫
全局守卫
beforeEach
router.beforeEach((to,from)=>{ //to path fullPath matched meta params query.....
console.log(to.matched) //to.matched 当前匹配路径的路由对象(数组)
return true;
})
afterEach
~~~
this.$router.afterEach((to,from)=>{ if(to.fullPath==="/"){ this.title="首页" } else if(to.fullPath==='/about'){ this.title="关于" } else{ this.title="默认" } }) ~~~
路由独享的守卫
beforeEnter(to,from,next){
if(prompt("输入密码")==="admin"){
next();
}
},
组件内的守卫
beforeRouteEnter
~~~
beforeRouteEnter(to,from,next){ //监听组件里路由刚刚进入时触发 执行事件比 beforeCreate 早
console.log(to.params.n)
next((vm)=>{
console.log(vm.color)
});
},
beforeCreate(){
console.log("beforeCreate")
},
~~~
beforeRouteLeave
beforeRouteLeave(to,from,next){
if(this.flag){
next();
}
else{
alert("不能退出,请先交卷")
}
},
~~~
beforeRouteUpdate
// beforeRouteUpdate(to,from){
// console.log(to.params.n)
// },
vuex
state 存放数据的对象
mutations 存放修改状态的同步方法
getteres 仓库里的计算属性
getters: {
selectedCount(state){ //选中的商品的数量
return state.list.filter(item=>item.flag).length
}
},
//获取仓库里面的数据
this.$store.getters.selectedCount // ------------this.$store.getters. 变量
actions
actions: { //存放异步的方法 dispatch
getList(){
axios.get("http://localhost:3000/list").then((res)=>{
console.log(res)
})
}
},
this.$store.dispatch('getList')
mock数据
npm i json-server -g
json-server -v
json-server xxx.json //运行对应的json文件
在src下面建立一个文件夹mock,在在mock文件夹中建立文件data.json 数据格式如下:
使用 json-server data.json 在data.json的集成中端运行该文件 //运行对应的json文件
{
"list":[
{
"id":1,
"name":"aaa",
"flag":true
},
{
"id":2,
"name":"bbb",
"flag":false
},
{
"id":3,
"name":"ccc",
"flag":false
}
]
}
action和节流(防抖和节流)
vuex的单向数据流
分模块
==========store/counter/index.js
export default {
namespaced:true //开启命名空间
state: { //存放数据
n:666,
},
mutations: { //存放的同步的方法 commit
changeN(state,payload){
state.n+=payload
}
},
actions: { //存放异步的方法 dispatch
asyncAdd(context,payload){
setTimeout(()=>{
context.commit("changeN",payload)
},2000)
}
}
}
=========store/list/index.js
import axios from 'axios'
export default {
namespaced:true //开启命名空间
state: { //存放数据
list:[
]
},
getters: { //存放计算属性
selectedCount(state){ //选中的商品的数量
return state.list.filter(item=>item.flag).length
}
},
mutations: { //存放的同步的方法 commit
selectAllNone(state,payload){ //全选全不选
state.list.forEach(item=>item.flag=payload)
},
changeList(state,payload){ //修改列表数据
state.list=payload
}
},
actions: { //存放异步的方法 dispatch
getList(context,payload){ //context 上下文对象 (commit方法可以调用mutations里的方法) payload 参数
axios.get("http://localhost:3000/list").then((res)=>{
context.commit("changeList",res.data) //异步成功以后,要调用mutations里的同步方法修改数据
})
}
}
}
==============store/index.js
import { createStore } from 'vuex'
import list from './list'
import counter from './counter'
export default createStore({
modules: {
list,counter
}
})
this.$store.state.模块名.变量
分完模块后 在组件里面 对应的数据之前要添加对应的文件名 例如:...list.list...
分完模块后。getters重名会报错 mutations actions重名多个模块都会调用
为了区分分模块后的getters ,mutations,actions要启用命名空间
namespaced:true
四个辅助函数
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex';
computed:{
...mapState({
n:(state)=>state.counter.n,
list:(state)=>state.list.list
}),
...mapGetters({
count:'list/selectedCount'
}),
select:{
set(v){
this.selectAllNone(v)
},
get(){
return this.list.every((item)=>item.flag)
}
}
},
methods:{
...mapMutations({
selectAllNone:"list/selectAllNone",
changeN:"counter/changeN"
}),
inc(){
this.changeN(10)
// ( _.debounce(()=>{
// this.$store.commit("counter/changeN",10)
// },2000))()
},
...mapActions({
asyncAdd:"counter/asyncAdd",
getList:"list/getList"
})
}
vue3第十一天
watch和computed的区别
watch:当 watch依赖多个值的时候,每个值都要分别监控;能监听异步;没有缓存
computed:当 computed依赖多个值的时候,如果有一个值变化了,计算属性就会变化;不能监听异步;有缓存
选项式语法
setup ----组合式语法(组合式api)
最早执行的钩子函数 setup setup里面不能写钩子函数beforeCreate和created 使用其他钩子函数时需要在钩子函数前面写on 然后引入
import { ref,reactive,onMounted} from "vue";
onMounted(()=>{
console.log('onMounted')
})
//需要引入ref
import { ref,reactive } from "vue";
//然后再setup里面写数据、方法等等 当值改变的时候用ref 当引用类型的数据的属性改变的时候用reactive
setup() { //组件式api
console.log("setup", this);
let k = ref(0); //ref 声明的变量相当于data里的响应式数据不调用ref没有响应式
let money = ref(100);
let obj = reactive({ //最重要的函数 让对象产生响应式 使对象的属性具有响应性
a:1
})
const inc = () => { //这个函数相当于methods里的方法
k.value++; //ref 声明的变量,使用时加上 .value
obj.a++
};
return { //无论是数据还是方法都必须return 才能在模板里面使用
k,
money,
inc,
obj
};
},
setup的语法糖:
直接在script里面写setup 之后直接不要写export default 也不需要写return 就可以直接使用了
复合api子接收父传过来的值使用 defineProps
defineProps({
msg: {
type: String,
required: true
}
})
UI库(vant)
安装 npm i vant -S
使用
在main.js中引入 Button示例
import { createApp } from 'vue';
import { Button } from 'vant';
const app = createApp();
app.use(Button);
1.复习选项式的语法
2.setup使用
import { ref } from 'vue';
setup(){ //组件式api
console.log("setup",this);
let k = ref(0); //ref 声明的变量相当于data里的响应式数据不调用ref没有响应性
let money=ref(100)
const inc=()=>{ //这个函数相当于methods里的方法
k.value++; //ref 声明的变量,使用时加上 .value
}
return {
k,money,inc //无论是数据还是方法必须return 才能在模板里使用
}
}
3.ref和reactive
setup(){ //组件式api
console.log("setup",this);
let k = ref(0); //ref 声明的变量相当于data里的响应式数据不调用ref没有响应性
//当你改变值得时候要用ref
//当应用类型的数据属性变化的时候要用 reactive
let money=ref(100) // 本质 相当于 reactive({value:xxxx})
let obj =ref({ //最重要的函数 是对象的属性具有响应性
a:1
})
const change=()=>{
obj.value={
b:5
}
}
const inc=()=>{ //这个函数相当于methods里的方法
// k.value++; //ref 声明的变量,使用时加上 .value
obj.a++
}
return {
k,money,inc,obj,change //无论是数据还是方法必须return 才能在模板里使用
}
}
### 8.列表组件
# vue3第十三天
### 打包
首先需要把路由设置为hash模式,base要设置为 ./
## axios的二次封装(为了每次发请求的时候重复的代码只写一次)
环境几种
import.meta.env.MODE(vite官网)应用运行的模式
开发环境 development
测试环境
生产环境 production
路由要设置为hash模式 base:"./"
service/index.js
import axios from 'axios' if(import.meta.env.MODE==="development"){ url="/api" //开发环境 } else if(import.meta.env.MODE==="production"){ url="http://47.94.148.165:3001/api" //开发环境 } let instance = axios.create({ baseURL:url }) export default instance
方法二:
.env.production VITE_APP_BASE_URL="http://47.94.148.165:3001"
.env.development VITE_APP_BASE_URL=""
import axios from 'axios' let instance = axios.create({ baseURL:import.meta.env.VITE_APP_BASE_URL+"/api" //开发环境 /api 生成环境 http://47.94.148.165:3001 }) export default instance
import axios from '../server/index.js'
## 打包遇到的问题
1.路由要变为hash模式
import { createRouter, createWebHashHistory } from 'vue-router' const router = createRouter({ history: createWebHashHistory(import.meta.env.BASE_URL) })
2.打包后请求数据请求不到
设置不同模式下不同的url,把baseURL改为获取数据的服务器ip地址
## 拦截器(interceptors)
请求拦截器
instance.interceptors.request.use((config)=>{ // 请求拦截器 if(localStorage.getItem("token")){ config.headers["token"]=localStorage.getItem("token"); } return config; })
响应拦截器
instance.interceptors.response.use((res)=>{ //响应拦截器 if(res.data && res?.data?.data.token){ //如果有token ,token就存储到本地存储里 localStorage.setItem("token",res.data?.data?.token) } showNotify({ //显示服务器端发回的消息 type:res.data.code=="200"?'success':'danger', message:res.data.message }) return res.data; //所有请求的返回值都少写一个data })
## vue管理系统项目
0. npm i axios -S
0. vue add vuex
0. npm i element-plus -S
0. npm i normalize.css
0. git init
0. git add .
0. git commit -m "xxxx"
0. git remote add origin 仓库地址
0. git push origin master
0. git clone 仓库地址(第一次)
0. cd 项目目录 git pull
### 防跳
//用导航守卫进行登录的防跳 router.beforeEach((to, from) => { if (to.fullPath == "/login") { if(localStorage.getItem('token')){ return {path:'/home'} }else{ return true } } else if (localStorage.getItem("token")) { return true; } else { return { path: "/login" }; } }); export default router;
# vue3第十八天
### echarts的使用 npm i echarts -S
## git ssh方式上传
ssh-keygen -t rsa -C xxxx@yy.com
找到.ssh目录
.pub文件的内容复制 贴到 gitee账号 /设置/ssh公钥
git remote -v 查看所有链接
git remote add origin1 ssh地址 添加一个远程链接
git add . 提交文件从工作区到暂存区
git commit -m "xxx" 把文件从暂存区提交到本地仓库
git push orgigin1 master 推送本地仓库文件到远程仓库
## tinymce
## echarts使用
# vue3第十九天
### ref和reactive的区别
ref是简单数据类型 监控简单值得变化 适合使用在值的变化上面
reactive是引用数据类型 监控数据对象得属性得变化 适合使用在对象值得变化
### setup的用法
Home About //使用button代替RouterLink hong about ```pinia
安装tsc npm i typescript -g
检测tsc tsc --version
将tsc转换成js tsc -w 在集成终端输入
组合式api
1.ref和reactive
<template>
<div>
<input v-model="str" @keyup.enter="add" />{{ str }}
<ul>
<li v-for="(item,index) in todos" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { ref,reactive } from 'vue';
let str=ref("");
let todos=reactive([]);
const add=()=>{
todos.push(str.value);
str.value=""
}
</script>
2.toRefs
是对象的属性结构后具有响应性
3.计算属性
<template>
<div class="about">
<h1>This is an about page ! price: {{price }}count:{{ count }}</h1>
<button @click="addA">+</button> {{ money }}
</div>
</template>
<script setup>
import { reactive,toRefs,computed } from 'vue';
let obj=reactive({
price:2,
count:3
})
let {price,count}=toRefs(obj);
let money=computed(()=>obj.price*obj.count) //计算属性
const addA=()=>{
obj.count++;
}
</script>
4.useRouter和useRoute
let router=useRouter();
const go=(path)=>{
router.push(path)
}
let route = useRoute();
5.watch
watch(route, (n) => { //动态的面包屑导航
routes.value = n.matched;
})
6.组合式api的自定义函数
import { ref, reactive } from 'vue';
export let str = ref("");
export const useTodos = (str) => { //str 就是文本框的内容
let todos = reactive([]); //代办事项列表
const add = () => {
todos.push(str.value);
str.value = ""
}
return {
todos,
add
}
}
Pinia
export const useCounterStore=defineStore('counter',{
state(){
return {
n:666 //状态
}
},
getters:{
doubleN(state){
return state.n*2; //计算属性
}
},
actions:{ //同步的方法异步的方法都放到这里
inc(){
this.n++
}
}
})
组件
<template>
<div>
test {{ n }} <button @click="couterStore.inc">+</button>
{{ couterStore.doubleN }}
</div>
</template>
<script setup>
import {storeToRefs} from 'pinia'
import {useCounterStore} from '@/stores/counter'
let couterStore=useCounterStore();
console.log(couterStore)
let {n}=storeToRefs(couterStore) //storeToRefs 使解构出来的数据具有响应性
console.log(n.value)
</script>
pinia持久化
npm i pinia-plugin-persistedstate -S
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
let piniains = createPinia()
piniains.use(piniaPluginPersistedstate)
app.use(piniains)
store/counter.js
....
persist: {
enabled: true // true 表示开启持久化保存
}
....
父子组件传值
<TestSon :n="n" @msg="receive"></TestSon> // 父组件
<template>
<div>
testSon {{ n }}
<button @click="send">send</button>
</div>
</template>
<script setup>
const receive=(newV)=>{
receiveN.value=newV
}
</script>
-----------------------------------------------------------------------------------
子组件
<script setup>
let props= defineProps({ //接收父组件传过来的值
n:Number
})
let emit =defineEmits(['msg']) //定义发送对象 ,参数是数组,里面是要发送的事件
const send=()=>{
emit("msg",props.n+1) //发送新值给父组件
}
</script>
父组件调用子组件里的方法
子组件里暴露方法
const fun=()=>{
console.log("Fun")
}
defineExpose({ //暴露fun方法给父组件使用
fun
})
父组件用ref标识组件
<TestSon :n="n" @msg="receive" ref="testson"></TestSon>
const testson=ref() //找到了子组件
testson.value.fun() //父组件调用子组件里的方法
typescript
npm i typescript -g
tsc --version
tsc --init 会产生一个tsconfig.json
tsc 第二十天
配置路径 在tsconfig.json中
29行 "rootDir": "./src",
58行 "outDir": "./dist",
提交tsc到git
git init
git add .
git commit -m "tsc第一次提交"
git log
git log --pretty=oneline
interface和type的区别
type不能重复定义 interface可以
type 除了对象外也可以约束基本数据类型 interface约束的是对象
type 不可以继承 interface 可以用extends继承
git版本管理
git log 提交的日志记录
git reflog 查看所有的日志记录
git reset --hard xxxxxxxxx
ts中的class
class Person {
// static xq:string="earth"; //静态属性 用类型可以直接调用的属性
protected name:string; //public 任何地方都可以访问 private 只有类的内部可以访问
//protected 只有自己和派生类可以访问
age:number;
constructor(name:string,age:number){
this.name=name;
this.age=age;
}
say():string{
return this.name+","+this.age;
}
}
//console.log(Person.xq)
// let p = new Person("zs",10);
// console.log(p.say())
class Student extends Person{
school:string;
constructor(name:string,age:number,school:string){
super(name,age);
this.school=school
}
say(): string {
return super.say()+','+this.school;
}
}
let s = new Student("ls",18,'qf');
console.log(s.say())
ts中的接口(interface)
1.属性接口
//对对象进行约束
interface iPerson{ //接口定义的是规范
name:string,
age:number,
//school?:string //可选属性
// [key:string]:any
}
// interface iPerson{ //可以重名 ,重名的属性合并
// height:number,
// city:string
// }
interface iStudent extends iPerson{ //可以继承
school:string
}
let p:iStudent={
name:'zs',
age:15,
school:"www"
}
2.函数类接口
interface ISum{ //这个接口是约束函数的
(a:number,b:number):number
}
let sum:ISum=(a:number,b:number)=>a+b
3.类类型接口
interface iAnimal{
eat:()=>string
}
class Dog implements iAnimal{ //类实现接口,一定要实现(implements)接口里的方法
eat(){
return "dog eat sth"
}
}
泛型
interface IArr<T>{ //泛型
[index:number]:T
}
let arr:IArr<number>=[1,2]
泛型函数
function sum2<P>(x:P):P{
return (x as any)+(x as any)
}
console.log(sum2<string>("hello"))
泛型类
class IWorker<T> {
no:T;
name:string;
constructor(no:T,name:string){
this.name=name;
this.no=no;
}
say():string{
return this.no+','+this.name
}
}
let w=new IWorker<number>(1,'zs');
console.log(w.say())
泛型接口
interface iDou<T>{
(x:T):T
}
function funDouble<T>(x:T):number{
return (x as number)*2
}
console.log(funDouble<number>(666))
vue3+ts
基本约束
import {reactive, ref} from 'vue'
let n= ref(666)
const add=(p:number)=>{
n.value+=p
}
let str=ref("");
let list:Array<string>=reactive([]);
const addItem=(e:KeyboardEvent)=>{
// list.push(str.value);
// str.value=""
list.push((e.target as HTMLInputElement).value);
(e.target as HTMLInputElement).value=""
}
<template>
<div>hello {{ n }} <button @click="add(10)">add</button>
<br />
<input type="text" @keyup.enter="addItem($event)" />
<ul>
<li v-for="(item,index) in list" :key="index">
{{ item }}
</li>
</ul>
</div>
</template>
type 自定义类型
//interface的区别
//不能重复定义,interface可以
//interface约束的是对象 type储里对象外也可以约束基本数据类型
//interface 可以用extends 继承 type不可以
]