Vuex
作用:全局状态管理。在state中定义一个数据后,可以在所在项目中的任何一个组件里进行获取、修改,并且修改的结果可以得到全局的响应变更。
Vuex的核心部分:State,Getter,Mutation,Action
state
:包含了store中存储的各个状态。
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
}
})
getter
: 类似于 Vue 中的计算属性,根据其他 getter 或 state 计算返回值。
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
mutation
: 包含有一组方法,是改变store中状态的执行者,只能是同步操作。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
//通过store.commit方法调用
store.commit('increment')
载荷(Payload)
mutations: {
increment (state, n) {
state.count += n
}
}
//第一个参数是state,后面的参数是向 store.commit 传入的额外的参数
this.$store.commit('increment', 10)
// 以载荷形式
store.commit('increment',{
amount: 10 //这是额外的参数
})
// 或者使用对象风格的提交方式
store.commit({
type: 'increment',
amount: 10 //这是额外的参数
})
action
: 包含有一组方法,其中可以包含异步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
//使用参数解构来简化代码
actions: {
increment ({ commit }) {
commit('increment')
}
}
action
进行异步调用:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
//调用方式
store.dispatch('actionA').then(() => {
……
})
//配合async/await使用,假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
modules
:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护
Vuex的运行逻辑
Vue组件使用this.$store.dispatch
调用Action
,Action
使用this.$store.commit
调用Mutations
,Mutations
修改State
的数据,再使用Render
函数重新渲染。
Backend API
:建议与后端交互放在Actions
进行操作Devtools
:Vue官方为我们提供的Vuex状态调试工具, 它能跟踪到Vuex的每一个操作和状态改变
Mutations
是否可以省略?
可以省略,省略后可以直接使用Actions
操作State
数据,但是不建议。省略后会出现几个问题:
Devtools
工具无法监控到对State
数据的操作Actions
可以对Mutations
进行分层和重复调用
事件循环(Event Loop)
JavaScript特点就是单线程
为了利用CPU多核计算的能力,HTML5提出了Web Worker标准,允许JavaScript创建多个线程,但是子线程完全受主线程控制且不能操作DOM。
异步任务
为了避免一些操作较慢的设备(Ajax操作)影响主线程的执行,提出了异步任务。可以将等待中的人任务挂起,等待IO设备操作完成后再调用该任务继续执行。
- 同步任务:在主线程上排队执行的任务,前一个任务执行完毕才能执行后一个任务
- 异步任务:不进入主线程,进入“任务队列”(task queue),当“任务队列”通知主线程某个异步任务可以执行后,该异步任务才能进入主线程执行。
主要主线程空了,就会去读取“任务队列”
回调函数(callback)
- 回调函数是被主线程挂起的代码。异步任务必须指定回调函数,主线程执行异步任务就是执行回调函数。
- “任务队列”中的事件,除了设备IO以外还包括了一些用户产生的事件(鼠标点击,页面滚动)。只要指定了回调函数的事件都会加入到“任务队列”中,等待主线程读取
- “任务队列”是一个先进先出的数据结构,排在前面的事件优先被主线程读取执行。只要执行栈清空,“任务队列”的第一个事件就会进入主线程。但是,由于存在“定时器”功能,主线程要检查一下执行时间,到了规定时间才能进入主线程。
事件循环
- 主线程运行时生成堆和栈
- 栈中的代码调用外部API
- 外部API在“任务队列”中加入各种事件
- 主线程执行完毕后读取“任务队列”,依次执行回调函数
- 同步任务总是先于异步任务执行
定时器
"任务队列"还可以放置定时事件,即指定某些代码在多少时间之后执行。
console.log(1);
setTimeout(function(){console.log(2);},0);
console.log(3);
// 1 3 2
setTimeout(fn,0)
的含义是在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。
宏任务和微任务
- 宏任务:由宿主(浏览器、Node)发起,后于微任务运行,会触发新一轮的事件循环
- setTimeout
- setInterval
- I/O
- setImmediate(Node.js)
- requestAnimationFrame(浏览器)
- 微任务:由JS引擎发起,先于宏任务运行,不会触发新一轮的事件循环
- Promise
- MutationObserver(浏览器)
- process.nextTick(Node.js)
Vue3和Vue2的区别以及双向绑定的区别
Vue3和Vue2的区别
- 获取变量的区别
Vue2
<template>
<div class='form-element'>
<h2> {{ title }} </h2>
<input type='text' v-model='username' placeholder='Username' />
<input type='password' v-model='password' placeholder='Password' />
<button @click='login'>
Submit
</button>
<p>
Values: {{ username + ' ' + password }}
</p>
</div>
</template>
Vue3
<template>
<div class='form-element'>
<h2> {{ state.title }} </h2>
<input
type='text'
v-model='state.username'
placeholder='Username'
/>
<input
type='password'
v-model='state.password'
placeholder='Password'
/>
<button @click='login'>
Submit
</button>
<p>
Values: {{ state.username + ' ' + state.password }}
</p>
</div>
</template>
Vue3中的反应数据(Reactive Data)是包含在一个反应状态(Reactive State)变量中。所以我们需要访问这个反应状态来获取数据值state.username
。
- 建立数据
data
的区别
Vue2
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
}
}
Vue3
import { reactive } from 'vue'
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: ''
})
return { state }
}
}
在Vue3,我们就需要使用一个新的setup()
方法,此方法在组件初始化构造的时候触发。
使用以下三步来建立反应性数据:
- 从vue引入reactive
- 使用reactive()方法来声名我们的数据为反应性数据
- 使用setup()方法来返回我们的反应性数据,从而我们的template可以获取这些反应性数据
- 编写
methods
的区别
Vue2
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
methods: {
login () {
// 登陆方法
}
}
}
Vue3
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: ''
})
const login = () => {
// 登陆方法
}
return {
login,
state
}
}
}
Vue3 的合成型API
里面的setup()
方法也是可以用来操控methods
的。创建声名方法其实和声名数据状态是一样的。— 我们需要先声名一个方法然后在setup()
方法中返回return
, 这样我们的组件内就可以调用这个方法了。
- 生命周期函数:
mounted()
为例
Vue2
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
},
mounted () {
console.log('组件已挂载')
},
methods: {
login () {
// login method
}
}
}
Vue3
import { reactive, onMounted } from 'vue'
export default {
props: {
title: String
},
setup () {
// ..
onMounted(() => {
console.log('组件已挂载')
})
// ...
}
}
需要另外从vue中引入。和刚刚引入reactive()
一样,生命周期的挂载钩子叫onMounted()
。
- 计算属性
Vue2
export default {
// ..
computed: {
lowerCaseUsername () {
return this.username.toLowerCase()
}
}
}
Vue3
import { reactive, onMounted, computed } from 'vue'
export default {
props: {
title: String
},
setup () {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
// ...
}
在 Vue3 使用计算属性,我们先需要在组件内引入computed()
。
使用方式就和反应性数据reactive data
一样,在state
中加入一个计算属性
Props
的接收
Vue2
mounted () {
console.log('title: ' + this.title)
}
Vue3
setup (props) {
// ...
onMounted(() => {
console.log('title: ' + props.title)
})
// ...
}
- 组件之间
props
参数传递是Vue2和Vue3的最大区别:this
在Vue2和Vue3中代表的意义不同- Vue2的
this
代表当前组件,不是特定属性 - Vue3的
this
不能拿到props
、emit
、events
和组件内其他属性,可以通过setup()
接收两个参数:props
、content
(emit、slots、attrs)
- Vue2的
emit
事件
Vue2
login () {
this.$emit('login', {
username: this.username,
password: this.password
})
}
Vue3
setup (props, { emit }) { // 解构赋值
// ...
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
// ...
}
在setup()
中的第二个参数content
对象中就有emit
,这个是和this.$emit
是一样的。那么我们只要在setup()
接收第二个参数中使用分解对象法取出emit
就可以在setup()
中随意使用了。
Vue2和Vue3在代码上的不同主要体现在:
- 获取变量的区别
- 建立数据
data
的区别 - 编写
methods
的区别 - 生命周期函数的区别
- 计算属性的区别
Props
的区别emit
的区别
Vue3和Vue2双向绑定的区别
Vue2
- 使用
Object.defineProperty
进行数据劫持 - vue需要递归遍历对象的所有属性挨个进行绑定
- 数组的更新不能被检测到
Vue3
- 使用
Proxy
进行数据劫持 - 可以直接拦截整个对象,不需要再进行递归
Vue的特性
- 双向数据绑定
- 生命周期
- 组件化
- 客户端路由
- 渐进式框架(路由、vuex、网络请求)
js垃圾回收机制
采用“标记-清除”算法
- 垃圾回收器获取根并“标记”(记住)它们
- 然后它访问并“标记”所有来自它们的引用
- 然后它访问标记的对象并标记它们的引用。所有被访问的对象都被记住,以便以后不再访问同一个对象两次
- 以此类推,直到有未访问的引用(可以从根访问)为止
- 除标记的对象外,所有对象都被删除
less、sass
less
Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量、混合(mixin)、函数等功能,让 CSS 更易维护、方便制作主题、扩充。
- less中的变量
@变量名:变量值 - 混合
相当scss的混合宏
①无参数混合;实际上就是一个普通的class选择器,会被编译到css文件中.class{}
②有参无默认值混合声明:.class(@param){}
调用:.class(paramValue);
③有参有默认值混合:声明:.class(@param:10px){}
调用:.class(paramValue); 或 .class()
- 匹配、@argument、嵌套
sass
Sass 是一款强化 CSS 的辅助工具,它在 CSS 语法的基础上增加了变量 (variables)、嵌套 (nested rules)、混合 (mixins)、导入 (inline imports) 等高级功能。
- scss中的变量
声明变量:$变量名:变量值,允许将变量嵌套在字符串中,但是变量必须使用 ${} 包裹 - scss中的运算
scss中的运算,会将单位进行运算,使用时需注意最终的单位是否正确 - scss中的嵌套
选择器嵌套 属性嵌套 伪类嵌
① 选择器嵌套 ul{li{}},嵌套默认表示后代选择器,如果需要子代选择器,可以在选择器前加>;可以在选择器的{}中,使用&表示上一层的选择器。
② 伪类嵌套: li{&:hover{}},在选择器的{}中,使用&配合伪类事件,可以表示当前选择器的伪类。
③ 属性嵌套: font:{size:18px;},对于属性名有-分割为多段的属性,可以使用属性嵌套,属性名的前半部分必须紧跟一个:才能用{}包裹属性的后半部分。 - 混合宏
使用@mixin声明混合宏,在其他选择器中使用@include调用混合宏 - 继承
声明一个普通class,在其他选择器中使用@extend继承这个class - 条件语句、循环、函数