1、在.vue文件中style标签可以有两个属性<style module>和<style scoped>
第一种:
<style>中使用scoped属性后,样式只作用在当前作用域,在less中不起作用。
第二种:
<style>
中使用module
属性, 可以形成名为$style
的计算属性,从而在节点中动态绑定样式。
<style module>
.test{color:red;}
</style>
使用1:
<span :class="$style.test"></span>
使用2:
<span :class="{[$style.test]:isRed}"></span>
注:isRed是在data中定义的boolean变量
可以在js中通过console.log(this.$style.red)
进行访问
2、标签使用
- v-once :如果不想改变标签的内容,可以通过使用该指令执行一次性地插值,当数据改变时,插值处的内容不会更新。
- v-html:用于输出 html 代码
- v-bind :属性值(v-bind:id/v-bind:class)。对于布尔属性,常规值为 true 或 false,如果属性值为 null 或 undefined,则该属性不会显示出来。
3、事件修饰符
- stop:阻止冒泡
- prevent:阻止默认事件
- capture:阻止捕获
- self:只监听触发该元素的事件
- once:只触发一次
- left:左键事件
- right:右键事件
- middle:中间滚轮事件
示例:
<!-- 阻止单击事件冒泡 -->
<span @click.stop="doThis">测试事件</span>
<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form @submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div @click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div @click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a @click.once="doThis"></a>
4、按键修饰符
按键:
- enter
- tab
- delete (捕获 "删除" 和 "退格" 键)
- esc
- space
- up
- down
- left
- right
系统修饰符:
- ctrl
- alt
- shift
- meta
鼠标按钮修饰符:
- left
- right
- middle
举例:
<!-- 只有在 keyCode 是 13 时调用 this.submit() -->
<input @keyup.13="submit">
或:
<input @keyup.enter="submit">
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
5、表单
(1)v-model
v-model 会根据控件类型自动选取正确的方法来更新元素。
v-model 会忽略所有表单元素的 value、checked、selected 属性的初始值,使用的是 data 选项中声明初始值。
v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value
属性和 input
事件;
- checkbox 和 radio 使用 checked
属性和 change
事件;
- select 字段将 value
作为属性并将 change
作为事件。
但是有时我们可能想把值绑定到当前活动实例的一个动态属性上,这时可以用 v-bind 实现,此外,使用 v-bind 可以将输入值绑定到非字符串。
复选框 (Checkbox):
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
// 选中时
this.toggle === 'yes'
// 取消选中
this.toggle === 'no'
单选框 (Radio):
<input type="radio" v-model="pick" v-bind:value="a" />
// 当选中时
this.pick === this.a
选择框选项 (Select):
<select v-model="selected">
<!-- 内联对象字面量 -->
<option :value="{ number: 123 }">123</option>
</select>
// 当被选中时
typeof this.selected // => 'object'
this.selected.number // => 123
修饰符:
- .lazy:在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >
- .number:自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值)
<input v-model.number="age" type="number">
- .trim:自动过滤用户输入的首尾空格
<input v-model.trim="msg">
6、vue兄弟组件之间通信
有两个组件A和B,怎么实现A和B之间的通信呢?
需要借助于一个公共的Vue的实例对象,不同的组件可以通过该对象完成事件的绑定和触发。
var bus = new Vue();
bus.$emit();
bus.$on();
定义A组件:
Vue.component("com-a",{
template:`
<div>
<h1>我是A组件</h1>
<input type="text" v-model="v"/>
<button @click="sendTo">Click Me</button>
</div>
`,
data:function(){
return {
v:""
}
},
watch:{
v:function(n){
bus.$emit("msgTo",n)
}
},
methods:{
sendTo:function(){
var me = this;
bus.$emit("msgTo",me.v);
me.v= "";
}
}
})
定义组件B:
Vue.component("com-b",{
data:function(){
return{
name:[]
}
},
template:`
<div>
<h1>我是B组件</h1>
<p v-for="it,index in name">{{it}}</p>
</div>
`,
mounted:function(){
var me = this;
bus.$on("msgTo",function(msg){
me.name.push(msg);
})
}
})
使用:
<com-a></com-a>
<hr>
<com-b></com-b>
这样就可以完成组件之间的通信啦
7.vue动画 -- transition
8、vue项目本地启动后,使用localhost可以访问,但换成本地ip不能访问的情况。找到webpack.dev.js文件,将host:"localhost"改为host:"0.0.0.0"即可使用ip进行访问。
Vue3.0
从2.0基于对象的编程(OOP)转向了函数时编程(FP).
OOP特点是对象或class是对数据和逻辑的封装。在3里面,把相关的数据和操作写在了setup里面。FP特点是函数只做一件事返回一个值。
// 可将vue组件看作一个函数
<template>
<div>测试</div>
</template>
<script>
export default{
props:{
xxx:{
type:XXX,
default:""
}
}
}
</script>
纯函数VS非纯函数对比:
纯函数特点:相同的输入产生相同的输出;不能有语义上可观察的函数"副作用"(side effect)。
在vue组件种,它需要获取数据、改变状态、修改DOM、事件监听等,这些可以理解为函数的“副作用”(side effect)。
响应式原理
vue2.0 是利用 Object.defineProperty可以挟持并自定义对象的 setter 操作和 getter 操作来实现的,但是这有一些问题:
- 数组 这一数据类型的数据 defineProperty 无法直接挟持,需要使用比较 hack 的操作去完成
- 增、删 操作在一些场景下无法捕获到,在无法挟持到的场景下,我们就必须使用
$set
、$delete
这些 Vue 封装的函数去代替 js 原生的值操作,增加了心智成本- 性能的浪费,因为无法知道具体哪些值是需要响应式的,2.0 对只要是在 data 里能挟持的都会挟持一边,但实际上开发者数据更改的粒度往往不会这么细,所以这会导致一定程度上的性能浪费(用
Object.freeze()
等操作可以在一定程度上解决这个问题)
vue3.0 则是使用 Proxy 来实现数据更改的监听,Proxy 从定义上来说简直就是完美的为 **数据挟持 **这一目的而准备的
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
从某种意义上来讲,Proxy 可以视为 Object.defineProperty 的强化,它拥有更丰富的可以挟持的内容,并且解决了上面描述使用 defineProperty 存在的问题:
- 不再需要专门为 数组 进行特殊的挟持操作,Proxy 直接搞定
- 增、删 操作 Proxy 也能直接挟持
- 因为 Proxy 的一些特点,Proxy 可以实现惰性的挟持,而不需要深度的挟持所有值
- 同时因为 Proxy 并不是对数据源进行更改,从而可以保证不会出现太多的副作用
依赖收集的区别:
2 中是通过
Observer
,Dep
,Watcher
这三个类来实现依赖收集;3 中是通过track
收集依赖,通过trigger
触发更新,本质上就是用 WeakMap,Map,Set 来实现
setup函数
// setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数
// setup函数中不能使用this
// setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新
// setup 若使用解构prop,可以通过使用 setup 函数中的 toRefs 来完成此操作
// context:attrs、slots、emit
import { toRefs } from 'vue'
....
setup(porps, context){
// Attribute (非响应式对象)
console.log(context.attrs)
// 插槽 (非响应式对象)
console.log(context.slots)
// 触发事件 (方法)
console.log(context.emit)
const { title } = toRefs(props);
console.log(title.value)
}
....
在vue3种提供watchEffect的钩子函数:
import { onMounted,reactive,watchEffect } from "vue";
export default{
name:"xxx",
setup(props){
onMounted(() => {......});
wacthEffect() => {......});
return{ ...... };
},
data(){return{.......}},
wacth:{......},
created(){......},
mounted(){......}
}
-
watchEffect()函数:监听函数,在props中数据第一次初始化和更改时进行触发。这个方法在初始化时会在onMounted周期之前调用一次,但是此时dom节点未挂载,若进行操作dom会报错。
// 第一个参数effect函数,effect()中还能接收一个参数onInvalidate()清除副作用函数,主要作用就是在组件销毁是进行 effect()的清除操作。 import {ref,watchEffect} from "vue"; export default { setup(){ const count = ref(0); const effect = () => console.log(count.value); watchEffect(effect); setTimeout(() => count.value++,1000); return{ count }; } } 带参数: watchEffect((onInvalidate) => { onInvalidate(() => { // run if id has changed or watcher is stopped }); }); // 第二个参数options,类型是WatchEffectOptions,是指何时运行副作用函数。比如,你希望副作用函数在组件更新前发生,可以将 flush 设为 'pre'(默认是 'post')。还有 WatchEffectOptions 也可以用于 debug:onTrack 和 onTrigger 选项可用于调试一个侦听器的行为(当然只开发阶段有效)。 watchEffect( () => { /* ... */ }, { flush: "pre", onTrigger(e) { debugger; }, } );
-
template模板部分:通过Fragment()函数巧妙进行切片处理,在template中可以进行多个根节点的书写
核心方法
reactive
// 接收一个参数,判断这个参数是否是对象;
// 创建拦截器对象 handler,设置 get / set / deleteProperty
// 返回 Proxy 对象
// 返回的对象,重新赋值丢失响应式
// 返回的对象不可以解构
export function reactive(target) {
const handler = {
get(target, key, receiver) {
},
set(target, key, value, receiver) {
},
deleteProperty(target, key, receiver) {
}
}
return new Proxy(target, handler) // targe 目标对象,handler
}
ref
// 适用于基本数据类型的响应式对象转化
// 返回的对象,重新赋值成对象是响应式的
export function ref(raw) {
// 判断 raw 是否是ref 创建的对象,如果是的话直接返回
if (isObject(raw) && raw.__v_isRef) {
return
}
let value = convert(raw) // 将 raw 转化为响应式对象,包括对象的对象
const r = {
__v_isRef: true, // ref 的标识
get value() {
return value
},
set value(newValue) {
if (newValue !== value) {
raw = newValue
value = convert(raw) // 将 newValue 转化为响应式对象,包括对象的对象
}
}
}
return r
}
toRefs
// 将 引用数据类型,遍历到 目标对象上,可实现解构方法,因 reactive 无法实现解构,可通过此方法进行解决
export function toRefs(proxy) {
const ret = proxy instanceof Array ? new Array(proxy.length) : {}
for (const key in proxy) {
ret[key] = toProxyRef(proxy, key) // 将目标对象的值分别挂载到最外层,可实现属性-解构
}
return ret
}
function toProxyRef(proxy, key) {
const r = {
__v_isRef: true,
get value() {
return proxy[key]
},
set value(newValue) {
proxy[key] = newValue
}
}
return r
}
// mapGetters:vue中的计算属性
// mapState:变量 ,相当于this.$store.state.xxx
// mapMutations:vue中的methods
...
computed:{
...mapGetters('cart',{
// 对应store中的this.$store.getters["cart/count"]
count:'count'
}),
...mapState('user',[
// 将this.user映射成this.$store.state.user.user
'user'
]),
},
methods:{
...mapMutations('user',[
// 将this.delete映射为this.$store.commit('user/delete')
'delete'
]),
}