简单了解MVVM 的了解
-
MVC ( model view controller )的工作原理
view : Html + Css 数据渲染层 ---> controller --->model
controller : js 控制逻辑的 --->model
Model :data,底层数据 ----> view
-
MVVM(Model-View-ViewModel)的工作原理
1.数据会绑定在viewModel层并自动将数据渲染到页面中
2.视图的变化,会通知viewModel更新数据
vue模版 {{}}
- 问题1: 绑定数据的计算可以写到{{}}吗? 可
- 问题2: {{}} 可以处理表达式吗? 可
- 问题3:{{add(number)}} 可以处理吗? 可
- 问题4:{{a>3?'ff':'dd'}}条件判断可以吗? 可
- 问题5: {{&& || !}} 可
总结:
-
利用花括号,构筑了数据和视图的双向绑定
-
通过视图绑定事件,来处理数据
Vue生命周期
- 前端需要维护一些状态,就要考虑时序的问题。生命周期也就是对时序的实现。
// 创建
beforeCreate => created => beforeMount => mounted => beforeUpdate => updated => beforeDesrtoy => destroyed
bC:new Vue() 空实例 ---> 实例挂在功能
c:有实例data、props、methods ---> 数据操作、不涉及vdom和dom
bM: vDom ---> 数据操作,但是不可操作dom
m: Dom ----> 任何操作
bU:vDom ----> vDom更新了的,dom未更新,可以操作数据
up:dom已经更新了 -----> 谨慎操作数据
bD:实例vm尚未被销毁。 -----> 清空eventBus reset store clear计时器
d:实例已经被销毁 -----> 收尾
为什么vue要用一个函数data ,return 返回出去
作用域问题:如果是一个对象,对象是一个指针,当多个同名组件一起使用,同名对象会存在引用关系,也就是会发生合并的情况,当改了一个组件,其他组件也会发生改变,就做不到组件之间相互隔离了。设计成一个函数,函数内部有自己的作用域,可以隔离开,函数不会发生合并。
总结:防止组件之间数据相互污染
定向监听
computed
计算属性是基于它们的响应依赖关系缓存的。计算属性只在相关响应式依赖发生改变时它们才会重新求值。 函数会反复执行,因此不推荐
// 计算属性
computed:{
changeComputed() {
return this.test.length >0?'字符串修改':"字符串未修改"
}
},
// 使用
{{ changeComputed }}
// 方法定义
changeComput() {
return this.test.length >0?'字符串修改':"字符串未修改"
}
// 方法使用,不推荐
{{ changeComput() }}
watch
只能监听data 里面的数据,做一些处理,不要再次修改监测的值,会触发死循环
watch:{
test(old,newD) {
console.log('old',old,newD)
}
},
computed 和 watch的区别
相同点:
- 1.基于vue的依赖收集机制
- 2.都是被依赖的变化触发,进而进行改变进行处理计算
不同点:
1.入和出
- computed : 多入单出 ---- 多个值变化,组成一个值的变化
- watch:单入多出 单个值的变化,进而影响一系列的状态变更
2.性能
- computed:会自动diff依赖,若依赖没有变化,会从缓存中读取当前计算值
- watch:无论监听值变化与否,都会执行回调
3.写法上
- computed :必须有return返回值
- watch:不一定
4.时机上
- computed: 从首次生成赋值,就开始计算了
- watch:首次不会运行,除非immediate:true
条件判断
v-if v-else v-else-if
- dom上展示元素或者销毁元素
- 可以使用在template 上,元素上
- v-if 不要与v-for 一起使用,v-for比v-if 有更高的优先级
<div v-if="isActive">
测试是否是条件渲染
</div>
<div v-else>
测试条件不渲染
</div>
v-show
- 一开始就渲染元素,dom元素始终存在文档中,只是display:none or block
- 不能使用在template 上
<div v-show="isActive">
测试是否是条件渲染
</div>
this.isActive = !this.isActive;
v-show 对比 v-if
- 渲染
- v-if 无dom,不会渲染实际节点及其子节点
- v-show 存在实际节点及其子节点,但不展示,不占据位置
- 性能
- v-show 初始开销比较大,适用于频繁的逻辑切换
- v-if 切换性能开销大,条件内的元素会适当的进行销毁和删除
列表循环
- in of 遍历都可以
- 两个参数(item,index) , 三个参数(value,name,index)
- 如果是修改数据,不想修改元始数据,可以使用计算属性
- 计算属性不适用的情况下,可以使用方法去处理
- v-for 可以使用在template,dom元素上
<div
v-for="(i,index) in testData" testData =[],{} 可以是数组也可以是对象
:key="index"
>
{{ i.name }}
</div>
v-for 和 v-if 的优先级
v-for > v-if 先循环 再判断
key的作用
1.模版的编译原理 template => dom
template => 正则匹配语法。生成ST: 静态值 + 动态值 => 转换为AST 为可执行方法 => render() => dom
2.dom diff
层级:只考虑单层复用,多层遍历实现
顺序:双向指针,首位指针向中间移动
替换:移动、新增、删除;优先复用 ---key(快速识别顺序)
3.key作用 尽可能复用节点 常见问题:index做key,随机数做key是不合适的
指令
默认指令
- v-once - 渲染一次
- v-text - 渲染字符串
- v-html 渲染html
- v-bind 绑定赋值
- v-on 监听
- v-model 双向绑定
- 自定义指令
---------------------v-html-----------------------
<div>{{ testHtml }}</div> // 文本
<div v-html="testHtml" /> //html
data(){
return {
test:'sdfas',
testHtml:'<span style="color:red">测试span</span>'
}
}
---------------------v-bind-----------------------
作用:表达式的值改变,响应式的作用给Dom
<div :id="testId"> // 绑定id
safsa
</div>
data(){
return {
test:'sdfas',
testHtml:'<span style="color:red">测试span</span>',
testId:'ddd'
}
<button :disabled='dis'>按钮</button> // 绑定禁用
<div :[daymic]='doSome'>动态参数</div> // 动态绑定
:class="[isActive,isColor]" // 数组绑定样式
<div :class="classObj"></div> //对象绑定样式
data:{
classObj:{
active:true,
textcolor:false
}
}
:style="{color:activeColor}" // 内联样式
---------------------v-on-----------------------
<div @click='fun'></div> // 单函数
<div @click='fun("sfs")'></div> // 单函数传递参数
<div @click="oneFun($event),twoFun($event)"> // 多函数执行
点击
</div>
---------------------v-model-----------------------
表单数据绑定
<select v-model='selected'>
<options v-for='item in data' :value = "item.value">{{item.label}}</options>
</select>
---------------------自定义指令-----------------------
directives:{ // v-focus
focus:{
mounted(el) {
el.focus();
},
},
chil:{ // v-chil
mounted(el) {
el.innerHTML = '添加子组件'
}
}
},
1. 可以定义全局或者局部的自定义指令
2. 可以定义指令钩子,也就是生命周期
3.可以动态参数
事件设计 - 为何vue把事件写在模版上,而不在js中
模版定位触发源 + 出发源区寻找触发事件逻辑 ---更方便定位问题
js与事件本身解耦,自动解绑事件
组件化
一般组件
一个文件就是一个组件,组件注册如下
components: {
ComA,
ComB
}
动态组件
comTab:['World','Home2'], // 组件名字
currentTab:'World' // 当前激活的组件
<button
v-for="tab in comTab"
:key="tab"
@click="handleClick(tab)" // 切换组件
>
{{ tab }}
</button>
<component :is="currentTab" /> // 自动匹配,通过组件名字
<keep-alive> // 将被缓存起来
<component :is="currentTab" />
</keep-alive>
handleClick(value) {
this.currentTab = value;
}
动态导入组件
import { createApp, defineAsyncComponent } from 'vue'
createApp({
// ...
components: {
AsyncComponent: defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
}
})
插槽
局部模版的使用
默认插槽
<Hello>
<p>sdfas</p>
</Hello>
// hello 内部
<slot />
具名插槽
以name 标记插槽的身份,可以控制具体的位置,从而在组件内部可以做到区分开来 ,接受父组件的数据
<Hello>
<template v-slot='test-slot'>{{name}}</template>
</Hello>
// Hello 内部
<slot name='test-slot'></slot>
作用域插槽
可以接受props(scope-slot),父组件接受子组件的数据
<Hello>
<template v-slot='test' slot-scop="{slotProps}">{{slotProps}}</template>
</Hello>
<Hello>
<template v-slot:slotProps>{{slotProps}}</template>
</Hello>
// Hello
<slot name='test' :slotProps="data"></slot>
模版数据的二次加工
过滤器
vue2中可以这样用,vue3中已经不推荐这样用了
// 使用
{{usrname | changeName }}
//定义
filters:{
changeName(name) {
// filter 过滤器this不指向实例
return name + '?ddd';
}
}
通过指令 v-html
<p v-html='name > 99 ? 99: name'></p>
jsx
vue2可以这样用,vue3已经不建议了
render(h) { // 和react差不多
return (
<div>
dasf
</div>
)
}
组件化
混入 mixin
- 1.应用:封装相同逻辑,逻辑相同,模版不一样
mixins:[minDemo]
合并策略: data冲突时,以组件主体优先; 生命周期钩子,先mixin 执行后主体执行 递归合并,递归优先级仍以主体优先
缺点:数据来源不太明确
继承扩展 extends
应用扩展逻辑的
extends:demo1
合并策略: 与mixin相同
合并优先级: mixin > extends 回调优先级:extends > mixin
extend
从预定义的配置中拓展一个独立配置项,并且进行合并
let _baseOptions = {
data:function() {
return {
course:'sdaf'
}
},
created() {
}
}
const BaseCom = Vue.extend(_baseOptions);
new BaseCom({
created() {
console.log('extend created')
}
})
插件
注册外部的插件
export default {
install:(Vue,options) => {
Vue.globalMethod = function() {
console.log('---')
}
Vue.directive('my-directive',{
})
Vue.mixin({
created() {
}
})
Vue.prototype.$yy = function() {
}
}
}
vue 通信
- 父组件通过props向子组件传递
- 子组件通过emit向父级传递,父级通过方法接收
- provide inject
- vuex
vuex
vuex的工作原理
component 会触发action ,action会触发mutation,mutation会更新states,states再作用于component
问: 为什么不直接mutation,而还要通过action操作
- 保证states的更新已经是同步的了,数据流转就是可控的。
- action的作用是处理所有的行为导致的值改变(async改变)
- mutation 不应该承载太多的业务逻辑,只改变值
问:为什么vuex自己定义告警
- 告警级别控制,可以提示,可以阻断,可以警告,可以上报
问:vuex中为什么通过Object.create(null)定义空对象,而不是{}
Object.create(null).proto 是undefined,不存在原型上的一些方法,比较纯粹
({}).proto 是Object.prototype,存在一些对象原型上的方法
vuex的使用
modules 为了隔离相互store的相互影响
// main.js
const app = createApp(App)
app.use(store)
app.mount('#app');
// store.js
import { createStore } from "vuex";
import testaStore from './testaSote';
export default createStore({
modules:{
testaStore
}
})
// testaStore.js
export default {
namespaced: true,
state: {
count: 0
},
mutations: {
incre (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('incre')
}
}
}
// 计算属性使用
computed:mapState({
te : state => state.testaStore.count
}),
// 方法调用
this.$store.dispatch('testaStore/increment')
SSR
- csr static 文件 + data -----> 客户端 ----> page
- SSR static 文件 + data ---> server ----> page ---->客户端 (提升首屏速度) SSR的优点
- 因为服务端渲染每次返回的只有一个页面,是一个多页应用,所以速度能够提升 spa单页应用是加载整个应用,所以速度会比较慢
- 利于SEO(搜索引擎检索),因为服务端刚开始就把页面信息返回了,而spa在解析出来之前是不知道的
SSR的缺点
1.spa的页面采用的是分布式的逻辑, 页面性能加载由自己的机器的性能负责
2.SSR的每次渲染都需要经过服务端处理,对所有的用户负责,压力大