Vue学习总结
Vue内容总结
Vue学习总结
本人工作两年后,工作中写的,笔记一直记录在语雀中
生命周期
普通生命周期
beforeCreate(创建前): 在此生命周期函数执行的时候,data和methods中的数据都还没有初始化。
created(创建后): 在此生命周期函数中,data和methods都已经被初始化好了,如果要调用 methods中的方法,或者操作data中的数 据,最早只能在created中操作。
beforeMount(载入前): 在此生命周期函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中去,此时页面还是旧的。
mounted(载入后): 此时页面和内存中都是最新的数据,这个钩子函数是最早可以操作dom节点的方法。
beforeUpdate(更新前): 此时页面中显示的数据还是旧的,但是data中的数据是最新的,且页面并未和最新的数据同步。
Updated(更新后): 此时页面显示数据和最新的data数据同步。
beforeDestroy(销毁前):当执行该生命周期函数的时候,实例身上所有的data,所有的methods以及过滤器…等都处于可用状态,并没有真正执行销毁。
destroyed(销毁后): 此时组件以及被完全销毁,实例中的所有的数据、方法、属性、过滤器…等都已经不可用了。
特殊生命周期
加入了keep-alive才会有下面这两个生命周期。
activated(组件激活时): 和上面的beforeDestroy和destroyed用法差不多,但是如果我们需要一个实例,在销毁后再次出现的话,用 beforeDestroy和destroyed的话,就太浪费性能了。实例被激活时使用,用于重复激活一个实例的时候
deactivated(组件未激活时): 实例没有被激活时。
errorCaptured(错误调用): 当捕获一个来自后代组件的错误时被调用
第一次进入keep-alive:会执行普通的生命周期函数然后再执行activated,退出执行deactivated。
第二次进入只执行:activated。
keep-alive
vue自带的组件,用来缓存组件,提升项目的性能的。减少相同入参的服务重复调用的问题。
v-if和v-show、v-for
区别
v-if:dom节点是否存在
v-show:是dom元素属性display: none/block
使用场景
v-if:初次加载和变化次数较少使用,不会加载dom盒子
v-show:频繁切换,比创建删除的开销小
v-if于v-for优先级
v-for的优先级高。
取决于源码,并没有多大意义。大概再源码一万一行左右,function genElement()
ref
获取dom,用来解决复杂繁琐的获取dom
nextTick
dom更新后,异步执行的。
简单使用场景:
再方法中用this修改了data的变量的数据,通过ref获取的innerHTML还是旧的,通过nextTick可以获取修改后的数据一般用于服务数据和data数据冲突时 延迟计算dom数据
scoped原理
让样式只在本组件中生效,不在其他组件生效。
给节点新增自定义属性,css根据属性选择器添加样式
CSS穿透
SCSS:/deep/
stylus:>>>、/deep/
传值通讯
父子传值
父->子
props
:key='value'
props: {
key: String(类型)
}
子->父
$emit
this.$emit(key,value)
子组件:
this.$emit(key,value)
父组件:
@key='getFunction'
methods:{
getFunction(value){
// value就是子组件的传值
}
}
兄弟传值
通过中转实现(bus)
import bus from '@/common/bus'
bus.$emit(key,value)
bus.$on(key,(data)=>{
// data就是传值
})
computed、methods、watch区别
computed:计算属性 计算属性是基于它们的响应式依赖进行缓存的
methods:方法
watch:监听
computed和methods
computed:是有缓存的
methods: 没有缓存的
computed和watch
watch:数据或者路由发生改变才会执行,监听的值发生改变。
computed:可以计算某一个属性的改变,内部的一个值改变了也会监听到并返回最新的值
props和data优先级
props优先级大于data
源码中已经体现了优先级,大概在50行左右;
优先级循序:props -> methods -> data -> computed -> watch
Vuex
专为 Vue.js 应用程序开发的状态管理模式 + 库
属性
state:用于存放数据。类似于组件中的data
getters:计算属性,类似组件中的computed
mutations(同步事务):方法,类似组件中的methods
actions:提交mutatations
modules:模板,可以再细分让仓库更好管理
Action(可以异步操作):提交的是 mutation,而不是直接变更状态
Vuex是单向数据流
不能直接更改state中的数据
持久化存储
Vuex本身不是持久化存储
- 使用localStorage
- 使用插件(npm install --save vuex-persist)
export default{
state: {
num: localStorage.getItem('key') || 1
},
mutations: {
add ( state ) {
state.num++
localStorage.setItem('key',state.num)
}
}
Vue代理
vue cli中的 devServer
module.exports = {
devServer: {
proxy: 'http://localhost:3000'
}
}
打包问题处理
使用publicPath打包修正
module.exports = {
publicPath: './',
devServer: {
proxy: 'http://localhost:3000'
}
}
路由
路由模式
自己写代码测试使用hash模式
上线一般使用history
区别
形态区别
hash带 # :http://localhost:8080/#/home(例)
history:http://localhost:8080/home(例)
跳转请求
history:找不到的时候会发送请求
http://localhost:8080/id(例) ===> 发送请求
hash: 不会发送请求
打包前端自测使用hash,如果使用history会出现空白页
路由传值
两种方式:显式、隐式
显式:http://localhost:8080/#/test?a=1
通过path跳转路径 query传递
this.$router.push({
path: '/test',
query: {
a: 1
}
})
// 获取
this.$route.query.a
隐式:http://localhost:8080/#/test
this.$router.push({
name: 'test',
params: {
a: 1
}
})
// 获取
this.$route.params.a
路由导航守卫
分类:全局、路由独享、组件内
全局:一般在main.js文件使用,还有在路由文件中使用
路由独享:在配置路由里面写
组件内:在组件中使用,一般不会用到
全局
一般在 main.js 文件中使用 (还有在路由文件中使用),router 即是路由。
- next(false): 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。就像我们调用 router.push 一样。
全局前置守卫 beforeEach
router.beforeEach((to, from, next) => {
console.log(to) => // 到哪个页面去?
console.log(from) => // 从哪个页面来?
next() => // 一个回调函数,顺利通过,往下走
}
全局解析守卫 beforeResolve
确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用。
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// ... 处理错误,然后取消导航
return false
} else {
// 意料之外的错误,取消导航并把错误传给全局处理器
throw error
}
}
}
})
全局后置钩子 afterEach
没有 next函数 也不会改变导航本身
router.afterEach((to, from, failure) => {
if (!failure) sendToAnalytics(to.fullPath)
})
路由独享beforeEnter
从一个路由跳到另一个路由导航时才会触发,params、**query **或 hash 改变时不会触发
**只在路由进入是触发,写在路由文件中。**
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
组件内(不常用)
进入路由:beforeRouteEnter
路由改变:beforeRouteUpdate
离开渲染该组件:beforeRouteLeave
必须有 next();,相当于一个按钮开启一样,使用了next 代表通过,直接进to 所指路由
- 进入路由时调用,此时不能获取组件实例 this ,因为的当前守卫执行前,组件实例还没被创建
beforeRouteEnter(to,from,next){
console.log(to,to);
next(vm => {
// 通过 `vm` 访问组件实例
})
}
- 在当前路由改变,但是该组件被复用时调用,它可以访问组件实例 this,举例来说,对于一个带有动态参数的路径 /users/:id,在 /users/1 和 /users/2 之间跳转的时候
beforeRouteUpdate (to, from, next) {
console.log(to, 'to')
next(vm => {
// 通过 `vm` 访问组件实例
})
}
- 在导航离开渲染该组件的对应路由时调用(离开当前路由页面时调用),与 beforeRouteUpdate 一样,它可以访问组件实例 this
beforeRouteLeave (to, from, next) {
console.log(to,to)
next(vm => {
// 通过 `vm` 访问组件实例
})
}
动态路由 children
一般来说就是,前面一个不改变通过改变后面一个。
通过动态路由实现当前页面下,点击切换到下面内同时不刷新当前页面的方法
{
path: '/list',
name: 'List',
children: [
{
path: '/list/:id',
component: () => import(/* webpackChunkName: "List" */ '../views/routerList/details.vue')
}
],
component: () => import(/* webpackChunkName: "List" */ '../views/routerList/list.vue')
}
SPA以及SPA优缺点
SPA是 单页面应用 的意思
缺点:
- SEO优化不好 (多页面,动态的改变等)
- 性能不是特别好(造成重复、浪费啊等问题)
源码解析(知道原理就好)
双向绑定原理
通过Object.defineProperty劫持数据发生的改变,如果数据发生改变了(在set中进行赋值的),触发update方法进行更新节点内容({{ str }}),从而实现了数据双向绑定的原理。
class Vue{
constructor( options ){
this.$el = document.querySelector(options.el) // 获取节点
this.$data = options.data // 获取数据
this.compile( this.$el ) // 绑定数据方法
}
compile( node ){
node.childNodes.forEach((item,index)=>{
// 元素节点
if( item.nodeType == 1 ){
if( item.childNodes.length > 0 ){
this.compile( item ) // 递归查找内同
}
}
// 这是文本节点,如果有{{}}就替换成数据
if( item.nodeType == 3 ){
let reg = /\{\{(.*?)\}\}/g // 正则匹配{{}}
let text = item.textContent // 获取文本内容
// 给节点赋值
item.textContent = text.replace(reg,(match,vmKey)=>{
vmKey = vmKey.trim()
return this.$data[vmKey] // 数据中的data数据
})
}
})
}
}
生命周期
接上代码修改
constructor( options ){
if( typeof options.beforeCreate == 'function' ){
options.beforeCreate.bind(this)()
// this.$el this.$data
// undefined undefined
}
// 获取data,后面的生命周期也都有了 this.$data
this.$data = options.data
if( typeof options.created == 'function' ){
options.created.bind(this)()
// this.$el this.$data
// undefined {}
}
if( typeof options.beforeMount == 'function' ){
options.beforeMount.bind(this)()
// this.$el this.$data
// undefined {}
}
// 获取节点,后面的生命周期也都有了 this.$el
this.$el = document.querySelector(options.el);
// 模版解析
this.compile( this.$el )
if( typeof options.mounted == 'function' ){
options.mounted.bind(this)()
// this.$el this.$data
// <div id="app" {a: '1'}
}
}
事件与data数据劫持
事件绑定
compile( node ){
node.childNodes.forEach((item,index)=>{
// 判断元素节点是否绑定了@click
if( item.hasAttribute('@click') ){
// @click后绑定的属性值
let vmKey = item.getAttribute('@click').trim()
item.addEventListener('click',( event )=>{
this.eventFn = this.$options.methods[vmKey].bind(this);
this.eventFn(event)
})
}
// 元素节点
if( item.nodeType == 1 ){
if( item.childNodes.length > 0 ){
this.compile( item ) // 递归查找内同
}
}
// 这是文本节点,如果有{{}}就替换成数据
if( item.nodeType == 3 ){
let reg = /\{\{(.*?)\}\}/g // 正则匹配{{}}
let text = item.textContent // 获取文本内容
// 给节点赋值
item.textContent = text.replace(reg,(match,vmKey)=>{
vmKey = vmKey.trim()
return this.$data[vmKey] // 数据中的data数据
})
}
})
}
data数据劫持
constructor( options ){
this.$el = document.querySelector(options.el) // 获取节点
this.$data = options.data // 获取数据
this.proxyData() // data数据劫持
this.compile( this.$el ) // 绑定数据方法
}
// 1、给Vue大对象赋属性,来自于data中
// 2、data中的属性值和Vue大对象的属性保持双向(劫持)
proxyData(){
for( let key in this.$data ){
Object.defineProperty(this,key,{
get(){
return this.$data[key]
},
set( val ){
this.$data[key] = val
}
})
}
}
更多推荐
所有评论(0)