只是学习过程中的一些小笔记,不喜勿喷
选项式/配置api
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.js" type="application/javascript"></script>
<script>
//配置api
const app = new Vue({
el:'#app'//挂载css元素或者节点,让vue在节点app工作
// el:document.getElementById('app')
data:{//data:状态,里面的数据都是响应式的,带有个体与set,同理react中的state
a:1,
b:2
},
})
</script>
data: 状态( vue对于这里声明的所有属性进行递归处理 给每个属性都加上getter和setter vue2: Object.defineProperty来处理 vue3: new Proxy 来处理 在setter当中可以劫持对于状态的修改的行为,从而主动的去进行视图的更新
vue2:
<script>
var data={a:1,b:2}
const $data={};
for(const key in data){
Object.defineProperty($data,key,{
get(){
return data[key]
},
set(val){
data[key]=val;
document.querySelector('.text').innerHTML=val
}
})
}
console.log($data);
$data.c=999;
console.log($data);
document.getElementById('btn1').onclick=(){
$data.c=$data.c+1
}
</script>
vue3:
<script>
var data={a:1,b:2,_c:3};
const $data =new Proxy(data,{
get(obj,key){
console.log(obj,key);
return obj[key]
},
set(obj,key,val){
}
})
$data.a
console.log($data.a);
</script>
模板指令
1.v-text 插入文本内容
<div v-text="c"></div>==<div>{{c}}</div>
2.v-html 插入html内容,使用v-html一定要确保插入内容的安全性,如果内容有xss攻击时,需要进行正则验证
3.v-if 为null,0,undefined,false时不渲染,且两个需要连用
<div v-if="b">v-if</div>
<div v-else>else</div>
4.v-show 通过布尔值来控制CSS中display进行显示
3,4区别:v-if一定会重排重绘,v-show不一定,所以在切换次数少的时候用v-if,反之v-show
5.v-bind 简写成‘:’ 绑定对象时会根据值来判断是否绑定
<div :class="'a b c'">class</div>a b c
<div :class="['a','b','c']">class</div>a b c
<div :class="['a','b','c'].join(' ')">class</div>a b c
<div :class="{1:33,2:0,3:11,4:22,f:''}">class</div>1 3 4
<div :style="{color:f,fontSize:''}">样式</div> f:'red'
6.v-for 需要绑定key唯一值,可以绑定数组,数字(循环个数),对象
<div v-for="item in g" :key="item.id">{{item.title}}</div>
<div v-for="(item,index) in g" >{{index}}-{{item.title}}</div>
<div v-for="item of 'abc'">{{item}}</div>循环字符串个数次
<div v-for="item in 11">{{item}}</div>
<div v-for="(val,key) in {a:1,c:2}">val:{{val}}</div>
g:[{id:1,title:'标题1'},{id:2,title:'标题2'}{id:3,title:'标题3'}]
7.v-on @
@click="函数,对象(通过对象绑定多个事件),函数(传参,如果需要获得合成事件对象需要输入$event对象),简单语句(@click='a=a+2')"
<button @click="fn1">按钮1</button>
<button @click="fn1(1)">按钮2</button>
<button @click="fn2(3,$event)">按钮3</button>
<button @click.once="fn1">单次绑定</button>
<button @click.capture="fn1">按钮1</button>
.capture将事件传递由冒泡改变;.stop阻止冒泡;.once执行一次;.prevent阻止默认行为
<a href="http://baidu.com" @click.prevent="fn1">111</a>
<input type="text" placeholder="提示内容" @keyup.13="fn2" @keydown="fn1"/>
methods: {//配置的是一些自定义函数
fn1(arg1){//绑定的函数不加(),那么第一个参数就是一个合成事件对象
console.log(arg1);
},
fn2(arg1,arg2){
console.log(arg1);
console.log(arg2);
}
},
8.v-pre 阻止编译,即当成文本进行展示;v-once 编译一次 这两个都不需要表达式<button v-once>+</button>
9.v-model 表单元素或自定义组件的双向绑定,语法糖。实际上都是监听某个事件并绑定。与vue双向绑定不一样(Object.defineProperty、Proxy)
<input type="text" :value="val" @input="val=$event.target.value"/>==<input type="text" v-model="val">
text 和 textarea 元素使用 value property 和 input 事件;
checkbox 和 radio 使用 checked property 和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件。
修饰符:.lazy 取代input监听change事件;.number输入字符串转为有效的数字;.trim输入首尾空格过滤
watch 监听事件;监听的是内存地址的变化,如果监听对象仅仅只改变属性值是不会被监听到的,如果想监听属性的话可以进行深度监听或者直接监听该属性。
watch:{ // 侦听器
ct(nv,ov){ // 第一个参数 是变化之后的新的值 第二个参数是变化之前旧的值
console.log('ct is change',nv,ov)
},
// 默认情况下 watch监听的是 一个值的内存地址
// handsome(nv,ov){
// console.log('handsome is change',nv,ov)
// }
handsome:{
handler(nv,ov){//hander处理函数
console.log('handsome is change')
},
deep: true, // 深度监听 监听对象的所有层的属性的变化
immediate: true, // 默认值是false 是否立即执行一次
},
"handsome.money"(nv,ov){//"对象.属性"() 监听某个属性的变化
console.log('属性变化',nv);
}
},
computed 计算属性,他的值是有缓存的,仅仅在相关依赖发生变化时才会重新计算。所以在一些根据其他状态计算出结果的情况下更优选。如购物车总价等。
computed:{
count(){
console.log('1111');
return this.num*this.price
}
}
methods 自定义函数
全选案例
<div id="app">
<div><input type="checkbox" v-model="options[0]"/>选项1</div>
<div><input type="checkbox" v-model="options[1]"/>选项2</div>
<div><input type="checkbox" v-model="options[2]"/>选项3</div>
<div><input type="checkbox" v-model="qx"/>全选</div>
</div>
<script>
const app = new Vue({
el:'#app',
data:{
// num:1,price:10,ct:100,
options:[false,false,false]
},
methods: {},
computed:{
qx:{
get(){
const qxall = this.options.every(item=>item);
// const qxall = this.options.some(item=>!item);
if(qxall) return true
return false
},
set(val){
console.log('修改全选');
if(val){
this.options=[true,true,true]
}else{
this.options=[false,false,false]
}
}
}
}
})
console.log(app);
</script>
filters 过滤器,实际上就是一个普通的函数,只是改变了函数的调用方式,在vue3中已移除!
发货状态案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.js" type="application/javascript"></script>
</head>
<body>
<div id="app">
<div> 订单状态: {{odtxt(orderState) }} {{orderState | newodtxt }}
</div>
</div>
</div>
<script>
const app = new Vue({
el: '#app',
data:{
orderState: 5, // 1 已下单未支付 2支付 3发货 4签收 5退款
},
methods:{
odtxt(val){
if(val==1) return '已下单未支付'
if(val==2) return '支付'
if(val==3) return '发货'
if(val==4) return '签收'
if(val==5) return '退款'
}
},
filters:{//过滤器
newodtxt(val){
if(val==1) return '已下单未支付'
if(val==2) return '支付'
if(val==3) return '发货'
if(val==4) return '签收'
if(val==5) return '退款'
}
}
})
console.log(app)
</script>
</body>
</html>
过滤时间格式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/dayjs/1.10.8/dayjs.min.js" type="application/javascript"></script>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.js" type="application/javascript"></script>
</head>
<body>
<div id="app">
<div>
{{ Date.now() | format('MM:DD hh:mm') }}
</div>
</div>
<script>
const app = new Vue({
el: '#app',
data:{},
filters:{
format(time,str){
const tpl = str || 'YYYY-MM-DD'
return dayjs(time).format(tpl)
}
}
})
console.log(app)
</script>
</body>
</html>
全局组件component
vue2 Vue.com
<div id="app">
<hd></hd>
</div>
<script>
Vue.component('hd',{
template:`<div>我来组成头部</div>`
})
const app = new Vue({
el: '#app',
})
console.log(app)
</script>
props:组件通信,父传子
<div id="app">
<!-- a传递字符串,:a传递数字 -->
<hd a="123" :c="123" d="999" :g="2"></hd>
</div>
<script>
Vue.component('hd', {
template: `<div>我来组成头部
<div>{{a}},{{c}},{{d}},{{e}},{{f}},{{g}}</div>
</div>`,
// props:['a','b'],//组件通信,父传子
props: {//对象写法需要相对应数据类型
a: String,
c: Number,
d: { type: String, required: true },//type:类型,required:是否必填
e: { default: 123 },//默认值,普通数据类型
f: {
default() {
return [1, 2, 3]
}
},//数组与对象需要使用函数返回值来确认唯一,原理如a=[1,2],b=a时会浅拷贝地址
g: {//验证器 对自定义值自定义校验
validator(val) {
if (val > 5) return false
return true
}
}
}
})
const app = new Vue({
el: '#app',
})
console.log(app)
</script>
$attrs 接收使用未被props接收的属性,推荐尽量使用props进行传参
$el 组件挂载的最终元素节点
$data 指向组件声明的状态
生命周期
beforeCreate() 组件创建前 created() 组件实例创建完成,data完成初始化,可以获取数据和进行数据处理 beforeMount() 挂载完成前 mounted() 挂载完成后,完成了节点挂载可以操作节点,如地图创建、图标创建都是要节点操作,可以在这个时间去初始化 beforeUpdate() updated() beforeDestroy() /unmounted组件卸载前,进行销毁前的一些移除事件监听以及清除定时器的最好时机 destroyed()/unmounted
activated激活 被缓存的组件在created之后会触发一次,之后的每次切换回来都会触发一次激活,所以被缓存
deactivated 失活
局部注册组件components
Vue.component('hd', {
template: `<div>我来组成头部<cp2></cp2></div>`,
components:{//局部注册
cp2:{
template:`<div>我来组成身体</div>`
}
}
})
混入mixin 可以把公共配置混入到当前组件中使用
Vue.mixin({//全局混入 所有组件可以直接使用
data(){
return{
name:'版本'
}
}
})
const mix2 = {//局部混入
methods: {
fn1() {
console.log('fn1执行')
}
}
}
Vue.component('cp1', {
template: `<div> 我是cp1组件 {{ version}} <button @click="fn1">我是cp1的按钮</button> </div>`,
//mixins: [mix1, mix2] 局部混入,可以混入多个
extends:mix1 继承,只能继承一个
})
provide/inject 依赖注入
父组件提供 provide: {供应的数据 } provide(){return{供应的数据}}
子组件注入inject: ['c','数据'] inject:{别名1:'数据源1',别名2:{from:'数据源名称'}}
Vue.component('cp1', {
template: `<div>我是cp1组件
<div> inject c in cp1 is :{{ c }}</div>
</div>`,
inject: ['c']
})
Vue.component('cp2', {
template: `<div>我是cp2组件 <cp1></cp1></div>`,
})
Vue.component('cp3', {
template: `<div>我是cp3组件 <cp2></cp2> </div>`,
provide: {
a: 1,
b: 2,
c: 3
}
})
<div id="app">
<cp3></cp3>
<cp1></cp1>
</div>
</div>
<script>
Vue.component('cp1', {
template: `<div>
我是cp1组件
<div> inject c in cp1 is :{{ c }}</div>
<div> a in cp1 :{{ a }}</div>
<div> newa in cp1 :{{ newa }}</div>
<div> newb in cp1 :{{ newb }}</div>
<div> newd in cp1 :{{ newd }}</div>
</div>`,
data() {
return {
a: 888
}
},
// inject: [ 'c' ,'a']
inject: {
c: 'c',
newa: 'a',
newb: {
from: 'b'
},
newd: {
from: 'd',
default: 7777
},
}
})
Vue.component('cp2', {
template: `<div>我是cp2组件 <cp1></cp1></div>`,
})
Vue.component('cp3', {
template: `<div>我是cp3组件 <cp2></cp2> </div>`,
provide: {
a: 1,
b: 2,
c: 3,
d: 99999999
}
})
const app = new Vue({
el: '#app',
})
console.log(app)
</script>
keep-alive
2创建,1销毁,2挂载
-
include- 指定被缓存的组件,数组、字符串或正则表达式。只有名称匹配的组件会被缓存。<!-- <keep-alive include="cp1,cp2"> --> <!-- <keep-alive :include="['cp1','cp2']"> --> <keep-alive :include="aliceCp"> -
exclude- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。 -
max- 数字。最多可以缓存多少组件实例,先进先销毁。
slot
简单使用:自定义组件内有东西想要使用时可以使用slot,被slot包裹的区域会进行展示。同时想东西展示在固定的位置时,可以slot=“aaa”取名,将想展示的东西包裹并slot="aaa"找到位置,如果有传参时使用slot-scope=“zzz”接收,{{zzz}}使用。而还有template时,寻找方式变为#aaa,如果有传参直接为#aaa="zzz",{{zzz}}使用。接收的参数是对象形式,所以可以通过zzz.调用。
refs
<div id="app">
<cp2 @abc="zzz"></cp2>
</div>
<script>
Vue.component('cp2',{
template:`<div><div ref='ccc'></div></div>`,
mounted() {//this.$refs可以访问到添加了ref命名的组件
this.$refs.ccc.innerText = Date.now()
},
})
new Vue({
el: '#app',
})
</script>
</body>
$emit(事件名,传参.子传父)
触发一个事件,并传递一个参数
<body>
<div id="app">
<cp2 @abc="zzz"></cp2>
</div>
<script>
Vue.component('cp2',{
template:`<div><h1>我是cp2</h1>{{ct}}
<button @click="fn1">我是cp2中的按钮</button></div>`,
methods: {
fn1(){
console.log('我是fn1');
this.$emit('abc', 9999)
}
},//点击按钮触发组件中fn1,再触发$emit监听的abc并传参9999,寻找abc绑定的函数并执行
data(){
return{
ct:100
}
},
})
new Vue({
el: '#app',
methods:{
zzz(res){
console.log('我是根节点中zzz');
}
}
})
</script>
</body>
$nextTick
如果有些操作需要在状态变化并且导致视图更新之后再操作的,就需要使用,因为vue的状态改变导致视图更新是异步的
$set
原理:使用object.defineproperty来给某个对象的执行添加getter与setter达到响应式。 vue3劫持整个对象,所以不需要$set了。数组只改写了几个方法,因为不确定数组有多大。
为什么数组直接通过索引值修改成员不会引起视图更新??
vue为了性能考虑并不会给每个数组都添加getter与setter,但是vue改写了pop/push/unshift/
<div id="app" >
{{ 1+ 1}}
<button @click="fn1">我是一个按钮</button>
</div>
</div>
<script>
const Qfvue = Vue.extend({//继承
methods:{
fn1(){
console.log('fn1执行')
}
}
})
new Qfvue({
el:'#app'
})
</script>
$mount 执行挂载节点
Vue.use(插件)
插件实际上就是一个对象,通过引入可以使用插件中的方法
$watch
监听器,在mounted方法中使用:
<div id="app">
数量:{{num}} x 价格:{{price}} ={{num*price}}元<br>
<button @click="num++">数量++</button>
<button @click="price++">价格++</button>
<button @click="fn1">获取金额</button><br>
{{zzzz.size}}
<button @click="zzzz.size++">大小++</button>
</div>
<script>
new Vue({
el:'#app',
data() {
return {
num:100,price:30,money:1,
zzzz:{
size:18
}
}
},
mounted() {
this.$watch('num',(nv,ov)=>{
console.log(nv);
}),
this.$watch('price',(nv,ov)=>{
console.log(nv);
}),
this.$watch('zzzz',(nv,ov)=>{
console.log(nv.size);
},{deep:true})
//深度监听,只监听zzzz时对象下size改变监听不到,使用deep:true
},
})
</script>
组件通信 不建议
<body>
<div id="app">
<cp1></cp1>
<cp2></cp2>
</div>
<script>
const bus = new Vue({})
Vue.component('cp1',{
template:`<div>我是cp1</div>`,
mounted(){
bus.$on('kfc',(val)=>{
console.log('kfc 得到val',val)
})
}
})
Vue.component('cp2',{
template:`<div>
我是一个cp2组件
<button @click="fn1">我是cp2的按钮</button>
</div>`,
methods:{
fn1(){
bus.$emit('kfc',6666)
}
}
})
const app = new Vue({
el: '#app',
})
</script>
</body>
虚拟dom render
render(h){return hh('div,'我是一个div')}
render(h) {
// vnode 虚拟dom
// h可以帮我们创建一个虚拟dom 组件最终渲染的就是这个虚拟dom
return h('div',{
class:'a',
attrs:{
id:'kkkk'
}
},
[
h('h1','我是一个h1'),
h('h1','我是一个p')
])
}
render(h){//使用
return h('div',[
h('div','我是cp1组件'),
// h('div', this.$slots.default)
this.$slots.default,
this.$slots.kfc,
])
directives 自定义指令
指针改变
<div id="app">
<div v-abc="999" class="ffff"> </div>
<button v-auth:add>添加</button>
<button v-auth:del>删除</button>
<button v-auth:update>编辑</button>
<div v-copy>111111</div>
<div v-happy> sad!!! </div>
</div>
<script>
Vue.directive('happy',{
inserted(el){
el.innerText = ' so happy!!! '
}
})
const app = new Vue({
el: '#app',
data:{
roles: ['add','update'],
weltext: '我是一段得到1212312312被复制的内容'
},
directives:{
abc:{
inserted(el,binding,vnode){
console.log('this in abc inserted', this)
console.log('el', el)
console.log('binding', binding)
console.log('vnode', vnode)
}
},
auth:{
inserted(el,binding,vnode){
// binding.arg
// vnode.context
// el ??
if(!vnode.context.roles.includes(binding.arg)){
el.remove()
}
}
},
copy:{
inserted(el,_,vnode){
// el.onclick = ()=>{
// navigator.clipboard.writeText(el.innerText);
// alert('复制成功')
// }
el.onclick = (function(){
navigator.clipboard.writeText(this.weltext);
}).bind(vnode.context)
}
}
}
})
</script>
Vue3 cn.vuejs.org/
使用const x=()=>{}来写方法,@click="x"来调用 使用const x=ref(100)或const x=reactive({num:100})来赋值 使用const x=computed(()=>{return a.value+})来写计算属性,使用.value修改值的方式修改ref对象
简写箭头函数 function() ()=> function变为箭头
const{ref,reactive,computed}=Vue //所有用到的需要导入
const app = Vue.createApp({//创建vue实例
setup() {
const a=ref(10)
const b = reactive({
name:'刚满十八岁',
money:100
})
//使用const x=()=>{}来写方法,@click="x"来调用
//使用const x=ref(100)或const x=reactive({num:100})来赋值
//使用const x=computed(()=>{return a.value+})来写计算属性,使用.value修改值的方式修改ref对象
const fn1=function(){
b.money++
}
const fn2=()=>console.log('fn2');
//简写箭头函数 function() ()=>,function变为箭头
const dbA = computed(()=>{
return a.value*2
})
return{//如果要在页面使用都需要返回
a,b,fn1,fn2,dbA
}
},
})
app.mount('#app')//挂载
组合式api
ref、reactive
ref 创建一个响应式的ref对象(状态) 读取修改值是通过value的值
reactive 创建一个被代理的响应式数据,代理的是对象或者数组等引用数据类型的数据
什么时候使用ref?
当reactive创建引用类型时,在后期重新赋值时会导致内存地址改变,响应式失效,而ref只是修改value,所以不会导致响应式失效。实际上ref就是特殊的reactive,
shallowRef、shallowReactive
由shallowRef创建的对象属性,只有在对象整体修改时才会触发响应式,而如修改对象下单个属性时不会触发响应式。
由shallowReactive创建的对象属性,在多层嵌套时只会在修改第一层的时候才会触发响应式,第二层开始就不会了。
unref
在setup中使用unref可以创建一个不具有响应式的数据(在setup中定义的const a=1;无法使用)
toref解构 将reactive解构为ref
setup中this指向问题
在setup中this指向windows,并且不能修改,那要这样得到和this.$refs(找到被ref标记的节点,获取到后可以进行相应的节点操作)对等的功能呢?首先需要const a = ref(),其中a为ref绑定的变量名(需要名字一模一样),然后就可以通过a.value获取到dom节点