这份笔记主要记录的是vue2和补充vue3的语法
看了一下这个vue2全家桶列举挺完整全面的
https://blog.csdn.net/qq_44663761/article/details/122328822?spm=1001.2101.3001.6650.16&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7ERate-16-122328822-blog-135767247.235%5Ev43%5Econtrol&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7ERate-16-122328822-blog-135767247.235%5Ev43%5Econtrol&utm_relevant_index=17
vue3的补充更新语法
https://24kcs.github.io/vue3_study/chapter4/01_Composition%20API_%E5%B8%B8%E7%94%A8%E9%83%A8%E5%88%86.html
vue3的关键在于理解Composition (组合) API和setup的使用
vue基本语法
v-for绑定迭代
v-on绑定事件,简写形式”@”
v-model绑定数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <div id="app"> <input type="text" v-model="inputValue"> <button v-on:click="handleBtnClick">提交</button> <ul> <li v-for="item in list">{{item}}</li> </ul> </div> var app=new Vue({ el:'#app', data:{ list:['first','second'], inputValue:'' }, methods:{ handleBtnClick:function(){ this.list.push(this.inputValue) this.inputValue='' } } })
|
v-bind绑定对象,简写形式”:”
component全局组件
1 2 3 4 5
| <todo-item v-bind:content="item" v-for="item in list"></todo-item> Vue.component("TodoItem", { props: ["content"], template: "<li>{{content}}</li>", });
|
局部组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var TodoItem = { props: ["content"], template: "<li>{{content}}</li>", }; var app = new Vue({ el: "#app", components: { TodoItem: TodoItem, }, data: { list: ["first", "second"], inputValue: "", }, methods: { handleBtnClick: function () { this.list.push(this.inputValue); this.inputValue = ""; }, }, });
|
父子组件传值
父组件v-bind的格式向子组件传递,然后props里接收
子组件$emit向上一层触发事件,父组件监听获取
props传递的数据是只读的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <todo-item v-bind:content="item" v-bind:index="index" v-for="(item, index) in list" @delete="handleItemDelete" > </todo-item> var TodoItem = { props: ['content', 'index'], template: "<li @click='handleItemClick'>{{content}}</li>", methods: { handleItemClick: function () { this.$emit("delete", this.index); }, }, }; var app = new Vue({ el: "#app", components: { TodoItem: TodoItem, }, data: { list: [], inputValue: "", }, methods: { handleBtnClick: function () { this.list.push(this.inputValue); this.inputValue = ""; }, handleItemDelete: function (index) { this.list.splice(index, 1); }, }, });
|
生命周期函数是指vue实例在某一个时间点会自动执行的函数
图里是vue2的,vue3在命名上多了on和setup
2.x 版本生命周期相对应的组合式 API
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
template
插值表达式用两个大括号
1 2 3 4 5 6 7 8 9 10
| <div v-text="name+' abc'"></div> 输出字符串 <div v-html="name+' abc'"></div> 输出解析成html格式的字符串 <div>{{name+' abc'}}</div> 输出字符串 var app = new Vue({ el: "#app", data: { name: "<h1>ella</h1>", }, });
|
computed属性会有缓存,methods没有缓存性能较差
watch监听变化也有缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <div> {{fullName}} {{age}} </div> var app = new Vue({ el: "#app", data: { firstName:"Ella", lastName:"L", age:20 }, computed:{ fullName:function(){ return this.firstName+" "+this.lastName } }, watch:{ firstName:function(){ this.fullName=this.firstName+" "+this.lastName }, lastName:function(){ this.fullName=this.firstName+" "+this.lastName }, }, });
|
set和get设置方法
1 2 3 4 5 6 7 8 9 10 11 12
| computed: { fullName: { get: function () { return this.firstName + " " + this.lastName; }, set:function(value){ var arr=value.split(" "); this.firstName=arr[0]; this.lastName=arr[1]; } }, },
|
样式绑定.通过v-band绑定class或者style来实现
可以绑定对象也可以是数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <style> .activated{ color: aquamarine; } </style> <div @click="handleDivClick" :class="{activated:isActivated}" 或者"[activated]" >Ella </div> methods: { handleDivClick:function(){ this.isActivated=!this.isActivated; 或者this.activated=this.activated==="activated"?"":"activated"; } }, 再或者<div :style="styleObj">hello world</div> 绑定style直接设置
|
条件渲染
v-if 对应的值为false直接在dom上不存在
v-show 对应的值为false,dom上存在,不显示
v-if可以写循环,必须连在一起
1 2 3
| <div v-if="show==='a'">A</div> <div v-else-if="show ==='b'">B</div> <div v-else>C</div>
|
key值唯一可以保证不被复用
在控制台直接用下标[]修改数组内容是不会改变页面显示
要使用相应的修改函数才可以
push pop shift unshift splice sort reverse
或者直接改变数组引用对象也可以
对象也可以循环,用set方法修改内容
Vue.set(app.list,”number”,18)
app.$set(app.list,”number”,18)
Vue.set(target, key, val)
1 2 3 4 5 6 7
| <div v-for="(item, index) of list" :key="item.id"> {{item.text}}---{{index}} </div> 数组 <div v-for="(item, key) of list"> {{item}}--{{key}} </div> 对象
|
\
里面有\里面有\里面有\ \ |
里面不能直接用template,但是可以用is语句来指向template子组件
ul,ol,select标签同理
子组件里data必须是函数,不能是父组件中的对象,因为子组件都拥有独立数据不共享
用ref来操作dom
1 2 3 4 5 6 7 8
| <div ref="hello" @click="handleClick" >hello world</div> methods: { handleClick: function(){ console.log(this.$refs.hello); console.log(this.$refs.hello.innerHTML) } },
|
父子组件传递
父组件向子组件传数据,要在父组件components里先注册,然后标签里传参,子组件用props接收属性数据,且最好不要修改,克隆一个副本修改
子组件向父组件传值调用$emit(方法名),父组件在标签里用@监听方法然后触发事件
子组件的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <div id="app"> <counter :count="0" @change="handelIncrease"></counter>注意在这里定义的事件是和父组件绑定的,要子组件$emit <counter :count="1" @change="handelIncrease"></counter> <div>{{total}}</div> </div> var counter = { props: ["count"], data: function () { return { number: this.count, }; }, template: "<div @click='handleClick'>{{number}}</div>",这里定义的事件才是子组件自定义事件,和子组件里面的method绑定 methods: { handleClick: function () { this.number++; this.$emit("change", 1); }, }, }; var vm = new Vue({ el: "#app", data: { total: 1, }, components: { counter: counter, }, methods: { handelIncrease: function (step) { this.total += step; }, }, });
|
传参校验
非props特性会显示在dom标签里,父组件传子组件不接收
绑定原生事件只需要在事件后加上.native
非父子组件传值bus 发布订阅模式 观察者模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <div id="app"> <child content="ella" ></child> <child content="syray"></child> </div> Vue.prototype.bus = new Vue(); Vue.component("child", { data:function(){ return{ selfContent:this.content } }, props: { content: String, }, template: "<div @click='handleClick'>{{selfContent}}</div>", methods:{ handleClick:function(){ this.bus.$emit('change',this.selfContent) } }, mounted:function(){ var this_=this; this.bus.$on('change',function(msg){ this_.selfContent=msg; }) } }); var vm = new Vue({ el: "#app", });
|
插槽
1 2 3 4 5 6 7 8 9 10 11
| <div id="app"> <child> <p slot="head">ella1</p> <p slot="foot">ella2</p> </child> </div> template: `<div> <p>hello</p> <slot name='head'>default</slot> <slot name='foot'>default</slot> </div>`,
|
动态组件
v-once是创建一次就放入内存中,提高性能,不会反复创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div id="app"> <component :is="type"></component> <button @click="handleBtnClick">change</button> </div> Vue.component('child-one',{ template:'<div v-once>one</div>' }) Vue.component('child-two',{ template:'<div v-once>two</div>' }) var vm = new Vue({ el: "#app", data:{ type:'child-one' }, methods:{ handleBtnClick:function(){ this.type=this.type==='child-one'?'child-two':'child-one'; } } });
|
CSS动画效果是通过在某一时间上自动往标签上增加一些样式实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| <style> .v-enter, .v-leave-to{ opacity: 0; } .v-enter-active, .v-leave-active{ transition:opacity 3s; } </style>
<style> @keyframes bounce-in { 0%{ transform:scale(0); } 50%{ transform:scale(1.5); } 100%{ transform:scale(1); } } .v-enter-active{ transform-origin: left center; animation: bounce-in 1s; } .v-leave-active{ transform-origin: left center; animation: bounce-in 1s reverse; } </style> <transition> <h1 v-if="show">ella</h1> </transition> <button @click="handleBtnClick">change</button>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> <style> .v-enter, .v-leave-to{ opacity: 0; } .v-enter-active, .v-leave-active{ transition:opacity 3s; } </style>
<div id="app"> <transition type="transition" //这里可以自定义入场和出场动画时长 :duration="{enter:5000,leave:10000}" appear enter-active-class="animate__animated animate__swing v-enter-active" leave-active-class="animate__animated animate__flip v-leave-active" appear-active-class="animate__animated animate__flash"> <h1 v-if="show">ella</h1> </transition> <button @click="handleBtnClick">change</button> </div>
<div id="app"> <transition @before-enter="handleBeforeEnter" @enter="handleEnter" @after-enter="handleAfterEnter"> <h1 v-if="show">ella</h1> </transition> <button @click="handleBtnClick">change</button> </div> var vm = new Vue({ el: "#app", data:{ show:true }, methods:{ handleBtnClick:function(){ this.show=!this.show; }, handleBeforeEnter:function(el){ el.style.color='red' }, handleEnter:function(el,done){ setTimeout(()=>{ el.style.color='green' },2000) setTimeout(()=>{ done() },4000) }, handleAfterEnter:function(el){ el.style.color='blue'; } } });
handleEnter:function(el,done){ Velocity(el,{opacity:1},{duration:1000,complete:done}) },
|
动画封装
开发注意小知识点
- \传参是v-bind:to
:to=”{path:’/root’,query:{id:value}}”
- v-bind:class
以键值对的形式绑定操作
- import
https://blog.csdn.net/qq_40323256/article/details/101867706
注意export导出的引入要{},export default不需要
前后端分离问题汇总
完整逻辑
https://blog.csdn.net/qq_42345108/article/details/108249173
- element UI表单 验证
https://blog.csdn.net/qq_41008567/article/details/115939951
- axios
https://www.axios-http.cn/docs/intro
请求/响应前进行拦截
拦截器
逻辑顺序
- 在vue.config.js里定义devServer的代理服务器和api前缀指向
- 然后定义utils文件夹里的request.js,在该文件里引入axios,创建axios对象和要访问的URL即前面定义的API前缀,然后定义请求和响应拦截器,最后export定义的request对象
- 在apis文件夹里引入定义好的request对象,开始编写具体的request函数,包括后台接口的url,http协议方法,传递的参数
- 在vue视图里引入apis里的request函数并调用,注意调用后的返回值
这里同样可以直接在vue组件里面使用类似下方结构来调用
1 2 3 4 5 6 7 8 9 10 11
| axios.post("/users/payMent",{ addressId:addressId, orderTotal:this.orderTotal }).then((response)=>{ let res = response.data; if(res.status=="0"){ this.$router.push({ path:'/orderSuccess?orderId='+res.result.orderId }) } })
|
get请求参数不同
post
https://zhuanlan.zhihu.com/p/275695831
- vuex
状态管理模式
解构简写
1 2 3 4
| import { mapState } from 'vuex' computed:{ ...mapState(['nickName','cartCount']) },
|
- state
唯一数据源,单一状态树
computed里面定义
this.$store.state.
- getter
可以派生出一些新的状态
可以直接在state数据上包装而不用重新定义数据,类似数据库里视图的概念
this.$store.state.getters
- mutation
更改state的唯一方法是提交mutation
this.$store.commit()
- action
提交的是mutation,而不是直接变更状态
可以异步操作
调用this.$store.dispatch(“f”)
定义f(a){a.commit()}
- module
将多个store对象分割成模块
this
this对象问题
https://juejin.cn/post/7109889547537743886
this.$
https://segmentfault.com/a/1190000040894587
vue插件
extend和component类似
https://juejin.cn/post/7239715295484821565
webpack
https://cloud.tencent.com/developer/article/1797938
跨域问题
cors
浏览器保护协议:只允许网页请求同一域名下的服务(域名和端口都要保持一致)
配置后端:
浏览器根据Access-Control-Allow-Origin响应头决定
*,允许所有请求
域名,允许指定域名请求
nodejs express 是添加cors中间件
配置前端
找代理服务器发送请求
vite.config.js里面配置proxy把”/api”的内容转发到真实的后端服务路径
配置中转服务器
打包好的前端项目在dist目录下,index.html是入口文件
使用express或者nginx创建服务器发送前端页面并代理请求
Vue3
- 指令语法
- 数组
变更方法
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
非变更方法
filter(), concat(), slice()
reverse()和sort()调用之前创建一个原数组的副本
- 事件处理
内敛事件 方法事件
vue2中:这种写法自定义事件可以通过.native修饰符变成原生DOM事件
vue3为原生DOM事件
- 生命周期
不要用箭头函数,否则无法用this访问组件实例
- watch侦听器
- 路由
动态路由匹配 :传递
routes:{}里面的的路由地址可以有相对路径,可以嵌套子路径
\</router-link>里面必须是绝对路径
$router.push(“”)JS实现页面跳转 ?传递
使用命名路由跳转的时候注意要用v-bind:to=”{name:’’,params:{}}”
用useRoute()可以获取当前路由下的数据
路由缓存问题
路由只有参数变化的时候会组件实例复用,会导致数据无法更新
解决思路:让组件不复用强行销毁重建或者监听路由的变化
1)给router-view添加key
以当前路由的完整路径为key,但是有一些不用重新发
2)使用beforeRouteUpdate
可以获取最新的路由参数,性能有提升,精细化控制
组件
注意data是一个函数要返回return,组件之间不共享
-